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
|
@@ -20682,6 +20682,8 @@ class KritzelSelectionGroup extends KritzelBaseObject {
|
|
|
20682
20682
|
_lastChildPersistTime = 0;
|
|
20683
20683
|
/** Minimum interval (ms) between child Yjs persists during drag */
|
|
20684
20684
|
static CHILD_PERSIST_THROTTLE_MS = 100;
|
|
20685
|
+
/** Ignore tiny floating-point diffs when comparing refreshed bounds */
|
|
20686
|
+
static DIMENSION_EPSILON = 0.001;
|
|
20685
20687
|
/**
|
|
20686
20688
|
* Gets the array of object IDs contained in this selection group.
|
|
20687
20689
|
* @returns Array of string IDs for the selected objects
|
|
@@ -21055,6 +21057,30 @@ class KritzelSelectionGroup extends KritzelBaseObject {
|
|
|
21055
21057
|
}
|
|
21056
21058
|
});
|
|
21057
21059
|
}
|
|
21060
|
+
/**
|
|
21061
|
+
* Returns true when the selection contains a line anchored to an object
|
|
21062
|
+
* that is not part of this selection group.
|
|
21063
|
+
*/
|
|
21064
|
+
hasExternalAnchoredLineConnections(cachedObjects) {
|
|
21065
|
+
const children = cachedObjects ?? this.objects;
|
|
21066
|
+
if (children.length === 0) {
|
|
21067
|
+
return false;
|
|
21068
|
+
}
|
|
21069
|
+
const selectedIds = new Set(children.map(child => child.id));
|
|
21070
|
+
for (const child of children) {
|
|
21071
|
+
if (!KritzelClassHelper.isInstanceOf(child, 'KritzelLine')) {
|
|
21072
|
+
continue;
|
|
21073
|
+
}
|
|
21074
|
+
const line = child;
|
|
21075
|
+
if (line.startAnchor && !selectedIds.has(line.startAnchor.objectId)) {
|
|
21076
|
+
return true;
|
|
21077
|
+
}
|
|
21078
|
+
if (line.endAnchor && !selectedIds.has(line.endAnchor.objectId)) {
|
|
21079
|
+
return true;
|
|
21080
|
+
}
|
|
21081
|
+
}
|
|
21082
|
+
return false;
|
|
21083
|
+
}
|
|
21058
21084
|
/**
|
|
21059
21085
|
* Resizes the selection group and scales all contained objects proportionally.
|
|
21060
21086
|
* Uses snapshot values to avoid compounding errors during continuous drag operations.
|
|
@@ -21147,8 +21173,13 @@ class KritzelSelectionGroup extends KritzelBaseObject {
|
|
|
21147
21173
|
*/
|
|
21148
21174
|
rotate(value) {
|
|
21149
21175
|
this.rotation = value;
|
|
21150
|
-
|
|
21151
|
-
|
|
21176
|
+
// Use the snapshot center as the rotation pivot rather than the live center.
|
|
21177
|
+
// refreshObjectDimensions() below can grow the box when an anchored line is pinned
|
|
21178
|
+
// to an external object; keeping the pivot fixed prevents the children from drifting.
|
|
21179
|
+
const snapshotTotalWidth = this.snapshotWidth + this.padding * 2;
|
|
21180
|
+
const snapshotTotalHeight = this.snapshotHeight + this.padding * 2;
|
|
21181
|
+
const centerX = this.snapshotTranslateX + snapshotTotalWidth / 2 / this.scale;
|
|
21182
|
+
const centerY = this.snapshotTranslateY + snapshotTotalHeight / 2 / this.scale;
|
|
21152
21183
|
const angle = value - this.snapshotRotation;
|
|
21153
21184
|
const cos = Math.cos(angle);
|
|
21154
21185
|
const sin = Math.sin(angle);
|
|
@@ -21170,6 +21201,31 @@ class KritzelSelectionGroup extends KritzelBaseObject {
|
|
|
21170
21201
|
child.translateY = centerY + rotatedY - child.totalHeight / 2 / child.scale;
|
|
21171
21202
|
child.rotate(childCount === 1 ? value : unchangedSnapshot.rotation + angle);
|
|
21172
21203
|
}
|
|
21204
|
+
// Re-sync any anchored lines so their stored geometry follows the rotated objects
|
|
21205
|
+
// (including lines anchored to an object outside this selection), then grow the
|
|
21206
|
+
// selection box to enclose the reshaped lines. Rotation sets state.isRotating
|
|
21207
|
+
// rather than state.isDragging, so the refresh must be triggered explicitly here.
|
|
21208
|
+
const anchorTargetIds = new Set();
|
|
21209
|
+
for (const child of children) {
|
|
21210
|
+
anchorTargetIds.add(child.id);
|
|
21211
|
+
if (KritzelClassHelper.isInstanceOf(child, 'KritzelLine')) {
|
|
21212
|
+
const line = child;
|
|
21213
|
+
if (line.startAnchor)
|
|
21214
|
+
anchorTargetIds.add(line.startAnchor.objectId);
|
|
21215
|
+
if (line.endAnchor)
|
|
21216
|
+
anchorTargetIds.add(line.endAnchor.objectId);
|
|
21217
|
+
}
|
|
21218
|
+
}
|
|
21219
|
+
let hasAnchoredLines = false;
|
|
21220
|
+
for (const targetId of anchorTargetIds) {
|
|
21221
|
+
if (this._core.anchorManager.getLinesAnchoredTo(targetId).length > 0) {
|
|
21222
|
+
hasAnchoredLines = true;
|
|
21223
|
+
this._core.anchorManager.updateAnchorsForObject(targetId);
|
|
21224
|
+
}
|
|
21225
|
+
}
|
|
21226
|
+
if (hasAnchoredLines) {
|
|
21227
|
+
this.refreshObjectDimensions(children);
|
|
21228
|
+
}
|
|
21173
21229
|
});
|
|
21174
21230
|
}
|
|
21175
21231
|
/**
|
|
@@ -21199,6 +21255,16 @@ class KritzelSelectionGroup extends KritzelBaseObject {
|
|
|
21199
21255
|
* @param skipPersist If true, only updates local state without persisting to Yjs (used during undo/redo)
|
|
21200
21256
|
*/
|
|
21201
21257
|
refreshObjectDimensions(cachedObjects, skipPersist = false) {
|
|
21258
|
+
const previous = {
|
|
21259
|
+
minX: this.minX,
|
|
21260
|
+
minY: this.minY,
|
|
21261
|
+
maxX: this.maxX,
|
|
21262
|
+
maxY: this.maxY,
|
|
21263
|
+
width: this.width,
|
|
21264
|
+
height: this.height,
|
|
21265
|
+
translateX: this.translateX,
|
|
21266
|
+
translateY: this.translateY,
|
|
21267
|
+
};
|
|
21202
21268
|
const children = cachedObjects ?? this.objects;
|
|
21203
21269
|
if (children.length === 1) {
|
|
21204
21270
|
const obj = children[0];
|
|
@@ -21252,9 +21318,18 @@ class KritzelSelectionGroup extends KritzelBaseObject {
|
|
|
21252
21318
|
this.translateX = cx - (this.width / this.scale + 2 * this.padding) / 2;
|
|
21253
21319
|
this.translateY = cy - (this.height / this.scale + 2 * this.padding) / 2;
|
|
21254
21320
|
}
|
|
21255
|
-
|
|
21321
|
+
const hasChanged = Math.abs(this.minX - previous.minX) > KritzelSelectionGroup.DIMENSION_EPSILON ||
|
|
21322
|
+
Math.abs(this.minY - previous.minY) > KritzelSelectionGroup.DIMENSION_EPSILON ||
|
|
21323
|
+
Math.abs(this.maxX - previous.maxX) > KritzelSelectionGroup.DIMENSION_EPSILON ||
|
|
21324
|
+
Math.abs(this.maxY - previous.maxY) > KritzelSelectionGroup.DIMENSION_EPSILON ||
|
|
21325
|
+
Math.abs(this.width - previous.width) > KritzelSelectionGroup.DIMENSION_EPSILON ||
|
|
21326
|
+
Math.abs(this.height - previous.height) > KritzelSelectionGroup.DIMENSION_EPSILON ||
|
|
21327
|
+
Math.abs(this.translateX - previous.translateX) > KritzelSelectionGroup.DIMENSION_EPSILON ||
|
|
21328
|
+
Math.abs(this.translateY - previous.translateY) > KritzelSelectionGroup.DIMENSION_EPSILON;
|
|
21329
|
+
if (!skipPersist && hasChanged) {
|
|
21256
21330
|
this._core.store.objects.update(this);
|
|
21257
21331
|
}
|
|
21332
|
+
return hasChanged;
|
|
21258
21333
|
}
|
|
21259
21334
|
/**
|
|
21260
21335
|
* Calculates the horizontal offset from the group's center to an object's center using snapshot data.
|
|
@@ -21264,7 +21339,7 @@ class KritzelSelectionGroup extends KritzelBaseObject {
|
|
|
21264
21339
|
*/
|
|
21265
21340
|
getOffsetXToCenterFromSnapshot(snapshot) {
|
|
21266
21341
|
const objCenterX = snapshot.translateX + snapshot.totalWidth / snapshot.scale / 2;
|
|
21267
|
-
const groupCenterX = this.
|
|
21342
|
+
const groupCenterX = this.snapshotTranslateX + (this.snapshotWidth + this.padding * 2) / this.scale / 2;
|
|
21268
21343
|
return objCenterX - groupCenterX;
|
|
21269
21344
|
}
|
|
21270
21345
|
/**
|
|
@@ -21275,7 +21350,7 @@ class KritzelSelectionGroup extends KritzelBaseObject {
|
|
|
21275
21350
|
*/
|
|
21276
21351
|
getOffsetYToCenterFromSnapshot(snapshot) {
|
|
21277
21352
|
const objCenterY = snapshot.translateY + snapshot.totalHeight / snapshot.scale / 2;
|
|
21278
|
-
const groupCenterY = this.
|
|
21353
|
+
const groupCenterY = this.snapshotTranslateY + (this.snapshotHeight + this.padding * 2) / this.scale / 2;
|
|
21279
21354
|
return objCenterY - groupCenterY;
|
|
21280
21355
|
}
|
|
21281
21356
|
/**
|
|
@@ -22648,6 +22723,7 @@ class KritzelMoveHandler extends KritzelBaseHandler {
|
|
|
22648
22723
|
this._core.store.state.isDragging = false;
|
|
22649
22724
|
if (this.hasMoved) {
|
|
22650
22725
|
this._core.store.selectionGroup.persistChildren();
|
|
22726
|
+
this._core.store.selectionGroup.refreshObjectDimensions(undefined, false);
|
|
22651
22727
|
this._core.store.selectionGroup.update();
|
|
22652
22728
|
this._core.engine.emitObjectsChange();
|
|
22653
22729
|
this._core.store.state.hasObjectsChanged = true;
|
|
@@ -22660,6 +22736,7 @@ class KritzelMoveHandler extends KritzelBaseHandler {
|
|
|
22660
22736
|
this._core.store.state.isDragging = false;
|
|
22661
22737
|
if (this.hasMoved) {
|
|
22662
22738
|
this._core.store.selectionGroup.persistChildren();
|
|
22739
|
+
this._core.store.selectionGroup.refreshObjectDimensions(undefined, false);
|
|
22663
22740
|
this._core.store.selectionGroup.update();
|
|
22664
22741
|
this._core.engine.emitObjectsChange();
|
|
22665
22742
|
this._core.store.state.hasObjectsChanged = true;
|
|
@@ -23052,6 +23129,10 @@ class KritzelRotationHandler extends KritzelBaseHandler {
|
|
|
23052
23129
|
rotation = 0;
|
|
23053
23130
|
/** The rotation value of the selection group before the current rotation operation began. */
|
|
23054
23131
|
initialSelectionGroupRotation = 0;
|
|
23132
|
+
/** The X coordinate of the rotation pivot (group center) captured when rotation starts, in world space. */
|
|
23133
|
+
initialCenterX = 0;
|
|
23134
|
+
/** The Y coordinate of the rotation pivot (group center) captured when rotation starts, in world space. */
|
|
23135
|
+
initialCenterY = 0;
|
|
23055
23136
|
/**
|
|
23056
23137
|
* Creates an instance of KritzelRotationHandler.
|
|
23057
23138
|
* @param core - The KritzelCore instance that provides access to the store, engine, and other core functionalities.
|
|
@@ -23066,6 +23147,8 @@ class KritzelRotationHandler extends KritzelBaseHandler {
|
|
|
23066
23147
|
reset() {
|
|
23067
23148
|
this.initialRotation = 0;
|
|
23068
23149
|
this.rotation = 0;
|
|
23150
|
+
this.initialCenterX = 0;
|
|
23151
|
+
this.initialCenterY = 0;
|
|
23069
23152
|
}
|
|
23070
23153
|
/**
|
|
23071
23154
|
* Handles the pointer down event to initiate a rotation operation.
|
|
@@ -23085,6 +23168,10 @@ class KritzelRotationHandler extends KritzelBaseHandler {
|
|
|
23085
23168
|
const objectScale = selectionGroup.scale || 1;
|
|
23086
23169
|
const centerX = selectionGroup.translateX + selectionGroup.width / 2 / objectScale;
|
|
23087
23170
|
const centerY = selectionGroup.translateY + selectionGroup.height / 2 / objectScale;
|
|
23171
|
+
// Capture the pivot once so it stays fixed even if the bounding box grows
|
|
23172
|
+
// mid-rotation (e.g. a line anchored to an object outside the selection).
|
|
23173
|
+
this.initialCenterX = centerX;
|
|
23174
|
+
this.initialCenterY = centerY;
|
|
23088
23175
|
const cursorX = (clientX - this._core.store.state.translateX) / this._core.store.state.scale;
|
|
23089
23176
|
const cursorY = (clientY - this._core.store.state.translateY) / this._core.store.state.scale;
|
|
23090
23177
|
this.initialSelectionGroupRotation = selectionGroup.rotation;
|
|
@@ -23108,6 +23195,10 @@ class KritzelRotationHandler extends KritzelBaseHandler {
|
|
|
23108
23195
|
const objectScale = selectionGroup.scale || 1;
|
|
23109
23196
|
const centerX = selectionGroup.translateX + selectionGroup.width / 2 / objectScale;
|
|
23110
23197
|
const centerY = selectionGroup.translateY + selectionGroup.height / 2 / objectScale;
|
|
23198
|
+
// Capture the pivot once so it stays fixed even if the bounding box grows
|
|
23199
|
+
// mid-rotation (e.g. a line anchored to an object outside the selection).
|
|
23200
|
+
this.initialCenterX = centerX;
|
|
23201
|
+
this.initialCenterY = centerY;
|
|
23111
23202
|
const cursorX = (clientX - this._core.store.state.translateX) / this._core.store.state.scale;
|
|
23112
23203
|
const cursorY = (clientY - this._core.store.state.translateY) / this._core.store.state.scale;
|
|
23113
23204
|
this.initialSelectionGroupRotation = selectionGroup.rotation;
|
|
@@ -23132,9 +23223,8 @@ class KritzelRotationHandler extends KritzelBaseHandler {
|
|
|
23132
23223
|
if (this._core.store.state.isRotating && selectionGroup) {
|
|
23133
23224
|
const clientX = event.clientX - this._core.store.offsetX;
|
|
23134
23225
|
const clientY = event.clientY - this._core.store.offsetY;
|
|
23135
|
-
const
|
|
23136
|
-
const
|
|
23137
|
-
const groupCenterY = selectionGroup.translateY + selectionGroup.height / 2 / objectScale;
|
|
23226
|
+
const groupCenterX = this.initialCenterX;
|
|
23227
|
+
const groupCenterY = this.initialCenterY;
|
|
23138
23228
|
const cursorX = (clientX - this._core.store.state.translateX) / this._core.store.state.scale;
|
|
23139
23229
|
const cursorY = (clientY - this._core.store.state.translateY) / this._core.store.state.scale;
|
|
23140
23230
|
const currentRotation = Math.atan2(groupCenterY - cursorY, groupCenterX - cursorX);
|
|
@@ -23152,9 +23242,8 @@ class KritzelRotationHandler extends KritzelBaseHandler {
|
|
|
23152
23242
|
if (this._core.store.state.isRotating && selectionGroup) {
|
|
23153
23243
|
const clientX = Math.round(firstTouch.clientX - this._core.store.offsetX);
|
|
23154
23244
|
const clientY = Math.round(firstTouch.clientY - this._core.store.offsetY);
|
|
23155
|
-
const
|
|
23156
|
-
const
|
|
23157
|
-
const groupCenterY = selectionGroup.translateY + selectionGroup.height / 2 / objectScale;
|
|
23245
|
+
const groupCenterX = this.initialCenterX;
|
|
23246
|
+
const groupCenterY = this.initialCenterY;
|
|
23158
23247
|
const cursorX = (clientX - this._core.store.state.translateX) / this._core.store.state.scale;
|
|
23159
23248
|
const cursorY = (clientY - this._core.store.state.translateY) / this._core.store.state.scale;
|
|
23160
23249
|
const currentRotation = Math.atan2(groupCenterY - cursorY, groupCenterX - cursorX);
|
|
@@ -23833,6 +23922,7 @@ class KritzelLineHandleHandler extends KritzelBaseHandler {
|
|
|
23833
23922
|
this.hasMoved = false;
|
|
23834
23923
|
this.currentSnapTarget = null;
|
|
23835
23924
|
this._core.anchorManager.clearSnapCandidate();
|
|
23925
|
+
this._core.anchorManager.clearSnapPreviewCandidate();
|
|
23836
23926
|
}
|
|
23837
23927
|
/**
|
|
23838
23928
|
* Handles pointer down events to initiate line handle dragging.
|
|
@@ -23935,15 +24025,19 @@ class KritzelLineHandleHandler extends KritzelBaseHandler {
|
|
|
23935
24025
|
const worldY = (clientY - this._core.store.state.translateY) / viewportScale;
|
|
23936
24026
|
// Get other endpoint's anchor to prevent anchoring both to same object
|
|
23937
24027
|
const otherAnchorId = line.endAnchor?.objectId;
|
|
24028
|
+
const previewTarget = this._core.anchorManager.findSnapPreviewTarget(worldX, worldY, line.id, otherAnchorId);
|
|
23938
24029
|
// Check for snap target
|
|
23939
|
-
const snapTarget = this._core.anchorManager.findSnapTarget(worldX, worldY, line.id, otherAnchorId);
|
|
24030
|
+
const snapTarget = this._core.anchorManager.findSnapTarget(worldX, worldY, line.id, otherAnchorId, event.pointerType);
|
|
23940
24031
|
this.currentSnapTarget = snapTarget;
|
|
24032
|
+
const otherEndpointWorld = this.lineLocalToWorld(line, this.initialEndX, this.initialEndY);
|
|
24033
|
+
let controlWorld = undefined;
|
|
24034
|
+
if (line.controlX !== undefined && line.controlY !== undefined) {
|
|
24035
|
+
controlWorld = this.lineLocalToWorld(line, line.controlX, line.controlY);
|
|
24036
|
+
}
|
|
23941
24037
|
if (snapTarget) {
|
|
23942
24038
|
// Snap to target center - convert world coords to local
|
|
23943
24039
|
const localCoords = this.worldToLineLocal(line, snapTarget.centerX, snapTarget.centerY);
|
|
23944
24040
|
this.updateLineEndpoint(line, localCoords.x, localCoords.y, this.initialEndX, this.initialEndY);
|
|
23945
|
-
// Get the other endpoint's world position for edge intersection calculation
|
|
23946
|
-
const otherEndpointWorld = this.lineLocalToWorld(line, this.initialEndX, this.initialEndY);
|
|
23947
24041
|
// Calculate edge intersection point using AnchorManager to support curves
|
|
23948
24042
|
const targetObject = this._core.store.allNonSelectionObjects.find(obj => obj.id === snapTarget.objectId);
|
|
23949
24043
|
let edgeX;
|
|
@@ -23957,30 +24051,16 @@ class KritzelLineHandleHandler extends KritzelBaseHandler {
|
|
|
23957
24051
|
t = clipInfo.t;
|
|
23958
24052
|
}
|
|
23959
24053
|
}
|
|
23960
|
-
// Calculate control point in world coordinates if it exists
|
|
23961
|
-
let controlWorld = undefined;
|
|
23962
|
-
if (line.controlX !== undefined && line.controlY !== undefined) {
|
|
23963
|
-
controlWorld = this.lineLocalToWorld(line, line.controlX, line.controlY);
|
|
23964
|
-
}
|
|
23965
24054
|
// Update snap indicator
|
|
23966
|
-
this._core.anchorManager.
|
|
23967
|
-
|
|
23968
|
-
|
|
23969
|
-
|
|
23970
|
-
|
|
23971
|
-
|
|
23972
|
-
|
|
23973
|
-
|
|
23974
|
-
|
|
23975
|
-
t,
|
|
23976
|
-
edgeX,
|
|
23977
|
-
edgeY,
|
|
23978
|
-
lineStroke: KritzelColorHelper.resolveThemeColor(line.stroke),
|
|
23979
|
-
lineStrokeWidth: line.strokeWidth / line.scale,
|
|
23980
|
-
arrowOffset: line.hasStartArrow ? line.getArrowSize('start') / line.scale : undefined,
|
|
23981
|
-
arrowStyle: line.hasStartArrow ? line.arrows?.start?.style : undefined,
|
|
23982
|
-
arrowFill: line.hasStartArrow ? line.getArrowFill('start') : undefined,
|
|
23983
|
-
});
|
|
24055
|
+
this._core.anchorManager.setSnapPreviewCandidate(null);
|
|
24056
|
+
this._core.anchorManager.setSnapCandidate(this.createIndicatorCandidate(line, 'start', snapTarget, otherEndpointWorld, controlWorld, { edgeX, edgeY, t }));
|
|
24057
|
+
}
|
|
24058
|
+
else if (previewTarget) {
|
|
24059
|
+
const newStartX = this.initialStartX + localDx;
|
|
24060
|
+
const newStartY = this.initialStartY + localDy;
|
|
24061
|
+
this.updateLineEndpoint(line, newStartX, newStartY, this.initialEndX, this.initialEndY);
|
|
24062
|
+
this._core.anchorManager.clearSnapCandidate();
|
|
24063
|
+
this._core.anchorManager.setSnapPreviewCandidate(this.createIndicatorCandidate(line, 'start', previewTarget, otherEndpointWorld, controlWorld));
|
|
23984
24064
|
}
|
|
23985
24065
|
else {
|
|
23986
24066
|
// No snap - use regular position
|
|
@@ -23989,6 +24069,7 @@ class KritzelLineHandleHandler extends KritzelBaseHandler {
|
|
|
23989
24069
|
this.updateLineEndpoint(line, newStartX, newStartY, this.initialEndX, this.initialEndY);
|
|
23990
24070
|
// Clear snap indicator
|
|
23991
24071
|
this._core.anchorManager.clearSnapCandidate();
|
|
24072
|
+
this._core.anchorManager.clearSnapPreviewCandidate();
|
|
23992
24073
|
}
|
|
23993
24074
|
}
|
|
23994
24075
|
else if (handleType === 'end') {
|
|
@@ -23997,15 +24078,19 @@ class KritzelLineHandleHandler extends KritzelBaseHandler {
|
|
|
23997
24078
|
const worldY = (clientY - this._core.store.state.translateY) / viewportScale;
|
|
23998
24079
|
// Get other endpoint's anchor to prevent anchoring both to same object
|
|
23999
24080
|
const otherAnchorId = line.startAnchor?.objectId;
|
|
24081
|
+
const previewTarget = this._core.anchorManager.findSnapPreviewTarget(worldX, worldY, line.id, otherAnchorId);
|
|
24000
24082
|
// Check for snap target
|
|
24001
|
-
const snapTarget = this._core.anchorManager.findSnapTarget(worldX, worldY, line.id, otherAnchorId);
|
|
24083
|
+
const snapTarget = this._core.anchorManager.findSnapTarget(worldX, worldY, line.id, otherAnchorId, event.pointerType);
|
|
24002
24084
|
this.currentSnapTarget = snapTarget;
|
|
24085
|
+
const otherEndpointWorld = this.lineLocalToWorld(line, this.initialStartX, this.initialStartY);
|
|
24086
|
+
let controlWorld = undefined;
|
|
24087
|
+
if (line.controlX !== undefined && line.controlY !== undefined) {
|
|
24088
|
+
controlWorld = this.lineLocalToWorld(line, line.controlX, line.controlY);
|
|
24089
|
+
}
|
|
24003
24090
|
if (snapTarget) {
|
|
24004
24091
|
// Snap to target center - convert world coords to local
|
|
24005
24092
|
const localCoords = this.worldToLineLocal(line, snapTarget.centerX, snapTarget.centerY);
|
|
24006
24093
|
this.updateLineEndpoint(line, this.initialStartX, this.initialStartY, localCoords.x, localCoords.y);
|
|
24007
|
-
// Get the other endpoint's world position for edge intersection calculation
|
|
24008
|
-
const otherEndpointWorld = this.lineLocalToWorld(line, this.initialStartX, this.initialStartY);
|
|
24009
24094
|
// Calculate edge intersection point using AnchorManager to support curves
|
|
24010
24095
|
const targetObject = this._core.store.allNonSelectionObjects.find(obj => obj.id === snapTarget.objectId);
|
|
24011
24096
|
let edgeX;
|
|
@@ -24019,30 +24104,16 @@ class KritzelLineHandleHandler extends KritzelBaseHandler {
|
|
|
24019
24104
|
t = clipInfo.t;
|
|
24020
24105
|
}
|
|
24021
24106
|
}
|
|
24022
|
-
// Calculate control point in world coordinates if it exists
|
|
24023
|
-
let controlWorld = undefined;
|
|
24024
|
-
if (line.controlX !== undefined && line.controlY !== undefined) {
|
|
24025
|
-
controlWorld = this.lineLocalToWorld(line, line.controlX, line.controlY);
|
|
24026
|
-
}
|
|
24027
24107
|
// Update snap indicator
|
|
24028
|
-
this._core.anchorManager.
|
|
24029
|
-
|
|
24030
|
-
|
|
24031
|
-
|
|
24032
|
-
|
|
24033
|
-
|
|
24034
|
-
|
|
24035
|
-
|
|
24036
|
-
|
|
24037
|
-
t,
|
|
24038
|
-
edgeX,
|
|
24039
|
-
edgeY,
|
|
24040
|
-
lineStroke: KritzelColorHelper.resolveThemeColor(line.stroke),
|
|
24041
|
-
lineStrokeWidth: line.strokeWidth / line.scale,
|
|
24042
|
-
arrowOffset: line.hasEndArrow ? line.getArrowSize('end') / line.scale : undefined,
|
|
24043
|
-
arrowStyle: line.hasEndArrow ? line.arrows?.end?.style : undefined,
|
|
24044
|
-
arrowFill: line.hasEndArrow ? line.getArrowFill('end') : undefined,
|
|
24045
|
-
});
|
|
24108
|
+
this._core.anchorManager.setSnapPreviewCandidate(null);
|
|
24109
|
+
this._core.anchorManager.setSnapCandidate(this.createIndicatorCandidate(line, 'end', snapTarget, otherEndpointWorld, controlWorld, { edgeX, edgeY, t }));
|
|
24110
|
+
}
|
|
24111
|
+
else if (previewTarget) {
|
|
24112
|
+
const newEndX = this.initialEndX + localDx;
|
|
24113
|
+
const newEndY = this.initialEndY + localDy;
|
|
24114
|
+
this.updateLineEndpoint(line, this.initialStartX, this.initialStartY, newEndX, newEndY);
|
|
24115
|
+
this._core.anchorManager.clearSnapCandidate();
|
|
24116
|
+
this._core.anchorManager.setSnapPreviewCandidate(this.createIndicatorCandidate(line, 'end', previewTarget, otherEndpointWorld, controlWorld));
|
|
24046
24117
|
}
|
|
24047
24118
|
else {
|
|
24048
24119
|
// No snap - use regular position
|
|
@@ -24051,9 +24122,12 @@ class KritzelLineHandleHandler extends KritzelBaseHandler {
|
|
|
24051
24122
|
this.updateLineEndpoint(line, this.initialStartX, this.initialStartY, newEndX, newEndY);
|
|
24052
24123
|
// Clear snap indicator
|
|
24053
24124
|
this._core.anchorManager.clearSnapCandidate();
|
|
24125
|
+
this._core.anchorManager.clearSnapPreviewCandidate();
|
|
24054
24126
|
}
|
|
24055
24127
|
}
|
|
24056
24128
|
else if (handleType === 'center') {
|
|
24129
|
+
this._core.anchorManager.clearSnapPreviewCandidate();
|
|
24130
|
+
this._core.anchorManager.clearSnapCandidate();
|
|
24057
24131
|
const startControlX = this.initialControlX ?? (this.initialStartX + this.initialEndX) / 2;
|
|
24058
24132
|
const startControlY = this.initialControlY ?? (this.initialStartY + this.initialEndY) / 2;
|
|
24059
24133
|
const newControlX = startControlX + localDx * 2;
|
|
@@ -24249,6 +24323,28 @@ class KritzelLineHandleHandler extends KritzelBaseHandler {
|
|
|
24249
24323
|
}
|
|
24250
24324
|
return null;
|
|
24251
24325
|
}
|
|
24326
|
+
createIndicatorCandidate(line, endpoint, target, otherEndpointWorld, controlWorld, clipInfo) {
|
|
24327
|
+
return {
|
|
24328
|
+
objectId: target.objectId,
|
|
24329
|
+
endpoint,
|
|
24330
|
+
centerX: target.centerX,
|
|
24331
|
+
centerY: target.centerY,
|
|
24332
|
+
lineEndpointX: otherEndpointWorld.x,
|
|
24333
|
+
lineEndpointY: otherEndpointWorld.y,
|
|
24334
|
+
controlX: controlWorld?.x,
|
|
24335
|
+
controlY: controlWorld?.y,
|
|
24336
|
+
t: clipInfo?.t,
|
|
24337
|
+
edgeX: clipInfo?.edgeX,
|
|
24338
|
+
edgeY: clipInfo?.edgeY,
|
|
24339
|
+
lineStroke: KritzelColorHelper.resolveThemeColor(line.stroke),
|
|
24340
|
+
lineStrokeWidth: line.strokeWidth / line.scale,
|
|
24341
|
+
arrowOffset: endpoint === 'start'
|
|
24342
|
+
? (line.hasStartArrow ? line.getArrowSize('start') / line.scale : undefined)
|
|
24343
|
+
: (line.hasEndArrow ? line.getArrowSize('end') / line.scale : undefined),
|
|
24344
|
+
arrowStyle: endpoint === 'start' ? line.arrows?.start?.style : line.arrows?.end?.style,
|
|
24345
|
+
arrowFill: endpoint === 'start' ? line.getArrowFill('start') : line.getArrowFill('end'),
|
|
24346
|
+
};
|
|
24347
|
+
}
|
|
24252
24348
|
}
|
|
24253
24349
|
|
|
24254
24350
|
/**
|
|
@@ -27890,6 +27986,14 @@ class KritzelAnchorManager {
|
|
|
27890
27986
|
}
|
|
27891
27987
|
this.snapEndpointToObject(line, entry.endpoint, objectId);
|
|
27892
27988
|
}
|
|
27989
|
+
const selectionGroup = this._core.store.selectionGroup;
|
|
27990
|
+
if (!selectionGroup || !this._core.store.state.isDragging) {
|
|
27991
|
+
return;
|
|
27992
|
+
}
|
|
27993
|
+
if (selectionGroup.hasExternalAnchoredLineConnections()) {
|
|
27994
|
+
// Keep the local drag overlay aligned with reshaped anchored lines without spamming Yjs.
|
|
27995
|
+
selectionGroup.refreshObjectDimensions(undefined, true);
|
|
27996
|
+
}
|
|
27893
27997
|
}
|
|
27894
27998
|
/**
|
|
27895
27999
|
* Snaps a line endpoint to an object's center position.
|
|
@@ -27946,15 +28050,35 @@ class KritzelAnchorManager {
|
|
|
27946
28050
|
// Snap Detection
|
|
27947
28051
|
// ============================================
|
|
27948
28052
|
/**
|
|
27949
|
-
* Finds
|
|
28053
|
+
* Finds an object that can be snapped to when the cursor is sufficiently close to its center.
|
|
28054
|
+
* Snap is only triggered when the cursor is inside the object's bounds AND within 1/5 of the
|
|
28055
|
+
* shorter side from the center, so snapping activates only as the user approaches the center
|
|
28056
|
+
* and releases quickly as they move away.
|
|
27950
28057
|
* Returns null if no suitable snap target is found.
|
|
27951
28058
|
*
|
|
27952
28059
|
* @param worldX - X coordinate in world space
|
|
27953
28060
|
* @param worldY - Y coordinate in world space
|
|
27954
28061
|
* @param excludeLineId - ID of the line being edited (to exclude from snap targets)
|
|
27955
28062
|
* @param otherEndpointAnchorId - ID of object anchored to the other endpoint (to prevent same-object anchoring)
|
|
28063
|
+
* @param pointerType - The pointer type driving the drag interaction.
|
|
28064
|
+
*/
|
|
28065
|
+
findSnapTarget(worldX, worldY, excludeLineId, otherEndpointAnchorId, pointerType = 'mouse') {
|
|
28066
|
+
return this.findBestSnapTarget(worldX, worldY, excludeLineId, otherEndpointAnchorId, object => {
|
|
28067
|
+
const snapRadius = this.getSnapRadius(object, pointerType);
|
|
28068
|
+
const dx = worldX - object.centerX;
|
|
28069
|
+
const dy = worldY - object.centerY;
|
|
28070
|
+
const distanceToCenter = Math.sqrt(dx * dx + dy * dy);
|
|
28071
|
+
return distanceToCenter <= snapRadius;
|
|
28072
|
+
});
|
|
28073
|
+
}
|
|
28074
|
+
/**
|
|
28075
|
+
* Finds the topmost anchorable object currently containing the dragged endpoint.
|
|
28076
|
+
* Used to drive preview UI before an actual snap engages.
|
|
27956
28077
|
*/
|
|
27957
|
-
|
|
28078
|
+
findSnapPreviewTarget(worldX, worldY, excludeLineId, otherEndpointAnchorId) {
|
|
28079
|
+
return this.findBestSnapTarget(worldX, worldY, excludeLineId, otherEndpointAnchorId, () => true);
|
|
28080
|
+
}
|
|
28081
|
+
findBestSnapTarget(worldX, worldY, excludeLineId, otherEndpointAnchorId, predicate) {
|
|
27958
28082
|
let bestTarget = null;
|
|
27959
28083
|
let highestZIndex = -Infinity;
|
|
27960
28084
|
const objects = this._core.store.allNonSelectionObjects;
|
|
@@ -27974,20 +28098,30 @@ class KritzelAnchorManager {
|
|
|
27974
28098
|
// Check if point is inside the object's rotated polygon
|
|
27975
28099
|
const polygon = object.rotatedPolygon;
|
|
27976
28100
|
const points = [polygon.topLeft, polygon.topRight, polygon.bottomRight, polygon.bottomLeft];
|
|
27977
|
-
if (KritzelGeometryHelper.isPointInPolygon({ x: worldX, y: worldY }, points)) {
|
|
27978
|
-
|
|
27979
|
-
|
|
27980
|
-
|
|
27981
|
-
|
|
27982
|
-
|
|
27983
|
-
|
|
27984
|
-
|
|
27985
|
-
|
|
27986
|
-
|
|
28101
|
+
if (!KritzelGeometryHelper.isPointInPolygon({ x: worldX, y: worldY }, points)) {
|
|
28102
|
+
continue;
|
|
28103
|
+
}
|
|
28104
|
+
if (!predicate(object)) {
|
|
28105
|
+
continue;
|
|
28106
|
+
}
|
|
28107
|
+
if (object.zIndex > highestZIndex) {
|
|
28108
|
+
highestZIndex = object.zIndex;
|
|
28109
|
+
bestTarget = {
|
|
28110
|
+
objectId: object.id,
|
|
28111
|
+
centerX: object.centerX,
|
|
28112
|
+
centerY: object.centerY,
|
|
28113
|
+
};
|
|
27987
28114
|
}
|
|
27988
28115
|
}
|
|
27989
28116
|
return bestTarget;
|
|
27990
28117
|
}
|
|
28118
|
+
getSnapRadius(object, pointerType) {
|
|
28119
|
+
const baseRadius = Math.min(object.width, object.height) / 5;
|
|
28120
|
+
if (pointerType === 'touch' || pointerType === 'pen') {
|
|
28121
|
+
return Math.max(baseRadius * 1.75, 24 / this.getSafeScale(this._core.store.state.scale));
|
|
28122
|
+
}
|
|
28123
|
+
return baseRadius;
|
|
28124
|
+
}
|
|
27991
28125
|
/**
|
|
27992
28126
|
* Sets the current snap candidate for visual feedback during line endpoint dragging.
|
|
27993
28127
|
* Updates the store state and triggers a rerender to show the snap indicator.
|
|
@@ -27998,6 +28132,17 @@ class KritzelAnchorManager {
|
|
|
27998
28132
|
this._core.store.state.snapCandidate = candidate;
|
|
27999
28133
|
this._core.rerender();
|
|
28000
28134
|
}
|
|
28135
|
+
/**
|
|
28136
|
+
* Sets the current snap preview candidate for early visual feedback during line dragging.
|
|
28137
|
+
* Preview state must remain separate from active snap state so line clipping and anchor
|
|
28138
|
+
* commit semantics only react to actual snaps.
|
|
28139
|
+
*
|
|
28140
|
+
* @param candidate - The preview candidate object, or null to clear.
|
|
28141
|
+
*/
|
|
28142
|
+
setSnapPreviewCandidate(candidate) {
|
|
28143
|
+
this._core.store.state.snapPreviewCandidate = candidate;
|
|
28144
|
+
this._core.rerender();
|
|
28145
|
+
}
|
|
28001
28146
|
/**
|
|
28002
28147
|
* Gets the current snap candidate from the store state.
|
|
28003
28148
|
*
|
|
@@ -28006,6 +28151,14 @@ class KritzelAnchorManager {
|
|
|
28006
28151
|
getSnapCandidate() {
|
|
28007
28152
|
return this._core.store.state.snapCandidate ?? null;
|
|
28008
28153
|
}
|
|
28154
|
+
/**
|
|
28155
|
+
* Gets the current snap preview candidate from the store state.
|
|
28156
|
+
*
|
|
28157
|
+
* @returns The current preview candidate if one exists, null otherwise.
|
|
28158
|
+
*/
|
|
28159
|
+
getSnapPreviewCandidate() {
|
|
28160
|
+
return this._core.store.state.snapPreviewCandidate ?? null;
|
|
28161
|
+
}
|
|
28009
28162
|
/**
|
|
28010
28163
|
* Clears the snap candidate from the store state and triggers a rerender.
|
|
28011
28164
|
* Should be called when snapping is cancelled or completed.
|
|
@@ -28014,6 +28167,13 @@ class KritzelAnchorManager {
|
|
|
28014
28167
|
this._core.store.state.snapCandidate = null;
|
|
28015
28168
|
this._core.rerender();
|
|
28016
28169
|
}
|
|
28170
|
+
/**
|
|
28171
|
+
* Clears the snap preview candidate from the store state and triggers a rerender.
|
|
28172
|
+
*/
|
|
28173
|
+
clearSnapPreviewCandidate() {
|
|
28174
|
+
this._core.store.state.snapPreviewCandidate = null;
|
|
28175
|
+
this._core.rerender();
|
|
28176
|
+
}
|
|
28017
28177
|
// ============================================
|
|
28018
28178
|
// Render Data
|
|
28019
28179
|
// ============================================
|
|
@@ -28067,33 +28227,43 @@ class KritzelAnchorManager {
|
|
|
28067
28227
|
* or null if there's no active snap candidate.
|
|
28068
28228
|
*/
|
|
28069
28229
|
getSnapIndicatorRenderData() {
|
|
28070
|
-
|
|
28071
|
-
|
|
28230
|
+
return this.getIndicatorRenderDataForCandidate(this.getSnapCandidate());
|
|
28231
|
+
}
|
|
28232
|
+
/**
|
|
28233
|
+
* Gets render data for snap preview visualization.
|
|
28234
|
+
*
|
|
28235
|
+
* @returns SnapIndicatorRenderData for the active preview candidate, or null if none exists.
|
|
28236
|
+
*/
|
|
28237
|
+
getSnapPreviewRenderData() {
|
|
28238
|
+
return this.getIndicatorRenderDataForCandidate(this.getSnapPreviewCandidate());
|
|
28239
|
+
}
|
|
28240
|
+
getIndicatorRenderDataForCandidate(candidate) {
|
|
28241
|
+
if (!candidate)
|
|
28072
28242
|
return null;
|
|
28073
|
-
if (!this.areFiniteNumbers(
|
|
28243
|
+
if (!this.areFiniteNumbers(candidate.centerX, candidate.centerY, candidate.lineEndpointX, candidate.lineEndpointY)) {
|
|
28074
28244
|
return null;
|
|
28075
28245
|
}
|
|
28076
28246
|
const scale = this.getSafeScale(this._core.store.state.scale);
|
|
28077
28247
|
const indicatorRadius = 8 / scale;
|
|
28078
28248
|
const indicatorStrokeWidth = `${2 / scale}`;
|
|
28079
|
-
const lineStrokeWidthNum = this.areFiniteNumbers(
|
|
28080
|
-
?
|
|
28249
|
+
const lineStrokeWidthNum = this.areFiniteNumbers(candidate.lineStrokeWidth) && candidate.lineStrokeWidth > 0
|
|
28250
|
+
? candidate.lineStrokeWidth
|
|
28081
28251
|
: (4 / scale);
|
|
28082
28252
|
const lineStrokeWidth = `${lineStrokeWidthNum}`;
|
|
28083
28253
|
const dashLength = Math.max(lineStrokeWidthNum * 2, 4 / scale);
|
|
28084
28254
|
const dashArray = `${dashLength} ${dashLength}`;
|
|
28085
|
-
const lineStroke =
|
|
28086
|
-
let edgeX = this.areFiniteNumbers(
|
|
28087
|
-
let edgeY = this.areFiniteNumbers(
|
|
28255
|
+
const lineStroke = candidate.lineStroke || '#000000';
|
|
28256
|
+
let edgeX = this.areFiniteNumbers(candidate.edgeX) ? candidate.edgeX : undefined;
|
|
28257
|
+
let edgeY = this.areFiniteNumbers(candidate.edgeY) ? candidate.edgeY : undefined;
|
|
28088
28258
|
let solidLineEndX = edgeX;
|
|
28089
28259
|
let solidLineEndY = edgeY;
|
|
28090
28260
|
let arrowPoints;
|
|
28091
|
-
const arrowOffset = this.areFiniteNumbers(
|
|
28092
|
-
?
|
|
28261
|
+
const arrowOffset = this.areFiniteNumbers(candidate.arrowOffset) && candidate.arrowOffset > 0
|
|
28262
|
+
? candidate.arrowOffset
|
|
28093
28263
|
: undefined;
|
|
28094
28264
|
if (arrowOffset !== undefined && edgeX !== undefined && edgeY !== undefined) {
|
|
28095
|
-
const dx =
|
|
28096
|
-
const dy =
|
|
28265
|
+
const dx = candidate.lineEndpointX - edgeX;
|
|
28266
|
+
const dy = candidate.lineEndpointY - edgeY;
|
|
28097
28267
|
const length = Math.sqrt(dx * dx + dy * dy);
|
|
28098
28268
|
if (length > arrowOffset) {
|
|
28099
28269
|
solidLineEndX = edgeX + (dx / length) * arrowOffset;
|
|
@@ -28101,8 +28271,8 @@ class KritzelAnchorManager {
|
|
|
28101
28271
|
}
|
|
28102
28272
|
// Calculate arrow head points
|
|
28103
28273
|
// Direction from line endpoint to edge (arrow direction)
|
|
28104
|
-
const arrowDx = edgeX -
|
|
28105
|
-
const arrowDy = edgeY -
|
|
28274
|
+
const arrowDx = edgeX - candidate.lineEndpointX;
|
|
28275
|
+
const arrowDy = edgeY - candidate.lineEndpointY;
|
|
28106
28276
|
const arrowLengthTotal = Math.sqrt(arrowDx * arrowDx + arrowDy * arrowDy);
|
|
28107
28277
|
if (arrowLengthTotal > 0) {
|
|
28108
28278
|
const ux = arrowDx / arrowLengthTotal;
|
|
@@ -28133,15 +28303,15 @@ class KritzelAnchorManager {
|
|
|
28133
28303
|
return null;
|
|
28134
28304
|
}
|
|
28135
28305
|
const snapLinePath = (() => {
|
|
28136
|
-
if (
|
|
28137
|
-
|
|
28138
|
-
|
|
28139
|
-
this.areFiniteNumbers(
|
|
28140
|
-
const startT =
|
|
28306
|
+
if (candidate.controlX !== undefined &&
|
|
28307
|
+
candidate.controlY !== undefined &&
|
|
28308
|
+
candidate.t !== undefined &&
|
|
28309
|
+
this.areFiniteNumbers(candidate.controlX, candidate.controlY, candidate.t)) {
|
|
28310
|
+
const startT = candidate.endpoint === 'start' ? 1 - candidate.t : candidate.t;
|
|
28141
28311
|
// Ensure meaningful range
|
|
28142
28312
|
if (startT >= 1)
|
|
28143
28313
|
return undefined;
|
|
28144
|
-
const segment = this.extractQuadraticSegment({ x:
|
|
28314
|
+
const segment = this.extractQuadraticSegment({ x: candidate.lineEndpointX, y: candidate.lineEndpointY }, { x: candidate.controlX, y: candidate.controlY }, { x: candidate.centerX, y: candidate.centerY }, startT, 1);
|
|
28145
28315
|
if (!this.areFiniteNumbers(segment.start.x, segment.start.y, segment.control.x, segment.control.y, segment.end.x, segment.end.y)) {
|
|
28146
28316
|
return undefined;
|
|
28147
28317
|
}
|
|
@@ -28155,15 +28325,15 @@ class KritzelAnchorManager {
|
|
|
28155
28325
|
lineStrokeWidth,
|
|
28156
28326
|
dashArray,
|
|
28157
28327
|
lineStroke,
|
|
28158
|
-
centerX:
|
|
28159
|
-
centerY:
|
|
28160
|
-
lineEndpointX:
|
|
28161
|
-
lineEndpointY:
|
|
28328
|
+
centerX: candidate.centerX,
|
|
28329
|
+
centerY: candidate.centerY,
|
|
28330
|
+
lineEndpointX: candidate.lineEndpointX,
|
|
28331
|
+
lineEndpointY: candidate.lineEndpointY,
|
|
28162
28332
|
edgeX,
|
|
28163
28333
|
edgeY,
|
|
28164
28334
|
arrowOffset,
|
|
28165
|
-
arrowStyle:
|
|
28166
|
-
arrowFill:
|
|
28335
|
+
arrowStyle: candidate.arrowStyle,
|
|
28336
|
+
arrowFill: candidate.arrowFill,
|
|
28167
28337
|
solidLineEndX,
|
|
28168
28338
|
solidLineEndY,
|
|
28169
28339
|
arrowPoints,
|