@fieldnotes/core 0.27.0 → 0.29.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 +9 -1
- package/dist/index.cjs +242 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -3
- package/dist/index.d.ts +12 -3
- package/dist/index.js +242 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -50,6 +50,8 @@ interface ArrowElement extends BaseElement {
|
|
|
50
50
|
toBinding?: Binding;
|
|
51
51
|
/** Derived from from/to/bend. Redundant in serialized state — safe to omit. */
|
|
52
52
|
cachedControlPoint?: Point;
|
|
53
|
+
/** Optional text rendered at the curve midpoint. */
|
|
54
|
+
label?: string;
|
|
53
55
|
}
|
|
54
56
|
interface ImageElement extends BaseElement {
|
|
55
57
|
type: 'image';
|
|
@@ -446,6 +448,7 @@ declare class Viewport {
|
|
|
446
448
|
private readonly background;
|
|
447
449
|
private readonly renderer;
|
|
448
450
|
private readonly noteEditor;
|
|
451
|
+
private readonly arrowLabelEditor;
|
|
449
452
|
private readonly historyRecorder;
|
|
450
453
|
readonly toolContext: ToolContext;
|
|
451
454
|
private readonly marginViewport;
|
|
@@ -517,6 +520,8 @@ declare class Viewport {
|
|
|
517
520
|
private onTextEditStop;
|
|
518
521
|
private onTapDown;
|
|
519
522
|
private onDoubleTap;
|
|
523
|
+
private findArrowAt;
|
|
524
|
+
private startArrowLabelEdit;
|
|
520
525
|
private hitTestWorld;
|
|
521
526
|
stopInteracting(): void;
|
|
522
527
|
private onDragOver;
|
|
@@ -574,6 +579,7 @@ interface ArrowInput extends BaseDefaults {
|
|
|
574
579
|
width?: number;
|
|
575
580
|
fromBinding?: Binding;
|
|
576
581
|
toBinding?: Binding;
|
|
582
|
+
label?: string;
|
|
577
583
|
}
|
|
578
584
|
interface ImageInput extends BaseDefaults {
|
|
579
585
|
position: Point;
|
|
@@ -695,14 +701,17 @@ declare class PencilTool implements Tool {
|
|
|
695
701
|
|
|
696
702
|
interface EraserToolOptions {
|
|
697
703
|
radius?: number;
|
|
704
|
+
mode?: 'partial' | 'stroke';
|
|
698
705
|
}
|
|
699
706
|
declare class EraserTool implements Tool {
|
|
700
707
|
readonly name = "eraser";
|
|
701
708
|
private erasing;
|
|
702
|
-
private
|
|
703
|
-
private
|
|
709
|
+
private radius;
|
|
710
|
+
private cursor;
|
|
711
|
+
private mode;
|
|
704
712
|
constructor(options?: EraserToolOptions);
|
|
705
713
|
getOptions(): EraserToolOptions;
|
|
714
|
+
setOptions(options: EraserToolOptions): void;
|
|
706
715
|
onActivate(ctx: ToolContext): void;
|
|
707
716
|
onDeactivate(ctx: ToolContext): void;
|
|
708
717
|
onPointerDown(state: PointerState, ctx: ToolContext): void;
|
|
@@ -951,6 +960,6 @@ declare class TemplateTool implements Tool {
|
|
|
951
960
|
private notifyOptionsChange;
|
|
952
961
|
}
|
|
953
962
|
|
|
954
|
-
declare const VERSION = "0.
|
|
963
|
+
declare const VERSION = "0.29.0";
|
|
955
964
|
|
|
956
965
|
export { type ActiveFormats, type ArrowElement, ArrowTool, type ArrowToolOptions, AutoSave, type AutoSaveOptions, type BackgroundOptions, type BackgroundPattern, type Binding, type Bounds, Camera, type CameraChangeInfo, type CameraOptions, type CanvasElement, type CanvasState, type Command, DEFAULT_NOTE_FONT_SIZE, ElementStore, type ElementStyle, type ElementType, type ElementUpdateEvent, EraserTool, type EraserToolOptions, type ExportImageOptions, type FontSizePreset, type GridElement, type GridInfo, HandTool, type HexOrientation, HistoryStack, type HistoryStackOptions, type HtmlElement, type ImageElement, ImageTool, type ImageToolOptions, type Layer, LayerManager, MeasureTool, type MeasureToolOptions, type Measurement, type NoteElement, NoteTool, type NoteToolOptions, PencilTool, type PencilToolOptions, type Point, type PointerState, type RenderStatsSnapshot, SelectTool, type ShapeElement, type ShapeKind, ShapeTool, type ShapeToolOptions, type ShortcutBindings, type ShortcutOptions, type ShortcutsApi, type Size, type StrokeElement, type StrokePoint, type TemplateElement, type TemplateShape, TemplateTool, type TemplateToolOptions, type TextElement, TextTool, type TextToolOptions, type Tool, type ToolContext, ToolManager, type ToolName, VERSION, Viewport, type ViewportOptions, boundsIntersect, createArrow, createGrid, createHtmlElement, createImage, createNote, createShape, createStroke, createTemplate, createText, drawHexPath, exportImage, getActiveFormats, getArrowBounds, getArrowControlPoint, getArrowMidpoint, getArrowTangentAngle, getBendFromPoint, getElementBounds, getElementStyle, getElementsBoundingBox, getHexCellsInCone, getHexCellsInLine, getHexCellsInRadius, getHexCellsInSquare, getHexDistance, isNearBezier, setFontSize, smartSnap, snapPoint, snapToHexCenter, styleToPatch, toggleBold, toggleItalic, toggleStrikethrough, toggleUnderline };
|
package/dist/index.d.ts
CHANGED
|
@@ -50,6 +50,8 @@ interface ArrowElement extends BaseElement {
|
|
|
50
50
|
toBinding?: Binding;
|
|
51
51
|
/** Derived from from/to/bend. Redundant in serialized state — safe to omit. */
|
|
52
52
|
cachedControlPoint?: Point;
|
|
53
|
+
/** Optional text rendered at the curve midpoint. */
|
|
54
|
+
label?: string;
|
|
53
55
|
}
|
|
54
56
|
interface ImageElement extends BaseElement {
|
|
55
57
|
type: 'image';
|
|
@@ -446,6 +448,7 @@ declare class Viewport {
|
|
|
446
448
|
private readonly background;
|
|
447
449
|
private readonly renderer;
|
|
448
450
|
private readonly noteEditor;
|
|
451
|
+
private readonly arrowLabelEditor;
|
|
449
452
|
private readonly historyRecorder;
|
|
450
453
|
readonly toolContext: ToolContext;
|
|
451
454
|
private readonly marginViewport;
|
|
@@ -517,6 +520,8 @@ declare class Viewport {
|
|
|
517
520
|
private onTextEditStop;
|
|
518
521
|
private onTapDown;
|
|
519
522
|
private onDoubleTap;
|
|
523
|
+
private findArrowAt;
|
|
524
|
+
private startArrowLabelEdit;
|
|
520
525
|
private hitTestWorld;
|
|
521
526
|
stopInteracting(): void;
|
|
522
527
|
private onDragOver;
|
|
@@ -574,6 +579,7 @@ interface ArrowInput extends BaseDefaults {
|
|
|
574
579
|
width?: number;
|
|
575
580
|
fromBinding?: Binding;
|
|
576
581
|
toBinding?: Binding;
|
|
582
|
+
label?: string;
|
|
577
583
|
}
|
|
578
584
|
interface ImageInput extends BaseDefaults {
|
|
579
585
|
position: Point;
|
|
@@ -695,14 +701,17 @@ declare class PencilTool implements Tool {
|
|
|
695
701
|
|
|
696
702
|
interface EraserToolOptions {
|
|
697
703
|
radius?: number;
|
|
704
|
+
mode?: 'partial' | 'stroke';
|
|
698
705
|
}
|
|
699
706
|
declare class EraserTool implements Tool {
|
|
700
707
|
readonly name = "eraser";
|
|
701
708
|
private erasing;
|
|
702
|
-
private
|
|
703
|
-
private
|
|
709
|
+
private radius;
|
|
710
|
+
private cursor;
|
|
711
|
+
private mode;
|
|
704
712
|
constructor(options?: EraserToolOptions);
|
|
705
713
|
getOptions(): EraserToolOptions;
|
|
714
|
+
setOptions(options: EraserToolOptions): void;
|
|
706
715
|
onActivate(ctx: ToolContext): void;
|
|
707
716
|
onDeactivate(ctx: ToolContext): void;
|
|
708
717
|
onPointerDown(state: PointerState, ctx: ToolContext): void;
|
|
@@ -951,6 +960,6 @@ declare class TemplateTool implements Tool {
|
|
|
951
960
|
private notifyOptionsChange;
|
|
952
961
|
}
|
|
953
962
|
|
|
954
|
-
declare const VERSION = "0.
|
|
963
|
+
declare const VERSION = "0.29.0";
|
|
955
964
|
|
|
956
965
|
export { type ActiveFormats, type ArrowElement, ArrowTool, type ArrowToolOptions, AutoSave, type AutoSaveOptions, type BackgroundOptions, type BackgroundPattern, type Binding, type Bounds, Camera, type CameraChangeInfo, type CameraOptions, type CanvasElement, type CanvasState, type Command, DEFAULT_NOTE_FONT_SIZE, ElementStore, type ElementStyle, type ElementType, type ElementUpdateEvent, EraserTool, type EraserToolOptions, type ExportImageOptions, type FontSizePreset, type GridElement, type GridInfo, HandTool, type HexOrientation, HistoryStack, type HistoryStackOptions, type HtmlElement, type ImageElement, ImageTool, type ImageToolOptions, type Layer, LayerManager, MeasureTool, type MeasureToolOptions, type Measurement, type NoteElement, NoteTool, type NoteToolOptions, PencilTool, type PencilToolOptions, type Point, type PointerState, type RenderStatsSnapshot, SelectTool, type ShapeElement, type ShapeKind, ShapeTool, type ShapeToolOptions, type ShortcutBindings, type ShortcutOptions, type ShortcutsApi, type Size, type StrokeElement, type StrokePoint, type TemplateElement, type TemplateShape, TemplateTool, type TemplateToolOptions, type TextElement, TextTool, type TextToolOptions, type Tool, type ToolContext, ToolManager, type ToolName, VERSION, Viewport, type ViewportOptions, boundsIntersect, createArrow, createGrid, createHtmlElement, createImage, createNote, createShape, createStroke, createTemplate, createText, drawHexPath, exportImage, getActiveFormats, getArrowBounds, getArrowControlPoint, getArrowMidpoint, getArrowTangentAngle, getBendFromPoint, getElementBounds, getElementStyle, getElementsBoundingBox, getHexCellsInCone, getHexCellsInLine, getHexCellsInRadius, getHexCellsInSquare, getHexDistance, isNearBezier, setFontSize, smartSnap, snapPoint, snapToHexCenter, styleToPatch, toggleBold, toggleItalic, toggleStrikethrough, toggleUnderline };
|
package/dist/index.js
CHANGED
|
@@ -2658,6 +2658,7 @@ function drawHexPath(ctx, cx, cy, cellSize, orientation) {
|
|
|
2658
2658
|
var DOM_ELEMENT_TYPES = /* @__PURE__ */ new Set(["note", "html", "text"]);
|
|
2659
2659
|
var ARROWHEAD_LENGTH = 12;
|
|
2660
2660
|
var ARROWHEAD_ANGLE = Math.PI / 6;
|
|
2661
|
+
var ARROW_LABEL_FONT_SIZE = 14;
|
|
2661
2662
|
var ElementRenderer = class {
|
|
2662
2663
|
store = null;
|
|
2663
2664
|
imageCache = /* @__PURE__ */ new Map();
|
|
@@ -2668,6 +2669,7 @@ var ElementRenderer = class {
|
|
|
2668
2669
|
hexTileCache = null;
|
|
2669
2670
|
hexTileCacheKey = "";
|
|
2670
2671
|
gridBoundsOverride = null;
|
|
2672
|
+
labelEditingId = null;
|
|
2671
2673
|
setStore(store) {
|
|
2672
2674
|
this.store = store;
|
|
2673
2675
|
}
|
|
@@ -2686,6 +2688,9 @@ var ElementRenderer = class {
|
|
|
2686
2688
|
setGridBoundsOverride(bounds) {
|
|
2687
2689
|
this.gridBoundsOverride = bounds;
|
|
2688
2690
|
}
|
|
2691
|
+
setLabelEditingId(id) {
|
|
2692
|
+
this.labelEditingId = id;
|
|
2693
|
+
}
|
|
2689
2694
|
isDomElement(element) {
|
|
2690
2695
|
return DOM_ELEMENT_TYPES.has(element.type);
|
|
2691
2696
|
}
|
|
@@ -2762,6 +2767,28 @@ var ElementRenderer = class {
|
|
|
2762
2767
|
ctx.stroke();
|
|
2763
2768
|
this.renderArrowhead(ctx, arrow, visualTo, geometry.tangentEnd);
|
|
2764
2769
|
ctx.restore();
|
|
2770
|
+
this.renderArrowLabel(ctx, arrow);
|
|
2771
|
+
}
|
|
2772
|
+
renderArrowLabel(ctx, arrow) {
|
|
2773
|
+
if (!arrow.label || arrow.label.length === 0) return;
|
|
2774
|
+
if (arrow.id === this.labelEditingId) return;
|
|
2775
|
+
const mid = getArrowMidpoint(arrow.from, arrow.to, arrow.bend);
|
|
2776
|
+
ctx.save();
|
|
2777
|
+
ctx.font = `${ARROW_LABEL_FONT_SIZE}px system-ui, sans-serif`;
|
|
2778
|
+
const metrics = ctx.measureText(arrow.label);
|
|
2779
|
+
const padX = 6;
|
|
2780
|
+
const padY = 4;
|
|
2781
|
+
const w = metrics.width + padX * 2;
|
|
2782
|
+
const h = ARROW_LABEL_FONT_SIZE + padY * 2;
|
|
2783
|
+
ctx.fillStyle = "rgba(255, 255, 255, 0.9)";
|
|
2784
|
+
ctx.beginPath();
|
|
2785
|
+
ctx.roundRect(mid.x - w / 2, mid.y - h / 2, w, h, 4);
|
|
2786
|
+
ctx.fill();
|
|
2787
|
+
ctx.fillStyle = "#1a1a1a";
|
|
2788
|
+
ctx.textAlign = "center";
|
|
2789
|
+
ctx.textBaseline = "middle";
|
|
2790
|
+
ctx.fillText(arrow.label, mid.x, mid.y);
|
|
2791
|
+
ctx.restore();
|
|
2765
2792
|
}
|
|
2766
2793
|
renderArrowhead(ctx, arrow, tip, angle) {
|
|
2767
2794
|
ctx.beginPath();
|
|
@@ -3172,6 +3199,7 @@ function createArrow(input) {
|
|
|
3172
3199
|
};
|
|
3173
3200
|
if (input.fromBinding) result.fromBinding = input.fromBinding;
|
|
3174
3201
|
if (input.toBinding) result.toBinding = input.toBinding;
|
|
3202
|
+
if (input.label !== void 0) result.label = input.label;
|
|
3175
3203
|
return result;
|
|
3176
3204
|
}
|
|
3177
3205
|
function createImage(input) {
|
|
@@ -3635,6 +3663,84 @@ var NoteEditor = class {
|
|
|
3635
3663
|
}
|
|
3636
3664
|
};
|
|
3637
3665
|
|
|
3666
|
+
// src/elements/arrow-label-editor.ts
|
|
3667
|
+
var ArrowLabelEditor = class {
|
|
3668
|
+
input = null;
|
|
3669
|
+
done = false;
|
|
3670
|
+
get isEditing() {
|
|
3671
|
+
return this.input !== null;
|
|
3672
|
+
}
|
|
3673
|
+
startEditing(start) {
|
|
3674
|
+
if (this.input) this.cleanup();
|
|
3675
|
+
const { arrow, layer, store, recorder, onDone } = start;
|
|
3676
|
+
const mid = getArrowMidpoint(arrow.from, arrow.to, arrow.bend);
|
|
3677
|
+
const input = document.createElement("input");
|
|
3678
|
+
input.type = "text";
|
|
3679
|
+
input.value = arrow.label ?? "";
|
|
3680
|
+
Object.assign(input.style, {
|
|
3681
|
+
position: "absolute",
|
|
3682
|
+
left: `${mid.x}px`,
|
|
3683
|
+
top: `${mid.y}px`,
|
|
3684
|
+
transform: "translate(-50%, -50%)",
|
|
3685
|
+
// domLayer is pointer-events:none; the input must opt back in to receive taps/clicks.
|
|
3686
|
+
pointerEvents: "auto",
|
|
3687
|
+
font: "14px system-ui, sans-serif",
|
|
3688
|
+
padding: "2px 6px",
|
|
3689
|
+
border: "1px solid #2196F3",
|
|
3690
|
+
borderRadius: "4px",
|
|
3691
|
+
background: "#ffffff",
|
|
3692
|
+
color: "#1a1a1a",
|
|
3693
|
+
outline: "none",
|
|
3694
|
+
minWidth: "40px"
|
|
3695
|
+
});
|
|
3696
|
+
this.done = false;
|
|
3697
|
+
const commit = () => {
|
|
3698
|
+
if (this.done) return;
|
|
3699
|
+
this.done = true;
|
|
3700
|
+
const next = input.value.trim() || void 0;
|
|
3701
|
+
if (next !== arrow.label) {
|
|
3702
|
+
recorder.begin();
|
|
3703
|
+
store.update(arrow.id, { label: next });
|
|
3704
|
+
recorder.commit();
|
|
3705
|
+
}
|
|
3706
|
+
this.cleanup();
|
|
3707
|
+
onDone();
|
|
3708
|
+
};
|
|
3709
|
+
const cancel = () => {
|
|
3710
|
+
if (this.done) return;
|
|
3711
|
+
this.done = true;
|
|
3712
|
+
this.cleanup();
|
|
3713
|
+
onDone();
|
|
3714
|
+
};
|
|
3715
|
+
input.addEventListener("keydown", (e) => {
|
|
3716
|
+
if (e.key === "Enter") {
|
|
3717
|
+
e.preventDefault();
|
|
3718
|
+
commit();
|
|
3719
|
+
} else if (e.key === "Escape") {
|
|
3720
|
+
e.preventDefault();
|
|
3721
|
+
cancel();
|
|
3722
|
+
}
|
|
3723
|
+
e.stopPropagation();
|
|
3724
|
+
});
|
|
3725
|
+
input.addEventListener("blur", commit);
|
|
3726
|
+
layer.appendChild(input);
|
|
3727
|
+
this.input = input;
|
|
3728
|
+
input.focus();
|
|
3729
|
+
input.select();
|
|
3730
|
+
}
|
|
3731
|
+
/** Abort any in-progress edit without committing (e.g. on viewport teardown). */
|
|
3732
|
+
cancel() {
|
|
3733
|
+
this.done = true;
|
|
3734
|
+
this.cleanup();
|
|
3735
|
+
}
|
|
3736
|
+
cleanup() {
|
|
3737
|
+
if (this.input) {
|
|
3738
|
+
this.input.remove();
|
|
3739
|
+
this.input = null;
|
|
3740
|
+
}
|
|
3741
|
+
}
|
|
3742
|
+
};
|
|
3743
|
+
|
|
3638
3744
|
// src/tools/tool-manager.ts
|
|
3639
3745
|
var ToolManager = class {
|
|
3640
3746
|
tools = /* @__PURE__ */ new Map();
|
|
@@ -5244,6 +5350,7 @@ function getElementStyle(element) {
|
|
|
5244
5350
|
|
|
5245
5351
|
// src/canvas/viewport.ts
|
|
5246
5352
|
var EMPTY_IDS = [];
|
|
5353
|
+
var ARROW_HIT_THRESHOLD = 10;
|
|
5247
5354
|
function noop() {
|
|
5248
5355
|
}
|
|
5249
5356
|
function sharedValue(values) {
|
|
@@ -5285,6 +5392,7 @@ var Viewport = class {
|
|
|
5285
5392
|
placeholder: options.placeholder
|
|
5286
5393
|
});
|
|
5287
5394
|
this.noteEditor.setOnStop((id) => this.onTextEditStop(id));
|
|
5395
|
+
this.arrowLabelEditor = new ArrowLabelEditor();
|
|
5288
5396
|
this.noteEditor.setHistoryHooks(
|
|
5289
5397
|
() => this.historyRecorder.begin(),
|
|
5290
5398
|
() => this.historyRecorder.commit()
|
|
@@ -5411,6 +5519,7 @@ var Viewport = class {
|
|
|
5411
5519
|
background;
|
|
5412
5520
|
renderer;
|
|
5413
5521
|
noteEditor;
|
|
5522
|
+
arrowLabelEditor;
|
|
5414
5523
|
historyRecorder;
|
|
5415
5524
|
toolContext;
|
|
5416
5525
|
marginViewport;
|
|
@@ -5665,6 +5774,7 @@ var Viewport = class {
|
|
|
5665
5774
|
this.renderLoop.stop();
|
|
5666
5775
|
this.interactMode.destroy();
|
|
5667
5776
|
this.noteEditor.destroy(this.store);
|
|
5777
|
+
this.arrowLabelEditor.cancel();
|
|
5668
5778
|
this.historyRecorder.destroy();
|
|
5669
5779
|
this.wrapper.removeEventListener("pointerdown", this.onTapDown);
|
|
5670
5780
|
this.wrapper.removeEventListener("pointerup", this.onDoubleTap);
|
|
@@ -5750,8 +5860,35 @@ var Viewport = class {
|
|
|
5750
5860
|
const hit = this.hitTestWorld(world);
|
|
5751
5861
|
if (hit?.type === "html") {
|
|
5752
5862
|
this.interactMode.startInteracting(hit.id);
|
|
5863
|
+
return;
|
|
5864
|
+
}
|
|
5865
|
+
const arrow = this.findArrowAt(world);
|
|
5866
|
+
if (arrow) {
|
|
5867
|
+
this.startArrowLabelEdit(arrow);
|
|
5753
5868
|
}
|
|
5754
5869
|
};
|
|
5870
|
+
findArrowAt(world) {
|
|
5871
|
+
const candidates = this.store.queryPoint(world).reverse();
|
|
5872
|
+
for (const el of candidates) {
|
|
5873
|
+
if (el.type === "arrow" && isNearBezier(world, el.from, el.to, el.bend, ARROW_HIT_THRESHOLD)) {
|
|
5874
|
+
return el;
|
|
5875
|
+
}
|
|
5876
|
+
}
|
|
5877
|
+
return void 0;
|
|
5878
|
+
}
|
|
5879
|
+
startArrowLabelEdit(arrow) {
|
|
5880
|
+
this.arrowLabelEditor.startEditing({
|
|
5881
|
+
arrow,
|
|
5882
|
+
layer: this.domLayer,
|
|
5883
|
+
store: this.store,
|
|
5884
|
+
recorder: this.historyRecorder,
|
|
5885
|
+
onDone: () => {
|
|
5886
|
+
this.renderer.setLabelEditingId(null);
|
|
5887
|
+
this.requestRender();
|
|
5888
|
+
}
|
|
5889
|
+
});
|
|
5890
|
+
this.renderer.setLabelEditingId(arrow.id);
|
|
5891
|
+
}
|
|
5755
5892
|
hitTestWorld(world) {
|
|
5756
5893
|
const candidates = this.store.queryPoint(world).reverse();
|
|
5757
5894
|
for (const el of candidates) {
|
|
@@ -6073,6 +6210,79 @@ function hitTestStroke(stroke, point, radius) {
|
|
|
6073
6210
|
return false;
|
|
6074
6211
|
}
|
|
6075
6212
|
|
|
6213
|
+
// src/elements/stroke-erase.ts
|
|
6214
|
+
function lerp(a, b, t) {
|
|
6215
|
+
return {
|
|
6216
|
+
x: a.x + (b.x - a.x) * t,
|
|
6217
|
+
y: a.y + (b.y - a.y) * t,
|
|
6218
|
+
pressure: a.pressure + (b.pressure - a.pressure) * t
|
|
6219
|
+
};
|
|
6220
|
+
}
|
|
6221
|
+
function erasePoints(points, eraser, radius) {
|
|
6222
|
+
const r2 = radius * radius;
|
|
6223
|
+
if (points.length < 2) {
|
|
6224
|
+
const p = points[0];
|
|
6225
|
+
if (p && (p.x - eraser.x) ** 2 + (p.y - eraser.y) ** 2 <= r2) return [];
|
|
6226
|
+
return null;
|
|
6227
|
+
}
|
|
6228
|
+
const runs = [];
|
|
6229
|
+
let current = [];
|
|
6230
|
+
let erased = false;
|
|
6231
|
+
const flush = () => {
|
|
6232
|
+
if (current.length >= 2) runs.push(current);
|
|
6233
|
+
current = [];
|
|
6234
|
+
};
|
|
6235
|
+
for (let i = 0; i < points.length - 1; i++) {
|
|
6236
|
+
const a = points[i];
|
|
6237
|
+
const b = points[i + 1];
|
|
6238
|
+
if (!a || !b) continue;
|
|
6239
|
+
const dx = b.x - a.x;
|
|
6240
|
+
const dy = b.y - a.y;
|
|
6241
|
+
const fx = a.x - eraser.x;
|
|
6242
|
+
const fy = a.y - eraser.y;
|
|
6243
|
+
const A = dx * dx + dy * dy;
|
|
6244
|
+
const B = 2 * (fx * dx + fy * dy);
|
|
6245
|
+
const C = fx * fx + fy * fy - r2;
|
|
6246
|
+
let tLo = 1;
|
|
6247
|
+
let tHi = 0;
|
|
6248
|
+
if (A === 0) {
|
|
6249
|
+
if (C <= 0) {
|
|
6250
|
+
tLo = 0;
|
|
6251
|
+
tHi = 1;
|
|
6252
|
+
}
|
|
6253
|
+
} else {
|
|
6254
|
+
const disc = B * B - 4 * A * C;
|
|
6255
|
+
if (disc >= 0) {
|
|
6256
|
+
const sq = Math.sqrt(disc);
|
|
6257
|
+
const lo = Math.max(0, (-B - sq) / (2 * A));
|
|
6258
|
+
const hi = Math.min(1, (-B + sq) / (2 * A));
|
|
6259
|
+
if (lo < hi) {
|
|
6260
|
+
tLo = lo;
|
|
6261
|
+
tHi = hi;
|
|
6262
|
+
}
|
|
6263
|
+
}
|
|
6264
|
+
}
|
|
6265
|
+
if (tLo > tHi) {
|
|
6266
|
+
if (current.length === 0) current.push(a);
|
|
6267
|
+
current.push(b);
|
|
6268
|
+
continue;
|
|
6269
|
+
}
|
|
6270
|
+
erased = true;
|
|
6271
|
+
if (tLo > 0) {
|
|
6272
|
+
if (current.length === 0) current.push(a);
|
|
6273
|
+
current.push(lerp(a, b, tLo));
|
|
6274
|
+
flush();
|
|
6275
|
+
} else {
|
|
6276
|
+
flush();
|
|
6277
|
+
}
|
|
6278
|
+
if (tHi < 1) {
|
|
6279
|
+
current = [lerp(a, b, tHi), b];
|
|
6280
|
+
}
|
|
6281
|
+
}
|
|
6282
|
+
flush();
|
|
6283
|
+
return erased ? runs : null;
|
|
6284
|
+
}
|
|
6285
|
+
|
|
6076
6286
|
// src/tools/eraser-tool.ts
|
|
6077
6287
|
var DEFAULT_RADIUS = 20;
|
|
6078
6288
|
function makeEraserCursor(radius) {
|
|
@@ -6085,12 +6295,21 @@ var EraserTool = class {
|
|
|
6085
6295
|
erasing = false;
|
|
6086
6296
|
radius;
|
|
6087
6297
|
cursor;
|
|
6298
|
+
mode;
|
|
6088
6299
|
constructor(options = {}) {
|
|
6089
6300
|
this.radius = options.radius ?? DEFAULT_RADIUS;
|
|
6090
6301
|
this.cursor = makeEraserCursor(this.radius);
|
|
6302
|
+
this.mode = options.mode ?? "partial";
|
|
6091
6303
|
}
|
|
6092
6304
|
getOptions() {
|
|
6093
|
-
return { radius: this.radius };
|
|
6305
|
+
return { radius: this.radius, mode: this.mode };
|
|
6306
|
+
}
|
|
6307
|
+
setOptions(options) {
|
|
6308
|
+
if (options.mode !== void 0) this.mode = options.mode;
|
|
6309
|
+
if (options.radius !== void 0) {
|
|
6310
|
+
this.radius = options.radius;
|
|
6311
|
+
this.cursor = makeEraserCursor(this.radius);
|
|
6312
|
+
}
|
|
6094
6313
|
}
|
|
6095
6314
|
onActivate(ctx) {
|
|
6096
6315
|
ctx.setCursor?.(this.cursor);
|
|
@@ -6123,10 +6342,30 @@ var EraserTool = class {
|
|
|
6123
6342
|
if (el.type !== "stroke") continue;
|
|
6124
6343
|
if (ctx.isLayerVisible && !ctx.isLayerVisible(el.layerId)) continue;
|
|
6125
6344
|
if (ctx.isLayerLocked && ctx.isLayerLocked(el.layerId)) continue;
|
|
6126
|
-
if (this.strokeIntersects(el, world))
|
|
6345
|
+
if (!this.strokeIntersects(el, world)) continue;
|
|
6346
|
+
if (this.mode === "stroke") {
|
|
6127
6347
|
ctx.store.remove(el.id);
|
|
6128
6348
|
erased = true;
|
|
6349
|
+
continue;
|
|
6350
|
+
}
|
|
6351
|
+
const localEraser = { x: world.x - el.position.x, y: world.y - el.position.y };
|
|
6352
|
+
const runs = erasePoints(el.points, localEraser, this.radius);
|
|
6353
|
+
if (runs === null) continue;
|
|
6354
|
+
ctx.store.remove(el.id);
|
|
6355
|
+
for (const run of runs) {
|
|
6356
|
+
ctx.store.add(
|
|
6357
|
+
createStroke({
|
|
6358
|
+
points: run,
|
|
6359
|
+
color: el.color,
|
|
6360
|
+
width: el.width,
|
|
6361
|
+
opacity: el.opacity,
|
|
6362
|
+
layerId: el.layerId,
|
|
6363
|
+
zIndex: el.zIndex,
|
|
6364
|
+
position: el.position
|
|
6365
|
+
})
|
|
6366
|
+
);
|
|
6129
6367
|
}
|
|
6368
|
+
erased = true;
|
|
6130
6369
|
}
|
|
6131
6370
|
if (erased) ctx.requestRender();
|
|
6132
6371
|
}
|
|
@@ -7665,7 +7904,7 @@ var TemplateTool = class {
|
|
|
7665
7904
|
};
|
|
7666
7905
|
|
|
7667
7906
|
// src/index.ts
|
|
7668
|
-
var VERSION = "0.
|
|
7907
|
+
var VERSION = "0.29.0";
|
|
7669
7908
|
export {
|
|
7670
7909
|
ArrowTool,
|
|
7671
7910
|
AutoSave,
|