From 340a1e2a5f1f9523672c82cad8445438211e7178 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrg=20Lehni?= Date: Tue, 2 Feb 2016 11:59:53 +0100 Subject: [PATCH] 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 --- examples/Scripts/BooleanOperations.html | 10 +- .../Scripts/CurveTimeParameterization.html | 8 +- src/export.js | 2 +- src/path/Curve.js | 362 +++++++++++------- src/path/CurveLocation.js | 106 ++--- src/path/Path.js | 156 +++----- src/path/PathItem.Boolean.js | 28 +- src/path/PathIterator.js | 10 +- test/tests/Curve.js | 110 +++--- test/tests/PathItem_Contains.js | 8 +- test/tests/Path_Intersections.js | 24 +- test/tests/Path_Length.js | 8 +- 12 files changed, 447 insertions(+), 385 deletions(-) diff --git a/examples/Scripts/BooleanOperations.html b/examples/Scripts/BooleanOperations.html index 0455c220..3f083f33 100644 --- a/examples/Scripts/BooleanOperations.html +++ b/examples/Scripts/BooleanOperations.html @@ -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'; diff --git a/examples/Scripts/CurveTimeParameterization.html b/examples/Scripts/CurveTimeParameterization.html index de31401a..9aac111e 100644 --- a/examples/Scripts/CurveTimeParameterization.html +++ b/examples/Scripts/CurveTimeParameterization.html @@ -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) diff --git a/src/export.js b/src/export.js index d6ab58db..60a61a5c 100644 --- a/src/export.js +++ b/src/export.js @@ -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 }))(); diff --git a/src/path/Curve.js b/src/path/Curve.js index 80378ae2..6488ba27 100644 --- a/src/path/Curve.js +++ b/src/path/Curve.js @@ -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 diff --git a/src/path/CurveLocation.js b/src/path/CurveLocation.js index ec18d07a..ec18a48e 100644 --- a/src/path/CurveLocation.js +++ b/src/path/CurveLocation.js @@ -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) { diff --git a/src/path/Path.js b/src/path/Path.js index e806bfa7..1af61dd1 100644 --- a/src/path/Path.js +++ b/src/path/Path.js @@ -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)); }, diff --git a/src/path/PathItem.Boolean.js b/src/path/PathItem.Boolean.js index 9bfd39fc..68b40387 100644 --- a/src/path/PathItem.Boolean.js +++ b/src/path/PathItem.Boolean.js @@ -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; diff --git a/src/path/PathIterator.js b/src/path/PathIterator.js index d5fe92d3..a3424e8c 100644 --- a/src/path/PathIterator.js +++ b/src/path/PathIterator.js @@ -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); }; }, {}) diff --git a/test/tests/Curve.js b/test/tests/Curve.js index 48b16e5b..c03ccf35 100644 --- a/test/tests/Curve.js +++ b/test/tests/Curve.js @@ -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;'); + } +}); diff --git a/test/tests/PathItem_Contains.js b/test/tests/PathItem_Contains.js index cc9e9de6..f7788331 100644 --- a/test/tests/PathItem_Contains.js +++ b/test/tests/PathItem_Contains.js @@ -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); } }); diff --git a/test/tests/Path_Intersections.js b/test/tests/Path_Intersections.js index 2d3b7a0d..07b983cb 100644 --- a/test/tests/Path_Intersections.js +++ b/test/tests/Path_Intersections.js @@ -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 } ]); }); diff --git a/test/tests/Path_Length.js b/test/tests/Path_Length.js index fb77b4f9..a79c8bff 100644 --- a/test/tests/Path_Length.js +++ b/test/tests/Path_Length.js @@ -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); });