Rename curve-time in API from 'parameter' to 'time'

And create separate versions of methods that receive curve-time arguments instead of offsets.

Curve#getNormalAt(time, true) -> #getNormalAtTime(true)
Curve#divide() -> #divideAt(offset) / #divideAtTime(time)
Curve#split() -> #splitAt(offset) / #splitAtTime(time)
Curve#getParameterAt(offset) -> #getTimeAt(offset)
Curve#getParameterOf(point) -> getTimeOf(point)
Curve#getPointAt(time, true) -> #getPointAtTime(time)
Curve#getTangentAt(time, true) -> #getTangenttTime(time)
Curve#getNormalAt(time, true) -> #getNormalAtTime(time)
Curve#getCurvatureAt(time, true) -> #getCurvatureAtTime(time)
CurveLocation#parameter -> #time
Path#split(offset/location) -> #splitAt(offset/location)

Closes #563
This commit is contained in:
Jürg Lehni 2016-02-02 11:59:53 +01:00
parent 45595b2b1d
commit 340a1e2a5f
12 changed files with 447 additions and 385 deletions

View file

@ -257,7 +257,7 @@
// pathB.style = pathStyleBoolean; // pathB.style = pathStyleBoolean;
// // var ixs = pathA.getIntersections(pathB); // // var ixs = pathA.getIntersections(pathB);
// // ixs.map(function(a) { console.log("(" + a.path.id + " , " + a.curve.index + " , "+ a.parameter +")"); // // ixs.map(function(a) { console.log("(" + a.path.id + " , " + a.curve.index + " , "+ a.time +")");
// // markPoint(a.point, " ") }); // // markPoint(a.point, " ") });
// // ixs.map(function(a) { markPoint(a.point, " "); }); // // ixs.map(function(a) { markPoint(a.point, " "); });
@ -402,7 +402,7 @@
t = t || s.index; t = t || s.index;
if (remove === undefined) { remove = true; } if (remove === undefined) { remove = true; }
var crv = s.curve; var crv = s.curve;
var t1 = crv.getNormalAt(0, true).normalize(10); var t1 = crv.getNormalAtTime(0).normalize(10);
var p = s.point.clone().add(t1); var p = s.point.clone().add(t1);
var cir = new Path.Circle(s.point, 2); var cir = new Path.Circle(s.point, 2);
cir.style.fillColor = c; cir.style.fillColor = c;
@ -427,12 +427,12 @@
tc = tc || '#ccc'; tc = tc || '#ccc';
t = t || crv.index; t = t || crv.index;
if (remove === undefined) { remove = true; } if (remove === undefined) { remove = true; }
var p = crv.getPointAt(0.57, true); var p = crv.getPointAtTime(0.57);
var t1 = crv.getTangentAt(0.57, true).normalize(-10); var t1 = crv.getTangentAtTime(0.57).normalize(-10);
var p2 = p.clone().add(t1); var p2 = p.clone().add(t1);
var l = new Path.Line(p, p2).rotate(30, p); var l = new Path.Line(p, p2).rotate(30, p);
var l2 = new Path.Line(p, p2).rotate(-30, p); var l2 = new Path.Line(p, p2).rotate(-30, p);
p = crv.getPointAt(0.43, true); p = crv.getPointAtTime(0.43);
var cir = new Path.Circle(p, 8); var cir = new Path.Circle(p, 8);
var text = new PointText(p.subtract([0, -4])); var text = new PointText(p.subtract([0, -4]));
text.justification = 'center'; text.justification = 'center';

View file

@ -21,7 +21,7 @@
curve.segment2.point = point; curve.segment2.point = point;
var length = curve.length; var length = curve.length;
var step = 10; var step = 10;
var iteratively = false; var iteratively = true;
var flatten = true; var flatten = true;
var num = Math.floor(length / step); var num = Math.floor(length / step);
var prev = 0; var prev = 0;
@ -39,9 +39,9 @@
} else { } else {
for (var i = 0, pos = 0; i <= num; i++, pos += step) { for (var i = 0, pos = 0; i <= num; i++, pos += step) {
var t = iteratively var t = iteratively
? curve.getParameterAt(step, prev) ? curve.getTimeAt(step, prev)
: curve.getParameterAt(pos); : curve.getTimeAt(pos);
var point = curve.getPointAt(t, true); var point = curve.getPointAtTime(t);
var circle = new Path.Circle(point, step / 2); var circle = new Path.Circle(point, step / 2);
circle.strokeColor = 'red'; circle.strokeColor = 'red';
if (remove) if (remove)

View file

@ -27,7 +27,7 @@ paper = new (PaperScope.inject(Base.exports, {
// Export jsdom document and window too, for Node.js // Export jsdom document and window too, for Node.js
document: document, document: document,
window: window, window: window,
// TODO: Remove in 2017 (deprecated January 2016): // TODO: Remove in 1.0.0? (deprecated January 2016):
Symbol: SymbolDefinition, Symbol: SymbolDefinition,
PlacedSymbol: SymbolItem PlacedSymbol: SymbolItem
}))(); }))();

View file

@ -403,7 +403,7 @@ var Curve = Base.extend(/** @lends Curve# */{
/** /**
* Creates a new curve as a sub-curve from this curve, its range defined by * Creates a new curve as a sub-curve from this curve, its range defined by
* the given parameters. If `from` is larger than `to`, then * the given curve-time parameters. If `from` is larger than `to`, then
* the resulting curve will have its direction reversed. * the resulting curve will have its direction reversed.
* *
* @param {Number} from the curve-time parameter at which the sub-curve * @param {Number} from the curve-time parameter at which the sub-curve
@ -439,46 +439,44 @@ var Curve = Base.extend(/** @lends Curve# */{
// TODO: adjustThroughPoint // TODO: adjustThroughPoint
/**
* Private method that handles all types of offset / isParameter pairs and
* converts it to a curve parameter.
*/
_getParameter: function(offset, isParameter) {
return isParameter
? offset
// Accept CurveLocation objects, and objects that act like
// them:
: offset && offset.curve === this
? offset.parameter
: offset === undefined && isParameter === undefined
? 0.5 // default is in the middle
: this.getParameterAt(offset, 0);
},
/** /**
* Divides the curve into two curves at the given offset. The curve itself * Divides the curve into two curves at the given offset. The curve itself
* is modified and becomes the first part, the second part is returned as a * is modified and becomes the first part, the second part is returned as a
* new curve. If the modified curve belongs to a path item, the second part * new curve. If the modified curve belongs to a path item, the second part
* is also added to the path. * is also added to the path.
* *
* @name Curve#divide * @param {Number|CurveLocation} location the offset or location on the
* @function * curve at which to divide
* @param {Number} [offset=0.5] the offset on the curve at which to split,
* or the curve time parameter if `isParameter` is `true`
* @param {Boolean} [isParameter=false] pass `true` if `offset` is a curve
* time parameter
* @return {Curve} the second part of the divided curve, if the offset is * @return {Curve} the second part of the divided curve, if the offset is
* within the valid range, {code null} otherwise. * within the valid range, {code null} otherwise.
* @see #divideAtTime(time)
*/ */
// TODO: Rename to divideAt()? divideAt: function(location) {
divide: function(offset, isParameter, _setHandles) { // Accept offsets and CurveLocation objects, as well as objects that act
var parameter = this._getParameter(offset, isParameter), // like them.
tMin = /*#=*/Numerical.CURVETIME_EPSILON, return this.divideAtTime(location && location.curve === this
? location.time : location);
},
/**
* Divides the curve into two curves at the given curve-time parameter. The
* curve itself is modified and becomes the first part, the second part is
* returned as a new curve. If the modified curve belongs to a path item,
* the second part is also added to the path.
*
* @param {Number} time the curve-time parameter on the curve at which to
* divide
* @return {Curve} the second part of the divided curve, if the offset is
* within the valid range, {code null} otherwise.
* @see #divideAt(offset)
*/
divideAtTime: function(time, _setHandles) {
// Only divide if not at the beginning or end.
var tMin = /*#=*/Numerical.CURVETIME_EPSILON,
tMax = 1 - tMin, tMax = 1 - tMin,
res = null; res = null;
// Only divide if not at the beginning or end. if (time >= tMin && time <= tMax) {
if (parameter >= tMin && parameter <= tMax) { var parts = Curve.subdivide(this.getValues(), time),
var parts = Curve.subdivide(this.getValues(), parameter),
left = parts[0], left = parts[0],
right = parts[1], right = parts[1],
setHandles = _setHandles || this.hasHandles(), setHandles = _setHandles || this.hasHandles(),
@ -519,21 +517,47 @@ var Curve = Base.extend(/** @lends Curve# */{
* splitting, the path will be open. If the path was open already, splitting * splitting, the path will be open. If the path was open already, splitting
* will result in two paths. * will result in two paths.
* *
* @name Curve#split * @param {Number|CurveLocation} location the offset or location on the
* @function * curve at which to split
* @param {Number} [offset=0.5] the offset on the curve at which to split,
* or the curve time parameter if `isParameter` is `true`
* @param {Boolean} [isParameter=false] pass `true` if `offset` is a curve
* time parameter
* @return {Path} the newly created path after splitting, if any * @return {Path} the newly created path after splitting, if any
* @see Path#split(index, parameter) * @see Path#splitAt(offset)
*/ */
// TODO: Rename to splitAt()? splitAt: function(location) {
split: function(offset, isParameter) { return this._path ? this._path.splitAt(location) : null;
return this._path },
? this._path.split(this._segment1._index,
this._getParameter(offset, isParameter)) /**
: null; * Splits the path this curve belongs to at the given offset. After
* splitting, the path will be open. If the path was open already, splitting
* will result in two paths.
*
* @param {Number} time the curve-time parameter on the curve at which to
* split
* @return {Path} the newly created path after splitting, if any
* @see Path#splitAt(offset)
*/
splitAtTime: function(t) {
return this.splitAt(this.getLocationAtTime(t));
},
// TODO: Remove in 1.0.0? (deprecated January 2016):
/**
* @deprecated, use use {@link #divideAt(offset)} or
* {@link #divideAtTime(time)} instead.
*/
divide: function(offset, isTime) {
return this.divideAtTime(offset === undefined ? 0.5 : isTime ? offset
: this.getTimeAt(offset));
},
// TODO: Remove in 1.0.0? (deprecated January 2016):
/**
* @deprecated, use use {@link #splitAt(offset)} or
* {@link #splitAtTime(time)} instead.
*/
split: function(offset, isTime) {
return this.splitAtTime(offset === undefined ? 0.5 : isTime ? offset
: this.getTimeAt(offset));
}, },
/** /**
@ -610,7 +634,7 @@ statics: {
return Numerical.solveCubic(a, b, c, p1 - val, roots, min, max); return Numerical.solveCubic(a, b, c, p1 - val, roots, min, max);
}, },
getParameterOf: 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 p1 = new Point(v[0], v[1]),
@ -642,7 +666,7 @@ statics: {
: null; : null;
}, },
getNearestParameter: function(v, point) { getNearestTime: function(v, point) {
if (Curve.isStraight(v)) { if (Curve.isStraight(v)) {
var p1x = v[0], p1y = v[1], var p1x = v[0], p1y = v[1],
p2x = v[6], p2y = v[7], p2x = v[6], p2y = v[7],
@ -656,7 +680,7 @@ statics: {
var u = ((point.x - p1x) * vx + (point.y - p1y) * vy) / det; var u = ((point.x - p1x) * vx + (point.y - p1y) * vy) / det;
return u < /*#=*/Numerical.EPSILON ? 0 return u < /*#=*/Numerical.EPSILON ? 0
: u > /*#=*/(1 - Numerical.EPSILON) ? 1 : u > /*#=*/(1 - Numerical.EPSILON) ? 1
: Curve.getParameterOf(v, : Curve.getTimeOf(v,
new Point(p1x + u * vx, p1y + u * vy)); new Point(p1x + u * vx, p1y + u * vy));
} }
@ -951,7 +975,7 @@ statics: {
* @return {Boolean} {@true if the line is horizontal} * @return {Boolean} {@true if the line is horizontal}
*/ */
isHorizontal: function() { isHorizontal: function() {
return this.isStraight() && Math.abs(this.getTangentAt(0.5, true).y) return this.isStraight() && Math.abs(this.getTangentAtTime(0.5).y)
< /*#=*/Numerical.TRIGONOMETRIC_EPSILON; < /*#=*/Numerical.TRIGONOMETRIC_EPSILON;
}, },
@ -961,59 +985,64 @@ statics: {
* @return {Boolean} {@true if the line is vertical} * @return {Boolean} {@true if the line is vertical}
*/ */
isVertical: function() { isVertical: function() {
return this.isStraight() && Math.abs(this.getTangentAt(0.5, true).x) return this.isStraight() && Math.abs(this.getTangentAtTime(0.5).x)
< /*#=*/Numerical.TRIGONOMETRIC_EPSILON; < /*#=*/Numerical.TRIGONOMETRIC_EPSILON;
} }
}), /** @lends Curve# */{ }), /** @lends Curve# */{
// Explicitly deactivate the creation of beans, as we have functions here // Explicitly deactivate the creation of beans, as we have functions here
// that look like bean getters but actually read arguments. // that look like bean getters but actually read arguments.
// See #getParameterOf(), #getLocationOf(), #getNearestLocation(), ... // See #getTimeOf(), #getLocationOf(), #getNearestLocation(), ...
beans: false, beans: false,
/** /**
* {@grouptitle Positions on Curves} * {@grouptitle Positions on Curves}
* *
* Calculates the curve time parameter of the specified offset on the path, * Calculates the curve location at the specified offset on the curve.
*
* @param {Number} offset the offset on the curve
* @return {CurveLocation} the curve location at the specified the offset
*/
getLocationAt: function(offset, _isTime) {
// TODO: Remove _isTime handling in 1.0.0? (deprecated Jan 2016):
return this.getLocationAtTime(
_isTime ? offset : this.getTimeAt(offset));
},
/**
* Calculates the curve location at the specified curve-time parameter on
* the curve.
*
* @param {Number} time the curve-time parameter on the curve
* @return {CurveLocation} the curve location at the specified the location
*/
getLocationAtTime: function(t) {
return t != null && t >= 0 && t <= 1
? new CurveLocation(this, t)
: null;
},
/**
* Calculates the curve-time parameter of the specified offset on the path,
* relative to the provided start parameter. If offset is a negative value, * relative to the provided start parameter. If offset is a negative value,
* the parameter is searched to the left of the start parameter. If no start * the parameter is searched to the left of the start parameter. If no start
* parameter is provided, a default of `0` for positive values of `offset` * parameter is provided, a default of `0` for positive values of `offset`
* and `1` for negative values of `offset`. * and `1` for negative values of `offset`.
* *
* @param {Number} offset * @param {Number} offset the offset at which to find the curve-time, in
* @param {Number} [start] * curve length units
* @return {Number} the curve time parameter at the specified offset * @param {Number} [start] the curve-time in relation to which the offset is
* determined
* @return {Number} the curve-time parameter at the specified location
*/ */
getParameterAt: function(offset, start) { getTimeAt: function(offset, start) {
return Curve.getParameterAt(this.getValues(), offset, start); return Curve.getTimeAt(this.getValues(), offset, start);
}, },
// TODO: Remove in 1.0.0? (deprecated January 2016):
/** /**
* Returns the curve time parameter of the specified point if it lies on the * @deprecated, use use {@link #getTimeOf(point)} instead.
* curve, `null` otherwise.
*
* @param {Point} point the point on the curve
* @return {Number} the curve time parameter of the specified point
*/ */
getParameterOf: function(/* point */) { getParameterAt: '#getTimeAt',
return Curve.getParameterOf(this.getValues(), Point.read(arguments));
},
/**
* Calculates the curve location at the specified offset or curve time
* parameter.
*
* @param {Number} offset the offset on the curve, or the curve time
* parameter if `isParameter` is `true`
* @param {Boolean} [isParameter=false] pass `true` if `offset` is a curve
* time parameter
* @return {CurveLocation} the curve location at the specified the offset
*/
getLocationAt: function(offset, isParameter) {
var t = isParameter ? offset : this.getParameterAt(offset);
return t != null && t >= 0 && t <= 1
? new CurveLocation(this, t)
: null;
},
/** /**
* Returns the curve location of the specified point if it lies on the * Returns the curve location of the specified point if it lies on the
@ -1023,8 +1052,7 @@ statics: {
* @return {CurveLocation} the curve location of the specified point * @return {CurveLocation} the curve location of the specified point
*/ */
getLocationOf: function(/* point */) { getLocationOf: function(/* point */) {
return this.getLocationAt(this.getParameterOf(Point.read(arguments)), return this.getLocationAtTime(this.getTimeOf(Point.read(arguments)));
true);
}, },
/** /**
@ -1039,6 +1067,23 @@ statics: {
return loc ? loc.getOffset() : null; return loc ? loc.getOffset() : null;
}, },
/**
* Returns the curve-time parameter of the specified point if it lies on the
* curve, `null` otherwise.
*
* @param {Point} point the point on the curve
* @return {Number} the curve-time parameter of the specified point
*/
getTimeOf: function(/* point */) {
return Curve.getTimeOf(this.getValues(), Point.read(arguments));
},
// TODO: Remove in 1.0.0? (deprecated January 2016):
/**
* @deprecated, use use {@link #getTimeOf(point)} instead.
*/
getParameterOf: '#getTimeOf',
/** /**
* Returns the nearest location on the curve to the specified point. * Returns the nearest location on the curve to the specified point.
* *
@ -1050,7 +1095,7 @@ statics: {
getNearestLocation: function(/* point */) { getNearestLocation: function(/* point */) {
var point = Point.read(arguments), var point = Point.read(arguments),
values = this.getValues(), values = this.getValues(),
t = Curve.getNearestParameter(values, point), t = Curve.getNearestTime(values, point),
pt = Curve.getPoint(values, t); pt = Curve.getPoint(values, t);
return new CurveLocation(this, t, pt, null, point.getDistance(pt)); return new CurveLocation(this, t, pt, null, point.getDistance(pt));
}, },
@ -1068,81 +1113,129 @@ statics: {
} }
/** /**
* Calculates the point on the curve at the given offset. * Calculates the point on the curve at the given location.
* *
* @name Curve#getPointAt * @name Curve#getPointAt
* @function * @function
* @param {Number} offset the offset on the curve, or the curve time * @param {Number|CurveLocation} location the offset or location on the
* parameter if `isParameter` is `true` * curve
* @param {Boolean} [isParameter=false] pass `true` if `offset` is a curve * @return {Point} the point on the curve at the given location
* time parameter
* @return {Point} the point on the curve at the given offset
*/ */
/** /**
* Calculates the normalized tangent vector of the curve at the given * Calculates the normalized tangent vector of the curve at the given
* offset. * location.
* *
* @name Curve#getTangentAt * @name Curve#getTangentAt
* @function * @function
* @param {Number} offset the offset on the curve, or the curve time * @param {Number|CurveLocation} location the offset or location on the
* parameter if `isParameter` is `true` * curve
* @param {Boolean} [isParameter=false] pass `true` if `offset` is a curve * @return {Point} the normalized tangent of the curve at the given location
* time parameter
* @return {Point} the normalized tangent of the curve at the given offset
*/ */
/** /**
* Calculates the normal vector of the curve at the given offset. * Calculates the normal vector of the curve at the given location.
* *
* @name Curve#getNormalAt * @name Curve#getNormalAt
* @function * @function
* @param {Number} offset the offset on the curve, or the curve time * @param {Number|CurveLocation} location the offset or location on the
* parameter if `isParameter` is `true` * curve
* @param {Boolean} [isParameter=false] pass `true` if `offset` is a curve * @return {Point} the normal of the curve at the given location
* time parameter
* @return {Point} the normal of the curve at the given offset
*/ */
/** /**
* Calculates the weighted tangent vector of the curve at the given offset, * Calculates the weighted tangent vector of the curve at the given
* its length reflecting the curve velocity at that location. * location, its length reflecting the curve velocity at that location.
* *
* @name Curve#getWeightedTangentAt * @name Curve#getWeightedTangentAt
* @function * @function
* @param {Number} offset the offset on the curve, or the curve time * @param {Number|CurveLocation} location the offset or location on the
* parameter if `isParameter` is `true` * curve
* @param {Boolean} [isParameter=false] pass `true` if `offset` is a curve * @return {Point} the weighted tangent of the curve at the given location
* 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, * Calculates the weighted normal vector of the curve at the given location,
* its length reflecting the curve velocity at that location. * its length reflecting the curve velocity at that location.
* *
* @name Curve#getWeightedNormalAt * @name Curve#getWeightedNormalAt
* @function * @function
* @param {Number} offset the offset on the curve, or the curve time * @param {Number|CurveLocation} location the offset or location on the
* parameter if `isParameter` is `true` * curve
* @param {Boolean} [isParameter=false] pass `true` if `offset` is a curve * @return {Point} the weighted normal of the curve at the given location
* time parameter
* @return {Point} the weighted normal of the curve at the given offset
*/ */
/** /**
* Calculates the curvature of the curve at the given offset. Curvatures * Calculates the curvature of the curve at the given location. Curvatures
* indicate how sharply a curve changes direction. A straight line has zero * indicate how sharply a curve changes direction. A straight line has zero
* curvature, where as a circle has a constant curvature. The curve's radius * curvature, where as a circle has a constant curvature. The curve's radius
* at the given offset is the reciprocal value of its curvature. * at the given location is the reciprocal value of its curvature.
* *
* @name Curve#getCurvatureAt * @name Curve#getCurvatureAt
* @function * @function
* @param {Number} offset the offset on the curve, or the curve time * @param {Number|CurveLocation} location the offset or location on the
* parameter if `isParameter` is `true` * curve
* @param {Boolean} [isParameter=false] pass `true` if `offset` is a curve * @return {Number} the curvature of the curve at the given location
* time parameter */
* @return {Number} the curvature of the curve at the given offset
/**
* Calculates the point on the curve at the given location.
*
* @name Curve#getPointAtTime
* @function
* @param {Number} time the curve-time parameter on the curve
* @return {Point} the point on the curve at the given location
*/
/**
* Calculates the normalized tangent vector of the curve at the given
* location.
*
* @name Curve#getTangentAtTime
* @function
* @param {Number} time the curve-time parameter on the curve
* @return {Point} the normalized tangent of the curve at the given location
*/
/**
* Calculates the normal vector of the curve at the given location.
*
* @name Curve#getNormalAtTime
* @function
* @param {Number} time the curve-time parameter on the curve
* @return {Point} the normal of the curve at the given location
*/
/**
* Calculates the weighted tangent vector of the curve at the given
* location, its length reflecting the curve velocity at that location.
*
* @name Curve#getWeightedTangentAtTime
* @function
* @param {Number} time the curve-time parameter on the curve
* @return {Point} the weighted tangent of the curve at the given location
*/
/**
* Calculates the weighted normal vector of the curve at the given location,
* its length reflecting the curve velocity at that location.
*
* @name Curve#getWeightedNormalAtTime
* @function
* @param {Number} time the curve-time parameter on the curve
* @return {Point} the weighted normal of the curve at the given location
*/
/**
* Calculates the curvature of the curve at the given location. Curvatures
* indicate how sharply a curve changes direction. A straight line has zero
* curvature, where as a circle has a constant curvature. The curve's radius
* at the given location is the reciprocal value of its curvature.
*
* @name Curve#getCurvatureAtTime
* @function
* @param {Number} time the curve-time parameter on the curve
* @return {Number} the curvature of the curve at the given location
*/ */
}, },
new function() { // // Scope to inject various curve evaluation methods new function() { // // Scope to inject various curve evaluation methods
@ -1152,11 +1245,18 @@ new function() { // // Scope to inject various curve evaluation methods
function(name) { function(name) {
// NOTE: (For easier searching): This loop produces: // NOTE: (For easier searching): This loop produces:
// getPointAt, getTangentAt, getNormalAt, getWeightedTangentAt, // getPointAt, getTangentAt, getNormalAt, getWeightedTangentAt,
// getWeightedNormalAt, getCurvatureAt // getWeightedNormalAt, getCurvatureAt, getPointAtTime,
this[name + 'At'] = function(offset, isParameter) { // getTangentAtTime, getNormalAtTime, getWeightedTangentAtTime,
// getWeightedNormalAtTime, getCurvatureAtTime
// TODO: Remove _isTime handling in 1.0.0? (deprecated Jan 2016):
this[name + 'At'] = function(location, _isTime) {
var values = this.getValues(); var values = this.getValues();
return Curve[name](values, isParameter ? offset return Curve[name](values, _isTime ? location
: Curve.getParameterAt(values, offset, 0)); : Curve.getTimeAt(values, location, 0));
};
this[name + 'AtTime'] = function(time) {
return Curve[name](this.getValues(), time);
}; };
}, { }, {
statics: { statics: {
@ -1306,7 +1406,7 @@ new function() { // Scope for methods that require private functions
return Numerical.integrate(ds, a, b, getIterations(a, b)); return Numerical.integrate(ds, a, b, getIterations(a, b));
}, },
getParameterAt: function(v, offset, start) { getTimeAt: function(v, offset, start) {
if (start === undefined) if (start === undefined)
start = offset < 0 ? 1 : 0; start = offset < 0 ? 1 : 0;
if (offset === 0) if (offset === 0)
@ -1387,7 +1487,7 @@ new function() { // Scope for intersection using bezier fat-line clipping
tMin = /*#=*/Numerical.CURVETIME_EPSILON, tMin = /*#=*/Numerical.CURVETIME_EPSILON,
tMax = 1 - tMin; tMax = 1 - tMin;
if (t1 == null) if (t1 == null)
t1 = Curve.getParameterOf(v1, p1); t1 = Curve.getTimeOf(v1, p1);
// Check t1 and t2 against correct bounds, based on excludeStart/End: // Check t1 and t2 against correct bounds, based on excludeStart/End:
// - excludeStart means the start of c1 connects to the end of c2 // - excludeStart means the start of c1 connects to the end of c2
// - endConneted means the end of c1 connects to the start of c2 // - endConneted means the end of c1 connects to the start of c2
@ -1398,7 +1498,7 @@ new function() { // Scope for intersection using bezier fat-line clipping
if (t1 !== null && t1 >= (excludeStart ? tMin : 0) && if (t1 !== null && t1 >= (excludeStart ? tMin : 0) &&
t1 <= (excludeEnd ? tMax : 1)) { t1 <= (excludeEnd ? tMax : 1)) {
if (t2 == null) if (t2 == null)
t2 = Curve.getParameterOf(v2, p2); t2 = Curve.getTimeOf(v2, p2);
if (t2 !== null && t2 >= (excludeEnd ? tMin : 0) && if (t2 !== null && t2 >= (excludeEnd ? tMin : 0) &&
t2 <= (excludeStart ? tMax : 1)) { t2 <= (excludeStart ? tMax : 1)) {
var renormalize = param.renormalize; var renormalize = param.renormalize;
@ -1643,7 +1743,7 @@ new function() { // Scope for intersection using bezier fat-line clipping
// the real curve and with that the location on the line. // the real curve and with that the location on the line.
var tc = roots[i], var tc = roots[i],
pc = Curve.getPoint(vc, tc), pc = Curve.getPoint(vc, tc),
tl = Curve.getParameterOf(vl, pc); tl = Curve.getTimeOf(vl, pc);
if (tl !== null) { if (tl !== null) {
var pl = Curve.getPoint(vl, tl), var pl = Curve.getPoint(vl, tl),
t1 = flip ? tl : tc, t1 = flip ? tl : tc,
@ -1809,7 +1909,7 @@ new function() { // Scope for intersection using bezier fat-line clipping
// the loop in case of a self intersection. // the loop in case of a self intersection.
for (var i = 0, maxCurvature = 0; i < count; i++) { for (var i = 0, maxCurvature = 0; i < count; i++) {
var curvature = Math.abs( var curvature = Math.abs(
c1.getCurvatureAt(roots[i], true)); c1.getCurvatureAtTime(roots[i]));
if (curvature > maxCurvature) { if (curvature > maxCurvature) {
maxCurvature = curvature; maxCurvature = curvature;
tSplit = roots[i]; tSplit = roots[i];
@ -1894,7 +1994,7 @@ new function() { // Scope for intersection using bezier fat-line clipping
for (var i = 0, t1 = 0; for (var i = 0, t1 = 0;
i < 2 && pairs.length < 2; i < 2 && pairs.length < 2;
i += t1 === 0 ? 0 : 1, t1 = t1 ^ 1) { i += t1 === 0 ? 0 : 1, t1 = t1 ^ 1) {
var t2 = Curve.getParameterOf(v[i ^ 1], new Point( var t2 = Curve.getTimeOf(v[i ^ 1], new Point(
v[i][t1 === 0 ? 0 : 6], v[i][t1 === 0 ? 0 : 6],
v[i][t1 === 0 ? 1 : 7])); v[i][t1 === 0 ? 1 : 7]));
if (t2 != null) { // If point is on curve if (t2 != null) { // If point is on curve

View file

@ -14,9 +14,9 @@
* @name CurveLocation * @name CurveLocation
* *
* @class CurveLocation objects describe a location on {@link Curve} objects, as * @class CurveLocation objects describe a location on {@link Curve} objects, as
* defined by the curve-time {@link #parameter}, a value between `0` * defined by the curve-time {@link #time}, a value between `0` (beginning
* (beginning of the curve) and `1` (end of the curve). If the curve is part * of the curve) and `1` (end of the curve). If the curve is part of a
* of a {@link Path} item, its {@link #index} inside the {@link Path#curves} * {@link Path} item, its {@link #index} inside the {@link Path#curves}
* array is also provided. * array is also provided.
* *
* The class is in use in many places, such as * The class is in use in many places, such as
@ -38,17 +38,16 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{
* Creates a new CurveLocation object. * Creates a new CurveLocation object.
* *
* @param {Curve} curve * @param {Curve} curve
* @param {Number} parameter * @param {Number} time
* @param {Point} [point] * @param {Point} [point]
*/ */
initialize: function CurveLocation(curve, parameter, point, initialize: function CurveLocation(curve, time, point, _overlap, _distance) {
_overlap, _distance) {
// Merge intersections very close to the end of a curve with the // Merge intersections very close to the end of a curve with the
// beginning of the next curve. // beginning of the next curve.
if (parameter > /*#=*/(1 - Numerical.CURVETIME_EPSILON)) { if (time > /*#=*/(1 - Numerical.CURVETIME_EPSILON)) {
var next = curve.getNext(); var next = curve.getNext();
if (next) { if (next) {
parameter = 0; time = 0;
curve = next; curve = next;
} }
} }
@ -58,8 +57,8 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{
// CurveLocation objects. // CurveLocation objects.
this._id = UID.get(CurveLocation); this._id = UID.get(CurveLocation);
this._setCurve(curve); this._setCurve(curve);
this._parameter = parameter; this._time = time;
this._point = point || curve.getPointAt(parameter, true); this._point = point || curve.getPointAtTime(time);
this._overlap = _overlap; this._overlap = _overlap;
this._distance = _distance; this._distance = _distance;
this._intersection = this._next = this._prev = null; this._intersection = this._next = this._prev = null;
@ -84,7 +83,7 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{
_setSegment: function(segment) { _setSegment: function(segment) {
this._setCurve(segment.getCurve()); this._setCurve(segment.getCurve());
this._segment = segment; this._segment = segment;
this._parameter = segment === this._segment1 ? 0 : 1; this._time = segment === this._segment1 ? 0 : 1;
// To avoid issues with imprecision in getCurve() / trySegment() // To avoid issues with imprecision in getCurve() / trySegment()
this._point = segment._point.clone(); this._point = segment._point.clone();
}, },
@ -100,15 +99,15 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{
var curve = this.getCurve(), var curve = this.getCurve(),
segment = this._segment; segment = this._segment;
if (!segment) { if (!segment) {
var parameter = this.getParameter(); var time = this.getTime();
if (parameter === 0) { if (time === 0) {
segment = curve._segment1; segment = curve._segment1;
} else if (parameter === 1) { } else if (time === 1) {
segment = curve._segment2; segment = curve._segment2;
} else if (parameter != null) { } else if (time != null) {
// Determine the closest segment by comparing curve lengths // Determine the closest segment by comparing curve lengths
segment = curve.getPartLength(0, parameter) segment = curve.getPartLength(0, time)
< curve.getPartLength(parameter, 1) < curve.getPartLength(time, 1)
? curve._segment1 ? curve._segment1
: curve._segment2; : curve._segment2;
} }
@ -128,9 +127,9 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{
that = this; that = this;
if (path && path._version !== this._version) { if (path && path._version !== this._version) {
// If the path's segments have changed in the meantime, clear the // If the path's segments have changed in the meantime, clear the
// internal _parameter value and force refetching of the correct // internal _time value and force re-fetching of the correct
// curve again here. // curve again here.
this._parameter = this._curve = this._offset = null; this._time = this._curve = this._offset = null;
} }
// If path is out of sync, access current curve objects through segment1 // If path is out of sync, access current curve objects through segment1
@ -139,7 +138,7 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{
// still, otherwise assume it's the curve before segment2. // still, otherwise assume it's the curve before segment2.
function trySegment(segment) { function trySegment(segment) {
var curve = segment && segment.getCurve(); var curve = segment && segment.getCurve();
if (curve && (that._parameter = curve.getParameterOf(that._point)) if (curve && (that._time = curve.getTimeOf(that._point))
!= null) { != null) {
// Fetch path again as it could be on a new one through split() // Fetch path again as it could be on a new one through split()
that._setCurve(curve); that._setCurve(curve);
@ -185,17 +184,24 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{
* @bean * @bean
* @type Number * @type Number
*/ */
getParameter: function() { getTime: function() {
var curve = this.getCurve(), var curve = this.getCurve(),
parameter = this._parameter; time = this._time;
return curve && parameter == null return curve && time == null
? this._parameter = curve.getParameterOf(this._point) ? this._time = curve.getTimeOf(this._point)
: parameter; : time;
}, },
/**
* @private
* @bean
* @deprecated use {@link #getTime()} instead.
*/
getParameter: '#getTime',
/** /**
* The point which is defined by the {@link #curve} and * The point which is defined by the {@link #curve} and
* {@link #parameter}. * {@link #time}.
* *
* @bean * @bean
* @type Point * @type Point
@ -237,8 +243,8 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{
*/ */
getCurveOffset: function() { getCurveOffset: function() {
var curve = this.getCurve(), var curve = this.getCurve(),
parameter = this.getParameter(); time = this.getTime();
return parameter != null && curve && curve.getPartLength(0, parameter); return time != null && curve && curve.getPartLength(0, time);
}, },
/** /**
@ -295,8 +301,8 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{
var curve = this.getCurve(), var curve = this.getCurve(),
res = null; res = null;
if (curve) { if (curve) {
res = curve.divide(this.getParameter(), true); res = curve.divideAtTime(this.getTime());
// Change to the newly inserted segment, also adjusting _parameter. // Change to the newly inserted segment, also adjusting _time.
if (res) if (res)
this._setSegment(res._segment1); this._setSegment(res._segment1);
} }
@ -305,13 +311,13 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{
split: function() { split: function() {
var curve = this.getCurve(); var curve = this.getCurve();
return curve ? curve.split(this.getParameter(), true) : null; return curve ? curve.splitAtTime(this.getTime()) : null;
}, },
/** /**
* Checks whether tow CurveLocation objects are describing the same location * Checks whether tow CurveLocation objects are describing the same location
* on a path, by applying the same tolerances as elsewhere when dealing with * on a path, by applying the same tolerances as elsewhere when dealing with
* curve time parameters. * curve-time parameters.
* *
* @param {CurveLocation} location * @param {CurveLocation} location
* @return {Boolean} {@true if the locations are equal} * @return {Boolean} {@true if the locations are equal}
@ -332,9 +338,9 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{
// We need to wrap diff around the path's beginning / end: // We need to wrap diff around the path's beginning / end:
diff = abs( diff = abs(
((c1.isLast() && c2.isFirst() ? -1 : c1.getIndex()) ((c1.isLast() && c2.isFirst() ? -1 : c1.getIndex())
+ this.getParameter()) - + this.getTime()) -
((c2.isLast() && c1.isFirst() ? -1 : c2.getIndex()) ((c2.isLast() && c1.isFirst() ? -1 : c2.getIndex())
+ loc.getParameter())); + loc.getTime()));
res = (diff < /*#=*/Numerical.CURVETIME_EPSILON res = (diff < /*#=*/Numerical.CURVETIME_EPSILON
// If diff isn't close enough, compare the actual offsets of // If diff isn't close enough, compare the actual offsets of
// both locations to determine if they're in the same spot, // both locations to determine if they're in the same spot,
@ -363,9 +369,9 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{
var index = this.getIndex(); var index = this.getIndex();
if (index != null) if (index != null)
parts.push('index: ' + index); parts.push('index: ' + index);
var parameter = this.getParameter(); var time = this.getTime();
if (parameter != null) if (time != null)
parts.push('parameter: ' + f.number(parameter)); parts.push('time: ' + f.number(time));
if (this._distance != null) if (this._distance != null)
parts.push('distance: ' + f.number(this._distance)); parts.push('distance: ' + f.number(this._distance));
return '{ ' + parts.join(', ') + ' }'; return '{ ' + parts.join(', ') + ' }';
@ -409,8 +415,8 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{
var inter = this._intersection; var inter = this._intersection;
if (!inter) if (!inter)
return false; return false;
var t1 = this.getParameter(), var t1 = this.getTime(),
t2 = inter.getParameter(), t2 = inter.getTime(),
tMin = /*#=*/Numerical.CURVETIME_EPSILON, tMin = /*#=*/Numerical.CURVETIME_EPSILON,
tMax = 1 - tMin, tMax = 1 - tMin,
// t*Inside specifies if the found intersection is inside the curve. // t*Inside specifies if the found intersection is inside the curve.
@ -480,10 +486,10 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{
// and the code will doe the right thing. // and the code will doe the right thing.
// The incomings tangents v1 & v3 are inverted, so that all angles // The incomings tangents v1 & v3 are inverted, so that all angles
// are pointing outwards in the right direction from the intersection. // are pointing outwards in the right direction from the intersection.
var v2 = c2.getTangentAt(t1Inside ? t1 : tMin, true), var v2 = c2.getTangentAtTime(t1Inside ? t1 : tMin),
v1 = (t1Inside ? v2 : c1.getTangentAt(tMax, true)).negate(), v1 = (t1Inside ? v2 : c1.getTangentAtTime(tMax)).negate(),
v4 = c4.getTangentAt(t2Inside ? t2 : tMin, true), v4 = c4.getTangentAtTime(t2Inside ? t2 : tMin),
v3 = (t2Inside ? v4 : c3.getTangentAt(tMax, true)).negate(), v3 = (t2Inside ? v4 : c3.getTangentAtTime(tMax)).negate(),
// NOTE: For shorter API calls we work with angles in degrees here: // NOTE: For shorter API calls we work with angles in degrees here:
a1 = v1.getAngle(), a1 = v1.getAngle(),
a2 = v2.getAngle(), a2 = v2.getAngle(),
@ -519,9 +525,9 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{
// getWeightedNormalAt, getCurvatureAt // getWeightedNormalAt, getCurvatureAt
var get = name + 'At'; var get = name + 'At';
this[name] = function() { this[name] = function() {
var parameter = this.getParameter(), var curve = this.getCurve(),
curve = this.getCurve(); time = this.getTime();
return parameter != null && curve && curve[get](parameter, true); return time != null && curve && curve[get](time, true);
}; };
}, { }, {
// Do not override the existing #getPoint(): // Do not override the existing #getPoint():
@ -530,7 +536,7 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{
new function() { // Scope for statics new function() { // Scope for statics
function insert(locations, loc, merge) { function insert(locations, loc, merge) {
// Insert-sort by path-id, curve, parameter so we can easily merge // Insert-sort by path-id, curve, time so we can easily merge
// duplicates with calls to equals() after. // duplicates with calls to equals() after.
var length = locations.length, var length = locations.length,
l = 0, l = 0,
@ -575,10 +581,10 @@ new function() { // Scope for statics
// NOTE: equals() takes the intersection location into account, // NOTE: equals() takes the intersection location into account,
// while this calculation of diff doesn't! // while this calculation of diff doesn't!
diff = path1 === path2 diff = path1 === path2
//Sort by both index and parameter. The two values added //Sort by both index and time. The two values added
// together provides a convenient sorting index. // together provides a convenient sorting index.
? (loc.getIndex() + loc.getParameter()) ? (loc.getIndex() + loc.getTime())
- (loc2.getIndex() + loc2.getParameter()) - (loc2.getIndex() + loc2.getTime())
// Sort by path id to group all locs on same path. // Sort by path id to group all locs on same path.
: path1._id - path2._id; : path1._id - path2._id;
if (diff < 0) { if (diff < 0) {

View file

@ -1092,56 +1092,12 @@ var Path = PathItem.extend(/** @lends Path# */{
// TODO: reduceSegments([flatness]) // TODO: reduceSegments([flatness])
/** /**
* Splits the path at the given offset. After splitting, the path will be * Splits the path at the given offset or location. After splitting, the
* open. If the path was open already, splitting will result in two paths. * path will be open. If the path was open already, splitting will result in
* two paths.
* *
* @name Path#split * @param {Number|CurveLocation} location the offset or location at which to
* @function * split the path
* @param {Number} offset the offset at which to split the path
* as a number between 0 and {@link Path#length}
* @return {Path} the newly created path after splitting, if any
*
* @example {@paperscript} // Splitting an open path
* var path = new Path();
* path.strokeColor = 'black';
* path.add(20, 20);
*
* // Add an arc through {x: 90, y: 80} to {x: 160, y: 20}
* path.arcTo([90, 80], [160, 20]);
*
* // Split the path at 30% of its length:
* var path2 = path.split(path.length * 0.3);
* path2.strokeColor = 'red';
*
* // Move the newly created path 40px to the right:
* path2.position.x += 40;
*
* @example {@paperscript} // Splitting a closed path
* var path = new Path.Rectangle({
* from: [20, 20],
* to: [80, 80],
* strokeColor: 'black'
* });
*
* // Split the path at 60% of its length:
* path.split(path.length * 0.6);
*
* // Move the first segment, to show where the path
* // was split:
* path.firstSegment.point.x += 20;
*
* // Select the first segment:
* path.firstSegment.selected = true;
*/
/**
* Splits the path at the given curve location. After splitting, the path
* will be open. If the path was open already, splitting will result in two
* paths.
*
* @name Path#split
* @function
* @param {CurveLocation} location the curve location at which to split
* the path
* @return {Path} the newly created path after splitting, if any * @return {Path} the newly created path after splitting, if any
* *
* @example {@paperscript} * @example {@paperscript}
@ -1156,23 +1112,18 @@ var Path = PathItem.extend(/** @lends Path# */{
* angle: 30 * angle: 30
* }; * };
* *
* var curveLocation = path.getNearestLocation(pointOnCircle); * var location = path.getNearestLocation(pointOnCircle);
* *
* path.split(curveLocation); * path.splitAt(location);
* path.lastSegment.selected = true; * path.lastSegment.selected = true;
*/
/**
* Splits the path at the given curve index and parameter. After splitting,
* the path will be open. If the path was open already, splitting will
* result in two paths.
* *
* @example {@paperscript} // Splitting an open path * @example {@paperscript} // Splitting an open path
* // Draw a V shaped path: * // Draw a V shaped path:
* var path = new Path([20, 20], [50, 80], [80, 20]); * var path = new Path([20, 20], [50, 80], [80, 20]);
* path.strokeColor = 'black'; * path.strokeColor = 'black';
* *
* // Split the path half-way down its second curve: * // Split the path half-way:
* var path2 = path.split(1, 0.5); * var path2 = path.splitAt(path2.length / 2);
* *
* // Give the resulting path a red stroke-color * // Give the resulting path a red stroke-color
* // and move it 20px to the right: * // and move it 20px to the right:
@ -1186,8 +1137,8 @@ var Path = PathItem.extend(/** @lends Path# */{
* strokeColor: 'black' * strokeColor: 'black'
* }); * });
* *
* // Split the path half-way down its second curve: * // Split the path half-way:
* path.split(2, 0.5); * path.splitAt(path.length / 2);
* *
* // Move the first segment, to show where the path * // Move the first segment, to show where the path
* // was split: * // was split:
@ -1195,44 +1146,27 @@ var Path = PathItem.extend(/** @lends Path# */{
* *
* // Select the first segment: * // Select the first segment:
* path.firstSegment.selected = true; * path.firstSegment.selected = true;
*
* @param {Number} index the index of the curve in the {@link Path#curves}
* array at which to split
* @param {Number} parameter the curve-time parameter at which the curve
* will be split
* @return {Path} the newly created path after splitting, if any
*/ */
split: function(index, parameter) { splitAt: function(location) {
if (parameter === null) var index = location && location.index,
return null; time = location && location.time,
if (arguments.length === 1) { tMin = /*#=*/Numerical.CURVETIME_EPSILON,
var arg = index;
// split(offset), convert offset to location
if (typeof arg === 'number')
arg = this.getLocationAt(arg);
if (!arg)
return null;
// split(location)
index = arg.index;
parameter = arg.parameter;
}
var tMin = /*#=*/Numerical.CURVETIME_EPSILON,
tMax = 1 - tMin; tMax = 1 - tMin;
if (parameter >= tMax) { if (time >= tMax) {
// t == 1 is the same as t == 0 and index ++ // time == 1 is the same location as time == 0 and index++
index++; index++;
parameter--; time = 0;
} }
var curves = this.getCurves(); var curves = this.getCurves();
if (index >= 0 && index < curves.length) { if (index >= 0 && index < curves.length) {
// Only divide curves if we're not on an existing segment already. // Only divide curves if we're not on an existing segment already.
if (parameter >= tMin) { if (time >= tMin) {
// Divide the curve with the index at given parameter. // Divide the curve with the index at the given curve-time.
// Increase because dividing adds more segments to the path. // Increase because dividing adds more segments to the path.
curves[index++].divide(parameter, true); curves[index++].divideAtTime(time);
} }
// Create the new path with the segments to the right of given // Create the new path with the segments to the right of given
// parameter, which are removed from the current path. Pass true // curve-time, which are removed from the current path. Pass true
// for includeCurves, since we want to preserve and move them to // for includeCurves, since we want to preserve and move them to
// the new path through _add(), allowing us to have CurveLocation // the new path through _add(), allowing us to have CurveLocation
// keep the connection to the new path through moved curves. // keep the connection to the new path through moved curves.
@ -1261,6 +1195,17 @@ var Path = PathItem.extend(/** @lends Path# */{
return null; return null;
}, },
/**
* @deprecated, use use {@link #splitAt(offset)} instead.
*/
split: function(index, time) {
var curve,
location = time === undefined ? index
: (curve = this.getCurves()[index])
&& curve.getLocationAtTime(time);
return location ? this.splitAt(location) : null;
},
/** /**
* Reverses the orientation of the path, by reversing all its segments. * Reverses the orientation of the path, by reversing all its segments.
*/ */
@ -1649,8 +1594,8 @@ var Path = PathItem.extend(/** @lends Path# */{
// Now see if we're on a segment, and if so, check for its // Now see if we're on a segment, and if so, check for its
// stroke join / cap first. If not, do a normal radius check // stroke join / cap first. If not, do a normal radius check
// for round strokes. // for round strokes.
var parameter = loc.getParameter(); var time = loc.getTime();
if (parameter === 0 || parameter === 1 && numSegments > 1) { if (time === 0 || time === 1 && numSegments > 1) {
if (!checkSegmentStroke(loc.getSegment())) if (!checkSegmentStroke(loc.getSegment()))
loc = null; loc = null;
} else if (!isCloseEnough(loc.getPoint(), strokePadding)) { } else if (!isCloseEnough(loc.getPoint(), strokePadding)) {
@ -1693,8 +1638,8 @@ var Path = PathItem.extend(/** @lends Path# */{
// NOTE: (For easier searching): This loop produces: // NOTE: (For easier searching): This loop produces:
// getPointAt, getTangentAt, getNormalAt, getWeightedTangentAt, // getPointAt, getTangentAt, getNormalAt, getWeightedTangentAt,
// getWeightedNormalAt, getCurvatureAt // getWeightedNormalAt, getCurvatureAt
this[name + 'At'] = function(offset, isParameter) { this[name + 'At'] = function(offset) {
var loc = this.getLocationAt(offset, isParameter); var loc = this.getLocationAt(offset);
return loc && loc[name](); return loc && loc[name]();
}; };
}, },
@ -1741,19 +1686,11 @@ var Path = PathItem.extend(/** @lends Path# */{
* *
* @param {Number} offset the offset on the path, where `0` is at * @param {Number} offset the offset on the path, where `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]
* @return {CurveLocation} the curve location at the specified offset * @return {CurveLocation} the curve location at the specified offset
*/ */
getLocationAt: function(offset, isParameter) { getLocationAt: function(offset) {
var curves = this.getCurves(), var curves = this.getCurves(),
length = 0; length = 0;
if (isParameter) {
// offset consists of curve index and curve parameter, before and
// after the fractional digit.
var index = ~~offset, // = Math.floor()
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,
curve = curves[i]; curve = curves[i];
@ -1777,7 +1714,6 @@ var Path = PathItem.extend(/** @lends Path# */{
* @function * @function
* @param {Number} offset the offset on the path, where `0` is at * @param {Number} offset the offset on the path, where `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]
* @return {Point} the point at the given offset * @return {Point} the point at the given offset
* *
* @example {@paperscript height=150} * @example {@paperscript height=150}
@ -1840,7 +1776,6 @@ var Path = PathItem.extend(/** @lends Path# */{
* @function * @function
* @param {Number} offset the offset on the path, where `0` is at * @param {Number} offset the offset on the path, where `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]
* @return {Point} the normalized tangent vector at the given offset * @return {Point} the normalized tangent vector at the given offset
* *
* @example {@paperscript height=150} * @example {@paperscript height=150}
@ -1907,7 +1842,6 @@ var Path = PathItem.extend(/** @lends Path# */{
* @function * @function
* @param {Number} offset the offset on the path, where `0` is at * @param {Number} offset the offset on the path, where `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]
* @return {Point} the normal vector at the given offset * @return {Point} the normal vector at the given offset
* *
* @example {@paperscript height=150} * @example {@paperscript height=150}
@ -1974,7 +1908,6 @@ var Path = PathItem.extend(/** @lends Path# */{
* @function * @function
* @param {Number} offset the offset on the path, where `0` is at * @param {Number} offset the offset on the path, where `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]
* @return {Point} the weighted tangent vector at the given offset * @return {Point} the weighted tangent vector at the given offset
*/ */
@ -1985,7 +1918,6 @@ var Path = PathItem.extend(/** @lends Path# */{
* @function * @function
* @param {Number} offset the offset on the path, where `0` is at * @param {Number} offset the offset on the path, where `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]
* @return {Point} the weighted normal vector at the given offset * @return {Point} the weighted normal vector at the given offset
*/ */
@ -1999,7 +1931,6 @@ var Path = PathItem.extend(/** @lends Path# */{
* @function * @function
* @param {Number} offset the offset on the path, where `0` is at * @param {Number} offset the offset on the path, where `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]
* @return {Number} the normal vector at the given offset * @return {Number} the normal vector at the given offset
*/ */
@ -2878,9 +2809,9 @@ statics: {
// Handles both 'bevel' and 'miter' joins, as they share a lot of code. // Handles both 'bevel' and 'miter' joins, as they share a lot of code.
var curve2 = segment.getCurve(), var curve2 = segment.getCurve(),
curve1 = curve2.getPrevious(), curve1 = curve2.getPrevious(),
point = curve2.getPointAt(0, true), point = curve2.getPointAtTime(0),
normal1 = curve1.getNormalAt(1, true), normal1 = curve1.getNormalAtTime(1),
normal2 = curve2.getNormalAt(0, true), normal2 = curve2.getNormalAtTime(0),
step = normal1.getDirectedAngle(normal2) < 0 ? -radius : radius; step = normal1.getDirectedAngle(normal2) < 0 ? -radius : radius;
normal1.setLength(step); normal1.setLength(step);
normal2.setLength(step); normal2.setLength(step);
@ -2923,11 +2854,12 @@ statics: {
} }
// For square caps, we need to step away from point in the direction of // For square caps, we need to step away from point in the direction of
// the tangent, which is the rotated normal. // the tangent, which is the rotated normal.
// Checking loc.getParameter() for 0 is to see whether this is the first // Checking loc.getTime() for 0 is to see whether this is the first
// or the last segment of the open path, in order to determine in which // or the last segment of the open path, in order to determine in which
// direction to move the point. // direction to move the point.
if (cap === 'square') if (cap === 'square')
point = point.add(normal.rotate(loc.getParameter() === 0 ? -90 : 90)); point = point.add(normal.rotate(
loc.getTime() === 0 ? -90 : 90));
addPoint(point.add(normal)); addPoint(point.add(normal));
addPoint(point.subtract(normal)); addPoint(point.subtract(normal));
}, },

View file

@ -248,26 +248,26 @@ PathItem.inject(new function() {
results.unshift(loc); results.unshift(loc);
} }
var curve = loc._curve, var curve = loc._curve,
t = loc._parameter, time = loc._time,
origT = t, origTime = time,
segment; segment;
if (curve !== prevCurve) { if (curve !== prevCurve) {
// This is a new curve, update noHandles setting. // This is a new curve, update noHandles setting.
noHandles = !curve.hasHandles(); noHandles = !curve.hasHandles();
} else if (prevT > 0) { } else if (prevTime > 0) {
// Scale parameter when we are splitting same curve multiple // Scale parameter when we are splitting same curve multiple
// times, but avoid dividing by zero. // times, but avoid dividing by zero.
t /= prevT; time /= prevTime;
} }
if (t < tMin) { if (time < tMin) {
segment = curve._segment1; segment = curve._segment1;
} else if (t > tMax) { } else if (time > tMax) {
segment = curve._segment2; segment = curve._segment2;
} else { } else {
// Split the curve at t, passing true for _setHandles to always // Split the curve at time, passing true for _setHandles to
// set the handles on the sub-curves even if the original curve // always set the handles on the sub-curves even if the original
// had no handles. // curve had no handles.
var newCurve = curve.divide(t, true, true); var newCurve = curve.divideAtTime(time, true);
// Keep track of curves without handles, so they can be cleared // Keep track of curves without handles, so they can be cleared
// again at the end. // again at the end.
if (noHandles) if (noHandles)
@ -294,7 +294,7 @@ PathItem.inject(new function() {
segment._intersection = dest; segment._intersection = dest;
} }
prevCurve = curve; prevCurve = curve;
prevT = origT; prevTime = origTime;
} }
// Clear segment handles if they were part of a curve with no handles, // Clear segment handles if they were part of a curve with no handles,
// once we are done with the entire curve. // once we are done with the entire curve.
@ -445,9 +445,9 @@ PathItem.inject(new function() {
var curve = entry.curve, var curve = entry.curve,
path = curve._path, path = curve._path,
parent = path._parent, parent = path._parent,
t = curve.getParameterAt(length), t = curve.getTimeAt(length),
pt = curve.getPointAt(t, true), pt = curve.getPointAtTime(t),
hor = Math.abs(curve.getTangentAt(t, true).y) hor = Math.abs(curve.getTangentAtTime(t).y)
< /*#=*/Numerical.TRIGONOMETRIC_EPSILON; < /*#=*/Numerical.TRIGONOMETRIC_EPSILON;
if (parent instanceof CompoundPath) if (parent instanceof CompoundPath)
path = parent; path = parent;

View file

@ -91,11 +91,11 @@ var PathIterator = Base.extend({
this.parts = parts; this.parts = parts;
this.length = length; this.length = length;
// Keep a current index from the part where we last where in // Keep a current index from the part where we last where in
// getParameterAt(), to optimise for iterator-like usage of iterator. // getTimeAt(), to optimise for iterator-like usage of iterator.
this.index = 0; this.index = 0;
}, },
getParameterAt: function(offset) { getTimeAt: function(offset) {
// Make sure we're not beyond the requested offset already. Search the // Make sure we're not beyond the requested offset already. Search the
// start position backwards from where to then process the loop below. // start position backwards from where to then process the loop below.
var i, j = this.index; var i, j = this.index;
@ -135,8 +135,8 @@ var PathIterator = Base.extend({
}, },
drawPart: function(ctx, from, to) { drawPart: function(ctx, from, to) {
from = this.getParameterAt(from); from = this.getTimeAt(from);
to = this.getParameterAt(to); to = this.getTimeAt(to);
for (var i = from.index; i <= to.index; i++) { for (var i = from.index; i <= to.index; i++) {
var curve = Curve.getPart(this.curves[i], var curve = Curve.getPart(this.curves[i],
i == from.index ? from.value : 0, i == from.index ? from.value : 0,
@ -149,7 +149,7 @@ var PathIterator = Base.extend({
}, Base.each(Curve._evaluateMethods, }, Base.each(Curve._evaluateMethods,
function(name) { function(name) {
this[name + 'At'] = function(offset, weighted) { this[name + 'At'] = function(offset, weighted) {
var param = this.getParameterAt(offset); var param = this.getTimeAt(offset);
return Curve[name](this.curves[param.index], param.value, weighted); return Curve[name](this.curves[param.index], param.value, weighted);
}; };
}, {}) }, {})

View file

@ -12,31 +12,7 @@
QUnit.module('Curve'); QUnit.module('Curve');
test('Curve#getParameterOf()', function() { test('Curve#getPointAtTime()', function() {
// For issue #708:
var path = new Path.Rectangle({
center: new Point(300, 100),
size: new Point(100, 100),
strokeColor: 'black'
});
for (var pos = 0; pos < path.length; pos += 10) {
var point1 = path.getPointAt(pos),
point2 = null;
for (var i = 0; i < path.curves.length; i++) {
var curve = path.curves[i];
var parameter = curve.getParameterOf(point1);
if (parameter) {
point2 = curve.getLocationAt(parameter, true).point;
break;
}
}
equals(point1, point2, 'curve.getLocationAt(curve.getParameterOf('
+ point1 + ')).point;');
}
});
test('Curve#getPointAt()', function() {
var curve = new Path.Circle({ var curve = new Path.Circle({
center: [100, 100], center: [100, 100],
radius: 100 radius: 100
@ -52,15 +28,18 @@ test('Curve#getPointAt()', function() {
for (var i = 0; i < points.length; i++) { for (var i = 0; i < points.length; i++) {
var entry = points[i]; var entry = points[i];
equals(curve.getPointAtTime(entry[0]), entry[1],
'curve.getPointAtTime(' + entry[0] + ');');
// Legacy version:
equals(curve.getPointAt(entry[0], true), entry[1], equals(curve.getPointAt(entry[0], true), entry[1],
'curve.getPointAt(' + entry[0] + ', true);'); 'Legacy: curve.getPointAt(' + entry[0] + ', true);');
} }
equals(curve.getPointAt(curve.length + 1), null, equals(curve.getPointAt(curve.length + 1), null,
'Should return null when offset is out of range.'); 'Should return null when offset is out of range.');
}); });
test('Curve#getTangentAt()', function() { test('Curve#getTangentAtTime()', function() {
var curve = new Path.Circle({ var curve = new Path.Circle({
center: [100, 100], center: [100, 100],
radius: 100 radius: 100
@ -76,14 +55,19 @@ 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.getTangentAtTime(entry[0]), entry[1].normalize(),
'curve.getTangentAtTime(' + entry[0] + ');');
equals(curve.getWeightedTangentAtTime(entry[0]), entry[1],
'curve.getWeightedTangentAtTime(' + entry[0] + ');');
// Legacy version:
equals(curve.getTangentAt(entry[0], true), entry[1].normalize(), equals(curve.getTangentAt(entry[0], true), entry[1].normalize(),
'curve.getTangentAt(' + entry[0] + ', true);'); 'Legacy: curve.getTangentAt(' + entry[0] + ', true);');
equals(curve.getWeightedTangentAt(entry[0], true), entry[1], equals(curve.getWeightedTangentAt(entry[0], true), entry[1],
'curve.getWeightedTangentAt(' + entry[0] + ', true);'); 'Legacy: curve.getWeightedTangentAt(' + entry[0] + ', true);');
} }
}); });
test('Curve#getNormalAt()', function() { test('Curve#getNormalAtTime()', function() {
var curve = new Path.Circle({ var curve = new Path.Circle({
center: [100, 100], center: [100, 100],
radius: 100 radius: 100
@ -99,14 +83,19 @@ 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.getNormalAtTime(entry[0]), entry[1].normalize(),
'curve.getNormalAtTime(' + entry[0] + ');');
equals(curve.getWeightedNormalAtTime(entry[0]), entry[1],
'curve.getWeightedNormalAtTime(' + entry[0] + ');');
// Legacy version:
equals(curve.getNormalAt(entry[0], true), entry[1].normalize(), equals(curve.getNormalAt(entry[0], true), entry[1].normalize(),
'curve.getNormalAt(' + entry[0] + ', true);'); 'Legacy: curve.getNormalAt(' + entry[0] + ', true);');
equals(curve.getWeightedNormalAt(entry[0], true), entry[1], equals(curve.getWeightedNormalAt(entry[0], true), entry[1],
'curve.getWeightedNormalAt(' + entry[0] + ', true);'); 'Legacy: curve.getWeightedNormalAt(' + entry[0] + ', true);');
} }
}); });
test('Curve#getCurvatureAt()', function() { test('Curve#getCurvatureAtTime()', function() {
var curve = new Path.Circle({ var curve = new Path.Circle({
center: [100, 100], center: [100, 100],
radius: 100 radius: 100
@ -122,12 +111,15 @@ test('Curve#getCurvatureAt()', function() {
for (var i = 0; i < curvatures.length; i++) { for (var i = 0; i < curvatures.length; i++) {
var entry = curvatures[i]; var entry = curvatures[i];
equals(curve.getCurvatureAtTime(entry[0]), entry[1],
'curve.getCurvatureAtTime(' + entry[0] + ');');
// Legacy version:
equals(curve.getCurvatureAt(entry[0], true), entry[1], equals(curve.getCurvatureAt(entry[0], true), entry[1],
'curve.getCurvatureAt(' + entry[0] + ', true);'); 'Legacy: curve.getCurvatureAt(' + entry[0] + ', true);');
} }
}); });
test('Curve#getCurvatureAt()', function() { test('Curve#getCurvatureAtTime()', function() {
var curve = new Path.Line({ var curve = new Path.Line({
from: [100, 100], from: [100, 100],
to: [200, 200], to: [200, 200],
@ -143,12 +135,15 @@ test('Curve#getCurvatureAt()', function() {
for (var i = 0; i < curvatures.length; i++) { for (var i = 0; i < curvatures.length; i++) {
var entry = curvatures[i]; var entry = curvatures[i];
equals(curve.getCurvatureAtTime(entry[0]), entry[1],
'curve.getCurvatureAtTime(' + entry[0] + ');');
// Legacy version:
equals(curve.getCurvatureAt(entry[0], true), entry[1], equals(curve.getCurvatureAt(entry[0], true), entry[1],
'curve.getCurvatureAt(' + entry[0] + ', true);'); 'Legacy: curve.getCurvatureAt(' + entry[0] + ', true);');
} }
}); });
test('Curve#getParameterAt()', function() { test('Curve#getTimeAt()', function() {
var curve = new Path([ var curve = new Path([
[[0, 0], [0, 0], [100, 0]], [[0, 0], [0, 0], [100, 0]],
[[200, 200]], [[200, 200]],
@ -157,14 +152,16 @@ test('Curve#getParameterAt()', function() {
for (var f = 0; f <= 1; f += 0.1) { for (var f = 0; f <= 1; f += 0.1) {
var o1 = curve.length * f; var o1 = curve.length * f;
var o2 = -curve.length * (1 - f); var o2 = -curve.length * (1 - f);
var t1 = curve.getParameterAt(o1); var message = 'Curve-time parameter at offset ' + o1
var t2 = curve.getParameterAt(o2); + ' should be the same value as at offset' + o2;
equals(t1, t2, 'Curve parameter at offset ' + o1 equals(curve.getTimeAt(o1), curve.getTimeAt(o2), message,
+ ' should be the same value as at offset' + o2,
Numerical.CURVETIME_EPSILON); Numerical.CURVETIME_EPSILON);
// Legacy version:
equals(curve.getParameterAt(o1), curve.getParameterAt(o2),
'Legacy: ' + message, Numerical.CURVETIME_EPSILON);
} }
equals(curve.getParameterAt(curve.length + 1), null, equals(curve.getTimeAt(curve.length + 1), null,
'Should return null when offset is out of range.'); 'Should return null when offset is out of range.');
}); });
@ -176,7 +173,6 @@ test('Curve#getLocationAt()', function() {
equals(curve.getLocationAt(curve.length + 1), null, equals(curve.getLocationAt(curve.length + 1), null,
'Should return null when offset is out of range.'); 'Should return null when offset is out of range.');
// 'Should return null when point is not on the curve.');
}); });
test('Curve#isStraight()', function() { test('Curve#isStraight()', function() {
@ -223,3 +219,31 @@ test('Curve#isLinear()', function() {
return new Curve([100, 100], null, null, [200, 200]).isLinear(); return new Curve([100, 100], null, null, [200, 200]).isLinear();
}, false); }, false);
}); });
test('Curve#getTimeOf()', function() {
// For issue #708:
var path = new Path.Rectangle({
center: new Point(300, 100),
size: new Point(100, 100),
strokeColor: 'black'
});
for (var pos = 0; pos < path.length; pos += 10) {
var point1 = path.getPointAt(pos),
point2 = null;
for (var i = 0; i < path.curves.length; i++) {
var curve = path.curves[i];
var time = curve.getTimeOf(point1);
if (time) {
// Legacy-check-hack:
equals(curve.getParameterOf(point1), time,
'Legacy: curve.getParameterOf() should return the same'
+ ' as curve.getTimeOf()');
point2 = curve.getLocationAtTime(time).point;
break;
}
}
equals(point1, point2, 'curve.getLocationAt(curve.getTimeOf('
+ point1 + ')).point;');
}
});

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].getPointAt(0, true), true); testPoint(path, curves[i].getPointAtTime(0), true);
testPoint(path, curves[i].getPointAt(0.5, true), true); testPoint(path, curves[i].getPointAtTime(0.5), 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].getPointAt(0, true), true); testPoint(path, curves[i].getPointAtTime(0), true);
testPoint(path, curves[i].getPointAt(0.5, true), true); testPoint(path, curves[i].getPointAtTime(0.5), true);
} }
}); });

View file

@ -20,7 +20,7 @@ function testIntersection(intersections, results) {
var name = 'intersections[' + i + ']'; var name = 'intersections[' + i + ']';
equals(inter.point, new Point(values.point), name + '.point'); equals(inter.point, new Point(values.point), name + '.point');
equals(inter.index, values.index, name + '.index'); equals(inter.index, values.index, name + '.index');
equals(inter.parameter, values.parameter || 0, name + '.parameter'); equals(inter.time, values.time || 0, name + '.time');
equals(inter.isCrossing(), values.crossing || false, name + '.isCrossing()'); equals(inter.isCrossing(), values.crossing || false, name + '.isCrossing()');
} }
} }
@ -31,8 +31,8 @@ test('#565', function() {
var path1 = new Path([curve1.segment1, curve1.segment2]); var path1 = new Path([curve1.segment1, curve1.segment2]);
var path2 = new Path([curve2.segment1, curve2.segment2]); var path2 = new Path([curve2.segment1, curve2.segment2]);
testIntersection(curve1.getIntersections(curve2), [ testIntersection(curve1.getIntersections(curve2), [
{ point: { x: 354.13635, y: 220.81369 }, index: 0, parameter: 0.46725, crossing: true }, { point: { x: 354.13635, y: 220.81369 }, index: 0, time: 0.46725, crossing: true },
{ point: { x: 390.24772, y: 224.27351 }, index: 0, parameter: 0.71605, crossing: true } { point: { x: 390.24772, y: 224.27351 }, index: 0, time: 0.71605, crossing: true }
]); ]);
// Alternative pair of curves that has the same issue // Alternative pair of curves that has the same issue
@ -41,7 +41,7 @@ test('#565', function() {
var path1 = new Path([curve1.segment1, curve1.segment2]); var path1 = new Path([curve1.segment1, curve1.segment2]);
var path2 = new Path([curve2.segment1, curve2.segment2]); var path2 = new Path([curve2.segment1, curve2.segment2]);
testIntersection(curve1.getIntersections(curve2), [ testIntersection(curve1.getIntersections(curve2), [
{ point: { x: 335.62744, y: 338.15939 }, index: 0, parameter: 0.26516, crossing: true } { point: { x: 335.62744, y: 338.15939 }, index: 0, time: 0.26516, crossing: true }
]); ]);
}); });
@ -51,8 +51,8 @@ test('#568', function() {
var path1 = new Path([curve1.segment1, curve1.segment2]); var path1 = new Path([curve1.segment1, curve1.segment2]);
var path2 = new Path([curve2.segment1, curve2.segment2]); var path2 = new Path([curve2.segment1, curve2.segment2]);
testIntersection(curve1.getIntersections(curve2), [ testIntersection(curve1.getIntersections(curve2), [
{ point: { x: 547.96568, y: 396.66339 }, index: 0, parameter: 0.07024, crossing: true }, { point: { x: 547.96568, y: 396.66339 }, index: 0, time: 0.07024, crossing: true },
{ point: { x: 504.79973, y: 383.37886 }, index: 0, parameter: 0.48077, crossing: true } { point: { x: 504.79973, y: 383.37886 }, index: 0, time: 0.48077, crossing: true }
]); ]);
var curve1 = new Curve(new Point(0, 0), new Point(20, 40) , new Point (-30, -50), new Point(50, 50)); var curve1 = new Curve(new Point(0, 0), new Point(20, 40) , new Point (-30, -50), new Point(50, 50));
@ -60,7 +60,7 @@ test('#568', function() {
var path1 = new Path([curve1.segment1, curve1.segment2]); var path1 = new Path([curve1.segment1, curve1.segment2]);
var path2 = new Path([curve2.segment1, curve2.segment2]); var path2 = new Path([curve2.segment1, curve2.segment2]);
testIntersection(curve1.getIntersections(curve2), [ testIntersection(curve1.getIntersections(curve2), [
{ point: { x: 50, y: 50 }, index: 0, parameter: 1, crossing: false } { point: { x: 50, y: 50 }, index: 0, time: 1, crossing: false }
]); ]);
}); });
@ -71,7 +71,7 @@ test('#570', function() {
var path2 = new Path([curve2.segment1, curve2.segment2]); var path2 = new Path([curve2.segment1, curve2.segment2]);
var ints = curve1.getIntersections(curve2); var ints = curve1.getIntersections(curve2);
testIntersection(curve1.getIntersections(curve2), [ testIntersection(curve1.getIntersections(curve2), [
{ point: { x: 311.16035, y: 406.29853 }, index: 0, parameter: 1, crossing: false } { point: { x: 311.16035, y: 406.29853 }, index: 0, time: 1, crossing: false }
]); ]);
}); });
@ -81,8 +81,8 @@ test('#571', function() {
var path1 = new Path([curve1.segment1, curve1.segment2]); var path1 = new Path([curve1.segment1, curve1.segment2]);
var path2 = new Path([curve2.segment1, curve2.segment2]); var path2 = new Path([curve2.segment1, curve2.segment2]);
testIntersection(curve1.getIntersections(curve2), [ testIntersection(curve1.getIntersections(curve2), [
{ point: { x: 352.39945, y: 330.44135 }, index: 0, parameter: 0.41159, crossing: true }, { point: { x: 352.39945, y: 330.44135 }, index: 0, time: 0.41159, crossing: true },
{ point: { x: 420.12359, y: 275.83519 }, index: 0, parameter: 1, crossing: false } { point: { x: 420.12359, y: 275.83519 }, index: 0, time: 1, crossing: false }
]); ]);
}); });
@ -101,7 +101,7 @@ test('circle and square (existing segments overlaps on curves)', function() {
var path1 = new Path.Circle(new Point(110, 110), 80); var path1 = new Path.Circle(new Point(110, 110), 80);
var path2 = new Path.Rectangle(new Point(110, 110), [100, 100]); var path2 = new Path.Rectangle(new Point(110, 110), [100, 100]);
testIntersection(path1.getIntersections(path2), [ testIntersection(path1.getIntersections(path2), [
{ point: { x: 190, y: 110 }, index: 2, parameter: 0, crossing: true }, { point: { x: 190, y: 110 }, index: 2, time: 0, crossing: true },
{ point: { x: 110, y: 190 }, index: 3, parameter: 0, crossing: true } { point: { x: 110, y: 190 }, index: 3, time: 0, crossing: true }
]); ]);
}); });

View file

@ -21,16 +21,16 @@ test('path.length', function() {
var length = path.length; var length = path.length;
equals(length, 172.10112809179614, 'path.length'); equals(length, 172.10112809179614, 'path.length');
var param = path.curves[0].getParameterAt(length / 4); var t = path.curves[0].getTimeAt(length / 4);
equals(param, 0.2255849553116685, 'path.curves[0].getParameterAt(length / 4)'); equals(t, 0.2255849553116685, 'path.curves[0].getTimeAt(length / 4)');
}); });
test('curve.getParameter with straight curve', function() { test('curve.getTimeAt() with straight curve', function() {
var path = new Path(); var path = new Path();
path.moveTo(100, 100); path.moveTo(100, 100);
path.lineTo(500, 500); path.lineTo(500, 500);
var curve = path.curves[0]; var curve = path.curves[0];
var length = curve.length; var length = curve.length;
var t = curve.getParameterAt(length / 3); var t = curve.getTimeAt(length / 3);
equals(t, 0.3869631475722452); equals(t, 0.3869631475722452);
}); });