2011-06-04 13:25:41 -04:00
|
|
|
/*
|
|
|
|
* Paper.js
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-06-04 13:25:41 -04:00
|
|
|
* 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/
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-06-04 13:25:41 -04:00
|
|
|
* Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
|
|
|
|
* http://lehni.org/ & http://jonathanpuckey.com/
|
2011-06-30 06:01:51 -04:00
|
|
|
*
|
2011-07-01 06:17:45 -04:00
|
|
|
* Distributed under the MIT license. See LICENSE file for details.
|
|
|
|
*
|
2011-06-04 13:25:41 -04:00
|
|
|
* All rights reserved.
|
|
|
|
*/
|
|
|
|
|
|
|
|
var PathFlattener = Base.extend({
|
|
|
|
initialize: function(path) {
|
2011-06-05 07:40:07 -04:00
|
|
|
this.curves = []; // The curve values as returned by getValues()
|
2011-06-04 13:35:00 -04:00
|
|
|
this.parts = []; // The calculated, subdivided parts of the path
|
|
|
|
this.length = 0; // The total length of the path
|
2011-06-04 13:25:41 -04:00
|
|
|
// 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;
|
|
|
|
|
2011-06-04 13:35:00 -04:00
|
|
|
// Instead of relying on path.curves, we only use segments here and
|
|
|
|
// get the curve values from them.
|
2011-06-30 06:01:51 -04:00
|
|
|
|
2011-06-04 13:35:00 -04:00
|
|
|
// Now walk through all curves and compute the parts for each of them,
|
|
|
|
// by recursively calling _computeParts().
|
2011-06-04 13:25:41 -04:00
|
|
|
var segments = path._segments,
|
|
|
|
segment1 = segments[0],
|
|
|
|
segment2,
|
|
|
|
that = this;
|
|
|
|
|
|
|
|
function addCurve(segment1, segment2) {
|
2011-06-05 07:40:07 -04:00
|
|
|
var curve = Curve.getValues(segment1, segment2);
|
2011-06-04 13:25:41 -04:00
|
|
|
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...
|
2011-07-05 07:17:07 -04:00
|
|
|
if ((maxT - minT) > 1 / 32 && !Curve.isFlatEnough.apply(Curve, curve)) {
|
2011-06-04 13:25:41 -04:00
|
|
|
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({
|
2011-06-05 06:34:40 -04:00
|
|
|
offset: this.length,
|
2011-06-04 13:25:41 -04:00
|
|
|
value: maxT,
|
|
|
|
index: index
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2011-06-05 06:34:40 -04:00
|
|
|
getParameter: function(offset) {
|
|
|
|
// Make sure we're not beyond the requested offset already. Search the
|
2011-06-04 13:25:41 -04:00
|
|
|
// start position backwards from where to then process the loop below.
|
|
|
|
var i, j = this.index;
|
|
|
|
for (;;) {
|
|
|
|
i = j;
|
2011-06-05 06:34:40 -04:00
|
|
|
if (j == 0 || this.parts[--j].offset < offset)
|
2011-06-04 13:25:41 -04:00
|
|
|
break;
|
|
|
|
}
|
2011-06-05 06:34:40 -04:00
|
|
|
// Find the part that succeeds the given offset, then interpolate
|
2011-06-04 13:25:41 -04:00
|
|
|
// with the previous part
|
|
|
|
for (var l = this.parts.length; i < l; i++) {
|
|
|
|
var part = this.parts[i];
|
2011-06-05 06:34:40 -04:00
|
|
|
if (part.offset >= offset) {
|
2011-06-04 14:25:50 -04:00
|
|
|
// Found the right part, remember current position
|
2011-06-04 13:25:41 -04:00
|
|
|
this.index = i;
|
2011-06-04 14:25:50 -04:00
|
|
|
// Now get the previous part so we can linearly interpolate
|
|
|
|
// the curve parameter
|
2011-06-04 13:25:41 -04:00
|
|
|
var prev = this.parts[i - 1];
|
2011-06-30 06:01:51 -04:00
|
|
|
// Make sure we only use the previous parameter value if its
|
2011-06-04 13:25:41 -04:00
|
|
|
// for the same curve, by checking index. Use 0 otherwise.
|
|
|
|
var prevVal = prev && prev.index == part.index ? prev.value : 0,
|
2011-06-05 06:34:40 -04:00
|
|
|
prevLen = prev ? prev.offset : 0;
|
2011-06-04 13:25:41 -04:00
|
|
|
return {
|
|
|
|
// Interpolate
|
|
|
|
value: prevVal + (part.value - prevVal)
|
2011-06-05 06:34:40 -04:00
|
|
|
* (offset - prevLen) / (part.offset - prevLen),
|
2011-06-04 13:25:41 -04:00
|
|
|
index: part.index
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Return last one
|
|
|
|
var part = this.parts[this.parts.length - 1];
|
|
|
|
return {
|
|
|
|
value: 1,
|
|
|
|
index: part.index
|
|
|
|
};
|
|
|
|
},
|
|
|
|
|
2011-06-05 08:20:20 -04:00
|
|
|
evaluate: function(offset, type) {
|
|
|
|
var param = this.getParameter(offset);
|
|
|
|
return Curve.evaluate.apply(Curve,
|
|
|
|
this.curves[param.index].concat([param.value, type]));
|
|
|
|
},
|
|
|
|
|
2011-06-04 14:25:50 -04:00
|
|
|
drawPart: function(ctx, from, to) {
|
2011-06-04 13:25:41 -04:00
|
|
|
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));
|
2011-06-04 16:46:48 -04:00
|
|
|
if (i == from.index)
|
2011-06-04 13:25:41 -04:00
|
|
|
ctx.moveTo(curve[0], curve[1]);
|
|
|
|
ctx.bezierCurveTo.apply(ctx, curve.slice(2));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|