@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,452 @@
|
|
|
1
|
+
import * as util from '../util/index.mjs';
|
|
2
|
+
import * as g from '../g/index.mjs';
|
|
3
|
+
|
|
4
|
+
// default size of jump if not specified in options
|
|
5
|
+
var JUMP_SIZE = 5;
|
|
6
|
+
|
|
7
|
+
// available jump types
|
|
8
|
+
// first one taken as default
|
|
9
|
+
var JUMP_TYPES = ['arc', 'gap', 'cubic'];
|
|
10
|
+
|
|
11
|
+
// default radius
|
|
12
|
+
var RADIUS = 0;
|
|
13
|
+
|
|
14
|
+
// takes care of math. error for case when jump is too close to end of line
|
|
15
|
+
var CLOSE_PROXIMITY_PADDING = 1;
|
|
16
|
+
|
|
17
|
+
// list of connector types not to jump over.
|
|
18
|
+
var IGNORED_CONNECTORS = ['smooth'];
|
|
19
|
+
|
|
20
|
+
// internal constants for round segment
|
|
21
|
+
var _13 = 1 / 3;
|
|
22
|
+
var _23 = 2 / 3;
|
|
23
|
+
|
|
24
|
+
function sortPointsAscending(p1, p2) {
|
|
25
|
+
|
|
26
|
+
let { x: x1, y: y1 } = p1;
|
|
27
|
+
let { x: x2, y: y2 } = p2;
|
|
28
|
+
|
|
29
|
+
if (x1 > x2) {
|
|
30
|
+
|
|
31
|
+
let swap = x1;
|
|
32
|
+
x1 = x2;
|
|
33
|
+
x2 = swap;
|
|
34
|
+
|
|
35
|
+
swap = y1;
|
|
36
|
+
y1 = y2;
|
|
37
|
+
y2 = swap;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (y1 > y2) {
|
|
41
|
+
let swap = x1;
|
|
42
|
+
x1 = x2;
|
|
43
|
+
x2 = swap;
|
|
44
|
+
|
|
45
|
+
swap = y1;
|
|
46
|
+
y1 = y2;
|
|
47
|
+
y2 = swap;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return [new g.Point(x1, y1), new g.Point(x2, y2)];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function overlapExists(line1, line2) {
|
|
54
|
+
|
|
55
|
+
const [{ x: x1, y: y1 }, { x: x2, y: y2 }] = sortPointsAscending(line1.start, line1.end);
|
|
56
|
+
const [{ x: x3, y: y3 }, { x: x4, y: y4 }] = sortPointsAscending(line2.start, line2.end);
|
|
57
|
+
|
|
58
|
+
const xMatch = x1 <= x4 && x3 <= x2;
|
|
59
|
+
const yMatch = y1 <= y4 && y3 <= y2;
|
|
60
|
+
|
|
61
|
+
return xMatch && yMatch;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Transform start/end and route into series of lines
|
|
66
|
+
* @param {g.point} sourcePoint start point
|
|
67
|
+
* @param {g.point} targetPoint end point
|
|
68
|
+
* @param {g.point[]} route optional list of route
|
|
69
|
+
* @return {g.line[]} [description]
|
|
70
|
+
*/
|
|
71
|
+
function createLines(sourcePoint, targetPoint, route) {
|
|
72
|
+
// make a flattened array of all points
|
|
73
|
+
var points = [].concat(sourcePoint, route, targetPoint);
|
|
74
|
+
return points.reduce(function(resultLines, point, idx) {
|
|
75
|
+
// if there is a next point, make a line with it
|
|
76
|
+
var nextPoint = points[idx + 1];
|
|
77
|
+
if (nextPoint != null) {
|
|
78
|
+
resultLines[idx] = g.line(point, nextPoint);
|
|
79
|
+
}
|
|
80
|
+
return resultLines;
|
|
81
|
+
}, []);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function setupUpdating(jumpOverLinkView) {
|
|
85
|
+
var paper = jumpOverLinkView.paper;
|
|
86
|
+
var updateList = paper._jumpOverUpdateList;
|
|
87
|
+
|
|
88
|
+
// first time setup for this paper
|
|
89
|
+
if (updateList == null) {
|
|
90
|
+
updateList = paper._jumpOverUpdateList = [];
|
|
91
|
+
var graph = paper.model;
|
|
92
|
+
graph.on('batch:stop', function() {
|
|
93
|
+
if (this.hasActiveBatch()) return;
|
|
94
|
+
updateJumpOver(paper);
|
|
95
|
+
});
|
|
96
|
+
graph.on('reset', function() {
|
|
97
|
+
updateList = paper._jumpOverUpdateList = [];
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// add this link to a list so it can be updated when some other link is updated
|
|
102
|
+
if (updateList.indexOf(jumpOverLinkView) < 0) {
|
|
103
|
+
updateList.push(jumpOverLinkView);
|
|
104
|
+
|
|
105
|
+
// watch for change of connector type or removal of link itself
|
|
106
|
+
// to remove the link from a list of jump over connectors
|
|
107
|
+
jumpOverLinkView.listenToOnce(jumpOverLinkView.model, 'change:connector remove', function() {
|
|
108
|
+
updateList.splice(updateList.indexOf(jumpOverLinkView), 1);
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Handler for a batch:stop event to force
|
|
115
|
+
* update of all registered links with jump over connector
|
|
116
|
+
* @param {object} batchEvent optional object with info about batch
|
|
117
|
+
*/
|
|
118
|
+
function updateJumpOver(paper) {
|
|
119
|
+
var updateList = paper._jumpOverUpdateList;
|
|
120
|
+
for (var i = 0; i < updateList.length; i++) {
|
|
121
|
+
const linkView = updateList[i];
|
|
122
|
+
const updateFlag = linkView.getFlag(linkView.constructor.Flags.CONNECTOR);
|
|
123
|
+
linkView.requestUpdate(updateFlag);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Utility function to collect all intersection points of a single
|
|
129
|
+
* line against group of other lines.
|
|
130
|
+
* @param {g.line} line where to find points
|
|
131
|
+
* @param {g.line[]} crossCheckLines lines to cross
|
|
132
|
+
* @return {g.point[]} list of intersection points
|
|
133
|
+
*/
|
|
134
|
+
function findLineIntersections(line, crossCheckLines) {
|
|
135
|
+
return util.toArray(crossCheckLines).reduce(function(res, crossCheckLine) {
|
|
136
|
+
var intersection = line.intersection(crossCheckLine);
|
|
137
|
+
if (intersection) {
|
|
138
|
+
res.push(intersection);
|
|
139
|
+
}
|
|
140
|
+
return res;
|
|
141
|
+
}, []);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Sorting function for list of points by their distance.
|
|
146
|
+
* @param {g.point} p1 first point
|
|
147
|
+
* @param {g.point} p2 second point
|
|
148
|
+
* @return {number} squared distance between points
|
|
149
|
+
*/
|
|
150
|
+
function sortPoints(p1, p2) {
|
|
151
|
+
return g.line(p1, p2).squaredLength();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Split input line into multiple based on intersection points.
|
|
156
|
+
* @param {g.line} line input line to split
|
|
157
|
+
* @param {g.point[]} intersections points where to split the line
|
|
158
|
+
* @param {number} jumpSize the size of jump arc (length empty spot on a line)
|
|
159
|
+
* @return {g.line[]} list of lines being split
|
|
160
|
+
*/
|
|
161
|
+
function createJumps(line, intersections, jumpSize) {
|
|
162
|
+
return intersections.reduce(function(resultLines, point, idx) {
|
|
163
|
+
// skipping points that were merged with the previous line
|
|
164
|
+
// to make bigger arc over multiple lines that are close to each other
|
|
165
|
+
if (point.skip === true) {
|
|
166
|
+
return resultLines;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// always grab the last line from buffer and modify it
|
|
170
|
+
var lastLine = resultLines.pop() || line;
|
|
171
|
+
|
|
172
|
+
// calculate start and end of jump by moving by a given size of jump
|
|
173
|
+
var jumpStart = g.point(point).move(lastLine.start, -(jumpSize));
|
|
174
|
+
var jumpEnd = g.point(point).move(lastLine.start, +(jumpSize));
|
|
175
|
+
|
|
176
|
+
// now try to look at the next intersection point
|
|
177
|
+
var nextPoint = intersections[idx + 1];
|
|
178
|
+
if (nextPoint != null) {
|
|
179
|
+
var distance = jumpEnd.distance(nextPoint);
|
|
180
|
+
if (distance <= jumpSize) {
|
|
181
|
+
// next point is close enough, move the jump end by this
|
|
182
|
+
// difference and mark the next point to be skipped
|
|
183
|
+
jumpEnd = nextPoint.move(lastLine.start, distance);
|
|
184
|
+
nextPoint.skip = true;
|
|
185
|
+
}
|
|
186
|
+
} else {
|
|
187
|
+
// this block is inside of `else` as an optimization so the distance is
|
|
188
|
+
// not calculated when we know there are no other intersection points
|
|
189
|
+
var endDistance = jumpStart.distance(lastLine.end);
|
|
190
|
+
// if the end is too close to possible jump, draw remaining line instead of a jump
|
|
191
|
+
if (endDistance < jumpSize * 2 + CLOSE_PROXIMITY_PADDING) {
|
|
192
|
+
resultLines.push(lastLine);
|
|
193
|
+
return resultLines;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
var startDistance = jumpEnd.distance(lastLine.start);
|
|
198
|
+
if (startDistance < jumpSize * 2 + CLOSE_PROXIMITY_PADDING) {
|
|
199
|
+
// if the start of line is too close to jump, draw that line instead of a jump
|
|
200
|
+
resultLines.push(lastLine);
|
|
201
|
+
return resultLines;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// finally create a jump line
|
|
205
|
+
var jumpLine = g.line(jumpStart, jumpEnd);
|
|
206
|
+
// it's just simple line but with a `isJump` property
|
|
207
|
+
jumpLine.isJump = true;
|
|
208
|
+
|
|
209
|
+
resultLines.push(
|
|
210
|
+
g.line(lastLine.start, jumpStart),
|
|
211
|
+
jumpLine,
|
|
212
|
+
g.line(jumpEnd, lastLine.end)
|
|
213
|
+
);
|
|
214
|
+
return resultLines;
|
|
215
|
+
}, []);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Assemble `D` attribute of a SVG path by iterating given lines.
|
|
220
|
+
* @param {g.line[]} lines source lines to use
|
|
221
|
+
* @param {number} jumpSize the size of jump arc (length empty spot on a line)
|
|
222
|
+
* @param {number} radius the radius
|
|
223
|
+
* @return {string}
|
|
224
|
+
*/
|
|
225
|
+
function buildPath(lines, jumpSize, jumpType, radius) {
|
|
226
|
+
|
|
227
|
+
var path = new g.Path();
|
|
228
|
+
var segment;
|
|
229
|
+
|
|
230
|
+
// first move to the start of a first line
|
|
231
|
+
segment = g.Path.createSegment('M', lines[0].start);
|
|
232
|
+
path.appendSegment(segment);
|
|
233
|
+
|
|
234
|
+
// make a paths from lines
|
|
235
|
+
util.toArray(lines).forEach(function(line, index) {
|
|
236
|
+
|
|
237
|
+
if (line.isJump) {
|
|
238
|
+
var angle, diff;
|
|
239
|
+
|
|
240
|
+
var control1, control2;
|
|
241
|
+
|
|
242
|
+
if (jumpType === 'arc') { // approximates semicircle with 2 curves
|
|
243
|
+
angle = -90;
|
|
244
|
+
// determine rotation of arc based on difference between points
|
|
245
|
+
diff = line.start.difference(line.end);
|
|
246
|
+
// make sure the arc always points up (or right)
|
|
247
|
+
var xAxisRotate = Number((diff.x < 0) || (diff.x === 0 && diff.y < 0));
|
|
248
|
+
if (xAxisRotate) angle += 180;
|
|
249
|
+
|
|
250
|
+
var midpoint = line.midpoint();
|
|
251
|
+
var centerLine = new g.Line(midpoint, line.end).rotate(midpoint, angle);
|
|
252
|
+
|
|
253
|
+
var halfLine;
|
|
254
|
+
|
|
255
|
+
// first half
|
|
256
|
+
halfLine = new g.Line(line.start, midpoint);
|
|
257
|
+
|
|
258
|
+
control1 = halfLine.pointAt(2 / 3).rotate(line.start, angle);
|
|
259
|
+
control2 = centerLine.pointAt(1 / 3).rotate(centerLine.end, -angle);
|
|
260
|
+
|
|
261
|
+
segment = g.Path.createSegment('C', control1, control2, centerLine.end);
|
|
262
|
+
path.appendSegment(segment);
|
|
263
|
+
|
|
264
|
+
// second half
|
|
265
|
+
halfLine = new g.Line(midpoint, line.end);
|
|
266
|
+
|
|
267
|
+
control1 = centerLine.pointAt(1 / 3).rotate(centerLine.end, angle);
|
|
268
|
+
control2 = halfLine.pointAt(1 / 3).rotate(line.end, -angle);
|
|
269
|
+
|
|
270
|
+
segment = g.Path.createSegment('C', control1, control2, line.end);
|
|
271
|
+
path.appendSegment(segment);
|
|
272
|
+
|
|
273
|
+
} else if (jumpType === 'gap') {
|
|
274
|
+
segment = g.Path.createSegment('M', line.end);
|
|
275
|
+
path.appendSegment(segment);
|
|
276
|
+
|
|
277
|
+
} else if (jumpType === 'cubic') { // approximates semicircle with 1 curve
|
|
278
|
+
angle = line.start.theta(line.end);
|
|
279
|
+
|
|
280
|
+
var xOffset = jumpSize * 0.6;
|
|
281
|
+
var yOffset = jumpSize * 1.35;
|
|
282
|
+
|
|
283
|
+
// determine rotation of arc based on difference between points
|
|
284
|
+
diff = line.start.difference(line.end);
|
|
285
|
+
// make sure the arc always points up (or right)
|
|
286
|
+
xAxisRotate = Number((diff.x < 0) || (diff.x === 0 && diff.y < 0));
|
|
287
|
+
if (xAxisRotate) yOffset *= -1;
|
|
288
|
+
|
|
289
|
+
control1 = g.Point(line.start.x + xOffset, line.start.y + yOffset).rotate(line.start, angle);
|
|
290
|
+
control2 = g.Point(line.end.x - xOffset, line.end.y + yOffset).rotate(line.end, angle);
|
|
291
|
+
|
|
292
|
+
segment = g.Path.createSegment('C', control1, control2, line.end);
|
|
293
|
+
path.appendSegment(segment);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
} else {
|
|
297
|
+
var nextLine = lines[index + 1];
|
|
298
|
+
if (radius == 0 || !nextLine || nextLine.isJump) {
|
|
299
|
+
segment = g.Path.createSegment('L', line.end);
|
|
300
|
+
path.appendSegment(segment);
|
|
301
|
+
} else {
|
|
302
|
+
buildRoundedSegment(radius, path, line.end, line.start, nextLine.end);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
return path;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function buildRoundedSegment(offset, path, curr, prev, next) {
|
|
311
|
+
var prevDistance = curr.distance(prev) / 2;
|
|
312
|
+
var nextDistance = curr.distance(next) / 2;
|
|
313
|
+
|
|
314
|
+
var startMove = -Math.min(offset, prevDistance);
|
|
315
|
+
var endMove = -Math.min(offset, nextDistance);
|
|
316
|
+
|
|
317
|
+
var roundedStart = curr.clone().move(prev, startMove).round();
|
|
318
|
+
var roundedEnd = curr.clone().move(next, endMove).round();
|
|
319
|
+
|
|
320
|
+
var control1 = new g.Point((_13 * roundedStart.x) + (_23 * curr.x), (_23 * curr.y) + (_13 * roundedStart.y));
|
|
321
|
+
var control2 = new g.Point((_13 * roundedEnd.x) + (_23 * curr.x), (_23 * curr.y) + (_13 * roundedEnd.y));
|
|
322
|
+
|
|
323
|
+
var segment;
|
|
324
|
+
segment = g.Path.createSegment('L', roundedStart);
|
|
325
|
+
path.appendSegment(segment);
|
|
326
|
+
|
|
327
|
+
segment = g.Path.createSegment('C', control1, control2, roundedEnd);
|
|
328
|
+
path.appendSegment(segment);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Actual connector function that will be run on every update.
|
|
333
|
+
* @param {g.point} sourcePoint start point of this link
|
|
334
|
+
* @param {g.point} targetPoint end point of this link
|
|
335
|
+
* @param {g.point[]} route of this link
|
|
336
|
+
* @param {object} opt options
|
|
337
|
+
* @property {number} size optional size of a jump arc
|
|
338
|
+
* @return {string} created `D` attribute of SVG path
|
|
339
|
+
*/
|
|
340
|
+
export const jumpover = function(sourcePoint, targetPoint, route, opt) { // eslint-disable-line max-params
|
|
341
|
+
|
|
342
|
+
setupUpdating(this);
|
|
343
|
+
|
|
344
|
+
var raw = opt.raw;
|
|
345
|
+
var jumpSize = opt.size || JUMP_SIZE;
|
|
346
|
+
var jumpType = opt.jump && ('' + opt.jump).toLowerCase();
|
|
347
|
+
var radius = opt.radius || RADIUS;
|
|
348
|
+
var ignoreConnectors = opt.ignoreConnectors || IGNORED_CONNECTORS;
|
|
349
|
+
|
|
350
|
+
// grab the first jump type as a default if specified one is invalid
|
|
351
|
+
if (JUMP_TYPES.indexOf(jumpType) === -1) {
|
|
352
|
+
jumpType = JUMP_TYPES[0];
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
var paper = this.paper;
|
|
356
|
+
var graph = paper.model;
|
|
357
|
+
var allLinks = graph.getLinks();
|
|
358
|
+
|
|
359
|
+
// there is just one link, draw it directly
|
|
360
|
+
if (allLinks.length === 1) {
|
|
361
|
+
return buildPath(
|
|
362
|
+
createLines(sourcePoint, targetPoint, route),
|
|
363
|
+
jumpSize, jumpType, radius
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
var thisModel = this.model;
|
|
368
|
+
var thisIndex = allLinks.indexOf(thisModel);
|
|
369
|
+
var defaultConnector = paper.options.defaultConnector || {};
|
|
370
|
+
|
|
371
|
+
// not all links are meant to be jumped over.
|
|
372
|
+
var links = allLinks.filter(function(link, idx) {
|
|
373
|
+
|
|
374
|
+
var connector = link.get('connector') || defaultConnector;
|
|
375
|
+
|
|
376
|
+
// avoid jumping over links with connector type listed in `ignored connectors`.
|
|
377
|
+
if (util.toArray(ignoreConnectors).includes(connector.name)) {
|
|
378
|
+
return false;
|
|
379
|
+
}
|
|
380
|
+
// filter out links that are above this one and have the same connector type
|
|
381
|
+
// otherwise there would double hoops for each intersection
|
|
382
|
+
if (idx > thisIndex) {
|
|
383
|
+
return connector.name !== 'jumpover';
|
|
384
|
+
}
|
|
385
|
+
return true;
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
// find views for all links
|
|
389
|
+
var linkViews = links.map(function(link) {
|
|
390
|
+
return paper.findViewByModel(link);
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
// create lines for this link
|
|
394
|
+
var thisLines = createLines(
|
|
395
|
+
sourcePoint,
|
|
396
|
+
targetPoint,
|
|
397
|
+
route
|
|
398
|
+
);
|
|
399
|
+
|
|
400
|
+
// create lines for all other links
|
|
401
|
+
var linkLines = linkViews.map(function(linkView) {
|
|
402
|
+
if (linkView == null) {
|
|
403
|
+
return [];
|
|
404
|
+
}
|
|
405
|
+
if (linkView === this) {
|
|
406
|
+
return thisLines;
|
|
407
|
+
}
|
|
408
|
+
return createLines(
|
|
409
|
+
linkView.sourcePoint,
|
|
410
|
+
linkView.targetPoint,
|
|
411
|
+
linkView.route
|
|
412
|
+
);
|
|
413
|
+
}, this);
|
|
414
|
+
|
|
415
|
+
// transform lines for this link by splitting with jump lines at
|
|
416
|
+
// points of intersection with other links
|
|
417
|
+
var jumpingLines = thisLines.reduce(function(resultLines, thisLine) {
|
|
418
|
+
// iterate all links and grab the intersections with this line
|
|
419
|
+
// these are then sorted by distance so the line can be split more easily
|
|
420
|
+
var intersections = links.reduce(function(res, link, i) {
|
|
421
|
+
// don't intersection with itself
|
|
422
|
+
if (link !== thisModel) {
|
|
423
|
+
|
|
424
|
+
const linkLinesToTest = linkLines[i].slice();
|
|
425
|
+
const overlapIndex = linkLinesToTest.findIndex((line) => overlapExists(thisLine, line));
|
|
426
|
+
|
|
427
|
+
// Overlap occurs and the end point of one segment lies on thisLine
|
|
428
|
+
if (overlapIndex > -1 && thisLine.containsPoint(linkLinesToTest[overlapIndex].end)) {
|
|
429
|
+
// Remove the next segment because there will never be a jump
|
|
430
|
+
linkLinesToTest.splice(overlapIndex + 1, 1);
|
|
431
|
+
}
|
|
432
|
+
const lineIntersections = findLineIntersections(thisLine, linkLinesToTest);
|
|
433
|
+
res.push.apply(res, lineIntersections);
|
|
434
|
+
}
|
|
435
|
+
return res;
|
|
436
|
+
}, []).sort(function(a, b) {
|
|
437
|
+
return sortPoints(thisLine.start, a) - sortPoints(thisLine.start, b);
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
if (intersections.length > 0) {
|
|
441
|
+
// split the line based on found intersection points
|
|
442
|
+
resultLines.push.apply(resultLines, createJumps(thisLine, intersections, jumpSize));
|
|
443
|
+
} else {
|
|
444
|
+
// without any intersection the line goes uninterrupted
|
|
445
|
+
resultLines.push(thisLine);
|
|
446
|
+
}
|
|
447
|
+
return resultLines;
|
|
448
|
+
}, []);
|
|
449
|
+
|
|
450
|
+
var path = buildPath(jumpingLines, jumpSize, jumpType, radius);
|
|
451
|
+
return (raw) ? path : path.serialize();
|
|
452
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { straight } from './straight.mjs';
|
|
2
|
+
|
|
3
|
+
export const normal = function(sourcePoint, targetPoint, route = [], opt = {}) {
|
|
4
|
+
|
|
5
|
+
const { raw } = opt;
|
|
6
|
+
const localOpt = {
|
|
7
|
+
cornerType: 'point',
|
|
8
|
+
raw
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
return straight(sourcePoint, targetPoint, route, localOpt);
|
|
12
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { straight } from './straight.mjs';
|
|
2
|
+
|
|
3
|
+
const CORNER_RADIUS = 10;
|
|
4
|
+
const PRECISION = 0;
|
|
5
|
+
|
|
6
|
+
export const rounded = function(sourcePoint, targetPoint, route = [], opt = {}) {
|
|
7
|
+
|
|
8
|
+
const { radius = CORNER_RADIUS, raw } = opt;
|
|
9
|
+
const localOpt = {
|
|
10
|
+
cornerType: 'cubic',
|
|
11
|
+
cornerRadius: radius,
|
|
12
|
+
precision: PRECISION,
|
|
13
|
+
raw
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
return straight(sourcePoint, targetPoint, route, localOpt);
|
|
17
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import * as g from '../g/index.mjs';
|
|
2
|
+
|
|
3
|
+
export const smooth = function(sourcePoint, targetPoint, route, opt) {
|
|
4
|
+
|
|
5
|
+
var raw = opt && opt.raw;
|
|
6
|
+
var path;
|
|
7
|
+
|
|
8
|
+
if (route && route.length !== 0) {
|
|
9
|
+
|
|
10
|
+
var points = [sourcePoint].concat(route).concat([targetPoint]);
|
|
11
|
+
var curves = g.Curve.throughPoints(points);
|
|
12
|
+
|
|
13
|
+
path = new g.Path(curves);
|
|
14
|
+
|
|
15
|
+
} else {
|
|
16
|
+
// if we have no route, use a default cubic bezier curve
|
|
17
|
+
// cubic bezier requires two control points
|
|
18
|
+
// the control points have `x` midway between source and target
|
|
19
|
+
// this produces an S-like curve
|
|
20
|
+
|
|
21
|
+
path = new g.Path();
|
|
22
|
+
|
|
23
|
+
var segment;
|
|
24
|
+
|
|
25
|
+
segment = g.Path.createSegment('M', sourcePoint);
|
|
26
|
+
path.appendSegment(segment);
|
|
27
|
+
|
|
28
|
+
if ((Math.abs(sourcePoint.x - targetPoint.x)) >= (Math.abs(sourcePoint.y - targetPoint.y))) {
|
|
29
|
+
var controlPointX = (sourcePoint.x + targetPoint.x) / 2;
|
|
30
|
+
|
|
31
|
+
segment = g.Path.createSegment('C', controlPointX, sourcePoint.y, controlPointX, targetPoint.y, targetPoint.x, targetPoint.y);
|
|
32
|
+
path.appendSegment(segment);
|
|
33
|
+
|
|
34
|
+
} else {
|
|
35
|
+
var controlPointY = (sourcePoint.y + targetPoint.y) / 2;
|
|
36
|
+
|
|
37
|
+
segment = g.Path.createSegment('C', sourcePoint.x, controlPointY, targetPoint.x, controlPointY, targetPoint.x, targetPoint.y);
|
|
38
|
+
path.appendSegment(segment);
|
|
39
|
+
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return (raw) ? path : path.serialize();
|
|
44
|
+
};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import * as g from '../g/index.mjs';
|
|
2
|
+
|
|
3
|
+
const CornerTypes = {
|
|
4
|
+
POINT: 'point',
|
|
5
|
+
CUBIC: 'cubic',
|
|
6
|
+
LINE: 'line',
|
|
7
|
+
GAP: 'gap'
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const DEFINED_CORNER_TYPES = Object.values(CornerTypes);
|
|
11
|
+
|
|
12
|
+
const CORNER_RADIUS = 10;
|
|
13
|
+
const PRECISION = 1;
|
|
14
|
+
|
|
15
|
+
export const straight = function(sourcePoint, targetPoint, routePoints = [], opt = {}) {
|
|
16
|
+
|
|
17
|
+
const {
|
|
18
|
+
cornerType = CornerTypes.POINT,
|
|
19
|
+
cornerRadius = CORNER_RADIUS,
|
|
20
|
+
cornerPreserveAspectRatio = false,
|
|
21
|
+
precision = PRECISION,
|
|
22
|
+
raw = false
|
|
23
|
+
} = opt;
|
|
24
|
+
|
|
25
|
+
if (DEFINED_CORNER_TYPES.indexOf(cornerType) === -1) {
|
|
26
|
+
// unknown `cornerType` provided => error
|
|
27
|
+
throw new Error('Invalid `cornerType` provided to `straight` connector.');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let path;
|
|
31
|
+
|
|
32
|
+
if ((cornerType === CornerTypes.POINT) || !cornerRadius) {
|
|
33
|
+
// default option => normal connector
|
|
34
|
+
// simply connect all points with straight lines
|
|
35
|
+
const points = [sourcePoint].concat(routePoints).concat([targetPoint]);
|
|
36
|
+
const polyline = new g.Polyline(points);
|
|
37
|
+
path = new g.Path(polyline);
|
|
38
|
+
|
|
39
|
+
} else {
|
|
40
|
+
// `cornerType` is not unknown and not 'point' (default) => must be one of other valid types
|
|
41
|
+
path = new g.Path();
|
|
42
|
+
|
|
43
|
+
// add initial gap segment = to source point
|
|
44
|
+
path.appendSegment(g.Path.createSegment('M', sourcePoint));
|
|
45
|
+
|
|
46
|
+
let nextDistance;
|
|
47
|
+
const routePointsLength = routePoints.length;
|
|
48
|
+
for (let i = 0; i < routePointsLength; i++) {
|
|
49
|
+
|
|
50
|
+
const curr = new g.Point(routePoints[i]);
|
|
51
|
+
const prev = (routePoints[i - 1] || sourcePoint);
|
|
52
|
+
const next = (routePoints[i + 1] || targetPoint);
|
|
53
|
+
const prevDistance = (nextDistance || (curr.distance(prev) / 2)); // try to re-use previously-computed `nextDistance`
|
|
54
|
+
nextDistance = (curr.distance(next) / 2);
|
|
55
|
+
|
|
56
|
+
let startMove, endMove;
|
|
57
|
+
if (!cornerPreserveAspectRatio) {
|
|
58
|
+
// `startMove` and `endMove` may be different
|
|
59
|
+
// (this happens when next or previous path point is closer than `2 * cornerRadius`)
|
|
60
|
+
startMove = -Math.min(cornerRadius, prevDistance);
|
|
61
|
+
endMove = -Math.min(cornerRadius, nextDistance);
|
|
62
|
+
} else {
|
|
63
|
+
// force `startMove` and `endMove` to be the same
|
|
64
|
+
startMove = endMove = -Math.min(cornerRadius, prevDistance, nextDistance);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// to find `cornerStart` and `cornerEnd`, the logic is as follows (using `cornerStart` as example):
|
|
68
|
+
// - find a point lying on the line `prev - startMove` such that...
|
|
69
|
+
// - ...the point lies `abs(startMove)` distance away from `curr`...
|
|
70
|
+
// - ...and its coordinates are rounded to whole numbers
|
|
71
|
+
const cornerStart = curr.clone().move(prev, startMove).round(precision);
|
|
72
|
+
const cornerEnd = curr.clone().move(next, endMove).round(precision);
|
|
73
|
+
|
|
74
|
+
// add in-between straight segment = from previous route point to corner start point
|
|
75
|
+
// (may have zero length)
|
|
76
|
+
path.appendSegment(g.Path.createSegment('L', cornerStart));
|
|
77
|
+
|
|
78
|
+
// add corner segment = from corner start point to corner end point
|
|
79
|
+
switch (cornerType) {
|
|
80
|
+
case CornerTypes.CUBIC: {
|
|
81
|
+
// corner is rounded
|
|
82
|
+
const _13 = (1 / 3);
|
|
83
|
+
const _23 = (2 / 3);
|
|
84
|
+
const control1 = new g.Point((_13 * cornerStart.x) + (_23 * curr.x), (_23 * curr.y) + (_13 * cornerStart.y));
|
|
85
|
+
const control2 = new g.Point((_13 * cornerEnd.x) + (_23 * curr.x), (_23 * curr.y) + (_13 * cornerEnd.y));
|
|
86
|
+
path.appendSegment(g.Path.createSegment('C', control1, control2, cornerEnd));
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
case CornerTypes.LINE: {
|
|
90
|
+
// corner has bevel
|
|
91
|
+
path.appendSegment(g.Path.createSegment('L', cornerEnd));
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
case CornerTypes.GAP: {
|
|
95
|
+
// corner has empty space
|
|
96
|
+
path.appendSegment(g.Path.createSegment('M', cornerEnd));
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
// default: no segment is created
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// add final straight segment = from last corner end point to target point
|
|
104
|
+
// (= or from start point to end point, if there are no route points)
|
|
105
|
+
// (may have zero length)
|
|
106
|
+
path.appendSegment(g.Path.createSegment('L', targetPoint));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return ((raw) ? path : path.serialize());
|
|
110
|
+
};
|