From 499c42102185fed72920f311faca263e627bfb91 Mon Sep 17 00:00:00 2001 From: Jonathan Puckey Date: Mon, 11 Apr 2011 23:30:08 +0200 Subject: [PATCH] Implement Path#join(path) and add tests for it. --- src/path/Path.js | 194 ++++++++++++++++++++++++++++----------------- test/tests/Path.js | 55 +++++++++++++ 2 files changed, 175 insertions(+), 74 deletions(-) diff --git a/src/path/Path.js b/src/path/Path.js index 88a1c9fd..f5adc56d 100644 --- a/src/path/Path.js +++ b/src/path/Path.js @@ -108,6 +108,123 @@ var Path = this.Path = PathItem.extend({ var segment = Segment.read(arguments, 1); return segment ? this._add(segment, index) : null; }, + + /** + * Reverses the segments of the path. + */ + reverse: function() { + this._segments.reverse(); + }, + + join: function(path) { + if (path != null) { + var segments = path.segments; + var last1 = this.getLastSegment(); + var last2 = path.getLastSegment(); + if (last1.getPoint().equals(last2.getPoint())) + path.reverse(); + var first2 = path.getFirstSegment(); + if (last1.getPoint().equals(first2.getPoint())) { + last1.setHandleOut(first2.getHandleOut()); + for (var i = 1, l = segments.length; i < l; i++) + this._add(segments[i]); + } else { + var first1 = this.getFirstSegment(); + if (first1.getPoint().equals(first2.getPoint())) + path.reverse(); + if (first1.getPoint().equals(last2.getPoint())) { + first1.setHandleIn(last2.getHandleIn()); + // Prepend all segments from path except last one + for (var i = 0, l = segments.length - 1; i < l; i++) + this._add(segments[i], 0); + } else { + for (var i = 0, l = segments.length; i < l; i++) + this._add(segments[i]); + } + } + path.remove(); + // Close if they touch in both places + var first1 = this.getFirstSegment(); + last1 = this.getLastSegment(); + if (last1.getPoint().equals(first1.getPoint())) { + first1.setHandleIn(last1.getHandleIn()); + last1.remove(); + this.closed = true; + } + return true; + } + return false; + }, + + // todo: getLocation(point, precision) + getLocation: function(length) { + var curves = this.getCurves(); + var currentLength = 0; + for (var i = 0, l = curves.length; i < l; i++) { + var startLength = currentLength; + var curve = curves[i]; + currentLength += curve.getLength(); + if(currentLength >= length) { + // found the segment within which the length lies + var t = curve.getParameter(length - startLength); + return new CurveLocation(curve, t); + } + } + // Todo: is this the case for paper.js too? + // it may be that through impreciseness of getLength, that the end + // of the curves was missed: + if (length <= this.getLength()) { + var curve = curves[curves.length - 1]; + return new CurveLocation(curve, 1); + } + return null; + }, + + getLength: function(/* location */) { + var location; + if(arguments.length) + location = arguments[0]; + var curves = this.getCurves(); + var index = location + ? location.getIndex() + : curves.length; + if (index != -1) { + var length = 0; + for (var i = 0; i < index; i++) + length += curves[i].getLength(); + var curve; + if (location) { + // Clone the curve as we're going to divide it to get the + // length. Without cloning it, this would modify the path. + curve = curves[index].clone(); + curve.divide(location.getParameter()); + length += curve.getLength(); + } + return length; + } + return -1; + }, + + /** + * Returns the tangent to the path at the given length as a vector + * point. + */ + getTangent: function(length) { + var loc = this.getLocation(length); + return loc + ? loc.getCurve().getTangent(loc.getParameter()) + : null; + }, + + /** + * Returns the normal to the path at the given length as a vector point. + */ + getNormal: function(length) { + var loc = this.getLocation(length); + return loc + ? loc.getCurve().getNormal(loc.getParameter()) + : null; + }, draw: function(ctx, param) { if (!param.compound) @@ -178,7 +295,7 @@ var Path = this.Path = PathItem.extend({ var func = Array.prototype[name]; this[name] = function() { return func.apply(this, arguments); - } + }; }, {}); return { @@ -207,7 +324,7 @@ var Path = this.Path = PathItem.extend({ this._add(Segment.read(segments, i, 1)); } } - } + }; }, new function() { // Inject methods that require scoped privates /** @@ -245,77 +362,6 @@ var Path = this.Path = PathItem.extend({ return { beans: true, - - // todo: getLocation(point, precision) - getLocation: function(length) { - var curves = this.getCurves(); - var currentLength = 0; - for (var i = 0, l = curves.length; i < l; i++) { - var startLength = currentLength; - var curve = curves[i]; - currentLength += curve.getLength(); - if(currentLength >= length) { - // found the segment within which the length lies - var t = curve.getParameter(length - startLength); - console.log(t, length - startLength, '??') - return new CurveLocation(curve, t); - } - } - // Todo: is this the case for paper.js too? - // it may be that through impreciseness of getLength, that the end - // of the curves was missed: - if (length <= this.getLength()) { - var curve = curves[curves.length - 1]; - return new CurveLocation(curve, 1); - } - return null; - }, - - getLength: function(/* location */) { - var location; - if(arguments.length) - location = arguments[0]; - var curves = this.getCurves(); - var index = location - ? location.getIndex() - : curves.length; - if (index != -1) { - var length = 0; - for (var i = 0; i < index; i++) - length += curves[i].getLength(); - var curve; - if (location) { - // Clone the curve as we're going to divide it to get the - // length. Without cloning it, this would modify the path. - curve = curves[index].clone(); - curve.divide(location.getParameter()); - length += curve.getLength(); - } - return length; - } - return -1; - }, - - /** - * Returns the tangent to the path at the given length as a vector - * point. - */ - getTangent: function(length) { - var loc = this.getLocation(length); - return loc - ? loc.getCurve().getTangent(loc.getParameter()) - : null; - }, - - /** - * Returns the normal to the path at the given length as a vector point. - */ - getNormal: function(length) { - var loc = this.getLocation(length); - return loc - ? loc.getCurve().getNormal(loc.getParameter()) - : null; - }, smooth: function() { var segments = this._segments; @@ -424,7 +470,7 @@ var Path = this.Path = PathItem.extend({ } } } - } + }; }, new function() { // PostScript-style drawing commands function getCurrentSegment(that) { diff --git a/test/tests/Path.js b/test/tests/Path.js index 2ef6c395..a4ebafaa 100644 --- a/test/tests/Path.js +++ b/test/tests/Path.js @@ -1 +1,56 @@ module('Path'); + +test('path.join(path)', function() { + var doc = new Document(); + var path = new Path(); + path.add(0, 0); + path.add(10, 0); + + var path2 = new Path(); + path2.add(10, 0); + path2.add(20, 10); + + path.join(path2); + compareSegmentLists(path.segments, [new Segment(new Point(0, 0)), + new Segment(new Point(10, 0)), new Segment(new Point(20, 10))]); + equals(doc.activeLayer.children.length, 1); + + var path = new Path(); + path.add(0, 0); + path.add(10, 0); + + var path2 = new Path(); + path2.add(20, 10); + path2.add(10, 0); + path.join(path2); + compareSegmentLists(path.segments, [new Segment(new Point(0, 0)), + new Segment(new Point(10, 0)), new Segment(new Point(20, 10))]); + + var path = new Path(); + path.add(0, 0); + path.add(10, 0); + + var path2 = new Path(); + path2.add(30, 10); + path2.add(40, 0); + path.join(path2); + compareSegmentLists(path.segments, [new Segment(new Point(0, 0)), + new Segment(new Point(10, 0)), new Segment(new Point(30, 10)), + new Segment(new Point(40, 0))]); + + var path = new Path(); + path.add(0, 0); + path.add(10, 0); + path.add(20, 10); + + var path2 = new Path(); + path2.add(0, 0); + path2.add(10, 5); + path2.add(20, 10); + + path.join(path2); + compareSegmentLists(path.segments, [new Segment(new Point(0, 0)), + new Segment(new Point(10, 0)), new Segment(new Point(20, 10)), + new Segment(new Point(10, 5))]); + equals(path.closed, true); +}); \ No newline at end of file