forked from 1b-agent/opencadstudio-upstream
fix: make viewports from external DXF/DWG files visible and enterable
Two root causes prevented paper-space viewports from loading correctly: 1. acadrust's ViewportStatusFlags::from_bits() maps bit 0 → is_on, but the real DXF/DWG spec uses bit 15 (0x8000) for "viewport on". Post-load fixup in io/mod.rs now corrects this for both DXF and DWG files: when bit 0 is clear but bit 15 is set, is_on is forced true and locked is derived from bit 14 (0x4000). 2. Some DXF exporters write id=-1 for all viewport entities instead of the standard id=1 (sheet) / id≥2 (user) convention. The is_user_viewport_id() helper now treats any id ≠ 0 and ≠ 1 as a user viewport, covering both the standard id≥2 and the non-standard id=-1 cases. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
This commit is contained in:
1 parent
00e61a1301
commit
66a73b00e1
3 files changed
+50
-11
No files matched your search
+1
-1
@@ -1696,7 +1696,7 @@ impl H7CAD {
|
||||
if let Some(AcadEntityType::Viewport(vp)) =
|
||||
self.tabs[i].scene.document.get_entity(h)
|
||||
{
|
||||
if vp.id > 1 { Some(h) } else { None }
|
||||
if Scene::is_user_viewport_id(vp.id) { Some(h) } else { None }
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
||||
+26
-3
@@ -58,8 +58,12 @@ pub fn load_file(path: &Path) -> Result<CadDocument, String> {
|
||||
|
||||
match ext.as_str() {
|
||||
"dwg" => {
|
||||
let mut reader = DwgReader::from_file(path).map_err(|e| e.to_string())?;
|
||||
reader.read().map_err(|e| e.to_string())
|
||||
let mut doc = DwgReader::from_file(path)
|
||||
.map_err(|e| e.to_string())?
|
||||
.read()
|
||||
.map_err(|e| e.to_string())?;
|
||||
fix_viewport_status_flags(&mut doc);
|
||||
Ok(doc)
|
||||
}
|
||||
"dxf" => {
|
||||
let mut doc = DxfReader::from_file(path)
|
||||
@@ -67,6 +71,7 @@ pub fn load_file(path: &Path) -> Result<CadDocument, String> {
|
||||
.read()
|
||||
.map_err(|e| e.to_string())?;
|
||||
fix_dxf_dimension_rotations(&mut doc);
|
||||
fix_viewport_status_flags(&mut doc);
|
||||
Ok(doc)
|
||||
}
|
||||
_ => Err(format!("Unsupported file format: .{ext}")),
|
||||
@@ -168,7 +173,25 @@ pub fn save_dxf(doc: &CadDocument, path: &Path) -> Result<(), String> {
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
// ── DXF post-load fixups ──────────────────────────────────────────────────
|
||||
// ── Post-load fixups ──────────────────────────────────────────────────────
|
||||
|
||||
/// acadrust's ViewportStatusFlags::from_bits() maps bit 0 → is_on and bit 15 → locked,
|
||||
/// but the real DXF/DWG spec uses bit 15 (0x8000) → viewport on and bit 14 (0x4000) → locked.
|
||||
/// Files from AutoCAD and other tools always set bit 15 for active viewports, leaving bit 0
|
||||
/// clear, so acadrust reads every such viewport as off. Correct that here after loading.
|
||||
fn fix_viewport_status_flags(doc: &mut CadDocument) {
|
||||
for entity in doc.entities_mut() {
|
||||
if let EntityType::Viewport(vp) = entity {
|
||||
let bits = vp.status.to_bits();
|
||||
// If bit 0 is not set but bit 15 is, this is an external-format viewport:
|
||||
// treat bit 15 as "on" and bit 14 as "locked".
|
||||
if (bits & 0x0001) == 0 && (bits & 0x8000) != 0 {
|
||||
vp.status.is_on = true;
|
||||
vp.status.locked = (bits & 0x4000) != 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The acadrust DXF reader stores several rotation fields directly from DXF
|
||||
/// group code 50 in degrees, while DWG and our own creation code store radians.
|
||||
|
||||
+23
-7
@@ -196,6 +196,14 @@ 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
|
||||
}
|
||||
|
||||
/// 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.
|
||||
@@ -325,7 +333,7 @@ impl Scene {
|
||||
}
|
||||
self.document.entities().find_map(|e| {
|
||||
if let EntityType::Viewport(vp) = e {
|
||||
if vp.id > 1 && vp.common.owner_handle == layout_block {
|
||||
if Self::is_user_viewport_id(vp.id) && 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 {
|
||||
@@ -354,7 +362,7 @@ impl Scene {
|
||||
.entities()
|
||||
.filter_map(|e| {
|
||||
if let EntityType::Viewport(vp) = e {
|
||||
if vp.id > 1 && vp.common.owner_handle == layout_block {
|
||||
if Self::is_user_viewport_id(vp.id) && vp.common.owner_handle == layout_block {
|
||||
Some((vp.common.handle, vp.id, vp.frozen_layers.clone()))
|
||||
} else {
|
||||
None
|
||||
@@ -365,7 +373,15 @@ impl Scene {
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.into_iter()
|
||||
.map(|(h, id, frozen)| (h, format!("VP {}", id - 1), frozen))
|
||||
.enumerate()
|
||||
.map(|(i, (h, id, frozen))| {
|
||||
let label = if id > 1 {
|
||||
format!("VP {}", id - 1)
|
||||
} else {
|
||||
format!("VP {}", i + 1)
|
||||
};
|
||||
(h, label, frozen)
|
||||
})
|
||||
.collect();
|
||||
result.sort_by_key(|(_, label, _)| label.clone());
|
||||
result
|
||||
@@ -382,7 +398,7 @@ impl Scene {
|
||||
}
|
||||
self.document.entities().filter(|e| {
|
||||
if let EntityType::Viewport(vp) = e {
|
||||
vp.id > 1 && vp.common.owner_handle == layout_block
|
||||
Self::is_user_viewport_id(vp.id) && vp.common.owner_handle == layout_block
|
||||
} else {
|
||||
false
|
||||
}
|
||||
@@ -786,7 +802,7 @@ impl Scene {
|
||||
if let EntityType::Viewport(vp) = e { Some(vp) } else { None }
|
||||
})
|
||||
.filter(|vp| {
|
||||
vp.id > 1
|
||||
Self::is_user_viewport_id(vp.id)
|
||||
&& vp.common.owner_handle == paper_block
|
||||
&& vp.status.is_on
|
||||
&& only_vp.map_or(true, |h| vp.common.handle == h)
|
||||
@@ -1126,7 +1142,7 @@ impl Scene {
|
||||
.entities()
|
||||
.find_map(|e| {
|
||||
let EntityType::Viewport(vp) = e else { return None; };
|
||||
if vp.id <= 1 || vp.common.owner_handle != layout_block || !vp.status.is_on {
|
||||
if !Self::is_user_viewport_id(vp.id) || vp.common.owner_handle != layout_block || !vp.status.is_on {
|
||||
return None;
|
||||
}
|
||||
let hw = (vp.width / 2.0) as f32;
|
||||
@@ -1147,7 +1163,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 vp.id > 1 && vp.common.owner_handle == layout_block && vp.status.is_on {
|
||||
if Self::is_user_viewport_id(vp.id) && vp.common.owner_handle == layout_block && vp.status.is_on {
|
||||
Some(vp.common.handle)
|
||||
} else {
|
||||
None
|
||||
|
||||
Reference in new issue
Block a user