lively.morphic.Morph.subclass('lively.morphic.Path',
'properties', {
isPath: true,
},
'initializing', {
initialize: function($super, vertices) {
var shape = new lively.morphic.Shapes.Path(vertices);
$super(shape);
},
},
'accessing', {
vertices: function() { return this.shape.vertices() },
},
'vertex and control point computations', {
pathBetweenRects: function(rect1, rect2) {
// copied and adpated from graffle Raphael 1.2.1 - JavaScript Vector Library
var p = [{x: rect1.x + rect1.width / 2, y: rect1.y - 1},
{x: rect1.x + rect1.width / 2, y: rect1.y + rect1.height + 1},
{x: rect1.x - 1, y: rect1.y + rect1.height / 2},
{x: rect1.x + rect1.width + 1, y: rect1.y + rect1.height / 2},
{x: rect2.x + rect2.width / 2, y: rect2.y - 1},
{x: rect2.x + rect2.width / 2, y: rect2.y + rect2.height + 1},
{x: rect2.x - 1, y: rect2.y + rect2.height / 2},
{x: rect2.x + rect2.width + 1, y: rect2.y + rect2.height / 2}];
var d = {}, dis = [];
for (var i = 0; i < 4; i++) {
for (var j = 4; j < 8; j++) {
var dx = Math.abs(p[i].x - p[j].x),
dy = Math.abs(p[i].y - p[j].y);
if ((i == j - 4) || (((i != 3 && j != 6) ||
p[i].x < p[j].x) && ((i != 2 && j != 7) || p[i].x > p[j].x) && ((i != 0 && j != 5) ||
p[i].y > p[j].y) && ((i != 1 && j != 4) || p[i].y < p[j].y))) {
dis.push(dx + dy);
d[dis[dis.length - 1]] = [i, j];
}
}
}
res = dis.length == 0 ? [0, 4] : d[Math.min.apply(Math, dis)];
var x1 = p[res[0]].x,
y1 = p[res[0]].y,
x4 = p[res[1]].x,
y4 = p[res[1]].y,
dx = Math.max(Math.abs(x1 - x4) / 2, 10),
dy = Math.max(Math.abs(y1 - y4) / 2, 10),
x2 = [x1, x1, x1 - dx, x1 + dx][res[0]].toFixed(3),
y2 = [y1 - dy, y1 + dy, y1, y1][res[0]].toFixed(3),
x3 = [0, 0, 0, 0, x4, x4, x4 - dx, x4 + dx][res[1]].toFixed(3),
y3 = [0, 0, 0, 0, y1 + dy, y1 - dy, y4, y4][res[1]].toFixed(3);
var p1 = this.localize(pt(x1, y1)),
c1 = this.localize(pt(x2, y2)),
c2 = this.localize(pt(x3, y3)),
p2 = this.localize(pt(x4, y4));
return [p1, c1, c2, p2];
},
},
'arrow behavior', {
addArrowHeadStart: function(morph) {
var ctrlPt = this.getControlPoints().first();
this.addMorph(morph);
ctrlPt.addMarker(morph, 'next')
},
addArrowHeadEnd: function(morph) {
var ctrlPt = this.getControlPoints().last();
this.addMorph(morph);
ctrlPt.addMarker(morph, 'prev')
},
},
'control points', {
getControlPoint: function(idx) { return this.getControlPoints()[idx] },
getControlPoints: function() {
var vertices = this.vertices();
if (!this.controlPoints) this.controlPoints = [];
if (vertices.length !== this.controlPoints.length)
this.controlPoints = this.vertices().collect(function(p, i) {
return this.controlPoints[i] || new lively.morphic.ControlPoint(this, i);
}, this);
return this.controlPoints;
},
insertControlPointBetween: function(index1, index2, vertex) {
var ctrlP1 = this.getControlPoint(index1);
ctrlP1.insertAfter(vertex);
},
},
'halos', {
getHalos: function($super) {
return $super()
.concat(this.getControlPointHalos())
.concat(this.getInsertPointHalos())
.reject(function(halo) { return halo instanceof lively.morphic.OriginHalo });
},
getControlPointHalos: function() { return this.getControlPoints().invoke('asHalo') },
getInsertPointHalo: function(idx) {
return new lively.morphic.PathInsertPointHalo(this.getControlPoint(idx));
},
getInsertPointHalos: function() {
var result = [];
for (var i = 0; i < this.vertices().length-1; i++) result.push(this.getInsertPointHalo(i));
return result
},
},
'event handling', {
onMouseDown: function($super, evt) {
return $super(evt);
// ----------------
if (!evt.isLeftMouseButtonDown()) return $super(evt);
var t = Date.now(), dblClickDelay = 400;
if (!this.firstClick || (t-this.firstClick) > dblClickDelay) {
this.firstClick = t
return $super(evt);
}
this.firstClick = null;
this.insertControlPoint(1, this.localize(evt.getPosition()));
return true;
},
},
'conversion', {
convertToCurve: function() {
var ctrlPoints = this.getControlPoints();
for (var i = 1; i < ctrlPoints.length; i++) {
var ctrlPt = ctrlPoints[i], prev = ctrlPoints[i-1];
if (ctrlPt.isCurve()) continue;
var prevPos = prev.getPos(),
currentPos = ctrlPt.getPos(),
ptArr = this.pathBetweenRects(
new Rectangle(prevPos.x,prevPos.y,0,0),
new Rectangle(currentPos.x,currentPos.y,0,0)),
c1 = ptArr[1],
c2 = ptArr[2],
p = ptArr[3];
ctrlPt.toCurve(p, c1, c2);
}
},
convertToLine: function() {
this.getControlPoints().forEach(function(ea) { return ea.toLine(ea.getPos()) });
},
},
'menu', {
morphMenuItems: function($super) {
var items = $super();
items.push(['to curve', this.convertToCurve.bind(this)]);
items.push(['to line', this.convertToLine.bind(this)]);
return items;
},
});