Skip to content

Commit 052001e

Browse files
Merge pull request #2821 from yashranaway/fix/sort-scene-segments-on-load
fix(project): sort scene segments when a project loads
2 parents 3b2b22d + 4176f15 commit 052001e

3 files changed

Lines changed: 94 additions & 1 deletion

File tree

‎crates/project/src/configuration.rs‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1824,6 +1824,15 @@ where
18241824
Ok(by_segment.into_values().collect())
18251825
}
18261826

1827+
fn deserialize_scene_segments<'de, D>(deserializer: D) -> Result<Vec<SceneSegment>, D::Error>
1828+
where
1829+
D: serde::Deserializer<'de>,
1830+
{
1831+
let mut segments = Vec::<SceneSegment>::deserialize(deserializer)?;
1832+
segments.sort_by(|left, right| left.start.total_cmp(&right.start));
1833+
Ok(segments)
1834+
}
1835+
18271836
#[derive(Type, Serialize, Deserialize, Clone, Debug)]
18281837
#[serde(rename_all = "camelCase")]
18291838
pub struct TimelineConfiguration {
@@ -1835,7 +1844,7 @@ pub struct TimelineConfiguration {
18351844
)]
18361845
pub transitions: Vec<ClipTransition>,
18371846
pub zoom_segments: Vec<ZoomSegment>,
1838-
#[serde(default)]
1847+
#[serde(default, deserialize_with = "deserialize_scene_segments")]
18391848
pub scene_segments: Vec<SceneSegment>,
18401849
#[serde(default)]
18411850
pub mask_segments: Vec<MaskSegment>,
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
use cap_project::ProjectConfiguration;
2+
use serde_json::json;
3+
4+
#[test]
5+
fn loading_sorts_scenes_without_changing_clip_edits() {
6+
let project = tempfile::tempdir().unwrap();
7+
let path = project.path().join("project-config.json");
8+
std::fs::write(
9+
&path,
10+
serde_json::to_vec(&json!({
11+
"timeline": {
12+
"segments": [
13+
{ "recordingSegment": 1, "start": 20.0, "end": 22.0, "timescale": 0.25, "name": "Opening", "volume": 0.5 },
14+
{ "recordingSegment": 0, "start": 10.0, "end": 10.0, "timescale": 1.0 },
15+
{ "recordingSegment": 0, "start": 0.0, "end": 32.0, "timescale": 8.0 }
16+
],
17+
"zoomSegments": [],
18+
"sceneSegments": [
19+
{ "start": 8.0, "end": 10.0, "mode": "cameraOnly" },
20+
{ "start": 1.0, "end": 3.0, "mode": "cameraOnly" },
21+
{ "start": 5.0, "end": 7.0, "mode": "hideCamera" }
22+
]
23+
}
24+
}))
25+
.unwrap(),
26+
)
27+
.unwrap();
28+
29+
let loaded = ProjectConfiguration::load(project.path()).unwrap();
30+
loaded.write(project.path()).unwrap();
31+
let reloaded = ProjectConfiguration::load(project.path()).unwrap();
32+
for config in [&loaded, &reloaded] {
33+
let timeline = config.timeline.as_ref().unwrap();
34+
assert_eq!(
35+
timeline
36+
.scene_segments
37+
.iter()
38+
.map(|scene| scene.start)
39+
.collect::<Vec<_>>(),
40+
[1.0, 5.0, 8.0]
41+
);
42+
assert_eq!(timeline.duration(), 12.0);
43+
let (source_time, clip) = timeline.get_segment_time(0.0).unwrap();
44+
assert_eq!((source_time, clip.recording_clip), (20.0, 1));
45+
assert_eq!(clip.name.as_deref(), Some("Opening"));
46+
assert_eq!(clip.volume(), 0.5);
47+
let (source_time, clip) = timeline.get_segment_time(9.0).unwrap();
48+
assert_eq!((source_time, clip.recording_clip), (8.0, 0));
49+
assert_eq!(timeline.segments.len(), 3);
50+
assert_eq!(timeline.segments[1].duration(), 0.0);
51+
}
52+
}

‎crates/rendering/src/scene.rs‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,38 @@ mod tests {
382382
}
383383
}
384384

385+
#[test]
386+
fn loaded_unsorted_scenes_fade_into_the_next_scene() {
387+
let project = tempfile::tempdir().unwrap();
388+
std::fs::write(
389+
project.path().join("project-config.json"),
390+
r#"{
391+
"timeline": {
392+
"segments": [{ "recordingSegment": 0, "start": 0.0, "end": 12.0, "timescale": 1.0 }],
393+
"zoomSegments": [],
394+
"sceneSegments": [
395+
{ "start": 8.0, "end": 10.0, "mode": "cameraOnly" },
396+
{ "start": 1.0, "end": 3.0, "mode": "cameraOnly" },
397+
{ "start": 5.0, "end": 7.0, "mode": "hideCamera" }
398+
]
399+
}
400+
}"#,
401+
)
402+
.unwrap();
403+
let config = cap_project::ProjectConfiguration::load(project.path()).unwrap();
404+
let segments = &config.timeline.as_ref().unwrap().scene_segments;
405+
let before = scene_at(4.6, segments);
406+
let during = scene_at(4.85, segments);
407+
let after = scene_at(5.0, segments);
408+
assert_eq!(before.camera_opacity, 1.0);
409+
assert!(
410+
(during.camera_opacity - 0.5).abs() < 1e-5,
411+
"expected the camera halfway faded out, got {}",
412+
during.camera_opacity
413+
);
414+
assert_eq!(after.camera_opacity, 0.0);
415+
}
416+
385417
#[test]
386418
fn camera_only_blur_tracks_motion_at_both_boundaries() {
387419
let segments = [camera_scene(0.3)];

0 commit comments

Comments
 (0)