diff --git a/src/scene/convert/dgn_linestyle.rs b/src/scene/convert/dgn_linestyle.rs index 3f1ae0cd0..9cb6f3109 100644 --- a/src/scene/convert/dgn_linestyle.rs +++ b/src/scene/convert/dgn_linestyle.rs @@ -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, 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 { - 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 diff --git a/src/scene/convert/tess.rs b/src/scene/convert/tess.rs index b186f9e5e..32eee54de 100644 --- a/src/scene/convert/tess.rs +++ b/src/scene/convert/tess.rs @@ -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,