@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,75 @@
|
|
|
1
|
+
import { createCanvas } from '@napi-rs/canvas';
|
|
2
|
+
import { Matrix3x3 } from '../math/matrix/Matrix3x3';
|
|
3
|
+
import { drawMobject } from './drawMobject';
|
|
4
|
+
import { MANIM_FRAME_HEIGHT } from '../camera/types';
|
|
5
|
+
/**
|
|
6
|
+
* Renders individual frames from a Scene.
|
|
7
|
+
* Responsible for drawing mobjects to a canvas at a specific point in time.
|
|
8
|
+
*/
|
|
9
|
+
export class FrameRenderer {
|
|
10
|
+
scene;
|
|
11
|
+
width;
|
|
12
|
+
height;
|
|
13
|
+
constructor(scene, width, height) {
|
|
14
|
+
this.scene = scene;
|
|
15
|
+
this.width = width;
|
|
16
|
+
this.height = height;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Calculates the matrix that transforms Manim world coordinates to screen pixels.
|
|
20
|
+
*
|
|
21
|
+
* Manim coordinate system:
|
|
22
|
+
* - Origin at center of screen
|
|
23
|
+
* - Y-axis points up
|
|
24
|
+
* - frameHeight = 8.0 units
|
|
25
|
+
*
|
|
26
|
+
* Screen coordinate system:
|
|
27
|
+
* - Origin at top-left
|
|
28
|
+
* - Y-axis points down
|
|
29
|
+
* - Width x Height pixels
|
|
30
|
+
*/
|
|
31
|
+
calculateWorldToScreenMatrix() {
|
|
32
|
+
const camera = this.scene.getCamera();
|
|
33
|
+
const viewMatrix = camera.getViewMatrix();
|
|
34
|
+
// Scale from Manim units to pixels
|
|
35
|
+
// pixelHeight / MANIM_FRAME_HEIGHT = pixels per unit
|
|
36
|
+
const scale = this.height / MANIM_FRAME_HEIGHT;
|
|
37
|
+
// Build the world-to-screen transform:
|
|
38
|
+
// 1. Apply camera view transform (pan, zoom, rotation)
|
|
39
|
+
// 2. Scale from units to pixels
|
|
40
|
+
// 3. Flip Y-axis (Manim Y-up to screen Y-down)
|
|
41
|
+
// 4. Translate origin to screen center
|
|
42
|
+
const scaleMatrix = Matrix3x3.scale(scale, -scale); // Flip Y
|
|
43
|
+
const translateToCenter = Matrix3x3.translation(this.width / 2, this.height / 2);
|
|
44
|
+
// Combined: translate * scale * view
|
|
45
|
+
return translateToCenter.multiply(scaleMatrix).multiply(viewMatrix);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Renders a single frame at the specified time.
|
|
49
|
+
*/
|
|
50
|
+
renderFrame(time) {
|
|
51
|
+
const canvas = createCanvas(this.width, this.height);
|
|
52
|
+
const ctx = canvas.getContext('2d');
|
|
53
|
+
// Seek timeline to the specified time
|
|
54
|
+
const timeline = this.scene.getTimeline();
|
|
55
|
+
timeline.seek(time);
|
|
56
|
+
// Draw background
|
|
57
|
+
const bgColor = this.scene.getBackgroundColor();
|
|
58
|
+
ctx.fillStyle = `rgb(${bgColor.r}, ${bgColor.g}, ${bgColor.b})`;
|
|
59
|
+
ctx.fillRect(0, 0, this.width, this.height);
|
|
60
|
+
// Calculate world-to-screen matrix each frame to pick up camera animations
|
|
61
|
+
const worldToScreen = this.calculateWorldToScreenMatrix();
|
|
62
|
+
// Draw all mobjects in order
|
|
63
|
+
const mobjects = this.scene.getMobjects();
|
|
64
|
+
for (const mobject of mobjects) {
|
|
65
|
+
drawMobject(ctx, mobject, worldToScreen);
|
|
66
|
+
}
|
|
67
|
+
return canvas;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Gets the canvas dimensions.
|
|
71
|
+
*/
|
|
72
|
+
getDimensions() {
|
|
73
|
+
return { width: this.width, height: this.height };
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ProgressCallback } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Tracks rendering progress and reports updates via callback.
|
|
4
|
+
*/
|
|
5
|
+
export declare class ProgressReporter {
|
|
6
|
+
private readonly totalFrames;
|
|
7
|
+
private readonly onProgress?;
|
|
8
|
+
private readonly startTime;
|
|
9
|
+
private currentFrame;
|
|
10
|
+
constructor(totalFrames: number, onProgress?: ProgressCallback);
|
|
11
|
+
/**
|
|
12
|
+
* Report progress for the current frame.
|
|
13
|
+
*/
|
|
14
|
+
reportFrame(frameIndex: number): void;
|
|
15
|
+
/**
|
|
16
|
+
* Report rendering complete.
|
|
17
|
+
*/
|
|
18
|
+
complete(): void;
|
|
19
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tracks rendering progress and reports updates via callback.
|
|
3
|
+
*/
|
|
4
|
+
export class ProgressReporter {
|
|
5
|
+
totalFrames;
|
|
6
|
+
onProgress;
|
|
7
|
+
startTime;
|
|
8
|
+
currentFrame = 0;
|
|
9
|
+
constructor(totalFrames, onProgress) {
|
|
10
|
+
this.totalFrames = totalFrames;
|
|
11
|
+
this.onProgress = onProgress;
|
|
12
|
+
this.startTime = Date.now();
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Report progress for the current frame.
|
|
16
|
+
*/
|
|
17
|
+
reportFrame(frameIndex) {
|
|
18
|
+
this.currentFrame = frameIndex;
|
|
19
|
+
if (!this.onProgress)
|
|
20
|
+
return;
|
|
21
|
+
const elapsedMs = Date.now() - this.startTime;
|
|
22
|
+
const framesComplete = frameIndex + 1;
|
|
23
|
+
const percentage = this.totalFrames > 0
|
|
24
|
+
? (framesComplete / this.totalFrames) * 100
|
|
25
|
+
: 100;
|
|
26
|
+
// Estimate remaining time based on average time per frame
|
|
27
|
+
let estimatedRemainingMs = 0;
|
|
28
|
+
if (framesComplete > 0 && framesComplete < this.totalFrames) {
|
|
29
|
+
const msPerFrame = elapsedMs / framesComplete;
|
|
30
|
+
const framesRemaining = this.totalFrames - framesComplete;
|
|
31
|
+
estimatedRemainingMs = msPerFrame * framesRemaining;
|
|
32
|
+
}
|
|
33
|
+
const progress = {
|
|
34
|
+
currentFrame: frameIndex,
|
|
35
|
+
totalFrames: this.totalFrames,
|
|
36
|
+
percentage: Math.min(100, Math.max(0, percentage)),
|
|
37
|
+
elapsedMs,
|
|
38
|
+
estimatedRemainingMs,
|
|
39
|
+
};
|
|
40
|
+
this.onProgress(progress);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Report rendering complete.
|
|
44
|
+
*/
|
|
45
|
+
complete() {
|
|
46
|
+
if (!this.onProgress)
|
|
47
|
+
return;
|
|
48
|
+
const elapsedMs = Date.now() - this.startTime;
|
|
49
|
+
const progress = {
|
|
50
|
+
currentFrame: this.totalFrames - 1,
|
|
51
|
+
totalFrames: this.totalFrames,
|
|
52
|
+
percentage: 100,
|
|
53
|
+
elapsedMs,
|
|
54
|
+
estimatedRemainingMs: 0,
|
|
55
|
+
};
|
|
56
|
+
this.onProgress(progress);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { Scene } from '../scene';
|
|
2
|
+
import type { RenderConfig } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Main renderer for producing output from Scenes.
|
|
5
|
+
* Supports multiple output formats and provides progress callbacks.
|
|
6
|
+
*/
|
|
7
|
+
export declare class Renderer {
|
|
8
|
+
/**
|
|
9
|
+
* Renders a scene to the specified output.
|
|
10
|
+
*
|
|
11
|
+
* @param scene The scene to render
|
|
12
|
+
* @param outputPath Output file or directory path (depends on format)
|
|
13
|
+
* @param config Optional render configuration
|
|
14
|
+
*/
|
|
15
|
+
render(scene: Scene, outputPath: string, config?: RenderConfig): Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* Renders only the last frame of a scene as a PNG.
|
|
18
|
+
*
|
|
19
|
+
* @param scene The scene to render
|
|
20
|
+
* @param outputPath Output PNG file path
|
|
21
|
+
* @param config Optional render configuration
|
|
22
|
+
*/
|
|
23
|
+
renderLastFrame(scene: Scene, outputPath: string, config?: RenderConfig): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Resolves partial config with scene defaults.
|
|
26
|
+
*/
|
|
27
|
+
private resolveConfig;
|
|
28
|
+
/**
|
|
29
|
+
* Ensures the directory exists.
|
|
30
|
+
*/
|
|
31
|
+
private ensureDirectory;
|
|
32
|
+
/**
|
|
33
|
+
* Renders a single frame (the last frame of the animation).
|
|
34
|
+
*/
|
|
35
|
+
private renderSingleFrame;
|
|
36
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { FrameRenderer } from './FrameRenderer';
|
|
2
|
+
import { ProgressReporter } from './ProgressReporter';
|
|
3
|
+
import { writePng, renderSpriteSequence, renderVideo } from './formats';
|
|
4
|
+
import { mkdir } from 'fs/promises';
|
|
5
|
+
import { dirname } from 'path';
|
|
6
|
+
/**
|
|
7
|
+
* Main renderer for producing output from Scenes.
|
|
8
|
+
* Supports multiple output formats and provides progress callbacks.
|
|
9
|
+
*/
|
|
10
|
+
export class Renderer {
|
|
11
|
+
/**
|
|
12
|
+
* Renders a scene to the specified output.
|
|
13
|
+
*
|
|
14
|
+
* @param scene The scene to render
|
|
15
|
+
* @param outputPath Output file or directory path (depends on format)
|
|
16
|
+
* @param config Optional render configuration
|
|
17
|
+
*/
|
|
18
|
+
async render(scene, outputPath, config = {}) {
|
|
19
|
+
const resolved = this.resolveConfig(scene, config);
|
|
20
|
+
const totalDuration = scene.getTotalDuration();
|
|
21
|
+
// Handle preview quality (half resolution)
|
|
22
|
+
const width = resolved.quality === 'preview'
|
|
23
|
+
? Math.floor(resolved.width / 2)
|
|
24
|
+
: resolved.width;
|
|
25
|
+
const height = resolved.quality === 'preview'
|
|
26
|
+
? Math.floor(resolved.height / 2)
|
|
27
|
+
: resolved.height;
|
|
28
|
+
const frameRenderer = new FrameRenderer(scene, width, height);
|
|
29
|
+
const totalFrames = Math.max(1, Math.floor(totalDuration * resolved.frameRate) + 1);
|
|
30
|
+
const progressReporter = new ProgressReporter(totalFrames, resolved.onProgress);
|
|
31
|
+
switch (resolved.format) {
|
|
32
|
+
case 'sprite':
|
|
33
|
+
await this.ensureDirectory(outputPath);
|
|
34
|
+
await renderSpriteSequence(frameRenderer, outputPath, resolved.frameRate, totalDuration, progressReporter);
|
|
35
|
+
break;
|
|
36
|
+
case 'png':
|
|
37
|
+
await this.ensureDirectory(dirname(outputPath));
|
|
38
|
+
await this.renderSingleFrame(frameRenderer, outputPath, totalDuration, progressReporter);
|
|
39
|
+
break;
|
|
40
|
+
case 'mp4':
|
|
41
|
+
case 'webp':
|
|
42
|
+
case 'gif':
|
|
43
|
+
await this.ensureDirectory(dirname(outputPath));
|
|
44
|
+
await renderVideo(frameRenderer, outputPath, resolved.format, resolved.frameRate, totalDuration, progressReporter);
|
|
45
|
+
break;
|
|
46
|
+
default:
|
|
47
|
+
throw new Error(`Unsupported render format: ${resolved.format}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Renders only the last frame of a scene as a PNG.
|
|
52
|
+
*
|
|
53
|
+
* @param scene The scene to render
|
|
54
|
+
* @param outputPath Output PNG file path
|
|
55
|
+
* @param config Optional render configuration
|
|
56
|
+
*/
|
|
57
|
+
async renderLastFrame(scene, outputPath, config = {}) {
|
|
58
|
+
const resolved = this.resolveConfig(scene, config);
|
|
59
|
+
const totalDuration = scene.getTotalDuration();
|
|
60
|
+
const width = resolved.quality === 'preview'
|
|
61
|
+
? Math.floor(resolved.width / 2)
|
|
62
|
+
: resolved.width;
|
|
63
|
+
const height = resolved.quality === 'preview'
|
|
64
|
+
? Math.floor(resolved.height / 2)
|
|
65
|
+
: resolved.height;
|
|
66
|
+
const frameRenderer = new FrameRenderer(scene, width, height);
|
|
67
|
+
const progressReporter = new ProgressReporter(1, resolved.onProgress);
|
|
68
|
+
await this.ensureDirectory(dirname(outputPath));
|
|
69
|
+
const canvas = frameRenderer.renderFrame(totalDuration);
|
|
70
|
+
await writePng(canvas, outputPath);
|
|
71
|
+
progressReporter.complete();
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Resolves partial config with scene defaults.
|
|
75
|
+
*/
|
|
76
|
+
resolveConfig(scene, config) {
|
|
77
|
+
return {
|
|
78
|
+
width: config.width ?? scene.getWidth(),
|
|
79
|
+
height: config.height ?? scene.getHeight(),
|
|
80
|
+
frameRate: config.frameRate ?? scene.getFrameRate(),
|
|
81
|
+
format: config.format ?? 'sprite',
|
|
82
|
+
quality: config.quality ?? 'production',
|
|
83
|
+
onProgress: config.onProgress,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Ensures the directory exists.
|
|
88
|
+
*/
|
|
89
|
+
async ensureDirectory(dirPath) {
|
|
90
|
+
if (!dirPath)
|
|
91
|
+
return;
|
|
92
|
+
await mkdir(dirPath, { recursive: true });
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Renders a single frame (the last frame of the animation).
|
|
96
|
+
*/
|
|
97
|
+
async renderSingleFrame(frameRenderer, outputPath, totalDuration, progressReporter) {
|
|
98
|
+
const canvas = frameRenderer.renderFrame(totalDuration);
|
|
99
|
+
await writePng(canvas, outputPath);
|
|
100
|
+
progressReporter.complete();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { SKRSContext2D } from '@napi-rs/canvas';
|
|
2
|
+
import type { Mobject } from '../../mobjects/Mobject';
|
|
3
|
+
import { Matrix3x3 } from '../math/matrix/Matrix3x3';
|
|
4
|
+
/**
|
|
5
|
+
* Draws a Mobject to a canvas context.
|
|
6
|
+
* Handles VMobject path rendering and VGroup recursion.
|
|
7
|
+
*/
|
|
8
|
+
export declare function drawMobject(ctx: SKRSContext2D, mobject: Mobject, worldToScreen: Matrix3x3): void;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { VMobject } from '../../mobjects/VMobject';
|
|
2
|
+
import { VGroup } from '../../mobjects/VGroup';
|
|
3
|
+
/**
|
|
4
|
+
* Draws a Mobject to a canvas context.
|
|
5
|
+
* Handles VMobject path rendering and VGroup recursion.
|
|
6
|
+
*/
|
|
7
|
+
export function drawMobject(ctx, mobject, worldToScreen) {
|
|
8
|
+
// Skip invisible mobjects
|
|
9
|
+
if (mobject.opacity <= 0)
|
|
10
|
+
return;
|
|
11
|
+
if (mobject instanceof VGroup) {
|
|
12
|
+
drawVGroup(ctx, mobject, worldToScreen);
|
|
13
|
+
}
|
|
14
|
+
else if (mobject instanceof VMobject) {
|
|
15
|
+
drawVMobject(ctx, mobject, worldToScreen);
|
|
16
|
+
}
|
|
17
|
+
// Base Mobject has no visual representation
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Draws a VGroup by recursively drawing its children.
|
|
21
|
+
*/
|
|
22
|
+
function drawVGroup(ctx, vgroup, worldToScreen) {
|
|
23
|
+
// VGroup opacity affects all children
|
|
24
|
+
if (vgroup.opacity <= 0)
|
|
25
|
+
return;
|
|
26
|
+
ctx.save();
|
|
27
|
+
ctx.globalAlpha *= vgroup.opacity;
|
|
28
|
+
for (const child of vgroup.getChildren()) {
|
|
29
|
+
drawMobject(ctx, child, worldToScreen);
|
|
30
|
+
}
|
|
31
|
+
ctx.restore();
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Draws a VMobject's paths to the canvas.
|
|
35
|
+
*/
|
|
36
|
+
function drawVMobject(ctx, vmobject, worldToScreen) {
|
|
37
|
+
const paths = vmobject.paths;
|
|
38
|
+
if (paths.length === 0)
|
|
39
|
+
return;
|
|
40
|
+
// Combine mobject's world matrix with world-to-screen transform
|
|
41
|
+
const mobjectWorld = vmobject.getWorldMatrix();
|
|
42
|
+
const transform = worldToScreen.multiply(mobjectWorld);
|
|
43
|
+
ctx.save();
|
|
44
|
+
ctx.globalAlpha *= vmobject.opacity;
|
|
45
|
+
// Draw each path
|
|
46
|
+
for (const path of paths) {
|
|
47
|
+
const commands = path.getCommands();
|
|
48
|
+
if (commands.length === 0)
|
|
49
|
+
continue;
|
|
50
|
+
ctx.beginPath();
|
|
51
|
+
applyPathCommands(ctx, commands, transform);
|
|
52
|
+
// Fill first (so stroke is on top)
|
|
53
|
+
if (vmobject.getFillOpacity() > 0) {
|
|
54
|
+
const fillColor = vmobject.getFillColor();
|
|
55
|
+
ctx.fillStyle = `rgba(${fillColor.r}, ${fillColor.g}, ${fillColor.b}, ${vmobject.getFillOpacity()})`;
|
|
56
|
+
ctx.fill();
|
|
57
|
+
}
|
|
58
|
+
// Then stroke
|
|
59
|
+
if (vmobject.getStrokeWidth() > 0) {
|
|
60
|
+
const strokeColor = vmobject.getStrokeColor();
|
|
61
|
+
ctx.strokeStyle = `rgba(${strokeColor.r}, ${strokeColor.g}, ${strokeColor.b}, ${strokeColor.a})`;
|
|
62
|
+
ctx.lineWidth = vmobject.getStrokeWidth();
|
|
63
|
+
ctx.lineCap = 'round';
|
|
64
|
+
ctx.lineJoin = 'round';
|
|
65
|
+
ctx.stroke();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
ctx.restore();
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Applies BezierPath commands to a canvas context.
|
|
72
|
+
*/
|
|
73
|
+
function applyPathCommands(ctx, commands, transform) {
|
|
74
|
+
for (const cmd of commands) {
|
|
75
|
+
switch (cmd.type) {
|
|
76
|
+
case 'Move': {
|
|
77
|
+
const p = transform.transformPoint(cmd.end);
|
|
78
|
+
ctx.moveTo(p.x, p.y);
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
case 'Line': {
|
|
82
|
+
const p = transform.transformPoint(cmd.end);
|
|
83
|
+
ctx.lineTo(p.x, p.y);
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
case 'Quadratic': {
|
|
87
|
+
if (cmd.control1) {
|
|
88
|
+
const cp = transform.transformPoint(cmd.control1);
|
|
89
|
+
const ep = transform.transformPoint(cmd.end);
|
|
90
|
+
ctx.quadraticCurveTo(cp.x, cp.y, ep.x, ep.y);
|
|
91
|
+
}
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
case 'Cubic': {
|
|
95
|
+
if (cmd.control1 && cmd.control2) {
|
|
96
|
+
const cp1 = transform.transformPoint(cmd.control1);
|
|
97
|
+
const cp2 = transform.transformPoint(cmd.control2);
|
|
98
|
+
const ep = transform.transformPoint(cmd.end);
|
|
99
|
+
ctx.bezierCurveTo(cp1.x, cp1.y, cp2.x, cp2.y, ep.x, ep.y);
|
|
100
|
+
}
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
case 'Close': {
|
|
104
|
+
ctx.closePath();
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { FrameRenderer } from '../FrameRenderer';
|
|
2
|
+
import type { ProgressReporter } from '../ProgressReporter';
|
|
3
|
+
/**
|
|
4
|
+
* Renders frames to a sprite sequence (numbered PNG files).
|
|
5
|
+
*/
|
|
6
|
+
export declare function renderSpriteSequence(frameRenderer: FrameRenderer, outputDir: string, frameRate: number, totalDuration: number, progressReporter: ProgressReporter): Promise<void>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { writePng } from './png';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
/**
|
|
4
|
+
* Pads a number with leading zeros to reach the specified length.
|
|
5
|
+
*/
|
|
6
|
+
function padNumber(n, length) {
|
|
7
|
+
return n.toString().padStart(length, '0');
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Renders frames to a sprite sequence (numbered PNG files).
|
|
11
|
+
*/
|
|
12
|
+
export async function renderSpriteSequence(frameRenderer, outputDir, frameRate, totalDuration, progressReporter) {
|
|
13
|
+
const totalFrames = Math.max(1, Math.floor(totalDuration * frameRate) + 1);
|
|
14
|
+
const digitCount = Math.max(4, totalFrames.toString().length);
|
|
15
|
+
for (let frameIndex = 0; frameIndex < totalFrames; frameIndex++) {
|
|
16
|
+
const time = frameIndex / frameRate;
|
|
17
|
+
const canvas = frameRenderer.renderFrame(time);
|
|
18
|
+
const filename = `frame_${padNumber(frameIndex, digitCount)}.png`;
|
|
19
|
+
const outputPath = join(outputDir, filename);
|
|
20
|
+
await writePng(canvas, outputPath);
|
|
21
|
+
progressReporter.reportFrame(frameIndex);
|
|
22
|
+
}
|
|
23
|
+
progressReporter.complete();
|
|
24
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { FrameRenderer } from '../FrameRenderer';
|
|
2
|
+
import type { ProgressReporter } from '../ProgressReporter';
|
|
3
|
+
import type { RenderFormat } from '../types';
|
|
4
|
+
/**
|
|
5
|
+
* Renders a scene to a video file using FFmpeg.
|
|
6
|
+
* Frames are rendered sequentially and piped to FFmpeg's stdin.
|
|
7
|
+
*/
|
|
8
|
+
export declare function renderVideo(frameRenderer: FrameRenderer, outputPath: string, format: RenderFormat, frameRate: number, totalDuration: number, progressReporter: ProgressReporter): Promise<void>;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Renders a scene to a video file using FFmpeg.
|
|
3
|
+
* Frames are rendered sequentially and piped to FFmpeg's stdin.
|
|
4
|
+
*/
|
|
5
|
+
export async function renderVideo(frameRenderer, outputPath, format, frameRate, totalDuration, progressReporter) {
|
|
6
|
+
const { width, height } = frameRenderer.getDimensions();
|
|
7
|
+
const totalFrames = Math.max(1, Math.floor(totalDuration * frameRate) + 1);
|
|
8
|
+
// Determine FFmpeg arguments based on format
|
|
9
|
+
const ffmpegArgs = [
|
|
10
|
+
'-y', // Overwrite output
|
|
11
|
+
'-f', 'image2pipe',
|
|
12
|
+
'-vcodec', 'png', // Input format from pipe
|
|
13
|
+
'-r', frameRate.toString(),
|
|
14
|
+
'-i', '-', // Read from stdin
|
|
15
|
+
];
|
|
16
|
+
if (format === 'mp4') {
|
|
17
|
+
ffmpegArgs.push('-c:v', 'libx264', '-pix_fmt', 'yuv420p', '-crf', '18');
|
|
18
|
+
}
|
|
19
|
+
else if (format === 'webp') {
|
|
20
|
+
ffmpegArgs.push('-c:v', 'libwebp', '-lossless', '0', '-compression_level', '4', '-q:v', '75', '-loop', '0');
|
|
21
|
+
}
|
|
22
|
+
else if (format === 'gif') {
|
|
23
|
+
ffmpegArgs.push('-vf', `fps=${frameRate},scale=${width}:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse`);
|
|
24
|
+
}
|
|
25
|
+
ffmpegArgs.push(outputPath);
|
|
26
|
+
const process = Bun.spawn(['ffmpeg', ...ffmpegArgs], {
|
|
27
|
+
stdin: 'pipe',
|
|
28
|
+
});
|
|
29
|
+
try {
|
|
30
|
+
for (let frameIndex = 0; frameIndex < totalFrames; frameIndex++) {
|
|
31
|
+
const time = frameIndex / frameRate;
|
|
32
|
+
const canvas = frameRenderer.renderFrame(time);
|
|
33
|
+
// Convert canvas to PNG buffer and write to FFmpeg's stdin
|
|
34
|
+
const pngBuffer = await canvas.toBuffer('image/png');
|
|
35
|
+
process.stdin.write(pngBuffer);
|
|
36
|
+
progressReporter.reportFrame(frameIndex);
|
|
37
|
+
}
|
|
38
|
+
// Close stdin and wait for FFmpeg to finish
|
|
39
|
+
process.stdin.end();
|
|
40
|
+
const status = await process.exited;
|
|
41
|
+
if (status !== 0) {
|
|
42
|
+
throw new Error(`FFmpeg process exited with code ${status}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
// Kill FFmpeg process if something went wrong
|
|
47
|
+
process.kill();
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
progressReporter.complete();
|
|
51
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { Renderer } from './Renderer';
|
|
2
|
+
export { FrameRenderer } from './FrameRenderer';
|
|
3
|
+
export { ProgressReporter } from './ProgressReporter';
|
|
4
|
+
export { drawMobject } from './drawMobject';
|
|
5
|
+
export type { RenderConfig, ResolvedRenderConfig, RenderFormat, RenderQuality, RenderProgress, ProgressCallback, } from './types';
|
|
6
|
+
export { Resolution } from './types';
|
|
7
|
+
export { writePng, renderSpriteSequence } from './formats';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Renderer Module
|
|
2
|
+
// Provides frame rendering and output generation for Scenes.
|
|
3
|
+
export { Renderer } from './Renderer';
|
|
4
|
+
export { FrameRenderer } from './FrameRenderer';
|
|
5
|
+
export { ProgressReporter } from './ProgressReporter';
|
|
6
|
+
export { drawMobject } from './drawMobject';
|
|
7
|
+
export { Resolution } from './types';
|
|
8
|
+
// Format utilities
|
|
9
|
+
export { writePng, renderSpriteSequence } from './formats';
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supported render output formats.
|
|
3
|
+
* - sprite: PNG sequence (frame_0000.png, frame_0001.png, ...)
|
|
4
|
+
* - png: Single PNG frame
|
|
5
|
+
* - mp4: H.264 video (requires FFmpeg - future)
|
|
6
|
+
* - webp: Animated WebP (requires FFmpeg - future)
|
|
7
|
+
* - gif: Animated GIF (requires FFmpeg - future)
|
|
8
|
+
*/
|
|
9
|
+
export type RenderFormat = 'sprite' | 'png' | 'mp4' | 'webp' | 'gif';
|
|
10
|
+
/**
|
|
11
|
+
* Quality presets for rendering.
|
|
12
|
+
* - production: Full resolution, best quality
|
|
13
|
+
* - preview: Half resolution for faster preview
|
|
14
|
+
*/
|
|
15
|
+
export type RenderQuality = 'production' | 'preview';
|
|
16
|
+
/**
|
|
17
|
+
* Standard resolution presets.
|
|
18
|
+
*/
|
|
19
|
+
export declare const Resolution: {
|
|
20
|
+
/** 854x480 (16:9) */
|
|
21
|
+
readonly p480: {
|
|
22
|
+
readonly width: 854;
|
|
23
|
+
readonly height: 480;
|
|
24
|
+
};
|
|
25
|
+
/** 1280x720 (16:9) */
|
|
26
|
+
readonly p720: {
|
|
27
|
+
readonly width: 1280;
|
|
28
|
+
readonly height: 720;
|
|
29
|
+
};
|
|
30
|
+
/** 1920x1080 (16:9) */
|
|
31
|
+
readonly p1080: {
|
|
32
|
+
readonly width: 1920;
|
|
33
|
+
readonly height: 1080;
|
|
34
|
+
};
|
|
35
|
+
/** 3840x2160 (16:9) */
|
|
36
|
+
readonly p4K: {
|
|
37
|
+
readonly width: 3840;
|
|
38
|
+
readonly height: 2160;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Configuration for rendering.
|
|
43
|
+
*/
|
|
44
|
+
export interface RenderConfig {
|
|
45
|
+
/** Output width in pixels. Defaults to scene width. */
|
|
46
|
+
width?: number;
|
|
47
|
+
/** Output height in pixels. Defaults to scene height. */
|
|
48
|
+
height?: number;
|
|
49
|
+
/** Frames per second. Defaults to scene frame rate. */
|
|
50
|
+
frameRate?: number;
|
|
51
|
+
/** Output format. Defaults to 'sprite'. */
|
|
52
|
+
format?: RenderFormat;
|
|
53
|
+
/** Quality preset. Defaults to 'production'. */
|
|
54
|
+
quality?: RenderQuality;
|
|
55
|
+
/** Progress callback for render updates. */
|
|
56
|
+
onProgress?: ProgressCallback;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Resolved render configuration with all values specified.
|
|
60
|
+
*/
|
|
61
|
+
export interface ResolvedRenderConfig {
|
|
62
|
+
width: number;
|
|
63
|
+
height: number;
|
|
64
|
+
frameRate: number;
|
|
65
|
+
format: RenderFormat;
|
|
66
|
+
quality: RenderQuality;
|
|
67
|
+
onProgress?: ProgressCallback;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Progress information during rendering.
|
|
71
|
+
*/
|
|
72
|
+
export interface RenderProgress {
|
|
73
|
+
/** Current frame being rendered (0-indexed). */
|
|
74
|
+
currentFrame: number;
|
|
75
|
+
/** Total number of frames to render. */
|
|
76
|
+
totalFrames: number;
|
|
77
|
+
/** Percentage complete (0-100). */
|
|
78
|
+
percentage: number;
|
|
79
|
+
/** Elapsed time in milliseconds. */
|
|
80
|
+
elapsedMs: number;
|
|
81
|
+
/** Estimated remaining time in milliseconds. */
|
|
82
|
+
estimatedRemainingMs: number;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Callback for progress updates during rendering.
|
|
86
|
+
*/
|
|
87
|
+
export type ProgressCallback = (progress: RenderProgress) => void;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standard resolution presets.
|
|
3
|
+
*/
|
|
4
|
+
export const Resolution = {
|
|
5
|
+
/** 854x480 (16:9) */
|
|
6
|
+
p480: { width: 854, height: 480 },
|
|
7
|
+
/** 1280x720 (16:9) */
|
|
8
|
+
p720: { width: 1280, height: 720 },
|
|
9
|
+
/** 1920x1080 (16:9) */
|
|
10
|
+
p1080: { width: 1920, height: 1080 },
|
|
11
|
+
/** 3840x2160 (16:9) */
|
|
12
|
+
p4K: { width: 3840, height: 2160 },
|
|
13
|
+
};
|