Ver código fonte

Common implementation for curve segment scanline

Chlumsky 1 ano atrás
pai
commit
5f9f885c4f
2 arquivos alterados com 115 adições e 86 exclusões
  1. 106 86
      core/edge-segments.cpp
  2. 9 0
      core/edge-segments.h

+ 106 - 86
core/edge-segments.cpp

@@ -325,6 +325,18 @@ SignedDistance CubicSegment::signedDistance(Point2 origin, double &param) const
 #endif
 
 int LinearSegment::scanlineIntersections(double x[3], int dy[3], double y) const {
+    return horizontalScanlineIntersections(x, dy, y);
+}
+
+int QuadraticSegment::scanlineIntersections(double x[3], int dy[3], double y) const {
+    return horizontalScanlineIntersections(x, dy, y);
+}
+
+int CubicSegment::scanlineIntersections(double x[3], int dy[3], double y) const {
+    return horizontalScanlineIntersections(x, dy, y);
+}
+
+int LinearSegment::horizontalScanlineIntersections(double x[3], int dy[3], double y) const {
     if ((y >= p[0].y && y < p[1].y) || (y >= p[1].y && y < p[0].y)) {
         double param = (y-p[0].y)/(p[1].y-p[0].y);
         x[0] = mix(p[0].x, p[1].x, param);
@@ -334,122 +346,130 @@ int LinearSegment::scanlineIntersections(double x[3], int dy[3], double y) const
     return 0;
 }
 
-int QuadraticSegment::scanlineIntersections(double x[3], int dy[3], double y) const {
-    int total = 0;
-    int nextDY = y > p[0].y ? 1 : -1;
-    x[total] = p[0].x;
-    if (p[0].y == y) {
-        if (p[0].y < p[1].y || (p[0].y == p[1].y && p[0].y < p[2].y))
-            dy[total++] = 1;
-        else
-            nextDY = 1;
-    }
-    {
-        Vector2 ab = p[1]-p[0];
-        Vector2 br = p[2]-p[1]-ab;
-        double t[2];
-        int solutions = solveQuadratic(t, br.y, 2*ab.y, p[0].y-y);
-        // Sort solutions
-        double tmp;
-        if (solutions >= 2 && t[0] > t[1])
-            tmp = t[0], t[0] = t[1], t[1] = tmp;
-        for (int i = 0; i < solutions && total < 2; ++i) {
-            if (t[i] >= 0 && t[i] <= 1) {
-                x[total] = p[0].x+2*t[i]*ab.x+t[i]*t[i]*br.x;
-                if (nextDY*(ab.y+t[i]*br.y) >= 0) {
-                    dy[total++] = nextDY;
-                    nextDY = -nextDY;
-                }
-            }
-        }
-    }
-    if (p[2].y == y) {
-        if (nextDY > 0 && total > 0) {
-            --total;
-            nextDY = -1;
-        }
-        if ((p[2].y < p[1].y || (p[2].y == p[1].y && p[2].y < p[0].y)) && total < 2) {
-            x[total] = p[2].x;
-            if (nextDY < 0) {
-                dy[total++] = -1;
-                nextDY = 1;
-            }
-        }
-    }
-    if (nextDY != (y >= p[2].y ? 1 : -1)) {
-        if (total > 0)
-            --total;
-        else {
-            if (fabs(p[2].y-y) < fabs(p[0].y-y))
-                x[total] = p[2].x;
-            dy[total++] = nextDY;
-        }
+int LinearSegment::verticalScanlineIntersections(double y[3], int dx[3], double x) const {
+    if ((x >= p[0].x && x < p[1].x) || (x >= p[1].x && x < p[0].x)) {
+        double param = (x-p[0].x)/(p[1].x-p[0].x);
+        y[0] = mix(p[0].y, p[1].y, param);
+        dx[0] = sign(p[1].x-p[0].x);
+        return 1;
     }
-    return total;
+    return 0;
 }
 
-int CubicSegment::scanlineIntersections(double x[3], int dy[3], double y) const {
+// p0, p1, q, r, s in the dimension of the scanline
+// p0 = scanline - first endpoint
+// p1 = scanline - last endpoint
+// q, r, s = first, second, third order derivatives (see bezier-solver)
+// descendingAtEnd is true if the first non-zero derivative at the last endpoint is negative
+static int curveScanlineIntersections(double t[3], int d[3], double p0, double p1, double q, double r, double s, bool descendingAtEnd) {
     int total = 0;
-    int nextDY = y > p[0].y ? 1 : -1;
-    x[total] = p[0].x;
-    if (p[0].y == y) {
-        if (p[0].y < p[1].y || (p[0].y == p[1].y && (p[0].y < p[2].y || (p[0].y == p[2].y && p[0].y < p[3].y))))
-            dy[total++] = 1;
+    int nextD = p0 > 0 ? 1 : -1;
+    t[total] = 0;
+    if (p0 == 0) {
+        if (q > 0 || (q == 0 && (r > 0 || (r == 0 && s > 0))))
+            d[total++] = 1;
         else
-            nextDY = 1;
+            nextD = 1;
     }
     {
-        Vector2 ab = p[1]-p[0];
-        Vector2 br = p[2]-p[1]-ab;
-        Vector2 as = (p[3]-p[2])-(p[2]-p[1])-br;
-        double t[3];
-        int solutions = solveCubic(t, as.y, 3*br.y, 3*ab.y, p[0].y-y);
+        double ts[3];
+        int solutions = solveCubic(ts, s, r, q, -p0);
         // Sort solutions
         double tmp;
         if (solutions >= 2) {
-            if (t[0] > t[1])
-                tmp = t[0], t[0] = t[1], t[1] = tmp;
-            if (solutions >= 3 && t[1] > t[2]) {
-                tmp = t[1], t[1] = t[2], t[2] = tmp;
-                if (t[0] > t[1])
-                    tmp = t[0], t[0] = t[1], t[1] = tmp;
+            if (ts[0] > ts[1])
+                tmp = ts[0], ts[0] = ts[1], ts[1] = tmp;
+            if (solutions >= 3 && ts[1] > ts[2]) {
+                tmp = ts[1], ts[1] = ts[2], ts[2] = tmp;
+                if (ts[0] > ts[1])
+                    tmp = ts[0], ts[0] = ts[1], ts[1] = tmp;
             }
         }
         for (int i = 0; i < solutions && total < 3; ++i) {
-            if (t[i] >= 0 && t[i] <= 1) {
-                x[total] = p[0].x+3*t[i]*ab.x+3*t[i]*t[i]*br.x+t[i]*t[i]*t[i]*as.x;
-                if (nextDY*(ab.y+2*t[i]*br.y+t[i]*t[i]*as.y) >= 0) {
-                    dy[total++] = nextDY;
-                    nextDY = -nextDY;
+            if (ts[i] >= 0 && ts[i] <= 1) {
+                t[total] = ts[i];
+                if (nextD*(q+(2*r+3*s*ts[i])*ts[i]) >= 0) {
+                    d[total++] = nextD;
+                    nextD = -nextD;
                 }
             }
         }
     }
-    if (p[3].y == y) {
-        if (nextDY > 0 && total > 0) {
+    if (p1 == 0) {
+        if (nextD > 0 && total > 0) {
             --total;
-            nextDY = -1;
+            nextD = -1;
         }
-        if ((p[3].y < p[2].y || (p[3].y == p[2].y && (p[3].y < p[1].y || (p[3].y == p[1].y && p[3].y < p[0].y)))) && total < 3) {
-            x[total] = p[3].x;
-            if (nextDY < 0) {
-                dy[total++] = -1;
-                nextDY = 1;
+        if (descendingAtEnd && total < 3) {
+            t[total] = 1;
+            if (nextD < 0) {
+                d[total++] = -1;
+                nextD = 1;
             }
         }
     }
-    if (nextDY != (y >= p[3].y ? 1 : -1)) {
+    if (nextD != (p1 >= 0 ? 1 : -1)) {
         if (total > 0)
             --total;
         else {
-            if (fabs(p[3].y-y) < fabs(p[0].y-y))
-                x[total] = p[3].x;
-            dy[total++] = nextDY;
+            if (fabs(p0) > fabs(p1))
+                t[total] = 1;
+            d[total++] = nextD;
         }
     }
     return total;
 }
 
+int QuadraticSegment::horizontalScanlineIntersections(double x[3], int dy[3], double y) const {
+    double p01 = p[1].y-p[0].y;
+    double p12 = p[2].y-p[1].y;
+    bool descendingAtEnd = p[2].y < p[1].y || (p[2].y == p[1].y && p[2].y < p[0].y);
+    double t[3] = { };
+    int n = curveScanlineIntersections(t, dy, y-p[0].y, y-p[2].y, 2*p01, p12-p01, 0, descendingAtEnd);
+    x[0] = point(t[0]).x;
+    x[1] = point(t[1]).x;
+    x[2] = point(t[2]).x;
+    return n;
+}
+
+int QuadraticSegment::verticalScanlineIntersections(double y[3], int dx[3], double x) const {
+    double p01 = p[1].x-p[0].x;
+    double p12 = p[2].x-p[1].x;
+    bool descendingAtEnd = p[2].x < p[1].x || (p[2].x == p[1].x && p[2].x < p[0].x);
+    double t[3] = { };
+    int n = curveScanlineIntersections(t, dx, x-p[0].x, x-p[2].x, 2*p01, p12-p01, 0, descendingAtEnd);
+    y[0] = point(t[0]).y;
+    y[1] = point(t[1]).y;
+    y[2] = point(t[2]).y;
+    return n;
+}
+
+int CubicSegment::horizontalScanlineIntersections(double x[3], int dy[3], double y) const {
+    double p01 = p[1].y-p[0].y;
+    double p12 = p[2].y-p[1].y;
+    double p23 = p[3].y-p[2].y;
+    bool descendingAtEnd = p[3].y < p[2].y || (p[3].y == p[2].y && (p[3].y < p[1].y || (p[3].y == p[1].y && p[3].y < p[0].y)));
+    double t[3] = { };
+    int n = curveScanlineIntersections(t, dy, y-p[0].y, y-p[3].y, 3*p01, 3*(p12-p01), p23-2*p12+p01, descendingAtEnd);
+    x[0] = point(t[0]).x;
+    x[1] = point(t[1]).x;
+    x[2] = point(t[2]).x;
+    return n;
+}
+
+int CubicSegment::verticalScanlineIntersections(double y[3], int dx[3], double x) const {
+    double p01 = p[1].x-p[0].x;
+    double p12 = p[2].x-p[1].x;
+    double p23 = p[3].x-p[2].x;
+    bool descendingAtEnd = p[3].x < p[2].x || (p[3].x == p[2].x && (p[3].x < p[1].x || (p[3].x == p[1].x && p[3].x < p[0].x)));
+    double t[3] = { };
+    int n = curveScanlineIntersections(t, dx, x-p[0].x, x-p[3].x, 3*p01, 3*(p12-p01), p23-2*p12+p01, descendingAtEnd);
+    y[0] = point(t[0]).y;
+    y[1] = point(t[1]).y;
+    y[2] = point(t[2]).y;
+    return n;
+}
+
 static void pointBounds(Point2 p, double &l, double &b, double &r, double &t) {
     if (p.x < l) l = p.x;
     if (p.y < b) b = p.y;

+ 9 - 0
core/edge-segments.h

@@ -33,6 +33,9 @@ public:
     virtual void distanceToPseudoDistance(SignedDistance &distance, Point2 origin, double param) const;
     /// Outputs a list of (at most three) intersections (their X coordinates) with an infinite horizontal scanline at y and returns how many there are.
     virtual int scanlineIntersections(double x[3], int dy[3], double y) const = 0;
+    virtual int horizontalScanlineIntersections(double x[3], int dy[3], double y) const = 0;
+    /// Outputs a list of (at most three) intersections (their Y coordinates) with an infinite vertical scanline at x and returns how many there are.
+    virtual int verticalScanlineIntersections(double y[3], int dx[3], double x) const = 0;
     /// Adjusts the bounding box to fit the edge segment.
     virtual void bound(double &l, double &b, double &r, double &t) const = 0;
 
@@ -67,6 +70,8 @@ public:
     double length() const;
     SignedDistance signedDistance(Point2 origin, double &param) const;
     int scanlineIntersections(double x[3], int dy[3], double y) const;
+    int horizontalScanlineIntersections(double x[3], int dy[3], double y) const;
+    int verticalScanlineIntersections(double y[3], int dx[3], double x) const;
     void bound(double &l, double &b, double &r, double &t) const;
 
     void reverse();
@@ -96,6 +101,8 @@ public:
     double length() const;
     SignedDistance signedDistance(Point2 origin, double &param) const;
     int scanlineIntersections(double x[3], int dy[3], double y) const;
+    int horizontalScanlineIntersections(double x[3], int dy[3], double y) const;
+    int verticalScanlineIntersections(double y[3], int dx[3], double x) const;
     void bound(double &l, double &b, double &r, double &t) const;
 
     void reverse();
@@ -126,6 +133,8 @@ public:
     Vector2 directionChange(double param) const;
     SignedDistance signedDistance(Point2 origin, double &param) const;
     int scanlineIntersections(double x[3], int dy[3], double y) const;
+    int horizontalScanlineIntersections(double x[3], int dy[3], double y) const;
+    int verticalScanlineIntersections(double y[3], int dx[3], double x) const;
     void bound(double &l, double &b, double &r, double &t) const;
 
     void reverse();