From 46b183f564ac65539d41bc0d263dc0749cd31c51 Mon Sep 17 00:00:00 2001 From: BobLd Date: Tue, 15 Sep 2020 11:41:24 +0100 Subject: [PATCH] add IntersectsWith(PdfRectangle, PdfLine) and Intersect(PdfRectangle, PdfLine) --- .../Geometry/ClippingExtensions.cs | 7 +- .../Geometry/GeometryExtensions.cs | 138 ++++++++++++++++++ 2 files changed, 144 insertions(+), 1 deletion(-) diff --git a/src/UglyToad.PdfPig/Geometry/ClippingExtensions.cs b/src/UglyToad.PdfPig/Geometry/ClippingExtensions.cs index 12d839ad..68791c24 100644 --- a/src/UglyToad.PdfPig/Geometry/ClippingExtensions.cs +++ b/src/UglyToad.PdfPig/Geometry/ClippingExtensions.cs @@ -13,7 +13,7 @@ /// internal static class ClippingExtensions { - private const double Factor = 10_000.0; + public const double Factor = 10_000.0; /// /// Number of lines to use when transforming bezier curve to polyline. @@ -220,5 +220,10 @@ { return new ClipperIntPoint(point.X * Factor, point.Y * Factor); } + + internal static List ToClipperIntPoint(this PdfLine line) + { + return new List() { line.Point1.ToClipperIntPoint(), line.Point2.ToClipperIntPoint() }; + } } } diff --git a/src/UglyToad.PdfPig/Geometry/GeometryExtensions.cs b/src/UglyToad.PdfPig/Geometry/GeometryExtensions.cs index 58f48102..283cc6d2 100644 --- a/src/UglyToad.PdfPig/Geometry/GeometryExtensions.cs +++ b/src/UglyToad.PdfPig/Geometry/GeometryExtensions.cs @@ -475,6 +475,65 @@ var points = new[] { rectangle.BottomLeft, rectangle.BottomRight, rectangle.TopLeft, rectangle.TopRight }; return new PdfRectangle(points.Min(p => p.X), points.Min(p => p.Y), points.Max(p => p.X), points.Max(p => p.Y)); } + + /// + /// Whether the rectangle and the line intersect. + /// + /// + /// + public static bool IntersectsWith(this PdfRectangle rectangle, PdfLine line) + { + return IntersectsWith(rectangle, line.Point1, line.Point2); + } + + /// + /// Gets the that is the intersection of the rectangle and the line. + /// + /// + /// + public static PdfLine? Intersect(this PdfRectangle rectangle, PdfLine line) + { + var i = Intersect(rectangle, line.Point1, line.Point2); + if (i != null) + { + return new PdfLine(i[0], i[1]); + } + return null; + } + + /// + /// Gets the list of s that are the intersection of the rectangle and the lines. + /// + /// + /// + /// + public static List Intersect(this PdfRectangle rectangle, List lines) + { + var clipper = new Clipper(); + clipper.AddPath(rectangle.ToClipperPolygon().ToList(), ClipperPolyType.Clip, true); + + foreach (var line in lines) + { + clipper.AddPath(line.ToClipperIntPoint(), ClipperPolyType.Subject, false); + } + + var solutions = new ClipperPolyTree(); + if (clipper.Execute(ClipperClipType.Intersection, solutions)) + { + List rv = new List(); + foreach (var solution in solutions.Children) + { + rv.Add(new PdfLine(new PdfPoint(solution.Contour[0].X / ClippingExtensions.Factor, solution.Contour[0].Y / ClippingExtensions.Factor), + new PdfPoint(solution.Contour[1].X / ClippingExtensions.Factor, solution.Contour[1].Y / ClippingExtensions.Factor))); + } + return rv; + } + else + { + return new List(); + } + // clipper.clear() ?? + } #endregion #region PdfLine @@ -533,6 +592,26 @@ { return ParallelTo(line.Point1, line.Point2, other.From, other.To); } + + /// + /// Gets the that is the intersection of the rectangle and the line. + /// + /// + /// + public static PdfLine? Intersect(this PdfLine line, PdfRectangle rectangle) + { + return rectangle.Intersect(line); + } + + /// + /// Whether the rectangle and the line intersect. + /// + /// + /// + public static bool IntersectsWith(this PdfLine line, PdfRectangle rectangle) + { + return rectangle.IntersectsWith(line); + } #endregion #region Path Line @@ -657,6 +736,65 @@ } } + /// + /// The intersection of the line formed by and + /// intersects the rectangle. + /// + private static PdfPoint[] Intersect(PdfRectangle rectangle, PdfPoint pl1, PdfPoint pl2) + { + var clipper = new Clipper(); + clipper.AddPath(rectangle.ToClipperPolygon().ToList(), ClipperPolyType.Clip, true); + + clipper.AddPath(new List() { pl1.ToClipperIntPoint(), pl2.ToClipperIntPoint() }, ClipperPolyType.Subject, false); + + var solutions = new ClipperPolyTree(); + if (clipper.Execute(ClipperClipType.Intersection, solutions)) + { + if (solutions.Children.Count == 0) + { + return null; + } + else if (solutions.Children.Count == 1) + { + var solution = solutions.Children[0]; + + return new[] + { + new PdfPoint(solution.Contour[0].X / ClippingExtensions.Factor, solution.Contour[0].Y / ClippingExtensions.Factor), + new PdfPoint(solution.Contour[1].X / ClippingExtensions.Factor, solution.Contour[1].Y / ClippingExtensions.Factor) + }; + } + else + { + throw new ArgumentException("GeometryExtensions.Intersect(PdfRectangle, PdfPoint, PdfPoint): more than one solution found."); + } + } + else + { + return null; + } + // clipper.clear() ?? + } + + /// + /// Whether the line formed by and + /// intersects the rectangle. + /// + public static bool IntersectsWith(PdfRectangle rectangle, PdfPoint pl1, PdfPoint pl2) + { + var clipper = new Clipper(); + clipper.AddPath(rectangle.ToClipperPolygon().ToList(), ClipperPolyType.Clip, true); + + clipper.AddPath(new List() { pl1.ToClipperIntPoint(), pl2.ToClipperIntPoint() }, ClipperPolyType.Subject, false); + + var solutions = new ClipperPolyTree(); + if (clipper.Execute(ClipperClipType.Intersection, solutions)) + { + return solutions.Children.Count > 0; + } + return false; + } + private static bool ParallelTo(PdfPoint p11, PdfPoint p12, PdfPoint p21, PdfPoint p22) { return Math.Abs((p12.Y - p11.Y) * (p22.X - p21.X) - (p22.Y - p21.Y) * (p12.X - p11.X)) < epsilon;