@plait/draw 0.76.0 → 0.77.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
- import { ACTIVE_STROKE_WIDTH, DEFAULT_COLOR, ThemeColorMode, PlaitElement, RectangleClient, getSelectedElements, idCreator, catmullRomFitting, PlaitBoard, createG, drawLinearPath, setStrokeLinecap, setPathStrokeLinecap, getNearestPointBetweenPointAndArc, distanceBetweenPointAndPoint, distanceBetweenPointAndSegments, HIT_DISTANCE_BUFFER, isPointInPolygon, isLineHitRectangle, rotatePointsByAngle, createDebugGenerator, rotateAntiPointsByElement, getEllipseArcCenter, Transforms, clearSelectedElement, addSelectedElement, BoardTransforms, PlaitPointerType, depthFirstRecursion, getIsRecursionFunc, SNAPPING_STROKE_WIDTH, SELECTION_BORDER_COLOR, SELECTION_FILL_COLOR, drawCircle, Point, arrowPoints, createPath, rotate, findElements, createMask, createRect, toActiveRectangleFromViewBoxRectangle, getElementById, rotatePointsByElement, PlaitNode, hasValidAngle, toViewBoxPoint, toHostPoint, Direction, rotatePoints, getRectangleByElements, getSelectionAngle, rotatedDataPoints, isAxisChangedByAngle, isSelectionMoving, drawRectangle, getRectangleByAngle, getSnapRectangles, getTripleAxis, getMinPointDelta, SNAP_TOLERANCE, drawPointSnapLines, drawSolidLines, getNearestPointBetweenPointAndSegments, isPointInEllipse, getNearestPointBetweenPointAndEllipse, getEllipseTangentSlope, getVectorFromPointAndSlope, drawRoundRectangle, isPointInRoundRectangle, getCrossingPointsBetweenEllipseAndSegment, drawLine, getNearestPointBetweenPointAndDiscreteSegments, getNearestPointBetweenPointAndSegment, Path, RgbaToHEX, SELECTION_RECTANGLE_CLASS_NAME, toActivePointFromViewBoxPoint, getHitElementByPoint, WritableClipboardOperationType, WritableClipboardType, addOrCreateClipboardContext, setAngleForG, CursorClass, toActivePoint, temporaryDisableSelection, toScreenPointFromActivePoint, PRESS_AND_MOVE_BUFFER, isMainPointer, throttleRAF, getAngleBetweenPoints, normalizeAngle, degreesToRadians, rotateElements, MERGING, ROTATE_HANDLE_CLASS_NAME, isSelectedElement, isDragging } from '@plait/core';
2
- import { DEFAULT_FILL, Alignment, WithTextPluginKey, TextManage, getMemorizedLatest, memorizeLatest, getPointOnPolyline, buildText, Generator, getStrokeLineDash, StrokeStyle, getCrossingPointsBetweenPointAndSegment, isPointOnSegment, getFirstTextEditor, sortElementsByArea, isFilled, getTextEditorsByElement, removeDuplicatePoints, generateElbowLineRoute, simplifyOrthogonalPoints, getExtendPoint, getUnitVectorByPointAndPoint, getPointByVectorComponent, RESIZE_HANDLE_DIAMETER, measureElement, DEFAULT_FONT_FAMILY, getFirstTextManage, isSourceAndTargetIntersect, getPoints, DEFAULT_ROUTE_MARGIN, normalizeShapePoints, resetPointsAfterResize, getDirectionByVector, getRectangleResizeHandleRefs, getRotatedResizeCursorClassByAngle, ROTATE_HANDLE_DISTANCE_TO_ELEMENT, ROTATE_HANDLE_SIZE, isCornerHandle, getIndexByResizeHandle, withResize, drawHandle, getSymmetricHandleIndex, getResizeHandlePointByIndex, getDirectionFactorByDirectionComponent, buildClipboardData as buildClipboardData$1, insertClipboardData as insertClipboardData$1, getDirectionByPointOfRectangle, getDirectionFactor, rotateVector, getOppositeDirection, rotateVectorAnti90, getSourceAndTargetOuterRectangle, getNextPoint, PRIMARY_COLOR, CommonElementFlavour, createActiveGenerator, hasResizeHandle, ActiveGenerator, drawPrimaryHandle, drawFillPrimaryHandle, isVirtualKey, isDelete, isSpaceHotkey, isDndMode, isDrawingMode, getElementsText, acceptImageTypes, getElementOfFocusedImage, buildImage, isResizingByCondition, getRatioByPoint, getTextManages, ImageGenerator, ResizeHandle, addRotating, removeRotating, drawRotateHandle } from '@plait/common';
3
- import { pointsOnBezierCurves } from 'points-on-curve';
1
+ import { ACTIVE_STROKE_WIDTH, DEFAULT_COLOR, ThemeColorMode, PlaitElement, RectangleClient, getSelectedElements, idCreator, createDebugGenerator, Point, createG, arrowPoints, createPath, distanceBetweenPointAndPoint, drawLinearPath, rotate, catmullRomFitting, PlaitBoard, setStrokeLinecap, findElements, createMask, createRect, setPathStrokeLinecap, getNearestPointBetweenPointAndArc, distanceBetweenPointAndSegments, HIT_DISTANCE_BUFFER, isPointInPolygon, isLineHitRectangle, rotatePointsByAngle, rotateAntiPointsByElement, getEllipseArcCenter, Transforms, clearSelectedElement, addSelectedElement, BoardTransforms, PlaitPointerType, depthFirstRecursion, getIsRecursionFunc, SNAPPING_STROKE_WIDTH, SELECTION_BORDER_COLOR, SELECTION_FILL_COLOR, drawCircle, getI18nValue, toActiveRectangleFromViewBoxRectangle, getElementById, rotatePointsByElement, PlaitNode, hasValidAngle, toViewBoxPoint, toHostPoint, Direction, rotatePoints, getRectangleByElements, getSelectionAngle, rotatedDataPoints, isAxisChangedByAngle, isSelectionMoving, drawRectangle, getRectangleByAngle, getSnapRectangles, getTripleAxis, getMinPointDelta, SNAP_TOLERANCE, drawPointSnapLines, drawSolidLines, getNearestPointBetweenPointAndSegments, isPointInEllipse, getNearestPointBetweenPointAndEllipse, getEllipseTangentSlope, getVectorFromPointAndSlope, drawRoundRectangle, isPointInRoundRectangle, getCrossingPointsBetweenEllipseAndSegment, drawLine, getNearestPointBetweenPointAndDiscreteSegments, getNearestPointBetweenPointAndSegment, Path, RgbaToHEX, SELECTION_RECTANGLE_CLASS_NAME, toActivePointFromViewBoxPoint, getHitElementByPoint, WritableClipboardOperationType, WritableClipboardType, addOrCreateClipboardContext, setAngleForG, CursorClass, toActivePoint, temporaryDisableSelection, toScreenPointFromActivePoint, PRESS_AND_MOVE_BUFFER, isMainPointer, throttleRAF, getAngleBetweenPoints, normalizeAngle, degreesToRadians, rotateElements, MERGING, ROTATE_HANDLE_CLASS_NAME, isSelectedElement, isDragging } from '@plait/core';
2
+ import { DEFAULT_FILL, Alignment, getMemorizedLatest, memorizeLatest, WithTextPluginKey, TextManage, StrokeStyle, removeDuplicatePoints, generateElbowLineRoute, simplifyOrthogonalPoints, getExtendPoint, getUnitVectorByPointAndPoint, Generator, getPointByVectorComponent, getStrokeLineDash, getPointOnPolyline, buildText, getCrossingPointsBetweenPointAndSegment, isPointOnSegment, getFirstTextEditor, sortElementsByArea, isFilled, getTextEditorsByElement, RESIZE_HANDLE_DIAMETER, measureElement, DEFAULT_FONT_FAMILY, getFirstTextManage, isSourceAndTargetIntersect, getPoints, DEFAULT_ROUTE_MARGIN, normalizeShapePoints, resetPointsAfterResize, getDirectionByVector, getRectangleResizeHandleRefs, getRotatedResizeCursorClassByAngle, ROTATE_HANDLE_DISTANCE_TO_ELEMENT, ROTATE_HANDLE_SIZE, isCornerHandle, getIndexByResizeHandle, withResize, getSymmetricHandleIndex, getResizeHandlePointByIndex, drawHandle, getDirectionFactorByDirectionComponent, buildClipboardData as buildClipboardData$1, insertClipboardData as insertClipboardData$1, getDirectionByPointOfRectangle, getDirectionFactor, rotateVector, getOppositeDirection, rotateVectorAnti90, getSourceAndTargetOuterRectangle, getNextPoint, PRIMARY_COLOR, CommonElementFlavour, createActiveGenerator, hasResizeHandle, ActiveGenerator, drawPrimaryHandle, drawFillPrimaryHandle, isVirtualKey, isDelete, isSpaceHotkey, isDndMode, isDrawingMode, getElementsText, acceptImageTypes, getElementOfFocusedImage, buildImage, isResizingByCondition, getRatioByPoint, getTextManages, ImageGenerator, ResizeHandle, addRotating, removeRotating, drawRotateHandle } from '@plait/common';
4
3
  import { TEXT_DEFAULT_HEIGHT, DEFAULT_FONT_SIZE, AlignEditor } from '@plait/text-plugins';
4
+ import { pointsOnBezierCurves } from 'points-on-curve';
5
5
  import { Editor, Node } from 'slate';
6
6
  import { isKeyHotkey } from 'is-hotkey';
7
7
 
@@ -114,6 +114,11 @@ const PlaitTableElement = {
114
114
  };
115
115
 
116
116
  const WithDrawPluginKey = 'plait-draw-plugin-key';
117
+ var DrawI18nKey;
118
+ (function (DrawI18nKey) {
119
+ DrawI18nKey["lineText"] = "line-text";
120
+ DrawI18nKey["geometryText"] = "geometry-text";
121
+ })(DrawI18nKey || (DrawI18nKey = {}));
117
122
 
118
123
  const ShapeDefaultSpace = {
119
124
  rectangleAndText: 4
@@ -461,6 +466,18 @@ const DefaultSwimlanePropertyMap = {
461
466
 
462
467
  const MIN_TEXT_WIDTH = 5;
463
468
 
469
+ const DefaultLineStyle = {
470
+ strokeWidth: 2,
471
+ strokeColor: '#000'
472
+ };
473
+ const LINE_TEXT_SPACE = 4;
474
+ const LINE_AUTO_COMPLETE_DIAMETER = 6;
475
+ const LINE_AUTO_COMPLETE_OPACITY = 0.6;
476
+ const LINE_AUTO_COMPLETE_HOVERED_OPACITY = 0.8;
477
+ const LINE_AUTO_COMPLETE_HOVERED_DIAMETER = 10;
478
+ const LINE_ALIGN_TOLERANCE = 3;
479
+ const LINE_TEXT = '文本';
480
+
464
481
  const getElementShape = (value) => {
465
482
  if (PlaitDrawElement.isImage(value)) {
466
483
  return BasicShapes.rectangle;
@@ -471,6 +488,97 @@ const getElementShape = (value) => {
471
488
  return value.shape;
472
489
  };
473
490
 
491
+ const SHAPE_MAX_LENGTH = 6;
492
+ const memorizedShape = new WeakMap();
493
+ const getMemorizeKey = (element) => {
494
+ let key = '';
495
+ switch (true) {
496
+ case PlaitDrawElement.isText(element): {
497
+ key = MemorizeKey.text;
498
+ break;
499
+ }
500
+ case PlaitDrawElement.isBasicShape(element): {
501
+ key = MemorizeKey.basicShape;
502
+ break;
503
+ }
504
+ case PlaitDrawElement.isFlowchart(element): {
505
+ key = MemorizeKey.flowchart;
506
+ break;
507
+ }
508
+ case PlaitDrawElement.isArrowLine(element): {
509
+ key = MemorizeKey.arrowLine;
510
+ break;
511
+ }
512
+ case PlaitDrawElement.isUML(element): {
513
+ key = MemorizeKey.UML;
514
+ }
515
+ }
516
+ return key;
517
+ };
518
+ const getLineMemorizedLatest = () => {
519
+ const properties = getMemorizedLatest(MemorizeKey.arrowLine);
520
+ return { ...properties } || {};
521
+ };
522
+ const getMemorizedLatestByPointer = (pointer) => {
523
+ let memorizeKey = '';
524
+ if (PlaitDrawElement.isBasicShape({ shape: pointer })) {
525
+ memorizeKey = pointer === BasicShapes.text ? MemorizeKey.text : MemorizeKey.basicShape;
526
+ }
527
+ else if (PlaitDrawElement.isUML({ shape: pointer })) {
528
+ memorizeKey = MemorizeKey.UML;
529
+ }
530
+ else {
531
+ memorizeKey = MemorizeKey.flowchart;
532
+ }
533
+ const properties = { ...getMemorizedLatest(memorizeKey) } || {};
534
+ const textProperties = { ...properties.text } || {};
535
+ delete properties.text;
536
+ return { textProperties, geometryProperties: properties };
537
+ };
538
+ const memorizeLatestText = (element, operations) => {
539
+ const memorizeKey = getMemorizeKey(element);
540
+ let textMemory = getMemorizedLatest(memorizeKey)?.text || {};
541
+ const setNodeOperation = operations.find(operation => operation.type === 'set_node');
542
+ if (setNodeOperation) {
543
+ const { properties, newProperties } = setNodeOperation;
544
+ for (const key in newProperties) {
545
+ const value = newProperties[key];
546
+ if (value == null) {
547
+ delete textMemory[key];
548
+ }
549
+ else {
550
+ textMemory[key] = value;
551
+ }
552
+ }
553
+ for (const key in properties) {
554
+ if (!newProperties.hasOwnProperty(key)) {
555
+ delete textMemory[key];
556
+ }
557
+ }
558
+ memorizeLatest(memorizeKey, 'text', textMemory);
559
+ }
560
+ };
561
+ const memorizeLatestShape = (board, shape) => {
562
+ const shapes = memorizedShape.has(board) ? memorizedShape.get(board) : [];
563
+ const shapeIndex = shapes.indexOf(shape);
564
+ if (shape === BasicShapes.text || shapeIndex === 0) {
565
+ return;
566
+ }
567
+ if (shapeIndex !== -1) {
568
+ shapes.splice(shapeIndex, 1);
569
+ }
570
+ else {
571
+ if (shapes.length === SHAPE_MAX_LENGTH) {
572
+ shapes.pop();
573
+ }
574
+ }
575
+ shapes.unshift(shape);
576
+ memorizedShape.set(board, shapes);
577
+ };
578
+ const getMemorizedLatestShape = (board) => {
579
+ return memorizedShape.get(board);
580
+ };
581
+
474
582
  // TODO: 是否可以完全基于位置定位 TextManager,实现 line 和 多文本 geometry 统一
475
583
  // 一个元素有多个文本时,单纯通过位置无法获取 TextManage,因此这里单独通过 Map 保存关键字 key 和 TextManage 的对应关系
476
584
  // 1. 单文本元素 key 就是元素的 id
@@ -602,10 +710,6 @@ const isSingleSelectTable = (board) => {
602
710
  const selectedElements = getSelectedElements(board);
603
711
  return selectedElements && selectedElements.length === 1 && PlaitDrawElement.isElementByTable(selectedElements[0]);
604
712
  };
605
- const isSingleSelectElementByTable = (board) => {
606
- const selectedElements = getSelectedElements(board);
607
- return selectedElements && selectedElements.length === 1 && PlaitDrawElement.isElementByTable(selectedElements[0]);
608
- };
609
713
  const getSelectedTableElements = (board, elements) => {
610
714
  const selectedElements = elements?.length ? elements : getSelectedElements(board);
611
715
  return selectedElements.filter(value => PlaitDrawElement.isElementByTable(value));
@@ -774,7 +878,7 @@ const createCell = (rowId, columnId, text = null) => {
774
878
  return cell;
775
879
  };
776
880
  const getSelectedTableCellsEditor = (board) => {
777
- if (isSingleSelectElementByTable(board)) {
881
+ if (isSingleSelectTable(board)) {
778
882
  const elements = getSelectedTableElements(board);
779
883
  const selectedCells = getSelectedCells(elements[0]);
780
884
  const selectedCellsEditor = selectedCells?.map(cell => {
@@ -788,1458 +892,1358 @@ const getSelectedTableCellsEditor = (board) => {
788
892
  return undefined;
789
893
  };
790
894
 
791
- const SHAPE_MAX_LENGTH = 6;
792
- const memorizedShape = new WeakMap();
793
- const getMemorizeKey = (element) => {
794
- let key = '';
795
- switch (true) {
796
- case PlaitDrawElement.isText(element): {
797
- key = MemorizeKey.text;
798
- break;
799
- }
800
- case PlaitDrawElement.isBasicShape(element): {
801
- key = MemorizeKey.basicShape;
802
- break;
803
- }
804
- case PlaitDrawElement.isFlowchart(element): {
805
- key = MemorizeKey.flowchart;
806
- break;
807
- }
808
- case PlaitDrawElement.isArrowLine(element): {
809
- key = MemorizeKey.arrowLine;
810
- break;
811
- }
812
- case PlaitDrawElement.isUML(element): {
813
- key = MemorizeKey.UML;
814
- }
815
- }
816
- return key;
895
+ const getStrokeColorByElement = (board, element) => {
896
+ const defaultColor = getDrawDefaultStrokeColor(board.theme.themeColorMode);
897
+ const strokeColor = element.strokeColor || defaultColor;
898
+ return strokeColor;
817
899
  };
818
- const getLineMemorizedLatest = () => {
819
- const properties = getMemorizedLatest(MemorizeKey.arrowLine);
820
- return { ...properties } || {};
900
+ const getFillByElement = (board, element) => {
901
+ const defaultFill = PlaitDrawElement.isFlowchart(element) && isClosedDrawElement(element)
902
+ ? getFlowchartDefaultFill(board.theme.themeColorMode)
903
+ : DefaultDrawStyle.fill;
904
+ const fill = element.fill || defaultFill;
905
+ return fill;
821
906
  };
822
- const getMemorizedLatestByPointer = (pointer) => {
823
- let memorizeKey = '';
824
- if (PlaitDrawElement.isBasicShape({ shape: pointer })) {
825
- memorizeKey = pointer === BasicShapes.text ? MemorizeKey.text : MemorizeKey.basicShape;
826
- }
827
- else if (PlaitDrawElement.isUML({ shape: pointer })) {
828
- memorizeKey = MemorizeKey.UML;
907
+ const getStrokeStyleByElement = (board, element) => {
908
+ return element.strokeStyle || StrokeStyle.solid;
909
+ };
910
+
911
+ const debugKey$4 = 'debug:plait:line-mirror';
912
+ const debugGenerator$4 = createDebugGenerator(debugKey$4);
913
+ const alignPoints = (basePoint, movingPoint) => {
914
+ const newPoint = [...movingPoint];
915
+ if (Point.isVertical(newPoint, basePoint, LINE_ALIGN_TOLERANCE)) {
916
+ newPoint[0] = basePoint[0];
829
917
  }
830
- else {
831
- memorizeKey = MemorizeKey.flowchart;
918
+ if (Point.isHorizontal(newPoint, basePoint, LINE_ALIGN_TOLERANCE)) {
919
+ newPoint[1] = basePoint[1];
832
920
  }
833
- const properties = { ...getMemorizedLatest(memorizeKey) } || {};
834
- const textProperties = { ...properties.text } || {};
835
- delete properties.text;
836
- return { textProperties, geometryProperties: properties };
921
+ return newPoint;
837
922
  };
838
- const memorizeLatestText = (element, operations) => {
839
- const memorizeKey = getMemorizeKey(element);
840
- let textMemory = getMemorizedLatest(memorizeKey)?.text || {};
841
- const setNodeOperation = operations.find(operation => operation.type === 'set_node');
842
- if (setNodeOperation) {
843
- const { properties, newProperties } = setNodeOperation;
844
- for (const key in newProperties) {
845
- const value = newProperties[key];
846
- if (value == null) {
847
- delete textMemory[key];
848
- }
849
- else {
850
- textMemory[key] = value;
851
- }
852
- }
853
- for (const key in properties) {
854
- if (!newProperties.hasOwnProperty(key)) {
855
- delete textMemory[key];
856
- }
857
- }
858
- memorizeLatest(memorizeKey, 'text', textMemory);
923
+ function getResizedPreviousAndNextPoint(nextRenderPoints, sourcePoint, targetPoint, handleIndex) {
924
+ const referencePoint = {
925
+ previous: null,
926
+ next: null
927
+ };
928
+ const startPoint = nextRenderPoints[handleIndex];
929
+ const endPoint = nextRenderPoints[handleIndex + 1];
930
+ const isHorizontal = Point.isHorizontal(startPoint, endPoint);
931
+ const isVertical = Point.isVertical(startPoint, endPoint);
932
+ const previousPoint = nextRenderPoints[handleIndex - 1] ?? nextRenderPoints[0];
933
+ const beforePreviousPoint = nextRenderPoints[handleIndex - 2] ?? sourcePoint;
934
+ if ((isHorizontal && Point.isHorizontal(beforePreviousPoint, previousPoint)) ||
935
+ (isVertical && Point.isVertical(beforePreviousPoint, previousPoint))) {
936
+ referencePoint.previous = previousPoint;
859
937
  }
860
- };
861
- const memorizeLatestShape = (board, shape) => {
862
- const shapes = memorizedShape.has(board) ? memorizedShape.get(board) : [];
863
- const shapeIndex = shapes.indexOf(shape);
864
- if (shape === BasicShapes.text || shapeIndex === 0) {
865
- return;
938
+ const nextPoint = nextRenderPoints[handleIndex + 2] ?? nextRenderPoints[nextRenderPoints.length - 1];
939
+ const afterNextPoint = nextRenderPoints[handleIndex + 3] ?? targetPoint;
940
+ if ((isHorizontal && Point.isHorizontal(nextPoint, afterNextPoint)) || (isVertical && Point.isVertical(nextPoint, afterNextPoint))) {
941
+ referencePoint.next = nextPoint;
866
942
  }
867
- if (shapeIndex !== -1) {
868
- shapes.splice(shapeIndex, 1);
943
+ return referencePoint;
944
+ }
945
+ function alignElbowSegment(startKeyPoint, endKeyPoint, resizeState, resizedPreviousAndNextPoint) {
946
+ let newStartPoint = startKeyPoint;
947
+ let newEndPoint = endKeyPoint;
948
+ if (Point.isHorizontal(startKeyPoint, endKeyPoint)) {
949
+ const offsetY = Point.getOffsetY(resizeState.startPoint, resizeState.endPoint);
950
+ let pointY = startKeyPoint[1] + offsetY;
951
+ if (resizedPreviousAndNextPoint.previous && Math.abs(resizedPreviousAndNextPoint.previous[1] - pointY) < LINE_ALIGN_TOLERANCE) {
952
+ pointY = resizedPreviousAndNextPoint.previous[1];
953
+ }
954
+ else if (resizedPreviousAndNextPoint.next && Math.abs(resizedPreviousAndNextPoint.next[1] - pointY) < LINE_ALIGN_TOLERANCE) {
955
+ pointY = resizedPreviousAndNextPoint.next[1];
956
+ }
957
+ newStartPoint = [startKeyPoint[0], pointY];
958
+ newEndPoint = [endKeyPoint[0], pointY];
869
959
  }
870
- else {
871
- if (shapes.length === SHAPE_MAX_LENGTH) {
872
- shapes.pop();
960
+ if (Point.isVertical(startKeyPoint, endKeyPoint)) {
961
+ const offsetX = Point.getOffsetX(resizeState.startPoint, resizeState.endPoint);
962
+ let pointX = startKeyPoint[0] + offsetX;
963
+ if (resizedPreviousAndNextPoint.previous && Math.abs(resizedPreviousAndNextPoint.previous[0] - pointX) < LINE_ALIGN_TOLERANCE) {
964
+ pointX = resizedPreviousAndNextPoint.previous[0];
965
+ }
966
+ else if (resizedPreviousAndNextPoint.next && Math.abs(resizedPreviousAndNextPoint.next[0] - pointX) < LINE_ALIGN_TOLERANCE) {
967
+ pointX = resizedPreviousAndNextPoint.next[0];
873
968
  }
969
+ newStartPoint = [pointX, startKeyPoint[1]];
970
+ newEndPoint = [pointX, endKeyPoint[1]];
874
971
  }
875
- shapes.unshift(shape);
876
- memorizedShape.set(board, shapes);
877
- };
878
- const getMemorizedLatestShape = (board) => {
879
- return memorizedShape.get(board);
880
- };
881
-
882
- const getHitArrowLineTextIndex = (board, element, point) => {
883
- const texts = element.texts;
884
- if (!texts.length)
885
- return -1;
886
- const points = getArrowLinePoints(board, element);
887
- return texts.findIndex(text => {
888
- const center = getPointOnPolyline(points, text.position);
889
- const rectangle = {
890
- x: center[0] - text.width / 2,
891
- y: center[1] - text.height / 2,
892
- width: text.width,
893
- height: text.height
894
- };
895
- return RectangleClient.isHit(rectangle, RectangleClient.getRectangleByPoints([point, point]));
896
- });
897
- };
898
-
899
- const isMultipleTextShape = (shape) => {
900
- return GEOMETRY_WITH_MULTIPLE_TEXT.includes(shape);
901
- };
902
- const isMultipleTextGeometry = (geometry) => {
903
- return PlaitDrawElement.isGeometry(geometry) && isMultipleTextShape(geometry.shape);
904
- };
905
- const getMultipleTextGeometryTextKeys = (shape) => {
906
- return MultipleTextGeometryTextKeys[shape];
907
- };
908
- const createMultipleTextGeometryElement = (shape, points, options = {}) => {
909
- const id = idCreator();
910
- const drawShapeTexts = buildDefaultTextsByShape(shape);
911
- return {
912
- id,
913
- type: 'geometry',
914
- shape,
915
- angle: 0,
916
- opacity: 1,
917
- texts: drawShapeTexts,
918
- points,
919
- ...options
920
- };
921
- };
922
- const buildDefaultTextsByShape = (shape) => {
923
- const memorizedLatest = getMemorizedLatestByPointer(shape);
924
- const textProperties = { ...memorizedLatest.textProperties };
925
- const alignment = textProperties?.align;
926
- const textHeight = textProperties?.textHeight || DefaultTextProperty.height;
927
- delete textProperties?.align;
928
- delete textProperties?.textHeight;
929
- const defaultTexts = getDefaultGeometryProperty(shape)?.texts || [];
930
- const textKeys = getMultipleTextGeometryTextKeys(shape);
931
- return (textKeys || []).map((textKey) => {
932
- const text = defaultTexts?.find((item) => item?.key === textKey);
972
+ return [newStartPoint, newEndPoint];
973
+ }
974
+ function getIndexAndDeleteCountByKeyPoint(board, element, dataPoints, nextRenderPoints, handleIndex) {
975
+ let index = null;
976
+ let deleteCount = null;
977
+ const startKeyPoint = nextRenderPoints[handleIndex];
978
+ const endKeyPoint = nextRenderPoints[handleIndex + 1];
979
+ if (!startKeyPoint || !endKeyPoint) {
933
980
  return {
934
- id: textKey,
935
- text: buildText(text?.text || '', alignment || text?.align || Alignment.center, textProperties),
936
- textHeight: textHeight
981
+ index,
982
+ deleteCount
937
983
  };
938
- });
939
- };
940
- const getHitMultipleGeometryText = (element, point) => {
941
- const engine = getEngine(element.shape);
942
- const rectangle = RectangleClient.getRectangleByPoints([point, point]);
943
- let hitText;
944
- if (engine.getTextRectangle) {
945
- hitText = element.texts.find(text => {
946
- const textRectangle = engine.getTextRectangle(element, { id: text.id });
947
- return RectangleClient.isHit(rectangle, textRectangle);
948
- });
949
- }
950
- return hitText;
951
- };
952
-
953
- const DefaultLineStyle = {
954
- strokeWidth: 2,
955
- strokeColor: '#000'
956
- };
957
- const LINE_TEXT_SPACE = 4;
958
- const LINE_AUTO_COMPLETE_DIAMETER = 6;
959
- const LINE_AUTO_COMPLETE_OPACITY = 0.6;
960
- const LINE_AUTO_COMPLETE_HOVERED_OPACITY = 0.8;
961
- const LINE_AUTO_COMPLETE_HOVERED_DIAMETER = 10;
962
- const LINE_ALIGN_TOLERANCE = 3;
963
- const LINE_TEXT = '文本';
964
-
965
- class VectorLineShapeGenerator extends Generator {
966
- canDraw(element) {
967
- return true;
968
984
  }
969
- draw(element) {
970
- let lineG;
971
- lineG = drawVectorLine(this.board, element);
972
- return lineG;
985
+ const midDataPoints = dataPoints.slice(1, -1);
986
+ const startIndex = midDataPoints.findIndex(item => Point.isEquals(item, startKeyPoint));
987
+ const endIndex = midDataPoints.findIndex(item => Point.isEquals(item, endKeyPoint));
988
+ if (Math.max(startIndex, endIndex) > -1) {
989
+ if (startIndex > -1 && endIndex > -1) {
990
+ return {
991
+ index: startIndex,
992
+ deleteCount: 2
993
+ };
994
+ }
995
+ if (startIndex > -1 && endIndex === -1) {
996
+ const isReplace = startIndex < midDataPoints.length - 1 &&
997
+ Point.isAlign([midDataPoints[startIndex], midDataPoints[startIndex + 1], startKeyPoint, endKeyPoint]);
998
+ if (isReplace) {
999
+ return {
1000
+ index: startIndex,
1001
+ deleteCount: 2
1002
+ };
1003
+ }
1004
+ return {
1005
+ index: startIndex,
1006
+ deleteCount: 1
1007
+ };
1008
+ }
1009
+ if (startIndex === -1 && endIndex > -1) {
1010
+ const isReplace = endIndex > 0 && Point.isAlign([midDataPoints[endIndex], midDataPoints[endIndex - 1], startKeyPoint, endKeyPoint]);
1011
+ if (isReplace) {
1012
+ return {
1013
+ index: endIndex - 1,
1014
+ deleteCount: 2
1015
+ };
1016
+ }
1017
+ return {
1018
+ index: endIndex,
1019
+ deleteCount: 1
1020
+ };
1021
+ }
973
1022
  }
974
- }
975
-
976
- const getVectorLinePoints = (board, element) => {
977
- switch (element.shape) {
978
- case VectorLineShape.straight: {
979
- return element.points;
1023
+ else {
1024
+ for (let i = 0; i < midDataPoints.length - 1; i++) {
1025
+ const currentPoint = midDataPoints[i];
1026
+ const nextPoint = midDataPoints[i + 1];
1027
+ if (Point.isAlign([currentPoint, nextPoint, startKeyPoint, endKeyPoint])) {
1028
+ index = i;
1029
+ deleteCount = 2;
1030
+ break;
1031
+ }
1032
+ if (Point.isAlign([currentPoint, nextPoint, startKeyPoint])) {
1033
+ index = Math.min(i + 1, midDataPoints.length - 1);
1034
+ deleteCount = 1;
1035
+ break;
1036
+ }
1037
+ if (Point.isAlign([currentPoint, nextPoint, endKeyPoint])) {
1038
+ index = Math.max(i - 1, 0);
1039
+ deleteCount = 1;
1040
+ break;
1041
+ }
980
1042
  }
981
- case VectorLineShape.curve: {
982
- if (element.points.length === 2) {
983
- return pointsOnBezierCurves(element.points);
1043
+ }
1044
+ if (index === null) {
1045
+ deleteCount = 0;
1046
+ if (midDataPoints.length > 0) {
1047
+ const handleRefPair = getArrowLineHandleRefPair(board, element);
1048
+ const params = getElbowLineRouteOptions(board, element, handleRefPair);
1049
+ const keyPoints = removeDuplicatePoints(generateElbowLineRoute(params, board));
1050
+ const nextKeyPoints = simplifyOrthogonalPoints(keyPoints.slice(1, keyPoints.length - 1));
1051
+ const nextDataPoints = [nextRenderPoints[0], ...midDataPoints, nextRenderPoints[nextRenderPoints.length - 1]];
1052
+ const mirrorDataPoints = getMirrorDataPoints(board, nextDataPoints, nextKeyPoints, params);
1053
+ for (let i = handleIndex - 1; i >= 0; i--) {
1054
+ const previousIndex = mirrorDataPoints.slice(1, -1).findIndex(item => Point.isEquals(item, nextRenderPoints[i]));
1055
+ if (previousIndex > -1) {
1056
+ index = previousIndex + 1;
1057
+ break;
1058
+ }
984
1059
  }
985
- else {
986
- let dataPoints = element.points;
987
- const points = catmullRomFitting(dataPoints);
988
- return pointsOnBezierCurves(points);
1060
+ if (index === null) {
1061
+ index = 0;
1062
+ // When renderPoints is a straight line and dataPoints are not on the line,
1063
+ // the default 'deleteCount' is set to midDataPoints.length.
1064
+ if (Point.isAlign(nextRenderPoints)) {
1065
+ deleteCount = midDataPoints.length;
1066
+ }
989
1067
  }
990
1068
  }
991
- default:
992
- return null;
1069
+ else {
1070
+ index = 0;
1071
+ }
993
1072
  }
994
- };
995
- const createVectorLineElement = (shape, points, options) => {
996
1073
  return {
997
- id: idCreator(),
998
- type: 'vector-line',
999
- shape,
1000
- opacity: 1,
1001
- points,
1002
- ...options
1074
+ index,
1075
+ deleteCount
1003
1076
  };
1004
- };
1005
- const vectorLineCreating = (board, lineShape, points, movingPoint, lineShapeG) => {
1006
- const lineGenerator = new VectorLineShapeGenerator(board);
1007
- const memorizedLatest = getLineMemorizedLatest();
1008
- const temporaryLineElement = createVectorLineElement(lineShape, [...points, movingPoint], {
1009
- strokeWidth: DefaultLineStyle.strokeWidth,
1010
- ...memorizedLatest
1011
- });
1012
- const otherPoint = points[points.length - 1];
1013
- temporaryLineElement.points[temporaryLineElement.points.length - 1] = alignPoints(otherPoint, movingPoint);
1014
- lineGenerator.processDrawing(temporaryLineElement, lineShapeG);
1015
- PlaitBoard.getElementTopHost(board).append(lineShapeG);
1016
- return temporaryLineElement;
1017
- };
1018
- const drawVectorLine = (board, element) => {
1019
- const strokeWidth = getStrokeWidthByElement(element);
1020
- const strokeColor = getStrokeColorByElement(board, element);
1021
- const strokeStyle = getStrokeStyleByElement(board, element);
1022
- const strokeLineDash = getStrokeLineDash(strokeStyle, strokeWidth);
1023
- const fill = getFillByElement(board, element);
1024
- const options = { stroke: strokeColor, strokeWidth, strokeLineDash, fill };
1025
- const lineG = createG();
1026
- let points = getVectorLinePoints(board, element);
1027
- const line = drawLinearPath(points, options);
1028
- const id = idCreator();
1029
- line.setAttribute('mask', `url(#${id})`);
1030
- if (element.strokeStyle === StrokeStyle.dotted) {
1031
- setStrokeLinecap(line, 'round');
1032
- }
1033
- lineG.appendChild(line);
1034
- return lineG;
1035
- };
1036
-
1037
- const getCenterPointsOnPolygon$1 = (points) => {
1038
- const centerPoints = [];
1039
- for (let i = 0; i < points.length; i++) {
1040
- let j = i == points.length - 1 ? 0 : i + 1;
1041
- centerPoints.push([(points[i][0] + points[j][0]) / 2, (points[i][1] + points[j][1]) / 2]);
1042
- }
1043
- return centerPoints;
1044
- };
1045
- const getCrossingPointBetweenPointAndPolygon = (corners, point) => {
1046
- const result = [];
1047
- for (let index = 1; index <= corners.length; index++) {
1048
- let start = corners[index - 1];
1049
- let end = index === corners.length ? corners[0] : corners[index];
1050
- const crossingPoint = getCrossingPointsBetweenPointAndSegment(point, start, end);
1051
- result.push(...crossingPoint);
1077
+ }
1078
+ function getMirrorDataPoints(board, nextDataPoints, nextKeyPoints, params) {
1079
+ for (let index = 1; index < nextDataPoints.length - 2; index++) {
1080
+ adjustByCustomPointStartIndex(board, index, nextDataPoints, nextKeyPoints, params);
1052
1081
  }
1053
- return result;
1054
- };
1055
- const getPolygonEdgeByConnectionPoint = (corners, point) => {
1056
- for (let index = 1; index <= corners.length; index++) {
1057
- let start = corners[index - 1];
1058
- let end = index === corners.length ? corners[0] : corners[index];
1059
- if (isPointOnSegment(point, start, end)) {
1060
- return [start, end];
1061
- }
1082
+ return nextDataPoints;
1083
+ }
1084
+ /**
1085
+ * adjust based parallel segment
1086
+ */
1087
+ const adjustByCustomPointStartIndex = (board, customPointStartIndex, nextDataPoints, nextKeyPoints, params) => {
1088
+ const beforePoint = nextDataPoints[customPointStartIndex - 1];
1089
+ const startPoint = nextDataPoints[customPointStartIndex];
1090
+ const endPoint = nextDataPoints[customPointStartIndex + 1];
1091
+ const afterPoint = nextDataPoints[customPointStartIndex + 2];
1092
+ const beforeSegment = [beforePoint, startPoint];
1093
+ const afterSegment = [endPoint, afterPoint];
1094
+ const isStraightWithBefore = Point.isAlign(beforeSegment);
1095
+ const isStraightWithAfter = Point.isAlign(afterSegment);
1096
+ let isAdjustStart = false;
1097
+ let isAdjustEnd = false;
1098
+ if (!isStraightWithBefore || !isStraightWithAfter) {
1099
+ const midKeyPointsWithBefore = getMidKeyPoints(nextKeyPoints, beforeSegment[0], beforeSegment[1]);
1100
+ const midKeyPointsWithAfter = getMidKeyPoints(nextKeyPoints, afterSegment[0], afterSegment[1]);
1101
+ const hasMidKeyPoints = midKeyPointsWithBefore.length > 0 && midKeyPointsWithAfter.length > 0;
1102
+ isAdjustStart = !isStraightWithBefore && !hasMidKeyPoints;
1103
+ isAdjustEnd = !isStraightWithAfter && !hasMidKeyPoints;
1062
1104
  }
1063
- return null;
1064
- };
1065
-
1066
- function generateCloudPath(rectangle) {
1067
- const divisionWidth = rectangle.width / 7;
1068
- const divisionHeight = rectangle.height / 3.2;
1069
- const xRadius = divisionWidth / 8.5;
1070
- const yRadius = divisionHeight / 20;
1071
- const startPoint = [rectangle.x + divisionWidth, rectangle.y + divisionHeight];
1072
- const arcCommands = [
1073
- {
1074
- rx: xRadius,
1075
- ry: yRadius * 1.2,
1076
- xAxisRotation: 0,
1077
- largeArcFlag: 1,
1078
- sweepFlag: 1,
1079
- endX: rectangle.x + divisionWidth * 2,
1080
- endY: rectangle.y + divisionHeight / 2
1081
- },
1082
- {
1083
- rx: xRadius,
1084
- ry: yRadius,
1085
- xAxisRotation: 0,
1086
- largeArcFlag: 1,
1087
- sweepFlag: 1,
1088
- endX: rectangle.x + divisionWidth * 4.2,
1089
- endY: rectangle.y + divisionHeight / 2.2
1090
- },
1091
- {
1092
- rx: xRadius,
1093
- ry: yRadius,
1094
- xAxisRotation: 0,
1095
- largeArcFlag: 1,
1096
- sweepFlag: 1,
1097
- endX: rectangle.x + divisionWidth * 5.8,
1098
- endY: rectangle.y + divisionHeight
1099
- },
1100
- {
1101
- rx: xRadius,
1102
- ry: yRadius * 1.3,
1103
- xAxisRotation: 0,
1104
- largeArcFlag: 1,
1105
- sweepFlag: 1,
1106
- endX: rectangle.x + divisionWidth * 6,
1107
- endY: rectangle.y + divisionHeight * 2.2
1108
- },
1109
- {
1110
- rx: xRadius,
1111
- ry: yRadius * 1.2,
1112
- xAxisRotation: 0,
1113
- largeArcFlag: 1,
1114
- sweepFlag: 1,
1115
- endX: rectangle.x + divisionWidth * 5,
1116
- endY: rectangle.y + divisionHeight * 2.8
1117
- },
1118
- {
1119
- rx: xRadius,
1120
- ry: yRadius / 1.2,
1121
- xAxisRotation: 0,
1122
- largeArcFlag: 1,
1123
- sweepFlag: 1,
1124
- endX: rectangle.x + divisionWidth * 2.8,
1125
- endY: rectangle.y + divisionHeight * 2.8
1126
- },
1127
- {
1128
- rx: xRadius,
1129
- ry: yRadius,
1130
- xAxisRotation: 0,
1131
- largeArcFlag: 1,
1132
- sweepFlag: 1,
1133
- endX: rectangle.x + divisionWidth,
1134
- endY: rectangle.y + divisionHeight * 2.2
1135
- },
1136
- {
1137
- rx: xRadius,
1138
- ry: yRadius * 1.42,
1139
- xAxisRotation: 0,
1140
- largeArcFlag: 1,
1141
- sweepFlag: 1,
1142
- endX: rectangle.x + divisionWidth,
1143
- endY: rectangle.y + divisionHeight
1105
+ if (isAdjustStart || isAdjustEnd) {
1106
+ const parallelSegment = [startPoint, endPoint];
1107
+ const parallelSegments = findOrthogonalParallelSegments(parallelSegment, nextKeyPoints);
1108
+ const mirrorSegments = findMirrorSegments(board, parallelSegment, parallelSegments, params.sourceRectangle, params.targetRectangle);
1109
+ if (mirrorSegments.length === 1) {
1110
+ const mirrorSegment = mirrorSegments[0];
1111
+ if (isAdjustStart) {
1112
+ nextDataPoints.splice(customPointStartIndex, 1, mirrorSegment[0]);
1113
+ }
1114
+ if (isAdjustEnd) {
1115
+ nextDataPoints.splice(customPointStartIndex + 1, 1, mirrorSegment[1]);
1116
+ }
1144
1117
  }
1145
- ];
1146
- return { startPoint, arcCommands };
1147
- }
1148
- const CloudEngine = {
1149
- draw(board, rectangle, options) {
1150
- const rs = PlaitBoard.getRoughSVG(board);
1151
- const { startPoint, arcCommands } = generateCloudPath(rectangle);
1152
- const pathData = `M ${startPoint[0]} ${startPoint[1]} ` +
1153
- arcCommands
1154
- .map((command) => `A ${command.rx} ${command.ry} ${command.xAxisRotation} ${command.largeArcFlag} ${command.sweepFlag} ${command.endX} ${command.endY}`)
1155
- .join('\n') +
1156
- ' Z';
1157
- const svgElement = rs.path(pathData, { ...options, fillStyle: 'solid' });
1158
- setPathStrokeLinecap(svgElement, 'round');
1159
- return svgElement;
1160
- },
1161
- isInsidePoint(rectangle, point) {
1162
- const rangeRectangle = RectangleClient.getRectangleByPoints([point, point]);
1163
- return RectangleClient.isHit(rectangle, rangeRectangle);
1164
- },
1165
- getCornerPoints(rectangle) {
1166
- return RectangleClient.getCornerPoints(rectangle);
1167
- },
1168
- getNearestPoint(rectangle, point) {
1169
- const { startPoint, arcCommands } = generateCloudPath(rectangle);
1170
- let minDistance = Infinity;
1171
- let nearestPoint = point;
1172
- let currentStart = startPoint;
1173
- for (const arcCommand of arcCommands) {
1174
- const arcNearestPoint = getNearestPointBetweenPointAndArc(point, currentStart, arcCommand);
1175
- const distance = distanceBetweenPointAndPoint(point[0], point[1], arcNearestPoint[0], arcNearestPoint[1]);
1176
- if (distance < minDistance) {
1177
- minDistance = distance;
1178
- nearestPoint = arcNearestPoint;
1118
+ else {
1119
+ const isHorizontal = Point.isHorizontal(startPoint, endPoint);
1120
+ const adjustIndex = isHorizontal ? 0 : 1;
1121
+ if (isAdjustStart) {
1122
+ const newStartPoint = [startPoint[0], startPoint[1]];
1123
+ newStartPoint[adjustIndex] = beforePoint[adjustIndex];
1124
+ nextDataPoints.splice(customPointStartIndex, 1, newStartPoint);
1125
+ }
1126
+ if (isAdjustEnd) {
1127
+ const newEndPoint = [endPoint[0], endPoint[1]];
1128
+ newEndPoint[adjustIndex] = afterPoint[adjustIndex];
1129
+ nextDataPoints.splice(customPointStartIndex + 1, 1, newEndPoint);
1179
1130
  }
1180
- currentStart = [arcCommand.endX, arcCommand.endY];
1181
1131
  }
1182
- return nearestPoint;
1183
- },
1184
- getEdgeByConnectionPoint(rectangle, pointOfRectangle) {
1185
- const corners = CloudEngine.getCornerPoints(rectangle);
1186
- const point = RectangleClient.getConnectionPoint(rectangle, pointOfRectangle);
1187
- return getPolygonEdgeByConnectionPoint(corners, point);
1188
- },
1189
- getConnectorPoints(rectangle) {
1190
- return RectangleClient.getEdgeCenterPoints(rectangle);
1191
- },
1192
- getTextRectangle(element) {
1193
- const elementRectangle = RectangleClient.getRectangleByPoints(element.points);
1194
- const strokeWidth = getStrokeWidthByElement(element);
1195
- const height = element.textHeight;
1196
- const originWidth = elementRectangle.width - ShapeDefaultSpace.rectangleAndText * 2 - strokeWidth * 2;
1197
- const width = originWidth / 1.5;
1198
- return {
1199
- height,
1200
- width: width > 0 ? width : 0,
1201
- x: elementRectangle.x + ShapeDefaultSpace.rectangleAndText + strokeWidth + originWidth / 6,
1202
- y: elementRectangle.y + elementRectangle.height / 6 + ((elementRectangle.height * 4) / 6 - height) / 2
1203
- };
1204
1132
  }
1205
1133
  };
1206
-
1207
- const isTextExceedingBounds = (geometry) => {
1208
- const client = RectangleClient.getRectangleByPoints(geometry.points);
1209
- if (geometry.textHeight && geometry.textHeight > client.height) {
1134
+ function isUpdatedHandleIndex(board, element, dataPoints, nextRenderPoints, handleIndex) {
1135
+ const { deleteCount } = getIndexAndDeleteCountByKeyPoint(board, element, dataPoints, nextRenderPoints, handleIndex);
1136
+ if (deleteCount !== null && deleteCount > 1) {
1210
1137
  return true;
1211
1138
  }
1212
1139
  return false;
1213
- };
1214
- const isHitArrowLineText = (board, element, point) => {
1215
- return getHitArrowLineTextIndex(board, element, point) !== -1;
1216
- };
1217
- const isHitPolyLine = (pathPoints, point) => {
1218
- const distance = distanceBetweenPointAndSegments(point, pathPoints);
1219
- return distance <= HIT_DISTANCE_BUFFER;
1220
- };
1221
- const isHitArrowLine = (board, element, point) => {
1222
- const points = getArrowLinePoints(board, element);
1223
- const isHitText = isHitArrowLineText(board, element, point);
1224
- return isHitText || isHitPolyLine(points, point);
1225
- };
1226
- const isHitVectorLine = (board, element, point) => {
1227
- const points = getVectorLinePoints(board, element);
1228
- if (isClosedPoints(element.points)) {
1229
- return isPointInPolygon(point, points) || isHitPolyLine(points, point);
1230
- }
1231
- else {
1232
- return isHitPolyLine(points, point);
1233
- }
1234
- };
1235
- const isRectangleHitElementText = (element, rectangle) => {
1236
- const engine = getEngine(element.shape);
1237
- if (isMultipleTextGeometry(element)) {
1238
- const texts = element.texts;
1239
- return texts.some((item) => {
1240
- const textClient = engine.getTextRectangle(element, { id: item.id });
1241
- return isRectangleHitRotatedPoints(rectangle, RectangleClient.getCornerPoints(textClient), element.angle);
1242
- });
1140
+ }
1141
+ function getMidKeyPoints(simplifiedNextKeyPoints, startPoint, endPoint) {
1142
+ let midElbowPoints = [];
1143
+ let startPointIndex = -1;
1144
+ let endPointIndex = -1;
1145
+ for (let i = 0; i < simplifiedNextKeyPoints.length; i++) {
1146
+ if (Point.isAlign([simplifiedNextKeyPoints[i], startPoint])) {
1147
+ startPointIndex = i;
1148
+ }
1149
+ if (startPointIndex > -1 && Point.isAlign([simplifiedNextKeyPoints[i], endPoint])) {
1150
+ endPointIndex = i;
1151
+ break;
1152
+ }
1243
1153
  }
1244
- else {
1245
- const textClient = engine.getTextRectangle ? engine.getTextRectangle(element) : getTextRectangle(element);
1246
- return isRectangleHitRotatedPoints(rectangle, RectangleClient.getCornerPoints(textClient), element.angle);
1154
+ if (startPointIndex > -1 && endPointIndex > -1) {
1155
+ midElbowPoints = simplifiedNextKeyPoints.slice(startPointIndex, endPointIndex + 1);
1247
1156
  }
1248
- };
1249
- const isHitElementText = (element, point) => {
1250
- const engine = getEngine(element.shape);
1251
- if (isMultipleTextGeometry(element)) {
1252
- const texts = element.texts;
1253
- return texts.some((item) => {
1254
- const textClient = engine.getTextRectangle(element, { id: item.id });
1255
- return RectangleClient.isPointInRectangle(textClient, point);
1256
- });
1157
+ return midElbowPoints;
1158
+ }
1159
+ function findOrthogonalParallelSegments(segment, keyPoints) {
1160
+ const isHorizontalSegment = Point.isHorizontal(segment[0], segment[1]);
1161
+ const parallelSegments = [];
1162
+ for (let i = 0; i < keyPoints.length - 1; i++) {
1163
+ const current = keyPoints[i];
1164
+ const next = keyPoints[i + 1];
1165
+ const isHorizontal = Point.isHorizontal(current, next, 0.1);
1166
+ if (isHorizontalSegment && isHorizontal) {
1167
+ parallelSegments.push([current, next]);
1168
+ }
1169
+ if (!isHorizontalSegment && !isHorizontal) {
1170
+ parallelSegments.push([current, next]);
1171
+ }
1257
1172
  }
1258
- else {
1259
- const textClient = engine.getTextRectangle ? engine.getTextRectangle(element) : getTextRectangle(element);
1260
- return RectangleClient.isPointInRectangle(textClient, point);
1173
+ return parallelSegments;
1174
+ }
1175
+ function findMirrorSegments(board, segment, parallelSegments, sourceRectangle, targetRectangle) {
1176
+ debugGenerator$4.isDebug() && debugGenerator$4.clear();
1177
+ const mirrorSegments = [];
1178
+ for (let index = 0; index < parallelSegments.length; index++) {
1179
+ const parallelPath = parallelSegments[index];
1180
+ const startPoint = [segment[0][0], segment[0][1]];
1181
+ const endPoint = [segment[1][0], segment[1][1]];
1182
+ const isHorizontal = Point.isHorizontal(startPoint, endPoint);
1183
+ const adjustDataIndex = isHorizontal ? 0 : 1;
1184
+ startPoint[adjustDataIndex] = parallelPath[0][adjustDataIndex];
1185
+ endPoint[adjustDataIndex] = parallelPath[1][adjustDataIndex];
1186
+ const fakeRectangle = RectangleClient.getRectangleByPoints([startPoint, endPoint, ...parallelPath]);
1187
+ const isValid = !RectangleClient.isHit(fakeRectangle, sourceRectangle) && !RectangleClient.isHit(fakeRectangle, targetRectangle);
1188
+ if (isValid) {
1189
+ mirrorSegments.push([startPoint, endPoint]);
1190
+ debugGenerator$4.isDebug() && debugGenerator$4.drawPolygon(board, RectangleClient.getCornerPoints(fakeRectangle));
1191
+ }
1261
1192
  }
1262
- };
1263
- const isEmptyTextElement = (element) => {
1264
- if (!isDrawElementIncludeText(element)) {
1193
+ return mirrorSegments;
1194
+ }
1195
+ const hasIllegalElbowPoint = (midDataPoints) => {
1196
+ if (midDataPoints.length === 1) {
1265
1197
  return true;
1266
1198
  }
1267
- const editor = getFirstTextEditor(element);
1268
- return Editor.isEmpty(editor, editor.children[0]);
1269
- };
1270
- const isRectangleHitDrawElement = (board, element, selection) => {
1271
- const rangeRectangle = RectangleClient.getRectangleByPoints([selection.anchor, selection.focus]);
1272
- if (PlaitDrawElement.isGeometry(element)) {
1273
- const isHitElement = isRectangleHitRotatedElement(board, rangeRectangle, element);
1274
- if (isHitElement) {
1275
- return isHitElement;
1199
+ return midDataPoints.some((item, index) => {
1200
+ const beforePoint = midDataPoints[index - 1];
1201
+ const afterPoint = midDataPoints[index + 1];
1202
+ const beforeSegment = beforePoint && [beforePoint, item];
1203
+ const afterSegment = afterPoint && [item, afterPoint];
1204
+ const isStraightWithBefore = beforeSegment && Point.isAlign(beforeSegment);
1205
+ const isStraightWithAfter = afterSegment && Point.isAlign(afterSegment);
1206
+ if (index === 0) {
1207
+ return !isStraightWithAfter;
1276
1208
  }
1277
- return !isEmptyTextElement(element) && isRectangleHitElementText(element, rangeRectangle);
1278
- }
1279
- if (PlaitDrawElement.isImage(element)) {
1280
- return isRectangleHitRotatedElement(board, rangeRectangle, element);
1281
- }
1282
- if (PlaitDrawElement.isArrowLine(element)) {
1283
- const points = getArrowLinePoints(board, element);
1284
- return isLineHitRectangle(points, rangeRectangle);
1285
- }
1286
- if (PlaitDrawElement.isVectorLine(element)) {
1287
- const points = getVectorLinePoints(board, element);
1288
- return isLineHitRectangle(points, rangeRectangle);
1289
- }
1290
- return null;
1291
- };
1292
- const isRectangleHitRotatedElement = (board, rectangle, element) => {
1293
- const client = RectangleClient.getRectangleByPoints(element.points);
1294
- return isRectangleHitRotatedPoints(rectangle, RectangleClient.getCornerPoints(client), element.angle);
1295
- };
1296
- const isRectangleHitRotatedPoints = (rectangle, points, angle) => {
1297
- let rotatedPoints = rotatePointsByAngle(points, angle) || points;
1298
- return isLineHitRectangle(rotatedPoints, rectangle);
1209
+ if (index === midDataPoints.length - 1) {
1210
+ return !isStraightWithBefore;
1211
+ }
1212
+ return !isStraightWithBefore && !isStraightWithAfter;
1213
+ });
1299
1214
  };
1300
- const getHitDrawElement = (board, elements) => {
1301
- let firstFilledElement = getFirstFilledDrawElement(board, elements);
1302
- let endIndex = elements.length;
1303
- if (firstFilledElement) {
1304
- endIndex = elements.indexOf(firstFilledElement) + 1;
1305
- }
1306
- const newElements = elements.slice(0, endIndex);
1307
- const element = getFirstTextOrLineElement(newElements);
1308
- if (element) {
1309
- return element;
1215
+
1216
+ const ARROW_LENGTH = 20;
1217
+ const drawArrowLineArrow = (element, points, options) => {
1218
+ const arrowG = createG();
1219
+ if (PlaitArrowLine.isSourceMark(element, ArrowLineMarkerType.none) && PlaitArrowLine.isTargetMark(element, ArrowLineMarkerType.none)) {
1220
+ return null;
1310
1221
  }
1311
- const sortElements = sortElementsByArea(board, newElements, 'asc');
1312
- return sortElements[0];
1313
- };
1314
- const getFirstFilledDrawElement = (board, elements) => {
1315
- let filledElement = null;
1316
- for (let i = 0; i < elements.length; i++) {
1317
- const element = elements[i];
1318
- if (isClosedCustomGeometry(board, element) || isClosedDrawElement(element)) {
1319
- const fill = getFillByElement(board, element);
1320
- if (isFilled(fill)) {
1321
- filledElement = element;
1322
- break;
1323
- }
1324
- }
1222
+ const strokeWidth = getStrokeWidthByElement(element);
1223
+ const offset = (strokeWidth * strokeWidth) / 3;
1224
+ if (points.length === 1) {
1225
+ points = [points[0], [points[0][0] + 0.1, points[0][1]]];
1325
1226
  }
1326
- return filledElement;
1327
- };
1328
- const isFilledDrawElement = (board, element) => {
1329
- return getFirstFilledDrawElement(board, [element]) !== null;
1330
- };
1331
- const getFirstTextOrLineElement = (elements) => {
1332
- const texts = elements.filter((item) => PlaitDrawElement.isText(item));
1333
- if (texts.length) {
1334
- return texts[0];
1227
+ if (!PlaitArrowLine.isSourceMark(element, ArrowLineMarkerType.none)) {
1228
+ const source = getExtendPoint(points[0], points[1], ARROW_LENGTH + offset);
1229
+ const sourceArrow = getArrow(element, { marker: element.source.marker, source, target: points[0], isSource: true }, options);
1230
+ sourceArrow && arrowG.appendChild(sourceArrow);
1335
1231
  }
1336
- const lines = elements.filter((item) => PlaitDrawElement.isArrowLine(item));
1337
- if (lines.length) {
1338
- return lines[0];
1232
+ if (!PlaitArrowLine.isTargetMark(element, ArrowLineMarkerType.none)) {
1233
+ const source = getExtendPoint(points[points.length - 1], points[points.length - 2], ARROW_LENGTH + offset);
1234
+ const arrow = getArrow(element, { marker: element.target.marker, source, target: points[points.length - 1], isSource: false }, options);
1235
+ arrow && arrowG.appendChild(arrow);
1339
1236
  }
1340
- return null;
1237
+ return arrowG;
1341
1238
  };
1342
- const debugKey$4 = 'debug:plait:hit:shape:edge:sample-points';
1343
- const debugGenerator$4 = createDebugGenerator(debugKey$4);
1344
- const shapes = [BasicShapes.cloud];
1345
- const isHitDrawElement = (board, element, point, isStrict = true) => {
1346
- const rectangle = board.getRectangle(element);
1347
- point = rotateAntiPointsByElement(board, point, element) || point;
1348
- if (PlaitDrawElement.isGeometry(element) && rectangle) {
1349
- if (debugGenerator$4.isDebug() && shapes.includes(element.shape)) {
1350
- debugGenerator$4.clear();
1351
- const { startPoint, arcCommands } = generateCloudPath(rectangle);
1352
- const points = [startPoint, ...arcCommands.map((arc) => [arc.endX, arc.endY])];
1353
- debugGenerator$4.drawCircles(board, points, 5, false);
1354
- let minDistance = Infinity;
1355
- let nearestPoint = point;
1356
- let currentStart = startPoint;
1357
- for (const arc of arcCommands) {
1358
- const arcNearestPoint = getNearestPointBetweenPointAndArc(point, currentStart, arc);
1359
- const distance = distanceBetweenPointAndPoint(point[0], point[1], arcNearestPoint[0], arcNearestPoint[1]);
1360
- const { center } = getEllipseArcCenter(currentStart, arc);
1361
- debugGenerator$4.drawCircles(board, [center], 8, false, { fill: 'yellow' });
1362
- if (distance < minDistance) {
1363
- minDistance = distance;
1364
- nearestPoint = arcNearestPoint;
1365
- }
1366
- currentStart = [arc.endX, arc.endY];
1367
- }
1368
- debugGenerator$4.drawCircles(board, [point], 12, false, { fill: 'black', stroke: 'black' });
1369
- debugGenerator$4.drawCircles(board, [nearestPoint], 12, false, { fill: 'green', stroke: 'green' });
1239
+ const getArrow = (element, arrowOptions, options) => {
1240
+ const { marker, target, source, isSource } = arrowOptions;
1241
+ let targetArrow;
1242
+ switch (marker) {
1243
+ case ArrowLineMarkerType.openTriangle: {
1244
+ targetArrow = drawOpenTriangle(element, source, target, options);
1245
+ break;
1370
1246
  }
1371
- if (isHitEdgeOfShape(board, element, point, HIT_DISTANCE_BUFFER)) {
1372
- return true;
1247
+ case ArrowLineMarkerType.solidTriangle: {
1248
+ targetArrow = drawSolidTriangle(source, target, options);
1249
+ break;
1373
1250
  }
1374
- const engine = getEngine(getElementShape(element));
1375
- if (PlaitDrawElement.isText(element)) {
1376
- const textClient = getTextRectangle(element);
1377
- return RectangleClient.isPointInRectangle(textClient, point);
1251
+ case ArrowLineMarkerType.arrow: {
1252
+ targetArrow = drawArrow(element, source, target, options);
1253
+ break;
1378
1254
  }
1379
- if (!!isStrict && isEmptyTextElement(element) && !isFilledDrawElement(board, element)) {
1380
- return false;
1255
+ case ArrowLineMarkerType.sharpArrow: {
1256
+ targetArrow = drawSharpArrow(source, target, options);
1257
+ break;
1381
1258
  }
1382
- const isHitText = isHitElementText(element, point);
1383
- return isHitText || engine.isInsidePoint(rectangle, point);
1384
- }
1385
- if (PlaitDrawElement.isImage(element)) {
1386
- const client = RectangleClient.getRectangleByPoints(element.points);
1387
- return RectangleClient.isPointInRectangle(client, point);
1388
- }
1389
- if (PlaitDrawElement.isArrowLine(element)) {
1390
- return isHitArrowLine(board, element, point);
1391
- }
1392
- if (PlaitDrawElement.isVectorLine(element)) {
1393
- return isHitVectorLine(board, element, point);
1394
- }
1395
- return null;
1396
- };
1397
- const isHitEdgeOfShape = (board, element, point, hitDistanceBuffer) => {
1398
- const nearestPoint = getNearestPoint(element, point);
1399
- const distance = distanceBetweenPointAndPoint(nearestPoint[0], nearestPoint[1], point[0], point[1]);
1400
- return distance <= hitDistanceBuffer;
1401
- };
1402
- const isInsideOfShape = (board, element, point, hitDistanceBuffer) => {
1403
- const client = RectangleClient.inflate(RectangleClient.getRectangleByPoints(element.points), hitDistanceBuffer);
1404
- return getEngine(getElementShape(element)).isInsidePoint(client, point);
1405
- };
1406
- const isHitElementInside = (board, element, point) => {
1407
- const rectangle = board.getRectangle(element);
1408
- point = rotateAntiPointsByElement(board, point, element) || point;
1409
- if (PlaitDrawElement.isGeometry(element) && !PlaitDrawElement.isGeometryByTable(element)) {
1410
- const engine = getEngine(getElementShape(element));
1411
- const isHitInside = engine.isInsidePoint(rectangle, point);
1412
- if (isHitInside) {
1413
- return isHitInside;
1259
+ case ArrowLineMarkerType.oneSideUp: {
1260
+ targetArrow = drawOneSideArrow(source, target, isSource ? 'down' : 'up', options);
1261
+ break;
1414
1262
  }
1415
- if (engine.getTextRectangle) {
1416
- const isHitText = isHitElementText(element, point);
1417
- if (isHitText) {
1418
- return isHitText;
1419
- }
1263
+ case ArrowLineMarkerType.oneSideDown: {
1264
+ targetArrow = drawOneSideArrow(source, target, isSource ? 'up' : 'down', options);
1265
+ break;
1266
+ }
1267
+ case ArrowLineMarkerType.hollowTriangle: {
1268
+ targetArrow = drawHollowTriangleArrow(source, target, options);
1269
+ break;
1270
+ }
1271
+ case ArrowLineMarkerType.singleSlash: {
1272
+ targetArrow = drawSingleSlash(source, target, isSource, options);
1273
+ break;
1420
1274
  }
1421
1275
  }
1422
- if (PlaitDrawElement.isImage(element)) {
1423
- const client = RectangleClient.getRectangleByPoints(element.points);
1424
- return RectangleClient.isPointInRectangle(client, point);
1425
- }
1426
- if (PlaitDrawElement.isArrowLine(element)) {
1427
- return isHitArrowLine(board, element, point);
1428
- }
1429
- if (PlaitDrawElement.isVectorLine(element)) {
1430
- return isHitVectorLine(board, element, point);
1431
- }
1432
- return null;
1276
+ return targetArrow;
1433
1277
  };
1434
-
1435
- const getTextRectangle = (element) => {
1436
- const elementRectangle = RectangleClient.getRectangleByPoints(element.points);
1437
- const strokeWidth = getStrokeWidthByElement(element);
1438
- const height = element.textHeight;
1439
- const width = elementRectangle.width - ShapeDefaultSpace.rectangleAndText * 2 - strokeWidth * 2;
1440
- return {
1441
- height,
1442
- width: width > 0 ? width : 0,
1443
- x: elementRectangle.x + ShapeDefaultSpace.rectangleAndText + strokeWidth,
1444
- y: elementRectangle.y + (elementRectangle.height - height) / 2
1445
- };
1278
+ const drawSharpArrow = (source, target, options) => {
1279
+ const startPoint = target;
1280
+ const { pointLeft, pointRight } = arrowPoints(source, target, 20);
1281
+ const g = createG();
1282
+ const path = createPath();
1283
+ let polylinePath = `M${pointRight[0]},${pointRight[1]}A25,25,20,0,1,${pointLeft[0]},${pointLeft[1]}L${startPoint[0]},${startPoint[1]}Z`;
1284
+ path.setAttribute('d', polylinePath);
1285
+ path.setAttribute('stroke', `${options?.stroke}`);
1286
+ path.setAttribute('stroke-width', `${options?.strokeWidth}`);
1287
+ path.setAttribute('fill', `${options?.stroke}`);
1288
+ g.appendChild(path);
1289
+ return g;
1446
1290
  };
1447
- const getStrokeWidthByElement = (element) => {
1448
- if (PlaitDrawElement.isText(element)) {
1449
- return 0;
1450
- }
1451
- const strokeWidth = element.strokeWidth || DefaultDrawStyle.strokeWidth;
1452
- return strokeWidth;
1291
+ const drawArrow = (element, source, target, options) => {
1292
+ const unitVector = getUnitVectorByPointAndPoint(source, target);
1293
+ const strokeWidth = getStrokeWidthByElement(element);
1294
+ const endPoint = [target[0] + (strokeWidth * unitVector[0]) / 2, target[1] + (strokeWidth * unitVector[1]) / 2];
1295
+ const distance = distanceBetweenPointAndPoint(...source, ...endPoint);
1296
+ const middlePoint = [
1297
+ endPoint[0] - (((distance * 3) / 5 + strokeWidth) / 2) * unitVector[0],
1298
+ endPoint[1] - (((distance * 3) / 5 + strokeWidth) / 2) * unitVector[1]
1299
+ ];
1300
+ const { pointLeft, pointRight } = arrowPoints(source, endPoint, 30);
1301
+ const arrowG = drawLinearPath([pointLeft, endPoint, pointRight, middlePoint], { ...options, fill: options.stroke }, true);
1302
+ const path = arrowG.querySelector('path');
1303
+ path.setAttribute('stroke-linejoin', 'round');
1304
+ return arrowG;
1453
1305
  };
1454
- const insertElement = (board, element) => {
1455
- memorizeLatestShape(board, element.shape);
1456
- Transforms.insertNode(board, element, [board.children.length]);
1457
- clearSelectedElement(board);
1458
- addSelectedElement(board, element);
1459
- BoardTransforms.updatePointerType(board, PlaitPointerType.selection);
1306
+ const drawSolidTriangle = (source, target, options) => {
1307
+ const endPoint = target;
1308
+ const { pointLeft, pointRight } = arrowPoints(source, endPoint, 30);
1309
+ return drawLinearPath([pointLeft, endPoint, pointRight], { ...options, fill: options.stroke }, true);
1460
1310
  };
1461
- const isDrawElementIncludeText = (element) => {
1462
- if (PlaitDrawElement.isText(element)) {
1311
+ const drawOpenTriangle = (element, source, target, options) => {
1312
+ const unitVector = getUnitVectorByPointAndPoint(source, target);
1313
+ const strokeWidth = getStrokeWidthByElement(element);
1314
+ const endPoint = [target[0] + (strokeWidth * unitVector[0]) / 2, target[1] + (strokeWidth * unitVector[1]) / 2];
1315
+ const { pointLeft, pointRight } = arrowPoints(source, endPoint, 40);
1316
+ return drawLinearPath([pointLeft, endPoint, pointRight], options);
1317
+ };
1318
+ const drawOneSideArrow = (source, target, side, options) => {
1319
+ const { pointLeft, pointRight } = arrowPoints(source, target, 40);
1320
+ return drawLinearPath([side === 'up' ? pointRight : pointLeft, target], options);
1321
+ };
1322
+ const drawSingleSlash = (source, target, isSource, options) => {
1323
+ const length = distanceBetweenPointAndPoint(...source, ...target);
1324
+ const middlePoint = getExtendPoint(target, source, length / 2);
1325
+ const angle = isSource ? 120 : 60;
1326
+ const start = rotate(...source, ...middlePoint, (angle * Math.PI) / 180);
1327
+ const end = rotate(...target, ...middlePoint, (angle * Math.PI) / 180);
1328
+ return drawLinearPath([start, end], options);
1329
+ };
1330
+ const drawHollowTriangleArrow = (source, target, options) => {
1331
+ const { pointLeft, pointRight } = arrowPoints(source, target, 30);
1332
+ return drawLinearPath([pointLeft, pointRight, target], { ...options, fill: 'white' }, true);
1333
+ };
1334
+
1335
+ class ArrowLineShapeGenerator extends Generator {
1336
+ canDraw(element) {
1463
1337
  return true;
1464
1338
  }
1465
- if (PlaitDrawElement.isImage(element)) {
1466
- return false;
1467
- }
1468
- if (PlaitDrawElement.isGeometry(element)) {
1469
- return isGeometryIncludeText(element);
1470
- }
1471
- if (PlaitDrawElement.isArrowLine(element)) {
1472
- const editors = getTextEditorsByElement(element);
1473
- return editors.length > 0;
1474
- }
1475
- if (PlaitDrawElement.isElementByTable(element)) {
1476
- return element.cells.some((cell) => isCellIncludeText(cell));
1339
+ draw(element) {
1340
+ let lineG;
1341
+ lineG = drawArrowLine(this.board, element);
1342
+ return lineG;
1477
1343
  }
1478
- return true;
1479
- };
1480
- const isDrawElementsIncludeText = (elements) => {
1481
- return elements.some((item) => {
1482
- return isDrawElementIncludeText(item);
1483
- });
1344
+ }
1345
+
1346
+ const createArrowLineElement = (shape, points, source, target, texts, options) => {
1347
+ return {
1348
+ id: idCreator(),
1349
+ type: 'arrow-line',
1350
+ shape,
1351
+ source,
1352
+ texts: texts ? texts : [],
1353
+ target,
1354
+ opacity: 1,
1355
+ points,
1356
+ ...options
1357
+ };
1484
1358
  };
1485
- const isClosedDrawElement = (element) => {
1486
- if (PlaitDrawElement.isDrawElement(element)) {
1487
- if (PlaitDrawElement.isText(element) || PlaitDrawElement.isArrowLine(element) || PlaitDrawElement.isImage(element)) {
1488
- return false;
1359
+ const getArrowLinePoints = (board, element) => {
1360
+ switch (element.shape) {
1361
+ case ArrowLineShape.elbow: {
1362
+ return getElbowPoints(board, element);
1489
1363
  }
1490
- if (PlaitDrawElement.isVectorLine(element)) {
1491
- return isClosedPoints(element.points);
1364
+ case ArrowLineShape.curve: {
1365
+ return getCurvePoints(board, element);
1492
1366
  }
1493
- if (PlaitDrawElement.isGeometry(element)) {
1494
- return isGeometryClosed(element);
1367
+ default: {
1368
+ const points = PlaitArrowLine.getPoints(board, element);
1369
+ const handleRefPair = getArrowLineHandleRefPair(board, element);
1370
+ points[0] = handleRefPair.source.point;
1371
+ points[points.length - 1] = handleRefPair.target.point;
1372
+ return points;
1495
1373
  }
1496
- return true;
1497
1374
  }
1498
- return false;
1499
1375
  };
1500
- const isClosedCustomGeometry = (board, value) => {
1501
- return PlaitDrawElement.isCustomGeometryElement(board, value) && isClosedPoints(value.points);
1502
- };
1503
- const getSnappingShape = (board, point) => {
1504
- let hitElement = getHitShape(board, point);
1505
- if (hitElement) {
1506
- const ref = getSnappingRef(board, hitElement, point);
1507
- if (ref.isHitConnector || ref.isHitEdge) {
1508
- return hitElement;
1376
+ const getCurvePoints = (board, element) => {
1377
+ if (element.points.length === 2) {
1378
+ const handleRefPair = getArrowLineHandleRefPair(board, element);
1379
+ const { source, target } = handleRefPair;
1380
+ const sourceBoundElement = handleRefPair.source.boundElement;
1381
+ const targetBoundElement = handleRefPair.target.boundElement;
1382
+ let curvePoints = [source.point];
1383
+ const sumDistance = distanceBetweenPointAndPoint(...source.point, ...target.point);
1384
+ const offset = 12 + sumDistance / 3;
1385
+ if (sourceBoundElement) {
1386
+ curvePoints.push(getPointByVectorComponent(source.point, source.vector, offset));
1509
1387
  }
1510
- }
1511
- return null;
1512
- };
1513
- const getSnappingRef = (board, hitElement, point) => {
1514
- const rotatedPoint = rotateAntiPointsByElement(board, point, hitElement) || point;
1515
- const connectorPoint = getHitConnectorPoint(rotatedPoint, hitElement);
1516
- const edgePoint = getNearestPoint(hitElement, rotatedPoint);
1517
- const isHitEdge = isHitEdgeOfShape(board, hitElement, rotatedPoint, LINE_SNAPPING_BUFFER);
1518
- return { isHitEdge, isHitConnector: !!connectorPoint, connectorPoint, edgePoint };
1519
- };
1520
- const getHitShape = (board, point, offset = LINE_HIT_GEOMETRY_BUFFER) => {
1521
- let hitShape = null;
1522
- traverseDrawShapes(board, (element) => {
1523
- if (hitShape === null && isInsideOfShape(board, element, rotateAntiPointsByElement(board, point, element) || point, offset * 2)) {
1524
- hitShape = element;
1388
+ if (targetBoundElement) {
1389
+ curvePoints.push(getPointByVectorComponent(target.point, target.vector, offset));
1525
1390
  }
1526
- });
1527
- return hitShape;
1528
- };
1529
- const traverseDrawShapes = (board, callback) => {
1530
- depthFirstRecursion(board, (node) => {
1531
- if (!PlaitBoard.isBoard(node) && PlaitDrawElement.isShapeElement(node)) {
1532
- callback(node);
1391
+ const isSingleBound = (sourceBoundElement && !targetBoundElement) || (!sourceBoundElement && targetBoundElement);
1392
+ if (isSingleBound) {
1393
+ curvePoints.push(target.point);
1394
+ const points = Q2C(curvePoints);
1395
+ return pointsOnBezierCurves(points);
1533
1396
  }
1534
- }, getIsRecursionFunc(board), true);
1535
- };
1536
- const drawShape = (board, outerRectangle, shape, roughOptions, drawOptions) => {
1537
- return getEngine(shape).draw(board, outerRectangle, roughOptions, drawOptions);
1538
- };
1539
- const drawBoundReaction = (board, element, roughOptions = { hasMask: true, hasConnector: true }) => {
1540
- const g = createG();
1541
- const rectangle = RectangleClient.getRectangleByPoints(element.points);
1542
- const activeRectangle = RectangleClient.inflate(rectangle, SNAPPING_STROKE_WIDTH);
1543
- const shape = getElementShape(element);
1544
- let drawOptions;
1545
- if (PlaitDrawElement.isElementByTable(element)) {
1546
- drawOptions = { element };
1547
- }
1548
- const strokeG = drawShape(board, activeRectangle, shape, {
1549
- stroke: SELECTION_BORDER_COLOR,
1550
- strokeWidth: SNAPPING_STROKE_WIDTH
1551
- }, drawOptions);
1552
- g.appendChild(strokeG);
1553
- if (roughOptions.hasMask) {
1554
- const maskG = drawShape(board, activeRectangle, shape, {
1555
- stroke: SELECTION_BORDER_COLOR,
1556
- strokeWidth: 0,
1557
- fill: isClosedDrawElement(element) ? SELECTION_FILL_COLOR : DefaultDrawStyle.fill,
1558
- fillStyle: 'solid'
1559
- }, drawOptions);
1560
- g.appendChild(maskG);
1397
+ if (!sourceBoundElement && !targetBoundElement) {
1398
+ curvePoints.push(getPointByVectorComponent(source.point, source.vector, offset));
1399
+ curvePoints.push(getPointByVectorComponent(target.point, target.vector, offset));
1400
+ }
1401
+ curvePoints.push(target.point);
1402
+ return pointsOnBezierCurves(curvePoints);
1561
1403
  }
1562
- if (roughOptions.hasConnector) {
1563
- const connectorPoints = getEngine(shape).getConnectorPoints(rectangle);
1564
- connectorPoints.forEach((point) => {
1565
- const circleG = drawCircle(PlaitBoard.getRoughSVG(board), point, 8, {
1566
- stroke: SELECTION_BORDER_COLOR,
1567
- strokeWidth: ACTIVE_STROKE_WIDTH,
1568
- fill: '#FFF',
1569
- fillStyle: 'solid'
1570
- });
1571
- g.appendChild(circleG);
1572
- });
1404
+ else {
1405
+ let dataPoints = PlaitArrowLine.getPoints(board, element);
1406
+ dataPoints = removeDuplicatePoints(dataPoints);
1407
+ const points = catmullRomFitting(dataPoints);
1408
+ return pointsOnBezierCurves(points);
1573
1409
  }
1574
- return g;
1575
1410
  };
1576
- const getTextKey = (element, text) => {
1577
- if (element && isMultipleTextGeometry(element)) {
1578
- return `${element.id}-${text.id}`;
1411
+ const drawArrowLine = (board, element) => {
1412
+ const strokeWidth = getStrokeWidthByElement(element);
1413
+ const strokeColor = getStrokeColorByElement(board, element);
1414
+ const strokeStyle = getStrokeStyleByElement(board, element);
1415
+ const strokeLineDash = getStrokeLineDash(strokeStyle, strokeWidth);
1416
+ const options = { stroke: strokeColor, strokeWidth, strokeLineDash };
1417
+ const lineG = createG();
1418
+ let points = getArrowLinePoints(board, element);
1419
+ let line;
1420
+ if (element.shape === ArrowLineShape.curve) {
1421
+ line = PlaitBoard.getRoughSVG(board).curve(points, options);
1579
1422
  }
1580
1423
  else {
1581
- return text.id;
1424
+ line = drawLinearPath(points, options);
1582
1425
  }
1583
- };
1584
- const getGeometryAlign = (board, element) => {
1585
- if (isMultipleTextGeometry(element)) {
1586
- const drawShapeText = element.texts.find((item) => item.id.includes(GeometryCommonTextKeys.content));
1587
- return drawShapeText?.text.align || Alignment.center;
1588
- }
1589
- if (isSingleTextGeometry(element)) {
1590
- return element.text?.align || Alignment.center;
1426
+ const id = idCreator();
1427
+ line.setAttribute('mask', `url(#${id})`);
1428
+ if (element.strokeStyle === StrokeStyle.dotted) {
1429
+ setStrokeLinecap(line, 'round');
1591
1430
  }
1592
- if (PlaitDrawElement.isElementByTable(element)) {
1593
- const firstTextCell = element.cells.find((item) => item.text);
1594
- return firstTextCell?.text?.align || Alignment.center;
1431
+ lineG.appendChild(line);
1432
+ const { mask, maskTargetFillRect } = drawArrowLineMask(board, element, id);
1433
+ lineG.appendChild(mask);
1434
+ line.appendChild(maskTargetFillRect);
1435
+ const arrow = drawArrowLineArrow(element, points, { stroke: strokeColor, strokeWidth });
1436
+ arrow && lineG.appendChild(arrow);
1437
+ return lineG;
1438
+ };
1439
+ const getHitConnection = (board, point, hitElement) => {
1440
+ let rectangle = RectangleClient.getRectangleByPoints(hitElement.points);
1441
+ const ref = getSnappingRef(board, hitElement, point);
1442
+ const connectionPoint = ref.connectorPoint || ref.edgePoint;
1443
+ return [(connectionPoint[0] - rectangle.x) / rectangle.width, (connectionPoint[1] - rectangle.y) / rectangle.height];
1444
+ };
1445
+ const getHitConnectorPoint = (point, hitElement) => {
1446
+ const rectangle = RectangleClient.getRectangleByPoints(hitElement.points);
1447
+ const shape = getElementShape(hitElement);
1448
+ const connectorPoints = getEngine(shape).getConnectorPoints(rectangle);
1449
+ return connectorPoints.find((connectorPoint) => {
1450
+ return distanceBetweenPointAndPoint(...connectorPoint, ...point) <= LINE_SNAPPING_CONNECTOR_BUFFER;
1451
+ });
1452
+ };
1453
+ const getArrowLineTextRectangle = (board, element, index) => {
1454
+ const text = element.texts[index];
1455
+ const elbowPoints = getArrowLinePoints(board, element);
1456
+ const point = getPointOnPolyline(elbowPoints, text.position);
1457
+ return {
1458
+ x: point[0] - text.width / 2,
1459
+ y: point[1] - text.height / 2,
1460
+ width: text.width,
1461
+ height: text.height
1462
+ };
1463
+ };
1464
+ const getArrowLines = (board) => {
1465
+ return findElements(board, {
1466
+ match: (element) => PlaitDrawElement.isArrowLine(element),
1467
+ recursion: (element) => PlaitDrawElement.isDrawElement(element)
1468
+ });
1469
+ };
1470
+ // quadratic Bezier to cubic Bezier
1471
+ const Q2C = (points) => {
1472
+ const result = [];
1473
+ const numSegments = points.length / 3;
1474
+ for (let i = 0; i < numSegments; i++) {
1475
+ const start = points[i];
1476
+ const qControl = points[i + 1];
1477
+ const end = points[i + 2];
1478
+ const startDistance = distanceBetweenPointAndPoint(...start, ...qControl);
1479
+ const endDistance = distanceBetweenPointAndPoint(...end, ...qControl);
1480
+ const cControl1 = getExtendPoint(start, qControl, (startDistance * 2) / 3);
1481
+ const cControl2 = getExtendPoint(end, qControl, (endDistance * 2) / 3);
1482
+ result.push(start, cControl1, cControl2, end);
1595
1483
  }
1596
- return Alignment.center;
1484
+ return result;
1597
1485
  };
1598
- const isClosedPoints = (points) => {
1599
- const startPoint = points[0];
1600
- const endPoint = points[points.length - 1];
1601
- return startPoint[0] === endPoint[0] && startPoint[1] === endPoint[1];
1486
+ const handleArrowLineCreating = (board, lineShape, sourcePoint, movingPoint, sourceElement, lineShapeG) => {
1487
+ const hitElement = getSnappingShape(board, movingPoint);
1488
+ const targetConnection = hitElement ? getHitConnection(board, movingPoint, hitElement) : undefined;
1489
+ const sourceConnection = sourceElement ? getHitConnection(board, sourcePoint, sourceElement) : undefined;
1490
+ const targetBoundId = hitElement ? hitElement.id : undefined;
1491
+ const lineGenerator = new ArrowLineShapeGenerator(board);
1492
+ const memorizedLatest = getLineMemorizedLatest();
1493
+ let sourceMarker, targetMarker;
1494
+ sourceMarker = memorizedLatest.source;
1495
+ targetMarker = memorizedLatest.target;
1496
+ sourceMarker && delete memorizedLatest.source;
1497
+ targetMarker && delete memorizedLatest.target;
1498
+ const temporaryLineElement = createArrowLineElement(lineShape, [sourcePoint, movingPoint], { marker: sourceMarker || ArrowLineMarkerType.none, connection: sourceConnection, boundId: sourceElement?.id }, { marker: targetMarker || ArrowLineMarkerType.arrow, connection: targetConnection, boundId: targetBoundId }, [], {
1499
+ strokeWidth: DefaultLineStyle.strokeWidth,
1500
+ ...memorizedLatest
1501
+ });
1502
+ const linePoints = getArrowLinePoints(board, temporaryLineElement);
1503
+ const otherPoint = linePoints[0];
1504
+ temporaryLineElement.points[1] = alignPoints(otherPoint, movingPoint);
1505
+ lineGenerator.processDrawing(temporaryLineElement, lineShapeG);
1506
+ PlaitBoard.getElementTopHost(board).append(lineShapeG);
1507
+ return temporaryLineElement;
1602
1508
  };
1509
+ function drawArrowLineMask(board, element, id) {
1510
+ const mask = createMask();
1511
+ mask.setAttribute('id', id);
1512
+ const points = getArrowLinePoints(board, element);
1513
+ let rectangle = RectangleClient.getRectangleByPoints(points);
1514
+ rectangle = RectangleClient.getOutlineRectangle(rectangle, -30);
1515
+ const maskFillRect = createRect(rectangle, {
1516
+ fill: 'white'
1517
+ });
1518
+ mask.appendChild(maskFillRect);
1519
+ const texts = element.texts;
1520
+ texts.forEach((text, index) => {
1521
+ let textRectangle = getArrowLineTextRectangle(board, element, index);
1522
+ textRectangle = RectangleClient.inflate(textRectangle, LINE_TEXT_SPACE * 2);
1523
+ const rect = createRect(textRectangle, {
1524
+ fill: 'black'
1525
+ });
1526
+ mask.appendChild(rect);
1527
+ });
1528
+ // open line
1529
+ const maskTargetFillRect = createRect(rectangle);
1530
+ maskTargetFillRect.setAttribute('opacity', '0');
1531
+ maskTargetFillRect.setAttribute('fill', 'none');
1532
+ return { mask, maskTargetFillRect };
1533
+ }
1603
1534
 
1604
- const getStrokeColorByElement = (board, element) => {
1605
- const defaultColor = getDrawDefaultStrokeColor(board.theme.themeColorMode);
1606
- const strokeColor = element.strokeColor || defaultColor;
1607
- return strokeColor;
1535
+ const getHitArrowLineTextIndex = (board, element, point) => {
1536
+ const texts = element.texts;
1537
+ if (!texts.length)
1538
+ return -1;
1539
+ const points = getArrowLinePoints(board, element);
1540
+ return texts.findIndex(text => {
1541
+ const center = getPointOnPolyline(points, text.position);
1542
+ const rectangle = {
1543
+ x: center[0] - text.width / 2,
1544
+ y: center[1] - text.height / 2,
1545
+ width: text.width,
1546
+ height: text.height
1547
+ };
1548
+ return RectangleClient.isHit(rectangle, RectangleClient.getRectangleByPoints([point, point]));
1549
+ });
1608
1550
  };
1609
- const getFillByElement = (board, element) => {
1610
- const defaultFill = PlaitDrawElement.isFlowchart(element) && isClosedDrawElement(element)
1611
- ? getFlowchartDefaultFill(board.theme.themeColorMode)
1612
- : DefaultDrawStyle.fill;
1613
- const fill = element.fill || defaultFill;
1614
- return fill;
1551
+
1552
+ const isMultipleTextShape = (shape) => {
1553
+ return GEOMETRY_WITH_MULTIPLE_TEXT.includes(shape);
1615
1554
  };
1616
- const getStrokeStyleByElement = (board, element) => {
1617
- return element.strokeStyle || StrokeStyle.solid;
1555
+ const isMultipleTextGeometry = (geometry) => {
1556
+ return PlaitDrawElement.isGeometry(geometry) && isMultipleTextShape(geometry.shape);
1618
1557
  };
1619
-
1620
- const debugKey$3 = 'debug:plait:line-mirror';
1621
- const debugGenerator$3 = createDebugGenerator(debugKey$3);
1622
- const alignPoints = (basePoint, movingPoint) => {
1623
- const newPoint = [...movingPoint];
1624
- if (Point.isVertical(newPoint, basePoint, LINE_ALIGN_TOLERANCE)) {
1625
- newPoint[0] = basePoint[0];
1626
- }
1627
- if (Point.isHorizontal(newPoint, basePoint, LINE_ALIGN_TOLERANCE)) {
1628
- newPoint[1] = basePoint[1];
1629
- }
1630
- return newPoint;
1558
+ const getMultipleTextGeometryTextKeys = (shape) => {
1559
+ return MultipleTextGeometryTextKeys[shape];
1631
1560
  };
1632
- function getResizedPreviousAndNextPoint(nextRenderPoints, sourcePoint, targetPoint, handleIndex) {
1633
- const referencePoint = {
1634
- previous: null,
1635
- next: null
1561
+ const createMultipleTextGeometryElement = (shape, points, options = {}) => {
1562
+ const id = idCreator();
1563
+ const drawShapeTexts = buildDefaultTextsByShape(shape);
1564
+ return {
1565
+ id,
1566
+ type: 'geometry',
1567
+ shape,
1568
+ angle: 0,
1569
+ opacity: 1,
1570
+ texts: drawShapeTexts,
1571
+ points,
1572
+ ...options
1636
1573
  };
1637
- const startPoint = nextRenderPoints[handleIndex];
1638
- const endPoint = nextRenderPoints[handleIndex + 1];
1639
- const isHorizontal = Point.isHorizontal(startPoint, endPoint);
1640
- const isVertical = Point.isVertical(startPoint, endPoint);
1641
- const previousPoint = nextRenderPoints[handleIndex - 1] ?? nextRenderPoints[0];
1642
- const beforePreviousPoint = nextRenderPoints[handleIndex - 2] ?? sourcePoint;
1643
- if ((isHorizontal && Point.isHorizontal(beforePreviousPoint, previousPoint)) ||
1644
- (isVertical && Point.isVertical(beforePreviousPoint, previousPoint))) {
1645
- referencePoint.previous = previousPoint;
1574
+ };
1575
+ const buildDefaultTextsByShape = (shape) => {
1576
+ const memorizedLatest = getMemorizedLatestByPointer(shape);
1577
+ const textProperties = { ...memorizedLatest.textProperties };
1578
+ const alignment = textProperties?.align;
1579
+ const textHeight = textProperties?.textHeight || DefaultTextProperty.height;
1580
+ delete textProperties?.align;
1581
+ delete textProperties?.textHeight;
1582
+ const defaultTexts = getDefaultGeometryProperty(shape)?.texts || [];
1583
+ const textKeys = getMultipleTextGeometryTextKeys(shape);
1584
+ return (textKeys || []).map((textKey) => {
1585
+ const text = defaultTexts?.find((item) => item?.key === textKey);
1586
+ return {
1587
+ id: textKey,
1588
+ text: buildText(text?.text || '', alignment || text?.align || Alignment.center, textProperties),
1589
+ textHeight: textHeight
1590
+ };
1591
+ });
1592
+ };
1593
+ const getHitMultipleGeometryText = (element, point) => {
1594
+ const engine = getEngine(element.shape);
1595
+ const rectangle = RectangleClient.getRectangleByPoints([point, point]);
1596
+ let hitText;
1597
+ if (engine.getTextRectangle) {
1598
+ hitText = element.texts.find(text => {
1599
+ const textRectangle = engine.getTextRectangle(element, { id: text.id });
1600
+ return RectangleClient.isHit(rectangle, textRectangle);
1601
+ });
1646
1602
  }
1647
- const nextPoint = nextRenderPoints[handleIndex + 2] ?? nextRenderPoints[nextRenderPoints.length - 1];
1648
- const afterNextPoint = nextRenderPoints[handleIndex + 3] ?? targetPoint;
1649
- if ((isHorizontal && Point.isHorizontal(nextPoint, afterNextPoint)) || (isVertical && Point.isVertical(nextPoint, afterNextPoint))) {
1650
- referencePoint.next = nextPoint;
1603
+ return hitText;
1604
+ };
1605
+
1606
+ class VectorLineShapeGenerator extends Generator {
1607
+ canDraw(element) {
1608
+ return true;
1609
+ }
1610
+ draw(element) {
1611
+ let lineG;
1612
+ lineG = drawVectorLine(this.board, element);
1613
+ return lineG;
1651
1614
  }
1652
- return referencePoint;
1653
1615
  }
1654
- function alignElbowSegment(startKeyPoint, endKeyPoint, resizeState, resizedPreviousAndNextPoint) {
1655
- let newStartPoint = startKeyPoint;
1656
- let newEndPoint = endKeyPoint;
1657
- if (Point.isHorizontal(startKeyPoint, endKeyPoint)) {
1658
- const offsetY = Point.getOffsetY(resizeState.startPoint, resizeState.endPoint);
1659
- let pointY = startKeyPoint[1] + offsetY;
1660
- if (resizedPreviousAndNextPoint.previous && Math.abs(resizedPreviousAndNextPoint.previous[1] - pointY) < LINE_ALIGN_TOLERANCE) {
1661
- pointY = resizedPreviousAndNextPoint.previous[1];
1616
+
1617
+ const getVectorLinePoints = (board, element) => {
1618
+ switch (element.shape) {
1619
+ case VectorLineShape.straight: {
1620
+ return element.points;
1662
1621
  }
1663
- else if (resizedPreviousAndNextPoint.next && Math.abs(resizedPreviousAndNextPoint.next[1] - pointY) < LINE_ALIGN_TOLERANCE) {
1664
- pointY = resizedPreviousAndNextPoint.next[1];
1622
+ case VectorLineShape.curve: {
1623
+ if (element.points.length === 2) {
1624
+ return pointsOnBezierCurves(element.points);
1625
+ }
1626
+ else {
1627
+ let dataPoints = element.points;
1628
+ const points = catmullRomFitting(dataPoints);
1629
+ return pointsOnBezierCurves(points);
1630
+ }
1665
1631
  }
1666
- newStartPoint = [startKeyPoint[0], pointY];
1667
- newEndPoint = [endKeyPoint[0], pointY];
1668
- }
1669
- if (Point.isVertical(startKeyPoint, endKeyPoint)) {
1670
- const offsetX = Point.getOffsetX(resizeState.startPoint, resizeState.endPoint);
1671
- let pointX = startKeyPoint[0] + offsetX;
1672
- if (resizedPreviousAndNextPoint.previous && Math.abs(resizedPreviousAndNextPoint.previous[0] - pointX) < LINE_ALIGN_TOLERANCE) {
1673
- pointX = resizedPreviousAndNextPoint.previous[0];
1674
- }
1675
- else if (resizedPreviousAndNextPoint.next && Math.abs(resizedPreviousAndNextPoint.next[0] - pointX) < LINE_ALIGN_TOLERANCE) {
1676
- pointX = resizedPreviousAndNextPoint.next[0];
1677
- }
1678
- newStartPoint = [pointX, startKeyPoint[1]];
1679
- newEndPoint = [pointX, endKeyPoint[1]];
1632
+ default:
1633
+ return null;
1680
1634
  }
1681
- return [newStartPoint, newEndPoint];
1682
- }
1683
- function getIndexAndDeleteCountByKeyPoint(board, element, dataPoints, nextRenderPoints, handleIndex) {
1684
- let index = null;
1685
- let deleteCount = null;
1686
- const startKeyPoint = nextRenderPoints[handleIndex];
1687
- const endKeyPoint = nextRenderPoints[handleIndex + 1];
1688
- if (!startKeyPoint || !endKeyPoint) {
1689
- return {
1690
- index,
1691
- deleteCount
1692
- };
1635
+ };
1636
+ const createVectorLineElement = (shape, points, options) => {
1637
+ return {
1638
+ id: idCreator(),
1639
+ type: 'vector-line',
1640
+ shape,
1641
+ opacity: 1,
1642
+ points,
1643
+ ...options
1644
+ };
1645
+ };
1646
+ const vectorLineCreating = (board, lineShape, points, movingPoint, lineShapeG) => {
1647
+ const lineGenerator = new VectorLineShapeGenerator(board);
1648
+ const memorizedLatest = getLineMemorizedLatest();
1649
+ const temporaryLineElement = createVectorLineElement(lineShape, [...points, movingPoint], {
1650
+ strokeWidth: DefaultLineStyle.strokeWidth,
1651
+ ...memorizedLatest
1652
+ });
1653
+ const otherPoint = points[points.length - 1];
1654
+ temporaryLineElement.points[temporaryLineElement.points.length - 1] = alignPoints(otherPoint, movingPoint);
1655
+ lineGenerator.processDrawing(temporaryLineElement, lineShapeG);
1656
+ PlaitBoard.getElementTopHost(board).append(lineShapeG);
1657
+ return temporaryLineElement;
1658
+ };
1659
+ const drawVectorLine = (board, element) => {
1660
+ const strokeWidth = getStrokeWidthByElement(element);
1661
+ const strokeColor = getStrokeColorByElement(board, element);
1662
+ const strokeStyle = getStrokeStyleByElement(board, element);
1663
+ const strokeLineDash = getStrokeLineDash(strokeStyle, strokeWidth);
1664
+ const fill = getFillByElement(board, element);
1665
+ const options = { stroke: strokeColor, strokeWidth, strokeLineDash, fill };
1666
+ const lineG = createG();
1667
+ let points = getVectorLinePoints(board, element);
1668
+ const line = drawLinearPath(points, options);
1669
+ const id = idCreator();
1670
+ line.setAttribute('mask', `url(#${id})`);
1671
+ if (element.strokeStyle === StrokeStyle.dotted) {
1672
+ setStrokeLinecap(line, 'round');
1693
1673
  }
1694
- const midDataPoints = dataPoints.slice(1, -1);
1695
- const startIndex = midDataPoints.findIndex(item => Point.isEquals(item, startKeyPoint));
1696
- const endIndex = midDataPoints.findIndex(item => Point.isEquals(item, endKeyPoint));
1697
- if (Math.max(startIndex, endIndex) > -1) {
1698
- if (startIndex > -1 && endIndex > -1) {
1699
- return {
1700
- index: startIndex,
1701
- deleteCount: 2
1702
- };
1703
- }
1704
- if (startIndex > -1 && endIndex === -1) {
1705
- const isReplace = startIndex < midDataPoints.length - 1 &&
1706
- Point.isAlign([midDataPoints[startIndex], midDataPoints[startIndex + 1], startKeyPoint, endKeyPoint]);
1707
- if (isReplace) {
1708
- return {
1709
- index: startIndex,
1710
- deleteCount: 2
1711
- };
1712
- }
1713
- return {
1714
- index: startIndex,
1715
- deleteCount: 1
1716
- };
1717
- }
1718
- if (startIndex === -1 && endIndex > -1) {
1719
- const isReplace = endIndex > 0 && Point.isAlign([midDataPoints[endIndex], midDataPoints[endIndex - 1], startKeyPoint, endKeyPoint]);
1720
- if (isReplace) {
1721
- return {
1722
- index: endIndex - 1,
1723
- deleteCount: 2
1724
- };
1725
- }
1726
- return {
1727
- index: endIndex,
1728
- deleteCount: 1
1729
- };
1730
- }
1674
+ lineG.appendChild(line);
1675
+ return lineG;
1676
+ };
1677
+
1678
+ const getCenterPointsOnPolygon$1 = (points) => {
1679
+ const centerPoints = [];
1680
+ for (let i = 0; i < points.length; i++) {
1681
+ let j = i == points.length - 1 ? 0 : i + 1;
1682
+ centerPoints.push([(points[i][0] + points[j][0]) / 2, (points[i][1] + points[j][1]) / 2]);
1731
1683
  }
1732
- else {
1733
- for (let i = 0; i < midDataPoints.length - 1; i++) {
1734
- const currentPoint = midDataPoints[i];
1735
- const nextPoint = midDataPoints[i + 1];
1736
- if (Point.isAlign([currentPoint, nextPoint, startKeyPoint, endKeyPoint])) {
1737
- index = i;
1738
- deleteCount = 2;
1739
- break;
1740
- }
1741
- if (Point.isAlign([currentPoint, nextPoint, startKeyPoint])) {
1742
- index = Math.min(i + 1, midDataPoints.length - 1);
1743
- deleteCount = 1;
1744
- break;
1745
- }
1746
- if (Point.isAlign([currentPoint, nextPoint, endKeyPoint])) {
1747
- index = Math.max(i - 1, 0);
1748
- deleteCount = 1;
1749
- break;
1750
- }
1751
- }
1684
+ return centerPoints;
1685
+ };
1686
+ const getCrossingPointBetweenPointAndPolygon = (corners, point) => {
1687
+ const result = [];
1688
+ for (let index = 1; index <= corners.length; index++) {
1689
+ let start = corners[index - 1];
1690
+ let end = index === corners.length ? corners[0] : corners[index];
1691
+ const crossingPoint = getCrossingPointsBetweenPointAndSegment(point, start, end);
1692
+ result.push(...crossingPoint);
1752
1693
  }
1753
- if (index === null) {
1754
- deleteCount = 0;
1755
- if (midDataPoints.length > 0) {
1756
- const handleRefPair = getArrowLineHandleRefPair(board, element);
1757
- const params = getElbowLineRouteOptions(board, element, handleRefPair);
1758
- const keyPoints = removeDuplicatePoints(generateElbowLineRoute(params, board));
1759
- const nextKeyPoints = simplifyOrthogonalPoints(keyPoints.slice(1, keyPoints.length - 1));
1760
- const nextDataPoints = [nextRenderPoints[0], ...midDataPoints, nextRenderPoints[nextRenderPoints.length - 1]];
1761
- const mirrorDataPoints = getMirrorDataPoints(board, nextDataPoints, nextKeyPoints, params);
1762
- for (let i = handleIndex - 1; i >= 0; i--) {
1763
- const previousIndex = mirrorDataPoints.slice(1, -1).findIndex(item => Point.isEquals(item, nextRenderPoints[i]));
1764
- if (previousIndex > -1) {
1765
- index = previousIndex + 1;
1766
- break;
1767
- }
1768
- }
1769
- if (index === null) {
1770
- index = 0;
1771
- // When renderPoints is a straight line and dataPoints are not on the line,
1772
- // the default 'deleteCount' is set to midDataPoints.length.
1773
- if (Point.isAlign(nextRenderPoints)) {
1774
- deleteCount = midDataPoints.length;
1775
- }
1776
- }
1777
- }
1778
- else {
1779
- index = 0;
1694
+ return result;
1695
+ };
1696
+ const getPolygonEdgeByConnectionPoint = (corners, point) => {
1697
+ for (let index = 1; index <= corners.length; index++) {
1698
+ let start = corners[index - 1];
1699
+ let end = index === corners.length ? corners[0] : corners[index];
1700
+ if (isPointOnSegment(point, start, end)) {
1701
+ return [start, end];
1780
1702
  }
1781
1703
  }
1782
- return {
1783
- index,
1784
- deleteCount
1785
- };
1786
- }
1787
- function getMirrorDataPoints(board, nextDataPoints, nextKeyPoints, params) {
1788
- for (let index = 1; index < nextDataPoints.length - 2; index++) {
1789
- adjustByCustomPointStartIndex(board, index, nextDataPoints, nextKeyPoints, params);
1790
- }
1791
- return nextDataPoints;
1792
- }
1793
- /**
1794
- * adjust based parallel segment
1795
- */
1796
- const adjustByCustomPointStartIndex = (board, customPointStartIndex, nextDataPoints, nextKeyPoints, params) => {
1797
- const beforePoint = nextDataPoints[customPointStartIndex - 1];
1798
- const startPoint = nextDataPoints[customPointStartIndex];
1799
- const endPoint = nextDataPoints[customPointStartIndex + 1];
1800
- const afterPoint = nextDataPoints[customPointStartIndex + 2];
1801
- const beforeSegment = [beforePoint, startPoint];
1802
- const afterSegment = [endPoint, afterPoint];
1803
- const isStraightWithBefore = Point.isAlign(beforeSegment);
1804
- const isStraightWithAfter = Point.isAlign(afterSegment);
1805
- let isAdjustStart = false;
1806
- let isAdjustEnd = false;
1807
- if (!isStraightWithBefore || !isStraightWithAfter) {
1808
- const midKeyPointsWithBefore = getMidKeyPoints(nextKeyPoints, beforeSegment[0], beforeSegment[1]);
1809
- const midKeyPointsWithAfter = getMidKeyPoints(nextKeyPoints, afterSegment[0], afterSegment[1]);
1810
- const hasMidKeyPoints = midKeyPointsWithBefore.length > 0 && midKeyPointsWithAfter.length > 0;
1811
- isAdjustStart = !isStraightWithBefore && !hasMidKeyPoints;
1812
- isAdjustEnd = !isStraightWithAfter && !hasMidKeyPoints;
1813
- }
1814
- if (isAdjustStart || isAdjustEnd) {
1815
- const parallelSegment = [startPoint, endPoint];
1816
- const parallelSegments = findOrthogonalParallelSegments(parallelSegment, nextKeyPoints);
1817
- const mirrorSegments = findMirrorSegments(board, parallelSegment, parallelSegments, params.sourceRectangle, params.targetRectangle);
1818
- if (mirrorSegments.length === 1) {
1819
- const mirrorSegment = mirrorSegments[0];
1820
- if (isAdjustStart) {
1821
- nextDataPoints.splice(customPointStartIndex, 1, mirrorSegment[0]);
1822
- }
1823
- if (isAdjustEnd) {
1824
- nextDataPoints.splice(customPointStartIndex + 1, 1, mirrorSegment[1]);
1825
- }
1704
+ return null;
1705
+ };
1706
+
1707
+ function generateCloudPath(rectangle) {
1708
+ const divisionWidth = rectangle.width / 7;
1709
+ const divisionHeight = rectangle.height / 3.2;
1710
+ const xRadius = divisionWidth / 8.5;
1711
+ const yRadius = divisionHeight / 20;
1712
+ const startPoint = [rectangle.x + divisionWidth, rectangle.y + divisionHeight];
1713
+ const arcCommands = [
1714
+ {
1715
+ rx: xRadius,
1716
+ ry: yRadius * 1.2,
1717
+ xAxisRotation: 0,
1718
+ largeArcFlag: 1,
1719
+ sweepFlag: 1,
1720
+ endX: rectangle.x + divisionWidth * 2,
1721
+ endY: rectangle.y + divisionHeight / 2
1722
+ },
1723
+ {
1724
+ rx: xRadius,
1725
+ ry: yRadius,
1726
+ xAxisRotation: 0,
1727
+ largeArcFlag: 1,
1728
+ sweepFlag: 1,
1729
+ endX: rectangle.x + divisionWidth * 4.2,
1730
+ endY: rectangle.y + divisionHeight / 2.2
1731
+ },
1732
+ {
1733
+ rx: xRadius,
1734
+ ry: yRadius,
1735
+ xAxisRotation: 0,
1736
+ largeArcFlag: 1,
1737
+ sweepFlag: 1,
1738
+ endX: rectangle.x + divisionWidth * 5.8,
1739
+ endY: rectangle.y + divisionHeight
1740
+ },
1741
+ {
1742
+ rx: xRadius,
1743
+ ry: yRadius * 1.3,
1744
+ xAxisRotation: 0,
1745
+ largeArcFlag: 1,
1746
+ sweepFlag: 1,
1747
+ endX: rectangle.x + divisionWidth * 6,
1748
+ endY: rectangle.y + divisionHeight * 2.2
1749
+ },
1750
+ {
1751
+ rx: xRadius,
1752
+ ry: yRadius * 1.2,
1753
+ xAxisRotation: 0,
1754
+ largeArcFlag: 1,
1755
+ sweepFlag: 1,
1756
+ endX: rectangle.x + divisionWidth * 5,
1757
+ endY: rectangle.y + divisionHeight * 2.8
1758
+ },
1759
+ {
1760
+ rx: xRadius,
1761
+ ry: yRadius / 1.2,
1762
+ xAxisRotation: 0,
1763
+ largeArcFlag: 1,
1764
+ sweepFlag: 1,
1765
+ endX: rectangle.x + divisionWidth * 2.8,
1766
+ endY: rectangle.y + divisionHeight * 2.8
1767
+ },
1768
+ {
1769
+ rx: xRadius,
1770
+ ry: yRadius,
1771
+ xAxisRotation: 0,
1772
+ largeArcFlag: 1,
1773
+ sweepFlag: 1,
1774
+ endX: rectangle.x + divisionWidth,
1775
+ endY: rectangle.y + divisionHeight * 2.2
1776
+ },
1777
+ {
1778
+ rx: xRadius,
1779
+ ry: yRadius * 1.42,
1780
+ xAxisRotation: 0,
1781
+ largeArcFlag: 1,
1782
+ sweepFlag: 1,
1783
+ endX: rectangle.x + divisionWidth,
1784
+ endY: rectangle.y + divisionHeight
1826
1785
  }
1827
- else {
1828
- const isHorizontal = Point.isHorizontal(startPoint, endPoint);
1829
- const adjustIndex = isHorizontal ? 0 : 1;
1830
- if (isAdjustStart) {
1831
- const newStartPoint = [startPoint[0], startPoint[1]];
1832
- newStartPoint[adjustIndex] = beforePoint[adjustIndex];
1833
- nextDataPoints.splice(customPointStartIndex, 1, newStartPoint);
1834
- }
1835
- if (isAdjustEnd) {
1836
- const newEndPoint = [endPoint[0], endPoint[1]];
1837
- newEndPoint[adjustIndex] = afterPoint[adjustIndex];
1838
- nextDataPoints.splice(customPointStartIndex + 1, 1, newEndPoint);
1786
+ ];
1787
+ return { startPoint, arcCommands };
1788
+ }
1789
+ const CloudEngine = {
1790
+ draw(board, rectangle, options) {
1791
+ const rs = PlaitBoard.getRoughSVG(board);
1792
+ const { startPoint, arcCommands } = generateCloudPath(rectangle);
1793
+ const pathData = `M ${startPoint[0]} ${startPoint[1]} ` +
1794
+ arcCommands
1795
+ .map((command) => `A ${command.rx} ${command.ry} ${command.xAxisRotation} ${command.largeArcFlag} ${command.sweepFlag} ${command.endX} ${command.endY}`)
1796
+ .join('\n') +
1797
+ ' Z';
1798
+ const svgElement = rs.path(pathData, { ...options, fillStyle: 'solid' });
1799
+ setPathStrokeLinecap(svgElement, 'round');
1800
+ return svgElement;
1801
+ },
1802
+ isInsidePoint(rectangle, point) {
1803
+ const rangeRectangle = RectangleClient.getRectangleByPoints([point, point]);
1804
+ return RectangleClient.isHit(rectangle, rangeRectangle);
1805
+ },
1806
+ getCornerPoints(rectangle) {
1807
+ return RectangleClient.getCornerPoints(rectangle);
1808
+ },
1809
+ getNearestPoint(rectangle, point) {
1810
+ const { startPoint, arcCommands } = generateCloudPath(rectangle);
1811
+ let minDistance = Infinity;
1812
+ let nearestPoint = point;
1813
+ let currentStart = startPoint;
1814
+ for (const arcCommand of arcCommands) {
1815
+ const arcNearestPoint = getNearestPointBetweenPointAndArc(point, currentStart, arcCommand);
1816
+ const distance = distanceBetweenPointAndPoint(point[0], point[1], arcNearestPoint[0], arcNearestPoint[1]);
1817
+ if (distance < minDistance) {
1818
+ minDistance = distance;
1819
+ nearestPoint = arcNearestPoint;
1839
1820
  }
1821
+ currentStart = [arcCommand.endX, arcCommand.endY];
1840
1822
  }
1823
+ return nearestPoint;
1824
+ },
1825
+ getEdgeByConnectionPoint(rectangle, pointOfRectangle) {
1826
+ const corners = CloudEngine.getCornerPoints(rectangle);
1827
+ const point = RectangleClient.getConnectionPoint(rectangle, pointOfRectangle);
1828
+ return getPolygonEdgeByConnectionPoint(corners, point);
1829
+ },
1830
+ getConnectorPoints(rectangle) {
1831
+ return RectangleClient.getEdgeCenterPoints(rectangle);
1832
+ },
1833
+ getTextRectangle(element) {
1834
+ const elementRectangle = RectangleClient.getRectangleByPoints(element.points);
1835
+ const strokeWidth = getStrokeWidthByElement(element);
1836
+ const height = element.textHeight;
1837
+ const originWidth = elementRectangle.width - ShapeDefaultSpace.rectangleAndText * 2 - strokeWidth * 2;
1838
+ const width = originWidth / 1.5;
1839
+ return {
1840
+ height,
1841
+ width: width > 0 ? width : 0,
1842
+ x: elementRectangle.x + ShapeDefaultSpace.rectangleAndText + strokeWidth + originWidth / 6,
1843
+ y: elementRectangle.y + elementRectangle.height / 6 + ((elementRectangle.height * 4) / 6 - height) / 2
1844
+ };
1841
1845
  }
1842
1846
  };
1843
- function isUpdatedHandleIndex(board, element, dataPoints, nextRenderPoints, handleIndex) {
1844
- const { deleteCount } = getIndexAndDeleteCountByKeyPoint(board, element, dataPoints, nextRenderPoints, handleIndex);
1845
- if (deleteCount !== null && deleteCount > 1) {
1847
+
1848
+ const isTextExceedingBounds = (geometry) => {
1849
+ const client = RectangleClient.getRectangleByPoints(geometry.points);
1850
+ if (geometry.textHeight && geometry.textHeight > client.height) {
1846
1851
  return true;
1847
1852
  }
1848
1853
  return false;
1849
- }
1850
- function getMidKeyPoints(simplifiedNextKeyPoints, startPoint, endPoint) {
1851
- let midElbowPoints = [];
1852
- let startPointIndex = -1;
1853
- let endPointIndex = -1;
1854
- for (let i = 0; i < simplifiedNextKeyPoints.length; i++) {
1855
- if (Point.isAlign([simplifiedNextKeyPoints[i], startPoint])) {
1856
- startPointIndex = i;
1857
- }
1858
- if (startPointIndex > -1 && Point.isAlign([simplifiedNextKeyPoints[i], endPoint])) {
1859
- endPointIndex = i;
1860
- break;
1861
- }
1854
+ };
1855
+ const isHitArrowLineText = (board, element, point) => {
1856
+ return getHitArrowLineTextIndex(board, element, point) !== -1;
1857
+ };
1858
+ const isHitPolyLine = (pathPoints, point) => {
1859
+ const distance = distanceBetweenPointAndSegments(point, pathPoints);
1860
+ return distance <= HIT_DISTANCE_BUFFER;
1861
+ };
1862
+ const isHitArrowLine = (board, element, point) => {
1863
+ const points = getArrowLinePoints(board, element);
1864
+ const isHitText = isHitArrowLineText(board, element, point);
1865
+ return isHitText || isHitPolyLine(points, point);
1866
+ };
1867
+ const isHitVectorLine = (board, element, point) => {
1868
+ const points = getVectorLinePoints(board, element);
1869
+ if (isClosedPoints(element.points)) {
1870
+ return isPointInPolygon(point, points) || isHitPolyLine(points, point);
1862
1871
  }
1863
- if (startPointIndex > -1 && endPointIndex > -1) {
1864
- midElbowPoints = simplifiedNextKeyPoints.slice(startPointIndex, endPointIndex + 1);
1872
+ else {
1873
+ return isHitPolyLine(points, point);
1865
1874
  }
1866
- return midElbowPoints;
1867
- }
1868
- function findOrthogonalParallelSegments(segment, keyPoints) {
1869
- const isHorizontalSegment = Point.isHorizontal(segment[0], segment[1]);
1870
- const parallelSegments = [];
1871
- for (let i = 0; i < keyPoints.length - 1; i++) {
1872
- const current = keyPoints[i];
1873
- const next = keyPoints[i + 1];
1874
- const isHorizontal = Point.isHorizontal(current, next, 0.1);
1875
- if (isHorizontalSegment && isHorizontal) {
1876
- parallelSegments.push([current, next]);
1877
- }
1878
- if (!isHorizontalSegment && !isHorizontal) {
1879
- parallelSegments.push([current, next]);
1880
- }
1875
+ };
1876
+ const isRectangleHitElementText = (element, rectangle) => {
1877
+ const engine = getEngine(element.shape);
1878
+ if (isMultipleTextGeometry(element)) {
1879
+ const texts = element.texts;
1880
+ return texts.some((item) => {
1881
+ const textClient = engine.getTextRectangle(element, { id: item.id });
1882
+ return isRectangleHitRotatedPoints(rectangle, RectangleClient.getCornerPoints(textClient), element.angle);
1883
+ });
1881
1884
  }
1882
- return parallelSegments;
1883
- }
1884
- function findMirrorSegments(board, segment, parallelSegments, sourceRectangle, targetRectangle) {
1885
- debugGenerator$3.isDebug() && debugGenerator$3.clear();
1886
- const mirrorSegments = [];
1887
- for (let index = 0; index < parallelSegments.length; index++) {
1888
- const parallelPath = parallelSegments[index];
1889
- const startPoint = [segment[0][0], segment[0][1]];
1890
- const endPoint = [segment[1][0], segment[1][1]];
1891
- const isHorizontal = Point.isHorizontal(startPoint, endPoint);
1892
- const adjustDataIndex = isHorizontal ? 0 : 1;
1893
- startPoint[adjustDataIndex] = parallelPath[0][adjustDataIndex];
1894
- endPoint[adjustDataIndex] = parallelPath[1][adjustDataIndex];
1895
- const fakeRectangle = RectangleClient.getRectangleByPoints([startPoint, endPoint, ...parallelPath]);
1896
- const isValid = !RectangleClient.isHit(fakeRectangle, sourceRectangle) && !RectangleClient.isHit(fakeRectangle, targetRectangle);
1897
- if (isValid) {
1898
- mirrorSegments.push([startPoint, endPoint]);
1899
- debugGenerator$3.isDebug() && debugGenerator$3.drawPolygon(board, RectangleClient.getCornerPoints(fakeRectangle));
1900
- }
1885
+ else {
1886
+ const textClient = engine.getTextRectangle ? engine.getTextRectangle(element) : getTextRectangle(element);
1887
+ return isRectangleHitRotatedPoints(rectangle, RectangleClient.getCornerPoints(textClient), element.angle);
1901
1888
  }
1902
- return mirrorSegments;
1903
- }
1904
- const hasIllegalElbowPoint = (midDataPoints) => {
1905
- if (midDataPoints.length === 1) {
1889
+ };
1890
+ const isHitElementText = (element, point) => {
1891
+ const engine = getEngine(element.shape);
1892
+ if (isMultipleTextGeometry(element)) {
1893
+ const texts = element.texts;
1894
+ return texts.some((item) => {
1895
+ const textClient = engine.getTextRectangle(element, { id: item.id });
1896
+ return RectangleClient.isPointInRectangle(textClient, point);
1897
+ });
1898
+ }
1899
+ else {
1900
+ const textClient = engine.getTextRectangle ? engine.getTextRectangle(element) : getTextRectangle(element);
1901
+ return RectangleClient.isPointInRectangle(textClient, point);
1902
+ }
1903
+ };
1904
+ const isEmptyTextElement = (element) => {
1905
+ if (!isDrawElementIncludeText(element)) {
1906
1906
  return true;
1907
1907
  }
1908
- return midDataPoints.some((item, index) => {
1909
- const beforePoint = midDataPoints[index - 1];
1910
- const afterPoint = midDataPoints[index + 1];
1911
- const beforeSegment = beforePoint && [beforePoint, item];
1912
- const afterSegment = afterPoint && [item, afterPoint];
1913
- const isStraightWithBefore = beforeSegment && Point.isAlign(beforeSegment);
1914
- const isStraightWithAfter = afterSegment && Point.isAlign(afterSegment);
1915
- if (index === 0) {
1916
- return !isStraightWithAfter;
1917
- }
1918
- if (index === midDataPoints.length - 1) {
1919
- return !isStraightWithBefore;
1908
+ const editor = getFirstTextEditor(element);
1909
+ return Editor.isEmpty(editor, editor.children[0]);
1910
+ };
1911
+ const isRectangleHitDrawElement = (board, element, selection) => {
1912
+ const rangeRectangle = RectangleClient.getRectangleByPoints([selection.anchor, selection.focus]);
1913
+ if (PlaitDrawElement.isGeometry(element)) {
1914
+ const isHitElement = isRectangleHitRotatedElement(board, rangeRectangle, element);
1915
+ if (isHitElement) {
1916
+ return isHitElement;
1920
1917
  }
1921
- return !isStraightWithBefore && !isStraightWithAfter;
1922
- });
1918
+ return !isEmptyTextElement(element) && isRectangleHitElementText(element, rangeRectangle);
1919
+ }
1920
+ if (PlaitDrawElement.isImage(element)) {
1921
+ return isRectangleHitRotatedElement(board, rangeRectangle, element);
1922
+ }
1923
+ if (PlaitDrawElement.isArrowLine(element)) {
1924
+ const points = getArrowLinePoints(board, element);
1925
+ return isLineHitRectangle(points, rangeRectangle);
1926
+ }
1927
+ if (PlaitDrawElement.isVectorLine(element)) {
1928
+ const points = getVectorLinePoints(board, element);
1929
+ return isLineHitRectangle(points, rangeRectangle);
1930
+ }
1931
+ return null;
1923
1932
  };
1924
-
1925
- const ARROW_LENGTH = 20;
1926
- const drawArrowLineArrow = (element, points, options) => {
1927
- const arrowG = createG();
1928
- if (PlaitArrowLine.isSourceMark(element, ArrowLineMarkerType.none) && PlaitArrowLine.isTargetMark(element, ArrowLineMarkerType.none)) {
1929
- return null;
1933
+ const isRectangleHitRotatedElement = (board, rectangle, element) => {
1934
+ const client = RectangleClient.getRectangleByPoints(element.points);
1935
+ return isRectangleHitRotatedPoints(rectangle, RectangleClient.getCornerPoints(client), element.angle);
1936
+ };
1937
+ const isRectangleHitRotatedPoints = (rectangle, points, angle) => {
1938
+ let rotatedPoints = rotatePointsByAngle(points, angle) || points;
1939
+ return isLineHitRectangle(rotatedPoints, rectangle);
1940
+ };
1941
+ const getHitDrawElement = (board, elements) => {
1942
+ let firstFilledElement = getFirstFilledDrawElement(board, elements);
1943
+ let endIndex = elements.length;
1944
+ if (firstFilledElement) {
1945
+ endIndex = elements.indexOf(firstFilledElement) + 1;
1930
1946
  }
1931
- const strokeWidth = getStrokeWidthByElement(element);
1932
- const offset = (strokeWidth * strokeWidth) / 3;
1933
- if (points.length === 1) {
1934
- points = [points[0], [points[0][0] + 0.1, points[0][1]]];
1947
+ const newElements = elements.slice(0, endIndex);
1948
+ const element = getFirstTextOrLineElement(newElements);
1949
+ if (element) {
1950
+ return element;
1935
1951
  }
1936
- if (!PlaitArrowLine.isSourceMark(element, ArrowLineMarkerType.none)) {
1937
- const source = getExtendPoint(points[0], points[1], ARROW_LENGTH + offset);
1938
- const sourceArrow = getArrow(element, { marker: element.source.marker, source, target: points[0], isSource: true }, options);
1939
- sourceArrow && arrowG.appendChild(sourceArrow);
1952
+ const sortElements = sortElementsByArea(board, newElements, 'asc');
1953
+ return sortElements[0];
1954
+ };
1955
+ const getFirstFilledDrawElement = (board, elements) => {
1956
+ let filledElement = null;
1957
+ for (let i = 0; i < elements.length; i++) {
1958
+ const element = elements[i];
1959
+ if (isClosedCustomGeometry(board, element) || isClosedDrawElement(element)) {
1960
+ const fill = getFillByElement(board, element);
1961
+ if (isFilled(fill)) {
1962
+ filledElement = element;
1963
+ break;
1964
+ }
1965
+ }
1940
1966
  }
1941
- if (!PlaitArrowLine.isTargetMark(element, ArrowLineMarkerType.none)) {
1942
- const source = getExtendPoint(points[points.length - 1], points[points.length - 2], ARROW_LENGTH + offset);
1943
- const arrow = getArrow(element, { marker: element.target.marker, source, target: points[points.length - 1], isSource: false }, options);
1944
- arrow && arrowG.appendChild(arrow);
1967
+ return filledElement;
1968
+ };
1969
+ const isFilledDrawElement = (board, element) => {
1970
+ return getFirstFilledDrawElement(board, [element]) !== null;
1971
+ };
1972
+ const getFirstTextOrLineElement = (elements) => {
1973
+ const texts = elements.filter((item) => PlaitDrawElement.isText(item));
1974
+ if (texts.length) {
1975
+ return texts[0];
1976
+ }
1977
+ const lines = elements.filter((item) => PlaitDrawElement.isArrowLine(item));
1978
+ if (lines.length) {
1979
+ return lines[0];
1945
1980
  }
1946
- return arrowG;
1981
+ return null;
1947
1982
  };
1948
- const getArrow = (element, arrowOptions, options) => {
1949
- const { marker, target, source, isSource } = arrowOptions;
1950
- let targetArrow;
1951
- switch (marker) {
1952
- case ArrowLineMarkerType.openTriangle: {
1953
- targetArrow = drawOpenTriangle(element, source, target, options);
1954
- break;
1955
- }
1956
- case ArrowLineMarkerType.solidTriangle: {
1957
- targetArrow = drawSolidTriangle(source, target, options);
1958
- break;
1959
- }
1960
- case ArrowLineMarkerType.arrow: {
1961
- targetArrow = drawArrow(element, source, target, options);
1962
- break;
1963
- }
1964
- case ArrowLineMarkerType.sharpArrow: {
1965
- targetArrow = drawSharpArrow(source, target, options);
1966
- break;
1967
- }
1968
- case ArrowLineMarkerType.oneSideUp: {
1969
- targetArrow = drawOneSideArrow(source, target, isSource ? 'down' : 'up', options);
1970
- break;
1983
+ const debugKey$3 = 'debug:plait:hit:shape:edge:sample-points';
1984
+ const debugGenerator$3 = createDebugGenerator(debugKey$3);
1985
+ const shapes = [BasicShapes.cloud];
1986
+ const isHitDrawElement = (board, element, point, isStrict = true) => {
1987
+ const rectangle = board.getRectangle(element);
1988
+ point = rotateAntiPointsByElement(board, point, element) || point;
1989
+ if (PlaitDrawElement.isGeometry(element) && rectangle) {
1990
+ if (debugGenerator$3.isDebug() && shapes.includes(element.shape)) {
1991
+ debugGenerator$3.clear();
1992
+ const { startPoint, arcCommands } = generateCloudPath(rectangle);
1993
+ const points = [startPoint, ...arcCommands.map((arc) => [arc.endX, arc.endY])];
1994
+ debugGenerator$3.drawCircles(board, points, 5, false);
1995
+ let minDistance = Infinity;
1996
+ let nearestPoint = point;
1997
+ let currentStart = startPoint;
1998
+ for (const arc of arcCommands) {
1999
+ const arcNearestPoint = getNearestPointBetweenPointAndArc(point, currentStart, arc);
2000
+ const distance = distanceBetweenPointAndPoint(point[0], point[1], arcNearestPoint[0], arcNearestPoint[1]);
2001
+ const { center } = getEllipseArcCenter(currentStart, arc);
2002
+ debugGenerator$3.drawCircles(board, [center], 8, false, { fill: 'yellow' });
2003
+ if (distance < minDistance) {
2004
+ minDistance = distance;
2005
+ nearestPoint = arcNearestPoint;
2006
+ }
2007
+ currentStart = [arc.endX, arc.endY];
2008
+ }
2009
+ debugGenerator$3.drawCircles(board, [point], 12, false, { fill: 'black', stroke: 'black' });
2010
+ debugGenerator$3.drawCircles(board, [nearestPoint], 12, false, { fill: 'green', stroke: 'green' });
1971
2011
  }
1972
- case ArrowLineMarkerType.oneSideDown: {
1973
- targetArrow = drawOneSideArrow(source, target, isSource ? 'up' : 'down', options);
1974
- break;
2012
+ if (isHitEdgeOfShape(board, element, point, HIT_DISTANCE_BUFFER)) {
2013
+ return true;
1975
2014
  }
1976
- case ArrowLineMarkerType.hollowTriangle: {
1977
- targetArrow = drawHollowTriangleArrow(source, target, options);
1978
- break;
2015
+ const engine = getEngine(getElementShape(element));
2016
+ if (PlaitDrawElement.isText(element)) {
2017
+ const textClient = getTextRectangle(element);
2018
+ return RectangleClient.isPointInRectangle(textClient, point);
1979
2019
  }
1980
- case ArrowLineMarkerType.singleSlash: {
1981
- targetArrow = drawSingleSlash(source, target, isSource, options);
1982
- break;
2020
+ if (!!isStrict && isEmptyTextElement(element) && !isFilledDrawElement(board, element)) {
2021
+ return false;
1983
2022
  }
2023
+ const isHitText = isHitElementText(element, point);
2024
+ return isHitText || engine.isInsidePoint(rectangle, point);
1984
2025
  }
1985
- return targetArrow;
2026
+ if (PlaitDrawElement.isImage(element)) {
2027
+ const client = RectangleClient.getRectangleByPoints(element.points);
2028
+ return RectangleClient.isPointInRectangle(client, point);
2029
+ }
2030
+ if (PlaitDrawElement.isArrowLine(element)) {
2031
+ return isHitArrowLine(board, element, point);
2032
+ }
2033
+ if (PlaitDrawElement.isVectorLine(element)) {
2034
+ return isHitVectorLine(board, element, point);
2035
+ }
2036
+ return null;
1986
2037
  };
1987
- const drawSharpArrow = (source, target, options) => {
1988
- const startPoint = target;
1989
- const { pointLeft, pointRight } = arrowPoints(source, target, 20);
1990
- const g = createG();
1991
- const path = createPath();
1992
- let polylinePath = `M${pointRight[0]},${pointRight[1]}A25,25,20,0,1,${pointLeft[0]},${pointLeft[1]}L${startPoint[0]},${startPoint[1]}Z`;
1993
- path.setAttribute('d', polylinePath);
1994
- path.setAttribute('stroke', `${options?.stroke}`);
1995
- path.setAttribute('stroke-width', `${options?.strokeWidth}`);
1996
- path.setAttribute('fill', `${options?.stroke}`);
1997
- g.appendChild(path);
1998
- return g;
2038
+ const isHitEdgeOfShape = (board, element, point, hitDistanceBuffer) => {
2039
+ const nearestPoint = getNearestPoint(element, point);
2040
+ const distance = distanceBetweenPointAndPoint(nearestPoint[0], nearestPoint[1], point[0], point[1]);
2041
+ return distance <= hitDistanceBuffer;
1999
2042
  };
2000
- const drawArrow = (element, source, target, options) => {
2001
- const unitVector = getUnitVectorByPointAndPoint(source, target);
2002
- const strokeWidth = getStrokeWidthByElement(element);
2003
- const endPoint = [target[0] + (strokeWidth * unitVector[0]) / 2, target[1] + (strokeWidth * unitVector[1]) / 2];
2004
- const distance = distanceBetweenPointAndPoint(...source, ...endPoint);
2005
- const middlePoint = [
2006
- endPoint[0] - (((distance * 3) / 5 + strokeWidth) / 2) * unitVector[0],
2007
- endPoint[1] - (((distance * 3) / 5 + strokeWidth) / 2) * unitVector[1]
2008
- ];
2009
- const { pointLeft, pointRight } = arrowPoints(source, endPoint, 30);
2010
- const arrowG = drawLinearPath([pointLeft, endPoint, pointRight, middlePoint], { ...options, fill: options.stroke }, true);
2011
- const path = arrowG.querySelector('path');
2012
- path.setAttribute('stroke-linejoin', 'round');
2013
- return arrowG;
2043
+ const isInsideOfShape = (board, element, point, hitDistanceBuffer) => {
2044
+ const client = RectangleClient.inflate(RectangleClient.getRectangleByPoints(element.points), hitDistanceBuffer);
2045
+ return getEngine(getElementShape(element)).isInsidePoint(client, point);
2014
2046
  };
2015
- const drawSolidTriangle = (source, target, options) => {
2016
- const endPoint = target;
2017
- const { pointLeft, pointRight } = arrowPoints(source, endPoint, 30);
2018
- return drawLinearPath([pointLeft, endPoint, pointRight], { ...options, fill: options.stroke }, true);
2047
+ const isHitElementInside = (board, element, point) => {
2048
+ const rectangle = board.getRectangle(element);
2049
+ point = rotateAntiPointsByElement(board, point, element) || point;
2050
+ if (PlaitDrawElement.isGeometry(element) && !PlaitDrawElement.isGeometryByTable(element)) {
2051
+ const engine = getEngine(getElementShape(element));
2052
+ const isHitInside = engine.isInsidePoint(rectangle, point);
2053
+ if (isHitInside) {
2054
+ return isHitInside;
2055
+ }
2056
+ if (engine.getTextRectangle) {
2057
+ const isHitText = isHitElementText(element, point);
2058
+ if (isHitText) {
2059
+ return isHitText;
2060
+ }
2061
+ }
2062
+ }
2063
+ if (PlaitDrawElement.isImage(element)) {
2064
+ const client = RectangleClient.getRectangleByPoints(element.points);
2065
+ return RectangleClient.isPointInRectangle(client, point);
2066
+ }
2067
+ if (PlaitDrawElement.isArrowLine(element)) {
2068
+ return isHitArrowLine(board, element, point);
2069
+ }
2070
+ if (PlaitDrawElement.isVectorLine(element)) {
2071
+ return isHitVectorLine(board, element, point);
2072
+ }
2073
+ return null;
2019
2074
  };
2020
- const drawOpenTriangle = (element, source, target, options) => {
2021
- const unitVector = getUnitVectorByPointAndPoint(source, target);
2075
+
2076
+ const getTextRectangle = (element) => {
2077
+ const elementRectangle = RectangleClient.getRectangleByPoints(element.points);
2022
2078
  const strokeWidth = getStrokeWidthByElement(element);
2023
- const endPoint = [target[0] + (strokeWidth * unitVector[0]) / 2, target[1] + (strokeWidth * unitVector[1]) / 2];
2024
- const { pointLeft, pointRight } = arrowPoints(source, endPoint, 40);
2025
- return drawLinearPath([pointLeft, endPoint, pointRight], options);
2026
- };
2027
- const drawOneSideArrow = (source, target, side, options) => {
2028
- const { pointLeft, pointRight } = arrowPoints(source, target, 40);
2029
- return drawLinearPath([side === 'up' ? pointRight : pointLeft, target], options);
2079
+ const height = element.textHeight;
2080
+ const width = elementRectangle.width - ShapeDefaultSpace.rectangleAndText * 2 - strokeWidth * 2;
2081
+ return {
2082
+ height,
2083
+ width: width > 0 ? width : 0,
2084
+ x: elementRectangle.x + ShapeDefaultSpace.rectangleAndText + strokeWidth,
2085
+ y: elementRectangle.y + (elementRectangle.height - height) / 2
2086
+ };
2030
2087
  };
2031
- const drawSingleSlash = (source, target, isSource, options) => {
2032
- const length = distanceBetweenPointAndPoint(...source, ...target);
2033
- const middlePoint = getExtendPoint(target, source, length / 2);
2034
- const angle = isSource ? 120 : 60;
2035
- const start = rotate(...source, ...middlePoint, (angle * Math.PI) / 180);
2036
- const end = rotate(...target, ...middlePoint, (angle * Math.PI) / 180);
2037
- return drawLinearPath([start, end], options);
2088
+ const getStrokeWidthByElement = (element) => {
2089
+ if (PlaitDrawElement.isText(element)) {
2090
+ return 0;
2091
+ }
2092
+ const strokeWidth = element.strokeWidth || DefaultDrawStyle.strokeWidth;
2093
+ return strokeWidth;
2038
2094
  };
2039
- const drawHollowTriangleArrow = (source, target, options) => {
2040
- const { pointLeft, pointRight } = arrowPoints(source, target, 30);
2041
- return drawLinearPath([pointLeft, pointRight, target], { ...options, fill: 'white' }, true);
2095
+ const insertElement = (board, element) => {
2096
+ memorizeLatestShape(board, element.shape);
2097
+ Transforms.insertNode(board, element, [board.children.length]);
2098
+ clearSelectedElement(board);
2099
+ addSelectedElement(board, element);
2100
+ BoardTransforms.updatePointerType(board, PlaitPointerType.selection);
2042
2101
  };
2043
-
2044
- class ArrowLineShapeGenerator extends Generator {
2045
- canDraw(element) {
2102
+ const isDrawElementIncludeText = (element) => {
2103
+ if (PlaitDrawElement.isText(element)) {
2046
2104
  return true;
2047
2105
  }
2048
- draw(element) {
2049
- let lineG;
2050
- lineG = drawArrowLine(this.board, element);
2051
- return lineG;
2106
+ if (PlaitDrawElement.isImage(element)) {
2107
+ return false;
2052
2108
  }
2053
- }
2054
-
2055
- const createArrowLineElement = (shape, points, source, target, texts, options) => {
2056
- return {
2057
- id: idCreator(),
2058
- type: 'arrow-line',
2059
- shape,
2060
- source,
2061
- texts: texts ? texts : [],
2062
- target,
2063
- opacity: 1,
2064
- points,
2065
- ...options
2066
- };
2067
- };
2068
- const getArrowLinePoints = (board, element) => {
2069
- switch (element.shape) {
2070
- case ArrowLineShape.elbow: {
2071
- return getElbowPoints(board, element);
2072
- }
2073
- case ArrowLineShape.curve: {
2074
- return getCurvePoints(board, element);
2075
- }
2076
- default: {
2077
- const points = PlaitArrowLine.getPoints(board, element);
2078
- const handleRefPair = getArrowLineHandleRefPair(board, element);
2079
- points[0] = handleRefPair.source.point;
2080
- points[points.length - 1] = handleRefPair.target.point;
2081
- return points;
2082
- }
2109
+ if (PlaitDrawElement.isGeometry(element)) {
2110
+ return isGeometryIncludeText(element);
2111
+ }
2112
+ if (PlaitDrawElement.isArrowLine(element)) {
2113
+ const editors = getTextEditorsByElement(element);
2114
+ return editors.length > 0;
2115
+ }
2116
+ if (PlaitDrawElement.isElementByTable(element)) {
2117
+ return element.cells.some((cell) => isCellIncludeText(cell));
2083
2118
  }
2119
+ return true;
2084
2120
  };
2085
- const getCurvePoints = (board, element) => {
2086
- if (element.points.length === 2) {
2087
- const handleRefPair = getArrowLineHandleRefPair(board, element);
2088
- const { source, target } = handleRefPair;
2089
- const sourceBoundElement = handleRefPair.source.boundElement;
2090
- const targetBoundElement = handleRefPair.target.boundElement;
2091
- let curvePoints = [source.point];
2092
- const sumDistance = distanceBetweenPointAndPoint(...source.point, ...target.point);
2093
- const offset = 12 + sumDistance / 3;
2094
- if (sourceBoundElement) {
2095
- curvePoints.push(getPointByVectorComponent(source.point, source.vector, offset));
2096
- }
2097
- if (targetBoundElement) {
2098
- curvePoints.push(getPointByVectorComponent(target.point, target.vector, offset));
2121
+ const isDrawElementsIncludeText = (elements) => {
2122
+ return elements.some((item) => {
2123
+ return isDrawElementIncludeText(item);
2124
+ });
2125
+ };
2126
+ const isClosedDrawElement = (element) => {
2127
+ if (PlaitDrawElement.isDrawElement(element)) {
2128
+ if (PlaitDrawElement.isText(element) || PlaitDrawElement.isArrowLine(element) || PlaitDrawElement.isImage(element)) {
2129
+ return false;
2099
2130
  }
2100
- const isSingleBound = (sourceBoundElement && !targetBoundElement) || (!sourceBoundElement && targetBoundElement);
2101
- if (isSingleBound) {
2102
- curvePoints.push(target.point);
2103
- const points = Q2C(curvePoints);
2104
- return pointsOnBezierCurves(points);
2131
+ if (PlaitDrawElement.isVectorLine(element)) {
2132
+ return isClosedPoints(element.points);
2105
2133
  }
2106
- if (!sourceBoundElement && !targetBoundElement) {
2107
- curvePoints.push(getPointByVectorComponent(source.point, source.vector, offset));
2108
- curvePoints.push(getPointByVectorComponent(target.point, target.vector, offset));
2134
+ if (PlaitDrawElement.isGeometry(element)) {
2135
+ return isGeometryClosed(element);
2109
2136
  }
2110
- curvePoints.push(target.point);
2111
- return pointsOnBezierCurves(curvePoints);
2112
- }
2113
- else {
2114
- let dataPoints = PlaitArrowLine.getPoints(board, element);
2115
- dataPoints = removeDuplicatePoints(dataPoints);
2116
- const points = catmullRomFitting(dataPoints);
2117
- return pointsOnBezierCurves(points);
2137
+ return true;
2118
2138
  }
2139
+ return false;
2119
2140
  };
2120
- const drawArrowLine = (board, element) => {
2121
- const strokeWidth = getStrokeWidthByElement(element);
2122
- const strokeColor = getStrokeColorByElement(board, element);
2123
- const strokeStyle = getStrokeStyleByElement(board, element);
2124
- const strokeLineDash = getStrokeLineDash(strokeStyle, strokeWidth);
2125
- const options = { stroke: strokeColor, strokeWidth, strokeLineDash };
2126
- const lineG = createG();
2127
- let points = getArrowLinePoints(board, element);
2128
- let line;
2129
- if (element.shape === ArrowLineShape.curve) {
2130
- line = PlaitBoard.getRoughSVG(board).curve(points, options);
2131
- }
2132
- else {
2133
- line = drawLinearPath(points, options);
2134
- }
2135
- const id = idCreator();
2136
- line.setAttribute('mask', `url(#${id})`);
2137
- if (element.strokeStyle === StrokeStyle.dotted) {
2138
- setStrokeLinecap(line, 'round');
2141
+ const isClosedCustomGeometry = (board, value) => {
2142
+ return PlaitDrawElement.isCustomGeometryElement(board, value) && isClosedPoints(value.points);
2143
+ };
2144
+ const getSnappingShape = (board, point) => {
2145
+ let hitElement = getHitShape(board, point);
2146
+ if (hitElement) {
2147
+ const ref = getSnappingRef(board, hitElement, point);
2148
+ if (ref.isHitConnector || ref.isHitEdge) {
2149
+ return hitElement;
2150
+ }
2139
2151
  }
2140
- lineG.appendChild(line);
2141
- const { mask, maskTargetFillRect } = drawArrowLineMask(board, element, id);
2142
- lineG.appendChild(mask);
2143
- line.appendChild(maskTargetFillRect);
2144
- const arrow = drawArrowLineArrow(element, points, { stroke: strokeColor, strokeWidth });
2145
- arrow && lineG.appendChild(arrow);
2146
- return lineG;
2152
+ return null;
2147
2153
  };
2148
- const getHitConnection = (board, point, hitElement) => {
2149
- let rectangle = RectangleClient.getRectangleByPoints(hitElement.points);
2150
- const ref = getSnappingRef(board, hitElement, point);
2151
- const connectionPoint = ref.connectorPoint || ref.edgePoint;
2152
- return [(connectionPoint[0] - rectangle.x) / rectangle.width, (connectionPoint[1] - rectangle.y) / rectangle.height];
2154
+ const getSnappingRef = (board, hitElement, point) => {
2155
+ const rotatedPoint = rotateAntiPointsByElement(board, point, hitElement) || point;
2156
+ const connectorPoint = getHitConnectorPoint(rotatedPoint, hitElement);
2157
+ const edgePoint = getNearestPoint(hitElement, rotatedPoint);
2158
+ const isHitEdge = isHitEdgeOfShape(board, hitElement, rotatedPoint, LINE_SNAPPING_BUFFER);
2159
+ return { isHitEdge, isHitConnector: !!connectorPoint, connectorPoint, edgePoint };
2153
2160
  };
2154
- const getHitConnectorPoint = (point, hitElement) => {
2155
- const rectangle = RectangleClient.getRectangleByPoints(hitElement.points);
2156
- const shape = getElementShape(hitElement);
2157
- const connectorPoints = getEngine(shape).getConnectorPoints(rectangle);
2158
- return connectorPoints.find((connectorPoint) => {
2159
- return distanceBetweenPointAndPoint(...connectorPoint, ...point) <= LINE_SNAPPING_CONNECTOR_BUFFER;
2161
+ const getHitShape = (board, point, offset = LINE_HIT_GEOMETRY_BUFFER) => {
2162
+ let hitShape = null;
2163
+ traverseDrawShapes(board, (element) => {
2164
+ if (hitShape === null && isInsideOfShape(board, element, rotateAntiPointsByElement(board, point, element) || point, offset * 2)) {
2165
+ hitShape = element;
2166
+ }
2160
2167
  });
2168
+ return hitShape;
2161
2169
  };
2162
- const getArrowLineTextRectangle = (board, element, index) => {
2163
- const text = element.texts[index];
2164
- const elbowPoints = getArrowLinePoints(board, element);
2165
- const point = getPointOnPolyline(elbowPoints, text.position);
2166
- return {
2167
- x: point[0] - text.width / 2,
2168
- y: point[1] - text.height / 2,
2169
- width: text.width,
2170
- height: text.height
2171
- };
2170
+ const traverseDrawShapes = (board, callback) => {
2171
+ depthFirstRecursion(board, (node) => {
2172
+ if (!PlaitBoard.isBoard(node) && PlaitDrawElement.isShapeElement(node)) {
2173
+ callback(node);
2174
+ }
2175
+ }, getIsRecursionFunc(board), true);
2172
2176
  };
2173
- const getArrowLines = (board) => {
2174
- return findElements(board, {
2175
- match: (element) => PlaitDrawElement.isArrowLine(element),
2176
- recursion: (element) => PlaitDrawElement.isDrawElement(element)
2177
- });
2177
+ const drawShape = (board, outerRectangle, shape, roughOptions, drawOptions) => {
2178
+ return getEngine(shape).draw(board, outerRectangle, roughOptions, drawOptions);
2178
2179
  };
2179
- // quadratic Bezier to cubic Bezier
2180
- const Q2C = (points) => {
2181
- const result = [];
2182
- const numSegments = points.length / 3;
2183
- for (let i = 0; i < numSegments; i++) {
2184
- const start = points[i];
2185
- const qControl = points[i + 1];
2186
- const end = points[i + 2];
2187
- const startDistance = distanceBetweenPointAndPoint(...start, ...qControl);
2188
- const endDistance = distanceBetweenPointAndPoint(...end, ...qControl);
2189
- const cControl1 = getExtendPoint(start, qControl, (startDistance * 2) / 3);
2190
- const cControl2 = getExtendPoint(end, qControl, (endDistance * 2) / 3);
2191
- result.push(start, cControl1, cControl2, end);
2180
+ const drawBoundReaction = (board, element, roughOptions = { hasMask: true, hasConnector: true }) => {
2181
+ const g = createG();
2182
+ const rectangle = RectangleClient.getRectangleByPoints(element.points);
2183
+ const activeRectangle = RectangleClient.inflate(rectangle, SNAPPING_STROKE_WIDTH);
2184
+ const shape = getElementShape(element);
2185
+ let drawOptions;
2186
+ if (PlaitDrawElement.isElementByTable(element)) {
2187
+ drawOptions = { element };
2192
2188
  }
2193
- return result;
2189
+ const strokeG = drawShape(board, activeRectangle, shape, {
2190
+ stroke: SELECTION_BORDER_COLOR,
2191
+ strokeWidth: SNAPPING_STROKE_WIDTH
2192
+ }, drawOptions);
2193
+ g.appendChild(strokeG);
2194
+ if (roughOptions.hasMask) {
2195
+ const maskG = drawShape(board, activeRectangle, shape, {
2196
+ stroke: SELECTION_BORDER_COLOR,
2197
+ strokeWidth: 0,
2198
+ fill: isClosedDrawElement(element) ? SELECTION_FILL_COLOR : DefaultDrawStyle.fill,
2199
+ fillStyle: 'solid'
2200
+ }, drawOptions);
2201
+ g.appendChild(maskG);
2202
+ }
2203
+ if (roughOptions.hasConnector) {
2204
+ const connectorPoints = getEngine(shape).getConnectorPoints(rectangle);
2205
+ connectorPoints.forEach((point) => {
2206
+ const circleG = drawCircle(PlaitBoard.getRoughSVG(board), point, 8, {
2207
+ stroke: SELECTION_BORDER_COLOR,
2208
+ strokeWidth: ACTIVE_STROKE_WIDTH,
2209
+ fill: '#FFF',
2210
+ fillStyle: 'solid'
2211
+ });
2212
+ g.appendChild(circleG);
2213
+ });
2214
+ }
2215
+ return g;
2194
2216
  };
2195
- const handleArrowLineCreating = (board, lineShape, sourcePoint, movingPoint, sourceElement, lineShapeG) => {
2196
- const hitElement = getSnappingShape(board, movingPoint);
2197
- const targetConnection = hitElement ? getHitConnection(board, movingPoint, hitElement) : undefined;
2198
- const sourceConnection = sourceElement ? getHitConnection(board, sourcePoint, sourceElement) : undefined;
2199
- const targetBoundId = hitElement ? hitElement.id : undefined;
2200
- const lineGenerator = new ArrowLineShapeGenerator(board);
2201
- const memorizedLatest = getLineMemorizedLatest();
2202
- let sourceMarker, targetMarker;
2203
- sourceMarker = memorizedLatest.source;
2204
- targetMarker = memorizedLatest.target;
2205
- sourceMarker && delete memorizedLatest.source;
2206
- targetMarker && delete memorizedLatest.target;
2207
- const temporaryLineElement = createArrowLineElement(lineShape, [sourcePoint, movingPoint], { marker: sourceMarker || ArrowLineMarkerType.none, connection: sourceConnection, boundId: sourceElement?.id }, { marker: targetMarker || ArrowLineMarkerType.arrow, connection: targetConnection, boundId: targetBoundId }, [], {
2208
- strokeWidth: DefaultLineStyle.strokeWidth,
2209
- ...memorizedLatest
2210
- });
2211
- const linePoints = getArrowLinePoints(board, temporaryLineElement);
2212
- const otherPoint = linePoints[0];
2213
- temporaryLineElement.points[1] = alignPoints(otherPoint, movingPoint);
2214
- lineGenerator.processDrawing(temporaryLineElement, lineShapeG);
2215
- PlaitBoard.getElementTopHost(board).append(lineShapeG);
2216
- return temporaryLineElement;
2217
+ const getTextKey = (element, text) => {
2218
+ if (element && isMultipleTextGeometry(element)) {
2219
+ return `${element.id}-${text.id}`;
2220
+ }
2221
+ else {
2222
+ return text.id;
2223
+ }
2224
+ };
2225
+ const getGeometryAlign = (board, element) => {
2226
+ if (isMultipleTextGeometry(element)) {
2227
+ const drawShapeText = element.texts.find((item) => item.id.includes(GeometryCommonTextKeys.content));
2228
+ return drawShapeText?.text.align || Alignment.center;
2229
+ }
2230
+ if (isSingleTextGeometry(element)) {
2231
+ return element.text?.align || Alignment.center;
2232
+ }
2233
+ if (PlaitDrawElement.isElementByTable(element)) {
2234
+ const firstTextCell = element.cells.find((item) => item.text);
2235
+ return firstTextCell?.text?.align || Alignment.center;
2236
+ }
2237
+ return Alignment.center;
2238
+ };
2239
+ const isClosedPoints = (points) => {
2240
+ const startPoint = points[0];
2241
+ const endPoint = points[points.length - 1];
2242
+ return startPoint[0] === endPoint[0] && startPoint[1] === endPoint[1];
2243
+ };
2244
+ const getDefaultGeometryText = (board) => {
2245
+ return getI18nValue(board, DrawI18nKey.geometryText, DefaultTextProperty.text);
2217
2246
  };
2218
- function drawArrowLineMask(board, element, id) {
2219
- const mask = createMask();
2220
- mask.setAttribute('id', id);
2221
- const points = getArrowLinePoints(board, element);
2222
- let rectangle = RectangleClient.getRectangleByPoints(points);
2223
- rectangle = RectangleClient.getOutlineRectangle(rectangle, -30);
2224
- const maskFillRect = createRect(rectangle, {
2225
- fill: 'white'
2226
- });
2227
- mask.appendChild(maskFillRect);
2228
- const texts = element.texts;
2229
- texts.forEach((text, index) => {
2230
- let textRectangle = getArrowLineTextRectangle(board, element, index);
2231
- textRectangle = RectangleClient.inflate(textRectangle, LINE_TEXT_SPACE * 2);
2232
- const rect = createRect(textRectangle, {
2233
- fill: 'black'
2234
- });
2235
- mask.appendChild(rect);
2236
- });
2237
- // open line
2238
- const maskTargetFillRect = createRect(rectangle);
2239
- maskTargetFillRect.setAttribute('opacity', '0');
2240
- maskTargetFillRect.setAttribute('fill', 'none');
2241
- return { mask, maskTargetFillRect };
2242
- }
2243
2247
 
2244
2248
  const createUMLClassOrInterfaceGeometryElement = (board, shape, points) => {
2245
2249
  const memorizedLatest = getMemorizedLatestByPointer(shape);
@@ -2301,8 +2305,9 @@ const buildTableCellsForGeometry = (board, rows, columns, shape) => {
2301
2305
  const memorizedLatest = getMemorizedLatestByPointer(shape);
2302
2306
  const cellCount = rows.length * columns.length;
2303
2307
  const defaultTexts = getDefaultGeometryProperty(shape)?.texts || [];
2304
- const testHeights = defaultTexts.map((textItem) => {
2305
- return getTextShapeProperty(board, textItem.text || DefaultTextProperty.text, memorizedLatest.textProperties['font-size']).height;
2308
+ const textHeights = defaultTexts.map((textItem) => {
2309
+ return getTextShapeProperty(board, textItem.text || getDefaultGeometryText(board), memorizedLatest.textProperties['font-size'])
2310
+ .height;
2306
2311
  });
2307
2312
  return new Array(cellCount).fill('').map((item, index) => {
2308
2313
  const rowIndex = Math.floor(index / columns.length);
@@ -2311,7 +2316,7 @@ const buildTableCellsForGeometry = (board, rows, columns, shape) => {
2311
2316
  id: idCreator(),
2312
2317
  rowId: rows[rowIndex].id,
2313
2318
  columnId: columns[columnIndex].id,
2314
- textHeight: testHeights[index],
2319
+ textHeight: textHeights[index],
2315
2320
  text: {
2316
2321
  children: [
2317
2322
  {
@@ -2394,61 +2399,6 @@ const getDefaultBasicShapeProperty = (shape) => {
2394
2399
  const getDefaultUMLProperty = (shape) => {
2395
2400
  return DefaultUMLPropertyMap[shape];
2396
2401
  };
2397
- const createDefaultFlowchart = (point) => {
2398
- const decisionProperty = getDefaultFlowchartProperty(FlowchartSymbols.decision);
2399
- const processProperty = getDefaultFlowchartProperty(FlowchartSymbols.process);
2400
- const terminalProperty = getDefaultFlowchartProperty(FlowchartSymbols.terminal);
2401
- const options = {
2402
- strokeWidth: DefaultBasicShapeProperty.strokeWidth
2403
- };
2404
- const lineOptions = {
2405
- strokeWidth: DefaultLineStyle.strokeWidth
2406
- };
2407
- const startElement = createGeometryElement(FlowchartSymbols.terminal, getDefaultGeometryPoints(FlowchartSymbols.terminal, point), '开始', options);
2408
- const processPoint1 = [point[0], point[1] + terminalProperty.height / 2 + 55 + processProperty.height / 2];
2409
- const processElement1 = createGeometryElement(FlowchartSymbols.process, getDefaultGeometryPoints(FlowchartSymbols.process, processPoint1), '过程', options);
2410
- const decisionPoint = [processPoint1[0], processPoint1[1] + processProperty.height / 2 + 55 + decisionProperty.height / 2];
2411
- const decisionElement = createGeometryElement(FlowchartSymbols.decision, getDefaultGeometryPoints(FlowchartSymbols.decision, decisionPoint), '判断', options);
2412
- const processPoint2 = [decisionPoint[0] + decisionProperty.width / 2 + 75 + processProperty.width / 2, decisionPoint[1]];
2413
- const processElement2 = createGeometryElement(FlowchartSymbols.process, getDefaultGeometryPoints(FlowchartSymbols.process, processPoint2), '过程', options);
2414
- const endPoint = [decisionPoint[0], decisionPoint[1] + decisionProperty.height / 2 + 95 + terminalProperty.height / 2];
2415
- const endElement = createGeometryElement(FlowchartSymbols.terminal, getDefaultGeometryPoints(FlowchartSymbols.terminal, endPoint), '结束', options);
2416
- const line1 = createArrowLineElement(ArrowLineShape.elbow, [
2417
- [0, 0],
2418
- [0, 0]
2419
- ], { marker: ArrowLineMarkerType.none, connection: [0.5, 1], boundId: startElement.id }, { marker: ArrowLineMarkerType.arrow, connection: [0.5, 0], boundId: processElement1.id }, [], lineOptions);
2420
- const line2 = createArrowLineElement(ArrowLineShape.elbow, [
2421
- [0, 0],
2422
- [0, 0]
2423
- ], { marker: ArrowLineMarkerType.none, connection: [0.5, 1], boundId: processElement1.id }, { marker: ArrowLineMarkerType.arrow, connection: [0.5, 0], boundId: decisionElement.id }, [], lineOptions);
2424
- const line3 = createArrowLineElement(ArrowLineShape.elbow, [
2425
- [0, 0],
2426
- [0, 0]
2427
- ], { marker: ArrowLineMarkerType.none, connection: [0.5, 1], boundId: decisionElement.id }, { marker: ArrowLineMarkerType.arrow, connection: [0.5, 0], boundId: endElement.id }, [
2428
- {
2429
- text: buildText('是'),
2430
- position: 0.5,
2431
- width: 14,
2432
- height: 20
2433
- }
2434
- ], lineOptions);
2435
- const line4 = createArrowLineElement(ArrowLineShape.elbow, [
2436
- [0, 0],
2437
- [0, 0]
2438
- ], { marker: ArrowLineMarkerType.none, connection: [1, 0.5], boundId: decisionElement.id }, { marker: ArrowLineMarkerType.arrow, connection: [0, 0.5], boundId: processElement2.id }, [
2439
- {
2440
- text: buildText('否'),
2441
- position: 0.5,
2442
- width: 14,
2443
- height: 20
2444
- }
2445
- ], lineOptions);
2446
- const line5 = createArrowLineElement(ArrowLineShape.elbow, [
2447
- [0, 0],
2448
- [0, 0]
2449
- ], { marker: ArrowLineMarkerType.none, connection: [0.5, 1], boundId: processElement2.id }, { marker: ArrowLineMarkerType.arrow, connection: [1, 0.5], boundId: endElement.id }, [], lineOptions);
2450
- return [startElement, processElement1, decisionElement, processElement2, endElement, line1, line2, line3, line4, line5];
2451
- };
2452
2402
  const getAutoCompletePoints = (board, element, isToActive = false) => {
2453
2403
  const AutoCompleteMargin = (12 + RESIZE_HANDLE_DIAMETER / 2) * 2;
2454
2404
  const rectangle = RectangleClient.getRectangleByPoints(element.points);
@@ -2470,7 +2420,7 @@ const getDrawDefaultStrokeColor = (theme) => {
2470
2420
  const getFlowchartDefaultFill = (theme) => {
2471
2421
  return DrawThemeColors[theme].fill;
2472
2422
  };
2473
- const getTextShapeProperty = (board, text = DefaultTextProperty.text, fontSize) => {
2423
+ const getTextShapeProperty = (board, text, fontSize) => {
2474
2424
  fontSize = fontSize ? Number(fontSize) : DEFAULT_FONT_SIZE;
2475
2425
  const textSize = measureElement(buildText(text), { fontSize, fontFamily: DEFAULT_FONT_FAMILY });
2476
2426
  return {
@@ -2499,7 +2449,7 @@ const getDefaultTextPoints = (board, centerPoint, fontSize) => {
2499
2449
  const property = getTextShapeProperty(board, DefaultTextProperty.text, fontSize);
2500
2450
  return RectangleClient.getPoints(RectangleClient.getRectangleByCenterPoint(centerPoint, property.width, property.height));
2501
2451
  };
2502
- const createTextElement = (board, points, text = DefaultTextProperty.text, textHeight) => {
2452
+ const createTextElement = (board, points, text, textHeight) => {
2503
2453
  const memorizedLatest = getMemorizedLatestByPointer(BasicShapes.text);
2504
2454
  textHeight = textHeight ? textHeight : RectangleClient.getRectangleByPoints(points).height;
2505
2455
  return createGeometryElement(BasicShapes.text, points, text, memorizedLatest.geometryProperties, {
@@ -3480,23 +3430,9 @@ function withDrawResize(board) {
3480
3430
  handleG.remove();
3481
3431
  handleG = null;
3482
3432
  }
3483
- if (canResize() && !isSelectionMoving(board)) {
3484
- handleG = createG();
3485
- const elements = getSelectedElements(board);
3486
- const boundingRectangle = needCustomActiveRectangle
3487
- ? RectangleClient.getRectangleByPoints(resizeActivePoints)
3488
- : getRectangleByElements(board, elements, false);
3489
- const boundingActiveRectangle = toActiveRectangleFromViewBoxRectangle(board, boundingRectangle);
3490
- let corners = RectangleClient.getCornerPoints(boundingActiveRectangle);
3491
- const angle = getSelectionAngle(elements);
3492
- if (angle) {
3493
- const centerPoint = RectangleClient.getCenterPoint(boundingActiveRectangle);
3494
- corners = rotatePoints(corners, centerPoint, angle);
3495
- }
3496
- corners.forEach((corner) => {
3497
- const g = drawHandle(board, corner);
3498
- handleG && handleG.append(g);
3499
- });
3433
+ const selectedElements = getSelectedElements(board);
3434
+ if (canResize() && !isSelectionMoving(board) && selectedElements.length > 1) {
3435
+ handleG = generatorResizeHandles(board, resizeActivePoints, needCustomActiveRectangle);
3500
3436
  PlaitBoard.getActiveHost(board).append(handleG);
3501
3437
  }
3502
3438
  };
@@ -3582,6 +3518,25 @@ const getResizePointsByOtherwiseAxis = (board, points, resizeOriginPoint, xZoom,
3582
3518
  const newRectangle = RectangleClient.getRectangleByPoints(resultPoints);
3583
3519
  return rotatePoints(resultPoints, RectangleClient.getCenterPoint(newRectangle), -(1 / 2) * Math.PI);
3584
3520
  };
3521
+ const generatorResizeHandles = (board, resizeActivePoints, needCustomActiveRectangle) => {
3522
+ const handleG = createG();
3523
+ const elements = getSelectedElements(board);
3524
+ const boundingRectangle = needCustomActiveRectangle
3525
+ ? RectangleClient.getRectangleByPoints(resizeActivePoints)
3526
+ : getRectangleByElements(board, elements, false);
3527
+ const boundingActiveRectangle = toActiveRectangleFromViewBoxRectangle(board, boundingRectangle);
3528
+ let corners = RectangleClient.getCornerPoints(boundingActiveRectangle);
3529
+ const angle = getSelectionAngle(elements);
3530
+ if (angle) {
3531
+ const centerPoint = RectangleClient.getCenterPoint(boundingActiveRectangle);
3532
+ corners = rotatePoints(corners, centerPoint, angle);
3533
+ }
3534
+ corners.forEach((corner) => {
3535
+ const g = drawHandle(board, corner);
3536
+ handleG.append(g);
3537
+ });
3538
+ return handleG;
3539
+ };
3585
3540
 
3586
3541
  const debugKey$1 = 'debug:plait:point-for-geometry';
3587
3542
  const debugGenerator$1 = createDebugGenerator(debugKey$1);
@@ -7214,8 +7169,7 @@ class GeometryComponent extends CommonElementFlavour {
7214
7169
  }
7215
7170
  else {
7216
7171
  const hasSameSelected = value.selected === previous.selected;
7217
- const hasSameHandleState = this.activeGenerator.options.hasResizeHandle() === this.activeGenerator.hasResizeHandle;
7218
- if (!hasSameSelected || !hasSameHandleState || value.selected) {
7172
+ if (!hasSameSelected || value.selected) {
7219
7173
  this.activeGenerator.processDrawing(this.element, PlaitBoard.getActiveHost(this.board), {
7220
7174
  selected: this.selected
7221
7175
  });
@@ -7706,9 +7660,9 @@ const withGeometryCreateByDrag = (board) => {
7706
7660
  if (dragMode) {
7707
7661
  const memorizedLatest = getMemorizedLatestByPointer(pointer);
7708
7662
  if (pointer === BasicShapes.text) {
7709
- const property = getTextShapeProperty(board, DefaultTextProperty.text, memorizedLatest.textProperties['font-size']);
7663
+ const property = getTextShapeProperty(board, getDefaultGeometryText(board), memorizedLatest.textProperties['font-size']);
7710
7664
  const points = RectangleClient.getPoints(RectangleClient.getRectangleByCenterPoint(movingPoint, property.width, property.height));
7711
- temporaryElement = createTextElement(board, points);
7665
+ temporaryElement = createTextElement(board, points, getDefaultGeometryText(board));
7712
7666
  if (!fakeCreateTextRef) {
7713
7667
  const textManage = new TextManage(board, {
7714
7668
  getRectangle: () => {
@@ -7779,9 +7733,9 @@ const withGeometryCreateByDrawing = (board) => {
7779
7733
  const pointer = PlaitBoard.getPointer(board);
7780
7734
  if (pointer === BasicShapes.text) {
7781
7735
  const memorizedLatest = getMemorizedLatestByPointer(pointer);
7782
- const property = getTextShapeProperty(board, DefaultTextProperty.text, memorizedLatest.textProperties['font-size']);
7736
+ const property = getTextShapeProperty(board, getDefaultGeometryText(board), memorizedLatest.textProperties['font-size']);
7783
7737
  const points = RectangleClient.getPoints(RectangleClient.getRectangleByCenterPoint(point, property.width, property.height));
7784
- const textElement = createTextElement(board, points);
7738
+ const textElement = createTextElement(board, points, getDefaultGeometryText(board));
7785
7739
  insertElement(board, textElement);
7786
7740
  start = null;
7787
7741
  }
@@ -8173,6 +8127,9 @@ const withArrowLineBoundReaction = (board) => {
8173
8127
  return board;
8174
8128
  };
8175
8129
 
8130
+ const getDefaultLineText = (board) => {
8131
+ return getI18nValue(board, DrawI18nKey.lineText, LINE_TEXT);
8132
+ };
8176
8133
  const withArrowLineText = (board) => {
8177
8134
  const { dblClick } = board;
8178
8135
  board.dblClick = (event) => {
@@ -8190,13 +8147,19 @@ const withArrowLineText = (board) => {
8190
8147
  editHandle(board, hitTarget, textIndex);
8191
8148
  }
8192
8149
  else {
8193
- const ratio = getRatioByPoint(points, point);
8150
+ const defaultLineText = getDefaultLineText(board);
8194
8151
  const textMemory = getMemorizedLatest('arrow-line')?.text || {};
8152
+ const textElement = buildText(defaultLineText, undefined, textMemory);
8153
+ const { width, height } = measureElement(textElement, {
8154
+ fontSize: DEFAULT_FONT_SIZE,
8155
+ fontFamily: DEFAULT_FONT_FAMILY
8156
+ });
8157
+ const ratio = getRatioByPoint(points, point);
8195
8158
  texts.push({
8196
- text: buildText(LINE_TEXT, undefined, textMemory),
8159
+ text: textElement,
8197
8160
  position: ratio,
8198
- width: 28,
8199
- height: 20
8161
+ width,
8162
+ height
8200
8163
  });
8201
8164
  DrawTransforms.setArrowLineTexts(board, hitTarget, texts);
8202
8165
  setTimeout(() => {
@@ -8218,7 +8181,8 @@ function editHandle(board, element, manageIndex, isFirstEdit = false) {
8218
8181
  const textManage = textManages[manageIndex];
8219
8182
  textManage.edit(() => {
8220
8183
  const text = Node.string(textManage.getText());
8221
- const shouldRemove = !text || (isFirstEdit && text === LINE_TEXT);
8184
+ const defaultLineText = getDefaultLineText(board);
8185
+ const shouldRemove = !text || (isFirstEdit && text === defaultLineText);
8222
8186
  if (shouldRemove) {
8223
8187
  DrawTransforms.removeArrowLineText(board, element, manageIndex);
8224
8188
  }
@@ -8274,9 +8238,7 @@ class ImageComponent extends CommonElementFlavour {
8274
8238
  }
8275
8239
  else {
8276
8240
  const hasSameSelected = value.selected === previous.selected;
8277
- const hasSameHandleState = this.imageGenerator.activeGenerator &&
8278
- this.imageGenerator.activeGenerator.options.hasResizeHandle() === this.imageGenerator.activeGenerator.hasResizeHandle;
8279
- if (!hasSameSelected || !hasSameHandleState || value.selected) {
8241
+ if (!hasSameSelected || value.selected) {
8280
8242
  this.imageGenerator.setFocus(this.element, this.selected);
8281
8243
  this.lineAutoCompleteGenerator.processDrawing(this.element, PlaitBoard.getActiveHost(this.board), {
8282
8244
  selected: this.selected
@@ -8698,9 +8660,8 @@ class TableComponent extends CommonElementFlavour {
8698
8660
  }
8699
8661
  else {
8700
8662
  const hasSameSelected = value.selected === previous.selected;
8701
- const hasSameHandleState = this.activeGenerator.options.hasResizeHandle() === this.activeGenerator.hasResizeHandle;
8702
8663
  const currentSelectedCells = getSelectedCells(value.element);
8703
- if (!hasSameSelected || !hasSameHandleState || currentSelectedCells?.length || value.selected) {
8664
+ if (!hasSameSelected || currentSelectedCells?.length || value.selected) {
8704
8665
  this.activeGenerator.processDrawing(value.element, PlaitBoard.getActiveHost(this.board), {
8705
8666
  selected: this.selected
8706
8667
  });
@@ -8728,17 +8689,21 @@ function withTableResize(board) {
8728
8689
  const options = {
8729
8690
  key: 'draw-table',
8730
8691
  canResize: () => {
8731
- return true;
8692
+ const selectedElements = getSelectedElements(board);
8693
+ return isSingleSelectTable(board) && !hasValidAngle(selectedElements[0]);
8732
8694
  },
8733
8695
  hitTest: (point) => {
8734
- const hitElement = getHitElementByPoint(board, point);
8696
+ const selectedElements = getSelectedElements(board);
8697
+ const hitElement = selectedElements[0];
8698
+ // debugGenerator.clear();
8735
8699
  if (hitElement && PlaitDrawElement.isElementByTable(hitElement)) {
8736
8700
  let rectangle = board.getRectangle(hitElement);
8701
+ // debugGenerator.drawRectangle(board, rectangle);
8702
+ // debugGenerator.drawCircles(board, [point], 5);
8737
8703
  let handleRef = getHitRectangleResizeHandleRef(board, rectangle, point, hitElement.angle);
8738
8704
  if (handleRef) {
8739
8705
  const selectElement = isSelectedElement(board, hitElement);
8740
- if ((selectElement && isSingleSelectElementByTable(board)) ||
8741
- (!selectElement && !isCornerHandle(board, handleRef.handle))) {
8706
+ if ((selectElement && isSingleSelectTable(board)) || (!selectElement && !isCornerHandle(board, handleRef.handle))) {
8742
8707
  return {
8743
8708
  element: hitElement,
8744
8709
  handle: handleRef.handle,
@@ -8857,7 +8822,9 @@ const withTable = (board) => {
8857
8822
  tableBoard.isHit = (element, point, isStrict) => {
8858
8823
  if (PlaitDrawElement.isElementByTable(element)) {
8859
8824
  const client = RectangleClient.getRectangleByPoints(element.points);
8860
- return RectangleClient.isPointInRectangle(client, point);
8825
+ const nearestPoint = TableEngine.getNearestPoint(client, point);
8826
+ const distance = distanceBetweenPointAndPoint(nearestPoint[0], nearestPoint[1], point[0], point[1]);
8827
+ return distance <= HIT_DISTANCE_BUFFER || RectangleClient.isPointInRectangle(client, point);
8861
8828
  }
8862
8829
  return isHit(element, point, isStrict);
8863
8830
  };
@@ -8894,9 +8861,9 @@ const withTable = (board) => {
8894
8861
  event.preventDefault();
8895
8862
  if (PlaitDrawElement.isElementByTable(targetElement)) {
8896
8863
  const cells = getSelectedCells(targetElement);
8897
- let cell = targetElement.cells.find(item => item.text && item.textHeight);
8864
+ let cell = targetElement.cells.find((item) => item.text && item.textHeight);
8898
8865
  if (cells?.length) {
8899
- cell = cells.find(item => item.text && item.textHeight);
8866
+ cell = cells.find((item) => item.text && item.textHeight);
8900
8867
  }
8901
8868
  if (cell) {
8902
8869
  editCell(board, cell);
@@ -9338,5 +9305,5 @@ const withDraw = (board) => {
9338
9305
  * Generated bundle index. Do not edit.
9339
9306
  */
9340
9307
 
9341
- export { ArrowLineAutoCompleteGenerator, ArrowLineComponent, ArrowLineHandleKey, ArrowLineMarkerType, ArrowLineShape, BasicShapes, DEFAULT_IMAGE_WIDTH, DefaultActivationProperty, DefaultActorProperty, DefaultArrowProperty, DefaultAssemblyProperty, DefaultBasicShapeProperty, DefaultBasicShapePropertyMap, DefaultClassProperty, DefaultCloudProperty, DefaultCombinedFragmentProperty, DefaultComponentBoxProperty, DefaultConnectorProperty, DefaultContainerProperty, DefaultDataBaseProperty, DefaultDataProperty, DefaultDecisionProperty, DefaultDeletionProperty, DefaultDocumentProperty, DefaultDrawActiveStyle, DefaultDrawStyle, DefaultFlowchartProperty, DefaultFlowchartPropertyMap, DefaultInterfaceProperty, DefaultInternalStorageProperty, DefaultManualInputProperty, DefaultMergeProperty, DefaultMultiDocumentProperty, DefaultNoteProperty, DefaultObjectProperty, DefaultPackageProperty, DefaultPentagonArrowProperty, DefaultPortProperty, DefaultProvidedInterfaceProperty, DefaultRequiredInterfaceProperty, DefaultSwimlaneHorizontalProperty, DefaultSwimlaneHorizontalWithHeaderProperty, DefaultSwimlanePropertyMap, DefaultSwimlaneVerticalProperty, DefaultSwimlaneVerticalWithHeaderProperty, DefaultTextProperty, DefaultTwoWayArrowProperty, DefaultUMLPropertyMap, DrawThemeColors, DrawTransforms, FlowchartSymbols, GEOMETRY_NOT_CLOSED, GEOMETRY_WITHOUT_TEXT, GEOMETRY_WITH_MULTIPLE_TEXT, GeometryCommonTextKeys, GeometryComponent, GeometryShapeGenerator, GeometryThreshold, KEY_TO_TEXT_MANAGE, LINE_HIT_GEOMETRY_BUFFER, LINE_SNAPPING_BUFFER, LINE_SNAPPING_CONNECTOR_BUFFER, LineActiveGenerator, MIN_TEXT_WIDTH, MemorizeKey, MultipleTextGeometryTextKeys, PlaitArrowLine, PlaitDrawElement, PlaitGeometry, PlaitTableElement, Q2C, SELECTED_CELLS, SWIMLANE_HEADER_SIZE, ShapeDefaultSpace, SingleTextGenerator, SwimlaneDrawSymbols, SwimlaneSymbols, TableGenerator, TableSymbols, TextGenerator, UMLSymbols, VectorLineComponent, VectorLinePointerType, VectorLineShape, WithArrowLineAutoCompletePluginKey, WithDrawPluginKey, adjustSwimlaneShape, alignElbowSegment, alignPoints, buildClipboardData, buildDefaultTextsByShape, buildSwimlaneTable, clearSelectedCells, collectArrowLineUpdatedRefsByGeometry, createArrowLineElement, createCell, createDefaultCells, createDefaultFlowchart, createDefaultGeometry, createDefaultRowsOrColumns, createDefaultSwimlane, createGeometryElement, createGeometryElementWithText, createGeometryElementWithoutText, createMultipleTextGeometryElement, createTextElement, createUMLClassOrInterfaceGeometryElement, createVectorLineElement, debugGenerator$1 as debugGenerator, deleteTextManage, drawArrowLine, drawArrowLineArrow, drawBoundReaction, drawGeometry, drawShape, drawVectorLine, editCell, editText, getArrowLineHandleRefPair, getArrowLinePointers, getArrowLinePoints, getArrowLineTextRectangle, getArrowLines, getAutoCompletePoints, getBasicPointers, getCellWithPoints, getCellsRectangle, getCellsWithPoints, getCenterPointsOnPolygon, getConnectionPoint, getCurvePoints, getDefaultBasicShapeProperty, getDefaultFlowchartProperty, getDefaultGeometryPoints, getDefaultGeometryProperty, getDefaultSwimlanePoints, getDefaultTextPoints, getDefaultUMLProperty, getDrawDefaultStrokeColor, getElbowLineRouteOptions, getElbowPoints, getFillByElement, getFirstFilledDrawElement, getFirstTextOrLineElement, getFlowchartDefaultFill, getFlowchartPointers, getGeometryAlign, getGeometryPointers, getHitCell, getHitConnection, getHitConnectorPoint, getHitDrawElement, getHitIndexOfAutoCompletePoint, getHitMultipleGeometryText, getHitShape, getIndexAndDeleteCountByKeyPoint, getLineMemorizedLatest, getMemorizeKey, getMemorizedLatestByPointer, getMemorizedLatestShape, getMidKeyPoints, getMiddlePoints, getMirrorDataPoints, getMultipleTextGeometryTextKeys, getNearestPoint, getNextRenderPoints, getNextSourceAndTargetPoints, getResizedPreviousAndNextPoint, getSelectedArrowLineElements, getSelectedCells, getSelectedCustomGeometryElements, getSelectedDrawElements, getSelectedGeometryElements, getSelectedImageElements, getSelectedSwimlane, getSelectedTableCellsEditor, getSelectedTableElements, getSelectedVectorLineElements, getSnapResizingRef, getSnapResizingRefOptions, getSnappingRef, getSnappingShape, getSourceAndTargetRectangle, getStrokeColorByElement, getStrokeStyleByElement, getStrokeWidthByElement, getSwimlaneCount, getSwimlanePointers, getTextKey, getTextManage, getTextManageByCell, getTextRectangle, getTextShapeProperty, getUMLPointers, getVectorByConnection, getVectorLinePointers, getVectorLinePoints, handleArrowLineCreating, hasIllegalElbowPoint, insertClipboardData, insertElement, isCellIncludeText, isClosedCustomGeometry, isClosedDrawElement, isClosedPoints, isDrawElementIncludeText, isDrawElementsIncludeText, isEmptyTextElement, isFilledDrawElement, isGeometryClosed, isGeometryIncludeText, isHitArrowLine, isHitArrowLineText, isHitDrawElement, isHitEdgeOfShape, isHitElementInside, isHitElementText, isHitPolyLine, isHitVectorLine, isInsideOfShape, isMultipleTextGeometry, isMultipleTextShape, isRectangleHitDrawElement, isRectangleHitElementText, isRectangleHitRotatedElement, isRectangleHitRotatedPoints, isSelfLoop, isSingleSelectElementByTable, isSingleSelectLine, isSingleSelectSwimlane, isSingleSelectTable, isSingleTextGeometry, isSingleTextShape, isSwimlanePointers, isSwimlaneWithHeader, isTextExceedingBounds, isUpdatedHandleIndex, isUseDefaultOrthogonalRoute, memorizeLatestShape, memorizeLatestText, setSelectedCells, setTextManage, traverseDrawShapes, updateCellIds, updateCellIdsByRowOrColumn, updateColumns, updateRowOrColumnIds, updateRows, vectorLineCreating, withArrowLineAutoComplete, withDraw };
9308
+ export { ArrowLineAutoCompleteGenerator, ArrowLineComponent, ArrowLineHandleKey, ArrowLineMarkerType, ArrowLineShape, BasicShapes, DEFAULT_IMAGE_WIDTH, DefaultActivationProperty, DefaultActorProperty, DefaultArrowProperty, DefaultAssemblyProperty, DefaultBasicShapeProperty, DefaultBasicShapePropertyMap, DefaultClassProperty, DefaultCloudProperty, DefaultCombinedFragmentProperty, DefaultComponentBoxProperty, DefaultConnectorProperty, DefaultContainerProperty, DefaultDataBaseProperty, DefaultDataProperty, DefaultDecisionProperty, DefaultDeletionProperty, DefaultDocumentProperty, DefaultDrawActiveStyle, DefaultDrawStyle, DefaultFlowchartProperty, DefaultFlowchartPropertyMap, DefaultInterfaceProperty, DefaultInternalStorageProperty, DefaultLineStyle, DefaultManualInputProperty, DefaultMergeProperty, DefaultMultiDocumentProperty, DefaultNoteProperty, DefaultObjectProperty, DefaultPackageProperty, DefaultPentagonArrowProperty, DefaultPortProperty, DefaultProvidedInterfaceProperty, DefaultRequiredInterfaceProperty, DefaultSwimlaneHorizontalProperty, DefaultSwimlaneHorizontalWithHeaderProperty, DefaultSwimlanePropertyMap, DefaultSwimlaneVerticalProperty, DefaultSwimlaneVerticalWithHeaderProperty, DefaultTextProperty, DefaultTwoWayArrowProperty, DefaultUMLPropertyMap, DrawI18nKey, DrawThemeColors, DrawTransforms, FlowchartSymbols, GEOMETRY_NOT_CLOSED, GEOMETRY_WITHOUT_TEXT, GEOMETRY_WITH_MULTIPLE_TEXT, GeometryCommonTextKeys, GeometryComponent, GeometryShapeGenerator, GeometryThreshold, KEY_TO_TEXT_MANAGE, LINE_ALIGN_TOLERANCE, LINE_AUTO_COMPLETE_DIAMETER, LINE_AUTO_COMPLETE_HOVERED_DIAMETER, LINE_AUTO_COMPLETE_HOVERED_OPACITY, LINE_AUTO_COMPLETE_OPACITY, LINE_HIT_GEOMETRY_BUFFER, LINE_SNAPPING_BUFFER, LINE_SNAPPING_CONNECTOR_BUFFER, LINE_TEXT, LINE_TEXT_SPACE, LineActiveGenerator, MIN_TEXT_WIDTH, MemorizeKey, MultipleTextGeometryTextKeys, PlaitArrowLine, PlaitDrawElement, PlaitGeometry, PlaitTableElement, Q2C, SELECTED_CELLS, SWIMLANE_HEADER_SIZE, ShapeDefaultSpace, SingleTextGenerator, SwimlaneDrawSymbols, SwimlaneSymbols, TableGenerator, TableSymbols, TextGenerator, UMLSymbols, VectorLineComponent, VectorLinePointerType, VectorLineShape, WithArrowLineAutoCompletePluginKey, WithDrawPluginKey, adjustSwimlaneShape, alignElbowSegment, alignPoints, buildClipboardData, buildDefaultTextsByShape, buildSwimlaneTable, clearSelectedCells, collectArrowLineUpdatedRefsByGeometry, createArrowLineElement, createCell, createDefaultCells, createDefaultGeometry, createDefaultRowsOrColumns, createDefaultSwimlane, createGeometryElement, createGeometryElementWithText, createGeometryElementWithoutText, createMultipleTextGeometryElement, createTextElement, createUMLClassOrInterfaceGeometryElement, createVectorLineElement, debugGenerator$1 as debugGenerator, deleteTextManage, drawArrowLine, drawArrowLineArrow, drawBoundReaction, drawGeometry, drawShape, drawVectorLine, editCell, editText, getArrowLineHandleRefPair, getArrowLinePointers, getArrowLinePoints, getArrowLineTextRectangle, getArrowLines, getAutoCompletePoints, getBasicPointers, getCellWithPoints, getCellsRectangle, getCellsWithPoints, getCenterPointsOnPolygon, getConnectionPoint, getCurvePoints, getDefaultBasicShapeProperty, getDefaultFlowchartProperty, getDefaultGeometryPoints, getDefaultGeometryProperty, getDefaultGeometryText, getDefaultSwimlanePoints, getDefaultTextPoints, getDefaultUMLProperty, getDrawDefaultStrokeColor, getElbowLineRouteOptions, getElbowPoints, getFillByElement, getFirstFilledDrawElement, getFirstTextOrLineElement, getFlowchartDefaultFill, getFlowchartPointers, getGeometryAlign, getGeometryPointers, getHitCell, getHitConnection, getHitConnectorPoint, getHitDrawElement, getHitIndexOfAutoCompletePoint, getHitMultipleGeometryText, getHitShape, getIndexAndDeleteCountByKeyPoint, getLineMemorizedLatest, getMemorizeKey, getMemorizedLatestByPointer, getMemorizedLatestShape, getMidKeyPoints, getMiddlePoints, getMirrorDataPoints, getMultipleTextGeometryTextKeys, getNearestPoint, getNextRenderPoints, getNextSourceAndTargetPoints, getResizedPreviousAndNextPoint, getSelectedArrowLineElements, getSelectedCells, getSelectedCustomGeometryElements, getSelectedDrawElements, getSelectedGeometryElements, getSelectedImageElements, getSelectedSwimlane, getSelectedTableCellsEditor, getSelectedTableElements, getSelectedVectorLineElements, getSnapResizingRef, getSnapResizingRefOptions, getSnappingRef, getSnappingShape, getSourceAndTargetRectangle, getStrokeColorByElement, getStrokeStyleByElement, getStrokeWidthByElement, getSwimlaneCount, getSwimlanePointers, getTextKey, getTextManage, getTextManageByCell, getTextRectangle, getTextShapeProperty, getUMLPointers, getVectorByConnection, getVectorLinePointers, getVectorLinePoints, handleArrowLineCreating, hasIllegalElbowPoint, insertClipboardData, insertElement, isCellIncludeText, isClosedCustomGeometry, isClosedDrawElement, isClosedPoints, isDrawElementIncludeText, isDrawElementsIncludeText, isEmptyTextElement, isFilledDrawElement, isGeometryClosed, isGeometryIncludeText, isHitArrowLine, isHitArrowLineText, isHitDrawElement, isHitEdgeOfShape, isHitElementInside, isHitElementText, isHitPolyLine, isHitVectorLine, isInsideOfShape, isMultipleTextGeometry, isMultipleTextShape, isRectangleHitDrawElement, isRectangleHitElementText, isRectangleHitRotatedElement, isRectangleHitRotatedPoints, isSelfLoop, isSingleSelectLine, isSingleSelectSwimlane, isSingleSelectTable, isSingleTextGeometry, isSingleTextShape, isSwimlanePointers, isSwimlaneWithHeader, isTextExceedingBounds, isUpdatedHandleIndex, isUseDefaultOrthogonalRoute, memorizeLatestShape, memorizeLatestText, setSelectedCells, setTextManage, traverseDrawShapes, updateCellIds, updateCellIdsByRowOrColumn, updateColumns, updateRowOrColumnIds, updateRows, vectorLineCreating, withArrowLineAutoComplete, withDraw };
9342
9309
  //# sourceMappingURL=plait-draw.mjs.map