@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,553 @@
|
|
|
1
|
+
import { Path, Point, Curve } from '../g/index.mjs';
|
|
2
|
+
|
|
3
|
+
const Directions = {
|
|
4
|
+
AUTO: 'auto',
|
|
5
|
+
HORIZONTAL: 'horizontal',
|
|
6
|
+
VERTICAL: 'vertical',
|
|
7
|
+
CLOSEST_POINT: 'closest-point',
|
|
8
|
+
OUTWARDS: 'outwards'
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const TangentDirections = {
|
|
12
|
+
UP: 'up',
|
|
13
|
+
DOWN: 'down',
|
|
14
|
+
LEFT: 'left',
|
|
15
|
+
RIGHT: 'right',
|
|
16
|
+
AUTO: 'auto',
|
|
17
|
+
CLOSEST_POINT: 'closest-point',
|
|
18
|
+
OUTWARDS: 'outwards'
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const curve = function(sourcePoint, targetPoint, route = [], opt = {}, linkView) {
|
|
22
|
+
const raw = Boolean(opt.raw);
|
|
23
|
+
// distanceCoefficient - a coefficient of the tangent vector length relative to the distance between points.
|
|
24
|
+
// angleTangentCoefficient - a coefficient of the end tangents length in the case of angles larger than 45 degrees.
|
|
25
|
+
// tension - a Catmull-Rom curve tension parameter.
|
|
26
|
+
// sourceTangent - a tangent vector along the curve at the sourcePoint.
|
|
27
|
+
// sourceDirection - a unit direction vector along the curve at the sourcePoint.
|
|
28
|
+
// targetTangent - a tangent vector along the curve at the targetPoint.
|
|
29
|
+
// targetDirection - a unit direction vector along the curve at the targetPoint.
|
|
30
|
+
// precision - a rounding precision for path values.
|
|
31
|
+
const { direction = Directions.AUTO, precision = 3 } = opt;
|
|
32
|
+
const options = {
|
|
33
|
+
coeff: opt.distanceCoefficient || 0.6,
|
|
34
|
+
angleTangentCoefficient: opt.angleTangentCoefficient || 80,
|
|
35
|
+
tau: opt.tension || 0.5,
|
|
36
|
+
sourceTangent: opt.sourceTangent ? new Point(opt.sourceTangent) : null,
|
|
37
|
+
targetTangent: opt.targetTangent ? new Point(opt.targetTangent) : null,
|
|
38
|
+
rotate: Boolean(opt.rotate)
|
|
39
|
+
};
|
|
40
|
+
if (typeof opt.sourceDirection === 'string')
|
|
41
|
+
options.sourceDirection = opt.sourceDirection;
|
|
42
|
+
else if (typeof opt.sourceDirection === 'number')
|
|
43
|
+
options.sourceDirection = new Point(1, 0).rotate(null, opt.sourceDirection);
|
|
44
|
+
else
|
|
45
|
+
options.sourceDirection = opt.sourceDirection ? new Point(opt.sourceDirection).normalize() : null;
|
|
46
|
+
|
|
47
|
+
if (typeof opt.targetDirection === 'string')
|
|
48
|
+
options.targetDirection = opt.targetDirection;
|
|
49
|
+
else if (typeof opt.targetDirection === 'number')
|
|
50
|
+
options.targetDirection = new Point(1, 0).rotate(null, opt.targetDirection);
|
|
51
|
+
else
|
|
52
|
+
options.targetDirection = opt.targetDirection ? new Point(opt.targetDirection).normalize() : null;
|
|
53
|
+
|
|
54
|
+
const completeRoute = [sourcePoint, ...route, targetPoint].map(p => new Point(p));
|
|
55
|
+
|
|
56
|
+
// The calculation of a sourceTangent
|
|
57
|
+
let sourceTangent;
|
|
58
|
+
if (options.sourceTangent) {
|
|
59
|
+
sourceTangent = options.sourceTangent;
|
|
60
|
+
} else {
|
|
61
|
+
const sourceDirection = getSourceTangentDirection(linkView, completeRoute, direction, options);
|
|
62
|
+
const tangentLength = completeRoute[0].distance(completeRoute[1]) * options.coeff;
|
|
63
|
+
const pointsVector = completeRoute[1].difference(completeRoute[0]).normalize();
|
|
64
|
+
const angle = angleBetweenVectors(sourceDirection, pointsVector);
|
|
65
|
+
if (angle > Math.PI / 4) {
|
|
66
|
+
const updatedLength = tangentLength + (angle - Math.PI / 4) * options.angleTangentCoefficient;
|
|
67
|
+
sourceTangent = sourceDirection.clone().scale(updatedLength, updatedLength);
|
|
68
|
+
} else {
|
|
69
|
+
sourceTangent = sourceDirection.clone().scale(tangentLength, tangentLength);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// The calculation of a targetTangent
|
|
74
|
+
let targetTangent;
|
|
75
|
+
if (options.targetTangent) {
|
|
76
|
+
targetTangent = options.targetTangent;
|
|
77
|
+
} else {
|
|
78
|
+
const targetDirection = getTargetTangentDirection(linkView, completeRoute, direction, options);
|
|
79
|
+
const last = completeRoute.length - 1;
|
|
80
|
+
const tangentLength = completeRoute[last - 1].distance(completeRoute[last]) * options.coeff;
|
|
81
|
+
const pointsVector = completeRoute[last - 1].difference(completeRoute[last]).normalize();
|
|
82
|
+
const angle = angleBetweenVectors(targetDirection, pointsVector);
|
|
83
|
+
if (angle > Math.PI / 4) {
|
|
84
|
+
const updatedLength = tangentLength + (angle - Math.PI / 4) * options.angleTangentCoefficient;
|
|
85
|
+
targetTangent = targetDirection.clone().scale(updatedLength, updatedLength);
|
|
86
|
+
} else {
|
|
87
|
+
targetTangent = targetDirection.clone().scale(tangentLength, tangentLength);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const catmullRomCurves = createCatmullRomCurves(completeRoute, sourceTangent, targetTangent, options);
|
|
92
|
+
const bezierCurves = catmullRomCurves.map(curve => catmullRomToBezier(curve, options));
|
|
93
|
+
const path = new Path(bezierCurves).round(precision);
|
|
94
|
+
|
|
95
|
+
return (raw) ? path : path.serialize();
|
|
96
|
+
};
|
|
97
|
+
curve.Directions = Directions;
|
|
98
|
+
curve.TangentDirections = TangentDirections;
|
|
99
|
+
|
|
100
|
+
function getHorizontalSourceDirection(linkView, route, options) {
|
|
101
|
+
const { sourceBBox } = linkView;
|
|
102
|
+
|
|
103
|
+
let sourceSide;
|
|
104
|
+
let rotation;
|
|
105
|
+
if (!linkView.sourceView) {
|
|
106
|
+
if (sourceBBox.x > route[1].x)
|
|
107
|
+
sourceSide = 'right';
|
|
108
|
+
else
|
|
109
|
+
sourceSide = 'left';
|
|
110
|
+
} else {
|
|
111
|
+
rotation = linkView.sourceView.model.angle();
|
|
112
|
+
if (options.rotate && rotation) {
|
|
113
|
+
const unrotatedBBox = linkView.sourceView.getNodeUnrotatedBBox(linkView.sourceView.el);
|
|
114
|
+
const sourcePoint = route[0].clone();
|
|
115
|
+
sourcePoint.rotate(sourceBBox.center(), rotation);
|
|
116
|
+
sourceSide = unrotatedBBox.sideNearestToPoint(sourcePoint);
|
|
117
|
+
} else {
|
|
118
|
+
sourceSide = sourceBBox.sideNearestToPoint(route[0]);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
let direction;
|
|
123
|
+
switch (sourceSide) {
|
|
124
|
+
case 'left':
|
|
125
|
+
direction = new Point(-1, 0);
|
|
126
|
+
break;
|
|
127
|
+
case 'right':
|
|
128
|
+
default:
|
|
129
|
+
direction = new Point(1, 0);
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (options.rotate && rotation) {
|
|
134
|
+
direction.rotate(null, -rotation);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return direction;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function getHorizontalTargetDirection(linkView, route, options) {
|
|
141
|
+
const { targetBBox } = linkView;
|
|
142
|
+
|
|
143
|
+
let targetSide;
|
|
144
|
+
let rotation;
|
|
145
|
+
if (!linkView.targetView) {
|
|
146
|
+
if (targetBBox.x > route[route.length - 2].x)
|
|
147
|
+
targetSide = 'left';
|
|
148
|
+
else
|
|
149
|
+
targetSide = 'right';
|
|
150
|
+
} else {
|
|
151
|
+
rotation = linkView.targetView.model.angle();
|
|
152
|
+
if (options.rotate && rotation) {
|
|
153
|
+
const unrotatedBBox = linkView.targetView.getNodeUnrotatedBBox(linkView.targetView.el);
|
|
154
|
+
const targetPoint = route[route.length - 1].clone();
|
|
155
|
+
targetPoint.rotate(targetBBox.center(), rotation);
|
|
156
|
+
targetSide = unrotatedBBox.sideNearestToPoint(targetPoint);
|
|
157
|
+
} else {
|
|
158
|
+
targetSide = targetBBox.sideNearestToPoint(route[route.length - 1]);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
let direction;
|
|
163
|
+
switch (targetSide) {
|
|
164
|
+
case 'left':
|
|
165
|
+
direction = new Point(-1, 0);
|
|
166
|
+
break;
|
|
167
|
+
case 'right':
|
|
168
|
+
default:
|
|
169
|
+
direction = new Point(1, 0);
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (options.rotate && rotation) {
|
|
174
|
+
direction.rotate(null, -rotation);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return direction;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function getVerticalSourceDirection(linkView, route, options) {
|
|
181
|
+
const { sourceBBox } = linkView;
|
|
182
|
+
|
|
183
|
+
let sourceSide;
|
|
184
|
+
let rotation;
|
|
185
|
+
if (!linkView.sourceView) {
|
|
186
|
+
if (sourceBBox.y > route[1].y)
|
|
187
|
+
sourceSide = 'bottom';
|
|
188
|
+
else
|
|
189
|
+
sourceSide = 'top';
|
|
190
|
+
} else {
|
|
191
|
+
rotation = linkView.sourceView.model.angle();
|
|
192
|
+
if (options.rotate && rotation) {
|
|
193
|
+
const unrotatedBBox = linkView.sourceView.getNodeUnrotatedBBox(linkView.sourceView.el);
|
|
194
|
+
const sourcePoint = route[0].clone();
|
|
195
|
+
sourcePoint.rotate(sourceBBox.center(), rotation);
|
|
196
|
+
sourceSide = unrotatedBBox.sideNearestToPoint(sourcePoint);
|
|
197
|
+
} else {
|
|
198
|
+
sourceSide = sourceBBox.sideNearestToPoint(route[0]);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
let direction;
|
|
203
|
+
switch (sourceSide) {
|
|
204
|
+
case 'top':
|
|
205
|
+
direction = new Point(0, -1);
|
|
206
|
+
break;
|
|
207
|
+
case 'bottom':
|
|
208
|
+
default:
|
|
209
|
+
direction = new Point(0, 1);
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (options.rotate && rotation) {
|
|
214
|
+
direction.rotate(null, -rotation);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return direction;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function getVerticalTargetDirection(linkView, route, options) {
|
|
221
|
+
const { targetBBox } = linkView;
|
|
222
|
+
|
|
223
|
+
let targetSide;
|
|
224
|
+
let rotation;
|
|
225
|
+
if (!linkView.targetView) {
|
|
226
|
+
if (targetBBox.y > route[route.length - 2].y)
|
|
227
|
+
targetSide = 'top';
|
|
228
|
+
else
|
|
229
|
+
targetSide = 'bottom';
|
|
230
|
+
} else {
|
|
231
|
+
rotation = linkView.targetView.model.angle();
|
|
232
|
+
if (options.rotate && rotation) {
|
|
233
|
+
const unrotatedBBox = linkView.targetView.getNodeUnrotatedBBox(linkView.targetView.el);
|
|
234
|
+
const targetPoint = route[route.length - 1].clone();
|
|
235
|
+
targetPoint.rotate(targetBBox.center(), rotation);
|
|
236
|
+
targetSide = unrotatedBBox.sideNearestToPoint(targetPoint);
|
|
237
|
+
} else {
|
|
238
|
+
targetSide = targetBBox.sideNearestToPoint(route[route.length - 1]);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
let direction;
|
|
244
|
+
switch (targetSide) {
|
|
245
|
+
case 'top':
|
|
246
|
+
direction = new Point(0, -1);
|
|
247
|
+
break;
|
|
248
|
+
case 'bottom':
|
|
249
|
+
default:
|
|
250
|
+
direction = new Point(0, 1);
|
|
251
|
+
break;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (options.rotate && rotation) {
|
|
255
|
+
direction.rotate(null, -rotation);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return direction;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function getAutoSourceDirection(linkView, route, options) {
|
|
262
|
+
const { sourceBBox } = linkView;
|
|
263
|
+
|
|
264
|
+
let sourceSide;
|
|
265
|
+
let rotation;
|
|
266
|
+
if (!linkView.sourceView) {
|
|
267
|
+
sourceSide = sourceBBox.sideNearestToPoint(route[1]);
|
|
268
|
+
} else {
|
|
269
|
+
rotation = linkView.sourceView.model.angle();
|
|
270
|
+
if (options.rotate && rotation) {
|
|
271
|
+
const unrotatedBBox = linkView.sourceView.getNodeUnrotatedBBox(linkView.sourceView.el);
|
|
272
|
+
const sourcePoint = route[0].clone();
|
|
273
|
+
sourcePoint.rotate(sourceBBox.center(), rotation);
|
|
274
|
+
sourceSide = unrotatedBBox.sideNearestToPoint(sourcePoint);
|
|
275
|
+
} else {
|
|
276
|
+
sourceSide = sourceBBox.sideNearestToPoint(route[0]);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
let direction;
|
|
281
|
+
switch (sourceSide) {
|
|
282
|
+
case 'top':
|
|
283
|
+
direction = new Point(0, -1);
|
|
284
|
+
break;
|
|
285
|
+
case 'bottom':
|
|
286
|
+
direction = new Point(0, 1);
|
|
287
|
+
break;
|
|
288
|
+
case 'right':
|
|
289
|
+
direction = new Point(1, 0);
|
|
290
|
+
break;
|
|
291
|
+
case 'left':
|
|
292
|
+
direction = new Point(-1, 0);
|
|
293
|
+
break;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (options.rotate && rotation) {
|
|
297
|
+
direction.rotate(null, -rotation);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return direction;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function getAutoTargetDirection(linkView, route, options) {
|
|
304
|
+
const { targetBBox } = linkView;
|
|
305
|
+
|
|
306
|
+
let targetSide;
|
|
307
|
+
let rotation;
|
|
308
|
+
if (!linkView.targetView) {
|
|
309
|
+
targetSide = targetBBox.sideNearestToPoint(route[route.length - 2]);
|
|
310
|
+
} else {
|
|
311
|
+
rotation = linkView.targetView.model.angle();
|
|
312
|
+
if (options.rotate && rotation) {
|
|
313
|
+
const unrotatedBBox = linkView.targetView.getNodeUnrotatedBBox(linkView.targetView.el);
|
|
314
|
+
const targetPoint = route[route.length - 1].clone();
|
|
315
|
+
targetPoint.rotate(targetBBox.center(), rotation);
|
|
316
|
+
targetSide = unrotatedBBox.sideNearestToPoint(targetPoint);
|
|
317
|
+
} else {
|
|
318
|
+
targetSide = targetBBox.sideNearestToPoint(route[route.length - 1]);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
let direction;
|
|
323
|
+
switch (targetSide) {
|
|
324
|
+
case 'top':
|
|
325
|
+
direction = new Point(0, -1);
|
|
326
|
+
break;
|
|
327
|
+
case 'bottom':
|
|
328
|
+
direction = new Point(0, 1);
|
|
329
|
+
break;
|
|
330
|
+
case 'right':
|
|
331
|
+
direction = new Point(1, 0);
|
|
332
|
+
break;
|
|
333
|
+
case 'left':
|
|
334
|
+
direction = new Point(-1, 0);
|
|
335
|
+
break;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (options.rotate && rotation) {
|
|
339
|
+
direction.rotate(null, -rotation);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return direction;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function getClosestPointSourceDirection(linkView, route, options) {
|
|
346
|
+
return route[1].difference(route[0]).normalize();
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function getClosestPointTargetDirection(linkView, route, options) {
|
|
350
|
+
const last = route.length - 1;
|
|
351
|
+
return route[last - 1].difference(route[last]).normalize();
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function getOutwardsSourceDirection(linkView, route, options) {
|
|
355
|
+
const { sourceBBox } = linkView;
|
|
356
|
+
const sourceCenter = sourceBBox.center();
|
|
357
|
+
return route[0].difference(sourceCenter).normalize();
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function getOutwardsTargetDirection(linkView, route, options) {
|
|
361
|
+
const { targetBBox } = linkView;
|
|
362
|
+
const targetCenter = targetBBox.center();
|
|
363
|
+
return route[route.length - 1].difference(targetCenter).normalize();
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function getSourceTangentDirection(linkView, route, direction, options) {
|
|
367
|
+
if (options.sourceDirection) {
|
|
368
|
+
switch (options.sourceDirection) {
|
|
369
|
+
case TangentDirections.UP:
|
|
370
|
+
return new Point(0, -1);
|
|
371
|
+
case TangentDirections.DOWN:
|
|
372
|
+
return new Point(0, 1);
|
|
373
|
+
case TangentDirections.LEFT:
|
|
374
|
+
return new Point(-1, 0);
|
|
375
|
+
case TangentDirections.RIGHT:
|
|
376
|
+
return new Point(1, 0);
|
|
377
|
+
case TangentDirections.AUTO:
|
|
378
|
+
return getAutoSourceDirection(linkView, route, options);
|
|
379
|
+
case TangentDirections.CLOSEST_POINT:
|
|
380
|
+
return getClosestPointSourceDirection(linkView, route, options);
|
|
381
|
+
case TangentDirections.OUTWARDS:
|
|
382
|
+
return getOutwardsSourceDirection(linkView, route, options);
|
|
383
|
+
default:
|
|
384
|
+
return options.sourceDirection;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
switch (direction) {
|
|
389
|
+
case Directions.HORIZONTAL:
|
|
390
|
+
return getHorizontalSourceDirection(linkView, route, options);
|
|
391
|
+
case Directions.VERTICAL:
|
|
392
|
+
return getVerticalSourceDirection(linkView, route, options);
|
|
393
|
+
case Directions.CLOSEST_POINT:
|
|
394
|
+
return getClosestPointSourceDirection(linkView, route, options);
|
|
395
|
+
case Directions.OUTWARDS:
|
|
396
|
+
return getOutwardsSourceDirection(linkView, route, options);
|
|
397
|
+
case Directions.AUTO:
|
|
398
|
+
default:
|
|
399
|
+
return getAutoSourceDirection(linkView, route, options);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
function getTargetTangentDirection(linkView, route, direction, options) {
|
|
404
|
+
if (options.targetDirection) {
|
|
405
|
+
switch (options.targetDirection) {
|
|
406
|
+
case TangentDirections.UP:
|
|
407
|
+
return new Point(0, -1);
|
|
408
|
+
case TangentDirections.DOWN:
|
|
409
|
+
return new Point(0, 1);
|
|
410
|
+
case TangentDirections.LEFT:
|
|
411
|
+
return new Point(-1, 0);
|
|
412
|
+
case TangentDirections.RIGHT:
|
|
413
|
+
return new Point(1, 0);
|
|
414
|
+
case TangentDirections.AUTO:
|
|
415
|
+
return getAutoTargetDirection(linkView, route, options);
|
|
416
|
+
case TangentDirections.CLOSEST_POINT:
|
|
417
|
+
return getClosestPointTargetDirection(linkView, route, options);
|
|
418
|
+
case TangentDirections.OUTWARDS:
|
|
419
|
+
return getOutwardsTargetDirection(linkView, route, options);
|
|
420
|
+
default:
|
|
421
|
+
return options.targetDirection;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
switch (direction) {
|
|
426
|
+
case Directions.HORIZONTAL:
|
|
427
|
+
return getHorizontalTargetDirection(linkView, route, options);
|
|
428
|
+
case Directions.VERTICAL:
|
|
429
|
+
return getVerticalTargetDirection(linkView, route, options);
|
|
430
|
+
case Directions.CLOSEST_POINT:
|
|
431
|
+
return getClosestPointTargetDirection(linkView, route, options);
|
|
432
|
+
case Directions.OUTWARDS:
|
|
433
|
+
return getOutwardsTargetDirection(linkView, route, options);
|
|
434
|
+
case Directions.AUTO:
|
|
435
|
+
default:
|
|
436
|
+
return getAutoTargetDirection(linkView, route, options);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
function rotateVector(vector, angle) {
|
|
441
|
+
const cos = Math.cos(angle);
|
|
442
|
+
const sin = Math.sin(angle);
|
|
443
|
+
const x = cos * vector.x - sin * vector.y;
|
|
444
|
+
const y = sin * vector.x + cos * vector.y;
|
|
445
|
+
vector.x = x;
|
|
446
|
+
vector.y = y;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
function angleBetweenVectors(v1, v2) {
|
|
450
|
+
let cos = v1.dot(v2) / (v1.magnitude() * v2.magnitude());
|
|
451
|
+
if (cos < -1) cos = -1;
|
|
452
|
+
if (cos > 1) cos = 1;
|
|
453
|
+
return Math.acos(cos);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
function determinant(v1, v2) {
|
|
457
|
+
return v1.x * v2.y - v1.y * v2.x;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
function createCatmullRomCurves(points, sourceTangent, targetTangent, options) {
|
|
461
|
+
const { tau, coeff } = options;
|
|
462
|
+
const distances = [];
|
|
463
|
+
const tangents = [];
|
|
464
|
+
const catmullRomCurves = [];
|
|
465
|
+
const n = points.length - 1;
|
|
466
|
+
|
|
467
|
+
for (let i = 0; i < n; i++) {
|
|
468
|
+
distances[i] = points[i].distance(points[i + 1]);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
tangents[0] = sourceTangent;
|
|
472
|
+
tangents[n] = targetTangent;
|
|
473
|
+
|
|
474
|
+
// The calculation of tangents of vertices
|
|
475
|
+
for (let i = 1; i < n; i++) {
|
|
476
|
+
let tpPrev;
|
|
477
|
+
let tpNext;
|
|
478
|
+
if (i === 1) {
|
|
479
|
+
tpPrev = points[i - 1].clone().offset(tangents[i - 1].x, tangents[i - 1].y);
|
|
480
|
+
} else {
|
|
481
|
+
tpPrev = points[i - 1].clone();
|
|
482
|
+
}
|
|
483
|
+
if (i === n - 1) {
|
|
484
|
+
tpNext = points[i + 1].clone().offset(tangents[i + 1].x, tangents[i + 1].y);
|
|
485
|
+
} else {
|
|
486
|
+
tpNext = points[i + 1].clone();
|
|
487
|
+
}
|
|
488
|
+
const v1 = tpPrev.difference(points[i]).normalize();
|
|
489
|
+
const v2 = tpNext.difference(points[i]).normalize();
|
|
490
|
+
const vAngle = angleBetweenVectors(v1, v2);
|
|
491
|
+
|
|
492
|
+
let rot = (Math.PI - vAngle) / 2;
|
|
493
|
+
let t;
|
|
494
|
+
const vectorDeterminant = determinant(v1, v2);
|
|
495
|
+
let pointsDeterminant;
|
|
496
|
+
pointsDeterminant = determinant(points[i].difference(points[i + 1]), points[i].difference(points[i - 1]));
|
|
497
|
+
if (vectorDeterminant < 0) {
|
|
498
|
+
rot = -rot;
|
|
499
|
+
}
|
|
500
|
+
if ((vAngle < Math.PI / 2) && ((rot < 0 && pointsDeterminant < 0) || (rot > 0 && pointsDeterminant > 0))) {
|
|
501
|
+
rot = rot - Math.PI;
|
|
502
|
+
}
|
|
503
|
+
t = v2.clone();
|
|
504
|
+
rotateVector(t, rot);
|
|
505
|
+
|
|
506
|
+
const t1 = t.clone();
|
|
507
|
+
const t2 = t.clone();
|
|
508
|
+
const scaleFactor1 = distances[i - 1] * coeff;
|
|
509
|
+
const scaleFactor2 = distances[i] * coeff;
|
|
510
|
+
t1.scale(scaleFactor1, scaleFactor1);
|
|
511
|
+
t2.scale(scaleFactor2, scaleFactor2);
|
|
512
|
+
|
|
513
|
+
tangents[i] = [t1, t2];
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// The building of a Catmull-Rom curve based of tangents of points
|
|
517
|
+
for (let i = 0; i < n; i++) {
|
|
518
|
+
let p0;
|
|
519
|
+
let p3;
|
|
520
|
+
if (i === 0) {
|
|
521
|
+
p0 = points[i + 1].difference(tangents[i].x / tau, tangents[i].y / tau);
|
|
522
|
+
} else {
|
|
523
|
+
p0 = points[i + 1].difference(tangents[i][1].x / tau, tangents[i][1].y / tau);
|
|
524
|
+
}
|
|
525
|
+
if (i === n - 1) {
|
|
526
|
+
p3 = points[i].clone().offset(tangents[i + 1].x / tau, tangents[i + 1].y / tau);
|
|
527
|
+
} else {
|
|
528
|
+
p3 = points[i].difference(tangents[i + 1][0].x / tau, tangents[i + 1][0].y / tau);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
catmullRomCurves[i] = [p0, points[i], points[i + 1], p3];
|
|
532
|
+
}
|
|
533
|
+
return catmullRomCurves;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// The function to convert Catmull-Rom curve to Bezier curve using the tension (tau)
|
|
537
|
+
function catmullRomToBezier(points, options) {
|
|
538
|
+
const { tau } = options;
|
|
539
|
+
|
|
540
|
+
const bcp1 = new Point();
|
|
541
|
+
bcp1.x = points[1].x + (points[2].x - points[0].x) / (6 * tau);
|
|
542
|
+
bcp1.y = points[1].y + (points[2].y - points[0].y) / (6 * tau);
|
|
543
|
+
|
|
544
|
+
const bcp2 = new Point();
|
|
545
|
+
bcp2.x = points[2].x + (points[3].x - points[1].x) / (6 * tau);
|
|
546
|
+
bcp2.y = points[2].y + (points[3].y - points[1].y) / (6 * tau);
|
|
547
|
+
return new Curve(
|
|
548
|
+
points[1],
|
|
549
|
+
bcp1,
|
|
550
|
+
bcp2,
|
|
551
|
+
points[2]
|
|
552
|
+
);
|
|
553
|
+
}
|