diff --git a/crates/effects/src/stylize2.rs b/crates/effects/src/stylize2.rs index d1020b1bc..94eda6370 100644 --- a/crates/effects/src/stylize2.rs +++ b/crates/effects/src/stylize2.rs @@ -827,8 +827,8 @@ mod tests { 0.0, ); assert_eq!((out.img.width, out.img.height), (10, 8)); - for (x, mirrored) in [(2u32, 0u32), (7, 9)] { - for y in 0..out.img.height { + for (x, mirrored) in [(2i64, 0i64), (7, 9)] { + for y in 0..i64::from(out.img.height) { let a = out.img.get(x, y); assert!((a[3] - 1.0).abs() < 1e-5, "fold x={x},y={y} faded to alpha {}", a[3]); let b = img.get(mirrored, y); diff --git a/crates/engine/src/cmd/model3d.rs b/crates/engine/src/cmd/model3d.rs index f4e62f6b7..37a70db00 100644 --- a/crates/engine/src/cmd/model3d.rs +++ b/crates/engine/src/cmd/model3d.rs @@ -245,7 +245,7 @@ fn apply_material(l: &mut Layer, p: &Value, t: Tick, cmd: &str) -> Result, missing: &[&str]) -> EngineError { let names = missing.iter().map(|k| format!("`{k}`")).collect::>().join(", "); let Some(l) = l else { return bad("material.set", format!("no layer has {names}")) }; - let Some(g) = l.props.sub("materialOptions") else { return bad("material.set", format!("`{}` has no Material Options", l.name)) }; + let Some(g) = l.props.sub("materialOptions") else { return bad("material.set", format!("`{}` has no Material Options, so it has no {names}", l.name)) }; let has: Vec<&str> = g.children.iter().map(|c| c.match_id()).collect(); let why = if matches!(l.source, LayerSource::Model { .. }) { ": a 3D model layer keeps its model file's materials" } else { "" }; bad( diff --git a/crates/interchange/src/import.rs b/crates/interchange/src/import.rs index 6831103a7..b7095ae1f 100644 --- a/crates/interchange/src/import.rs +++ b/crates/interchange/src/import.rs @@ -185,6 +185,9 @@ pub fn import( const EDL_MEDIA_TIME_NOTE: &str = "EDL source timecodes were imported as media time"; /// An EDL's source times are its source timecodes, which FilmCraft's reader keeps as media time. +/// The largest start timecode, in frames, a file may have: a day at 1000 fps. +const MAX_FILE_TIMECODE: i64 = 24 * 3600 * 1000; + /// Put the clips of each file that has a start timecode on the file's own time, as FilmCraft does /// when it links the media of an imported EDL. Returns the names of the media that stay on /// timecode though their clips use an hour or more of it (the file is missing or has none). @@ -212,7 +215,9 @@ fn rebase_to_file_timecode(project: &mut fp::Project, probe: &mut dyn FnMut(&str let mut left = Vec::new(); for (id, name, path, offline) in files { match if offline { None } else { probe(&path) } { - Some(Footage { file_timecode: Some(tc), native_rate, frame_rate, .. }) => { + // A corrupt timecode past a day of frames (at up to 1000 fps) would overflow the tick + // arithmetic; such a file stays on timecode. + Some(Footage { file_timecode: Some(tc), native_rate, frame_rate, .. }) if (0..=MAX_FILE_TIMECODE).contains(&tc) => { filmcraft_interchange::rebase_source_timecode(project, id, tc, crate::rate_out(native_rate.unwrap_or(frame_rate))); } _ if on_timecode.contains(&id) => left.push(name), diff --git a/crates/project/src/item.rs b/crates/project/src/item.rs index 047f1ef3f..ae66557a4 100644 --- a/crates/project/src/item.rs +++ b/crates/project/src/item.rs @@ -12,7 +12,7 @@ pub(crate) fn numbered_name(base: &str, taken: impl Fn(&str) -> bool) -> String let head = base.trim_end_matches(|c: char| c.is_ascii_digit()); let stem = match head.strip_suffix(' ') { Some(stem) if head.len() < base.len() && !stem.trim_end().is_empty() => stem.trim_end(), - _ => base, + _ => base.trim_end(), }; (2u64..).map(|k| format!("{stem} {k}")).find(|n| !taken(n)).unwrap_or_default() } diff --git a/crates/ui-egui/tests/it/ui_scrolling.rs b/crates/ui-egui/tests/it/ui_scrolling.rs index 001eb4503..2913a6770 100644 --- a/crates/ui-egui/tests/it/ui_scrolling.rs +++ b/crates/ui-egui/tests/it/ui_scrolling.rs @@ -409,13 +409,13 @@ fn panel_rows_stop_before_their_scroll_bar() { assert!(scrolled(&h, bar).1 > 0.0, "{bar}: the rows overflow"); let track = rect(&h, bar); if let Some(right) = rightmost(&h, prefix, bar, Some(track)) { - if right > track.min.x { eprintln!("MUT {}", format!("{bar}: the rows end at {right}, on the track {track:?}")); } + assert!(right <= track.min.x, "{bar}: the rows end at {right}, on the track {track:?}"); } else { assert!(matches!(bar, "info.scroll" | "markers.scroll"), "{bar}: no rows beside the track"); } let lane = Rect::from_x_y_ranges(track.min.x..=track.max.x, track.y_range()); for (text, at) in painted_text(&h) { - if at.intersects(lane) { eprintln!("MUT {}", format!("{bar}: {text:?} shows at {at:?}, on the track {track:?}")); } + assert!(!at.intersects(lane), "{bar}: {text:?} shows at {at:?}, on the track {track:?}"); } } let _ = std::fs::remove_dir_all(&folder);