@rickyzhangca/fpsr 0.0.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.
Files changed (41) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/LICENSE +21 -0
  3. package/README.md +267 -0
  4. package/dist/abort-CrgARaZd.js +43 -0
  5. package/dist/abort-CrgARaZd.js.map +1 -0
  6. package/dist/assets-Dy4fqZBV.d.ts +85 -0
  7. package/dist/assets-Dy4fqZBV.d.ts.map +1 -0
  8. package/dist/canvas.d.ts +98 -0
  9. package/dist/canvas.d.ts.map +1 -0
  10. package/dist/canvas.js +3 -0
  11. package/dist/execute-CRZV5zw1.js +963 -0
  12. package/dist/execute-CRZV5zw1.js.map +1 -0
  13. package/dist/host-DdX4g1YM.d.ts +34 -0
  14. package/dist/host-DdX4g1YM.d.ts.map +1 -0
  15. package/dist/index.d.ts +463 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +1776 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/node.d.ts +16 -0
  20. package/dist/node.d.ts.map +1 -0
  21. package/dist/node.js +101 -0
  22. package/dist/node.js.map +1 -0
  23. package/dist/planner.d.ts +418 -0
  24. package/dist/planner.d.ts.map +1 -0
  25. package/dist/planner.js +96 -0
  26. package/dist/planner.js.map +1 -0
  27. package/dist/profile-CTN1Q5SI.d.ts +365 -0
  28. package/dist/profile-CTN1Q5SI.d.ts.map +1 -0
  29. package/dist/profile-DR4i6HDp.js +610 -0
  30. package/dist/profile-DR4i6HDp.js.map +1 -0
  31. package/dist/render-db-BFQIspDB.d.ts +426 -0
  32. package/dist/render-db-BFQIspDB.d.ts.map +1 -0
  33. package/dist/render-db.d.ts +2 -0
  34. package/dist/render-db.js +1 -0
  35. package/dist/tiled-draw-list-Dc-5Ledi.js +3990 -0
  36. package/dist/tiled-draw-list-Dc-5Ledi.js.map +1 -0
  37. package/dist/types-CHgZBSra.d.ts +105 -0
  38. package/dist/types-CHgZBSra.d.ts.map +1 -0
  39. package/dist/upgrade-planner-CNjnMHPJ.d.ts +385 -0
  40. package/dist/upgrade-planner-CNjnMHPJ.d.ts.map +1 -0
  41. package/package.json +86 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,10 @@
1
+ # Changelog
2
+
3
+ ## 0.0.1
4
+
5
+ First public release.
6
+
7
+ - ESM library for browser and Node.js targeting Factorio **2.1.11** (vanilla + Space Age + Elevated Rails + Quality)
8
+ - Entry points: `@rickyzhangca/fpsr`, `@rickyzhangca/fpsr/planner`, `@rickyzhangca/fpsr/canvas`, `@rickyzhangca/fpsr/render-db`, `@rickyzhangca/fpsr/node`
9
+ - Decode/encode blueprint strings, resolve connectivity, plan draw lists, and render to Canvas2D
10
+ - Sprite atlases and render database are **not** bundled — load via `cdnAssets` or `localAssets` from your own pipeline output
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ricky Zhang
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,267 @@
1
+ # fpsr
2
+
3
+ Headless **Factorio 2.1.11** blueprint-string renderer for browser and Node.js.
4
+ Decodes compressed blueprint strings, resolves connectivity-aware sprite variants
5
+ (belts, pipes, walls, rails, trains, wires), plans a serializable draw list, and
6
+ executes it on a Canvas2D backend.
7
+
8
+ Supports vanilla plus **Space Age**, **Elevated Rails**, and **Quality** content
9
+ from the official data packages.
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ npm install @rickyzhangca/fpsr
15
+ ```
16
+
17
+ For Node.js rendering, add the optional peer dependency:
18
+
19
+ ```bash
20
+ npm install skia-canvas
21
+ ```
22
+
23
+ The npm package ships **code only** — sprite atlases and the render database are
24
+ not included (see [Asset hosting](#asset-hosting)).
25
+
26
+ ## Quick start (browser)
27
+
28
+ ```ts
29
+ import { cdnAssets, createRenderer, decode } from "@rickyzhangca/fpsr";
30
+
31
+ const doc = decode(blueprintString);
32
+ const renderer = await createRenderer({
33
+ assets: cdnAssets("https://your-cdn.example.com/2.1.11"),
34
+ });
35
+
36
+ const { canvas } = await renderer.render(doc);
37
+ document.body.appendChild(canvas as HTMLCanvasElement);
38
+ ```
39
+
40
+ `createRenderer` loads the render database once, plans the draw list, sizes a
41
+ canvas from blueprint bounds, and paints atlas sprites. Pass `blueprintPath` when
42
+ rendering a nested blueprint book entry.
43
+
44
+ ### Large PNG export
45
+
46
+ For full-resolution images that may exceed browser canvas limits, use the tiled
47
+ PNG exporter. It renders bounded temporary canvases and feeds their rows into an
48
+ incremental PNG encoder, so it never creates one canvas at the final dimensions:
49
+
50
+ ```ts
51
+ const { blob, width, height } = await renderer.renderTiledPng(doc, {
52
+ pixelsPerTile: 64,
53
+ tileSize: 2048,
54
+ onProgress(event) {
55
+ console.log(event);
56
+ },
57
+ });
58
+
59
+ const link = document.createElement("a");
60
+ link.href = URL.createObjectURL(blob);
61
+ link.download = `blueprint-${width}x${height}.png`;
62
+ link.click();
63
+ URL.revokeObjectURL(link.href);
64
+ ```
65
+
66
+ The exporter always produces lossless PNG. Canvas-based WebP export remains
67
+ available through `render(...).toImageBlob({ type: "image/webp" })` for outputs
68
+ that fit safely in one canvas.
69
+
70
+ ## Quick start (Node.js)
71
+
72
+ ```ts
73
+ import { writeFile } from "node:fs/promises";
74
+ import { Canvas } from "skia-canvas";
75
+ import { createRenderer, decode } from "@rickyzhangca/fpsr";
76
+ import { localAssets } from "@rickyzhangca/fpsr/node";
77
+
78
+ const doc = decode(blueprintString);
79
+ const renderer = await createRenderer({
80
+ assets: localAssets("assets-out/2.1.11"),
81
+ createCanvas: (w, h) => new Canvas(w, h),
82
+ });
83
+
84
+ const { toPngBuffer } = await renderer.render(doc);
85
+ await writeFile("out.png", await toPngBuffer());
86
+ ```
87
+
88
+ `localAssets` reads schema-2 `manifest.json` and the selected `1x` or `2x`
89
+ content-addressed render DB and atlas files from a pipeline output directory on
90
+ disk. Asset sources default to `2x`; callers can set `assetTier` when creating a
91
+ renderer. The render DB's `assetDensity` must match the selected tier or
92
+ `createRenderer` throws `AssetDensityMismatchError`.
93
+
94
+ ## Package entry points
95
+
96
+ | Import | Contents |
97
+ | ---------------- | ------------------------------------------------------------------------ |
98
+ | `@rickyzhangca/fpsr` | Decode/encode, books, migrate, `createRenderer`, `cdnAssets`, core types |
99
+ | `@rickyzhangca/fpsr/planner` | `resolve`, `planDrawList`, draw-list helpers, belt/train/wire utilities |
100
+ | `@rickyzhangca/fpsr/canvas` | `executeDrawList`, background painters, silhouette helpers |
101
+ | `@rickyzhangca/fpsr/render-db` | Full `RenderDb` / entity / terrain type surface |
102
+ | `@rickyzhangca/fpsr/node` | `localAssets` (filesystem; requires `skia-canvas`) |
103
+
104
+ ## Blueprint books
105
+
106
+ ```ts
107
+ import { decode, listBlueprints, selectBlueprint } from "@rickyzhangca/fpsr";
108
+
109
+ const doc = decode(bookString);
110
+ const refs = listBlueprints(doc); // flattened tree with paths
111
+
112
+ const bp = selectBlueprint(doc, [0, 2]); // third child of first entry
113
+ ```
114
+
115
+ `selectBlueprint(doc)` without a path follows the book's `active_index` chain.
116
+
117
+ ## Pure planning (no assets)
118
+
119
+ For tests, diff tooling, or custom backends, call the pure pipeline stages
120
+ directly — no images or network required beyond loading the render DB named by
121
+ `manifest.json`:
122
+
123
+ ```ts
124
+ import { decode, migrateTo2x, selectBlueprint } from "@rickyzhangca/fpsr";
125
+ import { planDrawList, resolve, serializeDrawList } from "@rickyzhangca/fpsr/planner";
126
+ import renderDb from "./render-db.json" with { type: "json" };
127
+
128
+ const bp = migrateTo2x(selectBlueprint(decode(source))); // optional; plan/resolve also migrate
129
+ const { entities, warnings } = resolve(bp, renderDb);
130
+ const list = planDrawList(bp, renderDb, { altMode: true });
131
+ const snapshot = serializeDrawList(list); // stable JSON for fixtures
132
+ ```
133
+
134
+ `serializeDrawList` rounds coordinates to four decimal places with stable key
135
+ order for reviewable text diffs.
136
+
137
+ ## Backgrounds
138
+
139
+ `RenderOptions.background` is a discriminated union:
140
+
141
+ ```ts
142
+ await renderer.render(doc, { background: { type: "auto" } });
143
+ await renderer.render(doc, { background: { type: "checkerboard" } });
144
+ await renderer.render(doc, { background: { type: "space", planet: true, planetName: "nauvis" } });
145
+ await renderer.render(doc, { background: { type: "terrain", name: "dirt" } });
146
+ await renderer.render(doc, { background: { type: "none" } });
147
+ ```
148
+
149
+ Unknown terrain names throw `UnknownTerrainBackgroundError` (they do not render
150
+ as transparent).
151
+
152
+ ## AbortSignal
153
+
154
+ `renderer.render` / `measure` / `renderTiledPng` honor `signal`. Asset loads via
155
+ `cdnAssets` / `localAssets` accept `loadAtlasImage(i, tier, { signal })`.
156
+
157
+ **Semantics:** aborting rejects only the waiting caller with `AbortError`. Shared
158
+ in-flight fetches/decodes continue for other concurrent consumers of the same
159
+ cache key. Aborted waits never poison a successful cache entry; genuine load
160
+ failures clear the shared promise so a later call can retry.
161
+
162
+ ## API reference
163
+
164
+ ### Decoding
165
+
166
+ | Export | Kind | Description |
167
+ | ----------------------- | -------- | ----------------------------------------------------------------- |
168
+ | `decode` | function | Parse a blueprint string or raw JSON string → `BlueprintDocument` |
169
+ | `encode` | function | Serialize a `BlueprintDocument` back to a blueprint string |
170
+ | `migrateTo2x` | function | Migrate a 1.x `Blueprint` to 2.x shape (idempotent) |
171
+ | `migrateDocumentTo2x` | function | Migrate every nested blueprint in a document / book |
172
+ | `BLUEPRINT_ADAPTERS` | const | Ordered 1.x→2.x adapter registry |
173
+ | `BlueprintAdapter` | type | `{ id, apply }` adapter entry |
174
+ | `BlueprintDecodeError` | class | Thrown when `decode` fails |
175
+ | `BlueprintDecodeReason` | type | Error reason codes for decode failures |
176
+ | `decodeVersion` | function | Split encoded `version` into major/minor/patch |
177
+
178
+ ### Blueprint books
179
+
180
+ | Export | Kind | Description |
181
+ | ----------------------- | -------- | ------------------------------------------------------- |
182
+ | `listBlueprints` | function | Flatten a book tree → `BlueprintRef[]` with index paths |
183
+ | `selectBlueprint` | function | Select a nested blueprint by optional `number[]` path |
184
+ | `BlueprintSelectError` | class | Thrown when book navigation fails |
185
+ | `BlueprintSelectReason` | type | Error reason codes for selection failures |
186
+
187
+ ### Planner (`@rickyzhangca/fpsr/planner`)
188
+
189
+ | Export | Kind | Description |
190
+ | ------------------------- | -------- | -------------------------------------------------------------------------- |
191
+ | `resolve` | function | `(bp, renderDb, opts?)` → `{ entities, warnings }` |
192
+ | `planDrawList` | function | `(bp, renderDb, opts?)` → `DrawList` |
193
+ | `planDrawListWithOptions` | function | Same as plan; returns `{ drawList, profile? }` when `profile: true` |
194
+ | `analyzePlan` | function | `(bp, drawList, renderDb)` → resolve coverage + draw-list integrity checks |
195
+ | `PlanOptions` | type | `altMode`, `beltEndings`, optional `profile` |
196
+ | `serializeDrawList` | function | Stable JSON serialization for fixtures |
197
+
198
+ ### Rendering (`@rickyzhangca/fpsr`)
199
+
200
+ | Export | Kind | Description |
201
+ | ------------------------------- | -------- | -------------------------------------------------------------------- |
202
+ | `createRenderer` | function | High-level async renderer (plan + load + paint) |
203
+ | `cdnAssets` / `localAssets` | function | HTTP / filesystem `AssetSource` |
204
+ | `MeasureOptions` | type | Layout-only options for `measure` |
205
+ | `RenderOptions` | type | Public paint options (`background`, `altMode`, `showCoordinates`, …) |
206
+ | `TiledPngOptions` | type | Full-resolution PNG options (no `profile` / canvas / maxOutputSize) |
207
+ | `RenderBackground` | type | Discriminated background mode |
208
+ | `AssetDensityMismatchError` | class | Thrown when render-db density does not match `assetTier` |
209
+ | `UnknownTerrainBackgroundError` | class | Thrown for unknown `background: { type: "terrain", name }` |
210
+ | `Renderer` | type | `{ measure, render, renderTiledPng, dispose }` |
211
+
212
+ `Renderer.dispose()` clears renderer-owned icon/silhouette caches only. It does
213
+ **not** call `AssetSource.dispose()` or close atlas images owned by the asset
214
+ source — those remain the caller's responsibility.
215
+
216
+ ### Canvas (`@rickyzhangca/fpsr/canvas`)
217
+
218
+ | Export | Kind | Description |
219
+ | ----------------------- | -------- | --------------------------------- |
220
+ | `executeDrawList` | function | Paint a `DrawList` onto a context |
221
+ | `Canvas2DContextLike` | type | Minimal Canvas2D context |
222
+ | `drawTileCheckerboard` | function | Tile-aligned checkerboard fill |
223
+ | `drawSpaceBackground` | function | Procedural starfield |
224
+ | `drawTerrainBackground` | function | Named terrain patches |
225
+
226
+ ### Blueprint / render-db types
227
+
228
+ | Export | Kind | Description |
229
+ | -------------------- | ---- | ---------------------------------------------- |
230
+ | `BlueprintDocument` | type | Exactly-one top-level wrapper (`blueprint`, …) |
231
+ | `BlueprintBookEntry` | type | Exactly-one book-slot content key |
232
+ | `RenderDb` | type | Pipeline-produced sprite/layer database |
233
+ | `FrameId` | type | Numeric frame index into `RenderDb.frames` |
234
+ | `FrameMeta` | type | Atlas frame trim and scale metadata |
235
+
236
+ ## Asset hosting
237
+
238
+ Rendering requires a content-addressed **render database** and packed **sprite
239
+ atlases** in deterministic `1x` and `2x` tiers described by a schema-2
240
+ `manifest.json`. These are generated from **your own game files** using the offline pipeline in this repository
241
+ ([`@fpsr/pipeline`](https://github.com/rickyzhangca/fpsr/tree/main/tools/pipeline)).
242
+
243
+ They are **never redistributed** with the npm package or committed to git. Host
244
+ them on private infrastructure (`cdnAssets`) or load from disk (`localAssets`).
245
+
246
+ ## Versioning
247
+
248
+ - **npm package**: semver; breaking changes may land in 0.x.
249
+ - **Render database schema**: version `2` (see `RenderDb` in source).
250
+ - **Game target**: Factorio **2.1.11** exactly (vanilla + Space Age + Elevated
251
+ Rails + Quality).
252
+
253
+ A new game version requires regenerating assets and may require a matching
254
+ library release.
255
+
256
+ ## License
257
+
258
+ **Source code** in this package is released under the [MIT License](LICENSE).
259
+
260
+ **Factorio game assets** — all sprites, icons, and atlases are the property of
261
+ **Wube Software Ltd.** They are not included in this package and must not be
262
+ redistributed. Generate assets locally from a licensed Factorio installation via
263
+ `@fpsr/pipeline`. The monorepo commits a metadata-only render-db test fixture (no
264
+ pixel data); golden PNG test renders are not redistributed.
265
+
266
+ _Factorio is a trademark of Wube Software Ltd. This project is not affiliated
267
+ with or endorsed by Wube Software._
@@ -0,0 +1,43 @@
1
+ //#region src/abort.ts
2
+ /**
3
+ * Abort helpers shared by asset sources and the renderer atlas cache.
4
+ *
5
+ * Semantics: aborting rejects only the waiting caller. Shared underlying work
6
+ * continues for other concurrent consumers and is never cancelled by a waiter.
7
+ */
8
+ function abortError(message = "Operation aborted") {
9
+ const error = new Error(message);
10
+ error.name = "AbortError";
11
+ return error;
12
+ }
13
+ function throwIfAborted(signal, message) {
14
+ if (!signal?.aborted) return;
15
+ if (typeof signal.throwIfAborted === "function") signal.throwIfAborted();
16
+ throw abortError(message);
17
+ }
18
+ /**
19
+ * Race a shared promise against an AbortSignal without cancelling the shared work.
20
+ */
21
+ function raceWithAbort(promise, signal) {
22
+ if (!signal) return promise;
23
+ throwIfAborted(signal);
24
+ return new Promise((resolve, reject) => {
25
+ const onAbort = () => {
26
+ signal.removeEventListener("abort", onAbort);
27
+ reject(abortError());
28
+ };
29
+ signal.addEventListener("abort", onAbort, { once: true });
30
+ promise.then((value) => {
31
+ signal.removeEventListener("abort", onAbort);
32
+ if (signal.aborted) reject(abortError());
33
+ else resolve(value);
34
+ }, (error) => {
35
+ signal.removeEventListener("abort", onAbort);
36
+ reject(error);
37
+ });
38
+ });
39
+ }
40
+ //#endregion
41
+ export { throwIfAborted as n, raceWithAbort as t };
42
+
43
+ //# sourceMappingURL=abort-CrgARaZd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"abort-CrgARaZd.js","names":[],"sources":["../src/abort.ts"],"sourcesContent":["/**\n * Abort helpers shared by asset sources and the renderer atlas cache.\n *\n * Semantics: aborting rejects only the waiting caller. Shared underlying work\n * continues for other concurrent consumers and is never cancelled by a waiter.\n */\n\nexport function abortError(message = \"Operation aborted\"): Error {\n const error = new Error(message);\n error.name = \"AbortError\";\n return error;\n}\n\nexport function throwIfAborted(signal?: AbortSignal, message?: string): void {\n if (!signal?.aborted) return;\n if (typeof signal.throwIfAborted === \"function\") signal.throwIfAborted();\n throw abortError(message);\n}\n\n/**\n * Race a shared promise against an AbortSignal without cancelling the shared work.\n */\nexport function raceWithAbort<T>(promise: Promise<T>, signal?: AbortSignal): Promise<T> {\n if (!signal) return promise;\n throwIfAborted(signal);\n return new Promise<T>((resolve, reject) => {\n const onAbort = () => {\n signal.removeEventListener(\"abort\", onAbort);\n reject(abortError());\n };\n signal.addEventListener(\"abort\", onAbort, { once: true });\n promise.then(\n (value) => {\n signal.removeEventListener(\"abort\", onAbort);\n if (signal.aborted) reject(abortError());\n else resolve(value);\n },\n (error) => {\n signal.removeEventListener(\"abort\", onAbort);\n reject(error);\n },\n );\n });\n}\n"],"mappings":";;;;;;;AAOA,SAAgB,WAAW,UAAU,qBAA4B;CAC/D,MAAM,QAAQ,IAAI,MAAM,OAAO;CAC/B,MAAM,OAAO;CACb,OAAO;AACT;AAEA,SAAgB,eAAe,QAAsB,SAAwB;CAC3E,IAAI,CAAC,QAAQ,SAAS;CACtB,IAAI,OAAO,OAAO,mBAAmB,YAAY,OAAO,eAAe;CACvE,MAAM,WAAW,OAAO;AAC1B;;;;AAKA,SAAgB,cAAiB,SAAqB,QAAkC;CACtF,IAAI,CAAC,QAAQ,OAAO;CACpB,eAAe,MAAM;CACrB,OAAO,IAAI,SAAY,SAAS,WAAW;EACzC,MAAM,gBAAgB;GACpB,OAAO,oBAAoB,SAAS,OAAO;GAC3C,OAAO,WAAW,CAAC;EACrB;EACA,OAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;EACxD,QAAQ,MACL,UAAU;GACT,OAAO,oBAAoB,SAAS,OAAO;GAC3C,IAAI,OAAO,SAAS,OAAO,WAAW,CAAC;QAClC,QAAQ,KAAK;EACpB,IACC,UAAU;GACT,OAAO,oBAAoB,SAAS,OAAO;GAC3C,OAAO,KAAK;EACd,CACF;CACF,CAAC;AACH"}
@@ -0,0 +1,85 @@
1
+ import { c as ImageSource, t as AssetLoadOptions } from "./host-DdX4g1YM.js";
2
+ import { v as RenderDb } from "./render-db-BFQIspDB.js";
3
+ import { t as AssetEvent } from "./profile-CTN1Q5SI.js";
4
+
5
+ //#region src/assets.d.ts
6
+ /**
7
+ * Pluggable atlas / render-db loader. Browser uses ImageBitmap; node uses
8
+ * skia-canvas Image (via fpsr/node).
9
+ *
10
+ * ## AbortSignal semantics
11
+ *
12
+ * `loadRenderDb` / `loadAtlasImage` accept an optional `signal` on the load
13
+ * options. Aborting rejects **only the waiting caller** with an `AbortError`.
14
+ * Shared in-flight network/decode work is **not** cancelled for other
15
+ * concurrent consumers of the same cache key. Aborted waits never write into
16
+ * or delete a successful cache entry; only genuine load failures clear the
17
+ * shared promise slot so a later call can retry.
18
+ */
19
+ interface AssetSource {
20
+ loadRenderDb(tier?: AssetTier, options?: AssetLoadOptions): Promise<RenderDb>;
21
+ loadAtlasImage(index: number, tier?: AssetTier, options?: AssetLoadOptions): Promise<ImageSource>;
22
+ /**
23
+ * Optional: register canvas text fonts listed in the asset manifest (e.g. DejaVu).
24
+ * Missing fonts are ignored so renders still work with CSS fallbacks.
25
+ */
26
+ ensureFonts?(options?: AssetLoadOptions): Promise<void>;
27
+ /**
28
+ * Optional: release retained decoded images / clear caches.
29
+ * Ownership stays with the AssetSource caller — Renderer.dispose never invokes this.
30
+ */
31
+ dispose?(): void;
32
+ }
33
+ type AssetTier = "1x" | "2x";
34
+ interface ManifestAtlas {
35
+ file: string;
36
+ w: number;
37
+ h: number;
38
+ sha256?: string;
39
+ }
40
+ interface ManifestFont {
41
+ file: string;
42
+ family: string;
43
+ sha256?: string;
44
+ bytes?: number;
45
+ }
46
+ interface AssetManifest {
47
+ schema: 2;
48
+ gameVersion: string;
49
+ mods: string[];
50
+ tiers: Record<AssetTier, AssetTierManifest>;
51
+ /** Optional bundled TTFs for canvas text (e.g. DejaVu Sans). */
52
+ fonts?: ManifestFont[];
53
+ }
54
+ interface AssetTierManifest {
55
+ density: 1 | 2;
56
+ atlases: ManifestAtlas[];
57
+ renderDb: {
58
+ file: string;
59
+ sha256: string;
60
+ bytes?: number;
61
+ };
62
+ }
63
+ interface CdnAssetsOptions {
64
+ /**
65
+ * Decode an atlas blob when `createImageBitmap` is unavailable (e.g. Node 22
66
+ * with fetch but no ImageBitmap). Required for node consumers of cdnAssets;
67
+ * browser builds ignore this when createImageBitmap exists.
68
+ */
69
+ decodeImage?: (blob: Blob, signal?: AbortSignal) => Promise<ImageSource>;
70
+ fetchImpl?: typeof fetch;
71
+ /** Maximum simultaneous bitmap decodes. Fetches remain concurrent. Default: 2. */
72
+ maxConcurrentDecodes?: number;
73
+ /** Optional sink for fetch/decode timing events (profiling). */
74
+ onAssetEvent?: (event: AssetEvent) => void;
75
+ }
76
+ /**
77
+ * Fetch-based AssetSource for CDN (or any HTTP) layouts:
78
+ * {baseUrl}/manifest.json
79
+ * {baseUrl}/{manifest.tiers[tier].renderDb.file}
80
+ * {baseUrl}/{atlas.file}
81
+ */
82
+ declare function cdnAssets(baseUrl: string, options?: CdnAssetsOptions): AssetSource;
83
+ //#endregion
84
+ export { CdnAssetsOptions as a, AssetTierManifest as i, AssetSource as n, ManifestFont as o, AssetTier as r, cdnAssets as s, AssetManifest as t };
85
+ //# sourceMappingURL=assets-Dy4fqZBV.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assets-Dy4fqZBV.d.ts","names":[],"sources":["../src/assets.ts"],"mappings":";;;;;;;AAkBA;;;;;;;;;;;UAAiB,WAAA;EACf,YAAA,CAAa,IAAA,GAAO,SAAA,EAAW,OAAA,GAAU,gBAAA,GAAmB,OAAA,CAAQ,QAAA;EACpE,cAAA,CAAe,KAAA,UAAe,IAAA,GAAO,SAAA,EAAW,OAAA,GAAU,gBAAA,GAAmB,OAAA,CAAQ,WAAA;EAKpC;;;;EAAjD,WAAA,EAAa,OAAA,GAAU,gBAAA,GAAmB,OAAA;EANX;;;;EAW/B,OAAA;AAAA;AAAA,KAGU,SAAA;AAAA,UAEK,aAAA;EACf,IAAA;EACA,CAAA;EACA,CAAA;EACA,MAAA;AAAA;AAAA,UAGe,YAAA;EACf,IAAA;EACA,MAAA;EACA,MAAA;EACA,KAAA;AAAA;AAAA,UAGe,aAAA;EACf,MAAA;EACA,WAAA;EACA,IAAA;EACA,KAAA,EAAO,MAAA,CAAO,SAAA,EAAW,iBAAA;EAlBG;EAoB5B,KAAA,GAAQ,YAAA;AAAA;AAAA,UAGO,iBAAA;EACf,OAAA;EACA,OAAA,EAAS,aAAa;EACtB,QAAA;IACE,IAAA;IACA,MAAA;IACA,KAAA;EAAA;AAAA;AAAA,UAIa,gBAAA;EAzBf;;;;;EA+BA,WAAA,IAAe,IAAA,EAAM,IAAA,EAAM,MAAA,GAAS,WAAA,KAAgB,OAAA,CAAQ,WAAA;EAC5D,SAAA,UAAmB,KAAA;EA1BS;EA4B5B,oBAAA;EAxBc;EA0Bd,YAAA,IAAgB,KAAA,EAAO,UAAA;AAAA;;;;;;;iBAoFT,SAAA,CAAU,OAAA,UAAiB,OAAA,GAAU,gBAAA,GAAmB,WAAW"}
@@ -0,0 +1,98 @@
1
+ import { c as ImageSource, s as ImageDataLike } from "./host-DdX4g1YM.js";
2
+ import { h as FrameMeta, w as TerrainPatchBackground } from "./render-db-BFQIspDB.js";
3
+ import { n as ExecuteDrawListOptions, r as ExecuteDrawListStats, t as Canvas2DContextLike } from "./types-CHgZBSra.js";
4
+ import { c as perfMark, l as perfMeasure, p as DrawList, s as nowMs, u as TileFrame } from "./profile-CTN1Q5SI.js";
5
+
6
+ //#region src/canvas2d/execute.d.ts
7
+ /**
8
+ * Execute a sorted draw list against a Canvas2D context.
9
+ * `images[i]` is the atlas image for atlas index i.
10
+ */
11
+ declare function executeDrawList(ctx: Canvas2DContextLike, list: DrawList, images: ImageSource[], opts: ExecuteDrawListOptions): void;
12
+ //#endregion
13
+ //#region src/icon-silhouette.d.ts
14
+ /** Pixel radius for feMorphology-style alpha dilation on entity-info icons. */
15
+ declare const ENTITY_INFO_SILHOUETTE_RADIUS_PX = 12;
16
+ /** Soft blur applied after dilation to feather the silhouette edge. */
17
+ declare const ENTITY_INFO_SILHOUETTE_BLUR_PX = 16;
18
+ /** Total source-pixel padding around icon crops for spread + blur. */
19
+ declare function entityInfoSilhouettePadPx(dilateRadius?: number, blurRadius?: number): number;
20
+ interface ImageDataContext {
21
+ drawImage(image: ImageSource, dx: number, dy: number): void;
22
+ createImageData?(sw: number, sh: number): ImageDataLike;
23
+ getImageData(sx: number, sy: number, sw: number, sh: number): ImageDataLike;
24
+ putImageData(data: ImageDataLike, dx: number, dy: number): void;
25
+ }
26
+ interface SilhouetteCanvasLike {
27
+ width: number;
28
+ height: number;
29
+ getContext(type: "2d"): ImageDataContext | null;
30
+ }
31
+ /**
32
+ * Dilate and blur an isolated icon crop into a padded black silhouette image.
33
+ * Returns undefined when the canvas context cannot read/write ImageData.
34
+ */
35
+ declare function bakeEntityInfoSilhouette(iconImage: ImageSource, iconWidth: number, iconHeight: number, createCanvas: (width: number, height: number) => SilhouetteCanvasLike, dilateRadius?: number, blurRadius?: number): ImageSource | undefined;
36
+ //#endregion
37
+ //#region src/coordinate-overlay.d.ts
38
+ /**
39
+ * Draw map-space tile grid lines and coordinate labels over a rendered preview.
40
+ * Canvas (0,0) corresponds to `tileFrame.minX/minY`.
41
+ */
42
+ declare function drawCoordinateOverlay(ctx: Canvas2DContextLike, tileFrame: TileFrame, pixelsPerTile: number, width: number, height: number, outputTileFrame?: TileFrame): void;
43
+ //#endregion
44
+ //#region src/checkerboard.d.ts
45
+ /** Fill `width`×`height` with an axis-aligned checkerboard; one cell = `pixelsPerTile` px. */
46
+ declare function drawTileCheckerboard(ctx: Canvas2DContextLike, width: number, height: number, pixelsPerTile: number, tileOffsetX?: number, tileOffsetY?: number): void;
47
+ /** Paint an image on top of a tile-aligned checkerboard (for external reference PNGs). */
48
+ declare function blitWithTileCheckerboard(canvas: {
49
+ width: number;
50
+ height: number;
51
+ getContext(type: "2d"): Canvas2DContextLike | null;
52
+ }, image: ImageSource, width: number, height: number, pixelsPerTile: number): void;
53
+ //#endregion
54
+ //#region src/space-background.d.ts
55
+ interface SpacePlanetDecoration {
56
+ frame: FrameMeta;
57
+ image: ImageSource;
58
+ }
59
+ interface DrawSpaceBackgroundOptions {
60
+ /** Optional starmap planet drawn bottom-left after the starfield. */
61
+ planet?: SpacePlanetDecoration;
62
+ /** Stable salt; selected planets use different skies while rerenders remain reproducible. */
63
+ seed?: number;
64
+ /** Full output dimensions and this viewport's offset within it (for tiled rendering). */
65
+ viewport?: {
66
+ x: number;
67
+ y: number;
68
+ fullWidth: number;
69
+ fullHeight: number;
70
+ };
71
+ }
72
+ /**
73
+ * Draw the space-platform planet peeking from the bottom-left, matching
74
+ * Factorio's backdrop offset ratios scaled to the canvas.
75
+ */
76
+ declare function drawSpacePlanet(ctx: Canvas2DContextLike, width: number, height: number, planet: SpacePlanetDecoration, viewport?: DrawSpaceBackgroundOptions["viewport"]): void;
77
+ /** Fill `width`×`height` with a layered deterministic starfield. */
78
+ declare function drawSpaceBackground(ctx: Canvas2DContextLike, width: number, height: number, options?: DrawSpaceBackgroundOptions): void;
79
+ //#endregion
80
+ //#region src/terrain-background.d.ts
81
+ interface DrawTerrainBackgroundOptions {
82
+ tileFrame: TileFrame;
83
+ pixelsPerTile: number;
84
+ frames: FrameMeta[];
85
+ images: ImageSource[];
86
+ background?: TerrainPatchBackground;
87
+ fallbackColor: [number, number, number, number];
88
+ }
89
+ /**
90
+ * Paint an infinite terrain plane anchored to absolute map coordinates.
91
+ * Only complete, untransformed Factorio patches are drawn. Larger regions may
92
+ * be subdivided into complete smaller authored patches, so every destination is
93
+ * covered exactly once and every texture edge keeps its intended orientation.
94
+ */
95
+ declare function drawTerrainBackground(ctx: Canvas2DContextLike, width: number, height: number, options: DrawTerrainBackgroundOptions): void;
96
+ //#endregion
97
+ export { type Canvas2DContextLike, type DrawSpaceBackgroundOptions, type DrawTerrainBackgroundOptions, ENTITY_INFO_SILHOUETTE_BLUR_PX, ENTITY_INFO_SILHOUETTE_RADIUS_PX, type ExecuteDrawListOptions, type ExecuteDrawListStats, type SpacePlanetDecoration, bakeEntityInfoSilhouette, blitWithTileCheckerboard, drawCoordinateOverlay, drawSpaceBackground, drawSpacePlanet, drawTerrainBackground, drawTileCheckerboard, entityInfoSilhouettePadPx, executeDrawList, nowMs, perfMark, perfMeasure };
98
+ //# sourceMappingURL=canvas.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canvas.d.ts","names":[],"sources":["../src/canvas2d/execute.ts","../src/icon-silhouette.ts","../src/coordinate-overlay.ts","../src/checkerboard.ts","../src/space-background.ts","../src/terrain-background.ts"],"mappings":";;;;;;;;;AAyBA;iBAAgB,eAAA,CACd,GAAA,EAAK,mBAAA,EACL,IAAA,EAAM,QAAA,EACN,MAAA,EAAQ,WAAA,IACR,IAAA,EAAM,sBAAA;;;;cC1BK,gCAAA;;cAEA,8BAAA;;iBAGG,yBAAA,CACd,YAAA,WACA,UAA2C;AAAA,UAK5B,gBAAA;EACf,SAAA,CAAU,KAAA,EAAO,WAAA,EAAa,EAAA,UAAY,EAAA;EAC1C,eAAA,EAAiB,EAAA,UAAY,EAAA,WAAa,aAAA;EAC1C,YAAA,CAAa,EAAA,UAAY,EAAA,UAAY,EAAA,UAAY,EAAA,WAAa,aAAA;EAC9D,YAAA,CAAa,IAAA,EAAM,aAAA,EAAe,EAAA,UAAY,EAAA;AAAA;AAAA,UAuK/B,oBAAA;EACf,KAAA;EACA,MAAA;EACA,UAAA,CAAW,IAAA,SAAa,gBAAgB;AAAA;;;AA1L1C;;iBAiOgB,wBAAA,CACd,SAAA,EAAW,WAAA,EACX,SAAA,UACA,UAAA,UACA,YAAA,GAAe,KAAA,UAAe,MAAA,aAAmB,oBAAA,EACjD,YAAA,WACA,UAAA,YACC,WAAA;;;;;;;iBCzNa,qBAAA,CACd,GAAA,EAAK,mBAAA,EACL,SAAA,EAAW,SAAA,EACX,aAAA,UACA,KAAA,UACA,MAAA,UACA,eAAA,GAAiB,SAAA;;;;iBCjBH,oBAAA,CACd,GAAA,EAAK,mBAAmB,EACxB,KAAA,UACA,MAAA,UACA,aAAA,UACA,WAAA,WACA,WAAA;;iBAoBc,wBAAA,CACd,MAAA;EACE,KAAA;EACA,MAAA;EACA,UAAA,CAAW,IAAA,SAAa,mBAAA;AAAA,GAE1B,KAAA,EAAO,WAAW,EAClB,KAAA,UACA,MAAA,UACA,aAAA;;;UCOe,qBAAA;EACf,KAAA,EAAO,SAAA;EACP,KAAA,EAAO,WAAW;AAAA;AAAA,UAGH,0BAAA;;EAEf,MAAA,GAAS,qBAAqB;EJ7BxB;EI+BN,IAAA;EJ7BM;EI+BN,QAAA;IACE,CAAA;IACA,CAAA;IACA,SAAA;IACA,UAAA;EAAA;AAAA;;;;;iBAQY,eAAA,CACd,GAAA,EAAK,mBAAA,EACL,KAAA,UACA,MAAA,UACA,MAAA,EAAQ,qBAAA,EACR,QAAA,GAAW,0BAAA;;iBAiFG,mBAAA,CACd,GAAA,EAAK,mBAAA,EACL,KAAA,UACA,MAAA,UACA,OAAA,GAAU,0BAA0B;;;UC9BrB,4BAAA;EACf,SAAA,EAAW,SAAA;EACX,aAAA;EACA,MAAA,EAAQ,SAAA;EACR,MAAA,EAAQ,WAAA;EACR,UAAA,GAAa,sBAAA;EACb,aAAA;AAAA;;;;;;;iBASc,qBAAA,CACd,GAAA,EAAK,mBAAA,EACL,KAAA,UACA,MAAA,UACA,OAAA,EAAS,4BAA4B"}
package/dist/canvas.js ADDED
@@ -0,0 +1,3 @@
1
+ import { d as bakeEntityInfoSilhouette, l as ENTITY_INFO_SILHOUETTE_BLUR_PX, n as perfMark, p as entityInfoSilhouettePadPx, r as perfMeasure, t as nowMs, u as ENTITY_INFO_SILHOUETTE_RADIUS_PX } from "./profile-DR4i6HDp.js";
2
+ import { a as drawCoordinateOverlay, i as drawSpacePlanet, n as drawTerrainBackground, o as blitWithTileCheckerboard, r as drawSpaceBackground, s as drawTileCheckerboard, t as executeDrawList } from "./execute-CRZV5zw1.js";
3
+ export { ENTITY_INFO_SILHOUETTE_BLUR_PX, ENTITY_INFO_SILHOUETTE_RADIUS_PX, bakeEntityInfoSilhouette, blitWithTileCheckerboard, drawCoordinateOverlay, drawSpaceBackground, drawSpacePlanet, drawTerrainBackground, drawTileCheckerboard, entityInfoSilhouettePadPx, executeDrawList, nowMs, perfMark, perfMeasure };