forked from 1b-agent/opencadstudio-upstream
Resolve the DIMLFAC sign convention and add the viewport measurement seams
DXF gives DIMLFAC a sign convention: a positive value scales every
dimension, a negative one applies |DIMLFAC| only to dimensions that live in
a paper-space layout (where it compensates a viewport's scale). The linear
formatter multiplied by the raw signed value, so a drawing with negative
DIMLFAC (e.g. 115448.dxf) rendered negative measurements.
Resolve the sign once, in `resolved_dimension_style`, the single place an
effective style is derived, so dimension text, alternate units, exploded and
baked text and the properties panel all agree. `extended_data` keeps the raw
signed value, so DXF/DWG export is unchanged. The properties "Measurement"
row now reports the value the text shows rather than the raw distance, while
`actual_measurement` (group 42) stays raw as the format requires.
Also adds the PR2 seams the dimension work builds on:
* MeasurementScale::user_lfac_for_space / viewport_dimlfac_override —
the persisted representation of viewport compensation.
* Scene::viewport_frame and paper-point viewport resolution.
* Model-geometry hit testing through a content viewport, including
descent into block instances with the instance transform baked in.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
dbdddb7dc2
commit
9ee809d389
4 files changed
+605
-3
No files matched your search
@@ -267,6 +267,65 @@ impl MeasurementScale {
|
||||
pub fn model_factor(&self) -> f64 {
|
||||
self.user_lfac
|
||||
}
|
||||
|
||||
/// The DXF DIMLFAC sign convention, resolved without a [`ViewportFrame`].
|
||||
///
|
||||
/// Rendering an *existing* dimension only knows whether the entity lives
|
||||
/// in a paper-space layout; the viewport compensation is already baked
|
||||
/// into the persisted magnitude (see
|
||||
/// [`MeasurementScale::viewport_dimlfac_override`]). This is the same rule
|
||||
/// as [`MeasurementScale::from_dimlfac`] with `viewport.is_some()`
|
||||
/// replaced by `paper_space`.
|
||||
pub fn user_lfac_for_space(dimlfac: f64, paper_space: bool) -> f64 {
|
||||
if dimlfac == 0.0 {
|
||||
1.0
|
||||
} else if dimlfac > 0.0 {
|
||||
dimlfac
|
||||
} else if paper_space {
|
||||
-dimlfac
|
||||
} else {
|
||||
1.0
|
||||
}
|
||||
}
|
||||
|
||||
/// The persisted representation of viewport compensation, PR2's single
|
||||
/// source of truth.
|
||||
///
|
||||
/// A paper-space dimension that measures model geometry through a viewport
|
||||
/// keeps its definition points in **paper** coordinates and carries a
|
||||
/// **negative DIMLFAC override** whose magnitude is
|
||||
/// `user_lfac * viewport_compensation`. Because a viewport frame is a
|
||||
/// similarity (uniform scale + in-plane twist), the raw paper measurement
|
||||
/// times that magnitude is exactly the model measurement times the user's
|
||||
/// linear factor — so the compensation is applied exactly once, at
|
||||
/// formatting time, and round-trips through DXF/DWG unchanged (AutoCAD
|
||||
/// applies |DIMLFAC| to paper-space dimensions by the same rule).
|
||||
///
|
||||
/// Returns `None` when no override is needed:
|
||||
/// * the compensation is 1:1, or
|
||||
/// * the drawing/style DIMLFAC is already negative with exactly this
|
||||
/// magnitude (the drawing-wide DIMLFAC is set for this purpose).
|
||||
///
|
||||
/// `style_dimlfac` is the effective DIMLFAC the dimension would inherit
|
||||
/// *without* an override.
|
||||
pub fn viewport_dimlfac_override(style_dimlfac: f64, compensation: f64) -> Option<f64> {
|
||||
if !compensation.is_finite() || compensation.abs() < 1e-12 {
|
||||
return None;
|
||||
}
|
||||
if (compensation - 1.0).abs() < 1e-12 {
|
||||
return None;
|
||||
}
|
||||
if style_dimlfac < 0.0
|
||||
&& ((-style_dimlfac) - compensation).abs() <= 1e-9 * compensation.abs().max(1.0)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
// A positive style DIMLFAC is the user's own multiplier and must be
|
||||
// preserved; a negative one of a *different* magnitude was meant for
|
||||
// another viewport scale, so the user multiplier there is 1.0.
|
||||
let user = if style_dimlfac > 0.0 { style_dimlfac } else { 1.0 };
|
||||
Some(-(user * compensation))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -318,4 +377,44 @@ mod tests {
|
||||
assert_eq!(positive.user_lfac, 3.0);
|
||||
assert_eq!(positive.viewport_compensation, 1.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn user_lfac_for_space_matches_from_dimlfac() {
|
||||
let f = frame(0.5, 0.0);
|
||||
for lfac in [-2.0, 0.0, 1.0, 3.5] {
|
||||
assert_eq!(
|
||||
MeasurementScale::user_lfac_for_space(lfac, true),
|
||||
MeasurementScale::from_dimlfac(lfac, Some(&f)).user_lfac
|
||||
);
|
||||
assert_eq!(
|
||||
MeasurementScale::user_lfac_for_space(lfac, false),
|
||||
MeasurementScale::from_dimlfac(lfac, None).user_lfac
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn override_encodes_compensation_once() {
|
||||
// 1:10 viewport, no user factor -> -10, and a 10-unit paper span then
|
||||
// formats as 100.
|
||||
let ovr = MeasurementScale::viewport_dimlfac_override(1.0, 10.0).unwrap();
|
||||
assert!((ovr + 10.0).abs() < 1e-12);
|
||||
let lfac = MeasurementScale::user_lfac_for_space(ovr, true);
|
||||
assert!((10.0 * lfac - 100.0).abs() < 1e-9);
|
||||
// Same dimension read back in model space must not scale.
|
||||
assert_eq!(MeasurementScale::user_lfac_for_space(ovr, false), 1.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn override_preserves_user_multiplier_and_skips_redundant_cases() {
|
||||
assert_eq!(MeasurementScale::viewport_dimlfac_override(1.0, 1.0), None);
|
||||
// Drawing-wide negative DIMLFAC already carries this compensation.
|
||||
assert_eq!(MeasurementScale::viewport_dimlfac_override(-10.0, 10.0), None);
|
||||
// Positive user factor is folded in.
|
||||
let ovr = MeasurementScale::viewport_dimlfac_override(25.4, 10.0).unwrap();
|
||||
assert!((ovr + 254.0).abs() < 1e-9);
|
||||
// Negative but for a different scale -> user multiplier is 1.0.
|
||||
let ovr = MeasurementScale::viewport_dimlfac_override(-2.0, 10.0).unwrap();
|
||||
assert!((ovr + 10.0).abs() < 1e-12);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user