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(' '); } }
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 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()); }); }
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; } SegmentBase segment1, segment2; var intersection = segments[i].SplitByNextIntersection(i > 0 ? segments[i - 1].EndPoint : startPoint, out segment1, out 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; } }
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 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 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 SubpathDirection CalculateDirection(Subpath subpath) { return SubpathFillAreaCalculator.CalculateSigned(subpath) <= 0 ? SubpathDirection.Clockwise : SubpathDirection.Counterclockwise; }
private static bool Equals(Subpath subpath1, Subpath subpath2) { return PathFormatter.ToString(subpath1) == PathFormatter.ToString(subpath2); }
private static bool HasFillArea(Subpath subpath) { // area is larger than 1/20 of a pixel return Math.Abs(SubpathFillAreaCalculator.CalculateSigned(subpath)) >= 0.05; }
public void Remove(Subpath subpath) { var i = _subpaths.IndexOf(subpath); if (i < 0) { i = _reverseSubpaths.IndexOf(subpath); } _subpaths.RemoveAt(i); _reverseSubpaths.RemoveAt(i); }
public void Add(Subpath subpath) { _subpaths.Add(subpath); _reverseSubpaths.Add(SubpathDirectionReverser.ReverseDirection(subpath)); }