@libs-ui/components-draw-line 0.2.10-6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1547 @@
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 (MoCanvasConnectNavigationNewElementUtil.START_MODE !== 'bottom-left') {
111
+ if ((directionY === 'under' && bottomElementBlockRoad - (Q1.y ?? 0) <= 0)
112
+ || (directionY === 'above' && topElementBlockRoad - (Q1.y ?? 0) >= 0)) {
113
+ L1.y = Q1.y ?? 0;
114
+ }
115
+ if (Math.abs((L1.y ?? 0) - endY) <= 10) {
116
+ L1.y = MoCanvasCalculatorBranchUtil.mathOperatorsCalculation(directionX, L1.y, lineCurve);
117
+ }
118
+ }
119
+ const Q2 = this.drawLineQ({ x: L1.x, y: L1.y ?? 0 }, `${directionY === 'under' ? 'under' : 'above'}-${directionX}`, lineCurve, lineCurve);
120
+ const pathQ1 = `Q ${Q1.x1},${Q1.y1} ${Q1.x},${Q1.y} L ${L1.x} ${L1.y}`;
121
+ const pathQ2 = `Q ${Q2.x1},${Q2.y1} ${Q2.x},${Q2.y}`;
122
+ 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' });
123
+ 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' });
124
+ 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' });
125
+ return {
126
+ pathByPassQ: `${pathQ1} ${pathQ2}`,
127
+ endPath: {
128
+ x: Q2.x ?? 0,
129
+ y: Q2.y ?? 0
130
+ }
131
+ };
132
+ }
133
+ /**Tránh khối phía bên trên */
134
+ static byPassElementOnTopBottomSide(startX, startY, endX, lineCurve, elementBlockRoad, directionY, separatedPoints, key) {
135
+ const leftElementBlockRoad = (elementBlockRoad.x ?? 0);
136
+ const rightElementBlockRoad = (elementBlockRoad.x ?? 0) + (elementBlockRoad.width ?? 0);
137
+ const directionX = this.checkUpDownLeftRight(startX, endX, 'x') === 'left' ? 'left' : 'right'; // xem nên tránh sang trái hay phải
138
+ const Q1 = this.drawLineQ({ x: startX, y: startY ?? 0 }, `${directionY}-${directionX}`, lineCurve, lineCurve);
139
+ const L1 = {
140
+ x: (directionX === 'left' ? leftElementBlockRoad : rightElementBlockRoad),
141
+ y: Q1.y ?? 0
142
+ };
143
+ if ((directionX === 'right' && rightElementBlockRoad - (Q1.x ?? 0) <= 0) || (directionX === 'left' && leftElementBlockRoad - (Q1.x ?? 0) >= 0)) {
144
+ L1.x = Q1.x ?? 0;
145
+ }
146
+ const Q2 = this.drawLineQ({ x: L1.x, y: L1.y ?? 0 }, `${directionX}-${directionY}`, lineCurve, lineCurve);
147
+ if (Math.abs((Q2.x ?? 0) - leftElementBlockRoad) <= 10) {
148
+ Q2.x = MoCanvasCalculatorBranchUtil.mathOperatorsCalculation(directionX, Q2.x, lineCurve, true);
149
+ }
150
+ const pathQ1 = `Q ${Q1.x1},${Q1.y1} ${Q1.x},${Q1.y} L ${L1.x} ${L1.y}`;
151
+ const pathQ2 = `Q ${Q2.x1},${Q2.y1} ${Q2.x},${Q2.y}`;
152
+ separatedPoints.push({ start: { x: startX, y: startY }, end: { x: Q1.x ?? 0, y: Q1.y ?? 0 }, mode: 'vertical-single-curve', id: key ?? 'qb' });
153
+ 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' });
154
+ 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' });
155
+ return {
156
+ pathByPassQ: `${pathQ1} ${pathQ2}`,
157
+ endPath: {
158
+ x: Q2.x ?? 0,
159
+ y: Q2.y ?? 0
160
+ }
161
+ };
162
+ }
163
+ // kiểm tra đường thẳng và khối có cắt nhau không
164
+ static checkLinePassBlockHorizontal(start, end, xOrYOfLine, direction, obstacleRect, returnAllElementBlockRoad) {
165
+ const minimumDistanceFromBlock = 8;
166
+ start += minimumDistanceFromBlock + 1; // tránh khối xuất phát vào list
167
+ const listPointsOfLine = range(start, end + 1);
168
+ const elementBlockRoad = obstacleRect.filter(item => {
169
+ const left = (item.x ?? 0) - minimumDistanceFromBlock;
170
+ const right = (item.x ?? 0) + (item.width ?? 0) + minimumDistanceFromBlock;
171
+ const top = (item.y ?? 0) - minimumDistanceFromBlock;
172
+ const bottom = (item.y ?? 0) + (item.height ?? 0) + minimumDistanceFromBlock;
173
+ if (direction === 'x' && listPointsOfLine.find(el => (left <= el && el <= right)) && (top <= xOrYOfLine && xOrYOfLine <= bottom)) {
174
+ return true;
175
+ }
176
+ if (direction === 'y' && listPointsOfLine.find(el => (top <= el && el <= bottom)) && (left <= xOrYOfLine && xOrYOfLine <= right)) {
177
+ return true;
178
+ }
179
+ return false;
180
+ });
181
+ if (!elementBlockRoad || !elementBlockRoad.length) {
182
+ return undefined;
183
+ }
184
+ if (returnAllElementBlockRoad) {
185
+ return elementBlockRoad;
186
+ }
187
+ if (direction === 'y') {
188
+ return elementBlockRoad.reduce((a, b) => (Math.abs(((a.y ?? 0) - start))) < (Math.abs(((b.y ?? 0) - start))) ? a : b);
189
+ }
190
+ return elementBlockRoad.reduce((a, b) => (Math.abs(((a.x ?? 0) - start))) < (Math.abs(((b.x ?? 0) - start))) ? a : b);
191
+ }
192
+ // 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é.
193
+ static checkBlocksElementStraightY(startX, startY, endPosition, obstacleRect) {
194
+ const elementBlockRoad = this.checkLinePassBlockHorizontal(startY, endPosition.y, startX, 'y', obstacleRect, true);
195
+ if (!elementBlockRoad || !elementBlockRoad.length) {
196
+ return;
197
+ }
198
+ return elementBlockRoad.reduce((a, b) => (Math.abs((a.x ?? 0 - startX))) < (Math.abs((b.x ?? 0 - startX))) ? a : b);
199
+ }
200
+ // 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é.
201
+ static checkBlocksElementX(startX, startY, endPosition, obstacleRect) {
202
+ const elementBlockRoad = this.checkLinePassBlockHorizontal(startX, endPosition.x, startY, 'x', obstacleRect, true);
203
+ if (!elementBlockRoad || !elementBlockRoad.length) {
204
+ return;
205
+ }
206
+ return elementBlockRoad.reduce((a, b) => (Math.abs(((a.y ?? 0) - startY))) < (Math.abs(((b.y ?? 0) - startY))) ? a : b);
207
+ }
208
+ // kiểm tra hướng sang phải là tránh khối
209
+ static checkAndAvoidBlocksLeft(startX, startY, lineCurve, endPosition, obstacleRect, separatedPoints) {
210
+ const coordLX = { x: endPosition.x, y: startY };
211
+ const elementBlockRoad = this.checkLinePassBlockHorizontal(startX, endPosition.x, startY, 'x', obstacleRect);
212
+ if (!elementBlockRoad) {
213
+ separatedPoints.push({ start: { x: startX, y: startY }, end: coordLX, mode: 'horizontal', id: '14' });
214
+ return coordLX;
215
+ }
216
+ const directionX = MoCanvasCalculatorDirectionElementUtil.checkUpDownLeftRight(startX, endPosition.x, 'x');
217
+ const specificXElement = directionX === 'right' ? (elementBlockRoad.x ?? 0) : ((elementBlockRoad.x ?? 0) + (elementBlockRoad.width ?? 0));
218
+ coordLX.x = specificXElement + (MoCanvasConnectNavigationNewElementUtil.ELEMENT_MARGIN_BETWEEN_BRANCH_DEFAULT / 2) + (lineCurve / 2);
219
+ if (directionX === 'right') {
220
+ coordLX.x = specificXElement - (MoCanvasConnectNavigationNewElementUtil.ELEMENT_MARGIN_BETWEEN_BRANCH_DEFAULT / 2) - (lineCurve / 2);
221
+ }
222
+ let startXBypassElement = coordLX.x;
223
+ if ((directionX === 'left' && startX - (coordLX.x ?? 0) <= 0) || (directionX === 'right' && startX - (coordLX.x ?? 0) >= 0)) {
224
+ coordLX.x = startX;
225
+ startXBypassElement = MoCanvasCalculatorBranchUtil.mathOperatorsCalculation(directionX, coordLX.x, lineCurve);
226
+ }
227
+ let lineByPassBlock = [];
228
+ let { endPath } = MoCanvasCalculatorDirectionElementUtil.byPassElementOnRightSide(startXBypassElement ?? 0, coordLX.y ?? 0, endPosition.y, lineCurve, elementBlockRoad, directionX, lineByPassBlock, undefined, coordLX.x);
229
+ if (MoCanvasConnectNavigationNewElementUtil.START_MODE === 'bottom-left' && elementBlockRoad.x + (elementBlockRoad.width) / 2 === endPosition.x && Math.abs(endPosition.y - elementBlockRoad.y) <= 20) { // 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') { // điểm out luôn ở phía bên dưới
534
+ start_x = (elementFrom.x ?? 0) + ((elementFrom.width ?? 0) / 2);
535
+ start_y = (elementFrom.y ?? 0) + (elementFrom.height ?? 0);
536
+ }
537
+ if (MoCanvasConnectNavigationNewElementUtil.START_MODE === 'bottom-left') { // điểm out luôn ở phía bên dưới
538
+ start_x = (elementFrom.x ?? 0) + ((elementFrom.width ?? 0) / 2);
539
+ start_y = (elementFrom.y ?? 0) + (elementFrom.height ?? 0);
540
+ }
541
+ const M_start = { x: start_x, y: start_y };
542
+ const directionX = MoCanvasCalculatorDirectionElementUtil.checkUpDownLeftRight(start_x, elementNext.x, 'x');
543
+ switch (MoCanvasConnectNavigationNewElementUtil.START_MODE) {
544
+ case 'right-left':
545
+ if (directionX === 'left') {
546
+ this.lineConnectLeft(elementNext, elementFrom, M_start, curve, obstacleRect, separatedPoints);
547
+ return;
548
+ }
549
+ this.lineConnectRight(elementNext, M_start, curve, obstacleRect, separatedPoints);
550
+ break;
551
+ case 'bottom-top':
552
+ if (directionX === 'left') {
553
+ this.lineConnectLeftWithBottomStart(elementNext, elementFrom, M_start, curve, obstacleRect, separatedPoints);
554
+ return;
555
+ }
556
+ this.lineConnectRightWithBottomStart(elementNext, M_start, curve, obstacleRect, separatedPoints);
557
+ break;
558
+ case 'bottom-left':
559
+ console.log(directionX);
560
+ if (directionX === 'left') {
561
+ MoCanvasConnectNavigationBottomLeftElementUtil.lineLeft(elementNext, elementFrom, M_start, curve, obstacleRect, separatedPoints);
562
+ return;
563
+ }
564
+ MoCanvasConnectNavigationBottomLeftElementUtil.rightLeft(elementNext, M_start, curve, obstacleRect, separatedPoints);
565
+ break;
566
+ }
567
+ }
568
+ static lineConnectRight(elementNext, start, lineCurve, obstacleRect, separatedPoints) {
569
+ const endPosition = {
570
+ x: (elementNext.x ?? 0) - (lineCurve * 3),
571
+ y: (elementNext.y ?? 0) + (elementNext.height ?? 0) / 2
572
+ };
573
+ const endPositionX = cloneDeep(endPosition);
574
+ let separatedPointsCheckAndAvoidBlocks = [];
575
+ let { path, endPath } = MoCanvasCalculatorDirectionElementUtil.checkAndAvoidBlocks(start.x ?? 0, start.y ?? 0, lineCurve, endPositionX, obstacleRect, separatedPointsCheckAndAvoidBlocks);
576
+ if (Math.abs((start.x ?? 0) - endPosition.x) < 10) {
577
+ path = '';
578
+ endPath = {
579
+ x: start.x,
580
+ y: start.y
581
+ };
582
+ separatedPointsCheckAndAvoidBlocks = [];
583
+ }
584
+ separatedPoints.splice(separatedPoints.length, 0, ...separatedPointsCheckAndAvoidBlocks);
585
+ // 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
586
+ const directionY = MoCanvasCalculatorDirectionElementUtil.checkUpDownLeftRight(endPath.y, endPosition.y, 'y');
587
+ if (directionY === 'center') {
588
+ const LEnd = { x: elementNext.x ?? 0, y: endPath.y };
589
+ separatedPoints.push({ start: endPath, end: LEnd, mode: 'horizontal', id: 'f' });
590
+ return {
591
+ dPath: `${path} L ${LEnd.x} ${LEnd.y}`,
592
+ endPath: LEnd
593
+ };
594
+ }
595
+ let lineCurveSubY = lineCurve;
596
+ if (Math.abs((endPath.y ?? 0) - endPosition.y) < lineCurve * 2) {
597
+ lineCurveSubY = Math.abs((endPath.y ?? 0) - endPosition.y) / 2;
598
+ 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
599
+ const pathQ1 = `Q ${coordQ1.x1},${coordQ1.y1} ${coordQ1.x},${coordQ1.y}`; // từ ngang sang thẳng phải có đường cong
600
+ 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
601
+ const pathQ2 = `Q ${coordQ2.x1},${coordQ2.y1} ${coordQ2.x},${coordQ2.y}`;
602
+ separatedPoints.push({ start: endPath, end: { x: coordQ1.x ?? 0, y: coordQ1.y ?? 0 }, mode: 'horizontal-single-curve', id: 'g' });
603
+ 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' });
604
+ const lineClose = this.closeLine({ x: coordQ2.x ?? 0, y: coordQ2.y ?? 0 }, endPosition, lineCurve, elementNext, separatedPoints);
605
+ return {
606
+ dPath: `${path} ${pathQ1} ${pathQ2} ${lineClose}`,
607
+ };
608
+ }
609
+ 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
610
+ const pathQ1 = `Q ${coordQ1.x1},${coordQ1.y1} ${coordQ1.x},${coordQ1.y}`; // từ ngang sang thẳng phải có đường cong
611
+ const endPositionY = cloneDeep(endPosition);
612
+ endPositionY.y = MoCanvasCalculatorBranchUtil.mathOperatorsCalculation(directionY, endPosition.y, lineCurve);
613
+ const straightLine = MoCanvasCalculatorDirectionElementUtil.checkAndAvoidBlocksY(coordQ1.x ?? 0, coordQ1.y ?? 0, lineCurve, endPositionY, directionY, obstacleRect, separatedPoints);
614
+ 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
615
+ const pathQ2 = `Q ${coordQ2.x1},${coordQ2.y1} ${coordQ2.x},${coordQ2.y}`; // từ ngang sang thẳng phải có đường cong
616
+ 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' });
617
+ 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' });
618
+ const lineClose = this.closeLine({ x: coordQ2.x ?? 0, y: coordQ2.y ?? 0 }, endPosition, lineCurve, elementNext, separatedPoints);
619
+ return {
620
+ dPath: `${path} ${pathQ1} ${straightLine.path} ${pathQ2} ${lineClose}`,
621
+ endPath: straightLine.endPath
622
+ };
623
+ }
624
+ static lineConnectLeft(elementNext, elementFrom, start, lineCurve, obstacleRect, separatedPoints) {
625
+ const endPositionOrigin = {
626
+ x: (elementNext.x ?? 0) - (lineCurve * 1.5),
627
+ y: (elementNext.y ?? 0) + (elementNext.height ?? 0) / 2
628
+ };
629
+ const endPosition = cloneDeep(endPositionOrigin);
630
+ const directionStartY = MoCanvasCalculatorDirectionElementUtil.checkUpDownLeftRight(start.y, endPositionOrigin.y, 'y');
631
+ const endY = (directionStartY === 'under' || directionStartY === 'center') ? (elementNext.y ?? 0) : (elementNext.y ?? 0) + (elementNext.height ?? 0);
632
+ endPosition.y = MoCanvasCalculatorBranchUtil.mathOperatorsCalculation(directionStartY, endY, (lineCurve * 2));
633
+ const pathQ = MoCanvasCalculatorDirectionElementUtil.drawLineQ({ x: (start.x ?? 0) + 10, y: start.y ?? 0 }, `right-${directionStartY === 'under' ? 'under' : 'above'}`, lineCurve, lineCurve);
634
+ const startQL = { lx: pathQ.x, ly: MoCanvasCalculatorBranchUtil.mathOperatorsCalculation(directionStartY, (pathQ.y ?? 0), ((elementFrom.height ?? 0) / 2), true) };
635
+ 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' });
636
+ separatedPoints.push({ start: { x: pathQ.x ?? 0, y: pathQ.y ?? 0 }, end: { x: startQL.lx ?? 0, y: startQL.ly }, mode: 'horizontal', id: '2' });
637
+ if (directionStartY === 'center') {
638
+ 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
639
+ 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' });
640
+ const endPath = MoCanvasCalculatorDirectionElementUtil.checkAndAvoidBlocksLeft(coordQ1.x ?? 0, coordQ1.y ?? 0, lineCurve, endPosition, obstacleRect, separatedPoints);
641
+ const directionStartY2 = MoCanvasCalculatorDirectionElementUtil.checkUpDownLeftRight(endPath.y, endPositionOrigin.y, 'y');
642
+ 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
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
+ this.closeLine({ x: coordQ2.x ?? 0, y: coordQ2.y ?? 0 }, endPositionOrigin, lineCurve, elementNext, separatedPoints);
645
+ return {
646
+ dPath: ``,
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
+ let path = '';
1070
+ points.separatedPoints.forEach(separatedPoints => {
1071
+ path += this.buildPathAndDrawSeparatedPoints(data, separatedPoints, separatedPoints.mode || mode);
1072
+ });
1073
+ const pathElement = document.createElementNS("http://www.w3.org/2000/svg", "path");
1074
+ this.svgElement.append(pathElement);
1075
+ points.pathElement = pathElement;
1076
+ const styleConfig = {
1077
+ stroke: data.lineStyle?.stroke ?? '#9CA2AD',
1078
+ strokeDasharray: data.lineStyle?.strokeDasharray,
1079
+ strokeWidth: data.lineStyle?.strokeWidth ?? '1px',
1080
+ fill: data.lineStyle?.fill ?? 'none',
1081
+ curve: data.lineStyle?.curve ?? 10,
1082
+ distancePoint: data.lineStyle?.distancePoint ?? 10
1083
+ };
1084
+ points.pathElement?.setAttribute('stroke', styleConfig.stroke);
1085
+ points.pathElement?.setAttribute('fill', styleConfig.fill);
1086
+ points.pathElement?.setAttribute('stroke-width', styleConfig.strokeWidth);
1087
+ if (styleConfig.strokeDasharray) {
1088
+ points.pathElement?.setAttribute('stroke-dasharray', styleConfig.strokeDasharray);
1089
+ }
1090
+ points.pathElement?.setAttribute('d', path);
1091
+ this.drawRectAndInitEvent(data, mode);
1092
+ });
1093
+ }
1094
+ updateViewBox(viewBoxConfig) {
1095
+ if (viewBoxConfig) {
1096
+ this.viewBoxConfig = viewBoxConfig;
1097
+ }
1098
+ const xPoints = new Set([0]);
1099
+ const yPoints = new Set([0]);
1100
+ const addPoints = (points) => {
1101
+ xPoints.add(points.start.x);
1102
+ xPoints.add(points.end.x);
1103
+ yPoints.add(points.start.y);
1104
+ yPoints.add(points.end.y);
1105
+ };
1106
+ this.dataDraw?.forEach(item => {
1107
+ const points = item.points;
1108
+ addPoints(points);
1109
+ points.separatedPoints?.forEach(separatedPoints => addPoints(separatedPoints));
1110
+ points.obstacleRect?.forEach(obstacleRect => {
1111
+ addPoints({
1112
+ start: { x: obstacleRect.x - (obstacleRect.gapXObstacleRect || 8), y: obstacleRect.y - (obstacleRect.gapYObstacleRect || 8) },
1113
+ end: { x: obstacleRect.x + obstacleRect.width + (obstacleRect.gapXObstacleRect || 8) * 2, y: obstacleRect.y + obstacleRect.height + (obstacleRect.gapYObstacleRect || 8) * 2 }
1114
+ });
1115
+ });
1116
+ });
1117
+ const xMax = this.viewBoxConfig?.width ?? (xPoints.size ? Math.max(...xPoints) : 0) + 14;
1118
+ const yMax = this.viewBoxConfig?.height ?? (yPoints.size ? Math.max(...yPoints) : 0) + 14;
1119
+ if (!this.viewBoxConfig?.ignoreViewBox) {
1120
+ const rectSvgElement = this.svgElement.getBoundingClientRect();
1121
+ this.svgElement?.setAttribute('viewBox', `${this.viewBoxConfig?.minX ?? rectSvgElement.x} ${this.viewBoxConfig?.minY ?? rectSvgElement.y} ${xMax} ${yMax}`);
1122
+ }
1123
+ this.svgElement?.setAttribute('width', `${xMax ?? 0}`);
1124
+ this.svgElement?.setAttribute('height', `${yMax ?? 0}`);
1125
+ }
1126
+ isPointInsideSquare(point, square) {
1127
+ const { x, y } = point;
1128
+ const { x: squareX, y: squareY, width, height } = square;
1129
+ return x >= squareX && x <= squareX + width && y >= squareY && y <= squareY + height;
1130
+ }
1131
+ calculatorSeparatedPoints(name, points, curve, separatedPoints, obstacleRects) {
1132
+ if (!obstacleRects.length) {
1133
+ return;
1134
+ }
1135
+ const elementStart = obstacleRects.find(item => this.isPointInsideSquare(points.start, item));
1136
+ const elementEnd = obstacleRects.find(item => this.isPointInsideSquare(points.end, item));
1137
+ if (this.viewBoxConfig?.marginBetweenElement) {
1138
+ MoCanvasConnectNavigationNewElementUtil.ELEMENT_MARGIN_BETWEEN_BRANCH_DEFAULT = this.viewBoxConfig?.marginBetweenElement ?? 32;
1139
+ }
1140
+ MoCanvasConnectNavigationNewElementUtil.setXYConnectElements(elementStart ?? { ...points.start, width: 1, height: 1 }, elementEnd ?? { ...points.end, width: 1, height: 1 }, obstacleRects, separatedPoints, curve);
1141
+ }
1142
+ buildPathAndDraw(data, points, mode) {
1143
+ let dPath = {};
1144
+ const styleConfig = {
1145
+ stroke: data.lineStyle?.stroke ?? '#9CA2AD',
1146
+ strokeDasharray: data.lineStyle?.strokeDasharray,
1147
+ strokeWidth: data.lineStyle?.strokeWidth ?? '1px',
1148
+ fill: data.lineStyle?.fill ?? 'none',
1149
+ curve: data.lineStyle?.curve ?? 10,
1150
+ distancePoint: data.lineStyle?.distancePoint ?? 10
1151
+ };
1152
+ points.pathElement?.setAttribute('stroke', styleConfig.stroke);
1153
+ points.pathElement?.setAttribute('fill', styleConfig.fill);
1154
+ points.pathElement?.setAttribute('stroke-width', styleConfig.strokeWidth);
1155
+ if (styleConfig.strokeDasharray) {
1156
+ points.pathElement?.setAttribute('stroke-dasharray', styleConfig.strokeDasharray);
1157
+ }
1158
+ let { curve, distancePoint } = styleConfig;
1159
+ mode = mode || "quart-in";
1160
+ if (mode !== 'quart-in') {
1161
+ curve = curve ?? 10;
1162
+ distancePoint = distancePoint ?? 10;
1163
+ }
1164
+ if (mode === 'quart-in' || points.start.x === points.end.x || points.start.y === points.end.y) {
1165
+ this.drawBalancedCurve(dPath, points);
1166
+ if (mode === 'quart-in') {
1167
+ dPath = {};
1168
+ this.drawBendBothEndsCurve(dPath, points);
1169
+ }
1170
+ points.pathElement?.setAttribute('d', this.builDAttributeToString(dPath));
1171
+ this.drawRectAndInitEvent(data, mode);
1172
+ return;
1173
+ }
1174
+ const result = this.calculatorDistancePointAndCurve(points, distancePoint, curve, mode);
1175
+ distancePoint = result.distancePoint;
1176
+ curve = result.curve;
1177
+ this.buildDAttributeByPointEndModeHorizontal(dPath, points, distancePoint || 0, curve || 0, mode);
1178
+ this.buildDAttributeByPointEndModeVertical(dPath, points, distancePoint || 0, curve || 0, mode);
1179
+ points.pathElement?.setAttribute('d', this.builDAttributeToString(dPath));
1180
+ this.drawRectAndInitEvent(data, mode);
1181
+ }
1182
+ calculatorDistancePointAndCurve(points, distancePoint, curve, mode) {
1183
+ if ((!mode.includes('vertical') && !mode.includes('horizontal'))) {
1184
+ return { curve, distancePoint };
1185
+ }
1186
+ const gapX = Math.abs(points.start.x - points.end.x);
1187
+ const gapY = Math.abs(points.start.y - points.end.y);
1188
+ const multiplierCurve = mode.includes('horizontal') ? 1 : 2;
1189
+ const multiplierDistance = mode.includes('horizontal') ? 2 : 1;
1190
+ if (!mode.includes('single-curve')) {
1191
+ if (gapX < distancePoint * multiplierDistance + curve * multiplierCurve) {
1192
+ distancePoint = curve = gapX / 3;
1193
+ }
1194
+ if (gapY < distancePoint * multiplierDistance + curve * multiplierCurve) {
1195
+ distancePoint = curve = gapY / 3;
1196
+ }
1197
+ return { curve, distancePoint };
1198
+ }
1199
+ const minGap = Math.min(gapX, gapY);
1200
+ if (minGap < distancePoint) {
1201
+ distancePoint = minGap;
1202
+ }
1203
+ if (minGap < curve) {
1204
+ curve = minGap;
1205
+ }
1206
+ return { curve, distancePoint };
1207
+ }
1208
+ drawRectAndInitEvent(data, mode) {
1209
+ const points = data.points;
1210
+ this.updateStyleArrow(points, mode, data);
1211
+ if (data.startCircle && points.circleStartElement) {
1212
+ this.updateAttributeCircle(points.circleStartElement, data.id, points.start, data.startCircleStyle);
1213
+ }
1214
+ if (data.endCircle && points.circleEndElement) {
1215
+ this.updateAttributeCircle(points.circleEndElement, data.id, points.end, data.endCircleStyle);
1216
+ }
1217
+ this.initEvent(data);
1218
+ }
1219
+ drawBalancedCurve(dPath, points) {
1220
+ dPath.M = { x: points.start.x, y: points.start.y };
1221
+ 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 }];
1222
+ 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 }];
1223
+ }
1224
+ drawBendBothEndsCurve(dPath, points) {
1225
+ const pointXCurve = (points.start.x + points.end.x) * 0.5;
1226
+ const pointControl1 = { x: pointXCurve, y: points.start.y };
1227
+ const pointControl2 = { x: pointXCurve, y: points.end.y };
1228
+ dPath.M = { x: points.start.x, y: points.start.y };
1229
+ dPath.C = [{ x: pointControl1.x, y: pointControl1.y }, { x: pointControl2.x, y: pointControl2.y }, { x: points.end.x, y: points.end.y }];
1230
+ }
1231
+ buildDAttributeByPointEndModeHorizontal(dPath, points, distancePoint, curve, mode) {
1232
+ if (!mode.includes('horizontal')) {
1233
+ return;
1234
+ }
1235
+ const multiplierX = points.start.x < points.end.x ? -1 : 1;
1236
+ const multiplierY = points.start.y < points.end.y ? 1 : -1;
1237
+ const reverseMultiplier = -1;
1238
+ dPath.M = { x: points.start.x, y: points.start.y };
1239
+ dPath.L = { x: points.end.x + ((distancePoint * 2 + curve) * multiplierX), y: points.start.y };
1240
+ if (mode.includes('horizontal-single')) {
1241
+ dPath.L.x = points.end.x + (curve * multiplierX);
1242
+ }
1243
+ 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) }];
1244
+ dPath.L2 = { x: dPath.Q[1].x, y: points.end.y + (curve * multiplierY * reverseMultiplier) };
1245
+ if (mode.includes('horizontal-single')) {
1246
+ dPath.L2 = points.end;
1247
+ }
1248
+ if (mode === 'horizontal') {
1249
+ dPath.Q2 = [{ x: dPath.L2.x, y: points.end.y }, { x: dPath.L2.x + (distancePoint * multiplierX * reverseMultiplier), y: points.end.y }];
1250
+ dPath.L3 = { x: points.end.x, y: points.end.y };
1251
+ }
1252
+ }
1253
+ buildDAttributeByPointEndModeVertical(dPath, points, distancePoint, curve, mode) {
1254
+ if (!mode.includes('vertical')) {
1255
+ return;
1256
+ }
1257
+ const multiplierX = points.start.x < points.end.x ? -1 : 1;
1258
+ const multiplierY = points.start.y < points.end.y ? 1 : -1;
1259
+ const reverseMultiplier = -1;
1260
+ dPath.M = points.start;
1261
+ dPath.L = { x: points.start.x, y: points.start.y + (distancePoint * multiplierY) };
1262
+ if (mode.includes('vertical-single')) {
1263
+ dPath.L.y = points.end.y + (distancePoint * multiplierY * reverseMultiplier);
1264
+ }
1265
+ 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) }];
1266
+ dPath.L2 = { x: points.end.x + ((curve * 2) * multiplierX), y: dPath.Q[1].y };
1267
+ if (mode.includes('vertical-single')) {
1268
+ dPath.L2 = points.end;
1269
+ }
1270
+ if (mode === 'vertical') {
1271
+ dPath.Q2 = [{ x: points.end.x, y: dPath.L2.y }, { x: points.end.x, y: dPath.L2.y + (curve * multiplierY) }];
1272
+ dPath.L3 = { x: points.end.x, y: points.end.y };
1273
+ }
1274
+ }
1275
+ builDAttributeToString(dAttribute) {
1276
+ let dString = '';
1277
+ Object.keys(dAttribute).forEach(key => {
1278
+ const data = get(dAttribute, key);
1279
+ if (Array.isArray(data)) {
1280
+ const [point1, point2, point3] = data;
1281
+ dString = `${dString}${key.replace(/\d/g, '')}${point1.x},${point1.y} ${point2.x},${point2.y}${point3 ? ' ' + point3.x + ',' + point3.y : ''}
1282
+ `;
1283
+ return;
1284
+ }
1285
+ dString = `${dString}${key.replace(/\d/g, '')}${data.x},${data.y}
1286
+ `;
1287
+ });
1288
+ return dString;
1289
+ }
1290
+ updateStyleArrow(points, mode, data) {
1291
+ if (!points.arrowElement || data.ignoreDrawArrow) {
1292
+ return;
1293
+ }
1294
+ const styleConfig = {
1295
+ fill: data.arrowStyle?.fill ?? '#9CA2AD',
1296
+ stroke: data.arrowStyle?.stroke ?? 'none',
1297
+ strokeWidth: data.arrowStyle?.strokeWidth ?? '0',
1298
+ width: data.arrowStyle?.width ?? 6,
1299
+ height: data.arrowStyle?.height ?? 8
1300
+ };
1301
+ const pointEnd = cloneDeep(points.end);
1302
+ const arrowElement = points.arrowElement;
1303
+ const direction = data.arrowDirection || this.getDirection(points, data.startEndMode);
1304
+ switch (direction) {
1305
+ case 'right':
1306
+ if (mode.includes('horizontal') || mode.includes('vertical')) {
1307
+ pointEnd.x -= styleConfig.height;
1308
+ }
1309
+ arrowElement?.setAttribute('points', `${pointEnd.x},${pointEnd.y - styleConfig.width} ${pointEnd.x + styleConfig.height},${pointEnd.y} ${pointEnd.x},${pointEnd.y + styleConfig.width} `);
1310
+ break;
1311
+ case 'left':
1312
+ if (mode.includes('horizontal') || mode.includes('vertical')) {
1313
+ pointEnd.x += styleConfig.height;
1314
+ }
1315
+ arrowElement?.setAttribute('points', `${pointEnd.x},${pointEnd.y - styleConfig.width} ${pointEnd.x - styleConfig.height},${pointEnd.y} ${pointEnd.x},${pointEnd.y + styleConfig.width} `);
1316
+ break;
1317
+ case 'top':
1318
+ if (mode.includes('horizontal') || mode.includes('vertical')) {
1319
+ pointEnd.y += styleConfig.height;
1320
+ }
1321
+ arrowElement?.setAttribute('points', `${pointEnd.x - styleConfig.width},${pointEnd.y} ${pointEnd.x},${pointEnd.y - styleConfig.height} ${pointEnd.x + styleConfig.width},${pointEnd.y} `);
1322
+ break;
1323
+ case 'bottom':
1324
+ if (mode.includes('horizontal') || mode.includes('vertical')) {
1325
+ pointEnd.y -= styleConfig.height;
1326
+ }
1327
+ arrowElement?.setAttribute('points', `${pointEnd.x - styleConfig.width},${pointEnd.y} ${pointEnd.x},${pointEnd.y + styleConfig.height} ${pointEnd.x + styleConfig.width},${pointEnd.y} `);
1328
+ break;
1329
+ }
1330
+ arrowElement?.setAttribute('fill', styleConfig.fill);
1331
+ arrowElement?.setAttribute('stroke', styleConfig.stroke);
1332
+ arrowElement?.setAttribute('stroke-width', styleConfig.strokeWidth);
1333
+ }
1334
+ updateAttributeCircle(circleElement, name, point, circleStyle) {
1335
+ circleElement.setAttribute('name', name);
1336
+ circleElement.setAttribute('cx', `${point.x}`);
1337
+ circleElement.setAttribute('cy', `${point.y}`);
1338
+ circleElement.setAttribute('r', `${circleStyle?.r ?? 2}`);
1339
+ circleElement.setAttribute('fill', `${circleStyle?.fill ?? 'green'}`);
1340
+ circleElement.setAttribute('stroke', `${circleStyle?.stroke ?? '#9CA2AD'}`);
1341
+ circleElement.setAttribute('stroke-width', `${circleStyle?.strokeWidth ?? '1px'}`);
1342
+ return circleElement;
1343
+ }
1344
+ getDirection(points, startEndMode) {
1345
+ let pointStart = { ...points.start };
1346
+ if (points.separatedPoints) {
1347
+ const lineToEnd = points.separatedPoints.find(item => item.end.x === points.end.x && item.end.y === points.end.y);
1348
+ if (lineToEnd) {
1349
+ pointStart = lineToEnd.start;
1350
+ }
1351
+ }
1352
+ const modeHorizontal = pointStart.x < points.end.x ? 'right' : 'left';
1353
+ const modeVertical = pointStart.y < points.end.y ? 'bottom' : 'top';
1354
+ if (!startEndMode) {
1355
+ if (Math.abs(points.end.x - pointStart.x) > Math.abs(points.end.y - pointStart.y)) {
1356
+ return modeHorizontal;
1357
+ }
1358
+ return modeVertical;
1359
+ }
1360
+ switch (startEndMode) {
1361
+ case 'bottom-left':
1362
+ return modeHorizontal;
1363
+ case 'right-left':
1364
+ return modeHorizontal;
1365
+ case 'bottom-top':
1366
+ return modeVertical;
1367
+ }
1368
+ }
1369
+ initEvent(data) {
1370
+ const points = data.points;
1371
+ let preEvent = undefined;
1372
+ if ((!points.arrowElement && !points.circleEndElement) || !points.onDestroyEvent || points.initialized) {
1373
+ return;
1374
+ }
1375
+ points.initialized = true;
1376
+ const stopEventMouse = (mouseEvent) => {
1377
+ mouseEvent.preventDefault();
1378
+ mouseEvent.stopPropagation();
1379
+ };
1380
+ const documentMouseUp = fromEvent(document, 'mouseup')
1381
+ .pipe(tap((eventMouseUp) => {
1382
+ stopEventMouse(eventMouseUp);
1383
+ if (preEvent) {
1384
+ preEvent = undefined;
1385
+ this.onDrawLineEnd.next({ dataLine: data, event: eventMouseUp });
1386
+ }
1387
+ }), takeUntil(points.onDestroyEvent), takeUntil(this.onDestroy));
1388
+ const documentMouseMove = fromEvent(document, 'mousemove').pipe(tap((eventMousemove) => stopEventMouse(eventMousemove)), takeUntil((documentMouseUp)), takeUntil(points.onDestroyEvent), takeUntil(this.onDestroy));
1389
+ const handlerDrag = (dragMouseEvent) => {
1390
+ if (!preEvent) {
1391
+ preEvent = dragMouseEvent;
1392
+ }
1393
+ const pointEnd = cloneDeep(points.end);
1394
+ const movementX = dragMouseEvent.clientX - preEvent.clientX;
1395
+ const movementY = dragMouseEvent.clientY - preEvent.clientY;
1396
+ preEvent = dragMouseEvent;
1397
+ pointEnd.x += movementX;
1398
+ pointEnd.y += movementY;
1399
+ if (data.points?.obstacleRect?.length && data.points?.obstacleRect.some(obstacleRect => this.checkPointIncludeRect(pointEnd, obstacleRect))) {
1400
+ return;
1401
+ }
1402
+ points.end.x = pointEnd.x;
1403
+ points.end.y = pointEnd.y;
1404
+ this.drawLine(data);
1405
+ this.updateViewBox();
1406
+ };
1407
+ if (points.arrowElement) {
1408
+ const elementMouseDownArrow = fromEvent(points.arrowElement, 'mousedown').pipe(tap(eventMousedown => {
1409
+ stopEventMouse(eventMousedown);
1410
+ }), takeUntil(points.onDestroyEvent), takeUntil(this.onDestroy));
1411
+ const dragArrow = elementMouseDownArrow.pipe(mergeMap((mouseEvent) => documentMouseMove.pipe(startWith((mouseEvent)))));
1412
+ dragArrow.pipe(tap(handlerDrag), takeUntil(points.onDestroyEvent), takeUntil(this.onDestroy)).subscribe();
1413
+ }
1414
+ if (points.circleEndElement) {
1415
+ const elementMouseDownCircle = fromEvent(points.circleEndElement, 'mousedown').pipe(tap(eventMousedown => {
1416
+ stopEventMouse(eventMousedown);
1417
+ }), takeUntil(points.onDestroyEvent), takeUntil(this.onDestroy));
1418
+ const dragCircle = elementMouseDownCircle.pipe(mergeMap((mouseEvent) => documentMouseMove.pipe(startWith((mouseEvent)))));
1419
+ dragCircle.pipe(tap(handlerDrag), takeUntil(points.onDestroyEvent), takeUntil(this.onDestroy)).subscribe();
1420
+ }
1421
+ }
1422
+ checkPointIncludeRect(point, obstacleRect) {
1423
+ const gapDefault = 8;
1424
+ obstacleRect = {
1425
+ x: obstacleRect.x - (obstacleRect.gapXObstacleRect || gapDefault),
1426
+ y: obstacleRect.y - (obstacleRect.gapYObstacleRect || gapDefault),
1427
+ width: obstacleRect.width + (obstacleRect.gapXObstacleRect || gapDefault) * 2,
1428
+ height: obstacleRect.height + (obstacleRect.gapYObstacleRect || gapDefault) * 2
1429
+ };
1430
+ if ((obstacleRect.x <= point.x && point.x <= obstacleRect.x + obstacleRect.width) && (obstacleRect.y <= point.y && point.y <= obstacleRect.y + obstacleRect.height)) {
1431
+ return true;
1432
+ }
1433
+ return false;
1434
+ }
1435
+ createPathAndArrowElement(name) {
1436
+ const pathElement = document.createElementNS("http://www.w3.org/2000/svg", "path");
1437
+ const arrowElement = document.createElementNS("http://www.w3.org/2000/svg", "polyline");
1438
+ const circleStartElement = document.createElementNS("http://www.w3.org/2000/svg", "circle");
1439
+ const circleEndElement = document.createElementNS("http://www.w3.org/2000/svg", "circle");
1440
+ pathElement.setAttribute('name', name);
1441
+ arrowElement.setAttribute('name', name);
1442
+ this.svgElement.append(pathElement);
1443
+ this.svgElement.append(arrowElement);
1444
+ return { pathElement, arrowElement, circleStartElement, circleEndElement };
1445
+ }
1446
+ addCircle(name, point) {
1447
+ if (!this.drawRectDebug) {
1448
+ return;
1449
+ }
1450
+ const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
1451
+ circle.setAttribute('name', name);
1452
+ circle.setAttribute('cx', `${point.x}`);
1453
+ circle.setAttribute('cy', `${point.y}`);
1454
+ circle.setAttribute('r', '2');
1455
+ circle.setAttribute('style', 'fill: none; stroke: blue; stroke-width: 1px;');
1456
+ this.svgElement.appendChild(circle);
1457
+ }
1458
+ clearCircle() {
1459
+ if (!this.drawRectDebug) {
1460
+ return;
1461
+ }
1462
+ Array.from(this.svgElement.getElementsByTagName('circle'))?.forEach(item => item.remove());
1463
+ }
1464
+ addRect(name, rect) {
1465
+ if (!this.drawRectDebug) {
1466
+ return;
1467
+ }
1468
+ const rectElement = document.createElementNS("http://www.w3.org/2000/svg", "rect");
1469
+ const textElement = document.createElementNS("http://www.w3.org/2000/svg", "text");
1470
+ rectElement.setAttribute('name', name);
1471
+ rectElement.setAttribute('x', `${rect.x}`);
1472
+ rectElement.setAttribute('y', `${rect.y}`);
1473
+ rectElement.setAttribute('width', `${rect.width}`);
1474
+ rectElement.setAttribute('height', `${rect.height}`);
1475
+ rectElement.setAttribute('style', 'fill: blue; stroke: blue;');
1476
+ this.svgElement.appendChild(rectElement);
1477
+ textElement.setAttribute('x', `${rect.x}`);
1478
+ textElement.setAttribute('y', `${rect.y}`);
1479
+ textElement.textContent = rect.id ?? '';
1480
+ this.svgElement.appendChild(textElement);
1481
+ }
1482
+ clearRect() {
1483
+ if (!this.drawRectDebug) {
1484
+ return;
1485
+ }
1486
+ Array.from(this.svgElement.getElementsByTagName('rect'))?.forEach(item => item.remove());
1487
+ Array.from(this.svgElement.getElementsByTagName('text'))?.forEach(item => item.remove());
1488
+ }
1489
+ buildPathAndDrawSeparatedPoints(data, points, mode) {
1490
+ let dPath = {};
1491
+ const styleConfig = {
1492
+ curve: data.lineStyle?.curve ?? 10,
1493
+ distancePoint: data.lineStyle?.distancePoint ?? 10
1494
+ };
1495
+ let { curve, distancePoint } = styleConfig;
1496
+ mode = mode || "quart-in";
1497
+ if (mode !== 'quart-in') {
1498
+ curve = curve ?? 10;
1499
+ distancePoint = distancePoint ?? 10;
1500
+ }
1501
+ if (mode === 'quart-in' || points.start.x === points.end.x || points.start.y === points.end.y) {
1502
+ this.drawBalancedCurve(dPath, points);
1503
+ if (mode === 'quart-in') {
1504
+ dPath = {};
1505
+ this.drawBendBothEndsCurve(dPath, points);
1506
+ }
1507
+ return this.builDAttributeToString(dPath);
1508
+ }
1509
+ const result = this.calculatorDistancePointAndCurve(points, distancePoint, curve, mode);
1510
+ distancePoint = result.distancePoint;
1511
+ curve = result.curve;
1512
+ this.buildDAttributeByPointEndModeHorizontal(dPath, points, distancePoint || 0, curve || 0, mode);
1513
+ this.buildDAttributeByPointEndModeVertical(dPath, points, distancePoint || 0, curve || 0, mode);
1514
+ return this.builDAttributeToString(dPath);
1515
+ }
1516
+ ngOnDestroy() {
1517
+ this.onDestroy.next();
1518
+ this.onDestroy.complete();
1519
+ }
1520
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: LibsUiComponentsDrawLineDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
1521
+ 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 });
1522
+ }
1523
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: LibsUiComponentsDrawLineDirective, decorators: [{
1524
+ type: Directive,
1525
+ args: [{
1526
+ // eslint-disable-next-line @angular-eslint/directive-selector
1527
+ selector: "[LibsUiComponentsDrawLineDirective]",
1528
+ standalone: true,
1529
+ }]
1530
+ }], propDecorators: { viewBoxConfig: [{
1531
+ type: Input
1532
+ }], svgElement: [{
1533
+ type: Input
1534
+ }], drawRectDebug: [{
1535
+ type: Input
1536
+ }], outDrawLineFunctionControl: [{
1537
+ type: Output
1538
+ }], outConnected: [{
1539
+ type: Output
1540
+ }] } });
1541
+
1542
+ /**
1543
+ * Generated bundle index. Do not edit.
1544
+ */
1545
+
1546
+ export { LibsUiComponentsDrawLineDirective };
1547
+ //# sourceMappingURL=libs-ui-components-draw-line.mjs.map