diff --git a/src/basic/Point.js b/src/basic/Point.js index a2494c46..13f827f7 100644 --- a/src/basic/Point.js +++ b/src/basic/Point.js @@ -149,29 +149,20 @@ var Point = this.Point = Base.extend({ }, setLength: function(length) { + // Whenever setting x/y, use #set() instead of direct assignment, + // so LinkedPoint does not report changes twice. if (this.isZero()) { - if (this._angle != null) { - var a = this._angle; - // Use #set() instead of direct assignment, so LinkedPoint - // can optimise - this.set( - Math.cos(a) * length, - Math.sin(a) * length - ); - } else { - // Assume angle = 0 - this.x = length; - // y is already 0 - } + var angle = this._angle || 0; + this.set( + Math.cos(angle) * length, + Math.sin(angle) * length + ); } else { var scale = length / this.getLength(); - if (scale == 0) { - // Calculate angle now, so it will be preserved even when - // x and y are 0 + // Force calculation of angle now, so it will be preserved even when + // x and y are 0 + if (scale == 0) this.getAngle(); - } - // Use #set() instead of direct assignment, so LinkedPoint - // can optimise this.set( this.x * scale, this.y * scale @@ -181,30 +172,18 @@ var Point = this.Point = Base.extend({ }, normalize: function(length) { - if (length === null) + if (length === undefined) length = 1; - var len = this.getLength(); - var scale = len != 0 ? length / len : 0; - var res = Point.create(this.x * scale, this.y * scale); + var current = this.getLength(), + scale = current != 0 ? length / current : 0, + point = Point.create(this.x * scale, this.y * scale); // Preserve angle. - res._angle = this._angle; - return res; + point._angle = this._angle; + return point; }, getQuadrant: function() { - if (this.x >= 0) { - if (this.y >= 0) { - return 1; - } else { - return 4; - } - } else { - if (this.y >= 0) { - return 2; - } else { - return 3; - } - } + return this.x >= 0 ? this.y >= 0 ? 1 : 4 : this.y >= 0 ? 2 : 3; }, /** @@ -230,8 +209,8 @@ var Point = this.Point = Base.extend({ angle = this._angle = angle * Math.PI / 180; if (!this.isZero()) { var length = this.getLength(); - // Use #set() instead of direct assignment, so LinkedPoint - // can optimise + // Use #set() instead of direct assignment of x/y, so LinkedPoint + // does not report changes twice. this.set( Math.cos(angle) * length, Math.sin(angle) * length @@ -514,7 +493,8 @@ var Point = this.Point = Base.extend({ min: function(point1, point2) { return Point.create( Math.min(point1.x, point2.x), - Math.min(point1.y, point2.y)); + Math.min(point1.y, point2.y) + ); }, /** @@ -536,7 +516,8 @@ var Point = this.Point = Base.extend({ max: function(point1, point2) { return Point.create( Math.max(point1.x, point2.x), - Math.max(point1.y, point2.y)); + Math.max(point1.y, point2.y) + ); }, /** diff --git a/src/color/Color.js b/src/color/Color.js index b8b53d1b..4d551037 100644 --- a/src/color/Color.js +++ b/src/color/Color.js @@ -173,15 +173,15 @@ var Color = this.Color = Base.extend(new function() { }, toString: function() { - var string = ''; + var parts = []; for (var i = 0, l = this._components.length; i < l; i++) { var component = this._components[i]; var value = this['_' + component]; if (component === 'alpha' && value == null) value = 1; - string += (i > 0 ? ', ' : '') + component + ': ' + value; + parts.push(component + ': ' + value); } - return '{ ' + string + ' }'; + return '{ ' + parts.join(', ') + ' }'; }, toCssString: function() { diff --git a/src/path/Curve.js b/src/path/Curve.js index 6f753223..24f091d7 100644 --- a/src/path/Curve.js +++ b/src/path/Curve.js @@ -35,6 +35,11 @@ var Curve = this.Curve = Base.extend({ } }, + _changed: function() { + // Clear cached values. + delete this._length; + }, + _updateSegments: function() { if (this._path) { this._index2 = this._index1 + 1; @@ -153,11 +158,19 @@ var Curve = this.Curve = Base.extend({ }, getLength: function(/* from, to */) { + var from = arguments[0], + to = arguments[1]; + fullLength = arguments.length == 0 || from == 0 && to == 1; + if (fullLength && this._length != null) + return this._length; // Hide parameters from Bootstrap so it injects bean too var args = this.getCurveValues(); - if (arguments.length > 0) - args.push(arguments[0], arguments[1]); - return Curve.getLength.apply(Curve, args); + if (!fullLength) + args.push(from, to); + var length = Curve.getLength.apply(Curve, args); + if (fullLength) + this._length = length; + return length; }, /** @@ -203,13 +216,13 @@ var Curve = this.Curve = Base.extend({ }, toString: function() { - return '{ point1: ' + this._segment1._point - + (!this._segment1._handleOut.isZero() - ? ', handle1: ' + this._segment1._handleOut : '') - + (this._segment2._handleIn.isZero() - ? ', handle2: ' + this._segment2._handleIn : '') - + ', point2: ' + this._segment2._point - + ' }'; + var parts = [ 'point1: ' + this._segment1._point ]; + if (!this._segment1._handleOut.isZero()) + parts.push('handle1: ' + this._segment1._handleOut); + if (!this._segment2._handleIn.isZero()) + parts.push('handle2: ' + this._segment2._handleIn); + parts.push('point2: ' + this._segment2._point); + return '{ ' + parts.join(', ') + ' }'; }, statics: { diff --git a/src/path/Path.js b/src/path/Path.js index 2b0a677a..9d40f6ea 100644 --- a/src/path/Path.js +++ b/src/path/Path.js @@ -28,6 +28,13 @@ var Path = this.Path = PathItem.extend({ || typeof segments[0] !== 'object' ? arguments : segments); }, + _changed: function() { + // Clear cached values. + delete this._length; + delete this._bounds; + delete this._strokeBounds; + }, + /** * The segments contained within the path. */ @@ -102,6 +109,7 @@ var Path = this.Path = PathItem.extend({ if (closed) this._curves[i = length - 1] = Curve.create(this, i); } + this._changed(); } }, @@ -116,6 +124,7 @@ var Path = this.Path = PathItem.extend({ this._segments[i]._transformCoordinates(matrix, coords, true); } } + this._changed(); }, /** @@ -163,6 +172,7 @@ var Path = this.Path = PathItem.extend({ if (this._closed) this._curves[l - 1]._updateSegments(); } + this._changed(); return segment; }, @@ -199,6 +209,7 @@ var Path = this.Path = PathItem.extend({ segment._selectionState = 0; } } + this._changed(); return segments; } return null; @@ -245,6 +256,7 @@ var Path = this.Path = PathItem.extend({ segment._handleIn = segment._handleOut; segment._handleOut = handleIn; } + this._changed(); }, join: function(path) { @@ -282,17 +294,20 @@ var Path = this.Path = PathItem.extend({ last1.remove(); this.setClosed(true); } + this._changed(); return true; } return false; }, getLength: function() { - var curves = this.getCurves(); - var length = 0; - for (var i = 0, l = curves.length; i < l; i++) - length += curves[i].getLength(); - return length; + if (this._length == null) { + var curves = this.getCurves(); + this._length = 0; + for (var i = 0, l = curves.length; i < l; i++) + this._length += curves[i].getLength(); + } + return this._length; }, _getOffset: function(location) { @@ -977,9 +992,12 @@ var Path = this.Path = PathItem.extend({ getBounds: function(/* matrix */) { // Pass the matrix hidden from Bootstrap, so it still inject // getBounds as bean too. - var bounds = getBounds(this, arguments[0]); - return LinkedRectangle.create(this, 'setBounds', - bounds.x, bounds.y, bounds.width, bounds.height); + if (!this._bounds) { + var bounds = getBounds(this, arguments[0]); + this._bounds = LinkedRectangle.create(this, 'setBounds', + bounds.x, bounds.y, bounds.width, bounds.height); + } + return this._bounds; }, /** diff --git a/src/path/Segment.js b/src/path/Segment.js index ca917c85..f8d30801 100644 --- a/src/path/Segment.js +++ b/src/path/Segment.js @@ -51,6 +51,28 @@ var Segment = this.Segment = Base.extend({ this._handleOut = SegmentPoint.create(this, 0, 0); }, + _changed: function(point) { + if (this._path) { + // Delegate changes to affected curves if they exist + if (this._path._curves) { + var curve = this.getCurve(), other; + if (curve) { + curve._changed(); + // Get the other affected curve, which is the previous one + // for _point or _handleIn changing when this segment is + // _segment1 of the curve, for all other cases it's the next + // (e.g. _handleOut or this segment == _segment2) + if (other = (curve[point == this._point + || point == this._handleIn && curve._segment1 == this + ? 'getPrevious' : 'getNext']())) { + other._changed(); + } + } + } + this._path._changed(); + } + }, + getPoint: function() { return this._point; }, @@ -71,7 +93,7 @@ var Segment = this.Segment = Base.extend({ // See #setPoint: this._handleIn.set(point.x, point.y); // Update corner accordingly - // this.corner = !this._handleIn.isParallel(this._handleOut); + // this.corner = !this._handleIn.isColinear(this._handleOut); }, getHandleInIfSet: function() { @@ -88,7 +110,7 @@ var Segment = this.Segment = Base.extend({ // See #setPoint: this._handleOut.set(point.x, point.y); // Update corner accordingly - // this.corner = !this._handleIn.isParallel(this._handleOut); + // this.corner = !this._handleIn.isColinear(this._handleOut); }, getHandleOutIfSet: function() { @@ -105,7 +127,7 @@ var Segment = this.Segment = Base.extend({ }, getCurve: function() { - if (this._path != null) { + if (this._path) { var index = this._index; // The last segment of an open path belongs to the last curve if (!this._path._closed && index == this._path._segments.length - 1) @@ -208,12 +230,12 @@ var Segment = this.Segment = Base.extend({ }, toString: function() { - return '{ point: ' + this._point - + (!this._handleIn.isZero() - ? ', handleIn: ' + this._handleIn : '') - + (this._handleOut.isZero() - ? ', handleOut: ' + this._handleOut : '') - + ' }'; + var parts = [ 'point: ' + this._point ]; + if (!this._handleIn.isZero()) + parts.push('handleIn: ' + this._handleIn); + if (!this._handleOut.isZero()) + parts.push('handleOut: ' + this._handleOut); + return '{ ' + parts.join(', ') + ' }'; }, _transformCoordinates: function(matrix, coords, change) { @@ -228,47 +250,47 @@ var Segment = this.Segment = Base.extend({ handleIn = matrix && this.getHandleInIfSet() || this._handleIn, handleOut = matrix && this.getHandleOutIfSet() || this._handleOut, x = point._x, - y = point._y; + y = point._y, + i = 2; coords[0] = x; coords[1] = y; - var index = 2; // We need to convert handles to absolute coordinates in order // to transform them. if (handleIn) { - coords[index++] = handleIn._x + x; - coords[index++] = handleIn._y + y; + coords[i++] = handleIn._x + x; + coords[i++] = handleIn._y + y; } if (handleOut) { - coords[index++] = handleOut._x + x; - coords[index++] = handleOut._y + y; + coords[i++] = handleOut._x + x; + coords[i++] = handleOut._y + y; } if (matrix) { - matrix.transform(coords, 0, coords, 0, index / 2); + matrix.transform(coords, 0, coords, 0, i / 2); x = coords[0]; y = coords[1]; if (change) { // If change is true, we need to set the new values back point._x = x; point._y = y; - index = 2; + i = 2; if (handleIn) { - handleIn._x = coords[index++] - x; - handleIn._y = coords[index++] - y; + handleIn._x = coords[i++] - x; + handleIn._y = coords[i++] - y; } if (handleOut) { - handleOut._x = coords[index++] - x; - handleOut._y = coords[index++] - y; + handleOut._x = coords[i++] - x; + handleOut._y = coords[i++] - y; } } else { // We want to receive the results in coords, so make sure // handleIn and out are defined too, even if they're 0 if (!handleIn) { - coords[index++] = x; - coords[index++] = y; + coords[i++] = x; + coords[i++] = y; } if (!handleOut) { - coords[index++] = x; - coords[index++] = y; + coords[i++] = x; + coords[i++] = y; } } } diff --git a/src/path/SegmentPoint.js b/src/path/SegmentPoint.js index f5c92255..700ddd36 100644 --- a/src/path/SegmentPoint.js +++ b/src/path/SegmentPoint.js @@ -8,7 +8,7 @@ var SegmentPoint = Point.extend({ set: function(x, y) { this._x = x; this._y = y; -// this._segment._markDirty(DirtyFlags.BOUNDS); + this._segment._changed(this); return this; }, @@ -18,7 +18,7 @@ var SegmentPoint = Point.extend({ setX: function(x) { this._x = x; -// this._segment._markDirty(DirtyFlags.BOUNDS); + this._segment._changed(this); }, getY: function() { @@ -27,7 +27,7 @@ var SegmentPoint = Point.extend({ setY: function(y) { this._y = y; -// this._segment._markDirty(DirtyFlags.BOUNDS); + this._segment._changed(this); }, setSelected: function(selected) { @@ -39,15 +39,16 @@ var SegmentPoint = Point.extend({ }, statics: { - create: function(segment, arg1, arg2) { - var point; - if (arguments.length == 2) { - point = new SegmentPoint(arg1); - } else { - point = new SegmentPoint(SegmentPoint.dont); - point._x = arg1; - point._y = arg2; + create: function(segment, x, y) { + if (y === undefined) { + // Use the normal point constructor to read in point values + var tmp = new Point(x); + x = tmp.x; + y = tmp.y; } + var point = new SegmentPoint(SegmentPoint.dont); + point._x = x; + point._y = y; point._segment = segment; return point; } diff --git a/test/tests/item.js b/test/tests/Item.js similarity index 100% rename from test/tests/item.js rename to test/tests/Item.js