@libs-ui/components-draw-line 0.1.1-1

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,1571 @@
1
+ import * as i0 from '@angular/core';
2
+ import { inject, ChangeDetectorRef, NgZone, ElementRef, EventEmitter, Output, Input, Directive } from '@angular/core';
3
+ import { range, cloneDeep, checkMouseOverInContainer, get, getDragEventByElement } from '@libs-ui/utils';
4
+ import { Subject, takeUntil, tap } from 'rxjs';
5
+
6
+ class MoCanvasCalculatorBranchUtil {
7
+ /**
8
+ *
9
+ * above, left a + b; opposite = true a - b;
10
+ *
11
+ * under, right a - b; opposite = true a + b
12
+ */
13
+ static mathOperatorsCalculation(direction, a, b, opposite) {
14
+ switch (direction) {
15
+ case 'left':
16
+ case 'above':
17
+ if (opposite) {
18
+ return (a ?? 0) - (b ?? 0);
19
+ }
20
+ return (a ?? 0) + (b ?? 0);
21
+ case 'right':
22
+ case 'under':
23
+ if (opposite) {
24
+ return (a ?? 0) + (b ?? 0);
25
+ }
26
+ return (a ?? 0) - (b ?? 0);
27
+ default:
28
+ return (a ?? 0) - (b ?? 0);
29
+ }
30
+ }
31
+ }
32
+
33
+ class MoCanvasCalculatorDirectionElementUtil {
34
+ static checkUpDownLeftRight(firstPoint, endPoint, direction) {
35
+ const distance = (firstPoint ?? 0) - (endPoint ?? 0);
36
+ if (distance === 0) {
37
+ return 'center';
38
+ }
39
+ if (distance > 0) {
40
+ return direction === 'x' ? 'left' : 'above';
41
+ }
42
+ return direction === 'x' ? 'right' : 'under';
43
+ }
44
+ static drawLineQ(start, corner, lineCurveX, lineCurveY) {
45
+ const coordQ = {};
46
+ switch (corner) {
47
+ case 'right-under':
48
+ coordQ.x1 = start.x + lineCurveX;
49
+ coordQ.y1 = start.y;
50
+ coordQ.x = coordQ.x1;
51
+ coordQ.y = coordQ.y1 + lineCurveY;
52
+ break;
53
+ case 'under-left':
54
+ coordQ.x1 = start.x;
55
+ coordQ.y1 = start.y + lineCurveY;
56
+ coordQ.x = coordQ.x1 - lineCurveX;
57
+ coordQ.y = coordQ.y1;
58
+ break;
59
+ case 'left-above':
60
+ coordQ.x1 = start.x - lineCurveX;
61
+ coordQ.y1 = start.y;
62
+ coordQ.x = coordQ.x1;
63
+ coordQ.y = coordQ.y1 - lineCurveY;
64
+ break;
65
+ case 'above-right':
66
+ coordQ.x1 = start.x;
67
+ coordQ.y1 = start.y - lineCurveY;
68
+ coordQ.x = coordQ.x1 + lineCurveX;
69
+ coordQ.y = coordQ.y1;
70
+ break;
71
+ case 'under-right':
72
+ coordQ.x1 = start.x;
73
+ coordQ.y1 = start.y + lineCurveY;
74
+ coordQ.x = coordQ.x1 + lineCurveX;
75
+ coordQ.y = coordQ.y1;
76
+ break;
77
+ case 'right-above':
78
+ coordQ.x1 = start.x + lineCurveX;
79
+ coordQ.y1 = start.y;
80
+ coordQ.x = coordQ.x1;
81
+ coordQ.y = coordQ.y1 - lineCurveY;
82
+ break;
83
+ case 'above-left':
84
+ coordQ.x1 = start.x;
85
+ coordQ.y1 = start.y - lineCurveY;
86
+ coordQ.x = coordQ.x1 - lineCurveX;
87
+ coordQ.y = coordQ.y1;
88
+ break;
89
+ case 'left-under':
90
+ coordQ.x1 = start.x - lineCurveX;
91
+ coordQ.y1 = start.y;
92
+ coordQ.x = coordQ.x1;
93
+ coordQ.y = coordQ.y1 + lineCurveY;
94
+ break;
95
+ default:
96
+ break;
97
+ }
98
+ return coordQ;
99
+ }
100
+ /**Tránh khối phía bên phải */
101
+ static byPassElementOnRightSide(startX, startY, endY, lineCurve, elementBlockRoad, directionX, separatedPoints, key, coordLX) {
102
+ const topElementBlockRoad = elementBlockRoad.y ?? 0;
103
+ const bottomElementBlockRoad = (elementBlockRoad.y ?? 0) + (elementBlockRoad.height ?? 0);
104
+ const directionY = this.checkUpDownLeftRight(startY, endY, 'y');
105
+ const Q1 = this.drawLineQ({ x: startX, y: startY ?? 0 }, `${directionX}-${directionY === 'under' ? 'under' : 'above'}`, lineCurve, lineCurve);
106
+ const L1 = {
107
+ x: Q1.x ?? 0,
108
+ y: directionY === 'under' ? bottomElementBlockRoad : topElementBlockRoad,
109
+ };
110
+ if (MoCanvasConnectNavigationNewElementUtil.START_MODE !== 'bottom-left') {
111
+ if ((directionY === 'under' && bottomElementBlockRoad - (Q1.y ?? 0) <= 0) || (directionY === 'above' && topElementBlockRoad - (Q1.y ?? 0) >= 0)) {
112
+ L1.y = Q1.y ?? 0;
113
+ }
114
+ if (Math.abs((L1.y ?? 0) - endY) <= 10) {
115
+ L1.y = MoCanvasCalculatorBranchUtil.mathOperatorsCalculation(directionX, L1.y, lineCurve);
116
+ }
117
+ }
118
+ const Q2 = this.drawLineQ({ x: L1.x, y: L1.y ?? 0 }, `${directionY === 'under' ? 'under' : 'above'}-${directionX}`, lineCurve, lineCurve);
119
+ const pathQ1 = `Q ${Q1.x1},${Q1.y1} ${Q1.x},${Q1.y} L ${L1.x} ${L1.y}`;
120
+ const pathQ2 = `Q ${Q2.x1},${Q2.y1} ${Q2.x},${Q2.y}`;
121
+ separatedPoints.push({ start: { x: coordLX ?? startX, y: startY }, end: { x: Q1.x ?? 0, y: Q1.y ?? 0 }, mode: 'horizontal-single-curve', id: key, name: 'qr' });
122
+ separatedPoints.push({ start: { x: Q1.x ?? 0, y: Q1.y ?? 0 }, end: { x: L1.x ?? 0, y: L1.y }, mode: 'horizontal', id: key, name: 'lr' });
123
+ separatedPoints.push({ start: { x: L1.x ?? 0, y: L1.y }, end: { x: Q2.x ?? 0, y: Q2.y ?? 0 }, mode: 'vertical-single-curve', id: key, name: 'q2r' });
124
+ return {
125
+ pathByPassQ: `${pathQ1} ${pathQ2}`,
126
+ endPath: {
127
+ x: Q2.x ?? 0,
128
+ y: Q2.y ?? 0,
129
+ },
130
+ };
131
+ }
132
+ /**Tránh khối phía bên trên */
133
+ static byPassElementOnTopBottomSide(startX, startY, endX, lineCurve, elementBlockRoad, directionY, separatedPoints, key) {
134
+ const leftElementBlockRoad = elementBlockRoad.x ?? 0;
135
+ const rightElementBlockRoad = (elementBlockRoad.x ?? 0) + (elementBlockRoad.width ?? 0);
136
+ const directionX = this.checkUpDownLeftRight(startX, endX, 'x') === 'left' ? 'left' : 'right'; // xem nên tránh sang trái hay phải
137
+ const Q1 = this.drawLineQ({ x: startX, y: startY ?? 0 }, `${directionY}-${directionX}`, lineCurve, lineCurve);
138
+ const L1 = {
139
+ x: directionX === 'left' ? leftElementBlockRoad : rightElementBlockRoad,
140
+ y: Q1.y ?? 0,
141
+ };
142
+ if ((directionX === 'right' && rightElementBlockRoad - (Q1.x ?? 0) <= 0) || (directionX === 'left' && leftElementBlockRoad - (Q1.x ?? 0) >= 0)) {
143
+ L1.x = Q1.x ?? 0;
144
+ }
145
+ const Q2 = this.drawLineQ({ x: L1.x, y: L1.y ?? 0 }, `${directionX}-${directionY}`, lineCurve, lineCurve);
146
+ if (Math.abs((Q2.x ?? 0) - leftElementBlockRoad) <= 10) {
147
+ Q2.x = MoCanvasCalculatorBranchUtil.mathOperatorsCalculation(directionX, Q2.x, lineCurve, true);
148
+ }
149
+ const pathQ1 = `Q ${Q1.x1},${Q1.y1} ${Q1.x},${Q1.y} L ${L1.x} ${L1.y}`;
150
+ const pathQ2 = `Q ${Q2.x1},${Q2.y1} ${Q2.x},${Q2.y}`;
151
+ separatedPoints.push({ start: { x: startX, y: startY }, end: { x: Q1.x ?? 0, y: Q1.y ?? 0 }, mode: 'vertical-single-curve', id: key ?? 'qb' });
152
+ separatedPoints.push({ start: { x: Q1.x ?? 0, y: Q1.y ?? 0 }, end: { x: L1.x ?? 0, y: L1.y ?? 0 }, mode: 'horizontal-single-curve', id: key ?? 'lb' });
153
+ separatedPoints.push({ start: { x: L1.x ?? 0, y: L1.y ?? 0 }, end: { x: Q2.x ?? 0, y: Q2.y ?? 0 }, mode: 'horizontal-single-curve', id: key ?? 'q2b' });
154
+ return {
155
+ pathByPassQ: `${pathQ1} ${pathQ2}`,
156
+ endPath: {
157
+ x: Q2.x ?? 0,
158
+ y: Q2.y ?? 0,
159
+ },
160
+ };
161
+ }
162
+ // kiểm tra đường thẳng và khối có cắt nhau không
163
+ static checkLinePassBlockHorizontal(start, end, xOrYOfLine, direction, obstacleRect, returnAllElementBlockRoad) {
164
+ const minimumDistanceFromBlock = 8;
165
+ start += minimumDistanceFromBlock + 1; // tránh khối xuất phát vào list
166
+ const listPointsOfLine = range(start, end + 1);
167
+ const elementBlockRoad = obstacleRect.filter((item) => {
168
+ const left = (item.x ?? 0) - minimumDistanceFromBlock;
169
+ const right = (item.x ?? 0) + (item.width ?? 0) + minimumDistanceFromBlock;
170
+ const top = (item.y ?? 0) - minimumDistanceFromBlock;
171
+ const bottom = (item.y ?? 0) + (item.height ?? 0) + minimumDistanceFromBlock;
172
+ if (direction === 'x' && listPointsOfLine.find((el) => left <= el && el <= right) && top <= xOrYOfLine && xOrYOfLine <= bottom) {
173
+ return true;
174
+ }
175
+ if (direction === 'y' && listPointsOfLine.find((el) => top <= el && el <= bottom) && left <= xOrYOfLine && xOrYOfLine <= right) {
176
+ return true;
177
+ }
178
+ return false;
179
+ });
180
+ if (!elementBlockRoad || !elementBlockRoad.length) {
181
+ return undefined;
182
+ }
183
+ if (returnAllElementBlockRoad) {
184
+ return elementBlockRoad;
185
+ }
186
+ if (direction === 'y') {
187
+ return elementBlockRoad.reduce((a, b) => (Math.abs((a.y ?? 0) - start) < Math.abs((b.y ?? 0) - start) ? a : b));
188
+ }
189
+ return elementBlockRoad.reduce((a, b) => (Math.abs((a.x ?? 0) - start) < Math.abs((b.x ?? 0) - start) ? a : b));
190
+ }
191
+ // kiểm tra hướng thẳng vẽ về điểm end xem có bị chắn bởi khối nào không, nếu có thì phải lùi lại điểm end nhé.
192
+ static checkBlocksElementStraightY(startX, startY, endPosition, obstacleRect) {
193
+ const elementBlockRoad = this.checkLinePassBlockHorizontal(startY, endPosition.y, startX, 'y', obstacleRect, true);
194
+ if (!elementBlockRoad || !elementBlockRoad.length) {
195
+ return;
196
+ }
197
+ return elementBlockRoad.reduce((a, b) => (Math.abs(a.x ?? 0 - startX) < Math.abs(b.x ?? 0 - startX) ? a : b));
198
+ }
199
+ // kiểm tra hướng ngang vẽ về điểm end xem có bị chắn bởi khối nào không, nếu có thì phải lùi lại điểm end nhé.
200
+ static checkBlocksElementX(startX, startY, endPosition, obstacleRect) {
201
+ const elementBlockRoad = this.checkLinePassBlockHorizontal(startX, endPosition.x, startY, 'x', obstacleRect, true);
202
+ if (!elementBlockRoad || !elementBlockRoad.length) {
203
+ return;
204
+ }
205
+ return elementBlockRoad.reduce((a, b) => (Math.abs((a.y ?? 0) - startY) < Math.abs((b.y ?? 0) - startY) ? a : b));
206
+ }
207
+ // kiểm tra hướng sang phải là tránh khối
208
+ static checkAndAvoidBlocksLeft(startX, startY, lineCurve, endPosition, obstacleRect, separatedPoints) {
209
+ const coordLX = { x: endPosition.x, y: startY };
210
+ const elementBlockRoad = this.checkLinePassBlockHorizontal(startX, endPosition.x, startY, 'x', obstacleRect);
211
+ if (!elementBlockRoad) {
212
+ separatedPoints.push({ start: { x: startX, y: startY }, end: coordLX, mode: 'horizontal', id: '14' });
213
+ return coordLX;
214
+ }
215
+ const directionX = MoCanvasCalculatorDirectionElementUtil.checkUpDownLeftRight(startX, endPosition.x, 'x');
216
+ const specificXElement = directionX === 'right' ? (elementBlockRoad.x ?? 0) : (elementBlockRoad.x ?? 0) + (elementBlockRoad.width ?? 0);
217
+ coordLX.x = specificXElement + MoCanvasConnectNavigationNewElementUtil.ELEMENT_MARGIN_BETWEEN_BRANCH_DEFAULT / 2 + lineCurve / 2;
218
+ if (directionX === 'right') {
219
+ coordLX.x = specificXElement - MoCanvasConnectNavigationNewElementUtil.ELEMENT_MARGIN_BETWEEN_BRANCH_DEFAULT / 2 - lineCurve / 2;
220
+ }
221
+ let startXBypassElement = coordLX.x;
222
+ if ((directionX === 'left' && startX - (coordLX.x ?? 0) <= 0) || (directionX === 'right' && startX - (coordLX.x ?? 0) >= 0)) {
223
+ coordLX.x = startX;
224
+ startXBypassElement = MoCanvasCalculatorBranchUtil.mathOperatorsCalculation(directionX, coordLX.x, lineCurve);
225
+ }
226
+ let lineByPassBlock = [];
227
+ let { endPath } = MoCanvasCalculatorDirectionElementUtil.byPassElementOnRightSide(startXBypassElement ?? 0, coordLX.y ?? 0, endPosition.y, lineCurve, elementBlockRoad, directionX, lineByPassBlock, undefined, coordLX.x);
228
+ if (MoCanvasConnectNavigationNewElementUtil.START_MODE === 'bottom-left' && elementBlockRoad.x + elementBlockRoad.width / 2 === endPosition.x && Math.abs(endPosition.y - elementBlockRoad.y) <= 20) {
229
+ // check xem khối chắn có phải là khối đích hay không?
230
+ lineByPassBlock = [];
231
+ const data = MoCanvasCalculatorDirectionElementUtil.byPassElementOnRightSideNewMode(startXBypassElement ?? 0, coordLX.y ?? 0, endPosition.y, lineByPassBlock, undefined, coordLX.x);
232
+ endPath = data.endPath;
233
+ }
234
+ separatedPoints.push(...lineByPassBlock);
235
+ separatedPoints.push({ start: { x: startX, y: startY }, end: coordLX, mode: 'horizontal', id: '15' });
236
+ const dataEndPath = this.checkAndAvoidBlocksLeft(endPath.x ?? 0, endPath.y ?? 0, lineCurve, endPosition, obstacleRect, separatedPoints);
237
+ return dataEndPath;
238
+ }
239
+ // kiểm tra hướng sang phải là tránh khối
240
+ static checkAndAvoidBlocks(startX, startY, lineCurve, endPosition, obstacleRect, separatedPoints, pathPre) {
241
+ const coordLX = { x: endPosition.x - 30, y: startY };
242
+ const elementBlockRoad = this.checkLinePassBlockHorizontal(startX, endPosition.x, startY, 'x', obstacleRect);
243
+ if (!elementBlockRoad) {
244
+ const elementBlockRoadStraightLineY = this.checkBlocksElementStraightY((coordLX.x ?? 0) + 10, coordLX.y ?? 0, endPosition, obstacleRect); // tính điểm end_x dự kiến theo trường hợp đẹp nhất xem nó có chắn bởi khối nào khi kéo xuống không
245
+ if (elementBlockRoadStraightLineY && (elementBlockRoadStraightLineY.x ?? 0) > startX) {
246
+ let toX = (elementBlockRoadStraightLineY.x ?? 0) - MoCanvasConnectNavigationNewElementUtil.ELEMENT_MARGIN_BETWEEN_BRANCH_DEFAULT / 2 - lineCurve / 2;
247
+ if ((elementBlockRoadStraightLineY?.x ?? 0) - startX < MoCanvasConnectNavigationNewElementUtil.ELEMENT_MARGIN_BETWEEN_BRANCH_DEFAULT) {
248
+ toX = (elementBlockRoadStraightLineY.x ?? 0) - ((elementBlockRoadStraightLineY?.x ?? 0) - startX) / 2;
249
+ }
250
+ coordLX.x = toX;
251
+ }
252
+ separatedPoints.push({ start: { x: startX, y: startY }, end: coordLX, mode: 'horizontal', id: 'checkAndAvoidBlocks', name: 'end' });
253
+ return {
254
+ path: pathPre ? `${pathPre} L ${coordLX.x} ${coordLX.y}` : `L ${coordLX.x} ${coordLX.y}`,
255
+ endPath: coordLX,
256
+ };
257
+ }
258
+ const directionX = MoCanvasCalculatorDirectionElementUtil.checkUpDownLeftRight(startX, endPosition.x, 'x');
259
+ coordLX.x = (elementBlockRoad.x ?? 0) - MoCanvasConnectNavigationNewElementUtil.ELEMENT_MARGIN_BETWEEN_BRANCH_DEFAULT / 2 - lineCurve / 2;
260
+ let separatedPointsByPassElementOnRightSide = [];
261
+ const { pathByPassQ, endPath } = MoCanvasCalculatorDirectionElementUtil.byPassElementOnRightSide(coordLX.x, coordLX.y ?? 0, endPosition.y, lineCurve, elementBlockRoad, directionX, separatedPointsByPassElementOnRightSide, 'checkAndAvoidBlocks', coordLX.x);
262
+ let line = pathPre ? `${pathPre} L ${coordLX.x} ${coordLX.y} ${pathByPassQ}` : `L ${coordLX.x} ${coordLX.y} ${pathByPassQ}`;
263
+ // tính trước trường hợp sau để lùi line
264
+ const elementBlockRoadPre = this.checkLinePassBlockHorizontal(endPath.x ?? 0, endPosition.x, endPath.y ?? 0, 'x', obstacleRect);
265
+ if (!elementBlockRoadPre) {
266
+ const elementBlockRoadStraightLineY = this.checkBlocksElementStraightY((endPosition.x ?? 0) + 10, endPath.y ?? 0, endPosition, obstacleRect);
267
+ if (elementBlockRoadStraightLineY) {
268
+ const XNew = (elementBlockRoadStraightLineY.x ?? 0) - MoCanvasConnectNavigationNewElementUtil.ELEMENT_MARGIN_BETWEEN_BRANCH_DEFAULT / 2 - lineCurve / 2;
269
+ if (XNew < (endPath.x ?? 0)) {
270
+ separatedPointsByPassElementOnRightSide = [];
271
+ separatedPoints.push({ start: { x: startX, y: startY }, end: coordLX, mode: 'horizontal', id: 'checkAndAvoidBlocks', name: 'u' });
272
+ line = pathPre ? `${pathPre} L ${coordLX.x} ${coordLX.y}` : `L ${coordLX.x} ${coordLX.y}`;
273
+ return {
274
+ path: line,
275
+ endPath: coordLX,
276
+ };
277
+ }
278
+ }
279
+ }
280
+ separatedPoints.splice(separatedPoints.length, 0, ...separatedPointsByPassElementOnRightSide);
281
+ separatedPoints.push({ start: { x: startX, y: startY }, end: coordLX, mode: 'horizontal', id: 'checkAndAvoidBlocks', name: 'p' });
282
+ const data = this.checkAndAvoidBlocks(endPath.x ?? 0, endPath.y ?? 0, lineCurve, endPosition, obstacleRect, separatedPoints, `${line}`);
283
+ return {
284
+ path: data.path,
285
+ endPath: data.endPath,
286
+ };
287
+ }
288
+ // kiểm tra hướng thẳng và tránh khối
289
+ static checkAndAvoidBlocksY(startX, startY, lineCurve, endPosition, directionY, obstacleRect, separatedPoints, pathPre) {
290
+ const coordLX = { x: startX, y: endPosition.y };
291
+ const elementBlockRoad = this.checkLinePassBlockHorizontal(startY, endPosition.y, startX, 'y', obstacleRect);
292
+ if ((directionY === 'under' && startY > endPosition.y) || (directionY === 'above' && startY < endPosition.y)) {
293
+ separatedPoints.push({ start: { x: startX, y: startY }, end: coordLX, mode: 'horizontal', id: 'k' });
294
+ return {
295
+ path: pathPre ?? '',
296
+ endPath: { x: startX, y: startY },
297
+ };
298
+ }
299
+ if (!elementBlockRoad) {
300
+ separatedPoints.push({ start: { x: startX, y: startY }, end: coordLX, mode: 'horizontal', id: 'r' });
301
+ return {
302
+ path: pathPre ? `${pathPre} L ${coordLX.x} ${coordLX.y}` : `L ${coordLX.x} ${coordLX.y}`,
303
+ endPath: coordLX,
304
+ };
305
+ }
306
+ const y = directionY === 'above' ? (elementBlockRoad.y ?? 0) + (elementBlockRoad.height ?? 0) : (elementBlockRoad.y ?? 0);
307
+ coordLX.y = MoCanvasCalculatorBranchUtil.mathOperatorsCalculation(directionY, y, lineCurve * 2);
308
+ separatedPoints.push({ start: { x: startX, y: startY }, end: coordLX, mode: 'horizontal', id: 'ra' });
309
+ const { pathByPassQ, endPath } = MoCanvasCalculatorDirectionElementUtil.byPassElementOnTopBottomSide(coordLX.x ?? 0, coordLX.y ?? 0, endPosition.x, lineCurve, elementBlockRoad, directionY, separatedPoints);
310
+ // sau khi đã tránh thì tiếp tục kéo dài cho tời điểm end x, lại kiểm tra xem có khối nào chắn không?
311
+ const line = pathPre ? `${pathPre} L ${coordLX.x} ${coordLX.y} ${pathByPassQ}` : `L ${coordLX.x} ${coordLX.y} ${pathByPassQ}`;
312
+ const data = this.checkAndAvoidBlocksY(endPath.x ?? 0, endPath.y ?? 0, lineCurve, endPosition, directionY, obstacleRect, separatedPoints, `${line}`);
313
+ return {
314
+ path: data.path,
315
+ endPath: data.endPath,
316
+ };
317
+ }
318
+ // kiểm tra hướng thẳng và tránh khối
319
+ static checkAndAvoidBlocksYLeft(startX, startY, lineCurve, endPosition, directionY, obstacleRect, separatedPoints, pathPre) {
320
+ const key = 'checkAndAvoidBlocksYLeft';
321
+ const coordLX = { x: startX, y: endPosition.y };
322
+ const elementBlockRoad = MoCanvasCalculatorDirectionElementUtil.checkLinePassBlockHorizontal(startY, endPosition.y, startX, 'y', obstacleRect);
323
+ if (!elementBlockRoad) {
324
+ const elementBlockRoadStraightLineX = MoCanvasCalculatorDirectionElementUtil.checkBlocksElementX(coordLX.x ?? 0, coordLX.y ?? 0, endPosition, obstacleRect);
325
+ if (elementBlockRoadStraightLineX) {
326
+ const topElementBlockRoad = elementBlockRoadStraightLineX.y ?? 0;
327
+ const bottomElementBlockRoad = (elementBlockRoadStraightLineX.y ?? 0) + (elementBlockRoadStraightLineX.height ?? 0);
328
+ let toY = directionY === 'under' ? topElementBlockRoad : bottomElementBlockRoad;
329
+ toY = MoCanvasCalculatorBranchUtil.mathOperatorsCalculation(directionY, toY, lineCurve * 2);
330
+ if (toY > startY && MoCanvasConnectNavigationNewElementUtil.START_MODE !== 'bottom-left') {
331
+ coordLX.y = toY;
332
+ }
333
+ }
334
+ separatedPoints.push({ start: { x: startX, y: startY }, end: coordLX, mode: 'horizontal', id: key, name: 'a' });
335
+ return {
336
+ path: pathPre ? `${pathPre} L ${coordLX.x} ${coordLX.y}` : `L ${coordLX.x} ${coordLX.y}`,
337
+ endPath: coordLX,
338
+ };
339
+ }
340
+ const y = directionY === 'above' ? (elementBlockRoad.y ?? 0) + (elementBlockRoad.height ?? 0) : (elementBlockRoad.y ?? 0);
341
+ const elementBlockRoadStraightLineX = MoCanvasCalculatorDirectionElementUtil.checkBlocksElementX(coordLX.x ?? 0, coordLX.y ?? 0, endPosition, obstacleRect);
342
+ let ignoreBypassElement = false;
343
+ if (elementBlockRoadStraightLineX && elementBlockRoadStraightLineX.id === elementBlockRoad.id) {
344
+ ignoreBypassElement = true;
345
+ }
346
+ coordLX.y = MoCanvasCalculatorBranchUtil.mathOperatorsCalculation(directionY, y, lineCurve * 2);
347
+ let separatedPointsByPassElementOnRightSide = [];
348
+ const { pathByPassQ, endPath } = MoCanvasCalculatorDirectionElementUtil.byPassElementOnTopBottomSide(coordLX.x ?? 0, coordLX.y ?? 0, endPosition.x, lineCurve, elementBlockRoad, directionY, separatedPointsByPassElementOnRightSide, key);
349
+ let end = coordLX;
350
+ let line = pathPre ? `${pathPre} L ${coordLX.x} ${coordLX.y}` : `L ${coordLX.x} ${coordLX.y}`;
351
+ separatedPoints.push({ start: { x: startX, y: startY }, end: coordLX, mode: 'horizontal', id: key, name: 'b' });
352
+ if (ignoreBypassElement) {
353
+ separatedPointsByPassElementOnRightSide = [];
354
+ separatedPoints.push({ start: coordLX, end, mode: 'horizontal', id: key, name: 'c' });
355
+ return {
356
+ path: line,
357
+ endPath: end,
358
+ };
359
+ }
360
+ end = endPath;
361
+ line += ` ${pathByPassQ}`;
362
+ separatedPoints.splice(separatedPoints.length, 0, ...separatedPointsByPassElementOnRightSide);
363
+ const data = this.checkAndAvoidBlocksYLeft(end.x ?? 0, end.y ?? 0, lineCurve, endPosition, directionY, obstacleRect, separatedPoints, `${line}`);
364
+ return {
365
+ path: data.path,
366
+ endPath: data.endPath,
367
+ };
368
+ }
369
+ static byPassElementOnRightSideNewMode(startX, startY, endY, separatedPoints, key, coordLX) {
370
+ const start = { x: coordLX ?? startX, y: startY };
371
+ const end = { x: (start.x ?? 0) - 20, y: endY };
372
+ separatedPoints.push({ start: start, end: end, mode: 'horizontal', id: key, name: 'q2r' });
373
+ return {
374
+ pathByPassQ: ``,
375
+ endPath: end,
376
+ };
377
+ }
378
+ }
379
+
380
+ class MoCanvasConnectNavigationBottomLeftElementUtil {
381
+ static lineLeft(pointNext, pointStart, start, lineCurve, obstacleRect, separatedPoints) {
382
+ const endPositionOrigin = {
383
+ x: pointNext.x,
384
+ y: pointNext.y,
385
+ };
386
+ const endPosition = cloneDeep(endPositionOrigin);
387
+ const directionStartY = MoCanvasCalculatorDirectionElementUtil.checkUpDownLeftRight(start.y, endPositionOrigin.y, 'y');
388
+ let newPosition = start;
389
+ if (directionStartY === 'above') {
390
+ const startQL = { lx: start.x - lineCurve, ly: (start.y ?? 0) + lineCurve };
391
+ const line = {
392
+ start: { x: start.x ?? 0, y: start.y ?? 0 },
393
+ end: { x: startQL.lx ?? 0, y: startQL.ly ?? 0 },
394
+ };
395
+ newPosition = {
396
+ x: pointStart.x - (lineCurve + 10),
397
+ y: MoCanvasCalculatorBranchUtil.mathOperatorsCalculation(directionStartY, line.end.y, lineCurve, true),
398
+ };
399
+ separatedPoints.push({ start: line.start, end: line.end, mode: 'vertical-single-curve', id: '1' }); // line vẽ đi khỏi khối đầu tiên
400
+ separatedPoints.push({ start: line.end, end: newPosition, mode: 'horizontal-single-curve', id: '1' }); // line vẽ đi khỏi khối đầu tiên
401
+ }
402
+ // kiểm tra hướng thẳng đến end, nếu có khối thì tránh
403
+ endPositionOrigin.y = MoCanvasCalculatorBranchUtil.mathOperatorsCalculation(directionStartY, endPositionOrigin.y, lineCurve);
404
+ const straightLine = MoCanvasCalculatorDirectionElementUtil.checkAndAvoidBlocksYLeft(newPosition.x ?? 0, newPosition.y ?? 0, lineCurve, endPositionOrigin, directionStartY, obstacleRect, separatedPoints);
405
+ const directionStartX = MoCanvasCalculatorDirectionElementUtil.checkUpDownLeftRight(straightLine.endPath.x, endPositionOrigin.x, 'x');
406
+ if (directionStartX === 'center' && directionStartY === 'under') {
407
+ return;
408
+ }
409
+ if (Math.abs((straightLine.endPath.x ?? 0) - endPositionOrigin.x) < lineCurve) {
410
+ const listLine = separatedPoints.filter((item) => item.id === 'checkAndAvoidBlocksYLeft');
411
+ const elements = listLine.filter((item) => item.start.x === straightLine.endPath.x || item.end.x === straightLine.endPath.x);
412
+ elements.forEach((item) => {
413
+ if (item.start.x === straightLine.endPath.x) {
414
+ item.start.x = MoCanvasCalculatorBranchUtil.mathOperatorsCalculation(directionStartX, item.start.x, lineCurve);
415
+ }
416
+ if (item.end.x === straightLine.endPath.x) {
417
+ item.end.x = MoCanvasCalculatorBranchUtil.mathOperatorsCalculation(directionStartX, item.end.x, lineCurve);
418
+ }
419
+ });
420
+ }
421
+ // // kiểm tra ngay khi cua có vướng ai không rồi hẵng vẽ Q1
422
+ const elementBlockRoad = MoCanvasCalculatorDirectionElementUtil.checkLinePassBlockHorizontal(straightLine.endPath.x ?? 0, endPositionOrigin.x, straightLine.endPath.y ?? 0, 'x', obstacleRect);
423
+ const directionNextX = MoCanvasCalculatorDirectionElementUtil.checkUpDownLeftRight(straightLine.endPath.x ?? 0, endPositionOrigin.x, 'x');
424
+ const coordQ1 = MoCanvasCalculatorDirectionElementUtil.drawLineQ({ x: straightLine.endPath.x ?? 0, y: straightLine.endPath.y ?? 0 }, `${directionStartY}-${directionNextX}`, lineCurve, lineCurve); // tính đoạn cong từ đường ngang sang đường thẳng
425
+ const elementBlockRoadX = (elementBlockRoad?.x ?? 0) + (elementBlockRoad?.width ?? 0);
426
+ if (elementBlockRoad && Math.abs(elementBlockRoadX - (straightLine.endPath.x ?? 0)) <= lineCurve * 2) {
427
+ coordQ1.x1 = straightLine.endPath.x ?? 0;
428
+ coordQ1.y1 = straightLine.endPath.y ?? 0;
429
+ coordQ1.x = straightLine.endPath.x ?? 0;
430
+ coordQ1.y = straightLine.endPath.y ?? 0;
431
+ }
432
+ // kiểm tra và vẽ hướng ngang
433
+ const directionXToEnd = MoCanvasCalculatorDirectionElementUtil.checkUpDownLeftRight(coordQ1.x ?? 0, endPosition.x, 'x');
434
+ const pathHorizontal = [];
435
+ const endPath = MoCanvasCalculatorDirectionElementUtil.checkAndAvoidBlocksLeft(coordQ1.x ?? 0, coordQ1.y ?? 0, lineCurve, endPosition, obstacleRect, pathHorizontal);
436
+ if (directionXToEnd === 'left' && endPath.x > (coordQ1.x ?? 0)) {
437
+ separatedPoints.push({ start: straightLine.endPath, end: endPosition, mode: 'vertical', id: '6a ' + directionStartY });
438
+ return;
439
+ }
440
+ const end = { x: coordQ1.x ?? 0, y: coordQ1.y ?? 0 };
441
+ separatedPoints.push(...pathHorizontal);
442
+ separatedPoints.push({ start: straightLine.endPath, end: end, mode: 'vertical-single-curve', id: '6a ' + directionStartY }); // góc cong từ đường thẳng sang ngang
443
+ if (endPath.x === endPosition.x && endPath.y < endPosition.y) {
444
+ const itemLast = pathHorizontal.find((item) => item.end.x === endPath.x);
445
+ if (itemLast) {
446
+ itemLast.end.x = endPosition.x + 50;
447
+ endPath.x = itemLast.end.x;
448
+ }
449
+ }
450
+ separatedPoints.push({ start: endPath, end: { x: pointNext.x, y: pointNext.y }, mode: 'horizontal', id: 'lineEnd' });
451
+ }
452
+ static rightLeft(elementNext, start, lineCurve, obstacleRect, separatedPoints) {
453
+ const endPosition = {
454
+ x: elementNext.x,
455
+ y: elementNext.y,
456
+ };
457
+ // vẽ 1 đường thẳng từ điểm start ra ngoài đã
458
+ const end = { x: (start.x ?? 0) + 10, y: (start.y ?? 0) + 10 };
459
+ separatedPoints.push({ start: { x: start.x ?? 0, y: start.y ?? 0 }, end: end, mode: 'vertical-single-curve', id: '1' });
460
+ const endPositionX = cloneDeep(endPosition);
461
+ endPositionX.x -= 50;
462
+ let separatedPointsCheckAndAvoidBlocks = [];
463
+ let { endPath } = MoCanvasCalculatorDirectionElementUtil.checkAndAvoidBlocks(end.x ?? 0, end.y ?? 0, lineCurve, endPositionX, obstacleRect, separatedPointsCheckAndAvoidBlocks);
464
+ if (Math.abs((start.x ?? 0) - endPosition.x) < 10 || end.x > endPath.x) {
465
+ endPath = {
466
+ x: end.x,
467
+ y: end.y,
468
+ };
469
+ separatedPointsCheckAndAvoidBlocks = [];
470
+ }
471
+ if (Math.abs((endPath.y ?? 0) - endPosition.y) < 15) {
472
+ const element = separatedPointsCheckAndAvoidBlocks.find((item) => item.end.y === endPath.y && item.end.x === endPath.x);
473
+ const elementQR = separatedPointsCheckAndAvoidBlocks.find((item) => item.end.y === element?.start.y && item.end.x === element.start.x);
474
+ if (element) {
475
+ element.start.y = endPosition.y;
476
+ element.end.y = endPosition.y;
477
+ }
478
+ if (elementQR) {
479
+ elementQR.end.y = endPosition.y;
480
+ }
481
+ }
482
+ separatedPoints.splice(separatedPoints.length, 0, ...separatedPointsCheckAndAvoidBlocks);
483
+ if (endPath.y === endPosition.y) {
484
+ separatedPoints.push({ start: { x: endPath.x ?? 0, y: endPath.y }, end: endPosition, mode: 'horizontal' });
485
+ return;
486
+ }
487
+ if (Math.abs((endPath.y ?? 0) - endPosition.y) < 15) {
488
+ separatedPoints.push({ start: { x: endPath.x ?? 0, y: endPath.y }, end: endPosition, mode: 'vertical' });
489
+ return;
490
+ }
491
+ // // sau khi đã đi ngang và tránh các khối, tiếp theo là đi tiếp hướng lên xuống để về khối cần nối tới, cần check xem điểm end X nó đang ở hướng nào
492
+ const directionY = MoCanvasCalculatorDirectionElementUtil.checkUpDownLeftRight(endPath.y, endPosition.y, 'y');
493
+ const coordQ1 = MoCanvasCalculatorDirectionElementUtil.drawLineQ({ x: endPath.x ?? 0, y: endPath.y ?? 0 }, `right-${directionY === 'above' ? 'above' : 'under'}`, lineCurve, lineCurve); // tính đoạn cong từ đường ngang sang đường thẳng
494
+ const endPositionY = cloneDeep(endPosition);
495
+ endPositionY.y = MoCanvasCalculatorBranchUtil.mathOperatorsCalculation(directionY, endPositionY.y, lineCurve);
496
+ separatedPoints.push({ start: { x: endPath.x ?? 0, y: endPath.y }, end: { x: coordQ1.x ?? 0, y: coordQ1.y ?? 0 }, mode: 'horizontal-single-curve', id: 'g' });
497
+ let avoidBlocksYLine = [];
498
+ const straightLine = MoCanvasCalculatorDirectionElementUtil.checkAndAvoidBlocksY(coordQ1.x ?? 0, coordQ1.y ?? 0, lineCurve, endPositionY, directionY, obstacleRect, avoidBlocksYLine);
499
+ if (directionY === 'under' && (coordQ1.y ?? 0) > endPositionY.y) {
500
+ avoidBlocksYLine = [];
501
+ straightLine.endPath = { x: coordQ1.x ?? 0, y: coordQ1.y ?? 0 };
502
+ }
503
+ if (directionY === 'above' && straightLine.endPath.y <= endPosition.y) {
504
+ avoidBlocksYLine = [];
505
+ straightLine.endPath = { x: coordQ1.x ?? 0, y: coordQ1.y ?? 0 };
506
+ }
507
+ if (Math.abs((straightLine.endPath.x ?? 0) - endPosition.x) < 15) {
508
+ const elements = avoidBlocksYLine.filter((item) => item.start.x === straightLine.endPath.x || item.end.x === straightLine.endPath.x);
509
+ elements.forEach((item) => {
510
+ if (item.start.x === straightLine.endPath.x) {
511
+ item.start.x += 30;
512
+ }
513
+ if (item.end.x === straightLine.endPath.x) {
514
+ item.end.x += 30;
515
+ }
516
+ });
517
+ }
518
+ separatedPoints.push(...avoidBlocksYLine);
519
+ separatedPoints.push({ start: { x: straightLine.endPath.x ?? 0, y: straightLine.endPath.y ?? 0 }, end: endPosition, mode: 'vertical-single-curve', id: '4' });
520
+ return;
521
+ }
522
+ }
523
+
524
+ class MoCanvasConnectNavigationNewElementUtil {
525
+ static ELEMENT_MARGIN_BETWEEN_BRANCH_DEFAULT = 32;
526
+ static START_MODE = 'right-left';
527
+ static setXYConnectElements(elementFrom, elementNext, obstacleRect, separatedPoints, curve) {
528
+ this.drawLineConnect(elementFrom, elementNext, obstacleRect, separatedPoints, curve);
529
+ }
530
+ static drawLineConnect(elementFrom, elementNext, obstacleRect, separatedPoints, curve) {
531
+ let start_x = (elementFrom.x ?? 0) + (elementFrom.width ?? 0); // điểm out luôn ở phía bên phải
532
+ let start_y = (elementFrom.y ?? 0) + (elementFrom.height ?? 0) / 2; // điểm out luôn ở phía bên phải
533
+ if (MoCanvasConnectNavigationNewElementUtil.START_MODE === 'bottom-top') {
534
+ // điểm out luôn ở phía bên dưới
535
+ start_x = (elementFrom.x ?? 0) + (elementFrom.width ?? 0) / 2;
536
+ start_y = (elementFrom.y ?? 0) + (elementFrom.height ?? 0);
537
+ }
538
+ if (MoCanvasConnectNavigationNewElementUtil.START_MODE === 'bottom-left') {
539
+ // điểm out luôn ở phía bên dưới
540
+ start_x = (elementFrom.x ?? 0) + (elementFrom.width ?? 0) / 2;
541
+ start_y = (elementFrom.y ?? 0) + (elementFrom.height ?? 0);
542
+ }
543
+ const M_start = { x: start_x, y: start_y };
544
+ const directionX = MoCanvasCalculatorDirectionElementUtil.checkUpDownLeftRight(start_x, elementNext.x, 'x');
545
+ switch (MoCanvasConnectNavigationNewElementUtil.START_MODE) {
546
+ case 'right-left':
547
+ if (directionX === 'left') {
548
+ this.lineConnectLeft(elementNext, elementFrom, M_start, curve, obstacleRect, separatedPoints);
549
+ return;
550
+ }
551
+ this.lineConnectRight(elementNext, M_start, curve, obstacleRect, separatedPoints);
552
+ break;
553
+ case 'bottom-top':
554
+ if (directionX === 'left') {
555
+ this.lineConnectLeftWithBottomStart(elementNext, elementFrom, M_start, curve, obstacleRect, separatedPoints);
556
+ return;
557
+ }
558
+ this.lineConnectRightWithBottomStart(elementNext, M_start, curve, obstacleRect, separatedPoints);
559
+ break;
560
+ case 'bottom-left':
561
+ console.log(directionX);
562
+ if (directionX === 'left') {
563
+ MoCanvasConnectNavigationBottomLeftElementUtil.lineLeft(elementNext, elementFrom, M_start, curve, obstacleRect, separatedPoints);
564
+ return;
565
+ }
566
+ MoCanvasConnectNavigationBottomLeftElementUtil.rightLeft(elementNext, M_start, curve, obstacleRect, separatedPoints);
567
+ break;
568
+ }
569
+ }
570
+ static lineConnectRight(elementNext, start, lineCurve, obstacleRect, separatedPoints) {
571
+ const endPosition = {
572
+ x: (elementNext.x ?? 0) - lineCurve * 3,
573
+ y: (elementNext.y ?? 0) + (elementNext.height ?? 0) / 2,
574
+ };
575
+ const endPositionX = cloneDeep(endPosition);
576
+ let separatedPointsCheckAndAvoidBlocks = [];
577
+ let { path, endPath } = MoCanvasCalculatorDirectionElementUtil.checkAndAvoidBlocks(start.x ?? 0, start.y ?? 0, lineCurve, endPositionX, obstacleRect, separatedPointsCheckAndAvoidBlocks);
578
+ if (Math.abs((start.x ?? 0) - endPosition.x) < 10) {
579
+ path = '';
580
+ endPath = {
581
+ x: start.x,
582
+ y: start.y,
583
+ };
584
+ separatedPointsCheckAndAvoidBlocks = [];
585
+ }
586
+ separatedPoints.splice(separatedPoints.length, 0, ...separatedPointsCheckAndAvoidBlocks);
587
+ // sau khi đã đi ngang và tránh các khối, tiếp theo là đi tiếp hướng lên xuống để về khối cần nối tới, cần check xem điểm end X nó đang ở hướng nào
588
+ const directionY = MoCanvasCalculatorDirectionElementUtil.checkUpDownLeftRight(endPath.y, endPosition.y, 'y');
589
+ if (directionY === 'center') {
590
+ const LEnd = { x: elementNext.x ?? 0, y: endPath.y };
591
+ separatedPoints.push({ start: endPath, end: LEnd, mode: 'horizontal', id: 'f' });
592
+ return {
593
+ dPath: `${path} L ${LEnd.x} ${LEnd.y}`,
594
+ endPath: LEnd,
595
+ };
596
+ }
597
+ let lineCurveSubY = lineCurve;
598
+ if (Math.abs((endPath.y ?? 0) - endPosition.y) < lineCurve * 2) {
599
+ lineCurveSubY = Math.abs((endPath.y ?? 0) - endPosition.y) / 2;
600
+ const coordQ1 = MoCanvasCalculatorDirectionElementUtil.drawLineQ({ x: endPath.x ?? 0, y: endPath.y ?? 0 }, `right-${directionY === 'above' ? 'above' : 'under'}`, lineCurveSubY * 2, lineCurveSubY); // tính đoạn cong từ đường ngang sang đường thẳng
601
+ const pathQ1 = `Q ${coordQ1.x1},${coordQ1.y1} ${coordQ1.x},${coordQ1.y}`; // từ ngang sang thẳng phải có đường cong
602
+ const coordQ2 = MoCanvasCalculatorDirectionElementUtil.drawLineQ({ x: coordQ1.x ?? 0, y: coordQ1.y ?? 0 }, `${directionY === 'above' ? 'above' : 'under'}-right`, lineCurveSubY * 2, lineCurveSubY); // tính đoạn cong từ đường thẳng sang ngang
603
+ const pathQ2 = `Q ${coordQ2.x1},${coordQ2.y1} ${coordQ2.x},${coordQ2.y}`;
604
+ separatedPoints.push({ start: endPath, end: { x: coordQ1.x ?? 0, y: coordQ1.y ?? 0 }, mode: 'horizontal-single-curve', id: 'g' });
605
+ separatedPoints.push({ start: { x: coordQ1.x ?? 0, y: coordQ1.y ?? 0 }, end: { x: coordQ2.x ?? 0, y: coordQ2.y ?? 0 }, mode: 'vertical-single-curve', id: 'j' });
606
+ const lineClose = this.closeLine({ x: coordQ2.x ?? 0, y: coordQ2.y ?? 0 }, endPosition, lineCurve, elementNext, separatedPoints);
607
+ return {
608
+ dPath: `${path} ${pathQ1} ${pathQ2} ${lineClose}`,
609
+ };
610
+ }
611
+ const coordQ1 = MoCanvasCalculatorDirectionElementUtil.drawLineQ({ x: endPath.x ?? 0, y: endPath.y ?? 0 }, `right-${directionY === 'above' ? 'above' : 'under'}`, lineCurve, lineCurve); // tính đoạn cong từ đường ngang sang đường thẳng
612
+ const pathQ1 = `Q ${coordQ1.x1},${coordQ1.y1} ${coordQ1.x},${coordQ1.y}`; // từ ngang sang thẳng phải có đường cong
613
+ const endPositionY = cloneDeep(endPosition);
614
+ endPositionY.y = MoCanvasCalculatorBranchUtil.mathOperatorsCalculation(directionY, endPosition.y, lineCurve);
615
+ const straightLine = MoCanvasCalculatorDirectionElementUtil.checkAndAvoidBlocksY(coordQ1.x ?? 0, coordQ1.y ?? 0, lineCurve, endPositionY, directionY, obstacleRect, separatedPoints);
616
+ const coordQ2 = MoCanvasCalculatorDirectionElementUtil.drawLineQ({ x: straightLine.endPath.x ?? 0, y: straightLine.endPath.y ?? 0 }, `${directionY === 'above' ? 'above' : 'under'}-right`, lineCurve, lineCurve); // tính đoạn cong từ đường thẳng sang ngang
617
+ const pathQ2 = `Q ${coordQ2.x1},${coordQ2.y1} ${coordQ2.x},${coordQ2.y}`; // từ ngang sang thẳng phải có đường cong
618
+ separatedPoints.push({ start: { x: endPath.x ?? 0, y: endPath.y }, end: { x: coordQ1.x ?? 0, y: coordQ1.y ?? 0 }, mode: 'horizontal-single-curve', id: 'g' });
619
+ separatedPoints.push({ start: { x: straightLine.endPath.x ?? 0, y: straightLine.endPath.y ?? 0 }, end: { x: coordQ2.x ?? 0, y: coordQ2.y ?? 0 }, mode: 'vertical-single-curve', id: 'j' });
620
+ const lineClose = this.closeLine({ x: coordQ2.x ?? 0, y: coordQ2.y ?? 0 }, endPosition, lineCurve, elementNext, separatedPoints);
621
+ return {
622
+ dPath: `${path} ${pathQ1} ${straightLine.path} ${pathQ2} ${lineClose}`,
623
+ endPath: straightLine.endPath,
624
+ };
625
+ }
626
+ static lineConnectLeft(elementNext, elementFrom, start, lineCurve, obstacleRect, separatedPoints) {
627
+ const endPositionOrigin = {
628
+ x: (elementNext.x ?? 0) - lineCurve * 1.5,
629
+ y: (elementNext.y ?? 0) + (elementNext.height ?? 0) / 2,
630
+ };
631
+ const endPosition = cloneDeep(endPositionOrigin);
632
+ const directionStartY = MoCanvasCalculatorDirectionElementUtil.checkUpDownLeftRight(start.y, endPositionOrigin.y, 'y');
633
+ const endY = directionStartY === 'under' || directionStartY === 'center' ? (elementNext.y ?? 0) : (elementNext.y ?? 0) + (elementNext.height ?? 0);
634
+ endPosition.y = MoCanvasCalculatorBranchUtil.mathOperatorsCalculation(directionStartY, endY, lineCurve * 2);
635
+ const pathQ = MoCanvasCalculatorDirectionElementUtil.drawLineQ({ x: (start.x ?? 0) + 10, y: start.y ?? 0 }, `right-${directionStartY === 'under' ? 'under' : 'above'}`, lineCurve, lineCurve);
636
+ const startQL = { lx: pathQ.x, ly: MoCanvasCalculatorBranchUtil.mathOperatorsCalculation(directionStartY, pathQ.y ?? 0, (elementFrom.height ?? 0) / 2, true) };
637
+ separatedPoints.push({ start: { x: start.x ?? 0, y: start.y ?? 0 }, end: { x: pathQ.x ?? 0, y: pathQ.y ?? 0 }, mode: 'horizontal-single-curve', id: '1' });
638
+ separatedPoints.push({ start: { x: pathQ.x ?? 0, y: pathQ.y ?? 0 }, end: { x: startQL.lx ?? 0, y: startQL.ly }, mode: 'horizontal', id: '2' });
639
+ if (directionStartY === 'center') {
640
+ const coordQ1 = MoCanvasCalculatorDirectionElementUtil.drawLineQ({ x: startQL.lx ?? 0, y: startQL.ly ?? 0 }, `above-left`, lineCurve, lineCurve); // tính đoạn cong từ đường ngang sang đường thẳng
641
+ separatedPoints.push({ start: { x: startQL.lx ?? 0, y: startQL.ly }, end: { x: coordQ1.x ?? 0, y: coordQ1.y ?? 0 }, mode: 'vertical-single-curve', id: '3' });
642
+ const endPath = MoCanvasCalculatorDirectionElementUtil.checkAndAvoidBlocksLeft(coordQ1.x ?? 0, coordQ1.y ?? 0, lineCurve, endPosition, obstacleRect, separatedPoints);
643
+ const directionStartY2 = MoCanvasCalculatorDirectionElementUtil.checkUpDownLeftRight(endPath.y, endPositionOrigin.y, 'y');
644
+ const coordQ2 = MoCanvasCalculatorDirectionElementUtil.drawLineQ({ x: endPath.x ?? 0, y: endPath.y ?? 0 }, `left-${directionStartY2 === 'above' ? 'above' : 'under'}`, lineCurve, lineCurve); // tính đoạn cong từ đường ngang sang đường thẳng
645
+ separatedPoints.push({ start: { x: endPath.x ?? 0, y: endPath.y ?? 0 }, end: { x: coordQ2.x ?? 0, y: coordQ2.y ?? 0 }, mode: 'horizontal-single-curve', id: '4' });
646
+ this.closeLine({ x: coordQ2.x ?? 0, y: coordQ2.y ?? 0 }, endPositionOrigin, lineCurve, elementNext, separatedPoints);
647
+ return {
648
+ dPath: ``,
649
+ };
650
+ }
651
+ const straightLine = MoCanvasCalculatorDirectionElementUtil.checkAndAvoidBlocksYLeft(startQL.lx ?? 0, startQL.ly ?? 0, lineCurve, endPosition, directionStartY, obstacleRect, separatedPoints);
652
+ if (Math.abs((endPosition.x ?? 0) - (straightLine.endPath.x ?? 0)) <= lineCurve * 2) {
653
+ endPosition.x = straightLine.endPath.x;
654
+ }
655
+ if ((directionStartY === 'under' && startQL.ly > endPosition.y) || (directionStartY === 'above' && startQL.ly < endPosition.y) || startQL.ly === endPosition.y) {
656
+ let condition = true;
657
+ while (condition) {
658
+ const checkAndAvoidBlocksYLeft = separatedPoints.findIndex((item) => item.id === 'checkAndAvoidBlocksYLeft');
659
+ if (checkAndAvoidBlocksYLeft !== -1) {
660
+ separatedPoints.splice(checkAndAvoidBlocksYLeft, 1);
661
+ }
662
+ if (checkAndAvoidBlocksYLeft === -1) {
663
+ condition = false;
664
+ }
665
+ }
666
+ straightLine.path = '';
667
+ straightLine.endPath = {
668
+ x: startQL.lx ?? 0,
669
+ y: startQL.ly ?? 0,
670
+ };
671
+ }
672
+ if (endPosition.x === straightLine.endPath.x) {
673
+ separatedPoints.push({ start: straightLine.endPath, end: endPositionOrigin, mode: 'vertical-single-curve', id: '6a ' + directionStartY });
674
+ this.closeLine({ x: endPositionOrigin.x ?? 0, y: endPositionOrigin.y ?? 0 }, endPositionOrigin, lineCurve, elementNext, separatedPoints);
675
+ return;
676
+ }
677
+ // kiểm tra ngay khi cua có vướng ai không rồi hẵng vẽ Q1,
678
+ const elementBlockRoad = MoCanvasCalculatorDirectionElementUtil.checkLinePassBlockHorizontal(straightLine.endPath.x ?? 0, endPosition.x, straightLine.endPath.y ?? 0, 'x', obstacleRect);
679
+ const directionNextX = MoCanvasCalculatorDirectionElementUtil.checkUpDownLeftRight(straightLine.endPath.x ?? 0, endPositionOrigin.x, 'x');
680
+ const coordQ1 = MoCanvasCalculatorDirectionElementUtil.drawLineQ({ x: straightLine.endPath.x ?? 0, y: straightLine.endPath.y ?? 0 }, `${directionStartY}-${directionNextX}`, lineCurve, lineCurve); // tính đoạn cong từ đường ngang sang đường thẳng
681
+ const elementBlockRoadX = (elementBlockRoad?.x ?? 0) + (elementBlockRoad?.width ?? 0);
682
+ if (elementBlockRoad && Math.abs(elementBlockRoadX - (straightLine.endPath.x ?? 0)) <= lineCurve * 2) {
683
+ coordQ1.x1 = straightLine.endPath.x ?? 0;
684
+ coordQ1.y1 = straightLine.endPath.y ?? 0;
685
+ coordQ1.x = straightLine.endPath.x ?? 0;
686
+ coordQ1.y = straightLine.endPath.y ?? 0;
687
+ }
688
+ separatedPoints.push({ start: straightLine.endPath, end: { x: coordQ1.x ?? 0, y: coordQ1.y ?? 0 }, mode: 'vertical-single-curve', id: '6a ' + directionStartY });
689
+ const endPath = MoCanvasCalculatorDirectionElementUtil.checkAndAvoidBlocksLeft(coordQ1.x ?? 0, coordQ1.y ?? 0, lineCurve, endPosition, obstacleRect, separatedPoints);
690
+ const directionStartY2 = MoCanvasCalculatorDirectionElementUtil.checkUpDownLeftRight(endPath.y, endPositionOrigin.y, 'y');
691
+ const directionNextY = MoCanvasCalculatorDirectionElementUtil.checkUpDownLeftRight(coordQ1.x ?? 0, endPath.x, 'x');
692
+ const coordQ2 = MoCanvasCalculatorDirectionElementUtil.drawLineQ({ x: endPath.x ?? 0, y: endPath.y ?? 0 }, `${directionNextY}-${directionStartY2 === 'above' ? 'above' : 'under'}`, lineCurve, lineCurve); // tính đoạn cong từ đường ngang sang đường thẳng
693
+ separatedPoints.push({ start: endPath, end: { x: coordQ2.x ?? 0, y: coordQ2.y ?? 0 }, mode: 'horizontal-single-curve', id: '6' });
694
+ this.closeLine({ x: coordQ2.x ?? 0, y: coordQ2.y ?? 0 }, endPositionOrigin, lineCurve, elementNext, separatedPoints);
695
+ return {
696
+ dPath: ``,
697
+ };
698
+ }
699
+ static closeLine(start, end, lineCurve, elementNext, separatedPoints) {
700
+ const directionY = MoCanvasCalculatorDirectionElementUtil.checkUpDownLeftRight(start.y, end.y, 'y');
701
+ if (start.y === end.y) {
702
+ separatedPoints.push({ start, end: { x: elementNext.x ?? 0, y: end.y ?? 0 }, mode: 'horizontal', id: '7' });
703
+ return `L ${elementNext.x} ${end.y}`;
704
+ }
705
+ const line = { x: start.x, y: start.y };
706
+ const endPoint = {
707
+ x: elementNext.x,
708
+ y: end.y,
709
+ };
710
+ let pathQ1 = '';
711
+ if (Math.abs(start.y - end.y) > lineCurve) {
712
+ const toY = MoCanvasCalculatorBranchUtil.mathOperatorsCalculation(directionY, end.y, lineCurve);
713
+ line.y = toY;
714
+ const directionX = MoCanvasCalculatorDirectionElementUtil.checkUpDownLeftRight(line.x, elementNext.x, 'x');
715
+ const coordQ1 = MoCanvasCalculatorDirectionElementUtil.drawLineQ({ x: line.x ?? 0, y: line.y ?? 0 }, `${directionY === 'above' ? 'above' : 'under'}-${directionX}`, lineCurve, lineCurve); // tính đoạn cong từ đường ngang sang đường thẳng
716
+ let lineL = '';
717
+ if (coordQ1.y === end.y) {
718
+ lineL = `L ${endPoint.x} ${endPoint.y}`;
719
+ }
720
+ pathQ1 = `Q ${coordQ1.x1},${coordQ1.y1} ${coordQ1.x},${coordQ1.y} ${lineL}`; // từ ngang sang thẳng phải có đường cong
721
+ }
722
+ separatedPoints.push({ start, end: line, mode: 'horizontal', id: '8' });
723
+ separatedPoints.push({ start: line, end: endPoint, mode: 'vertical-single-curve', id: '9' });
724
+ return `L ${line.x} ${line.y} ${pathQ1}`;
725
+ }
726
+ static lineConnectLeftWithBottomStart(elementNext, elementFrom, start, lineCurve, obstacleRect, separatedPoints) {
727
+ const endPositionOrigin = {
728
+ x: (elementNext.x ?? 0) + (elementNext.width ?? 0) / 2,
729
+ y: (elementNext.y ?? 0) - lineCurve,
730
+ };
731
+ const endPosition = cloneDeep(endPositionOrigin);
732
+ const directionStartY = MoCanvasCalculatorDirectionElementUtil.checkUpDownLeftRight(start.y, endPositionOrigin.y, 'y');
733
+ const endY = elementNext.y ?? 0;
734
+ console.log('directionStartY', directionStartY);
735
+ if (directionStartY === 'under') {
736
+ endPosition.y = endY - lineCurve * 3;
737
+ }
738
+ const startQL = { lx: start.x, ly: (start.y ?? 0) + lineCurve };
739
+ const line = {
740
+ start: { x: start.x ?? 0, y: start.y ?? 0 },
741
+ end: { x: startQL.lx ?? 0, y: startQL.ly ?? 0 },
742
+ };
743
+ const newEnd = { x: elementFrom.x - lineCurve * 2, y: line.end.y - lineCurve };
744
+ if (directionStartY === 'above') {
745
+ line.end = { x: (start.x ?? 0) - lineCurve, y: (start.y ?? 0) + lineCurve * 2 };
746
+ separatedPoints.push({ start: line.end, end: newEnd, mode: 'horizontal-single-curve', id: '1' });
747
+ }
748
+ separatedPoints.push({ start: line.start, end: line.end, mode: 'vertical-single-curve', id: '1' }); // line vẽ đi khỏi khối đầu tiên
749
+ const startNewLine = directionStartY === 'above' ? newEnd : line.end;
750
+ if (directionStartY === 'center') {
751
+ const coordQ1 = MoCanvasCalculatorDirectionElementUtil.drawLineQ({ x: startQL.lx ?? 0, y: startQL.ly ?? 0 }, `above-left`, lineCurve, lineCurve); // tính đoạn cong từ đường ngang sang đường thẳng
752
+ // từ ngang sang thẳng phải có đường cong
753
+ separatedPoints.push({ start: { x: startQL.lx ?? 0, y: startQL.ly }, end: { x: coordQ1.x ?? 0, y: coordQ1.y ?? 0 }, mode: 'vertical-single-curve', id: '3' });
754
+ const endPath = MoCanvasCalculatorDirectionElementUtil.checkAndAvoidBlocksLeft(coordQ1.x ?? 0, coordQ1.y ?? 0, lineCurve, endPosition, obstacleRect, separatedPoints);
755
+ const directionStartY2 = MoCanvasCalculatorDirectionElementUtil.checkUpDownLeftRight(endPath.y, endPositionOrigin.y, 'y');
756
+ const coordQ2 = MoCanvasCalculatorDirectionElementUtil.drawLineQ({ x: endPath.x ?? 0, y: endPath.y ?? 0 }, `left-${directionStartY2 === 'above' ? 'above' : 'under'}`, lineCurve, lineCurve); // tính đoạn cong từ đường ngang sang đường thẳng
757
+ // từ ngang sang thẳng phải có đường cong
758
+ separatedPoints.push({ start: { x: endPath.x ?? 0, y: endPath.y ?? 0 }, end: { x: coordQ2.x ?? 0, y: coordQ2.y ?? 0 }, mode: 'horizontal-single-curve', id: '4' });
759
+ this.closeLine({ x: coordQ2.x ?? 0, y: coordQ2.y ?? 0 }, endPositionOrigin, lineCurve, elementNext, separatedPoints);
760
+ return {
761
+ dPath: ``,
762
+ };
763
+ }
764
+ // kiểm tra hướng thẳng đến end, nếu có khối thì tránh
765
+ const straightLine = MoCanvasCalculatorDirectionElementUtil.checkAndAvoidBlocksYLeft(startNewLine.x ?? 0, startNewLine.y ?? 0, lineCurve, endPosition, directionStartY, obstacleRect, separatedPoints);
766
+ const directionStartX = MoCanvasCalculatorDirectionElementUtil.checkUpDownLeftRight(start.x, endPositionOrigin.x, 'x');
767
+ if (directionStartX === 'center' && directionStartY === 'under') {
768
+ this.closeLineWithLineTopBottom({ x: straightLine.endPath.x ?? 0, y: straightLine.endPath.y ?? 0 }, endPositionOrigin, lineCurve, elementNext, separatedPoints);
769
+ return {
770
+ dPath: ``,
771
+ };
772
+ }
773
+ if ((directionStartY === 'under' && startQL.ly > endPosition.y) || (directionStartY === 'above' && startQL.ly < endPosition.y) || startQL.ly === endPosition.y) {
774
+ let condition = true;
775
+ while (condition) {
776
+ const checkAndAvoidBlocksYLeft = separatedPoints.findIndex((item) => item.id === 'checkAndAvoidBlocksYLeft');
777
+ if (checkAndAvoidBlocksYLeft !== -1) {
778
+ separatedPoints.splice(checkAndAvoidBlocksYLeft, 1);
779
+ }
780
+ if (checkAndAvoidBlocksYLeft === -1) {
781
+ condition = false;
782
+ }
783
+ }
784
+ straightLine.path = '';
785
+ straightLine.endPath = {
786
+ x: startQL.lx ?? 0,
787
+ y: startQL.ly ?? 0,
788
+ };
789
+ }
790
+ // kiểm tra ngay khi cua có vướng ai không rồi hẵng vẽ Q1
791
+ const elementBlockRoad = MoCanvasCalculatorDirectionElementUtil.checkLinePassBlockHorizontal(straightLine.endPath.x ?? 0, endPosition.x, straightLine.endPath.y ?? 0, 'x', obstacleRect);
792
+ const directionNextX = MoCanvasCalculatorDirectionElementUtil.checkUpDownLeftRight(straightLine.endPath.x ?? 0, endPositionOrigin.x, 'x');
793
+ const coordQ1 = MoCanvasCalculatorDirectionElementUtil.drawLineQ({ x: straightLine.endPath.x ?? 0, y: straightLine.endPath.y ?? 0 }, `${directionStartY}-${directionNextX}`, lineCurve, lineCurve); // tính đoạn cong từ đường ngang sang đường thẳng
794
+ const elementBlockRoadX = (elementBlockRoad?.x ?? 0) + (elementBlockRoad?.width ?? 0);
795
+ if (elementBlockRoad && Math.abs(elementBlockRoadX - (straightLine.endPath.x ?? 0)) <= lineCurve * 2) {
796
+ coordQ1.x1 = straightLine.endPath.x ?? 0;
797
+ coordQ1.y1 = straightLine.endPath.y ?? 0;
798
+ coordQ1.x = straightLine.endPath.x ?? 0;
799
+ coordQ1.y = straightLine.endPath.y ?? 0;
800
+ }
801
+ // kiểm tra và vẽ hướng ngang
802
+ const directionXToEnd = MoCanvasCalculatorDirectionElementUtil.checkUpDownLeftRight(coordQ1.x ?? 0, endPositionOrigin.x, 'x');
803
+ endPosition.x = MoCanvasCalculatorBranchUtil.mathOperatorsCalculation(directionXToEnd, endPosition.x, lineCurve);
804
+ if (Math.abs((coordQ1.y ?? 0) - endPosition.y) <= lineCurve) {
805
+ endPosition.y -= lineCurve;
806
+ }
807
+ const pathHorizontal = [];
808
+ const endPath = MoCanvasCalculatorDirectionElementUtil.checkAndAvoidBlocksLeft(coordQ1.x ?? 0, coordQ1.y ?? 0, lineCurve, endPosition, obstacleRect, pathHorizontal);
809
+ if (directionXToEnd === 'left' && endPath.x > (coordQ1.x ?? 0)) {
810
+ separatedPoints.push({ start: straightLine.endPath, end: endPositionOrigin, mode: 'vertical', id: '6a ' + directionStartY });
811
+ this.closeLineWithLineTopBottom(endPositionOrigin, endPositionOrigin, lineCurve, elementNext, separatedPoints);
812
+ return;
813
+ }
814
+ separatedPoints.push(...pathHorizontal);
815
+ separatedPoints.push({ start: straightLine.endPath, end: { x: coordQ1.x ?? 0 - 10, y: coordQ1.y ?? 0 }, mode: 'vertical-single-curve', id: '6a ' + directionStartY });
816
+ this.closeLineWithLineTopBottom({ x: endPath.x ?? 0, y: endPath.y ?? 0 }, endPositionOrigin, lineCurve, elementNext, separatedPoints);
817
+ return {
818
+ dPath: ``,
819
+ };
820
+ }
821
+ static closeLineWithLineTopBottom(start, end, lineCurve, elementNext, separatedPoints) {
822
+ const directionX = MoCanvasCalculatorDirectionElementUtil.checkUpDownLeftRight(start.x, end.x, 'x');
823
+ if (start.x === end.x) {
824
+ separatedPoints.push({ start, end: { x: (elementNext.x ?? 0) + (elementNext.width ?? 0) / 2, y: elementNext.y ?? 0 }, mode: 'horizontal', id: '7' });
825
+ return `L ${elementNext.x} ${end.y}`;
826
+ }
827
+ const line = { x: start.x, y: start.y };
828
+ const endPoint = {
829
+ x: end.x,
830
+ y: elementNext.y,
831
+ };
832
+ const pathQ1 = '';
833
+ if (Math.abs(start.x - end.x) > lineCurve) {
834
+ const directionY = MoCanvasCalculatorDirectionElementUtil.checkUpDownLeftRight(start.y, endPoint.y, 'y');
835
+ const toX = MoCanvasCalculatorBranchUtil.mathOperatorsCalculation(directionX, end.x, lineCurve);
836
+ const toY = MoCanvasCalculatorBranchUtil.mathOperatorsCalculation(directionY, end.y, lineCurve);
837
+ line.x = toX;
838
+ line.y = toY;
839
+ separatedPoints.push({ start: start, end: line, mode: 'vertical-single-curve', id: '9' });
840
+ }
841
+ separatedPoints.push({ start: line, end: endPoint, mode: 'horizontal-single-curve', id: '9' });
842
+ return `L ${line.x} ${line.y} ${pathQ1}`;
843
+ }
844
+ static lineConnectRightWithBottomStart(elementNext, start, lineCurve, obstacleRect, separatedPoints) {
845
+ const endPosition = {
846
+ x: (elementNext.x ?? 0) + (elementNext.width ?? 0) / 2,
847
+ y: (elementNext.y ?? 0) - lineCurve,
848
+ };
849
+ // vẽ 1 đường thẳng từ điểm start ra ngoài đã
850
+ const end = { x: (start.x ?? 0) + 10, y: (start.y ?? 0) + 20 };
851
+ separatedPoints.push({ start: { x: start.x ?? 0, y: start.y ?? 0 }, end: end, mode: 'vertical-single-curve', id: '1' });
852
+ const endPositionX = cloneDeep(endPosition);
853
+ endPositionX.x -= 10;
854
+ let separatedPointsCheckAndAvoidBlocks = [];
855
+ let { path, endPath } = MoCanvasCalculatorDirectionElementUtil.checkAndAvoidBlocks(end.x ?? 0, end.y ?? 0, lineCurve, endPositionX, obstacleRect, separatedPointsCheckAndAvoidBlocks);
856
+ if (Math.abs((start.x ?? 0) - endPosition.x) < 10) {
857
+ path = '';
858
+ endPath = {
859
+ x: start.x,
860
+ y: start.y,
861
+ };
862
+ separatedPointsCheckAndAvoidBlocks = [];
863
+ }
864
+ separatedPoints.splice(separatedPoints.length, 0, ...separatedPointsCheckAndAvoidBlocks);
865
+ // sau khi đã đi ngang và tránh các khối, tiếp theo là đi tiếp hướng lên xuống để về khối cần nối tới, cần check xem điểm end X nó đang ở hướng nào
866
+ const directionY = MoCanvasCalculatorDirectionElementUtil.checkUpDownLeftRight(endPath.y, endPosition.y, 'y');
867
+ if (directionY === 'center') {
868
+ const LEnd = { x: elementNext.x ?? 0, y: endPath.y };
869
+ separatedPoints.push({ start: endPath, end: LEnd, mode: 'horizontal', id: 'f' });
870
+ // đoạn này chưa oce
871
+ this.closeLineWithLineTopBottom(LEnd, endPosition, lineCurve, elementNext, separatedPoints);
872
+ return {
873
+ dPath: `${path} L ${LEnd.x} ${LEnd.y}`,
874
+ endPath: LEnd,
875
+ };
876
+ }
877
+ const coordQ1 = MoCanvasCalculatorDirectionElementUtil.drawLineQ({ x: endPath.x ?? 0, y: endPath.y ?? 0 }, `right-${directionY === 'above' ? 'above' : 'under'}`, lineCurve, lineCurve); // tính đoạn cong từ đường ngang sang đường thẳng
878
+ const endPositionY = cloneDeep(endPosition);
879
+ endPositionY.y = MoCanvasCalculatorBranchUtil.mathOperatorsCalculation(directionY, endPosition.y, lineCurve) - 10;
880
+ // cần check khi đi thẳng lên có vướng khối nào không để tiến lên
881
+ const elementBlockRoadStraightLineY = MoCanvasCalculatorDirectionElementUtil.checkBlocksElementStraightY((endPath.x ?? 0) + 10, endPath.y ?? 0, endPositionY, obstacleRect);
882
+ if (elementBlockRoadStraightLineY) {
883
+ coordQ1.x = (elementBlockRoadStraightLineY.x ?? 0) + (elementBlockRoadStraightLineY.width ?? 0) + lineCurve;
884
+ }
885
+ separatedPoints.push({ start: { x: endPath.x ?? 0, y: endPath.y }, end: { x: coordQ1.x ?? 0, y: coordQ1.y ?? 0 }, mode: 'horizontal-single-curve', id: 'g' });
886
+ let avoidBlocksYLine = [];
887
+ const straightLine = MoCanvasCalculatorDirectionElementUtil.checkAndAvoidBlocksY(coordQ1.x ?? 0, coordQ1.y ?? 0, lineCurve, endPositionY, directionY, obstacleRect, avoidBlocksYLine);
888
+ if (directionY === 'under' && (coordQ1.y ?? 0) > endPositionY.y) {
889
+ avoidBlocksYLine = [];
890
+ straightLine.endPath = { x: coordQ1.x ?? 0, y: coordQ1.y ?? 0 };
891
+ }
892
+ separatedPoints.push(...avoidBlocksYLine);
893
+ this.closeLineWithLineTopBottom({ x: straightLine.endPath.x ?? 0, y: straightLine.endPath.y ?? 0 }, endPosition, lineCurve, elementNext, separatedPoints);
894
+ return {
895
+ dPath: ``,
896
+ };
897
+ }
898
+ }
899
+
900
+ class LibsUiComponentsDrawLineDirective {
901
+ cdr = inject(ChangeDetectorRef);
902
+ zone = inject(NgZone);
903
+ elementRef = inject(ElementRef);
904
+ intervalScrollToElement;
905
+ dataDraw = [];
906
+ dataReachablePointRange = [];
907
+ onDrawLineEnd = new Subject();
908
+ onDestroy = new Subject();
909
+ viewBoxConfig;
910
+ svgElement;
911
+ drawRectDebug;
912
+ outDrawLineFunctionControl = new EventEmitter();
913
+ outConnected = new EventEmitter();
914
+ ngAfterViewInit() {
915
+ this.svgElement = this.svgElement || document.createElementNS('http://www.w3.org/2000/svg', 'svg');
916
+ this.elementRef.nativeElement.append(this.svgElement);
917
+ this.outDrawLineFunctionControl.emit(this.FunctionsControl);
918
+ }
919
+ get FunctionsControl() {
920
+ return {
921
+ setData: this.setData.bind(this),
922
+ setReachablePointRange: this.setReachablePointRange.bind(this),
923
+ removeLine: this.removeLine.bind(this),
924
+ removeReachablePointRange: this.removeReachablePointRange.bind(this),
925
+ updateViewBox: this.updateViewBox.bind(this),
926
+ };
927
+ }
928
+ setData(data) {
929
+ const newData = [];
930
+ data.forEach((item) => {
931
+ if (this.dataDraw.find((dataDraw) => dataDraw.id === item.id && item.points.start.x === dataDraw.points.start.x && item.points.start.y === dataDraw.points.start.y && item.points.end.x === dataDraw.points.end.x && item.points.end.y === dataDraw.points.end.y)) {
932
+ return;
933
+ }
934
+ const itemClone = cloneDeep(item);
935
+ itemClone.ref = item;
936
+ newData.push(itemClone);
937
+ });
938
+ this.dataDraw.push(...newData);
939
+ this.startProcessDataRow(newData);
940
+ }
941
+ setReachablePointRange(data) {
942
+ const newData = [];
943
+ data.forEach((item) => {
944
+ if (this.dataReachablePointRange.find((dataDraw) => dataDraw.id === item.id || (item.x === dataDraw.x && item.y === dataDraw.y))) {
945
+ return;
946
+ }
947
+ const itemClone = cloneDeep(item);
948
+ itemClone.ref = item;
949
+ newData.push(itemClone);
950
+ });
951
+ this.dataReachablePointRange.push(...newData);
952
+ this.startProcessDataReachablePointRange(newData);
953
+ }
954
+ removeLine(id, points) {
955
+ const dataDrawById = this.dataDraw.filter((item) => item.id === id);
956
+ if (!dataDrawById?.length) {
957
+ return;
958
+ }
959
+ const removeByItem = (item) => {
960
+ this.removeElementOfPoints(item.points);
961
+ item.points.separatedPoints?.forEach((separatedPoints) => this.removeElementOfPoints(separatedPoints));
962
+ };
963
+ if (!points?.length) {
964
+ dataDrawById.forEach((item) => removeByItem(item));
965
+ this.dataDraw = this.dataDraw.filter((item) => item.id !== id);
966
+ this.updateViewBox();
967
+ return;
968
+ }
969
+ points.forEach((point) => {
970
+ const dataDrawRemoveByPoint = dataDrawById.find((item) => item.points.start.x === point.start.x && item.points.start.y === point.start.y && item.points.end.x === point.end.x && item.points.end.y === point.end.y);
971
+ if (dataDrawRemoveByPoint) {
972
+ dataDrawRemoveByPoint.isRemove = true;
973
+ removeByItem(dataDrawRemoveByPoint);
974
+ this.dataDraw = this.dataDraw.filter((item) => item.id !== id && !item.isRemove);
975
+ }
976
+ });
977
+ this.updateViewBox();
978
+ }
979
+ removeReachablePointRange(ids) {
980
+ this.clearRect();
981
+ if (!ids.length) {
982
+ return;
983
+ }
984
+ ids.forEach((id) => {
985
+ const itemRemove = this.dataReachablePointRange?.find((item) => item.id === id);
986
+ if (!itemRemove) {
987
+ return;
988
+ }
989
+ itemRemove.onDestroyEvent?.next();
990
+ itemRemove.element?.remove();
991
+ this.dataReachablePointRange = this.dataReachablePointRange?.filter((item) => item.id !== id);
992
+ });
993
+ }
994
+ startProcessDataRow(dataDraw) {
995
+ this.zone.runOutsideAngular(() => {
996
+ dataDraw.forEach((item) => {
997
+ const { pathElement, arrowElement, circleStartElement, circleEndElement } = this.createPathAndArrowElement(item.id);
998
+ item.points.obstacleRect?.forEach((data) => this.addRect('rect', data));
999
+ item.points.pathElement = pathElement;
1000
+ item.points.arrowElement = arrowElement;
1001
+ if (item.startCircle) {
1002
+ item.points.circleStartElement = circleStartElement;
1003
+ this.svgElement.append(circleStartElement);
1004
+ }
1005
+ if (item.endCircle) {
1006
+ item.points.circleEndElement = circleEndElement;
1007
+ this.svgElement.append(circleEndElement);
1008
+ }
1009
+ item.points.onDestroyEvent = new Subject();
1010
+ this.drawLine(item);
1011
+ });
1012
+ this.updateViewBox();
1013
+ });
1014
+ }
1015
+ startProcessDataReachablePointRange(data) {
1016
+ this.zone.runOutsideAngular(() => {
1017
+ data.forEach((item) => {
1018
+ item.onDestroyEvent = new Subject();
1019
+ item.element = this.createPathAndArrowElement(item.id).circleEndElement;
1020
+ this.svgElement.append(item.element);
1021
+ this.updateAttributeCircle(item.element, item.id, item, item.style);
1022
+ this.onDrawLineEnd.pipe(takeUntil(item.onDestroyEvent), takeUntil(this.onDestroy)).subscribe((eventData) => {
1023
+ const { dataLine, event } = eventData;
1024
+ if (!checkMouseOverInContainer(event, item.element) || !dataLine.ref || !item.ref) {
1025
+ return;
1026
+ }
1027
+ item.idConnected = eventData.dataLine.id;
1028
+ eventData.dataLine.idConnected = item.id;
1029
+ this.outConnected.emit({
1030
+ dataLine: dataLine.ref,
1031
+ dataReachablePointRange: item.ref,
1032
+ });
1033
+ });
1034
+ });
1035
+ this.updateViewBox();
1036
+ });
1037
+ }
1038
+ removeElementOfPoints(points) {
1039
+ this.zone.runOutsideAngular(() => {
1040
+ points.onDestroyEvent?.next();
1041
+ points.onDestroyEvent?.complete();
1042
+ points?.pathElement?.remove();
1043
+ points?.arrowElement?.remove();
1044
+ points?.circleStartElement?.remove();
1045
+ points.circleEndElement?.remove();
1046
+ });
1047
+ }
1048
+ drawLine(data) {
1049
+ this.zone.runOutsideAngular(() => {
1050
+ const { points, mode } = data;
1051
+ let { curve } = data?.lineStyle || {};
1052
+ if (mode !== 'quart-in') {
1053
+ curve = curve ?? 10;
1054
+ }
1055
+ const preLengthSeparatedPoints = points.separatedPoints?.length;
1056
+ points.separatedPoints?.forEach((separatedPoints) => this.removeElementOfPoints(separatedPoints));
1057
+ points.separatedPoints = [];
1058
+ MoCanvasConnectNavigationNewElementUtil.START_MODE = 'right-left';
1059
+ if (data.startEndMode) {
1060
+ MoCanvasConnectNavigationNewElementUtil.START_MODE = data.startEndMode;
1061
+ }
1062
+ this.calculatorSeparatedPoints(data.id, points, curve || 10, points.separatedPoints, points.obstacleRect || []);
1063
+ if (!preLengthSeparatedPoints && points.separatedPoints.length) {
1064
+ this.removeElementOfPoints(points);
1065
+ const { pathElement, arrowElement } = this.createPathAndArrowElement(data.id);
1066
+ points.pathElement = pathElement;
1067
+ points.arrowElement = arrowElement;
1068
+ points.initialized = false;
1069
+ }
1070
+ if (!points.separatedPoints?.length) {
1071
+ this.buildPathAndDraw(data, points, mode);
1072
+ return;
1073
+ }
1074
+ let path = '';
1075
+ points.separatedPoints.forEach((separatedPoints) => {
1076
+ path += this.buildPathAndDrawSeparatedPoints(data, separatedPoints, separatedPoints.mode || mode);
1077
+ });
1078
+ const pathElement = document.createElementNS('http://www.w3.org/2000/svg', 'path');
1079
+ this.svgElement.append(pathElement);
1080
+ points.pathElement = pathElement;
1081
+ const styleConfig = {
1082
+ stroke: data.lineStyle?.stroke ?? '#9CA2AD',
1083
+ strokeDasharray: data.lineStyle?.strokeDasharray,
1084
+ strokeWidth: data.lineStyle?.strokeWidth ?? '1px',
1085
+ fill: data.lineStyle?.fill ?? 'none',
1086
+ curve: data.lineStyle?.curve ?? 10,
1087
+ distancePoint: data.lineStyle?.distancePoint ?? 10,
1088
+ };
1089
+ points.pathElement?.setAttribute('stroke', styleConfig.stroke);
1090
+ points.pathElement?.setAttribute('fill', styleConfig.fill);
1091
+ points.pathElement?.setAttribute('stroke-width', styleConfig.strokeWidth);
1092
+ if (styleConfig.strokeDasharray) {
1093
+ points.pathElement?.setAttribute('stroke-dasharray', styleConfig.strokeDasharray);
1094
+ }
1095
+ points.pathElement?.setAttribute('d', path);
1096
+ this.drawRectAndInitEvent(data, mode);
1097
+ });
1098
+ }
1099
+ updateViewBox(viewBoxConfig) {
1100
+ if (viewBoxConfig) {
1101
+ this.viewBoxConfig = viewBoxConfig;
1102
+ }
1103
+ const xPoints = new Set([0]);
1104
+ const yPoints = new Set([0]);
1105
+ const addPoints = (points) => {
1106
+ xPoints.add(points.start.x);
1107
+ xPoints.add(points.end.x);
1108
+ yPoints.add(points.start.y);
1109
+ yPoints.add(points.end.y);
1110
+ };
1111
+ this.dataDraw?.forEach((item) => {
1112
+ const points = item.points;
1113
+ addPoints(points);
1114
+ points.separatedPoints?.forEach((separatedPoints) => addPoints(separatedPoints));
1115
+ points.obstacleRect?.forEach((obstacleRect) => {
1116
+ addPoints({
1117
+ start: { x: obstacleRect.x - (obstacleRect.gapXObstacleRect || 8), y: obstacleRect.y - (obstacleRect.gapYObstacleRect || 8) },
1118
+ end: { x: obstacleRect.x + obstacleRect.width + (obstacleRect.gapXObstacleRect || 8) * 2, y: obstacleRect.y + obstacleRect.height + (obstacleRect.gapYObstacleRect || 8) * 2 },
1119
+ });
1120
+ });
1121
+ });
1122
+ const xMax = this.viewBoxConfig?.width ?? (xPoints.size ? Math.max(...xPoints) : 0) + 14;
1123
+ const yMax = this.viewBoxConfig?.height ?? (yPoints.size ? Math.max(...yPoints) : 0) + 14;
1124
+ if (!this.viewBoxConfig?.ignoreViewBox) {
1125
+ const rectSvgElement = this.svgElement.getBoundingClientRect();
1126
+ this.svgElement?.setAttribute('viewBox', `${this.viewBoxConfig?.minX ?? rectSvgElement.x} ${this.viewBoxConfig?.minY ?? rectSvgElement.y} ${xMax} ${yMax}`);
1127
+ }
1128
+ this.svgElement?.setAttribute('width', `${xMax ?? 0}`);
1129
+ this.svgElement?.setAttribute('height', `${yMax ?? 0}`);
1130
+ }
1131
+ isPointInsideSquare(point, square) {
1132
+ const { x, y } = point;
1133
+ const { x: squareX, y: squareY, width, height } = square;
1134
+ return x >= squareX && x <= squareX + width && y >= squareY && y <= squareY + height;
1135
+ }
1136
+ calculatorSeparatedPoints(name, points, curve, separatedPoints, obstacleRects) {
1137
+ if (!obstacleRects.length) {
1138
+ return;
1139
+ }
1140
+ const elementStart = obstacleRects.find((item) => this.isPointInsideSquare(points.start, item));
1141
+ const elementEnd = obstacleRects.find((item) => this.isPointInsideSquare(points.end, item));
1142
+ if (this.viewBoxConfig?.marginBetweenElement) {
1143
+ MoCanvasConnectNavigationNewElementUtil.ELEMENT_MARGIN_BETWEEN_BRANCH_DEFAULT = this.viewBoxConfig?.marginBetweenElement ?? 32;
1144
+ }
1145
+ MoCanvasConnectNavigationNewElementUtil.setXYConnectElements(elementStart ?? { ...points.start, width: 1, height: 1 }, elementEnd ?? { ...points.end, width: 1, height: 1 }, obstacleRects, separatedPoints, curve);
1146
+ }
1147
+ buildPathAndDraw(data, points, mode) {
1148
+ let dPath = {};
1149
+ const styleConfig = {
1150
+ stroke: data.lineStyle?.stroke ?? '#9CA2AD',
1151
+ strokeDasharray: data.lineStyle?.strokeDasharray,
1152
+ strokeWidth: data.lineStyle?.strokeWidth ?? '1px',
1153
+ fill: data.lineStyle?.fill ?? 'none',
1154
+ curve: data.lineStyle?.curve ?? 10,
1155
+ distancePoint: data.lineStyle?.distancePoint ?? 10,
1156
+ };
1157
+ points.pathElement?.setAttribute('stroke', styleConfig.stroke);
1158
+ points.pathElement?.setAttribute('fill', styleConfig.fill);
1159
+ points.pathElement?.setAttribute('stroke-width', styleConfig.strokeWidth);
1160
+ if (styleConfig.strokeDasharray) {
1161
+ points.pathElement?.setAttribute('stroke-dasharray', styleConfig.strokeDasharray);
1162
+ }
1163
+ let { curve, distancePoint } = styleConfig;
1164
+ mode = mode || 'quart-in';
1165
+ if (mode !== 'quart-in') {
1166
+ curve = curve ?? 10;
1167
+ distancePoint = distancePoint ?? 10;
1168
+ }
1169
+ if (mode === 'quart-in' || points.start.x === points.end.x || points.start.y === points.end.y) {
1170
+ this.drawBalancedCurve(dPath, points);
1171
+ if (mode === 'quart-in') {
1172
+ dPath = {};
1173
+ this.drawBendBothEndsCurve(dPath, points);
1174
+ }
1175
+ points.pathElement?.setAttribute('d', this.builDAttributeToString(dPath));
1176
+ this.drawRectAndInitEvent(data, mode);
1177
+ return;
1178
+ }
1179
+ const result = this.calculatorDistancePointAndCurve(points, distancePoint, curve, mode);
1180
+ distancePoint = result.distancePoint;
1181
+ curve = result.curve;
1182
+ this.buildDAttributeByPointEndModeHorizontal(dPath, points, distancePoint || 0, curve || 0, mode);
1183
+ this.buildDAttributeByPointEndModeVertical(dPath, points, distancePoint || 0, curve || 0, mode);
1184
+ points.pathElement?.setAttribute('d', this.builDAttributeToString(dPath));
1185
+ this.drawRectAndInitEvent(data, mode);
1186
+ }
1187
+ calculatorDistancePointAndCurve(points, distancePoint, curve, mode) {
1188
+ if (!mode.includes('vertical') && !mode.includes('horizontal')) {
1189
+ return { curve, distancePoint };
1190
+ }
1191
+ const gapX = Math.abs(points.start.x - points.end.x);
1192
+ const gapY = Math.abs(points.start.y - points.end.y);
1193
+ const multiplierCurve = mode.includes('horizontal') ? 1 : 2;
1194
+ const multiplierDistance = mode.includes('horizontal') ? 2 : 1;
1195
+ if (!mode.includes('single-curve')) {
1196
+ if (gapX < distancePoint * multiplierDistance + curve * multiplierCurve) {
1197
+ distancePoint = curve = gapX / 3;
1198
+ }
1199
+ if (gapY < distancePoint * multiplierDistance + curve * multiplierCurve) {
1200
+ distancePoint = curve = gapY / 3;
1201
+ }
1202
+ return { curve, distancePoint };
1203
+ }
1204
+ const minGap = Math.min(gapX, gapY);
1205
+ if (minGap < distancePoint) {
1206
+ distancePoint = minGap;
1207
+ }
1208
+ if (minGap < curve) {
1209
+ curve = minGap;
1210
+ }
1211
+ return { curve, distancePoint };
1212
+ }
1213
+ drawRectAndInitEvent(data, mode) {
1214
+ const points = data.points;
1215
+ this.updateStyleArrow(points, mode, data);
1216
+ if (data.startCircle && points.circleStartElement) {
1217
+ this.updateAttributeCircle(points.circleStartElement, data.id, points.start, data.startCircleStyle);
1218
+ }
1219
+ if (data.endCircle && points.circleEndElement) {
1220
+ this.updateAttributeCircle(points.circleEndElement, data.id, points.end, data.endCircleStyle);
1221
+ }
1222
+ this.initEvent(data);
1223
+ }
1224
+ drawBalancedCurve(dPath, points) {
1225
+ dPath.M = { x: points.start.x, y: points.start.y };
1226
+ dPath.Q = [
1227
+ { x: points.start.x + (points.end.x - points.start.x) / 4, y: points.start.y + (points.end.y - points.start.y) / 2 },
1228
+ { x: points.start.x + (points.end.x - points.start.x) / 2, y: points.start.y + (points.end.y - points.start.y) / 2 },
1229
+ ];
1230
+ dPath.Q2 = [
1231
+ { x: points.end.x - (points.end.x - points.start.x) / 4, y: points.start.y + (points.end.y - points.start.y) / 2 },
1232
+ { x: points.end.x, y: points.end.y },
1233
+ ];
1234
+ }
1235
+ drawBendBothEndsCurve(dPath, points) {
1236
+ const pointXCurve = (points.start.x + points.end.x) * 0.5;
1237
+ const pointControl1 = { x: pointXCurve, y: points.start.y };
1238
+ const pointControl2 = { x: pointXCurve, y: points.end.y };
1239
+ dPath.M = { x: points.start.x, y: points.start.y };
1240
+ dPath.C = [
1241
+ { x: pointControl1.x, y: pointControl1.y },
1242
+ { x: pointControl2.x, y: pointControl2.y },
1243
+ { x: points.end.x, y: points.end.y },
1244
+ ];
1245
+ }
1246
+ buildDAttributeByPointEndModeHorizontal(dPath, points, distancePoint, curve, mode) {
1247
+ if (!mode.includes('horizontal')) {
1248
+ return;
1249
+ }
1250
+ const multiplierX = points.start.x < points.end.x ? -1 : 1;
1251
+ const multiplierY = points.start.y < points.end.y ? 1 : -1;
1252
+ const reverseMultiplier = -1;
1253
+ dPath.M = { x: points.start.x, y: points.start.y };
1254
+ dPath.L = { x: points.end.x + (distancePoint * 2 + curve) * multiplierX, y: points.start.y };
1255
+ if (mode.includes('horizontal-single')) {
1256
+ dPath.L.x = points.end.x + curve * multiplierX;
1257
+ }
1258
+ dPath.Q = [
1259
+ { x: dPath.L.x + curve * multiplierX * reverseMultiplier, y: points.start.y },
1260
+ { x: dPath.L.x + curve * multiplierX * reverseMultiplier, y: points.start.y + curve * multiplierY },
1261
+ ];
1262
+ dPath.L2 = { x: dPath.Q[1].x, y: points.end.y + curve * multiplierY * reverseMultiplier };
1263
+ if (mode.includes('horizontal-single')) {
1264
+ dPath.L2 = points.end;
1265
+ }
1266
+ if (mode === 'horizontal') {
1267
+ dPath.Q2 = [
1268
+ { x: dPath.L2.x, y: points.end.y },
1269
+ { x: dPath.L2.x + distancePoint * multiplierX * reverseMultiplier, y: points.end.y },
1270
+ ];
1271
+ dPath.L3 = { x: points.end.x, y: points.end.y };
1272
+ }
1273
+ }
1274
+ buildDAttributeByPointEndModeVertical(dPath, points, distancePoint, curve, mode) {
1275
+ if (!mode.includes('vertical')) {
1276
+ return;
1277
+ }
1278
+ const multiplierX = points.start.x < points.end.x ? -1 : 1;
1279
+ const multiplierY = points.start.y < points.end.y ? 1 : -1;
1280
+ const reverseMultiplier = -1;
1281
+ dPath.M = points.start;
1282
+ dPath.L = { x: points.start.x, y: points.start.y + distancePoint * multiplierY };
1283
+ if (mode.includes('vertical-single')) {
1284
+ dPath.L.y = points.end.y + distancePoint * multiplierY * reverseMultiplier;
1285
+ }
1286
+ dPath.Q = [
1287
+ { x: dPath.L.x, y: dPath.L.y + curve * multiplierY },
1288
+ { x: dPath.L.x + curve * multiplierX * reverseMultiplier, y: dPath.L.y + curve * multiplierY },
1289
+ ];
1290
+ dPath.L2 = { x: points.end.x + curve * 2 * multiplierX, y: dPath.Q[1].y };
1291
+ if (mode.includes('vertical-single')) {
1292
+ dPath.L2 = points.end;
1293
+ }
1294
+ if (mode === 'vertical') {
1295
+ dPath.Q2 = [
1296
+ { x: points.end.x, y: dPath.L2.y },
1297
+ { x: points.end.x, y: dPath.L2.y + curve * multiplierY },
1298
+ ];
1299
+ dPath.L3 = { x: points.end.x, y: points.end.y };
1300
+ }
1301
+ }
1302
+ builDAttributeToString(dAttribute) {
1303
+ let dString = '';
1304
+ Object.keys(dAttribute).forEach((key) => {
1305
+ const data = get(dAttribute, key);
1306
+ if (Array.isArray(data)) {
1307
+ const [point1, point2, point3] = data;
1308
+ dString = `${dString}${key.replace(/\d/g, '')}${point1.x},${point1.y} ${point2.x},${point2.y}${point3 ? ' ' + point3.x + ',' + point3.y : ''}
1309
+ `;
1310
+ return;
1311
+ }
1312
+ dString = `${dString}${key.replace(/\d/g, '')}${data?.x},${data?.y}
1313
+ `;
1314
+ });
1315
+ return dString;
1316
+ }
1317
+ updateStyleArrow(points, mode, data) {
1318
+ if (!points.arrowElement || data.ignoreDrawArrow) {
1319
+ return;
1320
+ }
1321
+ const styleConfig = {
1322
+ fill: data.arrowStyle?.fill ?? '#9CA2AD',
1323
+ stroke: data.arrowStyle?.stroke ?? 'none',
1324
+ strokeWidth: data.arrowStyle?.strokeWidth ?? '0',
1325
+ width: data.arrowStyle?.width ?? 6,
1326
+ height: data.arrowStyle?.height ?? 8,
1327
+ };
1328
+ const pointEnd = cloneDeep(points.end);
1329
+ const arrowElement = points.arrowElement;
1330
+ const direction = data.arrowDirection || this.getDirection(points, data.startEndMode);
1331
+ switch (direction) {
1332
+ case 'right':
1333
+ if (mode.includes('horizontal') || mode.includes('vertical')) {
1334
+ pointEnd.x -= styleConfig.height;
1335
+ }
1336
+ arrowElement?.setAttribute('points', `${pointEnd.x},${pointEnd.y - styleConfig.width} ${pointEnd.x + styleConfig.height},${pointEnd.y} ${pointEnd.x},${pointEnd.y + styleConfig.width} `);
1337
+ break;
1338
+ case 'left':
1339
+ if (mode.includes('horizontal') || mode.includes('vertical')) {
1340
+ pointEnd.x += styleConfig.height;
1341
+ }
1342
+ arrowElement?.setAttribute('points', `${pointEnd.x},${pointEnd.y - styleConfig.width} ${pointEnd.x - styleConfig.height},${pointEnd.y} ${pointEnd.x},${pointEnd.y + styleConfig.width} `);
1343
+ break;
1344
+ case 'top':
1345
+ if (mode.includes('horizontal') || mode.includes('vertical')) {
1346
+ pointEnd.y += styleConfig.height;
1347
+ }
1348
+ arrowElement?.setAttribute('points', `${pointEnd.x - styleConfig.width},${pointEnd.y} ${pointEnd.x},${pointEnd.y - styleConfig.height} ${pointEnd.x + styleConfig.width},${pointEnd.y} `);
1349
+ break;
1350
+ case 'bottom':
1351
+ if (mode.includes('horizontal') || mode.includes('vertical')) {
1352
+ pointEnd.y -= styleConfig.height;
1353
+ }
1354
+ arrowElement?.setAttribute('points', `${pointEnd.x - styleConfig.width},${pointEnd.y} ${pointEnd.x},${pointEnd.y + styleConfig.height} ${pointEnd.x + styleConfig.width},${pointEnd.y} `);
1355
+ break;
1356
+ }
1357
+ arrowElement?.setAttribute('fill', styleConfig.fill);
1358
+ arrowElement?.setAttribute('stroke', styleConfig.stroke);
1359
+ arrowElement?.setAttribute('stroke-width', styleConfig.strokeWidth);
1360
+ }
1361
+ updateAttributeCircle(circleElement, name, point, circleStyle) {
1362
+ circleElement.setAttribute('name', name);
1363
+ circleElement.setAttribute('cx', `${point.x}`);
1364
+ circleElement.setAttribute('cy', `${point.y}`);
1365
+ circleElement.setAttribute('r', `${circleStyle?.r ?? 2}`);
1366
+ circleElement.setAttribute('fill', `${circleStyle?.fill ?? 'green'}`);
1367
+ circleElement.setAttribute('stroke', `${circleStyle?.stroke ?? '#9CA2AD'}`);
1368
+ circleElement.setAttribute('stroke-width', `${circleStyle?.strokeWidth ?? '1px'}`);
1369
+ return circleElement;
1370
+ }
1371
+ getDirection(points, startEndMode) {
1372
+ let pointStart = { ...points.start };
1373
+ if (points.separatedPoints) {
1374
+ const lineToEnd = points.separatedPoints.find((item) => item.end.x === points.end.x && item.end.y === points.end.y);
1375
+ if (lineToEnd) {
1376
+ pointStart = lineToEnd.start;
1377
+ }
1378
+ }
1379
+ const modeHorizontal = pointStart.x < points.end.x ? 'right' : 'left';
1380
+ const modeVertical = pointStart.y < points.end.y ? 'bottom' : 'top';
1381
+ if (!startEndMode) {
1382
+ if (Math.abs(points.end.x - pointStart.x) > Math.abs(points.end.y - pointStart.y)) {
1383
+ return modeHorizontal;
1384
+ }
1385
+ return modeVertical;
1386
+ }
1387
+ switch (startEndMode) {
1388
+ case 'bottom-left':
1389
+ return modeHorizontal;
1390
+ case 'right-left':
1391
+ return modeHorizontal;
1392
+ case 'bottom-top':
1393
+ return modeVertical;
1394
+ }
1395
+ }
1396
+ initEvent(data) {
1397
+ const points = data.points;
1398
+ let preEvent = undefined;
1399
+ if ((!points.arrowElement && !points.circleEndElement) || !points.onDestroyEvent || points.initialized) {
1400
+ return;
1401
+ }
1402
+ points.initialized = true;
1403
+ const functionMouseUp = (mouseEvent) => {
1404
+ if (preEvent) {
1405
+ preEvent = undefined;
1406
+ this.onDrawLineEnd.next({ dataLine: data, event: mouseEvent });
1407
+ }
1408
+ };
1409
+ const handlerDrag = (dragMouseEvent) => {
1410
+ if (!preEvent) {
1411
+ preEvent = dragMouseEvent;
1412
+ }
1413
+ const pointEnd = cloneDeep(points.end);
1414
+ const movementX = dragMouseEvent.clientX - preEvent.clientX;
1415
+ const movementY = dragMouseEvent.clientY - preEvent.clientY;
1416
+ preEvent = dragMouseEvent;
1417
+ pointEnd.x += movementX;
1418
+ pointEnd.y += movementY;
1419
+ if (data.points?.obstacleRect?.length && data.points?.obstacleRect.some((obstacleRect) => this.checkPointIncludeRect(pointEnd, obstacleRect))) {
1420
+ return;
1421
+ }
1422
+ points.end.x = pointEnd.x;
1423
+ points.end.y = pointEnd.y;
1424
+ this.drawLine(data);
1425
+ this.updateViewBox();
1426
+ };
1427
+ if (points.arrowElement) {
1428
+ getDragEventByElement({
1429
+ elementMouseDown: points.arrowElement,
1430
+ functionMouseUp: functionMouseUp,
1431
+ onDestroy: this.onDestroy,
1432
+ })
1433
+ .pipe(tap(handlerDrag), takeUntil(points.onDestroyEvent))
1434
+ .subscribe();
1435
+ }
1436
+ if (points.circleEndElement) {
1437
+ getDragEventByElement({
1438
+ elementMouseDown: points.circleEndElement,
1439
+ functionMouseUp: functionMouseUp,
1440
+ onDestroy: this.onDestroy,
1441
+ })
1442
+ .pipe(tap(handlerDrag), takeUntil(points.onDestroyEvent))
1443
+ .subscribe();
1444
+ }
1445
+ }
1446
+ checkPointIncludeRect(point, obstacleRect) {
1447
+ const gapDefault = 8;
1448
+ obstacleRect = {
1449
+ x: obstacleRect.x - (obstacleRect.gapXObstacleRect || gapDefault),
1450
+ y: obstacleRect.y - (obstacleRect.gapYObstacleRect || gapDefault),
1451
+ width: obstacleRect.width + (obstacleRect.gapXObstacleRect || gapDefault) * 2,
1452
+ height: obstacleRect.height + (obstacleRect.gapYObstacleRect || gapDefault) * 2,
1453
+ };
1454
+ if (obstacleRect.x <= point.x && point.x <= obstacleRect.x + obstacleRect.width && obstacleRect.y <= point.y && point.y <= obstacleRect.y + obstacleRect.height) {
1455
+ return true;
1456
+ }
1457
+ return false;
1458
+ }
1459
+ createPathAndArrowElement(name) {
1460
+ const pathElement = document.createElementNS('http://www.w3.org/2000/svg', 'path');
1461
+ const arrowElement = document.createElementNS('http://www.w3.org/2000/svg', 'polyline');
1462
+ const circleStartElement = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
1463
+ const circleEndElement = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
1464
+ pathElement.setAttribute('name', name);
1465
+ arrowElement.setAttribute('name', name);
1466
+ this.svgElement.append(pathElement);
1467
+ this.svgElement.append(arrowElement);
1468
+ return { pathElement, arrowElement, circleStartElement, circleEndElement };
1469
+ }
1470
+ addCircle(name, point) {
1471
+ if (!this.drawRectDebug) {
1472
+ return;
1473
+ }
1474
+ const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
1475
+ circle.setAttribute('name', name);
1476
+ circle.setAttribute('cx', `${point.x}`);
1477
+ circle.setAttribute('cy', `${point.y}`);
1478
+ circle.setAttribute('r', '2');
1479
+ circle.setAttribute('style', 'fill: none; stroke: blue; stroke-width: 1px;');
1480
+ this.svgElement.appendChild(circle);
1481
+ }
1482
+ clearCircle() {
1483
+ if (!this.drawRectDebug) {
1484
+ return;
1485
+ }
1486
+ Array.from(this.svgElement.getElementsByTagName('circle'))?.forEach((item) => item.remove());
1487
+ }
1488
+ addRect(name, rect) {
1489
+ if (!this.drawRectDebug) {
1490
+ return;
1491
+ }
1492
+ const rectElement = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
1493
+ const textElement = document.createElementNS('http://www.w3.org/2000/svg', 'text');
1494
+ rectElement.setAttribute('name', name);
1495
+ rectElement.setAttribute('x', `${rect.x}`);
1496
+ rectElement.setAttribute('y', `${rect.y}`);
1497
+ rectElement.setAttribute('width', `${rect.width}`);
1498
+ rectElement.setAttribute('height', `${rect.height}`);
1499
+ rectElement.setAttribute('style', 'fill: blue; stroke: blue;');
1500
+ this.svgElement.appendChild(rectElement);
1501
+ textElement.setAttribute('x', `${rect.x}`);
1502
+ textElement.setAttribute('y', `${rect.y}`);
1503
+ textElement.textContent = rect.id ?? '';
1504
+ this.svgElement.appendChild(textElement);
1505
+ }
1506
+ clearRect() {
1507
+ if (!this.drawRectDebug) {
1508
+ return;
1509
+ }
1510
+ Array.from(this.svgElement.getElementsByTagName('rect'))?.forEach((item) => item.remove());
1511
+ Array.from(this.svgElement.getElementsByTagName('text'))?.forEach((item) => item.remove());
1512
+ }
1513
+ buildPathAndDrawSeparatedPoints(data, points, mode) {
1514
+ let dPath = {};
1515
+ const styleConfig = {
1516
+ curve: data.lineStyle?.curve ?? 10,
1517
+ distancePoint: data.lineStyle?.distancePoint ?? 10,
1518
+ };
1519
+ let { curve, distancePoint } = styleConfig;
1520
+ mode = mode || 'quart-in';
1521
+ if (mode !== 'quart-in') {
1522
+ curve = curve ?? 10;
1523
+ distancePoint = distancePoint ?? 10;
1524
+ }
1525
+ if (mode === 'quart-in' || points.start.x === points.end.x || points.start.y === points.end.y) {
1526
+ this.drawBalancedCurve(dPath, points);
1527
+ if (mode === 'quart-in') {
1528
+ dPath = {};
1529
+ this.drawBendBothEndsCurve(dPath, points);
1530
+ }
1531
+ return this.builDAttributeToString(dPath);
1532
+ }
1533
+ const result = this.calculatorDistancePointAndCurve(points, distancePoint, curve, mode);
1534
+ distancePoint = result.distancePoint;
1535
+ curve = result.curve;
1536
+ this.buildDAttributeByPointEndModeHorizontal(dPath, points, distancePoint || 0, curve || 0, mode);
1537
+ this.buildDAttributeByPointEndModeVertical(dPath, points, distancePoint || 0, curve || 0, mode);
1538
+ return this.builDAttributeToString(dPath);
1539
+ }
1540
+ ngOnDestroy() {
1541
+ this.onDestroy.next();
1542
+ this.onDestroy.complete();
1543
+ }
1544
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: LibsUiComponentsDrawLineDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1545
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.14", type: LibsUiComponentsDrawLineDirective, isStandalone: true, selector: "[LibsUiComponentsDrawLineDirective]", inputs: { viewBoxConfig: "viewBoxConfig", svgElement: "svgElement", drawRectDebug: "drawRectDebug" }, outputs: { outDrawLineFunctionControl: "outDrawLineFunctionControl", outConnected: "outConnected" }, ngImport: i0 });
1546
+ }
1547
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: LibsUiComponentsDrawLineDirective, decorators: [{
1548
+ type: Directive,
1549
+ args: [{
1550
+ // eslint-disable-next-line @angular-eslint/directive-selector
1551
+ selector: '[LibsUiComponentsDrawLineDirective]',
1552
+ standalone: true,
1553
+ }]
1554
+ }], propDecorators: { viewBoxConfig: [{
1555
+ type: Input
1556
+ }], svgElement: [{
1557
+ type: Input
1558
+ }], drawRectDebug: [{
1559
+ type: Input
1560
+ }], outDrawLineFunctionControl: [{
1561
+ type: Output
1562
+ }], outConnected: [{
1563
+ type: Output
1564
+ }] } });
1565
+
1566
+ /**
1567
+ * Generated bundle index. Do not edit.
1568
+ */
1569
+
1570
+ export { LibsUiComponentsDrawLineDirective };
1571
+ //# sourceMappingURL=libs-ui-components-draw-line.mjs.map