static int check_linear(const Cubic& cubic, Cubic& reduction, int minX, int maxX, int minY, int maxY) { int startIndex = 0; int endIndex = 3; while (cubic[startIndex].approximatelyEqual(cubic[endIndex])) { --endIndex; if (endIndex == 0) { printf("%s shouldn't get here if all four points are about equal", __FUNCTION__); assert(0); } } if (!isLinear(cubic, startIndex, endIndex)) { return 0; } // four are colinear: return line formed by outside reduction[0] = cubic[0]; reduction[1] = cubic[3]; int sameSide1; int sameSide2; bool useX = cubic[maxX].x - cubic[minX].x >= cubic[maxY].y - cubic[minY].y; if (useX) { sameSide1 = sign(cubic[0].x - cubic[1].x) + sign(cubic[3].x - cubic[1].x); sameSide2 = sign(cubic[0].x - cubic[2].x) + sign(cubic[3].x - cubic[2].x); } else { sameSide1 = sign(cubic[0].y - cubic[1].y) + sign(cubic[3].y - cubic[1].y); sameSide2 = sign(cubic[0].y - cubic[2].y) + sign(cubic[3].y - cubic[2].y); } if (sameSide1 == sameSide2 && (sameSide1 & 3) != 2) { return 2; } double tValues[2]; int roots; if (useX) { roots = findExtrema(cubic[0].x, cubic[1].x, cubic[2].x, cubic[3].x, tValues); } else { roots = findExtrema(cubic[0].y, cubic[1].y, cubic[2].y, cubic[3].y, tValues); } for (int index = 0; index < roots; ++index) { _Point extrema; extrema.x = interp_cubic_coords(&cubic[0].x, tValues[index]); extrema.y = interp_cubic_coords(&cubic[0].y, tValues[index]); // sameSide > 0 means mid is smaller than either [0] or [3], so replace smaller int replace; if (useX) { if (extrema.x < cubic[0].x ^ extrema.x < cubic[3].x) { continue; } replace = (extrema.x < cubic[0].x | extrema.x < cubic[3].x) ^ cubic[0].x < cubic[3].x; } else { if (extrema.y < cubic[0].y ^ extrema.y < cubic[3].y) { continue; } replace = (extrema.y < cubic[0].y | extrema.y < cubic[3].y) ^ cubic[0].y < cubic[3].y; } reduction[replace] = extrema; } return 2; }
void SkChopCubicAt(const SkPoint src[4], SkPoint dst[7], SkScalar t) { SkASSERT(t > 0 && t < SK_Scalar1); interp_cubic_coords(&src[0].fX, &dst[0].fX, t); interp_cubic_coords(&src[0].fY, &dst[0].fY, t); }
SkDCubic SkDCubic::subDivide(double t1, double t2) const { if (t1 == 0 || t2 == 1) { if (t1 == 0 && t2 == 1) { return *this; } SkDCubicPair pair = chopAt(t1 == 0 ? t2 : t1); SkDCubic dst = t1 == 0 ? pair.first() : pair.second(); return dst; } SkDCubic dst; double ax = dst[0].fX = interp_cubic_coords(&fPts[0].fX, t1); double ay = dst[0].fY = interp_cubic_coords(&fPts[0].fY, t1); double ex = interp_cubic_coords(&fPts[0].fX, (t1*2+t2)/3); double ey = interp_cubic_coords(&fPts[0].fY, (t1*2+t2)/3); double fx = interp_cubic_coords(&fPts[0].fX, (t1+t2*2)/3); double fy = interp_cubic_coords(&fPts[0].fY, (t1+t2*2)/3); double dx = dst[3].fX = interp_cubic_coords(&fPts[0].fX, t2); double dy = dst[3].fY = interp_cubic_coords(&fPts[0].fY, t2); double mx = ex * 27 - ax * 8 - dx; double my = ey * 27 - ay * 8 - dy; double nx = fx * 27 - ax - dx * 8; double ny = fy * 27 - ay - dy * 8; /* bx = */ dst[1].fX = (mx * 2 - nx) / 18; /* by = */ dst[1].fY = (my * 2 - ny) / 18; /* cx = */ dst[2].fX = (nx * 2 - mx) / 18; /* cy = */ dst[2].fY = (ny * 2 - my) / 18; // FIXME: call align() ? return dst; }
void chop_at(const Cubic& src, CubicPair& dst, double t) { if (t == 0.5) { dst.pts[0] = src[0]; dst.pts[1].x = (src[0].x + src[1].x) / 2; dst.pts[1].y = (src[0].y + src[1].y) / 2; dst.pts[2].x = (src[0].x + 2 * src[1].x + src[2].x) / 4; dst.pts[2].y = (src[0].y + 2 * src[1].y + src[2].y) / 4; dst.pts[3].x = (src[0].x + 3 * (src[1].x + src[2].x) + src[3].x) / 8; dst.pts[3].y = (src[0].y + 3 * (src[1].y + src[2].y) + src[3].y) / 8; dst.pts[4].x = (src[1].x + 2 * src[2].x + src[3].x) / 4; dst.pts[4].y = (src[1].y + 2 * src[2].y + src[3].y) / 4; dst.pts[5].x = (src[2].x + src[3].x) / 2; dst.pts[5].y = (src[2].y + src[3].y) / 2; dst.pts[6] = src[3]; return; } interp_cubic_coords(&src[0].x, &dst.pts[0].x, t); interp_cubic_coords(&src[0].y, &dst.pts[0].y, t); }
void SkDCubic::subDivide(const SkDPoint& a, const SkDPoint& d, double t1, double t2, SkDPoint dst[2]) const { SkASSERT(t1 != t2); #if 0 double ex = interp_cubic_coords(&fPts[0].fX, (t1 * 2 + t2) / 3); double ey = interp_cubic_coords(&fPts[0].fY, (t1 * 2 + t2) / 3); double fx = interp_cubic_coords(&fPts[0].fX, (t1 + t2 * 2) / 3); double fy = interp_cubic_coords(&fPts[0].fY, (t1 + t2 * 2) / 3); double mx = ex * 27 - a.fX * 8 - d.fX; double my = ey * 27 - a.fY * 8 - d.fY; double nx = fx * 27 - a.fX - d.fX * 8; double ny = fy * 27 - a.fY - d.fY * 8; /* bx = */ dst[0].fX = (mx * 2 - nx) / 18; /* by = */ dst[0].fY = (my * 2 - ny) / 18; /* cx = */ dst[1].fX = (nx * 2 - mx) / 18; /* cy = */ dst[1].fY = (ny * 2 - my) / 18; #else // this approach assumes that the control points computed directly are accurate enough SkDCubic sub = subDivide(t1, t2); dst[0] = sub[1] + (a - sub[0]); dst[1] = sub[2] + (d - sub[3]); #endif if (t1 == 0 || t2 == 0) { align(0, 1, t1 == 0 ? &dst[0] : &dst[1]); } if (t1 == 1 || t2 == 1) { align(3, 2, t1 == 1 ? &dst[0] : &dst[1]); } if (AlmostBequalUlps(dst[0].fX, a.fX)) { dst[0].fX = a.fX; } if (AlmostBequalUlps(dst[0].fY, a.fY)) { dst[0].fY = a.fY; } if (AlmostBequalUlps(dst[1].fX, d.fX)) { dst[1].fX = d.fX; } if (AlmostBequalUlps(dst[1].fY, d.fY)) { dst[1].fY = d.fY; } }
SkDCubicPair SkDCubic::chopAt(double t) const { SkDCubicPair dst; if (t == 0.5) { dst.pts[0] = fPts[0]; dst.pts[1].fX = (fPts[0].fX + fPts[1].fX) / 2; dst.pts[1].fY = (fPts[0].fY + fPts[1].fY) / 2; dst.pts[2].fX = (fPts[0].fX + 2 * fPts[1].fX + fPts[2].fX) / 4; dst.pts[2].fY = (fPts[0].fY + 2 * fPts[1].fY + fPts[2].fY) / 4; dst.pts[3].fX = (fPts[0].fX + 3 * (fPts[1].fX + fPts[2].fX) + fPts[3].fX) / 8; dst.pts[3].fY = (fPts[0].fY + 3 * (fPts[1].fY + fPts[2].fY) + fPts[3].fY) / 8; dst.pts[4].fX = (fPts[1].fX + 2 * fPts[2].fX + fPts[3].fX) / 4; dst.pts[4].fY = (fPts[1].fY + 2 * fPts[2].fY + fPts[3].fY) / 4; dst.pts[5].fX = (fPts[2].fX + fPts[3].fX) / 2; dst.pts[5].fY = (fPts[2].fY + fPts[3].fY) / 2; dst.pts[6] = fPts[3]; return dst; } interp_cubic_coords(&fPts[0].fX, &dst.pts[0].fX, t); interp_cubic_coords(&fPts[0].fY, &dst.pts[0].fY, t); return dst; }
void sub_divide(const Cubic& src, double t1, double t2, Cubic& dst) { double ax = dst[0].x = interp_cubic_coords(&src[0].x, t1); double ay = dst[0].y = interp_cubic_coords(&src[0].y, t1); double ex = interp_cubic_coords(&src[0].x, (t1*2+t2)/3); double ey = interp_cubic_coords(&src[0].y, (t1*2+t2)/3); double fx = interp_cubic_coords(&src[0].x, (t1+t2*2)/3); double fy = interp_cubic_coords(&src[0].y, (t1+t2*2)/3); double dx = dst[3].x = interp_cubic_coords(&src[0].x, t2); double dy = dst[3].y = interp_cubic_coords(&src[0].y, t2); double mx = ex * 27 - ax * 8 - dx; double my = ey * 27 - ay * 8 - dy; double nx = fx * 27 - ax - dx * 8; double ny = fy * 27 - ay - dy * 8; /* bx = */ dst[1].x = (mx * 2 - nx) / 18; /* by = */ dst[1].y = (my * 2 - ny) / 18; /* cx = */ dst[2].x = (nx * 2 - mx) / 18; /* cy = */ dst[2].y = (ny * 2 - my) / 18; }
static int horizontal_line(const Cubic& cubic, Cubic& reduction) { double tValues[2]; reduction[0] = cubic[0]; reduction[1] = cubic[3]; int smaller = reduction[1].x > reduction[0].x; int larger = smaller ^ 1; int roots = findExtrema(cubic[0].x, cubic[1].x, cubic[2].x, cubic[3].x, tValues); for (int index = 0; index < roots; ++index) { double xExtrema = interp_cubic_coords(&cubic[0].x, tValues[index]); if (reduction[smaller].x > xExtrema) { reduction[smaller].x = xExtrema; continue; } if (reduction[larger].x < xExtrema) { reduction[larger].x = xExtrema; } } return 2; }
static int vertical_line(const Cubic& cubic, Cubic& reduction) { double tValues[2]; reduction[0] = cubic[0]; reduction[1] = cubic[3]; int smaller = reduction[1].y > reduction[0].y; int larger = smaller ^ 1; int roots = findExtrema(cubic[0].y, cubic[1].y, cubic[2].y, cubic[3].y, tValues); for (int index = 0; index < roots; ++index) { double yExtrema = interp_cubic_coords(&cubic[0].y, tValues[index]); if (reduction[smaller].y > yExtrema) { reduction[smaller].y = yExtrema; continue; } if (reduction[larger].y < yExtrema) { reduction[larger].y = yExtrema; } } return 2; }
void chop_at(const Cubic& src, CubicPair& dst, double t) { interp_cubic_coords(&src[0].x, &dst.pts[0].x, t); interp_cubic_coords(&src[0].y, &dst.pts[0].y, t); }
static int check_linear(const Cubic& cubic, Cubic& reduction, int minX, int maxX, int minY, int maxY) { int startIndex = 0; int endIndex = 3; while (cubic[startIndex].approximatelyEqual(cubic[endIndex])) { --endIndex; if (endIndex == 0) { printf("%s shouldn't get here if all four points are about equal", __FUNCTION__); assert(0); } } LineParameters lineParameters; lineParameters.cubicEndPoints(cubic, startIndex, endIndex); double normalSquared = lineParameters.normalSquared(); double distance[2]; // distance is not normalized int mask = other_two(startIndex, endIndex); int inner1 = startIndex ^ mask; int inner2 = endIndex ^ mask; lineParameters.controlPtDistance(cubic, inner1, inner2, distance); double limit = normalSquared * SquaredEpsilon; int index; for (index = 0; index < 2; ++index) { double distSq = distance[index]; distSq *= distSq; if (distSq > limit) { return 0; } } // four are colinear: return line formed by outside reduction[0] = cubic[0]; reduction[1] = cubic[3]; int sameSide1; int sameSide2; bool useX = cubic[maxX].x - cubic[minX].x >= cubic[maxY].y - cubic[minY].y; if (useX) { sameSide1 = sign(cubic[0].x - cubic[1].x) + sign(cubic[3].x - cubic[1].x); sameSide2 = sign(cubic[0].x - cubic[2].x) + sign(cubic[3].x - cubic[2].x); } else { sameSide1 = sign(cubic[0].y - cubic[1].y) + sign(cubic[3].y - cubic[1].y); sameSide2 = sign(cubic[0].y - cubic[2].y) + sign(cubic[3].y - cubic[2].y); } if (sameSide1 == sameSide2 && (sameSide1 & 3) != 2) { return 2; } double tValues[2]; int roots; if (useX) { roots = SkFindCubicExtrema(cubic[0].x, cubic[1].x, cubic[2].x, cubic[3].x, tValues); } else { roots = SkFindCubicExtrema(cubic[0].y, cubic[1].y, cubic[2].y, cubic[3].y, tValues); } for (index = 0; index < roots; ++index) { _Point extrema; extrema.x = interp_cubic_coords(&cubic[0].x, tValues[index]); extrema.y = interp_cubic_coords(&cubic[0].y, tValues[index]); // sameSide > 0 means mid is smaller than either [0] or [3], so replace smaller int replace; if (useX) { if (extrema.x < cubic[0].x ^ extrema.x < cubic[3].x) { continue; } replace = (extrema.x < cubic[0].x | extrema.x < cubic[3].x) ^ cubic[0].x < cubic[3].x; } else { if (extrema.y < cubic[0].y ^ extrema.y < cubic[3].y) { continue; } replace = (extrema.y < cubic[0].y | extrema.y < cubic[3].y) ^ cubic[0].y < cubic[3].y; } reduction[replace] = extrema; } return 2; }