예제 #1
0
		/// <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);
			}
		}
예제 #2
0
		/// <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);
		}
예제 #3
0
		/// <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);
			}
		}
예제 #4
0
		/// <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);
			}
		}