static void lookNearEnd(const SkDQuad& q1, const SkDQuad& q2, int testT, const SkIntersections& orig, bool swap, SkIntersections* i) { if (orig.used() == 1 && orig[!swap][0] == testT) { return; } if (orig.used() == 2 && orig[!swap][1] == testT) { return; } SkDLine tmpLine; int testTIndex = testT << 1; tmpLine[0] = tmpLine[1] = q2[testTIndex]; tmpLine[1].fX += q2[1].fY - q2[testTIndex].fY; tmpLine[1].fY -= q2[1].fX - q2[testTIndex].fX; SkIntersections impTs; impTs.intersectRay(q1, tmpLine); for (int index = 0; index < impTs.used(); ++index) { SkDPoint realPt = impTs.pt(index); if (!tmpLine[0].approximatelyEqualHalf(realPt)) { continue; } if (swap) { i->insert(testT, impTs[0][index], tmpLine[0]); } else { i->insert(impTs[0][index], testT, tmpLine[0]); } } }
static void oneOff(skiatest::Reporter* reporter, const SkDConic& c1, const SkDConic& c2, bool coin) { #if DEBUG_VISUALIZE_CONICS writeFrames(); #endif chopBothWays(c1, 0.5, "c1"); chopBothWays(c2, 0.5, "c2"); #if DEBUG_VISUALIZE_CONICS writeDPng(c1, "d1"); writeDPng(c2, "d2"); #endif SkASSERT(ValidConic(c1)); SkASSERT(ValidConic(c2)); SkIntersections intersections; intersections.intersect(c1, c2); if (coin && intersections.used() != 2) { SkDebugf(""); } REPORTER_ASSERT(reporter, !coin || intersections.used() == 2); double tt1, tt2; SkDPoint xy1, xy2; for (int pt3 = 0; pt3 < intersections.used(); ++pt3) { tt1 = intersections[0][pt3]; xy1 = c1.ptAtT(tt1); tt2 = intersections[1][pt3]; xy2 = c2.ptAtT(tt2); const SkDPoint& iPt = intersections.pt(pt3); REPORTER_ASSERT(reporter, xy1.approximatelyEqual(iPt)); REPORTER_ASSERT(reporter, xy2.approximatelyEqual(iPt)); REPORTER_ASSERT(reporter, xy1.approximatelyEqual(xy2)); } reporter->bumpTestCount(); }
static double testArc(skiatest::Reporter* reporter, const SkDQuad& quad, const SkDQuad& arcRef, int octant) { SkDQuad arc = arcRef; SkDVector offset = {quad[0].fX, quad[0].fY}; arc[0] += offset; arc[1] += offset; arc[2] += offset; SkIntersections i; i.intersect(arc, quad); if (i.used() == 0) { return -1; } int smallest = -1; double t = 2; for (int idx = 0; idx < i.used(); ++idx) { if (i[0][idx] > 1 || i[0][idx] < 0) { i.reset(); i.intersect(arc, quad); } if (i[1][idx] > 1 || i[1][idx] < 0) { i.reset(); i.intersect(arc, quad); } if (t > i[1][idx]) { smallest = idx; t = i[1][idx]; } } REPORTER_ASSERT(reporter, smallest >= 0); REPORTER_ASSERT(reporter, t >= 0 && t <= 1); return i[1][smallest]; }
SkDPoint SkDQuad::subDivide(const SkDPoint& a, const SkDPoint& c, double t1, double t2) const { SkASSERT(t1 != t2); SkDPoint b; SkDQuad sub = subDivide(t1, t2); SkDLine b0 = {{a, sub[1] + (a - sub[0])}}; SkDLine b1 = {{c, sub[1] + (c - sub[2])}}; SkIntersections i; i.intersectRay(b0, b1); if (i.used() == 1 && i[0][0] >= 0 && i[1][0] >= 0) { b = i.pt(0); } else { SkASSERT(i.used() <= 2); b = SkDPoint::Mid(b0[1], b1[1]); } if (t1 == 0 || t2 == 0) { align(0, &b); } if (t1 == 1 || t2 == 1) { align(2, &b); } if (AlmostBequalUlps(b.fX, a.fX)) { b.fX = a.fX; } else if (AlmostBequalUlps(b.fX, c.fX)) { b.fX = c.fX; } if (AlmostBequalUlps(b.fY, a.fY)) { b.fY = a.fY; } else if (AlmostBequalUlps(b.fY, c.fY)) { b.fY = c.fY; } return b; }
DEF_TEST(PathOpsAngleFindQuadEpsilon, reporter) { if (gDisableAngleTests) { return; } SkRandom ran; int maxEpsilon = 0; double maxAngle = 0; for (int index = 0; index < 100000; ++index) { SkDLine line = {{{0, 0}, {ran.nextRangeF(0.0001f, 1000), ran.nextRangeF(0.0001f, 1000)}}}; float t = ran.nextRangeF(0.0001f, 1); SkDPoint dPt = line.ptAtT(t); float t2 = ran.nextRangeF(0.0001f, 1); SkDPoint qPt = line.ptAtT(t2); float t3 = ran.nextRangeF(0.0001f, 1); SkDPoint qPt2 = line.ptAtT(t3); qPt.fX += qPt2.fY; qPt.fY -= qPt2.fX; SkDQuad quad = {{line[0], dPt, qPt}}; // binary search for maximum movement of quad[1] towards test that still has 1 intersection double moveT = 0.5f; double deltaT = moveT / 2; SkDPoint last; do { last = quad[1]; quad[1].fX = dPt.fX - line[1].fY * moveT; quad[1].fY = dPt.fY + line[1].fX * moveT; SkIntersections i; i.intersect(quad, line); REPORTER_ASSERT(reporter, i.used() > 0); if (i.used() == 1) { moveT += deltaT; } else { moveT -= deltaT; } deltaT /= 2; } while (last.asSkPoint() != quad[1].asSkPoint()); float p1 = SkDoubleToScalar(line[1].fX * last.fY); float p2 = SkDoubleToScalar(line[1].fY * last.fX); int p1Bits = SkFloatAs2sCompliment(p1); int p2Bits = SkFloatAs2sCompliment(p2); int epsilon = SkTAbs(p1Bits - p2Bits); if (maxEpsilon < epsilon) { SkDebugf("line={{0, 0}, {%1.7g, %1.7g}} t=%1.7g/%1.7g/%1.7g moveT=%1.7g" " pt={%1.7g, %1.7g} epsilon=%d\n", line[1].fX, line[1].fY, t, t2, t3, moveT, last.fX, last.fY, epsilon); maxEpsilon = epsilon; } double a1 = atan2(line[1].fY, line[1].fX); double a2 = atan2(last.fY, last.fX); double angle = fabs(a1 - a2); if (maxAngle < angle) { SkDebugf("line={{0, 0}, {%1.7g, %1.7g}} t=%1.7g/%1.7g/%1.7g moveT=%1.7g" " pt={%1.7g, %1.7g} angle=%1.7g\n", line[1].fX, line[1].fY, t, t2, t3, moveT, last.fX, last.fY, angle); maxAngle = angle; } } }
static void oneOff(skiatest::Reporter* reporter, const SkDCubic& cubic1, const SkDCubic& cubic2, bool coin) { SkASSERT(ValidCubic(cubic1)); SkASSERT(ValidCubic(cubic2)); #if ONE_OFF_DEBUG SkDebugf("computed quadratics given\n"); SkDebugf(" {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", cubic1[0].fX, cubic1[0].fY, cubic1[1].fX, cubic1[1].fY, cubic1[2].fX, cubic1[2].fY, cubic1[3].fX, cubic1[3].fY); SkDebugf(" {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", cubic2[0].fX, cubic2[0].fY, cubic2[1].fX, cubic2[1].fY, cubic2[2].fX, cubic2[2].fY, cubic2[3].fX, cubic2[3].fY); #endif SkTArray<SkDQuad, true> quads1; CubicToQuads(cubic1, cubic1.calcPrecision(), quads1); #if ONE_OFF_DEBUG SkDebugf("computed quadratics set 1\n"); for (int index = 0; index < quads1.count(); ++index) { const SkDQuad& q = quads1[index]; SkDebugf(" {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", q[0].fX, q[0].fY, q[1].fX, q[1].fY, q[2].fX, q[2].fY); } #endif SkTArray<SkDQuad, true> quads2; CubicToQuads(cubic2, cubic2.calcPrecision(), quads2); #if ONE_OFF_DEBUG SkDebugf("computed quadratics set 2\n"); for (int index = 0; index < quads2.count(); ++index) { const SkDQuad& q = quads2[index]; SkDebugf(" {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", q[0].fX, q[0].fY, q[1].fX, q[1].fY, q[2].fX, q[2].fY); } #endif SkIntersections intersections; intersections.intersect(cubic1, cubic2); REPORTER_ASSERT(reporter, !coin || intersections.used() == 2); double tt1, tt2; SkDPoint xy1, xy2; for (int pt3 = 0; pt3 < intersections.used(); ++pt3) { tt1 = intersections[0][pt3]; xy1 = cubic1.ptAtT(tt1); tt2 = intersections[1][pt3]; xy2 = cubic2.ptAtT(tt2); const SkDPoint& iPt = intersections.pt(pt3); #if ONE_OFF_DEBUG SkDebugf("%s t1=%1.9g (%1.9g, %1.9g) (%1.9g, %1.9g) (%1.9g, %1.9g) t2=%1.9g\n", __FUNCTION__, tt1, xy1.fX, xy1.fY, iPt.fX, iPt.fY, xy2.fX, xy2.fY, tt2); #endif REPORTER_ASSERT(reporter, xy1.approximatelyEqual(iPt)); REPORTER_ASSERT(reporter, xy2.approximatelyEqual(iPt)); REPORTER_ASSERT(reporter, xy1.approximatelyEqual(xy2)); } reporter->bumpTestCount(); }
static void standardTestCases(skiatest::Reporter* reporter) { for (size_t index = firstCubicIntersectionTest; index < tests_count; ++index) { int iIndex = static_cast<int>(index); const CubicPts& cubic1 = tests[index][0]; const CubicPts& cubic2 = tests[index][1]; SkDCubic c1, c2; c1.debugSet(cubic1.fPts); c2.debugSet(cubic2.fPts); SkReduceOrder reduce1, reduce2; int order1 = reduce1.reduce(c1, SkReduceOrder::kNo_Quadratics); int order2 = reduce2.reduce(c2, SkReduceOrder::kNo_Quadratics); const bool showSkipped = false; if (order1 < 4) { if (showSkipped) { SkDebugf("%s [%d] cubic1 order=%d\n", __FUNCTION__, iIndex, order1); } continue; } if (order2 < 4) { if (showSkipped) { SkDebugf("%s [%d] cubic2 order=%d\n", __FUNCTION__, iIndex, order2); } continue; } SkIntersections tIntersections; tIntersections.intersect(c1, c2); if (!tIntersections.used()) { if (showSkipped) { SkDebugf("%s [%d] no intersection\n", __FUNCTION__, iIndex); } continue; } if (tIntersections.isCoincident(0)) { if (showSkipped) { SkDebugf("%s [%d] coincident\n", __FUNCTION__, iIndex); } continue; } for (int pt = 0; pt < tIntersections.used(); ++pt) { double tt1 = tIntersections[0][pt]; SkDPoint xy1 = c1.ptAtT(tt1); double tt2 = tIntersections[1][pt]; SkDPoint xy2 = c2.ptAtT(tt2); if (!xy1.approximatelyEqual(xy2)) { SkDebugf("%s [%d,%d] x!= t1=%g (%g,%g) t2=%g (%g,%g)\n", __FUNCTION__, (int)index, pt, tt1, xy1.fX, xy1.fY, tt2, xy2.fX, xy2.fY); } REPORTER_ASSERT(reporter, xy1.approximatelyEqual(xy2)); } reporter->bumpTestCount(); } }
static void check_results(skiatest::Reporter* reporter, const SkDLine& line1, const SkDLine& line2, const SkIntersections& ts) { for (int i = 0; i < ts.used(); ++i) { SkDPoint result1 = line1.ptAtT(ts[0][i]); SkDPoint result2 = line2.ptAtT(ts[1][i]); if (!result1.approximatelyEqual(result2)) { REPORTER_ASSERT(reporter, ts.used() != 1); result2 = line2.ptAtT(ts[1][i ^ 1]); REPORTER_ASSERT(reporter, result1.approximatelyEqual(result2)); REPORTER_ASSERT(reporter, result1.approximatelyEqual(ts.pt(i).asSkPoint())); } } }
static void oneOff(skiatest::Reporter* reporter, const CubicPts& cubic1, const CubicPts& cubic2, bool coin) { SkDCubic c1, c2; c1.debugSet(cubic1.fPts); c2.debugSet(cubic2.fPts); SkASSERT(ValidCubic(c1)); SkASSERT(ValidCubic(c2)); #if ONE_OFF_DEBUG SkDebugf("computed quadratics given\n"); SkDebugf(" {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", cubic1[0].fX, cubic1[0].fY, cubic1[1].fX, cubic1[1].fY, cubic1[2].fX, cubic1[2].fY, cubic1[3].fX, cubic1[3].fY); SkDebugf(" {{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", cubic2[0].fX, cubic2[0].fY, cubic2[1].fX, cubic2[1].fY, cubic2[2].fX, cubic2[2].fY, cubic2[3].fX, cubic2[3].fY); #endif SkIntersections intersections; intersections.intersect(c1, c2); #if DEBUG_T_SECT_DUMP == 3 SkDebugf("</div>\n\n"); SkDebugf("<script type=\"text/javascript\">\n\n"); SkDebugf("var testDivs = [\n"); for (int index = 1; index <= gDumpTSectNum; ++index) { SkDebugf("sect%d,\n", index); } #endif if (coin && intersections.used() < 2) { SkDebugf(""); } REPORTER_ASSERT(reporter, !coin || intersections.used() >= 2); double tt1, tt2; SkDPoint xy1, xy2; for (int pt3 = 0; pt3 < intersections.used(); ++pt3) { tt1 = intersections[0][pt3]; xy1 = c1.ptAtT(tt1); tt2 = intersections[1][pt3]; xy2 = c2.ptAtT(tt2); const SkDPoint& iPt = intersections.pt(pt3); #if ONE_OFF_DEBUG SkDebugf("%s t1=%1.9g (%1.9g, %1.9g) (%1.9g, %1.9g) (%1.9g, %1.9g) t2=%1.9g\n", __FUNCTION__, tt1, xy1.fX, xy1.fY, iPt.fX, iPt.fY, xy2.fX, xy2.fY, tt2); #endif REPORTER_ASSERT(reporter, xy1.approximatelyEqual(iPt)); REPORTER_ASSERT(reporter, xy2.approximatelyEqual(iPt)); REPORTER_ASSERT(reporter, xy1.approximatelyEqual(xy2)); } reporter->bumpTestCount(); }
static void selfOneOff(skiatest::Reporter* reporter, int index) { const SkDCubic& cubic = selfSet[index]; #if ONE_OFF_DEBUG int idx2; double max[3]; int ts = cubic.findMaxCurvature(max); for (idx2 = 0; idx2 < ts; ++idx2) { SkDebugf("%s max[%d]=%1.9g (%1.9g, %1.9g)\n", __FUNCTION__, idx2, max[idx2], cubic.ptAtT(max[idx2]).fX, cubic.ptAtT(max[idx2]).fY); } SkTArray<double, true> ts1; SkTArray<SkDQuad, true> quads1; cubic.toQuadraticTs(cubic.calcPrecision(), &ts1); for (idx2 = 0; idx2 < ts1.count(); ++idx2) { SkDebugf("%s t[%d]=%1.9g\n", __FUNCTION__, idx2, ts1[idx2]); } CubicToQuads(cubic, cubic.calcPrecision(), quads1); for (idx2 = 0; idx2 < quads1.count(); ++idx2) { const SkDQuad& q = quads1[idx2]; SkDebugf(" {{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}},\n", q[0].fX, q[0].fY, q[1].fX, q[1].fY, q[2].fX, q[2].fY); } SkDebugf("\n"); #endif SkIntersections i; int result = i.intersect(cubic); REPORTER_ASSERT(reporter, result == 1); REPORTER_ASSERT(reporter, i.used() == 1); REPORTER_ASSERT(reporter, !approximately_equal(i[0][0], i[1][0])); SkDPoint pt1 = cubic.ptAtT(i[0][0]); SkDPoint pt2 = cubic.ptAtT(i[1][0]); REPORTER_ASSERT(reporter, pt1.approximatelyEqual(pt2)); reporter->bumpTestCount(); }
static void selfOneOff(skiatest::Reporter* reporter, int index) { const CubicPts& cubic = selfSet[index]; SkPoint c[4]; for (int i = 0; i < 4; ++i) { c[i] = cubic.fPts[i].asSkPoint(); } SkScalar loopT; SkScalar d[3]; SkCubicType cubicType = SkClassifyCubic(c, d); if (SkDCubic::ComplexBreak(c, &loopT) && cubicType == SkCubicType::kLoop_SkCubicType) { SkIntersections i; SkPoint twoCubics[7]; SkChopCubicAt(c, twoCubics, loopT); SkDCubic chopped[2]; chopped[0].set(&twoCubics[0]); chopped[1].set(&twoCubics[3]); int result = i.intersect(chopped[0], chopped[1]); REPORTER_ASSERT(reporter, result == 2); REPORTER_ASSERT(reporter, i.used() == 2); for (int index = 0; index < result; ++index) { SkDPoint pt1 = chopped[0].ptAtT(i[0][index]); SkDPoint pt2 = chopped[1].ptAtT(i[1][index]); REPORTER_ASSERT(reporter, pt1.approximatelyEqual(pt2)); reporter->bumpTestCount(); } } }
// intersect the end of the cubic with the other. Try lines from the end to control and opposite // end to determine range of t on opposite cubic. bool SkIntersections::cubicExactEnd(const SkDCubic& cubic1, bool start, const SkDCubic& cubic2) { int t1Index = start ? 0 : 3; double testT = (double) !start; bool swap = swapped(); // quad/quad at this point checks to see if exact matches have already been found // cubic/cubic can't reject so easily since cubics can intersect same point more than once SkDLine tmpLine; tmpLine[0] = tmpLine[1] = cubic2[t1Index]; tmpLine[1].fX += cubic2[2 - start].fY - cubic2[t1Index].fY; tmpLine[1].fY -= cubic2[2 - start].fX - cubic2[t1Index].fX; SkIntersections impTs; impTs.allowNear(false); impTs.intersectRay(cubic1, tmpLine); for (int index = 0; index < impTs.used(); ++index) { SkDPoint realPt = impTs.pt(index); if (!tmpLine[0].approximatelyEqual(realPt)) { continue; } if (swap) { cubicInsert(testT, impTs[0][index], tmpLine[0], cubic2, cubic1); } else { cubicInsert(impTs[0][index], testT, tmpLine[0], cubic1, cubic2); } return true; } return false; }
static int doIntersect(SkIntersections& intersections, const SkDQuad& quad, const SkDLine& line, bool& flipped) { int result; flipped = false; if (line[0].fX == line[1].fX) { double top = line[0].fY; double bottom = line[1].fY; flipped = top > bottom; if (flipped) { using std::swap; swap(top, bottom); } result = intersections.vertical(quad, top, bottom, line[0].fX, flipped); } else if (line[0].fY == line[1].fY) { double left = line[0].fX; double right = line[1].fX; flipped = left > right; if (flipped) { using std::swap; swap(left, right); } result = intersections.horizontal(quad, left, right, line[0].fY, flipped); } else { intersections.intersect(quad, line); result = intersections.used(); } return result; }
static int doIntersect(SkIntersections& intersections, const SkDConic& conic, const SkDLine& line, bool& flipped) { int result; flipped = false; if (line[0].fX == line[1].fX) { double top = line[0].fY; double bottom = line[1].fY; flipped = top > bottom; if (flipped) { SkTSwap<double>(top, bottom); } result = intersections.vertical(conic, top, bottom, line[0].fX, flipped); } else if (line[0].fY == line[1].fY) { double left = line[0].fX; double right = line[1].fX; flipped = left > right; if (flipped) { SkTSwap<double>(left, right); } result = intersections.horizontal(conic, left, right, line[0].fY, flipped); } else { intersections.intersect(conic, line); result = intersections.used(); } return result; }
static bool closeEnd(const SkDCubic& cubic, int cubicIndex, SkIntersections& i, SkDPoint& pt) { int last = i.used() - 1; if (i[cubicIndex][last] != 1 || i[cubicIndex][last - 1] < 1 - CLOSE_ENOUGH) { return false; } pt = cubic.xyAtT((i[cubicIndex][last] + i[cubicIndex][last - 1]) / 2); return true; }
static void standardTestCases(skiatest::Reporter* reporter) { bool showSkipped = false; for (size_t index = 0; index < quadraticTests_count; ++index) { const SkDQuad& quad1 = quadraticTests[index][0]; SkASSERT(ValidQuad(quad1)); const SkDQuad& quad2 = quadraticTests[index][1]; SkASSERT(ValidQuad(quad2)); SkReduceOrder reduce1, reduce2; int order1 = reduce1.reduce(quad1); int order2 = reduce2.reduce(quad2); if (order1 < 3) { if (showSkipped) { SkDebugf("[%d] quad1 order=%d\n", static_cast<int>(index), order1); } } if (order2 < 3) { if (showSkipped) { SkDebugf("[%d] quad2 order=%d\n", static_cast<int>(index), order2); } } if (order1 == 3 && order2 == 3) { SkIntersections intersections; intersections.intersect(quad1, quad2); if (intersections.used() > 0) { for (int pt = 0; pt < intersections.used(); ++pt) { double tt1 = intersections[0][pt]; SkDPoint xy1 = quad1.ptAtT(tt1); double tt2 = intersections[1][pt]; SkDPoint xy2 = quad2.ptAtT(tt2); if (!xy1.approximatelyEqual(xy2)) { SkDebugf("%s [%d,%d] x!= t1=%g (%g,%g) t2=%g (%g,%g)\n", __FUNCTION__, static_cast<int>(index), pt, tt1, xy1.fX, xy1.fY, tt2, xy2.fX, xy2.fY); REPORTER_ASSERT(reporter, 0); } } } } } }
SkDPoint SkDQuad::subDivide(const SkDPoint& a, const SkDPoint& c, double t1, double t2) const { SkASSERT(t1 != t2); SkDPoint b; #if 0 // this approach assumes that the control point computed directly is accurate enough double dx = interp_quad_coords(&fPts[0].fX, (t1 + t2) / 2); double dy = interp_quad_coords(&fPts[0].fY, (t1 + t2) / 2); b.fX = 2 * dx - (a.fX + c.fX) / 2; b.fY = 2 * dy - (a.fY + c.fY) / 2; #else SkDQuad sub = subDivide(t1, t2); SkDLine b0 = {{a, sub[1] + (a - sub[0])}}; SkDLine b1 = {{c, sub[1] + (c - sub[2])}}; SkIntersections i; i.intersectRay(b0, b1); if (i.used() == 1 && i[0][0] >= 0 && i[1][0] >= 0) { b = i.pt(0); } else { SkASSERT(i.used() <= 2); b = SkDPoint::Mid(b0[1], b1[1]); } #endif if (t1 == 0 || t2 == 0) { align(0, &b); } if (t1 == 1 || t2 == 1) { align(2, &b); } if (AlmostBequalUlps(b.fX, a.fX)) { b.fX = a.fX; } else if (AlmostBequalUlps(b.fX, c.fX)) { b.fX = c.fX; } if (AlmostBequalUlps(b.fY, a.fY)) { b.fY = a.fY; } else if (AlmostBequalUlps(b.fY, c.fY)) { b.fY = c.fY; } return b; }
static void debugShowCubicIntersection(int pts, const SkIntersectionHelper& wt, const SkIntersections& i) { SkASSERT(i.used() == pts); if (!pts) { SkDebugf("%s no self intersect " CUBIC_DEBUG_STR "\n", __FUNCTION__, CUBIC_DEBUG_DATA(wt.pts())); return; } SkDebugf("%s " T_DEBUG_STR(wtTs, 0) " " CUBIC_DEBUG_STR " " PT_DEBUG_STR, __FUNCTION__, i[0][0], CUBIC_DEBUG_DATA(wt.pts()), PT_DEBUG_DATA(i, 0)); SkDebugf(" " T_DEBUG_STR(wtTs, 1), i[1][0]); SkDebugf("\n"); }
DEF_TEST(PathOpsConicLineIntersection, reporter) { for (size_t index = 0; index < lineConicTests_count; ++index) { int iIndex = static_cast<int>(index); const SkDConic& conic = lineConicTests[index].conic; SkASSERT(ValidConic(conic)); const SkDLine& line = lineConicTests[index].line; SkASSERT(ValidLine(line)); SkReduceOrder reducer; SkPoint pts[3] = { conic.fPts.fPts[0].asSkPoint(), conic.fPts.fPts[1].asSkPoint(), conic.fPts.fPts[2].asSkPoint() }; SkPoint reduced[3]; SkPath::Verb order1 = SkReduceOrder::Conic(pts, conic.fWeight, reduced); if (order1 != SkPath::kConic_Verb) { SkDebugf("%s [%d] conic verb=%d\n", __FUNCTION__, iIndex, order1); REPORTER_ASSERT(reporter, 0); } int order2 = reducer.reduce(line); if (order2 < 2) { SkDebugf("%s [%d] line order=%d\n", __FUNCTION__, iIndex, order2); REPORTER_ASSERT(reporter, 0); } SkIntersections intersections; bool flipped = false; int result = doIntersect(intersections, conic, line, flipped); REPORTER_ASSERT(reporter, result == lineConicTests[index].result); if (intersections.used() <= 0) { continue; } for (int pt = 0; pt < result; ++pt) { double tt1 = intersections[0][pt]; REPORTER_ASSERT(reporter, tt1 >= 0 && tt1 <= 1); SkDPoint t1 = conic.ptAtT(tt1); double tt2 = intersections[1][pt]; REPORTER_ASSERT(reporter, tt2 >= 0 && tt2 <= 1); SkDPoint t2 = line.ptAtT(tt2); if (!t1.approximatelyEqual(t2)) { SkDebugf("%s [%d,%d] x!= t1=%1.9g (%1.9g,%1.9g) t2=%1.9g (%1.9g,%1.9g)\n", __FUNCTION__, iIndex, pt, tt1, t1.fX, t1.fY, tt2, t2.fX, t2.fY); REPORTER_ASSERT(reporter, 0); } if (!t1.approximatelyEqual(lineConicTests[index].expected[0]) && (lineConicTests[index].result == 1 || !t1.approximatelyEqual(lineConicTests[index].expected[1]))) { SkDebugf("%s t1=(%1.9g,%1.9g)\n", __FUNCTION__, t1.fX, t1.fY); REPORTER_ASSERT(reporter, 0); } } } }
DEF_TEST(PathOpsQuadLineIntersection, reporter) { for (size_t index = 0; index < lineQuadTests_count; ++index) { int iIndex = static_cast<int>(index); const QuadPts& q = lineQuadTests[index].quad; SkDQuad quad; quad.debugSet(q.fPts); SkASSERT(ValidQuad(quad)); const SkDLine& line = lineQuadTests[index].line; SkASSERT(ValidLine(line)); SkReduceOrder reducer1, reducer2; int order1 = reducer1.reduce(quad); int order2 = reducer2.reduce(line); if (order1 < 3) { SkDebugf("%s [%d] quad order=%d\n", __FUNCTION__, iIndex, order1); REPORTER_ASSERT(reporter, 0); } if (order2 < 2) { SkDebugf("%s [%d] line order=%d\n", __FUNCTION__, iIndex, order2); REPORTER_ASSERT(reporter, 0); } SkIntersections intersections; bool flipped = false; int result = doIntersect(intersections, quad, line, flipped); REPORTER_ASSERT(reporter, result == lineQuadTests[index].result); if (intersections.used() <= 0) { continue; } for (int pt = 0; pt < result; ++pt) { double tt1 = intersections[0][pt]; REPORTER_ASSERT(reporter, tt1 >= 0 && tt1 <= 1); SkDPoint t1 = quad.ptAtT(tt1); double tt2 = intersections[1][pt]; REPORTER_ASSERT(reporter, tt2 >= 0 && tt2 <= 1); SkDPoint t2 = line.ptAtT(tt2); if (!t1.approximatelyEqual(t2)) { SkDebugf("%s [%d,%d] x!= t1=%1.9g (%1.9g,%1.9g) t2=%1.9g (%1.9g,%1.9g)\n", __FUNCTION__, iIndex, pt, tt1, t1.fX, t1.fY, tt2, t2.fX, t2.fY); REPORTER_ASSERT(reporter, 0); } if (!t1.approximatelyEqual(lineQuadTests[index].expected[0]) && (lineQuadTests[index].result == 1 || !t1.approximatelyEqual(lineQuadTests[index].expected[1]))) { SkDebugf("%s t1=(%1.9g,%1.9g)\n", __FUNCTION__, t1.fX, t1.fY); REPORTER_ASSERT(reporter, 0); } } } }
static void testOneCoincident(skiatest::Reporter* reporter, const SkDLine& line1, const SkDLine& line2) { SkASSERT(ValidLine(line1)); SkASSERT(ValidLine(line2)); SkIntersections ts2; int pts2 = ts2.intersect(line1, line2); REPORTER_ASSERT(reporter, pts2 == 2); REPORTER_ASSERT(reporter, pts2 == ts2.used()); check_results(reporter, line1, line2, ts2); #if 0 SkIntersections ts; int pts = ts.intersect(line1, line2); REPORTER_ASSERT(reporter, pts == pts2); REPORTER_ASSERT(reporter, pts == 2); REPORTER_ASSERT(reporter, pts == ts.used()); check_results(reporter, line1, line2, ts); #endif }
static void debugShowLineIntersection(int pts, const SkIntersectionHelper& wt, const SkIntersectionHelper& wn, const SkIntersections& i) { SkASSERT(i.used() == pts); if (!pts) { SkDebugf("%s no intersect " LINE_DEBUG_STR " " LINE_DEBUG_STR "\n", __FUNCTION__, LINE_DEBUG_DATA(wt.pts()), LINE_DEBUG_DATA(wn.pts())); return; } SkDebugf("%s " T_DEBUG_STR(wtTs, 0) " " LINE_DEBUG_STR " " PT_DEBUG_STR, __FUNCTION__, i[0][0], LINE_DEBUG_DATA(wt.pts()), PT_DEBUG_DATA(i, 0)); if (pts == 2) { SkDebugf(" " T_DEBUG_STR(wtTs, 1) " " PT_DEBUG_STR, i[0][1], PT_DEBUG_DATA(i, 1)); } SkDebugf(" wnTs[0]=%g " LINE_DEBUG_STR, i[1][0], LINE_DEBUG_DATA(wn.pts())); if (pts == 2) { SkDebugf(" " T_DEBUG_STR(wnTs, 1), i[1][1]); } SkDebugf("\n"); }
static void debugShowCubicQuadIntersection(int pts, const SkIntersectionHelper& wt, const SkIntersectionHelper& wn, const SkIntersections& i) { SkASSERT(i.used() == pts); if (!pts) { SkDebugf("%s no intersect " CUBIC_DEBUG_STR " " QUAD_DEBUG_STR "\n", __FUNCTION__, CUBIC_DEBUG_DATA(wt.pts()), QUAD_DEBUG_DATA(wn.pts())); return; } SkDebugf("%s " T_DEBUG_STR(wtTs, 0) " " CUBIC_DEBUG_STR " " PT_DEBUG_STR, __FUNCTION__, i[0][0], CUBIC_DEBUG_DATA(wt.pts()), PT_DEBUG_DATA(i, 0)); for (int n = 1; n < pts; ++n) { SkDebugf(" " TX_DEBUG_STR(wtTs) " " PT_DEBUG_STR, n, i[0][n], PT_DEBUG_DATA(i, n)); } SkDebugf(" wnTs[0]=%g " QUAD_DEBUG_STR, i[1][0], QUAD_DEBUG_DATA(wn.pts())); for (int n = 1; n < pts; ++n) { SkDebugf(" " TX_DEBUG_STR(wnTs), n, i[1][n]); } SkDebugf("\n"); }
static void testOne(skiatest::Reporter* reporter, const SkDLine& line1, const SkDLine& line2) { SkASSERT(ValidLine(line1)); SkASSERT(ValidLine(line2)); SkIntersections i; int pts = i.intersect(line1, line2); REPORTER_ASSERT(reporter, pts); REPORTER_ASSERT(reporter, pts == i.used()); check_results(reporter, line1, line2, i); if (line1[0] == line1[1] || line2[0] == line2[1]) { return; } if (line1[0].fY == line1[1].fY) { double left = SkTMin(line1[0].fX, line1[1].fX); double right = SkTMax(line1[0].fX, line1[1].fX); SkIntersections ts; ts.horizontal(line2, left, right, line1[0].fY, line1[0].fX != left); check_results(reporter, line2, line1, ts); } if (line2[0].fY == line2[1].fY) { double left = SkTMin(line2[0].fX, line2[1].fX); double right = SkTMax(line2[0].fX, line2[1].fX); SkIntersections ts; ts.horizontal(line1, left, right, line2[0].fY, line2[0].fX != left); check_results(reporter, line1, line2, ts); } if (line1[0].fX == line1[1].fX) { double top = SkTMin(line1[0].fY, line1[1].fY); double bottom = SkTMax(line1[0].fY, line1[1].fY); SkIntersections ts; ts.vertical(line2, top, bottom, line1[0].fX, line1[0].fY != top); check_results(reporter, line2, line1, ts); } if (line2[0].fX == line2[1].fX) { double top = SkTMin(line2[0].fY, line2[1].fY); double bottom = SkTMax(line2[0].fY, line2[1].fY); SkIntersections ts; ts.vertical(line1, top, bottom, line2[0].fX, line2[0].fY != top); check_results(reporter, line1, line2, ts); } }
static void PathOpsLineIntersectionTest(skiatest::Reporter* reporter) { size_t index; for (index = 0; index < coincidentTests_count; ++index) { const SkDLine& line1 = coincidentTests[index][0]; const SkDLine& line2 = coincidentTests[index][1]; testOneCoincident(reporter, line1, line2); reporter->bumpTestCount(); } for (index = 0; index < tests_count; ++index) { const SkDLine& line1 = tests[index][0]; const SkDLine& line2 = tests[index][1]; testOne(reporter, line1, line2); reporter->bumpTestCount(); } for (index = 0; index < noIntersect_count; ++index) { const SkDLine& line1 = noIntersect[index][0]; const SkDLine& line2 = noIntersect[index][1]; SkIntersections ts; int pts = ts.intersect(line1, line2); REPORTER_ASSERT(reporter, !pts); REPORTER_ASSERT(reporter, pts == ts.used()); reporter->bumpTestCount(); } }
// determine that slop required after quad/quad finds a candidate intersection // use the cross of the tangents plus the distance from 1 or 0 as knobs DEF_TEST(PathOpsCubicQuadSlop, reporter) { // create a random non-selfintersecting cubic // break it into quadratics // offset the quadratic, measuring the slop required to find the intersection if (!gPathOpCubicQuadSlopVerbose) { // takes a while to run -- so exclude it by default return; } int results[101]; sk_bzero(results, sizeof(results)); double minCross[101]; sk_bzero(minCross, sizeof(minCross)); double maxCross[101]; sk_bzero(maxCross, sizeof(maxCross)); double sumCross[101]; sk_bzero(sumCross, sizeof(sumCross)); int foundOne = 0; int slopCount = 1; SkRandom ran; for (int index = 0; index < 10000000; ++index) { if (index % 1000 == 999) SkDebugf("."); SkDCubic cubic = {{ {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)}, {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)}, {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)}, {ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000)} }}; SkIntersections i; if (i.intersect(cubic)) { continue; } SkSTArray<kCubicToQuadSubdivisionDepth, double, true> ts; cubic.toQuadraticTs(cubic.calcPrecision(), &ts); double tStart = 0; int tsCount = ts.count(); for (int i1 = 0; i1 <= tsCount; ++i1) { const double tEnd = i1 < tsCount ? ts[i1] : 1; SkDCubic part = cubic.subDivide(tStart, tEnd); SkDQuad quad = part.toQuad(); SkReduceOrder reducer; int order = reducer.reduce(quad); if (order != 3) { continue; } for (int i2 = 0; i2 < 100; ++i2) { SkDPoint endDisplacement = {ran.nextRangeF(-100, 100), ran.nextRangeF(-100, 100)}; SkDQuad nearby = {{ {quad[0].fX + endDisplacement.fX, quad[0].fY + endDisplacement.fY}, {quad[1].fX + ran.nextRangeF(-100, 100), quad[1].fY + ran.nextRangeF(-100, 100)}, {quad[2].fX - endDisplacement.fX, quad[2].fY - endDisplacement.fY} }}; order = reducer.reduce(nearby); if (order != 3) { continue; } SkIntersections locals; locals.allowNear(false); locals.intersect(quad, nearby); if (locals.used() != 1) { continue; } // brute force find actual intersection SkDLine cubicLine = {{ {0, 0}, {cubic[0].fX, cubic[0].fY } }}; SkIntersections liner; int i3; int found = -1; int foundErr = true; for (i3 = 1; i3 <= 1000; ++i3) { cubicLine[0] = cubicLine[1]; cubicLine[1] = cubic.ptAtT(i3 / 1000.); liner.reset(); liner.allowNear(false); liner.intersect(nearby, cubicLine); if (liner.used() == 0) { continue; } if (liner.used() > 1) { foundErr = true; break; } if (found > 0) { foundErr = true; break; } foundErr = false; found = i3; } if (foundErr) { continue; } SkDVector dist = liner.pt(0) - locals.pt(0); SkDVector qV = nearby.dxdyAtT(locals[0][0]); double cubicT = (found - 1 + liner[1][0]) / 1000.; SkDVector cV = cubic.dxdyAtT(cubicT); double qxc = qV.crossCheck(cV); double qvLen = qV.length(); double cvLen = cV.length(); double maxLen = SkTMax(qvLen, cvLen); qxc /= maxLen; double quadT = tStart + (tEnd - tStart) * locals[0][0]; double diffT = fabs(cubicT - quadT); int diffIdx = (int) (diffT * 100); results[diffIdx]++; double absQxc = fabs(qxc); if (sumCross[diffIdx] == 0) { minCross[diffIdx] = maxCross[diffIdx] = sumCross[diffIdx] = absQxc; } else { minCross[diffIdx] = SkTMin(minCross[diffIdx], absQxc); maxCross[diffIdx] = SkTMax(maxCross[diffIdx], absQxc); sumCross[diffIdx] += absQxc; } if (diffIdx >= 20) { #if 01 SkDebugf("cubic={{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}" " quad={{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}" " {{{%1.9g,%1.9g}, {%1.9g,%1.9g}}}" " qT=%1.9g cT=%1.9g dist=%1.9g cross=%1.9g\n", cubic[0].fX, cubic[0].fY, cubic[1].fX, cubic[1].fY, cubic[2].fX, cubic[2].fY, cubic[3].fX, cubic[3].fY, nearby[0].fX, nearby[0].fY, nearby[1].fX, nearby[1].fY, nearby[2].fX, nearby[2].fY, liner.pt(0).fX, liner.pt(0).fY, locals.pt(0).fX, locals.pt(0).fY, quadT, cubicT, dist.length(), qxc); #else SkDebugf("qT=%1.9g cT=%1.9g dist=%1.9g cross=%1.9g\n", quadT, cubicT, dist.length(), qxc); SkDebugf("<div id=\"slop%d\">\n", ++slopCount); SkDebugf("{{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}\n" "{{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}}}\n" "{{{%1.9g,%1.9g}, {%1.9g,%1.9g}}}\n", cubic[0].fX, cubic[0].fY, cubic[1].fX, cubic[1].fY, cubic[2].fX, cubic[2].fY, cubic[3].fX, cubic[3].fY, nearby[0].fX, nearby[0].fY, nearby[1].fX, nearby[1].fY, nearby[2].fX, nearby[2].fY, liner.pt(0).fX, liner.pt(0).fY, locals.pt(0).fX, locals.pt(0).fY); SkDebugf("</div>\n\n"); #endif } ++foundOne; } tStart = tEnd; } if (++foundOne >= 100000) { break; } } #if 01 SkDebugf("slopCount=%d\n", slopCount); int max = 100; while (results[max] == 0) { --max; } for (int i = 0; i <= max; ++i) { if (i > 0 && i % 10 == 0) { SkDebugf("\n"); } SkDebugf("%d ", results[i]); } SkDebugf("min\n"); for (int i = 0; i <= max; ++i) { if (i > 0 && i % 10 == 0) { SkDebugf("\n"); } SkDebugf("%1.9g ", minCross[i]); } SkDebugf("max\n"); for (int i = 0; i <= max; ++i) { if (i > 0 && i % 10 == 0) { SkDebugf("\n"); } SkDebugf("%1.9g ", maxCross[i]); } SkDebugf("avg\n"); for (int i = 0; i <= max; ++i) { if (i > 0 && i % 10 == 0) { SkDebugf("\n"); } SkDebugf("%1.9g ", sumCross[i] / results[i]); } #else for (int i = 1; i < slopCount; ++i) { SkDebugf(" slop%d,\n", i); } #endif SkDebugf("\n"); }
// this flavor centers potential intersections recursively. In contrast, '2' may inadvertently // chase intersections near quadratic ends, requiring odd hacks to find them. static void intersect(const SkDCubic& cubic1, double t1s, double t1e, const SkDCubic& cubic2, double t2s, double t2e, double precisionScale, SkIntersections& i) { i.upDepth(); SkDCubic c1 = cubic1.subDivide(t1s, t1e); SkDCubic c2 = cubic2.subDivide(t2s, t2e); SkSTArray<kCubicToQuadSubdivisionDepth, double, true> ts1; // OPTIMIZE: if c1 == c2, call once (happens when detecting self-intersection) c1.toQuadraticTs(c1.calcPrecision() * precisionScale, &ts1); SkSTArray<kCubicToQuadSubdivisionDepth, double, true> ts2; c2.toQuadraticTs(c2.calcPrecision() * precisionScale, &ts2); double t1Start = t1s; int ts1Count = ts1.count(); for (int i1 = 0; i1 <= ts1Count; ++i1) { const double tEnd1 = i1 < ts1Count ? ts1[i1] : 1; const double t1 = t1s + (t1e - t1s) * tEnd1; SkReduceOrder s1; int o1 = quadPart(cubic1, t1Start, t1, &s1); double t2Start = t2s; int ts2Count = ts2.count(); for (int i2 = 0; i2 <= ts2Count; ++i2) { const double tEnd2 = i2 < ts2Count ? ts2[i2] : 1; const double t2 = t2s + (t2e - t2s) * tEnd2; if (&cubic1 == &cubic2 && t1Start >= t2Start) { t2Start = t2; continue; } SkReduceOrder s2; int o2 = quadPart(cubic2, t2Start, t2, &s2); #if ONE_OFF_DEBUG char tab[] = " "; if (tLimits1[0][0] >= t1Start && tLimits1[0][1] <= t1 && tLimits1[1][0] >= t2Start && tLimits1[1][1] <= t2) { SkDebugf("%.*s %s t1=(%1.9g,%1.9g) t2=(%1.9g,%1.9g)", i.depth()*2, tab, __FUNCTION__, t1Start, t1, t2Start, t2); SkIntersections xlocals; xlocals.allowNear(false); intersectWithOrder(s1.fQuad, o1, s2.fQuad, o2, xlocals); SkDebugf(" xlocals.fUsed=%d\n", xlocals.used()); } #endif SkIntersections locals; locals.allowNear(false); intersectWithOrder(s1.fQuad, o1, s2.fQuad, o2, locals); int tCount = locals.used(); for (int tIdx = 0; tIdx < tCount; ++tIdx) { double to1 = t1Start + (t1 - t1Start) * locals[0][tIdx]; double to2 = t2Start + (t2 - t2Start) * locals[1][tIdx]; // if the computed t is not sufficiently precise, iterate SkDPoint p1 = cubic1.ptAtT(to1); SkDPoint p2 = cubic2.ptAtT(to2); if (p1.approximatelyEqual(p2)) { // FIXME: local edge may be coincident -- experiment with not propagating coincidence to caller // SkASSERT(!locals.isCoincident(tIdx)); if (&cubic1 != &cubic2 || !approximately_equal(to1, to2)) { if (i.swapped()) { // FIXME: insert should respect swap i.insert(to2, to1, p1); } else { i.insert(to1, to2, p1); } } } else { /*for random cubics, 16 below catches 99.997% of the intersections. To test for the remaining 0.003% look for nearly coincident curves. and check each 1/16th section. */ double offset = precisionScale / 16; // FIXME: const is arbitrary: test, refine double c1Bottom = tIdx == 0 ? 0 : (t1Start + (t1 - t1Start) * locals[0][tIdx - 1] + to1) / 2; double c1Min = SkTMax(c1Bottom, to1 - offset); double c1Top = tIdx == tCount - 1 ? 1 : (t1Start + (t1 - t1Start) * locals[0][tIdx + 1] + to1) / 2; double c1Max = SkTMin(c1Top, to1 + offset); double c2Min = SkTMax(0., to2 - offset); double c2Max = SkTMin(1., to2 + offset); #if ONE_OFF_DEBUG SkDebugf("%.*s %s 1 contains1=%d/%d contains2=%d/%d\n", i.depth()*2, tab, __FUNCTION__, c1Min <= tLimits1[0][1] && tLimits1[0][0] <= c1Max && c2Min <= tLimits1[1][1] && tLimits1[1][0] <= c2Max, to1 - offset <= tLimits1[0][1] && tLimits1[0][0] <= to1 + offset && to2 - offset <= tLimits1[1][1] && tLimits1[1][0] <= to2 + offset, c1Min <= tLimits2[0][1] && tLimits2[0][0] <= c1Max && c2Min <= tLimits2[1][1] && tLimits2[1][0] <= c2Max, to1 - offset <= tLimits2[0][1] && tLimits2[0][0] <= to1 + offset && to2 - offset <= tLimits2[1][1] && tLimits2[1][0] <= to2 + offset); SkDebugf("%.*s %s 1 c1Bottom=%1.9g c1Top=%1.9g c2Bottom=%1.9g c2Top=%1.9g" " 1-o=%1.9g 1+o=%1.9g 2-o=%1.9g 2+o=%1.9g offset=%1.9g\n", i.depth()*2, tab, __FUNCTION__, c1Bottom, c1Top, 0., 1., to1 - offset, to1 + offset, to2 - offset, to2 + offset, offset); SkDebugf("%.*s %s 1 to1=%1.9g to2=%1.9g c1Min=%1.9g c1Max=%1.9g c2Min=%1.9g" " c2Max=%1.9g\n", i.depth()*2, tab, __FUNCTION__, to1, to2, c1Min, c1Max, c2Min, c2Max); #endif intersect(cubic1, c1Min, c1Max, cubic2, c2Min, c2Max, offset, i); #if ONE_OFF_DEBUG SkDebugf("%.*s %s 1 i.used=%d t=%1.9g\n", i.depth()*2, tab, __FUNCTION__, i.used(), i.used() > 0 ? i[0][i.used() - 1] : -1); #endif if (tCount > 1) { c1Min = SkTMax(0., to1 - offset); c1Max = SkTMin(1., to1 + offset); double c2Bottom = tIdx == 0 ? to2 : (t2Start + (t2 - t2Start) * locals[1][tIdx - 1] + to2) / 2; double c2Top = tIdx == tCount - 1 ? to2 : (t2Start + (t2 - t2Start) * locals[1][tIdx + 1] + to2) / 2; if (c2Bottom > c2Top) { SkTSwap(c2Bottom, c2Top); } if (c2Bottom == to2) { c2Bottom = 0; } if (c2Top == to2) { c2Top = 1; } c2Min = SkTMax(c2Bottom, to2 - offset); c2Max = SkTMin(c2Top, to2 + offset); #if ONE_OFF_DEBUG SkDebugf("%.*s %s 2 contains1=%d/%d contains2=%d/%d\n", i.depth()*2, tab, __FUNCTION__, c1Min <= tLimits1[0][1] && tLimits1[0][0] <= c1Max && c2Min <= tLimits1[1][1] && tLimits1[1][0] <= c2Max, to1 - offset <= tLimits1[0][1] && tLimits1[0][0] <= to1 + offset && to2 - offset <= tLimits1[1][1] && tLimits1[1][0] <= to2 + offset, c1Min <= tLimits2[0][1] && tLimits2[0][0] <= c1Max && c2Min <= tLimits2[1][1] && tLimits2[1][0] <= c2Max, to1 - offset <= tLimits2[0][1] && tLimits2[0][0] <= to1 + offset && to2 - offset <= tLimits2[1][1] && tLimits2[1][0] <= to2 + offset); SkDebugf("%.*s %s 2 c1Bottom=%1.9g c1Top=%1.9g c2Bottom=%1.9g c2Top=%1.9g" " 1-o=%1.9g 1+o=%1.9g 2-o=%1.9g 2+o=%1.9g offset=%1.9g\n", i.depth()*2, tab, __FUNCTION__, 0., 1., c2Bottom, c2Top, to1 - offset, to1 + offset, to2 - offset, to2 + offset, offset); SkDebugf("%.*s %s 2 to1=%1.9g to2=%1.9g c1Min=%1.9g c1Max=%1.9g c2Min=%1.9g" " c2Max=%1.9g\n", i.depth()*2, tab, __FUNCTION__, to1, to2, c1Min, c1Max, c2Min, c2Max); #endif intersect(cubic1, c1Min, c1Max, cubic2, c2Min, c2Max, offset, i); #if ONE_OFF_DEBUG SkDebugf("%.*s %s 2 i.used=%d t=%1.9g\n", i.depth()*2, tab, __FUNCTION__, i.used(), i.used() > 0 ? i[0][i.used() - 1] : -1); #endif c1Min = SkTMax(c1Bottom, to1 - offset); c1Max = SkTMin(c1Top, to1 + offset); #if ONE_OFF_DEBUG SkDebugf("%.*s %s 3 contains1=%d/%d contains2=%d/%d\n", i.depth()*2, tab, __FUNCTION__, c1Min <= tLimits1[0][1] && tLimits1[0][0] <= c1Max && c2Min <= tLimits1[1][1] && tLimits1[1][0] <= c2Max, to1 - offset <= tLimits1[0][1] && tLimits1[0][0] <= to1 + offset && to2 - offset <= tLimits1[1][1] && tLimits1[1][0] <= to2 + offset, c1Min <= tLimits2[0][1] && tLimits2[0][0] <= c1Max && c2Min <= tLimits2[1][1] && tLimits2[1][0] <= c2Max, to1 - offset <= tLimits2[0][1] && tLimits2[0][0] <= to1 + offset && to2 - offset <= tLimits2[1][1] && tLimits2[1][0] <= to2 + offset); SkDebugf("%.*s %s 3 c1Bottom=%1.9g c1Top=%1.9g c2Bottom=%1.9g c2Top=%1.9g" " 1-o=%1.9g 1+o=%1.9g 2-o=%1.9g 2+o=%1.9g offset=%1.9g\n", i.depth()*2, tab, __FUNCTION__, 0., 1., c2Bottom, c2Top, to1 - offset, to1 + offset, to2 - offset, to2 + offset, offset); SkDebugf("%.*s %s 3 to1=%1.9g to2=%1.9g c1Min=%1.9g c1Max=%1.9g c2Min=%1.9g" " c2Max=%1.9g\n", i.depth()*2, tab, __FUNCTION__, to1, to2, c1Min, c1Max, c2Min, c2Max); #endif intersect(cubic1, c1Min, c1Max, cubic2, c2Min, c2Max, offset, i); #if ONE_OFF_DEBUG SkDebugf("%.*s %s 3 i.used=%d t=%1.9g\n", i.depth()*2, tab, __FUNCTION__, i.used(), i.used() > 0 ? i[0][i.used() - 1] : -1); #endif } // intersect(cubic1, c1Min, c1Max, cubic2, c2Min, c2Max, offset, i); // FIXME: if no intersection is found, either quadratics intersected where // cubics did not, or the intersection was missed. In the former case, expect // the quadratics to be nearly parallel at the point of intersection, and check // for that. } } t2Start = t2; } t1Start = t1; } i.downDepth(); }
// intersect the end of the cubic with the other. Try lines from the end to control and opposite // end to determine range of t on opposite cubic. static void intersectEnd(const SkDCubic& cubic1, bool start, const SkDCubic& cubic2, const SkDRect& bounds2, SkIntersections& i) { SkDLine line; int t1Index = start ? 0 : 3; line[0] = cubic1[t1Index]; // don't bother if the two cubics are connnected SkTDArray<double> tVals; // OPTIMIZE: replace with hard-sized array for (int index = 0; index < 4; ++index) { if (index == t1Index) { continue; } SkDVector dxy1 = cubic1[index] - line[0]; dxy1 /= SkDCubic::gPrecisionUnit; line[1] = line[0] + dxy1; SkDRect lineBounds; lineBounds.setBounds(line); if (!bounds2.intersects(&lineBounds)) { continue; } SkIntersections local; if (!local.intersect(cubic2, line)) { continue; } for (int idx2 = 0; idx2 < local.used(); ++idx2) { double foundT = local[0][idx2]; if (approximately_less_than_zero(foundT) || approximately_greater_than_one(foundT)) { continue; } if (local.pt(idx2).approximatelyEqual(line[0])) { if (i.swapped()) { // FIXME: insert should respect swap i.insert(foundT, start ? 0 : 1, line[0]); } else { i.insert(start ? 0 : 1, foundT, line[0]); } } else { *tVals.append() = local[0][idx2]; } } } if (tVals.count() == 0) { return; } QSort<double>(tVals.begin(), tVals.end() - 1); double tMin1 = start ? 0 : 1 - LINE_FRACTION; double tMax1 = start ? LINE_FRACTION : 1; int tIdx = 0; do { int tLast = tIdx; while (tLast + 1 < tVals.count() && roughly_equal(tVals[tLast + 1], tVals[tIdx])) { ++tLast; } double tMin2 = SkTMax<double>(tVals[tIdx] - LINE_FRACTION, 0.0); double tMax2 = SkTMin<double>(tVals[tLast] + LINE_FRACTION, 1.0); int lastUsed = i.used(); intersect(cubic1, tMin1, tMax1, cubic2, tMin2, tMax2, 1, i); if (lastUsed == i.used()) { tMin2 = SkTMax<double>(tVals[tIdx] - (1.0 / SkDCubic::gPrecisionUnit), 0.0); tMax2 = SkTMin<double>(tVals[tLast] + (1.0 / SkDCubic::gPrecisionUnit), 1.0); intersect(cubic1, tMin1, tMax1, cubic2, tMin2, tMax2, 1, i); } tIdx = tLast + 1; } while (tIdx < tVals.count()); return; }
// this flavor centers potential intersections recursively. In contrast, '2' may inadvertently // chase intersections near quadratic ends, requiring odd hacks to find them. static void intersect(const SkDCubic& cubic1, double t1s, double t1e, const SkDCubic& cubic2, double t2s, double t2e, double precisionScale, SkIntersections& i) { i.upDepth(); SkDCubic c1 = cubic1.subDivide(t1s, t1e); SkDCubic c2 = cubic2.subDivide(t2s, t2e); SkTDArray<double> ts1; // OPTIMIZE: if c1 == c2, call once (happens when detecting self-intersection) c1.toQuadraticTs(c1.calcPrecision() * precisionScale, &ts1); SkTDArray<double> ts2; c2.toQuadraticTs(c2.calcPrecision() * precisionScale, &ts2); double t1Start = t1s; int ts1Count = ts1.count(); for (int i1 = 0; i1 <= ts1Count; ++i1) { const double tEnd1 = i1 < ts1Count ? ts1[i1] : 1; const double t1 = t1s + (t1e - t1s) * tEnd1; SkReduceOrder s1; int o1 = quadPart(cubic1, t1Start, t1, &s1); double t2Start = t2s; int ts2Count = ts2.count(); for (int i2 = 0; i2 <= ts2Count; ++i2) { const double tEnd2 = i2 < ts2Count ? ts2[i2] : 1; const double t2 = t2s + (t2e - t2s) * tEnd2; if (&cubic1 == &cubic2 && t1Start >= t2Start) { t2Start = t2; continue; } SkReduceOrder s2; int o2 = quadPart(cubic2, t2Start, t2, &s2); #if ONE_OFF_DEBUG char tab[] = " "; if (tLimits1[0][0] >= t1Start && tLimits1[0][1] <= t1 && tLimits1[1][0] >= t2Start && tLimits1[1][1] <= t2) { SkDCubic cSub1 = cubic1.subDivide(t1Start, t1); SkDCubic cSub2 = cubic2.subDivide(t2Start, t2); SkDebugf("%.*s %s t1=(%1.9g,%1.9g) t2=(%1.9g,%1.9g)", i.depth()*2, tab, __FUNCTION__, t1Start, t1, t2Start, t2); SkIntersections xlocals; intersectWithOrder(s1.fQuad, o1, s2.fQuad, o2, xlocals); SkDebugf(" xlocals.fUsed=%d\n", xlocals.used()); } #endif SkIntersections locals; intersectWithOrder(s1.fQuad, o1, s2.fQuad, o2, locals); double coStart[2] = { -1 }; SkDPoint coPoint; int tCount = locals.used(); for (int tIdx = 0; tIdx < tCount; ++tIdx) { double to1 = t1Start + (t1 - t1Start) * locals[0][tIdx]; double to2 = t2Start + (t2 - t2Start) * locals[1][tIdx]; // if the computed t is not sufficiently precise, iterate SkDPoint p1 = cubic1.xyAtT(to1); SkDPoint p2 = cubic2.xyAtT(to2); if (p1.approximatelyEqual(p2)) { if (locals.isCoincident(tIdx)) { if (coStart[0] < 0) { coStart[0] = to1; coStart[1] = to2; coPoint = p1; } else { i.insertCoincidentPair(coStart[0], to1, coStart[1], to2, coPoint, p1); coStart[0] = -1; } } else if (&cubic1 != &cubic2 || !approximately_equal(to1, to2)) { if (i.swapped()) { // FIXME: insert should respect swap i.insert(to2, to1, p1); } else { i.insert(to1, to2, p1); } } } else { double offset = precisionScale / 16; // FIME: const is arbitrary: test, refine #if 1 double c1Bottom = tIdx == 0 ? 0 : (t1Start + (t1 - t1Start) * locals[0][tIdx - 1] + to1) / 2; double c1Min = SkTMax<double>(c1Bottom, to1 - offset); double c1Top = tIdx == tCount - 1 ? 1 : (t1Start + (t1 - t1Start) * locals[0][tIdx + 1] + to1) / 2; double c1Max = SkTMin<double>(c1Top, to1 + offset); double c2Min = SkTMax<double>(0., to2 - offset); double c2Max = SkTMin<double>(1., to2 + offset); #if ONE_OFF_DEBUG SkDebugf("%.*s %s 1 contains1=%d/%d contains2=%d/%d\n", i.depth()*2, tab, __FUNCTION__, c1Min <= tLimits1[0][1] && tLimits1[0][0] <= c1Max && c2Min <= tLimits1[1][1] && tLimits1[1][0] <= c2Max, to1 - offset <= tLimits1[0][1] && tLimits1[0][0] <= to1 + offset && to2 - offset <= tLimits1[1][1] && tLimits1[1][0] <= to2 + offset, c1Min <= tLimits2[0][1] && tLimits2[0][0] <= c1Max && c2Min <= tLimits2[1][1] && tLimits2[1][0] <= c2Max, to1 - offset <= tLimits2[0][1] && tLimits2[0][0] <= to1 + offset && to2 - offset <= tLimits2[1][1] && tLimits2[1][0] <= to2 + offset); SkDebugf("%.*s %s 1 c1Bottom=%1.9g c1Top=%1.9g c2Bottom=%1.9g c2Top=%1.9g" " 1-o=%1.9g 1+o=%1.9g 2-o=%1.9g 2+o=%1.9g offset=%1.9g\n", i.depth()*2, tab, __FUNCTION__, c1Bottom, c1Top, 0., 1., to1 - offset, to1 + offset, to2 - offset, to2 + offset, offset); SkDebugf("%.*s %s 1 to1=%1.9g to2=%1.9g c1Min=%1.9g c1Max=%1.9g c2Min=%1.9g" " c2Max=%1.9g\n", i.depth()*2, tab, __FUNCTION__, to1, to2, c1Min, c1Max, c2Min, c2Max); #endif intersect(cubic1, c1Min, c1Max, cubic2, c2Min, c2Max, offset, i); #if ONE_OFF_DEBUG SkDebugf("%.*s %s 1 i.used=%d t=%1.9g\n", i.depth()*2, tab, __FUNCTION__, i.used(), i.used() > 0 ? i[0][i.used() - 1] : -1); #endif if (tCount > 1) { c1Min = SkTMax<double>(0., to1 - offset); c1Max = SkTMin<double>(1., to1 + offset); double c2Bottom = tIdx == 0 ? to2 : (t2Start + (t2 - t2Start) * locals[1][tIdx - 1] + to2) / 2; double c2Top = tIdx == tCount - 1 ? to2 : (t2Start + (t2 - t2Start) * locals[1][tIdx + 1] + to2) / 2; if (c2Bottom > c2Top) { SkTSwap(c2Bottom, c2Top); } if (c2Bottom == to2) { c2Bottom = 0; } if (c2Top == to2) { c2Top = 1; } c2Min = SkTMax<double>(c2Bottom, to2 - offset); c2Max = SkTMin<double>(c2Top, to2 + offset); #if ONE_OFF_DEBUG SkDebugf("%.*s %s 2 contains1=%d/%d contains2=%d/%d\n", i.depth()*2, tab, __FUNCTION__, c1Min <= tLimits1[0][1] && tLimits1[0][0] <= c1Max && c2Min <= tLimits1[1][1] && tLimits1[1][0] <= c2Max, to1 - offset <= tLimits1[0][1] && tLimits1[0][0] <= to1 + offset && to2 - offset <= tLimits1[1][1] && tLimits1[1][0] <= to2 + offset, c1Min <= tLimits2[0][1] && tLimits2[0][0] <= c1Max && c2Min <= tLimits2[1][1] && tLimits2[1][0] <= c2Max, to1 - offset <= tLimits2[0][1] && tLimits2[0][0] <= to1 + offset && to2 - offset <= tLimits2[1][1] && tLimits2[1][0] <= to2 + offset); SkDebugf("%.*s %s 2 c1Bottom=%1.9g c1Top=%1.9g c2Bottom=%1.9g c2Top=%1.9g" " 1-o=%1.9g 1+o=%1.9g 2-o=%1.9g 2+o=%1.9g offset=%1.9g\n", i.depth()*2, tab, __FUNCTION__, 0., 1., c2Bottom, c2Top, to1 - offset, to1 + offset, to2 - offset, to2 + offset, offset); SkDebugf("%.*s %s 2 to1=%1.9g to2=%1.9g c1Min=%1.9g c1Max=%1.9g c2Min=%1.9g" " c2Max=%1.9g\n", i.depth()*2, tab, __FUNCTION__, to1, to2, c1Min, c1Max, c2Min, c2Max); #endif intersect(cubic1, c1Min, c1Max, cubic2, c2Min, c2Max, offset, i); #if ONE_OFF_DEBUG SkDebugf("%.*s %s 2 i.used=%d t=%1.9g\n", i.depth()*2, tab, __FUNCTION__, i.used(), i.used() > 0 ? i[0][i.used() - 1] : -1); #endif c1Min = SkTMax<double>(c1Bottom, to1 - offset); c1Max = SkTMin<double>(c1Top, to1 + offset); #if ONE_OFF_DEBUG SkDebugf("%.*s %s 3 contains1=%d/%d contains2=%d/%d\n", i.depth()*2, tab, __FUNCTION__, c1Min <= tLimits1[0][1] && tLimits1[0][0] <= c1Max && c2Min <= tLimits1[1][1] && tLimits1[1][0] <= c2Max, to1 - offset <= tLimits1[0][1] && tLimits1[0][0] <= to1 + offset && to2 - offset <= tLimits1[1][1] && tLimits1[1][0] <= to2 + offset, c1Min <= tLimits2[0][1] && tLimits2[0][0] <= c1Max && c2Min <= tLimits2[1][1] && tLimits2[1][0] <= c2Max, to1 - offset <= tLimits2[0][1] && tLimits2[0][0] <= to1 + offset && to2 - offset <= tLimits2[1][1] && tLimits2[1][0] <= to2 + offset); SkDebugf("%.*s %s 3 c1Bottom=%1.9g c1Top=%1.9g c2Bottom=%1.9g c2Top=%1.9g" " 1-o=%1.9g 1+o=%1.9g 2-o=%1.9g 2+o=%1.9g offset=%1.9g\n", i.depth()*2, tab, __FUNCTION__, 0., 1., c2Bottom, c2Top, to1 - offset, to1 + offset, to2 - offset, to2 + offset, offset); SkDebugf("%.*s %s 3 to1=%1.9g to2=%1.9g c1Min=%1.9g c1Max=%1.9g c2Min=%1.9g" " c2Max=%1.9g\n", i.depth()*2, tab, __FUNCTION__, to1, to2, c1Min, c1Max, c2Min, c2Max); #endif intersect(cubic1, c1Min, c1Max, cubic2, c2Min, c2Max, offset, i); #if ONE_OFF_DEBUG SkDebugf("%.*s %s 3 i.used=%d t=%1.9g\n", i.depth()*2, tab, __FUNCTION__, i.used(), i.used() > 0 ? i[0][i.used() - 1] : -1); #endif } #else double c1Bottom = tIdx == 0 ? 0 : (t1Start + (t1 - t1Start) * locals.fT[0][tIdx - 1] + to1) / 2; double c1Min = SkTMax<double>(c1Bottom, to1 - offset); double c1Top = tIdx == tCount - 1 ? 1 : (t1Start + (t1 - t1Start) * locals.fT[0][tIdx + 1] + to1) / 2; double c1Max = SkTMin<double>(c1Top, to1 + offset); double c2Bottom = tIdx == 0 ? to2 : (t2Start + (t2 - t2Start) * locals.fT[1][tIdx - 1] + to2) / 2; double c2Top = tIdx == tCount - 1 ? to2 : (t2Start + (t2 - t2Start) * locals.fT[1][tIdx + 1] + to2) / 2; if (c2Bottom > c2Top) { SkTSwap(c2Bottom, c2Top); } if (c2Bottom == to2) { c2Bottom = 0; } if (c2Top == to2) { c2Top = 1; } double c2Min = SkTMax<double>(c2Bottom, to2 - offset); double c2Max = SkTMin<double>(c2Top, to2 + offset); #if ONE_OFF_DEBUG SkDebugf("%s contains1=%d/%d contains2=%d/%d\n", __FUNCTION__, c1Min <= 0.210357794 && 0.210357794 <= c1Max && c2Min <= 0.223476406 && 0.223476406 <= c2Max, to1 - offset <= 0.210357794 && 0.210357794 <= to1 + offset && to2 - offset <= 0.223476406 && 0.223476406 <= to2 + offset, c1Min <= 0.211324707 && 0.211324707 <= c1Max && c2Min <= 0.211327209 && 0.211327209 <= c2Max, to1 - offset <= 0.211324707 && 0.211324707 <= to1 + offset && to2 - offset <= 0.211327209 && 0.211327209 <= to2 + offset); SkDebugf("%s c1Bottom=%1.9g c1Top=%1.9g c2Bottom=%1.9g c2Top=%1.9g" " 1-o=%1.9g 1+o=%1.9g 2-o=%1.9g 2+o=%1.9g offset=%1.9g\n", __FUNCTION__, c1Bottom, c1Top, c2Bottom, c2Top, to1 - offset, to1 + offset, to2 - offset, to2 + offset, offset); SkDebugf("%s to1=%1.9g to2=%1.9g c1Min=%1.9g c1Max=%1.9g c2Min=%1.9g" " c2Max=%1.9g\n", __FUNCTION__, to1, to2, c1Min, c1Max, c2Min, c2Max); #endif #endif intersect(cubic1, c1Min, c1Max, cubic2, c2Min, c2Max, offset, i); // FIXME: if no intersection is found, either quadratics intersected where // cubics did not, or the intersection was missed. In the former case, expect // the quadratics to be nearly parallel at the point of intersection, and check // for that. } } SkASSERT(coStart[0] == -1); t2Start = t2; } t1Start = t1; } i.downDepth(); }
bool SkOpAngle::endsIntersect(const SkOpAngle& rh) const { SkPath::Verb lVerb = fSegment->verb(); SkPath::Verb rVerb = rh.fSegment->verb(); int lPts = SkPathOpsVerbToPoints(lVerb); int rPts = SkPathOpsVerbToPoints(rVerb); SkDLine rays[] = {{{fCurvePart[0], rh.fCurvePart[rPts]}}, {{fCurvePart[0], fCurvePart[lPts]}}}; if (rays[0][1] == rays[1][1]) { return checkParallel(rh); } double smallTs[2] = {-1, -1}; bool limited[2] = {false, false}; for (int index = 0; index < 2; ++index) { const SkOpSegment& segment = index ? *rh.fSegment : *fSegment; SkIntersections i; (*CurveIntersectRay[index ? rPts : lPts])(segment.pts(), rays[index], &i); // SkASSERT(i.used() >= 1); // if (i.used() <= 1) { // continue; // } double tStart = segment.t(index ? rh.fStart : fStart); double tEnd = segment.t(index ? rh.fComputedEnd : fComputedEnd); bool testAscends = index ? rh.fStart < rh.fComputedEnd : fStart < fComputedEnd; double t = testAscends ? 0 : 1; for (int idx2 = 0; idx2 < i.used(); ++idx2) { double testT = i[0][idx2]; if (!approximately_between_orderable(tStart, testT, tEnd)) { continue; } if (approximately_equal_orderable(tStart, testT)) { continue; } smallTs[index] = t = testAscends ? SkTMax(t, testT) : SkTMin(t, testT); limited[index] = approximately_equal_orderable(t, tEnd); } } #if 0 if (smallTs[0] < 0 && smallTs[1] < 0) { // if neither ray intersects, do endpoint sort double m0xm1 = 0; if (lVerb == SkPath::kLine_Verb) { SkASSERT(rVerb != SkPath::kLine_Verb); SkDVector m0 = rays[1][1] - fCurvePart[0]; SkDPoint endPt; endPt.set(rh.fSegment->pts()[rh.fStart < rh.fEnd ? rPts : 0]); SkDVector m1 = endPt - fCurvePart[0]; m0xm1 = m0.crossCheck(m1); } if (rVerb == SkPath::kLine_Verb) { SkDPoint endPt; endPt.set(fSegment->pts()[fStart < fEnd ? lPts : 0]); SkDVector m0 = endPt - fCurvePart[0]; SkDVector m1 = rays[0][1] - fCurvePart[0]; m0xm1 = m0.crossCheck(m1); } if (m0xm1 != 0) { return m0xm1 < 0; } } #endif bool sRayLonger = false; SkDVector sCept = {0, 0}; double sCeptT = -1; int sIndex = -1; bool useIntersect = false; for (int index = 0; index < 2; ++index) { if (smallTs[index] < 0) { continue; } const SkOpSegment& segment = index ? *rh.fSegment : *fSegment; const SkDPoint& dPt = segment.dPtAtT(smallTs[index]); SkDVector cept = dPt - rays[index][0]; // If this point is on the curve, it should have been detected earlier by ordinary // curve intersection. This may be hard to determine in general, but for lines, // the point could be close to or equal to its end, but shouldn't be near the start. if ((index ? lPts : rPts) == 1) { SkDVector total = rays[index][1] - rays[index][0]; if (cept.lengthSquared() * 2 < total.lengthSquared()) { continue; } } SkDVector end = rays[index][1] - rays[index][0]; if (cept.fX * end.fX < 0 || cept.fY * end.fY < 0) { continue; } double rayDist = cept.length(); double endDist = end.length(); bool rayLonger = rayDist > endDist; if (limited[0] && limited[1] && rayLonger) { useIntersect = true; sRayLonger = rayLonger; sCept = cept; sCeptT = smallTs[index]; sIndex = index; break; } double delta = fabs(rayDist - endDist); double minX, minY, maxX, maxY; minX = minY = SK_ScalarInfinity; maxX = maxY = -SK_ScalarInfinity; const SkDCubic& curve = index ? rh.fCurvePart : fCurvePart; int ptCount = index ? rPts : lPts; for (int idx2 = 0; idx2 <= ptCount; ++idx2) { minX = SkTMin(minX, curve[idx2].fX); minY = SkTMin(minY, curve[idx2].fY); maxX = SkTMax(maxX, curve[idx2].fX); maxY = SkTMax(maxY, curve[idx2].fY); } double maxWidth = SkTMax(maxX - minX, maxY - minY); delta /= maxWidth; if (delta > 1e-4 && (useIntersect ^= true)) { // FIXME: move this magic number sRayLonger = rayLonger; sCept = cept; sCeptT = smallTs[index]; sIndex = index; } } if (useIntersect) { const SkDCubic& curve = sIndex ? rh.fCurvePart : fCurvePart; const SkOpSegment& segment = sIndex ? *rh.fSegment : *fSegment; double tStart = segment.t(sIndex ? rh.fStart : fStart); SkDVector mid = segment.dPtAtT(tStart + (sCeptT - tStart) / 2) - curve[0]; double septDir = mid.crossCheck(sCept); if (!septDir) { return checkParallel(rh); } return sRayLonger ^ (sIndex == 0) ^ (septDir < 0); } else { return checkParallel(rh); } }