/* * 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)); } } });