static void oneOffTest() { for (size_t outer = 0; outer < testSetCount - 1; ++outer) { for (size_t inner = outer + 1; inner < testSetCount; ++inner) { const Quadratic& quad1 = testSet[outer]; const Quadratic& quad2 = testSet[inner]; double tt1, tt2; Intersections intersections2; intersect2(quad1, quad2, intersections2); for (int pt = 0; pt < intersections2.used(); ++pt) { tt1 = intersections2.fT[0][pt]; double tx1, ty1; xy_at_t(quad1, tt1, tx1, ty1); int pt2 = intersections2.fFlip ? intersections2.used() - pt - 1 : pt; tt2 = intersections2.fT[1][pt2]; double tx2, ty2; xy_at_t(quad2, tt2, tx2, ty2); if (!approximately_equal(tx1, tx2)) { SkDebugf("%s [%d,%d] x!= t1=%g (%g,%g) t2=%g (%g,%g)\n", __FUNCTION__, (int)index, pt, tt1, tx1, ty1, tt2, tx2, ty2); SkASSERT(0); } if (!approximately_equal(ty1, ty2)) { SkDebugf("%s [%d,%d] y!= t1=%g (%g,%g) t2=%g (%g,%g)\n", __FUNCTION__, (int)index, pt, tt1, tx1, ty1, tt2, tx2, ty2); SkASSERT(0); } SkDebugf("%s [%d][%d] t1=%1.9g (%1.9g, %1.9g) t2=%1.9g\n", __FUNCTION__, outer, inner, tt1, tx1, tx2, tt2); } } } }
// from SkGeometry.cpp (and Numeric Solutions, 5.6) int SkDCubic::RootsValidT(double A, double B, double C, double D, double t[3]) { double s[3]; int realRoots = RootsReal(A, B, C, D, s); int foundRoots = SkDQuad::AddValidTs(s, realRoots, t); for (int index = 0; index < realRoots; ++index) { double tValue = s[index]; if (!approximately_one_or_less(tValue) && between(1, tValue, 1.00005)) { for (int idx2 = 0; idx2 < foundRoots; ++idx2) { if (approximately_equal(t[idx2], 1)) { goto nextRoot; } } SkASSERT(foundRoots < 3); t[foundRoots++] = 1; } else if (!approximately_zero_or_more(tValue) && between(-0.00005, tValue, 0)) { for (int idx2 = 0; idx2 < foundRoots; ++idx2) { if (approximately_equal(t[idx2], 0)) { goto nextRoot; } } SkASSERT(foundRoots < 3); t[foundRoots++] = 0; } nextRoot: ; } return foundRoots; }
// 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; }
static void quadraticTest() { // (x - a)(x - b) == x^2 - (a + b)x + ab for (size_t aIndex = 0; aIndex < mulACount; ++aIndex) { for (size_t bIndex = 0; bIndex < rootBCount; ++bIndex) { for (size_t cIndex = 0; cIndex < rootCCount; ++cIndex) { const double A = mulA[aIndex]; const double B = rootB[bIndex]; const double C = rootC[cIndex]; const double b = A * (B + C); const double c = A * B * C; double roots[2]; const int rootCount = QuarticRootTest::quadraticRootsX(A, b, c, roots); const int expected = 1 + (B != C); assert(rootCount == expected); assert(approximately_equal(roots[0], -B) || approximately_equal(roots[0], -C)); if (B != C) { assert(!approximately_equal(roots[0], roots[1])); assert(approximately_equal(roots[1], -B) || approximately_equal(roots[1], -C)); } } } } }
void check_identity(float m[4][4]) { for (int i=0; i<4; ++i) { for (int j=0; j<4; ++j) { if (i==j) { BOOST_REQUIRE(approximately_equal(m[i][j], 1.f)); } else { BOOST_REQUIRE(approximately_equal(m[i][j], 0.f)); } } } }
static void coincidentTest() { for (size_t testIndex = 0; testIndex < coincidentTestSetCount - 1; testIndex += 2) { const Quadratic& quad1 = coincidentTestSet[testIndex]; const Quadratic& quad2 = coincidentTestSet[testIndex + 1]; Intersections intersections2; intersect2(quad1, quad2, intersections2); SkASSERT(intersections2.coincidentUsed() == 2); for (int pt = 0; pt < intersections2.coincidentUsed(); ++pt) { double tt1 = intersections2.fT[0][pt]; double tt2 = intersections2.fT[1][pt]; SkASSERT(approximately_equal(1, tt1) || approximately_zero(tt1)); SkASSERT(approximately_equal(1, tt2) || approximately_zero(tt2)); } } }
// FIXME: this test no longer valid -- does not take minimum scale contribution into account void CubicIntersection_ComputeDeltaTest() { SkASSERT(deltaTestSetLen == deltaTestSetTLen); SkASSERT(expectedTLen == deltaTestSetTLen); for (size_t index = 0; index < deltaTestSetLen; index += 2) { const Cubic& c1 = deltaTestSet[index]; const Cubic& c2 = deltaTestSet[index + 1]; double t1 = deltaTestSetT[index]; double t2 = deltaTestSetT[index + 1]; double d1, d2; computeDelta(c1, t1, 1, c2, t2, 1, d1, d2); SkASSERT(approximately_equal(t1 + d1, expectedT[index]) || approximately_equal(t1 - d1, expectedT[index])); SkASSERT(approximately_equal(t2 + d2, expectedT[index + 1]) || approximately_equal(t2 - d2, expectedT[index + 1])); } }
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 bool orderTRange(skiatest::Reporter* reporter, const SkDQuad& quad1, const SkDQuad& quad2, double r, TRange* result) { SkTArray<double, false> t1Array, t2Array; orderQuads(reporter, quad1, r, &t1Array); orderQuads(reporter,quad2, r, &t2Array); if (!t1Array.count() || !t2Array.count()) { return false; } SkTQSort<double>(t1Array.begin(), t1Array.end() - 1); SkTQSort<double>(t2Array.begin(), t2Array.end() - 1); double t1 = result->tMin1 = t1Array[0]; double t2 = result->tMin2 = t2Array[0]; double a1 = quadAngle(reporter,quad1, t1); double a2 = quadAngle(reporter,quad2, t2); if (approximately_equal(a1, a2)) { return false; } bool refCCW = angleDirection(a1, a2); result->t1 = t1; result->t2 = t2; result->tMin = SkTMin(t1, t2); result->a1 = a1; result->a2 = a2; result->ccw = refCCW; return true; }
void x_at(const _Point& p1, const _Point& p2, double top, double bottom, int flags, double& minX, double& maxX) { if (approximately_equal(p1.y, p2.y)) { // It should be OK to bail early in this case. There's another edge // which shares this end point which can intersect without failing to // have a slope ... maybe return; } // p2.x is always greater than p1.x -- the part of points (p1, p2) are // moving from the start of the cubic towards its end. // if p1.y < p2.y, minX can be affected // if p1.y > p2.y, maxX can be affected double slope = (p2.x - p1.x) / (p2.y - p1.y); int topFlags = flags & (kFindTopMin | kFindTopMax); if (topFlags && ((top <= p1.y && top >= p2.y) || (top >= p1.y && top <= p2.y))) { double x = p1.x + (top - p1.y) * slope; setMinMax(x, topFlags, minX, maxX); } int bottomFlags = flags & (kFindBottomMin | kFindBottomMax); if (bottomFlags && ((bottom <= p1.y && bottom >= p2.y) || (bottom >= p1.y && bottom <= p2.y))) { double x = p1.x + (bottom - p1.y) * slope; setMinMax(x, bottomFlags, minX, maxX); } }
static void PathOpsDVectorTest(skiatest::Reporter* reporter) { for (size_t index = 0; index < tests_count - 1; ++index) { SkDVector v1 = tests[index + 1] - tests[index]; SkDVector v2 = tests[index] - tests[index + 1]; v1 += v2; REPORTER_ASSERT(reporter, v1.fX == 0 && v1.fY == 0); SkDPoint p = tests[index + 1] + v2; REPORTER_ASSERT(reporter, p == tests[index]); v2 -= v2; REPORTER_ASSERT(reporter, v2.fX == 0 && v2.fY == 0); v1 = tests[index + 1] - tests[index]; v1 /= 2; v1 *= 2; v1 -= tests[index + 1] - tests[index]; REPORTER_ASSERT(reporter, v1.fX == 0 && v1.fY == 0); SkVector sv = v1.asSkVector(); REPORTER_ASSERT(reporter, sv.fX == 0 && sv.fY == 0); v1 = tests[index + 1] - tests[index]; double lenSq = v1.lengthSquared(); double v1Dot = v1.dot(v1); REPORTER_ASSERT(reporter, lenSq == v1Dot); REPORTER_ASSERT(reporter, approximately_equal(sqrt(lenSq), v1.length())); double v1Cross = v1.cross(v1); REPORTER_ASSERT(reporter, v1Cross == 0); } }
/* check if \m is approximately equal to \r */ void check_matrix(float m[4][4], float r[4][4]) { for (int i=0; i<4; ++i) { for (int j=0; j<4; ++j) { BOOST_REQUIRE(approximately_equal(m[i][j], r[i][j])); } } }
void check_zero(float m[4][4]) { for (int i=0; i<4; ++i) { for (int j=0; j<4; ++j) { BOOST_REQUIRE(approximately_equal(m[i][j], 0.f)); } } }
DEF_TEST(PathOpsDPoint, reporter) { for (size_t index = 0; index < tests_count; ++index) { const SkDPoint& pt = tests[index]; SkASSERT(ValidPoint(pt)); SkDPoint p = pt; REPORTER_ASSERT(reporter, p == pt); REPORTER_ASSERT(reporter, !(pt != pt)); SkDVector v = p - pt; p += v; REPORTER_ASSERT(reporter, p == pt); p -= v; REPORTER_ASSERT(reporter, p == pt); REPORTER_ASSERT(reporter, p.approximatelyEqual(pt)); SkPoint sPt = pt.asSkPoint(); p.set(sPt); REPORTER_ASSERT(reporter, p == pt); REPORTER_ASSERT(reporter, p.approximatelyEqual(sPt)); REPORTER_ASSERT(reporter, p.roughlyEqual(pt)); p.fX = p.fY = 0; REPORTER_ASSERT(reporter, p.fX == 0 && p.fY == 0); REPORTER_ASSERT(reporter, p.approximatelyZero()); REPORTER_ASSERT(reporter, pt.distanceSquared(p) == pt.fX * pt.fX + pt.fY * pt.fY); REPORTER_ASSERT(reporter, approximately_equal(pt.distance(p), sqrt(pt.fX * pt.fX + pt.fY * pt.fY))); } }
static void orderQuads(skiatest::Reporter* reporter, const SkDQuad& quad, double radius, SkTArray<double, false>* tArray) { double r = radius; double s = r * SK_ScalarTanPIOver8; double m = r * SK_ScalarRoot2Over2; // construct circle from quads const SkDQuad circle[8] = {{{{ r, 0}, { r, -s}, { m, -m}}}, {{{ m, -m}, { s, -r}, { 0, -r}}}, {{{ 0, -r}, {-s, -r}, {-m, -m}}}, {{{-m, -m}, {-r, -s}, {-r, 0}}}, {{{-r, 0}, {-r, s}, {-m, m}}}, {{{-m, m}, {-s, r}, { 0, r}}}, {{{ 0, r}, { s, r}, { m, m}}}, {{{ m, m}, { r, s}, { r, 0}}}}; for (int octant = 0; octant < 8; ++octant) { double t = testArc(reporter, quad, circle[octant], octant); if (t < 0) { continue; } for (int index = 0; index < tArray->count(); ++index) { double matchT = (*tArray)[index]; if (approximately_equal(t, matchT)) { goto next; } } tArray->push_back(t); next: ; } }
// 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 Cubic& cubic, Cubic& reduction, ReduceOrder_Flags allowQuadratics) { int index, minX, maxX, minY, maxY; int minXSet, minYSet; minX = maxX = minY = maxY = 0; minXSet = minYSet = 0; for (index = 1; index < 4; ++index) { if (cubic[minX].x > cubic[index].x) { minX = index; } if (cubic[minY].y > cubic[index].y) { minY = index; } if (cubic[maxX].x < cubic[index].x) { maxX = index; } if (cubic[maxY].y < cubic[index].y) { maxY = index; } } for (index = 0; index < 4; ++index) { if (approximately_equal(cubic[index].x, cubic[minX].x)) { minXSet |= 1 << index; } if (approximately_equal(cubic[index].y, cubic[minY].y)) { minYSet |= 1 << index; } } if (minXSet == 0xF) { // test for vertical line if (minYSet == 0xF) { // return 1 if all four are coincident return coincident_line(cubic, reduction); } return vertical_line(cubic, reduction); } if (minYSet == 0xF) { // test for horizontal line return horizontal_line(cubic, reduction); } int result = check_linear(cubic, reduction, minX, maxX, minY, maxY); if (result) { return result; } if (allowQuadratics && (result = check_quadratic(cubic, reduction))) { return result; } memcpy(reduction, cubic, sizeof(Cubic)); return 4; }
static void standardTestCases() { for (size_t index = firstQuadIntersectionTest; index < quadraticTests_count; ++index) { const Quadratic& quad1 = quadraticTests[index][0]; const Quadratic& quad2 = quadraticTests[index][1]; Quadratic reduce1, reduce2; int order1 = reduceOrder(quad1, reduce1); int order2 = reduceOrder(quad2, reduce2); if (order1 < 3) { printf("[%d] quad1 order=%d\n", (int) index, order1); } if (order2 < 3) { printf("[%d] quad2 order=%d\n", (int) index, order2); } if (order1 == 3 && order2 == 3) { Intersections intersections, intersections2; intersect(reduce1, reduce2, intersections); intersect2(reduce1, reduce2, intersections2); SkASSERT(intersections.used() == intersections2.used()); if (intersections.intersected()) { for (int pt = 0; pt < intersections.used(); ++pt) { double tt1 = intersections.fT[0][pt]; double tx1, ty1; xy_at_t(quad1, tt1, tx1, ty1); double tt2 = intersections.fT[1][pt]; double tx2, ty2; xy_at_t(quad2, tt2, tx2, ty2); if (!approximately_equal(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 (!approximately_equal(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); } tt1 = intersections2.fT[0][pt]; SkASSERT(approximately_equal(intersections.fT[0][pt], tt1)); tt2 = intersections2.fT[1][pt]; SkASSERT(approximately_equal(intersections.fT[1][pt], tt2)); } } } } }
// 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 (!approximately_equal(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 (!approximately_equal(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 (approximately_equal(quad[index].x, quad[minX].x)) { minXSet |= 1 << index; } if (approximately_equal(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; }
int SkReducedQuarticRoots(const double t4, const double t3, const double t2, const double t1, const double t0, const bool oneHint, double roots[4]) { #ifdef SK_DEBUG // create a string mathematica understands // GDB set print repe 15 # if repeated digits is a bother // set print elements 400 # if line doesn't fit char str[1024]; sk_bzero(str, sizeof(str)); SK_SNPRINTF(str, sizeof(str), "Solve[%1.19g x^4 + %1.19g x^3 + %1.19g x^2 + %1.19g x + %1.19g == 0, x]", t4, t3, t2, t1, t0); SkPathOpsDebug::MathematicaIze(str, sizeof(str)); #if ONE_OFF_DEBUG && ONE_OFF_DEBUG_MATHEMATICA SkDebugf("%s\n", str); #endif #endif if (approximately_zero_when_compared_to(t4, t0) // 0 is one root && approximately_zero_when_compared_to(t4, t1) && approximately_zero_when_compared_to(t4, t2)) { if (approximately_zero_when_compared_to(t3, t0) && approximately_zero_when_compared_to(t3, t1) && approximately_zero_when_compared_to(t3, t2)) { return SkDQuad::RootsReal(t2, t1, t0, roots); } if (approximately_zero_when_compared_to(t4, t3)) { return SkDCubic::RootsReal(t3, t2, t1, t0, roots); } } if ((approximately_zero_when_compared_to(t0, t1) || approximately_zero(t1)) // 0 is one root // && approximately_zero_when_compared_to(t0, t2) && approximately_zero_when_compared_to(t0, t3) && approximately_zero_when_compared_to(t0, t4)) { int num = SkDCubic::RootsReal(t4, t3, t2, t1, roots); for (int i = 0; i < num; ++i) { if (approximately_zero(roots[i])) { return num; } } roots[num++] = 0; return num; } if (oneHint) { SkASSERT(approximately_zero_double(t4 + t3 + t2 + t1 + t0)); // 1 is one root // note that -C == A + B + D + E int num = SkDCubic::RootsReal(t4, t4 + t3, -(t1 + t0), -t0, roots); for (int i = 0; i < num; ++i) { if (approximately_equal(roots[i], 1)) { return num; } } roots[num++] = 1; return num; } return -1; }
static bool tiny(const Cubic& cubic) { int index, minX, maxX, minY, maxY; minX = maxX = minY = maxY = 0; for (index = 1; index < 4; ++index) { if (cubic[minX].x > cubic[index].x) { minX = index; } if (cubic[minY].y > cubic[index].y) { minY = index; } if (cubic[maxX].x < cubic[index].x) { maxX = index; } if (cubic[maxY].y < cubic[index].y) { maxY = index; } } return approximately_equal(cubic[maxX].x, cubic[minX].x) && approximately_equal(cubic[maxY].y, cubic[minY].y); }
static bool check_quadratic(const Cubic& cubic, Quadratic& reduction) { float dx10 = cubic[1].x - cubic[0].x; float dx23 = cubic[2].x - cubic[3].x; float midX = cubic[0].x + dx10 * 3 / 2; //NOTE: !approximately_equal(midX - cubic[3].x, dx23 * 3 / 2) //does not work as subnormals get in between the left side and 0. if (!approximately_equal(midX, (dx23 * 3 / 2) + cubic[3].x)) { return false; } float dy10 = cubic[1].y - cubic[0].y; float dy23 = cubic[2].y - cubic[3].y; float midY = cubic[0].y + dy10 * 3 / 2; if (!approximately_equal(midY, (dy23 * 3 / 2) + cubic[3].y)) { return false; } reduction[0] = cubic[0]; reduction[1].x = midX; reduction[1].y = midY; reduction[2] = cubic[3]; return true; }
static void quadraticTest(bool limit) { // (x - a)(x - b) == x^2 - (a + b)x + ab for (size_t aIndex = 0; aIndex < mulACount; ++aIndex) { for (size_t bIndex = 0; bIndex < rootBCount; ++bIndex) { for (size_t cIndex = 0; cIndex < rootCCount; ++cIndex) { const double A = mulA[aIndex]; double B = rootB[bIndex]; double C = rootC[cIndex]; if (limit) { B = (B - 6) / 12; C = (C - 6) / 12; } const double b = A * (B + C); const double c = A * B * C; double roots[2]; const int rootCount = limit ? quadraticRootsValidT(A, b, c, roots) : quadraticRootsReal(A, b, c, roots); int expected; if (limit) { expected = B <= 0 && B >= -1; expected += B != C && C <= 0 && C >= -1; } else { expected = 1 + (B != C); } SkASSERT(rootCount == expected); if (!rootCount) { continue; } SkASSERT(approximately_equal(roots[0], -B) || approximately_equal(roots[0], -C)); if (expected > 1) { SkASSERT(!approximately_equal(roots[0], roots[1])); SkASSERT(approximately_equal(roots[1], -B) || approximately_equal(roots[1], -C)); } } } } }
void SkIntersections::cleanUpParallelLines(bool parallel) { while (fUsed > 2) { removeOne(1); } if (fUsed == 2 && !parallel) { bool startMatch = fT[0][0] == 0 || fT[1][0] == 0 || fT[1][0] == 1; bool endMatch = fT[0][1] == 1 || fT[1][1] == 0 || fT[1][1] == 1; if ((!startMatch && !endMatch) || approximately_equal(fT[0][0], fT[0][1])) { SkASSERT(startMatch || endMatch); removeOne(endMatch); } } }
void LineParameter_Test() { for (size_t index = firstLineParameterTest; index < tests_count; ++index) { LineParameters lineParameters; const Cubic& cubic = tests[index]; lineParameters.cubicEndPoints(cubic); double denormalizedDistance[2]; lineParameters.controlPtDistance(cubic, denormalizedDistance); 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 (approximately_equal(distSq, normalSquared * answersSq)) { continue; } printf("%s [%d,%d] denormalizedDistance:%g != answer:%g" " distSq:%g answerSq:%g normalSquared:%g\n", __FUNCTION__, (int)index, (int)inner, denormalizedDistance[inner], answers[index][inner], distSq, answersSq, normalSquared); } lineParameters.normalize(); double normalizedDistance[2]; lineParameters.controlPtDistance(cubic, normalizedDistance); for (inner = 0; inner < 2; ++inner) { if (approximately_equal(fabs(normalizedDistance[inner]), answers[index][inner])) { continue; } printf("%s [%d,%d] normalizedDistance:%1.10g != answer:%g\n", __FUNCTION__, (int)index, (int)inner, normalizedDistance[inner], answers[index][inner]); } } }
void LineCubicIntersection_Test() { for (size_t index = firstLineCubicIntersectionTest; index < lineCubicTests_count; ++index) { const Cubic& cubic = lineCubicTests[index].cubic; const _Line& line = lineCubicTests[index].line; Cubic reduce1; _Line reduce2; int order1 = reduceOrder(cubic, reduce1, kReduceOrder_NoQuadraticsAllowed); int order2 = reduceOrder(line, reduce2); if (order1 < 4) { printf("[%d] cubic order=%d\n", (int) index, order1); } if (order2 < 2) { printf("[%d] line order=%d\n", (int) index, order2); } if (order1 == 4 && order2 == 2) { double range1[2], range2[2]; int roots = intersect(reduce1, reduce2, range1, range2); for (int pt = 0; pt < roots; ++pt) { double tt1 = range1[pt]; double tx1, ty1; xy_at_t(cubic, tt1, tx1, ty1); double tt2 = range2[pt]; double tx2, ty2; xy_at_t(line, tt2, tx2, ty2); if (!approximately_equal(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 (!approximately_equal(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); } } } } }
static double verticalIntersect(const Quadratic& quad, const _Point& pt) { Intersections intersections; LineQuadraticIntersections q(quad, *((_Line*) 0), intersections); int result = q.horizontalIntersect(pt.x); if (result == 0) { return -1; } assert(result == 1); double x, y; xy_at_t(quad, intersections.fT[0][0], x, y); if (approximately_equal(y, pt.y)) { return intersections.fT[0][0]; } return -1; }
static int VerticalIntersect(const SkDCubic& c, double axisIntercept, double roots[3]) { double A, B, C, D; SkDCubic::Coefficients(&c[0].fX, &A, &B, &C, &D); D -= axisIntercept; int count = SkDCubic::RootsValidT(A, B, C, D, roots); for (int index = 0; index < count; ++index) { SkDPoint calcPt = c.ptAtT(roots[index]); if (!approximately_equal(calcPt.fX, axisIntercept)) { double extremeTs[6]; int extrema = SkDCubic::FindExtrema(&c[0].fX, extremeTs); count = c.searchRoots(extremeTs, extrema, axisIntercept, SkDCubic::kXAxis, roots); break; } } return count; }
// give up when changing t no longer moves point // also, copy point rather than recompute it when it does change double SkDCubic::binarySearch(double min, double max, double axisIntercept, SearchAxis xAxis) const { double t = (min + max) / 2; double step = (t - min) / 2; SkDPoint cubicAtT = ptAtT(t); double calcPos = (&cubicAtT.fX)[xAxis]; double calcDist = calcPos - axisIntercept; do { double priorT = t - step; SkOPASSERT(priorT >= min); SkDPoint lessPt = ptAtT(priorT); if (approximately_equal_half(lessPt.fX, cubicAtT.fX) && approximately_equal_half(lessPt.fY, cubicAtT.fY)) { return -1; // binary search found no point at this axis intercept } double lessDist = (&lessPt.fX)[xAxis] - axisIntercept; #if DEBUG_CUBIC_BINARY_SEARCH SkDebugf("t=%1.9g calc=%1.9g dist=%1.9g step=%1.9g less=%1.9g\n", t, calcPos, calcDist, step, lessDist); #endif double lastStep = step; step /= 2; if (calcDist > 0 ? calcDist > lessDist : calcDist < lessDist) { t = priorT; } else { double nextT = t + lastStep; if (nextT > max) { return -1; } SkDPoint morePt = ptAtT(nextT); if (approximately_equal_half(morePt.fX, cubicAtT.fX) && approximately_equal_half(morePt.fY, cubicAtT.fY)) { return -1; // binary search found no point at this axis intercept } double moreDist = (&morePt.fX)[xAxis] - axisIntercept; if (calcDist > 0 ? calcDist <= moreDist : calcDist >= moreDist) { continue; } t = nextT; } SkDPoint testAtT = ptAtT(t); cubicAtT = testAtT; calcPos = (&cubicAtT.fX)[xAxis]; calcDist = calcPos - axisIntercept; } while (!approximately_equal(calcPos, axisIntercept)); return t; }
// the original angle is too short to get meaningful sector information // lengthen it until it is long enough to be meaningful or leave it unset if lengthening it // would cause it to intersect one of the adjacent angles bool SkOpAngle::computeSector() { if (fComputedSector) { // FIXME: logically, this should return !fUnorderable, but doing so breaks testQuadratic51 // -- but in general, this code may not work so this may be the least of problems // adding the bang fixes testQuads46x in release, however return !fUnorderable; } SkASSERT(fSegment->verb() != SkPath::kLine_Verb && small()); fComputedSector = true; int step = fStart < fEnd ? 1 : -1; int limit = step > 0 ? fSegment->count() : -1; int checkEnd = fEnd; do { // advance end const SkOpSpan& span = fSegment->span(checkEnd); const SkOpSegment* other = span.fOther; int oCount = other->count(); for (int oIndex = 0; oIndex < oCount; ++oIndex) { const SkOpSpan& oSpan = other->span(oIndex); if (oSpan.fOther != fSegment) { continue; } if (oSpan.fOtherIndex == checkEnd) { continue; } if (!approximately_equal(oSpan.fOtherT, span.fT)) { continue; } goto recomputeSector; } checkEnd += step; } while (checkEnd != limit); recomputeSector: if (checkEnd == fEnd || checkEnd - step == fEnd) { fUnorderable = true; return false; } int saveEnd = fEnd; fComputedEnd = fEnd = checkEnd - step; setSpans(); setSector(); fEnd = saveEnd; return !fUnorderable; }