@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.js
CHANGED
|
@@ -7174,19 +7174,196 @@ async function renderHtmlElements(elements, options) {
|
|
|
7174
7174
|
return sources;
|
|
7175
7175
|
}
|
|
7176
7176
|
|
|
7177
|
+
// src/fog/fog-style.ts
|
|
7178
|
+
var DEFAULT_PROCEDURAL_OPACITY = 0.6;
|
|
7179
|
+
var DEFAULT_PROCEDURAL_SCALE = 256;
|
|
7180
|
+
var DEFAULT_PROCEDURAL_SEED = 0;
|
|
7181
|
+
var DEFAULT_PROCEDURAL_DETAIL = 2;
|
|
7182
|
+
var DEFAULT_PROCEDURAL_TINT = "#ffffff";
|
|
7183
|
+
var MIN_SCALE = 64;
|
|
7184
|
+
var MAX_SCALE = 1024;
|
|
7185
|
+
var MIN_DETAIL = 1;
|
|
7186
|
+
var MAX_DETAIL = 4;
|
|
7187
|
+
var MAX_SEED = 65535;
|
|
7188
|
+
function clamp(value, min, max) {
|
|
7189
|
+
return Math.max(min, Math.min(max, value));
|
|
7190
|
+
}
|
|
7191
|
+
function finiteOrDefault(value, defaultValue) {
|
|
7192
|
+
return value !== void 0 && Number.isFinite(value) ? value : defaultValue;
|
|
7193
|
+
}
|
|
7194
|
+
function resolveFogStyle(style, legacyColor, defaultColor) {
|
|
7195
|
+
if (style && style.kind === "procedural") {
|
|
7196
|
+
const opacity = clamp(finiteOrDefault(style.opacity, DEFAULT_PROCEDURAL_OPACITY), 0, 1);
|
|
7197
|
+
const scale = clamp(
|
|
7198
|
+
finiteOrDefault(style.scale, DEFAULT_PROCEDURAL_SCALE),
|
|
7199
|
+
MIN_SCALE,
|
|
7200
|
+
MAX_SCALE
|
|
7201
|
+
);
|
|
7202
|
+
const seed = clamp(
|
|
7203
|
+
Math.floor(finiteOrDefault(style.seed, DEFAULT_PROCEDURAL_SEED)),
|
|
7204
|
+
0,
|
|
7205
|
+
MAX_SEED
|
|
7206
|
+
);
|
|
7207
|
+
const detail = clamp(
|
|
7208
|
+
Math.floor(finiteOrDefault(style.detail, DEFAULT_PROCEDURAL_DETAIL)),
|
|
7209
|
+
MIN_DETAIL,
|
|
7210
|
+
MAX_DETAIL
|
|
7211
|
+
);
|
|
7212
|
+
return {
|
|
7213
|
+
kind: "procedural",
|
|
7214
|
+
backdrop: style.backdrop,
|
|
7215
|
+
// `tint` is required for typed callers. Keep a visible runtime fallback for
|
|
7216
|
+
// untyped/older JavaScript hosts instead of producing a flat same-color overlay.
|
|
7217
|
+
tint: style.tint || DEFAULT_PROCEDURAL_TINT,
|
|
7218
|
+
opacity,
|
|
7219
|
+
scale,
|
|
7220
|
+
seed,
|
|
7221
|
+
detail
|
|
7222
|
+
};
|
|
7223
|
+
}
|
|
7224
|
+
if (style && (!style.kind || style.kind === "solid")) {
|
|
7225
|
+
return { kind: "solid", color: style.color };
|
|
7226
|
+
}
|
|
7227
|
+
return { kind: "solid", color: legacyColor ?? defaultColor };
|
|
7228
|
+
}
|
|
7229
|
+
|
|
7230
|
+
// src/fog/fog-procedural-tile.ts
|
|
7231
|
+
var TILE_PX = 128;
|
|
7232
|
+
function xorshift32(state) {
|
|
7233
|
+
state ^= state << 13;
|
|
7234
|
+
state ^= state >>> 17;
|
|
7235
|
+
state ^= state << 5;
|
|
7236
|
+
return state >>> 0;
|
|
7237
|
+
}
|
|
7238
|
+
function seedState(seed) {
|
|
7239
|
+
return seed * 2654435761 + 1 >>> 0 || 1;
|
|
7240
|
+
}
|
|
7241
|
+
function smoothstep(t) {
|
|
7242
|
+
return t * t * (3 - 2 * t);
|
|
7243
|
+
}
|
|
7244
|
+
function lerp(a, b, t) {
|
|
7245
|
+
return a + (b - a) * t;
|
|
7246
|
+
}
|
|
7247
|
+
function generateGradients(size, prngState) {
|
|
7248
|
+
const count = size * size;
|
|
7249
|
+
const gx = new Float32Array(count);
|
|
7250
|
+
const gy = new Float32Array(count);
|
|
7251
|
+
let s = prngState;
|
|
7252
|
+
for (let i = 0; i < count; i++) {
|
|
7253
|
+
s = xorshift32(s);
|
|
7254
|
+
const angle = (s >>> 0) / 4294967296 * Math.PI * 2;
|
|
7255
|
+
gx[i] = Math.cos(angle);
|
|
7256
|
+
gy[i] = Math.sin(angle);
|
|
7257
|
+
}
|
|
7258
|
+
return { gx, gy, state: s };
|
|
7259
|
+
}
|
|
7260
|
+
function perlinNoise(px, py, gridSize, gx, gy) {
|
|
7261
|
+
const gx0 = Math.floor(px) % gridSize;
|
|
7262
|
+
const gy0 = Math.floor(py) % gridSize;
|
|
7263
|
+
const gx1 = (gx0 + 1) % gridSize;
|
|
7264
|
+
const gy1 = (gy0 + 1) % gridSize;
|
|
7265
|
+
const fx = px - Math.floor(px);
|
|
7266
|
+
const fy = py - Math.floor(py);
|
|
7267
|
+
const sx = smoothstep(fx);
|
|
7268
|
+
const sy = smoothstep(fy);
|
|
7269
|
+
const dot = (ix, iy, dx, dy) => {
|
|
7270
|
+
const idx = iy * gridSize + ix;
|
|
7271
|
+
return gx[idx] * dx + gy[idx] * dy;
|
|
7272
|
+
};
|
|
7273
|
+
const n00 = dot(gx0, gy0, fx, fy);
|
|
7274
|
+
const n10 = dot(gx1, gy0, fx - 1, fy);
|
|
7275
|
+
const n01 = dot(gx0, gy1, fx, fy - 1);
|
|
7276
|
+
const n11 = dot(gx1, gy1, fx - 1, fy - 1);
|
|
7277
|
+
return lerp(lerp(n00, n10, sx), lerp(n01, n11, sx), sy);
|
|
7278
|
+
}
|
|
7279
|
+
function layeredNoise(x, y, octaves, gridSize, gx, gy) {
|
|
7280
|
+
let value = 0;
|
|
7281
|
+
let amplitude = 1;
|
|
7282
|
+
let frequency = 1;
|
|
7283
|
+
let maxAmplitude = 0;
|
|
7284
|
+
for (let o = 0; o < octaves; o++) {
|
|
7285
|
+
value += perlinNoise(x * frequency, y * frequency, gridSize * frequency, gx, gy) * amplitude;
|
|
7286
|
+
maxAmplitude += amplitude;
|
|
7287
|
+
amplitude *= 0.5;
|
|
7288
|
+
frequency *= 2;
|
|
7289
|
+
}
|
|
7290
|
+
return (value / maxAmplitude + 1) * 0.5;
|
|
7291
|
+
}
|
|
7292
|
+
function generateProceduralTile(style) {
|
|
7293
|
+
const gridSize = 8;
|
|
7294
|
+
const maxFreq = gridSize * (1 << style.detail - 1);
|
|
7295
|
+
const { gx, gy } = generateGradients(maxFreq, seedState(style.seed));
|
|
7296
|
+
const data = new Uint8ClampedArray(TILE_PX * TILE_PX * 4);
|
|
7297
|
+
for (let py = 0; py < TILE_PX; py++) {
|
|
7298
|
+
for (let px = 0; px < TILE_PX; px++) {
|
|
7299
|
+
const nx = px / TILE_PX * gridSize;
|
|
7300
|
+
const ny = py / TILE_PX * gridSize;
|
|
7301
|
+
const n2 = layeredNoise(nx, ny, style.detail, gridSize, gx, gy);
|
|
7302
|
+
const alpha = Math.round(n2 * style.opacity * 255);
|
|
7303
|
+
const idx = (py * TILE_PX + px) * 4;
|
|
7304
|
+
data[idx] = 255;
|
|
7305
|
+
data[idx + 1] = 255;
|
|
7306
|
+
data[idx + 2] = 255;
|
|
7307
|
+
data[idx + 3] = alpha;
|
|
7308
|
+
}
|
|
7309
|
+
}
|
|
7310
|
+
return { data, width: TILE_PX, height: TILE_PX };
|
|
7311
|
+
}
|
|
7312
|
+
var tileCache = /* @__PURE__ */ new Map();
|
|
7313
|
+
var MAX_CACHED_TILES = 16;
|
|
7314
|
+
function getCachedProceduralTile(style) {
|
|
7315
|
+
const key = `${style.opacity}\0${style.seed}\0${style.detail}`;
|
|
7316
|
+
const cached = tileCache.get(key);
|
|
7317
|
+
if (cached) return cached;
|
|
7318
|
+
const tile = generateProceduralTile(style);
|
|
7319
|
+
if (tileCache.size >= MAX_CACHED_TILES) {
|
|
7320
|
+
const oldest = tileCache.keys().next().value;
|
|
7321
|
+
if (oldest !== void 0) tileCache.delete(oldest);
|
|
7322
|
+
}
|
|
7323
|
+
tileCache.set(key, tile);
|
|
7324
|
+
return tile;
|
|
7325
|
+
}
|
|
7326
|
+
function clearProceduralTileCache() {
|
|
7327
|
+
tileCache.clear();
|
|
7328
|
+
}
|
|
7329
|
+
|
|
7177
7330
|
// src/fog/fog-renderer.ts
|
|
7178
7331
|
var DEFAULT_EDITOR_COLOR = "rgba(30, 40, 60, 0.45)";
|
|
7179
7332
|
var DEFAULT_PLAYER_COLOR = "#0b1020";
|
|
7180
7333
|
var FogRenderer = class {
|
|
7181
7334
|
tileCache = /* @__PURE__ */ new Map();
|
|
7335
|
+
patternCache = /* @__PURE__ */ new Map();
|
|
7182
7336
|
state = null;
|
|
7183
7337
|
viewMode = "off";
|
|
7184
7338
|
dirty = true;
|
|
7185
|
-
|
|
7186
|
-
|
|
7339
|
+
editorStyle;
|
|
7340
|
+
playerStyle;
|
|
7187
7341
|
constructor(options = {}) {
|
|
7188
|
-
this.
|
|
7189
|
-
|
|
7342
|
+
this.editorStyle = resolveFogStyle(
|
|
7343
|
+
options.editorStyle,
|
|
7344
|
+
options.editorColor,
|
|
7345
|
+
DEFAULT_EDITOR_COLOR
|
|
7346
|
+
);
|
|
7347
|
+
this.playerStyle = resolveFogStyle(
|
|
7348
|
+
options.playerStyle,
|
|
7349
|
+
options.playerColor,
|
|
7350
|
+
DEFAULT_PLAYER_COLOR
|
|
7351
|
+
);
|
|
7352
|
+
}
|
|
7353
|
+
setOptions(options) {
|
|
7354
|
+
this.editorStyle = resolveFogStyle(
|
|
7355
|
+
options.editorStyle,
|
|
7356
|
+
options.editorColor,
|
|
7357
|
+
DEFAULT_EDITOR_COLOR
|
|
7358
|
+
);
|
|
7359
|
+
this.playerStyle = resolveFogStyle(
|
|
7360
|
+
options.playerStyle,
|
|
7361
|
+
options.playerColor,
|
|
7362
|
+
DEFAULT_PLAYER_COLOR
|
|
7363
|
+
);
|
|
7364
|
+
this.tileCache.clear();
|
|
7365
|
+
this.patternCache.clear();
|
|
7366
|
+
this.dirty = true;
|
|
7190
7367
|
}
|
|
7191
7368
|
setState(state) {
|
|
7192
7369
|
this.state = state;
|
|
@@ -7212,12 +7389,23 @@ var FogRenderer = class {
|
|
|
7212
7389
|
isVisible() {
|
|
7213
7390
|
return this.viewMode !== "off" && this.state !== null;
|
|
7214
7391
|
}
|
|
7392
|
+
getResolvedStyle(mode) {
|
|
7393
|
+
return mode === "editor" ? this.editorStyle : this.playerStyle;
|
|
7394
|
+
}
|
|
7215
7395
|
render(ctx, camera, viewportWidth, viewportHeight, _dpr) {
|
|
7216
7396
|
if (!this.state || this.viewMode === "off") return;
|
|
7217
7397
|
const def = this.state.definition;
|
|
7218
7398
|
const cellSize = def.cellSize;
|
|
7219
7399
|
const tileWorldSize = FOG_TILE_CELLS * cellSize;
|
|
7220
|
-
const
|
|
7400
|
+
const mode = this.viewMode === "editor" ? "editor" : "player";
|
|
7401
|
+
const style = this.getResolvedStyle(mode);
|
|
7402
|
+
const proceduralStyle = style.kind === "procedural" ? style : null;
|
|
7403
|
+
const color = style.kind === "procedural" ? normalizeCanvasColor(
|
|
7404
|
+
ctx,
|
|
7405
|
+
style.backdrop,
|
|
7406
|
+
mode === "player" ? DEFAULT_PLAYER_COLOR : DEFAULT_EDITOR_COLOR
|
|
7407
|
+
) : style.color;
|
|
7408
|
+
const safetyColor = proceduralStyle && mode === "player" ? DEFAULT_PLAYER_COLOR : null;
|
|
7221
7409
|
const worldBounds = getVisibleWorld(camera, viewportWidth, viewportHeight);
|
|
7222
7410
|
const minTX = Math.floor(Math.max(def.bounds.x, worldBounds.x) / tileWorldSize);
|
|
7223
7411
|
const minTY = Math.floor(Math.max(def.bounds.y, worldBounds.y) / tileWorldSize);
|
|
@@ -7248,7 +7436,22 @@ var FogRenderer = class {
|
|
|
7248
7436
|
const clipR = Math.min(tileWorldX + tileWorldSize, def.bounds.x + def.bounds.w);
|
|
7249
7437
|
const clipB = Math.min(tileWorldY + tileWorldSize, def.bounds.y + def.bounds.h);
|
|
7250
7438
|
if (clipR > clipX && clipB > clipY) {
|
|
7439
|
+
if (safetyColor) {
|
|
7440
|
+
ctx.fillStyle = safetyColor;
|
|
7441
|
+
ctx.fillRect(clipX, clipY, clipR - clipX, clipB - clipY);
|
|
7442
|
+
}
|
|
7443
|
+
ctx.fillStyle = color;
|
|
7251
7444
|
ctx.fillRect(clipX, clipY, clipR - clipX, clipB - clipY);
|
|
7445
|
+
if (proceduralStyle) {
|
|
7446
|
+
this.paintProceduralOverlay(
|
|
7447
|
+
ctx,
|
|
7448
|
+
proceduralStyle,
|
|
7449
|
+
clipX,
|
|
7450
|
+
clipY,
|
|
7451
|
+
clipR - clipX,
|
|
7452
|
+
clipB - clipY
|
|
7453
|
+
);
|
|
7454
|
+
}
|
|
7252
7455
|
}
|
|
7253
7456
|
continue;
|
|
7254
7457
|
}
|
|
@@ -7256,18 +7459,33 @@ var FogRenderer = class {
|
|
|
7256
7459
|
continue;
|
|
7257
7460
|
}
|
|
7258
7461
|
if (data) {
|
|
7462
|
+
if (safetyColor) this.renderTile(ctx, data, tx, ty, def, safetyColor);
|
|
7259
7463
|
this.renderTile(ctx, data, tx, ty, def, color);
|
|
7464
|
+
if (proceduralStyle) {
|
|
7465
|
+
this.renderTileProceduralOverlay(ctx, data, tx, ty, def, proceduralStyle);
|
|
7466
|
+
}
|
|
7260
7467
|
}
|
|
7261
7468
|
}
|
|
7262
7469
|
}
|
|
7263
7470
|
ctx.restore();
|
|
7264
7471
|
this.dirty = false;
|
|
7265
7472
|
}
|
|
7266
|
-
renderForExport(ctx, state, mode, color) {
|
|
7473
|
+
renderForExport(ctx, state, mode, color, style) {
|
|
7267
7474
|
const def = state.definition;
|
|
7268
7475
|
const cellSize = def.cellSize;
|
|
7269
7476
|
const tileWorldSize = FOG_TILE_CELLS * cellSize;
|
|
7270
|
-
const
|
|
7477
|
+
const resolved = style ? resolveFogStyle(
|
|
7478
|
+
style,
|
|
7479
|
+
void 0,
|
|
7480
|
+
mode === "editor" ? DEFAULT_EDITOR_COLOR : DEFAULT_PLAYER_COLOR
|
|
7481
|
+
) : this.getResolvedStyle(mode);
|
|
7482
|
+
const proceduralStyle = !color && resolved.kind === "procedural" ? resolved : null;
|
|
7483
|
+
const fogColor = color ?? (proceduralStyle ? normalizeCanvasColor(
|
|
7484
|
+
ctx,
|
|
7485
|
+
proceduralStyle.backdrop,
|
|
7486
|
+
mode === "player" ? DEFAULT_PLAYER_COLOR : DEFAULT_EDITOR_COLOR
|
|
7487
|
+
) : resolved.kind === "solid" ? resolved.color : resolved.backdrop);
|
|
7488
|
+
const safetyColor = proceduralStyle && mode === "player" ? DEFAULT_PLAYER_COLOR : null;
|
|
7271
7489
|
const baseCovered = def.base === "covered";
|
|
7272
7490
|
const tileMap = /* @__PURE__ */ new Map();
|
|
7273
7491
|
for (const tile of state.tiles) {
|
|
@@ -7290,20 +7508,129 @@ var FogRenderer = class {
|
|
|
7290
7508
|
const clipR = Math.min(tileWorldX + tileWorldSize, def.bounds.x + def.bounds.w);
|
|
7291
7509
|
const clipB = Math.min(tileWorldY + tileWorldSize, def.bounds.y + def.bounds.h);
|
|
7292
7510
|
if (clipR > clipX && clipB > clipY) {
|
|
7511
|
+
if (safetyColor) {
|
|
7512
|
+
ctx.fillStyle = safetyColor;
|
|
7513
|
+
ctx.fillRect(clipX, clipY, clipR - clipX, clipB - clipY);
|
|
7514
|
+
}
|
|
7515
|
+
ctx.fillStyle = fogColor;
|
|
7293
7516
|
ctx.fillRect(clipX, clipY, clipR - clipX, clipB - clipY);
|
|
7517
|
+
if (proceduralStyle) {
|
|
7518
|
+
this.paintProceduralOverlay(
|
|
7519
|
+
ctx,
|
|
7520
|
+
proceduralStyle,
|
|
7521
|
+
clipX,
|
|
7522
|
+
clipY,
|
|
7523
|
+
clipR - clipX,
|
|
7524
|
+
clipB - clipY
|
|
7525
|
+
);
|
|
7526
|
+
}
|
|
7294
7527
|
}
|
|
7295
7528
|
continue;
|
|
7296
7529
|
}
|
|
7297
7530
|
if (data) {
|
|
7531
|
+
if (safetyColor) this.renderTileForExport(ctx, data, tx, ty, def, safetyColor);
|
|
7298
7532
|
this.renderTileForExport(ctx, data, tx, ty, def, fogColor);
|
|
7533
|
+
if (proceduralStyle) {
|
|
7534
|
+
this.renderTileProceduralOverlay(ctx, data, tx, ty, def, proceduralStyle);
|
|
7535
|
+
}
|
|
7299
7536
|
}
|
|
7300
7537
|
}
|
|
7301
7538
|
}
|
|
7302
7539
|
}
|
|
7303
7540
|
dispose() {
|
|
7304
7541
|
this.tileCache.clear();
|
|
7542
|
+
this.patternCache.clear();
|
|
7543
|
+
clearProceduralTileCache();
|
|
7305
7544
|
this.state = null;
|
|
7306
7545
|
}
|
|
7546
|
+
getOrCreatePattern(ctx, style, worldScale) {
|
|
7547
|
+
const key = `${style.backdrop}\0${style.tint}\0${style.opacity}\0${style.scale}\0${style.seed}\0${style.detail}\0${worldScale}`;
|
|
7548
|
+
const cached = this.patternCache.get(key);
|
|
7549
|
+
if (cached !== void 0) return cached;
|
|
7550
|
+
let pattern = null;
|
|
7551
|
+
try {
|
|
7552
|
+
const tileData = getCachedProceduralTile(style);
|
|
7553
|
+
pattern = this.createPatternFromTileData(ctx, tileData, style, worldScale);
|
|
7554
|
+
} catch {
|
|
7555
|
+
}
|
|
7556
|
+
if (this.patternCache.size >= 32) {
|
|
7557
|
+
const oldest = this.patternCache.keys().next().value;
|
|
7558
|
+
if (oldest !== void 0) this.patternCache.delete(oldest);
|
|
7559
|
+
}
|
|
7560
|
+
this.patternCache.set(key, pattern);
|
|
7561
|
+
return pattern;
|
|
7562
|
+
}
|
|
7563
|
+
createPatternFromTileData(ctx, tileData, style, worldScale) {
|
|
7564
|
+
if (typeof document === "undefined") return null;
|
|
7565
|
+
const sourceCanvas = document.createElement("canvas");
|
|
7566
|
+
sourceCanvas.width = tileData.width;
|
|
7567
|
+
sourceCanvas.height = tileData.height;
|
|
7568
|
+
const sourceCtx = sourceCanvas.getContext("2d");
|
|
7569
|
+
if (!sourceCtx) return null;
|
|
7570
|
+
const imageData = new ImageData(
|
|
7571
|
+
new Uint8ClampedArray(tileData.data),
|
|
7572
|
+
tileData.width,
|
|
7573
|
+
tileData.height
|
|
7574
|
+
);
|
|
7575
|
+
sourceCtx.putImageData(imageData, 0, 0);
|
|
7576
|
+
sourceCtx.globalCompositeOperation = "source-in";
|
|
7577
|
+
sourceCtx.fillStyle = normalizeCanvasColor(sourceCtx, style.tint, "#ffffff");
|
|
7578
|
+
sourceCtx.fillRect(0, 0, tileData.width, tileData.height);
|
|
7579
|
+
sourceCtx.globalCompositeOperation = "source-over";
|
|
7580
|
+
const patternScale = style.scale * worldScale / tileData.width;
|
|
7581
|
+
const pattern = ctx.createPattern(sourceCanvas, "repeat");
|
|
7582
|
+
if (pattern && typeof pattern.setTransform === "function" && typeof DOMMatrix !== "undefined") {
|
|
7583
|
+
try {
|
|
7584
|
+
pattern.setTransform(new DOMMatrix([patternScale, 0, 0, patternScale, 0, 0]));
|
|
7585
|
+
return pattern;
|
|
7586
|
+
} catch {
|
|
7587
|
+
}
|
|
7588
|
+
}
|
|
7589
|
+
const patternPx = Math.round(style.scale * worldScale);
|
|
7590
|
+
if (patternPx < 1) return null;
|
|
7591
|
+
const patternCanvas = document.createElement("canvas");
|
|
7592
|
+
patternCanvas.width = patternPx;
|
|
7593
|
+
patternCanvas.height = patternPx;
|
|
7594
|
+
const patternCtx = patternCanvas.getContext("2d");
|
|
7595
|
+
if (!patternCtx) return null;
|
|
7596
|
+
patternCtx.drawImage(sourceCanvas, 0, 0, patternPx, patternPx);
|
|
7597
|
+
return ctx.createPattern(patternCanvas, "repeat");
|
|
7598
|
+
}
|
|
7599
|
+
paintProceduralOverlay(ctx, style, x, y, w, h) {
|
|
7600
|
+
const pattern = this.getOrCreatePattern(ctx, style, 1);
|
|
7601
|
+
if (!pattern) return;
|
|
7602
|
+
ctx.save();
|
|
7603
|
+
ctx.fillStyle = pattern;
|
|
7604
|
+
ctx.fillRect(x, y, w, h);
|
|
7605
|
+
ctx.restore();
|
|
7606
|
+
}
|
|
7607
|
+
renderTileProceduralOverlay(ctx, data, tx, ty, def, style) {
|
|
7608
|
+
const cellSize = def.cellSize;
|
|
7609
|
+
const tileWorldX = tx * FOG_TILE_CELLS * cellSize;
|
|
7610
|
+
const tileWorldY = ty * FOG_TILE_CELLS * cellSize;
|
|
7611
|
+
const pattern = this.getOrCreatePattern(ctx, style, 1);
|
|
7612
|
+
if (!pattern) return;
|
|
7613
|
+
const bytes = decodeBase64(data);
|
|
7614
|
+
ctx.save();
|
|
7615
|
+
ctx.fillStyle = pattern;
|
|
7616
|
+
for (let row = 0; row < FOG_TILE_CELLS; row++) {
|
|
7617
|
+
for (let col = 0; col < FOG_TILE_CELLS; col++) {
|
|
7618
|
+
const cellWorldX = tileWorldX + col * cellSize;
|
|
7619
|
+
const cellWorldY = tileWorldY + row * cellSize;
|
|
7620
|
+
if (cellWorldX < def.bounds.x || cellWorldY < def.bounds.y || cellWorldX >= def.bounds.x + def.bounds.w || cellWorldY >= def.bounds.y + def.bounds.h) {
|
|
7621
|
+
continue;
|
|
7622
|
+
}
|
|
7623
|
+
const index = row * FOG_TILE_CELLS + col;
|
|
7624
|
+
const byteIndex = index >> 3;
|
|
7625
|
+
const bitIndex = 7 - (index & 7);
|
|
7626
|
+
const revealed = (bytes[byteIndex] >> bitIndex & 1) === 1;
|
|
7627
|
+
if (!revealed) {
|
|
7628
|
+
ctx.fillRect(cellWorldX, cellWorldY, cellSize, cellSize);
|
|
7629
|
+
}
|
|
7630
|
+
}
|
|
7631
|
+
}
|
|
7632
|
+
ctx.restore();
|
|
7633
|
+
}
|
|
7307
7634
|
tileRaster(data, color) {
|
|
7308
7635
|
const key = `${color}\0${data}`;
|
|
7309
7636
|
const cached = this.tileCache.get(key);
|
|
@@ -7387,6 +7714,22 @@ function getVisibleWorld(camera, viewportWidth, viewportHeight) {
|
|
|
7387
7714
|
h: bottomRight.y - topLeft.y
|
|
7388
7715
|
};
|
|
7389
7716
|
}
|
|
7717
|
+
function normalizeCanvasColor(ctx, value, fallback) {
|
|
7718
|
+
const previous = ctx.fillStyle;
|
|
7719
|
+
try {
|
|
7720
|
+
ctx.fillStyle = "#010203";
|
|
7721
|
+
ctx.fillStyle = value;
|
|
7722
|
+
const first = ctx.fillStyle;
|
|
7723
|
+
ctx.fillStyle = "#040506";
|
|
7724
|
+
ctx.fillStyle = value;
|
|
7725
|
+
const second = ctx.fillStyle;
|
|
7726
|
+
return typeof first === "string" && first === second ? first : fallback;
|
|
7727
|
+
} catch {
|
|
7728
|
+
return fallback;
|
|
7729
|
+
} finally {
|
|
7730
|
+
ctx.fillStyle = previous;
|
|
7731
|
+
}
|
|
7732
|
+
}
|
|
7390
7733
|
|
|
7391
7734
|
// src/canvas/export-image.ts
|
|
7392
7735
|
var DEFAULT_IMAGE_TIMEOUT_MS = 1e4;
|
|
@@ -7802,7 +8145,13 @@ async function exportImage(store, options = {}, layerManager) {
|
|
|
7802
8145
|
}
|
|
7803
8146
|
if (options.fog) {
|
|
7804
8147
|
const fogRenderer = new FogRenderer();
|
|
7805
|
-
fogRenderer.renderForExport(
|
|
8148
|
+
fogRenderer.renderForExport(
|
|
8149
|
+
ctx,
|
|
8150
|
+
options.fog.state,
|
|
8151
|
+
options.fog.mode,
|
|
8152
|
+
options.fog.color,
|
|
8153
|
+
options.fog.style
|
|
8154
|
+
);
|
|
7806
8155
|
}
|
|
7807
8156
|
const mimeType = format === "jpeg" ? "image/jpeg" : "image/png";
|
|
7808
8157
|
return new Promise((resolve) => {
|
|
@@ -8196,7 +8545,13 @@ async function exportSvg(store, options = {}, layerManager) {
|
|
|
8196
8545
|
if (fogCtx) {
|
|
8197
8546
|
fogCtx.translate(-bounds.x, -bounds.y);
|
|
8198
8547
|
const fogRenderer = new FogRenderer();
|
|
8199
|
-
fogRenderer.renderForExport(
|
|
8548
|
+
fogRenderer.renderForExport(
|
|
8549
|
+
fogCtx,
|
|
8550
|
+
fogState,
|
|
8551
|
+
options.fog.mode,
|
|
8552
|
+
options.fog.color,
|
|
8553
|
+
options.fog.style
|
|
8554
|
+
);
|
|
8200
8555
|
try {
|
|
8201
8556
|
const fogDataUri = fogCanvas.toDataURL("image/png");
|
|
8202
8557
|
if (fogDataUri.startsWith("data:")) {
|
|
@@ -11085,6 +11440,11 @@ var Viewport = class _Viewport {
|
|
|
11085
11440
|
get fog() {
|
|
11086
11441
|
return this.fogManager;
|
|
11087
11442
|
}
|
|
11443
|
+
setFogStyle(options) {
|
|
11444
|
+
this.fogRenderer.setOptions(options);
|
|
11445
|
+
this.renderLoop.requestRender();
|
|
11446
|
+
this.minimap?.invalidateScene();
|
|
11447
|
+
}
|
|
11088
11448
|
get snapToGrid() {
|
|
11089
11449
|
return this._snapToGrid;
|
|
11090
11450
|
}
|
|
@@ -11191,26 +11551,35 @@ var Viewport = class _Viewport {
|
|
|
11191
11551
|
const expected = base.expectedCanvasTypes ? /* @__PURE__ */ new Set([...declared, ...base.expectedCanvasTypes]) : declared;
|
|
11192
11552
|
return { ...base, htmlPainters: registry, expectedCanvasTypes: expected };
|
|
11193
11553
|
}
|
|
11194
|
-
|
|
11195
|
-
|
|
11196
|
-
|
|
11197
|
-
|
|
11198
|
-
|
|
11199
|
-
|
|
11200
|
-
|
|
11201
|
-
|
|
11554
|
+
/**
|
|
11555
|
+
* Carry constructor-configured fog presentation into both implicit exports and
|
|
11556
|
+
* explicit state/mode exports. Explicit style and legacy color overrides win.
|
|
11557
|
+
*/
|
|
11558
|
+
withFogDefaults(options) {
|
|
11559
|
+
const fog = options.fog;
|
|
11560
|
+
if (fog === false) return options;
|
|
11561
|
+
if (fog !== void 0) {
|
|
11562
|
+
if (fog.style !== void 0 || fog.color !== void 0) return options;
|
|
11563
|
+
return {
|
|
11564
|
+
...options,
|
|
11565
|
+
fog: { ...fog, style: this.fogRenderer.getResolvedStyle(fog.mode) }
|
|
11566
|
+
};
|
|
11202
11567
|
}
|
|
11568
|
+
if (!this.fogRenderer.isVisible()) return options;
|
|
11569
|
+
const state = this.fogManager.getState();
|
|
11570
|
+
if (!state) return options;
|
|
11571
|
+
const mode = this.fogRenderer.getViewMode();
|
|
11572
|
+
return {
|
|
11573
|
+
...options,
|
|
11574
|
+
fog: { state, mode, style: this.fogRenderer.getResolvedStyle(mode) }
|
|
11575
|
+
};
|
|
11576
|
+
}
|
|
11577
|
+
async exportImage(options) {
|
|
11578
|
+
const opts = this.withFogDefaults(this.withHtmlDefaults(options));
|
|
11203
11579
|
return exportImage(this.store, opts, this.layerManager);
|
|
11204
11580
|
}
|
|
11205
11581
|
async exportSVG(options) {
|
|
11206
|
-
const opts = this.withHtmlDefaults(options);
|
|
11207
|
-
if (opts.fog === void 0 && this.fogRenderer.isVisible()) {
|
|
11208
|
-
const state = this.fogManager.getState();
|
|
11209
|
-
if (state) {
|
|
11210
|
-
const mode = this.fogRenderer.getViewMode();
|
|
11211
|
-
opts.fog = { state, mode };
|
|
11212
|
-
}
|
|
11213
|
-
}
|
|
11582
|
+
const opts = this.withFogDefaults(this.withHtmlDefaults(options));
|
|
11214
11583
|
return exportSvg(this.store, opts, this.layerManager);
|
|
11215
11584
|
}
|
|
11216
11585
|
loadState(state) {
|
|
@@ -12844,15 +13213,15 @@ function applyCameraView(camera, view, canvasW, canvasH) {
|
|
|
12844
13213
|
var DEFAULT_DURATION_MS3 = 400;
|
|
12845
13214
|
var FRAMED_EPSILON = 1e-6;
|
|
12846
13215
|
var easeOutCubic2 = (t) => 1 - Math.pow(1 - t, 3);
|
|
12847
|
-
function
|
|
13216
|
+
function lerp2(a, b, k) {
|
|
12848
13217
|
return a + (b - a) * k;
|
|
12849
13218
|
}
|
|
12850
13219
|
function lerpView(from, to, k) {
|
|
12851
13220
|
return {
|
|
12852
|
-
x:
|
|
12853
|
-
y:
|
|
12854
|
-
w:
|
|
12855
|
-
h:
|
|
13221
|
+
x: lerp2(from.x, to.x, k),
|
|
13222
|
+
y: lerp2(from.y, to.y, k),
|
|
13223
|
+
w: lerp2(from.w, to.w, k),
|
|
13224
|
+
h: lerp2(from.h, to.h, k)
|
|
12856
13225
|
};
|
|
12857
13226
|
}
|
|
12858
13227
|
function viewsClose(a, b) {
|
|
@@ -14106,7 +14475,7 @@ var PencilTool = class {
|
|
|
14106
14475
|
};
|
|
14107
14476
|
|
|
14108
14477
|
// src/elements/stroke-erase.ts
|
|
14109
|
-
function
|
|
14478
|
+
function lerp3(a, b, t) {
|
|
14110
14479
|
return {
|
|
14111
14480
|
x: a.x + (b.x - a.x) * t,
|
|
14112
14481
|
y: a.y + (b.y - a.y) * t,
|
|
@@ -14165,13 +14534,13 @@ function erasePoints(points, eraser, radius) {
|
|
|
14165
14534
|
erased = true;
|
|
14166
14535
|
if (tLo > 0) {
|
|
14167
14536
|
if (current.length === 0) current.push(a);
|
|
14168
|
-
current.push(
|
|
14537
|
+
current.push(lerp3(a, b, tLo));
|
|
14169
14538
|
flush();
|
|
14170
14539
|
} else {
|
|
14171
14540
|
flush();
|
|
14172
14541
|
}
|
|
14173
14542
|
if (tHi < 1) {
|
|
14174
|
-
current = [
|
|
14543
|
+
current = [lerp3(a, b, tHi), b];
|
|
14175
14544
|
}
|
|
14176
14545
|
}
|
|
14177
14546
|
flush();
|
|
@@ -16687,7 +17056,7 @@ var FogTool = class {
|
|
|
16687
17056
|
};
|
|
16688
17057
|
|
|
16689
17058
|
// src/index.ts
|
|
16690
|
-
var VERSION = "0.
|
|
17059
|
+
var VERSION = "0.68.0";
|
|
16691
17060
|
export {
|
|
16692
17061
|
AWARENESS_MAX_SELECTION,
|
|
16693
17062
|
AWARENESS_PRESENCE_KIND,
|
|
@@ -16795,6 +17164,7 @@ export {
|
|
|
16795
17164
|
isPingPresence,
|
|
16796
17165
|
pathDistanceCells,
|
|
16797
17166
|
recommendedFogCellSize,
|
|
17167
|
+
resolveFogStyle,
|
|
16798
17168
|
resolveHtmlRouting,
|
|
16799
17169
|
setFontSize,
|
|
16800
17170
|
smartSnap,
|