@3driseai/3drise-core 1.0.18 → 1.0.20
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/dist/animations/index.d.ts +2 -0
- package/dist/animations/index.d.ts.map +1 -0
- package/dist/animations/index.js +1 -0
- package/dist/animations/trackGenerators.d.ts +140 -0
- package/dist/animations/trackGenerators.d.ts.map +1 -0
- package/dist/animations/trackGenerators.js +443 -0
- package/dist/autoAnimate/bounds.d.ts +7 -0
- package/dist/autoAnimate/bounds.d.ts.map +1 -0
- package/dist/autoAnimate/bounds.js +23 -0
- package/dist/autoAnimate/composer.d.ts +30 -0
- package/dist/autoAnimate/composer.d.ts.map +1 -0
- package/dist/autoAnimate/composer.js +251 -0
- package/dist/autoAnimate/describeScene.d.ts +105 -0
- package/dist/autoAnimate/describeScene.d.ts.map +1 -0
- package/dist/autoAnimate/describeScene.js +165 -0
- package/dist/autoAnimate/draft.d.ts +59 -0
- package/dist/autoAnimate/draft.d.ts.map +1 -0
- package/dist/autoAnimate/draft.js +2 -0
- package/dist/autoAnimate/index.d.ts +14 -0
- package/dist/autoAnimate/index.d.ts.map +1 -0
- package/dist/autoAnimate/index.js +9 -0
- package/dist/autoAnimate/recipes.d.ts +87 -0
- package/dist/autoAnimate/recipes.d.ts.map +1 -0
- package/dist/autoAnimate/recipes.js +224 -0
- package/dist/autoAnimate/rng.d.ts +10 -0
- package/dist/autoAnimate/rng.d.ts.map +1 -0
- package/dist/autoAnimate/rng.js +16 -0
- package/dist/autoAnimate/sceneAnalyzer.d.ts +7 -0
- package/dist/autoAnimate/sceneAnalyzer.d.ts.map +1 -0
- package/dist/autoAnimate/sceneAnalyzer.js +291 -0
- package/dist/autoAnimate/types.d.ts +124 -0
- package/dist/autoAnimate/types.d.ts.map +1 -0
- package/dist/autoAnimate/types.js +1 -0
- package/dist/autoAnimate/validator.d.ts +10 -0
- package/dist/autoAnimate/validator.d.ts.map +1 -0
- package/dist/autoAnimate/validator.js +59 -0
- package/dist/data/effectsDefaults.d.ts +4 -0
- package/dist/data/effectsDefaults.d.ts.map +1 -1
- package/dist/data/effectsDefaults.js +393 -2
- package/dist/data/gridsDefaults.d.ts +5 -0
- package/dist/data/gridsDefaults.d.ts.map +1 -0
- package/dist/data/gridsDefaults.js +290 -0
- package/dist/data/index.d.ts +1 -0
- package/dist/data/index.d.ts.map +1 -1
- package/dist/data/index.js +1 -0
- package/dist/data/particlesDefaults.d.ts +3 -1
- package/dist/data/particlesDefaults.d.ts.map +1 -1
- package/dist/data/particlesDefaults.js +78 -0
- package/dist/environment/cloudConfigs.d.ts +313 -0
- package/dist/environment/cloudConfigs.d.ts.map +1 -0
- package/dist/environment/cloudConfigs.js +342 -0
- package/dist/environment/index.d.ts +5 -0
- package/dist/environment/index.d.ts.map +1 -0
- package/dist/environment/index.js +6 -0
- package/dist/environment/landscapeConfigs.d.ts +220 -0
- package/dist/environment/landscapeConfigs.d.ts.map +1 -0
- package/dist/environment/landscapeConfigs.js +256 -0
- package/dist/environment/oceanConfigs.d.ts +293 -0
- package/dist/environment/oceanConfigs.d.ts.map +1 -0
- package/dist/environment/oceanConfigs.js +380 -0
- package/dist/environment/terrainConfigs.d.ts +254 -0
- package/dist/environment/terrainConfigs.d.ts.map +1 -0
- package/dist/environment/terrainConfigs.js +292 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/properties/index.d.ts +4 -0
- package/dist/properties/index.d.ts.map +1 -0
- package/dist/properties/index.js +3 -0
- package/dist/properties/registry.d.ts +44 -0
- package/dist/properties/registry.d.ts.map +1 -0
- package/dist/properties/registry.js +343 -0
- package/dist/properties/tables/effectsProperties.d.ts +40 -0
- package/dist/properties/tables/effectsProperties.d.ts.map +1 -0
- package/dist/properties/tables/effectsProperties.js +512 -0
- package/dist/properties/tables/geometryProperties.d.ts +38 -0
- package/dist/properties/tables/geometryProperties.d.ts.map +1 -0
- package/dist/properties/tables/geometryProperties.js +91 -0
- package/dist/properties/tables/getPropertyConfig.d.ts +3 -0
- package/dist/properties/tables/getPropertyConfig.d.ts.map +1 -0
- package/dist/properties/tables/getPropertyConfig.js +16 -0
- package/dist/properties/tables/gridProperties.d.ts +6 -0
- package/dist/properties/tables/gridProperties.d.ts.map +1 -0
- package/dist/properties/tables/gridProperties.js +121 -0
- package/dist/properties/tables/index.d.ts +9 -0
- package/dist/properties/tables/index.d.ts.map +1 -0
- package/dist/properties/tables/index.js +8 -0
- package/dist/properties/tables/lightProperties.d.ts +14 -0
- package/dist/properties/tables/lightProperties.d.ts.map +1 -0
- package/dist/properties/tables/lightProperties.js +263 -0
- package/dist/properties/tables/materialBounds.d.ts +33 -0
- package/dist/properties/tables/materialBounds.d.ts.map +1 -0
- package/dist/properties/tables/materialBounds.js +180 -0
- package/dist/properties/tables/optionalProperties.d.ts +83 -0
- package/dist/properties/tables/optionalProperties.d.ts.map +1 -0
- package/dist/properties/tables/optionalProperties.js +710 -0
- package/dist/properties/tables/particlesProperties.d.ts +17 -0
- package/dist/properties/tables/particlesProperties.d.ts.map +1 -0
- package/dist/properties/tables/particlesProperties.js +334 -0
- package/dist/properties/types.d.ts +43 -0
- package/dist/properties/types.d.ts.map +1 -0
- package/dist/properties/types.js +12 -0
- package/dist/services/AnimationsManager.d.ts +6 -106
- package/dist/services/AnimationsManager.d.ts.map +1 -1
- package/dist/services/AnimationsManager.js +5 -391
- package/dist/services/SceneActions.d.ts +17 -3
- package/dist/services/SceneActions.d.ts.map +1 -1
- package/dist/services/SceneActions.js +12 -8
- package/dist/services/ShadersLibrary.d.ts +1 -1
- package/dist/services/ShadersLibrary.d.ts.map +1 -1
- package/dist/services/index.d.ts +1 -2
- package/dist/services/index.d.ts.map +1 -1
- package/dist/shaders/WallpaperGridMaterial.d.ts.map +1 -1
- package/dist/shaders/WallpaperGridMaterial.js +6 -1
- package/dist/three/GalleryLayoutV.d.ts.map +1 -1
- package/dist/three/GalleryLayoutV.js +0 -10
- package/dist/three/SceneBuilderV.d.ts.map +1 -1
- package/dist/three/SceneBuilderV.js +1 -7
- package/dist/types/actions.d.ts +8 -1
- package/dist/types/actions.d.ts.map +1 -1
- package/dist/types/actions.js +0 -4
- package/dist/types/animations.d.ts +0 -51
- package/dist/types/animations.d.ts.map +1 -1
- package/dist/types/generativeEffects.d.ts +0 -4
- package/dist/types/generativeEffects.d.ts.map +1 -1
- package/dist/types/generativeParticles.d.ts +175 -2
- package/dist/types/generativeParticles.d.ts.map +1 -1
- package/dist/types/objectSettings.d.ts +96 -17
- package/dist/types/objectSettings.d.ts.map +1 -1
- package/dist/types/scene3d.d.ts +3 -3
- package/dist/types/scene3d.d.ts.map +1 -1
- package/dist/types/types.d.ts +3 -2
- package/dist/types/types.d.ts.map +1 -1
- package/dist/utils/generativeAnimatable.d.ts +1 -1
- package/dist/utils/generativeAnimatable.d.ts.map +1 -1
- package/dist/utils/generativeAnimatable.js +7 -1
- package/dist/utils/getAnimatableProperties.d.ts +12 -1
- package/dist/utils/getAnimatableProperties.d.ts.map +1 -1
- package/dist/utils/getAnimatableProperties.js +35 -138
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -0
- package/dist/utils/objectEffects.d.ts +6 -0
- package/dist/utils/objectEffects.d.ts.map +1 -0
- package/dist/utils/objectEffects.js +5 -0
- package/package.json +1 -1
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// generateAnimation — scene profile + template + style → clips and a sequence.
|
|
3
|
+
// =============================================================================
|
|
4
|
+
//
|
|
5
|
+
// Templates:
|
|
6
|
+
// intro Reveal (hero enters, camera pulls in) → Ensemble (groups cascade in,
|
|
7
|
+
// the rest follow, lights) → Showcase (camera swings around the hero,
|
|
8
|
+
// the hero pulses). Ends on the authored scene. Plays once (hold).
|
|
9
|
+
// ambient One looping step: everything breathes / floats / sways and returns to
|
|
10
|
+
// rest, the camera swings out and back — seamless at the loop seam.
|
|
11
|
+
//
|
|
12
|
+
// Rules enforced here (see claude/auto-animate-plan.md): continuity through a
|
|
13
|
+
// ledger; group coherence (same recipe, ease, duration, ±7% amplitude, cascade in
|
|
14
|
+
// spatial order with a fixed total stagger); scale-relative magnitudes; ground;
|
|
15
|
+
// camera speed limit; return to rest; beat grid (0.25 s); no parent + child on the
|
|
16
|
+
// same object chain; saved objects only; a later entrance is staged at its start
|
|
17
|
+
// pose from the first frame (so nothing pops in before its turn).
|
|
18
|
+
//
|
|
19
|
+
import { toActionKey, uniqueActionKey } from '../utils/actionKeys.js';
|
|
20
|
+
import { analyzeScene } from './sceneAnalyzer.js';
|
|
21
|
+
import { createRng } from './rng.js';
|
|
22
|
+
import { chooseEnter, environmentPaths, Ledger, planAmbient, planCameraDolly, planCameraOrbit, planEnter, planLightEnter, round, STYLES, } from './recipes.js';
|
|
23
|
+
import { validateGenerated } from './validator.js';
|
|
24
|
+
const LENGTH_MUL = { short: 0.8, medium: 1, long: 1.3 };
|
|
25
|
+
const BEAT = 0.25;
|
|
26
|
+
const snap = (t, min = BEAT) => Math.max(min, Math.round(t / BEAT) * BEAT);
|
|
27
|
+
const defaultId = (prefix) => `${prefix}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 7)}`;
|
|
28
|
+
const getAtPath = (obj, path) => path.split('.').reduce((o, k) => (o == null ? undefined : o[k]), obj);
|
|
29
|
+
export function generateAnimation(input, options) {
|
|
30
|
+
const profile = options.profile ?? analyzeScene(input, options.analyze);
|
|
31
|
+
const makeId = options.makeId ?? defaultId;
|
|
32
|
+
const seed = options.seed ?? Math.floor(Math.random() * 2 ** 31);
|
|
33
|
+
const rng = createRng(seed);
|
|
34
|
+
const style = STYLES[options.style] ?? STYLES.calm;
|
|
35
|
+
const driver = options.driver ?? 'time';
|
|
36
|
+
const objectsById = new Map((input.objects ?? []).map((o) => [o.id, o]));
|
|
37
|
+
const authored = (state, path) => {
|
|
38
|
+
const src = state === 'camera' ? input.camera
|
|
39
|
+
: state === 'sky' ? input.sky
|
|
40
|
+
: state === 'clouds' ? input.clouds
|
|
41
|
+
: state === 'ocean' ? input.ocean
|
|
42
|
+
: state === 'terrain' ? input.terrain
|
|
43
|
+
: objectsById.get(state);
|
|
44
|
+
const v = getAtPath(src, path);
|
|
45
|
+
return v === undefined ? undefined : (Array.isArray(v) ? [...v] : v);
|
|
46
|
+
};
|
|
47
|
+
const ctx = { profile, rng, style, ledger: new Ledger(authored), makeId, driven: driver !== 'time' };
|
|
48
|
+
const taken = new Set(options.takenKeys ?? []);
|
|
49
|
+
const keyFor = (name) => {
|
|
50
|
+
const k = uniqueActionKey(toActionKey(name), taken);
|
|
51
|
+
taken.add(k);
|
|
52
|
+
return k;
|
|
53
|
+
};
|
|
54
|
+
const clips = [];
|
|
55
|
+
const steps = [];
|
|
56
|
+
const lenMul = LENGTH_MUL[options.length] ?? 1;
|
|
57
|
+
const dur = (base) => snap(base * style.durationMul * lenMul);
|
|
58
|
+
// --- helpers ------------------------------------------------------------------------
|
|
59
|
+
const key = (t, value, ease) => ({ id: makeId('k'), t: round(t), value, ...(ease ? { ease } : {}) });
|
|
60
|
+
const track = (state, path, keys) => ({ id: makeId('tr'), state, path, keys });
|
|
61
|
+
const enterTracks = (moves, t0, d) => moves.map(m => track(m.state, m.path, [key(t0, m.away, m.ease), key(t0 + d, m.rest)]));
|
|
62
|
+
const addClip = (name, tracks, duration, description) => {
|
|
63
|
+
if (!tracks.length)
|
|
64
|
+
return null;
|
|
65
|
+
const clip = {
|
|
66
|
+
id: makeId('clip'), name: `Auto · ${name}`, key: keyFor(`auto ${name}`), description,
|
|
67
|
+
duration: round(duration, 3), fps: 30, loop: false, yoyo: false, endMode: 'hold', tracks,
|
|
68
|
+
};
|
|
69
|
+
clips.push(clip);
|
|
70
|
+
ctx.ledger.commit(tracks);
|
|
71
|
+
return clip;
|
|
72
|
+
};
|
|
73
|
+
const addStep = (list) => {
|
|
74
|
+
const ok = list.filter((c) => !!c);
|
|
75
|
+
if (ok.length)
|
|
76
|
+
steps.push({ id: makeId('step'), clips: ok.map(c => ({ id: makeId('sc'), clipId: c.id, delay: 0, speed: 1 })) });
|
|
77
|
+
return ok.length ? Math.max(...ok.map(c => c.duration)) : 0;
|
|
78
|
+
};
|
|
79
|
+
// --- who moves ----------------------------------------------------------------------------
|
|
80
|
+
const byId = new Map(profile.objects.map(p => [p.id, p]));
|
|
81
|
+
const ancestors = (p) => {
|
|
82
|
+
const out = [];
|
|
83
|
+
let cur = p.parentId ? byId.get(p.parentId) : undefined;
|
|
84
|
+
while (cur && out.length < 64) {
|
|
85
|
+
out.push(cur.id);
|
|
86
|
+
cur = cur.parentId ? byId.get(cur.parentId) : undefined;
|
|
87
|
+
}
|
|
88
|
+
return out;
|
|
89
|
+
};
|
|
90
|
+
const hero = profile.heroId ? byId.get(profile.heroId) ?? null : null;
|
|
91
|
+
const animated = new Set();
|
|
92
|
+
/** A new object may move unless it or one of its ancestors / descendants already does. */
|
|
93
|
+
const canAnimate = (p) => p.saved && p.visible && !animated.has(p.id) && !ancestors(p).some(a => animated.has(a)) &&
|
|
94
|
+
![...animated].some(id => ancestors(byId.get(id)).includes(p.id));
|
|
95
|
+
if (hero && hero.saved)
|
|
96
|
+
animated.add(hero.id);
|
|
97
|
+
const groups = [];
|
|
98
|
+
for (const g of profile.groups) {
|
|
99
|
+
const members = g.memberIds.map(id => byId.get(id)).filter(m => m.role !== 'hero');
|
|
100
|
+
const free = members.filter(canAnimate);
|
|
101
|
+
if (free.length < 2)
|
|
102
|
+
continue;
|
|
103
|
+
free.forEach(m => animated.add(m.id));
|
|
104
|
+
const order = (rng.next() < 0.5 ? g.order : g.radialOrder).filter(id => free.some(m => m.id === id));
|
|
105
|
+
const first = byId.get(order[0]);
|
|
106
|
+
const label = g.kind === 'parent' ? byId.get(g.parentId)?.name ?? 'Group' : `${first.name.replace(/[\s_\-.#]*\d+\s*$/, '') || first.name}s`;
|
|
107
|
+
groups.push({ group: g, name: label, members: order.map(id => byId.get(id)) });
|
|
108
|
+
}
|
|
109
|
+
const standalone = profile.objects
|
|
110
|
+
.filter(p => (p.role === 'standalone' || p.role === 'member') && canAnimate(p))
|
|
111
|
+
.sort((a, b) => a.bounds.center[0] - b.bounds.center[0]);
|
|
112
|
+
standalone.forEach(p => animated.add(p.id));
|
|
113
|
+
const lights = profile.objects.filter(p => p.role === 'light' && p.saved && p.capabilities.config.includes('intensity'));
|
|
114
|
+
const center = hero ? hero.bounds.center : profile.bounds.center;
|
|
115
|
+
// =========================================================================================
|
|
116
|
+
if (options.template === 'ambient') {
|
|
117
|
+
const T = dur(4);
|
|
118
|
+
const phaseSpan = T * 0.5;
|
|
119
|
+
const stepClips = [];
|
|
120
|
+
const ambientTracks = (p, name, amp, phase) => {
|
|
121
|
+
const plan = planAmbient(name, p, amp, ctx);
|
|
122
|
+
if (!plan)
|
|
123
|
+
return [];
|
|
124
|
+
return [track(p.id, plan.path, plan.points.map(([f, v], i) => key(phase + f * T, v, i < plan.points.length - 1 ? style.ambientEase : undefined)))];
|
|
125
|
+
};
|
|
126
|
+
if (hero && animated.has(hero.id)) {
|
|
127
|
+
stepClips.push(addClip(hero.name, [...ambientTracks(hero, 'float', 1, 0), ...ambientTracks(hero, 'glow', 1, 0)], T, 'Hero: float and glow'));
|
|
128
|
+
}
|
|
129
|
+
for (const g of groups) {
|
|
130
|
+
const name = rng.pick(['float', 'sway', 'breathe']);
|
|
131
|
+
const n = g.members.length;
|
|
132
|
+
const tracks = g.members.flatMap((m, i) => ambientTracks(m, name, rng.jitter(0.07), snap((phaseSpan * i) / Math.max(1, n), 0)));
|
|
133
|
+
stepClips.push(addClip(g.name, tracks, T + phaseSpan, `${name}, a wave across the group`));
|
|
134
|
+
}
|
|
135
|
+
if (standalone.length) {
|
|
136
|
+
const n = standalone.length;
|
|
137
|
+
const tracks = standalone.flatMap((m, i) => ambientTracks(m, 'float', rng.jitter(0.07) * 0.7, snap((phaseSpan * i) / Math.max(1, n), 0)));
|
|
138
|
+
stepClips.push(addClip('Others', tracks, T + phaseSpan, 'Gentle float'));
|
|
139
|
+
}
|
|
140
|
+
if (lights.length) {
|
|
141
|
+
stepClips.push(addClip('Lights', lights.flatMap(l => ambientTracks(l, 'swell', rng.jitter(0.07), 0)), T, 'Light swell'));
|
|
142
|
+
}
|
|
143
|
+
const L = Math.max(T, ...stepClips.filter(Boolean).map(c => c.duration));
|
|
144
|
+
stepClips.push(addClip('Camera', planCameraOrbit(ctx, center, L, rng.next() < 0.5 ? 1 : -1), L, 'Swing out and back'));
|
|
145
|
+
const envTracks = environmentPaths(ctx).map(e => {
|
|
146
|
+
const v = Number(ctx.ledger.get(e.state, e.path));
|
|
147
|
+
const peak = round(Math.min(e.max, Math.max(e.min, v + e.delta / 2)));
|
|
148
|
+
return track(e.state, e.path, [key(0, v, 'easeInOutSine'), key(L / 2, peak, 'easeInOutSine'), key(L, v)]);
|
|
149
|
+
});
|
|
150
|
+
stepClips.push(addClip('Atmosphere', envTracks, L, 'Slow environment drift'));
|
|
151
|
+
addStep(stepClips);
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
// ---------------------------------------------------------------- intro
|
|
155
|
+
const D = dur(1.2);
|
|
156
|
+
const envs = environmentPaths(ctx);
|
|
157
|
+
// Plan every entrance first: their start poses are staged from the first frame.
|
|
158
|
+
const heroMoves = hero && animated.has(hero.id) ? chooseEnter(hero, 1, ctx).moves : [];
|
|
159
|
+
const groupPlans = groups.map(g => {
|
|
160
|
+
// One recipe for the whole group: the first of the style that works for every member.
|
|
161
|
+
const names = [...style.enters, 'pop'];
|
|
162
|
+
const name = names.find(nm => g.members.every(m => planEnter(nm, m, 1, ctx))) ?? 'pop';
|
|
163
|
+
const amps = g.members.map(() => rng.jitter(0.07));
|
|
164
|
+
return { ...g, moves: g.members.map((m, i) => planEnter(name, m, amps[i], ctx) ?? []), recipe: name };
|
|
165
|
+
});
|
|
166
|
+
const standaloneMoves = standalone.map(p => chooseEnter(p, 0.8 * rng.jitter(0.07), ctx).moves);
|
|
167
|
+
const lightMoves = style.name === 'cinematic' ? lights.flatMap(l => planLightEnter(l, ctx)) : [];
|
|
168
|
+
// Step 1 — Reveal
|
|
169
|
+
const later = [...groupPlans.flatMap(g => g.moves.flat()), ...standaloneMoves.flat(), ...lightMoves];
|
|
170
|
+
const step1 = [];
|
|
171
|
+
step1.push(addClip('Staging', later.map(m => track(m.state, m.path, [key(0, m.away)])), BEAT, 'Start poses of what enters later'));
|
|
172
|
+
if (heroMoves.length)
|
|
173
|
+
step1.push(addClip(`${hero.name} enters`, enterTracks(heroMoves, 0, D), D, 'Hero entrance'));
|
|
174
|
+
const dolly = planCameraDolly(ctx, 1);
|
|
175
|
+
const Dc = dur(1.6);
|
|
176
|
+
if (dolly)
|
|
177
|
+
step1.push(addClip('Camera in', [track('camera', dolly.path, [key(0, dolly.away, style.name === 'cinematic' ? 'easeInOutCubic' : 'easeOutCubic'), key(Dc, dolly.rest)])], Dc, 'Pull in to the authored view'));
|
|
178
|
+
const L1 = Math.max(D, Dc);
|
|
179
|
+
// Step 2 — Ensemble
|
|
180
|
+
const step2 = [];
|
|
181
|
+
for (const g of groupPlans) {
|
|
182
|
+
const n = g.members.length;
|
|
183
|
+
const S = snap(Math.min(1.4, Math.max(0.4, 0.18 * n)) * style.durationMul, 0);
|
|
184
|
+
const tracks = g.moves.flatMap((moves, i) => enterTracks(moves, n > 1 ? round((S * i) / (n - 1)) : 0, D));
|
|
185
|
+
step2.push(addClip(`${g.name} enter`, tracks, D + S, `${g.recipe}, cascading`));
|
|
186
|
+
}
|
|
187
|
+
if (standalone.length) {
|
|
188
|
+
const n = standalone.length;
|
|
189
|
+
const S = snap(Math.min(1.2, Math.max(0.3, 0.15 * n)) * style.durationMul, 0);
|
|
190
|
+
step2.push(addClip('Others enter', standaloneMoves.flatMap((moves, i) => enterTracks(moves, n > 1 ? round((S * i) / (n - 1)) : 0, D)), D + S));
|
|
191
|
+
}
|
|
192
|
+
if (lightMoves.length)
|
|
193
|
+
step2.push(addClip('Lights on', enterTracks(lightMoves, 0, D * 1.5), D * 1.5));
|
|
194
|
+
const L2 = step2.filter(Boolean).length ? Math.max(...step2.filter(Boolean).map(c => c.duration)) : 0;
|
|
195
|
+
// Step 3 — Showcase
|
|
196
|
+
const Do = dur(4);
|
|
197
|
+
const step3 = [];
|
|
198
|
+
step3.push(addClip('Camera orbit', planCameraOrbit(ctx, center, Do, rng.next() < 0.5 ? 1 : -1), Do, 'Swing around and back'));
|
|
199
|
+
if (hero && animated.has(hero.id)) {
|
|
200
|
+
const accent = planAmbient(hero.capabilities.emissive ? 'glow' : 'breathe', hero, 1, ctx);
|
|
201
|
+
if (accent) {
|
|
202
|
+
const T = Math.min(Do, dur(1.6));
|
|
203
|
+
step3.push(addClip(`${hero.name} accent`, [track(hero.id, accent.path, accent.points.map(([f, v], i) => key(round((Do - T) / 2 + f * T), v, i < accent.points.length - 1 ? style.ambientEase : undefined)))], Do));
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
// Atmosphere: one continuous slow move across the steps (rest − δ → rest).
|
|
207
|
+
const lengths = [L1, L2, Do];
|
|
208
|
+
const total = lengths.reduce((a, b) => a + b, 0);
|
|
209
|
+
const envClip = (stepIndex) => {
|
|
210
|
+
const L = lengths[stepIndex];
|
|
211
|
+
if (!L || !envs.length)
|
|
212
|
+
return null;
|
|
213
|
+
const before = lengths.slice(0, stepIndex).reduce((a, b) => a + b, 0);
|
|
214
|
+
const tracks = envs.map(e => {
|
|
215
|
+
const rest = Number(ctx.ledger.get(e.state, e.path));
|
|
216
|
+
// rest here is where the previous slice left it; the full move ends at the authored value
|
|
217
|
+
const authoredV = Number(authored(e.state, e.path));
|
|
218
|
+
const start = Math.min(e.max, Math.max(e.min, authoredV - e.delta));
|
|
219
|
+
const at = (t) => round(start + (authoredV - start) * (t / total));
|
|
220
|
+
return track(e.state, e.path, [key(0, stepIndex === 0 ? at(0) : rest, 'linear'), key(L, at(before + L))]);
|
|
221
|
+
});
|
|
222
|
+
return addClip(`Atmosphere ${stepIndex + 1}`, tracks, L, 'Slow environment layer');
|
|
223
|
+
};
|
|
224
|
+
step1.push(envClip(0));
|
|
225
|
+
addStep(step1);
|
|
226
|
+
if (L2) {
|
|
227
|
+
step2.push(envClip(1));
|
|
228
|
+
addStep(step2);
|
|
229
|
+
}
|
|
230
|
+
step3.push(envClip(2));
|
|
231
|
+
addStep(step3);
|
|
232
|
+
}
|
|
233
|
+
const name = `Auto · ${options.template === 'ambient' ? 'Ambient loop' : 'Intro'} (${style.name})`;
|
|
234
|
+
const sequence = {
|
|
235
|
+
id: makeId('seq'),
|
|
236
|
+
name,
|
|
237
|
+
key: keyFor(name),
|
|
238
|
+
description: `Generated from the scene (seed ${seed}).`,
|
|
239
|
+
steps,
|
|
240
|
+
cues: [],
|
|
241
|
+
loop: options.template === 'ambient' && driver === 'time',
|
|
242
|
+
yoyo: false,
|
|
243
|
+
reverse: false,
|
|
244
|
+
endMode: 'hold',
|
|
245
|
+
driver,
|
|
246
|
+
driverSettings: { sensitivity: 1, smoothing: 0.25, invert: false },
|
|
247
|
+
autoplay: options.autoplay ?? false,
|
|
248
|
+
};
|
|
249
|
+
const issues = validateGenerated(clips, sequence, { driven: ctx.driven });
|
|
250
|
+
return { clips, sequence, profile, issues };
|
|
251
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import type { AnalyzeOptions, SceneInput } from './types';
|
|
2
|
+
export type PropKind = 'vec3' | 'number' | 'color' | 'boolean' | 'select' | 'string';
|
|
3
|
+
export interface PropContext {
|
|
4
|
+
path: string;
|
|
5
|
+
kind: PropKind;
|
|
6
|
+
/** Authored value. */
|
|
7
|
+
value?: unknown;
|
|
8
|
+
min?: number;
|
|
9
|
+
max?: number;
|
|
10
|
+
options?: string[];
|
|
11
|
+
}
|
|
12
|
+
export interface ElementContext {
|
|
13
|
+
tracks: PropContext[];
|
|
14
|
+
patch: PropContext[];
|
|
15
|
+
}
|
|
16
|
+
export interface ObjectContext extends ElementContext {
|
|
17
|
+
id: string;
|
|
18
|
+
name: string;
|
|
19
|
+
type: string;
|
|
20
|
+
/** config.type (light / particles / effect / grid kind, shape name). */
|
|
21
|
+
subType?: string;
|
|
22
|
+
role: string;
|
|
23
|
+
/** Has a real (saved) id — only saved objects can be referenced by animations. */
|
|
24
|
+
saved: boolean;
|
|
25
|
+
visible: boolean;
|
|
26
|
+
groupId?: string;
|
|
27
|
+
parentId?: string;
|
|
28
|
+
childIds?: string[];
|
|
29
|
+
/** Authored local transform. */
|
|
30
|
+
position: number[];
|
|
31
|
+
rotation: number[];
|
|
32
|
+
scale: number[];
|
|
33
|
+
/** World bounds. */
|
|
34
|
+
center: number[];
|
|
35
|
+
size: number[];
|
|
36
|
+
fadeable: boolean;
|
|
37
|
+
material?: {
|
|
38
|
+
type?: string;
|
|
39
|
+
variant?: string;
|
|
40
|
+
color?: string;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
export type EnvironmentKey = 'sky' | 'clouds' | 'ocean' | 'terrain';
|
|
44
|
+
export interface EnvironmentContext extends ElementContext {
|
|
45
|
+
/** The element exists in the scene (configured), on or off. */
|
|
46
|
+
present: boolean;
|
|
47
|
+
on: boolean;
|
|
48
|
+
/** deckType / waterType / terrainType. */
|
|
49
|
+
kind?: string;
|
|
50
|
+
preset?: string;
|
|
51
|
+
/** Ocean: 'world' | 'object'… */
|
|
52
|
+
host?: string;
|
|
53
|
+
/** Clouds: the high ice layer. */
|
|
54
|
+
cirrus?: {
|
|
55
|
+
on: boolean;
|
|
56
|
+
preset?: string;
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
export interface SceneAnalysis {
|
|
60
|
+
version: 2;
|
|
61
|
+
scene: {
|
|
62
|
+
scale: number;
|
|
63
|
+
ground: number;
|
|
64
|
+
center: number[];
|
|
65
|
+
size: number[];
|
|
66
|
+
heroId: string | null;
|
|
67
|
+
countsByType: Record<string, number>;
|
|
68
|
+
};
|
|
69
|
+
camera: ElementContext & {
|
|
70
|
+
position: number[];
|
|
71
|
+
target: number[];
|
|
72
|
+
fov: number;
|
|
73
|
+
distance: number;
|
|
74
|
+
/** Visible area on the ground plane (y = 0), 0 when no camera is mounted. */
|
|
75
|
+
viewport: {
|
|
76
|
+
visibleWidth: number;
|
|
77
|
+
visibleHeight: number;
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
environment: Record<EnvironmentKey, EnvironmentContext>;
|
|
81
|
+
background: {
|
|
82
|
+
hdri: string | null;
|
|
83
|
+
};
|
|
84
|
+
groups: Array<{
|
|
85
|
+
id: string;
|
|
86
|
+
kind: string;
|
|
87
|
+
axis: string;
|
|
88
|
+
order: string[];
|
|
89
|
+
radialOrder: string[];
|
|
90
|
+
}>;
|
|
91
|
+
objects: ObjectContext[];
|
|
92
|
+
}
|
|
93
|
+
export interface DescribeSceneInput extends SceneInput {
|
|
94
|
+
/** HDR background settings (`name` is the preset). */
|
|
95
|
+
hdr?: any;
|
|
96
|
+
}
|
|
97
|
+
export interface DescribeOptions extends AnalyzeOptions {
|
|
98
|
+
/** Visible area on the ground plane (viewer camera), when mounted. */
|
|
99
|
+
viewport?: {
|
|
100
|
+
visibleWidth: number;
|
|
101
|
+
visibleHeight: number;
|
|
102
|
+
} | null;
|
|
103
|
+
}
|
|
104
|
+
export declare function describeScene(input: DescribeSceneInput, options?: DescribeOptions): SceneAnalysis;
|
|
105
|
+
//# sourceMappingURL=describeScene.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"describeScene.d.ts","sourceRoot":"","sources":["../../src/autoAnimate/describeScene.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// describeScene — the ONE description of the scene every AI request carries.
|
|
3
|
+
// =============================================================================
|
|
4
|
+
//
|
|
5
|
+
// analyzeScene (layout, roles, groups, hero) + the property registry (every
|
|
6
|
+
// element's keyable tracks and settable properties, with current values and
|
|
7
|
+
// bounds) in one compact, rounded shape. The client sends it as the request's
|
|
8
|
+
// `scene`; the aiAgent mirrors these types (utils/v2/sceneAnalysis.ts — keep them
|
|
9
|
+
// in step).
|
|
10
|
+
//
|
|
11
|
+
// scene scale / ground / bounds / hero / counts — size every magnitude by `scale`
|
|
12
|
+
// camera pose, viewport at ground, keyable paths
|
|
13
|
+
// environment sky / clouds / ocean / terrain: present? on? kind / preset, and — when
|
|
14
|
+
// on — current values as tracks (keyable) + patch (action-only)
|
|
15
|
+
// background HDRI preset name
|
|
16
|
+
// groups cascade orders
|
|
17
|
+
// objects EVERY object (each consumer filters): role, saved, visible, hierarchy,
|
|
18
|
+
// transform, world bounds, material, and
|
|
19
|
+
// tracks what a clip can key (getTrackSpecs) with value / range
|
|
20
|
+
// patch config / environment properties only an instant change can set
|
|
21
|
+
//
|
|
22
|
+
// Pure like analyzeScene: live bounds and the viewport are injected by the caller.
|
|
23
|
+
// Model nodes are left out (node animation has its own path).
|
|
24
|
+
//
|
|
25
|
+
import { analyzeScene } from './sceneAnalyzer.js';
|
|
26
|
+
import { describeProperties, getTrackSpecs } from '../properties/registry.js';
|
|
27
|
+
// --- helpers --------------------------------------------------------------------
|
|
28
|
+
const r = (n) => {
|
|
29
|
+
const v = Number(n);
|
|
30
|
+
return Number.isFinite(v) ? Math.round(v * 1000) / 1000 : 0;
|
|
31
|
+
};
|
|
32
|
+
const rv = (v) => (v ?? []).map(r);
|
|
33
|
+
const roundValue = (v) => (Array.isArray(v) ? v.map(r) : typeof v === 'number' ? r(v) : v);
|
|
34
|
+
/** Registry kinds the AI can write; strings, vec2 and textures are not offered. */
|
|
35
|
+
const PROP_KINDS = new Set(['vec3', 'number', 'color', 'boolean', 'select']);
|
|
36
|
+
/** Families whose set-only properties go out as patch (transform / material / edges / shadow are not offered). */
|
|
37
|
+
const PATCH_DOMAINS = new Set(['light', 'particles', 'effect', 'grid', 'rain', 'clouds', 'config', 'sky', 'cirrus', 'ocean', 'terrain']);
|
|
38
|
+
const toProp = (p) => {
|
|
39
|
+
if (!PROP_KINDS.has(p.kind))
|
|
40
|
+
return null;
|
|
41
|
+
const out = { path: p.key, kind: p.kind, value: roundValue(p.value) };
|
|
42
|
+
if (p.min !== undefined)
|
|
43
|
+
out.min = p.min;
|
|
44
|
+
if (p.max !== undefined)
|
|
45
|
+
out.max = p.max;
|
|
46
|
+
if (p.options?.length)
|
|
47
|
+
out.options = p.options.map(o => o.value);
|
|
48
|
+
return out;
|
|
49
|
+
};
|
|
50
|
+
/** tracks = what a clip keys (with values and bounds); patch = the element's other settable properties. */
|
|
51
|
+
function elementProps(element, settings) {
|
|
52
|
+
const described = new Map(describeProperties(element, settings).map(p => [p.key, p]));
|
|
53
|
+
const trackPaths = getTrackSpecs(element, settings).map(s => s.key);
|
|
54
|
+
const keyed = new Set(trackPaths);
|
|
55
|
+
const tracks = [];
|
|
56
|
+
for (const path of trackPaths) {
|
|
57
|
+
const p = described.get(path);
|
|
58
|
+
const prop = p && toProp(p);
|
|
59
|
+
if (prop)
|
|
60
|
+
tracks.push(prop);
|
|
61
|
+
}
|
|
62
|
+
const patch = [];
|
|
63
|
+
for (const p of described.values()) {
|
|
64
|
+
if (keyed.has(p.key) || !PATCH_DOMAINS.has(p.domain))
|
|
65
|
+
continue;
|
|
66
|
+
const prop = toProp(p);
|
|
67
|
+
if (prop)
|
|
68
|
+
patch.push(prop);
|
|
69
|
+
}
|
|
70
|
+
return { tracks, patch };
|
|
71
|
+
}
|
|
72
|
+
/** One environment element: always its identity; values only when it is on (they are what an animation / change can touch). */
|
|
73
|
+
function environmentOf(key, settings, on) {
|
|
74
|
+
const cfg = key === 'sky' ? settings : settings?.config;
|
|
75
|
+
const present = key === 'sky' ? !!settings
|
|
76
|
+
: key === 'ocean' ? !!(cfg && typeof cfg === 'object' && cfg.waveModel)
|
|
77
|
+
: !!(cfg && typeof cfg === 'object');
|
|
78
|
+
const kind = key === 'clouds' ? cfg?.deckType : key === 'ocean' ? cfg?.waterType : key === 'terrain' ? cfg?.terrainType : undefined;
|
|
79
|
+
const values = on ? elementProps(key, settings) : { tracks: [], patch: [] };
|
|
80
|
+
return {
|
|
81
|
+
present,
|
|
82
|
+
on,
|
|
83
|
+
...(kind ? { kind } : {}),
|
|
84
|
+
...(cfg?.preset ? { preset: cfg.preset } : {}),
|
|
85
|
+
...(key === 'ocean' && cfg?.host ? { host: cfg.host } : {}),
|
|
86
|
+
...(key === 'clouds' && settings?.cirrus ? { cirrus: { on: settings.cirrus.enabled !== false, ...(settings.cirrus.preset ? { preset: settings.cirrus.preset } : {}) } } : {}),
|
|
87
|
+
...values,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function materialOf(obj) {
|
|
91
|
+
const ms = obj?.materialSettings;
|
|
92
|
+
if (!ms?.materialType && !ms?.color)
|
|
93
|
+
return undefined;
|
|
94
|
+
return {
|
|
95
|
+
...(ms.materialType ? { type: ms.materialType } : {}),
|
|
96
|
+
...(ms.materialVariant ? { variant: ms.materialVariant } : {}),
|
|
97
|
+
...(typeof ms.color === 'string' ? { color: ms.color } : {}),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
// --- main ---------------------------------------------------------------------------
|
|
101
|
+
export function describeScene(input, options = {}) {
|
|
102
|
+
// Live bounds, no model nodes.
|
|
103
|
+
const profile = analyzeScene(input, { measure: options.measure });
|
|
104
|
+
const byId = new Map((input.objects ?? []).filter((o) => o && typeof o.id === 'string').map((o) => [o.id, o]));
|
|
105
|
+
const objects = [];
|
|
106
|
+
for (const p of profile.objects) {
|
|
107
|
+
const obj = byId.get(p.id);
|
|
108
|
+
if (!obj)
|
|
109
|
+
continue;
|
|
110
|
+
const material = materialOf(obj);
|
|
111
|
+
objects.push({
|
|
112
|
+
id: p.id,
|
|
113
|
+
name: p.name,
|
|
114
|
+
type: p.type,
|
|
115
|
+
...(obj.config?.type ? { subType: obj.config.type } : {}),
|
|
116
|
+
role: p.role,
|
|
117
|
+
saved: p.saved,
|
|
118
|
+
visible: p.visible,
|
|
119
|
+
...(p.groupId ? { groupId: p.groupId } : {}),
|
|
120
|
+
...(p.parentId ? { parentId: p.parentId } : {}),
|
|
121
|
+
...(p.childIds.length ? { childIds: p.childIds } : {}),
|
|
122
|
+
position: rv(p.position),
|
|
123
|
+
rotation: rv(p.rotation),
|
|
124
|
+
scale: rv(p.scale),
|
|
125
|
+
center: rv(p.bounds.center),
|
|
126
|
+
size: rv(p.bounds.size),
|
|
127
|
+
fadeable: p.capabilities.fadeable,
|
|
128
|
+
...(material ? { material } : {}),
|
|
129
|
+
...elementProps('object', obj),
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
const countsByType = {};
|
|
133
|
+
Object.entries(profile.countsByType).forEach(([k, v]) => { if (v)
|
|
134
|
+
countsByType[k] = v; });
|
|
135
|
+
const vp = options.viewport;
|
|
136
|
+
return {
|
|
137
|
+
version: 2,
|
|
138
|
+
scene: {
|
|
139
|
+
scale: r(profile.scale),
|
|
140
|
+
ground: r(profile.ground),
|
|
141
|
+
center: rv(profile.bounds.center),
|
|
142
|
+
size: rv(profile.bounds.size),
|
|
143
|
+
heroId: profile.heroId,
|
|
144
|
+
countsByType,
|
|
145
|
+
},
|
|
146
|
+
camera: {
|
|
147
|
+
position: rv(profile.camera.position),
|
|
148
|
+
target: rv(profile.camera.target),
|
|
149
|
+
fov: r(profile.camera.fov),
|
|
150
|
+
distance: r(profile.camera.distance),
|
|
151
|
+
viewport: vp ? { visibleWidth: r(vp.visibleWidth), visibleHeight: r(vp.visibleHeight) } : { visibleWidth: 0, visibleHeight: 0 },
|
|
152
|
+
tracks: elementProps('camera', input.camera ?? {}).tracks,
|
|
153
|
+
patch: [],
|
|
154
|
+
},
|
|
155
|
+
environment: {
|
|
156
|
+
sky: environmentOf('sky', input.sky, profile.environment.sky),
|
|
157
|
+
clouds: environmentOf('clouds', input.clouds, profile.environment.clouds),
|
|
158
|
+
ocean: environmentOf('ocean', input.ocean, profile.environment.ocean),
|
|
159
|
+
terrain: environmentOf('terrain', input.terrain, profile.environment.terrain),
|
|
160
|
+
},
|
|
161
|
+
background: { hdri: input.hdr?.name ?? null },
|
|
162
|
+
groups: profile.groups.map(g => ({ id: g.id, kind: g.kind, axis: g.axis, order: g.order, radialOrder: g.radialOrder })),
|
|
163
|
+
objects,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { V3 } from './types';
|
|
2
|
+
export type DraftEase = 'linear' | 'step' | 'easeInQuad' | 'easeOutQuad' | 'easeInOutQuad' | 'easeInCubic' | 'easeOutCubic' | 'easeInOutCubic' | 'easeInQuart' | 'easeOutQuart' | 'easeInOutQuart' | 'easeInSine' | 'easeOutSine' | 'easeInOutSine' | 'easeInExpo' | 'easeOutExpo' | 'easeInOutExpo' | 'easeInBack' | 'easeOutBack' | 'easeInOutBack' | 'easeOutElastic' | 'easeOutBounce';
|
|
3
|
+
export type DraftValue = number | V3 | number[] | string | boolean;
|
|
4
|
+
export interface DraftKey {
|
|
5
|
+
id: string;
|
|
6
|
+
t: number;
|
|
7
|
+
value: DraftValue;
|
|
8
|
+
ease?: DraftEase;
|
|
9
|
+
}
|
|
10
|
+
export interface DraftTrack {
|
|
11
|
+
id: string;
|
|
12
|
+
state: string;
|
|
13
|
+
node?: string;
|
|
14
|
+
path: string;
|
|
15
|
+
keys: DraftKey[];
|
|
16
|
+
}
|
|
17
|
+
export interface DraftClip {
|
|
18
|
+
id: string;
|
|
19
|
+
name: string;
|
|
20
|
+
key: string;
|
|
21
|
+
description?: string;
|
|
22
|
+
duration: number;
|
|
23
|
+
fps: number;
|
|
24
|
+
loop: boolean;
|
|
25
|
+
yoyo: boolean;
|
|
26
|
+
endMode: 'hold' | 'restore';
|
|
27
|
+
tracks: DraftTrack[];
|
|
28
|
+
}
|
|
29
|
+
export interface DraftStepClip {
|
|
30
|
+
id: string;
|
|
31
|
+
clipId: string;
|
|
32
|
+
delay: number;
|
|
33
|
+
speed: number;
|
|
34
|
+
}
|
|
35
|
+
export interface DraftSequence {
|
|
36
|
+
id: string;
|
|
37
|
+
name: string;
|
|
38
|
+
key: string;
|
|
39
|
+
description?: string;
|
|
40
|
+
steps: Array<{
|
|
41
|
+
id: string;
|
|
42
|
+
clips: DraftStepClip[];
|
|
43
|
+
}>;
|
|
44
|
+
cues: [];
|
|
45
|
+
loop: boolean;
|
|
46
|
+
yoyo: boolean;
|
|
47
|
+
reverse: boolean;
|
|
48
|
+
endMode: 'hold' | 'restore';
|
|
49
|
+
driver: 'time' | 'mouseWheel' | 'mouseX' | 'mouseY';
|
|
50
|
+
driverSettings: {
|
|
51
|
+
sensitivity: number;
|
|
52
|
+
smoothing: number;
|
|
53
|
+
invert: boolean;
|
|
54
|
+
};
|
|
55
|
+
autoplay: boolean;
|
|
56
|
+
}
|
|
57
|
+
/** Eases that feel wrong when an input scrubs them backwards (wheel / mouse drivers). */
|
|
58
|
+
export declare const OVERSHOOT_EASES: Set<DraftEase>;
|
|
59
|
+
//# sourceMappingURL=draft.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"draft.d.ts","sourceRoot":"","sources":["../../src/autoAnimate/draft.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAElC,MAAM,MAAM,SAAS,GACf,QAAQ,GAAG,MAAM,GACjB,YAAY,GAAG,aAAa,GAAG,eAAe,GAC9C,aAAa,GAAG,cAAc,GAAG,gBAAgB,GACjD,aAAa,GAAG,cAAc,GAAG,gBAAgB,GACjD,YAAY,GAAG,aAAa,GAAG,eAAe,GAC9C,YAAY,GAAG,aAAa,GAAG,eAAe,GAC9C,YAAY,GAAG,aAAa,GAAG,eAAe,GAC9C,gBAAgB,GAAG,eAAe,CAAC;AAEzC,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,EAAE,GAAG,MAAM,EAAE,GAAG,MAAM,GAAG,OAAO,CAAC;AAEnE,MAAM,WAAW,QAAQ;IAAG,EAAE,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,UAAU,CAAC;IAAC,IAAI,CAAC,EAAE,SAAS,CAAA;CAAE;AACxF,MAAM,WAAW,UAAU;IAAG,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,QAAQ,EAAE,CAAA;CAAE;AACxG,MAAM,WAAW,SAAS;IACtB,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC5D,QAAQ,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IACzF,MAAM,EAAE,UAAU,EAAE,CAAC;CACxB;AACD,MAAM,WAAW,aAAa;IAAG,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE;AAC3F,MAAM,WAAW,aAAa;IAC1B,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC5D,KAAK,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,aAAa,EAAE,CAAA;KAAE,CAAC,CAAC;IACrD,IAAI,EAAE,EAAE,CAAC;IACT,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5E,MAAM,EAAE,MAAM,GAAG,YAAY,GAAG,QAAQ,GAAG,QAAQ,CAAC;IACpD,cAAc,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC;IAC5E,QAAQ,EAAE,OAAO,CAAC;CACrB;AAED,yFAAyF;AACzF,eAAO,MAAM,eAAe,gBAAwG,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export * from './types';
|
|
2
|
+
export { analyzeScene, baseName } from './sceneAnalyzer';
|
|
3
|
+
export { makeBounds, boundsAround, unionBounds } from './bounds';
|
|
4
|
+
export { generateAnimation } from './composer';
|
|
5
|
+
export type { GenerateOptions, GenerateResult, AutoTemplate, LengthName } from './composer';
|
|
6
|
+
export { validateGenerated } from './validator';
|
|
7
|
+
export type { GenerationIssue } from './validator';
|
|
8
|
+
export { STYLES } from './recipes';
|
|
9
|
+
export type { StyleName, EnterName, AmbientName } from './recipes';
|
|
10
|
+
export type { DraftClip, DraftSequence, DraftTrack, DraftKey, DraftEase, DraftValue } from './draft';
|
|
11
|
+
export { createRng } from './rng';
|
|
12
|
+
export { describeScene } from './describeScene';
|
|
13
|
+
export type { SceneAnalysis, DescribeSceneInput, DescribeOptions, PropKind, PropContext, ElementContext, ObjectContext, EnvironmentKey, EnvironmentContext, } from './describeScene';
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/autoAnimate/index.ts"],"names":[],"mappings":"AACA,cAAc,SAAS,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC5F,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACnE,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrG,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,YAAY,EACR,aAAa,EAAE,kBAAkB,EAAE,eAAe,EAAE,QAAQ,EAAE,WAAW,EAAE,cAAc,EACzF,aAAa,EAAE,cAAc,EAAE,kBAAkB,GACpD,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Auto-animate: scene analysis (→ recipes → composer → validator). See claude/auto-animate-plan.md.
|
|
2
|
+
export * from './types.js';
|
|
3
|
+
export { analyzeScene, baseName } from './sceneAnalyzer.js';
|
|
4
|
+
export { makeBounds, boundsAround, unionBounds } from './bounds.js';
|
|
5
|
+
export { generateAnimation } from './composer.js';
|
|
6
|
+
export { validateGenerated } from './validator.js';
|
|
7
|
+
export { STYLES } from './recipes.js';
|
|
8
|
+
export { createRng } from './rng.js';
|
|
9
|
+
export { describeScene } from './describeScene.js';
|