protected override Geometry GetOrCreateConnectorGeometry(Size renderSize)
        {
            var ellipse = Ellipse.CreateFromSize(renderSize);
            this.SetCurrentValue(EllipseProperty, ellipse);
            if (ellipse.IsZero)
            {
                return Geometry.Empty;
            }

            var direction = this.ConnectorOffset;
            var ip = ellipse.PointOnCircumference(direction);
            var vertexPoint = ip + this.ConnectorOffset;
            var ray = new Ray(vertexPoint, this.ConnectorOffset.Negated());

            var p1 = ConnectorPoint.Find(ray, this.ConnectorAngle / 2, this.StrokeThickness, ellipse);
            var p2 = ConnectorPoint.Find(ray, -this.ConnectorAngle / 2, this.StrokeThickness, ellipse);

            this.SetCurrentValue(ConnectorVertexPointProperty, vertexPoint);
            this.SetCurrentValue(ConnectorPoint1Property, p1);
            this.SetCurrentValue(ConnectorPoint2Property, p2);
            if (this.ConnectorGeometry is PathGeometry)
            {
                return this.ConnectorGeometry;
            }

            var figure = this.CreatePathFigureStartingAt(ConnectorPoint1Property);
            figure.Segments.Add(this.CreateLineSegmentTo(ConnectorVertexPointProperty));
            figure.Segments.Add(this.CreateLineSegmentTo(ConnectorPoint2Property));
            var geometry = new PathGeometry();
            geometry.Figures.Add(figure);
            return geometry;
        }
        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);
            }
        protected override Geometry GetOrCreateConnectorGeometry(Size renderSize)
        {
            var rectangle = new Rect(new Point(0, 0), renderSize);
            if (rectangle.Width <= 0 || rectangle.Height <= 0)
            {
                return Geometry.Empty;
            }

            var fromCenter = new Ray(rectangle.CenterPoint(), this.ConnectorOffset);
            var ip = fromCenter.FirstIntersectionWith(rectangle);
            if (ip == null)
            {
                Debug.Assert(false, $"Line {fromCenter} does not intersect rectangle {rectangle}");

                // ReSharper disable once HeuristicUnreachableCode
                return Geometry.Empty;
            }

            var cr = this.AdjustedCornerRadius();
            var vertexPoint = ip.Value + this.ConnectorOffset;
            var toCenter = new Ray(vertexPoint, this.ConnectorOffset.Negated());
            var p1 = ConnectorPoint.Find(toCenter, this.ConnectorAngle / 2, this.StrokeThickness, rectangle, cr);
            var p2 = ConnectorPoint.Find(toCenter, -this.ConnectorAngle / 2, this.StrokeThickness, rectangle, cr);
            this.SetCurrentValue(ConnectorVertexPointProperty, vertexPoint);
            this.SetCurrentValue(ConnectorPoint1Property, p1);
            this.SetCurrentValue(ConnectorPoint2Property, p2);
            if (this.ConnectorGeometry is PathGeometry)
            {
                return this.ConnectorGeometry;
            }

            var figure = this.CreatePathFigureStartingAt(ConnectorPoint1Property);
            figure.Segments.Add(this.CreateLineSegmentTo(ConnectorVertexPointProperty));
            figure.Segments.Add(this.CreateLineSegmentTo(ConnectorPoint2Property));
            var geometry = new PathGeometry();
            geometry.Figures.Add(figure);
            return geometry;
        }
            private static Point FindTangentPoint(Ray ray, Rect rectangle, CornerRadius cornerRadius)
            {
                var toMid = ray.PerpendicularLineTo(rectangle.CenterPoint());
                Debug.Assert(toMid != null, "Cannot find tangent if line goes through center");
                if (toMid == null)
                {
                    // failing silently in release
                    return rectangle.CenterPoint();
                }

                // Debug.Assert(!rectangle.Contains(toMid.Value.StartPoint), "Cannot find tangent if line intersects rectangle");
                if (toMid.Value.Direction.Axis() != null)
                {
                    return ray.Point.Closest(rectangle.TopLeft, rectangle.TopRight, rectangle.BottomRight, rectangle.BottomLeft);
                }

                Circle corner;
                switch (toMid.Value.Direction.Quadrant())
                {
                    case Quadrant.NegativeXPositiveY:
                        corner = CreateTopRight(rectangle.TopRight, cornerRadius.TopRight);
                        break;
                    case Quadrant.PositiveXPositiveY:
                        corner = CreateTopLeft(rectangle.TopLeft, cornerRadius.TopLeft);
                        break;
                    case Quadrant.PositiveXNegativeY:
                        corner = CreateBottomLeft(rectangle.BottomLeft, cornerRadius.BottomLeft);
                        break;
                    case Quadrant.NegativeXNegativeY:
                        corner = CreateBottomRight(rectangle.BottomRight, cornerRadius.BottomRight);
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }

                // ReSharper disable once CompareOfFloatsByEqualityOperator
                if (corner.Radius == 0)
                {
                    return corner.Center;
                }

                var lineToCenter = ray.PerpendicularLineTo(corner.Center);
                Debug.Assert(lineToCenter != null, "Ray cannot go through center here");
                if (lineToCenter == null)
                {
                    // this should never happen but failing silently
                    // the balloons should not throw much.
                    return corner.Center;
                }

                return corner.Center - corner.Radius * lineToCenter.Value.Direction;
            }
            private static Point FindForRotated(Ray ray, double strokeThickness, Rect rectangle, CornerRadius cornerRadius)
            {
                var ip = ray.FirstIntersectionWith(rectangle);
                if (ip == null)
                {
                    return FindTangentPoint(ray, rectangle, cornerRadius);
                }

                Circle corner;
                if (TryGetCorner(ip.Value, rectangle, cornerRadius, out corner))
                {
                    ip = ray.FirstIntersectionWith(corner);
                    if (ip == null)
                    {
                        return FindTangentPoint(ray, rectangle, cornerRadius);
                    }

                    return ip.Value + strokeThickness * ray.Direction;
                }

                return ip.Value + strokeThickness * ray.Direction;
            }
 internal static Point Find(Ray toCenter, double angle, double strokeThickness, Rect rectangle, CornerRadius cornerRadius)
 {
     var rotated = toCenter.Rotate(angle);
     return FindForRotated(rotated, strokeThickness, rectangle, cornerRadius);
 }
Example #10
0
        // http://geomalgorithms.com/a05-_intersect-1.html#intersect2D_2Segments()
        private static Point? IntersectionPoint(Ray ray, Line l2, bool mustBeBetweenStartAndEnd)
        {
            var u = ray.Direction;
            var v = l2.Direction;
            var w = ray.Point - l2.StartPoint;
            var d = Perp(u, v);
            if (Math.Abs(d) < Constants.Tolerance)
            {
                // parallel lines
                return null;
            }

            var sI = Perp(v, w) / d;
            var p = ray.Point + sI * u;
            if (mustBeBetweenStartAndEnd)
            {
                if (ray.IsPointOn(p) && l2.IsPointOnLine(p))
                {
                    return p;
                }

                return null;
            }

            return p;
        }