diff --git a/src/load.js b/src/load.js index 325e68af..3fec896e 100644 --- a/src/load.js +++ b/src/load.js @@ -93,6 +93,7 @@ if (window.tests) { 'test/tests/Path.js', 'test/tests/Path_Shapes.js', 'test/tests/Path_Drawing_Commands.js', + 'test/tests/Path_Curves.js', 'test/tests/Path_Bounds.js', 'test/tests/Path_Length.js', 'test/tests/PathStyle.js', diff --git a/src/path/Curve.js b/src/path/Curve.js index 24f091d7..95d991b8 100644 --- a/src/path/Curve.js +++ b/src/path/Curve.js @@ -40,18 +40,6 @@ var Curve = this.Curve = Base.extend({ delete this._length; }, - _updateSegments: function() { - if (this._path) { - this._index2 = this._index1 + 1; - // A closing curve? - var segments = this._path._segments; - if (this._index2 >= segments.length) - this._index2 = 0; - this._segment1 = segments[this._index1]; - this._segment2 = segments[this._index2]; - } - }, - /** * The first anchor point of the curve. */ @@ -119,19 +107,18 @@ var Curve = this.Curve = Base.extend({ }, getIndex: function() { - return this._index1; + return this._segment1._index; }, getNext: function() { - // TODO: No need to call getCurves() here? var curves = this._path && this._path._curves; - return curves && (curves[this._index1 + 1] + return curves && (curves[this._segment1._index + 1] || this._path._closed && curves[0]) || null; }, getPrevious: function() { var curves = this._path && this._path._curves; - return curves && (curves[this._index1 - 1] + return curves && (curves[this._segment1._index - 1] || this._path._closed && curves[curves.length - 1]) || null; }, @@ -226,11 +213,11 @@ var Curve = this.Curve = Base.extend({ }, statics: { - create: function(path, index) { + create: function(path, segment1, segment2) { var curve = new Curve(Curve.dont); curve._path = path; - curve._index1 = index; - curve._updateSegments(); + curve._segment1 = segment1; + curve._segment2 = segment2; return curve; } } diff --git a/src/path/Path.js b/src/path/Path.js index 9d40f6ea..feb9f749 100644 --- a/src/path/Path.js +++ b/src/path/Path.js @@ -69,13 +69,16 @@ var Path = this.Path = PathItem.extend({ */ getCurves: function() { if (!this._curves) { - var length = this._segments.length; + var segments = this._segments, + length = segments.length; // Reduce length by one if it's an open path: if (!this._closed && length > 0) length--; this._curves = new Array(length); for (var i = 0; i < length; i++) - this._curves[i] = Curve.create(this, i); + this._curves[i] = Curve.create(this, segments[i], + // Use first segment for segment2 of closing curve + segments[i + 1] || segments[0]); } return this._curves; }, @@ -107,7 +110,8 @@ var Path = this.Path = PathItem.extend({ this._curves.length = length; // If we were closing this path, we need to add a new curve now if (closed) - this._curves[i = length - 1] = Curve.create(this, i); + this._curves[i = length - 1] = Curve.create(this, + this._segments[i], this._segments[0]); } this._changed(); } @@ -135,42 +139,37 @@ var Path = this.Path = PathItem.extend({ */ // TODO: Add support for adding multiple segments at once _add: function(segment, index) { + // Local short-cuts: + var segments = this._segments, + curves = this._curves; // If this segment belongs to another path already, clone it before // adding. if (segment._path) segment = new Segment(segment); if (index === undefined) { // Insert at the end - index = this._segments.push(segment) - 1; + index = segments.push(segment) - 1; } else { // Insert somewhere else - this._segments.splice(index, 0, segment); + segments.splice(index, 0, segment); // Adjust the indices of the segments above. - for (var i = index + 1, l = this._segments.length; i < l; i++) - this._segments[i]._index = i; + for (var i = index + 1, l = segments.length; i < l; i++) + segments[i]._index = i; } segment._path = this; segment._index = index; // Keep the curves list in sync all the time in case it as requested // already. We need to step one index down from the inserted segment to // get its curve: - if (this._curves && --index >= 0) { + if (curves && --index >= 0) { // Insert a new curve as well and update the curves above - this._curves.splice(index, 0, Curve.create(this, index)); - // Adjust indices now for the curves above this one. - for (var i = index + 1, l = this._curves.length; i < l; i++) { - var curve = this._curves[i]; - curve._index1 = i; - // This is wrong for the last closing curve but it will be - // corrected further down. - curve._index2 = i + 1; - } - // The curve that comes right after will has changed beyond a simple - // shift in indices, so it needs an update: - this._curves[index + 1]._updateSegments(); - // If this is a closed path, also update the closing curve - if (this._closed) - this._curves[l - 1]._updateSegments(); + curves.splice(index, 0, Curve.create(this, segments[index], + segments[++index])); + // Adjust segment1 now for the curves above the inserted one + // (note the ++index in the statement above)/ + var curve = curves[index]; + if (curve) + curve._segment1 = segments[index]; } this._changed(); return segment; @@ -198,21 +197,40 @@ var Path = this.Path = PathItem.extend({ removeSegments: function(from, to) { from = from || 0; to = Base.pick(to, this._segments.length - 1); - var amount = to - from, - segments = this._segments.splice(from, amount); - if (segments.length == amount) { - // TODO: Keep _curves in sync - for (var i = 0; i < amount; i++) { - var segment = segments[0]; - if (segment._selectionState) { - this._selectedSegmentCount--; - segment._selectionState = 0; - } + var segments = this._segments, + curves = this._curves, + last = to >= segments.length, + removed = segments.splice(from, to - from), + amount = removed.length; + if (!amount) + return removed; + // Update selection state accordingly + for (var i = 0; i < amount; i++) { + var segment = removed[i]; + if (segment._selectionState) { + this._selectedSegmentCount--; + segment._selectionState = 0; } - this._changed(); - return segments; } - return null; + // Adjust the indices of the segments above. + for (var i = from, l = segments.length; i < l; i++) + segments[i]._index = i; + // Keep curves in sync + if (curves) { + curves.splice(from, amount); + // Adjust segments for the curves before and after the removed ones + var curve; + if (curve = curves[from - 1]) + curve._segment2 = segments[from]; + if (curve = curves[from]) + curve._segment1 = segments[from]; + // If the last segment of a closing path was removed, we need to + // readjust the last curve of the list now. + if (last && this._closed && (curve = curves[curves.length - 1])) + curve._segment2 = segments[0]; + } + this._changed(); + return removed; }, isSelected: function() { diff --git a/test/tests/Path_Curves.js b/test/tests/Path_Curves.js new file mode 100644 index 00000000..63afff08 --- /dev/null +++ b/test/tests/Path_Curves.js @@ -0,0 +1,49 @@ +module('Path Curves'); + +test('path.curves Synchronisation', function() { + + var doc = new Document(); + var path = new Path(); + + path.add(new Point(0, 100)); + equals(path.curves.toString(), ''); + + path.add(new Point(100, 100)); + equals(path.curves.toString(), + '{ point1: { x: 0, y: 100 }, point2: { x: 100, y: 100 } }', + '2 x path.add()'); + + path.insert(1, { point: [50, 0], handleIn: [-25, 0], handleOut: [25, 0] }); + equals(path.curves.toString(), + '{ point1: { x: 0, y: 100 }, handle2: { x: -25, y: 0 }, point2: { x: 50, y: 0 } },{ point1: { x: 50, y: 0 }, handle1: { x: 25, y: 0 }, point2: { x: 100, y: 100 } }', + 'path.insert()' + ); + + path.closed = true; + equals(path.curves.toString(), + '{ point1: { x: 0, y: 100 }, handle2: { x: -25, y: 0 }, point2: { x: 50, y: 0 } },{ point1: { x: 50, y: 0 }, handle1: { x: 25, y: 0 }, point2: { x: 100, y: 100 } },{ point1: { x: 100, y: 100 }, point2: { x: 0, y: 100 } }', + 'path.closed = true'); + + path.removeSegments(2, 3); + equals(path.curves.toString(), + '{ point1: { x: 0, y: 100 }, handle2: { x: -25, y: 0 }, point2: { x: 50, y: 0 } },{ point1: { x: 50, y: 0 }, handle1: { x: 25, y: 0 }, point2: { x: 0, y: 100 } }', + 'path.removeSegments(2, 3)'); + + equals(path.segments.toString(), + '{ point: { x: 0, y: 100 } },{ point: { x: 50, y: 0 }, handleIn: { x: -25, y: 0 }, handleOut: { x: 25, y: 0 } }', + 'segments'); + + path.add(new Point(100, 100)); + equals(path.curves.toString(), + '{ point1: { x: 0, y: 100 }, handle2: { x: -25, y: 0 }, point2: { x: 50, y: 0 } },{ point1: { x: 50, y: 0 }, handle1: { x: 25, y: 0 }, point2: { x: 100, y: 100 } },{ point1: { x: 100, y: 100 }, point2: { x: 0, y: 100 } }', + 'path.add()'); + + path.removeSegments(1, 2); + equals(path.curves.toString(), + '{ point1: { x: 0, y: 100 }, point2: { x: 100, y: 100 } },{ point1: { x: 100, y: 100 }, point2: { x: 0, y: 100 } }', + 'path.removeSegments(1, 2)'); + + equals(path.segments.toString(), + '{ point: { x: 0, y: 100 } },{ point: { x: 100, y: 100 } }', + 'segments'); +});