shape ops work in progress

git-svn-id: http://skia.googlecode.com/svn/trunk@7294 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
caryclark@google.com 2013-01-19 13:22:39 +00:00
parent 36df7ed46b
commit 05c4bad470
15 changed files with 292 additions and 77 deletions

View File

@ -161,39 +161,64 @@ bool intersect(const Cubic& c1, const Cubic& c2, Intersections& i) {
#include "CubicUtilities.h"
// FIXME: ? if needed, compute the error term from the tangent length
static double computeDelta(const Cubic& cubic, double t, double scale) {
double attempt = scale / precisionUnit;
#if SK_DEBUG
double precision = calcPrecision(cubic);
_Point dxy;
dxdy_at_t(cubic, t, dxy);
_Point p1, p2;
xy_at_t(cubic, std::max(t - attempt, 0.), p1.x, p1.y);
xy_at_t(cubic, std::min(t + attempt, 1.), p2.x, p2.y);
double dx = p1.x - p2.x;
double dy = p1.y - p2.y;
double distSq = dx * dx + dy * dy;
double dist = sqrt(distSq);
assert(dist > precision);
#endif
return attempt;
}
// this flavor approximates the cubics with quads to find the intersecting ts
// OPTIMIZE: if this strategy proves successful, the quad approximations, or the ts used
// to create the approximations, could be stored in the cubic segment
// fixme: this strategy needs to add short line segments on either end, or similarly extend the
// initial and final quadratics
bool intersect2(const Cubic& c1, const Cubic& c2, Intersections& i) {
// FIXME: this strategy needs to intersect the convex hull on either end with the opposite to
// account for inset quadratics that cause the endpoint intersection to avoid detection
// the segments can be very short -- the length of the maximum quadratic error (precision)
// FIXME: this needs to recurse on itself, taking a range of T values and computing the new
// t range ala is linear inner. The range can be figured by taking the dx/dy and determining
// the fraction that matches the precision. That fraction is the change in t for the smaller cubic.
static bool intersect2(const Cubic& cubic1, double t1s, double t1e, const Cubic& cubic2,
double t2s, double t2e, Intersections& i) {
Cubic c1, c2;
sub_divide(cubic1, t1s, t1e, c1);
sub_divide(cubic2, t2s, t2e, c2);
SkTDArray<double> ts1;
double precision1 = calcPrecision(c1);
cubic_to_quadratics(c1, precision1, ts1);
cubic_to_quadratics(c1, calcPrecision(c1), ts1);
SkTDArray<double> ts2;
double precision2 = calcPrecision(c2);
cubic_to_quadratics(c2, precision2, ts2);
double t1Start = 0;
cubic_to_quadratics(c2, calcPrecision(c2), ts2);
double t1Start = t1s;
int ts1Count = ts1.count();
for (int i1 = 0; i1 <= ts1Count; ++i1) {
const double t1 = i1 < ts1Count ? ts1[i1] : 1;
const double tEnd1 = i1 < ts1Count ? ts1[i1] : 1;
const double t1 = t1s + (t1e - t1s) * tEnd1;
Cubic part1;
sub_divide(c1, t1Start, t1, part1);
sub_divide(cubic1, t1Start, t1, part1);
Quadratic q1;
demote_cubic_to_quad(part1, q1);
// start here;
// should reduceOrder be looser in this use case if quartic is going to blow up on an
// extremely shallow quadratic?
// maybe quadratics to lines need the same sort of recursive solution that I hope to find
// for cubics to quadratics ...
Quadratic s1;
int o1 = reduceOrder(q1, s1);
double t2Start = 0;
double t2Start = t2s;
int ts2Count = ts2.count();
for (int i2 = 0; i2 <= ts2Count; ++i2) {
const double t2 = i2 < ts2Count ? ts2[i2] : 1;
const double tEnd2 = i2 < ts2Count ? ts2[i2] : 1;
const double t2 = t2s + (t2e - t2s) * tEnd2;
Cubic part2;
sub_divide(c2, t2Start, t2, part2);
sub_divide(cubic2, t2Start, t2, part2);
Quadratic q2;
demote_cubic_to_quad(part2, q2);
Quadratic s2;
@ -202,20 +227,34 @@ bool intersect2(const Cubic& c1, const Cubic& c2, Intersections& i) {
if (o1 == 3 && o2 == 3) {
intersect2(q1, q2, locals);
} else if (o1 <= 2 && o2 <= 2) {
i.fUsed = intersect((const _Line&) s1, (const _Line&) s2, i.fT[0], i.fT[1]);
locals.fUsed = intersect((const _Line&) s1, (const _Line&) s2, locals.fT[0],
locals.fT[1]);
} else if (o1 == 3 && o2 <= 2) {
intersect(q1, (const _Line&) s2, i);
intersect(q1, (const _Line&) s2, locals);
} else {
SkASSERT(o1 <= 2 && o2 == 3);
intersect(q2, (const _Line&) s1, i);
for (int s = 0; s < i.fUsed; ++s) {
SkTSwap(i.fT[0][s], i.fT[1][s]);
intersect(q2, (const _Line&) s1, locals);
for (int s = 0; s < locals.fUsed; ++s) {
SkTSwap(locals.fT[0][s], locals.fT[1][s]);
}
}
for (int tIdx = 0; tIdx < locals.used(); ++tIdx) {
double to1 = t1Start + (t1 - t1Start) * locals.fT[0][tIdx];
double to2 = t2Start + (t2 - t2Start) * locals.fT[1][tIdx];
i.insert(to1, to2);
// if the computed t is not sufficiently precise, iterate
_Point p1, p2;
xy_at_t(cubic1, to1, p1.x, p1.y);
xy_at_t(cubic2, to2, p2.x, p2.y);
if (p1.approximatelyEqual(p2)) {
i.insert(i.swapped() ? to2 : to1, i.swapped() ? to1 : to2);
} else {
double dt1 = computeDelta(cubic1, to1, t1e - t1s);
double dt2 = computeDelta(cubic2, to2, t2e - t2s);
i.swap();
intersect2(cubic2, std::max(to2 - dt2, 0.), std::min(to2 + dt2, 1.),
cubic1, std::max(to1 - dt1, 0.), std::min(to1 + dt1, 1.), i);
i.swap();
}
}
t2Start = t2;
}
@ -224,6 +263,69 @@ bool intersect2(const Cubic& c1, const Cubic& c2, Intersections& i) {
return i.intersected();
}
static bool intersectEnd(const Cubic& cubic1, bool start, const Cubic& cubic2, const _Rect& bounds2,
Intersections& i) {
_Line line1;
line1[0] = line1[1] = cubic1[start ? 0 : 3];
_Point dxy1 = line1[0] - cubic1[start ? 1 : 2];
dxy1 /= precisionUnit;
line1[1] += dxy1;
_Rect line1Bounds;
line1Bounds.setBounds(line1);
if (!bounds2.intersects(line1Bounds)) {
return false;
}
_Line line2;
line2[0] = line2[1] = line1[0];
_Point dxy2 = line2[0] - cubic1[start ? 3 : 0];
dxy2 /= precisionUnit;
line2[1] += dxy2;
#if 0 // this is so close to the first bounds test it isn't worth the short circuit test
_Rect line2Bounds;
line2Bounds.setBounds(line2);
if (!bounds2.intersects(line2Bounds)) {
return false;
}
#endif
Intersections local1;
if (!intersect(cubic2, line1, local1)) {
return false;
}
Intersections local2;
if (!intersect(cubic2, line2, local2)) {
return false;
}
double tMin, tMax;
tMin = tMax = local1.fT[0][0];
for (int index = 1; index < local1.fUsed; ++index) {
tMin = std::min(tMin, local1.fT[0][index]);
tMax = std::max(tMax, local1.fT[0][index]);
}
for (int index = 1; index < local2.fUsed; ++index) {
tMin = std::min(tMin, local2.fT[0][index]);
tMax = std::max(tMax, local2.fT[0][index]);
}
return intersect2(cubic1, start ? 0 : 1, start ? 1.0 / precisionUnit : 1 - 1.0 / precisionUnit,
cubic2, tMin, tMax, i);
}
// FIXME: add intersection of convex null on cubics' ends with the opposite cubic. The hull line
// segments can be constructed to be only as long as the calculated precision suggests. If the hull
// line segments intersect the cubic, then use the intersections to construct a subdivision for
// quadratic curve fitting.
bool intersect2(const Cubic& c1, const Cubic& c2, Intersections& i) {
bool result = intersect2(c1, 0, 1, c2, 0, 1, i);
// FIXME: pass in cached bounds from caller
_Rect c1Bounds, c2Bounds;
c1Bounds.setBounds(c1); // OPTIMIZE use setRawBounds ?
c2Bounds.setBounds(c2);
result |= intersectEnd(c1, false, c2, c2Bounds, i);
result |= intersectEnd(c1, true, c2, c2Bounds, i);
result |= intersectEnd(c2, false, c1, c1Bounds, i);
result |= intersectEnd(c2, true, c1, c1Bounds, i);
return result;
}
int intersect(const Cubic& cubic, const Quadratic& quad, Intersections& i) {
SkTDArray<double> ts;
double precision = calcPrecision(cubic);

View File

@ -78,14 +78,14 @@ static void oneOff(const Cubic& cubic1, const Cubic& cubic2) {
intersect2(cubic1, cubic2, intersections2);
for (int pt = 0; pt < intersections2.used(); ++pt) {
double tt1 = intersections2.fT[0][pt];
double tx1, ty1;
xy_at_t(cubic1, tt1, tx1, ty1);
_Point xy1, xy2;
xy_at_t(cubic1, tt1, xy1.x, xy1.y);
int pt2 = intersections2.fFlip ? intersections2.used() - pt - 1 : pt;
double tt2 = intersections2.fT[1][pt2];
double tx2, ty2;
xy_at_t(cubic2, tt2, tx2, ty2);
xy_at_t(cubic2, tt2, xy2.x, xy2.y);
SkDebugf("%s t1=%1.9g (%1.9g, %1.9g) (%1.9g, %1.9g) t2=%1.9g\n", __FUNCTION__,
tt1, tx1, ty1, tx2, ty2, tt2);
tt1, xy1.x, xy1.y, xy2.x, xy2.y, tt2);
assert(xy1.approximatelyEqual(xy2));
}
}
@ -168,7 +168,7 @@ int depth;
#define TRY_OLD 0 // old way fails on test == 1
void CubicIntersection_RandTest() {
void CubicIntersection_RandTestOld() {
srand(0);
const int tests = 1000000; // 10000000;
double largestFactor = DBL_MAX;
@ -265,3 +265,55 @@ void CubicIntersection_RandTest() {
}
}
}
void CubicIntersection_RandTest() {
srand(0);
const int tests = 1000000; // 10000000;
// double largestFactor = DBL_MAX;
for (int test = 0; test < tests; ++test) {
Cubic cubic1, cubic2;
for (int i = 0; i < 4; ++i) {
cubic1[i].x = (double) rand() / RAND_MAX * 100;
cubic1[i].y = (double) rand() / RAND_MAX * 100;
cubic2[i].x = (double) rand() / RAND_MAX * 100;
cubic2[i].y = (double) rand() / RAND_MAX * 100;
}
#if DEBUG_CRASH
char str[1024];
sprintf(str, "{{%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}, {%1.9g, %1.9g}},\n",
cubic1[0].x, cubic1[0].y, cubic1[1].x, cubic1[1].y, cubic1[2].x, cubic1[2].y,
cubic1[3].x, cubic1[3].y,
cubic2[0].x, cubic2[0].y, cubic2[1].x, cubic2[1].y, cubic2[2].x, cubic2[2].y,
cubic2[3].x, cubic2[3].y);
#endif
_Rect rect1, rect2;
rect1.setBounds(cubic1);
rect2.setBounds(cubic2);
bool boundsIntersect = rect1.left <= rect2.right && rect2.left <= rect2.right
&& rect1.top <= rect2.bottom && rect2.top <= rect1.bottom;
Intersections i1, i2;
if (test == -1) {
SkDebugf("ready...\n");
}
Intersections intersections2;
bool newIntersects = intersect2(cubic1, cubic2, intersections2);
if (!boundsIntersect && newIntersects) {
SkDebugf("%s %d unexpected intersection boundsIntersect=%d "
" newIntersects=%d\n%s %s\n", __FUNCTION__, test, boundsIntersect,
newIntersects, __FUNCTION__, str);
assert(0);
}
for (int pt = 0; pt < intersections2.used(); ++pt) {
double tt1 = intersections2.fT[0][pt];
_Point xy1, xy2;
xy_at_t(cubic1, tt1, xy1.x, xy1.y);
int pt2 = intersections2.fFlip ? intersections2.used() - pt - 1 : pt;
double tt2 = intersections2.fT[1][pt2];
xy_at_t(cubic2, tt2, xy2.x, xy2.y);
SkDebugf("%s t1=%1.9g (%1.9g, %1.9g) (%1.9g, %1.9g) t2=%1.9g\n", __FUNCTION__,
tt1, xy1.x, xy1.y, xy2.x, xy2.y, tt2);
assert(xy1.approximatelyEqual(xy2));
}
}
}

View File

@ -137,6 +137,9 @@ static void addTs(const Cubic& cubic, double precision, double start, double end
}
// flavor that returns T values only, deferring computing the quads until they are needed
// FIXME: when called from recursive intersect 2, this could take the original cubic
// and do a more precise job when calling chop at and sub divide by computing the fractional ts.
// it would still take the prechopped cubic for reduce order and find cubic inflections
void cubic_to_quadratics(const Cubic& cubic, double precision, SkTDArray<double>& ts) {
Cubic reduced;
int order = reduceOrder(cubic, reduced, kReduceOrder_QuadraticsAllowed);

View File

@ -7,12 +7,15 @@
#include "CubicUtilities.h"
#include "QuadraticUtilities.h"
const int precisionUnit = 256; // FIXME: arbitrary -- should try different values in test framework
// FIXME: cache keep the bounds and/or precision with the caller?
double calcPrecision(const Cubic& cubic) {
_Rect dRect;
dRect.setBounds(cubic);
dRect.setBounds(cubic); // OPTIMIZATION: just use setRawBounds ?
double width = dRect.right - dRect.left;
double height = dRect.bottom - dRect.top;
return (width > height ? width : height) / 256;
return (width > height ? width : height) / precisionUnit;
}
void coefficients(const double* cubic, double& A, double& B, double& C, double& D) {
@ -92,24 +95,30 @@ int cubicRoots(double A, double B, double C, double D, double t[3]) {
// c(t) = a(1-t)^3 + 3bt(1-t)^2 + 3c(1-t)t^2 + dt^3
// c'(t) = -3a(1-t)^2 + 3b((1-t)^2 - 2t(1-t)) + 3c(2t(1-t) - t^2) + 3dt^2
// = 3(b-a)(1-t)^2 + 6(c-b)t(1-t) + 3(d-c)t^2
double derivativeAtT(const double* cubic, double t) {
static double derivativeAtT(const double* cubic, double t) {
double one_t = 1 - t;
double a = cubic[0];
double b = cubic[2];
double c = cubic[4];
double d = cubic[6];
return (b - a) * one_t * one_t + 2 * (c - b) * t * one_t + (d - c) * t * t;
return 3 * ((b - a) * one_t * one_t + 2 * (c - b) * t * one_t + (d - c) * t * t);
}
void dxdy_at_t(const Cubic& cubic, double t, double& dx, double& dy) {
if (&dx) {
dx = derivativeAtT(&cubic[0].x, t);
}
if (&dy) {
dy = derivativeAtT(&cubic[0].y, t);
}
double dx_at_t(const Cubic& cubic, double t) {
return derivativeAtT(&cubic[0].x, t);
}
double dy_at_t(const Cubic& cubic, double t) {
return derivativeAtT(&cubic[0].y, t);
}
// OPTIMIZE? compute t^2, t(1-t), and (1-t)^2 and pass them to another version of derivative at t?
void dxdy_at_t(const Cubic& cubic, double t, _Point& dxdy) {
dxdy.x = derivativeAtT(&cubic[0].x, t);
dxdy.y = derivativeAtT(&cubic[0].y, t);
}
int find_cubic_inflections(const Cubic& src, double tValues[])
{
double Ax = src[1].x - src[0].x;
@ -138,6 +147,7 @@ bool rotate(const Cubic& cubic, int zero, int index, Cubic& rotPath) {
return true;
}
#if 0 // unused for now
double secondDerivativeAtT(const double* cubic, double t) {
double a = cubic[0];
double b = cubic[2];
@ -145,6 +155,7 @@ double secondDerivativeAtT(const double* cubic, double t) {
double d = cubic[6];
return (c - 2 * b + a) * (1 - t) + (d - 2 * c + b) * t;
}
#endif
void xy_at_t(const Cubic& cubic, double t, double& x, double& y) {
double one_t = 1 - t;

View File

@ -19,11 +19,12 @@ void coefficients(const double* cubic, double& A, double& B, double& C, double&
int cubicRoots(double A, double B, double C, double D, double t[3]);
int cubicRootsX(double A, double B, double C, double D, double s[3]);
void demote_cubic_to_quad(const Cubic& cubic, Quadratic& quad);
double derivativeAtT(const double* cubic, double t);
void dxdy_at_t(const Cubic& , double t, double& x, double& y);
double dx_at_t(const Cubic& , double t);
double dy_at_t(const Cubic& , double t);
void dxdy_at_t(const Cubic& , double t, _Point& y);
int find_cubic_inflections(const Cubic& src, double tValues[]);
bool rotate(const Cubic& cubic, int zero, int index, Cubic& rotPath);
double secondDerivativeAtT(const double* cubic, double t);
void xy_at_t(const Cubic& , double t, double& x, double& y);
extern const int precisionUnit;
#endif

View File

@ -135,11 +135,26 @@ struct _Point {
return v;
}
void operator+=(const _Point& v) {
x += v.x;
y += v.y;
}
void operator-=(const _Point& v) {
x -= v.x;
y -= v.y;
}
void operator/=(const double s) {
x /= s;
y /= s;
}
void operator*=(const double s) {
x *= s;
y *= s;
}
friend bool operator==(const _Point& a, const _Point& b) {
return a.x == b.x && a.y == b.y;
}
@ -187,11 +202,19 @@ struct _Rect {
}
// FIXME: used by debugging only ?
bool contains(const _Point& pt) {
bool contains(const _Point& pt) const {
return approximately_between(left, pt.x, right)
&& approximately_between(top, pt.y, bottom);
}
bool intersects(_Rect& r) const {
assert(left < right);
assert(top < bottom);
assert(r.left < r.right);
assert(r.top < r.bottom);
return r.left < right && left < r.right && r.top < bottom && top < r.bottom;
}
void set(const _Point& pt) {
left = right = pt.x;
top = bottom = pt.y;

View File

@ -14,6 +14,7 @@ void CubicCoincidence_Test();
void CubicIntersection_OneOffTest();
void CubicIntersection_Test();
void CubicIntersection_RandTest();
void CubicIntersection_RandTestOld();
void CubicParameterization_Test();
void CubicReduceOrder_Test();
void CubicsToQuadratics_RandTest();

View File

@ -68,6 +68,7 @@ void Intersections::cleanUp() {
}
// FIXME: this doesn't respect swap, but add coincident does -- seems inconsistent
void Intersections::insert(double one, double two) {
assert(fUsed <= 1 || fT[0][0] < fT[0][1]);
int index;

View File

@ -12,17 +12,14 @@
class Intersections {
public:
Intersections()
: fUsed(0)
, fUsed2(0)
, fCoincidentUsed(0)
, fFlip(false)
, fUnsortable(false)
Intersections()
: fFlip(0)
, fSwap(0)
{
// OPTIMIZE: don't need to be initialized in release
bzero(fT, sizeof(fT));
bzero(fCoincidentT, sizeof(fCoincidentT));
reset();
}
void add(double one, double two) {
@ -82,6 +79,12 @@ public:
return fUsed == fUsed2;
}
// leaves flip, swap alone
void reset() {
fUsed = fUsed2 = fCoincidentUsed = 0;
fUnsortable = false;
}
void swap() {
fSwap ^= true;
}

View File

@ -188,12 +188,12 @@ static bool addIntercept(const Quadratic& q1, const Quadratic& q2, double tMin,
xy_at_t(q2, tMid, mid.x, mid.y);
_Line line;
line[0] = line[1] = mid;
double dx, dy;
dxdy_at_t(q2, tMid, dx, dy);
line[0].x -= dx;
line[0].y -= dy;
line[1].x += dx;
line[1].y += dy;
_Point dxdy;
dxdy_at_t(q2, tMid, dxdy);
line[0].x -= dxdy.x;
line[0].y -= dxdy.y;
line[1].x += dxdy.x;
line[1].y += dxdy.y;
Intersections rootTs;
int roots = intersect(q1, line, rootTs);
assert(roots == 1);
@ -251,10 +251,10 @@ static bool isLinearInner(const Quadratic& q1, double t1s, double t1e, const Qua
}
int split = 0;
if (tMin != tMax || tCount > 2) {
dxdy_at_t(q2, tMin, dxy2.x, dxy2.y);
dxdy_at_t(q2, tMin, dxy2);
for (int index = 1; index < tCount; ++index) {
dxy1 = dxy2;
dxdy_at_t(q2, tsFound[index], dxy2.x, dxy2.y);
dxdy_at_t(q2, tsFound[index], dxy2);
double dot = dxy1.dot(dxy2);
if (dot < 0) {
split = index - 1;
@ -309,6 +309,7 @@ static bool isLinear(const Quadratic& q1, const Quadratic& q2, Intersections& i)
static bool relaxedIsLinear(const Quadratic& q1, const Quadratic& q2, Intersections& i) {
double m1 = flatMeasure(q1);
double m2 = flatMeasure(q2);
i.reset();
if (fabs(m1) < fabs(m2)) {
isLinearInner(q1, 0, 1, q2, 0, 1, i);
return false;
@ -329,7 +330,7 @@ static void unsortableExpanse(const Quadratic& q1, const Quadratic& q2, Intersec
for (int qIdx = 0; qIdx < 2; qIdx++) {
for (int t = 0; t < 2; t++) {
_Point dxdy;
dxdy_at_t(*qs[qIdx], t, dxdy.x, dxdy.y);
dxdy_at_t(*qs[qIdx], t, dxdy);
_Line perp;
perp[0] = perp[1] = (*qs[qIdx])[t == 0 ? 0 : 2];
perp[0].x += dxdy.y;
@ -342,7 +343,7 @@ static void unsortableExpanse(const Quadratic& q1, const Quadratic& q2, Intersec
if (hits) {
if (flip < 0) {
_Point dxdy2;
dxdy_at_t(*qs[qIdx ^ 1], hitData.fT[0][0], dxdy2.x, dxdy2.y);
dxdy_at_t(*qs[qIdx ^ 1], hitData.fT[0][0], dxdy2);
double dot = dxdy.dot(dxdy2);
flip = dot < 0;
i.fT[1][0] = flip;

View File

@ -59,12 +59,15 @@ static void standardTestCases() {
}
static const Quadratic testSet[] = {
{{53.774852327053594, 53.318060789841951}, {45.787877803416805, 51.393492026284981}, {46.703936967162392, 53.06860709822206}},
{{46.703936967162392, 53.06860709822206}, {47.619996130907957, 54.74372217015916}, {53.020051653535361, 48.633140968832024}},
{{67.426548091427676, 37.993772624988935}, {51.129513170665035, 57.542281234563646}, {44.594748190899189, 65.644267382683879}},
{{61.336508189019057, 82.693132843213675}, {54.825078921449354, 71.663932799212432}, {47.727444217558926, 61.4049645128392}},
{{67.4265481,37.9937726}, {51.1295132,57.5422812}, {44.5947482,65.6442674}},
{{61.3365082,82.6931328}, {54.8250789,71.6639328}, {47.7274442,61.4049645}},
{{53.774852327053594, 53.318060789841951}, {45.787877803416805, 51.393492026284981}, {46.703936967162392, 53.06860709822206}},
{{46.703936967162392, 53.06860709822206}, {47.619996130907957, 54.74372217015916}, {53.020051653535361, 48.633140968832024}},
{{50.934805397717923, 51.52391952648901}, {56.803308902971423, 44.246234610627596}, {69.776888596721406, 40.166645096692555}},
{{50.230212796400401, 38.386469101526998}, {49.855620812184917, 38.818990392153609}, {56.356567496227363, 47.229909093319407}},

View File

@ -64,16 +64,27 @@ int quadraticRoots(double A, double B, double C, double t[2]) {
return foundRoots;
}
void dxdy_at_t(const Quadratic& quad, double t, double& x, double& y) {
static double derivativeAtT(const double* quad, double t) {
double a = t - 1;
double b = 1 - 2 * t;
double c = t;
if (&x) {
x = a * quad[0].x + b * quad[1].x + c * quad[2].x;
}
if (&y) {
y = a * quad[0].y + b * quad[1].y + c * quad[2].y;
}
return a * quad[0] + b * quad[2] + c * quad[4];
}
double dx_at_t(const Quadratic& quad, double t) {
return derivativeAtT(&quad[0].x, t);
}
double dy_at_t(const Quadratic& quad, double t) {
return derivativeAtT(&quad[0].y, t);
}
void dxdy_at_t(const Quadratic& quad, double t, _Point& dxy) {
double a = t - 1;
double b = 1 - 2 * t;
double c = t;
dxy.x = a * quad[0].x + b * quad[1].x + c * quad[2].x;
dxy.y = a * quad[0].y + b * quad[1].y + c * quad[2].y;
}
void xy_at_t(const Quadratic& quad, double t, double& x, double& y) {

View File

@ -9,8 +9,9 @@
#define QUADRATIC_UTILITIES_H
#include "DataTypes.h"
void dxdy_at_t(const Quadratic& , double t, double& x, double& y);
double dx_at_t(const Quadratic& , double t);
double dy_at_t(const Quadratic& , double t);
void dxdy_at_t(const Quadratic& , double t, _Point& xy);
/* Parameterization form, given A*t*t + 2*B*t*(1-t) + C*(1-t)*(1-t)
*

View File

@ -305,15 +305,13 @@ static SkScalar LineDXAtT(const SkPoint a[2], double ) {
static SkScalar QuadDXAtT(const SkPoint a[3], double t) {
MAKE_CONST_QUAD(quad, a);
double x;
dxdy_at_t(quad, t, x, *(double*) 0);
double x = dx_at_t(quad, t);
return SkDoubleToScalar(x);
}
static SkScalar CubicDXAtT(const SkPoint a[4], double t) {
MAKE_CONST_CUBIC(cubic, a);
double x;
dxdy_at_t(cubic, t, x, *(double*) 0);
double x = dx_at_t(cubic, t);
return SkDoubleToScalar(x);
}
@ -330,15 +328,13 @@ static SkScalar LineDYAtT(const SkPoint a[2], double ) {
static SkScalar QuadDYAtT(const SkPoint a[3], double t) {
MAKE_CONST_QUAD(quad, a);
double y;
dxdy_at_t(quad, t, *(double*) 0, y);
double y = dy_at_t(quad, t);
return SkDoubleToScalar(y);
}
static SkScalar CubicDYAtT(const SkPoint a[4], double t) {
MAKE_CONST_CUBIC(cubic, a);
double y;
dxdy_at_t(cubic, t, *(double*) 0, y);
double y = dy_at_t(cubic, t);
return SkDoubleToScalar(y);
}

View File

@ -1540,11 +1540,17 @@ $7 = {{x = 24.006224853920855, y = 72.621119847810419}, {x = 29.758671200376888,
{{41.873704,30.8489321}, {53.3823801,24.1476487}, {77.3190123,0.63959742}},
</div>
<div id="quad14">
{{67.4265481,37.9937726}, {51.1295132,57.5422812}, {44.5947482,65.6442674}},
{{61.3365082,82.6931328}, {54.8250789,71.6639328}, {47.7274442,61.4049645}},
</div>
</div>
<script type="text/javascript">
var testDivs = [
quad14,
cubic18,
cubic17,
cubic16,