@plait/draw 0.53.0 → 0.54.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/constants/geometry.d.ts +3 -1
  2. package/esm2022/constants/geometry.mjs +4 -2
  3. package/esm2022/generators/line-auto-complete.generator.mjs +1 -1
  4. package/esm2022/interfaces/index.mjs +2 -2
  5. package/esm2022/plugins/with-draw-fragment.mjs +1 -1
  6. package/esm2022/plugins/with-draw-resize.mjs +56 -9
  7. package/esm2022/plugins/with-draw.mjs +4 -4
  8. package/esm2022/plugins/with-geometry-resize.mjs +7 -14
  9. package/esm2022/plugins/with-line-auto-complete-reaction.mjs +2 -2
  10. package/esm2022/plugins/with-line-auto-complete.mjs +15 -14
  11. package/esm2022/plugins/with-line-bound-reaction.mjs +19 -21
  12. package/esm2022/plugins/with-line-create.mjs +4 -4
  13. package/esm2022/plugins/with-line-resize.mjs +11 -12
  14. package/esm2022/transforms/line.mjs +4 -4
  15. package/esm2022/utils/geometry.mjs +31 -22
  16. package/esm2022/utils/hit.mjs +26 -26
  17. package/esm2022/utils/line/elbow.mjs +11 -6
  18. package/esm2022/utils/line/line-basic.mjs +21 -29
  19. package/esm2022/utils/line/line-common.mjs +3 -3
  20. package/esm2022/utils/line/line-resize.mjs +2 -2
  21. package/esm2022/utils/position/geometry.mjs +51 -21
  22. package/esm2022/utils/resize-snap.mjs +361 -0
  23. package/esm2022/utils/shape.mjs +2 -2
  24. package/fesm2022/plait-draw.mjs +432 -344
  25. package/fesm2022/plait-draw.mjs.map +1 -1
  26. package/generators/line-auto-complete.generator.d.ts +3 -3
  27. package/interfaces/index.d.ts +2 -2
  28. package/package.json +1 -1
  29. package/plugins/with-draw-fragment.d.ts +2 -2
  30. package/plugins/with-draw-resize.d.ts +6 -0
  31. package/utils/geometry.d.ts +7 -4
  32. package/utils/hit.d.ts +3 -1
  33. package/utils/line/elbow.d.ts +3 -0
  34. package/utils/line/line-basic.d.ts +4 -4
  35. package/utils/position/geometry.d.ts +10 -2
  36. package/utils/resize-snap.d.ts +49 -0
  37. package/utils/shape.d.ts +2 -2
  38. package/esm2022/utils/resize-align-reaction.mjs +0 -316
  39. package/esm2022/utils/resize-align.mjs +0 -37
  40. package/utils/resize-align-reaction.d.ts +0 -42
  41. package/utils/resize-align.d.ts +0 -8
@@ -1,5 +1,5 @@
1
- import { ACTIVE_STROKE_WIDTH, ThemeColorMode, createDebugGenerator, Point, RectangleClient, getElementById, rotatePointsByElement, createG, arrowPoints, createPath, distanceBetweenPointAndPoint, drawLinearPath, rotate, rotatePoints, depthFirstRecursion, rotateAntiPointsByElement, getIsRecursionFunc, idCreator, catmullRomFitting, PlaitBoard, findElements, createMask, createRect, getSelectedElements, distanceBetweenPointAndSegments, HIT_DISTANCE_BUFFER, isPolylineHitRectangle, isPointInPolygon, setStrokeLinecap, getNearestPointBetweenPointAndSegments, isPointInEllipse, getEllipseTangentSlope, getVectorFromPointAndSlope, drawRectangle, drawRoundRectangle, isPointInRoundRectangle, SELECTION_BORDER_COLOR, SELECTION_FILL_COLOR, drawCircle, Transforms, clearSelectedElement, addSelectedElement, BoardTransforms, PlaitPointerType, Direction, hasValidAngle, Path, PlaitNode, toViewBoxPoint, toHostPoint, isSelectionMoving, RgbaToHEX, PlaitElement, getHitElementByPoint, preventTouchMove, createClipboardContext, WritableClipboardType, addClipboardContext, getRectangleByElements, getSelectionAngle, rotatedDataPoints, setAngleForG, CursorClass, temporaryDisableSelection, PRESS_AND_MOVE_BUFFER } from '@plait/core';
2
- import { removeDuplicatePoints, generateElbowLineRoute, simplifyOrthogonalPoints, isSourceAndTargetIntersect, getPoints, getPointByVectorComponent, getExtendPoint, getUnitVectorByPointAndPoint, Generator, getRectangleResizeHandleRefs, RESIZE_HANDLE_DIAMETER, getMemorizedLatest, memorizeLatest, getPointOnPolyline, TRANSPARENT, getCrossingPointsBetweenPointAndSegment, isPointOnSegment, getDirectionByVector, getOppositeDirection, getDirectionFactor, rotateVector, getDirectionByPointOfRectangle, rotateVectorAnti90, getSourceAndTargetOuterRectangle, getNextPoint, normalizeShapePoints, getFirstTextEditor, PRIMARY_COLOR, CommonPluginElement, ActiveGenerator, WithTextPluginKey, drawPrimaryHandle, drawFillPrimaryHandle, isVirtualKey, isDelete, isSpaceHotkey, isDndMode, isDrawingMode, getElementsText, acceptImageTypes, getElementOfFocusedImage, buildImage, getDirectionFactorByDirectionComponent, isCornerHandle, resetPointsAfterResize, getFirstTextManage, withResize, drawHandle, getIndexByResizeHandle, getSymmetricHandleIndex, getResizeHandlePointByIndex, isResizingByCondition, getRatioByPoint, ImageGenerator, ResizeHandle } from '@plait/common';
1
+ import { ACTIVE_STROKE_WIDTH, ThemeColorMode, createDebugGenerator, Point, RectangleClient, getElementById, rotatePointsByElement, createG, arrowPoints, createPath, distanceBetweenPointAndPoint, drawLinearPath, rotate, distanceBetweenPointAndSegments, HIT_DISTANCE_BUFFER, isPolylineHitRectangle, rotateAntiPointsByElement, rotatePoints, depthFirstRecursion, PlaitBoard, getIsRecursionFunc, idCreator, catmullRomFitting, findElements, createMask, createRect, getSelectedElements, setStrokeLinecap, isPointInPolygon, getNearestPointBetweenPointAndSegments, isPointInEllipse, getEllipseTangentSlope, getVectorFromPointAndSlope, drawRectangle, drawRoundRectangle, isPointInRoundRectangle, SNAPPING_STROKE_WIDTH, SELECTION_BORDER_COLOR, SELECTION_FILL_COLOR, drawCircle, Transforms, clearSelectedElement, addSelectedElement, BoardTransforms, PlaitPointerType, Direction, hasValidAngle, Path, PlaitNode, toViewBoxPoint, toHostPoint, isSelectionMoving, RgbaToHEX, PlaitElement, getHitElementByPoint, preventTouchMove, createClipboardContext, WritableClipboardType, addClipboardContext, getSelectionAngle, getRectangleByAngle, getRectangleByElements, rotatedDataPoints, isAxisChangedByAngle, setAngleForG, CursorClass, temporaryDisableSelection, PRESS_AND_MOVE_BUFFER } from '@plait/core';
2
+ import { removeDuplicatePoints, generateElbowLineRoute, simplifyOrthogonalPoints, isSourceAndTargetIntersect, getPoints, DEFAULT_ROUTE_MARGIN, getPointByVectorComponent, getExtendPoint, getUnitVectorByPointAndPoint, Generator, RESIZE_HANDLE_DIAMETER, getPointOnPolyline, TRANSPARENT, getRectangleResizeHandleRefs, getRotatedResizeCursorClassByAngle, getMemorizedLatest, memorizeLatest, getCrossingPointsBetweenPointAndSegment, isPointOnSegment, getDirectionByVector, getOppositeDirection, getDirectionFactor, rotateVector, getDirectionByPointOfRectangle, rotateVectorAnti90, getSourceAndTargetOuterRectangle, getNextPoint, normalizeShapePoints, getFirstTextEditor, PRIMARY_COLOR, CommonPluginElement, ActiveGenerator, WithTextPluginKey, drawPrimaryHandle, drawFillPrimaryHandle, isVirtualKey, isDelete, isSpaceHotkey, isDndMode, isDrawingMode, getElementsText, acceptImageTypes, getElementOfFocusedImage, buildImage, resetPointsAfterResize, getDirectionFactorByDirectionComponent, isCornerHandle, getFirstTextManage, withResize, drawHandle, getIndexByResizeHandle, getSymmetricHandleIndex, getResizeHandlePointByIndex, isResizingByCondition, getRatioByPoint, ImageGenerator, ResizeHandle } from '@plait/common';
3
3
  import { Alignment, buildText, DEFAULT_FONT_SIZE, getTextSize, AlignEditor, TextManage } from '@plait/text';
4
4
  import { pointsOnBezierCurves } from 'points-on-curve';
5
5
  import * as i0 from '@angular/core';
@@ -111,7 +111,9 @@ const DefaultFlowchartPropertyMap = {
111
111
  [FlowchartSymbols.delay]: DefaultFlowchartProperty,
112
112
  [FlowchartSymbols.storedData]: DefaultFlowchartProperty
113
113
  };
114
- const REACTION_MARGIN = -4;
114
+ const LINE_HIT_GEOMETRY_BUFFER = 10;
115
+ const LINE_SNAPPING_BUFFER = 6;
116
+ const LINE_SNAPPING_CONNECTOR_BUFFER = 8;
115
117
 
116
118
  const getGeometryPointers = () => {
117
119
  return [...Object.keys(BasicShapes), ...Object.keys(FlowchartSymbols)];
@@ -166,8 +168,8 @@ const LINE_AUTO_COMPLETE_HOVERED_OPACITY = 0.8;
166
168
  const LINE_AUTO_COMPLETE_HOVERED_DIAMETER = 10;
167
169
  const LINE_ALIGN_TOLERANCE = 3;
168
170
 
169
- const debugKey$2 = 'debug:plait:line-mirror';
170
- const debugGenerator$2 = createDebugGenerator(debugKey$2);
171
+ const debugKey$3 = 'debug:plait:line-mirror';
172
+ const debugGenerator$3 = createDebugGenerator(debugKey$3);
171
173
  const alignPoints = (basePoint, movingPoint) => {
172
174
  const newPoint = [...movingPoint];
173
175
  if (Point.isVertical(newPoint, basePoint, LINE_ALIGN_TOLERANCE)) {
@@ -304,7 +306,7 @@ function getIndexAndDeleteCountByKeyPoint(board, element, dataPoints, nextRender
304
306
  if (midDataPoints.length > 0) {
305
307
  const handleRefPair = getLineHandleRefPair(board, element);
306
308
  const params = getElbowLineRouteOptions(board, element, handleRefPair);
307
- const keyPoints = removeDuplicatePoints(generateElbowLineRoute(params));
309
+ const keyPoints = removeDuplicatePoints(generateElbowLineRoute(params, board));
308
310
  const nextKeyPoints = simplifyOrthogonalPoints(keyPoints.slice(1, keyPoints.length - 1));
309
311
  const nextDataPoints = [nextRenderPoints[0], ...midDataPoints, nextRenderPoints[nextRenderPoints.length - 1]];
310
312
  const mirrorDataPoints = getMirrorDataPoints(board, nextDataPoints, nextKeyPoints, params);
@@ -431,7 +433,7 @@ function findOrthogonalParallelSegments(segment, keyPoints) {
431
433
  return parallelSegments;
432
434
  }
433
435
  function findMirrorSegments(board, segment, parallelSegments, sourceRectangle, targetRectangle) {
434
- debugGenerator$2.isDebug() && debugGenerator$2.clear();
436
+ debugGenerator$3.isDebug() && debugGenerator$3.clear();
435
437
  const mirrorSegments = [];
436
438
  for (let index = 0; index < parallelSegments.length; index++) {
437
439
  const parallelPath = parallelSegments[index];
@@ -445,7 +447,7 @@ function findMirrorSegments(board, segment, parallelSegments, sourceRectangle, t
445
447
  const isValid = !RectangleClient.isHit(fakeRectangle, sourceRectangle) && !RectangleClient.isHit(fakeRectangle, targetRectangle);
446
448
  if (isValid) {
447
449
  mirrorSegments.push([startPoint, endPoint]);
448
- debugGenerator$2.isDebug() && debugGenerator$2.drawPolygon(board, RectangleClient.getCornerPoints(fakeRectangle));
450
+ debugGenerator$3.isDebug() && debugGenerator$3.drawPolygon(board, RectangleClient.getCornerPoints(fakeRectangle));
449
451
  }
450
452
  }
451
453
  return mirrorSegments;
@@ -471,15 +473,20 @@ const hasIllegalElbowPoint = (midDataPoints) => {
471
473
  });
472
474
  };
473
475
 
476
+ const isSelfLoop = (element) => {
477
+ return element.source.boundId && element.source.boundId === element.target.boundId;
478
+ };
479
+ const isUseDefaultOrthogonalRoute = (element, options) => {
480
+ return isSourceAndTargetIntersect(options) && !isSelfLoop(element);
481
+ };
474
482
  const getElbowPoints = (board, element) => {
475
483
  const handleRefPair = getLineHandleRefPair(board, element);
476
484
  const params = getElbowLineRouteOptions(board, element, handleRefPair);
477
485
  // console.log(params, 'params');
478
- const isIntersect = isSourceAndTargetIntersect(params);
479
- if (isIntersect) {
480
- return simplifyOrthogonalPoints(getPoints(handleRefPair.source.point, handleRefPair.source.direction, handleRefPair.target.point, handleRefPair.target.direction, 0));
486
+ if (isUseDefaultOrthogonalRoute(element, params)) {
487
+ return simplifyOrthogonalPoints(getPoints(handleRefPair.source.point, handleRefPair.source.direction, handleRefPair.target.point, handleRefPair.target.direction, DEFAULT_ROUTE_MARGIN));
481
488
  }
482
- const keyPoints = removeDuplicatePoints(generateElbowLineRoute(params));
489
+ const keyPoints = removeDuplicatePoints(generateElbowLineRoute(params, board));
483
490
  const nextKeyPoints = keyPoints.slice(1, keyPoints.length - 1);
484
491
  if (element.points.length === 2) {
485
492
  return simplifyOrthogonalPoints(keyPoints);
@@ -692,7 +699,7 @@ const drawHollowTriangleArrow = (source, target, options) => {
692
699
  return drawLinearPath([pointLeft, pointRight, target], { ...options, fill: 'white' }, true);
693
700
  };
694
701
 
695
- const getShape = (value) => {
702
+ const getElementShape = (value) => {
696
703
  if (PlaitDrawElement.isImage(value)) {
697
704
  return BasicShapes.rectangle;
698
705
  }
@@ -710,29 +717,238 @@ class LineShapeGenerator extends Generator {
710
717
  }
711
718
  }
712
719
 
720
+ var LineResizeHandle;
721
+ (function (LineResizeHandle) {
722
+ LineResizeHandle["source"] = "source";
723
+ LineResizeHandle["target"] = "target";
724
+ LineResizeHandle["addHandle"] = "addHandle";
725
+ })(LineResizeHandle || (LineResizeHandle = {}));
726
+ const getHitLineResizeHandleRef = (board, element, point) => {
727
+ let dataPoints = PlaitLine.getPoints(board, element);
728
+ const index = getHitPointIndex(dataPoints, point);
729
+ if (index !== -1) {
730
+ const handleIndex = index;
731
+ if (index === 0) {
732
+ return { handle: LineResizeHandle.source, handleIndex };
733
+ }
734
+ if (index === dataPoints.length - 1) {
735
+ return { handle: LineResizeHandle.target, handleIndex };
736
+ }
737
+ // elbow line, data points only verify source connection point and target connection point
738
+ if (element.shape !== LineShape.elbow) {
739
+ return { handleIndex };
740
+ }
741
+ }
742
+ const middlePoints = getMiddlePoints(board, element);
743
+ const indexOfMiddlePoints = getHitPointIndex(middlePoints, point);
744
+ if (indexOfMiddlePoints !== -1) {
745
+ return {
746
+ handle: LineResizeHandle.addHandle,
747
+ handleIndex: indexOfMiddlePoints
748
+ };
749
+ }
750
+ return undefined;
751
+ };
752
+ function getHitPointIndex(points, movingPoint) {
753
+ const rectangles = points.map(point => {
754
+ return {
755
+ x: point[0] - RESIZE_HANDLE_DIAMETER / 2,
756
+ y: point[1] - RESIZE_HANDLE_DIAMETER / 2,
757
+ width: RESIZE_HANDLE_DIAMETER,
758
+ height: RESIZE_HANDLE_DIAMETER
759
+ };
760
+ });
761
+ const rectangle = rectangles.find(rectangle => {
762
+ return RectangleClient.isHit(RectangleClient.getRectangleByPoints([movingPoint, movingPoint]), rectangle);
763
+ });
764
+ return rectangle ? rectangles.indexOf(rectangle) : -1;
765
+ }
766
+ const getHitLineTextIndex = (board, element, point) => {
767
+ const texts = element.texts;
768
+ if (!texts.length)
769
+ return -1;
770
+ const points = getLinePoints(board, element);
771
+ return texts.findIndex(text => {
772
+ const center = getPointOnPolyline(points, text.position);
773
+ const rectangle = {
774
+ x: center[0] - text.width / 2,
775
+ y: center[1] - text.height / 2,
776
+ width: text.width,
777
+ height: text.height
778
+ };
779
+ return RectangleClient.isHit(rectangle, RectangleClient.getRectangleByPoints([point, point]));
780
+ });
781
+ };
782
+
783
+ const isTextExceedingBounds = (geometry) => {
784
+ const client = RectangleClient.getRectangleByPoints(geometry.points);
785
+ if (geometry.textHeight > client.height) {
786
+ return true;
787
+ }
788
+ return false;
789
+ };
790
+ const isHitLineText = (board, element, point) => {
791
+ return getHitLineTextIndex(board, element, point) !== -1;
792
+ };
793
+ const isHitPolyLine = (pathPoints, point) => {
794
+ const distance = distanceBetweenPointAndSegments(pathPoints, point);
795
+ return distance <= HIT_DISTANCE_BUFFER;
796
+ };
797
+ const isHitLine = (board, element, point) => {
798
+ const points = getLinePoints(board, element);
799
+ const isHitText = isHitLineText(board, element, point);
800
+ return isHitText || isHitPolyLine(points, point);
801
+ };
802
+ const isRectangleHitDrawElement = (board, element, selection) => {
803
+ const rangeRectangle = RectangleClient.getRectangleByPoints([selection.anchor, selection.focus]);
804
+ if (PlaitDrawElement.isGeometry(element)) {
805
+ const client = RectangleClient.getRectangleByPoints(element.points);
806
+ let rotatedCornerPoints = rotatePointsByElement(RectangleClient.getCornerPoints(client), element) || RectangleClient.getCornerPoints(client);
807
+ if (isTextExceedingBounds(element)) {
808
+ const textClient = getTextRectangle(element);
809
+ rotatedCornerPoints =
810
+ rotatePointsByElement(RectangleClient.getCornerPoints(textClient), element) || RectangleClient.getCornerPoints(textClient);
811
+ }
812
+ return isPolylineHitRectangle(rotatedCornerPoints, rangeRectangle);
813
+ }
814
+ if (PlaitDrawElement.isImage(element)) {
815
+ const client = RectangleClient.getRectangleByPoints(element.points);
816
+ const rotatedCornerPoints = rotatePointsByElement(RectangleClient.getCornerPoints(client), element) || RectangleClient.getCornerPoints(client);
817
+ return isPolylineHitRectangle(rotatedCornerPoints, rangeRectangle);
818
+ }
819
+ if (PlaitDrawElement.isLine(element)) {
820
+ const points = getLinePoints(board, element);
821
+ return isPolylineHitRectangle(points, rangeRectangle);
822
+ }
823
+ return null;
824
+ };
825
+ const isHitDrawElement = (board, element, point) => {
826
+ const rectangle = board.getRectangle(element);
827
+ point = rotateAntiPointsByElement(point, element) || point;
828
+ if (PlaitDrawElement.isGeometry(element)) {
829
+ const fill = getFillByElement(board, element);
830
+ if (isHitEdgeOfShape(board, element, point, HIT_DISTANCE_BUFFER)) {
831
+ return true;
832
+ }
833
+ const engine = getEngine(getElementShape(element));
834
+ // when shape equals text, fill is not allowed
835
+ if (fill !== DefaultGeometryStyle.fill && fill !== TRANSPARENT && !PlaitDrawElement.isText(element)) {
836
+ const isHitInside = engine.isInsidePoint(rectangle, point);
837
+ if (isHitInside) {
838
+ return isHitInside;
839
+ }
840
+ }
841
+ else {
842
+ // if shape equals text, only check text rectangle
843
+ if (PlaitDrawElement.isText(element)) {
844
+ const textClient = getTextRectangle(element);
845
+ let isHitText = RectangleClient.isPointInRectangle(textClient, point);
846
+ return isHitText;
847
+ }
848
+ // check textRectangle of element
849
+ const textClient = engine.getTextRectangle ? engine.getTextRectangle(element) : getTextRectangle(element);
850
+ const isHitTextRectangle = RectangleClient.isPointInRectangle(textClient, point);
851
+ if (isHitTextRectangle) {
852
+ return isHitTextRectangle;
853
+ }
854
+ }
855
+ }
856
+ if (PlaitDrawElement.isImage(element)) {
857
+ const client = RectangleClient.getRectangleByPoints(element.points);
858
+ return RectangleClient.isPointInRectangle(client, point);
859
+ }
860
+ if (PlaitDrawElement.isLine(element)) {
861
+ return isHitLine(board, element, point);
862
+ }
863
+ return null;
864
+ };
865
+ const isHitEdgeOfShape = (board, element, point, hitDistanceBuffer) => {
866
+ const nearestPoint = getNearestPoint(element, point);
867
+ const distance = distanceBetweenPointAndPoint(nearestPoint[0], nearestPoint[1], point[0], point[1]);
868
+ return distance <= hitDistanceBuffer;
869
+ };
870
+ const isInsideOfShape = (board, element, point, hitDistanceBuffer) => {
871
+ const client = RectangleClient.inflate(RectangleClient.getRectangleByPoints(element.points), hitDistanceBuffer);
872
+ return getEngine(getElementShape(element)).isInsidePoint(client, point);
873
+ };
874
+ const isHitElementInside = (board, element, point) => {
875
+ const rectangle = board.getRectangle(element);
876
+ point = rotateAntiPointsByElement(point, element) || point;
877
+ if (PlaitDrawElement.isGeometry(element)) {
878
+ const engine = getEngine(getElementShape(element));
879
+ const isHitInside = engine.isInsidePoint(rectangle, point);
880
+ if (isHitInside) {
881
+ return isHitInside;
882
+ }
883
+ if (engine.getTextRectangle) {
884
+ const textClient = engine.getTextRectangle(element);
885
+ const isHitTextRectangle = RectangleClient.isPointInRectangle(textClient, point);
886
+ if (isHitTextRectangle) {
887
+ return isHitTextRectangle;
888
+ }
889
+ }
890
+ }
891
+ if (PlaitDrawElement.isImage(element)) {
892
+ const client = RectangleClient.getRectangleByPoints(element.points);
893
+ return RectangleClient.isPointInRectangle(client, point);
894
+ }
895
+ if (PlaitDrawElement.isLine(element)) {
896
+ return isHitLine(board, element, point);
897
+ }
898
+ return null;
899
+ };
900
+
713
901
  const getHitRectangleResizeHandleRef = (board, rectangle, point, angle = 0) => {
714
902
  const centerPoint = RectangleClient.getCenterPoint(rectangle);
715
- const rotatedPoint = rotatePoints([point], centerPoint, -angle)[0];
716
- const resizeHandleRefs = getRectangleResizeHandleRefs(rectangle, RESIZE_HANDLE_DIAMETER, angle);
717
- const result = resizeHandleRefs.find(resizeHandleRef => {
718
- return RectangleClient.isHit(RectangleClient.getRectangleByPoints([rotatedPoint, rotatedPoint]), resizeHandleRef.rectangle);
903
+ const resizeHandleRefs = getRectangleResizeHandleRefs(rectangle, RESIZE_HANDLE_DIAMETER);
904
+ if (angle) {
905
+ const rotatedPoint = rotatePoints([point], centerPoint, -angle)[0];
906
+ let result = resizeHandleRefs.find(resizeHandleRef => {
907
+ return RectangleClient.isHit(RectangleClient.getRectangleByPoints([rotatedPoint, rotatedPoint]), resizeHandleRef.rectangle);
908
+ });
909
+ if (result) {
910
+ result.cursorClass = getRotatedResizeCursorClassByAngle(result.cursorClass, angle);
911
+ }
912
+ return result;
913
+ }
914
+ else {
915
+ return resizeHandleRefs.find(resizeHandleRef => {
916
+ return RectangleClient.isHit(RectangleClient.getRectangleByPoints([point, point]), resizeHandleRef.rectangle);
917
+ });
918
+ }
919
+ };
920
+ const getSnappingGeometry = (board, point) => {
921
+ let hitElement = getHitGeometry(board, point);
922
+ if (hitElement) {
923
+ const ref = getSnappingRef(board, hitElement, point);
924
+ if (ref.isHitConnector || ref.isHitEdge) {
925
+ return hitElement;
926
+ }
927
+ }
928
+ return null;
929
+ };
930
+ const getSnappingRef = (board, hitElement, point) => {
931
+ const rotatedPoint = rotateAntiPointsByElement(point, hitElement) || point;
932
+ const connectorPoint = getHitConnectorPoint(rotatedPoint, hitElement);
933
+ const edgePoint = getNearestPoint(hitElement, rotatedPoint);
934
+ const isHitEdge = isHitEdgeOfShape(board, hitElement, rotatedPoint, LINE_SNAPPING_BUFFER);
935
+ return { isHitEdge, isHitConnector: !!connectorPoint, connectorPoint, edgePoint };
936
+ };
937
+ const getHitGeometry = (board, point, offset = LINE_HIT_GEOMETRY_BUFFER) => {
938
+ let hitShape = null;
939
+ traverseDrawShapes(board, (element) => {
940
+ if (hitShape === null && isInsideOfShape(board, element, rotateAntiPointsByElement(point, element) || point, offset * 2)) {
941
+ hitShape = element;
942
+ }
719
943
  });
720
- return result;
944
+ return hitShape;
721
945
  };
722
- const getHitOutlineGeometry = (board, point, offset = 0) => {
723
- let geometry = null;
946
+ const traverseDrawShapes = (board, callback) => {
724
947
  depthFirstRecursion(board, node => {
725
- if (PlaitDrawElement.isGeometry(node) || PlaitDrawElement.isImage(node)) {
726
- let client = RectangleClient.getRectangleByPoints(node.points);
727
- client = RectangleClient.getOutlineRectangle(client, offset);
728
- const shape = getShape(node);
729
- const isHit = getEngine(shape).isInsidePoint(client, rotateAntiPointsByElement(point, node) || point);
730
- if (isHit) {
731
- geometry = node;
732
- }
948
+ if (!PlaitBoard.isBoard(node) && PlaitDrawElement.isShapeElement(node)) {
949
+ callback(node);
733
950
  }
734
951
  }, getIsRecursionFunc(board), true);
735
- return geometry;
736
952
  };
737
953
 
738
954
  const SHAPE_MAX_LENGTH = 6;
@@ -910,8 +1126,7 @@ function getMiddlePoints(board, element) {
910
1126
  if (shape === LineShape.elbow) {
911
1127
  const renderPoints = getElbowPoints(board, element);
912
1128
  const options = getElbowLineRouteOptions(board, element);
913
- const isIntersect = isSourceAndTargetIntersect(options);
914
- if (!isIntersect) {
1129
+ if (!isUseDefaultOrthogonalRoute(element, options)) {
915
1130
  const [nextSourcePoint, nextTargetPoint] = getNextSourceAndTargetPoints(board, element);
916
1131
  for (let i = 0; i < renderPoints.length - 1; i++) {
917
1132
  if ((i == 0 && Point.isEquals(renderPoints[i + 1], nextSourcePoint)) ||
@@ -951,20 +1166,18 @@ const drawLine = (board, element) => {
951
1166
  arrow && lineG.appendChild(arrow);
952
1167
  return lineG;
953
1168
  };
954
- const getConnectionByNearestPoint = (board, point, hitElement) => {
1169
+ const getHitConnection = (board, point, hitElement) => {
955
1170
  let rectangle = RectangleClient.getRectangleByPoints(hitElement.points);
956
- let nearestPoint = getNearestPoint(hitElement, point);
957
- const hitConnector = getHitConnectorPoint(nearestPoint, hitElement, rectangle);
958
- nearestPoint = hitConnector ? hitConnector : nearestPoint;
959
- return [(nearestPoint[0] - rectangle.x) / rectangle.width, (nearestPoint[1] - rectangle.y) / rectangle.height];
960
- };
961
- const getHitConnectorPoint = (point, hitElement, rectangle) => {
962
- const shape = getShape(hitElement);
963
- const connector = getEngine(shape).getConnectorPoints(rectangle);
964
- const points = RectangleClient.getPoints(RectangleClient.getRectangleByCenterPoint(point, 10, 10));
965
- const pointRectangle = RectangleClient.getRectangleByPoints(points);
966
- return connector.find(point => {
967
- return RectangleClient.isHit(pointRectangle, RectangleClient.getRectangleByPoints([point, point]));
1171
+ const ref = getSnappingRef(board, hitElement, point);
1172
+ const connectionPoint = ref.connectorPoint || ref.edgePoint;
1173
+ return [(connectionPoint[0] - rectangle.x) / rectangle.width, (connectionPoint[1] - rectangle.y) / rectangle.height];
1174
+ };
1175
+ const getHitConnectorPoint = (point, hitElement) => {
1176
+ const rectangle = RectangleClient.getRectangleByPoints(hitElement.points);
1177
+ const shape = getElementShape(hitElement);
1178
+ const connectorPoints = getEngine(shape).getConnectorPoints(rectangle);
1179
+ return connectorPoints.find(connectorPoint => {
1180
+ return distanceBetweenPointAndPoint(...connectorPoint, ...point) <= LINE_SNAPPING_CONNECTOR_BUFFER;
968
1181
  });
969
1182
  };
970
1183
  const getLineTextRectangle = (board, element, index) => {
@@ -1001,13 +1214,9 @@ const Q2C = (points) => {
1001
1214
  return result;
1002
1215
  };
1003
1216
  const handleLineCreating = (board, lineShape, sourcePoint, movingPoint, sourceElement, lineShapeG) => {
1004
- const hitElement = getHitOutlineGeometry(board, movingPoint, REACTION_MARGIN);
1005
- const targetConnection = hitElement
1006
- ? getConnectionByNearestPoint(board, rotateAntiPointsByElement(movingPoint, hitElement) || movingPoint, hitElement)
1007
- : undefined;
1008
- const sourceConnection = sourceElement
1009
- ? getConnectionByNearestPoint(board, rotateAntiPointsByElement(sourcePoint, sourceElement) || sourcePoint, sourceElement)
1010
- : undefined;
1217
+ const hitElement = getSnappingGeometry(board, movingPoint);
1218
+ const targetConnection = hitElement ? getHitConnection(board, movingPoint, hitElement) : undefined;
1219
+ const sourceConnection = sourceElement ? getHitConnection(board, sourcePoint, sourceElement) : undefined;
1011
1220
  const targetBoundId = hitElement ? hitElement.id : undefined;
1012
1221
  const lineGenerator = new LineShapeGenerator(board);
1013
1222
  const memorizedLatest = getLineMemorizedLatest();
@@ -1070,187 +1279,6 @@ const getSelectedImageElements = (board) => {
1070
1279
  return selectedElements;
1071
1280
  };
1072
1281
 
1073
- var LineResizeHandle;
1074
- (function (LineResizeHandle) {
1075
- LineResizeHandle["source"] = "source";
1076
- LineResizeHandle["target"] = "target";
1077
- LineResizeHandle["addHandle"] = "addHandle";
1078
- })(LineResizeHandle || (LineResizeHandle = {}));
1079
- const getHitLineResizeHandleRef = (board, element, point) => {
1080
- let dataPoints = PlaitLine.getPoints(board, element);
1081
- const index = getHitPointIndex(dataPoints, point);
1082
- if (index !== -1) {
1083
- const handleIndex = index;
1084
- if (index === 0) {
1085
- return { handle: LineResizeHandle.source, handleIndex };
1086
- }
1087
- if (index === dataPoints.length - 1) {
1088
- return { handle: LineResizeHandle.target, handleIndex };
1089
- }
1090
- // elbow line, data points only verify source connection point and target connection point
1091
- if (element.shape !== LineShape.elbow) {
1092
- return { handleIndex };
1093
- }
1094
- }
1095
- const middlePoints = getMiddlePoints(board, element);
1096
- const indexOfMiddlePoints = getHitPointIndex(middlePoints, point);
1097
- if (indexOfMiddlePoints !== -1) {
1098
- return {
1099
- handle: LineResizeHandle.addHandle,
1100
- handleIndex: indexOfMiddlePoints
1101
- };
1102
- }
1103
- return undefined;
1104
- };
1105
- function getHitPointIndex(points, movingPoint) {
1106
- const rectangles = points.map(point => {
1107
- return {
1108
- x: point[0] - RESIZE_HANDLE_DIAMETER / 2,
1109
- y: point[1] - RESIZE_HANDLE_DIAMETER / 2,
1110
- width: RESIZE_HANDLE_DIAMETER,
1111
- height: RESIZE_HANDLE_DIAMETER
1112
- };
1113
- });
1114
- const rectangle = rectangles.find(rectangle => {
1115
- return RectangleClient.isHit(RectangleClient.getRectangleByPoints([movingPoint, movingPoint]), rectangle);
1116
- });
1117
- return rectangle ? rectangles.indexOf(rectangle) : -1;
1118
- }
1119
- const getHitLineTextIndex = (board, element, point) => {
1120
- const texts = element.texts;
1121
- if (!texts.length)
1122
- return -1;
1123
- const points = getLinePoints(board, element);
1124
- return texts.findIndex(text => {
1125
- const center = getPointOnPolyline(points, text.position);
1126
- const rectangle = {
1127
- x: center[0] - text.width / 2,
1128
- y: center[1] - text.height / 2,
1129
- width: text.width,
1130
- height: text.height
1131
- };
1132
- return RectangleClient.isHit(rectangle, RectangleClient.getRectangleByPoints([point, point]));
1133
- });
1134
- };
1135
-
1136
- const isTextExceedingBounds = (geometry) => {
1137
- const client = RectangleClient.getRectangleByPoints(geometry.points);
1138
- if (geometry.textHeight > client.height) {
1139
- return true;
1140
- }
1141
- return false;
1142
- };
1143
- const isHitLineText = (board, element, point) => {
1144
- return getHitLineTextIndex(board, element, point) !== -1;
1145
- };
1146
- const isHitPolyLine = (pathPoints, point) => {
1147
- const distance = distanceBetweenPointAndSegments(pathPoints, point);
1148
- return distance <= HIT_DISTANCE_BUFFER;
1149
- };
1150
- const isHitLine = (board, element, point) => {
1151
- const points = getLinePoints(board, element);
1152
- const isHitText = isHitLineText(board, element, point);
1153
- return isHitText || isHitPolyLine(points, point);
1154
- };
1155
- const isRectangleHitDrawElement = (board, element, selection) => {
1156
- const rangeRectangle = RectangleClient.getRectangleByPoints([selection.anchor, selection.focus]);
1157
- if (PlaitDrawElement.isGeometry(element)) {
1158
- const client = RectangleClient.getRectangleByPoints(element.points);
1159
- const centerPoint = RectangleClient.getCenterPoint(client);
1160
- let rotatedCornerPoints = rotatePoints(RectangleClient.getCornerPoints(client), centerPoint, element.angle);
1161
- if (isTextExceedingBounds(element)) {
1162
- const textClient = getTextRectangle(element);
1163
- rotatedCornerPoints = rotatePoints(RectangleClient.getCornerPoints(textClient), centerPoint, element.angle);
1164
- }
1165
- return isPolylineHitRectangle(rotatedCornerPoints, rangeRectangle);
1166
- }
1167
- if (PlaitDrawElement.isImage(element)) {
1168
- const client = RectangleClient.getRectangleByPoints(element.points);
1169
- const rotatedCornerPoints = rotatePoints(RectangleClient.getCornerPoints(client), RectangleClient.getCenterPoint(client), element.angle);
1170
- return isPolylineHitRectangle(rotatedCornerPoints, rangeRectangle);
1171
- }
1172
- if (PlaitDrawElement.isLine(element)) {
1173
- const points = getLinePoints(board, element);
1174
- return isPolylineHitRectangle(points, rangeRectangle);
1175
- }
1176
- return null;
1177
- };
1178
- const isHitDrawElement = (board, element, point) => {
1179
- const rectangle = board.getRectangle(element);
1180
- const centerPoint = RectangleClient.getCenterPoint(rectangle);
1181
- if (element.angle) {
1182
- point = rotate(point[0], point[1], centerPoint[0], centerPoint[1], -element.angle);
1183
- }
1184
- if (PlaitDrawElement.isGeometry(element)) {
1185
- const fill = getFillByElement(board, element);
1186
- const engine = getEngine(getShape(element));
1187
- const nearestPoint = engine.getNearestPoint(rectangle, point);
1188
- const distance = distanceBetweenPointAndPoint(nearestPoint[0], nearestPoint[1], point[0], point[1]);
1189
- const isHitEdge = distance <= HIT_DISTANCE_BUFFER;
1190
- if (isHitEdge) {
1191
- return isHitEdge;
1192
- }
1193
- // when shape equals text, fill is not allowed
1194
- if (fill !== DefaultGeometryStyle.fill && fill !== TRANSPARENT && !PlaitDrawElement.isText(element)) {
1195
- const isHitInside = engine.isInsidePoint(rectangle, point);
1196
- if (isHitInside) {
1197
- return isHitInside;
1198
- }
1199
- }
1200
- else {
1201
- // if shape equals text, only check text rectangle
1202
- if (PlaitDrawElement.isText(element)) {
1203
- const textClient = getTextRectangle(element);
1204
- let isHitText = RectangleClient.isPointInRectangle(textClient, point);
1205
- return isHitText;
1206
- }
1207
- // check textRectangle of element
1208
- const textClient = engine.getTextRectangle ? engine.getTextRectangle(element) : getTextRectangle(element);
1209
- const isHitTextRectangle = RectangleClient.isPointInRectangle(textClient, point);
1210
- if (isHitTextRectangle) {
1211
- return isHitTextRectangle;
1212
- }
1213
- }
1214
- }
1215
- if (PlaitDrawElement.isImage(element)) {
1216
- const client = RectangleClient.getRectangleByPoints(element.points);
1217
- const rotatedCornerPoints = rotatePoints(RectangleClient.getCornerPoints(client), RectangleClient.getCenterPoint(client), element.angle);
1218
- return isPointInPolygon(point, rotatedCornerPoints);
1219
- }
1220
- if (PlaitDrawElement.isLine(element)) {
1221
- return isHitLine(board, element, point);
1222
- }
1223
- return null;
1224
- };
1225
- const isHitElementInside = (board, element, point) => {
1226
- const rectangle = board.getRectangle(element);
1227
- const centerPoint = RectangleClient.getCenterPoint(rectangle);
1228
- if (element.angle) {
1229
- point = rotate(point[0], point[1], centerPoint[0], centerPoint[1], -element.angle);
1230
- }
1231
- if (PlaitDrawElement.isGeometry(element)) {
1232
- const engine = getEngine(getShape(element));
1233
- const isHitInside = engine.isInsidePoint(rectangle, point);
1234
- if (isHitInside) {
1235
- return isHitInside;
1236
- }
1237
- if (engine.getTextRectangle) {
1238
- const textClient = engine.getTextRectangle(element);
1239
- const isHitTextRectangle = RectangleClient.isPointInRectangle(textClient, point);
1240
- if (isHitTextRectangle) {
1241
- return isHitTextRectangle;
1242
- }
1243
- }
1244
- }
1245
- if (PlaitDrawElement.isImage(element)) {
1246
- return isRectangleHitDrawElement(board, element, { anchor: point, focus: point });
1247
- }
1248
- if (PlaitDrawElement.isLine(element)) {
1249
- return isHitLine(board, element, point);
1250
- }
1251
- return null;
1252
- };
1253
-
1254
1282
  const getCenterPointsOnPolygon$1 = (points) => {
1255
1283
  const centerPoints = [];
1256
1284
  for (let i = 0; i < points.length; i++) {
@@ -2329,36 +2357,45 @@ const getTextRectangle = (element) => {
2329
2357
  y: elementRectangle.y + (elementRectangle.height - height) / 2
2330
2358
  };
2331
2359
  };
2332
- const drawBoundMask = (board, element) => {
2333
- const G = createG();
2360
+ const drawBoundReaction = (board, element, options = { hasMask: true, hasConnector: true }) => {
2361
+ const g = createG();
2334
2362
  const rectangle = RectangleClient.getRectangleByPoints(element.points);
2335
- const activeRectangle = RectangleClient.inflate(rectangle, ACTIVE_STROKE_WIDTH);
2336
- const shape = getShape(element);
2337
- const maskG = drawGeometry(board, activeRectangle, shape, {
2363
+ const activeRectangle = RectangleClient.inflate(rectangle, SNAPPING_STROKE_WIDTH);
2364
+ const shape = getElementShape(element);
2365
+ const strokeG = drawGeometry(board, activeRectangle, shape, {
2338
2366
  stroke: SELECTION_BORDER_COLOR,
2339
- strokeWidth: 1,
2340
- fill: SELECTION_FILL_COLOR,
2341
- fillStyle: 'solid'
2367
+ strokeWidth: SNAPPING_STROKE_WIDTH
2342
2368
  });
2343
- G.appendChild(maskG);
2344
- const connectorPoints = getEngine(shape).getConnectorPoints(activeRectangle);
2345
- connectorPoints.forEach(point => {
2346
- const circleG = drawCircle(PlaitBoard.getRoughSVG(board), point, 6, {
2347
- stroke: '#999999',
2348
- strokeWidth: 1,
2349
- fill: '#FFF',
2369
+ g.appendChild(strokeG);
2370
+ if (options.hasMask) {
2371
+ const maskG = drawGeometry(board, activeRectangle, shape, {
2372
+ stroke: SELECTION_BORDER_COLOR,
2373
+ strokeWidth: 0,
2374
+ fill: SELECTION_FILL_COLOR,
2350
2375
  fillStyle: 'solid'
2351
2376
  });
2352
- G.appendChild(circleG);
2353
- });
2354
- return G;
2377
+ g.appendChild(maskG);
2378
+ }
2379
+ if (options.hasConnector) {
2380
+ const connectorPoints = getEngine(shape).getConnectorPoints(rectangle);
2381
+ connectorPoints.forEach(point => {
2382
+ const circleG = drawCircle(PlaitBoard.getRoughSVG(board), point, 8, {
2383
+ stroke: SELECTION_BORDER_COLOR,
2384
+ strokeWidth: ACTIVE_STROKE_WIDTH,
2385
+ fill: '#FFF',
2386
+ fillStyle: 'solid'
2387
+ });
2388
+ g.appendChild(circleG);
2389
+ });
2390
+ }
2391
+ return g;
2355
2392
  };
2356
2393
  const drawGeometry = (board, outerRectangle, shape, options) => {
2357
2394
  return getEngine(shape).draw(board, outerRectangle, options);
2358
2395
  };
2359
2396
  const getNearestPoint = (element, point) => {
2360
2397
  const rectangle = RectangleClient.getRectangleByPoints(element.points);
2361
- const shape = getShape(element);
2398
+ const shape = getElementShape(element);
2362
2399
  return getEngine(shape).getNearestPoint(rectangle, point);
2363
2400
  };
2364
2401
  const getCenterPointsOnPolygon = (points) => {
@@ -2595,7 +2632,7 @@ const getConnectionPoint = (geometry, connection, direction, delta) => {
2595
2632
  };
2596
2633
  const getVectorByConnection = (boundElement, connection) => {
2597
2634
  const rectangle = RectangleClient.getRectangleByPoints(boundElement.points);
2598
- const shape = getShape(boundElement);
2635
+ const shape = getElementShape(boundElement);
2599
2636
  const engine = getEngine(shape);
2600
2637
  let vector = [0, 0];
2601
2638
  const direction = getDirectionByPointOfRectangle(connection);
@@ -2740,7 +2777,7 @@ const PlaitDrawElement = {
2740
2777
  return false;
2741
2778
  }
2742
2779
  },
2743
- isShape: (value) => {
2780
+ isShapeElement: (value) => {
2744
2781
  return PlaitDrawElement.isImage(value) || PlaitDrawElement.isGeometry(value);
2745
2782
  },
2746
2783
  isBasicShape: (value) => {
@@ -2826,7 +2863,7 @@ const collectLineUpdatedRefsByGeometry = (board, geometry, refs) => {
2826
2863
  const object = { ...line[handle] };
2827
2864
  const linePoints = getLinePoints(board, line);
2828
2865
  const point = isSourceBound ? linePoints[0] : linePoints[linePoints.length - 1];
2829
- object.connection = getConnectionByNearestPoint(board, point, geometry);
2866
+ object.connection = getHitConnection(board, point, geometry);
2830
2867
  const path = PlaitBoard.findPath(board, line);
2831
2868
  const index = refs.findIndex(obj => Path.equals(obj.path, path));
2832
2869
  if (index === -1) {
@@ -2846,7 +2883,7 @@ const collectLineUpdatedRefsByGeometry = (board, geometry, refs) => {
2846
2883
  const connectLineToGeometry = (board, lineElement, handle, geometryElement) => {
2847
2884
  const linePoints = PlaitLine.getPoints(board, lineElement);
2848
2885
  const point = handle === LineHandleKey.source ? linePoints[0] : linePoints[linePoints.length - 1];
2849
- const connection = getConnectionByNearestPoint(board, point, geometryElement);
2886
+ const connection = getHitConnection(board, point, geometryElement);
2850
2887
  if (connection) {
2851
2888
  let source = lineElement.source;
2852
2889
  let target = lineElement.target;
@@ -3271,8 +3308,8 @@ class LineActiveGenerator extends Generator {
3271
3308
  }
3272
3309
  }
3273
3310
 
3274
- const debugKey$1 = 'debug:plait:line-turning';
3275
- const debugGenerator$1 = createDebugGenerator(debugKey$1);
3311
+ const debugKey$2 = 'debug:plait:line-turning';
3312
+ const debugGenerator$2 = createDebugGenerator(debugKey$2);
3276
3313
  class LineComponent extends CommonPluginElement {
3277
3314
  constructor(viewContainerRef, cdr) {
3278
3315
  super(cdr);
@@ -3297,7 +3334,7 @@ class LineComponent extends CommonPluginElement {
3297
3334
  super.ngOnInit();
3298
3335
  this.boundedElements = this.getBoundedElements();
3299
3336
  this.drawText();
3300
- debugGenerator$1.isDebug() && debugGenerator$1.drawCircles(this.board, this.element.points.slice(1, -1), 4, true);
3337
+ debugGenerator$2.isDebug() && debugGenerator$2.drawCircles(this.board, this.element.points.slice(1, -1), 4, true);
3301
3338
  }
3302
3339
  getBoundedElements() {
3303
3340
  const boundedElements = {};
@@ -3766,7 +3803,7 @@ const withLineCreateByDraw = (board) => {
3766
3803
  if (!PlaitBoard.isReadonly(board) && isLinePointer && isDrawingMode(board)) {
3767
3804
  const point = toViewBoxPoint(board, toHostPoint(board, event.x, event.y));
3768
3805
  start = point;
3769
- const hitElement = getHitOutlineGeometry(board, point, REACTION_MARGIN);
3806
+ const hitElement = getSnappingGeometry(board, point);
3770
3807
  if (hitElement) {
3771
3808
  sourceElement = hitElement;
3772
3809
  }
@@ -3802,14 +3839,17 @@ const withLineCreateByDraw = (board) => {
3802
3839
  return board;
3803
3840
  };
3804
3841
 
3842
+ const debugKey$1 = 'debug:plait:align-for-geometry';
3843
+ const debugGenerator$1 = createDebugGenerator(debugKey$1);
3805
3844
  const ALIGN_TOLERANCE = 2;
3806
3845
  const EQUAL_SPACING = 10;
3807
3846
  const ALIGN_SPACING = 24;
3808
- class ResizeAlignReaction {
3847
+ class ResizeSnapReaction {
3809
3848
  constructor(board, activeElements) {
3810
3849
  this.board = board;
3811
3850
  this.activeElements = activeElements;
3812
3851
  this.alignRectangles = this.getAlignRectangle();
3852
+ this.angle = getSelectionAngle(activeElements);
3813
3853
  }
3814
3854
  getAlignRectangle() {
3815
3855
  const elements = findElements(this.board, {
@@ -3817,19 +3857,22 @@ class ResizeAlignReaction {
3817
3857
  recursion: () => false,
3818
3858
  isReverse: false
3819
3859
  });
3820
- return elements.map(item => this.board.getRectangle(item));
3860
+ return elements.map(item => getRectangleByAngle(this.board.getRectangle(item), item.angle) || this.board.getRectangle(item));
3821
3861
  }
3822
- getAlignLineRef(resizeAlignDelta, resizeAlignOptions) {
3862
+ getSnapRef(resizeAlignDelta, resizeSnapOptions) {
3823
3863
  const { deltaX, deltaY } = resizeAlignDelta;
3824
- const { resizeState, originPoint, handlePoint, isFromCorner, isAspectRatio, resizeOriginPoints } = resizeAlignOptions;
3864
+ const { resizeState, originPoint, handlePoint, isFromCorner, isAspectRatio, resizeOriginPoints } = resizeSnapOptions;
3825
3865
  const newResizeState = {
3826
3866
  ...resizeState,
3827
3867
  endPoint: [resizeState.endPoint[0] + deltaX, resizeState.endPoint[1] + deltaY]
3828
3868
  };
3829
3869
  const { xZoom, yZoom } = getResizeZoom(newResizeState, originPoint, handlePoint, isFromCorner, isAspectRatio);
3830
- const activePoints = resizeOriginPoints.map(p => {
3870
+ let activePoints = resizeOriginPoints.map(p => {
3831
3871
  return movePointByZoomAndOriginPoint(p, originPoint, xZoom, yZoom);
3832
3872
  });
3873
+ if (this.angle) {
3874
+ activePoints = resetPointsAfterResize(RectangleClient.getRectangleByPoints(resizeOriginPoints), RectangleClient.getRectangleByPoints(activePoints), RectangleClient.getCenterPoint(RectangleClient.getRectangleByPoints(resizeOriginPoints)), RectangleClient.getCenterPoint(RectangleClient.getRectangleByPoints(activePoints)), this.angle);
3875
+ }
3833
3876
  return {
3834
3877
  deltaX,
3835
3878
  deltaY,
@@ -3838,40 +3881,41 @@ class ResizeAlignReaction {
3838
3881
  activePoints
3839
3882
  };
3840
3883
  }
3841
- getEqualLineDelta(resizeAlignOptions) {
3884
+ getEqualLineDelta(resizeSnapOptions) {
3842
3885
  let equalLineDelta = {
3843
3886
  deltaX: 0,
3844
3887
  deltaY: 0
3845
3888
  };
3846
- const { isAspectRatio, activeRectangle } = resizeAlignOptions;
3889
+ const { isAspectRatio, activeRectangle } = resizeSnapOptions;
3847
3890
  const widthAlignRectangle = this.alignRectangles.find(item => Math.abs(item.width - activeRectangle.width) < ALIGN_TOLERANCE);
3848
3891
  if (widthAlignRectangle) {
3849
3892
  const deltaWidth = widthAlignRectangle.width - activeRectangle.width;
3850
- equalLineDelta.deltaX = deltaWidth * resizeAlignOptions.directionFactors[0];
3893
+ equalLineDelta.deltaX = deltaWidth * resizeSnapOptions.directionFactors[0];
3851
3894
  if (isAspectRatio) {
3852
3895
  const deltaHeight = deltaWidth / (activeRectangle.width / activeRectangle.height);
3853
- equalLineDelta.deltaY = deltaHeight * resizeAlignOptions.directionFactors[1];
3896
+ equalLineDelta.deltaY = deltaHeight * resizeSnapOptions.directionFactors[1];
3854
3897
  return equalLineDelta;
3855
3898
  }
3856
3899
  }
3857
3900
  const heightAlignRectangle = this.alignRectangles.find(item => Math.abs(item.height - activeRectangle.height) < ALIGN_TOLERANCE);
3858
3901
  if (heightAlignRectangle) {
3859
3902
  const deltaHeight = heightAlignRectangle.height - activeRectangle.height;
3860
- equalLineDelta.deltaY = deltaHeight * resizeAlignOptions.directionFactors[1];
3903
+ equalLineDelta.deltaY = deltaHeight * resizeSnapOptions.directionFactors[1];
3861
3904
  if (isAspectRatio) {
3862
3905
  const deltaWidth = deltaHeight * (activeRectangle.width / activeRectangle.height);
3863
- equalLineDelta.deltaX = deltaWidth * resizeAlignOptions.directionFactors[0];
3906
+ equalLineDelta.deltaX = deltaWidth * resizeSnapOptions.directionFactors[0];
3864
3907
  return equalLineDelta;
3865
3908
  }
3866
3909
  }
3867
3910
  return equalLineDelta;
3868
3911
  }
3869
- drawEqualLines(activePoints, resizeAlignOptions) {
3912
+ drawEqualLines(activePoints, resizeSnapOptions) {
3870
3913
  let widthEqualPoints = [];
3871
3914
  let heightEqualPoints = [];
3872
- const drawHorizontalLine = resizeAlignOptions.directionFactors[0] !== 0 || resizeAlignOptions.isAspectRatio;
3873
- const drawVerticalLine = resizeAlignOptions.directionFactors[1] !== 0 || resizeAlignOptions.isAspectRatio;
3874
- const activeRectangle = RectangleClient.getRectangleByPoints(activePoints);
3915
+ const drawHorizontalLine = resizeSnapOptions.directionFactors[0] !== 0 || resizeSnapOptions.isAspectRatio;
3916
+ const drawVerticalLine = resizeSnapOptions.directionFactors[1] !== 0 || resizeSnapOptions.isAspectRatio;
3917
+ const activeRectangle = getRectangleByAngle(RectangleClient.getRectangleByPoints(activePoints), this.angle) ||
3918
+ RectangleClient.getRectangleByPoints(activePoints);
3875
3919
  for (let alignRectangle of this.alignRectangles) {
3876
3920
  if (activeRectangle.width === alignRectangle.width && drawHorizontalLine) {
3877
3921
  widthEqualPoints.push(getEqualLinePoints(alignRectangle, true));
@@ -3889,12 +3933,12 @@ class ResizeAlignReaction {
3889
3933
  const equalLinePoints = [...widthEqualPoints, ...heightEqualPoints];
3890
3934
  return drawEqualLines(this.board, equalLinePoints);
3891
3935
  }
3892
- getAlignLineDelta(resizeAlignOptions) {
3936
+ getAlignLineDelta(resizeSnapOptions) {
3893
3937
  let alignLineDelta = {
3894
3938
  deltaX: 0,
3895
3939
  deltaY: 0
3896
3940
  };
3897
- const { isAspectRatio, activeRectangle, directionFactors } = resizeAlignOptions;
3941
+ const { isAspectRatio, activeRectangle, directionFactors } = resizeSnapOptions;
3898
3942
  const drawHorizontal = directionFactors[0] !== 0 || isAspectRatio;
3899
3943
  const drawVertical = directionFactors[1] !== 0 || isAspectRatio;
3900
3944
  if (drawHorizontal) {
@@ -3923,9 +3967,12 @@ class ResizeAlignReaction {
3923
3967
  }
3924
3968
  return alignLineDelta;
3925
3969
  }
3926
- drawAlignLines(activePoints, resizeAlignOptions) {
3970
+ drawAlignLines(activePoints, resizeSnapOptions) {
3971
+ debugGenerator$1.isDebug() && debugGenerator$1.clear();
3927
3972
  let alignLinePoints = [];
3928
- const activeRectangle = RectangleClient.getRectangleByPoints(activePoints);
3973
+ const activeRectangle = getRectangleByAngle(RectangleClient.getRectangleByPoints(activePoints), this.angle) ||
3974
+ RectangleClient.getRectangleByPoints(activePoints);
3975
+ debugGenerator$1.isDebug() && debugGenerator$1.drawRectangle(this.board, activeRectangle, { stroke: 'green' });
3929
3976
  const alignAxisX = getTripleAlignAxis(activeRectangle, true);
3930
3977
  const alignAxisY = getTripleAlignAxis(activeRectangle, false);
3931
3978
  const alignLineRefs = [
@@ -3963,11 +4010,12 @@ class ResizeAlignReaction {
3963
4010
  alignLinePoints.push([pointStart, pointEnd]);
3964
4011
  }
3965
4012
  };
3966
- const { isAspectRatio, directionFactors } = resizeAlignOptions;
4013
+ const { isAspectRatio, directionFactors } = resizeSnapOptions;
3967
4014
  const drawHorizontal = directionFactors[0] !== 0 || isAspectRatio;
3968
4015
  const drawVertical = directionFactors[1] !== 0 || isAspectRatio;
3969
4016
  for (let index = 0; index < this.alignRectangles.length; index++) {
3970
4017
  const element = this.alignRectangles[index];
4018
+ debugGenerator$1.isDebug() && debugGenerator$1.drawRectangle(this.board, element);
3971
4019
  if (isAlign(alignLineRefs[0].axis, element, alignLineRefs[0].isHorizontal)) {
3972
4020
  alignLineRefs[0].alignRectangles.push(element);
3973
4021
  }
@@ -4007,15 +4055,15 @@ class ResizeAlignReaction {
4007
4055
  }
4008
4056
  return drawAlignLines(this.board, alignLinePoints);
4009
4057
  }
4010
- handleResizeAlign(resizeAlignOptions) {
4058
+ handleResizeSnap(resizeSnapOptions) {
4011
4059
  const alignG = createG();
4012
- let alignLineDelta = this.getEqualLineDelta(resizeAlignOptions);
4060
+ let alignLineDelta = this.getEqualLineDelta(resizeSnapOptions);
4013
4061
  if (alignLineDelta.deltaX === 0 && alignLineDelta.deltaY === 0) {
4014
- alignLineDelta = this.getAlignLineDelta(resizeAlignOptions);
4062
+ alignLineDelta = this.getAlignLineDelta(resizeSnapOptions);
4015
4063
  }
4016
- const equalLineRef = this.getAlignLineRef(alignLineDelta, resizeAlignOptions);
4017
- const equalLinesG = this.drawEqualLines(equalLineRef.activePoints, resizeAlignOptions);
4018
- const alignLinesG = this.drawAlignLines(equalLineRef.activePoints, resizeAlignOptions);
4064
+ const equalLineRef = this.getSnapRef(alignLineDelta, resizeSnapOptions);
4065
+ const equalLinesG = this.drawEqualLines(equalLineRef.activePoints, resizeSnapOptions);
4066
+ const alignLinesG = this.drawAlignLines(equalLineRef.activePoints, resizeSnapOptions);
4019
4067
  alignG.append(equalLinesG, alignLinesG);
4020
4068
  return { ...equalLineRef, alignG };
4021
4069
  }
@@ -4115,8 +4163,7 @@ function getNearestAlignRectangle(alignRectangles, activeRectangle) {
4115
4163
  });
4116
4164
  return nearestRectangle;
4117
4165
  }
4118
-
4119
- function getResizeAlignRef(board, resizeRef, resizeState, resizeOriginPointAndHandlePoint, isAspectRatio, isFromCorner) {
4166
+ function getResizeSnapRef(board, resizeRef, resizeState, resizeOriginPointAndHandlePoint, isAspectRatio, isFromCorner) {
4120
4167
  const { originPoint, handlePoint } = resizeOriginPointAndHandlePoint;
4121
4168
  const { xZoom, yZoom } = getResizeZoom(resizeState, originPoint, handlePoint, isFromCorner, isAspectRatio);
4122
4169
  let activeElements;
@@ -4133,11 +4180,12 @@ function getResizeAlignRef(board, resizeRef, resizeState, resizeOriginPointAndHa
4133
4180
  const points = resizeOriginPoints.map(p => {
4134
4181
  return movePointByZoomAndOriginPoint(p, originPoint, xZoom, yZoom);
4135
4182
  });
4136
- const activeRectangle = RectangleClient.getRectangleByPoints(points);
4137
- const resizeAlignReaction = new ResizeAlignReaction(board, activeElements);
4183
+ const activeRectangle = getRectangleByAngle(RectangleClient.getRectangleByPoints(points), getSelectionAngle(activeElements)) ||
4184
+ RectangleClient.getRectangleByPoints(points);
4185
+ const resizeSnapReaction = new ResizeSnapReaction(board, activeElements);
4138
4186
  const resizeHandlePoint = movePointByZoomAndOriginPoint(handlePoint, originPoint, xZoom, yZoom);
4139
4187
  const [x, y] = getUnitVectorByPointAndPoint(originPoint, resizeHandlePoint);
4140
- return resizeAlignReaction.handleResizeAlign({
4188
+ return resizeSnapReaction.handleResizeSnap({
4141
4189
  resizeState,
4142
4190
  resizeOriginPoints,
4143
4191
  activeRectangle,
@@ -4152,9 +4200,11 @@ function getResizeAlignRef(board, resizeRef, resizeState, resizeOriginPointAndHa
4152
4200
  const debugKey = 'debug:plait:resize-for-rotation';
4153
4201
  const debugGenerator = createDebugGenerator(debugKey);
4154
4202
  function withDrawResize(board) {
4155
- const { afterChange } = board;
4203
+ const { afterChange, drawActiveRectangle } = board;
4156
4204
  let alignG;
4157
4205
  let handleG;
4206
+ let needCustomActiveRectangle = false;
4207
+ let resizeActivePoints = null;
4158
4208
  const canResize = () => {
4159
4209
  const elements = getSelectedElements(board);
4160
4210
  return elements.length > 1 && elements.every(el => PlaitDrawElement.isDrawElement(el));
@@ -4197,10 +4247,11 @@ function withDrawResize(board) {
4197
4247
  resizeState.startPoint = rotatedStartPoint;
4198
4248
  resizeState.endPoint = rotateEndPoint;
4199
4249
  }
4200
- const resizeAlignRef = getResizeAlignRef(board, resizeRef, resizeState, {
4250
+ const resizeAlignRef = getResizeSnapRef(board, resizeRef, resizeState, {
4201
4251
  originPoint,
4202
4252
  handlePoint
4203
4253
  }, isAspectRatio, isFromCorner);
4254
+ resizeActivePoints = resizeAlignRef.activePoints;
4204
4255
  alignG = resizeAlignRef.alignG;
4205
4256
  PlaitBoard.getElementActiveHost(board).append(alignG);
4206
4257
  if (bulkRotationRef) {
@@ -4235,9 +4286,17 @@ function withDrawResize(board) {
4235
4286
  points = rotatedDataPoints(adjustTargetPoints, bulkRotationRef.newCenterPoint, bulkRotationRef.angle);
4236
4287
  }
4237
4288
  else {
4238
- points = target.points.map(p => {
4239
- return movePointByZoomAndOriginPoint(p, originPoint, resizeAlignRef.xZoom, resizeAlignRef.yZoom);
4240
- });
4289
+ if (hasValidAngle(target)) {
4290
+ needCustomActiveRectangle = true;
4291
+ }
4292
+ if (hasValidAngle(target) && isAxisChangedByAngle(target.angle)) {
4293
+ points = getResizePointsByOtherwiseAxis(board, target.points, originPoint, resizeAlignRef.xZoom, resizeAlignRef.yZoom);
4294
+ }
4295
+ else {
4296
+ points = target.points.map(p => {
4297
+ return movePointByZoomAndOriginPoint(p, originPoint, resizeAlignRef.xZoom, resizeAlignRef.yZoom);
4298
+ });
4299
+ }
4241
4300
  }
4242
4301
  if (PlaitDrawElement.isGeometry(target)) {
4243
4302
  const { height: textHeight } = getFirstTextManage(target).getSize();
@@ -4264,6 +4323,12 @@ function withDrawResize(board) {
4264
4323
  afterResize: (resizeRef) => {
4265
4324
  alignG?.remove();
4266
4325
  alignG = null;
4326
+ if (needCustomActiveRectangle) {
4327
+ needCustomActiveRectangle = false;
4328
+ resizeActivePoints = null;
4329
+ const selectedElements = getSelectedElements(board);
4330
+ Transforms.addSelectionWithTemporaryElements(board, selectedElements);
4331
+ }
4267
4332
  }
4268
4333
  };
4269
4334
  withResize(board, options);
@@ -4276,7 +4341,9 @@ function withDrawResize(board) {
4276
4341
  if (canResize() && !isSelectionMoving(board)) {
4277
4342
  handleG = createG();
4278
4343
  const elements = getSelectedElements(board);
4279
- const boundingRectangle = getRectangleByElements(board, elements, false);
4344
+ const boundingRectangle = needCustomActiveRectangle
4345
+ ? RectangleClient.getRectangleByPoints(resizeActivePoints)
4346
+ : getRectangleByElements(board, elements, false);
4280
4347
  let corners = RectangleClient.getCornerPoints(boundingRectangle);
4281
4348
  const angle = getSelectionAngle(elements);
4282
4349
  if (angle) {
@@ -4290,6 +4357,16 @@ function withDrawResize(board) {
4290
4357
  PlaitBoard.getElementActiveHost(board).append(handleG);
4291
4358
  }
4292
4359
  };
4360
+ board.drawActiveRectangle = () => {
4361
+ if (needCustomActiveRectangle) {
4362
+ const rectangle = RectangleClient.getRectangleByPoints(resizeActivePoints);
4363
+ return drawRectangle(board, RectangleClient.inflate(rectangle, ACTIVE_STROKE_WIDTH), {
4364
+ stroke: SELECTION_BORDER_COLOR,
4365
+ strokeWidth: ACTIVE_STROKE_WIDTH
4366
+ });
4367
+ }
4368
+ return drawActiveRectangle();
4369
+ };
4293
4370
  return board;
4294
4371
  }
4295
4372
  const getResizeOriginPointAndHandlePoint = (board, resizeRef) => {
@@ -4345,6 +4422,24 @@ const movePointByZoomAndOriginPoint = (p, resizeOriginPoint, xZoom, yZoom) => {
4345
4422
  const offsetY = (p[1] - resizeOriginPoint[1]) * yZoom;
4346
4423
  return [p[0] + offsetX, p[1] + offsetY];
4347
4424
  };
4425
+ /**
4426
+ * 1. Rotate 90°
4427
+ * 2. Scale based on the rotated points
4428
+ * 3. Reverse rotate the scaled points by 90°
4429
+ */
4430
+ const getResizePointsByOtherwiseAxis = (board, points, resizeOriginPoint, xZoom, yZoom) => {
4431
+ const currentRectangle = RectangleClient.getRectangleByPoints(points);
4432
+ debugGenerator.isDebug() && debugGenerator.drawRectangle(board, currentRectangle, { stroke: 'black' });
4433
+ let resultPoints = points;
4434
+ resultPoints = rotatePoints(resultPoints, RectangleClient.getCenterPoint(currentRectangle), (1 / 2) * Math.PI);
4435
+ debugGenerator.isDebug() && debugGenerator.drawRectangle(board, resultPoints, { stroke: 'blue' });
4436
+ resultPoints = resultPoints.map(p => {
4437
+ return movePointByZoomAndOriginPoint(p, resizeOriginPoint, xZoom, yZoom);
4438
+ });
4439
+ debugGenerator.isDebug() && debugGenerator.drawRectangle(board, resultPoints);
4440
+ const newRectangle = RectangleClient.getRectangleByPoints(resultPoints);
4441
+ return rotatePoints(resultPoints, RectangleClient.getCenterPoint(newRectangle), -(1 / 2) * Math.PI);
4442
+ };
4348
4443
 
4349
4444
  const withGeometryResize = (board) => {
4350
4445
  let alignG;
@@ -4376,26 +4471,19 @@ const withGeometryResize = (board) => {
4376
4471
  },
4377
4472
  onResize: (resizeRef, resizeState) => {
4378
4473
  const centerPoint = RectangleClient.getCenterPoint(RectangleClient.getRectangleByPoints(resizeRef.element.points));
4379
- const angle = resizeRef.element.angle;
4380
- if (angle) {
4381
- const [rotatedStartPoint, rotateEndPoint] = rotatePoints([resizeState.startPoint, resizeState.endPoint], centerPoint, -resizeRef.element.angle);
4382
- resizeState.startPoint = rotatedStartPoint;
4383
- resizeState.endPoint = rotateEndPoint;
4384
- }
4474
+ resizeState.startPoint = rotateAntiPointsByElement(resizeState.startPoint, resizeRef.element) || resizeState.startPoint;
4475
+ resizeState.endPoint = rotateAntiPointsByElement(resizeState.endPoint, resizeRef.element) || resizeState.endPoint;
4385
4476
  alignG?.remove();
4386
4477
  const isFromCorner = isCornerHandle(board, resizeRef.handle);
4387
4478
  const isAspectRatio = resizeState.isShift || PlaitDrawElement.isImage(resizeRef.element);
4388
4479
  const { originPoint, handlePoint } = getResizeOriginPointAndHandlePoint(board, resizeRef);
4389
- const resizeAlignRef = getResizeAlignRef(board, resizeRef, resizeState, {
4480
+ const resizeAlignRef = getResizeSnapRef(board, resizeRef, resizeState, {
4390
4481
  originPoint,
4391
4482
  handlePoint
4392
4483
  }, isAspectRatio, isFromCorner);
4393
4484
  alignG = resizeAlignRef.alignG;
4394
4485
  PlaitBoard.getElementActiveHost(board).append(alignG);
4395
4486
  let points = resizeAlignRef.activePoints;
4396
- if (angle) {
4397
- points = resetPointsAfterResize(resizeRef.rectangle, RectangleClient.getRectangleByPoints(points), centerPoint, RectangleClient.getCenterPoint(RectangleClient.getRectangleByPoints(points)), angle);
4398
- }
4399
4487
  if (PlaitDrawElement.isGeometry(resizeRef.element)) {
4400
4488
  const { height: textHeight } = getFirstTextManage(resizeRef.element).getSize();
4401
4489
  DrawTransforms.resizeGeometry(board, points, textHeight, resizeRef.path);
@@ -4448,8 +4536,7 @@ const withLineResize = (board) => {
4448
4536
  resizeRef.handle !== LineResizeHandle.source &&
4449
4537
  resizeRef.handle !== LineResizeHandle.target) {
4450
4538
  const params = getElbowLineRouteOptions(board, resizeRef.element);
4451
- const isIntersect = isSourceAndTargetIntersect(params);
4452
- if (isIntersect) {
4539
+ if (isUseDefaultOrthogonalRoute(resizeRef.element, params)) {
4453
4540
  return;
4454
4541
  }
4455
4542
  const points = [...resizeRef.element.points];
@@ -4468,12 +4555,12 @@ const withLineResize = (board) => {
4468
4555
  let source = { ...resizeRef.element.source };
4469
4556
  let target = { ...resizeRef.element.target };
4470
4557
  let handleIndex = resizeRef.handleIndex;
4471
- const hitElement = getHitOutlineGeometry(board, resizeState.endPoint, REACTION_MARGIN);
4558
+ const hitElement = getSnappingGeometry(board, resizeState.endPoint);
4472
4559
  if (resizeRef.handle === LineResizeHandle.source || resizeRef.handle === LineResizeHandle.target) {
4473
4560
  const object = resizeRef.handle === LineResizeHandle.source ? source : target;
4474
4561
  points[handleIndex] = resizeState.endPoint;
4475
4562
  if (hitElement) {
4476
- object.connection = getConnectionByNearestPoint(board, rotateAntiPointsByElement(resizeState.endPoint, hitElement) || resizeState.endPoint, hitElement);
4563
+ object.connection = getHitConnection(board, resizeState.endPoint, hitElement);
4477
4564
  object.boundId = hitElement.id;
4478
4565
  }
4479
4566
  else {
@@ -4515,7 +4602,8 @@ const withLineResize = (board) => {
4515
4602
  const newPoints = [...points];
4516
4603
  newPoints[0] = drawPoints[0];
4517
4604
  newPoints[newPoints.length - 1] = drawPoints[drawPoints.length - 1];
4518
- if (resizeRef.element.shape !== LineShape.elbow) {
4605
+ if (resizeRef.element.shape !== LineShape.elbow ||
4606
+ (resizeRef.element.shape === LineShape.elbow && newPoints.length === 2)) {
4519
4607
  newPoints.forEach((point, index) => {
4520
4608
  if (index === handleIndex)
4521
4609
  return;
@@ -4579,25 +4667,24 @@ const withLineBoundReaction = (board) => {
4579
4667
  return PlaitDrawElement.isLine(element) && isSourceOrTarget;
4580
4668
  });
4581
4669
  if (isLinePointer || isLineResizing) {
4582
- const hitElement = getHitOutlineGeometry(board, movingPoint, -4);
4670
+ const hitElement = getHitGeometry(board, movingPoint);
4583
4671
  if (hitElement) {
4584
- const rectangle = RectangleClient.getRectangleByPoints(hitElement.points);
4585
- boundShapeG = drawBoundMask(board, hitElement);
4586
- let nearestPoint = getNearestPoint(hitElement, rotateAntiPointsByElement(movingPoint, hitElement) || movingPoint);
4587
- const activeRectangle = RectangleClient.inflate(rectangle, ACTIVE_STROKE_WIDTH);
4588
- const hitConnector = getHitConnectorPoint(nearestPoint, hitElement, activeRectangle);
4589
- nearestPoint = hitConnector ? hitConnector : nearestPoint;
4590
- const circleG = drawCircle(PlaitBoard.getRoughSVG(board), nearestPoint, 6, {
4591
- stroke: SELECTION_BORDER_COLOR,
4592
- strokeWidth: ACTIVE_STROKE_WIDTH,
4593
- fill: SELECTION_BORDER_COLOR,
4594
- fillStyle: 'solid'
4595
- });
4596
- boundShapeG.appendChild(circleG);
4597
- PlaitBoard.getElementActiveHost(board).append(boundShapeG);
4672
+ const ref = getSnappingRef(board, hitElement, movingPoint);
4673
+ const isSnapping = ref.isHitEdge || ref.isHitConnector;
4674
+ boundShapeG = drawBoundReaction(board, hitElement, { hasMask: isSnapping, hasConnector: true });
4675
+ if (isSnapping) {
4676
+ const circleG = drawCircle(PlaitBoard.getRoughSVG(board), ref.connectorPoint || ref.edgePoint, 6, {
4677
+ stroke: SELECTION_BORDER_COLOR,
4678
+ strokeWidth: SNAPPING_STROKE_WIDTH,
4679
+ fill: SELECTION_BORDER_COLOR,
4680
+ fillStyle: 'solid'
4681
+ });
4682
+ boundShapeG.appendChild(circleG);
4683
+ }
4598
4684
  if (hasValidAngle(hitElement)) {
4599
- setAngleForG(boundShapeG, RectangleClient.getCenterPoint(rectangle), hitElement.angle);
4685
+ setAngleForG(boundShapeG, RectangleClient.getCenterPointByPoints(hitElement.points), hitElement.angle);
4600
4686
  }
4687
+ PlaitBoard.getElementActiveHost(board).append(boundShapeG);
4601
4688
  }
4602
4689
  }
4603
4690
  pointerMove(event);
@@ -4746,7 +4833,7 @@ const withLineAutoCompleteReaction = (board) => {
4746
4833
  const selectedElements = getSelectedDrawElements(board);
4747
4834
  const targetElement = selectedElements.length === 1 && selectedElements[0];
4748
4835
  const movingPoint = toViewBoxPoint(board, toHostPoint(board, event.x, event.y));
4749
- if (!PlaitBoard.isReadonly(board) && !isSelectionMoving(board) && targetElement && PlaitDrawElement.isShape(targetElement)) {
4836
+ if (!PlaitBoard.isReadonly(board) && !isSelectionMoving(board) && targetElement && PlaitDrawElement.isShapeElement(targetElement)) {
4750
4837
  const points = getAutoCompletePoints(targetElement);
4751
4838
  const hitIndex = getHitIndexOfAutoCompletePoint(rotateAntiPointsByElement(movingPoint, targetElement) || movingPoint, points);
4752
4839
  const hitPoint = points[hitIndex];
@@ -4774,7 +4861,7 @@ const withLineAutoCompleteReaction = (board) => {
4774
4861
  const WithLineAutoCompletePluginKey = 'plait-line-auto-complete-plugin-key';
4775
4862
  const withLineAutoComplete = (board) => {
4776
4863
  const { pointerDown, pointerMove, globalPointerUp } = board;
4777
- let startPoint = null;
4864
+ let autoCompletePoint = null;
4778
4865
  let lineShapeG = null;
4779
4866
  let sourceElement;
4780
4867
  let temporaryElement;
@@ -4782,13 +4869,13 @@ const withLineAutoComplete = (board) => {
4782
4869
  const selectedElements = getSelectedDrawElements(board);
4783
4870
  const targetElement = selectedElements.length === 1 && selectedElements[0];
4784
4871
  const clickPoint = toViewBoxPoint(board, toHostPoint(board, event.x, event.y));
4785
- if (!PlaitBoard.isReadonly(board) && targetElement && PlaitDrawElement.isShape(targetElement)) {
4872
+ if (!PlaitBoard.isReadonly(board) && targetElement && PlaitDrawElement.isShapeElement(targetElement)) {
4786
4873
  const points = getAutoCompletePoints(targetElement);
4787
4874
  const index = getHitIndexOfAutoCompletePoint(rotateAntiPointsByElement(clickPoint, targetElement) || clickPoint, points);
4788
4875
  const hitPoint = points[index];
4789
4876
  if (hitPoint) {
4790
4877
  temporaryDisableSelection(board);
4791
- startPoint = hitPoint;
4878
+ autoCompletePoint = hitPoint;
4792
4879
  sourceElement = targetElement;
4793
4880
  BoardTransforms.updatePointerType(board, LineShape.elbow);
4794
4881
  }
@@ -4799,18 +4886,19 @@ const withLineAutoComplete = (board) => {
4799
4886
  lineShapeG?.remove();
4800
4887
  lineShapeG = createG();
4801
4888
  let movingPoint = toViewBoxPoint(board, toHostPoint(board, event.x, event.y));
4802
- if (startPoint && sourceElement) {
4803
- const distance = distanceBetweenPointAndPoint(...movingPoint, ...(rotatePointsByElement(startPoint, sourceElement) || startPoint));
4889
+ if (autoCompletePoint && sourceElement) {
4890
+ const distance = distanceBetweenPointAndPoint(...(rotateAntiPointsByElement(movingPoint, sourceElement) || movingPoint), ...autoCompletePoint);
4804
4891
  if (distance > PRESS_AND_MOVE_BUFFER) {
4805
4892
  const rectangle = RectangleClient.getRectangleByPoints(sourceElement.points);
4806
- const shape = getShape(sourceElement);
4893
+ const shape = getElementShape(sourceElement);
4807
4894
  const engine = getEngine(shape);
4808
- let sourcePoint = startPoint;
4809
4895
  if (engine.getNearestCrossingPoint) {
4810
- const crossingPoint = engine.getNearestCrossingPoint(rectangle, startPoint);
4811
- sourcePoint = crossingPoint;
4896
+ const crossingPoint = engine.getNearestCrossingPoint(rectangle, autoCompletePoint);
4897
+ autoCompletePoint = crossingPoint;
4812
4898
  }
4813
- temporaryElement = handleLineCreating(board, LineShape.elbow, rotatePointsByElement(sourcePoint, sourceElement) || sourcePoint, movingPoint, sourceElement, lineShapeG);
4899
+ // source point must be click point
4900
+ const rotatedSourcePoint = rotatePointsByElement(autoCompletePoint, sourceElement) || autoCompletePoint;
4901
+ temporaryElement = handleLineCreating(board, LineShape.elbow, rotatedSourcePoint, movingPoint, sourceElement, lineShapeG);
4814
4902
  }
4815
4903
  }
4816
4904
  pointerMove(event);
@@ -4824,9 +4912,9 @@ const withLineAutoComplete = (board) => {
4824
4912
  ?.afterComplete;
4825
4913
  afterComplete && afterComplete(temporaryElement);
4826
4914
  }
4827
- if (startPoint) {
4915
+ if (autoCompletePoint) {
4828
4916
  BoardTransforms.updatePointerType(board, PlaitPointerType.selection);
4829
- startPoint = null;
4917
+ autoCompletePoint = null;
4830
4918
  }
4831
4919
  lineShapeG?.remove();
4832
4920
  lineShapeG = null;
@@ -4968,8 +5056,8 @@ const withDraw = (board) => {
4968
5056
  }
4969
5057
  return isAlign(element);
4970
5058
  };
4971
- board.getRelatedFragment = (elements) => {
4972
- const selectedElements = getSelectedElements(board);
5059
+ board.getRelatedFragment = (elements, originData) => {
5060
+ const selectedElements = originData || getSelectedElements(board);
4973
5061
  const lineElements = board.children.filter(element => PlaitDrawElement.isLine(element));
4974
5062
  const activeLines = lineElements.filter(line => {
4975
5063
  const source = selectedElements.find(element => element.id === line.source.boundId);
@@ -4977,7 +5065,7 @@ const withDraw = (board) => {
4977
5065
  const isSelected = selectedElements.includes(line);
4978
5066
  return source && target && !isSelected;
4979
5067
  });
4980
- return getRelatedFragment([...elements, ...activeLines]);
5068
+ return getRelatedFragment([...elements, ...activeLines], originData);
4981
5069
  };
4982
5070
  return withDrawResize(withLineTextMove(withLineAutoCompleteReaction(withLineText(withLineBoundReaction(withLineResize(withGeometryResize(withLineCreateByDraw(withLineAutoComplete(withGeometryCreateByDrag(withGeometryCreateByDrawing(withDrawFragment(withDrawHotkey(board)))))))))))));
4983
5071
  };
@@ -4986,5 +5074,5 @@ const withDraw = (board) => {
4986
5074
  * Generated bundle index. Do not edit.
4987
5075
  */
4988
5076
 
4989
- export { BasicShapes, DEFAULT_IMAGE_WIDTH, DefaultBasicShapeProperty, DefaultConnectorProperty, DefaultDataProperty, DefaultDecisionProperty, DefaultFlowchartProperty, DefaultFlowchartPropertyMap, DefaultGeometryActiveStyle, DefaultGeometryStyle, DefaultManualInputProperty, DefaultMergeProperty, DefaultTextProperty, DrawThemeColors, DrawTransforms, FlowchartSymbols, GeometryComponent, GeometryThreshold, LineComponent, LineHandleKey, LineMarkerType, LineShape, MemorizeKey, PlaitDrawElement, PlaitGeometry, PlaitLine, Q2C, REACTION_MARGIN, ShapeDefaultSpace, StrokeStyle, WithLineAutoCompletePluginKey, alignElbowSegment, alignPoints, createDefaultFlowchart, createDefaultGeometry, createGeometryElement, createLineElement, createTextElement, drawBoundMask, drawGeometry, drawLine, drawLineArrow, getAutoCompletePoints, getBasicPointers, getCenterPointsOnPolygon, getConnectionByNearestPoint, getConnectionPoint, getCurvePoints, getDefaultFlowchartProperty, getDefaultGeometryPoints, getDefaultGeometryProperty, getDefaultTextPoints, getDrawDefaultStrokeColor, getElbowLineRouteOptions, getElbowPoints, getFillByElement, getFlowchartDefaultFill, getFlowchartPointers, getGeometryPointers, getHitConnectorPoint, getHitIndexOfAutoCompletePoint, getIndexAndDeleteCountByKeyPoint, getLineDashByElement, getLineHandleRefPair, getLineMemorizedLatest, getLinePointers, getLinePoints, getLineTextRectangle, getLines, getMemorizeKey, getMemorizedLatestByPointer, getMemorizedLatestShape, getMidKeyPoints, getMiddlePoints, getMirrorDataPoints, getNearestPoint, getNextRenderPoints, getNextSourceAndTargetPoints, getResizedPreviousAndNextPoint, getSelectedDrawElements, getSelectedGeometryElements, getSelectedImageElements, getSelectedLineElements, getSourceAndTargetRectangle, getStrokeColorByElement, getStrokeStyleByElement, getStrokeWidthByElement, getTextRectangle, getTextShapeProperty, getVectorByConnection, handleLineCreating, hasIllegalElbowPoint, insertElement, isHitDrawElement, isHitElementInside, isHitLine, isHitLineText, isHitPolyLine, isRectangleHitDrawElement, isTextExceedingBounds, isUpdatedHandleIndex, memorizeLatestShape, memorizeLatestText, withDraw, withLineAutoComplete };
5077
+ export { BasicShapes, DEFAULT_IMAGE_WIDTH, DefaultBasicShapeProperty, DefaultConnectorProperty, DefaultDataProperty, DefaultDecisionProperty, DefaultFlowchartProperty, DefaultFlowchartPropertyMap, DefaultGeometryActiveStyle, DefaultGeometryStyle, DefaultManualInputProperty, DefaultMergeProperty, DefaultTextProperty, DrawThemeColors, DrawTransforms, FlowchartSymbols, GeometryComponent, GeometryThreshold, LINE_HIT_GEOMETRY_BUFFER, LINE_SNAPPING_BUFFER, LINE_SNAPPING_CONNECTOR_BUFFER, LineComponent, LineHandleKey, LineMarkerType, LineShape, MemorizeKey, PlaitDrawElement, PlaitGeometry, PlaitLine, Q2C, ShapeDefaultSpace, StrokeStyle, WithLineAutoCompletePluginKey, alignElbowSegment, alignPoints, createDefaultFlowchart, createDefaultGeometry, createGeometryElement, createLineElement, createTextElement, drawBoundReaction, drawGeometry, drawLine, drawLineArrow, getAutoCompletePoints, getBasicPointers, getCenterPointsOnPolygon, getConnectionPoint, getCurvePoints, getDefaultFlowchartProperty, getDefaultGeometryPoints, getDefaultGeometryProperty, getDefaultTextPoints, getDrawDefaultStrokeColor, getElbowLineRouteOptions, getElbowPoints, getFillByElement, getFlowchartDefaultFill, getFlowchartPointers, getGeometryPointers, getHitConnection, getHitConnectorPoint, getHitIndexOfAutoCompletePoint, getIndexAndDeleteCountByKeyPoint, getLineDashByElement, getLineHandleRefPair, getLineMemorizedLatest, getLinePointers, getLinePoints, getLineTextRectangle, getLines, getMemorizeKey, getMemorizedLatestByPointer, getMemorizedLatestShape, getMidKeyPoints, getMiddlePoints, getMirrorDataPoints, getNearestPoint, getNextRenderPoints, getNextSourceAndTargetPoints, getResizedPreviousAndNextPoint, getSelectedDrawElements, getSelectedGeometryElements, getSelectedImageElements, getSelectedLineElements, getSourceAndTargetRectangle, getStrokeColorByElement, getStrokeStyleByElement, getStrokeWidthByElement, getTextRectangle, getTextShapeProperty, getVectorByConnection, handleLineCreating, hasIllegalElbowPoint, insertElement, isHitDrawElement, isHitEdgeOfShape, isHitElementInside, isHitLine, isHitLineText, isHitPolyLine, isInsideOfShape, isRectangleHitDrawElement, isSelfLoop, isTextExceedingBounds, isUpdatedHandleIndex, isUseDefaultOrthogonalRoute, memorizeLatestShape, memorizeLatestText, withDraw, withLineAutoComplete };
4990
5078
  //# sourceMappingURL=plait-draw.mjs.map