fix: identify user viewports by geometry when id is 0 or negative

The acadrust DWG reader never assigns a viewport id (always 0).
Some DXF exporters use id=-1 for all viewports.  In both cases the
previous id > 1 check excluded every viewport from content rendering.

Replace is_user_viewport_id(id) with is_content_viewport(vp) that
uses a geometry heuristic for unknown ids (0 / negative):
  - id=1           → always sheet viewport (excluded)
  - id≥2           → always user viewport  (included)
  - id=0 or id<0   → sheet if center≈(0,0) AND scale≈1.0, else user

This allows DWG files and non-standard DXF files to display model
content inside their paper-space viewports and be entered via
double-click.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Hakan Seven 2026-04-30 22:06:43 +03:00
commit 6a0b9169d6
2 changed files with 29 additions and 13 deletions

View file

@ -1696,7 +1696,7 @@ impl H7CAD {
if let Some(AcadEntityType::Viewport(vp)) = if let Some(AcadEntityType::Viewport(vp)) =
self.tabs[i].scene.document.get_entity(h) self.tabs[i].scene.document.get_entity(h)
{ {
if Scene::is_user_viewport_id(vp.id) { Some(h) } else { None } if Scene::is_content_viewport(vp) { Some(h) } else { None }
} else { } else {
None None
} }

View file

@ -196,14 +196,30 @@ impl Scene {
} }
} }
/// Returns true if this viewport ID identifies a user viewport (not the /// Returns true if this viewport should display model-space content
/// sheet/overall viewport). The standard sheet viewport has id=1; id=0 /// (i.e. it is a user viewport, not the sheet/overall viewport).
/// means "unset". External DXF exporters sometimes use id=-1 for all ///
/// viewports, so any negative id is also treated as a user viewport. /// Rules:
pub fn is_user_viewport_id(id: i16) -> bool { /// - id=1 → always the sheet viewport → false
id != 0 && id != 1 /// - id≥2 → always a user viewport → true
/// - id=0 or id<0 (DWG reader omits the id; some DXF exporters write -1):
/// use geometry: the sheet viewport is centred at the paper origin (0,0)
/// with scale≈1.0 (view_height ≈ paper-space height).
pub fn is_content_viewport(vp: &acadrust::entities::Viewport) -> bool {
if vp.id == 1 { return false; }
if vp.id > 1 { return true; }
// id=0 or id<0 — distinguish by geometry
let scale = if vp.view_height.abs() > 1e-9 {
vp.height / vp.view_height
} else {
1.0
};
let at_origin = vp.center.x.abs() < 0.5 && vp.center.y.abs() < 0.5;
let scale_one = (scale - 1.0).abs() < 0.02;
!(at_origin && scale_one)
} }
/// Public accessor for the block-record handle of the current layout. /// Public accessor for the block-record handle of the current layout.
/// Used by external callers (e.g. `commit_entity`) that need the handle /// Used by external callers (e.g. `commit_entity`) that need the handle
/// without going through private API. /// without going through private API.
@ -333,7 +349,7 @@ impl Scene {
} }
self.document.entities().find_map(|e| { self.document.entities().find_map(|e| {
if let EntityType::Viewport(vp) = e { if let EntityType::Viewport(vp) = e {
if Self::is_user_viewport_id(vp.id) && vp.common.owner_handle == layout_block { if Self::is_content_viewport(vp) && vp.common.owner_handle == layout_block {
let scale = if vp.custom_scale.abs() > 1e-9 { let scale = if vp.custom_scale.abs() > 1e-9 {
vp.custom_scale vp.custom_scale
} else if vp.view_height.abs() > 1e-9 { } else if vp.view_height.abs() > 1e-9 {
@ -362,7 +378,7 @@ impl Scene {
.entities() .entities()
.filter_map(|e| { .filter_map(|e| {
if let EntityType::Viewport(vp) = e { if let EntityType::Viewport(vp) = e {
if Self::is_user_viewport_id(vp.id) && vp.common.owner_handle == layout_block { if Self::is_content_viewport(vp) && vp.common.owner_handle == layout_block {
Some((vp.common.handle, vp.id, vp.frozen_layers.clone())) Some((vp.common.handle, vp.id, vp.frozen_layers.clone()))
} else { } else {
None None
@ -398,7 +414,7 @@ impl Scene {
} }
self.document.entities().filter(|e| { self.document.entities().filter(|e| {
if let EntityType::Viewport(vp) = e { if let EntityType::Viewport(vp) = e {
Self::is_user_viewport_id(vp.id) && vp.common.owner_handle == layout_block Self::is_content_viewport(vp) && vp.common.owner_handle == layout_block
} else { } else {
false false
} }
@ -802,7 +818,7 @@ impl Scene {
if let EntityType::Viewport(vp) = e { Some(vp) } else { None } if let EntityType::Viewport(vp) = e { Some(vp) } else { None }
}) })
.filter(|vp| { .filter(|vp| {
Self::is_user_viewport_id(vp.id) Self::is_content_viewport(vp)
&& vp.common.owner_handle == paper_block && vp.common.owner_handle == paper_block
&& vp.status.is_on && vp.status.is_on
&& only_vp.map_or(true, |h| vp.common.handle == h) && only_vp.map_or(true, |h| vp.common.handle == h)
@ -1142,7 +1158,7 @@ impl Scene {
.entities() .entities()
.find_map(|e| { .find_map(|e| {
let EntityType::Viewport(vp) = e else { return None; }; let EntityType::Viewport(vp) = e else { return None; };
if !Self::is_user_viewport_id(vp.id) || vp.common.owner_handle != layout_block || !vp.status.is_on { if !Self::is_content_viewport(vp) || vp.common.owner_handle != layout_block || !vp.status.is_on {
return None; return None;
} }
let hw = (vp.width / 2.0) as f32; let hw = (vp.width / 2.0) as f32;
@ -1163,7 +1179,7 @@ impl Scene {
let layout_block = self.current_layout_block_handle(); let layout_block = self.current_layout_block_handle();
self.document.entities().find_map(|e| { self.document.entities().find_map(|e| {
let EntityType::Viewport(vp) = e else { return None; }; let EntityType::Viewport(vp) = e else { return None; };
if Self::is_user_viewport_id(vp.id) && vp.common.owner_handle == layout_block && vp.status.is_on { if Self::is_content_viewport(vp) && vp.common.owner_handle == layout_block && vp.status.is_on {
Some(vp.common.handle) Some(vp.common.handle)
} else { } else {
None None