Merge pull request #776 from NgoQuocViet2001/fix-unitless-block-insert-scale

fix(blocks): don't unit-scale unitless block inserts
This commit is contained in:
Hakan Seven authored and GitHub committed 2026-08-17 21:17:55 +03:00
commit a65ba28270
1 file changed
+68 -16
+68 -16
View File
@@ -1794,29 +1794,20 @@ impl OpenCADStudio {
// INSUNITS: when inserting a block whose BlockRecord.units differ
// from the host's header.insertion_units, scale the new INSERT so
// 1 source-unit equals the matching host length. When either side
// is unitless (0) AutoCAD falls back to MEASUREMENT (0 = Imperial /
// inches, 1 = Metric / mm); honour the same fallback.
// 1 source-unit equals the matching host length.
if let acadrust::EntityType::Insert(ref mut ins) = entity {
let header = &self.tabs[i].scene.document.header;
let measurement_fallback = if header.measurement == 1 { 4 } else { 1 };
let host_raw = header.insertion_units;
let host_units = if host_raw == 0 { measurement_fallback } else { host_raw };
let src_raw = self.tabs[i]
let host_units = self.tabs[i].scene.document.header.insertion_units;
let src_units = self.tabs[i]
.scene
.document
.block_records
.get(&ins.block_name)
.map(|br| br.units)
.unwrap_or(0);
let src_units = if src_raw == 0 { measurement_fallback } else { src_raw };
if src_units != host_units {
let ratio = insunits_to_mm(src_units) / insunits_to_mm(host_units);
if ratio.is_finite() && (ratio - 1.0).abs() > 1e-9 {
ins.set_x_scale(ratio);
ins.set_y_scale(ratio);
ins.set_z_scale(ratio);
}
if let Some(ratio) = insert_unit_scale(host_units, src_units) {
ins.set_x_scale(ratio);
ins.set_y_scale(ratio);
ins.set_z_scale(ratio);
}
}
@@ -2383,6 +2374,24 @@ fn insunits_name(code: i16) -> &'static str {
}
}
/// Unit-conversion scale for a new INSERT, or `None` when the reference should
/// keep scale 1.
///
/// A unitless (0) block carries no length to convert from, and a unitless
/// drawing none to convert to, so either side being unitless means the
/// reference is inserted as authored. This matches the `unit_factor` row shown
/// for the same insert in the properties panel.
fn insert_unit_scale(host_units: i16, src_units: i16) -> Option<f64> {
if host_units == 0 || src_units == 0 || src_units == host_units {
return None;
}
let ratio = insunits_to_mm(src_units) / insunits_to_mm(host_units);
if !ratio.is_finite() || (ratio - 1.0).abs() <= 1e-9 {
return None;
}
Some(ratio)
}
/// Convert INSUNITS (DXF group 70) to millimetres.
/// 0 = unitless / unknown: returns 1.0 so the caller treats it as "do not scale".
fn insunits_to_mm(code: i16) -> f64 {
@@ -2411,3 +2420,46 @@ fn insunits_to_mm(code: i16) -> f64 {
_ => 1.0,
}
}
#[cfg(test)]
mod insert_unit_scale_tests {
use super::insert_unit_scale;
const UNITLESS: i16 = 0;
const INCHES: i16 = 1;
const MILLIMETERS: i16 = 4;
const CENTIMETERS: i16 = 5;
const METERS: i16 = 6;
#[test]
fn unitless_block_is_inserted_as_authored() {
// Reported case: a unitless block with a 1000-unit edge inserted into a
// millimetre drawing must keep that edge, not gain a conversion factor.
assert_eq!(insert_unit_scale(MILLIMETERS, UNITLESS), None);
assert_eq!(insert_unit_scale(INCHES, UNITLESS), None);
}
#[test]
fn unitless_drawing_does_not_scale_a_measured_block() {
assert_eq!(insert_unit_scale(UNITLESS, METERS), None);
assert_eq!(insert_unit_scale(UNITLESS, INCHES), None);
assert_eq!(insert_unit_scale(UNITLESS, UNITLESS), None);
}
#[test]
fn matching_units_do_not_scale() {
assert_eq!(insert_unit_scale(METERS, METERS), None);
}
#[test]
fn differing_units_convert_through_millimetres() {
let ratio = insert_unit_scale(MILLIMETERS, METERS).expect("metres into mm should scale");
assert!((ratio - 1000.0).abs() < 1e-9, "got {ratio}");
let ratio = insert_unit_scale(MILLIMETERS, INCHES).expect("inches into mm should scale");
assert!((ratio - 25.4).abs() < 1e-9, "got {ratio}");
let ratio = insert_unit_scale(METERS, CENTIMETERS).expect("cm into m should scale");
assert!((ratio - 0.01).abs() < 1e-12, "got {ratio}");
}
}