@plait/draw 0.1.0-next.12 → 0.1.0-next.14

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 (107) 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 -14
  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/{utils/engine → engines}/left-arrow.d.ts +1 -1
  11. package/engines/octagon.d.ts +4 -0
  12. package/{utils/engine → engines}/parallelogram.d.ts +1 -1
  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/{utils/engine → engines}/right-arrow.d.ts +1 -1
  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/{utils/engine → engines}/trapezoid.d.ts +1 -1
  22. package/{utils/engine → engines}/triangle.d.ts +1 -1
  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 -27
  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 +49 -15
  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 +11 -1
  54. package/esm2022/interfaces/image.mjs +2 -0
  55. package/esm2022/interfaces/index.mjs +8 -2
  56. package/esm2022/interfaces/line.mjs +17 -2
  57. package/esm2022/line.component.mjs +4 -3
  58. package/esm2022/plugins/with-draw-fragment.mjs +26 -5
  59. package/esm2022/plugins/with-draw.mjs +25 -4
  60. package/esm2022/plugins/with-geometry-create.mjs +15 -12
  61. package/esm2022/plugins/with-geometry-resize.mjs +17 -11
  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 +3 -3
  69. package/esm2022/utils/geometry.mjs +8 -5
  70. package/esm2022/utils/index.mjs +1 -1
  71. package/esm2022/utils/line-arrow.mjs +43 -18
  72. package/esm2022/utils/line.mjs +163 -49
  73. package/esm2022/utils/position/geometry.mjs +5 -4
  74. package/esm2022/utils/position/line.mjs +32 -21
  75. package/esm2022/utils/selected.mjs +5 -1
  76. package/esm2022/utils/shape.mjs +8 -0
  77. package/fesm2022/plait-draw.mjs +1081 -261
  78. package/fesm2022/plait-draw.mjs.map +1 -1
  79. package/generators/line-active.generator.d.ts +2 -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 +12 -1
  84. package/interfaces/image.d.ts +7 -0
  85. package/interfaces/index.d.ts +5 -1
  86. package/interfaces/line.d.ts +11 -4
  87. package/line.component.d.ts +1 -1
  88. package/package.json +1 -1
  89. package/plugins/with-draw-fragment.d.ts +2 -2
  90. package/plugins/with-geometry-create.d.ts +1 -1
  91. package/transforms/image.d.ts +3 -0
  92. package/transforms/index.d.ts +1 -0
  93. package/utils/line.d.ts +8 -4
  94. package/utils/position/geometry.d.ts +2 -1
  95. package/utils/position/line.d.ts +7 -3
  96. package/utils/selected.d.ts +2 -0
  97. package/utils/shape.d.ts +2 -0
  98. package/esm2022/utils/engine/diamond.mjs +0 -30
  99. package/esm2022/utils/engine/ellipse.mjs +0 -92
  100. package/esm2022/utils/engine/index.mjs +0 -26
  101. package/esm2022/utils/engine/left-arrow.mjs +0 -45
  102. package/esm2022/utils/engine/parallelogram.mjs +0 -39
  103. package/esm2022/utils/engine/rectangle.mjs +0 -26
  104. package/esm2022/utils/engine/right-arrow.mjs +0 -45
  105. package/esm2022/utils/engine/round-rectangle.mjs +0 -59
  106. package/esm2022/utils/engine/trapezoid.mjs +0 -40
  107. package/esm2022/utils/engine/triangle.mjs +0 -40
@@ -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, distanceBetweenPointAndSegment, arrowPoints, createPath, drawLinearPath, getElementById, Direction, distanceBetweenPointAndSegments, createMask, createRect, 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, getPoints, getPointOnPolyline, getDirectionByPointOfRectangle, rotateVector90, getDirectionByVector, getDirectionFactor, Generator, normalizeShapePoints, CommonPluginElement, ActiveGenerator, WithTextPluginKey, RESIZE_HANDLE_DIAMETER, PRIMARY_COLOR, 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
 
@@ -19,6 +20,16 @@ var GeometryShape;
19
20
  GeometryShape["leftArrow"] = "leftArrow";
20
21
  GeometryShape["trapezoid"] = "trapezoid";
21
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";
22
33
  })(GeometryShape || (GeometryShape = {}));
23
34
  const PlaitGeometry = {
24
35
  getTextEditor(element) {
@@ -33,87 +44,6 @@ const PlaitGeometry = {
33
44
  }
34
45
  };
35
46
 
36
- var LineMarkerType;
37
- (function (LineMarkerType) {
38
- LineMarkerType["arrow"] = "arrow";
39
- LineMarkerType["none"] = "none";
40
- LineMarkerType["openTriangle"] = "open-triangle";
41
- LineMarkerType["solidTriangle"] = "solid-triangle";
42
- LineMarkerType["sharpArrow"] = "sharp-arrow";
43
- })(LineMarkerType || (LineMarkerType = {}));
44
- var LineShape;
45
- (function (LineShape) {
46
- LineShape["straight"] = "straight";
47
- LineShape["curve"] = "curve";
48
- LineShape["elbow"] = "elbow";
49
- })(LineShape || (LineShape = {}));
50
- var LineHandleKey;
51
- (function (LineHandleKey) {
52
- LineHandleKey["source"] = "source";
53
- LineHandleKey["target"] = "target";
54
- })(LineHandleKey || (LineHandleKey = {}));
55
- const PlaitLine = {
56
- getTextEditors(element) {
57
- const component = PlaitElement.getComponent(element);
58
- if (component) {
59
- const manage = component.textManages.find(manage => manage.isEditing);
60
- if (manage) {
61
- return [manage.componentRef.instance.editor];
62
- }
63
- else {
64
- return component.textManages.map(manage => manage.componentRef.instance.editor);
65
- }
66
- }
67
- throw new Error('can not get correctly component in get text editor');
68
- },
69
- isSourceMarkOrTargetMark(line, markType, handleKey) {
70
- if (handleKey === LineHandleKey.source) {
71
- return line.source.marker === markType;
72
- }
73
- else {
74
- return line.target.marker === markType;
75
- }
76
- },
77
- isSourceMark(line, markType) {
78
- return PlaitLine.isSourceMarkOrTargetMark(line, markType, LineHandleKey.source);
79
- },
80
- isTargetMark(line, markType) {
81
- return PlaitLine.isSourceMarkOrTargetMark(line, markType, LineHandleKey.target);
82
- },
83
- isBoundElementOfSource(line, element) {
84
- return line.source.boundId === element.id;
85
- },
86
- isBoundElementOfTarget(line, element) {
87
- return line.target.boundId === element.id;
88
- }
89
- };
90
-
91
- var StrokeStyle;
92
- (function (StrokeStyle) {
93
- StrokeStyle["solid"] = "solid";
94
- StrokeStyle["dashed"] = "dashed";
95
- })(StrokeStyle || (StrokeStyle = {}));
96
-
97
- const PlaitDrawElement = {
98
- isGeometry: (value) => {
99
- return value.type === 'geometry';
100
- },
101
- isLine: (value) => {
102
- return value.type === 'line';
103
- },
104
- isText: (value) => {
105
- return value.type === 'geometry' && value.shape === GeometryShape.text;
106
- },
107
- isDrawElement: (value) => {
108
- if (PlaitDrawElement.isGeometry(value) || PlaitDrawElement.isLine(value)) {
109
- return true;
110
- }
111
- else {
112
- return false;
113
- }
114
- }
115
- };
116
-
117
47
  const ShapeDefaultSpace = {
118
48
  rectangleAndText: 4
119
49
  };
@@ -139,35 +69,17 @@ const DefaultTextProperty = {
139
69
  text: '文本'
140
70
  };
141
71
  const GeometryThreshold = {
142
- defaultTextMaxWidth: 34 * 14,
72
+ defaultTextMaxWidth: 34 * 14
143
73
  };
144
74
 
145
- var DrawPointerType;
146
- (function (DrawPointerType) {
147
- DrawPointerType["text"] = "text";
148
- DrawPointerType["rectangle"] = "rectangle";
149
- DrawPointerType["line"] = "line";
150
- DrawPointerType["diamond"] = "diamond";
151
- DrawPointerType["roundRectangle"] = "roundRectangle";
152
- DrawPointerType["parallelogram"] = "parallelogram";
153
- DrawPointerType["ellipse"] = "ellipse";
154
- DrawPointerType["triangle"] = "triangle";
155
- DrawPointerType["leftArrow"] = "leftArrow";
156
- DrawPointerType["trapezoid"] = "trapezoid";
157
- DrawPointerType["rightArrow"] = "rightArrow";
158
- })(DrawPointerType || (DrawPointerType = {}));
159
- const GeometryPointer = [
160
- DrawPointerType.rectangle,
161
- DrawPointerType.text,
162
- DrawPointerType.diamond,
163
- DrawPointerType.ellipse,
164
- DrawPointerType.parallelogram,
165
- DrawPointerType.roundRectangle,
166
- DrawPointerType.triangle,
167
- DrawPointerType.leftArrow,
168
- DrawPointerType.trapezoid,
169
- DrawPointerType.rightArrow
170
- ];
75
+ const getGeometryPointers = () => {
76
+ return Object.keys(GeometryShape);
77
+ };
78
+ const getLinePointers = () => {
79
+ return Object.keys(LineShape);
80
+ };
81
+
82
+ const DEFAULT_IMAGE_WIDTH = 1000;
171
83
 
172
84
  const getStrokeWidthByElement = (element) => {
173
85
  if (PlaitDrawElement.isText(element)) {
@@ -191,6 +103,102 @@ const getStrokeStyleByElement = (element) => {
191
103
  return element.strokeStyle || StrokeStyle.solid;
192
104
  };
193
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
+
194
202
  const DiamondEngine = {
195
203
  draw(board, rectangle, options) {
196
204
  const points = RectangleClient.getEdgeCenterPoints(rectangle);
@@ -310,6 +318,44 @@ function getVectorBySlope(x, y, slope) {
310
318
  return vector;
311
319
  }
312
320
 
321
+ const HexagonEngine = {
322
+ draw(board, rectangle, options) {
323
+ const points = getHexagonPoints(rectangle);
324
+ const rs = PlaitBoard.getRoughSVG(board);
325
+ const polygon = rs.polygon(points, { ...options, fillStyle: 'solid' });
326
+ setStrokeLinecap(polygon, 'round');
327
+ return polygon;
328
+ },
329
+ isHit(rectangle, point) {
330
+ const parallelogramPoints = getHexagonPoints(rectangle);
331
+ return isPointInPolygon(point, parallelogramPoints);
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
+
313
359
  const LeftArrowEngine = {
314
360
  draw(board, rectangle, options) {
315
361
  const points = getLeftArrowPoints(rectangle);
@@ -353,6 +399,46 @@ const getLeftArrowPoints = (rectangle) => {
353
399
  ];
354
400
  };
355
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
+
356
442
  const ParallelogramEngine = {
357
443
  draw(board, rectangle, options) {
358
444
  const points = getParallelogramCornerPoints(rectangle);
@@ -390,6 +476,119 @@ const getParallelogramCornerPoints = (rectangle) => {
390
476
  ];
391
477
  };
392
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
+
393
592
  const RectangleEngine = {
394
593
  draw(board, rectangle, options) {
395
594
  return drawRectangle(board, rectangle, { ...options, fillStyle: 'solid' });
@@ -512,6 +711,124 @@ function getNearestPointBetweenPointAndRoundRectangle(point, rectangle, radius)
512
711
  return result;
513
712
  }
514
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
+
515
832
  const TrapezoidEngine = {
516
833
  draw(board, rectangle, options) {
517
834
  const points = getTrapezoidPoints(rectangle);
@@ -588,6 +905,52 @@ const getTrianglePoints = (rectangle) => {
588
905
  ];
589
906
  };
590
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
+
591
954
  const ShapeEngineMap = {
592
955
  [GeometryShape.rectangle]: RectangleEngine,
593
956
  [GeometryShape.diamond]: DiamondEngine,
@@ -598,12 +961,29 @@ const ShapeEngineMap = {
598
961
  [GeometryShape.triangle]: TriangleEngine,
599
962
  [GeometryShape.leftArrow]: LeftArrowEngine,
600
963
  [GeometryShape.trapezoid]: TrapezoidEngine,
601
- [GeometryShape.rightArrow]: RightArrowEngine
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
602
975
  };
603
976
  const getEngine = (shape) => {
604
977
  return ShapeEngineMap[shape];
605
978
  };
606
979
 
980
+ const getShape = (value) => {
981
+ if (PlaitDrawElement.isImage(value)) {
982
+ return GeometryShape.rectangle;
983
+ }
984
+ return value.shape;
985
+ };
986
+
607
987
  const createGeometryElement = (shape, points, text, options) => {
608
988
  let textOptions = {};
609
989
  let alignment = Alignment.center;
@@ -645,14 +1025,15 @@ const drawBoundMask = (board, element) => {
645
1025
  const G = createG();
646
1026
  const rectangle = getRectangleByPoints(element.points);
647
1027
  const activeRectangle = RectangleClient.inflate(rectangle, ACTIVE_STROKE_WIDTH);
648
- const maskG = drawGeometry(board, activeRectangle, element.shape, {
1028
+ const shape = getShape(element);
1029
+ const maskG = drawGeometry(board, activeRectangle, shape, {
649
1030
  stroke: SELECTION_BORDER_COLOR,
650
1031
  strokeWidth: 1,
651
1032
  fill: SELECTION_FILL_COLOR,
652
1033
  fillStyle: 'solid'
653
1034
  });
654
1035
  G.appendChild(maskG);
655
- const connectorPoints = getEngine(element.shape).getConnectorPoints(activeRectangle);
1036
+ const connectorPoints = getEngine(shape).getConnectorPoints(activeRectangle);
656
1037
  connectorPoints.forEach(point => {
657
1038
  const circleG = drawCircle(PlaitBoard.getRoughSVG(board), point, 6, {
658
1039
  stroke: '#999999',
@@ -670,7 +1051,8 @@ const drawGeometry = (board, outerRectangle, shape, options) => {
670
1051
  const getNearestPoint = (element, point, inflateDelta = 0) => {
671
1052
  const rectangle = getRectangleByPoints(element.points);
672
1053
  const activeRectangle = RectangleClient.inflate(rectangle, inflateDelta);
673
- return getEngine(element.shape).getNearestPoint(activeRectangle, point);
1054
+ const shape = getShape(element);
1055
+ return getEngine(shape).getNearestPoint(activeRectangle, point);
674
1056
  };
675
1057
  const getCenterPointsOnPolygon = (points) => {
676
1058
  const centerPoint = [];
@@ -698,16 +1080,19 @@ const drawLineArrow = (element, points, options) => {
698
1080
  return null;
699
1081
  }
700
1082
  if (!PlaitLine.isSourceMark(element, LineMarkerType.none)) {
701
- 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);
702
1085
  sourceArrow && arrowG.appendChild(sourceArrow);
703
1086
  }
704
1087
  if (!PlaitLine.isTargetMark(element, LineMarkerType.none)) {
705
- 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);
706
1090
  arrow && arrowG.appendChild(arrow);
707
1091
  }
708
1092
  return arrowG;
709
1093
  };
710
- const getArrow = (element, marker, source, target, options) => {
1094
+ const getArrow = (element, arrowOptions, options) => {
1095
+ const { marker, source, target, isSource } = arrowOptions;
711
1096
  let targetArrow;
712
1097
  switch (marker) {
713
1098
  case LineMarkerType.openTriangle: {
@@ -726,16 +1111,27 @@ const getArrow = (element, marker, source, target, options) => {
726
1111
  targetArrow = drawSharpArrow(source, target, options);
727
1112
  break;
728
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);
1128
+ break;
1129
+ }
729
1130
  }
730
1131
  return targetArrow;
731
1132
  };
732
1133
  const drawSharpArrow = (source, target, options) => {
733
- const directionFactor = getFactorByPoints(source, target);
734
1134
  const startPoint = target;
735
- // const startPoint: Point = [
736
- // target[0],
737
- // target[1]
738
- // ];
739
1135
  const { pointLeft, pointRight } = arrowPoints(source, target, 12, 20);
740
1136
  const g = createG();
741
1137
  const path = createPath();
@@ -750,10 +1146,7 @@ const drawSharpArrow = (source, target, options) => {
750
1146
  const drawArrow = (element, source, target, options) => {
751
1147
  const directionFactor = getFactorByPoints(source, target);
752
1148
  const strokeWidth = getStrokeWidthByElement(element);
753
- const endPoint = [
754
- target[0] + strokeWidth * directionFactor.x / 2,
755
- target[1] + strokeWidth * directionFactor.y / 2
756
- ];
1149
+ const endPoint = [target[0] + (strokeWidth * directionFactor.x) / 2, target[1] + (strokeWidth * directionFactor.y) / 2];
757
1150
  const middlePoint = [endPoint[0] - 8 * directionFactor.x, endPoint[1] - 8 * directionFactor.y];
758
1151
  const { pointLeft, pointRight } = arrowPoints(source, endPoint, 12, 30);
759
1152
  const arrowG = drawLinearPath([pointLeft, endPoint, pointRight, middlePoint], { ...options, fill: options.stroke }, true);
@@ -769,13 +1162,26 @@ const drawSolidTriangle = (source, target, options) => {
769
1162
  const drawOpenTriangle = (element, source, target, options) => {
770
1163
  const directionFactor = getFactorByPoints(source, target);
771
1164
  const strokeWidth = getStrokeWidthByElement(element);
772
- const endPoint = [
773
- target[0] + strokeWidth * directionFactor.x / 2,
774
- target[1] + strokeWidth * directionFactor.y / 2
775
- ];
1165
+ const endPoint = [target[0] + (strokeWidth * directionFactor.x) / 2, target[1] + (strokeWidth * directionFactor.y) / 2];
776
1166
  const { pointLeft, pointRight } = arrowPoints(source, endPoint, 12, 40);
777
1167
  return drawLinearPath([pointLeft, endPoint, pointRight], options);
778
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
+ };
779
1185
 
780
1186
  const createLineElement = (shape, points, source, target, options) => {
781
1187
  return {
@@ -791,14 +1197,17 @@ const createLineElement = (shape, points, source, target, options) => {
791
1197
  };
792
1198
  };
793
1199
  const getLinePoints = (board, element) => {
794
- return element.shape === LineShape.elbow ? getElbowPoints(board, element) : getStraightPoints(board, element);
795
- };
796
- const getStraightPoints = (board, element) => {
797
- return getLineHandlePoints(board, element);
798
- };
799
- const getLineHandlePoints = (board, element) => {
800
- const handleRefPair = getLineHandleRefPair(board, element);
801
- return [handleRefPair.source.point, handleRefPair.target.point];
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
+ }
802
1211
  };
803
1212
  const getLineHandleRefPair = (board, element) => {
804
1213
  const strokeWidth = getStrokeWidthByElement(element);
@@ -808,38 +1217,107 @@ const getLineHandleRefPair = (board, element) => {
808
1217
  let targetPoint = targetBoundElement
809
1218
  ? getConnectionPoint(targetBoundElement, element.target.connection)
810
1219
  : element.points[element.points.length - 1];
811
- let sourceDirection = sourcePoint[0] < targetPoint[0] ? Direction.right : Direction.left;
812
- let targetDirection = sourcePoint[0] < targetPoint[0] ? Direction.left : Direction.right;
813
- const sourceHandleRef = { key: LineHandleKey.source, point: sourcePoint, direction: sourceDirection };
814
- const targetHandleRef = { key: LineHandleKey.target, point: targetPoint, direction: targetDirection };
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
+ };
815
1236
  if (sourceBoundElement) {
816
1237
  const connectionOffset = PlaitLine.isSourceMarkOrTargetMark(element, LineMarkerType.none, LineHandleKey.source) ? 0 : strokeWidth;
817
- const direction = getDirectionByBoundElementAndConnection(board, sourceBoundElement, element.source.connection);
1238
+ const sourceVector = getVectorByConnection(sourceBoundElement, element.source.connection);
1239
+ const direction = getDirectionByVector(sourceVector);
818
1240
  sourceDirection = direction ? direction : sourceDirection;
819
1241
  sourcePoint = getConnectionPoint(sourceBoundElement, element.source.connection, sourceDirection, connectionOffset);
820
1242
  sourceHandleRef.boundElement = sourceBoundElement;
821
1243
  sourceHandleRef.direction = sourceDirection;
822
1244
  sourceHandleRef.point = sourcePoint;
1245
+ sourceHandleRef.vector = sourceVector;
823
1246
  }
824
1247
  if (targetBoundElement) {
825
1248
  const connectionOffset = PlaitLine.isSourceMarkOrTargetMark(element, LineMarkerType.none, LineHandleKey.target) ? 0 : strokeWidth;
826
- const direction = getDirectionByBoundElementAndConnection(board, targetBoundElement, element.target.connection);
1249
+ const targetVector = getVectorByConnection(targetBoundElement, element.target.connection);
1250
+ const direction = getDirectionByVector(targetVector);
827
1251
  targetDirection = direction ? direction : targetDirection;
828
1252
  targetPoint = getConnectionPoint(targetBoundElement, element.target.connection, targetDirection, connectionOffset);
829
- targetHandleRef.boundElement = sourceBoundElement;
1253
+ targetHandleRef.boundElement = targetBoundElement;
830
1254
  targetHandleRef.direction = targetDirection;
831
1255
  targetHandleRef.point = targetPoint;
1256
+ targetHandleRef.vector = targetVector;
832
1257
  }
833
1258
  return { source: sourceHandleRef, target: targetHandleRef };
834
1259
  };
835
1260
  const getElbowPoints = (board, element) => {
836
1261
  if (element.points.length === 2) {
837
1262
  const handleRefPair = getLineHandleRefPair(board, element);
838
- const points = getPoints(handleRefPair.source.point, handleRefPair.source.direction, handleRefPair.target.point, handleRefPair.target.direction, 30);
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);
839
1266
  return points;
840
1267
  }
841
1268
  return element.points;
842
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
+ };
843
1321
  const isHitPolyLine = (pathPoints, point, strokeWidth, expand = 0) => {
844
1322
  const distance = distanceBetweenPointAndSegments(pathPoints, point);
845
1323
  return distance <= strokeWidth + expand;
@@ -869,8 +1347,15 @@ const drawLine = (board, element) => {
869
1347
  const strokeLineDash = getLineDashByElement(element);
870
1348
  const options = { stroke: strokeColor, strokeWidth, strokeLineDash };
871
1349
  const lineG = createG();
872
- const points = getLinePoints(board, element);
873
- const line = drawLinearPath(points, options);
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
+ }
874
1359
  const id = idCreator();
875
1360
  line.setAttribute('mask', `url(#${id})`);
876
1361
  lineG.appendChild(line);
@@ -904,32 +1389,6 @@ function drawMask(board, element, id) {
904
1389
  maskTargetFillRect.setAttribute('opacity', '0');
905
1390
  return { mask, maskTargetFillRect };
906
1391
  }
907
- const getDirectionByBoundElementAndConnection = (board, boundElement, connection) => {
908
- const rectangle = getRectangleByPoints(boundElement.points);
909
- const engine = getEngine(boundElement.shape);
910
- const direction = getDirectionByPointOfRectangle(connection);
911
- if (direction) {
912
- return direction;
913
- }
914
- if (engine.getEdgeByConnectionPoint) {
915
- const edge = engine.getEdgeByConnectionPoint(rectangle, connection);
916
- if (edge) {
917
- const vector = [edge[1][0] - edge[0][0], edge[1][1] - edge[0][1]];
918
- const vector90 = rotateVector90(vector);
919
- const direction = getDirectionByVector(vector90);
920
- return direction;
921
- }
922
- }
923
- if (engine.getTangentVectorByConnectionPoint) {
924
- const vector = engine.getTangentVectorByConnectionPoint(rectangle, connection);
925
- if (vector) {
926
- const vector90 = rotateVector90(vector);
927
- const direction = getDirectionByVector(vector90);
928
- return direction;
929
- }
930
- }
931
- return null;
932
- };
933
1392
  const getConnectionPoint = (geometry, connection, direction, delta) => {
934
1393
  const rectangle = getRectangleByPoints(geometry.points);
935
1394
  if (direction && delta) {
@@ -950,7 +1409,8 @@ const transformPointToConnection = (board, point, hitElement) => {
950
1409
  return [(nearestPoint[0] - rectangle.x) / rectangle.width, (nearestPoint[1] - rectangle.y) / rectangle.height];
951
1410
  };
952
1411
  const getHitConnectorPoint = (movingPoint, hitElement, rectangle) => {
953
- const connector = getEngine(hitElement.shape).getConnectorPoints(rectangle);
1412
+ const shape = getShape(hitElement);
1413
+ const connector = getEngine(shape).getConnectorPoints(rectangle);
954
1414
  const points = getPointsByCenterPoint(movingPoint, 5, 5);
955
1415
  const pointRectangle = getRectangleByPoints(points);
956
1416
  return connector.find(point => {
@@ -974,6 +1434,64 @@ const getBoardLines = (board) => {
974
1434
  recursion: (element) => PlaitDrawElement.isDrawElement(element)
975
1435
  });
976
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
+ };
977
1495
 
978
1496
  const getSelectedDrawElements = (board) => {
979
1497
  const selectedElements = getSelectedElements(board).filter(value => PlaitDrawElement.isDrawElement(value));
@@ -987,6 +1505,111 @@ const getSelectedLineElements = (board) => {
987
1505
  const selectedElements = getSelectedElements(board).filter(value => PlaitDrawElement.isLine(value));
988
1506
  return selectedElements;
989
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
+ };
990
1613
 
991
1614
  class GeometryShapeGenerator extends Generator {
992
1615
  canDraw(element, data) {
@@ -1002,7 +1625,12 @@ class GeometryShapeGenerator extends Generator {
1002
1625
  const strokeColor = getStrokeColorByElement(element);
1003
1626
  const fill = getFillByElement(element);
1004
1627
  const strokeLineDash = getLineDashByElement(element);
1005
- 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
+ });
1006
1634
  }
1007
1635
  }
1008
1636
 
@@ -1082,6 +1710,28 @@ const setTextSize = (board, element, textWidth, textHeight) => {
1082
1710
  }
1083
1711
  };
1084
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
+
1085
1735
  const resizeLine = (board, options, path) => {
1086
1736
  Transforms.setNode(board, options, path);
1087
1737
  };
@@ -1112,7 +1762,8 @@ const DrawTransforms = {
1112
1762
  resizeLine,
1113
1763
  setLineTexts,
1114
1764
  removeLineText,
1115
- setLineMark
1765
+ setLineMark,
1766
+ insertImage
1116
1767
  };
1117
1768
 
1118
1769
  class GeometryComponent extends CommonPluginElement {
@@ -1130,10 +1781,10 @@ class GeometryComponent extends CommonPluginElement {
1130
1781
  getStrokeWidth: () => {
1131
1782
  const selectedElements = getSelectedElements(this.board);
1132
1783
  if (selectedElements.length === 1 && !isSelectionMoving(this.board)) {
1133
- return DefaultGeometryActiveStyle.strokeWidth;
1784
+ return ACTIVE_STROKE_WIDTH;
1134
1785
  }
1135
1786
  else {
1136
- return DefaultGeometryActiveStyle.selectionStrokeWidth;
1787
+ return ACTIVE_STROKE_WIDTH;
1137
1788
  }
1138
1789
  },
1139
1790
  getStrokeOpacity: () => {
@@ -1196,6 +1847,10 @@ class GeometryComponent extends CommonPluginElement {
1196
1847
  const plugins = this.board.getPluginOptions(WithTextPluginKey).textPlugins;
1197
1848
  const manage = new TextManage(this.board, this.viewContainerRef, {
1198
1849
  getRectangle: () => {
1850
+ const getRectangle = getEngine(this.element.shape).getTextRectangle;
1851
+ if (getRectangle) {
1852
+ return getRectangle(this.element);
1853
+ }
1199
1854
  return getTextRectangle(this.element);
1200
1855
  },
1201
1856
  onValueChangeHandle: (textManageRef) => {
@@ -1223,14 +1878,15 @@ class GeometryComponent extends CommonPluginElement {
1223
1878
  this.destroy$.complete();
1224
1879
  }
1225
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 }); }
1226
- 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 }); }
1227
1882
  }
1228
1883
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: GeometryComponent, decorators: [{
1229
1884
  type: Component,
1230
1885
  args: [{
1231
1886
  selector: 'plait-draw-geometry',
1232
1887
  template: ``,
1233
- changeDetection: ChangeDetectionStrategy.OnPush
1888
+ changeDetection: ChangeDetectionStrategy.OnPush,
1889
+ standalone: true
1234
1890
  }]
1235
1891
  }], ctorParameters: function () { return [{ type: i0.ViewContainerRef }, { type: i0.ChangeDetectorRef }]; } });
1236
1892
 
@@ -1239,16 +1895,8 @@ class LineShapeGenerator extends Generator {
1239
1895
  return true;
1240
1896
  }
1241
1897
  baseDraw(element, data) {
1242
- const shape = element.shape;
1243
1898
  let lineG;
1244
- switch (shape) {
1245
- case LineShape.elbow:
1246
- case LineShape.straight:
1247
- lineG = drawLine(this.board, element);
1248
- break;
1249
- default:
1250
- break;
1251
- }
1899
+ lineG = drawLine(this.board, element);
1252
1900
  return lineG;
1253
1901
  }
1254
1902
  }
@@ -1271,21 +1919,25 @@ class LineActiveGenerator extends Generator {
1271
1919
  if (this.hasResizeHandle) {
1272
1920
  activeG.classList.add('active');
1273
1921
  activeG.classList.add('line-handle');
1274
- const [sourcePoint, targetPoint] = getLineHandlePoints(this.board, element);
1275
- const sourceCircle = drawCircle(PlaitBoard.getRoughSVG(this.board), sourcePoint, RESIZE_HANDLE_DIAMETER, {
1276
- stroke: '#999999',
1277
- strokeWidth: 1,
1278
- fill: '#FFF',
1279
- fillStyle: 'solid'
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);
1280
1931
  });
1281
- const targetCircle = drawCircle(PlaitBoard.getRoughSVG(this.board), targetPoint, RESIZE_HANDLE_DIAMETER, {
1282
- stroke: '#999999',
1283
- strokeWidth: 1,
1284
- fill: '#FFF',
1285
- fillStyle: 'solid'
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);
1286
1940
  });
1287
- activeG.appendChild(targetCircle);
1288
- activeG.appendChild(sourceCircle);
1289
1941
  }
1290
1942
  else {
1291
1943
  const points = getLinePoints(this.board, element);
@@ -1299,6 +1951,35 @@ class LineActiveGenerator extends Generator {
1299
1951
  return activeG;
1300
1952
  }
1301
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
+ }
1302
1983
 
1303
1984
  class LineComponent extends PlaitPluginElementComponent {
1304
1985
  constructor(viewContainerRef, cdr) {
@@ -1438,14 +2119,15 @@ class LineComponent extends PlaitPluginElementComponent {
1438
2119
  this.destroy$.complete();
1439
2120
  }
1440
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 }); }
1441
- 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 }); }
1442
2123
  }
1443
2124
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.3", ngImport: i0, type: LineComponent, decorators: [{
1444
2125
  type: Component,
1445
2126
  args: [{
1446
2127
  selector: 'plait-draw-line',
1447
2128
  template: ``,
1448
- changeDetection: ChangeDetectionStrategy.OnPush
2129
+ changeDetection: ChangeDetectionStrategy.OnPush,
2130
+ standalone: true
1449
2131
  }]
1450
2132
  }], ctorParameters: function () { return [{ type: i0.ViewContainerRef }, { type: i0.ChangeDetectorRef }]; } });
1451
2133
 
@@ -1485,13 +2167,14 @@ const withGeometryCreateByDrag = (board) => {
1485
2167
  geometryShapeG?.remove();
1486
2168
  geometryShapeG = createG();
1487
2169
  const geometryGenerator = new GeometryShapeGenerator(board);
1488
- const isGeometryPointer = PlaitBoard.isInPointer(board, GeometryPointer);
2170
+ const geometryPointers = getGeometryPointers();
2171
+ const isGeometryPointer = PlaitBoard.isInPointer(board, geometryPointers);
1489
2172
  const dragMode = isGeometryPointer && isDndMode(board);
1490
2173
  const movingPoint = transformPoint(board, toPoint(event.x, event.y, PlaitBoard.getHost(board)));
1491
2174
  const pointer = PlaitBoard.getPointer(board);
1492
2175
  if (dragMode) {
1493
2176
  const points = getDefaultGeometryPoints(pointer, movingPoint);
1494
- if (pointer === DrawPointerType.text) {
2177
+ if (pointer === GeometryShape.text) {
1495
2178
  const textG = getTemporaryTextG(movingPoint);
1496
2179
  geometryShapeG.appendChild(textG);
1497
2180
  PlaitBoard.getElementActiveHost(board).append(geometryShapeG);
@@ -1509,12 +2192,13 @@ const withGeometryCreateByDrag = (board) => {
1509
2192
  };
1510
2193
  board.pointerUp = (event) => {
1511
2194
  const pointer = PlaitBoard.getPointer(board);
1512
- const isGeometryPointer = PlaitBoard.isInPointer(board, GeometryPointer);
2195
+ const geometryPointers = getGeometryPointers();
2196
+ const isGeometryPointer = PlaitBoard.isInPointer(board, geometryPointers);
1513
2197
  const dragMode = isGeometryPointer && isDndMode(board);
1514
2198
  if (dragMode) {
1515
2199
  const targetPoint = transformPoint(board, toPoint(event.x, event.y, PlaitBoard.getHost(board)));
1516
2200
  const points = getDefaultGeometryPoints(pointer, targetPoint);
1517
- if (pointer === DrawPointerType.text) {
2201
+ if (pointer === GeometryShape.text) {
1518
2202
  DrawTransforms.insertText(board, points);
1519
2203
  }
1520
2204
  else {
@@ -1529,7 +2213,7 @@ const withGeometryCreateByDrag = (board) => {
1529
2213
  };
1530
2214
  return board;
1531
2215
  };
1532
- const withGeometryCreateByDraw = (board) => {
2216
+ const withGeometryCreateByDrawing = (board) => {
1533
2217
  const { pointerDown, pointerMove, pointerUp, keydown, keyup } = board;
1534
2218
  let start = null;
1535
2219
  let geometryShapeG = null;
@@ -1544,13 +2228,14 @@ const withGeometryCreateByDraw = (board) => {
1544
2228
  keyup(event);
1545
2229
  };
1546
2230
  board.pointerDown = (event) => {
1547
- const isGeometryPointer = PlaitBoard.isInPointer(board, GeometryPointer);
2231
+ const geometryPointers = getGeometryPointers();
2232
+ const isGeometryPointer = PlaitBoard.isInPointer(board, geometryPointers);
1548
2233
  if (isGeometryPointer && isDrawingMode(board)) {
1549
2234
  const point = transformPoint(board, toPoint(event.x, event.y, PlaitBoard.getHost(board)));
1550
2235
  start = point;
1551
2236
  const pointer = PlaitBoard.getPointer(board);
1552
2237
  preventTouchMove(board, event, true);
1553
- if (pointer === DrawPointerType.text) {
2238
+ if (pointer === GeometryShape.text) {
1554
2239
  const points = getDefaultGeometryPoints(pointer, point);
1555
2240
  const textElement = createGeometryElement(GeometryShape.text, points, DefaultTextProperty.text);
1556
2241
  Transforms.insertNode(board, textElement, [board.children.length]);
@@ -1569,7 +2254,7 @@ const withGeometryCreateByDraw = (board) => {
1569
2254
  const drawMode = !!start;
1570
2255
  const movingPoint = transformPoint(board, toPoint(event.x, event.y, PlaitBoard.getHost(board)));
1571
2256
  const pointer = PlaitBoard.getPointer(board);
1572
- if (drawMode && pointer !== DrawPointerType.text) {
2257
+ if (drawMode && pointer !== GeometryShape.text) {
1573
2258
  const points = normalizeShapePoints([start, movingPoint], isShift);
1574
2259
  temporaryElement = createGeometryElement(pointer, points, '', {
1575
2260
  strokeColor: DefaultGeometryProperty.strokeColor,
@@ -1588,7 +2273,7 @@ const withGeometryCreateByDraw = (board) => {
1588
2273
  if (Math.hypot(width, height) === 0) {
1589
2274
  const pointer = PlaitBoard.getPointer(board);
1590
2275
  const points = getDefaultGeometryPoints(pointer, targetPoint);
1591
- if (pointer !== DrawPointerType.text) {
2276
+ if (pointer !== GeometryShape.text) {
1592
2277
  temporaryElement = createGeometryElement(pointer, points, '', {
1593
2278
  strokeColor: DefaultGeometryProperty.strokeColor,
1594
2279
  strokeWidth: DefaultGeometryProperty.strokeWidth
@@ -1612,7 +2297,7 @@ const withGeometryCreateByDraw = (board) => {
1612
2297
  return board;
1613
2298
  };
1614
2299
  const getDefaultGeometryPoints = (pointer, targetPoint) => {
1615
- return pointer === DrawPointerType.text
2300
+ return pointer === GeometryShape.text
1616
2301
  ? getPointsByCenterPoint(targetPoint, DefaultTextProperty.width, DefaultTextProperty.height)
1617
2302
  : getPointsByCenterPoint(targetPoint, DefaultGeometryProperty.width, DefaultGeometryProperty.height);
1618
2303
  };
@@ -1631,7 +2316,7 @@ const getTemporaryTextG = (movingPoint) => {
1631
2316
 
1632
2317
  const buildClipboardData = (board, elements, startPoint) => {
1633
2318
  return elements.map(element => {
1634
- if (PlaitDrawElement.isGeometry(element)) {
2319
+ if (PlaitDrawElement.isGeometry(element) || PlaitDrawElement.isImage(element)) {
1635
2320
  const points = element.points.map(point => [point[0] - startPoint[0], point[1] - startPoint[1]]);
1636
2321
  return { ...element, points };
1637
2322
  }
@@ -1654,7 +2339,7 @@ const buildClipboardData = (board, elements, startPoint) => {
1654
2339
  };
1655
2340
  const insertClipboardData = (board, elements, startPoint) => {
1656
2341
  const lines = elements.filter(value => PlaitDrawElement.isLine(value));
1657
- const geometries = elements.filter(value => PlaitDrawElement.isGeometry(value));
2342
+ const geometries = elements.filter(value => PlaitDrawElement.isGeometry(value) || PlaitDrawElement.isImage(value));
1658
2343
  geometries.forEach(element => {
1659
2344
  const sourceLines = [];
1660
2345
  const targetLines = [];
@@ -1689,8 +2374,17 @@ const withDrawFragment = (baseBoard) => {
1689
2374
  if (drawElements.length) {
1690
2375
  const geometryElements = drawElements.filter(value => PlaitDrawElement.isGeometry(value));
1691
2376
  const lineElements = drawElements.filter(value => PlaitDrawElement.isLine(value));
1692
- const boundLineElements = getBoundedLineElements(board, geometryElements).filter(line => !lineElements.includes(line));
1693
- 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
+ ]);
1694
2388
  }
1695
2389
  return getDeletedFragment(data);
1696
2390
  };
@@ -1726,13 +2420,23 @@ const withDrawFragment = (baseBoard) => {
1726
2420
  return;
1727
2421
  }
1728
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
+ }
1729
2433
  insertFragment(data, targetPoint);
1730
2434
  };
1731
2435
  return board;
1732
2436
  };
1733
- const getBoundedLineElements = (board, geometries) => {
2437
+ const getBoundedLineElements = (board, plaitShapes) => {
1734
2438
  const lines = getBoardLines(board);
1735
- return lines.filter(line => geometries.find(geometry => PlaitLine.isBoundElementOfSource(line, geometry) || PlaitLine.isBoundElementOfTarget(line, geometry)));
2439
+ return lines.filter(line => plaitShapes.find(shape => PlaitLine.isBoundElementOfSource(line, shape) || PlaitLine.isBoundElementOfTarget(line, shape)));
1736
2440
  };
1737
2441
 
1738
2442
  const DefaultLineStyle = {
@@ -1751,10 +2455,10 @@ const getHitGeometryResizeHandleRef = (board, element, point) => {
1751
2455
  const getHitOutlineGeometry = (board, point, offset = 0) => {
1752
2456
  let geometry = null;
1753
2457
  depthFirstRecursion(board, node => {
1754
- if (PlaitDrawElement.isGeometry(node)) {
1755
- const shape = node.shape;
2458
+ if (PlaitDrawElement.isGeometry(node) || PlaitDrawElement.isImage(node)) {
1756
2459
  let client = getRectangleByPoints(node.points);
1757
2460
  client = RectangleClient.getOutlineRectangle(client, offset);
2461
+ const shape = getShape(node);
1758
2462
  const isHit = getEngine(shape).isHit(client, point);
1759
2463
  if (isHit) {
1760
2464
  geometry = node;
@@ -1772,7 +2476,8 @@ const withLineCreateByDraw = (board) => {
1772
2476
  let lineShapeG = null;
1773
2477
  let temporaryElement = null;
1774
2478
  board.pointerDown = (event) => {
1775
- const isLinePointer = PlaitBoard.isPointer(board, DrawPointerType.line);
2479
+ const linePointers = getLinePointers();
2480
+ const isLinePointer = PlaitBoard.isInPointer(board, linePointers);
1776
2481
  if (isLinePointer && isDrawingMode(board)) {
1777
2482
  const point = transformPoint(board, toPoint(event.x, event.y, PlaitBoard.getHost(board)));
1778
2483
  start = point;
@@ -1794,7 +2499,8 @@ const withLineCreateByDraw = (board) => {
1794
2499
  targetRef.connection = hitElement ? transformPointToConnection(board, movingPoint, hitElement) : undefined;
1795
2500
  targetRef.boundId = hitElement ? hitElement.id : undefined;
1796
2501
  const lineGenerator = new LineShapeGenerator(board);
1797
- 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 }, {
1798
2504
  strokeColor: DefaultLineStyle.strokeColor,
1799
2505
  strokeWidth: DefaultLineStyle.strokeWidth
1800
2506
  });
@@ -1839,12 +2545,12 @@ const withGeometryResize = (board) => {
1839
2545
  return true;
1840
2546
  },
1841
2547
  detect: (point) => {
1842
- const selectedGeometryElements = getSelectedGeometryElements(board);
1843
- if (selectedGeometryElements.length !== 1 || getSelectedElements(board).length !== 1) {
2548
+ const selectedElements = [...getSelectedGeometryElements(board), ...getSelectedImageElements(board)];
2549
+ if (selectedElements.length !== 1 || getSelectedElements(board).length !== 1) {
1844
2550
  return null;
1845
2551
  }
1846
- const target = selectedGeometryElements[0];
1847
- const targetComponent = PlaitElement.getComponent(selectedGeometryElements[0]);
2552
+ const target = selectedElements[0];
2553
+ const targetComponent = PlaitElement.getComponent(selectedElements[0]);
1848
2554
  if (targetComponent.activeGenerator.hasResizeHandle) {
1849
2555
  const handleRef = getHitGeometryResizeHandleRef(board, target, point);
1850
2556
  if (handleRef) {
@@ -1873,15 +2579,20 @@ const withGeometryResize = (board) => {
1873
2579
  if (resizeRef.handle === ResizeHandle.sw) {
1874
2580
  points = [resizeState.endTransformPoint, [resizeRef.element.points[1][0], resizeRef.element.points[0][1]]];
1875
2581
  }
1876
- if (isShift) {
2582
+ if (isShift || PlaitDrawElement.isImage(resizeRef.element)) {
1877
2583
  const rectangle = getRectangleByPoints(points);
1878
2584
  const factor = points[0][1] > points[1][1] ? 1 : -1;
1879
2585
  const height = rectangle.width * ratio * factor;
1880
2586
  points = [[resizeState.endTransformPoint[0], points[1][1] + height], points[1]];
1881
2587
  }
1882
- points = normalizeShapePoints(points);
1883
- const { height: textHeight } = PlaitGeometry.getTextManage(resizeRef.element).getSize();
1884
- DrawTransforms.resizeGeometry(board, points, textHeight, resizeRef.path);
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);
2595
+ }
1885
2596
  }
1886
2597
  };
1887
2598
  withResize(board, options);
@@ -1892,33 +2603,44 @@ var LineResizeHandle;
1892
2603
  (function (LineResizeHandle) {
1893
2604
  LineResizeHandle["source"] = "source";
1894
2605
  LineResizeHandle["target"] = "target";
2606
+ LineResizeHandle["addHandle"] = "addHandle";
1895
2607
  })(LineResizeHandle || (LineResizeHandle = {}));
1896
2608
  const getHitLineResizeHandleRef = (board, element, point) => {
1897
- const [sourcePoint, targetPoint] = getLineHandlePoints(board, element);
1898
- const sourceRectangle = {
1899
- x: sourcePoint[0] - RESIZE_HANDLE_DIAMETER / 2,
1900
- y: sourcePoint[1] - RESIZE_HANDLE_DIAMETER / 2,
1901
- width: RESIZE_HANDLE_DIAMETER,
1902
- height: RESIZE_HANDLE_DIAMETER
1903
- };
1904
- const targetRectangle = {
1905
- x: targetPoint[0] - RESIZE_HANDLE_DIAMETER / 2,
1906
- y: targetPoint[1] - RESIZE_HANDLE_DIAMETER / 2,
1907
- width: RESIZE_HANDLE_DIAMETER,
1908
- height: RESIZE_HANDLE_DIAMETER
1909
- };
1910
- const isHitSourceRectangle = RectangleClient.isHit(RectangleClient.toRectangleClient([point, point]), sourceRectangle);
1911
- const isHitTargetRectangle = RectangleClient.isHit(RectangleClient.toRectangleClient([point, point]), targetRectangle);
1912
- if (isHitSourceRectangle) {
1913
- 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 };
1914
2619
  }
1915
- if (isHitTargetRectangle) {
1916
- 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 };
1917
2624
  }
1918
2625
  return undefined;
1919
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
+ }
1920
2641
 
1921
2642
  const withLineResize = (board) => {
2643
+ let pointIndex = 0;
1922
2644
  const options = {
1923
2645
  key: 'draw-line',
1924
2646
  canResize: () => {
@@ -1935,6 +2657,7 @@ const withLineResize = (board) => {
1935
2657
  element: value,
1936
2658
  handle: handleRef.handle
1937
2659
  };
2660
+ pointIndex = handleRef.index;
1938
2661
  }
1939
2662
  });
1940
2663
  return result;
@@ -1946,17 +2669,23 @@ const withLineResize = (board) => {
1946
2669
  let source = { ...resizeRef.element.source };
1947
2670
  let target = { ...resizeRef.element.target };
1948
2671
  if (resizeRef.handle === LineResizeHandle.source) {
1949
- points[0] = resizeState.endTransformPoint;
2672
+ points[pointIndex] = resizeState.endTransformPoint;
1950
2673
  const hitElement = getHitOutlineGeometry(board, resizeState.endTransformPoint, -4);
1951
2674
  source.connection = hitElement ? transformPointToConnection(board, resizeState.endTransformPoint, hitElement) : undefined;
1952
2675
  source.boundId = hitElement ? hitElement.id : undefined;
1953
2676
  }
1954
- if (resizeRef.handle === LineResizeHandle.target) {
1955
- points[1] = resizeState.endTransformPoint;
2677
+ else if (resizeRef.handle === LineResizeHandle.target) {
2678
+ points[pointIndex] = resizeState.endTransformPoint;
1956
2679
  const hitElement = getHitOutlineGeometry(board, resizeState.endTransformPoint, -4);
1957
2680
  target.connection = hitElement ? transformPointToConnection(board, resizeState.endTransformPoint, hitElement) : undefined;
1958
2681
  target.boundId = hitElement ? hitElement.id : undefined;
1959
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
+ }
1960
2689
  DrawTransforms.resizeLine(board, { points, source, target }, resizeRef.path);
1961
2690
  }
1962
2691
  };
@@ -1969,9 +2698,14 @@ const withLineBoundReaction = (board) => {
1969
2698
  let boundShapeG = null;
1970
2699
  board.pointerMove = (event) => {
1971
2700
  boundShapeG?.remove();
1972
- const isLinePointer = PlaitBoard.isPointer(board, DrawPointerType.line);
2701
+ const linePointers = Object.keys(LineShape);
2702
+ const isLinePointer = PlaitBoard.isInPointer(board, linePointers);
1973
2703
  const movingPoint = transformPoint(board, toPoint(event.x, event.y, PlaitBoard.getHost(board)));
1974
- 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
+ });
1975
2709
  if (isLinePointer || isLineResizing) {
1976
2710
  const hitElement = getHitOutlineGeometry(board, movingPoint, -4);
1977
2711
  if (hitElement) {
@@ -2049,8 +2783,74 @@ function editHandle(board, element, manageIndex, isFirstEdit = false) {
2049
2783
  });
2050
2784
  }
2051
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
+
2052
2852
  const withDraw = (board) => {
2053
- const { drawElement, getRectangle, isHitSelection, isMovable, dblclick } = board;
2853
+ const { drawElement, getRectangle, isHitSelection, isMovable, isAlign } = board;
2054
2854
  board.drawElement = (context) => {
2055
2855
  if (PlaitDrawElement.isGeometry(context.element)) {
2056
2856
  return GeometryComponent;
@@ -2058,6 +2858,9 @@ const withDraw = (board) => {
2058
2858
  else if (PlaitDrawElement.isLine(context.element)) {
2059
2859
  return LineComponent;
2060
2860
  }
2861
+ else if (PlaitDrawElement.isImage(context.element)) {
2862
+ return ImageComponent;
2863
+ }
2061
2864
  return drawElement(context);
2062
2865
  };
2063
2866
  board.getRectangle = (element) => {
@@ -2068,6 +2871,9 @@ const withDraw = (board) => {
2068
2871
  const points = getLinePoints(board, element);
2069
2872
  return getRectangleByPoints(points);
2070
2873
  }
2874
+ if (PlaitDrawElement.isImage(element)) {
2875
+ return getRectangleByPoints(element.points);
2876
+ }
2071
2877
  return getRectangle(element);
2072
2878
  };
2073
2879
  board.isHitSelection = (element, range) => {
@@ -2080,6 +2886,11 @@ const withDraw = (board) => {
2080
2886
  }
2081
2887
  return RectangleClient.isHit(rangeRectangle, client);
2082
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
+ }
2083
2894
  if (PlaitDrawElement.isLine(element)) {
2084
2895
  const points = getLinePoints(board, element);
2085
2896
  const strokeWidth = getStrokeWidthByElement(element);
@@ -2098,17 +2909,26 @@ const withDraw = (board) => {
2098
2909
  if (PlaitDrawElement.isGeometry(element)) {
2099
2910
  return true;
2100
2911
  }
2912
+ if (PlaitDrawElement.isImage(element)) {
2913
+ return true;
2914
+ }
2101
2915
  if (PlaitDrawElement.isLine(element)) {
2102
2916
  return !element.source.boundId && !element.target.boundId;
2103
2917
  }
2104
2918
  return isMovable(element);
2105
2919
  };
2106
- return withLineText(withLineBoundReaction(withLineResize(withGeometryResize(withLineCreateByDraw(withGeometryCreateByDrag(withGeometryCreateByDraw(withDrawFragment(withDrawHotkey(board)))))))));
2920
+ board.isAlign = (element) => {
2921
+ if (PlaitDrawElement.isGeometry(element) || PlaitDrawElement.isImage(element)) {
2922
+ return true;
2923
+ }
2924
+ return isAlign(element);
2925
+ };
2926
+ return withLineText(withLineBoundReaction(withLineResize(withGeometryResize(withLineCreateByDraw(withGeometryCreateByDrag(withGeometryCreateByDrawing(withDrawFragment(withDrawHotkey(board)))))))));
2107
2927
  };
2108
2928
 
2109
2929
  /**
2110
2930
  * Generated bundle index. Do not edit.
2111
2931
  */
2112
2932
 
2113
- 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, getDirectionByBoundElementAndConnection, getEdgeOnPolygonByPoint, getElbowPoints, getFillByElement, getHitConnectorPoint, getHitLineTextIndex, getLineDashByElement, getLineHandlePoints, getLineHandleRefPair, getLinePoints, getLineTextRectangle, getNearestPoint, getPointsByCenterPoint, getSelectedDrawElements, getSelectedGeometryElements, getSelectedLineElements, getStraightPoints, getStrokeColorByElement, getStrokeStyleByElement, getStrokeWidthByElement, getTextRectangle, isHitLineText, isHitPolyLine, transformPointToConnection, withDraw };
2933
+ 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 };
2114
2934
  //# sourceMappingURL=plait-draw.mjs.map