Add support for insertion of multiple segments at once to Path#_add(), #add() and #insert(), and change code that relies on these methods.

This commit is contained in:
Jürg Lehni 2011-05-04 18:42:40 +01:00
parent bdbbca487f
commit a5099fd51d
4 changed files with 116 additions and 129 deletions

View file

@ -37,11 +37,12 @@ Path.inject({ statics: new function() {
rect = Rectangle.read(arguments); rect = Rectangle.read(arguments);
var path = new Path(), var path = new Path(),
corners = ['getBottomLeft', 'getTopLeft', 'getTopRight', corners = ['getBottomLeft', 'getTopLeft', 'getTopRight',
'getBottomRight']; 'getBottomRight'],
for (var i = 0; i < 4; i++) { segments = new Array(4);
path.add(rect[corners[i]]()); for (var i = 0; i < 4; i++)
} segments[i] = new Segment(rect[corners[i]]());
path.setClosed(true); path._add(segments);
path._closed = true;
return path; return path;
}, },
@ -56,25 +57,24 @@ Path.inject({ statics: new function() {
size = Size.min(size, rect.getSize().divide(2)); size = Size.min(size, rect.getSize().divide(2));
var path = new Path(), var path = new Path(),
uSize = size.multiply(kappa * 2), uSize = size.multiply(kappa * 2),
bl = rect.getBottomLeft(), bl = rect.getBottomLeft(),
tl = rect.getTopLeft(), tl = rect.getTopLeft(),
tr = rect.getTopRight(), tr = rect.getTopRight(),
br = rect.getBottomRight(); br = rect.getBottomRight();
path._add([
new Segment(bl.add(size.width, 0), null, [-uSize.width, 0]),
new Segment(bl.subtract(0, size.height), [0, uSize.height], null),
path.add(bl.add(size.width, 0), null, [-uSize.width, 0]); new Segment(tl.add(0, size.height), null, [0, -uSize.height]),
path.add(bl.subtract(0, size.height), [0, uSize.height], null); new Segment(tl.add(size.width, 0), [-uSize.width, 0], null),
path.add(tl.add(0, size.height), null, [0, -uSize.height]); new Segment(tr.subtract(size.width, 0), null, [uSize.width, 0]),
path.add(tl.add(size.width, 0), [-uSize.width, 0], null); new Segment(tr.add(0, size.height), [0, -uSize.height], null),
path.add(tr.subtract(size.width, 0), null, [uSize.width, 0]); new Segment(br.subtract(0, size.height), null, [0, uSize.height]),
path.add(tr.add(0, size.height), [0, -uSize.height], null); new Segment(br.subtract(size.width, 0), [uSize.width, 0], null)
]);
path.add(br.subtract(0, size.height), null, [0, uSize.height]); path._closed = true;
path.add(br.subtract(size.width, 0), [uSize.width, 0], null);
path.setClosed(true);
return path; return path;
}, },
@ -82,16 +82,18 @@ Path.inject({ statics: new function() {
rect = Rectangle.read(arguments); rect = Rectangle.read(arguments);
var path = new Path(), var path = new Path(),
topLeft = rect.getTopLeft(), topLeft = rect.getTopLeft(),
size = new Size(rect.width, rect.height); size = new Size(rect.width, rect.height),
segments = new Array(4);
for (var i = 0; i < 4; i++) { for (var i = 0; i < 4; i++) {
var segment = ovalSegments[i]; var segment = ovalSegments[i];
path._add(new Segment( segments[i] = new Segment(
segment._point.multiply(size).add(topLeft), segment._point.multiply(size).add(topLeft),
segment._handleIn.multiply(size), segment._handleIn.multiply(size),
segment._handleOut.multiply(size) segment._handleOut.multiply(size)
)); );
} }
path.setClosed(true); path._add(segments);
path._closed = true;
return path; return path;
}, },
@ -116,29 +118,33 @@ Path.inject({ statics: new function() {
RegularPolygon: function(center, numSides, radius) { RegularPolygon: function(center, numSides, radius) {
center = Point.read(arguments, 0, 1); center = Point.read(arguments, 0, 1);
var path = new Path(), var path = new Path(),
step = 360 / numSides,
three = !(numSides % 3), three = !(numSides % 3),
vector = new Point(0, three ? -radius : radius), vector = new Point(0, three ? -radius : radius),
offset = three ? -1 : 0.5; offset = three ? -1 : 0.5,
segments = new Array(numSides);
for (var i = 0; i < numSides; i++) { for (var i = 0; i < numSides; i++) {
var angle = (360 / numSides) * (i + offset); segments[i] = new Segment(center.add(
path.add(center.add(vector.rotate(angle))); vector.rotate((i + offset) * step)));
} }
path.setClosed(true); path._add(segments);
path._closed = true;
return path; return path;
}, },
Star: function(center, numPoints, radius1, radius2) { Star: function(center, numPoints, radius1, radius2) {
center = Point.read(arguments, 0, 1); center = Point.read(arguments, 0, 1);
numPoints *= 2; numPoints *= 2;
var angle = 360 / numPoints, var path = new Path(),
path = new Path(); step = 360 / numPoints,
vector = new Point(0, -1),
segments = new Array(numPoints);
for (var i = 0; i < numPoints; i++) { for (var i = 0; i < numPoints; i++) {
path.add(center.add({ segments[i] = new Segment(center.add(
angle: -90 + angle * i, vector.rotate(step * i).multiply(i % 2 ? radius2 : radius1)));
length: i % 2 ? radius2 : radius1
}));
} }
path.setClosed(true); path._add(segments);
path._closed = true;
return path; return path;
} }
}; };

View file

@ -52,8 +52,7 @@ var Path = this.Path = PathItem.extend({
if (this._curves) if (this._curves)
this._curves = null; this._curves = null;
} }
for (var i = 0, l = segments.length; i < l; i++) this._add(Segment.readAll(segments));
this._add(Segment.read(segments, i, 1));
}, },
getFirstSegment: function() { getFirstSegment: function() {
@ -137,60 +136,63 @@ var Path = this.Path = PathItem.extend({
* If a curves list was requested, it will kept in sync with the segments * If a curves list was requested, it will kept in sync with the segments
* list automatically. * list automatically.
*/ */
// TODO: Add support for adding multiple segments at once _add: function(segs, index) {
_add: function(segment, index) {
// Local short-cuts: // Local short-cuts:
var segments = this._segments, var segments = this._segments,
curves = this._curves; curves = this._curves,
// If this segment belongs to another path already, clone it before amount = segs.length,
// adding. append = index === undefined,
if (segment._path) index = append ? segments.length : index;
segment = new Segment(segment); for (var i = 0; i < amount; i++) {
if (index === undefined) { var segment = segs[i];
// Insert at the end // If the segments belong to another path already, clone them before
index = segments.push(segment) - 1; // adding:
if (segment._path)
segment = segs[i] = new Segment(segment);
// Set _path and _index references for these new segments now
segment._path = this;
segment._index = index + i;
}
if (append) {
// Append them all at the end by using push
segments.push.apply(segments, segs);
} else { } else {
// Insert somewhere else // Insert somewhere else
segments.splice(index, 0, segment); segments.splice.apply(segments, [index, 0].concat(segs));
// Adjust the indices of the segments above. // Adjust the indices of the segments above.
for (var i = index + 1, l = segments.length; i < l; i++) for (var i = index + amount, l = segments.length; i < l; i++)
segments[i]._index = 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 // 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 // already. We need to step one index down from the inserted segment to
// get its curve: // get its curve:
if (curves && --index >= 0) { if (curves && --index >= 0) {
// Insert a new curve as well and update the curves above // Insert a new curve as well and update the curves above
curves.splice(index, 0, Curve.create(this, segments[index], curves.splice(index, 0, Curve.create(this, segments[index],
segments[++index])); segments[index + 1]));
// Adjust segment1 now for the curves above the inserted one // Adjust segment1 now for the curves above the inserted one
// (note the ++index in the statement above)/ var curve = curves[index + amount];
var curve = curves[index];
if (curve) if (curve)
curve._segment1 = segments[index]; curve._segment1 = segments[index + amount];
} }
this._changed(); this._changed();
return segment; return segs;
}, },
// TODO: Add support for adding multiple segments at once // TODO: Port back support for adding multiple segments at once to Sg
add: function(segment) { add: function(segment1 /*, segment2, ... */) {
segment = Segment.read(arguments); return this._add(Segment.readAll(arguments));
return segment ? this._add(segment) : null;
}, },
// TODO: Add support for adding multiple segments at once // TODO: Port back support for adding multiple segments at once to Sg
insert: function(index, segment) { insert: function(index, segment1 /*, segment2, ... */) {
segment = Segment.read(arguments, 1); return this._add(Segment.readAll(arguments, 1), index);
return segment ? this._add(segment, index) : null;
}, },
// TODO: Port back to Sg // TODO: Port back to Sg
removeSegment: function(index) { removeSegment: function(index) {
var segments = this.removeSegments(index, index + 1); var segments = this.removeSegments(index, index + 1);
return segments ? segments[0] : null; return segments[0] || null;
}, },
// TODO: Port back to Sg // TODO: Port back to Sg
@ -287,8 +289,7 @@ var Path = this.Path = PathItem.extend({
var first2 = path.getFirstSegment(); var first2 = path.getFirstSegment();
if (last1._point.equals(first2._point)) { if (last1._point.equals(first2._point)) {
last1.setHandleOut(first2._handleOut); last1.setHandleOut(first2._handleOut);
for (var i = 1, l = segments.length; i < l; i++) this._add(segments.slice(1));
this._add(segments[i]);
} else { } else {
var first1 = this.getFirstSegment(); var first1 = this.getFirstSegment();
if (first1._point.equals(first2._point)) if (first1._point.equals(first2._point))
@ -296,11 +297,10 @@ var Path = this.Path = PathItem.extend({
if (first1._point.equals(last2._point)) { if (first1._point.equals(last2._point)) {
first1.setHandleIn(last2._handleIn); first1.setHandleIn(last2._handleIn);
// Prepend all segments from path except last one // Prepend all segments from path except last one
for (var i = 0, l = segments.length - 1; i < l; i++) // TODO: Test if -1 (== all) or -2 (as described by comment)
this._add(segments[i], 0); this._add(segments.slice(0, segments.length - 2), 0);
} else { } else {
for (var i = 0, l = segments.length; i < l; i++) this._add(segments.slice(0));
this._add(segments[i]);
} }
} }
path.remove(); path.remove();
@ -679,12 +679,12 @@ var Path = this.Path = PathItem.extend({
// Let's not be picky about calling moveTo() when not at the // Let's not be picky about calling moveTo() when not at the
// beginning of a path, just bail out: // beginning of a path, just bail out:
if (!this._segments.length) if (!this._segments.length)
this._add(new Segment(Point.read(arguments))); this._add([new Segment(Point.read(arguments))]);
}, },
lineTo: function() { lineTo: function() {
// Let's not be picky about calling moveTo() first: // Let's not be picky about calling moveTo() first:
this._add(new Segment(Point.read(arguments))); this._add([new Segment(Point.read(arguments))]);
}, },
/** /**
@ -700,7 +700,7 @@ var Path = this.Path = PathItem.extend({
// Convert to relative values: // Convert to relative values:
current.setHandleOut(handle1.subtract(current._point)); current.setHandleOut(handle1.subtract(current._point));
// And add the new segment, with handleIn set to c2 // And add the new segment, with handleIn set to c2
this._add(new Segment(to, handle2.subtract(to), new Point())); this._add([new Segment(to, handle2.subtract(to), new Point())]);
}, },
/** /**
@ -807,6 +807,7 @@ var Path = this.Path = PathItem.extend({
var halfInc = inc / 2, var halfInc = inc / 2,
z = 4 / 3 * Math.sin(halfInc) / (1 + Math.cos(halfInc)); z = 4 / 3 * Math.sin(halfInc) / (1 + Math.cos(halfInc));
var segments = [];
for (var i = 0; i <= arcSegs; i++) { for (var i = 0; i <= arcSegs; i++) {
var relx = Math.cos(angle), var relx = Math.cos(angle),
rely = Math.sin(angle), rely = Math.sin(angle),
@ -829,10 +830,12 @@ var Path = this.Path = PathItem.extend({
var handleIn = new Point( var handleIn = new Point(
centerX + (relx + z * rely) * radius - pt.x, centerX + (relx + z * rely) * radius - pt.x,
centerY + (rely - z * relx) * radius - pt.y); centerY + (rely - z * relx) * radius - pt.y);
this._add(new Segment(pt, handleIn, out)); segments.push(new Segment(pt, handleIn, out));
} }
angle += inc; angle += inc;
} }
// Add all segments at once at the end for higher performance
this._add(segments);
}, },
lineBy: function(vector) { lineBy: function(vector) {

View file

@ -3,12 +3,12 @@ module('Path');
test('path.join(path)', function() { test('path.join(path)', function() {
var doc = new Document(); var doc = new Document();
var path = new Path(); var path = new Path();
path.add(0, 0); path.add([0, 0]);
path.add(10, 0); path.add([10, 0]);
var path2 = new Path(); var path2 = new Path();
path2.add(10, 0); path2.add([10, 0]);
path2.add(20, 10); path2.add([20, 10]);
path.join(path2); path.join(path2);
compareSegmentLists(path.segments, [new Segment(new Point(0, 0)), compareSegmentLists(path.segments, [new Segment(new Point(0, 0)),
@ -16,37 +16,37 @@ test('path.join(path)', function() {
equals(doc.activeLayer.children.length, 1); equals(doc.activeLayer.children.length, 1);
var path = new Path(); var path = new Path();
path.add(0, 0); path.add([0, 0]);
path.add(10, 0); path.add([10, 0]);
var path2 = new Path(); var path2 = new Path();
path2.add(20, 10); path2.add([20, 10]);
path2.add(10, 0); path2.add([10, 0]);
path.join(path2); path.join(path2);
compareSegmentLists(path.segments, [new Segment(new Point(0, 0)), 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, 0)), new Segment(new Point(20, 10))]);
var path = new Path(); var path = new Path();
path.add(0, 0); path.add([0, 0]);
path.add(10, 0); path.add([10, 0]);
var path2 = new Path(); var path2 = new Path();
path2.add(30, 10); path2.add([30, 10]);
path2.add(40, 0); path2.add([40, 0]);
path.join(path2); path.join(path2);
compareSegmentLists(path.segments, [new Segment(new Point(0, 0)), 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(10, 0)), new Segment(new Point(30, 10)),
new Segment(new Point(40, 0))]); new Segment(new Point(40, 0))]);
var path = new Path(); var path = new Path();
path.add(0, 0); path.add([0, 0]);
path.add(10, 0); path.add([10, 0]);
path.add(20, 10); path.add([20, 10]);
var path2 = new Path(); var path2 = new Path();
path2.add(0, 0); path2.add([0, 0]);
path2.add(10, 5); path2.add([10, 5]);
path2.add(20, 10); path2.add([20, 10]);
path.join(path2); path.join(path2);
compareSegmentLists(path.segments, [new Segment(new Point(0, 0)), compareSegmentLists(path.segments, [new Segment(new Point(0, 0)),
@ -58,10 +58,10 @@ test('path.join(path)', function() {
test('path.remove()', function() { test('path.remove()', function() {
var doc = new Document(); var doc = new Document();
var path = new Path(); var path = new Path();
path.add(0, 0); path.add([0, 0]);
path.add(10, 0); path.add([10, 0]);
path.add(20, 0); path.add([20, 0]);
path.add(30, 0); path.add([30, 0]);
path.removeSegment(0); path.removeSegment(0);
equals(path.segments.length, 3); equals(path.segments.length, 3);

View file

@ -6,44 +6,22 @@ test('path.curves Synchronisation', function() {
var path = new Path(); var path = new Path();
path.add(new Point(0, 100)); path.add(new Point(0, 100));
equals(path.curves.toString(), ''); equals(path.segments.toString(), '{ point: { x: 0, y: 100 } }', 'path.segments: path.add(new Point(0, 100));');
equals(path.curves.toString(), '', 'path.curves: path.add(new Point(0, 100));');
path.add(new Point(100, 100)); path.add(new Point(100, 100));
equals(path.curves.toString(), equals(path.segments.toString(), '{ point: { x: 0, y: 100 } },{ point: { x: 100, y: 100 } }', 'path.segments: path.add(new Point(100, 100));');
'{ point1: { x: 0, y: 100 }, point2: { x: 100, y: 100 } }', equals(path.curves.toString(), '{ point1: { x: 0, y: 100 }, point2: { x: 100, y: 100 } }', 'path.curves: path.add(new Point(100, 100));');
'2 x path.add()'); path.insert(1, {point:[50, 0], handleIn:[-25, 0], handleOut:[25, 0]});
equals(path.segments.toString(), '{ point: { x: 0, y: 100 } },{ point: { x: 50, y: 0 }, handleIn: { x: -25, y: 0 }, handleOut: { x: 25, y: 0 } },{ point: { x: 100, y: 100 } }', 'path.segments: path.insert(1, {point:[50, 0], handleIn:[-25, 0], handleOut:[25, 0]});');
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.curves: 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; path.closed = true;
equals(path.curves.toString(), equals(path.segments.toString(), '{ point: { x: 0, y: 100 } },{ point: { x: 50, y: 0 }, handleIn: { x: -25, y: 0 }, handleOut: { x: 25, y: 0 } },{ point: { x: 100, y: 100 } }', 'path.segments: path.closed = true;');
'{ 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 } }', 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.curves: path.closed = true;');
'path.closed = true');
path.removeSegments(2, 3); path.removeSegments(2, 3);
equals(path.curves.toString(), equals(path.segments.toString(), '{ point: { x: 0, y: 100 } },{ point: { x: 50, y: 0 }, handleIn: { x: -25, y: 0 }, handleOut: { x: 25, y: 0 } }', 'path.segments: path.removeSegments(2, 3);');
'{ 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 } }', 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.curves: path.removeSegments(2, 3);');
'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)); 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); path.removeSegments(1, 2);
equals(path.curves.toString(), equals(path.segments.toString(), '{ point: { x: 0, y: 100 } },{ point: { x: 100, y: 100 } }', 'path.segments: path.add(new Point(100, 100));\n path.removeSegments(1, 2);');
'{ point1: { x: 0, y: 100 }, point2: { x: 100, y: 100 } },{ point1: { x: 100, y: 100 }, point2: { x: 0, y: 100 } }', 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.curves: path.add(new Point(100, 100));\n path.removeSegments(1, 2);');
'path.removeSegments(1, 2)');
equals(path.segments.toString(),
'{ point: { x: 0, y: 100 } },{ point: { x: 100, y: 100 } }',
'segments');
}); });