Prebuilt module for commit 3965dd9b77

This commit is contained in:
Paper.js Bot 2016-02-09 13:16:35 +00:00
parent df790ce2e9
commit 329d629583
9 changed files with 1501 additions and 1224 deletions

View file

@ -9,7 +9,7 @@
*
* All rights reserved.
*
* Date: Tue Feb 9 10:07:28 2016 +0100
* Date: Tue Feb 9 14:13:30 2016 +0100
*
***
*
@ -7764,39 +7764,6 @@ var Path = PathItem.extend({
this.setSelected(true);
},
flatten: function(maxDistance) {
var iterator = new PathIterator(this, 64, 0.1),
pos = 0,
step = iterator.length / Math.ceil(iterator.length / maxDistance),
end = iterator.length + (this._closed ? -step : step) / 2;
var segments = [];
while (pos <= end) {
segments.push(new Segment(iterator.getPointAt(pos)));
pos += step;
}
this.setSegments(segments);
},
reduce: function(options) {
var curves = this.getCurves(),
simplify = options && options.simplify,
tolerance = simplify ? 2e-7 : 0;
for (var i = curves.length - 1; i >= 0; i--) {
var curve = curves[i];
if (!curve.hasHandles() && (curve.getLength() < tolerance
|| simplify && curve.isCollinear(curve.getNext())))
curve.remove();
}
return this;
},
simplify: function(tolerance) {
if (this._segments.length > 2) {
var fitter = new PathFitter(this, tolerance || 2.5);
this.setSegments(fitter.fit());
}
},
splitAt: function(location) {
var index = location && location.index,
time = location && location.time,
@ -7836,21 +7803,6 @@ var Path = PathItem.extend({
return location ? this.splitAt(location) : null;
},
reverse: function() {
this._segments.reverse();
for (var i = 0, l = this._segments.length; i < l; i++) {
var segment = this._segments[i];
var handleIn = segment._handleIn;
segment._handleIn = segment._handleOut;
segment._handleOut = handleIn;
segment._index = i;
}
this._curves = null;
if (this._clockwise !== undefined)
this._clockwise = !this._clockwise;
this._changed(9);
},
join: function(path) {
if (path) {
var segments = path._segments,
@ -7890,6 +7842,159 @@ var Path = PathItem.extend({
return this;
},
reverse: function() {
this._segments.reverse();
for (var i = 0, l = this._segments.length; i < l; i++) {
var segment = this._segments[i];
var handleIn = segment._handleIn;
segment._handleIn = segment._handleOut;
segment._handleOut = handleIn;
segment._index = i;
}
this._curves = null;
if (this._clockwise !== undefined)
this._clockwise = !this._clockwise;
this._changed(9);
},
flatten: function(maxDistance) {
var iterator = new PathIterator(this, 64, 0.1),
pos = 0,
step = iterator.length / Math.ceil(iterator.length / maxDistance),
end = iterator.length + (this._closed ? -step : step) / 2;
var segments = [];
while (pos <= end) {
segments.push(new Segment(iterator.getPointAt(pos)));
pos += step;
}
this.setSegments(segments);
},
reduce: function(options) {
var curves = this.getCurves(),
simplify = options && options.simplify,
tolerance = simplify ? 2e-7 : 0;
for (var i = curves.length - 1; i >= 0; i--) {
var curve = curves[i];
if (!curve.hasHandles() && (curve.getLength() < tolerance
|| simplify && curve.isCollinear(curve.getNext())))
curve.remove();
}
return this;
},
simplify: function(tolerance) {
var segments = new PathFitter(this).fit(tolerance || 2.5);
if (segments)
this.setSegments(segments);
return !!segments;
},
smooth: function(options) {
function getIndex(value, _default) {
var index = value && value.index;
if (index != null) {
var path = value.path;
if (path && path !== that)
throw new Error(value._class + ' ' + index + ' of ' + path
+ ' is not part of ' + that);
if (_default && value instanceof Curve)
index++;
} else {
index = typeof value === 'number' ? value : _default;
}
return Math.min(index < 0 && closed
? index % length
: index < 0 ? index + length : index, length - 1);
}
var that = this,
opts = options || {},
type = opts.type || 'asymmetric',
segments = this._segments,
length = segments.length,
closed = this._closed,
loop = closed && opts.from === undefined && opts.to === undefined,
from = getIndex(opts.from, 0),
to = getIndex(opts.to, length - 1);
if (from > to) {
if (closed) {
from -= length;
} else {
var tmp = from;
from = to;
to = tmp;
}
}
if (/^(?:asymmetric|continuous)$/.test(type)) {
var asymmetric = type === 'asymmetric',
min = Math.min,
amount = to - from + 1,
n = amount - 1,
padding = loop ? min(amount, 4) : 1,
paddingLeft = padding,
paddingRight = padding,
knots = [];
if (!closed) {
paddingLeft = min(1, from);
paddingRight = min(1, length - to - 1);
}
n += paddingLeft + paddingRight;
if (n <= 1)
return;
for (var i = 0, j = from - paddingLeft; i <= n; i++, j++) {
knots[i] = segments[(j < 0 ? j + length : j) % length]._point;
}
var x = knots[0]._x + 2 * knots[1]._x,
y = knots[0]._y + 2 * knots[1]._y,
f = 2,
n_1 = n - 1,
rx = [x],
ry = [y],
rf = [f],
px = [],
py = [];
for (var i = 1; i < n; i++) {
var internal = i < n_1,
a = internal ? 1 : asymmetric ? 1 : 2,
b = internal ? 4 : asymmetric ? 2 : 7,
u = internal ? 4 : asymmetric ? 3 : 8,
v = internal ? 2 : asymmetric ? 0 : 1,
m = a / f;
f = rf[i] = b - m;
x = rx[i] = u * knots[i]._x + v * knots[i + 1]._x - m * x;
y = ry[i] = u * knots[i]._y + v * knots[i + 1]._y - m * y;
}
px[n_1] = rx[n_1] / rf[n_1];
py[n_1] = ry[n_1] / rf[n_1];
for (var i = n - 2; i >= 0; i--) {
px[i] = (rx[i] - px[i + 1]) / rf[i];
py[i] = (ry[i] - py[i + 1]) / rf[i];
}
px[n] = (3 * knots[n]._x - px[n_1]) / 2;
py[n] = (3 * knots[n]._y - py[n_1]) / 2;
for (var i = paddingLeft, max = n - paddingRight, j = from;
i <= max; i++, j++) {
var segment = segments[j < 0 ? j + length : j],
pt = segment._point,
hx = px[i] - pt._x,
hy = py[i] - pt._y;
if (loop || i < max)
segment.setHandleOut(hx, hy);
if (loop || i > paddingLeft)
segment.setHandleIn(-hx, -hy);
}
} else {
for (var i = from; i <= to; i++) {
segments[i < 0 ? i + length : i].smooth(opts,
!loop && i === from, !loop && i === to);
}
}
},
toShape: function(insert) {
if (!this._closed)
return null;
@ -8172,111 +8277,6 @@ var Path = PathItem.extend({
getNearestPoint: function() {
return this.getNearestLocation.apply(this, arguments).getPoint();
},
smooth: function(options) {
function getIndex(value, _default) {
var index = value && value.index;
if (index != null) {
var path = value.path;
if (path && path !== that)
throw new Error(value._class + ' ' + index + ' of ' + path
+ ' is not part of ' + that);
if (_default && value instanceof Curve)
index++;
} else {
index = typeof value === 'number' ? value : _default;
}
return Math.min(index < 0 && closed
? index % length
: index < 0 ? index + length : index, length - 1);
}
var that = this,
opts = options || {},
type = opts.type || 'asymmetric',
segments = this._segments,
length = segments.length,
closed = this._closed,
loop = closed && opts.from === undefined && opts.to === undefined,
from = getIndex(opts.from, 0),
to = getIndex(opts.to, length - 1);
if (from > to) {
if (closed) {
from -= length;
} else {
var tmp = from;
from = to;
to = tmp;
}
}
if (/^(?:asymmetric|continuous)$/.test(type)) {
var asymmetric = type === 'asymmetric',
min = Math.min,
amount = to - from + 1,
n = amount - 1,
padding = loop ? min(amount, 4) : 1,
paddingLeft = padding,
paddingRight = padding,
knots = [];
if (!closed) {
paddingLeft = min(1, from);
paddingRight = min(1, length - to - 1);
}
n += paddingLeft + paddingRight;
if (n <= 1)
return;
for (var i = 0, j = from - paddingLeft; i <= n; i++, j++) {
knots[i] = segments[(j < 0 ? j + length : j) % length]._point;
}
var x = knots[0]._x + 2 * knots[1]._x,
y = knots[0]._y + 2 * knots[1]._y,
f = 2,
n_1 = n - 1,
rx = [x],
ry = [y],
rf = [f],
px = [],
py = [];
for (var i = 1; i < n; i++) {
var internal = i < n_1,
a = internal ? 1 : asymmetric ? 1 : 2,
b = internal ? 4 : asymmetric ? 2 : 7,
u = internal ? 4 : asymmetric ? 3 : 8,
v = internal ? 2 : asymmetric ? 0 : 1,
m = a / f;
f = rf[i] = b - m;
x = rx[i] = u * knots[i]._x + v * knots[i + 1]._x - m * x;
y = ry[i] = u * knots[i]._y + v * knots[i + 1]._y - m * y;
}
px[n_1] = rx[n_1] / rf[n_1];
py[n_1] = ry[n_1] / rf[n_1];
for (var i = n - 2; i >= 0; i--) {
px[i] = (rx[i] - px[i + 1]) / rf[i];
py[i] = (ry[i] - py[i + 1]) / rf[i];
}
px[n] = (3 * knots[n]._x - px[n_1]) / 2;
py[n] = (3 * knots[n]._y - py[n_1]) / 2;
for (var i = paddingLeft, max = n - paddingRight, j = from;
i <= max; i++, j++) {
var segment = segments[j < 0 ? j + length : j],
pt = segment._point,
hx = px[i] - pt._x,
hy = py[i] - pt._y;
if (loop || i < max)
segment.setHandleOut(hx, hy);
if (loop || i > paddingLeft)
segment.setHandleIn(-hx, -hy);
}
} else {
for (var i = from; i <= to; i++) {
segments[i < 0 ? i + length : i].smooth(opts,
!loop && i === from, !loop && i === to);
}
}
}
}),
new function() {
@ -9040,12 +9040,6 @@ var CompoundPath = PathItem.extend({
return items;
},
reverse: function() {
var children = this._children;
for (var i = 0, l = children.length; i < l; i++)
children[i].reverse();
},
reduce: function reduce(options) {
var children = this._children;
for (var i = children.length - 1; i >= 0; i--) {
@ -9063,12 +9057,6 @@ var CompoundPath = PathItem.extend({
return reduce.base.call(this);
},
smooth: function(options) {
var children = this._children;
for (var i = 0, l = children.length; i < l; i++)
children[i].smooth(options);
},
isClockwise: function() {
var child = this.getFirstChild();
return child && child.isClockwise();
@ -9176,40 +9164,46 @@ new function() {
return children[children.length - 1];
}
var fields = {
moveTo: function() {
var current = getCurrentPath(this),
path = current && current.isEmpty() ? current
: new Path(Item.NO_INSERT);
if (path !== current)
this.addChild(path);
path.moveTo.apply(path, arguments);
},
return Base.each(['lineTo', 'cubicCurveTo', 'quadraticCurveTo', 'curveTo',
'arcTo', 'lineBy', 'cubicCurveBy', 'quadraticCurveBy', 'curveBy',
'arcBy'],
function(key) {
this[key] = function() {
var path = getCurrentPath(this, true);
path[key].apply(path, arguments);
};
}, {
moveTo: function() {
var current = getCurrentPath(this),
path = current && current.isEmpty() ? current
: new Path(Item.NO_INSERT);
if (path !== current)
this.addChild(path);
path.moveTo.apply(path, arguments);
},
moveBy: function() {
var current = getCurrentPath(this, true),
last = current && current.getLastSegment(),
point = Point.read(arguments);
this.moveTo(last ? point.add(last._point) : point);
},
moveBy: function() {
var current = getCurrentPath(this, true),
last = current && current.getLastSegment(),
point = Point.read(arguments);
this.moveTo(last ? point.add(last._point) : point);
},
closePath: function(join) {
getCurrentPath(this, true).closePath(join);
}
};
Base.each(['lineTo', 'cubicCurveTo', 'quadraticCurveTo', 'curveTo', 'arcTo',
'lineBy', 'cubicCurveBy', 'quadraticCurveBy', 'curveBy', 'arcBy'],
function(key) {
fields[key] = function() {
var path = getCurrentPath(this, true);
path[key].apply(path, arguments);
};
closePath: function(join) {
getCurrentPath(this, true).closePath(join);
}
}
);
return fields;
});
}, Base.each(['reverse', 'flatten', 'simplify', 'smooth'], function(key) {
this[key] = function(param) {
var children = this._children,
res;
for (var i = 0, l = children.length; i < l; i++) {
res = children[i][key](param) || res;
}
return res;
};
}, {}));
PathItem.inject(new function() {
var operators = {
@ -9972,63 +9966,61 @@ var PathIterator = Base.extend({
);
var PathFitter = Base.extend({
initialize: function(path, error) {
initialize: function(path) {
var points = this.points = [],
segments = path._segments,
prev;
for (var i = 0, l = segments.length; i < l; i++) {
var point = segments[i].point.clone();
closed = path._closed;
for (var i = 0, prev, l = segments.length; i < l; i++) {
var point = segments[i].point;
if (!prev || !prev.equals(point)) {
points.push(point);
prev = point;
points.push(prev = point.clone());
}
}
if (path._closed) {
this.closed = true;
if (closed) {
points.unshift(points[points.length - 1]);
points.push(points[1]);
}
this.error = error;
this.closed = closed;
},
fit: function() {
fit: function(error) {
var points = this.points,
length = points.length,
segments = this.segments = length > 0
? [new Segment(points[0])] : [];
if (length > 1)
this.fitCubic(0, length - 1,
points[1].subtract(points[0]).normalize(),
points[length - 2].subtract(points[length - 1]).normalize());
if (this.closed) {
segments.shift();
segments.pop();
segments = null;
if (length > 0) {
segments = [new Segment(points[0])];
if (length > 1) {
this.fitCubic(segments, error, 0, length - 1,
points[1].subtract(points[0]),
points[length - 2].subtract(points[length - 1]));
if (this.closed) {
segments.shift();
segments.pop();
}
}
}
return segments;
},
fitCubic: function(first, last, tan1, tan2) {
if (last - first == 1) {
var pt1 = this.points[first],
pt2 = this.points[last],
fitCubic: function(segments, error, first, last, tan1, tan2) {
var points = this.points;
if (last - first === 1) {
var pt1 = points[first],
pt2 = points[last],
dist = pt1.getDistance(pt2) / 3;
this.addCurve([pt1, pt1.add(tan1.normalize(dist)),
this.addCurve(segments, [pt1, pt1.add(tan1.normalize(dist)),
pt2.add(tan2.normalize(dist)), pt2]);
return;
}
var uPrime = this.chordLengthParameterize(first, last),
maxError = Math.max(this.error, this.error * this.error),
maxError = Math.max(error, error * error),
split,
parametersInOrder = true;
for (var i = 0; i <= 4; i++) {
var curve = this.generateBezier(first, last, uPrime, tan1, tan2);
var max = this.findMaxError(first, last, curve, uPrime);
if (max.error < this.error && parametersInOrder) {
this.addCurve(curve);
if (max.error < error && parametersInOrder) {
this.addCurve(segments, curve);
return;
}
split = max.index;
@ -10037,23 +10029,23 @@ var PathFitter = Base.extend({
parametersInOrder = this.reparameterize(first, last, uPrime, curve);
maxError = max.error;
}
var tanCenter = this.points[split - 1].subtract(this.points[split + 1])
.normalize();
this.fitCubic(first, split, tan1, tanCenter);
this.fitCubic(split, last, tanCenter.negate(), tan2);
var tanCenter = points[split - 1].subtract(points[split + 1]);
this.fitCubic(segments, error, first, split, tan1, tanCenter);
this.fitCubic(segments, error, split, last, tanCenter.negate(), tan2);
},
addCurve: function(curve) {
var prev = this.segments[this.segments.length - 1];
addCurve: function(segments, curve) {
var prev = segments[segments.length - 1];
prev.setHandleOut(curve[1].subtract(curve[0]));
this.segments.push(
new Segment(curve[3], curve[2].subtract(curve[3])));
segments.push(new Segment(curve[3], curve[2].subtract(curve[3])));
},
generateBezier: function(first, last, uPrime, tan1, tan2) {
var epsilon = 1e-12,
pt1 = this.points[first],
pt2 = this.points[last],
abs = Math.abs,
points = this.points,
pt1 = points[first],
pt2 = points[last],
C = [[0, 0], [0, 0]],
X = [0, 0];
@ -10067,7 +10059,7 @@ var PathFitter = Base.extend({
b3 = u * u * u,
a1 = tan1.normalize(b1),
a2 = tan2.normalize(b2),
tmp = this.points[first + i]
tmp = points[first + i]
.subtract(pt1.multiply(b0 + b1))
.subtract(pt2.multiply(b2 + b3));
C[0][0] += a1.dot(a1);
@ -10080,7 +10072,7 @@ var PathFitter = Base.extend({
var detC0C1 = C[0][0] * C[1][1] - C[1][0] * C[0][1],
alpha1, alpha2;
if (Math.abs(detC0C1) > epsilon) {
if (abs(detC0C1) > epsilon) {
var detC0X = C[0][0] * X[1] - C[1][0] * X[0],
detXC1 = X[0] * C[1][1] - X[1] * C[0][1];
alpha1 = detXC1 / detC0C1;
@ -10088,9 +10080,9 @@ var PathFitter = Base.extend({
} else {
var c0 = C[0][0] + C[0][1],
c1 = C[1][0] + C[1][1];
if (Math.abs(c0) > epsilon) {
if (abs(c0) > epsilon) {
alpha1 = alpha2 = X[0] / c0;
} else if (Math.abs(c1) > epsilon) {
} else if (abs(c1) > epsilon) {
alpha1 = alpha2 = X[1] / c1;
} else {
alpha1 = alpha2 = 0;
@ -10113,8 +10105,10 @@ var PathFitter = Base.extend({
}
}
return [pt1, pt1.add(handle1 || tan1.normalize(alpha1)),
pt2.add(handle2 || tan2.normalize(alpha2)), pt2];
return [pt1,
pt1.add(handle1 || tan1.normalize(alpha1)),
pt2.add(handle2 || tan2.normalize(alpha2)),
pt2];
},
reparameterize: function(first, last, u, curve) {

View file

@ -355,31 +355,6 @@ path.fillColor = 'black';
<!-- ============================== methods ================================ -->
<div class="reference-members">
<h2>Methods</h2>
<div id="reverse" class="member">
<div class="member-link">
<a name="reverse" href="#reverse"><tt><b>reverse</b>()</tt></a>
</div>
<div class="member-description hidden">
<div class="member-text">
<p>Reverses the orientation of all nested paths.</p>
</div>
</div>
</div>
</div>
@ -6619,6 +6594,81 @@ function onFrame(event) {
</div>
<div id="reverse" class="member">
<div class="member-link">
<a name="reverse" href="#reverse"><tt><b>reverse</b>()</tt></a>
</div>
<div class="member-description hidden">
<div class="member-text">
<p>Reverses the orientation of the path item. When called on <a href="../classes/CompoundPath.html"><tt>CompoundPath</tt></a> items, each of the nested paths is reversed. On <a href="../classes/Path.html"><tt>Path</tt></a> items, the sequence of <a href="../classes/Path.html#segments"><tt>path.segments</tt></a> is reversed.</p>
</div>
</div>
</div>
<div id="flatten-maxDistance" class="member">
<div class="member-link">
<a name="flatten-maxDistance" href="#flatten-maxDistance"><tt><b>flatten</b>(maxDistance)</tt></a>
</div>
<div class="member-description hidden">
<div class="member-text">
<p>Converts the curves in a path to straight lines with an even distribution of points. The distance between the produced segments is as close as possible to the value specified by the <code>maxDistance</code> parameter.</p>
<ul class="member-list">
<h4>Parameters:</h4>
<li>
<tt>maxDistance:</tt>
<tt>Number</tt>
&mdash;&nbsp;the maximum distance between the points
</li>
</ul>
<h4>Example:<span class="description">Flattening a circle shaped path:</span></h4>
<div class="paperscript split">
<div class="buttons">
<div class="button run">Run</div>
</div>
<script type="text/paperscript" canvas="canvas-56">
// Create a circle shaped path at { x: 80, y: 50 }
// with a radius of 35:
var path = new Path.Circle({
center: new Size(80, 50),
radius: 35
});
// Select the path, so we can inspect its segments:
path.selected = true;
// Create a copy of the path and move it 150 points to the right:
var copy = path.clone();
copy.position.x += 150;
// Convert its curves to points, with a max distance of 20:
copy.flatten(20);
</script>
<div class="canvas"><canvas width="516" height="100" id="canvas-56"></canvas></div>
</div>
</div>
</div>
</div>
<div id="smooth" class="member">
<div class="member-link">
<a name="smooth" href="#smooth"><tt><b>smooth</b>([options])</tt></a>
@ -6686,7 +6736,7 @@ function onFrame(event) {
<div class="button run">Run</div>
</div>
<script type="text/paperscript" canvas="canvas-56">
<script type="text/paperscript" canvas="canvas-57">
// 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({
@ -6705,7 +6755,7 @@ copy.position.x += 100;
// Smooth the segments of the copy:
copy.smooth({ type: 'continuous' });
</script>
<div class="canvas"><canvas width="516" height="100" id="canvas-56"></canvas></div>
<div class="canvas"><canvas width="516" height="100" id="canvas-57"></canvas></div>
</div>
@ -6717,7 +6767,7 @@ copy.smooth({ type: 'continuous' });
<div class="button run">Run</div>
</div>
<script type="text/paperscript" canvas="canvas-57">
<script type="text/paperscript" canvas="canvas-58">
var path = new Path();
path.strokeColor = 'black';
@ -6742,7 +6792,7 @@ copy.fullySelected = true;
// Smooth the path using centripetal Catmull-Rom splines:
copy.smooth({ type: 'catmull-rom', factor: 0.5 });
</script>
<div class="canvas"><canvas width="516" height="220" id="canvas-57"></canvas></div>
<div class="canvas"><canvas width="516" height="220" id="canvas-58"></canvas></div>
</div>
@ -6754,7 +6804,7 @@ copy.smooth({ type: 'catmull-rom', factor: 0.5 });
<div class="button run">Run</div>
</div>
<script type="text/paperscript" canvas="canvas-58">
<script type="text/paperscript" canvas="canvas-59">
// Create 5 rectangles, next to each other:
var paths = [];
for (var i = 0; i < 5; i++) {
@ -6784,7 +6834,87 @@ paths[3].smooth({ type: 'continuous', from: 0, to: 2 });
// Smooth a range, using negative indices:
paths[4].smooth({ type: 'continuous', from: -1, to: 1 });
</script>
<div class="canvas"><canvas width="516" height="110" id="canvas-58"></canvas></div>
<div class="canvas"><canvas width="516" height="110" id="canvas-59"></canvas></div>
</div>
</div>
</div>
</div>
<div id="simplify" class="member">
<div class="member-link">
<a name="simplify" href="#simplify"><tt><b>simplify</b>([tolerance])</tt></a>
</div>
<div class="member-description hidden">
<div class="member-text">
<p>Fits a sequence of as few curves as possible through the path&rsquo;s anchor points, ignoring the path items&rsquo;s curve-handles, with an allowed maximum error. When called on <a href="../classes/CompoundPath.html"><tt>CompoundPath</tt></a> items, each of the nested paths is simplified. On <a href="../classes/Path.html"><tt>Path</tt></a> items, the <a href="../classes/Path.html#segments"><tt>path.segments</tt></a> array is processed and replaced by the resulting sequence of fitted curves.</p>
<p>This method can be used to process and simplify the point data received from a mouse or touch device.</p>
<ul class="member-list">
<h4>Parameters:</h4>
<li>
<tt>tolerance:</tt>
<tt>Number</tt>
&mdash;&nbsp;the allowed maximum error when fitting the curves through the segment points
&mdash;&nbsp;optional, default: <tt>2.5</tt>
</li>
</ul>
<ul class="member-list">
<h4>Returns:</h4>
<li>
<tt><tt>Boolean</tt></tt>&nbsp;&mdash;&nbsp;<tt>true</tt> if the method was capable of fitting curves through the path&rsquo;s segment points, <tt>false</tt> otherwise
</li>
</ul>
<h4>Example:<span class="description">Click and drag below to draw to draw a line, when you release the mouse, the is made smooth using path.simplify():</span></h4>
<div class="paperscript split">
<div class="buttons">
<div class="button run">Run</div>
</div>
<script type="text/paperscript" canvas="canvas-60">
var path;
function onMouseDown(event) {
// If we already made a path before, deselect it:
if (path) {
path.selected = false;
}
// Create a new path and add the position of the mouse
// as its first segment. Select it, so we can see the
// segment points:
path = new Path({
segments: [event.point],
strokeColor: 'black',
selected: true
});
}
function onMouseDrag(event) {
// On every drag event, add a segment to the path
// at the position of the mouse:
path.add(event.point);
}
function onMouseUp(event) {
// When the mouse is released, simplify the path:
path.simplify();
path.selected = true;
}
</script>
<div class="canvas"><canvas width="516" height="300" id="canvas-60"></canvas></div>
</div>
@ -7020,7 +7150,7 @@ paths[4].smooth({ type: 'continuous', from: -1, to: 1 });
<div class="button run">Run</div>
</div>
<script type="text/paperscript" canvas="canvas-59">
<script type="text/paperscript" canvas="canvas-61">
var myPath;
function onMouseMove(event) {
// If we created a path before, remove it:
@ -7041,7 +7171,7 @@ function onMouseMove(event) {
myPath.selected = true;
}
</script>
<div class="canvas"><canvas width="516" height="300" id="canvas-59"></canvas></div>
<div class="canvas"><canvas width="516" height="300" id="canvas-61"></canvas></div>
</div>
@ -7088,7 +7218,7 @@ function onMouseMove(event) {
<div class="button run">Run</div>
</div>
<script type="text/paperscript" canvas="canvas-60">
<script type="text/paperscript" canvas="canvas-62">
var path = new Path();
path.strokeColor = 'black';
@ -7108,7 +7238,7 @@ path.arcTo(throughPoint, toPoint);
var circle = new Path.Circle(throughPoint, 3);
circle.fillColor = 'red';
</script>
<div class="canvas"><canvas width="516" height="100" id="canvas-60"></canvas></div>
<div class="canvas"><canvas width="516" height="100" id="canvas-62"></canvas></div>
</div>
@ -7120,7 +7250,7 @@ circle.fillColor = 'red';
<div class="button run">Run</div>
</div>
<script type="text/paperscript" canvas="canvas-61">
<script type="text/paperscript" canvas="canvas-63">
var myPath;
function onMouseDrag(event) {
// If we created a path before, remove it:
@ -7148,7 +7278,7 @@ function onMouseUp(event) {
myPath.fillColor = 'black';
}
</script>
<div class="canvas"><canvas width="516" height="300" id="canvas-61"></canvas></div>
<div class="canvas"><canvas width="516" height="300" id="canvas-63"></canvas></div>
</div>
@ -7195,7 +7325,7 @@ function onMouseUp(event) {
<div class="button run">Run</div>
</div>
<script type="text/paperscript" canvas="canvas-62">
<script type="text/paperscript" canvas="canvas-64">
var path = new Path();
path.strokeColor = 'black';
@ -7210,7 +7340,7 @@ path2.add(new Point(180, 25));
// we pass `false` as the second argument to arcTo:
path2.arcTo(new Point(280, 25), false);
</script>
<div class="canvas"><canvas width="516" height="100" id="canvas-62"></canvas></div>
<div class="canvas"><canvas width="516" height="100" id="canvas-64"></canvas></div>
</div>
@ -7222,7 +7352,7 @@ path2.arcTo(new Point(280, 25), false);
<div class="button run">Run</div>
</div>
<script type="text/paperscript" canvas="canvas-63">
<script type="text/paperscript" canvas="canvas-65">
var myPath;
// The mouse has to move at least 20 points before
@ -7243,7 +7373,7 @@ function onMouseDrag(event) {
myPath.arcTo(event.point);
}
</script>
<div class="canvas"><canvas width="516" height="300" id="canvas-63"></canvas></div>
<div class="canvas"><canvas width="516" height="300" id="canvas-65"></canvas></div>
</div>
@ -7350,7 +7480,7 @@ function onMouseDrag(event) {
<div class="button run">Run</div>
</div>
<script type="text/paperscript" canvas="canvas-64">
<script type="text/paperscript" canvas="canvas-66">
var path = new Path();
path.strokeColor = 'black';
@ -7364,7 +7494,7 @@ path.lineBy(50, 0);
// 0 in x direction and 50 in y direction, becomes {x: 75, y: 75}
path.lineBy(0, 50);
</script>
<div class="canvas"><canvas width="516" height="100" id="canvas-64"></canvas></div>
<div class="canvas"><canvas width="516" height="100" id="canvas-66"></canvas></div>
</div>
@ -7376,7 +7506,7 @@ path.lineBy(0, 50);
<div class="button run">Run</div>
</div>
<script type="text/paperscript" canvas="canvas-65">
<script type="text/paperscript" canvas="canvas-67">
var path = new Path();
path.strokeColor = 'black';
@ -7402,7 +7532,7 @@ path.smooth();
// the construction of the path:
// path.selected = true;
</script>
<div class="canvas"><canvas width="516" height="300" id="canvas-65"></canvas></div>
<div class="canvas"><canvas width="516" height="300" id="canvas-67"></canvas></div>
</div>

View file

@ -1557,7 +1557,7 @@
</div>
<div class="member-description hidden">
<div class="member-text">
<p>Returns the curve-time parameter of the specified point if it lies on the curve, <code>null</code> otherwise.</p>
<p>Returns the curve-time parameter of the specified point if it lies on the curve, <code>null</code> otherwise. Note that if there is more than one possible solution in a self-intersecting curve, the first found result is returned.</p>
<ul class="member-list">

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

480
dist/paper-core.js vendored
View file

@ -9,7 +9,7 @@
*
* All rights reserved.
*
* Date: Tue Feb 9 10:07:28 2016 +0100
* Date: Tue Feb 9 14:13:30 2016 +0100
*
***
*
@ -7764,39 +7764,6 @@ var Path = PathItem.extend({
this.setSelected(true);
},
flatten: function(maxDistance) {
var iterator = new PathIterator(this, 64, 0.1),
pos = 0,
step = iterator.length / Math.ceil(iterator.length / maxDistance),
end = iterator.length + (this._closed ? -step : step) / 2;
var segments = [];
while (pos <= end) {
segments.push(new Segment(iterator.getPointAt(pos)));
pos += step;
}
this.setSegments(segments);
},
reduce: function(options) {
var curves = this.getCurves(),
simplify = options && options.simplify,
tolerance = simplify ? 2e-7 : 0;
for (var i = curves.length - 1; i >= 0; i--) {
var curve = curves[i];
if (!curve.hasHandles() && (curve.getLength() < tolerance
|| simplify && curve.isCollinear(curve.getNext())))
curve.remove();
}
return this;
},
simplify: function(tolerance) {
if (this._segments.length > 2) {
var fitter = new PathFitter(this, tolerance || 2.5);
this.setSegments(fitter.fit());
}
},
splitAt: function(location) {
var index = location && location.index,
time = location && location.time,
@ -7836,21 +7803,6 @@ var Path = PathItem.extend({
return location ? this.splitAt(location) : null;
},
reverse: function() {
this._segments.reverse();
for (var i = 0, l = this._segments.length; i < l; i++) {
var segment = this._segments[i];
var handleIn = segment._handleIn;
segment._handleIn = segment._handleOut;
segment._handleOut = handleIn;
segment._index = i;
}
this._curves = null;
if (this._clockwise !== undefined)
this._clockwise = !this._clockwise;
this._changed(9);
},
join: function(path) {
if (path) {
var segments = path._segments,
@ -7890,6 +7842,159 @@ var Path = PathItem.extend({
return this;
},
reverse: function() {
this._segments.reverse();
for (var i = 0, l = this._segments.length; i < l; i++) {
var segment = this._segments[i];
var handleIn = segment._handleIn;
segment._handleIn = segment._handleOut;
segment._handleOut = handleIn;
segment._index = i;
}
this._curves = null;
if (this._clockwise !== undefined)
this._clockwise = !this._clockwise;
this._changed(9);
},
flatten: function(maxDistance) {
var iterator = new PathIterator(this, 64, 0.1),
pos = 0,
step = iterator.length / Math.ceil(iterator.length / maxDistance),
end = iterator.length + (this._closed ? -step : step) / 2;
var segments = [];
while (pos <= end) {
segments.push(new Segment(iterator.getPointAt(pos)));
pos += step;
}
this.setSegments(segments);
},
reduce: function(options) {
var curves = this.getCurves(),
simplify = options && options.simplify,
tolerance = simplify ? 2e-7 : 0;
for (var i = curves.length - 1; i >= 0; i--) {
var curve = curves[i];
if (!curve.hasHandles() && (curve.getLength() < tolerance
|| simplify && curve.isCollinear(curve.getNext())))
curve.remove();
}
return this;
},
simplify: function(tolerance) {
var segments = new PathFitter(this).fit(tolerance || 2.5);
if (segments)
this.setSegments(segments);
return !!segments;
},
smooth: function(options) {
function getIndex(value, _default) {
var index = value && value.index;
if (index != null) {
var path = value.path;
if (path && path !== that)
throw new Error(value._class + ' ' + index + ' of ' + path
+ ' is not part of ' + that);
if (_default && value instanceof Curve)
index++;
} else {
index = typeof value === 'number' ? value : _default;
}
return Math.min(index < 0 && closed
? index % length
: index < 0 ? index + length : index, length - 1);
}
var that = this,
opts = options || {},
type = opts.type || 'asymmetric',
segments = this._segments,
length = segments.length,
closed = this._closed,
loop = closed && opts.from === undefined && opts.to === undefined,
from = getIndex(opts.from, 0),
to = getIndex(opts.to, length - 1);
if (from > to) {
if (closed) {
from -= length;
} else {
var tmp = from;
from = to;
to = tmp;
}
}
if (/^(?:asymmetric|continuous)$/.test(type)) {
var asymmetric = type === 'asymmetric',
min = Math.min,
amount = to - from + 1,
n = amount - 1,
padding = loop ? min(amount, 4) : 1,
paddingLeft = padding,
paddingRight = padding,
knots = [];
if (!closed) {
paddingLeft = min(1, from);
paddingRight = min(1, length - to - 1);
}
n += paddingLeft + paddingRight;
if (n <= 1)
return;
for (var i = 0, j = from - paddingLeft; i <= n; i++, j++) {
knots[i] = segments[(j < 0 ? j + length : j) % length]._point;
}
var x = knots[0]._x + 2 * knots[1]._x,
y = knots[0]._y + 2 * knots[1]._y,
f = 2,
n_1 = n - 1,
rx = [x],
ry = [y],
rf = [f],
px = [],
py = [];
for (var i = 1; i < n; i++) {
var internal = i < n_1,
a = internal ? 1 : asymmetric ? 1 : 2,
b = internal ? 4 : asymmetric ? 2 : 7,
u = internal ? 4 : asymmetric ? 3 : 8,
v = internal ? 2 : asymmetric ? 0 : 1,
m = a / f;
f = rf[i] = b - m;
x = rx[i] = u * knots[i]._x + v * knots[i + 1]._x - m * x;
y = ry[i] = u * knots[i]._y + v * knots[i + 1]._y - m * y;
}
px[n_1] = rx[n_1] / rf[n_1];
py[n_1] = ry[n_1] / rf[n_1];
for (var i = n - 2; i >= 0; i--) {
px[i] = (rx[i] - px[i + 1]) / rf[i];
py[i] = (ry[i] - py[i + 1]) / rf[i];
}
px[n] = (3 * knots[n]._x - px[n_1]) / 2;
py[n] = (3 * knots[n]._y - py[n_1]) / 2;
for (var i = paddingLeft, max = n - paddingRight, j = from;
i <= max; i++, j++) {
var segment = segments[j < 0 ? j + length : j],
pt = segment._point,
hx = px[i] - pt._x,
hy = py[i] - pt._y;
if (loop || i < max)
segment.setHandleOut(hx, hy);
if (loop || i > paddingLeft)
segment.setHandleIn(-hx, -hy);
}
} else {
for (var i = from; i <= to; i++) {
segments[i < 0 ? i + length : i].smooth(opts,
!loop && i === from, !loop && i === to);
}
}
},
toShape: function(insert) {
if (!this._closed)
return null;
@ -8172,111 +8277,6 @@ var Path = PathItem.extend({
getNearestPoint: function() {
return this.getNearestLocation.apply(this, arguments).getPoint();
},
smooth: function(options) {
function getIndex(value, _default) {
var index = value && value.index;
if (index != null) {
var path = value.path;
if (path && path !== that)
throw new Error(value._class + ' ' + index + ' of ' + path
+ ' is not part of ' + that);
if (_default && value instanceof Curve)
index++;
} else {
index = typeof value === 'number' ? value : _default;
}
return Math.min(index < 0 && closed
? index % length
: index < 0 ? index + length : index, length - 1);
}
var that = this,
opts = options || {},
type = opts.type || 'asymmetric',
segments = this._segments,
length = segments.length,
closed = this._closed,
loop = closed && opts.from === undefined && opts.to === undefined,
from = getIndex(opts.from, 0),
to = getIndex(opts.to, length - 1);
if (from > to) {
if (closed) {
from -= length;
} else {
var tmp = from;
from = to;
to = tmp;
}
}
if (/^(?:asymmetric|continuous)$/.test(type)) {
var asymmetric = type === 'asymmetric',
min = Math.min,
amount = to - from + 1,
n = amount - 1,
padding = loop ? min(amount, 4) : 1,
paddingLeft = padding,
paddingRight = padding,
knots = [];
if (!closed) {
paddingLeft = min(1, from);
paddingRight = min(1, length - to - 1);
}
n += paddingLeft + paddingRight;
if (n <= 1)
return;
for (var i = 0, j = from - paddingLeft; i <= n; i++, j++) {
knots[i] = segments[(j < 0 ? j + length : j) % length]._point;
}
var x = knots[0]._x + 2 * knots[1]._x,
y = knots[0]._y + 2 * knots[1]._y,
f = 2,
n_1 = n - 1,
rx = [x],
ry = [y],
rf = [f],
px = [],
py = [];
for (var i = 1; i < n; i++) {
var internal = i < n_1,
a = internal ? 1 : asymmetric ? 1 : 2,
b = internal ? 4 : asymmetric ? 2 : 7,
u = internal ? 4 : asymmetric ? 3 : 8,
v = internal ? 2 : asymmetric ? 0 : 1,
m = a / f;
f = rf[i] = b - m;
x = rx[i] = u * knots[i]._x + v * knots[i + 1]._x - m * x;
y = ry[i] = u * knots[i]._y + v * knots[i + 1]._y - m * y;
}
px[n_1] = rx[n_1] / rf[n_1];
py[n_1] = ry[n_1] / rf[n_1];
for (var i = n - 2; i >= 0; i--) {
px[i] = (rx[i] - px[i + 1]) / rf[i];
py[i] = (ry[i] - py[i + 1]) / rf[i];
}
px[n] = (3 * knots[n]._x - px[n_1]) / 2;
py[n] = (3 * knots[n]._y - py[n_1]) / 2;
for (var i = paddingLeft, max = n - paddingRight, j = from;
i <= max; i++, j++) {
var segment = segments[j < 0 ? j + length : j],
pt = segment._point,
hx = px[i] - pt._x,
hy = py[i] - pt._y;
if (loop || i < max)
segment.setHandleOut(hx, hy);
if (loop || i > paddingLeft)
segment.setHandleIn(-hx, -hy);
}
} else {
for (var i = from; i <= to; i++) {
segments[i < 0 ? i + length : i].smooth(opts,
!loop && i === from, !loop && i === to);
}
}
}
}),
new function() {
@ -9040,12 +9040,6 @@ var CompoundPath = PathItem.extend({
return items;
},
reverse: function() {
var children = this._children;
for (var i = 0, l = children.length; i < l; i++)
children[i].reverse();
},
reduce: function reduce(options) {
var children = this._children;
for (var i = children.length - 1; i >= 0; i--) {
@ -9063,12 +9057,6 @@ var CompoundPath = PathItem.extend({
return reduce.base.call(this);
},
smooth: function(options) {
var children = this._children;
for (var i = 0, l = children.length; i < l; i++)
children[i].smooth(options);
},
isClockwise: function() {
var child = this.getFirstChild();
return child && child.isClockwise();
@ -9176,40 +9164,46 @@ new function() {
return children[children.length - 1];
}
var fields = {
moveTo: function() {
var current = getCurrentPath(this),
path = current && current.isEmpty() ? current
: new Path(Item.NO_INSERT);
if (path !== current)
this.addChild(path);
path.moveTo.apply(path, arguments);
},
return Base.each(['lineTo', 'cubicCurveTo', 'quadraticCurveTo', 'curveTo',
'arcTo', 'lineBy', 'cubicCurveBy', 'quadraticCurveBy', 'curveBy',
'arcBy'],
function(key) {
this[key] = function() {
var path = getCurrentPath(this, true);
path[key].apply(path, arguments);
};
}, {
moveTo: function() {
var current = getCurrentPath(this),
path = current && current.isEmpty() ? current
: new Path(Item.NO_INSERT);
if (path !== current)
this.addChild(path);
path.moveTo.apply(path, arguments);
},
moveBy: function() {
var current = getCurrentPath(this, true),
last = current && current.getLastSegment(),
point = Point.read(arguments);
this.moveTo(last ? point.add(last._point) : point);
},
moveBy: function() {
var current = getCurrentPath(this, true),
last = current && current.getLastSegment(),
point = Point.read(arguments);
this.moveTo(last ? point.add(last._point) : point);
},
closePath: function(join) {
getCurrentPath(this, true).closePath(join);
}
};
Base.each(['lineTo', 'cubicCurveTo', 'quadraticCurveTo', 'curveTo', 'arcTo',
'lineBy', 'cubicCurveBy', 'quadraticCurveBy', 'curveBy', 'arcBy'],
function(key) {
fields[key] = function() {
var path = getCurrentPath(this, true);
path[key].apply(path, arguments);
};
closePath: function(join) {
getCurrentPath(this, true).closePath(join);
}
}
);
return fields;
});
}, Base.each(['reverse', 'flatten', 'simplify', 'smooth'], function(key) {
this[key] = function(param) {
var children = this._children,
res;
for (var i = 0, l = children.length; i < l; i++) {
res = children[i][key](param) || res;
}
return res;
};
}, {}));
PathItem.inject(new function() {
var operators = {
@ -9972,63 +9966,61 @@ var PathIterator = Base.extend({
);
var PathFitter = Base.extend({
initialize: function(path, error) {
initialize: function(path) {
var points = this.points = [],
segments = path._segments,
prev;
for (var i = 0, l = segments.length; i < l; i++) {
var point = segments[i].point.clone();
closed = path._closed;
for (var i = 0, prev, l = segments.length; i < l; i++) {
var point = segments[i].point;
if (!prev || !prev.equals(point)) {
points.push(point);
prev = point;
points.push(prev = point.clone());
}
}
if (path._closed) {
this.closed = true;
if (closed) {
points.unshift(points[points.length - 1]);
points.push(points[1]);
}
this.error = error;
this.closed = closed;
},
fit: function() {
fit: function(error) {
var points = this.points,
length = points.length,
segments = this.segments = length > 0
? [new Segment(points[0])] : [];
if (length > 1)
this.fitCubic(0, length - 1,
points[1].subtract(points[0]).normalize(),
points[length - 2].subtract(points[length - 1]).normalize());
if (this.closed) {
segments.shift();
segments.pop();
segments = null;
if (length > 0) {
segments = [new Segment(points[0])];
if (length > 1) {
this.fitCubic(segments, error, 0, length - 1,
points[1].subtract(points[0]),
points[length - 2].subtract(points[length - 1]));
if (this.closed) {
segments.shift();
segments.pop();
}
}
}
return segments;
},
fitCubic: function(first, last, tan1, tan2) {
if (last - first == 1) {
var pt1 = this.points[first],
pt2 = this.points[last],
fitCubic: function(segments, error, first, last, tan1, tan2) {
var points = this.points;
if (last - first === 1) {
var pt1 = points[first],
pt2 = points[last],
dist = pt1.getDistance(pt2) / 3;
this.addCurve([pt1, pt1.add(tan1.normalize(dist)),
this.addCurve(segments, [pt1, pt1.add(tan1.normalize(dist)),
pt2.add(tan2.normalize(dist)), pt2]);
return;
}
var uPrime = this.chordLengthParameterize(first, last),
maxError = Math.max(this.error, this.error * this.error),
maxError = Math.max(error, error * error),
split,
parametersInOrder = true;
for (var i = 0; i <= 4; i++) {
var curve = this.generateBezier(first, last, uPrime, tan1, tan2);
var max = this.findMaxError(first, last, curve, uPrime);
if (max.error < this.error && parametersInOrder) {
this.addCurve(curve);
if (max.error < error && parametersInOrder) {
this.addCurve(segments, curve);
return;
}
split = max.index;
@ -10037,23 +10029,23 @@ var PathFitter = Base.extend({
parametersInOrder = this.reparameterize(first, last, uPrime, curve);
maxError = max.error;
}
var tanCenter = this.points[split - 1].subtract(this.points[split + 1])
.normalize();
this.fitCubic(first, split, tan1, tanCenter);
this.fitCubic(split, last, tanCenter.negate(), tan2);
var tanCenter = points[split - 1].subtract(points[split + 1]);
this.fitCubic(segments, error, first, split, tan1, tanCenter);
this.fitCubic(segments, error, split, last, tanCenter.negate(), tan2);
},
addCurve: function(curve) {
var prev = this.segments[this.segments.length - 1];
addCurve: function(segments, curve) {
var prev = segments[segments.length - 1];
prev.setHandleOut(curve[1].subtract(curve[0]));
this.segments.push(
new Segment(curve[3], curve[2].subtract(curve[3])));
segments.push(new Segment(curve[3], curve[2].subtract(curve[3])));
},
generateBezier: function(first, last, uPrime, tan1, tan2) {
var epsilon = 1e-12,
pt1 = this.points[first],
pt2 = this.points[last],
abs = Math.abs,
points = this.points,
pt1 = points[first],
pt2 = points[last],
C = [[0, 0], [0, 0]],
X = [0, 0];
@ -10067,7 +10059,7 @@ var PathFitter = Base.extend({
b3 = u * u * u,
a1 = tan1.normalize(b1),
a2 = tan2.normalize(b2),
tmp = this.points[first + i]
tmp = points[first + i]
.subtract(pt1.multiply(b0 + b1))
.subtract(pt2.multiply(b2 + b3));
C[0][0] += a1.dot(a1);
@ -10080,7 +10072,7 @@ var PathFitter = Base.extend({
var detC0C1 = C[0][0] * C[1][1] - C[1][0] * C[0][1],
alpha1, alpha2;
if (Math.abs(detC0C1) > epsilon) {
if (abs(detC0C1) > epsilon) {
var detC0X = C[0][0] * X[1] - C[1][0] * X[0],
detXC1 = X[0] * C[1][1] - X[1] * C[0][1];
alpha1 = detXC1 / detC0C1;
@ -10088,9 +10080,9 @@ var PathFitter = Base.extend({
} else {
var c0 = C[0][0] + C[0][1],
c1 = C[1][0] + C[1][1];
if (Math.abs(c0) > epsilon) {
if (abs(c0) > epsilon) {
alpha1 = alpha2 = X[0] / c0;
} else if (Math.abs(c1) > epsilon) {
} else if (abs(c1) > epsilon) {
alpha1 = alpha2 = X[1] / c1;
} else {
alpha1 = alpha2 = 0;
@ -10113,8 +10105,10 @@ var PathFitter = Base.extend({
}
}
return [pt1, pt1.add(handle1 || tan1.normalize(alpha1)),
pt2.add(handle2 || tan2.normalize(alpha2)), pt2];
return [pt1,
pt1.add(handle1 || tan1.normalize(alpha1)),
pt2.add(handle2 || tan2.normalize(alpha2)),
pt2];
},
reparameterize: function(first, last, u, curve) {

File diff suppressed because one or more lines are too long

480
dist/paper-full.js vendored
View file

@ -9,7 +9,7 @@
*
* All rights reserved.
*
* Date: Tue Feb 9 10:07:28 2016 +0100
* Date: Tue Feb 9 14:13:30 2016 +0100
*
***
*
@ -7764,39 +7764,6 @@ var Path = PathItem.extend({
this.setSelected(true);
},
flatten: function(maxDistance) {
var iterator = new PathIterator(this, 64, 0.1),
pos = 0,
step = iterator.length / Math.ceil(iterator.length / maxDistance),
end = iterator.length + (this._closed ? -step : step) / 2;
var segments = [];
while (pos <= end) {
segments.push(new Segment(iterator.getPointAt(pos)));
pos += step;
}
this.setSegments(segments);
},
reduce: function(options) {
var curves = this.getCurves(),
simplify = options && options.simplify,
tolerance = simplify ? 2e-7 : 0;
for (var i = curves.length - 1; i >= 0; i--) {
var curve = curves[i];
if (!curve.hasHandles() && (curve.getLength() < tolerance
|| simplify && curve.isCollinear(curve.getNext())))
curve.remove();
}
return this;
},
simplify: function(tolerance) {
if (this._segments.length > 2) {
var fitter = new PathFitter(this, tolerance || 2.5);
this.setSegments(fitter.fit());
}
},
splitAt: function(location) {
var index = location && location.index,
time = location && location.time,
@ -7836,21 +7803,6 @@ var Path = PathItem.extend({
return location ? this.splitAt(location) : null;
},
reverse: function() {
this._segments.reverse();
for (var i = 0, l = this._segments.length; i < l; i++) {
var segment = this._segments[i];
var handleIn = segment._handleIn;
segment._handleIn = segment._handleOut;
segment._handleOut = handleIn;
segment._index = i;
}
this._curves = null;
if (this._clockwise !== undefined)
this._clockwise = !this._clockwise;
this._changed(9);
},
join: function(path) {
if (path) {
var segments = path._segments,
@ -7890,6 +7842,159 @@ var Path = PathItem.extend({
return this;
},
reverse: function() {
this._segments.reverse();
for (var i = 0, l = this._segments.length; i < l; i++) {
var segment = this._segments[i];
var handleIn = segment._handleIn;
segment._handleIn = segment._handleOut;
segment._handleOut = handleIn;
segment._index = i;
}
this._curves = null;
if (this._clockwise !== undefined)
this._clockwise = !this._clockwise;
this._changed(9);
},
flatten: function(maxDistance) {
var iterator = new PathIterator(this, 64, 0.1),
pos = 0,
step = iterator.length / Math.ceil(iterator.length / maxDistance),
end = iterator.length + (this._closed ? -step : step) / 2;
var segments = [];
while (pos <= end) {
segments.push(new Segment(iterator.getPointAt(pos)));
pos += step;
}
this.setSegments(segments);
},
reduce: function(options) {
var curves = this.getCurves(),
simplify = options && options.simplify,
tolerance = simplify ? 2e-7 : 0;
for (var i = curves.length - 1; i >= 0; i--) {
var curve = curves[i];
if (!curve.hasHandles() && (curve.getLength() < tolerance
|| simplify && curve.isCollinear(curve.getNext())))
curve.remove();
}
return this;
},
simplify: function(tolerance) {
var segments = new PathFitter(this).fit(tolerance || 2.5);
if (segments)
this.setSegments(segments);
return !!segments;
},
smooth: function(options) {
function getIndex(value, _default) {
var index = value && value.index;
if (index != null) {
var path = value.path;
if (path && path !== that)
throw new Error(value._class + ' ' + index + ' of ' + path
+ ' is not part of ' + that);
if (_default && value instanceof Curve)
index++;
} else {
index = typeof value === 'number' ? value : _default;
}
return Math.min(index < 0 && closed
? index % length
: index < 0 ? index + length : index, length - 1);
}
var that = this,
opts = options || {},
type = opts.type || 'asymmetric',
segments = this._segments,
length = segments.length,
closed = this._closed,
loop = closed && opts.from === undefined && opts.to === undefined,
from = getIndex(opts.from, 0),
to = getIndex(opts.to, length - 1);
if (from > to) {
if (closed) {
from -= length;
} else {
var tmp = from;
from = to;
to = tmp;
}
}
if (/^(?:asymmetric|continuous)$/.test(type)) {
var asymmetric = type === 'asymmetric',
min = Math.min,
amount = to - from + 1,
n = amount - 1,
padding = loop ? min(amount, 4) : 1,
paddingLeft = padding,
paddingRight = padding,
knots = [];
if (!closed) {
paddingLeft = min(1, from);
paddingRight = min(1, length - to - 1);
}
n += paddingLeft + paddingRight;
if (n <= 1)
return;
for (var i = 0, j = from - paddingLeft; i <= n; i++, j++) {
knots[i] = segments[(j < 0 ? j + length : j) % length]._point;
}
var x = knots[0]._x + 2 * knots[1]._x,
y = knots[0]._y + 2 * knots[1]._y,
f = 2,
n_1 = n - 1,
rx = [x],
ry = [y],
rf = [f],
px = [],
py = [];
for (var i = 1; i < n; i++) {
var internal = i < n_1,
a = internal ? 1 : asymmetric ? 1 : 2,
b = internal ? 4 : asymmetric ? 2 : 7,
u = internal ? 4 : asymmetric ? 3 : 8,
v = internal ? 2 : asymmetric ? 0 : 1,
m = a / f;
f = rf[i] = b - m;
x = rx[i] = u * knots[i]._x + v * knots[i + 1]._x - m * x;
y = ry[i] = u * knots[i]._y + v * knots[i + 1]._y - m * y;
}
px[n_1] = rx[n_1] / rf[n_1];
py[n_1] = ry[n_1] / rf[n_1];
for (var i = n - 2; i >= 0; i--) {
px[i] = (rx[i] - px[i + 1]) / rf[i];
py[i] = (ry[i] - py[i + 1]) / rf[i];
}
px[n] = (3 * knots[n]._x - px[n_1]) / 2;
py[n] = (3 * knots[n]._y - py[n_1]) / 2;
for (var i = paddingLeft, max = n - paddingRight, j = from;
i <= max; i++, j++) {
var segment = segments[j < 0 ? j + length : j],
pt = segment._point,
hx = px[i] - pt._x,
hy = py[i] - pt._y;
if (loop || i < max)
segment.setHandleOut(hx, hy);
if (loop || i > paddingLeft)
segment.setHandleIn(-hx, -hy);
}
} else {
for (var i = from; i <= to; i++) {
segments[i < 0 ? i + length : i].smooth(opts,
!loop && i === from, !loop && i === to);
}
}
},
toShape: function(insert) {
if (!this._closed)
return null;
@ -8172,111 +8277,6 @@ var Path = PathItem.extend({
getNearestPoint: function() {
return this.getNearestLocation.apply(this, arguments).getPoint();
},
smooth: function(options) {
function getIndex(value, _default) {
var index = value && value.index;
if (index != null) {
var path = value.path;
if (path && path !== that)
throw new Error(value._class + ' ' + index + ' of ' + path
+ ' is not part of ' + that);
if (_default && value instanceof Curve)
index++;
} else {
index = typeof value === 'number' ? value : _default;
}
return Math.min(index < 0 && closed
? index % length
: index < 0 ? index + length : index, length - 1);
}
var that = this,
opts = options || {},
type = opts.type || 'asymmetric',
segments = this._segments,
length = segments.length,
closed = this._closed,
loop = closed && opts.from === undefined && opts.to === undefined,
from = getIndex(opts.from, 0),
to = getIndex(opts.to, length - 1);
if (from > to) {
if (closed) {
from -= length;
} else {
var tmp = from;
from = to;
to = tmp;
}
}
if (/^(?:asymmetric|continuous)$/.test(type)) {
var asymmetric = type === 'asymmetric',
min = Math.min,
amount = to - from + 1,
n = amount - 1,
padding = loop ? min(amount, 4) : 1,
paddingLeft = padding,
paddingRight = padding,
knots = [];
if (!closed) {
paddingLeft = min(1, from);
paddingRight = min(1, length - to - 1);
}
n += paddingLeft + paddingRight;
if (n <= 1)
return;
for (var i = 0, j = from - paddingLeft; i <= n; i++, j++) {
knots[i] = segments[(j < 0 ? j + length : j) % length]._point;
}
var x = knots[0]._x + 2 * knots[1]._x,
y = knots[0]._y + 2 * knots[1]._y,
f = 2,
n_1 = n - 1,
rx = [x],
ry = [y],
rf = [f],
px = [],
py = [];
for (var i = 1; i < n; i++) {
var internal = i < n_1,
a = internal ? 1 : asymmetric ? 1 : 2,
b = internal ? 4 : asymmetric ? 2 : 7,
u = internal ? 4 : asymmetric ? 3 : 8,
v = internal ? 2 : asymmetric ? 0 : 1,
m = a / f;
f = rf[i] = b - m;
x = rx[i] = u * knots[i]._x + v * knots[i + 1]._x - m * x;
y = ry[i] = u * knots[i]._y + v * knots[i + 1]._y - m * y;
}
px[n_1] = rx[n_1] / rf[n_1];
py[n_1] = ry[n_1] / rf[n_1];
for (var i = n - 2; i >= 0; i--) {
px[i] = (rx[i] - px[i + 1]) / rf[i];
py[i] = (ry[i] - py[i + 1]) / rf[i];
}
px[n] = (3 * knots[n]._x - px[n_1]) / 2;
py[n] = (3 * knots[n]._y - py[n_1]) / 2;
for (var i = paddingLeft, max = n - paddingRight, j = from;
i <= max; i++, j++) {
var segment = segments[j < 0 ? j + length : j],
pt = segment._point,
hx = px[i] - pt._x,
hy = py[i] - pt._y;
if (loop || i < max)
segment.setHandleOut(hx, hy);
if (loop || i > paddingLeft)
segment.setHandleIn(-hx, -hy);
}
} else {
for (var i = from; i <= to; i++) {
segments[i < 0 ? i + length : i].smooth(opts,
!loop && i === from, !loop && i === to);
}
}
}
}),
new function() {
@ -9040,12 +9040,6 @@ var CompoundPath = PathItem.extend({
return items;
},
reverse: function() {
var children = this._children;
for (var i = 0, l = children.length; i < l; i++)
children[i].reverse();
},
reduce: function reduce(options) {
var children = this._children;
for (var i = children.length - 1; i >= 0; i--) {
@ -9063,12 +9057,6 @@ var CompoundPath = PathItem.extend({
return reduce.base.call(this);
},
smooth: function(options) {
var children = this._children;
for (var i = 0, l = children.length; i < l; i++)
children[i].smooth(options);
},
isClockwise: function() {
var child = this.getFirstChild();
return child && child.isClockwise();
@ -9176,40 +9164,46 @@ new function() {
return children[children.length - 1];
}
var fields = {
moveTo: function() {
var current = getCurrentPath(this),
path = current && current.isEmpty() ? current
: new Path(Item.NO_INSERT);
if (path !== current)
this.addChild(path);
path.moveTo.apply(path, arguments);
},
return Base.each(['lineTo', 'cubicCurveTo', 'quadraticCurveTo', 'curveTo',
'arcTo', 'lineBy', 'cubicCurveBy', 'quadraticCurveBy', 'curveBy',
'arcBy'],
function(key) {
this[key] = function() {
var path = getCurrentPath(this, true);
path[key].apply(path, arguments);
};
}, {
moveTo: function() {
var current = getCurrentPath(this),
path = current && current.isEmpty() ? current
: new Path(Item.NO_INSERT);
if (path !== current)
this.addChild(path);
path.moveTo.apply(path, arguments);
},
moveBy: function() {
var current = getCurrentPath(this, true),
last = current && current.getLastSegment(),
point = Point.read(arguments);
this.moveTo(last ? point.add(last._point) : point);
},
moveBy: function() {
var current = getCurrentPath(this, true),
last = current && current.getLastSegment(),
point = Point.read(arguments);
this.moveTo(last ? point.add(last._point) : point);
},
closePath: function(join) {
getCurrentPath(this, true).closePath(join);
}
};
Base.each(['lineTo', 'cubicCurveTo', 'quadraticCurveTo', 'curveTo', 'arcTo',
'lineBy', 'cubicCurveBy', 'quadraticCurveBy', 'curveBy', 'arcBy'],
function(key) {
fields[key] = function() {
var path = getCurrentPath(this, true);
path[key].apply(path, arguments);
};
closePath: function(join) {
getCurrentPath(this, true).closePath(join);
}
}
);
return fields;
});
}, Base.each(['reverse', 'flatten', 'simplify', 'smooth'], function(key) {
this[key] = function(param) {
var children = this._children,
res;
for (var i = 0, l = children.length; i < l; i++) {
res = children[i][key](param) || res;
}
return res;
};
}, {}));
PathItem.inject(new function() {
var operators = {
@ -9972,63 +9966,61 @@ var PathIterator = Base.extend({
);
var PathFitter = Base.extend({
initialize: function(path, error) {
initialize: function(path) {
var points = this.points = [],
segments = path._segments,
prev;
for (var i = 0, l = segments.length; i < l; i++) {
var point = segments[i].point.clone();
closed = path._closed;
for (var i = 0, prev, l = segments.length; i < l; i++) {
var point = segments[i].point;
if (!prev || !prev.equals(point)) {
points.push(point);
prev = point;
points.push(prev = point.clone());
}
}
if (path._closed) {
this.closed = true;
if (closed) {
points.unshift(points[points.length - 1]);
points.push(points[1]);
}
this.error = error;
this.closed = closed;
},
fit: function() {
fit: function(error) {
var points = this.points,
length = points.length,
segments = this.segments = length > 0
? [new Segment(points[0])] : [];
if (length > 1)
this.fitCubic(0, length - 1,
points[1].subtract(points[0]).normalize(),
points[length - 2].subtract(points[length - 1]).normalize());
if (this.closed) {
segments.shift();
segments.pop();
segments = null;
if (length > 0) {
segments = [new Segment(points[0])];
if (length > 1) {
this.fitCubic(segments, error, 0, length - 1,
points[1].subtract(points[0]),
points[length - 2].subtract(points[length - 1]));
if (this.closed) {
segments.shift();
segments.pop();
}
}
}
return segments;
},
fitCubic: function(first, last, tan1, tan2) {
if (last - first == 1) {
var pt1 = this.points[first],
pt2 = this.points[last],
fitCubic: function(segments, error, first, last, tan1, tan2) {
var points = this.points;
if (last - first === 1) {
var pt1 = points[first],
pt2 = points[last],
dist = pt1.getDistance(pt2) / 3;
this.addCurve([pt1, pt1.add(tan1.normalize(dist)),
this.addCurve(segments, [pt1, pt1.add(tan1.normalize(dist)),
pt2.add(tan2.normalize(dist)), pt2]);
return;
}
var uPrime = this.chordLengthParameterize(first, last),
maxError = Math.max(this.error, this.error * this.error),
maxError = Math.max(error, error * error),
split,
parametersInOrder = true;
for (var i = 0; i <= 4; i++) {
var curve = this.generateBezier(first, last, uPrime, tan1, tan2);
var max = this.findMaxError(first, last, curve, uPrime);
if (max.error < this.error && parametersInOrder) {
this.addCurve(curve);
if (max.error < error && parametersInOrder) {
this.addCurve(segments, curve);
return;
}
split = max.index;
@ -10037,23 +10029,23 @@ var PathFitter = Base.extend({
parametersInOrder = this.reparameterize(first, last, uPrime, curve);
maxError = max.error;
}
var tanCenter = this.points[split - 1].subtract(this.points[split + 1])
.normalize();
this.fitCubic(first, split, tan1, tanCenter);
this.fitCubic(split, last, tanCenter.negate(), tan2);
var tanCenter = points[split - 1].subtract(points[split + 1]);
this.fitCubic(segments, error, first, split, tan1, tanCenter);
this.fitCubic(segments, error, split, last, tanCenter.negate(), tan2);
},
addCurve: function(curve) {
var prev = this.segments[this.segments.length - 1];
addCurve: function(segments, curve) {
var prev = segments[segments.length - 1];
prev.setHandleOut(curve[1].subtract(curve[0]));
this.segments.push(
new Segment(curve[3], curve[2].subtract(curve[3])));
segments.push(new Segment(curve[3], curve[2].subtract(curve[3])));
},
generateBezier: function(first, last, uPrime, tan1, tan2) {
var epsilon = 1e-12,
pt1 = this.points[first],
pt2 = this.points[last],
abs = Math.abs,
points = this.points,
pt1 = points[first],
pt2 = points[last],
C = [[0, 0], [0, 0]],
X = [0, 0];
@ -10067,7 +10059,7 @@ var PathFitter = Base.extend({
b3 = u * u * u,
a1 = tan1.normalize(b1),
a2 = tan2.normalize(b2),
tmp = this.points[first + i]
tmp = points[first + i]
.subtract(pt1.multiply(b0 + b1))
.subtract(pt2.multiply(b2 + b3));
C[0][0] += a1.dot(a1);
@ -10080,7 +10072,7 @@ var PathFitter = Base.extend({
var detC0C1 = C[0][0] * C[1][1] - C[1][0] * C[0][1],
alpha1, alpha2;
if (Math.abs(detC0C1) > epsilon) {
if (abs(detC0C1) > epsilon) {
var detC0X = C[0][0] * X[1] - C[1][0] * X[0],
detXC1 = X[0] * C[1][1] - X[1] * C[0][1];
alpha1 = detXC1 / detC0C1;
@ -10088,9 +10080,9 @@ var PathFitter = Base.extend({
} else {
var c0 = C[0][0] + C[0][1],
c1 = C[1][0] + C[1][1];
if (Math.abs(c0) > epsilon) {
if (abs(c0) > epsilon) {
alpha1 = alpha2 = X[0] / c0;
} else if (Math.abs(c1) > epsilon) {
} else if (abs(c1) > epsilon) {
alpha1 = alpha2 = X[1] / c1;
} else {
alpha1 = alpha2 = 0;
@ -10113,8 +10105,10 @@ var PathFitter = Base.extend({
}
}
return [pt1, pt1.add(handle1 || tan1.normalize(alpha1)),
pt2.add(handle2 || tan2.normalize(alpha2)), pt2];
return [pt1,
pt1.add(handle1 || tan1.normalize(alpha1)),
pt2.add(handle2 || tan2.normalize(alpha2)),
pt2];
},
reparameterize: function(first, last, u, curve) {

File diff suppressed because one or more lines are too long