static void oneOffTest1(size_t outer, size_t inner) { const Quadratic& quad1 = testSet[outer]; const Quadratic& quad2 = testSet[inner]; Intersections intersections2; intersect2(quad1, quad2, intersections2); if (intersections2.fUnsortable) { SkASSERT(0); return; } for (int pt = 0; pt < intersections2.used(); ++pt) { double tt1 = intersections2.fT[0][pt]; double tx1, ty1; xy_at_t(quad1, tt1, tx1, ty1); int pt2 = intersections2.fFlip ? intersections2.used() - pt - 1 : pt; double tt2 = intersections2.fT[1][pt2]; double tx2, ty2; xy_at_t(quad2, tt2, tx2, ty2); if (!AlmostEqualUlps(tx1, tx2)) { SkDebugf("%s [%d,%d] x!= t1=%g (%g,%g) t2=%g (%g,%g)\n", __FUNCTION__, (int)outer, (int)inner, tt1, tx1, ty1, tt2, tx2, ty2); SkASSERT(0); } if (!AlmostEqualUlps(ty1, ty2)) { SkDebugf("%s [%d,%d] y!= t1=%g (%g,%g) t2=%g (%g,%g)\n", __FUNCTION__, (int)outer, (int)inner, tt1, tx1, ty1, tt2, tx2, ty2); SkASSERT(0); } #if ONE_OFF_DEBUG SkDebugf("%s [%d][%d] t1=%1.9g (%1.9g, %1.9g) t2=%1.9g\n", __FUNCTION__, outer, inner, tt1, tx1, tx2, tt2); #endif } }
// check to see if it is a quadratic or a line static int check_quadratic(const SkDCubic& cubic, SkDCubic& reduction) { double dx10 = cubic[1].fX - cubic[0].fX; double dx23 = cubic[2].fX - cubic[3].fX; double midX = cubic[0].fX + dx10 * 3 / 2; double sideAx = midX - cubic[3].fX; double sideBx = dx23 * 3 / 2; if (approximately_zero(sideAx) ? !approximately_equal(sideAx, sideBx) : !AlmostEqualUlps(sideAx, sideBx)) { return 0; } double dy10 = cubic[1].fY - cubic[0].fY; double dy23 = cubic[2].fY - cubic[3].fY; double midY = cubic[0].fY + dy10 * 3 / 2; double sideAy = midY - cubic[3].fY; double sideBy = dy23 * 3 / 2; if (approximately_zero(sideAy) ? !approximately_equal(sideAy, sideBy) : !AlmostEqualUlps(sideAy, sideBy)) { return 0; } reduction[0] = cubic[0]; reduction[1].fX = midX; reduction[1].fY = midY; reduction[2] = cubic[3]; return 3; }
bool intersect(double minT1, double maxT1, double minT2, double maxT2) { Cubic sub1, sub2; // FIXME: carry last subdivide and reduceOrder result with cubic sub_divide(cubic1, minT1, maxT1, sub1); sub_divide(cubic2, minT2, maxT2, sub2); Intersections i; intersect2(sub1, sub2, i); if (i.used() == 0) { return false; } double x1, y1, x2, y2; t1 = minT1 + i.fT[0][0] * (maxT1 - minT1); t2 = minT2 + i.fT[1][0] * (maxT2 - minT2); xy_at_t(cubic1, t1, x1, y1); xy_at_t(cubic2, t2, x2, y2); if (AlmostEqualUlps(x1, x2) && AlmostEqualUlps(y1, y2)) { return true; } double half1 = (minT1 + maxT1) / 2; double half2 = (minT2 + maxT2) / 2; ++depth; bool result; if (depth & 1) { result = intersect(minT1, half1, minT2, maxT2) || intersect(half1, maxT1, minT2, maxT2) || intersect(minT1, maxT1, minT2, half2) || intersect(minT1, maxT1, half2, maxT2); } else { result = intersect(minT1, maxT1, minT2, half2) || intersect(minT1, maxT1, half2, maxT2) || intersect(minT1, half1, minT2, maxT2) || intersect(half1, maxT1, minT2, maxT2); } --depth; return result; }
// unlike quadratic roots, this does not discard real roots <= 0 or >= 1 int quadraticRootsReal(const double A, const double B, const double C, double s[2]) { const double p = B / (2 * A); const double q = C / A; if (approximately_zero(A) && (approximately_zero_inverse(p) || approximately_zero_inverse(q))) { if (approximately_zero(B)) { s[0] = 0; return C == 0; } s[0] = -C / B; return 1; } /* normal form: x^2 + px + q = 0 */ const double p2 = p * p; #if 0 double D = AlmostEqualUlps(p2, q) ? 0 : p2 - q; if (D <= 0) { if (D < 0) { return 0; } s[0] = -p; SkDebugf("[%d] %1.9g\n", 1, s[0]); return 1; } double sqrt_D = sqrt(D); s[0] = sqrt_D - p; s[1] = -sqrt_D - p; SkDebugf("[%d] %1.9g %1.9g\n", 2, s[0], s[1]); return 2; #else if (!AlmostEqualUlps(p2, q) && p2 < q) { return 0; } double sqrt_D = 0; if (p2 > q) { sqrt_D = sqrt(p2 - q); } s[0] = sqrt_D - p; s[1] = -sqrt_D - p; #if 0 if (AlmostEqualUlps(s[0], s[1])) { SkDebugf("[%d] %1.9g\n", 1, s[0]); } else { SkDebugf("[%d] %1.9g %1.9g\n", 2, s[0], s[1]); } #endif return 1 + !AlmostEqualUlps(s[0], s[1]); #endif }
double SkDLine::nearPoint(const SkDPoint& xy) const { if (!AlmostBetweenUlps(fPts[0].fX, xy.fX, fPts[1].fX) || !AlmostBetweenUlps(fPts[0].fY, xy.fY, fPts[1].fY)) { return -1; } // project a perpendicular ray from the point to the line; find the T on the line SkDVector len = fPts[1] - fPts[0]; // the x/y magnitudes of the line double denom = len.fX * len.fX + len.fY * len.fY; // see DLine intersectRay SkDVector ab0 = xy - fPts[0]; double numer = len.fX * ab0.fX + ab0.fY * len.fY; if (!between(0, numer, denom)) { return -1; } double t = numer / denom; SkDPoint realPt = ptAtT(t); double dist = realPt.distance(xy); // OPTIMIZATION: can we compare against distSq instead ? // find the ordinal in the original line with the largest unsigned exponent double tiniest = SkTMin(SkTMin(SkTMin(fPts[0].fX, fPts[0].fY), fPts[1].fX), fPts[1].fY); double largest = SkTMax(SkTMax(SkTMax(fPts[0].fX, fPts[0].fY), fPts[1].fX), fPts[1].fY); largest = SkTMax(largest, -tiniest); if (!AlmostEqualUlps(largest, largest + dist)) { // is the dist within ULPS tolerance? return -1; } t = SkPinT(t); SkASSERT(between(0, t, 1)); return t; }
// reduce to a quadratic or smaller // look for identical points // look for all four points in a line // note that three points in a line doesn't simplify a cubic // look for approximation with single quadratic // save approximation with multiple quadratics for later int SkReduceOrder::reduce(const SkDQuad& quad) { int index, minX, maxX, minY, maxY; int minXSet, minYSet; minX = maxX = minY = maxY = 0; minXSet = minYSet = 0; for (index = 1; index < 3; ++index) { if (quad[minX].fX > quad[index].fX) { minX = index; } if (quad[minY].fY > quad[index].fY) { minY = index; } if (quad[maxX].fX < quad[index].fX) { maxX = index; } if (quad[maxY].fY < quad[index].fY) { maxY = index; } } for (index = 0; index < 3; ++index) { if (AlmostEqualUlps(quad[index].fX, quad[minX].fX)) { minXSet |= 1 << index; } if (AlmostEqualUlps(quad[index].fY, quad[minY].fY)) { minYSet |= 1 << index; } } if ((minXSet & 0x05) == 0x5 && (minYSet & 0x05) == 0x5) { // test for degenerate // this quad starts and ends at the same place, so never contributes // to the fill return coincident_line(quad, fQuad); } if (minXSet == 0x7) { // test for vertical line return vertical_line(quad, fQuad); } if (minYSet == 0x7) { // test for horizontal line return horizontal_line(quad, fQuad); } int result = check_linear(quad, minX, maxX, minY, maxY, fQuad); if (result) { return result; } fQuad = quad; return 3; }
void SkOpSegment::debugValidate() const { #if DEBUG_VALIDATE int count = fTs.count(); SK_ALWAYSBREAK(count >= 2); SK_ALWAYSBREAK(fTs[0].fT == 0); SK_ALWAYSBREAK(fTs[count - 1].fT == 1); int done = 0; double t = -1; const SkOpSpan* last = NULL; bool tinyTFound = false; bool hasLoop = false; for (int i = 0; i < count; ++i) { const SkOpSpan& span = fTs[i]; SK_ALWAYSBREAK(t <= span.fT); t = span.fT; int otherIndex = span.fOtherIndex; const SkOpSegment* other = span.fOther; SK_ALWAYSBREAK(other != this || fVerb == SkPath::kCubic_Verb); const SkOpSpan& otherSpan = other->fTs[otherIndex]; SK_ALWAYSBREAK(otherSpan.fPt == span.fPt); SK_ALWAYSBREAK(otherSpan.fOtherT == t); SK_ALWAYSBREAK(&fTs[i] == &otherSpan.fOther->fTs[otherSpan.fOtherIndex]); done += span.fDone; if (last) { SK_ALWAYSBREAK(last->fT != span.fT || last->fOther != span.fOther); bool tsEqual = last->fT == span.fT; bool tsPreciselyEqual = precisely_equal(last->fT, span.fT); SK_ALWAYSBREAK(!tsEqual || tsPreciselyEqual); bool pointsEqual = last->fPt == span.fPt; bool pointsNearlyEqual = AlmostEqualUlps(last->fPt, span.fPt); #if 0 // bufferOverflow test triggers this SK_ALWAYSBREAK(!tsPreciselyEqual || pointsNearlyEqual); #endif // SK_ALWAYSBREAK(!last->fTiny || !tsPreciselyEqual || span.fTiny || tinyTFound); SK_ALWAYSBREAK(last->fTiny || tsPreciselyEqual || !pointsEqual || hasLoop); SK_ALWAYSBREAK(!last->fTiny || pointsEqual); SK_ALWAYSBREAK(!last->fTiny || last->fDone); SK_ALWAYSBREAK(!last->fSmall || pointsNearlyEqual); SK_ALWAYSBREAK(!last->fSmall || last->fDone); // SK_ALWAYSBREAK(!last->fSmall || last->fTiny); // SK_ALWAYSBREAK(last->fTiny || !pointsEqual || last->fDone == span.fDone); if (last->fTiny) { tinyTFound |= !tsPreciselyEqual; } else { tinyTFound = false; } } last = &span; hasLoop |= last->fLoop; } SK_ALWAYSBREAK(done == fDoneSpans); // if (fAngles.count() ) { // fAngles.begin()->debugValidateLoop(); // } #endif }
// check to see if it is a quadratic or a line static int check_quadratic(const Cubic& cubic, Cubic& reduction) { double dx10 = cubic[1].x - cubic[0].x; double dx23 = cubic[2].x - cubic[3].x; double midX = cubic[0].x + dx10 * 3 / 2; if (!AlmostEqualUlps(midX - cubic[3].x, dx23 * 3 / 2)) { return 0; } double dy10 = cubic[1].y - cubic[0].y; double dy23 = cubic[2].y - cubic[3].y; double midY = cubic[0].y + dy10 * 3 / 2; if (!AlmostEqualUlps(midY - cubic[3].y, dy23 * 3 / 2)) { return 0; } reduction[0] = cubic[0]; reduction[1].x = midX; reduction[1].y = midY; reduction[2] = cubic[3]; return 3; }
// reduce to a quadratic or smaller // look for identical points // look for all four points in a line // note that three points in a line doesn't simplify a cubic // look for approximation with single quadratic // save approximation with multiple quadratics for later int reduceOrder(const Quadratic& quad, Quadratic& reduction) { int index, minX, maxX, minY, maxY; int minXSet, minYSet; minX = maxX = minY = maxY = 0; minXSet = minYSet = 0; for (index = 1; index < 3; ++index) { if (quad[minX].x > quad[index].x) { minX = index; } if (quad[minY].y > quad[index].y) { minY = index; } if (quad[maxX].x < quad[index].x) { maxX = index; } if (quad[maxY].y < quad[index].y) { maxY = index; } } for (index = 0; index < 3; ++index) { if (AlmostEqualUlps(quad[index].x, quad[minX].x)) { minXSet |= 1 << index; } if (AlmostEqualUlps(quad[index].y, quad[minY].y)) { minYSet |= 1 << index; } } if (minXSet == 0x7) { // test for vertical line if (minYSet == 0x7) { // return 1 if all four are coincident return coincident_line(quad, reduction); } return vertical_line(quad, reduction); } if (minYSet == 0xF) { // test for horizontal line return horizontal_line(quad, reduction); } int result = check_linear(quad, reduction, minX, maxX, minY, maxY); if (result) { return result; } memcpy(reduction, quad, sizeof(Quadratic)); return 3; }
static bool checkEndPointH(const SkDPoint& end, double left, double right, double y, bool flipped, double* tPtr) { if (!between(left, end.fX, right) || !AlmostEqualUlps(y, end.fY)) { return false; } double t = (end.fX - left) / (right - left); SkASSERT(between(0, t, 1)); *tPtr = flipped ? 1 - t : t; return true; }
static bool checkEndPointV(const SkDPoint& end, double top, double bottom, double x, bool flipped, double* tPtr) { if (!between(top, end.fY, bottom) || !AlmostEqualUlps(x, end.fX)) { return false; } double t = (end.fY - top) / (bottom - top); SkASSERT(between(0, t, 1)); *tPtr = flipped ? 1 - t : t; return true; }
// reduce to a quadratic or smaller // look for identical points // look for all four points in a line // note that three points in a line doesn't simplify a cubic // look for approximation with single quadratic // save approximation with multiple quadratics for later int SkReduceOrder::reduce(const SkDQuad& quad) { int index, minX, maxX, minY, maxY; int minXSet, minYSet; minX = maxX = minY = maxY = 0; minXSet = minYSet = 0; for (index = 1; index < 3; ++index) { if (quad[minX].fX > quad[index].fX) { minX = index; } if (quad[minY].fY > quad[index].fY) { minY = index; } if (quad[maxX].fX < quad[index].fX) { maxX = index; } if (quad[maxY].fY < quad[index].fY) { maxY = index; } } for (index = 0; index < 3; ++index) { if (AlmostEqualUlps(quad[index].fX, quad[minX].fX)) { minXSet |= 1 << index; } if (AlmostEqualUlps(quad[index].fY, quad[minY].fY)) { minYSet |= 1 << index; } } if (minXSet == 0x7) { // test for vertical line if (minYSet == 0x7) { // return 1 if all four are coincident return coincident_line(quad, fQuad); } return vertical_line(quad, fQuad); } if (minYSet == 0xF) { // test for horizontal line return horizontal_line(quad, fQuad); } int result = check_linear(quad, minX, maxX, minY, maxY, fQuad); if (result) { return result; } fQuad = quad; return 3; }
void CubicIntersection_Test() { for (size_t index = firstCubicIntersectionTest; index < tests_count; ++index) { const Cubic& cubic1 = tests[index][0]; const Cubic& cubic2 = tests[index][1]; Cubic reduce1, reduce2; int order1 = reduceOrder(cubic1, reduce1, kReduceOrder_NoQuadraticsAllowed); int order2 = reduceOrder(cubic2, reduce2, kReduceOrder_NoQuadraticsAllowed); if (order1 < 4) { printf("%s [%d] cubic1 order=%d\n", __FUNCTION__, (int) index, order1); continue; } if (order2 < 4) { printf("%s [%d] cubic2 order=%d\n", __FUNCTION__, (int) index, order2); continue; } if (implicit_matches(reduce1, reduce2)) { printf("%s [%d] coincident\n", __FUNCTION__, (int) index); continue; } Intersections tIntersections; intersect(reduce1, reduce2, tIntersections); if (!tIntersections.intersected()) { printf("%s [%d] no intersection\n", __FUNCTION__, (int) index); continue; } for (int pt = 0; pt < tIntersections.used(); ++pt) { double tt1 = tIntersections.fT[0][pt]; double tx1, ty1; xy_at_t(cubic1, tt1, tx1, ty1); double tt2 = tIntersections.fT[1][pt]; double tx2, ty2; xy_at_t(cubic2, tt2, tx2, ty2); if (!AlmostEqualUlps(tx1, tx2)) { printf("%s [%d,%d] x!= t1=%g (%g,%g) t2=%g (%g,%g)\n", __FUNCTION__, (int)index, pt, tt1, tx1, ty1, tt2, tx2, ty2); } if (!AlmostEqualUlps(ty1, ty2)) { printf("%s [%d,%d] y!= t1=%g (%g,%g) t2=%g (%g,%g)\n", __FUNCTION__, (int)index, pt, tt1, tx1, ty1, tt2, tx2, ty2); } } } }
double SkDLine::NearPointV(const SkDPoint& xy, double top, double bottom, double x) { if (!AlmostEqualUlps(xy.fX, x)) { return -1; } if (!AlmostBetweenUlps(top, xy.fY, bottom)) { return -1; } double t = (xy.fY - top) / (bottom - top); t = SkPinT(t); SkASSERT(between(0, t, 1)); return t; }
double SkDLine::NearPointH(const SkDPoint& xy, double left, double right, double y) { if (!AlmostEqualUlps(xy.fY, y)) { return -1; } if (!AlmostBetweenUlps(left, xy.fX, right)) { return -1; } double t = (xy.fX - left) / (right - left); t = SkPinT(t); SkASSERT(between(0, t, 1)); return t; }
static void testC(skiatest::Reporter* reporter, const SkDCubic* cubics, const char* name, int firstTest, size_t testCount) { // test if computed line end points are valid for (size_t index = firstTest; index < testCount; ++index) { const SkDCubic& cubic = cubics[index]; double precision = cubic.calcPrecision(); SkTDArray<SkDQuad> quads; CubicToQuads(cubic, precision, quads); if (!AlmostEqualUlps(cubic[0].fX, quads[0][0].fX) || !AlmostEqualUlps(cubic[0].fY, quads[0][0].fY)) { SkDebugf("[%d] unmatched start\n", static_cast<int>(index)); REPORTER_ASSERT(reporter, 0); } int last = quads.count() - 1; if (!AlmostEqualUlps(cubic[3].fX, quads[last][2].fX) || !AlmostEqualUlps(cubic[3].fY, quads[last][2].fY)) { SkDebugf("[%d] unmatched end\n", static_cast<int>(index)); REPORTER_ASSERT(reporter, 0); } } }
static void LineParameterTest(skiatest::Reporter* reporter) { for (size_t index = 0; index < tests_count; ++index) { SkLineParameters lineParameters; const SkDCubic& cubic = tests[index]; lineParameters.cubicEndPoints(cubic); double denormalizedDistance[2]; denormalizedDistance[0] = lineParameters.controlPtDistance(cubic, 1); denormalizedDistance[1] = lineParameters.controlPtDistance(cubic, 2); double normalSquared = lineParameters.normalSquared(); size_t inner; for (inner = 0; inner < 2; ++inner) { double distSq = denormalizedDistance[inner]; distSq *= distSq; double answersSq = answers[index][inner]; answersSq *= answersSq; if (AlmostEqualUlps(distSq, normalSquared * answersSq)) { continue; } SkDebugf("%s [%d,%d] denormalizedDistance:%g != answer:%g" " distSq:%g answerSq:%g normalSquared:%g\n", __FUNCTION__, static_cast<int>(index), (int)inner, denormalizedDistance[inner], answers[index][inner], distSq, answersSq, normalSquared); } lineParameters.normalize(); double normalizedDistance[2]; normalizedDistance[0] = lineParameters.controlPtDistance(cubic, 1); normalizedDistance[1] = lineParameters.controlPtDistance(cubic, 2); for (inner = 0; inner < 2; ++inner) { if (AlmostEqualUlps(fabs(normalizedDistance[inner]), answers[index][inner])) { continue; } SkDebugf("%s [%d,%d] normalizedDistance:%1.10g != answer:%g\n", __FUNCTION__, static_cast<int>(index), (int)inner, normalizedDistance[inner], answers[index][inner]); REPORTER_ASSERT(reporter, 0); } } }
static int horizontal_coincident(const SkDLine& line, double y) { double min = line[0].fY; double max = line[1].fY; if (min > max) { SkTSwap(min, max); } if (min > y || max < y) { return 0; } if (AlmostEqualUlps(min, max) && max - min < fabs(line[0].fX - line[1].fX)) { return 2; } return 1; }
static int vertical_coincident(const SkDLine& line, double x) { double min = line[0].fX; double max = line[1].fX; if (min > max) { SkTSwap(min, max); } if (!precisely_between(min, x, max)) { return 0; } if (AlmostEqualUlps(min, max)) { return 2; } return 1; }
int SkIntersections::horizontal(const SkDLine& line, double y) { double min = line[0].fY; double max = line[1].fY; if (min > max) { SkTSwap(min, max); } if (min > y || max < y) { return fUsed = 0; } if (AlmostEqualUlps(min, max)) { fT[0][0] = 0; fT[0][1] = 1; return fUsed = 2; } fT[0][0] = (y - line[0].fY) / (line[1].fY - line[0].fY); return fUsed = 1; }
int SkIntersections::vertical(const SkDLine& line, double x) { double min = line[0].fX; double max = line[1].fX; if (min > max) { SkTSwap(min, max); } if (!precisely_between(min, x, max)) { return fUsed = 0; } if (AlmostEqualUlps(min, max)) { fT[0][0] = 0; fT[0][1] = 1; return fUsed = 2; } fT[0][0] = (x - line[0].fX) / (line[1].fX - line[0].fX); return fUsed = 1; }
int SkIntersections::intersectRay(const SkDLine& a, const SkDLine& b) { fMax = 2; SkDVector aLen = a[1] - a[0]; SkDVector bLen = b[1] - b[0]; /* Slopes match when denom goes to zero: axLen / ayLen == bxLen / byLen (ayLen * byLen) * axLen / ayLen == (ayLen * byLen) * bxLen / byLen byLen * axLen == ayLen * bxLen byLen * axLen - ayLen * bxLen == 0 ( == denom ) */ double denom = bLen.fY * aLen.fX - aLen.fY * bLen.fX; SkDVector ab0 = a[0] - b[0]; double numerA = ab0.fY * bLen.fX - bLen.fY * ab0.fX; double numerB = ab0.fY * aLen.fX - aLen.fY * ab0.fX; #if 0 if (!between(0, numerA, denom) || !between(0, numerB, denom)) { fUsed = 0; return 0; } #endif numerA /= denom; numerB /= denom; int used; if (!approximately_zero(denom)) { fT[0][0] = numerA; fT[1][0] = numerB; used = 1; } else { /* See if the axis intercepts match: ay - ax * ayLen / axLen == by - bx * ayLen / axLen axLen * (ay - ax * ayLen / axLen) == axLen * (by - bx * ayLen / axLen) axLen * ay - ax * ayLen == axLen * by - bx * ayLen */ if (!AlmostEqualUlps(aLen.fX * a[0].fY - aLen.fY * a[0].fX, aLen.fX * b[0].fY - aLen.fY * b[0].fX)) { return fUsed = 0; } // there's no great answer for intersection points for coincident rays, but return something fT[0][0] = fT[1][0] = 0; fT[1][0] = fT[1][1] = 1; used = 2; } computePoints(a, used); return fUsed; }
/* y<0 y==0 y>0 x<0 x==0 x>0 xy<0 xy==0 xy>0 0 x x x 1 x x x 2 x x x 3 x x x 4 x x x 5 x x x 6 x x x 7 x x x 8 x x x 9 x x x 10 x x x 11 x x x 12 x x x 13 x x x 14 x x x 15 x x x */ int SkOpAngle::findSector(SkPath::Verb verb, double x, double y) const { double absX = fabs(x); double absY = fabs(y); double xy = SkPath::kLine_Verb == verb || !AlmostEqualUlps(absX, absY) ? absX - absY : 0; // If there are four quadrants and eight octants, and since the Latin for sixteen is sedecim, // one could coin the term sedecimant for a space divided into 16 sections. // http://english.stackexchange.com/questions/133688/word-for-something-partitioned-into-16-parts static const int sedecimant[3][3][3] = { // y<0 y==0 y>0 // x<0 x==0 x>0 x<0 x==0 x>0 x<0 x==0 x>0 {{ 4, 3, 2}, { 7, -1, 15}, {10, 11, 12}}, // abs(x) < abs(y) {{ 5, -1, 1}, {-1, -1, -1}, { 9, -1, 13}}, // abs(x) == abs(y) {{ 6, 3, 0}, { 7, -1, 15}, { 8, 11, 14}}, // abs(x) > abs(y) }; int sector = sedecimant[(xy >= 0) + (xy > 0)][(y >= 0) + (y > 0)][(x >= 0) + (x > 0)] * 2 + 1; SkASSERT(SkPath::kLine_Verb == verb || sector >= 0); return sector; }
// given a line, see if the opposite curve's convex hull is all on one side // returns -1=not on one side 0=this CW of test 1=this CCW of test int SkOpAngle::allOnOneSide(const SkOpAngle& test) const { SkASSERT(!fIsCurve); SkASSERT(test.fIsCurve); const SkDPoint& origin = test.fCurvePart[0]; SkVector line; if (fSegment->verb() == SkPath::kLine_Verb) { const SkPoint* linePts = fSegment->pts(); int lineStart = fStart < fEnd ? 0 : 1; line = linePts[lineStart ^ 1] - linePts[lineStart]; } else { SkPoint shortPts[2] = { fCurvePart[0].asSkPoint(), fCurvePart[1].asSkPoint() }; line = shortPts[1] - shortPts[0]; } float crosses[3]; SkPath::Verb testVerb = test.fSegment->verb(); int iMax = SkPathOpsVerbToPoints(testVerb); // SkASSERT(origin == test.fCurveHalf[0]); const SkDCubic& testCurve = test.fCurvePart; // do { for (int index = 1; index <= iMax; ++index) { float xy1 = (float) (line.fX * (testCurve[index].fY - origin.fY)); float xy2 = (float) (line.fY * (testCurve[index].fX - origin.fX)); crosses[index - 1] = AlmostEqualUlps(xy1, xy2) ? 0 : xy1 - xy2; } if (crosses[0] * crosses[1] < 0) { return -1; } if (SkPath::kCubic_Verb == testVerb) { if (crosses[0] * crosses[2] < 0 || crosses[1] * crosses[2] < 0) { return -1; } } if (crosses[0]) { return crosses[0] < 0; } if (crosses[1]) { return crosses[1] < 0; } if (SkPath::kCubic_Verb == testVerb && crosses[2]) { return crosses[2] < 0; } fUnorderable = true; return -1; }
static bool checkEndPoint(double x, double y, const SkDLine& l, double* tPtr, int useX) { if (!between(l[0].fX, x, l[1].fX) || !between(l[0].fY, y, l[1].fY)) { return false; } double xLen = l[1].fX - l[0].fX; double yLen = l[1].fY - l[0].fY; if (useX < 0) { useX = SkTAbs(xLen) > SkTAbs(yLen); } // OPTIMIZATION: do between test before divide double t = useX ? (x - l[0].fX) / xLen : (y - l[0].fY) / yLen; if (!between(0, t, 1)) { return false; } double opp = useX ? (1 - t) * l[0].fY + t * l[1].fY : (1 - t) * l[0].fX + t * l[1].fX; if (!AlmostEqualUlps(opp, useX ? y : x)) { return false; } *tPtr = t; return true; }
int SkIntersections::intersectRay(const SkDLine& a, const SkDLine& b) { double axLen = a[1].fX - a[0].fX; double ayLen = a[1].fY - a[0].fY; double bxLen = b[1].fX - b[0].fX; double byLen = b[1].fY - b[0].fY; /* Slopes match when denom goes to zero: axLen / ayLen == bxLen / byLen (ayLen * byLen) * axLen / ayLen == (ayLen * byLen) * bxLen / byLen byLen * axLen == ayLen * bxLen byLen * axLen - ayLen * bxLen == 0 ( == denom ) */ double denom = byLen * axLen - ayLen * bxLen; double ab0y = a[0].fY - b[0].fY; double ab0x = a[0].fX - b[0].fX; double numerA = ab0y * bxLen - byLen * ab0x; double numerB = ab0y * axLen - ayLen * ab0x; numerA /= denom; numerB /= denom; int used; if (!approximately_zero(denom)) { fT[0][0] = numerA; fT[1][0] = numerB; used = 1; } else { /* See if the axis intercepts match: ay - ax * ayLen / axLen == by - bx * ayLen / axLen axLen * (ay - ax * ayLen / axLen) == axLen * (by - bx * ayLen / axLen) axLen * ay - ax * ayLen == axLen * by - bx * ayLen */ if (!AlmostEqualUlps(axLen * a[0].fY - ayLen * a[0].fX, axLen * b[0].fY - ayLen * b[0].fX)) { return fUsed = 0; } // there's no great answer for intersection points for coincident rays, but return something fT[0][0] = fT[1][0] = 0; fT[1][0] = fT[1][1] = 1; used = 2; } return computePoints(a, used); }
double SkDLine::NearPointV(const SkDPoint& xy, double top, double bottom, double x) { if (!AlmostBequalUlps(xy.fX, x)) { return -1; } if (!AlmostBetweenUlps(top, xy.fY, bottom)) { return -1; } double t = (xy.fY - top) / (bottom - top); t = SkPinT(t); SkASSERT(between(0, t, 1)); double realPtY = (1 - t) * top + t * bottom; SkDVector distU = {xy.fX - x, xy.fY - realPtY}; double distSq = distU.fX * distU.fX + distU.fY * distU.fY; double dist = sqrt(distSq); // OPTIMIZATION: can we compare against distSq instead ? double tiniest = SkTMin(SkTMin(x, top), bottom); double largest = SkTMax(SkTMax(x, top), bottom); largest = SkTMax(largest, -tiniest); if (!AlmostEqualUlps(largest, largest + dist)) { // is the dist within ULPS tolerance? return -1; } return t; }
double SkDLine::NearPointH(const SkDPoint& xy, double left, double right, double y) { if (!AlmostBequalUlps(xy.fY, y)) { return -1; } if (!AlmostBetweenUlps(left, xy.fX, right)) { return -1; } double t = (xy.fX - left) / (right - left); t = SkPinT(t); SkASSERT(between(0, t, 1)); double realPtX = (1 - t) * left + t * right; SkDVector distU = {xy.fY - y, xy.fX - realPtX}; double distSq = distU.fX * distU.fX + distU.fY * distU.fY; double dist = sqrt(distSq); // OPTIMIZATION: can we compare against distSq instead ? double tiniest = SkTMin(SkTMin(y, left), right); double largest = SkTMax(SkTMax(y, left), right); largest = SkTMax(largest, -tiniest); if (!AlmostEqualUlps(largest, largest + dist)) { // is the dist within ULPS tolerance? return -1; } return t; }
int quarticRootsReal(int firstCubicRoot, const double A, const double B, const double C, const double D, const double E, double s[4]) { double u, v; /* normal form: x^4 + Ax^3 + Bx^2 + Cx + D = 0 */ const double invA = 1 / A; const double a = B * invA; const double b = C * invA; const double c = D * invA; const double d = E * invA; /* substitute x = y - a/4 to eliminate cubic term: x^4 + px^2 + qx + r = 0 */ const double a2 = a * a; const double p = -3 * a2 / 8 + b; const double q = a2 * a / 8 - a * b / 2 + c; const double r = -3 * a2 * a2 / 256 + a2 * b / 16 - a * c / 4 + d; int num; if (approximately_zero(r)) { /* no absolute term: y(y^3 + py + q) = 0 */ num = cubicRootsReal(1, 0, p, q, s); s[num++] = 0; } else { /* solve the resolvent cubic ... */ double cubicRoots[3]; int roots = cubicRootsReal(1, -p / 2, -r, r * p / 2 - q * q / 8, cubicRoots); int index; #if 0 && SK_DEBUG // enable to verify that any cubic root is as good as any other double tries[3][4]; int nums[3]; for (index = 0; index < roots; ++index) { /* ... and take one real solution ... */ const double z = cubicRoots[index]; /* ... to build two quadric equations */ u = z * z - r; v = 2 * z - p; if (approximately_zero_squared(u)) { u = 0; } else if (u > 0) { u = sqrt(u); } else { SkDebugf("%s u=%1.9g <0\n", __FUNCTION__, u); continue; } if (approximately_zero_squared(v)) { v = 0; } else if (v > 0) { v = sqrt(v); } else { SkDebugf("%s v=%1.9g <0\n", __FUNCTION__, v); continue; } nums[index] = quadraticRootsReal(1, q < 0 ? -v : v, z - u, tries[index]); nums[index] += quadraticRootsReal(1, q < 0 ? v : -v, z + u, tries[index] + nums[index]); /* resubstitute */ const double sub = a / 4; for (int i = 0; i < nums[index]; ++i) { tries[index][i] -= sub; } } for (index = 0; index < roots; ++index) { SkDebugf("%s", __FUNCTION__); for (int idx2 = 0; idx2 < nums[index]; ++idx2) { SkDebugf(" %1.9g", tries[index][idx2]); } SkDebugf("\n"); } #endif /* ... and take one real solution ... */ double z; num = 0; int num2 = 0; for (index = firstCubicRoot; index < roots; ++index) { z = cubicRoots[index]; /* ... to build two quadric equations */ u = z * z - r; v = 2 * z - p; if (approximately_zero_squared(u)) { u = 0; } else if (u > 0) { u = sqrt(u); } else { continue; } if (approximately_zero_squared(v)) { v = 0; } else if (v > 0) { v = sqrt(v); } else { continue; } num = quadraticRootsReal(1, q < 0 ? -v : v, z - u, s); num2 = quadraticRootsReal(1, q < 0 ? v : -v, z + u, s + num); if (!((num | num2) & 1)) { break; // prefer solutions without single quad roots } } num += num2; if (!num) { return 0; // no valid cubic root } } /* resubstitute */ const double sub = a / 4; for (int i = 0; i < num; ++i) { s[i] -= sub; } // eliminate duplicates for (int i = 0; i < num - 1; ++i) { for (int j = i + 1; j < num; ) { if (AlmostEqualUlps(s[i], s[j])) { if (j < --num) { s[j] = s[num]; } } else { ++j; } } } return num; }
bool intersect(double minT1, double maxT1, double minT2, double maxT2) { bool t1IsLine = maxT1 - minT1 <= quad1Divisions; bool t2IsLine = maxT2 - minT2 <= quad2Divisions; if (t1IsLine | t2IsLine) { return intersectAsLine(minT1, maxT1, minT2, maxT2, t1IsLine, t2IsLine); } Quadratic smaller, larger; // FIXME: carry last subdivide and reduceOrder result with quad sub_divide(quad1, minT1, maxT1, intersections.swapped() ? larger : smaller); sub_divide(quad2, minT2, maxT2, intersections.swapped() ? smaller : larger); double minT, maxT; if (!bezier_clip(smaller, larger, minT, maxT)) { if (approximately_equal(minT, maxT)) { double smallT, largeT; _Point q2pt, q1pt; if (intersections.swapped()) { largeT = interp(minT2, maxT2, minT); xy_at_t(quad2, largeT, q2pt.x, q2pt.y); xy_at_t(quad1, minT1, q1pt.x, q1pt.y); if (AlmostEqualUlps(q2pt.x, q1pt.x) && AlmostEqualUlps(q2pt.y, q1pt.y)) { smallT = minT1; } else { xy_at_t(quad1, maxT1, q1pt.x, q1pt.y); // FIXME: debug code assert(AlmostEqualUlps(q2pt.x, q1pt.x) && AlmostEqualUlps(q2pt.y, q1pt.y)); smallT = maxT1; } } else { smallT = interp(minT1, maxT1, minT); xy_at_t(quad1, smallT, q1pt.x, q1pt.y); xy_at_t(quad2, minT2, q2pt.x, q2pt.y); if (AlmostEqualUlps(q2pt.x, q1pt.x) && AlmostEqualUlps(q2pt.y, q1pt.y)) { largeT = minT2; } else { xy_at_t(quad2, maxT2, q2pt.x, q2pt.y); // FIXME: debug code assert(AlmostEqualUlps(q2pt.x, q1pt.x) && AlmostEqualUlps(q2pt.y, q1pt.y)); largeT = maxT2; } } intersections.add(smallT, largeT); return true; } return false; } int split; if (intersections.swapped()) { double newMinT1 = interp(minT1, maxT1, minT); double newMaxT1 = interp(minT1, maxT1, maxT); split = (newMaxT1 - newMinT1 > (maxT1 - minT1) * tClipLimit) << 1; #define VERBOSE 0 #if VERBOSE printf("%s d=%d s=%d new1=(%g,%g) old1=(%g,%g) split=%d\n", __FUNCTION__, depth, splits, newMinT1, newMaxT1, minT1, maxT1, split); #endif minT1 = newMinT1; maxT1 = newMaxT1; } else { double newMinT2 = interp(minT2, maxT2, minT); double newMaxT2 = interp(minT2, maxT2, maxT); split = newMaxT2 - newMinT2 > (maxT2 - minT2) * tClipLimit; #if VERBOSE printf("%s d=%d s=%d new2=(%g,%g) old2=(%g,%g) split=%d\n", __FUNCTION__, depth, splits, newMinT2, newMaxT2, minT2, maxT2, split); #endif minT2 = newMinT2; maxT2 = newMaxT2; } return chop(minT1, maxT1, minT2, maxT2, split); }