@tsdraw/core 0.8.5 → 0.9.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/dist/index.cjs +169 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -2
- package/dist/index.d.ts +19 -2
- package/dist/index.js +169 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -372,9 +372,12 @@ function resolveThemeColor(colorStyle, theme) {
|
|
|
372
372
|
return DARK_COLORS[colorStyle] ?? lightThemeColor;
|
|
373
373
|
}
|
|
374
374
|
var CanvasRenderer = class {
|
|
375
|
-
|
|
375
|
+
_theme = "light";
|
|
376
|
+
get theme() {
|
|
377
|
+
return this._theme;
|
|
378
|
+
}
|
|
376
379
|
setTheme(theme) {
|
|
377
|
-
this.
|
|
380
|
+
this._theme = theme;
|
|
378
381
|
}
|
|
379
382
|
render(ctx, viewport, shapes) {
|
|
380
383
|
ctx.save();
|
|
@@ -392,7 +395,7 @@ var CanvasRenderer = class {
|
|
|
392
395
|
const width = (STROKE_WIDTHS[shape.props.size] ?? 3.5) * shape.props.scale;
|
|
393
396
|
const samples = flattenSegments(shape);
|
|
394
397
|
if (samples.length === 0) return;
|
|
395
|
-
const color = resolveThemeColor(shape.props.color, this.
|
|
398
|
+
const color = resolveThemeColor(shape.props.color, this._theme);
|
|
396
399
|
const fillStyle = shape.props.fill ?? "none";
|
|
397
400
|
if (shape.props.isClosed && fillStyle !== "none") {
|
|
398
401
|
this.paintClosedShapeFill(ctx, samples, color, fillStyle);
|
|
@@ -458,7 +461,7 @@ var CanvasRenderer = class {
|
|
|
458
461
|
ctx.fillStyle = color;
|
|
459
462
|
ctx.globalAlpha = 0.55;
|
|
460
463
|
} else if (fillStyle === "none") {
|
|
461
|
-
ctx.fillStyle = this.
|
|
464
|
+
ctx.fillStyle = this._theme === "dark" ? "#0f0f0f" : "#fafafa";
|
|
462
465
|
ctx.globalAlpha = 1;
|
|
463
466
|
} else {
|
|
464
467
|
ctx.fillStyle = color;
|
|
@@ -554,6 +557,167 @@ function getLineDash(dash, width) {
|
|
|
554
557
|
}
|
|
555
558
|
}
|
|
556
559
|
|
|
560
|
+
// src/canvas/backgroundRenderer.ts
|
|
561
|
+
var DEFAULT_SPACING = 20;
|
|
562
|
+
var DEFAULT_LINE_WIDTH = 0.5;
|
|
563
|
+
var DEFAULT_DOT_RADIUS = 1;
|
|
564
|
+
var DEFAULT_OPACITY = 1;
|
|
565
|
+
var LINE_GAP_FADE_IN = 3;
|
|
566
|
+
var LINE_GAP_FADE_FULL = 8;
|
|
567
|
+
var DOT_GAP_FADE_IN = 2;
|
|
568
|
+
var DOT_GAP_FADE_FULL = 16;
|
|
569
|
+
function resolvePresetPatternColor(colorLight, colorDark, theme) {
|
|
570
|
+
if (theme === "dark") return colorDark ?? colorLight ?? "#888888";
|
|
571
|
+
return colorLight ?? "#c0c0c0";
|
|
572
|
+
}
|
|
573
|
+
function visiblePageRect(viewport, canvasWidth, canvasHeight) {
|
|
574
|
+
return {
|
|
575
|
+
minX: (0 - viewport.x) / viewport.zoom,
|
|
576
|
+
minY: (0 - viewport.y) / viewport.zoom,
|
|
577
|
+
maxX: (canvasWidth - viewport.x) / viewport.zoom,
|
|
578
|
+
maxY: (canvasHeight - viewport.y) / viewport.zoom
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
function isOnGrid(value, gridSpacing) {
|
|
582
|
+
const remainder = (value % gridSpacing + gridSpacing) % gridSpacing;
|
|
583
|
+
return remainder < 0.5 || remainder > gridSpacing - 0.5;
|
|
584
|
+
}
|
|
585
|
+
function fadeForScreenGap(screenGap, fadeIn, fadeFull) {
|
|
586
|
+
return Math.min(1, Math.max(0, (screenGap - fadeIn) / (fadeFull - fadeIn)));
|
|
587
|
+
}
|
|
588
|
+
function buildLevels(baseSpacing, zoom, fadeIn, fadeFull) {
|
|
589
|
+
let topSpacing = baseSpacing;
|
|
590
|
+
while (topSpacing * zoom < fadeFull) {
|
|
591
|
+
topSpacing *= 2;
|
|
592
|
+
}
|
|
593
|
+
const levels = [];
|
|
594
|
+
for (let s = topSpacing; s >= baseSpacing; s /= 2) {
|
|
595
|
+
const fade = fadeForScreenGap(s * zoom, fadeIn, fadeFull);
|
|
596
|
+
if (fade < 0.01) break;
|
|
597
|
+
levels.push({ spacing: s, fade });
|
|
598
|
+
}
|
|
599
|
+
return levels;
|
|
600
|
+
}
|
|
601
|
+
function drawDotTile(ctx, physicalWidth, physicalHeight, panXPx, panYPx, exactTileSize, radiusPx, color, alpha) {
|
|
602
|
+
if (alpha < 5e-3 || exactTileSize < 2) return;
|
|
603
|
+
const tilePixels = Math.ceil(exactTileSize);
|
|
604
|
+
const tileScale = exactTileSize / tilePixels;
|
|
605
|
+
const center = tilePixels / 2;
|
|
606
|
+
const tile = new OffscreenCanvas(tilePixels, tilePixels);
|
|
607
|
+
const tctx = tile.getContext("2d");
|
|
608
|
+
tctx.fillStyle = color;
|
|
609
|
+
tctx.beginPath();
|
|
610
|
+
tctx.arc(center, center, radiusPx / tileScale, 0, Math.PI * 2);
|
|
611
|
+
tctx.fill();
|
|
612
|
+
const pattern = ctx.createPattern(tile, "repeat");
|
|
613
|
+
if (!pattern) return;
|
|
614
|
+
pattern.setTransform(
|
|
615
|
+
new DOMMatrix().translateSelf(panXPx - exactTileSize / 2, panYPx - exactTileSize / 2).scaleSelf(tileScale, tileScale)
|
|
616
|
+
);
|
|
617
|
+
ctx.save();
|
|
618
|
+
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
619
|
+
ctx.globalAlpha = alpha;
|
|
620
|
+
ctx.fillStyle = pattern;
|
|
621
|
+
ctx.fillRect(0, 0, physicalWidth, physicalHeight);
|
|
622
|
+
ctx.restore();
|
|
623
|
+
}
|
|
624
|
+
function drawMergingDots(ctx, viewport, canvasWidth, canvasHeight, baseSpacing, dotRadius, color, opacity) {
|
|
625
|
+
const dpr = ctx.getTransform().a;
|
|
626
|
+
const levels = buildLevels(baseSpacing, viewport.zoom, DOT_GAP_FADE_IN, DOT_GAP_FADE_FULL);
|
|
627
|
+
if (levels.length === 0) return;
|
|
628
|
+
const physicalWidth = canvasWidth * dpr;
|
|
629
|
+
const physicalHeight = canvasHeight * dpr;
|
|
630
|
+
const panXPx = viewport.x * dpr;
|
|
631
|
+
const panYPx = viewport.y * dpr;
|
|
632
|
+
const radiusPx = dotRadius * dpr;
|
|
633
|
+
for (const level of levels) {
|
|
634
|
+
const tileSize = level.spacing * viewport.zoom * dpr;
|
|
635
|
+
drawDotTile(ctx, physicalWidth, physicalHeight, panXPx, panYPx, tileSize, radiusPx, color, opacity * level.fade);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
function drawHorizontalLinesForLevel(ctx, visible, spacing, skipSpacing, lineWidth, color, alpha) {
|
|
639
|
+
const startY = Math.floor(visible.minY / spacing) * spacing;
|
|
640
|
+
ctx.save();
|
|
641
|
+
ctx.strokeStyle = color;
|
|
642
|
+
ctx.lineWidth = lineWidth;
|
|
643
|
+
ctx.globalAlpha = alpha;
|
|
644
|
+
ctx.beginPath();
|
|
645
|
+
for (let y = startY; y <= visible.maxY; y += spacing) {
|
|
646
|
+
if (skipSpacing > 0 && isOnGrid(y, skipSpacing)) continue;
|
|
647
|
+
ctx.moveTo(visible.minX, y);
|
|
648
|
+
ctx.lineTo(visible.maxX, y);
|
|
649
|
+
}
|
|
650
|
+
ctx.stroke();
|
|
651
|
+
ctx.restore();
|
|
652
|
+
}
|
|
653
|
+
function drawMergingLines(ctx, visible, baseSpacing, lineWidth, color, opacity, zoom) {
|
|
654
|
+
const compensatedWidth = lineWidth / ctx.getTransform().a;
|
|
655
|
+
const levels = buildLevels(baseSpacing, zoom, LINE_GAP_FADE_IN, LINE_GAP_FADE_FULL);
|
|
656
|
+
for (let i = 0; i < levels.length; i++) {
|
|
657
|
+
const level = levels[i];
|
|
658
|
+
const coarserSpacing = i > 0 ? levels[i - 1].spacing : 0;
|
|
659
|
+
drawHorizontalLinesForLevel(ctx, visible, level.spacing, coarserSpacing, compensatedWidth, color, opacity * level.fade);
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
function drawGridForLevel(ctx, visible, spacing, skipSpacing, lineWidth, color, alpha) {
|
|
663
|
+
const startX = Math.floor(visible.minX / spacing) * spacing;
|
|
664
|
+
const startY = Math.floor(visible.minY / spacing) * spacing;
|
|
665
|
+
ctx.save();
|
|
666
|
+
ctx.strokeStyle = color;
|
|
667
|
+
ctx.lineWidth = lineWidth;
|
|
668
|
+
ctx.globalAlpha = alpha;
|
|
669
|
+
ctx.beginPath();
|
|
670
|
+
for (let x = startX; x <= visible.maxX; x += spacing) {
|
|
671
|
+
if (skipSpacing > 0 && isOnGrid(x, skipSpacing)) continue;
|
|
672
|
+
ctx.moveTo(x, visible.minY);
|
|
673
|
+
ctx.lineTo(x, visible.maxY);
|
|
674
|
+
}
|
|
675
|
+
for (let y = startY; y <= visible.maxY; y += spacing) {
|
|
676
|
+
if (skipSpacing > 0 && isOnGrid(y, skipSpacing)) continue;
|
|
677
|
+
ctx.moveTo(visible.minX, y);
|
|
678
|
+
ctx.lineTo(visible.maxX, y);
|
|
679
|
+
}
|
|
680
|
+
ctx.stroke();
|
|
681
|
+
ctx.restore();
|
|
682
|
+
}
|
|
683
|
+
function drawMergingGrid(ctx, visible, baseSpacing, lineWidth, color, opacity, zoom) {
|
|
684
|
+
const compensatedWidth = lineWidth / ctx.getTransform().a;
|
|
685
|
+
const levels = buildLevels(baseSpacing, zoom, LINE_GAP_FADE_IN, LINE_GAP_FADE_FULL);
|
|
686
|
+
for (let i = 0; i < levels.length; i++) {
|
|
687
|
+
const level = levels[i];
|
|
688
|
+
const coarserSpacing = i > 0 ? levels[i - 1].spacing : 0;
|
|
689
|
+
drawGridForLevel(ctx, visible, level.spacing, coarserSpacing, compensatedWidth, color, opacity * level.fade);
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
function renderCanvasBackground(ctx, viewport, canvasWidth, canvasHeight, options, theme) {
|
|
693
|
+
if (!options || options.type === "blank") return;
|
|
694
|
+
if (options.type === "custom") {
|
|
695
|
+
options.render(ctx, viewport, canvasWidth, canvasHeight);
|
|
696
|
+
return;
|
|
697
|
+
}
|
|
698
|
+
const baseSpacing = options.spacing ?? DEFAULT_SPACING;
|
|
699
|
+
if (baseSpacing <= 0) return;
|
|
700
|
+
const color = resolvePresetPatternColor(options.color, options.colorDark, theme);
|
|
701
|
+
const opacity = options.opacity ?? DEFAULT_OPACITY;
|
|
702
|
+
if (options.type === "dots") {
|
|
703
|
+
drawMergingDots(ctx, viewport, canvasWidth, canvasHeight, baseSpacing, options.size ?? DEFAULT_DOT_RADIUS, color, opacity);
|
|
704
|
+
return;
|
|
705
|
+
}
|
|
706
|
+
const visible = visiblePageRect(viewport, canvasWidth, canvasHeight);
|
|
707
|
+
ctx.save();
|
|
708
|
+
ctx.translate(viewport.x, viewport.y);
|
|
709
|
+
ctx.scale(viewport.zoom, viewport.zoom);
|
|
710
|
+
switch (options.type) {
|
|
711
|
+
case "lines":
|
|
712
|
+
drawMergingLines(ctx, visible, baseSpacing, options.size ?? DEFAULT_LINE_WIDTH, color, opacity, viewport.zoom);
|
|
713
|
+
break;
|
|
714
|
+
case "grid":
|
|
715
|
+
drawMergingGrid(ctx, visible, baseSpacing, options.size ?? DEFAULT_LINE_WIDTH, color, opacity, viewport.zoom);
|
|
716
|
+
break;
|
|
717
|
+
}
|
|
718
|
+
ctx.restore();
|
|
719
|
+
}
|
|
720
|
+
|
|
557
721
|
// src/input/inputManager.ts
|
|
558
722
|
var InputManager = class {
|
|
559
723
|
_current = { x: 0, y: 0 };
|
|
@@ -2318,6 +2482,7 @@ exports.pageToScreen = pageToScreen;
|
|
|
2318
2482
|
exports.panViewport = panViewport;
|
|
2319
2483
|
exports.pointHitsShape = pointHitsShape;
|
|
2320
2484
|
exports.recordsToDocumentSnapshot = recordsToDocumentSnapshot;
|
|
2485
|
+
exports.renderCanvasBackground = renderCanvasBackground;
|
|
2321
2486
|
exports.resolveThemeColor = resolveThemeColor;
|
|
2322
2487
|
exports.rotatePoint = rotatePoint;
|
|
2323
2488
|
exports.screenToPage = screenToPage;
|