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)); }); }
public static string ToString([NotNull] Subpath subpath) { var builder = new StringBuilder(); Append(builder, subpath); return(builder.ToString()); }
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(' '); } }
/// <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))); }
public static IEnumerable <Path> CreatePaths(Subpath data) { yield return(Visualizer.CreateFillPath(PathFormatter.ToString(data))); foreach (var x in Visualizer.CreatePathsForSubpath(data)) { yield return(x); } }
public void Remove(Subpath subpath) { var i = _subpaths.IndexOf(subpath); if (i < 0) { i = _reverseSubpaths.IndexOf(subpath); } _subpaths.RemoveAt(i); _reverseSubpaths.RemoveAt(i); }
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; }
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)); }
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); }
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 }); }
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(); } }
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); }
public void Add(Subpath subpath) { _subpaths.Add(subpath); _reverseSubpaths.Add(SubpathDirectionReverser.ReverseDirection(subpath)); }
private static bool HasFillArea(Subpath subpath) { // area is larger than 1/20 of a pixel return(Math.Abs(SubpathFillAreaCalculator.CalculateSigned(subpath)) >= 0.05); }
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); }
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); }