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;
// // 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, " ") });
// // ixs.map(function(a) { markPoint(a.point, " "); });
@ -402,7 +402,7 @@
t = t || s.index;
if (remove === undefined) { remove = true; }
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 cir = new Path.Circle(s.point, 2);
cir.style.fillColor = c;
@ -427,12 +427,12 @@
tc = tc || '#ccc';
t = t || crv.index;
if (remove === undefined) { remove = true; }
var p = crv.getPointAt(0.57, true);
var t1 = crv.getTangentAt(0.57, true).normalize(-10);
var p = crv.getPointAtTime(0.57);
var t1 = crv.getTangentAtTime(0.57).normalize(-10);
var p2 = p.clone().add(t1);
var l = 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 text = new PointText(p.subtract([0, -4]));
text.justification = 'center';

View file

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

View file

@ -27,7 +27,7 @@ paper = new (PaperScope.inject(Base.exports, {
// Export jsdom document and window too, for Node.js
document: document,
window: window,
// TODO: Remove in 2017 (deprecated January 2016):
// TODO: Remove in 1.0.0? (deprecated January 2016):
Symbol: SymbolDefinition,
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
* 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.
*
* @param {Number} from the curve-time parameter at which the sub-curve
@ -439,46 +439,44 @@ var Curve = Base.extend(/** @lends Curve# */{
// 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
* 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.
*
* @name Curve#divide
* @function
* @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
* @param {Number|CurveLocation} location the offset or location 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 #divideAtTime(time)
*/
// TODO: Rename to divideAt()?
divide: function(offset, isParameter, _setHandles) {
var parameter = this._getParameter(offset, isParameter),
tMin = /*#=*/Numerical.CURVETIME_EPSILON,
divideAt: function(location) {
// Accept offsets and CurveLocation objects, as well as objects that act
// like them.
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,
res = null;
// Only divide if not at the beginning or end.
if (parameter >= tMin && parameter <= tMax) {
var parts = Curve.subdivide(this.getValues(), parameter),
if (time >= tMin && time <= tMax) {
var parts = Curve.subdivide(this.getValues(), time),
left = parts[0],
right = parts[1],
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
* will result in two paths.
*
* @name Curve#split
* @function
* @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
* @param {Number|CurveLocation} location the offset or location on the
* curve at which to split
* @return {Path} the newly created path after splitting, if any
* @see Path#split(index, parameter)
* @see Path#splitAt(offset)
*/
// TODO: Rename to splitAt()?
split: function(offset, isParameter) {
return this._path
? this._path.split(this._segment1._index,
this._getParameter(offset, isParameter))
: null;
splitAt: function(location) {
return this._path ? this._path.splitAt(location) : 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);
},
getParameterOf: function(v, point) {
getTimeOf: function(v, point) {
// Before solving cubics, compare the beginning and end of the curve
// with zero epsilon:
var p1 = new Point(v[0], v[1]),
@ -642,7 +666,7 @@ statics: {
: null;
},
getNearestParameter: function(v, point) {
getNearestTime: function(v, point) {
if (Curve.isStraight(v)) {
var p1x = v[0], p1y = v[1],
p2x = v[6], p2y = v[7],
@ -656,7 +680,7 @@ statics: {
var u = ((point.x - p1x) * vx + (point.y - p1y) * vy) / det;
return u < /*#=*/Numerical.EPSILON ? 0
: u > /*#=*/(1 - Numerical.EPSILON) ? 1
: Curve.getParameterOf(v,
: Curve.getTimeOf(v,
new Point(p1x + u * vx, p1y + u * vy));
}
@ -951,7 +975,7 @@ statics: {
* @return {Boolean} {@true if the line is horizontal}
*/
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;
},
@ -961,59 +985,64 @@ statics: {
* @return {Boolean} {@true if the line is vertical}
*/
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;
}
}), /** @lends Curve# */{
// Explicitly deactivate the creation of beans, as we have functions here
// that look like bean getters but actually read arguments.
// See #getParameterOf(), #getLocationOf(), #getNearestLocation(), ...
// See #getTimeOf(), #getLocationOf(), #getNearestLocation(), ...
beans: false,
/**
* {@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,
* 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`
* and `1` for negative values of `offset`.
*
* @param {Number} offset
* @param {Number} [start]
* @return {Number} the curve time parameter at the specified offset
* @param {Number} offset the offset at which to find the curve-time, in
* curve length units
* @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) {
return Curve.getParameterAt(this.getValues(), offset, start);
getTimeAt: function(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
* curve, `null` otherwise.
*
* @param {Point} point the point on the curve
* @return {Number} the curve time parameter of the specified point
* @deprecated, use use {@link #getTimeOf(point)} instead.
*/
getParameterOf: function(/* point */) {
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;
},
getParameterAt: '#getTimeAt',
/**
* 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
*/
getLocationOf: function(/* point */) {
return this.getLocationAt(this.getParameterOf(Point.read(arguments)),
true);
return this.getLocationAtTime(this.getTimeOf(Point.read(arguments)));
},
/**
@ -1039,6 +1067,23 @@ statics: {
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.
*
@ -1050,7 +1095,7 @@ statics: {
getNearestLocation: function(/* point */) {
var point = Point.read(arguments),
values = this.getValues(),
t = Curve.getNearestParameter(values, point),
t = Curve.getNearestTime(values, point),
pt = Curve.getPoint(values, t);
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
* @function
* @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 {Point} the point on the curve at the given offset
* @param {Number|CurveLocation} location the offset or location 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
* offset.
* location.
*
* @name Curve#getTangentAt
* @function
* @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 {Point} the normalized tangent of the curve at the given offset
* @param {Number|CurveLocation} location the offset or location 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 offset.
* Calculates the normal vector of the curve at the given location.
*
* @name Curve#getNormalAt
* @function
* @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 {Point} the normal of the curve at the given offset
* @param {Number|CurveLocation} location the offset or location 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 offset,
* its length reflecting the curve velocity at that location.
* Calculates the weighted tangent vector of the curve at the given
* location, 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 `isParameter` is `true`
* @param {Boolean} [isParameter=false] pass `true` if `offset` is a curve
* time parameter
* @return {Point} the weighted tangent of the curve at the given offset
* @param {Number|CurveLocation} location the offset or location 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 offset,
* Calculates the weighted normal vector of the curve at the given location,
* 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 `isParameter` is `true`
* @param {Boolean} [isParameter=false] pass `true` if `offset` is a curve
* time parameter
* @return {Point} the weighted normal of the curve at the given offset
* @param {Number|CurveLocation} location the offset or location on the
* curve
* @return {Point} the weighted normal of the curve at the given location
*/
/**
* 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
* 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
* @function
* @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 {Number} the curvature of the curve at the given offset
* @param {Number|CurveLocation} location the offset or location on the
* curve
* @return {Number} the curvature of the curve at the given location
*/
/**
* 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
@ -1152,11 +1245,18 @@ new function() { // // Scope to inject various curve evaluation methods
function(name) {
// NOTE: (For easier searching): This loop produces:
// getPointAt, getTangentAt, getNormalAt, getWeightedTangentAt,
// getWeightedNormalAt, getCurvatureAt
this[name + 'At'] = function(offset, isParameter) {
// getWeightedNormalAt, getCurvatureAt, getPointAtTime,
// 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();
return Curve[name](values, isParameter ? offset
: Curve.getParameterAt(values, offset, 0));
return Curve[name](values, _isTime ? location
: Curve.getTimeAt(values, location, 0));
};
this[name + 'AtTime'] = function(time) {
return Curve[name](this.getValues(), time);
};
}, {
statics: {
@ -1306,7 +1406,7 @@ new function() { // Scope for methods that require private functions
return Numerical.integrate(ds, a, b, getIterations(a, b));
},
getParameterAt: function(v, offset, start) {
getTimeAt: function(v, offset, start) {
if (start === undefined)
start = offset < 0 ? 1 : 0;
if (offset === 0)
@ -1387,7 +1487,7 @@ new function() { // Scope for intersection using bezier fat-line clipping
tMin = /*#=*/Numerical.CURVETIME_EPSILON,
tMax = 1 - tMin;
if (t1 == null)
t1 = Curve.getParameterOf(v1, p1);
t1 = Curve.getTimeOf(v1, p1);
// Check t1 and t2 against correct bounds, based on excludeStart/End:
// - excludeStart means the start of c1 connects to the end 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) &&
t1 <= (excludeEnd ? tMax : 1)) {
if (t2 == null)
t2 = Curve.getParameterOf(v2, p2);
t2 = Curve.getTimeOf(v2, p2);
if (t2 !== null && t2 >= (excludeEnd ? tMin : 0) &&
t2 <= (excludeStart ? tMax : 1)) {
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.
var tc = roots[i],
pc = Curve.getPoint(vc, tc),
tl = Curve.getParameterOf(vl, pc);
tl = Curve.getTimeOf(vl, pc);
if (tl !== null) {
var pl = Curve.getPoint(vl, tl),
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.
for (var i = 0, maxCurvature = 0; i < count; i++) {
var curvature = Math.abs(
c1.getCurvatureAt(roots[i], true));
c1.getCurvatureAtTime(roots[i]));
if (curvature > maxCurvature) {
maxCurvature = curvature;
tSplit = roots[i];
@ -1894,7 +1994,7 @@ new function() { // Scope for intersection using bezier fat-line clipping
for (var i = 0, t1 = 0;
i < 2 && pairs.length < 2;
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 ? 1 : 7]));
if (t2 != null) { // If point is on curve

View file

@ -14,9 +14,9 @@
* @name CurveLocation
*
* @class CurveLocation objects describe a location on {@link Curve} objects, as
* defined by the curve-time {@link #parameter}, a value between `0`
* (beginning of the curve) and `1` (end of the curve). If the curve is part
* of a {@link Path} item, its {@link #index} inside the {@link Path#curves}
* defined by the curve-time {@link #time}, a value between `0` (beginning
* of the curve) and `1` (end of the curve). If the curve is part of a
* {@link Path} item, its {@link #index} inside the {@link Path#curves}
* array is also provided.
*
* 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.
*
* @param {Curve} curve
* @param {Number} parameter
* @param {Number} time
* @param {Point} [point]
*/
initialize: function CurveLocation(curve, parameter, point,
_overlap, _distance) {
initialize: function CurveLocation(curve, time, point, _overlap, _distance) {
// Merge intersections very close to the end of a curve with the
// beginning of the next curve.
if (parameter > /*#=*/(1 - Numerical.CURVETIME_EPSILON)) {
if (time > /*#=*/(1 - Numerical.CURVETIME_EPSILON)) {
var next = curve.getNext();
if (next) {
parameter = 0;
time = 0;
curve = next;
}
}
@ -58,8 +57,8 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{
// CurveLocation objects.
this._id = UID.get(CurveLocation);
this._setCurve(curve);
this._parameter = parameter;
this._point = point || curve.getPointAt(parameter, true);
this._time = time;
this._point = point || curve.getPointAtTime(time);
this._overlap = _overlap;
this._distance = _distance;
this._intersection = this._next = this._prev = null;
@ -84,7 +83,7 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{
_setSegment: function(segment) {
this._setCurve(segment.getCurve());
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()
this._point = segment._point.clone();
},
@ -100,15 +99,15 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{
var curve = this.getCurve(),
segment = this._segment;
if (!segment) {
var parameter = this.getParameter();
if (parameter === 0) {
var time = this.getTime();
if (time === 0) {
segment = curve._segment1;
} else if (parameter === 1) {
} else if (time === 1) {
segment = curve._segment2;
} else if (parameter != null) {
} else if (time != null) {
// Determine the closest segment by comparing curve lengths
segment = curve.getPartLength(0, parameter)
< curve.getPartLength(parameter, 1)
segment = curve.getPartLength(0, time)
< curve.getPartLength(time, 1)
? curve._segment1
: curve._segment2;
}
@ -128,9 +127,9 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{
that = this;
if (path && path._version !== this._version) {
// 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.
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
@ -139,7 +138,7 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{
// still, otherwise assume it's the curve before segment2.
function trySegment(segment) {
var curve = segment && segment.getCurve();
if (curve && (that._parameter = curve.getParameterOf(that._point))
if (curve && (that._time = curve.getTimeOf(that._point))
!= null) {
// Fetch path again as it could be on a new one through split()
that._setCurve(curve);
@ -185,17 +184,24 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{
* @bean
* @type Number
*/
getParameter: function() {
getTime: function() {
var curve = this.getCurve(),
parameter = this._parameter;
return curve && parameter == null
? this._parameter = curve.getParameterOf(this._point)
: parameter;
time = this._time;
return curve && time == null
? this._time = curve.getTimeOf(this._point)
: time;
},
/**
* @private
* @bean
* @deprecated use {@link #getTime()} instead.
*/
getParameter: '#getTime',
/**
* The point which is defined by the {@link #curve} and
* {@link #parameter}.
* {@link #time}.
*
* @bean
* @type Point
@ -237,8 +243,8 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{
*/
getCurveOffset: function() {
var curve = this.getCurve(),
parameter = this.getParameter();
return parameter != null && curve && curve.getPartLength(0, parameter);
time = this.getTime();
return time != null && curve && curve.getPartLength(0, time);
},
/**
@ -295,8 +301,8 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{
var curve = this.getCurve(),
res = null;
if (curve) {
res = curve.divide(this.getParameter(), true);
// Change to the newly inserted segment, also adjusting _parameter.
res = curve.divideAtTime(this.getTime());
// Change to the newly inserted segment, also adjusting _time.
if (res)
this._setSegment(res._segment1);
}
@ -305,13 +311,13 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{
split: function() {
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
* on a path, by applying the same tolerances as elsewhere when dealing with
* curve time parameters.
* curve-time parameters.
*
* @param {CurveLocation} location
* @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:
diff = abs(
((c1.isLast() && c2.isFirst() ? -1 : c1.getIndex())
+ this.getParameter()) -
+ this.getTime()) -
((c2.isLast() && c1.isFirst() ? -1 : c2.getIndex())
+ loc.getParameter()));
+ loc.getTime()));
res = (diff < /*#=*/Numerical.CURVETIME_EPSILON
// If diff isn't close enough, compare the actual offsets of
// 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();
if (index != null)
parts.push('index: ' + index);
var parameter = this.getParameter();
if (parameter != null)
parts.push('parameter: ' + f.number(parameter));
var time = this.getTime();
if (time != null)
parts.push('time: ' + f.number(time));
if (this._distance != null)
parts.push('distance: ' + f.number(this._distance));
return '{ ' + parts.join(', ') + ' }';
@ -409,8 +415,8 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{
var inter = this._intersection;
if (!inter)
return false;
var t1 = this.getParameter(),
t2 = inter.getParameter(),
var t1 = this.getTime(),
t2 = inter.getTime(),
tMin = /*#=*/Numerical.CURVETIME_EPSILON,
tMax = 1 - tMin,
// 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.
// The incomings tangents v1 & v3 are inverted, so that all angles
// are pointing outwards in the right direction from the intersection.
var v2 = c2.getTangentAt(t1Inside ? t1 : tMin, true),
v1 = (t1Inside ? v2 : c1.getTangentAt(tMax, true)).negate(),
v4 = c4.getTangentAt(t2Inside ? t2 : tMin, true),
v3 = (t2Inside ? v4 : c3.getTangentAt(tMax, true)).negate(),
var v2 = c2.getTangentAtTime(t1Inside ? t1 : tMin),
v1 = (t1Inside ? v2 : c1.getTangentAtTime(tMax)).negate(),
v4 = c4.getTangentAtTime(t2Inside ? t2 : tMin),
v3 = (t2Inside ? v4 : c3.getTangentAtTime(tMax)).negate(),
// NOTE: For shorter API calls we work with angles in degrees here:
a1 = v1.getAngle(),
a2 = v2.getAngle(),
@ -519,9 +525,9 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{
// getWeightedNormalAt, getCurvatureAt
var get = name + 'At';
this[name] = function() {
var parameter = this.getParameter(),
curve = this.getCurve();
return parameter != null && curve && curve[get](parameter, true);
var curve = this.getCurve(),
time = this.getTime();
return time != null && curve && curve[get](time, true);
};
}, {
// Do not override the existing #getPoint():
@ -530,7 +536,7 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{
new function() { // Scope for statics
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.
var length = locations.length,
l = 0,
@ -575,10 +581,10 @@ new function() { // Scope for statics
// NOTE: equals() takes the intersection location into account,
// while this calculation of diff doesn't!
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.
? (loc.getIndex() + loc.getParameter())
- (loc2.getIndex() + loc2.getParameter())
? (loc.getIndex() + loc.getTime())
- (loc2.getIndex() + loc2.getTime())
// Sort by path id to group all locs on same path.
: path1._id - path2._id;
if (diff < 0) {

View file

@ -1092,56 +1092,12 @@ var Path = PathItem.extend(/** @lends Path# */{
// TODO: reduceSegments([flatness])
/**
* Splits the path at the given offset. After splitting, the path will be
* open. If the path was open already, splitting will result in two paths.
* Splits the path at the given offset or 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 {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
* @param {Number|CurveLocation} location the offset or location at which to
* split the path
* @return {Path} the newly created path after splitting, if any
*
* @example {@paperscript}
@ -1156,23 +1112,18 @@ var Path = PathItem.extend(/** @lends Path# */{
* angle: 30
* };
*
* var curveLocation = path.getNearestLocation(pointOnCircle);
* var location = path.getNearestLocation(pointOnCircle);
*
* path.split(curveLocation);
* path.splitAt(location);
* 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
* // Draw a V shaped path:
* var path = new Path([20, 20], [50, 80], [80, 20]);
* path.strokeColor = 'black';
*
* // Split the path half-way down its second curve:
* var path2 = path.split(1, 0.5);
* // Split the path half-way:
* var path2 = path.splitAt(path2.length / 2);
*
* // Give the resulting path a red stroke-color
* // and move it 20px to the right:
@ -1186,8 +1137,8 @@ var Path = PathItem.extend(/** @lends Path# */{
* strokeColor: 'black'
* });
*
* // Split the path half-way down its second curve:
* path.split(2, 0.5);
* // Split the path half-way:
* path.splitAt(path.length / 2);
*
* // Move the first segment, to show where the path
* // was split:
@ -1195,44 +1146,27 @@ var Path = PathItem.extend(/** @lends Path# */{
*
* // Select the first segment:
* 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) {
if (parameter === null)
return null;
if (arguments.length === 1) {
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,
splitAt: function(location) {
var index = location && location.index,
time = location && location.time,
tMin = /*#=*/Numerical.CURVETIME_EPSILON,
tMax = 1 - tMin;
if (parameter >= tMax) {
// t == 1 is the same as t == 0 and index ++
if (time >= tMax) {
// time == 1 is the same location as time == 0 and index++
index++;
parameter--;
time = 0;
}
var curves = this.getCurves();
if (index >= 0 && index < curves.length) {
// Only divide curves if we're not on an existing segment already.
if (parameter >= tMin) {
// Divide the curve with the index at given parameter.
if (time >= tMin) {
// Divide the curve with the index at the given curve-time.
// 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
// 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
// the new path through _add(), allowing us to have CurveLocation
// keep the connection to the new path through moved curves.
@ -1261,6 +1195,17 @@ var Path = PathItem.extend(/** @lends Path# */{
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.
*/
@ -1649,8 +1594,8 @@ var Path = PathItem.extend(/** @lends Path# */{
// 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
// for round strokes.
var parameter = loc.getParameter();
if (parameter === 0 || parameter === 1 && numSegments > 1) {
var time = loc.getTime();
if (time === 0 || time === 1 && numSegments > 1) {
if (!checkSegmentStroke(loc.getSegment()))
loc = null;
} else if (!isCloseEnough(loc.getPoint(), strokePadding)) {
@ -1693,8 +1638,8 @@ var Path = PathItem.extend(/** @lends Path# */{
// NOTE: (For easier searching): This loop produces:
// getPointAt, getTangentAt, getNormalAt, getWeightedTangentAt,
// getWeightedNormalAt, getCurvatureAt
this[name + 'At'] = function(offset, isParameter) {
var loc = this.getLocationAt(offset, isParameter);
this[name + 'At'] = function(offset) {
var loc = this.getLocationAt(offset);
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
* 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
*/
getLocationAt: function(offset, isParameter) {
getLocationAt: function(offset) {
var curves = this.getCurves(),
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++) {
var start = length,
curve = curves[i];
@ -1777,7 +1714,6 @@ var Path = PathItem.extend(/** @lends Path# */{
* @function
* @param {Number} offset the offset on the path, where `0` is at
* the beginning of the path and {@link Path#length} at the end
* @param {Boolean} [isParameter=false]
* @return {Point} the point at the given offset
*
* @example {@paperscript height=150}
@ -1840,7 +1776,6 @@ var Path = PathItem.extend(/** @lends Path# */{
* @function
* @param {Number} offset the offset on the path, where `0` is at
* 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
*
* @example {@paperscript height=150}
@ -1907,7 +1842,6 @@ var Path = PathItem.extend(/** @lends Path# */{
* @function
* @param {Number} offset the offset on the path, where `0` is at
* 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
*
* @example {@paperscript height=150}
@ -1974,7 +1908,6 @@ var Path = PathItem.extend(/** @lends Path# */{
* @function
* @param {Number} offset the offset on the path, where `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
*/
@ -1985,7 +1918,6 @@ var Path = PathItem.extend(/** @lends Path# */{
* @function
* @param {Number} offset the offset on the path, where `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
*/
@ -1999,7 +1931,6 @@ var Path = PathItem.extend(/** @lends Path# */{
* @function
* @param {Number} offset the offset on the path, where `0` is at
* 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
*/
@ -2878,9 +2809,9 @@ statics: {
// Handles both 'bevel' and 'miter' joins, as they share a lot of code.
var curve2 = segment.getCurve(),
curve1 = curve2.getPrevious(),
point = curve2.getPointAt(0, true),
normal1 = curve1.getNormalAt(1, true),
normal2 = curve2.getNormalAt(0, true),
point = curve2.getPointAtTime(0),
normal1 = curve1.getNormalAtTime(1),
normal2 = curve2.getNormalAtTime(0),
step = normal1.getDirectedAngle(normal2) < 0 ? -radius : radius;
normal1.setLength(step);
normal2.setLength(step);
@ -2923,11 +2854,12 @@ statics: {
}
// For square caps, we need to step away from point in the direction of
// 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
// direction to move the point.
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.subtract(normal));
},

View file

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

View file

@ -91,11 +91,11 @@ var PathIterator = Base.extend({
this.parts = parts;
this.length = length;
// 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;
},
getParameterAt: function(offset) {
getTimeAt: function(offset) {
// Make sure we're not beyond the requested offset already. Search the
// start position backwards from where to then process the loop below.
var i, j = this.index;
@ -135,8 +135,8 @@ var PathIterator = Base.extend({
},
drawPart: function(ctx, from, to) {
from = this.getParameterAt(from);
to = this.getParameterAt(to);
from = this.getTimeAt(from);
to = this.getTimeAt(to);
for (var i = from.index; i <= to.index; i++) {
var curve = Curve.getPart(this.curves[i],
i == from.index ? from.value : 0,
@ -149,7 +149,7 @@ var PathIterator = Base.extend({
}, Base.each(Curve._evaluateMethods,
function(name) {
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);
};
}, {})

View file

@ -12,31 +12,7 @@
QUnit.module('Curve');
test('Curve#getParameterOf()', 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() {
test('Curve#getPointAtTime()', function() {
var curve = new Path.Circle({
center: [100, 100],
radius: 100
@ -52,15 +28,18 @@ test('Curve#getPointAt()', function() {
for (var i = 0; i < points.length; 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],
'curve.getPointAt(' + entry[0] + ', true);');
'Legacy: curve.getPointAt(' + entry[0] + ', true);');
}
equals(curve.getPointAt(curve.length + 1), null,
'Should return null when offset is out of range.');
});
test('Curve#getTangentAt()', function() {
test('Curve#getTangentAtTime()', function() {
var curve = new Path.Circle({
center: [100, 100],
radius: 100
@ -76,14 +55,19 @@ test('Curve#getTangentAt()', function() {
for (var i = 0; i < tangents.length; 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(),
'curve.getTangentAt(' + entry[0] + ', true);');
'Legacy: curve.getTangentAt(' + entry[0] + ', true);');
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({
center: [100, 100],
radius: 100
@ -99,14 +83,19 @@ test('Curve#getNormalAt()', function() {
for (var i = 0; i < normals.length; 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(),
'curve.getNormalAt(' + entry[0] + ', true);');
'Legacy: curve.getNormalAt(' + entry[0] + ', true);');
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({
center: [100, 100],
radius: 100
@ -122,12 +111,15 @@ test('Curve#getCurvatureAt()', function() {
for (var i = 0; i < curvatures.length; 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],
'curve.getCurvatureAt(' + entry[0] + ', true);');
'Legacy: curve.getCurvatureAt(' + entry[0] + ', true);');
}
});
test('Curve#getCurvatureAt()', function() {
test('Curve#getCurvatureAtTime()', function() {
var curve = new Path.Line({
from: [100, 100],
to: [200, 200],
@ -143,12 +135,15 @@ test('Curve#getCurvatureAt()', function() {
for (var i = 0; i < curvatures.length; 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],
'curve.getCurvatureAt(' + entry[0] + ', true);');
'Legacy: curve.getCurvatureAt(' + entry[0] + ', true);');
}
});
test('Curve#getParameterAt()', function() {
test('Curve#getTimeAt()', function() {
var curve = new Path([
[[0, 0], [0, 0], [100, 0]],
[[200, 200]],
@ -157,14 +152,16 @@ test('Curve#getParameterAt()', function() {
for (var f = 0; f <= 1; f += 0.1) {
var o1 = curve.length * f;
var o2 = -curve.length * (1 - f);
var t1 = curve.getParameterAt(o1);
var t2 = curve.getParameterAt(o2);
equals(t1, t2, 'Curve parameter at offset ' + o1
+ ' should be the same value as at offset' + o2,
var message = 'Curve-time parameter at offset ' + o1
+ ' should be the same value as at offset' + o2;
equals(curve.getTimeAt(o1), curve.getTimeAt(o2), message,
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.');
});
@ -176,7 +173,6 @@ test('Curve#getLocationAt()', function() {
equals(curve.getLocationAt(curve.length + 1), null,
'Should return null when offset is out of range.');
// 'Should return null when point is not on the curve.');
});
test('Curve#isStraight()', function() {
@ -223,3 +219,31 @@ test('Curve#isLinear()', function() {
return new Curve([100, 100], null, null, [200, 200]).isLinear();
}, 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();
for (var i = 0; i < curves.length; i++) {
testPoint(path, curves[i].getPointAt(0, true), true);
testPoint(path, curves[i].getPointAt(0.5, true), true);
testPoint(path, curves[i].getPointAtTime(0), true);
testPoint(path, curves[i].getPointAtTime(0.5), true);
}
});
@ -181,8 +181,8 @@ test('Path#contains() (Rotated Rectangle Contours)', function() {
path.rotate(45);
for (var i = 0; i < curves.length; i++) {
testPoint(path, curves[i].getPointAt(0, true), true);
testPoint(path, curves[i].getPointAt(0.5, true), true);
testPoint(path, curves[i].getPointAtTime(0), true);
testPoint(path, curves[i].getPointAtTime(0.5), true);
}
});

View file

@ -20,7 +20,7 @@ function testIntersection(intersections, results) {
var name = 'intersections[' + i + ']';
equals(inter.point, new Point(values.point), name + '.point');
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()');
}
}
@ -31,8 +31,8 @@ test('#565', function() {
var path1 = new Path([curve1.segment1, curve1.segment2]);
var path2 = new Path([curve2.segment1, curve2.segment2]);
testIntersection(curve1.getIntersections(curve2), [
{ point: { x: 354.13635, y: 220.81369 }, index: 0, parameter: 0.46725, crossing: true },
{ point: { x: 390.24772, y: 224.27351 }, index: 0, parameter: 0.71605, 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, time: 0.71605, crossing: true }
]);
// 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 path2 = new Path([curve2.segment1, curve2.segment2]);
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 path2 = new Path([curve2.segment1, curve2.segment2]);
testIntersection(curve1.getIntersections(curve2), [
{ point: { x: 547.96568, y: 396.66339 }, index: 0, parameter: 0.07024, crossing: true },
{ point: { x: 504.79973, y: 383.37886 }, index: 0, parameter: 0.48077, 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, 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));
@ -60,7 +60,7 @@ test('#568', function() {
var path1 = new Path([curve1.segment1, curve1.segment2]);
var path2 = new Path([curve2.segment1, curve2.segment2]);
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 ints = 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 path2 = new Path([curve2.segment1, curve2.segment2]);
testIntersection(curve1.getIntersections(curve2), [
{ point: { x: 352.39945, y: 330.44135 }, index: 0, parameter: 0.41159, crossing: true },
{ point: { x: 420.12359, y: 275.83519 }, index: 0, parameter: 1, crossing: false }
{ point: { x: 352.39945, y: 330.44135 }, index: 0, time: 0.41159, crossing: true },
{ 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 path2 = new Path.Rectangle(new Point(110, 110), [100, 100]);
testIntersection(path1.getIntersections(path2), [
{ point: { x: 190, y: 110 }, index: 2, parameter: 0, crossing: true },
{ point: { x: 110, y: 190 }, index: 3, parameter: 0, crossing: true }
{ point: { x: 190, y: 110 }, index: 2, time: 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;
equals(length, 172.10112809179614, 'path.length');
var param = path.curves[0].getParameterAt(length / 4);
equals(param, 0.2255849553116685, 'path.curves[0].getParameterAt(length / 4)');
var t = path.curves[0].getTimeAt(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();
path.moveTo(100, 100);
path.lineTo(500, 500);
var curve = path.curves[0];
var length = curve.length;
var t = curve.getParameterAt(length / 3);
var t = curve.getTimeAt(length / 3);
equals(t, 0.3869631475722452);
});