fix(dgn): use typed stroke patterns
This commit is contained in:
parent
9fdd26e1ed
commit
0c58bdd55a
2 changed files with 49 additions and 57 deletions
|
|
@ -6,11 +6,10 @@
|
|||
//! [`CadDocument::dgn_ls_definitions`] / `dgn_ls_components` instead. See
|
||||
//! `objects/dgn_linestyle.rs` in acadrust and `~/Documents/OCS/DGN_LINESTYLE_PLAN.md`.
|
||||
//!
|
||||
//! The visible content is the **symbol components**, each of which references an
|
||||
//! anonymous block (e.g. a pipe's end circle). This renders those blocks at the
|
||||
//! host polyline's endpoints. The exact placement / scale / dash pattern live in
|
||||
//! the components' leaf data-stream fields, which are not decoded yet, so this is
|
||||
//! an approximation: symbols at native scale on the first and last vertices.
|
||||
//! The visible content combines **symbol components**, each of which references
|
||||
//! an anonymous block (e.g. a pipe's end circle), with typed stroke patterns.
|
||||
//! Symbols are rendered at the host polyline's endpoints and stroke dash/gap
|
||||
//! lengths are carried through to the pipe walls.
|
||||
|
||||
use acadrust::objects::DgnLsComponentType;
|
||||
use acadrust::types::{Handle, Transform, Vector3};
|
||||
|
|
@ -73,36 +72,30 @@ fn walk(doc: &CadDocument, h: Handle, out: &mut Vec<DgnSymbol>, seen: &mut HashS
|
|||
}
|
||||
}
|
||||
|
||||
/// Native dash lengths in a stroke component's leaf. The DGN line-style leaf is
|
||||
/// byte-aligned big-endian f64 (MicroStation origin, not DWG bit-codes); the
|
||||
/// stroke's dash/gap values sit in an 8-byte-aligned run after the shared
|
||||
/// 16-byte class GUID (whose fixed tail is `ae da 14`). Values outside a sane
|
||||
/// size band are skipped (denormals / stream-boundary bytes). Empty when the
|
||||
/// object carries no raw snapshot or no plausible lengths.
|
||||
/// Signed native dash lengths in a typed stroke component: dashes are positive
|
||||
/// and gaps negative. Empty when the component has no usable stroke lengths.
|
||||
fn stroke_dashes(doc: &CadDocument, h: Handle) -> Vec<f64> {
|
||||
use acadrust::objects::ObjectType;
|
||||
let Some(ObjectType::Unknown {
|
||||
raw_dwg_data: Some(d),
|
||||
..
|
||||
}) = doc.objects.get(&h)
|
||||
use acadrust::objects::{DgnLineStyleData, DgnLsComponentData, ObjectType};
|
||||
let Some(ObjectType::DgnLineStyle(style)) = doc.objects.get(&h)
|
||||
else {
|
||||
return Vec::new();
|
||||
};
|
||||
let Some(gi) = d.windows(3).position(|w| w == [0xae, 0xda, 0x14]) else {
|
||||
let DgnLineStyleData::Component {
|
||||
component: DgnLsComponentData::Stroke(pattern),
|
||||
..
|
||||
} = &style.data
|
||||
else {
|
||||
return Vec::new();
|
||||
};
|
||||
// GUID tail (3) + version/type bytes (3) → the first length field.
|
||||
let start = gi + 6;
|
||||
let mut out = Vec::new();
|
||||
let mut i = start;
|
||||
while i + 8 <= d.len() {
|
||||
let v = f64::from_be_bytes(d[i..i + 8].try_into().unwrap());
|
||||
if v.is_finite() && v.abs() >= 0.01 && v.abs() < 1.0e4 {
|
||||
out.push(v);
|
||||
}
|
||||
i += 8;
|
||||
}
|
||||
out
|
||||
pattern
|
||||
.strokes
|
||||
.iter()
|
||||
.filter_map(|stroke| {
|
||||
let length = stroke.length.abs();
|
||||
(length.is_finite() && length > 0.0)
|
||||
.then_some(if stroke.is_dash { length } else { -length })
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Native dash pattern of a DGN line style's pipe walls: the dash lengths of the
|
||||
|
|
|
|||
|
|
@ -1329,46 +1329,45 @@ pub(crate) fn tessellate_entity(
|
|||
|
||||
// DGN line-style: the linetype's real pattern lives in DGN line-style objects
|
||||
// (empty standard LTYPE), so `resolve_complex_lt` sees nothing. Render its
|
||||
// symbol blocks (e.g. a pipe's end circles) at the polyline endpoints. First
|
||||
// pass — exact dash pattern / placement need the undecoded leaf data.
|
||||
// symbol blocks (e.g. a pipe's end circles) at the polyline endpoints and
|
||||
// apply the typed DGN stroke pattern to its parallel walls.
|
||||
let dgn_syms = convert::dgn_linestyle::symbol_blocks(document, lt_name);
|
||||
if !dgn_syms.is_empty() {
|
||||
let verts = convert::dgn_linestyle::polyline_points(e);
|
||||
if verts.len() >= 2 {
|
||||
let display_scale = lt_scale.max(1.0e-4) as f64;
|
||||
// The pipe body is drawn as two parallel walls, not a single centre
|
||||
// line: offset the host polyline by ±(symbol radius) so each wall
|
||||
// sits tangent to the end circles, and replace the centre line with
|
||||
// them. The radius is the rendered symbol extent (block / scale).
|
||||
// them. The radius is the rendered symbol extent after applying the
|
||||
// entity/global linetype display scale.
|
||||
let radius = dgn_syms
|
||||
.iter()
|
||||
.map(|s| convert::dgn_linestyle::symbol_radius(document, s.block, s.scale))
|
||||
.map(|s| {
|
||||
convert::dgn_linestyle::symbol_radius(
|
||||
document,
|
||||
s.block,
|
||||
s.scale / display_scale,
|
||||
)
|
||||
})
|
||||
.fold(0.0_f64, f64::max);
|
||||
if radius > 1e-6 {
|
||||
// The walls carry the line style's dash pattern. Its native
|
||||
// lengths scale to drawing units by f = radius / symbol-scale
|
||||
// (the same factor that turns the compound's native offset into
|
||||
// the measured wall offset). Sign-alternate: dash, gap, dash…
|
||||
let scale = dgn_syms
|
||||
.iter()
|
||||
.map(|s| s.scale)
|
||||
.find(|s| *s > 1e-9)
|
||||
.unwrap_or(1.0);
|
||||
let f = radius / scale;
|
||||
// The wall stroke's dash length (`wall_dashes[0]`) renders as an
|
||||
// equal dash/gap: dash-first `[+dash, -dash]`. Combined with the
|
||||
// `dash_from_start` flag set below, each wall tiles from its own
|
||||
// start vertex with a dash, no A-type end alignment.
|
||||
// Preserve the typed stroke's signed dash/gap sequence and scale
|
||||
// every element by the linetype display scale. Combined with the
|
||||
// `dash_from_start` flag below, each wall tiles from its own start
|
||||
// vertex with no A-type end alignment.
|
||||
let native = convert::dgn_linestyle::wall_dashes(document, lt_name);
|
||||
let (wall_pat, wall_pat_len) = if f > 1e-9 && !native.is_empty() {
|
||||
let dash = (native[0] * f) as f32;
|
||||
if dash > 1e-6 {
|
||||
let mut pat = [0.0_f32; 8];
|
||||
pat[0] = dash;
|
||||
pat[1] = -dash;
|
||||
(pat, 2.0 * dash)
|
||||
} else {
|
||||
([0.0_f32; 8], 0.0)
|
||||
let (wall_pat, wall_pat_len) = if !native.is_empty() {
|
||||
let mut pat = [0.0_f32; 8];
|
||||
let mut length = 0.0_f32;
|
||||
for (slot, value) in pat.iter_mut().zip(native.iter().take(8)) {
|
||||
let scaled = (*value * display_scale) as f32;
|
||||
if scaled.is_finite() && scaled.abs() > 1.0e-6 {
|
||||
*slot = scaled;
|
||||
length += scaled.abs();
|
||||
}
|
||||
}
|
||||
(pat, length)
|
||||
} else {
|
||||
([0.0_f32; 8], 0.0)
|
||||
};
|
||||
|
|
@ -1414,7 +1413,7 @@ pub(crate) fn tessellate_entity(
|
|||
let mut wires = convert::dgn_linestyle::place_block_wires(
|
||||
document,
|
||||
sym.block,
|
||||
sym.scale,
|
||||
sym.scale / display_scale,
|
||||
at,
|
||||
entity_color,
|
||||
line_weight_px,
|
||||
|
|
|
|||
Loading…
Reference in a new issue