@plait/draw 0.1.0-next.10
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.
- package/README.md +3 -0
- package/constants/geometry.d.ts +27 -0
- package/constants/index.d.ts +2 -0
- package/constants/line.d.ts +4 -0
- package/constants/pointer.d.ts +10 -0
- package/esm2020/constants/geometry.mjs +29 -0
- package/esm2020/constants/index.mjs +3 -0
- package/esm2020/constants/line.mjs +5 -0
- package/esm2020/constants/pointer.mjs +19 -0
- package/esm2020/generators/geometry-shape.generator.mjs +23 -0
- package/esm2020/generators/line-active.generator.mjs +36 -0
- package/esm2020/generators/line.generator.mjs +22 -0
- package/esm2020/geometry.component.mjs +130 -0
- package/esm2020/interfaces/element.mjs +6 -0
- package/esm2020/interfaces/geometry.mjs +23 -0
- package/esm2020/interfaces/index.mjs +25 -0
- package/esm2020/interfaces/line.mjs +48 -0
- package/esm2020/interfaces/text.mjs +2 -0
- package/esm2020/line.component.mjs +152 -0
- package/esm2020/plait-draw.mjs +5 -0
- package/esm2020/plugins/with-draw-fragment.mjs +52 -0
- package/esm2020/plugins/with-draw-hotkey.mjs +33 -0
- package/esm2020/plugins/with-draw.mjs +73 -0
- package/esm2020/plugins/with-geometry-create.mjs +160 -0
- package/esm2020/plugins/with-geometry-resize.mjs +73 -0
- package/esm2020/plugins/with-line-bound-reaction.mjs +43 -0
- package/esm2020/plugins/with-line-create.mjs +66 -0
- package/esm2020/plugins/with-line-resize.mjs +52 -0
- package/esm2020/plugins/with-line-text.mjs +55 -0
- package/esm2020/public-api.mjs +8 -0
- package/esm2020/transforms/geometry-text.mjs +54 -0
- package/esm2020/transforms/geometry.mjs +31 -0
- package/esm2020/transforms/index.mjs +15 -0
- package/esm2020/transforms/line.mjs +23 -0
- package/esm2020/utils/clipboard.mjs +53 -0
- package/esm2020/utils/engine/diamond.mjs +22 -0
- package/esm2020/utils/engine/ellipse.mjs +55 -0
- package/esm2020/utils/engine/index.mjs +18 -0
- package/esm2020/utils/engine/parallelogram.mjs +32 -0
- package/esm2020/utils/engine/rectangle.mjs +18 -0
- package/esm2020/utils/engine/round-rectangle.mjs +49 -0
- package/esm2020/utils/geometry.mjs +84 -0
- package/esm2020/utils/index.mjs +5 -0
- package/esm2020/utils/line-arrow.mjs +89 -0
- package/esm2020/utils/line.mjs +169 -0
- package/esm2020/utils/position/geometry.mjs +28 -0
- package/esm2020/utils/position/line.mjs +34 -0
- package/esm2020/utils/selected.mjs +15 -0
- package/esm2020/utils/style/index.mjs +2 -0
- package/esm2020/utils/style/stroke.mjs +24 -0
- package/fesm2015/plait-draw.mjs +1765 -0
- package/fesm2015/plait-draw.mjs.map +1 -0
- package/fesm2020/plait-draw.mjs +1786 -0
- package/fesm2020/plait-draw.mjs.map +1 -0
- package/generators/geometry-shape.generator.d.ts +8 -0
- package/generators/line-active.generator.d.ts +9 -0
- package/generators/line.generator.d.ts +8 -0
- package/geometry.component.d.ts +27 -0
- package/index.d.ts +5 -0
- package/interfaces/element.d.ts +4 -0
- package/interfaces/geometry.d.ts +44 -0
- package/interfaces/index.d.ts +14 -0
- package/interfaces/line.d.ts +59 -0
- package/interfaces/text.d.ts +5 -0
- package/line.component.d.ts +36 -0
- package/package.json +31 -0
- package/plugins/with-draw-fragment.d.ts +2 -0
- package/plugins/with-draw-hotkey.d.ts +2 -0
- package/plugins/with-draw.d.ts +2 -0
- package/plugins/with-geometry-create.d.ts +3 -0
- package/plugins/with-geometry-resize.d.ts +2 -0
- package/plugins/with-line-bound-reaction.d.ts +2 -0
- package/plugins/with-line-create.d.ts +2 -0
- package/plugins/with-line-resize.d.ts +2 -0
- package/plugins/with-line-text.d.ts +2 -0
- package/public-api.d.ts +7 -0
- package/styles/styles.scss +16 -0
- package/transforms/geometry-text.d.ts +5 -0
- package/transforms/geometry.d.ts +6 -0
- package/transforms/index.d.ts +11 -0
- package/transforms/line.d.ts +6 -0
- package/utils/clipboard.d.ts +4 -0
- package/utils/engine/diamond.d.ts +2 -0
- package/utils/engine/ellipse.d.ts +4 -0
- package/utils/engine/index.d.ts +3 -0
- package/utils/engine/parallelogram.d.ts +4 -0
- package/utils/engine/rectangle.d.ts +2 -0
- package/utils/engine/round-rectangle.d.ts +5 -0
- package/utils/geometry.d.ts +16 -0
- package/utils/index.d.ts +4 -0
- package/utils/line-arrow.d.ts +4 -0
- package/utils/line.d.ts +17 -0
- package/utils/position/geometry.d.ts +13 -0
- package/utils/position/line.d.ts +10 -0
- package/utils/selected.d.ts +5 -0
- package/utils/style/index.d.ts +1 -0
- package/utils/style/stroke.d.ts +6 -0
|
@@ -0,0 +1,1786 @@
|
|
|
1
|
+
import { PlaitElement, ACTIVE_STROKE_WIDTH, RectangleClient, PlaitBoard, setStrokeLinecap, isPointInPolygon, getNearestPointBetweenPointAndSegments, isPointInEllipse, drawRectangle, drawRoundRectangle, isPointInRoundRectangle, idCreator, createG, SELECTION_BORDER_COLOR, SELECTION_FILL_COLOR, drawCircle, arrowPoints, createPath, drawLinearPath, distanceBetweenPointAndSegments, createMask, createRect, getElementById, findElements, getSelectedElements, Transforms, clearSelectedElement, addSelectedElement, PlaitNode, Point, isSelectionMoving, PlaitPluginElementComponent, transformPoint, toPoint, BoardTransforms, PlaitPointerType, preventTouchMove, createForeignObject, setClipboardData, getDataFromClipboard, depthFirstRecursion, getIsRecursionFunc, getHitElements, isPolylineHitRectangle } from '@plait/core';
|
|
2
|
+
import * as i0 from '@angular/core';
|
|
3
|
+
import { Component, ChangeDetectionStrategy } from '@angular/core';
|
|
4
|
+
import { Subject } from 'rxjs';
|
|
5
|
+
import { getRectangleByPoints, getFactorByPoints, Direction, getDirectionByPoint, getPoints, getPointOnPolyline, getDirectionFactor, Generator, normalizeShapePoints, CommonPluginElement, ActiveGenerator, WithTextPluginKey, RESIZE_HANDLE_DIAMETER, isVirtualKey, isDelete, isSpaceHotkey, isDndMode, isDrawingMode, getRectangleResizeHandleRefs, ResizeHandle, withResize, isResizingByCondition, getRatioByPoint } from '@plait/common';
|
|
6
|
+
import { Alignment, buildText, AlignEditor, TextManage, DEFAULT_FONT_SIZE, getTextFromClipboard, getTextSize } from '@plait/text';
|
|
7
|
+
import { isKeyHotkey } from 'is-hotkey';
|
|
8
|
+
import { Node } from 'slate';
|
|
9
|
+
|
|
10
|
+
var GeometryShape;
|
|
11
|
+
(function (GeometryShape) {
|
|
12
|
+
GeometryShape["rectangle"] = "rectangle";
|
|
13
|
+
GeometryShape["ellipse"] = "ellipse";
|
|
14
|
+
GeometryShape["diamond"] = "diamond";
|
|
15
|
+
GeometryShape["roundRectangle"] = "roundRectangle";
|
|
16
|
+
GeometryShape["parallelogram"] = "parallelogram";
|
|
17
|
+
GeometryShape["text"] = "text";
|
|
18
|
+
})(GeometryShape || (GeometryShape = {}));
|
|
19
|
+
const PlaitGeometry = {
|
|
20
|
+
getTextEditor(element) {
|
|
21
|
+
return PlaitGeometry.getTextManage(element).componentRef.instance.editor;
|
|
22
|
+
},
|
|
23
|
+
getTextManage(element) {
|
|
24
|
+
const component = PlaitElement.getComponent(element);
|
|
25
|
+
if (component) {
|
|
26
|
+
return component.textManage;
|
|
27
|
+
}
|
|
28
|
+
throw new Error('can not get correctly component in get text editor');
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
var LineMarkerType;
|
|
33
|
+
(function (LineMarkerType) {
|
|
34
|
+
LineMarkerType["arrow"] = "arrow";
|
|
35
|
+
LineMarkerType["none"] = "none";
|
|
36
|
+
LineMarkerType["openTriangle"] = "open-triangle";
|
|
37
|
+
LineMarkerType["solidTriangle"] = "solid-triangle";
|
|
38
|
+
LineMarkerType["sharpArrow"] = "sharp-arrow";
|
|
39
|
+
})(LineMarkerType || (LineMarkerType = {}));
|
|
40
|
+
var LineShape;
|
|
41
|
+
(function (LineShape) {
|
|
42
|
+
LineShape["straight"] = "straight";
|
|
43
|
+
LineShape["curve"] = "curve";
|
|
44
|
+
LineShape["elbow"] = "elbow";
|
|
45
|
+
})(LineShape || (LineShape = {}));
|
|
46
|
+
var LineHandleKey;
|
|
47
|
+
(function (LineHandleKey) {
|
|
48
|
+
LineHandleKey["source"] = "source";
|
|
49
|
+
LineHandleKey["target"] = "target";
|
|
50
|
+
})(LineHandleKey || (LineHandleKey = {}));
|
|
51
|
+
const PlaitLine = {
|
|
52
|
+
getTextEditors(element) {
|
|
53
|
+
const component = PlaitElement.getComponent(element);
|
|
54
|
+
if (component) {
|
|
55
|
+
const manage = component.textManages.find(manage => manage.isEditing);
|
|
56
|
+
if (manage) {
|
|
57
|
+
return [manage.componentRef.instance.editor];
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
return component.textManages.map(manage => manage.componentRef.instance.editor);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
throw new Error('can not get correctly component in get text editor');
|
|
64
|
+
},
|
|
65
|
+
isSourceMark(line, markType) {
|
|
66
|
+
return line.source.marker === markType;
|
|
67
|
+
},
|
|
68
|
+
isTargetMark(line, markType) {
|
|
69
|
+
return line.target.marker === markType;
|
|
70
|
+
},
|
|
71
|
+
isBoundElementOfSource(line, element) {
|
|
72
|
+
return line.source.boundId === element.id;
|
|
73
|
+
},
|
|
74
|
+
isBoundElementOfTarget(line, element) {
|
|
75
|
+
return line.target.boundId === element.id;
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
var StrokeStyle;
|
|
80
|
+
(function (StrokeStyle) {
|
|
81
|
+
StrokeStyle["solid"] = "solid";
|
|
82
|
+
StrokeStyle["dashed"] = "dashed";
|
|
83
|
+
})(StrokeStyle || (StrokeStyle = {}));
|
|
84
|
+
|
|
85
|
+
const PlaitDrawElement = {
|
|
86
|
+
isGeometry: (value) => {
|
|
87
|
+
return value.type === 'geometry';
|
|
88
|
+
},
|
|
89
|
+
isLine: (value) => {
|
|
90
|
+
return value.type === 'line';
|
|
91
|
+
},
|
|
92
|
+
isText: (value) => {
|
|
93
|
+
return value.type === 'geometry' && value.shape === GeometryShape.text;
|
|
94
|
+
},
|
|
95
|
+
isDrawElement: (value) => {
|
|
96
|
+
if (PlaitDrawElement.isGeometry(value) || PlaitDrawElement.isLine(value)) {
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const ShapeDefaultSpace = {
|
|
106
|
+
rectangleAndText: 4
|
|
107
|
+
};
|
|
108
|
+
const DefaultGeometryStyle = {
|
|
109
|
+
strokeWidth: 2,
|
|
110
|
+
defaultRadius: 4,
|
|
111
|
+
strokeColor: '#000',
|
|
112
|
+
fill: 'none'
|
|
113
|
+
};
|
|
114
|
+
const DefaultGeometryActiveStyle = {
|
|
115
|
+
strokeWidth: ACTIVE_STROKE_WIDTH,
|
|
116
|
+
selectionStrokeWidth: ACTIVE_STROKE_WIDTH
|
|
117
|
+
};
|
|
118
|
+
const DefaultGeometryProperty = {
|
|
119
|
+
width: 100,
|
|
120
|
+
height: 100,
|
|
121
|
+
strokeColor: '#333',
|
|
122
|
+
strokeWidth: 2
|
|
123
|
+
};
|
|
124
|
+
const DefaultTextProperty = {
|
|
125
|
+
width: 36,
|
|
126
|
+
height: 20,
|
|
127
|
+
text: '文本'
|
|
128
|
+
};
|
|
129
|
+
const GeometryThreshold = {
|
|
130
|
+
defaultTextMaxWidth: 34 * 14,
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
var DrawPointerType;
|
|
134
|
+
(function (DrawPointerType) {
|
|
135
|
+
DrawPointerType["text"] = "text";
|
|
136
|
+
DrawPointerType["rectangle"] = "rectangle";
|
|
137
|
+
DrawPointerType["line"] = "line";
|
|
138
|
+
DrawPointerType["diamond"] = "diamond";
|
|
139
|
+
DrawPointerType["roundRectangle"] = "roundRectangle";
|
|
140
|
+
DrawPointerType["parallelogram"] = "parallelogram";
|
|
141
|
+
DrawPointerType["ellipse"] = "ellipse";
|
|
142
|
+
})(DrawPointerType || (DrawPointerType = {}));
|
|
143
|
+
const GeometryPointer = [
|
|
144
|
+
DrawPointerType.rectangle,
|
|
145
|
+
DrawPointerType.text,
|
|
146
|
+
DrawPointerType.diamond,
|
|
147
|
+
DrawPointerType.ellipse,
|
|
148
|
+
DrawPointerType.parallelogram,
|
|
149
|
+
DrawPointerType.roundRectangle
|
|
150
|
+
];
|
|
151
|
+
|
|
152
|
+
const getStrokeWidthByElement = (element) => {
|
|
153
|
+
if (PlaitDrawElement.isText(element)) {
|
|
154
|
+
return 0;
|
|
155
|
+
}
|
|
156
|
+
const strokeWidth = element.strokeWidth || DefaultGeometryStyle.strokeWidth;
|
|
157
|
+
return strokeWidth;
|
|
158
|
+
};
|
|
159
|
+
const getStrokeColorByElement = (element) => {
|
|
160
|
+
const strokeColor = element.strokeColor || DefaultGeometryStyle.strokeColor;
|
|
161
|
+
return strokeColor;
|
|
162
|
+
};
|
|
163
|
+
const getFillByElement = (element) => {
|
|
164
|
+
const fill = element.fill || DefaultGeometryStyle.fill;
|
|
165
|
+
return fill;
|
|
166
|
+
};
|
|
167
|
+
const getLineDashByElement = (element) => {
|
|
168
|
+
return element.strokeStyle === 'dashed' ? [8, 8 + getStrokeWidthByElement(element)] : undefined;
|
|
169
|
+
};
|
|
170
|
+
const getStrokeStyleByElement = (element) => {
|
|
171
|
+
return element.strokeStyle || StrokeStyle.solid;
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const DiamondEngine = {
|
|
175
|
+
draw(board, rectangle, options) {
|
|
176
|
+
const points = RectangleClient.getEdgeCenterPoints(rectangle);
|
|
177
|
+
const rs = PlaitBoard.getRoughSVG(board);
|
|
178
|
+
const polygon = rs.polygon(points, { ...options, fillStyle: 'solid' });
|
|
179
|
+
setStrokeLinecap(polygon, 'round');
|
|
180
|
+
return polygon;
|
|
181
|
+
},
|
|
182
|
+
isHit(rectangle, point) {
|
|
183
|
+
const controlPoints = RectangleClient.getEdgeCenterPoints(rectangle);
|
|
184
|
+
return isPointInPolygon(point, controlPoints);
|
|
185
|
+
},
|
|
186
|
+
getNearestPoint(rectangle, point) {
|
|
187
|
+
const connectorPoints = RectangleClient.getEdgeCenterPoints(rectangle);
|
|
188
|
+
return getNearestPointBetweenPointAndSegments(point, connectorPoints);
|
|
189
|
+
},
|
|
190
|
+
getConnectorPoints(rectangle) {
|
|
191
|
+
return RectangleClient.getEdgeCenterPoints(rectangle);
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
const EllipseEngine = {
|
|
196
|
+
draw(board, rectangle, options) {
|
|
197
|
+
const centerPoint = [rectangle.x + rectangle.width / 2, rectangle.y + rectangle.height / 2];
|
|
198
|
+
const rs = PlaitBoard.getRoughSVG(board);
|
|
199
|
+
return rs.ellipse(centerPoint[0], centerPoint[1], rectangle.width, rectangle.height, { ...options, fillStyle: 'solid' });
|
|
200
|
+
},
|
|
201
|
+
isHit(rectangle, point) {
|
|
202
|
+
const centerPoint = [rectangle.x + rectangle.width / 2, rectangle.y + rectangle.height / 2];
|
|
203
|
+
return isPointInEllipse(point, centerPoint, rectangle.width / 2, rectangle.height / 2);
|
|
204
|
+
},
|
|
205
|
+
getNearestPoint(rectangle, point) {
|
|
206
|
+
const centerPoint = [rectangle.x + rectangle.width / 2, rectangle.y + rectangle.height / 2];
|
|
207
|
+
return getNearestPointBetweenPointAndEllipse(point, centerPoint, rectangle.width / 2, rectangle.height / 2);
|
|
208
|
+
},
|
|
209
|
+
getConnectorPoints(rectangle) {
|
|
210
|
+
return RectangleClient.getEdgeCenterPoints(rectangle);
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
function getNearestPointBetweenPointAndEllipse(point, center, rx, ry, rotation = 0) {
|
|
214
|
+
const rectangleClient = {
|
|
215
|
+
x: center[0] - rx,
|
|
216
|
+
y: center[1] - ry,
|
|
217
|
+
height: ry * 2,
|
|
218
|
+
width: rx * 2
|
|
219
|
+
};
|
|
220
|
+
// https://stackoverflow.com/a/46007540/232122
|
|
221
|
+
const px = Math.abs(point[0] - rectangleClient.x - rectangleClient.width / 2);
|
|
222
|
+
const py = Math.abs(point[1] - rectangleClient.y - rectangleClient.height / 2);
|
|
223
|
+
let tx = 0.707;
|
|
224
|
+
let ty = 0.707;
|
|
225
|
+
const a = Math.abs(rectangleClient.width) / 2;
|
|
226
|
+
const b = Math.abs(rectangleClient.height) / 2;
|
|
227
|
+
[0, 1, 2, 3].forEach(x => {
|
|
228
|
+
const xx = a * tx;
|
|
229
|
+
const yy = b * ty;
|
|
230
|
+
const ex = ((a * a - b * b) * tx ** 3) / a;
|
|
231
|
+
const ey = ((b * b - a * a) * ty ** 3) / b;
|
|
232
|
+
const rx = xx - ex;
|
|
233
|
+
const ry = yy - ey;
|
|
234
|
+
const qx = px - ex;
|
|
235
|
+
const qy = py - ey;
|
|
236
|
+
const r = Math.hypot(ry, rx);
|
|
237
|
+
const q = Math.hypot(qy, qx);
|
|
238
|
+
tx = Math.min(1, Math.max(0, ((qx * r) / q + ex) / a));
|
|
239
|
+
ty = Math.min(1, Math.max(0, ((qy * r) / q + ey) / b));
|
|
240
|
+
const t = Math.hypot(ty, tx);
|
|
241
|
+
tx /= t;
|
|
242
|
+
ty /= t;
|
|
243
|
+
});
|
|
244
|
+
const signX = point[0] > center[0] ? 1 : -1;
|
|
245
|
+
const signY = point[1] > center[1] ? 1 : -1;
|
|
246
|
+
return [center[0] + a * tx * signX, center[1] + b * ty * signY];
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const ParallelogramEngine = {
|
|
250
|
+
draw(board, rectangle, options) {
|
|
251
|
+
const points = getParallelogramPoints(rectangle);
|
|
252
|
+
const rs = PlaitBoard.getRoughSVG(board);
|
|
253
|
+
const polygon = rs.polygon(points, { ...options, fillStyle: 'solid' });
|
|
254
|
+
setStrokeLinecap(polygon, 'round');
|
|
255
|
+
return polygon;
|
|
256
|
+
},
|
|
257
|
+
isHit(rectangle, point) {
|
|
258
|
+
const parallelogramPoints = getParallelogramPoints(rectangle);
|
|
259
|
+
return isPointInPolygon(point, parallelogramPoints);
|
|
260
|
+
},
|
|
261
|
+
getNearestPoint(rectangle, point) {
|
|
262
|
+
const cornerPoints = getParallelogramPoints(rectangle);
|
|
263
|
+
return getNearestPointBetweenPointAndSegments(point, cornerPoints);
|
|
264
|
+
},
|
|
265
|
+
getConnectorPoints(rectangle) {
|
|
266
|
+
const cornerPoints = getParallelogramPoints(rectangle);
|
|
267
|
+
return getCenterPointsOnPolygon(cornerPoints);
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
const getParallelogramPoints = (rectangle) => {
|
|
271
|
+
return [
|
|
272
|
+
[rectangle.x + rectangle.width / 4, rectangle.y],
|
|
273
|
+
[rectangle.x + rectangle.width, rectangle.y],
|
|
274
|
+
[rectangle.x + (rectangle.width * 3) / 4, rectangle.y + rectangle.height],
|
|
275
|
+
[rectangle.x, rectangle.y + rectangle.height]
|
|
276
|
+
];
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
const RectangleEngine = {
|
|
280
|
+
draw(board, rectangle, options) {
|
|
281
|
+
return drawRectangle(board, rectangle, { ...options, fillStyle: 'solid' });
|
|
282
|
+
},
|
|
283
|
+
isHit(rectangle, point) {
|
|
284
|
+
const rangeRectangle = RectangleClient.toRectangleClient([point, point]);
|
|
285
|
+
return RectangleClient.isHit(rectangle, rangeRectangle);
|
|
286
|
+
},
|
|
287
|
+
getNearestPoint(rectangle, point) {
|
|
288
|
+
const cornerPoints = RectangleClient.getCornerPoints(rectangle);
|
|
289
|
+
return getNearestPointBetweenPointAndSegments(point, cornerPoints);
|
|
290
|
+
},
|
|
291
|
+
getConnectorPoints(rectangle) {
|
|
292
|
+
return RectangleClient.getEdgeCenterPoints(rectangle);
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
const RoundRectangleEngine = {
|
|
297
|
+
draw(board, rectangle, options) {
|
|
298
|
+
return drawRoundRectangle(PlaitBoard.getRoughSVG(board), rectangle.x, rectangle.y, rectangle.x + rectangle.width, rectangle.y + rectangle.height, { ...options, fillStyle: 'solid' }, false, getRoundRectangleRadius(rectangle));
|
|
299
|
+
},
|
|
300
|
+
isHit(rectangle, point) {
|
|
301
|
+
return isPointInRoundRectangle(point, rectangle, getRoundRectangleRadius(rectangle));
|
|
302
|
+
},
|
|
303
|
+
getNearestPoint(rectangle, point) {
|
|
304
|
+
return getNearestPointBetweenPointAndRoundRectangle(point, rectangle, getRoundRectangleRadius(rectangle));
|
|
305
|
+
},
|
|
306
|
+
getConnectorPoints(rectangle) {
|
|
307
|
+
return RectangleClient.getEdgeCenterPoints(rectangle);
|
|
308
|
+
}
|
|
309
|
+
};
|
|
310
|
+
const getRoundRectangleRadius = (rectangle) => {
|
|
311
|
+
return Math.min(rectangle.width * 0.1, rectangle.height * 0.1);
|
|
312
|
+
};
|
|
313
|
+
function getNearestPointBetweenPointAndRoundRectangle(point, rectangle, radius) {
|
|
314
|
+
const { x: rectX, y: rectY, width, height } = rectangle;
|
|
315
|
+
const cornerPoints = RectangleClient.getCornerPoints(rectangle);
|
|
316
|
+
let result = getNearestPointBetweenPointAndSegments(point, cornerPoints);
|
|
317
|
+
let circleCenter = null;
|
|
318
|
+
const inLeftTop = point[0] >= rectX && point[0] <= rectX + radius && point[1] >= rectY && point[1] <= rectY + radius;
|
|
319
|
+
if (inLeftTop) {
|
|
320
|
+
circleCenter = [rectX + radius, rectY + radius];
|
|
321
|
+
}
|
|
322
|
+
const inLeftBottom = point[0] >= rectX && point[0] <= rectX + radius && point[1] >= rectY + height && point[1] <= rectY + height - radius;
|
|
323
|
+
if (inLeftBottom) {
|
|
324
|
+
circleCenter = [rectX + radius, rectY + height - radius];
|
|
325
|
+
}
|
|
326
|
+
const inRightTop = point[0] >= rectX + width - radius && point[0] <= rectX + width && point[1] >= rectY && point[1] <= rectY + radius;
|
|
327
|
+
if (inRightTop) {
|
|
328
|
+
circleCenter = [rectX + width - radius, rectY + radius];
|
|
329
|
+
}
|
|
330
|
+
const inRightBottom = point[0] >= rectX + width - radius &&
|
|
331
|
+
point[0] <= rectX + width &&
|
|
332
|
+
point[1] >= rectY + height - radius &&
|
|
333
|
+
point[1] <= rectY + height;
|
|
334
|
+
if (inRightBottom) {
|
|
335
|
+
circleCenter = [rectX + width - radius, rectY + height - radius];
|
|
336
|
+
}
|
|
337
|
+
if (circleCenter) {
|
|
338
|
+
result = getNearestPointBetweenPointAndEllipse(point, circleCenter, radius, radius);
|
|
339
|
+
}
|
|
340
|
+
return result;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const ShapeEngineMap = {
|
|
344
|
+
[GeometryShape.rectangle]: RectangleEngine,
|
|
345
|
+
[GeometryShape.diamond]: DiamondEngine,
|
|
346
|
+
[GeometryShape.ellipse]: EllipseEngine,
|
|
347
|
+
[GeometryShape.parallelogram]: ParallelogramEngine,
|
|
348
|
+
[GeometryShape.roundRectangle]: RoundRectangleEngine,
|
|
349
|
+
[GeometryShape.text]: RectangleEngine
|
|
350
|
+
};
|
|
351
|
+
const getEngine = (shape) => {
|
|
352
|
+
return ShapeEngineMap[shape];
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
const createGeometryElement = (shape, points, text, options) => {
|
|
356
|
+
let textOptions = {};
|
|
357
|
+
let alignment = Alignment.center;
|
|
358
|
+
if (shape === GeometryShape.text) {
|
|
359
|
+
textOptions = { autoSize: true };
|
|
360
|
+
alignment = undefined;
|
|
361
|
+
}
|
|
362
|
+
return {
|
|
363
|
+
id: idCreator(),
|
|
364
|
+
type: 'geometry',
|
|
365
|
+
shape,
|
|
366
|
+
angle: 0,
|
|
367
|
+
opacity: 1,
|
|
368
|
+
textHeight: DefaultTextProperty.height,
|
|
369
|
+
text: buildText(text, alignment),
|
|
370
|
+
points,
|
|
371
|
+
...textOptions,
|
|
372
|
+
...options
|
|
373
|
+
};
|
|
374
|
+
};
|
|
375
|
+
const getPointsByCenterPoint = (point, width, height) => {
|
|
376
|
+
const leftTopPoint = [point[0] - width / 2, point[1] - height / 2];
|
|
377
|
+
const rightBottomPoint = [point[0] + width / 2, point[1] + height / 2];
|
|
378
|
+
return [leftTopPoint, rightBottomPoint];
|
|
379
|
+
};
|
|
380
|
+
const getTextRectangle = (element) => {
|
|
381
|
+
const elementRectangle = getRectangleByPoints(element.points);
|
|
382
|
+
const strokeWidth = getStrokeWidthByElement(element);
|
|
383
|
+
const height = element.textHeight;
|
|
384
|
+
const width = elementRectangle.width - ShapeDefaultSpace.rectangleAndText * 2 - strokeWidth * 2;
|
|
385
|
+
return {
|
|
386
|
+
height,
|
|
387
|
+
width: width > 0 ? width : 0,
|
|
388
|
+
x: elementRectangle.x + ShapeDefaultSpace.rectangleAndText + strokeWidth,
|
|
389
|
+
y: elementRectangle.y + (elementRectangle.height - height) / 2
|
|
390
|
+
};
|
|
391
|
+
};
|
|
392
|
+
const drawBoundMask = (board, element) => {
|
|
393
|
+
const G = createG();
|
|
394
|
+
const rectangle = getRectangleByPoints(element.points);
|
|
395
|
+
const activeRectangle = RectangleClient.inflate(rectangle, ACTIVE_STROKE_WIDTH);
|
|
396
|
+
const maskG = drawGeometry(board, activeRectangle, element.shape, {
|
|
397
|
+
stroke: SELECTION_BORDER_COLOR,
|
|
398
|
+
strokeWidth: 1,
|
|
399
|
+
fill: SELECTION_FILL_COLOR,
|
|
400
|
+
fillStyle: 'solid'
|
|
401
|
+
});
|
|
402
|
+
G.appendChild(maskG);
|
|
403
|
+
const connectorPoints = getEngine(element.shape).getConnectorPoints(activeRectangle);
|
|
404
|
+
connectorPoints.forEach(point => {
|
|
405
|
+
const circleG = drawCircle(PlaitBoard.getRoughSVG(board), point, 6, {
|
|
406
|
+
stroke: '#999999',
|
|
407
|
+
strokeWidth: 1,
|
|
408
|
+
fill: '#FFF',
|
|
409
|
+
fillStyle: 'solid'
|
|
410
|
+
});
|
|
411
|
+
G.appendChild(circleG);
|
|
412
|
+
});
|
|
413
|
+
return G;
|
|
414
|
+
};
|
|
415
|
+
const drawGeometry = (board, outerRectangle, shape, options) => {
|
|
416
|
+
return getEngine(shape).draw(board, outerRectangle, options);
|
|
417
|
+
};
|
|
418
|
+
const getNearestPoint = (element, point, inflateDelta = 0) => {
|
|
419
|
+
const rectangle = getRectangleByPoints(element.points);
|
|
420
|
+
const activeRectangle = RectangleClient.inflate(rectangle, inflateDelta);
|
|
421
|
+
return getEngine(element.shape).getNearestPoint(activeRectangle, point);
|
|
422
|
+
};
|
|
423
|
+
const getCenterPointsOnPolygon = (points) => {
|
|
424
|
+
const centerPoint = [];
|
|
425
|
+
for (let i = 0; i < points.length; i++) {
|
|
426
|
+
let j = i == points.length - 1 ? 0 : i + 1;
|
|
427
|
+
centerPoint.push([(points[i][0] + points[j][0]) / 2, (points[i][1] + points[j][1]) / 2]);
|
|
428
|
+
}
|
|
429
|
+
return centerPoint;
|
|
430
|
+
};
|
|
431
|
+
|
|
432
|
+
const drawLineArrow = (element, points, options) => {
|
|
433
|
+
const arrowG = createG();
|
|
434
|
+
if (PlaitLine.isSourceMark(element, LineMarkerType.none) && PlaitLine.isTargetMark(element, LineMarkerType.none)) {
|
|
435
|
+
return null;
|
|
436
|
+
}
|
|
437
|
+
if (!PlaitLine.isSourceMark(element, LineMarkerType.none)) {
|
|
438
|
+
const sourceArrow = getArrow(element, element.source.marker, points[1], points[0], options);
|
|
439
|
+
sourceArrow && arrowG.appendChild(sourceArrow);
|
|
440
|
+
}
|
|
441
|
+
if (!PlaitLine.isTargetMark(element, LineMarkerType.none)) {
|
|
442
|
+
const arrow = getArrow(element, element.target.marker, points[points.length - 2], points[points.length - 1], options);
|
|
443
|
+
arrow && arrowG.appendChild(arrow);
|
|
444
|
+
}
|
|
445
|
+
return arrowG;
|
|
446
|
+
};
|
|
447
|
+
const getArrow = (element, marker, source, target, options) => {
|
|
448
|
+
let targetArrow;
|
|
449
|
+
switch (marker) {
|
|
450
|
+
case LineMarkerType.openTriangle: {
|
|
451
|
+
targetArrow = drawOpenTriangle(element, source, target, options);
|
|
452
|
+
break;
|
|
453
|
+
}
|
|
454
|
+
case LineMarkerType.solidTriangle: {
|
|
455
|
+
targetArrow = drawSolidTriangle(source, target, options);
|
|
456
|
+
break;
|
|
457
|
+
}
|
|
458
|
+
case LineMarkerType.arrow: {
|
|
459
|
+
targetArrow = drawArrow(element, source, target, options);
|
|
460
|
+
break;
|
|
461
|
+
}
|
|
462
|
+
case LineMarkerType.sharpArrow: {
|
|
463
|
+
targetArrow = drawSharpArrow(source, target, options);
|
|
464
|
+
break;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
return targetArrow;
|
|
468
|
+
};
|
|
469
|
+
const drawSharpArrow = (source, target, options) => {
|
|
470
|
+
const directionFactor = getFactorByPoints(source, target);
|
|
471
|
+
const startPoint = target;
|
|
472
|
+
// const startPoint: Point = [
|
|
473
|
+
// target[0],
|
|
474
|
+
// target[1]
|
|
475
|
+
// ];
|
|
476
|
+
const { pointLeft, pointRight } = arrowPoints(source, target, 12, 20);
|
|
477
|
+
const g = createG();
|
|
478
|
+
const path = createPath();
|
|
479
|
+
let polylinePath = `M${pointRight[0]},${pointRight[1]}A8,8,20,0,1,${pointLeft[0]},${pointLeft[1]}L${startPoint[0]},${startPoint[1]}Z`;
|
|
480
|
+
path.setAttribute('d', polylinePath);
|
|
481
|
+
path.setAttribute('stroke', `${options?.stroke}`);
|
|
482
|
+
path.setAttribute('stroke-width', `${options?.strokeWidth}`);
|
|
483
|
+
path.setAttribute('fill', `${options?.stroke}`);
|
|
484
|
+
g.appendChild(path);
|
|
485
|
+
return g;
|
|
486
|
+
};
|
|
487
|
+
const drawArrow = (element, source, target, options) => {
|
|
488
|
+
const directionFactor = getFactorByPoints(source, target);
|
|
489
|
+
const strokeWidth = getStrokeWidthByElement(element);
|
|
490
|
+
const endPoint = [
|
|
491
|
+
target[0] + strokeWidth * directionFactor.x / 2,
|
|
492
|
+
target[1] + strokeWidth * directionFactor.y / 2
|
|
493
|
+
];
|
|
494
|
+
const middlePoint = [endPoint[0] - 8 * directionFactor.x, endPoint[1] - 8 * directionFactor.y];
|
|
495
|
+
const { pointLeft, pointRight } = arrowPoints(source, endPoint, 12, 30);
|
|
496
|
+
const arrowG = drawLinearPath([pointLeft, endPoint, pointRight, middlePoint], { ...options, fill: options.stroke }, true);
|
|
497
|
+
const path = arrowG.querySelector('path');
|
|
498
|
+
path.setAttribute('stroke-linejoin', 'round');
|
|
499
|
+
return arrowG;
|
|
500
|
+
};
|
|
501
|
+
const drawSolidTriangle = (source, target, options) => {
|
|
502
|
+
const endPoint = target;
|
|
503
|
+
const { pointLeft, pointRight } = arrowPoints(source, endPoint, 12, 30);
|
|
504
|
+
return drawLinearPath([pointLeft, endPoint, pointRight], { ...options, fill: options.stroke }, true);
|
|
505
|
+
};
|
|
506
|
+
const drawOpenTriangle = (element, source, target, options) => {
|
|
507
|
+
const directionFactor = getFactorByPoints(source, target);
|
|
508
|
+
const strokeWidth = getStrokeWidthByElement(element);
|
|
509
|
+
const endPoint = [
|
|
510
|
+
target[0] + strokeWidth * directionFactor.x / 2,
|
|
511
|
+
target[1] + strokeWidth * directionFactor.y / 2
|
|
512
|
+
];
|
|
513
|
+
const { pointLeft, pointRight } = arrowPoints(source, endPoint, 12, 40);
|
|
514
|
+
return drawLinearPath([pointLeft, endPoint, pointRight], options);
|
|
515
|
+
};
|
|
516
|
+
|
|
517
|
+
const createLineElement = (shape, points, source, target, options) => {
|
|
518
|
+
return {
|
|
519
|
+
id: idCreator(),
|
|
520
|
+
type: 'line',
|
|
521
|
+
shape,
|
|
522
|
+
source,
|
|
523
|
+
texts: [],
|
|
524
|
+
target,
|
|
525
|
+
opacity: 1,
|
|
526
|
+
points,
|
|
527
|
+
...options
|
|
528
|
+
};
|
|
529
|
+
};
|
|
530
|
+
const getLinePoints = (board, element) => {
|
|
531
|
+
return element.shape === LineShape.elbow ? getElbowPoints(board, element) : getStraightPoints(board, element);
|
|
532
|
+
};
|
|
533
|
+
const getStraightPoints = (board, element) => {
|
|
534
|
+
return [getSourcePoint(board, element), getTargetPoint(board, element)];
|
|
535
|
+
};
|
|
536
|
+
const getElbowPoints = (board, element) => {
|
|
537
|
+
if (element.points.length === 2) {
|
|
538
|
+
const source = getSourcePoint(board, element);
|
|
539
|
+
const target = getTargetPoint(board, element);
|
|
540
|
+
let sourceDirection = source[0] < target[0] ? Direction.right : Direction.left;
|
|
541
|
+
let targetDirection = source[0] < target[0] ? Direction.left : Direction.right;
|
|
542
|
+
if (element.source.connection) {
|
|
543
|
+
sourceDirection = getDirectionByPoint(element.source.connection, sourceDirection);
|
|
544
|
+
}
|
|
545
|
+
if (element.target.connection) {
|
|
546
|
+
targetDirection = getDirectionByPoint(element.target.connection, targetDirection);
|
|
547
|
+
}
|
|
548
|
+
const points = getPoints(source, sourceDirection, target, targetDirection, 30);
|
|
549
|
+
return points;
|
|
550
|
+
}
|
|
551
|
+
return element.points;
|
|
552
|
+
};
|
|
553
|
+
const isHitPolyLine = (pathPoints, point, strokeWidth, expand = 0) => {
|
|
554
|
+
const distance = distanceBetweenPointAndSegments(pathPoints, point);
|
|
555
|
+
return distance <= strokeWidth + expand;
|
|
556
|
+
};
|
|
557
|
+
const getHitLineTextIndex = (board, element, point) => {
|
|
558
|
+
const texts = element.texts;
|
|
559
|
+
if (!texts.length)
|
|
560
|
+
return -1;
|
|
561
|
+
const points = getElbowPoints(board, element);
|
|
562
|
+
return texts.findIndex(text => {
|
|
563
|
+
const center = getPointOnPolyline(points, text.position);
|
|
564
|
+
const rectangle = {
|
|
565
|
+
x: center[0] - text.width / 2,
|
|
566
|
+
y: center[1] - text.height / 2,
|
|
567
|
+
width: text.width,
|
|
568
|
+
height: text.height
|
|
569
|
+
};
|
|
570
|
+
return RectangleClient.isHit(rectangle, RectangleClient.toRectangleClient([point, point]));
|
|
571
|
+
});
|
|
572
|
+
};
|
|
573
|
+
const isHitLineText = (board, element, point) => {
|
|
574
|
+
return getHitLineTextIndex(board, element, point) !== -1;
|
|
575
|
+
};
|
|
576
|
+
const drawLine = (board, element) => {
|
|
577
|
+
const strokeWidth = getStrokeWidthByElement(element);
|
|
578
|
+
const strokeColor = getStrokeColorByElement(element);
|
|
579
|
+
const strokeLineDash = getLineDashByElement(element);
|
|
580
|
+
const options = { stroke: strokeColor, strokeWidth, strokeLineDash };
|
|
581
|
+
const lineG = createG();
|
|
582
|
+
const points = getLinePoints(board, element);
|
|
583
|
+
const line = drawLinearPath(points, options);
|
|
584
|
+
const id = idCreator();
|
|
585
|
+
line.setAttribute('mask', `url(#${id})`);
|
|
586
|
+
lineG.appendChild(line);
|
|
587
|
+
const { mask, maskTargetFillRect } = drawMask(board, element, id);
|
|
588
|
+
lineG.appendChild(mask);
|
|
589
|
+
line.appendChild(maskTargetFillRect);
|
|
590
|
+
const arrow = drawLineArrow(element, points, { stroke: strokeColor, strokeWidth });
|
|
591
|
+
arrow && lineG.appendChild(arrow);
|
|
592
|
+
return lineG;
|
|
593
|
+
};
|
|
594
|
+
function drawMask(board, element, id) {
|
|
595
|
+
const mask = createMask();
|
|
596
|
+
mask.setAttribute('id', id);
|
|
597
|
+
const points = getLinePoints(board, element);
|
|
598
|
+
let rectangle = getRectangleByPoints(points);
|
|
599
|
+
rectangle = RectangleClient.getOutlineRectangle(rectangle, -30);
|
|
600
|
+
const maskFillRect = createRect(rectangle, {
|
|
601
|
+
fill: 'white'
|
|
602
|
+
});
|
|
603
|
+
mask.appendChild(maskFillRect);
|
|
604
|
+
const texts = element.texts;
|
|
605
|
+
texts.forEach((text, index) => {
|
|
606
|
+
const textRectangle = getLineTextRectangle(board, element, index);
|
|
607
|
+
const rect = createRect(textRectangle, {
|
|
608
|
+
fill: 'black'
|
|
609
|
+
});
|
|
610
|
+
mask.appendChild(rect);
|
|
611
|
+
});
|
|
612
|
+
//撑开 line
|
|
613
|
+
const maskTargetFillRect = createRect(rectangle);
|
|
614
|
+
maskTargetFillRect.setAttribute('opacity', '0');
|
|
615
|
+
return { mask, maskTargetFillRect };
|
|
616
|
+
}
|
|
617
|
+
const getSourcePoint = (board, element) => {
|
|
618
|
+
if (element.source.boundId) {
|
|
619
|
+
const strokeWidth = getStrokeWidthByElement(element);
|
|
620
|
+
const connectionOffset = PlaitLine.isSourceMark(element, LineMarkerType.none) ? 0 : strokeWidth;
|
|
621
|
+
const boundElement = getElementById(board, element.source.boundId);
|
|
622
|
+
return boundElement ? getConnectionPoint(boundElement, element.source.connection, connectionOffset) : element.points[0];
|
|
623
|
+
}
|
|
624
|
+
return element.points[0];
|
|
625
|
+
};
|
|
626
|
+
const getTargetPoint = (board, element) => {
|
|
627
|
+
if (element.target.boundId) {
|
|
628
|
+
const strokeWidth = getStrokeWidthByElement(element);
|
|
629
|
+
const connectionOffset = PlaitLine.isTargetMark(element, LineMarkerType.none) ? 0 : strokeWidth;
|
|
630
|
+
const boundElement = getElementById(board, element.target.boundId);
|
|
631
|
+
return boundElement
|
|
632
|
+
? getConnectionPoint(boundElement, element.target.connection, connectionOffset)
|
|
633
|
+
: element.points[element.points.length - 1];
|
|
634
|
+
}
|
|
635
|
+
return element.points[element.points.length - 1];
|
|
636
|
+
};
|
|
637
|
+
const getConnectionPoint = (geometry, connection, offset) => {
|
|
638
|
+
const rectangle = getRectangleByPoints(geometry.points);
|
|
639
|
+
const directionFactor = getDirectionFactor(getDirectionByPoint(connection, Direction.bottom));
|
|
640
|
+
return [
|
|
641
|
+
rectangle.x + rectangle.width * connection[0] + directionFactor.x * offset,
|
|
642
|
+
rectangle.y + rectangle.height * connection[1] + directionFactor.y * offset
|
|
643
|
+
];
|
|
644
|
+
};
|
|
645
|
+
const transformPointToConnection = (board, point, hitElement) => {
|
|
646
|
+
let rectangle = getRectangleByPoints(hitElement.points);
|
|
647
|
+
rectangle = RectangleClient.inflate(rectangle, ACTIVE_STROKE_WIDTH);
|
|
648
|
+
let nearestPoint = getNearestPoint(hitElement, point, ACTIVE_STROKE_WIDTH);
|
|
649
|
+
const hitConnector = getHitConnectorPoint(nearestPoint, hitElement, rectangle);
|
|
650
|
+
nearestPoint = hitConnector ? hitConnector : nearestPoint;
|
|
651
|
+
return [(nearestPoint[0] - rectangle.x) / rectangle.width, (nearestPoint[1] - rectangle.y) / rectangle.height];
|
|
652
|
+
};
|
|
653
|
+
const getHitConnectorPoint = (movingPoint, hitElement, rectangle) => {
|
|
654
|
+
const connector = getEngine(hitElement.shape).getConnectorPoints(rectangle);
|
|
655
|
+
const points = getPointsByCenterPoint(movingPoint, 5, 5);
|
|
656
|
+
const pointRectangle = getRectangleByPoints(points);
|
|
657
|
+
return connector.find(point => {
|
|
658
|
+
return RectangleClient.isHit(pointRectangle, RectangleClient.toRectangleClient([point, point]));
|
|
659
|
+
});
|
|
660
|
+
};
|
|
661
|
+
const getLineTextRectangle = (board, element, index) => {
|
|
662
|
+
const text = element.texts[index];
|
|
663
|
+
const elbowPoints = getLinePoints(board, element);
|
|
664
|
+
const point = getPointOnPolyline(elbowPoints, text.position);
|
|
665
|
+
return {
|
|
666
|
+
x: point[0] - text.width / 2,
|
|
667
|
+
y: point[1] - text.height / 2,
|
|
668
|
+
width: text.width,
|
|
669
|
+
height: text.height
|
|
670
|
+
};
|
|
671
|
+
};
|
|
672
|
+
const getBoardLines = (board) => {
|
|
673
|
+
return findElements(board, {
|
|
674
|
+
match: (element) => PlaitDrawElement.isLine(element),
|
|
675
|
+
recursion: (element) => PlaitDrawElement.isDrawElement(element)
|
|
676
|
+
});
|
|
677
|
+
};
|
|
678
|
+
|
|
679
|
+
const getSelectedDrawElements = (board) => {
|
|
680
|
+
const selectedElements = getSelectedElements(board).filter(value => PlaitDrawElement.isDrawElement(value));
|
|
681
|
+
return selectedElements;
|
|
682
|
+
};
|
|
683
|
+
const getSelectedGeometryElements = (board) => {
|
|
684
|
+
const selectedElements = getSelectedElements(board).filter(value => PlaitDrawElement.isGeometry(value));
|
|
685
|
+
return selectedElements;
|
|
686
|
+
};
|
|
687
|
+
const getSelectedLineElements = (board) => {
|
|
688
|
+
const selectedElements = getSelectedElements(board).filter(value => PlaitDrawElement.isLine(value));
|
|
689
|
+
return selectedElements;
|
|
690
|
+
};
|
|
691
|
+
|
|
692
|
+
class GeometryShapeGenerator extends Generator {
|
|
693
|
+
canDraw(element, data) {
|
|
694
|
+
return true;
|
|
695
|
+
}
|
|
696
|
+
baseDraw(element, data) {
|
|
697
|
+
const rectangle = getRectangleByPoints(element.points);
|
|
698
|
+
const shape = element.shape;
|
|
699
|
+
if (shape === GeometryShape.text) {
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
const strokeWidth = getStrokeWidthByElement(element);
|
|
703
|
+
const strokeColor = getStrokeColorByElement(element);
|
|
704
|
+
const fill = getFillByElement(element);
|
|
705
|
+
const strokeLineDash = getLineDashByElement(element);
|
|
706
|
+
return drawGeometry(this.board, RectangleClient.inflate(rectangle, -strokeWidth), shape, { stroke: strokeColor, strokeWidth, fill, strokeLineDash });
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
const insertGeometry = (board, points, shape) => {
|
|
711
|
+
let newElement = createGeometryElement(shape, points, '', {
|
|
712
|
+
strokeColor: DefaultGeometryProperty.strokeColor,
|
|
713
|
+
strokeWidth: DefaultGeometryProperty.strokeWidth
|
|
714
|
+
});
|
|
715
|
+
Transforms.insertNode(board, newElement, [board.children.length]);
|
|
716
|
+
clearSelectedElement(board);
|
|
717
|
+
addSelectedElement(board, newElement);
|
|
718
|
+
};
|
|
719
|
+
const insertText = (board, points, text = '文本') => {
|
|
720
|
+
let newElement = createGeometryElement(GeometryShape.text, points, text);
|
|
721
|
+
Transforms.insertNode(board, newElement, [board.children.length]);
|
|
722
|
+
clearSelectedElement(board);
|
|
723
|
+
addSelectedElement(board, newElement);
|
|
724
|
+
};
|
|
725
|
+
const resizeGeometry = (board, points, textHeight, path) => {
|
|
726
|
+
const normalizePoints = normalizeShapePoints(points);
|
|
727
|
+
const element = PlaitNode.get(board, path);
|
|
728
|
+
const newHeight = textHeight / board.viewport.zoom;
|
|
729
|
+
const newProperties = { points: normalizePoints, textHeight: newHeight };
|
|
730
|
+
if (PlaitDrawElement.isText(element) && element.autoSize) {
|
|
731
|
+
newProperties.autoSize = false;
|
|
732
|
+
}
|
|
733
|
+
Transforms.setNode(board, newProperties, path);
|
|
734
|
+
};
|
|
735
|
+
|
|
736
|
+
const normalizePoints = (board, element, width, textHeight) => {
|
|
737
|
+
let points = element.points;
|
|
738
|
+
let autoSize = element.autoSize;
|
|
739
|
+
const defaultSpace = ShapeDefaultSpace.rectangleAndText;
|
|
740
|
+
if (autoSize) {
|
|
741
|
+
const editor = PlaitGeometry.getTextEditor(element);
|
|
742
|
+
if (AlignEditor.isActive(editor, Alignment.right)) {
|
|
743
|
+
points = [
|
|
744
|
+
[points[1][0] - (width + defaultSpace * 2), points[0][1]],
|
|
745
|
+
[points[1][0], points[0][1] + textHeight]
|
|
746
|
+
];
|
|
747
|
+
}
|
|
748
|
+
else if (AlignEditor.isActive(editor, Alignment.center)) {
|
|
749
|
+
const oldWidth = Math.abs(points[0][0] - points[1][0]);
|
|
750
|
+
const offset = (width - oldWidth) / 2;
|
|
751
|
+
points = [
|
|
752
|
+
[points[0][0] - offset - defaultSpace, points[0][1]],
|
|
753
|
+
[points[1][0] + offset + defaultSpace, points[0][1] + textHeight]
|
|
754
|
+
];
|
|
755
|
+
}
|
|
756
|
+
else {
|
|
757
|
+
points = [points[0], [points[0][0] + width + defaultSpace * 2, points[0][1] + textHeight]];
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
return { points };
|
|
761
|
+
};
|
|
762
|
+
const setText = (board, element, text, width, textHeight) => {
|
|
763
|
+
const newElement = {
|
|
764
|
+
text,
|
|
765
|
+
textHeight,
|
|
766
|
+
...normalizePoints(board, element, width, textHeight)
|
|
767
|
+
};
|
|
768
|
+
const path = board.children.findIndex(child => child === element);
|
|
769
|
+
Transforms.setNode(board, newElement, [path]);
|
|
770
|
+
};
|
|
771
|
+
const setTextSize = (board, element, textWidth, textHeight) => {
|
|
772
|
+
if (element.autoSize) {
|
|
773
|
+
const newElement = {
|
|
774
|
+
textHeight,
|
|
775
|
+
...normalizePoints(board, element, textWidth, textHeight)
|
|
776
|
+
};
|
|
777
|
+
const isPointsEqual = Point.isEquals(element.points[0], newElement.points[0]) && Point.isEquals(element.points[1], newElement.points[1]);
|
|
778
|
+
const isTextHeightEqual = Math.round(textHeight) === Math.round(element.textHeight);
|
|
779
|
+
if (!isPointsEqual || !isTextHeightEqual) {
|
|
780
|
+
const path = board.children.findIndex(child => child === element);
|
|
781
|
+
Transforms.setNode(board, newElement, [path]);
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
};
|
|
785
|
+
|
|
786
|
+
const resizeLine = (board, options, path) => {
|
|
787
|
+
Transforms.setNode(board, options, path);
|
|
788
|
+
};
|
|
789
|
+
const setLineTexts = (board, element, texts) => {
|
|
790
|
+
const path = PlaitBoard.findPath(board, element);
|
|
791
|
+
Transforms.setNode(board, { texts }, path);
|
|
792
|
+
};
|
|
793
|
+
const removeLineText = (board, element, index) => {
|
|
794
|
+
const path = PlaitBoard.findPath(board, element);
|
|
795
|
+
const texts = element.texts?.length ? [...element.texts] : [];
|
|
796
|
+
const newTexts = [...texts];
|
|
797
|
+
newTexts.splice(index, 1);
|
|
798
|
+
Transforms.setNode(board, { texts: newTexts }, path);
|
|
799
|
+
};
|
|
800
|
+
const setLineMark = (board, element, handleKey, marker) => {
|
|
801
|
+
const path = PlaitBoard.findPath(board, element);
|
|
802
|
+
let handle = handleKey === LineHandleKey.source ? element.source : element.target;
|
|
803
|
+
handle = { ...handle, marker };
|
|
804
|
+
Transforms.setNode(board, { [handleKey]: handle }, path);
|
|
805
|
+
};
|
|
806
|
+
|
|
807
|
+
const DrawTransforms = {
|
|
808
|
+
setText,
|
|
809
|
+
insertGeometry,
|
|
810
|
+
resizeGeometry,
|
|
811
|
+
insertText,
|
|
812
|
+
setTextSize,
|
|
813
|
+
resizeLine,
|
|
814
|
+
setLineTexts,
|
|
815
|
+
removeLineText,
|
|
816
|
+
setLineMark
|
|
817
|
+
};
|
|
818
|
+
|
|
819
|
+
class GeometryComponent extends CommonPluginElement {
|
|
820
|
+
get textManage() {
|
|
821
|
+
return this.getTextManages()[0];
|
|
822
|
+
}
|
|
823
|
+
constructor(viewContainerRef, cdr) {
|
|
824
|
+
super(cdr);
|
|
825
|
+
this.viewContainerRef = viewContainerRef;
|
|
826
|
+
this.cdr = cdr;
|
|
827
|
+
this.destroy$ = new Subject();
|
|
828
|
+
}
|
|
829
|
+
initializeGenerator() {
|
|
830
|
+
this.activeGenerator = new ActiveGenerator(this.board, {
|
|
831
|
+
getStrokeWidth: () => {
|
|
832
|
+
const selectedElements = getSelectedElements(this.board);
|
|
833
|
+
if (selectedElements.length === 1 && !isSelectionMoving(this.board)) {
|
|
834
|
+
return DefaultGeometryActiveStyle.strokeWidth;
|
|
835
|
+
}
|
|
836
|
+
else {
|
|
837
|
+
return DefaultGeometryActiveStyle.selectionStrokeWidth;
|
|
838
|
+
}
|
|
839
|
+
},
|
|
840
|
+
getStrokeOpacity: () => {
|
|
841
|
+
const selectedElements = getSelectedElements(this.board);
|
|
842
|
+
if (selectedElements.length === 1 && !isSelectionMoving(this.board)) {
|
|
843
|
+
return 1;
|
|
844
|
+
}
|
|
845
|
+
else {
|
|
846
|
+
return 0.5;
|
|
847
|
+
}
|
|
848
|
+
},
|
|
849
|
+
getRectangle: (element) => {
|
|
850
|
+
return getRectangleByPoints(element.points);
|
|
851
|
+
},
|
|
852
|
+
hasResizeHandle: () => {
|
|
853
|
+
const selectedElements = getSelectedElements(this.board);
|
|
854
|
+
if (PlaitBoard.hasBeenTextEditing(this.board) && PlaitDrawElement.isText(this.element)) {
|
|
855
|
+
return false;
|
|
856
|
+
}
|
|
857
|
+
return selectedElements.length === 1 && !isSelectionMoving(this.board);
|
|
858
|
+
}
|
|
859
|
+
});
|
|
860
|
+
this.shapeGenerator = new GeometryShapeGenerator(this.board);
|
|
861
|
+
this.initializeTextManage();
|
|
862
|
+
}
|
|
863
|
+
ngOnInit() {
|
|
864
|
+
super.ngOnInit();
|
|
865
|
+
this.initializeGenerator();
|
|
866
|
+
this.shapeGenerator.draw(this.element, this.g);
|
|
867
|
+
this.activeGenerator.draw(this.element, this.g, { selected: this.selected });
|
|
868
|
+
this.drawText();
|
|
869
|
+
}
|
|
870
|
+
onContextChanged(value, previous) {
|
|
871
|
+
if (value.element !== previous.element) {
|
|
872
|
+
this.shapeGenerator.draw(this.element, this.g);
|
|
873
|
+
this.activeGenerator.draw(this.element, this.g, { selected: this.selected });
|
|
874
|
+
this.updateText();
|
|
875
|
+
}
|
|
876
|
+
else {
|
|
877
|
+
const hasSameSelected = value.selected === previous.selected;
|
|
878
|
+
const hasSameHandleState = this.activeGenerator.options.hasResizeHandle() === this.activeGenerator.hasResizeHandle;
|
|
879
|
+
if (!hasSameSelected || !hasSameHandleState) {
|
|
880
|
+
this.activeGenerator.draw(this.element, this.g, { selected: this.selected });
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
editText() {
|
|
885
|
+
this.textManage.edit();
|
|
886
|
+
this.activeGenerator.draw(this.element, this.g, { selected: this.selected });
|
|
887
|
+
}
|
|
888
|
+
drawText() {
|
|
889
|
+
this.textManage.draw(this.element.text);
|
|
890
|
+
this.g.append(this.textManage.g);
|
|
891
|
+
}
|
|
892
|
+
updateText() {
|
|
893
|
+
this.textManage.updateText(this.element.text);
|
|
894
|
+
this.textManage.updateRectangle();
|
|
895
|
+
}
|
|
896
|
+
initializeTextManage() {
|
|
897
|
+
const plugins = this.board.getPluginOptions(WithTextPluginKey).textPlugins;
|
|
898
|
+
const manage = new TextManage(this.board, this.viewContainerRef, {
|
|
899
|
+
getRectangle: () => {
|
|
900
|
+
return getTextRectangle(this.element);
|
|
901
|
+
},
|
|
902
|
+
onValueChangeHandle: (textManageRef) => {
|
|
903
|
+
const height = textManageRef.height / this.board.viewport.zoom;
|
|
904
|
+
const width = textManageRef.width / this.board.viewport.zoom;
|
|
905
|
+
if (textManageRef.newValue) {
|
|
906
|
+
DrawTransforms.setText(this.board, this.element, textManageRef.newValue, width, height);
|
|
907
|
+
}
|
|
908
|
+
else {
|
|
909
|
+
DrawTransforms.setTextSize(this.board, this.element, width, height);
|
|
910
|
+
}
|
|
911
|
+
},
|
|
912
|
+
getMaxWidth: () => {
|
|
913
|
+
const width = getTextRectangle(this.element).width;
|
|
914
|
+
return this.element?.autoSize ? GeometryThreshold.defaultTextMaxWidth : width;
|
|
915
|
+
},
|
|
916
|
+
textPlugins: plugins
|
|
917
|
+
});
|
|
918
|
+
this.initializeTextManages([manage]);
|
|
919
|
+
}
|
|
920
|
+
ngOnDestroy() {
|
|
921
|
+
super.ngOnDestroy();
|
|
922
|
+
this.textManage.destroy();
|
|
923
|
+
this.destroy$.next();
|
|
924
|
+
this.destroy$.complete();
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
GeometryComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: GeometryComponent, deps: [{ token: i0.ViewContainerRef }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
928
|
+
GeometryComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.2", type: GeometryComponent, selector: "plait-draw-geometry", usesInheritance: true, ngImport: i0, template: ``, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
929
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: GeometryComponent, decorators: [{
|
|
930
|
+
type: Component,
|
|
931
|
+
args: [{
|
|
932
|
+
selector: 'plait-draw-geometry',
|
|
933
|
+
template: ``,
|
|
934
|
+
changeDetection: ChangeDetectionStrategy.OnPush
|
|
935
|
+
}]
|
|
936
|
+
}], ctorParameters: function () { return [{ type: i0.ViewContainerRef }, { type: i0.ChangeDetectorRef }]; } });
|
|
937
|
+
|
|
938
|
+
class LineShapeGenerator extends Generator {
|
|
939
|
+
canDraw(element, data) {
|
|
940
|
+
return true;
|
|
941
|
+
}
|
|
942
|
+
baseDraw(element, data) {
|
|
943
|
+
const shape = element.shape;
|
|
944
|
+
let lineG;
|
|
945
|
+
switch (shape) {
|
|
946
|
+
case LineShape.elbow:
|
|
947
|
+
case LineShape.straight:
|
|
948
|
+
lineG = drawLine(this.board, element);
|
|
949
|
+
break;
|
|
950
|
+
default:
|
|
951
|
+
break;
|
|
952
|
+
}
|
|
953
|
+
return lineG;
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
class LineActiveGenerator extends Generator {
|
|
958
|
+
canDraw(element, data) {
|
|
959
|
+
if (data.selected) {
|
|
960
|
+
return true;
|
|
961
|
+
}
|
|
962
|
+
else {
|
|
963
|
+
return false;
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
baseDraw(element, data) {
|
|
967
|
+
const activeG = createG();
|
|
968
|
+
activeG.classList.add('active');
|
|
969
|
+
activeG.classList.add('line-handle');
|
|
970
|
+
const sourcePoint = getSourcePoint(this.board, element);
|
|
971
|
+
const targetPoint = getTargetPoint(this.board, element);
|
|
972
|
+
const sourceCircle = drawCircle(PlaitBoard.getRoughSVG(this.board), sourcePoint, RESIZE_HANDLE_DIAMETER, {
|
|
973
|
+
stroke: '#999999',
|
|
974
|
+
strokeWidth: 1,
|
|
975
|
+
fill: '#FFF',
|
|
976
|
+
fillStyle: 'solid'
|
|
977
|
+
});
|
|
978
|
+
const targetCircle = drawCircle(PlaitBoard.getRoughSVG(this.board), targetPoint, RESIZE_HANDLE_DIAMETER, {
|
|
979
|
+
stroke: '#999999',
|
|
980
|
+
strokeWidth: 1,
|
|
981
|
+
fill: '#FFF',
|
|
982
|
+
fillStyle: 'solid'
|
|
983
|
+
});
|
|
984
|
+
activeG.appendChild(targetCircle);
|
|
985
|
+
activeG.appendChild(sourceCircle);
|
|
986
|
+
return activeG;
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
class LineComponent extends PlaitPluginElementComponent {
|
|
991
|
+
constructor(viewContainerRef, cdr) {
|
|
992
|
+
super(cdr);
|
|
993
|
+
this.viewContainerRef = viewContainerRef;
|
|
994
|
+
this.cdr = cdr;
|
|
995
|
+
this.destroy$ = new Subject();
|
|
996
|
+
this.textManages = [];
|
|
997
|
+
this.boundedElements = {};
|
|
998
|
+
}
|
|
999
|
+
initializeGenerator() {
|
|
1000
|
+
this.shapeGenerator = new LineShapeGenerator(this.board);
|
|
1001
|
+
this.activeGenerator = new LineActiveGenerator(this.board);
|
|
1002
|
+
this.initializeTextManages();
|
|
1003
|
+
}
|
|
1004
|
+
ngOnInit() {
|
|
1005
|
+
this.initializeGenerator();
|
|
1006
|
+
this.shapeGenerator.draw(this.element, this.g);
|
|
1007
|
+
this.activeGenerator.draw(this.element, PlaitBoard.getElementActiveHost(this.board), { selected: this.selected });
|
|
1008
|
+
super.ngOnInit();
|
|
1009
|
+
this.boundedElements = this.getBoundedElements();
|
|
1010
|
+
this.drawText();
|
|
1011
|
+
}
|
|
1012
|
+
getBoundedElements() {
|
|
1013
|
+
const boundedElements = {};
|
|
1014
|
+
if (this.element.source.boundId) {
|
|
1015
|
+
const boundElement = getElementById(this.board, this.element.source.boundId);
|
|
1016
|
+
if (boundElement) {
|
|
1017
|
+
boundedElements.source = boundElement;
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
if (this.element.target.boundId) {
|
|
1021
|
+
const boundElement = getElementById(this.board, this.element.target.boundId);
|
|
1022
|
+
if (boundElement) {
|
|
1023
|
+
boundedElements.target = boundElement;
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
return boundedElements;
|
|
1027
|
+
}
|
|
1028
|
+
onContextChanged(value, previous) {
|
|
1029
|
+
const boundedElements = this.getBoundedElements();
|
|
1030
|
+
const isBoundedElementsChanged = boundedElements.source !== this.boundedElements.source || boundedElements.target !== this.boundedElements.target;
|
|
1031
|
+
this.boundedElements = boundedElements;
|
|
1032
|
+
if (value.element !== previous.element) {
|
|
1033
|
+
this.shapeGenerator.draw(this.element, this.g);
|
|
1034
|
+
this.activeGenerator.draw(this.element, PlaitBoard.getElementActiveHost(this.board), { selected: this.selected });
|
|
1035
|
+
this.updateText(previous.element.texts, value.element.texts);
|
|
1036
|
+
this.updateTextRectangle();
|
|
1037
|
+
}
|
|
1038
|
+
if (isBoundedElementsChanged) {
|
|
1039
|
+
this.shapeGenerator.draw(this.element, this.g);
|
|
1040
|
+
this.activeGenerator.draw(this.element, PlaitBoard.getElementActiveHost(this.board), { selected: this.selected });
|
|
1041
|
+
this.updateTextRectangle();
|
|
1042
|
+
return;
|
|
1043
|
+
}
|
|
1044
|
+
const hasSameSelected = value.selected === previous.selected;
|
|
1045
|
+
if (!hasSameSelected) {
|
|
1046
|
+
this.activeGenerator.draw(this.element, PlaitBoard.getElementActiveHost(this.board), { selected: this.selected });
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
initializeTextManages() {
|
|
1050
|
+
if (this.element.texts?.length) {
|
|
1051
|
+
this.element.texts.forEach((text, index) => {
|
|
1052
|
+
const manage = this.createTextManage(text, index);
|
|
1053
|
+
this.textManages.push(manage);
|
|
1054
|
+
});
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
destroyTextManages() {
|
|
1058
|
+
this.textManages.forEach(manage => {
|
|
1059
|
+
manage.destroy();
|
|
1060
|
+
});
|
|
1061
|
+
}
|
|
1062
|
+
drawText() {
|
|
1063
|
+
if (this.element.texts?.length) {
|
|
1064
|
+
this.textManages.forEach((manage, index) => {
|
|
1065
|
+
manage.draw(this.element.texts[index].text);
|
|
1066
|
+
this.g.append(manage.g);
|
|
1067
|
+
});
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
createTextManage(text, index) {
|
|
1071
|
+
return new TextManage(this.board, this.viewContainerRef, {
|
|
1072
|
+
getRectangle: () => {
|
|
1073
|
+
return getLineTextRectangle(this.board, this.element, index);
|
|
1074
|
+
},
|
|
1075
|
+
onValueChangeHandle: (textManageRef) => {
|
|
1076
|
+
const height = textManageRef.height / this.board.viewport.zoom;
|
|
1077
|
+
const width = textManageRef.width / this.board.viewport.zoom;
|
|
1078
|
+
const texts = [...this.element.texts];
|
|
1079
|
+
texts.splice(index, 1, {
|
|
1080
|
+
text: textManageRef.newValue ? textManageRef.newValue : this.element.texts[index].text,
|
|
1081
|
+
position: this.element.texts[index].position,
|
|
1082
|
+
width,
|
|
1083
|
+
height
|
|
1084
|
+
});
|
|
1085
|
+
DrawTransforms.setLineTexts(this.board, this.element, texts);
|
|
1086
|
+
},
|
|
1087
|
+
getMaxWidth: () => GeometryThreshold.defaultTextMaxWidth
|
|
1088
|
+
});
|
|
1089
|
+
}
|
|
1090
|
+
updateText(previousTexts, currentTexts) {
|
|
1091
|
+
if (previousTexts === currentTexts)
|
|
1092
|
+
return;
|
|
1093
|
+
const previousTextsLength = previousTexts.length;
|
|
1094
|
+
const currentTextsLength = currentTexts.length;
|
|
1095
|
+
if (currentTextsLength === previousTextsLength) {
|
|
1096
|
+
for (let i = 0; i < previousTextsLength; i++) {
|
|
1097
|
+
if (previousTexts[i].text !== currentTexts[i].text) {
|
|
1098
|
+
this.textManages[i].updateText(currentTexts[i].text);
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
else {
|
|
1103
|
+
this.destroyTextManages();
|
|
1104
|
+
this.textManages = [];
|
|
1105
|
+
this.initializeTextManages();
|
|
1106
|
+
this.drawText();
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
updateTextRectangle() {
|
|
1110
|
+
this.textManages.forEach(manage => {
|
|
1111
|
+
manage.updateRectangle();
|
|
1112
|
+
});
|
|
1113
|
+
}
|
|
1114
|
+
ngOnDestroy() {
|
|
1115
|
+
super.ngOnDestroy();
|
|
1116
|
+
this.activeGenerator.destroy();
|
|
1117
|
+
this.destroy$.next();
|
|
1118
|
+
this.destroy$.complete();
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
LineComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: LineComponent, deps: [{ token: i0.ViewContainerRef }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
1122
|
+
LineComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.2", type: LineComponent, selector: "plait-draw-line", usesInheritance: true, ngImport: i0, template: ``, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1123
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.2", ngImport: i0, type: LineComponent, decorators: [{
|
|
1124
|
+
type: Component,
|
|
1125
|
+
args: [{
|
|
1126
|
+
selector: 'plait-draw-line',
|
|
1127
|
+
template: ``,
|
|
1128
|
+
changeDetection: ChangeDetectionStrategy.OnPush
|
|
1129
|
+
}]
|
|
1130
|
+
}], ctorParameters: function () { return [{ type: i0.ViewContainerRef }, { type: i0.ChangeDetectorRef }]; } });
|
|
1131
|
+
|
|
1132
|
+
const withDrawHotkey = (board) => {
|
|
1133
|
+
const { keydown, dblclick } = board;
|
|
1134
|
+
board.keydown = (event) => {
|
|
1135
|
+
const selectedElements = getSelectedElements(board);
|
|
1136
|
+
const isSingleSelection = selectedElements.length === 1;
|
|
1137
|
+
const targetElement = selectedElements[0];
|
|
1138
|
+
if (!isVirtualKey(event) &&
|
|
1139
|
+
!isDelete(event) &&
|
|
1140
|
+
!isSpaceHotkey(event) &&
|
|
1141
|
+
isSingleSelection &&
|
|
1142
|
+
PlaitDrawElement.isGeometry(targetElement)) {
|
|
1143
|
+
event.preventDefault();
|
|
1144
|
+
PlaitElement.getComponent(targetElement).editText();
|
|
1145
|
+
return;
|
|
1146
|
+
}
|
|
1147
|
+
keydown(event);
|
|
1148
|
+
};
|
|
1149
|
+
board.dblclick = (event) => {
|
|
1150
|
+
event.preventDefault();
|
|
1151
|
+
const geometries = getSelectedGeometryElements(board);
|
|
1152
|
+
if (geometries.length === 1) {
|
|
1153
|
+
const component = PlaitElement.getComponent(geometries[0]);
|
|
1154
|
+
component.editText();
|
|
1155
|
+
}
|
|
1156
|
+
dblclick(event);
|
|
1157
|
+
};
|
|
1158
|
+
return board;
|
|
1159
|
+
};
|
|
1160
|
+
|
|
1161
|
+
const withGeometryCreateByDrag = (board) => {
|
|
1162
|
+
const { pointerMove, pointerUp } = board;
|
|
1163
|
+
let geometryShapeG = null;
|
|
1164
|
+
board.pointerMove = (event) => {
|
|
1165
|
+
geometryShapeG?.remove();
|
|
1166
|
+
geometryShapeG = createG();
|
|
1167
|
+
const geometryGenerator = new GeometryShapeGenerator(board);
|
|
1168
|
+
const isGeometryPointer = PlaitBoard.isInPointer(board, GeometryPointer);
|
|
1169
|
+
const dragMode = isGeometryPointer && isDndMode(board);
|
|
1170
|
+
const movingPoint = transformPoint(board, toPoint(event.x, event.y, PlaitBoard.getHost(board)));
|
|
1171
|
+
const pointer = PlaitBoard.getPointer(board);
|
|
1172
|
+
if (dragMode) {
|
|
1173
|
+
const points = getDefaultGeometryPoints(pointer, movingPoint);
|
|
1174
|
+
if (pointer === DrawPointerType.text) {
|
|
1175
|
+
const textG = getTemporaryTextG(movingPoint);
|
|
1176
|
+
geometryShapeG.appendChild(textG);
|
|
1177
|
+
PlaitBoard.getElementActiveHost(board).append(geometryShapeG);
|
|
1178
|
+
}
|
|
1179
|
+
else {
|
|
1180
|
+
const temporaryElement = createGeometryElement(pointer, points, '', {
|
|
1181
|
+
strokeColor: DefaultGeometryProperty.strokeColor,
|
|
1182
|
+
strokeWidth: DefaultGeometryProperty.strokeWidth
|
|
1183
|
+
});
|
|
1184
|
+
geometryGenerator.draw(temporaryElement, geometryShapeG);
|
|
1185
|
+
PlaitBoard.getElementActiveHost(board).append(geometryShapeG);
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
pointerMove(event);
|
|
1189
|
+
};
|
|
1190
|
+
board.pointerUp = (event) => {
|
|
1191
|
+
const pointer = PlaitBoard.getPointer(board);
|
|
1192
|
+
const isGeometryPointer = PlaitBoard.isInPointer(board, GeometryPointer);
|
|
1193
|
+
const dragMode = isGeometryPointer && isDndMode(board);
|
|
1194
|
+
if (dragMode) {
|
|
1195
|
+
const targetPoint = transformPoint(board, toPoint(event.x, event.y, PlaitBoard.getHost(board)));
|
|
1196
|
+
const points = getDefaultGeometryPoints(pointer, targetPoint);
|
|
1197
|
+
if (pointer === DrawPointerType.text) {
|
|
1198
|
+
DrawTransforms.insertText(board, points);
|
|
1199
|
+
}
|
|
1200
|
+
else {
|
|
1201
|
+
DrawTransforms.insertGeometry(board, points, pointer);
|
|
1202
|
+
}
|
|
1203
|
+
BoardTransforms.updatePointerType(board, PlaitPointerType.selection);
|
|
1204
|
+
}
|
|
1205
|
+
geometryShapeG?.remove();
|
|
1206
|
+
geometryShapeG = null;
|
|
1207
|
+
preventTouchMove(board, event, false);
|
|
1208
|
+
pointerUp(event);
|
|
1209
|
+
};
|
|
1210
|
+
return board;
|
|
1211
|
+
};
|
|
1212
|
+
const withGeometryCreateByDraw = (board) => {
|
|
1213
|
+
const { pointerDown, pointerMove, pointerUp, keydown, keyup } = board;
|
|
1214
|
+
let start = null;
|
|
1215
|
+
let geometryShapeG = null;
|
|
1216
|
+
let temporaryElement = null;
|
|
1217
|
+
let isShift = false;
|
|
1218
|
+
board.keydown = (event) => {
|
|
1219
|
+
isShift = isKeyHotkey('shift', event);
|
|
1220
|
+
keydown(event);
|
|
1221
|
+
};
|
|
1222
|
+
board.keyup = (event) => {
|
|
1223
|
+
isShift = false;
|
|
1224
|
+
keyup(event);
|
|
1225
|
+
};
|
|
1226
|
+
board.pointerDown = (event) => {
|
|
1227
|
+
const isGeometryPointer = PlaitBoard.isInPointer(board, GeometryPointer);
|
|
1228
|
+
if (isGeometryPointer && isDrawingMode(board)) {
|
|
1229
|
+
const point = transformPoint(board, toPoint(event.x, event.y, PlaitBoard.getHost(board)));
|
|
1230
|
+
start = point;
|
|
1231
|
+
const pointer = PlaitBoard.getPointer(board);
|
|
1232
|
+
preventTouchMove(board, event, true);
|
|
1233
|
+
if (pointer === DrawPointerType.text) {
|
|
1234
|
+
const points = getDefaultGeometryPoints(pointer, point);
|
|
1235
|
+
const textElement = createGeometryElement(GeometryShape.text, points, DefaultTextProperty.text);
|
|
1236
|
+
Transforms.insertNode(board, textElement, [board.children.length]);
|
|
1237
|
+
clearSelectedElement(board);
|
|
1238
|
+
addSelectedElement(board, textElement);
|
|
1239
|
+
BoardTransforms.updatePointerType(board, PlaitPointerType.selection);
|
|
1240
|
+
start = null;
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
pointerDown(event);
|
|
1244
|
+
};
|
|
1245
|
+
board.pointerMove = (event) => {
|
|
1246
|
+
geometryShapeG?.remove();
|
|
1247
|
+
geometryShapeG = createG();
|
|
1248
|
+
const geometryGenerator = new GeometryShapeGenerator(board);
|
|
1249
|
+
const drawMode = !!start;
|
|
1250
|
+
const movingPoint = transformPoint(board, toPoint(event.x, event.y, PlaitBoard.getHost(board)));
|
|
1251
|
+
const pointer = PlaitBoard.getPointer(board);
|
|
1252
|
+
if (drawMode && pointer !== DrawPointerType.text) {
|
|
1253
|
+
const points = normalizeShapePoints([start, movingPoint], isShift);
|
|
1254
|
+
temporaryElement = createGeometryElement(pointer, points, '', {
|
|
1255
|
+
strokeColor: DefaultGeometryProperty.strokeColor,
|
|
1256
|
+
strokeWidth: DefaultGeometryProperty.strokeWidth
|
|
1257
|
+
});
|
|
1258
|
+
geometryGenerator.draw(temporaryElement, geometryShapeG);
|
|
1259
|
+
PlaitBoard.getElementActiveHost(board).append(geometryShapeG);
|
|
1260
|
+
}
|
|
1261
|
+
pointerMove(event);
|
|
1262
|
+
};
|
|
1263
|
+
board.pointerUp = (event) => {
|
|
1264
|
+
const isDrawMode = !!start;
|
|
1265
|
+
if (isDrawMode) {
|
|
1266
|
+
const targetPoint = transformPoint(board, toPoint(event.x, event.y, PlaitBoard.getHost(board)));
|
|
1267
|
+
const { width, height } = RectangleClient.toRectangleClient([start, targetPoint]);
|
|
1268
|
+
if (Math.hypot(width, height) === 0) {
|
|
1269
|
+
const pointer = PlaitBoard.getPointer(board);
|
|
1270
|
+
const points = getDefaultGeometryPoints(pointer, targetPoint);
|
|
1271
|
+
if (pointer !== DrawPointerType.text) {
|
|
1272
|
+
temporaryElement = createGeometryElement(pointer, points, '', {
|
|
1273
|
+
strokeColor: DefaultGeometryProperty.strokeColor,
|
|
1274
|
+
strokeWidth: DefaultGeometryProperty.strokeWidth
|
|
1275
|
+
});
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
if (temporaryElement) {
|
|
1280
|
+
Transforms.insertNode(board, temporaryElement, [board.children.length]);
|
|
1281
|
+
clearSelectedElement(board);
|
|
1282
|
+
addSelectedElement(board, temporaryElement);
|
|
1283
|
+
BoardTransforms.updatePointerType(board, PlaitPointerType.selection);
|
|
1284
|
+
}
|
|
1285
|
+
geometryShapeG?.remove();
|
|
1286
|
+
geometryShapeG = null;
|
|
1287
|
+
start = null;
|
|
1288
|
+
temporaryElement = null;
|
|
1289
|
+
preventTouchMove(board, event, false);
|
|
1290
|
+
pointerUp(event);
|
|
1291
|
+
};
|
|
1292
|
+
return board;
|
|
1293
|
+
};
|
|
1294
|
+
const getDefaultGeometryPoints = (pointer, targetPoint) => {
|
|
1295
|
+
return pointer === DrawPointerType.text
|
|
1296
|
+
? getPointsByCenterPoint(targetPoint, DefaultTextProperty.width, DefaultTextProperty.height)
|
|
1297
|
+
: getPointsByCenterPoint(targetPoint, DefaultGeometryProperty.width, DefaultGeometryProperty.height);
|
|
1298
|
+
};
|
|
1299
|
+
const getTemporaryTextG = (movingPoint) => {
|
|
1300
|
+
const textG = createG();
|
|
1301
|
+
const width = DefaultTextProperty.width - ShapeDefaultSpace.rectangleAndText * 2;
|
|
1302
|
+
const foreignObject = createForeignObject(movingPoint[0] - width / 2, movingPoint[1] - DefaultTextProperty.height / 2, width, DefaultTextProperty.height);
|
|
1303
|
+
const richtext = document.createElement('div');
|
|
1304
|
+
richtext.textContent = DefaultTextProperty.text;
|
|
1305
|
+
richtext.style.fontSize = `${DEFAULT_FONT_SIZE}px`;
|
|
1306
|
+
richtext.style.cursor = 'default';
|
|
1307
|
+
foreignObject.appendChild(richtext);
|
|
1308
|
+
textG.appendChild(foreignObject);
|
|
1309
|
+
return textG;
|
|
1310
|
+
};
|
|
1311
|
+
|
|
1312
|
+
const buildClipboardData = (board, elements, startPoint) => {
|
|
1313
|
+
return elements.map(element => {
|
|
1314
|
+
if (PlaitDrawElement.isGeometry(element)) {
|
|
1315
|
+
const points = element.points.map(point => [point[0] - startPoint[0], point[1] - startPoint[1]]);
|
|
1316
|
+
return { ...element, points };
|
|
1317
|
+
}
|
|
1318
|
+
if (PlaitDrawElement.isLine(element)) {
|
|
1319
|
+
let source = { ...element.source };
|
|
1320
|
+
let target = { ...element.target };
|
|
1321
|
+
if (element.source.boundId && !getElementById(board, element.source.boundId, elements)) {
|
|
1322
|
+
delete source.boundId;
|
|
1323
|
+
delete source.connection;
|
|
1324
|
+
}
|
|
1325
|
+
if (element.target.boundId && !getElementById(board, element.target.boundId, elements)) {
|
|
1326
|
+
delete target.boundId;
|
|
1327
|
+
delete target.connection;
|
|
1328
|
+
}
|
|
1329
|
+
const points = element.points.map(point => [point[0] - startPoint[0], point[1] - startPoint[1]]);
|
|
1330
|
+
return { ...element, points, source, target };
|
|
1331
|
+
}
|
|
1332
|
+
return element;
|
|
1333
|
+
});
|
|
1334
|
+
};
|
|
1335
|
+
const insertClipboardData = (board, elements, startPoint) => {
|
|
1336
|
+
const lines = elements.filter(value => PlaitDrawElement.isLine(value));
|
|
1337
|
+
const geometries = elements.filter(value => PlaitDrawElement.isGeometry(value));
|
|
1338
|
+
geometries.forEach(element => {
|
|
1339
|
+
const sourceLines = [];
|
|
1340
|
+
const targetLines = [];
|
|
1341
|
+
lines.forEach(line => {
|
|
1342
|
+
if (PlaitLine.isBoundElementOfSource(line, element)) {
|
|
1343
|
+
sourceLines.push(line);
|
|
1344
|
+
}
|
|
1345
|
+
if (PlaitLine.isBoundElementOfTarget(line, element)) {
|
|
1346
|
+
targetLines.push(line);
|
|
1347
|
+
}
|
|
1348
|
+
});
|
|
1349
|
+
element.id = idCreator();
|
|
1350
|
+
// update lines
|
|
1351
|
+
sourceLines.forEach(sourceLine => (sourceLine.source.boundId = element.id));
|
|
1352
|
+
targetLines.forEach(targetLine => (targetLine.target.boundId = element.id));
|
|
1353
|
+
element.points = element.points.map(point => [startPoint[0] + point[0], startPoint[1] + point[1]]);
|
|
1354
|
+
Transforms.insertNode(board, element, [board.children.length]);
|
|
1355
|
+
});
|
|
1356
|
+
lines.forEach(element => {
|
|
1357
|
+
element.id = idCreator();
|
|
1358
|
+
element.points = element.points.map(point => [startPoint[0] + point[0], startPoint[1] + point[1]]);
|
|
1359
|
+
Transforms.insertNode(board, element, [board.children.length]);
|
|
1360
|
+
});
|
|
1361
|
+
};
|
|
1362
|
+
|
|
1363
|
+
const withDrawFragment = (baseBoard) => {
|
|
1364
|
+
const board = baseBoard;
|
|
1365
|
+
const { getDeletedFragment, setFragment, insertFragment } = board;
|
|
1366
|
+
board.getDeletedFragment = (data) => {
|
|
1367
|
+
const drawElements = getSelectedDrawElements(board);
|
|
1368
|
+
if (drawElements.length) {
|
|
1369
|
+
const lines = getBoardLines(board);
|
|
1370
|
+
const geometryElements = drawElements.filter(value => PlaitDrawElement.isGeometry(value));
|
|
1371
|
+
const lineElements = drawElements.filter(value => PlaitDrawElement.isLine(value));
|
|
1372
|
+
const boundLineElements = lines.filter(line => geometryElements.find(geometry => PlaitLine.isBoundElementOfSource(line, geometry) || PlaitLine.isBoundElementOfTarget(line, geometry)));
|
|
1373
|
+
data.push(...[...geometryElements, ...lineElements, ...boundLineElements.filter(line => !lineElements.includes(line))]);
|
|
1374
|
+
}
|
|
1375
|
+
return getDeletedFragment(data);
|
|
1376
|
+
};
|
|
1377
|
+
board.setFragment = (data, rectangle) => {
|
|
1378
|
+
const targetDrawElements = getSelectedDrawElements(board);
|
|
1379
|
+
if (targetDrawElements.length) {
|
|
1380
|
+
const elements = buildClipboardData(board, targetDrawElements, rectangle ? [rectangle.x, rectangle.y] : [0, 0]);
|
|
1381
|
+
setClipboardData(data, elements);
|
|
1382
|
+
}
|
|
1383
|
+
setFragment(data, rectangle);
|
|
1384
|
+
};
|
|
1385
|
+
board.insertFragment = (data, targetPoint) => {
|
|
1386
|
+
const elements = getDataFromClipboard(data);
|
|
1387
|
+
const drawElements = elements.filter(value => PlaitDrawElement.isDrawElement(value));
|
|
1388
|
+
if (elements.length > 0 && drawElements.length > 0) {
|
|
1389
|
+
insertClipboardData(board, drawElements, targetPoint);
|
|
1390
|
+
}
|
|
1391
|
+
else if (elements.length === 0) {
|
|
1392
|
+
const text = getTextFromClipboard(data);
|
|
1393
|
+
const selectedElements = getSelectedElements(board);
|
|
1394
|
+
// (* ̄︶ ̄)
|
|
1395
|
+
const insertAsChildren = selectedElements.length === 1 && selectedElements[0].children;
|
|
1396
|
+
const insertAsFreeText = !insertAsChildren;
|
|
1397
|
+
if (text && insertAsFreeText) {
|
|
1398
|
+
const { width, height } = getTextSize(board, text);
|
|
1399
|
+
DrawTransforms.insertText(board, [targetPoint, [targetPoint[0] + width, targetPoint[1] + height]], text);
|
|
1400
|
+
return;
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
insertFragment(data, targetPoint);
|
|
1404
|
+
};
|
|
1405
|
+
return board;
|
|
1406
|
+
};
|
|
1407
|
+
|
|
1408
|
+
const DefaultLineStyle = {
|
|
1409
|
+
strokeWidth: 2,
|
|
1410
|
+
strokeColor: '#000'
|
|
1411
|
+
};
|
|
1412
|
+
|
|
1413
|
+
const getHitGeometryResizeHandleRef = (board, element, point) => {
|
|
1414
|
+
const rectangle = getRectangleByPoints(element.points);
|
|
1415
|
+
const resizeHandleRefs = getRectangleResizeHandleRefs(rectangle, RESIZE_HANDLE_DIAMETER);
|
|
1416
|
+
const result = resizeHandleRefs.find(resizeHandleRef => {
|
|
1417
|
+
return RectangleClient.isHit(RectangleClient.toRectangleClient([point, point]), resizeHandleRef.rectangle);
|
|
1418
|
+
});
|
|
1419
|
+
return result;
|
|
1420
|
+
};
|
|
1421
|
+
const getHitOutlineGeometry = (board, point, offset = 0) => {
|
|
1422
|
+
let geometry = null;
|
|
1423
|
+
depthFirstRecursion(board, node => {
|
|
1424
|
+
if (PlaitDrawElement.isGeometry(node)) {
|
|
1425
|
+
const shape = node.shape;
|
|
1426
|
+
let client = getRectangleByPoints(node.points);
|
|
1427
|
+
client = RectangleClient.getOutlineRectangle(client, offset);
|
|
1428
|
+
const isHit = getEngine(shape).isHit(client, point);
|
|
1429
|
+
if (isHit) {
|
|
1430
|
+
geometry = node;
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
}, getIsRecursionFunc(board), true);
|
|
1434
|
+
return geometry;
|
|
1435
|
+
};
|
|
1436
|
+
|
|
1437
|
+
const withLineCreateByDraw = (board) => {
|
|
1438
|
+
const { pointerDown, pointerMove, pointerUp } = board;
|
|
1439
|
+
let start = null;
|
|
1440
|
+
let sourceRef = {};
|
|
1441
|
+
let targetRef = {};
|
|
1442
|
+
let lineShapeG = null;
|
|
1443
|
+
let temporaryElement = null;
|
|
1444
|
+
board.pointerDown = (event) => {
|
|
1445
|
+
const isLinePointer = PlaitBoard.isPointer(board, DrawPointerType.line);
|
|
1446
|
+
if (isLinePointer && isDrawingMode(board)) {
|
|
1447
|
+
const point = transformPoint(board, toPoint(event.x, event.y, PlaitBoard.getHost(board)));
|
|
1448
|
+
start = point;
|
|
1449
|
+
const hitElement = getHitOutlineGeometry(board, point, -4);
|
|
1450
|
+
if (hitElement) {
|
|
1451
|
+
sourceRef.connection = transformPointToConnection(board, point, hitElement);
|
|
1452
|
+
sourceRef.boundId = hitElement.id;
|
|
1453
|
+
}
|
|
1454
|
+
preventTouchMove(board, event, true);
|
|
1455
|
+
}
|
|
1456
|
+
pointerDown(event);
|
|
1457
|
+
};
|
|
1458
|
+
board.pointerMove = (event) => {
|
|
1459
|
+
lineShapeG?.remove();
|
|
1460
|
+
lineShapeG = createG();
|
|
1461
|
+
const movingPoint = transformPoint(board, toPoint(event.x, event.y, PlaitBoard.getHost(board)));
|
|
1462
|
+
if (start) {
|
|
1463
|
+
const hitElement = getHitOutlineGeometry(board, movingPoint, -4);
|
|
1464
|
+
targetRef.connection = hitElement ? transformPointToConnection(board, movingPoint, hitElement) : undefined;
|
|
1465
|
+
targetRef.boundId = hitElement ? hitElement.id : undefined;
|
|
1466
|
+
const lineGenerator = new LineShapeGenerator(board);
|
|
1467
|
+
temporaryElement = createLineElement(LineShape.elbow, [start, movingPoint], { marker: LineMarkerType.none, connection: sourceRef.connection, boundId: sourceRef?.boundId }, { marker: LineMarkerType.arrow, connection: targetRef.connection, boundId: targetRef?.boundId }, {
|
|
1468
|
+
strokeColor: DefaultLineStyle.strokeColor,
|
|
1469
|
+
strokeWidth: DefaultLineStyle.strokeWidth
|
|
1470
|
+
});
|
|
1471
|
+
lineGenerator.draw(temporaryElement, lineShapeG);
|
|
1472
|
+
PlaitBoard.getElementActiveHost(board).append(lineShapeG);
|
|
1473
|
+
}
|
|
1474
|
+
pointerMove(event);
|
|
1475
|
+
};
|
|
1476
|
+
board.pointerUp = (event) => {
|
|
1477
|
+
if (temporaryElement) {
|
|
1478
|
+
Transforms.insertNode(board, temporaryElement, [board.children.length]);
|
|
1479
|
+
clearSelectedElement(board);
|
|
1480
|
+
addSelectedElement(board, temporaryElement);
|
|
1481
|
+
BoardTransforms.updatePointerType(board, PlaitPointerType.selection);
|
|
1482
|
+
}
|
|
1483
|
+
lineShapeG?.remove();
|
|
1484
|
+
lineShapeG = null;
|
|
1485
|
+
start = null;
|
|
1486
|
+
sourceRef = {};
|
|
1487
|
+
targetRef = {};
|
|
1488
|
+
temporaryElement = null;
|
|
1489
|
+
preventTouchMove(board, event, false);
|
|
1490
|
+
pointerUp(event);
|
|
1491
|
+
};
|
|
1492
|
+
return board;
|
|
1493
|
+
};
|
|
1494
|
+
|
|
1495
|
+
const withGeometryResize = (board) => {
|
|
1496
|
+
const { keydown, keyup } = board;
|
|
1497
|
+
let isShift = false;
|
|
1498
|
+
board.keydown = (event) => {
|
|
1499
|
+
isShift = isKeyHotkey('shift', event);
|
|
1500
|
+
keydown(event);
|
|
1501
|
+
};
|
|
1502
|
+
board.keyup = (event) => {
|
|
1503
|
+
isShift = false;
|
|
1504
|
+
keyup(event);
|
|
1505
|
+
};
|
|
1506
|
+
const options = {
|
|
1507
|
+
key: 'draw-geometry',
|
|
1508
|
+
canResize: () => {
|
|
1509
|
+
return true;
|
|
1510
|
+
},
|
|
1511
|
+
detect: (point) => {
|
|
1512
|
+
const selectedGeometryElements = getSelectedGeometryElements(board);
|
|
1513
|
+
if (selectedGeometryElements.length !== 1 || getSelectedElements(board).length !== 1) {
|
|
1514
|
+
return null;
|
|
1515
|
+
}
|
|
1516
|
+
const target = selectedGeometryElements[0];
|
|
1517
|
+
const targetComponent = PlaitElement.getComponent(selectedGeometryElements[0]);
|
|
1518
|
+
if (targetComponent.activeGenerator.hasResizeHandle) {
|
|
1519
|
+
const handleRef = getHitGeometryResizeHandleRef(board, target, point);
|
|
1520
|
+
if (handleRef) {
|
|
1521
|
+
return {
|
|
1522
|
+
element: target,
|
|
1523
|
+
handle: handleRef.handle,
|
|
1524
|
+
cursorClass: handleRef.cursorClass
|
|
1525
|
+
};
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
return null;
|
|
1529
|
+
},
|
|
1530
|
+
onResize: (resizeRef, resizeState) => {
|
|
1531
|
+
let points = [...resizeRef.element.points];
|
|
1532
|
+
const rectangle = getRectangleByPoints(resizeRef.element.points);
|
|
1533
|
+
const ratio = rectangle.height / rectangle.width;
|
|
1534
|
+
if (resizeRef.handle === ResizeHandle.nw) {
|
|
1535
|
+
points = [resizeState.endTransformPoint, resizeRef.element.points[1]];
|
|
1536
|
+
}
|
|
1537
|
+
if (resizeRef.handle === ResizeHandle.ne) {
|
|
1538
|
+
points = [resizeState.endTransformPoint, [resizeRef.element.points[0][0], resizeRef.element.points[1][1]]];
|
|
1539
|
+
}
|
|
1540
|
+
if (resizeRef.handle === ResizeHandle.se) {
|
|
1541
|
+
points = [resizeState.endTransformPoint, resizeRef.element.points[0]];
|
|
1542
|
+
}
|
|
1543
|
+
if (resizeRef.handle === ResizeHandle.sw) {
|
|
1544
|
+
points = [resizeState.endTransformPoint, [resizeRef.element.points[1][0], resizeRef.element.points[0][1]]];
|
|
1545
|
+
}
|
|
1546
|
+
if (isShift) {
|
|
1547
|
+
const rectangle = getRectangleByPoints(points);
|
|
1548
|
+
const factor = points[0][1] > points[1][1] ? 1 : -1;
|
|
1549
|
+
const height = rectangle.width * ratio * factor;
|
|
1550
|
+
points = [[resizeState.endTransformPoint[0], points[1][1] + height], points[1]];
|
|
1551
|
+
}
|
|
1552
|
+
points = normalizeShapePoints(points);
|
|
1553
|
+
const { height: textHeight } = PlaitGeometry.getTextManage(resizeRef.element).getSize();
|
|
1554
|
+
DrawTransforms.resizeGeometry(board, points, textHeight, resizeRef.path);
|
|
1555
|
+
}
|
|
1556
|
+
};
|
|
1557
|
+
withResize(board, options);
|
|
1558
|
+
return board;
|
|
1559
|
+
};
|
|
1560
|
+
|
|
1561
|
+
var LineResizeHandle;
|
|
1562
|
+
(function (LineResizeHandle) {
|
|
1563
|
+
LineResizeHandle["source"] = "source";
|
|
1564
|
+
LineResizeHandle["target"] = "target";
|
|
1565
|
+
})(LineResizeHandle || (LineResizeHandle = {}));
|
|
1566
|
+
const getHitLineResizeHandleRef = (board, element, point) => {
|
|
1567
|
+
const sourcePoint = getSourcePoint(board, element);
|
|
1568
|
+
const targetPoint = getTargetPoint(board, element);
|
|
1569
|
+
const sourceRectangle = {
|
|
1570
|
+
x: sourcePoint[0] - RESIZE_HANDLE_DIAMETER / 2,
|
|
1571
|
+
y: sourcePoint[1] - RESIZE_HANDLE_DIAMETER / 2,
|
|
1572
|
+
width: RESIZE_HANDLE_DIAMETER,
|
|
1573
|
+
height: RESIZE_HANDLE_DIAMETER
|
|
1574
|
+
};
|
|
1575
|
+
const targetRectangle = {
|
|
1576
|
+
x: targetPoint[0] - RESIZE_HANDLE_DIAMETER / 2,
|
|
1577
|
+
y: targetPoint[1] - RESIZE_HANDLE_DIAMETER / 2,
|
|
1578
|
+
width: RESIZE_HANDLE_DIAMETER,
|
|
1579
|
+
height: RESIZE_HANDLE_DIAMETER
|
|
1580
|
+
};
|
|
1581
|
+
const isHitSourceRectangle = RectangleClient.isHit(RectangleClient.toRectangleClient([point, point]), sourceRectangle);
|
|
1582
|
+
const isHitTargetRectangle = RectangleClient.isHit(RectangleClient.toRectangleClient([point, point]), targetRectangle);
|
|
1583
|
+
if (isHitSourceRectangle) {
|
|
1584
|
+
return { rectangle: sourceRectangle, handle: LineResizeHandle.source };
|
|
1585
|
+
}
|
|
1586
|
+
if (isHitTargetRectangle) {
|
|
1587
|
+
return { rectangle: targetRectangle, handle: LineResizeHandle.target };
|
|
1588
|
+
}
|
|
1589
|
+
return undefined;
|
|
1590
|
+
};
|
|
1591
|
+
|
|
1592
|
+
const withLineResize = (board) => {
|
|
1593
|
+
const options = {
|
|
1594
|
+
key: 'draw-line',
|
|
1595
|
+
canResize: () => {
|
|
1596
|
+
return true;
|
|
1597
|
+
},
|
|
1598
|
+
detect: (point) => {
|
|
1599
|
+
const selectedLineElements = getSelectedLineElements(board);
|
|
1600
|
+
if (selectedLineElements.length > 0) {
|
|
1601
|
+
let result = null;
|
|
1602
|
+
selectedLineElements.forEach(value => {
|
|
1603
|
+
const handleRef = getHitLineResizeHandleRef(board, value, point);
|
|
1604
|
+
if (handleRef) {
|
|
1605
|
+
result = {
|
|
1606
|
+
element: value,
|
|
1607
|
+
handle: handleRef.handle
|
|
1608
|
+
};
|
|
1609
|
+
}
|
|
1610
|
+
});
|
|
1611
|
+
return result;
|
|
1612
|
+
}
|
|
1613
|
+
return null;
|
|
1614
|
+
},
|
|
1615
|
+
onResize: (resizeRef, resizeState) => {
|
|
1616
|
+
let points = [...resizeRef.element.points];
|
|
1617
|
+
let source = { ...resizeRef.element.source };
|
|
1618
|
+
let target = { ...resizeRef.element.target };
|
|
1619
|
+
if (resizeRef.handle === LineResizeHandle.source) {
|
|
1620
|
+
points[0] = resizeState.endTransformPoint;
|
|
1621
|
+
const hitElement = getHitOutlineGeometry(board, resizeState.endTransformPoint, -4);
|
|
1622
|
+
source.connection = hitElement ? transformPointToConnection(board, resizeState.endTransformPoint, hitElement) : undefined;
|
|
1623
|
+
source.boundId = hitElement ? hitElement.id : undefined;
|
|
1624
|
+
}
|
|
1625
|
+
if (resizeRef.handle === LineResizeHandle.target) {
|
|
1626
|
+
points[1] = resizeState.endTransformPoint;
|
|
1627
|
+
const hitElement = getHitOutlineGeometry(board, resizeState.endTransformPoint, -4);
|
|
1628
|
+
target.connection = hitElement ? transformPointToConnection(board, resizeState.endTransformPoint, hitElement) : undefined;
|
|
1629
|
+
target.boundId = hitElement ? hitElement.id : undefined;
|
|
1630
|
+
}
|
|
1631
|
+
DrawTransforms.resizeLine(board, { points, source, target }, resizeRef.path);
|
|
1632
|
+
}
|
|
1633
|
+
};
|
|
1634
|
+
withResize(board, options);
|
|
1635
|
+
return board;
|
|
1636
|
+
};
|
|
1637
|
+
|
|
1638
|
+
const withLineBoundReaction = (board) => {
|
|
1639
|
+
const { pointerMove, pointerUp } = board;
|
|
1640
|
+
let boundShapeG = null;
|
|
1641
|
+
board.pointerMove = (event) => {
|
|
1642
|
+
boundShapeG?.remove();
|
|
1643
|
+
const isLinePointer = PlaitBoard.isPointer(board, DrawPointerType.line);
|
|
1644
|
+
const movingPoint = transformPoint(board, toPoint(event.x, event.y, PlaitBoard.getHost(board)));
|
|
1645
|
+
const isLineResizing = isResizingByCondition(board, element => PlaitDrawElement.isLine(element));
|
|
1646
|
+
if (isLinePointer || isLineResizing) {
|
|
1647
|
+
const hitElement = getHitOutlineGeometry(board, movingPoint, -4);
|
|
1648
|
+
if (hitElement) {
|
|
1649
|
+
boundShapeG = drawBoundMask(board, hitElement);
|
|
1650
|
+
let nearestPoint = getNearestPoint(hitElement, movingPoint, ACTIVE_STROKE_WIDTH);
|
|
1651
|
+
const rectangle = getRectangleByPoints(hitElement.points);
|
|
1652
|
+
const activeRectangle = RectangleClient.inflate(rectangle, ACTIVE_STROKE_WIDTH);
|
|
1653
|
+
const hitConnector = getHitConnectorPoint(nearestPoint, hitElement, activeRectangle);
|
|
1654
|
+
nearestPoint = hitConnector ? hitConnector : nearestPoint;
|
|
1655
|
+
const circleG = drawCircle(PlaitBoard.getRoughSVG(board), nearestPoint, 6, {
|
|
1656
|
+
stroke: SELECTION_BORDER_COLOR,
|
|
1657
|
+
strokeWidth: ACTIVE_STROKE_WIDTH,
|
|
1658
|
+
fill: SELECTION_BORDER_COLOR,
|
|
1659
|
+
fillStyle: 'solid'
|
|
1660
|
+
});
|
|
1661
|
+
boundShapeG.appendChild(circleG);
|
|
1662
|
+
PlaitBoard.getElementActiveHost(board).append(boundShapeG);
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
pointerMove(event);
|
|
1666
|
+
};
|
|
1667
|
+
board.pointerUp = event => {
|
|
1668
|
+
boundShapeG?.remove();
|
|
1669
|
+
boundShapeG = null;
|
|
1670
|
+
pointerUp(event);
|
|
1671
|
+
};
|
|
1672
|
+
return board;
|
|
1673
|
+
};
|
|
1674
|
+
|
|
1675
|
+
const withLineText = (board) => {
|
|
1676
|
+
const { dblclick } = board;
|
|
1677
|
+
board.dblclick = (event) => {
|
|
1678
|
+
const clickPoint = transformPoint(board, toPoint(event.x, event.y, PlaitBoard.getHost(board)));
|
|
1679
|
+
const hitTarget = getHitElements(board, { ranges: [{ anchor: clickPoint, focus: clickPoint }] }, (element) => {
|
|
1680
|
+
return PlaitDrawElement.isLine(element);
|
|
1681
|
+
})[0];
|
|
1682
|
+
if (hitTarget) {
|
|
1683
|
+
const points = getLinePoints(board, hitTarget);
|
|
1684
|
+
const point = getNearestPointBetweenPointAndSegments(clickPoint, points);
|
|
1685
|
+
const texts = hitTarget.texts?.length ? [...hitTarget.texts] : [];
|
|
1686
|
+
const textIndex = getHitLineTextIndex(board, hitTarget, clickPoint);
|
|
1687
|
+
const isHitText = isHitLineText(board, hitTarget, clickPoint);
|
|
1688
|
+
if (isHitText) {
|
|
1689
|
+
editHandle(board, hitTarget, textIndex);
|
|
1690
|
+
}
|
|
1691
|
+
else {
|
|
1692
|
+
const ratio = getRatioByPoint(points, point);
|
|
1693
|
+
texts.push({
|
|
1694
|
+
text: buildText('文本'),
|
|
1695
|
+
position: ratio,
|
|
1696
|
+
width: 28,
|
|
1697
|
+
height: 20
|
|
1698
|
+
});
|
|
1699
|
+
DrawTransforms.setLineTexts(board, hitTarget, texts);
|
|
1700
|
+
setTimeout(() => {
|
|
1701
|
+
const hitComponent = PlaitElement.getComponent(hitTarget);
|
|
1702
|
+
editHandle(board, hitTarget, hitComponent.textManages.length - 1, true);
|
|
1703
|
+
});
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
dblclick(event);
|
|
1707
|
+
};
|
|
1708
|
+
return board;
|
|
1709
|
+
};
|
|
1710
|
+
function editHandle(board, element, manageIndex, isFirstEdit = false) {
|
|
1711
|
+
const hitComponent = PlaitElement.getComponent(element);
|
|
1712
|
+
const textManage = hitComponent.textManages[manageIndex];
|
|
1713
|
+
const originText = textManage.componentRef.instance.children;
|
|
1714
|
+
textManage.edit((origin, descendant) => {
|
|
1715
|
+
const text = Node.string(descendant[0]);
|
|
1716
|
+
const shouldRemove = (isFirstEdit && originText === descendant) || !text;
|
|
1717
|
+
if (shouldRemove) {
|
|
1718
|
+
DrawTransforms.removeLineText(board, element, manageIndex);
|
|
1719
|
+
}
|
|
1720
|
+
});
|
|
1721
|
+
}
|
|
1722
|
+
|
|
1723
|
+
const withDraw = (board) => {
|
|
1724
|
+
const { drawElement, getRectangle, isHitSelection, isMovable, dblclick } = board;
|
|
1725
|
+
board.drawElement = (context) => {
|
|
1726
|
+
if (PlaitDrawElement.isGeometry(context.element)) {
|
|
1727
|
+
return GeometryComponent;
|
|
1728
|
+
}
|
|
1729
|
+
else if (PlaitDrawElement.isLine(context.element)) {
|
|
1730
|
+
return LineComponent;
|
|
1731
|
+
}
|
|
1732
|
+
return drawElement(context);
|
|
1733
|
+
};
|
|
1734
|
+
board.getRectangle = (element) => {
|
|
1735
|
+
if (PlaitDrawElement.isGeometry(element)) {
|
|
1736
|
+
return getRectangleByPoints(element.points);
|
|
1737
|
+
}
|
|
1738
|
+
if (PlaitDrawElement.isLine(element)) {
|
|
1739
|
+
const source = getSourcePoint(board, element);
|
|
1740
|
+
const target = getTargetPoint(board, element);
|
|
1741
|
+
return getRectangleByPoints([source, target]);
|
|
1742
|
+
}
|
|
1743
|
+
return getRectangle(element);
|
|
1744
|
+
};
|
|
1745
|
+
board.isHitSelection = (element, range) => {
|
|
1746
|
+
if (PlaitDrawElement.isGeometry(element)) {
|
|
1747
|
+
const client = getRectangleByPoints(element.points);
|
|
1748
|
+
const rangeRectangle = RectangleClient.toRectangleClient([range.anchor, range.focus]);
|
|
1749
|
+
if (element.textHeight > client.height) {
|
|
1750
|
+
const textClient = getTextRectangle(element);
|
|
1751
|
+
return RectangleClient.isHit(rangeRectangle, client) || RectangleClient.isHit(rangeRectangle, textClient);
|
|
1752
|
+
}
|
|
1753
|
+
return RectangleClient.isHit(rangeRectangle, client);
|
|
1754
|
+
}
|
|
1755
|
+
if (PlaitDrawElement.isLine(element)) {
|
|
1756
|
+
const points = getLinePoints(board, element);
|
|
1757
|
+
const strokeWidth = getStrokeWidthByElement(element);
|
|
1758
|
+
const isHitText = isHitLineText(board, element, range.focus);
|
|
1759
|
+
const isHit = isHitPolyLine(points, range.focus, strokeWidth, 3) || isHitText;
|
|
1760
|
+
const rangeRectangle = RectangleClient.toRectangleClient([range.anchor, range.focus]);
|
|
1761
|
+
const isContainPolyLinePoint = points.some(point => {
|
|
1762
|
+
return RectangleClient.isHit(rangeRectangle, RectangleClient.toRectangleClient([point, point]));
|
|
1763
|
+
});
|
|
1764
|
+
const isIntersect = range.anchor === range.focus ? isHit : isPolylineHitRectangle(points, rangeRectangle);
|
|
1765
|
+
return isContainPolyLinePoint || isIntersect;
|
|
1766
|
+
}
|
|
1767
|
+
return isHitSelection(element, range);
|
|
1768
|
+
};
|
|
1769
|
+
board.isMovable = (element) => {
|
|
1770
|
+
if (PlaitDrawElement.isGeometry(element)) {
|
|
1771
|
+
return true;
|
|
1772
|
+
}
|
|
1773
|
+
if (PlaitDrawElement.isLine(element)) {
|
|
1774
|
+
return !element.source.boundId && !element.target.boundId;
|
|
1775
|
+
}
|
|
1776
|
+
return isMovable(element);
|
|
1777
|
+
};
|
|
1778
|
+
return withLineText(withLineBoundReaction(withLineResize(withGeometryResize(withLineCreateByDraw(withGeometryCreateByDrag(withGeometryCreateByDraw(withDrawFragment(withDrawHotkey(board)))))))));
|
|
1779
|
+
};
|
|
1780
|
+
|
|
1781
|
+
/**
|
|
1782
|
+
* Generated bundle index. Do not edit.
|
|
1783
|
+
*/
|
|
1784
|
+
|
|
1785
|
+
export { DefaultGeometryActiveStyle, DefaultGeometryProperty, DefaultGeometryStyle, DefaultTextProperty, DrawPointerType, DrawTransforms, GeometryComponent, GeometryPointer, GeometryShape, GeometryThreshold, LineComponent, LineHandleKey, LineMarkerType, LineShape, PlaitDrawElement, PlaitGeometry, PlaitLine, ShapeDefaultSpace, StrokeStyle, createGeometryElement, createLineElement, drawBoundMask, drawGeometry, drawLine, getBoardLines, getCenterPointsOnPolygon, getConnectionPoint, getElbowPoints, getFillByElement, getHitConnectorPoint, getHitLineTextIndex, getLineDashByElement, getLinePoints, getLineTextRectangle, getNearestPoint, getPointsByCenterPoint, getSelectedDrawElements, getSelectedGeometryElements, getSelectedLineElements, getSourcePoint, getStraightPoints, getStrokeColorByElement, getStrokeStyleByElement, getStrokeWidthByElement, getTargetPoint, getTextRectangle, isHitLineText, isHitPolyLine, transformPointToConnection, withDraw };
|
|
1786
|
+
//# sourceMappingURL=plait-draw.mjs.map
|