Group nested DGN complex elements under their own headers

The element collector keeps a stack of open complex elements instead of
one. A chain, text node or cell inside a cell is gathered on its own and,
once complete, counts as one component of the cell, rather than closing
the cell early and leaving its remaining components at the top level.

A B-spline curve whose poles cannot be read still takes its place in the
open cell, so the cell does not absorb the element after it. An element
that is not a component ends every complex element still open, so a
header whose count overstates its components cannot swallow the rest of
the model.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Hakan SevenandClaude Opus 5.5 committed 2026-10-16 23:12:45 +03:00
1 parent 11ef1efc43
commit a5f965ead4
3 files changed
+56 -16

No files matched your search

+5
View File
@@ -254,6 +254,11 @@ fn read_run(run: &[&[u8]], colors: &[[u8; 3]], d3: bool, depth: usize) -> Raw {
if e[1] & 0x80 != 0 || e.len() < 36 {
continue;
}
// An element that is no component ends the complex elements still
// open.
if e[0] & 0x80 == 0 {
collect.close();
}
let symbology = uint16(e, 34).unwrap_or(0);
let color = colors[(symbology >> 8) as usize];
let element = |geom: DgnGeom| DgnElement {
+13 -1
View File
@@ -686,6 +686,11 @@ fn read_run(run: &[&[u8]], styles: &Styles, xf: &Xf) -> Raw {
// An open B-spline curve: order, pole count, closure and its header.
let mut spline: Option<(usize, usize, bool, DgnElement)> = None;
for (index, e) in run.iter().enumerate() {
// An element that is no component ends the complex elements still
// open.
if !is_component(e) {
collect.close();
}
if e.len() < 104 || styles.hidden.contains(&u32_at(e, 12).unwrap_or(0)) {
continue;
}
@@ -1007,12 +1012,17 @@ fn read_run(run: &[&[u8]], styles: &Styles, xf: &Xf) -> Raw {
continue;
};
let flags = u16::from_le_bytes([lo, hi]);
spline = Some((
// A curve before it that never got its poles still takes
// its place in an open cell.
let unread = spline.replace((
((flags & 0x0f) + 2) as usize,
u32_at(e, 112).unwrap_or(0) as usize,
flags & 0x0180 != 0,
element(DgnGeom::Chain(Vec::new())),
));
if unread.is_some() {
collect.skip();
}
}
21 => {
let n = u32_at(e, 104).unwrap_or(0) as usize;
@@ -1042,6 +1052,8 @@ fn read_run(run: &[&[u8]], styles: &Styles, xf: &Xf) -> Raw {
);
}
collect.push(DgnElement { geom, ..header });
} else {
collect.skip();
}
}
}
+38 -15
View File
@@ -728,26 +728,33 @@ impl DgnGeom {
}
/// Collects elements in file order, gathering the components of a complex
/// chain, shape or text node into their header's element.
/// chain, shape, text node or cell into their header's element.
#[derive(Default)]
pub struct DgnCollector {
pub elements: Vec<DgnElement>,
/// The open complex element and how many components it still takes.
open: Option<(usize, DgnElement)>,
/// The open complex elements, innermost last, each with how many
/// components it still takes. A complex element inside another is one
/// component of it once it is complete.
open: Vec<(usize, DgnElement)>,
}
impl DgnCollector {
/// A complex chain or shape (or a text node) header with `count`
/// A complex chain or shape, a text node or a cell header with `count`
/// components.
pub fn open(&mut self, count: usize, header: DgnElement) {
self.close();
if count > 0 {
self.open = Some((count, header));
self.open.push((count, header));
} else {
self.skip();
}
}
pub fn push(&mut self, element: DgnElement) {
if let Some((left, header)) = self.open.as_mut() {
pub fn push(&mut self, mut element: DgnElement) {
loop {
let Some((left, header)) = self.open.last_mut() else {
self.elements.push(element);
return;
};
match &mut header.geom {
DgnGeom::Chain(parts) | DgnGeom::TextNode { lines: parts, .. } => {
parts.push(element.geom)
@@ -756,18 +763,34 @@ impl DgnCollector {
_ => {}
}
*left -= 1;
if *left == 0 {
self.close();
if *left > 0 {
return;
}
return;
let Some((_, done)) = self.open.pop() else {
return;
};
element = done;
}
self.elements.push(element);
}
/// Ends a complex element cut short with the components it has.
/// A component that converts to nothing (a curve that could not be
/// read) still takes its place in the open complex element.
pub fn skip(&mut self) {
if let Some((left, _)) = self.open.last_mut() {
*left -= 1;
if *left == 0 {
if let Some((_, done)) = self.open.pop() {
self.push(done);
}
}
}
}
/// Ends the complex elements cut short with the components they have
/// (an element that is no component ends them all).
pub fn close(&mut self) {
if let Some((_, header)) = self.open.take() {
self.elements.push(header);
while let Some((_, header)) = self.open.pop() {
self.push(header);
}
}
}