sketchmark 2.1.1 → 2.1.2
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/package.json +1 -1
- package/dist/src/builders/index.d.ts +0 -64
- package/dist/src/builders/index.js +0 -212
- package/dist/src/compounds.d.ts +0 -13
- package/dist/src/compounds.js +0 -118
- package/dist/src/deck.d.ts +0 -4
- package/dist/src/deck.js +0 -91
- package/dist/src/export/index.d.ts +0 -8
- package/dist/src/export/index.js +0 -15
- package/dist/src/kernel.d.ts +0 -8
- package/dist/src/kernel.js +0 -68
- package/dist/src/motion.d.ts +0 -4
- package/dist/src/motion.js +0 -262
- package/dist/src/patch.d.ts +0 -5
- package/dist/src/patch.js +0 -72
- package/dist/src/player/index.d.ts +0 -68
- package/dist/src/player/index.js +0 -600
- package/dist/src/project.d.ts +0 -11
- package/dist/src/project.js +0 -107
- package/dist/src/render/raw-three.d.ts +0 -7
- package/dist/src/render/raw-three.js +0 -17
- package/dist/src/render/three-html.d.ts +0 -2
- package/dist/src/render/three-html.js +0 -257
- package/dist/src/render/three-preview-svg.d.ts +0 -3
- package/dist/src/render/three-preview-svg.js +0 -102
- package/dist/src/scenes.d.ts +0 -4
- package/dist/src/scenes.js +0 -26
- package/dist/src/sequences.d.ts +0 -43
- package/dist/src/sequences.js +0 -109
- package/dist/src/shapes/builtins.d.ts +0 -2
- package/dist/src/shapes/builtins.js +0 -393
- package/dist/src/shapes/common.d.ts +0 -9
- package/dist/src/shapes/common.js +0 -76
- package/dist/src/shapes/geometry.d.ts +0 -22
- package/dist/src/shapes/geometry.js +0 -166
- package/dist/src/shapes/index.d.ts +0 -2
- package/dist/src/shapes/index.js +0 -18
- package/dist/src/shapes/registry.d.ts +0 -8
- package/dist/src/shapes/registry.js +0 -31
- package/dist/src/shapes/types.d.ts +0 -32
- package/dist/src/shapes/types.js +0 -2
package/dist/src/project.js
DELETED
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.loadVisualProject = loadVisualProject;
|
|
4
|
-
exports.buildSymbolIndex = buildSymbolIndex;
|
|
5
|
-
exports.validateVisualProject = validateVisualProject;
|
|
6
|
-
const validate_1 = require("./validate");
|
|
7
|
-
const utils_1 = require("./utils");
|
|
8
|
-
const fs = require("node:fs");
|
|
9
|
-
const path = require("node:path");
|
|
10
|
-
function loadVisualProject(entryPath) {
|
|
11
|
-
const entry = path.resolve(entryPath);
|
|
12
|
-
const root = path.dirname(entry);
|
|
13
|
-
const files = {};
|
|
14
|
-
const document = loadRecursive(entry, root, files, new Set());
|
|
15
|
-
const project = { root, entry, document, files, symbols: [] };
|
|
16
|
-
project.symbols = buildSymbolIndex(project);
|
|
17
|
-
const result = validateVisualProject(project);
|
|
18
|
-
if (!result.ok) {
|
|
19
|
-
const first = result.issues[0];
|
|
20
|
-
throw new Error(first ? `${first.path}: ${first.message}` : "Invalid visual project.");
|
|
21
|
-
}
|
|
22
|
-
return project;
|
|
23
|
-
}
|
|
24
|
-
function buildSymbolIndex(project) {
|
|
25
|
-
const symbols = [];
|
|
26
|
-
const addElements = (elements, file, scene, prefix) => {
|
|
27
|
-
for (const [index, element] of (0, utils_1.flattenElements)(elements ?? []).entries()) {
|
|
28
|
-
if (!element.id)
|
|
29
|
-
continue;
|
|
30
|
-
symbols.push({
|
|
31
|
-
id: element.id,
|
|
32
|
-
type: element.type,
|
|
33
|
-
...(file ? { file } : {}),
|
|
34
|
-
...(scene ? { scene } : {}),
|
|
35
|
-
path: `${prefix}/${index}`
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
};
|
|
39
|
-
for (const [file, document] of Object.entries(project.files ?? {})) {
|
|
40
|
-
addElements(document.elements, file, undefined, `/files/${file}/elements`);
|
|
41
|
-
for (const [sceneId, scene] of Object.entries(document.scenes ?? {})) {
|
|
42
|
-
addElements(scene.elements, file, sceneId, `/files/${file}/scenes/${sceneId}/elements`);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
if (!project.files || !Object.keys(project.files).length) {
|
|
46
|
-
addElements(project.document.elements, undefined, undefined, "/elements");
|
|
47
|
-
for (const [sceneId, scene] of Object.entries(project.document.scenes ?? {})) {
|
|
48
|
-
addElements(scene.elements, undefined, sceneId, `/scenes/${sceneId}/elements`);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
return symbols;
|
|
52
|
-
}
|
|
53
|
-
function validateVisualProject(project) {
|
|
54
|
-
const merged = (0, validate_1.validateVisualDocument)(project.document);
|
|
55
|
-
const issues = [...merged.issues];
|
|
56
|
-
const warnings = [...merged.warnings];
|
|
57
|
-
for (const [file, document] of Object.entries(project.files ?? {})) {
|
|
58
|
-
const result = (0, validate_1.validateVisualDocument)({ ...document, sequences: undefined });
|
|
59
|
-
issues.push(...result.issues.map((item) => ({ ...item, path: `/files/${file}${item.path}` })));
|
|
60
|
-
warnings.push(...result.warnings.map((item) => ({ ...item, path: `/files/${file}${item.path}` })));
|
|
61
|
-
}
|
|
62
|
-
const seen = new Set();
|
|
63
|
-
for (const symbol of project.symbols ?? []) {
|
|
64
|
-
const scope = `${symbol.file ?? "<merged>"}:${symbol.scene ?? "<root>"}:${symbol.id}`;
|
|
65
|
-
if (seen.has(scope)) {
|
|
66
|
-
issues.push({
|
|
67
|
-
path: symbol.path,
|
|
68
|
-
code: "duplicate_symbol",
|
|
69
|
-
message: `Duplicate id '${symbol.id}' in the same file/scene scope.`,
|
|
70
|
-
suggestion: "Use stable unique ids so AI patches can target one primitive."
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
seen.add(scope);
|
|
74
|
-
}
|
|
75
|
-
return { ok: issues.length === 0, issues, warnings };
|
|
76
|
-
}
|
|
77
|
-
function loadRecursive(filePath, root, files, seen) {
|
|
78
|
-
const absolute = path.resolve(filePath);
|
|
79
|
-
const relative = normalizePath(path.relative(root, absolute));
|
|
80
|
-
if (seen.has(absolute))
|
|
81
|
-
throw new Error(`Circular import detected at '${relative}'.`);
|
|
82
|
-
seen.add(absolute);
|
|
83
|
-
const source = JSON.parse(fs.readFileSync(absolute, "utf8"));
|
|
84
|
-
files[relative] = source;
|
|
85
|
-
const merged = {
|
|
86
|
-
...source,
|
|
87
|
-
elements: [...(source.elements ?? [])],
|
|
88
|
-
scenes: { ...(source.scenes ?? {}) },
|
|
89
|
-
sequences: { ...(source.sequences ?? {}) },
|
|
90
|
-
assets: { ...(source.assets ?? {}) }
|
|
91
|
-
};
|
|
92
|
-
for (const [key, importPath] of Object.entries(source.imports ?? {})) {
|
|
93
|
-
const child = loadRecursive(path.resolve(path.dirname(absolute), importPath), root, files, seen);
|
|
94
|
-
if (child.elements?.length) {
|
|
95
|
-
merged.scenes = merged.scenes ?? {};
|
|
96
|
-
merged.scenes[key] = { id: key, canvas: child.canvas, elements: child.elements };
|
|
97
|
-
}
|
|
98
|
-
merged.scenes = { ...(merged.scenes ?? {}), ...(child.scenes ?? {}) };
|
|
99
|
-
merged.sequences = { ...(merged.sequences ?? {}), ...(child.sequences ?? {}) };
|
|
100
|
-
merged.assets = { ...(merged.assets ?? {}), ...(child.assets ?? {}) };
|
|
101
|
-
}
|
|
102
|
-
seen.delete(absolute);
|
|
103
|
-
return merged;
|
|
104
|
-
}
|
|
105
|
-
function normalizePath(value) {
|
|
106
|
-
return value.replace(/\\/g, "/");
|
|
107
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.renderRawThreeModuleHtml = renderRawThreeModuleHtml;
|
|
4
|
-
function renderRawThreeModuleHtml(options) {
|
|
5
|
-
const background = options.background ?? "#ffffff";
|
|
6
|
-
return `<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Sketchmark Raw Three Module</title><style>html,body{margin:0;width:100%;height:100%;overflow:hidden;background:${escapeCss(background)}}canvas{display:block}</style></head><body><canvas id="stage"></canvas><script type="module">
|
|
7
|
-
import { createSketchmarkThreeScene } from ${JSON.stringify(options.moduleUrl)};
|
|
8
|
-
const canvas = document.getElementById("stage");
|
|
9
|
-
const runtime = await createSketchmarkThreeScene({ canvas, width: ${Number(options.width)}, height: ${Number(options.height)}, background: ${JSON.stringify(background)} });
|
|
10
|
-
if (runtime && typeof runtime.render === "function") runtime.render(0);
|
|
11
|
-
window.__SKETCHMARK_SHOW_TIME__ = (time) => runtime && typeof runtime.render === "function" ? runtime.render(time || 0) : undefined;
|
|
12
|
-
window.__SKETCHMARK_READY__ = true;
|
|
13
|
-
</script></body></html>`;
|
|
14
|
-
}
|
|
15
|
-
function escapeCss(value) {
|
|
16
|
-
return String(value).replace(/[<>"'()\\]/g, "");
|
|
17
|
-
}
|
|
@@ -1,257 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.renderToThreeHtml = renderToThreeHtml;
|
|
4
|
-
const kernel_1 = require("../kernel");
|
|
5
|
-
const DEFAULT_THREE_RUNTIME = "https://cdn.jsdelivr.net/npm/three@0.184.0/build/three.module.js";
|
|
6
|
-
function renderToThreeHtml(document, options = {}) {
|
|
7
|
-
const width = document.canvas.width;
|
|
8
|
-
const height = document.canvas.height;
|
|
9
|
-
const background = options.transparent ? "transparent" : (document.canvas.background ?? "#ffffff");
|
|
10
|
-
const initialTime = Number(options.time ?? 0);
|
|
11
|
-
const frames = JSON.stringify(sampleKernelFrames(document, initialTime));
|
|
12
|
-
const threeRuntime = options.threeRuntime ?? DEFAULT_THREE_RUNTIME;
|
|
13
|
-
return `<!doctype html>
|
|
14
|
-
<html>
|
|
15
|
-
<head>
|
|
16
|
-
<meta charset="utf-8">
|
|
17
|
-
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
18
|
-
<title>Sketchmark Three</title>
|
|
19
|
-
<style>
|
|
20
|
-
html, body {
|
|
21
|
-
margin: 0;
|
|
22
|
-
width: 100%;
|
|
23
|
-
height: 100%;
|
|
24
|
-
overflow: hidden;
|
|
25
|
-
background: ${background};
|
|
26
|
-
display: grid;
|
|
27
|
-
place-items: center;
|
|
28
|
-
}
|
|
29
|
-
main { width: min(100vw, ${width}px); }
|
|
30
|
-
canvas {
|
|
31
|
-
display: block;
|
|
32
|
-
width: min(100vw, ${width}px);
|
|
33
|
-
height: auto;
|
|
34
|
-
aspect-ratio: ${width} / ${height};
|
|
35
|
-
background: transparent;
|
|
36
|
-
}
|
|
37
|
-
</style>
|
|
38
|
-
</head>
|
|
39
|
-
<body>
|
|
40
|
-
<main><canvas id="stage"></canvas></main>
|
|
41
|
-
<script>
|
|
42
|
-
window.__SKETCHMARK_ERROR__ = "";
|
|
43
|
-
window.addEventListener("error", (event) => {
|
|
44
|
-
window.__SKETCHMARK_ERROR__ = event.message || String(event.error || "Unknown script error");
|
|
45
|
-
});
|
|
46
|
-
window.addEventListener("unhandledrejection", (event) => {
|
|
47
|
-
window.__SKETCHMARK_ERROR__ = event.reason && event.reason.message ? event.reason.message : String(event.reason || "Unhandled promise rejection");
|
|
48
|
-
});
|
|
49
|
-
</script>
|
|
50
|
-
<script type="module">
|
|
51
|
-
import * as THREE from ${JSON.stringify(threeRuntime)};
|
|
52
|
-
|
|
53
|
-
const width = ${JSON.stringify(width)};
|
|
54
|
-
const height = ${JSON.stringify(height)};
|
|
55
|
-
const background = ${JSON.stringify(background)};
|
|
56
|
-
const frames = ${frames};
|
|
57
|
-
const canvas = document.getElementById("stage");
|
|
58
|
-
const renderer = new THREE.WebGLRenderer({ canvas, antialias: true, alpha: true, preserveDrawingBuffer: true });
|
|
59
|
-
renderer.setSize(width, height, false);
|
|
60
|
-
renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2));
|
|
61
|
-
|
|
62
|
-
const scene = new THREE.Scene();
|
|
63
|
-
if (background !== "transparent") scene.background = new THREE.Color(background);
|
|
64
|
-
|
|
65
|
-
const camera = new THREE.PerspectiveCamera(50, width / height, 0.1, 1000);
|
|
66
|
-
camera.position.set(6, 4, 8);
|
|
67
|
-
camera.lookAt(0, 0, 0);
|
|
68
|
-
|
|
69
|
-
const content = new THREE.Group();
|
|
70
|
-
scene.add(content);
|
|
71
|
-
let currentFrameIndex = -1;
|
|
72
|
-
|
|
73
|
-
function createObject(element) {
|
|
74
|
-
if (element.type === "mesh3d") {
|
|
75
|
-
const vertices = Array.isArray(element.vertices) ? element.vertices.flatMap((point) => vector(point, [0, 0, 0])) : [];
|
|
76
|
-
const indices = Array.isArray(element.indices) ? element.indices.map((value) => Number(value) || 0) : [];
|
|
77
|
-
const geometry = new THREE.BufferGeometry();
|
|
78
|
-
geometry.setAttribute("position", new THREE.Float32BufferAttribute(vertices, 3));
|
|
79
|
-
if (indices.length) geometry.setIndex(indices);
|
|
80
|
-
geometry.computeVertexNormals();
|
|
81
|
-
const material = materialFor(element);
|
|
82
|
-
const mesh = new THREE.Mesh(geometry, material);
|
|
83
|
-
applyPosition(mesh, element.position);
|
|
84
|
-
return mesh;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (element.type === "line3d") {
|
|
88
|
-
const points = [new THREE.Vector3(...vector(element.from, [0, 0, 0])), new THREE.Vector3(...vector(element.to, [0, 0, 0]))];
|
|
89
|
-
return new THREE.Line(new THREE.BufferGeometry().setFromPoints(points), new THREE.LineBasicMaterial({ color: element.stroke || "#111827" }));
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
if (element.type === "point3d") {
|
|
93
|
-
const object = new THREE.Object3D();
|
|
94
|
-
applyPosition(object, element.position);
|
|
95
|
-
return object;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (element.type === "group3d") {
|
|
99
|
-
const group = new THREE.Group();
|
|
100
|
-
for (const child of element.children || []) {
|
|
101
|
-
const childObject = createObject(child);
|
|
102
|
-
if (childObject) group.add(childObject);
|
|
103
|
-
}
|
|
104
|
-
applyPosition(group, element.position);
|
|
105
|
-
return group;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (element.type === "text3d") {
|
|
109
|
-
const textCanvas = document.createElement("canvas");
|
|
110
|
-
textCanvas.width = 512;
|
|
111
|
-
textCanvas.height = 128;
|
|
112
|
-
const ctx = textCanvas.getContext("2d");
|
|
113
|
-
ctx.clearRect(0, 0, textCanvas.width, textCanvas.height);
|
|
114
|
-
ctx.fillStyle = element.fill || "#111827";
|
|
115
|
-
ctx.font = "700 48px Inter, Arial, sans-serif";
|
|
116
|
-
ctx.textAlign = "center";
|
|
117
|
-
ctx.textBaseline = "middle";
|
|
118
|
-
ctx.fillText(element.text || "", 256, 64);
|
|
119
|
-
const texture = new THREE.CanvasTexture(textCanvas);
|
|
120
|
-
const sprite = new THREE.Sprite(new THREE.SpriteMaterial({ map: texture, transparent: true, opacity: number(element.opacity, 1) }));
|
|
121
|
-
const scale = number(element.fontSize, 1);
|
|
122
|
-
sprite.scale.set(scale * 4, scale, 1);
|
|
123
|
-
applyPosition(sprite, element.position);
|
|
124
|
-
return sprite;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if (element.type === "light") {
|
|
128
|
-
const kind = element.kind || "ambient";
|
|
129
|
-
const intensity = number(element.intensity, kind === "ambient" ? 0.6 : 1);
|
|
130
|
-
const light = kind === "directional"
|
|
131
|
-
? new THREE.DirectionalLight(0xffffff, intensity)
|
|
132
|
-
: kind === "point"
|
|
133
|
-
? new THREE.PointLight(0xffffff, intensity)
|
|
134
|
-
: new THREE.AmbientLight(0xffffff, intensity);
|
|
135
|
-
applyPosition(light, element.position);
|
|
136
|
-
return light;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
return undefined;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
function materialFor(element) {
|
|
143
|
-
const opacity = number(element.opacity, 1);
|
|
144
|
-
return new THREE.MeshStandardMaterial({
|
|
145
|
-
color: element.fill || element.stroke || "#2563eb",
|
|
146
|
-
roughness: 0.45,
|
|
147
|
-
transparent: opacity < 1,
|
|
148
|
-
opacity
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
function showTime(rawTime = 0) {
|
|
153
|
-
const time = Number.isFinite(Number(rawTime)) ? Number(rawTime) : 0;
|
|
154
|
-
const frameIndex = nearestFrameIndex(time);
|
|
155
|
-
if (frameIndex !== currentFrameIndex) {
|
|
156
|
-
currentFrameIndex = frameIndex;
|
|
157
|
-
setElements(frames[frameIndex] ? frames[frameIndex].elements : []);
|
|
158
|
-
}
|
|
159
|
-
renderer.render(scene, camera);
|
|
160
|
-
return true;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
function setElements(elements) {
|
|
164
|
-
while (content.children.length) {
|
|
165
|
-
const child = content.children[0];
|
|
166
|
-
content.remove(child);
|
|
167
|
-
disposeObject(child);
|
|
168
|
-
}
|
|
169
|
-
let hasLight = false;
|
|
170
|
-
for (const element of elements || []) {
|
|
171
|
-
const object = createObject(element);
|
|
172
|
-
if (!object) continue;
|
|
173
|
-
content.add(object);
|
|
174
|
-
if (element.type === "light") hasLight = true;
|
|
175
|
-
}
|
|
176
|
-
if (!hasLight) {
|
|
177
|
-
const ambient = new THREE.AmbientLight(0xffffff, 0.6);
|
|
178
|
-
content.add(ambient);
|
|
179
|
-
const light = new THREE.DirectionalLight(0xffffff, 1);
|
|
180
|
-
light.position.set(5, 8, 5);
|
|
181
|
-
content.add(light);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
function nearestFrameIndex(time) {
|
|
186
|
-
if (!frames.length) return 0;
|
|
187
|
-
let best = 0;
|
|
188
|
-
let bestDistance = Infinity;
|
|
189
|
-
for (let index = 0; index < frames.length; index += 1) {
|
|
190
|
-
const distance = Math.abs(number(frames[index].time, 0) - time);
|
|
191
|
-
if (distance < bestDistance) {
|
|
192
|
-
best = index;
|
|
193
|
-
bestDistance = distance;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
return best;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
function disposeObject(object) {
|
|
200
|
-
object.traverse((child) => {
|
|
201
|
-
if (child.geometry) child.geometry.dispose();
|
|
202
|
-
const materials = Array.isArray(child.material) ? child.material : child.material ? [child.material] : [];
|
|
203
|
-
for (const material of materials) {
|
|
204
|
-
if (material.map) material.map.dispose();
|
|
205
|
-
material.dispose();
|
|
206
|
-
}
|
|
207
|
-
});
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
function applyPosition(object, position) {
|
|
211
|
-
const p = vector(position, [0, 0, 0]);
|
|
212
|
-
object.position.set(p[0], p[1], p[2]);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
function vector(value, fallback) {
|
|
216
|
-
return Array.isArray(value) ? fallback.map((item, index) => number(value[index], item)) : fallback;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
function number(value, fallback) {
|
|
220
|
-
const numeric = Number(value);
|
|
221
|
-
return Number.isFinite(numeric) ? numeric : fallback;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
function degrees(value) {
|
|
225
|
-
return number(value, 0) * Math.PI / 180;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
window.__SKETCHMARK_SHOW_TIME__ = showTime;
|
|
229
|
-
window.__SKETCHMARK_READY__ = true;
|
|
230
|
-
window.addEventListener("message", (event) => {
|
|
231
|
-
const data = event.data || {};
|
|
232
|
-
if (data.type === "sketchmark-show") showTime(data.time || 0);
|
|
233
|
-
});
|
|
234
|
-
showTime(${JSON.stringify(initialTime)});
|
|
235
|
-
</script>
|
|
236
|
-
</body>
|
|
237
|
-
</html>`;
|
|
238
|
-
}
|
|
239
|
-
function sampleKernelFrames(document, initialTime) {
|
|
240
|
-
const duration = finiteNumber(document.canvas.duration, 0);
|
|
241
|
-
const fps = Math.max(1, Math.min(60, finiteNumber(document.canvas.fps, 30)));
|
|
242
|
-
const times = new Set([0, Math.max(0, initialTime)]);
|
|
243
|
-
if (duration > 0) {
|
|
244
|
-
const frameCount = Math.ceil(duration * fps);
|
|
245
|
-
for (let index = 0; index <= frameCount; index += 1) {
|
|
246
|
-
times.add(Number((index / fps).toFixed(6)));
|
|
247
|
-
}
|
|
248
|
-
times.add(duration);
|
|
249
|
-
}
|
|
250
|
-
return [...times]
|
|
251
|
-
.sort((left, right) => left - right)
|
|
252
|
-
.map((time) => ({ time, elements: (0, kernel_1.resolveKernelFrame)(document, time).elements ?? [] }));
|
|
253
|
-
}
|
|
254
|
-
function finiteNumber(value, fallback) {
|
|
255
|
-
const numeric = Number(value);
|
|
256
|
-
return Number.isFinite(numeric) ? numeric : fallback;
|
|
257
|
-
}
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import type { KernelVisualDocument, RenderOptions, ResolvedVisualDocument, VisualDocument } from "../types";
|
|
2
|
-
export declare function renderThreePreviewSvg(document: VisualDocument, time?: number, options?: RenderOptions): string;
|
|
3
|
-
export declare function renderResolvedThreePreviewSvg(document: ResolvedVisualDocument | KernelVisualDocument, options?: RenderOptions): string;
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.renderThreePreviewSvg = renderThreePreviewSvg;
|
|
4
|
-
exports.renderResolvedThreePreviewSvg = renderResolvedThreePreviewSvg;
|
|
5
|
-
const kernel_1 = require("../kernel");
|
|
6
|
-
function renderThreePreviewSvg(document, time = 0, options = {}) {
|
|
7
|
-
const frame = (0, kernel_1.resolveKernelFrame)(document, time);
|
|
8
|
-
return renderResolvedThreePreviewSvg(frame, options);
|
|
9
|
-
}
|
|
10
|
-
function renderResolvedThreePreviewSvg(document, options = {}) {
|
|
11
|
-
const kernel = isKernelVisualDocument(document) ? document : (0, kernel_1.lowerResolvedVisualDocument)(document);
|
|
12
|
-
const width = document.canvas.width;
|
|
13
|
-
const height = document.canvas.height;
|
|
14
|
-
const background = document.canvas.background ?? "#ffffff";
|
|
15
|
-
const elements = [...(kernel.elements ?? [])].sort((a, b) => depthOf(a) - depthOf(b));
|
|
16
|
-
const backdrop = options.transparent ? "" : `<rect x="0" y="0" width="${width}" height="${height}" fill="${escapeAttr(background)}"/>`;
|
|
17
|
-
return `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}" role="img">${backdrop}${elements.map((element) => renderElement(element, width, height)).join("")}</svg>`;
|
|
18
|
-
}
|
|
19
|
-
function renderElement(element, width, height) {
|
|
20
|
-
const opacity = element.opacity === undefined ? "" : ` opacity="${Number(element.opacity)}"`;
|
|
21
|
-
const id = element.id ? ` id="${escapeAttr(element.id)}"` : "";
|
|
22
|
-
const fill = escapeAttr(String(element.fill ?? "#2563eb"));
|
|
23
|
-
const stroke = escapeAttr(String(element.stroke ?? "#111827"));
|
|
24
|
-
const strokeWidth = Number(element.strokeWidth ?? 1);
|
|
25
|
-
if (element.type === "mesh3d") {
|
|
26
|
-
const faces = meshFaces(element, width, height);
|
|
27
|
-
return faces
|
|
28
|
-
.sort((a, b) => a.depth - b.depth)
|
|
29
|
-
.map((face, index) => `<polygon${index === 0 ? `${id}${opacity}` : opacity} points="${face.points.map((point) => point.join(",")).join(" ")}" fill="${shade(fill, face.shade)}" stroke="${stroke}" stroke-width="${strokeWidth}"/>`)
|
|
30
|
-
.join("");
|
|
31
|
-
}
|
|
32
|
-
if (element.type === "line3d" && Array.isArray(element.from) && Array.isArray(element.to)) {
|
|
33
|
-
const from = project(element.from, width, height);
|
|
34
|
-
const to = project(element.to, width, height);
|
|
35
|
-
return `<line${id}${opacity} x1="${from[0]}" y1="${from[1]}" x2="${to[0]}" y2="${to[1]}" stroke="${stroke}" stroke-width="${strokeWidth}" fill="none"/>`;
|
|
36
|
-
}
|
|
37
|
-
if (element.type === "text3d") {
|
|
38
|
-
const point = project(element.position, width, height);
|
|
39
|
-
return `<text${id}${opacity} x="${point[0]}" y="${point[1]}" text-anchor="middle" dominant-baseline="middle" font-family="Inter, Arial, sans-serif" font-size="${Number(element.fontSize ?? 18)}" font-weight="700" fill="${fill}">${escapeText(element.text)}</text>`;
|
|
40
|
-
}
|
|
41
|
-
return "";
|
|
42
|
-
}
|
|
43
|
-
function isKernelVisualDocument(document) {
|
|
44
|
-
return (document.elements ?? []).every((element) => element.type === "group" || element.type === "path" || element.type === "text" || element.type === "image" || element.type === "point" || element.type === "group3d" || element.type === "mesh3d" || element.type === "line3d" || element.type === "text3d" || element.type === "point3d" || element.type === "light");
|
|
45
|
-
}
|
|
46
|
-
function meshFaces(element, width, height) {
|
|
47
|
-
const faces = element.faces && element.faces.length ? element.faces : indicesToFaces(element.indices);
|
|
48
|
-
return faces
|
|
49
|
-
.filter((face) => face.length >= 3)
|
|
50
|
-
.map((face, index) => {
|
|
51
|
-
const world = face.map((vertexIndex) => worldVertex(element, vertexIndex));
|
|
52
|
-
const depth = world.reduce((total, point) => total + point[2] - point[1] * 0.1, 0) / world.length;
|
|
53
|
-
return {
|
|
54
|
-
points: world.map((point) => project(point, width, height)),
|
|
55
|
-
shade: 0.74 + (index % 4) * 0.08,
|
|
56
|
-
depth
|
|
57
|
-
};
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
function indicesToFaces(indices) {
|
|
61
|
-
const faces = [];
|
|
62
|
-
for (let index = 0; index < indices.length; index += 3)
|
|
63
|
-
faces.push(indices.slice(index, index + 3));
|
|
64
|
-
return faces;
|
|
65
|
-
}
|
|
66
|
-
function worldVertex(element, index) {
|
|
67
|
-
const vertex = element.vertices[index] ?? [0, 0, 0];
|
|
68
|
-
const position = element.position ?? [0, 0, 0];
|
|
69
|
-
return [vertex[0] + position[0], vertex[1] + position[1], vertex[2] + position[2]];
|
|
70
|
-
}
|
|
71
|
-
function project([x, y, z], width, height) {
|
|
72
|
-
const s = scale(width, height);
|
|
73
|
-
return [
|
|
74
|
-
width / 2 + (x - z) * s * 0.9,
|
|
75
|
-
height / 2 - y * s * 0.9 + (x + z) * s * 0.35
|
|
76
|
-
];
|
|
77
|
-
}
|
|
78
|
-
function scale(width, height) {
|
|
79
|
-
return Math.min(width, height) / 8;
|
|
80
|
-
}
|
|
81
|
-
function depthOf(element) {
|
|
82
|
-
if ("position" in element && Array.isArray(element.position))
|
|
83
|
-
return element.position[2] - element.position[1] * 0.1;
|
|
84
|
-
return 0;
|
|
85
|
-
}
|
|
86
|
-
function shade(hex, factor) {
|
|
87
|
-
const color = parseHex(hex);
|
|
88
|
-
if (!color)
|
|
89
|
-
return hex;
|
|
90
|
-
return `#${color.map((channel) => Math.max(0, Math.min(255, Math.round(channel * factor))).toString(16).padStart(2, "0")).join("")}`;
|
|
91
|
-
}
|
|
92
|
-
function parseHex(value) {
|
|
93
|
-
if (!/^#[0-9a-fA-F]{6}$/.test(value))
|
|
94
|
-
return undefined;
|
|
95
|
-
return [parseInt(value.slice(1, 3), 16), parseInt(value.slice(3, 5), 16), parseInt(value.slice(5, 7), 16)];
|
|
96
|
-
}
|
|
97
|
-
function escapeAttr(value) {
|
|
98
|
-
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<");
|
|
99
|
-
}
|
|
100
|
-
function escapeText(value) {
|
|
101
|
-
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
102
|
-
}
|
package/dist/src/scenes.d.ts
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import type { ResolvedVisualDocument, VisualDocument } from "./types";
|
|
2
|
-
export declare function listScenes(document: VisualDocument): string[];
|
|
3
|
-
export declare function documentForScene(document: VisualDocument, sceneId: string): VisualDocument;
|
|
4
|
-
export declare function resolvedFrameForScene(document: VisualDocument, sceneId: string, time?: number): ResolvedVisualDocument;
|
package/dist/src/scenes.js
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.listScenes = listScenes;
|
|
4
|
-
exports.documentForScene = documentForScene;
|
|
5
|
-
exports.resolvedFrameForScene = resolvedFrameForScene;
|
|
6
|
-
const normalize_1 = require("./normalize");
|
|
7
|
-
const utils_1 = require("./utils");
|
|
8
|
-
function listScenes(document) {
|
|
9
|
-
return Object.keys(document.scenes ?? {});
|
|
10
|
-
}
|
|
11
|
-
function documentForScene(document, sceneId) {
|
|
12
|
-
const scene = document.scenes?.[sceneId];
|
|
13
|
-
if (!scene)
|
|
14
|
-
throw new Error(`Unknown scene '${sceneId}'.`);
|
|
15
|
-
return {
|
|
16
|
-
...(0, utils_1.clone)(document),
|
|
17
|
-
canvas: { ...document.canvas, ...(scene.canvas ?? {}) },
|
|
18
|
-
elements: (0, utils_1.clone)(scene.elements),
|
|
19
|
-
motion: (0, utils_1.clone)(scene.motion),
|
|
20
|
-
scenes: undefined,
|
|
21
|
-
sequences: undefined
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
function resolvedFrameForScene(document, sceneId, time = 0) {
|
|
25
|
-
return (0, normalize_1.resolveVisualFrame)(documentForScene(document, sceneId), time);
|
|
26
|
-
}
|
package/dist/src/sequences.d.ts
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import type { ResolvedVisualDocument, VisualDocument } from "./types";
|
|
2
|
-
export interface CompiledVisualSequence {
|
|
3
|
-
id: string;
|
|
4
|
-
duration: number;
|
|
5
|
-
clips: Array<{
|
|
6
|
-
scene: string;
|
|
7
|
-
start: number;
|
|
8
|
-
duration: number;
|
|
9
|
-
transition: NormalizedTransition;
|
|
10
|
-
}>;
|
|
11
|
-
}
|
|
12
|
-
export interface SequenceFrame {
|
|
13
|
-
document: VisualDocument;
|
|
14
|
-
scene: string;
|
|
15
|
-
localTime: number;
|
|
16
|
-
globalTime: number;
|
|
17
|
-
transition?: {
|
|
18
|
-
type: "fade";
|
|
19
|
-
fromScene: string;
|
|
20
|
-
toScene: string;
|
|
21
|
-
progress: number;
|
|
22
|
-
duration: number;
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
export interface ResolvedSequenceFrame extends Omit<SequenceFrame, "document"> {
|
|
26
|
-
document: ResolvedVisualDocument;
|
|
27
|
-
}
|
|
28
|
-
export interface SequenceTimelineFrame {
|
|
29
|
-
index: number;
|
|
30
|
-
time: number;
|
|
31
|
-
scene: string;
|
|
32
|
-
localTime: number;
|
|
33
|
-
transition?: SequenceFrame["transition"];
|
|
34
|
-
}
|
|
35
|
-
export interface NormalizedTransition {
|
|
36
|
-
type: "cut" | "fade";
|
|
37
|
-
duration: number;
|
|
38
|
-
}
|
|
39
|
-
export declare function compileVisualSequence(document: VisualDocument, sequenceId: string): CompiledVisualSequence;
|
|
40
|
-
export declare function defaultSequenceId(document: VisualDocument): string | undefined;
|
|
41
|
-
export declare function documentForSequenceTime(document: VisualDocument, sequenceId: string, time: number): SequenceFrame;
|
|
42
|
-
export declare function resolvedFrameForSequenceTime(document: VisualDocument, sequenceId: string, time: number): ResolvedSequenceFrame;
|
|
43
|
-
export declare function sequenceTimeline(document: VisualDocument, sequenceId: string, fps: number): SequenceTimelineFrame[];
|