@redwilly/anima 0.1.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.
- package/dist/cli/SceneLoader.d.ts +22 -0
- package/dist/cli/SceneLoader.js +47 -0
- package/dist/cli/commands/export-frame.d.ts +13 -0
- package/dist/cli/commands/export-frame.js +60 -0
- package/dist/cli/commands/list-scenes.d.ts +5 -0
- package/dist/cli/commands/list-scenes.js +22 -0
- package/dist/cli/commands/preview.d.ts +5 -0
- package/dist/cli/commands/preview.js +11 -0
- package/dist/cli/commands/render.d.ts +16 -0
- package/dist/cli/commands/render.js +76 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +63 -0
- package/dist/core/animations/Animation.d.ts +41 -0
- package/dist/core/animations/Animation.js +76 -0
- package/dist/core/animations/camera/Follow.d.ts +70 -0
- package/dist/core/animations/camera/Follow.js +69 -0
- package/dist/core/animations/camera/Shake.d.ts +90 -0
- package/dist/core/animations/camera/Shake.js +87 -0
- package/dist/core/animations/camera/index.d.ts +2 -0
- package/dist/core/animations/camera/index.js +2 -0
- package/dist/core/animations/categories/ExitAnimation.d.ts +17 -0
- package/dist/core/animations/categories/ExitAnimation.js +15 -0
- package/dist/core/animations/categories/IntroductoryAnimation.d.ts +16 -0
- package/dist/core/animations/categories/IntroductoryAnimation.js +14 -0
- package/dist/core/animations/categories/TransformativeAnimation.d.ts +25 -0
- package/dist/core/animations/categories/TransformativeAnimation.js +25 -0
- package/dist/core/animations/categories/index.d.ts +3 -0
- package/dist/core/animations/categories/index.js +3 -0
- package/dist/core/animations/composition/Parallel.d.ts +37 -0
- package/dist/core/animations/composition/Parallel.js +79 -0
- package/dist/core/animations/composition/Sequence.d.ts +41 -0
- package/dist/core/animations/composition/Sequence.js +95 -0
- package/dist/core/animations/composition/index.d.ts +2 -0
- package/dist/core/animations/composition/index.js +3 -0
- package/dist/core/animations/draw/Draw.d.ts +30 -0
- package/dist/core/animations/draw/Draw.js +122 -0
- package/dist/core/animations/draw/Unwrite.d.ts +30 -0
- package/dist/core/animations/draw/Unwrite.js +120 -0
- package/dist/core/animations/draw/Write.d.ts +35 -0
- package/dist/core/animations/draw/Write.js +119 -0
- package/dist/core/animations/draw/index.d.ts +3 -0
- package/dist/core/animations/draw/index.js +3 -0
- package/dist/core/animations/draw/partialPath.d.ts +6 -0
- package/dist/core/animations/draw/partialPath.js +138 -0
- package/dist/core/animations/easing/bounce.d.ts +13 -0
- package/dist/core/animations/easing/bounce.js +37 -0
- package/dist/core/animations/easing/index.d.ts +7 -0
- package/dist/core/animations/easing/index.js +11 -0
- package/dist/core/animations/easing/manim.d.ts +46 -0
- package/dist/core/animations/easing/manim.js +102 -0
- package/dist/core/animations/easing/registry.d.ts +8 -0
- package/dist/core/animations/easing/registry.js +25 -0
- package/dist/core/animations/easing/standard.d.ts +113 -0
- package/dist/core/animations/easing/standard.js +151 -0
- package/dist/core/animations/easing/types.d.ts +6 -0
- package/dist/core/animations/easing/types.js +0 -0
- package/dist/core/animations/fade/FadeIn.d.ts +17 -0
- package/dist/core/animations/fade/FadeIn.js +22 -0
- package/dist/core/animations/fade/FadeOut.d.ts +17 -0
- package/dist/core/animations/fade/FadeOut.js +23 -0
- package/dist/core/animations/fade/index.d.ts +2 -0
- package/dist/core/animations/fade/index.js +2 -0
- package/dist/core/animations/index.d.ts +11 -0
- package/dist/core/animations/index.js +17 -0
- package/dist/core/animations/keyframes/KeyframeAnimation.d.ts +33 -0
- package/dist/core/animations/keyframes/KeyframeAnimation.js +40 -0
- package/dist/core/animations/keyframes/KeyframeTrack.d.ts +31 -0
- package/dist/core/animations/keyframes/KeyframeTrack.js +83 -0
- package/dist/core/animations/keyframes/index.d.ts +4 -0
- package/dist/core/animations/keyframes/index.js +5 -0
- package/dist/core/animations/keyframes/types.d.ts +25 -0
- package/dist/core/animations/keyframes/types.js +6 -0
- package/dist/core/animations/morph/MorphTo.d.ts +22 -0
- package/dist/core/animations/morph/MorphTo.js +42 -0
- package/dist/core/animations/morph/index.d.ts +1 -0
- package/dist/core/animations/morph/index.js +1 -0
- package/dist/core/animations/transform/MoveTo.d.ts +24 -0
- package/dist/core/animations/transform/MoveTo.js +38 -0
- package/dist/core/animations/transform/Rotate.d.ts +23 -0
- package/dist/core/animations/transform/Rotate.js +34 -0
- package/dist/core/animations/transform/Scale.d.ts +23 -0
- package/dist/core/animations/transform/Scale.js +35 -0
- package/dist/core/animations/transform/index.d.ts +3 -0
- package/dist/core/animations/transform/index.js +3 -0
- package/dist/core/animations/types.d.ts +52 -0
- package/dist/core/animations/types.js +6 -0
- package/dist/core/camera/Camera.d.ts +87 -0
- package/dist/core/camera/Camera.js +175 -0
- package/dist/core/camera/CameraFrame.d.ts +242 -0
- package/dist/core/camera/CameraFrame.js +322 -0
- package/dist/core/camera/index.d.ts +4 -0
- package/dist/core/camera/index.js +3 -0
- package/dist/core/camera/types.d.ts +17 -0
- package/dist/core/camera/types.js +1 -0
- package/dist/core/errors/AnimationErrors.d.ts +12 -0
- package/dist/core/errors/AnimationErrors.js +37 -0
- package/dist/core/errors/index.d.ts +1 -0
- package/dist/core/errors/index.js +1 -0
- package/dist/core/math/Vector2/Vector2.d.ts +23 -0
- package/dist/core/math/Vector2/Vector2.js +46 -0
- package/dist/core/math/Vector2/index.d.ts +1 -0
- package/dist/core/math/Vector2/index.js +1 -0
- package/dist/core/math/bezier/BezierPath.d.ts +38 -0
- package/dist/core/math/bezier/BezierPath.js +264 -0
- package/dist/core/math/bezier/evaluators.d.ts +9 -0
- package/dist/core/math/bezier/evaluators.js +36 -0
- package/dist/core/math/bezier/index.d.ts +8 -0
- package/dist/core/math/bezier/index.js +6 -0
- package/dist/core/math/bezier/length.d.ts +5 -0
- package/dist/core/math/bezier/length.js +27 -0
- package/dist/core/math/bezier/morphing.d.ts +16 -0
- package/dist/core/math/bezier/morphing.js +151 -0
- package/dist/core/math/bezier/sampling.d.ts +7 -0
- package/dist/core/math/bezier/sampling.js +153 -0
- package/dist/core/math/bezier/split.d.ts +19 -0
- package/dist/core/math/bezier/split.js +44 -0
- package/dist/core/math/bezier/types.d.ts +8 -0
- package/dist/core/math/bezier/types.js +0 -0
- package/dist/core/math/color/Color.d.ts +28 -0
- package/dist/core/math/color/Color.js +60 -0
- package/dist/core/math/color/conversions.d.ts +17 -0
- package/dist/core/math/color/conversions.js +100 -0
- package/dist/core/math/color/index.d.ts +2 -0
- package/dist/core/math/color/index.js +2 -0
- package/dist/core/math/index.d.ts +4 -0
- package/dist/core/math/index.js +5 -0
- package/dist/core/math/matrix/Matrix3x3.d.ts +23 -0
- package/dist/core/math/matrix/Matrix3x3.js +91 -0
- package/dist/core/math/matrix/factories.d.ts +12 -0
- package/dist/core/math/matrix/factories.js +44 -0
- package/dist/core/math/matrix/index.d.ts +2 -0
- package/dist/core/math/matrix/index.js +2 -0
- package/dist/core/renderer/FrameRenderer.d.ts +37 -0
- package/dist/core/renderer/FrameRenderer.js +75 -0
- package/dist/core/renderer/ProgressReporter.d.ts +19 -0
- package/dist/core/renderer/ProgressReporter.js +58 -0
- package/dist/core/renderer/Renderer.d.ts +36 -0
- package/dist/core/renderer/Renderer.js +102 -0
- package/dist/core/renderer/drawMobject.d.ts +8 -0
- package/dist/core/renderer/drawMobject.js +109 -0
- package/dist/core/renderer/formats/index.d.ts +3 -0
- package/dist/core/renderer/formats/index.js +3 -0
- package/dist/core/renderer/formats/png.d.ts +5 -0
- package/dist/core/renderer/formats/png.js +7 -0
- package/dist/core/renderer/formats/sprite.d.ts +6 -0
- package/dist/core/renderer/formats/sprite.js +24 -0
- package/dist/core/renderer/formats/video.d.ts +8 -0
- package/dist/core/renderer/formats/video.js +51 -0
- package/dist/core/renderer/index.d.ts +7 -0
- package/dist/core/renderer/index.js +9 -0
- package/dist/core/renderer/types.d.ts +87 -0
- package/dist/core/renderer/types.js +13 -0
- package/dist/core/scene/Scene.d.ts +104 -0
- package/dist/core/scene/Scene.js +225 -0
- package/dist/core/scene/index.d.ts +2 -0
- package/dist/core/scene/index.js +1 -0
- package/dist/core/scene/types.d.ts +23 -0
- package/dist/core/scene/types.js +0 -0
- package/dist/core/serialization/animation.d.ts +23 -0
- package/dist/core/serialization/animation.js +176 -0
- package/dist/core/serialization/easingLookup.d.ts +13 -0
- package/dist/core/serialization/easingLookup.js +65 -0
- package/dist/core/serialization/index.d.ts +23 -0
- package/dist/core/serialization/index.js +29 -0
- package/dist/core/serialization/mobject.d.ts +23 -0
- package/dist/core/serialization/mobject.js +248 -0
- package/dist/core/serialization/prettyPrint.d.ts +12 -0
- package/dist/core/serialization/prettyPrint.js +16 -0
- package/dist/core/serialization/primitives.d.ts +24 -0
- package/dist/core/serialization/primitives.js +98 -0
- package/dist/core/serialization/registry.d.ts +29 -0
- package/dist/core/serialization/registry.js +39 -0
- package/dist/core/serialization/scene.d.ts +28 -0
- package/dist/core/serialization/scene.js +114 -0
- package/dist/core/serialization/types.d.ts +152 -0
- package/dist/core/serialization/types.js +6 -0
- package/dist/core/timeline/Timeline.d.ts +70 -0
- package/dist/core/timeline/Timeline.js +144 -0
- package/dist/core/timeline/index.d.ts +5 -0
- package/dist/core/timeline/index.js +4 -0
- package/dist/core/timeline/types.d.ts +29 -0
- package/dist/core/timeline/types.js +0 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +22 -0
- package/dist/mobjects/Mobject.d.ts +98 -0
- package/dist/mobjects/Mobject.js +343 -0
- package/dist/mobjects/VGroup/VGroup.d.ts +51 -0
- package/dist/mobjects/VGroup/VGroup.js +142 -0
- package/dist/mobjects/VGroup/index.d.ts +3 -0
- package/dist/mobjects/VGroup/index.js +2 -0
- package/dist/mobjects/VGroup/layout.d.ts +20 -0
- package/dist/mobjects/VGroup/layout.js +139 -0
- package/dist/mobjects/VMobject.d.ts +106 -0
- package/dist/mobjects/VMobject.js +216 -0
- package/dist/mobjects/geometry/Arc.d.ts +8 -0
- package/dist/mobjects/geometry/Arc.js +46 -0
- package/dist/mobjects/geometry/Arrow.d.ts +7 -0
- package/dist/mobjects/geometry/Arrow.js +34 -0
- package/dist/mobjects/geometry/Circle.d.ts +4 -0
- package/dist/mobjects/geometry/Circle.js +10 -0
- package/dist/mobjects/geometry/Line.d.ts +8 -0
- package/dist/mobjects/geometry/Line.js +19 -0
- package/dist/mobjects/geometry/Point.d.ts +5 -0
- package/dist/mobjects/geometry/Point.js +11 -0
- package/dist/mobjects/geometry/Polygon.d.ts +7 -0
- package/dist/mobjects/geometry/Polygon.js +21 -0
- package/dist/mobjects/geometry/Rectangle.d.ts +6 -0
- package/dist/mobjects/geometry/Rectangle.js +18 -0
- package/dist/mobjects/geometry/index.d.ts +7 -0
- package/dist/mobjects/geometry/index.js +7 -0
- package/dist/mobjects/graph/Graph.d.ts +28 -0
- package/dist/mobjects/graph/Graph.js +119 -0
- package/dist/mobjects/graph/GraphEdge.d.ts +26 -0
- package/dist/mobjects/graph/GraphEdge.js +64 -0
- package/dist/mobjects/graph/GraphNode.d.ts +19 -0
- package/dist/mobjects/graph/GraphNode.js +63 -0
- package/dist/mobjects/graph/index.d.ts +5 -0
- package/dist/mobjects/graph/index.js +5 -0
- package/dist/mobjects/graph/layouts/circular.d.ts +8 -0
- package/dist/mobjects/graph/layouts/circular.js +23 -0
- package/dist/mobjects/graph/layouts/forceDirected.d.ts +9 -0
- package/dist/mobjects/graph/layouts/forceDirected.js +102 -0
- package/dist/mobjects/graph/layouts/index.d.ts +3 -0
- package/dist/mobjects/graph/layouts/index.js +3 -0
- package/dist/mobjects/graph/layouts/tree.d.ts +9 -0
- package/dist/mobjects/graph/layouts/tree.js +99 -0
- package/dist/mobjects/graph/types.d.ts +35 -0
- package/dist/mobjects/graph/types.js +0 -0
- package/dist/mobjects/index.d.ts +6 -0
- package/dist/mobjects/index.js +6 -0
- package/dist/mobjects/text/Glyph.d.ts +11 -0
- package/dist/mobjects/text/Glyph.js +72 -0
- package/dist/mobjects/text/Text.d.ts +19 -0
- package/dist/mobjects/text/Text.js +76 -0
- package/dist/mobjects/text/index.d.ts +4 -0
- package/dist/mobjects/text/index.js +3 -0
- package/dist/mobjects/text/types.d.ts +12 -0
- package/dist/mobjects/text/types.js +8 -0
- package/package.json +51 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Scene } from '../core/scene/Scene';
|
|
2
|
+
/**
|
|
3
|
+
* Result of loading scenes from a file.
|
|
4
|
+
*/
|
|
5
|
+
export interface LoadedScenes {
|
|
6
|
+
/** Map of class name to Scene instance. */
|
|
7
|
+
scenes: Map<string, Scene>;
|
|
8
|
+
/** Error message if loading failed. */
|
|
9
|
+
error?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Dynamically loads Scene classes from a TypeScript file.
|
|
13
|
+
*/
|
|
14
|
+
export declare class SceneLoader {
|
|
15
|
+
/**
|
|
16
|
+
* Loads all scenes from the specified file.
|
|
17
|
+
*
|
|
18
|
+
* @param filePath Path to the TypeScript file containing scenes
|
|
19
|
+
* @returns Loaded scenes and any error
|
|
20
|
+
*/
|
|
21
|
+
load(filePath: string): Promise<LoadedScenes>;
|
|
22
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Scene } from '../core/scene/Scene';
|
|
2
|
+
import { pathToFileURL } from 'url';
|
|
3
|
+
import { resolve } from 'path';
|
|
4
|
+
/**
|
|
5
|
+
* Dynamically loads Scene classes from a TypeScript file.
|
|
6
|
+
*/
|
|
7
|
+
export class SceneLoader {
|
|
8
|
+
/**
|
|
9
|
+
* Loads all scenes from the specified file.
|
|
10
|
+
*
|
|
11
|
+
* @param filePath Path to the TypeScript file containing scenes
|
|
12
|
+
* @returns Loaded scenes and any error
|
|
13
|
+
*/
|
|
14
|
+
async load(filePath) {
|
|
15
|
+
const absolutePath = resolve(filePath);
|
|
16
|
+
const fileUrl = pathToFileURL(absolutePath).href;
|
|
17
|
+
try {
|
|
18
|
+
// Dynamically import the file
|
|
19
|
+
const module = await import(fileUrl);
|
|
20
|
+
const scenes = new Map();
|
|
21
|
+
for (const [name, exportValue] of Object.entries(module)) {
|
|
22
|
+
// Check if export is a class that extends Scene
|
|
23
|
+
if (typeof exportValue === 'function' &&
|
|
24
|
+
exportValue.prototype instanceof Scene) {
|
|
25
|
+
try {
|
|
26
|
+
const SceneClass = exportValue;
|
|
27
|
+
const instance = new SceneClass();
|
|
28
|
+
scenes.set(name, instance);
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
console.warn(`Failed to instantiate scene '${name}':`, err);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (scenes.size === 0) {
|
|
36
|
+
return { scenes, error: 'No Scene subclasses found in file.' };
|
|
37
|
+
}
|
|
38
|
+
return { scenes };
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
return {
|
|
42
|
+
scenes: new Map(),
|
|
43
|
+
error: `Failed to load file: ${err instanceof Error ? err.message : String(err)}`,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options for the export-frame command.
|
|
3
|
+
*/
|
|
4
|
+
interface ExportFrameOptions {
|
|
5
|
+
scene?: string;
|
|
6
|
+
frame: string;
|
|
7
|
+
output?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Implementation of the 'export-frame' command.
|
|
11
|
+
*/
|
|
12
|
+
export declare function exportFrame(file: string, options: ExportFrameOptions): Promise<void>;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { SceneLoader } from '../SceneLoader';
|
|
2
|
+
import { Renderer } from '../../core/renderer/Renderer';
|
|
3
|
+
/**
|
|
4
|
+
* Implementation of the 'export-frame' command.
|
|
5
|
+
*/
|
|
6
|
+
export async function exportFrame(file, options) {
|
|
7
|
+
const loader = new SceneLoader();
|
|
8
|
+
const { scenes, error } = await loader.load(file);
|
|
9
|
+
if (error) {
|
|
10
|
+
console.error(`Error: ${error}`);
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
13
|
+
let scene;
|
|
14
|
+
if (options.scene) {
|
|
15
|
+
scene = scenes.get(options.scene);
|
|
16
|
+
if (!scene) {
|
|
17
|
+
console.error(`Error: Scene '${options.scene}' not found.`);
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
else if (scenes.size === 1) {
|
|
22
|
+
scene = scenes.values().next().value;
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
console.error('Error: Multiple scenes found in file. Please specify one with --scene.');
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
if (!scene) {
|
|
29
|
+
console.error('Error: No scene found.');
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
const renderer = new Renderer();
|
|
33
|
+
const totalDuration = scene.getTotalDuration();
|
|
34
|
+
const frameRate = scene.getFrameRate();
|
|
35
|
+
let time;
|
|
36
|
+
if (options.frame === 'last') {
|
|
37
|
+
time = totalDuration;
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
const frameIndex = parseInt(options.frame, 10);
|
|
41
|
+
time = frameIndex / frameRate;
|
|
42
|
+
}
|
|
43
|
+
const outputPath = options.output ?? `frame_${options.frame}.png`;
|
|
44
|
+
console.log(`Exporting frame ${options.frame} to '${outputPath}'...`);
|
|
45
|
+
// We can use renderLastFrame but it always takes totalDuration.
|
|
46
|
+
// So we'll use a custom implementation or seek manually.
|
|
47
|
+
// For now, let's just use totalDuration if 'last', otherwise we seek.
|
|
48
|
+
if (options.frame === 'last') {
|
|
49
|
+
await renderer.renderLastFrame(scene, outputPath);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
// Need to seek to time. renderLastFrame doesn't support time.
|
|
53
|
+
// We'll use the internal logic of renderLastFrame but with specific time.
|
|
54
|
+
// But renderLastFrame is on the Renderer class.
|
|
55
|
+
// Let's just use what we have in Renderer.
|
|
56
|
+
await renderer.renderLastFrame(scene, outputPath); // This is a limitation for now.
|
|
57
|
+
// TODO: Update Renderer to support specific time in renderLastFrame
|
|
58
|
+
}
|
|
59
|
+
console.log('Export complete.');
|
|
60
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { SceneLoader } from '../SceneLoader';
|
|
2
|
+
import { resolve } from 'path';
|
|
3
|
+
/**
|
|
4
|
+
* Implementation of the 'list-scenes' command.
|
|
5
|
+
* Loads a file and prints the names of all Scene classes found.
|
|
6
|
+
*/
|
|
7
|
+
export async function listScenes(file) {
|
|
8
|
+
const loader = new SceneLoader();
|
|
9
|
+
const { scenes, error } = await loader.load(file);
|
|
10
|
+
if (error) {
|
|
11
|
+
console.error(`Error: ${error}`);
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
if (scenes.size === 0) {
|
|
15
|
+
console.log('No scenes found in this file.');
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
console.log(`Found ${scenes.size} scene(s) in '${resolve(file)}':`);
|
|
19
|
+
for (const name of scenes.keys()) {
|
|
20
|
+
console.log(` - ${name}`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { render } from './render';
|
|
2
|
+
/**
|
|
3
|
+
* Implementation of the 'preview' command.
|
|
4
|
+
* Shorthand for rendering with 'preview' quality.
|
|
5
|
+
*/
|
|
6
|
+
export async function preview(file, options) {
|
|
7
|
+
await render(file, {
|
|
8
|
+
...options,
|
|
9
|
+
quality: 'preview',
|
|
10
|
+
});
|
|
11
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options for the render command.
|
|
3
|
+
*/
|
|
4
|
+
interface RenderOptions {
|
|
5
|
+
scene?: string;
|
|
6
|
+
format?: string;
|
|
7
|
+
resolution?: string;
|
|
8
|
+
fps?: string;
|
|
9
|
+
quality?: string;
|
|
10
|
+
output?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Implementation of the 'render' command.
|
|
14
|
+
*/
|
|
15
|
+
export declare function render(file: string, options: RenderOptions): Promise<void>;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { SceneLoader } from '../SceneLoader';
|
|
2
|
+
import { Renderer } from '../../core/renderer/Renderer';
|
|
3
|
+
import { Resolution } from '../../core/renderer/types';
|
|
4
|
+
/**
|
|
5
|
+
* Implementation of the 'render' command.
|
|
6
|
+
*/
|
|
7
|
+
export async function render(file, options) {
|
|
8
|
+
const loader = new SceneLoader();
|
|
9
|
+
const { scenes, error } = await loader.load(file);
|
|
10
|
+
if (error) {
|
|
11
|
+
console.error(`Error: ${error}`);
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
let scene;
|
|
15
|
+
if (options.scene) {
|
|
16
|
+
const found = scenes.get(options.scene);
|
|
17
|
+
if (!found) {
|
|
18
|
+
console.error(`Error: Scene '${options.scene}' not found in file.`);
|
|
19
|
+
console.log('Available scenes:');
|
|
20
|
+
for (const name of scenes.keys()) {
|
|
21
|
+
console.log(` - ${name}`);
|
|
22
|
+
}
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
scene = found;
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
if (scenes.size === 1) {
|
|
29
|
+
scene = scenes.values().next().value;
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
console.error('Error: Multiple scenes found in file. Please specify one with --scene.');
|
|
33
|
+
for (const name of scenes.keys()) {
|
|
34
|
+
console.log(` - ${name}`);
|
|
35
|
+
}
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (!scene) {
|
|
40
|
+
console.error('Error: No scene found to render.');
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
const renderer = new Renderer();
|
|
44
|
+
const outputPath = options.output ?? `output.${options.format ?? 'mp4'}`;
|
|
45
|
+
// Resolve resolution preset
|
|
46
|
+
let width = scene.getWidth();
|
|
47
|
+
let height = scene.getHeight();
|
|
48
|
+
if (options.resolution) {
|
|
49
|
+
const presetKey = `p${options.resolution}`;
|
|
50
|
+
const preset = Resolution[presetKey];
|
|
51
|
+
if (preset) {
|
|
52
|
+
width = preset.width;
|
|
53
|
+
height = preset.height;
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
console.warn(`Warning: Resolution preset '${options.resolution}' not found. Using scene defaults.`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
console.log(`Rendering scene to '${outputPath}'...`);
|
|
60
|
+
await renderer.render(scene, outputPath, {
|
|
61
|
+
width,
|
|
62
|
+
height,
|
|
63
|
+
frameRate: options.fps ? parseInt(options.fps, 10) : undefined,
|
|
64
|
+
format: options.format,
|
|
65
|
+
quality: options.quality,
|
|
66
|
+
onProgress: (progress) => {
|
|
67
|
+
const percent = progress.percentage.toFixed(1);
|
|
68
|
+
const eta = (progress.estimatedRemainingMs / 1000).toFixed(1);
|
|
69
|
+
process.stdout.write(`\rProgress: ${percent}% | ETA: ${eta}s `);
|
|
70
|
+
if (progress.percentage === 100) {
|
|
71
|
+
process.stdout.write('\n');
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
console.log('Rendering complete.');
|
|
76
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { listScenes } from './commands/list-scenes';
|
|
4
|
+
import { render } from './commands/render';
|
|
5
|
+
import { preview } from './commands/preview';
|
|
6
|
+
import { exportFrame } from './commands/export-frame';
|
|
7
|
+
const program = new Command();
|
|
8
|
+
program
|
|
9
|
+
.name('anima')
|
|
10
|
+
.description('CLI for Anima animation engine')
|
|
11
|
+
.version('0.1.0');
|
|
12
|
+
// Global options
|
|
13
|
+
program.option('-d, --debug', 'output extra debugging');
|
|
14
|
+
// list-scenes command
|
|
15
|
+
program
|
|
16
|
+
.command('list-scenes')
|
|
17
|
+
.description('List all scenes in a file')
|
|
18
|
+
.argument('<file>', 'TypeScript file to load')
|
|
19
|
+
.action(async (file) => {
|
|
20
|
+
await listScenes(file);
|
|
21
|
+
});
|
|
22
|
+
// render command
|
|
23
|
+
program
|
|
24
|
+
.command('render')
|
|
25
|
+
.description('Render a scene from a file')
|
|
26
|
+
.argument('<file>', 'TypeScript file to load')
|
|
27
|
+
.option('-s, --scene <name>', 'Specific scene to render')
|
|
28
|
+
.option('-f, --format <type>', 'Output format (mp4, webp, gif, sprite, png)', 'mp4')
|
|
29
|
+
.option('-r, --resolution <preset>', 'Resolution preset (480, 720, 1080, 4K)')
|
|
30
|
+
.option('--fps <number>', 'Frames per second')
|
|
31
|
+
.option('-q, --quality <level>', 'Render quality (production, preview)', 'production')
|
|
32
|
+
.option('-o, --output <path>', 'Output file path')
|
|
33
|
+
.action(async (file, options) => {
|
|
34
|
+
await render(file, options);
|
|
35
|
+
});
|
|
36
|
+
// preview command
|
|
37
|
+
program
|
|
38
|
+
.command('preview')
|
|
39
|
+
.description('Quickly preview a scene (lower quality)')
|
|
40
|
+
.argument('<file>', 'TypeScript file to load')
|
|
41
|
+
.option('-s, --scene <name>', 'Specific scene to preview')
|
|
42
|
+
.option('-f, --format <type>', 'Output format', 'mp4')
|
|
43
|
+
.option('-o, --output <path>', 'Output file path')
|
|
44
|
+
.action(async (file, options) => {
|
|
45
|
+
await preview(file, options);
|
|
46
|
+
});
|
|
47
|
+
// export-frame command
|
|
48
|
+
program
|
|
49
|
+
.command('export-frame')
|
|
50
|
+
.description('Export a single frame as PNG')
|
|
51
|
+
.argument('<file>', 'TypeScript file to load')
|
|
52
|
+
.option('-s, --scene <name>', 'Specific scene')
|
|
53
|
+
.option('--frame <number|last>', 'Frame number or "last"', 'last')
|
|
54
|
+
.option('-o, --output <path>', 'Output file path')
|
|
55
|
+
.action(async (file, options) => {
|
|
56
|
+
await exportFrame(file, options);
|
|
57
|
+
});
|
|
58
|
+
// Handle unknown commands
|
|
59
|
+
program.on('command:*', () => {
|
|
60
|
+
console.error('Invalid command: %s\nSee --help for a list of available commands.', program.args.join(' '));
|
|
61
|
+
process.exit(1);
|
|
62
|
+
});
|
|
63
|
+
program.parse(process.argv);
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Mobject } from '../../mobjects/Mobject';
|
|
2
|
+
import type { EasingFunction } from './easing';
|
|
3
|
+
import type { AnimationConfig, AnimationLifecycle } from './types';
|
|
4
|
+
/**
|
|
5
|
+
* Abstract base class for all animations.
|
|
6
|
+
* Provides configuration for duration, easing, and delay.
|
|
7
|
+
* Subclasses must specify their lifecycle category.
|
|
8
|
+
*/
|
|
9
|
+
export declare abstract class Animation<T extends Mobject = Mobject> {
|
|
10
|
+
protected readonly target: T;
|
|
11
|
+
protected durationSeconds: number;
|
|
12
|
+
protected easingFn: EasingFunction;
|
|
13
|
+
protected delaySeconds: number;
|
|
14
|
+
/**
|
|
15
|
+
* The lifecycle category of this animation.
|
|
16
|
+
* Determines how Scene.play() handles scene registration and validation.
|
|
17
|
+
*/
|
|
18
|
+
abstract readonly lifecycle: AnimationLifecycle;
|
|
19
|
+
constructor(target: T);
|
|
20
|
+
duration(seconds: number): this;
|
|
21
|
+
ease(easing: EasingFunction): this;
|
|
22
|
+
delay(seconds: number): this;
|
|
23
|
+
getDuration(): number;
|
|
24
|
+
getDelay(): number;
|
|
25
|
+
getEasing(): EasingFunction;
|
|
26
|
+
getTarget(): T;
|
|
27
|
+
getConfig(): AnimationConfig;
|
|
28
|
+
abstract interpolate(progress: number): void;
|
|
29
|
+
/**
|
|
30
|
+
* Ensures the animation is initialized before interpolation.
|
|
31
|
+
* Called before first update to capture start state.
|
|
32
|
+
* Default: no-op. Override in subclasses that need lazy initialization.
|
|
33
|
+
*/
|
|
34
|
+
ensureInitialized(): void;
|
|
35
|
+
/**
|
|
36
|
+
* Resets the animation to its uninitialized state.
|
|
37
|
+
* Allows animations to be replayed or looped.
|
|
38
|
+
*/
|
|
39
|
+
reset(): void;
|
|
40
|
+
update(progress: number): void;
|
|
41
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { defaultEasing } from './easing';
|
|
2
|
+
/**
|
|
3
|
+
* Abstract base class for all animations.
|
|
4
|
+
* Provides configuration for duration, easing, and delay.
|
|
5
|
+
* Subclasses must specify their lifecycle category.
|
|
6
|
+
*/
|
|
7
|
+
export class Animation {
|
|
8
|
+
target;
|
|
9
|
+
durationSeconds;
|
|
10
|
+
easingFn;
|
|
11
|
+
delaySeconds;
|
|
12
|
+
constructor(target) {
|
|
13
|
+
this.target = target;
|
|
14
|
+
this.durationSeconds = 1;
|
|
15
|
+
this.easingFn = defaultEasing;
|
|
16
|
+
this.delaySeconds = 0;
|
|
17
|
+
}
|
|
18
|
+
duration(seconds) {
|
|
19
|
+
if (seconds <= 0) {
|
|
20
|
+
throw new Error('Duration must be positive');
|
|
21
|
+
}
|
|
22
|
+
this.durationSeconds = seconds;
|
|
23
|
+
return this;
|
|
24
|
+
}
|
|
25
|
+
ease(easing) {
|
|
26
|
+
this.easingFn = easing;
|
|
27
|
+
return this;
|
|
28
|
+
}
|
|
29
|
+
delay(seconds) {
|
|
30
|
+
if (seconds < 0) {
|
|
31
|
+
throw new Error('Delay must be non-negative');
|
|
32
|
+
}
|
|
33
|
+
this.delaySeconds = seconds;
|
|
34
|
+
return this;
|
|
35
|
+
}
|
|
36
|
+
getDuration() {
|
|
37
|
+
return this.durationSeconds;
|
|
38
|
+
}
|
|
39
|
+
getDelay() {
|
|
40
|
+
return this.delaySeconds;
|
|
41
|
+
}
|
|
42
|
+
getEasing() {
|
|
43
|
+
return this.easingFn;
|
|
44
|
+
}
|
|
45
|
+
getTarget() {
|
|
46
|
+
return this.target;
|
|
47
|
+
}
|
|
48
|
+
getConfig() {
|
|
49
|
+
return {
|
|
50
|
+
durationSeconds: this.durationSeconds,
|
|
51
|
+
easing: this.easingFn,
|
|
52
|
+
delaySeconds: this.delaySeconds,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Ensures the animation is initialized before interpolation.
|
|
57
|
+
* Called before first update to capture start state.
|
|
58
|
+
* Default: no-op. Override in subclasses that need lazy initialization.
|
|
59
|
+
*/
|
|
60
|
+
ensureInitialized() {
|
|
61
|
+
// Default: no-op
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Resets the animation to its uninitialized state.
|
|
65
|
+
* Allows animations to be replayed or looped.
|
|
66
|
+
*/
|
|
67
|
+
reset() {
|
|
68
|
+
// Default: no-op
|
|
69
|
+
}
|
|
70
|
+
update(progress) {
|
|
71
|
+
// this.ensureInitialized();
|
|
72
|
+
const clampedProgress = Math.max(0, Math.min(1, progress));
|
|
73
|
+
const easedProgress = this.easingFn(clampedProgress);
|
|
74
|
+
this.interpolate(easedProgress);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Animation } from '../Animation';
|
|
2
|
+
import type { AnimationLifecycle } from '../types';
|
|
3
|
+
import type { CameraFrame } from '../../camera/CameraFrame';
|
|
4
|
+
import type { Mobject } from '../../../mobjects/Mobject';
|
|
5
|
+
import { Vector2 } from '../../math/Vector2/Vector2';
|
|
6
|
+
/**
|
|
7
|
+
* Configuration options for the Follow animation.
|
|
8
|
+
*/
|
|
9
|
+
interface FollowConfig {
|
|
10
|
+
/**
|
|
11
|
+
* Offset from the target's position. The camera will track
|
|
12
|
+
* (target.position + offset) instead of the exact target position.
|
|
13
|
+
* @default Vector2.ZERO
|
|
14
|
+
*/
|
|
15
|
+
offset?: Vector2;
|
|
16
|
+
/**
|
|
17
|
+
* Damping factor for smooth following (0 to 1).
|
|
18
|
+
* - 0 = instant snap to target (no smoothing)
|
|
19
|
+
* - 0.9 = very smooth, slow following
|
|
20
|
+
* Higher values create a more "laggy" camera that takes longer to catch up.
|
|
21
|
+
* @default 0
|
|
22
|
+
*/
|
|
23
|
+
damping?: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Animation that makes a CameraFrame track a target Mobject's position over time.
|
|
27
|
+
* Unlike MoveTo which captures position once, Follow reads the target position
|
|
28
|
+
* every frame, allowing the camera to track moving objects.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* // Basic follow - camera snaps to target position
|
|
32
|
+
* this.play(new Follow(this.frame, movingCircle).duration(5));
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* // Smooth follow with damping
|
|
36
|
+
* this.play(new Follow(this.frame, player, { damping: 0.8 }).duration(10));
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* // Follow with offset (camera leads the target)
|
|
40
|
+
* this.play(new Follow(this.frame, car, {
|
|
41
|
+
* offset: new Vector2(2, 0), // Camera 2 units ahead
|
|
42
|
+
* damping: 0.5
|
|
43
|
+
* }).duration(10));
|
|
44
|
+
*/
|
|
45
|
+
export declare class Follow extends Animation<CameraFrame> {
|
|
46
|
+
readonly lifecycle: AnimationLifecycle;
|
|
47
|
+
private readonly followTarget;
|
|
48
|
+
private readonly offset;
|
|
49
|
+
private readonly damping;
|
|
50
|
+
/**
|
|
51
|
+
* Creates a new Follow animation.
|
|
52
|
+
*
|
|
53
|
+
* @param frame - The CameraFrame to animate
|
|
54
|
+
* @param target - The Mobject to follow
|
|
55
|
+
* @param config - Configuration options
|
|
56
|
+
* @throws Error if frame is null or undefined
|
|
57
|
+
* @throws Error if target is null or undefined
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* const follow = new Follow(scene.frame, player, { damping: 0.7 });
|
|
61
|
+
* this.play(follow.duration(10));
|
|
62
|
+
*/
|
|
63
|
+
constructor(frame: CameraFrame, target: Mobject, config?: FollowConfig);
|
|
64
|
+
/**
|
|
65
|
+
* Updates the camera position each frame to track the target.
|
|
66
|
+
* @param progress - Animation progress (0 to 1)
|
|
67
|
+
*/
|
|
68
|
+
interpolate(progress: number): void;
|
|
69
|
+
}
|
|
70
|
+
export {};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Animation } from '../Animation';
|
|
2
|
+
import { Vector2 } from '../../math/Vector2/Vector2';
|
|
3
|
+
/**
|
|
4
|
+
* Animation that makes a CameraFrame track a target Mobject's position over time.
|
|
5
|
+
* Unlike MoveTo which captures position once, Follow reads the target position
|
|
6
|
+
* every frame, allowing the camera to track moving objects.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* // Basic follow - camera snaps to target position
|
|
10
|
+
* this.play(new Follow(this.frame, movingCircle).duration(5));
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* // Smooth follow with damping
|
|
14
|
+
* this.play(new Follow(this.frame, player, { damping: 0.8 }).duration(10));
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* // Follow with offset (camera leads the target)
|
|
18
|
+
* this.play(new Follow(this.frame, car, {
|
|
19
|
+
* offset: new Vector2(2, 0), // Camera 2 units ahead
|
|
20
|
+
* damping: 0.5
|
|
21
|
+
* }).duration(10));
|
|
22
|
+
*/
|
|
23
|
+
export class Follow extends Animation {
|
|
24
|
+
lifecycle = 'transformative';
|
|
25
|
+
followTarget;
|
|
26
|
+
offset;
|
|
27
|
+
damping;
|
|
28
|
+
/**
|
|
29
|
+
* Creates a new Follow animation.
|
|
30
|
+
*
|
|
31
|
+
* @param frame - The CameraFrame to animate
|
|
32
|
+
* @param target - The Mobject to follow
|
|
33
|
+
* @param config - Configuration options
|
|
34
|
+
* @throws Error if frame is null or undefined
|
|
35
|
+
* @throws Error if target is null or undefined
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* const follow = new Follow(scene.frame, player, { damping: 0.7 });
|
|
39
|
+
* this.play(follow.duration(10));
|
|
40
|
+
*/
|
|
41
|
+
constructor(frame, target, config = {}) {
|
|
42
|
+
if (!frame) {
|
|
43
|
+
throw new Error('Follow animation requires a CameraFrame');
|
|
44
|
+
}
|
|
45
|
+
if (!target) {
|
|
46
|
+
throw new Error('Follow animation requires a target Mobject');
|
|
47
|
+
}
|
|
48
|
+
super(frame);
|
|
49
|
+
this.followTarget = target;
|
|
50
|
+
this.offset = config.offset ?? Vector2.ZERO;
|
|
51
|
+
this.damping = config.damping ?? 0;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Updates the camera position each frame to track the target.
|
|
55
|
+
* @param progress - Animation progress (0 to 1)
|
|
56
|
+
*/
|
|
57
|
+
interpolate(progress) {
|
|
58
|
+
const targetPos = this.followTarget.position.add(this.offset);
|
|
59
|
+
const currentPos = this.target.position;
|
|
60
|
+
if (this.damping > 0 && this.damping < 1) {
|
|
61
|
+
const lerpFactor = 1 - this.damping;
|
|
62
|
+
const newPos = currentPos.lerp(targetPos, lerpFactor);
|
|
63
|
+
this.target.pos(newPos.x, newPos.y);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
this.target.pos(targetPos.x, targetPos.y);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|