@fieldnotes/core 0.30.0 → 0.31.1
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 +2 -2
- package/dist/index.cjs +64 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +64 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -439,7 +439,7 @@ new Viewport(container, {
|
|
|
439
439
|
|
|
440
440
|
```typescript
|
|
441
441
|
new PencilTool({ color: '#ff0000', width: 3, smoothing: 1.5 });
|
|
442
|
-
new EraserTool({ radius: 30 }); // mode: 'partial' (default) splits strokes at the erased span; mode: 'stroke' deletes the whole stroke
|
|
442
|
+
new EraserTool({ radius: 30 }); // radius is screen pixels (converted to world units per zoom); mode: 'partial' (default) splits strokes at the erased span; mode: 'stroke' deletes the whole stroke
|
|
443
443
|
new ArrowTool({ color: '#333', width: 2 });
|
|
444
444
|
```
|
|
445
445
|
|
|
@@ -453,7 +453,7 @@ viewport.toolManager.register(
|
|
|
453
453
|
viewport.setTool('highlighter');
|
|
454
454
|
```
|
|
455
455
|
|
|
456
|
-
`ShapeTool` supports a `'line'` shape kind that draws a straight segment between two points. Hold **Shift** while drawing to snap to 45° increments. Lines are hit-tested by proximity to the segment, and `ShapeElement.flip` records which diagonal of the bounding box the line runs along.
|
|
456
|
+
`ShapeTool` supports a `'line'` shape kind that draws a straight segment between two points. Hold **Shift** while drawing to snap to 45° increments. Lines are hit-tested by proximity to the segment, and `ShapeElement.flip` records which diagonal of the bounding box the line runs along. When a line is selected, it shows two endpoint drag-handles instead of bounding-box resize handles — drag one endpoint to reshape the line while the other stays anchored.
|
|
457
457
|
|
|
458
458
|
### Arrow Labels
|
|
459
459
|
|
package/dist/index.cjs
CHANGED
|
@@ -2277,6 +2277,13 @@ function getArrowRenderGeometry(arrow) {
|
|
|
2277
2277
|
}
|
|
2278
2278
|
|
|
2279
2279
|
// src/elements/shape-geometry.ts
|
|
2280
|
+
function lineFromEndpoints(a, b) {
|
|
2281
|
+
return {
|
|
2282
|
+
position: { x: Math.min(a.x, b.x), y: Math.min(a.y, b.y) },
|
|
2283
|
+
size: { w: Math.abs(b.x - a.x), h: Math.abs(b.y - a.y) },
|
|
2284
|
+
flip: b.x > a.x !== b.y > a.y
|
|
2285
|
+
};
|
|
2286
|
+
}
|
|
2280
2287
|
function lineEndpoints(shape) {
|
|
2281
2288
|
const { x, y } = shape.position;
|
|
2282
2289
|
const { w, h } = shape.size;
|
|
@@ -6450,11 +6457,12 @@ var EraserTool = class {
|
|
|
6450
6457
|
}
|
|
6451
6458
|
eraseAt(state, ctx) {
|
|
6452
6459
|
const world = ctx.camera.screenToWorld({ x: state.x, y: state.y });
|
|
6460
|
+
const worldRadius = this.radius / ctx.camera.zoom;
|
|
6453
6461
|
const queryBounds = {
|
|
6454
|
-
x: world.x -
|
|
6455
|
-
y: world.y -
|
|
6456
|
-
w:
|
|
6457
|
-
h:
|
|
6462
|
+
x: world.x - worldRadius,
|
|
6463
|
+
y: world.y - worldRadius,
|
|
6464
|
+
w: worldRadius * 2,
|
|
6465
|
+
h: worldRadius * 2
|
|
6458
6466
|
};
|
|
6459
6467
|
const candidates = ctx.store.queryRect(queryBounds);
|
|
6460
6468
|
let erased = false;
|
|
@@ -6462,14 +6470,14 @@ var EraserTool = class {
|
|
|
6462
6470
|
if (el.type !== "stroke") continue;
|
|
6463
6471
|
if (ctx.isLayerVisible && !ctx.isLayerVisible(el.layerId)) continue;
|
|
6464
6472
|
if (ctx.isLayerLocked && ctx.isLayerLocked(el.layerId)) continue;
|
|
6465
|
-
if (!this.strokeIntersects(el, world)) continue;
|
|
6473
|
+
if (!this.strokeIntersects(el, world, worldRadius)) continue;
|
|
6466
6474
|
if (this.mode === "stroke") {
|
|
6467
6475
|
ctx.store.remove(el.id);
|
|
6468
6476
|
erased = true;
|
|
6469
6477
|
continue;
|
|
6470
6478
|
}
|
|
6471
6479
|
const localEraser = { x: world.x - el.position.x, y: world.y - el.position.y };
|
|
6472
|
-
const runs = erasePoints(el.points, localEraser,
|
|
6480
|
+
const runs = erasePoints(el.points, localEraser, worldRadius);
|
|
6473
6481
|
if (runs === null) continue;
|
|
6474
6482
|
ctx.store.remove(el.id);
|
|
6475
6483
|
for (const run of runs) {
|
|
@@ -6489,8 +6497,8 @@ var EraserTool = class {
|
|
|
6489
6497
|
}
|
|
6490
6498
|
if (erased) ctx.requestRender();
|
|
6491
6499
|
}
|
|
6492
|
-
strokeIntersects(stroke, point) {
|
|
6493
|
-
return hitTestStroke(stroke, point,
|
|
6500
|
+
strokeIntersects(stroke, point, worldRadius) {
|
|
6501
|
+
return hitTestStroke(stroke, point, worldRadius);
|
|
6494
6502
|
}
|
|
6495
6503
|
};
|
|
6496
6504
|
|
|
@@ -6682,6 +6690,12 @@ var SelectTool = class {
|
|
|
6682
6690
|
ctx.requestRender();
|
|
6683
6691
|
return;
|
|
6684
6692
|
}
|
|
6693
|
+
const lineHit = this.hitTestLineHandles(world, ctx);
|
|
6694
|
+
if (lineHit) {
|
|
6695
|
+
this.mode = { type: "line-handle", elementId: lineHit.elementId, fixed: lineHit.fixed };
|
|
6696
|
+
ctx.requestRender();
|
|
6697
|
+
return;
|
|
6698
|
+
}
|
|
6685
6699
|
const templateResizeHit = this.hitTestTemplateResizeHandle(world, ctx);
|
|
6686
6700
|
if (templateResizeHit) {
|
|
6687
6701
|
this.mode = { type: "resizing-template", elementId: templateResizeHit };
|
|
@@ -6737,6 +6751,15 @@ var SelectTool = class {
|
|
|
6737
6751
|
applyArrowHandleDrag(this.mode.handle, this.mode.elementId, world, ctx);
|
|
6738
6752
|
return;
|
|
6739
6753
|
}
|
|
6754
|
+
if (this.mode.type === "line-handle") {
|
|
6755
|
+
ctx.setCursor?.("grabbing");
|
|
6756
|
+
const el = ctx.store.getById(this.mode.elementId);
|
|
6757
|
+
if (el && el.type === "shape") {
|
|
6758
|
+
ctx.store.update(el.id, lineFromEndpoints(this.mode.fixed, world));
|
|
6759
|
+
}
|
|
6760
|
+
ctx.requestRender();
|
|
6761
|
+
return;
|
|
6762
|
+
}
|
|
6740
6763
|
if (this.mode.type === "resizing-template") {
|
|
6741
6764
|
ctx.setCursor?.("nwse-resize");
|
|
6742
6765
|
this.handleTemplateResize(world, ctx);
|
|
@@ -6905,6 +6928,10 @@ var SelectTool = class {
|
|
|
6905
6928
|
ctx.setCursor?.(getArrowHandleCursor(arrowHit.handle, false));
|
|
6906
6929
|
return null;
|
|
6907
6930
|
}
|
|
6931
|
+
if (this.hitTestLineHandles(world, ctx)) {
|
|
6932
|
+
ctx.setCursor?.("grab");
|
|
6933
|
+
return null;
|
|
6934
|
+
}
|
|
6908
6935
|
const templateResizeHit = this.hitTestTemplateResizeHandle(world, ctx);
|
|
6909
6936
|
if (templateResizeHit) {
|
|
6910
6937
|
ctx.setCursor?.("nwse-resize");
|
|
@@ -6992,6 +7019,7 @@ var SelectTool = class {
|
|
|
6992
7019
|
for (const id of this._selectedIds) {
|
|
6993
7020
|
const el = ctx.store.getById(id);
|
|
6994
7021
|
if (!el || !("size" in el)) continue;
|
|
7022
|
+
if (el.type === "shape" && el.shape === "line") continue;
|
|
6995
7023
|
const bounds = getElementBounds(el);
|
|
6996
7024
|
if (!bounds) continue;
|
|
6997
7025
|
const corners = this.getHandlePositions(bounds);
|
|
@@ -7003,6 +7031,20 @@ var SelectTool = class {
|
|
|
7003
7031
|
}
|
|
7004
7032
|
return null;
|
|
7005
7033
|
}
|
|
7034
|
+
hitTestLineHandles(world, ctx) {
|
|
7035
|
+
if (this._selectedIds.length === 0) return null;
|
|
7036
|
+
const zoom = ctx.camera.zoom;
|
|
7037
|
+
const r = (HANDLE_SIZE / 2 + HANDLE_HIT_PADDING2) / zoom;
|
|
7038
|
+
const r2 = r * r;
|
|
7039
|
+
for (const id of this._selectedIds) {
|
|
7040
|
+
const el = ctx.store.getById(id);
|
|
7041
|
+
if (!el || el.type !== "shape" || el.shape !== "line") continue;
|
|
7042
|
+
const [a, b] = lineEndpoints(el);
|
|
7043
|
+
if ((world.x - a.x) ** 2 + (world.y - a.y) ** 2 <= r2) return { elementId: id, fixed: b };
|
|
7044
|
+
if ((world.x - b.x) ** 2 + (world.y - b.y) ** 2 <= r2) return { elementId: id, fixed: a };
|
|
7045
|
+
}
|
|
7046
|
+
return null;
|
|
7047
|
+
}
|
|
7006
7048
|
getHandlePositions(bounds) {
|
|
7007
7049
|
return [
|
|
7008
7050
|
["nw", { x: bounds.x, y: bounds.y }],
|
|
@@ -7040,6 +7082,19 @@ var SelectTool = class {
|
|
|
7040
7082
|
this.renderBindingHighlights(canvasCtx, el, zoom);
|
|
7041
7083
|
continue;
|
|
7042
7084
|
}
|
|
7085
|
+
if (el.type === "shape" && el.shape === "line") {
|
|
7086
|
+
canvasCtx.setLineDash([]);
|
|
7087
|
+
canvasCtx.fillStyle = "#ffffff";
|
|
7088
|
+
const r = handleWorldSize / 2;
|
|
7089
|
+
for (const p of lineEndpoints(el)) {
|
|
7090
|
+
canvasCtx.beginPath();
|
|
7091
|
+
canvasCtx.arc(p.x, p.y, r, 0, Math.PI * 2);
|
|
7092
|
+
canvasCtx.fill();
|
|
7093
|
+
canvasCtx.stroke();
|
|
7094
|
+
}
|
|
7095
|
+
canvasCtx.setLineDash([4 / zoom, 4 / zoom]);
|
|
7096
|
+
continue;
|
|
7097
|
+
}
|
|
7043
7098
|
const bounds = getElementBounds(el);
|
|
7044
7099
|
if (!bounds) continue;
|
|
7045
7100
|
const pad = SELECTION_PAD / zoom;
|
|
@@ -8050,7 +8105,7 @@ var TemplateTool = class {
|
|
|
8050
8105
|
};
|
|
8051
8106
|
|
|
8052
8107
|
// src/index.ts
|
|
8053
|
-
var VERSION = "0.
|
|
8108
|
+
var VERSION = "0.31.1";
|
|
8054
8109
|
// Annotate the CommonJS export names for ESM import in node:
|
|
8055
8110
|
0 && (module.exports = {
|
|
8056
8111
|
ArrowTool,
|