Rework handling of weighted and normalized curve tangents and normals.

Relates to #563
This commit is contained in:
Jürg Lehni 2015-08-19 17:15:41 +02:00
parent eb8c5b4a3e
commit da82116501
7 changed files with 164 additions and 87 deletions

View file

@ -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));
} }
} }
} }

View file

@ -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() {

View file

@ -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));

View file

@ -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;

View file

@ -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);
}; };
}, {}) }, {})
); );

View file

@ -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);');
} }
}); });

View file

@ -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);
} }
}); });