diff --git a/crates/engine/src/cmd/object.rs b/crates/engine/src/cmd/object.rs index 2d581760..e766eeea 100644 --- a/crates/engine/src/cmd/object.rs +++ b/crates/engine/src/cmd/object.rs @@ -297,7 +297,7 @@ pub fn specs() -> Vec { st.swatch = v.into(); } if let Some(v) = p2.get("tint").and_then(Value::as_f64) { - st.tint = v as f32; + st.tint = v.clamp(0.0, 1.0) as f32; } if let Some(v) = p2.get("weight").and_then(Value::as_f64) { st.weight = v.max(0.0); @@ -309,7 +309,7 @@ pub fn specs() -> Vec { st.gap_swatch = v.into(); } if let Some(v) = p2.get("gapTint").and_then(Value::as_f64) { - st.gap_tint = v as f32; + st.gap_tint = v.clamp(0.0, 1.0) as f32; } if let Some(v) = p2.get("gapOverprint").and_then(Value::as_bool) { st.gap_overprint = v; diff --git a/crates/engine/tests/stroke_tint_range.rs b/crates/engine/tests/stroke_tint_range.rs new file mode 100644 index 00000000..40aca49c --- /dev/null +++ b/crates/engine/tests/stroke_tint_range.rs @@ -0,0 +1,25 @@ +//! `object.stroke` keeps `tint` and `gapTint` within 0..1, so a saved document always reloads. +use designcraft_doc::ItemId; +use designcraft_engine::Session; +use serde_json::json; + +fn stroked(tint: f64, gap_tint: f64) -> (Session, ItemId) { + let mut s = Session::new(); + s.execute("file.new", &json!({})).unwrap(); + let created = s.execute("frame.create", &json!({"rect": [36, 36, 200, 200]})).unwrap(); + let id = ItemId(created["id"].as_u64().unwrap()); + s.execute("object.stroke", &json!({"ids": [id.0], "swatch": "[Black]", "tint": tint, "gapTint": gap_tint})).unwrap(); + (s, id) +} + +#[test] +fn out_of_range_stroke_tints_are_clamped_and_the_saved_file_reloads() { + for (tint, gap, want_tint, want_gap) in [(1e300, 1e300, 1.0, 1.0), (2.0, -1.0, 1.0, 0.0), (-1e300, 0.5, 0.0, 0.5)] { + let (s, id) = stroked(tint, gap); + let doc = &s.doc().unwrap().doc; + let st = &doc.item(id).unwrap().stroke; + assert_eq!((st.tint, st.gap_tint), (want_tint, want_gap), "tint {tint}, gapTint {gap}"); + let bytes = designcraft_format::save(doc).unwrap(); + designcraft_format::load(&bytes).expect("reload what we just saved"); + } +}