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; }
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 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]); } } }
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; }
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; }
// 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 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 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 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(); }
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; }
void AddSelfIntersectTs(SkOpContour* test) { SkIntersectionHelper wt; wt.init(test); do { if (wt.segmentType() != SkIntersectionHelper::kCubic_Segment) { continue; } SkIntersections ts; int pts = ts.cubic(wt.pts()); debugShowCubicIntersection(pts, wt, ts); if (!pts) { continue; } SkASSERT(pts == 1); SkASSERT(ts[0][0] >= 0 && ts[0][0] <= 1); SkASSERT(ts[1][0] >= 0 && ts[1][0] <= 1); SkPoint point = ts.pt(0).asSkPoint(); int testTAt = wt.addSelfT(wt, point, ts[0][0]); int nextTAt = wt.addT(wt, point, ts[1][0]); wt.addOtherT(testTAt, ts[1][0], nextTAt); wt.addOtherT(nextTAt, ts[0][0], testTAt); } while (wt.advance()); }
// 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; }
void SkIntersections::cubicNearEnd(const SkDCubic& cubic1, bool start, const SkDCubic& cubic2, const SkDRect& bounds2) { SkDLine line; int t1Index = start ? 0 : 3; double testT = (double) !start; // don't bother if the two cubics are connnected static const int kPointsInCubic = 4; // FIXME: move to DCubic, replace '4' with this static const int kMaxLineCubicIntersections = 3; SkSTArray<(kMaxLineCubicIntersections - 1) * kMaxLineCubicIntersections, double, true> tVals; line[0] = cubic1[t1Index]; // this variant looks for intersections with the end point and lines parallel to other points for (int index = 0; index < kPointsInCubic; ++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 (swapped()) { // FIXME: insert should respect swap insert(foundT, testT, line[0]); } else { insert(testT, foundT, line[0]); } } else { tVals.push_back(foundT); } } } if (tVals.count() == 0) { return; } SkTQSort<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(tVals[tIdx] - LINE_FRACTION, 0.0); double tMax2 = SkTMin(tVals[tLast] + LINE_FRACTION, 1.0); int lastUsed = used(); if (start ? tMax1 < tMin2 : tMax2 < tMin1) { ::intersect(cubic1, tMin1, tMax1, cubic2, tMin2, tMax2, 1, *this); } if (lastUsed == used()) { tMin2 = SkTMax(tVals[tIdx] - (1.0 / SkDCubic::gPrecisionUnit), 0.0); tMax2 = SkTMin(tVals[tLast] + (1.0 / SkDCubic::gPrecisionUnit), 1.0); if (start ? tMax1 < tMin2 : tMax2 < tMin1) { ::intersect(cubic1, tMin1, tMax1, cubic2, tMin2, tMax2, 1, *this); } } tIdx = tLast + 1; } while (tIdx < tVals.count()); return; }
// 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"); }
void SkIntersections::append(const SkIntersections& i) { for (int index = 0; index < i.fUsed; ++index) { insert(i[0][index], i[1][index], i.pt(index)); } }
bool AddIntersectTs(SkOpContour* test, SkOpContour* next) { if (test != next) { if (AlmostLessUlps(test->bounds().fBottom, next->bounds().fTop)) { return false; } // OPTIMIZATION: outset contour bounds a smidgen instead? if (!SkPathOpsBounds::Intersects(test->bounds(), next->bounds())) { return true; } } SkIntersectionHelper wt; wt.init(test); bool foundCommonContour = test == next; do { SkIntersectionHelper wn; wn.init(next); if (test == next && !wn.startAfter(wt)) { continue; } do { if (!SkPathOpsBounds::Intersects(wt.bounds(), wn.bounds())) { continue; } int pts = 0; SkIntersections ts; bool swap = false; switch (wt.segmentType()) { case SkIntersectionHelper::kHorizontalLine_Segment: swap = true; switch (wn.segmentType()) { case SkIntersectionHelper::kHorizontalLine_Segment: case SkIntersectionHelper::kVerticalLine_Segment: case SkIntersectionHelper::kLine_Segment: { pts = ts.lineHorizontal(wn.pts(), wt.left(), wt.right(), wt.y(), wt.xFlipped()); debugShowLineIntersection(pts, wn, wt, ts); break; } case SkIntersectionHelper::kQuad_Segment: { pts = ts.quadHorizontal(wn.pts(), wt.left(), wt.right(), wt.y(), wt.xFlipped()); debugShowQuadLineIntersection(pts, wn, wt, ts); break; } case SkIntersectionHelper::kCubic_Segment: { pts = ts.cubicHorizontal(wn.pts(), wt.left(), wt.right(), wt.y(), wt.xFlipped()); debugShowCubicLineIntersection(pts, wn, wt, ts); break; } default: SkASSERT(0); } break; case SkIntersectionHelper::kVerticalLine_Segment: swap = true; switch (wn.segmentType()) { case SkIntersectionHelper::kHorizontalLine_Segment: case SkIntersectionHelper::kVerticalLine_Segment: case SkIntersectionHelper::kLine_Segment: { pts = ts.lineVertical(wn.pts(), wt.top(), wt.bottom(), wt.x(), wt.yFlipped()); debugShowLineIntersection(pts, wn, wt, ts); break; } case SkIntersectionHelper::kQuad_Segment: { pts = ts.quadVertical(wn.pts(), wt.top(), wt.bottom(), wt.x(), wt.yFlipped()); debugShowQuadLineIntersection(pts, wn, wt, ts); break; } case SkIntersectionHelper::kCubic_Segment: { pts = ts.cubicVertical(wn.pts(), wt.top(), wt.bottom(), wt.x(), wt.yFlipped()); debugShowCubicLineIntersection(pts, wn, wt, ts); break; } default: SkASSERT(0); } break; case SkIntersectionHelper::kLine_Segment: switch (wn.segmentType()) { case SkIntersectionHelper::kHorizontalLine_Segment: pts = ts.lineHorizontal(wt.pts(), wn.left(), wn.right(), wn.y(), wn.xFlipped()); debugShowLineIntersection(pts, wt, wn, ts); break; case SkIntersectionHelper::kVerticalLine_Segment: pts = ts.lineVertical(wt.pts(), wn.top(), wn.bottom(), wn.x(), wn.yFlipped()); debugShowLineIntersection(pts, wt, wn, ts); break; case SkIntersectionHelper::kLine_Segment: { pts = ts.lineLine(wt.pts(), wn.pts()); debugShowLineIntersection(pts, wt, wn, ts); break; } case SkIntersectionHelper::kQuad_Segment: { swap = true; pts = ts.quadLine(wn.pts(), wt.pts()); debugShowQuadLineIntersection(pts, wn, wt, ts); break; } case SkIntersectionHelper::kCubic_Segment: { swap = true; pts = ts.cubicLine(wn.pts(), wt.pts()); debugShowCubicLineIntersection(pts, wn, wt, ts); break; } default: SkASSERT(0); } break; case SkIntersectionHelper::kQuad_Segment: switch (wn.segmentType()) { case SkIntersectionHelper::kHorizontalLine_Segment: pts = ts.quadHorizontal(wt.pts(), wn.left(), wn.right(), wn.y(), wn.xFlipped()); debugShowQuadLineIntersection(pts, wt, wn, ts); break; case SkIntersectionHelper::kVerticalLine_Segment: pts = ts.quadVertical(wt.pts(), wn.top(), wn.bottom(), wn.x(), wn.yFlipped()); debugShowQuadLineIntersection(pts, wt, wn, ts); break; case SkIntersectionHelper::kLine_Segment: { pts = ts.quadLine(wt.pts(), wn.pts()); debugShowQuadLineIntersection(pts, wt, wn, ts); break; } case SkIntersectionHelper::kQuad_Segment: { pts = ts.quadQuad(wt.pts(), wn.pts()); debugShowQuadIntersection(pts, wt, wn, ts); break; } case SkIntersectionHelper::kCubic_Segment: { swap = true; pts = ts.cubicQuad(wn.pts(), wt.pts()); debugShowCubicQuadIntersection(pts, wn, wt, ts); break; } default: SkASSERT(0); } break; case SkIntersectionHelper::kCubic_Segment: switch (wn.segmentType()) { case SkIntersectionHelper::kHorizontalLine_Segment: pts = ts.cubicHorizontal(wt.pts(), wn.left(), wn.right(), wn.y(), wn.xFlipped()); debugShowCubicLineIntersection(pts, wt, wn, ts); break; case SkIntersectionHelper::kVerticalLine_Segment: pts = ts.cubicVertical(wt.pts(), wn.top(), wn.bottom(), wn.x(), wn.yFlipped()); debugShowCubicLineIntersection(pts, wt, wn, ts); break; case SkIntersectionHelper::kLine_Segment: { pts = ts.cubicLine(wt.pts(), wn.pts()); debugShowCubicLineIntersection(pts, wt, wn, ts); break; } case SkIntersectionHelper::kQuad_Segment: { pts = ts.cubicQuad(wt.pts(), wn.pts()); debugShowCubicQuadIntersection(pts, wt, wn, ts); break; } case SkIntersectionHelper::kCubic_Segment: { pts = ts.cubicCubic(wt.pts(), wn.pts()); debugShowCubicIntersection(pts, wt, wn, ts); break; } default: SkASSERT(0); } break; default: SkASSERT(0); } if (!foundCommonContour && pts > 0) { test->addCross(next); next->addCross(test); foundCommonContour = true; } // in addition to recording T values, record matching segment if (pts == 2) { if (wn.segmentType() <= SkIntersectionHelper::kLine_Segment && wt.segmentType() <= SkIntersectionHelper::kLine_Segment) { if (wt.addCoincident(wn, ts, swap)) { continue; } ts.cleanUpCoincidence(); // prefer (t == 0 or t == 1) pts = 1; } else if (wn.segmentType() >= SkIntersectionHelper::kQuad_Segment && wt.segmentType() >= SkIntersectionHelper::kQuad_Segment && ts.isCoincident(0)) { SkASSERT(ts.coincidentUsed() == 2); if (wt.addCoincident(wn, ts, swap)) { continue; } ts.cleanUpCoincidence(); // prefer (t == 0 or t == 1) pts = 1; } } if (pts >= 2) { for (int pt = 0; pt < pts - 1; ++pt) { const SkDPoint& point = ts.pt(pt); const SkDPoint& next = ts.pt(pt + 1); if (wt.isPartial(ts[swap][pt], ts[swap][pt + 1], point, next) && wn.isPartial(ts[!swap][pt], ts[!swap][pt + 1], point, next)) { if (!wt.addPartialCoincident(wn, ts, pt, swap)) { // remove extra point if two map to same float values ts.cleanUpCoincidence(); // prefer (t == 0 or t == 1) pts = 1; } } } } for (int pt = 0; pt < pts; ++pt) { SkASSERT(ts[0][pt] >= 0 && ts[0][pt] <= 1); SkASSERT(ts[1][pt] >= 0 && ts[1][pt] <= 1); SkPoint point = ts.pt(pt).asSkPoint(); int testTAt = wt.addT(wn, point, ts[swap][pt]); int nextTAt = wn.addT(wt, point, ts[!swap][pt]); wt.addOtherT(testTAt, ts[!swap][pt], nextTAt); wn.addOtherT(nextTAt, ts[swap][pt], testTAt); } } while (wn.advance()); } while (wt.advance()); return true; }