Added optional "ambiguous" outgoing argument to XRay queries so that
authorkbr@chromium.org <kbr@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Wed, 7 Jul 2010 22:20:35 +0000 (22:20 +0000)
committerkbr@chromium.org <kbr@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Wed, 7 Jul 2010 22:20:35 +0000 (22:20 +0000)
calling code may choose different y-coordinates for better robustness.
Tested and verified manually inside O3D.

BUG=none
TEST=none

Review URL: http://codereview.appspot.com/1695051

git-svn-id: http://skia.googlecode.com/svn/trunk@586 2bbb7eff-a529-9590-31e7-b0007b416f81

include/core/SkGeometry.h
src/core/SkGeometry.cpp

index b8ab74caef9721cc1c0f04340157fe113397dd74..a20978326d376153083b8106203fa0aad4b6199e 100644 (file)
  */
 typedef SkPoint SkXRay;
 
-/** Given a line segment from pts[0] to pts[1], and ax xray, return true if
-    they intersect.
+/** Given a line segment from pts[0] to pts[1], and an xray, return true if
+    they intersect. Optional outgoing "ambiguous" argument indicates
+    whether the answer is ambiguous because the query occurred exactly at
+    one of the endpoints' y coordinates, indicating that another query y
+    coordinate is preferred for robustness.
 */
-bool SkXRayCrossesLine(const SkXRay& pt, const SkPoint pts[2]);
+bool SkXRayCrossesLine(const SkXRay& pt, const SkPoint pts[2], bool* ambiguous = NULL);
 
 /** Given a quadratic equation Ax^2 + Bx + C = 0, return 0, 1, 2 roots for the
     equation.
@@ -155,8 +158,12 @@ int SkChopCubicAtMaxCurvature(const SkPoint src[4], SkPoint dst[13], SkScalar tV
     left of the curve, the line is not considered to cross the curve,
     but if it is equal to cubic[3].fY then it is considered to
     cross.
+    Optional outgoing "ambiguous" argument indicates whether the answer is
+    ambiguous because the query occurred exactly at one of the endpoints' y
+    coordinates, indicating that another query y coordinate is preferred
+    for robustness.
  */
-bool SkXRayCrossesMonotonicCubic(const SkXRay& pt, const SkPoint cubic[4]);
+bool SkXRayCrossesMonotonicCubic(const SkXRay& pt, const SkPoint cubic[4], bool* ambiguous = NULL);
 
 /** Given an arbitrary cubic bezier, return the number of times an xray crosses
     the cubic. Valid return values are [0..3]
@@ -165,8 +172,12 @@ bool SkXRayCrossesMonotonicCubic(const SkXRay& pt, const SkPoint cubic[4]);
     left of the curve, the line is not considered to cross the curve,
     but if it is equal to cubic[3].fY then it is considered to
     cross.
+    Optional outgoing "ambiguous" argument indicates whether the answer is
+    ambiguous because the query occurred exactly at one of the endpoints' y
+    coordinates or at a tangent point, indicating that another query y
+    coordinate is preferred for robustness.
  */
-int SkNumXRayCrossingsForCubic(const SkXRay& pt, const SkPoint cubic[4]);
+int SkNumXRayCrossingsForCubic(const SkXRay& pt, const SkPoint cubic[4], bool* ambiguous = NULL);
 
 ///////////////////////////////////////////////////////////////////////////////////////////
 
index 34a0fe0647aff914479cfc4a8432ef69c79be533..3c9e9f90f0ed94b63ee475b09ffc83d2c88c9b90 100644 (file)
 #include "Sk64.h"
 #include "SkMatrix.h"
 
-bool SkXRayCrossesLine(const SkXRay& pt, const SkPoint pts[2]) {
+bool SkXRayCrossesLine(const SkXRay& pt, const SkPoint pts[2], bool* ambiguous) {
+    if (ambiguous) {
+        *ambiguous = false;
+    }
     // Determine quick discards.
     // Consider query line going exactly through point 0 to not
     // intersect, for symmetry with SkXRayCrossesMonotonicCubic.
-    if (pt.fY == pts[0].fY)
+    if (pt.fY == pts[0].fY) {
+        if (ambiguous) {
+            *ambiguous = true;
+        }
         return false;
+    }
     if (pt.fY < pts[0].fY && pt.fY < pts[1].fY)
         return false;
     if (pt.fY > pts[0].fY && pt.fY > pts[1].fY)
@@ -34,10 +41,27 @@ bool SkXRayCrossesLine(const SkXRay& pt, const SkPoint pts[2]) {
     // Determine degenerate cases
     if (SkScalarNearlyZero(pts[0].fY - pts[1].fY))
         return false;
-    if (SkScalarNearlyZero(pts[0].fX - pts[1].fX))
+    if (SkScalarNearlyZero(pts[0].fX - pts[1].fX)) {
         // We've already determined the query point lies within the
         // vertical range of the line segment.
-        return pt.fX <= pts[0].fX;
+        if (pt.fX <= pts[0].fX) {
+            if (ambiguous) {
+                *ambiguous = (pt.fY == pts[1].fY);
+            }
+            return true;
+        }
+        return false;
+    }
+    // Ambiguity check
+    if (pt.fY == pts[1].fY) {
+        if (pt.fX <= pts[1].fX) {
+            if (ambiguous) {
+                *ambiguous = true;
+            }
+            return true;
+        }
+        return false;
+    }
     // Full line segment evaluation
     SkScalar delta_y = pts[1].fY - pts[0].fY;
     SkScalar delta_x = pts[1].fX - pts[0].fX;
@@ -986,7 +1010,11 @@ int SkChopCubicAtMaxCurvature(const SkPoint src[4], SkPoint dst[13], SkScalar tV
     return count + 1;
 }
 
-bool SkXRayCrossesMonotonicCubic(const SkXRay& pt, const SkPoint cubic[4]) {
+bool SkXRayCrossesMonotonicCubic(const SkXRay& pt, const SkPoint cubic[4], bool* ambiguous) {
+    if (ambiguous) {
+        *ambiguous = false;
+    }
+
     // Find the minimum and maximum y of the extrema, which are the
     // first and last points since this cubic is monotonic
     SkScalar min_y = SkMinScalar(cubic[0].fY, cubic[3].fY);
@@ -996,9 +1024,14 @@ bool SkXRayCrossesMonotonicCubic(const SkXRay& pt, const SkPoint cubic[4]) {
         || pt.fY < min_y
         || pt.fY > max_y) {
         // The query line definitely does not cross the curve
+        if (ambiguous) {
+            *ambiguous = (pt.fY == cubic[0].fY);
+        }
         return false;
     }
 
+    bool pt_at_extremum = (pt.fY == cubic[3].fY);
+
     SkScalar min_x =
         SkMinScalar(
             SkMinScalar(
@@ -1007,6 +1040,9 @@ bool SkXRayCrossesMonotonicCubic(const SkXRay& pt, const SkPoint cubic[4]) {
             cubic[3].fX);
     if (pt.fX < min_x) {
         // The query line definitely crosses the curve
+        if (ambiguous) {
+            *ambiguous = pt_at_extremum;
+        }
         return true;
     }
 
@@ -1053,23 +1089,39 @@ bool SkXRayCrossesMonotonicCubic(const SkXRay& pt, const SkPoint cubic[4]) {
     } while (++iter < kMaxIter
              && !SkScalarNearlyZero(eval.fY - pt.fY));
     if (pt.fX <= eval.fX) {
+        if (ambiguous) {
+            *ambiguous = pt_at_extremum;
+        }
         return true;
     }
     return false;
 }
 
-int SkNumXRayCrossingsForCubic(const SkXRay& pt, const SkPoint cubic[4]) {
+int SkNumXRayCrossingsForCubic(const SkXRay& pt, const SkPoint cubic[4], bool* ambiguous) {
     int num_crossings = 0;
     SkPoint monotonic_cubics[10];
     int num_monotonic_cubics = SkChopCubicAtYExtrema(cubic, monotonic_cubics);
-    if (SkXRayCrossesMonotonicCubic(pt, &monotonic_cubics[0]))
+    if (ambiguous) {
+        *ambiguous = false;
+    }
+    bool locally_ambiguous;
+    if (SkXRayCrossesMonotonicCubic(pt, &monotonic_cubics[0], &locally_ambiguous))
         ++num_crossings;
+    if (ambiguous) {
+        *ambiguous |= locally_ambiguous;
+    }
     if (num_monotonic_cubics > 0)
-        if (SkXRayCrossesMonotonicCubic(pt, &monotonic_cubics[3]))
+        if (SkXRayCrossesMonotonicCubic(pt, &monotonic_cubics[3], &locally_ambiguous))
             ++num_crossings;
+    if (ambiguous) {
+        *ambiguous |= locally_ambiguous;
+    }
     if (num_monotonic_cubics > 1)
-        if (SkXRayCrossesMonotonicCubic(pt, &monotonic_cubics[6]))
+        if (SkXRayCrossesMonotonicCubic(pt, &monotonic_cubics[6], &locally_ambiguous))
             ++num_crossings;
+    if (ambiguous) {
+        *ambiguous |= locally_ambiguous;
+    }
     return num_crossings;
 }