@plait/draw 0.50.1 → 0.51.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -1
- package/constants/line.d.ts +1 -0
- package/engines/basic-shapes/ellipse.d.ts +1 -10
- package/engines/flowchart/terminal.d.ts +1 -0
- package/esm2022/constants/line.mjs +2 -1
- package/esm2022/engines/basic-shapes/comment.mjs +4 -5
- package/esm2022/engines/basic-shapes/ellipse.mjs +5 -29
- package/esm2022/engines/basic-shapes/parallelogram.mjs +3 -2
- package/esm2022/engines/basic-shapes/pentagon.mjs +3 -3
- package/esm2022/engines/basic-shapes/polygon.mjs +20 -4
- package/esm2022/engines/basic-shapes/process-arrow.mjs +3 -3
- package/esm2022/engines/basic-shapes/rectangle.mjs +4 -4
- package/esm2022/engines/basic-shapes/round-comment.mjs +4 -5
- package/esm2022/engines/basic-shapes/round-rectangle.mjs +3 -3
- package/esm2022/engines/basic-shapes/star.mjs +3 -3
- package/esm2022/engines/basic-shapes/trapezoid.mjs +3 -2
- package/esm2022/engines/basic-shapes/triangle.mjs +5 -4
- package/esm2022/engines/flowchart/delay.mjs +6 -6
- package/esm2022/engines/flowchart/manual-input.mjs +5 -4
- package/esm2022/engines/flowchart/manual-loop.mjs +3 -2
- package/esm2022/engines/flowchart/merge.mjs +4 -4
- package/esm2022/engines/flowchart/stored-data.mjs +16 -10
- package/esm2022/engines/flowchart/terminal.mjs +37 -27
- package/esm2022/generators/geometry-shape.generator.mjs +3 -3
- package/esm2022/generators/line-active.generator.mjs +52 -68
- package/esm2022/generators/line.generator.mjs +2 -2
- package/esm2022/geometry.component.mjs +4 -4
- package/esm2022/interfaces/geometry.mjs +1 -1
- package/esm2022/interfaces/line.mjs +2 -2
- package/esm2022/line.component.mjs +39 -9
- package/esm2022/plugins/with-draw-fragment.mjs +3 -3
- package/esm2022/plugins/with-draw-hotkey.mjs +6 -6
- package/esm2022/plugins/with-draw-resize.mjs +149 -0
- package/esm2022/plugins/with-draw.mjs +14 -8
- package/esm2022/plugins/with-geometry-create.mjs +10 -10
- package/esm2022/plugins/with-geometry-resize.mjs +27 -74
- package/esm2022/plugins/with-line-auto-complete.mjs +17 -5
- package/esm2022/plugins/with-line-bound-reaction.mjs +6 -5
- package/esm2022/plugins/with-line-create.mjs +2 -2
- package/esm2022/plugins/with-line-resize.mjs +105 -19
- package/esm2022/plugins/with-line-text-move.mjs +5 -4
- package/esm2022/plugins/with-line-text.mjs +7 -5
- package/esm2022/transforms/geometry.mjs +4 -4
- package/esm2022/transforms/line.mjs +6 -8
- package/esm2022/utils/clipboard.mjs +2 -2
- package/esm2022/utils/geometry.mjs +16 -33
- package/esm2022/utils/hit.mjs +18 -10
- package/esm2022/utils/index.mjs +2 -2
- package/esm2022/utils/line/elbow.mjs +101 -0
- package/esm2022/utils/line/index.mjs +6 -0
- package/esm2022/utils/line/line-arrow.mjs +123 -0
- package/esm2022/utils/line/line-basic.mjs +258 -0
- package/esm2022/utils/line/line-common.mjs +111 -0
- package/esm2022/utils/line/line-resize.mjs +313 -0
- package/esm2022/utils/polygon.mjs +30 -0
- package/esm2022/utils/position/geometry.mjs +5 -6
- package/esm2022/utils/position/line.mjs +38 -15
- package/esm2022/utils/resize-align-reaction.mjs +316 -0
- package/esm2022/utils/resize-align.mjs +37 -0
- package/fesm2022/plait-draw.mjs +2108 -1143
- package/fesm2022/plait-draw.mjs.map +1 -1
- package/generators/line-active.generator.d.ts +4 -2
- package/interfaces/geometry.d.ts +1 -0
- package/interfaces/line.d.ts +4 -0
- package/package.json +1 -1
- package/plugins/with-draw-resize.d.ts +13 -0
- package/utils/geometry.d.ts +1 -3
- package/utils/hit.d.ts +3 -1
- package/utils/index.d.ts +1 -1
- package/utils/line/elbow.d.ts +19 -0
- package/utils/line/index.d.ts +5 -0
- package/utils/{line-arrow.d.ts → line/line-arrow.d.ts} +1 -1
- package/utils/line/line-basic.d.ts +13 -0
- package/utils/line/line-common.d.ts +35 -0
- package/utils/line/line-resize.d.ts +23 -0
- package/utils/polygon.d.ts +4 -0
- package/utils/position/geometry.d.ts +2 -3
- package/utils/position/line.d.ts +4 -2
- package/utils/resize-align-reaction.d.ts +42 -0
- package/utils/resize-align.d.ts +8 -0
- package/esm2022/utils/line-arrow.mjs +0 -123
- package/esm2022/utils/line.mjs +0 -392
- package/utils/line.d.ts +0 -25
package/fesm2022/plait-draw.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ACTIVE_STROKE_WIDTH, ThemeColorMode,
|
|
2
|
-
import {
|
|
1
|
+
import { ACTIVE_STROKE_WIDTH, ThemeColorMode, Point, RectangleClient, getElementById, createG, arrowPoints, createPath, distanceBetweenPointAndPoint, drawLinearPath, rotate, depthFirstRecursion, getIsRecursionFunc, idCreator, catmullRomFitting, PlaitBoard, findElements, createMask, createRect, getSelectedElements, distanceBetweenPointAndSegments, isPolylineHitRectangle, setStrokeLinecap, isPointInPolygon, getNearestPointBetweenPointAndSegments, isPointInEllipse, getEllipseTangentSlope, getVectorFromPointAndSlope, drawRectangle, drawRoundRectangle, isPointInRoundRectangle, SELECTION_BORDER_COLOR, SELECTION_FILL_COLOR, drawCircle, Transforms, clearSelectedElement, addSelectedElement, BoardTransforms, PlaitPointerType, Direction, Path, PlaitNode, toViewBoxPoint, toHostPoint, isSelectionMoving, RgbaToHEX, PlaitElement, preventTouchMove, createClipboardContext, WritableClipboardType, addClipboardContext, getRectangleByElements, getHitElementByPoint, CursorClass, temporaryDisableSelection, PRESS_AND_MOVE_BUFFER } from '@plait/core';
|
|
2
|
+
import { removeDuplicatePoints, generateElbowLineRoute, simplifyOrthogonalPoints, isSourceAndTargetIntersect, getPoints, getPointByVector, getExtendPoint, getUnitVectorByPointAndPoint, Generator, getRectangleResizeHandleRefs, RESIZE_HANDLE_DIAMETER, getMemorizedLatest, memorizeLatest, getPointOnPolyline, TRANSPARENT, getCrossingPointsBetweenPointAndSegment, isPointOnSegment, getDirectionByVector, getOppositeDirection, getDirectionFactor, getDirectionByPointOfRectangle, rotateVectorAnti90, getSourceAndTargetOuterRectangle, getNextPoint, normalizeShapePoints, getFirstTextEditor, PRIMARY_COLOR, CommonPluginElement, ActiveGenerator, WithTextPluginKey, drawPrimaryHandle, drawFillPrimaryHandle, isVirtualKey, isDelete, isSpaceHotkey, isDndMode, isDrawingMode, getElementsText, acceptImageTypes, getElementOfFocusedImage, buildImage, getDirectionFactorByVectorComponent, isCornerHandle, getFirstTextManage, withResize, drawHandle, getIndexByResizeHandle, getSymmetricHandleIndex, getResizeHandlePointByIndex, isResizingByCondition, getRatioByPoint, ImageGenerator, ResizeHandle } from '@plait/common';
|
|
3
3
|
import { Alignment, buildText, DEFAULT_FONT_SIZE, getTextSize, AlignEditor, TextManage } from '@plait/text';
|
|
4
4
|
import { pointsOnBezierCurves } from 'points-on-curve';
|
|
5
5
|
import * as i0 from '@angular/core';
|
|
@@ -155,30 +155,1071 @@ const DrawThemeColors = {
|
|
|
155
155
|
}
|
|
156
156
|
};
|
|
157
157
|
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
158
|
+
const DefaultLineStyle = {
|
|
159
|
+
strokeWidth: 2,
|
|
160
|
+
strokeColor: '#000'
|
|
161
|
+
};
|
|
162
|
+
const LINE_TEXT_SPACE = 4;
|
|
163
|
+
const LINE_AUTO_COMPLETE_DIAMETER = 6;
|
|
164
|
+
const LINE_AUTO_COMPLETE_OPACITY = 0.6;
|
|
165
|
+
const LINE_AUTO_COMPLETE_HOVERED_OPACITY = 0.8;
|
|
166
|
+
const LINE_AUTO_COMPLETE_HOVERED_DIAMETER = 10;
|
|
167
|
+
const LINE_ALIGN_TOLERANCE = 3;
|
|
168
|
+
|
|
169
|
+
const alignPoints = (basePoint, movingPoint) => {
|
|
170
|
+
const newPoint = [...movingPoint];
|
|
171
|
+
if (Point.isVertical(newPoint, basePoint, LINE_ALIGN_TOLERANCE)) {
|
|
172
|
+
newPoint[0] = basePoint[0];
|
|
173
|
+
}
|
|
174
|
+
if (Point.isHorizontal(newPoint, basePoint, LINE_ALIGN_TOLERANCE)) {
|
|
175
|
+
newPoint[1] = basePoint[1];
|
|
176
|
+
}
|
|
177
|
+
return newPoint;
|
|
178
|
+
};
|
|
179
|
+
function getResizedPreviousAndNextPoint(nextRenderPoints, sourcePoint, targetPoint, handleIndex) {
|
|
180
|
+
const referencePoint = {
|
|
181
|
+
previous: null,
|
|
182
|
+
next: null
|
|
183
|
+
};
|
|
184
|
+
const startPoint = nextRenderPoints[handleIndex];
|
|
185
|
+
const endPoint = nextRenderPoints[handleIndex + 1];
|
|
186
|
+
const isHorizontal = Point.isHorizontal(startPoint, endPoint);
|
|
187
|
+
const isVertical = Point.isVertical(startPoint, endPoint);
|
|
188
|
+
const previousPoint = nextRenderPoints[handleIndex - 1] ?? nextRenderPoints[0];
|
|
189
|
+
const beforePreviousPoint = nextRenderPoints[handleIndex - 2] ?? sourcePoint;
|
|
190
|
+
if ((isHorizontal && Point.isHorizontal(beforePreviousPoint, previousPoint)) ||
|
|
191
|
+
(isVertical && Point.isVertical(beforePreviousPoint, previousPoint))) {
|
|
192
|
+
referencePoint.previous = previousPoint;
|
|
193
|
+
}
|
|
194
|
+
const nextPoint = nextRenderPoints[handleIndex + 2] ?? nextRenderPoints[nextRenderPoints.length - 1];
|
|
195
|
+
const afterNextPoint = nextRenderPoints[handleIndex + 3] ?? targetPoint;
|
|
196
|
+
if ((isHorizontal && Point.isHorizontal(nextPoint, afterNextPoint)) || (isVertical && Point.isVertical(nextPoint, afterNextPoint))) {
|
|
197
|
+
referencePoint.next = nextPoint;
|
|
198
|
+
}
|
|
199
|
+
return referencePoint;
|
|
200
|
+
}
|
|
201
|
+
function alignElbowSegment(startKeyPoint, endKeyPoint, resizeState, resizedPreviousAndNextPoint) {
|
|
202
|
+
let newStartPoint = startKeyPoint;
|
|
203
|
+
let newEndPoint = endKeyPoint;
|
|
204
|
+
if (Point.isHorizontal(startKeyPoint, endKeyPoint)) {
|
|
205
|
+
const offsetY = Point.getOffsetY(resizeState.startPoint, resizeState.endPoint);
|
|
206
|
+
let pointY = startKeyPoint[1] + offsetY;
|
|
207
|
+
if (resizedPreviousAndNextPoint.previous && Math.abs(resizedPreviousAndNextPoint.previous[1] - pointY) < LINE_ALIGN_TOLERANCE) {
|
|
208
|
+
pointY = resizedPreviousAndNextPoint.previous[1];
|
|
209
|
+
}
|
|
210
|
+
else if (resizedPreviousAndNextPoint.next && Math.abs(resizedPreviousAndNextPoint.next[1] - pointY) < LINE_ALIGN_TOLERANCE) {
|
|
211
|
+
pointY = resizedPreviousAndNextPoint.next[1];
|
|
212
|
+
}
|
|
213
|
+
newStartPoint = [startKeyPoint[0], pointY];
|
|
214
|
+
newEndPoint = [endKeyPoint[0], pointY];
|
|
215
|
+
}
|
|
216
|
+
if (Point.isVertical(startKeyPoint, endKeyPoint)) {
|
|
217
|
+
const offsetX = Point.getOffsetX(resizeState.startPoint, resizeState.endPoint);
|
|
218
|
+
let pointX = startKeyPoint[0] + offsetX;
|
|
219
|
+
if (resizedPreviousAndNextPoint.previous && Math.abs(resizedPreviousAndNextPoint.previous[0] - pointX) < LINE_ALIGN_TOLERANCE) {
|
|
220
|
+
pointX = resizedPreviousAndNextPoint.previous[0];
|
|
221
|
+
}
|
|
222
|
+
else if (resizedPreviousAndNextPoint.next && Math.abs(resizedPreviousAndNextPoint.next[0] - pointX) < LINE_ALIGN_TOLERANCE) {
|
|
223
|
+
pointX = resizedPreviousAndNextPoint.next[0];
|
|
224
|
+
}
|
|
225
|
+
newStartPoint = [pointX, startKeyPoint[1]];
|
|
226
|
+
newEndPoint = [pointX, endKeyPoint[1]];
|
|
227
|
+
}
|
|
228
|
+
return [newStartPoint, newEndPoint];
|
|
229
|
+
}
|
|
230
|
+
function getIndexAndDeleteCountByKeyPoint(board, element, dataPoints, nextRenderPoints, handleIndex) {
|
|
231
|
+
let index = null;
|
|
232
|
+
let deleteCount = null;
|
|
233
|
+
const startKeyPoint = nextRenderPoints[handleIndex];
|
|
234
|
+
const endKeyPoint = nextRenderPoints[handleIndex + 1];
|
|
235
|
+
if (!startKeyPoint || !endKeyPoint) {
|
|
236
|
+
return {
|
|
237
|
+
index,
|
|
238
|
+
deleteCount
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
const midDataPoints = dataPoints.slice(1, -1);
|
|
242
|
+
const startIndex = midDataPoints.findIndex(item => Point.isEquals(item, startKeyPoint));
|
|
243
|
+
const endIndex = midDataPoints.findIndex(item => Point.isEquals(item, endKeyPoint));
|
|
244
|
+
if (Math.max(startIndex, endIndex) > -1) {
|
|
245
|
+
if (startIndex > -1 && endIndex > -1) {
|
|
246
|
+
return {
|
|
247
|
+
index: startIndex,
|
|
248
|
+
deleteCount: 2
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
if (startIndex > -1 && endIndex === -1) {
|
|
252
|
+
const isReplace = startIndex < midDataPoints.length - 1 &&
|
|
253
|
+
Point.isAlign([midDataPoints[startIndex], midDataPoints[startIndex + 1], startKeyPoint, endKeyPoint]);
|
|
254
|
+
if (isReplace) {
|
|
255
|
+
return {
|
|
256
|
+
index: startIndex,
|
|
257
|
+
deleteCount: 2
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
return {
|
|
261
|
+
index: startIndex,
|
|
262
|
+
deleteCount: 1
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
if (startIndex === -1 && endIndex > -1) {
|
|
266
|
+
const isReplace = endIndex > 0 && Point.isAlign([midDataPoints[endIndex], midDataPoints[endIndex - 1], startKeyPoint, endKeyPoint]);
|
|
267
|
+
if (isReplace) {
|
|
268
|
+
return {
|
|
269
|
+
index: endIndex - 1,
|
|
270
|
+
deleteCount: 2
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
return {
|
|
274
|
+
index: endIndex,
|
|
275
|
+
deleteCount: 1
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
for (let i = 0; i < midDataPoints.length - 1; i++) {
|
|
281
|
+
const currentPoint = midDataPoints[i];
|
|
282
|
+
const nextPoint = midDataPoints[i + 1];
|
|
283
|
+
if (Point.isAlign([currentPoint, nextPoint, startKeyPoint, endKeyPoint])) {
|
|
284
|
+
index = i;
|
|
285
|
+
deleteCount = 2;
|
|
286
|
+
break;
|
|
287
|
+
}
|
|
288
|
+
if (Point.isAlign([currentPoint, nextPoint, startKeyPoint])) {
|
|
289
|
+
index = Math.min(i + 1, midDataPoints.length - 1);
|
|
290
|
+
deleteCount = 1;
|
|
291
|
+
break;
|
|
292
|
+
}
|
|
293
|
+
if (Point.isAlign([currentPoint, nextPoint, endKeyPoint])) {
|
|
294
|
+
index = Math.max(i - 1, 0);
|
|
295
|
+
deleteCount = 1;
|
|
296
|
+
break;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
if (index === null) {
|
|
301
|
+
deleteCount = 0;
|
|
302
|
+
if (midDataPoints.length > 0) {
|
|
303
|
+
const handleRefPair = getLineHandleRefPair(board, element);
|
|
304
|
+
const params = getElbowLineRouteOptions(board, element, handleRefPair);
|
|
305
|
+
const keyPoints = removeDuplicatePoints(generateElbowLineRoute(params));
|
|
306
|
+
const nextKeyPoints = simplifyOrthogonalPoints(keyPoints.slice(1, keyPoints.length - 1));
|
|
307
|
+
const nextDataPoints = [nextRenderPoints[0], ...midDataPoints, nextRenderPoints[nextRenderPoints.length - 1]];
|
|
308
|
+
const mirrorDataPoints = getMirrorDataPoints(board, nextDataPoints, nextKeyPoints, params);
|
|
309
|
+
for (let i = handleIndex - 1; i >= 0; i--) {
|
|
310
|
+
const previousIndex = mirrorDataPoints.slice(1, -1).findIndex(item => Point.isEquals(item, nextRenderPoints[i]));
|
|
311
|
+
if (previousIndex > -1) {
|
|
312
|
+
index = previousIndex + 1;
|
|
313
|
+
break;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
if (index === null) {
|
|
317
|
+
index = 0;
|
|
318
|
+
// When renderPoints is a straight line and dataPoints are not on the line,
|
|
319
|
+
// the default 'deleteCount' is set to midDataPoints.length.
|
|
320
|
+
if (Point.isAlign(nextRenderPoints)) {
|
|
321
|
+
deleteCount = midDataPoints.length;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
else {
|
|
326
|
+
index = 0;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return {
|
|
330
|
+
index,
|
|
331
|
+
deleteCount
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
function getMirrorDataPoints(board, nextDataPoints, nextKeyPoints, params) {
|
|
335
|
+
for (let index = 1; index < nextDataPoints.length - 2; index++) {
|
|
336
|
+
adjustByCustomPointStartIndex(board, index, nextDataPoints, nextKeyPoints, params);
|
|
337
|
+
}
|
|
338
|
+
return nextDataPoints;
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* adjust based parallel segment
|
|
342
|
+
*/
|
|
343
|
+
const adjustByCustomPointStartIndex = (board, customPointStartIndex, nextDataPoints, nextKeyPoints, params) => {
|
|
344
|
+
const beforePoint = nextDataPoints[customPointStartIndex - 1];
|
|
345
|
+
const startPoint = nextDataPoints[customPointStartIndex];
|
|
346
|
+
const endPoint = nextDataPoints[customPointStartIndex + 1];
|
|
347
|
+
const afterPoint = nextDataPoints[customPointStartIndex + 2];
|
|
348
|
+
const beforeSegment = [beforePoint, startPoint];
|
|
349
|
+
const afterSegment = [endPoint, afterPoint];
|
|
350
|
+
const isStraightWithBefore = Point.isAlign(beforeSegment);
|
|
351
|
+
const isStraightWithAfter = Point.isAlign(afterSegment);
|
|
352
|
+
let isAdjustStart = false;
|
|
353
|
+
let isAdjustEnd = false;
|
|
354
|
+
if (!isStraightWithBefore || !isStraightWithAfter) {
|
|
355
|
+
const midKeyPointsWithBefore = getMidKeyPoints(nextKeyPoints, beforeSegment[0], beforeSegment[1]);
|
|
356
|
+
const midKeyPointsWithAfter = getMidKeyPoints(nextKeyPoints, afterSegment[0], afterSegment[1]);
|
|
357
|
+
const hasMidKeyPoints = midKeyPointsWithBefore.length > 0 && midKeyPointsWithAfter.length > 0;
|
|
358
|
+
isAdjustStart = !isStraightWithBefore && !hasMidKeyPoints;
|
|
359
|
+
isAdjustEnd = !isStraightWithAfter && !hasMidKeyPoints;
|
|
360
|
+
}
|
|
361
|
+
if (isAdjustStart || isAdjustEnd) {
|
|
362
|
+
const parallelSegment = [startPoint, endPoint];
|
|
363
|
+
const parallelSegments = findOrthogonalParallelSegments(parallelSegment, nextKeyPoints);
|
|
364
|
+
const mirrorSegments = findMirrorSegments(board, parallelSegment, parallelSegments, params.sourceRectangle, params.targetRectangle);
|
|
365
|
+
if (mirrorSegments.length === 1) {
|
|
366
|
+
const mirrorSegment = mirrorSegments[0];
|
|
367
|
+
if (isAdjustStart) {
|
|
368
|
+
nextDataPoints.splice(customPointStartIndex, 1, mirrorSegment[0]);
|
|
369
|
+
}
|
|
370
|
+
if (isAdjustEnd) {
|
|
371
|
+
nextDataPoints.splice(customPointStartIndex + 1, 1, mirrorSegment[1]);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
else {
|
|
375
|
+
const isHorizontal = Point.isHorizontal(startPoint, endPoint);
|
|
376
|
+
const adjustIndex = isHorizontal ? 0 : 1;
|
|
377
|
+
if (isAdjustStart) {
|
|
378
|
+
const newStartPoint = [startPoint[0], startPoint[1]];
|
|
379
|
+
newStartPoint[adjustIndex] = beforePoint[adjustIndex];
|
|
380
|
+
nextDataPoints.splice(customPointStartIndex, 1, newStartPoint);
|
|
381
|
+
}
|
|
382
|
+
if (isAdjustEnd) {
|
|
383
|
+
const newEndPoint = [endPoint[0], endPoint[1]];
|
|
384
|
+
newEndPoint[adjustIndex] = afterPoint[adjustIndex];
|
|
385
|
+
nextDataPoints.splice(customPointStartIndex + 1, 1, newEndPoint);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
};
|
|
390
|
+
function isUpdatedHandleIndex(board, element, dataPoints, nextRenderPoints, handleIndex) {
|
|
391
|
+
const { deleteCount } = getIndexAndDeleteCountByKeyPoint(board, element, dataPoints, nextRenderPoints, handleIndex);
|
|
392
|
+
if (deleteCount !== null && deleteCount > 1) {
|
|
393
|
+
return true;
|
|
394
|
+
}
|
|
395
|
+
return false;
|
|
396
|
+
}
|
|
397
|
+
function getMidKeyPoints(simplifiedNextKeyPoints, startPoint, endPoint) {
|
|
398
|
+
let midElbowPoints = [];
|
|
399
|
+
let startPointIndex = -1;
|
|
400
|
+
let endPointIndex = -1;
|
|
401
|
+
for (let i = 0; i < simplifiedNextKeyPoints.length; i++) {
|
|
402
|
+
if (Point.isAlign([simplifiedNextKeyPoints[i], startPoint])) {
|
|
403
|
+
startPointIndex = i;
|
|
404
|
+
}
|
|
405
|
+
if (startPointIndex > -1 && Point.isAlign([simplifiedNextKeyPoints[i], endPoint])) {
|
|
406
|
+
endPointIndex = i;
|
|
407
|
+
break;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
if (startPointIndex > -1 && endPointIndex > -1) {
|
|
411
|
+
midElbowPoints = simplifiedNextKeyPoints.slice(startPointIndex, endPointIndex + 1);
|
|
412
|
+
}
|
|
413
|
+
return midElbowPoints;
|
|
414
|
+
}
|
|
415
|
+
function findOrthogonalParallelSegments(segment, keyPoints) {
|
|
416
|
+
const isHorizontalSegment = Point.isHorizontal(segment[0], segment[1]);
|
|
417
|
+
const parallelSegments = [];
|
|
418
|
+
for (let i = 0; i < keyPoints.length - 1; i++) {
|
|
419
|
+
const current = keyPoints[i];
|
|
420
|
+
const next = keyPoints[i + 1];
|
|
421
|
+
const isHorizontal = Point.isHorizontal(current, next, 0.1);
|
|
422
|
+
if (isHorizontalSegment && isHorizontal) {
|
|
423
|
+
parallelSegments.push([current, next]);
|
|
424
|
+
}
|
|
425
|
+
if (!isHorizontalSegment && !isHorizontal) {
|
|
426
|
+
parallelSegments.push([current, next]);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
return parallelSegments;
|
|
430
|
+
}
|
|
431
|
+
function findMirrorSegments(board, segment, parallelSegments, sourceRectangle, targetRectangle) {
|
|
432
|
+
const mirrorSegments = [];
|
|
433
|
+
for (let index = 0; index < parallelSegments.length; index++) {
|
|
434
|
+
const parallelPath = parallelSegments[index];
|
|
435
|
+
const startPoint = [segment[0][0], segment[0][1]];
|
|
436
|
+
const endPoint = [segment[1][0], segment[1][1]];
|
|
437
|
+
const isHorizontal = Point.isHorizontal(startPoint, endPoint);
|
|
438
|
+
const adjustDataIndex = isHorizontal ? 0 : 1;
|
|
439
|
+
startPoint[adjustDataIndex] = parallelPath[0][adjustDataIndex];
|
|
440
|
+
endPoint[adjustDataIndex] = parallelPath[1][adjustDataIndex];
|
|
441
|
+
const fakeRectangle = RectangleClient.getRectangleByPoints([startPoint, endPoint, ...parallelPath]);
|
|
442
|
+
const isValid = !RectangleClient.isHit(fakeRectangle, sourceRectangle) && !RectangleClient.isHit(fakeRectangle, targetRectangle);
|
|
443
|
+
if (isValid) {
|
|
444
|
+
mirrorSegments.push([startPoint, endPoint]);
|
|
445
|
+
// const fakeRectangleG = PlaitBoard.getRoughSVG(board).rectangle(
|
|
446
|
+
// fakeRectangle.x,
|
|
447
|
+
// fakeRectangle.y,
|
|
448
|
+
// fakeRectangle.width,
|
|
449
|
+
// fakeRectangle.height,
|
|
450
|
+
// { stroke: 'blue' }
|
|
451
|
+
// );
|
|
452
|
+
// PlaitBoard.getElementActiveHost(board).append(fakeRectangleG);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
return mirrorSegments;
|
|
456
|
+
}
|
|
457
|
+
const hasIllegalElbowPoint = (midDataPoints) => {
|
|
458
|
+
if (midDataPoints.length === 1) {
|
|
459
|
+
return true;
|
|
460
|
+
}
|
|
461
|
+
return midDataPoints.some((item, index) => {
|
|
462
|
+
const beforePoint = midDataPoints[index - 1];
|
|
463
|
+
const afterPoint = midDataPoints[index + 1];
|
|
464
|
+
const beforeSegment = beforePoint && [beforePoint, item];
|
|
465
|
+
const afterSegment = afterPoint && [item, afterPoint];
|
|
466
|
+
const isStraightWithBefore = beforeSegment && Point.isAlign(beforeSegment);
|
|
467
|
+
const isStraightWithAfter = afterSegment && Point.isAlign(afterSegment);
|
|
468
|
+
if (index === 0) {
|
|
469
|
+
return !isStraightWithAfter;
|
|
470
|
+
}
|
|
471
|
+
if (index === midDataPoints.length - 1) {
|
|
472
|
+
return !isStraightWithBefore;
|
|
473
|
+
}
|
|
474
|
+
return !isStraightWithBefore && !isStraightWithAfter;
|
|
475
|
+
});
|
|
476
|
+
};
|
|
477
|
+
|
|
478
|
+
const getElbowPoints = (board, element) => {
|
|
479
|
+
const handleRefPair = getLineHandleRefPair(board, element);
|
|
480
|
+
const params = getElbowLineRouteOptions(board, element, handleRefPair);
|
|
481
|
+
// console.log(params, 'params');
|
|
482
|
+
const isIntersect = isSourceAndTargetIntersect(params);
|
|
483
|
+
if (isIntersect) {
|
|
484
|
+
return simplifyOrthogonalPoints(getPoints(handleRefPair.source.point, handleRefPair.source.direction, handleRefPair.target.point, handleRefPair.target.direction, 0));
|
|
485
|
+
}
|
|
486
|
+
const keyPoints = removeDuplicatePoints(generateElbowLineRoute(params));
|
|
487
|
+
const nextKeyPoints = keyPoints.slice(1, keyPoints.length - 1);
|
|
488
|
+
if (element.points.length === 2) {
|
|
489
|
+
return simplifyOrthogonalPoints(keyPoints);
|
|
490
|
+
}
|
|
491
|
+
else {
|
|
492
|
+
const simplifiedNextKeyPoints = simplifyOrthogonalPoints(nextKeyPoints);
|
|
493
|
+
const dataPoints = removeDuplicatePoints(PlaitLine.getPoints(board, element));
|
|
494
|
+
const midDataPoints = dataPoints.slice(1, -1);
|
|
495
|
+
if (hasIllegalElbowPoint(midDataPoints)) {
|
|
496
|
+
return simplifyOrthogonalPoints(keyPoints);
|
|
497
|
+
}
|
|
498
|
+
const nextDataPoints = [simplifiedNextKeyPoints[0], ...midDataPoints, simplifiedNextKeyPoints[simplifiedNextKeyPoints.length - 1]];
|
|
499
|
+
const mirrorDataPoints = getMirrorDataPoints(board, nextDataPoints, simplifiedNextKeyPoints, params);
|
|
500
|
+
// console.log(mirrorDataPoints, 'mirrorDataPoints');
|
|
501
|
+
const renderPoints = [keyPoints[0]];
|
|
502
|
+
for (let index = 0; index < mirrorDataPoints.length - 1; index++) {
|
|
503
|
+
let currentPoint = mirrorDataPoints[index];
|
|
504
|
+
let nextPoint = mirrorDataPoints[index + 1];
|
|
505
|
+
const isStraight = Point.isAlign([currentPoint, nextPoint]);
|
|
506
|
+
if (!isStraight) {
|
|
507
|
+
const midKeyPoints = getMidKeyPoints(simplifiedNextKeyPoints, currentPoint, nextPoint);
|
|
508
|
+
if (midKeyPoints.length) {
|
|
509
|
+
renderPoints.push(currentPoint);
|
|
510
|
+
renderPoints.push(...midKeyPoints);
|
|
511
|
+
}
|
|
512
|
+
else {
|
|
513
|
+
renderPoints.push(currentPoint);
|
|
514
|
+
console.log('unknown data points');
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
else {
|
|
518
|
+
renderPoints.push(currentPoint);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
renderPoints.push(keyPoints[keyPoints.length - 2], keyPoints[keyPoints.length - 1]);
|
|
522
|
+
// Remove the middle point to avoid the situation where the starting and ending positions are drawn back, such as when sourcePoint is between nextSourcePoint and the first key point.
|
|
523
|
+
// Issue
|
|
524
|
+
// keyPoint2
|
|
525
|
+
// |
|
|
526
|
+
// |
|
|
527
|
+
// nextPoint---sourcePoint---keyPoint1
|
|
528
|
+
// The correct rendering should be (nextPoint should be filtered out):
|
|
529
|
+
// keyPoint2
|
|
530
|
+
// |
|
|
531
|
+
// |
|
|
532
|
+
// sourcePoint---keyPoint1
|
|
533
|
+
const ret = simplifyOrthogonalPoints(renderPoints);
|
|
534
|
+
return ret;
|
|
535
|
+
}
|
|
536
|
+
};
|
|
537
|
+
const getNextSourceAndTargetPoints = (board, element) => {
|
|
538
|
+
const options = getElbowLineRouteOptions(board, element);
|
|
539
|
+
return [options.nextSourcePoint, options.nextTargetPoint];
|
|
540
|
+
};
|
|
541
|
+
const getSourceAndTargetRectangle = (board, element, handleRefPair) => {
|
|
542
|
+
let sourceElement = element.source.boundId ? getElementById(board, element.source.boundId) : undefined;
|
|
543
|
+
let targetElement = element.target.boundId ? getElementById(board, element.target.boundId) : undefined;
|
|
544
|
+
if (!sourceElement) {
|
|
545
|
+
const source = handleRefPair.source;
|
|
546
|
+
sourceElement = createFakeElement(source.point, source.vector);
|
|
547
|
+
}
|
|
548
|
+
if (!targetElement) {
|
|
549
|
+
const target = handleRefPair.target;
|
|
550
|
+
targetElement = createFakeElement(target.point, target.vector);
|
|
551
|
+
}
|
|
552
|
+
const sourceRectangle = RectangleClient.inflate(RectangleClient.getRectangleByPoints(sourceElement.points), getStrokeWidthByElement(sourceElement) * 2);
|
|
553
|
+
const targetRectangle = RectangleClient.inflate(RectangleClient.getRectangleByPoints(targetElement.points), getStrokeWidthByElement(targetElement) * 2);
|
|
554
|
+
return {
|
|
555
|
+
sourceRectangle,
|
|
556
|
+
targetRectangle
|
|
557
|
+
};
|
|
558
|
+
};
|
|
559
|
+
const createFakeElement = (startPoint, vector) => {
|
|
560
|
+
const point = getPointByVector(startPoint, vector, -25);
|
|
561
|
+
const points = RectangleClient.getPoints(RectangleClient.getRectangleByCenterPoint(point, 50, 50));
|
|
562
|
+
return createGeometryElement(BasicShapes.rectangle, points, '');
|
|
563
|
+
};
|
|
564
|
+
function getNextRenderPoints(board, element, renderPoints) {
|
|
565
|
+
let newRenderKeyPoints = renderPoints ?? getElbowPoints(board, element);
|
|
566
|
+
const [nextSourcePoint, nextTargetPoint] = getNextSourceAndTargetPoints(board, element);
|
|
567
|
+
newRenderKeyPoints.splice(0, 1, nextSourcePoint);
|
|
568
|
+
newRenderKeyPoints.splice(-1, 1, nextTargetPoint);
|
|
569
|
+
return removeDuplicatePoints(newRenderKeyPoints);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
const ARROW_LENGTH = 20;
|
|
573
|
+
const drawLineArrow = (element, points, options) => {
|
|
574
|
+
const arrowG = createG();
|
|
575
|
+
if (PlaitLine.isSourceMark(element, LineMarkerType.none) && PlaitLine.isTargetMark(element, LineMarkerType.none)) {
|
|
576
|
+
return null;
|
|
577
|
+
}
|
|
578
|
+
const strokeWidth = getStrokeWidthByElement(element);
|
|
579
|
+
const offset = (strokeWidth * strokeWidth) / 3;
|
|
580
|
+
if (points.length === 1) {
|
|
581
|
+
points = [points[0], [points[0][0] + 0.1, points[0][1]]];
|
|
582
|
+
}
|
|
583
|
+
if (!PlaitLine.isSourceMark(element, LineMarkerType.none)) {
|
|
584
|
+
const source = getExtendPoint(points[0], points[1], ARROW_LENGTH + offset);
|
|
585
|
+
const sourceArrow = getArrow(element, { marker: element.source.marker, source, target: points[0], isSource: true }, options);
|
|
586
|
+
sourceArrow && arrowG.appendChild(sourceArrow);
|
|
587
|
+
}
|
|
588
|
+
if (!PlaitLine.isTargetMark(element, LineMarkerType.none)) {
|
|
589
|
+
const source = getExtendPoint(points[points.length - 1], points[points.length - 2], ARROW_LENGTH + offset);
|
|
590
|
+
const arrow = getArrow(element, { marker: element.target.marker, source, target: points[points.length - 1], isSource: false }, options);
|
|
591
|
+
arrow && arrowG.appendChild(arrow);
|
|
592
|
+
}
|
|
593
|
+
return arrowG;
|
|
594
|
+
};
|
|
595
|
+
const getArrow = (element, arrowOptions, options) => {
|
|
596
|
+
const { marker, target, source, isSource } = arrowOptions;
|
|
597
|
+
let targetArrow;
|
|
598
|
+
switch (marker) {
|
|
599
|
+
case LineMarkerType.openTriangle: {
|
|
600
|
+
targetArrow = drawOpenTriangle(element, source, target, options);
|
|
601
|
+
break;
|
|
602
|
+
}
|
|
603
|
+
case LineMarkerType.solidTriangle: {
|
|
604
|
+
targetArrow = drawSolidTriangle(source, target, options);
|
|
605
|
+
break;
|
|
606
|
+
}
|
|
607
|
+
case LineMarkerType.arrow: {
|
|
608
|
+
targetArrow = drawArrow(element, source, target, options);
|
|
609
|
+
break;
|
|
610
|
+
}
|
|
611
|
+
case LineMarkerType.sharpArrow: {
|
|
612
|
+
targetArrow = drawSharpArrow(source, target, options);
|
|
613
|
+
break;
|
|
614
|
+
}
|
|
615
|
+
case LineMarkerType.oneSideUp: {
|
|
616
|
+
targetArrow = drawOneSideArrow(source, target, isSource ? 'down' : 'up', options);
|
|
617
|
+
break;
|
|
618
|
+
}
|
|
619
|
+
case LineMarkerType.oneSideDown: {
|
|
620
|
+
targetArrow = drawOneSideArrow(source, target, isSource ? 'up' : 'down', options);
|
|
621
|
+
break;
|
|
622
|
+
}
|
|
623
|
+
case LineMarkerType.hollowTriangle: {
|
|
624
|
+
targetArrow = drawHollowTriangleArrow(source, target, options);
|
|
625
|
+
break;
|
|
626
|
+
}
|
|
627
|
+
case LineMarkerType.singleSlash: {
|
|
628
|
+
targetArrow = drawSingleSlash(source, target, isSource, options);
|
|
629
|
+
break;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
return targetArrow;
|
|
633
|
+
};
|
|
634
|
+
const drawSharpArrow = (source, target, options) => {
|
|
635
|
+
const startPoint = target;
|
|
636
|
+
const { pointLeft, pointRight } = arrowPoints(source, target, 20);
|
|
637
|
+
const g = createG();
|
|
638
|
+
const path = createPath();
|
|
639
|
+
let polylinePath = `M${pointRight[0]},${pointRight[1]}A25,25,20,0,1,${pointLeft[0]},${pointLeft[1]}L${startPoint[0]},${startPoint[1]}Z`;
|
|
640
|
+
path.setAttribute('d', polylinePath);
|
|
641
|
+
path.setAttribute('stroke', `${options?.stroke}`);
|
|
642
|
+
path.setAttribute('stroke-width', `${options?.strokeWidth}`);
|
|
643
|
+
path.setAttribute('fill', `${options?.stroke}`);
|
|
644
|
+
g.appendChild(path);
|
|
645
|
+
return g;
|
|
646
|
+
};
|
|
647
|
+
const drawArrow = (element, source, target, options) => {
|
|
648
|
+
const unitVector = getUnitVectorByPointAndPoint(source, target);
|
|
649
|
+
const strokeWidth = getStrokeWidthByElement(element);
|
|
650
|
+
const endPoint = [target[0] + (strokeWidth * unitVector[0]) / 2, target[1] + (strokeWidth * unitVector[1]) / 2];
|
|
651
|
+
const distance = distanceBetweenPointAndPoint(...source, ...endPoint);
|
|
652
|
+
const middlePoint = [
|
|
653
|
+
endPoint[0] - (((distance * 3) / 5 + strokeWidth) / 2) * unitVector[0],
|
|
654
|
+
endPoint[1] - (((distance * 3) / 5 + strokeWidth) / 2) * unitVector[1]
|
|
655
|
+
];
|
|
656
|
+
const { pointLeft, pointRight } = arrowPoints(source, endPoint, 30);
|
|
657
|
+
const arrowG = drawLinearPath([pointLeft, endPoint, pointRight, middlePoint], { ...options, fill: options.stroke }, true);
|
|
658
|
+
const path = arrowG.querySelector('path');
|
|
659
|
+
path.setAttribute('stroke-linejoin', 'round');
|
|
660
|
+
return arrowG;
|
|
661
|
+
};
|
|
662
|
+
const drawSolidTriangle = (source, target, options) => {
|
|
663
|
+
const endPoint = target;
|
|
664
|
+
const { pointLeft, pointRight } = arrowPoints(source, endPoint, 30);
|
|
665
|
+
return drawLinearPath([pointLeft, endPoint, pointRight], { ...options, fill: options.stroke }, true);
|
|
666
|
+
};
|
|
667
|
+
const drawOpenTriangle = (element, source, target, options) => {
|
|
668
|
+
const unitVector = getUnitVectorByPointAndPoint(source, target);
|
|
669
|
+
const strokeWidth = getStrokeWidthByElement(element);
|
|
670
|
+
const endPoint = [target[0] + (strokeWidth * unitVector[0]) / 2, target[1] + (strokeWidth * unitVector[1]) / 2];
|
|
671
|
+
const { pointLeft, pointRight } = arrowPoints(source, endPoint, 40);
|
|
672
|
+
return drawLinearPath([pointLeft, endPoint, pointRight], options);
|
|
673
|
+
};
|
|
674
|
+
const drawOneSideArrow = (source, target, side, options) => {
|
|
675
|
+
const { pointLeft, pointRight } = arrowPoints(source, target, 40);
|
|
676
|
+
return drawLinearPath([side === 'up' ? pointRight : pointLeft, target], options);
|
|
677
|
+
};
|
|
678
|
+
const drawSingleSlash = (source, target, isSource, options) => {
|
|
679
|
+
const length = distanceBetweenPointAndPoint(...source, ...target);
|
|
680
|
+
const middlePoint = getExtendPoint(target, source, length / 2);
|
|
681
|
+
const angle = isSource ? 120 : 60;
|
|
682
|
+
const start = rotate(...source, ...middlePoint, (angle * Math.PI) / 180);
|
|
683
|
+
const end = rotate(...target, ...middlePoint, (angle * Math.PI) / 180);
|
|
684
|
+
return drawLinearPath([start, end], options);
|
|
685
|
+
};
|
|
686
|
+
const drawHollowTriangleArrow = (source, target, options) => {
|
|
687
|
+
const { pointLeft, pointRight } = arrowPoints(source, target, 30);
|
|
688
|
+
return drawLinearPath([pointLeft, pointRight, target], { ...options, fill: 'white' }, true);
|
|
689
|
+
};
|
|
690
|
+
|
|
691
|
+
const getShape = (value) => {
|
|
692
|
+
if (PlaitDrawElement.isImage(value)) {
|
|
693
|
+
return BasicShapes.rectangle;
|
|
694
|
+
}
|
|
695
|
+
return value.shape;
|
|
696
|
+
};
|
|
697
|
+
|
|
698
|
+
class LineShapeGenerator extends Generator {
|
|
699
|
+
canDraw(element, data) {
|
|
700
|
+
return true;
|
|
701
|
+
}
|
|
702
|
+
draw(element, data) {
|
|
703
|
+
let lineG;
|
|
704
|
+
lineG = drawLine(this.board, element);
|
|
705
|
+
return lineG;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
const getHitRectangleResizeHandleRef = (board, rectangle, point) => {
|
|
710
|
+
const resizeHandleRefs = getRectangleResizeHandleRefs(rectangle, RESIZE_HANDLE_DIAMETER);
|
|
711
|
+
const result = resizeHandleRefs.find(resizeHandleRef => {
|
|
712
|
+
return RectangleClient.isHit(RectangleClient.getRectangleByPoints([point, point]), resizeHandleRef.rectangle);
|
|
713
|
+
});
|
|
714
|
+
return result;
|
|
715
|
+
};
|
|
716
|
+
const getHitOutlineGeometry = (board, point, offset = 0) => {
|
|
717
|
+
let geometry = null;
|
|
718
|
+
depthFirstRecursion(board, node => {
|
|
719
|
+
if (PlaitDrawElement.isGeometry(node) || PlaitDrawElement.isImage(node)) {
|
|
720
|
+
let client = RectangleClient.getRectangleByPoints(node.points);
|
|
721
|
+
client = RectangleClient.getOutlineRectangle(client, offset);
|
|
722
|
+
const shape = getShape(node);
|
|
723
|
+
const isHit = getEngine(shape).isHit(client, point);
|
|
724
|
+
if (isHit) {
|
|
725
|
+
geometry = node;
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
}, getIsRecursionFunc(board), true);
|
|
729
|
+
return geometry;
|
|
730
|
+
};
|
|
731
|
+
|
|
732
|
+
const SHAPE_MAX_LENGTH = 6;
|
|
733
|
+
const memorizedShape = new WeakMap();
|
|
734
|
+
const getMemorizeKey = (element) => {
|
|
735
|
+
let key = '';
|
|
736
|
+
switch (true) {
|
|
737
|
+
case PlaitDrawElement.isText(element): {
|
|
738
|
+
key = MemorizeKey.text;
|
|
739
|
+
break;
|
|
740
|
+
}
|
|
741
|
+
case PlaitDrawElement.isBasicShape(element): {
|
|
742
|
+
key = MemorizeKey.basicShape;
|
|
743
|
+
break;
|
|
744
|
+
}
|
|
745
|
+
case PlaitDrawElement.isFlowchart(element): {
|
|
746
|
+
key = MemorizeKey.flowchart;
|
|
747
|
+
break;
|
|
748
|
+
}
|
|
749
|
+
case PlaitDrawElement.isLine(element): {
|
|
750
|
+
key = MemorizeKey.line;
|
|
751
|
+
break;
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
return key;
|
|
755
|
+
};
|
|
756
|
+
const getLineMemorizedLatest = () => {
|
|
757
|
+
const properties = getMemorizedLatest(MemorizeKey.line);
|
|
758
|
+
delete properties?.text;
|
|
759
|
+
return { ...properties } || {};
|
|
760
|
+
};
|
|
761
|
+
const getMemorizedLatestByPointer = (pointer) => {
|
|
762
|
+
let memorizeKey = '';
|
|
763
|
+
if (PlaitDrawElement.isBasicShape({ shape: pointer })) {
|
|
764
|
+
memorizeKey = pointer === BasicShapes.text ? MemorizeKey.text : MemorizeKey.basicShape;
|
|
765
|
+
}
|
|
766
|
+
else {
|
|
767
|
+
memorizeKey = MemorizeKey.flowchart;
|
|
768
|
+
}
|
|
769
|
+
const properties = { ...getMemorizedLatest(memorizeKey) } || {};
|
|
770
|
+
const textProperties = { ...properties.text } || {};
|
|
771
|
+
delete properties.text;
|
|
772
|
+
return { textProperties, geometryProperties: properties };
|
|
773
|
+
};
|
|
774
|
+
const memorizeLatestText = (element, operations) => {
|
|
775
|
+
const memorizeKey = getMemorizeKey(element);
|
|
776
|
+
let textMemory = getMemorizedLatest(memorizeKey)?.text || {};
|
|
777
|
+
const setNodeOperation = operations.find(operation => operation.type === 'set_node');
|
|
778
|
+
if (setNodeOperation) {
|
|
779
|
+
const newProperties = setNodeOperation.newProperties;
|
|
780
|
+
textMemory = { ...textMemory, ...newProperties };
|
|
781
|
+
memorizeLatest(memorizeKey, 'text', textMemory);
|
|
782
|
+
}
|
|
783
|
+
};
|
|
784
|
+
const memorizeLatestShape = (board, shape) => {
|
|
785
|
+
const shapes = memorizedShape.has(board) ? memorizedShape.get(board) : [];
|
|
786
|
+
const shapeIndex = shapes.indexOf(shape);
|
|
787
|
+
if (shape === BasicShapes.text || shapeIndex === 0) {
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
if (shapeIndex !== -1) {
|
|
791
|
+
shapes.splice(shapeIndex, 1);
|
|
792
|
+
}
|
|
793
|
+
else {
|
|
794
|
+
if (shapes.length === SHAPE_MAX_LENGTH) {
|
|
795
|
+
shapes.pop();
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
shapes.unshift(shape);
|
|
799
|
+
memorizedShape.set(board, shapes);
|
|
800
|
+
};
|
|
801
|
+
const getMemorizedLatestShape = (board) => {
|
|
802
|
+
return memorizedShape.get(board);
|
|
803
|
+
};
|
|
804
|
+
|
|
805
|
+
const createLineElement = (shape, points, source, target, texts, options) => {
|
|
806
|
+
return {
|
|
807
|
+
id: idCreator(),
|
|
808
|
+
type: 'line',
|
|
809
|
+
shape,
|
|
810
|
+
source,
|
|
811
|
+
texts: texts ? texts : [],
|
|
812
|
+
target,
|
|
813
|
+
opacity: 1,
|
|
814
|
+
points,
|
|
815
|
+
...options
|
|
816
|
+
};
|
|
817
|
+
};
|
|
818
|
+
const getLinePoints = (board, element) => {
|
|
819
|
+
switch (element.shape) {
|
|
820
|
+
case LineShape.elbow: {
|
|
821
|
+
return getElbowPoints(board, element);
|
|
822
|
+
}
|
|
823
|
+
case LineShape.curve: {
|
|
824
|
+
return getCurvePoints(board, element);
|
|
825
|
+
}
|
|
826
|
+
default: {
|
|
827
|
+
const points = PlaitLine.getPoints(board, element);
|
|
828
|
+
const handleRefPair = getLineHandleRefPair(board, element);
|
|
829
|
+
points[0] = handleRefPair.source.point;
|
|
830
|
+
points[points.length - 1] = handleRefPair.target.point;
|
|
831
|
+
return points;
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
};
|
|
835
|
+
const getCurvePoints = (board, element) => {
|
|
836
|
+
if (element.points.length === 2) {
|
|
837
|
+
const handleRefPair = getLineHandleRefPair(board, element);
|
|
838
|
+
const { source, target } = handleRefPair;
|
|
839
|
+
const sourceBoundElement = handleRefPair.source.boundElement;
|
|
840
|
+
const targetBoundElement = handleRefPair.target.boundElement;
|
|
841
|
+
let curvePoints = [source.point];
|
|
842
|
+
const sumDistance = distanceBetweenPointAndPoint(...source.point, ...target.point);
|
|
843
|
+
const offset = 12 + sumDistance / 3;
|
|
844
|
+
if (sourceBoundElement) {
|
|
845
|
+
curvePoints.push(getPointByVector(source.point, source.vector, offset));
|
|
846
|
+
}
|
|
847
|
+
if (targetBoundElement) {
|
|
848
|
+
curvePoints.push(getPointByVector(target.point, target.vector, offset));
|
|
849
|
+
}
|
|
850
|
+
const isSingleBound = (sourceBoundElement && !targetBoundElement) || (!sourceBoundElement && targetBoundElement);
|
|
851
|
+
if (isSingleBound) {
|
|
852
|
+
curvePoints.push(target.point);
|
|
853
|
+
const points = Q2C(curvePoints);
|
|
854
|
+
return pointsOnBezierCurves(points);
|
|
855
|
+
}
|
|
856
|
+
if (!sourceBoundElement && !targetBoundElement) {
|
|
857
|
+
curvePoints.push(getPointByVector(source.point, source.vector, offset));
|
|
858
|
+
curvePoints.push(getPointByVector(target.point, target.vector, offset));
|
|
859
|
+
}
|
|
860
|
+
curvePoints.push(target.point);
|
|
861
|
+
return pointsOnBezierCurves(curvePoints);
|
|
862
|
+
}
|
|
863
|
+
else {
|
|
864
|
+
let dataPoints = PlaitLine.getPoints(board, element);
|
|
865
|
+
dataPoints = removeDuplicatePoints(dataPoints);
|
|
866
|
+
const points = catmullRomFitting(dataPoints);
|
|
867
|
+
return pointsOnBezierCurves(points);
|
|
868
|
+
}
|
|
869
|
+
};
|
|
870
|
+
function getMiddlePoints(board, element) {
|
|
871
|
+
const result = [];
|
|
872
|
+
const shape = element.shape;
|
|
873
|
+
const hideBuffer = 10;
|
|
874
|
+
if (shape === LineShape.straight) {
|
|
875
|
+
const points = PlaitLine.getPoints(board, element);
|
|
876
|
+
for (let i = 0; i < points.length - 1; i++) {
|
|
877
|
+
const distance = distanceBetweenPointAndPoint(...points[i], ...points[i + 1]);
|
|
878
|
+
if (distance < hideBuffer)
|
|
879
|
+
continue;
|
|
880
|
+
result.push([(points[i][0] + points[i + 1][0]) / 2, (points[i][1] + points[i + 1][1]) / 2]);
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
if (shape === LineShape.curve) {
|
|
884
|
+
const points = PlaitLine.getPoints(board, element);
|
|
885
|
+
const pointsOnBezier = getCurvePoints(board, element);
|
|
886
|
+
if (points.length === 2) {
|
|
887
|
+
const start = 0;
|
|
888
|
+
const endIndex = pointsOnBezier.length - 1;
|
|
889
|
+
const middleIndex = Math.round((start + endIndex) / 2);
|
|
890
|
+
result.push(pointsOnBezier[middleIndex]);
|
|
891
|
+
}
|
|
892
|
+
else {
|
|
893
|
+
for (let i = 0; i < points.length - 1; i++) {
|
|
894
|
+
const startIndex = pointsOnBezier.findIndex(point => point[0] === points[i][0] && point[1] === points[i][1]);
|
|
895
|
+
const endIndex = pointsOnBezier.findIndex(point => point[0] === points[i + 1][0] && point[1] === points[i + 1][1]);
|
|
896
|
+
const middleIndex = Math.round((startIndex + endIndex) / 2);
|
|
897
|
+
const distance = distanceBetweenPointAndPoint(...points[i], ...points[i + 1]);
|
|
898
|
+
if (distance < hideBuffer)
|
|
899
|
+
continue;
|
|
900
|
+
result.push(pointsOnBezier[middleIndex]);
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
if (shape === LineShape.elbow) {
|
|
905
|
+
const renderPoints = getElbowPoints(board, element);
|
|
906
|
+
const options = getElbowLineRouteOptions(board, element);
|
|
907
|
+
const isIntersect = isSourceAndTargetIntersect(options);
|
|
908
|
+
if (!isIntersect) {
|
|
909
|
+
const [nextSourcePoint, nextTargetPoint] = getNextSourceAndTargetPoints(board, element);
|
|
910
|
+
for (let i = 0; i < renderPoints.length - 1; i++) {
|
|
911
|
+
if ((i == 0 && Point.isEquals(renderPoints[i + 1], nextSourcePoint)) ||
|
|
912
|
+
(i === renderPoints.length - 2 && Point.isEquals(renderPoints[renderPoints.length - 2], nextTargetPoint))) {
|
|
913
|
+
continue;
|
|
914
|
+
}
|
|
915
|
+
const [currentX, currentY] = renderPoints[i];
|
|
916
|
+
const [nextX, nextY] = renderPoints[i + 1];
|
|
917
|
+
const middlePoint = [(currentX + nextX) / 2, (currentY + nextY) / 2];
|
|
918
|
+
result.push(middlePoint);
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
return result;
|
|
923
|
+
}
|
|
924
|
+
const drawLine = (board, element) => {
|
|
925
|
+
const strokeWidth = getStrokeWidthByElement(element);
|
|
926
|
+
const strokeColor = getStrokeColorByElement(board, element);
|
|
927
|
+
const strokeLineDash = getLineDashByElement(element);
|
|
928
|
+
const options = { stroke: strokeColor, strokeWidth, strokeLineDash };
|
|
929
|
+
const lineG = createG();
|
|
930
|
+
let points = getLinePoints(board, element);
|
|
931
|
+
let line;
|
|
932
|
+
if (element.shape === LineShape.curve) {
|
|
933
|
+
line = PlaitBoard.getRoughSVG(board).curve(points, options);
|
|
934
|
+
}
|
|
935
|
+
else {
|
|
936
|
+
line = drawLinearPath(points, options);
|
|
937
|
+
}
|
|
938
|
+
const id = idCreator();
|
|
939
|
+
line.setAttribute('mask', `url(#${id})`);
|
|
940
|
+
lineG.appendChild(line);
|
|
941
|
+
const { mask, maskTargetFillRect } = drawMask(board, element, id);
|
|
942
|
+
lineG.appendChild(mask);
|
|
943
|
+
line.appendChild(maskTargetFillRect);
|
|
944
|
+
const arrow = drawLineArrow(element, points, { stroke: strokeColor, strokeWidth });
|
|
945
|
+
arrow && lineG.appendChild(arrow);
|
|
946
|
+
return lineG;
|
|
947
|
+
};
|
|
948
|
+
const getConnectionByNearestPoint = (board, point, hitElement) => {
|
|
949
|
+
let rectangle = RectangleClient.getRectangleByPoints(hitElement.points);
|
|
950
|
+
let nearestPoint = getNearestPoint(hitElement, point);
|
|
951
|
+
const hitConnector = getHitConnectorPoint(nearestPoint, hitElement, rectangle);
|
|
952
|
+
nearestPoint = hitConnector ? hitConnector : nearestPoint;
|
|
953
|
+
return [(nearestPoint[0] - rectangle.x) / rectangle.width, (nearestPoint[1] - rectangle.y) / rectangle.height];
|
|
954
|
+
};
|
|
955
|
+
const getHitConnectorPoint = (point, hitElement, rectangle) => {
|
|
956
|
+
const shape = getShape(hitElement);
|
|
957
|
+
const connector = getEngine(shape).getConnectorPoints(rectangle);
|
|
958
|
+
const points = RectangleClient.getPoints(RectangleClient.getRectangleByCenterPoint(point, 10, 10));
|
|
959
|
+
const pointRectangle = RectangleClient.getRectangleByPoints(points);
|
|
960
|
+
return connector.find(point => {
|
|
961
|
+
return RectangleClient.isHit(pointRectangle, RectangleClient.getRectangleByPoints([point, point]));
|
|
962
|
+
});
|
|
963
|
+
};
|
|
964
|
+
const getLineTextRectangle = (board, element, index) => {
|
|
965
|
+
const text = element.texts[index];
|
|
966
|
+
const elbowPoints = getLinePoints(board, element);
|
|
967
|
+
const point = getPointOnPolyline(elbowPoints, text.position);
|
|
968
|
+
return {
|
|
969
|
+
x: point[0] - text.width / 2,
|
|
970
|
+
y: point[1] - text.height / 2,
|
|
971
|
+
width: text.width,
|
|
972
|
+
height: text.height
|
|
973
|
+
};
|
|
974
|
+
};
|
|
975
|
+
const getLines = (board) => {
|
|
976
|
+
return findElements(board, {
|
|
977
|
+
match: (element) => PlaitDrawElement.isLine(element),
|
|
978
|
+
recursion: (element) => PlaitDrawElement.isDrawElement(element)
|
|
979
|
+
});
|
|
980
|
+
};
|
|
981
|
+
// quadratic Bezier to cubic Bezier
|
|
982
|
+
const Q2C = (points) => {
|
|
983
|
+
const result = [];
|
|
984
|
+
const numSegments = points.length / 3;
|
|
985
|
+
for (let i = 0; i < numSegments; i++) {
|
|
986
|
+
const start = points[i];
|
|
987
|
+
const qControl = points[i + 1];
|
|
988
|
+
const end = points[i + 2];
|
|
989
|
+
const startDistance = distanceBetweenPointAndPoint(...start, ...qControl);
|
|
990
|
+
const endDistance = distanceBetweenPointAndPoint(...end, ...qControl);
|
|
991
|
+
const cControl1 = getExtendPoint(start, qControl, (startDistance * 2) / 3);
|
|
992
|
+
const cControl2 = getExtendPoint(end, qControl, (endDistance * 2) / 3);
|
|
993
|
+
result.push(start, cControl1, cControl2, end);
|
|
994
|
+
}
|
|
995
|
+
return result;
|
|
996
|
+
};
|
|
997
|
+
const handleLineCreating = (board, lineShape, sourcePoint, movingPoint, sourceElement, lineShapeG) => {
|
|
998
|
+
const hitElement = getHitOutlineGeometry(board, movingPoint, REACTION_MARGIN);
|
|
999
|
+
const targetConnection = hitElement ? getConnectionByNearestPoint(board, movingPoint, hitElement) : undefined;
|
|
1000
|
+
const connection = sourceElement ? getConnectionByNearestPoint(board, sourcePoint, sourceElement) : undefined;
|
|
1001
|
+
const targetBoundId = hitElement ? hitElement.id : undefined;
|
|
1002
|
+
const lineGenerator = new LineShapeGenerator(board);
|
|
1003
|
+
const memorizedLatest = getLineMemorizedLatest();
|
|
1004
|
+
let sourceMarker, targetMarker;
|
|
1005
|
+
sourceMarker = memorizedLatest.source;
|
|
1006
|
+
targetMarker = memorizedLatest.target;
|
|
1007
|
+
sourceMarker && delete memorizedLatest.source;
|
|
1008
|
+
targetMarker && delete memorizedLatest.target;
|
|
1009
|
+
const temporaryLineElement = createLineElement(lineShape, [sourcePoint, movingPoint], { marker: sourceMarker || LineMarkerType.none, connection: connection, boundId: sourceElement?.id }, { marker: targetMarker || LineMarkerType.arrow, connection: targetConnection, boundId: targetBoundId }, [], {
|
|
1010
|
+
strokeWidth: DefaultLineStyle.strokeWidth,
|
|
1011
|
+
...memorizedLatest
|
|
1012
|
+
});
|
|
1013
|
+
const linePoints = getLinePoints(board, temporaryLineElement);
|
|
1014
|
+
const otherPoint = linePoints[0];
|
|
1015
|
+
temporaryLineElement.points[1] = alignPoints(otherPoint, movingPoint);
|
|
1016
|
+
lineGenerator.processDrawing(temporaryLineElement, lineShapeG);
|
|
1017
|
+
PlaitBoard.getElementActiveHost(board).append(lineShapeG);
|
|
1018
|
+
return temporaryLineElement;
|
|
1019
|
+
};
|
|
1020
|
+
function drawMask(board, element, id) {
|
|
1021
|
+
const mask = createMask();
|
|
1022
|
+
mask.setAttribute('id', id);
|
|
1023
|
+
const points = getLinePoints(board, element);
|
|
1024
|
+
let rectangle = RectangleClient.getRectangleByPoints(points);
|
|
1025
|
+
rectangle = RectangleClient.getOutlineRectangle(rectangle, -30);
|
|
1026
|
+
const maskFillRect = createRect(rectangle, {
|
|
1027
|
+
fill: 'white'
|
|
1028
|
+
});
|
|
1029
|
+
mask.appendChild(maskFillRect);
|
|
1030
|
+
const texts = element.texts;
|
|
1031
|
+
texts.forEach((text, index) => {
|
|
1032
|
+
let textRectangle = getLineTextRectangle(board, element, index);
|
|
1033
|
+
textRectangle = RectangleClient.inflate(textRectangle, LINE_TEXT_SPACE * 2);
|
|
1034
|
+
const rect = createRect(textRectangle, {
|
|
1035
|
+
fill: 'black'
|
|
1036
|
+
});
|
|
1037
|
+
mask.appendChild(rect);
|
|
1038
|
+
});
|
|
1039
|
+
// open line
|
|
1040
|
+
const maskTargetFillRect = createRect(rectangle);
|
|
1041
|
+
maskTargetFillRect.setAttribute('opacity', '0');
|
|
1042
|
+
maskTargetFillRect.setAttribute('fill', 'none');
|
|
1043
|
+
return { mask, maskTargetFillRect };
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
const getSelectedDrawElements = (board) => {
|
|
1047
|
+
const selectedElements = getSelectedElements(board).filter(value => PlaitDrawElement.isDrawElement(value));
|
|
1048
|
+
return selectedElements;
|
|
1049
|
+
};
|
|
1050
|
+
const getSelectedGeometryElements = (board) => {
|
|
1051
|
+
const selectedElements = getSelectedElements(board).filter(value => PlaitDrawElement.isGeometry(value));
|
|
1052
|
+
return selectedElements;
|
|
1053
|
+
};
|
|
1054
|
+
const getSelectedLineElements = (board) => {
|
|
1055
|
+
const selectedElements = getSelectedElements(board).filter(value => PlaitDrawElement.isLine(value));
|
|
1056
|
+
return selectedElements;
|
|
1057
|
+
};
|
|
1058
|
+
const getSelectedImageElements = (board) => {
|
|
1059
|
+
const selectedElements = getSelectedElements(board).filter(value => PlaitDrawElement.isImage(value));
|
|
1060
|
+
return selectedElements;
|
|
1061
|
+
};
|
|
1062
|
+
|
|
1063
|
+
var LineResizeHandle;
|
|
1064
|
+
(function (LineResizeHandle) {
|
|
1065
|
+
LineResizeHandle["source"] = "source";
|
|
1066
|
+
LineResizeHandle["target"] = "target";
|
|
1067
|
+
LineResizeHandle["addHandle"] = "addHandle";
|
|
1068
|
+
})(LineResizeHandle || (LineResizeHandle = {}));
|
|
1069
|
+
const getHitLineResizeHandleRef = (board, element, point) => {
|
|
1070
|
+
let dataPoints = PlaitLine.getPoints(board, element);
|
|
1071
|
+
const index = getHitPointIndex(dataPoints, point);
|
|
1072
|
+
if (index !== -1) {
|
|
1073
|
+
const handleIndex = index;
|
|
1074
|
+
if (index === 0) {
|
|
1075
|
+
return { handle: LineResizeHandle.source, handleIndex };
|
|
1076
|
+
}
|
|
1077
|
+
if (index === dataPoints.length - 1) {
|
|
1078
|
+
return { handle: LineResizeHandle.target, handleIndex };
|
|
1079
|
+
}
|
|
1080
|
+
// elbow line, data points only verify source connection point and target connection point
|
|
1081
|
+
if (element.shape !== LineShape.elbow) {
|
|
1082
|
+
return { handleIndex };
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
const middlePoints = getMiddlePoints(board, element);
|
|
1086
|
+
const indexOfMiddlePoints = getHitPointIndex(middlePoints, point);
|
|
1087
|
+
if (indexOfMiddlePoints !== -1) {
|
|
1088
|
+
return {
|
|
1089
|
+
handle: LineResizeHandle.addHandle,
|
|
1090
|
+
handleIndex: indexOfMiddlePoints
|
|
1091
|
+
};
|
|
1092
|
+
}
|
|
1093
|
+
return undefined;
|
|
1094
|
+
};
|
|
1095
|
+
function getHitPointIndex(points, movingPoint) {
|
|
1096
|
+
const rectangles = points.map(point => {
|
|
1097
|
+
return {
|
|
1098
|
+
x: point[0] - RESIZE_HANDLE_DIAMETER / 2,
|
|
1099
|
+
y: point[1] - RESIZE_HANDLE_DIAMETER / 2,
|
|
1100
|
+
width: RESIZE_HANDLE_DIAMETER,
|
|
1101
|
+
height: RESIZE_HANDLE_DIAMETER
|
|
1102
|
+
};
|
|
1103
|
+
});
|
|
1104
|
+
const rectangle = rectangles.find(rectangle => {
|
|
1105
|
+
return RectangleClient.isHit(RectangleClient.getRectangleByPoints([movingPoint, movingPoint]), rectangle);
|
|
1106
|
+
});
|
|
1107
|
+
return rectangle ? rectangles.indexOf(rectangle) : -1;
|
|
1108
|
+
}
|
|
1109
|
+
const getHitLineTextIndex = (board, element, point) => {
|
|
1110
|
+
const texts = element.texts;
|
|
1111
|
+
if (!texts.length)
|
|
1112
|
+
return -1;
|
|
1113
|
+
const points = getLinePoints(board, element);
|
|
1114
|
+
return texts.findIndex(text => {
|
|
1115
|
+
const center = getPointOnPolyline(points, text.position);
|
|
1116
|
+
const rectangle = {
|
|
1117
|
+
x: center[0] - text.width / 2,
|
|
1118
|
+
y: center[1] - text.height / 2,
|
|
1119
|
+
width: text.width,
|
|
1120
|
+
height: text.height
|
|
1121
|
+
};
|
|
1122
|
+
return RectangleClient.isHit(rectangle, RectangleClient.getRectangleByPoints([point, point]));
|
|
1123
|
+
});
|
|
1124
|
+
};
|
|
1125
|
+
|
|
1126
|
+
const isTextExceedingBounds = (geometry) => {
|
|
1127
|
+
const client = RectangleClient.getRectangleByPoints(geometry.points);
|
|
1128
|
+
if (geometry.textHeight > client.height) {
|
|
1129
|
+
return true;
|
|
161
1130
|
}
|
|
162
|
-
|
|
163
|
-
return strokeWidth;
|
|
1131
|
+
return false;
|
|
164
1132
|
};
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
const strokeColor = element.strokeColor || defaultColor;
|
|
168
|
-
return strokeColor;
|
|
1133
|
+
const isHitLineText = (board, element, point) => {
|
|
1134
|
+
return getHitLineTextIndex(board, element, point) !== -1;
|
|
169
1135
|
};
|
|
170
|
-
const
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
: DefaultGeometryStyle.fill;
|
|
174
|
-
const fill = element.fill || defaultFill;
|
|
175
|
-
return fill;
|
|
1136
|
+
const isHitPolyLine = (pathPoints, point, strokeWidth, expand = 0) => {
|
|
1137
|
+
const distance = distanceBetweenPointAndSegments(pathPoints, point);
|
|
1138
|
+
return distance <= strokeWidth + expand;
|
|
176
1139
|
};
|
|
177
|
-
const
|
|
178
|
-
|
|
1140
|
+
const isRectangleHitDrawElement = (board, element, selection) => {
|
|
1141
|
+
const rangeRectangle = RectangleClient.getRectangleByPoints([selection.anchor, selection.focus]);
|
|
1142
|
+
if (PlaitDrawElement.isGeometry(element)) {
|
|
1143
|
+
const client = RectangleClient.getRectangleByPoints(element.points);
|
|
1144
|
+
if (isTextExceedingBounds(element)) {
|
|
1145
|
+
const textClient = getTextRectangle(element);
|
|
1146
|
+
return RectangleClient.isHit(rangeRectangle, client) || RectangleClient.isHit(rangeRectangle, textClient);
|
|
1147
|
+
}
|
|
1148
|
+
return RectangleClient.isHit(rangeRectangle, client);
|
|
1149
|
+
}
|
|
1150
|
+
if (PlaitDrawElement.isImage(element)) {
|
|
1151
|
+
const client = RectangleClient.getRectangleByPoints(element.points);
|
|
1152
|
+
return RectangleClient.isHit(rangeRectangle, client);
|
|
1153
|
+
}
|
|
1154
|
+
if (PlaitDrawElement.isLine(element)) {
|
|
1155
|
+
const points = getLinePoints(board, element);
|
|
1156
|
+
const strokeWidth = getStrokeWidthByElement(element);
|
|
1157
|
+
const isHitText = isHitLineText(board, element, selection.focus);
|
|
1158
|
+
const isHit = isHitPolyLine(points, selection.focus, strokeWidth, 3) || isHitText;
|
|
1159
|
+
const isContainPolyLinePoint = points.some(point => {
|
|
1160
|
+
return RectangleClient.isHit(rangeRectangle, RectangleClient.getRectangleByPoints([point, point]));
|
|
1161
|
+
});
|
|
1162
|
+
const isIntersect = Point.isEquals(selection.anchor, selection.focus) ? isHit : isPolylineHitRectangle(points, rangeRectangle);
|
|
1163
|
+
return isContainPolyLinePoint || isIntersect;
|
|
1164
|
+
}
|
|
1165
|
+
return null;
|
|
179
1166
|
};
|
|
180
|
-
const
|
|
181
|
-
|
|
1167
|
+
const isHitDrawElement = (board, element, point) => {
|
|
1168
|
+
if (PlaitDrawElement.isGeometry(element)) {
|
|
1169
|
+
const fill = getFillByElement(board, element);
|
|
1170
|
+
// when shape equals text, fill is not allowed
|
|
1171
|
+
if (fill !== DefaultGeometryStyle.fill && fill !== TRANSPARENT && !PlaitDrawElement.isText(element)) {
|
|
1172
|
+
return isRectangleHitDrawElement(board, element, { anchor: point, focus: point });
|
|
1173
|
+
}
|
|
1174
|
+
else {
|
|
1175
|
+
// if shape equals text, only check text rectangle
|
|
1176
|
+
if (PlaitDrawElement.isText(element)) {
|
|
1177
|
+
const textClient = getTextRectangle(element);
|
|
1178
|
+
let isHitText = RectangleClient.isPointInRectangle(textClient, point);
|
|
1179
|
+
return isHitText;
|
|
1180
|
+
}
|
|
1181
|
+
const strokeWidth = getStrokeWidthByElement(element);
|
|
1182
|
+
const engine = getEngine(getShape(element));
|
|
1183
|
+
const corners = engine.getCornerPoints(RectangleClient.getRectangleByPoints(element.points));
|
|
1184
|
+
const isHit = isHitPolyLine(corners, point, strokeWidth, 3);
|
|
1185
|
+
const textClient = getTextRectangle(element);
|
|
1186
|
+
let isHitText = RectangleClient.isPointInRectangle(textClient, point);
|
|
1187
|
+
return isHit || isHitText;
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
if (PlaitDrawElement.isImage(element) || PlaitDrawElement.isLine(element)) {
|
|
1191
|
+
return isRectangleHitDrawElement(board, element, { anchor: point, focus: point });
|
|
1192
|
+
}
|
|
1193
|
+
return null;
|
|
1194
|
+
};
|
|
1195
|
+
|
|
1196
|
+
const getCenterPointsOnPolygon$1 = (points) => {
|
|
1197
|
+
const centerPoints = [];
|
|
1198
|
+
for (let i = 0; i < points.length; i++) {
|
|
1199
|
+
let j = i == points.length - 1 ? 0 : i + 1;
|
|
1200
|
+
centerPoints.push([(points[i][0] + points[j][0]) / 2, (points[i][1] + points[j][1]) / 2]);
|
|
1201
|
+
}
|
|
1202
|
+
return centerPoints;
|
|
1203
|
+
};
|
|
1204
|
+
const getCrossingPointBetweenPointAndPolygon = (corners, point) => {
|
|
1205
|
+
const result = [];
|
|
1206
|
+
for (let index = 1; index <= corners.length; index++) {
|
|
1207
|
+
let start = corners[index - 1];
|
|
1208
|
+
let end = index === corners.length ? corners[0] : corners[index];
|
|
1209
|
+
const crossingPoint = getCrossingPointsBetweenPointAndSegment(point, start, end);
|
|
1210
|
+
result.push(...crossingPoint);
|
|
1211
|
+
}
|
|
1212
|
+
return result;
|
|
1213
|
+
};
|
|
1214
|
+
const getPolygonEdgeByConnectionPoint = (corners, point) => {
|
|
1215
|
+
for (let index = 1; index <= corners.length; index++) {
|
|
1216
|
+
let start = corners[index - 1];
|
|
1217
|
+
let end = index === corners.length ? corners[0] : corners[index];
|
|
1218
|
+
if (isPointOnSegment(point, start, end)) {
|
|
1219
|
+
return [start, end];
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
return null;
|
|
182
1223
|
};
|
|
183
1224
|
|
|
184
1225
|
const heightRatio$1 = 3 / 4;
|
|
@@ -203,13 +1244,13 @@ const CommentEngine = {
|
|
|
203
1244
|
getEdgeByConnectionPoint(rectangle, pointOfRectangle) {
|
|
204
1245
|
const corners = getCommentPoints(rectangle);
|
|
205
1246
|
const point = RectangleClient.getConnectionPoint(rectangle, pointOfRectangle);
|
|
206
|
-
return
|
|
1247
|
+
return getPolygonEdgeByConnectionPoint(corners, point);
|
|
207
1248
|
},
|
|
208
1249
|
getConnectorPoints(rectangle) {
|
|
209
1250
|
return RectangleClient.getEdgeCenterPoints(rectangle);
|
|
210
1251
|
},
|
|
211
1252
|
getTextRectangle(element) {
|
|
212
|
-
const elementRectangle = getRectangleByPoints(element.points);
|
|
1253
|
+
const elementRectangle = RectangleClient.getRectangleByPoints(element.points);
|
|
213
1254
|
const strokeWidth = getStrokeWidthByElement(element);
|
|
214
1255
|
const height = element.textHeight;
|
|
215
1256
|
const width = elementRectangle.width - ShapeDefaultSpace.rectangleAndText * 2 - strokeWidth * 2;
|
|
@@ -253,10 +1294,26 @@ function createPolygonEngine(options) {
|
|
|
253
1294
|
getNearestPoint(rectangle, point) {
|
|
254
1295
|
return getNearestPointBetweenPointAndSegments(point, getPoints(rectangle));
|
|
255
1296
|
},
|
|
1297
|
+
getNearestCrossingPoint(rectangle, point) {
|
|
1298
|
+
const corners = getPoints(rectangle);
|
|
1299
|
+
const crossingPoints = getCrossingPointBetweenPointAndPolygon(corners, point);
|
|
1300
|
+
let nearestPoint = crossingPoints[0];
|
|
1301
|
+
let nearestDistance = distanceBetweenPointAndPoint(point[0], point[1], nearestPoint[0], nearestPoint[1]);
|
|
1302
|
+
crossingPoints
|
|
1303
|
+
.filter((v, index) => index > 0)
|
|
1304
|
+
.forEach(crossingPoint => {
|
|
1305
|
+
let distance = distanceBetweenPointAndPoint(point[0], point[1], crossingPoint[0], crossingPoint[1]);
|
|
1306
|
+
if (distance < nearestDistance) {
|
|
1307
|
+
nearestDistance = distance;
|
|
1308
|
+
nearestPoint = crossingPoint;
|
|
1309
|
+
}
|
|
1310
|
+
});
|
|
1311
|
+
return nearestPoint;
|
|
1312
|
+
},
|
|
256
1313
|
getEdgeByConnectionPoint(rectangle, pointOfRectangle) {
|
|
257
1314
|
const corners = getPoints(rectangle);
|
|
258
1315
|
const point = RectangleClient.getConnectionPoint(rectangle, pointOfRectangle);
|
|
259
|
-
return
|
|
1316
|
+
return getPolygonEdgeByConnectionPoint(corners, point);
|
|
260
1317
|
},
|
|
261
1318
|
getConnectorPoints(rectangle) {
|
|
262
1319
|
if (options.getConnectorPoints) {
|
|
@@ -337,8 +1394,9 @@ const EllipseEngine = {
|
|
|
337
1394
|
const point = [connectionPoint[0] - centerPoint[0], -(connectionPoint[1] - centerPoint[1])];
|
|
338
1395
|
const a = rectangle.width / 2;
|
|
339
1396
|
const b = rectangle.height / 2;
|
|
340
|
-
const slope =
|
|
341
|
-
|
|
1397
|
+
const slope = getEllipseTangentSlope(point[0], point[1], a, b);
|
|
1398
|
+
const vector = getVectorFromPointAndSlope(point[0], point[1], slope);
|
|
1399
|
+
return vector;
|
|
342
1400
|
},
|
|
343
1401
|
getConnectorPoints(rectangle) {
|
|
344
1402
|
return RectangleClient.getEdgeCenterPoints(rectangle);
|
|
@@ -386,31 +1444,6 @@ function getNearestPointBetweenPointAndEllipse(point, center, rx, ry, rotation =
|
|
|
386
1444
|
const signY = point[1] > center[1] ? 1 : -1;
|
|
387
1445
|
return [center[0] + a * tx * signX, center[1] + b * ty * signY];
|
|
388
1446
|
}
|
|
389
|
-
/**
|
|
390
|
-
* the result of slope is based on Cartesian coordinate system
|
|
391
|
-
* x, y are based on the position in the Cartesian coordinate system
|
|
392
|
-
*/
|
|
393
|
-
function getTangentSlope(x, y, a, b) {
|
|
394
|
-
const k = (-b * b * x) / (a * a * y);
|
|
395
|
-
return k;
|
|
396
|
-
}
|
|
397
|
-
/**
|
|
398
|
-
* x, y are based on the position in the Cartesian coordinate system
|
|
399
|
-
*/
|
|
400
|
-
function getVectorBySlope(x, y, slope) {
|
|
401
|
-
const deltaX = 30;
|
|
402
|
-
const deltaY = -slope * deltaX;
|
|
403
|
-
let start = [0 - deltaX, 0 - deltaY];
|
|
404
|
-
let end = [0 + deltaX, 0 + deltaY];
|
|
405
|
-
// y < 0 acts on the lower half of the x-axis, with the starting point at the top and the end point at the bottom.
|
|
406
|
-
if (y < 0) {
|
|
407
|
-
const temp = start;
|
|
408
|
-
start = end;
|
|
409
|
-
end = temp;
|
|
410
|
-
}
|
|
411
|
-
const vector = [end[0] - start[0], end[1] - start[1]];
|
|
412
|
-
return vector;
|
|
413
|
-
}
|
|
414
1447
|
|
|
415
1448
|
const getHexagonPoints = (rectangle) => {
|
|
416
1449
|
return [
|
|
@@ -502,7 +1535,7 @@ const ParallelogramEngine = createPolygonEngine({
|
|
|
502
1535
|
getPolygonPoints: getParallelogramPoints,
|
|
503
1536
|
getConnectorPoints: (rectangle) => {
|
|
504
1537
|
const cornerPoints = getParallelogramPoints(rectangle);
|
|
505
|
-
return getCenterPointsOnPolygon(cornerPoints);
|
|
1538
|
+
return getCenterPointsOnPolygon$1(cornerPoints);
|
|
506
1539
|
},
|
|
507
1540
|
getTextRectangle(element) {
|
|
508
1541
|
const rectangle = getTextRectangle(element);
|
|
@@ -525,7 +1558,7 @@ const getPentagonPoints = (rectangle) => {
|
|
|
525
1558
|
const PentagonEngine = createPolygonEngine({
|
|
526
1559
|
getPolygonPoints: getPentagonPoints,
|
|
527
1560
|
getTextRectangle(element) {
|
|
528
|
-
const elementRectangle = getRectangleByPoints(element.points);
|
|
1561
|
+
const elementRectangle = RectangleClient.getRectangleByPoints(element.points);
|
|
529
1562
|
const strokeWidth = getStrokeWidthByElement(element);
|
|
530
1563
|
const height = element.textHeight;
|
|
531
1564
|
const originWidth = elementRectangle.width - ShapeDefaultSpace.rectangleAndText * 2 - strokeWidth * 2;
|
|
@@ -575,7 +1608,7 @@ const ProcessArrowEngine = createPolygonEngine({
|
|
|
575
1608
|
getPolygonPoints: getProcessArrowPoints,
|
|
576
1609
|
getTextRectangle(element) {
|
|
577
1610
|
const rectangle = getTextRectangle(element);
|
|
578
|
-
const elementRectangle = getRectangleByPoints(element.points);
|
|
1611
|
+
const elementRectangle = RectangleClient.getRectangleByPoints(element.points);
|
|
579
1612
|
const width = rectangle.width;
|
|
580
1613
|
rectangle.width = elementRectangle.height / 2;
|
|
581
1614
|
rectangle.x += elementRectangle.height / 2;
|
|
@@ -588,7 +1621,7 @@ const RectangleEngine = {
|
|
|
588
1621
|
return drawRectangle(board, rectangle, { ...options, fillStyle: 'solid' });
|
|
589
1622
|
},
|
|
590
1623
|
isHit(rectangle, point) {
|
|
591
|
-
const rangeRectangle = RectangleClient.
|
|
1624
|
+
const rangeRectangle = RectangleClient.getRectangleByPoints([point, point]);
|
|
592
1625
|
return RectangleClient.isHit(rectangle, rangeRectangle);
|
|
593
1626
|
},
|
|
594
1627
|
getCornerPoints(rectangle) {
|
|
@@ -600,7 +1633,7 @@ const RectangleEngine = {
|
|
|
600
1633
|
getEdgeByConnectionPoint(rectangle, pointOfRectangle) {
|
|
601
1634
|
const corners = RectangleEngine.getCornerPoints(rectangle);
|
|
602
1635
|
const point = RectangleClient.getConnectionPoint(rectangle, pointOfRectangle);
|
|
603
|
-
return
|
|
1636
|
+
return getPolygonEdgeByConnectionPoint(corners, point);
|
|
604
1637
|
},
|
|
605
1638
|
getConnectorPoints(rectangle) {
|
|
606
1639
|
return RectangleClient.getEdgeCenterPoints(rectangle);
|
|
@@ -649,7 +1682,7 @@ const RoundRectangleEngine = {
|
|
|
649
1682
|
getEdgeByConnectionPoint(rectangle, pointOfRectangle) {
|
|
650
1683
|
const corners = RectangleEngine.getCornerPoints(rectangle);
|
|
651
1684
|
const point = RectangleClient.getConnectionPoint(rectangle, pointOfRectangle);
|
|
652
|
-
return
|
|
1685
|
+
return getPolygonEdgeByConnectionPoint(corners, point);
|
|
653
1686
|
},
|
|
654
1687
|
getConnectorPoints(rectangle) {
|
|
655
1688
|
return RectangleClient.getEdgeCenterPoints(rectangle);
|
|
@@ -728,7 +1761,7 @@ const RoundCommentEngine = {
|
|
|
728
1761
|
getEdgeByConnectionPoint(rectangle, pointOfRectangle) {
|
|
729
1762
|
const corners = getRoundCommentPoints(rectangle);
|
|
730
1763
|
const point = RectangleClient.getConnectionPoint(rectangle, pointOfRectangle);
|
|
731
|
-
return
|
|
1764
|
+
return getPolygonEdgeByConnectionPoint(corners, point);
|
|
732
1765
|
},
|
|
733
1766
|
getConnectorPoints(rectangle) {
|
|
734
1767
|
return [
|
|
@@ -739,7 +1772,7 @@ const RoundCommentEngine = {
|
|
|
739
1772
|
];
|
|
740
1773
|
},
|
|
741
1774
|
getTextRectangle(element) {
|
|
742
|
-
const elementRectangle = getRectangleByPoints(element.points);
|
|
1775
|
+
const elementRectangle = RectangleClient.getRectangleByPoints(element.points);
|
|
743
1776
|
const strokeWidth = getStrokeWidthByElement(element);
|
|
744
1777
|
const height = element.textHeight;
|
|
745
1778
|
const width = elementRectangle.width - ShapeDefaultSpace.rectangleAndText * 2 - strokeWidth * 2;
|
|
@@ -775,7 +1808,7 @@ const TrapezoidEngine = createPolygonEngine({
|
|
|
775
1808
|
getPolygonPoints: getTrapezoidPoints,
|
|
776
1809
|
getConnectorPoints(rectangle) {
|
|
777
1810
|
const points = getTrapezoidPoints(rectangle);
|
|
778
|
-
return getCenterPointsOnPolygon(points);
|
|
1811
|
+
return getCenterPointsOnPolygon$1(points);
|
|
779
1812
|
},
|
|
780
1813
|
getTextRectangle(element) {
|
|
781
1814
|
const rectangle = getTextRectangle(element);
|
|
@@ -797,11 +1830,11 @@ const TriangleEngine = createPolygonEngine({
|
|
|
797
1830
|
getPolygonPoints: getTrianglePoints,
|
|
798
1831
|
getConnectorPoints(rectangle) {
|
|
799
1832
|
const cornerPoints = getTrianglePoints(rectangle);
|
|
800
|
-
const lineCenterPoints = getCenterPointsOnPolygon(cornerPoints);
|
|
1833
|
+
const lineCenterPoints = getCenterPointsOnPolygon$1(cornerPoints);
|
|
801
1834
|
return [...lineCenterPoints, ...cornerPoints];
|
|
802
1835
|
},
|
|
803
1836
|
getTextRectangle(element) {
|
|
804
|
-
const elementRectangle = getRectangleByPoints(element.points);
|
|
1837
|
+
const elementRectangle = RectangleClient.getRectangleByPoints(element.points);
|
|
805
1838
|
const strokeWidth = getStrokeWidthByElement(element);
|
|
806
1839
|
const height = element.textHeight;
|
|
807
1840
|
const originWidth = elementRectangle.width - ShapeDefaultSpace.rectangleAndText * 2 - strokeWidth * 2;
|
|
@@ -860,7 +1893,7 @@ const StarEngine = createPolygonEngine({
|
|
|
860
1893
|
return [points[1], points[3], points[5], points[7], points[9]];
|
|
861
1894
|
},
|
|
862
1895
|
getTextRectangle(element) {
|
|
863
|
-
const elementRectangle = getRectangleByPoints(element.points);
|
|
1896
|
+
const elementRectangle = RectangleClient.getRectangleByPoints(element.points);
|
|
864
1897
|
const strokeWidth = getStrokeWidthByElement(element);
|
|
865
1898
|
const height = element.textHeight;
|
|
866
1899
|
const originWidth = elementRectangle.width - ShapeDefaultSpace.rectangleAndText * 2 - strokeWidth * 2;
|
|
@@ -887,10 +1920,18 @@ const TerminalEngine = {
|
|
|
887
1920
|
getNearestPoint(rectangle, point) {
|
|
888
1921
|
return getNearestPointBetweenPointAndRoundRectangle(point, rectangle, getStartEndRadius(rectangle));
|
|
889
1922
|
},
|
|
890
|
-
|
|
891
|
-
const
|
|
892
|
-
const
|
|
893
|
-
|
|
1923
|
+
getTangentVectorByConnectionPoint(rectangle, pointOfRectangle) {
|
|
1924
|
+
const connectionPoint = RectangleClient.getConnectionPoint(rectangle, pointOfRectangle);
|
|
1925
|
+
const radius = getStartEndRadius(rectangle);
|
|
1926
|
+
const center = getBoundCenterOfRoundRectangle(rectangle, radius, connectionPoint);
|
|
1927
|
+
if (center) {
|
|
1928
|
+
const point = [connectionPoint[0] - center[0], -(connectionPoint[1] - center[1])];
|
|
1929
|
+
const a = radius;
|
|
1930
|
+
const b = radius;
|
|
1931
|
+
const slope = getEllipseTangentSlope(point[0], point[1], a, b);
|
|
1932
|
+
return getVectorFromPointAndSlope(point[0], point[1], slope);
|
|
1933
|
+
}
|
|
1934
|
+
return null;
|
|
894
1935
|
},
|
|
895
1936
|
getConnectorPoints(rectangle) {
|
|
896
1937
|
return RectangleClient.getEdgeCenterPoints(rectangle);
|
|
@@ -900,33 +1941,37 @@ const getStartEndRadius = (rectangle) => {
|
|
|
900
1941
|
return Math.min(rectangle.width / 2, rectangle.height / 2);
|
|
901
1942
|
};
|
|
902
1943
|
function getNearestPointBetweenPointAndRoundRectangle(point, rectangle, radius) {
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
1944
|
+
let result = null;
|
|
1945
|
+
let boundCenter = getBoundCenterOfRoundRectangle(rectangle, radius, point);
|
|
1946
|
+
if (boundCenter) {
|
|
1947
|
+
result = getNearestPointBetweenPointAndEllipse(point, boundCenter, radius, radius);
|
|
1948
|
+
}
|
|
1949
|
+
else {
|
|
1950
|
+
const cornerPoints = RectangleClient.getCornerPoints(rectangle);
|
|
1951
|
+
result = getNearestPointBetweenPointAndSegments(point, cornerPoints);
|
|
1952
|
+
}
|
|
1953
|
+
return result;
|
|
1954
|
+
}
|
|
1955
|
+
function getBoundCenterOfRoundRectangle(rectangle, radius, point) {
|
|
1956
|
+
const { x, y, width, height } = rectangle;
|
|
1957
|
+
let center = null;
|
|
1958
|
+
const inLeftTop = point[0] >= x && point[0] <= x + radius && point[1] >= y && point[1] <= y + radius;
|
|
908
1959
|
if (inLeftTop) {
|
|
909
|
-
|
|
1960
|
+
center = [x + radius, y + radius];
|
|
910
1961
|
}
|
|
911
|
-
const inLeftBottom = point[0] >=
|
|
1962
|
+
const inLeftBottom = point[0] >= x && point[0] <= x + radius && point[1] >= y + height - radius && point[1] <= y + height;
|
|
912
1963
|
if (inLeftBottom) {
|
|
913
|
-
|
|
1964
|
+
center = [x + radius, y + height - radius];
|
|
914
1965
|
}
|
|
915
|
-
const inRightTop = point[0] >=
|
|
1966
|
+
const inRightTop = point[0] >= x + width - radius && point[0] <= x + width && point[1] >= y && point[1] <= y + radius;
|
|
916
1967
|
if (inRightTop) {
|
|
917
|
-
|
|
1968
|
+
center = [x + width - radius, y + radius];
|
|
918
1969
|
}
|
|
919
|
-
const inRightBottom = point[0] >=
|
|
920
|
-
point[0] <= rectX + width &&
|
|
921
|
-
point[1] >= rectY + height - radius &&
|
|
922
|
-
point[1] <= rectY + height;
|
|
1970
|
+
const inRightBottom = point[0] >= x + width - radius && point[0] <= x + width && point[1] >= y + height - radius && point[1] <= y + height;
|
|
923
1971
|
if (inRightBottom) {
|
|
924
|
-
|
|
925
|
-
}
|
|
926
|
-
if (circleCenter) {
|
|
927
|
-
result = getNearestPointBetweenPointAndEllipse(point, circleCenter, radius, radius);
|
|
1972
|
+
center = [x + width - radius, y + height - radius];
|
|
928
1973
|
}
|
|
929
|
-
return
|
|
1974
|
+
return center;
|
|
930
1975
|
}
|
|
931
1976
|
|
|
932
1977
|
const getManualInputPoints = (rectangle) => {
|
|
@@ -941,10 +1986,10 @@ const ManualInputEngine = createPolygonEngine({
|
|
|
941
1986
|
getPolygonPoints: getManualInputPoints,
|
|
942
1987
|
getConnectorPoints: (rectangle) => {
|
|
943
1988
|
const cornerPoints = getManualInputPoints(rectangle);
|
|
944
|
-
return getCenterPointsOnPolygon(cornerPoints);
|
|
1989
|
+
return getCenterPointsOnPolygon$1(cornerPoints);
|
|
945
1990
|
},
|
|
946
1991
|
getTextRectangle: (element) => {
|
|
947
|
-
const elementRectangle = getRectangleByPoints(element.points);
|
|
1992
|
+
const elementRectangle = RectangleClient.getRectangleByPoints(element.points);
|
|
948
1993
|
const strokeWidth = getStrokeWidthByElement(element);
|
|
949
1994
|
const height = element.textHeight;
|
|
950
1995
|
const width = elementRectangle.width - ShapeDefaultSpace.rectangleAndText * 2 - strokeWidth * 2;
|
|
@@ -993,7 +2038,7 @@ const ManualLoopEngine = createPolygonEngine({
|
|
|
993
2038
|
getPolygonPoints: getManualLoopPoints,
|
|
994
2039
|
getConnectorPoints: (rectangle) => {
|
|
995
2040
|
const cornerPoints = getManualLoopPoints(rectangle);
|
|
996
|
-
return getCenterPointsOnPolygon(cornerPoints);
|
|
2041
|
+
return getCenterPointsOnPolygon$1(cornerPoints);
|
|
997
2042
|
},
|
|
998
2043
|
getTextRectangle(element) {
|
|
999
2044
|
const rectangle = getTextRectangle(element);
|
|
@@ -1015,11 +2060,11 @@ const MergeEngine = createPolygonEngine({
|
|
|
1015
2060
|
getPolygonPoints: getMergePoints,
|
|
1016
2061
|
getConnectorPoints: (rectangle) => {
|
|
1017
2062
|
const cornerPoints = getMergePoints(rectangle);
|
|
1018
|
-
const lineCenterPoints = getCenterPointsOnPolygon(cornerPoints);
|
|
2063
|
+
const lineCenterPoints = getCenterPointsOnPolygon$1(cornerPoints);
|
|
1019
2064
|
return [...lineCenterPoints, ...cornerPoints];
|
|
1020
2065
|
},
|
|
1021
2066
|
getTextRectangle(element) {
|
|
1022
|
-
const elementRectangle = getRectangleByPoints(element.points);
|
|
2067
|
+
const elementRectangle = RectangleClient.getRectangleByPoints(element.points);
|
|
1023
2068
|
const strokeWidth = getStrokeWidthByElement(element);
|
|
1024
2069
|
const height = element.textHeight;
|
|
1025
2070
|
const originWidth = elementRectangle.width - ShapeDefaultSpace.rectangleAndText * 2 - strokeWidth * 2;
|
|
@@ -1043,7 +2088,7 @@ const DelayEngine = {
|
|
|
1043
2088
|
},
|
|
1044
2089
|
isHit(rectangle, point) {
|
|
1045
2090
|
//split shape to rectangle and a half ellipse
|
|
1046
|
-
const rangeRectangle = RectangleClient.
|
|
2091
|
+
const rangeRectangle = RectangleClient.getRectangleByPoints([point, point]);
|
|
1047
2092
|
const isInRectangle = RectangleClient.isHit({
|
|
1048
2093
|
...rectangle,
|
|
1049
2094
|
width: (rectangle.width * 3) / 4
|
|
@@ -1067,8 +2112,8 @@ const DelayEngine = {
|
|
|
1067
2112
|
const point = [connectionPoint[0] - centerPoint[0], -(connectionPoint[1] - centerPoint[1])];
|
|
1068
2113
|
const a = rectangle.width / 4;
|
|
1069
2114
|
const b = rectangle.height / 2;
|
|
1070
|
-
const slope =
|
|
1071
|
-
return
|
|
2115
|
+
const slope = getEllipseTangentSlope(point[0], point[1], a, b);
|
|
2116
|
+
return getVectorFromPointAndSlope(point[0], point[1], slope);
|
|
1072
2117
|
},
|
|
1073
2118
|
getConnectorPoints(rectangle) {
|
|
1074
2119
|
return RectangleClient.getEdgeCenterPoints(rectangle);
|
|
@@ -1087,7 +2132,7 @@ const StoredDataEngine = {
|
|
|
1087
2132
|
},
|
|
1088
2133
|
isHit(rectangle, point) {
|
|
1089
2134
|
//split shape to rectangle and a half ellipse
|
|
1090
|
-
const rangeRectangle = RectangleClient.
|
|
2135
|
+
const rangeRectangle = RectangleClient.getRectangleByPoints([point, point]);
|
|
1091
2136
|
const isInRectangle = RectangleClient.isHit({
|
|
1092
2137
|
...rectangle,
|
|
1093
2138
|
x: rectangle.x + rectangle.width / 10,
|
|
@@ -1103,14 +2148,20 @@ const StoredDataEngine = {
|
|
|
1103
2148
|
getNearestPoint(rectangle, point) {
|
|
1104
2149
|
const nearestPoint = getNearestPointBetweenPointAndSegments(point, RectangleEngine.getCornerPoints(rectangle));
|
|
1105
2150
|
if (nearestPoint[0] < rectangle.x + rectangle.width / 10) {
|
|
1106
|
-
const
|
|
1107
|
-
|
|
1108
|
-
|
|
2151
|
+
const centerPoint = [rectangle.x + rectangle.width / 10, rectangle.y + rectangle.height / 2];
|
|
2152
|
+
const nearestPoint = getNearestPointBetweenPointAndEllipse(point, centerPoint, rectangle.width / 10, rectangle.height / 2);
|
|
2153
|
+
if (nearestPoint[0] > centerPoint[0]) {
|
|
2154
|
+
nearestPoint[0] = centerPoint[0] * 2 - nearestPoint[0];
|
|
1109
2155
|
}
|
|
1110
2156
|
return nearestPoint;
|
|
1111
2157
|
}
|
|
1112
2158
|
if (nearestPoint[0] > rectangle.x + (rectangle.width * 9) / 10) {
|
|
1113
|
-
|
|
2159
|
+
const centerPoint = [rectangle.x + rectangle.width, rectangle.y + rectangle.height / 2];
|
|
2160
|
+
const nearestPoint = getNearestPointBetweenPointAndEllipse(point, centerPoint, rectangle.width / 10, rectangle.height / 2);
|
|
2161
|
+
if (nearestPoint[0] > centerPoint[0]) {
|
|
2162
|
+
nearestPoint[0] = centerPoint[0] * 2 - nearestPoint[0];
|
|
2163
|
+
}
|
|
2164
|
+
return nearestPoint;
|
|
1114
2165
|
}
|
|
1115
2166
|
return nearestPoint;
|
|
1116
2167
|
},
|
|
@@ -1124,8 +2175,8 @@ const StoredDataEngine = {
|
|
|
1124
2175
|
centerPoint = [rectangle.x + rectangle.width, rectangle.y + rectangle.height / 2];
|
|
1125
2176
|
}
|
|
1126
2177
|
const point = [connectionPoint[0] - centerPoint[0], -(connectionPoint[1] - centerPoint[1])];
|
|
1127
|
-
const slope =
|
|
1128
|
-
const vector =
|
|
2178
|
+
const slope = getEllipseTangentSlope(point[0], point[1], a, b);
|
|
2179
|
+
const vector = getVectorFromPointAndSlope(point[0], point[1], slope);
|
|
1129
2180
|
return isBackEllipse ? vector.map(num => -num) : vector;
|
|
1130
2181
|
},
|
|
1131
2182
|
getConnectorPoints(rectangle) {
|
|
@@ -1146,130 +2197,40 @@ const StoredDataEngine = {
|
|
|
1146
2197
|
};
|
|
1147
2198
|
|
|
1148
2199
|
const ShapeEngineMap = {
|
|
1149
|
-
[BasicShapes.rectangle]: RectangleEngine,
|
|
1150
|
-
[BasicShapes.diamond]: DiamondEngine,
|
|
1151
|
-
[BasicShapes.ellipse]: EllipseEngine,
|
|
1152
|
-
[BasicShapes.parallelogram]: ParallelogramEngine,
|
|
1153
|
-
[BasicShapes.roundRectangle]: RoundRectangleEngine,
|
|
1154
|
-
[BasicShapes.text]: RectangleEngine,
|
|
1155
|
-
[BasicShapes.triangle]: TriangleEngine,
|
|
1156
|
-
[BasicShapes.leftArrow]: LeftArrowEngine,
|
|
1157
|
-
[BasicShapes.trapezoid]: TrapezoidEngine,
|
|
1158
|
-
[BasicShapes.rightArrow]: RightArrowEngine,
|
|
1159
|
-
[BasicShapes.cross]: CrossEngine,
|
|
1160
|
-
[BasicShapes.star]: StarEngine,
|
|
1161
|
-
[BasicShapes.pentagon]: PentagonEngine,
|
|
1162
|
-
[BasicShapes.hexagon]: HexagonEngine,
|
|
1163
|
-
[BasicShapes.octagon]: OctagonEngine,
|
|
1164
|
-
[BasicShapes.pentagonArrow]: PentagonArrowEngine,
|
|
1165
|
-
[BasicShapes.processArrow]: ProcessArrowEngine,
|
|
1166
|
-
[BasicShapes.twoWayArrow]: TwoWayArrowEngine,
|
|
1167
|
-
[BasicShapes.comment]: CommentEngine,
|
|
1168
|
-
[BasicShapes.roundComment]: RoundCommentEngine,
|
|
1169
|
-
[FlowchartSymbols.process]: RectangleEngine,
|
|
1170
|
-
[FlowchartSymbols.decision]: DiamondEngine,
|
|
1171
|
-
[FlowchartSymbols.connector]: EllipseEngine,
|
|
1172
|
-
[FlowchartSymbols.data]: ParallelogramEngine,
|
|
1173
|
-
[FlowchartSymbols.terminal]: TerminalEngine,
|
|
1174
|
-
[FlowchartSymbols.manualInput]: ManualInputEngine,
|
|
1175
|
-
[FlowchartSymbols.preparation]: PreparationEngine,
|
|
1176
|
-
[FlowchartSymbols.manualLoop]: ManualLoopEngine,
|
|
1177
|
-
[FlowchartSymbols.merge]: MergeEngine,
|
|
1178
|
-
[FlowchartSymbols.delay]: DelayEngine,
|
|
1179
|
-
[FlowchartSymbols.storedData]: StoredDataEngine
|
|
1180
|
-
};
|
|
1181
|
-
const getEngine = (shape) => {
|
|
1182
|
-
return ShapeEngineMap[shape];
|
|
1183
|
-
};
|
|
1184
|
-
|
|
1185
|
-
const getShape = (value) => {
|
|
1186
|
-
if (PlaitDrawElement.isImage(value)) {
|
|
1187
|
-
return BasicShapes.rectangle;
|
|
1188
|
-
}
|
|
1189
|
-
return value.shape;
|
|
1190
|
-
};
|
|
1191
|
-
|
|
1192
|
-
const DefaultLineStyle = {
|
|
1193
|
-
strokeWidth: 2,
|
|
1194
|
-
strokeColor: '#000'
|
|
1195
|
-
};
|
|
1196
|
-
const LINE_TEXT_SPACE = 4;
|
|
1197
|
-
const LINE_AUTO_COMPLETE_DIAMETER = 6;
|
|
1198
|
-
const LINE_AUTO_COMPLETE_OPACITY = 0.6;
|
|
1199
|
-
const LINE_AUTO_COMPLETE_HOVERED_OPACITY = 0.8;
|
|
1200
|
-
const LINE_AUTO_COMPLETE_HOVERED_DIAMETER = 10;
|
|
1201
|
-
|
|
1202
|
-
const SHAPE_MAX_LENGTH = 6;
|
|
1203
|
-
const memorizedShape = new WeakMap();
|
|
1204
|
-
const getMemorizeKey = (element) => {
|
|
1205
|
-
let key = '';
|
|
1206
|
-
switch (true) {
|
|
1207
|
-
case PlaitDrawElement.isText(element): {
|
|
1208
|
-
key = MemorizeKey.text;
|
|
1209
|
-
break;
|
|
1210
|
-
}
|
|
1211
|
-
case PlaitDrawElement.isBasicShape(element): {
|
|
1212
|
-
key = MemorizeKey.basicShape;
|
|
1213
|
-
break;
|
|
1214
|
-
}
|
|
1215
|
-
case PlaitDrawElement.isFlowchart(element): {
|
|
1216
|
-
key = MemorizeKey.flowchart;
|
|
1217
|
-
break;
|
|
1218
|
-
}
|
|
1219
|
-
case PlaitDrawElement.isLine(element): {
|
|
1220
|
-
key = MemorizeKey.line;
|
|
1221
|
-
break;
|
|
1222
|
-
}
|
|
1223
|
-
}
|
|
1224
|
-
return key;
|
|
1225
|
-
};
|
|
1226
|
-
const getLineMemorizedLatest = () => {
|
|
1227
|
-
const properties = getMemorizedLatest(MemorizeKey.line);
|
|
1228
|
-
delete properties?.text;
|
|
1229
|
-
return { ...properties } || {};
|
|
1230
|
-
};
|
|
1231
|
-
const getMemorizedLatestByPointer = (pointer) => {
|
|
1232
|
-
let memorizeKey = '';
|
|
1233
|
-
if (PlaitDrawElement.isBasicShape({ shape: pointer })) {
|
|
1234
|
-
memorizeKey = pointer === BasicShapes.text ? MemorizeKey.text : MemorizeKey.basicShape;
|
|
1235
|
-
}
|
|
1236
|
-
else {
|
|
1237
|
-
memorizeKey = MemorizeKey.flowchart;
|
|
1238
|
-
}
|
|
1239
|
-
const properties = { ...getMemorizedLatest(memorizeKey) } || {};
|
|
1240
|
-
const textProperties = { ...properties.text } || {};
|
|
1241
|
-
delete properties.text;
|
|
1242
|
-
return { textProperties, geometryProperties: properties };
|
|
1243
|
-
};
|
|
1244
|
-
const memorizeLatestText = (element, operations) => {
|
|
1245
|
-
const memorizeKey = getMemorizeKey(element);
|
|
1246
|
-
let textMemory = getMemorizedLatest(memorizeKey)?.text || {};
|
|
1247
|
-
const setNodeOperation = operations.find(operation => operation.type === 'set_node');
|
|
1248
|
-
if (setNodeOperation) {
|
|
1249
|
-
const newProperties = setNodeOperation.newProperties;
|
|
1250
|
-
textMemory = { ...textMemory, ...newProperties };
|
|
1251
|
-
memorizeLatest(memorizeKey, 'text', textMemory);
|
|
1252
|
-
}
|
|
1253
|
-
};
|
|
1254
|
-
const memorizeLatestShape = (board, shape) => {
|
|
1255
|
-
const shapes = memorizedShape.has(board) ? memorizedShape.get(board) : [];
|
|
1256
|
-
const shapeIndex = shapes.indexOf(shape);
|
|
1257
|
-
if (shape === BasicShapes.text || shapeIndex === 0) {
|
|
1258
|
-
return;
|
|
1259
|
-
}
|
|
1260
|
-
if (shapeIndex !== -1) {
|
|
1261
|
-
shapes.splice(shapeIndex, 1);
|
|
1262
|
-
}
|
|
1263
|
-
else {
|
|
1264
|
-
if (shapes.length === SHAPE_MAX_LENGTH) {
|
|
1265
|
-
shapes.pop();
|
|
1266
|
-
}
|
|
1267
|
-
}
|
|
1268
|
-
shapes.unshift(shape);
|
|
1269
|
-
memorizedShape.set(board, shapes);
|
|
2200
|
+
[BasicShapes.rectangle]: RectangleEngine,
|
|
2201
|
+
[BasicShapes.diamond]: DiamondEngine,
|
|
2202
|
+
[BasicShapes.ellipse]: EllipseEngine,
|
|
2203
|
+
[BasicShapes.parallelogram]: ParallelogramEngine,
|
|
2204
|
+
[BasicShapes.roundRectangle]: RoundRectangleEngine,
|
|
2205
|
+
[BasicShapes.text]: RectangleEngine,
|
|
2206
|
+
[BasicShapes.triangle]: TriangleEngine,
|
|
2207
|
+
[BasicShapes.leftArrow]: LeftArrowEngine,
|
|
2208
|
+
[BasicShapes.trapezoid]: TrapezoidEngine,
|
|
2209
|
+
[BasicShapes.rightArrow]: RightArrowEngine,
|
|
2210
|
+
[BasicShapes.cross]: CrossEngine,
|
|
2211
|
+
[BasicShapes.star]: StarEngine,
|
|
2212
|
+
[BasicShapes.pentagon]: PentagonEngine,
|
|
2213
|
+
[BasicShapes.hexagon]: HexagonEngine,
|
|
2214
|
+
[BasicShapes.octagon]: OctagonEngine,
|
|
2215
|
+
[BasicShapes.pentagonArrow]: PentagonArrowEngine,
|
|
2216
|
+
[BasicShapes.processArrow]: ProcessArrowEngine,
|
|
2217
|
+
[BasicShapes.twoWayArrow]: TwoWayArrowEngine,
|
|
2218
|
+
[BasicShapes.comment]: CommentEngine,
|
|
2219
|
+
[BasicShapes.roundComment]: RoundCommentEngine,
|
|
2220
|
+
[FlowchartSymbols.process]: RectangleEngine,
|
|
2221
|
+
[FlowchartSymbols.decision]: DiamondEngine,
|
|
2222
|
+
[FlowchartSymbols.connector]: EllipseEngine,
|
|
2223
|
+
[FlowchartSymbols.data]: ParallelogramEngine,
|
|
2224
|
+
[FlowchartSymbols.terminal]: TerminalEngine,
|
|
2225
|
+
[FlowchartSymbols.manualInput]: ManualInputEngine,
|
|
2226
|
+
[FlowchartSymbols.preparation]: PreparationEngine,
|
|
2227
|
+
[FlowchartSymbols.manualLoop]: ManualLoopEngine,
|
|
2228
|
+
[FlowchartSymbols.merge]: MergeEngine,
|
|
2229
|
+
[FlowchartSymbols.delay]: DelayEngine,
|
|
2230
|
+
[FlowchartSymbols.storedData]: StoredDataEngine
|
|
1270
2231
|
};
|
|
1271
|
-
const
|
|
1272
|
-
return
|
|
2232
|
+
const getEngine = (shape) => {
|
|
2233
|
+
return ShapeEngineMap[shape];
|
|
1273
2234
|
};
|
|
1274
2235
|
|
|
1275
2236
|
const createGeometryElement = (shape, points, text, options = {}, textProperties = {}) => {
|
|
@@ -1298,13 +2259,8 @@ const createGeometryElement = (shape, points, text, options = {}, textProperties
|
|
|
1298
2259
|
...options
|
|
1299
2260
|
};
|
|
1300
2261
|
};
|
|
1301
|
-
const getPointsByCenterPoint = (point, width, height) => {
|
|
1302
|
-
const leftTopPoint = [point[0] - width / 2, point[1] - height / 2];
|
|
1303
|
-
const rightBottomPoint = [point[0] + width / 2, point[1] + height / 2];
|
|
1304
|
-
return [leftTopPoint, rightBottomPoint];
|
|
1305
|
-
};
|
|
1306
2262
|
const getTextRectangle = (element) => {
|
|
1307
|
-
const elementRectangle = getRectangleByPoints(element.points);
|
|
2263
|
+
const elementRectangle = RectangleClient.getRectangleByPoints(element.points);
|
|
1308
2264
|
const strokeWidth = getStrokeWidthByElement(element);
|
|
1309
2265
|
const height = element.textHeight;
|
|
1310
2266
|
const width = elementRectangle.width - ShapeDefaultSpace.rectangleAndText * 2 - strokeWidth * 2;
|
|
@@ -1317,7 +2273,7 @@ const getTextRectangle = (element) => {
|
|
|
1317
2273
|
};
|
|
1318
2274
|
const drawBoundMask = (board, element) => {
|
|
1319
2275
|
const G = createG();
|
|
1320
|
-
const rectangle = getRectangleByPoints(element.points);
|
|
2276
|
+
const rectangle = RectangleClient.getRectangleByPoints(element.points);
|
|
1321
2277
|
const activeRectangle = RectangleClient.inflate(rectangle, ACTIVE_STROKE_WIDTH);
|
|
1322
2278
|
const shape = getShape(element);
|
|
1323
2279
|
const maskG = drawGeometry(board, activeRectangle, shape, {
|
|
@@ -1342,11 +2298,10 @@ const drawBoundMask = (board, element) => {
|
|
|
1342
2298
|
const drawGeometry = (board, outerRectangle, shape, options) => {
|
|
1343
2299
|
return getEngine(shape).draw(board, outerRectangle, options);
|
|
1344
2300
|
};
|
|
1345
|
-
const getNearestPoint = (element, point
|
|
1346
|
-
const rectangle = getRectangleByPoints(element.points);
|
|
1347
|
-
const activeRectangle = RectangleClient.inflate(rectangle, inflateDelta);
|
|
2301
|
+
const getNearestPoint = (element, point) => {
|
|
2302
|
+
const rectangle = RectangleClient.getRectangleByPoints(element.points);
|
|
1348
2303
|
const shape = getShape(element);
|
|
1349
|
-
return getEngine(shape).getNearestPoint(
|
|
2304
|
+
return getEngine(shape).getNearestPoint(rectangle, point);
|
|
1350
2305
|
};
|
|
1351
2306
|
const getCenterPointsOnPolygon = (points) => {
|
|
1352
2307
|
const centerPoint = [];
|
|
@@ -1356,17 +2311,6 @@ const getCenterPointsOnPolygon = (points) => {
|
|
|
1356
2311
|
}
|
|
1357
2312
|
return centerPoint;
|
|
1358
2313
|
};
|
|
1359
|
-
const getEdgeOnPolygonByPoint = (corners, point) => {
|
|
1360
|
-
for (let index = 1; index <= corners.length; index++) {
|
|
1361
|
-
let start = corners[index - 1];
|
|
1362
|
-
let end = index === corners.length ? corners[0] : corners[index];
|
|
1363
|
-
const distance = distanceBetweenPointAndSegment(point[0], point[1], start[0], start[1], end[0], end[1]);
|
|
1364
|
-
if (distance < 1) {
|
|
1365
|
-
return [start, end];
|
|
1366
|
-
}
|
|
1367
|
-
}
|
|
1368
|
-
return null;
|
|
1369
|
-
};
|
|
1370
2314
|
const getDefaultFlowchartProperty = (symbol) => {
|
|
1371
2315
|
return DefaultFlowchartPropertyMap[symbol];
|
|
1372
2316
|
};
|
|
@@ -1382,525 +2326,194 @@ const createDefaultFlowchart = (point) => {
|
|
|
1382
2326
|
};
|
|
1383
2327
|
const startElement = createGeometryElement(FlowchartSymbols.terminal, getDefaultGeometryPoints(FlowchartSymbols.terminal, point), '开始', options);
|
|
1384
2328
|
const processPoint1 = [point[0], point[1] + terminalProperty.height / 2 + 55 + processProperty.height / 2];
|
|
1385
|
-
const processElement1 = createGeometryElement(FlowchartSymbols.process, getDefaultGeometryPoints(FlowchartSymbols.process, processPoint1), '过程', options);
|
|
1386
|
-
const decisionPoint = [processPoint1[0], processPoint1[1] + processProperty.height / 2 + 55 + decisionProperty.height / 2];
|
|
1387
|
-
const decisionElement = createGeometryElement(FlowchartSymbols.decision, getDefaultGeometryPoints(FlowchartSymbols.decision, decisionPoint), '过程', options);
|
|
1388
|
-
const processPoint2 = [decisionPoint[0] + decisionProperty.width / 2 + 75 + processProperty.width / 2, decisionPoint[1]];
|
|
1389
|
-
const processElement2 = createGeometryElement(FlowchartSymbols.process, getDefaultGeometryPoints(FlowchartSymbols.process, processPoint2), '过程', options);
|
|
1390
|
-
const endPoint = [decisionPoint[0], decisionPoint[1] + decisionProperty.height / 2 + 95 + terminalProperty.height / 2];
|
|
1391
|
-
const endElement = createGeometryElement(FlowchartSymbols.terminal, getDefaultGeometryPoints(FlowchartSymbols.terminal, endPoint), '结束', options);
|
|
1392
|
-
const line1 = createLineElement(LineShape.elbow, [
|
|
1393
|
-
[0, 0],
|
|
1394
|
-
[0, 0]
|
|
1395
|
-
], { marker: LineMarkerType.none, connection: [0.5, 1], boundId: startElement.id }, { marker: LineMarkerType.arrow, connection: [0.5, 0], boundId: processElement1.id }, [], lineOptions);
|
|
1396
|
-
const line2 = createLineElement(LineShape.elbow, [
|
|
1397
|
-
[0, 0],
|
|
1398
|
-
[0, 0]
|
|
1399
|
-
], { marker: LineMarkerType.none, connection: [0.5, 1], boundId: processElement1.id }, { marker: LineMarkerType.arrow, connection: [0.5, 0], boundId: decisionElement.id }, [], lineOptions);
|
|
1400
|
-
const line3 = createLineElement(LineShape.elbow, [
|
|
1401
|
-
[0, 0],
|
|
1402
|
-
[0, 0]
|
|
1403
|
-
], { marker: LineMarkerType.none, connection: [0.5, 1], boundId: decisionElement.id }, { marker: LineMarkerType.arrow, connection: [0.5, 0], boundId: endElement.id }, [
|
|
1404
|
-
{
|
|
1405
|
-
text: buildText('是'),
|
|
1406
|
-
position: 0.5,
|
|
1407
|
-
width: 14,
|
|
1408
|
-
height: 20
|
|
1409
|
-
}
|
|
1410
|
-
], lineOptions);
|
|
1411
|
-
const line4 = createLineElement(LineShape.elbow, [
|
|
1412
|
-
[0, 0],
|
|
1413
|
-
[0, 0]
|
|
1414
|
-
], { marker: LineMarkerType.none, connection: [1, 0.5], boundId: decisionElement.id }, { marker: LineMarkerType.arrow, connection: [0, 0.5], boundId: processElement2.id }, [
|
|
1415
|
-
{
|
|
1416
|
-
text: buildText('否'),
|
|
1417
|
-
position: 0.5,
|
|
1418
|
-
width: 14,
|
|
1419
|
-
height: 20
|
|
1420
|
-
}
|
|
1421
|
-
], lineOptions);
|
|
1422
|
-
const line5 = createLineElement(LineShape.elbow, [
|
|
1423
|
-
[0, 0],
|
|
1424
|
-
[0, 0]
|
|
1425
|
-
], { marker: LineMarkerType.none, connection: [0.5, 1], boundId: processElement2.id }, { marker: LineMarkerType.arrow, connection: [1, 0.5], boundId: endElement.id }, [], lineOptions);
|
|
1426
|
-
return [startElement, processElement1, decisionElement, processElement2, endElement, line1, line2, line3, line4, line5];
|
|
1427
|
-
};
|
|
1428
|
-
const getAutoCompletePoints = (element) => {
|
|
1429
|
-
const AutoCompleteMargin = (12 + RESIZE_HANDLE_DIAMETER / 2) * 2;
|
|
1430
|
-
let rectangle = getRectangleByPoints(element.points);
|
|
1431
|
-
rectangle = RectangleClient.inflate(rectangle, AutoCompleteMargin);
|
|
1432
|
-
return RectangleClient.getEdgeCenterPoints(rectangle);
|
|
1433
|
-
};
|
|
1434
|
-
const getHitIndexOfAutoCompletePoint = (movingPoint, points) => {
|
|
1435
|
-
return points.findIndex(point => {
|
|
1436
|
-
const movingRectangle = RectangleClient.toRectangleClient([movingPoint]);
|
|
1437
|
-
let rectangle = RectangleClient.toRectangleClient([point]);
|
|
1438
|
-
rectangle = RectangleClient.inflate(rectangle, RESIZE_HANDLE_DIAMETER);
|
|
1439
|
-
return RectangleClient.isHit(movingRectangle, rectangle);
|
|
1440
|
-
});
|
|
1441
|
-
};
|
|
1442
|
-
const getDrawDefaultStrokeColor = (theme) => {
|
|
1443
|
-
return DrawThemeColors[theme].strokeColor;
|
|
1444
|
-
};
|
|
1445
|
-
const getFlowchartDefaultFill = (theme) => {
|
|
1446
|
-
return DrawThemeColors[theme].fill;
|
|
1447
|
-
};
|
|
1448
|
-
const getTextShapeProperty = (board, text = DefaultTextProperty.text, fontSize) => {
|
|
1449
|
-
fontSize = fontSize ? Number(fontSize) : DEFAULT_FONT_SIZE;
|
|
1450
|
-
const textSize = getTextSize(board, text, Infinity, { fontSize });
|
|
1451
|
-
return {
|
|
1452
|
-
width: textSize.width + ShapeDefaultSpace.rectangleAndText * 2,
|
|
1453
|
-
height: textSize.height
|
|
1454
|
-
};
|
|
1455
|
-
};
|
|
1456
|
-
const getDefaultGeometryPoints = (pointer, centerPoint) => {
|
|
1457
|
-
const defaultProperty = getDefaultGeometryProperty(pointer);
|
|
1458
|
-
return getPointsByCenterPoint(centerPoint, defaultProperty.width, defaultProperty.height);
|
|
1459
|
-
};
|
|
1460
|
-
const getDefaultGeometryProperty = (pointer) => {
|
|
1461
|
-
const isFlowChart = getFlowchartPointers().includes(pointer);
|
|
1462
|
-
if (isFlowChart) {
|
|
1463
|
-
return getDefaultFlowchartProperty(pointer);
|
|
1464
|
-
}
|
|
1465
|
-
else {
|
|
1466
|
-
return DefaultBasicShapeProperty;
|
|
1467
|
-
}
|
|
1468
|
-
};
|
|
1469
|
-
const getDefaultTextPoints = (board, centerPoint, fontSize) => {
|
|
1470
|
-
const property = getTextShapeProperty(board, DefaultTextProperty.text, fontSize);
|
|
1471
|
-
return getPointsByCenterPoint(centerPoint, property.width, property.height);
|
|
1472
|
-
};
|
|
1473
|
-
const insertElement = (board, element) => {
|
|
1474
|
-
memorizeLatestShape(board, element.shape);
|
|
1475
|
-
Transforms.insertNode(board, element, [board.children.length]);
|
|
1476
|
-
clearSelectedElement(board);
|
|
1477
|
-
addSelectedElement(board, element);
|
|
1478
|
-
BoardTransforms.updatePointerType(board, PlaitPointerType.selection);
|
|
1479
|
-
};
|
|
1480
|
-
const createTextElement = (board, points, text = DefaultTextProperty.text, textHeight) => {
|
|
1481
|
-
const memorizedLatest = getMemorizedLatestByPointer(BasicShapes.text);
|
|
1482
|
-
textHeight = textHeight ? textHeight : getRectangleByPoints(points).height;
|
|
1483
|
-
return createGeometryElement(BasicShapes.text, points, text, memorizedLatest.geometryProperties, {
|
|
1484
|
-
...memorizedLatest.textProperties,
|
|
1485
|
-
textHeight
|
|
1486
|
-
});
|
|
1487
|
-
};
|
|
1488
|
-
const createDefaultGeometry = (board, points, shape) => {
|
|
1489
|
-
const memorizedLatest = getMemorizedLatestByPointer(shape);
|
|
1490
|
-
const textHeight = getTextShapeProperty(board, DefaultTextProperty.text, memorizedLatest.textProperties['font-size']).height;
|
|
1491
|
-
return createGeometryElement(shape, points, '', {
|
|
1492
|
-
strokeWidth: DefaultBasicShapeProperty.strokeWidth,
|
|
1493
|
-
...memorizedLatest.geometryProperties
|
|
1494
|
-
}, { ...memorizedLatest.textProperties, textHeight });
|
|
1495
|
-
};
|
|
1496
|
-
|
|
1497
|
-
const ARROW_LENGTH = 20;
|
|
1498
|
-
const drawLineArrow = (element, points, options) => {
|
|
1499
|
-
const arrowG = createG();
|
|
1500
|
-
if (PlaitLine.isSourceMark(element, LineMarkerType.none) && PlaitLine.isTargetMark(element, LineMarkerType.none)) {
|
|
1501
|
-
return null;
|
|
1502
|
-
}
|
|
1503
|
-
const strokeWidth = getStrokeWidthByElement(element);
|
|
1504
|
-
const offset = (strokeWidth * strokeWidth) / 3;
|
|
1505
|
-
if (points.length === 1) {
|
|
1506
|
-
points = [points[0], [points[0][0] + 0.1, points[0][1]]];
|
|
1507
|
-
}
|
|
1508
|
-
if (!PlaitLine.isSourceMark(element, LineMarkerType.none)) {
|
|
1509
|
-
const source = getExtendPoint(points[0], points[1], ARROW_LENGTH + offset);
|
|
1510
|
-
const sourceArrow = getArrow(element, { marker: element.source.marker, source, target: points[0], isSource: true }, options);
|
|
1511
|
-
sourceArrow && arrowG.appendChild(sourceArrow);
|
|
1512
|
-
}
|
|
1513
|
-
if (!PlaitLine.isTargetMark(element, LineMarkerType.none)) {
|
|
1514
|
-
const source = getExtendPoint(points[points.length - 1], points[points.length - 2], ARROW_LENGTH + offset);
|
|
1515
|
-
const arrow = getArrow(element, { marker: element.target.marker, source, target: points[points.length - 1], isSource: false }, options);
|
|
1516
|
-
arrow && arrowG.appendChild(arrow);
|
|
1517
|
-
}
|
|
1518
|
-
return arrowG;
|
|
1519
|
-
};
|
|
1520
|
-
const getArrow = (element, arrowOptions, options) => {
|
|
1521
|
-
const { marker, target, source, isSource } = arrowOptions;
|
|
1522
|
-
let targetArrow;
|
|
1523
|
-
switch (marker) {
|
|
1524
|
-
case LineMarkerType.openTriangle: {
|
|
1525
|
-
targetArrow = drawOpenTriangle(element, source, target, options);
|
|
1526
|
-
break;
|
|
1527
|
-
}
|
|
1528
|
-
case LineMarkerType.solidTriangle: {
|
|
1529
|
-
targetArrow = drawSolidTriangle(source, target, options);
|
|
1530
|
-
break;
|
|
1531
|
-
}
|
|
1532
|
-
case LineMarkerType.arrow: {
|
|
1533
|
-
targetArrow = drawArrow(element, source, target, options);
|
|
1534
|
-
break;
|
|
1535
|
-
}
|
|
1536
|
-
case LineMarkerType.sharpArrow: {
|
|
1537
|
-
targetArrow = drawSharpArrow(source, target, options);
|
|
1538
|
-
break;
|
|
1539
|
-
}
|
|
1540
|
-
case LineMarkerType.oneSideUp: {
|
|
1541
|
-
targetArrow = drawOneSideArrow(source, target, isSource ? 'down' : 'up', options);
|
|
1542
|
-
break;
|
|
1543
|
-
}
|
|
1544
|
-
case LineMarkerType.oneSideDown: {
|
|
1545
|
-
targetArrow = drawOneSideArrow(source, target, isSource ? 'up' : 'down', options);
|
|
1546
|
-
break;
|
|
1547
|
-
}
|
|
1548
|
-
case LineMarkerType.hollowTriangle: {
|
|
1549
|
-
targetArrow = drawHollowTriangleArrow(source, target, options);
|
|
1550
|
-
break;
|
|
1551
|
-
}
|
|
1552
|
-
case LineMarkerType.singleSlash: {
|
|
1553
|
-
targetArrow = drawSingleSlash(source, target, isSource, options);
|
|
1554
|
-
break;
|
|
1555
|
-
}
|
|
1556
|
-
}
|
|
1557
|
-
return targetArrow;
|
|
1558
|
-
};
|
|
1559
|
-
const drawSharpArrow = (source, target, options) => {
|
|
1560
|
-
const startPoint = target;
|
|
1561
|
-
const { pointLeft, pointRight } = arrowPoints(source, target, 20);
|
|
1562
|
-
const g = createG();
|
|
1563
|
-
const path = createPath();
|
|
1564
|
-
let polylinePath = `M${pointRight[0]},${pointRight[1]}A25,25,20,0,1,${pointLeft[0]},${pointLeft[1]}L${startPoint[0]},${startPoint[1]}Z`;
|
|
1565
|
-
path.setAttribute('d', polylinePath);
|
|
1566
|
-
path.setAttribute('stroke', `${options?.stroke}`);
|
|
1567
|
-
path.setAttribute('stroke-width', `${options?.strokeWidth}`);
|
|
1568
|
-
path.setAttribute('fill', `${options?.stroke}`);
|
|
1569
|
-
g.appendChild(path);
|
|
1570
|
-
return g;
|
|
1571
|
-
};
|
|
1572
|
-
const drawArrow = (element, source, target, options) => {
|
|
1573
|
-
const directionFactor = getFactorByPoints(source, target);
|
|
1574
|
-
const strokeWidth = getStrokeWidthByElement(element);
|
|
1575
|
-
const endPoint = [target[0] + (strokeWidth * directionFactor.x) / 2, target[1] + (strokeWidth * directionFactor.y) / 2];
|
|
1576
|
-
const distance = distanceBetweenPointAndPoint(...source, ...endPoint);
|
|
1577
|
-
const middlePoint = [
|
|
1578
|
-
endPoint[0] - (((distance * 3) / 5 + strokeWidth) / 2) * directionFactor.x,
|
|
1579
|
-
endPoint[1] - (((distance * 3) / 5 + strokeWidth) / 2) * directionFactor.y
|
|
1580
|
-
];
|
|
1581
|
-
const { pointLeft, pointRight } = arrowPoints(source, endPoint, 30);
|
|
1582
|
-
const arrowG = drawLinearPath([pointLeft, endPoint, pointRight, middlePoint], { ...options, fill: options.stroke }, true);
|
|
1583
|
-
const path = arrowG.querySelector('path');
|
|
1584
|
-
path.setAttribute('stroke-linejoin', 'round');
|
|
1585
|
-
return arrowG;
|
|
1586
|
-
};
|
|
1587
|
-
const drawSolidTriangle = (source, target, options) => {
|
|
1588
|
-
const endPoint = target;
|
|
1589
|
-
const { pointLeft, pointRight } = arrowPoints(source, endPoint, 30);
|
|
1590
|
-
return drawLinearPath([pointLeft, endPoint, pointRight], { ...options, fill: options.stroke }, true);
|
|
1591
|
-
};
|
|
1592
|
-
const drawOpenTriangle = (element, source, target, options) => {
|
|
1593
|
-
const directionFactor = getFactorByPoints(source, target);
|
|
1594
|
-
const strokeWidth = getStrokeWidthByElement(element);
|
|
1595
|
-
const endPoint = [target[0] + (strokeWidth * directionFactor.x) / 2, target[1] + (strokeWidth * directionFactor.y) / 2];
|
|
1596
|
-
const { pointLeft, pointRight } = arrowPoints(source, endPoint, 40);
|
|
1597
|
-
return drawLinearPath([pointLeft, endPoint, pointRight], options);
|
|
1598
|
-
};
|
|
1599
|
-
const drawOneSideArrow = (source, target, side, options) => {
|
|
1600
|
-
const { pointLeft, pointRight } = arrowPoints(source, target, 40);
|
|
1601
|
-
return drawLinearPath([side === 'up' ? pointRight : pointLeft, target], options);
|
|
1602
|
-
};
|
|
1603
|
-
const drawSingleSlash = (source, target, isSource, options) => {
|
|
1604
|
-
const length = distanceBetweenPointAndPoint(...source, ...target);
|
|
1605
|
-
const middlePoint = getExtendPoint(target, source, length / 2);
|
|
1606
|
-
const angle = isSource ? 120 : 60;
|
|
1607
|
-
const start = rotate(...source, ...middlePoint, (angle * Math.PI) / 180);
|
|
1608
|
-
const end = rotate(...target, ...middlePoint, (angle * Math.PI) / 180);
|
|
1609
|
-
return drawLinearPath([start, end], options);
|
|
2329
|
+
const processElement1 = createGeometryElement(FlowchartSymbols.process, getDefaultGeometryPoints(FlowchartSymbols.process, processPoint1), '过程', options);
|
|
2330
|
+
const decisionPoint = [processPoint1[0], processPoint1[1] + processProperty.height / 2 + 55 + decisionProperty.height / 2];
|
|
2331
|
+
const decisionElement = createGeometryElement(FlowchartSymbols.decision, getDefaultGeometryPoints(FlowchartSymbols.decision, decisionPoint), '过程', options);
|
|
2332
|
+
const processPoint2 = [decisionPoint[0] + decisionProperty.width / 2 + 75 + processProperty.width / 2, decisionPoint[1]];
|
|
2333
|
+
const processElement2 = createGeometryElement(FlowchartSymbols.process, getDefaultGeometryPoints(FlowchartSymbols.process, processPoint2), '过程', options);
|
|
2334
|
+
const endPoint = [decisionPoint[0], decisionPoint[1] + decisionProperty.height / 2 + 95 + terminalProperty.height / 2];
|
|
2335
|
+
const endElement = createGeometryElement(FlowchartSymbols.terminal, getDefaultGeometryPoints(FlowchartSymbols.terminal, endPoint), '结束', options);
|
|
2336
|
+
const line1 = createLineElement(LineShape.elbow, [
|
|
2337
|
+
[0, 0],
|
|
2338
|
+
[0, 0]
|
|
2339
|
+
], { marker: LineMarkerType.none, connection: [0.5, 1], boundId: startElement.id }, { marker: LineMarkerType.arrow, connection: [0.5, 0], boundId: processElement1.id }, [], lineOptions);
|
|
2340
|
+
const line2 = createLineElement(LineShape.elbow, [
|
|
2341
|
+
[0, 0],
|
|
2342
|
+
[0, 0]
|
|
2343
|
+
], { marker: LineMarkerType.none, connection: [0.5, 1], boundId: processElement1.id }, { marker: LineMarkerType.arrow, connection: [0.5, 0], boundId: decisionElement.id }, [], lineOptions);
|
|
2344
|
+
const line3 = createLineElement(LineShape.elbow, [
|
|
2345
|
+
[0, 0],
|
|
2346
|
+
[0, 0]
|
|
2347
|
+
], { marker: LineMarkerType.none, connection: [0.5, 1], boundId: decisionElement.id }, { marker: LineMarkerType.arrow, connection: [0.5, 0], boundId: endElement.id }, [
|
|
2348
|
+
{
|
|
2349
|
+
text: buildText('是'),
|
|
2350
|
+
position: 0.5,
|
|
2351
|
+
width: 14,
|
|
2352
|
+
height: 20
|
|
2353
|
+
}
|
|
2354
|
+
], lineOptions);
|
|
2355
|
+
const line4 = createLineElement(LineShape.elbow, [
|
|
2356
|
+
[0, 0],
|
|
2357
|
+
[0, 0]
|
|
2358
|
+
], { marker: LineMarkerType.none, connection: [1, 0.5], boundId: decisionElement.id }, { marker: LineMarkerType.arrow, connection: [0, 0.5], boundId: processElement2.id }, [
|
|
2359
|
+
{
|
|
2360
|
+
text: buildText('否'),
|
|
2361
|
+
position: 0.5,
|
|
2362
|
+
width: 14,
|
|
2363
|
+
height: 20
|
|
2364
|
+
}
|
|
2365
|
+
], lineOptions);
|
|
2366
|
+
const line5 = createLineElement(LineShape.elbow, [
|
|
2367
|
+
[0, 0],
|
|
2368
|
+
[0, 0]
|
|
2369
|
+
], { marker: LineMarkerType.none, connection: [0.5, 1], boundId: processElement2.id }, { marker: LineMarkerType.arrow, connection: [1, 0.5], boundId: endElement.id }, [], lineOptions);
|
|
2370
|
+
return [startElement, processElement1, decisionElement, processElement2, endElement, line1, line2, line3, line4, line5];
|
|
1610
2371
|
};
|
|
1611
|
-
const
|
|
1612
|
-
const
|
|
1613
|
-
|
|
2372
|
+
const getAutoCompletePoints = (element) => {
|
|
2373
|
+
const AutoCompleteMargin = (12 + RESIZE_HANDLE_DIAMETER / 2) * 2;
|
|
2374
|
+
let rectangle = RectangleClient.getRectangleByPoints(element.points);
|
|
2375
|
+
rectangle = RectangleClient.inflate(rectangle, AutoCompleteMargin);
|
|
2376
|
+
return RectangleClient.getEdgeCenterPoints(rectangle);
|
|
1614
2377
|
};
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
let lineG;
|
|
1622
|
-
lineG = drawLine(this.board, element);
|
|
1623
|
-
return lineG;
|
|
1624
|
-
}
|
|
1625
|
-
}
|
|
1626
|
-
|
|
1627
|
-
const getHitGeometryResizeHandleRef = (board, element, point) => {
|
|
1628
|
-
const rectangle = getRectangleByPoints(element.points);
|
|
1629
|
-
const resizeHandleRefs = getRectangleResizeHandleRefs(rectangle, RESIZE_HANDLE_DIAMETER);
|
|
1630
|
-
const result = resizeHandleRefs.find(resizeHandleRef => {
|
|
1631
|
-
return RectangleClient.isHit(RectangleClient.toRectangleClient([point, point]), resizeHandleRef.rectangle);
|
|
2378
|
+
const getHitIndexOfAutoCompletePoint = (movingPoint, points) => {
|
|
2379
|
+
return points.findIndex(point => {
|
|
2380
|
+
const movingRectangle = RectangleClient.getRectangleByPoints([movingPoint]);
|
|
2381
|
+
let rectangle = RectangleClient.getRectangleByPoints([point]);
|
|
2382
|
+
rectangle = RectangleClient.inflate(rectangle, RESIZE_HANDLE_DIAMETER);
|
|
2383
|
+
return RectangleClient.isHit(movingRectangle, rectangle);
|
|
1632
2384
|
});
|
|
1633
|
-
return result;
|
|
1634
2385
|
};
|
|
1635
|
-
const
|
|
1636
|
-
|
|
1637
|
-
depthFirstRecursion(board, node => {
|
|
1638
|
-
if (PlaitDrawElement.isGeometry(node) || PlaitDrawElement.isImage(node)) {
|
|
1639
|
-
let client = getRectangleByPoints(node.points);
|
|
1640
|
-
client = RectangleClient.getOutlineRectangle(client, offset);
|
|
1641
|
-
const shape = getShape(node);
|
|
1642
|
-
const isHit = getEngine(shape).isHit(client, point);
|
|
1643
|
-
if (isHit) {
|
|
1644
|
-
geometry = node;
|
|
1645
|
-
}
|
|
1646
|
-
}
|
|
1647
|
-
}, getIsRecursionFunc(board), true);
|
|
1648
|
-
return geometry;
|
|
2386
|
+
const getDrawDefaultStrokeColor = (theme) => {
|
|
2387
|
+
return DrawThemeColors[theme].strokeColor;
|
|
1649
2388
|
};
|
|
1650
|
-
|
|
1651
|
-
|
|
2389
|
+
const getFlowchartDefaultFill = (theme) => {
|
|
2390
|
+
return DrawThemeColors[theme].fill;
|
|
2391
|
+
};
|
|
2392
|
+
const getTextShapeProperty = (board, text = DefaultTextProperty.text, fontSize) => {
|
|
2393
|
+
fontSize = fontSize ? Number(fontSize) : DEFAULT_FONT_SIZE;
|
|
2394
|
+
const textSize = getTextSize(board, text, Infinity, { fontSize });
|
|
1652
2395
|
return {
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
shape,
|
|
1656
|
-
source,
|
|
1657
|
-
texts: texts ? texts : [],
|
|
1658
|
-
target,
|
|
1659
|
-
opacity: 1,
|
|
1660
|
-
points,
|
|
1661
|
-
...options
|
|
2396
|
+
width: textSize.width + ShapeDefaultSpace.rectangleAndText * 2,
|
|
2397
|
+
height: textSize.height
|
|
1662
2398
|
};
|
|
1663
2399
|
};
|
|
1664
|
-
const
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
return getElbowPoints(board, element);
|
|
1668
|
-
}
|
|
1669
|
-
case LineShape.curve: {
|
|
1670
|
-
return getCurvePoints(board, element);
|
|
1671
|
-
}
|
|
1672
|
-
default: {
|
|
1673
|
-
const points = PlaitLine.getPoints(board, element);
|
|
1674
|
-
const handleRefPair = getLineHandleRefPair(board, element);
|
|
1675
|
-
points[0] = handleRefPair.source.point;
|
|
1676
|
-
points[points.length - 1] = handleRefPair.target.point;
|
|
1677
|
-
return points;
|
|
1678
|
-
}
|
|
1679
|
-
}
|
|
2400
|
+
const getDefaultGeometryPoints = (pointer, centerPoint) => {
|
|
2401
|
+
const property = getDefaultGeometryProperty(pointer);
|
|
2402
|
+
return RectangleClient.getPoints(RectangleClient.getRectangleByCenterPoint(centerPoint, property.width, property.height));
|
|
1680
2403
|
};
|
|
1681
|
-
const
|
|
1682
|
-
const
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
let sourcePoint = sourceBoundElement ? getConnectionPoint(sourceBoundElement, element.source.connection) : element.points[0];
|
|
1686
|
-
let targetPoint = targetBoundElement
|
|
1687
|
-
? getConnectionPoint(targetBoundElement, element.target.connection)
|
|
1688
|
-
: element.points[element.points.length - 1];
|
|
1689
|
-
let sourceDirection = getDirectionByVector([targetPoint[0] - sourcePoint[0], targetPoint[1] - sourcePoint[1]]) || Direction.right;
|
|
1690
|
-
let targetDirection = getOppositeDirection(sourceDirection);
|
|
1691
|
-
const sourceFactor = getDirectionFactor(sourceDirection);
|
|
1692
|
-
const targetFactor = getDirectionFactor(targetDirection);
|
|
1693
|
-
const sourceHandleRef = {
|
|
1694
|
-
key: LineHandleKey.source,
|
|
1695
|
-
point: sourcePoint,
|
|
1696
|
-
direction: sourceDirection,
|
|
1697
|
-
vector: [sourceFactor.x, sourceFactor.y]
|
|
1698
|
-
};
|
|
1699
|
-
const targetHandleRef = {
|
|
1700
|
-
key: LineHandleKey.target,
|
|
1701
|
-
point: targetPoint,
|
|
1702
|
-
direction: targetDirection,
|
|
1703
|
-
vector: [targetFactor.x, targetFactor.y]
|
|
1704
|
-
};
|
|
1705
|
-
if (sourceBoundElement) {
|
|
1706
|
-
const connectionOffset = PlaitLine.isSourceMarkOrTargetMark(element, LineMarkerType.none, LineHandleKey.source) ? 0 : strokeWidth;
|
|
1707
|
-
const sourceVector = getVectorByConnection(sourceBoundElement, element.source.connection);
|
|
1708
|
-
const direction = getDirectionByVector(sourceVector);
|
|
1709
|
-
sourceDirection = direction ? direction : sourceDirection;
|
|
1710
|
-
sourcePoint = getConnectionPoint(sourceBoundElement, element.source.connection, sourceDirection, connectionOffset);
|
|
1711
|
-
sourceHandleRef.boundElement = sourceBoundElement;
|
|
1712
|
-
sourceHandleRef.direction = sourceDirection;
|
|
1713
|
-
sourceHandleRef.point = sourcePoint;
|
|
1714
|
-
sourceHandleRef.vector = sourceVector;
|
|
2404
|
+
const getDefaultGeometryProperty = (pointer) => {
|
|
2405
|
+
const isFlowChart = getFlowchartPointers().includes(pointer);
|
|
2406
|
+
if (isFlowChart) {
|
|
2407
|
+
return getDefaultFlowchartProperty(pointer);
|
|
1715
2408
|
}
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
const targetVector = getVectorByConnection(targetBoundElement, element.target.connection);
|
|
1719
|
-
const direction = getDirectionByVector(targetVector);
|
|
1720
|
-
targetDirection = direction ? direction : targetDirection;
|
|
1721
|
-
targetPoint = getConnectionPoint(targetBoundElement, element.target.connection, targetDirection, connectionOffset);
|
|
1722
|
-
targetHandleRef.boundElement = targetBoundElement;
|
|
1723
|
-
targetHandleRef.direction = targetDirection;
|
|
1724
|
-
targetHandleRef.point = targetPoint;
|
|
1725
|
-
targetHandleRef.vector = targetVector;
|
|
2409
|
+
else {
|
|
2410
|
+
return DefaultBasicShapeProperty;
|
|
1726
2411
|
}
|
|
1727
|
-
return { source: sourceHandleRef, target: targetHandleRef };
|
|
1728
2412
|
};
|
|
1729
|
-
const
|
|
1730
|
-
const
|
|
1731
|
-
|
|
1732
|
-
return createGeometryElement(BasicShapes.rectangle, points, '');
|
|
2413
|
+
const getDefaultTextPoints = (board, centerPoint, fontSize) => {
|
|
2414
|
+
const property = getTextShapeProperty(board, DefaultTextProperty.text, fontSize);
|
|
2415
|
+
return RectangleClient.getPoints(RectangleClient.getRectangleByCenterPoint(centerPoint, property.width, property.height));
|
|
1733
2416
|
};
|
|
1734
|
-
const
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
if (!sourceElement) {
|
|
1741
|
-
const source = handleRefPair.source;
|
|
1742
|
-
sourceElement = createFakeElement(source.point, source.vector);
|
|
1743
|
-
}
|
|
1744
|
-
if (!targetElement) {
|
|
1745
|
-
const target = handleRefPair.target;
|
|
1746
|
-
targetElement = createFakeElement(target.point, target.vector);
|
|
1747
|
-
}
|
|
1748
|
-
const targetRectangle = RectangleClient.inflate(getRectangleByPoints(targetElement.points), getStrokeWidthByElement(targetElement) * 2);
|
|
1749
|
-
const sourceRectangle = RectangleClient.inflate(getRectangleByPoints(sourceElement.points), getStrokeWidthByElement(sourceElement) * 2);
|
|
1750
|
-
const sourcePoint = handleRefPair.source.point;
|
|
1751
|
-
const targetPoint = handleRefPair.target.point;
|
|
1752
|
-
const { sourceOffset, targetOffset } = reduceRouteMargin(sourceRectangle, targetRectangle);
|
|
1753
|
-
const sourceOuterRectangle = RectangleClient.expand(sourceRectangle, sourceOffset[3], sourceOffset[0], sourceOffset[1], sourceOffset[2]);
|
|
1754
|
-
const targetOuterRectangle = RectangleClient.expand(targetRectangle, targetOffset[3], targetOffset[0], targetOffset[1], targetOffset[2]);
|
|
1755
|
-
const nextSourcePoint = getNextPoint(sourcePoint, sourceOuterRectangle, handleRefPair.source.direction);
|
|
1756
|
-
const nextTargetPoint = getNextPoint(targetPoint, targetOuterRectangle, handleRefPair.target.direction);
|
|
1757
|
-
const isIntersect = RectangleClient.isPointInRectangle(targetRectangle, sourcePoint) ||
|
|
1758
|
-
RectangleClient.isPointInRectangle(targetOuterRectangle, nextSourcePoint) ||
|
|
1759
|
-
RectangleClient.isPointInRectangle(sourceOuterRectangle, nextTargetPoint) ||
|
|
1760
|
-
RectangleClient.isPointInRectangle(sourceRectangle, targetPoint);
|
|
1761
|
-
if (!isIntersect) {
|
|
1762
|
-
points = generateElbowLineRoute({
|
|
1763
|
-
sourcePoint,
|
|
1764
|
-
nextSourcePoint,
|
|
1765
|
-
sourceRectangle,
|
|
1766
|
-
sourceOuterRectangle,
|
|
1767
|
-
targetPoint,
|
|
1768
|
-
nextTargetPoint,
|
|
1769
|
-
targetRectangle,
|
|
1770
|
-
targetOuterRectangle
|
|
1771
|
-
});
|
|
1772
|
-
}
|
|
1773
|
-
else {
|
|
1774
|
-
points = getPoints(handleRefPair.source.point, handleRefPair.source.direction, handleRefPair.target.point, handleRefPair.target.direction, 0);
|
|
1775
|
-
}
|
|
1776
|
-
points = removeDuplicatePoints(points);
|
|
1777
|
-
return points;
|
|
1778
|
-
}
|
|
1779
|
-
return element.points;
|
|
2417
|
+
const insertElement = (board, element) => {
|
|
2418
|
+
memorizeLatestShape(board, element.shape);
|
|
2419
|
+
Transforms.insertNode(board, element, [board.children.length]);
|
|
2420
|
+
clearSelectedElement(board);
|
|
2421
|
+
addSelectedElement(board, element);
|
|
2422
|
+
BoardTransforms.updatePointerType(board, PlaitPointerType.selection);
|
|
1780
2423
|
};
|
|
1781
|
-
const
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
const sumDistance = distanceBetweenPointAndPoint(...source.point, ...target.point);
|
|
1789
|
-
const offset = 12 + sumDistance / 3;
|
|
1790
|
-
if (sourceBoundElement) {
|
|
1791
|
-
curvePoints.push(getPointByVector(source.point, source.vector, offset));
|
|
1792
|
-
}
|
|
1793
|
-
if (targetBoundElement) {
|
|
1794
|
-
curvePoints.push(getPointByVector(target.point, target.vector, offset));
|
|
1795
|
-
}
|
|
1796
|
-
const isSingleBound = (sourceBoundElement && !targetBoundElement) || (!sourceBoundElement && targetBoundElement);
|
|
1797
|
-
if (isSingleBound) {
|
|
1798
|
-
curvePoints.push(target.point);
|
|
1799
|
-
const points = Q2C(curvePoints);
|
|
1800
|
-
return pointsOnBezierCurves(points);
|
|
1801
|
-
}
|
|
1802
|
-
if (!sourceBoundElement && !targetBoundElement) {
|
|
1803
|
-
curvePoints.push(getPointByVector(source.point, source.vector, offset));
|
|
1804
|
-
curvePoints.push(getPointByVector(target.point, target.vector, offset));
|
|
1805
|
-
}
|
|
1806
|
-
curvePoints.push(target.point);
|
|
1807
|
-
return pointsOnBezierCurves(curvePoints);
|
|
1808
|
-
}
|
|
1809
|
-
else {
|
|
1810
|
-
let allPoints = PlaitLine.getPoints(board, element);
|
|
1811
|
-
allPoints = removeDuplicatePoints(allPoints);
|
|
1812
|
-
const points = catmullRomFitting(allPoints);
|
|
1813
|
-
return pointsOnBezierCurves(points);
|
|
1814
|
-
}
|
|
2424
|
+
const createTextElement = (board, points, text = DefaultTextProperty.text, textHeight) => {
|
|
2425
|
+
const memorizedLatest = getMemorizedLatestByPointer(BasicShapes.text);
|
|
2426
|
+
textHeight = textHeight ? textHeight : RectangleClient.getRectangleByPoints(points).height;
|
|
2427
|
+
return createGeometryElement(BasicShapes.text, points, text, memorizedLatest.geometryProperties, {
|
|
2428
|
+
...memorizedLatest.textProperties,
|
|
2429
|
+
textHeight
|
|
2430
|
+
});
|
|
1815
2431
|
};
|
|
1816
|
-
const
|
|
1817
|
-
const
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
2432
|
+
const createDefaultGeometry = (board, points, shape) => {
|
|
2433
|
+
const memorizedLatest = getMemorizedLatestByPointer(shape);
|
|
2434
|
+
const textHeight = getTextShapeProperty(board, DefaultTextProperty.text, memorizedLatest.textProperties['font-size']).height;
|
|
2435
|
+
return createGeometryElement(shape, points, '', {
|
|
2436
|
+
strokeWidth: DefaultBasicShapeProperty.strokeWidth,
|
|
2437
|
+
...memorizedLatest.geometryProperties
|
|
2438
|
+
}, { ...memorizedLatest.textProperties, textHeight });
|
|
2439
|
+
};
|
|
2440
|
+
|
|
2441
|
+
const getStrokeWidthByElement = (element) => {
|
|
2442
|
+
if (PlaitDrawElement.isText(element)) {
|
|
2443
|
+
return 0;
|
|
1827
2444
|
}
|
|
1828
|
-
|
|
2445
|
+
const strokeWidth = element.strokeWidth || DefaultGeometryStyle.strokeWidth;
|
|
2446
|
+
return strokeWidth;
|
|
1829
2447
|
};
|
|
1830
|
-
const
|
|
1831
|
-
const
|
|
1832
|
-
|
|
2448
|
+
const getStrokeColorByElement = (board, element) => {
|
|
2449
|
+
const defaultColor = getDrawDefaultStrokeColor(board.theme.themeColorMode);
|
|
2450
|
+
const strokeColor = element.strokeColor || defaultColor;
|
|
2451
|
+
return strokeColor;
|
|
2452
|
+
};
|
|
2453
|
+
const getFillByElement = (board, element) => {
|
|
2454
|
+
const defaultFill = PlaitDrawElement.isFlowchart(element)
|
|
2455
|
+
? getFlowchartDefaultFill(board.theme.themeColorMode)
|
|
2456
|
+
: DefaultGeometryStyle.fill;
|
|
2457
|
+
const fill = element.fill || defaultFill;
|
|
2458
|
+
return fill;
|
|
1833
2459
|
};
|
|
1834
|
-
const
|
|
1835
|
-
|
|
1836
|
-
if (!texts.length)
|
|
1837
|
-
return -1;
|
|
1838
|
-
const points = getLinePoints(board, element);
|
|
1839
|
-
return texts.findIndex(text => {
|
|
1840
|
-
const center = getPointOnPolyline(points, text.position);
|
|
1841
|
-
const rectangle = {
|
|
1842
|
-
x: center[0] - text.width / 2,
|
|
1843
|
-
y: center[1] - text.height / 2,
|
|
1844
|
-
width: text.width,
|
|
1845
|
-
height: text.height
|
|
1846
|
-
};
|
|
1847
|
-
return RectangleClient.isHit(rectangle, RectangleClient.toRectangleClient([point, point]));
|
|
1848
|
-
});
|
|
2460
|
+
const getLineDashByElement = (element) => {
|
|
2461
|
+
return element.strokeStyle === 'dashed' ? [8, 8 + getStrokeWidthByElement(element)] : undefined;
|
|
1849
2462
|
};
|
|
1850
|
-
const
|
|
1851
|
-
return
|
|
2463
|
+
const getStrokeStyleByElement = (element) => {
|
|
2464
|
+
return element.strokeStyle || StrokeStyle.solid;
|
|
1852
2465
|
};
|
|
1853
|
-
|
|
2466
|
+
|
|
2467
|
+
const getLineHandleRefPair = (board, element) => {
|
|
1854
2468
|
const strokeWidth = getStrokeWidthByElement(element);
|
|
1855
|
-
const
|
|
1856
|
-
const
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
2469
|
+
const sourceBoundElement = element.source.boundId ? getElementById(board, element.source.boundId) : undefined;
|
|
2470
|
+
const targetBoundElement = element.target.boundId ? getElementById(board, element.target.boundId) : undefined;
|
|
2471
|
+
let sourcePoint = sourceBoundElement ? getConnectionPoint(sourceBoundElement, element.source.connection) : element.points[0];
|
|
2472
|
+
let targetPoint = targetBoundElement
|
|
2473
|
+
? getConnectionPoint(targetBoundElement, element.target.connection)
|
|
2474
|
+
: element.points[element.points.length - 1];
|
|
2475
|
+
let sourceDirection = getDirectionByVector([targetPoint[0] - sourcePoint[0], targetPoint[1] - sourcePoint[1]]) || Direction.right;
|
|
2476
|
+
let targetDirection = getOppositeDirection(sourceDirection);
|
|
2477
|
+
const sourceFactor = getDirectionFactor(sourceDirection);
|
|
2478
|
+
const targetFactor = getDirectionFactor(targetDirection);
|
|
2479
|
+
const sourceHandleRef = {
|
|
2480
|
+
key: LineHandleKey.source,
|
|
2481
|
+
point: sourcePoint,
|
|
2482
|
+
direction: sourceDirection,
|
|
2483
|
+
vector: [sourceFactor.x, sourceFactor.y]
|
|
2484
|
+
};
|
|
2485
|
+
const targetHandleRef = {
|
|
2486
|
+
key: LineHandleKey.target,
|
|
2487
|
+
point: targetPoint,
|
|
2488
|
+
direction: targetDirection,
|
|
2489
|
+
vector: [targetFactor.x, targetFactor.y]
|
|
2490
|
+
};
|
|
2491
|
+
if (sourceBoundElement) {
|
|
2492
|
+
const connectionOffset = PlaitLine.isSourceMarkOrTargetMark(element, LineMarkerType.none, LineHandleKey.source) ? 0 : strokeWidth;
|
|
2493
|
+
const sourceVector = getVectorByConnection(sourceBoundElement, element.source.connection);
|
|
2494
|
+
const direction = getDirectionByVector(sourceVector);
|
|
2495
|
+
sourceDirection = direction ? direction : sourceDirection;
|
|
2496
|
+
sourcePoint = getConnectionPoint(sourceBoundElement, element.source.connection, sourceDirection, connectionOffset);
|
|
2497
|
+
sourceHandleRef.boundElement = sourceBoundElement;
|
|
2498
|
+
sourceHandleRef.direction = sourceDirection;
|
|
2499
|
+
sourceHandleRef.point = sourcePoint;
|
|
2500
|
+
sourceHandleRef.vector = sourceVector;
|
|
1863
2501
|
}
|
|
1864
|
-
|
|
1865
|
-
|
|
2502
|
+
if (targetBoundElement) {
|
|
2503
|
+
const connectionOffset = PlaitLine.isSourceMarkOrTargetMark(element, LineMarkerType.none, LineHandleKey.target) ? 0 : strokeWidth;
|
|
2504
|
+
const targetVector = getVectorByConnection(targetBoundElement, element.target.connection);
|
|
2505
|
+
const direction = getDirectionByVector(targetVector);
|
|
2506
|
+
targetDirection = direction ? direction : targetDirection;
|
|
2507
|
+
targetPoint = getConnectionPoint(targetBoundElement, element.target.connection, targetDirection, connectionOffset);
|
|
2508
|
+
targetHandleRef.boundElement = targetBoundElement;
|
|
2509
|
+
targetHandleRef.direction = targetDirection;
|
|
2510
|
+
targetHandleRef.point = targetPoint;
|
|
2511
|
+
targetHandleRef.vector = targetVector;
|
|
1866
2512
|
}
|
|
1867
|
-
|
|
1868
|
-
line.setAttribute('mask', `url(#${id})`);
|
|
1869
|
-
lineG.appendChild(line);
|
|
1870
|
-
const { mask, maskTargetFillRect } = drawMask(board, element, id);
|
|
1871
|
-
lineG.appendChild(mask);
|
|
1872
|
-
line.appendChild(maskTargetFillRect);
|
|
1873
|
-
const arrow = drawLineArrow(element, points, { stroke: strokeColor, strokeWidth });
|
|
1874
|
-
arrow && lineG.appendChild(arrow);
|
|
1875
|
-
return lineG;
|
|
2513
|
+
return { source: sourceHandleRef, target: targetHandleRef };
|
|
1876
2514
|
};
|
|
1877
|
-
function drawMask(board, element, id) {
|
|
1878
|
-
const mask = createMask();
|
|
1879
|
-
mask.setAttribute('id', id);
|
|
1880
|
-
const points = getLinePoints(board, element);
|
|
1881
|
-
let rectangle = getRectangleByPoints(points);
|
|
1882
|
-
rectangle = RectangleClient.getOutlineRectangle(rectangle, -30);
|
|
1883
|
-
const maskFillRect = createRect(rectangle, {
|
|
1884
|
-
fill: 'white'
|
|
1885
|
-
});
|
|
1886
|
-
mask.appendChild(maskFillRect);
|
|
1887
|
-
const texts = element.texts;
|
|
1888
|
-
texts.forEach((text, index) => {
|
|
1889
|
-
let textRectangle = getLineTextRectangle(board, element, index);
|
|
1890
|
-
textRectangle = RectangleClient.inflate(textRectangle, LINE_TEXT_SPACE * 2);
|
|
1891
|
-
const rect = createRect(textRectangle, {
|
|
1892
|
-
fill: 'black'
|
|
1893
|
-
});
|
|
1894
|
-
mask.appendChild(rect);
|
|
1895
|
-
});
|
|
1896
|
-
// open line
|
|
1897
|
-
const maskTargetFillRect = createRect(rectangle);
|
|
1898
|
-
maskTargetFillRect.setAttribute('opacity', '0');
|
|
1899
|
-
maskTargetFillRect.setAttribute('fill', 'none');
|
|
1900
|
-
return { mask, maskTargetFillRect };
|
|
1901
|
-
}
|
|
1902
2515
|
const getConnectionPoint = (geometry, connection, direction, delta) => {
|
|
1903
|
-
const rectangle = getRectangleByPoints(geometry.points);
|
|
2516
|
+
const rectangle = RectangleClient.getRectangleByPoints(geometry.points);
|
|
1904
2517
|
if (direction && delta) {
|
|
1905
2518
|
const directionFactor = getDirectionFactor(direction);
|
|
1906
2519
|
const point = RectangleClient.getConnectionPoint(rectangle, connection);
|
|
@@ -1910,69 +2523,13 @@ const getConnectionPoint = (geometry, connection, direction, delta) => {
|
|
|
1910
2523
|
return RectangleClient.getConnectionPoint(rectangle, connection);
|
|
1911
2524
|
}
|
|
1912
2525
|
};
|
|
1913
|
-
const transformPointToConnection = (board, point, hitElement) => {
|
|
1914
|
-
let rectangle = getRectangleByPoints(hitElement.points);
|
|
1915
|
-
rectangle = RectangleClient.inflate(rectangle, ACTIVE_STROKE_WIDTH);
|
|
1916
|
-
let nearestPoint = getNearestPoint(hitElement, point, ACTIVE_STROKE_WIDTH);
|
|
1917
|
-
const hitConnector = getHitConnectorPoint(nearestPoint, hitElement, rectangle);
|
|
1918
|
-
nearestPoint = hitConnector ? hitConnector : nearestPoint;
|
|
1919
|
-
return [(nearestPoint[0] - rectangle.x) / rectangle.width, (nearestPoint[1] - rectangle.y) / rectangle.height];
|
|
1920
|
-
};
|
|
1921
|
-
// export const getConnectionByPointOnRectangleEdge = (board: PlaitBoard, point: Point, hitElement: PlaitShape): Point => {
|
|
1922
|
-
// let rectangle = getRectangleByPoints(hitElement.points);
|
|
1923
|
-
// const hitConnector = getHitConnectorPoint(point, hitElement, rectangle);
|
|
1924
|
-
// const newPoint = hitConnector ?? point;
|
|
1925
|
-
// return [(newPoint[0] - rectangle.x) / rectangle.width, (newPoint[1] - rectangle.y) / rectangle.height];
|
|
1926
|
-
// };
|
|
1927
|
-
const getHitConnectorPoint = (movingPoint, hitElement, rectangle) => {
|
|
1928
|
-
const shape = getShape(hitElement);
|
|
1929
|
-
const connector = getEngine(shape).getConnectorPoints(rectangle);
|
|
1930
|
-
const points = getPointsByCenterPoint(movingPoint, 10, 10);
|
|
1931
|
-
const pointRectangle = getRectangleByPoints(points);
|
|
1932
|
-
return connector.find(point => {
|
|
1933
|
-
return RectangleClient.isHit(pointRectangle, RectangleClient.toRectangleClient([point, point]));
|
|
1934
|
-
});
|
|
1935
|
-
};
|
|
1936
|
-
const getLineTextRectangle = (board, element, index) => {
|
|
1937
|
-
const text = element.texts[index];
|
|
1938
|
-
const elbowPoints = getLinePoints(board, element);
|
|
1939
|
-
const point = getPointOnPolyline(elbowPoints, text.position);
|
|
1940
|
-
return {
|
|
1941
|
-
x: point[0] - text.width / 2,
|
|
1942
|
-
y: point[1] - text.height / 2,
|
|
1943
|
-
width: text.width,
|
|
1944
|
-
height: text.height
|
|
1945
|
-
};
|
|
1946
|
-
};
|
|
1947
|
-
const getBoardLines = (board) => {
|
|
1948
|
-
return findElements(board, {
|
|
1949
|
-
match: (element) => PlaitDrawElement.isLine(element),
|
|
1950
|
-
recursion: (element) => PlaitDrawElement.isDrawElement(element)
|
|
1951
|
-
});
|
|
1952
|
-
};
|
|
1953
|
-
// quadratic Bezier to cubic Bezier
|
|
1954
|
-
const Q2C = (points) => {
|
|
1955
|
-
const result = [];
|
|
1956
|
-
const numSegments = points.length / 3;
|
|
1957
|
-
for (let i = 0; i < numSegments; i++) {
|
|
1958
|
-
const start = points[i];
|
|
1959
|
-
const qControl = points[i + 1];
|
|
1960
|
-
const end = points[i + 2];
|
|
1961
|
-
const startDistance = distanceBetweenPointAndPoint(...start, ...qControl);
|
|
1962
|
-
const endDistance = distanceBetweenPointAndPoint(...end, ...qControl);
|
|
1963
|
-
const cControl1 = getExtendPoint(start, qControl, (startDistance * 2) / 3);
|
|
1964
|
-
const cControl2 = getExtendPoint(end, qControl, (endDistance * 2) / 3);
|
|
1965
|
-
result.push(start, cControl1, cControl2, end);
|
|
1966
|
-
}
|
|
1967
|
-
return result;
|
|
1968
|
-
};
|
|
1969
2526
|
const getVectorByConnection = (boundElement, connection) => {
|
|
1970
|
-
const rectangle = getRectangleByPoints(boundElement.points);
|
|
2527
|
+
const rectangle = RectangleClient.getRectangleByPoints(boundElement.points);
|
|
1971
2528
|
const shape = getShape(boundElement);
|
|
1972
2529
|
const engine = getEngine(shape);
|
|
1973
2530
|
let vector = [0, 0];
|
|
1974
2531
|
const direction = getDirectionByPointOfRectangle(connection);
|
|
1975
|
-
if (direction) {
|
|
2532
|
+
if (direction && boundElement.shape !== BasicShapes.ellipse) {
|
|
1976
2533
|
const factor = getDirectionFactor(direction);
|
|
1977
2534
|
return [factor.x, factor.y];
|
|
1978
2535
|
}
|
|
@@ -1991,120 +2548,25 @@ const getVectorByConnection = (boundElement, connection) => {
|
|
|
1991
2548
|
}
|
|
1992
2549
|
return vector;
|
|
1993
2550
|
};
|
|
1994
|
-
const
|
|
1995
|
-
|
|
1996
|
-
const
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
sourceMarker = memorizedLatest.source;
|
|
2014
|
-
targetMarker = memorizedLatest.target;
|
|
2015
|
-
sourceMarker && delete memorizedLatest.source;
|
|
2016
|
-
targetMarker && delete memorizedLatest.target;
|
|
2017
|
-
const temporaryLineElement = createLineElement(lineShape, [startPoint, movingPoint], { marker: sourceMarker || LineMarkerType.none, connection: connection, boundId: sourceElement?.id }, { marker: targetMarker || LineMarkerType.arrow, connection: targetConnection, boundId: targetBoundId }, [], {
|
|
2018
|
-
strokeWidth: DefaultLineStyle.strokeWidth,
|
|
2019
|
-
...memorizedLatest
|
|
2020
|
-
});
|
|
2021
|
-
const linePoints = getLinePoints(board, temporaryLineElement);
|
|
2022
|
-
const otherPoint = linePoints[0];
|
|
2023
|
-
temporaryLineElement.points[1] = alignPoints(otherPoint, movingPoint);
|
|
2024
|
-
lineGenerator.processDrawing(temporaryLineElement, lineShapeG);
|
|
2025
|
-
PlaitBoard.getElementActiveHost(board).append(lineShapeG);
|
|
2026
|
-
return temporaryLineElement;
|
|
2027
|
-
};
|
|
2028
|
-
|
|
2029
|
-
const getSelectedDrawElements = (board) => {
|
|
2030
|
-
const selectedElements = getSelectedElements(board).filter(value => PlaitDrawElement.isDrawElement(value));
|
|
2031
|
-
return selectedElements;
|
|
2032
|
-
};
|
|
2033
|
-
const getSelectedGeometryElements = (board) => {
|
|
2034
|
-
const selectedElements = getSelectedElements(board).filter(value => PlaitDrawElement.isGeometry(value));
|
|
2035
|
-
return selectedElements;
|
|
2036
|
-
};
|
|
2037
|
-
const getSelectedLineElements = (board) => {
|
|
2038
|
-
const selectedElements = getSelectedElements(board).filter(value => PlaitDrawElement.isLine(value));
|
|
2039
|
-
return selectedElements;
|
|
2040
|
-
};
|
|
2041
|
-
const getSelectedImageElements = (board) => {
|
|
2042
|
-
const selectedElements = getSelectedElements(board).filter(value => PlaitDrawElement.isImage(value));
|
|
2043
|
-
return selectedElements;
|
|
2044
|
-
};
|
|
2045
|
-
|
|
2046
|
-
const isTextExceedingBounds = (geometry) => {
|
|
2047
|
-
const client = getRectangleByPoints(geometry.points);
|
|
2048
|
-
if (geometry.textHeight > client.height) {
|
|
2049
|
-
return true;
|
|
2050
|
-
}
|
|
2051
|
-
return false;
|
|
2052
|
-
};
|
|
2053
|
-
const isRectangleHitDrawElement = (board, element, selection) => {
|
|
2054
|
-
const rangeRectangle = RectangleClient.toRectangleClient([selection.anchor, selection.focus]);
|
|
2055
|
-
if (PlaitDrawElement.isGeometry(element)) {
|
|
2056
|
-
const client = getRectangleByPoints(element.points);
|
|
2057
|
-
if (isTextExceedingBounds(element)) {
|
|
2058
|
-
const textClient = getTextRectangle(element);
|
|
2059
|
-
return RectangleClient.isHit(rangeRectangle, client) || RectangleClient.isHit(rangeRectangle, textClient);
|
|
2060
|
-
}
|
|
2061
|
-
return RectangleClient.isHit(rangeRectangle, client);
|
|
2062
|
-
}
|
|
2063
|
-
if (PlaitDrawElement.isImage(element)) {
|
|
2064
|
-
const client = getRectangleByPoints(element.points);
|
|
2065
|
-
return RectangleClient.isHit(rangeRectangle, client);
|
|
2066
|
-
}
|
|
2067
|
-
if (PlaitDrawElement.isLine(element)) {
|
|
2068
|
-
const points = getLinePoints(board, element);
|
|
2069
|
-
const strokeWidth = getStrokeWidthByElement(element);
|
|
2070
|
-
const isHitText = isHitLineText(board, element, selection.focus);
|
|
2071
|
-
const isHit = isHitPolyLine(points, selection.focus, strokeWidth, 3) || isHitText;
|
|
2072
|
-
const isContainPolyLinePoint = points.some(point => {
|
|
2073
|
-
return RectangleClient.isHit(rangeRectangle, RectangleClient.toRectangleClient([point, point]));
|
|
2074
|
-
});
|
|
2075
|
-
const isIntersect = Point.isEquals(selection.anchor, selection.focus) ? isHit : isPolylineHitRectangle(points, rangeRectangle);
|
|
2076
|
-
return isContainPolyLinePoint || isIntersect;
|
|
2077
|
-
}
|
|
2078
|
-
return null;
|
|
2079
|
-
};
|
|
2080
|
-
const isHitDrawElement = (board, element, point) => {
|
|
2081
|
-
if (PlaitDrawElement.isGeometry(element)) {
|
|
2082
|
-
const fill = getFillByElement(board, element);
|
|
2083
|
-
// when shape equals text, fill is not allowed
|
|
2084
|
-
if (fill !== DefaultGeometryStyle.fill && fill !== TRANSPARENT && !PlaitDrawElement.isText(element)) {
|
|
2085
|
-
return isRectangleHitDrawElement(board, element, { anchor: point, focus: point });
|
|
2086
|
-
}
|
|
2087
|
-
else {
|
|
2088
|
-
// if shape equals text, only check text rectangle
|
|
2089
|
-
if (PlaitDrawElement.isText(element)) {
|
|
2090
|
-
const textClient = getTextRectangle(element);
|
|
2091
|
-
let isHitText = RectangleClient.isPointInRectangle(textClient, point);
|
|
2092
|
-
return isHitText;
|
|
2093
|
-
}
|
|
2094
|
-
const strokeWidth = getStrokeWidthByElement(element);
|
|
2095
|
-
const engine = getEngine(getShape(element));
|
|
2096
|
-
const corners = engine.getCornerPoints(getRectangleByPoints(element.points));
|
|
2097
|
-
const isHit = isHitPolyLine(corners, point, strokeWidth, 3);
|
|
2098
|
-
const textClient = getTextRectangle(element);
|
|
2099
|
-
let isHitText = RectangleClient.isPointInRectangle(textClient, point);
|
|
2100
|
-
return isHit || isHitText;
|
|
2101
|
-
}
|
|
2102
|
-
}
|
|
2103
|
-
if (PlaitDrawElement.isImage(element) || PlaitDrawElement.isLine(element)) {
|
|
2104
|
-
return isRectangleHitDrawElement(board, element, { anchor: point, focus: point });
|
|
2105
|
-
}
|
|
2106
|
-
return null;
|
|
2107
|
-
};
|
|
2551
|
+
const getElbowLineRouteOptions = (board, element, handleRefPair) => {
|
|
2552
|
+
handleRefPair = handleRefPair ?? getLineHandleRefPair(board, element);
|
|
2553
|
+
const { sourceRectangle, targetRectangle } = getSourceAndTargetRectangle(board, element, handleRefPair);
|
|
2554
|
+
const { sourceOuterRectangle, targetOuterRectangle } = getSourceAndTargetOuterRectangle(sourceRectangle, targetRectangle);
|
|
2555
|
+
const sourcePoint = handleRefPair.source.point;
|
|
2556
|
+
const targetPoint = handleRefPair.target.point;
|
|
2557
|
+
const nextSourcePoint = getNextPoint(sourcePoint, sourceOuterRectangle, handleRefPair.source.direction);
|
|
2558
|
+
const nextTargetPoint = getNextPoint(targetPoint, targetOuterRectangle, handleRefPair.target.direction);
|
|
2559
|
+
return {
|
|
2560
|
+
sourcePoint,
|
|
2561
|
+
nextSourcePoint,
|
|
2562
|
+
sourceRectangle,
|
|
2563
|
+
sourceOuterRectangle,
|
|
2564
|
+
targetPoint,
|
|
2565
|
+
nextTargetPoint,
|
|
2566
|
+
targetRectangle,
|
|
2567
|
+
targetOuterRectangle
|
|
2568
|
+
};
|
|
2569
|
+
};
|
|
2108
2570
|
|
|
2109
2571
|
var LineMarkerType;
|
|
2110
2572
|
(function (LineMarkerType) {
|
|
@@ -2212,7 +2674,7 @@ class GeometryShapeGenerator extends Generator {
|
|
|
2212
2674
|
return true;
|
|
2213
2675
|
}
|
|
2214
2676
|
draw(element, data) {
|
|
2215
|
-
const rectangle = getRectangleByPoints(element.points);
|
|
2677
|
+
const rectangle = RectangleClient.getRectangleByPoints(element.points);
|
|
2216
2678
|
const shape = element.shape;
|
|
2217
2679
|
if (shape === BasicShapes.text) {
|
|
2218
2680
|
return;
|
|
@@ -2261,9 +2723,6 @@ const setLineShape = (board, newProperties) => {
|
|
|
2261
2723
|
if (element.shape === newProperties.shape) {
|
|
2262
2724
|
return;
|
|
2263
2725
|
}
|
|
2264
|
-
if (newProperties.shape === LineShape.elbow) {
|
|
2265
|
-
_properties.points = [element.points[0], element.points[element.points.length - 1]];
|
|
2266
|
-
}
|
|
2267
2726
|
const path = PlaitBoard.findPath(board, element);
|
|
2268
2727
|
Transforms.setNode(board, _properties, path);
|
|
2269
2728
|
});
|
|
@@ -2285,7 +2744,7 @@ const collectLineUpdatedRefsByGeometry = (board, geometry, refs) => {
|
|
|
2285
2744
|
const object = { ...line[handle] };
|
|
2286
2745
|
const linePoints = getLinePoints(board, line);
|
|
2287
2746
|
const point = isSourceBound ? linePoints[0] : linePoints[linePoints.length - 1];
|
|
2288
|
-
object.connection =
|
|
2747
|
+
object.connection = getConnectionByNearestPoint(board, point, geometry);
|
|
2289
2748
|
const path = PlaitBoard.findPath(board, line);
|
|
2290
2749
|
const index = refs.findIndex(obj => Path.equals(obj.path, path));
|
|
2291
2750
|
if (index === -1) {
|
|
@@ -2305,7 +2764,7 @@ const collectLineUpdatedRefsByGeometry = (board, geometry, refs) => {
|
|
|
2305
2764
|
const connectLineToGeometry = (board, lineElement, handle, geometryElement) => {
|
|
2306
2765
|
const linePoints = PlaitLine.getPoints(board, lineElement);
|
|
2307
2766
|
const point = handle === LineHandleKey.source ? linePoints[0] : linePoints[linePoints.length - 1];
|
|
2308
|
-
const connection =
|
|
2767
|
+
const connection = getConnectionByNearestPoint(board, point, geometryElement);
|
|
2309
2768
|
if (connection) {
|
|
2310
2769
|
let source = lineElement.source;
|
|
2311
2770
|
let target = lineElement.target;
|
|
@@ -2345,7 +2804,7 @@ const insertGeometryByVector = (board, point, shape, vector) => {
|
|
|
2345
2804
|
offset = -shapeProperty.height / 2;
|
|
2346
2805
|
}
|
|
2347
2806
|
const vectorPoint = getPointByVector(point, vector, offset);
|
|
2348
|
-
const points =
|
|
2807
|
+
const points = RectangleClient.getPoints(RectangleClient.getRectangleByCenterPoint(vectorPoint, shapeProperty.width, shapeProperty.height));
|
|
2349
2808
|
return insertGeometry(board, points, shape);
|
|
2350
2809
|
}
|
|
2351
2810
|
return null;
|
|
@@ -2544,7 +3003,7 @@ class GeometryComponent extends CommonPluginElement {
|
|
|
2544
3003
|
}
|
|
2545
3004
|
},
|
|
2546
3005
|
getRectangle: (element) => {
|
|
2547
|
-
return getRectangleByPoints(element.points);
|
|
3006
|
+
return RectangleClient.getRectangleByPoints(element.points);
|
|
2548
3007
|
},
|
|
2549
3008
|
hasResizeHandle: () => {
|
|
2550
3009
|
const selectedElements = getSelectedElements(this.board);
|
|
@@ -2656,6 +3115,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
|
|
|
2656
3115
|
}], ctorParameters: function () { return [{ type: i0.ViewContainerRef }, { type: i0.ChangeDetectorRef }]; } });
|
|
2657
3116
|
|
|
2658
3117
|
class LineActiveGenerator extends Generator {
|
|
3118
|
+
constructor() {
|
|
3119
|
+
super(...arguments);
|
|
3120
|
+
this.onlySelectedCurrentLine = false;
|
|
3121
|
+
}
|
|
2659
3122
|
canDraw(element, data) {
|
|
2660
3123
|
if (data.selected) {
|
|
2661
3124
|
return true;
|
|
@@ -2667,82 +3130,59 @@ class LineActiveGenerator extends Generator {
|
|
|
2667
3130
|
draw(element, data) {
|
|
2668
3131
|
const activeG = createG();
|
|
2669
3132
|
const selectedElements = getSelectedElements(this.board);
|
|
2670
|
-
|
|
2671
|
-
if (
|
|
3133
|
+
this.onlySelectedCurrentLine = selectedElements.length === 1;
|
|
3134
|
+
if (this.onlySelectedCurrentLine) {
|
|
2672
3135
|
activeG.classList.add('active');
|
|
2673
3136
|
activeG.classList.add('line-handle');
|
|
2674
3137
|
const points = PlaitLine.getPoints(this.board, element);
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
3138
|
+
let updatePoints = [...points];
|
|
3139
|
+
let elbowNextRenderPoints = [];
|
|
3140
|
+
if (element.shape === LineShape.elbow) {
|
|
3141
|
+
updatePoints = points.slice(0, 1).concat(points.slice(-1));
|
|
3142
|
+
elbowNextRenderPoints = getNextRenderPoints(this.board, element, data.linePoints);
|
|
3143
|
+
}
|
|
3144
|
+
updatePoints.forEach(point => {
|
|
3145
|
+
const updateHandle = drawPrimaryHandle(this.board, point);
|
|
3146
|
+
activeG.appendChild(updateHandle);
|
|
2683
3147
|
});
|
|
2684
|
-
getMiddlePoints(this.board, element)
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
3148
|
+
const middlePoints = getMiddlePoints(this.board, element);
|
|
3149
|
+
for (let i = 0; i < middlePoints.length; i++) {
|
|
3150
|
+
const point = middlePoints[i];
|
|
3151
|
+
if (element.shape === LineShape.elbow && elbowNextRenderPoints.length) {
|
|
3152
|
+
const handleIndex = getHitPointIndex(middlePoints, point);
|
|
3153
|
+
const isUpdateHandleIndex = isUpdatedHandleIndex(this.board, element, [...points], elbowNextRenderPoints, handleIndex);
|
|
3154
|
+
if (isUpdateHandleIndex) {
|
|
3155
|
+
const updateHandle = drawPrimaryHandle(this.board, point);
|
|
3156
|
+
activeG.appendChild(updateHandle);
|
|
3157
|
+
continue;
|
|
3158
|
+
}
|
|
3159
|
+
}
|
|
3160
|
+
const circle = drawFillPrimaryHandle(this.board, point);
|
|
2691
3161
|
activeG.appendChild(circle);
|
|
2692
|
-
}
|
|
3162
|
+
}
|
|
2693
3163
|
}
|
|
2694
3164
|
else {
|
|
2695
|
-
const
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
3165
|
+
const activeRectangle = this.board.getRectangle(element);
|
|
3166
|
+
if (activeRectangle) {
|
|
3167
|
+
let opacity = '0.5';
|
|
3168
|
+
if (activeRectangle.height === 0 || activeRectangle.width === 0) {
|
|
3169
|
+
opacity = '0.8';
|
|
3170
|
+
}
|
|
3171
|
+
const strokeG = drawRectangle(this.board, activeRectangle, {
|
|
3172
|
+
stroke: PRIMARY_COLOR,
|
|
3173
|
+
strokeWidth: DefaultGeometryActiveStyle.selectionStrokeWidth
|
|
3174
|
+
});
|
|
3175
|
+
strokeG.style.opacity = opacity;
|
|
3176
|
+
activeG.appendChild(strokeG);
|
|
3177
|
+
}
|
|
2707
3178
|
}
|
|
2708
3179
|
return activeG;
|
|
2709
3180
|
}
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
const hideBuffer = 10;
|
|
2715
|
-
if (shape === LineShape.straight) {
|
|
2716
|
-
const points = PlaitLine.getPoints(board, element);
|
|
2717
|
-
for (let i = 0; i < points.length - 1; i++) {
|
|
2718
|
-
const distance = distanceBetweenPointAndPoint(...points[i], ...points[i + 1]);
|
|
2719
|
-
if (distance < hideBuffer)
|
|
2720
|
-
continue;
|
|
2721
|
-
result.push([(points[i][0] + points[i + 1][0]) / 2, (points[i][1] + points[i + 1][1]) / 2]);
|
|
2722
|
-
}
|
|
2723
|
-
}
|
|
2724
|
-
if (shape === LineShape.curve) {
|
|
2725
|
-
const points = PlaitLine.getPoints(board, element);
|
|
2726
|
-
const pointsOnBezier = getCurvePoints(board, element);
|
|
2727
|
-
if (points.length === 2) {
|
|
2728
|
-
const start = 0;
|
|
2729
|
-
const endIndex = pointsOnBezier.length - 1;
|
|
2730
|
-
const middleIndex = Math.round((start + endIndex) / 2);
|
|
2731
|
-
result.push(pointsOnBezier[middleIndex]);
|
|
2732
|
-
}
|
|
2733
|
-
else {
|
|
2734
|
-
for (let i = 0; i < points.length - 1; i++) {
|
|
2735
|
-
const startIndex = pointsOnBezier.findIndex(point => point[0] === points[i][0] && point[1] === points[i][1]);
|
|
2736
|
-
const endIndex = pointsOnBezier.findIndex(point => point[0] === points[i + 1][0] && point[1] === points[i + 1][1]);
|
|
2737
|
-
const middleIndex = Math.round((startIndex + endIndex) / 2);
|
|
2738
|
-
const distance = distanceBetweenPointAndPoint(...points[i], ...points[i + 1]);
|
|
2739
|
-
if (distance < hideBuffer)
|
|
2740
|
-
continue;
|
|
2741
|
-
result.push(pointsOnBezier[middleIndex]);
|
|
2742
|
-
}
|
|
2743
|
-
}
|
|
3181
|
+
needUpdate() {
|
|
3182
|
+
const selectedElements = getSelectedElements(this.board);
|
|
3183
|
+
const onlySelectedCurrentLine = selectedElements.length === 1;
|
|
3184
|
+
return onlySelectedCurrentLine !== this.onlySelectedCurrentLine;
|
|
2744
3185
|
}
|
|
2745
|
-
return result;
|
|
2746
3186
|
}
|
|
2747
3187
|
|
|
2748
3188
|
class LineComponent extends CommonPluginElement {
|
|
@@ -2761,10 +3201,29 @@ class LineComponent extends CommonPluginElement {
|
|
|
2761
3201
|
ngOnInit() {
|
|
2762
3202
|
this.initializeGenerator();
|
|
2763
3203
|
this.shapeGenerator.processDrawing(this.element, this.g);
|
|
2764
|
-
|
|
3204
|
+
const linePoints = getLinePoints(this.board, this.element);
|
|
3205
|
+
this.activeGenerator.processDrawing(this.element, PlaitBoard.getElementActiveHost(this.board), {
|
|
3206
|
+
selected: this.selected,
|
|
3207
|
+
linePoints
|
|
3208
|
+
});
|
|
2765
3209
|
super.ngOnInit();
|
|
2766
3210
|
this.boundedElements = this.getBoundedElements();
|
|
2767
3211
|
this.drawText();
|
|
3212
|
+
// const points = this.element.points;
|
|
3213
|
+
// points.forEach((p, index) => {
|
|
3214
|
+
// if (index === 0) {
|
|
3215
|
+
// return;
|
|
3216
|
+
// }
|
|
3217
|
+
// if (index === points.length - 1) {
|
|
3218
|
+
// return;
|
|
3219
|
+
// }
|
|
3220
|
+
// const dataPointG = PlaitBoard.getRoughSVG(this.board).circle(p[0], p[1], 8 * index, {
|
|
3221
|
+
// stroke: '#f08c02',
|
|
3222
|
+
// fill: '#f08c02',
|
|
3223
|
+
// fillStyle: 'solid'
|
|
3224
|
+
// });
|
|
3225
|
+
// PlaitBoard.getElementActiveHost(this.board).append(dataPointG);
|
|
3226
|
+
// });
|
|
2768
3227
|
}
|
|
2769
3228
|
getBoundedElements() {
|
|
2770
3229
|
const boundedElements = {};
|
|
@@ -2787,21 +3246,31 @@ class LineComponent extends CommonPluginElement {
|
|
|
2787
3246
|
const isBoundedElementsChanged = boundedElements.source !== this.boundedElements.source || boundedElements.target !== this.boundedElements.target;
|
|
2788
3247
|
this.boundedElements = boundedElements;
|
|
2789
3248
|
const isChangeTheme = this.board.operations.find(op => op.type === 'set_theme');
|
|
3249
|
+
const linePoints = getLinePoints(this.board, this.element);
|
|
2790
3250
|
if (value.element !== previous.element || isChangeTheme) {
|
|
2791
3251
|
this.shapeGenerator.processDrawing(this.element, this.g);
|
|
2792
|
-
this.activeGenerator.processDrawing(this.element, PlaitBoard.getElementActiveHost(this.board), {
|
|
3252
|
+
this.activeGenerator.processDrawing(this.element, PlaitBoard.getElementActiveHost(this.board), {
|
|
3253
|
+
selected: this.selected,
|
|
3254
|
+
linePoints
|
|
3255
|
+
});
|
|
2793
3256
|
this.updateText(previous.element.texts, value.element.texts);
|
|
2794
3257
|
this.updateTextRectangle();
|
|
2795
3258
|
}
|
|
2796
3259
|
else {
|
|
2797
|
-
const
|
|
2798
|
-
if (
|
|
2799
|
-
this.activeGenerator.processDrawing(this.element, PlaitBoard.getElementActiveHost(this.board), {
|
|
3260
|
+
const needUpdate = value.selected !== previous.selected || this.activeGenerator.needUpdate();
|
|
3261
|
+
if (needUpdate) {
|
|
3262
|
+
this.activeGenerator.processDrawing(this.element, PlaitBoard.getElementActiveHost(this.board), {
|
|
3263
|
+
selected: this.selected,
|
|
3264
|
+
linePoints
|
|
3265
|
+
});
|
|
2800
3266
|
}
|
|
2801
3267
|
}
|
|
2802
3268
|
if (isBoundedElementsChanged) {
|
|
2803
3269
|
this.shapeGenerator.processDrawing(this.element, this.g);
|
|
2804
|
-
this.activeGenerator.processDrawing(this.element, PlaitBoard.getElementActiveHost(this.board), {
|
|
3270
|
+
this.activeGenerator.processDrawing(this.element, PlaitBoard.getElementActiveHost(this.board), {
|
|
3271
|
+
selected: this.selected,
|
|
3272
|
+
linePoints
|
|
3273
|
+
});
|
|
2805
3274
|
this.updateTextRectangle();
|
|
2806
3275
|
return;
|
|
2807
3276
|
}
|
|
@@ -2891,8 +3360,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
|
|
|
2891
3360
|
}], ctorParameters: function () { return [{ type: i0.ViewContainerRef }, { type: i0.ChangeDetectorRef }]; } });
|
|
2892
3361
|
|
|
2893
3362
|
const withDrawHotkey = (board) => {
|
|
2894
|
-
const {
|
|
2895
|
-
board.
|
|
3363
|
+
const { keyDown, dblClick } = board;
|
|
3364
|
+
board.keyDown = (event) => {
|
|
2896
3365
|
const selectedElements = getSelectedElements(board);
|
|
2897
3366
|
const isSingleSelection = selectedElements.length === 1;
|
|
2898
3367
|
const targetElement = selectedElements[0];
|
|
@@ -2906,16 +3375,16 @@ const withDrawHotkey = (board) => {
|
|
|
2906
3375
|
PlaitElement.getComponent(targetElement).editText();
|
|
2907
3376
|
return;
|
|
2908
3377
|
}
|
|
2909
|
-
|
|
3378
|
+
keyDown(event);
|
|
2910
3379
|
};
|
|
2911
|
-
board.
|
|
3380
|
+
board.dblClick = (event) => {
|
|
2912
3381
|
event.preventDefault();
|
|
2913
3382
|
const geometries = getSelectedGeometryElements(board);
|
|
2914
3383
|
if (!PlaitBoard.isReadonly(board) && geometries.length === 1) {
|
|
2915
3384
|
const component = PlaitElement.getComponent(geometries[0]);
|
|
2916
3385
|
component.editText();
|
|
2917
3386
|
}
|
|
2918
|
-
|
|
3387
|
+
dblClick(event);
|
|
2919
3388
|
};
|
|
2920
3389
|
return board;
|
|
2921
3390
|
};
|
|
@@ -2938,7 +3407,7 @@ const withGeometryCreateByDrag = (board) => {
|
|
|
2938
3407
|
const memorizedLatest = getMemorizedLatestByPointer(pointer);
|
|
2939
3408
|
if (pointer === BasicShapes.text) {
|
|
2940
3409
|
const property = getTextShapeProperty(board, DefaultTextProperty.text, memorizedLatest.textProperties['font-size']);
|
|
2941
|
-
const points =
|
|
3410
|
+
const points = RectangleClient.getPoints(RectangleClient.getRectangleByCenterPoint(movingPoint, property.width, property.height));
|
|
2942
3411
|
temporaryElement = createTextElement(board, points);
|
|
2943
3412
|
if (!fakeCreateTextRef) {
|
|
2944
3413
|
const textManage = new TextManage(board, PlaitBoard.getComponent(board).viewContainerRef, {
|
|
@@ -2990,18 +3459,18 @@ const withGeometryCreateByDrag = (board) => {
|
|
|
2990
3459
|
return board;
|
|
2991
3460
|
};
|
|
2992
3461
|
const withGeometryCreateByDrawing = (board) => {
|
|
2993
|
-
const { pointerDown, pointerMove, pointerUp,
|
|
3462
|
+
const { pointerDown, pointerMove, pointerUp, keyDown, keyUp } = board;
|
|
2994
3463
|
let start = null;
|
|
2995
3464
|
let geometryShapeG = null;
|
|
2996
3465
|
let temporaryElement = null;
|
|
2997
3466
|
let isShift = false;
|
|
2998
|
-
board.
|
|
3467
|
+
board.keyDown = (event) => {
|
|
2999
3468
|
isShift = isKeyHotkey('shift', event);
|
|
3000
|
-
|
|
3469
|
+
keyDown(event);
|
|
3001
3470
|
};
|
|
3002
|
-
board.
|
|
3471
|
+
board.keyUp = (event) => {
|
|
3003
3472
|
isShift = false;
|
|
3004
|
-
|
|
3473
|
+
keyUp(event);
|
|
3005
3474
|
};
|
|
3006
3475
|
board.pointerDown = (event) => {
|
|
3007
3476
|
const geometryPointers = getGeometryPointers();
|
|
@@ -3014,7 +3483,7 @@ const withGeometryCreateByDrawing = (board) => {
|
|
|
3014
3483
|
if (pointer === BasicShapes.text) {
|
|
3015
3484
|
const memorizedLatest = getMemorizedLatestByPointer(pointer);
|
|
3016
3485
|
const property = getTextShapeProperty(board, DefaultTextProperty.text, memorizedLatest.textProperties['font-size']);
|
|
3017
|
-
const points =
|
|
3486
|
+
const points = RectangleClient.getPoints(RectangleClient.getRectangleByCenterPoint(point, property.width, property.height));
|
|
3018
3487
|
const textElement = createTextElement(board, points);
|
|
3019
3488
|
insertElement(board, textElement);
|
|
3020
3489
|
start = null;
|
|
@@ -3041,7 +3510,7 @@ const withGeometryCreateByDrawing = (board) => {
|
|
|
3041
3510
|
const isDrawMode = !!start;
|
|
3042
3511
|
if (isDrawMode) {
|
|
3043
3512
|
const targetPoint = toViewBoxPoint(board, toHostPoint(board, event.x, event.y));
|
|
3044
|
-
const { width, height } = RectangleClient.
|
|
3513
|
+
const { width, height } = RectangleClient.getRectangleByPoints([start, targetPoint]);
|
|
3045
3514
|
if (Math.hypot(width, height) === 0) {
|
|
3046
3515
|
const pointer = PlaitBoard.getPointer(board);
|
|
3047
3516
|
if (pointer !== BasicShapes.text) {
|
|
@@ -3204,7 +3673,7 @@ const withDrawFragment = (baseBoard) => {
|
|
|
3204
3673
|
return board;
|
|
3205
3674
|
};
|
|
3206
3675
|
const getBoundedLineElements = (board, plaitShapes) => {
|
|
3207
|
-
const lines =
|
|
3676
|
+
const lines = getLines(board);
|
|
3208
3677
|
return lines.filter(line => plaitShapes.find(shape => PlaitLine.isBoundElementOfSource(line, shape) || PlaitLine.isBoundElementOfTarget(line, shape)));
|
|
3209
3678
|
};
|
|
3210
3679
|
|
|
@@ -3256,23 +3725,504 @@ const withLineCreateByDraw = (board) => {
|
|
|
3256
3725
|
return board;
|
|
3257
3726
|
};
|
|
3258
3727
|
|
|
3259
|
-
const
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3728
|
+
const ALIGN_TOLERANCE = 2;
|
|
3729
|
+
const EQUAL_SPACING = 10;
|
|
3730
|
+
const ALIGN_SPACING = 24;
|
|
3731
|
+
class ResizeAlignReaction {
|
|
3732
|
+
constructor(board, activeElements) {
|
|
3733
|
+
this.board = board;
|
|
3734
|
+
this.activeElements = activeElements;
|
|
3735
|
+
this.alignRectangles = this.getAlignRectangle();
|
|
3736
|
+
}
|
|
3737
|
+
getAlignRectangle() {
|
|
3738
|
+
const elements = findElements(this.board, {
|
|
3739
|
+
match: element => this.board.isAlign(element) && !this.activeElements.some(item => item.id === element.id),
|
|
3740
|
+
recursion: () => false,
|
|
3741
|
+
isReverse: false
|
|
3742
|
+
});
|
|
3743
|
+
return elements.map(item => this.board.getRectangle(item));
|
|
3744
|
+
}
|
|
3745
|
+
getAlignLineRef(resizeAlignDelta, resizeAlignOptions) {
|
|
3746
|
+
const { deltaX, deltaY } = resizeAlignDelta;
|
|
3747
|
+
const { resizeState, originPoint, handlePoint, isFromCorner, isAspectRatio, resizeOriginPoints } = resizeAlignOptions;
|
|
3748
|
+
const newResizeState = {
|
|
3749
|
+
...resizeState,
|
|
3750
|
+
endPoint: [resizeState.endPoint[0] + deltaX, resizeState.endPoint[1] + deltaY]
|
|
3751
|
+
};
|
|
3752
|
+
const { xZoom, yZoom } = getResizeZoom(newResizeState, originPoint, handlePoint, isFromCorner, isAspectRatio);
|
|
3753
|
+
const activePoints = resizeOriginPoints.map(p => {
|
|
3754
|
+
return movePointByZoomAndOriginPoint(p, originPoint, xZoom, yZoom);
|
|
3755
|
+
});
|
|
3756
|
+
return {
|
|
3757
|
+
deltaX,
|
|
3758
|
+
deltaY,
|
|
3759
|
+
xZoom,
|
|
3760
|
+
yZoom,
|
|
3761
|
+
activePoints
|
|
3762
|
+
};
|
|
3763
|
+
}
|
|
3764
|
+
getEqualLineDelta(resizeAlignOptions) {
|
|
3765
|
+
let equalLineDelta = {
|
|
3766
|
+
deltaX: 0,
|
|
3767
|
+
deltaY: 0
|
|
3768
|
+
};
|
|
3769
|
+
const { isAspectRatio, activeRectangle } = resizeAlignOptions;
|
|
3770
|
+
const widthAlignRectangle = this.alignRectangles.find(item => Math.abs(item.width - activeRectangle.width) < ALIGN_TOLERANCE);
|
|
3771
|
+
if (widthAlignRectangle) {
|
|
3772
|
+
const deltaWidth = widthAlignRectangle.width - activeRectangle.width;
|
|
3773
|
+
equalLineDelta.deltaX = deltaWidth * resizeAlignOptions.directionFactors[0];
|
|
3774
|
+
if (isAspectRatio) {
|
|
3775
|
+
const deltaHeight = deltaWidth / (activeRectangle.width / activeRectangle.height);
|
|
3776
|
+
equalLineDelta.deltaY = deltaHeight * resizeAlignOptions.directionFactors[1];
|
|
3777
|
+
return equalLineDelta;
|
|
3778
|
+
}
|
|
3779
|
+
}
|
|
3780
|
+
const heightAlignRectangle = this.alignRectangles.find(item => Math.abs(item.height - activeRectangle.height) < ALIGN_TOLERANCE);
|
|
3781
|
+
if (heightAlignRectangle) {
|
|
3782
|
+
const deltaHeight = heightAlignRectangle.height - activeRectangle.height;
|
|
3783
|
+
equalLineDelta.deltaY = deltaHeight * resizeAlignOptions.directionFactors[1];
|
|
3784
|
+
if (isAspectRatio) {
|
|
3785
|
+
const deltaWidth = deltaHeight * (activeRectangle.width / activeRectangle.height);
|
|
3786
|
+
equalLineDelta.deltaX = deltaWidth * resizeAlignOptions.directionFactors[0];
|
|
3787
|
+
return equalLineDelta;
|
|
3788
|
+
}
|
|
3789
|
+
}
|
|
3790
|
+
return equalLineDelta;
|
|
3791
|
+
}
|
|
3792
|
+
drawEqualLines(activePoints, resizeAlignOptions) {
|
|
3793
|
+
let widthEqualPoints = [];
|
|
3794
|
+
let heightEqualPoints = [];
|
|
3795
|
+
const drawHorizontalLine = resizeAlignOptions.directionFactors[0] !== 0 || resizeAlignOptions.isAspectRatio;
|
|
3796
|
+
const drawVerticalLine = resizeAlignOptions.directionFactors[1] !== 0 || resizeAlignOptions.isAspectRatio;
|
|
3797
|
+
const activeRectangle = RectangleClient.getRectangleByPoints(activePoints);
|
|
3798
|
+
for (let alignRectangle of this.alignRectangles) {
|
|
3799
|
+
if (activeRectangle.width === alignRectangle.width && drawHorizontalLine) {
|
|
3800
|
+
widthEqualPoints.push(getEqualLinePoints(alignRectangle, true));
|
|
3801
|
+
}
|
|
3802
|
+
if (activeRectangle.height === alignRectangle.height && drawVerticalLine) {
|
|
3803
|
+
heightEqualPoints.push(getEqualLinePoints(alignRectangle, false));
|
|
3804
|
+
}
|
|
3805
|
+
}
|
|
3806
|
+
if (widthEqualPoints.length && drawHorizontalLine) {
|
|
3807
|
+
widthEqualPoints.push(getEqualLinePoints(activeRectangle, true));
|
|
3808
|
+
}
|
|
3809
|
+
if (heightEqualPoints.length && drawVerticalLine) {
|
|
3810
|
+
heightEqualPoints.push(getEqualLinePoints(activeRectangle, false));
|
|
3811
|
+
}
|
|
3812
|
+
const equalLinePoints = [...widthEqualPoints, ...heightEqualPoints];
|
|
3813
|
+
return drawEqualLines(this.board, equalLinePoints);
|
|
3814
|
+
}
|
|
3815
|
+
getAlignLineDelta(resizeAlignOptions) {
|
|
3816
|
+
let alignLineDelta = {
|
|
3817
|
+
deltaX: 0,
|
|
3818
|
+
deltaY: 0
|
|
3819
|
+
};
|
|
3820
|
+
const { isAspectRatio, activeRectangle, directionFactors } = resizeAlignOptions;
|
|
3821
|
+
const drawHorizontal = directionFactors[0] !== 0 || isAspectRatio;
|
|
3822
|
+
const drawVertical = directionFactors[1] !== 0 || isAspectRatio;
|
|
3823
|
+
if (drawHorizontal) {
|
|
3824
|
+
const xAlignAxis = getTripleAlignAxis(activeRectangle, true);
|
|
3825
|
+
const alignX = directionFactors[0] === -1 ? xAlignAxis[0] : xAlignAxis[2];
|
|
3826
|
+
const deltaX = getMinAlignDelta(this.alignRectangles, alignX, true);
|
|
3827
|
+
if (Math.abs(deltaX) < ALIGN_TOLERANCE) {
|
|
3828
|
+
alignLineDelta.deltaX = deltaX;
|
|
3829
|
+
if (alignLineDelta.deltaX !== 0 && isAspectRatio) {
|
|
3830
|
+
alignLineDelta.deltaY = alignLineDelta.deltaX / (activeRectangle.width / activeRectangle.height);
|
|
3831
|
+
return alignLineDelta;
|
|
3832
|
+
}
|
|
3833
|
+
}
|
|
3834
|
+
}
|
|
3835
|
+
if (drawVertical) {
|
|
3836
|
+
const yAlignAxis = getTripleAlignAxis(activeRectangle, false);
|
|
3837
|
+
const alignY = directionFactors[1] === -1 ? yAlignAxis[0] : yAlignAxis[2];
|
|
3838
|
+
const deltaY = getMinAlignDelta(this.alignRectangles, alignY, false);
|
|
3839
|
+
if (Math.abs(deltaY) < ALIGN_TOLERANCE) {
|
|
3840
|
+
alignLineDelta.deltaY = deltaY;
|
|
3841
|
+
if (alignLineDelta.deltaY !== 0 && isAspectRatio) {
|
|
3842
|
+
alignLineDelta.deltaX = alignLineDelta.deltaY * (activeRectangle.width / activeRectangle.height);
|
|
3843
|
+
return alignLineDelta;
|
|
3844
|
+
}
|
|
3845
|
+
}
|
|
3846
|
+
}
|
|
3847
|
+
return alignLineDelta;
|
|
3848
|
+
}
|
|
3849
|
+
drawAlignLines(activePoints, resizeAlignOptions) {
|
|
3850
|
+
let alignLinePoints = [];
|
|
3851
|
+
const activeRectangle = RectangleClient.getRectangleByPoints(activePoints);
|
|
3852
|
+
const alignAxisX = getTripleAlignAxis(activeRectangle, true);
|
|
3853
|
+
const alignAxisY = getTripleAlignAxis(activeRectangle, false);
|
|
3854
|
+
const alignLineRefs = [
|
|
3855
|
+
{
|
|
3856
|
+
axis: alignAxisX[0],
|
|
3857
|
+
isHorizontal: true,
|
|
3858
|
+
alignRectangles: []
|
|
3859
|
+
},
|
|
3860
|
+
{
|
|
3861
|
+
axis: alignAxisX[2],
|
|
3862
|
+
isHorizontal: true,
|
|
3863
|
+
alignRectangles: []
|
|
3864
|
+
},
|
|
3865
|
+
{
|
|
3866
|
+
axis: alignAxisY[0],
|
|
3867
|
+
isHorizontal: false,
|
|
3868
|
+
alignRectangles: []
|
|
3869
|
+
},
|
|
3870
|
+
{
|
|
3871
|
+
axis: alignAxisY[2],
|
|
3872
|
+
isHorizontal: false,
|
|
3873
|
+
alignRectangles: []
|
|
3874
|
+
}
|
|
3875
|
+
];
|
|
3876
|
+
const setAlignLine = (axis, alignRectangle, isHorizontal) => {
|
|
3877
|
+
const boundingRectangle = RectangleClient.inflate(RectangleClient.getBoundingRectangle([activeRectangle, alignRectangle]), ALIGN_SPACING);
|
|
3878
|
+
if (isHorizontal) {
|
|
3879
|
+
const pointStart = [axis, boundingRectangle.y];
|
|
3880
|
+
const pointEnd = [axis, boundingRectangle.y + boundingRectangle.height];
|
|
3881
|
+
alignLinePoints.push([pointStart, pointEnd]);
|
|
3882
|
+
}
|
|
3883
|
+
else {
|
|
3884
|
+
const pointStart = [boundingRectangle.x, axis];
|
|
3885
|
+
const pointEnd = [boundingRectangle.x + boundingRectangle.width, axis];
|
|
3886
|
+
alignLinePoints.push([pointStart, pointEnd]);
|
|
3887
|
+
}
|
|
3888
|
+
};
|
|
3889
|
+
const { isAspectRatio, directionFactors } = resizeAlignOptions;
|
|
3890
|
+
const drawHorizontal = directionFactors[0] !== 0 || isAspectRatio;
|
|
3891
|
+
const drawVertical = directionFactors[1] !== 0 || isAspectRatio;
|
|
3892
|
+
for (let index = 0; index < this.alignRectangles.length; index++) {
|
|
3893
|
+
const element = this.alignRectangles[index];
|
|
3894
|
+
if (isAlign(alignLineRefs[0].axis, element, alignLineRefs[0].isHorizontal)) {
|
|
3895
|
+
alignLineRefs[0].alignRectangles.push(element);
|
|
3896
|
+
}
|
|
3897
|
+
if (isAlign(alignLineRefs[1].axis, element, alignLineRefs[1].isHorizontal)) {
|
|
3898
|
+
alignLineRefs[1].alignRectangles.push(element);
|
|
3899
|
+
}
|
|
3900
|
+
if (isAlign(alignLineRefs[2].axis, element, alignLineRefs[2].isHorizontal)) {
|
|
3901
|
+
alignLineRefs[2].alignRectangles.push(element);
|
|
3902
|
+
}
|
|
3903
|
+
if (isAlign(alignLineRefs[3].axis, element, alignLineRefs[3].isHorizontal)) {
|
|
3904
|
+
alignLineRefs[3].alignRectangles.push(element);
|
|
3905
|
+
}
|
|
3906
|
+
}
|
|
3907
|
+
if (drawHorizontal && alignLineRefs[0].alignRectangles.length) {
|
|
3908
|
+
const leftRectangle = alignLineRefs[0].alignRectangles.length === 1
|
|
3909
|
+
? alignLineRefs[0].alignRectangles[0]
|
|
3910
|
+
: getNearestAlignRectangle(alignLineRefs[0].alignRectangles, activeRectangle);
|
|
3911
|
+
setAlignLine(alignLineRefs[0].axis, leftRectangle, alignLineRefs[0].isHorizontal);
|
|
3912
|
+
}
|
|
3913
|
+
if (drawHorizontal && alignLineRefs[1].alignRectangles.length) {
|
|
3914
|
+
const rightRectangle = alignLineRefs[1].alignRectangles.length === 1
|
|
3915
|
+
? alignLineRefs[1].alignRectangles[0]
|
|
3916
|
+
: getNearestAlignRectangle(alignLineRefs[1].alignRectangles, activeRectangle);
|
|
3917
|
+
setAlignLine(alignLineRefs[1].axis, rightRectangle, alignLineRefs[1].isHorizontal);
|
|
3918
|
+
}
|
|
3919
|
+
if (drawVertical && alignLineRefs[2].alignRectangles.length) {
|
|
3920
|
+
const topRectangle = alignLineRefs[2].alignRectangles.length === 1
|
|
3921
|
+
? alignLineRefs[2].alignRectangles[0]
|
|
3922
|
+
: getNearestAlignRectangle(alignLineRefs[2].alignRectangles, activeRectangle);
|
|
3923
|
+
setAlignLine(alignLineRefs[2].axis, topRectangle, alignLineRefs[2].isHorizontal);
|
|
3924
|
+
}
|
|
3925
|
+
if (drawVertical && alignLineRefs[3].alignRectangles.length) {
|
|
3926
|
+
const bottomRectangle = alignLineRefs[3].alignRectangles.length === 1
|
|
3927
|
+
? alignLineRefs[3].alignRectangles[0]
|
|
3928
|
+
: getNearestAlignRectangle(alignLineRefs[3].alignRectangles, activeRectangle);
|
|
3929
|
+
setAlignLine(alignLineRefs[3].axis, bottomRectangle, alignLineRefs[3].isHorizontal);
|
|
3930
|
+
}
|
|
3931
|
+
return drawAlignLines(this.board, alignLinePoints);
|
|
3932
|
+
}
|
|
3933
|
+
handleResizeAlign(resizeAlignOptions) {
|
|
3934
|
+
const alignG = createG();
|
|
3935
|
+
let alignLineDelta = this.getEqualLineDelta(resizeAlignOptions);
|
|
3936
|
+
if (alignLineDelta.deltaX === 0 && alignLineDelta.deltaY === 0) {
|
|
3937
|
+
alignLineDelta = this.getAlignLineDelta(resizeAlignOptions);
|
|
3938
|
+
}
|
|
3939
|
+
const equalLineRef = this.getAlignLineRef(alignLineDelta, resizeAlignOptions);
|
|
3940
|
+
const equalLinesG = this.drawEqualLines(equalLineRef.activePoints, resizeAlignOptions);
|
|
3941
|
+
const alignLinesG = this.drawAlignLines(equalLineRef.activePoints, resizeAlignOptions);
|
|
3942
|
+
alignG.append(equalLinesG, alignLinesG);
|
|
3943
|
+
return { ...equalLineRef, alignG };
|
|
3944
|
+
}
|
|
3945
|
+
}
|
|
3946
|
+
function getBarPoint(point, isHorizontal) {
|
|
3947
|
+
return isHorizontal
|
|
3948
|
+
? [
|
|
3949
|
+
[point[0], point[1] - 4],
|
|
3950
|
+
[point[0], point[1] + 4]
|
|
3951
|
+
]
|
|
3952
|
+
: [
|
|
3953
|
+
[point[0] - 4, point[1]],
|
|
3954
|
+
[point[0] + 4, point[1]]
|
|
3955
|
+
];
|
|
3956
|
+
}
|
|
3957
|
+
function getEqualLinePoints(rectangle, isHorizontal) {
|
|
3958
|
+
return isHorizontal
|
|
3959
|
+
? [
|
|
3960
|
+
[rectangle.x, rectangle.y - EQUAL_SPACING],
|
|
3961
|
+
[rectangle.x + rectangle.width, rectangle.y - EQUAL_SPACING]
|
|
3962
|
+
]
|
|
3963
|
+
: [
|
|
3964
|
+
[rectangle.x - EQUAL_SPACING, rectangle.y],
|
|
3965
|
+
[rectangle.x - EQUAL_SPACING, rectangle.y + rectangle.height]
|
|
3966
|
+
];
|
|
3967
|
+
}
|
|
3968
|
+
function drawEqualLines(board, lines) {
|
|
3969
|
+
const g = createG();
|
|
3970
|
+
lines.forEach(line => {
|
|
3971
|
+
if (!line.length)
|
|
3972
|
+
return;
|
|
3973
|
+
const yAlign = PlaitBoard.getRoughSVG(board).line(line[0][0], line[0][1], line[1][0], line[1][1], {
|
|
3974
|
+
stroke: SELECTION_BORDER_COLOR,
|
|
3975
|
+
strokeWidth: 1
|
|
3976
|
+
});
|
|
3977
|
+
g.appendChild(yAlign);
|
|
3978
|
+
line.forEach(point => {
|
|
3979
|
+
const barPoint = getBarPoint(point, !!Point.isHorizontal(line[0], line[1]));
|
|
3980
|
+
const bar = PlaitBoard.getRoughSVG(board).line(barPoint[0][0], barPoint[0][1], barPoint[1][0], barPoint[1][1], {
|
|
3981
|
+
stroke: SELECTION_BORDER_COLOR,
|
|
3982
|
+
strokeWidth: 1
|
|
3983
|
+
});
|
|
3984
|
+
g.appendChild(bar);
|
|
3985
|
+
});
|
|
3986
|
+
});
|
|
3987
|
+
return g;
|
|
3988
|
+
}
|
|
3989
|
+
function drawAlignLines(board, lines) {
|
|
3990
|
+
const g = createG();
|
|
3991
|
+
lines.forEach(points => {
|
|
3992
|
+
if (!points.length)
|
|
3993
|
+
return;
|
|
3994
|
+
const xAlign = PlaitBoard.getRoughSVG(board).line(points[0][0], points[0][1], points[1][0], points[1][1], {
|
|
3995
|
+
stroke: SELECTION_BORDER_COLOR,
|
|
3996
|
+
strokeWidth: 1,
|
|
3997
|
+
strokeLineDash: [4, 4]
|
|
3998
|
+
});
|
|
3999
|
+
g.appendChild(xAlign);
|
|
4000
|
+
});
|
|
4001
|
+
return g;
|
|
4002
|
+
}
|
|
4003
|
+
const getTripleAlignAxis = (rectangle, isHorizontal) => {
|
|
4004
|
+
const axis = isHorizontal ? 'x' : 'y';
|
|
4005
|
+
const side = isHorizontal ? 'width' : 'height';
|
|
4006
|
+
return [rectangle[axis], rectangle[axis] + rectangle[side] / 2, rectangle[axis] + rectangle[side]];
|
|
4007
|
+
};
|
|
4008
|
+
const isAlign = (axis, rectangle, isHorizontal) => {
|
|
4009
|
+
const alignAxis = getTripleAlignAxis(rectangle, isHorizontal);
|
|
4010
|
+
return alignAxis.includes(axis);
|
|
4011
|
+
};
|
|
4012
|
+
const getClosestDelta = (axis, rectangle, isHorizontal) => {
|
|
4013
|
+
const alignAxis = getTripleAlignAxis(rectangle, isHorizontal);
|
|
4014
|
+
const deltas = alignAxis.map(item => item - axis);
|
|
4015
|
+
const absDeltas = deltas.map(item => Math.abs(item));
|
|
4016
|
+
const index = absDeltas.indexOf(Math.min(...absDeltas));
|
|
4017
|
+
return deltas[index];
|
|
4018
|
+
};
|
|
4019
|
+
function getMinAlignDelta(alignRectangles, axis, isHorizontal) {
|
|
4020
|
+
let delta = ALIGN_TOLERANCE;
|
|
4021
|
+
alignRectangles.forEach(item => {
|
|
4022
|
+
const distance = getClosestDelta(axis, item, isHorizontal);
|
|
4023
|
+
if (Math.abs(distance) < Math.abs(delta)) {
|
|
4024
|
+
delta = distance;
|
|
4025
|
+
}
|
|
4026
|
+
});
|
|
4027
|
+
return delta;
|
|
4028
|
+
}
|
|
4029
|
+
function getNearestAlignRectangle(alignRectangles, activeRectangle) {
|
|
4030
|
+
let minDistance = Infinity;
|
|
4031
|
+
let nearestRectangle = alignRectangles[0];
|
|
4032
|
+
alignRectangles.forEach(item => {
|
|
4033
|
+
const distance = Math.sqrt(Math.pow(activeRectangle.x - item.x, 2) + Math.pow(activeRectangle.y - item.y, 2));
|
|
4034
|
+
if (distance < minDistance) {
|
|
4035
|
+
minDistance = distance;
|
|
4036
|
+
nearestRectangle = item;
|
|
4037
|
+
}
|
|
4038
|
+
});
|
|
4039
|
+
return nearestRectangle;
|
|
4040
|
+
}
|
|
4041
|
+
|
|
4042
|
+
function getResizeAlignRef(board, resizeRef, resizeState, resizeOriginPointAndHandlePoint, isAspectRatio, isFromCorner) {
|
|
4043
|
+
const { originPoint, handlePoint } = resizeOriginPointAndHandlePoint;
|
|
4044
|
+
const { xZoom, yZoom } = getResizeZoom(resizeState, originPoint, handlePoint, isFromCorner, isAspectRatio);
|
|
4045
|
+
let activeElements;
|
|
4046
|
+
let resizeOriginPoints = [];
|
|
4047
|
+
if (Array.isArray(resizeRef.element)) {
|
|
4048
|
+
activeElements = resizeRef.element;
|
|
4049
|
+
const rectangle = getRectangleByElements(board, resizeRef.element, false);
|
|
4050
|
+
resizeOriginPoints = RectangleClient.getPoints(rectangle);
|
|
4051
|
+
}
|
|
4052
|
+
else {
|
|
4053
|
+
activeElements = [resizeRef.element];
|
|
4054
|
+
resizeOriginPoints = resizeRef.element.points;
|
|
4055
|
+
}
|
|
4056
|
+
const points = resizeOriginPoints.map(p => {
|
|
4057
|
+
return movePointByZoomAndOriginPoint(p, originPoint, xZoom, yZoom);
|
|
4058
|
+
});
|
|
4059
|
+
const activeRectangle = RectangleClient.getRectangleByPoints(points);
|
|
4060
|
+
const resizeAlignReaction = new ResizeAlignReaction(board, activeElements);
|
|
4061
|
+
const resizeHandlePoint = movePointByZoomAndOriginPoint(handlePoint, originPoint, xZoom, yZoom);
|
|
4062
|
+
const [x, y] = getUnitVectorByPointAndPoint(originPoint, resizeHandlePoint);
|
|
4063
|
+
return resizeAlignReaction.handleResizeAlign({
|
|
4064
|
+
resizeState,
|
|
4065
|
+
resizeOriginPoints,
|
|
4066
|
+
activeRectangle,
|
|
4067
|
+
originPoint,
|
|
4068
|
+
handlePoint,
|
|
4069
|
+
directionFactors: [getDirectionFactorByVectorComponent(x), getDirectionFactorByVectorComponent(y)],
|
|
4070
|
+
isAspectRatio,
|
|
4071
|
+
isFromCorner
|
|
4072
|
+
});
|
|
4073
|
+
}
|
|
4074
|
+
|
|
4075
|
+
function withDrawResize(board) {
|
|
4076
|
+
const { afterChange } = board;
|
|
4077
|
+
let alignG;
|
|
4078
|
+
let handleG;
|
|
4079
|
+
const canResize = () => {
|
|
4080
|
+
const elements = getSelectedElements(board);
|
|
4081
|
+
return elements.length > 1 && elements.every(el => PlaitDrawElement.isDrawElement(el));
|
|
3265
4082
|
};
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
4083
|
+
const options = {
|
|
4084
|
+
key: 'draw-elements',
|
|
4085
|
+
canResize,
|
|
4086
|
+
hitTest: (point) => {
|
|
4087
|
+
const elements = getSelectedElements(board);
|
|
4088
|
+
const boundingRectangle = getRectangleByElements(board, elements, false);
|
|
4089
|
+
const handleRef = getHitRectangleResizeHandleRef(board, boundingRectangle, point);
|
|
4090
|
+
if (handleRef) {
|
|
4091
|
+
return {
|
|
4092
|
+
element: elements,
|
|
4093
|
+
rectangle: boundingRectangle,
|
|
4094
|
+
handle: handleRef.handle,
|
|
4095
|
+
cursorClass: handleRef.cursorClass
|
|
4096
|
+
};
|
|
4097
|
+
}
|
|
4098
|
+
return null;
|
|
4099
|
+
},
|
|
4100
|
+
onResize: (resizeRef, resizeState) => {
|
|
4101
|
+
alignG?.remove();
|
|
4102
|
+
const isFromCorner = isCornerHandle(board, resizeRef.handle);
|
|
4103
|
+
const isAspectRatio = resizeState.isShift || isFromCorner;
|
|
4104
|
+
const { originPoint, handlePoint } = getResizeOriginPointAndHandlePoint(board, resizeRef);
|
|
4105
|
+
const resizeAlignRef = getResizeAlignRef(board, resizeRef, resizeState, {
|
|
4106
|
+
originPoint,
|
|
4107
|
+
handlePoint
|
|
4108
|
+
}, isAspectRatio, isFromCorner);
|
|
4109
|
+
alignG = resizeAlignRef.alignG;
|
|
4110
|
+
PlaitBoard.getElementActiveHost(board).append(alignG);
|
|
4111
|
+
resizeRef.element.forEach(target => {
|
|
4112
|
+
const path = PlaitBoard.findPath(board, target);
|
|
4113
|
+
let points = target.points.map(p => {
|
|
4114
|
+
return movePointByZoomAndOriginPoint(p, originPoint, resizeAlignRef.xZoom, resizeAlignRef.yZoom);
|
|
4115
|
+
});
|
|
4116
|
+
if (PlaitDrawElement.isGeometry(target)) {
|
|
4117
|
+
const { height: textHeight } = getFirstTextManage(target).getSize();
|
|
4118
|
+
DrawTransforms.resizeGeometry(board, points, textHeight, path);
|
|
4119
|
+
}
|
|
4120
|
+
else if (PlaitDrawElement.isLine(target)) {
|
|
4121
|
+
Transforms.setNode(board, { points }, path);
|
|
4122
|
+
}
|
|
4123
|
+
else if (PlaitDrawElement.isImage(target)) {
|
|
4124
|
+
if (isAspectRatio) {
|
|
4125
|
+
Transforms.setNode(board, { points }, path);
|
|
4126
|
+
}
|
|
4127
|
+
else {
|
|
4128
|
+
// The image element does not follow the resize, but moves based on the center point.
|
|
4129
|
+
const targetRectangle = RectangleClient.getRectangleByPoints(target.points);
|
|
4130
|
+
const centerPoint = RectangleClient.getCenterPoint(targetRectangle);
|
|
4131
|
+
const newCenterPoint = movePointByZoomAndOriginPoint(centerPoint, originPoint, resizeAlignRef.xZoom, resizeAlignRef.yZoom);
|
|
4132
|
+
const newTargetRectangle = RectangleClient.getRectangleByCenterPoint(newCenterPoint, targetRectangle.width, targetRectangle.height);
|
|
4133
|
+
Transforms.setNode(board, { points: RectangleClient.getPoints(newTargetRectangle) }, path);
|
|
4134
|
+
}
|
|
4135
|
+
}
|
|
4136
|
+
});
|
|
4137
|
+
},
|
|
4138
|
+
afterResize: (resizeRef) => {
|
|
4139
|
+
alignG?.remove();
|
|
4140
|
+
alignG = null;
|
|
4141
|
+
}
|
|
4142
|
+
};
|
|
4143
|
+
withResize(board, options);
|
|
4144
|
+
board.afterChange = () => {
|
|
4145
|
+
afterChange();
|
|
4146
|
+
if (handleG) {
|
|
4147
|
+
handleG.remove();
|
|
4148
|
+
handleG = null;
|
|
4149
|
+
}
|
|
4150
|
+
if (canResize() && !isSelectionMoving(board)) {
|
|
4151
|
+
handleG = createG();
|
|
4152
|
+
const elements = getSelectedElements(board);
|
|
4153
|
+
const boundingRectangle = getRectangleByElements(board, elements, false);
|
|
4154
|
+
const corners = RectangleClient.getCornerPoints(boundingRectangle);
|
|
4155
|
+
corners.forEach((corner) => {
|
|
4156
|
+
const g = drawHandle(board, corner);
|
|
4157
|
+
handleG && handleG.append(g);
|
|
4158
|
+
});
|
|
4159
|
+
PlaitBoard.getElementActiveHost(board).append(handleG);
|
|
4160
|
+
}
|
|
4161
|
+
};
|
|
4162
|
+
return board;
|
|
4163
|
+
}
|
|
4164
|
+
const getResizeOriginPointAndHandlePoint = (board, resizeRef) => {
|
|
4165
|
+
const handleIndex = getIndexByResizeHandle(resizeRef.handle);
|
|
4166
|
+
const symmetricHandleIndex = getSymmetricHandleIndex(board, handleIndex);
|
|
4167
|
+
const originPoint = getResizeHandlePointByIndex(resizeRef.rectangle, symmetricHandleIndex);
|
|
4168
|
+
const handlePoint = getResizeHandlePointByIndex(resizeRef.rectangle, handleIndex);
|
|
4169
|
+
return {
|
|
4170
|
+
originPoint,
|
|
4171
|
+
handlePoint
|
|
3269
4172
|
};
|
|
4173
|
+
};
|
|
4174
|
+
const getResizeZoom = (resizeState, resizeOriginPoint, resizeHandlePoint, isFromCorner, isAspectRatio) => {
|
|
4175
|
+
const startPoint = resizeState.startPoint;
|
|
4176
|
+
const endPoint = resizeState.endPoint;
|
|
4177
|
+
let xZoom = 0;
|
|
4178
|
+
let yZoom = 0;
|
|
4179
|
+
if (isFromCorner) {
|
|
4180
|
+
if (isAspectRatio) {
|
|
4181
|
+
let normalizedOffsetX = Point.getOffsetX(startPoint, endPoint);
|
|
4182
|
+
xZoom = normalizedOffsetX / (resizeHandlePoint[0] - resizeOriginPoint[0]);
|
|
4183
|
+
yZoom = xZoom;
|
|
4184
|
+
}
|
|
4185
|
+
else {
|
|
4186
|
+
let normalizedOffsetX = Point.getOffsetX(startPoint, endPoint);
|
|
4187
|
+
let normalizedOffsetY = Point.getOffsetY(startPoint, endPoint);
|
|
4188
|
+
xZoom = normalizedOffsetX / (resizeHandlePoint[0] - resizeOriginPoint[0]);
|
|
4189
|
+
yZoom = normalizedOffsetY / (resizeHandlePoint[1] - resizeOriginPoint[1]);
|
|
4190
|
+
}
|
|
4191
|
+
}
|
|
4192
|
+
else {
|
|
4193
|
+
const isHorizontal = Point.isHorizontal(resizeOriginPoint, resizeHandlePoint, 0.1) || false;
|
|
4194
|
+
let normalizedOffset = isHorizontal ? Point.getOffsetX(startPoint, endPoint) : Point.getOffsetY(startPoint, endPoint);
|
|
4195
|
+
let benchmarkOffset = isHorizontal ? resizeHandlePoint[0] - resizeOriginPoint[0] : resizeHandlePoint[1] - resizeOriginPoint[1];
|
|
4196
|
+
const zoom = normalizedOffset / benchmarkOffset;
|
|
4197
|
+
if (isAspectRatio) {
|
|
4198
|
+
xZoom = zoom;
|
|
4199
|
+
yZoom = zoom;
|
|
4200
|
+
}
|
|
4201
|
+
else {
|
|
4202
|
+
if (isHorizontal) {
|
|
4203
|
+
xZoom = zoom;
|
|
4204
|
+
}
|
|
4205
|
+
else {
|
|
4206
|
+
yZoom = zoom;
|
|
4207
|
+
}
|
|
4208
|
+
}
|
|
4209
|
+
}
|
|
4210
|
+
return { xZoom, yZoom };
|
|
4211
|
+
};
|
|
4212
|
+
const movePointByZoomAndOriginPoint = (p, resizeOriginPoint, xZoom, yZoom) => {
|
|
4213
|
+
const offsetX = (p[0] - resizeOriginPoint[0]) * xZoom;
|
|
4214
|
+
const offsetY = (p[1] - resizeOriginPoint[1]) * yZoom;
|
|
4215
|
+
return [p[0] + offsetX, p[1] + offsetY];
|
|
4216
|
+
};
|
|
4217
|
+
|
|
4218
|
+
const withGeometryResize = (board) => {
|
|
4219
|
+
let alignG;
|
|
3270
4220
|
const options = {
|
|
3271
4221
|
key: 'draw-geometry',
|
|
3272
4222
|
canResize: () => {
|
|
3273
4223
|
return true;
|
|
3274
4224
|
},
|
|
3275
|
-
|
|
4225
|
+
hitTest: (point) => {
|
|
3276
4226
|
const selectedElements = [...getSelectedGeometryElements(board), ...getSelectedImageElements(board)];
|
|
3277
4227
|
if (selectedElements.length !== 1 || getSelectedElements(board).length !== 1) {
|
|
3278
4228
|
return null;
|
|
@@ -3280,46 +4230,31 @@ const withGeometryResize = (board) => {
|
|
|
3280
4230
|
const target = selectedElements[0];
|
|
3281
4231
|
const targetComponent = PlaitElement.getComponent(selectedElements[0]);
|
|
3282
4232
|
if (targetComponent.activeGenerator.hasResizeHandle) {
|
|
3283
|
-
const
|
|
4233
|
+
const rectangle = board.getRectangle(target);
|
|
4234
|
+
const handleRef = getHitRectangleResizeHandleRef(board, rectangle, point);
|
|
3284
4235
|
if (handleRef) {
|
|
3285
4236
|
return {
|
|
3286
4237
|
element: target,
|
|
3287
4238
|
handle: handleRef.handle,
|
|
3288
|
-
cursorClass: handleRef.cursorClass
|
|
4239
|
+
cursorClass: handleRef.cursorClass,
|
|
4240
|
+
rectangle
|
|
3289
4241
|
};
|
|
3290
4242
|
}
|
|
3291
4243
|
}
|
|
3292
4244
|
return null;
|
|
3293
4245
|
},
|
|
3294
4246
|
onResize: (resizeRef, resizeState) => {
|
|
3295
|
-
|
|
3296
|
-
const
|
|
3297
|
-
const
|
|
3298
|
-
const
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
if ((isShift || PlaitDrawElement.isImage(resizeRef.element)) && !isCornerHandle) {
|
|
3307
|
-
const rectangle = getRectangleByPoints(points);
|
|
3308
|
-
if (resizeRef.handle === ResizeHandle.n || resizeRef.handle === ResizeHandle.s) {
|
|
3309
|
-
const newWidth = rectangle.height / ratio;
|
|
3310
|
-
const offset = (newWidth - rectangle.width) / 2;
|
|
3311
|
-
const newRectangle = RectangleClient.expand(rectangle, offset, 0);
|
|
3312
|
-
const cornerPoints = RectangleClient.getCornerPoints(newRectangle);
|
|
3313
|
-
points = [cornerPoints[0], cornerPoints[2]];
|
|
3314
|
-
}
|
|
3315
|
-
if (resizeRef.handle === ResizeHandle.e || resizeRef.handle === ResizeHandle.w) {
|
|
3316
|
-
const newHeight = rectangle.width * ratio;
|
|
3317
|
-
const offset = (newHeight - rectangle.height) / 2;
|
|
3318
|
-
const newRectangle = RectangleClient.expand(rectangle, 0, offset);
|
|
3319
|
-
const cornerPoints = RectangleClient.getCornerPoints(newRectangle);
|
|
3320
|
-
points = [cornerPoints[0], cornerPoints[2]];
|
|
3321
|
-
}
|
|
3322
|
-
}
|
|
4247
|
+
alignG?.remove();
|
|
4248
|
+
const isFromCorner = isCornerHandle(board, resizeRef.handle);
|
|
4249
|
+
const isAspectRatio = resizeState.isShift || PlaitDrawElement.isImage(resizeRef.element);
|
|
4250
|
+
const { originPoint, handlePoint } = getResizeOriginPointAndHandlePoint(board, resizeRef);
|
|
4251
|
+
const resizeAlignRef = getResizeAlignRef(board, resizeRef, resizeState, {
|
|
4252
|
+
originPoint,
|
|
4253
|
+
handlePoint
|
|
4254
|
+
}, isAspectRatio, isFromCorner);
|
|
4255
|
+
alignG = resizeAlignRef.alignG;
|
|
4256
|
+
PlaitBoard.getElementActiveHost(board).append(alignG);
|
|
4257
|
+
let points = resizeAlignRef.activePoints;
|
|
3323
4258
|
if (PlaitDrawElement.isGeometry(resizeRef.element)) {
|
|
3324
4259
|
const { height: textHeight } = getFirstTextManage(resizeRef.element).getSize();
|
|
3325
4260
|
DrawTransforms.resizeGeometry(board, points, textHeight, resizeRef.path);
|
|
@@ -3328,88 +4263,28 @@ const withGeometryResize = (board) => {
|
|
|
3328
4263
|
points = normalizeShapePoints(points);
|
|
3329
4264
|
Transforms.setNode(board, { points }, resizeRef.path);
|
|
3330
4265
|
}
|
|
4266
|
+
},
|
|
4267
|
+
afterResize: (resizeRef) => {
|
|
4268
|
+
alignG?.remove();
|
|
4269
|
+
alignG = null;
|
|
3331
4270
|
}
|
|
3332
4271
|
};
|
|
3333
4272
|
withResize(board, options);
|
|
3334
4273
|
return board;
|
|
3335
4274
|
};
|
|
3336
|
-
const getPointsByResizeHandle = (movingPoint, elementPoints, handle) => {
|
|
3337
|
-
switch (handle) {
|
|
3338
|
-
case ResizeHandle.nw: {
|
|
3339
|
-
return [movingPoint, elementPoints[1]];
|
|
3340
|
-
}
|
|
3341
|
-
case ResizeHandle.ne: {
|
|
3342
|
-
return [movingPoint, [elementPoints[0][0], elementPoints[1][1]]];
|
|
3343
|
-
}
|
|
3344
|
-
case ResizeHandle.se: {
|
|
3345
|
-
return [movingPoint, elementPoints[0]];
|
|
3346
|
-
}
|
|
3347
|
-
case ResizeHandle.sw: {
|
|
3348
|
-
return [movingPoint, [elementPoints[1][0], elementPoints[0][1]]];
|
|
3349
|
-
}
|
|
3350
|
-
case ResizeHandle.n: {
|
|
3351
|
-
return [[elementPoints[0][0], movingPoint[1]], elementPoints[1]];
|
|
3352
|
-
}
|
|
3353
|
-
case ResizeHandle.s: {
|
|
3354
|
-
return [elementPoints[0], [elementPoints[1][0], movingPoint[1]]];
|
|
3355
|
-
}
|
|
3356
|
-
case ResizeHandle.w: {
|
|
3357
|
-
return [[movingPoint[0], elementPoints[0][1]], elementPoints[1]];
|
|
3358
|
-
}
|
|
3359
|
-
default: {
|
|
3360
|
-
return [elementPoints[0], [movingPoint[0], elementPoints[1][1]]];
|
|
3361
|
-
}
|
|
3362
|
-
}
|
|
3363
|
-
};
|
|
3364
|
-
|
|
3365
|
-
var LineResizeHandle;
|
|
3366
|
-
(function (LineResizeHandle) {
|
|
3367
|
-
LineResizeHandle["source"] = "source";
|
|
3368
|
-
LineResizeHandle["target"] = "target";
|
|
3369
|
-
LineResizeHandle["addHandle"] = "addHandle";
|
|
3370
|
-
})(LineResizeHandle || (LineResizeHandle = {}));
|
|
3371
|
-
const getHitLineResizeHandleRef = (board, element, point) => {
|
|
3372
|
-
const points = PlaitLine.getPoints(board, element);
|
|
3373
|
-
const index = getHitPointIndex(points, point);
|
|
3374
|
-
if (index !== -1) {
|
|
3375
|
-
if (index === 0) {
|
|
3376
|
-
return { handle: LineResizeHandle.source, index };
|
|
3377
|
-
}
|
|
3378
|
-
if (index === points.length - 1) {
|
|
3379
|
-
return { handle: LineResizeHandle.target, index };
|
|
3380
|
-
}
|
|
3381
|
-
return { index };
|
|
3382
|
-
}
|
|
3383
|
-
const middlePoints = getMiddlePoints(board, element);
|
|
3384
|
-
const middleIndex = getHitPointIndex(middlePoints, point);
|
|
3385
|
-
if (middleIndex !== -1) {
|
|
3386
|
-
return { handle: LineResizeHandle.addHandle, index: middleIndex };
|
|
3387
|
-
}
|
|
3388
|
-
return undefined;
|
|
3389
|
-
};
|
|
3390
|
-
function getHitPointIndex(points, movingPoint) {
|
|
3391
|
-
const rectangles = points.map(point => {
|
|
3392
|
-
return {
|
|
3393
|
-
x: point[0] - RESIZE_HANDLE_DIAMETER / 2,
|
|
3394
|
-
y: point[1] - RESIZE_HANDLE_DIAMETER / 2,
|
|
3395
|
-
width: RESIZE_HANDLE_DIAMETER,
|
|
3396
|
-
height: RESIZE_HANDLE_DIAMETER
|
|
3397
|
-
};
|
|
3398
|
-
});
|
|
3399
|
-
const rectangle = rectangles.find(rectangle => {
|
|
3400
|
-
return RectangleClient.isHit(RectangleClient.toRectangleClient([movingPoint, movingPoint]), rectangle);
|
|
3401
|
-
});
|
|
3402
|
-
return rectangle ? rectangles.indexOf(rectangle) : -1;
|
|
3403
|
-
}
|
|
3404
4275
|
|
|
3405
4276
|
const withLineResize = (board) => {
|
|
3406
|
-
let
|
|
4277
|
+
let elbowLineIndex;
|
|
4278
|
+
let elbowLineDeleteCount;
|
|
4279
|
+
let elbowSourcePoint;
|
|
4280
|
+
let elbowTargetPoint;
|
|
4281
|
+
let elbowNextRenderPoints;
|
|
3407
4282
|
const options = {
|
|
3408
4283
|
key: 'draw-line',
|
|
3409
4284
|
canResize: () => {
|
|
3410
4285
|
return true;
|
|
3411
4286
|
},
|
|
3412
|
-
|
|
4287
|
+
hitTest: (point) => {
|
|
3413
4288
|
const selectedLineElements = getSelectedLineElements(board);
|
|
3414
4289
|
if (selectedLineElements.length > 0) {
|
|
3415
4290
|
let result = null;
|
|
@@ -3418,25 +4293,46 @@ const withLineResize = (board) => {
|
|
|
3418
4293
|
if (handleRef) {
|
|
3419
4294
|
result = {
|
|
3420
4295
|
element: value,
|
|
3421
|
-
handle: handleRef.handle
|
|
4296
|
+
handle: handleRef.handle,
|
|
4297
|
+
handleIndex: handleRef.handleIndex
|
|
3422
4298
|
};
|
|
3423
|
-
pointIndex = handleRef.handle === LineResizeHandle.addHandle ? handleRef.index + 1 : handleRef.index;
|
|
3424
4299
|
}
|
|
3425
4300
|
});
|
|
3426
4301
|
return result;
|
|
3427
4302
|
}
|
|
3428
4303
|
return null;
|
|
3429
4304
|
},
|
|
4305
|
+
beforeResize: (resizeRef) => {
|
|
4306
|
+
if (resizeRef.element.shape === LineShape.elbow &&
|
|
4307
|
+
resizeRef.handle !== LineResizeHandle.source &&
|
|
4308
|
+
resizeRef.handle !== LineResizeHandle.target) {
|
|
4309
|
+
const params = getElbowLineRouteOptions(board, resizeRef.element);
|
|
4310
|
+
const isIntersect = isSourceAndTargetIntersect(params);
|
|
4311
|
+
if (isIntersect) {
|
|
4312
|
+
return;
|
|
4313
|
+
}
|
|
4314
|
+
const points = [...resizeRef.element.points];
|
|
4315
|
+
const handleIndex = resizeRef.handleIndex;
|
|
4316
|
+
const pointsOnElbow = getElbowPoints(board, resizeRef.element);
|
|
4317
|
+
elbowSourcePoint = pointsOnElbow[0];
|
|
4318
|
+
elbowTargetPoint = pointsOnElbow[pointsOnElbow.length - 1];
|
|
4319
|
+
elbowNextRenderPoints = getNextRenderPoints(board, resizeRef.element, pointsOnElbow);
|
|
4320
|
+
const value = getIndexAndDeleteCountByKeyPoint(board, resizeRef.element, [...points], elbowNextRenderPoints, handleIndex);
|
|
4321
|
+
elbowLineIndex = value.index;
|
|
4322
|
+
elbowLineDeleteCount = value.deleteCount;
|
|
4323
|
+
}
|
|
4324
|
+
},
|
|
3430
4325
|
onResize: (resizeRef, resizeState) => {
|
|
3431
4326
|
let points = [...resizeRef.element.points];
|
|
3432
4327
|
let source = { ...resizeRef.element.source };
|
|
3433
4328
|
let target = { ...resizeRef.element.target };
|
|
3434
|
-
|
|
4329
|
+
let handleIndex = resizeRef.handleIndex;
|
|
4330
|
+
const hitElement = getHitOutlineGeometry(board, resizeState.endPoint, REACTION_MARGIN);
|
|
3435
4331
|
if (resizeRef.handle === LineResizeHandle.source || resizeRef.handle === LineResizeHandle.target) {
|
|
3436
4332
|
const object = resizeRef.handle === LineResizeHandle.source ? source : target;
|
|
3437
|
-
points[
|
|
4333
|
+
points[handleIndex] = resizeState.endPoint;
|
|
3438
4334
|
if (hitElement) {
|
|
3439
|
-
object.connection =
|
|
4335
|
+
object.connection = getConnectionByNearestPoint(board, resizeState.endPoint, hitElement);
|
|
3440
4336
|
object.boundId = hitElement.id;
|
|
3441
4337
|
}
|
|
3442
4338
|
else {
|
|
@@ -3444,24 +4340,80 @@ const withLineResize = (board) => {
|
|
|
3444
4340
|
object.boundId = undefined;
|
|
3445
4341
|
}
|
|
3446
4342
|
}
|
|
3447
|
-
else if (resizeRef.handle === LineResizeHandle.addHandle) {
|
|
3448
|
-
points.splice(pointIndex, 0, resizeState.endTransformPoint);
|
|
3449
|
-
}
|
|
3450
4343
|
else {
|
|
3451
|
-
|
|
4344
|
+
if (resizeRef.element.shape === LineShape.elbow) {
|
|
4345
|
+
if (elbowNextRenderPoints && elbowSourcePoint && elbowTargetPoint) {
|
|
4346
|
+
const resizedPreviousAndNextPoint = getResizedPreviousAndNextPoint(elbowNextRenderPoints, elbowSourcePoint, elbowTargetPoint, handleIndex);
|
|
4347
|
+
const startKeyPoint = elbowNextRenderPoints[handleIndex];
|
|
4348
|
+
const endKeyPoint = elbowNextRenderPoints[handleIndex + 1];
|
|
4349
|
+
const [newStartPoint, newEndPoint] = alignElbowSegment(startKeyPoint, endKeyPoint, resizeState, resizedPreviousAndNextPoint);
|
|
4350
|
+
let midDataPoints = [...points].slice(1, points.length - 1);
|
|
4351
|
+
if (elbowLineIndex !== null && elbowLineDeleteCount !== null) {
|
|
4352
|
+
if (hasIllegalElbowPoint(midDataPoints)) {
|
|
4353
|
+
midDataPoints = [newStartPoint, newEndPoint];
|
|
4354
|
+
}
|
|
4355
|
+
else {
|
|
4356
|
+
midDataPoints.splice(elbowLineIndex, elbowLineDeleteCount, newStartPoint, newEndPoint);
|
|
4357
|
+
}
|
|
4358
|
+
points = [elbowSourcePoint, ...midDataPoints, elbowTargetPoint];
|
|
4359
|
+
}
|
|
4360
|
+
}
|
|
4361
|
+
}
|
|
4362
|
+
else {
|
|
4363
|
+
if (resizeRef.handle === LineResizeHandle.addHandle) {
|
|
4364
|
+
points.splice(handleIndex + 1, 0, resizeState.endPoint);
|
|
4365
|
+
}
|
|
4366
|
+
else {
|
|
4367
|
+
points[handleIndex] = resizeState.endPoint;
|
|
4368
|
+
}
|
|
4369
|
+
}
|
|
3452
4370
|
}
|
|
3453
4371
|
if (!hitElement) {
|
|
4372
|
+
handleIndex = resizeRef.handle === LineResizeHandle.addHandle ? handleIndex + 1 : handleIndex;
|
|
3454
4373
|
const drawPoints = getLinePoints(board, resizeRef.element);
|
|
3455
4374
|
const newPoints = [...points];
|
|
3456
4375
|
newPoints[0] = drawPoints[0];
|
|
3457
4376
|
newPoints[newPoints.length - 1] = drawPoints[drawPoints.length - 1];
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
4377
|
+
if (resizeRef.element.shape !== LineShape.elbow) {
|
|
4378
|
+
newPoints.forEach((point, index) => {
|
|
4379
|
+
if (index === handleIndex)
|
|
4380
|
+
return;
|
|
4381
|
+
if (points[handleIndex]) {
|
|
4382
|
+
points[handleIndex] = alignPoints(point, points[handleIndex]);
|
|
4383
|
+
}
|
|
4384
|
+
});
|
|
4385
|
+
}
|
|
3463
4386
|
}
|
|
3464
4387
|
DrawTransforms.resizeLine(board, { points, source, target }, resizeRef.path);
|
|
4388
|
+
},
|
|
4389
|
+
afterResize: (resizeRef) => {
|
|
4390
|
+
if (resizeRef.element.shape === LineShape.elbow) {
|
|
4391
|
+
const element = PlaitNode.get(board, resizeRef.path);
|
|
4392
|
+
let points = element && [...element.points];
|
|
4393
|
+
if (points.length > 2 && elbowNextRenderPoints && elbowSourcePoint && elbowTargetPoint) {
|
|
4394
|
+
const nextSourcePoint = elbowNextRenderPoints[0];
|
|
4395
|
+
const nextTargetPoint = elbowNextRenderPoints[elbowNextRenderPoints.length - 1];
|
|
4396
|
+
points.splice(0, 1, nextSourcePoint);
|
|
4397
|
+
points.splice(-1, 1, nextTargetPoint);
|
|
4398
|
+
points = simplifyOrthogonalPoints(points);
|
|
4399
|
+
if (Point.isEquals(points[0], nextSourcePoint)) {
|
|
4400
|
+
points.splice(0, 1);
|
|
4401
|
+
}
|
|
4402
|
+
if (Point.isEquals(points[points.length - 1], nextTargetPoint)) {
|
|
4403
|
+
points.pop();
|
|
4404
|
+
}
|
|
4405
|
+
if (points.length === 1) {
|
|
4406
|
+
points = [];
|
|
4407
|
+
}
|
|
4408
|
+
points = [elbowSourcePoint, ...points, elbowTargetPoint];
|
|
4409
|
+
DrawTransforms.resizeLine(board, { points }, resizeRef.path);
|
|
4410
|
+
}
|
|
4411
|
+
}
|
|
4412
|
+
elbowLineIndex = null;
|
|
4413
|
+
elbowLineDeleteCount = null;
|
|
4414
|
+
elbowSourcePoint = null;
|
|
4415
|
+
elbowTargetPoint = null;
|
|
4416
|
+
elbowNextRenderPoints = null;
|
|
3465
4417
|
}
|
|
3466
4418
|
};
|
|
3467
4419
|
withResize(board, options);
|
|
@@ -3489,8 +4441,8 @@ const withLineBoundReaction = (board) => {
|
|
|
3489
4441
|
const hitElement = getHitOutlineGeometry(board, movingPoint, -4);
|
|
3490
4442
|
if (hitElement) {
|
|
3491
4443
|
boundShapeG = drawBoundMask(board, hitElement);
|
|
3492
|
-
let nearestPoint = getNearestPoint(hitElement, movingPoint
|
|
3493
|
-
const rectangle = getRectangleByPoints(hitElement.points);
|
|
4444
|
+
let nearestPoint = getNearestPoint(hitElement, movingPoint);
|
|
4445
|
+
const rectangle = RectangleClient.getRectangleByPoints(hitElement.points);
|
|
3494
4446
|
const activeRectangle = RectangleClient.inflate(rectangle, ACTIVE_STROKE_WIDTH);
|
|
3495
4447
|
const hitConnector = getHitConnectorPoint(nearestPoint, hitElement, activeRectangle);
|
|
3496
4448
|
nearestPoint = hitConnector ? hitConnector : nearestPoint;
|
|
@@ -3515,8 +4467,8 @@ const withLineBoundReaction = (board) => {
|
|
|
3515
4467
|
};
|
|
3516
4468
|
|
|
3517
4469
|
const withLineText = (board) => {
|
|
3518
|
-
const {
|
|
3519
|
-
board.
|
|
4470
|
+
const { dblClick } = board;
|
|
4471
|
+
board.dblClick = (event) => {
|
|
3520
4472
|
if (!PlaitBoard.isReadonly(board)) {
|
|
3521
4473
|
const clickPoint = toViewBoxPoint(board, toHostPoint(board, event.x, event.y));
|
|
3522
4474
|
const hitTarget = getHitElementByPoint(board, clickPoint, (element) => {
|
|
@@ -3547,7 +4499,7 @@ const withLineText = (board) => {
|
|
|
3547
4499
|
}
|
|
3548
4500
|
}
|
|
3549
4501
|
}
|
|
3550
|
-
|
|
4502
|
+
dblClick(event);
|
|
3551
4503
|
};
|
|
3552
4504
|
return board;
|
|
3553
4505
|
};
|
|
@@ -3689,7 +4641,7 @@ const withLineAutoComplete = (board) => {
|
|
|
3689
4641
|
const hitPoint = points[index];
|
|
3690
4642
|
if (hitPoint) {
|
|
3691
4643
|
temporaryDisableSelection(board);
|
|
3692
|
-
startPoint =
|
|
4644
|
+
startPoint = hitPoint;
|
|
3693
4645
|
sourceElement = targetElement;
|
|
3694
4646
|
BoardTransforms.updatePointerType(board, LineShape.elbow);
|
|
3695
4647
|
}
|
|
@@ -3703,7 +4655,15 @@ const withLineAutoComplete = (board) => {
|
|
|
3703
4655
|
if (startPoint && sourceElement) {
|
|
3704
4656
|
const distance = distanceBetweenPointAndPoint(...movingPoint, ...startPoint);
|
|
3705
4657
|
if (distance > PRESS_AND_MOVE_BUFFER) {
|
|
3706
|
-
|
|
4658
|
+
const rectangle = RectangleClient.getRectangleByPoints(sourceElement.points);
|
|
4659
|
+
const shape = getShape(sourceElement);
|
|
4660
|
+
const engine = getEngine(shape);
|
|
4661
|
+
let sourcePoint = startPoint;
|
|
4662
|
+
if (engine.getNearestCrossingPoint) {
|
|
4663
|
+
const crossingPoint = engine.getNearestCrossingPoint(rectangle, startPoint);
|
|
4664
|
+
sourcePoint = crossingPoint;
|
|
4665
|
+
}
|
|
4666
|
+
temporaryElement = handleLineCreating(board, LineShape.elbow, sourcePoint, movingPoint, sourceElement, lineShapeG);
|
|
3707
4667
|
}
|
|
3708
4668
|
}
|
|
3709
4669
|
pointerMove(event);
|
|
@@ -3738,7 +4698,7 @@ const withLineTextMove = (board) => {
|
|
|
3738
4698
|
canResize: () => {
|
|
3739
4699
|
return true;
|
|
3740
4700
|
},
|
|
3741
|
-
|
|
4701
|
+
hitTest: (point) => {
|
|
3742
4702
|
let result = null;
|
|
3743
4703
|
const line = getHitElementByPoint(board, point, (element) => {
|
|
3744
4704
|
return PlaitDrawElement.isLine(element);
|
|
@@ -3757,7 +4717,7 @@ const withLineTextMove = (board) => {
|
|
|
3757
4717
|
onResize: (resizeRef, resizeState) => {
|
|
3758
4718
|
const element = resizeRef.element;
|
|
3759
4719
|
if (element) {
|
|
3760
|
-
const movingPoint = resizeState.
|
|
4720
|
+
const movingPoint = resizeState.endPoint;
|
|
3761
4721
|
const points = getLinePoints(board, element);
|
|
3762
4722
|
const distance = distanceBetweenPointAndSegments(points, movingPoint);
|
|
3763
4723
|
if (distance <= movableBuffer) {
|
|
@@ -3793,14 +4753,19 @@ const withDraw = (board) => {
|
|
|
3793
4753
|
};
|
|
3794
4754
|
board.getRectangle = (element) => {
|
|
3795
4755
|
if (PlaitDrawElement.isGeometry(element)) {
|
|
3796
|
-
return getRectangleByPoints(element.points);
|
|
4756
|
+
return RectangleClient.getRectangleByPoints(element.points);
|
|
3797
4757
|
}
|
|
3798
4758
|
if (PlaitDrawElement.isLine(element)) {
|
|
3799
4759
|
const points = getLinePoints(board, element);
|
|
3800
|
-
|
|
4760
|
+
const lineTextRectangles = element.texts.map((text, index) => {
|
|
4761
|
+
const rectangle = getLineTextRectangle(board, element, index);
|
|
4762
|
+
return rectangle;
|
|
4763
|
+
});
|
|
4764
|
+
const linePointsRectangle = RectangleClient.getRectangleByPoints(points);
|
|
4765
|
+
return RectangleClient.getBoundingRectangle([linePointsRectangle, ...lineTextRectangles]);
|
|
3801
4766
|
}
|
|
3802
4767
|
if (PlaitDrawElement.isImage(element)) {
|
|
3803
|
-
return getRectangleByPoints(element.points);
|
|
4768
|
+
return RectangleClient.getRectangleByPoints(element.points);
|
|
3804
4769
|
}
|
|
3805
4770
|
return getRectangle(element);
|
|
3806
4771
|
};
|
|
@@ -3860,12 +4825,12 @@ const withDraw = (board) => {
|
|
|
3860
4825
|
});
|
|
3861
4826
|
return getRelatedFragment([...elements, ...activeLines]);
|
|
3862
4827
|
};
|
|
3863
|
-
return withLineTextMove(withLineAutoCompleteReaction(withLineText(withLineBoundReaction(withLineResize(withGeometryResize(withLineCreateByDraw(withLineAutoComplete(withGeometryCreateByDrag(withGeometryCreateByDrawing(withDrawFragment(withDrawHotkey(board))))))))))));
|
|
4828
|
+
return withDrawResize(withLineTextMove(withLineAutoCompleteReaction(withLineText(withLineBoundReaction(withLineResize(withGeometryResize(withLineCreateByDraw(withLineAutoComplete(withGeometryCreateByDrag(withGeometryCreateByDrawing(withDrawFragment(withDrawHotkey(board)))))))))))));
|
|
3864
4829
|
};
|
|
3865
4830
|
|
|
3866
4831
|
/**
|
|
3867
4832
|
* Generated bundle index. Do not edit.
|
|
3868
4833
|
*/
|
|
3869
4834
|
|
|
3870
|
-
export { BasicShapes, DEFAULT_IMAGE_WIDTH, DefaultBasicShapeProperty, DefaultConnectorProperty, DefaultDataProperty, DefaultDecisionProperty, DefaultFlowchartProperty, DefaultFlowchartPropertyMap, DefaultGeometryActiveStyle, DefaultGeometryStyle, DefaultManualInputProperty, DefaultMergeProperty, DefaultTextProperty, DrawThemeColors, DrawTransforms, FlowchartSymbols, GeometryComponent, GeometryThreshold, LineComponent, LineHandleKey, LineMarkerType, LineShape, MemorizeKey, PlaitDrawElement, PlaitGeometry, PlaitLine, Q2C, REACTION_MARGIN, ShapeDefaultSpace, StrokeStyle, WithLineAutoCompletePluginKey, alignPoints, createDefaultFlowchart, createDefaultGeometry, createGeometryElement, createLineElement, createTextElement, drawBoundMask, drawGeometry, drawLine, getAutoCompletePoints, getBasicPointers,
|
|
4835
|
+
export { BasicShapes, DEFAULT_IMAGE_WIDTH, DefaultBasicShapeProperty, DefaultConnectorProperty, DefaultDataProperty, DefaultDecisionProperty, DefaultFlowchartProperty, DefaultFlowchartPropertyMap, DefaultGeometryActiveStyle, DefaultGeometryStyle, DefaultManualInputProperty, DefaultMergeProperty, DefaultTextProperty, DrawThemeColors, DrawTransforms, FlowchartSymbols, GeometryComponent, GeometryThreshold, LineComponent, LineHandleKey, LineMarkerType, LineShape, MemorizeKey, PlaitDrawElement, PlaitGeometry, PlaitLine, Q2C, REACTION_MARGIN, ShapeDefaultSpace, StrokeStyle, WithLineAutoCompletePluginKey, alignElbowSegment, alignPoints, createDefaultFlowchart, createDefaultGeometry, createGeometryElement, createLineElement, createTextElement, drawBoundMask, drawGeometry, drawLine, drawLineArrow, getAutoCompletePoints, getBasicPointers, getCenterPointsOnPolygon, getConnectionByNearestPoint, getConnectionPoint, getCurvePoints, getDefaultFlowchartProperty, getDefaultGeometryPoints, getDefaultGeometryProperty, getDefaultTextPoints, getDrawDefaultStrokeColor, getElbowLineRouteOptions, getElbowPoints, getFillByElement, getFlowchartDefaultFill, getFlowchartPointers, getGeometryPointers, getHitConnectorPoint, getHitIndexOfAutoCompletePoint, getIndexAndDeleteCountByKeyPoint, getLineDashByElement, getLineHandleRefPair, getLineMemorizedLatest, getLinePointers, getLinePoints, getLineTextRectangle, getLines, getMemorizeKey, getMemorizedLatestByPointer, getMemorizedLatestShape, getMidKeyPoints, getMiddlePoints, getMirrorDataPoints, getNearestPoint, getNextRenderPoints, getNextSourceAndTargetPoints, getResizedPreviousAndNextPoint, getSelectedDrawElements, getSelectedGeometryElements, getSelectedImageElements, getSelectedLineElements, getSourceAndTargetRectangle, getStrokeColorByElement, getStrokeStyleByElement, getStrokeWidthByElement, getTextRectangle, getTextShapeProperty, getVectorByConnection, handleLineCreating, hasIllegalElbowPoint, insertElement, isHitDrawElement, isHitLineText, isHitPolyLine, isRectangleHitDrawElement, isTextExceedingBounds, isUpdatedHandleIndex, memorizeLatestShape, memorizeLatestText, withDraw, withLineAutoComplete };
|
|
3871
4836
|
//# sourceMappingURL=plait-draw.mjs.map
|