kritzel-stencil 0.3.24 → 0.3.26
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/dist/cjs/index.cjs.js +1 -1
- package/dist/cjs/kritzel-active-users_45.cjs.entry.js +60 -36
- package/dist/cjs/{schema.constants-D8JoUfNc.js → schema.constants-CPz_o--2.js} +271 -101
- package/dist/collection/classes/handlers/line-handle.handler.js +59 -52
- package/dist/collection/classes/handlers/move.handler.js +2 -0
- package/dist/collection/classes/handlers/rotation.handler.js +18 -6
- package/dist/collection/classes/managers/anchor.manager.js +112 -38
- package/dist/collection/classes/managers/clipboard.manager.js +7 -0
- package/dist/collection/classes/objects/selection-group.class.js +80 -5
- package/dist/collection/components/core/kritzel-engine/kritzel-engine.js +50 -34
- package/dist/collection/configs/default-engine-config.js +1 -0
- package/dist/collection/constants/version.js +1 -1
- package/dist/components/index.js +1 -1
- package/dist/components/kritzel-controls.js +1 -1
- package/dist/components/kritzel-editor.js +1 -1
- package/dist/components/kritzel-engine.js +1 -1
- package/dist/components/kritzel-settings.js +1 -1
- package/dist/components/kritzel-tool-config.js +1 -1
- package/dist/components/p-B0VgBZUT.js +9 -0
- package/dist/components/{p-Bz_zj6dQ.js → p-BAhZcKRe.js} +1 -1
- package/dist/components/{p-Dw-t1qQc.js → p-DKxt_AjI.js} +1 -1
- package/dist/components/{p-B5XGjTLd.js → p-DRncK1E6.js} +1 -1
- package/dist/components/{p-DNDt6O6A.js → p-DUo2M5uM.js} +1 -1
- package/dist/esm/index.js +2 -2
- package/dist/esm/kritzel-active-users_45.entry.js +60 -36
- package/dist/esm/{schema.constants-B3IqnD8l.js → schema.constants-C9ZX7hQv.js} +271 -101
- package/dist/stencil/index.esm.js +1 -1
- package/dist/stencil/p-71271db1.entry.js +9 -0
- package/dist/stencil/{p-B3IqnD8l.js → p-C9ZX7hQv.js} +1 -1
- package/dist/stencil/stencil.esm.js +1 -1
- package/dist/types/classes/handlers/line-handle.handler.d.ts +1 -0
- package/dist/types/classes/handlers/rotation.handler.d.ts +4 -0
- package/dist/types/classes/managers/anchor.manager.d.ts +39 -3
- package/dist/types/classes/objects/selection-group.class.d.ts +8 -1
- package/dist/types/constants/version.d.ts +1 -1
- package/dist/types/interfaces/anchor.interface.d.ts +12 -2
- package/dist/types/interfaces/engine-state.interface.d.ts +2 -1
- package/package.json +1 -1
- package/dist/components/p-on11Z8cZ.js +0 -9
- package/dist/stencil/p-63bc234f.entry.js +0 -9
|
@@ -20704,6 +20704,8 @@ class KritzelSelectionGroup extends KritzelBaseObject {
|
|
|
20704
20704
|
_lastChildPersistTime = 0;
|
|
20705
20705
|
/** Minimum interval (ms) between child Yjs persists during drag */
|
|
20706
20706
|
static CHILD_PERSIST_THROTTLE_MS = 100;
|
|
20707
|
+
/** Ignore tiny floating-point diffs when comparing refreshed bounds */
|
|
20708
|
+
static DIMENSION_EPSILON = 0.001;
|
|
20707
20709
|
/**
|
|
20708
20710
|
* Gets the array of object IDs contained in this selection group.
|
|
20709
20711
|
* @returns Array of string IDs for the selected objects
|
|
@@ -21077,6 +21079,30 @@ class KritzelSelectionGroup extends KritzelBaseObject {
|
|
|
21077
21079
|
}
|
|
21078
21080
|
});
|
|
21079
21081
|
}
|
|
21082
|
+
/**
|
|
21083
|
+
* Returns true when the selection contains a line anchored to an object
|
|
21084
|
+
* that is not part of this selection group.
|
|
21085
|
+
*/
|
|
21086
|
+
hasExternalAnchoredLineConnections(cachedObjects) {
|
|
21087
|
+
const children = cachedObjects ?? this.objects;
|
|
21088
|
+
if (children.length === 0) {
|
|
21089
|
+
return false;
|
|
21090
|
+
}
|
|
21091
|
+
const selectedIds = new Set(children.map(child => child.id));
|
|
21092
|
+
for (const child of children) {
|
|
21093
|
+
if (!KritzelClassHelper.isInstanceOf(child, 'KritzelLine')) {
|
|
21094
|
+
continue;
|
|
21095
|
+
}
|
|
21096
|
+
const line = child;
|
|
21097
|
+
if (line.startAnchor && !selectedIds.has(line.startAnchor.objectId)) {
|
|
21098
|
+
return true;
|
|
21099
|
+
}
|
|
21100
|
+
if (line.endAnchor && !selectedIds.has(line.endAnchor.objectId)) {
|
|
21101
|
+
return true;
|
|
21102
|
+
}
|
|
21103
|
+
}
|
|
21104
|
+
return false;
|
|
21105
|
+
}
|
|
21080
21106
|
/**
|
|
21081
21107
|
* Resizes the selection group and scales all contained objects proportionally.
|
|
21082
21108
|
* Uses snapshot values to avoid compounding errors during continuous drag operations.
|
|
@@ -21169,8 +21195,13 @@ class KritzelSelectionGroup extends KritzelBaseObject {
|
|
|
21169
21195
|
*/
|
|
21170
21196
|
rotate(value) {
|
|
21171
21197
|
this.rotation = value;
|
|
21172
|
-
|
|
21173
|
-
|
|
21198
|
+
// Use the snapshot center as the rotation pivot rather than the live center.
|
|
21199
|
+
// refreshObjectDimensions() below can grow the box when an anchored line is pinned
|
|
21200
|
+
// to an external object; keeping the pivot fixed prevents the children from drifting.
|
|
21201
|
+
const snapshotTotalWidth = this.snapshotWidth + this.padding * 2;
|
|
21202
|
+
const snapshotTotalHeight = this.snapshotHeight + this.padding * 2;
|
|
21203
|
+
const centerX = this.snapshotTranslateX + snapshotTotalWidth / 2 / this.scale;
|
|
21204
|
+
const centerY = this.snapshotTranslateY + snapshotTotalHeight / 2 / this.scale;
|
|
21174
21205
|
const angle = value - this.snapshotRotation;
|
|
21175
21206
|
const cos = Math.cos(angle);
|
|
21176
21207
|
const sin = Math.sin(angle);
|
|
@@ -21192,6 +21223,31 @@ class KritzelSelectionGroup extends KritzelBaseObject {
|
|
|
21192
21223
|
child.translateY = centerY + rotatedY - child.totalHeight / 2 / child.scale;
|
|
21193
21224
|
child.rotate(childCount === 1 ? value : unchangedSnapshot.rotation + angle);
|
|
21194
21225
|
}
|
|
21226
|
+
// Re-sync any anchored lines so their stored geometry follows the rotated objects
|
|
21227
|
+
// (including lines anchored to an object outside this selection), then grow the
|
|
21228
|
+
// selection box to enclose the reshaped lines. Rotation sets state.isRotating
|
|
21229
|
+
// rather than state.isDragging, so the refresh must be triggered explicitly here.
|
|
21230
|
+
const anchorTargetIds = new Set();
|
|
21231
|
+
for (const child of children) {
|
|
21232
|
+
anchorTargetIds.add(child.id);
|
|
21233
|
+
if (KritzelClassHelper.isInstanceOf(child, 'KritzelLine')) {
|
|
21234
|
+
const line = child;
|
|
21235
|
+
if (line.startAnchor)
|
|
21236
|
+
anchorTargetIds.add(line.startAnchor.objectId);
|
|
21237
|
+
if (line.endAnchor)
|
|
21238
|
+
anchorTargetIds.add(line.endAnchor.objectId);
|
|
21239
|
+
}
|
|
21240
|
+
}
|
|
21241
|
+
let hasAnchoredLines = false;
|
|
21242
|
+
for (const targetId of anchorTargetIds) {
|
|
21243
|
+
if (this._core.anchorManager.getLinesAnchoredTo(targetId).length > 0) {
|
|
21244
|
+
hasAnchoredLines = true;
|
|
21245
|
+
this._core.anchorManager.updateAnchorsForObject(targetId);
|
|
21246
|
+
}
|
|
21247
|
+
}
|
|
21248
|
+
if (hasAnchoredLines) {
|
|
21249
|
+
this.refreshObjectDimensions(children);
|
|
21250
|
+
}
|
|
21195
21251
|
});
|
|
21196
21252
|
}
|
|
21197
21253
|
/**
|
|
@@ -21221,6 +21277,16 @@ class KritzelSelectionGroup extends KritzelBaseObject {
|
|
|
21221
21277
|
* @param skipPersist If true, only updates local state without persisting to Yjs (used during undo/redo)
|
|
21222
21278
|
*/
|
|
21223
21279
|
refreshObjectDimensions(cachedObjects, skipPersist = false) {
|
|
21280
|
+
const previous = {
|
|
21281
|
+
minX: this.minX,
|
|
21282
|
+
minY: this.minY,
|
|
21283
|
+
maxX: this.maxX,
|
|
21284
|
+
maxY: this.maxY,
|
|
21285
|
+
width: this.width,
|
|
21286
|
+
height: this.height,
|
|
21287
|
+
translateX: this.translateX,
|
|
21288
|
+
translateY: this.translateY,
|
|
21289
|
+
};
|
|
21224
21290
|
const children = cachedObjects ?? this.objects;
|
|
21225
21291
|
if (children.length === 1) {
|
|
21226
21292
|
const obj = children[0];
|
|
@@ -21274,9 +21340,18 @@ class KritzelSelectionGroup extends KritzelBaseObject {
|
|
|
21274
21340
|
this.translateX = cx - (this.width / this.scale + 2 * this.padding) / 2;
|
|
21275
21341
|
this.translateY = cy - (this.height / this.scale + 2 * this.padding) / 2;
|
|
21276
21342
|
}
|
|
21277
|
-
|
|
21343
|
+
const hasChanged = Math.abs(this.minX - previous.minX) > KritzelSelectionGroup.DIMENSION_EPSILON ||
|
|
21344
|
+
Math.abs(this.minY - previous.minY) > KritzelSelectionGroup.DIMENSION_EPSILON ||
|
|
21345
|
+
Math.abs(this.maxX - previous.maxX) > KritzelSelectionGroup.DIMENSION_EPSILON ||
|
|
21346
|
+
Math.abs(this.maxY - previous.maxY) > KritzelSelectionGroup.DIMENSION_EPSILON ||
|
|
21347
|
+
Math.abs(this.width - previous.width) > KritzelSelectionGroup.DIMENSION_EPSILON ||
|
|
21348
|
+
Math.abs(this.height - previous.height) > KritzelSelectionGroup.DIMENSION_EPSILON ||
|
|
21349
|
+
Math.abs(this.translateX - previous.translateX) > KritzelSelectionGroup.DIMENSION_EPSILON ||
|
|
21350
|
+
Math.abs(this.translateY - previous.translateY) > KritzelSelectionGroup.DIMENSION_EPSILON;
|
|
21351
|
+
if (!skipPersist && hasChanged) {
|
|
21278
21352
|
this._core.store.objects.update(this);
|
|
21279
21353
|
}
|
|
21354
|
+
return hasChanged;
|
|
21280
21355
|
}
|
|
21281
21356
|
/**
|
|
21282
21357
|
* Calculates the horizontal offset from the group's center to an object's center using snapshot data.
|
|
@@ -21286,7 +21361,7 @@ class KritzelSelectionGroup extends KritzelBaseObject {
|
|
|
21286
21361
|
*/
|
|
21287
21362
|
getOffsetXToCenterFromSnapshot(snapshot) {
|
|
21288
21363
|
const objCenterX = snapshot.translateX + snapshot.totalWidth / snapshot.scale / 2;
|
|
21289
|
-
const groupCenterX = this.
|
|
21364
|
+
const groupCenterX = this.snapshotTranslateX + (this.snapshotWidth + this.padding * 2) / this.scale / 2;
|
|
21290
21365
|
return objCenterX - groupCenterX;
|
|
21291
21366
|
}
|
|
21292
21367
|
/**
|
|
@@ -21297,7 +21372,7 @@ class KritzelSelectionGroup extends KritzelBaseObject {
|
|
|
21297
21372
|
*/
|
|
21298
21373
|
getOffsetYToCenterFromSnapshot(snapshot) {
|
|
21299
21374
|
const objCenterY = snapshot.translateY + snapshot.totalHeight / snapshot.scale / 2;
|
|
21300
|
-
const groupCenterY = this.
|
|
21375
|
+
const groupCenterY = this.snapshotTranslateY + (this.snapshotHeight + this.padding * 2) / this.scale / 2;
|
|
21301
21376
|
return objCenterY - groupCenterY;
|
|
21302
21377
|
}
|
|
21303
21378
|
/**
|
|
@@ -22670,6 +22745,7 @@ class KritzelMoveHandler extends KritzelBaseHandler {
|
|
|
22670
22745
|
this._core.store.state.isDragging = false;
|
|
22671
22746
|
if (this.hasMoved) {
|
|
22672
22747
|
this._core.store.selectionGroup.persistChildren();
|
|
22748
|
+
this._core.store.selectionGroup.refreshObjectDimensions(undefined, false);
|
|
22673
22749
|
this._core.store.selectionGroup.update();
|
|
22674
22750
|
this._core.engine.emitObjectsChange();
|
|
22675
22751
|
this._core.store.state.hasObjectsChanged = true;
|
|
@@ -22682,6 +22758,7 @@ class KritzelMoveHandler extends KritzelBaseHandler {
|
|
|
22682
22758
|
this._core.store.state.isDragging = false;
|
|
22683
22759
|
if (this.hasMoved) {
|
|
22684
22760
|
this._core.store.selectionGroup.persistChildren();
|
|
22761
|
+
this._core.store.selectionGroup.refreshObjectDimensions(undefined, false);
|
|
22685
22762
|
this._core.store.selectionGroup.update();
|
|
22686
22763
|
this._core.engine.emitObjectsChange();
|
|
22687
22764
|
this._core.store.state.hasObjectsChanged = true;
|
|
@@ -23074,6 +23151,10 @@ class KritzelRotationHandler extends KritzelBaseHandler {
|
|
|
23074
23151
|
rotation = 0;
|
|
23075
23152
|
/** The rotation value of the selection group before the current rotation operation began. */
|
|
23076
23153
|
initialSelectionGroupRotation = 0;
|
|
23154
|
+
/** The X coordinate of the rotation pivot (group center) captured when rotation starts, in world space. */
|
|
23155
|
+
initialCenterX = 0;
|
|
23156
|
+
/** The Y coordinate of the rotation pivot (group center) captured when rotation starts, in world space. */
|
|
23157
|
+
initialCenterY = 0;
|
|
23077
23158
|
/**
|
|
23078
23159
|
* Creates an instance of KritzelRotationHandler.
|
|
23079
23160
|
* @param core - The KritzelCore instance that provides access to the store, engine, and other core functionalities.
|
|
@@ -23088,6 +23169,8 @@ class KritzelRotationHandler extends KritzelBaseHandler {
|
|
|
23088
23169
|
reset() {
|
|
23089
23170
|
this.initialRotation = 0;
|
|
23090
23171
|
this.rotation = 0;
|
|
23172
|
+
this.initialCenterX = 0;
|
|
23173
|
+
this.initialCenterY = 0;
|
|
23091
23174
|
}
|
|
23092
23175
|
/**
|
|
23093
23176
|
* Handles the pointer down event to initiate a rotation operation.
|
|
@@ -23107,6 +23190,10 @@ class KritzelRotationHandler extends KritzelBaseHandler {
|
|
|
23107
23190
|
const objectScale = selectionGroup.scale || 1;
|
|
23108
23191
|
const centerX = selectionGroup.translateX + selectionGroup.width / 2 / objectScale;
|
|
23109
23192
|
const centerY = selectionGroup.translateY + selectionGroup.height / 2 / objectScale;
|
|
23193
|
+
// Capture the pivot once so it stays fixed even if the bounding box grows
|
|
23194
|
+
// mid-rotation (e.g. a line anchored to an object outside the selection).
|
|
23195
|
+
this.initialCenterX = centerX;
|
|
23196
|
+
this.initialCenterY = centerY;
|
|
23110
23197
|
const cursorX = (clientX - this._core.store.state.translateX) / this._core.store.state.scale;
|
|
23111
23198
|
const cursorY = (clientY - this._core.store.state.translateY) / this._core.store.state.scale;
|
|
23112
23199
|
this.initialSelectionGroupRotation = selectionGroup.rotation;
|
|
@@ -23130,6 +23217,10 @@ class KritzelRotationHandler extends KritzelBaseHandler {
|
|
|
23130
23217
|
const objectScale = selectionGroup.scale || 1;
|
|
23131
23218
|
const centerX = selectionGroup.translateX + selectionGroup.width / 2 / objectScale;
|
|
23132
23219
|
const centerY = selectionGroup.translateY + selectionGroup.height / 2 / objectScale;
|
|
23220
|
+
// Capture the pivot once so it stays fixed even if the bounding box grows
|
|
23221
|
+
// mid-rotation (e.g. a line anchored to an object outside the selection).
|
|
23222
|
+
this.initialCenterX = centerX;
|
|
23223
|
+
this.initialCenterY = centerY;
|
|
23133
23224
|
const cursorX = (clientX - this._core.store.state.translateX) / this._core.store.state.scale;
|
|
23134
23225
|
const cursorY = (clientY - this._core.store.state.translateY) / this._core.store.state.scale;
|
|
23135
23226
|
this.initialSelectionGroupRotation = selectionGroup.rotation;
|
|
@@ -23154,9 +23245,8 @@ class KritzelRotationHandler extends KritzelBaseHandler {
|
|
|
23154
23245
|
if (this._core.store.state.isRotating && selectionGroup) {
|
|
23155
23246
|
const clientX = event.clientX - this._core.store.offsetX;
|
|
23156
23247
|
const clientY = event.clientY - this._core.store.offsetY;
|
|
23157
|
-
const
|
|
23158
|
-
const
|
|
23159
|
-
const groupCenterY = selectionGroup.translateY + selectionGroup.height / 2 / objectScale;
|
|
23248
|
+
const groupCenterX = this.initialCenterX;
|
|
23249
|
+
const groupCenterY = this.initialCenterY;
|
|
23160
23250
|
const cursorX = (clientX - this._core.store.state.translateX) / this._core.store.state.scale;
|
|
23161
23251
|
const cursorY = (clientY - this._core.store.state.translateY) / this._core.store.state.scale;
|
|
23162
23252
|
const currentRotation = Math.atan2(groupCenterY - cursorY, groupCenterX - cursorX);
|
|
@@ -23174,9 +23264,8 @@ class KritzelRotationHandler extends KritzelBaseHandler {
|
|
|
23174
23264
|
if (this._core.store.state.isRotating && selectionGroup) {
|
|
23175
23265
|
const clientX = Math.round(firstTouch.clientX - this._core.store.offsetX);
|
|
23176
23266
|
const clientY = Math.round(firstTouch.clientY - this._core.store.offsetY);
|
|
23177
|
-
const
|
|
23178
|
-
const
|
|
23179
|
-
const groupCenterY = selectionGroup.translateY + selectionGroup.height / 2 / objectScale;
|
|
23267
|
+
const groupCenterX = this.initialCenterX;
|
|
23268
|
+
const groupCenterY = this.initialCenterY;
|
|
23180
23269
|
const cursorX = (clientX - this._core.store.state.translateX) / this._core.store.state.scale;
|
|
23181
23270
|
const cursorY = (clientY - this._core.store.state.translateY) / this._core.store.state.scale;
|
|
23182
23271
|
const currentRotation = Math.atan2(groupCenterY - cursorY, groupCenterX - cursorX);
|
|
@@ -23855,6 +23944,7 @@ class KritzelLineHandleHandler extends KritzelBaseHandler {
|
|
|
23855
23944
|
this.hasMoved = false;
|
|
23856
23945
|
this.currentSnapTarget = null;
|
|
23857
23946
|
this._core.anchorManager.clearSnapCandidate();
|
|
23947
|
+
this._core.anchorManager.clearSnapPreviewCandidate();
|
|
23858
23948
|
}
|
|
23859
23949
|
/**
|
|
23860
23950
|
* Handles pointer down events to initiate line handle dragging.
|
|
@@ -23957,15 +24047,19 @@ class KritzelLineHandleHandler extends KritzelBaseHandler {
|
|
|
23957
24047
|
const worldY = (clientY - this._core.store.state.translateY) / viewportScale;
|
|
23958
24048
|
// Get other endpoint's anchor to prevent anchoring both to same object
|
|
23959
24049
|
const otherAnchorId = line.endAnchor?.objectId;
|
|
24050
|
+
const previewTarget = this._core.anchorManager.findSnapPreviewTarget(worldX, worldY, line.id, otherAnchorId);
|
|
23960
24051
|
// Check for snap target
|
|
23961
|
-
const snapTarget = this._core.anchorManager.findSnapTarget(worldX, worldY, line.id, otherAnchorId);
|
|
24052
|
+
const snapTarget = this._core.anchorManager.findSnapTarget(worldX, worldY, line.id, otherAnchorId, event.pointerType);
|
|
23962
24053
|
this.currentSnapTarget = snapTarget;
|
|
24054
|
+
const otherEndpointWorld = this.lineLocalToWorld(line, this.initialEndX, this.initialEndY);
|
|
24055
|
+
let controlWorld = undefined;
|
|
24056
|
+
if (line.controlX !== undefined && line.controlY !== undefined) {
|
|
24057
|
+
controlWorld = this.lineLocalToWorld(line, line.controlX, line.controlY);
|
|
24058
|
+
}
|
|
23963
24059
|
if (snapTarget) {
|
|
23964
24060
|
// Snap to target center - convert world coords to local
|
|
23965
24061
|
const localCoords = this.worldToLineLocal(line, snapTarget.centerX, snapTarget.centerY);
|
|
23966
24062
|
this.updateLineEndpoint(line, localCoords.x, localCoords.y, this.initialEndX, this.initialEndY);
|
|
23967
|
-
// Get the other endpoint's world position for edge intersection calculation
|
|
23968
|
-
const otherEndpointWorld = this.lineLocalToWorld(line, this.initialEndX, this.initialEndY);
|
|
23969
24063
|
// Calculate edge intersection point using AnchorManager to support curves
|
|
23970
24064
|
const targetObject = this._core.store.allNonSelectionObjects.find(obj => obj.id === snapTarget.objectId);
|
|
23971
24065
|
let edgeX;
|
|
@@ -23979,30 +24073,16 @@ class KritzelLineHandleHandler extends KritzelBaseHandler {
|
|
|
23979
24073
|
t = clipInfo.t;
|
|
23980
24074
|
}
|
|
23981
24075
|
}
|
|
23982
|
-
// Calculate control point in world coordinates if it exists
|
|
23983
|
-
let controlWorld = undefined;
|
|
23984
|
-
if (line.controlX !== undefined && line.controlY !== undefined) {
|
|
23985
|
-
controlWorld = this.lineLocalToWorld(line, line.controlX, line.controlY);
|
|
23986
|
-
}
|
|
23987
24076
|
// Update snap indicator
|
|
23988
|
-
this._core.anchorManager.
|
|
23989
|
-
|
|
23990
|
-
|
|
23991
|
-
|
|
23992
|
-
|
|
23993
|
-
|
|
23994
|
-
|
|
23995
|
-
|
|
23996
|
-
|
|
23997
|
-
t,
|
|
23998
|
-
edgeX,
|
|
23999
|
-
edgeY,
|
|
24000
|
-
lineStroke: KritzelColorHelper.resolveThemeColor(line.stroke),
|
|
24001
|
-
lineStrokeWidth: line.strokeWidth / line.scale,
|
|
24002
|
-
arrowOffset: line.hasStartArrow ? line.getArrowSize('start') / line.scale : undefined,
|
|
24003
|
-
arrowStyle: line.hasStartArrow ? line.arrows?.start?.style : undefined,
|
|
24004
|
-
arrowFill: line.hasStartArrow ? line.getArrowFill('start') : undefined,
|
|
24005
|
-
});
|
|
24077
|
+
this._core.anchorManager.setSnapPreviewCandidate(null);
|
|
24078
|
+
this._core.anchorManager.setSnapCandidate(this.createIndicatorCandidate(line, 'start', snapTarget, otherEndpointWorld, controlWorld, { edgeX, edgeY, t }));
|
|
24079
|
+
}
|
|
24080
|
+
else if (previewTarget) {
|
|
24081
|
+
const newStartX = this.initialStartX + localDx;
|
|
24082
|
+
const newStartY = this.initialStartY + localDy;
|
|
24083
|
+
this.updateLineEndpoint(line, newStartX, newStartY, this.initialEndX, this.initialEndY);
|
|
24084
|
+
this._core.anchorManager.clearSnapCandidate();
|
|
24085
|
+
this._core.anchorManager.setSnapPreviewCandidate(this.createIndicatorCandidate(line, 'start', previewTarget, otherEndpointWorld, controlWorld));
|
|
24006
24086
|
}
|
|
24007
24087
|
else {
|
|
24008
24088
|
// No snap - use regular position
|
|
@@ -24011,6 +24091,7 @@ class KritzelLineHandleHandler extends KritzelBaseHandler {
|
|
|
24011
24091
|
this.updateLineEndpoint(line, newStartX, newStartY, this.initialEndX, this.initialEndY);
|
|
24012
24092
|
// Clear snap indicator
|
|
24013
24093
|
this._core.anchorManager.clearSnapCandidate();
|
|
24094
|
+
this._core.anchorManager.clearSnapPreviewCandidate();
|
|
24014
24095
|
}
|
|
24015
24096
|
}
|
|
24016
24097
|
else if (handleType === 'end') {
|
|
@@ -24019,15 +24100,19 @@ class KritzelLineHandleHandler extends KritzelBaseHandler {
|
|
|
24019
24100
|
const worldY = (clientY - this._core.store.state.translateY) / viewportScale;
|
|
24020
24101
|
// Get other endpoint's anchor to prevent anchoring both to same object
|
|
24021
24102
|
const otherAnchorId = line.startAnchor?.objectId;
|
|
24103
|
+
const previewTarget = this._core.anchorManager.findSnapPreviewTarget(worldX, worldY, line.id, otherAnchorId);
|
|
24022
24104
|
// Check for snap target
|
|
24023
|
-
const snapTarget = this._core.anchorManager.findSnapTarget(worldX, worldY, line.id, otherAnchorId);
|
|
24105
|
+
const snapTarget = this._core.anchorManager.findSnapTarget(worldX, worldY, line.id, otherAnchorId, event.pointerType);
|
|
24024
24106
|
this.currentSnapTarget = snapTarget;
|
|
24107
|
+
const otherEndpointWorld = this.lineLocalToWorld(line, this.initialStartX, this.initialStartY);
|
|
24108
|
+
let controlWorld = undefined;
|
|
24109
|
+
if (line.controlX !== undefined && line.controlY !== undefined) {
|
|
24110
|
+
controlWorld = this.lineLocalToWorld(line, line.controlX, line.controlY);
|
|
24111
|
+
}
|
|
24025
24112
|
if (snapTarget) {
|
|
24026
24113
|
// Snap to target center - convert world coords to local
|
|
24027
24114
|
const localCoords = this.worldToLineLocal(line, snapTarget.centerX, snapTarget.centerY);
|
|
24028
24115
|
this.updateLineEndpoint(line, this.initialStartX, this.initialStartY, localCoords.x, localCoords.y);
|
|
24029
|
-
// Get the other endpoint's world position for edge intersection calculation
|
|
24030
|
-
const otherEndpointWorld = this.lineLocalToWorld(line, this.initialStartX, this.initialStartY);
|
|
24031
24116
|
// Calculate edge intersection point using AnchorManager to support curves
|
|
24032
24117
|
const targetObject = this._core.store.allNonSelectionObjects.find(obj => obj.id === snapTarget.objectId);
|
|
24033
24118
|
let edgeX;
|
|
@@ -24041,30 +24126,16 @@ class KritzelLineHandleHandler extends KritzelBaseHandler {
|
|
|
24041
24126
|
t = clipInfo.t;
|
|
24042
24127
|
}
|
|
24043
24128
|
}
|
|
24044
|
-
// Calculate control point in world coordinates if it exists
|
|
24045
|
-
let controlWorld = undefined;
|
|
24046
|
-
if (line.controlX !== undefined && line.controlY !== undefined) {
|
|
24047
|
-
controlWorld = this.lineLocalToWorld(line, line.controlX, line.controlY);
|
|
24048
|
-
}
|
|
24049
24129
|
// Update snap indicator
|
|
24050
|
-
this._core.anchorManager.
|
|
24051
|
-
|
|
24052
|
-
|
|
24053
|
-
|
|
24054
|
-
|
|
24055
|
-
|
|
24056
|
-
|
|
24057
|
-
|
|
24058
|
-
|
|
24059
|
-
t,
|
|
24060
|
-
edgeX,
|
|
24061
|
-
edgeY,
|
|
24062
|
-
lineStroke: KritzelColorHelper.resolveThemeColor(line.stroke),
|
|
24063
|
-
lineStrokeWidth: line.strokeWidth / line.scale,
|
|
24064
|
-
arrowOffset: line.hasEndArrow ? line.getArrowSize('end') / line.scale : undefined,
|
|
24065
|
-
arrowStyle: line.hasEndArrow ? line.arrows?.end?.style : undefined,
|
|
24066
|
-
arrowFill: line.hasEndArrow ? line.getArrowFill('end') : undefined,
|
|
24067
|
-
});
|
|
24130
|
+
this._core.anchorManager.setSnapPreviewCandidate(null);
|
|
24131
|
+
this._core.anchorManager.setSnapCandidate(this.createIndicatorCandidate(line, 'end', snapTarget, otherEndpointWorld, controlWorld, { edgeX, edgeY, t }));
|
|
24132
|
+
}
|
|
24133
|
+
else if (previewTarget) {
|
|
24134
|
+
const newEndX = this.initialEndX + localDx;
|
|
24135
|
+
const newEndY = this.initialEndY + localDy;
|
|
24136
|
+
this.updateLineEndpoint(line, this.initialStartX, this.initialStartY, newEndX, newEndY);
|
|
24137
|
+
this._core.anchorManager.clearSnapCandidate();
|
|
24138
|
+
this._core.anchorManager.setSnapPreviewCandidate(this.createIndicatorCandidate(line, 'end', previewTarget, otherEndpointWorld, controlWorld));
|
|
24068
24139
|
}
|
|
24069
24140
|
else {
|
|
24070
24141
|
// No snap - use regular position
|
|
@@ -24073,9 +24144,12 @@ class KritzelLineHandleHandler extends KritzelBaseHandler {
|
|
|
24073
24144
|
this.updateLineEndpoint(line, this.initialStartX, this.initialStartY, newEndX, newEndY);
|
|
24074
24145
|
// Clear snap indicator
|
|
24075
24146
|
this._core.anchorManager.clearSnapCandidate();
|
|
24147
|
+
this._core.anchorManager.clearSnapPreviewCandidate();
|
|
24076
24148
|
}
|
|
24077
24149
|
}
|
|
24078
24150
|
else if (handleType === 'center') {
|
|
24151
|
+
this._core.anchorManager.clearSnapPreviewCandidate();
|
|
24152
|
+
this._core.anchorManager.clearSnapCandidate();
|
|
24079
24153
|
const startControlX = this.initialControlX ?? (this.initialStartX + this.initialEndX) / 2;
|
|
24080
24154
|
const startControlY = this.initialControlY ?? (this.initialStartY + this.initialEndY) / 2;
|
|
24081
24155
|
const newControlX = startControlX + localDx * 2;
|
|
@@ -24271,6 +24345,28 @@ class KritzelLineHandleHandler extends KritzelBaseHandler {
|
|
|
24271
24345
|
}
|
|
24272
24346
|
return null;
|
|
24273
24347
|
}
|
|
24348
|
+
createIndicatorCandidate(line, endpoint, target, otherEndpointWorld, controlWorld, clipInfo) {
|
|
24349
|
+
return {
|
|
24350
|
+
objectId: target.objectId,
|
|
24351
|
+
endpoint,
|
|
24352
|
+
centerX: target.centerX,
|
|
24353
|
+
centerY: target.centerY,
|
|
24354
|
+
lineEndpointX: otherEndpointWorld.x,
|
|
24355
|
+
lineEndpointY: otherEndpointWorld.y,
|
|
24356
|
+
controlX: controlWorld?.x,
|
|
24357
|
+
controlY: controlWorld?.y,
|
|
24358
|
+
t: clipInfo?.t,
|
|
24359
|
+
edgeX: clipInfo?.edgeX,
|
|
24360
|
+
edgeY: clipInfo?.edgeY,
|
|
24361
|
+
lineStroke: KritzelColorHelper.resolveThemeColor(line.stroke),
|
|
24362
|
+
lineStrokeWidth: line.strokeWidth / line.scale,
|
|
24363
|
+
arrowOffset: endpoint === 'start'
|
|
24364
|
+
? (line.hasStartArrow ? line.getArrowSize('start') / line.scale : undefined)
|
|
24365
|
+
: (line.hasEndArrow ? line.getArrowSize('end') / line.scale : undefined),
|
|
24366
|
+
arrowStyle: endpoint === 'start' ? line.arrows?.start?.style : line.arrows?.end?.style,
|
|
24367
|
+
arrowFill: endpoint === 'start' ? line.getArrowFill('start') : line.getArrowFill('end'),
|
|
24368
|
+
};
|
|
24369
|
+
}
|
|
24274
24370
|
}
|
|
24275
24371
|
|
|
24276
24372
|
/**
|
|
@@ -27912,6 +28008,14 @@ class KritzelAnchorManager {
|
|
|
27912
28008
|
}
|
|
27913
28009
|
this.snapEndpointToObject(line, entry.endpoint, objectId);
|
|
27914
28010
|
}
|
|
28011
|
+
const selectionGroup = this._core.store.selectionGroup;
|
|
28012
|
+
if (!selectionGroup || !this._core.store.state.isDragging) {
|
|
28013
|
+
return;
|
|
28014
|
+
}
|
|
28015
|
+
if (selectionGroup.hasExternalAnchoredLineConnections()) {
|
|
28016
|
+
// Keep the local drag overlay aligned with reshaped anchored lines without spamming Yjs.
|
|
28017
|
+
selectionGroup.refreshObjectDimensions(undefined, true);
|
|
28018
|
+
}
|
|
27915
28019
|
}
|
|
27916
28020
|
/**
|
|
27917
28021
|
* Snaps a line endpoint to an object's center position.
|
|
@@ -27968,15 +28072,35 @@ class KritzelAnchorManager {
|
|
|
27968
28072
|
// Snap Detection
|
|
27969
28073
|
// ============================================
|
|
27970
28074
|
/**
|
|
27971
|
-
* Finds
|
|
28075
|
+
* Finds an object that can be snapped to when the cursor is sufficiently close to its center.
|
|
28076
|
+
* Snap is only triggered when the cursor is inside the object's bounds AND within 1/5 of the
|
|
28077
|
+
* shorter side from the center, so snapping activates only as the user approaches the center
|
|
28078
|
+
* and releases quickly as they move away.
|
|
27972
28079
|
* Returns null if no suitable snap target is found.
|
|
27973
28080
|
*
|
|
27974
28081
|
* @param worldX - X coordinate in world space
|
|
27975
28082
|
* @param worldY - Y coordinate in world space
|
|
27976
28083
|
* @param excludeLineId - ID of the line being edited (to exclude from snap targets)
|
|
27977
28084
|
* @param otherEndpointAnchorId - ID of object anchored to the other endpoint (to prevent same-object anchoring)
|
|
28085
|
+
* @param pointerType - The pointer type driving the drag interaction.
|
|
28086
|
+
*/
|
|
28087
|
+
findSnapTarget(worldX, worldY, excludeLineId, otherEndpointAnchorId, pointerType = 'mouse') {
|
|
28088
|
+
return this.findBestSnapTarget(worldX, worldY, excludeLineId, otherEndpointAnchorId, object => {
|
|
28089
|
+
const snapRadius = this.getSnapRadius(object, pointerType);
|
|
28090
|
+
const dx = worldX - object.centerX;
|
|
28091
|
+
const dy = worldY - object.centerY;
|
|
28092
|
+
const distanceToCenter = Math.sqrt(dx * dx + dy * dy);
|
|
28093
|
+
return distanceToCenter <= snapRadius;
|
|
28094
|
+
});
|
|
28095
|
+
}
|
|
28096
|
+
/**
|
|
28097
|
+
* Finds the topmost anchorable object currently containing the dragged endpoint.
|
|
28098
|
+
* Used to drive preview UI before an actual snap engages.
|
|
27978
28099
|
*/
|
|
27979
|
-
|
|
28100
|
+
findSnapPreviewTarget(worldX, worldY, excludeLineId, otherEndpointAnchorId) {
|
|
28101
|
+
return this.findBestSnapTarget(worldX, worldY, excludeLineId, otherEndpointAnchorId, () => true);
|
|
28102
|
+
}
|
|
28103
|
+
findBestSnapTarget(worldX, worldY, excludeLineId, otherEndpointAnchorId, predicate) {
|
|
27980
28104
|
let bestTarget = null;
|
|
27981
28105
|
let highestZIndex = -Infinity;
|
|
27982
28106
|
const objects = this._core.store.allNonSelectionObjects;
|
|
@@ -27996,20 +28120,30 @@ class KritzelAnchorManager {
|
|
|
27996
28120
|
// Check if point is inside the object's rotated polygon
|
|
27997
28121
|
const polygon = object.rotatedPolygon;
|
|
27998
28122
|
const points = [polygon.topLeft, polygon.topRight, polygon.bottomRight, polygon.bottomLeft];
|
|
27999
|
-
if (KritzelGeometryHelper.isPointInPolygon({ x: worldX, y: worldY }, points)) {
|
|
28000
|
-
|
|
28001
|
-
|
|
28002
|
-
|
|
28003
|
-
|
|
28004
|
-
|
|
28005
|
-
|
|
28006
|
-
|
|
28007
|
-
|
|
28008
|
-
|
|
28123
|
+
if (!KritzelGeometryHelper.isPointInPolygon({ x: worldX, y: worldY }, points)) {
|
|
28124
|
+
continue;
|
|
28125
|
+
}
|
|
28126
|
+
if (!predicate(object)) {
|
|
28127
|
+
continue;
|
|
28128
|
+
}
|
|
28129
|
+
if (object.zIndex > highestZIndex) {
|
|
28130
|
+
highestZIndex = object.zIndex;
|
|
28131
|
+
bestTarget = {
|
|
28132
|
+
objectId: object.id,
|
|
28133
|
+
centerX: object.centerX,
|
|
28134
|
+
centerY: object.centerY,
|
|
28135
|
+
};
|
|
28009
28136
|
}
|
|
28010
28137
|
}
|
|
28011
28138
|
return bestTarget;
|
|
28012
28139
|
}
|
|
28140
|
+
getSnapRadius(object, pointerType) {
|
|
28141
|
+
const baseRadius = Math.min(object.width, object.height) / 5;
|
|
28142
|
+
if (pointerType === 'touch' || pointerType === 'pen') {
|
|
28143
|
+
return Math.max(baseRadius * 1.75, 24 / this.getSafeScale(this._core.store.state.scale));
|
|
28144
|
+
}
|
|
28145
|
+
return baseRadius;
|
|
28146
|
+
}
|
|
28013
28147
|
/**
|
|
28014
28148
|
* Sets the current snap candidate for visual feedback during line endpoint dragging.
|
|
28015
28149
|
* Updates the store state and triggers a rerender to show the snap indicator.
|
|
@@ -28020,6 +28154,17 @@ class KritzelAnchorManager {
|
|
|
28020
28154
|
this._core.store.state.snapCandidate = candidate;
|
|
28021
28155
|
this._core.rerender();
|
|
28022
28156
|
}
|
|
28157
|
+
/**
|
|
28158
|
+
* Sets the current snap preview candidate for early visual feedback during line dragging.
|
|
28159
|
+
* Preview state must remain separate from active snap state so line clipping and anchor
|
|
28160
|
+
* commit semantics only react to actual snaps.
|
|
28161
|
+
*
|
|
28162
|
+
* @param candidate - The preview candidate object, or null to clear.
|
|
28163
|
+
*/
|
|
28164
|
+
setSnapPreviewCandidate(candidate) {
|
|
28165
|
+
this._core.store.state.snapPreviewCandidate = candidate;
|
|
28166
|
+
this._core.rerender();
|
|
28167
|
+
}
|
|
28023
28168
|
/**
|
|
28024
28169
|
* Gets the current snap candidate from the store state.
|
|
28025
28170
|
*
|
|
@@ -28028,6 +28173,14 @@ class KritzelAnchorManager {
|
|
|
28028
28173
|
getSnapCandidate() {
|
|
28029
28174
|
return this._core.store.state.snapCandidate ?? null;
|
|
28030
28175
|
}
|
|
28176
|
+
/**
|
|
28177
|
+
* Gets the current snap preview candidate from the store state.
|
|
28178
|
+
*
|
|
28179
|
+
* @returns The current preview candidate if one exists, null otherwise.
|
|
28180
|
+
*/
|
|
28181
|
+
getSnapPreviewCandidate() {
|
|
28182
|
+
return this._core.store.state.snapPreviewCandidate ?? null;
|
|
28183
|
+
}
|
|
28031
28184
|
/**
|
|
28032
28185
|
* Clears the snap candidate from the store state and triggers a rerender.
|
|
28033
28186
|
* Should be called when snapping is cancelled or completed.
|
|
@@ -28036,6 +28189,13 @@ class KritzelAnchorManager {
|
|
|
28036
28189
|
this._core.store.state.snapCandidate = null;
|
|
28037
28190
|
this._core.rerender();
|
|
28038
28191
|
}
|
|
28192
|
+
/**
|
|
28193
|
+
* Clears the snap preview candidate from the store state and triggers a rerender.
|
|
28194
|
+
*/
|
|
28195
|
+
clearSnapPreviewCandidate() {
|
|
28196
|
+
this._core.store.state.snapPreviewCandidate = null;
|
|
28197
|
+
this._core.rerender();
|
|
28198
|
+
}
|
|
28039
28199
|
// ============================================
|
|
28040
28200
|
// Render Data
|
|
28041
28201
|
// ============================================
|
|
@@ -28089,33 +28249,43 @@ class KritzelAnchorManager {
|
|
|
28089
28249
|
* or null if there's no active snap candidate.
|
|
28090
28250
|
*/
|
|
28091
28251
|
getSnapIndicatorRenderData() {
|
|
28092
|
-
|
|
28093
|
-
|
|
28252
|
+
return this.getIndicatorRenderDataForCandidate(this.getSnapCandidate());
|
|
28253
|
+
}
|
|
28254
|
+
/**
|
|
28255
|
+
* Gets render data for snap preview visualization.
|
|
28256
|
+
*
|
|
28257
|
+
* @returns SnapIndicatorRenderData for the active preview candidate, or null if none exists.
|
|
28258
|
+
*/
|
|
28259
|
+
getSnapPreviewRenderData() {
|
|
28260
|
+
return this.getIndicatorRenderDataForCandidate(this.getSnapPreviewCandidate());
|
|
28261
|
+
}
|
|
28262
|
+
getIndicatorRenderDataForCandidate(candidate) {
|
|
28263
|
+
if (!candidate)
|
|
28094
28264
|
return null;
|
|
28095
|
-
if (!this.areFiniteNumbers(
|
|
28265
|
+
if (!this.areFiniteNumbers(candidate.centerX, candidate.centerY, candidate.lineEndpointX, candidate.lineEndpointY)) {
|
|
28096
28266
|
return null;
|
|
28097
28267
|
}
|
|
28098
28268
|
const scale = this.getSafeScale(this._core.store.state.scale);
|
|
28099
28269
|
const indicatorRadius = 8 / scale;
|
|
28100
28270
|
const indicatorStrokeWidth = `${2 / scale}`;
|
|
28101
|
-
const lineStrokeWidthNum = this.areFiniteNumbers(
|
|
28102
|
-
?
|
|
28271
|
+
const lineStrokeWidthNum = this.areFiniteNumbers(candidate.lineStrokeWidth) && candidate.lineStrokeWidth > 0
|
|
28272
|
+
? candidate.lineStrokeWidth
|
|
28103
28273
|
: (4 / scale);
|
|
28104
28274
|
const lineStrokeWidth = `${lineStrokeWidthNum}`;
|
|
28105
28275
|
const dashLength = Math.max(lineStrokeWidthNum * 2, 4 / scale);
|
|
28106
28276
|
const dashArray = `${dashLength} ${dashLength}`;
|
|
28107
|
-
const lineStroke =
|
|
28108
|
-
let edgeX = this.areFiniteNumbers(
|
|
28109
|
-
let edgeY = this.areFiniteNumbers(
|
|
28277
|
+
const lineStroke = candidate.lineStroke || '#000000';
|
|
28278
|
+
let edgeX = this.areFiniteNumbers(candidate.edgeX) ? candidate.edgeX : undefined;
|
|
28279
|
+
let edgeY = this.areFiniteNumbers(candidate.edgeY) ? candidate.edgeY : undefined;
|
|
28110
28280
|
let solidLineEndX = edgeX;
|
|
28111
28281
|
let solidLineEndY = edgeY;
|
|
28112
28282
|
let arrowPoints;
|
|
28113
|
-
const arrowOffset = this.areFiniteNumbers(
|
|
28114
|
-
?
|
|
28283
|
+
const arrowOffset = this.areFiniteNumbers(candidate.arrowOffset) && candidate.arrowOffset > 0
|
|
28284
|
+
? candidate.arrowOffset
|
|
28115
28285
|
: undefined;
|
|
28116
28286
|
if (arrowOffset !== undefined && edgeX !== undefined && edgeY !== undefined) {
|
|
28117
|
-
const dx =
|
|
28118
|
-
const dy =
|
|
28287
|
+
const dx = candidate.lineEndpointX - edgeX;
|
|
28288
|
+
const dy = candidate.lineEndpointY - edgeY;
|
|
28119
28289
|
const length = Math.sqrt(dx * dx + dy * dy);
|
|
28120
28290
|
if (length > arrowOffset) {
|
|
28121
28291
|
solidLineEndX = edgeX + (dx / length) * arrowOffset;
|
|
@@ -28123,8 +28293,8 @@ class KritzelAnchorManager {
|
|
|
28123
28293
|
}
|
|
28124
28294
|
// Calculate arrow head points
|
|
28125
28295
|
// Direction from line endpoint to edge (arrow direction)
|
|
28126
|
-
const arrowDx = edgeX -
|
|
28127
|
-
const arrowDy = edgeY -
|
|
28296
|
+
const arrowDx = edgeX - candidate.lineEndpointX;
|
|
28297
|
+
const arrowDy = edgeY - candidate.lineEndpointY;
|
|
28128
28298
|
const arrowLengthTotal = Math.sqrt(arrowDx * arrowDx + arrowDy * arrowDy);
|
|
28129
28299
|
if (arrowLengthTotal > 0) {
|
|
28130
28300
|
const ux = arrowDx / arrowLengthTotal;
|
|
@@ -28155,15 +28325,15 @@ class KritzelAnchorManager {
|
|
|
28155
28325
|
return null;
|
|
28156
28326
|
}
|
|
28157
28327
|
const snapLinePath = (() => {
|
|
28158
|
-
if (
|
|
28159
|
-
|
|
28160
|
-
|
|
28161
|
-
this.areFiniteNumbers(
|
|
28162
|
-
const startT =
|
|
28328
|
+
if (candidate.controlX !== undefined &&
|
|
28329
|
+
candidate.controlY !== undefined &&
|
|
28330
|
+
candidate.t !== undefined &&
|
|
28331
|
+
this.areFiniteNumbers(candidate.controlX, candidate.controlY, candidate.t)) {
|
|
28332
|
+
const startT = candidate.endpoint === 'start' ? 1 - candidate.t : candidate.t;
|
|
28163
28333
|
// Ensure meaningful range
|
|
28164
28334
|
if (startT >= 1)
|
|
28165
28335
|
return undefined;
|
|
28166
|
-
const segment = this.extractQuadraticSegment({ x:
|
|
28336
|
+
const segment = this.extractQuadraticSegment({ x: candidate.lineEndpointX, y: candidate.lineEndpointY }, { x: candidate.controlX, y: candidate.controlY }, { x: candidate.centerX, y: candidate.centerY }, startT, 1);
|
|
28167
28337
|
if (!this.areFiniteNumbers(segment.start.x, segment.start.y, segment.control.x, segment.control.y, segment.end.x, segment.end.y)) {
|
|
28168
28338
|
return undefined;
|
|
28169
28339
|
}
|
|
@@ -28177,15 +28347,15 @@ class KritzelAnchorManager {
|
|
|
28177
28347
|
lineStrokeWidth,
|
|
28178
28348
|
dashArray,
|
|
28179
28349
|
lineStroke,
|
|
28180
|
-
centerX:
|
|
28181
|
-
centerY:
|
|
28182
|
-
lineEndpointX:
|
|
28183
|
-
lineEndpointY:
|
|
28350
|
+
centerX: candidate.centerX,
|
|
28351
|
+
centerY: candidate.centerY,
|
|
28352
|
+
lineEndpointX: candidate.lineEndpointX,
|
|
28353
|
+
lineEndpointY: candidate.lineEndpointY,
|
|
28184
28354
|
edgeX,
|
|
28185
28355
|
edgeY,
|
|
28186
28356
|
arrowOffset,
|
|
28187
|
-
arrowStyle:
|
|
28188
|
-
arrowFill:
|
|
28357
|
+
arrowStyle: candidate.arrowStyle,
|
|
28358
|
+
arrowFill: candidate.arrowFill,
|
|
28189
28359
|
solidLineEndX,
|
|
28190
28360
|
solidLineEndY,
|
|
28191
28361
|
arrowPoints,
|