refactor(dwg): use the first-class SectionSymbol and ViewBorder entities

Section marks and drawing-view borders are now real acadrust entity
types instead of Unknown-with-extras, so the renderer dispatches on the
variant directly, the arrow-direction resolver reads the active viewport
off the ViewBorder entity, and selection / properties show "Section
Symbol" and "View Border" instead of "Unknown". View borders are
non-plotting and tessellate to nothing; both count toward the lossy
Save-As warning since they round-trip via their raw records.

Bumps acadrust for the entity promotion.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hakan Seven 2026-07-21 00:19:51 +03:00
commit 1cfb856224
7 changed files with 48 additions and 29 deletions

2
Cargo.lock generated
View file

@ -73,7 +73,7 @@ checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618"
[[package]]
name = "acadrust"
version = "0.4.0"
source = "git+https://github.com/OpenAEC-Foundation/acadifc?branch=main#8f2cf9543af65353c5fe3f7afbe5139de4ad711c"
source = "git+https://github.com/OpenAEC-Foundation/acadifc?branch=main#6ddff17beca4413df1bad0d441eff9d6aaad298b"
dependencies = [
"ahash 0.8.12",
"anyhow",

View file

@ -346,6 +346,8 @@ pub(super) fn entity_type_key(entity: &acadrust::EntityType) -> String {
AttributeEntity(_) => "attrib",
Ole2Frame(_) => "ole2frame",
Light(_) => "light",
SectionSymbol(_) => "sectionsymbol",
ViewBorder(_) => "viewborder",
Seqend(_) => "seqend",
Unknown(_) => "unknown",
}

View file

@ -55,6 +55,8 @@ pub fn ui_name(e: &EntityType) -> &'static str {
EntityType::AttributeEntity(_) => "Attribute",
EntityType::Ole2Frame(_) => "OLE Frame",
EntityType::Light(_) => "Light",
EntityType::SectionSymbol(_) => "Section Symbol",
EntityType::ViewBorder(_) => "View Border",
EntityType::Seqend(_) => "Seqend",
EntityType::Unknown(_) => "Unknown",
}
@ -103,6 +105,8 @@ pub fn dxf_name(e: &EntityType) -> &'static str {
EntityType::Surface(_) => "SURFACE",
EntityType::Mesh(_) => "MESH",
EntityType::Light(_) => "LIGHT",
EntityType::SectionSymbol(_) => "SECTIONLINE",
EntityType::ViewBorder(_) => "DRAWINGVIEW",
_ => "ENTITY",
}
}

View file

@ -143,6 +143,8 @@ pub fn entity_type_name(et: &EntityType) -> &'static str {
EntityType::Ole2Frame(_) => "Ole2Frame",
EntityType::PolygonMesh(_) => "PolygonMesh",
EntityType::Light(_) => "Light",
EntityType::SectionSymbol(_) => "SectionSymbol",
EntityType::ViewBorder(_) => "ViewBorder",
EntityType::Unknown(_) => "Unknown",
}
}

View file

@ -338,7 +338,12 @@ pub fn dropped_on_save_count(
})
.count();
for e in doc.entities() {
if matches!(e, acadrust::EntityType::Unknown(_)) {
if matches!(
e,
acadrust::EntityType::Unknown(_)
| acadrust::EntityType::SectionSymbol(_)
| acadrust::EntityType::ViewBorder(_)
) {
n += 1;
}
}

View file

@ -113,6 +113,8 @@ pub fn visualization_section(entity: &EntityType) -> Option<PropSection> {
| EntityType::Seqend(_)
| EntityType::Leader(_)
| EntityType::Unknown(_)
// Non-plotting drawing-view border: never rendered, no properties.
| EntityType::ViewBorder(_)
) {
return None;
}

View file

@ -24,8 +24,8 @@ fn fade_if_locked(
/// 2. The parent's refs name the section view's `AcDbViewRep` (any ref that
/// owns an `AcDbViewRepSectionDefinition`), falling back to the document's
/// section views.
/// 3. Each view's refs include its `AcDbViewBorder`, whose first handle is the
/// view's *active* viewport — the entity carrying the real camera.
/// 3. Each view's refs include its `AcDbViewBorder` entity, which carries the
/// view's *active* viewport — the entity holding the real camera.
/// 4. The section camera's sight (−view_direction) is projected onto the parent
/// camera's right/up basis (same convention as `camera_from_view`), giving
/// the on-paper arrow direction.
@ -40,20 +40,23 @@ fn section_arrow_dir_from_views(
if s.view_rep_handle == 0 {
return None;
}
// A view's active viewport, via its border entity among the ViewRep refs.
let border_vp = |h: &AHandle| -> Option<AHandle> {
match document.get_entity(*h)? {
EntityType::ViewBorder(b) if !b.active_viewport.is_null() => Some(b.active_viewport),
_ => None,
}
};
let parent_vr = AHandle::from(s.view_rep_handle);
let parent_refs = document.view_rep_refs.get(&parent_vr)?;
let parent_vp_h = parent_refs
.iter()
.find_map(|h| document.view_border_viewport.get(h))?;
let parent_vp_h = parent_refs.iter().find_map(border_vp)?;
let sect_vr = parent_refs
.iter()
.find(|h| **h != parent_vr && document.section_view_reps.contains(h))
.or_else(|| document.section_view_reps.iter().find(|&&h| h != parent_vr))
.copied()?;
let sect_refs = document.view_rep_refs.get(&sect_vr)?;
let sect_vp_h = sect_refs
.iter()
.find_map(|h| document.view_border_viewport.get(h))?;
let sect_vp_h = sect_refs.iter().find_map(border_vp)?;
let viewport = |h: &AHandle| {
document.entities().find_map(|e| match e {
@ -61,8 +64,8 @@ fn section_arrow_dir_from_views(
_ => None,
})
};
let pvp = viewport(parent_vp_h)?;
let svp = viewport(sect_vp_h)?;
let pvp = viewport(&parent_vp_h)?;
let svp = viewport(&sect_vp_h)?;
// Section sight: view_direction points target→camera, sight is its inverse.
let sight = -glam::DVec3::new(
@ -606,23 +609,24 @@ pub(crate) fn tessellate_entity(
// ── Section symbol (AcDbSectionSymbol): draw the "A-A" cut mark ──────────
//
// Model-Documentation section marks arrive as `Unknown` (class 825); the
// DWG reader decodes the two cut-line endpoints, the signed end ticks and
// the identifier. Synthesize the cut line + end ticks + label glyphs so the
// mark is visible on the layout, the way AutoCAD draws it. The raw record is
// still preserved for lossless write-back.
if let EntityType::Unknown(u) = e {
if let Some(s) = u.section_symbol.as_ref() {
return section_symbol_wires(
document,
s,
document.section_view_style.as_ref(),
h,
entity_color,
sel,
line_weight_px,
);
}
// The DWG reader decodes the two cut-line endpoints, the signed end ticks
// and the identifier. Synthesize the end ticks + arrowheads + label glyphs
// so the mark is visible on the layout, the way AutoCAD draws it. The raw
// record is still preserved for lossless write-back.
if let EntityType::SectionSymbol(s) = e {
return section_symbol_wires(
document,
s,
document.section_view_style.as_ref(),
h,
entity_color,
sel,
line_weight_px,
);
}
// Drawing-view borders are non-plotting aids: nothing is drawn.
if let EntityType::ViewBorder(_) = e {
return vec![];
}
// ── Dimension baked-block fast path ─────────────────────────────────────