protected static new void DrawDecorator(DiagramPaintEventArgs e, IGeometryHost geometryHost, float rotation, PointD centerRight, LinkDecorator decorator) { SizeD size = LinkShapeGeometry.SizeDecorator; #endif double offsetBy = 0d; bool doOffset = false; ILinkDecoratorSettings settings = decorator as ILinkDecoratorSettings; if (settings != null) { size = settings.DecoratorSize; offsetBy = settings.OffsetBy; doOffset = !VGConstants.FuzzZero(offsetBy, VGConstants.FuzzDistance); } Graphics g = e.Graphics; RectangleD boundingRect = new RectangleD(centerRight.X - size.Width, centerRight.Y - (size.Height / 2), size.Width, size.Height); Matrix rotationMatrix = g.Transform; float offsetX = 0f; float offsetY = 0f; if (doOffset) { double rotationRadians = rotation * Math.PI / 180; offsetX = (float)(offsetBy * Math.Cos(rotationRadians)); offsetY = (float)(offsetBy * Math.Sin(rotationRadians)); rotationMatrix.Translate(offsetX, offsetY); } rotationMatrix.RotateAt(rotation, PointD.ToPointF(centerRight)); g.Transform = rotationMatrix; decorator.DoPaintShape(boundingRect, geometryHost, e); rotationMatrix.RotateAt(-rotation, PointD.ToPointF(centerRight)); if (doOffset) { rotationMatrix.Translate(-offsetX, -offsetY); } g.Transform = rotationMatrix; }
/// <summary> /// Pulled directly from Reflector disassembly /// </summary> private LineSegment[] CalculateSegments(IGeometryHost geometryHost, DiagramHitTestInfo hitTestInfo) { IBinaryLinkGeometryData data1 = geometryHost as IBinaryLinkGeometryData; EdgePointCollection collection1 = data1.GeometryEdgePointsNoJumps; LineSegment[] segmentArray1 = new LineSegment[collection1.Count - 1]; Pen pen1 = geometryHost.GeometryStyleSet.GetPen(this.GetOutlinePenId(geometryHost)); if (pen1 != null) { for (int num1 = 0; num1 < (collection1.Count - 1); num1++) { RectangleD ed1 = GeometryHelpers.RectangleDFrom2Pts(collection1[num1].Point, collection1[num1 + 1].Point); // In DSL Tools v8.2, GetHitTestTolerance is an instance method, but in DSL Tools v9.0, it is a static method. // We call it without a qualifier here so that it will compile against both versions. SizeD ed2 = GetHitTestTolerance(hitTestInfo); if (ed1.Height < ed2.Height) { ed1.Inflate(0, (pen1.Width / 2f) + ed2.Height); } else if (ed1.Width < ed2.Width) { ed1.Inflate((pen1.Width / 2f) + ed2.Width, 0); } segmentArray1[num1] = new LineSegment(collection1[num1].Point, collection1[num1 + 1].Point, num1, num1 + 1, num1 == 0, (num1 + 1) == (collection1.Count - 1), ed1); } } return(segmentArray1); }
/// <summary> /// Return a path modified to include any ER multiplicity decorators /// </summary> public override GraphicsPath GetPath(IGeometryHost geometryHost) { //TODO get this to work properly //GraphicsPath path = base.UninitializedPath; //path.Reset(); //AssociationConnector connector = (AssociationConnector)geometryHost; //EdgePointCollection edgePoints; //int edgePointCount; //if (null != (edgePoints = connector.EdgePoints) && // 1 < (edgePointCount = edgePoints.Count)) //{ // PointD p1 = edgePoints[edgePointCount - 1].Point; // PointD p2 = edgePoints[0].Point; // path.AddLine(PointD.ToPointF(p1), PointD.ToPointF(p2)); // //DrawAssociationEnd(e, geometryHost, connector.FromShape, p2, midP, true, true); // //DrawAssociationEnd(e, geometryHost, connector.ToShape, p1, midP, true, false); //} //return path; return(base.GetPath(geometryHost)); }
/// <summary> /// Gets the suggested connection points of this geometry. /// </summary> /// <value></value> public override PointD[] GetGeometryConnectionPoints(IGeometryHost geometryHost) { RectangleD boundingBox = this.GetPerimeterBoundingBox(geometryHost); return(new PointD[] { new PointD(boundingBox.Right, (boundingBox.Top + (boundingBox.Height / 2))), }); }
/// <summary> /// Gets the suggested connection points of this geometry. /// </summary> /// <value></value> public override PointD[] GetGeometryConnectionPoints(IGeometryHost geometryHost) { RectangleD boundingBox = this.GetPerimeterBoundingBox(geometryHost); return new PointD[] { new PointD(boundingBox.Right, (boundingBox.Top + (boundingBox.Height / 2))), }; }
/// <summary> /// Paint the composite shape decorators. /// </summary> /// <param name="bounds">Forwarded to composite decorators</param> /// <param name="shape">Forwarded to composite decorators</param> /// <param name="e">Forwarded to composite decorators</param> public override void DoPaintShape(RectangleD bounds, IGeometryHost shape, DiagramPaintEventArgs e) { if (myDecorators == null) { myDecorators = DecoratorCollection; if (myDecorators == null) { myDecorators = new LinkDecorator[] {}; } } for (int i = myDecorators.Count - 1; i >= 0; --i) { myDecorators[i].DoPaintShape(bounds, shape, e); } }
/// <summary> /// Paint the composite shape decorators. /// </summary> /// <param name="bounds">Forwarded to composite decorators</param> /// <param name="shape">Forwarded to composite decorators</param> /// <param name="e">Forwarded to composite decorators</param> public override void DoPaintShape(RectangleD bounds, IGeometryHost shape, DiagramPaintEventArgs e) { if (myDecorators == null) { myDecorators = DecoratorCollection; if (myDecorators == null) { myDecorators = new LinkDecorator[]{}; } } for (int i = myDecorators.Count - 1; i >= 0; --i) { myDecorators[i].DoPaintShape(bounds, shape, e); } }
public override bool DoHitTest(IGeometryHost geometryHost, PointD hitPoint, DiagramHitTestInfo hitTestInfo, bool includeTolerance) { RectangleD perimeterBoundingBox = this.GetPerimeterBoundingBox(geometryHost); if (includeTolerance) { perimeterBoundingBox.Inflate(ShapeGeometry.GetHitTestTolerance(hitTestInfo)); } if (hitTestInfo != null) { hitTestInfo.HitDiagramItem = null; hitTestInfo.HitGrabHandle = null; } // point is in the bounding rectangle bool flag = false; if (perimeterBoundingBox.Contains(hitPoint)) { // point is in the diamond shape flag = isPointInTriangle( new PointD(perimeterBoundingBox.X, perimeterBoundingBox.Y + perimeterBoundingBox.Height / 2), new PointD(perimeterBoundingBox.X + perimeterBoundingBox.Width / 2, perimeterBoundingBox.Y), new PointD(perimeterBoundingBox.X + perimeterBoundingBox.Width, perimeterBoundingBox.Y + perimeterBoundingBox.Height / 2), hitPoint ); if (!flag) { flag = isPointInTriangle( new PointD(perimeterBoundingBox.X, perimeterBoundingBox.Y + perimeterBoundingBox.Height / 2), new PointD(perimeterBoundingBox.X + perimeterBoundingBox.Width / 2, perimeterBoundingBox.Y + perimeterBoundingBox.Height), new PointD(perimeterBoundingBox.X + perimeterBoundingBox.Width, perimeterBoundingBox.Y + perimeterBoundingBox.Height / 2), hitPoint ); } } if (flag && (hitTestInfo != null)) { hitTestInfo.HitDiagramItem = ShapeGeometry.CreateDiagramItem(geometryHost); } return(flag); }
/* * the function return the 'anchor point' on the shape, lines of links will connect to the 'anchor point' * * potentialPoint is on the line which is crossing with the shape, and it's relative to (left, top) of the geometry * bounding box * * vectorEndpoint is a vector which defines the slope and direction of the line which is crossing with the shape * * the crossing point is the returned 'anchor point' * * the returned point is also relative to (left, top) of the geometry bounding box */ public override PointD DoFoldToShape(IGeometryHost geometryHost, PointD potentialPoint, PointD vectorEndpoint) { RectangleD geometryBoundingBox = geometryHost.GeometryBoundingBox; PointD A = new PointD(0, geometryBoundingBox.Height / 2); PointD B = new PointD(geometryBoundingBox.Width / 2, 0); PointD C = new PointD(geometryBoundingBox.Width, geometryBoundingBox.Height / 2); PointD D = new PointD(geometryBoundingBox.Width / 2, geometryBoundingBox.Height); // corssing point PointD[] cp = { getCrossPoint(A, B, potentialPoint, vectorEndpoint), getCrossPoint(B, C, potentialPoint, vectorEndpoint), getCrossPoint(C, D, potentialPoint, vectorEndpoint), getCrossPoint(D, A, potentialPoint, vectorEndpoint) }; double distance = double.MaxValue; PointD crossingPoint = PointD.Empty; foreach (PointD pt in cp) { if (pt.IsEmpty) { continue; } double dist = getPointDistance(potentialPoint, pt); if (dist < distance) { distance = dist; crossingPoint = pt; } } return(crossingPoint); }
/// <summary> /// Locate the opposite shape based on the given points and /// adjust the opposite the endpoint accordingly. The endpoint /// is also modified to represent an absolute value. Use VectorEndPointForBase /// to restore the vector endpoint to its natural value. /// CenterToCenter routing is assumed. /// </summary> /// <param name="geometryHost">IGeometryHost (passed from DoFoldToShape)</param> /// <param name="vectorEndPoint">PointD (passed from DoFoldToShape)</param> /// <returns>Absolute location of end point</returns> public static PointD AdjustVectorEndPoint(IGeometryHost geometryHost, PointD vectorEndPoint) { NodeShape oppositeShape; return AdjustVectorEndPoint(geometryHost, vectorEndPoint, out oppositeShape); }
/// <summary> /// Reimplementation of <see cref="LinkDecorator.DoPaintShape"/> that /// recognizes <see cref="IDynamicColorGeometryHost"/> /// </summary> public override void DoPaintShape(RectangleD bounds, IGeometryHost shape, DiagramPaintEventArgs e) { StyleSet styleSet = shape.GeometryStyleSet; GraphicsPath decoratorPath = GetPath(bounds); StyleSetResourceId penId = PenId; Pen pen = styleSet.GetPen(penId); StyleSetResourceId brushId = BrushId; Brush brush = styleSet.GetBrush(brushId); if (((decoratorPath != null) && (pen != null)) && (brush != null)) { DiagramClientView clientView = e.View; IDynamicColorGeometryHost dynamicColors = shape as IDynamicColorGeometryHost; Graphics g = e.Graphics; Color restoreColor; if (FillDecorator) { restoreColor = Color.Empty; if (dynamicColors == null || (restoreColor = dynamicColors.UpdateDynamicColor(brushId, brush)).IsEmpty) { if (clientView != null) { restoreColor = shape.UpdateGeometryLuminosity(clientView, brush); } } else if (clientView != null) { shape.UpdateGeometryLuminosity(clientView, brush); } g.FillPath(brush, decoratorPath); SolidBrush solidBrush; if (!restoreColor.IsEmpty && null != (solidBrush = brush as SolidBrush)) { solidBrush.Color = restoreColor; } } restoreColor = Color.Empty; if (dynamicColors == null || (restoreColor = dynamicColors.UpdateDynamicColor(penId, pen)).IsEmpty) { if (clientView != null) { restoreColor = shape.UpdateGeometryLuminosity(clientView, pen); } } else if (clientView != null) { shape.UpdateGeometryLuminosity(clientView, pen); } GeometryUtility.SafeDrawPath(e.Graphics, pen, decoratorPath); if (!restoreColor.IsEmpty) { pen.Color = restoreColor; } } }
/// <summary> /// Implements <see cref="IOffsetBorderPoint.OffsetBorderPoint"/> /// </summary> protected PointD? OffsetBorderPoint(IGeometryHost geometryHost, PointD borderPoint, PointD outsidePoint, double offset, bool parallelVector) { double angle = GeometryUtility.CalculateRadiansRotationAngle(outsidePoint, borderPoint); // Get the sample point PointD samplePoint = borderPoint; samplePoint.Offset(-offset * Math.Sin(angle), offset * Math.Cos(angle)); // Figure out the slope, either parallel to the incoming line, or pointed at the outside point PointD slopeThrough = parallelVector ? borderPoint : samplePoint; // Translate the rectangle to the origin RectangleD bounds = geometryHost.GeometryBoundingBox; PointD hostCenter = bounds.Center; double hcx = hostCenter.X; double hcy = hostCenter.Y; samplePoint.Offset(-hcx, -hcy); borderPoint.Offset(-hcx, -hcy); double px = samplePoint.X; double py = samplePoint.Y; double solvedX; double solvedY; double halfWidth = bounds.Width / 2; double halfHeight = bounds.Height / 2; double r = Radius; if (VGConstants.FuzzEqual(slopeThrough.X, outsidePoint.X, VGConstants.FuzzDistance)) { double absX = Math.Abs(px); // Vertical line, hit the same edge as the border point if (absX > (halfWidth + VGConstants.FuzzDistance)) { // Line hits outside rectangle return null; } solvedX = px; solvedY = halfHeight; absX = halfWidth - absX; if (absX < r) { // We're on the rounded corner. Figure out how far down the circle we need to go. solvedY -= r - Math.Sqrt(absX * (r + r - absX)); } if (borderPoint.Y < 0) { solvedY = -solvedY; } } else if (VGConstants.FuzzEqual(slopeThrough.Y, outsidePoint.Y, VGConstants.FuzzDistance)) { double absY = Math.Abs(py); // Horizontal line, hit the same edge as the border point if (absY > (halfHeight + VGConstants.FuzzDistance)) { // Line hits outside rectangle return null; } solvedY = py; solvedX = halfWidth; absY = halfHeight - absY; if (absY < r) { // We're on the rounded corner. Figure out how far down the circle we need to go. solvedX -= r - Math.Sqrt(absY * (r + r - absY)); } if (borderPoint.X < 0) { solvedX = -solvedX; } } else { int hitCount = 0; solvedX = 0; solvedY = 0; double solvedXAlternate = 0; double solvedYAlternate = 0; PointD? corner; // Use for corner tracking (both the center and the hit points) CornerQuadrant quadrant = 0; // We've already checked vertical and horizontal lines, so we know the lines will intersect either // zero or two sides of the rectangle. Find the two sides. // The intersecting line equation is y = m(x - px) + py (solved for y) or x = 1/m(y-py) + px (solved for x) // The rectangle borders are y = halfHeight, y = -halfHeight, x = halfWidth, x = -halfWidth double slope = (outsidePoint.Y - slopeThrough.Y) / (outsidePoint.X - slopeThrough.X); double inverseSlope = 1 / slope; double testIntersect; // Bottom edge testIntersect = inverseSlope * (halfHeight - py) + px; if (Math.Abs(testIntersect) < (halfWidth + VGConstants.FuzzDistance)) { solvedX = testIntersect; solvedY = halfHeight; corner = null; if (solvedX > 0) { if (r > (halfWidth - solvedX)) { corner = new PointD(halfWidth - r, halfHeight - r); quadrant = CornerQuadrant.LowerRight; } } else if (r > (halfWidth + solvedX)) { corner = new PointD(-halfWidth + r, halfHeight - r); quadrant = CornerQuadrant.LowerLeft; } if (corner.HasValue) { corner = FindCornerHit(corner.Value, samplePoint, slope, r, quadrant); if (!corner.HasValue) { return null; } solvedX = corner.Value.X; solvedY = corner.Value.Y; } hitCount = 1; } // Top edge testIntersect = inverseSlope * (-halfHeight - py) + px; if (Math.Abs(testIntersect) < (halfWidth + VGConstants.FuzzDistance)) { solvedXAlternate = testIntersect; solvedYAlternate = -halfHeight; corner = null; if (solvedXAlternate > 0) { if (r > (halfWidth - solvedXAlternate)) { corner = new PointD(halfWidth - r, -halfHeight + r); quadrant = CornerQuadrant.UpperRight; } } else if (r > (halfWidth + solvedXAlternate)) { corner = new PointD(-halfWidth + r, -halfHeight + r); quadrant = CornerQuadrant.UpperLeft; } if (corner.HasValue) { corner = FindCornerHit(corner.Value, samplePoint, slope, r, quadrant); if (!corner.HasValue) { return null; } solvedXAlternate = corner.Value.X; solvedYAlternate = corner.Value.Y; } if (hitCount == 0) { solvedX = solvedXAlternate; solvedY = solvedYAlternate; } ++hitCount; } // Right edge if (hitCount != 2) { testIntersect = slope * (halfWidth - px) + py; if (Math.Abs(testIntersect) < (halfHeight + VGConstants.FuzzDistance)) { solvedYAlternate = testIntersect; solvedXAlternate = halfWidth; corner = null; if (solvedYAlternate > 0) { if (r > (halfHeight - solvedYAlternate)) { corner = new PointD(halfWidth - r, halfHeight - r); quadrant = CornerQuadrant.LowerRight; } } else if (r > (halfHeight + solvedYAlternate)) { corner = new PointD(halfWidth - r, -halfHeight + r); quadrant = CornerQuadrant.UpperRight; } if (corner.HasValue) { corner = FindCornerHit(corner.Value, samplePoint, slope, r, quadrant); if (!corner.HasValue) { return null; } solvedXAlternate = corner.Value.X; solvedYAlternate = corner.Value.Y; } if (hitCount == 0) { solvedX = solvedXAlternate; solvedY = solvedYAlternate; } ++hitCount; } } // Left edge if (hitCount == 1) { testIntersect = slope * (-halfWidth - px) + py; if (Math.Abs(testIntersect) < (halfHeight + VGConstants.FuzzDistance)) { solvedYAlternate = testIntersect; solvedXAlternate = -halfWidth; corner = null; if (solvedYAlternate > 0) { if (r > (halfHeight - solvedYAlternate)) { corner = new PointD(-halfWidth + r, halfHeight - r); quadrant = CornerQuadrant.LowerLeft; } } else if (r > (halfHeight + solvedYAlternate)) { corner = new PointD(-halfWidth + r, -halfHeight + r); quadrant = CornerQuadrant.UpperLeft; } if (corner.HasValue) { corner = FindCornerHit(corner.Value, samplePoint, slope, r, quadrant); if (!corner.HasValue) { return null; } solvedXAlternate = corner.Value.X; solvedYAlternate = corner.Value.Y; } ++hitCount; } } // Choose the best match and translate the point back out if (hitCount == 2) { // Find the point closest to the sample point double xDif = px - solvedX; double yDif = py - solvedY; double xDifAlternate = px - solvedXAlternate; double yDifAlternate = py - solvedYAlternate; if ((xDif * xDif + yDif * yDif) > (xDifAlternate * xDifAlternate + yDifAlternate * yDifAlternate)) { solvedX = solvedXAlternate; solvedY = solvedYAlternate; } } else { // Unsolvable return null; } } return new PointD(solvedX + hcx, solvedY + hcy); }
/// <summary> /// Replacement for LinkShapeGeometry.DrawDecorator /// </summary> #if VISUALSTUDIO_10_0 protected static new void DrawDecorator(DiagramPaintEventArgs e, IGeometryHost geometryHost, float rotation, PointD centerRight, LinkDecorator decorator, SizeD size) {
/// <summary> /// A utility function to attempt to perfom custom shape folding. /// Custom shape folding uses shape-specific information beyond that /// available to the ShapeGeometry to determine connection points. /// This function should be call after AdjustVectorEndPoint. /// </summary> /// <param name="geometryHost">The geometryHost value passed to DoFoldToShape</param> /// <param name="vectorEndPoint">The vectorEndPoint passed to DoFoldToShape and adjusted by <see cref="AdjustVectorEndPoint(IGeometryHost,PointD,out NodeShape)"/></param> /// <param name="oppositeShape">The opposite shape returned by <see cref="AdjustVectorEndPoint(IGeometryHost,PointD,out NodeShape)"/></param> /// <returns>Nullable<PointD> with a value on success, without a value if custom folding is not available</returns> public static PointD? DoCustomFoldShape(IGeometryHost geometryHost, PointD vectorEndPoint, NodeShape oppositeShape) { ICustomShapeFolding customFolding; IProxyConnectorShape proxyConnector; PointD customPoint; NodeShape realHost; if (oppositeShape != null && null != (customFolding = geometryHost as ICustomShapeFolding) && !(customPoint = customFolding.CalculateConnectionPoint(oppositeShape)).IsEmpty) { // Translate back to local coordinates PointD location = geometryHost.TranslateGeometryToAbsoluteBounds(geometryHost.GeometryBoundingBox).Location; customPoint.Offset(-location.X, -location.Y); return customPoint; } else if (null != (proxyConnector = geometryHost as IProxyConnectorShape) && null != (realHost = proxyConnector.ProxyConnectorShapeFor as NodeShape)) { SizeD size = realHost.Size; customPoint = realHost.ShapeGeometry.DoFoldToShape(new GeometryHostWrapper(realHost), new PointD(size.Width / 2, size.Height / 2), GeometryUtility.VectorEndPointForBase(realHost, vectorEndPoint)); PointD location = geometryHost.TranslateGeometryToAbsoluteBounds(geometryHost.GeometryBoundingBox).Location; PointD realLocation = realHost.AbsoluteBounds.Location; customPoint.Offset(realLocation.X - location.X, realLocation.Y - location.Y); return customPoint; } return null; }
/// <summary> /// Locate the opposite shape based on the given points and /// adjust the opposite the endpoint accordingly. The endpoint /// is also modified to represent an absolute value. Use VectorEndPointForBase /// to restore the vector endpoint to its natural value. /// CenterToCenter routing is assumed. /// </summary> /// <param name="geometryHost">IGeometryHost (passed from DoFoldToShape)</param> /// <param name="vectorEndPoint">PointD (passed from DoFoldToShape)</param> /// <param name="oppositeShape">The located opposite shape at this location</param> /// <returns>Absolute location of end point</returns> public static PointD AdjustVectorEndPoint(IGeometryHost geometryHost, PointD vectorEndPoint, out NodeShape oppositeShape) { oppositeShape = null; // The vectorEndPoint value is coming in (negative, negative) for the lower // right quadrant instead of (positive, positive). All other values are // (positive, positive), so we switch the end point to make the rest of the work // easier. For CenterToCenter routing, subtracting the vectorEndPoint from the // lower right corner gives the correct value. RectangleD absoluteBoundingBox = geometryHost.TranslateGeometryToAbsoluteBounds(geometryHost.GeometryBoundingBox); #if VISUALSTUDIO_10_0 PointD absoluteCenter = absoluteBoundingBox.Center; vectorEndPoint = new PointD(absoluteCenter.X - vectorEndPoint.X, absoluteCenter.Y - vectorEndPoint.Y); #else vectorEndPoint = new PointD(absoluteBoundingBox.Right - vectorEndPoint.X, absoluteBoundingBox.Bottom - vectorEndPoint.Y); #endif NodeShape shape = geometryHost as NodeShape; if (shape != null) { ReadOnlyCollection<LinkConnectsToNode> links = DomainRoleInfo.GetElementLinks<LinkConnectsToNode>(shape, LinkConnectsToNode.NodesDomainRoleId); int linksCount = links.Count; for (int i = 0; i < linksCount; ++i) { LinkConnectsToNode link = links[i]; BinaryLinkShape linkShape = link.Link as BinaryLinkShape; if (link != null) { // Get the opposite shape NodeShape testShape; if (linkShape.FromLinkConnectsToNode == link) { testShape = linkShape.ToShape; } else { testShape = linkShape.FromShape; } PointD shapeCenter = testShape.AbsoluteCenter; if (VGConstants.FuzzEqual(shapeCenter.X, vectorEndPoint.X, VGConstants.FuzzDistance) && VGConstants.FuzzEqual(shapeCenter.Y, vectorEndPoint.Y, VGConstants.FuzzDistance)) { oppositeShape = testShape; ICustomShapeFolding customFolding = testShape as ICustomShapeFolding; if (customFolding != null) { PointD customEndPoint = customFolding.CalculateConnectionPoint(shape); if (!customEndPoint.IsEmpty) { vectorEndPoint = customEndPoint; } } break; } } } } return vectorEndPoint; }
/// <summary> /// Implement shape folding on the triangle boundary /// </summary> /// <param name="geometryHost">The host view</param> /// <param name="potentialPoint">A point on the rectangular boundary of the shape</param> /// <param name="vectorEndPoint">A point on the opposite end of the connecting line</param> /// <returns>A point on the triangular border</returns> public override PointD DoFoldToShape(IGeometryHost geometryHost, PointD potentialPoint, PointD vectorEndPoint) { // Get an endpoint we can work with NodeShape oppositeShape; vectorEndPoint = GeometryUtility.AdjustVectorEndPoint(geometryHost, vectorEndPoint, out oppositeShape); PointD? customPoint = GeometryUtility.DoCustomFoldShape(geometryHost, vectorEndPoint, oppositeShape); if (customPoint.HasValue) { return customPoint.Value; } vectorEndPoint = GeometryUtility.ResolveProxyConnectorVectorEndPoint(vectorEndPoint, oppositeShape); RectangleD bounds = geometryHost.TranslateGeometryToAbsoluteBounds(geometryHost.GeometryBoundingBox); PointD center = bounds.Center; PointD[] trianglePoints = GeometryUtility.GetTrianglePointsD(bounds); double offsetByX = -center.X; double offsetByY = -center.Y; for (int i = 0; i < trianglePoints.Length; ++i) { trianglePoints[i].Offset(offsetByX, offsetByY); } vectorEndPoint.Offset(offsetByX, offsetByY); bool negativeX = vectorEndPoint.X < 0; bool negativeY = vectorEndPoint.Y < 0; double halfWidth = bounds.Width / 2; double halfHeight = bounds.Height / 2; if (VGConstants.FuzzZero(vectorEndPoint.X, VGConstants.FuzzDistance)) { // Vertical line, skip slope calculations return new PointD(halfWidth, trianglePoints[negativeY ? 0 : 1].Y + halfHeight); } else if (VGConstants.FuzzZero(vectorEndPoint.Y, VGConstants.FuzzDistance)) { // Horizontal line, skip slope calculations // Solve two-point form of line between triangle points for x with y = 0. PointD topPoint = trianglePoints[0]; PointD bottomPoint = trianglePoints[negativeX ? 1 : 2]; return new PointD(topPoint.X - topPoint.Y * (bottomPoint.X - topPoint.X)/(bottomPoint.Y - topPoint.Y) + halfWidth, halfHeight); } else { double slope = vectorEndPoint.Y / vectorEndPoint.X; double x; double y; if (negativeY || (Math.Abs(slope) < (halfHeight / halfWidth))) // Reasonable, but allows y to spill below the bottom { // Try to attach to the left/right lines PointD topPoint = trianglePoints[0]; PointD bottomPoint = trianglePoints[negativeX ? 1 : 2]; double inverseTriangleSlope = (bottomPoint.X - topPoint.X) / (bottomPoint.Y - topPoint.Y); x = (topPoint.X - topPoint.Y * inverseTriangleSlope) / (1 - slope * inverseTriangleSlope); y = slope * x; if (y > bottomPoint.Y) { // Adjust for a y below the bottom point. y = bottomPoint.Y; x = y / slope; } } else { // Attach to the bottom edge y = trianglePoints[1].Y; x = y / slope; } return new PointD(x + halfWidth, y + halfHeight); } }
/// <summary> /// Paint the solid crowsfoot on the end of an optional line /// </summary> protected override void DoPaintGeometry(DiagramPaintEventArgs e, IGeometryHost geometryHost) { AssociationConnector connector = (AssociationConnector)geometryHost; BarkerErModelContainsBinaryAssociation link = connector.ModelElement as BarkerErModelContainsBinaryAssociation; EdgePointCollection edgePoints; int edgePointCount; BinaryAssociation association; LinkedElementCollection <Barker.Role> roles; if (null != (edgePoints = connector.EdgePoints) && 1 < (edgePointCount = edgePoints.Count) && null != (association = link.BinaryAssociation) && 2 == (roles = association.RoleCollection).Count) { PointD p1 = edgePoints[edgePointCount - 1].Point; PointD p2 = edgePoints[0].Point; PointD midP = new PointD((p1.X + p2.X) / 2, (p1.Y + p2.Y) / 2); #region find correct roles EntityType fromEntity = connector.FromShape.ModelElement as EntityType; EntityType toEntity = connector.ToShape.ModelElement as EntityType; Barker.Role fromRole = null, toRole = null; if (fromEntity == roles[0].EntityType) { fromRole = roles[0]; } else if (fromEntity == roles[1].EntityType) { fromRole = roles[1]; } if (toEntity == roles[0].EntityType) { toRole = roles[0]; } else if (toEntity == roles[1].EntityType) { toRole = roles[1]; } #endregion if (fromRole != null && toRole != null) { bool fromOptional = !fromRole.IsMandatory; bool toOptional = !toRole.IsMandatory; bool bothOptional = fromOptional && toOptional; PointF?v1 = null, v2 = null; DrawAssociationEnd( e, geometryHost, connector.FromShape, p2, midP, fromRole.IsMultiValued, fromOptional, fromRole.IsPrimaryIdComponent, fromRole.PredicateText, ref v1, bothOptional); DrawAssociationEnd( e, geometryHost, connector.ToShape, p1, midP, toRole.IsMultiValued, toOptional, toRole.IsPrimaryIdComponent, toRole.PredicateText, ref v2, bothOptional); if (bothOptional && v1.HasValue && v2.HasValue) { Pen pen = geometryHost.GeometryStyleSet.GetPen(DiagramPens.ConnectionLine); pen = (Pen)pen.Clone(); pen.DashPattern = DashPattern; e.Graphics.DrawLine(pen, v1.Value, v2.Value); } } } }
/// <summary> /// Replacement for <see cref="NodeShapeGeometry.DoPaintGeometry"/> that supports /// dynamic background and outline colors. /// </summary> protected override void DoPaintGeometry(DiagramPaintEventArgs e, IGeometryHost geometryHost) { IDynamicColorGeometryHost dynamicColors = geometryHost as IDynamicColorGeometryHost; if (dynamicColors != null) { GeometryUtility.PaintDynamicColorGeometry(e, geometryHost, dynamicColors, this, this.HasFilledBackground(geometryHost), this.HasOutline(geometryHost)); } else { base.DoPaintGeometry(e, geometryHost); } }
/// <summary> /// Implement shape folding on the ellipse boundary /// </summary> /// <param name="geometryHost">The host view</param> /// <param name="potentialPoint">A point on the rectangular boundary of the shape</param> /// <param name="vectorEndPoint">A point on the opposite end of the connecting line</param> /// <returns>A point on the ellipse border</returns> public override PointD DoFoldToShape(IGeometryHost geometryHost, PointD potentialPoint, PointD vectorEndPoint) { // Get an endpoint we can work with NodeShape oppositeShape; vectorEndPoint = GeometryUtility.AdjustVectorEndPoint(geometryHost, vectorEndPoint, out oppositeShape); PointD? customPoint = GeometryUtility.DoCustomFoldShape(geometryHost, vectorEndPoint, oppositeShape); if (customPoint.HasValue) { return customPoint.Value; } vectorEndPoint = GeometryUtility.ResolveProxyConnectorVectorEndPoint(vectorEndPoint, oppositeShape); // The point returned needs to be relative to the upper left corner of the bounding // box. The goal is to get a point on the circle that points to the center of the // line. To do this, we translate the coordinate system to the center of the circle, // get a slope from the vectorEndPoint/circle center, then solve the circle equation // and retranslate the coordinates back out. // The quadrant we're coming in from can be determined by the position of the vectorEndPoint // relative to the ellipse center. // // The pertinent equations are: // vectorEndPoint (relative point) = (xe, ye) // center = (xc, yc) // circle radius = r // slope = m = (ye - yc)/(xe - xc) // line equation: y = mx // circle equation (centered at origin): x^2 + y^2 = r^2 // solving gives us: x = +/-(r/sqrt(1 + m^2)) // Plugging back into the line equation gives us a +/- y value // Final point = (xc, yc) + (x, y) // The quadrant is determined by the relative position of the vectorEndPoint RectangleD box = geometryHost.TranslateGeometryToAbsoluteBounds(geometryHost.GeometryBoundingBox); PointD boxCenter = box.Center; double radius = Math.Min(box.Width / 2, box.Height / 2); if (VGConstants.FuzzEqual(vectorEndPoint.X, boxCenter.X, VGConstants.FuzzDistance)) { return new PointD(box.Width / 2, (vectorEndPoint.Y < boxCenter.Y) ? 0 : box.Height); } else if (VGConstants.FuzzEqual(vectorEndPoint.Y, boxCenter.Y, VGConstants.FuzzDistance)) { return new PointD((vectorEndPoint.X < boxCenter.X) ? 0 : box.Width, box.Height / 2); } else { bool negativeX = vectorEndPoint.X < boxCenter.X; bool negativeY = vectorEndPoint.Y < boxCenter.Y; double slope = (vectorEndPoint.Y - boxCenter.Y) / (vectorEndPoint.X - boxCenter.X); double x = radius / Math.Sqrt(1 + slope * slope); double y = slope * x; x = Math.Abs(x); y = Math.Abs(y); if (negativeX) { x = -x; if (negativeY) { y = -y; } } else if (negativeY) { y = -y; } // Return a point relative to the shape return new PointD(x + radius, y + radius); } }
PointD? IOffsetBorderPoint.OffsetBorderPoint(IGeometryHost geometryHost, PointD borderPoint, PointD outsidePoint, double offset, bool parallelVector) { return OffsetBorderPoint(geometryHost, borderPoint, outsidePoint, offset, parallelVector); }
/// <summary> /// Implements <see cref="IOffsetBorderPoint.OffsetBorderPoint"/> /// </summary> protected PointD? OffsetBorderPoint(IGeometryHost geometryHost, PointD borderPoint, PointD outsidePoint, double offset, bool parallelVector) { double angle = GeometryUtility.CalculateRadiansRotationAngle(outsidePoint, borderPoint); // Get the sample point PointD samplePoint = borderPoint; samplePoint.Offset(-offset * Math.Sin(angle), offset * Math.Cos(angle)); // Figure out the slope, either parallel to the incoming line, or pointed at the outside point PointD slopeThrough = parallelVector ? borderPoint : samplePoint; // Translate the ellipse to the origin RectangleD bounds = geometryHost.GeometryBoundingBox; PointD hostCenter = bounds.Center; samplePoint.Offset(-hostCenter.X, -hostCenter.Y); double px = samplePoint.X; double py = samplePoint.Y; double solvedX; double solvedY; bool checkAlternate = false; double solvedXAlternate = 0; double solvedYAlternate = 0; if (VGConstants.FuzzEqual(slopeThrough.X, outsidePoint.X, VGConstants.FuzzDistance)) { // Vertical line, can't get slope, y = +/-b * sqrt(1-(x0/a)^2) double discriminant = px / (bounds.Width / 2); discriminant = 1 - discriminant * discriminant; if (VGConstants.FuzzZero(discriminant, VGConstants.FuzzGeneral)) { solvedX = px; solvedY = 0; } else if (discriminant < 0) { // Equation is not solvable return null; } else { solvedX = px; solvedY = (bounds.Height / 2) * Math.Sqrt(discriminant); solvedXAlternate = px; solvedYAlternate = -solvedY; checkAlternate = true; } } else if (VGConstants.FuzzEqual(slopeThrough.Y, outsidePoint.Y, VGConstants.FuzzDistance)) { // Horizontal line, main equation works, but this is a lot cleaner. // Switch axes from vertical block double discriminant = py / (bounds.Height / 2); discriminant = 1 - discriminant * discriminant; if (VGConstants.FuzzZero(discriminant, VGConstants.FuzzGeneral)) { solvedY = py; solvedX = 0; } else if (discriminant < 0) { // Equation is not solvable return null; } else { solvedY = py; solvedX = (bounds.Width / 2) * Math.Sqrt(discriminant); solvedYAlternate = py; solvedXAlternate = -solvedX; checkAlternate = true; } } else { double slope = (outsidePoint.Y - slopeThrough.Y) / (outsidePoint.X - slopeThrough.X); double xRadiusSquared = bounds.Width / 2; xRadiusSquared *= xRadiusSquared; double yRadiusSquared = bounds.Height / 2; yRadiusSquared *= yRadiusSquared; // The A/B/C below refer to the quadratic equation (Ax^2 + Bx + C = 0) gives // The equations involved are // Ellipse equation: x^2/a^2 + y^2/b^2 = 1 for the ellipse (a = width/2, b = height/2, centered at 0,0) // Line equation: x = m(x - x0) + y0 (m = slope, x0 = px above, y0 = py above). Solving into // quadratic form gives the following equations: double quadA = yRadiusSquared / xRadiusSquared + slope * slope; double sharedQuadPart = py - slope * px; double quadB = (slope + slope) * sharedQuadPart; double quadC = sharedQuadPart * sharedQuadPart - yRadiusSquared; double discriminant = quadB * quadB - 4 * quadA * quadC; if (VGConstants.FuzzZero(discriminant, VGConstants.FuzzGeneral)) { // Tangential line, one possibility only solvedX = -quadB / (quadA + quadA); solvedY = slope * (solvedX - px) + py; } else if (discriminant < 0) { // Equation is not solvable return null; } else { // We want the point that is closest to the sample point. discriminant = Math.Sqrt(discriminant); solvedX = (-quadB + discriminant) / (quadA + quadA); solvedY = slope * (solvedX - px) + py; solvedXAlternate = (-quadB - discriminant) / (quadA + quadA); solvedYAlternate = slope * (solvedXAlternate - px) + py; checkAlternate = true; } } // Choose the best match and translate the point back out if (checkAlternate) { // Note that simple quadrant checks are no sufficient here, we must // find the closest solution to the starting point double xDif = px - solvedX; double yDif = py - solvedY; double xDifAlternate = px - solvedXAlternate; double yDifAlternate = py - solvedYAlternate; if ((xDif * xDif + yDif * yDif) > (xDifAlternate * xDifAlternate + yDifAlternate * yDifAlternate)) { solvedX = solvedXAlternate; solvedY = solvedYAlternate; } } return new PointD(solvedX + hostCenter.X, solvedY + hostCenter.Y); }
/// <summary> /// Paint the background and outline of a <see cref="IGeometryHost"/> using /// dynamic colors provided by the <see cref="IDynamicColorGeometryHost"/> interface. /// This is a helper method designed to be called by an override of the <see cref="NodeShapeGeometry.DoPaintGeometry"/> method. /// </summary> /// <param name="e">The <see cref="DiagramPaintEventArgs"/> passed to DoPaintGeometry</param> /// <param name="geometryHost">The <see cref="IGeometryHost"/> passed to DoPaintGeometry</param> /// <param name="dynamicColors">The <see cref="IDynamicColorGeometryHost"/> retrieved from the <paramref name="geometryHost"/></param> /// <param name="shapeGeometry">The <see cref="ShapeGeometry"/> this is a helper for.</param> /// <param name="hasFilledBackground">The result of the <see cref="ShapeGeometry.HasFilledBackground"/> method</param> /// <param name="hasOutline">The result of the <see cref="ShapeGeometry.HasOutline"/> method</param> /// <remarks> /// The DoPaintGeometry override should look similar to /// <code> /// protected override void DoPaintGeometry(DiagramPaintEventArgs e, IGeometryHost geometryHost) /// { /// IDynamicColorGeometryHost dynamicColors = geometryHost as IDynamicColorGeometryHost; /// if (dynamicColors != null) /// { /// GeometryUtility.PaintDynamicColorGeometry(e, geometryHost, dynamicColors, this, this.HasFilledBackground(geometryHost), this.HasOutline(geometryHost)); /// } /// else /// { /// base.DoPaintGeometry(e, geometryHost); /// } /// } /// </code> /// </remarks> public static void PaintDynamicColorGeometry(DiagramPaintEventArgs e, IGeometryHost geometryHost, IDynamicColorGeometryHost dynamicColors, ShapeGeometry shapeGeometry, bool hasFilledBackground, bool hasOutline) { if (hasFilledBackground || hasOutline) { StyleSet geometryStyleSet = geometryHost.GeometryStyleSet; GraphicsPath path; StyleSetResourceId penId = null; Pen pen = null; StyleSetResourceId brushId = null; Brush brush = null; if (null != (path = shapeGeometry.GetPath(geometryHost)) && (!hasFilledBackground || null != (brush = geometryStyleSet.GetBrush(brushId = shapeGeometry.GetBackgroundBrushId(geometryHost)))) && (!hasOutline || null != (pen = geometryStyleSet.GetPen(penId = shapeGeometry.GetOutlinePenId(geometryHost))))) { Graphics g = e.Graphics; DiagramClientView clientView = e.View; Color restoreColor; if (brush != null) { restoreColor = Color.Empty; if (dynamicColors == null || (restoreColor = dynamicColors.UpdateDynamicColor(brushId, brush)).IsEmpty) { if (clientView != null) { restoreColor = geometryHost.UpdateGeometryLuminosity(clientView, brush); } } else if (clientView != null) { geometryHost.UpdateGeometryLuminosity(clientView, brush); } g.FillPath(brush, path); SolidBrush solidBrush = brush as SolidBrush; if (!restoreColor.IsEmpty && null != (solidBrush = brush as SolidBrush)) { solidBrush.Color = restoreColor; } } if (pen != null) { restoreColor = Color.Empty; if (dynamicColors == null || (restoreColor = dynamicColors.UpdateDynamicColor(penId, pen)).IsEmpty) { if (clientView != null) { restoreColor = geometryHost.UpdateGeometryLuminosity(clientView, pen); } } else if (clientView != null) { geometryHost.UpdateGeometryLuminosity(clientView, pen); } SafeDrawPath(g, pen, path); if (!restoreColor.IsEmpty) { pen.Color = restoreColor; } } } } }
/// <summary> /// Override of DoHitTest so it works with non-rectilinear line segments /// </summary> public override bool DoHitTest(IGeometryHost geometryHost, PointD hitPoint, DiagramHitTestInfo hitTestInfo, bool includeTolerance) { bool retVal = false; LineSegment hitSegment = null; AnchorPoint anchorPoint = null; // In DSL Tools v8.2, GetHitTestTolerance is an instance method, but in DSL Tools v9.0, it is a static method. // We call it without a qualifier here so that it will compile against both versions. SizeD tolerance = GetHitTestTolerance(hitTestInfo); RectangleD perimeter = this.GetPerimeterBoundingBox(geometryHost); perimeter.Inflate(tolerance); if (perimeter.Contains(hitPoint)) { LineSegment[] segments = this.CalculateSegments(geometryHost, hitTestInfo); int segmentCount = segments.Length; for (int i = 0; i < segmentCount; ++i) { LineSegment testSegment = segments[i]; RectangleD testBounds = GeometryHelpers.RectangleDFrom2Pts(testSegment.StartPoint, testSegment.EndPoint); testBounds.Inflate(tolerance); if (testBounds.Contains(hitPoint)) { anchorPoint = TestHitAnchor(geometryHost as BinaryLinkShape, testSegment, tolerance, hitPoint); if (anchorPoint != null) { retVal = true; hitSegment = testSegment; break; } double distance = DistanceFromPointToLine(hitPoint, testSegment.StartPoint, testSegment.EndPoint, true); if (!double.IsNaN(distance) && distance < (tolerance.Width + geometryHost.GeometryStyleSet.GetPen(geometryHost.GeometryOutlinePenId).Width / 2f)) { retVal = true; hitSegment = testSegment; break; } } } } if (hitTestInfo != null) { DiagramItem diagramItem; if (retVal) { if (anchorPoint == null) { // In DSL Tools v8.2, CreateDiagramItem is an instance method, but in DSL Tools v9.0, it is a static method. // We call it without a qualifier here so that it will compile against both versions. diagramItem = CreateDiagramItem(geometryHost, hitSegment); } else { diagramItem = new DiagramItem(geometryHost as LinkShape, hitSegment, anchorPoint); } } else { diagramItem = null; } hitTestInfo.HitDiagramItem = diagramItem; hitTestInfo.HitGrabHandle = null; } return retVal; }
/// <summary> /// Pulled directly from Reflector disassembly /// </summary> private LineSegment[] CalculateSegments(IGeometryHost geometryHost, DiagramHitTestInfo hitTestInfo) { IBinaryLinkGeometryData data1 = geometryHost as IBinaryLinkGeometryData; EdgePointCollection collection1 = data1.GeometryEdgePointsNoJumps; LineSegment[] segmentArray1 = new LineSegment[collection1.Count - 1]; Pen pen1 = geometryHost.GeometryStyleSet.GetPen(this.GetOutlinePenId(geometryHost)); if (pen1 != null) { for (int num1 = 0; num1 < (collection1.Count - 1); num1++) { RectangleD ed1 = GeometryHelpers.RectangleDFrom2Pts(collection1[num1].Point, collection1[num1 + 1].Point); // In DSL Tools v8.2, GetHitTestTolerance is an instance method, but in DSL Tools v9.0, it is a static method. // We call it without a qualifier here so that it will compile against both versions. SizeD ed2 = GetHitTestTolerance(hitTestInfo); if (ed1.Height < ed2.Height) { ed1.Inflate(0, (pen1.Width / 2f) + ed2.Height); } else if (ed1.Width < ed2.Width) { ed1.Inflate((pen1.Width / 2f) + ed2.Width, 0); } segmentArray1[num1] = new LineSegment(collection1[num1].Point, collection1[num1 + 1].Point, num1, num1 + 1, num1 == 0, (num1 + 1) == (collection1.Count - 1), ed1); } } return segmentArray1; }
/// <summary> /// Adjust a vector end point retrieved from AdjustVectorEndPoint into /// the value in its original (very strange) coordinate system. /// </summary> /// <param name="geometryHost">The geometryHost passed to FoldToShape</param> /// <param name="vectorEndPoint">An adjusted vector end point</param> /// <returns>An unadjusted value</returns> public static PointD VectorEndPointForBase(IGeometryHost geometryHost, PointD vectorEndPoint) { RectangleD absoluteBoundingBox = geometryHost.TranslateGeometryToAbsoluteBounds(geometryHost.GeometryBoundingBox); #if VISUALSTUDIO_10_0 PointD absoluteCenter = absoluteBoundingBox.Center; return new PointD(absoluteCenter.X - vectorEndPoint.X, absoluteCenter.X - vectorEndPoint.Y); #else return new PointD(absoluteBoundingBox.Right - vectorEndPoint.X, absoluteBoundingBox.Bottom - vectorEndPoint.Y); #endif }
/// <summary> /// Draws the shape's shadow. /// </summary> /// <remarks> /// The shadow is replaced with a glow. /// </remarks> protected override void DoPaintShadow(DiagramPaintEventArgs e, IGeometryHost geometryHost) { Guard.NotNull(() => geometryHost, geometryHost); Guard.NotNull(() => e, e); Graphics graphics = e.Graphics; GraphicsState state = graphics.Save(); SizeD shadowOffset = this.ShadowOffset; try { GraphicsPath shapePath = this.GetPath(geometryHost); RectangleF shapeRectangle = shapePath.GetBounds(); // Create shadow path GraphicsPath shadowPath = shapePath.Clone() as GraphicsPath; // Enlarge the shadow (by fixed magnifier amount) using (Matrix scaleMatrix = new Matrix()) { scaleMatrix.Scale( (ShadowMagnifier / shapeRectangle.Width) + 1, (ShadowMagnifier / shapeRectangle.Height) + 1); shadowPath.Transform(scaleMatrix); // Center shadow back on the shape RectangleF shadowRectangle = shadowPath.GetBounds(); scaleMatrix.Reset(); scaleMatrix.Translate( -((shadowRectangle.X + (shadowRectangle.Width / 2)) - (shapeRectangle.X + (shapeRectangle.Width / 2))), -((shadowRectangle.Y + (shadowRectangle.Height / 2)) - (shapeRectangle.Y + (shapeRectangle.Height / 2)))); shadowPath.Transform(scaleMatrix); } // Set the clip region (on the shape) using (Region clip = graphics.Clip) { graphics.SetClip(shapePath); // Offset the shadow path (move diagonally down-right) from shape using (Matrix translateMatrix = new Matrix()) { translateMatrix.Translate((float)shadowOffset.Width, (float)shadowOffset.Height); shadowPath.Transform(translateMatrix); } // Mask-off the shadow from the original shape graphics.SetClip(shadowPath, CombineMode.Complement); graphics.SetClip(clip, CombineMode.Intersect); // Fill the shadow using (PathGradientBrush shadowBrush = new PathGradientBrush(shadowPath)) { shadowBrush.CenterColor = Color.FromArgb(ShadowColorOpacity, ShadowColor); shadowBrush.SurroundColors = new Color[] { Color.Transparent }; shadowBrush.FocusScales = new PointF(ShadowGradientFocalPoint, ShadowGradientFocalPoint); graphics.FillPath(shadowBrush, shadowPath); graphics.ResetClip(); } } } finally { graphics.Restore(state); } }
/// <summary> /// Don't let the lines interfere with clicking on other items /// </summary> public override bool DoHitTest(IGeometryHost geometryHost, PointD hitPoint, DiagramHitTestInfo hitTestInfo, bool includeTolerance) { return(false); }
/// <summary> /// Provide custom shape folding for rectangular fact types /// </summary> /// <param name="geometryHost">The host view</param> /// <param name="potentialPoint">A point on the rectangular boundary of the shape</param> /// <param name="vectorEndPoint">A point on the opposite end of the connecting line</param> /// <returns>A point on the rectangle edge border</returns> public override PointD DoFoldToShape(IGeometryHost geometryHost, PointD potentialPoint, PointD vectorEndPoint) { NodeShape oppositeShape; vectorEndPoint = GeometryUtility.AdjustVectorEndPoint(geometryHost, vectorEndPoint, out oppositeShape); PointD? customPoint = GeometryUtility.DoCustomFoldShape(geometryHost, vectorEndPoint, oppositeShape); if (customPoint.HasValue) { return customPoint.Value; } vectorEndPoint = GeometryUtility.ResolveProxyConnectorVectorEndPoint(vectorEndPoint, oppositeShape); // Fold to the shape // This is used for center to center routing, so the potential point is the // center of the shape. We need to see where a line through the center intersects // the rectangle border and return relative coordinates. RectangleD bounds = geometryHost.TranslateGeometryToAbsoluteBounds(geometryHost.GeometryBoundingBox); PointD center = bounds.Center; vectorEndPoint.Offset(-center.X, -center.Y); bool negativeX = vectorEndPoint.X < 0; bool negativeY = vectorEndPoint.Y < 0; if (VGConstants.FuzzZero(vectorEndPoint.X, VGConstants.FuzzDistance)) { // Vertical line, skip slope calculations return new PointD(bounds.Width / 2, negativeY ? 0 : bounds.Height); } else if (VGConstants.FuzzZero(vectorEndPoint.Y, VGConstants.FuzzDistance)) { // Horizontal line, skip slope calculations return new PointD(negativeX ? 0 : bounds.Width, bounds.Height / 2); } else { double slope = vectorEndPoint.Y / vectorEndPoint.X; // The intersecting line equation is y = mx. We can tell // whether to use the vertical or horizontal lines by // comparing the relative sizes of the rectangle sides // with the slope double x; double y; if (Math.Abs(slope) < (bounds.Height / bounds.Width)) { // Attach to left/right edges // Intersect with line x = +/- bounds.Width / 2 x = bounds.Width / 2; if (negativeX) { x = -x; } y = x * slope; } else { // Attach to top/bottom edges // Intersect with line y = +/- bounds.Height / 2 y = bounds.Height / 2; if (negativeY) { y = -y; } x = y / slope; } return new PointD(x + bounds.Width / 2, y + bounds.Height / 2); } }
public GeometryHostWrapper(IGeometryHost inner) { myInner = inner; }
private static void DrawAssociationEnd( DiagramPaintEventArgs e, IGeometryHost geometryHost, NodeShape node, PointD pointOnBorder, PointD endPoint, bool many, bool optional, bool id, string predicateText, ref PointF?lineEnd, bool bothOptional) { Pen pen; ShapeElement shapeHost; IOffsetBorderPoint offsetPointProvider; Font font; Brush brush; Graphics g = e.Graphics; if (null != (shapeHost = node) && null != (offsetPointProvider = shapeHost.ShapeGeometry as IOffsetBorderPoint) && null != (pen = geometryHost.GeometryStyleSet.GetPen(DiagramPens.ConnectionLine)) && null != (font = geometryHost.GeometryStyleSet.GetFont(DiagramFonts.ConnectionLine)) && null != (brush = geometryHost.GeometryStyleSet.GetBrush(DiagramBrushes.ConnectionLineText))) { Color restoreColor = pen.Color; pen.Color = geometryHost.UpdateGeometryLuminosity(e.View, pen); double angle = GeometryUtility.CalculateRadiansRotationAngle(endPoint, pointOnBorder); PointD vertexPoint = pointOnBorder; vertexPoint.Offset(CrowsFootHeight * Math.Cos(angle), CrowsFootHeight * Math.Sin(angle)); #region draw the main line Pen mainLinePen = (Pen)pen.Clone(); if (optional) { mainLinePen.DashPattern = DashPattern; mainLinePen.DashOffset = DashPattern[0]; } if (many & optional) { if (!bothOptional) { g.DrawLine(mainLinePen, PointD.ToPointF(endPoint), PointD.ToPointF(vertexPoint)); } else { lineEnd = PointD.ToPointF(vertexPoint); } g.DrawLine(pen, PointD.ToPointF(vertexPoint), PointD.ToPointF(pointOnBorder)); } else { if (!bothOptional) { g.DrawLine(mainLinePen, PointD.ToPointF(endPoint), PointD.ToPointF(pointOnBorder)); } else { lineEnd = PointD.ToPointF(pointOnBorder); } } #endregion #region draw crow's foot if necessary if (many) { PointD?offsetBorderPoint = offsetPointProvider.OffsetBorderPoint(shapeHost, pointOnBorder, vertexPoint, CrowsFootHalfWidth, CrowsFootParallelMode); if (offsetBorderPoint.HasValue) { g.DrawLine(pen, PointD.ToPointF(vertexPoint), PointD.ToPointF(offsetBorderPoint.Value)); } offsetBorderPoint = offsetPointProvider.OffsetBorderPoint(shapeHost, pointOnBorder, vertexPoint, -CrowsFootHalfWidth, CrowsFootParallelMode); if (offsetBorderPoint.HasValue) { g.DrawLine(pen, PointD.ToPointF(vertexPoint), PointD.ToPointF(offsetBorderPoint.Value)); } } #endregion #region draw tick mark if necessary if (id) { PointD oneMarkLeft = vertexPoint; double cosAngle = Math.Cos(angle); double sinAngle = Math.Sin(angle); oneMarkLeft.Offset(InfEngInnerOneMarkOffset * cosAngle, InfEngInnerOneMarkOffset * sinAngle); PointD oneMarkRight = oneMarkLeft; oneMarkLeft.Offset(-InfEngMarkerHalfWidth * sinAngle, InfEngMarkerHalfWidth * cosAngle); oneMarkRight.Offset(InfEngMarkerHalfWidth * sinAngle, -InfEngMarkerHalfWidth * cosAngle); g.DrawLine(pen, PointD.ToPointF(oneMarkLeft), PointD.ToPointF(oneMarkRight)); } #endregion #region draw text //determine the line's properties double edgeX = pointOnBorder.X; double edgeY = pointOnBorder.Y; EntitySideType whichSideShapeIsOn = EntitySide.FindWhichSide(pointOnBorder, shapeHost.GeometryBoundingBox); double w = 0; //, y = 0; angle = Math.Atan2(Math.Abs(endPoint.Y - edgeY), Math.Abs(endPoint.X - edgeX)); double inDegrees = angle * 180 / Math.PI; if (inDegrees < 0) { inDegrees += 360; } SizeF textSize = g.MeasureString(predicateText, font); w = textSize.Width; //y = Math.Abs(w * Math.Tan(angle)); //determine what to offset double textX = edgeX; double textY = edgeY; double h = textSize.Height; bool lessThan45 = inDegrees < 45; switch (whichSideShapeIsOn) { case EntitySideType.OnBottom: textY -= TextPaddingY + h; if (lessThan45) { textX += TextPaddingX; } else { textX -= TextPaddingX + w; } break; case EntitySideType.OnTop: textY += TextPaddingY; if (lessThan45) { textX -= TextPaddingX + w; } else { textX += TextPaddingX; } break; case EntitySideType.OnLeft: textX += TextPaddingX; if (lessThan45) { textY -= TextPaddingY + h; } else { textY += TextPaddingY; } break; case EntitySideType.OnRight: textX -= TextPaddingX + w; if (lessThan45) { textY += TextPaddingY; } else { textY -= TextPaddingY + h; } break; } //perform the drawing g.DrawString(predicateText, font, brush, new PointF((float)textX, (float)textY)); #endregion pen.Color = restoreColor; } }
/// <summary> /// Gets the suggested connection points of this geometry. /// </summary> public abstract PointD[] GetGeometryConnectionPoints(IGeometryHost geometryHost);
/// <summary> /// Paint the solid crowsfoot on the end of an optional line /// </summary> protected override void DoPaintGeometry(DiagramPaintEventArgs e, IGeometryHost geometryHost) { EntityRelationshipBinaryMultiplicityDisplay displaySetting = OptionsPage.CurrentEntityRelationshipBinaryMultiplicityDisplay; RolePlayerLink connector; EdgePointCollection edgePoints; int edgePointCount; Pen pen; ShapeElement shapeHost; IOffsetBorderPoint offsetPointProvider; if (displaySetting == EntityRelationshipBinaryMultiplicityDisplay.Barker && RoleMultiplicity.ZeroToMany == (connector = (RolePlayerLink)geometryHost).GetDisplayRoleMultiplicity(displaySetting) && null != (edgePoints = connector.EdgePoints) && 1 < (edgePointCount = edgePoints.Count) && null != (shapeHost = connector.ToShape) && null != (offsetPointProvider = shapeHost.ShapeGeometry as IOffsetBorderPoint) && null != (pen = geometryHost.GeometryStyleSet.GetPen(DiagramPens.ConnectionLine))) { Color restoreColor = pen.Color; pen.Color = geometryHost.UpdateGeometryLuminosity(e.View, pen); PointD pointOnBorder = edgePoints[edgePointCount - 1].Point; double angle = GeometryUtility.CalculateRadiansRotationAngle(edgePoints[0].Point, pointOnBorder); PointD vertexPoint = pointOnBorder; vertexPoint.Offset(CrowsFootHeight * Math.Cos(angle), CrowsFootHeight * Math.Sin(angle)); e.Graphics.DrawLine(pen, PointD.ToPointF(vertexPoint), PointD.ToPointF(pointOnBorder)); PointD? offsetBorderPoint = offsetPointProvider.OffsetBorderPoint(shapeHost, pointOnBorder, vertexPoint, CrowsFootHalfWidth, CrowsFootParallelMode); if (offsetBorderPoint.HasValue) { e.Graphics.DrawLine(pen, PointD.ToPointF(vertexPoint), PointD.ToPointF(offsetBorderPoint.Value)); } offsetBorderPoint = offsetPointProvider.OffsetBorderPoint(shapeHost, pointOnBorder, vertexPoint, -CrowsFootHalfWidth, CrowsFootParallelMode); if (offsetBorderPoint.HasValue) { e.Graphics.DrawLine(pen, PointD.ToPointF(vertexPoint), PointD.ToPointF(offsetBorderPoint.Value)); } pen.Color = restoreColor; } base.DoPaintGeometry(e, geometryHost); }
/// <summary> /// Return a path modified to include any ER multiplicity decorators /// </summary> public override GraphicsPath GetPath(IGeometryHost geometryHost) { EntityRelationshipBinaryMultiplicityDisplay displaySetting = OptionsPage.CurrentEntityRelationshipBinaryMultiplicityDisplay; if (displaySetting == EntityRelationshipBinaryMultiplicityDisplay.Off) { return base.GetPath(geometryHost); } RolePlayerLink connector = (RolePlayerLink)geometryHost; EdgePointCollection edgePoints; int edgePointCount; RoleMultiplicity multiplicity; if (RoleMultiplicity.Unspecified != (multiplicity = connector.GetDisplayRoleMultiplicity(displaySetting)) && 1 < (edgePointCount = (edgePoints = connector.EdgePoints).Count)) { switch (displaySetting) { case EntityRelationshipBinaryMultiplicityDisplay.CrowsFootOnly: CrowsFootOnly: switch (multiplicity) { case RoleMultiplicity.OneToMany: case RoleMultiplicity.ZeroToMany: { PointD objectTypeEndPoint = edgePoints[edgePointCount - 1].Point; PointD roleBoxEndPoint = edgePoints[0].Point; double angle = GeometryUtility.CalculateRadiansRotationAngle(roleBoxEndPoint, objectTypeEndPoint); GraphicsPath path = base.UninitializedPath; path.Reset(); path.AddLine(PointD.ToPointF(roleBoxEndPoint), PointD.ToPointF(objectTypeEndPoint)); PointD vertexPoint = objectTypeEndPoint; vertexPoint.Offset(CrowsFootHeight * Math.Cos(angle), CrowsFootHeight * Math.Sin(angle)); ShapeElement shapeHost; IOffsetBorderPoint offsetPointProvider; if (null != (shapeHost = connector.ToShape) && null != (offsetPointProvider = shapeHost.ShapeGeometry as IOffsetBorderPoint)) { PointD? offsetBorderPoint = offsetPointProvider.OffsetBorderPoint(shapeHost, objectTypeEndPoint, vertexPoint, CrowsFootHalfWidth, CrowsFootParallelMode); if (offsetBorderPoint.HasValue) { path.StartFigure(); path.AddLine(PointD.ToPointF(vertexPoint), PointD.ToPointF(offsetBorderPoint.Value)); } offsetBorderPoint = offsetPointProvider.OffsetBorderPoint(shapeHost, objectTypeEndPoint, vertexPoint, -CrowsFootHalfWidth, CrowsFootParallelMode); if (offsetBorderPoint.HasValue) { path.StartFigure(); path.AddLine(PointD.ToPointF(vertexPoint), PointD.ToPointF(offsetBorderPoint.Value)); } } return path; } } break; case EntityRelationshipBinaryMultiplicityDisplay.Barker: // Stop short of the crows foot if optional, draw the broken line under the solid crows foot switch (multiplicity) { case RoleMultiplicity.OneToMany: // Single pen only, include the crowsfoot as part of the path goto CrowsFootOnly; case RoleMultiplicity.ZeroToMany: // Stop the path at the vertex point, use a different pen for the crowsfoot in DoPaintGeometry { PointD objectTypeEndPoint = edgePoints[edgePointCount - 1].Point; PointD roleBoxEndPoint = edgePoints[0].Point; double angle = GeometryUtility.CalculateRadiansRotationAngle(roleBoxEndPoint, objectTypeEndPoint); GraphicsPath path = base.UninitializedPath; path.Reset(); objectTypeEndPoint.Offset(CrowsFootHeight * Math.Cos(angle), CrowsFootHeight * Math.Sin(angle)); path.AddLine(PointD.ToPointF(roleBoxEndPoint), PointD.ToPointF(objectTypeEndPoint)); return path; } } break; case EntityRelationshipBinaryMultiplicityDisplay.InformationEngineering: { PointD objectTypeEndPoint = edgePoints[edgePointCount - 1].Point; PointD roleBoxEndPoint = edgePoints[0].Point; double angle = GeometryUtility.CalculateRadiansRotationAngle(roleBoxEndPoint, objectTypeEndPoint); double cosAngle = Math.Cos(angle); double sinAngle = Math.Sin(angle); GraphicsPath path = base.UninitializedPath; path.Reset(); switch (multiplicity) { case RoleMultiplicity.ExactlyOne: { path.AddLine(PointD.ToPointF(roleBoxEndPoint), PointD.ToPointF(objectTypeEndPoint)); path.StartFigure(); PointD oneMarkLeft = objectTypeEndPoint; oneMarkLeft.Offset(InfEngInnerOneMarkOffset * cosAngle, InfEngInnerOneMarkOffset * sinAngle); PointD oneMarkRight = oneMarkLeft; oneMarkLeft.Offset(-InfEngMarkerHalfWidth * sinAngle, InfEngMarkerHalfWidth * cosAngle); oneMarkRight.Offset(InfEngMarkerHalfWidth * sinAngle, -InfEngMarkerHalfWidth * cosAngle); path.AddLine(PointD.ToPointF(oneMarkLeft), PointD.ToPointF(oneMarkRight)); oneMarkLeft.Offset(InfEngOuterOneMarkOffset * cosAngle, InfEngOuterOneMarkOffset * sinAngle); oneMarkRight.Offset(InfEngOuterOneMarkOffset * cosAngle, InfEngOuterOneMarkOffset * sinAngle); path.StartFigure(); path.AddLine(PointD.ToPointF(oneMarkLeft), PointD.ToPointF(oneMarkRight)); break; } case RoleMultiplicity.ZeroToOne: { PointD circleStart = objectTypeEndPoint; circleStart.Offset((InfEngInnerOneMarkOffset + InfEngOuterOneMarkOffset + InfEngMarkerHalfWidth + InfEngMarkerHalfWidth) * cosAngle, (InfEngInnerOneMarkOffset + InfEngOuterOneMarkOffset + InfEngMarkerHalfWidth + InfEngMarkerHalfWidth) * sinAngle); path.AddLine(PointD.ToPointF(roleBoxEndPoint), PointD.ToPointF(circleStart)); circleStart.Offset(-InfEngMarkerHalfWidth * cosAngle, -InfEngMarkerHalfWidth * sinAngle); path.StartFigure(); path.AddArc( (float)(circleStart.X - InfEngMarkerHalfWidth), (float)(circleStart.Y - InfEngMarkerHalfWidth), (float)(InfEngMarkerHalfWidth + InfEngMarkerHalfWidth), (float)(InfEngMarkerHalfWidth + InfEngMarkerHalfWidth), 0, 360); circleStart.Offset(-InfEngMarkerHalfWidth * cosAngle, -InfEngMarkerHalfWidth * sinAngle); path.StartFigure(); path.AddLine(PointD.ToPointF(circleStart), PointD.ToPointF(objectTypeEndPoint)); circleStart.Offset(-InfEngOuterOneMarkOffset * cosAngle, -InfEngOuterOneMarkOffset * sinAngle); PointD oneMarkRight = circleStart; circleStart.Offset(-InfEngMarkerHalfWidth * sinAngle, InfEngMarkerHalfWidth * cosAngle); oneMarkRight.Offset(InfEngMarkerHalfWidth * sinAngle, -InfEngMarkerHalfWidth * cosAngle); path.StartFigure(); path.AddLine(PointD.ToPointF(circleStart), PointD.ToPointF(oneMarkRight)); break; } case RoleMultiplicity.ZeroToMany: { PointD circleStart = objectTypeEndPoint; circleStart.Offset((CrowsFootHeight + InfEngMarkerHalfWidth + InfEngMarkerHalfWidth) * cosAngle, (CrowsFootHeight + InfEngMarkerHalfWidth + InfEngMarkerHalfWidth) * sinAngle); path.AddLine(PointD.ToPointF(roleBoxEndPoint), PointD.ToPointF(circleStart)); circleStart.Offset(-InfEngMarkerHalfWidth * cosAngle, -InfEngMarkerHalfWidth * sinAngle); path.StartFigure(); path.AddArc( (float)(circleStart.X - InfEngMarkerHalfWidth), (float)(circleStart.Y - InfEngMarkerHalfWidth), (float)(InfEngMarkerHalfWidth + InfEngMarkerHalfWidth), (float)(InfEngMarkerHalfWidth + InfEngMarkerHalfWidth), 0, 360); circleStart.Offset(-InfEngMarkerHalfWidth * cosAngle, -InfEngMarkerHalfWidth * sinAngle); path.StartFigure(); path.AddLine(PointD.ToPointF(circleStart), PointD.ToPointF(objectTypeEndPoint)); ShapeElement shapeHost; IOffsetBorderPoint offsetPointProvider; if (null != (shapeHost = connector.ToShape) && null != (offsetPointProvider = shapeHost.ShapeGeometry as IOffsetBorderPoint)) { path.StartFigure(); path.AddLine(PointD.ToPointF(circleStart), PointD.ToPointF(objectTypeEndPoint)); PointD? offsetBorderPoint = offsetPointProvider.OffsetBorderPoint(shapeHost, objectTypeEndPoint, circleStart, CrowsFootHalfWidth, CrowsFootParallelMode); if (offsetBorderPoint.HasValue) { path.StartFigure(); path.AddLine(PointD.ToPointF(circleStart), PointD.ToPointF(offsetBorderPoint.Value)); } offsetBorderPoint = offsetPointProvider.OffsetBorderPoint(shapeHost, objectTypeEndPoint, circleStart, -CrowsFootHalfWidth, CrowsFootParallelMode); if (offsetBorderPoint.HasValue) { path.StartFigure(); path.AddLine(PointD.ToPointF(circleStart), PointD.ToPointF(offsetBorderPoint.Value)); } } break; } case RoleMultiplicity.OneToMany: { path.AddLine(PointD.ToPointF(roleBoxEndPoint), PointD.ToPointF(objectTypeEndPoint)); PointD vertexPoint = objectTypeEndPoint; vertexPoint.Offset(CrowsFootHeight * cosAngle, CrowsFootHeight * sinAngle); // Add the one mark PointD oneMarkLeft = vertexPoint; oneMarkLeft.Offset(InfEngOuterOneMarkOffset * cosAngle, InfEngOuterOneMarkOffset * sinAngle); PointD oneMarkRight = oneMarkLeft; oneMarkLeft.Offset(-InfEngMarkerHalfWidth * sinAngle, InfEngMarkerHalfWidth * cosAngle); oneMarkRight.Offset(InfEngMarkerHalfWidth * sinAngle, -InfEngMarkerHalfWidth * cosAngle); path.StartFigure(); path.AddLine(PointD.ToPointF(oneMarkLeft), PointD.ToPointF(oneMarkRight)); ShapeElement shapeHost; IOffsetBorderPoint offsetPointProvider; if (null != (shapeHost = connector.ToShape) && null != (offsetPointProvider = shapeHost.ShapeGeometry as IOffsetBorderPoint)) { PointD? offsetBorderPoint = offsetPointProvider.OffsetBorderPoint(shapeHost, objectTypeEndPoint, vertexPoint, CrowsFootHalfWidth, CrowsFootParallelMode); if (offsetBorderPoint.HasValue) { path.StartFigure(); path.AddLine(PointD.ToPointF(vertexPoint), PointD.ToPointF(offsetBorderPoint.Value)); } offsetBorderPoint = offsetPointProvider.OffsetBorderPoint(shapeHost, objectTypeEndPoint, vertexPoint, -CrowsFootHalfWidth, CrowsFootParallelMode); if (offsetBorderPoint.HasValue) { path.StartFigure(); path.AddLine(PointD.ToPointF(vertexPoint), PointD.ToPointF(offsetBorderPoint.Value)); } } break; } } return path; } } } return base.GetPath(geometryHost); }
/// <summary> /// Gets the suggested connection points of this geometry. /// </summary> /// <value></value> public override PointD[] GetGeometryConnectionPoints(IGeometryHost geometryHost) { return(null); }
/// <summary> /// Override of DoHitTest so it works with non-rectilinear line segments /// </summary> public override bool DoHitTest(IGeometryHost geometryHost, PointD hitPoint, DiagramHitTestInfo hitTestInfo, bool includeTolerance) { bool retVal = false; LineSegment hitSegment = null; AnchorPoint anchorPoint = null; // In DSL Tools v8.2, GetHitTestTolerance is an instance method, but in DSL Tools v9.0, it is a static method. // We call it without a qualifier here so that it will compile against both versions. SizeD tolerance = GetHitTestTolerance(hitTestInfo); RectangleD perimeter = this.GetPerimeterBoundingBox(geometryHost); perimeter.Inflate(tolerance); if (perimeter.Contains(hitPoint)) { LineSegment[] segments = this.CalculateSegments(geometryHost, hitTestInfo); int segmentCount = segments.Length; for (int i = 0; i < segmentCount; ++i) { LineSegment testSegment = segments[i]; RectangleD testBounds = GeometryHelpers.RectangleDFrom2Pts(testSegment.StartPoint, testSegment.EndPoint); testBounds.Inflate(tolerance); if (testBounds.Contains(hitPoint)) { anchorPoint = TestHitAnchor(geometryHost as BinaryLinkShape, testSegment, tolerance, hitPoint); if (anchorPoint != null) { retVal = true; hitSegment = testSegment; break; } double distance = DistanceFromPointToLine(hitPoint, testSegment.StartPoint, testSegment.EndPoint, true); if (!double.IsNaN(distance) && distance < (tolerance.Width + geometryHost.GeometryStyleSet.GetPen(geometryHost.GeometryOutlinePenId).Width / 2f)) { retVal = true; hitSegment = testSegment; break; } } } } if (hitTestInfo != null) { DiagramItem diagramItem; if (retVal) { if (anchorPoint == null) { // In DSL Tools v8.2, CreateDiagramItem is an instance method, but in DSL Tools v9.0, it is a static method. // We call it without a qualifier here so that it will compile against both versions. diagramItem = CreateDiagramItem(geometryHost, hitSegment); } else { diagramItem = new DiagramItem(geometryHost as LinkShape, hitSegment, anchorPoint); } } else { diagramItem = null; } hitTestInfo.HitDiagramItem = diagramItem; hitTestInfo.HitGrabHandle = null; } return(retVal); }
/// <summary> /// Replacement for BinaryLinkShapeGeometry.DoPaintGeometry /// </summary> protected override void DoPaintGeometry(DiagramPaintEventArgs e, IGeometryHost geometryHost) { LinkShape linkShape; IJumpFreeLinkShape jumpFreeShape; VGObjectLineJumpCode expectedJumpCode; if (null != (jumpFreeShape = geometryHost as IJumpFreeLinkShape) && null != (linkShape = geometryHost as LinkShape) && linkShape.RouteJumpType != (expectedJumpCode = (jumpFreeShape.IsJumpFree ? VGObjectLineJumpCode.VGObjectJumpCodeNever : VGObjectLineJumpCode.VGObjectJumpCodePage))) { // Backup plan, sometimes we can't set this during configuration, and // it tends to revert in undo/redo scenarios when the backing graph // wrapper is not preserved. linkShape.RouteJumpType = expectedJumpCode; } Graphics g = e.Graphics; GraphicsPath path = this.GetPath(geometryHost); StyleSetResourceId penId = GetOutlinePenId(geometryHost); Pen pen = geometryHost.GeometryStyleSet.GetPen(penId); if ((path != null) && (pen != null)) { IDynamicColorGeometryHost dynamicColors = geometryHost as IDynamicColorGeometryHost; if (this.HasOutline(geometryHost)) { Color restoreColor; if (null == (dynamicColors = geometryHost as IDynamicColorGeometryHost) || (restoreColor = dynamicColors.UpdateDynamicColor(penId, pen)).IsEmpty) { restoreColor = geometryHost.UpdateGeometryLuminosity(e.View, pen); } else { geometryHost.UpdateGeometryLuminosity(e.View, pen); } GeometryUtility.SafeDrawPath(g, pen, path); pen.Color = restoreColor; } IBinaryLinkGeometryData hostData; EdgePointCollection edgePoints; int edgePointCount; if (null != (hostData = geometryHost as IBinaryLinkGeometryData) && null != (edgePoints = hostData.GeometryEdgePoints) && 1 < (edgePointCount = edgePoints.Count)) { float rotationAngle = 0f; LinkDecorator decorator; if (null != (decorator = hostData.GeometryDecoratorFrom)) { rotationAngle = CalculateRotationAngle(edgePoints[0].Point, edgePoints[1].Point); DrawDecorator(e, geometryHost, rotationAngle, edgePoints[0].Point, decorator #if VISUALSTUDIO_10_0 , hostData.GeometryDecoratorFromSize #endif ); } if (null != (decorator = hostData.GeometryDecoratorTo)) { rotationAngle = CalculateRotationAngle(edgePoints[edgePointCount - 1].Point, edgePoints[edgePointCount - 2].Point); DrawDecorator(e, geometryHost, rotationAngle, edgePoints[edgePointCount - 1].Point, decorator #if VISUALSTUDIO_10_0 , hostData.GeometryDecoratorToSize #endif ); } } } }
/// <summary> /// Gets the suggested connection points of this geometry. /// </summary> public abstract PointD[] GetGeometryConnectionPoints(IGeometryHost geometryHost);
/// <summary> /// Don't let the lines interfere with clicking on other items /// </summary> public override bool DoHitTest(IGeometryHost geometryHost, PointD hitPoint, DiagramHitTestInfo hitTestInfo, bool includeTolerance) { return false; }
/// <summary> /// Provide custom shape folding for rectangular fact types /// </summary> /// <param name="geometryHost">The host view</param> /// <param name="potentialPoint">A point on the rectangular boundary of the shape</param> /// <param name="vectorEndPoint">A point on the opposite end of the connecting line</param> /// <returns>A point on the rounded rectangle border</returns> public override PointD DoFoldToShape(IGeometryHost geometryHost, PointD potentialPoint, PointD vectorEndPoint) { // Get an endpoint we can work with NodeShape oppositeShape; vectorEndPoint = GeometryUtility.AdjustVectorEndPoint(geometryHost, vectorEndPoint, out oppositeShape); PointD? customPoint = GeometryUtility.DoCustomFoldShape(geometryHost, vectorEndPoint, oppositeShape); if (customPoint.HasValue) { return customPoint.Value; } vectorEndPoint = GeometryUtility.ResolveProxyConnectorVectorEndPoint(vectorEndPoint, oppositeShape); // This is used for center to center routing, so the potential point is the // center of the shape. We need to see where a line through the center intersects // the rectangle border and return relative coordinates. RectangleD bounds = geometryHost.TranslateGeometryToAbsoluteBounds(geometryHost.GeometryBoundingBox); PointD center = bounds.Center; vectorEndPoint.Offset(-center.X, -center.Y); bool negativeX = vectorEndPoint.X < 0; bool negativeY = vectorEndPoint.Y < 0; if (VGConstants.FuzzZero(vectorEndPoint.X, VGConstants.FuzzDistance)) { // Vertical line, skip slope calculations return new PointD(bounds.Width / 2, negativeY ? 0 : bounds.Height); } else if (VGConstants.FuzzZero(vectorEndPoint.Y, VGConstants.FuzzDistance)) { // Horizontal line, skip slope calculations return new PointD(negativeX ? 0 : bounds.Width, bounds.Height / 2); } else { double slope = vectorEndPoint.Y / vectorEndPoint.X; // The intersecting line equation is y = mx. We can tell // whether to use the vertical or horizontal lines by // comparing the relative sizes of the rectangle sides // with the slope double x; double y; double r = Radius; double halfHeight = bounds.Height / 2; double halfWidth = bounds.Width / 2; bool cornerHit; if (Math.Abs(slope) < (bounds.Height / bounds.Width)) { // Attach to left/right edges // Intersect with line x = +/- bounds.Width / 2 x = halfWidth; if (negativeX) { x = -x; } y = x * slope; cornerHit = Math.Abs(y) > (halfHeight - r); } else { // Attach to top/bottom edges // Intersect with line y = +/- bounds.Height / 2 y = halfHeight; if (negativeY) { y = -y; } x = y / slope; cornerHit = Math.Abs(x) > (halfWidth - r); } if (cornerHit) { // The equation here is significantly more complicated than // other shapes because of the off center circle, which is // centered at (ccx, ccy) in these equations. The raw equations are: // (x - ccx)^2 + (y - ccy)^2 = r^2 // y = m*x where m is the slope // Solving for x gives (algebra is non-trivial and ommitted): // v1 = 1 + m*m // v2 = m*ccx + ccy // v3 = sqrt(r^2*v1 - v2^2) // x = (+/-v3 + ccx-m*ccy)/v1 // Note that picking the correct center is absolutely necessary. // Unlike the other shapes, where all lines will pass the ellipse/circle // at some point, the off-center circle means that choosing the wrong // center will result in an unsolvable equation (taking the square // root will throw). double ccx = halfWidth - r; // Corner center x value double ccy = halfHeight - r; // Corner center y value bool useNegativeSquareRoot = false; if (negativeX) // Left quadrants { ccx = -ccx; useNegativeSquareRoot = true; if (!negativeY) { // Lower left quadrant ccy = -ccy; } } else if (!negativeY) // Right quadrants { // Lower right quadrant ccy = -ccy; } double v1 = 1 + slope * slope; double v2 = slope * ccx + ccy; double v3 = Math.Sqrt(r * r * v1 - v2 * v2); if (useNegativeSquareRoot) { v3 = -v3; } x = (v3 + ccx - slope * ccy) / v1; y = slope * x; } return new PointD(x + halfWidth, y + halfHeight); } }
/// <summary> /// Replacement for BinaryLinkShapeGeometry.DoPaintGeometry /// </summary> protected override void DoPaintGeometry(DiagramPaintEventArgs e, IGeometryHost geometryHost) { LinkShape linkShape = geometryHost as LinkShape; if (linkShape != null && linkShape.RouteJumpType != VGObjectLineJumpCode.VGObjectJumpCodeNever) { // Backup plan, sometimes we can't set this during configuration, and // it tends to revert in undo/redo scenarios when the backing graph // wrapper is not preserved. linkShape.RouteJumpType = VGObjectLineJumpCode.VGObjectJumpCodeNever; } Graphics g = e.Graphics; GraphicsPath path = this.GetPath(geometryHost); StyleSetResourceId penId = GetOutlinePenId(geometryHost); Pen pen = geometryHost.GeometryStyleSet.GetPen(penId); if ((path != null) && (pen != null)) { IDynamicColorGeometryHost dynamicColors = geometryHost as IDynamicColorGeometryHost; if (this.HasOutline(geometryHost)) { Color restoreColor; if (null == (dynamicColors = geometryHost as IDynamicColorGeometryHost) || (restoreColor = dynamicColors.UpdateDynamicColor(penId, pen)).IsEmpty) { restoreColor = geometryHost.UpdateGeometryLuminosity(e.View, pen); } else { geometryHost.UpdateGeometryLuminosity(e.View, pen); } GeometryUtility.SafeDrawPath(g, pen, path); pen.Color = restoreColor; } IBinaryLinkGeometryData hostData; EdgePointCollection edgePoints; int edgePointCount; if (null != (hostData = geometryHost as IBinaryLinkGeometryData) && null != (edgePoints = hostData.GeometryEdgePoints) && 1 < (edgePointCount = edgePoints.Count)) { float rotationAngle = 0f; LinkDecorator decorator; if (null != (decorator = hostData.GeometryDecoratorFrom)) { rotationAngle = CalculateRotationAngle(edgePoints[0].Point, edgePoints[1].Point); DrawDecorator(e, geometryHost, rotationAngle, edgePoints[0].Point, decorator #if VISUALSTUDIO_10_0 , hostData.GeometryDecoratorFromSize #endif ); } if (null != (decorator = hostData.GeometryDecoratorTo)) { rotationAngle = CalculateRotationAngle(edgePoints[edgePointCount - 1].Point, edgePoints[edgePointCount - 2].Point); DrawDecorator(e, geometryHost, rotationAngle, edgePoints[edgePointCount - 1].Point, decorator #if VISUALSTUDIO_10_0 , hostData.GeometryDecoratorToSize #endif ); } } } }
/// <summary> /// Replacement for LinkShapeGeometry.DrawDecorator /// </summary> #if VISUALSTUDIO_10_0 protected static new void DrawDecorator(DiagramPaintEventArgs e, IGeometryHost geometryHost, float rotation, PointD centerRight, LinkDecorator decorator, SizeD size) {
/// <summary> /// Draws the shape's shadow. /// </summary> /// <remarks> /// The shadow is replaced with a glow. /// </remarks> protected override void DoPaintShadow(DiagramPaintEventArgs e, IGeometryHost geometryHost) { Guard.NotNull(() => geometryHost, geometryHost); Guard.NotNull(() => e, e); Graphics graphics = e.Graphics; GraphicsState state = graphics.Save(); SizeD shadowOffset = this.ShadowOffset; try { GraphicsPath shapePath = this.GetPath(geometryHost); RectangleF shapeRectangle = shapePath.GetBounds(); // Create shadow path GraphicsPath shadowPath = shapePath.Clone() as GraphicsPath; // Enlarge the shadow (by fixed magnifier amount) using (Matrix scaleMatrix = new Matrix()) { scaleMatrix.Scale( (ShadowMagnifier / shapeRectangle.Width) + 1, (ShadowMagnifier / shapeRectangle.Height) + 1); shadowPath.Transform(scaleMatrix); // Center shadow back on the shape RectangleF shadowRectangle = shadowPath.GetBounds(); scaleMatrix.Reset(); scaleMatrix.Translate( -((shadowRectangle.X + (shadowRectangle.Width / 2)) - (shapeRectangle.X + (shapeRectangle.Width / 2))), -((shadowRectangle.Y + (shadowRectangle.Height / 2)) - (shapeRectangle.Y + (shapeRectangle.Height / 2)))); shadowPath.Transform(scaleMatrix); } // Set the clip region (on the shape) using (Region clip = graphics.Clip) { graphics.SetClip(shapePath); // Offset the shadow path (move diagonally down-right) from shape using (Matrix translateMatrix = new Matrix()) { translateMatrix.Translate((float)shadowOffset.Width, (float)shadowOffset.Height); shadowPath.Transform(translateMatrix); } // Mask-off the shadow from the original shape graphics.SetClip(shadowPath, CombineMode.Complement); graphics.SetClip(clip, CombineMode.Intersect); // Fill the shadow using (PathGradientBrush shadowBrush = new PathGradientBrush(shadowPath)) { shadowBrush.CenterColor = Color.FromArgb(ShadowColorOpacity, ShadowColor); shadowBrush.SurroundColors = new Color[] { Color.Transparent }; shadowBrush.FocusScales = new PointF(ShadowGradientFocalPoint, ShadowGradientFocalPoint); graphics.FillPath(shadowBrush, shadowPath); graphics.ResetClip(); } } } finally { graphics.Restore(state); } }
protected static new void DrawDecorator(DiagramPaintEventArgs e, IGeometryHost geometryHost, float rotation, PointD centerRight, LinkDecorator decorator) { SizeD size = LinkShapeGeometry.SizeDecorator; #endif double offsetBy = 0d; bool doOffset = false; ILinkDecoratorSettings settings = decorator as ILinkDecoratorSettings; if (settings != null) { size = settings.DecoratorSize; offsetBy = settings.OffsetBy; doOffset = !VGConstants.FuzzZero(offsetBy, VGConstants.FuzzDistance); } Graphics g = e.Graphics; RectangleD boundingRect = new RectangleD(centerRight.X - size.Width, centerRight.Y - (size.Height / 2), size.Width, size.Height); Matrix rotationMatrix = g.Transform; float offsetX = 0f; float offsetY = 0f; if (doOffset) { double rotationRadians = rotation * Math.PI / 180; offsetX = (float)(offsetBy * Math.Cos(rotationRadians)); offsetY = (float)(offsetBy * Math.Sin(rotationRadians)); rotationMatrix.Translate(offsetX, offsetY); } rotationMatrix.RotateAt(rotation, PointD.ToPointF(centerRight)); g.Transform = rotationMatrix; decorator.DoPaintShape(boundingRect, geometryHost, e); rotationMatrix.RotateAt(-rotation, PointD.ToPointF(centerRight)); if (doOffset) { rotationMatrix.Translate(-offsetX, -offsetY); } g.Transform = rotationMatrix; }
/// <summary> /// Gets the suggested connection points of this geometry. /// </summary> /// <value></value> public override PointD[] GetGeometryConnectionPoints(IGeometryHost geometryHost) { return null; }
/// <summary> /// Implements <see cref="IOffsetBorderPoint.OffsetBorderPoint"/> /// </summary> protected PointD? OffsetBorderPoint(IGeometryHost geometryHost, PointD borderPoint, PointD outsidePoint, double offset, bool parallelVector) { double angle = GeometryUtility.CalculateRadiansRotationAngle(outsidePoint, borderPoint); // Get the sample point PointD samplePoint = borderPoint; samplePoint.Offset(-offset * Math.Sin(angle), offset * Math.Cos(angle)); // Figure out the slope, either parallel to the incoming line, or pointed at the outside point PointD slopeThrough = parallelVector ? borderPoint : samplePoint; // Translate the rectangle to the origin RectangleD bounds = geometryHost.GeometryBoundingBox; PointD hostCenter = bounds.Center; samplePoint.Offset(-hostCenter.X, -hostCenter.Y); double px = samplePoint.X; double py = samplePoint.Y; double solvedX; double solvedY; if (VGConstants.FuzzEqual(slopeThrough.X, outsidePoint.X, VGConstants.FuzzDistance)) { // Vertical line, hit the same edge as the border point if (Math.Abs(px) > (bounds.Width / 2 + VGConstants.FuzzDistance)) { // Line hits outside rectangle return null; } solvedX = px; solvedY = borderPoint.Y - hostCenter.Y; } else if (VGConstants.FuzzEqual(slopeThrough.Y, outsidePoint.Y, VGConstants.FuzzDistance)) { // Horizontal line, hit the same edge as the border point if (Math.Abs(py) > (bounds.Height / 2 + VGConstants.FuzzDistance)) { // Line hits outside rectangle return null; } solvedY = py; solvedX = borderPoint.X - hostCenter.X; } else { int hitCount = 0; solvedX = 0; solvedY = 0; double solvedXAlternate = 0; double solvedYAlternate = 0; // We've already checked vertical and horizontal lines, so we know the lines will intersect either // zero or two sides of the rectangle. Find the two sides. // The intersecting line equation is y = m(x - px) + py (solved for y) or x = 1/m(y-py) + px (solved for x) // The rectangle borders are y = halfHeight, y = -halfHeight, x = halfWidth, x = -halfWidth double halfWidth = bounds.Width / 2; double halfHeight = bounds.Height / 2; double slope = (outsidePoint.Y - slopeThrough.Y) / (outsidePoint.X - slopeThrough.X); double inverseSlope = 1 / slope; double testIntersect; // Top edge testIntersect = inverseSlope * (halfHeight - py) + px; if (Math.Abs(testIntersect) < (halfWidth + VGConstants.FuzzDistance)) { solvedX = testIntersect; solvedY = halfHeight; hitCount = 1; } // Bottom edge testIntersect = inverseSlope * (-halfHeight - py) + px; if (Math.Abs(testIntersect) < (halfWidth + VGConstants.FuzzDistance)) { if (hitCount == 1) { solvedXAlternate = testIntersect; solvedYAlternate = -halfHeight; } else { solvedX = testIntersect; solvedY = -halfHeight; } ++hitCount; } // Right edge if (hitCount != 2) { testIntersect = slope * (halfWidth - px) + py; if (Math.Abs(testIntersect) < (halfHeight + VGConstants.FuzzDistance)) { if (hitCount == 1) { solvedYAlternate = testIntersect; solvedXAlternate = halfWidth; } else { solvedY = testIntersect; solvedX = halfWidth; } ++hitCount; } } // Left edge if (hitCount != 2) { testIntersect = slope * (-halfWidth - px) + py; if (Math.Abs(testIntersect) < (halfHeight + VGConstants.FuzzDistance)) { if (hitCount == 1) { solvedYAlternate = testIntersect; solvedXAlternate = -halfWidth; } else { solvedY = testIntersect; solvedX = -halfWidth; } ++hitCount; } } // Choose the best match and translate the point back out if (hitCount == 2) { // Find the point closest to the sample point double xDif = px - solvedX; double yDif = py - solvedY; double xDifAlternate = px - solvedXAlternate; double yDifAlternate = py - solvedYAlternate; if ((xDif * xDif + yDif * yDif) > (xDifAlternate * xDifAlternate + yDifAlternate * yDifAlternate)) { solvedX = solvedXAlternate; solvedY = solvedYAlternate; } } else { // Unsolvable return null; } } return new PointD(solvedX + hostCenter.X, solvedY + hostCenter.Y); }