mirror of
https://github.com/scratchfoundation/paper.js.git
synced 2025-01-01 02:38:43 -05:00
Rework handling of weighted and normalized curve tangents and normals.
Relates to #563
This commit is contained in:
parent
eb8c5b4a3e
commit
da82116501
7 changed files with 164 additions and 87 deletions
|
@ -518,8 +518,7 @@ statics: {
|
||||||
return values;
|
return values;
|
||||||
},
|
},
|
||||||
|
|
||||||
// TODO: Instead of constants for type, use a "enum" and code substitution.
|
_evaluate: function(v, t, type, normalized) {
|
||||||
evaluate: function(v, t, type) {
|
|
||||||
// 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;
|
||||||
|
@ -552,31 +551,45 @@ statics: {
|
||||||
// 1: tangent, 1st derivative
|
// 1: tangent, 1st derivative
|
||||||
// 2: normal, 1st derivative
|
// 2: normal, 1st derivative
|
||||||
// 3: curvature, 1st derivative & 2nd derivative
|
// 3: curvature, 1st derivative & 2nd derivative
|
||||||
|
// Simply use the derivation of the bezier function for both
|
||||||
|
// the x and y coordinates:
|
||||||
// Prevent tangents and normals of length 0:
|
// Prevent tangents and normals of length 0:
|
||||||
// http://stackoverflow.com/questions/10506868/
|
// http://stackoverflow.com/questions/10506868/
|
||||||
if (t < tolerance && c1x === p1x && c1y === p1y
|
if (t < tolerance) {
|
||||||
|| t > 1 - tolerance && c2x === p2x && c2y === p2y) {
|
|
||||||
x = c2x - c1x;
|
|
||||||
y = c2y - c1y;
|
|
||||||
} else if (t < tolerance) {
|
|
||||||
x = cx;
|
x = cx;
|
||||||
y = cy;
|
y = cy;
|
||||||
} else if (t > 1 - tolerance) {
|
} else if (t > 1 - tolerance) {
|
||||||
x = 3 * (p2x - c2x);
|
x = 3 * (p2x - c2x);
|
||||||
y = 3 * (p2y - c2y);
|
y = 3 * (p2y - c2y);
|
||||||
} else {
|
} else {
|
||||||
// Simply use the derivation of the bezier function for both
|
|
||||||
// the x and y coordinates:
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
if (normalized) {
|
||||||
|
// When the tangent at t is zero and we're at the beginning
|
||||||
|
// 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 < tolerance || t > 1 - tolerance)) {
|
||||||
|
x = c2x - c1x;
|
||||||
|
y = c2y - c1y;
|
||||||
|
}
|
||||||
|
// Now normalize x & y
|
||||||
|
var len = Math.sqrt(x * x + y * y);
|
||||||
|
x /= len;
|
||||||
|
y /= len;
|
||||||
|
}
|
||||||
if (type === 3) {
|
if (type === 3) {
|
||||||
// Calculate 2nd derivative, and curvature from there:
|
// Calculate 2nd derivative, and curvature from there:
|
||||||
// http://cagd.cs.byu.edu/~557/text/ch2.pdf page#31
|
// http://cagd.cs.byu.edu/~557/text/ch2.pdf page#31
|
||||||
// k = |dx * d2y - dy * d2x| / (( dx^2 + dy^2 )^(3/2))
|
// k = |dx * d2y - dy * d2x| / (( dx^2 + dy^2 )^(3/2))
|
||||||
var x2 = 6 * ax * t + 2 * bx,
|
var x2 = 6 * ax * t + 2 * bx,
|
||||||
y2 = 6 * ay * t + 2 * by;
|
y2 = 6 * ay * t + 2 * by,
|
||||||
return (x * y2 - y * x2) / Math.pow(x * x + y * y, 3 / 2);
|
d = Math.pow(x * x + y * y, 3 / 2);
|
||||||
|
// For JS optimizations we always return a Point, although
|
||||||
|
// curvature is just a numeric value, stored in x:
|
||||||
|
x = d !== 0 ? (x * y2 - y * x2) / d : 0;
|
||||||
|
y = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -584,6 +597,30 @@ statics: {
|
||||||
return type === 2 ? new Point(y, -x) : new Point(x, y);
|
return type === 2 ? new Point(y, -x) : new Point(x, y);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getPoint: function(v, t) {
|
||||||
|
return Curve._evaluate(v, t, 0, false);
|
||||||
|
},
|
||||||
|
|
||||||
|
getTangent: function(v, t) {
|
||||||
|
return Curve._evaluate(v, t, 1, true);
|
||||||
|
},
|
||||||
|
|
||||||
|
getWeightedTangent: function(v, t) {
|
||||||
|
return Curve._evaluate(v, t, 1, false);
|
||||||
|
},
|
||||||
|
|
||||||
|
getNormal: function(v, t) {
|
||||||
|
return Curve._evaluate(v, t, 2, true);
|
||||||
|
},
|
||||||
|
|
||||||
|
getWeightedNormal: function(v, t) {
|
||||||
|
return Curve._evaluate(v, t, 2, false);
|
||||||
|
},
|
||||||
|
|
||||||
|
getCurvature: function(v, t) {
|
||||||
|
return Curve._evaluate(v, t, 3, false).x;
|
||||||
|
},
|
||||||
|
|
||||||
subdivide: function(v, t) {
|
subdivide: function(v, t) {
|
||||||
var p1x = v[0], p1y = v[1],
|
var p1x = v[0], p1y = v[1],
|
||||||
c1x = v[2], c1y = v[3],
|
c1x = v[2], c1y = v[3],
|
||||||
|
@ -835,22 +872,18 @@ statics: {
|
||||||
* @type Rectangle
|
* @type Rectangle
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
}), Base.each(['getPoint', 'getTangent', 'getNormal', 'getCurvature'],
|
}), Base.each(['getPoint', 'getTangent', 'getNormal', 'getWeightedTangent',
|
||||||
|
'getWeightedNormal', 'getCurvature'],
|
||||||
// Note: Although Curve.getBounds() exists, we are using Path.getBounds() to
|
// Note: Although Curve.getBounds() exists, we are using Path.getBounds() to
|
||||||
// determine the bounds of Curve objects with defined segment1 and segment2
|
// determine the bounds of Curve objects with defined segment1 and segment2
|
||||||
// values Curve.getBounds() can be used directly on curve arrays, without
|
// values Curve.getBounds() can be used directly on curve arrays, without
|
||||||
// the need to create a Curve object first, as required by the code that
|
// the need to create a Curve object first, as required by the code that
|
||||||
// finds path interesections.
|
// finds path interesections.
|
||||||
function(name, index) {
|
function(name) {
|
||||||
this[name + 'At'] = function(offset, isParameter) {
|
this[name + 'At'] = function(offset, isParameter) {
|
||||||
var values = this.getValues();
|
var values = this.getValues();
|
||||||
return Curve.evaluate(values, isParameter
|
return Curve[name](values, isParameter ? offset
|
||||||
? offset : Curve.getParameterAt(values, offset, 0), index);
|
: Curve.getParameterAt(values, offset, 0));
|
||||||
};
|
|
||||||
// Deprecated and undocumented, but keep around for now.
|
|
||||||
// TODO: Remove once enough time has passed (28.01.2013)
|
|
||||||
this[name] = function(parameter) {
|
|
||||||
return Curve.evaluate(this.getValues(), parameter, index);
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
/** @lends Curve# */{
|
/** @lends Curve# */{
|
||||||
|
@ -936,8 +969,7 @@ statics: {
|
||||||
|
|
||||||
function refine(t) {
|
function refine(t) {
|
||||||
if (t >= 0 && t <= 1) {
|
if (t >= 0 && t <= 1) {
|
||||||
var dist = point.getDistance(
|
var dist = point.getDistance(Curve.getPoint(values, t), true);
|
||||||
Curve.evaluate(values, t, 0), true);
|
|
||||||
if (dist < minDist) {
|
if (dist < minDist) {
|
||||||
minDist = dist;
|
minDist = dist;
|
||||||
minT = t;
|
minT = t;
|
||||||
|
@ -955,7 +987,7 @@ statics: {
|
||||||
if (!refine(minT - step) && !refine(minT + step))
|
if (!refine(minT - step) && !refine(minT + step))
|
||||||
step /= 2;
|
step /= 2;
|
||||||
}
|
}
|
||||||
var pt = Curve.evaluate(values, minT, 0);
|
var pt = Curve.getPoint(values, minT);
|
||||||
return new CurveLocation(this, minT, pt, null, null, null,
|
return new CurveLocation(this, minT, pt, null, null, null,
|
||||||
point.getDistance(pt));
|
point.getDistance(pt));
|
||||||
},
|
},
|
||||||
|
@ -973,11 +1005,12 @@ statics: {
|
||||||
* parameter if {@code isParameter} is {@code true}
|
* parameter if {@code isParameter} is {@code true}
|
||||||
* @param {Boolean} [isParameter=false] pass {@code true} if {@code offset}
|
* @param {Boolean} [isParameter=false] pass {@code true} if {@code offset}
|
||||||
* is a curve time parameter
|
* is a curve time parameter
|
||||||
* @return {Point} the point on the curve at the specified offset
|
* @return {Point} the point on the curve at the given offset
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the tangent vector of the curve at the given offset.
|
* Calculates the normalized tangent vector of the curve at the given
|
||||||
|
* offset.
|
||||||
*
|
*
|
||||||
* @name Curve#getTangentAt
|
* @name Curve#getTangentAt
|
||||||
* @function
|
* @function
|
||||||
|
@ -985,7 +1018,7 @@ statics: {
|
||||||
* parameter if {@code isParameter} is {@code true}
|
* parameter if {@code isParameter} is {@code true}
|
||||||
* @param {Boolean} [isParameter=false] pass {@code true} if {@code offset}
|
* @param {Boolean} [isParameter=false] pass {@code true} if {@code offset}
|
||||||
* is a curve time parameter
|
* is a curve time parameter
|
||||||
* @return {Point} the tangent of the curve at the specified offset
|
* @return {Point} the normalized tangent of the curve at the given offset
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -997,7 +1030,33 @@ statics: {
|
||||||
* parameter if {@code isParameter} is {@code true}
|
* parameter if {@code isParameter} is {@code true}
|
||||||
* @param {Boolean} [isParameter=false] pass {@code true} if {@code offset}
|
* @param {Boolean} [isParameter=false] pass {@code true} if {@code offset}
|
||||||
* is a curve time parameter
|
* is a curve time parameter
|
||||||
* @return {Point} the normal of the curve at the specified offset
|
* @return {Point} the normal of the curve at the given offset
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the weighted tangent vector of the curve at the given
|
||||||
|
* offset, its length reflecting the curve velocity at that location.
|
||||||
|
*
|
||||||
|
* @name Curve#getWeightedTangentAt
|
||||||
|
* @function
|
||||||
|
* @param {Number} offset the offset on the curve, or the curve time
|
||||||
|
* parameter if {@code isParameter} is {@code true}
|
||||||
|
* @param {Boolean} [isParameter=false] pass {@code true} if {@code offset}
|
||||||
|
* is a curve time parameter
|
||||||
|
* @return {Point} the weighted tangent of the curve at the given offset
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the weighted normal vector of the curve at the given offset,
|
||||||
|
* its length reflecting the curve velocity at that location.
|
||||||
|
*
|
||||||
|
* @name Curve#getWeightedNormalAt
|
||||||
|
* @function
|
||||||
|
* @param {Number} offset the offset on the curve, or the curve time
|
||||||
|
* parameter if {@code isParameter} is {@code true}
|
||||||
|
* @param {Boolean} [isParameter=false] pass {@code true} if {@code offset}
|
||||||
|
* is a curve time parameter
|
||||||
|
* @return {Point} the weighted normal of the curve at the given offset
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1012,7 +1071,7 @@ statics: {
|
||||||
* parameter if {@code isParameter} is {@code true}
|
* parameter if {@code isParameter} is {@code true}
|
||||||
* @param {Boolean} [isParameter=false] pass {@code true} if {@code offset}
|
* @param {Boolean} [isParameter=false] pass {@code true} if {@code offset}
|
||||||
* is a curve time parameter
|
* is a curve time parameter
|
||||||
* @return {Number} the curvature of the curve at the specified offset
|
* @return {Number} the curvature of the curve at the given offset
|
||||||
*/
|
*/
|
||||||
}),
|
}),
|
||||||
new function() { // Scope for methods that require numerical integration
|
new function() { // Scope for methods that require numerical integration
|
||||||
|
@ -1210,12 +1269,12 @@ new function() { // Scope for methods that require numerical integration
|
||||||
t2 = uMin + (uMax - uMin) / 2;
|
t2 = uMin + (uMax - uMin) / 2;
|
||||||
if (reverse) {
|
if (reverse) {
|
||||||
addLocation(locations, include,
|
addLocation(locations, include,
|
||||||
curve2, t2, Curve.evaluate(v2, t2, 0),
|
curve2, t2, Curve.getPoint(v2, t2),
|
||||||
curve1, t1, Curve.evaluate(v1, t1, 0));
|
curve1, t1, Curve.getPoint(v1, t1));
|
||||||
} else {
|
} else {
|
||||||
addLocation(locations, include,
|
addLocation(locations, include,
|
||||||
curve1, t1, Curve.evaluate(v1, t1, 0),
|
curve1, t1, Curve.getPoint(v1, t1),
|
||||||
curve2, t2, Curve.evaluate(v2, t2, 0));
|
curve2, t2, Curve.getPoint(v2, t2));
|
||||||
}
|
}
|
||||||
} else if (tDiff > 0) { // Iterate
|
} else if (tDiff > 0) { // Iterate
|
||||||
addCurveIntersections(v2, v1, curve2, curve1, locations, include,
|
addCurveIntersections(v2, v1, curve2, curve1, locations, include,
|
||||||
|
@ -1393,7 +1452,7 @@ new function() { // Scope for methods that require numerical integration
|
||||||
// happen with lines, in which case we should not be here.
|
// happen with lines, in which case we should not be here.
|
||||||
for (var i = 0; i < count; i++) {
|
for (var i = 0; i < count; i++) {
|
||||||
var tc = roots[i],
|
var tc = roots[i],
|
||||||
x = Curve.evaluate(rvc, tc, 0).x;
|
x = Curve.getPoint(rvc, tc).x;
|
||||||
// We do have a point on the infinite line. Check if it falls on
|
// We do have a point on the infinite line. Check if it falls on
|
||||||
// the line *segment*.
|
// the line *segment*.
|
||||||
if (x >= 0 && x <= rlx2) {
|
if (x >= 0 && x <= rlx2) {
|
||||||
|
@ -1402,8 +1461,8 @@ new function() { // Scope for methods that require numerical integration
|
||||||
t1 = flip ? tl : tc,
|
t1 = flip ? tl : tc,
|
||||||
t2 = flip ? tc : tl;
|
t2 = flip ? tc : tl;
|
||||||
addLocation(locations, include,
|
addLocation(locations, include,
|
||||||
curve1, t1, Curve.evaluate(v1, t1, 0),
|
curve1, t1, Curve.getPoint(v1, t1),
|
||||||
curve2, t2, Curve.evaluate(v2, t2, 0));
|
curve2, t2, Curve.getPoint(v2, t2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -307,7 +307,8 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{
|
||||||
parts.push('distance: ' + f.number(this._distance));
|
parts.push('distance: ' + f.number(this._distance));
|
||||||
return '{ ' + parts.join(', ') + ' }';
|
return '{ ' + parts.join(', ') + ' }';
|
||||||
}
|
}
|
||||||
}, Base.each(['getTangent', 'getNormal', 'getCurvature'], function(name) {
|
}, Base.each(['getTangent', 'getNormal', 'getWeightedTangent',
|
||||||
|
'getWeightedNormal', 'getCurvature'], function(name) {
|
||||||
// Produce getters for #getTangent() / #getNormal() / #getCurvature()
|
// Produce getters for #getTangent() / #getNormal() / #getCurvature()
|
||||||
var get = name + 'At';
|
var get = name + 'At';
|
||||||
this[name] = function() {
|
this[name] = function() {
|
||||||
|
|
|
@ -996,7 +996,7 @@ var Path = PathItem.extend(/** @lends Path# */{
|
||||||
// Iterate over path and evaluate and add points at given offsets
|
// Iterate over path and evaluate and add points at given offsets
|
||||||
var segments = [];
|
var segments = [];
|
||||||
while (pos <= end) {
|
while (pos <= end) {
|
||||||
segments.push(new Segment(iterator.evaluate(pos, 0)));
|
segments.push(new Segment(iterator.getPointAt(pos)));
|
||||||
pos += step;
|
pos += step;
|
||||||
}
|
}
|
||||||
this.setSegments(segments);
|
this.setSegments(segments);
|
||||||
|
@ -1638,7 +1638,8 @@ var Path = PathItem.extend(/** @lends Path# */{
|
||||||
|
|
||||||
// TODO: intersects(item)
|
// TODO: intersects(item)
|
||||||
// TODO: contains(item)
|
// TODO: contains(item)
|
||||||
}, Base.each(['getPoint', 'getTangent', 'getNormal', 'getCurvature'],
|
}, Base.each(['getPoint', 'getTangent', 'getNormal', 'getWeightedTangent',
|
||||||
|
'getWeightedNormal', 'getCurvature'],
|
||||||
function(name) {
|
function(name) {
|
||||||
this[name + 'At'] = function(offset, isParameter) {
|
this[name + 'At'] = function(offset, isParameter) {
|
||||||
var loc = this.getLocationAt(offset, isParameter);
|
var loc = this.getLocationAt(offset, isParameter);
|
||||||
|
@ -1713,8 +1714,9 @@ var Path = PathItem.extend(/** @lends Path# */{
|
||||||
if (isParameter) {
|
if (isParameter) {
|
||||||
// offset consists of curve index and curve parameter, before and
|
// offset consists of curve index and curve parameter, before and
|
||||||
// after the fractional digit.
|
// after the fractional digit.
|
||||||
var index = ~~offset; // = Math.floor()
|
var index = ~~offset, // = Math.floor()
|
||||||
return curves[index].getLocationAt(offset - index, true);
|
curve = curves[index];
|
||||||
|
return curve ? curve.getLocationAt(offset - index, true) : null;
|
||||||
}
|
}
|
||||||
for (var i = 0, l = curves.length; i < l; i++) {
|
for (var i = 0, l = curves.length; i < l; i++) {
|
||||||
var start = length,
|
var start = length,
|
||||||
|
@ -1796,14 +1798,14 @@ var Path = PathItem.extend(/** @lends Path# */{
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the tangent vector of the path at the given offset.
|
* Calculates the normalized tangent vector of the path at the given offset.
|
||||||
*
|
*
|
||||||
* @name Path#getTangentAt
|
* @name Path#getTangentAt
|
||||||
* @function
|
* @function
|
||||||
* @param {Number} offset the offset on the path, where {@code 0} is at
|
* @param {Number} offset the offset on the path, where {@code 0} is at
|
||||||
* the beginning of the path and {@link Path#length} at the end
|
* the beginning of the path and {@link Path#length} at the end
|
||||||
* @param {Boolean} [isParameter=false]
|
* @param {Boolean} [isParameter=false]
|
||||||
* @return {Point} the tangent vector at the given offset
|
* @return {Point} the normalized tangent vector at the given offset
|
||||||
*
|
*
|
||||||
* @example {@paperscript height=150}
|
* @example {@paperscript height=150}
|
||||||
* // Working with the tangent vector at a given offset:
|
* // Working with the tangent vector at a given offset:
|
||||||
|
@ -1823,11 +1825,9 @@ var Path = PathItem.extend(/** @lends Path# */{
|
||||||
* // Find the point on the path:
|
* // Find the point on the path:
|
||||||
* var point = path.getPointAt(offset);
|
* var point = path.getPointAt(offset);
|
||||||
*
|
*
|
||||||
* // Find the tangent vector at the given offset:
|
* // Find the tangent vector at the given offset
|
||||||
* var tangent = path.getTangentAt(offset);
|
* // and give it a length of 60:
|
||||||
*
|
* var tangent = path.getTangentAt(offset) * 60;
|
||||||
* // Make the tangent vector 60pt long:
|
|
||||||
* tangent.length = 60;
|
|
||||||
*
|
*
|
||||||
* var line = new Path({
|
* var line = new Path({
|
||||||
* segments: [point, point + tangent],
|
* segments: [point, point + tangent],
|
||||||
|
@ -1853,11 +1853,9 @@ var Path = PathItem.extend(/** @lends Path# */{
|
||||||
* // Find the point on the path at the given offset:
|
* // Find the point on the path at the given offset:
|
||||||
* var point = path.getPointAt(offset);
|
* var point = path.getPointAt(offset);
|
||||||
*
|
*
|
||||||
* // Find the normal vector on the path at the given offset:
|
* // Find the tangent vector on the path at the given offset
|
||||||
* var tangent = path.getTangentAt(offset);
|
* // and give it a length of 60:
|
||||||
*
|
* var tangent = path.getTangentAt(offset) * 60;
|
||||||
* // Make the tangent vector 60pt long:
|
|
||||||
* tangent.length = 60;
|
|
||||||
*
|
*
|
||||||
* var line = new Path({
|
* var line = new Path({
|
||||||
* segments: [point, point + tangent],
|
* segments: [point, point + tangent],
|
||||||
|
@ -1894,11 +1892,9 @@ var Path = PathItem.extend(/** @lends Path# */{
|
||||||
* // Find the point on the path:
|
* // Find the point on the path:
|
||||||
* var point = path.getPointAt(offset);
|
* var point = path.getPointAt(offset);
|
||||||
*
|
*
|
||||||
* // Find the normal vector at the given offset:
|
* // Find the normal vector on the path at the given offset
|
||||||
* var normal = path.getNormalAt(offset);
|
* // and give it a length of 30:
|
||||||
*
|
* var normal = path.getNormalAt(offset) * 30;
|
||||||
* // Make the normal vector 30pt long:
|
|
||||||
* normal.length = 30;
|
|
||||||
*
|
*
|
||||||
* var line = new Path({
|
* var line = new Path({
|
||||||
* segments: [point, point + normal],
|
* segments: [point, point + normal],
|
||||||
|
@ -1924,11 +1920,9 @@ var Path = PathItem.extend(/** @lends Path# */{
|
||||||
* // Find the point on the path at the given offset:
|
* // Find the point on the path at the given offset:
|
||||||
* var point = path.getPointAt(offset);
|
* var point = path.getPointAt(offset);
|
||||||
*
|
*
|
||||||
* // Find the normal vector on the path at the given offset:
|
* // Find the normal vector on the path at the given offset
|
||||||
* var normal = path.getNormalAt(offset);
|
* // and give it a length of 30:
|
||||||
*
|
* var normal = path.getNormalAt(offset) * 30;
|
||||||
* // Make the normal vector 30pt long:
|
|
||||||
* normal.length = 30;
|
|
||||||
*
|
*
|
||||||
* var line = new Path({
|
* var line = new Path({
|
||||||
* segments: [point, point + normal],
|
* segments: [point, point + normal],
|
||||||
|
@ -1937,6 +1931,28 @@ var Path = PathItem.extend(/** @lends Path# */{
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the weighted tangent vector of the path at the given offset.
|
||||||
|
*
|
||||||
|
* @name Path#getWeightedTangentAt
|
||||||
|
* @function
|
||||||
|
* @param {Number} offset the offset on the path, where {@code 0} is at
|
||||||
|
* the beginning of the path and {@link Path#length} at the end
|
||||||
|
* @param {Boolean} [isParameter=false]
|
||||||
|
* @return {Point} the weighted tangent vector at the given offset
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the weighted normal vector of the path at the given offset.
|
||||||
|
*
|
||||||
|
* @name Path#getWeightedNormalAt
|
||||||
|
* @function
|
||||||
|
* @param {Number} offset the offset on the path, where {@code 0} is at
|
||||||
|
* the beginning of the path and {@link Path#length} at the end
|
||||||
|
* @param {Boolean} [isParameter=false]
|
||||||
|
* @return {Point} the weighted normal vector at the given offset
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the curvature of the path at the given offset. Curvatures
|
* Calculates the curvature of the path at the given offset. Curvatures
|
||||||
* indicate how sharply a path changes direction. A straight line has zero
|
* indicate how sharply a path changes direction. A straight line has zero
|
||||||
|
@ -1949,7 +1965,7 @@ var Path = PathItem.extend(/** @lends Path# */{
|
||||||
* the beginning of the path and {@link Path#length} at the end
|
* the beginning of the path and {@link Path#length} at the end
|
||||||
* @param {Boolean} [isParameter=false]
|
* @param {Boolean} [isParameter=false]
|
||||||
* @return {Number} the normal vector at the given offset
|
* @return {Number} the normal vector at the given offset
|
||||||
*
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the nearest location on the path to the specified point.
|
* Returns the nearest location on the path to the specified point.
|
||||||
|
@ -2848,7 +2864,7 @@ statics: {
|
||||||
// Calculate the corner points of butt and square caps
|
// Calculate the corner points of butt and square caps
|
||||||
var point = segment._point,
|
var point = segment._point,
|
||||||
loc = segment.getLocation(),
|
loc = segment.getLocation(),
|
||||||
normal = loc.getNormal().normalize(radius);
|
normal = loc.getNormal().multiply(radius); // normal is normalized
|
||||||
if (area) {
|
if (area) {
|
||||||
addPoint(point.subtract(normal));
|
addPoint(point.subtract(normal));
|
||||||
addPoint(point.add(normal));
|
addPoint(point.add(normal));
|
||||||
|
|
|
@ -271,7 +271,7 @@ PathItem.inject(new function() {
|
||||||
var values = curves[i].values;
|
var values = curves[i].values;
|
||||||
if (Curve.solveCubic(values, 0, px, roots, 0, 1) > 0) {
|
if (Curve.solveCubic(values, 0, px, roots, 0, 1) > 0) {
|
||||||
for (var j = roots.length - 1; j >= 0; j--) {
|
for (var j = roots.length - 1; j >= 0; j--) {
|
||||||
var y = Curve.evaluate(values, roots[j], 0).y;
|
var y = Curve.getPoint(values, roots[j]).y;
|
||||||
if (y < yBefore && y > yTop) {
|
if (y < yBefore && y > yTop) {
|
||||||
yTop = y;
|
yTop = y;
|
||||||
} else if (y > yAfter && y < yBottom) {
|
} else if (y > yAfter && y < yBottom) {
|
||||||
|
@ -325,8 +325,8 @@ PathItem.inject(new function() {
|
||||||
// sure we're still on the same loop.
|
// sure we're still on the same loop.
|
||||||
|| t < tMin && prevT > tMax
|
|| t < tMin && prevT > tMax
|
||||||
&& curve.previous === prevCurve)) {
|
&& curve.previous === prevCurve)) {
|
||||||
var x = Curve.evaluate(values, t, 0).x,
|
var x = Curve.getPoint(values, t).x,
|
||||||
slope = Curve.evaluate(values, t, 1).y,
|
slope = Curve.getTangent(values, t).y,
|
||||||
counted = false;
|
counted = false;
|
||||||
// Take care of cases where the curve and the preceding
|
// Take care of cases where the curve and the preceding
|
||||||
// curve merely touches the ray towards +-x direction,
|
// curve merely touches the ray towards +-x direction,
|
||||||
|
@ -334,11 +334,11 @@ PathItem.inject(new function() {
|
||||||
// This essentially is not a crossing.
|
// This essentially is not a crossing.
|
||||||
if (Numerical.isZero(slope) && !Curve.isLinear(values)
|
if (Numerical.isZero(slope) && !Curve.isLinear(values)
|
||||||
// Does the slope over curve beginning change?
|
// Does the slope over curve beginning change?
|
||||||
|| t < tMin && slope * Curve.evaluate(
|
|| t < tMin && slope * Curve.getTangent(
|
||||||
curve.previous.values, 1, 1).y < 0
|
curve.previous.values, 1).y < 0
|
||||||
// Does the slope over curve end change?
|
// Does the slope over curve end change?
|
||||||
|| t > tMax && slope * Curve.evaluate(
|
|| t > tMax && slope * Curve.getTangent(
|
||||||
curve.next.values, 0, 1).y < 0) {
|
curve.next.values, 0).y < 0) {
|
||||||
if (testContains && x >= xBefore && x <= xAfter) {
|
if (testContains && x >= xBefore && x <= xAfter) {
|
||||||
++windLeft;
|
++windLeft;
|
||||||
++windRight;
|
++windRight;
|
||||||
|
@ -695,7 +695,7 @@ Path.inject(/** @lends Path# */{
|
||||||
|| y >= values[7] && y <= values[1])
|
|| y >= values[7] && y <= values[1])
|
||||||
&& Curve.solveCubic(values, 1, y, roots, 0, 1) > 0) {
|
&& Curve.solveCubic(values, 1, y, roots, 0, 1) > 0) {
|
||||||
for (var j = roots.length - 1; j >= 0; j--)
|
for (var j = roots.length - 1; j >= 0; j--)
|
||||||
xIntercepts.push(Curve.evaluate(values, roots[j], 0).x);
|
xIntercepts.push(Curve.getPoint(values, roots[j]).x);
|
||||||
}
|
}
|
||||||
if (xIntercepts.length > 1)
|
if (xIntercepts.length > 1)
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -134,11 +134,6 @@ var PathIterator = Base.extend({
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
evaluate: function(offset, type) {
|
|
||||||
var param = this.getParameterAt(offset);
|
|
||||||
return Curve.evaluate(this.curves[param.index], param.value, type);
|
|
||||||
},
|
|
||||||
|
|
||||||
drawPart: function(ctx, from, to) {
|
drawPart: function(ctx, from, to) {
|
||||||
from = this.getParameterAt(from);
|
from = this.getParameterAt(from);
|
||||||
to = this.getParameterAt(to);
|
to = this.getParameterAt(to);
|
||||||
|
@ -151,10 +146,12 @@ var PathIterator = Base.extend({
|
||||||
ctx.bezierCurveTo.apply(ctx, curve.slice(2));
|
ctx.bezierCurveTo.apply(ctx, curve.slice(2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, Base.each(['getPoint', 'getTangent', 'getNormal', 'getCurvature'],
|
}, Base.each(['getPoint', 'getTangent', 'getNormal', 'getWeightedTangent',
|
||||||
function(name, index) {
|
'getWeightedNormal', 'getCurvature'],
|
||||||
this[name + 'At'] = function(offset) {
|
function(name) {
|
||||||
return this.evaluate(offset, index);
|
this[name + 'At'] = function(offset, weighted) {
|
||||||
|
var param = this.getParameterAt(offset);
|
||||||
|
return Curve[name](this.curves[param.index], param.value, weighted);
|
||||||
};
|
};
|
||||||
}, {})
|
}, {})
|
||||||
);
|
);
|
||||||
|
|
|
@ -76,8 +76,10 @@ test('Curve#getTangentAt()', function() {
|
||||||
|
|
||||||
for (var i = 0; i < tangents.length; i++) {
|
for (var i = 0; i < tangents.length; i++) {
|
||||||
var entry = tangents[i];
|
var entry = tangents[i];
|
||||||
equals(curve.getTangentAt(entry[0], true), entry[1],
|
equals(curve.getTangentAt(entry[0], true), entry[1].normalize(),
|
||||||
'curve.getTangentAt(' + entry[0] + ', true);');
|
'curve.getTangentAt(' + entry[0] + ', true);');
|
||||||
|
equals(curve.getWeightedTangentAt(entry[0], true), entry[1],
|
||||||
|
'curve.getWeightedTangentAt(' + entry[0] + ', true);');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -97,8 +99,10 @@ test('Curve#getNormalAt()', function() {
|
||||||
|
|
||||||
for (var i = 0; i < normals.length; i++) {
|
for (var i = 0; i < normals.length; i++) {
|
||||||
var entry = normals[i];
|
var entry = normals[i];
|
||||||
equals(curve.getNormalAt(entry[0], true), entry[1],
|
equals(curve.getNormalAt(entry[0], true), entry[1].normalize(),
|
||||||
'curve.getNormalAt(' + entry[0] + ', true);');
|
'curve.getNormalAt(' + entry[0] + ', true);');
|
||||||
|
equals(curve.getWeightedNormalAt(entry[0], true), entry[1],
|
||||||
|
'curve.getWeightedNormalAt(' + entry[0] + ', true);');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -168,8 +168,8 @@ test('Path#contains() (Rectangle Contours)', function() {
|
||||||
curves = path.getCurves();
|
curves = path.getCurves();
|
||||||
|
|
||||||
for (var i = 0; i < curves.length; i++) {
|
for (var i = 0; i < curves.length; i++) {
|
||||||
testPoint(path, curves[i].getPoint(0), true);
|
testPoint(path, curves[i].getPointAt(0, true), true);
|
||||||
testPoint(path, curves[i].getPoint(0.5), true);
|
testPoint(path, curves[i].getPointAt(0.5, true), true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -181,8 +181,8 @@ test('Path#contains() (Rotated Rectangle Contours)', function() {
|
||||||
path.rotate(45);
|
path.rotate(45);
|
||||||
|
|
||||||
for (var i = 0; i < curves.length; i++) {
|
for (var i = 0; i < curves.length; i++) {
|
||||||
testPoint(path, curves[i].getPoint(0), true);
|
testPoint(path, curves[i].getPointAt(0, true), true);
|
||||||
testPoint(path, curves[i].getPoint(0.5), true);
|
testPoint(path, curves[i].getPointAt(0.5, true), true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue