feat(export): exact ACIS geometry for planar solids on save
OCS models 3-D solids with truck, but extrude/boolean results carried no ACIS body, so other CAD apps dropped them on open. Add an exact planar exporter and run it at save time. - scene::convert::acis_export::planar_solid_to_sat walks a truck Solid whose faces are all planes bounded by straight edges into a vertex/face-ring mesh and builds an exact ACIS body via acadrust's build_planar_body. Curved faces/edges return None (left for the NURBS path). A signed-volume check flips the winding if the shell comes out inside-out. - OpenCADStudio::sync_truck_solids_to_acis fills in ACIS geometry for every cached truck solid that still has none, just before a Save / Save-As, so the written DWG/DXF carries real modeler geometry. - Bump acadrust to 9142a50 (build_planar_body + classic-ACIS AcDs roundtrip fix). Booleans (already cached as truck solids) now export exact geometry. Extruded polygons will too once the EXTRUDE command caches its truck solid (a separate change kept out of this commit because command_driver.rs holds unrelated in-progress edits). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
79a5ac6fed
commit
f9b6834ad9
4 changed files with 175 additions and 1 deletions
|
|
@ -22,6 +22,45 @@ use iced::{mouse, Point, Task};
|
|||
|
||||
|
||||
impl OpenCADStudio {
|
||||
/// Before a save, give every cached truck solid that still has no ACIS
|
||||
/// geometry (EXTRUDE/REVOLVE/SWEEP/LOFT/boolean results) an exact modeler
|
||||
/// body derived from its truck B-rep, so the written DWG/DXF carries real
|
||||
/// 3-D geometry other CAD apps can open instead of an empty data stream.
|
||||
/// Curved solids that the exact planar path can't yet express are left
|
||||
/// untouched (handled by the NURBS path).
|
||||
#[cfg(feature = "solid3d")]
|
||||
fn sync_truck_solids_to_acis(&mut self, i: usize) {
|
||||
use acadrust::EntityType;
|
||||
let scene = &mut self.tabs[i].scene;
|
||||
let targets: Vec<acadrust::Handle> = scene
|
||||
.solid_models
|
||||
.keys()
|
||||
.copied()
|
||||
.filter(|h| {
|
||||
matches!(
|
||||
scene.document.get_entity(*h),
|
||||
Some(EntityType::Solid3D(s)) if !s.acis_data.has_data()
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
for h in targets {
|
||||
// Build the SAT while borrowing solid_models; the returned document
|
||||
// is owned, so the borrow ends before we mutate the entity.
|
||||
let sat = scene
|
||||
.solid_models
|
||||
.get(&h)
|
||||
.and_then(crate::scene::convert::acis_export::planar_solid_to_sat);
|
||||
if let Some(sat) = sat {
|
||||
if let Some(EntityType::Solid3D(s)) = scene.document.get_entity_mut(h) {
|
||||
s.set_sat_document(&sat);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "solid3d"))]
|
||||
fn sync_truck_solids_to_acis(&mut self, _i: usize) {}
|
||||
|
||||
/// Snapshot the persisted UI preferences from live state.
|
||||
pub(in crate::app) fn current_settings(&self) -> crate::app::settings::UserSettings {
|
||||
crate::app::settings::UserSettings {
|
||||
|
|
@ -556,6 +595,7 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
|
|||
if self.backup_on_save {
|
||||
crate::io::write_backup(&path);
|
||||
}
|
||||
self.sync_truck_solids_to_acis(i);
|
||||
match crate::io::save(&self.tabs[i].scene.document, &path) {
|
||||
Ok(()) => {
|
||||
self.command_line
|
||||
|
|
@ -613,6 +653,7 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
|
|||
let close = self.close_save_dialog_window();
|
||||
let i = self.active_tab;
|
||||
sync_annotation_scale_header(&mut self.tabs[i].scene);
|
||||
self.sync_truck_solids_to_acis(i);
|
||||
|
||||
// Native: write to the chosen path. Web: download the bytes
|
||||
// under the chosen name (no filesystem).
|
||||
|
|
|
|||
Loading…
Reference in a new issue