protected override void UpdateConnectorOffset() { if (this.IsVisible && this.RenderSize.Width > 0 && this.PlacementTarget?.IsVisible == true) { if (!this.IsLoaded) { this.Dispatcher.Invoke(this.UpdateConnectorOffset, DispatcherPriority.Loaded); return; } var selfRect = new Rect(new Point(0, 0).ToScreen(this), this.RenderSize).ToScreen(this); var ellipse = new Ellipse(selfRect); var targetRect = new Rect(new Point(0, 0).ToScreen(this.PlacementTarget), this.PlacementTarget.RenderSize).ToScreen(this); var tp = this.PlacementOptions?.GetPointOnTarget(selfRect, targetRect); if (tp == null || ellipse.Contains(tp.Value)) { this.InvalidateProperty(ConnectorOffsetProperty); return; } if (ellipse.Contains(tp.Value)) { this.SetCurrentValue(ConnectorOffsetProperty, new Vector(0, 0)); return; } var mp = ellipse.CenterPoint; var ip = new Ray(mp, mp.VectorTo(tp.Value)).FirstIntersectionWith(ellipse); Debug.Assert(ip != null, "Did not find an intersection, bug in the library"); if (ip == null) { // failing silently in release this.InvalidateProperty(ConnectorOffsetProperty); } else { var v = tp.Value - ip.Value; // ReSharper disable once CompareOfFloatsByEqualityOperator if (this.PlacementOptions != null && v.Length > 0 && this.PlacementOptions.Offset != 0) { v = v - this.PlacementOptions.Offset * v.Normalized(); } this.SetCurrentValue(ConnectorOffsetProperty, v); } } else { this.InvalidateProperty(ConnectorOffsetProperty); } }
private static Point FindTangentPoint(Ray toCenter, Ellipse ellipse) { var toEllipseCenter = toCenter.PerpendicularLineTo(ellipse.CenterPoint); Debug.Assert(toEllipseCenter != null, "Ray should not go through ellipse center here"); if (toEllipseCenter == null) { // this should never happen but failing silently // the balloons should not throw much returning random point. return ellipse.CenterPoint; } return ellipse.PointOnCircumference(toEllipseCenter.Value.Direction.Negated()); }
internal static Point Find(Ray ray, double angle, double strokeThickness, Ellipse ellipse) { return Find(ray.Rotate(angle), strokeThickness, ellipse); }
private static Point Find(Ray ray, double strokeThickness, Ellipse ellipse) { var ip = ray.FirstIntersectionWith(ellipse); if (ip != null) { return ip.Value + strokeThickness * ray.Direction; } return FindTangentPoint(ray, ellipse); }
// http://www.mare.ee/indrek/misc/2d.pdf internal Point? FirstIntersectionWith(Ellipse ellipse) { if (Math.Abs(ellipse.CenterPoint.X) < Constants.Tolerance && Math.Abs(ellipse.CenterPoint.Y) < Constants.Tolerance) { return FirstIntersectionWithEllipseCenteredAtOrigin(this.Point, this.Direction, ellipse.RadiusX, ellipse.RadiusY); } var offset = new Point(0, 0).VectorTo(ellipse.CenterPoint); var ip = FirstIntersectionWithEllipseCenteredAtOrigin(this.Point - offset, this.Direction, ellipse.RadiusX, ellipse.RadiusY); return ip + offset; }