diff --git a/crates/engine/src/cmd/file/compare.rs b/crates/engine/src/cmd/file/compare.rs index d4e2878e8..3b6837342 100644 --- a/crates/engine/src/cmd/file/compare.rs +++ b/crates/engine/src/cmd/file/compare.rs @@ -144,7 +144,8 @@ struct Signer<'a> { /// given, and the /// signatures of a multileader and a dimension cover their blocks'. Each object in a block /// then also counts with its visibility, transparency, thickness and extrusion, and an - /// anonymous block's signature leaves out its name. + /// anonymous block's signature leaves out its name (a dimension also leaves its block's name + /// out of its own geometry; an INSERT or MLEADER keeps it). frames: Option>, } @@ -195,7 +196,8 @@ impl<'a> Signer<'a> { let mut h = std::collections::hash_map::DefaultHasher::new(); let block = self.d.block(name); // With frames, an anonymous block (`*D3`, `*U7`) is signed by its contents only: the - // same block can have another generated name in another drawing. + // same block can have another generated name in another drawing. Only a dimension's + // geometry leaves the name out, so this matches renamed `*D` blocks, not `*U` references. if !(self.frames.is_some() && block.is_some_and(|b| b.anonymous)) { key.hash(&mut h); } diff --git a/crates/engine/src/cmd/file/compare/export.rs b/crates/engine/src/cmd/file/compare/export.rs index db41abfba..f3801049d 100644 --- a/crates/engine/src/cmd/file/compare/export.rs +++ b/crates/engine/src/cmd/file/compare/export.rs @@ -439,6 +439,12 @@ fn paint(d: &mut Drawing, e: &mut Entity, color: Color, tinted: &mut Tinted, dep } _ => {} } + if let EntityKind::Dimension(dm) = &mut e.kind { + // A dimension whose block can't be copied (past the limits) is drawn again from its + // geometry: its DIMCLR* overrides are ByBlock, so the original block would keep its colors. + dm.block = dm.block.take().and_then(|n| tinted_block(d, &n, color, tinted, depth)); + return; + } each_ref(&mut e.kind, |n| { if let Some(copy) = tinted_block(d, n, color, tinted, depth) { *n = copy; diff --git a/crates/engine/src/cmd/inquiry.rs b/crates/engine/src/cmd/inquiry.rs index 9962c3fd6..49e0424c2 100644 --- a/crates/engine/src/cmd/inquiry.rs +++ b/crates/engine/src/cmd/inquiry.rs @@ -1054,6 +1054,7 @@ impl Interactive for AreaM { }, (Input::Enter, None) => s.echo("Requires a height."), (Input::Enter, Some(_)) => s.echo("Requires a second point."), + (Input::Text(_), Some(_)) => s.echo("Requires a point."), _ => {} } return Ok(Step::Continue); diff --git a/crates/engine/tests/it/compare_export.rs b/crates/engine/tests/it/compare_export.rs index 36afcdec6..79d28737e 100644 --- a/crates/engine/tests/it/compare_export.rs +++ b/crates/engine/tests/it/compare_export.rs @@ -351,13 +351,28 @@ fn a_named_block_whose_dimension_draws_another_block_shows_the_other_definition( .unwrap_or_default(); assert_eq!(drawn, [Some("*D2".to_string())], "the other drawing's PART is copied with its dimension block"); assert_eq!(ends(&s, "*D2"), vec![5.0]); - // The colored copies redraw their dimensions instead of keeping colored copies of `*D` blocks. + // The colored copies of PART refer to colored copies of the dimension block, which are + // anonymous `*D` blocks of their own, not the originals. let d = s.doc().unwrap(); - let dim_copy = |n: &str| { - n.starts_with("COMPARE_") - && n.rsplit('_').next().is_some_and(|t| t.len() > 1 && t.starts_with('D') && t[1..].bytes().all(|c| c.is_ascii_digit())) - }; - assert!(!d.blocks.keys().any(|n| dim_copy(n)), "{:?}", d.blocks.keys().collect::>()); + let copies: Vec = d + .blocks + .values() + .filter(|b| b.name.starts_with("COMPARE_")) + .flat_map(|b| { + b.entities + .iter() + .filter_map(|e| match &e.kind { + EntityKind::Dimension(dm) => Some(dm.block.clone().unwrap_or_default()), + _ => None, + }) + .collect::>() + }) + .collect(); + assert!(!copies.is_empty(), "PART has colored copies"); + for n in &copies { + assert!(n.starts_with("*D") && !["*D1", "*D2"].contains(&n.as_str()), "{n} in {copies:?}"); + assert!(d.block(n).is_some_and(|b| b.anonymous), "{n} is an anonymous block"); + } } #[test] @@ -789,6 +804,64 @@ fn says_when_blocks_both_drawings_define_differently_were_left_out() { assert!(r["message"].as_str().unwrap().contains("some blocks of the other drawing were too many or too large to copy"), "{}", r["message"]); } +#[test] +fn a_dimension_whose_block_is_past_the_nesting_limit_is_drawn_again() { + use cadcraft_engine::doc::{DimKind, Dimension, MAX_BLOCK_DEPTH}; + // B1 holds B2, …, and the innermost block holds a dimension: its `*D1` copy would be past + // the nesting limit. + let mut s = Session::new(); + let d = s.doc_mut().unwrap(); + let mut drawn = Block::new("*D1"); + drawn.entities.push(Entity::new(Handle(0x600), line(30.0))); + d.blocks.insert("*D1".into(), Arc::new(drawn)); + let dim = Dimension { + kind: DimKind::Linear { rotation: 0.0 }, + defpt: Vec3::new(15.0, 5.0, 0.0), + text_mid: Vec3::new(7.0, 5.0, 0.0), + p13: Vec3::ZERO, + p14: Vec3::new(30.0, 0.0, 0.0), + p15: Vec3::ZERO, + p16: Vec3::ZERO, + text: String::new(), + style: "Standard".into(), + measurement: 30.0, + text_rotation: 0.0, + user_text_pos: false, + block: Some("*D1".into()), + overrides: Default::default(), + assoc: Vec::new(), + marks: Default::default(), + }; + for k in 1..=MAX_BLOCK_DEPTH { + let mut b = Block::new(&format!("B{k}")); + let kind = if k == MAX_BLOCK_DEPTH { EntityKind::Dimension(dim.clone()) } else { EntityKind::Insert(insert(&format!("B{}", k + 1))) }; + b.entities.push(Entity::new(Handle(0x700 + k as u64), kind)); + d.blocks.insert(b.name.clone(), Arc::new(b)); + } + d.add(&Space::Model, Common::default(), EntityKind::Insert(insert("B1"))).unwrap(); + s.open_drawing(Drawing::new_imperial(), "Other", None); + s.execute("document.switch", &json!({"index": 0})).unwrap(); + s.execute("compare", &json!({"document": "Other"})).unwrap(); + s.execute("compareexport", &json!({})).unwrap(); + let d = s.doc().unwrap(); + let dims: Vec> = d + .blocks + .values() + .filter(|b| b.name.starts_with("COMPARE_")) + .flat_map(|b| { + b.entities + .iter() + .filter_map(|e| match &e.kind { + EntityKind::Dimension(dm) => Some(dm.block.clone()), + _ => None, + }) + .collect::>() + }) + .collect(); + // The colored copy keeps no block, so it is drawn in the comparison color, not with `*D1`'s. + assert_eq!(dims, [None], "{:?}", d.blocks.keys().collect::>()); +} + #[test] fn needs_a_comparison_of_the_current_drawing() { let mut s = Session::new();