forked from 1b-agent/opencadstudio-upstream
The "Dim line color" row on Leader and Dimension entities was read-only: an earlier attempt wired it to Leader.override_color, which acadrust never serialises, so the pick was lost on save. Store it instead as a standard ACAD_DSTYLE per-object dimension-style override (DXF code 176, an ACI index) through the existing dim_override codec, so it round-trips through both DWG and DXF like the other dim overrides. RGB picks collapse to the nearest ACI, matching the rest of the dim-colour stack (dimension styles are index-only through the file layer). The renderer prefers the override over the style's DIMCLRD. The write branch is guarded to leaders and dimensions so a mixed selection cannot stamp the override onto other entity types. Bumps acadrust to c9fe982, which carries parsed XDATA onto dimensions on DXF read (it was dropping common.extended_data), so the dimension override persists on DXF save as well as DWG. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
108 lines
4.0 KiB
Rust
108 lines
4.0 KiB
Rust
//! Per-object dimension-variable overrides.
|
|
//!
|
|
//! A leader (or dimension) that departs from its dimension style stores the
|
|
//! changed variables in the standard `ACAD_DSTYLE` XDATA record as a list of
|
|
//! (dimvar group code, value) pairs wrapped in `{ }` control strings. Both the
|
|
//! renderer and the properties panel prefer an override over the style default,
|
|
//! so editing one of these rows writes here and the change round-trips to file.
|
|
|
|
use acadrust::types::Color;
|
|
use acadrust::xdata::{ExtendedData, XDataValue};
|
|
use acadrust::{CadDocument, Handle};
|
|
|
|
// DXF group codes of the dimension variables surfaced on the leader panel.
|
|
pub const DIMSCALE: i16 = 40; // overall scale (real)
|
|
pub const DIMASZ: i16 = 41; // arrow size (real)
|
|
pub const DIMTAD: i16 = 77; // text vertical pos (int16)
|
|
pub const DIMCLRD: i16 = 176; // dim line colour (int16 = ACI index)
|
|
pub const DIMGAP: i16 = 147; // text offset / gap (real)
|
|
pub const DIMLWD: i16 = 371; // dim line lineweight (int16)
|
|
pub const DIMLDRBLK: i16 = 341; // leader arrow block (handle)
|
|
|
|
/// Every (code, value) override present in the `ACAD_DSTYLE` record.
|
|
pub fn pairs(xd: &ExtendedData) -> Vec<(i16, XDataValue)> {
|
|
let Some(rec) = xd.get_record("ACAD_DSTYLE") else {
|
|
return Vec::new();
|
|
};
|
|
let mut out = Vec::new();
|
|
let mut it = rec.values.iter();
|
|
// The record is a flat stream: a 1070 code marker followed by its typed
|
|
// value, bracketed by 1002 "{" / "}" control strings (which are skipped).
|
|
while let Some(v) = it.next() {
|
|
if let XDataValue::Integer16(code) = v {
|
|
if let Some(val) = it.next() {
|
|
out.push((*code, val.clone()));
|
|
}
|
|
}
|
|
}
|
|
out
|
|
}
|
|
|
|
/// The real-valued override for `code`, if present.
|
|
pub fn real(xd: &ExtendedData, code: i16) -> Option<f64> {
|
|
pairs(xd)
|
|
.into_iter()
|
|
.find(|(c, _)| *c == code)
|
|
.and_then(|(_, v)| match v {
|
|
XDataValue::Real(r) | XDataValue::Distance(r) | XDataValue::ScaleFactor(r) => Some(r),
|
|
_ => None,
|
|
})
|
|
}
|
|
|
|
/// The 16-bit-integer override for `code`, if present.
|
|
pub fn int(xd: &ExtendedData, code: i16) -> Option<i16> {
|
|
pairs(xd)
|
|
.into_iter()
|
|
.find(|(c, _)| *c == code)
|
|
.and_then(|(_, v)| match v {
|
|
XDataValue::Integer16(n) => Some(n),
|
|
_ => None,
|
|
})
|
|
}
|
|
|
|
/// The colour override for `code`, if present. Dim-colour overrides are stored
|
|
/// as an ACI index (the same `int16` slot the dimension style uses), so this
|
|
/// decodes it back into a `Color` (0 = ByBlock, 256 = ByLayer, else indexed).
|
|
pub fn color(xd: &ExtendedData, code: i16) -> Option<Color> {
|
|
int(xd, code).map(Color::from_index)
|
|
}
|
|
|
|
/// The handle-valued override for `code`, if present.
|
|
pub fn handle(xd: &ExtendedData, code: i16) -> Option<Handle> {
|
|
pairs(xd)
|
|
.into_iter()
|
|
.find(|(c, _)| *c == code)
|
|
.and_then(|(_, v)| match v {
|
|
XDataValue::Handle(h) => Some(h),
|
|
_ => None,
|
|
})
|
|
}
|
|
|
|
/// Set — or, with `value: None`, clear — a single override on entity `handle`,
|
|
/// leaving the other overrides in the record untouched. Clearing the last one
|
|
/// drops the whole `ACAD_DSTYLE` record.
|
|
pub fn set(doc: &mut CadDocument, handle: Handle, code: i16, value: Option<XDataValue>) {
|
|
let Some(entity) = doc.get_entity(handle) else {
|
|
return;
|
|
};
|
|
let mut kept: Vec<(i16, XDataValue)> = pairs(&entity.common().extended_data)
|
|
.into_iter()
|
|
.filter(|(c, _)| *c != code)
|
|
.collect();
|
|
if let Some(v) = value {
|
|
kept.push((code, v));
|
|
}
|
|
let values = if kept.is_empty() {
|
|
None
|
|
} else {
|
|
let mut vals = vec![XDataValue::ControlString("{".to_string())];
|
|
for (c, v) in kept {
|
|
vals.push(XDataValue::Integer16(c));
|
|
vals.push(v);
|
|
}
|
|
vals.push(XDataValue::ControlString("}".to_string()));
|
|
Some(vals)
|
|
};
|
|
crate::scene::view::dispatch::set_entity_xdata(doc, handle, "ACAD_DSTYLE", values);
|
|
}
|