Пример #1
0
		/// <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);
			}
		}
Пример #2
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);
			}
		}
Пример #3
0
		/// <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&lt;PointD&gt; 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;
		}
Пример #4
0
		/// <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
		}
Пример #5
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);
			}
		}
Пример #6
0
		/// <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;
		}
Пример #7
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);
			}
		}