mirror of
https://github.com/scratchfoundation/paper.js.git
synced 2025-01-07 13:22:07 -05:00
Start implementing new smooth() functions that merge all approaches.
Work in progress, needs more work on range handling for 'continous', and docs.
This commit is contained in:
parent
2539527864
commit
bf4eb47fae
4 changed files with 253 additions and 184 deletions
|
@ -135,12 +135,6 @@ var CompoundPath = PathItem.extend(/** @lends CompoundPath# */{
|
||||||
children[i].reverse();
|
children[i].reverse();
|
||||||
},
|
},
|
||||||
|
|
||||||
smooth: function() {
|
|
||||||
var children = this._children;
|
|
||||||
for (var i = 0, l = children.length; i < l; i++)
|
|
||||||
children[i].smooth();
|
|
||||||
},
|
|
||||||
|
|
||||||
// DOCS: reduce()
|
// DOCS: reduce()
|
||||||
// TEST: reduce()
|
// TEST: reduce()
|
||||||
reduce: function reduce(options) {
|
reduce: function reduce(options) {
|
||||||
|
@ -160,6 +154,13 @@ var CompoundPath = PathItem.extend(/** @lends CompoundPath# */{
|
||||||
return reduce.base.call(this);
|
return reduce.base.call(this);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// TODO: Docs
|
||||||
|
smooth: function(options) {
|
||||||
|
var children = this._children;
|
||||||
|
for (var i = 0, l = children.length; i < l; i++)
|
||||||
|
children[i].smooth(options);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies whether the compound path is oriented clock-wise.
|
* Specifies whether the compound path is oriented clock-wise.
|
||||||
*
|
*
|
||||||
|
|
300
src/path/Path.js
300
src/path/Path.js
|
@ -2060,9 +2060,183 @@ var Path = PathItem.extend(/** @lends Path# */{
|
||||||
*/
|
*/
|
||||||
getNearestPoint: function(/* point */) {
|
getNearestPoint: function(/* point */) {
|
||||||
return this.getNearestLocation.apply(this, arguments).getPoint();
|
return this.getNearestLocation.apply(this, arguments).getPoint();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: continuous:
|
||||||
|
* Smooths the path by adjusting its curve handles so that the first and
|
||||||
|
* second derivatives of all involved curves are continuous across their
|
||||||
|
* boundaries.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Smooths the path without changing the amount of segments in the path
|
||||||
|
* or moving their locations, by only smoothing and adjusting the angle and
|
||||||
|
* length of their handles.
|
||||||
|
* This works for open paths as well as closed paths.
|
||||||
|
*
|
||||||
|
* @param {Object} [options] TODO
|
||||||
|
* TODO: controls the amount of smoothing as a factor by which to scale each
|
||||||
|
* handle.
|
||||||
|
*
|
||||||
|
* @see Segment#smooth(options)
|
||||||
|
*
|
||||||
|
* @example {@paperscript}
|
||||||
|
* // Smoothing a closed shape:
|
||||||
|
*
|
||||||
|
* // Create a rectangular path with its top-left point at
|
||||||
|
* // {x: 30, y: 25} and a size of {width: 50, height: 50}:
|
||||||
|
* var path = new Path.Rectangle(new Point(30, 25), new Size(50, 50));
|
||||||
|
* path.strokeColor = 'black';
|
||||||
|
*
|
||||||
|
* // Select the path, so we can see its handles:
|
||||||
|
* path.fullySelected = true;
|
||||||
|
*
|
||||||
|
* // Create a copy of the path and move it 100pt to the right:
|
||||||
|
* var copy = path.clone();
|
||||||
|
* copy.position.x += 100;
|
||||||
|
*
|
||||||
|
* // Smooth the segments of the copy:
|
||||||
|
* copy.smooth();
|
||||||
|
*
|
||||||
|
* @example {@paperscript height=220}
|
||||||
|
* var path = new Path();
|
||||||
|
* path.strokeColor = 'black';
|
||||||
|
*
|
||||||
|
* path.add(new Point(30, 50));
|
||||||
|
*
|
||||||
|
* var y = 5;
|
||||||
|
* var x = 3;
|
||||||
|
*
|
||||||
|
* for (var i = 0; i < 28; i++) {
|
||||||
|
* y *= -1.1;
|
||||||
|
* x *= 1.1;
|
||||||
|
* path.lineBy(x, y);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // Create a copy of the path and move it 100pt down:
|
||||||
|
* var copy = path.clone();
|
||||||
|
* copy.position.y += 120;
|
||||||
|
*
|
||||||
|
* // Set its stroke color to red:
|
||||||
|
* copy.strokeColor = 'red';
|
||||||
|
*
|
||||||
|
* // Smooth the segments of the copy:
|
||||||
|
* copy.smooth();
|
||||||
|
*/
|
||||||
|
smooth: function(options) {
|
||||||
|
function getIndex(value, _default) {
|
||||||
|
return value == null
|
||||||
|
? _default
|
||||||
|
: typeof value === 'number'
|
||||||
|
? value
|
||||||
|
: value.getIndex
|
||||||
|
? value.getIndex()
|
||||||
|
: _default;
|
||||||
|
}
|
||||||
|
|
||||||
|
var opts = options || {},
|
||||||
|
type = opts.type,
|
||||||
|
segments = this._segments,
|
||||||
|
length = segments.length,
|
||||||
|
from = getIndex(opts.from, 0),
|
||||||
|
to = getIndex(opts.to, length - 1);
|
||||||
|
|
||||||
|
if (!type || type === 'continuous') {
|
||||||
|
// Continuous smoothing approach based on work by Lubos Brieda,
|
||||||
|
// Particle In Cell Consulting LLC, but further simplified by
|
||||||
|
// addressing handle symmetry across segments, and the possibility
|
||||||
|
// to process x and y coordinates simultaneously. Also added
|
||||||
|
// handling of closed paths.
|
||||||
|
// https://www.particleincell.com/2012/bezier-splines/
|
||||||
|
var closed = this._closed,
|
||||||
|
n = length - 1,
|
||||||
|
// Add overlapping ends for closed paths.
|
||||||
|
overlap = 0;
|
||||||
|
if (length <= 2)
|
||||||
|
return;
|
||||||
|
if (closed) {
|
||||||
|
// Overlap by up to 4 points since a current segment is affected
|
||||||
|
// by 4 neighbors.
|
||||||
|
overlap = Math.min(length, 4);
|
||||||
|
n += Math.min(length, overlap) * 2;
|
||||||
|
}
|
||||||
|
var knots = [];
|
||||||
|
for (var i = 0; i < length; i++)
|
||||||
|
knots[i + overlap] = segments[i]._point;
|
||||||
|
if (closed) {
|
||||||
|
// Add the last points again at the beginning, and the first
|
||||||
|
// ones at the end.
|
||||||
|
for (var i = 0; i < overlap; i++) {
|
||||||
|
knots[i] = knots[i + length];
|
||||||
|
knots[i + length + overlap] = knots[i + overlap];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Right-hand side vectors, with left most segment added
|
||||||
|
var a = [0],
|
||||||
|
b = [2],
|
||||||
|
c = [1],
|
||||||
|
rx = [knots[0]._x + 2 * knots[1]._x],
|
||||||
|
ry = [knots[0]._y + 2 * knots[1]._y],
|
||||||
|
n_1 = n - 1;
|
||||||
|
|
||||||
|
// Internal segments
|
||||||
|
for (var i = 1; i < n_1; i++) {
|
||||||
|
a[i] = 1;
|
||||||
|
b[i] = 4;
|
||||||
|
c[i] = 1;
|
||||||
|
rx[i] = 4 * knots[i]._x + 2 * knots[i + 1]._x;
|
||||||
|
ry[i] = 4 * knots[i]._y + 2 * knots[i + 1]._y;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Right segment
|
||||||
|
a[n_1] = 2;
|
||||||
|
b[n_1] = 7;
|
||||||
|
c[n_1] = 0;
|
||||||
|
rx[n_1] = 8 * knots[n_1]._x + knots[n]._x;
|
||||||
|
ry[n_1] = 8 * knots[n_1]._y + knots[n]._y;
|
||||||
|
|
||||||
|
// Solve Ax = b with the Thomas algorithm (from Wikipedia)
|
||||||
|
for (var i = 1, j = 0; i < n; i++, j++) {
|
||||||
|
var m = a[i] / b[j];
|
||||||
|
b[i] = b[i] - m * c[j];
|
||||||
|
rx[i] = rx[i] - m * rx[j];
|
||||||
|
ry[i] = ry[i] - m * ry[j];
|
||||||
|
}
|
||||||
|
|
||||||
|
var px = [],
|
||||||
|
py = [];
|
||||||
|
|
||||||
|
px[n_1] = rx[n_1] / b[n_1];
|
||||||
|
py[n_1] = ry[n_1] / b[n_1];
|
||||||
|
for (var i = n - 2; i >= 0; i--) {
|
||||||
|
px[i] = (rx[i] - c[i] * px[i + 1]) / b[i];
|
||||||
|
py[i] = (ry[i] - c[i] * py[i + 1]) / b[i];
|
||||||
|
}
|
||||||
|
px[n] = (3 * knots[n]._x - px[n_1]) / 2;
|
||||||
|
py[n] = (3 * knots[n]._y - py[n_1]) / 2;
|
||||||
|
|
||||||
|
// Now update the segments
|
||||||
|
n -= overlap;
|
||||||
|
for (var i = overlap; i <= n; i++) {
|
||||||
|
var segment = segments[i - overlap],
|
||||||
|
pt = segment._point,
|
||||||
|
hx = px[i] - pt._x,
|
||||||
|
hy = py[i] - pt._y;
|
||||||
|
if (closed || i < n)
|
||||||
|
segment.setHandleOut(hx, hy);
|
||||||
|
if (closed || i > 0)
|
||||||
|
segment.setHandleIn(-hx, -hy);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// AlL other smoothing methods are handled directly on the segments:
|
||||||
|
for (var i = from; i <= to; i++)
|
||||||
|
segments[i].smooth(opts);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
new function() { // Scope for drawing
|
new function() { // Scope for drawing
|
||||||
|
|
||||||
// Note that in the code below we're often accessing _x and _y on point
|
// Note that in the code below we're often accessing _x and _y on point
|
||||||
// objects that were read from segments. This is because the SegmentPoint
|
// objects that were read from segments. This is because the SegmentPoint
|
||||||
// class overrides the plain x / y properties with getter / setters and
|
// class overrides the plain x / y properties with getter / setters and
|
||||||
|
@ -2262,132 +2436,6 @@ new function() { // Scope for drawing
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
new function() { // Path Smoothing
|
|
||||||
/**
|
|
||||||
* Solves a tri-diagonal system for one of coordinates (x or y) of first
|
|
||||||
* bezier control points.
|
|
||||||
*
|
|
||||||
* @param rhs right hand side vector
|
|
||||||
* @return Solution vector
|
|
||||||
*/
|
|
||||||
function getFirstControlPoints(rhs) {
|
|
||||||
var n = rhs.length,
|
|
||||||
x = [], // Solution vector.
|
|
||||||
tmp = [], // Temporary workspace.
|
|
||||||
b = 2;
|
|
||||||
x[0] = rhs[0] / b;
|
|
||||||
// Decomposition and forward substitution.
|
|
||||||
for (var i = 1; i < n; i++) {
|
|
||||||
tmp[i] = 1 / b;
|
|
||||||
b = (i < n - 1 ? 4 : 2) - tmp[i];
|
|
||||||
x[i] = (rhs[i] - x[i - 1]) / b;
|
|
||||||
}
|
|
||||||
// Back-substitution.
|
|
||||||
for (var i = 1; i < n; i++) {
|
|
||||||
x[n - i - 1] -= tmp[n - i] * x[n - i];
|
|
||||||
}
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
// NOTE: Documentation for smooth() is in PathItem
|
|
||||||
smooth: function() {
|
|
||||||
// This code is based on the work by Oleg V. Polikarpotchkin,
|
|
||||||
// http://ov-p.spaces.live.com/blog/cns!39D56F0C7A08D703!147.entry
|
|
||||||
// It was extended to support closed paths by averaging overlapping
|
|
||||||
// beginnings and ends. The result of this approach is very close to
|
|
||||||
// Polikarpotchkin's closed curve solution, but reuses the same
|
|
||||||
// algorithm as for open paths, and is probably executing faster as
|
|
||||||
// well, so it is preferred.
|
|
||||||
var segments = this._segments,
|
|
||||||
size = segments.length,
|
|
||||||
closed = this._closed,
|
|
||||||
n = size,
|
|
||||||
// Add overlapping ends for averaging handles in closed paths
|
|
||||||
overlap = 0;
|
|
||||||
if (size <= 2)
|
|
||||||
return;
|
|
||||||
if (closed) {
|
|
||||||
// Overlap up to 4 points since averaging beziers affect the 4
|
|
||||||
// neighboring points
|
|
||||||
overlap = Math.min(size, 4);
|
|
||||||
n += Math.min(size, overlap) * 2;
|
|
||||||
}
|
|
||||||
var knots = [];
|
|
||||||
for (var i = 0; i < size; i++)
|
|
||||||
knots[i + overlap] = segments[i]._point;
|
|
||||||
if (closed) {
|
|
||||||
// If we're averaging, add the 4 last points again at the
|
|
||||||
// beginning, and the 4 first ones at the end.
|
|
||||||
for (var i = 0; i < overlap; i++) {
|
|
||||||
knots[i] = segments[i + size - overlap]._point;
|
|
||||||
knots[i + size + overlap] = segments[i]._point;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
n--;
|
|
||||||
}
|
|
||||||
// Calculate first Bezier control points
|
|
||||||
// Right hand side vector
|
|
||||||
var rhs = [];
|
|
||||||
|
|
||||||
// Set right hand side X values
|
|
||||||
for (var i = 1; i < n - 1; i++)
|
|
||||||
rhs[i] = 4 * knots[i]._x + 2 * knots[i + 1]._x;
|
|
||||||
rhs[0] = knots[0]._x + 2 * knots[1]._x;
|
|
||||||
rhs[n - 1] = 3 * knots[n - 1]._x;
|
|
||||||
// Get first control points X-values
|
|
||||||
var x = getFirstControlPoints(rhs);
|
|
||||||
|
|
||||||
// Set right hand side Y values
|
|
||||||
for (var i = 1; i < n - 1; i++)
|
|
||||||
rhs[i] = 4 * knots[i]._y + 2 * knots[i + 1]._y;
|
|
||||||
rhs[0] = knots[0]._y + 2 * knots[1]._y;
|
|
||||||
rhs[n - 1] = 3 * knots[n - 1]._y;
|
|
||||||
// Get first control points Y-values
|
|
||||||
var y = getFirstControlPoints(rhs);
|
|
||||||
|
|
||||||
if (closed) {
|
|
||||||
// Do the actual averaging simply by linearly fading between the
|
|
||||||
// overlapping values.
|
|
||||||
for (var i = 0, j = size; i < overlap; i++, j++) {
|
|
||||||
var f1 = i / overlap,
|
|
||||||
f2 = 1 - f1,
|
|
||||||
ie = i + overlap,
|
|
||||||
je = j + overlap;
|
|
||||||
// Beginning
|
|
||||||
x[j] = x[i] * f1 + x[j] * f2;
|
|
||||||
y[j] = y[i] * f1 + y[j] * f2;
|
|
||||||
// End
|
|
||||||
x[je] = x[ie] * f2 + x[je] * f1;
|
|
||||||
y[je] = y[ie] * f2 + y[je] * f1;
|
|
||||||
}
|
|
||||||
n--;
|
|
||||||
}
|
|
||||||
var handleIn = null;
|
|
||||||
// Now set the calculated handles
|
|
||||||
for (var i = overlap; i <= n - overlap; i++) {
|
|
||||||
var segment = segments[i - overlap];
|
|
||||||
if (handleIn)
|
|
||||||
segment.setHandleIn(handleIn.subtract(segment._point));
|
|
||||||
if (i < n) {
|
|
||||||
segment.setHandleOut(
|
|
||||||
new Point(x[i], y[i]).subtract(segment._point));
|
|
||||||
handleIn = i < n - 1
|
|
||||||
? new Point(
|
|
||||||
2 * knots[i + 1]._x - x[i + 1],
|
|
||||||
2 * knots[i + 1]._y - y[i + 1])
|
|
||||||
: new Point(
|
|
||||||
(knots[n]._x + x[n - 1]) / 2,
|
|
||||||
(knots[n]._y + y[n - 1]) / 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (closed && handleIn) {
|
|
||||||
var segment = this._segments[0];
|
|
||||||
segment.setHandleIn(handleIn.subtract(segment._point));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
new function() { // PostScript-style drawing commands
|
new function() { // PostScript-style drawing commands
|
||||||
/**
|
/**
|
||||||
* Helper method that returns the current segment and checks if a moveTo()
|
* Helper method that returns the current segment and checks if a moveTo()
|
||||||
|
|
|
@ -314,58 +314,6 @@ var PathItem = Item.extend(/** @lends PathItem# */{
|
||||||
/*#*/ } // !__options.nativeContains && __options.booleanOperations
|
/*#*/ } // !__options.nativeContains && __options.booleanOperations
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Smooth bezier curves without changing the amount of segments or their
|
|
||||||
* points, by only smoothing and adjusting their handle points, for both
|
|
||||||
* open ended and closed paths.
|
|
||||||
*
|
|
||||||
* @name PathItem#smooth
|
|
||||||
* @function
|
|
||||||
*
|
|
||||||
* @example {@paperscript}
|
|
||||||
* // Smoothing a closed shape:
|
|
||||||
*
|
|
||||||
* // Create a rectangular path with its top-left point at
|
|
||||||
* // {x: 30, y: 25} and a size of {width: 50, height: 50}:
|
|
||||||
* var path = new Path.Rectangle(new Point(30, 25), new Size(50, 50));
|
|
||||||
* path.strokeColor = 'black';
|
|
||||||
*
|
|
||||||
* // Select the path, so we can see its handles:
|
|
||||||
* path.fullySelected = true;
|
|
||||||
*
|
|
||||||
* // Create a copy of the path and move it 100pt to the right:
|
|
||||||
* var copy = path.clone();
|
|
||||||
* copy.position.x += 100;
|
|
||||||
*
|
|
||||||
* // Smooth the segments of the copy:
|
|
||||||
* copy.smooth();
|
|
||||||
*
|
|
||||||
* @example {@paperscript height=220}
|
|
||||||
* var path = new Path();
|
|
||||||
* path.strokeColor = 'black';
|
|
||||||
*
|
|
||||||
* path.add(new Point(30, 50));
|
|
||||||
*
|
|
||||||
* var y = 5;
|
|
||||||
* var x = 3;
|
|
||||||
*
|
|
||||||
* for (var i = 0; i < 28; i++) {
|
|
||||||
* y *= -1.1;
|
|
||||||
* x *= 1.1;
|
|
||||||
* path.lineBy(x, y);
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* // Create a copy of the path and move it 100pt down:
|
|
||||||
* var copy = path.clone();
|
|
||||||
* copy.position.y += 120;
|
|
||||||
*
|
|
||||||
* // Set its stroke color to red:
|
|
||||||
* copy.strokeColor = 'red';
|
|
||||||
*
|
|
||||||
* // Smooth the segments of the copy:
|
|
||||||
* copy.smooth();
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@grouptitle Postscript Style Drawing Commands}
|
* {@grouptitle Postscript Style Drawing Commands}
|
||||||
*
|
*
|
||||||
|
|
|
@ -378,6 +378,78 @@ var Segment = Base.extend(/** @lends Segment# */{
|
||||||
|| this._path._closed && segments[0]) || null;
|
|| this._path._closed && segments[0]) || null;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Smooths the bezier curves that pass through this segment without moving
|
||||||
|
* its point, by taking into its distance to the neighboring segments and
|
||||||
|
* changing the direction and length of the segment's handles accordingly.
|
||||||
|
*
|
||||||
|
* @param {Object} [options] TODO
|
||||||
|
* TODO: controls the amount of smoothing as a factor by which to scale each
|
||||||
|
* handle.
|
||||||
|
*
|
||||||
|
* @see PathItem#smooth(options)
|
||||||
|
*/
|
||||||
|
smooth: function(options) {
|
||||||
|
var opts = options || {},
|
||||||
|
type = opts.type,
|
||||||
|
factor = opts.factor,
|
||||||
|
prev = this.getPrevious(),
|
||||||
|
next = this.getNext(),
|
||||||
|
// Some precalculations valid for both 'catmull-rom' and 'geometric'
|
||||||
|
p0 = (prev || this)._point,
|
||||||
|
p1 = this._point,
|
||||||
|
p2 = (next || this)._point,
|
||||||
|
d1 = p0.getDistance(p1),
|
||||||
|
d2 = p1.getDistance(p2);
|
||||||
|
if (!type || type === 'catmull-rom') {
|
||||||
|
// Implementation of by Catmull-Rom splines with factor parameter
|
||||||
|
// based on work by @nicholaswmin:
|
||||||
|
// https://github.com/nicholaswmin/VectorTests
|
||||||
|
// Using these factor values produces different types of splines:
|
||||||
|
// 0.0: the standard, uniform Catmull-Rom spline
|
||||||
|
// 0.5: the centripetal Catmull-Rom spline, guaranteeing no self-
|
||||||
|
// intersections
|
||||||
|
// 1.0: the chordal Catmull-Rom spline.
|
||||||
|
var alpha = factor === undefined ? 0.5 : factor,
|
||||||
|
d1_a = Math.pow(d1, alpha),
|
||||||
|
d1_2a = d1_a * d1_a,
|
||||||
|
d2_a = Math.pow(d2, alpha),
|
||||||
|
d2_2a = d2_a * d2_a;
|
||||||
|
if (prev) {
|
||||||
|
var A = 2 * d2_2a + 3 * d2_a * d1_a + d1_2a,
|
||||||
|
N = 3 * d2_a * (d2_a + d1_a);
|
||||||
|
this.setHandleIn(N !== 0
|
||||||
|
? new Point(
|
||||||
|
(d2_2a * p0._x + A * p1._x - d1_2a * p2._x) / N - p1._x,
|
||||||
|
(d2_2a * p0._y + A * p1._y - d1_2a * p2._y) / N - p1._y)
|
||||||
|
: new Point());
|
||||||
|
}
|
||||||
|
if (next) {
|
||||||
|
var A = 2 * d1_2a + 3 * d1_a * d2_a + d2_2a,
|
||||||
|
N = 3 * d1_a * (d1_a + d2_a);
|
||||||
|
this.setHandleOut(N !== 0
|
||||||
|
? new Point(
|
||||||
|
(d1_2a * p2._x + A * p1._x - d2_2a * p0._x) / N - p1._x,
|
||||||
|
(d1_2a * p2._y + A * p1._y - d2_2a * p0._y) / N - p1._y)
|
||||||
|
: new Point());
|
||||||
|
}
|
||||||
|
} else if (type === 'geometric') {
|
||||||
|
// Geometric smoothing approach based on:
|
||||||
|
// http://www.antigrain.com/research/bezier_interpolation/
|
||||||
|
// http://scaledinnovation.com/analytics/splines/aboutSplines.html
|
||||||
|
// http://bseth99.github.io/projects/animate/2-bezier-curves.html
|
||||||
|
if (prev && next) {
|
||||||
|
var vector = p0.subtract(p2),
|
||||||
|
t = factor === undefined ? 0.4 : factor,
|
||||||
|
k = t * d1 / (d1 + d2);
|
||||||
|
this.setHandleIn(vector.multiply(k));
|
||||||
|
this.setHandleOut(vector.multiply(k - t));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error('Smoothing method \'' + type + '\' not supported.');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The previous segment in the {@link Path#segments} array that the
|
* The previous segment in the {@link Path#segments} array that the
|
||||||
* segment belongs to. If the segments belongs to a closed path, the last
|
* segment belongs to. If the segments belongs to a closed path, the last
|
||||||
|
|
Loading…
Reference in a new issue