@joint/core 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +376 -0
- package/README.md +49 -0
- package/dist/geometry.js +6486 -0
- package/dist/geometry.min.js +8 -0
- package/dist/joint.d.ts +5536 -0
- package/dist/joint.js +39629 -0
- package/dist/joint.min.js +8 -0
- package/dist/joint.nowrap.js +39626 -0
- package/dist/joint.nowrap.min.js +8 -0
- package/dist/vectorizer.js +9135 -0
- package/dist/vectorizer.min.js +8 -0
- package/dist/version.mjs +3 -0
- package/index.js +3 -0
- package/joint.mjs +27 -0
- package/package.json +192 -0
- package/src/V/annotation.mjs +0 -0
- package/src/V/index.mjs +2642 -0
- package/src/anchors/index.mjs +123 -0
- package/src/config/index.mjs +12 -0
- package/src/connectionPoints/index.mjs +202 -0
- package/src/connectionStrategies/index.mjs +73 -0
- package/src/connectors/curve.mjs +553 -0
- package/src/connectors/index.mjs +6 -0
- package/src/connectors/jumpover.mjs +452 -0
- package/src/connectors/normal.mjs +12 -0
- package/src/connectors/rounded.mjs +17 -0
- package/src/connectors/smooth.mjs +44 -0
- package/src/connectors/straight.mjs +110 -0
- package/src/dia/Cell.mjs +945 -0
- package/src/dia/CellView.mjs +1316 -0
- package/src/dia/Element.mjs +519 -0
- package/src/dia/ElementView.mjs +859 -0
- package/src/dia/Graph.mjs +1112 -0
- package/src/dia/HighlighterView.mjs +319 -0
- package/src/dia/Link.mjs +565 -0
- package/src/dia/LinkView.mjs +2207 -0
- package/src/dia/Paper.mjs +3171 -0
- package/src/dia/PaperLayer.mjs +75 -0
- package/src/dia/ToolView.mjs +69 -0
- package/src/dia/ToolsView.mjs +128 -0
- package/src/dia/attributes/calc.mjs +128 -0
- package/src/dia/attributes/connection.mjs +75 -0
- package/src/dia/attributes/defs.mjs +76 -0
- package/src/dia/attributes/eval.mjs +64 -0
- package/src/dia/attributes/index.mjs +69 -0
- package/src/dia/attributes/legacy.mjs +148 -0
- package/src/dia/attributes/offset.mjs +53 -0
- package/src/dia/attributes/props.mjs +30 -0
- package/src/dia/attributes/shape.mjs +92 -0
- package/src/dia/attributes/text.mjs +180 -0
- package/src/dia/index.mjs +13 -0
- package/src/dia/layers/GridLayer.mjs +176 -0
- package/src/dia/ports.mjs +874 -0
- package/src/elementTools/Control.mjs +153 -0
- package/src/elementTools/HoverConnect.mjs +37 -0
- package/src/elementTools/index.mjs +5 -0
- package/src/env/index.mjs +43 -0
- package/src/g/bezier.mjs +175 -0
- package/src/g/curve.mjs +956 -0
- package/src/g/ellipse.mjs +245 -0
- package/src/g/extend.mjs +64 -0
- package/src/g/geometry.helpers.mjs +58 -0
- package/src/g/index.mjs +17 -0
- package/src/g/intersection.mjs +511 -0
- package/src/g/line.bearing.mjs +30 -0
- package/src/g/line.length.mjs +5 -0
- package/src/g/line.mjs +356 -0
- package/src/g/line.squaredLength.mjs +10 -0
- package/src/g/path.mjs +2260 -0
- package/src/g/point.mjs +375 -0
- package/src/g/points.mjs +247 -0
- package/src/g/polygon.mjs +51 -0
- package/src/g/polyline.mjs +523 -0
- package/src/g/rect.mjs +556 -0
- package/src/g/types.mjs +10 -0
- package/src/highlighters/addClass.mjs +27 -0
- package/src/highlighters/index.mjs +5 -0
- package/src/highlighters/list.mjs +111 -0
- package/src/highlighters/mask.mjs +220 -0
- package/src/highlighters/opacity.mjs +17 -0
- package/src/highlighters/stroke.mjs +100 -0
- package/src/layout/index.mjs +4 -0
- package/src/layout/ports/port.mjs +188 -0
- package/src/layout/ports/portLabel.mjs +224 -0
- package/src/linkAnchors/index.mjs +76 -0
- package/src/linkTools/Anchor.mjs +235 -0
- package/src/linkTools/Arrowhead.mjs +103 -0
- package/src/linkTools/Boundary.mjs +48 -0
- package/src/linkTools/Button.mjs +121 -0
- package/src/linkTools/Connect.mjs +85 -0
- package/src/linkTools/HoverConnect.mjs +161 -0
- package/src/linkTools/Segments.mjs +393 -0
- package/src/linkTools/Vertices.mjs +253 -0
- package/src/linkTools/helpers.mjs +33 -0
- package/src/linkTools/index.mjs +8 -0
- package/src/mvc/Collection.mjs +560 -0
- package/src/mvc/Data.mjs +46 -0
- package/src/mvc/Dom/Dom.mjs +587 -0
- package/src/mvc/Dom/Event.mjs +130 -0
- package/src/mvc/Dom/animations.mjs +122 -0
- package/src/mvc/Dom/events.mjs +69 -0
- package/src/mvc/Dom/index.mjs +13 -0
- package/src/mvc/Dom/methods.mjs +392 -0
- package/src/mvc/Dom/props.mjs +77 -0
- package/src/mvc/Dom/vars.mjs +5 -0
- package/src/mvc/Events.mjs +337 -0
- package/src/mvc/Listener.mjs +33 -0
- package/src/mvc/Model.mjs +239 -0
- package/src/mvc/View.mjs +323 -0
- package/src/mvc/ViewBase.mjs +182 -0
- package/src/mvc/index.mjs +9 -0
- package/src/mvc/mvcUtils.mjs +90 -0
- package/src/polyfills/array.js +4 -0
- package/src/polyfills/base64.js +68 -0
- package/src/polyfills/index.mjs +5 -0
- package/src/polyfills/number.js +3 -0
- package/src/polyfills/string.js +3 -0
- package/src/polyfills/typedArray.js +47 -0
- package/src/routers/index.mjs +6 -0
- package/src/routers/manhattan.mjs +856 -0
- package/src/routers/metro.mjs +91 -0
- package/src/routers/normal.mjs +6 -0
- package/src/routers/oneSide.mjs +60 -0
- package/src/routers/orthogonal.mjs +323 -0
- package/src/routers/rightAngle.mjs +1056 -0
- package/src/shapes/index.mjs +3 -0
- package/src/shapes/standard.mjs +755 -0
- package/src/util/cloneCells.mjs +67 -0
- package/src/util/getRectPoint.mjs +65 -0
- package/src/util/index.mjs +5 -0
- package/src/util/svgTagTemplate.mjs +110 -0
- package/src/util/util.mjs +1754 -0
- package/src/util/utilHelpers.mjs +2402 -0
- package/src/util/wrappers.mjs +56 -0
- package/types/geometry.d.ts +815 -0
- package/types/index.d.ts +53 -0
- package/types/joint.d.ts +4391 -0
- package/types/joint.head.d.ts +12 -0
- package/types/vectorizer.d.ts +327 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { manhattan } from './manhattan.mjs';
|
|
2
|
+
import * as util from '../util/index.mjs';
|
|
3
|
+
import * as g from '../g/index.mjs';
|
|
4
|
+
|
|
5
|
+
var config = {
|
|
6
|
+
|
|
7
|
+
maxAllowedDirectionChange: 45,
|
|
8
|
+
|
|
9
|
+
// cost of a diagonal step
|
|
10
|
+
diagonalCost: function() {
|
|
11
|
+
|
|
12
|
+
var step = this.step;
|
|
13
|
+
return Math.ceil(Math.sqrt(step * step << 1));
|
|
14
|
+
},
|
|
15
|
+
|
|
16
|
+
// an array of directions to find next points on the route
|
|
17
|
+
// different from start/end directions
|
|
18
|
+
directions: function() {
|
|
19
|
+
|
|
20
|
+
var step = this.step;
|
|
21
|
+
var cost = this.cost();
|
|
22
|
+
var diagonalCost = this.diagonalCost();
|
|
23
|
+
|
|
24
|
+
return [
|
|
25
|
+
{ offsetX: step, offsetY: 0, cost: cost },
|
|
26
|
+
{ offsetX: step, offsetY: step, cost: diagonalCost },
|
|
27
|
+
{ offsetX: 0, offsetY: step, cost: cost },
|
|
28
|
+
{ offsetX: -step, offsetY: step, cost: diagonalCost },
|
|
29
|
+
{ offsetX: -step, offsetY: 0, cost: cost },
|
|
30
|
+
{ offsetX: -step, offsetY: -step, cost: diagonalCost },
|
|
31
|
+
{ offsetX: 0, offsetY: -step, cost: cost },
|
|
32
|
+
{ offsetX: step, offsetY: -step, cost: diagonalCost }
|
|
33
|
+
];
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
// a simple route used in situations when main routing method fails
|
|
37
|
+
// (exceed max number of loop iterations, inaccessible)
|
|
38
|
+
fallbackRoute: function(from, to, opt) {
|
|
39
|
+
|
|
40
|
+
// Find a route which breaks by 45 degrees ignoring all obstacles.
|
|
41
|
+
|
|
42
|
+
var theta = from.theta(to);
|
|
43
|
+
|
|
44
|
+
var route = [];
|
|
45
|
+
|
|
46
|
+
var a = { x: to.x, y: from.y };
|
|
47
|
+
var b = { x: from.x, y: to.y };
|
|
48
|
+
|
|
49
|
+
if (theta % 180 > 90) {
|
|
50
|
+
var t = a;
|
|
51
|
+
a = b;
|
|
52
|
+
b = t;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
var p1 = (theta % 90) < 45 ? a : b;
|
|
56
|
+
var l1 = new g.Line(from, p1);
|
|
57
|
+
|
|
58
|
+
var alpha = 90 * Math.ceil(theta / 90);
|
|
59
|
+
|
|
60
|
+
var p2 = g.Point.fromPolar(l1.squaredLength(), g.toRad(alpha + 135), p1);
|
|
61
|
+
var l2 = new g.Line(to, p2);
|
|
62
|
+
|
|
63
|
+
var intersectionPoint = l1.intersection(l2);
|
|
64
|
+
var point = intersectionPoint ? intersectionPoint : to;
|
|
65
|
+
|
|
66
|
+
var directionFrom = intersectionPoint ? point : from;
|
|
67
|
+
|
|
68
|
+
var quadrant = 360 / opt.directions.length;
|
|
69
|
+
var angleTheta = directionFrom.theta(to);
|
|
70
|
+
var normalizedAngle = g.normalizeAngle(angleTheta + (quadrant / 2));
|
|
71
|
+
var directionAngle = quadrant * Math.floor(normalizedAngle / quadrant);
|
|
72
|
+
|
|
73
|
+
opt.previousDirectionAngle = directionAngle;
|
|
74
|
+
|
|
75
|
+
if (point) route.push(point.round());
|
|
76
|
+
route.push(to);
|
|
77
|
+
|
|
78
|
+
return route;
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// public function
|
|
83
|
+
export const metro = function(vertices, opt, linkView) {
|
|
84
|
+
|
|
85
|
+
if (!util.isFunction(manhattan)) {
|
|
86
|
+
throw new Error('Metro requires the manhattan router.');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return manhattan(vertices, util.assign({}, config, opt), linkView);
|
|
90
|
+
};
|
|
91
|
+
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import * as util from '../util/index.mjs';
|
|
2
|
+
|
|
3
|
+
// Routes the link always to/from a certain side
|
|
4
|
+
//
|
|
5
|
+
// Arguments:
|
|
6
|
+
// padding ... gap between the element and the first vertex. :: Default 40.
|
|
7
|
+
// side ... 'left' | 'right' | 'top' | 'bottom' :: Default 'bottom'.
|
|
8
|
+
//
|
|
9
|
+
export const oneSide = function(vertices, opt, linkView) {
|
|
10
|
+
|
|
11
|
+
var side = opt.side || 'bottom';
|
|
12
|
+
var padding = util.normalizeSides(opt.padding || 40);
|
|
13
|
+
|
|
14
|
+
// LinkView contains cached source an target bboxes.
|
|
15
|
+
// Note that those are Geometry rectangle objects.
|
|
16
|
+
var sourceBBox = linkView.sourceBBox;
|
|
17
|
+
var targetBBox = linkView.targetBBox;
|
|
18
|
+
var sourcePoint = sourceBBox.center();
|
|
19
|
+
var targetPoint = targetBBox.center();
|
|
20
|
+
|
|
21
|
+
var coordinate, dimension, direction;
|
|
22
|
+
|
|
23
|
+
switch (side) {
|
|
24
|
+
case 'bottom':
|
|
25
|
+
direction = 1;
|
|
26
|
+
coordinate = 'y';
|
|
27
|
+
dimension = 'height';
|
|
28
|
+
break;
|
|
29
|
+
case 'top':
|
|
30
|
+
direction = -1;
|
|
31
|
+
coordinate = 'y';
|
|
32
|
+
dimension = 'height';
|
|
33
|
+
break;
|
|
34
|
+
case 'left':
|
|
35
|
+
direction = -1;
|
|
36
|
+
coordinate = 'x';
|
|
37
|
+
dimension = 'width';
|
|
38
|
+
break;
|
|
39
|
+
case 'right':
|
|
40
|
+
direction = 1;
|
|
41
|
+
coordinate = 'x';
|
|
42
|
+
dimension = 'width';
|
|
43
|
+
break;
|
|
44
|
+
default:
|
|
45
|
+
throw new Error('Router: invalid side');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// move the points from the center of the element to outside of it.
|
|
49
|
+
sourcePoint[coordinate] += direction * (sourceBBox[dimension] / 2 + padding[side]);
|
|
50
|
+
targetPoint[coordinate] += direction * (targetBBox[dimension] / 2 + padding[side]);
|
|
51
|
+
|
|
52
|
+
// make link orthogonal (at least the first and last vertex).
|
|
53
|
+
if ((direction * (sourcePoint[coordinate] - targetPoint[coordinate])) > 0) {
|
|
54
|
+
targetPoint[coordinate] = sourcePoint[coordinate];
|
|
55
|
+
} else {
|
|
56
|
+
sourcePoint[coordinate] = targetPoint[coordinate];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return [sourcePoint].concat(vertices, targetPoint);
|
|
60
|
+
};
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import * as g from '../g/index.mjs';
|
|
2
|
+
import * as util from '../util/index.mjs';
|
|
3
|
+
|
|
4
|
+
// bearing -> opposite bearing
|
|
5
|
+
var opposites = {
|
|
6
|
+
N: 'S',
|
|
7
|
+
S: 'N',
|
|
8
|
+
E: 'W',
|
|
9
|
+
W: 'E'
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// bearing -> radians
|
|
13
|
+
var radians = {
|
|
14
|
+
N: -Math.PI / 2 * 3,
|
|
15
|
+
S: -Math.PI / 2,
|
|
16
|
+
E: 0,
|
|
17
|
+
W: Math.PI
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// HELPERS //
|
|
21
|
+
|
|
22
|
+
// returns a point `p` where lines p,p1 and p,p2 are perpendicular and p is not contained
|
|
23
|
+
// in the given box
|
|
24
|
+
function freeJoin(p1, p2, bbox) {
|
|
25
|
+
|
|
26
|
+
var p = new g.Point(p1.x, p2.y);
|
|
27
|
+
if (bbox.containsPoint(p)) p = new g.Point(p2.x, p1.y);
|
|
28
|
+
// kept for reference
|
|
29
|
+
// if (bbox.containsPoint(p)) p = null;
|
|
30
|
+
|
|
31
|
+
return p;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// returns either width or height of a bbox based on the given bearing
|
|
35
|
+
function getBBoxSize(bbox, bearing) {
|
|
36
|
+
|
|
37
|
+
return bbox[(bearing === 'W' || bearing === 'E') ? 'width' : 'height'];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// simple bearing method (calculates only orthogonal cardinals)
|
|
41
|
+
function getBearing(from, to) {
|
|
42
|
+
|
|
43
|
+
if (from.x === to.x) return (from.y > to.y) ? 'N' : 'S';
|
|
44
|
+
if (from.y === to.y) return (from.x > to.x) ? 'W' : 'E';
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// transform point to a rect
|
|
49
|
+
function getPointBox(p) {
|
|
50
|
+
|
|
51
|
+
return new g.Rect(p.x, p.y, 0, 0);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function getPaddingBox(opt) {
|
|
55
|
+
|
|
56
|
+
// if both provided, opt.padding wins over opt.elementPadding
|
|
57
|
+
var sides = util.normalizeSides(opt.padding || opt.elementPadding || 20);
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
x: -sides.left,
|
|
61
|
+
y: -sides.top,
|
|
62
|
+
width: sides.left + sides.right,
|
|
63
|
+
height: sides.top + sides.bottom
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// return source bbox
|
|
68
|
+
function getSourceBBox(linkView, opt) {
|
|
69
|
+
|
|
70
|
+
return linkView.sourceBBox.clone().moveAndExpand(getPaddingBox(opt));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// return target bbox
|
|
74
|
+
function getTargetBBox(linkView, opt) {
|
|
75
|
+
|
|
76
|
+
return linkView.targetBBox.clone().moveAndExpand(getPaddingBox(opt));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// return source anchor
|
|
80
|
+
function getSourceAnchor(linkView, opt) {
|
|
81
|
+
|
|
82
|
+
if (linkView.sourceAnchor) return linkView.sourceAnchor;
|
|
83
|
+
|
|
84
|
+
// fallback: center of bbox
|
|
85
|
+
var sourceBBox = getSourceBBox(linkView, opt);
|
|
86
|
+
return sourceBBox.center();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// return target anchor
|
|
90
|
+
function getTargetAnchor(linkView, opt) {
|
|
91
|
+
|
|
92
|
+
if (linkView.targetAnchor) return linkView.targetAnchor;
|
|
93
|
+
|
|
94
|
+
// fallback: center of bbox
|
|
95
|
+
var targetBBox = getTargetBBox(linkView, opt);
|
|
96
|
+
return targetBBox.center(); // default
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// PARTIAL ROUTERS //
|
|
100
|
+
|
|
101
|
+
function vertexVertex(from, to, bearing) {
|
|
102
|
+
|
|
103
|
+
var p1 = new g.Point(from.x, to.y);
|
|
104
|
+
var p2 = new g.Point(to.x, from.y);
|
|
105
|
+
var d1 = getBearing(from, p1);
|
|
106
|
+
var d2 = getBearing(from, p2);
|
|
107
|
+
var opposite = opposites[bearing];
|
|
108
|
+
|
|
109
|
+
var p = (d1 === bearing || (d1 !== opposite && (d2 === opposite || d2 !== bearing))) ? p1 : p2;
|
|
110
|
+
|
|
111
|
+
return { points: [p], direction: getBearing(p, to) };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function elementVertex(from, to, fromBBox) {
|
|
115
|
+
|
|
116
|
+
var p = freeJoin(from, to, fromBBox);
|
|
117
|
+
|
|
118
|
+
return { points: [p], direction: getBearing(p, to) };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function vertexElement(from, to, toBBox, bearing) {
|
|
122
|
+
|
|
123
|
+
var route = {};
|
|
124
|
+
|
|
125
|
+
var points = [new g.Point(from.x, to.y), new g.Point(to.x, from.y)];
|
|
126
|
+
var freePoints = points.filter(function(pt) {
|
|
127
|
+
return !toBBox.containsPoint(pt);
|
|
128
|
+
});
|
|
129
|
+
var freeBearingPoints = freePoints.filter(function(pt) {
|
|
130
|
+
return getBearing(pt, from) !== bearing;
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
var p;
|
|
134
|
+
|
|
135
|
+
if (freeBearingPoints.length > 0) {
|
|
136
|
+
// Try to pick a point which bears the same direction as the previous segment.
|
|
137
|
+
|
|
138
|
+
p = freeBearingPoints.filter(function(pt) {
|
|
139
|
+
return getBearing(from, pt) === bearing;
|
|
140
|
+
}).pop();
|
|
141
|
+
p = p || freeBearingPoints[0];
|
|
142
|
+
|
|
143
|
+
route.points = [p];
|
|
144
|
+
route.direction = getBearing(p, to);
|
|
145
|
+
|
|
146
|
+
} else {
|
|
147
|
+
// Here we found only points which are either contained in the element or they would create
|
|
148
|
+
// a link segment going in opposite direction from the previous one.
|
|
149
|
+
// We take the point inside element and move it outside the element in the direction the
|
|
150
|
+
// route is going. Now we can join this point with the current end (using freeJoin).
|
|
151
|
+
|
|
152
|
+
p = util.difference(points, freePoints)[0];
|
|
153
|
+
|
|
154
|
+
var p2 = (new g.Point(to)).move(p, -getBBoxSize(toBBox, bearing) / 2);
|
|
155
|
+
var p1 = freeJoin(p2, from, toBBox);
|
|
156
|
+
|
|
157
|
+
route.points = [p1, p2];
|
|
158
|
+
route.direction = getBearing(p2, to);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return route;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function elementElement(from, to, fromBBox, toBBox) {
|
|
165
|
+
|
|
166
|
+
var route = elementVertex(to, from, toBBox);
|
|
167
|
+
var p1 = route.points[0];
|
|
168
|
+
|
|
169
|
+
if (fromBBox.containsPoint(p1)) {
|
|
170
|
+
|
|
171
|
+
route = elementVertex(from, to, fromBBox);
|
|
172
|
+
var p2 = route.points[0];
|
|
173
|
+
|
|
174
|
+
if (toBBox.containsPoint(p2)) {
|
|
175
|
+
|
|
176
|
+
var fromBorder = (new g.Point(from)).move(p2, -getBBoxSize(fromBBox, getBearing(from, p2)) / 2);
|
|
177
|
+
var toBorder = (new g.Point(to)).move(p1, -getBBoxSize(toBBox, getBearing(to, p1)) / 2);
|
|
178
|
+
var mid = (new g.Line(fromBorder, toBorder)).midpoint();
|
|
179
|
+
|
|
180
|
+
var startRoute = elementVertex(from, mid, fromBBox);
|
|
181
|
+
var endRoute = vertexVertex(mid, to, startRoute.direction);
|
|
182
|
+
|
|
183
|
+
route.points = [startRoute.points[0], endRoute.points[0]];
|
|
184
|
+
route.direction = endRoute.direction;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return route;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Finds route for situations where one element is inside the other.
|
|
192
|
+
// Typically the route is directed outside the outer element first and
|
|
193
|
+
// then back towards the inner element.
|
|
194
|
+
function insideElement(from, to, fromBBox, toBBox, bearing) {
|
|
195
|
+
|
|
196
|
+
var route = {};
|
|
197
|
+
var boundary = fromBBox.union(toBBox).inflate(1);
|
|
198
|
+
|
|
199
|
+
// start from the point which is closer to the boundary
|
|
200
|
+
var reversed = boundary.center().distance(to) > boundary.center().distance(from);
|
|
201
|
+
var start = reversed ? to : from;
|
|
202
|
+
var end = reversed ? from : to;
|
|
203
|
+
|
|
204
|
+
var p1, p2, p3;
|
|
205
|
+
|
|
206
|
+
if (bearing) {
|
|
207
|
+
// Points on circle with radius equals 'W + H` are always outside the rectangle
|
|
208
|
+
// with width W and height H if the center of that circle is the center of that rectangle.
|
|
209
|
+
p1 = g.Point.fromPolar(boundary.width + boundary.height, radians[bearing], start);
|
|
210
|
+
p1 = boundary.pointNearestToPoint(p1).move(p1, -1);
|
|
211
|
+
|
|
212
|
+
} else {
|
|
213
|
+
p1 = boundary.pointNearestToPoint(start).move(start, 1);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
p2 = freeJoin(p1, end, boundary);
|
|
217
|
+
|
|
218
|
+
if (p1.round().equals(p2.round())) {
|
|
219
|
+
p2 = g.Point.fromPolar(boundary.width + boundary.height, g.toRad(p1.theta(start)) + Math.PI / 2, end);
|
|
220
|
+
p2 = boundary.pointNearestToPoint(p2).move(end, 1).round();
|
|
221
|
+
p3 = freeJoin(p1, p2, boundary);
|
|
222
|
+
route.points = reversed ? [p2, p3, p1] : [p1, p3, p2];
|
|
223
|
+
|
|
224
|
+
} else {
|
|
225
|
+
route.points = reversed ? [p2, p1] : [p1, p2];
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
route.direction = reversed ? getBearing(p1, to) : getBearing(p2, to);
|
|
229
|
+
|
|
230
|
+
return route;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// MAIN ROUTER //
|
|
234
|
+
|
|
235
|
+
// Return points through which a connection needs to be drawn in order to obtain an orthogonal link
|
|
236
|
+
// routing from source to target going through `vertices`.
|
|
237
|
+
export function orthogonal(vertices, opt, linkView) {
|
|
238
|
+
|
|
239
|
+
var sourceBBox = getSourceBBox(linkView, opt);
|
|
240
|
+
var targetBBox = getTargetBBox(linkView, opt);
|
|
241
|
+
|
|
242
|
+
var sourceAnchor = getSourceAnchor(linkView, opt);
|
|
243
|
+
var targetAnchor = getTargetAnchor(linkView, opt);
|
|
244
|
+
|
|
245
|
+
// if anchor lies outside of bbox, the bbox expands to include it
|
|
246
|
+
sourceBBox = sourceBBox.union(getPointBox(sourceAnchor));
|
|
247
|
+
targetBBox = targetBBox.union(getPointBox(targetAnchor));
|
|
248
|
+
|
|
249
|
+
vertices = util.toArray(vertices).map(g.Point);
|
|
250
|
+
vertices.unshift(sourceAnchor);
|
|
251
|
+
vertices.push(targetAnchor);
|
|
252
|
+
|
|
253
|
+
var bearing; // bearing of previous route segment
|
|
254
|
+
|
|
255
|
+
var orthogonalVertices = []; // the array of found orthogonal vertices to be returned
|
|
256
|
+
for (var i = 0, max = vertices.length - 1; i < max; i++) {
|
|
257
|
+
|
|
258
|
+
var route = null;
|
|
259
|
+
|
|
260
|
+
var from = vertices[i];
|
|
261
|
+
var to = vertices[i + 1];
|
|
262
|
+
|
|
263
|
+
var isOrthogonal = !!getBearing(from, to);
|
|
264
|
+
|
|
265
|
+
if (i === 0) { // source
|
|
266
|
+
|
|
267
|
+
if (i + 1 === max) { // route source -> target
|
|
268
|
+
|
|
269
|
+
// Expand one of the elements by 1px to detect situations when the two
|
|
270
|
+
// elements are positioned next to each other with no gap in between.
|
|
271
|
+
if (sourceBBox.intersect(targetBBox.clone().inflate(1))) {
|
|
272
|
+
route = insideElement(from, to, sourceBBox, targetBBox);
|
|
273
|
+
|
|
274
|
+
} else if (!isOrthogonal) {
|
|
275
|
+
route = elementElement(from, to, sourceBBox, targetBBox);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
} else { // route source -> vertex
|
|
279
|
+
|
|
280
|
+
if (sourceBBox.containsPoint(to)) {
|
|
281
|
+
route = insideElement(from, to, sourceBBox, getPointBox(to).moveAndExpand(getPaddingBox(opt)));
|
|
282
|
+
|
|
283
|
+
} else if (!isOrthogonal) {
|
|
284
|
+
route = elementVertex(from, to, sourceBBox);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
} else if (i + 1 === max) { // route vertex -> target
|
|
289
|
+
|
|
290
|
+
// prevent overlaps with previous line segment
|
|
291
|
+
var isOrthogonalLoop = isOrthogonal && getBearing(to, from) === bearing;
|
|
292
|
+
|
|
293
|
+
if (targetBBox.containsPoint(from) || isOrthogonalLoop) {
|
|
294
|
+
route = insideElement(from, to, getPointBox(from).moveAndExpand(getPaddingBox(opt)), targetBBox, bearing);
|
|
295
|
+
|
|
296
|
+
} else if (!isOrthogonal) {
|
|
297
|
+
route = vertexElement(from, to, targetBBox, bearing);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
} else if (!isOrthogonal) { // route vertex -> vertex
|
|
301
|
+
route = vertexVertex(from, to, bearing);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// applicable to all routes:
|
|
305
|
+
|
|
306
|
+
// set bearing for next iteration
|
|
307
|
+
if (route) {
|
|
308
|
+
Array.prototype.push.apply(orthogonalVertices, route.points);
|
|
309
|
+
bearing = route.direction;
|
|
310
|
+
|
|
311
|
+
} else {
|
|
312
|
+
// orthogonal route and not looped
|
|
313
|
+
bearing = getBearing(from, to);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// push `to` point to identified orthogonal vertices array
|
|
317
|
+
if (i + 1 < max) {
|
|
318
|
+
orthogonalVertices.push(to);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return orthogonalVertices;
|
|
323
|
+
}
|