forked from 1b-agent/opencadstudio-upstream
Merge pull request #1301: Add associative viewport dimensions
Refresh associations after complete SpaceMouse camera updates and recognize imported paper-space owners without Layout objects. Use kernel containment, intersection, perpendicular and spline tangent solving; pin cadkernel 7ce27f2 and add regressions.
This commit is contained in:
commit
d10a0cf798
35 files changed
+7077
-85
No files matched your search
@@ -235,6 +235,65 @@ pub fn set(doc: &mut CadDocument, handle: Handle, code: i16, value: Option<XData
|
||||
write_pairs(doc, handle, kept);
|
||||
}
|
||||
|
||||
/// Set — or, with `value: None`, clear — a single override on an entity that
|
||||
/// is **not yet in the document** (a command's pending commit), leaving the
|
||||
/// other overrides intact.
|
||||
///
|
||||
/// The document-backed [`set`] additionally registers the `ACAD` APPID; that
|
||||
/// APPID is present in every drawing OCS writes, and committing the entity
|
||||
/// goes through the normal add path, so an uncommitted write needs no
|
||||
/// document. Keeping the override on the entity means it is part of the single
|
||||
/// committed object and rides the ordinary undo/redo history.
|
||||
pub fn set_on_entity(entity: &mut EntityType, code: i16, value: Option<XDataValue>) {
|
||||
let common = entity.common_mut();
|
||||
let mut kept: Vec<(i16, XDataValue)> = pairs(&common.extended_data)
|
||||
.into_iter()
|
||||
.filter(|(c, _)| *c != code)
|
||||
.collect();
|
||||
if let Some(v) = value {
|
||||
kept.push((code, v));
|
||||
}
|
||||
|
||||
let legacy = common.extended_data.get_record("ACAD_DSTYLE").is_some()
|
||||
&& common.extended_data.get_record("ACAD").is_some_and(
|
||||
|rec| !matches!(rec.values.first(), Some(XDataValue::String(n)) if n == "DSTYLE"),
|
||||
);
|
||||
let app = if legacy { "ACAD_DSTYLE" } else { "ACAD" };
|
||||
|
||||
let values = if kept.is_empty() {
|
||||
None
|
||||
} else {
|
||||
let mut vals = Vec::new();
|
||||
if !legacy {
|
||||
vals.push(XDataValue::String("DSTYLE".to_string()));
|
||||
}
|
||||
vals.push(XDataValue::ControlString("{".to_string()));
|
||||
for (code, value) in kept {
|
||||
vals.push(XDataValue::Integer16(code));
|
||||
vals.push(value);
|
||||
}
|
||||
vals.push(XDataValue::ControlString("}".to_string()));
|
||||
Some(vals)
|
||||
};
|
||||
|
||||
let mut rebuilt = ExtendedData::new();
|
||||
for record in common.extended_data.records() {
|
||||
if record.application_name != app && !(!legacy && record.application_name == "ACAD_DSTYLE")
|
||||
{
|
||||
rebuilt.add_record(record.clone());
|
||||
}
|
||||
}
|
||||
if let Some(vals) = values {
|
||||
let mut record = acadrust::xdata::ExtendedDataRecord::new(app);
|
||||
for v in vals {
|
||||
record.add_value(v);
|
||||
}
|
||||
rebuilt.add_record(record);
|
||||
}
|
||||
rebuilt.raw_dwg_eed = common.extended_data.raw_dwg_eed.clone();
|
||||
common.extended_data = rebuilt;
|
||||
}
|
||||
|
||||
pub fn property_real_code(field: &str) -> Option<i16> {
|
||||
Some(match field {
|
||||
"dim_arrow_size" => DIMASZ,
|
||||
|
||||
@@ -1613,9 +1613,56 @@ pub(crate) fn resolved_dimension_style(
|
||||
style.dimtxsty = record.name.clone();
|
||||
}
|
||||
}
|
||||
|
||||
// Negative DIMLFAC applies only in paper space; preserve its sign on export.
|
||||
if style.dimlfac < 0.0 {
|
||||
style.dimlfac = crate::scene::viewport_ref::MeasurementScale::user_lfac_for_space(
|
||||
style.dimlfac,
|
||||
dimension_in_paper_space(dimension, document),
|
||||
);
|
||||
}
|
||||
style
|
||||
}
|
||||
|
||||
/// Whether the dimension's owner is paper space, even without a Layout object.
|
||||
pub(crate) fn dimension_in_paper_space(dimension: &Dimension, document: &CadDocument) -> bool {
|
||||
let owner = dimension.base().common.owner_handle;
|
||||
if !owner.is_valid() {
|
||||
return false;
|
||||
}
|
||||
document.objects.values().any(|object| {
|
||||
matches!(object,
|
||||
acadrust::objects::ObjectType::Layout(layout)
|
||||
if layout.name != "Model" && layout.block_record == owner
|
||||
)
|
||||
}) || document.block_records.iter().any(|block| {
|
||||
block.handle == owner && block.name.to_ascii_uppercase().starts_with("*PAPER_SPACE")
|
||||
})
|
||||
}
|
||||
|
||||
/// The value a dimension's text reports for its measurement: the raw
|
||||
/// geometric measurement times the resolved linear factor. Angular dimensions
|
||||
/// never scale.
|
||||
///
|
||||
/// `actual_measurement` (DXF group 42) stays **raw** — that is what the format
|
||||
/// specifies and what export writes — so this is the one helper UI surfaces
|
||||
/// use when they want the number the user reads.
|
||||
pub(crate) fn displayed_measurement(dimension: &Dimension, style: Option<&DimStyle>) -> f64 {
|
||||
let raw = dimension.measurement();
|
||||
if matches!(
|
||||
dimension,
|
||||
Dimension::Angular2Ln(_) | Dimension::Angular3Pt(_)
|
||||
) {
|
||||
return raw;
|
||||
}
|
||||
let lfac = style.map(|s| s.dimlfac).unwrap_or(1.0);
|
||||
if lfac.abs() < 1e-12 {
|
||||
raw
|
||||
} else {
|
||||
raw * lfac
|
||||
}
|
||||
}
|
||||
|
||||
/// Build the linear-dimension property groups from the assigned style plus
|
||||
/// any entity-level DSTYLE overrides. Conditional rows remain visible but are
|
||||
/// read-only while their controlling option is disabled.
|
||||
@@ -2097,7 +2144,13 @@ pub fn style_sections(
|
||||
property(
|
||||
t!("Measurement").as_ref(),
|
||||
"measurement",
|
||||
PropValue::ReadOnly(format!("{:.4}", dimension.measurement())),
|
||||
// The value the dimension text reports (DIMLFAC applied,
|
||||
// including viewport compensation) — not the raw paper
|
||||
// distance, which would disagree with what is drawn.
|
||||
PropValue::ReadOnly(format!(
|
||||
"{:.4}",
|
||||
displayed_measurement(dimension, Some(s))
|
||||
)),
|
||||
),
|
||||
property(
|
||||
t!("Text override").as_ref(),
|
||||
@@ -2196,7 +2249,12 @@ pub fn style_sections(
|
||||
number(
|
||||
t!("Dim scale linear").as_ref(),
|
||||
"dim_scale_linear",
|
||||
real(ov::DIMLFAC, s.dimlfac),
|
||||
// The *raw* signed DIMLFAC: a negative value is the
|
||||
// paper-space / viewport-compensation convention, and
|
||||
// hiding its sign would make the row uneditable in
|
||||
// practice. `s.dimlfac` is sign-resolved, so fall back to
|
||||
// the unresolved source style instead.
|
||||
real(ov::DIMLFAC, style.dimlfac),
|
||||
true,
|
||||
),
|
||||
number(
|
||||
@@ -6403,7 +6461,14 @@ fn format_linear_value(measurement: f64, style: Option<&DimStyle>) -> String {
|
||||
})
|
||||
.unwrap_or((4, 8, 1.0, 0.0, 46, 2, 0, 1.0, ""));
|
||||
|
||||
let lfac = if lfac.abs() < 1e-12 { 1.0 } else { lfac };
|
||||
// `resolved_dimension_style` has already applied the DXF sign convention,
|
||||
// so a negative value here means the caller passed an unresolved style;
|
||||
// fall back to 1.0 rather than reporting a negative length.
|
||||
let lfac = if lfac.abs() < 1e-12 || lfac < 0.0 {
|
||||
1.0
|
||||
} else {
|
||||
lfac
|
||||
};
|
||||
let scaled = measurement * lfac;
|
||||
let use_sub_units = zin & 4 != 0 && scaled.abs() < 1.0 && sub_factor.abs() > 1e-12;
|
||||
// For values below one unit, DIMMZF replaces DIMLFAC; applying it after
|
||||
|
||||
Reference in new issue
Block a user