@leafer-in/linker 2.3.0 → 2.3.2

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.
@@ -0,0 +1,569 @@
1
+ "use strict";
2
+
3
+ var draw = require("@leafer-ui/draw");
4
+
5
+ var core = require("@leafer-ui/core");
6
+
7
+ function __decorate(decorators, target, key, desc) {
8
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
9
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
10
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
11
+ }
12
+
13
+ typeof SuppressedError === "function" ? SuppressedError : function(error, suppressed, message) {
14
+ var e = new Error(message);
15
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
16
+ };
17
+
18
+ class LinkerData extends draw.LineData {
19
+ get __usePathBox() {
20
+ return this.path || this.__pathInputed;
21
+ }
22
+ }
23
+
24
+ const {directionData: directionData} = draw.AroundHelper;
25
+
26
+ const directionNames = [ "top-left", "top", "top-right", "right", "bottom-right", "bottom", "bottom-left", "left", "center" ];
27
+
28
+ const LinkerHelper = {
29
+ getCtrlPoint(linker, node, point, direction, ctrl, offsetAngle) {
30
+ if (node) {
31
+ point = node.getBoxPoint(point, linker);
32
+ }
33
+ let ctrlPoint;
34
+ switch (direction) {
35
+ case "left":
36
+ case "top-left":
37
+ case "bottom-left":
38
+ ctrlPoint = {
39
+ x: point.x - ctrl.x,
40
+ y: point.y
41
+ };
42
+ break;
43
+
44
+ case "top":
45
+ ctrlPoint = {
46
+ x: point.x,
47
+ y: point.y - ctrl.y
48
+ };
49
+ break;
50
+
51
+ case "right":
52
+ case "top-right":
53
+ case "bottom-right":
54
+ ctrlPoint = {
55
+ x: point.x + ctrl.x,
56
+ y: point.y
57
+ };
58
+ break;
59
+
60
+ case "bottom":
61
+ ctrlPoint = {
62
+ x: point.x,
63
+ y: point.y + ctrl.y
64
+ };
65
+ break;
66
+
67
+ default:
68
+ ctrlPoint = {
69
+ x: point.x,
70
+ y: point.y
71
+ };
72
+ }
73
+ if (offsetAngle) {
74
+ offsetAngle = offsetAngle / 180 * Math.PI;
75
+ ctrlPoint = {
76
+ x: (ctrlPoint.x - point.x) * Math.cos(offsetAngle) - (ctrlPoint.y - point.y) * Math.sin(offsetAngle) + point.x,
77
+ y: (ctrlPoint.x - point.x) * Math.sin(offsetAngle) + (ctrlPoint.y - point.y) * Math.cos(offsetAngle) + point.y
78
+ };
79
+ }
80
+ if (node) node.getWorldPointByBox(ctrlPoint, linker, undefined, true);
81
+ return ctrlPoint;
82
+ },
83
+ isOppositeDirection(from, to) {
84
+ switch (from) {
85
+ case "left":
86
+ return to == "right";
87
+
88
+ case "top":
89
+ return to == "bottom";
90
+
91
+ case "right":
92
+ return to == "left";
93
+
94
+ case "bottom":
95
+ return to == "top";
96
+
97
+ case "center":
98
+ return to == "center";
99
+
100
+ default:
101
+ return false;
102
+ }
103
+ },
104
+ getDirectionForFromPoint(from, to) {
105
+ const a = from.x - to.x;
106
+ const b = from.y - to.y;
107
+ let direction;
108
+ let angle = 180 * Math.atan2(b, a) / Math.PI;
109
+ if (angle < 0) angle = 360 + angle;
110
+ if (angle > 315 || angle < 45) {
111
+ direction = "left";
112
+ } else if (angle > 45 && angle < 135) {
113
+ direction = "top";
114
+ } else if (angle > 135 && angle < 225) {
115
+ direction = "right";
116
+ } else {
117
+ direction = "bottom";
118
+ }
119
+ return direction;
120
+ },
121
+ getRotateDirection(direction, rotation, totalDirection = 8) {
122
+ let directionIndex = draw.Direction9[direction];
123
+ directionIndex = (directionIndex + Math.round(rotation / (360 / totalDirection))) % totalDirection;
124
+ if (directionIndex < 0) directionIndex += totalDirection;
125
+ return directionNames[directionIndex];
126
+ },
127
+ getDirection: getDirection,
128
+ getPointDirection: getPointDirection,
129
+ autoRotatePoint(point, origin, ui) {
130
+ const {rotation: rotation, scaleX: scaleX, scaleY: scaleY} = ui;
131
+ const oneFlip = scaleX * scaleY < 0;
132
+ if (rotation || oneFlip) draw.PointHelper.rotate(point, oneFlip ? -rotation : rotation, origin);
133
+ }
134
+ };
135
+
136
+ function getPointDirection(boxPoint, bounds, eight) {
137
+ const {width: width, height: height} = bounds;
138
+ const hitRadiusX = width / 4;
139
+ const hitRadiusY = height / 4;
140
+ for (let i = 0; i < directionData.length; i++) {
141
+ const unit = directionData[i];
142
+ const px = unit.x * width;
143
+ const py = unit.y * height;
144
+ const dx = boxPoint.x - px;
145
+ const dy = boxPoint.y - py;
146
+ if (Math.abs(dx) <= hitRadiusX && Math.abs(dy) <= hitRadiusY) {
147
+ if (eight) {
148
+ if (directionNames[i] !== "center") return directionNames[i];
149
+ } else return directionNames[i];
150
+ }
151
+ }
152
+ return undefined;
153
+ }
154
+
155
+ function getDirection(boxPoint, bounds, four) {
156
+ const {width: width, height: height} = bounds;
157
+ const center = draw.BoundsHelper.getPoint(bounds, "center", true);
158
+ const dx = boxPoint.x - center.x;
159
+ const dy = boxPoint.y - center.y;
160
+ if (!four) {
161
+ if (Math.abs(dx) < width / 4 && Math.abs(dy) < height / 4) return "center";
162
+ }
163
+ const ax = Math.abs(dx) * height;
164
+ const ay = Math.abs(dy) * width;
165
+ if (ax > ay) {
166
+ return dx > 0 ? "right" : "left";
167
+ }
168
+ return dy > 0 ? "bottom" : "top";
169
+ }
170
+
171
+ function linkerPointType(defaultValue) {
172
+ return draw.decorateLeafAttr(defaultValue, key => draw.attr({
173
+ set(value) {
174
+ if (this.__setAttr(key, value)) {
175
+ const layout = this.__layout;
176
+ if (!layout.linkerChanged) {
177
+ draw.doBoundsType(this);
178
+ draw.doStrokeType(this);
179
+ layout.linkerChanged = true;
180
+ }
181
+ }
182
+ }
183
+ }));
184
+ }
185
+
186
+ const {moveTo: moveTo, bezierCurveTo: bezierCurveTo, drawPoints: drawPoints$1} = draw.PathCommandDataHelper;
187
+
188
+ const {isEmpty: isEmpty, getPoint: getPoint} = draw.BoundsHelper;
189
+
190
+ const {getDistance: getDistance} = draw.PointHelper;
191
+
192
+ const {getCtrlPoint: getCtrlPoint, getDirectionForFromPoint: getDirectionForFromPoint, isOppositeDirection: isOppositeDirection} = LinkerHelper;
193
+
194
+ const {max: max, min: min, abs: abs} = Math;
195
+
196
+ exports.Linker = class Linker extends draw.Line {
197
+ get __tag() {
198
+ return "Linker";
199
+ }
200
+ constructor(data) {
201
+ super(data);
202
+ this.onStartNodeChange = this.onStartNodeChange.bind(this);
203
+ this.onEndNodeChange = this.onEndNodeChange.bind(this);
204
+ }
205
+ __updatePath() {
206
+ const layout = this.__layout;
207
+ const {startNode: startNode, endNode: endNode} = this;
208
+ if (layout.linkerChanged) {
209
+ this.updateLinkerNode("start");
210
+ this.updateLinkerNode("end");
211
+ this.updateLinkerPoint("start");
212
+ this.updateLinkerPoint("end");
213
+ if (startNode !== this.startNode) this.listenLinkerNode(this.startNode, startNode, "start");
214
+ if (endNode !== this.endNode) this.listenLinkerNode(this.endNode, endNode, "end");
215
+ layout.linkerChanged = false;
216
+ }
217
+ this.updateLinkerPath();
218
+ }
219
+ listenLinkerNode(newNode, oldNode, type) {
220
+ if (oldNode) {
221
+ oldNode.off(draw.BoundsEvent.WORLD, type === "end" ? this.onEndNodeChange : this.onStartNodeChange);
222
+ const list = type === "end" ? oldNode.endLinker : oldNode.startLinker;
223
+ if (list) {
224
+ const index = list.indexOf(this);
225
+ if (index > -1) list.splice(index, 1);
226
+ }
227
+ }
228
+ if (newNode) {
229
+ newNode.on(draw.BoundsEvent.WORLD, type === "end" ? this.onEndNodeChange : this.onStartNodeChange);
230
+ const list = type === "end" ? newNode.endLinker || (newNode.endLinker = []) : newNode.startLinker || (newNode.startLinker = []);
231
+ list.push(this);
232
+ }
233
+ }
234
+ onStartNodeChange() {
235
+ this.onLinkerNodeChange("start");
236
+ }
237
+ onEndNodeChange() {
238
+ this.onLinkerNodeChange("end");
239
+ }
240
+ onLinkerNodeChange(type) {
241
+ if (this.leaferIsCreated) {
242
+ const {layouter: layouter, zoomLayer: zoomLayer} = this.leafer;
243
+ if (layouter.__updatedList.has(zoomLayer)) return;
244
+ layouter.addExtra(this, true);
245
+ }
246
+ const {startData: startData, endData: endData} = this;
247
+ if (type == "start") {
248
+ this.updateLinkerPoint("start");
249
+ if (endData && endData.auto) this.updateLinkerPoint("end");
250
+ } else {
251
+ if (startData && startData.auto) this.updateLinkerPoint("start");
252
+ this.updateLinkerPoint("end");
253
+ }
254
+ this.__layout.boxChange();
255
+ draw.LeafHelper.updateBounds(this);
256
+ }
257
+ updateLinkerNode(type) {
258
+ const {startPoint: startPoint, endPoint: endPoint} = this;
259
+ const nowPoint = type === "end" ? endPoint : startPoint;
260
+ if (nowPoint) {
261
+ const {id: id} = nowPoint;
262
+ const ui = id && (draw.isString(id) ? this.app.findId(id) : id);
263
+ type === "end" ? this.endNode = ui : this.startNode = ui;
264
+ }
265
+ }
266
+ getLinkerNodeCenter(type) {
267
+ const {startPoint: startPoint, endPoint: endPoint, startNode: startNode, endNode: endNode, startData: startData, endData: endData} = this;
268
+ const node = type === "end" ? endNode : startNode;
269
+ const data = type === "end" ? endData || endPoint : startData || startPoint;
270
+ if (node) {
271
+ const {worldRenderBounds: worldRenderBounds} = node;
272
+ if (isEmpty(worldRenderBounds)) draw.LeafHelper.updateBounds(node);
273
+ return getPoint(node.worldRenderBounds, "center");
274
+ } else {
275
+ return data && draw.isObject(data.point) && this.getWorldPointByLocal(data.point);
276
+ }
277
+ }
278
+ updateLinkerPoint(type) {
279
+ const {startPoint: startPoint, endPoint: endPoint} = this;
280
+ const nowPoint = type === "end" ? endPoint : startPoint;
281
+ if (nowPoint) {
282
+ const data = {};
283
+ const {id: id, offset: offset} = nowPoint;
284
+ let {direction: direction, point: point} = nowPoint;
285
+ if (direction === "center" || !direction && id && !point) {
286
+ const start = this.getLinkerNodeCenter("start");
287
+ const end = this.getLinkerNodeCenter("end");
288
+ const from = type === "end" ? end : start;
289
+ const to = type === "end" ? start : end;
290
+ if (from && to) direction = getDirectionForFromPoint(from, to);
291
+ data.auto = true;
292
+ }
293
+ if (!direction && draw.isString(point)) direction = point;
294
+ data.direction = direction;
295
+ const ui = type === "end" ? this.endNode : this.startNode;
296
+ let p;
297
+ if (ui) {
298
+ if (!point) point = direction;
299
+ if (point) {
300
+ const {boxBounds: boxBounds} = ui;
301
+ if (isEmpty(boxBounds)) draw.LeafHelper.updateBounds(ui);
302
+ p = getPoint(boxBounds, point, true);
303
+ ui.getWorldPointByBox(p, this, false, true);
304
+ }
305
+ } else {
306
+ if (draw.isObject(point)) p = point;
307
+ }
308
+ if (p && offset) {
309
+ const o = Object.assign({}, offset);
310
+ if (!offset.x) o.x = 0;
311
+ if (!offset.y) o.y = 0;
312
+ if (ui) ui.getWorldPoint(o, this, true, true);
313
+ draw.PointHelper.move(p, o);
314
+ }
315
+ data.point = p;
316
+ type === "end" ? this.endData = p && data : this.startData = p && data;
317
+ }
318
+ }
319
+ updateLinkerPath() {
320
+ const data = this.__;
321
+ const path = data.path = [];
322
+ const hasPoints = data.points && data.points.length;
323
+ const {startData: startData, endData: endData, startNode: startNode, endNode: endNode} = this;
324
+ if (!startData || !endData) {
325
+ return;
326
+ }
327
+ const startPoint = startData.point, endPoint = endData.point;
328
+ const startDirection = startData.direction, endDirection = endData.direction;
329
+ const half = {
330
+ x: abs((startPoint.x - endPoint.x) / 2),
331
+ y: abs((startPoint.y - endPoint.y) / 2)
332
+ };
333
+ const arrowWidth = this.__.strokeWidth * 8;
334
+ let minCtrl;
335
+ if (this.__.__useArrow) {
336
+ const ratio = min(getDistance(startPoint, endPoint) / (arrowWidth * 25), 1);
337
+ minCtrl = max(arrowWidth * ratio * 12, arrowWidth * 3);
338
+ } else {
339
+ minCtrl = arrowWidth * 2;
340
+ }
341
+ if (endDirection) {
342
+ if (isOppositeDirection(startDirection, endDirection) && startDirection === getDirectionForFromPoint(startPoint, endPoint)) {
343
+ minCtrl = arrowWidth * 2;
344
+ }
345
+ }
346
+ half.x = max(minCtrl, half.x);
347
+ half.y = max(minCtrl, half.y);
348
+ if (hasPoints) {
349
+ minCtrl *= .6;
350
+ const points = draw.PointHelper.toNumberPoints(data.points);
351
+ drawPoints$1(path, [ startPoint.x, startPoint.y, ...points, endPoint.x, endPoint.y ], data.curve, false, data);
352
+ if (startDirection) {
353
+ const handleX = path[4];
354
+ const handleY = path[5];
355
+ half.x = max(minCtrl, abs(handleX - startPoint.x));
356
+ half.y = max(minCtrl, abs(handleY - startPoint.y));
357
+ const startCtrlPoint = getCtrlPoint(this, startNode, startPoint, startDirection, half);
358
+ path[4] = startCtrlPoint.x;
359
+ path[5] = startCtrlPoint.y;
360
+ }
361
+ const len = path.length;
362
+ if (endDirection) {
363
+ const handleX = path[len - 4];
364
+ const handleY = path[len - 3];
365
+ half.x = max(minCtrl, abs(handleX - endPoint.x));
366
+ half.y = max(minCtrl, abs(handleY - endPoint.y));
367
+ const endCtrlPoint = getCtrlPoint(this, endNode, endPoint, endDirection, half);
368
+ path[len - 4] = endCtrlPoint.x;
369
+ path[len - 3] = endCtrlPoint.y;
370
+ } else {
371
+ path[len - 4] = endPoint.x;
372
+ path[len - 3] = endPoint.y;
373
+ }
374
+ } else {
375
+ const startCtrlPoint = startDirection ? getCtrlPoint(this, startNode, startPoint, startDirection, half) : startPoint;
376
+ const endCtrlPoint = endDirection ? getCtrlPoint(this, endNode, endPoint, endDirection, half) : endPoint;
377
+ moveTo(path, startPoint.x, startPoint.y);
378
+ bezierCurveTo(path, startCtrlPoint.x, startCtrlPoint.y, endCtrlPoint.x, endCtrlPoint.y, endPoint.x, endPoint.y);
379
+ }
380
+ }
381
+ createStartPoint(worldPoint, node, options) {
382
+ const data = this.createLinkerPoint(worldPoint, node, options);
383
+ this.startPoint = data;
384
+ return data;
385
+ }
386
+ createEndPoint(worldPoint, node, options) {
387
+ const data = this.createLinkerPoint(worldPoint, node, options);
388
+ this.endPoint = data;
389
+ return data;
390
+ }
391
+ createLinkerPoint(worldPoint, node, options) {
392
+ const data = {};
393
+ if (node && node.id) {
394
+ const point = node.getBoxPoint(worldPoint);
395
+ const {boxBounds: boxBounds} = node;
396
+ const mode = options && options.mode;
397
+ const edge = mode === "edge" || !mode;
398
+ const four = mode === "four";
399
+ const auto = mode === "auto";
400
+ const free = mode === "free";
401
+ if (!auto) {
402
+ if (four) {
403
+ data.point = LinkerHelper.getDirection(point, boxBounds, four);
404
+ } else {
405
+ data.direction = LinkerHelper.getDirection(point, boxBounds, edge);
406
+ data.point = LinkerHelper.getPointDirection(point, boxBounds, edge) || data.direction;
407
+ if (data.point === data.direction) delete data.direction;
408
+ if (free || edge) {
409
+ const snapPoint = draw.BoundsHelper.getPoint(boxBounds, data.point);
410
+ const offsetX = point.x - snapPoint.x;
411
+ const offsetY = point.y - snapPoint.y;
412
+ const snapRadius = options && options.snapRadius || 10;
413
+ if (Math.abs(offsetX) > snapRadius || Math.abs(offsetY) > snapRadius) {
414
+ const offset = data.offset = {
415
+ x: Math.abs(offsetX) > snapRadius ? offsetX : 0,
416
+ y: Math.abs(offsetY) > snapRadius ? offsetY : 0
417
+ };
418
+ if (edge && (offset.x || offset.y)) {
419
+ switch (data.direction || data.point) {
420
+ case "top":
421
+ case "bottom":
422
+ offset.y = 0;
423
+ break;
424
+
425
+ case "right":
426
+ case "left":
427
+ offset.x = 0;
428
+ break;
429
+
430
+ default:
431
+ if (offset.x > offset.y) offset.y = 0; else offset.x = 0;
432
+ }
433
+ }
434
+ }
435
+ }
436
+ }
437
+ }
438
+ data.id = node.id;
439
+ } else {
440
+ data.point = this.getLocalPoint(worldPoint);
441
+ }
442
+ return data;
443
+ }
444
+ };
445
+
446
+ __decorate([ draw.dataProcessor(LinkerData) ], exports.Linker.prototype, "__", void 0);
447
+
448
+ __decorate([ linkerPointType() ], exports.Linker.prototype, "startPoint", void 0);
449
+
450
+ __decorate([ linkerPointType() ], exports.Linker.prototype, "endPoint", void 0);
451
+
452
+ __decorate([ draw.pathType({
453
+ type: "C",
454
+ value: .5
455
+ }) ], exports.Linker.prototype, "curve", void 0);
456
+
457
+ exports.Linker = __decorate([ draw.registerUI() ], exports.Linker);
458
+
459
+ const {toNumberPoints: toNumberPoints} = core.PointHelper;
460
+
461
+ const {M: M, L: L, C: C, Z: Z} = core.PathCommandMap;
462
+
463
+ function drawPoints(data, originPoints, curve, close) {
464
+ const points = toNumberPoints(originPoints);
465
+ const count = points.length >> 1;
466
+ if (count < 2) return;
467
+ data.push(M, points[0], points[1]);
468
+ if (!curve || count < 3) {
469
+ for (let i = 1; i < count; i++) {
470
+ data.push(L, points[i * 2], points[i * 2 + 1]);
471
+ }
472
+ if (close) data.push(Z);
473
+ return;
474
+ }
475
+ let tension = curve === true ? .5 : curve;
476
+ if (tension < 0) tension = 0; else if (tension > 1) tension = 1;
477
+ const end = close ? count : count - 1;
478
+ let lastOutX = points[0];
479
+ let lastOutY = points[1];
480
+ {
481
+ const dx = points[2] - points[0];
482
+ const dy = points[3] - points[1];
483
+ const len = Math.sqrt(dx * dx + dy * dy);
484
+ if (len) {
485
+ lastOutX = points[0] + dx / len * len * tension;
486
+ lastOutY = points[1] + dy / len * len * tension;
487
+ }
488
+ }
489
+ for (let i = 0; i < end; i++) {
490
+ const index = i + 1 === count ? 0 : i + 1;
491
+ const bx = points[index * 2];
492
+ const by = points[index * 2 + 1];
493
+ const prev = index === 0 ? count - 1 : index - 1;
494
+ const next = index + 1 === count ? 0 : index + 1;
495
+ const ax = points[prev * 2];
496
+ const ay = points[prev * 2 + 1];
497
+ const cx = points[next * 2];
498
+ const cy = points[next * 2 + 1];
499
+ let bax = ax - bx;
500
+ let bay = ay - by;
501
+ let bcx = cx - bx;
502
+ let bcy = cy - by;
503
+ const ab = Math.sqrt(bax * bax + bay * bay);
504
+ const bc = Math.sqrt(bcx * bcx + bcy * bcy);
505
+ let inX = bx;
506
+ let inY = by;
507
+ let outX = bx;
508
+ let outY = by;
509
+ if (ab || bc) {
510
+ let ux = 0;
511
+ let uy = 0;
512
+ if (ab) {
513
+ ux += bax / ab;
514
+ uy += bay / ab;
515
+ }
516
+ if (bc) {
517
+ ux += bcx / bc;
518
+ uy += bcy / bc;
519
+ }
520
+ let uLen = Math.sqrt(ux * ux + uy * uy);
521
+ if (!uLen) {
522
+ if (bc) {
523
+ ux = bcx / bc;
524
+ uy = bcy / bc;
525
+ } else {
526
+ ux = bax / ab;
527
+ uy = bay / ab;
528
+ }
529
+ uLen = 1;
530
+ }
531
+ ux /= uLen;
532
+ uy /= uLen;
533
+ let tx = -uy;
534
+ let ty = ux;
535
+ if (bc) {
536
+ if (tx * bcx + ty * bcy < 0) {
537
+ tx = -tx;
538
+ ty = -ty;
539
+ }
540
+ }
541
+ let cos = 0;
542
+ if (ab && bc) {
543
+ cos = (bax * bcx + bay * bcy) / (ab * bc);
544
+ if (cos > 1) cos = 1; else if (cos < -1) cos = -1;
545
+ }
546
+ let angleFactor = Math.sqrt(Math.sqrt(Math.max(0, 1 - cos * cos)));
547
+ if (angleFactor < .15) angleFactor = .15;
548
+ let inLength = ab * tension * angleFactor;
549
+ let outLength = bc * tension * angleFactor;
550
+ const maxLength = Math.min(ab || bc, bc || ab) * .5;
551
+ if (inLength > maxLength) inLength = maxLength;
552
+ if (outLength > maxLength) outLength = maxLength;
553
+ inX = bx - tx * inLength;
554
+ inY = by - ty * inLength;
555
+ outX = bx + tx * outLength;
556
+ outY = by + ty * outLength;
557
+ }
558
+ data.push(C, lastOutX, lastOutY, inX, inY, bx, by);
559
+ lastOutX = outX;
560
+ lastOutY = outY;
561
+ }
562
+ if (close) data.push(Z);
563
+ }
564
+
565
+ draw.Plugin.add("linker");
566
+
567
+ draw.registerPointsCurve("C", drawPoints);
568
+
569
+ exports.LinkerData = LinkerData;