2011-03-06 19:50:44 -05:00
|
|
|
/*
|
|
|
|
* 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.
|
2011-03-07 20:41:50 -05:00
|
|
|
* http://paperjs.org/
|
2011-03-06 19:50:44 -05:00
|
|
|
* http://scriptographer.org/
|
|
|
|
*
|
2011-03-07 20:41:50 -05:00
|
|
|
* Distributed under the MIT license. See LICENSE file for details.
|
|
|
|
*
|
2011-03-06 19:50:44 -05:00
|
|
|
* Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
|
|
|
|
* http://lehni.org/ & http://jonathanpuckey.com/
|
|
|
|
*
|
2011-03-07 20:41:50 -05:00
|
|
|
* All rights reserved.
|
2011-03-06 19:50:44 -05:00
|
|
|
*/
|
|
|
|
|
2011-03-04 08:34:31 -05:00
|
|
|
var Segment = this.Segment = Base.extend({
|
2011-03-06 05:57:14 -05:00
|
|
|
beans: true,
|
|
|
|
|
2011-03-06 07:24:03 -05:00
|
|
|
initialize: function(arg0, arg1, arg2, arg3, arg4, arg5) {
|
2011-02-13 11:26:24 -05:00
|
|
|
if (arguments.length == 0) {
|
2011-04-21 08:21:56 -04:00
|
|
|
this._point = SegmentPoint.create(this, 0, 0);
|
2011-02-13 11:26:24 -05:00
|
|
|
} else if (arguments.length == 1) {
|
2011-03-06 05:57:14 -05:00
|
|
|
// TODO: If beans are not activated, this won't copy from
|
|
|
|
// an existing segment. OK?
|
2011-03-06 07:24:03 -05:00
|
|
|
if (arg0.point) {
|
2011-04-21 08:21:56 -04:00
|
|
|
this._point = SegmentPoint.create(this, arg0.point);
|
|
|
|
this._handleIn = SegmentPoint.create(this, arg0.handleIn);
|
|
|
|
this._handleOut = SegmentPoint.create(this, arg0.handleOut);
|
2011-02-07 13:28:09 -05:00
|
|
|
} else {
|
2011-04-21 08:21:56 -04:00
|
|
|
this._point = SegmentPoint.create(this, arg0);
|
2011-02-07 13:28:09 -05:00
|
|
|
}
|
2011-02-13 11:26:24 -05:00
|
|
|
} else if (arguments.length < 6) {
|
2011-03-06 07:24:03 -05:00
|
|
|
if (arguments.length == 2 && !arg1.x) {
|
2011-04-21 08:21:56 -04:00
|
|
|
this._point = SegmentPoint.create(this, arg0, arg1);
|
2011-02-07 13:28:09 -05:00
|
|
|
} else {
|
2011-04-21 08:21:56 -04:00
|
|
|
this._point = SegmentPoint.create(this, arg0);
|
2011-03-06 05:57:14 -05:00
|
|
|
// Doesn't matter if these arguments exist, it creates 0, 0
|
|
|
|
// points otherwise
|
2011-04-21 08:21:56 -04:00
|
|
|
this._handleIn = SegmentPoint.create(this, arg1);
|
|
|
|
this._handleOut = SegmentPoint.create(this, arg2);
|
2011-02-07 13:28:09 -05:00
|
|
|
}
|
2011-02-13 11:26:24 -05:00
|
|
|
} else if (arguments.length == 6) {
|
2011-04-21 08:21:56 -04:00
|
|
|
this._point = SegmentPoint.create(this, arg0, arg1);
|
|
|
|
this._handleIn = SegmentPoint.create(this, arg2, arg3);
|
|
|
|
this._handleOut = SegmentPoint.create(this, arg4, arg5);
|
2011-02-13 10:09:24 -05:00
|
|
|
}
|
2011-03-06 05:57:14 -05:00
|
|
|
if (!this._handleIn)
|
2011-04-21 08:21:56 -04:00
|
|
|
this._handleIn = SegmentPoint.create(this, 0, 0);
|
2011-03-06 05:57:14 -05:00
|
|
|
if (!this._handleOut)
|
2011-04-21 08:21:56 -04:00
|
|
|
this._handleOut = SegmentPoint.create(this, 0, 0);
|
2011-02-07 13:28:09 -05:00
|
|
|
},
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-02-07 13:28:09 -05:00
|
|
|
getPoint: function() {
|
2011-03-06 05:57:14 -05:00
|
|
|
return this._point;
|
2011-02-07 13:28:09 -05:00
|
|
|
},
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-03-13 13:31:00 -04:00
|
|
|
setPoint: function(point) {
|
|
|
|
point = Point.read(arguments);
|
2011-03-06 07:24:03 -05:00
|
|
|
// Do not replace the internal object but update it instead, so
|
|
|
|
// references to it are kept alive.
|
|
|
|
this._point.set(point.x, point.y);
|
2011-02-07 13:28:09 -05:00
|
|
|
},
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-02-07 13:28:09 -05:00
|
|
|
getHandleIn: function() {
|
2011-03-06 05:57:14 -05:00
|
|
|
return this._handleIn;
|
|
|
|
},
|
|
|
|
|
2011-03-13 13:31:00 -04:00
|
|
|
setHandleIn: function(point) {
|
|
|
|
point = Point.read(arguments);
|
2011-03-06 07:24:03 -05:00
|
|
|
// See #setPoint:
|
|
|
|
this._handleIn.set(point.x, point.y);
|
2011-03-06 05:57:14 -05:00
|
|
|
// Update corner accordingly
|
|
|
|
// this.corner = !this._handleIn.isParallel(this._handleOut);
|
2011-02-07 13:28:09 -05:00
|
|
|
},
|
|
|
|
|
2011-03-06 06:18:35 -05:00
|
|
|
getHandleInIfSet: function() {
|
2011-04-21 08:33:12 -04:00
|
|
|
return this._handleIn._x == 0 && this._handleIn._y == 0
|
2011-03-06 06:18:35 -05:00
|
|
|
? null : this._handleIn;
|
2011-03-06 05:57:14 -05:00
|
|
|
},
|
|
|
|
|
2011-03-06 06:18:35 -05:00
|
|
|
getHandleOut: function() {
|
|
|
|
return this._handleOut;
|
2011-02-07 13:28:09 -05:00
|
|
|
},
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-03-13 13:31:00 -04:00
|
|
|
setHandleOut: function(point) {
|
|
|
|
point = Point.read(arguments);
|
2011-03-06 07:24:03 -05:00
|
|
|
// See #setPoint:
|
|
|
|
this._handleOut.set(point.x, point.y);
|
2011-03-06 05:57:14 -05:00
|
|
|
// Update corner accordingly
|
|
|
|
// this.corner = !this._handleIn.isParallel(this._handleOut);
|
2011-02-07 13:28:09 -05:00
|
|
|
},
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-03-06 06:18:35 -05:00
|
|
|
getHandleOutIfSet: function() {
|
2011-04-21 08:33:12 -04:00
|
|
|
return this._handleOut._x == 0 && this._handleOut._y == 0
|
2011-03-06 06:18:35 -05:00
|
|
|
? null : this._handleOut;
|
|
|
|
},
|
|
|
|
|
2011-03-06 08:11:04 -05:00
|
|
|
getIndex: function() {
|
|
|
|
// TODO: Cache and update indices instead of searching?
|
|
|
|
return this._path ? this._path._segments.indexOf(this) : -1;
|
|
|
|
},
|
|
|
|
|
2011-03-06 07:24:03 -05:00
|
|
|
getPath: function() {
|
|
|
|
return this._path;
|
|
|
|
},
|
|
|
|
|
2011-03-06 08:11:04 -05:00
|
|
|
getCurve: function() {
|
|
|
|
if (this._path != null) {
|
|
|
|
var index = this.getIndex();
|
|
|
|
// The last segment of an open path belongs to the last curve
|
|
|
|
// TODO: Port back to Scriptographer
|
|
|
|
if (!this._path.closed && index == this._path._segments.length - 1)
|
|
|
|
index--;
|
|
|
|
return this._path.getCurves()[index];
|
|
|
|
}
|
|
|
|
return null;
|
2011-02-07 13:28:09 -05:00
|
|
|
},
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-02-07 13:28:09 -05:00
|
|
|
getNext: function() {
|
2011-03-06 07:24:03 -05:00
|
|
|
return this._path && this._path._segments[this.getIndex() + 1] || null;
|
2011-02-07 13:28:09 -05:00
|
|
|
},
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-02-07 13:28:09 -05:00
|
|
|
getPrevious: function() {
|
2011-03-06 07:24:03 -05:00
|
|
|
return this._path && this._path._segments[this.getIndex() - 1] || null;
|
2011-02-07 13:28:09 -05:00
|
|
|
},
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-04-21 12:06:06 -04:00
|
|
|
getSelected: function(/* point */) {
|
|
|
|
var point = arguments.length ? arguments[0] : this.point;
|
|
|
|
var state = this._selectionState;
|
|
|
|
if (point == this.point) {
|
2011-04-21 13:37:51 -04:00
|
|
|
return state == SelectionState.POINT;
|
2011-04-21 12:06:06 -04:00
|
|
|
} else if (point == this.handleIn) {
|
2011-04-21 13:37:51 -04:00
|
|
|
return (state & SelectionState.HANDLE_IN)
|
|
|
|
== SelectionState.HANDLE_IN;
|
2011-04-21 12:06:06 -04:00
|
|
|
} else if (point == this.handleOut) {
|
2011-04-21 13:37:51 -04:00
|
|
|
return (state & SelectionState.HANDLE_OUT)
|
|
|
|
== SelectionState.HANDLE_OUT;
|
2011-04-21 12:06:06 -04:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
},
|
|
|
|
|
|
|
|
setSelected: function(/* pt, selected */) {
|
|
|
|
var pt, selected;
|
|
|
|
if (arguments.length == 2) {
|
|
|
|
// setSelected(pt, selected)
|
|
|
|
pt = arguments[0];
|
|
|
|
selected = arguments[1];
|
|
|
|
} else {
|
|
|
|
// setSelected(selected)
|
|
|
|
pt = this._point;
|
|
|
|
selected = arguments[0];
|
|
|
|
}
|
|
|
|
if (!this._path)
|
|
|
|
return;
|
|
|
|
var wasSelected = !!this._selectionState;
|
|
|
|
var state = this._selectionState,
|
2011-04-21 13:37:51 -04:00
|
|
|
pointSelected = state == SelectionState.POINT,
|
|
|
|
handleInSelected = (state & SelectionState.HANDLE_IN)
|
|
|
|
== SelectionState.HANDLE_IN,
|
|
|
|
handleOutSelected = (state & SelectionState.HANDLE_OUT)
|
|
|
|
== SelectionState.HANDLE_OUT,
|
2011-04-21 12:06:06 -04:00
|
|
|
previous = this.getPrevious(),
|
|
|
|
next = this.getNext(),
|
|
|
|
closed = this._path.closed,
|
|
|
|
segments = this._path._segments,
|
|
|
|
length = segments.length;
|
|
|
|
if (length > 1 && closed) {
|
|
|
|
if (previous == null)
|
|
|
|
previous = segments[length - 1];
|
|
|
|
if (next == null)
|
|
|
|
next = segments[0];
|
|
|
|
}
|
|
|
|
if (pt == this._point) {
|
|
|
|
if (pointSelected != selected) {
|
|
|
|
if (selected) {
|
|
|
|
handleInSelected = handleOutSelected = false;
|
|
|
|
} else {
|
|
|
|
// When deselecting a point, the handles get selected
|
|
|
|
// instead depending on the selection state of their
|
|
|
|
// neighbors.
|
|
|
|
handleInSelected = previous != null
|
|
|
|
&& (previous._point.getSelected()
|
|
|
|
|| previous._handleOut.getSelected());
|
|
|
|
handleOutSelected = next != null
|
|
|
|
&& (next._point.getSelected()
|
|
|
|
|| next._handleOut.getSelected());
|
|
|
|
}
|
|
|
|
pointSelected = selected;
|
|
|
|
}
|
|
|
|
} else if (pt == this._handleIn) {
|
|
|
|
if (handleInSelected != selected) {
|
|
|
|
// When selecting handles, the point get deselected.
|
|
|
|
if (selected)
|
|
|
|
pointSelected = false;
|
|
|
|
handleInSelected = selected;
|
|
|
|
}
|
|
|
|
} else if (pt == this._handleOut) {
|
|
|
|
if (handleOutSelected != selected) {
|
|
|
|
// When selecting handles, the point get deselected.
|
|
|
|
if (selected)
|
|
|
|
pointSelected = false;
|
|
|
|
handleOutSelected = selected;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this._selectionState = pointSelected
|
2011-04-21 13:37:51 -04:00
|
|
|
? SelectionState.POINT
|
2011-04-21 12:06:06 -04:00
|
|
|
: handleInSelected
|
|
|
|
? handleOutSelected
|
2011-04-21 13:37:51 -04:00
|
|
|
? SelectionState.HANDLE_BOTH
|
|
|
|
: SelectionState.HANDLE_IN
|
2011-04-21 12:06:06 -04:00
|
|
|
: handleOutSelected
|
2011-04-21 13:37:51 -04:00
|
|
|
? SelectionState.HANDLE_OUT
|
2011-04-21 12:06:06 -04:00
|
|
|
: null;
|
|
|
|
// If the selection state of the segment has changed, we need to let
|
|
|
|
// it's path know and possibly add or remove it from
|
|
|
|
// document._selectedItems
|
|
|
|
if (wasSelected == !this._selectionState) {
|
|
|
|
var path = this._path,
|
|
|
|
selectedItems = path._document._selectedItems;
|
|
|
|
if (!this._selectionState) {
|
|
|
|
path._selectedSegmentCount--;
|
|
|
|
if (path._selectedSegmentCount == 0) {
|
|
|
|
var index = selectedItems.indexOf(this);
|
|
|
|
selectedItems.slice(index, 1);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
path._selectedSegmentCount++;
|
|
|
|
if (path._selectedSegmentCount == 1) {
|
|
|
|
selectedItems.push(path);
|
|
|
|
}
|
|
|
|
}
|
2011-04-21 13:37:51 -04:00
|
|
|
}
|
2011-04-21 12:06:06 -04:00
|
|
|
},
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-02-07 13:28:09 -05:00
|
|
|
reverse: function() {
|
2011-03-06 05:57:14 -05:00
|
|
|
return new Segment(this._point, this._handleOut, this._handleIn);
|
2011-02-07 13:28:09 -05:00
|
|
|
},
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-02-07 13:28:09 -05:00
|
|
|
clone: function() {
|
|
|
|
return new Segment(this);
|
|
|
|
},
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-02-07 13:28:09 -05:00
|
|
|
remove: function() {
|
2011-04-21 12:06:06 -04:00
|
|
|
if (this._path) {
|
|
|
|
this._path._segments.splice(this.getIndex(), 1);
|
|
|
|
if (this.getSelected())
|
|
|
|
this._path._selectedSegmentCount--;
|
|
|
|
return true;
|
|
|
|
}
|
2011-02-07 13:28:09 -05:00
|
|
|
return false;
|
|
|
|
},
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-02-07 13:28:09 -05:00
|
|
|
toString: function() {
|
2011-03-06 05:57:14 -05:00
|
|
|
return '{ point: ' + this._point
|
2011-03-06 07:56:30 -05:00
|
|
|
+ (!this._handleIn.isZero()
|
|
|
|
? ', handleIn: ' + this._handleIn : '')
|
|
|
|
+ (this._handleOut.isZero()
|
|
|
|
? ', handleOut: ' + this._handleOut : '')
|
2011-02-07 13:28:09 -05:00
|
|
|
+ ' }';
|
2011-03-06 16:26:38 -05:00
|
|
|
},
|
|
|
|
|
|
|
|
_transformCoordinates: function(matrix, coords, change) {
|
|
|
|
// Use matrix.transform version() that takes arrays of multiple
|
|
|
|
// points for largely improved performance, as no calls to
|
|
|
|
// Point.read() and Point constructors are necessary.
|
|
|
|
var point = this._point,
|
|
|
|
// If a matrix is defined, only transform handles if they are set.
|
|
|
|
// This saves some computation time. If no matrix is set, always
|
|
|
|
// use the real handles, as we just want to receive a filled
|
2011-03-06 19:36:44 -05:00
|
|
|
// coords array for getBounds().
|
2011-03-06 16:26:38 -05:00
|
|
|
handleIn = matrix && this.getHandleInIfSet() || this._handleIn,
|
|
|
|
handleOut = matrix && this.getHandleOutIfSet() || this._handleOut,
|
2011-04-21 08:33:12 -04:00
|
|
|
x = point._x,
|
|
|
|
y = point._y;
|
2011-03-06 16:26:38 -05:00
|
|
|
coords[0] = x;
|
|
|
|
coords[1] = y;
|
|
|
|
var index = 2;
|
|
|
|
// We need to convert handles to absolute coordinates in order
|
|
|
|
// to transform them.
|
|
|
|
if (handleIn) {
|
2011-04-21 08:33:12 -04:00
|
|
|
coords[index++] = handleIn._x + x;
|
|
|
|
coords[index++] = handleIn._y + y;
|
2011-03-06 16:26:38 -05:00
|
|
|
}
|
|
|
|
if (handleOut) {
|
2011-04-21 08:33:12 -04:00
|
|
|
coords[index++] = handleOut._x + x;
|
|
|
|
coords[index++] = handleOut._y + y;
|
2011-03-06 16:26:38 -05:00
|
|
|
}
|
|
|
|
if (matrix) {
|
|
|
|
matrix.transform(coords, 0, coords, 0, index / 2);
|
|
|
|
x = coords[0];
|
|
|
|
y = coords[1];
|
|
|
|
if (change) {
|
|
|
|
// If change is true, we need to set the new values back
|
2011-04-21 08:33:12 -04:00
|
|
|
point._x = x;
|
|
|
|
point._y = y;
|
2011-03-06 16:26:38 -05:00
|
|
|
index = 2;
|
|
|
|
if (handleIn) {
|
2011-04-21 08:33:12 -04:00
|
|
|
handleIn._x = coords[index++] - x;
|
|
|
|
handleIn._y = coords[index++] - y;
|
2011-03-06 16:26:38 -05:00
|
|
|
}
|
|
|
|
if (handleOut) {
|
2011-04-21 08:33:12 -04:00
|
|
|
handleOut._x = coords[index++] - x;
|
|
|
|
handleOut._y = coords[index++] - y;
|
2011-03-06 16:26:38 -05:00
|
|
|
}
|
|
|
|
} 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;
|
|
|
|
}
|
|
|
|
if (!handleOut) {
|
|
|
|
coords[index++] = x;
|
|
|
|
coords[index++] = y;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-02-07 13:28:09 -05:00
|
|
|
}
|
2011-02-13 11:26:24 -05:00
|
|
|
});
|