@fieldnotes/core 0.8.7 → 0.8.8
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/index.cjs +126 -17
- package/dist/index.d.cts +11 -1
- package/dist/index.d.ts +11 -1
- package/dist/index.js +126 -17
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -542,6 +542,13 @@ var Background = class {
|
|
|
542
542
|
color;
|
|
543
543
|
dotRadius;
|
|
544
544
|
lineWidth;
|
|
545
|
+
cachedCanvas = null;
|
|
546
|
+
cachedCtx = null;
|
|
547
|
+
lastZoom = -1;
|
|
548
|
+
lastOffsetX = -Infinity;
|
|
549
|
+
lastOffsetY = -Infinity;
|
|
550
|
+
lastWidth = 0;
|
|
551
|
+
lastHeight = 0;
|
|
545
552
|
constructor(options = {}) {
|
|
546
553
|
this.pattern = options.pattern ?? DEFAULTS.pattern;
|
|
547
554
|
this.spacing = options.spacing ?? DEFAULTS.spacing;
|
|
@@ -557,13 +564,69 @@ var Background = class {
|
|
|
557
564
|
ctx.save();
|
|
558
565
|
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
559
566
|
ctx.clearRect(0, 0, cssWidth, cssHeight);
|
|
567
|
+
if (this.pattern === "none") {
|
|
568
|
+
ctx.restore();
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
const spacing = this.adaptSpacing(this.spacing, camera.zoom);
|
|
572
|
+
const keyZoom = camera.zoom;
|
|
573
|
+
const keyX = Math.floor(camera.position.x % spacing);
|
|
574
|
+
const keyY = Math.floor(camera.position.y % spacing);
|
|
575
|
+
if (this.cachedCanvas !== null && keyZoom === this.lastZoom && keyX === this.lastOffsetX && keyY === this.lastOffsetY && cssWidth === this.lastWidth && cssHeight === this.lastHeight) {
|
|
576
|
+
ctx.drawImage(this.cachedCanvas, 0, 0);
|
|
577
|
+
ctx.restore();
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
this.ensureCachedCanvas(cssWidth, cssHeight, dpr);
|
|
581
|
+
if (this.cachedCtx === null) {
|
|
582
|
+
if (this.pattern === "dots") {
|
|
583
|
+
this.renderDots(ctx, camera, cssWidth, cssHeight);
|
|
584
|
+
} else if (this.pattern === "grid") {
|
|
585
|
+
this.renderGrid(ctx, camera, cssWidth, cssHeight);
|
|
586
|
+
}
|
|
587
|
+
ctx.restore();
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
const offCtx = this.cachedCtx;
|
|
591
|
+
offCtx.clearRect(0, 0, cssWidth, cssHeight);
|
|
560
592
|
if (this.pattern === "dots") {
|
|
561
|
-
this.renderDots(
|
|
593
|
+
this.renderDots(offCtx, camera, cssWidth, cssHeight);
|
|
562
594
|
} else if (this.pattern === "grid") {
|
|
563
|
-
this.renderGrid(
|
|
564
|
-
}
|
|
595
|
+
this.renderGrid(offCtx, camera, cssWidth, cssHeight);
|
|
596
|
+
}
|
|
597
|
+
this.lastZoom = keyZoom;
|
|
598
|
+
this.lastOffsetX = keyX;
|
|
599
|
+
this.lastOffsetY = keyY;
|
|
600
|
+
this.lastWidth = cssWidth;
|
|
601
|
+
this.lastHeight = cssHeight;
|
|
602
|
+
ctx.drawImage(this.cachedCanvas, 0, 0);
|
|
565
603
|
ctx.restore();
|
|
566
604
|
}
|
|
605
|
+
ensureCachedCanvas(cssWidth, cssHeight, dpr) {
|
|
606
|
+
if (this.cachedCanvas !== null && this.lastWidth === cssWidth && this.lastHeight === cssHeight) {
|
|
607
|
+
return;
|
|
608
|
+
}
|
|
609
|
+
const physWidth = Math.round(cssWidth * dpr);
|
|
610
|
+
const physHeight = Math.round(cssHeight * dpr);
|
|
611
|
+
if (typeof OffscreenCanvas !== "undefined") {
|
|
612
|
+
this.cachedCanvas = new OffscreenCanvas(physWidth, physHeight);
|
|
613
|
+
} else if (typeof document !== "undefined") {
|
|
614
|
+
const el = document.createElement("canvas");
|
|
615
|
+
el.width = physWidth;
|
|
616
|
+
el.height = physHeight;
|
|
617
|
+
this.cachedCanvas = el;
|
|
618
|
+
} else {
|
|
619
|
+
this.cachedCanvas = null;
|
|
620
|
+
this.cachedCtx = null;
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
623
|
+
const offCtx = this.cachedCanvas.getContext("2d");
|
|
624
|
+
if (offCtx !== null) {
|
|
625
|
+
offCtx.scale(dpr, dpr);
|
|
626
|
+
}
|
|
627
|
+
this.cachedCtx = offCtx;
|
|
628
|
+
this.lastZoom = -1;
|
|
629
|
+
}
|
|
567
630
|
adaptSpacing(baseSpacing, zoom) {
|
|
568
631
|
let spacing = baseSpacing * zoom;
|
|
569
632
|
while (spacing < MIN_PATTERN_SPACING) {
|
|
@@ -1046,6 +1109,10 @@ var ElementStore = class {
|
|
|
1046
1109
|
const existing = this.elements.get(id);
|
|
1047
1110
|
if (!existing) return;
|
|
1048
1111
|
const updated = { ...existing, ...partial, id: existing.id, type: existing.type };
|
|
1112
|
+
if (updated.type === "arrow") {
|
|
1113
|
+
const arrow = updated;
|
|
1114
|
+
arrow.cachedControlPoint = getArrowControlPoint(arrow.from, arrow.to, arrow.bend);
|
|
1115
|
+
}
|
|
1049
1116
|
this.elements.set(id, updated);
|
|
1050
1117
|
const newBounds = getElementBounds(updated);
|
|
1051
1118
|
if (newBounds) {
|
|
@@ -1301,6 +1368,25 @@ function smoothToSegments(points) {
|
|
|
1301
1368
|
return segments;
|
|
1302
1369
|
}
|
|
1303
1370
|
|
|
1371
|
+
// src/elements/stroke-cache.ts
|
|
1372
|
+
var cache = /* @__PURE__ */ new WeakMap();
|
|
1373
|
+
function computeStrokeSegments(stroke) {
|
|
1374
|
+
const segments = smoothToSegments(stroke.points);
|
|
1375
|
+
const widths = [];
|
|
1376
|
+
for (const seg of segments) {
|
|
1377
|
+
const w = (pressureToWidth(seg.start.pressure, stroke.width) + pressureToWidth(seg.end.pressure, stroke.width)) / 2;
|
|
1378
|
+
widths.push(w);
|
|
1379
|
+
}
|
|
1380
|
+
const data = { segments, widths };
|
|
1381
|
+
cache.set(stroke, data);
|
|
1382
|
+
return data;
|
|
1383
|
+
}
|
|
1384
|
+
function getStrokeRenderData(stroke) {
|
|
1385
|
+
const cached = cache.get(stroke);
|
|
1386
|
+
if (cached) return cached;
|
|
1387
|
+
return computeStrokeSegments(stroke);
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1304
1390
|
// src/elements/grid-renderer.ts
|
|
1305
1391
|
function getSquareGridLines(bounds, cellSize) {
|
|
1306
1392
|
if (cellSize <= 0) return { verticals: [], horizontals: [] };
|
|
@@ -1465,9 +1551,11 @@ var ElementRenderer = class {
|
|
|
1465
1551
|
ctx.lineCap = "round";
|
|
1466
1552
|
ctx.lineJoin = "round";
|
|
1467
1553
|
ctx.globalAlpha = stroke.opacity;
|
|
1468
|
-
const segments =
|
|
1469
|
-
for (
|
|
1470
|
-
const
|
|
1554
|
+
const { segments, widths } = getStrokeRenderData(stroke);
|
|
1555
|
+
for (let i = 0; i < segments.length; i++) {
|
|
1556
|
+
const seg = segments[i];
|
|
1557
|
+
const w = widths[i];
|
|
1558
|
+
if (!seg || w === void 0) continue;
|
|
1471
1559
|
ctx.lineWidth = w;
|
|
1472
1560
|
ctx.beginPath();
|
|
1473
1561
|
ctx.moveTo(seg.start.x, seg.start.y);
|
|
@@ -1488,7 +1576,7 @@ var ElementRenderer = class {
|
|
|
1488
1576
|
ctx.beginPath();
|
|
1489
1577
|
ctx.moveTo(visualFrom.x, visualFrom.y);
|
|
1490
1578
|
if (arrow.bend !== 0) {
|
|
1491
|
-
const cp = getArrowControlPoint(arrow.from, arrow.to, arrow.bend);
|
|
1579
|
+
const cp = arrow.cachedControlPoint ?? getArrowControlPoint(arrow.from, arrow.to, arrow.bend);
|
|
1492
1580
|
ctx.quadraticCurveTo(cp.x, cp.y, visualTo.x, visualTo.y);
|
|
1493
1581
|
} else {
|
|
1494
1582
|
ctx.lineTo(visualTo.x, visualTo.y);
|
|
@@ -1629,15 +1717,33 @@ var ElementRenderer = class {
|
|
|
1629
1717
|
renderImage(ctx, image) {
|
|
1630
1718
|
const img = this.getImage(image.src);
|
|
1631
1719
|
if (!img) return;
|
|
1632
|
-
ctx.drawImage(
|
|
1720
|
+
ctx.drawImage(
|
|
1721
|
+
img,
|
|
1722
|
+
image.position.x,
|
|
1723
|
+
image.position.y,
|
|
1724
|
+
image.size.w,
|
|
1725
|
+
image.size.h
|
|
1726
|
+
);
|
|
1633
1727
|
}
|
|
1634
1728
|
getImage(src) {
|
|
1635
1729
|
const cached = this.imageCache.get(src);
|
|
1636
|
-
if (cached)
|
|
1730
|
+
if (cached) {
|
|
1731
|
+
if (cached instanceof HTMLImageElement) return cached.complete ? cached : null;
|
|
1732
|
+
return cached;
|
|
1733
|
+
}
|
|
1637
1734
|
const img = new Image();
|
|
1638
1735
|
img.src = src;
|
|
1639
1736
|
this.imageCache.set(src, img);
|
|
1640
|
-
img.onload = () =>
|
|
1737
|
+
img.onload = () => {
|
|
1738
|
+
this.onImageLoad?.();
|
|
1739
|
+
if (typeof createImageBitmap !== "undefined") {
|
|
1740
|
+
createImageBitmap(img).then((bitmap) => {
|
|
1741
|
+
this.imageCache.set(src, bitmap);
|
|
1742
|
+
this.onImageLoad?.();
|
|
1743
|
+
}).catch(() => {
|
|
1744
|
+
});
|
|
1745
|
+
}
|
|
1746
|
+
};
|
|
1641
1747
|
return null;
|
|
1642
1748
|
}
|
|
1643
1749
|
};
|
|
@@ -2013,6 +2119,7 @@ function createNote(input) {
|
|
|
2013
2119
|
};
|
|
2014
2120
|
}
|
|
2015
2121
|
function createArrow(input) {
|
|
2122
|
+
const bend = input.bend ?? 0;
|
|
2016
2123
|
const result = {
|
|
2017
2124
|
id: createId("arrow"),
|
|
2018
2125
|
type: "arrow",
|
|
@@ -2022,9 +2129,10 @@ function createArrow(input) {
|
|
|
2022
2129
|
layerId: input.layerId ?? "",
|
|
2023
2130
|
from: input.from,
|
|
2024
2131
|
to: input.to,
|
|
2025
|
-
bend
|
|
2132
|
+
bend,
|
|
2026
2133
|
color: input.color ?? "#000000",
|
|
2027
|
-
width: input.width ?? 2
|
|
2134
|
+
width: input.width ?? 2,
|
|
2135
|
+
cachedControlPoint: getArrowControlPoint(input.from, input.to, bend)
|
|
2028
2136
|
};
|
|
2029
2137
|
if (input.fromBinding) result.fromBinding = input.fromBinding;
|
|
2030
2138
|
if (input.toBinding) result.toBinding = input.toBinding;
|
|
@@ -2275,19 +2383,19 @@ function loadImages(elements) {
|
|
|
2275
2383
|
const imageElements = elements.filter(
|
|
2276
2384
|
(el) => el.type === "image" && "src" in el
|
|
2277
2385
|
);
|
|
2278
|
-
const
|
|
2279
|
-
if (imageElements.length === 0) return Promise.resolve(
|
|
2386
|
+
const cache2 = /* @__PURE__ */ new Map();
|
|
2387
|
+
if (imageElements.length === 0) return Promise.resolve(cache2);
|
|
2280
2388
|
return new Promise((resolve) => {
|
|
2281
2389
|
let remaining = imageElements.length;
|
|
2282
2390
|
const done = () => {
|
|
2283
2391
|
remaining--;
|
|
2284
|
-
if (remaining <= 0) resolve(
|
|
2392
|
+
if (remaining <= 0) resolve(cache2);
|
|
2285
2393
|
};
|
|
2286
2394
|
for (const el of imageElements) {
|
|
2287
2395
|
const img = new Image();
|
|
2288
2396
|
img.crossOrigin = "anonymous";
|
|
2289
2397
|
img.onload = () => {
|
|
2290
|
-
|
|
2398
|
+
cache2.set(el.id, img);
|
|
2291
2399
|
done();
|
|
2292
2400
|
};
|
|
2293
2401
|
img.onerror = done;
|
|
@@ -3535,6 +3643,7 @@ var PencilTool = class {
|
|
|
3535
3643
|
layerId: ctx.activeLayerId ?? ""
|
|
3536
3644
|
});
|
|
3537
3645
|
ctx.store.add(stroke);
|
|
3646
|
+
computeStrokeSegments(stroke);
|
|
3538
3647
|
this.points = [];
|
|
3539
3648
|
ctx.requestRender();
|
|
3540
3649
|
}
|
|
@@ -4609,7 +4718,7 @@ var UpdateLayerCommand = class {
|
|
|
4609
4718
|
};
|
|
4610
4719
|
|
|
4611
4720
|
// src/index.ts
|
|
4612
|
-
var VERSION = "0.8.
|
|
4721
|
+
var VERSION = "0.8.8";
|
|
4613
4722
|
// Annotate the CommonJS export names for ESM import in node:
|
|
4614
4723
|
0 && (module.exports = {
|
|
4615
4724
|
AddElementCommand,
|
package/dist/index.d.cts
CHANGED
|
@@ -72,6 +72,8 @@ interface ArrowElement extends BaseElement {
|
|
|
72
72
|
width: number;
|
|
73
73
|
fromBinding?: Binding;
|
|
74
74
|
toBinding?: Binding;
|
|
75
|
+
/** Derived from from/to/bend. Redundant in serialized state — safe to omit. */
|
|
76
|
+
cachedControlPoint?: Point;
|
|
75
77
|
}
|
|
76
78
|
interface ImageElement extends BaseElement {
|
|
77
79
|
type: 'image';
|
|
@@ -273,8 +275,16 @@ declare class Background {
|
|
|
273
275
|
private readonly color;
|
|
274
276
|
private readonly dotRadius;
|
|
275
277
|
private readonly lineWidth;
|
|
278
|
+
private cachedCanvas;
|
|
279
|
+
private cachedCtx;
|
|
280
|
+
private lastZoom;
|
|
281
|
+
private lastOffsetX;
|
|
282
|
+
private lastOffsetY;
|
|
283
|
+
private lastWidth;
|
|
284
|
+
private lastHeight;
|
|
276
285
|
constructor(options?: BackgroundOptions);
|
|
277
286
|
render(ctx: CanvasRenderingContext2D, camera: Camera): void;
|
|
287
|
+
private ensureCachedCanvas;
|
|
278
288
|
private adaptSpacing;
|
|
279
289
|
private renderDots;
|
|
280
290
|
private renderGrid;
|
|
@@ -914,6 +924,6 @@ declare class UpdateLayerCommand implements Command {
|
|
|
914
924
|
undo(_store: ElementStore): void;
|
|
915
925
|
}
|
|
916
926
|
|
|
917
|
-
declare const VERSION = "0.8.
|
|
927
|
+
declare const VERSION = "0.8.8";
|
|
918
928
|
|
|
919
929
|
export { AddElementCommand, type ArrowElement, ArrowTool, type ArrowToolOptions, AutoSave, type AutoSaveOptions, Background, type BackgroundOptions, type BackgroundPattern, BatchCommand, type Binding, type Bounds, Camera, type CameraChangeInfo, type CameraOptions, type CanvasElement, type CanvasState, type Command, CreateLayerCommand, ElementRenderer, ElementStore, type ElementType, type ElementUpdateEvent, EraserTool, type EraserToolOptions, EventBus, type ExportImageOptions, type GridElement, HandTool, type HexOrientation, HistoryRecorder, HistoryStack, type HistoryStackOptions, type HtmlElement, type ImageElement, ImageTool, type ImageToolOptions, InputHandler, type Layer, LayerManager, NoteEditor, type NoteElement, NoteTool, type NoteToolOptions, PencilTool, type PencilToolOptions, type Point, type PointerState, Quadtree, RemoveElementCommand, RemoveLayerCommand, type RenderStatsSnapshot, SelectTool, type ShapeElement, type ShapeKind, ShapeTool, type ShapeToolOptions, type Size, type StrokeElement, type StrokePoint, type TextElement, TextTool, type TextToolOptions, type Tool, type ToolContext, ToolManager, type ToolName, UpdateElementCommand, UpdateLayerCommand, VERSION, Viewport, type ViewportOptions, boundsIntersect, clearStaleBindings, createArrow, createGrid, createHtmlElement, createId, createImage, createNote, createShape, createStroke, createText, exportImage, exportState, findBindTarget, findBoundArrows, getArrowBounds, getArrowControlPoint, getArrowMidpoint, getArrowTangentAngle, getBendFromPoint, getEdgeIntersection, getElementBounds, getElementCenter, isBindable, isNearBezier, parseState, snapPoint, unbindArrow, updateBoundArrow };
|
package/dist/index.d.ts
CHANGED
|
@@ -72,6 +72,8 @@ interface ArrowElement extends BaseElement {
|
|
|
72
72
|
width: number;
|
|
73
73
|
fromBinding?: Binding;
|
|
74
74
|
toBinding?: Binding;
|
|
75
|
+
/** Derived from from/to/bend. Redundant in serialized state — safe to omit. */
|
|
76
|
+
cachedControlPoint?: Point;
|
|
75
77
|
}
|
|
76
78
|
interface ImageElement extends BaseElement {
|
|
77
79
|
type: 'image';
|
|
@@ -273,8 +275,16 @@ declare class Background {
|
|
|
273
275
|
private readonly color;
|
|
274
276
|
private readonly dotRadius;
|
|
275
277
|
private readonly lineWidth;
|
|
278
|
+
private cachedCanvas;
|
|
279
|
+
private cachedCtx;
|
|
280
|
+
private lastZoom;
|
|
281
|
+
private lastOffsetX;
|
|
282
|
+
private lastOffsetY;
|
|
283
|
+
private lastWidth;
|
|
284
|
+
private lastHeight;
|
|
276
285
|
constructor(options?: BackgroundOptions);
|
|
277
286
|
render(ctx: CanvasRenderingContext2D, camera: Camera): void;
|
|
287
|
+
private ensureCachedCanvas;
|
|
278
288
|
private adaptSpacing;
|
|
279
289
|
private renderDots;
|
|
280
290
|
private renderGrid;
|
|
@@ -914,6 +924,6 @@ declare class UpdateLayerCommand implements Command {
|
|
|
914
924
|
undo(_store: ElementStore): void;
|
|
915
925
|
}
|
|
916
926
|
|
|
917
|
-
declare const VERSION = "0.8.
|
|
927
|
+
declare const VERSION = "0.8.8";
|
|
918
928
|
|
|
919
929
|
export { AddElementCommand, type ArrowElement, ArrowTool, type ArrowToolOptions, AutoSave, type AutoSaveOptions, Background, type BackgroundOptions, type BackgroundPattern, BatchCommand, type Binding, type Bounds, Camera, type CameraChangeInfo, type CameraOptions, type CanvasElement, type CanvasState, type Command, CreateLayerCommand, ElementRenderer, ElementStore, type ElementType, type ElementUpdateEvent, EraserTool, type EraserToolOptions, EventBus, type ExportImageOptions, type GridElement, HandTool, type HexOrientation, HistoryRecorder, HistoryStack, type HistoryStackOptions, type HtmlElement, type ImageElement, ImageTool, type ImageToolOptions, InputHandler, type Layer, LayerManager, NoteEditor, type NoteElement, NoteTool, type NoteToolOptions, PencilTool, type PencilToolOptions, type Point, type PointerState, Quadtree, RemoveElementCommand, RemoveLayerCommand, type RenderStatsSnapshot, SelectTool, type ShapeElement, type ShapeKind, ShapeTool, type ShapeToolOptions, type Size, type StrokeElement, type StrokePoint, type TextElement, TextTool, type TextToolOptions, type Tool, type ToolContext, ToolManager, type ToolName, UpdateElementCommand, UpdateLayerCommand, VERSION, Viewport, type ViewportOptions, boundsIntersect, clearStaleBindings, createArrow, createGrid, createHtmlElement, createId, createImage, createNote, createShape, createStroke, createText, exportImage, exportState, findBindTarget, findBoundArrows, getArrowBounds, getArrowControlPoint, getArrowMidpoint, getArrowTangentAngle, getBendFromPoint, getEdgeIntersection, getElementBounds, getElementCenter, isBindable, isNearBezier, parseState, snapPoint, unbindArrow, updateBoundArrow };
|
package/dist/index.js
CHANGED
|
@@ -457,6 +457,13 @@ var Background = class {
|
|
|
457
457
|
color;
|
|
458
458
|
dotRadius;
|
|
459
459
|
lineWidth;
|
|
460
|
+
cachedCanvas = null;
|
|
461
|
+
cachedCtx = null;
|
|
462
|
+
lastZoom = -1;
|
|
463
|
+
lastOffsetX = -Infinity;
|
|
464
|
+
lastOffsetY = -Infinity;
|
|
465
|
+
lastWidth = 0;
|
|
466
|
+
lastHeight = 0;
|
|
460
467
|
constructor(options = {}) {
|
|
461
468
|
this.pattern = options.pattern ?? DEFAULTS.pattern;
|
|
462
469
|
this.spacing = options.spacing ?? DEFAULTS.spacing;
|
|
@@ -472,13 +479,69 @@ var Background = class {
|
|
|
472
479
|
ctx.save();
|
|
473
480
|
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
474
481
|
ctx.clearRect(0, 0, cssWidth, cssHeight);
|
|
482
|
+
if (this.pattern === "none") {
|
|
483
|
+
ctx.restore();
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
const spacing = this.adaptSpacing(this.spacing, camera.zoom);
|
|
487
|
+
const keyZoom = camera.zoom;
|
|
488
|
+
const keyX = Math.floor(camera.position.x % spacing);
|
|
489
|
+
const keyY = Math.floor(camera.position.y % spacing);
|
|
490
|
+
if (this.cachedCanvas !== null && keyZoom === this.lastZoom && keyX === this.lastOffsetX && keyY === this.lastOffsetY && cssWidth === this.lastWidth && cssHeight === this.lastHeight) {
|
|
491
|
+
ctx.drawImage(this.cachedCanvas, 0, 0);
|
|
492
|
+
ctx.restore();
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
this.ensureCachedCanvas(cssWidth, cssHeight, dpr);
|
|
496
|
+
if (this.cachedCtx === null) {
|
|
497
|
+
if (this.pattern === "dots") {
|
|
498
|
+
this.renderDots(ctx, camera, cssWidth, cssHeight);
|
|
499
|
+
} else if (this.pattern === "grid") {
|
|
500
|
+
this.renderGrid(ctx, camera, cssWidth, cssHeight);
|
|
501
|
+
}
|
|
502
|
+
ctx.restore();
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
const offCtx = this.cachedCtx;
|
|
506
|
+
offCtx.clearRect(0, 0, cssWidth, cssHeight);
|
|
475
507
|
if (this.pattern === "dots") {
|
|
476
|
-
this.renderDots(
|
|
508
|
+
this.renderDots(offCtx, camera, cssWidth, cssHeight);
|
|
477
509
|
} else if (this.pattern === "grid") {
|
|
478
|
-
this.renderGrid(
|
|
479
|
-
}
|
|
510
|
+
this.renderGrid(offCtx, camera, cssWidth, cssHeight);
|
|
511
|
+
}
|
|
512
|
+
this.lastZoom = keyZoom;
|
|
513
|
+
this.lastOffsetX = keyX;
|
|
514
|
+
this.lastOffsetY = keyY;
|
|
515
|
+
this.lastWidth = cssWidth;
|
|
516
|
+
this.lastHeight = cssHeight;
|
|
517
|
+
ctx.drawImage(this.cachedCanvas, 0, 0);
|
|
480
518
|
ctx.restore();
|
|
481
519
|
}
|
|
520
|
+
ensureCachedCanvas(cssWidth, cssHeight, dpr) {
|
|
521
|
+
if (this.cachedCanvas !== null && this.lastWidth === cssWidth && this.lastHeight === cssHeight) {
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
const physWidth = Math.round(cssWidth * dpr);
|
|
525
|
+
const physHeight = Math.round(cssHeight * dpr);
|
|
526
|
+
if (typeof OffscreenCanvas !== "undefined") {
|
|
527
|
+
this.cachedCanvas = new OffscreenCanvas(physWidth, physHeight);
|
|
528
|
+
} else if (typeof document !== "undefined") {
|
|
529
|
+
const el = document.createElement("canvas");
|
|
530
|
+
el.width = physWidth;
|
|
531
|
+
el.height = physHeight;
|
|
532
|
+
this.cachedCanvas = el;
|
|
533
|
+
} else {
|
|
534
|
+
this.cachedCanvas = null;
|
|
535
|
+
this.cachedCtx = null;
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
const offCtx = this.cachedCanvas.getContext("2d");
|
|
539
|
+
if (offCtx !== null) {
|
|
540
|
+
offCtx.scale(dpr, dpr);
|
|
541
|
+
}
|
|
542
|
+
this.cachedCtx = offCtx;
|
|
543
|
+
this.lastZoom = -1;
|
|
544
|
+
}
|
|
482
545
|
adaptSpacing(baseSpacing, zoom) {
|
|
483
546
|
let spacing = baseSpacing * zoom;
|
|
484
547
|
while (spacing < MIN_PATTERN_SPACING) {
|
|
@@ -961,6 +1024,10 @@ var ElementStore = class {
|
|
|
961
1024
|
const existing = this.elements.get(id);
|
|
962
1025
|
if (!existing) return;
|
|
963
1026
|
const updated = { ...existing, ...partial, id: existing.id, type: existing.type };
|
|
1027
|
+
if (updated.type === "arrow") {
|
|
1028
|
+
const arrow = updated;
|
|
1029
|
+
arrow.cachedControlPoint = getArrowControlPoint(arrow.from, arrow.to, arrow.bend);
|
|
1030
|
+
}
|
|
964
1031
|
this.elements.set(id, updated);
|
|
965
1032
|
const newBounds = getElementBounds(updated);
|
|
966
1033
|
if (newBounds) {
|
|
@@ -1216,6 +1283,25 @@ function smoothToSegments(points) {
|
|
|
1216
1283
|
return segments;
|
|
1217
1284
|
}
|
|
1218
1285
|
|
|
1286
|
+
// src/elements/stroke-cache.ts
|
|
1287
|
+
var cache = /* @__PURE__ */ new WeakMap();
|
|
1288
|
+
function computeStrokeSegments(stroke) {
|
|
1289
|
+
const segments = smoothToSegments(stroke.points);
|
|
1290
|
+
const widths = [];
|
|
1291
|
+
for (const seg of segments) {
|
|
1292
|
+
const w = (pressureToWidth(seg.start.pressure, stroke.width) + pressureToWidth(seg.end.pressure, stroke.width)) / 2;
|
|
1293
|
+
widths.push(w);
|
|
1294
|
+
}
|
|
1295
|
+
const data = { segments, widths };
|
|
1296
|
+
cache.set(stroke, data);
|
|
1297
|
+
return data;
|
|
1298
|
+
}
|
|
1299
|
+
function getStrokeRenderData(stroke) {
|
|
1300
|
+
const cached = cache.get(stroke);
|
|
1301
|
+
if (cached) return cached;
|
|
1302
|
+
return computeStrokeSegments(stroke);
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1219
1305
|
// src/elements/grid-renderer.ts
|
|
1220
1306
|
function getSquareGridLines(bounds, cellSize) {
|
|
1221
1307
|
if (cellSize <= 0) return { verticals: [], horizontals: [] };
|
|
@@ -1380,9 +1466,11 @@ var ElementRenderer = class {
|
|
|
1380
1466
|
ctx.lineCap = "round";
|
|
1381
1467
|
ctx.lineJoin = "round";
|
|
1382
1468
|
ctx.globalAlpha = stroke.opacity;
|
|
1383
|
-
const segments =
|
|
1384
|
-
for (
|
|
1385
|
-
const
|
|
1469
|
+
const { segments, widths } = getStrokeRenderData(stroke);
|
|
1470
|
+
for (let i = 0; i < segments.length; i++) {
|
|
1471
|
+
const seg = segments[i];
|
|
1472
|
+
const w = widths[i];
|
|
1473
|
+
if (!seg || w === void 0) continue;
|
|
1386
1474
|
ctx.lineWidth = w;
|
|
1387
1475
|
ctx.beginPath();
|
|
1388
1476
|
ctx.moveTo(seg.start.x, seg.start.y);
|
|
@@ -1403,7 +1491,7 @@ var ElementRenderer = class {
|
|
|
1403
1491
|
ctx.beginPath();
|
|
1404
1492
|
ctx.moveTo(visualFrom.x, visualFrom.y);
|
|
1405
1493
|
if (arrow.bend !== 0) {
|
|
1406
|
-
const cp = getArrowControlPoint(arrow.from, arrow.to, arrow.bend);
|
|
1494
|
+
const cp = arrow.cachedControlPoint ?? getArrowControlPoint(arrow.from, arrow.to, arrow.bend);
|
|
1407
1495
|
ctx.quadraticCurveTo(cp.x, cp.y, visualTo.x, visualTo.y);
|
|
1408
1496
|
} else {
|
|
1409
1497
|
ctx.lineTo(visualTo.x, visualTo.y);
|
|
@@ -1544,15 +1632,33 @@ var ElementRenderer = class {
|
|
|
1544
1632
|
renderImage(ctx, image) {
|
|
1545
1633
|
const img = this.getImage(image.src);
|
|
1546
1634
|
if (!img) return;
|
|
1547
|
-
ctx.drawImage(
|
|
1635
|
+
ctx.drawImage(
|
|
1636
|
+
img,
|
|
1637
|
+
image.position.x,
|
|
1638
|
+
image.position.y,
|
|
1639
|
+
image.size.w,
|
|
1640
|
+
image.size.h
|
|
1641
|
+
);
|
|
1548
1642
|
}
|
|
1549
1643
|
getImage(src) {
|
|
1550
1644
|
const cached = this.imageCache.get(src);
|
|
1551
|
-
if (cached)
|
|
1645
|
+
if (cached) {
|
|
1646
|
+
if (cached instanceof HTMLImageElement) return cached.complete ? cached : null;
|
|
1647
|
+
return cached;
|
|
1648
|
+
}
|
|
1552
1649
|
const img = new Image();
|
|
1553
1650
|
img.src = src;
|
|
1554
1651
|
this.imageCache.set(src, img);
|
|
1555
|
-
img.onload = () =>
|
|
1652
|
+
img.onload = () => {
|
|
1653
|
+
this.onImageLoad?.();
|
|
1654
|
+
if (typeof createImageBitmap !== "undefined") {
|
|
1655
|
+
createImageBitmap(img).then((bitmap) => {
|
|
1656
|
+
this.imageCache.set(src, bitmap);
|
|
1657
|
+
this.onImageLoad?.();
|
|
1658
|
+
}).catch(() => {
|
|
1659
|
+
});
|
|
1660
|
+
}
|
|
1661
|
+
};
|
|
1556
1662
|
return null;
|
|
1557
1663
|
}
|
|
1558
1664
|
};
|
|
@@ -1928,6 +2034,7 @@ function createNote(input) {
|
|
|
1928
2034
|
};
|
|
1929
2035
|
}
|
|
1930
2036
|
function createArrow(input) {
|
|
2037
|
+
const bend = input.bend ?? 0;
|
|
1931
2038
|
const result = {
|
|
1932
2039
|
id: createId("arrow"),
|
|
1933
2040
|
type: "arrow",
|
|
@@ -1937,9 +2044,10 @@ function createArrow(input) {
|
|
|
1937
2044
|
layerId: input.layerId ?? "",
|
|
1938
2045
|
from: input.from,
|
|
1939
2046
|
to: input.to,
|
|
1940
|
-
bend
|
|
2047
|
+
bend,
|
|
1941
2048
|
color: input.color ?? "#000000",
|
|
1942
|
-
width: input.width ?? 2
|
|
2049
|
+
width: input.width ?? 2,
|
|
2050
|
+
cachedControlPoint: getArrowControlPoint(input.from, input.to, bend)
|
|
1943
2051
|
};
|
|
1944
2052
|
if (input.fromBinding) result.fromBinding = input.fromBinding;
|
|
1945
2053
|
if (input.toBinding) result.toBinding = input.toBinding;
|
|
@@ -2190,19 +2298,19 @@ function loadImages(elements) {
|
|
|
2190
2298
|
const imageElements = elements.filter(
|
|
2191
2299
|
(el) => el.type === "image" && "src" in el
|
|
2192
2300
|
);
|
|
2193
|
-
const
|
|
2194
|
-
if (imageElements.length === 0) return Promise.resolve(
|
|
2301
|
+
const cache2 = /* @__PURE__ */ new Map();
|
|
2302
|
+
if (imageElements.length === 0) return Promise.resolve(cache2);
|
|
2195
2303
|
return new Promise((resolve) => {
|
|
2196
2304
|
let remaining = imageElements.length;
|
|
2197
2305
|
const done = () => {
|
|
2198
2306
|
remaining--;
|
|
2199
|
-
if (remaining <= 0) resolve(
|
|
2307
|
+
if (remaining <= 0) resolve(cache2);
|
|
2200
2308
|
};
|
|
2201
2309
|
for (const el of imageElements) {
|
|
2202
2310
|
const img = new Image();
|
|
2203
2311
|
img.crossOrigin = "anonymous";
|
|
2204
2312
|
img.onload = () => {
|
|
2205
|
-
|
|
2313
|
+
cache2.set(el.id, img);
|
|
2206
2314
|
done();
|
|
2207
2315
|
};
|
|
2208
2316
|
img.onerror = done;
|
|
@@ -3450,6 +3558,7 @@ var PencilTool = class {
|
|
|
3450
3558
|
layerId: ctx.activeLayerId ?? ""
|
|
3451
3559
|
});
|
|
3452
3560
|
ctx.store.add(stroke);
|
|
3561
|
+
computeStrokeSegments(stroke);
|
|
3453
3562
|
this.points = [];
|
|
3454
3563
|
ctx.requestRender();
|
|
3455
3564
|
}
|
|
@@ -4524,7 +4633,7 @@ var UpdateLayerCommand = class {
|
|
|
4524
4633
|
};
|
|
4525
4634
|
|
|
4526
4635
|
// src/index.ts
|
|
4527
|
-
var VERSION = "0.8.
|
|
4636
|
+
var VERSION = "0.8.8";
|
|
4528
4637
|
export {
|
|
4529
4638
|
AddElementCommand,
|
|
4530
4639
|
ArrowTool,
|