@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.
- package/CHANGELOG.md +10 -0
- package/LICENSE +21 -0
- package/README.md +267 -0
- package/dist/abort-CrgARaZd.js +43 -0
- package/dist/abort-CrgARaZd.js.map +1 -0
- package/dist/assets-Dy4fqZBV.d.ts +85 -0
- package/dist/assets-Dy4fqZBV.d.ts.map +1 -0
- package/dist/canvas.d.ts +98 -0
- package/dist/canvas.d.ts.map +1 -0
- package/dist/canvas.js +3 -0
- package/dist/execute-CRZV5zw1.js +963 -0
- package/dist/execute-CRZV5zw1.js.map +1 -0
- package/dist/host-DdX4g1YM.d.ts +34 -0
- package/dist/host-DdX4g1YM.d.ts.map +1 -0
- package/dist/index.d.ts +463 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1776 -0
- package/dist/index.js.map +1 -0
- package/dist/node.d.ts +16 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +101 -0
- package/dist/node.js.map +1 -0
- package/dist/planner.d.ts +418 -0
- package/dist/planner.d.ts.map +1 -0
- package/dist/planner.js +96 -0
- package/dist/planner.js.map +1 -0
- package/dist/profile-CTN1Q5SI.d.ts +365 -0
- package/dist/profile-CTN1Q5SI.d.ts.map +1 -0
- package/dist/profile-DR4i6HDp.js +610 -0
- package/dist/profile-DR4i6HDp.js.map +1 -0
- package/dist/render-db-BFQIspDB.d.ts +426 -0
- package/dist/render-db-BFQIspDB.d.ts.map +1 -0
- package/dist/render-db.d.ts +2 -0
- package/dist/render-db.js +1 -0
- package/dist/tiled-draw-list-Dc-5Ledi.js +3990 -0
- package/dist/tiled-draw-list-Dc-5Ledi.js.map +1 -0
- package/dist/types-CHgZBSra.d.ts +105 -0
- package/dist/types-CHgZBSra.d.ts.map +1 -0
- package/dist/upgrade-planner-CNjnMHPJ.d.ts +385 -0
- package/dist/upgrade-planner-CNjnMHPJ.d.ts.map +1 -0
- package/package.json +86 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile-DR4i6HDp.js","names":[],"sources":["../src/types/draw-list.ts","../src/resolve/trains.ts","../src/icon-silhouette.ts","../src/train-chains.ts","../src/profile.ts"],"sourcesContent":["/**\n * The draw-list IR: the fully-resolved, ordered list of drawing commands the\n * planner emits and the canvas backend executes. JSON-serializable; this is\n * the Tier-2 snapshot surface.\n *\n * All geometry is in tile units (see docs/CONTRACTS.md, \"Coordinate system\").\n */\n\nimport type { FrameId, RenderLayerName } from \"./render-db.js\";\n\n/**\n * Full Factorio `RenderLayer` enum (lowest → highest).\n * Source: https://lua-api.factorio.com/latest/types/RenderLayer.html\n * (same data as Factorio.app `doc-html/prototype-api.json`, 2.1.11).\n * Values are the enum indices; order is the contract.\n */\nexport const FACTORIO_RENDER_LAYERS = {\n zero: 0,\n \"background-transitions\": 1,\n \"under-tiles\": 2,\n decals: 3,\n \"above-tiles\": 4,\n \"ground-layer-1\": 5,\n \"ground-layer-2\": 6,\n \"ground-layer-3\": 7,\n \"ground-layer-4\": 8,\n \"ground-layer-5\": 9,\n \"lower-radius-visualization\": 10,\n \"radius-visualization\": 11,\n \"transport-belt-integration\": 12,\n resource: 13,\n \"building-smoke\": 14,\n \"rail-stone-path-lower\": 15,\n \"rail-stone-path\": 16,\n \"rail-tie\": 17,\n decorative: 18,\n \"ground-patch\": 19,\n \"ground-patch-higher\": 20,\n \"ground-patch-higher2\": 21,\n \"rail-chain-signal-metal\": 22,\n \"rail-screw\": 23,\n \"rail-metal\": 24,\n remnants: 25,\n floor: 26,\n \"transport-belt\": 27,\n \"transport-belt-endings\": 28,\n \"floor-mechanics-under-corpse\": 29,\n corpse: 30,\n \"floor-mechanics\": 31,\n item: 32,\n \"transport-belt-reader\": 33,\n \"lower-object\": 34,\n \"transport-belt-circuit-connector\": 35,\n \"lower-object-above-shadow\": 36,\n \"lower-object-overlay\": 37,\n \"object-under\": 38,\n object: 39,\n \"cargo-hatch\": 40,\n \"higher-object-under\": 41,\n \"higher-object-above\": 42,\n \"train-stop-top\": 43,\n \"item-in-inserter-hand\": 44,\n \"above-inserters\": 45,\n wires: 46,\n \"under-elevated\": 47,\n \"elevated-rail-stone-path-lower\": 48,\n \"elevated-rail-stone-path\": 49,\n \"elevated-rail-tie\": 50,\n \"elevated-rail-screw\": 51,\n \"elevated-rail-metal\": 52,\n \"elevated-lower-object\": 53,\n \"elevated-object\": 54,\n \"elevated-higher-object\": 55,\n \"fluid-visualization\": 56,\n \"wires-above\": 57,\n \"entity-info-icon\": 58,\n \"entity-info-icon-above\": 59,\n explosion: 60,\n projectile: 61,\n smoke: 62,\n \"air-object\": 63,\n \"air-entity-info-icon\": 64,\n \"light-effect\": 65,\n \"selection-box\": 66,\n \"higher-selection-box\": 67,\n \"collision-selection-box\": 68,\n arrow: 69,\n cursor: 70,\n} as const;\n\n/**\n * fpsr-only names (not in Factorio's enum) and legacy aliases.\n * Aliases share the official index of the Factorio name they stand for.\n */\nconst FPSR_RENDER_LAYERS = {\n /** ≈ `background-transitions` */\n \"tile-transition\": 1,\n /** ≈ `under-tiles` */\n \"ground-tile\": 2,\n \"water-tile\": 2,\n /** alias of `rail-tie` */\n \"rail-ties\": 17,\n /** Dedicated `draw_as_shadow` pass (no Factorio RenderLayer for this) */\n shadow: 37,\n /** alias of `elevated-rail-tie` */\n \"elevated-rail-ties\": 50,\n /** alias of `entity-info-icon` */\n icons: 58,\n} as const;\n\nexport const RENDER_LAYERS: Record<RenderLayerName, number> = {\n ...FACTORIO_RENDER_LAYERS,\n ...FPSR_RENDER_LAYERS,\n};\n\ninterface DrawCmdBase {\n /** Numeric layer from RENDER_LAYERS. */\n layer: number;\n /** Y-sort key within the layer (entity collision-box bottom; 0 for non-object layers). */\n sortY: number;\n /**\n * X-sort key within the layer (entity position.x for object layers; 0 otherwise).\n * Same-Y neighbors paint west→east so baked hood shadows don’t cover eastern sprites.\n */\n sortX: number;\n /** Owning entity_number (0 for tiles/background), for stable sort + debugging. */\n entity: number;\n /** Intra-entity sub-layer index. */\n sub: number;\n}\n\nexport interface RectCmd extends DrawCmdBase {\n kind: \"rect\";\n x: number;\n y: number;\n w: number;\n h: number;\n color: [number, number, number, number];\n}\n\nexport interface SpriteCmd extends DrawCmdBase {\n kind: \"sprite\";\n frame: FrameId;\n x: number;\n y: number;\n w: number;\n h: number;\n tint?: [number, number, number, number];\n shadow?: boolean;\n flipX?: boolean;\n flipY?: boolean;\n /** Clockwise degrees around the sprite center. */\n rotation?: number;\n /**\n * Continue a small interior band beyond the authored destination. Legacy\n * lower rail-bed sprites have sparse alpha-feathered ends that can otherwise\n * expose terrain where straight and curved pieces meet.\n */\n seamBleed?: {\n top?: true;\n right?: true;\n bottom?: true;\n left?: true;\n };\n /**\n * Optional sub-rect within the untrimmed source sprite (`FrameMeta.sw`×`sh`,\n * pixels). When set, `x`/`y`/`w`/`h` are the destination rect for this slice.\n */\n src?: { x: number; y: number; w: number; h: number };\n /**\n * Optional destination-space clip rect (tile units, same space as x/y/w/h).\n * Backend applies before drawing; used for UG/loader belt underlay halves.\n */\n clip?: { x: number; y: number; w: number; h: number };\n}\n\nexport interface WireCmd extends DrawCmdBase {\n kind: \"wire\";\n /** Wire color / copper. */\n wire: \"copper\" | \"red\" | \"green\";\n x1: number;\n y1: number;\n x2: number;\n y2: number;\n}\n\n/**\n * Rolling-stock coupling overlay: straight neon-green segments between joints\n * plus hollow circles at each bogie joint.\n */\nexport interface TrainChainCmd extends DrawCmdBase {\n kind: \"train-chain\";\n segments: { x1: number; y1: number; x2: number; y2: number }[];\n joints: { x: number; y: number }[];\n}\n\n/** Alt-mode entity-info icon, badge, backing, or directional indicator. */\nexport interface IconCmd extends DrawCmdBase {\n kind: \"icon\";\n frame: FrameId;\n x: number;\n y: number;\n /** Destination size in tiles (square). */\n size: number;\n /** Draw a dark rounded backing behind the icon (recipe badge). */\n backing?: boolean;\n /** Atlas-backed Factorio entity-info background; preferred over synthetic backing. */\n backingFrame?: FrameId;\n /** Draw the entity-info black silhouette even when no backing is present. */\n silhouette?: boolean;\n /**\n * How to composite `backingFrame` / silhouette:\n * - `entity-info` (default): soft dark disc + black silhouette + color icon\n * - `request-pin`: cyan item-request pin at full alpha, color icon only (no silhouette)\n */\n backingStyle?: \"entity-info\" | \"request-pin\";\n /** Clockwise degrees around the icon center. */\n rotation?: number;\n}\n\n/**\n * Blueprint snap-to-grid visualization: dashed green perimeter along the snap cell.\n */\nexport interface SnapGridCmd extends DrawCmdBase {\n kind: \"snap-grid\";\n x: number;\n y: number;\n w: number;\n h: number;\n}\n\n/** Canvas text label in tile space (e.g. deconstruction planner section headers). */\nexport interface TextCmd extends DrawCmdBase {\n kind: \"text\";\n text: string;\n x: number;\n y: number;\n /** Font size in tiles (scaled by pixels-per-tile at execute time). */\n size: number;\n color: [number, number, number, number];\n align?: \"left\" | \"center\" | \"right\";\n baseline?: \"top\" | \"middle\" | \"alphabetic\";\n}\n\nexport type DrawCmd =\n | RectCmd\n | SpriteCmd\n | WireCmd\n | TrainChainCmd\n | IconCmd\n | SnapGridCmd\n | TextCmd;\n\nexport interface DrawListBounds {\n minX: number;\n minY: number;\n maxX: number;\n maxY: number;\n}\n\nexport interface DrawList {\n schema: 1;\n bounds: DrawListBounds;\n /** Already sorted by (layer, sortY, sortX, entity, sub); backend executes in order. */\n commands: DrawCmd[];\n}\n\n/** Comparator implementing the contract sort order. */\nexport function compareDrawCmd(a: DrawCmd, b: DrawCmd): number {\n return (\n a.layer - b.layer ||\n a.sortY - b.sortY ||\n a.sortX - b.sortX ||\n a.entity - b.entity ||\n a.sub - b.sub\n );\n}\n\n/**\n * Canonical serialization for Tier-2 snapshots: stable key order, numbers\n * rounded to 4 decimals, one command per line for reviewable diffs.\n */\nexport function serializeDrawList(list: DrawList): string {\n const roundDeep = (v: unknown): unknown => {\n if (typeof v === \"number\") return Math.round(v * 1e4) / 1e4;\n if (Array.isArray(v)) return v.map(roundDeep);\n if (v && typeof v === \"object\") {\n // Preserve key insertion order for nested objects (e.g. clip); only\n // top-level command keys are sorted for stable diffs.\n const out: Record<string, unknown> = {};\n for (const k of Object.keys(v as Record<string, unknown>)) {\n out[k] = roundDeep((v as Record<string, unknown>)[k]);\n }\n return out;\n }\n return v;\n };\n const canon = (obj: Record<string, unknown>): Record<string, unknown> => {\n const out: Record<string, unknown> = {};\n for (const k of Object.keys(obj).sort()) {\n out[k] = roundDeep(obj[k]);\n }\n return out;\n };\n const lines = list.commands.map((c) =>\n JSON.stringify(canon(c as unknown as Record<string, unknown>)),\n );\n const bounds = canon(list.bounds as unknown as Record<string, unknown>);\n return `{\\n\"schema\": ${list.schema},\\n\"bounds\": ${JSON.stringify(bounds)},\\n\"commands\": [\\n${lines.join(\",\\n\")}\\n]\\n}\\n`;\n}\n","/**\n * Vehicle RotatedAnimation art is already authored in oblique screen angles.\n * Convert the world-space orientation back into that authored ellipse before\n * selecting a pose. This is the inverse axis scaling of rolling-stock\n * projection; vehicles also have no rail/bogie geometry.\n */\nexport function projectVehicleOrientation(orientation: number): number {\n const turn = ((orientation % 1) + 1) % 1;\n let x = Math.sin(turn * Math.PI * 2);\n const y = -Math.cos(turn * Math.PI * 2);\n x *= Math.SQRT1_2;\n let out = Math.atan2(x, -y) / (Math.PI * 2);\n if (out < 0) out += 1;\n return out;\n}\n\n/**\n * Map continuous blueprint `orientation` ∈ [0,1) to a distilled pose index.\n * `round(orientation * N) % N` — orientation 0 → pose 0 (north).\n *\n * With `backEqualsFront` (cargo/fluid wagons): the sheet only authors half a\n * turn; Factorio indexes as `round(o * 2N) % N` so east and west share a pose.\n * @see FBSR FPRotatedSprite.getIndex\n */\nexport function trainOrientationIndex(\n orientation: number,\n poseCount: number,\n backEqualsFront = false,\n): number {\n if (poseCount <= 0) return 0;\n const o = ((orientation % 1) + 1) % 1;\n if (backEqualsFront) {\n return Math.round(o * poseCount * 2) % poseCount;\n }\n return Math.round(o * poseCount) % poseCount;\n}\n\n/**\n * Factorio's camera is a 45° orthographic tilt: circular orientations project\n * onto an ellipse (Y scaled by 1/√2) before picking rotated sprites.\n * @see https://forums.factorio.com/viewtopic.php?t=109695\n */\nexport function projectTrainOrientation(orientation: number): number {\n const turn = ((orientation % 1) + 1) % 1;\n const x = Math.sin(turn * Math.PI * 2);\n let y = -Math.cos(turn * Math.PI * 2);\n y *= Math.SQRT1_2;\n let out = Math.atan2(x, -y) / (Math.PI * 2);\n if (out < 0) out += 1;\n return out;\n}\n\n/**\n * Rolling-stock \"rail shift\": FBSR stores this as height and flattens with\n * `y - height`. Equivalent forum form: `-(0.25 * abs(cos(o*TAU + PI/2)))`.\n * @see https://forums.factorio.com/viewtopic.php?t=109695\n */\nexport function trainRailShiftY(orientation: number): number {\n const o = ((orientation % 1) + 1) % 1;\n const angle = o * Math.PI * 2;\n return -0.25 * Math.abs(Math.cos(angle + Math.PI / 2));\n}\n\n/** Factorio oblique Y/Z scale (1/√2). @see FBSR FPUtils.PROJECTION_CONSTANT */\nconst TRAIN_PROJECTION = Math.SQRT1_2;\n\n/**\n * Artillery-wagon cannon mount offset (tile units, before rail-shift Y).\n * Ports FBSR ArtilleryWagonRendering.calculateCannonPosition for slope=0.\n */\nexport function artilleryCannonShift(\n orientation: number,\n opts: {\n cannonBaseHeight?: number;\n cannonBaseShiftWhenVertical?: number;\n cannonBaseShiftWhenHorizontal?: number;\n /** Distilled pose count; used to snap orientation like FBSR getAlignedOrientation. */\n orientationCount?: number;\n },\n): [number, number] {\n const o = ((orientation % 1) + 1) % 1;\n const n =\n typeof opts.orientationCount === \"number\" && opts.orientationCount > 0\n ? opts.orientationCount\n : 64;\n const projected = projectTrainOrientation(o);\n const aligned = trainOrientationIndex(projected, n) / n;\n const vf = Math.abs(Math.abs(aligned - 0.5) - 0.25) * 4;\n const vert = opts.cannonBaseShiftWhenVertical ?? 0;\n const horiz = opts.cannonBaseShiftWhenHorizontal ?? 0;\n const offsetForward = -(vert * vf + horiz * (1 - vf));\n const offsetHeight = opts.cannonBaseHeight ?? 0;\n // FBSR: rotation = orientation * TAU + PI/2\n const rotation = o * Math.PI * 2 + Math.PI / 2;\n const offsetX = offsetForward * Math.cos(rotation);\n const offsetY = offsetForward * Math.sin(rotation);\n // flatten: (x, y * P - height * P)\n return [offsetX, offsetY * TRAIN_PROJECTION - offsetHeight * TRAIN_PROJECTION];\n}\n\n/** Dual bogie shifts for a rolling-stock entity (tile units relative to center). */\nexport function trainWheelShifts(\n orientation: number,\n jointDistance: number,\n): { shift: [number, number]; orientation: number }[] {\n const o = ((orientation % 1) + 1) % 1;\n const projected = projectTrainOrientation(o);\n const half = jointDistance / 2;\n const angle = o * Math.PI * 2;\n const ox = Math.sin(angle) * half;\n const oy = -Math.cos(angle) * half;\n const vert = trainRailShiftY(o);\n // Couplers are baked into the wheel sprite on one end only. Forward bogie\n // (+joint/2) uses orientation+0.5 so its hook faces outward; rearward bogie\n // keeps body orientation. Matching FBSR's front=orientation leaves both\n // hooks inward and dark bogie backs in the inter-wagon gap.\n const flipped = projectTrainOrientation((o + 0.5) % 1);\n return [\n { shift: [ox, oy + vert], orientation: flipped },\n { shift: [-ox, -oy + vert], orientation: projected },\n ];\n}\n\n/**\n * Map blueprint rail direction (0..15) to a direction8 picture index.\n * Straight / half-diagonal pieces only author 4 of 8 keys; fold 4..7 → 0..3.\n */\nexport function railDirectionIndex(direction: number, foldTo4: boolean): number {\n let i = Math.floor((((direction % 16) + 16) % 16) / 2) % 8;\n if (foldTo4) i = i % 4;\n return i;\n}\n","import type { ImageDataLike, ImageSource } from \"./host.js\";\n\n/** Pixel radius for feMorphology-style alpha dilation on entity-info icons. */\nexport const ENTITY_INFO_SILHOUETTE_RADIUS_PX = 12;\n/** Soft blur applied after dilation to feather the silhouette edge. */\nexport const ENTITY_INFO_SILHOUETTE_BLUR_PX = 16;\n\n/** Total source-pixel padding around icon crops for spread + blur. */\nexport function entityInfoSilhouettePadPx(\n dilateRadius = ENTITY_INFO_SILHOUETTE_RADIUS_PX,\n blurRadius = ENTITY_INFO_SILHOUETTE_BLUR_PX,\n): number {\n return dilateRadius + blurRadius;\n}\n\nexport interface ImageDataContext {\n drawImage(image: ImageSource, dx: number, dy: number): void;\n createImageData?(sw: number, sh: number): ImageDataLike;\n getImageData(sx: number, sy: number, sw: number, sh: number): ImageDataLike;\n putImageData(data: ImageDataLike, dx: number, dy: number): void;\n}\n\n/** Box blur on the alpha channel; output RGB is solid black. */\nexport function blurAlphaBox(\n src: Uint8ClampedArray,\n width: number,\n height: number,\n radius: number,\n): Uint8ClampedArray {\n if (radius <= 0) return src;\n\n const r = Math.floor(radius);\n if (r <= 0) return src;\n const alpha = new Uint8Array(width * height);\n for (let pixel = 0; pixel < alpha.length; pixel++) alpha[pixel] = src[pixel * 4 + 3]!;\n const out = new Uint8ClampedArray(src.length);\n writeBlurredBlackRgba(alpha, out, width, height, r);\n return out;\n}\n\n/** Box dilation on the alpha channel; output RGB is solid black. */\nfunction dilateAlphaChannel(\n src: Uint8ClampedArray,\n width: number,\n height: number,\n radius: number,\n): Uint8Array {\n if (radius <= 0) {\n const out = new Uint8Array(width * height);\n for (let pixel = 0; pixel < out.length; pixel++) out[pixel] = src[pixel * 4 + 3]!;\n return out;\n }\n\n const r = Math.floor(radius);\n if (r <= 0) return dilateAlphaChannel(src, width, height, 0);\n\n const horizontal = new Uint8Array(width * height);\n const deque = new Int32Array(Math.max(width, height));\n\n // Horizontal sliding maximum.\n for (let y = 0; y < height; y++) {\n let head = 0;\n let tail = 0;\n let next = 0;\n const row = y * width;\n for (let x = 0; x < width; x++) {\n const addThrough = Math.min(width - 1, x + r);\n while (next <= addThrough) {\n const alpha = src[(row + next) * 4 + 3]!;\n while (tail > head) {\n const previous = deque[tail - 1]!;\n if (src[(row + previous) * 4 + 3]! > alpha) break;\n tail--;\n }\n deque[tail++] = next++;\n }\n const removeBefore = x - r;\n while (tail > head && deque[head]! < removeBefore) head++;\n horizontal[row + x] = src[(row + deque[head]!) * 4 + 3]!;\n }\n }\n\n // Vertical sliding maximum.\n const out = new Uint8Array(width * height);\n for (let x = 0; x < width; x++) {\n let head = 0;\n let tail = 0;\n let next = 0;\n for (let y = 0; y < height; y++) {\n const addThrough = Math.min(height - 1, y + r);\n while (next <= addThrough) {\n const alpha = horizontal[next * width + x]!;\n while (tail > head) {\n const previous = deque[tail - 1]!;\n if (horizontal[previous * width + x]! > alpha) break;\n tail--;\n }\n deque[tail++] = next++;\n }\n const removeBefore = y - r;\n while (tail > head && deque[head]! < removeBefore) head++;\n out[y * width + x] = horizontal[deque[head]! * width + x]!;\n }\n }\n return out;\n}\n\nfunction writeBlurredBlackRgba(\n alpha: Uint8Array,\n target: Uint8ClampedArray,\n width: number,\n height: number,\n radius: number,\n): void {\n const r = Math.floor(radius);\n if (r <= 0) {\n for (let pixel = 0; pixel < alpha.length; pixel++) {\n const i = pixel * 4;\n target[i] = 0;\n target[i + 1] = 0;\n target[i + 2] = 0;\n target[i + 3] = alpha[pixel]!;\n }\n return;\n }\n\n const stride = width + 1;\n const integral = new Uint32Array((width + 1) * (height + 1));\n for (let y = 0; y < height; y++) {\n let rowSum = 0;\n const srcRow = y * width;\n const integralRow = (y + 1) * stride;\n const previousRow = y * stride;\n for (let x = 0; x < width; x++) {\n rowSum += alpha[srcRow + x]!;\n integral[integralRow + x + 1] = integral[previousRow + x + 1]! + rowSum;\n }\n }\n\n for (let y = 0; y < height; y++) {\n const top = Math.max(0, y - r);\n const bottom = Math.min(height, y + r + 1);\n for (let x = 0; x < width; x++) {\n const left = Math.max(0, x - r);\n const right = Math.min(width, x + r + 1);\n const sum =\n integral[bottom * stride + right]! -\n integral[top * stride + right]! -\n integral[bottom * stride + left]! +\n integral[top * stride + left]!;\n const i = (y * width + x) * 4;\n target[i] = 0;\n target[i + 1] = 0;\n target[i + 2] = 0;\n target[i + 3] = Math.round(sum / ((right - left) * (bottom - top)));\n }\n }\n}\n\n/** Box dilation on the alpha channel; output RGB is solid black. */\nexport function dilateAlphaBox(\n src: Uint8ClampedArray,\n width: number,\n height: number,\n radius: number,\n): Uint8ClampedArray {\n const alpha = dilateAlphaChannel(src, width, height, radius);\n const out = new Uint8ClampedArray(src.length);\n writeBlurredBlackRgba(alpha, out, width, height, 0);\n return out;\n}\n\n/** Fused silhouette operation used by the canvas path to avoid RGBA intermediates. */\nexport function dilateAndBlurAlphaBox(\n src: Uint8ClampedArray,\n width: number,\n height: number,\n dilateRadius: number,\n blurRadius: number,\n): Uint8ClampedArray {\n const alpha = dilateAlphaChannel(src, width, height, dilateRadius);\n const out = new Uint8ClampedArray(src.length);\n writeBlurredBlackRgba(alpha, out, width, height, blurRadius);\n return out;\n}\n\nexport interface SilhouetteCanvasLike {\n width: number;\n height: number;\n getContext(type: \"2d\"): ImageDataContext | null;\n}\n\n/** Bake from an already-cropped icon without reading back a second offscreen canvas. */\nexport function bakeEntityInfoSilhouetteFromImageData(\n iconData: ImageDataLike,\n createCanvas: (width: number, height: number) => SilhouetteCanvasLike,\n dilateRadius = ENTITY_INFO_SILHOUETTE_RADIUS_PX,\n blurRadius = ENTITY_INFO_SILHOUETTE_BLUR_PX,\n): ImageSource | undefined {\n const iconWidth = iconData.width;\n const iconHeight = iconData.height;\n const pad = entityInfoSilhouettePadPx(dilateRadius, blurRadius);\n const width = iconWidth + 2 * pad;\n const height = iconHeight + 2 * pad;\n const canvas = createCanvas(width, height);\n canvas.width = width;\n canvas.height = height;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return undefined;\n\n const imageData = ctx.createImageData?.(width, height) ?? ctx.getImageData(0, 0, width, height);\n for (let y = 0; y < iconHeight; y++) {\n for (let x = 0; x < iconWidth; x++) {\n const source = (y * iconWidth + x) * 4 + 3;\n const target = ((y + pad) * width + x + pad) * 4 + 3;\n imageData.data[target] = iconData.data[source]!;\n }\n }\n const alpha = dilateAlphaChannel(imageData.data, width, height, dilateRadius);\n writeBlurredBlackRgba(alpha, imageData.data, width, height, blurRadius);\n ctx.putImageData(imageData, 0, 0);\n return canvas as unknown as ImageSource;\n}\n\n/**\n * Dilate and blur an isolated icon crop into a padded black silhouette image.\n * Returns undefined when the canvas context cannot read/write ImageData.\n */\nexport function bakeEntityInfoSilhouette(\n iconImage: ImageSource,\n iconWidth: number,\n iconHeight: number,\n createCanvas: (width: number, height: number) => SilhouetteCanvasLike,\n dilateRadius = ENTITY_INFO_SILHOUETTE_RADIUS_PX,\n blurRadius = ENTITY_INFO_SILHOUETTE_BLUR_PX,\n): ImageSource | undefined {\n const pad = entityInfoSilhouettePadPx(dilateRadius, blurRadius);\n const width = iconWidth + 2 * pad;\n const height = iconHeight + 2 * pad;\n const canvas = createCanvas(width, height);\n canvas.width = width;\n canvas.height = height;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return undefined;\n\n ctx.drawImage(iconImage, pad, pad);\n const imageData = ctx.getImageData(0, 0, width, height);\n const alpha = dilateAlphaChannel(imageData.data, width, height, dilateRadius);\n writeBlurredBlackRgba(alpha, imageData.data, width, height, blurRadius);\n ctx.putImageData(imageData, 0, 0);\n return canvas as unknown as ImageSource;\n}\n","/**\n * Rolling-stock coupling chain overlay: green joint circles + straight segments\n * along connected wagons (Factorio attach/coupling visualization).\n */\n\nimport { trainWheelShifts } from \"./resolve.js\";\nimport type {\n Blueprint,\n BlueprintEntity,\n BlueprintRollingStockConnection,\n} from \"./types/blueprint.js\";\nimport type { EntityRenderDef } from \"./types/render-db.js\";\n\n/** Default Factorio RollingStock connection_distance when not distilled. */\nexport const DEFAULT_CONNECTION_DISTANCE = 3;\n/** Default Factorio RollingStock joint_distance when not distilled. */\nexport const DEFAULT_JOINT_DISTANCE = 4;\n/** Hollow joint circle radius in tiles — segments stop at this edge. */\nexport const TRAIN_CHAIN_JOINT_RADIUS = 0.3;\n\n/** Tolerance when inferring couplings from joint proximity (tiles). */\nconst COUPLE_TOLERANCE = 0.25;\n\nfunction round4(n: number): number {\n return Math.round(n * 1e4) / 1e4;\n}\n\nexport interface TrainJointPoints {\n /** Joint in the +orientation direction (±joint/2). */\n front: [number, number];\n /** Joint in the −orientation direction. */\n back: [number, number];\n}\n\nexport interface TrainChainGeometry {\n segments: { x1: number; y1: number; x2: number; y2: number }[];\n joints: { x: number; y: number }[];\n}\n\nfunction jointDistanceOf(def: EntityRenderDef): number {\n const j = def.data?.jointDistance;\n return typeof j === \"number\" && j > 0 ? j : DEFAULT_JOINT_DISTANCE;\n}\n\nfunction connectionDistanceOf(def: EntityRenderDef): number {\n const c = def.data?.connectionDistance;\n return typeof c === \"number\" && c > 0 ? c : DEFAULT_CONNECTION_DISTANCE;\n}\n\n/**\n * World-space front/back joint positions for a rolling-stock entity.\n * Matches bogie placement from `trainWheelShifts` (index 0 = front/+half).\n */\nexport function trainJointWorldPoints(\n entity: BlueprintEntity,\n jointDistance: number,\n): TrainJointPoints {\n const bogies = trainWheelShifts(entity.orientation ?? 0, jointDistance);\n const front = bogies[0]!;\n const back = bogies[1]!;\n return {\n front: [round4(entity.position.x + front.shift[0]), round4(entity.position.y + front.shift[1])],\n back: [round4(entity.position.x + back.shift[0]), round4(entity.position.y + back.shift[1])],\n };\n}\n\nfunction dist(a: [number, number], b: [number, number]): number {\n return Math.hypot(a[0] - b[0], a[1] - b[1]);\n}\n\nfunction jointKey(x: number, y: number): string {\n return `${Math.round(x * 1e4) / 1e4},${Math.round(y * 1e4) / 1e4}`;\n}\n\nfunction addSegment(\n segments: { x1: number; y1: number; x2: number; y2: number }[],\n seen: Set<string>,\n a: [number, number],\n b: [number, number],\n /** Inset each end by this many tiles so the stroke meets circle edges. */\n endInset = 0,\n): void {\n const k1 = `${jointKey(a[0], a[1])}|${jointKey(b[0], b[1])}`;\n const k2 = `${jointKey(b[0], b[1])}|${jointKey(a[0], a[1])}`;\n if (seen.has(k1) || seen.has(k2)) return;\n seen.add(k1);\n\n let x1 = a[0];\n let y1 = a[1];\n let x2 = b[0];\n let y2 = b[1];\n if (endInset > 0) {\n const len = Math.hypot(x2 - x1, y2 - y1);\n if (len <= endInset * 2) return; // too short to draw between circles\n const ux = (x2 - x1) / len;\n const uy = (y2 - y1) / len;\n x1 = round4(x1 + ux * endInset);\n y1 = round4(y1 + uy * endInset);\n x2 = round4(x2 - ux * endInset);\n y2 = round4(y2 - uy * endInset);\n }\n segments.push({ x1, y1, x2, y2 });\n}\n\nfunction addJoint(\n joints: { x: number; y: number }[],\n seen: Set<string>,\n p: [number, number],\n): void {\n const k = jointKey(p[0], p[1]);\n if (seen.has(k)) return;\n seen.add(k);\n joints.push({ x: p[0], y: p[1] });\n}\n\ntype StockSide = \"front\" | \"back\";\n\ninterface CouplingEdge {\n a: number;\n aSide: StockSide;\n b: number;\n bSide: StockSide;\n}\n\nfunction sideOfNeighbor(\n links: Map<number, { front?: number; back?: number }>,\n neighbor: number,\n stock: number,\n fallback: StockSide,\n): StockSide {\n const n = links.get(neighbor);\n if (n?.front === stock) return \"front\";\n if (n?.back === stock) return \"back\";\n return fallback;\n}\n\nfunction parseStockConnections(\n raw: BlueprintRollingStockConnection[] | undefined,\n trainNums: Set<number>,\n): CouplingEdge[] {\n if (!raw?.length) return [];\n const links = new Map<number, { front?: number; back?: number }>();\n for (const entry of raw) {\n if (!entry || typeof entry.stock !== \"number\") continue;\n if (!trainNums.has(entry.stock)) continue;\n const cur = links.get(entry.stock) ?? {};\n if (typeof entry.front === \"number\") cur.front = entry.front;\n if (typeof entry.back === \"number\") cur.back = entry.back;\n links.set(entry.stock, cur);\n }\n\n const edges: CouplingEdge[] = [];\n const seen = new Set<string>();\n for (const [stock, sides] of links) {\n for (const aSide of [\"front\", \"back\"] as const) {\n const neighbor = sides[aSide];\n if (typeof neighbor !== \"number\" || !trainNums.has(neighbor) || neighbor === stock) {\n continue;\n }\n const lo = Math.min(stock, neighbor);\n const hi = Math.max(stock, neighbor);\n const key = `${lo}-${hi}`;\n if (seen.has(key)) continue;\n seen.add(key);\n // Prefer the neighbor's declared side; if missing, assume opposite joint.\n const fallback: StockSide = aSide === \"front\" ? \"back\" : \"front\";\n const bSide = sideOfNeighbor(links, neighbor, stock, fallback);\n edges.push({ a: stock, aSide, b: neighbor, bSide });\n }\n }\n return edges;\n}\n\n/**\n * Infer couplings when `stock_connections` is absent: facing joints within\n * connection_distance ± tolerance, orientations aligned or opposite.\n */\nfunction inferCouplings(\n trains: { entity: BlueprintEntity; def: EntityRenderDef }[],\n jointsByNum: Map<number, TrainJointPoints>,\n): CouplingEdge[] {\n const edges: CouplingEdge[] = [];\n const seen = new Set<string>();\n\n for (let i = 0; i < trains.length; i++) {\n const a = trains[i]!;\n const ja = jointsByNum.get(a.entity.entity_number);\n if (!ja) continue;\n const connA = connectionDistanceOf(a.def);\n\n for (let j = i + 1; j < trains.length; j++) {\n const b = trains[j]!;\n const jb = jointsByNum.get(b.entity.entity_number);\n if (!jb) continue;\n const connB = connectionDistanceOf(b.def);\n const expected = (connA + connB) / 2;\n\n const candidates: { aSide: StockSide; bSide: StockSide; d: number }[] = [\n { aSide: \"front\", bSide: \"front\", d: dist(ja.front, jb.front) },\n { aSide: \"front\", bSide: \"back\", d: dist(ja.front, jb.back) },\n { aSide: \"back\", bSide: \"front\", d: dist(ja.back, jb.front) },\n { aSide: \"back\", bSide: \"back\", d: dist(ja.back, jb.back) },\n ];\n\n let best: (typeof candidates)[number] | undefined;\n for (const c of candidates) {\n if (Math.abs(c.d - expected) > COUPLE_TOLERANCE) continue;\n if (!best || c.d < best.d) best = c;\n }\n if (!best) continue;\n\n const lo = Math.min(a.entity.entity_number, b.entity.entity_number);\n const hi = Math.max(a.entity.entity_number, b.entity.entity_number);\n const key = `${lo}-${hi}`;\n if (seen.has(key)) continue;\n seen.add(key);\n edges.push({\n a: a.entity.entity_number,\n aSide: best.aSide,\n b: b.entity.entity_number,\n bSide: best.bSide,\n });\n }\n }\n return edges;\n}\n\nfunction pointOf(joints: TrainJointPoints, side: StockSide): [number, number] {\n return side === \"front\" ? joints.front : joints.back;\n}\n\n/**\n * Build train-chain overlay geometry for all coupled rolling stock in a blueprint.\n *\n * Draws hollow circles only at coupled joints (free ends omitted), and straight\n * segments between those facing joints. Segment endpoints are inset to the\n * circle perimeter so strokes meet the rings instead of crossing through them.\n *\n * Isolated single wagons with no neighbors are omitted.\n */\nexport function buildTrainChainGeometry(\n bp: Blueprint,\n byNumber: Map<number, { entity: BlueprintEntity; def: EntityRenderDef }>,\n): TrainChainGeometry | null {\n const trains: { entity: BlueprintEntity; def: EntityRenderDef }[] = [];\n for (const entry of byNumber.values()) {\n if (entry.def.kind === \"train\") trains.push(entry);\n }\n if (trains.length === 0) return null;\n\n const trainNums = new Set(trains.map((t) => t.entity.entity_number));\n const jointsByNum = new Map<number, TrainJointPoints>();\n for (const t of trains) {\n jointsByNum.set(\n t.entity.entity_number,\n trainJointWorldPoints(t.entity, jointDistanceOf(t.def)),\n );\n }\n\n let edges = parseStockConnections(bp.stock_connections, trainNums);\n if (edges.length === 0) {\n edges = inferCouplings(trains, jointsByNum);\n }\n if (edges.length === 0) return null;\n\n const segments: { x1: number; y1: number; x2: number; y2: number }[] = [];\n const joints: { x: number; y: number }[] = [];\n const segSeen = new Set<string>();\n const jointSeen = new Set<string>();\n\n // Only joints that participate in a coupling (skip free ends).\n for (const e of edges) {\n const ja = jointsByNum.get(e.a);\n const jb = jointsByNum.get(e.b);\n if (!ja || !jb) continue;\n const aPt = pointOf(ja, e.aSide);\n const bPt = pointOf(jb, e.bSide);\n addJoint(joints, jointSeen, aPt);\n addJoint(joints, jointSeen, bPt);\n addSegment(segments, segSeen, aPt, bPt, TRAIN_CHAIN_JOINT_RADIUS);\n }\n\n if (segments.length === 0 && joints.length === 0) return null;\n return { segments, joints };\n}\n","import type { TileFrame } from \"./frame.js\";\n\n/** High-resolution clock; falls back to Date.now in exotic hosts. */\nexport function nowMs(): number {\n if (typeof performance !== \"undefined\" && typeof performance.now === \"function\") {\n return performance.now();\n }\n return Date.now();\n}\n\n/** Best-effort Performance API mark (ignored when unavailable). */\nexport function perfMark(name: string): void {\n try {\n performance.mark?.(name);\n } catch {\n // ignore\n }\n}\n\n/** Best-effort Performance API measure between two marks. */\nexport function perfMeasure(name: string, startMark: string, endMark: string): void {\n try {\n performance.measure?.(name, startMark, endMark);\n } catch {\n // ignore\n }\n}\n\nexport interface DecodeStats {\n mode: \"compressed\" | \"json\";\n inputChars: number;\n /** Base64-decoded zlib payload length (compressed mode only). */\n compressedBytes?: number;\n /** Inflated UTF-8 byte length (compressed mode only). */\n inflatedBytes?: number;\n /** JSON text character length. */\n jsonChars: number;\n /** inflatedBytes / compressedBytes when both known. */\n compressionRatio?: number;\n timings: {\n totalMs: number;\n base64Ms?: number;\n inflateMs?: number;\n utf8Ms?: number;\n jsonParseMs: number;\n validateMs: number;\n };\n}\n\nexport interface PlanProfile {\n migrateMs: number;\n resolveMs: number;\n tilesMs: number;\n entitiesMs: number;\n overlaysMs: number;\n sortMs: number;\n totalMs: number;\n}\n\nexport type AssetEventKind = \"render-db\" | \"manifest\" | \"atlas\";\n\nexport interface AssetEvent {\n kind: AssetEventKind;\n /** Atlas index when kind === \"atlas\". */\n index?: number;\n tier?: \"1x\" | \"2x\";\n url?: string;\n cached: boolean;\n fetchMs?: number;\n /** Time spent waiting for an available image decoder slot. */\n queueMs?: number;\n decodeMs?: number;\n /** Decoded atlas area, independent of compressed blob size. */\n decodedPixels?: number;\n totalMs: number;\n bytes?: number;\n}\n\nexport interface DrawListStats {\n commandCount: number;\n byKind: Record<string, number>;\n uniqueFrames: number;\n /** layer number → command count */\n layerHistogram: Record<string, number>;\n atlasIndices: number[];\n}\n\nexport interface RenderProfile {\n selectMs: number;\n plan: PlanProfile;\n assets: AssetEvent[];\n /** Wall-clock for the parallel atlas load phase (not sum of per-atlas times). */\n assetsMs: number;\n iconBakeMs: number;\n iconBakeCount: number;\n silhouetteBakeCount: number;\n iconCacheHits: number;\n iconCacheMisses: number;\n silhouetteCacheHits: number;\n silhouetteCacheMisses: number;\n frameMs: number;\n paintMs: number;\n shadow: {\n runs: number;\n tiles: number;\n compositedPixels: number;\n peakScratchPixels: number;\n };\n totalMs: number;\n /** True when any atlas referenced by this render was not already cached. */\n cold: boolean;\n drawList: DrawListStats;\n output: {\n width: number;\n height: number;\n megapixels: number;\n pixelsPerTile: number;\n requestedPixelsPerTile: number;\n capped: boolean;\n assetTier: \"1x\" | \"2x\";\n tileFrame: TileFrame;\n };\n db: {\n entityDefs: number;\n tileDefs: number;\n frameCount: number;\n atlasCount: number;\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
//#region src/types/render-db.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* The render database ("render-db") IR: everything the runtime needs to draw a
|
|
4
|
+
* blueprint, distilled offline by tools/pipeline from Factorio's data.raw dump.
|
|
5
|
+
*
|
|
6
|
+
* Design rules:
|
|
7
|
+
* - The runtime is dumb: all prototype heterogeneity (animation / sprite4way /
|
|
8
|
+
* rotated_sprite / belt_animation_set / connection pictures / graphics_set)
|
|
9
|
+
* is normalized here by the pipeline. The runtime only selects variants.
|
|
10
|
+
* - Outer shapes here are a stable contract. Kind-specific payloads
|
|
11
|
+
* (`EntityRenderDef.data`) may be extended additively; document new kinds in
|
|
12
|
+
* this file when adding them.
|
|
13
|
+
* - Keep it compact: this JSON ships to browsers. Frames are an array (ids are
|
|
14
|
+
* indices); geometry is rounded by the pipeline.
|
|
15
|
+
*/
|
|
16
|
+
/** Index into RenderDb.frames. */
|
|
17
|
+
type FrameId = number;
|
|
18
|
+
interface AtlasMeta {
|
|
19
|
+
/** Content-addressed PNG filename resolved relative to the asset base. */
|
|
20
|
+
file: string;
|
|
21
|
+
width: number;
|
|
22
|
+
height: number;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* One packed sprite frame. Trimmed: (x,y,w,h) is the opaque sub-rect inside the
|
|
26
|
+
* atlas; (ox,oy) is its offset within the untrimmed source sprite of size (sw,sh).
|
|
27
|
+
*/
|
|
28
|
+
interface FrameMeta {
|
|
29
|
+
/** Atlas index. */
|
|
30
|
+
a: number;
|
|
31
|
+
x: number;
|
|
32
|
+
y: number;
|
|
33
|
+
w: number;
|
|
34
|
+
h: number;
|
|
35
|
+
ox: number;
|
|
36
|
+
oy: number;
|
|
37
|
+
sw: number;
|
|
38
|
+
sh: number;
|
|
39
|
+
/** Packed dimensions when this atlas tier stores a downsampled frame. */
|
|
40
|
+
pw?: number;
|
|
41
|
+
ph?: number;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* A drawable sprite variant: a frame plus placement.
|
|
45
|
+
* On-map untrimmed size in tiles = (sw * scale / 32, sh * scale / 32), centered
|
|
46
|
+
* on the entity position, then offset by `shift` (tile units, +x east, +y south).
|
|
47
|
+
*/
|
|
48
|
+
interface SpriteVariant {
|
|
49
|
+
frame: FrameId;
|
|
50
|
+
/** Prototype scale (typically 0.5). */
|
|
51
|
+
scale: number;
|
|
52
|
+
/** Tile-unit shift of the sprite center relative to the entity position. */
|
|
53
|
+
shift: [number, number];
|
|
54
|
+
/** RGBA 0-1. */
|
|
55
|
+
tint?: [number, number, number, number];
|
|
56
|
+
drawAsShadow?: boolean;
|
|
57
|
+
flipX?: boolean;
|
|
58
|
+
flipY?: boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Optional extra Y scale (1 = none). Used to foreshorten inserter hands
|
|
61
|
+
* (FBE `squishY` → scaleY = 1/squishY).
|
|
62
|
+
*/
|
|
63
|
+
scaleY?: number;
|
|
64
|
+
/**
|
|
65
|
+
* Clockwise rotation in degrees around the sprite center (canvas convention).
|
|
66
|
+
* Used for inserter hands (north-facing source art rotated per direction).
|
|
67
|
+
*/
|
|
68
|
+
rotation?: number;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Factorio `RenderLayer` names (full enum from
|
|
72
|
+
* https://lua-api.factorio.com/latest/types/RenderLayer.html ).
|
|
73
|
+
*/
|
|
74
|
+
type FactorioRenderLayerName = "zero" | "background-transitions" | "under-tiles" | "decals" | "above-tiles" | "ground-layer-1" | "ground-layer-2" | "ground-layer-3" | "ground-layer-4" | "ground-layer-5" | "lower-radius-visualization" | "radius-visualization" | "transport-belt-integration" | "resource" | "building-smoke" | "rail-stone-path-lower" | "rail-stone-path" | "rail-tie" | "decorative" | "ground-patch" | "ground-patch-higher" | "ground-patch-higher2" | "rail-chain-signal-metal" | "rail-screw" | "rail-metal" | "remnants" | "floor" | "transport-belt" | "transport-belt-endings" | "floor-mechanics-under-corpse" | "corpse" | "floor-mechanics" | "item" | "transport-belt-reader" | "lower-object" | "transport-belt-circuit-connector" | "lower-object-above-shadow" | "lower-object-overlay" | "object-under" | "object" | "cargo-hatch" | "higher-object-under" | "higher-object-above" | "train-stop-top" | "item-in-inserter-hand" | "above-inserters" | "wires" | "under-elevated" | "elevated-rail-stone-path-lower" | "elevated-rail-stone-path" | "elevated-rail-tie" | "elevated-rail-screw" | "elevated-rail-metal" | "elevated-lower-object" | "elevated-object" | "elevated-higher-object" | "fluid-visualization" | "wires-above" | "entity-info-icon" | "entity-info-icon-above" | "explosion" | "projectile" | "smoke" | "air-object" | "air-entity-info-icon" | "light-effect" | "selection-box" | "higher-selection-box" | "collision-selection-box" | "arrow" | "cursor";
|
|
75
|
+
/**
|
|
76
|
+
* fpsr-only layer names / legacy aliases (not in Factorio's enum, or renamed).
|
|
77
|
+
*/
|
|
78
|
+
type FpsrRenderLayerName = "ground-tile" | "tile-transition" | "water-tile" | "rail-ties" | "shadow" | "elevated-rail-ties" | "icons";
|
|
79
|
+
/** Layer names used in the render-db / planner. */
|
|
80
|
+
type RenderLayerName = FactorioRenderLayerName | FpsrRenderLayerName;
|
|
81
|
+
/**
|
|
82
|
+
* One visual layer of an entity. `variants[variantKey][index]` where:
|
|
83
|
+
* - variantKey: chosen by the connectivity resolver ("default" when trivial;
|
|
84
|
+
* e.g. connection-bitmask strings for pipes/walls, "left"/"right" for
|
|
85
|
+
* splitter halves, etc. — resolver and pipeline must agree per `kind`).
|
|
86
|
+
* - index: direction/frame index. Its meaning is given by `indexing`.
|
|
87
|
+
*/
|
|
88
|
+
interface LayerGroup {
|
|
89
|
+
layer: RenderLayerName;
|
|
90
|
+
/**
|
|
91
|
+
* How `index` is derived from the entity:
|
|
92
|
+
* - "single": always index 0
|
|
93
|
+
* - "direction4": [N,E,S,W] -> 0..3 (direction/4)
|
|
94
|
+
* - "direction8": 0..7 (direction/2)
|
|
95
|
+
* - "direction16": raw blueprint direction 0..15
|
|
96
|
+
* - "resolver": index computed by the connectivity resolver (belts' 20-way
|
|
97
|
+
* curve table, rail pieces, etc.)
|
|
98
|
+
*/
|
|
99
|
+
indexing: "single" | "direction4" | "direction8" | "direction16" | "resolver";
|
|
100
|
+
variants: Record<string, (SpriteVariant | null)[]>;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Resolver strategy discriminator. Adding a kind = adding a resolver strategy
|
|
104
|
+
* and (usually) a `data` payload; keep both documented here.
|
|
105
|
+
* - "simple": no neighbor logic; direction-indexed layers only.
|
|
106
|
+
* - "belt": transport belt; resolver picks index 0..19 from the belt curve table.
|
|
107
|
+
* - "underground-belt", "loader": uses entity `type` (input/output) + direction.
|
|
108
|
+
* - "splitter": two-tile entity, layers for whole body per direction.
|
|
109
|
+
* - "pipe": variantKey = connection bitmask "NESW" (e.g. "1010").
|
|
110
|
+
* - "heat-pipe", "wall": same bitmask scheme as pipe.
|
|
111
|
+
* - "gate": wall gate, horizontal/vertical variants.
|
|
112
|
+
* - "inserter": platform + arm parts (static pose).
|
|
113
|
+
* - "assembler": body (+ optional fluid-box pipe pictures when recipe needs them).
|
|
114
|
+
* - "rail": rail piece kinds; direction8-indexed layered sprites (stone/ties/metal).
|
|
115
|
+
* - "rail-signal": signal with direction16 indexing plus elevated variants.
|
|
116
|
+
* - "train": rolling stock; index from continuous `orientation` via
|
|
117
|
+
* `data.orientationCount` (pose count in variants.default).
|
|
118
|
+
* - "storage-tank", "mining-drill", "offshore-pump", "pump", "power-pole",
|
|
119
|
+
* "roboport", "beacon", "reactor", "turret", ...: treated as "simple" unless
|
|
120
|
+
* a dedicated kind is introduced.
|
|
121
|
+
*
|
|
122
|
+
* Kind-specific `data` (additive):
|
|
123
|
+
* - `wireAnchors`: Record<dirIndex, { copper?: [x,y], red?: [x,y], green?: [x,y] }>
|
|
124
|
+
* tile-space offsets from entity center for wire endpoints (poles + circuit).
|
|
125
|
+
* - `wireAnchorsOutput`: same shape as `wireAnchors`, used for combinator output
|
|
126
|
+
* connectors (ids 3/4) and power-switch right copper (id 6). When absent, the
|
|
127
|
+
* planner falls back to `wireAnchors`.
|
|
128
|
+
* - `wireConnectorGraphics`: circuit-connector decorations (CCM) drawn when the
|
|
129
|
+
* entity appears in `bp.wires`. Shape:
|
|
130
|
+
* `{ indexing: "direction4"|"direction16"|"single", layers: {
|
|
131
|
+
* connector_shadow?, connector_main?, wire_pins_shadow?, wire_pins?,
|
|
132
|
+
* led_blue_off?: (SpriteVariant|null)[] } }`
|
|
133
|
+
* - `combinatorGraphics`: direction4 operator/comparator display sprites keyed
|
|
134
|
+
* by blueprint operation (for example `*`, `≥`, `max`, or `min`).
|
|
135
|
+
* - `beltConnectorGraphics`: transport-belt yellow cage + LEDs when wired. Shape:
|
|
136
|
+
* `{ indexing: "belt-topology", layers: {
|
|
137
|
+
* frame_shadow?, frame_main?: (SpriteVariant|null)[][], // [variation][behavior state]
|
|
138
|
+
* frame_back_patch?: (SpriteVariant|null)[], // 3 variations
|
|
139
|
+
* wire_horizontal?, wire_vertical?: (SpriteVariant|null)[][], // décor by mode
|
|
140
|
+
* led_red?, led_green?, led_blue?: (SpriteVariant|null)[] } }`
|
|
141
|
+
* Variation order: X, H, V, SE, SW, NE, NW (Factorio TransportBeltPrototype).
|
|
142
|
+
* Behavior-state order: none, output, input, both (bitmask 0..3).
|
|
143
|
+
* Output (`circuit_enabled`) → wire_horizontal + led_red/green;
|
|
144
|
+
* input (`circuit_read_hand_contents`) → wire_vertical + led_blue.
|
|
145
|
+
* - `beltReaderGraphics`: whole-belt-read side skirts (`belt_animation_set.belt_reader`).
|
|
146
|
+
* Shape: `{ indexing: "belt-reader-band-nesw", layers: { layer, variants: SpriteVariant[4][4] }[] }`
|
|
147
|
+
* (bands × N/E/S/W edge frames; see Factorio BeltReaderLayer validation).
|
|
148
|
+
* variants indexed east=0, west=1, north=2, south=3.
|
|
149
|
+
* - `orientationCount`: number of distilled train or vehicle poses. Each uses
|
|
150
|
+
* its corresponding camera/original-sheet projection; only trains add
|
|
151
|
+
* rail/bogie geometry.
|
|
152
|
+
* - `backEqualsFront`: cargo/fluid wagon bodies only author half a turn; index
|
|
153
|
+
* folds with `round(o * 2N) % N` (wheels stay full-circle).
|
|
154
|
+
* - `wheelsGroupIndex` / `jointDistance` / `connectionDistance`: rolling-stock
|
|
155
|
+
* bogies are drawn twice at ±jointDistance/2 along circular orientation
|
|
156
|
+
* (Factorio RollingStock). The forward bogie uses orientation+0.5 so coupler
|
|
157
|
+
* hooks face outward. `connectionDistance` is the gap between facing joints
|
|
158
|
+
* of two coupled wagons (default 3); used by the train-chain overlay.
|
|
159
|
+
* - `cannonGroupIndices` / `cannonBaseHeight` /
|
|
160
|
+
* `cannonBaseShiftWhenVertical` / `cannonBaseShiftWhenHorizontal`:
|
|
161
|
+
* artillery-wagon cannon barrel+base layers and mount offset (FBSR).
|
|
162
|
+
* - `colorMaskGroupIndex` / `colorMaskGroupIndices`: graphics group(s) that are
|
|
163
|
+
* runtime-tint masks (apply `entity.color` / `defaultColor` at plan time).
|
|
164
|
+
* - `defaultColor`: prototype `color` for rolling stock when the blueprint omits it.
|
|
165
|
+
* - `fluidConnections` / `heatConnections` / `tileSize` / … (see M2).
|
|
166
|
+
* - `heatConnectionPatchGroupIndices`: graphics groups whose `connected` /
|
|
167
|
+
* `disconnected` variations correspond by index to `heatConnections` ports.
|
|
168
|
+
* - `pipeCovers`: `{ covers: SpriteVariant[4], shadows?: SpriteVariant[4] }` —
|
|
169
|
+
* fluid-box pipe covers (N/E/S/W). Planner draws a cover on each *unconnected*
|
|
170
|
+
* port's adjacent tile (Factorio: pictures when no FluidBox is connected).
|
|
171
|
+
* - `cargoBayConnections` / `cargoBayConnectionsPlatform`: Factorio 2.1
|
|
172
|
+
* `CargoBayConnections` (tileset + bridges) for hub / cargo-bay / landing-pad.
|
|
173
|
+
* Body graphics may also expose a `platform` variant key beside `default`.
|
|
174
|
+
* - `placeholder`: true when the pipeline baked a gray footprint sprite because
|
|
175
|
+
* no usable graphics were resolved; `placeholderReason` explains why.
|
|
176
|
+
*/
|
|
177
|
+
type EntityKind = "simple" | "belt" | "underground-belt" | "loader" | "splitter" | "pipe" | "heat-pipe" | "wall" | "gate" | "inserter" | "assembler" | "rail" | "rail-signal" | "vehicle" | "train";
|
|
178
|
+
type DirectionalConnectionMap = Record<string, [number, number][]>;
|
|
179
|
+
interface WireAnchorSet {
|
|
180
|
+
copper?: [number, number];
|
|
181
|
+
red?: [number, number];
|
|
182
|
+
green?: [number, number];
|
|
183
|
+
}
|
|
184
|
+
type WireAnchorMap = Record<string, WireAnchorSet>;
|
|
185
|
+
interface PipeCoverGraphics {
|
|
186
|
+
covers: (SpriteVariant | null)[];
|
|
187
|
+
shadows?: (SpriteVariant | null)[];
|
|
188
|
+
}
|
|
189
|
+
type WireConnectorLayerName = "connector_shadow" | "connector_main" | "wire_pins_shadow" | "wire_pins" | "led_blue_off";
|
|
190
|
+
interface WireConnectorGraphics {
|
|
191
|
+
indexing: "direction4" | "direction16" | "single";
|
|
192
|
+
layers: Partial<Record<WireConnectorLayerName, (SpriteVariant | null)[]>>;
|
|
193
|
+
}
|
|
194
|
+
interface CombinatorGraphics {
|
|
195
|
+
symbols: Record<string, (SpriteVariant | null)[]>;
|
|
196
|
+
}
|
|
197
|
+
interface BeltConnectorGraphics {
|
|
198
|
+
indexing: "belt-topology";
|
|
199
|
+
layers: {
|
|
200
|
+
frame_shadow?: (SpriteVariant | null)[][];
|
|
201
|
+
frame_main?: (SpriteVariant | null)[][];
|
|
202
|
+
frame_back_patch?: (SpriteVariant | null)[];
|
|
203
|
+
wire_horizontal?: (SpriteVariant | null)[][];
|
|
204
|
+
wire_vertical?: (SpriteVariant | null)[][];
|
|
205
|
+
led_red?: (SpriteVariant | null)[];
|
|
206
|
+
led_green?: (SpriteVariant | null)[];
|
|
207
|
+
led_blue?: (SpriteVariant | null)[];
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
interface BeltReaderGraphics {
|
|
211
|
+
indexing: "belt-reader-band-nesw";
|
|
212
|
+
layers: {
|
|
213
|
+
layer: RenderLayerName;
|
|
214
|
+
variants: (SpriteVariant | null)[][];
|
|
215
|
+
}[];
|
|
216
|
+
}
|
|
217
|
+
/** One layered sprite in a cargo-bay connection cell (tileset or bridge). */
|
|
218
|
+
interface CargoBayConnectionLayer {
|
|
219
|
+
layer: RenderLayerName;
|
|
220
|
+
variant: SpriteVariant;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* One variation of a cargo-bay connection cell: parallel layered sprites
|
|
224
|
+
* (typically lower-object-above-shadow → object).
|
|
225
|
+
*/
|
|
226
|
+
interface CargoBayConnectionCell {
|
|
227
|
+
layers: CargoBayConnectionLayer[];
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Factorio 2.1 `CargoBayConnections` distilled for runtime planning.
|
|
231
|
+
* `tileset` is 0-based; dump `tileset_mapping` values are 1-based (0 = skip).
|
|
232
|
+
* Shape: tileset[index][group 0|1][variation] = layered cell.
|
|
233
|
+
*/
|
|
234
|
+
interface CargoBayConnections {
|
|
235
|
+
tileset: CargoBayConnectionCell[][][];
|
|
236
|
+
/** Bitmask (0–255) → tileset index (1-based) or list of indices. */
|
|
237
|
+
tilesetMapping: Record<string, number | number[]>;
|
|
238
|
+
bridges: {
|
|
239
|
+
horizontalNarrow: CargoBayConnectionCell[];
|
|
240
|
+
verticalNarrow: CargoBayConnectionCell[];
|
|
241
|
+
horizontalWide: CargoBayConnectionCell[];
|
|
242
|
+
verticalWide: CargoBayConnectionCell[];
|
|
243
|
+
crossing: CargoBayConnectionCell[];
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
/** Stable, additive payload shared by the offline pipeline and runtime planner. */
|
|
247
|
+
interface EntityRenderData {
|
|
248
|
+
tileSize?: [number, number];
|
|
249
|
+
fluidConnections?: DirectionalConnectionMap;
|
|
250
|
+
heatConnections?: DirectionalConnectionMap;
|
|
251
|
+
heatConnectionPatchGroupIndices?: number[];
|
|
252
|
+
fluidBoxesRequireFluidRecipe?: boolean;
|
|
253
|
+
pipeCovers?: PipeCoverGraphics;
|
|
254
|
+
wireAnchors?: WireAnchorMap;
|
|
255
|
+
wireAnchorsOutput?: WireAnchorMap;
|
|
256
|
+
wireConnectorGraphics?: WireConnectorGraphics;
|
|
257
|
+
combinatorGraphics?: CombinatorGraphics;
|
|
258
|
+
beltConnectorGraphics?: BeltConnectorGraphics;
|
|
259
|
+
beltReaderGraphics?: BeltReaderGraphics;
|
|
260
|
+
/**
|
|
261
|
+
* Grounded (planet / landing-pad) cargo-bay connection graphics.
|
|
262
|
+
* When `cargoBayConnectionsPlatform` is set, resolve/plan picks platform
|
|
263
|
+
* art on space platforms (hub or space-platform tiles present).
|
|
264
|
+
*/
|
|
265
|
+
cargoBayConnections?: CargoBayConnections;
|
|
266
|
+
/** Platform-surface connection graphics (`platform_graphics_set.connections`). */
|
|
267
|
+
cargoBayConnectionsPlatform?: CargoBayConnections;
|
|
268
|
+
orientationCount?: number;
|
|
269
|
+
backEqualsFront?: boolean;
|
|
270
|
+
wheelsGroupIndex?: number;
|
|
271
|
+
jointDistance?: number;
|
|
272
|
+
connectionDistance?: number;
|
|
273
|
+
cannonGroupIndices?: number[];
|
|
274
|
+
cannonBaseHeight?: number;
|
|
275
|
+
cannonBaseShiftWhenVertical?: number;
|
|
276
|
+
cannonBaseShiftWhenHorizontal?: number;
|
|
277
|
+
colorMaskGroupIndex?: number;
|
|
278
|
+
colorMaskGroupIndices?: number[];
|
|
279
|
+
defaultColor?: [number, number, number, number];
|
|
280
|
+
placeholder?: boolean;
|
|
281
|
+
placeholderReason?: string;
|
|
282
|
+
}
|
|
283
|
+
interface EntityRenderDef {
|
|
284
|
+
kind: EntityKind;
|
|
285
|
+
/** Factorio prototype type (e.g. "transport-belt"), for resolver heuristics. */
|
|
286
|
+
protoType: string;
|
|
287
|
+
/** Collision box [[x1,y1],[x2,y2]] relative to entity center, tile units. */
|
|
288
|
+
collisionBox: [[number, number], [number, number]];
|
|
289
|
+
/** Selection box, used for bounds and the viewer's hover UI. */
|
|
290
|
+
selectionBox: [[number, number], [number, number]];
|
|
291
|
+
/** Ordered visual layers (sub-order within the entity). */
|
|
292
|
+
graphics: LayerGroup[];
|
|
293
|
+
/**
|
|
294
|
+
* Kind-specific extra data (belt speed group, fluid box connection positions,
|
|
295
|
+
* wire connection anchor points per direction, splitter half offsets, ...).
|
|
296
|
+
* Additive extension point — resolver code owns its shape per kind.
|
|
297
|
+
*/
|
|
298
|
+
data?: EntityRenderData;
|
|
299
|
+
/** Icon frame for alt-mode fallbacks and the viewer. */
|
|
300
|
+
icon?: FrameId;
|
|
301
|
+
/** Factorio prototype placement rules for entity-info (alt-mode) overlays. */
|
|
302
|
+
iconDrawSpecification?: {
|
|
303
|
+
shift: [number, number];
|
|
304
|
+
scale: number;
|
|
305
|
+
scaleForMany: number;
|
|
306
|
+
renderLayer: "entity-info-icon" | "entity-info-icon-above" | "air-entity-info-icon";
|
|
307
|
+
};
|
|
308
|
+
/**
|
|
309
|
+
* Scale for the selection-box quality badge (`EntityPrototype.quality_indicator_scale`).
|
|
310
|
+
* Default when omitted: clamp(min(tile_w, tile_h) / 3, 0.5, 1) from the collision box.
|
|
311
|
+
*/
|
|
312
|
+
qualityIndicatorScale?: number;
|
|
313
|
+
}
|
|
314
|
+
/** Factorio `material_background` atlas: repeating patchW×patchH variants. */
|
|
315
|
+
interface TileMaterialAtlas {
|
|
316
|
+
/** Cropped material_background spritesheet (all variant patches). */
|
|
317
|
+
sheet: FrameId;
|
|
318
|
+
/** `material_background.count`. */
|
|
319
|
+
count: number;
|
|
320
|
+
/** `material_texture_width_in_tiles` (default 8). */
|
|
321
|
+
patchW: number;
|
|
322
|
+
/** `material_texture_height_in_tiles` (default 8). */
|
|
323
|
+
patchH: number;
|
|
324
|
+
/** Source pixels per map tile (`32 / material_background.scale`). */
|
|
325
|
+
tilePx: number;
|
|
326
|
+
/** `material_background.line_length`; 0 = single horizontal row. */
|
|
327
|
+
lineLength?: number;
|
|
328
|
+
/** `material_background.x` / `y` offset into the source file (pixels). */
|
|
329
|
+
sheetX?: number;
|
|
330
|
+
sheetY?: number;
|
|
331
|
+
}
|
|
332
|
+
interface TileRenderDef {
|
|
333
|
+
layer: RenderLayerName;
|
|
334
|
+
/** Item that places this tile (`place_as_tile.result`); used for item-icon fallback. */
|
|
335
|
+
item?: string;
|
|
336
|
+
/**
|
|
337
|
+
* 1×1 map-texture swatch for planner/UI (`icons["tile/{name}"]`). Prefer this
|
|
338
|
+
* over the placing-item icon when showing tile filters.
|
|
339
|
+
*/
|
|
340
|
+
icon?: FrameId;
|
|
341
|
+
/**
|
|
342
|
+
* Plain color fallback (RGBA 0-1). When atlases are unavailable, tiles draw as
|
|
343
|
+
* solid rects using this color.
|
|
344
|
+
*/
|
|
345
|
+
color: [number, number, number, number];
|
|
346
|
+
/**
|
|
347
|
+
* Legacy 1×1 hashed variants from `variants.main[0]` (e.g. stone-path size-1
|
|
348
|
+
* sheet). Multi-size 2×2 / 4×4 packing is not implemented yet.
|
|
349
|
+
*/
|
|
350
|
+
frames?: FrameId[];
|
|
351
|
+
/** Factorio `material_background` mode (concrete, landfill, hazard, …). */
|
|
352
|
+
material?: TileMaterialAtlas;
|
|
353
|
+
}
|
|
354
|
+
type TerrainBackgroundName = string;
|
|
355
|
+
/** One authored group of same-size, edge-compatible terrain patches. */
|
|
356
|
+
interface TerrainPatchSet {
|
|
357
|
+
/** Width and height of every patch in map tiles. */
|
|
358
|
+
patchSize: number;
|
|
359
|
+
/** Authored patch variants, selected deterministically from world coordinates. */
|
|
360
|
+
frames: FrameId[];
|
|
361
|
+
/** Optional positive selection weights parallel to `frames`. */
|
|
362
|
+
weights?: number[];
|
|
363
|
+
/** Factorio's authored chance for using this patch size. */
|
|
364
|
+
probability?: number;
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* A full-plane terrain background assembled from world-aligned square patches.
|
|
368
|
+
* `frames` contains authored variants of the same `patchSize`; `weights`, when
|
|
369
|
+
* present, mirrors Factorio's weighted tile-main-picture selection. `patches`
|
|
370
|
+
* carries smaller complete patch groups used to break up large-scale repetition
|
|
371
|
+
* without cropping or transforming any authored art.
|
|
372
|
+
*/
|
|
373
|
+
interface TerrainPatchBackground {
|
|
374
|
+
/** Stable per-terrain salt so different surfaces do not repeat the same patch layout. */
|
|
375
|
+
seed?: number;
|
|
376
|
+
/** Width and height of every patch in map tiles. */
|
|
377
|
+
patchSize: number;
|
|
378
|
+
/** Authored patch variants, selected deterministically from world coordinates. */
|
|
379
|
+
frames: FrameId[];
|
|
380
|
+
/** Optional positive selection weights parallel to `frames`. */
|
|
381
|
+
weights?: number[];
|
|
382
|
+
/** Factorio's authored chance for using the largest patch size. */
|
|
383
|
+
probability?: number;
|
|
384
|
+
/** Smaller authored patch groups, normally 1×1 and 2×2. */
|
|
385
|
+
patches?: TerrainPatchSet[];
|
|
386
|
+
/** Solid RGBA fallback painted before patches and used by older asset bundles. */
|
|
387
|
+
color: [number, number, number, number];
|
|
388
|
+
}
|
|
389
|
+
type TerrainBackgrounds = Partial<Record<string, TerrainPatchBackground>>;
|
|
390
|
+
/**
|
|
391
|
+
* Decorative space-platform backdrop art (starmap planet spheres).
|
|
392
|
+
* Drawn in screen space after the procedural starfield when `showSpace` is on.
|
|
393
|
+
*/
|
|
394
|
+
interface SpaceBackground {
|
|
395
|
+
/** Default planet frame (Nauvis when available). */
|
|
396
|
+
planetFrame: FrameId;
|
|
397
|
+
/** Optional named planet frames keyed by Factorio planet prototype name. */
|
|
398
|
+
planets?: Record<string, FrameId>;
|
|
399
|
+
}
|
|
400
|
+
interface RenderDb {
|
|
401
|
+
/** Schema version of this IR; bump on breaking change. */
|
|
402
|
+
schema: 2;
|
|
403
|
+
gameVersion: string;
|
|
404
|
+
mods: string[];
|
|
405
|
+
/** Physical atlas density; frame geometry remains in canonical 2× pixels. */
|
|
406
|
+
assetDensity?: 1 | 2;
|
|
407
|
+
atlases: AtlasMeta[];
|
|
408
|
+
frames: FrameMeta[];
|
|
409
|
+
entities: Record<string, EntityRenderDef>;
|
|
410
|
+
tiles: Record<string, TileRenderDef>;
|
|
411
|
+
/** Optional natural terrain art used by full-canvas preview backgrounds. */
|
|
412
|
+
terrainBackgrounds?: TerrainBackgrounds;
|
|
413
|
+
/** Optional space-platform backdrop (planet peeking from the corner). */
|
|
414
|
+
spaceBackground?: SpaceBackground;
|
|
415
|
+
/** Icon frames by "type/name" (e.g. "item/iron-plate", "utility/unsupported-entity"). */
|
|
416
|
+
icons: Record<string, FrameId>;
|
|
417
|
+
/**
|
|
418
|
+
* Optional on-map tile size for utility (and similar) icons whose Factorio Sprite
|
|
419
|
+
* carries an explicit `scale`. Missing keys fall back to caller defaults.
|
|
420
|
+
* Example: utility-sprites.indication_arrow has scale 0.5.
|
|
421
|
+
*/
|
|
422
|
+
iconScales?: Record<string, number>;
|
|
423
|
+
}
|
|
424
|
+
//#endregion
|
|
425
|
+
export { WireConnectorGraphics as A, TerrainBackgrounds as C, TileRenderDef as D, TileMaterialAtlas as E, WireAnchorMap as O, TerrainBackgroundName as S, TerrainPatchSet as T, PipeCoverGraphics as _, CargoBayConnectionLayer as a, SpaceBackground as b, DirectionalConnectionMap as c, EntityRenderDef as d, FactorioRenderLayerName as f, LayerGroup as g, FrameMeta as h, CargoBayConnectionCell as i, WireConnectorLayerName as j, WireAnchorSet as k, EntityKind as l, FrameId as m, BeltConnectorGraphics as n, CargoBayConnections as o, FpsrRenderLayerName as p, BeltReaderGraphics as r, CombinatorGraphics as s, AtlasMeta as t, EntityRenderData as u, RenderDb as v, TerrainPatchBackground as w, SpriteVariant as x, RenderLayerName as y };
|
|
426
|
+
//# sourceMappingURL=render-db-BFQIspDB.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render-db-BFQIspDB.d.ts","names":[],"sources":["../src/types/render-db.ts"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { A as WireConnectorGraphics, C as TerrainBackgrounds, D as TileRenderDef, E as TileMaterialAtlas, O as WireAnchorMap, S as TerrainBackgroundName, T as TerrainPatchSet, _ as PipeCoverGraphics, a as CargoBayConnectionLayer, b as SpaceBackground, c as DirectionalConnectionMap, d as EntityRenderDef, f as FactorioRenderLayerName, g as LayerGroup, h as FrameMeta, i as CargoBayConnectionCell, j as WireConnectorLayerName, k as WireAnchorSet, l as EntityKind, m as FrameId, n as BeltConnectorGraphics, o as CargoBayConnections, p as FpsrRenderLayerName, r as BeltReaderGraphics, s as CombinatorGraphics, t as AtlasMeta, u as EntityRenderData, v as RenderDb, w as TerrainPatchBackground, x as SpriteVariant, y as RenderLayerName } from "./render-db-BFQIspDB.js";
|
|
2
|
+
export type { AtlasMeta, BeltConnectorGraphics, BeltReaderGraphics, CargoBayConnectionCell, CargoBayConnectionLayer, CargoBayConnections, CombinatorGraphics, DirectionalConnectionMap, EntityKind, EntityRenderData, EntityRenderDef, FactorioRenderLayerName, FpsrRenderLayerName, FrameId, FrameMeta, LayerGroup, PipeCoverGraphics, RenderDb, RenderLayerName, SpaceBackground, SpriteVariant, TerrainBackgroundName, TerrainBackgrounds, TerrainPatchBackground, TerrainPatchSet, TileMaterialAtlas, TileRenderDef, WireAnchorMap, WireAnchorSet, WireConnectorGraphics, WireConnectorLayerName };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|