private void PreCompute()
        {
            _Path = ComputedValue <string> .From(() =>
            {
                if (Parent == null)
                {
                    return("");
                }

                return($"{Parent.Path}/{Subpath.EscapeJsonPath()}");
            });

            _Value = ComputedValue <object> .From(() =>
            {
                if (!IsAlive)
                {
                    return(null);
                }
                return(Type.GetValue(this));
            });

            _Snapshot = ComputedValue <object> .From(() =>
            {
                if (!IsAlive)
                {
                    return(null);
                }
                return(Type.GetSnapshot(this, false));
            });
        }
Beispiel #2
0
        public static string ToString([NotNull] Subpath subpath)
        {
            var builder = new StringBuilder();

            Append(builder, subpath);
            return(builder.ToString());
        }
Beispiel #3
0
 private static void Append(StringBuilder builder, Subpath subpath)
 {
     builder.Append('M');
     builder.Append(PointToString(subpath.StartPoint));
     builder.Append(' ');
     foreach (var segment in subpath.Segments)
     {
         if (segment is LineSegment)
         {
             builder.Append('L');
             builder.Append(PointToString(segment.EndPoint));
             builder.Append(' ');
         }
         else if (segment is CubicBezierSegment)
         {
             var cubicBezierSegment = (CubicBezierSegment)segment;
             builder.Append('C');
             builder.Append(PointToString(cubicBezierSegment.ControlPoint1));
             builder.Append(' ');
             builder.Append(PointToString(cubicBezierSegment.ControlPoint2));
             builder.Append(' ');
             builder.Append(PointToString(cubicBezierSegment.EndPoint));
             builder.Append(' ');
         }
         else if (segment is QuadraticBezierSegment)
         {
             var quadraticBezierSegment = (QuadraticBezierSegment)segment;
             builder.Append('Q');
             builder.Append(PointToString(quadraticBezierSegment.ControlPoint));
             builder.Append(' ');
             builder.Append(PointToString(quadraticBezierSegment.EndPoint));
             builder.Append(' ');
         }
         else if (segment is EllipticalArcSegment)
         {
             var ellipticalArcSegment = (EllipticalArcSegment)segment;
             builder.Append('A');
             builder.Append(PointToString(ellipticalArcSegment.Radii));
             builder.Append(' ');
             builder.Append(DoubleToString(ellipticalArcSegment.XAxisRotation));
             builder.Append(' ');
             builder.Append(ellipticalArcSegment.IsLargeArc ? '1' : '0');
             builder.Append(' ');
             builder.Append(ellipticalArcSegment.IsSweep ? '1' : '0');
             builder.Append(' ');
             builder.Append(PointToString(ellipticalArcSegment.EndPoint));
             builder.Append(' ');
         }
         else
         {
             throw new PathDataConverterException("Unknown segment type.");
         }
     }
     if (subpath.IsClosed)
     {
         builder.Append('Z');
         builder.Append(' ');
     }
 }
Beispiel #4
0
 /// <summary>
 /// Returns true if the child is entirely contained within the parent, inclusive the edges.
 /// Assumes that there is no intersection between the two subpaths, touchings allowed.
 /// </summary>
 private static bool Contains(Subpath parent, Subpath child)
 {
     if (!PointInPolygonTest.Contains(parent.PolygonApproximation, child.PolygonApproximation.InsidePoint))
     {
         return(false);
     }
     return(child.PolygonApproximation.Points.Any(x => PointInPolygonTest.Contains(parent.PolygonApproximation, x)));
 }
Beispiel #5
0
        public static IEnumerable <Path> CreatePaths(Subpath data)
        {
            yield return(Visualizer.CreateFillPath(PathFormatter.ToString(data)));

            foreach (var x in Visualizer.CreatePathsForSubpath(data))
            {
                yield return(x);
            }
        }
Beispiel #6
0
            public void Remove(Subpath subpath)
            {
                var i = _subpaths.IndexOf(subpath);

                if (i < 0)
                {
                    i = _reverseSubpaths.IndexOf(subpath);
                }
                _subpaths.RemoveAt(i);
                _reverseSubpaths.RemoveAt(i);
            }
Beispiel #7
0
        private void TransformClippingPath(Matrix newCtm)
        {
            Path path = new Path();

            foreach (Subpath subpath in clippingPath.GetSubpaths())
            {
                Subpath transformedSubpath = TransformSubpath(subpath, newCtm);
                path.AddSubpath(transformedSubpath);
            }
            clippingPath = path;
        }
Beispiel #8
0
        private Subpath TransformSubpath(Subpath subpath, Matrix newCtm)
        {
            Subpath newSubpath = new Subpath();

            newSubpath.SetClosed(subpath.IsClosed());
            foreach (IShape segment in subpath.GetSegments())
            {
                IShape transformedSegment = TransformSegment(segment, newCtm);
                newSubpath.AddSegment(transformedSegment);
            }
            return(newSubpath);
        }
        public virtual void SquareClippingTest()
        {
            Subpath squareSubpath = new Subpath(new Point(10, 10));

            squareSubpath.AddSegment(new Line(10, 10, 10, 30));
            squareSubpath.AddSegment(new Line(10, 30, 30, 30));
            squareSubpath.AddSegment(new Line(30, 30, 30, 10));
            squareSubpath.AddSegment(new Line(30, 10, 10, 10));
            squareSubpath.SetClosed(true);
            Path squarePath = new Path();

            squarePath.AddSubpath(squareSubpath);
            Subpath rectangleSubpath = new Subpath(new Point(20, 20));

            rectangleSubpath.AddSegment(new Line(20, 20, 20, 40));
            rectangleSubpath.AddSegment(new Line(20, 40, 30, 40));
            rectangleSubpath.AddSegment(new Line(30, 40, 30, 20));
            rectangleSubpath.AddSegment(new Line(30, 20, 20, 20));
            rectangleSubpath.SetClosed(true);
            Path rectanglePath = new Path();

            rectanglePath.AddSubpath(rectangleSubpath);
            Clipper clipper = new Clipper();

            ClipperBridge.AddPath(clipper, squarePath, PolyType.SUBJECT);
            ClipperBridge.AddPath(clipper, rectanglePath, PolyType.CLIP);
            PolyTree polyTree = new PolyTree();

            clipper.Execute(ClipType.UNION, polyTree);
            Path result = ClipperBridge.ConvertToPath(polyTree);

            NUnit.Framework.Assert.AreEqual(new Point(20, 40), result.GetCurrentPoint());
            NUnit.Framework.Assert.AreEqual(2, result.GetSubpaths().Count);
            Subpath closedPath = result.GetSubpaths()[0];

            NUnit.Framework.Assert.AreEqual(new Point(20, 40), closedPath.GetStartPoint());
            IList <IShape> closedPartSegments = closedPath.GetSegments();

            NUnit.Framework.Assert.AreEqual(5, closedPartSegments.Count);
            NUnit.Framework.Assert.IsTrue(AreShapesEqual(new Line(20, 40, 20, 30), closedPartSegments[0]));
            NUnit.Framework.Assert.IsTrue(AreShapesEqual(new Line(20, 30, 10, 30), closedPartSegments[1]));
            NUnit.Framework.Assert.IsTrue(AreShapesEqual(new Line(10, 30, 10, 10), closedPartSegments[2]));
            NUnit.Framework.Assert.IsTrue(AreShapesEqual(new Line(10, 10, 30, 10), closedPartSegments[3]));
            NUnit.Framework.Assert.IsTrue(AreShapesEqual(new Line(30, 10, 30, 40), closedPartSegments[4]));
            NUnit.Framework.Assert.IsTrue(closedPath.IsClosed());
            Subpath openPart = result.GetSubpaths()[1];

            NUnit.Framework.Assert.AreEqual(new Point(20, 40), openPart.GetStartPoint());
            NUnit.Framework.Assert.AreEqual(0, openPart.GetSegments().Count);
            NUnit.Framework.Assert.IsFalse(openPart.IsClosed());
        }
        public static double CalculateSigned(Subpath subpath)
        {
            var points = subpath.PolygonApproximation.Points;

            double doubleArea = 0;
            var    point1     = points.Last();

            foreach (var point2 in points)
            {
                doubleArea += (point2.X - point1.X) * (point1.Y + point2.Y);
                point1      = point2;
            }
            return(doubleArea / 2);
        }
        public static Subpath ReverseDirection(Subpath subpath)
        {
            var segments   = new SegmentBase[subpath.Segments.Count];
            var startPoint = subpath.StartPoint;

            for (var i = 0; i < segments.Length; i++)
            {
                var segment = subpath.Segments[i];
                segments[i] = segment.Reverse(startPoint);
                startPoint  = segment.EndPoint;
            }
            Array.Reverse(segments);
            return(new Subpath(startPoint, segments, subpath.IsClosed));
        }
Beispiel #12
0
 private static Subpath ChooseConcatenation(Subpath subpath, IReadOnlyList <Subpath> concatenations)
 {
     if (concatenations.Count == 1)
     {
         return(concatenations[0]);
     }
     return(concatenations
            .MinBy(
                x =>
     {
         var points1 = subpath.PolygonApproximation.Points;
         var points2 = x.PolygonApproximation.Points;
         return Angle(points1.Last() - points1.LastButOne(), points2.Second() - points2.First());
     }));
 }
        private static Subpath ConstructSquare(Point squareCenter, double widthHalf, double rotationAngle)
        {
            // Orthogonal square is the square with sides parallel to one of the axes.
            Point[] ortogonalSquareVertices = new Point[] { new Point(-widthHalf, -widthHalf), new Point(-widthHalf, widthHalf
                                                                                                         ), new Point(widthHalf, widthHalf), new Point(widthHalf, -widthHalf) };
            Point[] rotatedSquareVertices = GetRotatedSquareVertices(ortogonalSquareVertices, rotationAngle, squareCenter
                                                                     );
            Subpath square = new Subpath();

            square.AddSegment(new Line(rotatedSquareVertices[0], rotatedSquareVertices[1]));
            square.AddSegment(new Line(rotatedSquareVertices[1], rotatedSquareVertices[2]));
            square.AddSegment(new Line(rotatedSquareVertices[2], rotatedSquareVertices[3]));
            square.AddSegment(new Line(rotatedSquareVertices[3], rotatedSquareVertices[0]));
            return(square);
        }
        /// <summary>Converts specified degenerate subpaths to circles.</summary>
        /// <remarks>
        /// Converts specified degenerate subpaths to circles.
        /// Note: actually the resultant subpaths are not real circles but approximated.
        /// </remarks>
        /// <param name="radius">Radius of each constructed circle.</param>
        /// <returns>
        ///
        /// <see cref="System.Collections.IList{E}"/>
        /// consisting of circles constructed on given degenerated subpaths.
        /// </returns>
        private static IList <Subpath> ConvertToCircles(IList <Subpath> degenerateSubpaths, double radius)
        {
            IList <Subpath> circles = new List <Subpath>(degenerateSubpaths.Count);

            foreach (Subpath subpath in degenerateSubpaths)
            {
                BezierCurve[] circleSectors = ApproximateCircle(subpath.GetStartPoint(), radius);
                Subpath       circle        = new Subpath();
                circle.AddSegment(circleSectors[0]);
                circle.AddSegment(circleSectors[1]);
                circle.AddSegment(circleSectors[2]);
                circle.AddSegment(circleSectors[3]);
                circles.Add(circle);
            }
            return(circles);
        }
        public static IEnumerable <Subpath> SplitByIntersections(Subpath subpath)
        {
            var startPoint = subpath.StartPoint;
            var segments   = subpath.ClosedSegments.ToList();

            while (true)
            {
                var i = segments.FindIndex(x => x.Intersections.Count > 0);
                if (i < 0)
                {
                    if (segments.Count > 0)
                    {
                        yield return(new Subpath(startPoint, segments, false));
                    }
                    yield break;
                }

                var intersection = segments[i].SplitByNextIntersection(i > 0 ? segments[i - 1].EndPoint : startPoint, out SegmentBase segment1, out SegmentBase segment2);
                var segments1    = segments.GetRange(0, i);
                if (segment1 != null)
                {
                    if (segment1.Intersections.Count > 0)
                    {
                        throw new PathDataConverterException();
                    }
                    segments1.Add(segment1);
                }
                if (segments1.Count > 0)
                {
                    yield return(new Subpath(startPoint, segments1, false));
                }
                i++;
                var segments2 = segments.GetRange(i, segments.Count - i);
                if (segment2 != null)
                {
                    segments2.Insert(0, segment2);
                }

                startPoint = intersection;
                segments   = segments2;
            }
        }
        private static Subpath Preprocess([NotNull] Subpath subpath)
        {
            var segments   = new List <SegmentBase>();
            var startPoint = subpath.StartPoint;

            foreach (var segment in subpath.Segments)
            {
                var x = Simplify(startPoint, segment);
                if (!IsDegenerate(startPoint, x))
                {
                    segments.Add(x);
                }
                startPoint = segment.EndPoint;
            }
            if (segments.Count > 0)
            {
                return(new Subpath(subpath.StartPoint, segments, subpath.IsClosed));
            }
            return(null);
        }
Beispiel #17
0
        public static IEnumerable <Path> CreatePathsForSubpath(Subpath subpath)
        {
            var source = PathFormatter.ToString(subpath);
            var hash   = BitConverter.GetBytes(source.GetHashCode());
            var color  = ColorHelper.FromHsb(360f * hash[0] / 255, 0.5f * hash[1] / 255 + 0.5f, 0.5f * hash[2] / 255);
            var group1 = new GeometryGroup();

            group1.Children.Add(Geometry.Parse(source));
            group1.Children.Add(new RectangleGeometry(new Rect(subpath.StartPoint.X - 2.5, subpath.StartPoint.Y - 2.5, 5, 5)));
            foreach (var segment in subpath.Segments)
            {
                group1.Children.Add(new EllipseGeometry(new Point(segment.EndPoint.X, segment.EndPoint.Y), 1.5, 1.5));
            }
            yield return(new Path
            {
                Data = group1,
                Stroke = new SolidColorBrush(color),
                StrokeThickness = 3,
                Opacity = 0.5
            });

            var intersections = subpath.ClosedSegments.SelectMany(x => x.Intersections).ToArray();

            if (intersections.Length == 0)
            {
                yield break;
            }
            var group2 = new GeometryGroup();

            foreach (var point in intersections)
            {
                group2.Children.Add(new EllipseGeometry(new Point(point.X, point.Y), 1, 1));
            }
            yield return(new Path
            {
                Data = group2,
                Stroke = new SolidColorBrush(Colors.Black),
                StrokeThickness = 2,
                Opacity = 0.3
            });
        }
Beispiel #18
0
        public static IEnumerable <Subpath> Unify(IEnumerable <Subpath> subpaths)
        {
            var collection = new SubpathCollection();

            foreach (var subpath in subpaths)
            {
                if (subpath.AreSegmentsClosed)
                {
                    if (HasFillArea(subpath))
                    {
                        yield return(subpath);
                    }
                }
                else
                {
                    collection.Add(subpath);
                }
            }
            var subpath1 = collection.Dequeue();

            while (subpath1 != null)
            {
                var concatenations = collection.GetAll(subpath1.EndPoint).ToArray();
                Debugger.BreakWhen(concatenations.Length == 0);
                if (concatenations.Length > 0)
                {
                    var subpath2 = ChooseConcatenation(subpath1, concatenations);
                    collection.Remove(subpath2);
                    subpath1 = new Subpath(subpath1.StartPoint, subpath1.Segments.Concat(subpath2.Segments), false);
                    if (!subpath1.AreSegmentsClosed)
                    {
                        continue;
                    }
                }
                if (HasFillArea(subpath1))
                {
                    yield return(subpath1);
                }
                subpath1 = collection.Dequeue();
            }
        }
Beispiel #19
0
 public static bool EqualsIgnoreDirection(this Subpath subpath1, Subpath subpath2)
 {
     if (subpath1.Segments.Count != subpath2.Segments.Count ||
         subpath1.IsClosed != subpath2.IsClosed)
     {
         return(false);
     }
     if (subpath1.StartPoint == subpath2.StartPoint &&
         subpath1.EndPoint == subpath2.EndPoint &&
         Equals(subpath1, subpath2))
     {
         return(true);
     }
     if (subpath1.StartPoint == subpath2.EndPoint &&
         subpath1.EndPoint == subpath2.StartPoint &&
         Equals(subpath1, SubpathDirectionReverser.ReverseDirection(subpath2)))
     {
         return(true);
     }
     return(false);
 }
Beispiel #20
0
 public void Add(Subpath subpath)
 {
     _subpaths.Add(subpath);
     _reverseSubpaths.Add(SubpathDirectionReverser.ReverseDirection(subpath));
 }
Beispiel #21
0
 private static bool HasFillArea(Subpath subpath)
 {
     // area is larger than 1/20 of a pixel
     return(Math.Abs(SubpathFillAreaCalculator.CalculateSigned(subpath)) >= 0.05);
 }
Beispiel #22
0
        private static TreeNode <Subpath> FindParent(IEnumerable <TreeNode <Subpath> > nodes, Subpath subpath)
        {
            var parent = nodes.FirstOrDefault(x => Contains(x.AssociatedObject, subpath));

            if (parent == null)
            {
                return(null);
            }
            return(FindParent(parent.Children, subpath) ?? parent);
        }
Beispiel #23
0
 private static bool Equals(Subpath subpath1, Subpath subpath2)
 {
     return(PathFormatter.ToString(subpath1) == PathFormatter.ToString(subpath2));
 }
 public static SubpathDirection CalculateDirection(Subpath subpath)
 {
     return(SubpathFillAreaCalculator.CalculateSigned(subpath) <= 0 ? SubpathDirection.Clockwise : SubpathDirection.Counterclockwise);
 }