bool SkOpContour::addPartialCoincident(int index, SkOpContour* other, int otherIndex, const SkIntersections& ts, int ptIndex, bool swap) { SkPoint pt0 = ts.pt(ptIndex).asSkPoint(); SkPoint pt1 = ts.pt(ptIndex + 1).asSkPoint(); if (SkDPoint::ApproximatelyEqual(pt0, pt1)) { // FIXME: one could imagine a case where it would be incorrect to ignore this // suppose two self-intersecting cubics overlap to form a partial coincidence -- // although it isn't clear why the regular coincidence could wouldn't pick this up // this is exceptional enough to ignore for now return false; } SkCoincidence& coincidence = fPartialCoincidences.push_back(); coincidence.fOther = other; coincidence.fSegments[0] = index; coincidence.fSegments[1] = otherIndex; coincidence.fTs[swap][0] = ts[0][ptIndex]; coincidence.fTs[swap][1] = ts[0][ptIndex + 1]; coincidence.fTs[!swap][0] = ts[1][ptIndex]; coincidence.fTs[!swap][1] = ts[1][ptIndex + 1]; coincidence.fPts[0][0] = coincidence.fPts[1][0] = pt0; coincidence.fPts[0][1] = coincidence.fPts[1][1] = pt1; coincidence.fNearly[0] = 0; coincidence.fNearly[1] = 0; return true; }
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(); }
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; QuadPts q = {{line[0], dPt, qPt}}; SkDQuad quad; quad.debugSet(q.fPts); // 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 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; }
bool SkOpContour::addCoincident(int index, SkOpContour* other, int otherIndex, const SkIntersections& ts, bool swap) { SkPoint pt0 = ts.pt(0).asSkPoint(); SkPoint pt1 = ts.pt(1).asSkPoint(); if (pt0 == pt1) { // FIXME: one could imagine a case where it would be incorrect to ignore this // suppose two self-intersecting cubics overlap to be coincident -- // this needs to check that by some measure the t values are far enough apart // or needs to check to see if the self-intersection bit was set on the cubic segment return false; } SkCoincidence& coincidence = fCoincidences.push_back(); coincidence.fOther = other; coincidence.fSegments[0] = index; coincidence.fSegments[1] = otherIndex; coincidence.fTs[swap][0] = ts[0][0]; coincidence.fTs[swap][1] = ts[0][1]; coincidence.fTs[!swap][0] = ts[1][0]; coincidence.fTs[!swap][1] = ts[1][1]; coincidence.fPts[swap][0] = pt0; coincidence.fPts[swap][1] = pt1; bool nearStart = ts.nearlySame(0); bool nearEnd = ts.nearlySame(1); coincidence.fPts[!swap][0] = nearStart ? ts.pt2(0).asSkPoint() : pt0; coincidence.fPts[!swap][1] = nearEnd ? ts.pt2(1).asSkPoint() : pt1; coincidence.fNearly[0] = nearStart; coincidence.fNearly[1] = nearEnd; return true; }
static void TestLineIntersection(skiatest::Reporter* reporter) { size_t index; for (index = 0; index < tests_count; ++index) { const SkDLine& line1 = tests[index][0]; const SkDLine& line2 = tests[index][1]; SkIntersections ts; int pts = ts.intersect(line1, line2); REPORTER_ASSERT(reporter, pts); for (int i = 0; i < pts; ++i) { SkDPoint result1 = line1.xyAtT(ts[0][i]); SkDPoint result2 = line2.xyAtT(ts[1][i]); if (!result1.approximatelyEqual(result2)) { REPORTER_ASSERT(reporter, pts != 1); result2 = line2.xyAtT(ts[1][i ^ 1]); REPORTER_ASSERT(reporter, result1.approximatelyEqual(result2)); } } } 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); } }
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; }
// returns false if there's more than one intercept or the intercept doesn't match the point // returns true if the intercept was successfully added or if the // original quads need to be subdivided static bool add_intercept(const SkDQuad& q1, const SkDQuad& q2, double tMin, double tMax, SkIntersections* i, bool* subDivide) { double tMid = (tMin + tMax) / 2; SkDPoint mid = q2.ptAtT(tMid); SkDLine line; line[0] = line[1] = mid; SkDVector dxdy = q2.dxdyAtT(tMid); line[0] -= dxdy; line[1] += dxdy; SkIntersections rootTs; rootTs.allowNear(false); int roots = rootTs.intersect(q1, line); if (roots == 0) { if (subDivide) { *subDivide = true; } return true; } if (roots == 2) { return false; } SkDPoint pt2 = q1.ptAtT(rootTs[0][0]); if (!pt2.approximatelyEqualHalf(mid)) { return false; } i->insertSwap(rootTs[0][0], tMid, pt2); return true; }
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; }
static void PathOpsCubicLineIntersectionTest(skiatest::Reporter* reporter) { for (size_t index = 0; index < lineCubicTests_count; ++index) { int iIndex = static_cast<int>(index); const SkDCubic& cubic = lineCubicTests[index].cubic; const SkDLine& line = lineCubicTests[index].line; SkReduceOrder reduce1; SkReduceOrder reduce2; int order1 = reduce1.reduce(cubic, SkReduceOrder::kNo_Quadratics, SkReduceOrder::kFill_Style); int order2 = reduce2.reduce(line); if (order1 < 4) { SkDebugf("[%d] cubic order=%d\n", iIndex, order1); REPORTER_ASSERT(reporter, 0); } if (order2 < 2) { SkDebugf("[%d] line order=%d\n", iIndex, order2); REPORTER_ASSERT(reporter, 0); } if (order1 == 4 && order2 == 2) { SkIntersections i; int roots = i.intersect(cubic, line); for (int pt = 0; pt < roots; ++pt) { double tt1 = i[0][pt]; SkDPoint xy1 = cubic.xyAtT(tt1); double tt2 = i[1][pt]; SkDPoint xy2 = line.xyAtT(tt2); if (!xy1.approximatelyEqual(xy2)) { SkDebugf("%s [%d,%d] x!= t1=%g (%g,%g) t2=%g (%g,%g)\n", __FUNCTION__, iIndex, pt, tt1, xy1.fX, xy1.fY, tt2, xy2.fX, xy2.fY); } REPORTER_ASSERT(reporter, xy1.approximatelyEqual(xy2)); } } } }
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 void cubicQuadIntersection(skiatest::Reporter* reporter, int index) { int iIndex = static_cast<int>(index); const SkDCubic& cubic = quadCubicTests[index].cubic; SkASSERT(ValidCubic(cubic)); const SkDQuad& quad = quadCubicTests[index].quad; SkASSERT(ValidQuad(quad)); SkReduceOrder reduce1; SkReduceOrder reduce2; int order1 = reduce1.reduce(cubic, SkReduceOrder::kNo_Quadratics); int order2 = reduce2.reduce(quad); if (order1 != 4) { SkDebugf("[%d] cubic order=%d\n", iIndex, order1); REPORTER_ASSERT(reporter, 0); } if (order2 != 3) { SkDebugf("[%d] quad order=%d\n", iIndex, order2); REPORTER_ASSERT(reporter, 0); } SkIntersections i; int roots = i.intersect(cubic, quad); for (int pt = 0; pt < roots; ++pt) { double tt1 = i[0][pt]; SkDPoint xy1 = cubic.ptAtT(tt1); double tt2 = i[1][pt]; SkDPoint xy2 = quad.ptAtT(tt2); if (!xy1.approximatelyEqual(xy2)) { SkDebugf("%s [%d,%d] x!= t1=%g (%g,%g) t2=%g (%g,%g)\n", __FUNCTION__, iIndex, pt, tt1, xy1.fX, xy1.fY, tt2, xy2.fX, xy2.fY); } REPORTER_ASSERT(reporter, xy1.approximatelyEqual(xy2)); } reporter->bumpTestCount(); }
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(); } }
void SkOpContour::addPartialCoincident(int index, SkOpContour* other, int otherIndex, const SkIntersections& ts, int ptIndex, bool swap) { SkCoincidence& coincidence = fPartialCoincidences.push_back(); coincidence.fOther = other; coincidence.fSegments[0] = index; coincidence.fSegments[1] = otherIndex; coincidence.fTs[swap][0] = ts[0][index]; coincidence.fTs[swap][1] = ts[0][index + 1]; coincidence.fTs[!swap][0] = ts[1][index]; coincidence.fTs[!swap][1] = ts[1][index + 1]; coincidence.fPts[0] = ts.pt(index).asSkPoint(); coincidence.fPts[1] = ts.pt(index + 1).asSkPoint(); }
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 intersectWithOrder(const SkDQuad& simple1, int order1, const SkDQuad& simple2, int order2, SkIntersections& i) { if (order1 == 3 && order2 == 3) { i.intersect(simple1, simple2); } else if (order1 <= 2 && order2 <= 2) { i.intersect((const SkDLine&) simple1, (const SkDLine&) simple2); } else if (order1 == 3 && order2 <= 2) { i.intersect(simple1, (const SkDLine&) simple2); } else { SkASSERT(order1 <= 2 && order2 == 3); i.intersect(simple2, (const SkDLine&) simple1); i.swapPts(); } }
static void hullIntersect(const SkDQuad& q1, const SkDQuad& q2) { SkDebugf("%s", __FUNCTION__); SkIntersections ts; for (int i1 = 0; i1 < 3; ++i1) { SkDLine l1 = {{q1[i1], q1[(i1 + 1) % 3]}}; for (int i2 = 0; i2 < 3; ++i2) { SkDLine l2 = {{q2[i2], q2[(i2 + 1) % 3]}}; if (ts.intersect(l1, l2)) { SkDebugf(" [%d,%d]", i1, i2); } } } SkDebugf("\n"); }
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]; }
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 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 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 PathOpsCubicQuadIntersectionTest(skiatest::Reporter* reporter) { for (size_t index = 0; index < quadCubicTests_count; ++index) { int iIndex = static_cast<int>(index); const SkDCubic& cubic = quadCubicTests[index].cubic; const SkDQuad& quad = quadCubicTests[index].quad; SkReduceOrder reduce1; SkReduceOrder reduce2; int order1 = reduce1.reduce(cubic, SkReduceOrder::kNo_Quadratics, SkReduceOrder::kFill_Style); int order2 = reduce2.reduce(quad, SkReduceOrder::kFill_Style); if (order1 != 4) { SkDebugf("[%d] cubic order=%d\n", iIndex, order1); REPORTER_ASSERT(reporter, 0); } if (order2 != 3) { SkDebugf("[%d] quad order=%d\n", iIndex, order2); REPORTER_ASSERT(reporter, 0); } SkIntersections i; int roots = i.intersect(cubic, quad); SkASSERT(roots == quadCubicTests[index].answerCount); for (int pt = 0; pt < roots; ++pt) { double tt1 = i[0][pt]; SkDPoint xy1 = cubic.xyAtT(tt1); double tt2 = i[1][pt]; SkDPoint xy2 = quad.xyAtT(tt2); if (!xy1.approximatelyEqual(xy2)) { SkDebugf("%s [%d,%d] x!= t1=%g (%g,%g) t2=%g (%g,%g)\n", __FUNCTION__, iIndex, pt, tt1, xy1.fX, xy1.fY, tt2, xy2.fX, xy2.fY); } REPORTER_ASSERT(reporter, xy1.approximatelyEqual(xy2)); bool found = false; for (int idx2 = 0; idx2 < quadCubicTests[index].answerCount; ++idx2) { found |= quadCubicTests[index].answers[idx2].approximatelyEqual(xy1); } REPORTER_ASSERT(reporter, found); } reporter->bumpTestCount(); } }
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(); }
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 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); } } } } } }
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"); }