forked from 1b-agent/opencadstudio-upstream
fix: harden dimension and constraint bar changes
This commit is contained in:
1 parent
c8e34e47ef
commit
8429ad50ec
3 files changed
+56
-18
No files matched your search
+19
-17
@@ -1669,9 +1669,9 @@ pub(crate) fn resolved_dimension_style(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Negative DIMLFAC applies only in paper space, and there only to a
|
// Negative DIMLFAC applies only in paper space, and there only to a
|
||||||
// dimension that measures model space through a viewport. AutoCAD writes
|
// dimension that measures model space through a viewport. The associative
|
||||||
// the factor it actually applied as ACAD_DIMASSOC_CALC_DIMLFAC: zero for a
|
// record stores the applied factor; zero marks paper-space geometry whose
|
||||||
// dimension of paper-space geometry, which then reads its sheet distance.
|
// dimension reads its sheet distance.
|
||||||
if style.dimlfac < 0.0 {
|
if style.dimlfac < 0.0 {
|
||||||
style.dimlfac = match calculated_dimlfac(dimension) {
|
style.dimlfac = match calculated_dimlfac(dimension) {
|
||||||
Some(calculated) if calculated.abs() < 1e-12 => 1.0,
|
Some(calculated) if calculated.abs() < 1e-12 => 1.0,
|
||||||
@@ -1685,7 +1685,7 @@ pub(crate) fn resolved_dimension_style(
|
|||||||
style
|
style
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The linear factor AutoCAD applied when it last regenerated the dimension.
|
/// The linear factor recorded when the dimension was last regenerated.
|
||||||
fn calculated_dimlfac(dimension: &Dimension) -> Option<f64> {
|
fn calculated_dimlfac(dimension: &Dimension) -> Option<f64> {
|
||||||
dimension
|
dimension
|
||||||
.base()
|
.base()
|
||||||
@@ -1694,7 +1694,7 @@ fn calculated_dimlfac(dimension: &Dimension) -> Option<f64> {
|
|||||||
.get_record("ACAD_DIMASSOC_CALC_DIMLFAC")
|
.get_record("ACAD_DIMASSOC_CALC_DIMLFAC")
|
||||||
.and_then(|record| {
|
.and_then(|record| {
|
||||||
record.values.iter().find_map(|value| match value {
|
record.values.iter().find_map(|value| match value {
|
||||||
acadrust::xdata::XDataValue::Real(factor) => Some(*factor),
|
acadrust::xdata::XDataValue::Real(factor) if factor.is_finite() => Some(*factor),
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -4437,8 +4437,8 @@ fn text_fill_rect(
|
|||||||
}
|
}
|
||||||
let pos = dimension_text_pos_f64(dim, style, text_height, dim_scale);
|
let pos = dimension_text_pos_f64(dim, style, text_height, dim_scale);
|
||||||
let dimgap = style.map(|s| s.dimgap.abs()).unwrap_or(0.0) * dim_scale;
|
let dimgap = style.map(|s| s.dimgap.abs()).unwrap_or(0.0) * dim_scale;
|
||||||
// ~0.6 × text_height per character; matches average glyph aspect for
|
// CELL_WIDTH × text_height per character approximates the average glyph
|
||||||
// the bundled stick fonts. Inflate by 1 DIMGAP on each side.
|
// aspect of the bundled stick fonts. Inflate by 1 DIMGAP on each side.
|
||||||
let stack_scale = style.map(dimtfac_or_one).unwrap_or(1.0);
|
let stack_scale = style.map(dimtfac_or_one).unwrap_or(1.0);
|
||||||
let approx_w = text_cells(&value, stack_scale) * text_height * CELL_WIDTH + dimgap * 2.0;
|
let approx_w = text_cells(&value, stack_scale) * text_height * CELL_WIDTH + dimgap * 2.0;
|
||||||
let approx_h = dimension_text_extent_height(dim, style, text_height) + dimgap * 2.0;
|
let approx_h = dimension_text_extent_height(dim, style, text_height) + dimgap * 2.0;
|
||||||
@@ -6366,8 +6366,8 @@ fn dimension_text_parts(
|
|||||||
};
|
};
|
||||||
let primary_raw = match dim {
|
let primary_raw = match dim {
|
||||||
Dimension::Radius(_) | Dimension::LargeRadial(_) => format!("R{}", value),
|
Dimension::Radius(_) | Dimension::LargeRadial(_) => format!("R{}", value),
|
||||||
// U+2205 is the diameter sign AutoCAD writes (and what `%%c` resolves
|
// U+2205 is the native diameter sign and what `%%c` resolves to; the
|
||||||
// to); the bundled dimension fonts carry it where Latin Ø is missing.
|
// bundled dimension fonts carry it where Latin Ø is missing.
|
||||||
Dimension::Diameter(_) => format!("∅{}", value),
|
Dimension::Diameter(_) => format!("∅{}", value),
|
||||||
_ => value,
|
_ => value,
|
||||||
};
|
};
|
||||||
@@ -7323,9 +7323,9 @@ fn text_on_single_arrow_dim_line(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// AutoCAD writes the text middle point (group 11) for automatic and hand
|
/// The stored text middle point (group 11) applies to automatic and hand-placed
|
||||||
/// placed text alike, so a non-zero point is where the text was drawn and a
|
/// text alike. A regenerated dimension keeps any non-zero point; only a cleared
|
||||||
/// regenerated dimension keeps it. Only a cleared point is placed from the style.
|
/// point is placed from the style.
|
||||||
fn stored_text_point(dim: &Dimension) -> Option<Vector3> {
|
fn stored_text_point(dim: &Dimension) -> Option<Vector3> {
|
||||||
let p = dim.base().text_middle_point;
|
let p = dim.base().text_middle_point;
|
||||||
(p.x * p.x + p.y * p.y + p.z * p.z > 1e-16).then_some(p)
|
(p.x * p.x + p.y * p.y + p.z * p.z > 1e-16).then_some(p)
|
||||||
@@ -8622,8 +8622,8 @@ mod layout_parity_tests {
|
|||||||
d
|
d
|
||||||
}
|
}
|
||||||
|
|
||||||
// AutoCAD writes group 11 for every dimension; the regenerated text goes
|
// Group 11 is authoritative whenever present, whether or not the user moved
|
||||||
// where the file says it was, moved by the user or not.
|
// the text.
|
||||||
#[test]
|
#[test]
|
||||||
fn stored_text_point_places_automatic_text() {
|
fn stored_text_point_places_automatic_text() {
|
||||||
let document = document();
|
let document = document();
|
||||||
@@ -8681,10 +8681,10 @@ mod layout_parity_tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// A negative DIMLFAC scales only a paper-space dimension that measures
|
// A negative DIMLFAC scales only a paper-space dimension that measures
|
||||||
// model space through a viewport. AutoCAD records the factor it applied;
|
// model space through a viewport. The association record carries the
|
||||||
// zero means the dimension reads its sheet distance unscaled.
|
// applied factor; zero means the dimension reads its sheet distance.
|
||||||
#[test]
|
#[test]
|
||||||
fn negative_dimlfac_follows_the_factor_autocad_applied() {
|
fn negative_dimlfac_follows_the_recorded_association_factor() {
|
||||||
use acadrust::xdata::{ExtendedDataRecord, XDataValue};
|
use acadrust::xdata::{ExtendedDataRecord, XDataValue};
|
||||||
let document = CadDocument::new();
|
let document = CadDocument::new();
|
||||||
let mut source = style(&document);
|
let mut source = style(&document);
|
||||||
@@ -8704,6 +8704,8 @@ mod layout_parity_tests {
|
|||||||
assert_eq!(through_viewport.dimlfac, 4.0, "the recorded factor, made positive");
|
assert_eq!(through_viewport.dimlfac, 4.0, "the recorded factor, made positive");
|
||||||
let unknown = resolved_dimension_style(&source, &Dimension::Linear(d.clone()), &document);
|
let unknown = resolved_dimension_style(&source, &Dimension::Linear(d.clone()), &document);
|
||||||
assert_eq!(unknown.dimlfac, 1.0, "model space without a record: unscaled");
|
assert_eq!(unknown.dimlfac, 1.0, "model space without a record: unscaled");
|
||||||
|
let invalid = resolved_dimension_style(&source, &with_factor(f64::NAN), &document);
|
||||||
|
assert_eq!(invalid.dimlfac, 1.0, "invalid recorded factor: unscaled");
|
||||||
}
|
}
|
||||||
|
|
||||||
// DIMTMOVE 1: the stored point is where the hook starts. The hook runs
|
// DIMTMOVE 1: the stored point is where the hook starts. The hook runs
|
||||||
|
|||||||
@@ -197,7 +197,7 @@ pub fn text_local_bounds(
|
|||||||
/// Expand DXF `%%x` special-character sequences that appear in both TEXT and MTEXT values:
|
/// Expand DXF `%%x` special-character sequences that appear in both TEXT and MTEXT values:
|
||||||
/// - `%%d` / `%%D` → `°`
|
/// - `%%d` / `%%D` → `°`
|
||||||
/// - `%%p` / `%%P` → `±`
|
/// - `%%p` / `%%P` → `±`
|
||||||
/// - `%%c` / `%%C` → `∅` (U+2205, the character AutoCAD writes for the diameter sign)
|
/// - `%%c` / `%%C` → `∅` (U+2205, the native diameter sign)
|
||||||
/// - `%%u` / `%%U` → underline toggle (stripped — not renderable with stroke fonts)
|
/// - `%%u` / `%%U` → underline toggle (stripped — not renderable with stroke fonts)
|
||||||
/// - `%%o` / `%%O` → overline toggle (stripped)
|
/// - `%%o` / `%%O` → overline toggle (stripped)
|
||||||
/// - `%%%%` → `%`
|
/// - `%%%%` → `%`
|
||||||
|
|||||||
@@ -64,3 +64,39 @@ impl CadCommand for ConstraintBarOptionCommand {
|
|||||||
inventory::submit!(crate::command::CommandRegistration {
|
inventory::submit!(crate::command::CommandRegistration {
|
||||||
names: &["CONSTRAINTBAR"]
|
names: &["CONSTRAINTBAR"]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn relaunch(result: CmdResult) -> (String, Vec<Handle>) {
|
||||||
|
let CmdResult::Relaunch(command, handles) = result else {
|
||||||
|
panic!("expected relaunch");
|
||||||
|
};
|
||||||
|
(command, handles)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn enter_defaults_to_show_and_keeps_the_selection() {
|
||||||
|
let handles = vec![Handle::new(2), Handle::new(5)];
|
||||||
|
let mut command = ConstraintBarOptionCommand::new(handles.clone());
|
||||||
|
|
||||||
|
assert_eq!(relaunch(command.on_enter()), ("GCSHOW".to_string(), handles));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hide_and_reset_route_to_their_apply_commands() {
|
||||||
|
let handle = Handle::new(7);
|
||||||
|
let mut hide = ConstraintBarOptionCommand::new(vec![handle]);
|
||||||
|
assert_eq!(
|
||||||
|
relaunch(hide.on_text_input("hide").unwrap()),
|
||||||
|
("GCHIDE".to_string(), vec![handle])
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut reset = ConstraintBarOptionCommand::new(vec![handle]);
|
||||||
|
assert_eq!(
|
||||||
|
relaunch(reset.on_text_input("r").unwrap()),
|
||||||
|
("CONSTRAINTBAR_RESET".to_string(), vec![handle])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user