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

@ -196,14 +196,30 @@ impl Scene {
}
}
/// Returns true if this viewport ID identifies a user viewport (not the
/// sheet/overall viewport). The standard sheet viewport has id=1; id=0
/// means "unset". External DXF exporters sometimes use id=-1 for all
/// viewports, so any negative id is also treated as a user viewport.
pub fn is_user_viewport_id(id: i16) -> bool {
id != 0 && id != 1
/// Returns true if this viewport should display model-space content
/// (i.e. it is a user viewport, not the sheet/overall viewport).
///
/// Rules:
/// - id=1 → always the sheet viewport → false
/// - 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.
/// Used by external callers (e.g. `commit_entity`) that need the handle
/// without going through private API.
@ -333,7 +349,7 @@ impl Scene {
}
self.document.entities().find_map(|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 {
vp.custom_scale
} else if vp.view_height.abs() > 1e-9 {
@ -362,7 +378,7 @@ impl Scene {
.entities()
.filter_map(|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()))
} else {
None
@ -398,7 +414,7 @@ impl Scene {
}
self.document.entities().filter(|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 {
false
}
@ -802,7 +818,7 @@ impl Scene {
if let EntityType::Viewport(vp) = e { Some(vp) } else { None }
})
.filter(|vp| {
Self::is_user_viewport_id(vp.id)
Self::is_content_viewport(vp)
&& vp.common.owner_handle == paper_block
&& vp.status.is_on
&& only_vp.map_or(true, |h| vp.common.handle == h)
@ -1142,7 +1158,7 @@ impl Scene {
.entities()
.find_map(|e| {
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;
}
let hw = (vp.width / 2.0) as f32;
@ -1163,7 +1179,7 @@ impl Scene {
let layout_block = self.current_layout_block_handle();
self.document.entities().find_map(|e| {
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)
} else {
None