예제 #1
0
        private static double[] IntersectT(PdfPath.BezierCurve bezierCurve, PdfPoint p1, PdfPoint p2)
        {
            // if the bounding boxes do not intersect, they cannot intersect
            var bezierBbox = bezierCurve.GetBoundingRectangle();

            if (!bezierBbox.HasValue)
            {
                return(null);
            }

            if (bezierBbox.Value.Left > Math.Max(p1.X, p2.X) || Math.Min(p1.X, p2.X) > bezierBbox.Value.Right)
            {
                return(null);
            }

            if (bezierBbox.Value.Top < Math.Min(p1.Y, p2.Y) || Math.Max(p1.Y, p2.Y) < bezierBbox.Value.Bottom)
            {
                return(null);
            }

            double A = (p2.Y - p1.Y);
            double B = (p1.X - p2.X);
            double C = p1.X * (p1.Y - p2.Y) + p1.Y * (p2.X - p1.X);

            double alpha = bezierCurve.StartPoint.X * A + bezierCurve.StartPoint.Y * B;
            double beta  = 3.0 * (bezierCurve.FirstControlPoint.X * A + bezierCurve.FirstControlPoint.Y * B);
            double gamma = 3.0 * (bezierCurve.SecondControlPoint.X * A + bezierCurve.SecondControlPoint.Y * B);
            double delta = bezierCurve.EndPoint.X * A + bezierCurve.EndPoint.Y * B;

            double a = -alpha + beta - gamma + delta;
            double b = 3 * alpha - 2 * beta + gamma;
            double c = -3 * alpha + beta;
            double d = alpha + C;

            var solution = SolveCubicEquation(a, b, c, d);

            return(solution.Where(s => !double.IsNaN(s)).Where(s => s >= -epsilon && (s - 1) <= epsilon).OrderBy(s => s).ToArray());
        }
예제 #2
0
        /// <summary>
        /// Algorithm to find a minimal bounding rectangle (MBR) such that the MBR corresponds to a rectangle
        /// with smallest possible area completely enclosing the polygon.
        /// <para>From 'A Fast Algorithm for Generating a Minimal Bounding Rectangle' by Lennert D. Den Boer.</para>
        /// </summary>
        /// <param name="polygon">
        /// Polygon P is assumed to be both simple and convex, and to contain no duplicate (coincident) vertices.
        /// The vertices of P are assumed to be in strict cyclic sequential order, either clockwise or
        /// counter-clockwise relative to the origin P0.
        /// </param>
        internal static PdfRectangle ParametricPerpendicularProjection(IReadOnlyList <PdfPoint> polygon)
        {
            if (polygon == null || polygon.Count == 0)
            {
                throw new ArgumentException("ParametricPerpendicularProjection(): polygon cannot be null and must contain at least one point.");
            }

            if (polygon.Count < 4)
            {
                if (polygon.Count == 1)
                {
                    return(new PdfRectangle(polygon[0], polygon[0]));
                }
                else if (polygon.Count == 2)
                {
                    return(new PdfRectangle(polygon[0], polygon[1]));
                }
                else
                {
                    PdfPoint p3 = polygon[0].Add(polygon[1].Subtract(polygon[2]));
                    return(new PdfRectangle(p3, polygon[1], polygon[0], polygon[2]));
                }
            }

            PdfPoint[] MBR = new PdfPoint[0];

            double Amin = double.MaxValue;
            double tmin = 1;
            double tmax = 0;
            double smax = 0;
            int    j    = 1;
            int    k    = 0;
            int    l    = -1;

            PdfPoint Q  = new PdfPoint();
            PdfPoint R0 = new PdfPoint();
            PdfPoint R1 = new PdfPoint();
            PdfPoint u  = new PdfPoint();

            int nv = polygon.Count;

            while (true)
            {
                var Pk = polygon[k];

                PdfPoint v = polygon[j].Subtract(Pk);
                double   r = 1.0 / v.DotProduct(v);

                for (j = 0; j < nv; j++)
                {
                    if (j == k)
                    {
                        continue;
                    }
                    PdfPoint Pj = polygon[j];
                    u = Pj.Subtract(Pk);
                    double   t  = u.DotProduct(v) * r;
                    PdfPoint Pt = new PdfPoint(t * v.X + Pk.X, t * v.Y + Pk.Y);
                    u = Pt.Subtract(Pj);
                    double s = u.DotProduct(u);

                    if (t < tmin)
                    {
                        tmin = t;
                        R0   = Pt;
                    }

                    if (t > tmax)
                    {
                        tmax = t;
                        R1   = Pt;
                    }

                    if (s > smax)
                    {
                        smax = s;
                        Q    = Pt;
                        l    = j;
                    }
                }

                if (l == -1)
                {
                    // All points are colinear - rectangle has no area (need more tests)
                    var bottomLeft = polygon.OrderBy(p => p.X).ThenBy(p => p.Y).First();
                    var topRight   = polygon.OrderByDescending(p => p.Y).OrderByDescending(p => p.X).First();
                    return(new PdfRectangle(bottomLeft, topRight, bottomLeft, topRight));
                }

                PdfPoint PlMinusQ = polygon[l].Subtract(Q);
                PdfPoint R2       = R1.Add(PlMinusQ);
                PdfPoint R3       = R0.Add(PlMinusQ);
                u = R1.Subtract(R0);
                double A = u.DotProduct(u) * smax;

                if (A < Amin)
                {
                    Amin = A;
                    MBR  = new[] { R0, R1, R2, R3 };
                }

                k++;
                j = k;

                if (j == nv)
                {
                    j = 0;
                }

                if (k == nv)
                {
                    break;
                }
            }

            return(new PdfRectangle(MBR[2], MBR[3], MBR[1], MBR[0]));
        }
예제 #3
0
 /// <summary>
 /// Whether the line segment contains the point.
 /// </summary>
 public static bool Contains(this PdfPath.Line line, PdfPoint point)
 {
     return(Contains(line.From, line.To, point));
 }
예제 #4
0
 /// <summary>
 /// Whether the line segment contains the point.
 /// </summary>
 public static bool Contains(this PdfLine line, PdfPoint point)
 {
     return(Contains(line.Point1, line.Point2, point));
 }
예제 #5
0
 internal PdfRectangle(PdfPoint point1, PdfPoint point2) : this(point1.X, point1.Y, point2.X, point2.Y)
 {
 }
예제 #6
0
        /// <summary>
        /// Get the <see cref="PdfPoint"/> that is the intersection of two lines.
        /// </summary>
        public static PdfPoint?Intersect(this Line line, Line other)
        {
            // if the bounding boxes do not intersect, the lines cannot intersect
            var thisLineBbox = line.GetBoundingRectangle();

            if (!thisLineBbox.HasValue)
            {
                return(null);
            }

            var lineBbox = other.GetBoundingRectangle();

            if (!lineBbox.HasValue)
            {
                return(null);
            }

            if (!thisLineBbox.Value.IntersectsWith(lineBbox.Value))
            {
                return(null);
            }

            var eq1 = GetSlopeIntercept(line.From, line.To);
            var eq2 = GetSlopeIntercept(other.From, other.To);

            if (double.IsNaN(eq1.Slope) && double.IsNaN(eq2.Slope))
            {
                return(null);                                                    // both lines are vertical (hence parallel)
            }
            if (eq1.Slope == eq2.Slope)
            {
                return(null);                        // both lines are parallel
            }
            var intersection = new PdfPoint();

            if (double.IsNaN(eq1.Slope))
            {
                var x = eq1.Intercept;
                var y = eq2.Slope * x + eq2.Intercept;
                intersection = new PdfPoint(x, y);
            }
            else if (double.IsNaN(eq2.Slope))
            {
                var x = eq2.Intercept;
                var y = eq1.Slope * x + eq1.Intercept;
                intersection = new PdfPoint(x, y);
            }
            else
            {
                var x = (eq2.Intercept - eq1.Intercept) / (eq1.Slope - eq2.Slope);
                var y = eq1.Slope * x + eq1.Intercept;
                intersection = new PdfPoint(x, y);
            }

            // check if the intersection point belongs to both segments
            // (for the moment we only know it belongs to both lines)
            if (!line.Contains(intersection))
            {
                return(null);
            }
            if (!other.Contains(intersection))
            {
                return(null);
            }
            return(intersection);
        }
예제 #7
0
        /// <summary>
        /// Algorithm to find a minimal bounding rectangle (MBR) such that the MBR corresponds to a rectangle
        /// with smallest possible area completely enclosing the polygon.
        /// <para>From 'A Fast Algorithm for Generating a Minimal Bounding Rectangle' by Lennert D. Den Boer.</para>
        /// </summary>
        /// <param name="polygon">
        /// Polygon P is assumed to be both simple and convex, and to contain no duplicate (coincident) vertices.
        /// The vertices of P are assumed to be in strict cyclic sequential order, either clockwise or
        /// counter-clockwise relative to the origin P0.
        /// </param>
        private static PdfRectangle ParametricPerpendicularProjection(IReadOnlyList <PdfPoint> polygon)
        {
            if (polygon == null || polygon.Count == 0)
            {
                throw new ArgumentException("ParametricPerpendicularProjection(): polygon cannot be null and must contain at least one point.", nameof(polygon));
            }
            else if (polygon.Count == 1)
            {
                return(new PdfRectangle(polygon[0], polygon[0]));
            }
            else if (polygon.Count == 2)
            {
                return(new PdfRectangle(polygon[0], polygon[1]));
            }

            PdfPoint[] MBR = new PdfPoint[0];

            double Amin = double.PositiveInfinity;
            int    j    = 1;
            int    k    = 0;

            PdfPoint Q  = new PdfPoint();
            PdfPoint R0 = new PdfPoint();
            PdfPoint R1 = new PdfPoint();
            PdfPoint u  = new PdfPoint();

            while (true)
            {
                PdfPoint Pk = polygon[k];
                PdfPoint v  = polygon[j].Subtract(Pk);
                double   r  = 1.0 / v.DotProduct(v);

                double tmin = 1;
                double tmax = 0;
                double smax = 0;
                int    l    = -1;

                for (j = 0; j < polygon.Count; j++)
                {
                    PdfPoint Pj = polygon[j];
                    u = Pj.Subtract(Pk);
                    double   t  = u.DotProduct(v) * r;
                    PdfPoint Pt = new PdfPoint(t * v.X + Pk.X, t * v.Y + Pk.Y);
                    u = Pt.Subtract(Pj);
                    double s = u.DotProduct(u);

                    if (t < tmin)
                    {
                        tmin = t;
                        R0   = Pt;
                    }

                    if (t > tmax)
                    {
                        tmax = t;
                        R1   = Pt;
                    }

                    if (s > smax)
                    {
                        smax = s;
                        Q    = Pt;
                        l    = j;
                    }
                }

                if (l != -1)
                {
                    PdfPoint PlMinusQ = polygon[l].Subtract(Q);
                    PdfPoint R2       = R1.Add(PlMinusQ);
                    PdfPoint R3       = R0.Add(PlMinusQ);

                    u = R1.Subtract(R0);

                    double A = u.DotProduct(u) * smax;

                    if (A < Amin)
                    {
                        Amin = A;
                        MBR  = new[] { R0, R1, R2, R3 };
                    }
                }

                k++;
                j = k + 1;

                if (j == polygon.Count)
                {
                    j = 0;
                }
                if (k == polygon.Count)
                {
                    break;
                }
            }

            return(new PdfRectangle(MBR[2], MBR[3], MBR[1], MBR[0]));
        }
예제 #8
0
 private static bool IntersectsWith(PdfPath.BezierCurve bezierCurve, PdfPoint p1, PdfPoint p2)
 {
     return(Intersect(bezierCurve, p1, p2).Length > 0);
 }
예제 #9
0
 /// <summary>
 /// Create a new <see cref="Line"/>.
 /// </summary>
 public Line(PdfPoint from, PdfPoint to)
 {
     From = from;
     To   = to;
 }
예제 #10
0
 /// <summary>
 /// Create a Bezier curve at the provided points.
 /// </summary>
 public BezierCurve(PdfPoint startPoint, PdfPoint firstControlPoint, PdfPoint secondControlPoint, PdfPoint endPoint)
 {
     StartPoint         = startPoint;
     FirstControlPoint  = firstControlPoint;
     SecondControlPoint = secondControlPoint;
     EndPoint           = endPoint;
 }
예제 #11
0
 /// <summary>
 /// Create a new <see cref="Move"/> path command.
 /// </summary>
 /// <param name="location"></param>
 public Move(PdfPoint location)
 {
     Location = location;
 }
예제 #12
0
        /// <summary>
        /// Algorithm to find a minimal bounding rectangle (MBR) such that the MBR corresponds to a rectangle
        /// with smallest possible area completely enclosing the polygon.
        /// <para>From 'A Fast Algorithm for Generating a Minimal Bounding Rectangle' by Lennert D. Den Boer.</para>
        /// </summary>
        /// <param name="polygon">
        /// Polygon P is assumed to be both simple and convex, and to contain no duplicate (coincident) vertices.
        /// The vertices of P are assumed to be in strict cyclic sequential order, either clockwise or
        /// counter-clockwise relative to the origin P0.
        /// </param>
        private static PdfRectangle ParametricPerpendicularProjection(IReadOnlyList <PdfPoint> polygon)
        {
            if (polygon == null || polygon.Count == 0)
            {
                throw new ArgumentException("ParametricPerpendicularProjection(): polygon cannot be null and must contain at least one point.", nameof(polygon));
            }
            else if (polygon.Count == 1)
            {
                return(new PdfRectangle(polygon[0], polygon[0]));
            }
            else if (polygon.Count == 2)
            {
                return(new PdfRectangle(polygon[0], polygon[1]));
            }

            double[] MBR = new double[8];

            double Amin = double.PositiveInfinity;
            int    j    = 1;
            int    k    = 0;

            double QX  = double.NaN;
            double QY  = double.NaN;
            double R0X = double.NaN;
            double R0Y = double.NaN;
            double R1X = double.NaN;
            double R1Y = double.NaN;

            while (true)
            {
                PdfPoint Pk = polygon[k];
                PdfPoint Pj = polygon[j];

                double vX = Pj.X - Pk.X;
                double vY = Pj.Y - Pk.Y;
                double r  = 1.0 / (vX * vX + vY * vY);

                double tmin = 1;
                double tmax = 0;
                double smax = 0;
                int    l    = -1;
                double uX;
                double uY;

                for (j = 0; j < polygon.Count; j++)
                {
                    Pj = polygon[j];
                    uX = Pj.X - Pk.X;
                    uY = Pj.Y - Pk.Y;
                    double t = (uX * vX + uY * vY) * r;

                    double PtX = t * vX + Pk.X;
                    double PtY = t * vY + Pk.Y;
                    uX = PtX - Pj.X;
                    uY = PtY - Pj.Y;

                    double s = uX * uX + uY * uY;

                    if (t < tmin)
                    {
                        tmin = t;
                        R0X  = PtX;
                        R0Y  = PtY;
                    }

                    if (t > tmax)
                    {
                        tmax = t;
                        R1X  = PtX;
                        R1Y  = PtY;
                    }

                    if (s > smax)
                    {
                        smax = s;
                        QX   = PtX;
                        QY   = PtY;
                        l    = j;
                    }
                }

                if (l != -1)
                {
                    PdfPoint Pl        = polygon[l];
                    double   PlMinusQX = Pl.X - QX;
                    double   PlMinusQY = Pl.Y - QY;

                    double R2X = R1X + PlMinusQX;
                    double R2Y = R1Y + PlMinusQY;

                    double R3X = R0X + PlMinusQX;
                    double R3Y = R0Y + PlMinusQY;

                    uX = R1X - R0X;
                    uY = R1Y - R0Y;

                    double A = (uX * uX + uY * uY) * smax;

                    if (A < Amin)
                    {
                        Amin = A;
                        MBR  = new[] { R0X, R0Y, R1X, R1Y, R2X, R2Y, R3X, R3Y };
                    }
                }

                k++;
                j = k + 1;

                if (j == polygon.Count)
                {
                    j = 0;
                }
                if (k == polygon.Count)
                {
                    break;
                }
            }

            return(new PdfRectangle(new PdfPoint(MBR[4], MBR[5]),
                                    new PdfPoint(MBR[6], MBR[7]),
                                    new PdfPoint(MBR[2], MBR[3]),
                                    new PdfPoint(MBR[0], MBR[1])));
        }
예제 #13
0
 public PdfRectangle(PdfPoint point1, PdfPoint point2) : this(point1.X, point1.Y, point2.X, point2.Y)
 {
 }
예제 #14
0
 /// <summary>
 /// Whether the line formed by <paramref name="p11"/> and <paramref name="p12"/>
 /// intersects the line formed by <paramref name="p21"/> and <paramref name="p22"/>.
 /// </summary>
 public static bool IntersectsWith(PdfPoint p11, PdfPoint p12, PdfPoint p21, PdfPoint p22)
 {
     return((ccw(p11, p12, p21) != ccw(p11, p12, p22)) &&
            (ccw(p21, p22, p11) != ccw(p21, p22, p12)));
 }
예제 #15
0
 /// <summary>
 /// Return true if the points are in counter-clockwise order.
 /// </summary>
 /// <param name="point1">The first point.</param>
 /// <param name="point2">The second point.</param>
 /// <param name="point3">The third point.</param>
 private static bool ccw(PdfPoint point1, PdfPoint point2, PdfPoint point3)
 {
     return((point2.X - point1.X) * (point3.Y - point1.Y) > (point2.Y - point1.Y) * (point3.X - point1.X));
 }
예제 #16
0
 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);
 }
예제 #17
0
 /// <summary>
 /// Get the dot product of both points.
 /// </summary>
 /// <param name="point1">The first point.</param>
 /// <param name="point2">The second point.</param>
 public static double DotProduct(this PdfPoint point1, PdfPoint point2)
 {
     return(point1.X * point2.X + point1.Y * point2.Y);
 }
예제 #18
0
        private static PdfPoint[] Intersect(PdfPath.BezierCurve bezierCurve, PdfPoint p1, PdfPoint p2)
        {
            var ts = IntersectT(bezierCurve, p1, p2);

            if (ts == null || ts.Length == 0)
            {
                return(EmptyArray <PdfPoint> .Instance);
            }

            List <PdfPoint> points = new List <PdfPoint>();

            foreach (var t in ts)
            {
                PdfPoint point = new PdfPoint(
                    PdfPath.BezierCurve.ValueWithT(bezierCurve.StartPoint.X,
                                                   bezierCurve.FirstControlPoint.X,
                                                   bezierCurve.SecondControlPoint.X,
                                                   bezierCurve.EndPoint.X,
                                                   t),
                    PdfPath.BezierCurve.ValueWithT(bezierCurve.StartPoint.Y,
                                                   bezierCurve.FirstControlPoint.Y,
                                                   bezierCurve.SecondControlPoint.Y,
                                                   bezierCurve.EndPoint.Y,
                                                   t));
                if (Contains(p1, p2, point))
                {
                    points.Add(point);
                }
            }
            return(points.ToArray());
        }
예제 #19
0
 /// <summary>
 /// Get a point with the summed coordinates of both points.
 /// </summary>
 /// <param name="point1">The first point.</param>
 /// <param name="point2">The second point.</param>
 public static PdfPoint Add(this PdfPoint point1, PdfPoint point2)
 {
     return(new PdfPoint(point1.X + point2.X, point1.Y + point2.Y));
 }
예제 #20
0
 private static (double Slope, double Intercept) GetSlopeIntercept(PdfPoint point1, PdfPoint point2)
 {
     if (Math.Abs(point1.X - point2.X) > epsilon)
     {
         var slope     = (point2.Y - point1.Y) / (point2.X - point1.X);
         var intercept = point2.Y - slope * point2.X;
         return(slope, intercept);
     }
     else
     {
         // vertical line special case
         return(double.NaN, point1.X);
     }
 }
예제 #21
0
 /// <summary>
 /// Get a point with the substracted coordinates of both points.
 /// </summary>
 /// <param name="point1">The first point.</param>
 /// <param name="point2">The second point.</param>
 public static PdfPoint Subtract(this PdfPoint point1, PdfPoint point2)
 {
     return(new PdfPoint(point1.X - point2.X, point1.Y - point2.Y));
 }
예제 #22
0
 private static (double Slope, double Intercept) GetSlopeIntercept(PdfPoint point1, PdfPoint point2)
 {
     if ((point1.X - point2.X) != 0) // vertical line special case
     {
         var slope     = (double)((point2.Y - point1.Y) / (point2.X - point1.X));
         var intercept = (double)point2.Y - slope * (double)point2.X;
         return(slope, intercept);
     }
     else
     {
         return(double.NaN, (double)point1.X);
     }
 }
예제 #23
0
 /// <summary>
 /// Create a new <see cref="PdfLine"/>.
 /// </summary>
 /// <param name="point1">First point of the line.</param>
 /// <param name="point2">Second point of the line.</param>
 public PdfLine(PdfPoint point1, PdfPoint point2)
 {
     Point1 = point1;
     Point2 = point2;
 }