@empty-sekai/sekai-custom-profile-sdk 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/LICENSE +235 -0
  2. package/LICENSE-EXCEPTION +32 -0
  3. package/NOTICE +11 -0
  4. package/README.en.md +456 -0
  5. package/README.md +458 -0
  6. package/dist/allium_renderer_wasm.js +14 -0
  7. package/dist/allium_renderer_wasm.wasm +0 -0
  8. package/dist/authoring.d.ts +14 -0
  9. package/dist/authoring.js +34 -0
  10. package/dist/cache/glyphPersistentCache.d.ts +145 -0
  11. package/dist/cache/glyphPersistentCache.js +346 -0
  12. package/dist/cache/indexedDbGlyphRecordStore.d.ts +26 -0
  13. package/dist/cache/indexedDbGlyphRecordStore.js +214 -0
  14. package/dist/cache/sessionImageResourceCache.d.ts +39 -0
  15. package/dist/cache/sessionImageResourceCache.js +156 -0
  16. package/dist/emscripten.d.ts +11 -0
  17. package/dist/emscripten.js +1 -0
  18. package/dist/fontProvider.d.ts +36 -0
  19. package/dist/fontProvider.js +84 -0
  20. package/dist/fontSdfAtlas.d.ts +92 -0
  21. package/dist/fontSdfAtlas.js +296 -0
  22. package/dist/gpu/browserSemanticResources.d.ts +64 -0
  23. package/dist/gpu/browserSemanticResources.js +232 -0
  24. package/dist/gpu/generalTextRenderPlacement.d.ts +8 -0
  25. package/dist/gpu/generalTextRenderPlacement.js +131 -0
  26. package/dist/gpu/previewTransformTextureLayout.d.ts +1 -0
  27. package/dist/gpu/previewTransformTextureLayout.js +12 -0
  28. package/dist/gpu/semanticCommandGeometry.d.ts +21 -0
  29. package/dist/gpu/semanticCommandGeometry.js +226 -0
  30. package/dist/gpu/semanticCommandPlanner.d.ts +131 -0
  31. package/dist/gpu/semanticCommandPlanner.js +203 -0
  32. package/dist/gpu/semanticTextGlyphBridge.d.ts +3 -0
  33. package/dist/gpu/semanticTextGlyphBridge.js +22 -0
  34. package/dist/gpu/semanticWebglSceneRenderer.d.ts +70 -0
  35. package/dist/gpu/semanticWebglSceneRenderer.js +211 -0
  36. package/dist/gpu/slotRanges.d.ts +5 -0
  37. package/dist/gpu/slotRanges.js +14 -0
  38. package/dist/gpu/webglSdfAtlasTexture.d.ts +17 -0
  39. package/dist/gpu/webglSdfAtlasTexture.js +72 -0
  40. package/dist/gpu/webglSdfGlyphPipeline.d.ts +26 -0
  41. package/dist/gpu/webglSdfGlyphPipeline.js +267 -0
  42. package/dist/gpu/webglSemanticCommandExecutor.d.ts +79 -0
  43. package/dist/gpu/webglSemanticCommandExecutor.js +686 -0
  44. package/dist/index.d.ts +18 -0
  45. package/dist/index.js +8 -0
  46. package/dist/interaction/numericTextRegions.d.ts +46 -0
  47. package/dist/interaction/numericTextRegions.js +44 -0
  48. package/dist/localizationProvider.d.ts +33 -0
  49. package/dist/localizationProvider.js +74 -0
  50. package/dist/originPrebuiltSdfAtlasPackage.d.ts +39 -0
  51. package/dist/originPrebuiltSdfAtlasPackage.js +316 -0
  52. package/dist/prebuiltSdfAtlas.d.ts +36 -0
  53. package/dist/prebuiltSdfAtlas.js +261 -0
  54. package/dist/protocol.d.ts +456 -0
  55. package/dist/protocol.js +1 -0
  56. package/dist/renderer.d.ts +218 -0
  57. package/dist/renderer.js +641 -0
  58. package/dist/resourceProvider.d.ts +24 -0
  59. package/dist/resourceProvider.js +41 -0
  60. package/dist/telemetry/rendererTelemetry.d.ts +158 -0
  61. package/dist/telemetry/rendererTelemetry.js +232 -0
  62. package/dist/third-party/freetype/FTL.txt +169 -0
  63. package/dist/types/atlas.d.ts +90 -0
  64. package/dist/types/atlas.js +1 -0
  65. package/dist/types/authoring.d.ts +81 -0
  66. package/dist/types/authoring.js +1 -0
  67. package/dist/types/core.d.ts +128 -0
  68. package/dist/types/core.js +1 -0
  69. package/dist/types/freeType.d.ts +54 -0
  70. package/dist/types/freeType.js +13 -0
  71. package/dist/types/glyph.d.ts +33 -0
  72. package/dist/types/glyph.js +1 -0
  73. package/dist/types/layout.d.ts +25 -0
  74. package/dist/types/layout.js +1 -0
  75. package/dist/types/text.d.ts +28 -0
  76. package/dist/types/text.js +1 -0
  77. package/dist/worker/glyphWorkScheduler.d.ts +29 -0
  78. package/dist/worker/glyphWorkScheduler.js +107 -0
  79. package/dist/worker-client.d.ts +114 -0
  80. package/dist/worker-client.js +413 -0
  81. package/dist/worker.d.ts +1 -0
  82. package/dist/worker.js +708 -0
  83. package/package.json +46 -0
  84. package/src/atlas.rs +652 -0
  85. package/src/authoring.ts +42 -0
  86. package/src/authoring_runtime.rs +303 -0
  87. package/src/cache/glyphPersistentCache.ts +463 -0
  88. package/src/cache/indexedDbGlyphRecordStore.ts +238 -0
  89. package/src/cache/sessionImageResourceCache.ts +186 -0
  90. package/src/edt.rs +97 -0
  91. package/src/emscripten.ts +17 -0
  92. package/src/fontProvider.ts +127 -0
  93. package/src/fontSdfAtlas.ts +431 -0
  94. package/src/geometry.rs +149 -0
  95. package/src/glyph_plan.rs +226 -0
  96. package/src/gpu/browserSemanticResources.ts +294 -0
  97. package/src/gpu/generalTextRenderPlacement.ts +175 -0
  98. package/src/gpu/previewTransformTextureLayout.ts +12 -0
  99. package/src/gpu/semanticCommandGeometry.ts +260 -0
  100. package/src/gpu/semanticCommandPlanner.ts +280 -0
  101. package/src/gpu/semanticTextGlyphBridge.ts +35 -0
  102. package/src/gpu/semanticWebglSceneRenderer.ts +261 -0
  103. package/src/gpu/slotRanges.ts +15 -0
  104. package/src/gpu/webglSdfAtlasTexture.ts +69 -0
  105. package/src/gpu/webglSdfGlyphPipeline.ts +299 -0
  106. package/src/gpu/webglSemanticCommandExecutor.ts +739 -0
  107. package/src/index.ts +84 -0
  108. package/src/interaction/numericTextRegions.ts +80 -0
  109. package/src/layout.rs +1882 -0
  110. package/src/lib.rs +1443 -0
  111. package/src/localizationProvider.ts +113 -0
  112. package/src/main.rs +55 -0
  113. package/src/masterdata_runtime.rs +496 -0
  114. package/src/originPrebuiltSdfAtlasPackage.ts +403 -0
  115. package/src/prebuiltSdfAtlas.ts +306 -0
  116. package/src/protocol.ts +150 -0
  117. package/src/renderer.ts +792 -0
  118. package/src/resourceProvider.ts +69 -0
  119. package/src/scene.rs +665 -0
  120. package/src/telemetry/rendererTelemetry.ts +358 -0
  121. package/src/types/atlas.ts +80 -0
  122. package/src/types/authoring.ts +85 -0
  123. package/src/types/core.ts +110 -0
  124. package/src/types/freeType.ts +63 -0
  125. package/src/types/glyph.ts +33 -0
  126. package/src/types/layout.ts +28 -0
  127. package/src/types/text.ts +28 -0
  128. package/src/worker/glyphWorkScheduler.ts +130 -0
  129. package/src/worker-client.ts +484 -0
  130. package/src/worker.ts +895 -0
@@ -0,0 +1,131 @@
1
+ const TEXT_SCALE = 2;
2
+ /**
3
+ * Applies General's measured draw anchors after the Rust TMP layout has
4
+ * finished. The returned instances retain the exact layout metrics and glyph
5
+ * relationships; only final local/device draw coordinates are translated.
6
+ */
7
+ export function placeGeneralTextInstances(instances, operations) {
8
+ const operationByCommand = new Map(operations
9
+ .filter((operation) => operation.command.payload.kind === "text")
10
+ .map((operation) => [operation.command.id, operation]));
11
+ const translationByCommand = new Map();
12
+ return instances.map((instance) => {
13
+ const operation = operationByCommand.get(instance.layerId);
14
+ if (!operation)
15
+ return cloneInstance(instance);
16
+ const placement = renderPlacement(operation.command.render_placement);
17
+ if (!placement)
18
+ return cloneInstance(instance);
19
+ let translation = translationByCommand.get(instance.layerId);
20
+ if (!translation) {
21
+ translation = placementTranslation(instance, operation, placement);
22
+ translationByCommand.set(instance.layerId, translation);
23
+ }
24
+ return translateInstance(instance, translation);
25
+ });
26
+ }
27
+ function placementTranslation(instance, operation, placement) {
28
+ const alignment = numericAlignment(operation.command.payload.alignment);
29
+ const autoAnchor = alignment === 2
30
+ ? 0
31
+ : alignment === 4
32
+ ? instance.layoutMetrics.boxW / 2
33
+ : -instance.layoutMetrics.boxW / 2;
34
+ const localX = placement.anchor_x - autoAnchor;
35
+ const localY = placement.baseline == null
36
+ ? 0
37
+ : placement.baseline - instance.layoutMetrics.anchorBase;
38
+ const commandMatrix = matrix(operation.command.matrix);
39
+ const combined = multiply(operation.baseMatrix, commandMatrix);
40
+ const scaledX = localX * TEXT_SCALE;
41
+ const scaledY = localY * TEXT_SCALE;
42
+ return {
43
+ localX,
44
+ localY,
45
+ deviceX: combined[0] * scaledX + combined[2] * scaledY,
46
+ deviceY: combined[1] * scaledX + combined[3] * scaledY,
47
+ };
48
+ }
49
+ function translateInstance(instance, value) {
50
+ const localDeviceX = value.localX * TEXT_SCALE;
51
+ const localDeviceY = value.localY * TEXT_SCALE;
52
+ const output = cloneInstance(instance);
53
+ output.charOp[1] += value.localX;
54
+ output.charOp[2] += value.localY;
55
+ output.charPosition[1] += localDeviceX;
56
+ output.charPosition[2] -= localDeviceY;
57
+ output.charQuad[1] = shiftPoints(output.charQuad[1], localDeviceX, -localDeviceY);
58
+ output.quad = output.quad.map(([x, y, u, v, spread]) => [
59
+ x + value.deviceX,
60
+ y + value.deviceY,
61
+ u,
62
+ v,
63
+ spread,
64
+ ]);
65
+ output.deviceCharPosition[1] += value.deviceX;
66
+ output.deviceCharPosition[2] += value.deviceY;
67
+ output.deviceCharQuad[1] = shiftPoints(output.deviceCharQuad[1], value.deviceX, value.deviceY);
68
+ output.deviceGlyphQuad[1] = shiftPoints(output.deviceGlyphQuad[1], value.deviceX, value.deviceY);
69
+ return output;
70
+ }
71
+ function cloneInstance(instance) {
72
+ return {
73
+ ...instance,
74
+ quad: instance.quad.map((value) => [...value]),
75
+ charPosition: [...instance.charPosition],
76
+ charOp: [...instance.charOp],
77
+ charQuad: [instance.charQuad[0], instance.charQuad[1].map((value) => [...value])],
78
+ deviceCharPosition: [...instance.deviceCharPosition],
79
+ deviceCharQuad: [
80
+ instance.deviceCharQuad[0],
81
+ instance.deviceCharQuad[1].map((value) => [...value]),
82
+ ],
83
+ deviceGlyphQuad: [
84
+ instance.deviceGlyphQuad[0],
85
+ instance.deviceGlyphQuad[1].map((value) => [...value]),
86
+ ],
87
+ layoutMetrics: {
88
+ ...instance.layoutMetrics,
89
+ lineWidths: [...instance.layoutMetrics.lineWidths],
90
+ rectWidths: [...instance.layoutMetrics.rectWidths],
91
+ lineOffsets: [...instance.layoutMetrics.lineOffsets],
92
+ },
93
+ fill: [...instance.fill],
94
+ outline: [...instance.outline],
95
+ };
96
+ }
97
+ function shiftPoints(points, dx, dy) {
98
+ return points.map(([x, y]) => [x + dx, y + dy]);
99
+ }
100
+ function renderPlacement(value) {
101
+ if (!value || typeof value !== "object" || Array.isArray(value))
102
+ return null;
103
+ const record = value;
104
+ if (!Number.isFinite(record.anchor_x))
105
+ return null;
106
+ if (record.baseline != null && !Number.isFinite(record.baseline))
107
+ return null;
108
+ return {
109
+ anchor_x: Number(record.anchor_x),
110
+ baseline: record.baseline == null ? null : Number(record.baseline),
111
+ };
112
+ }
113
+ function numericAlignment(value) {
114
+ return Number.isInteger(value) ? Number(value) & 0x07 : 1;
115
+ }
116
+ function matrix(value) {
117
+ if (!Array.isArray(value) || value.length !== 6 || value.some((part) => !Number.isFinite(part))) {
118
+ return [1, 0, 0, 1, 0, 0];
119
+ }
120
+ return value.map(Number);
121
+ }
122
+ function multiply(parent, child) {
123
+ return [
124
+ parent[0] * child[0] + parent[2] * child[1],
125
+ parent[1] * child[0] + parent[3] * child[1],
126
+ parent[0] * child[2] + parent[2] * child[3],
127
+ parent[1] * child[2] + parent[3] * child[3],
128
+ parent[0] * child[4] + parent[2] * child[5] + parent[4],
129
+ parent[1] * child[4] + parent[3] * child[5] + parent[5],
130
+ ];
131
+ }
@@ -0,0 +1 @@
1
+ export declare function packPreviewTransformsForTexture(transforms: Float32Array, width: number): Float32Array;
@@ -0,0 +1,12 @@
1
+ export function packPreviewTransformsForTexture(transforms, width) {
2
+ if (!Number.isInteger(width) || width < 1 || transforms.length !== width * 8) {
3
+ throw new Error(`invalid preview transform buffer ${transforms.length} for width ${width}`);
4
+ }
5
+ const packed = new Float32Array(transforms.length);
6
+ for (let slot = 0; slot < width; slot += 1) {
7
+ const source = slot * 8;
8
+ packed.set(transforms.subarray(source, source + 4), slot * 4);
9
+ packed.set(transforms.subarray(source + 4, source + 8), (width + slot) * 4);
10
+ }
11
+ return packed;
12
+ }
@@ -0,0 +1,21 @@
1
+ import type { SemanticDrawOperation, SemanticResourceKey } from "./semanticCommandPlanner.js";
2
+ export type SemanticDrawBatchKind = "shape" | "image" | "mask" | "text" | "composite";
3
+ export type SemanticBlendMode = "src_over" | "src_in" | "dst_in" | "multiply" | "screen" | "add";
4
+ export type SemanticCompositeOperation = "marker" | "begin_isolation" | "end_isolation";
5
+ export type SemanticDrawBatch = {
6
+ kind: SemanticDrawBatchKind;
7
+ commandIds: string[];
8
+ operations: SemanticDrawOperation[];
9
+ vertices: Float32Array;
10
+ layerSlots: Uint32Array;
11
+ commandSlots: Uint32Array;
12
+ resource: SemanticResourceKey | null;
13
+ maskResource: SemanticResourceKey | null;
14
+ blendMode: SemanticBlendMode;
15
+ compositeOperation: SemanticCompositeOperation | null;
16
+ };
17
+ export declare const SEMANTIC_FLOATS_PER_VERTEX = 28;
18
+ export declare function semanticTextBatchKey(commandIds: readonly string[]): string;
19
+ /** Compile immutable geometry only. Render mask and dynamic translation remain
20
+ * in the dense layer-state textures, so toggles/ticks never rebuild vertices. */
21
+ export declare function compileSemanticDrawBatches(operations: SemanticDrawOperation[]): SemanticDrawBatch[];
@@ -0,0 +1,226 @@
1
+ export const SEMANTIC_FLOATS_PER_VERTEX = 28;
2
+ export function semanticTextBatchKey(commandIds) {
3
+ return `semantic-text-batch\0${commandIds.join("\0")}`;
4
+ }
5
+ const UNIT_TRIANGLES = [
6
+ [0, 0], [1, 0], [1, 1],
7
+ [0, 0], [1, 1], [0, 1],
8
+ ];
9
+ /** Compile immutable geometry only. Render mask and dynamic translation remain
10
+ * in the dense layer-state textures, so toggles/ticks never rebuild vertices. */
11
+ export function compileSemanticDrawBatches(operations) {
12
+ const groups = [];
13
+ for (const operation of operations) {
14
+ const descriptor = batchDescriptor(operation);
15
+ const previous = groups.at(-1);
16
+ if (previous?.key === descriptor.key)
17
+ previous.operations.push(operation);
18
+ else
19
+ groups.push({ ...descriptor, operations: [operation] });
20
+ }
21
+ return groups.map((group) => compileGroup(group.kind, group.resource, group.maskResource, group.blendMode, group.compositeOperation, group.operations));
22
+ }
23
+ function batchDescriptor(operation) {
24
+ const payload = operation.command.payload;
25
+ const blendMode = commandBlendMode(operation.command.blend_mode);
26
+ if (payload.kind === "image") {
27
+ const resource = requireResource(payload.resource, operation.command.id);
28
+ const maskResource = optionalResource(payload.alpha_mask);
29
+ const maskKey = maskResource ? `\0${maskResource.namespace}\0${maskResource.key}` : "";
30
+ return { key: `image\0${blendMode}\0${resource.namespace}\0${resource.key}${maskKey}`, kind: "image", resource, maskResource, blendMode, compositeOperation: null };
31
+ }
32
+ if (payload.kind === "shape") {
33
+ const maskResource = assetMaskResource(payload.primitive);
34
+ if (maskResource)
35
+ return { key: `mask\0${blendMode}\0${maskResource.namespace}\0${maskResource.key}`, kind: "mask", resource: maskResource, maskResource: null, blendMode, compositeOperation: null };
36
+ return { key: `shape\0${blendMode}`, kind: "shape", resource: null, maskResource: null, blendMode, compositeOperation: null };
37
+ }
38
+ if (payload.kind === "text")
39
+ return { key: `text\0${blendMode}`, kind: "text", resource: null, maskResource: null, blendMode, compositeOperation: null };
40
+ if (payload.kind === "composite") {
41
+ const compositeOperation = requireCompositeOperation(payload.operation, operation.command.id);
42
+ return { key: `composite\0${operation.command.id}`, kind: "composite", resource: null, maskResource: null, blendMode, compositeOperation };
43
+ }
44
+ throw new Error(`unsupported semantic command payload ${payload.kind}`);
45
+ }
46
+ function compileGroup(kind, resource, maskResource, blendMode, compositeOperation, operations) {
47
+ if (kind === "text" || kind === "composite") {
48
+ return { kind, resource, maskResource, blendMode, compositeOperation, operations: [...operations], commandIds: operations.map((op) => op.command.id), vertices: new Float32Array(), layerSlots: new Uint32Array(), commandSlots: new Uint32Array() };
49
+ }
50
+ const vertices = new Float32Array(operations.length * 6 * SEMANTIC_FLOATS_PER_VERTEX);
51
+ const layerSlots = new Uint32Array(operations.length * 6);
52
+ const commandSlots = new Uint32Array(operations.length * 6);
53
+ let vertexOffset = 0;
54
+ for (const operation of operations) {
55
+ const bounds = requireRect(operation.command.bounds, operation.command.id);
56
+ const commandMatrix = requireMatrix(operation.command.matrix, operation.command.id);
57
+ const clip = commandClip(operation.command.clip, operation.baseMatrix, operation.command.id);
58
+ const payload = operation.command.payload;
59
+ const uv = payload.kind === "image" ? optionalRect(payload.uv) : { x: 0, y: 0, width: 1, height: 1 };
60
+ const fill = payload.kind === "image" ? optionalColor(payload.tint, [1, 1, 1, 1]) : optionalColor(payload.fill, [1, 1, 1, 1]);
61
+ const stroke = payload.kind === "shape" ? optionalColor(payload.stroke, [0, 0, 0, 0]) : [0, 0, 0, 0];
62
+ const [primitive, radiusX, radiusY] = payload.kind === "shape"
63
+ ? shapeParams(payload.primitive, bounds)
64
+ : payload.kind === "image" ? imageClipParams(payload.clip, bounds) : [0, 0, 0];
65
+ const strokeWidth = payload.kind === "shape" && typeof payload.stroke_width === "number"
66
+ ? payload.stroke_width
67
+ : 0;
68
+ for (const [unitX, unitY] of UNIT_TRIANGLES) {
69
+ const vertexFill = payload.kind === "shape" ? gradientColor(payload.gradient, unitX, unitY, fill) : fill;
70
+ const localX = bounds.x + bounds.width * unitX;
71
+ const localY = bounds.y + bounds.height * unitY;
72
+ const commandPoint = transformPoint(commandMatrix, localX, localY);
73
+ const [x, y] = transformPoint(operation.baseMatrix, commandPoint[0], commandPoint[1]);
74
+ const base = vertexOffset * SEMANTIC_FLOATS_PER_VERTEX;
75
+ vertices.set([
76
+ x, y,
77
+ uv.x + uv.width * unitX, uv.y + uv.height * unitY,
78
+ unitX, unitY,
79
+ ...vertexFill,
80
+ ...stroke,
81
+ primitive, radiusX, radiusY, strokeWidth,
82
+ ...clip[0], ...clip[1], ...clip[2], ...clip[3],
83
+ bounds.width, bounds.height,
84
+ ], base);
85
+ layerSlots[vertexOffset] = operation.layerSlot;
86
+ commandSlots[vertexOffset] = operation.commandSlot;
87
+ vertexOffset += 1;
88
+ }
89
+ }
90
+ return {
91
+ kind,
92
+ resource,
93
+ maskResource,
94
+ blendMode,
95
+ compositeOperation,
96
+ operations: [...operations],
97
+ commandIds: operations.map((operation) => operation.command.id),
98
+ vertices,
99
+ layerSlots,
100
+ commandSlots,
101
+ };
102
+ }
103
+ function commandBlendMode(value) {
104
+ if (value == null)
105
+ return "src_over";
106
+ if (value === "src_over" || value === "src_in" || value === "dst_in" || value === "multiply" || value === "screen" || value === "add")
107
+ return value;
108
+ throw new Error(`unsupported semantic blend mode ${String(value)}`);
109
+ }
110
+ function requireCompositeOperation(value, commandId) {
111
+ if (value == null || value === "marker")
112
+ return "marker";
113
+ if (value === "begin_isolation" || value === "end_isolation")
114
+ return value;
115
+ throw new Error(`unsupported composite operation ${commandId}: ${String(value)}`);
116
+ }
117
+ function commandClip(value, baseMatrix, commandId) {
118
+ if (value == null)
119
+ return [[-1e9, -1e9], [1e9, -1e9], [1e9, 1e9], [-1e9, 1e9]];
120
+ if (!Array.isArray(value) || value.length !== 4 || value.some((point) => !Array.isArray(point) || point.length !== 2 || point.some((entry) => typeof entry !== "number" || !Number.isFinite(entry)))) {
121
+ throw new Error(`invalid command clip ${commandId}`);
122
+ }
123
+ return value.map((point) => transformPoint(baseMatrix, point[0], point[1]));
124
+ }
125
+ function gradientColor(value, x, y, fallback) {
126
+ if (!value || typeof value !== "object" || Array.isArray(value))
127
+ return fallback;
128
+ const gradient = value;
129
+ const start = optionalPoint(gradient.start, [0, 0.5]);
130
+ const end = optionalPoint(gradient.end, [1, 0.5]);
131
+ const startColor = optionalColor(gradient.start_color, fallback);
132
+ const endColor = optionalColor(gradient.end_color, fallback);
133
+ const dx = end[0] - start[0];
134
+ const dy = end[1] - start[1];
135
+ const lengthSq = dx * dx + dy * dy;
136
+ const t = lengthSq > 1e-9 ? Math.max(0, Math.min(1, ((x - start[0]) * dx + (y - start[1]) * dy) / lengthSq)) : 0;
137
+ return startColor.map((component, index) => component + (endColor[index] - component) * t);
138
+ }
139
+ function optionalPoint(value, fallback) {
140
+ return Array.isArray(value) && value.length === 2 && value.every((entry) => typeof entry === "number" && Number.isFinite(entry))
141
+ ? [value[0], value[1]]
142
+ : fallback;
143
+ }
144
+ function transformPoint(matrix, x, y) {
145
+ return [matrix[0] * x + matrix[2] * y + matrix[4], matrix[1] * x + matrix[3] * y + matrix[5]];
146
+ }
147
+ function requireMatrix(value, commandId) {
148
+ if (!Array.isArray(value) || value.length !== 6 || value.some((entry) => typeof entry !== "number" || !Number.isFinite(entry))) {
149
+ throw new Error(`invalid command matrix ${commandId}`);
150
+ }
151
+ return value;
152
+ }
153
+ function requireRect(value, commandId) {
154
+ const rect = optionalRect(value);
155
+ if (![rect.x, rect.y, rect.width, rect.height].every(Number.isFinite) || rect.width < 0 || rect.height < 0) {
156
+ throw new Error(`invalid command bounds ${commandId}`);
157
+ }
158
+ return rect;
159
+ }
160
+ function optionalRect(value) {
161
+ if (!value || typeof value !== "object" || Array.isArray(value))
162
+ return { x: 0, y: 0, width: 1, height: 1 };
163
+ const rect = value;
164
+ return {
165
+ x: Number(rect.x ?? 0),
166
+ y: Number(rect.y ?? 0),
167
+ width: Number(rect.width ?? 1),
168
+ height: Number(rect.height ?? 1),
169
+ };
170
+ }
171
+ function optionalColor(value, fallback) {
172
+ return Array.isArray(value) && value.length === 4 && value.every((entry) => typeof entry === "number" && Number.isFinite(entry))
173
+ ? value
174
+ : fallback;
175
+ }
176
+ function shapeParams(value, _bounds) {
177
+ if (value === "ellipse")
178
+ return [2, 0, 0];
179
+ if (value === "rect")
180
+ return [0, 0, 0];
181
+ if (!value || typeof value !== "object" || Array.isArray(value))
182
+ return [0, 0, 0];
183
+ const rounded = value.rounded_rect;
184
+ if (rounded && typeof rounded === "object" && !Array.isArray(rounded)) {
185
+ const radius = rounded.radius;
186
+ if (Array.isArray(radius) && radius.length === 2) {
187
+ return [1, Number(radius[0]), Number(radius[1])];
188
+ }
189
+ }
190
+ return [0, 0, 0];
191
+ }
192
+ function imageClipParams(value, _bounds) {
193
+ if (value === "ellipse")
194
+ return [2, 0, 0];
195
+ if (!value || typeof value !== "object" || Array.isArray(value))
196
+ return [0, 0, 0];
197
+ const rounded = value.rounded_rect;
198
+ if (rounded && typeof rounded === "object" && !Array.isArray(rounded)) {
199
+ const radius = rounded.radius;
200
+ if (Array.isArray(radius) && radius.length === 2) {
201
+ return [1, Number(radius[0]), Number(radius[1])];
202
+ }
203
+ }
204
+ return [0, 0, 0];
205
+ }
206
+ function assetMaskResource(value) {
207
+ if (!value || typeof value !== "object" || Array.isArray(value))
208
+ return null;
209
+ const mask = value.asset_mask;
210
+ if (!mask || typeof mask !== "object" || Array.isArray(mask))
211
+ return null;
212
+ return requireResource(mask.resource, "asset-mask");
213
+ }
214
+ function requireResource(value, commandId) {
215
+ if (!value || typeof value !== "object" || Array.isArray(value))
216
+ throw new Error(`missing command resource ${commandId}`);
217
+ const resource = value;
218
+ if (typeof resource.namespace !== "string" || typeof resource.key !== "string")
219
+ throw new Error(`invalid command resource ${commandId}`);
220
+ return { namespace: resource.namespace, key: resource.key };
221
+ }
222
+ function optionalResource(value) {
223
+ if (value == null)
224
+ return null;
225
+ return requireResource(value, "image-alpha-mask");
226
+ }
@@ -0,0 +1,131 @@
1
+ export type SemanticResourceKey = {
2
+ namespace: string;
3
+ key: string;
4
+ };
5
+ export type SemanticCommand = {
6
+ id: string;
7
+ layer_id: string;
8
+ role: string;
9
+ payload: Record<string, unknown> & {
10
+ kind: string;
11
+ };
12
+ [key: string]: unknown;
13
+ };
14
+ export type SemanticLayerPatch = {
15
+ layer_id: string;
16
+ render_mask: boolean | null;
17
+ transform: {
18
+ dx: number;
19
+ dy: number;
20
+ } | null;
21
+ };
22
+ export type SemanticCommandStatePatch = {
23
+ slot: number;
24
+ render_mask: boolean | null;
25
+ transform: {
26
+ dx: number;
27
+ dy: number;
28
+ } | null;
29
+ };
30
+ export type SemanticCommandPlanInput = {
31
+ layerTableRevision: number;
32
+ layerTable: Array<{
33
+ layer_id: string;
34
+ parent_id: string | null;
35
+ slot: number;
36
+ subtree_start: number;
37
+ subtree_end: number;
38
+ }>;
39
+ layerSources: Array<{
40
+ layer_id: string;
41
+ matrix: [number, number, number, number, number, number];
42
+ }>;
43
+ layerCommands: Array<{
44
+ layer_id: string;
45
+ render_mask: boolean;
46
+ transform: {
47
+ dx: number;
48
+ dy: number;
49
+ };
50
+ command_start: number;
51
+ command_count: number;
52
+ }>;
53
+ semanticCommands: SemanticCommand[];
54
+ commandStates?: Array<{
55
+ command_id: string;
56
+ slot: number;
57
+ render_mask: boolean;
58
+ transform: {
59
+ dx: number;
60
+ dy: number;
61
+ };
62
+ }>;
63
+ };
64
+ export type SemanticDrawOperation = {
65
+ command: SemanticCommand;
66
+ layerId: string;
67
+ layerSlot: number;
68
+ baseMatrix: [number, number, number, number, number, number];
69
+ visible: boolean;
70
+ transform: {
71
+ dx: number;
72
+ dy: number;
73
+ };
74
+ commandSlot: number;
75
+ commandVisible: boolean;
76
+ commandTransform: {
77
+ dx: number;
78
+ dy: number;
79
+ };
80
+ };
81
+ /**
82
+ * Immutable authored-layer command ownership plus mutable render state.
83
+ * Visibility and dynamic deltas update only the dense state table; command
84
+ * order and resource identity remain stable for the plan lifetime.
85
+ */
86
+ export declare class SemanticCommandPlan {
87
+ readonly layerTableRevision: number;
88
+ readonly commandRevision = 1;
89
+ readonly resourceRevision = 1;
90
+ private readonly commands;
91
+ private readonly operationLayers;
92
+ private readonly stateByLayer;
93
+ private readonly resources;
94
+ private readonly commandVisible;
95
+ private readonly commandTransforms;
96
+ constructor(input: SemanticCommandPlanInput);
97
+ operations(): SemanticDrawOperation[];
98
+ resourceRequests(): SemanticResourceKey[];
99
+ applyLayerPatches(patches: SemanticLayerPatch[]): void;
100
+ applyCommandPatches(patches: SemanticCommandStatePatch[]): void;
101
+ }
102
+ export declare function semanticCommandPlanFromCoreSnapshot(snapshot: {
103
+ revisions: {
104
+ layer_table: number;
105
+ } & Record<string, number>;
106
+ layer_table: SemanticCommandPlanInput["layerTable"];
107
+ layer_sources: Array<{
108
+ id: string;
109
+ matrix: [number, number, number, number, number, number];
110
+ }>;
111
+ commands: SemanticCommandPlanInput["layerCommands"];
112
+ semantic_commands: SemanticCommand[];
113
+ command_states: NonNullable<SemanticCommandPlanInput["commandStates"]>;
114
+ }): SemanticCommandPlan;
115
+ export type CoreSceneDumpInput = {
116
+ revisions: {
117
+ layer_table: number;
118
+ } & Record<string, number>;
119
+ layer_table: SemanticCommandPlanInput["layerTable"];
120
+ layers: Array<{
121
+ layer_id: string;
122
+ matrix: [number, number, number, number, number, number];
123
+ render_mask: boolean;
124
+ commands: SemanticCommand[];
125
+ }>;
126
+ command_states: NonNullable<SemanticCommandPlanInput["commandStates"]>;
127
+ };
128
+ /** Adapts the native/server debug dump to the exact same immutable browser
129
+ * command plan used by a live WASM Scene. This is intentionally strict so a
130
+ * dump cannot silently reorder authored layers or command ownership. */
131
+ export declare function semanticCommandPlanFromCoreDump(dump: CoreSceneDumpInput): SemanticCommandPlan;