gfx/skia/trunk/src/pathops/SkDCubicIntersection.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /*
michael@0 2 * Copyright 2012 Google Inc.
michael@0 3 *
michael@0 4 * Use of this source code is governed by a BSD-style license that can be
michael@0 5 * found in the LICENSE file.
michael@0 6 */
michael@0 7
michael@0 8 #include "SkIntersections.h"
michael@0 9 #include "SkPathOpsCubic.h"
michael@0 10 #include "SkPathOpsLine.h"
michael@0 11 #include "SkPathOpsPoint.h"
michael@0 12 #include "SkPathOpsQuad.h"
michael@0 13 #include "SkPathOpsRect.h"
michael@0 14 #include "SkReduceOrder.h"
michael@0 15 #include "SkTSort.h"
michael@0 16
michael@0 17 #if ONE_OFF_DEBUG
michael@0 18 static const double tLimits1[2][2] = {{0.3, 0.4}, {0.8, 0.9}};
michael@0 19 static const double tLimits2[2][2] = {{-0.8, -0.9}, {-0.8, -0.9}};
michael@0 20 #endif
michael@0 21
michael@0 22 #define DEBUG_QUAD_PART ONE_OFF_DEBUG && 1
michael@0 23 #define DEBUG_QUAD_PART_SHOW_SIMPLE DEBUG_QUAD_PART && 0
michael@0 24 #define SWAP_TOP_DEBUG 0
michael@0 25
michael@0 26 static const int kCubicToQuadSubdivisionDepth = 8; // slots reserved for cubic to quads subdivision
michael@0 27
michael@0 28 static int quadPart(const SkDCubic& cubic, double tStart, double tEnd, SkReduceOrder* reducer) {
michael@0 29 SkDCubic part = cubic.subDivide(tStart, tEnd);
michael@0 30 SkDQuad quad = part.toQuad();
michael@0 31 // FIXME: should reduceOrder be looser in this use case if quartic is going to blow up on an
michael@0 32 // extremely shallow quadratic?
michael@0 33 int order = reducer->reduce(quad);
michael@0 34 #if DEBUG_QUAD_PART
michael@0 35 SkDebugf("%s cubic=(%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g)"
michael@0 36 " t=(%1.9g,%1.9g)\n", __FUNCTION__, cubic[0].fX, cubic[0].fY,
michael@0 37 cubic[1].fX, cubic[1].fY, cubic[2].fX, cubic[2].fY,
michael@0 38 cubic[3].fX, cubic[3].fY, tStart, tEnd);
michael@0 39 SkDebugf(" {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n"
michael@0 40 " {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n",
michael@0 41 part[0].fX, part[0].fY, part[1].fX, part[1].fY, part[2].fX, part[2].fY,
michael@0 42 part[3].fX, part[3].fY, quad[0].fX, quad[0].fY,
michael@0 43 quad[1].fX, quad[1].fY, quad[2].fX, quad[2].fY);
michael@0 44 #if DEBUG_QUAD_PART_SHOW_SIMPLE
michael@0 45 SkDebugf("%s simple=(%1.9g,%1.9g", __FUNCTION__, reducer->fQuad[0].fX, reducer->fQuad[0].fY);
michael@0 46 if (order > 1) {
michael@0 47 SkDebugf(" %1.9g,%1.9g", reducer->fQuad[1].fX, reducer->fQuad[1].fY);
michael@0 48 }
michael@0 49 if (order > 2) {
michael@0 50 SkDebugf(" %1.9g,%1.9g", reducer->fQuad[2].fX, reducer->fQuad[2].fY);
michael@0 51 }
michael@0 52 SkDebugf(")\n");
michael@0 53 SkASSERT(order < 4 && order > 0);
michael@0 54 #endif
michael@0 55 #endif
michael@0 56 return order;
michael@0 57 }
michael@0 58
michael@0 59 static void intersectWithOrder(const SkDQuad& simple1, int order1, const SkDQuad& simple2,
michael@0 60 int order2, SkIntersections& i) {
michael@0 61 if (order1 == 3 && order2 == 3) {
michael@0 62 i.intersect(simple1, simple2);
michael@0 63 } else if (order1 <= 2 && order2 <= 2) {
michael@0 64 i.intersect((const SkDLine&) simple1, (const SkDLine&) simple2);
michael@0 65 } else if (order1 == 3 && order2 <= 2) {
michael@0 66 i.intersect(simple1, (const SkDLine&) simple2);
michael@0 67 } else {
michael@0 68 SkASSERT(order1 <= 2 && order2 == 3);
michael@0 69 i.intersect(simple2, (const SkDLine&) simple1);
michael@0 70 i.swapPts();
michael@0 71 }
michael@0 72 }
michael@0 73
michael@0 74 // this flavor centers potential intersections recursively. In contrast, '2' may inadvertently
michael@0 75 // chase intersections near quadratic ends, requiring odd hacks to find them.
michael@0 76 static void intersect(const SkDCubic& cubic1, double t1s, double t1e, const SkDCubic& cubic2,
michael@0 77 double t2s, double t2e, double precisionScale, SkIntersections& i) {
michael@0 78 i.upDepth();
michael@0 79 SkDCubic c1 = cubic1.subDivide(t1s, t1e);
michael@0 80 SkDCubic c2 = cubic2.subDivide(t2s, t2e);
michael@0 81 SkSTArray<kCubicToQuadSubdivisionDepth, double, true> ts1;
michael@0 82 // OPTIMIZE: if c1 == c2, call once (happens when detecting self-intersection)
michael@0 83 c1.toQuadraticTs(c1.calcPrecision() * precisionScale, &ts1);
michael@0 84 SkSTArray<kCubicToQuadSubdivisionDepth, double, true> ts2;
michael@0 85 c2.toQuadraticTs(c2.calcPrecision() * precisionScale, &ts2);
michael@0 86 double t1Start = t1s;
michael@0 87 int ts1Count = ts1.count();
michael@0 88 for (int i1 = 0; i1 <= ts1Count; ++i1) {
michael@0 89 const double tEnd1 = i1 < ts1Count ? ts1[i1] : 1;
michael@0 90 const double t1 = t1s + (t1e - t1s) * tEnd1;
michael@0 91 SkReduceOrder s1;
michael@0 92 int o1 = quadPart(cubic1, t1Start, t1, &s1);
michael@0 93 double t2Start = t2s;
michael@0 94 int ts2Count = ts2.count();
michael@0 95 for (int i2 = 0; i2 <= ts2Count; ++i2) {
michael@0 96 const double tEnd2 = i2 < ts2Count ? ts2[i2] : 1;
michael@0 97 const double t2 = t2s + (t2e - t2s) * tEnd2;
michael@0 98 if (&cubic1 == &cubic2 && t1Start >= t2Start) {
michael@0 99 t2Start = t2;
michael@0 100 continue;
michael@0 101 }
michael@0 102 SkReduceOrder s2;
michael@0 103 int o2 = quadPart(cubic2, t2Start, t2, &s2);
michael@0 104 #if ONE_OFF_DEBUG
michael@0 105 char tab[] = " ";
michael@0 106 if (tLimits1[0][0] >= t1Start && tLimits1[0][1] <= t1
michael@0 107 && tLimits1[1][0] >= t2Start && tLimits1[1][1] <= t2) {
michael@0 108 SkDebugf("%.*s %s t1=(%1.9g,%1.9g) t2=(%1.9g,%1.9g)", i.depth()*2, tab,
michael@0 109 __FUNCTION__, t1Start, t1, t2Start, t2);
michael@0 110 SkIntersections xlocals;
michael@0 111 xlocals.allowNear(false);
michael@0 112 intersectWithOrder(s1.fQuad, o1, s2.fQuad, o2, xlocals);
michael@0 113 SkDebugf(" xlocals.fUsed=%d\n", xlocals.used());
michael@0 114 }
michael@0 115 #endif
michael@0 116 SkIntersections locals;
michael@0 117 locals.allowNear(false);
michael@0 118 intersectWithOrder(s1.fQuad, o1, s2.fQuad, o2, locals);
michael@0 119 int tCount = locals.used();
michael@0 120 for (int tIdx = 0; tIdx < tCount; ++tIdx) {
michael@0 121 double to1 = t1Start + (t1 - t1Start) * locals[0][tIdx];
michael@0 122 double to2 = t2Start + (t2 - t2Start) * locals[1][tIdx];
michael@0 123 // if the computed t is not sufficiently precise, iterate
michael@0 124 SkDPoint p1 = cubic1.ptAtT(to1);
michael@0 125 SkDPoint p2 = cubic2.ptAtT(to2);
michael@0 126 if (p1.approximatelyEqual(p2)) {
michael@0 127 // FIXME: local edge may be coincident -- experiment with not propagating coincidence to caller
michael@0 128 // SkASSERT(!locals.isCoincident(tIdx));
michael@0 129 if (&cubic1 != &cubic2 || !approximately_equal(to1, to2)) {
michael@0 130 if (i.swapped()) { // FIXME: insert should respect swap
michael@0 131 i.insert(to2, to1, p1);
michael@0 132 } else {
michael@0 133 i.insert(to1, to2, p1);
michael@0 134 }
michael@0 135 }
michael@0 136 } else {
michael@0 137 double offset = precisionScale / 16; // FIME: const is arbitrary: test, refine
michael@0 138 double c1Bottom = tIdx == 0 ? 0 :
michael@0 139 (t1Start + (t1 - t1Start) * locals[0][tIdx - 1] + to1) / 2;
michael@0 140 double c1Min = SkTMax(c1Bottom, to1 - offset);
michael@0 141 double c1Top = tIdx == tCount - 1 ? 1 :
michael@0 142 (t1Start + (t1 - t1Start) * locals[0][tIdx + 1] + to1) / 2;
michael@0 143 double c1Max = SkTMin(c1Top, to1 + offset);
michael@0 144 double c2Min = SkTMax(0., to2 - offset);
michael@0 145 double c2Max = SkTMin(1., to2 + offset);
michael@0 146 #if ONE_OFF_DEBUG
michael@0 147 SkDebugf("%.*s %s 1 contains1=%d/%d contains2=%d/%d\n", i.depth()*2, tab,
michael@0 148 __FUNCTION__,
michael@0 149 c1Min <= tLimits1[0][1] && tLimits1[0][0] <= c1Max
michael@0 150 && c2Min <= tLimits1[1][1] && tLimits1[1][0] <= c2Max,
michael@0 151 to1 - offset <= tLimits1[0][1] && tLimits1[0][0] <= to1 + offset
michael@0 152 && to2 - offset <= tLimits1[1][1] && tLimits1[1][0] <= to2 + offset,
michael@0 153 c1Min <= tLimits2[0][1] && tLimits2[0][0] <= c1Max
michael@0 154 && c2Min <= tLimits2[1][1] && tLimits2[1][0] <= c2Max,
michael@0 155 to1 - offset <= tLimits2[0][1] && tLimits2[0][0] <= to1 + offset
michael@0 156 && to2 - offset <= tLimits2[1][1] && tLimits2[1][0] <= to2 + offset);
michael@0 157 SkDebugf("%.*s %s 1 c1Bottom=%1.9g c1Top=%1.9g c2Bottom=%1.9g c2Top=%1.9g"
michael@0 158 " 1-o=%1.9g 1+o=%1.9g 2-o=%1.9g 2+o=%1.9g offset=%1.9g\n",
michael@0 159 i.depth()*2, tab, __FUNCTION__, c1Bottom, c1Top, 0., 1.,
michael@0 160 to1 - offset, to1 + offset, to2 - offset, to2 + offset, offset);
michael@0 161 SkDebugf("%.*s %s 1 to1=%1.9g to2=%1.9g c1Min=%1.9g c1Max=%1.9g c2Min=%1.9g"
michael@0 162 " c2Max=%1.9g\n", i.depth()*2, tab, __FUNCTION__, to1, to2, c1Min,
michael@0 163 c1Max, c2Min, c2Max);
michael@0 164 #endif
michael@0 165 intersect(cubic1, c1Min, c1Max, cubic2, c2Min, c2Max, offset, i);
michael@0 166 #if ONE_OFF_DEBUG
michael@0 167 SkDebugf("%.*s %s 1 i.used=%d t=%1.9g\n", i.depth()*2, tab, __FUNCTION__,
michael@0 168 i.used(), i.used() > 0 ? i[0][i.used() - 1] : -1);
michael@0 169 #endif
michael@0 170 if (tCount > 1) {
michael@0 171 c1Min = SkTMax(0., to1 - offset);
michael@0 172 c1Max = SkTMin(1., to1 + offset);
michael@0 173 double c2Bottom = tIdx == 0 ? to2 :
michael@0 174 (t2Start + (t2 - t2Start) * locals[1][tIdx - 1] + to2) / 2;
michael@0 175 double c2Top = tIdx == tCount - 1 ? to2 :
michael@0 176 (t2Start + (t2 - t2Start) * locals[1][tIdx + 1] + to2) / 2;
michael@0 177 if (c2Bottom > c2Top) {
michael@0 178 SkTSwap(c2Bottom, c2Top);
michael@0 179 }
michael@0 180 if (c2Bottom == to2) {
michael@0 181 c2Bottom = 0;
michael@0 182 }
michael@0 183 if (c2Top == to2) {
michael@0 184 c2Top = 1;
michael@0 185 }
michael@0 186 c2Min = SkTMax(c2Bottom, to2 - offset);
michael@0 187 c2Max = SkTMin(c2Top, to2 + offset);
michael@0 188 #if ONE_OFF_DEBUG
michael@0 189 SkDebugf("%.*s %s 2 contains1=%d/%d contains2=%d/%d\n", i.depth()*2, tab,
michael@0 190 __FUNCTION__,
michael@0 191 c1Min <= tLimits1[0][1] && tLimits1[0][0] <= c1Max
michael@0 192 && c2Min <= tLimits1[1][1] && tLimits1[1][0] <= c2Max,
michael@0 193 to1 - offset <= tLimits1[0][1] && tLimits1[0][0] <= to1 + offset
michael@0 194 && to2 - offset <= tLimits1[1][1] && tLimits1[1][0] <= to2 + offset,
michael@0 195 c1Min <= tLimits2[0][1] && tLimits2[0][0] <= c1Max
michael@0 196 && c2Min <= tLimits2[1][1] && tLimits2[1][0] <= c2Max,
michael@0 197 to1 - offset <= tLimits2[0][1] && tLimits2[0][0] <= to1 + offset
michael@0 198 && to2 - offset <= tLimits2[1][1] && tLimits2[1][0] <= to2 + offset);
michael@0 199 SkDebugf("%.*s %s 2 c1Bottom=%1.9g c1Top=%1.9g c2Bottom=%1.9g c2Top=%1.9g"
michael@0 200 " 1-o=%1.9g 1+o=%1.9g 2-o=%1.9g 2+o=%1.9g offset=%1.9g\n",
michael@0 201 i.depth()*2, tab, __FUNCTION__, 0., 1., c2Bottom, c2Top,
michael@0 202 to1 - offset, to1 + offset, to2 - offset, to2 + offset, offset);
michael@0 203 SkDebugf("%.*s %s 2 to1=%1.9g to2=%1.9g c1Min=%1.9g c1Max=%1.9g c2Min=%1.9g"
michael@0 204 " c2Max=%1.9g\n", i.depth()*2, tab, __FUNCTION__, to1, to2, c1Min,
michael@0 205 c1Max, c2Min, c2Max);
michael@0 206 #endif
michael@0 207 intersect(cubic1, c1Min, c1Max, cubic2, c2Min, c2Max, offset, i);
michael@0 208 #if ONE_OFF_DEBUG
michael@0 209 SkDebugf("%.*s %s 2 i.used=%d t=%1.9g\n", i.depth()*2, tab, __FUNCTION__,
michael@0 210 i.used(), i.used() > 0 ? i[0][i.used() - 1] : -1);
michael@0 211 #endif
michael@0 212 c1Min = SkTMax(c1Bottom, to1 - offset);
michael@0 213 c1Max = SkTMin(c1Top, to1 + offset);
michael@0 214 #if ONE_OFF_DEBUG
michael@0 215 SkDebugf("%.*s %s 3 contains1=%d/%d contains2=%d/%d\n", i.depth()*2, tab,
michael@0 216 __FUNCTION__,
michael@0 217 c1Min <= tLimits1[0][1] && tLimits1[0][0] <= c1Max
michael@0 218 && c2Min <= tLimits1[1][1] && tLimits1[1][0] <= c2Max,
michael@0 219 to1 - offset <= tLimits1[0][1] && tLimits1[0][0] <= to1 + offset
michael@0 220 && to2 - offset <= tLimits1[1][1] && tLimits1[1][0] <= to2 + offset,
michael@0 221 c1Min <= tLimits2[0][1] && tLimits2[0][0] <= c1Max
michael@0 222 && c2Min <= tLimits2[1][1] && tLimits2[1][0] <= c2Max,
michael@0 223 to1 - offset <= tLimits2[0][1] && tLimits2[0][0] <= to1 + offset
michael@0 224 && to2 - offset <= tLimits2[1][1] && tLimits2[1][0] <= to2 + offset);
michael@0 225 SkDebugf("%.*s %s 3 c1Bottom=%1.9g c1Top=%1.9g c2Bottom=%1.9g c2Top=%1.9g"
michael@0 226 " 1-o=%1.9g 1+o=%1.9g 2-o=%1.9g 2+o=%1.9g offset=%1.9g\n",
michael@0 227 i.depth()*2, tab, __FUNCTION__, 0., 1., c2Bottom, c2Top,
michael@0 228 to1 - offset, to1 + offset, to2 - offset, to2 + offset, offset);
michael@0 229 SkDebugf("%.*s %s 3 to1=%1.9g to2=%1.9g c1Min=%1.9g c1Max=%1.9g c2Min=%1.9g"
michael@0 230 " c2Max=%1.9g\n", i.depth()*2, tab, __FUNCTION__, to1, to2, c1Min,
michael@0 231 c1Max, c2Min, c2Max);
michael@0 232 #endif
michael@0 233 intersect(cubic1, c1Min, c1Max, cubic2, c2Min, c2Max, offset, i);
michael@0 234 #if ONE_OFF_DEBUG
michael@0 235 SkDebugf("%.*s %s 3 i.used=%d t=%1.9g\n", i.depth()*2, tab, __FUNCTION__,
michael@0 236 i.used(), i.used() > 0 ? i[0][i.used() - 1] : -1);
michael@0 237 #endif
michael@0 238 }
michael@0 239 // intersect(cubic1, c1Min, c1Max, cubic2, c2Min, c2Max, offset, i);
michael@0 240 // FIXME: if no intersection is found, either quadratics intersected where
michael@0 241 // cubics did not, or the intersection was missed. In the former case, expect
michael@0 242 // the quadratics to be nearly parallel at the point of intersection, and check
michael@0 243 // for that.
michael@0 244 }
michael@0 245 }
michael@0 246 t2Start = t2;
michael@0 247 }
michael@0 248 t1Start = t1;
michael@0 249 }
michael@0 250 i.downDepth();
michael@0 251 }
michael@0 252
michael@0 253 // if two ends intersect, check middle for coincidence
michael@0 254 bool SkIntersections::cubicCheckCoincidence(const SkDCubic& c1, const SkDCubic& c2) {
michael@0 255 if (fUsed < 2) {
michael@0 256 return false;
michael@0 257 }
michael@0 258 int last = fUsed - 1;
michael@0 259 double tRange1 = fT[0][last] - fT[0][0];
michael@0 260 double tRange2 = fT[1][last] - fT[1][0];
michael@0 261 for (int index = 1; index < 5; ++index) {
michael@0 262 double testT1 = fT[0][0] + tRange1 * index / 5;
michael@0 263 double testT2 = fT[1][0] + tRange2 * index / 5;
michael@0 264 SkDPoint testPt1 = c1.ptAtT(testT1);
michael@0 265 SkDPoint testPt2 = c2.ptAtT(testT2);
michael@0 266 if (!testPt1.approximatelyEqual(testPt2)) {
michael@0 267 return false;
michael@0 268 }
michael@0 269 }
michael@0 270 if (fUsed > 2) {
michael@0 271 fPt[1] = fPt[last];
michael@0 272 fT[0][1] = fT[0][last];
michael@0 273 fT[1][1] = fT[1][last];
michael@0 274 fUsed = 2;
michael@0 275 }
michael@0 276 fIsCoincident[0] = fIsCoincident[1] = 0x03;
michael@0 277 return true;
michael@0 278 }
michael@0 279
michael@0 280 #define LINE_FRACTION 0.1
michael@0 281
michael@0 282 // intersect the end of the cubic with the other. Try lines from the end to control and opposite
michael@0 283 // end to determine range of t on opposite cubic.
michael@0 284 bool SkIntersections::cubicExactEnd(const SkDCubic& cubic1, bool start, const SkDCubic& cubic2) {
michael@0 285 int t1Index = start ? 0 : 3;
michael@0 286 double testT = (double) !start;
michael@0 287 bool swap = swapped();
michael@0 288 // quad/quad at this point checks to see if exact matches have already been found
michael@0 289 // cubic/cubic can't reject so easily since cubics can intersect same point more than once
michael@0 290 SkDLine tmpLine;
michael@0 291 tmpLine[0] = tmpLine[1] = cubic2[t1Index];
michael@0 292 tmpLine[1].fX += cubic2[2 - start].fY - cubic2[t1Index].fY;
michael@0 293 tmpLine[1].fY -= cubic2[2 - start].fX - cubic2[t1Index].fX;
michael@0 294 SkIntersections impTs;
michael@0 295 impTs.allowNear(false);
michael@0 296 impTs.intersectRay(cubic1, tmpLine);
michael@0 297 for (int index = 0; index < impTs.used(); ++index) {
michael@0 298 SkDPoint realPt = impTs.pt(index);
michael@0 299 if (!tmpLine[0].approximatelyEqual(realPt)) {
michael@0 300 continue;
michael@0 301 }
michael@0 302 if (swap) {
michael@0 303 insert(testT, impTs[0][index], tmpLine[0]);
michael@0 304 } else {
michael@0 305 insert(impTs[0][index], testT, tmpLine[0]);
michael@0 306 }
michael@0 307 return true;
michael@0 308 }
michael@0 309 return false;
michael@0 310 }
michael@0 311
michael@0 312 void SkIntersections::cubicNearEnd(const SkDCubic& cubic1, bool start, const SkDCubic& cubic2,
michael@0 313 const SkDRect& bounds2) {
michael@0 314 SkDLine line;
michael@0 315 int t1Index = start ? 0 : 3;
michael@0 316 double testT = (double) !start;
michael@0 317 // don't bother if the two cubics are connnected
michael@0 318 static const int kPointsInCubic = 4; // FIXME: move to DCubic, replace '4' with this
michael@0 319 static const int kMaxLineCubicIntersections = 3;
michael@0 320 SkSTArray<(kMaxLineCubicIntersections - 1) * kMaxLineCubicIntersections, double, true> tVals;
michael@0 321 line[0] = cubic1[t1Index];
michael@0 322 // this variant looks for intersections with the end point and lines parallel to other points
michael@0 323 for (int index = 0; index < kPointsInCubic; ++index) {
michael@0 324 if (index == t1Index) {
michael@0 325 continue;
michael@0 326 }
michael@0 327 SkDVector dxy1 = cubic1[index] - line[0];
michael@0 328 dxy1 /= SkDCubic::gPrecisionUnit;
michael@0 329 line[1] = line[0] + dxy1;
michael@0 330 SkDRect lineBounds;
michael@0 331 lineBounds.setBounds(line);
michael@0 332 if (!bounds2.intersects(&lineBounds)) {
michael@0 333 continue;
michael@0 334 }
michael@0 335 SkIntersections local;
michael@0 336 if (!local.intersect(cubic2, line)) {
michael@0 337 continue;
michael@0 338 }
michael@0 339 for (int idx2 = 0; idx2 < local.used(); ++idx2) {
michael@0 340 double foundT = local[0][idx2];
michael@0 341 if (approximately_less_than_zero(foundT)
michael@0 342 || approximately_greater_than_one(foundT)) {
michael@0 343 continue;
michael@0 344 }
michael@0 345 if (local.pt(idx2).approximatelyEqual(line[0])) {
michael@0 346 if (swapped()) { // FIXME: insert should respect swap
michael@0 347 insert(foundT, testT, line[0]);
michael@0 348 } else {
michael@0 349 insert(testT, foundT, line[0]);
michael@0 350 }
michael@0 351 } else {
michael@0 352 tVals.push_back(foundT);
michael@0 353 }
michael@0 354 }
michael@0 355 }
michael@0 356 if (tVals.count() == 0) {
michael@0 357 return;
michael@0 358 }
michael@0 359 SkTQSort<double>(tVals.begin(), tVals.end() - 1);
michael@0 360 double tMin1 = start ? 0 : 1 - LINE_FRACTION;
michael@0 361 double tMax1 = start ? LINE_FRACTION : 1;
michael@0 362 int tIdx = 0;
michael@0 363 do {
michael@0 364 int tLast = tIdx;
michael@0 365 while (tLast + 1 < tVals.count() && roughly_equal(tVals[tLast + 1], tVals[tIdx])) {
michael@0 366 ++tLast;
michael@0 367 }
michael@0 368 double tMin2 = SkTMax(tVals[tIdx] - LINE_FRACTION, 0.0);
michael@0 369 double tMax2 = SkTMin(tVals[tLast] + LINE_FRACTION, 1.0);
michael@0 370 int lastUsed = used();
michael@0 371 ::intersect(cubic1, tMin1, tMax1, cubic2, tMin2, tMax2, 1, *this);
michael@0 372 if (lastUsed == used()) {
michael@0 373 tMin2 = SkTMax(tVals[tIdx] - (1.0 / SkDCubic::gPrecisionUnit), 0.0);
michael@0 374 tMax2 = SkTMin(tVals[tLast] + (1.0 / SkDCubic::gPrecisionUnit), 1.0);
michael@0 375 ::intersect(cubic1, tMin1, tMax1, cubic2, tMin2, tMax2, 1, *this);
michael@0 376 }
michael@0 377 tIdx = tLast + 1;
michael@0 378 } while (tIdx < tVals.count());
michael@0 379 return;
michael@0 380 }
michael@0 381
michael@0 382 const double CLOSE_ENOUGH = 0.001;
michael@0 383
michael@0 384 static bool closeStart(const SkDCubic& cubic, int cubicIndex, SkIntersections& i, SkDPoint& pt) {
michael@0 385 if (i[cubicIndex][0] != 0 || i[cubicIndex][1] > CLOSE_ENOUGH) {
michael@0 386 return false;
michael@0 387 }
michael@0 388 pt = cubic.ptAtT((i[cubicIndex][0] + i[cubicIndex][1]) / 2);
michael@0 389 return true;
michael@0 390 }
michael@0 391
michael@0 392 static bool closeEnd(const SkDCubic& cubic, int cubicIndex, SkIntersections& i, SkDPoint& pt) {
michael@0 393 int last = i.used() - 1;
michael@0 394 if (i[cubicIndex][last] != 1 || i[cubicIndex][last - 1] < 1 - CLOSE_ENOUGH) {
michael@0 395 return false;
michael@0 396 }
michael@0 397 pt = cubic.ptAtT((i[cubicIndex][last] + i[cubicIndex][last - 1]) / 2);
michael@0 398 return true;
michael@0 399 }
michael@0 400
michael@0 401 static bool only_end_pts_in_common(const SkDCubic& c1, const SkDCubic& c2) {
michael@0 402 // the idea here is to see at minimum do a quick reject by rotating all points
michael@0 403 // to either side of the line formed by connecting the endpoints
michael@0 404 // if the opposite curves points are on the line or on the other side, the
michael@0 405 // curves at most intersect at the endpoints
michael@0 406 for (int oddMan = 0; oddMan < 4; ++oddMan) {
michael@0 407 const SkDPoint* endPt[3];
michael@0 408 for (int opp = 1; opp < 4; ++opp) {
michael@0 409 int end = oddMan ^ opp; // choose a value not equal to oddMan
michael@0 410 endPt[opp - 1] = &c1[end];
michael@0 411 }
michael@0 412 for (int triTest = 0; triTest < 3; ++triTest) {
michael@0 413 double origX = endPt[triTest]->fX;
michael@0 414 double origY = endPt[triTest]->fY;
michael@0 415 int oppTest = triTest + 1;
michael@0 416 if (3 == oppTest) {
michael@0 417 oppTest = 0;
michael@0 418 }
michael@0 419 double adj = endPt[oppTest]->fX - origX;
michael@0 420 double opp = endPt[oppTest]->fY - origY;
michael@0 421 double sign = (c1[oddMan].fY - origY) * adj - (c1[oddMan].fX - origX) * opp;
michael@0 422 if (approximately_zero(sign)) {
michael@0 423 goto tryNextHalfPlane;
michael@0 424 }
michael@0 425 for (int n = 0; n < 4; ++n) {
michael@0 426 double test = (c2[n].fY - origY) * adj - (c2[n].fX - origX) * opp;
michael@0 427 if (test * sign > 0 && !precisely_zero(test)) {
michael@0 428 goto tryNextHalfPlane;
michael@0 429 }
michael@0 430 }
michael@0 431 }
michael@0 432 return true;
michael@0 433 tryNextHalfPlane:
michael@0 434 ;
michael@0 435 }
michael@0 436 return false;
michael@0 437 }
michael@0 438
michael@0 439 int SkIntersections::intersect(const SkDCubic& c1, const SkDCubic& c2) {
michael@0 440 if (fMax == 0) {
michael@0 441 fMax = 9;
michael@0 442 }
michael@0 443 bool selfIntersect = &c1 == &c2;
michael@0 444 if (selfIntersect) {
michael@0 445 if (c1[0].approximatelyEqual(c1[3])) {
michael@0 446 insert(0, 1, c1[0]);
michael@0 447 return fUsed;
michael@0 448 }
michael@0 449 } else {
michael@0 450 // OPTIMIZATION: set exact end bits here to avoid cubic exact end later
michael@0 451 for (int i1 = 0; i1 < 4; i1 += 3) {
michael@0 452 for (int i2 = 0; i2 < 4; i2 += 3) {
michael@0 453 if (c1[i1].approximatelyEqual(c2[i2])) {
michael@0 454 insert(i1 >> 1, i2 >> 1, c1[i1]);
michael@0 455 }
michael@0 456 }
michael@0 457 }
michael@0 458 }
michael@0 459 SkASSERT(fUsed < 4);
michael@0 460 if (!selfIntersect) {
michael@0 461 if (only_end_pts_in_common(c1, c2)) {
michael@0 462 return fUsed;
michael@0 463 }
michael@0 464 if (only_end_pts_in_common(c2, c1)) {
michael@0 465 return fUsed;
michael@0 466 }
michael@0 467 }
michael@0 468 // quad/quad does linear test here -- cubic does not
michael@0 469 // cubics which are really lines should have been detected in reduce step earlier
michael@0 470 int exactEndBits = 0;
michael@0 471 if (selfIntersect) {
michael@0 472 if (fUsed) {
michael@0 473 return fUsed;
michael@0 474 }
michael@0 475 } else {
michael@0 476 exactEndBits |= cubicExactEnd(c1, false, c2) << 0;
michael@0 477 exactEndBits |= cubicExactEnd(c1, true, c2) << 1;
michael@0 478 swap();
michael@0 479 exactEndBits |= cubicExactEnd(c2, false, c1) << 2;
michael@0 480 exactEndBits |= cubicExactEnd(c2, true, c1) << 3;
michael@0 481 swap();
michael@0 482 }
michael@0 483 if (cubicCheckCoincidence(c1, c2)) {
michael@0 484 SkASSERT(!selfIntersect);
michael@0 485 return fUsed;
michael@0 486 }
michael@0 487 // FIXME: pass in cached bounds from caller
michael@0 488 SkDRect c2Bounds;
michael@0 489 c2Bounds.setBounds(c2);
michael@0 490 if (!(exactEndBits & 4)) {
michael@0 491 cubicNearEnd(c1, false, c2, c2Bounds);
michael@0 492 }
michael@0 493 if (!(exactEndBits & 8)) {
michael@0 494 cubicNearEnd(c1, true, c2, c2Bounds);
michael@0 495 }
michael@0 496 if (!selfIntersect) {
michael@0 497 SkDRect c1Bounds;
michael@0 498 c1Bounds.setBounds(c1); // OPTIMIZE use setRawBounds ?
michael@0 499 swap();
michael@0 500 if (!(exactEndBits & 1)) {
michael@0 501 cubicNearEnd(c2, false, c1, c1Bounds);
michael@0 502 }
michael@0 503 if (!(exactEndBits & 2)) {
michael@0 504 cubicNearEnd(c2, true, c1, c1Bounds);
michael@0 505 }
michael@0 506 swap();
michael@0 507 }
michael@0 508 if (cubicCheckCoincidence(c1, c2)) {
michael@0 509 SkASSERT(!selfIntersect);
michael@0 510 return fUsed;
michael@0 511 }
michael@0 512 SkIntersections i;
michael@0 513 i.fAllowNear = false;
michael@0 514 i.fMax = 9;
michael@0 515 ::intersect(c1, 0, 1, c2, 0, 1, 1, i);
michael@0 516 int compCount = i.used();
michael@0 517 if (compCount) {
michael@0 518 int exactCount = used();
michael@0 519 if (exactCount == 0) {
michael@0 520 set(i);
michael@0 521 } else {
michael@0 522 // at least one is exact or near, and at least one was computed. Eliminate duplicates
michael@0 523 for (int exIdx = 0; exIdx < exactCount; ++exIdx) {
michael@0 524 for (int cpIdx = 0; cpIdx < compCount; ) {
michael@0 525 if (fT[0][0] == i[0][0] && fT[1][0] == i[1][0]) {
michael@0 526 i.removeOne(cpIdx);
michael@0 527 --compCount;
michael@0 528 continue;
michael@0 529 }
michael@0 530 double tAvg = (fT[0][exIdx] + i[0][cpIdx]) / 2;
michael@0 531 SkDPoint pt = c1.ptAtT(tAvg);
michael@0 532 if (!pt.approximatelyEqual(fPt[exIdx])) {
michael@0 533 ++cpIdx;
michael@0 534 continue;
michael@0 535 }
michael@0 536 tAvg = (fT[1][exIdx] + i[1][cpIdx]) / 2;
michael@0 537 pt = c2.ptAtT(tAvg);
michael@0 538 if (!pt.approximatelyEqual(fPt[exIdx])) {
michael@0 539 ++cpIdx;
michael@0 540 continue;
michael@0 541 }
michael@0 542 i.removeOne(cpIdx);
michael@0 543 --compCount;
michael@0 544 }
michael@0 545 }
michael@0 546 // if mid t evaluates to nearly the same point, skip the t
michael@0 547 for (int cpIdx = 0; cpIdx < compCount - 1; ) {
michael@0 548 double tAvg = (fT[0][cpIdx] + i[0][cpIdx + 1]) / 2;
michael@0 549 SkDPoint pt = c1.ptAtT(tAvg);
michael@0 550 if (!pt.approximatelyEqual(fPt[cpIdx])) {
michael@0 551 ++cpIdx;
michael@0 552 continue;
michael@0 553 }
michael@0 554 tAvg = (fT[1][cpIdx] + i[1][cpIdx + 1]) / 2;
michael@0 555 pt = c2.ptAtT(tAvg);
michael@0 556 if (!pt.approximatelyEqual(fPt[cpIdx])) {
michael@0 557 ++cpIdx;
michael@0 558 continue;
michael@0 559 }
michael@0 560 i.removeOne(cpIdx);
michael@0 561 --compCount;
michael@0 562 }
michael@0 563 // in addition to adding below missing function, think about how to say
michael@0 564 append(i);
michael@0 565 }
michael@0 566 }
michael@0 567 // If an end point and a second point very close to the end is returned, the second
michael@0 568 // point may have been detected because the approximate quads
michael@0 569 // intersected at the end and close to it. Verify that the second point is valid.
michael@0 570 if (fUsed <= 1) {
michael@0 571 return fUsed;
michael@0 572 }
michael@0 573 SkDPoint pt[2];
michael@0 574 if (closeStart(c1, 0, *this, pt[0]) && closeStart(c2, 1, *this, pt[1])
michael@0 575 && pt[0].approximatelyEqual(pt[1])) {
michael@0 576 removeOne(1);
michael@0 577 }
michael@0 578 if (closeEnd(c1, 0, *this, pt[0]) && closeEnd(c2, 1, *this, pt[1])
michael@0 579 && pt[0].approximatelyEqual(pt[1])) {
michael@0 580 removeOne(used() - 2);
michael@0 581 }
michael@0 582 // vet the pairs of t values to see if the mid value is also on the curve. If so, mark
michael@0 583 // the span as coincident
michael@0 584 if (fUsed >= 2 && !coincidentUsed()) {
michael@0 585 int last = fUsed - 1;
michael@0 586 int match = 0;
michael@0 587 for (int index = 0; index < last; ++index) {
michael@0 588 double mid1 = (fT[0][index] + fT[0][index + 1]) / 2;
michael@0 589 double mid2 = (fT[1][index] + fT[1][index + 1]) / 2;
michael@0 590 pt[0] = c1.ptAtT(mid1);
michael@0 591 pt[1] = c2.ptAtT(mid2);
michael@0 592 if (pt[0].approximatelyEqual(pt[1])) {
michael@0 593 match |= 1 << index;
michael@0 594 }
michael@0 595 }
michael@0 596 if (match) {
michael@0 597 #if DEBUG_CONCIDENT
michael@0 598 if (((match + 1) & match) != 0) {
michael@0 599 SkDebugf("%s coincident hole\n", __FUNCTION__);
michael@0 600 }
michael@0 601 #endif
michael@0 602 // for now, assume that everything from start to finish is coincident
michael@0 603 if (fUsed > 2) {
michael@0 604 fPt[1] = fPt[last];
michael@0 605 fT[0][1] = fT[0][last];
michael@0 606 fT[1][1] = fT[1][last];
michael@0 607 fIsCoincident[0] = 0x03;
michael@0 608 fIsCoincident[1] = 0x03;
michael@0 609 fUsed = 2;
michael@0 610 }
michael@0 611 }
michael@0 612 }
michael@0 613 return fUsed;
michael@0 614 }
michael@0 615
michael@0 616 // Up promote the quad to a cubic.
michael@0 617 // OPTIMIZATION If this is a common use case, optimize by duplicating
michael@0 618 // the intersect 3 loop to avoid the promotion / demotion code
michael@0 619 int SkIntersections::intersect(const SkDCubic& cubic, const SkDQuad& quad) {
michael@0 620 fMax = 6;
michael@0 621 SkDCubic up = quad.toCubic();
michael@0 622 (void) intersect(cubic, up);
michael@0 623 return used();
michael@0 624 }
michael@0 625
michael@0 626 /* http://www.ag.jku.at/compass/compasssample.pdf
michael@0 627 ( Self-Intersection Problems and Approximate Implicitization by Jan B. Thomassen
michael@0 628 Centre of Mathematics for Applications, University of Oslo http://www.cma.uio.no janbth@math.uio.no
michael@0 629 SINTEF Applied Mathematics http://www.sintef.no )
michael@0 630 describes a method to find the self intersection of a cubic by taking the gradient of the implicit
michael@0 631 form dotted with the normal, and solving for the roots. My math foo is too poor to implement this.*/
michael@0 632
michael@0 633 int SkIntersections::intersect(const SkDCubic& c) {
michael@0 634 fMax = 1;
michael@0 635 // check to see if x or y end points are the extrema. Are other quick rejects possible?
michael@0 636 if (c.endsAreExtremaInXOrY()) {
michael@0 637 return false;
michael@0 638 }
michael@0 639 (void) intersect(c, c);
michael@0 640 if (used() > 0) {
michael@0 641 SkASSERT(used() == 1);
michael@0 642 if (fT[0][0] > fT[1][0]) {
michael@0 643 swapPts();
michael@0 644 }
michael@0 645 }
michael@0 646 return used();
michael@0 647 }

mercurial