@plait/draw 0.31.0 → 0.33.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 (46) hide show
  1. package/constants/geometry.d.ts +1 -0
  2. package/constants/index.d.ts +1 -0
  3. package/constants/line.d.ts +1 -0
  4. package/constants/theme.d.ts +26 -0
  5. package/esm2022/constants/geometry.mjs +2 -1
  6. package/esm2022/constants/index.mjs +2 -1
  7. package/esm2022/constants/line.mjs +2 -1
  8. package/esm2022/constants/theme.mjs +28 -0
  9. package/esm2022/generators/auto-complete.generator.mjs +44 -0
  10. package/esm2022/generators/geometry-shape.generator.mjs +4 -4
  11. package/esm2022/generators/line-active.generator.mjs +2 -2
  12. package/esm2022/generators/line.generator.mjs +2 -2
  13. package/esm2022/geometry.component.mjs +14 -8
  14. package/esm2022/image.component.mjs +2 -2
  15. package/esm2022/interfaces/index.mjs +3 -3
  16. package/esm2022/line.component.mjs +10 -9
  17. package/esm2022/plugins/with-auto-complete-reaction.mjs +35 -0
  18. package/esm2022/plugins/with-auto-complete.mjs +61 -0
  19. package/esm2022/plugins/with-draw.mjs +10 -6
  20. package/esm2022/plugins/with-geometry-create.mjs +3 -6
  21. package/esm2022/plugins/with-line-create.mjs +9 -24
  22. package/esm2022/plugins/with-line-resize.mjs +7 -16
  23. package/esm2022/transforms/geometry.mjs +6 -3
  24. package/esm2022/transforms/index.mjs +4 -3
  25. package/esm2022/utils/geometry.mjs +24 -9
  26. package/esm2022/utils/hit.mjs +22 -8
  27. package/esm2022/utils/line-arrow.mjs +22 -17
  28. package/esm2022/utils/line.mjs +42 -18
  29. package/esm2022/utils/style/stroke.mjs +10 -5
  30. package/fesm2022/plait-draw.mjs +355 -148
  31. package/fesm2022/plait-draw.mjs.map +1 -1
  32. package/generators/auto-complete.generator.d.ts +13 -0
  33. package/generators/geometry-shape.generator.d.ts +1 -1
  34. package/generators/line-active.generator.d.ts +1 -1
  35. package/generators/line.generator.d.ts +1 -1
  36. package/geometry.component.d.ts +2 -0
  37. package/package.json +1 -1
  38. package/plugins/with-auto-complete-reaction.d.ts +2 -0
  39. package/plugins/with-auto-complete.d.ts +7 -0
  40. package/transforms/geometry.d.ts +2 -1
  41. package/transforms/index.d.ts +1 -0
  42. package/utils/clipboard.d.ts +1 -1
  43. package/utils/geometry.d.ts +6 -2
  44. package/utils/hit.d.ts +2 -0
  45. package/utils/line.d.ts +2 -1
  46. package/utils/style/stroke.d.ts +3 -2
@@ -1,5 +1,5 @@
1
- import { PlaitElement, ACTIVE_STROKE_WIDTH, PlaitBoard, setStrokeLinecap, isPointInPolygon, getNearestPointBetweenPointAndSegments, RectangleClient, isPointInEllipse, drawRectangle, drawRoundRectangle, isPointInRoundRectangle, Transforms, clearSelectedElement, addSelectedElement, PlaitNode, Point, BOARD_TO_HOST, transformPoint, toPoint, idCreator, createG, BoardTransforms, PlaitPointerType, preventTouchMove, createForeignObject, SELECTION_BORDER_COLOR, SELECTION_FILL_COLOR, drawCircle, distanceBetweenPointAndSegment, arrowPoints, createPath, drawLinearPath, rotate, getElementById, distanceBetweenPointAndPoint, distanceBetweenPointAndSegments, createMask, createRect, findElements, getSelectedElements, isPolylineHitRectangle, isSelectionMoving, PlaitPluginElementComponent, setClipboardData, getDataFromClipboard, depthFirstRecursion, getIsRecursionFunc, getHitElementByPoint } from '@plait/core';
2
- import { getRectangleByPoints, Generator, normalizeShapePoints, isDndMode, isDrawingMode, getFactorByPoints, getDirectionByVector, getOppositeDirection, getDirectionFactor, DEFAULT_ROUTE_MARGIN, reduceRouteMargin, getNextPoint, generateElbowLineRoute, getPoints, removeDuplicatePoints, getPointByVector, getPointOnPolyline, getDirectionByPointOfRectangle, rotateVectorAnti90, TRANSPARENT, CommonPluginElement, ActiveGenerator, WithTextPluginKey, RESIZE_HANDLE_DIAMETER, PRIMARY_COLOR, isVirtualKey, isDelete, isSpaceHotkey, acceptImageTypes, getElementOfFocusedImage, buildImage, getRectangleResizeHandleRefs, ResizeHandle, withResize, isResizingByCondition, getRatioByPoint, ImageGenerator } from '@plait/common';
1
+ import { PlaitElement, ACTIVE_STROKE_WIDTH, ThemeColorMode, PlaitBoard, setStrokeLinecap, isPointInPolygon, getNearestPointBetweenPointAndSegments, RectangleClient, isPointInEllipse, drawRectangle, drawRoundRectangle, isPointInRoundRectangle, Transforms, clearSelectedElement, addSelectedElement, PlaitNode, Point, BOARD_TO_HOST, transformPoint, toPoint, idCreator, createG, BoardTransforms, PlaitPointerType, preventTouchMove, createForeignObject, SELECTION_BORDER_COLOR, SELECTION_FILL_COLOR, drawCircle, distanceBetweenPointAndSegment, arrowPoints, createPath, drawLinearPath, distanceBetweenPointAndPoint, rotate, depthFirstRecursion, getIsRecursionFunc, getElementById, Direction, catmullRomFitting, distanceBetweenPointAndSegments, createMask, createRect, findElements, getSelectedElements, isPolylineHitRectangle, isSelectionMoving, PlaitPluginElementComponent, setClipboardData, getDataFromClipboard, getHitElementByPoint, temporaryDisableSelection } from '@plait/core';
2
+ import { getRectangleByPoints, Generator, normalizeShapePoints, isDndMode, isDrawingMode, RESIZE_HANDLE_DIAMETER, getExtendPoint, getFactorByPoints, getRectangleResizeHandleRefs, getDirectionByVector, getOppositeDirection, getDirectionFactor, DEFAULT_ROUTE_MARGIN, reduceRouteMargin, getNextPoint, generateElbowLineRoute, getPoints, removeDuplicatePoints, getPointByVector, getPointOnPolyline, getDirectionByPointOfRectangle, rotateVectorAnti90, TRANSPARENT, CommonPluginElement, ActiveGenerator, WithTextPluginKey, PRIMARY_COLOR, isVirtualKey, isDelete, isSpaceHotkey, acceptImageTypes, getElementOfFocusedImage, buildImage, ResizeHandle, withResize, isResizingByCondition, getRatioByPoint, ImageGenerator } from '@plait/common';
3
3
  import { AlignEditor, Alignment, DEFAULT_FONT_SIZE, buildText, TextManage, getTextFromClipboard, getTextSize } from '@plait/text';
4
4
  import { isKeyHotkey } from 'is-hotkey';
5
5
  import { pointsOnBezierCurves } from 'points-on-curve';
@@ -122,6 +122,7 @@ const DefaultFlowchartPropertyMap = {
122
122
  [FlowchartSymbols.delay]: DefaultFlowchartProperty,
123
123
  [FlowchartSymbols.storedData]: DefaultFlowchartProperty
124
124
  };
125
+ const REACTION_MARGIN = -4;
125
126
 
126
127
  const getGeometryPointers = () => {
127
128
  return [...Object.keys(BasicShapes), ...Object.keys(FlowchartSymbols)];
@@ -138,6 +139,33 @@ const getLinePointers = () => {
138
139
 
139
140
  const DEFAULT_IMAGE_WIDTH = 1000;
140
141
 
142
+ const DrawThemeColors = {
143
+ [ThemeColorMode.default]: {
144
+ strokeColor: '#333333',
145
+ fill: '#FFFFFF'
146
+ },
147
+ [ThemeColorMode.colorful]: {
148
+ strokeColor: '#06ADBF',
149
+ fill: '#CDEFF2'
150
+ },
151
+ [ThemeColorMode.soft]: {
152
+ strokeColor: '#6D89C1',
153
+ fill: '#DADFEB'
154
+ },
155
+ [ThemeColorMode.retro]: {
156
+ strokeColor: '#E9C358',
157
+ fill: '#F6EDCF'
158
+ },
159
+ [ThemeColorMode.dark]: {
160
+ strokeColor: '#FFFFFF',
161
+ fill: '#434343'
162
+ },
163
+ [ThemeColorMode.starry]: {
164
+ strokeColor: '#42ABE5',
165
+ fill: '#163F5A'
166
+ }
167
+ };
168
+
141
169
  const getStrokeWidthByElement = (element) => {
142
170
  if (PlaitDrawElement.isText(element)) {
143
171
  return 0;
@@ -145,12 +173,16 @@ const getStrokeWidthByElement = (element) => {
145
173
  const strokeWidth = element.strokeWidth || DefaultGeometryStyle.strokeWidth;
146
174
  return strokeWidth;
147
175
  };
148
- const getStrokeColorByElement = (element) => {
149
- const strokeColor = element.strokeColor || DefaultGeometryStyle.strokeColor;
176
+ const getStrokeColorByElement = (board, element) => {
177
+ const defaultColor = getDrawDefaultStrokeColor(board.theme.themeColorMode);
178
+ const strokeColor = element.strokeColor || defaultColor;
150
179
  return strokeColor;
151
180
  };
152
- const getFillByElement = (element) => {
153
- const fill = element.fill || DefaultGeometryStyle.fill;
181
+ const getFillByElement = (board, element) => {
182
+ const defaultFill = PlaitDrawElement.isFlowchart(element)
183
+ ? getFlowchartDefaultFill(board.theme.themeColorMode)
184
+ : DefaultGeometryStyle.fill;
185
+ const fill = element.fill || defaultFill;
154
186
  return fill;
155
187
  };
156
188
  const getLineDashByElement = (element) => {
@@ -1172,15 +1204,15 @@ class GeometryShapeGenerator extends Generator {
1172
1204
  canDraw(element, data) {
1173
1205
  return true;
1174
1206
  }
1175
- baseDraw(element, data) {
1207
+ draw(element, data) {
1176
1208
  const rectangle = getRectangleByPoints(element.points);
1177
1209
  const shape = element.shape;
1178
1210
  if (shape === BasicShapes.text) {
1179
1211
  return;
1180
1212
  }
1181
1213
  const strokeWidth = getStrokeWidthByElement(element);
1182
- const strokeColor = getStrokeColorByElement(element);
1183
- const fill = getFillByElement(element);
1214
+ const strokeColor = getStrokeColorByElement(this.board, element);
1215
+ const fill = getFillByElement(this.board, element);
1184
1216
  const strokeLineDash = getLineDashByElement(element);
1185
1217
  return drawGeometry(this.board, RectangleClient.inflate(rectangle, -strokeWidth), shape, {
1186
1218
  stroke: strokeColor,
@@ -1193,7 +1225,6 @@ class GeometryShapeGenerator extends Generator {
1193
1225
 
1194
1226
  const insertGeometry = (board, points, shape) => {
1195
1227
  let newElement = createGeometryElement(shape, points, '', {
1196
- strokeColor: DefaultBasicShapeProperty.strokeColor,
1197
1228
  strokeWidth: DefaultBasicShapeProperty.strokeWidth
1198
1229
  });
1199
1230
  Transforms.insertNode(board, newElement, [board.children.length]);
@@ -1216,6 +1247,10 @@ const resizeGeometry = (board, points, textHeight, path) => {
1216
1247
  }
1217
1248
  Transforms.setNode(board, newProperties, path);
1218
1249
  };
1250
+ const transformShape = (board, element, shape) => {
1251
+ const path = PlaitBoard.findPath(board, element);
1252
+ Transforms.setNode(board, { shape }, path);
1253
+ };
1219
1254
 
1220
1255
  const normalizePoints = (board, element, width, textHeight) => {
1221
1256
  let points = element.points;
@@ -1320,7 +1355,8 @@ const DrawTransforms = {
1320
1355
  setLineTexts,
1321
1356
  removeLineText,
1322
1357
  setLineMark,
1323
- insertImage
1358
+ insertImage,
1359
+ transformShape
1324
1360
  };
1325
1361
 
1326
1362
  const withGeometryCreateByDrag = (board) => {
@@ -1344,10 +1380,9 @@ const withGeometryCreateByDrag = (board) => {
1344
1380
  }
1345
1381
  else {
1346
1382
  const temporaryElement = createGeometryElement(pointer, points, '', {
1347
- strokeColor: DefaultBasicShapeProperty.strokeColor,
1348
1383
  strokeWidth: DefaultBasicShapeProperty.strokeWidth
1349
1384
  });
1350
- geometryGenerator.draw(temporaryElement, geometryShapeG);
1385
+ geometryGenerator.processDrawing(temporaryElement, geometryShapeG);
1351
1386
  PlaitBoard.getElementActiveHost(board).append(geometryShapeG);
1352
1387
  }
1353
1388
  }
@@ -1420,10 +1455,9 @@ const withGeometryCreateByDrawing = (board) => {
1420
1455
  if (drawMode && pointer !== BasicShapes.text) {
1421
1456
  const points = normalizeShapePoints([start, movingPoint], isShift);
1422
1457
  temporaryElement = createGeometryElement(pointer, points, '', {
1423
- strokeColor: DefaultBasicShapeProperty.strokeColor,
1424
1458
  strokeWidth: DefaultBasicShapeProperty.strokeWidth
1425
1459
  });
1426
- geometryGenerator.draw(temporaryElement, geometryShapeG);
1460
+ geometryGenerator.processDrawing(temporaryElement, geometryShapeG);
1427
1461
  PlaitBoard.getElementActiveHost(board).append(geometryShapeG);
1428
1462
  }
1429
1463
  pointerMove(event);
@@ -1438,7 +1472,6 @@ const withGeometryCreateByDrawing = (board) => {
1438
1472
  const points = getDefaultGeometryPoints(pointer, targetPoint);
1439
1473
  if (pointer !== BasicShapes.text) {
1440
1474
  temporaryElement = createGeometryElement(pointer, points, '', {
1441
- strokeColor: DefaultBasicShapeProperty.strokeColor,
1442
1475
  strokeWidth: DefaultBasicShapeProperty.strokeWidth
1443
1476
  });
1444
1477
  }
@@ -1493,6 +1526,7 @@ const DefaultLineStyle = {
1493
1526
  strokeWidth: 2,
1494
1527
  strokeColor: '#000'
1495
1528
  };
1529
+ const LINE_TEXT_SPACE = 4;
1496
1530
 
1497
1531
  const createGeometryElement = (shape, points, text, options) => {
1498
1532
  let textOptions = {};
@@ -1501,10 +1535,6 @@ const createGeometryElement = (shape, points, text, options) => {
1501
1535
  textOptions = { autoSize: true };
1502
1536
  alignment = undefined;
1503
1537
  }
1504
- let flowchartOptions = {};
1505
- if (getFlowchartPointers().includes(shape)) {
1506
- flowchartOptions = { fill: '#ffffff' };
1507
- }
1508
1538
  return {
1509
1539
  id: idCreator(),
1510
1540
  type: 'geometry',
@@ -1515,8 +1545,7 @@ const createGeometryElement = (shape, points, text, options) => {
1515
1545
  text: buildText(text, alignment),
1516
1546
  points,
1517
1547
  ...textOptions,
1518
- ...options,
1519
- ...flowchartOptions
1548
+ ...options
1520
1549
  };
1521
1550
  };
1522
1551
  const getPointsByCenterPoint = (point, width, height) => {
@@ -1648,26 +1677,49 @@ const createDefaultFlowchart = (point) => {
1648
1677
  ], { marker: LineMarkerType.none, connection: [0.5, 1], boundId: processElement2.id }, { marker: LineMarkerType.arrow, connection: [1, 0.5], boundId: endElement.id }, [], lineOptions);
1649
1678
  return [startElement, processElement1, decisionElement, processElement2, endElement, line1, line2, line3, line4, line5];
1650
1679
  };
1680
+ const getAutoCompletePoints = (element) => {
1681
+ const AutoCompleteMargin = (12 + RESIZE_HANDLE_DIAMETER / 2) * 2;
1682
+ let rectangle = getRectangleByPoints(element.points);
1683
+ rectangle = RectangleClient.inflate(rectangle, AutoCompleteMargin);
1684
+ return RectangleClient.getEdgeCenterPoints(rectangle);
1685
+ };
1686
+ const getHitIndexOfAutoCompletePoint = (movingPoint, points) => {
1687
+ return points.findIndex(point => {
1688
+ const movingRectangle = RectangleClient.toRectangleClient([movingPoint]);
1689
+ let rectangle = RectangleClient.toRectangleClient([point]);
1690
+ rectangle = RectangleClient.inflate(rectangle, RESIZE_HANDLE_DIAMETER);
1691
+ return RectangleClient.isHit(movingRectangle, rectangle);
1692
+ });
1693
+ };
1694
+ const getDrawDefaultStrokeColor = (theme) => {
1695
+ return DrawThemeColors[theme].strokeColor;
1696
+ };
1697
+ const getFlowchartDefaultFill = (theme) => {
1698
+ return DrawThemeColors[theme].fill;
1699
+ };
1651
1700
 
1701
+ const MAX_LENGTH = 100;
1652
1702
  const drawLineArrow = (element, points, options) => {
1653
1703
  const arrowG = createG();
1654
1704
  if (PlaitLine.isSourceMark(element, LineMarkerType.none) && PlaitLine.isTargetMark(element, LineMarkerType.none)) {
1655
1705
  return null;
1656
1706
  }
1707
+ const strokeWidth = getStrokeWidthByElement(element);
1708
+ const offset = (strokeWidth * strokeWidth) / 3;
1657
1709
  if (!PlaitLine.isSourceMark(element, LineMarkerType.none)) {
1658
- const source = getExtendPoint(points[0], points[1], 24);
1710
+ const source = getExtendPoint(points[0], points[1], 24 + offset);
1659
1711
  const sourceArrow = getArrow(element, { marker: element.source.marker, source, target: points[0], isSource: true }, options);
1660
1712
  sourceArrow && arrowG.appendChild(sourceArrow);
1661
1713
  }
1662
1714
  if (!PlaitLine.isTargetMark(element, LineMarkerType.none)) {
1663
- const source = getExtendPoint(points[points.length - 1], points[points.length - 2], 24);
1715
+ const source = getExtendPoint(points[points.length - 1], points[points.length - 2], 24 + offset);
1664
1716
  const arrow = getArrow(element, { marker: element.target.marker, source, target: points[points.length - 1], isSource: false }, options);
1665
1717
  arrow && arrowG.appendChild(arrow);
1666
1718
  }
1667
1719
  return arrowG;
1668
1720
  };
1669
1721
  const getArrow = (element, arrowOptions, options) => {
1670
- const { marker, source, target, isSource } = arrowOptions;
1722
+ const { marker, target, source, isSource } = arrowOptions;
1671
1723
  let targetArrow;
1672
1724
  switch (marker) {
1673
1725
  case LineMarkerType.openTriangle: {
@@ -1707,10 +1759,10 @@ const getArrow = (element, arrowOptions, options) => {
1707
1759
  };
1708
1760
  const drawSharpArrow = (source, target, options) => {
1709
1761
  const startPoint = target;
1710
- const { pointLeft, pointRight } = arrowPoints(source, target, 12, 20);
1762
+ const { pointLeft, pointRight } = arrowPoints(source, target, 20);
1711
1763
  const g = createG();
1712
1764
  const path = createPath();
1713
- let polylinePath = `M${pointRight[0]},${pointRight[1]}A8,8,20,0,1,${pointLeft[0]},${pointLeft[1]}L${startPoint[0]},${startPoint[1]}Z`;
1765
+ let polylinePath = `M${pointRight[0]},${pointRight[1]}A25,25,20,0,1,${pointLeft[0]},${pointLeft[1]}L${startPoint[0]},${startPoint[1]}Z`;
1714
1766
  path.setAttribute('d', polylinePath);
1715
1767
  path.setAttribute('stroke', `${options?.stroke}`);
1716
1768
  path.setAttribute('stroke-width', `${options?.strokeWidth}`);
@@ -1722,8 +1774,11 @@ const drawArrow = (element, source, target, options) => {
1722
1774
  const directionFactor = getFactorByPoints(source, target);
1723
1775
  const strokeWidth = getStrokeWidthByElement(element);
1724
1776
  const endPoint = [target[0] + (strokeWidth * directionFactor.x) / 2, target[1] + (strokeWidth * directionFactor.y) / 2];
1725
- const middlePoint = [endPoint[0] - 8 * directionFactor.x, endPoint[1] - 8 * directionFactor.y];
1726
- const { pointLeft, pointRight } = arrowPoints(source, endPoint, 12, 30);
1777
+ const middlePoint = [
1778
+ endPoint[0] - (8 + strokeWidth / 2) * directionFactor.x,
1779
+ endPoint[1] - (8 + strokeWidth / 2) * directionFactor.y
1780
+ ];
1781
+ const { pointLeft, pointRight } = arrowPoints(source, endPoint, 30);
1727
1782
  const arrowG = drawLinearPath([pointLeft, endPoint, pointRight, middlePoint], { ...options, fill: options.stroke }, true);
1728
1783
  const path = arrowG.querySelector('path');
1729
1784
  path.setAttribute('stroke-linejoin', 'round');
@@ -1731,33 +1786,68 @@ const drawArrow = (element, source, target, options) => {
1731
1786
  };
1732
1787
  const drawSolidTriangle = (source, target, options) => {
1733
1788
  const endPoint = target;
1734
- const { pointLeft, pointRight } = arrowPoints(source, endPoint, 12, 30);
1789
+ const { pointLeft, pointRight } = arrowPoints(source, endPoint, 30);
1735
1790
  return drawLinearPath([pointLeft, endPoint, pointRight], { ...options, fill: options.stroke }, true);
1736
1791
  };
1737
1792
  const drawOpenTriangle = (element, source, target, options) => {
1738
1793
  const directionFactor = getFactorByPoints(source, target);
1739
1794
  const strokeWidth = getStrokeWidthByElement(element);
1740
1795
  const endPoint = [target[0] + (strokeWidth * directionFactor.x) / 2, target[1] + (strokeWidth * directionFactor.y) / 2];
1741
- const { pointLeft, pointRight } = arrowPoints(source, endPoint, 12, 40);
1796
+ const { pointLeft, pointRight } = arrowPoints(source, endPoint, 40);
1742
1797
  return drawLinearPath([pointLeft, endPoint, pointRight], options);
1743
1798
  };
1744
1799
  const drawOneSideArrow = (source, target, side, options) => {
1745
- const { pointLeft, pointRight } = arrowPoints(source, target, 12, 40);
1800
+ const { pointLeft, pointRight } = arrowPoints(source, target, 40);
1746
1801
  return drawLinearPath([side === 'up' ? pointRight : pointLeft, target], options);
1747
1802
  };
1748
1803
  const drawSingleSlash = (source, target, isSource, options) => {
1749
- source = getExtendPoint(target, source, 12);
1750
- const middlePoint = getExtendPoint(target, source, 6);
1804
+ const length = distanceBetweenPointAndPoint(...source, ...target);
1805
+ const middlePoint = getExtendPoint(target, source, length / 2);
1751
1806
  const angle = isSource ? 120 : 60;
1752
1807
  const start = rotate(...source, ...middlePoint, (angle * Math.PI) / 180);
1753
1808
  const end = rotate(...target, ...middlePoint, (angle * Math.PI) / 180);
1754
1809
  return drawLinearPath([start, end], options);
1755
1810
  };
1756
1811
  const drawHollowTriangleArrow = (source, target, options) => {
1757
- const { pointLeft, pointRight } = arrowPoints(source, target, 12, 30);
1812
+ const { pointLeft, pointRight } = arrowPoints(source, target, 30);
1758
1813
  return drawLinearPath([pointLeft, pointRight, target], { ...options, fill: 'white' }, true);
1759
1814
  };
1760
1815
 
1816
+ class LineShapeGenerator extends Generator {
1817
+ canDraw(element, data) {
1818
+ return true;
1819
+ }
1820
+ draw(element, data) {
1821
+ let lineG;
1822
+ lineG = drawLine(this.board, element);
1823
+ return lineG;
1824
+ }
1825
+ }
1826
+
1827
+ const getHitGeometryResizeHandleRef = (board, element, point) => {
1828
+ const rectangle = getRectangleByPoints(element.points);
1829
+ const resizeHandleRefs = getRectangleResizeHandleRefs(rectangle, RESIZE_HANDLE_DIAMETER);
1830
+ const result = resizeHandleRefs.find(resizeHandleRef => {
1831
+ return RectangleClient.isHit(RectangleClient.toRectangleClient([point, point]), resizeHandleRef.rectangle);
1832
+ });
1833
+ return result;
1834
+ };
1835
+ const getHitOutlineGeometry = (board, point, offset = 0) => {
1836
+ let geometry = null;
1837
+ depthFirstRecursion(board, node => {
1838
+ if (PlaitDrawElement.isGeometry(node) || PlaitDrawElement.isImage(node)) {
1839
+ let client = getRectangleByPoints(node.points);
1840
+ client = RectangleClient.getOutlineRectangle(client, offset);
1841
+ const shape = getShape(node);
1842
+ const isHit = getEngine(shape).isHit(client, point);
1843
+ if (isHit) {
1844
+ geometry = node;
1845
+ }
1846
+ }
1847
+ }, getIsRecursionFunc(board), true);
1848
+ return geometry;
1849
+ };
1850
+
1761
1851
  const createLineElement = (shape, points, source, target, texts, options) => {
1762
1852
  return {
1763
1853
  id: idCreator(),
@@ -1792,7 +1882,7 @@ const getLineHandleRefPair = (board, element) => {
1792
1882
  let targetPoint = targetBoundElement
1793
1883
  ? getConnectionPoint(targetBoundElement, element.target.connection)
1794
1884
  : element.points[element.points.length - 1];
1795
- let sourceDirection = getDirectionByVector([targetPoint[0] - sourcePoint[0], targetPoint[1] - sourcePoint[1]]);
1885
+ let sourceDirection = getDirectionByVector([targetPoint[0] - sourcePoint[0], targetPoint[1] - sourcePoint[1]]) || Direction.right;
1796
1886
  let targetDirection = getOppositeDirection(sourceDirection);
1797
1887
  const sourceFactor = getDirectionFactor(sourceDirection);
1798
1888
  const targetFactor = getDirectionFactor(targetDirection);
@@ -1904,12 +1994,9 @@ const getCurvePoints = (board, element) => {
1904
1994
  return pointsOnBezierCurves(curvePoints);
1905
1995
  }
1906
1996
  else {
1907
- //TODO 直接获取贝塞尔曲线上高密度点
1908
- const points = PlaitLine.getPoints(board, element);
1909
- const draw = PlaitBoard.getRoughSVG(board).generator.curve(points);
1910
- let bezierPoints = transformOpsToPoints(draw.sets[0].ops);
1911
- bezierPoints = removeDuplicatePoints(bezierPoints);
1912
- return pointsOnBezierCurves(bezierPoints);
1997
+ const allPoints = PlaitLine.getPoints(board, element);
1998
+ const points = catmullRomFitting(allPoints);
1999
+ return pointsOnBezierCurves(points);
1913
2000
  }
1914
2001
  };
1915
2002
  const transformOpsToPoints = (ops) => {
@@ -1951,7 +2038,7 @@ const isHitLineText = (board, element, point) => {
1951
2038
  };
1952
2039
  const drawLine = (board, element) => {
1953
2040
  const strokeWidth = getStrokeWidthByElement(element);
1954
- const strokeColor = getStrokeColorByElement(element);
2041
+ const strokeColor = getStrokeColorByElement(board, element);
1955
2042
  const strokeLineDash = getLineDashByElement(element);
1956
2043
  const options = { stroke: strokeColor, strokeWidth, strokeLineDash };
1957
2044
  const lineG = createG();
@@ -1986,7 +2073,8 @@ function drawMask(board, element, id) {
1986
2073
  mask.appendChild(maskFillRect);
1987
2074
  const texts = element.texts;
1988
2075
  texts.forEach((text, index) => {
1989
- const textRectangle = getLineTextRectangle(board, element, index);
2076
+ let textRectangle = getLineTextRectangle(board, element, index);
2077
+ textRectangle = RectangleClient.inflate(textRectangle, LINE_TEXT_SPACE * 2);
1990
2078
  const rect = createRect(textRectangle, {
1991
2079
  fill: 'black'
1992
2080
  });
@@ -1995,6 +2083,7 @@ function drawMask(board, element, id) {
1995
2083
  //撑开 line
1996
2084
  const maskTargetFillRect = createRect(rectangle);
1997
2085
  maskTargetFillRect.setAttribute('opacity', '0');
2086
+ maskTargetFillRect.setAttribute('fill', 'none');
1998
2087
  return { mask, maskTargetFillRect };
1999
2088
  }
2000
2089
  const getConnectionPoint = (geometry, connection, direction, delta) => {
@@ -2042,12 +2131,6 @@ const getBoardLines = (board) => {
2042
2131
  recursion: (element) => PlaitDrawElement.isDrawElement(element)
2043
2132
  });
2044
2133
  };
2045
- const getExtendPoint = (source, target, extendDistance) => {
2046
- const distance = distanceBetweenPointAndPoint(...source, ...target);
2047
- const sin = (target[1] - source[1]) / distance;
2048
- const cos = (target[0] - source[0]) / distance;
2049
- return [source[0] + extendDistance * cos, source[1] + extendDistance * sin];
2050
- };
2051
2134
  // quadratic Bezier to cubic Bezier
2052
2135
  const Q2C = (points) => {
2053
2136
  const result = [];
@@ -2089,6 +2172,33 @@ const getVectorByConnection = (boundElement, connection) => {
2089
2172
  }
2090
2173
  return vector;
2091
2174
  };
2175
+ const alignPoints = (basePoint, movingPoint) => {
2176
+ const offset = 3;
2177
+ const newPoint = [...movingPoint];
2178
+ if (Point.isVerticalAlign(newPoint, basePoint, offset)) {
2179
+ newPoint[0] = basePoint[0];
2180
+ }
2181
+ if (Point.isHorizontalAlign(newPoint, basePoint, offset)) {
2182
+ newPoint[1] = basePoint[1];
2183
+ }
2184
+ return newPoint;
2185
+ };
2186
+ const handleLineCreating = (board, lineShape, startPoint, movingPoint, sourceElement, lineShapeG) => {
2187
+ const hitElement = getHitOutlineGeometry(board, movingPoint, REACTION_MARGIN);
2188
+ const targetConnection = hitElement ? transformPointToConnection(board, movingPoint, hitElement) : undefined;
2189
+ const connection = sourceElement ? transformPointToConnection(board, startPoint, sourceElement) : undefined;
2190
+ const targetBoundId = hitElement ? hitElement.id : undefined;
2191
+ const lineGenerator = new LineShapeGenerator(board);
2192
+ const temporaryLineElement = createLineElement(lineShape, [startPoint, movingPoint], { marker: LineMarkerType.none, connection: connection, boundId: sourceElement?.id }, { marker: LineMarkerType.arrow, connection: targetConnection, boundId: targetBoundId }, [], {
2193
+ strokeWidth: DefaultLineStyle.strokeWidth
2194
+ });
2195
+ const linePoints = getLinePoints(board, temporaryLineElement);
2196
+ const otherPoint = linePoints[0];
2197
+ temporaryLineElement.points[1] = alignPoints(otherPoint, movingPoint);
2198
+ lineGenerator.processDrawing(temporaryLineElement, lineShapeG);
2199
+ PlaitBoard.getElementActiveHost(board).append(lineShapeG);
2200
+ return temporaryLineElement;
2201
+ };
2092
2202
 
2093
2203
  const getSelectedDrawElements = (board) => {
2094
2204
  const selectedElements = getSelectedElements(board).filter(value => PlaitDrawElement.isDrawElement(value));
@@ -2107,11 +2217,18 @@ const getSelectedImageElements = (board) => {
2107
2217
  return selectedElements;
2108
2218
  };
2109
2219
 
2220
+ const isTextExceedingBounds = (geometry) => {
2221
+ const client = getRectangleByPoints(geometry.points);
2222
+ if (geometry.textHeight > client.height) {
2223
+ return true;
2224
+ }
2225
+ return false;
2226
+ };
2110
2227
  const isRectangleHitDrawElement = (board, element, selection) => {
2228
+ const rangeRectangle = RectangleClient.toRectangleClient([selection.anchor, selection.focus]);
2111
2229
  if (PlaitDrawElement.isGeometry(element)) {
2112
2230
  const client = getRectangleByPoints(element.points);
2113
- const rangeRectangle = RectangleClient.toRectangleClient([selection.anchor, selection.focus]);
2114
- if (element.textHeight > client.height) {
2231
+ if (isTextExceedingBounds(element)) {
2115
2232
  const textClient = getTextRectangle(element);
2116
2233
  return RectangleClient.isHit(rangeRectangle, client) || RectangleClient.isHit(rangeRectangle, textClient);
2117
2234
  }
@@ -2119,7 +2236,6 @@ const isRectangleHitDrawElement = (board, element, selection) => {
2119
2236
  }
2120
2237
  if (PlaitDrawElement.isImage(element)) {
2121
2238
  const client = getRectangleByPoints(element.points);
2122
- const rangeRectangle = RectangleClient.toRectangleClient([selection.anchor, selection.focus]);
2123
2239
  return RectangleClient.isHit(rangeRectangle, client);
2124
2240
  }
2125
2241
  if (PlaitDrawElement.isLine(element)) {
@@ -2127,7 +2243,6 @@ const isRectangleHitDrawElement = (board, element, selection) => {
2127
2243
  const strokeWidth = getStrokeWidthByElement(element);
2128
2244
  const isHitText = isHitLineText(board, element, selection.focus);
2129
2245
  const isHit = isHitPolyLine(points, selection.focus, strokeWidth, 3) || isHitText;
2130
- const rangeRectangle = RectangleClient.toRectangleClient([selection.anchor, selection.focus]);
2131
2246
  const isContainPolyLinePoint = points.some(point => {
2132
2247
  return RectangleClient.isHit(rangeRectangle, RectangleClient.toRectangleClient([point, point]));
2133
2248
  });
@@ -2138,16 +2253,25 @@ const isRectangleHitDrawElement = (board, element, selection) => {
2138
2253
  };
2139
2254
  const isHitDrawElement = (board, element, point) => {
2140
2255
  if (PlaitDrawElement.isGeometry(element)) {
2141
- const fill = getFillByElement(element);
2142
- if (fill !== DefaultGeometryStyle.fill && fill !== TRANSPARENT) {
2256
+ const fill = getFillByElement(board, element);
2257
+ // when shape equals text, fill is not allowed
2258
+ if (fill !== DefaultGeometryStyle.fill && fill !== TRANSPARENT && !PlaitDrawElement.isText(element)) {
2143
2259
  return isRectangleHitDrawElement(board, element, { anchor: point, focus: point });
2144
2260
  }
2145
2261
  else {
2262
+ // if shape equals text, only check text rectangle
2263
+ if (PlaitDrawElement.isText(element)) {
2264
+ const textClient = getTextRectangle(element);
2265
+ let isHitText = RectangleClient.isPointInRectangle(textClient, point);
2266
+ return isHitText;
2267
+ }
2146
2268
  const strokeWidth = getStrokeWidthByElement(element);
2147
2269
  const engine = getEngine(getShape(element));
2148
2270
  const corners = engine.getCornerPoints(getRectangleByPoints(element.points));
2149
2271
  const isHit = isHitPolyLine(corners, point, strokeWidth, 3);
2150
- return isHit;
2272
+ const textClient = getTextRectangle(element);
2273
+ let isHitText = RectangleClient.isPointInRectangle(textClient, point);
2274
+ return isHit || isHitText;
2151
2275
  }
2152
2276
  }
2153
2277
  if (PlaitDrawElement.isImage(element) || PlaitDrawElement.isLine(element)) {
@@ -2256,13 +2380,54 @@ const PlaitDrawElement = {
2256
2380
  return PlaitDrawElement.isImage(value) || PlaitDrawElement.isGeometry(value);
2257
2381
  },
2258
2382
  isBaseShape: (value) => {
2259
- return Object.keys(BasicShapes).includes(value.type);
2383
+ return Object.keys(BasicShapes).includes(value.shape);
2260
2384
  },
2261
2385
  isFlowchart: (value) => {
2262
- return Object.keys(FlowchartSymbols).includes(value.type);
2386
+ return Object.keys(FlowchartSymbols).includes(value.shape);
2263
2387
  }
2264
2388
  };
2265
2389
 
2390
+ class AutoCompleteGenerator extends Generator {
2391
+ constructor(board) {
2392
+ super(board);
2393
+ this.board = board;
2394
+ this.hoverElement = null;
2395
+ }
2396
+ canDraw(element, data) {
2397
+ const selectedElements = getSelectedElements(this.board);
2398
+ if (data.selected && selectedElements.length === 1 && !isSelectionMoving(this.board)) {
2399
+ return true;
2400
+ }
2401
+ else {
2402
+ return false;
2403
+ }
2404
+ }
2405
+ draw(element, data) {
2406
+ this.autoCompleteG = createG();
2407
+ const middlePoints = getAutoCompletePoints(element);
2408
+ middlePoints.forEach((point, index) => {
2409
+ const circle = drawCircle(PlaitBoard.getRoughSVG(this.board), point, RESIZE_HANDLE_DIAMETER, {
2410
+ stroke: 'none',
2411
+ fill: '#6698FF4d',
2412
+ fillStyle: 'solid'
2413
+ });
2414
+ circle.classList.add(`geometry-auto-complete-${index}`);
2415
+ this.autoCompleteG.appendChild(circle);
2416
+ });
2417
+ return this.autoCompleteG;
2418
+ }
2419
+ removeAutoCompleteG(index) {
2420
+ this.hoverElement = this.autoCompleteG.querySelector(`.geometry-auto-complete-${index}`);
2421
+ this.hoverElement.style.visibility = 'hidden';
2422
+ }
2423
+ recoverAutoCompleteG() {
2424
+ if (this.hoverElement) {
2425
+ this.hoverElement.style.visibility = 'visible';
2426
+ this.hoverElement = null;
2427
+ }
2428
+ }
2429
+ }
2430
+
2266
2431
  class GeometryComponent extends CommonPluginElement {
2267
2432
  get textManage() {
2268
2433
  return this.getTextManages()[0];
@@ -2304,33 +2469,38 @@ class GeometryComponent extends CommonPluginElement {
2304
2469
  return selectedElements.length === 1 && !isSelectionMoving(this.board);
2305
2470
  }
2306
2471
  });
2472
+ this.autoCompleteGenerator = new AutoCompleteGenerator(this.board);
2307
2473
  this.shapeGenerator = new GeometryShapeGenerator(this.board);
2308
2474
  this.initializeTextManage();
2309
2475
  }
2310
2476
  ngOnInit() {
2311
2477
  super.ngOnInit();
2312
2478
  this.initializeGenerator();
2313
- this.shapeGenerator.draw(this.element, this.g);
2314
- this.activeGenerator.draw(this.element, this.g, { selected: this.selected });
2479
+ this.shapeGenerator.processDrawing(this.element, this.g);
2480
+ this.activeGenerator.processDrawing(this.element, this.g, { selected: this.selected });
2481
+ this.autoCompleteGenerator.processDrawing(this.element, this.g, { selected: this.selected });
2315
2482
  this.drawText();
2316
2483
  }
2317
2484
  onContextChanged(value, previous) {
2318
- if (value.element !== previous.element) {
2319
- this.shapeGenerator.draw(this.element, this.g);
2320
- this.activeGenerator.draw(this.element, this.g, { selected: this.selected });
2485
+ const isChangeTheme = this.board.operations.find(op => op.type === 'set_theme');
2486
+ if (value.element !== previous.element || isChangeTheme) {
2487
+ this.shapeGenerator.processDrawing(this.element, this.g);
2488
+ this.activeGenerator.processDrawing(this.element, this.g, { selected: this.selected });
2489
+ this.autoCompleteGenerator.processDrawing(this.element, this.g, { selected: this.selected });
2321
2490
  this.updateText();
2322
2491
  }
2323
2492
  else {
2324
2493
  const hasSameSelected = value.selected === previous.selected;
2325
2494
  const hasSameHandleState = this.activeGenerator.options.hasResizeHandle() === this.activeGenerator.hasResizeHandle;
2326
2495
  if (!hasSameSelected || !hasSameHandleState) {
2327
- this.activeGenerator.draw(this.element, this.g, { selected: this.selected });
2496
+ this.activeGenerator.processDrawing(this.element, this.g, { selected: this.selected });
2497
+ this.autoCompleteGenerator.processDrawing(this.element, this.g, { selected: this.selected });
2328
2498
  }
2329
2499
  }
2330
2500
  }
2331
2501
  editText() {
2332
2502
  this.textManage.edit();
2333
- this.activeGenerator.draw(this.element, this.g, { selected: this.selected });
2503
+ this.activeGenerator.processDrawing(this.element, this.g, { selected: this.selected });
2334
2504
  }
2335
2505
  drawText() {
2336
2506
  this.textManage.draw(this.element.text);
@@ -2391,17 +2561,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
2391
2561
  }]
2392
2562
  }], ctorParameters: function () { return [{ type: i0.ViewContainerRef }, { type: i0.ChangeDetectorRef }]; } });
2393
2563
 
2394
- class LineShapeGenerator extends Generator {
2395
- canDraw(element, data) {
2396
- return true;
2397
- }
2398
- baseDraw(element, data) {
2399
- let lineG;
2400
- lineG = drawLine(this.board, element);
2401
- return lineG;
2402
- }
2403
- }
2404
-
2405
2564
  class LineActiveGenerator extends Generator {
2406
2565
  constructor() {
2407
2566
  super(...arguments);
@@ -2415,7 +2574,7 @@ class LineActiveGenerator extends Generator {
2415
2574
  return false;
2416
2575
  }
2417
2576
  }
2418
- baseDraw(element, data) {
2577
+ draw(element, data) {
2419
2578
  const activeG = createG();
2420
2579
  if (this.hasResizeHandle) {
2421
2580
  activeG.classList.add('active');
@@ -2498,8 +2657,8 @@ class LineComponent extends PlaitPluginElementComponent {
2498
2657
  }
2499
2658
  ngOnInit() {
2500
2659
  this.initializeGenerator();
2501
- this.shapeGenerator.draw(this.element, this.g);
2502
- this.activeGenerator.draw(this.element, PlaitBoard.getElementActiveHost(this.board), { selected: this.selected });
2660
+ this.shapeGenerator.processDrawing(this.element, this.g);
2661
+ this.activeGenerator.processDrawing(this.element, PlaitBoard.getElementActiveHost(this.board), { selected: this.selected });
2503
2662
  super.ngOnInit();
2504
2663
  this.boundedElements = this.getBoundedElements();
2505
2664
  this.drawText();
@@ -2524,21 +2683,22 @@ class LineComponent extends PlaitPluginElementComponent {
2524
2683
  const boundedElements = this.getBoundedElements();
2525
2684
  const isBoundedElementsChanged = boundedElements.source !== this.boundedElements.source || boundedElements.target !== this.boundedElements.target;
2526
2685
  this.boundedElements = boundedElements;
2527
- if (value.element !== previous.element) {
2528
- this.shapeGenerator.draw(this.element, this.g);
2529
- this.activeGenerator.draw(this.element, PlaitBoard.getElementActiveHost(this.board), { selected: this.selected });
2686
+ const isChangeTheme = this.board.operations.find(op => op.type === 'set_theme');
2687
+ if (value.element !== previous.element || isChangeTheme) {
2688
+ this.shapeGenerator.processDrawing(this.element, this.g);
2689
+ this.activeGenerator.processDrawing(this.element, PlaitBoard.getElementActiveHost(this.board), { selected: this.selected });
2530
2690
  this.updateText(previous.element.texts, value.element.texts);
2531
2691
  this.updateTextRectangle();
2532
2692
  }
2533
2693
  if (isBoundedElementsChanged) {
2534
- this.shapeGenerator.draw(this.element, this.g);
2535
- this.activeGenerator.draw(this.element, PlaitBoard.getElementActiveHost(this.board), { selected: this.selected });
2694
+ this.shapeGenerator.processDrawing(this.element, this.g);
2695
+ this.activeGenerator.processDrawing(this.element, PlaitBoard.getElementActiveHost(this.board), { selected: this.selected });
2536
2696
  this.updateTextRectangle();
2537
2697
  return;
2538
2698
  }
2539
2699
  if (!isSelectionMoving(this.board)) {
2540
2700
  this.activeGenerator.hasResizeHandle = this.hasResizeHandle();
2541
- this.activeGenerator.draw(this.element, PlaitBoard.getElementActiveHost(this.board), { selected: this.selected });
2701
+ this.activeGenerator.processDrawing(this.element, PlaitBoard.getElementActiveHost(this.board), { selected: this.selected });
2542
2702
  }
2543
2703
  }
2544
2704
  hasResizeHandle() {
@@ -2787,35 +2947,10 @@ const getBoundedLineElements = (board, plaitShapes) => {
2787
2947
  return lines.filter(line => plaitShapes.find(shape => PlaitLine.isBoundElementOfSource(line, shape) || PlaitLine.isBoundElementOfTarget(line, shape)));
2788
2948
  };
2789
2949
 
2790
- const getHitGeometryResizeHandleRef = (board, element, point) => {
2791
- const rectangle = getRectangleByPoints(element.points);
2792
- const resizeHandleRefs = getRectangleResizeHandleRefs(rectangle, RESIZE_HANDLE_DIAMETER);
2793
- const result = resizeHandleRefs.find(resizeHandleRef => {
2794
- return RectangleClient.isHit(RectangleClient.toRectangleClient([point, point]), resizeHandleRef.rectangle);
2795
- });
2796
- return result;
2797
- };
2798
- const getHitOutlineGeometry = (board, point, offset = 0) => {
2799
- let geometry = null;
2800
- depthFirstRecursion(board, node => {
2801
- if (PlaitDrawElement.isGeometry(node) || PlaitDrawElement.isImage(node)) {
2802
- let client = getRectangleByPoints(node.points);
2803
- client = RectangleClient.getOutlineRectangle(client, offset);
2804
- const shape = getShape(node);
2805
- const isHit = getEngine(shape).isHit(client, point);
2806
- if (isHit) {
2807
- geometry = node;
2808
- }
2809
- }
2810
- }, getIsRecursionFunc(board), true);
2811
- return geometry;
2812
- };
2813
-
2814
2950
  const withLineCreateByDraw = (board) => {
2815
2951
  const { pointerDown, pointerMove, pointerUp } = board;
2816
2952
  let start = null;
2817
- let sourceRef = {};
2818
- let targetRef = {};
2953
+ let sourceElement;
2819
2954
  let lineShapeG = null;
2820
2955
  let temporaryElement = null;
2821
2956
  board.pointerDown = (event) => {
@@ -2824,10 +2959,9 @@ const withLineCreateByDraw = (board) => {
2824
2959
  if (isLinePointer && isDrawingMode(board)) {
2825
2960
  const point = transformPoint(board, toPoint(event.x, event.y, PlaitBoard.getHost(board)));
2826
2961
  start = point;
2827
- const hitElement = getHitOutlineGeometry(board, point, -4);
2962
+ const hitElement = getHitOutlineGeometry(board, point, REACTION_MARGIN);
2828
2963
  if (hitElement) {
2829
- sourceRef.connection = transformPointToConnection(board, point, hitElement);
2830
- sourceRef.boundId = hitElement.id;
2964
+ sourceElement = hitElement;
2831
2965
  }
2832
2966
  preventTouchMove(board, event, true);
2833
2967
  }
@@ -2836,19 +2970,10 @@ const withLineCreateByDraw = (board) => {
2836
2970
  board.pointerMove = (event) => {
2837
2971
  lineShapeG?.remove();
2838
2972
  lineShapeG = createG();
2839
- const movingPoint = transformPoint(board, toPoint(event.x, event.y, PlaitBoard.getHost(board)));
2973
+ let movingPoint = transformPoint(board, toPoint(event.x, event.y, PlaitBoard.getHost(board)));
2840
2974
  if (start) {
2841
- const hitElement = getHitOutlineGeometry(board, movingPoint, -4);
2842
- targetRef.connection = hitElement ? transformPointToConnection(board, movingPoint, hitElement) : undefined;
2843
- targetRef.boundId = hitElement ? hitElement.id : undefined;
2844
- const lineGenerator = new LineShapeGenerator(board);
2845
2975
  const lineShape = PlaitBoard.getPointer(board);
2846
- temporaryElement = createLineElement(lineShape, [start, movingPoint], { marker: LineMarkerType.none, connection: sourceRef.connection, boundId: sourceRef?.boundId }, { marker: LineMarkerType.arrow, connection: targetRef.connection, boundId: targetRef?.boundId }, [], {
2847
- strokeColor: DefaultLineStyle.strokeColor,
2848
- strokeWidth: DefaultLineStyle.strokeWidth
2849
- });
2850
- lineGenerator.draw(temporaryElement, lineShapeG);
2851
- PlaitBoard.getElementActiveHost(board).append(lineShapeG);
2976
+ temporaryElement = handleLineCreating(board, lineShape, start, movingPoint, sourceElement, lineShapeG);
2852
2977
  }
2853
2978
  pointerMove(event);
2854
2979
  };
@@ -2861,9 +2986,8 @@ const withLineCreateByDraw = (board) => {
2861
2986
  }
2862
2987
  lineShapeG?.remove();
2863
2988
  lineShapeG = null;
2989
+ sourceElement = null;
2864
2990
  start = null;
2865
- sourceRef = {};
2866
- targetRef = {};
2867
2991
  temporaryElement = null;
2868
2992
  preventTouchMove(board, event, false);
2869
2993
  pointerUp(event);
@@ -3049,7 +3173,7 @@ const withLineResize = (board) => {
3049
3173
  if (resizeRef.handle === LineResizeHandle.source || resizeRef.handle === LineResizeHandle.target) {
3050
3174
  const object = resizeRef.handle === LineResizeHandle.source ? source : target;
3051
3175
  points[pointIndex] = resizeState.endTransformPoint;
3052
- const hitElement = getHitOutlineGeometry(board, resizeState.endTransformPoint, -4);
3176
+ const hitElement = getHitOutlineGeometry(board, resizeState.endTransformPoint, REACTION_MARGIN);
3053
3177
  if (hitElement) {
3054
3178
  object.connection = transformPointToConnection(board, resizeState.endTransformPoint, hitElement);
3055
3179
  object.boundId = hitElement.id;
@@ -3059,7 +3183,9 @@ const withLineResize = (board) => {
3059
3183
  object.boundId = undefined;
3060
3184
  if (points.length === 2) {
3061
3185
  let movingPoint = points[pointIndex];
3062
- const otherPoint = points[Number(!pointIndex)];
3186
+ const drawPoints = getLinePoints(board, resizeRef.element);
3187
+ const index = pointIndex === 0 ? drawPoints.length - 1 : pointIndex;
3188
+ const otherPoint = drawPoints[index];
3063
3189
  points[pointIndex] = alignPoints(otherPoint, movingPoint);
3064
3190
  }
3065
3191
  }
@@ -3076,17 +3202,6 @@ const withLineResize = (board) => {
3076
3202
  withResize(board, options);
3077
3203
  return board;
3078
3204
  };
3079
- const alignPoints = (basePoint, movingPoint) => {
3080
- const offset = 3;
3081
- const newPoint = [...movingPoint];
3082
- if (Point.isVerticalAlign(newPoint, basePoint, offset)) {
3083
- newPoint[0] = basePoint[0];
3084
- }
3085
- if (Point.isHorizontalAlign(newPoint, basePoint, offset)) {
3086
- newPoint[1] = basePoint[1];
3087
- }
3088
- return newPoint;
3089
- };
3090
3205
 
3091
3206
  const withLineBoundReaction = (board) => {
3092
3207
  const { pointerMove, pointerUp } = board;
@@ -3210,7 +3325,7 @@ class ImageComponent extends CommonPluginElement {
3210
3325
  ngOnInit() {
3211
3326
  super.ngOnInit();
3212
3327
  this.initializeGenerator();
3213
- this.imageGenerator.draw(this.element, this.g, this.viewContainerRef);
3328
+ this.imageGenerator.processDrawing(this.element, this.g, this.viewContainerRef);
3214
3329
  }
3215
3330
  onContextChanged(value, previous) {
3216
3331
  if (value.element !== previous.element) {
@@ -3244,6 +3359,96 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
3244
3359
  }]
3245
3360
  }], ctorParameters: function () { return [{ type: i0.ViewContainerRef }, { type: i0.ChangeDetectorRef }]; } });
3246
3361
 
3362
+ const withAutoCompleteReaction = (board) => {
3363
+ const { pointerMove } = board;
3364
+ let reactionG = null;
3365
+ board.pointerMove = (event) => {
3366
+ reactionG?.remove();
3367
+ if (isSelectionMoving(board)) {
3368
+ pointerMove(event);
3369
+ return;
3370
+ }
3371
+ const selectedElements = getSelectedDrawElements(board);
3372
+ const movingPoint = transformPoint(board, toPoint(event.x, event.y, PlaitBoard.getHost(board)));
3373
+ if (selectedElements.length === 1 && PlaitDrawElement.isGeometry(selectedElements[0])) {
3374
+ const points = getAutoCompletePoints(selectedElements[0]);
3375
+ const hitIndex = getHitIndexOfAutoCompletePoint(movingPoint, points);
3376
+ const hitPoint = points[hitIndex];
3377
+ const component = PlaitElement.getComponent(selectedElements[0]);
3378
+ component.autoCompleteGenerator.recoverAutoCompleteG();
3379
+ if (hitPoint) {
3380
+ component.autoCompleteGenerator.removeAutoCompleteG(hitIndex);
3381
+ reactionG = drawCircle(PlaitBoard.getRoughSVG(board), hitPoint, 10, {
3382
+ stroke: 'none',
3383
+ fill: '#6698FF80',
3384
+ fillStyle: 'solid'
3385
+ });
3386
+ PlaitBoard.getElementActiveHost(board).append(reactionG);
3387
+ }
3388
+ }
3389
+ pointerMove(event);
3390
+ };
3391
+ return board;
3392
+ };
3393
+
3394
+ const withAutoCompletePluginKey = 'plait-auto-complete-plugin-key';
3395
+ const withAutoComplete = (board) => {
3396
+ const { pointerDown, pointerMove, pointerUp } = board;
3397
+ const tolerance = 3;
3398
+ let startPoint = null;
3399
+ let lineShapeG = null;
3400
+ let sourceElement;
3401
+ let temporaryElement;
3402
+ board.pointerDown = (event) => {
3403
+ const selectedElements = getSelectedDrawElements(board);
3404
+ const clickPoint = transformPoint(board, toPoint(event.x, event.y, PlaitBoard.getHost(board)));
3405
+ if (selectedElements.length === 1 && PlaitDrawElement.isGeometry(selectedElements[0])) {
3406
+ const points = getAutoCompletePoints(selectedElements[0]);
3407
+ const index = getHitIndexOfAutoCompletePoint(clickPoint, points);
3408
+ const hitPoint = points[index];
3409
+ if (hitPoint) {
3410
+ temporaryDisableSelection(board);
3411
+ startPoint = clickPoint;
3412
+ sourceElement = selectedElements[0];
3413
+ BoardTransforms.updatePointerType(board, LineShape.elbow);
3414
+ }
3415
+ }
3416
+ pointerDown(event);
3417
+ };
3418
+ board.pointerMove = (event) => {
3419
+ lineShapeG?.remove();
3420
+ lineShapeG = createG();
3421
+ let movingPoint = transformPoint(board, toPoint(event.x, event.y, PlaitBoard.getHost(board)));
3422
+ if (startPoint && sourceElement) {
3423
+ const distance = distanceBetweenPointAndPoint(...movingPoint, ...startPoint);
3424
+ if (distance > tolerance) {
3425
+ temporaryElement = handleLineCreating(board, LineShape.elbow, startPoint, movingPoint, sourceElement, lineShapeG);
3426
+ }
3427
+ }
3428
+ pointerMove(event);
3429
+ };
3430
+ board.pointerUp = event => {
3431
+ if (temporaryElement) {
3432
+ Transforms.insertNode(board, temporaryElement, [board.children.length]);
3433
+ clearSelectedElement(board);
3434
+ addSelectedElement(board, temporaryElement);
3435
+ const afterComplete = board.getPluginOptions(withAutoCompletePluginKey)
3436
+ ?.afterComplete;
3437
+ afterComplete && afterComplete(temporaryElement);
3438
+ }
3439
+ if (startPoint) {
3440
+ BoardTransforms.updatePointerType(board, PlaitPointerType.selection);
3441
+ startPoint = null;
3442
+ }
3443
+ lineShapeG?.remove();
3444
+ lineShapeG = null;
3445
+ sourceElement = null;
3446
+ temporaryElement = null;
3447
+ pointerUp(event);
3448
+ };
3449
+ return board;
3450
+ };
3451
+
3247
3452
  const withDraw = (board) => {
3248
3453
  const { drawElement, getRectangle, isRectangleHit, isHit, isMovable, isAlign } = board;
3249
3454
  board.drawElement = (context) => {
@@ -3297,11 +3502,13 @@ const withDraw = (board) => {
3297
3502
  const isSelected = (boundId) => {
3298
3503
  return !!selectedElements.find(value => value.id === boundId);
3299
3504
  };
3300
- if ((element.source.boundId && !isSelected(element.source.boundId)) ||
3301
- (element.target.boundId && !isSelected(element.target.boundId))) {
3302
- return false;
3505
+ if ((!element.source.boundId ||
3506
+ (element.source.boundId && isSelected(element.source.boundId) && selectedElements.includes(element))) &&
3507
+ (!element.target.boundId ||
3508
+ (element.source.boundId && isSelected(element.source.boundId) && selectedElements.includes(element)))) {
3509
+ return true;
3303
3510
  }
3304
- return true;
3511
+ return false;
3305
3512
  }
3306
3513
  return isMovable(element);
3307
3514
  };
@@ -3311,12 +3518,12 @@ const withDraw = (board) => {
3311
3518
  }
3312
3519
  return isAlign(element);
3313
3520
  };
3314
- return withLineText(withLineBoundReaction(withLineResize(withGeometryResize(withLineCreateByDraw(withGeometryCreateByDrag(withGeometryCreateByDrawing(withDrawFragment(withDrawHotkey(board)))))))));
3521
+ return withAutoCompleteReaction(withLineText(withLineBoundReaction(withLineResize(withGeometryResize(withLineCreateByDraw(withAutoComplete(withGeometryCreateByDrag(withGeometryCreateByDrawing(withDrawFragment(withDrawHotkey(board)))))))))));
3315
3522
  };
3316
3523
 
3317
3524
  /**
3318
3525
  * Generated bundle index. Do not edit.
3319
3526
  */
3320
3527
 
3321
- export { BasicShapes, DEFAULT_IMAGE_WIDTH, DefaultBasicShapeProperty, DefaultConnectorProperty, DefaultDataProperty, DefaultDecisionProperty, DefaultFlowchartProperty, DefaultFlowchartPropertyMap, DefaultGeometryActiveStyle, DefaultGeometryStyle, DefaultManualInputProperty, DefaultMergeProperty, DefaultTextProperty, DrawTransforms, FlowchartSymbols, GeometryComponent, GeometryThreshold, LineComponent, LineHandleKey, LineMarkerType, LineShape, PlaitDrawElement, PlaitGeometry, PlaitLine, Q2C, ShapeDefaultSpace, StrokeStyle, createDefaultFlowchart, createGeometryElement, createLineElement, drawBoundMask, drawGeometry, drawLine, getBasicPointers, getBoardLines, getCenterPointsOnPolygon, getConnectionPoint, getCurvePoints, getDefaultFlowchartProperty, getEdgeOnPolygonByPoint, getElbowPoints, getExtendPoint, getFillByElement, getFlowchartPointers, getGeometryPointers, getHitConnectorPoint, getHitLineTextIndex, getLineDashByElement, getLineHandleRefPair, getLinePointers, getLinePoints, getLineTextRectangle, getNearestPoint, getPointsByCenterPoint, getSelectedDrawElements, getSelectedGeometryElements, getSelectedImageElements, getSelectedLineElements, getStrokeColorByElement, getStrokeStyleByElement, getStrokeWidthByElement, getTextRectangle, getVectorByConnection, isHitDrawElement, isHitLineText, isHitPolyLine, isRectangleHitDrawElement, transformOpsToPoints, transformPointToConnection, withDraw };
3528
+ 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, PlaitDrawElement, PlaitGeometry, PlaitLine, Q2C, REACTION_MARGIN, ShapeDefaultSpace, StrokeStyle, alignPoints, createDefaultFlowchart, createGeometryElement, createLineElement, drawBoundMask, drawGeometry, drawLine, getAutoCompletePoints, getBasicPointers, getBoardLines, getCenterPointsOnPolygon, getConnectionPoint, getCurvePoints, getDefaultFlowchartProperty, getDrawDefaultStrokeColor, getEdgeOnPolygonByPoint, getElbowPoints, getFillByElement, getFlowchartDefaultFill, getFlowchartPointers, getGeometryPointers, getHitConnectorPoint, getHitIndexOfAutoCompletePoint, getHitLineTextIndex, getLineDashByElement, getLineHandleRefPair, getLinePointers, getLinePoints, getLineTextRectangle, getNearestPoint, getPointsByCenterPoint, getSelectedDrawElements, getSelectedGeometryElements, getSelectedImageElements, getSelectedLineElements, getStrokeColorByElement, getStrokeStyleByElement, getStrokeWidthByElement, getTextRectangle, getVectorByConnection, handleLineCreating, isHitDrawElement, isHitLineText, isHitPolyLine, isRectangleHitDrawElement, isTextExceedingBounds, transformOpsToPoints, transformPointToConnection, withDraw };
3322
3529
  //# sourceMappingURL=plait-draw.mjs.map