fix(dxf): stop writing a duplicate *Active viewport (#319)

Opening a DXF whose active viewport is stored uppercase (*ACTIVE),
editing and re-saving produced a file with two full-window model-space
viewports — the file's own *ACTIVE plus a fresh *Active we add on save.
The reserved viewport name is case-insensitive, so the two didn't
collapse and other CAD apps drew them one on top of the other, which
read as the copied objects being linked / "reacting as one".

save_model_tiles_to_vports dropped existing active viewports with a
case-sensitive `name != "*Active"` filter, so an uppercase *ACTIVE
survived and sat next to the new entry. Route every *Active comparison
(save filter + camera/tile restore) through a case-insensitive
is_active_vport_name helper so the file's record is recognised and
replaced instead of duplicated.

Also bumps acadrust to #99f75fe (merged PR #1): the ASCII DXF writer now
emits shortest round-trippable floats instead of the full 16-digit
expansion, halving the round-tripped file size.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hakan Seven 2026-07-09 22:36:00 +03:00
commit 9838cd6d83
2 changed files with 16 additions and 6 deletions

2
Cargo.lock generated
View file

@ -71,7 +71,7 @@ checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618"
[[package]]
name = "acadrust"
version = "0.4.0"
source = "git+https://github.com/HakanSeven12/acadrust?branch=main#f1f4dbf9f0e1273670556f86d3c26f2b14543fcc"
source = "git+https://github.com/HakanSeven12/acadrust?branch=main#99f75fe116ba836ae5fab70a886290688bbc5f50"
dependencies = [
"ahash 0.8.12",
"anyhow",

View file

@ -3,6 +3,16 @@
// hit-testing lives in `scene::pick::hit_test`.)
use super::*;
/// The model-space active viewport is reserved as `*Active`, but that name is
/// case-insensitive in DXF/DWG — a file may store it as `*ACTIVE`. Match it
/// accordingly, otherwise an uppercased record reads as a *distinct* viewport:
/// on save it survives the "drop the old *Active" filter and sits next to the
/// fresh entry we add, so the file gets two overlapping full-window viewports
/// that render "one on top of the other". (#319)
fn is_active_vport_name(name: &str) -> bool {
name.eq_ignore_ascii_case("*Active")
}
impl Scene {
// ── Hit-test convenience: wire name → Handle ──────────────────────────
@ -131,7 +141,7 @@ impl Scene {
fn apply_active_vport_camera(&mut self) -> bool {
// Restore the single tile's visual style + grid/snap from the *Active
// entry, independent of where the camera itself comes from below.
if let Some(vp) = self.document.vports.iter().find(|v| v.name == "*Active") {
if let Some(vp) = self.document.vports.iter().find(|v| is_active_vport_name(&v.name)) {
let mode = vp.render_mode;
let (grid_on, snap_on) = (vp.grid_on, vp.snap_on);
let mut tiles = self.model_tiles.borrow_mut();
@ -146,7 +156,7 @@ impl Scene {
// wrote an app-specific "OpenCADStudio_Camera_Model" View record and
// preferred it here — that polluted the file for other CAD programs and
// is no longer written or read; the view round-trips fine via VPORT.)
let vp = match self.document.vports.iter().find(|v| v.name == "*Active") {
let vp = match self.document.vports.iter().find(|v| is_active_vport_name(&v.name)) {
Some(v) => v.clone(),
None => return false,
};
@ -317,7 +327,7 @@ impl Scene {
.document
.vports
.iter()
.filter(|v| v.name == "*Active")
.filter(|v| is_active_vport_name(&v.name))
.cloned()
.collect();
@ -372,7 +382,7 @@ impl Scene {
.document
.vports
.iter()
.filter(|v| v.name != "*Active")
.filter(|v| !is_active_vport_name(&v.name))
.cloned()
.collect();
let mut new_vports = acadrust::tables::Table::with_handle(table_handle);
@ -544,7 +554,7 @@ impl Scene {
.document
.vports
.iter_mut()
.find(|v| v.name == "*Active")
.find(|v| is_active_vport_name(&v.name))
{
vp.view_target = target_wcs;
vp.view_center = acadrust::types::Vector2::ZERO;