@libs-ui/components-draw-line 0.2.61

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