@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.
Files changed (239) hide show
  1. package/dist/cli/SceneLoader.d.ts +22 -0
  2. package/dist/cli/SceneLoader.js +47 -0
  3. package/dist/cli/commands/export-frame.d.ts +13 -0
  4. package/dist/cli/commands/export-frame.js +60 -0
  5. package/dist/cli/commands/list-scenes.d.ts +5 -0
  6. package/dist/cli/commands/list-scenes.js +22 -0
  7. package/dist/cli/commands/preview.d.ts +5 -0
  8. package/dist/cli/commands/preview.js +11 -0
  9. package/dist/cli/commands/render.d.ts +16 -0
  10. package/dist/cli/commands/render.js +76 -0
  11. package/dist/cli/index.d.ts +2 -0
  12. package/dist/cli/index.js +63 -0
  13. package/dist/core/animations/Animation.d.ts +41 -0
  14. package/dist/core/animations/Animation.js +76 -0
  15. package/dist/core/animations/camera/Follow.d.ts +70 -0
  16. package/dist/core/animations/camera/Follow.js +69 -0
  17. package/dist/core/animations/camera/Shake.d.ts +90 -0
  18. package/dist/core/animations/camera/Shake.js +87 -0
  19. package/dist/core/animations/camera/index.d.ts +2 -0
  20. package/dist/core/animations/camera/index.js +2 -0
  21. package/dist/core/animations/categories/ExitAnimation.d.ts +17 -0
  22. package/dist/core/animations/categories/ExitAnimation.js +15 -0
  23. package/dist/core/animations/categories/IntroductoryAnimation.d.ts +16 -0
  24. package/dist/core/animations/categories/IntroductoryAnimation.js +14 -0
  25. package/dist/core/animations/categories/TransformativeAnimation.d.ts +25 -0
  26. package/dist/core/animations/categories/TransformativeAnimation.js +25 -0
  27. package/dist/core/animations/categories/index.d.ts +3 -0
  28. package/dist/core/animations/categories/index.js +3 -0
  29. package/dist/core/animations/composition/Parallel.d.ts +37 -0
  30. package/dist/core/animations/composition/Parallel.js +79 -0
  31. package/dist/core/animations/composition/Sequence.d.ts +41 -0
  32. package/dist/core/animations/composition/Sequence.js +95 -0
  33. package/dist/core/animations/composition/index.d.ts +2 -0
  34. package/dist/core/animations/composition/index.js +3 -0
  35. package/dist/core/animations/draw/Draw.d.ts +30 -0
  36. package/dist/core/animations/draw/Draw.js +122 -0
  37. package/dist/core/animations/draw/Unwrite.d.ts +30 -0
  38. package/dist/core/animations/draw/Unwrite.js +120 -0
  39. package/dist/core/animations/draw/Write.d.ts +35 -0
  40. package/dist/core/animations/draw/Write.js +119 -0
  41. package/dist/core/animations/draw/index.d.ts +3 -0
  42. package/dist/core/animations/draw/index.js +3 -0
  43. package/dist/core/animations/draw/partialPath.d.ts +6 -0
  44. package/dist/core/animations/draw/partialPath.js +138 -0
  45. package/dist/core/animations/easing/bounce.d.ts +13 -0
  46. package/dist/core/animations/easing/bounce.js +37 -0
  47. package/dist/core/animations/easing/index.d.ts +7 -0
  48. package/dist/core/animations/easing/index.js +11 -0
  49. package/dist/core/animations/easing/manim.d.ts +46 -0
  50. package/dist/core/animations/easing/manim.js +102 -0
  51. package/dist/core/animations/easing/registry.d.ts +8 -0
  52. package/dist/core/animations/easing/registry.js +25 -0
  53. package/dist/core/animations/easing/standard.d.ts +113 -0
  54. package/dist/core/animations/easing/standard.js +151 -0
  55. package/dist/core/animations/easing/types.d.ts +6 -0
  56. package/dist/core/animations/easing/types.js +0 -0
  57. package/dist/core/animations/fade/FadeIn.d.ts +17 -0
  58. package/dist/core/animations/fade/FadeIn.js +22 -0
  59. package/dist/core/animations/fade/FadeOut.d.ts +17 -0
  60. package/dist/core/animations/fade/FadeOut.js +23 -0
  61. package/dist/core/animations/fade/index.d.ts +2 -0
  62. package/dist/core/animations/fade/index.js +2 -0
  63. package/dist/core/animations/index.d.ts +11 -0
  64. package/dist/core/animations/index.js +17 -0
  65. package/dist/core/animations/keyframes/KeyframeAnimation.d.ts +33 -0
  66. package/dist/core/animations/keyframes/KeyframeAnimation.js +40 -0
  67. package/dist/core/animations/keyframes/KeyframeTrack.d.ts +31 -0
  68. package/dist/core/animations/keyframes/KeyframeTrack.js +83 -0
  69. package/dist/core/animations/keyframes/index.d.ts +4 -0
  70. package/dist/core/animations/keyframes/index.js +5 -0
  71. package/dist/core/animations/keyframes/types.d.ts +25 -0
  72. package/dist/core/animations/keyframes/types.js +6 -0
  73. package/dist/core/animations/morph/MorphTo.d.ts +22 -0
  74. package/dist/core/animations/morph/MorphTo.js +42 -0
  75. package/dist/core/animations/morph/index.d.ts +1 -0
  76. package/dist/core/animations/morph/index.js +1 -0
  77. package/dist/core/animations/transform/MoveTo.d.ts +24 -0
  78. package/dist/core/animations/transform/MoveTo.js +38 -0
  79. package/dist/core/animations/transform/Rotate.d.ts +23 -0
  80. package/dist/core/animations/transform/Rotate.js +34 -0
  81. package/dist/core/animations/transform/Scale.d.ts +23 -0
  82. package/dist/core/animations/transform/Scale.js +35 -0
  83. package/dist/core/animations/transform/index.d.ts +3 -0
  84. package/dist/core/animations/transform/index.js +3 -0
  85. package/dist/core/animations/types.d.ts +52 -0
  86. package/dist/core/animations/types.js +6 -0
  87. package/dist/core/camera/Camera.d.ts +87 -0
  88. package/dist/core/camera/Camera.js +175 -0
  89. package/dist/core/camera/CameraFrame.d.ts +242 -0
  90. package/dist/core/camera/CameraFrame.js +322 -0
  91. package/dist/core/camera/index.d.ts +4 -0
  92. package/dist/core/camera/index.js +3 -0
  93. package/dist/core/camera/types.d.ts +17 -0
  94. package/dist/core/camera/types.js +1 -0
  95. package/dist/core/errors/AnimationErrors.d.ts +12 -0
  96. package/dist/core/errors/AnimationErrors.js +37 -0
  97. package/dist/core/errors/index.d.ts +1 -0
  98. package/dist/core/errors/index.js +1 -0
  99. package/dist/core/math/Vector2/Vector2.d.ts +23 -0
  100. package/dist/core/math/Vector2/Vector2.js +46 -0
  101. package/dist/core/math/Vector2/index.d.ts +1 -0
  102. package/dist/core/math/Vector2/index.js +1 -0
  103. package/dist/core/math/bezier/BezierPath.d.ts +38 -0
  104. package/dist/core/math/bezier/BezierPath.js +264 -0
  105. package/dist/core/math/bezier/evaluators.d.ts +9 -0
  106. package/dist/core/math/bezier/evaluators.js +36 -0
  107. package/dist/core/math/bezier/index.d.ts +8 -0
  108. package/dist/core/math/bezier/index.js +6 -0
  109. package/dist/core/math/bezier/length.d.ts +5 -0
  110. package/dist/core/math/bezier/length.js +27 -0
  111. package/dist/core/math/bezier/morphing.d.ts +16 -0
  112. package/dist/core/math/bezier/morphing.js +151 -0
  113. package/dist/core/math/bezier/sampling.d.ts +7 -0
  114. package/dist/core/math/bezier/sampling.js +153 -0
  115. package/dist/core/math/bezier/split.d.ts +19 -0
  116. package/dist/core/math/bezier/split.js +44 -0
  117. package/dist/core/math/bezier/types.d.ts +8 -0
  118. package/dist/core/math/bezier/types.js +0 -0
  119. package/dist/core/math/color/Color.d.ts +28 -0
  120. package/dist/core/math/color/Color.js +60 -0
  121. package/dist/core/math/color/conversions.d.ts +17 -0
  122. package/dist/core/math/color/conversions.js +100 -0
  123. package/dist/core/math/color/index.d.ts +2 -0
  124. package/dist/core/math/color/index.js +2 -0
  125. package/dist/core/math/index.d.ts +4 -0
  126. package/dist/core/math/index.js +5 -0
  127. package/dist/core/math/matrix/Matrix3x3.d.ts +23 -0
  128. package/dist/core/math/matrix/Matrix3x3.js +91 -0
  129. package/dist/core/math/matrix/factories.d.ts +12 -0
  130. package/dist/core/math/matrix/factories.js +44 -0
  131. package/dist/core/math/matrix/index.d.ts +2 -0
  132. package/dist/core/math/matrix/index.js +2 -0
  133. package/dist/core/renderer/FrameRenderer.d.ts +37 -0
  134. package/dist/core/renderer/FrameRenderer.js +75 -0
  135. package/dist/core/renderer/ProgressReporter.d.ts +19 -0
  136. package/dist/core/renderer/ProgressReporter.js +58 -0
  137. package/dist/core/renderer/Renderer.d.ts +36 -0
  138. package/dist/core/renderer/Renderer.js +102 -0
  139. package/dist/core/renderer/drawMobject.d.ts +8 -0
  140. package/dist/core/renderer/drawMobject.js +109 -0
  141. package/dist/core/renderer/formats/index.d.ts +3 -0
  142. package/dist/core/renderer/formats/index.js +3 -0
  143. package/dist/core/renderer/formats/png.d.ts +5 -0
  144. package/dist/core/renderer/formats/png.js +7 -0
  145. package/dist/core/renderer/formats/sprite.d.ts +6 -0
  146. package/dist/core/renderer/formats/sprite.js +24 -0
  147. package/dist/core/renderer/formats/video.d.ts +8 -0
  148. package/dist/core/renderer/formats/video.js +51 -0
  149. package/dist/core/renderer/index.d.ts +7 -0
  150. package/dist/core/renderer/index.js +9 -0
  151. package/dist/core/renderer/types.d.ts +87 -0
  152. package/dist/core/renderer/types.js +13 -0
  153. package/dist/core/scene/Scene.d.ts +104 -0
  154. package/dist/core/scene/Scene.js +225 -0
  155. package/dist/core/scene/index.d.ts +2 -0
  156. package/dist/core/scene/index.js +1 -0
  157. package/dist/core/scene/types.d.ts +23 -0
  158. package/dist/core/scene/types.js +0 -0
  159. package/dist/core/serialization/animation.d.ts +23 -0
  160. package/dist/core/serialization/animation.js +176 -0
  161. package/dist/core/serialization/easingLookup.d.ts +13 -0
  162. package/dist/core/serialization/easingLookup.js +65 -0
  163. package/dist/core/serialization/index.d.ts +23 -0
  164. package/dist/core/serialization/index.js +29 -0
  165. package/dist/core/serialization/mobject.d.ts +23 -0
  166. package/dist/core/serialization/mobject.js +248 -0
  167. package/dist/core/serialization/prettyPrint.d.ts +12 -0
  168. package/dist/core/serialization/prettyPrint.js +16 -0
  169. package/dist/core/serialization/primitives.d.ts +24 -0
  170. package/dist/core/serialization/primitives.js +98 -0
  171. package/dist/core/serialization/registry.d.ts +29 -0
  172. package/dist/core/serialization/registry.js +39 -0
  173. package/dist/core/serialization/scene.d.ts +28 -0
  174. package/dist/core/serialization/scene.js +114 -0
  175. package/dist/core/serialization/types.d.ts +152 -0
  176. package/dist/core/serialization/types.js +6 -0
  177. package/dist/core/timeline/Timeline.d.ts +70 -0
  178. package/dist/core/timeline/Timeline.js +144 -0
  179. package/dist/core/timeline/index.d.ts +5 -0
  180. package/dist/core/timeline/index.js +4 -0
  181. package/dist/core/timeline/types.d.ts +29 -0
  182. package/dist/core/timeline/types.js +0 -0
  183. package/dist/index.d.ts +18 -0
  184. package/dist/index.js +22 -0
  185. package/dist/mobjects/Mobject.d.ts +98 -0
  186. package/dist/mobjects/Mobject.js +343 -0
  187. package/dist/mobjects/VGroup/VGroup.d.ts +51 -0
  188. package/dist/mobjects/VGroup/VGroup.js +142 -0
  189. package/dist/mobjects/VGroup/index.d.ts +3 -0
  190. package/dist/mobjects/VGroup/index.js +2 -0
  191. package/dist/mobjects/VGroup/layout.d.ts +20 -0
  192. package/dist/mobjects/VGroup/layout.js +139 -0
  193. package/dist/mobjects/VMobject.d.ts +106 -0
  194. package/dist/mobjects/VMobject.js +216 -0
  195. package/dist/mobjects/geometry/Arc.d.ts +8 -0
  196. package/dist/mobjects/geometry/Arc.js +46 -0
  197. package/dist/mobjects/geometry/Arrow.d.ts +7 -0
  198. package/dist/mobjects/geometry/Arrow.js +34 -0
  199. package/dist/mobjects/geometry/Circle.d.ts +4 -0
  200. package/dist/mobjects/geometry/Circle.js +10 -0
  201. package/dist/mobjects/geometry/Line.d.ts +8 -0
  202. package/dist/mobjects/geometry/Line.js +19 -0
  203. package/dist/mobjects/geometry/Point.d.ts +5 -0
  204. package/dist/mobjects/geometry/Point.js +11 -0
  205. package/dist/mobjects/geometry/Polygon.d.ts +7 -0
  206. package/dist/mobjects/geometry/Polygon.js +21 -0
  207. package/dist/mobjects/geometry/Rectangle.d.ts +6 -0
  208. package/dist/mobjects/geometry/Rectangle.js +18 -0
  209. package/dist/mobjects/geometry/index.d.ts +7 -0
  210. package/dist/mobjects/geometry/index.js +7 -0
  211. package/dist/mobjects/graph/Graph.d.ts +28 -0
  212. package/dist/mobjects/graph/Graph.js +119 -0
  213. package/dist/mobjects/graph/GraphEdge.d.ts +26 -0
  214. package/dist/mobjects/graph/GraphEdge.js +64 -0
  215. package/dist/mobjects/graph/GraphNode.d.ts +19 -0
  216. package/dist/mobjects/graph/GraphNode.js +63 -0
  217. package/dist/mobjects/graph/index.d.ts +5 -0
  218. package/dist/mobjects/graph/index.js +5 -0
  219. package/dist/mobjects/graph/layouts/circular.d.ts +8 -0
  220. package/dist/mobjects/graph/layouts/circular.js +23 -0
  221. package/dist/mobjects/graph/layouts/forceDirected.d.ts +9 -0
  222. package/dist/mobjects/graph/layouts/forceDirected.js +102 -0
  223. package/dist/mobjects/graph/layouts/index.d.ts +3 -0
  224. package/dist/mobjects/graph/layouts/index.js +3 -0
  225. package/dist/mobjects/graph/layouts/tree.d.ts +9 -0
  226. package/dist/mobjects/graph/layouts/tree.js +99 -0
  227. package/dist/mobjects/graph/types.d.ts +35 -0
  228. package/dist/mobjects/graph/types.js +0 -0
  229. package/dist/mobjects/index.d.ts +6 -0
  230. package/dist/mobjects/index.js +6 -0
  231. package/dist/mobjects/text/Glyph.d.ts +11 -0
  232. package/dist/mobjects/text/Glyph.js +72 -0
  233. package/dist/mobjects/text/Text.d.ts +19 -0
  234. package/dist/mobjects/text/Text.js +76 -0
  235. package/dist/mobjects/text/index.d.ts +4 -0
  236. package/dist/mobjects/text/index.js +3 -0
  237. package/dist/mobjects/text/types.d.ts +12 -0
  238. package/dist/mobjects/text/types.js +8 -0
  239. 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,5 @@
1
+ /**
2
+ * Implementation of the 'list-scenes' command.
3
+ * Loads a file and prints the names of all Scene classes found.
4
+ */
5
+ export declare function listScenes(file: string): Promise<void>;
@@ -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,5 @@
1
+ /**
2
+ * Implementation of the 'preview' command.
3
+ * Shorthand for rendering with 'preview' quality.
4
+ */
5
+ export declare function preview(file: string, options: any): Promise<void>;
@@ -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,2 @@
1
+ #!/usr/bin/env bun
2
+ export {};
@@ -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
+ }