@plait/draw 0.1.0-next.9 → 0.28.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 (106) hide show
  1. package/constants/image.d.ts +1 -0
  2. package/constants/index.d.ts +1 -0
  3. package/constants/pointer.d.ts +4 -10
  4. package/engines/comment.d.ts +4 -0
  5. package/engines/cross.d.ts +4 -0
  6. package/{utils/engine → engines}/diamond.d.ts +1 -1
  7. package/{utils/engine → engines}/ellipse.d.ts +1 -1
  8. package/engines/hexagon.d.ts +4 -0
  9. package/{utils/engine → engines}/index.d.ts +1 -1
  10. package/engines/left-arrow.d.ts +4 -0
  11. package/engines/octagon.d.ts +4 -0
  12. package/engines/parallelogram.d.ts +4 -0
  13. package/engines/pentagon-arrow.d.ts +4 -0
  14. package/engines/pentagon.d.ts +4 -0
  15. package/engines/process-arrow.d.ts +4 -0
  16. package/{utils/engine → engines}/rectangle.d.ts +1 -1
  17. package/engines/right-arrow.d.ts +4 -0
  18. package/engines/round-comment.d.ts +4 -0
  19. package/{utils/engine → engines}/round-rectangle.d.ts +1 -1
  20. package/engines/star.d.ts +4 -0
  21. package/engines/trapezoid.d.ts +4 -0
  22. package/engines/triangle.d.ts +4 -0
  23. package/engines/two-way-arrow.d.ts +4 -0
  24. package/esm2022/constants/geometry.mjs +3 -3
  25. package/esm2022/constants/image.mjs +2 -0
  26. package/esm2022/constants/index.mjs +2 -1
  27. package/esm2022/constants/pointer.mjs +8 -19
  28. package/esm2022/engines/comment.mjs +57 -0
  29. package/esm2022/engines/cross.mjs +46 -0
  30. package/esm2022/engines/diamond.mjs +30 -0
  31. package/esm2022/engines/ellipse.mjs +92 -0
  32. package/esm2022/engines/hexagon.mjs +40 -0
  33. package/esm2022/engines/index.mjs +46 -0
  34. package/esm2022/engines/left-arrow.mjs +45 -0
  35. package/esm2022/engines/octagon.mjs +42 -0
  36. package/esm2022/engines/parallelogram.mjs +39 -0
  37. package/esm2022/engines/pentagon-arrow.mjs +39 -0
  38. package/esm2022/engines/pentagon.mjs +39 -0
  39. package/esm2022/engines/process-arrow.mjs +41 -0
  40. package/esm2022/engines/rectangle.mjs +26 -0
  41. package/esm2022/engines/right-arrow.mjs +45 -0
  42. package/esm2022/engines/round-comment.mjs +81 -0
  43. package/esm2022/engines/round-rectangle.mjs +59 -0
  44. package/esm2022/engines/star.mjs +45 -0
  45. package/esm2022/engines/trapezoid.mjs +40 -0
  46. package/esm2022/engines/triangle.mjs +40 -0
  47. package/esm2022/engines/two-way-arrow.mjs +48 -0
  48. package/esm2022/generators/geometry-shape.generator.mjs +7 -2
  49. package/esm2022/generators/line-active.generator.mjs +71 -22
  50. package/esm2022/generators/line.generator.mjs +2 -11
  51. package/esm2022/geometry.component.mjs +13 -7
  52. package/esm2022/image.component.mjs +70 -0
  53. package/esm2022/interfaces/geometry.mjs +15 -1
  54. package/esm2022/interfaces/image.mjs +2 -0
  55. package/esm2022/interfaces/index.mjs +8 -2
  56. package/esm2022/interfaces/line.mjs +27 -4
  57. package/esm2022/line.component.mjs +15 -6
  58. package/esm2022/plugins/with-draw-fragment.mjs +37 -7
  59. package/esm2022/plugins/with-draw.mjs +38 -10
  60. package/esm2022/plugins/with-geometry-create.mjs +15 -12
  61. package/esm2022/plugins/with-geometry-resize.mjs +28 -20
  62. package/esm2022/plugins/with-line-bound-reaction.mjs +10 -5
  63. package/esm2022/plugins/with-line-create.mjs +7 -5
  64. package/esm2022/plugins/with-line-resize.mjs +12 -4
  65. package/esm2022/transforms/geometry-text.mjs +1 -1
  66. package/esm2022/transforms/image.mjs +23 -0
  67. package/esm2022/transforms/index.mjs +4 -2
  68. package/esm2022/utils/clipboard.mjs +4 -3
  69. package/esm2022/utils/geometry.mjs +20 -6
  70. package/esm2022/utils/index.mjs +1 -1
  71. package/esm2022/utils/line-arrow.mjs +43 -18
  72. package/esm2022/utils/line.mjs +202 -51
  73. package/esm2022/utils/position/geometry.mjs +5 -4
  74. package/esm2022/utils/position/line.mjs +32 -22
  75. package/esm2022/utils/selected.mjs +5 -1
  76. package/esm2022/utils/shape.mjs +8 -0
  77. package/fesm2022/plait-draw.mjs +1439 -280
  78. package/fesm2022/plait-draw.mjs.map +1 -1
  79. package/generators/line-active.generator.d.ts +3 -0
  80. package/generators/line.generator.d.ts +1 -1
  81. package/geometry.component.d.ts +1 -1
  82. package/image.component.d.ts +20 -0
  83. package/interfaces/geometry.d.ts +20 -2
  84. package/interfaces/image.d.ts +7 -0
  85. package/interfaces/index.d.ts +5 -1
  86. package/interfaces/line.d.ts +19 -5
  87. package/line.component.d.ts +2 -1
  88. package/package.json +3 -2
  89. package/plugins/with-draw-fragment.d.ts +2 -0
  90. package/plugins/with-geometry-create.d.ts +1 -1
  91. package/styles/styles.scss +2 -2
  92. package/transforms/image.d.ts +3 -0
  93. package/transforms/index.d.ts +1 -0
  94. package/utils/geometry.d.ts +1 -0
  95. package/utils/line.d.ts +14 -6
  96. package/utils/position/geometry.d.ts +2 -1
  97. package/utils/position/line.d.ts +7 -3
  98. package/utils/selected.d.ts +2 -0
  99. package/utils/shape.d.ts +2 -0
  100. package/esm2022/utils/engine/diamond.mjs +0 -22
  101. package/esm2022/utils/engine/ellipse.mjs +0 -55
  102. package/esm2022/utils/engine/index.mjs +0 -18
  103. package/esm2022/utils/engine/parallelogram.mjs +0 -32
  104. package/esm2022/utils/engine/rectangle.mjs +0 -18
  105. package/esm2022/utils/engine/round-rectangle.mjs +0 -49
  106. package/utils/engine/parallelogram.d.ts +0 -4
@@ -1,9 +1,10 @@
1
- import { PlaitElement, ACTIVE_STROKE_WIDTH, RectangleClient, PlaitBoard, setStrokeLinecap, isPointInPolygon, getNearestPointBetweenPointAndSegments, isPointInEllipse, drawRectangle, drawRoundRectangle, isPointInRoundRectangle, idCreator, createG, SELECTION_BORDER_COLOR, SELECTION_FILL_COLOR, drawCircle, arrowPoints, createPath, drawLinearPath, distanceBetweenPointAndSegments, createMask, createRect, getElementById, findElements, getSelectedElements, Transforms, clearSelectedElement, addSelectedElement, PlaitNode, Point, isSelectionMoving, PlaitPluginElementComponent, transformPoint, toPoint, BoardTransforms, PlaitPointerType, preventTouchMove, createForeignObject, setClipboardData, getDataFromClipboard, depthFirstRecursion, getIsRecursionFunc, getHitElements, isPolylineHitRectangle } from '@plait/core';
1
+ import { PlaitElement, ACTIVE_STROKE_WIDTH, PlaitBoard, setStrokeLinecap, isPointInPolygon, getNearestPointBetweenPointAndSegments, RectangleClient, isPointInEllipse, drawRectangle, drawRoundRectangle, isPointInRoundRectangle, idCreator, createG, SELECTION_BORDER_COLOR, SELECTION_FILL_COLOR, drawCircle, distanceBetweenPointAndSegment, arrowPoints, createPath, drawLinearPath, rotate, getElementById, distanceBetweenPointAndPoint, distanceBetweenPointAndSegments, createMask, createRect, findElements, getSelectedElements, Transforms, clearSelectedElement, addSelectedElement, PlaitNode, Point, BOARD_TO_HOST, transformPoint, toPoint, isSelectionMoving, PlaitPluginElementComponent, BoardTransforms, PlaitPointerType, preventTouchMove, createForeignObject, setClipboardData, getDataFromClipboard, depthFirstRecursion, getIsRecursionFunc, getHitElements, isPolylineHitRectangle } from '@plait/core';
2
+ import { getRectangleByPoints, getFactorByPoints, getDirectionByVector, getOppositeDirection, getDirectionFactor, getPoints, getPointByVector, getPointOnPolyline, getDirectionByPointOfRectangle, rotateVectorAnti90, Generator, normalizeShapePoints, CommonPluginElement, ActiveGenerator, WithTextPluginKey, RESIZE_HANDLE_DIAMETER, PRIMARY_COLOR, isVirtualKey, isDelete, isSpaceHotkey, isDndMode, isDrawingMode, acceptImageTypes, buildImage, getRectangleResizeHandleRefs, ResizeHandle, withResize, isResizingByCondition, getRatioByPoint, ImageGenerator } from '@plait/common';
3
+ import { Alignment, buildText, AlignEditor, TextManage, DEFAULT_FONT_SIZE, getTextFromClipboard, getTextSize } from '@plait/text';
4
+ import { pointsOnBezierCurves } from 'points-on-curve';
2
5
  import * as i0 from '@angular/core';
3
6
  import { Component, ChangeDetectionStrategy } from '@angular/core';
4
7
  import { Subject } from 'rxjs';
5
- import { getRectangleByPoints, getFactorByPoints, Direction, getDirectionByPoint, getPoints, getPointOnPolyline, getDirectionFactor, Generator, normalizeShapePoints, CommonPluginElement, ActiveGenerator, WithTextPluginKey, RESIZE_HANDLE_DIAMETER, isVirtualKey, isDelete, isSpaceHotkey, isDndMode, isDrawingMode, getRectangleResizeHandleRefs, ResizeHandle, withResize, isResizingByCondition, getRatioByPoint } from '@plait/common';
6
- import { Alignment, buildText, AlignEditor, TextManage, DEFAULT_FONT_SIZE, getTextFromClipboard, getTextSize } from '@plait/text';
7
8
  import { isKeyHotkey } from 'is-hotkey';
8
9
  import { Node } from 'slate';
9
10
 
@@ -15,6 +16,20 @@ var GeometryShape;
15
16
  GeometryShape["roundRectangle"] = "roundRectangle";
16
17
  GeometryShape["parallelogram"] = "parallelogram";
17
18
  GeometryShape["text"] = "text";
19
+ GeometryShape["triangle"] = "triangle";
20
+ GeometryShape["leftArrow"] = "leftArrow";
21
+ GeometryShape["trapezoid"] = "trapezoid";
22
+ GeometryShape["rightArrow"] = "rightArrow";
23
+ GeometryShape["cross"] = "cross";
24
+ GeometryShape["star"] = "star";
25
+ GeometryShape["pentagon"] = "pentagon";
26
+ GeometryShape["hexagon"] = "hexagon";
27
+ GeometryShape["octagon"] = "octagon";
28
+ GeometryShape["pentagonArrow"] = "pentagonArrow";
29
+ GeometryShape["processArrow"] = "processArrow";
30
+ GeometryShape["twoWayArrow"] = "twoWayArrow";
31
+ GeometryShape["comment"] = "comment";
32
+ GeometryShape["roundComment"] = "roundComment";
18
33
  })(GeometryShape || (GeometryShape = {}));
19
34
  const PlaitGeometry = {
20
35
  getTextEditor(element) {
@@ -29,79 +44,6 @@ const PlaitGeometry = {
29
44
  }
30
45
  };
31
46
 
32
- var LineMarkerType;
33
- (function (LineMarkerType) {
34
- LineMarkerType["arrow"] = "arrow";
35
- LineMarkerType["none"] = "none";
36
- LineMarkerType["openTriangle"] = "open-triangle";
37
- LineMarkerType["solidTriangle"] = "solid-triangle";
38
- LineMarkerType["sharpArrow"] = "sharp-arrow";
39
- })(LineMarkerType || (LineMarkerType = {}));
40
- var LineShape;
41
- (function (LineShape) {
42
- LineShape["straight"] = "straight";
43
- LineShape["curve"] = "curve";
44
- LineShape["elbow"] = "elbow";
45
- })(LineShape || (LineShape = {}));
46
- var LineHandleKey;
47
- (function (LineHandleKey) {
48
- LineHandleKey["source"] = "source";
49
- LineHandleKey["target"] = "target";
50
- })(LineHandleKey || (LineHandleKey = {}));
51
- const PlaitLine = {
52
- getTextEditors(element) {
53
- const component = PlaitElement.getComponent(element);
54
- if (component) {
55
- const manage = component.textManages.find(manage => manage.isEditing);
56
- if (manage) {
57
- return [manage.componentRef.instance.editor];
58
- }
59
- else {
60
- return component.textManages.map(manage => manage.componentRef.instance.editor);
61
- }
62
- }
63
- throw new Error('can not get correctly component in get text editor');
64
- },
65
- isSourceMark(line, markType) {
66
- return line.source.marker === markType;
67
- },
68
- isTargetMark(line, markType) {
69
- return line.target.marker === markType;
70
- },
71
- isBoundElementOfSource(line, element) {
72
- return line.source.boundId === element.id;
73
- },
74
- isBoundElementOfTarget(line, element) {
75
- return line.target.boundId === element.id;
76
- }
77
- };
78
-
79
- var StrokeStyle;
80
- (function (StrokeStyle) {
81
- StrokeStyle["solid"] = "solid";
82
- StrokeStyle["dashed"] = "dashed";
83
- })(StrokeStyle || (StrokeStyle = {}));
84
-
85
- const PlaitDrawElement = {
86
- isGeometry: (value) => {
87
- return value.type === 'geometry';
88
- },
89
- isLine: (value) => {
90
- return value.type === 'line';
91
- },
92
- isText: (value) => {
93
- return value.type === 'geometry' && value.shape === GeometryShape.text;
94
- },
95
- isDrawElement: (value) => {
96
- if (PlaitDrawElement.isGeometry(value) || PlaitDrawElement.isLine(value)) {
97
- return true;
98
- }
99
- else {
100
- return false;
101
- }
102
- }
103
- };
104
-
105
47
  const ShapeDefaultSpace = {
106
48
  rectangleAndText: 4
107
49
  };
@@ -127,27 +69,17 @@ const DefaultTextProperty = {
127
69
  text: '文本'
128
70
  };
129
71
  const GeometryThreshold = {
130
- defaultTextMaxWidth: 34 * 14,
72
+ defaultTextMaxWidth: 34 * 14
73
+ };
74
+
75
+ const getGeometryPointers = () => {
76
+ return Object.keys(GeometryShape);
77
+ };
78
+ const getLinePointers = () => {
79
+ return Object.keys(LineShape);
131
80
  };
132
81
 
133
- var DrawPointerType;
134
- (function (DrawPointerType) {
135
- DrawPointerType["text"] = "text";
136
- DrawPointerType["rectangle"] = "rectangle";
137
- DrawPointerType["line"] = "line";
138
- DrawPointerType["diamond"] = "diamond";
139
- DrawPointerType["roundRectangle"] = "roundRectangle";
140
- DrawPointerType["parallelogram"] = "parallelogram";
141
- DrawPointerType["ellipse"] = "ellipse";
142
- })(DrawPointerType || (DrawPointerType = {}));
143
- const GeometryPointer = [
144
- DrawPointerType.rectangle,
145
- DrawPointerType.text,
146
- DrawPointerType.diamond,
147
- DrawPointerType.ellipse,
148
- DrawPointerType.parallelogram,
149
- DrawPointerType.roundRectangle
150
- ];
82
+ const DEFAULT_IMAGE_WIDTH = 1000;
151
83
 
152
84
  const getStrokeWidthByElement = (element) => {
153
85
  if (PlaitDrawElement.isText(element)) {
@@ -171,6 +103,102 @@ const getStrokeStyleByElement = (element) => {
171
103
  return element.strokeStyle || StrokeStyle.solid;
172
104
  };
173
105
 
106
+ const heightRatio$1 = 3 / 4;
107
+ const CommentEngine = {
108
+ draw(board, rectangle, options) {
109
+ const points = getCommentPoints(rectangle);
110
+ const rs = PlaitBoard.getRoughSVG(board);
111
+ const polygon = rs.polygon(points, { ...options, fillStyle: 'solid' });
112
+ setStrokeLinecap(polygon, 'round');
113
+ return polygon;
114
+ },
115
+ isHit(rectangle, point) {
116
+ const parallelogramPoints = getCommentPoints(rectangle);
117
+ return isPointInPolygon(point, parallelogramPoints);
118
+ },
119
+ getCornerPoints(rectangle) {
120
+ return getCommentPoints(rectangle);
121
+ },
122
+ getNearestPoint(rectangle, point) {
123
+ return getNearestPointBetweenPointAndSegments(point, getCommentPoints(rectangle));
124
+ },
125
+ getEdgeByConnectionPoint(rectangle, pointOfRectangle) {
126
+ const corners = getCommentPoints(rectangle);
127
+ const point = RectangleClient.getConnectionPoint(rectangle, pointOfRectangle);
128
+ return getEdgeOnPolygonByPoint(corners, point);
129
+ },
130
+ getConnectorPoints(rectangle) {
131
+ return RectangleClient.getEdgeCenterPoints(rectangle);
132
+ },
133
+ getTextRectangle(element) {
134
+ const elementRectangle = getRectangleByPoints(element.points);
135
+ const strokeWidth = getStrokeWidthByElement(element);
136
+ const height = element.textHeight;
137
+ const width = elementRectangle.width - ShapeDefaultSpace.rectangleAndText * 2 - strokeWidth * 2;
138
+ return {
139
+ height,
140
+ width: width > 0 ? width : 0,
141
+ x: elementRectangle.x + ShapeDefaultSpace.rectangleAndText + strokeWidth,
142
+ y: elementRectangle.y + (elementRectangle.height * heightRatio$1 - height) / 2
143
+ };
144
+ }
145
+ };
146
+ const getCommentPoints = (rectangle) => {
147
+ return [
148
+ [rectangle.x, rectangle.y],
149
+ [rectangle.x + rectangle.width, rectangle.y],
150
+ [rectangle.x + rectangle.width, rectangle.y + rectangle.height * heightRatio$1],
151
+ [rectangle.x + (rectangle.width * 3) / 5, rectangle.y + rectangle.height * heightRatio$1],
152
+ [rectangle.x + rectangle.width / 2, rectangle.y + rectangle.height],
153
+ [rectangle.x + (rectangle.width * 2) / 5, rectangle.y + rectangle.height * heightRatio$1],
154
+ [rectangle.x, rectangle.y + rectangle.height * heightRatio$1]
155
+ ];
156
+ };
157
+
158
+ const CrossEngine = {
159
+ draw(board, rectangle, options) {
160
+ const points = getCrossPoints(rectangle);
161
+ const rs = PlaitBoard.getRoughSVG(board);
162
+ const polygon = rs.polygon(points, { ...options, fillStyle: 'solid' });
163
+ setStrokeLinecap(polygon, 'round');
164
+ return polygon;
165
+ },
166
+ isHit(rectangle, point) {
167
+ const parallelogramPoints = getCrossPoints(rectangle);
168
+ return isPointInPolygon(point, parallelogramPoints);
169
+ },
170
+ getCornerPoints(rectangle) {
171
+ return getCrossPoints(rectangle);
172
+ },
173
+ getNearestPoint(rectangle, point) {
174
+ return getNearestPointBetweenPointAndSegments(point, getCrossPoints(rectangle));
175
+ },
176
+ getEdgeByConnectionPoint(rectangle, pointOfRectangle) {
177
+ const corners = getCrossPoints(rectangle);
178
+ const point = RectangleClient.getConnectionPoint(rectangle, pointOfRectangle);
179
+ return getEdgeOnPolygonByPoint(corners, point);
180
+ },
181
+ getConnectorPoints(rectangle) {
182
+ return RectangleClient.getEdgeCenterPoints(rectangle);
183
+ }
184
+ };
185
+ const getCrossPoints = (rectangle) => {
186
+ return [
187
+ [rectangle.x + rectangle.width / 4, rectangle.y],
188
+ [rectangle.x + (rectangle.width * 3) / 4, rectangle.y],
189
+ [rectangle.x + (rectangle.width * 3) / 4, rectangle.y + rectangle.height / 4],
190
+ [rectangle.x + rectangle.width, rectangle.y + rectangle.height / 4],
191
+ [rectangle.x + rectangle.width, rectangle.y + (rectangle.height * 3) / 4],
192
+ [rectangle.x + (rectangle.width * 3) / 4, rectangle.y + (rectangle.height * 3) / 4],
193
+ [rectangle.x + (rectangle.width * 3) / 4, rectangle.y + rectangle.height],
194
+ [rectangle.x + rectangle.width / 4, rectangle.y + rectangle.height],
195
+ [rectangle.x + rectangle.width / 4, rectangle.y + (rectangle.height * 3) / 4],
196
+ [rectangle.x, rectangle.y + (rectangle.height * 3) / 4],
197
+ [rectangle.x, rectangle.y + rectangle.height / 4],
198
+ [rectangle.x + rectangle.width / 4, rectangle.y + rectangle.height / 4]
199
+ ];
200
+ };
201
+
174
202
  const DiamondEngine = {
175
203
  draw(board, rectangle, options) {
176
204
  const points = RectangleClient.getEdgeCenterPoints(rectangle);
@@ -183,9 +211,16 @@ const DiamondEngine = {
183
211
  const controlPoints = RectangleClient.getEdgeCenterPoints(rectangle);
184
212
  return isPointInPolygon(point, controlPoints);
185
213
  },
214
+ getCornerPoints(rectangle) {
215
+ return RectangleClient.getEdgeCenterPoints(rectangle);
216
+ },
186
217
  getNearestPoint(rectangle, point) {
187
- const connectorPoints = RectangleClient.getEdgeCenterPoints(rectangle);
188
- return getNearestPointBetweenPointAndSegments(point, connectorPoints);
218
+ return getNearestPointBetweenPointAndSegments(point, DiamondEngine.getCornerPoints(rectangle));
219
+ },
220
+ getEdgeByConnectionPoint(rectangle, pointOfRectangle) {
221
+ const corners = DiamondEngine.getCornerPoints(rectangle);
222
+ const point = RectangleClient.getConnectionPoint(rectangle, pointOfRectangle);
223
+ return getEdgeOnPolygonByPoint(corners, point);
189
224
  },
190
225
  getConnectorPoints(rectangle) {
191
226
  return RectangleClient.getEdgeCenterPoints(rectangle);
@@ -202,10 +237,22 @@ const EllipseEngine = {
202
237
  const centerPoint = [rectangle.x + rectangle.width / 2, rectangle.y + rectangle.height / 2];
203
238
  return isPointInEllipse(point, centerPoint, rectangle.width / 2, rectangle.height / 2);
204
239
  },
240
+ getCornerPoints(rectangle) {
241
+ return RectangleClient.getEdgeCenterPoints(rectangle);
242
+ },
205
243
  getNearestPoint(rectangle, point) {
206
244
  const centerPoint = [rectangle.x + rectangle.width / 2, rectangle.y + rectangle.height / 2];
207
245
  return getNearestPointBetweenPointAndEllipse(point, centerPoint, rectangle.width / 2, rectangle.height / 2);
208
246
  },
247
+ getTangentVectorByConnectionPoint(rectangle, pointOfRectangle) {
248
+ const connectionPoint = RectangleClient.getConnectionPoint(rectangle, pointOfRectangle);
249
+ const centerPoint = [rectangle.x + rectangle.width / 2, rectangle.y + rectangle.height / 2];
250
+ const point = [connectionPoint[0] - centerPoint[0], -(connectionPoint[1] - centerPoint[1])];
251
+ const a = rectangle.width / 2;
252
+ const b = rectangle.height / 2;
253
+ const slope = getTangentSlope(point[0], point[1], a, b);
254
+ return getVectorBySlope(point[0], point[1], slope);
255
+ },
209
256
  getConnectorPoints(rectangle) {
210
257
  return RectangleClient.getEdgeCenterPoints(rectangle);
211
258
  }
@@ -245,29 +292,182 @@ function getNearestPointBetweenPointAndEllipse(point, center, rx, ry, rotation =
245
292
  const signY = point[1] > center[1] ? 1 : -1;
246
293
  return [center[0] + a * tx * signX, center[1] + b * ty * signY];
247
294
  }
295
+ /**
296
+ * the result of slope is based on Cartesian coordinate system
297
+ * x, y are based on the position in the Cartesian coordinate system
298
+ */
299
+ function getTangentSlope(x, y, a, b) {
300
+ const k = (-b * b * x) / (a * a * y);
301
+ return k;
302
+ }
303
+ /**
304
+ * x, y are based on the position in the Cartesian coordinate system
305
+ */
306
+ function getVectorBySlope(x, y, slope) {
307
+ const deltaX = 30;
308
+ const deltaY = -slope * deltaX;
309
+ let start = [0 - deltaX, 0 - deltaY];
310
+ let end = [0 + deltaX, 0 + deltaY];
311
+ // y < 0 acts on the lower half of the x-axis, with the starting point at the top and the end point at the bottom.
312
+ if (y < 0) {
313
+ const temp = start;
314
+ start = end;
315
+ end = temp;
316
+ }
317
+ const vector = [end[0] - start[0], end[1] - start[1]];
318
+ return vector;
319
+ }
248
320
 
249
- const ParallelogramEngine = {
321
+ const HexagonEngine = {
250
322
  draw(board, rectangle, options) {
251
- const points = getParallelogramPoints(rectangle);
323
+ const points = getHexagonPoints(rectangle);
252
324
  const rs = PlaitBoard.getRoughSVG(board);
253
325
  const polygon = rs.polygon(points, { ...options, fillStyle: 'solid' });
254
326
  setStrokeLinecap(polygon, 'round');
255
327
  return polygon;
256
328
  },
257
329
  isHit(rectangle, point) {
258
- const parallelogramPoints = getParallelogramPoints(rectangle);
330
+ const parallelogramPoints = getHexagonPoints(rectangle);
259
331
  return isPointInPolygon(point, parallelogramPoints);
260
332
  },
333
+ getCornerPoints(rectangle) {
334
+ return getHexagonPoints(rectangle);
335
+ },
336
+ getNearestPoint(rectangle, point) {
337
+ return getNearestPointBetweenPointAndSegments(point, getHexagonPoints(rectangle));
338
+ },
339
+ getEdgeByConnectionPoint(rectangle, pointOfRectangle) {
340
+ const corners = getHexagonPoints(rectangle);
341
+ const point = RectangleClient.getConnectionPoint(rectangle, pointOfRectangle);
342
+ return getEdgeOnPolygonByPoint(corners, point);
343
+ },
344
+ getConnectorPoints(rectangle) {
345
+ return RectangleClient.getEdgeCenterPoints(rectangle);
346
+ }
347
+ };
348
+ const getHexagonPoints = (rectangle) => {
349
+ return [
350
+ [rectangle.x + rectangle.width / 4, rectangle.y],
351
+ [rectangle.x + (rectangle.width * 3) / 4, rectangle.y],
352
+ [rectangle.x + rectangle.width, rectangle.y + rectangle.height / 2],
353
+ [rectangle.x + (rectangle.width * 3) / 4, rectangle.y + rectangle.height],
354
+ [rectangle.x + rectangle.width / 4, rectangle.y + rectangle.height],
355
+ [rectangle.x, rectangle.y + rectangle.height / 2]
356
+ ];
357
+ };
358
+
359
+ const LeftArrowEngine = {
360
+ draw(board, rectangle, options) {
361
+ const points = getLeftArrowPoints(rectangle);
362
+ const rs = PlaitBoard.getRoughSVG(board);
363
+ const polygon = rs.polygon(points, { ...options, fillStyle: 'solid' });
364
+ setStrokeLinecap(polygon, 'round');
365
+ return polygon;
366
+ },
367
+ getCornerPoints(rectangle) {
368
+ return getLeftArrowPoints(rectangle);
369
+ },
370
+ isHit(rectangle, point) {
371
+ const points = getLeftArrowPoints(rectangle);
372
+ return isPointInPolygon(point, points);
373
+ },
261
374
  getNearestPoint(rectangle, point) {
262
- const cornerPoints = getParallelogramPoints(rectangle);
375
+ const cornerPoints = getLeftArrowPoints(rectangle);
263
376
  return getNearestPointBetweenPointAndSegments(point, cornerPoints);
264
377
  },
378
+ getEdgeByConnectionPoint(rectangle, pointOfRectangle) {
379
+ const corners = getLeftArrowPoints(rectangle);
380
+ const point = RectangleClient.getConnectionPoint(rectangle, pointOfRectangle);
381
+ return getEdgeOnPolygonByPoint(corners, point);
382
+ },
383
+ getConnectorPoints(rectangle) {
384
+ return [
385
+ [rectangle.x, rectangle.y + rectangle.height / 2],
386
+ [rectangle.x + rectangle.width, rectangle.y + rectangle.height / 2]
387
+ ];
388
+ }
389
+ };
390
+ const getLeftArrowPoints = (rectangle) => {
391
+ return [
392
+ [rectangle.x, rectangle.y + rectangle.height / 2],
393
+ [rectangle.x + rectangle.width * 0.32, rectangle.y],
394
+ [rectangle.x + rectangle.width * 0.32, rectangle.y + rectangle.height * 0.2],
395
+ [rectangle.x + rectangle.width, rectangle.y + rectangle.height * 0.2],
396
+ [rectangle.x + rectangle.width, rectangle.y + rectangle.height * 0.8],
397
+ [rectangle.x + rectangle.width * 0.32, rectangle.y + rectangle.height * 0.8],
398
+ [rectangle.x + rectangle.width * 0.32, rectangle.y + rectangle.height]
399
+ ];
400
+ };
401
+
402
+ const OctagonEngine = {
403
+ draw(board, rectangle, options) {
404
+ const points = getOctagonPoints(rectangle);
405
+ const rs = PlaitBoard.getRoughSVG(board);
406
+ const polygon = rs.polygon(points, { ...options, fillStyle: 'solid' });
407
+ setStrokeLinecap(polygon, 'round');
408
+ return polygon;
409
+ },
410
+ isHit(rectangle, point) {
411
+ const parallelogramPoints = getOctagonPoints(rectangle);
412
+ return isPointInPolygon(point, parallelogramPoints);
413
+ },
414
+ getCornerPoints(rectangle) {
415
+ return getOctagonPoints(rectangle);
416
+ },
417
+ getNearestPoint(rectangle, point) {
418
+ return getNearestPointBetweenPointAndSegments(point, getOctagonPoints(rectangle));
419
+ },
420
+ getEdgeByConnectionPoint(rectangle, pointOfRectangle) {
421
+ const corners = getOctagonPoints(rectangle);
422
+ const point = RectangleClient.getConnectionPoint(rectangle, pointOfRectangle);
423
+ return getEdgeOnPolygonByPoint(corners, point);
424
+ },
425
+ getConnectorPoints(rectangle) {
426
+ return RectangleClient.getEdgeCenterPoints(rectangle);
427
+ }
428
+ };
429
+ const getOctagonPoints = (rectangle) => {
430
+ return [
431
+ [rectangle.x + (rectangle.width * 3) / 10, rectangle.y],
432
+ [rectangle.x + (rectangle.width * 7) / 10, rectangle.y],
433
+ [rectangle.x + rectangle.width, rectangle.y + (rectangle.height * 3) / 10],
434
+ [rectangle.x + rectangle.width, rectangle.y + (rectangle.height * 7) / 10],
435
+ [rectangle.x + (rectangle.width * 7) / 10, rectangle.y + rectangle.height],
436
+ [rectangle.x + (rectangle.width * 3) / 10, rectangle.y + rectangle.height],
437
+ [rectangle.x, rectangle.y + (rectangle.height * 7) / 10],
438
+ [rectangle.x, rectangle.y + (rectangle.height * 3) / 10]
439
+ ];
440
+ };
441
+
442
+ const ParallelogramEngine = {
443
+ draw(board, rectangle, options) {
444
+ const points = getParallelogramCornerPoints(rectangle);
445
+ const rs = PlaitBoard.getRoughSVG(board);
446
+ const polygon = rs.polygon(points, { ...options, fillStyle: 'solid' });
447
+ setStrokeLinecap(polygon, 'round');
448
+ return polygon;
449
+ },
450
+ isHit(rectangle, point) {
451
+ const parallelogramPoints = getParallelogramCornerPoints(rectangle);
452
+ return isPointInPolygon(point, parallelogramPoints);
453
+ },
454
+ getCornerPoints(rectangle) {
455
+ return getParallelogramCornerPoints(rectangle);
456
+ },
457
+ getNearestPoint(rectangle, point) {
458
+ return getNearestPointBetweenPointAndSegments(point, ParallelogramEngine.getCornerPoints(rectangle));
459
+ },
460
+ getEdgeByConnectionPoint(rectangle, pointOfRectangle) {
461
+ const corners = getParallelogramCornerPoints(rectangle);
462
+ const point = RectangleClient.getConnectionPoint(rectangle, pointOfRectangle);
463
+ return getEdgeOnPolygonByPoint(corners, point);
464
+ },
265
465
  getConnectorPoints(rectangle) {
266
- const cornerPoints = getParallelogramPoints(rectangle);
466
+ const cornerPoints = getParallelogramCornerPoints(rectangle);
267
467
  return getCenterPointsOnPolygon(cornerPoints);
268
468
  }
269
469
  };
270
- const getParallelogramPoints = (rectangle) => {
470
+ const getParallelogramCornerPoints = (rectangle) => {
271
471
  return [
272
472
  [rectangle.x + rectangle.width / 4, rectangle.y],
273
473
  [rectangle.x + rectangle.width, rectangle.y],
@@ -276,6 +476,119 @@ const getParallelogramPoints = (rectangle) => {
276
476
  ];
277
477
  };
278
478
 
479
+ const PentagonEngine = {
480
+ draw(board, rectangle, options) {
481
+ const points = getPentagonPoints(rectangle);
482
+ const rs = PlaitBoard.getRoughSVG(board);
483
+ const polygon = rs.polygon(points, { ...options, fillStyle: 'solid' });
484
+ setStrokeLinecap(polygon, 'round');
485
+ return polygon;
486
+ },
487
+ isHit(rectangle, point) {
488
+ const parallelogramPoints = getPentagonPoints(rectangle);
489
+ return isPointInPolygon(point, parallelogramPoints);
490
+ },
491
+ getCornerPoints(rectangle) {
492
+ return getPentagonPoints(rectangle);
493
+ },
494
+ getNearestPoint(rectangle, point) {
495
+ return getNearestPointBetweenPointAndSegments(point, getPentagonPoints(rectangle));
496
+ },
497
+ getEdgeByConnectionPoint(rectangle, pointOfRectangle) {
498
+ const corners = getPentagonPoints(rectangle);
499
+ const point = RectangleClient.getConnectionPoint(rectangle, pointOfRectangle);
500
+ return getEdgeOnPolygonByPoint(corners, point);
501
+ },
502
+ getConnectorPoints(rectangle) {
503
+ return getPentagonPoints(rectangle);
504
+ }
505
+ };
506
+ const getPentagonPoints = (rectangle) => {
507
+ return [
508
+ [rectangle.x + rectangle.width / 2, rectangle.y],
509
+ [rectangle.x + rectangle.width, rectangle.y + (rectangle.height * 2) / 5],
510
+ [rectangle.x + (rectangle.width * 4) / 5, rectangle.y + rectangle.height],
511
+ [rectangle.x + rectangle.width / 5, rectangle.y + rectangle.height],
512
+ [rectangle.x, rectangle.y + (rectangle.height * 2) / 5]
513
+ ];
514
+ };
515
+
516
+ const PentagonArrowEngine = {
517
+ draw(board, rectangle, options) {
518
+ const points = getPentagonArrowPoints(rectangle);
519
+ const rs = PlaitBoard.getRoughSVG(board);
520
+ const polygon = rs.polygon(points, { ...options, fillStyle: 'solid' });
521
+ setStrokeLinecap(polygon, 'round');
522
+ return polygon;
523
+ },
524
+ isHit(rectangle, point) {
525
+ const parallelogramPoints = getPentagonArrowPoints(rectangle);
526
+ return isPointInPolygon(point, parallelogramPoints);
527
+ },
528
+ getCornerPoints(rectangle) {
529
+ return getPentagonArrowPoints(rectangle);
530
+ },
531
+ getNearestPoint(rectangle, point) {
532
+ return getNearestPointBetweenPointAndSegments(point, getPentagonArrowPoints(rectangle));
533
+ },
534
+ getEdgeByConnectionPoint(rectangle, pointOfRectangle) {
535
+ const corners = getPentagonArrowPoints(rectangle);
536
+ const point = RectangleClient.getConnectionPoint(rectangle, pointOfRectangle);
537
+ return getEdgeOnPolygonByPoint(corners, point);
538
+ },
539
+ getConnectorPoints(rectangle) {
540
+ return RectangleClient.getEdgeCenterPoints(rectangle);
541
+ }
542
+ };
543
+ const getPentagonArrowPoints = (rectangle) => {
544
+ return [
545
+ [rectangle.x, rectangle.y],
546
+ [rectangle.x + (rectangle.width * 3) / 5, rectangle.y],
547
+ [rectangle.x + rectangle.width, rectangle.y + rectangle.height / 2],
548
+ [rectangle.x + (rectangle.width * 3) / 5, rectangle.y + rectangle.height],
549
+ [rectangle.x, rectangle.y + rectangle.height]
550
+ ];
551
+ };
552
+
553
+ const ProcessArrowEngine = {
554
+ draw(board, rectangle, options) {
555
+ const points = getProcessArrowPoints(rectangle);
556
+ const rs = PlaitBoard.getRoughSVG(board);
557
+ const polygon = rs.polygon(points, { ...options, fillStyle: 'solid' });
558
+ setStrokeLinecap(polygon, 'round');
559
+ return polygon;
560
+ },
561
+ isHit(rectangle, point) {
562
+ const parallelogramPoints = getProcessArrowPoints(rectangle);
563
+ return isPointInPolygon(point, parallelogramPoints);
564
+ },
565
+ getCornerPoints(rectangle) {
566
+ return getProcessArrowPoints(rectangle);
567
+ },
568
+ getNearestPoint(rectangle, point) {
569
+ return getNearestPointBetweenPointAndSegments(point, getProcessArrowPoints(rectangle));
570
+ },
571
+ getEdgeByConnectionPoint(rectangle, pointOfRectangle) {
572
+ const corners = getProcessArrowPoints(rectangle);
573
+ const point = RectangleClient.getConnectionPoint(rectangle, pointOfRectangle);
574
+ return getEdgeOnPolygonByPoint(corners, point);
575
+ },
576
+ getConnectorPoints(rectangle) {
577
+ return getProcessArrowPoints(rectangle);
578
+ }
579
+ };
580
+ const getProcessArrowPoints = (rectangle) => {
581
+ const wider = rectangle.width > rectangle.height / 2;
582
+ return [
583
+ [rectangle.x, rectangle.y],
584
+ [rectangle.x + (wider ? rectangle.width - rectangle.height / 2 : 0), rectangle.y],
585
+ [rectangle.x + rectangle.width, rectangle.y + rectangle.height / 2],
586
+ [rectangle.x + (wider ? rectangle.width - rectangle.height / 2 : 0), rectangle.y + rectangle.height],
587
+ [rectangle.x, rectangle.y + rectangle.height],
588
+ [rectangle.x + (wider ? rectangle.height / 2 : rectangle.width), rectangle.y + rectangle.height / 2]
589
+ ];
590
+ };
591
+
279
592
  const RectangleEngine = {
280
593
  draw(board, rectangle, options) {
281
594
  return drawRectangle(board, rectangle, { ...options, fillStyle: 'solid' });
@@ -284,15 +597,65 @@ const RectangleEngine = {
284
597
  const rangeRectangle = RectangleClient.toRectangleClient([point, point]);
285
598
  return RectangleClient.isHit(rectangle, rangeRectangle);
286
599
  },
600
+ getCornerPoints(rectangle) {
601
+ return RectangleClient.getCornerPoints(rectangle);
602
+ },
287
603
  getNearestPoint(rectangle, point) {
288
- const cornerPoints = RectangleClient.getCornerPoints(rectangle);
289
- return getNearestPointBetweenPointAndSegments(point, cornerPoints);
604
+ return getNearestPointBetweenPointAndSegments(point, RectangleEngine.getCornerPoints(rectangle));
605
+ },
606
+ getEdgeByConnectionPoint(rectangle, pointOfRectangle) {
607
+ const corners = RectangleEngine.getCornerPoints(rectangle);
608
+ const point = RectangleClient.getConnectionPoint(rectangle, pointOfRectangle);
609
+ return getEdgeOnPolygonByPoint(corners, point);
290
610
  },
291
611
  getConnectorPoints(rectangle) {
292
612
  return RectangleClient.getEdgeCenterPoints(rectangle);
293
613
  }
294
614
  };
295
615
 
616
+ const RightArrowEngine = {
617
+ draw(board, rectangle, options) {
618
+ const points = getRightArrowPoints(rectangle);
619
+ const rs = PlaitBoard.getRoughSVG(board);
620
+ const polygon = rs.polygon(points, { ...options, fillStyle: 'solid' });
621
+ setStrokeLinecap(polygon, 'round');
622
+ return polygon;
623
+ },
624
+ getCornerPoints(rectangle) {
625
+ return getRightArrowPoints(rectangle);
626
+ },
627
+ isHit(rectangle, point) {
628
+ const points = getRightArrowPoints(rectangle);
629
+ return isPointInPolygon(point, points);
630
+ },
631
+ getNearestPoint(rectangle, point) {
632
+ const cornerPoints = getRightArrowPoints(rectangle);
633
+ return getNearestPointBetweenPointAndSegments(point, cornerPoints);
634
+ },
635
+ getEdgeByConnectionPoint(rectangle, pointOfRectangle) {
636
+ const corners = getRightArrowPoints(rectangle);
637
+ const point = RectangleClient.getConnectionPoint(rectangle, pointOfRectangle);
638
+ return getEdgeOnPolygonByPoint(corners, point);
639
+ },
640
+ getConnectorPoints(rectangle) {
641
+ return [
642
+ [rectangle.x, rectangle.y + rectangle.height / 2],
643
+ [rectangle.x + rectangle.width, rectangle.y + rectangle.height / 2]
644
+ ];
645
+ }
646
+ };
647
+ const getRightArrowPoints = (rectangle) => {
648
+ return [
649
+ [rectangle.x, rectangle.y + rectangle.height * 0.2],
650
+ [rectangle.x + rectangle.width * 0.68, rectangle.y + rectangle.height * 0.2],
651
+ [rectangle.x + rectangle.width * 0.68, rectangle.y],
652
+ [rectangle.x + rectangle.width, rectangle.y + rectangle.height / 2],
653
+ [rectangle.x + rectangle.width * 0.68, rectangle.y + rectangle.height],
654
+ [rectangle.x + rectangle.width * 0.68, rectangle.y + rectangle.height * 0.8],
655
+ [rectangle.x, rectangle.y + rectangle.height * 0.8]
656
+ ];
657
+ };
658
+
296
659
  const RoundRectangleEngine = {
297
660
  draw(board, rectangle, options) {
298
661
  return drawRoundRectangle(PlaitBoard.getRoughSVG(board), rectangle.x, rectangle.y, rectangle.x + rectangle.width, rectangle.y + rectangle.height, { ...options, fillStyle: 'solid' }, false, getRoundRectangleRadius(rectangle));
@@ -300,9 +663,17 @@ const RoundRectangleEngine = {
300
663
  isHit(rectangle, point) {
301
664
  return isPointInRoundRectangle(point, rectangle, getRoundRectangleRadius(rectangle));
302
665
  },
666
+ getCornerPoints(rectangle) {
667
+ return RectangleClient.getCornerPoints(rectangle);
668
+ },
303
669
  getNearestPoint(rectangle, point) {
304
670
  return getNearestPointBetweenPointAndRoundRectangle(point, rectangle, getRoundRectangleRadius(rectangle));
305
671
  },
672
+ getEdgeByConnectionPoint(rectangle, pointOfRectangle) {
673
+ const corners = RectangleEngine.getCornerPoints(rectangle);
674
+ const point = RectangleClient.getConnectionPoint(rectangle, pointOfRectangle);
675
+ return getEdgeOnPolygonByPoint(corners, point);
676
+ },
306
677
  getConnectorPoints(rectangle) {
307
678
  return RectangleClient.getEdgeCenterPoints(rectangle);
308
679
  }
@@ -340,18 +711,279 @@ function getNearestPointBetweenPointAndRoundRectangle(point, rectangle, radius)
340
711
  return result;
341
712
  }
342
713
 
714
+ const heightRatio = 3 / 4;
715
+ const RoundCommentEngine = {
716
+ draw(board, rectangle, options) {
717
+ const rs = PlaitBoard.getRoughSVG(board);
718
+ const x1 = rectangle.x;
719
+ const y1 = rectangle.y;
720
+ const x2 = rectangle.x + rectangle.width;
721
+ const y2 = rectangle.y + rectangle.height * heightRatio;
722
+ const radius = getRoundRectangleRadius(rectangle);
723
+ const point1 = [x1 + radius, y1];
724
+ const point2 = [x2 - radius, y1];
725
+ const point3 = [x2, y1 + radius];
726
+ const point4 = [x2, y2 - radius];
727
+ const point5 = [x2 - radius, y2];
728
+ const point6 = [x1 + radius, y2];
729
+ const point7 = [x1, y2 - radius];
730
+ const point8 = [x1, y1 + radius];
731
+ const point9 = [x1 + rectangle.width / 4, y2];
732
+ const point10 = [x1 + rectangle.width / 4, rectangle.y + rectangle.height];
733
+ const point11 = [x1 + rectangle.width / 2, y2];
734
+ return rs.path(`M${point2[0]} ${point2[1]} A ${radius} ${radius}, 0, 0, 1, ${point3[0]} ${point3[1]} L ${point4[0]} ${point4[1]} A ${radius} ${radius}, 0, 0, 1, ${point5[0]} ${point5[1]} L ${point11[0]} ${point11[1]} ${point10[0]} ${point10[1]} ${point9[0]} ${point9[1]} ${point6[0]} ${point6[1]} A ${radius} ${radius}, 0, 0, 1, ${point7[0]} ${point7[1]} L ${point8[0]} ${point8[1]} A ${radius} ${radius}, 0, 0, 1, ${point1[0]} ${point1[1]} Z`, options);
735
+ },
736
+ isHit(rectangle, point) {
737
+ const points = [
738
+ [rectangle.x + rectangle.width / 4, rectangle.y + (rectangle.height * 3) / 4],
739
+ [rectangle.x + rectangle.width / 4, rectangle.y + rectangle.height],
740
+ [rectangle.x + rectangle.width / 2, rectangle.y + (rectangle.height * 3) / 4]
741
+ ];
742
+ rectangle.height = (rectangle.height * 3) / 4;
743
+ return isPointInPolygon(point, points) || isPointInRoundRectangle(point, rectangle, getRoundRectangleRadius(rectangle));
744
+ },
745
+ getCornerPoints(rectangle) {
746
+ return getRoundCommentPoints(rectangle);
747
+ },
748
+ getNearestPoint(rectangle, point) {
749
+ return getNearestPointBetweenPointAndSegments(point, getRoundCommentPoints(rectangle));
750
+ },
751
+ getEdgeByConnectionPoint(rectangle, pointOfRectangle) {
752
+ const corners = getRoundCommentPoints(rectangle);
753
+ const point = RectangleClient.getConnectionPoint(rectangle, pointOfRectangle);
754
+ return getEdgeOnPolygonByPoint(corners, point);
755
+ },
756
+ getConnectorPoints(rectangle) {
757
+ return [
758
+ [rectangle.x + rectangle.width / 2, rectangle.y],
759
+ [rectangle.x + rectangle.width, rectangle.y + rectangle.height / 2],
760
+ [rectangle.x + rectangle.width / 2, rectangle.y + rectangle.height * heightRatio],
761
+ [rectangle.x, rectangle.y + rectangle.height / 2]
762
+ ];
763
+ },
764
+ getTextRectangle(element) {
765
+ const elementRectangle = getRectangleByPoints(element.points);
766
+ const strokeWidth = getStrokeWidthByElement(element);
767
+ const height = element.textHeight;
768
+ const width = elementRectangle.width - ShapeDefaultSpace.rectangleAndText * 2 - strokeWidth * 2;
769
+ return {
770
+ height,
771
+ width: width > 0 ? width : 0,
772
+ x: elementRectangle.x + ShapeDefaultSpace.rectangleAndText + strokeWidth,
773
+ y: elementRectangle.y + (elementRectangle.height * heightRatio - height) / 2
774
+ };
775
+ }
776
+ };
777
+ const getRoundCommentPoints = (rectangle) => {
778
+ return [
779
+ [rectangle.x, rectangle.y],
780
+ [rectangle.x + rectangle.width, rectangle.y],
781
+ [rectangle.x + rectangle.width, rectangle.y + rectangle.height * heightRatio],
782
+ [rectangle.x + rectangle.width / 2, rectangle.y + rectangle.height * heightRatio],
783
+ [rectangle.x + rectangle.width / 4, rectangle.y + rectangle.height],
784
+ [rectangle.x + rectangle.width / 4, rectangle.y + rectangle.height * heightRatio],
785
+ [rectangle.x, rectangle.y + rectangle.height * heightRatio]
786
+ ];
787
+ };
788
+
789
+ const StarEngine = {
790
+ draw(board, rectangle, options) {
791
+ const points = getStarPoints(rectangle);
792
+ const rs = PlaitBoard.getRoughSVG(board);
793
+ const polygon = rs.polygon(points, { ...options, fillStyle: 'solid' });
794
+ setStrokeLinecap(polygon, 'round');
795
+ return polygon;
796
+ },
797
+ isHit(rectangle, point) {
798
+ const parallelogramPoints = getStarPoints(rectangle);
799
+ return isPointInPolygon(point, parallelogramPoints);
800
+ },
801
+ getCornerPoints(rectangle) {
802
+ return getStarPoints(rectangle);
803
+ },
804
+ getNearestPoint(rectangle, point) {
805
+ return getNearestPointBetweenPointAndSegments(point, getStarPoints(rectangle));
806
+ },
807
+ getEdgeByConnectionPoint(rectangle, pointOfRectangle) {
808
+ const corners = getStarPoints(rectangle);
809
+ const point = RectangleClient.getConnectionPoint(rectangle, pointOfRectangle);
810
+ return getEdgeOnPolygonByPoint(corners, point);
811
+ },
812
+ getConnectorPoints(rectangle) {
813
+ const points = getStarPoints(rectangle);
814
+ return [points[1], points[3], points[5], points[7], points[9]];
815
+ }
816
+ };
817
+ const getStarPoints = (rectangle) => {
818
+ return [
819
+ [rectangle.x + rectangle.width / 2, rectangle.y + (rectangle.height * 75) / 91],
820
+ [rectangle.x + (rectangle.width * 18.61) / 96, rectangle.y + rectangle.height],
821
+ [rectangle.x + (rectangle.width * 24.2235871) / 96, rectangle.y + (rectangle.height * 57.7254249) / 91],
822
+ [rectangle.x, rectangle.y + (rectangle.height * 34.5491503) / 91],
823
+ [rectangle.x + (rectangle.width * 33.3053687) / 96, rectangle.y + (rectangle.height * 29.7745751) / 91],
824
+ [rectangle.x + rectangle.width / 2, rectangle.y],
825
+ [rectangle.x + (rectangle.width * 62.6946313) / 96, rectangle.y + (rectangle.height * 29.7745751) / 91],
826
+ [rectangle.x + rectangle.width, rectangle.y + (rectangle.height * 34.5491503) / 91],
827
+ [rectangle.x + (rectangle.width * 71.7764129) / 96, rectangle.y + (rectangle.height * 57.7254249) / 91],
828
+ [rectangle.x + (rectangle.width * 77.3892626) / 96, rectangle.y + rectangle.height]
829
+ ];
830
+ };
831
+
832
+ const TrapezoidEngine = {
833
+ draw(board, rectangle, options) {
834
+ const points = getTrapezoidPoints(rectangle);
835
+ const rs = PlaitBoard.getRoughSVG(board);
836
+ const polygon = rs.polygon(points, { ...options, fillStyle: 'solid' });
837
+ setStrokeLinecap(polygon, 'round');
838
+ return polygon;
839
+ },
840
+ isHit(rectangle, point) {
841
+ const points = getTrapezoidPoints(rectangle);
842
+ return isPointInPolygon(point, points);
843
+ },
844
+ getNearestPoint(rectangle, point) {
845
+ const cornerPoints = getTrapezoidPoints(rectangle);
846
+ return getNearestPointBetweenPointAndSegments(point, cornerPoints);
847
+ },
848
+ getConnectorPoints(rectangle) {
849
+ const points = getTrapezoidPoints(rectangle);
850
+ return getCenterPointsOnPolygon(points);
851
+ },
852
+ getEdgeByConnectionPoint(rectangle, pointOfRectangle) {
853
+ const corners = getTrapezoidPoints(rectangle);
854
+ const point = RectangleClient.getConnectionPoint(rectangle, pointOfRectangle);
855
+ return getEdgeOnPolygonByPoint(corners, point);
856
+ },
857
+ getCornerPoints(rectangle) {
858
+ return getTrapezoidPoints(rectangle);
859
+ }
860
+ };
861
+ const getTrapezoidPoints = (rectangle) => {
862
+ return [
863
+ [rectangle.x + rectangle.width * 0.15, rectangle.y],
864
+ [rectangle.x + rectangle.width * 0.85, rectangle.y],
865
+ [rectangle.x + rectangle.width, rectangle.y + rectangle.height],
866
+ [rectangle.x, rectangle.y + rectangle.height]
867
+ ];
868
+ };
869
+
870
+ const TriangleEngine = {
871
+ draw(board, rectangle, options) {
872
+ const points = getTrianglePoints(rectangle);
873
+ const rs = PlaitBoard.getRoughSVG(board);
874
+ const polygon = rs.polygon(points, { ...options, fillStyle: 'solid' });
875
+ setStrokeLinecap(polygon, 'round');
876
+ return polygon;
877
+ },
878
+ isHit(rectangle, point) {
879
+ const points = getTrianglePoints(rectangle);
880
+ return isPointInPolygon(point, points);
881
+ },
882
+ getNearestPoint(rectangle, point) {
883
+ const cornerPoints = getTrianglePoints(rectangle);
884
+ return getNearestPointBetweenPointAndSegments(point, cornerPoints);
885
+ },
886
+ getConnectorPoints(rectangle) {
887
+ const cornerPoints = getTrianglePoints(rectangle);
888
+ const lineCenterPoints = getCenterPointsOnPolygon(cornerPoints);
889
+ return [...lineCenterPoints, ...cornerPoints];
890
+ },
891
+ getEdgeByConnectionPoint(rectangle, pointOfRectangle) {
892
+ const corners = getTrianglePoints(rectangle);
893
+ const point = RectangleClient.getConnectionPoint(rectangle, pointOfRectangle);
894
+ return getEdgeOnPolygonByPoint(corners, point);
895
+ },
896
+ getCornerPoints(rectangle) {
897
+ return getTrianglePoints(rectangle);
898
+ }
899
+ };
900
+ const getTrianglePoints = (rectangle) => {
901
+ return [
902
+ [rectangle.x + rectangle.width / 2, rectangle.y],
903
+ [rectangle.x + rectangle.width, rectangle.y + rectangle.height],
904
+ [rectangle.x, rectangle.y + rectangle.height]
905
+ ];
906
+ };
907
+
908
+ const TwoWayArrowEngine = {
909
+ draw(board, rectangle, options) {
910
+ const points = getTwoWayArrowPoints(rectangle);
911
+ const rs = PlaitBoard.getRoughSVG(board);
912
+ const polygon = rs.polygon(points, { ...options, fillStyle: 'solid' });
913
+ setStrokeLinecap(polygon, 'round');
914
+ return polygon;
915
+ },
916
+ getCornerPoints(rectangle) {
917
+ return getTwoWayArrowPoints(rectangle);
918
+ },
919
+ isHit(rectangle, point) {
920
+ const points = getTwoWayArrowPoints(rectangle);
921
+ return isPointInPolygon(point, points);
922
+ },
923
+ getNearestPoint(rectangle, point) {
924
+ const cornerPoints = getTwoWayArrowPoints(rectangle);
925
+ return getNearestPointBetweenPointAndSegments(point, cornerPoints);
926
+ },
927
+ getEdgeByConnectionPoint(rectangle, pointOfRectangle) {
928
+ const corners = getTwoWayArrowPoints(rectangle);
929
+ const point = RectangleClient.getConnectionPoint(rectangle, pointOfRectangle);
930
+ return getEdgeOnPolygonByPoint(corners, point);
931
+ },
932
+ getConnectorPoints(rectangle) {
933
+ return [
934
+ [rectangle.x, rectangle.y + rectangle.height / 2],
935
+ [rectangle.x + rectangle.width, rectangle.y + rectangle.height / 2]
936
+ ];
937
+ }
938
+ };
939
+ const getTwoWayArrowPoints = (rectangle) => {
940
+ return [
941
+ [rectangle.x, rectangle.y + rectangle.height / 2],
942
+ [rectangle.x + (rectangle.width * 8) / 25, rectangle.y],
943
+ [rectangle.x + (rectangle.width * 8) / 25, rectangle.y + rectangle.height / 5],
944
+ [rectangle.x + (rectangle.width * 17) / 25, rectangle.y + rectangle.height / 5],
945
+ [rectangle.x + (rectangle.width * 17) / 25, rectangle.y],
946
+ [rectangle.x + rectangle.width, rectangle.y + rectangle.height / 2],
947
+ [rectangle.x + (rectangle.width * 17) / 25, rectangle.y + rectangle.height],
948
+ [rectangle.x + (rectangle.width * 17) / 25, rectangle.y + (rectangle.height * 4) / 5],
949
+ [rectangle.x + (rectangle.width * 8) / 25, rectangle.y + (rectangle.height * 4) / 5],
950
+ [rectangle.x + (rectangle.width * 8) / 25, rectangle.y + rectangle.height]
951
+ ];
952
+ };
953
+
343
954
  const ShapeEngineMap = {
344
955
  [GeometryShape.rectangle]: RectangleEngine,
345
956
  [GeometryShape.diamond]: DiamondEngine,
346
957
  [GeometryShape.ellipse]: EllipseEngine,
347
958
  [GeometryShape.parallelogram]: ParallelogramEngine,
348
959
  [GeometryShape.roundRectangle]: RoundRectangleEngine,
349
- [GeometryShape.text]: RectangleEngine
960
+ [GeometryShape.text]: RectangleEngine,
961
+ [GeometryShape.triangle]: TriangleEngine,
962
+ [GeometryShape.leftArrow]: LeftArrowEngine,
963
+ [GeometryShape.trapezoid]: TrapezoidEngine,
964
+ [GeometryShape.rightArrow]: RightArrowEngine,
965
+ [GeometryShape.cross]: CrossEngine,
966
+ [GeometryShape.star]: StarEngine,
967
+ [GeometryShape.pentagon]: PentagonEngine,
968
+ [GeometryShape.hexagon]: HexagonEngine,
969
+ [GeometryShape.octagon]: OctagonEngine,
970
+ [GeometryShape.pentagonArrow]: PentagonArrowEngine,
971
+ [GeometryShape.processArrow]: ProcessArrowEngine,
972
+ [GeometryShape.twoWayArrow]: TwoWayArrowEngine,
973
+ [GeometryShape.comment]: CommentEngine,
974
+ [GeometryShape.roundComment]: RoundCommentEngine
350
975
  };
351
976
  const getEngine = (shape) => {
352
977
  return ShapeEngineMap[shape];
353
978
  };
354
979
 
980
+ const getShape = (value) => {
981
+ if (PlaitDrawElement.isImage(value)) {
982
+ return GeometryShape.rectangle;
983
+ }
984
+ return value.shape;
985
+ };
986
+
355
987
  const createGeometryElement = (shape, points, text, options) => {
356
988
  let textOptions = {};
357
989
  let alignment = Alignment.center;
@@ -393,14 +1025,15 @@ const drawBoundMask = (board, element) => {
393
1025
  const G = createG();
394
1026
  const rectangle = getRectangleByPoints(element.points);
395
1027
  const activeRectangle = RectangleClient.inflate(rectangle, ACTIVE_STROKE_WIDTH);
396
- const maskG = drawGeometry(board, activeRectangle, element.shape, {
1028
+ const shape = getShape(element);
1029
+ const maskG = drawGeometry(board, activeRectangle, shape, {
397
1030
  stroke: SELECTION_BORDER_COLOR,
398
1031
  strokeWidth: 1,
399
1032
  fill: SELECTION_FILL_COLOR,
400
1033
  fillStyle: 'solid'
401
1034
  });
402
1035
  G.appendChild(maskG);
403
- const connectorPoints = getEngine(element.shape).getConnectorPoints(activeRectangle);
1036
+ const connectorPoints = getEngine(shape).getConnectorPoints(activeRectangle);
404
1037
  connectorPoints.forEach(point => {
405
1038
  const circleG = drawCircle(PlaitBoard.getRoughSVG(board), point, 6, {
406
1039
  stroke: '#999999',
@@ -418,7 +1051,8 @@ const drawGeometry = (board, outerRectangle, shape, options) => {
418
1051
  const getNearestPoint = (element, point, inflateDelta = 0) => {
419
1052
  const rectangle = getRectangleByPoints(element.points);
420
1053
  const activeRectangle = RectangleClient.inflate(rectangle, inflateDelta);
421
- return getEngine(element.shape).getNearestPoint(activeRectangle, point);
1054
+ const shape = getShape(element);
1055
+ return getEngine(shape).getNearestPoint(activeRectangle, point);
422
1056
  };
423
1057
  const getCenterPointsOnPolygon = (points) => {
424
1058
  const centerPoint = [];
@@ -428,6 +1062,17 @@ const getCenterPointsOnPolygon = (points) => {
428
1062
  }
429
1063
  return centerPoint;
430
1064
  };
1065
+ const getEdgeOnPolygonByPoint = (corners, point) => {
1066
+ for (let index = 1; index <= corners.length; index++) {
1067
+ let start = corners[index - 1];
1068
+ let end = index === corners.length ? corners[0] : corners[index];
1069
+ const distance = distanceBetweenPointAndSegment(point[0], point[1], start[0], start[1], end[0], end[1]);
1070
+ if (distance < 1) {
1071
+ return [start, end];
1072
+ }
1073
+ }
1074
+ return null;
1075
+ };
431
1076
 
432
1077
  const drawLineArrow = (element, points, options) => {
433
1078
  const arrowG = createG();
@@ -435,16 +1080,19 @@ const drawLineArrow = (element, points, options) => {
435
1080
  return null;
436
1081
  }
437
1082
  if (!PlaitLine.isSourceMark(element, LineMarkerType.none)) {
438
- const sourceArrow = getArrow(element, element.source.marker, points[1], points[0], options);
1083
+ const source = getExtendPoint(points[0], points[1], 24);
1084
+ const sourceArrow = getArrow(element, { marker: element.source.marker, source, target: points[0], isSource: true }, options);
439
1085
  sourceArrow && arrowG.appendChild(sourceArrow);
440
1086
  }
441
1087
  if (!PlaitLine.isTargetMark(element, LineMarkerType.none)) {
442
- const arrow = getArrow(element, element.target.marker, points[points.length - 2], points[points.length - 1], options);
1088
+ const source = getExtendPoint(points[points.length - 1], points[points.length - 2], 24);
1089
+ const arrow = getArrow(element, { marker: element.target.marker, source, target: points[points.length - 1], isSource: false }, options);
443
1090
  arrow && arrowG.appendChild(arrow);
444
1091
  }
445
1092
  return arrowG;
446
1093
  };
447
- const getArrow = (element, marker, source, target, options) => {
1094
+ const getArrow = (element, arrowOptions, options) => {
1095
+ const { marker, source, target, isSource } = arrowOptions;
448
1096
  let targetArrow;
449
1097
  switch (marker) {
450
1098
  case LineMarkerType.openTriangle: {
@@ -459,20 +1107,31 @@ const getArrow = (element, marker, source, target, options) => {
459
1107
  targetArrow = drawArrow(element, source, target, options);
460
1108
  break;
461
1109
  }
462
- case LineMarkerType.sharpArrow: {
463
- targetArrow = drawSharpArrow(source, target, options);
1110
+ case LineMarkerType.sharpArrow: {
1111
+ targetArrow = drawSharpArrow(source, target, options);
1112
+ break;
1113
+ }
1114
+ case LineMarkerType.oneSideUp: {
1115
+ targetArrow = drawOneSideArrow(source, target, 'up', options);
1116
+ break;
1117
+ }
1118
+ case LineMarkerType.oneSideDown: {
1119
+ targetArrow = drawOneSideArrow(source, target, 'down', options);
1120
+ break;
1121
+ }
1122
+ case LineMarkerType.hollowTriangle: {
1123
+ targetArrow = drawHollowTriangleArrow(source, target, options);
1124
+ break;
1125
+ }
1126
+ case LineMarkerType.singleSlash: {
1127
+ targetArrow = drawSingleSlash(source, target, isSource, options);
464
1128
  break;
465
1129
  }
466
1130
  }
467
1131
  return targetArrow;
468
1132
  };
469
1133
  const drawSharpArrow = (source, target, options) => {
470
- const directionFactor = getFactorByPoints(source, target);
471
1134
  const startPoint = target;
472
- // const startPoint: Point = [
473
- // target[0],
474
- // target[1]
475
- // ];
476
1135
  const { pointLeft, pointRight } = arrowPoints(source, target, 12, 20);
477
1136
  const g = createG();
478
1137
  const path = createPath();
@@ -487,10 +1146,7 @@ const drawSharpArrow = (source, target, options) => {
487
1146
  const drawArrow = (element, source, target, options) => {
488
1147
  const directionFactor = getFactorByPoints(source, target);
489
1148
  const strokeWidth = getStrokeWidthByElement(element);
490
- const endPoint = [
491
- target[0] + strokeWidth * directionFactor.x / 2,
492
- target[1] + strokeWidth * directionFactor.y / 2
493
- ];
1149
+ const endPoint = [target[0] + (strokeWidth * directionFactor.x) / 2, target[1] + (strokeWidth * directionFactor.y) / 2];
494
1150
  const middlePoint = [endPoint[0] - 8 * directionFactor.x, endPoint[1] - 8 * directionFactor.y];
495
1151
  const { pointLeft, pointRight } = arrowPoints(source, endPoint, 12, 30);
496
1152
  const arrowG = drawLinearPath([pointLeft, endPoint, pointRight, middlePoint], { ...options, fill: options.stroke }, true);
@@ -506,13 +1162,26 @@ const drawSolidTriangle = (source, target, options) => {
506
1162
  const drawOpenTriangle = (element, source, target, options) => {
507
1163
  const directionFactor = getFactorByPoints(source, target);
508
1164
  const strokeWidth = getStrokeWidthByElement(element);
509
- const endPoint = [
510
- target[0] + strokeWidth * directionFactor.x / 2,
511
- target[1] + strokeWidth * directionFactor.y / 2
512
- ];
1165
+ const endPoint = [target[0] + (strokeWidth * directionFactor.x) / 2, target[1] + (strokeWidth * directionFactor.y) / 2];
513
1166
  const { pointLeft, pointRight } = arrowPoints(source, endPoint, 12, 40);
514
1167
  return drawLinearPath([pointLeft, endPoint, pointRight], options);
515
1168
  };
1169
+ const drawOneSideArrow = (source, target, side, options) => {
1170
+ const { pointLeft, pointRight } = arrowPoints(source, target, 12, 40);
1171
+ return drawLinearPath([side === 'up' ? pointRight : pointLeft, target], options);
1172
+ };
1173
+ const drawSingleSlash = (source, target, isSource, options) => {
1174
+ source = getExtendPoint(target, source, 12);
1175
+ const middlePoint = getExtendPoint(target, source, 6);
1176
+ const angle = isSource ? 120 : 60;
1177
+ const start = rotate(...source, ...middlePoint, (angle * Math.PI) / 180);
1178
+ const end = rotate(...target, ...middlePoint, (angle * Math.PI) / 180);
1179
+ return drawLinearPath([start, end], options);
1180
+ };
1181
+ const drawHollowTriangleArrow = (source, target, options) => {
1182
+ const { pointLeft, pointRight } = arrowPoints(source, target, 12, 30);
1183
+ return drawLinearPath([pointLeft, pointRight, target], { ...options, fill: 'white' }, true);
1184
+ };
516
1185
 
517
1186
  const createLineElement = (shape, points, source, target, options) => {
518
1187
  return {
@@ -528,28 +1197,127 @@ const createLineElement = (shape, points, source, target, options) => {
528
1197
  };
529
1198
  };
530
1199
  const getLinePoints = (board, element) => {
531
- return element.shape === LineShape.elbow ? getElbowPoints(board, element) : getStraightPoints(board, element);
1200
+ switch (element.shape) {
1201
+ case LineShape.elbow: {
1202
+ return getElbowPoints(board, element);
1203
+ }
1204
+ case LineShape.curve: {
1205
+ return getCurvePoints(board, element);
1206
+ }
1207
+ default: {
1208
+ return PlaitLine.getPoints(board, element);
1209
+ }
1210
+ }
532
1211
  };
533
- const getStraightPoints = (board, element) => {
534
- return [getSourcePoint(board, element), getTargetPoint(board, element)];
1212
+ const getLineHandleRefPair = (board, element) => {
1213
+ const strokeWidth = getStrokeWidthByElement(element);
1214
+ const sourceBoundElement = element.source.boundId ? getElementById(board, element.source.boundId) : undefined;
1215
+ const targetBoundElement = element.target.boundId ? getElementById(board, element.target.boundId) : undefined;
1216
+ let sourcePoint = sourceBoundElement ? getConnectionPoint(sourceBoundElement, element.source.connection) : element.points[0];
1217
+ let targetPoint = targetBoundElement
1218
+ ? getConnectionPoint(targetBoundElement, element.target.connection)
1219
+ : element.points[element.points.length - 1];
1220
+ let sourceDirection = getDirectionByVector([targetPoint[0] - sourcePoint[0], targetPoint[1] - sourcePoint[1]]);
1221
+ let targetDirection = getOppositeDirection(sourceDirection);
1222
+ const sourceFactor = getDirectionFactor(sourceDirection);
1223
+ const targetFactor = getDirectionFactor(targetDirection);
1224
+ const sourceHandleRef = {
1225
+ key: LineHandleKey.source,
1226
+ point: sourcePoint,
1227
+ direction: sourceDirection,
1228
+ vector: [sourceFactor.x, sourceFactor.y]
1229
+ };
1230
+ const targetHandleRef = {
1231
+ key: LineHandleKey.target,
1232
+ point: targetPoint,
1233
+ direction: targetDirection,
1234
+ vector: [targetFactor.x, targetFactor.y]
1235
+ };
1236
+ if (sourceBoundElement) {
1237
+ const connectionOffset = PlaitLine.isSourceMarkOrTargetMark(element, LineMarkerType.none, LineHandleKey.source) ? 0 : strokeWidth;
1238
+ const sourceVector = getVectorByConnection(sourceBoundElement, element.source.connection);
1239
+ const direction = getDirectionByVector(sourceVector);
1240
+ sourceDirection = direction ? direction : sourceDirection;
1241
+ sourcePoint = getConnectionPoint(sourceBoundElement, element.source.connection, sourceDirection, connectionOffset);
1242
+ sourceHandleRef.boundElement = sourceBoundElement;
1243
+ sourceHandleRef.direction = sourceDirection;
1244
+ sourceHandleRef.point = sourcePoint;
1245
+ sourceHandleRef.vector = sourceVector;
1246
+ }
1247
+ if (targetBoundElement) {
1248
+ const connectionOffset = PlaitLine.isSourceMarkOrTargetMark(element, LineMarkerType.none, LineHandleKey.target) ? 0 : strokeWidth;
1249
+ const targetVector = getVectorByConnection(targetBoundElement, element.target.connection);
1250
+ const direction = getDirectionByVector(targetVector);
1251
+ targetDirection = direction ? direction : targetDirection;
1252
+ targetPoint = getConnectionPoint(targetBoundElement, element.target.connection, targetDirection, connectionOffset);
1253
+ targetHandleRef.boundElement = targetBoundElement;
1254
+ targetHandleRef.direction = targetDirection;
1255
+ targetHandleRef.point = targetPoint;
1256
+ targetHandleRef.vector = targetVector;
1257
+ }
1258
+ return { source: sourceHandleRef, target: targetHandleRef };
535
1259
  };
536
1260
  const getElbowPoints = (board, element) => {
537
1261
  if (element.points.length === 2) {
538
- const source = getSourcePoint(board, element);
539
- const target = getTargetPoint(board, element);
540
- let sourceDirection = source[0] < target[0] ? Direction.right : Direction.left;
541
- let targetDirection = source[0] < target[0] ? Direction.left : Direction.right;
542
- if (element.source.connection) {
543
- sourceDirection = getDirectionByPoint(element.source.connection, sourceDirection);
544
- }
545
- if (element.target.connection) {
546
- targetDirection = getDirectionByPoint(element.target.connection, targetDirection);
547
- }
548
- const points = getPoints(source, sourceDirection, target, targetDirection, 30);
1262
+ const handleRefPair = getLineHandleRefPair(board, element);
1263
+ const offset = element.source.boundId || element.target.boundId ? 30 : 0;
1264
+ let points = getPoints(handleRefPair.source.point, handleRefPair.source.direction, handleRefPair.target.point, handleRefPair.target.direction, offset);
1265
+ points = removeDuplicatePoints(points);
549
1266
  return points;
550
1267
  }
551
1268
  return element.points;
552
1269
  };
1270
+ const getCurvePoints = (board, element) => {
1271
+ if (element.points.length === 2) {
1272
+ const handleRefPair = getLineHandleRefPair(board, element);
1273
+ const { source, target } = handleRefPair;
1274
+ const sourceBoundElement = handleRefPair.source.boundElement;
1275
+ const targetBoundElement = handleRefPair.target.boundElement;
1276
+ let curvePoints = [source.point];
1277
+ const sumDistance = distanceBetweenPointAndPoint(...source.point, ...target.point);
1278
+ const offset = 12 + sumDistance / 3;
1279
+ if (sourceBoundElement) {
1280
+ curvePoints.push(getPointByVector(source.point, source.vector, offset));
1281
+ }
1282
+ if (targetBoundElement) {
1283
+ curvePoints.push(getPointByVector(target.point, target.vector, offset));
1284
+ }
1285
+ const isSingleBound = (sourceBoundElement && !targetBoundElement) || (!sourceBoundElement && targetBoundElement);
1286
+ if (isSingleBound) {
1287
+ curvePoints.push(target.point);
1288
+ const points = Q2C(curvePoints);
1289
+ return pointsOnBezierCurves(points);
1290
+ }
1291
+ if (!sourceBoundElement && !targetBoundElement) {
1292
+ curvePoints.push(getPointByVector(source.point, source.vector, offset));
1293
+ curvePoints.push(getPointByVector(target.point, target.vector, offset));
1294
+ }
1295
+ curvePoints.push(target.point);
1296
+ return pointsOnBezierCurves(curvePoints);
1297
+ }
1298
+ else {
1299
+ //TODO 直接获取贝塞尔曲线上高密度点
1300
+ const points = PlaitLine.getPoints(board, element);
1301
+ const draw = PlaitBoard.getRoughSVG(board).generator.curve(points);
1302
+ let bezierPoints = transformOpsToPoints(draw.sets[0].ops);
1303
+ bezierPoints = removeDuplicatePoints(bezierPoints);
1304
+ return pointsOnBezierCurves(bezierPoints);
1305
+ }
1306
+ };
1307
+ const transformOpsToPoints = (ops) => {
1308
+ const result = [];
1309
+ for (let item of ops) {
1310
+ if (item.op === 'move') {
1311
+ result.push([item.data[0], item.data[1]]);
1312
+ }
1313
+ else {
1314
+ result.push([item.data[0], item.data[1]]);
1315
+ result.push([item.data[2], item.data[3]]);
1316
+ result.push([item.data[4], item.data[5]]);
1317
+ }
1318
+ }
1319
+ return result;
1320
+ };
553
1321
  const isHitPolyLine = (pathPoints, point, strokeWidth, expand = 0) => {
554
1322
  const distance = distanceBetweenPointAndSegments(pathPoints, point);
555
1323
  return distance <= strokeWidth + expand;
@@ -579,20 +1347,28 @@ const drawLine = (board, element) => {
579
1347
  const strokeLineDash = getLineDashByElement(element);
580
1348
  const options = { stroke: strokeColor, strokeWidth, strokeLineDash };
581
1349
  const lineG = createG();
582
- const points = getLinePoints(board, element);
583
- const line = drawLinearPath(points, options);
584
- line.setAttribute('mask', `url(#${element.id})`);
1350
+ let points = getLinePoints(board, element);
1351
+ let line;
1352
+ if (element.shape === LineShape.curve) {
1353
+ //TODO element.points 应为曲线拐点
1354
+ line = PlaitBoard.getRoughSVG(board).curve(points, options);
1355
+ }
1356
+ else {
1357
+ line = drawLinearPath(points, options);
1358
+ }
1359
+ const id = idCreator();
1360
+ line.setAttribute('mask', `url(#${id})`);
585
1361
  lineG.appendChild(line);
586
- const { mask, maskTargetFillRect } = drawMask(board, element);
1362
+ const { mask, maskTargetFillRect } = drawMask(board, element, id);
587
1363
  lineG.appendChild(mask);
588
1364
  line.appendChild(maskTargetFillRect);
589
1365
  const arrow = drawLineArrow(element, points, { stroke: strokeColor, strokeWidth });
590
1366
  arrow && lineG.appendChild(arrow);
591
1367
  return lineG;
592
1368
  };
593
- function drawMask(board, element) {
1369
+ function drawMask(board, element, id) {
594
1370
  const mask = createMask();
595
- mask.setAttribute('id', element.id);
1371
+ mask.setAttribute('id', id);
596
1372
  const points = getLinePoints(board, element);
597
1373
  let rectangle = getRectangleByPoints(points);
598
1374
  rectangle = RectangleClient.getOutlineRectangle(rectangle, -30);
@@ -613,33 +1389,16 @@ function drawMask(board, element) {
613
1389
  maskTargetFillRect.setAttribute('opacity', '0');
614
1390
  return { mask, maskTargetFillRect };
615
1391
  }
616
- const getSourcePoint = (board, element) => {
617
- if (element.source.boundId) {
618
- const strokeWidth = getStrokeWidthByElement(element);
619
- const connectionOffset = PlaitLine.isSourceMark(element, LineMarkerType.none) ? 0 : strokeWidth;
620
- const boundElement = getElementById(board, element.source.boundId);
621
- return boundElement ? getConnectionPoint(boundElement, element.source.connection, connectionOffset) : element.points[0];
1392
+ const getConnectionPoint = (geometry, connection, direction, delta) => {
1393
+ const rectangle = getRectangleByPoints(geometry.points);
1394
+ if (direction && delta) {
1395
+ const directionFactor = getDirectionFactor(direction);
1396
+ const point = RectangleClient.getConnectionPoint(rectangle, connection);
1397
+ return [point[0] + directionFactor.x * delta, point[1] + directionFactor.y * delta];
622
1398
  }
623
- return element.points[0];
624
- };
625
- const getTargetPoint = (board, element) => {
626
- if (element.target.boundId) {
627
- const strokeWidth = getStrokeWidthByElement(element);
628
- const connectionOffset = PlaitLine.isTargetMark(element, LineMarkerType.none) ? 0 : strokeWidth;
629
- const boundElement = getElementById(board, element.target.boundId);
630
- return boundElement
631
- ? getConnectionPoint(boundElement, element.target.connection, connectionOffset)
632
- : element.points[element.points.length - 1];
1399
+ else {
1400
+ return RectangleClient.getConnectionPoint(rectangle, connection);
633
1401
  }
634
- return element.points[element.points.length - 1];
635
- };
636
- const getConnectionPoint = (geometry, connection, offset) => {
637
- const rectangle = getRectangleByPoints(geometry.points);
638
- const directionFactor = getDirectionFactor(getDirectionByPoint(connection, Direction.bottom));
639
- return [
640
- rectangle.x + rectangle.width * connection[0] + directionFactor.x * offset,
641
- rectangle.y + rectangle.height * connection[1] + directionFactor.y * offset
642
- ];
643
1402
  };
644
1403
  const transformPointToConnection = (board, point, hitElement) => {
645
1404
  let rectangle = getRectangleByPoints(hitElement.points);
@@ -650,7 +1409,8 @@ const transformPointToConnection = (board, point, hitElement) => {
650
1409
  return [(nearestPoint[0] - rectangle.x) / rectangle.width, (nearestPoint[1] - rectangle.y) / rectangle.height];
651
1410
  };
652
1411
  const getHitConnectorPoint = (movingPoint, hitElement, rectangle) => {
653
- const connector = getEngine(hitElement.shape).getConnectorPoints(rectangle);
1412
+ const shape = getShape(hitElement);
1413
+ const connector = getEngine(shape).getConnectorPoints(rectangle);
654
1414
  const points = getPointsByCenterPoint(movingPoint, 5, 5);
655
1415
  const pointRectangle = getRectangleByPoints(points);
656
1416
  return connector.find(point => {
@@ -674,6 +1434,64 @@ const getBoardLines = (board) => {
674
1434
  recursion: (element) => PlaitDrawElement.isDrawElement(element)
675
1435
  });
676
1436
  };
1437
+ const removeDuplicatePoints = (points) => {
1438
+ const newArray = [];
1439
+ points.forEach(point => {
1440
+ const index = newArray.findIndex(otherPoint => {
1441
+ return point[0] === otherPoint[0] && point[1] === otherPoint[1];
1442
+ });
1443
+ if (index === -1)
1444
+ newArray.push(point);
1445
+ });
1446
+ return newArray;
1447
+ };
1448
+ const getExtendPoint = (source, target, extendDistance) => {
1449
+ const distance = distanceBetweenPointAndPoint(...source, ...target);
1450
+ const sin = (target[1] - source[1]) / distance;
1451
+ const cos = (target[0] - source[0]) / distance;
1452
+ return [source[0] + extendDistance * cos, source[1] + extendDistance * sin];
1453
+ };
1454
+ // quadratic Bezier to cubic Bezier
1455
+ const Q2C = (points) => {
1456
+ const result = [];
1457
+ const numSegments = points.length / 3;
1458
+ for (let i = 0; i < numSegments; i++) {
1459
+ const start = points[i];
1460
+ const qControl = points[i + 1];
1461
+ const end = points[i + 2];
1462
+ const startDistance = distanceBetweenPointAndPoint(...start, ...qControl);
1463
+ const endDistance = distanceBetweenPointAndPoint(...end, ...qControl);
1464
+ const cControl1 = getExtendPoint(start, qControl, (startDistance * 2) / 3);
1465
+ const cControl2 = getExtendPoint(end, qControl, (endDistance * 2) / 3);
1466
+ result.push(start, cControl1, cControl2, end);
1467
+ }
1468
+ return result;
1469
+ };
1470
+ const getVectorByConnection = (boundElement, connection) => {
1471
+ const rectangle = getRectangleByPoints(boundElement.points);
1472
+ const shape = getShape(boundElement);
1473
+ const engine = getEngine(shape);
1474
+ let vector = [0, 0];
1475
+ const direction = getDirectionByPointOfRectangle(connection);
1476
+ if (direction) {
1477
+ const factor = getDirectionFactor(direction);
1478
+ return [factor.x, factor.y];
1479
+ }
1480
+ if (engine.getEdgeByConnectionPoint) {
1481
+ const edge = engine.getEdgeByConnectionPoint(rectangle, connection);
1482
+ if (edge) {
1483
+ const lineVector = [edge[1][0] - edge[0][0], edge[1][1] - edge[0][1]];
1484
+ return rotateVectorAnti90(lineVector);
1485
+ }
1486
+ }
1487
+ if (engine.getTangentVectorByConnectionPoint) {
1488
+ const lineVector = engine.getTangentVectorByConnectionPoint(rectangle, connection);
1489
+ if (lineVector) {
1490
+ return rotateVectorAnti90(lineVector);
1491
+ }
1492
+ }
1493
+ return vector;
1494
+ };
677
1495
 
678
1496
  const getSelectedDrawElements = (board) => {
679
1497
  const selectedElements = getSelectedElements(board).filter(value => PlaitDrawElement.isDrawElement(value));
@@ -687,6 +1505,111 @@ const getSelectedLineElements = (board) => {
687
1505
  const selectedElements = getSelectedElements(board).filter(value => PlaitDrawElement.isLine(value));
688
1506
  return selectedElements;
689
1507
  };
1508
+ const getSelectedImageElements = (board) => {
1509
+ const selectedElements = getSelectedElements(board).filter(value => PlaitDrawElement.isImage(value));
1510
+ return selectedElements;
1511
+ };
1512
+
1513
+ var LineMarkerType;
1514
+ (function (LineMarkerType) {
1515
+ LineMarkerType["arrow"] = "arrow";
1516
+ LineMarkerType["none"] = "none";
1517
+ LineMarkerType["openTriangle"] = "open-triangle";
1518
+ LineMarkerType["solidTriangle"] = "solid-triangle";
1519
+ LineMarkerType["sharpArrow"] = "sharp-arrow";
1520
+ LineMarkerType["oneSideUp"] = "one-side-up";
1521
+ LineMarkerType["oneSideDown"] = "one-side-down";
1522
+ LineMarkerType["hollowTriangle"] = "hollow-triangle";
1523
+ LineMarkerType["singleSlash"] = "single-slash";
1524
+ })(LineMarkerType || (LineMarkerType = {}));
1525
+ var LineShape;
1526
+ (function (LineShape) {
1527
+ LineShape["straight"] = "straight";
1528
+ LineShape["curve"] = "curve";
1529
+ LineShape["elbow"] = "elbow";
1530
+ })(LineShape || (LineShape = {}));
1531
+ var LineHandleKey;
1532
+ (function (LineHandleKey) {
1533
+ LineHandleKey["source"] = "source";
1534
+ LineHandleKey["target"] = "target";
1535
+ })(LineHandleKey || (LineHandleKey = {}));
1536
+ const PlaitLine = {
1537
+ getTextEditors(element) {
1538
+ const component = PlaitElement.getComponent(element);
1539
+ if (component) {
1540
+ const manage = component.textManages.find(manage => manage.isEditing);
1541
+ if (manage) {
1542
+ return [manage.componentRef.instance.editor];
1543
+ }
1544
+ else {
1545
+ return component.textManages.map(manage => manage.componentRef.instance.editor);
1546
+ }
1547
+ }
1548
+ throw new Error('can not get correctly component in get text editor');
1549
+ },
1550
+ isSourceMarkOrTargetMark(line, markType, handleKey) {
1551
+ if (handleKey === LineHandleKey.source) {
1552
+ return line.source.marker === markType;
1553
+ }
1554
+ else {
1555
+ return line.target.marker === markType;
1556
+ }
1557
+ },
1558
+ isSourceMark(line, markType) {
1559
+ return PlaitLine.isSourceMarkOrTargetMark(line, markType, LineHandleKey.source);
1560
+ },
1561
+ isTargetMark(line, markType) {
1562
+ return PlaitLine.isSourceMarkOrTargetMark(line, markType, LineHandleKey.target);
1563
+ },
1564
+ isBoundElementOfSource(line, element) {
1565
+ return line.source.boundId === element.id;
1566
+ },
1567
+ isBoundElementOfTarget(line, element) {
1568
+ return line.target.boundId === element.id;
1569
+ },
1570
+ getPoints(board, line) {
1571
+ let sourcePoint = line.source.boundId
1572
+ ? getConnectionPoint(getElementById(board, line.source.boundId), line.source.connection)
1573
+ : line.points[0];
1574
+ let targetPoint = line.target.boundId
1575
+ ? getConnectionPoint(getElementById(board, line.target.boundId), line.target.connection)
1576
+ : line.points[line.points.length - 1];
1577
+ const restPoints = line.points.length > 2 ? line.points.slice(1, line.points.length - 1) : [];
1578
+ return [sourcePoint, ...restPoints, targetPoint];
1579
+ }
1580
+ };
1581
+
1582
+ var StrokeStyle;
1583
+ (function (StrokeStyle) {
1584
+ StrokeStyle["solid"] = "solid";
1585
+ StrokeStyle["dashed"] = "dashed";
1586
+ })(StrokeStyle || (StrokeStyle = {}));
1587
+
1588
+ const PlaitDrawElement = {
1589
+ isGeometry: (value) => {
1590
+ return value.type === 'geometry';
1591
+ },
1592
+ isLine: (value) => {
1593
+ return value.type === 'line';
1594
+ },
1595
+ isText: (value) => {
1596
+ return value.type === 'geometry' && value.shape === GeometryShape.text;
1597
+ },
1598
+ isImage: (value) => {
1599
+ return value.type === 'image';
1600
+ },
1601
+ isDrawElement: (value) => {
1602
+ if (PlaitDrawElement.isGeometry(value) || PlaitDrawElement.isLine(value) || PlaitDrawElement.isImage(value)) {
1603
+ return true;
1604
+ }
1605
+ else {
1606
+ return false;
1607
+ }
1608
+ },
1609
+ isShape: (value) => {
1610
+ return PlaitDrawElement.isImage(value) || PlaitDrawElement.isGeometry(value);
1611
+ }
1612
+ };
690
1613
 
691
1614
  class GeometryShapeGenerator extends Generator {
692
1615
  canDraw(element, data) {
@@ -702,7 +1625,12 @@ class GeometryShapeGenerator extends Generator {
702
1625
  const strokeColor = getStrokeColorByElement(element);
703
1626
  const fill = getFillByElement(element);
704
1627
  const strokeLineDash = getLineDashByElement(element);
705
- return drawGeometry(this.board, RectangleClient.inflate(rectangle, -strokeWidth), shape, { stroke: strokeColor, strokeWidth, fill, strokeLineDash });
1628
+ return drawGeometry(this.board, RectangleClient.inflate(rectangle, -strokeWidth), shape, {
1629
+ stroke: strokeColor,
1630
+ strokeWidth,
1631
+ fill,
1632
+ strokeLineDash
1633
+ });
706
1634
  }
707
1635
  }
708
1636
 
@@ -782,6 +1710,28 @@ const setTextSize = (board, element, textWidth, textHeight) => {
782
1710
  }
783
1711
  };
784
1712
 
1713
+ const insertImage = (board, imageItem, startPoint) => {
1714
+ const { width, height, url } = imageItem;
1715
+ const host = BOARD_TO_HOST.get(board);
1716
+ const viewportWidth = PlaitBoard.getComponent(board).nativeElement.clientWidth;
1717
+ const viewportHeight = PlaitBoard.getComponent(board).nativeElement.clientHeight;
1718
+ const point = transformPoint(board, toPoint(viewportWidth / 2, viewportHeight / 2, host));
1719
+ const points = startPoint
1720
+ ? [startPoint, [startPoint[0] + width, startPoint[1] + height]]
1721
+ : [
1722
+ [point[0] - width / 2, point[1] - height / 2],
1723
+ [point[0] + width / 2, point[1] + height / 2]
1724
+ ];
1725
+ const imageElement = {
1726
+ id: idCreator(),
1727
+ type: 'image',
1728
+ points,
1729
+ url
1730
+ };
1731
+ Transforms.insertNode(board, imageElement, [board.children.length]);
1732
+ Transforms.addSelectionWithTemporaryElements(board, [imageElement]);
1733
+ };
1734
+
785
1735
  const resizeLine = (board, options, path) => {
786
1736
  Transforms.setNode(board, options, path);
787
1737
  };
@@ -812,7 +1762,8 @@ const DrawTransforms = {
812
1762
  resizeLine,
813
1763
  setLineTexts,
814
1764
  removeLineText,
815
- setLineMark
1765
+ setLineMark,
1766
+ insertImage
816
1767
  };
817
1768
 
818
1769
  class GeometryComponent extends CommonPluginElement {
@@ -830,10 +1781,10 @@ class GeometryComponent extends CommonPluginElement {
830
1781
  getStrokeWidth: () => {
831
1782
  const selectedElements = getSelectedElements(this.board);
832
1783
  if (selectedElements.length === 1 && !isSelectionMoving(this.board)) {
833
- return DefaultGeometryActiveStyle.strokeWidth;
1784
+ return ACTIVE_STROKE_WIDTH;
834
1785
  }
835
1786
  else {
836
- return DefaultGeometryActiveStyle.selectionStrokeWidth;
1787
+ return ACTIVE_STROKE_WIDTH;
837
1788
  }
838
1789
  },
839
1790
  getStrokeOpacity: () => {
@@ -896,6 +1847,10 @@ class GeometryComponent extends CommonPluginElement {
896
1847
  const plugins = this.board.getPluginOptions(WithTextPluginKey).textPlugins;
897
1848
  const manage = new TextManage(this.board, this.viewContainerRef, {
898
1849
  getRectangle: () => {
1850
+ const getRectangle = getEngine(this.element.shape).getTextRectangle;
1851
+ if (getRectangle) {
1852
+ return getRectangle(this.element);
1853
+ }
899
1854
  return getTextRectangle(this.element);
900
1855
  },
901
1856
  onValueChangeHandle: (textManageRef) => {
@@ -923,14 +1878,15 @@ class GeometryComponent extends CommonPluginElement {
923
1878
  this.destroy$.complete();
924
1879
  }
925
1880
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: GeometryComponent, deps: [{ token: i0.ViewContainerRef }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
926
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.3", type: GeometryComponent, selector: "plait-draw-geometry", usesInheritance: true, ngImport: i0, template: ``, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1881
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.3", type: GeometryComponent, isStandalone: true, selector: "plait-draw-geometry", usesInheritance: true, ngImport: i0, template: ``, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
927
1882
  }
928
1883
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: GeometryComponent, decorators: [{
929
1884
  type: Component,
930
1885
  args: [{
931
1886
  selector: 'plait-draw-geometry',
932
1887
  template: ``,
933
- changeDetection: ChangeDetectionStrategy.OnPush
1888
+ changeDetection: ChangeDetectionStrategy.OnPush,
1889
+ standalone: true
934
1890
  }]
935
1891
  }], ctorParameters: function () { return [{ type: i0.ViewContainerRef }, { type: i0.ChangeDetectorRef }]; } });
936
1892
 
@@ -939,21 +1895,17 @@ class LineShapeGenerator extends Generator {
939
1895
  return true;
940
1896
  }
941
1897
  baseDraw(element, data) {
942
- const shape = element.shape;
943
1898
  let lineG;
944
- switch (shape) {
945
- case LineShape.elbow:
946
- case LineShape.straight:
947
- lineG = drawLine(this.board, element);
948
- break;
949
- default:
950
- break;
951
- }
1899
+ lineG = drawLine(this.board, element);
952
1900
  return lineG;
953
1901
  }
954
1902
  }
955
1903
 
956
1904
  class LineActiveGenerator extends Generator {
1905
+ constructor() {
1906
+ super(...arguments);
1907
+ this.hasResizeHandle = false;
1908
+ }
957
1909
  canDraw(element, data) {
958
1910
  if (data.selected) {
959
1911
  return true;
@@ -964,27 +1916,70 @@ class LineActiveGenerator extends Generator {
964
1916
  }
965
1917
  baseDraw(element, data) {
966
1918
  const activeG = createG();
967
- activeG.classList.add('active');
968
- activeG.classList.add('line-handle');
969
- const sourcePoint = getSourcePoint(this.board, element);
970
- const targetPoint = getTargetPoint(this.board, element);
971
- const sourceCircle = drawCircle(PlaitBoard.getRoughSVG(this.board), sourcePoint, RESIZE_HANDLE_DIAMETER, {
972
- stroke: '#999999',
973
- strokeWidth: 1,
974
- fill: '#FFF',
975
- fillStyle: 'solid'
976
- });
977
- const targetCircle = drawCircle(PlaitBoard.getRoughSVG(this.board), targetPoint, RESIZE_HANDLE_DIAMETER, {
978
- stroke: '#999999',
979
- strokeWidth: 1,
980
- fill: '#FFF',
981
- fillStyle: 'solid'
982
- });
983
- activeG.appendChild(targetCircle);
984
- activeG.appendChild(sourceCircle);
1919
+ if (this.hasResizeHandle) {
1920
+ activeG.classList.add('active');
1921
+ activeG.classList.add('line-handle');
1922
+ const points = PlaitLine.getPoints(this.board, element);
1923
+ points.forEach(point => {
1924
+ const circle = drawCircle(PlaitBoard.getRoughSVG(this.board), point, RESIZE_HANDLE_DIAMETER, {
1925
+ stroke: '#999999',
1926
+ strokeWidth: 1,
1927
+ fill: '#FFF',
1928
+ fillStyle: 'solid'
1929
+ });
1930
+ activeG.appendChild(circle);
1931
+ });
1932
+ getMiddlePoints(this.board, element).forEach(point => {
1933
+ const circle = drawCircle(PlaitBoard.getRoughSVG(this.board), point, RESIZE_HANDLE_DIAMETER, {
1934
+ stroke: '#FFFFFF80',
1935
+ strokeWidth: 1,
1936
+ fill: `${PRIMARY_COLOR}80`,
1937
+ fillStyle: 'solid'
1938
+ });
1939
+ activeG.appendChild(circle);
1940
+ });
1941
+ }
1942
+ else {
1943
+ const points = getLinePoints(this.board, element);
1944
+ const activeRectangle = getRectangleByPoints(points);
1945
+ const strokeG = drawRectangle(this.board, activeRectangle, {
1946
+ stroke: PRIMARY_COLOR,
1947
+ strokeWidth: DefaultGeometryActiveStyle.selectionStrokeWidth
1948
+ });
1949
+ activeG.appendChild(strokeG);
1950
+ }
985
1951
  return activeG;
986
1952
  }
987
1953
  }
1954
+ function getMiddlePoints(board, element) {
1955
+ const result = [];
1956
+ const shape = element.shape;
1957
+ if (shape === LineShape.straight) {
1958
+ const points = PlaitLine.getPoints(board, element);
1959
+ for (let i = 0; i < points.length - 1; i++) {
1960
+ result.push([(points[i][0] + points[i + 1][0]) / 2, (points[i][1] + points[i + 1][1]) / 2]);
1961
+ }
1962
+ }
1963
+ if (shape === LineShape.curve) {
1964
+ const points = PlaitLine.getPoints(board, element);
1965
+ const pointsOnBezier = getCurvePoints(board, element);
1966
+ if (points.length === 2) {
1967
+ const start = 0;
1968
+ const endIndex = pointsOnBezier.length - 1;
1969
+ const middleIndex = Math.round((start + endIndex) / 2);
1970
+ result.push(pointsOnBezier[middleIndex]);
1971
+ }
1972
+ else {
1973
+ for (let i = 0; i < points.length - 1; i++) {
1974
+ const startIndex = pointsOnBezier.findIndex(point => point[0] === points[i][0] && point[1] === points[i][1]);
1975
+ const endIndex = pointsOnBezier.findIndex(point => point[0] === points[i + 1][0] && point[1] === points[i + 1][1]);
1976
+ const middleIndex = Math.round((startIndex + endIndex) / 2);
1977
+ result.push(pointsOnBezier[middleIndex]);
1978
+ }
1979
+ }
1980
+ }
1981
+ return result;
1982
+ }
988
1983
 
989
1984
  class LineComponent extends PlaitPluginElementComponent {
990
1985
  constructor(viewContainerRef, cdr) {
@@ -1040,11 +2035,18 @@ class LineComponent extends PlaitPluginElementComponent {
1040
2035
  this.updateTextRectangle();
1041
2036
  return;
1042
2037
  }
1043
- const hasSameSelected = value.selected === previous.selected;
1044
- if (!hasSameSelected) {
2038
+ if (!isSelectionMoving(this.board)) {
2039
+ this.activeGenerator.hasResizeHandle = this.hasResizeHandle();
1045
2040
  this.activeGenerator.draw(this.element, PlaitBoard.getElementActiveHost(this.board), { selected: this.selected });
1046
2041
  }
1047
2042
  }
2043
+ hasResizeHandle() {
2044
+ const selectedElements = getSelectedElements(this.board);
2045
+ if (PlaitBoard.hasBeenTextEditing(this.board) && PlaitDrawElement.isText(this.element)) {
2046
+ return false;
2047
+ }
2048
+ return selectedElements.length === 1 && !isSelectionMoving(this.board);
2049
+ }
1048
2050
  initializeTextManages() {
1049
2051
  if (this.element.texts?.length) {
1050
2052
  this.element.texts.forEach((text, index) => {
@@ -1117,14 +2119,15 @@ class LineComponent extends PlaitPluginElementComponent {
1117
2119
  this.destroy$.complete();
1118
2120
  }
1119
2121
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: LineComponent, deps: [{ token: i0.ViewContainerRef }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
1120
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.3", type: LineComponent, selector: "plait-draw-line", usesInheritance: true, ngImport: i0, template: ``, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2122
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.3", type: LineComponent, isStandalone: true, selector: "plait-draw-line", usesInheritance: true, ngImport: i0, template: ``, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1121
2123
  }
1122
2124
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: LineComponent, decorators: [{
1123
2125
  type: Component,
1124
2126
  args: [{
1125
2127
  selector: 'plait-draw-line',
1126
2128
  template: ``,
1127
- changeDetection: ChangeDetectionStrategy.OnPush
2129
+ changeDetection: ChangeDetectionStrategy.OnPush,
2130
+ standalone: true
1128
2131
  }]
1129
2132
  }], ctorParameters: function () { return [{ type: i0.ViewContainerRef }, { type: i0.ChangeDetectorRef }]; } });
1130
2133
 
@@ -1164,13 +2167,14 @@ const withGeometryCreateByDrag = (board) => {
1164
2167
  geometryShapeG?.remove();
1165
2168
  geometryShapeG = createG();
1166
2169
  const geometryGenerator = new GeometryShapeGenerator(board);
1167
- const isGeometryPointer = PlaitBoard.isInPointer(board, GeometryPointer);
2170
+ const geometryPointers = getGeometryPointers();
2171
+ const isGeometryPointer = PlaitBoard.isInPointer(board, geometryPointers);
1168
2172
  const dragMode = isGeometryPointer && isDndMode(board);
1169
2173
  const movingPoint = transformPoint(board, toPoint(event.x, event.y, PlaitBoard.getHost(board)));
1170
2174
  const pointer = PlaitBoard.getPointer(board);
1171
2175
  if (dragMode) {
1172
2176
  const points = getDefaultGeometryPoints(pointer, movingPoint);
1173
- if (pointer === DrawPointerType.text) {
2177
+ if (pointer === GeometryShape.text) {
1174
2178
  const textG = getTemporaryTextG(movingPoint);
1175
2179
  geometryShapeG.appendChild(textG);
1176
2180
  PlaitBoard.getElementActiveHost(board).append(geometryShapeG);
@@ -1188,12 +2192,13 @@ const withGeometryCreateByDrag = (board) => {
1188
2192
  };
1189
2193
  board.pointerUp = (event) => {
1190
2194
  const pointer = PlaitBoard.getPointer(board);
1191
- const isGeometryPointer = PlaitBoard.isInPointer(board, GeometryPointer);
2195
+ const geometryPointers = getGeometryPointers();
2196
+ const isGeometryPointer = PlaitBoard.isInPointer(board, geometryPointers);
1192
2197
  const dragMode = isGeometryPointer && isDndMode(board);
1193
2198
  if (dragMode) {
1194
2199
  const targetPoint = transformPoint(board, toPoint(event.x, event.y, PlaitBoard.getHost(board)));
1195
2200
  const points = getDefaultGeometryPoints(pointer, targetPoint);
1196
- if (pointer === DrawPointerType.text) {
2201
+ if (pointer === GeometryShape.text) {
1197
2202
  DrawTransforms.insertText(board, points);
1198
2203
  }
1199
2204
  else {
@@ -1208,7 +2213,7 @@ const withGeometryCreateByDrag = (board) => {
1208
2213
  };
1209
2214
  return board;
1210
2215
  };
1211
- const withGeometryCreateByDraw = (board) => {
2216
+ const withGeometryCreateByDrawing = (board) => {
1212
2217
  const { pointerDown, pointerMove, pointerUp, keydown, keyup } = board;
1213
2218
  let start = null;
1214
2219
  let geometryShapeG = null;
@@ -1223,13 +2228,14 @@ const withGeometryCreateByDraw = (board) => {
1223
2228
  keyup(event);
1224
2229
  };
1225
2230
  board.pointerDown = (event) => {
1226
- const isGeometryPointer = PlaitBoard.isInPointer(board, GeometryPointer);
2231
+ const geometryPointers = getGeometryPointers();
2232
+ const isGeometryPointer = PlaitBoard.isInPointer(board, geometryPointers);
1227
2233
  if (isGeometryPointer && isDrawingMode(board)) {
1228
2234
  const point = transformPoint(board, toPoint(event.x, event.y, PlaitBoard.getHost(board)));
1229
2235
  start = point;
1230
2236
  const pointer = PlaitBoard.getPointer(board);
1231
2237
  preventTouchMove(board, event, true);
1232
- if (pointer === DrawPointerType.text) {
2238
+ if (pointer === GeometryShape.text) {
1233
2239
  const points = getDefaultGeometryPoints(pointer, point);
1234
2240
  const textElement = createGeometryElement(GeometryShape.text, points, DefaultTextProperty.text);
1235
2241
  Transforms.insertNode(board, textElement, [board.children.length]);
@@ -1248,7 +2254,7 @@ const withGeometryCreateByDraw = (board) => {
1248
2254
  const drawMode = !!start;
1249
2255
  const movingPoint = transformPoint(board, toPoint(event.x, event.y, PlaitBoard.getHost(board)));
1250
2256
  const pointer = PlaitBoard.getPointer(board);
1251
- if (drawMode && pointer !== DrawPointerType.text) {
2257
+ if (drawMode && pointer !== GeometryShape.text) {
1252
2258
  const points = normalizeShapePoints([start, movingPoint], isShift);
1253
2259
  temporaryElement = createGeometryElement(pointer, points, '', {
1254
2260
  strokeColor: DefaultGeometryProperty.strokeColor,
@@ -1267,7 +2273,7 @@ const withGeometryCreateByDraw = (board) => {
1267
2273
  if (Math.hypot(width, height) === 0) {
1268
2274
  const pointer = PlaitBoard.getPointer(board);
1269
2275
  const points = getDefaultGeometryPoints(pointer, targetPoint);
1270
- if (pointer !== DrawPointerType.text) {
2276
+ if (pointer !== GeometryShape.text) {
1271
2277
  temporaryElement = createGeometryElement(pointer, points, '', {
1272
2278
  strokeColor: DefaultGeometryProperty.strokeColor,
1273
2279
  strokeWidth: DefaultGeometryProperty.strokeWidth
@@ -1291,7 +2297,7 @@ const withGeometryCreateByDraw = (board) => {
1291
2297
  return board;
1292
2298
  };
1293
2299
  const getDefaultGeometryPoints = (pointer, targetPoint) => {
1294
- return pointer === DrawPointerType.text
2300
+ return pointer === GeometryShape.text
1295
2301
  ? getPointsByCenterPoint(targetPoint, DefaultTextProperty.width, DefaultTextProperty.height)
1296
2302
  : getPointsByCenterPoint(targetPoint, DefaultGeometryProperty.width, DefaultGeometryProperty.height);
1297
2303
  };
@@ -1310,7 +2316,7 @@ const getTemporaryTextG = (movingPoint) => {
1310
2316
 
1311
2317
  const buildClipboardData = (board, elements, startPoint) => {
1312
2318
  return elements.map(element => {
1313
- if (PlaitDrawElement.isGeometry(element)) {
2319
+ if (PlaitDrawElement.isGeometry(element) || PlaitDrawElement.isImage(element)) {
1314
2320
  const points = element.points.map(point => [point[0] - startPoint[0], point[1] - startPoint[1]]);
1315
2321
  return { ...element, points };
1316
2322
  }
@@ -1333,7 +2339,7 @@ const buildClipboardData = (board, elements, startPoint) => {
1333
2339
  };
1334
2340
  const insertClipboardData = (board, elements, startPoint) => {
1335
2341
  const lines = elements.filter(value => PlaitDrawElement.isLine(value));
1336
- const geometries = elements.filter(value => PlaitDrawElement.isGeometry(value));
2342
+ const geometries = elements.filter(value => PlaitDrawElement.isGeometry(value) || PlaitDrawElement.isImage(value));
1337
2343
  geometries.forEach(element => {
1338
2344
  const sourceLines = [];
1339
2345
  const targetLines = [];
@@ -1357,6 +2363,7 @@ const insertClipboardData = (board, elements, startPoint) => {
1357
2363
  element.points = element.points.map(point => [startPoint[0] + point[0], startPoint[1] + point[1]]);
1358
2364
  Transforms.insertNode(board, element, [board.children.length]);
1359
2365
  });
2366
+ Transforms.addSelectionWithTemporaryElements(board, elements);
1360
2367
  };
1361
2368
 
1362
2369
  const withDrawFragment = (baseBoard) => {
@@ -1365,21 +2372,35 @@ const withDrawFragment = (baseBoard) => {
1365
2372
  board.getDeletedFragment = (data) => {
1366
2373
  const drawElements = getSelectedDrawElements(board);
1367
2374
  if (drawElements.length) {
1368
- const lines = getBoardLines(board);
1369
2375
  const geometryElements = drawElements.filter(value => PlaitDrawElement.isGeometry(value));
1370
2376
  const lineElements = drawElements.filter(value => PlaitDrawElement.isLine(value));
1371
- const boundLineElements = lines.filter(line => geometryElements.find(geometry => PlaitLine.isBoundElementOfSource(line, geometry) || PlaitLine.isBoundElementOfTarget(line, geometry)));
1372
- data.push(...[...geometryElements, ...lineElements, ...boundLineElements.filter(line => !lineElements.includes(line))]);
2377
+ const imageElements = drawElements.filter(value => PlaitDrawElement.isImage(value));
2378
+ const boundLineElements = [
2379
+ ...getBoundedLineElements(board, geometryElements),
2380
+ ...getBoundedLineElements(board, imageElements)
2381
+ ].filter(line => !lineElements.includes(line));
2382
+ data.push(...[
2383
+ ...geometryElements,
2384
+ ...lineElements,
2385
+ ...imageElements,
2386
+ ...boundLineElements.filter(line => !lineElements.includes(line))
2387
+ ]);
1373
2388
  }
1374
2389
  return getDeletedFragment(data);
1375
2390
  };
1376
- board.setFragment = (data, rectangle) => {
2391
+ board.setFragment = (data, rectangle, type) => {
1377
2392
  const targetDrawElements = getSelectedDrawElements(board);
2393
+ let boundLineElements = [];
1378
2394
  if (targetDrawElements.length) {
1379
- const elements = buildClipboardData(board, targetDrawElements, rectangle ? [rectangle.x, rectangle.y] : [0, 0]);
2395
+ if (type === 'cut') {
2396
+ const geometryElements = targetDrawElements.filter(value => PlaitDrawElement.isGeometry(value));
2397
+ const lineElements = targetDrawElements.filter(value => PlaitDrawElement.isLine(value));
2398
+ boundLineElements = getBoundedLineElements(board, geometryElements).filter(line => !lineElements.includes(line));
2399
+ }
2400
+ const elements = buildClipboardData(board, [...targetDrawElements, ...boundLineElements], rectangle ? [rectangle.x, rectangle.y] : [0, 0]);
1380
2401
  setClipboardData(data, elements);
1381
2402
  }
1382
- setFragment(data, rectangle);
2403
+ setFragment(data, rectangle, type);
1383
2404
  };
1384
2405
  board.insertFragment = (data, targetPoint) => {
1385
2406
  const elements = getDataFromClipboard(data);
@@ -1399,10 +2420,24 @@ const withDrawFragment = (baseBoard) => {
1399
2420
  return;
1400
2421
  }
1401
2422
  }
2423
+ if (data?.files.length) {
2424
+ const acceptImageArray = acceptImageTypes.map(type => 'image/' + type);
2425
+ if (acceptImageArray.includes(data?.files[0].type)) {
2426
+ const imageFile = data.files[0];
2427
+ buildImage(board, imageFile, DEFAULT_IMAGE_WIDTH, imageItem => {
2428
+ DrawTransforms.insertImage(board, imageItem, targetPoint);
2429
+ });
2430
+ return;
2431
+ }
2432
+ }
1402
2433
  insertFragment(data, targetPoint);
1403
2434
  };
1404
2435
  return board;
1405
2436
  };
2437
+ const getBoundedLineElements = (board, plaitShapes) => {
2438
+ const lines = getBoardLines(board);
2439
+ return lines.filter(line => plaitShapes.find(shape => PlaitLine.isBoundElementOfSource(line, shape) || PlaitLine.isBoundElementOfTarget(line, shape)));
2440
+ };
1406
2441
 
1407
2442
  const DefaultLineStyle = {
1408
2443
  strokeWidth: 2,
@@ -1420,10 +2455,10 @@ const getHitGeometryResizeHandleRef = (board, element, point) => {
1420
2455
  const getHitOutlineGeometry = (board, point, offset = 0) => {
1421
2456
  let geometry = null;
1422
2457
  depthFirstRecursion(board, node => {
1423
- if (PlaitDrawElement.isGeometry(node)) {
1424
- const shape = node.shape;
2458
+ if (PlaitDrawElement.isGeometry(node) || PlaitDrawElement.isImage(node)) {
1425
2459
  let client = getRectangleByPoints(node.points);
1426
2460
  client = RectangleClient.getOutlineRectangle(client, offset);
2461
+ const shape = getShape(node);
1427
2462
  const isHit = getEngine(shape).isHit(client, point);
1428
2463
  if (isHit) {
1429
2464
  geometry = node;
@@ -1441,7 +2476,8 @@ const withLineCreateByDraw = (board) => {
1441
2476
  let lineShapeG = null;
1442
2477
  let temporaryElement = null;
1443
2478
  board.pointerDown = (event) => {
1444
- const isLinePointer = PlaitBoard.isPointer(board, DrawPointerType.line);
2479
+ const linePointers = getLinePointers();
2480
+ const isLinePointer = PlaitBoard.isInPointer(board, linePointers);
1445
2481
  if (isLinePointer && isDrawingMode(board)) {
1446
2482
  const point = transformPoint(board, toPoint(event.x, event.y, PlaitBoard.getHost(board)));
1447
2483
  start = point;
@@ -1463,7 +2499,8 @@ const withLineCreateByDraw = (board) => {
1463
2499
  targetRef.connection = hitElement ? transformPointToConnection(board, movingPoint, hitElement) : undefined;
1464
2500
  targetRef.boundId = hitElement ? hitElement.id : undefined;
1465
2501
  const lineGenerator = new LineShapeGenerator(board);
1466
- temporaryElement = createLineElement(LineShape.elbow, [start, movingPoint], { marker: LineMarkerType.none, connection: sourceRef.connection, boundId: sourceRef?.boundId }, { marker: LineMarkerType.arrow, connection: targetRef.connection, boundId: targetRef?.boundId }, {
2502
+ const lineShape = PlaitBoard.getPointer(board);
2503
+ temporaryElement = createLineElement(lineShape, [start, movingPoint], { marker: LineMarkerType.none, connection: sourceRef.connection, boundId: sourceRef?.boundId }, { marker: LineMarkerType.arrow, connection: targetRef.connection, boundId: targetRef?.boundId }, {
1467
2504
  strokeColor: DefaultLineStyle.strokeColor,
1468
2505
  strokeWidth: DefaultLineStyle.strokeWidth
1469
2506
  });
@@ -1508,12 +2545,12 @@ const withGeometryResize = (board) => {
1508
2545
  return true;
1509
2546
  },
1510
2547
  detect: (point) => {
1511
- const selectedGeometryElements = getSelectedGeometryElements(board);
1512
- if (selectedGeometryElements.length !== 1 || getSelectedElements(board).length !== 1) {
2548
+ const selectedElements = [...getSelectedGeometryElements(board), ...getSelectedImageElements(board)];
2549
+ if (selectedElements.length !== 1 || getSelectedElements(board).length !== 1) {
1513
2550
  return null;
1514
2551
  }
1515
- const target = selectedGeometryElements[0];
1516
- const targetComponent = PlaitElement.getComponent(selectedGeometryElements[0]);
2552
+ const target = selectedElements[0];
2553
+ const targetComponent = PlaitElement.getComponent(selectedElements[0]);
1517
2554
  if (targetComponent.activeGenerator.hasResizeHandle) {
1518
2555
  const handleRef = getHitGeometryResizeHandleRef(board, target, point);
1519
2556
  if (handleRef) {
@@ -1528,27 +2565,34 @@ const withGeometryResize = (board) => {
1528
2565
  },
1529
2566
  onResize: (resizeRef, resizeState) => {
1530
2567
  let points = [...resizeRef.element.points];
2568
+ const rectangle = getRectangleByPoints(resizeRef.element.points);
2569
+ const ratio = rectangle.height / rectangle.width;
1531
2570
  if (resizeRef.handle === ResizeHandle.nw) {
1532
2571
  points = [resizeState.endTransformPoint, resizeRef.element.points[1]];
1533
2572
  }
1534
2573
  if (resizeRef.handle === ResizeHandle.ne) {
1535
- points = [
1536
- [resizeRef.element.points[0][0], resizeState.endTransformPoint[1]],
1537
- [resizeState.endTransformPoint[0], resizeRef.element.points[1][1]]
1538
- ];
2574
+ points = [resizeState.endTransformPoint, [resizeRef.element.points[0][0], resizeRef.element.points[1][1]]];
1539
2575
  }
1540
2576
  if (resizeRef.handle === ResizeHandle.se) {
1541
- points = [resizeRef.element.points[0], resizeState.endTransformPoint];
2577
+ points = [resizeState.endTransformPoint, resizeRef.element.points[0]];
1542
2578
  }
1543
2579
  if (resizeRef.handle === ResizeHandle.sw) {
1544
- points = [
1545
- [resizeState.endTransformPoint[0], resizeRef.element.points[0][1]],
1546
- [resizeRef.element.points[1][0], resizeState.endTransformPoint[1]]
1547
- ];
2580
+ points = [resizeState.endTransformPoint, [resizeRef.element.points[1][0], resizeRef.element.points[0][1]]];
2581
+ }
2582
+ if (isShift || PlaitDrawElement.isImage(resizeRef.element)) {
2583
+ const rectangle = getRectangleByPoints(points);
2584
+ const factor = points[0][1] > points[1][1] ? 1 : -1;
2585
+ const height = rectangle.width * ratio * factor;
2586
+ points = [[resizeState.endTransformPoint[0], points[1][1] + height], points[1]];
2587
+ }
2588
+ if (PlaitDrawElement.isGeometry(resizeRef.element)) {
2589
+ const { height: textHeight } = PlaitGeometry.getTextManage(resizeRef.element).getSize();
2590
+ DrawTransforms.resizeGeometry(board, points, textHeight, resizeRef.path);
2591
+ }
2592
+ else {
2593
+ points = normalizeShapePoints(points);
2594
+ Transforms.setNode(board, { points }, resizeRef.path);
1548
2595
  }
1549
- points = normalizeShapePoints(points, isShift);
1550
- const { height: textHeight } = PlaitGeometry.getTextManage(resizeRef.element).getSize();
1551
- DrawTransforms.resizeGeometry(board, points, textHeight, resizeRef.path);
1552
2596
  }
1553
2597
  };
1554
2598
  withResize(board, options);
@@ -1559,34 +2603,44 @@ var LineResizeHandle;
1559
2603
  (function (LineResizeHandle) {
1560
2604
  LineResizeHandle["source"] = "source";
1561
2605
  LineResizeHandle["target"] = "target";
2606
+ LineResizeHandle["addHandle"] = "addHandle";
1562
2607
  })(LineResizeHandle || (LineResizeHandle = {}));
1563
2608
  const getHitLineResizeHandleRef = (board, element, point) => {
1564
- const sourcePoint = getSourcePoint(board, element);
1565
- const targetPoint = getTargetPoint(board, element);
1566
- const sourceRectangle = {
1567
- x: sourcePoint[0] - RESIZE_HANDLE_DIAMETER / 2,
1568
- y: sourcePoint[1] - RESIZE_HANDLE_DIAMETER / 2,
1569
- width: RESIZE_HANDLE_DIAMETER,
1570
- height: RESIZE_HANDLE_DIAMETER
1571
- };
1572
- const targetRectangle = {
1573
- x: targetPoint[0] - RESIZE_HANDLE_DIAMETER / 2,
1574
- y: targetPoint[1] - RESIZE_HANDLE_DIAMETER / 2,
1575
- width: RESIZE_HANDLE_DIAMETER,
1576
- height: RESIZE_HANDLE_DIAMETER
1577
- };
1578
- const isHitSourceRectangle = RectangleClient.isHit(RectangleClient.toRectangleClient([point, point]), sourceRectangle);
1579
- const isHitTargetRectangle = RectangleClient.isHit(RectangleClient.toRectangleClient([point, point]), targetRectangle);
1580
- if (isHitSourceRectangle) {
1581
- return { rectangle: sourceRectangle, handle: LineResizeHandle.source };
2609
+ const points = PlaitLine.getPoints(board, element);
2610
+ const index = getHitPointIndex(points, point);
2611
+ if (index !== -1) {
2612
+ if (index === 0) {
2613
+ return { handle: LineResizeHandle.source, index };
2614
+ }
2615
+ if (index === points.length - 1) {
2616
+ return { handle: LineResizeHandle.target, index };
2617
+ }
2618
+ return { index };
1582
2619
  }
1583
- if (isHitTargetRectangle) {
1584
- return { rectangle: targetRectangle, handle: LineResizeHandle.target };
2620
+ const middlePoints = getMiddlePoints(board, element);
2621
+ const middleIndex = getHitPointIndex(middlePoints, point);
2622
+ if (middleIndex !== -1) {
2623
+ return { handle: LineResizeHandle.addHandle, index: middleIndex };
1585
2624
  }
1586
2625
  return undefined;
1587
2626
  };
2627
+ function getHitPointIndex(points, movingPoint) {
2628
+ const rectangles = points.map(point => {
2629
+ return {
2630
+ x: point[0] - RESIZE_HANDLE_DIAMETER / 2,
2631
+ y: point[1] - RESIZE_HANDLE_DIAMETER / 2,
2632
+ width: RESIZE_HANDLE_DIAMETER,
2633
+ height: RESIZE_HANDLE_DIAMETER
2634
+ };
2635
+ });
2636
+ const rectangle = rectangles.find(rectangle => {
2637
+ return RectangleClient.isHit(RectangleClient.toRectangleClient([movingPoint, movingPoint]), rectangle);
2638
+ });
2639
+ return rectangle ? rectangles.indexOf(rectangle) : -1;
2640
+ }
1588
2641
 
1589
2642
  const withLineResize = (board) => {
2643
+ let pointIndex = 0;
1590
2644
  const options = {
1591
2645
  key: 'draw-line',
1592
2646
  canResize: () => {
@@ -1603,6 +2657,7 @@ const withLineResize = (board) => {
1603
2657
  element: value,
1604
2658
  handle: handleRef.handle
1605
2659
  };
2660
+ pointIndex = handleRef.index;
1606
2661
  }
1607
2662
  });
1608
2663
  return result;
@@ -1614,17 +2669,23 @@ const withLineResize = (board) => {
1614
2669
  let source = { ...resizeRef.element.source };
1615
2670
  let target = { ...resizeRef.element.target };
1616
2671
  if (resizeRef.handle === LineResizeHandle.source) {
1617
- points[0] = resizeState.endTransformPoint;
2672
+ points[pointIndex] = resizeState.endTransformPoint;
1618
2673
  const hitElement = getHitOutlineGeometry(board, resizeState.endTransformPoint, -4);
1619
2674
  source.connection = hitElement ? transformPointToConnection(board, resizeState.endTransformPoint, hitElement) : undefined;
1620
2675
  source.boundId = hitElement ? hitElement.id : undefined;
1621
2676
  }
1622
- if (resizeRef.handle === LineResizeHandle.target) {
1623
- points[1] = resizeState.endTransformPoint;
2677
+ else if (resizeRef.handle === LineResizeHandle.target) {
2678
+ points[pointIndex] = resizeState.endTransformPoint;
1624
2679
  const hitElement = getHitOutlineGeometry(board, resizeState.endTransformPoint, -4);
1625
2680
  target.connection = hitElement ? transformPointToConnection(board, resizeState.endTransformPoint, hitElement) : undefined;
1626
2681
  target.boundId = hitElement ? hitElement.id : undefined;
1627
2682
  }
2683
+ else if (resizeRef.handle === LineResizeHandle.addHandle) {
2684
+ points.splice(pointIndex + 1, 0, resizeState.endTransformPoint);
2685
+ }
2686
+ else {
2687
+ points[pointIndex] = resizeState.endTransformPoint;
2688
+ }
1628
2689
  DrawTransforms.resizeLine(board, { points, source, target }, resizeRef.path);
1629
2690
  }
1630
2691
  };
@@ -1637,9 +2698,14 @@ const withLineBoundReaction = (board) => {
1637
2698
  let boundShapeG = null;
1638
2699
  board.pointerMove = (event) => {
1639
2700
  boundShapeG?.remove();
1640
- const isLinePointer = PlaitBoard.isPointer(board, DrawPointerType.line);
2701
+ const linePointers = Object.keys(LineShape);
2702
+ const isLinePointer = PlaitBoard.isInPointer(board, linePointers);
1641
2703
  const movingPoint = transformPoint(board, toPoint(event.x, event.y, PlaitBoard.getHost(board)));
1642
- const isLineResizing = isResizingByCondition(board, element => PlaitDrawElement.isLine(element));
2704
+ const isLineResizing = isResizingByCondition(board, resizeRef => {
2705
+ const { element, handle } = resizeRef;
2706
+ const isSourceOrTarget = handle === LineResizeHandle.target || handle === LineResizeHandle.source;
2707
+ return PlaitDrawElement.isLine(element) && isSourceOrTarget;
2708
+ });
1643
2709
  if (isLinePointer || isLineResizing) {
1644
2710
  const hitElement = getHitOutlineGeometry(board, movingPoint, -4);
1645
2711
  if (hitElement) {
@@ -1717,8 +2783,74 @@ function editHandle(board, element, manageIndex, isFirstEdit = false) {
1717
2783
  });
1718
2784
  }
1719
2785
 
2786
+ class ImageComponent extends CommonPluginElement {
2787
+ get activeGenerator() {
2788
+ return this.imageGenerator.componentRef.instance.activeGenerator;
2789
+ }
2790
+ constructor(viewContainerRef, cdr) {
2791
+ super(cdr);
2792
+ this.viewContainerRef = viewContainerRef;
2793
+ this.cdr = cdr;
2794
+ this.destroy$ = new Subject();
2795
+ }
2796
+ initializeGenerator() {
2797
+ this.imageGenerator = new ImageGenerator(this.board, {
2798
+ getRectangle: (element) => {
2799
+ return {
2800
+ x: element.points[0][0],
2801
+ y: element.points[0][1],
2802
+ width: element.points[1][0] - element.points[0][0],
2803
+ height: element.points[1][1] - element.points[0][1]
2804
+ };
2805
+ },
2806
+ getImageItem: element => {
2807
+ return {
2808
+ url: element.url,
2809
+ width: element.points[1][0] - element.points[0][0],
2810
+ height: element.points[1][1] - element.points[0][1]
2811
+ };
2812
+ }
2813
+ });
2814
+ }
2815
+ ngOnInit() {
2816
+ super.ngOnInit();
2817
+ this.initializeGenerator();
2818
+ this.imageGenerator.draw(this.element, this.g, this.viewContainerRef);
2819
+ }
2820
+ onContextChanged(value, previous) {
2821
+ if (value.element !== previous.element) {
2822
+ this.imageGenerator.updateImage(this.g, previous.element, value.element);
2823
+ this.imageGenerator.componentRef.instance.isFocus = this.selected;
2824
+ }
2825
+ else {
2826
+ const hasSameSelected = value.selected === previous.selected;
2827
+ const hasSameHandleState = this.activeGenerator.options.hasResizeHandle() === this.activeGenerator.hasResizeHandle;
2828
+ if (!hasSameSelected || !hasSameHandleState) {
2829
+ this.imageGenerator.componentRef.instance.isFocus = this.selected;
2830
+ }
2831
+ }
2832
+ }
2833
+ ngOnDestroy() {
2834
+ super.ngOnDestroy();
2835
+ this.destroy$.next();
2836
+ this.destroy$.complete();
2837
+ this.imageGenerator.destroy();
2838
+ }
2839
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: ImageComponent, deps: [{ token: i0.ViewContainerRef }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
2840
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.3", type: ImageComponent, isStandalone: true, selector: "plait-draw-geometry", usesInheritance: true, ngImport: i0, template: ``, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2841
+ }
2842
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: ImageComponent, decorators: [{
2843
+ type: Component,
2844
+ args: [{
2845
+ selector: 'plait-draw-geometry',
2846
+ template: ``,
2847
+ changeDetection: ChangeDetectionStrategy.OnPush,
2848
+ standalone: true
2849
+ }]
2850
+ }], ctorParameters: function () { return [{ type: i0.ViewContainerRef }, { type: i0.ChangeDetectorRef }]; } });
2851
+
1720
2852
  const withDraw = (board) => {
1721
- const { drawElement, getRectangle, isHitSelection, isMovable, dblclick } = board;
2853
+ const { drawElement, getRectangle, isHitSelection, isMovable, isAlign } = board;
1722
2854
  board.drawElement = (context) => {
1723
2855
  if (PlaitDrawElement.isGeometry(context.element)) {
1724
2856
  return GeometryComponent;
@@ -1726,6 +2858,9 @@ const withDraw = (board) => {
1726
2858
  else if (PlaitDrawElement.isLine(context.element)) {
1727
2859
  return LineComponent;
1728
2860
  }
2861
+ else if (PlaitDrawElement.isImage(context.element)) {
2862
+ return ImageComponent;
2863
+ }
1729
2864
  return drawElement(context);
1730
2865
  };
1731
2866
  board.getRectangle = (element) => {
@@ -1733,9 +2868,11 @@ const withDraw = (board) => {
1733
2868
  return getRectangleByPoints(element.points);
1734
2869
  }
1735
2870
  if (PlaitDrawElement.isLine(element)) {
1736
- const source = getSourcePoint(board, element);
1737
- const target = getTargetPoint(board, element);
1738
- return getRectangleByPoints([source, target]);
2871
+ const points = getLinePoints(board, element);
2872
+ return getRectangleByPoints(points);
2873
+ }
2874
+ if (PlaitDrawElement.isImage(element)) {
2875
+ return getRectangleByPoints(element.points);
1739
2876
  }
1740
2877
  return getRectangle(element);
1741
2878
  };
@@ -1749,6 +2886,11 @@ const withDraw = (board) => {
1749
2886
  }
1750
2887
  return RectangleClient.isHit(rangeRectangle, client);
1751
2888
  }
2889
+ if (PlaitDrawElement.isImage(element)) {
2890
+ const client = getRectangleByPoints(element.points);
2891
+ const rangeRectangle = RectangleClient.toRectangleClient([range.anchor, range.focus]);
2892
+ return RectangleClient.isHit(rangeRectangle, client);
2893
+ }
1752
2894
  if (PlaitDrawElement.isLine(element)) {
1753
2895
  const points = getLinePoints(board, element);
1754
2896
  const strokeWidth = getStrokeWidthByElement(element);
@@ -1767,17 +2909,34 @@ const withDraw = (board) => {
1767
2909
  if (PlaitDrawElement.isGeometry(element)) {
1768
2910
  return true;
1769
2911
  }
2912
+ if (PlaitDrawElement.isImage(element)) {
2913
+ return true;
2914
+ }
1770
2915
  if (PlaitDrawElement.isLine(element)) {
1771
- return !element.source.boundId && !element.target.boundId;
2916
+ const selectedElements = getSelectedElements(board);
2917
+ const isSelected = (boundId) => {
2918
+ return !!selectedElements.find(value => value.id === boundId);
2919
+ };
2920
+ if ((element.source.boundId && !isSelected(element.source.boundId)) ||
2921
+ (element.target.boundId && !isSelected(element.target.boundId))) {
2922
+ return false;
2923
+ }
2924
+ return true;
1772
2925
  }
1773
2926
  return isMovable(element);
1774
2927
  };
1775
- return withLineText(withLineBoundReaction(withLineResize(withGeometryResize(withLineCreateByDraw(withGeometryCreateByDrag(withGeometryCreateByDraw(withDrawFragment(withDrawHotkey(board)))))))));
2928
+ board.isAlign = (element) => {
2929
+ if (PlaitDrawElement.isGeometry(element) || PlaitDrawElement.isImage(element)) {
2930
+ return true;
2931
+ }
2932
+ return isAlign(element);
2933
+ };
2934
+ return withLineText(withLineBoundReaction(withLineResize(withGeometryResize(withLineCreateByDraw(withGeometryCreateByDrag(withGeometryCreateByDrawing(withDrawFragment(withDrawHotkey(board)))))))));
1776
2935
  };
1777
2936
 
1778
2937
  /**
1779
2938
  * Generated bundle index. Do not edit.
1780
2939
  */
1781
2940
 
1782
- export { DefaultGeometryActiveStyle, DefaultGeometryProperty, DefaultGeometryStyle, DefaultTextProperty, DrawPointerType, DrawTransforms, GeometryComponent, GeometryPointer, GeometryShape, GeometryThreshold, LineComponent, LineHandleKey, LineMarkerType, LineShape, PlaitDrawElement, PlaitGeometry, PlaitLine, ShapeDefaultSpace, StrokeStyle, createGeometryElement, createLineElement, drawBoundMask, drawGeometry, drawLine, getBoardLines, getCenterPointsOnPolygon, getConnectionPoint, getElbowPoints, getFillByElement, getHitConnectorPoint, getHitLineTextIndex, getLineDashByElement, getLinePoints, getLineTextRectangle, getNearestPoint, getPointsByCenterPoint, getSelectedDrawElements, getSelectedGeometryElements, getSelectedLineElements, getSourcePoint, getStraightPoints, getStrokeColorByElement, getStrokeStyleByElement, getStrokeWidthByElement, getTargetPoint, getTextRectangle, isHitLineText, isHitPolyLine, transformPointToConnection, withDraw };
2941
+ export { DEFAULT_IMAGE_WIDTH, DefaultGeometryActiveStyle, DefaultGeometryProperty, DefaultGeometryStyle, DefaultTextProperty, DrawTransforms, GeometryComponent, GeometryShape, GeometryThreshold, LineComponent, LineHandleKey, LineMarkerType, LineShape, PlaitDrawElement, PlaitGeometry, PlaitLine, Q2C, ShapeDefaultSpace, StrokeStyle, createGeometryElement, createLineElement, drawBoundMask, drawGeometry, drawLine, getBoardLines, getCenterPointsOnPolygon, getConnectionPoint, getCurvePoints, getEdgeOnPolygonByPoint, getElbowPoints, getExtendPoint, getFillByElement, getGeometryPointers, getHitConnectorPoint, getHitLineTextIndex, getLineDashByElement, getLineHandleRefPair, getLinePointers, getLinePoints, getLineTextRectangle, getNearestPoint, getPointsByCenterPoint, getSelectedDrawElements, getSelectedGeometryElements, getSelectedImageElements, getSelectedLineElements, getStrokeColorByElement, getStrokeStyleByElement, getStrokeWidthByElement, getTextRectangle, getVectorByConnection, isHitLineText, isHitPolyLine, removeDuplicatePoints, transformOpsToPoints, transformPointToConnection, withDraw };
1783
2942
  //# sourceMappingURL=plait-draw.mjs.map