paper.js/src/path/PathFlattener.js

123 lines
3.4 KiB
JavaScript
Raw Normal View History

/*
* Paper.js
*
* This file is part of Paper.js, a JavaScript Vector Graphics Library,
* based on Scriptographer.org and designed to be largely API compatible.
* http://paperjs.org/
* http://scriptographer.org/
*
* Distributed under the MIT license. See LICENSE file for details.
*
* Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
* http://lehni.org/ & http://jonathanpuckey.com/
*
* All rights reserved.
*/
var PathFlattener = Base.extend({
initialize: function(path) {
this.parts = [];
this.curves = [];
this.length = 0;
// Keep a current index from the part where we last where in
// getParameter(), to optimise for iterator-like usage of the flattener.
this.index = 0;
var segments = path._segments,
segment1 = segments[0],
segment2,
that = this;
function addCurve(segment1, segment2) {
var curve = Curve.getCurveValues(segment1, segment2);
that.curves.push(curve);
that._computeParts(curve, segment1._index, 0, 1);
}
for (var i = 1, l = segments.length; i < l; i++) {
segment2 = segments[i];
addCurve(segment1, segment2);
segment1 = segment2;
}
if (path._closed)
addCurve(segment2, segments[0]);
},
_computeParts: function(curve, index, minT, maxT) {
// Check if the t-span is big enough for subdivision.
// We're not subdividing more than 32 times...
if ((maxT - minT) > 1 / 32
&& !Curve.isSufficientlyFlat.apply(Curve, curve)) {
var curves = Curve.subdivide.apply(Curve, curve);
var halfT = (minT + maxT) / 2;
// Recursively subdive and compute parts again.
this._computeParts(curves[0], index, minT, halfT);
this._computeParts(curves[1], index, halfT, maxT);
} else {
// Calculate distance between p1 and p2
var x = curve[6] - curve[0],
y = curve[7] - curve[1],
dist = Math.sqrt(x * x + y * y);
if (dist > Numerical.TOLERANCE) {
this.length += dist;
this.parts.push({
length: this.length,
value: maxT,
index: index
});
}
}
},
getParameter: function(length) {
// Make sure we're not beyond the requested length already. Search the
// start position backwards from where to then process the loop below.
var i, j = this.index;
for (;;) {
i = j;
if (j == 0 || this.parts[--j].length < length)
break;
}
// Find the part that succeeds the given length, then interpolate
// with the previous part
for (var l = this.parts.length; i < l; i++) {
var part = this.parts[i];
if (part.length >= length) {
this.index = i;
var prev = this.parts[i - 1];
// Make sure we only use the previous parameter value if its
// for the same curve, by checking index. Use 0 otherwise.
var prevVal = prev && prev.index == part.index ? prev.value : 0,
prevLen = prev ? prev.length : 0;
return {
// Interpolate
value: prevVal + (part.value - prevVal)
* (length - prevLen) / (part.length - prevLen),
index: part.index
};
}
}
// Return last one
var part = this.parts[this.parts.length - 1];
return {
value: 1,
index: part.index
};
},
drawPart: function(ctx, from, to, moveTo) {
from = this.getParameter(from);
to = this.getParameter(to);
for (var i = from.index; i <= to.index; i++) {
var curve = Curve.getPart.apply(Curve, this.curves[i].concat(
i == from.index ? from.value : 0,
i == to.index ? to.value : 1));
if (moveTo) {
ctx.moveTo(curve[0], curve[1]);
moveTo = false;
}
ctx.bezierCurveTo.apply(ctx, curve.slice(2));
}
}
});