@fieldnotes/core 0.66.0 → 0.68.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 +742 -706
- package/dist/index.cjs +405 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +59 -9
- package/dist/index.d.ts +59 -9
- package/dist/index.js +404 -34
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -126,6 +126,7 @@ __export(index_exports, {
|
|
|
126
126
|
isPingPresence: () => isPingPresence,
|
|
127
127
|
pathDistanceCells: () => pathDistanceCells,
|
|
128
128
|
recommendedFogCellSize: () => recommendedFogCellSize,
|
|
129
|
+
resolveFogStyle: () => resolveFogStyle,
|
|
129
130
|
resolveHtmlRouting: () => resolveHtmlRouting,
|
|
130
131
|
setFontSize: () => setFontSize,
|
|
131
132
|
smartSnap: () => smartSnap,
|
|
@@ -7325,19 +7326,196 @@ async function renderHtmlElements(elements, options) {
|
|
|
7325
7326
|
return sources;
|
|
7326
7327
|
}
|
|
7327
7328
|
|
|
7329
|
+
// src/fog/fog-style.ts
|
|
7330
|
+
var DEFAULT_PROCEDURAL_OPACITY = 0.6;
|
|
7331
|
+
var DEFAULT_PROCEDURAL_SCALE = 256;
|
|
7332
|
+
var DEFAULT_PROCEDURAL_SEED = 0;
|
|
7333
|
+
var DEFAULT_PROCEDURAL_DETAIL = 2;
|
|
7334
|
+
var DEFAULT_PROCEDURAL_TINT = "#ffffff";
|
|
7335
|
+
var MIN_SCALE = 64;
|
|
7336
|
+
var MAX_SCALE = 1024;
|
|
7337
|
+
var MIN_DETAIL = 1;
|
|
7338
|
+
var MAX_DETAIL = 4;
|
|
7339
|
+
var MAX_SEED = 65535;
|
|
7340
|
+
function clamp(value, min, max) {
|
|
7341
|
+
return Math.max(min, Math.min(max, value));
|
|
7342
|
+
}
|
|
7343
|
+
function finiteOrDefault(value, defaultValue) {
|
|
7344
|
+
return value !== void 0 && Number.isFinite(value) ? value : defaultValue;
|
|
7345
|
+
}
|
|
7346
|
+
function resolveFogStyle(style, legacyColor, defaultColor) {
|
|
7347
|
+
if (style && style.kind === "procedural") {
|
|
7348
|
+
const opacity = clamp(finiteOrDefault(style.opacity, DEFAULT_PROCEDURAL_OPACITY), 0, 1);
|
|
7349
|
+
const scale = clamp(
|
|
7350
|
+
finiteOrDefault(style.scale, DEFAULT_PROCEDURAL_SCALE),
|
|
7351
|
+
MIN_SCALE,
|
|
7352
|
+
MAX_SCALE
|
|
7353
|
+
);
|
|
7354
|
+
const seed = clamp(
|
|
7355
|
+
Math.floor(finiteOrDefault(style.seed, DEFAULT_PROCEDURAL_SEED)),
|
|
7356
|
+
0,
|
|
7357
|
+
MAX_SEED
|
|
7358
|
+
);
|
|
7359
|
+
const detail = clamp(
|
|
7360
|
+
Math.floor(finiteOrDefault(style.detail, DEFAULT_PROCEDURAL_DETAIL)),
|
|
7361
|
+
MIN_DETAIL,
|
|
7362
|
+
MAX_DETAIL
|
|
7363
|
+
);
|
|
7364
|
+
return {
|
|
7365
|
+
kind: "procedural",
|
|
7366
|
+
backdrop: style.backdrop,
|
|
7367
|
+
// `tint` is required for typed callers. Keep a visible runtime fallback for
|
|
7368
|
+
// untyped/older JavaScript hosts instead of producing a flat same-color overlay.
|
|
7369
|
+
tint: style.tint || DEFAULT_PROCEDURAL_TINT,
|
|
7370
|
+
opacity,
|
|
7371
|
+
scale,
|
|
7372
|
+
seed,
|
|
7373
|
+
detail
|
|
7374
|
+
};
|
|
7375
|
+
}
|
|
7376
|
+
if (style && (!style.kind || style.kind === "solid")) {
|
|
7377
|
+
return { kind: "solid", color: style.color };
|
|
7378
|
+
}
|
|
7379
|
+
return { kind: "solid", color: legacyColor ?? defaultColor };
|
|
7380
|
+
}
|
|
7381
|
+
|
|
7382
|
+
// src/fog/fog-procedural-tile.ts
|
|
7383
|
+
var TILE_PX = 128;
|
|
7384
|
+
function xorshift32(state) {
|
|
7385
|
+
state ^= state << 13;
|
|
7386
|
+
state ^= state >>> 17;
|
|
7387
|
+
state ^= state << 5;
|
|
7388
|
+
return state >>> 0;
|
|
7389
|
+
}
|
|
7390
|
+
function seedState(seed) {
|
|
7391
|
+
return seed * 2654435761 + 1 >>> 0 || 1;
|
|
7392
|
+
}
|
|
7393
|
+
function smoothstep(t) {
|
|
7394
|
+
return t * t * (3 - 2 * t);
|
|
7395
|
+
}
|
|
7396
|
+
function lerp(a, b, t) {
|
|
7397
|
+
return a + (b - a) * t;
|
|
7398
|
+
}
|
|
7399
|
+
function generateGradients(size, prngState) {
|
|
7400
|
+
const count = size * size;
|
|
7401
|
+
const gx = new Float32Array(count);
|
|
7402
|
+
const gy = new Float32Array(count);
|
|
7403
|
+
let s = prngState;
|
|
7404
|
+
for (let i = 0; i < count; i++) {
|
|
7405
|
+
s = xorshift32(s);
|
|
7406
|
+
const angle = (s >>> 0) / 4294967296 * Math.PI * 2;
|
|
7407
|
+
gx[i] = Math.cos(angle);
|
|
7408
|
+
gy[i] = Math.sin(angle);
|
|
7409
|
+
}
|
|
7410
|
+
return { gx, gy, state: s };
|
|
7411
|
+
}
|
|
7412
|
+
function perlinNoise(px, py, gridSize, gx, gy) {
|
|
7413
|
+
const gx0 = Math.floor(px) % gridSize;
|
|
7414
|
+
const gy0 = Math.floor(py) % gridSize;
|
|
7415
|
+
const gx1 = (gx0 + 1) % gridSize;
|
|
7416
|
+
const gy1 = (gy0 + 1) % gridSize;
|
|
7417
|
+
const fx = px - Math.floor(px);
|
|
7418
|
+
const fy = py - Math.floor(py);
|
|
7419
|
+
const sx = smoothstep(fx);
|
|
7420
|
+
const sy = smoothstep(fy);
|
|
7421
|
+
const dot = (ix, iy, dx, dy) => {
|
|
7422
|
+
const idx = iy * gridSize + ix;
|
|
7423
|
+
return gx[idx] * dx + gy[idx] * dy;
|
|
7424
|
+
};
|
|
7425
|
+
const n00 = dot(gx0, gy0, fx, fy);
|
|
7426
|
+
const n10 = dot(gx1, gy0, fx - 1, fy);
|
|
7427
|
+
const n01 = dot(gx0, gy1, fx, fy - 1);
|
|
7428
|
+
const n11 = dot(gx1, gy1, fx - 1, fy - 1);
|
|
7429
|
+
return lerp(lerp(n00, n10, sx), lerp(n01, n11, sx), sy);
|
|
7430
|
+
}
|
|
7431
|
+
function layeredNoise(x, y, octaves, gridSize, gx, gy) {
|
|
7432
|
+
let value = 0;
|
|
7433
|
+
let amplitude = 1;
|
|
7434
|
+
let frequency = 1;
|
|
7435
|
+
let maxAmplitude = 0;
|
|
7436
|
+
for (let o = 0; o < octaves; o++) {
|
|
7437
|
+
value += perlinNoise(x * frequency, y * frequency, gridSize * frequency, gx, gy) * amplitude;
|
|
7438
|
+
maxAmplitude += amplitude;
|
|
7439
|
+
amplitude *= 0.5;
|
|
7440
|
+
frequency *= 2;
|
|
7441
|
+
}
|
|
7442
|
+
return (value / maxAmplitude + 1) * 0.5;
|
|
7443
|
+
}
|
|
7444
|
+
function generateProceduralTile(style) {
|
|
7445
|
+
const gridSize = 8;
|
|
7446
|
+
const maxFreq = gridSize * (1 << style.detail - 1);
|
|
7447
|
+
const { gx, gy } = generateGradients(maxFreq, seedState(style.seed));
|
|
7448
|
+
const data = new Uint8ClampedArray(TILE_PX * TILE_PX * 4);
|
|
7449
|
+
for (let py = 0; py < TILE_PX; py++) {
|
|
7450
|
+
for (let px = 0; px < TILE_PX; px++) {
|
|
7451
|
+
const nx = px / TILE_PX * gridSize;
|
|
7452
|
+
const ny = py / TILE_PX * gridSize;
|
|
7453
|
+
const n2 = layeredNoise(nx, ny, style.detail, gridSize, gx, gy);
|
|
7454
|
+
const alpha = Math.round(n2 * style.opacity * 255);
|
|
7455
|
+
const idx = (py * TILE_PX + px) * 4;
|
|
7456
|
+
data[idx] = 255;
|
|
7457
|
+
data[idx + 1] = 255;
|
|
7458
|
+
data[idx + 2] = 255;
|
|
7459
|
+
data[idx + 3] = alpha;
|
|
7460
|
+
}
|
|
7461
|
+
}
|
|
7462
|
+
return { data, width: TILE_PX, height: TILE_PX };
|
|
7463
|
+
}
|
|
7464
|
+
var tileCache = /* @__PURE__ */ new Map();
|
|
7465
|
+
var MAX_CACHED_TILES = 16;
|
|
7466
|
+
function getCachedProceduralTile(style) {
|
|
7467
|
+
const key = `${style.opacity}\0${style.seed}\0${style.detail}`;
|
|
7468
|
+
const cached = tileCache.get(key);
|
|
7469
|
+
if (cached) return cached;
|
|
7470
|
+
const tile = generateProceduralTile(style);
|
|
7471
|
+
if (tileCache.size >= MAX_CACHED_TILES) {
|
|
7472
|
+
const oldest = tileCache.keys().next().value;
|
|
7473
|
+
if (oldest !== void 0) tileCache.delete(oldest);
|
|
7474
|
+
}
|
|
7475
|
+
tileCache.set(key, tile);
|
|
7476
|
+
return tile;
|
|
7477
|
+
}
|
|
7478
|
+
function clearProceduralTileCache() {
|
|
7479
|
+
tileCache.clear();
|
|
7480
|
+
}
|
|
7481
|
+
|
|
7328
7482
|
// src/fog/fog-renderer.ts
|
|
7329
7483
|
var DEFAULT_EDITOR_COLOR = "rgba(30, 40, 60, 0.45)";
|
|
7330
7484
|
var DEFAULT_PLAYER_COLOR = "#0b1020";
|
|
7331
7485
|
var FogRenderer = class {
|
|
7332
7486
|
tileCache = /* @__PURE__ */ new Map();
|
|
7487
|
+
patternCache = /* @__PURE__ */ new Map();
|
|
7333
7488
|
state = null;
|
|
7334
7489
|
viewMode = "off";
|
|
7335
7490
|
dirty = true;
|
|
7336
|
-
|
|
7337
|
-
|
|
7491
|
+
editorStyle;
|
|
7492
|
+
playerStyle;
|
|
7338
7493
|
constructor(options = {}) {
|
|
7339
|
-
this.
|
|
7340
|
-
|
|
7494
|
+
this.editorStyle = resolveFogStyle(
|
|
7495
|
+
options.editorStyle,
|
|
7496
|
+
options.editorColor,
|
|
7497
|
+
DEFAULT_EDITOR_COLOR
|
|
7498
|
+
);
|
|
7499
|
+
this.playerStyle = resolveFogStyle(
|
|
7500
|
+
options.playerStyle,
|
|
7501
|
+
options.playerColor,
|
|
7502
|
+
DEFAULT_PLAYER_COLOR
|
|
7503
|
+
);
|
|
7504
|
+
}
|
|
7505
|
+
setOptions(options) {
|
|
7506
|
+
this.editorStyle = resolveFogStyle(
|
|
7507
|
+
options.editorStyle,
|
|
7508
|
+
options.editorColor,
|
|
7509
|
+
DEFAULT_EDITOR_COLOR
|
|
7510
|
+
);
|
|
7511
|
+
this.playerStyle = resolveFogStyle(
|
|
7512
|
+
options.playerStyle,
|
|
7513
|
+
options.playerColor,
|
|
7514
|
+
DEFAULT_PLAYER_COLOR
|
|
7515
|
+
);
|
|
7516
|
+
this.tileCache.clear();
|
|
7517
|
+
this.patternCache.clear();
|
|
7518
|
+
this.dirty = true;
|
|
7341
7519
|
}
|
|
7342
7520
|
setState(state) {
|
|
7343
7521
|
this.state = state;
|
|
@@ -7363,12 +7541,23 @@ var FogRenderer = class {
|
|
|
7363
7541
|
isVisible() {
|
|
7364
7542
|
return this.viewMode !== "off" && this.state !== null;
|
|
7365
7543
|
}
|
|
7544
|
+
getResolvedStyle(mode) {
|
|
7545
|
+
return mode === "editor" ? this.editorStyle : this.playerStyle;
|
|
7546
|
+
}
|
|
7366
7547
|
render(ctx, camera, viewportWidth, viewportHeight, _dpr) {
|
|
7367
7548
|
if (!this.state || this.viewMode === "off") return;
|
|
7368
7549
|
const def = this.state.definition;
|
|
7369
7550
|
const cellSize = def.cellSize;
|
|
7370
7551
|
const tileWorldSize = FOG_TILE_CELLS * cellSize;
|
|
7371
|
-
const
|
|
7552
|
+
const mode = this.viewMode === "editor" ? "editor" : "player";
|
|
7553
|
+
const style = this.getResolvedStyle(mode);
|
|
7554
|
+
const proceduralStyle = style.kind === "procedural" ? style : null;
|
|
7555
|
+
const color = style.kind === "procedural" ? normalizeCanvasColor(
|
|
7556
|
+
ctx,
|
|
7557
|
+
style.backdrop,
|
|
7558
|
+
mode === "player" ? DEFAULT_PLAYER_COLOR : DEFAULT_EDITOR_COLOR
|
|
7559
|
+
) : style.color;
|
|
7560
|
+
const safetyColor = proceduralStyle && mode === "player" ? DEFAULT_PLAYER_COLOR : null;
|
|
7372
7561
|
const worldBounds = getVisibleWorld(camera, viewportWidth, viewportHeight);
|
|
7373
7562
|
const minTX = Math.floor(Math.max(def.bounds.x, worldBounds.x) / tileWorldSize);
|
|
7374
7563
|
const minTY = Math.floor(Math.max(def.bounds.y, worldBounds.y) / tileWorldSize);
|
|
@@ -7399,7 +7588,22 @@ var FogRenderer = class {
|
|
|
7399
7588
|
const clipR = Math.min(tileWorldX + tileWorldSize, def.bounds.x + def.bounds.w);
|
|
7400
7589
|
const clipB = Math.min(tileWorldY + tileWorldSize, def.bounds.y + def.bounds.h);
|
|
7401
7590
|
if (clipR > clipX && clipB > clipY) {
|
|
7591
|
+
if (safetyColor) {
|
|
7592
|
+
ctx.fillStyle = safetyColor;
|
|
7593
|
+
ctx.fillRect(clipX, clipY, clipR - clipX, clipB - clipY);
|
|
7594
|
+
}
|
|
7595
|
+
ctx.fillStyle = color;
|
|
7402
7596
|
ctx.fillRect(clipX, clipY, clipR - clipX, clipB - clipY);
|
|
7597
|
+
if (proceduralStyle) {
|
|
7598
|
+
this.paintProceduralOverlay(
|
|
7599
|
+
ctx,
|
|
7600
|
+
proceduralStyle,
|
|
7601
|
+
clipX,
|
|
7602
|
+
clipY,
|
|
7603
|
+
clipR - clipX,
|
|
7604
|
+
clipB - clipY
|
|
7605
|
+
);
|
|
7606
|
+
}
|
|
7403
7607
|
}
|
|
7404
7608
|
continue;
|
|
7405
7609
|
}
|
|
@@ -7407,18 +7611,33 @@ var FogRenderer = class {
|
|
|
7407
7611
|
continue;
|
|
7408
7612
|
}
|
|
7409
7613
|
if (data) {
|
|
7614
|
+
if (safetyColor) this.renderTile(ctx, data, tx, ty, def, safetyColor);
|
|
7410
7615
|
this.renderTile(ctx, data, tx, ty, def, color);
|
|
7616
|
+
if (proceduralStyle) {
|
|
7617
|
+
this.renderTileProceduralOverlay(ctx, data, tx, ty, def, proceduralStyle);
|
|
7618
|
+
}
|
|
7411
7619
|
}
|
|
7412
7620
|
}
|
|
7413
7621
|
}
|
|
7414
7622
|
ctx.restore();
|
|
7415
7623
|
this.dirty = false;
|
|
7416
7624
|
}
|
|
7417
|
-
renderForExport(ctx, state, mode, color) {
|
|
7625
|
+
renderForExport(ctx, state, mode, color, style) {
|
|
7418
7626
|
const def = state.definition;
|
|
7419
7627
|
const cellSize = def.cellSize;
|
|
7420
7628
|
const tileWorldSize = FOG_TILE_CELLS * cellSize;
|
|
7421
|
-
const
|
|
7629
|
+
const resolved = style ? resolveFogStyle(
|
|
7630
|
+
style,
|
|
7631
|
+
void 0,
|
|
7632
|
+
mode === "editor" ? DEFAULT_EDITOR_COLOR : DEFAULT_PLAYER_COLOR
|
|
7633
|
+
) : this.getResolvedStyle(mode);
|
|
7634
|
+
const proceduralStyle = !color && resolved.kind === "procedural" ? resolved : null;
|
|
7635
|
+
const fogColor = color ?? (proceduralStyle ? normalizeCanvasColor(
|
|
7636
|
+
ctx,
|
|
7637
|
+
proceduralStyle.backdrop,
|
|
7638
|
+
mode === "player" ? DEFAULT_PLAYER_COLOR : DEFAULT_EDITOR_COLOR
|
|
7639
|
+
) : resolved.kind === "solid" ? resolved.color : resolved.backdrop);
|
|
7640
|
+
const safetyColor = proceduralStyle && mode === "player" ? DEFAULT_PLAYER_COLOR : null;
|
|
7422
7641
|
const baseCovered = def.base === "covered";
|
|
7423
7642
|
const tileMap = /* @__PURE__ */ new Map();
|
|
7424
7643
|
for (const tile of state.tiles) {
|
|
@@ -7441,20 +7660,129 @@ var FogRenderer = class {
|
|
|
7441
7660
|
const clipR = Math.min(tileWorldX + tileWorldSize, def.bounds.x + def.bounds.w);
|
|
7442
7661
|
const clipB = Math.min(tileWorldY + tileWorldSize, def.bounds.y + def.bounds.h);
|
|
7443
7662
|
if (clipR > clipX && clipB > clipY) {
|
|
7663
|
+
if (safetyColor) {
|
|
7664
|
+
ctx.fillStyle = safetyColor;
|
|
7665
|
+
ctx.fillRect(clipX, clipY, clipR - clipX, clipB - clipY);
|
|
7666
|
+
}
|
|
7667
|
+
ctx.fillStyle = fogColor;
|
|
7444
7668
|
ctx.fillRect(clipX, clipY, clipR - clipX, clipB - clipY);
|
|
7669
|
+
if (proceduralStyle) {
|
|
7670
|
+
this.paintProceduralOverlay(
|
|
7671
|
+
ctx,
|
|
7672
|
+
proceduralStyle,
|
|
7673
|
+
clipX,
|
|
7674
|
+
clipY,
|
|
7675
|
+
clipR - clipX,
|
|
7676
|
+
clipB - clipY
|
|
7677
|
+
);
|
|
7678
|
+
}
|
|
7445
7679
|
}
|
|
7446
7680
|
continue;
|
|
7447
7681
|
}
|
|
7448
7682
|
if (data) {
|
|
7683
|
+
if (safetyColor) this.renderTileForExport(ctx, data, tx, ty, def, safetyColor);
|
|
7449
7684
|
this.renderTileForExport(ctx, data, tx, ty, def, fogColor);
|
|
7685
|
+
if (proceduralStyle) {
|
|
7686
|
+
this.renderTileProceduralOverlay(ctx, data, tx, ty, def, proceduralStyle);
|
|
7687
|
+
}
|
|
7450
7688
|
}
|
|
7451
7689
|
}
|
|
7452
7690
|
}
|
|
7453
7691
|
}
|
|
7454
7692
|
dispose() {
|
|
7455
7693
|
this.tileCache.clear();
|
|
7694
|
+
this.patternCache.clear();
|
|
7695
|
+
clearProceduralTileCache();
|
|
7456
7696
|
this.state = null;
|
|
7457
7697
|
}
|
|
7698
|
+
getOrCreatePattern(ctx, style, worldScale) {
|
|
7699
|
+
const key = `${style.backdrop}\0${style.tint}\0${style.opacity}\0${style.scale}\0${style.seed}\0${style.detail}\0${worldScale}`;
|
|
7700
|
+
const cached = this.patternCache.get(key);
|
|
7701
|
+
if (cached !== void 0) return cached;
|
|
7702
|
+
let pattern = null;
|
|
7703
|
+
try {
|
|
7704
|
+
const tileData = getCachedProceduralTile(style);
|
|
7705
|
+
pattern = this.createPatternFromTileData(ctx, tileData, style, worldScale);
|
|
7706
|
+
} catch {
|
|
7707
|
+
}
|
|
7708
|
+
if (this.patternCache.size >= 32) {
|
|
7709
|
+
const oldest = this.patternCache.keys().next().value;
|
|
7710
|
+
if (oldest !== void 0) this.patternCache.delete(oldest);
|
|
7711
|
+
}
|
|
7712
|
+
this.patternCache.set(key, pattern);
|
|
7713
|
+
return pattern;
|
|
7714
|
+
}
|
|
7715
|
+
createPatternFromTileData(ctx, tileData, style, worldScale) {
|
|
7716
|
+
if (typeof document === "undefined") return null;
|
|
7717
|
+
const sourceCanvas = document.createElement("canvas");
|
|
7718
|
+
sourceCanvas.width = tileData.width;
|
|
7719
|
+
sourceCanvas.height = tileData.height;
|
|
7720
|
+
const sourceCtx = sourceCanvas.getContext("2d");
|
|
7721
|
+
if (!sourceCtx) return null;
|
|
7722
|
+
const imageData = new ImageData(
|
|
7723
|
+
new Uint8ClampedArray(tileData.data),
|
|
7724
|
+
tileData.width,
|
|
7725
|
+
tileData.height
|
|
7726
|
+
);
|
|
7727
|
+
sourceCtx.putImageData(imageData, 0, 0);
|
|
7728
|
+
sourceCtx.globalCompositeOperation = "source-in";
|
|
7729
|
+
sourceCtx.fillStyle = normalizeCanvasColor(sourceCtx, style.tint, "#ffffff");
|
|
7730
|
+
sourceCtx.fillRect(0, 0, tileData.width, tileData.height);
|
|
7731
|
+
sourceCtx.globalCompositeOperation = "source-over";
|
|
7732
|
+
const patternScale = style.scale * worldScale / tileData.width;
|
|
7733
|
+
const pattern = ctx.createPattern(sourceCanvas, "repeat");
|
|
7734
|
+
if (pattern && typeof pattern.setTransform === "function" && typeof DOMMatrix !== "undefined") {
|
|
7735
|
+
try {
|
|
7736
|
+
pattern.setTransform(new DOMMatrix([patternScale, 0, 0, patternScale, 0, 0]));
|
|
7737
|
+
return pattern;
|
|
7738
|
+
} catch {
|
|
7739
|
+
}
|
|
7740
|
+
}
|
|
7741
|
+
const patternPx = Math.round(style.scale * worldScale);
|
|
7742
|
+
if (patternPx < 1) return null;
|
|
7743
|
+
const patternCanvas = document.createElement("canvas");
|
|
7744
|
+
patternCanvas.width = patternPx;
|
|
7745
|
+
patternCanvas.height = patternPx;
|
|
7746
|
+
const patternCtx = patternCanvas.getContext("2d");
|
|
7747
|
+
if (!patternCtx) return null;
|
|
7748
|
+
patternCtx.drawImage(sourceCanvas, 0, 0, patternPx, patternPx);
|
|
7749
|
+
return ctx.createPattern(patternCanvas, "repeat");
|
|
7750
|
+
}
|
|
7751
|
+
paintProceduralOverlay(ctx, style, x, y, w, h) {
|
|
7752
|
+
const pattern = this.getOrCreatePattern(ctx, style, 1);
|
|
7753
|
+
if (!pattern) return;
|
|
7754
|
+
ctx.save();
|
|
7755
|
+
ctx.fillStyle = pattern;
|
|
7756
|
+
ctx.fillRect(x, y, w, h);
|
|
7757
|
+
ctx.restore();
|
|
7758
|
+
}
|
|
7759
|
+
renderTileProceduralOverlay(ctx, data, tx, ty, def, style) {
|
|
7760
|
+
const cellSize = def.cellSize;
|
|
7761
|
+
const tileWorldX = tx * FOG_TILE_CELLS * cellSize;
|
|
7762
|
+
const tileWorldY = ty * FOG_TILE_CELLS * cellSize;
|
|
7763
|
+
const pattern = this.getOrCreatePattern(ctx, style, 1);
|
|
7764
|
+
if (!pattern) return;
|
|
7765
|
+
const bytes = decodeBase64(data);
|
|
7766
|
+
ctx.save();
|
|
7767
|
+
ctx.fillStyle = pattern;
|
|
7768
|
+
for (let row = 0; row < FOG_TILE_CELLS; row++) {
|
|
7769
|
+
for (let col = 0; col < FOG_TILE_CELLS; col++) {
|
|
7770
|
+
const cellWorldX = tileWorldX + col * cellSize;
|
|
7771
|
+
const cellWorldY = tileWorldY + row * cellSize;
|
|
7772
|
+
if (cellWorldX < def.bounds.x || cellWorldY < def.bounds.y || cellWorldX >= def.bounds.x + def.bounds.w || cellWorldY >= def.bounds.y + def.bounds.h) {
|
|
7773
|
+
continue;
|
|
7774
|
+
}
|
|
7775
|
+
const index = row * FOG_TILE_CELLS + col;
|
|
7776
|
+
const byteIndex = index >> 3;
|
|
7777
|
+
const bitIndex = 7 - (index & 7);
|
|
7778
|
+
const revealed = (bytes[byteIndex] >> bitIndex & 1) === 1;
|
|
7779
|
+
if (!revealed) {
|
|
7780
|
+
ctx.fillRect(cellWorldX, cellWorldY, cellSize, cellSize);
|
|
7781
|
+
}
|
|
7782
|
+
}
|
|
7783
|
+
}
|
|
7784
|
+
ctx.restore();
|
|
7785
|
+
}
|
|
7458
7786
|
tileRaster(data, color) {
|
|
7459
7787
|
const key = `${color}\0${data}`;
|
|
7460
7788
|
const cached = this.tileCache.get(key);
|
|
@@ -7538,6 +7866,22 @@ function getVisibleWorld(camera, viewportWidth, viewportHeight) {
|
|
|
7538
7866
|
h: bottomRight.y - topLeft.y
|
|
7539
7867
|
};
|
|
7540
7868
|
}
|
|
7869
|
+
function normalizeCanvasColor(ctx, value, fallback) {
|
|
7870
|
+
const previous = ctx.fillStyle;
|
|
7871
|
+
try {
|
|
7872
|
+
ctx.fillStyle = "#010203";
|
|
7873
|
+
ctx.fillStyle = value;
|
|
7874
|
+
const first = ctx.fillStyle;
|
|
7875
|
+
ctx.fillStyle = "#040506";
|
|
7876
|
+
ctx.fillStyle = value;
|
|
7877
|
+
const second = ctx.fillStyle;
|
|
7878
|
+
return typeof first === "string" && first === second ? first : fallback;
|
|
7879
|
+
} catch {
|
|
7880
|
+
return fallback;
|
|
7881
|
+
} finally {
|
|
7882
|
+
ctx.fillStyle = previous;
|
|
7883
|
+
}
|
|
7884
|
+
}
|
|
7541
7885
|
|
|
7542
7886
|
// src/canvas/export-image.ts
|
|
7543
7887
|
var DEFAULT_IMAGE_TIMEOUT_MS = 1e4;
|
|
@@ -7953,7 +8297,13 @@ async function exportImage(store, options = {}, layerManager) {
|
|
|
7953
8297
|
}
|
|
7954
8298
|
if (options.fog) {
|
|
7955
8299
|
const fogRenderer = new FogRenderer();
|
|
7956
|
-
fogRenderer.renderForExport(
|
|
8300
|
+
fogRenderer.renderForExport(
|
|
8301
|
+
ctx,
|
|
8302
|
+
options.fog.state,
|
|
8303
|
+
options.fog.mode,
|
|
8304
|
+
options.fog.color,
|
|
8305
|
+
options.fog.style
|
|
8306
|
+
);
|
|
7957
8307
|
}
|
|
7958
8308
|
const mimeType = format === "jpeg" ? "image/jpeg" : "image/png";
|
|
7959
8309
|
return new Promise((resolve) => {
|
|
@@ -8347,7 +8697,13 @@ async function exportSvg(store, options = {}, layerManager) {
|
|
|
8347
8697
|
if (fogCtx) {
|
|
8348
8698
|
fogCtx.translate(-bounds.x, -bounds.y);
|
|
8349
8699
|
const fogRenderer = new FogRenderer();
|
|
8350
|
-
fogRenderer.renderForExport(
|
|
8700
|
+
fogRenderer.renderForExport(
|
|
8701
|
+
fogCtx,
|
|
8702
|
+
fogState,
|
|
8703
|
+
options.fog.mode,
|
|
8704
|
+
options.fog.color,
|
|
8705
|
+
options.fog.style
|
|
8706
|
+
);
|
|
8351
8707
|
try {
|
|
8352
8708
|
const fogDataUri = fogCanvas.toDataURL("image/png");
|
|
8353
8709
|
if (fogDataUri.startsWith("data:")) {
|
|
@@ -11236,6 +11592,11 @@ var Viewport = class _Viewport {
|
|
|
11236
11592
|
get fog() {
|
|
11237
11593
|
return this.fogManager;
|
|
11238
11594
|
}
|
|
11595
|
+
setFogStyle(options) {
|
|
11596
|
+
this.fogRenderer.setOptions(options);
|
|
11597
|
+
this.renderLoop.requestRender();
|
|
11598
|
+
this.minimap?.invalidateScene();
|
|
11599
|
+
}
|
|
11239
11600
|
get snapToGrid() {
|
|
11240
11601
|
return this._snapToGrid;
|
|
11241
11602
|
}
|
|
@@ -11342,26 +11703,35 @@ var Viewport = class _Viewport {
|
|
|
11342
11703
|
const expected = base.expectedCanvasTypes ? /* @__PURE__ */ new Set([...declared, ...base.expectedCanvasTypes]) : declared;
|
|
11343
11704
|
return { ...base, htmlPainters: registry, expectedCanvasTypes: expected };
|
|
11344
11705
|
}
|
|
11345
|
-
|
|
11346
|
-
|
|
11347
|
-
|
|
11348
|
-
|
|
11349
|
-
|
|
11350
|
-
|
|
11351
|
-
|
|
11352
|
-
|
|
11706
|
+
/**
|
|
11707
|
+
* Carry constructor-configured fog presentation into both implicit exports and
|
|
11708
|
+
* explicit state/mode exports. Explicit style and legacy color overrides win.
|
|
11709
|
+
*/
|
|
11710
|
+
withFogDefaults(options) {
|
|
11711
|
+
const fog = options.fog;
|
|
11712
|
+
if (fog === false) return options;
|
|
11713
|
+
if (fog !== void 0) {
|
|
11714
|
+
if (fog.style !== void 0 || fog.color !== void 0) return options;
|
|
11715
|
+
return {
|
|
11716
|
+
...options,
|
|
11717
|
+
fog: { ...fog, style: this.fogRenderer.getResolvedStyle(fog.mode) }
|
|
11718
|
+
};
|
|
11353
11719
|
}
|
|
11720
|
+
if (!this.fogRenderer.isVisible()) return options;
|
|
11721
|
+
const state = this.fogManager.getState();
|
|
11722
|
+
if (!state) return options;
|
|
11723
|
+
const mode = this.fogRenderer.getViewMode();
|
|
11724
|
+
return {
|
|
11725
|
+
...options,
|
|
11726
|
+
fog: { state, mode, style: this.fogRenderer.getResolvedStyle(mode) }
|
|
11727
|
+
};
|
|
11728
|
+
}
|
|
11729
|
+
async exportImage(options) {
|
|
11730
|
+
const opts = this.withFogDefaults(this.withHtmlDefaults(options));
|
|
11354
11731
|
return exportImage(this.store, opts, this.layerManager);
|
|
11355
11732
|
}
|
|
11356
11733
|
async exportSVG(options) {
|
|
11357
|
-
const opts = this.withHtmlDefaults(options);
|
|
11358
|
-
if (opts.fog === void 0 && this.fogRenderer.isVisible()) {
|
|
11359
|
-
const state = this.fogManager.getState();
|
|
11360
|
-
if (state) {
|
|
11361
|
-
const mode = this.fogRenderer.getViewMode();
|
|
11362
|
-
opts.fog = { state, mode };
|
|
11363
|
-
}
|
|
11364
|
-
}
|
|
11734
|
+
const opts = this.withFogDefaults(this.withHtmlDefaults(options));
|
|
11365
11735
|
return exportSvg(this.store, opts, this.layerManager);
|
|
11366
11736
|
}
|
|
11367
11737
|
loadState(state) {
|
|
@@ -12995,15 +13365,15 @@ function applyCameraView(camera, view, canvasW, canvasH) {
|
|
|
12995
13365
|
var DEFAULT_DURATION_MS3 = 400;
|
|
12996
13366
|
var FRAMED_EPSILON = 1e-6;
|
|
12997
13367
|
var easeOutCubic2 = (t) => 1 - Math.pow(1 - t, 3);
|
|
12998
|
-
function
|
|
13368
|
+
function lerp2(a, b, k) {
|
|
12999
13369
|
return a + (b - a) * k;
|
|
13000
13370
|
}
|
|
13001
13371
|
function lerpView(from, to, k) {
|
|
13002
13372
|
return {
|
|
13003
|
-
x:
|
|
13004
|
-
y:
|
|
13005
|
-
w:
|
|
13006
|
-
h:
|
|
13373
|
+
x: lerp2(from.x, to.x, k),
|
|
13374
|
+
y: lerp2(from.y, to.y, k),
|
|
13375
|
+
w: lerp2(from.w, to.w, k),
|
|
13376
|
+
h: lerp2(from.h, to.h, k)
|
|
13007
13377
|
};
|
|
13008
13378
|
}
|
|
13009
13379
|
function viewsClose(a, b) {
|
|
@@ -14257,7 +14627,7 @@ var PencilTool = class {
|
|
|
14257
14627
|
};
|
|
14258
14628
|
|
|
14259
14629
|
// src/elements/stroke-erase.ts
|
|
14260
|
-
function
|
|
14630
|
+
function lerp3(a, b, t) {
|
|
14261
14631
|
return {
|
|
14262
14632
|
x: a.x + (b.x - a.x) * t,
|
|
14263
14633
|
y: a.y + (b.y - a.y) * t,
|
|
@@ -14316,13 +14686,13 @@ function erasePoints(points, eraser, radius) {
|
|
|
14316
14686
|
erased = true;
|
|
14317
14687
|
if (tLo > 0) {
|
|
14318
14688
|
if (current.length === 0) current.push(a);
|
|
14319
|
-
current.push(
|
|
14689
|
+
current.push(lerp3(a, b, tLo));
|
|
14320
14690
|
flush();
|
|
14321
14691
|
} else {
|
|
14322
14692
|
flush();
|
|
14323
14693
|
}
|
|
14324
14694
|
if (tHi < 1) {
|
|
14325
|
-
current = [
|
|
14695
|
+
current = [lerp3(a, b, tHi), b];
|
|
14326
14696
|
}
|
|
14327
14697
|
}
|
|
14328
14698
|
flush();
|
|
@@ -16838,7 +17208,7 @@ var FogTool = class {
|
|
|
16838
17208
|
};
|
|
16839
17209
|
|
|
16840
17210
|
// src/index.ts
|
|
16841
|
-
var VERSION = "0.
|
|
17211
|
+
var VERSION = "0.68.0";
|
|
16842
17212
|
// Annotate the CommonJS export names for ESM import in node:
|
|
16843
17213
|
0 && (module.exports = {
|
|
16844
17214
|
AWARENESS_MAX_SELECTION,
|
|
@@ -16947,6 +17317,7 @@ var VERSION = "0.66.0";
|
|
|
16947
17317
|
isPingPresence,
|
|
16948
17318
|
pathDistanceCells,
|
|
16949
17319
|
recommendedFogCellSize,
|
|
17320
|
+
resolveFogStyle,
|
|
16950
17321
|
resolveHtmlRouting,
|
|
16951
17322
|
setFontSize,
|
|
16952
17323
|
smartSnap,
|