// 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_Quadratics allowQuadratics, ReduceOrder_Styles reduceStyle) { 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) { double cx = cubic[index].x; double cy = cubic[index].y; double denom = SkTMax(fabs(cx), SkTMax(fabs(cy), SkTMax(fabs(cubic[minX].x), fabs(cubic[minY].y)))); if (denom == 0) { minXSet |= 1 << index; minYSet |= 1 << index; continue; } double inv = 1 / denom; if (approximately_equal_half(cx * inv, cubic[minX].x * inv)) { minXSet |= 1 << index; } if (approximately_equal_half(cy * inv, cubic[minY].y * inv)) { 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, reduceStyle, reduction); } if (minYSet == 0xF) { // test for horizontal line return horizontal_line(cubic, reduceStyle, reduction); } int result = check_linear(cubic, reduceStyle, minX, maxX, minY, maxY, reduction); if (result) { return result; } if (allowQuadratics == kReduceOrder_QuadraticsAllowed && (result = check_quadratic(cubic, reduction))) { return result; } memcpy(reduction, cubic, sizeof(Cubic)); return 4; }
// 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 SkDCubic& cubic, Quadratics 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].fX > cubic[index].fX) { minX = index; } if (cubic[minY].fY > cubic[index].fY) { minY = index; } if (cubic[maxX].fX < cubic[index].fX) { maxX = index; } if (cubic[maxY].fY < cubic[index].fY) { maxY = index; } } for (index = 0; index < 4; ++index) { double cx = cubic[index].fX; double cy = cubic[index].fY; double denom = SkTMax(fabs(cx), SkTMax(fabs(cy), SkTMax(fabs(cubic[minX].fX), fabs(cubic[minY].fY)))); if (denom == 0) { minXSet |= 1 << index; minYSet |= 1 << index; continue; } double inv = 1 / denom; if (approximately_equal_half(cx * inv, cubic[minX].fX * inv)) { minXSet |= 1 << index; } if (approximately_equal_half(cy * inv, cubic[minY].fY * inv)) { minYSet |= 1 << index; } } if (minXSet == 0xF) { // test for vertical line if (minYSet == 0xF) { // return 1 if all four are coincident return coincident_line(cubic, fCubic); } return vertical_line(cubic, fCubic); } if (minYSet == 0xF) { // test for horizontal line return horizontal_line(cubic, fCubic); } int result = check_linear(cubic, minX, maxX, minY, maxY, fCubic); if (result) { return result; } if (allowQuadratics == SkReduceOrder::kAllow_Quadratics && (result = check_quadratic(cubic, fCubic))) { return result; } fCubic = cubic; return 4; }
// 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; }