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