@plait/draw 0.50.1 → 0.51.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/README.md +13 -1
  2. package/constants/line.d.ts +1 -0
  3. package/engines/basic-shapes/ellipse.d.ts +1 -10
  4. package/engines/flowchart/terminal.d.ts +1 -0
  5. package/esm2022/constants/line.mjs +2 -1
  6. package/esm2022/engines/basic-shapes/comment.mjs +4 -5
  7. package/esm2022/engines/basic-shapes/ellipse.mjs +5 -29
  8. package/esm2022/engines/basic-shapes/parallelogram.mjs +3 -2
  9. package/esm2022/engines/basic-shapes/pentagon.mjs +3 -3
  10. package/esm2022/engines/basic-shapes/polygon.mjs +20 -4
  11. package/esm2022/engines/basic-shapes/process-arrow.mjs +3 -3
  12. package/esm2022/engines/basic-shapes/rectangle.mjs +4 -4
  13. package/esm2022/engines/basic-shapes/round-comment.mjs +4 -5
  14. package/esm2022/engines/basic-shapes/round-rectangle.mjs +3 -3
  15. package/esm2022/engines/basic-shapes/star.mjs +3 -3
  16. package/esm2022/engines/basic-shapes/trapezoid.mjs +3 -2
  17. package/esm2022/engines/basic-shapes/triangle.mjs +5 -4
  18. package/esm2022/engines/flowchart/delay.mjs +6 -6
  19. package/esm2022/engines/flowchart/manual-input.mjs +5 -4
  20. package/esm2022/engines/flowchart/manual-loop.mjs +3 -2
  21. package/esm2022/engines/flowchart/merge.mjs +4 -4
  22. package/esm2022/engines/flowchart/stored-data.mjs +16 -10
  23. package/esm2022/engines/flowchart/terminal.mjs +37 -27
  24. package/esm2022/generators/geometry-shape.generator.mjs +3 -3
  25. package/esm2022/generators/line-active.generator.mjs +52 -68
  26. package/esm2022/generators/line.generator.mjs +2 -2
  27. package/esm2022/geometry.component.mjs +4 -4
  28. package/esm2022/interfaces/geometry.mjs +1 -1
  29. package/esm2022/interfaces/line.mjs +2 -2
  30. package/esm2022/line.component.mjs +39 -9
  31. package/esm2022/plugins/with-draw-fragment.mjs +3 -3
  32. package/esm2022/plugins/with-draw-hotkey.mjs +6 -6
  33. package/esm2022/plugins/with-draw-resize.mjs +149 -0
  34. package/esm2022/plugins/with-draw.mjs +14 -8
  35. package/esm2022/plugins/with-geometry-create.mjs +10 -10
  36. package/esm2022/plugins/with-geometry-resize.mjs +27 -74
  37. package/esm2022/plugins/with-line-auto-complete.mjs +17 -5
  38. package/esm2022/plugins/with-line-bound-reaction.mjs +6 -5
  39. package/esm2022/plugins/with-line-create.mjs +2 -2
  40. package/esm2022/plugins/with-line-resize.mjs +105 -19
  41. package/esm2022/plugins/with-line-text-move.mjs +5 -4
  42. package/esm2022/plugins/with-line-text.mjs +7 -5
  43. package/esm2022/transforms/geometry.mjs +4 -4
  44. package/esm2022/transforms/line.mjs +6 -8
  45. package/esm2022/utils/clipboard.mjs +2 -2
  46. package/esm2022/utils/geometry.mjs +16 -33
  47. package/esm2022/utils/hit.mjs +18 -10
  48. package/esm2022/utils/index.mjs +2 -2
  49. package/esm2022/utils/line/elbow.mjs +101 -0
  50. package/esm2022/utils/line/index.mjs +6 -0
  51. package/esm2022/utils/line/line-arrow.mjs +123 -0
  52. package/esm2022/utils/line/line-basic.mjs +258 -0
  53. package/esm2022/utils/line/line-common.mjs +111 -0
  54. package/esm2022/utils/line/line-resize.mjs +313 -0
  55. package/esm2022/utils/polygon.mjs +30 -0
  56. package/esm2022/utils/position/geometry.mjs +5 -6
  57. package/esm2022/utils/position/line.mjs +38 -15
  58. package/esm2022/utils/resize-align-reaction.mjs +316 -0
  59. package/esm2022/utils/resize-align.mjs +37 -0
  60. package/fesm2022/plait-draw.mjs +2108 -1143
  61. package/fesm2022/plait-draw.mjs.map +1 -1
  62. package/generators/line-active.generator.d.ts +4 -2
  63. package/interfaces/geometry.d.ts +1 -0
  64. package/interfaces/line.d.ts +4 -0
  65. package/package.json +1 -1
  66. package/plugins/with-draw-resize.d.ts +13 -0
  67. package/utils/geometry.d.ts +1 -3
  68. package/utils/hit.d.ts +3 -1
  69. package/utils/index.d.ts +1 -1
  70. package/utils/line/elbow.d.ts +19 -0
  71. package/utils/line/index.d.ts +5 -0
  72. package/utils/{line-arrow.d.ts → line/line-arrow.d.ts} +1 -1
  73. package/utils/line/line-basic.d.ts +13 -0
  74. package/utils/line/line-common.d.ts +35 -0
  75. package/utils/line/line-resize.d.ts +23 -0
  76. package/utils/polygon.d.ts +4 -0
  77. package/utils/position/geometry.d.ts +2 -3
  78. package/utils/position/line.d.ts +4 -2
  79. package/utils/resize-align-reaction.d.ts +42 -0
  80. package/utils/resize-align.d.ts +8 -0
  81. package/esm2022/utils/line-arrow.mjs +0 -123
  82. package/esm2022/utils/line.mjs +0 -392
  83. package/utils/line.d.ts +0 -25
@@ -1,5 +1,5 @@
1
- import { ACTIVE_STROKE_WIDTH, ThemeColorMode, PlaitBoard, setStrokeLinecap, isPointInPolygon, getNearestPointBetweenPointAndSegments, RectangleClient, isPointInEllipse, drawRectangle, drawRoundRectangle, isPointInRoundRectangle, idCreator, createG, SELECTION_BORDER_COLOR, SELECTION_FILL_COLOR, drawCircle, distanceBetweenPointAndSegment, Transforms, clearSelectedElement, addSelectedElement, BoardTransforms, PlaitPointerType, arrowPoints, createPath, distanceBetweenPointAndPoint, drawLinearPath, rotate, depthFirstRecursion, getIsRecursionFunc, getElementById, Direction, catmullRomFitting, distanceBetweenPointAndSegments, createMask, createRect, findElements, Point, getSelectedElements, isPolylineHitRectangle, Path, PlaitNode, toViewBoxPoint, toHostPoint, isSelectionMoving, RgbaToHEX, PlaitElement, preventTouchMove, createClipboardContext, WritableClipboardType, addClipboardContext, getHitElementByPoint, CursorClass, temporaryDisableSelection, PRESS_AND_MOVE_BUFFER } from '@plait/core';
2
- import { getRectangleByPoints, getMemorizedLatest, memorizeLatest, RESIZE_HANDLE_DIAMETER, getExtendPoint, getFactorByPoints, Generator, getRectangleResizeHandleRefs, getDirectionByVector, getOppositeDirection, getDirectionFactor, getPointByVector, reduceRouteMargin, getNextPoint, generateElbowLineRoute, getPoints, removeDuplicatePoints, getPointOnPolyline, getDirectionByPointOfRectangle, rotateVectorAnti90, TRANSPARENT, normalizeShapePoints, getFirstTextEditor, PRIMARY_COLOR, CommonPluginElement, ActiveGenerator, WithTextPluginKey, isVirtualKey, isDelete, isSpaceHotkey, isDndMode, isDrawingMode, getElementsText, acceptImageTypes, getElementOfFocusedImage, buildImage, ResizeHandle, getFirstTextManage, withResize, isResizingByCondition, getRatioByPoint, ImageGenerator } from '@plait/common';
1
+ import { ACTIVE_STROKE_WIDTH, ThemeColorMode, Point, RectangleClient, getElementById, createG, arrowPoints, createPath, distanceBetweenPointAndPoint, drawLinearPath, rotate, depthFirstRecursion, getIsRecursionFunc, idCreator, catmullRomFitting, PlaitBoard, findElements, createMask, createRect, getSelectedElements, distanceBetweenPointAndSegments, isPolylineHitRectangle, setStrokeLinecap, isPointInPolygon, getNearestPointBetweenPointAndSegments, isPointInEllipse, getEllipseTangentSlope, getVectorFromPointAndSlope, drawRectangle, drawRoundRectangle, isPointInRoundRectangle, SELECTION_BORDER_COLOR, SELECTION_FILL_COLOR, drawCircle, Transforms, clearSelectedElement, addSelectedElement, BoardTransforms, PlaitPointerType, Direction, Path, PlaitNode, toViewBoxPoint, toHostPoint, isSelectionMoving, RgbaToHEX, PlaitElement, preventTouchMove, createClipboardContext, WritableClipboardType, addClipboardContext, getRectangleByElements, getHitElementByPoint, CursorClass, temporaryDisableSelection, PRESS_AND_MOVE_BUFFER } from '@plait/core';
2
+ import { removeDuplicatePoints, generateElbowLineRoute, simplifyOrthogonalPoints, isSourceAndTargetIntersect, getPoints, getPointByVector, getExtendPoint, getUnitVectorByPointAndPoint, Generator, getRectangleResizeHandleRefs, RESIZE_HANDLE_DIAMETER, getMemorizedLatest, memorizeLatest, getPointOnPolyline, TRANSPARENT, getCrossingPointsBetweenPointAndSegment, isPointOnSegment, getDirectionByVector, getOppositeDirection, getDirectionFactor, getDirectionByPointOfRectangle, rotateVectorAnti90, getSourceAndTargetOuterRectangle, getNextPoint, normalizeShapePoints, getFirstTextEditor, PRIMARY_COLOR, CommonPluginElement, ActiveGenerator, WithTextPluginKey, drawPrimaryHandle, drawFillPrimaryHandle, isVirtualKey, isDelete, isSpaceHotkey, isDndMode, isDrawingMode, getElementsText, acceptImageTypes, getElementOfFocusedImage, buildImage, getDirectionFactorByVectorComponent, isCornerHandle, getFirstTextManage, withResize, drawHandle, getIndexByResizeHandle, getSymmetricHandleIndex, getResizeHandlePointByIndex, isResizingByCondition, getRatioByPoint, ImageGenerator, ResizeHandle } from '@plait/common';
3
3
  import { Alignment, buildText, DEFAULT_FONT_SIZE, getTextSize, AlignEditor, TextManage } from '@plait/text';
4
4
  import { pointsOnBezierCurves } from 'points-on-curve';
5
5
  import * as i0 from '@angular/core';
@@ -155,30 +155,1071 @@ const DrawThemeColors = {
155
155
  }
156
156
  };
157
157
 
158
- const getStrokeWidthByElement = (element) => {
159
- if (PlaitDrawElement.isText(element)) {
160
- return 0;
158
+ const DefaultLineStyle = {
159
+ strokeWidth: 2,
160
+ strokeColor: '#000'
161
+ };
162
+ const LINE_TEXT_SPACE = 4;
163
+ const LINE_AUTO_COMPLETE_DIAMETER = 6;
164
+ const LINE_AUTO_COMPLETE_OPACITY = 0.6;
165
+ const LINE_AUTO_COMPLETE_HOVERED_OPACITY = 0.8;
166
+ const LINE_AUTO_COMPLETE_HOVERED_DIAMETER = 10;
167
+ const LINE_ALIGN_TOLERANCE = 3;
168
+
169
+ const alignPoints = (basePoint, movingPoint) => {
170
+ const newPoint = [...movingPoint];
171
+ if (Point.isVertical(newPoint, basePoint, LINE_ALIGN_TOLERANCE)) {
172
+ newPoint[0] = basePoint[0];
173
+ }
174
+ if (Point.isHorizontal(newPoint, basePoint, LINE_ALIGN_TOLERANCE)) {
175
+ newPoint[1] = basePoint[1];
176
+ }
177
+ return newPoint;
178
+ };
179
+ function getResizedPreviousAndNextPoint(nextRenderPoints, sourcePoint, targetPoint, handleIndex) {
180
+ const referencePoint = {
181
+ previous: null,
182
+ next: null
183
+ };
184
+ const startPoint = nextRenderPoints[handleIndex];
185
+ const endPoint = nextRenderPoints[handleIndex + 1];
186
+ const isHorizontal = Point.isHorizontal(startPoint, endPoint);
187
+ const isVertical = Point.isVertical(startPoint, endPoint);
188
+ const previousPoint = nextRenderPoints[handleIndex - 1] ?? nextRenderPoints[0];
189
+ const beforePreviousPoint = nextRenderPoints[handleIndex - 2] ?? sourcePoint;
190
+ if ((isHorizontal && Point.isHorizontal(beforePreviousPoint, previousPoint)) ||
191
+ (isVertical && Point.isVertical(beforePreviousPoint, previousPoint))) {
192
+ referencePoint.previous = previousPoint;
193
+ }
194
+ const nextPoint = nextRenderPoints[handleIndex + 2] ?? nextRenderPoints[nextRenderPoints.length - 1];
195
+ const afterNextPoint = nextRenderPoints[handleIndex + 3] ?? targetPoint;
196
+ if ((isHorizontal && Point.isHorizontal(nextPoint, afterNextPoint)) || (isVertical && Point.isVertical(nextPoint, afterNextPoint))) {
197
+ referencePoint.next = nextPoint;
198
+ }
199
+ return referencePoint;
200
+ }
201
+ function alignElbowSegment(startKeyPoint, endKeyPoint, resizeState, resizedPreviousAndNextPoint) {
202
+ let newStartPoint = startKeyPoint;
203
+ let newEndPoint = endKeyPoint;
204
+ if (Point.isHorizontal(startKeyPoint, endKeyPoint)) {
205
+ const offsetY = Point.getOffsetY(resizeState.startPoint, resizeState.endPoint);
206
+ let pointY = startKeyPoint[1] + offsetY;
207
+ if (resizedPreviousAndNextPoint.previous && Math.abs(resizedPreviousAndNextPoint.previous[1] - pointY) < LINE_ALIGN_TOLERANCE) {
208
+ pointY = resizedPreviousAndNextPoint.previous[1];
209
+ }
210
+ else if (resizedPreviousAndNextPoint.next && Math.abs(resizedPreviousAndNextPoint.next[1] - pointY) < LINE_ALIGN_TOLERANCE) {
211
+ pointY = resizedPreviousAndNextPoint.next[1];
212
+ }
213
+ newStartPoint = [startKeyPoint[0], pointY];
214
+ newEndPoint = [endKeyPoint[0], pointY];
215
+ }
216
+ if (Point.isVertical(startKeyPoint, endKeyPoint)) {
217
+ const offsetX = Point.getOffsetX(resizeState.startPoint, resizeState.endPoint);
218
+ let pointX = startKeyPoint[0] + offsetX;
219
+ if (resizedPreviousAndNextPoint.previous && Math.abs(resizedPreviousAndNextPoint.previous[0] - pointX) < LINE_ALIGN_TOLERANCE) {
220
+ pointX = resizedPreviousAndNextPoint.previous[0];
221
+ }
222
+ else if (resizedPreviousAndNextPoint.next && Math.abs(resizedPreviousAndNextPoint.next[0] - pointX) < LINE_ALIGN_TOLERANCE) {
223
+ pointX = resizedPreviousAndNextPoint.next[0];
224
+ }
225
+ newStartPoint = [pointX, startKeyPoint[1]];
226
+ newEndPoint = [pointX, endKeyPoint[1]];
227
+ }
228
+ return [newStartPoint, newEndPoint];
229
+ }
230
+ function getIndexAndDeleteCountByKeyPoint(board, element, dataPoints, nextRenderPoints, handleIndex) {
231
+ let index = null;
232
+ let deleteCount = null;
233
+ const startKeyPoint = nextRenderPoints[handleIndex];
234
+ const endKeyPoint = nextRenderPoints[handleIndex + 1];
235
+ if (!startKeyPoint || !endKeyPoint) {
236
+ return {
237
+ index,
238
+ deleteCount
239
+ };
240
+ }
241
+ const midDataPoints = dataPoints.slice(1, -1);
242
+ const startIndex = midDataPoints.findIndex(item => Point.isEquals(item, startKeyPoint));
243
+ const endIndex = midDataPoints.findIndex(item => Point.isEquals(item, endKeyPoint));
244
+ if (Math.max(startIndex, endIndex) > -1) {
245
+ if (startIndex > -1 && endIndex > -1) {
246
+ return {
247
+ index: startIndex,
248
+ deleteCount: 2
249
+ };
250
+ }
251
+ if (startIndex > -1 && endIndex === -1) {
252
+ const isReplace = startIndex < midDataPoints.length - 1 &&
253
+ Point.isAlign([midDataPoints[startIndex], midDataPoints[startIndex + 1], startKeyPoint, endKeyPoint]);
254
+ if (isReplace) {
255
+ return {
256
+ index: startIndex,
257
+ deleteCount: 2
258
+ };
259
+ }
260
+ return {
261
+ index: startIndex,
262
+ deleteCount: 1
263
+ };
264
+ }
265
+ if (startIndex === -1 && endIndex > -1) {
266
+ const isReplace = endIndex > 0 && Point.isAlign([midDataPoints[endIndex], midDataPoints[endIndex - 1], startKeyPoint, endKeyPoint]);
267
+ if (isReplace) {
268
+ return {
269
+ index: endIndex - 1,
270
+ deleteCount: 2
271
+ };
272
+ }
273
+ return {
274
+ index: endIndex,
275
+ deleteCount: 1
276
+ };
277
+ }
278
+ }
279
+ else {
280
+ for (let i = 0; i < midDataPoints.length - 1; i++) {
281
+ const currentPoint = midDataPoints[i];
282
+ const nextPoint = midDataPoints[i + 1];
283
+ if (Point.isAlign([currentPoint, nextPoint, startKeyPoint, endKeyPoint])) {
284
+ index = i;
285
+ deleteCount = 2;
286
+ break;
287
+ }
288
+ if (Point.isAlign([currentPoint, nextPoint, startKeyPoint])) {
289
+ index = Math.min(i + 1, midDataPoints.length - 1);
290
+ deleteCount = 1;
291
+ break;
292
+ }
293
+ if (Point.isAlign([currentPoint, nextPoint, endKeyPoint])) {
294
+ index = Math.max(i - 1, 0);
295
+ deleteCount = 1;
296
+ break;
297
+ }
298
+ }
299
+ }
300
+ if (index === null) {
301
+ deleteCount = 0;
302
+ if (midDataPoints.length > 0) {
303
+ const handleRefPair = getLineHandleRefPair(board, element);
304
+ const params = getElbowLineRouteOptions(board, element, handleRefPair);
305
+ const keyPoints = removeDuplicatePoints(generateElbowLineRoute(params));
306
+ const nextKeyPoints = simplifyOrthogonalPoints(keyPoints.slice(1, keyPoints.length - 1));
307
+ const nextDataPoints = [nextRenderPoints[0], ...midDataPoints, nextRenderPoints[nextRenderPoints.length - 1]];
308
+ const mirrorDataPoints = getMirrorDataPoints(board, nextDataPoints, nextKeyPoints, params);
309
+ for (let i = handleIndex - 1; i >= 0; i--) {
310
+ const previousIndex = mirrorDataPoints.slice(1, -1).findIndex(item => Point.isEquals(item, nextRenderPoints[i]));
311
+ if (previousIndex > -1) {
312
+ index = previousIndex + 1;
313
+ break;
314
+ }
315
+ }
316
+ if (index === null) {
317
+ index = 0;
318
+ // When renderPoints is a straight line and dataPoints are not on the line,
319
+ // the default 'deleteCount' is set to midDataPoints.length.
320
+ if (Point.isAlign(nextRenderPoints)) {
321
+ deleteCount = midDataPoints.length;
322
+ }
323
+ }
324
+ }
325
+ else {
326
+ index = 0;
327
+ }
328
+ }
329
+ return {
330
+ index,
331
+ deleteCount
332
+ };
333
+ }
334
+ function getMirrorDataPoints(board, nextDataPoints, nextKeyPoints, params) {
335
+ for (let index = 1; index < nextDataPoints.length - 2; index++) {
336
+ adjustByCustomPointStartIndex(board, index, nextDataPoints, nextKeyPoints, params);
337
+ }
338
+ return nextDataPoints;
339
+ }
340
+ /**
341
+ * adjust based parallel segment
342
+ */
343
+ const adjustByCustomPointStartIndex = (board, customPointStartIndex, nextDataPoints, nextKeyPoints, params) => {
344
+ const beforePoint = nextDataPoints[customPointStartIndex - 1];
345
+ const startPoint = nextDataPoints[customPointStartIndex];
346
+ const endPoint = nextDataPoints[customPointStartIndex + 1];
347
+ const afterPoint = nextDataPoints[customPointStartIndex + 2];
348
+ const beforeSegment = [beforePoint, startPoint];
349
+ const afterSegment = [endPoint, afterPoint];
350
+ const isStraightWithBefore = Point.isAlign(beforeSegment);
351
+ const isStraightWithAfter = Point.isAlign(afterSegment);
352
+ let isAdjustStart = false;
353
+ let isAdjustEnd = false;
354
+ if (!isStraightWithBefore || !isStraightWithAfter) {
355
+ const midKeyPointsWithBefore = getMidKeyPoints(nextKeyPoints, beforeSegment[0], beforeSegment[1]);
356
+ const midKeyPointsWithAfter = getMidKeyPoints(nextKeyPoints, afterSegment[0], afterSegment[1]);
357
+ const hasMidKeyPoints = midKeyPointsWithBefore.length > 0 && midKeyPointsWithAfter.length > 0;
358
+ isAdjustStart = !isStraightWithBefore && !hasMidKeyPoints;
359
+ isAdjustEnd = !isStraightWithAfter && !hasMidKeyPoints;
360
+ }
361
+ if (isAdjustStart || isAdjustEnd) {
362
+ const parallelSegment = [startPoint, endPoint];
363
+ const parallelSegments = findOrthogonalParallelSegments(parallelSegment, nextKeyPoints);
364
+ const mirrorSegments = findMirrorSegments(board, parallelSegment, parallelSegments, params.sourceRectangle, params.targetRectangle);
365
+ if (mirrorSegments.length === 1) {
366
+ const mirrorSegment = mirrorSegments[0];
367
+ if (isAdjustStart) {
368
+ nextDataPoints.splice(customPointStartIndex, 1, mirrorSegment[0]);
369
+ }
370
+ if (isAdjustEnd) {
371
+ nextDataPoints.splice(customPointStartIndex + 1, 1, mirrorSegment[1]);
372
+ }
373
+ }
374
+ else {
375
+ const isHorizontal = Point.isHorizontal(startPoint, endPoint);
376
+ const adjustIndex = isHorizontal ? 0 : 1;
377
+ if (isAdjustStart) {
378
+ const newStartPoint = [startPoint[0], startPoint[1]];
379
+ newStartPoint[adjustIndex] = beforePoint[adjustIndex];
380
+ nextDataPoints.splice(customPointStartIndex, 1, newStartPoint);
381
+ }
382
+ if (isAdjustEnd) {
383
+ const newEndPoint = [endPoint[0], endPoint[1]];
384
+ newEndPoint[adjustIndex] = afterPoint[adjustIndex];
385
+ nextDataPoints.splice(customPointStartIndex + 1, 1, newEndPoint);
386
+ }
387
+ }
388
+ }
389
+ };
390
+ function isUpdatedHandleIndex(board, element, dataPoints, nextRenderPoints, handleIndex) {
391
+ const { deleteCount } = getIndexAndDeleteCountByKeyPoint(board, element, dataPoints, nextRenderPoints, handleIndex);
392
+ if (deleteCount !== null && deleteCount > 1) {
393
+ return true;
394
+ }
395
+ return false;
396
+ }
397
+ function getMidKeyPoints(simplifiedNextKeyPoints, startPoint, endPoint) {
398
+ let midElbowPoints = [];
399
+ let startPointIndex = -1;
400
+ let endPointIndex = -1;
401
+ for (let i = 0; i < simplifiedNextKeyPoints.length; i++) {
402
+ if (Point.isAlign([simplifiedNextKeyPoints[i], startPoint])) {
403
+ startPointIndex = i;
404
+ }
405
+ if (startPointIndex > -1 && Point.isAlign([simplifiedNextKeyPoints[i], endPoint])) {
406
+ endPointIndex = i;
407
+ break;
408
+ }
409
+ }
410
+ if (startPointIndex > -1 && endPointIndex > -1) {
411
+ midElbowPoints = simplifiedNextKeyPoints.slice(startPointIndex, endPointIndex + 1);
412
+ }
413
+ return midElbowPoints;
414
+ }
415
+ function findOrthogonalParallelSegments(segment, keyPoints) {
416
+ const isHorizontalSegment = Point.isHorizontal(segment[0], segment[1]);
417
+ const parallelSegments = [];
418
+ for (let i = 0; i < keyPoints.length - 1; i++) {
419
+ const current = keyPoints[i];
420
+ const next = keyPoints[i + 1];
421
+ const isHorizontal = Point.isHorizontal(current, next, 0.1);
422
+ if (isHorizontalSegment && isHorizontal) {
423
+ parallelSegments.push([current, next]);
424
+ }
425
+ if (!isHorizontalSegment && !isHorizontal) {
426
+ parallelSegments.push([current, next]);
427
+ }
428
+ }
429
+ return parallelSegments;
430
+ }
431
+ function findMirrorSegments(board, segment, parallelSegments, sourceRectangle, targetRectangle) {
432
+ const mirrorSegments = [];
433
+ for (let index = 0; index < parallelSegments.length; index++) {
434
+ const parallelPath = parallelSegments[index];
435
+ const startPoint = [segment[0][0], segment[0][1]];
436
+ const endPoint = [segment[1][0], segment[1][1]];
437
+ const isHorizontal = Point.isHorizontal(startPoint, endPoint);
438
+ const adjustDataIndex = isHorizontal ? 0 : 1;
439
+ startPoint[adjustDataIndex] = parallelPath[0][adjustDataIndex];
440
+ endPoint[adjustDataIndex] = parallelPath[1][adjustDataIndex];
441
+ const fakeRectangle = RectangleClient.getRectangleByPoints([startPoint, endPoint, ...parallelPath]);
442
+ const isValid = !RectangleClient.isHit(fakeRectangle, sourceRectangle) && !RectangleClient.isHit(fakeRectangle, targetRectangle);
443
+ if (isValid) {
444
+ mirrorSegments.push([startPoint, endPoint]);
445
+ // const fakeRectangleG = PlaitBoard.getRoughSVG(board).rectangle(
446
+ // fakeRectangle.x,
447
+ // fakeRectangle.y,
448
+ // fakeRectangle.width,
449
+ // fakeRectangle.height,
450
+ // { stroke: 'blue' }
451
+ // );
452
+ // PlaitBoard.getElementActiveHost(board).append(fakeRectangleG);
453
+ }
454
+ }
455
+ return mirrorSegments;
456
+ }
457
+ const hasIllegalElbowPoint = (midDataPoints) => {
458
+ if (midDataPoints.length === 1) {
459
+ return true;
460
+ }
461
+ return midDataPoints.some((item, index) => {
462
+ const beforePoint = midDataPoints[index - 1];
463
+ const afterPoint = midDataPoints[index + 1];
464
+ const beforeSegment = beforePoint && [beforePoint, item];
465
+ const afterSegment = afterPoint && [item, afterPoint];
466
+ const isStraightWithBefore = beforeSegment && Point.isAlign(beforeSegment);
467
+ const isStraightWithAfter = afterSegment && Point.isAlign(afterSegment);
468
+ if (index === 0) {
469
+ return !isStraightWithAfter;
470
+ }
471
+ if (index === midDataPoints.length - 1) {
472
+ return !isStraightWithBefore;
473
+ }
474
+ return !isStraightWithBefore && !isStraightWithAfter;
475
+ });
476
+ };
477
+
478
+ const getElbowPoints = (board, element) => {
479
+ const handleRefPair = getLineHandleRefPair(board, element);
480
+ const params = getElbowLineRouteOptions(board, element, handleRefPair);
481
+ // console.log(params, 'params');
482
+ const isIntersect = isSourceAndTargetIntersect(params);
483
+ if (isIntersect) {
484
+ return simplifyOrthogonalPoints(getPoints(handleRefPair.source.point, handleRefPair.source.direction, handleRefPair.target.point, handleRefPair.target.direction, 0));
485
+ }
486
+ const keyPoints = removeDuplicatePoints(generateElbowLineRoute(params));
487
+ const nextKeyPoints = keyPoints.slice(1, keyPoints.length - 1);
488
+ if (element.points.length === 2) {
489
+ return simplifyOrthogonalPoints(keyPoints);
490
+ }
491
+ else {
492
+ const simplifiedNextKeyPoints = simplifyOrthogonalPoints(nextKeyPoints);
493
+ const dataPoints = removeDuplicatePoints(PlaitLine.getPoints(board, element));
494
+ const midDataPoints = dataPoints.slice(1, -1);
495
+ if (hasIllegalElbowPoint(midDataPoints)) {
496
+ return simplifyOrthogonalPoints(keyPoints);
497
+ }
498
+ const nextDataPoints = [simplifiedNextKeyPoints[0], ...midDataPoints, simplifiedNextKeyPoints[simplifiedNextKeyPoints.length - 1]];
499
+ const mirrorDataPoints = getMirrorDataPoints(board, nextDataPoints, simplifiedNextKeyPoints, params);
500
+ // console.log(mirrorDataPoints, 'mirrorDataPoints');
501
+ const renderPoints = [keyPoints[0]];
502
+ for (let index = 0; index < mirrorDataPoints.length - 1; index++) {
503
+ let currentPoint = mirrorDataPoints[index];
504
+ let nextPoint = mirrorDataPoints[index + 1];
505
+ const isStraight = Point.isAlign([currentPoint, nextPoint]);
506
+ if (!isStraight) {
507
+ const midKeyPoints = getMidKeyPoints(simplifiedNextKeyPoints, currentPoint, nextPoint);
508
+ if (midKeyPoints.length) {
509
+ renderPoints.push(currentPoint);
510
+ renderPoints.push(...midKeyPoints);
511
+ }
512
+ else {
513
+ renderPoints.push(currentPoint);
514
+ console.log('unknown data points');
515
+ }
516
+ }
517
+ else {
518
+ renderPoints.push(currentPoint);
519
+ }
520
+ }
521
+ renderPoints.push(keyPoints[keyPoints.length - 2], keyPoints[keyPoints.length - 1]);
522
+ // Remove the middle point to avoid the situation where the starting and ending positions are drawn back, such as when sourcePoint is between nextSourcePoint and the first key point.
523
+ // Issue
524
+ // keyPoint2
525
+ // |
526
+ // |
527
+ // nextPoint---sourcePoint---keyPoint1
528
+ // The correct rendering should be (nextPoint should be filtered out):
529
+ // keyPoint2
530
+ // |
531
+ // |
532
+ // sourcePoint---keyPoint1
533
+ const ret = simplifyOrthogonalPoints(renderPoints);
534
+ return ret;
535
+ }
536
+ };
537
+ const getNextSourceAndTargetPoints = (board, element) => {
538
+ const options = getElbowLineRouteOptions(board, element);
539
+ return [options.nextSourcePoint, options.nextTargetPoint];
540
+ };
541
+ const getSourceAndTargetRectangle = (board, element, handleRefPair) => {
542
+ let sourceElement = element.source.boundId ? getElementById(board, element.source.boundId) : undefined;
543
+ let targetElement = element.target.boundId ? getElementById(board, element.target.boundId) : undefined;
544
+ if (!sourceElement) {
545
+ const source = handleRefPair.source;
546
+ sourceElement = createFakeElement(source.point, source.vector);
547
+ }
548
+ if (!targetElement) {
549
+ const target = handleRefPair.target;
550
+ targetElement = createFakeElement(target.point, target.vector);
551
+ }
552
+ const sourceRectangle = RectangleClient.inflate(RectangleClient.getRectangleByPoints(sourceElement.points), getStrokeWidthByElement(sourceElement) * 2);
553
+ const targetRectangle = RectangleClient.inflate(RectangleClient.getRectangleByPoints(targetElement.points), getStrokeWidthByElement(targetElement) * 2);
554
+ return {
555
+ sourceRectangle,
556
+ targetRectangle
557
+ };
558
+ };
559
+ const createFakeElement = (startPoint, vector) => {
560
+ const point = getPointByVector(startPoint, vector, -25);
561
+ const points = RectangleClient.getPoints(RectangleClient.getRectangleByCenterPoint(point, 50, 50));
562
+ return createGeometryElement(BasicShapes.rectangle, points, '');
563
+ };
564
+ function getNextRenderPoints(board, element, renderPoints) {
565
+ let newRenderKeyPoints = renderPoints ?? getElbowPoints(board, element);
566
+ const [nextSourcePoint, nextTargetPoint] = getNextSourceAndTargetPoints(board, element);
567
+ newRenderKeyPoints.splice(0, 1, nextSourcePoint);
568
+ newRenderKeyPoints.splice(-1, 1, nextTargetPoint);
569
+ return removeDuplicatePoints(newRenderKeyPoints);
570
+ }
571
+
572
+ const ARROW_LENGTH = 20;
573
+ const drawLineArrow = (element, points, options) => {
574
+ const arrowG = createG();
575
+ if (PlaitLine.isSourceMark(element, LineMarkerType.none) && PlaitLine.isTargetMark(element, LineMarkerType.none)) {
576
+ return null;
577
+ }
578
+ const strokeWidth = getStrokeWidthByElement(element);
579
+ const offset = (strokeWidth * strokeWidth) / 3;
580
+ if (points.length === 1) {
581
+ points = [points[0], [points[0][0] + 0.1, points[0][1]]];
582
+ }
583
+ if (!PlaitLine.isSourceMark(element, LineMarkerType.none)) {
584
+ const source = getExtendPoint(points[0], points[1], ARROW_LENGTH + offset);
585
+ const sourceArrow = getArrow(element, { marker: element.source.marker, source, target: points[0], isSource: true }, options);
586
+ sourceArrow && arrowG.appendChild(sourceArrow);
587
+ }
588
+ if (!PlaitLine.isTargetMark(element, LineMarkerType.none)) {
589
+ const source = getExtendPoint(points[points.length - 1], points[points.length - 2], ARROW_LENGTH + offset);
590
+ const arrow = getArrow(element, { marker: element.target.marker, source, target: points[points.length - 1], isSource: false }, options);
591
+ arrow && arrowG.appendChild(arrow);
592
+ }
593
+ return arrowG;
594
+ };
595
+ const getArrow = (element, arrowOptions, options) => {
596
+ const { marker, target, source, isSource } = arrowOptions;
597
+ let targetArrow;
598
+ switch (marker) {
599
+ case LineMarkerType.openTriangle: {
600
+ targetArrow = drawOpenTriangle(element, source, target, options);
601
+ break;
602
+ }
603
+ case LineMarkerType.solidTriangle: {
604
+ targetArrow = drawSolidTriangle(source, target, options);
605
+ break;
606
+ }
607
+ case LineMarkerType.arrow: {
608
+ targetArrow = drawArrow(element, source, target, options);
609
+ break;
610
+ }
611
+ case LineMarkerType.sharpArrow: {
612
+ targetArrow = drawSharpArrow(source, target, options);
613
+ break;
614
+ }
615
+ case LineMarkerType.oneSideUp: {
616
+ targetArrow = drawOneSideArrow(source, target, isSource ? 'down' : 'up', options);
617
+ break;
618
+ }
619
+ case LineMarkerType.oneSideDown: {
620
+ targetArrow = drawOneSideArrow(source, target, isSource ? 'up' : 'down', options);
621
+ break;
622
+ }
623
+ case LineMarkerType.hollowTriangle: {
624
+ targetArrow = drawHollowTriangleArrow(source, target, options);
625
+ break;
626
+ }
627
+ case LineMarkerType.singleSlash: {
628
+ targetArrow = drawSingleSlash(source, target, isSource, options);
629
+ break;
630
+ }
631
+ }
632
+ return targetArrow;
633
+ };
634
+ const drawSharpArrow = (source, target, options) => {
635
+ const startPoint = target;
636
+ const { pointLeft, pointRight } = arrowPoints(source, target, 20);
637
+ const g = createG();
638
+ const path = createPath();
639
+ let polylinePath = `M${pointRight[0]},${pointRight[1]}A25,25,20,0,1,${pointLeft[0]},${pointLeft[1]}L${startPoint[0]},${startPoint[1]}Z`;
640
+ path.setAttribute('d', polylinePath);
641
+ path.setAttribute('stroke', `${options?.stroke}`);
642
+ path.setAttribute('stroke-width', `${options?.strokeWidth}`);
643
+ path.setAttribute('fill', `${options?.stroke}`);
644
+ g.appendChild(path);
645
+ return g;
646
+ };
647
+ const drawArrow = (element, source, target, options) => {
648
+ const unitVector = getUnitVectorByPointAndPoint(source, target);
649
+ const strokeWidth = getStrokeWidthByElement(element);
650
+ const endPoint = [target[0] + (strokeWidth * unitVector[0]) / 2, target[1] + (strokeWidth * unitVector[1]) / 2];
651
+ const distance = distanceBetweenPointAndPoint(...source, ...endPoint);
652
+ const middlePoint = [
653
+ endPoint[0] - (((distance * 3) / 5 + strokeWidth) / 2) * unitVector[0],
654
+ endPoint[1] - (((distance * 3) / 5 + strokeWidth) / 2) * unitVector[1]
655
+ ];
656
+ const { pointLeft, pointRight } = arrowPoints(source, endPoint, 30);
657
+ const arrowG = drawLinearPath([pointLeft, endPoint, pointRight, middlePoint], { ...options, fill: options.stroke }, true);
658
+ const path = arrowG.querySelector('path');
659
+ path.setAttribute('stroke-linejoin', 'round');
660
+ return arrowG;
661
+ };
662
+ const drawSolidTriangle = (source, target, options) => {
663
+ const endPoint = target;
664
+ const { pointLeft, pointRight } = arrowPoints(source, endPoint, 30);
665
+ return drawLinearPath([pointLeft, endPoint, pointRight], { ...options, fill: options.stroke }, true);
666
+ };
667
+ const drawOpenTriangle = (element, source, target, options) => {
668
+ const unitVector = getUnitVectorByPointAndPoint(source, target);
669
+ const strokeWidth = getStrokeWidthByElement(element);
670
+ const endPoint = [target[0] + (strokeWidth * unitVector[0]) / 2, target[1] + (strokeWidth * unitVector[1]) / 2];
671
+ const { pointLeft, pointRight } = arrowPoints(source, endPoint, 40);
672
+ return drawLinearPath([pointLeft, endPoint, pointRight], options);
673
+ };
674
+ const drawOneSideArrow = (source, target, side, options) => {
675
+ const { pointLeft, pointRight } = arrowPoints(source, target, 40);
676
+ return drawLinearPath([side === 'up' ? pointRight : pointLeft, target], options);
677
+ };
678
+ const drawSingleSlash = (source, target, isSource, options) => {
679
+ const length = distanceBetweenPointAndPoint(...source, ...target);
680
+ const middlePoint = getExtendPoint(target, source, length / 2);
681
+ const angle = isSource ? 120 : 60;
682
+ const start = rotate(...source, ...middlePoint, (angle * Math.PI) / 180);
683
+ const end = rotate(...target, ...middlePoint, (angle * Math.PI) / 180);
684
+ return drawLinearPath([start, end], options);
685
+ };
686
+ const drawHollowTriangleArrow = (source, target, options) => {
687
+ const { pointLeft, pointRight } = arrowPoints(source, target, 30);
688
+ return drawLinearPath([pointLeft, pointRight, target], { ...options, fill: 'white' }, true);
689
+ };
690
+
691
+ const getShape = (value) => {
692
+ if (PlaitDrawElement.isImage(value)) {
693
+ return BasicShapes.rectangle;
694
+ }
695
+ return value.shape;
696
+ };
697
+
698
+ class LineShapeGenerator extends Generator {
699
+ canDraw(element, data) {
700
+ return true;
701
+ }
702
+ draw(element, data) {
703
+ let lineG;
704
+ lineG = drawLine(this.board, element);
705
+ return lineG;
706
+ }
707
+ }
708
+
709
+ const getHitRectangleResizeHandleRef = (board, rectangle, point) => {
710
+ const resizeHandleRefs = getRectangleResizeHandleRefs(rectangle, RESIZE_HANDLE_DIAMETER);
711
+ const result = resizeHandleRefs.find(resizeHandleRef => {
712
+ return RectangleClient.isHit(RectangleClient.getRectangleByPoints([point, point]), resizeHandleRef.rectangle);
713
+ });
714
+ return result;
715
+ };
716
+ const getHitOutlineGeometry = (board, point, offset = 0) => {
717
+ let geometry = null;
718
+ depthFirstRecursion(board, node => {
719
+ if (PlaitDrawElement.isGeometry(node) || PlaitDrawElement.isImage(node)) {
720
+ let client = RectangleClient.getRectangleByPoints(node.points);
721
+ client = RectangleClient.getOutlineRectangle(client, offset);
722
+ const shape = getShape(node);
723
+ const isHit = getEngine(shape).isHit(client, point);
724
+ if (isHit) {
725
+ geometry = node;
726
+ }
727
+ }
728
+ }, getIsRecursionFunc(board), true);
729
+ return geometry;
730
+ };
731
+
732
+ const SHAPE_MAX_LENGTH = 6;
733
+ const memorizedShape = new WeakMap();
734
+ const getMemorizeKey = (element) => {
735
+ let key = '';
736
+ switch (true) {
737
+ case PlaitDrawElement.isText(element): {
738
+ key = MemorizeKey.text;
739
+ break;
740
+ }
741
+ case PlaitDrawElement.isBasicShape(element): {
742
+ key = MemorizeKey.basicShape;
743
+ break;
744
+ }
745
+ case PlaitDrawElement.isFlowchart(element): {
746
+ key = MemorizeKey.flowchart;
747
+ break;
748
+ }
749
+ case PlaitDrawElement.isLine(element): {
750
+ key = MemorizeKey.line;
751
+ break;
752
+ }
753
+ }
754
+ return key;
755
+ };
756
+ const getLineMemorizedLatest = () => {
757
+ const properties = getMemorizedLatest(MemorizeKey.line);
758
+ delete properties?.text;
759
+ return { ...properties } || {};
760
+ };
761
+ const getMemorizedLatestByPointer = (pointer) => {
762
+ let memorizeKey = '';
763
+ if (PlaitDrawElement.isBasicShape({ shape: pointer })) {
764
+ memorizeKey = pointer === BasicShapes.text ? MemorizeKey.text : MemorizeKey.basicShape;
765
+ }
766
+ else {
767
+ memorizeKey = MemorizeKey.flowchart;
768
+ }
769
+ const properties = { ...getMemorizedLatest(memorizeKey) } || {};
770
+ const textProperties = { ...properties.text } || {};
771
+ delete properties.text;
772
+ return { textProperties, geometryProperties: properties };
773
+ };
774
+ const memorizeLatestText = (element, operations) => {
775
+ const memorizeKey = getMemorizeKey(element);
776
+ let textMemory = getMemorizedLatest(memorizeKey)?.text || {};
777
+ const setNodeOperation = operations.find(operation => operation.type === 'set_node');
778
+ if (setNodeOperation) {
779
+ const newProperties = setNodeOperation.newProperties;
780
+ textMemory = { ...textMemory, ...newProperties };
781
+ memorizeLatest(memorizeKey, 'text', textMemory);
782
+ }
783
+ };
784
+ const memorizeLatestShape = (board, shape) => {
785
+ const shapes = memorizedShape.has(board) ? memorizedShape.get(board) : [];
786
+ const shapeIndex = shapes.indexOf(shape);
787
+ if (shape === BasicShapes.text || shapeIndex === 0) {
788
+ return;
789
+ }
790
+ if (shapeIndex !== -1) {
791
+ shapes.splice(shapeIndex, 1);
792
+ }
793
+ else {
794
+ if (shapes.length === SHAPE_MAX_LENGTH) {
795
+ shapes.pop();
796
+ }
797
+ }
798
+ shapes.unshift(shape);
799
+ memorizedShape.set(board, shapes);
800
+ };
801
+ const getMemorizedLatestShape = (board) => {
802
+ return memorizedShape.get(board);
803
+ };
804
+
805
+ const createLineElement = (shape, points, source, target, texts, options) => {
806
+ return {
807
+ id: idCreator(),
808
+ type: 'line',
809
+ shape,
810
+ source,
811
+ texts: texts ? texts : [],
812
+ target,
813
+ opacity: 1,
814
+ points,
815
+ ...options
816
+ };
817
+ };
818
+ const getLinePoints = (board, element) => {
819
+ switch (element.shape) {
820
+ case LineShape.elbow: {
821
+ return getElbowPoints(board, element);
822
+ }
823
+ case LineShape.curve: {
824
+ return getCurvePoints(board, element);
825
+ }
826
+ default: {
827
+ const points = PlaitLine.getPoints(board, element);
828
+ const handleRefPair = getLineHandleRefPair(board, element);
829
+ points[0] = handleRefPair.source.point;
830
+ points[points.length - 1] = handleRefPair.target.point;
831
+ return points;
832
+ }
833
+ }
834
+ };
835
+ const getCurvePoints = (board, element) => {
836
+ if (element.points.length === 2) {
837
+ const handleRefPair = getLineHandleRefPair(board, element);
838
+ const { source, target } = handleRefPair;
839
+ const sourceBoundElement = handleRefPair.source.boundElement;
840
+ const targetBoundElement = handleRefPair.target.boundElement;
841
+ let curvePoints = [source.point];
842
+ const sumDistance = distanceBetweenPointAndPoint(...source.point, ...target.point);
843
+ const offset = 12 + sumDistance / 3;
844
+ if (sourceBoundElement) {
845
+ curvePoints.push(getPointByVector(source.point, source.vector, offset));
846
+ }
847
+ if (targetBoundElement) {
848
+ curvePoints.push(getPointByVector(target.point, target.vector, offset));
849
+ }
850
+ const isSingleBound = (sourceBoundElement && !targetBoundElement) || (!sourceBoundElement && targetBoundElement);
851
+ if (isSingleBound) {
852
+ curvePoints.push(target.point);
853
+ const points = Q2C(curvePoints);
854
+ return pointsOnBezierCurves(points);
855
+ }
856
+ if (!sourceBoundElement && !targetBoundElement) {
857
+ curvePoints.push(getPointByVector(source.point, source.vector, offset));
858
+ curvePoints.push(getPointByVector(target.point, target.vector, offset));
859
+ }
860
+ curvePoints.push(target.point);
861
+ return pointsOnBezierCurves(curvePoints);
862
+ }
863
+ else {
864
+ let dataPoints = PlaitLine.getPoints(board, element);
865
+ dataPoints = removeDuplicatePoints(dataPoints);
866
+ const points = catmullRomFitting(dataPoints);
867
+ return pointsOnBezierCurves(points);
868
+ }
869
+ };
870
+ function getMiddlePoints(board, element) {
871
+ const result = [];
872
+ const shape = element.shape;
873
+ const hideBuffer = 10;
874
+ if (shape === LineShape.straight) {
875
+ const points = PlaitLine.getPoints(board, element);
876
+ for (let i = 0; i < points.length - 1; i++) {
877
+ const distance = distanceBetweenPointAndPoint(...points[i], ...points[i + 1]);
878
+ if (distance < hideBuffer)
879
+ continue;
880
+ result.push([(points[i][0] + points[i + 1][0]) / 2, (points[i][1] + points[i + 1][1]) / 2]);
881
+ }
882
+ }
883
+ if (shape === LineShape.curve) {
884
+ const points = PlaitLine.getPoints(board, element);
885
+ const pointsOnBezier = getCurvePoints(board, element);
886
+ if (points.length === 2) {
887
+ const start = 0;
888
+ const endIndex = pointsOnBezier.length - 1;
889
+ const middleIndex = Math.round((start + endIndex) / 2);
890
+ result.push(pointsOnBezier[middleIndex]);
891
+ }
892
+ else {
893
+ for (let i = 0; i < points.length - 1; i++) {
894
+ const startIndex = pointsOnBezier.findIndex(point => point[0] === points[i][0] && point[1] === points[i][1]);
895
+ const endIndex = pointsOnBezier.findIndex(point => point[0] === points[i + 1][0] && point[1] === points[i + 1][1]);
896
+ const middleIndex = Math.round((startIndex + endIndex) / 2);
897
+ const distance = distanceBetweenPointAndPoint(...points[i], ...points[i + 1]);
898
+ if (distance < hideBuffer)
899
+ continue;
900
+ result.push(pointsOnBezier[middleIndex]);
901
+ }
902
+ }
903
+ }
904
+ if (shape === LineShape.elbow) {
905
+ const renderPoints = getElbowPoints(board, element);
906
+ const options = getElbowLineRouteOptions(board, element);
907
+ const isIntersect = isSourceAndTargetIntersect(options);
908
+ if (!isIntersect) {
909
+ const [nextSourcePoint, nextTargetPoint] = getNextSourceAndTargetPoints(board, element);
910
+ for (let i = 0; i < renderPoints.length - 1; i++) {
911
+ if ((i == 0 && Point.isEquals(renderPoints[i + 1], nextSourcePoint)) ||
912
+ (i === renderPoints.length - 2 && Point.isEquals(renderPoints[renderPoints.length - 2], nextTargetPoint))) {
913
+ continue;
914
+ }
915
+ const [currentX, currentY] = renderPoints[i];
916
+ const [nextX, nextY] = renderPoints[i + 1];
917
+ const middlePoint = [(currentX + nextX) / 2, (currentY + nextY) / 2];
918
+ result.push(middlePoint);
919
+ }
920
+ }
921
+ }
922
+ return result;
923
+ }
924
+ const drawLine = (board, element) => {
925
+ const strokeWidth = getStrokeWidthByElement(element);
926
+ const strokeColor = getStrokeColorByElement(board, element);
927
+ const strokeLineDash = getLineDashByElement(element);
928
+ const options = { stroke: strokeColor, strokeWidth, strokeLineDash };
929
+ const lineG = createG();
930
+ let points = getLinePoints(board, element);
931
+ let line;
932
+ if (element.shape === LineShape.curve) {
933
+ line = PlaitBoard.getRoughSVG(board).curve(points, options);
934
+ }
935
+ else {
936
+ line = drawLinearPath(points, options);
937
+ }
938
+ const id = idCreator();
939
+ line.setAttribute('mask', `url(#${id})`);
940
+ lineG.appendChild(line);
941
+ const { mask, maskTargetFillRect } = drawMask(board, element, id);
942
+ lineG.appendChild(mask);
943
+ line.appendChild(maskTargetFillRect);
944
+ const arrow = drawLineArrow(element, points, { stroke: strokeColor, strokeWidth });
945
+ arrow && lineG.appendChild(arrow);
946
+ return lineG;
947
+ };
948
+ const getConnectionByNearestPoint = (board, point, hitElement) => {
949
+ let rectangle = RectangleClient.getRectangleByPoints(hitElement.points);
950
+ let nearestPoint = getNearestPoint(hitElement, point);
951
+ const hitConnector = getHitConnectorPoint(nearestPoint, hitElement, rectangle);
952
+ nearestPoint = hitConnector ? hitConnector : nearestPoint;
953
+ return [(nearestPoint[0] - rectangle.x) / rectangle.width, (nearestPoint[1] - rectangle.y) / rectangle.height];
954
+ };
955
+ const getHitConnectorPoint = (point, hitElement, rectangle) => {
956
+ const shape = getShape(hitElement);
957
+ const connector = getEngine(shape).getConnectorPoints(rectangle);
958
+ const points = RectangleClient.getPoints(RectangleClient.getRectangleByCenterPoint(point, 10, 10));
959
+ const pointRectangle = RectangleClient.getRectangleByPoints(points);
960
+ return connector.find(point => {
961
+ return RectangleClient.isHit(pointRectangle, RectangleClient.getRectangleByPoints([point, point]));
962
+ });
963
+ };
964
+ const getLineTextRectangle = (board, element, index) => {
965
+ const text = element.texts[index];
966
+ const elbowPoints = getLinePoints(board, element);
967
+ const point = getPointOnPolyline(elbowPoints, text.position);
968
+ return {
969
+ x: point[0] - text.width / 2,
970
+ y: point[1] - text.height / 2,
971
+ width: text.width,
972
+ height: text.height
973
+ };
974
+ };
975
+ const getLines = (board) => {
976
+ return findElements(board, {
977
+ match: (element) => PlaitDrawElement.isLine(element),
978
+ recursion: (element) => PlaitDrawElement.isDrawElement(element)
979
+ });
980
+ };
981
+ // quadratic Bezier to cubic Bezier
982
+ const Q2C = (points) => {
983
+ const result = [];
984
+ const numSegments = points.length / 3;
985
+ for (let i = 0; i < numSegments; i++) {
986
+ const start = points[i];
987
+ const qControl = points[i + 1];
988
+ const end = points[i + 2];
989
+ const startDistance = distanceBetweenPointAndPoint(...start, ...qControl);
990
+ const endDistance = distanceBetweenPointAndPoint(...end, ...qControl);
991
+ const cControl1 = getExtendPoint(start, qControl, (startDistance * 2) / 3);
992
+ const cControl2 = getExtendPoint(end, qControl, (endDistance * 2) / 3);
993
+ result.push(start, cControl1, cControl2, end);
994
+ }
995
+ return result;
996
+ };
997
+ const handleLineCreating = (board, lineShape, sourcePoint, movingPoint, sourceElement, lineShapeG) => {
998
+ const hitElement = getHitOutlineGeometry(board, movingPoint, REACTION_MARGIN);
999
+ const targetConnection = hitElement ? getConnectionByNearestPoint(board, movingPoint, hitElement) : undefined;
1000
+ const connection = sourceElement ? getConnectionByNearestPoint(board, sourcePoint, sourceElement) : undefined;
1001
+ const targetBoundId = hitElement ? hitElement.id : undefined;
1002
+ const lineGenerator = new LineShapeGenerator(board);
1003
+ const memorizedLatest = getLineMemorizedLatest();
1004
+ let sourceMarker, targetMarker;
1005
+ sourceMarker = memorizedLatest.source;
1006
+ targetMarker = memorizedLatest.target;
1007
+ sourceMarker && delete memorizedLatest.source;
1008
+ targetMarker && delete memorizedLatest.target;
1009
+ const temporaryLineElement = createLineElement(lineShape, [sourcePoint, movingPoint], { marker: sourceMarker || LineMarkerType.none, connection: connection, boundId: sourceElement?.id }, { marker: targetMarker || LineMarkerType.arrow, connection: targetConnection, boundId: targetBoundId }, [], {
1010
+ strokeWidth: DefaultLineStyle.strokeWidth,
1011
+ ...memorizedLatest
1012
+ });
1013
+ const linePoints = getLinePoints(board, temporaryLineElement);
1014
+ const otherPoint = linePoints[0];
1015
+ temporaryLineElement.points[1] = alignPoints(otherPoint, movingPoint);
1016
+ lineGenerator.processDrawing(temporaryLineElement, lineShapeG);
1017
+ PlaitBoard.getElementActiveHost(board).append(lineShapeG);
1018
+ return temporaryLineElement;
1019
+ };
1020
+ function drawMask(board, element, id) {
1021
+ const mask = createMask();
1022
+ mask.setAttribute('id', id);
1023
+ const points = getLinePoints(board, element);
1024
+ let rectangle = RectangleClient.getRectangleByPoints(points);
1025
+ rectangle = RectangleClient.getOutlineRectangle(rectangle, -30);
1026
+ const maskFillRect = createRect(rectangle, {
1027
+ fill: 'white'
1028
+ });
1029
+ mask.appendChild(maskFillRect);
1030
+ const texts = element.texts;
1031
+ texts.forEach((text, index) => {
1032
+ let textRectangle = getLineTextRectangle(board, element, index);
1033
+ textRectangle = RectangleClient.inflate(textRectangle, LINE_TEXT_SPACE * 2);
1034
+ const rect = createRect(textRectangle, {
1035
+ fill: 'black'
1036
+ });
1037
+ mask.appendChild(rect);
1038
+ });
1039
+ // open line
1040
+ const maskTargetFillRect = createRect(rectangle);
1041
+ maskTargetFillRect.setAttribute('opacity', '0');
1042
+ maskTargetFillRect.setAttribute('fill', 'none');
1043
+ return { mask, maskTargetFillRect };
1044
+ }
1045
+
1046
+ const getSelectedDrawElements = (board) => {
1047
+ const selectedElements = getSelectedElements(board).filter(value => PlaitDrawElement.isDrawElement(value));
1048
+ return selectedElements;
1049
+ };
1050
+ const getSelectedGeometryElements = (board) => {
1051
+ const selectedElements = getSelectedElements(board).filter(value => PlaitDrawElement.isGeometry(value));
1052
+ return selectedElements;
1053
+ };
1054
+ const getSelectedLineElements = (board) => {
1055
+ const selectedElements = getSelectedElements(board).filter(value => PlaitDrawElement.isLine(value));
1056
+ return selectedElements;
1057
+ };
1058
+ const getSelectedImageElements = (board) => {
1059
+ const selectedElements = getSelectedElements(board).filter(value => PlaitDrawElement.isImage(value));
1060
+ return selectedElements;
1061
+ };
1062
+
1063
+ var LineResizeHandle;
1064
+ (function (LineResizeHandle) {
1065
+ LineResizeHandle["source"] = "source";
1066
+ LineResizeHandle["target"] = "target";
1067
+ LineResizeHandle["addHandle"] = "addHandle";
1068
+ })(LineResizeHandle || (LineResizeHandle = {}));
1069
+ const getHitLineResizeHandleRef = (board, element, point) => {
1070
+ let dataPoints = PlaitLine.getPoints(board, element);
1071
+ const index = getHitPointIndex(dataPoints, point);
1072
+ if (index !== -1) {
1073
+ const handleIndex = index;
1074
+ if (index === 0) {
1075
+ return { handle: LineResizeHandle.source, handleIndex };
1076
+ }
1077
+ if (index === dataPoints.length - 1) {
1078
+ return { handle: LineResizeHandle.target, handleIndex };
1079
+ }
1080
+ // elbow line, data points only verify source connection point and target connection point
1081
+ if (element.shape !== LineShape.elbow) {
1082
+ return { handleIndex };
1083
+ }
1084
+ }
1085
+ const middlePoints = getMiddlePoints(board, element);
1086
+ const indexOfMiddlePoints = getHitPointIndex(middlePoints, point);
1087
+ if (indexOfMiddlePoints !== -1) {
1088
+ return {
1089
+ handle: LineResizeHandle.addHandle,
1090
+ handleIndex: indexOfMiddlePoints
1091
+ };
1092
+ }
1093
+ return undefined;
1094
+ };
1095
+ function getHitPointIndex(points, movingPoint) {
1096
+ const rectangles = points.map(point => {
1097
+ return {
1098
+ x: point[0] - RESIZE_HANDLE_DIAMETER / 2,
1099
+ y: point[1] - RESIZE_HANDLE_DIAMETER / 2,
1100
+ width: RESIZE_HANDLE_DIAMETER,
1101
+ height: RESIZE_HANDLE_DIAMETER
1102
+ };
1103
+ });
1104
+ const rectangle = rectangles.find(rectangle => {
1105
+ return RectangleClient.isHit(RectangleClient.getRectangleByPoints([movingPoint, movingPoint]), rectangle);
1106
+ });
1107
+ return rectangle ? rectangles.indexOf(rectangle) : -1;
1108
+ }
1109
+ const getHitLineTextIndex = (board, element, point) => {
1110
+ const texts = element.texts;
1111
+ if (!texts.length)
1112
+ return -1;
1113
+ const points = getLinePoints(board, element);
1114
+ return texts.findIndex(text => {
1115
+ const center = getPointOnPolyline(points, text.position);
1116
+ const rectangle = {
1117
+ x: center[0] - text.width / 2,
1118
+ y: center[1] - text.height / 2,
1119
+ width: text.width,
1120
+ height: text.height
1121
+ };
1122
+ return RectangleClient.isHit(rectangle, RectangleClient.getRectangleByPoints([point, point]));
1123
+ });
1124
+ };
1125
+
1126
+ const isTextExceedingBounds = (geometry) => {
1127
+ const client = RectangleClient.getRectangleByPoints(geometry.points);
1128
+ if (geometry.textHeight > client.height) {
1129
+ return true;
161
1130
  }
162
- const strokeWidth = element.strokeWidth || DefaultGeometryStyle.strokeWidth;
163
- return strokeWidth;
1131
+ return false;
164
1132
  };
165
- const getStrokeColorByElement = (board, element) => {
166
- const defaultColor = getDrawDefaultStrokeColor(board.theme.themeColorMode);
167
- const strokeColor = element.strokeColor || defaultColor;
168
- return strokeColor;
1133
+ const isHitLineText = (board, element, point) => {
1134
+ return getHitLineTextIndex(board, element, point) !== -1;
169
1135
  };
170
- const getFillByElement = (board, element) => {
171
- const defaultFill = PlaitDrawElement.isFlowchart(element)
172
- ? getFlowchartDefaultFill(board.theme.themeColorMode)
173
- : DefaultGeometryStyle.fill;
174
- const fill = element.fill || defaultFill;
175
- return fill;
1136
+ const isHitPolyLine = (pathPoints, point, strokeWidth, expand = 0) => {
1137
+ const distance = distanceBetweenPointAndSegments(pathPoints, point);
1138
+ return distance <= strokeWidth + expand;
176
1139
  };
177
- const getLineDashByElement = (element) => {
178
- return element.strokeStyle === 'dashed' ? [8, 8 + getStrokeWidthByElement(element)] : undefined;
1140
+ const isRectangleHitDrawElement = (board, element, selection) => {
1141
+ const rangeRectangle = RectangleClient.getRectangleByPoints([selection.anchor, selection.focus]);
1142
+ if (PlaitDrawElement.isGeometry(element)) {
1143
+ const client = RectangleClient.getRectangleByPoints(element.points);
1144
+ if (isTextExceedingBounds(element)) {
1145
+ const textClient = getTextRectangle(element);
1146
+ return RectangleClient.isHit(rangeRectangle, client) || RectangleClient.isHit(rangeRectangle, textClient);
1147
+ }
1148
+ return RectangleClient.isHit(rangeRectangle, client);
1149
+ }
1150
+ if (PlaitDrawElement.isImage(element)) {
1151
+ const client = RectangleClient.getRectangleByPoints(element.points);
1152
+ return RectangleClient.isHit(rangeRectangle, client);
1153
+ }
1154
+ if (PlaitDrawElement.isLine(element)) {
1155
+ const points = getLinePoints(board, element);
1156
+ const strokeWidth = getStrokeWidthByElement(element);
1157
+ const isHitText = isHitLineText(board, element, selection.focus);
1158
+ const isHit = isHitPolyLine(points, selection.focus, strokeWidth, 3) || isHitText;
1159
+ const isContainPolyLinePoint = points.some(point => {
1160
+ return RectangleClient.isHit(rangeRectangle, RectangleClient.getRectangleByPoints([point, point]));
1161
+ });
1162
+ const isIntersect = Point.isEquals(selection.anchor, selection.focus) ? isHit : isPolylineHitRectangle(points, rangeRectangle);
1163
+ return isContainPolyLinePoint || isIntersect;
1164
+ }
1165
+ return null;
179
1166
  };
180
- const getStrokeStyleByElement = (element) => {
181
- return element.strokeStyle || StrokeStyle.solid;
1167
+ const isHitDrawElement = (board, element, point) => {
1168
+ if (PlaitDrawElement.isGeometry(element)) {
1169
+ const fill = getFillByElement(board, element);
1170
+ // when shape equals text, fill is not allowed
1171
+ if (fill !== DefaultGeometryStyle.fill && fill !== TRANSPARENT && !PlaitDrawElement.isText(element)) {
1172
+ return isRectangleHitDrawElement(board, element, { anchor: point, focus: point });
1173
+ }
1174
+ else {
1175
+ // if shape equals text, only check text rectangle
1176
+ if (PlaitDrawElement.isText(element)) {
1177
+ const textClient = getTextRectangle(element);
1178
+ let isHitText = RectangleClient.isPointInRectangle(textClient, point);
1179
+ return isHitText;
1180
+ }
1181
+ const strokeWidth = getStrokeWidthByElement(element);
1182
+ const engine = getEngine(getShape(element));
1183
+ const corners = engine.getCornerPoints(RectangleClient.getRectangleByPoints(element.points));
1184
+ const isHit = isHitPolyLine(corners, point, strokeWidth, 3);
1185
+ const textClient = getTextRectangle(element);
1186
+ let isHitText = RectangleClient.isPointInRectangle(textClient, point);
1187
+ return isHit || isHitText;
1188
+ }
1189
+ }
1190
+ if (PlaitDrawElement.isImage(element) || PlaitDrawElement.isLine(element)) {
1191
+ return isRectangleHitDrawElement(board, element, { anchor: point, focus: point });
1192
+ }
1193
+ return null;
1194
+ };
1195
+
1196
+ const getCenterPointsOnPolygon$1 = (points) => {
1197
+ const centerPoints = [];
1198
+ for (let i = 0; i < points.length; i++) {
1199
+ let j = i == points.length - 1 ? 0 : i + 1;
1200
+ centerPoints.push([(points[i][0] + points[j][0]) / 2, (points[i][1] + points[j][1]) / 2]);
1201
+ }
1202
+ return centerPoints;
1203
+ };
1204
+ const getCrossingPointBetweenPointAndPolygon = (corners, point) => {
1205
+ const result = [];
1206
+ for (let index = 1; index <= corners.length; index++) {
1207
+ let start = corners[index - 1];
1208
+ let end = index === corners.length ? corners[0] : corners[index];
1209
+ const crossingPoint = getCrossingPointsBetweenPointAndSegment(point, start, end);
1210
+ result.push(...crossingPoint);
1211
+ }
1212
+ return result;
1213
+ };
1214
+ const getPolygonEdgeByConnectionPoint = (corners, point) => {
1215
+ for (let index = 1; index <= corners.length; index++) {
1216
+ let start = corners[index - 1];
1217
+ let end = index === corners.length ? corners[0] : corners[index];
1218
+ if (isPointOnSegment(point, start, end)) {
1219
+ return [start, end];
1220
+ }
1221
+ }
1222
+ return null;
182
1223
  };
183
1224
 
184
1225
  const heightRatio$1 = 3 / 4;
@@ -203,13 +1244,13 @@ const CommentEngine = {
203
1244
  getEdgeByConnectionPoint(rectangle, pointOfRectangle) {
204
1245
  const corners = getCommentPoints(rectangle);
205
1246
  const point = RectangleClient.getConnectionPoint(rectangle, pointOfRectangle);
206
- return getEdgeOnPolygonByPoint(corners, point);
1247
+ return getPolygonEdgeByConnectionPoint(corners, point);
207
1248
  },
208
1249
  getConnectorPoints(rectangle) {
209
1250
  return RectangleClient.getEdgeCenterPoints(rectangle);
210
1251
  },
211
1252
  getTextRectangle(element) {
212
- const elementRectangle = getRectangleByPoints(element.points);
1253
+ const elementRectangle = RectangleClient.getRectangleByPoints(element.points);
213
1254
  const strokeWidth = getStrokeWidthByElement(element);
214
1255
  const height = element.textHeight;
215
1256
  const width = elementRectangle.width - ShapeDefaultSpace.rectangleAndText * 2 - strokeWidth * 2;
@@ -253,10 +1294,26 @@ function createPolygonEngine(options) {
253
1294
  getNearestPoint(rectangle, point) {
254
1295
  return getNearestPointBetweenPointAndSegments(point, getPoints(rectangle));
255
1296
  },
1297
+ getNearestCrossingPoint(rectangle, point) {
1298
+ const corners = getPoints(rectangle);
1299
+ const crossingPoints = getCrossingPointBetweenPointAndPolygon(corners, point);
1300
+ let nearestPoint = crossingPoints[0];
1301
+ let nearestDistance = distanceBetweenPointAndPoint(point[0], point[1], nearestPoint[0], nearestPoint[1]);
1302
+ crossingPoints
1303
+ .filter((v, index) => index > 0)
1304
+ .forEach(crossingPoint => {
1305
+ let distance = distanceBetweenPointAndPoint(point[0], point[1], crossingPoint[0], crossingPoint[1]);
1306
+ if (distance < nearestDistance) {
1307
+ nearestDistance = distance;
1308
+ nearestPoint = crossingPoint;
1309
+ }
1310
+ });
1311
+ return nearestPoint;
1312
+ },
256
1313
  getEdgeByConnectionPoint(rectangle, pointOfRectangle) {
257
1314
  const corners = getPoints(rectangle);
258
1315
  const point = RectangleClient.getConnectionPoint(rectangle, pointOfRectangle);
259
- return getEdgeOnPolygonByPoint(corners, point);
1316
+ return getPolygonEdgeByConnectionPoint(corners, point);
260
1317
  },
261
1318
  getConnectorPoints(rectangle) {
262
1319
  if (options.getConnectorPoints) {
@@ -337,8 +1394,9 @@ const EllipseEngine = {
337
1394
  const point = [connectionPoint[0] - centerPoint[0], -(connectionPoint[1] - centerPoint[1])];
338
1395
  const a = rectangle.width / 2;
339
1396
  const b = rectangle.height / 2;
340
- const slope = getTangentSlope(point[0], point[1], a, b);
341
- return getVectorBySlope(point[0], point[1], slope);
1397
+ const slope = getEllipseTangentSlope(point[0], point[1], a, b);
1398
+ const vector = getVectorFromPointAndSlope(point[0], point[1], slope);
1399
+ return vector;
342
1400
  },
343
1401
  getConnectorPoints(rectangle) {
344
1402
  return RectangleClient.getEdgeCenterPoints(rectangle);
@@ -386,31 +1444,6 @@ function getNearestPointBetweenPointAndEllipse(point, center, rx, ry, rotation =
386
1444
  const signY = point[1] > center[1] ? 1 : -1;
387
1445
  return [center[0] + a * tx * signX, center[1] + b * ty * signY];
388
1446
  }
389
- /**
390
- * the result of slope is based on Cartesian coordinate system
391
- * x, y are based on the position in the Cartesian coordinate system
392
- */
393
- function getTangentSlope(x, y, a, b) {
394
- const k = (-b * b * x) / (a * a * y);
395
- return k;
396
- }
397
- /**
398
- * x, y are based on the position in the Cartesian coordinate system
399
- */
400
- function getVectorBySlope(x, y, slope) {
401
- const deltaX = 30;
402
- const deltaY = -slope * deltaX;
403
- let start = [0 - deltaX, 0 - deltaY];
404
- let end = [0 + deltaX, 0 + deltaY];
405
- // y < 0 acts on the lower half of the x-axis, with the starting point at the top and the end point at the bottom.
406
- if (y < 0) {
407
- const temp = start;
408
- start = end;
409
- end = temp;
410
- }
411
- const vector = [end[0] - start[0], end[1] - start[1]];
412
- return vector;
413
- }
414
1447
 
415
1448
  const getHexagonPoints = (rectangle) => {
416
1449
  return [
@@ -502,7 +1535,7 @@ const ParallelogramEngine = createPolygonEngine({
502
1535
  getPolygonPoints: getParallelogramPoints,
503
1536
  getConnectorPoints: (rectangle) => {
504
1537
  const cornerPoints = getParallelogramPoints(rectangle);
505
- return getCenterPointsOnPolygon(cornerPoints);
1538
+ return getCenterPointsOnPolygon$1(cornerPoints);
506
1539
  },
507
1540
  getTextRectangle(element) {
508
1541
  const rectangle = getTextRectangle(element);
@@ -525,7 +1558,7 @@ const getPentagonPoints = (rectangle) => {
525
1558
  const PentagonEngine = createPolygonEngine({
526
1559
  getPolygonPoints: getPentagonPoints,
527
1560
  getTextRectangle(element) {
528
- const elementRectangle = getRectangleByPoints(element.points);
1561
+ const elementRectangle = RectangleClient.getRectangleByPoints(element.points);
529
1562
  const strokeWidth = getStrokeWidthByElement(element);
530
1563
  const height = element.textHeight;
531
1564
  const originWidth = elementRectangle.width - ShapeDefaultSpace.rectangleAndText * 2 - strokeWidth * 2;
@@ -575,7 +1608,7 @@ const ProcessArrowEngine = createPolygonEngine({
575
1608
  getPolygonPoints: getProcessArrowPoints,
576
1609
  getTextRectangle(element) {
577
1610
  const rectangle = getTextRectangle(element);
578
- const elementRectangle = getRectangleByPoints(element.points);
1611
+ const elementRectangle = RectangleClient.getRectangleByPoints(element.points);
579
1612
  const width = rectangle.width;
580
1613
  rectangle.width = elementRectangle.height / 2;
581
1614
  rectangle.x += elementRectangle.height / 2;
@@ -588,7 +1621,7 @@ const RectangleEngine = {
588
1621
  return drawRectangle(board, rectangle, { ...options, fillStyle: 'solid' });
589
1622
  },
590
1623
  isHit(rectangle, point) {
591
- const rangeRectangle = RectangleClient.toRectangleClient([point, point]);
1624
+ const rangeRectangle = RectangleClient.getRectangleByPoints([point, point]);
592
1625
  return RectangleClient.isHit(rectangle, rangeRectangle);
593
1626
  },
594
1627
  getCornerPoints(rectangle) {
@@ -600,7 +1633,7 @@ const RectangleEngine = {
600
1633
  getEdgeByConnectionPoint(rectangle, pointOfRectangle) {
601
1634
  const corners = RectangleEngine.getCornerPoints(rectangle);
602
1635
  const point = RectangleClient.getConnectionPoint(rectangle, pointOfRectangle);
603
- return getEdgeOnPolygonByPoint(corners, point);
1636
+ return getPolygonEdgeByConnectionPoint(corners, point);
604
1637
  },
605
1638
  getConnectorPoints(rectangle) {
606
1639
  return RectangleClient.getEdgeCenterPoints(rectangle);
@@ -649,7 +1682,7 @@ const RoundRectangleEngine = {
649
1682
  getEdgeByConnectionPoint(rectangle, pointOfRectangle) {
650
1683
  const corners = RectangleEngine.getCornerPoints(rectangle);
651
1684
  const point = RectangleClient.getConnectionPoint(rectangle, pointOfRectangle);
652
- return getEdgeOnPolygonByPoint(corners, point);
1685
+ return getPolygonEdgeByConnectionPoint(corners, point);
653
1686
  },
654
1687
  getConnectorPoints(rectangle) {
655
1688
  return RectangleClient.getEdgeCenterPoints(rectangle);
@@ -728,7 +1761,7 @@ const RoundCommentEngine = {
728
1761
  getEdgeByConnectionPoint(rectangle, pointOfRectangle) {
729
1762
  const corners = getRoundCommentPoints(rectangle);
730
1763
  const point = RectangleClient.getConnectionPoint(rectangle, pointOfRectangle);
731
- return getEdgeOnPolygonByPoint(corners, point);
1764
+ return getPolygonEdgeByConnectionPoint(corners, point);
732
1765
  },
733
1766
  getConnectorPoints(rectangle) {
734
1767
  return [
@@ -739,7 +1772,7 @@ const RoundCommentEngine = {
739
1772
  ];
740
1773
  },
741
1774
  getTextRectangle(element) {
742
- const elementRectangle = getRectangleByPoints(element.points);
1775
+ const elementRectangle = RectangleClient.getRectangleByPoints(element.points);
743
1776
  const strokeWidth = getStrokeWidthByElement(element);
744
1777
  const height = element.textHeight;
745
1778
  const width = elementRectangle.width - ShapeDefaultSpace.rectangleAndText * 2 - strokeWidth * 2;
@@ -775,7 +1808,7 @@ const TrapezoidEngine = createPolygonEngine({
775
1808
  getPolygonPoints: getTrapezoidPoints,
776
1809
  getConnectorPoints(rectangle) {
777
1810
  const points = getTrapezoidPoints(rectangle);
778
- return getCenterPointsOnPolygon(points);
1811
+ return getCenterPointsOnPolygon$1(points);
779
1812
  },
780
1813
  getTextRectangle(element) {
781
1814
  const rectangle = getTextRectangle(element);
@@ -797,11 +1830,11 @@ const TriangleEngine = createPolygonEngine({
797
1830
  getPolygonPoints: getTrianglePoints,
798
1831
  getConnectorPoints(rectangle) {
799
1832
  const cornerPoints = getTrianglePoints(rectangle);
800
- const lineCenterPoints = getCenterPointsOnPolygon(cornerPoints);
1833
+ const lineCenterPoints = getCenterPointsOnPolygon$1(cornerPoints);
801
1834
  return [...lineCenterPoints, ...cornerPoints];
802
1835
  },
803
1836
  getTextRectangle(element) {
804
- const elementRectangle = getRectangleByPoints(element.points);
1837
+ const elementRectangle = RectangleClient.getRectangleByPoints(element.points);
805
1838
  const strokeWidth = getStrokeWidthByElement(element);
806
1839
  const height = element.textHeight;
807
1840
  const originWidth = elementRectangle.width - ShapeDefaultSpace.rectangleAndText * 2 - strokeWidth * 2;
@@ -860,7 +1893,7 @@ const StarEngine = createPolygonEngine({
860
1893
  return [points[1], points[3], points[5], points[7], points[9]];
861
1894
  },
862
1895
  getTextRectangle(element) {
863
- const elementRectangle = getRectangleByPoints(element.points);
1896
+ const elementRectangle = RectangleClient.getRectangleByPoints(element.points);
864
1897
  const strokeWidth = getStrokeWidthByElement(element);
865
1898
  const height = element.textHeight;
866
1899
  const originWidth = elementRectangle.width - ShapeDefaultSpace.rectangleAndText * 2 - strokeWidth * 2;
@@ -887,10 +1920,18 @@ const TerminalEngine = {
887
1920
  getNearestPoint(rectangle, point) {
888
1921
  return getNearestPointBetweenPointAndRoundRectangle(point, rectangle, getStartEndRadius(rectangle));
889
1922
  },
890
- getEdgeByConnectionPoint(rectangle, pointOfRectangle) {
891
- const corners = RectangleEngine.getCornerPoints(rectangle);
892
- const point = RectangleClient.getConnectionPoint(rectangle, pointOfRectangle);
893
- return getEdgeOnPolygonByPoint(corners, point);
1923
+ getTangentVectorByConnectionPoint(rectangle, pointOfRectangle) {
1924
+ const connectionPoint = RectangleClient.getConnectionPoint(rectangle, pointOfRectangle);
1925
+ const radius = getStartEndRadius(rectangle);
1926
+ const center = getBoundCenterOfRoundRectangle(rectangle, radius, connectionPoint);
1927
+ if (center) {
1928
+ const point = [connectionPoint[0] - center[0], -(connectionPoint[1] - center[1])];
1929
+ const a = radius;
1930
+ const b = radius;
1931
+ const slope = getEllipseTangentSlope(point[0], point[1], a, b);
1932
+ return getVectorFromPointAndSlope(point[0], point[1], slope);
1933
+ }
1934
+ return null;
894
1935
  },
895
1936
  getConnectorPoints(rectangle) {
896
1937
  return RectangleClient.getEdgeCenterPoints(rectangle);
@@ -900,33 +1941,37 @@ const getStartEndRadius = (rectangle) => {
900
1941
  return Math.min(rectangle.width / 2, rectangle.height / 2);
901
1942
  };
902
1943
  function getNearestPointBetweenPointAndRoundRectangle(point, rectangle, radius) {
903
- const { x: rectX, y: rectY, width, height } = rectangle;
904
- const cornerPoints = RectangleClient.getCornerPoints(rectangle);
905
- let result = getNearestPointBetweenPointAndSegments(point, cornerPoints);
906
- let circleCenter = null;
907
- const inLeftTop = point[0] >= rectX && point[0] <= rectX + radius && point[1] >= rectY && point[1] <= rectY + radius;
1944
+ let result = null;
1945
+ let boundCenter = getBoundCenterOfRoundRectangle(rectangle, radius, point);
1946
+ if (boundCenter) {
1947
+ result = getNearestPointBetweenPointAndEllipse(point, boundCenter, radius, radius);
1948
+ }
1949
+ else {
1950
+ const cornerPoints = RectangleClient.getCornerPoints(rectangle);
1951
+ result = getNearestPointBetweenPointAndSegments(point, cornerPoints);
1952
+ }
1953
+ return result;
1954
+ }
1955
+ function getBoundCenterOfRoundRectangle(rectangle, radius, point) {
1956
+ const { x, y, width, height } = rectangle;
1957
+ let center = null;
1958
+ const inLeftTop = point[0] >= x && point[0] <= x + radius && point[1] >= y && point[1] <= y + radius;
908
1959
  if (inLeftTop) {
909
- circleCenter = [rectX + radius, rectY + radius];
1960
+ center = [x + radius, y + radius];
910
1961
  }
911
- const inLeftBottom = point[0] >= rectX && point[0] <= rectX + radius && point[1] >= rectY + height && point[1] <= rectY + height - radius;
1962
+ const inLeftBottom = point[0] >= x && point[0] <= x + radius && point[1] >= y + height - radius && point[1] <= y + height;
912
1963
  if (inLeftBottom) {
913
- circleCenter = [rectX + radius, rectY + height - radius];
1964
+ center = [x + radius, y + height - radius];
914
1965
  }
915
- const inRightTop = point[0] >= rectX + width - radius && point[0] <= rectX + width && point[1] >= rectY && point[1] <= rectY + radius;
1966
+ const inRightTop = point[0] >= x + width - radius && point[0] <= x + width && point[1] >= y && point[1] <= y + radius;
916
1967
  if (inRightTop) {
917
- circleCenter = [rectX + width - radius, rectY + radius];
1968
+ center = [x + width - radius, y + radius];
918
1969
  }
919
- const inRightBottom = point[0] >= rectX + width - radius &&
920
- point[0] <= rectX + width &&
921
- point[1] >= rectY + height - radius &&
922
- point[1] <= rectY + height;
1970
+ const inRightBottom = point[0] >= x + width - radius && point[0] <= x + width && point[1] >= y + height - radius && point[1] <= y + height;
923
1971
  if (inRightBottom) {
924
- circleCenter = [rectX + width - radius, rectY + height - radius];
925
- }
926
- if (circleCenter) {
927
- result = getNearestPointBetweenPointAndEllipse(point, circleCenter, radius, radius);
1972
+ center = [x + width - radius, y + height - radius];
928
1973
  }
929
- return result;
1974
+ return center;
930
1975
  }
931
1976
 
932
1977
  const getManualInputPoints = (rectangle) => {
@@ -941,10 +1986,10 @@ const ManualInputEngine = createPolygonEngine({
941
1986
  getPolygonPoints: getManualInputPoints,
942
1987
  getConnectorPoints: (rectangle) => {
943
1988
  const cornerPoints = getManualInputPoints(rectangle);
944
- return getCenterPointsOnPolygon(cornerPoints);
1989
+ return getCenterPointsOnPolygon$1(cornerPoints);
945
1990
  },
946
1991
  getTextRectangle: (element) => {
947
- const elementRectangle = getRectangleByPoints(element.points);
1992
+ const elementRectangle = RectangleClient.getRectangleByPoints(element.points);
948
1993
  const strokeWidth = getStrokeWidthByElement(element);
949
1994
  const height = element.textHeight;
950
1995
  const width = elementRectangle.width - ShapeDefaultSpace.rectangleAndText * 2 - strokeWidth * 2;
@@ -993,7 +2038,7 @@ const ManualLoopEngine = createPolygonEngine({
993
2038
  getPolygonPoints: getManualLoopPoints,
994
2039
  getConnectorPoints: (rectangle) => {
995
2040
  const cornerPoints = getManualLoopPoints(rectangle);
996
- return getCenterPointsOnPolygon(cornerPoints);
2041
+ return getCenterPointsOnPolygon$1(cornerPoints);
997
2042
  },
998
2043
  getTextRectangle(element) {
999
2044
  const rectangle = getTextRectangle(element);
@@ -1015,11 +2060,11 @@ const MergeEngine = createPolygonEngine({
1015
2060
  getPolygonPoints: getMergePoints,
1016
2061
  getConnectorPoints: (rectangle) => {
1017
2062
  const cornerPoints = getMergePoints(rectangle);
1018
- const lineCenterPoints = getCenterPointsOnPolygon(cornerPoints);
2063
+ const lineCenterPoints = getCenterPointsOnPolygon$1(cornerPoints);
1019
2064
  return [...lineCenterPoints, ...cornerPoints];
1020
2065
  },
1021
2066
  getTextRectangle(element) {
1022
- const elementRectangle = getRectangleByPoints(element.points);
2067
+ const elementRectangle = RectangleClient.getRectangleByPoints(element.points);
1023
2068
  const strokeWidth = getStrokeWidthByElement(element);
1024
2069
  const height = element.textHeight;
1025
2070
  const originWidth = elementRectangle.width - ShapeDefaultSpace.rectangleAndText * 2 - strokeWidth * 2;
@@ -1043,7 +2088,7 @@ const DelayEngine = {
1043
2088
  },
1044
2089
  isHit(rectangle, point) {
1045
2090
  //split shape to rectangle and a half ellipse
1046
- const rangeRectangle = RectangleClient.toRectangleClient([point, point]);
2091
+ const rangeRectangle = RectangleClient.getRectangleByPoints([point, point]);
1047
2092
  const isInRectangle = RectangleClient.isHit({
1048
2093
  ...rectangle,
1049
2094
  width: (rectangle.width * 3) / 4
@@ -1067,8 +2112,8 @@ const DelayEngine = {
1067
2112
  const point = [connectionPoint[0] - centerPoint[0], -(connectionPoint[1] - centerPoint[1])];
1068
2113
  const a = rectangle.width / 4;
1069
2114
  const b = rectangle.height / 2;
1070
- const slope = getTangentSlope(point[0], point[1], a, b);
1071
- return getVectorBySlope(point[0], point[1], slope);
2115
+ const slope = getEllipseTangentSlope(point[0], point[1], a, b);
2116
+ return getVectorFromPointAndSlope(point[0], point[1], slope);
1072
2117
  },
1073
2118
  getConnectorPoints(rectangle) {
1074
2119
  return RectangleClient.getEdgeCenterPoints(rectangle);
@@ -1087,7 +2132,7 @@ const StoredDataEngine = {
1087
2132
  },
1088
2133
  isHit(rectangle, point) {
1089
2134
  //split shape to rectangle and a half ellipse
1090
- const rangeRectangle = RectangleClient.toRectangleClient([point, point]);
2135
+ const rangeRectangle = RectangleClient.getRectangleByPoints([point, point]);
1091
2136
  const isInRectangle = RectangleClient.isHit({
1092
2137
  ...rectangle,
1093
2138
  x: rectangle.x + rectangle.width / 10,
@@ -1103,14 +2148,20 @@ const StoredDataEngine = {
1103
2148
  getNearestPoint(rectangle, point) {
1104
2149
  const nearestPoint = getNearestPointBetweenPointAndSegments(point, RectangleEngine.getCornerPoints(rectangle));
1105
2150
  if (nearestPoint[0] < rectangle.x + rectangle.width / 10) {
1106
- const nearestPoint = getNearestPointBetweenPointAndEllipse(point, [rectangle.x + rectangle.width / 10, rectangle.y + rectangle.height / 2], rectangle.width / 10, rectangle.height / 2);
1107
- if (nearestPoint[0] > rectangle.x + rectangle.width / 10) {
1108
- nearestPoint[0] = (rectangle.x + rectangle.width / 10) * 2 - nearestPoint[0];
2151
+ const centerPoint = [rectangle.x + rectangle.width / 10, rectangle.y + rectangle.height / 2];
2152
+ const nearestPoint = getNearestPointBetweenPointAndEllipse(point, centerPoint, rectangle.width / 10, rectangle.height / 2);
2153
+ if (nearestPoint[0] > centerPoint[0]) {
2154
+ nearestPoint[0] = centerPoint[0] * 2 - nearestPoint[0];
1109
2155
  }
1110
2156
  return nearestPoint;
1111
2157
  }
1112
2158
  if (nearestPoint[0] > rectangle.x + (rectangle.width * 9) / 10) {
1113
- return getNearestPointBetweenPointAndEllipse(point, [rectangle.x + rectangle.width, rectangle.y + rectangle.height / 2], rectangle.width / 10, rectangle.height / 2);
2159
+ const centerPoint = [rectangle.x + rectangle.width, rectangle.y + rectangle.height / 2];
2160
+ const nearestPoint = getNearestPointBetweenPointAndEllipse(point, centerPoint, rectangle.width / 10, rectangle.height / 2);
2161
+ if (nearestPoint[0] > centerPoint[0]) {
2162
+ nearestPoint[0] = centerPoint[0] * 2 - nearestPoint[0];
2163
+ }
2164
+ return nearestPoint;
1114
2165
  }
1115
2166
  return nearestPoint;
1116
2167
  },
@@ -1124,8 +2175,8 @@ const StoredDataEngine = {
1124
2175
  centerPoint = [rectangle.x + rectangle.width, rectangle.y + rectangle.height / 2];
1125
2176
  }
1126
2177
  const point = [connectionPoint[0] - centerPoint[0], -(connectionPoint[1] - centerPoint[1])];
1127
- const slope = getTangentSlope(point[0], point[1], a, b);
1128
- const vector = getVectorBySlope(point[0], point[1], slope);
2178
+ const slope = getEllipseTangentSlope(point[0], point[1], a, b);
2179
+ const vector = getVectorFromPointAndSlope(point[0], point[1], slope);
1129
2180
  return isBackEllipse ? vector.map(num => -num) : vector;
1130
2181
  },
1131
2182
  getConnectorPoints(rectangle) {
@@ -1146,130 +2197,40 @@ const StoredDataEngine = {
1146
2197
  };
1147
2198
 
1148
2199
  const ShapeEngineMap = {
1149
- [BasicShapes.rectangle]: RectangleEngine,
1150
- [BasicShapes.diamond]: DiamondEngine,
1151
- [BasicShapes.ellipse]: EllipseEngine,
1152
- [BasicShapes.parallelogram]: ParallelogramEngine,
1153
- [BasicShapes.roundRectangle]: RoundRectangleEngine,
1154
- [BasicShapes.text]: RectangleEngine,
1155
- [BasicShapes.triangle]: TriangleEngine,
1156
- [BasicShapes.leftArrow]: LeftArrowEngine,
1157
- [BasicShapes.trapezoid]: TrapezoidEngine,
1158
- [BasicShapes.rightArrow]: RightArrowEngine,
1159
- [BasicShapes.cross]: CrossEngine,
1160
- [BasicShapes.star]: StarEngine,
1161
- [BasicShapes.pentagon]: PentagonEngine,
1162
- [BasicShapes.hexagon]: HexagonEngine,
1163
- [BasicShapes.octagon]: OctagonEngine,
1164
- [BasicShapes.pentagonArrow]: PentagonArrowEngine,
1165
- [BasicShapes.processArrow]: ProcessArrowEngine,
1166
- [BasicShapes.twoWayArrow]: TwoWayArrowEngine,
1167
- [BasicShapes.comment]: CommentEngine,
1168
- [BasicShapes.roundComment]: RoundCommentEngine,
1169
- [FlowchartSymbols.process]: RectangleEngine,
1170
- [FlowchartSymbols.decision]: DiamondEngine,
1171
- [FlowchartSymbols.connector]: EllipseEngine,
1172
- [FlowchartSymbols.data]: ParallelogramEngine,
1173
- [FlowchartSymbols.terminal]: TerminalEngine,
1174
- [FlowchartSymbols.manualInput]: ManualInputEngine,
1175
- [FlowchartSymbols.preparation]: PreparationEngine,
1176
- [FlowchartSymbols.manualLoop]: ManualLoopEngine,
1177
- [FlowchartSymbols.merge]: MergeEngine,
1178
- [FlowchartSymbols.delay]: DelayEngine,
1179
- [FlowchartSymbols.storedData]: StoredDataEngine
1180
- };
1181
- const getEngine = (shape) => {
1182
- return ShapeEngineMap[shape];
1183
- };
1184
-
1185
- const getShape = (value) => {
1186
- if (PlaitDrawElement.isImage(value)) {
1187
- return BasicShapes.rectangle;
1188
- }
1189
- return value.shape;
1190
- };
1191
-
1192
- const DefaultLineStyle = {
1193
- strokeWidth: 2,
1194
- strokeColor: '#000'
1195
- };
1196
- const LINE_TEXT_SPACE = 4;
1197
- const LINE_AUTO_COMPLETE_DIAMETER = 6;
1198
- const LINE_AUTO_COMPLETE_OPACITY = 0.6;
1199
- const LINE_AUTO_COMPLETE_HOVERED_OPACITY = 0.8;
1200
- const LINE_AUTO_COMPLETE_HOVERED_DIAMETER = 10;
1201
-
1202
- const SHAPE_MAX_LENGTH = 6;
1203
- const memorizedShape = new WeakMap();
1204
- const getMemorizeKey = (element) => {
1205
- let key = '';
1206
- switch (true) {
1207
- case PlaitDrawElement.isText(element): {
1208
- key = MemorizeKey.text;
1209
- break;
1210
- }
1211
- case PlaitDrawElement.isBasicShape(element): {
1212
- key = MemorizeKey.basicShape;
1213
- break;
1214
- }
1215
- case PlaitDrawElement.isFlowchart(element): {
1216
- key = MemorizeKey.flowchart;
1217
- break;
1218
- }
1219
- case PlaitDrawElement.isLine(element): {
1220
- key = MemorizeKey.line;
1221
- break;
1222
- }
1223
- }
1224
- return key;
1225
- };
1226
- const getLineMemorizedLatest = () => {
1227
- const properties = getMemorizedLatest(MemorizeKey.line);
1228
- delete properties?.text;
1229
- return { ...properties } || {};
1230
- };
1231
- const getMemorizedLatestByPointer = (pointer) => {
1232
- let memorizeKey = '';
1233
- if (PlaitDrawElement.isBasicShape({ shape: pointer })) {
1234
- memorizeKey = pointer === BasicShapes.text ? MemorizeKey.text : MemorizeKey.basicShape;
1235
- }
1236
- else {
1237
- memorizeKey = MemorizeKey.flowchart;
1238
- }
1239
- const properties = { ...getMemorizedLatest(memorizeKey) } || {};
1240
- const textProperties = { ...properties.text } || {};
1241
- delete properties.text;
1242
- return { textProperties, geometryProperties: properties };
1243
- };
1244
- const memorizeLatestText = (element, operations) => {
1245
- const memorizeKey = getMemorizeKey(element);
1246
- let textMemory = getMemorizedLatest(memorizeKey)?.text || {};
1247
- const setNodeOperation = operations.find(operation => operation.type === 'set_node');
1248
- if (setNodeOperation) {
1249
- const newProperties = setNodeOperation.newProperties;
1250
- textMemory = { ...textMemory, ...newProperties };
1251
- memorizeLatest(memorizeKey, 'text', textMemory);
1252
- }
1253
- };
1254
- const memorizeLatestShape = (board, shape) => {
1255
- const shapes = memorizedShape.has(board) ? memorizedShape.get(board) : [];
1256
- const shapeIndex = shapes.indexOf(shape);
1257
- if (shape === BasicShapes.text || shapeIndex === 0) {
1258
- return;
1259
- }
1260
- if (shapeIndex !== -1) {
1261
- shapes.splice(shapeIndex, 1);
1262
- }
1263
- else {
1264
- if (shapes.length === SHAPE_MAX_LENGTH) {
1265
- shapes.pop();
1266
- }
1267
- }
1268
- shapes.unshift(shape);
1269
- memorizedShape.set(board, shapes);
2200
+ [BasicShapes.rectangle]: RectangleEngine,
2201
+ [BasicShapes.diamond]: DiamondEngine,
2202
+ [BasicShapes.ellipse]: EllipseEngine,
2203
+ [BasicShapes.parallelogram]: ParallelogramEngine,
2204
+ [BasicShapes.roundRectangle]: RoundRectangleEngine,
2205
+ [BasicShapes.text]: RectangleEngine,
2206
+ [BasicShapes.triangle]: TriangleEngine,
2207
+ [BasicShapes.leftArrow]: LeftArrowEngine,
2208
+ [BasicShapes.trapezoid]: TrapezoidEngine,
2209
+ [BasicShapes.rightArrow]: RightArrowEngine,
2210
+ [BasicShapes.cross]: CrossEngine,
2211
+ [BasicShapes.star]: StarEngine,
2212
+ [BasicShapes.pentagon]: PentagonEngine,
2213
+ [BasicShapes.hexagon]: HexagonEngine,
2214
+ [BasicShapes.octagon]: OctagonEngine,
2215
+ [BasicShapes.pentagonArrow]: PentagonArrowEngine,
2216
+ [BasicShapes.processArrow]: ProcessArrowEngine,
2217
+ [BasicShapes.twoWayArrow]: TwoWayArrowEngine,
2218
+ [BasicShapes.comment]: CommentEngine,
2219
+ [BasicShapes.roundComment]: RoundCommentEngine,
2220
+ [FlowchartSymbols.process]: RectangleEngine,
2221
+ [FlowchartSymbols.decision]: DiamondEngine,
2222
+ [FlowchartSymbols.connector]: EllipseEngine,
2223
+ [FlowchartSymbols.data]: ParallelogramEngine,
2224
+ [FlowchartSymbols.terminal]: TerminalEngine,
2225
+ [FlowchartSymbols.manualInput]: ManualInputEngine,
2226
+ [FlowchartSymbols.preparation]: PreparationEngine,
2227
+ [FlowchartSymbols.manualLoop]: ManualLoopEngine,
2228
+ [FlowchartSymbols.merge]: MergeEngine,
2229
+ [FlowchartSymbols.delay]: DelayEngine,
2230
+ [FlowchartSymbols.storedData]: StoredDataEngine
1270
2231
  };
1271
- const getMemorizedLatestShape = (board) => {
1272
- return memorizedShape.get(board);
2232
+ const getEngine = (shape) => {
2233
+ return ShapeEngineMap[shape];
1273
2234
  };
1274
2235
 
1275
2236
  const createGeometryElement = (shape, points, text, options = {}, textProperties = {}) => {
@@ -1298,13 +2259,8 @@ const createGeometryElement = (shape, points, text, options = {}, textProperties
1298
2259
  ...options
1299
2260
  };
1300
2261
  };
1301
- const getPointsByCenterPoint = (point, width, height) => {
1302
- const leftTopPoint = [point[0] - width / 2, point[1] - height / 2];
1303
- const rightBottomPoint = [point[0] + width / 2, point[1] + height / 2];
1304
- return [leftTopPoint, rightBottomPoint];
1305
- };
1306
2262
  const getTextRectangle = (element) => {
1307
- const elementRectangle = getRectangleByPoints(element.points);
2263
+ const elementRectangle = RectangleClient.getRectangleByPoints(element.points);
1308
2264
  const strokeWidth = getStrokeWidthByElement(element);
1309
2265
  const height = element.textHeight;
1310
2266
  const width = elementRectangle.width - ShapeDefaultSpace.rectangleAndText * 2 - strokeWidth * 2;
@@ -1317,7 +2273,7 @@ const getTextRectangle = (element) => {
1317
2273
  };
1318
2274
  const drawBoundMask = (board, element) => {
1319
2275
  const G = createG();
1320
- const rectangle = getRectangleByPoints(element.points);
2276
+ const rectangle = RectangleClient.getRectangleByPoints(element.points);
1321
2277
  const activeRectangle = RectangleClient.inflate(rectangle, ACTIVE_STROKE_WIDTH);
1322
2278
  const shape = getShape(element);
1323
2279
  const maskG = drawGeometry(board, activeRectangle, shape, {
@@ -1342,11 +2298,10 @@ const drawBoundMask = (board, element) => {
1342
2298
  const drawGeometry = (board, outerRectangle, shape, options) => {
1343
2299
  return getEngine(shape).draw(board, outerRectangle, options);
1344
2300
  };
1345
- const getNearestPoint = (element, point, inflateDelta = 0) => {
1346
- const rectangle = getRectangleByPoints(element.points);
1347
- const activeRectangle = RectangleClient.inflate(rectangle, inflateDelta);
2301
+ const getNearestPoint = (element, point) => {
2302
+ const rectangle = RectangleClient.getRectangleByPoints(element.points);
1348
2303
  const shape = getShape(element);
1349
- return getEngine(shape).getNearestPoint(activeRectangle, point);
2304
+ return getEngine(shape).getNearestPoint(rectangle, point);
1350
2305
  };
1351
2306
  const getCenterPointsOnPolygon = (points) => {
1352
2307
  const centerPoint = [];
@@ -1356,17 +2311,6 @@ const getCenterPointsOnPolygon = (points) => {
1356
2311
  }
1357
2312
  return centerPoint;
1358
2313
  };
1359
- const getEdgeOnPolygonByPoint = (corners, point) => {
1360
- for (let index = 1; index <= corners.length; index++) {
1361
- let start = corners[index - 1];
1362
- let end = index === corners.length ? corners[0] : corners[index];
1363
- const distance = distanceBetweenPointAndSegment(point[0], point[1], start[0], start[1], end[0], end[1]);
1364
- if (distance < 1) {
1365
- return [start, end];
1366
- }
1367
- }
1368
- return null;
1369
- };
1370
2314
  const getDefaultFlowchartProperty = (symbol) => {
1371
2315
  return DefaultFlowchartPropertyMap[symbol];
1372
2316
  };
@@ -1382,525 +2326,194 @@ const createDefaultFlowchart = (point) => {
1382
2326
  };
1383
2327
  const startElement = createGeometryElement(FlowchartSymbols.terminal, getDefaultGeometryPoints(FlowchartSymbols.terminal, point), '开始', options);
1384
2328
  const processPoint1 = [point[0], point[1] + terminalProperty.height / 2 + 55 + processProperty.height / 2];
1385
- const processElement1 = createGeometryElement(FlowchartSymbols.process, getDefaultGeometryPoints(FlowchartSymbols.process, processPoint1), '过程', options);
1386
- const decisionPoint = [processPoint1[0], processPoint1[1] + processProperty.height / 2 + 55 + decisionProperty.height / 2];
1387
- const decisionElement = createGeometryElement(FlowchartSymbols.decision, getDefaultGeometryPoints(FlowchartSymbols.decision, decisionPoint), '过程', options);
1388
- const processPoint2 = [decisionPoint[0] + decisionProperty.width / 2 + 75 + processProperty.width / 2, decisionPoint[1]];
1389
- const processElement2 = createGeometryElement(FlowchartSymbols.process, getDefaultGeometryPoints(FlowchartSymbols.process, processPoint2), '过程', options);
1390
- const endPoint = [decisionPoint[0], decisionPoint[1] + decisionProperty.height / 2 + 95 + terminalProperty.height / 2];
1391
- const endElement = createGeometryElement(FlowchartSymbols.terminal, getDefaultGeometryPoints(FlowchartSymbols.terminal, endPoint), '结束', options);
1392
- const line1 = createLineElement(LineShape.elbow, [
1393
- [0, 0],
1394
- [0, 0]
1395
- ], { marker: LineMarkerType.none, connection: [0.5, 1], boundId: startElement.id }, { marker: LineMarkerType.arrow, connection: [0.5, 0], boundId: processElement1.id }, [], lineOptions);
1396
- const line2 = createLineElement(LineShape.elbow, [
1397
- [0, 0],
1398
- [0, 0]
1399
- ], { marker: LineMarkerType.none, connection: [0.5, 1], boundId: processElement1.id }, { marker: LineMarkerType.arrow, connection: [0.5, 0], boundId: decisionElement.id }, [], lineOptions);
1400
- const line3 = createLineElement(LineShape.elbow, [
1401
- [0, 0],
1402
- [0, 0]
1403
- ], { marker: LineMarkerType.none, connection: [0.5, 1], boundId: decisionElement.id }, { marker: LineMarkerType.arrow, connection: [0.5, 0], boundId: endElement.id }, [
1404
- {
1405
- text: buildText('是'),
1406
- position: 0.5,
1407
- width: 14,
1408
- height: 20
1409
- }
1410
- ], lineOptions);
1411
- const line4 = createLineElement(LineShape.elbow, [
1412
- [0, 0],
1413
- [0, 0]
1414
- ], { marker: LineMarkerType.none, connection: [1, 0.5], boundId: decisionElement.id }, { marker: LineMarkerType.arrow, connection: [0, 0.5], boundId: processElement2.id }, [
1415
- {
1416
- text: buildText('否'),
1417
- position: 0.5,
1418
- width: 14,
1419
- height: 20
1420
- }
1421
- ], lineOptions);
1422
- const line5 = createLineElement(LineShape.elbow, [
1423
- [0, 0],
1424
- [0, 0]
1425
- ], { marker: LineMarkerType.none, connection: [0.5, 1], boundId: processElement2.id }, { marker: LineMarkerType.arrow, connection: [1, 0.5], boundId: endElement.id }, [], lineOptions);
1426
- return [startElement, processElement1, decisionElement, processElement2, endElement, line1, line2, line3, line4, line5];
1427
- };
1428
- const getAutoCompletePoints = (element) => {
1429
- const AutoCompleteMargin = (12 + RESIZE_HANDLE_DIAMETER / 2) * 2;
1430
- let rectangle = getRectangleByPoints(element.points);
1431
- rectangle = RectangleClient.inflate(rectangle, AutoCompleteMargin);
1432
- return RectangleClient.getEdgeCenterPoints(rectangle);
1433
- };
1434
- const getHitIndexOfAutoCompletePoint = (movingPoint, points) => {
1435
- return points.findIndex(point => {
1436
- const movingRectangle = RectangleClient.toRectangleClient([movingPoint]);
1437
- let rectangle = RectangleClient.toRectangleClient([point]);
1438
- rectangle = RectangleClient.inflate(rectangle, RESIZE_HANDLE_DIAMETER);
1439
- return RectangleClient.isHit(movingRectangle, rectangle);
1440
- });
1441
- };
1442
- const getDrawDefaultStrokeColor = (theme) => {
1443
- return DrawThemeColors[theme].strokeColor;
1444
- };
1445
- const getFlowchartDefaultFill = (theme) => {
1446
- return DrawThemeColors[theme].fill;
1447
- };
1448
- const getTextShapeProperty = (board, text = DefaultTextProperty.text, fontSize) => {
1449
- fontSize = fontSize ? Number(fontSize) : DEFAULT_FONT_SIZE;
1450
- const textSize = getTextSize(board, text, Infinity, { fontSize });
1451
- return {
1452
- width: textSize.width + ShapeDefaultSpace.rectangleAndText * 2,
1453
- height: textSize.height
1454
- };
1455
- };
1456
- const getDefaultGeometryPoints = (pointer, centerPoint) => {
1457
- const defaultProperty = getDefaultGeometryProperty(pointer);
1458
- return getPointsByCenterPoint(centerPoint, defaultProperty.width, defaultProperty.height);
1459
- };
1460
- const getDefaultGeometryProperty = (pointer) => {
1461
- const isFlowChart = getFlowchartPointers().includes(pointer);
1462
- if (isFlowChart) {
1463
- return getDefaultFlowchartProperty(pointer);
1464
- }
1465
- else {
1466
- return DefaultBasicShapeProperty;
1467
- }
1468
- };
1469
- const getDefaultTextPoints = (board, centerPoint, fontSize) => {
1470
- const property = getTextShapeProperty(board, DefaultTextProperty.text, fontSize);
1471
- return getPointsByCenterPoint(centerPoint, property.width, property.height);
1472
- };
1473
- const insertElement = (board, element) => {
1474
- memorizeLatestShape(board, element.shape);
1475
- Transforms.insertNode(board, element, [board.children.length]);
1476
- clearSelectedElement(board);
1477
- addSelectedElement(board, element);
1478
- BoardTransforms.updatePointerType(board, PlaitPointerType.selection);
1479
- };
1480
- const createTextElement = (board, points, text = DefaultTextProperty.text, textHeight) => {
1481
- const memorizedLatest = getMemorizedLatestByPointer(BasicShapes.text);
1482
- textHeight = textHeight ? textHeight : getRectangleByPoints(points).height;
1483
- return createGeometryElement(BasicShapes.text, points, text, memorizedLatest.geometryProperties, {
1484
- ...memorizedLatest.textProperties,
1485
- textHeight
1486
- });
1487
- };
1488
- const createDefaultGeometry = (board, points, shape) => {
1489
- const memorizedLatest = getMemorizedLatestByPointer(shape);
1490
- const textHeight = getTextShapeProperty(board, DefaultTextProperty.text, memorizedLatest.textProperties['font-size']).height;
1491
- return createGeometryElement(shape, points, '', {
1492
- strokeWidth: DefaultBasicShapeProperty.strokeWidth,
1493
- ...memorizedLatest.geometryProperties
1494
- }, { ...memorizedLatest.textProperties, textHeight });
1495
- };
1496
-
1497
- const ARROW_LENGTH = 20;
1498
- const drawLineArrow = (element, points, options) => {
1499
- const arrowG = createG();
1500
- if (PlaitLine.isSourceMark(element, LineMarkerType.none) && PlaitLine.isTargetMark(element, LineMarkerType.none)) {
1501
- return null;
1502
- }
1503
- const strokeWidth = getStrokeWidthByElement(element);
1504
- const offset = (strokeWidth * strokeWidth) / 3;
1505
- if (points.length === 1) {
1506
- points = [points[0], [points[0][0] + 0.1, points[0][1]]];
1507
- }
1508
- if (!PlaitLine.isSourceMark(element, LineMarkerType.none)) {
1509
- const source = getExtendPoint(points[0], points[1], ARROW_LENGTH + offset);
1510
- const sourceArrow = getArrow(element, { marker: element.source.marker, source, target: points[0], isSource: true }, options);
1511
- sourceArrow && arrowG.appendChild(sourceArrow);
1512
- }
1513
- if (!PlaitLine.isTargetMark(element, LineMarkerType.none)) {
1514
- const source = getExtendPoint(points[points.length - 1], points[points.length - 2], ARROW_LENGTH + offset);
1515
- const arrow = getArrow(element, { marker: element.target.marker, source, target: points[points.length - 1], isSource: false }, options);
1516
- arrow && arrowG.appendChild(arrow);
1517
- }
1518
- return arrowG;
1519
- };
1520
- const getArrow = (element, arrowOptions, options) => {
1521
- const { marker, target, source, isSource } = arrowOptions;
1522
- let targetArrow;
1523
- switch (marker) {
1524
- case LineMarkerType.openTriangle: {
1525
- targetArrow = drawOpenTriangle(element, source, target, options);
1526
- break;
1527
- }
1528
- case LineMarkerType.solidTriangle: {
1529
- targetArrow = drawSolidTriangle(source, target, options);
1530
- break;
1531
- }
1532
- case LineMarkerType.arrow: {
1533
- targetArrow = drawArrow(element, source, target, options);
1534
- break;
1535
- }
1536
- case LineMarkerType.sharpArrow: {
1537
- targetArrow = drawSharpArrow(source, target, options);
1538
- break;
1539
- }
1540
- case LineMarkerType.oneSideUp: {
1541
- targetArrow = drawOneSideArrow(source, target, isSource ? 'down' : 'up', options);
1542
- break;
1543
- }
1544
- case LineMarkerType.oneSideDown: {
1545
- targetArrow = drawOneSideArrow(source, target, isSource ? 'up' : 'down', options);
1546
- break;
1547
- }
1548
- case LineMarkerType.hollowTriangle: {
1549
- targetArrow = drawHollowTriangleArrow(source, target, options);
1550
- break;
1551
- }
1552
- case LineMarkerType.singleSlash: {
1553
- targetArrow = drawSingleSlash(source, target, isSource, options);
1554
- break;
1555
- }
1556
- }
1557
- return targetArrow;
1558
- };
1559
- const drawSharpArrow = (source, target, options) => {
1560
- const startPoint = target;
1561
- const { pointLeft, pointRight } = arrowPoints(source, target, 20);
1562
- const g = createG();
1563
- const path = createPath();
1564
- let polylinePath = `M${pointRight[0]},${pointRight[1]}A25,25,20,0,1,${pointLeft[0]},${pointLeft[1]}L${startPoint[0]},${startPoint[1]}Z`;
1565
- path.setAttribute('d', polylinePath);
1566
- path.setAttribute('stroke', `${options?.stroke}`);
1567
- path.setAttribute('stroke-width', `${options?.strokeWidth}`);
1568
- path.setAttribute('fill', `${options?.stroke}`);
1569
- g.appendChild(path);
1570
- return g;
1571
- };
1572
- const drawArrow = (element, source, target, options) => {
1573
- const directionFactor = getFactorByPoints(source, target);
1574
- const strokeWidth = getStrokeWidthByElement(element);
1575
- const endPoint = [target[0] + (strokeWidth * directionFactor.x) / 2, target[1] + (strokeWidth * directionFactor.y) / 2];
1576
- const distance = distanceBetweenPointAndPoint(...source, ...endPoint);
1577
- const middlePoint = [
1578
- endPoint[0] - (((distance * 3) / 5 + strokeWidth) / 2) * directionFactor.x,
1579
- endPoint[1] - (((distance * 3) / 5 + strokeWidth) / 2) * directionFactor.y
1580
- ];
1581
- const { pointLeft, pointRight } = arrowPoints(source, endPoint, 30);
1582
- const arrowG = drawLinearPath([pointLeft, endPoint, pointRight, middlePoint], { ...options, fill: options.stroke }, true);
1583
- const path = arrowG.querySelector('path');
1584
- path.setAttribute('stroke-linejoin', 'round');
1585
- return arrowG;
1586
- };
1587
- const drawSolidTriangle = (source, target, options) => {
1588
- const endPoint = target;
1589
- const { pointLeft, pointRight } = arrowPoints(source, endPoint, 30);
1590
- return drawLinearPath([pointLeft, endPoint, pointRight], { ...options, fill: options.stroke }, true);
1591
- };
1592
- const drawOpenTriangle = (element, source, target, options) => {
1593
- const directionFactor = getFactorByPoints(source, target);
1594
- const strokeWidth = getStrokeWidthByElement(element);
1595
- const endPoint = [target[0] + (strokeWidth * directionFactor.x) / 2, target[1] + (strokeWidth * directionFactor.y) / 2];
1596
- const { pointLeft, pointRight } = arrowPoints(source, endPoint, 40);
1597
- return drawLinearPath([pointLeft, endPoint, pointRight], options);
1598
- };
1599
- const drawOneSideArrow = (source, target, side, options) => {
1600
- const { pointLeft, pointRight } = arrowPoints(source, target, 40);
1601
- return drawLinearPath([side === 'up' ? pointRight : pointLeft, target], options);
1602
- };
1603
- const drawSingleSlash = (source, target, isSource, options) => {
1604
- const length = distanceBetweenPointAndPoint(...source, ...target);
1605
- const middlePoint = getExtendPoint(target, source, length / 2);
1606
- const angle = isSource ? 120 : 60;
1607
- const start = rotate(...source, ...middlePoint, (angle * Math.PI) / 180);
1608
- const end = rotate(...target, ...middlePoint, (angle * Math.PI) / 180);
1609
- return drawLinearPath([start, end], options);
2329
+ const processElement1 = createGeometryElement(FlowchartSymbols.process, getDefaultGeometryPoints(FlowchartSymbols.process, processPoint1), '过程', options);
2330
+ const decisionPoint = [processPoint1[0], processPoint1[1] + processProperty.height / 2 + 55 + decisionProperty.height / 2];
2331
+ const decisionElement = createGeometryElement(FlowchartSymbols.decision, getDefaultGeometryPoints(FlowchartSymbols.decision, decisionPoint), '过程', options);
2332
+ const processPoint2 = [decisionPoint[0] + decisionProperty.width / 2 + 75 + processProperty.width / 2, decisionPoint[1]];
2333
+ const processElement2 = createGeometryElement(FlowchartSymbols.process, getDefaultGeometryPoints(FlowchartSymbols.process, processPoint2), '过程', options);
2334
+ const endPoint = [decisionPoint[0], decisionPoint[1] + decisionProperty.height / 2 + 95 + terminalProperty.height / 2];
2335
+ const endElement = createGeometryElement(FlowchartSymbols.terminal, getDefaultGeometryPoints(FlowchartSymbols.terminal, endPoint), '结束', options);
2336
+ const line1 = createLineElement(LineShape.elbow, [
2337
+ [0, 0],
2338
+ [0, 0]
2339
+ ], { marker: LineMarkerType.none, connection: [0.5, 1], boundId: startElement.id }, { marker: LineMarkerType.arrow, connection: [0.5, 0], boundId: processElement1.id }, [], lineOptions);
2340
+ const line2 = createLineElement(LineShape.elbow, [
2341
+ [0, 0],
2342
+ [0, 0]
2343
+ ], { marker: LineMarkerType.none, connection: [0.5, 1], boundId: processElement1.id }, { marker: LineMarkerType.arrow, connection: [0.5, 0], boundId: decisionElement.id }, [], lineOptions);
2344
+ const line3 = createLineElement(LineShape.elbow, [
2345
+ [0, 0],
2346
+ [0, 0]
2347
+ ], { marker: LineMarkerType.none, connection: [0.5, 1], boundId: decisionElement.id }, { marker: LineMarkerType.arrow, connection: [0.5, 0], boundId: endElement.id }, [
2348
+ {
2349
+ text: buildText('是'),
2350
+ position: 0.5,
2351
+ width: 14,
2352
+ height: 20
2353
+ }
2354
+ ], lineOptions);
2355
+ const line4 = createLineElement(LineShape.elbow, [
2356
+ [0, 0],
2357
+ [0, 0]
2358
+ ], { marker: LineMarkerType.none, connection: [1, 0.5], boundId: decisionElement.id }, { marker: LineMarkerType.arrow, connection: [0, 0.5], boundId: processElement2.id }, [
2359
+ {
2360
+ text: buildText('否'),
2361
+ position: 0.5,
2362
+ width: 14,
2363
+ height: 20
2364
+ }
2365
+ ], lineOptions);
2366
+ const line5 = createLineElement(LineShape.elbow, [
2367
+ [0, 0],
2368
+ [0, 0]
2369
+ ], { marker: LineMarkerType.none, connection: [0.5, 1], boundId: processElement2.id }, { marker: LineMarkerType.arrow, connection: [1, 0.5], boundId: endElement.id }, [], lineOptions);
2370
+ return [startElement, processElement1, decisionElement, processElement2, endElement, line1, line2, line3, line4, line5];
1610
2371
  };
1611
- const drawHollowTriangleArrow = (source, target, options) => {
1612
- const { pointLeft, pointRight } = arrowPoints(source, target, 30);
1613
- return drawLinearPath([pointLeft, pointRight, target], { ...options, fill: 'white' }, true);
2372
+ const getAutoCompletePoints = (element) => {
2373
+ const AutoCompleteMargin = (12 + RESIZE_HANDLE_DIAMETER / 2) * 2;
2374
+ let rectangle = RectangleClient.getRectangleByPoints(element.points);
2375
+ rectangle = RectangleClient.inflate(rectangle, AutoCompleteMargin);
2376
+ return RectangleClient.getEdgeCenterPoints(rectangle);
1614
2377
  };
1615
-
1616
- class LineShapeGenerator extends Generator {
1617
- canDraw(element, data) {
1618
- return true;
1619
- }
1620
- draw(element, data) {
1621
- let lineG;
1622
- lineG = drawLine(this.board, element);
1623
- return lineG;
1624
- }
1625
- }
1626
-
1627
- const getHitGeometryResizeHandleRef = (board, element, point) => {
1628
- const rectangle = getRectangleByPoints(element.points);
1629
- const resizeHandleRefs = getRectangleResizeHandleRefs(rectangle, RESIZE_HANDLE_DIAMETER);
1630
- const result = resizeHandleRefs.find(resizeHandleRef => {
1631
- return RectangleClient.isHit(RectangleClient.toRectangleClient([point, point]), resizeHandleRef.rectangle);
2378
+ const getHitIndexOfAutoCompletePoint = (movingPoint, points) => {
2379
+ return points.findIndex(point => {
2380
+ const movingRectangle = RectangleClient.getRectangleByPoints([movingPoint]);
2381
+ let rectangle = RectangleClient.getRectangleByPoints([point]);
2382
+ rectangle = RectangleClient.inflate(rectangle, RESIZE_HANDLE_DIAMETER);
2383
+ return RectangleClient.isHit(movingRectangle, rectangle);
1632
2384
  });
1633
- return result;
1634
2385
  };
1635
- const getHitOutlineGeometry = (board, point, offset = 0) => {
1636
- let geometry = null;
1637
- depthFirstRecursion(board, node => {
1638
- if (PlaitDrawElement.isGeometry(node) || PlaitDrawElement.isImage(node)) {
1639
- let client = getRectangleByPoints(node.points);
1640
- client = RectangleClient.getOutlineRectangle(client, offset);
1641
- const shape = getShape(node);
1642
- const isHit = getEngine(shape).isHit(client, point);
1643
- if (isHit) {
1644
- geometry = node;
1645
- }
1646
- }
1647
- }, getIsRecursionFunc(board), true);
1648
- return geometry;
2386
+ const getDrawDefaultStrokeColor = (theme) => {
2387
+ return DrawThemeColors[theme].strokeColor;
1649
2388
  };
1650
-
1651
- const createLineElement = (shape, points, source, target, texts, options) => {
2389
+ const getFlowchartDefaultFill = (theme) => {
2390
+ return DrawThemeColors[theme].fill;
2391
+ };
2392
+ const getTextShapeProperty = (board, text = DefaultTextProperty.text, fontSize) => {
2393
+ fontSize = fontSize ? Number(fontSize) : DEFAULT_FONT_SIZE;
2394
+ const textSize = getTextSize(board, text, Infinity, { fontSize });
1652
2395
  return {
1653
- id: idCreator(),
1654
- type: 'line',
1655
- shape,
1656
- source,
1657
- texts: texts ? texts : [],
1658
- target,
1659
- opacity: 1,
1660
- points,
1661
- ...options
2396
+ width: textSize.width + ShapeDefaultSpace.rectangleAndText * 2,
2397
+ height: textSize.height
1662
2398
  };
1663
2399
  };
1664
- const getLinePoints = (board, element) => {
1665
- switch (element.shape) {
1666
- case LineShape.elbow: {
1667
- return getElbowPoints(board, element);
1668
- }
1669
- case LineShape.curve: {
1670
- return getCurvePoints(board, element);
1671
- }
1672
- default: {
1673
- const points = PlaitLine.getPoints(board, element);
1674
- const handleRefPair = getLineHandleRefPair(board, element);
1675
- points[0] = handleRefPair.source.point;
1676
- points[points.length - 1] = handleRefPair.target.point;
1677
- return points;
1678
- }
1679
- }
2400
+ const getDefaultGeometryPoints = (pointer, centerPoint) => {
2401
+ const property = getDefaultGeometryProperty(pointer);
2402
+ return RectangleClient.getPoints(RectangleClient.getRectangleByCenterPoint(centerPoint, property.width, property.height));
1680
2403
  };
1681
- const getLineHandleRefPair = (board, element) => {
1682
- const strokeWidth = getStrokeWidthByElement(element);
1683
- const sourceBoundElement = element.source.boundId ? getElementById(board, element.source.boundId) : undefined;
1684
- const targetBoundElement = element.target.boundId ? getElementById(board, element.target.boundId) : undefined;
1685
- let sourcePoint = sourceBoundElement ? getConnectionPoint(sourceBoundElement, element.source.connection) : element.points[0];
1686
- let targetPoint = targetBoundElement
1687
- ? getConnectionPoint(targetBoundElement, element.target.connection)
1688
- : element.points[element.points.length - 1];
1689
- let sourceDirection = getDirectionByVector([targetPoint[0] - sourcePoint[0], targetPoint[1] - sourcePoint[1]]) || Direction.right;
1690
- let targetDirection = getOppositeDirection(sourceDirection);
1691
- const sourceFactor = getDirectionFactor(sourceDirection);
1692
- const targetFactor = getDirectionFactor(targetDirection);
1693
- const sourceHandleRef = {
1694
- key: LineHandleKey.source,
1695
- point: sourcePoint,
1696
- direction: sourceDirection,
1697
- vector: [sourceFactor.x, sourceFactor.y]
1698
- };
1699
- const targetHandleRef = {
1700
- key: LineHandleKey.target,
1701
- point: targetPoint,
1702
- direction: targetDirection,
1703
- vector: [targetFactor.x, targetFactor.y]
1704
- };
1705
- if (sourceBoundElement) {
1706
- const connectionOffset = PlaitLine.isSourceMarkOrTargetMark(element, LineMarkerType.none, LineHandleKey.source) ? 0 : strokeWidth;
1707
- const sourceVector = getVectorByConnection(sourceBoundElement, element.source.connection);
1708
- const direction = getDirectionByVector(sourceVector);
1709
- sourceDirection = direction ? direction : sourceDirection;
1710
- sourcePoint = getConnectionPoint(sourceBoundElement, element.source.connection, sourceDirection, connectionOffset);
1711
- sourceHandleRef.boundElement = sourceBoundElement;
1712
- sourceHandleRef.direction = sourceDirection;
1713
- sourceHandleRef.point = sourcePoint;
1714
- sourceHandleRef.vector = sourceVector;
2404
+ const getDefaultGeometryProperty = (pointer) => {
2405
+ const isFlowChart = getFlowchartPointers().includes(pointer);
2406
+ if (isFlowChart) {
2407
+ return getDefaultFlowchartProperty(pointer);
1715
2408
  }
1716
- if (targetBoundElement) {
1717
- const connectionOffset = PlaitLine.isSourceMarkOrTargetMark(element, LineMarkerType.none, LineHandleKey.target) ? 0 : strokeWidth;
1718
- const targetVector = getVectorByConnection(targetBoundElement, element.target.connection);
1719
- const direction = getDirectionByVector(targetVector);
1720
- targetDirection = direction ? direction : targetDirection;
1721
- targetPoint = getConnectionPoint(targetBoundElement, element.target.connection, targetDirection, connectionOffset);
1722
- targetHandleRef.boundElement = targetBoundElement;
1723
- targetHandleRef.direction = targetDirection;
1724
- targetHandleRef.point = targetPoint;
1725
- targetHandleRef.vector = targetVector;
2409
+ else {
2410
+ return DefaultBasicShapeProperty;
1726
2411
  }
1727
- return { source: sourceHandleRef, target: targetHandleRef };
1728
2412
  };
1729
- const createFakeElement = (startPoint, vector) => {
1730
- const point = getPointByVector(startPoint, vector, -25);
1731
- const points = getPointsByCenterPoint(point, 50, 50);
1732
- return createGeometryElement(BasicShapes.rectangle, points, '');
2413
+ const getDefaultTextPoints = (board, centerPoint, fontSize) => {
2414
+ const property = getTextShapeProperty(board, DefaultTextProperty.text, fontSize);
2415
+ return RectangleClient.getPoints(RectangleClient.getRectangleByCenterPoint(centerPoint, property.width, property.height));
1733
2416
  };
1734
- const getElbowPoints = (board, element) => {
1735
- if (element.points.length === 2) {
1736
- const handleRefPair = getLineHandleRefPair(board, element);
1737
- let sourceElement = element.source.boundId ? getElementById(board, element.source.boundId) : undefined;
1738
- let targetElement = element.target.boundId ? getElementById(board, element.target.boundId) : undefined;
1739
- let points = [];
1740
- if (!sourceElement) {
1741
- const source = handleRefPair.source;
1742
- sourceElement = createFakeElement(source.point, source.vector);
1743
- }
1744
- if (!targetElement) {
1745
- const target = handleRefPair.target;
1746
- targetElement = createFakeElement(target.point, target.vector);
1747
- }
1748
- const targetRectangle = RectangleClient.inflate(getRectangleByPoints(targetElement.points), getStrokeWidthByElement(targetElement) * 2);
1749
- const sourceRectangle = RectangleClient.inflate(getRectangleByPoints(sourceElement.points), getStrokeWidthByElement(sourceElement) * 2);
1750
- const sourcePoint = handleRefPair.source.point;
1751
- const targetPoint = handleRefPair.target.point;
1752
- const { sourceOffset, targetOffset } = reduceRouteMargin(sourceRectangle, targetRectangle);
1753
- const sourceOuterRectangle = RectangleClient.expand(sourceRectangle, sourceOffset[3], sourceOffset[0], sourceOffset[1], sourceOffset[2]);
1754
- const targetOuterRectangle = RectangleClient.expand(targetRectangle, targetOffset[3], targetOffset[0], targetOffset[1], targetOffset[2]);
1755
- const nextSourcePoint = getNextPoint(sourcePoint, sourceOuterRectangle, handleRefPair.source.direction);
1756
- const nextTargetPoint = getNextPoint(targetPoint, targetOuterRectangle, handleRefPair.target.direction);
1757
- const isIntersect = RectangleClient.isPointInRectangle(targetRectangle, sourcePoint) ||
1758
- RectangleClient.isPointInRectangle(targetOuterRectangle, nextSourcePoint) ||
1759
- RectangleClient.isPointInRectangle(sourceOuterRectangle, nextTargetPoint) ||
1760
- RectangleClient.isPointInRectangle(sourceRectangle, targetPoint);
1761
- if (!isIntersect) {
1762
- points = generateElbowLineRoute({
1763
- sourcePoint,
1764
- nextSourcePoint,
1765
- sourceRectangle,
1766
- sourceOuterRectangle,
1767
- targetPoint,
1768
- nextTargetPoint,
1769
- targetRectangle,
1770
- targetOuterRectangle
1771
- });
1772
- }
1773
- else {
1774
- points = getPoints(handleRefPair.source.point, handleRefPair.source.direction, handleRefPair.target.point, handleRefPair.target.direction, 0);
1775
- }
1776
- points = removeDuplicatePoints(points);
1777
- return points;
1778
- }
1779
- return element.points;
2417
+ const insertElement = (board, element) => {
2418
+ memorizeLatestShape(board, element.shape);
2419
+ Transforms.insertNode(board, element, [board.children.length]);
2420
+ clearSelectedElement(board);
2421
+ addSelectedElement(board, element);
2422
+ BoardTransforms.updatePointerType(board, PlaitPointerType.selection);
1780
2423
  };
1781
- const getCurvePoints = (board, element) => {
1782
- if (element.points.length === 2) {
1783
- const handleRefPair = getLineHandleRefPair(board, element);
1784
- const { source, target } = handleRefPair;
1785
- const sourceBoundElement = handleRefPair.source.boundElement;
1786
- const targetBoundElement = handleRefPair.target.boundElement;
1787
- let curvePoints = [source.point];
1788
- const sumDistance = distanceBetweenPointAndPoint(...source.point, ...target.point);
1789
- const offset = 12 + sumDistance / 3;
1790
- if (sourceBoundElement) {
1791
- curvePoints.push(getPointByVector(source.point, source.vector, offset));
1792
- }
1793
- if (targetBoundElement) {
1794
- curvePoints.push(getPointByVector(target.point, target.vector, offset));
1795
- }
1796
- const isSingleBound = (sourceBoundElement && !targetBoundElement) || (!sourceBoundElement && targetBoundElement);
1797
- if (isSingleBound) {
1798
- curvePoints.push(target.point);
1799
- const points = Q2C(curvePoints);
1800
- return pointsOnBezierCurves(points);
1801
- }
1802
- if (!sourceBoundElement && !targetBoundElement) {
1803
- curvePoints.push(getPointByVector(source.point, source.vector, offset));
1804
- curvePoints.push(getPointByVector(target.point, target.vector, offset));
1805
- }
1806
- curvePoints.push(target.point);
1807
- return pointsOnBezierCurves(curvePoints);
1808
- }
1809
- else {
1810
- let allPoints = PlaitLine.getPoints(board, element);
1811
- allPoints = removeDuplicatePoints(allPoints);
1812
- const points = catmullRomFitting(allPoints);
1813
- return pointsOnBezierCurves(points);
1814
- }
2424
+ const createTextElement = (board, points, text = DefaultTextProperty.text, textHeight) => {
2425
+ const memorizedLatest = getMemorizedLatestByPointer(BasicShapes.text);
2426
+ textHeight = textHeight ? textHeight : RectangleClient.getRectangleByPoints(points).height;
2427
+ return createGeometryElement(BasicShapes.text, points, text, memorizedLatest.geometryProperties, {
2428
+ ...memorizedLatest.textProperties,
2429
+ textHeight
2430
+ });
1815
2431
  };
1816
- const transformOpsToPoints = (ops) => {
1817
- const result = [];
1818
- for (let item of ops) {
1819
- if (item.op === 'move') {
1820
- result.push([item.data[0], item.data[1]]);
1821
- }
1822
- else {
1823
- result.push([item.data[0], item.data[1]]);
1824
- result.push([item.data[2], item.data[3]]);
1825
- result.push([item.data[4], item.data[5]]);
1826
- }
2432
+ const createDefaultGeometry = (board, points, shape) => {
2433
+ const memorizedLatest = getMemorizedLatestByPointer(shape);
2434
+ const textHeight = getTextShapeProperty(board, DefaultTextProperty.text, memorizedLatest.textProperties['font-size']).height;
2435
+ return createGeometryElement(shape, points, '', {
2436
+ strokeWidth: DefaultBasicShapeProperty.strokeWidth,
2437
+ ...memorizedLatest.geometryProperties
2438
+ }, { ...memorizedLatest.textProperties, textHeight });
2439
+ };
2440
+
2441
+ const getStrokeWidthByElement = (element) => {
2442
+ if (PlaitDrawElement.isText(element)) {
2443
+ return 0;
1827
2444
  }
1828
- return result;
2445
+ const strokeWidth = element.strokeWidth || DefaultGeometryStyle.strokeWidth;
2446
+ return strokeWidth;
1829
2447
  };
1830
- const isHitPolyLine = (pathPoints, point, strokeWidth, expand = 0) => {
1831
- const distance = distanceBetweenPointAndSegments(pathPoints, point);
1832
- return distance <= strokeWidth + expand;
2448
+ const getStrokeColorByElement = (board, element) => {
2449
+ const defaultColor = getDrawDefaultStrokeColor(board.theme.themeColorMode);
2450
+ const strokeColor = element.strokeColor || defaultColor;
2451
+ return strokeColor;
2452
+ };
2453
+ const getFillByElement = (board, element) => {
2454
+ const defaultFill = PlaitDrawElement.isFlowchart(element)
2455
+ ? getFlowchartDefaultFill(board.theme.themeColorMode)
2456
+ : DefaultGeometryStyle.fill;
2457
+ const fill = element.fill || defaultFill;
2458
+ return fill;
1833
2459
  };
1834
- const getHitLineTextIndex = (board, element, point) => {
1835
- const texts = element.texts;
1836
- if (!texts.length)
1837
- return -1;
1838
- const points = getLinePoints(board, element);
1839
- return texts.findIndex(text => {
1840
- const center = getPointOnPolyline(points, text.position);
1841
- const rectangle = {
1842
- x: center[0] - text.width / 2,
1843
- y: center[1] - text.height / 2,
1844
- width: text.width,
1845
- height: text.height
1846
- };
1847
- return RectangleClient.isHit(rectangle, RectangleClient.toRectangleClient([point, point]));
1848
- });
2460
+ const getLineDashByElement = (element) => {
2461
+ return element.strokeStyle === 'dashed' ? [8, 8 + getStrokeWidthByElement(element)] : undefined;
1849
2462
  };
1850
- const isHitLineText = (board, element, point) => {
1851
- return getHitLineTextIndex(board, element, point) !== -1;
2463
+ const getStrokeStyleByElement = (element) => {
2464
+ return element.strokeStyle || StrokeStyle.solid;
1852
2465
  };
1853
- const drawLine = (board, element) => {
2466
+
2467
+ const getLineHandleRefPair = (board, element) => {
1854
2468
  const strokeWidth = getStrokeWidthByElement(element);
1855
- const strokeColor = getStrokeColorByElement(board, element);
1856
- const strokeLineDash = getLineDashByElement(element);
1857
- const options = { stroke: strokeColor, strokeWidth, strokeLineDash };
1858
- const lineG = createG();
1859
- let points = getLinePoints(board, element);
1860
- let line;
1861
- if (element.shape === LineShape.curve) {
1862
- line = PlaitBoard.getRoughSVG(board).curve(points, options);
2469
+ const sourceBoundElement = element.source.boundId ? getElementById(board, element.source.boundId) : undefined;
2470
+ const targetBoundElement = element.target.boundId ? getElementById(board, element.target.boundId) : undefined;
2471
+ let sourcePoint = sourceBoundElement ? getConnectionPoint(sourceBoundElement, element.source.connection) : element.points[0];
2472
+ let targetPoint = targetBoundElement
2473
+ ? getConnectionPoint(targetBoundElement, element.target.connection)
2474
+ : element.points[element.points.length - 1];
2475
+ let sourceDirection = getDirectionByVector([targetPoint[0] - sourcePoint[0], targetPoint[1] - sourcePoint[1]]) || Direction.right;
2476
+ let targetDirection = getOppositeDirection(sourceDirection);
2477
+ const sourceFactor = getDirectionFactor(sourceDirection);
2478
+ const targetFactor = getDirectionFactor(targetDirection);
2479
+ const sourceHandleRef = {
2480
+ key: LineHandleKey.source,
2481
+ point: sourcePoint,
2482
+ direction: sourceDirection,
2483
+ vector: [sourceFactor.x, sourceFactor.y]
2484
+ };
2485
+ const targetHandleRef = {
2486
+ key: LineHandleKey.target,
2487
+ point: targetPoint,
2488
+ direction: targetDirection,
2489
+ vector: [targetFactor.x, targetFactor.y]
2490
+ };
2491
+ if (sourceBoundElement) {
2492
+ const connectionOffset = PlaitLine.isSourceMarkOrTargetMark(element, LineMarkerType.none, LineHandleKey.source) ? 0 : strokeWidth;
2493
+ const sourceVector = getVectorByConnection(sourceBoundElement, element.source.connection);
2494
+ const direction = getDirectionByVector(sourceVector);
2495
+ sourceDirection = direction ? direction : sourceDirection;
2496
+ sourcePoint = getConnectionPoint(sourceBoundElement, element.source.connection, sourceDirection, connectionOffset);
2497
+ sourceHandleRef.boundElement = sourceBoundElement;
2498
+ sourceHandleRef.direction = sourceDirection;
2499
+ sourceHandleRef.point = sourcePoint;
2500
+ sourceHandleRef.vector = sourceVector;
1863
2501
  }
1864
- else {
1865
- line = drawLinearPath(points, options);
2502
+ if (targetBoundElement) {
2503
+ const connectionOffset = PlaitLine.isSourceMarkOrTargetMark(element, LineMarkerType.none, LineHandleKey.target) ? 0 : strokeWidth;
2504
+ const targetVector = getVectorByConnection(targetBoundElement, element.target.connection);
2505
+ const direction = getDirectionByVector(targetVector);
2506
+ targetDirection = direction ? direction : targetDirection;
2507
+ targetPoint = getConnectionPoint(targetBoundElement, element.target.connection, targetDirection, connectionOffset);
2508
+ targetHandleRef.boundElement = targetBoundElement;
2509
+ targetHandleRef.direction = targetDirection;
2510
+ targetHandleRef.point = targetPoint;
2511
+ targetHandleRef.vector = targetVector;
1866
2512
  }
1867
- const id = idCreator();
1868
- line.setAttribute('mask', `url(#${id})`);
1869
- lineG.appendChild(line);
1870
- const { mask, maskTargetFillRect } = drawMask(board, element, id);
1871
- lineG.appendChild(mask);
1872
- line.appendChild(maskTargetFillRect);
1873
- const arrow = drawLineArrow(element, points, { stroke: strokeColor, strokeWidth });
1874
- arrow && lineG.appendChild(arrow);
1875
- return lineG;
2513
+ return { source: sourceHandleRef, target: targetHandleRef };
1876
2514
  };
1877
- function drawMask(board, element, id) {
1878
- const mask = createMask();
1879
- mask.setAttribute('id', id);
1880
- const points = getLinePoints(board, element);
1881
- let rectangle = getRectangleByPoints(points);
1882
- rectangle = RectangleClient.getOutlineRectangle(rectangle, -30);
1883
- const maskFillRect = createRect(rectangle, {
1884
- fill: 'white'
1885
- });
1886
- mask.appendChild(maskFillRect);
1887
- const texts = element.texts;
1888
- texts.forEach((text, index) => {
1889
- let textRectangle = getLineTextRectangle(board, element, index);
1890
- textRectangle = RectangleClient.inflate(textRectangle, LINE_TEXT_SPACE * 2);
1891
- const rect = createRect(textRectangle, {
1892
- fill: 'black'
1893
- });
1894
- mask.appendChild(rect);
1895
- });
1896
- // open line
1897
- const maskTargetFillRect = createRect(rectangle);
1898
- maskTargetFillRect.setAttribute('opacity', '0');
1899
- maskTargetFillRect.setAttribute('fill', 'none');
1900
- return { mask, maskTargetFillRect };
1901
- }
1902
2515
  const getConnectionPoint = (geometry, connection, direction, delta) => {
1903
- const rectangle = getRectangleByPoints(geometry.points);
2516
+ const rectangle = RectangleClient.getRectangleByPoints(geometry.points);
1904
2517
  if (direction && delta) {
1905
2518
  const directionFactor = getDirectionFactor(direction);
1906
2519
  const point = RectangleClient.getConnectionPoint(rectangle, connection);
@@ -1910,69 +2523,13 @@ const getConnectionPoint = (geometry, connection, direction, delta) => {
1910
2523
  return RectangleClient.getConnectionPoint(rectangle, connection);
1911
2524
  }
1912
2525
  };
1913
- const transformPointToConnection = (board, point, hitElement) => {
1914
- let rectangle = getRectangleByPoints(hitElement.points);
1915
- rectangle = RectangleClient.inflate(rectangle, ACTIVE_STROKE_WIDTH);
1916
- let nearestPoint = getNearestPoint(hitElement, point, ACTIVE_STROKE_WIDTH);
1917
- const hitConnector = getHitConnectorPoint(nearestPoint, hitElement, rectangle);
1918
- nearestPoint = hitConnector ? hitConnector : nearestPoint;
1919
- return [(nearestPoint[0] - rectangle.x) / rectangle.width, (nearestPoint[1] - rectangle.y) / rectangle.height];
1920
- };
1921
- // export const getConnectionByPointOnRectangleEdge = (board: PlaitBoard, point: Point, hitElement: PlaitShape): Point => {
1922
- // let rectangle = getRectangleByPoints(hitElement.points);
1923
- // const hitConnector = getHitConnectorPoint(point, hitElement, rectangle);
1924
- // const newPoint = hitConnector ?? point;
1925
- // return [(newPoint[0] - rectangle.x) / rectangle.width, (newPoint[1] - rectangle.y) / rectangle.height];
1926
- // };
1927
- const getHitConnectorPoint = (movingPoint, hitElement, rectangle) => {
1928
- const shape = getShape(hitElement);
1929
- const connector = getEngine(shape).getConnectorPoints(rectangle);
1930
- const points = getPointsByCenterPoint(movingPoint, 10, 10);
1931
- const pointRectangle = getRectangleByPoints(points);
1932
- return connector.find(point => {
1933
- return RectangleClient.isHit(pointRectangle, RectangleClient.toRectangleClient([point, point]));
1934
- });
1935
- };
1936
- const getLineTextRectangle = (board, element, index) => {
1937
- const text = element.texts[index];
1938
- const elbowPoints = getLinePoints(board, element);
1939
- const point = getPointOnPolyline(elbowPoints, text.position);
1940
- return {
1941
- x: point[0] - text.width / 2,
1942
- y: point[1] - text.height / 2,
1943
- width: text.width,
1944
- height: text.height
1945
- };
1946
- };
1947
- const getBoardLines = (board) => {
1948
- return findElements(board, {
1949
- match: (element) => PlaitDrawElement.isLine(element),
1950
- recursion: (element) => PlaitDrawElement.isDrawElement(element)
1951
- });
1952
- };
1953
- // quadratic Bezier to cubic Bezier
1954
- const Q2C = (points) => {
1955
- const result = [];
1956
- const numSegments = points.length / 3;
1957
- for (let i = 0; i < numSegments; i++) {
1958
- const start = points[i];
1959
- const qControl = points[i + 1];
1960
- const end = points[i + 2];
1961
- const startDistance = distanceBetweenPointAndPoint(...start, ...qControl);
1962
- const endDistance = distanceBetweenPointAndPoint(...end, ...qControl);
1963
- const cControl1 = getExtendPoint(start, qControl, (startDistance * 2) / 3);
1964
- const cControl2 = getExtendPoint(end, qControl, (endDistance * 2) / 3);
1965
- result.push(start, cControl1, cControl2, end);
1966
- }
1967
- return result;
1968
- };
1969
2526
  const getVectorByConnection = (boundElement, connection) => {
1970
- const rectangle = getRectangleByPoints(boundElement.points);
2527
+ const rectangle = RectangleClient.getRectangleByPoints(boundElement.points);
1971
2528
  const shape = getShape(boundElement);
1972
2529
  const engine = getEngine(shape);
1973
2530
  let vector = [0, 0];
1974
2531
  const direction = getDirectionByPointOfRectangle(connection);
1975
- if (direction) {
2532
+ if (direction && boundElement.shape !== BasicShapes.ellipse) {
1976
2533
  const factor = getDirectionFactor(direction);
1977
2534
  return [factor.x, factor.y];
1978
2535
  }
@@ -1991,120 +2548,25 @@ const getVectorByConnection = (boundElement, connection) => {
1991
2548
  }
1992
2549
  return vector;
1993
2550
  };
1994
- const alignPoints = (basePoint, movingPoint) => {
1995
- const offset = 3;
1996
- const newPoint = [...movingPoint];
1997
- if (Point.isVerticalAlign(newPoint, basePoint, offset)) {
1998
- newPoint[0] = basePoint[0];
1999
- }
2000
- if (Point.isHorizontalAlign(newPoint, basePoint, offset)) {
2001
- newPoint[1] = basePoint[1];
2002
- }
2003
- return newPoint;
2004
- };
2005
- const handleLineCreating = (board, lineShape, startPoint, movingPoint, sourceElement, lineShapeG) => {
2006
- const hitElement = getHitOutlineGeometry(board, movingPoint, REACTION_MARGIN);
2007
- const targetConnection = hitElement ? transformPointToConnection(board, movingPoint, hitElement) : undefined;
2008
- const connection = sourceElement ? transformPointToConnection(board, startPoint, sourceElement) : undefined;
2009
- const targetBoundId = hitElement ? hitElement.id : undefined;
2010
- const lineGenerator = new LineShapeGenerator(board);
2011
- const memorizedLatest = getLineMemorizedLatest();
2012
- let sourceMarker, targetMarker;
2013
- sourceMarker = memorizedLatest.source;
2014
- targetMarker = memorizedLatest.target;
2015
- sourceMarker && delete memorizedLatest.source;
2016
- targetMarker && delete memorizedLatest.target;
2017
- const temporaryLineElement = createLineElement(lineShape, [startPoint, movingPoint], { marker: sourceMarker || LineMarkerType.none, connection: connection, boundId: sourceElement?.id }, { marker: targetMarker || LineMarkerType.arrow, connection: targetConnection, boundId: targetBoundId }, [], {
2018
- strokeWidth: DefaultLineStyle.strokeWidth,
2019
- ...memorizedLatest
2020
- });
2021
- const linePoints = getLinePoints(board, temporaryLineElement);
2022
- const otherPoint = linePoints[0];
2023
- temporaryLineElement.points[1] = alignPoints(otherPoint, movingPoint);
2024
- lineGenerator.processDrawing(temporaryLineElement, lineShapeG);
2025
- PlaitBoard.getElementActiveHost(board).append(lineShapeG);
2026
- return temporaryLineElement;
2027
- };
2028
-
2029
- const getSelectedDrawElements = (board) => {
2030
- const selectedElements = getSelectedElements(board).filter(value => PlaitDrawElement.isDrawElement(value));
2031
- return selectedElements;
2032
- };
2033
- const getSelectedGeometryElements = (board) => {
2034
- const selectedElements = getSelectedElements(board).filter(value => PlaitDrawElement.isGeometry(value));
2035
- return selectedElements;
2036
- };
2037
- const getSelectedLineElements = (board) => {
2038
- const selectedElements = getSelectedElements(board).filter(value => PlaitDrawElement.isLine(value));
2039
- return selectedElements;
2040
- };
2041
- const getSelectedImageElements = (board) => {
2042
- const selectedElements = getSelectedElements(board).filter(value => PlaitDrawElement.isImage(value));
2043
- return selectedElements;
2044
- };
2045
-
2046
- const isTextExceedingBounds = (geometry) => {
2047
- const client = getRectangleByPoints(geometry.points);
2048
- if (geometry.textHeight > client.height) {
2049
- return true;
2050
- }
2051
- return false;
2052
- };
2053
- const isRectangleHitDrawElement = (board, element, selection) => {
2054
- const rangeRectangle = RectangleClient.toRectangleClient([selection.anchor, selection.focus]);
2055
- if (PlaitDrawElement.isGeometry(element)) {
2056
- const client = getRectangleByPoints(element.points);
2057
- if (isTextExceedingBounds(element)) {
2058
- const textClient = getTextRectangle(element);
2059
- return RectangleClient.isHit(rangeRectangle, client) || RectangleClient.isHit(rangeRectangle, textClient);
2060
- }
2061
- return RectangleClient.isHit(rangeRectangle, client);
2062
- }
2063
- if (PlaitDrawElement.isImage(element)) {
2064
- const client = getRectangleByPoints(element.points);
2065
- return RectangleClient.isHit(rangeRectangle, client);
2066
- }
2067
- if (PlaitDrawElement.isLine(element)) {
2068
- const points = getLinePoints(board, element);
2069
- const strokeWidth = getStrokeWidthByElement(element);
2070
- const isHitText = isHitLineText(board, element, selection.focus);
2071
- const isHit = isHitPolyLine(points, selection.focus, strokeWidth, 3) || isHitText;
2072
- const isContainPolyLinePoint = points.some(point => {
2073
- return RectangleClient.isHit(rangeRectangle, RectangleClient.toRectangleClient([point, point]));
2074
- });
2075
- const isIntersect = Point.isEquals(selection.anchor, selection.focus) ? isHit : isPolylineHitRectangle(points, rangeRectangle);
2076
- return isContainPolyLinePoint || isIntersect;
2077
- }
2078
- return null;
2079
- };
2080
- const isHitDrawElement = (board, element, point) => {
2081
- if (PlaitDrawElement.isGeometry(element)) {
2082
- const fill = getFillByElement(board, element);
2083
- // when shape equals text, fill is not allowed
2084
- if (fill !== DefaultGeometryStyle.fill && fill !== TRANSPARENT && !PlaitDrawElement.isText(element)) {
2085
- return isRectangleHitDrawElement(board, element, { anchor: point, focus: point });
2086
- }
2087
- else {
2088
- // if shape equals text, only check text rectangle
2089
- if (PlaitDrawElement.isText(element)) {
2090
- const textClient = getTextRectangle(element);
2091
- let isHitText = RectangleClient.isPointInRectangle(textClient, point);
2092
- return isHitText;
2093
- }
2094
- const strokeWidth = getStrokeWidthByElement(element);
2095
- const engine = getEngine(getShape(element));
2096
- const corners = engine.getCornerPoints(getRectangleByPoints(element.points));
2097
- const isHit = isHitPolyLine(corners, point, strokeWidth, 3);
2098
- const textClient = getTextRectangle(element);
2099
- let isHitText = RectangleClient.isPointInRectangle(textClient, point);
2100
- return isHit || isHitText;
2101
- }
2102
- }
2103
- if (PlaitDrawElement.isImage(element) || PlaitDrawElement.isLine(element)) {
2104
- return isRectangleHitDrawElement(board, element, { anchor: point, focus: point });
2105
- }
2106
- return null;
2107
- };
2551
+ const getElbowLineRouteOptions = (board, element, handleRefPair) => {
2552
+ handleRefPair = handleRefPair ?? getLineHandleRefPair(board, element);
2553
+ const { sourceRectangle, targetRectangle } = getSourceAndTargetRectangle(board, element, handleRefPair);
2554
+ const { sourceOuterRectangle, targetOuterRectangle } = getSourceAndTargetOuterRectangle(sourceRectangle, targetRectangle);
2555
+ const sourcePoint = handleRefPair.source.point;
2556
+ const targetPoint = handleRefPair.target.point;
2557
+ const nextSourcePoint = getNextPoint(sourcePoint, sourceOuterRectangle, handleRefPair.source.direction);
2558
+ const nextTargetPoint = getNextPoint(targetPoint, targetOuterRectangle, handleRefPair.target.direction);
2559
+ return {
2560
+ sourcePoint,
2561
+ nextSourcePoint,
2562
+ sourceRectangle,
2563
+ sourceOuterRectangle,
2564
+ targetPoint,
2565
+ nextTargetPoint,
2566
+ targetRectangle,
2567
+ targetOuterRectangle
2568
+ };
2569
+ };
2108
2570
 
2109
2571
  var LineMarkerType;
2110
2572
  (function (LineMarkerType) {
@@ -2212,7 +2674,7 @@ class GeometryShapeGenerator extends Generator {
2212
2674
  return true;
2213
2675
  }
2214
2676
  draw(element, data) {
2215
- const rectangle = getRectangleByPoints(element.points);
2677
+ const rectangle = RectangleClient.getRectangleByPoints(element.points);
2216
2678
  const shape = element.shape;
2217
2679
  if (shape === BasicShapes.text) {
2218
2680
  return;
@@ -2261,9 +2723,6 @@ const setLineShape = (board, newProperties) => {
2261
2723
  if (element.shape === newProperties.shape) {
2262
2724
  return;
2263
2725
  }
2264
- if (newProperties.shape === LineShape.elbow) {
2265
- _properties.points = [element.points[0], element.points[element.points.length - 1]];
2266
- }
2267
2726
  const path = PlaitBoard.findPath(board, element);
2268
2727
  Transforms.setNode(board, _properties, path);
2269
2728
  });
@@ -2285,7 +2744,7 @@ const collectLineUpdatedRefsByGeometry = (board, geometry, refs) => {
2285
2744
  const object = { ...line[handle] };
2286
2745
  const linePoints = getLinePoints(board, line);
2287
2746
  const point = isSourceBound ? linePoints[0] : linePoints[linePoints.length - 1];
2288
- object.connection = transformPointToConnection(board, point, geometry);
2747
+ object.connection = getConnectionByNearestPoint(board, point, geometry);
2289
2748
  const path = PlaitBoard.findPath(board, line);
2290
2749
  const index = refs.findIndex(obj => Path.equals(obj.path, path));
2291
2750
  if (index === -1) {
@@ -2305,7 +2764,7 @@ const collectLineUpdatedRefsByGeometry = (board, geometry, refs) => {
2305
2764
  const connectLineToGeometry = (board, lineElement, handle, geometryElement) => {
2306
2765
  const linePoints = PlaitLine.getPoints(board, lineElement);
2307
2766
  const point = handle === LineHandleKey.source ? linePoints[0] : linePoints[linePoints.length - 1];
2308
- const connection = transformPointToConnection(board, point, geometryElement);
2767
+ const connection = getConnectionByNearestPoint(board, point, geometryElement);
2309
2768
  if (connection) {
2310
2769
  let source = lineElement.source;
2311
2770
  let target = lineElement.target;
@@ -2345,7 +2804,7 @@ const insertGeometryByVector = (board, point, shape, vector) => {
2345
2804
  offset = -shapeProperty.height / 2;
2346
2805
  }
2347
2806
  const vectorPoint = getPointByVector(point, vector, offset);
2348
- const points = getPointsByCenterPoint(vectorPoint, shapeProperty.width, shapeProperty.height);
2807
+ const points = RectangleClient.getPoints(RectangleClient.getRectangleByCenterPoint(vectorPoint, shapeProperty.width, shapeProperty.height));
2349
2808
  return insertGeometry(board, points, shape);
2350
2809
  }
2351
2810
  return null;
@@ -2544,7 +3003,7 @@ class GeometryComponent extends CommonPluginElement {
2544
3003
  }
2545
3004
  },
2546
3005
  getRectangle: (element) => {
2547
- return getRectangleByPoints(element.points);
3006
+ return RectangleClient.getRectangleByPoints(element.points);
2548
3007
  },
2549
3008
  hasResizeHandle: () => {
2550
3009
  const selectedElements = getSelectedElements(this.board);
@@ -2656,6 +3115,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
2656
3115
  }], ctorParameters: function () { return [{ type: i0.ViewContainerRef }, { type: i0.ChangeDetectorRef }]; } });
2657
3116
 
2658
3117
  class LineActiveGenerator extends Generator {
3118
+ constructor() {
3119
+ super(...arguments);
3120
+ this.onlySelectedCurrentLine = false;
3121
+ }
2659
3122
  canDraw(element, data) {
2660
3123
  if (data.selected) {
2661
3124
  return true;
@@ -2667,82 +3130,59 @@ class LineActiveGenerator extends Generator {
2667
3130
  draw(element, data) {
2668
3131
  const activeG = createG();
2669
3132
  const selectedElements = getSelectedElements(this.board);
2670
- const isSingleSelection = selectedElements.length === 1;
2671
- if (isSingleSelection) {
3133
+ this.onlySelectedCurrentLine = selectedElements.length === 1;
3134
+ if (this.onlySelectedCurrentLine) {
2672
3135
  activeG.classList.add('active');
2673
3136
  activeG.classList.add('line-handle');
2674
3137
  const points = PlaitLine.getPoints(this.board, element);
2675
- points.forEach(point => {
2676
- const circle = drawCircle(PlaitBoard.getRoughSVG(this.board), point, RESIZE_HANDLE_DIAMETER, {
2677
- stroke: '#999999',
2678
- strokeWidth: 1,
2679
- fill: '#FFF',
2680
- fillStyle: 'solid'
2681
- });
2682
- activeG.appendChild(circle);
3138
+ let updatePoints = [...points];
3139
+ let elbowNextRenderPoints = [];
3140
+ if (element.shape === LineShape.elbow) {
3141
+ updatePoints = points.slice(0, 1).concat(points.slice(-1));
3142
+ elbowNextRenderPoints = getNextRenderPoints(this.board, element, data.linePoints);
3143
+ }
3144
+ updatePoints.forEach(point => {
3145
+ const updateHandle = drawPrimaryHandle(this.board, point);
3146
+ activeG.appendChild(updateHandle);
2683
3147
  });
2684
- getMiddlePoints(this.board, element).forEach(point => {
2685
- const circle = drawCircle(PlaitBoard.getRoughSVG(this.board), point, RESIZE_HANDLE_DIAMETER, {
2686
- stroke: '#FFFFFF80',
2687
- strokeWidth: 1,
2688
- fill: `${PRIMARY_COLOR}80`,
2689
- fillStyle: 'solid'
2690
- });
3148
+ const middlePoints = getMiddlePoints(this.board, element);
3149
+ for (let i = 0; i < middlePoints.length; i++) {
3150
+ const point = middlePoints[i];
3151
+ if (element.shape === LineShape.elbow && elbowNextRenderPoints.length) {
3152
+ const handleIndex = getHitPointIndex(middlePoints, point);
3153
+ const isUpdateHandleIndex = isUpdatedHandleIndex(this.board, element, [...points], elbowNextRenderPoints, handleIndex);
3154
+ if (isUpdateHandleIndex) {
3155
+ const updateHandle = drawPrimaryHandle(this.board, point);
3156
+ activeG.appendChild(updateHandle);
3157
+ continue;
3158
+ }
3159
+ }
3160
+ const circle = drawFillPrimaryHandle(this.board, point);
2691
3161
  activeG.appendChild(circle);
2692
- });
3162
+ }
2693
3163
  }
2694
3164
  else {
2695
- const points = getLinePoints(this.board, element);
2696
- const activeRectangle = getRectangleByPoints(points);
2697
- let opacity = '0.5';
2698
- if (activeRectangle.height === 0 || activeRectangle.width === 0) {
2699
- opacity = '0.8';
2700
- }
2701
- const strokeG = drawRectangle(this.board, activeRectangle, {
2702
- stroke: PRIMARY_COLOR,
2703
- strokeWidth: DefaultGeometryActiveStyle.selectionStrokeWidth
2704
- });
2705
- strokeG.style.opacity = opacity;
2706
- activeG.appendChild(strokeG);
3165
+ const activeRectangle = this.board.getRectangle(element);
3166
+ if (activeRectangle) {
3167
+ let opacity = '0.5';
3168
+ if (activeRectangle.height === 0 || activeRectangle.width === 0) {
3169
+ opacity = '0.8';
3170
+ }
3171
+ const strokeG = drawRectangle(this.board, activeRectangle, {
3172
+ stroke: PRIMARY_COLOR,
3173
+ strokeWidth: DefaultGeometryActiveStyle.selectionStrokeWidth
3174
+ });
3175
+ strokeG.style.opacity = opacity;
3176
+ activeG.appendChild(strokeG);
3177
+ }
2707
3178
  }
2708
3179
  return activeG;
2709
3180
  }
2710
- }
2711
- function getMiddlePoints(board, element) {
2712
- const result = [];
2713
- const shape = element.shape;
2714
- const hideBuffer = 10;
2715
- if (shape === LineShape.straight) {
2716
- const points = PlaitLine.getPoints(board, element);
2717
- for (let i = 0; i < points.length - 1; i++) {
2718
- const distance = distanceBetweenPointAndPoint(...points[i], ...points[i + 1]);
2719
- if (distance < hideBuffer)
2720
- continue;
2721
- result.push([(points[i][0] + points[i + 1][0]) / 2, (points[i][1] + points[i + 1][1]) / 2]);
2722
- }
2723
- }
2724
- if (shape === LineShape.curve) {
2725
- const points = PlaitLine.getPoints(board, element);
2726
- const pointsOnBezier = getCurvePoints(board, element);
2727
- if (points.length === 2) {
2728
- const start = 0;
2729
- const endIndex = pointsOnBezier.length - 1;
2730
- const middleIndex = Math.round((start + endIndex) / 2);
2731
- result.push(pointsOnBezier[middleIndex]);
2732
- }
2733
- else {
2734
- for (let i = 0; i < points.length - 1; i++) {
2735
- const startIndex = pointsOnBezier.findIndex(point => point[0] === points[i][0] && point[1] === points[i][1]);
2736
- const endIndex = pointsOnBezier.findIndex(point => point[0] === points[i + 1][0] && point[1] === points[i + 1][1]);
2737
- const middleIndex = Math.round((startIndex + endIndex) / 2);
2738
- const distance = distanceBetweenPointAndPoint(...points[i], ...points[i + 1]);
2739
- if (distance < hideBuffer)
2740
- continue;
2741
- result.push(pointsOnBezier[middleIndex]);
2742
- }
2743
- }
3181
+ needUpdate() {
3182
+ const selectedElements = getSelectedElements(this.board);
3183
+ const onlySelectedCurrentLine = selectedElements.length === 1;
3184
+ return onlySelectedCurrentLine !== this.onlySelectedCurrentLine;
2744
3185
  }
2745
- return result;
2746
3186
  }
2747
3187
 
2748
3188
  class LineComponent extends CommonPluginElement {
@@ -2761,10 +3201,29 @@ class LineComponent extends CommonPluginElement {
2761
3201
  ngOnInit() {
2762
3202
  this.initializeGenerator();
2763
3203
  this.shapeGenerator.processDrawing(this.element, this.g);
2764
- this.activeGenerator.processDrawing(this.element, PlaitBoard.getElementActiveHost(this.board), { selected: this.selected });
3204
+ const linePoints = getLinePoints(this.board, this.element);
3205
+ this.activeGenerator.processDrawing(this.element, PlaitBoard.getElementActiveHost(this.board), {
3206
+ selected: this.selected,
3207
+ linePoints
3208
+ });
2765
3209
  super.ngOnInit();
2766
3210
  this.boundedElements = this.getBoundedElements();
2767
3211
  this.drawText();
3212
+ // const points = this.element.points;
3213
+ // points.forEach((p, index) => {
3214
+ // if (index === 0) {
3215
+ // return;
3216
+ // }
3217
+ // if (index === points.length - 1) {
3218
+ // return;
3219
+ // }
3220
+ // const dataPointG = PlaitBoard.getRoughSVG(this.board).circle(p[0], p[1], 8 * index, {
3221
+ // stroke: '#f08c02',
3222
+ // fill: '#f08c02',
3223
+ // fillStyle: 'solid'
3224
+ // });
3225
+ // PlaitBoard.getElementActiveHost(this.board).append(dataPointG);
3226
+ // });
2768
3227
  }
2769
3228
  getBoundedElements() {
2770
3229
  const boundedElements = {};
@@ -2787,21 +3246,31 @@ class LineComponent extends CommonPluginElement {
2787
3246
  const isBoundedElementsChanged = boundedElements.source !== this.boundedElements.source || boundedElements.target !== this.boundedElements.target;
2788
3247
  this.boundedElements = boundedElements;
2789
3248
  const isChangeTheme = this.board.operations.find(op => op.type === 'set_theme');
3249
+ const linePoints = getLinePoints(this.board, this.element);
2790
3250
  if (value.element !== previous.element || isChangeTheme) {
2791
3251
  this.shapeGenerator.processDrawing(this.element, this.g);
2792
- this.activeGenerator.processDrawing(this.element, PlaitBoard.getElementActiveHost(this.board), { selected: this.selected });
3252
+ this.activeGenerator.processDrawing(this.element, PlaitBoard.getElementActiveHost(this.board), {
3253
+ selected: this.selected,
3254
+ linePoints
3255
+ });
2793
3256
  this.updateText(previous.element.texts, value.element.texts);
2794
3257
  this.updateTextRectangle();
2795
3258
  }
2796
3259
  else {
2797
- const hasSameSelected = value.selected === previous.selected;
2798
- if (!hasSameSelected || (value.selected && isSelectionMoving(this.board))) {
2799
- this.activeGenerator.processDrawing(this.element, PlaitBoard.getElementActiveHost(this.board), { selected: this.selected });
3260
+ const needUpdate = value.selected !== previous.selected || this.activeGenerator.needUpdate();
3261
+ if (needUpdate) {
3262
+ this.activeGenerator.processDrawing(this.element, PlaitBoard.getElementActiveHost(this.board), {
3263
+ selected: this.selected,
3264
+ linePoints
3265
+ });
2800
3266
  }
2801
3267
  }
2802
3268
  if (isBoundedElementsChanged) {
2803
3269
  this.shapeGenerator.processDrawing(this.element, this.g);
2804
- this.activeGenerator.processDrawing(this.element, PlaitBoard.getElementActiveHost(this.board), { selected: this.selected });
3270
+ this.activeGenerator.processDrawing(this.element, PlaitBoard.getElementActiveHost(this.board), {
3271
+ selected: this.selected,
3272
+ linePoints
3273
+ });
2805
3274
  this.updateTextRectangle();
2806
3275
  return;
2807
3276
  }
@@ -2891,8 +3360,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
2891
3360
  }], ctorParameters: function () { return [{ type: i0.ViewContainerRef }, { type: i0.ChangeDetectorRef }]; } });
2892
3361
 
2893
3362
  const withDrawHotkey = (board) => {
2894
- const { keydown, dblclick } = board;
2895
- board.keydown = (event) => {
3363
+ const { keyDown, dblClick } = board;
3364
+ board.keyDown = (event) => {
2896
3365
  const selectedElements = getSelectedElements(board);
2897
3366
  const isSingleSelection = selectedElements.length === 1;
2898
3367
  const targetElement = selectedElements[0];
@@ -2906,16 +3375,16 @@ const withDrawHotkey = (board) => {
2906
3375
  PlaitElement.getComponent(targetElement).editText();
2907
3376
  return;
2908
3377
  }
2909
- keydown(event);
3378
+ keyDown(event);
2910
3379
  };
2911
- board.dblclick = (event) => {
3380
+ board.dblClick = (event) => {
2912
3381
  event.preventDefault();
2913
3382
  const geometries = getSelectedGeometryElements(board);
2914
3383
  if (!PlaitBoard.isReadonly(board) && geometries.length === 1) {
2915
3384
  const component = PlaitElement.getComponent(geometries[0]);
2916
3385
  component.editText();
2917
3386
  }
2918
- dblclick(event);
3387
+ dblClick(event);
2919
3388
  };
2920
3389
  return board;
2921
3390
  };
@@ -2938,7 +3407,7 @@ const withGeometryCreateByDrag = (board) => {
2938
3407
  const memorizedLatest = getMemorizedLatestByPointer(pointer);
2939
3408
  if (pointer === BasicShapes.text) {
2940
3409
  const property = getTextShapeProperty(board, DefaultTextProperty.text, memorizedLatest.textProperties['font-size']);
2941
- const points = getPointsByCenterPoint(movingPoint, property.width, property.height);
3410
+ const points = RectangleClient.getPoints(RectangleClient.getRectangleByCenterPoint(movingPoint, property.width, property.height));
2942
3411
  temporaryElement = createTextElement(board, points);
2943
3412
  if (!fakeCreateTextRef) {
2944
3413
  const textManage = new TextManage(board, PlaitBoard.getComponent(board).viewContainerRef, {
@@ -2990,18 +3459,18 @@ const withGeometryCreateByDrag = (board) => {
2990
3459
  return board;
2991
3460
  };
2992
3461
  const withGeometryCreateByDrawing = (board) => {
2993
- const { pointerDown, pointerMove, pointerUp, keydown, keyup } = board;
3462
+ const { pointerDown, pointerMove, pointerUp, keyDown, keyUp } = board;
2994
3463
  let start = null;
2995
3464
  let geometryShapeG = null;
2996
3465
  let temporaryElement = null;
2997
3466
  let isShift = false;
2998
- board.keydown = (event) => {
3467
+ board.keyDown = (event) => {
2999
3468
  isShift = isKeyHotkey('shift', event);
3000
- keydown(event);
3469
+ keyDown(event);
3001
3470
  };
3002
- board.keyup = (event) => {
3471
+ board.keyUp = (event) => {
3003
3472
  isShift = false;
3004
- keyup(event);
3473
+ keyUp(event);
3005
3474
  };
3006
3475
  board.pointerDown = (event) => {
3007
3476
  const geometryPointers = getGeometryPointers();
@@ -3014,7 +3483,7 @@ const withGeometryCreateByDrawing = (board) => {
3014
3483
  if (pointer === BasicShapes.text) {
3015
3484
  const memorizedLatest = getMemorizedLatestByPointer(pointer);
3016
3485
  const property = getTextShapeProperty(board, DefaultTextProperty.text, memorizedLatest.textProperties['font-size']);
3017
- const points = getPointsByCenterPoint(point, property.width, property.height);
3486
+ const points = RectangleClient.getPoints(RectangleClient.getRectangleByCenterPoint(point, property.width, property.height));
3018
3487
  const textElement = createTextElement(board, points);
3019
3488
  insertElement(board, textElement);
3020
3489
  start = null;
@@ -3041,7 +3510,7 @@ const withGeometryCreateByDrawing = (board) => {
3041
3510
  const isDrawMode = !!start;
3042
3511
  if (isDrawMode) {
3043
3512
  const targetPoint = toViewBoxPoint(board, toHostPoint(board, event.x, event.y));
3044
- const { width, height } = RectangleClient.toRectangleClient([start, targetPoint]);
3513
+ const { width, height } = RectangleClient.getRectangleByPoints([start, targetPoint]);
3045
3514
  if (Math.hypot(width, height) === 0) {
3046
3515
  const pointer = PlaitBoard.getPointer(board);
3047
3516
  if (pointer !== BasicShapes.text) {
@@ -3204,7 +3673,7 @@ const withDrawFragment = (baseBoard) => {
3204
3673
  return board;
3205
3674
  };
3206
3675
  const getBoundedLineElements = (board, plaitShapes) => {
3207
- const lines = getBoardLines(board);
3676
+ const lines = getLines(board);
3208
3677
  return lines.filter(line => plaitShapes.find(shape => PlaitLine.isBoundElementOfSource(line, shape) || PlaitLine.isBoundElementOfTarget(line, shape)));
3209
3678
  };
3210
3679
 
@@ -3256,23 +3725,504 @@ const withLineCreateByDraw = (board) => {
3256
3725
  return board;
3257
3726
  };
3258
3727
 
3259
- const withGeometryResize = (board) => {
3260
- const { keydown, keyup } = board;
3261
- let isShift = false;
3262
- board.keydown = (event) => {
3263
- isShift = isKeyHotkey('shift', event);
3264
- keydown(event);
3728
+ const ALIGN_TOLERANCE = 2;
3729
+ const EQUAL_SPACING = 10;
3730
+ const ALIGN_SPACING = 24;
3731
+ class ResizeAlignReaction {
3732
+ constructor(board, activeElements) {
3733
+ this.board = board;
3734
+ this.activeElements = activeElements;
3735
+ this.alignRectangles = this.getAlignRectangle();
3736
+ }
3737
+ getAlignRectangle() {
3738
+ const elements = findElements(this.board, {
3739
+ match: element => this.board.isAlign(element) && !this.activeElements.some(item => item.id === element.id),
3740
+ recursion: () => false,
3741
+ isReverse: false
3742
+ });
3743
+ return elements.map(item => this.board.getRectangle(item));
3744
+ }
3745
+ getAlignLineRef(resizeAlignDelta, resizeAlignOptions) {
3746
+ const { deltaX, deltaY } = resizeAlignDelta;
3747
+ const { resizeState, originPoint, handlePoint, isFromCorner, isAspectRatio, resizeOriginPoints } = resizeAlignOptions;
3748
+ const newResizeState = {
3749
+ ...resizeState,
3750
+ endPoint: [resizeState.endPoint[0] + deltaX, resizeState.endPoint[1] + deltaY]
3751
+ };
3752
+ const { xZoom, yZoom } = getResizeZoom(newResizeState, originPoint, handlePoint, isFromCorner, isAspectRatio);
3753
+ const activePoints = resizeOriginPoints.map(p => {
3754
+ return movePointByZoomAndOriginPoint(p, originPoint, xZoom, yZoom);
3755
+ });
3756
+ return {
3757
+ deltaX,
3758
+ deltaY,
3759
+ xZoom,
3760
+ yZoom,
3761
+ activePoints
3762
+ };
3763
+ }
3764
+ getEqualLineDelta(resizeAlignOptions) {
3765
+ let equalLineDelta = {
3766
+ deltaX: 0,
3767
+ deltaY: 0
3768
+ };
3769
+ const { isAspectRatio, activeRectangle } = resizeAlignOptions;
3770
+ const widthAlignRectangle = this.alignRectangles.find(item => Math.abs(item.width - activeRectangle.width) < ALIGN_TOLERANCE);
3771
+ if (widthAlignRectangle) {
3772
+ const deltaWidth = widthAlignRectangle.width - activeRectangle.width;
3773
+ equalLineDelta.deltaX = deltaWidth * resizeAlignOptions.directionFactors[0];
3774
+ if (isAspectRatio) {
3775
+ const deltaHeight = deltaWidth / (activeRectangle.width / activeRectangle.height);
3776
+ equalLineDelta.deltaY = deltaHeight * resizeAlignOptions.directionFactors[1];
3777
+ return equalLineDelta;
3778
+ }
3779
+ }
3780
+ const heightAlignRectangle = this.alignRectangles.find(item => Math.abs(item.height - activeRectangle.height) < ALIGN_TOLERANCE);
3781
+ if (heightAlignRectangle) {
3782
+ const deltaHeight = heightAlignRectangle.height - activeRectangle.height;
3783
+ equalLineDelta.deltaY = deltaHeight * resizeAlignOptions.directionFactors[1];
3784
+ if (isAspectRatio) {
3785
+ const deltaWidth = deltaHeight * (activeRectangle.width / activeRectangle.height);
3786
+ equalLineDelta.deltaX = deltaWidth * resizeAlignOptions.directionFactors[0];
3787
+ return equalLineDelta;
3788
+ }
3789
+ }
3790
+ return equalLineDelta;
3791
+ }
3792
+ drawEqualLines(activePoints, resizeAlignOptions) {
3793
+ let widthEqualPoints = [];
3794
+ let heightEqualPoints = [];
3795
+ const drawHorizontalLine = resizeAlignOptions.directionFactors[0] !== 0 || resizeAlignOptions.isAspectRatio;
3796
+ const drawVerticalLine = resizeAlignOptions.directionFactors[1] !== 0 || resizeAlignOptions.isAspectRatio;
3797
+ const activeRectangle = RectangleClient.getRectangleByPoints(activePoints);
3798
+ for (let alignRectangle of this.alignRectangles) {
3799
+ if (activeRectangle.width === alignRectangle.width && drawHorizontalLine) {
3800
+ widthEqualPoints.push(getEqualLinePoints(alignRectangle, true));
3801
+ }
3802
+ if (activeRectangle.height === alignRectangle.height && drawVerticalLine) {
3803
+ heightEqualPoints.push(getEqualLinePoints(alignRectangle, false));
3804
+ }
3805
+ }
3806
+ if (widthEqualPoints.length && drawHorizontalLine) {
3807
+ widthEqualPoints.push(getEqualLinePoints(activeRectangle, true));
3808
+ }
3809
+ if (heightEqualPoints.length && drawVerticalLine) {
3810
+ heightEqualPoints.push(getEqualLinePoints(activeRectangle, false));
3811
+ }
3812
+ const equalLinePoints = [...widthEqualPoints, ...heightEqualPoints];
3813
+ return drawEqualLines(this.board, equalLinePoints);
3814
+ }
3815
+ getAlignLineDelta(resizeAlignOptions) {
3816
+ let alignLineDelta = {
3817
+ deltaX: 0,
3818
+ deltaY: 0
3819
+ };
3820
+ const { isAspectRatio, activeRectangle, directionFactors } = resizeAlignOptions;
3821
+ const drawHorizontal = directionFactors[0] !== 0 || isAspectRatio;
3822
+ const drawVertical = directionFactors[1] !== 0 || isAspectRatio;
3823
+ if (drawHorizontal) {
3824
+ const xAlignAxis = getTripleAlignAxis(activeRectangle, true);
3825
+ const alignX = directionFactors[0] === -1 ? xAlignAxis[0] : xAlignAxis[2];
3826
+ const deltaX = getMinAlignDelta(this.alignRectangles, alignX, true);
3827
+ if (Math.abs(deltaX) < ALIGN_TOLERANCE) {
3828
+ alignLineDelta.deltaX = deltaX;
3829
+ if (alignLineDelta.deltaX !== 0 && isAspectRatio) {
3830
+ alignLineDelta.deltaY = alignLineDelta.deltaX / (activeRectangle.width / activeRectangle.height);
3831
+ return alignLineDelta;
3832
+ }
3833
+ }
3834
+ }
3835
+ if (drawVertical) {
3836
+ const yAlignAxis = getTripleAlignAxis(activeRectangle, false);
3837
+ const alignY = directionFactors[1] === -1 ? yAlignAxis[0] : yAlignAxis[2];
3838
+ const deltaY = getMinAlignDelta(this.alignRectangles, alignY, false);
3839
+ if (Math.abs(deltaY) < ALIGN_TOLERANCE) {
3840
+ alignLineDelta.deltaY = deltaY;
3841
+ if (alignLineDelta.deltaY !== 0 && isAspectRatio) {
3842
+ alignLineDelta.deltaX = alignLineDelta.deltaY * (activeRectangle.width / activeRectangle.height);
3843
+ return alignLineDelta;
3844
+ }
3845
+ }
3846
+ }
3847
+ return alignLineDelta;
3848
+ }
3849
+ drawAlignLines(activePoints, resizeAlignOptions) {
3850
+ let alignLinePoints = [];
3851
+ const activeRectangle = RectangleClient.getRectangleByPoints(activePoints);
3852
+ const alignAxisX = getTripleAlignAxis(activeRectangle, true);
3853
+ const alignAxisY = getTripleAlignAxis(activeRectangle, false);
3854
+ const alignLineRefs = [
3855
+ {
3856
+ axis: alignAxisX[0],
3857
+ isHorizontal: true,
3858
+ alignRectangles: []
3859
+ },
3860
+ {
3861
+ axis: alignAxisX[2],
3862
+ isHorizontal: true,
3863
+ alignRectangles: []
3864
+ },
3865
+ {
3866
+ axis: alignAxisY[0],
3867
+ isHorizontal: false,
3868
+ alignRectangles: []
3869
+ },
3870
+ {
3871
+ axis: alignAxisY[2],
3872
+ isHorizontal: false,
3873
+ alignRectangles: []
3874
+ }
3875
+ ];
3876
+ const setAlignLine = (axis, alignRectangle, isHorizontal) => {
3877
+ const boundingRectangle = RectangleClient.inflate(RectangleClient.getBoundingRectangle([activeRectangle, alignRectangle]), ALIGN_SPACING);
3878
+ if (isHorizontal) {
3879
+ const pointStart = [axis, boundingRectangle.y];
3880
+ const pointEnd = [axis, boundingRectangle.y + boundingRectangle.height];
3881
+ alignLinePoints.push([pointStart, pointEnd]);
3882
+ }
3883
+ else {
3884
+ const pointStart = [boundingRectangle.x, axis];
3885
+ const pointEnd = [boundingRectangle.x + boundingRectangle.width, axis];
3886
+ alignLinePoints.push([pointStart, pointEnd]);
3887
+ }
3888
+ };
3889
+ const { isAspectRatio, directionFactors } = resizeAlignOptions;
3890
+ const drawHorizontal = directionFactors[0] !== 0 || isAspectRatio;
3891
+ const drawVertical = directionFactors[1] !== 0 || isAspectRatio;
3892
+ for (let index = 0; index < this.alignRectangles.length; index++) {
3893
+ const element = this.alignRectangles[index];
3894
+ if (isAlign(alignLineRefs[0].axis, element, alignLineRefs[0].isHorizontal)) {
3895
+ alignLineRefs[0].alignRectangles.push(element);
3896
+ }
3897
+ if (isAlign(alignLineRefs[1].axis, element, alignLineRefs[1].isHorizontal)) {
3898
+ alignLineRefs[1].alignRectangles.push(element);
3899
+ }
3900
+ if (isAlign(alignLineRefs[2].axis, element, alignLineRefs[2].isHorizontal)) {
3901
+ alignLineRefs[2].alignRectangles.push(element);
3902
+ }
3903
+ if (isAlign(alignLineRefs[3].axis, element, alignLineRefs[3].isHorizontal)) {
3904
+ alignLineRefs[3].alignRectangles.push(element);
3905
+ }
3906
+ }
3907
+ if (drawHorizontal && alignLineRefs[0].alignRectangles.length) {
3908
+ const leftRectangle = alignLineRefs[0].alignRectangles.length === 1
3909
+ ? alignLineRefs[0].alignRectangles[0]
3910
+ : getNearestAlignRectangle(alignLineRefs[0].alignRectangles, activeRectangle);
3911
+ setAlignLine(alignLineRefs[0].axis, leftRectangle, alignLineRefs[0].isHorizontal);
3912
+ }
3913
+ if (drawHorizontal && alignLineRefs[1].alignRectangles.length) {
3914
+ const rightRectangle = alignLineRefs[1].alignRectangles.length === 1
3915
+ ? alignLineRefs[1].alignRectangles[0]
3916
+ : getNearestAlignRectangle(alignLineRefs[1].alignRectangles, activeRectangle);
3917
+ setAlignLine(alignLineRefs[1].axis, rightRectangle, alignLineRefs[1].isHorizontal);
3918
+ }
3919
+ if (drawVertical && alignLineRefs[2].alignRectangles.length) {
3920
+ const topRectangle = alignLineRefs[2].alignRectangles.length === 1
3921
+ ? alignLineRefs[2].alignRectangles[0]
3922
+ : getNearestAlignRectangle(alignLineRefs[2].alignRectangles, activeRectangle);
3923
+ setAlignLine(alignLineRefs[2].axis, topRectangle, alignLineRefs[2].isHorizontal);
3924
+ }
3925
+ if (drawVertical && alignLineRefs[3].alignRectangles.length) {
3926
+ const bottomRectangle = alignLineRefs[3].alignRectangles.length === 1
3927
+ ? alignLineRefs[3].alignRectangles[0]
3928
+ : getNearestAlignRectangle(alignLineRefs[3].alignRectangles, activeRectangle);
3929
+ setAlignLine(alignLineRefs[3].axis, bottomRectangle, alignLineRefs[3].isHorizontal);
3930
+ }
3931
+ return drawAlignLines(this.board, alignLinePoints);
3932
+ }
3933
+ handleResizeAlign(resizeAlignOptions) {
3934
+ const alignG = createG();
3935
+ let alignLineDelta = this.getEqualLineDelta(resizeAlignOptions);
3936
+ if (alignLineDelta.deltaX === 0 && alignLineDelta.deltaY === 0) {
3937
+ alignLineDelta = this.getAlignLineDelta(resizeAlignOptions);
3938
+ }
3939
+ const equalLineRef = this.getAlignLineRef(alignLineDelta, resizeAlignOptions);
3940
+ const equalLinesG = this.drawEqualLines(equalLineRef.activePoints, resizeAlignOptions);
3941
+ const alignLinesG = this.drawAlignLines(equalLineRef.activePoints, resizeAlignOptions);
3942
+ alignG.append(equalLinesG, alignLinesG);
3943
+ return { ...equalLineRef, alignG };
3944
+ }
3945
+ }
3946
+ function getBarPoint(point, isHorizontal) {
3947
+ return isHorizontal
3948
+ ? [
3949
+ [point[0], point[1] - 4],
3950
+ [point[0], point[1] + 4]
3951
+ ]
3952
+ : [
3953
+ [point[0] - 4, point[1]],
3954
+ [point[0] + 4, point[1]]
3955
+ ];
3956
+ }
3957
+ function getEqualLinePoints(rectangle, isHorizontal) {
3958
+ return isHorizontal
3959
+ ? [
3960
+ [rectangle.x, rectangle.y - EQUAL_SPACING],
3961
+ [rectangle.x + rectangle.width, rectangle.y - EQUAL_SPACING]
3962
+ ]
3963
+ : [
3964
+ [rectangle.x - EQUAL_SPACING, rectangle.y],
3965
+ [rectangle.x - EQUAL_SPACING, rectangle.y + rectangle.height]
3966
+ ];
3967
+ }
3968
+ function drawEqualLines(board, lines) {
3969
+ const g = createG();
3970
+ lines.forEach(line => {
3971
+ if (!line.length)
3972
+ return;
3973
+ const yAlign = PlaitBoard.getRoughSVG(board).line(line[0][0], line[0][1], line[1][0], line[1][1], {
3974
+ stroke: SELECTION_BORDER_COLOR,
3975
+ strokeWidth: 1
3976
+ });
3977
+ g.appendChild(yAlign);
3978
+ line.forEach(point => {
3979
+ const barPoint = getBarPoint(point, !!Point.isHorizontal(line[0], line[1]));
3980
+ const bar = PlaitBoard.getRoughSVG(board).line(barPoint[0][0], barPoint[0][1], barPoint[1][0], barPoint[1][1], {
3981
+ stroke: SELECTION_BORDER_COLOR,
3982
+ strokeWidth: 1
3983
+ });
3984
+ g.appendChild(bar);
3985
+ });
3986
+ });
3987
+ return g;
3988
+ }
3989
+ function drawAlignLines(board, lines) {
3990
+ const g = createG();
3991
+ lines.forEach(points => {
3992
+ if (!points.length)
3993
+ return;
3994
+ const xAlign = PlaitBoard.getRoughSVG(board).line(points[0][0], points[0][1], points[1][0], points[1][1], {
3995
+ stroke: SELECTION_BORDER_COLOR,
3996
+ strokeWidth: 1,
3997
+ strokeLineDash: [4, 4]
3998
+ });
3999
+ g.appendChild(xAlign);
4000
+ });
4001
+ return g;
4002
+ }
4003
+ const getTripleAlignAxis = (rectangle, isHorizontal) => {
4004
+ const axis = isHorizontal ? 'x' : 'y';
4005
+ const side = isHorizontal ? 'width' : 'height';
4006
+ return [rectangle[axis], rectangle[axis] + rectangle[side] / 2, rectangle[axis] + rectangle[side]];
4007
+ };
4008
+ const isAlign = (axis, rectangle, isHorizontal) => {
4009
+ const alignAxis = getTripleAlignAxis(rectangle, isHorizontal);
4010
+ return alignAxis.includes(axis);
4011
+ };
4012
+ const getClosestDelta = (axis, rectangle, isHorizontal) => {
4013
+ const alignAxis = getTripleAlignAxis(rectangle, isHorizontal);
4014
+ const deltas = alignAxis.map(item => item - axis);
4015
+ const absDeltas = deltas.map(item => Math.abs(item));
4016
+ const index = absDeltas.indexOf(Math.min(...absDeltas));
4017
+ return deltas[index];
4018
+ };
4019
+ function getMinAlignDelta(alignRectangles, axis, isHorizontal) {
4020
+ let delta = ALIGN_TOLERANCE;
4021
+ alignRectangles.forEach(item => {
4022
+ const distance = getClosestDelta(axis, item, isHorizontal);
4023
+ if (Math.abs(distance) < Math.abs(delta)) {
4024
+ delta = distance;
4025
+ }
4026
+ });
4027
+ return delta;
4028
+ }
4029
+ function getNearestAlignRectangle(alignRectangles, activeRectangle) {
4030
+ let minDistance = Infinity;
4031
+ let nearestRectangle = alignRectangles[0];
4032
+ alignRectangles.forEach(item => {
4033
+ const distance = Math.sqrt(Math.pow(activeRectangle.x - item.x, 2) + Math.pow(activeRectangle.y - item.y, 2));
4034
+ if (distance < minDistance) {
4035
+ minDistance = distance;
4036
+ nearestRectangle = item;
4037
+ }
4038
+ });
4039
+ return nearestRectangle;
4040
+ }
4041
+
4042
+ function getResizeAlignRef(board, resizeRef, resizeState, resizeOriginPointAndHandlePoint, isAspectRatio, isFromCorner) {
4043
+ const { originPoint, handlePoint } = resizeOriginPointAndHandlePoint;
4044
+ const { xZoom, yZoom } = getResizeZoom(resizeState, originPoint, handlePoint, isFromCorner, isAspectRatio);
4045
+ let activeElements;
4046
+ let resizeOriginPoints = [];
4047
+ if (Array.isArray(resizeRef.element)) {
4048
+ activeElements = resizeRef.element;
4049
+ const rectangle = getRectangleByElements(board, resizeRef.element, false);
4050
+ resizeOriginPoints = RectangleClient.getPoints(rectangle);
4051
+ }
4052
+ else {
4053
+ activeElements = [resizeRef.element];
4054
+ resizeOriginPoints = resizeRef.element.points;
4055
+ }
4056
+ const points = resizeOriginPoints.map(p => {
4057
+ return movePointByZoomAndOriginPoint(p, originPoint, xZoom, yZoom);
4058
+ });
4059
+ const activeRectangle = RectangleClient.getRectangleByPoints(points);
4060
+ const resizeAlignReaction = new ResizeAlignReaction(board, activeElements);
4061
+ const resizeHandlePoint = movePointByZoomAndOriginPoint(handlePoint, originPoint, xZoom, yZoom);
4062
+ const [x, y] = getUnitVectorByPointAndPoint(originPoint, resizeHandlePoint);
4063
+ return resizeAlignReaction.handleResizeAlign({
4064
+ resizeState,
4065
+ resizeOriginPoints,
4066
+ activeRectangle,
4067
+ originPoint,
4068
+ handlePoint,
4069
+ directionFactors: [getDirectionFactorByVectorComponent(x), getDirectionFactorByVectorComponent(y)],
4070
+ isAspectRatio,
4071
+ isFromCorner
4072
+ });
4073
+ }
4074
+
4075
+ function withDrawResize(board) {
4076
+ const { afterChange } = board;
4077
+ let alignG;
4078
+ let handleG;
4079
+ const canResize = () => {
4080
+ const elements = getSelectedElements(board);
4081
+ return elements.length > 1 && elements.every(el => PlaitDrawElement.isDrawElement(el));
3265
4082
  };
3266
- board.keyup = (event) => {
3267
- isShift = false;
3268
- keyup(event);
4083
+ const options = {
4084
+ key: 'draw-elements',
4085
+ canResize,
4086
+ hitTest: (point) => {
4087
+ const elements = getSelectedElements(board);
4088
+ const boundingRectangle = getRectangleByElements(board, elements, false);
4089
+ const handleRef = getHitRectangleResizeHandleRef(board, boundingRectangle, point);
4090
+ if (handleRef) {
4091
+ return {
4092
+ element: elements,
4093
+ rectangle: boundingRectangle,
4094
+ handle: handleRef.handle,
4095
+ cursorClass: handleRef.cursorClass
4096
+ };
4097
+ }
4098
+ return null;
4099
+ },
4100
+ onResize: (resizeRef, resizeState) => {
4101
+ alignG?.remove();
4102
+ const isFromCorner = isCornerHandle(board, resizeRef.handle);
4103
+ const isAspectRatio = resizeState.isShift || isFromCorner;
4104
+ const { originPoint, handlePoint } = getResizeOriginPointAndHandlePoint(board, resizeRef);
4105
+ const resizeAlignRef = getResizeAlignRef(board, resizeRef, resizeState, {
4106
+ originPoint,
4107
+ handlePoint
4108
+ }, isAspectRatio, isFromCorner);
4109
+ alignG = resizeAlignRef.alignG;
4110
+ PlaitBoard.getElementActiveHost(board).append(alignG);
4111
+ resizeRef.element.forEach(target => {
4112
+ const path = PlaitBoard.findPath(board, target);
4113
+ let points = target.points.map(p => {
4114
+ return movePointByZoomAndOriginPoint(p, originPoint, resizeAlignRef.xZoom, resizeAlignRef.yZoom);
4115
+ });
4116
+ if (PlaitDrawElement.isGeometry(target)) {
4117
+ const { height: textHeight } = getFirstTextManage(target).getSize();
4118
+ DrawTransforms.resizeGeometry(board, points, textHeight, path);
4119
+ }
4120
+ else if (PlaitDrawElement.isLine(target)) {
4121
+ Transforms.setNode(board, { points }, path);
4122
+ }
4123
+ else if (PlaitDrawElement.isImage(target)) {
4124
+ if (isAspectRatio) {
4125
+ Transforms.setNode(board, { points }, path);
4126
+ }
4127
+ else {
4128
+ // The image element does not follow the resize, but moves based on the center point.
4129
+ const targetRectangle = RectangleClient.getRectangleByPoints(target.points);
4130
+ const centerPoint = RectangleClient.getCenterPoint(targetRectangle);
4131
+ const newCenterPoint = movePointByZoomAndOriginPoint(centerPoint, originPoint, resizeAlignRef.xZoom, resizeAlignRef.yZoom);
4132
+ const newTargetRectangle = RectangleClient.getRectangleByCenterPoint(newCenterPoint, targetRectangle.width, targetRectangle.height);
4133
+ Transforms.setNode(board, { points: RectangleClient.getPoints(newTargetRectangle) }, path);
4134
+ }
4135
+ }
4136
+ });
4137
+ },
4138
+ afterResize: (resizeRef) => {
4139
+ alignG?.remove();
4140
+ alignG = null;
4141
+ }
4142
+ };
4143
+ withResize(board, options);
4144
+ board.afterChange = () => {
4145
+ afterChange();
4146
+ if (handleG) {
4147
+ handleG.remove();
4148
+ handleG = null;
4149
+ }
4150
+ if (canResize() && !isSelectionMoving(board)) {
4151
+ handleG = createG();
4152
+ const elements = getSelectedElements(board);
4153
+ const boundingRectangle = getRectangleByElements(board, elements, false);
4154
+ const corners = RectangleClient.getCornerPoints(boundingRectangle);
4155
+ corners.forEach((corner) => {
4156
+ const g = drawHandle(board, corner);
4157
+ handleG && handleG.append(g);
4158
+ });
4159
+ PlaitBoard.getElementActiveHost(board).append(handleG);
4160
+ }
4161
+ };
4162
+ return board;
4163
+ }
4164
+ const getResizeOriginPointAndHandlePoint = (board, resizeRef) => {
4165
+ const handleIndex = getIndexByResizeHandle(resizeRef.handle);
4166
+ const symmetricHandleIndex = getSymmetricHandleIndex(board, handleIndex);
4167
+ const originPoint = getResizeHandlePointByIndex(resizeRef.rectangle, symmetricHandleIndex);
4168
+ const handlePoint = getResizeHandlePointByIndex(resizeRef.rectangle, handleIndex);
4169
+ return {
4170
+ originPoint,
4171
+ handlePoint
3269
4172
  };
4173
+ };
4174
+ const getResizeZoom = (resizeState, resizeOriginPoint, resizeHandlePoint, isFromCorner, isAspectRatio) => {
4175
+ const startPoint = resizeState.startPoint;
4176
+ const endPoint = resizeState.endPoint;
4177
+ let xZoom = 0;
4178
+ let yZoom = 0;
4179
+ if (isFromCorner) {
4180
+ if (isAspectRatio) {
4181
+ let normalizedOffsetX = Point.getOffsetX(startPoint, endPoint);
4182
+ xZoom = normalizedOffsetX / (resizeHandlePoint[0] - resizeOriginPoint[0]);
4183
+ yZoom = xZoom;
4184
+ }
4185
+ else {
4186
+ let normalizedOffsetX = Point.getOffsetX(startPoint, endPoint);
4187
+ let normalizedOffsetY = Point.getOffsetY(startPoint, endPoint);
4188
+ xZoom = normalizedOffsetX / (resizeHandlePoint[0] - resizeOriginPoint[0]);
4189
+ yZoom = normalizedOffsetY / (resizeHandlePoint[1] - resizeOriginPoint[1]);
4190
+ }
4191
+ }
4192
+ else {
4193
+ const isHorizontal = Point.isHorizontal(resizeOriginPoint, resizeHandlePoint, 0.1) || false;
4194
+ let normalizedOffset = isHorizontal ? Point.getOffsetX(startPoint, endPoint) : Point.getOffsetY(startPoint, endPoint);
4195
+ let benchmarkOffset = isHorizontal ? resizeHandlePoint[0] - resizeOriginPoint[0] : resizeHandlePoint[1] - resizeOriginPoint[1];
4196
+ const zoom = normalizedOffset / benchmarkOffset;
4197
+ if (isAspectRatio) {
4198
+ xZoom = zoom;
4199
+ yZoom = zoom;
4200
+ }
4201
+ else {
4202
+ if (isHorizontal) {
4203
+ xZoom = zoom;
4204
+ }
4205
+ else {
4206
+ yZoom = zoom;
4207
+ }
4208
+ }
4209
+ }
4210
+ return { xZoom, yZoom };
4211
+ };
4212
+ const movePointByZoomAndOriginPoint = (p, resizeOriginPoint, xZoom, yZoom) => {
4213
+ const offsetX = (p[0] - resizeOriginPoint[0]) * xZoom;
4214
+ const offsetY = (p[1] - resizeOriginPoint[1]) * yZoom;
4215
+ return [p[0] + offsetX, p[1] + offsetY];
4216
+ };
4217
+
4218
+ const withGeometryResize = (board) => {
4219
+ let alignG;
3270
4220
  const options = {
3271
4221
  key: 'draw-geometry',
3272
4222
  canResize: () => {
3273
4223
  return true;
3274
4224
  },
3275
- detect: (point) => {
4225
+ hitTest: (point) => {
3276
4226
  const selectedElements = [...getSelectedGeometryElements(board), ...getSelectedImageElements(board)];
3277
4227
  if (selectedElements.length !== 1 || getSelectedElements(board).length !== 1) {
3278
4228
  return null;
@@ -3280,46 +4230,31 @@ const withGeometryResize = (board) => {
3280
4230
  const target = selectedElements[0];
3281
4231
  const targetComponent = PlaitElement.getComponent(selectedElements[0]);
3282
4232
  if (targetComponent.activeGenerator.hasResizeHandle) {
3283
- const handleRef = getHitGeometryResizeHandleRef(board, target, point);
4233
+ const rectangle = board.getRectangle(target);
4234
+ const handleRef = getHitRectangleResizeHandleRef(board, rectangle, point);
3284
4235
  if (handleRef) {
3285
4236
  return {
3286
4237
  element: target,
3287
4238
  handle: handleRef.handle,
3288
- cursorClass: handleRef.cursorClass
4239
+ cursorClass: handleRef.cursorClass,
4240
+ rectangle
3289
4241
  };
3290
4242
  }
3291
4243
  }
3292
4244
  return null;
3293
4245
  },
3294
4246
  onResize: (resizeRef, resizeState) => {
3295
- let points = [...resizeRef.element.points];
3296
- const rectangle = getRectangleByPoints(resizeRef.element.points);
3297
- const ratio = rectangle.height / rectangle.width;
3298
- const isCornerHandle = [ResizeHandle.nw, ResizeHandle.ne, ResizeHandle.se, ResizeHandle.sw].includes(resizeRef.handle);
3299
- points = getPointsByResizeHandle(resizeState.endTransformPoint, resizeRef.element.points, resizeRef.handle);
3300
- if ((isShift || PlaitDrawElement.isImage(resizeRef.element)) && isCornerHandle) {
3301
- const rectangle = getRectangleByPoints(points);
3302
- const factor = points[0][1] > points[1][1] ? 1 : -1;
3303
- const height = rectangle.width * ratio * factor;
3304
- points = [[resizeState.endTransformPoint[0], points[1][1] + height], points[1]];
3305
- }
3306
- if ((isShift || PlaitDrawElement.isImage(resizeRef.element)) && !isCornerHandle) {
3307
- const rectangle = getRectangleByPoints(points);
3308
- if (resizeRef.handle === ResizeHandle.n || resizeRef.handle === ResizeHandle.s) {
3309
- const newWidth = rectangle.height / ratio;
3310
- const offset = (newWidth - rectangle.width) / 2;
3311
- const newRectangle = RectangleClient.expand(rectangle, offset, 0);
3312
- const cornerPoints = RectangleClient.getCornerPoints(newRectangle);
3313
- points = [cornerPoints[0], cornerPoints[2]];
3314
- }
3315
- if (resizeRef.handle === ResizeHandle.e || resizeRef.handle === ResizeHandle.w) {
3316
- const newHeight = rectangle.width * ratio;
3317
- const offset = (newHeight - rectangle.height) / 2;
3318
- const newRectangle = RectangleClient.expand(rectangle, 0, offset);
3319
- const cornerPoints = RectangleClient.getCornerPoints(newRectangle);
3320
- points = [cornerPoints[0], cornerPoints[2]];
3321
- }
3322
- }
4247
+ alignG?.remove();
4248
+ const isFromCorner = isCornerHandle(board, resizeRef.handle);
4249
+ const isAspectRatio = resizeState.isShift || PlaitDrawElement.isImage(resizeRef.element);
4250
+ const { originPoint, handlePoint } = getResizeOriginPointAndHandlePoint(board, resizeRef);
4251
+ const resizeAlignRef = getResizeAlignRef(board, resizeRef, resizeState, {
4252
+ originPoint,
4253
+ handlePoint
4254
+ }, isAspectRatio, isFromCorner);
4255
+ alignG = resizeAlignRef.alignG;
4256
+ PlaitBoard.getElementActiveHost(board).append(alignG);
4257
+ let points = resizeAlignRef.activePoints;
3323
4258
  if (PlaitDrawElement.isGeometry(resizeRef.element)) {
3324
4259
  const { height: textHeight } = getFirstTextManage(resizeRef.element).getSize();
3325
4260
  DrawTransforms.resizeGeometry(board, points, textHeight, resizeRef.path);
@@ -3328,88 +4263,28 @@ const withGeometryResize = (board) => {
3328
4263
  points = normalizeShapePoints(points);
3329
4264
  Transforms.setNode(board, { points }, resizeRef.path);
3330
4265
  }
4266
+ },
4267
+ afterResize: (resizeRef) => {
4268
+ alignG?.remove();
4269
+ alignG = null;
3331
4270
  }
3332
4271
  };
3333
4272
  withResize(board, options);
3334
4273
  return board;
3335
4274
  };
3336
- const getPointsByResizeHandle = (movingPoint, elementPoints, handle) => {
3337
- switch (handle) {
3338
- case ResizeHandle.nw: {
3339
- return [movingPoint, elementPoints[1]];
3340
- }
3341
- case ResizeHandle.ne: {
3342
- return [movingPoint, [elementPoints[0][0], elementPoints[1][1]]];
3343
- }
3344
- case ResizeHandle.se: {
3345
- return [movingPoint, elementPoints[0]];
3346
- }
3347
- case ResizeHandle.sw: {
3348
- return [movingPoint, [elementPoints[1][0], elementPoints[0][1]]];
3349
- }
3350
- case ResizeHandle.n: {
3351
- return [[elementPoints[0][0], movingPoint[1]], elementPoints[1]];
3352
- }
3353
- case ResizeHandle.s: {
3354
- return [elementPoints[0], [elementPoints[1][0], movingPoint[1]]];
3355
- }
3356
- case ResizeHandle.w: {
3357
- return [[movingPoint[0], elementPoints[0][1]], elementPoints[1]];
3358
- }
3359
- default: {
3360
- return [elementPoints[0], [movingPoint[0], elementPoints[1][1]]];
3361
- }
3362
- }
3363
- };
3364
-
3365
- var LineResizeHandle;
3366
- (function (LineResizeHandle) {
3367
- LineResizeHandle["source"] = "source";
3368
- LineResizeHandle["target"] = "target";
3369
- LineResizeHandle["addHandle"] = "addHandle";
3370
- })(LineResizeHandle || (LineResizeHandle = {}));
3371
- const getHitLineResizeHandleRef = (board, element, point) => {
3372
- const points = PlaitLine.getPoints(board, element);
3373
- const index = getHitPointIndex(points, point);
3374
- if (index !== -1) {
3375
- if (index === 0) {
3376
- return { handle: LineResizeHandle.source, index };
3377
- }
3378
- if (index === points.length - 1) {
3379
- return { handle: LineResizeHandle.target, index };
3380
- }
3381
- return { index };
3382
- }
3383
- const middlePoints = getMiddlePoints(board, element);
3384
- const middleIndex = getHitPointIndex(middlePoints, point);
3385
- if (middleIndex !== -1) {
3386
- return { handle: LineResizeHandle.addHandle, index: middleIndex };
3387
- }
3388
- return undefined;
3389
- };
3390
- function getHitPointIndex(points, movingPoint) {
3391
- const rectangles = points.map(point => {
3392
- return {
3393
- x: point[0] - RESIZE_HANDLE_DIAMETER / 2,
3394
- y: point[1] - RESIZE_HANDLE_DIAMETER / 2,
3395
- width: RESIZE_HANDLE_DIAMETER,
3396
- height: RESIZE_HANDLE_DIAMETER
3397
- };
3398
- });
3399
- const rectangle = rectangles.find(rectangle => {
3400
- return RectangleClient.isHit(RectangleClient.toRectangleClient([movingPoint, movingPoint]), rectangle);
3401
- });
3402
- return rectangle ? rectangles.indexOf(rectangle) : -1;
3403
- }
3404
4275
 
3405
4276
  const withLineResize = (board) => {
3406
- let pointIndex = 0;
4277
+ let elbowLineIndex;
4278
+ let elbowLineDeleteCount;
4279
+ let elbowSourcePoint;
4280
+ let elbowTargetPoint;
4281
+ let elbowNextRenderPoints;
3407
4282
  const options = {
3408
4283
  key: 'draw-line',
3409
4284
  canResize: () => {
3410
4285
  return true;
3411
4286
  },
3412
- detect: (point) => {
4287
+ hitTest: (point) => {
3413
4288
  const selectedLineElements = getSelectedLineElements(board);
3414
4289
  if (selectedLineElements.length > 0) {
3415
4290
  let result = null;
@@ -3418,25 +4293,46 @@ const withLineResize = (board) => {
3418
4293
  if (handleRef) {
3419
4294
  result = {
3420
4295
  element: value,
3421
- handle: handleRef.handle
4296
+ handle: handleRef.handle,
4297
+ handleIndex: handleRef.handleIndex
3422
4298
  };
3423
- pointIndex = handleRef.handle === LineResizeHandle.addHandle ? handleRef.index + 1 : handleRef.index;
3424
4299
  }
3425
4300
  });
3426
4301
  return result;
3427
4302
  }
3428
4303
  return null;
3429
4304
  },
4305
+ beforeResize: (resizeRef) => {
4306
+ if (resizeRef.element.shape === LineShape.elbow &&
4307
+ resizeRef.handle !== LineResizeHandle.source &&
4308
+ resizeRef.handle !== LineResizeHandle.target) {
4309
+ const params = getElbowLineRouteOptions(board, resizeRef.element);
4310
+ const isIntersect = isSourceAndTargetIntersect(params);
4311
+ if (isIntersect) {
4312
+ return;
4313
+ }
4314
+ const points = [...resizeRef.element.points];
4315
+ const handleIndex = resizeRef.handleIndex;
4316
+ const pointsOnElbow = getElbowPoints(board, resizeRef.element);
4317
+ elbowSourcePoint = pointsOnElbow[0];
4318
+ elbowTargetPoint = pointsOnElbow[pointsOnElbow.length - 1];
4319
+ elbowNextRenderPoints = getNextRenderPoints(board, resizeRef.element, pointsOnElbow);
4320
+ const value = getIndexAndDeleteCountByKeyPoint(board, resizeRef.element, [...points], elbowNextRenderPoints, handleIndex);
4321
+ elbowLineIndex = value.index;
4322
+ elbowLineDeleteCount = value.deleteCount;
4323
+ }
4324
+ },
3430
4325
  onResize: (resizeRef, resizeState) => {
3431
4326
  let points = [...resizeRef.element.points];
3432
4327
  let source = { ...resizeRef.element.source };
3433
4328
  let target = { ...resizeRef.element.target };
3434
- const hitElement = getHitOutlineGeometry(board, resizeState.endTransformPoint, REACTION_MARGIN);
4329
+ let handleIndex = resizeRef.handleIndex;
4330
+ const hitElement = getHitOutlineGeometry(board, resizeState.endPoint, REACTION_MARGIN);
3435
4331
  if (resizeRef.handle === LineResizeHandle.source || resizeRef.handle === LineResizeHandle.target) {
3436
4332
  const object = resizeRef.handle === LineResizeHandle.source ? source : target;
3437
- points[pointIndex] = resizeState.endTransformPoint;
4333
+ points[handleIndex] = resizeState.endPoint;
3438
4334
  if (hitElement) {
3439
- object.connection = transformPointToConnection(board, resizeState.endTransformPoint, hitElement);
4335
+ object.connection = getConnectionByNearestPoint(board, resizeState.endPoint, hitElement);
3440
4336
  object.boundId = hitElement.id;
3441
4337
  }
3442
4338
  else {
@@ -3444,24 +4340,80 @@ const withLineResize = (board) => {
3444
4340
  object.boundId = undefined;
3445
4341
  }
3446
4342
  }
3447
- else if (resizeRef.handle === LineResizeHandle.addHandle) {
3448
- points.splice(pointIndex, 0, resizeState.endTransformPoint);
3449
- }
3450
4343
  else {
3451
- points[pointIndex] = resizeState.endTransformPoint;
4344
+ if (resizeRef.element.shape === LineShape.elbow) {
4345
+ if (elbowNextRenderPoints && elbowSourcePoint && elbowTargetPoint) {
4346
+ const resizedPreviousAndNextPoint = getResizedPreviousAndNextPoint(elbowNextRenderPoints, elbowSourcePoint, elbowTargetPoint, handleIndex);
4347
+ const startKeyPoint = elbowNextRenderPoints[handleIndex];
4348
+ const endKeyPoint = elbowNextRenderPoints[handleIndex + 1];
4349
+ const [newStartPoint, newEndPoint] = alignElbowSegment(startKeyPoint, endKeyPoint, resizeState, resizedPreviousAndNextPoint);
4350
+ let midDataPoints = [...points].slice(1, points.length - 1);
4351
+ if (elbowLineIndex !== null && elbowLineDeleteCount !== null) {
4352
+ if (hasIllegalElbowPoint(midDataPoints)) {
4353
+ midDataPoints = [newStartPoint, newEndPoint];
4354
+ }
4355
+ else {
4356
+ midDataPoints.splice(elbowLineIndex, elbowLineDeleteCount, newStartPoint, newEndPoint);
4357
+ }
4358
+ points = [elbowSourcePoint, ...midDataPoints, elbowTargetPoint];
4359
+ }
4360
+ }
4361
+ }
4362
+ else {
4363
+ if (resizeRef.handle === LineResizeHandle.addHandle) {
4364
+ points.splice(handleIndex + 1, 0, resizeState.endPoint);
4365
+ }
4366
+ else {
4367
+ points[handleIndex] = resizeState.endPoint;
4368
+ }
4369
+ }
3452
4370
  }
3453
4371
  if (!hitElement) {
4372
+ handleIndex = resizeRef.handle === LineResizeHandle.addHandle ? handleIndex + 1 : handleIndex;
3454
4373
  const drawPoints = getLinePoints(board, resizeRef.element);
3455
4374
  const newPoints = [...points];
3456
4375
  newPoints[0] = drawPoints[0];
3457
4376
  newPoints[newPoints.length - 1] = drawPoints[drawPoints.length - 1];
3458
- newPoints.forEach((point, index) => {
3459
- if (index === pointIndex)
3460
- return;
3461
- points[pointIndex] = alignPoints(point, points[pointIndex]);
3462
- });
4377
+ if (resizeRef.element.shape !== LineShape.elbow) {
4378
+ newPoints.forEach((point, index) => {
4379
+ if (index === handleIndex)
4380
+ return;
4381
+ if (points[handleIndex]) {
4382
+ points[handleIndex] = alignPoints(point, points[handleIndex]);
4383
+ }
4384
+ });
4385
+ }
3463
4386
  }
3464
4387
  DrawTransforms.resizeLine(board, { points, source, target }, resizeRef.path);
4388
+ },
4389
+ afterResize: (resizeRef) => {
4390
+ if (resizeRef.element.shape === LineShape.elbow) {
4391
+ const element = PlaitNode.get(board, resizeRef.path);
4392
+ let points = element && [...element.points];
4393
+ if (points.length > 2 && elbowNextRenderPoints && elbowSourcePoint && elbowTargetPoint) {
4394
+ const nextSourcePoint = elbowNextRenderPoints[0];
4395
+ const nextTargetPoint = elbowNextRenderPoints[elbowNextRenderPoints.length - 1];
4396
+ points.splice(0, 1, nextSourcePoint);
4397
+ points.splice(-1, 1, nextTargetPoint);
4398
+ points = simplifyOrthogonalPoints(points);
4399
+ if (Point.isEquals(points[0], nextSourcePoint)) {
4400
+ points.splice(0, 1);
4401
+ }
4402
+ if (Point.isEquals(points[points.length - 1], nextTargetPoint)) {
4403
+ points.pop();
4404
+ }
4405
+ if (points.length === 1) {
4406
+ points = [];
4407
+ }
4408
+ points = [elbowSourcePoint, ...points, elbowTargetPoint];
4409
+ DrawTransforms.resizeLine(board, { points }, resizeRef.path);
4410
+ }
4411
+ }
4412
+ elbowLineIndex = null;
4413
+ elbowLineDeleteCount = null;
4414
+ elbowSourcePoint = null;
4415
+ elbowTargetPoint = null;
4416
+ elbowNextRenderPoints = null;
3465
4417
  }
3466
4418
  };
3467
4419
  withResize(board, options);
@@ -3489,8 +4441,8 @@ const withLineBoundReaction = (board) => {
3489
4441
  const hitElement = getHitOutlineGeometry(board, movingPoint, -4);
3490
4442
  if (hitElement) {
3491
4443
  boundShapeG = drawBoundMask(board, hitElement);
3492
- let nearestPoint = getNearestPoint(hitElement, movingPoint, ACTIVE_STROKE_WIDTH);
3493
- const rectangle = getRectangleByPoints(hitElement.points);
4444
+ let nearestPoint = getNearestPoint(hitElement, movingPoint);
4445
+ const rectangle = RectangleClient.getRectangleByPoints(hitElement.points);
3494
4446
  const activeRectangle = RectangleClient.inflate(rectangle, ACTIVE_STROKE_WIDTH);
3495
4447
  const hitConnector = getHitConnectorPoint(nearestPoint, hitElement, activeRectangle);
3496
4448
  nearestPoint = hitConnector ? hitConnector : nearestPoint;
@@ -3515,8 +4467,8 @@ const withLineBoundReaction = (board) => {
3515
4467
  };
3516
4468
 
3517
4469
  const withLineText = (board) => {
3518
- const { dblclick } = board;
3519
- board.dblclick = (event) => {
4470
+ const { dblClick } = board;
4471
+ board.dblClick = (event) => {
3520
4472
  if (!PlaitBoard.isReadonly(board)) {
3521
4473
  const clickPoint = toViewBoxPoint(board, toHostPoint(board, event.x, event.y));
3522
4474
  const hitTarget = getHitElementByPoint(board, clickPoint, (element) => {
@@ -3547,7 +4499,7 @@ const withLineText = (board) => {
3547
4499
  }
3548
4500
  }
3549
4501
  }
3550
- dblclick(event);
4502
+ dblClick(event);
3551
4503
  };
3552
4504
  return board;
3553
4505
  };
@@ -3689,7 +4641,7 @@ const withLineAutoComplete = (board) => {
3689
4641
  const hitPoint = points[index];
3690
4642
  if (hitPoint) {
3691
4643
  temporaryDisableSelection(board);
3692
- startPoint = clickPoint;
4644
+ startPoint = hitPoint;
3693
4645
  sourceElement = targetElement;
3694
4646
  BoardTransforms.updatePointerType(board, LineShape.elbow);
3695
4647
  }
@@ -3703,7 +4655,15 @@ const withLineAutoComplete = (board) => {
3703
4655
  if (startPoint && sourceElement) {
3704
4656
  const distance = distanceBetweenPointAndPoint(...movingPoint, ...startPoint);
3705
4657
  if (distance > PRESS_AND_MOVE_BUFFER) {
3706
- temporaryElement = handleLineCreating(board, LineShape.elbow, startPoint, movingPoint, sourceElement, lineShapeG);
4658
+ const rectangle = RectangleClient.getRectangleByPoints(sourceElement.points);
4659
+ const shape = getShape(sourceElement);
4660
+ const engine = getEngine(shape);
4661
+ let sourcePoint = startPoint;
4662
+ if (engine.getNearestCrossingPoint) {
4663
+ const crossingPoint = engine.getNearestCrossingPoint(rectangle, startPoint);
4664
+ sourcePoint = crossingPoint;
4665
+ }
4666
+ temporaryElement = handleLineCreating(board, LineShape.elbow, sourcePoint, movingPoint, sourceElement, lineShapeG);
3707
4667
  }
3708
4668
  }
3709
4669
  pointerMove(event);
@@ -3738,7 +4698,7 @@ const withLineTextMove = (board) => {
3738
4698
  canResize: () => {
3739
4699
  return true;
3740
4700
  },
3741
- detect: (point) => {
4701
+ hitTest: (point) => {
3742
4702
  let result = null;
3743
4703
  const line = getHitElementByPoint(board, point, (element) => {
3744
4704
  return PlaitDrawElement.isLine(element);
@@ -3757,7 +4717,7 @@ const withLineTextMove = (board) => {
3757
4717
  onResize: (resizeRef, resizeState) => {
3758
4718
  const element = resizeRef.element;
3759
4719
  if (element) {
3760
- const movingPoint = resizeState.endTransformPoint;
4720
+ const movingPoint = resizeState.endPoint;
3761
4721
  const points = getLinePoints(board, element);
3762
4722
  const distance = distanceBetweenPointAndSegments(points, movingPoint);
3763
4723
  if (distance <= movableBuffer) {
@@ -3793,14 +4753,19 @@ const withDraw = (board) => {
3793
4753
  };
3794
4754
  board.getRectangle = (element) => {
3795
4755
  if (PlaitDrawElement.isGeometry(element)) {
3796
- return getRectangleByPoints(element.points);
4756
+ return RectangleClient.getRectangleByPoints(element.points);
3797
4757
  }
3798
4758
  if (PlaitDrawElement.isLine(element)) {
3799
4759
  const points = getLinePoints(board, element);
3800
- return getRectangleByPoints(points);
4760
+ const lineTextRectangles = element.texts.map((text, index) => {
4761
+ const rectangle = getLineTextRectangle(board, element, index);
4762
+ return rectangle;
4763
+ });
4764
+ const linePointsRectangle = RectangleClient.getRectangleByPoints(points);
4765
+ return RectangleClient.getBoundingRectangle([linePointsRectangle, ...lineTextRectangles]);
3801
4766
  }
3802
4767
  if (PlaitDrawElement.isImage(element)) {
3803
- return getRectangleByPoints(element.points);
4768
+ return RectangleClient.getRectangleByPoints(element.points);
3804
4769
  }
3805
4770
  return getRectangle(element);
3806
4771
  };
@@ -3860,12 +4825,12 @@ const withDraw = (board) => {
3860
4825
  });
3861
4826
  return getRelatedFragment([...elements, ...activeLines]);
3862
4827
  };
3863
- return withLineTextMove(withLineAutoCompleteReaction(withLineText(withLineBoundReaction(withLineResize(withGeometryResize(withLineCreateByDraw(withLineAutoComplete(withGeometryCreateByDrag(withGeometryCreateByDrawing(withDrawFragment(withDrawHotkey(board))))))))))));
4828
+ return withDrawResize(withLineTextMove(withLineAutoCompleteReaction(withLineText(withLineBoundReaction(withLineResize(withGeometryResize(withLineCreateByDraw(withLineAutoComplete(withGeometryCreateByDrag(withGeometryCreateByDrawing(withDrawFragment(withDrawHotkey(board)))))))))))));
3864
4829
  };
3865
4830
 
3866
4831
  /**
3867
4832
  * Generated bundle index. Do not edit.
3868
4833
  */
3869
4834
 
3870
- export { BasicShapes, DEFAULT_IMAGE_WIDTH, DefaultBasicShapeProperty, DefaultConnectorProperty, DefaultDataProperty, DefaultDecisionProperty, DefaultFlowchartProperty, DefaultFlowchartPropertyMap, DefaultGeometryActiveStyle, DefaultGeometryStyle, DefaultManualInputProperty, DefaultMergeProperty, DefaultTextProperty, DrawThemeColors, DrawTransforms, FlowchartSymbols, GeometryComponent, GeometryThreshold, LineComponent, LineHandleKey, LineMarkerType, LineShape, MemorizeKey, PlaitDrawElement, PlaitGeometry, PlaitLine, Q2C, REACTION_MARGIN, ShapeDefaultSpace, StrokeStyle, WithLineAutoCompletePluginKey, alignPoints, createDefaultFlowchart, createDefaultGeometry, createGeometryElement, createLineElement, createTextElement, drawBoundMask, drawGeometry, drawLine, getAutoCompletePoints, getBasicPointers, getBoardLines, getCenterPointsOnPolygon, getConnectionPoint, getCurvePoints, getDefaultFlowchartProperty, getDefaultGeometryPoints, getDefaultGeometryProperty, getDefaultTextPoints, getDrawDefaultStrokeColor, getEdgeOnPolygonByPoint, getElbowPoints, getFillByElement, getFlowchartDefaultFill, getFlowchartPointers, getGeometryPointers, getHitConnectorPoint, getHitIndexOfAutoCompletePoint, getHitLineTextIndex, getLineDashByElement, getLineHandleRefPair, getLineMemorizedLatest, getLinePointers, getLinePoints, getLineTextRectangle, getMemorizeKey, getMemorizedLatestByPointer, getMemorizedLatestShape, getNearestPoint, getPointsByCenterPoint, getSelectedDrawElements, getSelectedGeometryElements, getSelectedImageElements, getSelectedLineElements, getStrokeColorByElement, getStrokeStyleByElement, getStrokeWidthByElement, getTextRectangle, getTextShapeProperty, getVectorByConnection, handleLineCreating, insertElement, isHitDrawElement, isHitLineText, isHitPolyLine, isRectangleHitDrawElement, isTextExceedingBounds, memorizeLatestShape, memorizeLatestText, transformOpsToPoints, transformPointToConnection, withDraw, withLineAutoComplete };
4835
+ export { BasicShapes, DEFAULT_IMAGE_WIDTH, DefaultBasicShapeProperty, DefaultConnectorProperty, DefaultDataProperty, DefaultDecisionProperty, DefaultFlowchartProperty, DefaultFlowchartPropertyMap, DefaultGeometryActiveStyle, DefaultGeometryStyle, DefaultManualInputProperty, DefaultMergeProperty, DefaultTextProperty, DrawThemeColors, DrawTransforms, FlowchartSymbols, GeometryComponent, GeometryThreshold, LineComponent, LineHandleKey, LineMarkerType, LineShape, MemorizeKey, PlaitDrawElement, PlaitGeometry, PlaitLine, Q2C, REACTION_MARGIN, ShapeDefaultSpace, StrokeStyle, WithLineAutoCompletePluginKey, alignElbowSegment, alignPoints, createDefaultFlowchart, createDefaultGeometry, createGeometryElement, createLineElement, createTextElement, drawBoundMask, drawGeometry, drawLine, drawLineArrow, getAutoCompletePoints, getBasicPointers, getCenterPointsOnPolygon, getConnectionByNearestPoint, getConnectionPoint, getCurvePoints, getDefaultFlowchartProperty, getDefaultGeometryPoints, getDefaultGeometryProperty, getDefaultTextPoints, getDrawDefaultStrokeColor, getElbowLineRouteOptions, getElbowPoints, getFillByElement, getFlowchartDefaultFill, getFlowchartPointers, getGeometryPointers, getHitConnectorPoint, getHitIndexOfAutoCompletePoint, getIndexAndDeleteCountByKeyPoint, getLineDashByElement, getLineHandleRefPair, getLineMemorizedLatest, getLinePointers, getLinePoints, getLineTextRectangle, getLines, getMemorizeKey, getMemorizedLatestByPointer, getMemorizedLatestShape, getMidKeyPoints, getMiddlePoints, getMirrorDataPoints, getNearestPoint, getNextRenderPoints, getNextSourceAndTargetPoints, getResizedPreviousAndNextPoint, getSelectedDrawElements, getSelectedGeometryElements, getSelectedImageElements, getSelectedLineElements, getSourceAndTargetRectangle, getStrokeColorByElement, getStrokeStyleByElement, getStrokeWidthByElement, getTextRectangle, getTextShapeProperty, getVectorByConnection, handleLineCreating, hasIllegalElbowPoint, insertElement, isHitDrawElement, isHitLineText, isHitPolyLine, isRectangleHitDrawElement, isTextExceedingBounds, isUpdatedHandleIndex, memorizeLatestShape, memorizeLatestText, withDraw, withLineAutoComplete };
3871
4836
  //# sourceMappingURL=plait-draw.mjs.map