IMPORT: read SAT, STEP, IGES and WMF files; ACISIN and WMFIN

ACIS text files come in one object per body (3DSOLID, REGION or SURFACE).
STEP B-rep models become a block per product with component inserts at
their placements, scaled to the drawing's units; IGES manifold solids come
in as 3DSOLIDs scaled to the drawing's units with their colours. Splines
that are exactly analytic shapes are kept as those shapes. Windows
metafiles become a WMF<n> block of polylines and text fitted to half the
view, placed at its top-left corner or by WMFIN's insertion prompts. The
Import File window lists the four formats as supported.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ramox81andClaude Opus 5.5 committed 2026-10-16 15:06:16 +03:00
1 parent 3c9404510f
commit 659bb06cd6
38 files changed
+2400 -113

No files matched your search

+41 -3
View File
@@ -8,6 +8,7 @@ use std::collections::HashMap;
use std::sync::Arc;
use super::*;
use crate::io::model_import::ModelFormat;
use crate::modules::insert::dgn_import::{self, DgnImportCommand, DgnImportRequest};
use crate::ui::window::import_dialogs::{
unsupported_reason, DgnImportState, ImportDialogMsg, ImportFileState, Support, FORMATS,
@@ -18,7 +19,7 @@ const RECENT: usize = 5;
/// Picks a file and registers its bytes (the web build reads them only
/// here); `None` when the picker is cancelled.
async fn pick(title: String, filters: Vec<(String, Vec<String>)>) -> Option<String> {
pub(super) async fn pick(title: String, filters: Vec<(String, Vec<String>)>) -> Option<String> {
let mut dialog = crate::sys::file_dialog().set_title(title.as_str());
for (name, extensions) in &filters {
let extensions: Vec<&str> = extensions.iter().map(String::as_str).collect();
@@ -62,6 +63,8 @@ fn is_dgn(path: &str) -> bool {
enum Route {
Pdf,
Dgn,
Model(ModelFormat),
Wmf,
}
/// The importer for `path` chosen as `format` (an index of [`FORMATS`]; `None`
@@ -75,6 +78,21 @@ fn route(path: &str, format: Option<usize>) -> Result<Route, String> {
}
let ext = extension(path);
let by_extension = f.is_none_or(|f| f.support == Support::ByExtension);
// The chosen format, or the one whose patterns name the extension.
let named = if by_extension {
FORMATS
.iter()
.find(|f| f.extensions.iter().any(|e| e.eq_ignore_ascii_case(&ext)))
} else {
f
};
match named.map(|f| f.name) {
Some("ACIS") => return Ok(Route::Model(ModelFormat::Sat)),
Some("STEP") => return Ok(Route::Model(ModelFormat::Step)),
Some("IGES") => return Ok(Route::Model(ModelFormat::Iges)),
Some("Metafile") => return Ok(Route::Wmf),
_ => {}
}
let pdf = if by_extension { ext == "pdf" } else { f.is_some_and(|f| f.name == "PDF Files") };
if pdf {
return Ok(Route::Pdf);
@@ -111,8 +129,8 @@ impl OpenCADStudio {
Some(Task::none())
}
("IMPORT", true) => {
use crate::command::{CadCommand, ValuePromptCommand};
let command = ValuePromptCommand::new("_IMPORTFILE", "Enter import file name:");
use crate::command::{CadCommand, FreeTextValuePromptCommand};
let command = FreeTextValuePromptCommand::new("_IMPORTFILE", "Enter import file name:");
self.command_line.push_info(&command.prompt());
self.tabs[i].active_cmd = Some(Box::new(command));
Some(Task::none())
@@ -217,6 +235,16 @@ impl OpenCADStudio {
}
self.dispatch_command(&format!("-DGNIMPORT {path}"))
}
Ok(Route::Model(format)) => {
self.remember_import(path);
let i = self.active_tab;
self.import_model(i, path, format)
}
Ok(Route::Wmf) => {
self.remember_import(path);
let i = self.active_tab;
self.import_wmf(i, path, false)
}
Err(error) => {
self.command_line.push_error(&error);
Task::none()
@@ -324,6 +352,16 @@ impl OpenCADStudio {
self.command_line.push_error(&error);
}
}
Ok(Route::Model(kind)) => {
self.remember_import(&path);
self.close_active_modal();
return self.import_model(i, &path, kind);
}
Ok(Route::Wmf) => {
self.remember_import(&path);
self.close_active_modal();
return self.import_wmf(i, &path, false);
}
}
}
// ── DGN Import ───────────────────────────────────────────────