Unify naming of bezier coordinates in internal calculations.

This commit is contained in:
Jürg Lehni 2017-01-17 12:05:32 +01:00
parent 6e96fd6be5
commit 4e215b0eab

View file

@ -616,27 +616,27 @@ statics: /** @lends Curve */{
}, },
subdivide: function(v, t) { subdivide: function(v, t) {
var p1x = v[0], p1y = v[1], var x0 = v[0], y0 = v[1],
c1x = v[2], c1y = v[3], x1 = v[2], y1 = v[3],
c2x = v[4], c2y = v[5], x2 = v[4], y2 = v[5],
p2x = v[6], p2y = v[7]; x3 = v[6], y3 = v[7];
if (t === undefined) if (t === undefined)
t = 0.5; t = 0.5;
// Triangle computation, with loops unrolled. // Triangle computation, with loops unrolled.
var u = 1 - t, var u = 1 - t,
// Interpolate from 4 to 3 points // Interpolate from 4 to 3 points
p3x = u * p1x + t * c1x, p3y = u * p1y + t * c1y, x4 = u * x0 + t * x1, y4 = u * y0 + t * y1,
p4x = u * c1x + t * c2x, p4y = u * c1y + t * c2y, x5 = u * x1 + t * x2, y5 = u * y1 + t * y2,
p5x = u * c2x + t * p2x, p5y = u * c2y + t * p2y, x6 = u * x2 + t * x3, y6 = u * y2 + t * y3,
// Interpolate from 3 to 2 points // Interpolate from 3 to 2 points
p6x = u * p3x + t * p4x, p6y = u * p3y + t * p4y, x7 = u * x4 + t * x5, y7 = u * y4 + t * y5,
p7x = u * p4x + t * p5x, p7y = u * p4y + t * p5y, x8 = u * x5 + t * x6, y8 = u * y5 + t * y6,
// Interpolate from 2 points to 1 point // Interpolate from 2 points to 1 point
p8x = u * p6x + t * p7x, p8y = u * p6y + t * p7y; x9 = u * x7 + t * x8, y9 = u * y7 + t * y8;
// We now have all the values we need to build the sub-curves: // We now have all the values we need to build the sub-curves:
return [ return [
[p1x, p1y, p3x, p3y, p6x, p6y, p8x, p8y], // left [x0, y0, x4, y4, x7, y7, x9, y9], // left
[p8x, p8y, p7x, p7y, p5x, p5y, p2x, p2y] // right [x9, y9, x8, y8, x6, y6, x3, y3] // right
]; ];
}, },
@ -691,21 +691,21 @@ statics: /** @lends Curve */{
return curves; return curves;
}, },
// Converts from the point coordinates (p1, c1, c2, p2) for one axis to // Converts from the point coordinates (p0, p1, p2, p3) for one axis to
// the polynomial coefficients and solves the polynomial for val // the polynomial coefficients and solves the polynomial for val
solveCubic: function (v, coord, val, roots, min, max) { solveCubic: function (v, coord, val, roots, min, max) {
var p1 = v[coord], var v0 = v[coord],
c1 = v[coord + 2], v1 = v[coord + 2],
c2 = v[coord + 4], v2 = v[coord + 4],
p2 = v[coord + 6], v3 = v[coord + 6],
res = 0; res = 0;
// If val is outside the curve values, no solution is possible. // If val is outside the curve values, no solution is possible.
if ( !(p1 < val && p2 < val && c1 < val && c2 < val || if ( !(v0 < val && v3 < val && v1 < val && v2 < val ||
p1 > val && p2 > val && c1 > val && c2 > val)) { v0 > val && v3 > val && v1 > val && v2 > val)) {
var c = 3 * (c1 - p1), var c = 3 * (v1 - v0),
b = 3 * (c2 - c1) - c, b = 3 * (v2 - v1) - c,
a = p2 - p1 - c - b; a = v3 - v0 - c - b;
res = Numerical.solveCubic(a, b, c, p1 - val, roots, min, max); res = Numerical.solveCubic(a, b, c, v0 - val, roots, min, max);
} }
return res; return res;
}, },
@ -713,12 +713,12 @@ statics: /** @lends Curve */{
getTimeOf: function(v, point) { getTimeOf: function(v, point) {
// Before solving cubics, compare the beginning and end of the curve // Before solving cubics, compare the beginning and end of the curve
// with zero epsilon: // with zero epsilon:
var p1 = new Point(v[0], v[1]), var p0 = new Point(v[0], v[1]),
p2 = new Point(v[6], v[7]), p3 = new Point(v[6], v[7]),
epsilon = /*#=*/Numerical.EPSILON, epsilon = /*#=*/Numerical.EPSILON,
geomEpsilon = /*#=*/Numerical.GEOMETRIC_EPSILON, geomEpsilon = /*#=*/Numerical.GEOMETRIC_EPSILON,
t = point.isClose(p1, epsilon) ? 0 t = point.isClose(p0, epsilon) ? 0
: point.isClose(p2, epsilon) ? 1 : point.isClose(p3, epsilon) ? 1
: null; : null;
if (t === null) { if (t === null) {
// Solve the cubic for both x- and y-coordinates and consider all // Solve the cubic for both x- and y-coordinates and consider all
@ -736,27 +736,27 @@ statics: /** @lends Curve */{
} }
// Since we're comparing with geometric epsilon for any other t along // Since we're comparing with geometric epsilon for any other t along
// the curve, do so as well now for the beginning and end of the curve. // the curve, do so as well now for the beginning and end of the curve.
return point.isClose(p1, geomEpsilon) ? 0 return point.isClose(p0, geomEpsilon) ? 0
: point.isClose(p2, geomEpsilon) ? 1 : point.isClose(p3, geomEpsilon) ? 1
: null; : null;
}, },
getNearestTime: function(v, point) { getNearestTime: function(v, point) {
if (Curve.isStraight(v)) { if (Curve.isStraight(v)) {
var p1x = v[0], p1y = v[1], var x0 = v[0], y0 = v[1],
p2x = v[6], p2y = v[7], x3 = v[6], y3 = v[7],
vx = p2x - p1x, vy = p2y - p1y, vx = x3 - x0, vy = y3 - y0,
det = vx * vx + vy * vy; det = vx * vx + vy * vy;
// Avoid divisions by zero. // Avoid divisions by zero.
if (det === 0) if (det === 0)
return 0; return 0;
// Project the point onto the line and calculate its linear // Project the point onto the line and calculate its linear
// parameter u along the line: u = (point - p1).dot(v) / v.dot(v) // parameter u along the line: u = (point - p1).dot(v) / v.dot(v)
var u = ((point.x - p1x) * vx + (point.y - p1y) * vy) / det; var u = ((point.x - x0) * vx + (point.y - y0) * vy) / det;
return u < /*#=*/Numerical.EPSILON ? 0 return u < /*#=*/Numerical.EPSILON ? 0
: u > /*#=*/(1 - Numerical.EPSILON) ? 1 : u > /*#=*/(1 - Numerical.EPSILON) ? 1
: Curve.getTimeOf(v, : Curve.getTimeOf(v,
new Point(p1x + u * vx, p1y + u * vy)); new Point(x0 + u * vx, y0 + u * vy));
} }
var count = 100, var count = 100,
@ -818,27 +818,27 @@ statics: /** @lends Curve */{
isFlatEnough: function(v, flatness) { isFlatEnough: function(v, flatness) {
// Thanks to Kaspar Fischer and Roger Willcocks for the following: // Thanks to Kaspar Fischer and Roger Willcocks for the following:
// http://hcklbrrfnn.files.wordpress.com/2012/08/bez.pdf // http://hcklbrrfnn.files.wordpress.com/2012/08/bez.pdf
var p1x = v[0], p1y = v[1], var x0 = v[0], y0 = v[1],
c1x = v[2], c1y = v[3], x1 = v[2], y1 = v[3],
c2x = v[4], c2y = v[5], x2 = v[4], y2 = v[5],
p2x = v[6], p2y = v[7], x3 = v[6], y3 = v[7],
ux = 3 * c1x - 2 * p1x - p2x, ux = 3 * x1 - 2 * x0 - x3,
uy = 3 * c1y - 2 * p1y - p2y, uy = 3 * y1 - 2 * y0 - y3,
vx = 3 * c2x - 2 * p2x - p1x, vx = 3 * x2 - 2 * x3 - x0,
vy = 3 * c2y - 2 * p2y - p1y; vy = 3 * y2 - 2 * y3 - y0;
return Math.max(ux * ux, vx * vx) + Math.max(uy * uy, vy * vy) return Math.max(ux * ux, vx * vx) + Math.max(uy * uy, vy * vy)
<= 16 * flatness * flatness; <= 16 * flatness * flatness;
}, },
getArea: function(v) { getArea: function(v) {
// http://objectmix.com/graphics/133553-area-closed-bezier-curve.html // http://objectmix.com/graphics/133553-area-closed-bezier-curve.html
var p1x = v[0], p1y = v[1], var x0 = v[0], y0 = v[1],
c1x = v[2], c1y = v[3], x1 = v[2], y1 = v[3],
c2x = v[4], c2y = v[5], x2 = v[4], y2 = v[5],
p2x = v[6], p2y = v[7]; x3 = v[6], y3 = v[7];
return 3 * ((p2y - p1y) * (c1x + c2x) - (p2x - p1x) * (c1y + c2y) return 3 * ((y3 - y0) * (x1 + x2) - (x3 - x0) * (y1 + y2)
+ c1y * (p1x - c2x) - c1x * (p1y - c2y) + y1 * (x0 - x2) - x1 * (y0 - y2)
+ p2y * (c2x + p1x / 3) - p2x * (c2y + p1y / 3)) / 20; + y3 * (x2 + x0 / 3) - x3 * (y2 + y0 / 3)) / 20;
}, },
getBounds: function(v) { getBounds: function(v) {
@ -1007,11 +1007,11 @@ statics: /** @lends Curve */{
// Produce the static version that handles a curve values array. // Produce the static version that handles a curve values array.
this.statics[name] = function(v) { this.statics[name] = function(v) {
var p1x = v[0], p1y = v[1], var x0 = v[0], y0 = v[1],
p2x = v[6], p2y = v[7]; x3 = v[6], y3 = v[7];
return test(new Line(p1x, p1y, p2x, p2y), return test(new Line(x0, y0, x3, y3),
new Point(v[2] - p1x, v[3] - p1y), new Point(v[2] - x0, v[3] - y0),
new Point(v[4] - p2x, v[5] - p2y)); new Point(v[4] - x3, v[5] - y3));
}; };
}, /** @lends Curve# */{ }, /** @lends Curve# */{
statics: {}, // Filled in the Base.each loop above. statics: {}, // Filled in the Base.each loop above.
@ -1394,18 +1394,18 @@ new function() { // Scope for methods that require private functions
function getLengthIntegrand(v) { function getLengthIntegrand(v) {
// Calculate the coefficients of a Bezier derivative. // Calculate the coefficients of a Bezier derivative.
var p1x = v[0], p1y = v[1], var x0 = v[0], y0 = v[1],
c1x = v[2], c1y = v[3], x1 = v[2], y1 = v[3],
c2x = v[4], c2y = v[5], x2 = v[4], y2 = v[5],
p2x = v[6], p2y = v[7], x3 = v[6], y3 = v[7],
ax = 9 * (c1x - c2x) + 3 * (p2x - p1x), ax = 9 * (x1 - x2) + 3 * (x3 - x0),
bx = 6 * (p1x + c2x) - 12 * c1x, bx = 6 * (x0 + x2) - 12 * x1,
cx = 3 * (c1x - p1x), cx = 3 * (x1 - x0),
ay = 9 * (c1y - c2y) + 3 * (p2y - p1y), ay = 9 * (y1 - y2) + 3 * (y3 - y0),
by = 6 * (p1y + c2y) - 12 * c1y, by = 6 * (y0 + y2) - 12 * y1,
cy = 3 * (c1y - p1y); cy = 3 * (y1 - y0);
return function(t) { return function(t) {
// Calculate quadratic equations of derivatives for x and y // Calculate quadratic equations of derivatives for x and y
@ -1427,38 +1427,38 @@ new function() { // Scope for methods that require private functions
// Do not produce results if parameter is out of range or invalid. // Do not produce results if parameter is out of range or invalid.
if (t == null || t < 0 || t > 1) if (t == null || t < 0 || t > 1)
return null; return null;
var p1x = v[0], p1y = v[1], var x0 = v[0], y0 = v[1],
c1x = v[2], c1y = v[3], x1 = v[2], y1 = v[3],
c2x = v[4], c2y = v[5], x2 = v[4], y2 = v[5],
p2x = v[6], p2y = v[7], x3 = v[6], y3 = v[7],
isZero = Numerical.isZero; isZero = Numerical.isZero;
// If the curve handles are almost zero, reset the control points to the // If the curve handles are almost zero, reset the control points to the
// anchors. // anchors.
if (isZero(c1x - p1x) && isZero(c1y - p1y)) { if (isZero(x1 - x0) && isZero(y1 - y0)) {
c1x = p1x; x1 = x0;
c1y = p1y; y1 = y0;
} }
if (isZero(c2x - p2x) && isZero(c2y - p2y)) { if (isZero(x2 - x3) && isZero(y2 - y3)) {
c2x = p2x; x2 = x3;
c2y = p2y; y2 = y3;
} }
// Calculate the polynomial coefficients. // Calculate the polynomial coefficients.
var cx = 3 * (c1x - p1x), var cx = 3 * (x1 - x0),
bx = 3 * (c2x - c1x) - cx, bx = 3 * (x2 - x1) - cx,
ax = p2x - p1x - cx - bx, ax = x3 - x0 - cx - bx,
cy = 3 * (c1y - p1y), cy = 3 * (y1 - y0),
by = 3 * (c2y - c1y) - cy, by = 3 * (y2 - y1) - cy,
ay = p2y - p1y - cy - by, ay = y3 - y0 - cy - by,
x, y; x, y;
if (type === 0) { if (type === 0) {
// type === 0: getPoint() // type === 0: getPoint()
// Calculate the curve point at parameter value t // Calculate the curve point at parameter value t
// Use special handling at t === 0 / 1, to avoid imprecisions. // Use special handling at t === 0 / 1, to avoid imprecisions.
// See #960 // See #960
x = t === 0 ? p1x : t === 1 ? p2x x = t === 0 ? x0 : t === 1 ? x3
: ((ax * t + bx) * t + cx) * t + p1x; : ((ax * t + bx) * t + cx) * t + x0;
y = t === 0 ? p1y : t === 1 ? p2y y = t === 0 ? y0 : t === 1 ? y3
: ((ay * t + by) * t + cy) * t + p1y; : ((ay * t + by) * t + cy) * t + y0;
} else { } else {
// type === 1: getTangent() // type === 1: getTangent()
// type === 2: getNormal() // type === 2: getNormal()
@ -1476,8 +1476,8 @@ new function() { // Scope for methods that require private functions
x = cx; x = cx;
y = cy; y = cy;
} else if (t > tMax) { } else if (t > tMax) {
x = 3 * (p2x - c2x); x = 3 * (x3 - x2);
y = 3 * (p2y - c2y); y = 3 * (y3 - y2);
} else { } else {
x = (3 * ax * t + 2 * bx) * t + cx; x = (3 * ax * t + 2 * bx) * t + cx;
y = (3 * ay * t + 2 * by) * t + cy; y = (3 * ay * t + 2 * by) * t + cy;
@ -1487,8 +1487,8 @@ new function() { // Scope for methods that require private functions
// or the end, we can use the vector between the handles, // or the end, we can use the vector between the handles,
// but only when normalizing as its weighted length is 0. // but only when normalizing as its weighted length is 0.
if (x === 0 && y === 0 && (t < tMin || t > tMax)) { if (x === 0 && y === 0 && (t < tMin || t > tMax)) {
x = c2x - c1x; x = x2 - x1;
y = c2y - c1y; y = y2 - y1;
} }
// Now normalize x & y // Now normalize x & y
var len = Math.sqrt(x * x + y * y); var len = Math.sqrt(x * x + y * y);
@ -1532,15 +1532,15 @@ new function() { // Scope for methods that require private functions
// considered if they are within 0..1. If the roots are outside, // considered if they are within 0..1. If the roots are outside,
// then we degrade the type of curve down to an 'arch'. // then we degrade the type of curve down to an 'arch'.
var x1 = v[0], y1 = v[1], var x0 = v[0], y0 = v[1],
x2 = v[2], y2 = v[3], x1 = v[2], y1 = v[3],
x3 = v[4], y3 = v[5], x2 = v[4], y2 = v[5],
x4 = v[6], y4 = v[7], x3 = v[6], y3 = v[7],
// Calculate coefficients of I(s, t), of which the roots are // Calculate coefficients of I(s, t), of which the roots are
// inflection points. // inflection points.
a1 = x1 * (y4 - y3) + y1 * (x3 - x4) + x4 * y3 - y4 * x3, a1 = x0 * (y3 - y2) + y0 * (x2 - x3) + x3 * y2 - y3 * x2,
a2 = x2 * (y1 - y4) + y2 * (x4 - x1) + x1 * y4 - y1 * x4, a2 = x1 * (y0 - y3) + y1 * (x3 - x0) + x0 * y3 - y0 * x3,
a3 = x3 * (y2 - y1) + y3 * (x1 - x2) + x2 * y1 - y2 * x1, a3 = x2 * (y1 - y0) + y2 * (x0 - x1) + x1 * y0 - y1 * x0,
d3 = 3 * a3, d3 = 3 * a3,
d2 = d3 - a2, d2 = d3 - a2,
d1 = d2 - a2 + a1, d1 = d2 - a2 + a1,
@ -1607,8 +1607,8 @@ new function() { // Scope for methods that require private functions
c = Curve.subdivide(c, a)[1]; // right c = Curve.subdivide(c, a)[1]; // right
} }
// The length of straight curves can be calculated more easily. // The length of straight curves can be calculated more easily.
var dx = c[6] - c[0], // p2x - p1x var dx = c[6] - c[0], // x3 - x0
dy = c[7] - c[1]; // p2y - p1y dy = c[7] - c[1]; // y3 - y0
return Math.sqrt(dx * dx + dy * dy); return Math.sqrt(dx * dx + dy * dy);
} }
return Numerical.integrate(ds || getLengthIntegrand(v), a, b, return Numerical.integrate(ds || getLengthIntegrand(v), a, b,
@ -1993,24 +1993,24 @@ new function() { // Scope for intersection using bezier fat-line clipping
} }
// Avoid checking curves if completely out of control bounds. // Avoid checking curves if completely out of control bounds.
var epsilon = /*#=*/Numerical.EPSILON, var epsilon = /*#=*/Numerical.EPSILON,
c1p1x = v1[0], c1p1y = v1[1], c1x0 = v1[0], c1y0 = v1[1],
c1h1x = v1[2], c1h1y = v1[3], c1x1 = v1[2], c1y1 = v1[3],
c1h2x = v1[4], c1h2y = v1[5], c1x2 = v1[4], c1y2 = v1[5],
c1p2x = v1[6], c1p2y = v1[7], c1x3 = v1[6], c1y3 = v1[7],
c2p1x = v2[0], c2p1y = v2[1], c2x0 = v2[0], c2y0 = v2[1],
c2h1x = v2[2], c2h1y = v2[3], c2x1 = v2[2], c2y1 = v2[3],
c2h2x = v2[4], c2h2y = v2[5], c2x2 = v2[4], c2y2 = v2[5],
c2p2x = v2[6], c2p2y = v2[7], c2x3 = v2[6], c2y3 = v2[7],
min = Math.min, min = Math.min,
max = Math.max; max = Math.max;
if (!( max(c1p1x, c1h1x, c1h2x, c1p2x) + epsilon > if (!( max(c1x0, c1x1, c1x2, c1x3) + epsilon >
min(c2p1x, c2h1x, c2h2x, c2p2x) && min(c2x0, c2x1, c2x2, c2x3) &&
min(c1p1x, c1h1x, c1h2x, c1p2x) - epsilon < min(c1x0, c1x1, c1x2, c1x3) - epsilon <
max(c2p1x, c2h1x, c2h2x, c2p2x) && max(c2x0, c2x1, c2x2, c2x3) &&
max(c1p1y, c1h1y, c1h2y, c1p2y) + epsilon > max(c1y0, c1y1, c1y2, c1y3) + epsilon >
min(c2p1y, c2h1y, c2h2y, c2p2y) && min(c2y0, c2y1, c2y2, c2y3) &&
min(c1p1y, c1h1y, c1h2y, c1p2y) - epsilon < min(c1y0, c1y1, c1y2, c1y3) - epsilon <
max(c2p1y, c2h1y, c2h2y, c2p2y))) max(c2y0, c2y1, c2y2, c2y3)))
return locations; return locations;
// Now detect and handle overlaps: // Now detect and handle overlaps:
var overlaps = Curve.getOverlaps(v1, v2); var overlaps = Curve.getOverlaps(v1, v2);
@ -2046,18 +2046,18 @@ new function() { // Scope for intersection using bezier fat-line clipping
return locations; return locations;
// Handle the special case where the first curve's start- or end- // Handle the special case where the first curve's start- or end-
// point overlaps with the second curve's start or end-point. // point overlaps with the second curve's start or end-point.
var c1p1 = new Point(c1p1x, c1p1y), var c1p0 = new Point(c1x0, c1y0),
c1p2 = new Point(c1p2x, c1p2y), c1p3 = new Point(c1x3, c1y3),
c2p1 = new Point(c2p1x, c2p1y), c2p0 = new Point(c2x0, c2y0),
c2p2 = new Point(c2p2x, c2p2y); c2p3 = new Point(c2x3, c2y3);
if (c1p1.isClose(c2p1, epsilon)) if (c1p0.isClose(c2p0, epsilon))
addLocation(locations, param, v1, c1, 0, c1p1, v2, c2, 0, c2p1); addLocation(locations, param, v1, c1, 0, c1p0, v2, c2, 0, c2p0);
if (!param.excludeStart && c1p1.isClose(c2p2, epsilon)) if (!param.excludeStart && c1p0.isClose(c2p3, epsilon))
addLocation(locations, param, v1, c1, 0, c1p1, v2, c2, 1, c2p2); addLocation(locations, param, v1, c1, 0, c1p0, v2, c2, 1, c2p3);
if (!param.excludeEnd && c1p2.isClose(c2p1, epsilon)) if (!param.excludeEnd && c1p3.isClose(c2p0, epsilon))
addLocation(locations, param, v1, c1, 1, c1p2, v2, c2, 0, c2p1); addLocation(locations, param, v1, c1, 1, c1p3, v2, c2, 0, c2p0);
if (c1p2.isClose(c2p2, epsilon)) if (c1p3.isClose(c2p3, epsilon))
addLocation(locations, param, v1, c1, 1, c1p2, v2, c2, 1, c2p2); addLocation(locations, param, v1, c1, 1, c1p3, v2, c2, 1, c2p3);
return locations; return locations;
}, },