motion_canvas_rs/
project.rs1use crate::core::scene::BaseScene;
2use crate::core::scene::Scene2D;
3use peniko::Color;
4use std::path::PathBuf;
5
6const DEFAULT_FPS: u32 = 60;
7const DEFAULT_WIDTH: u32 = 800;
8const DEFAULT_HEIGHT: u32 = 600;
9const DEFAULT_TITLE: &str = "motion-canvas-rs";
10const DEFAULT_OUTPUT_PATH: &str = "output";
11const DEFAULT_BACKGROUND_COLOR: Color = Color::rgb8(0x1a, 0x1a, 0x1a);
12const DEFAULT_USE_CACHE: bool = true;
13const DEFAULT_USE_GPU: bool = true;
14const DEFAULT_USE_FFMPEG: bool = false;
15const DEFAULT_CACHE_WRITE_INTERVAL: u32 = 500;
16
17pub struct Project {
23 pub width: u32,
25 pub height: u32,
27 pub fps: u32,
29 pub title: String,
31 pub scene: BaseScene,
33 pub output_path: PathBuf,
35 pub use_cache: bool,
37 pub use_ffmpeg: bool,
39 pub use_gpu: bool,
41 pub background_color: Color,
43 pub close_on_finish: bool,
45 pub current_time: std::time::Duration,
47 pub paused: bool,
49 pub speed: f32,
51 pub timeline: crate::core::Timeline,
53 pub cache_write_interval: u32,
55}
56
57impl Project {
58 pub fn new(width: u32, height: u32) -> Self {
60 Self {
61 width,
62 height,
63 fps: DEFAULT_FPS,
64 title: DEFAULT_TITLE.to_string(),
65 scene: BaseScene::new(),
66 output_path: PathBuf::from(DEFAULT_OUTPUT_PATH),
67 use_cache: DEFAULT_USE_CACHE,
68 use_ffmpeg: DEFAULT_USE_FFMPEG,
69 use_gpu: DEFAULT_USE_GPU,
70 background_color: DEFAULT_BACKGROUND_COLOR,
71 close_on_finish: false,
72 current_time: std::time::Duration::ZERO,
73 paused: false,
74 speed: 1.0,
75 timeline: crate::core::Timeline::new(),
76 cache_write_interval: DEFAULT_CACHE_WRITE_INTERVAL,
77 }
78 }
79}
80
81impl Default for Project {
82 fn default() -> Self {
83 Self::new(DEFAULT_WIDTH, DEFAULT_HEIGHT)
84 }
85}
86
87impl Project {
88 pub fn with_fps(mut self, fps: u32) -> Self {
90 self.fps = fps;
91 self
92 }
93
94 pub fn with_dimensions(mut self, width: u32, height: u32) -> Self {
96 self.width = width;
97 self.height = height;
98 self
99 }
100
101 pub fn with_title(mut self, title: &str) -> Self {
103 self.title = title.to_string();
104 self
105 }
106
107 pub fn with_output_path(mut self, path: &str) -> Self {
109 self.output_path = PathBuf::from(path);
110 self
111 }
112
113 pub fn with_cache(mut self, use_cache: bool) -> Self {
115 self.use_cache = use_cache;
116 self
117 }
118
119 pub fn with_cache_write_interval(mut self, interval: u32) -> Self {
121 self.cache_write_interval = interval;
122 self
123 }
124
125 pub fn with_ffmpeg(mut self, use_ffmpeg: bool) -> Self {
127 self.use_ffmpeg = use_ffmpeg;
128 self
129 }
130
131 pub fn with_gpu(mut self, use_gpu: bool) -> Self {
133 self.use_gpu = use_gpu;
134 self
135 }
136
137 pub fn with_background(mut self, color: Color) -> Self {
139 self.background_color = color;
140 self
141 }
142
143 pub fn with_close_on_finish(mut self, close: bool) -> Self {
145 self.close_on_finish = close;
146 self
147 }
148
149 pub fn close_on_finish(self) -> Self {
151 self.with_close_on_finish(true)
152 }
153
154 pub fn seek_to(&mut self, target_time: std::time::Duration) {
158 self.scene.reset();
159 self.current_time = std::time::Duration::ZERO;
160 let dt = std::time::Duration::from_secs_f32(1.0 / self.fps as f32);
161 while self.current_time < target_time {
162 self.scene.update(dt);
163 self.current_time += dt;
164 }
165 }
166
167 pub fn get_frame_name(&self, frame_index: u32) -> String {
169 let sanitized = crate::assets::sanitize_title(&self.title);
170 format!("{}_{:04}.png", sanitized, frame_index)
171 }
172}