forked from 1b-agent/opencadstudio-upstream
Fix IMPORT naming, messages and 3D Studio limits
STEP assemblies: a link that closes a loop in the assembly is dropped, so no block inserts itself; the walk is iterative. Each imported block gets a name the drawing does not use yet instead of silently reusing an existing definition, inserts are pointed at the names the blocks got, and an insert of a block that could not be defined is left out with the error reported. Names that come from files (STEP products, 3D Studio objects, materials and cameras, WMF font faces) go through a new shared scene::sanitized_table_name, which applies the block-name rules (reserved and control characters, 255 characters). 3D Studio: meshes with more vertices than a polyface mesh can index are split over several meshes. Single-layer mode uses the current layer. Materials are only created when the drawing has a material dictionary. Camera views get the current view's proportions and the perspective flag. Messages show paths as given, the file-not-found and read-failure messages are plain text of our own, and the "Press Esc" line is gone since the import cannot be cancelled. The true-colour packing is shared. "Select ACIS File" and "Import WMF" get catalog entries and keys in all 22 locales. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
dbc428b237
commit
a0ed20df28
28 files changed
+205
-88
No files matched your search
@@ -1250,6 +1250,7 @@ common =
|
||||
.dgn-files = DGN Files
|
||||
.select-dwf-file = Select DWF File
|
||||
.select-dgn-file = Select DGN File
|
||||
.select-acis-file = اختيار ملف ACIS
|
||||
.pdfattach = PDFATTACH: __ocs_fmt_0__
|
||||
.select-external-reference-file = { draw.select } · { properties.external-reference } · { common.files }
|
||||
.cad-files = { common.files } (CAD)
|
||||
@@ -6683,6 +6684,7 @@ dgn-import =
|
||||
.import-file = Import File
|
||||
.dgn-import = DGN Import
|
||||
.import-dgn-file = Import DGN File
|
||||
.import-wmf = استيراد WMF
|
||||
.help-dgn = Imports a DGN model as drawing objects: levels as layers, cells as blocks.
|
||||
.help-import = Imports a file of the chosen format with the format's importer.
|
||||
.blank = Filename cannot be blank
|
||||
|
||||
@@ -1237,6 +1237,7 @@ common =
|
||||
.dgn-files = DGN Files
|
||||
.select-dwf-file = Select DWF File
|
||||
.select-dgn-file = Select DGN File
|
||||
.select-acis-file = Избор на ACIS файл
|
||||
.pdfattach = PDFATTACH: __ocs_fmt_0__
|
||||
.select-external-reference-file = { draw.select } · { properties.external-reference } · { common.files }
|
||||
.cad-files = { common.files } (CAD)
|
||||
@@ -6671,6 +6672,7 @@ dgn-import =
|
||||
.import-file = Import File
|
||||
.dgn-import = DGN Import
|
||||
.import-dgn-file = Import DGN File
|
||||
.import-wmf = Импортиране на WMF
|
||||
.help-dgn = Imports a DGN model as drawing objects: levels as layers, cells as blocks.
|
||||
.help-import = Imports a file of the chosen format with the format's importer.
|
||||
.blank = Filename cannot be blank
|
||||
|
||||
@@ -1237,6 +1237,7 @@ common =
|
||||
.dgn-files = DGN Files
|
||||
.select-dwf-file = Select DWF File
|
||||
.select-dgn-file = Select DGN File
|
||||
.select-acis-file = Vybrat soubor ACIS
|
||||
.pdfattach = PDFATTACH: __ocs_fmt_0__
|
||||
.select-external-reference-file = { draw.select } · { properties.external-reference } · { common.files }
|
||||
.cad-files = { common.files } (CAD)
|
||||
@@ -6668,6 +6669,7 @@ dgn-import =
|
||||
.import-file = Import File
|
||||
.dgn-import = DGN Import
|
||||
.import-dgn-file = Import DGN File
|
||||
.import-wmf = Importovat WMF
|
||||
.help-dgn = Imports a DGN model as drawing objects: levels as layers, cells as blocks.
|
||||
.help-import = Imports a file of the chosen format with the format's importer.
|
||||
.blank = Filename cannot be blank
|
||||
|
||||
@@ -1238,6 +1238,7 @@ common =
|
||||
.dgn-files = DGN Files
|
||||
.select-dwf-file = Select DWF File
|
||||
.select-dgn-file = Select DGN File
|
||||
.select-acis-file = ACIS-Datei auswählen
|
||||
.pdfattach = PDFATTACH: __ocs_fmt_0__
|
||||
.select-external-reference-file = { draw.select } · { properties.external-reference } · { common.files }
|
||||
.cad-files = { common.files } (CAD)
|
||||
@@ -6669,6 +6670,7 @@ dgn-import =
|
||||
.import-file = Import File
|
||||
.dgn-import = DGN Import
|
||||
.import-dgn-file = Import DGN File
|
||||
.import-wmf = WMF importieren
|
||||
.help-dgn = Imports a DGN model as drawing objects: levels as layers, cells as blocks.
|
||||
.help-import = Imports a file of the chosen format with the format's importer.
|
||||
.blank = Filename cannot be blank
|
||||
|
||||
@@ -1239,6 +1239,7 @@ common =
|
||||
.dgn-files = DGN Files
|
||||
.select-dwf-file = Select DWF File
|
||||
.select-dgn-file = Select DGN File
|
||||
.select-acis-file = Επιλογή αρχείου ACIS
|
||||
.pdfattach = PDFATTACH: __ocs_fmt_0__
|
||||
.select-external-reference-file = { draw.select } · { properties.external-reference } · { common.files }
|
||||
.cad-files = { common.files } (CAD)
|
||||
@@ -6671,6 +6672,7 @@ dgn-import =
|
||||
.import-file = Import File
|
||||
.dgn-import = DGN Import
|
||||
.import-dgn-file = Import DGN File
|
||||
.import-wmf = Εισαγωγή WMF
|
||||
.help-dgn = Imports a DGN model as drawing objects: levels as layers, cells as blocks.
|
||||
.help-import = Imports a file of the chosen format with the format's importer.
|
||||
.blank = Filename cannot be blank
|
||||
|
||||
@@ -1236,6 +1236,7 @@ common =
|
||||
.dgn-files = DGN Files
|
||||
.select-dwf-file = Select DWF File
|
||||
.select-dgn-file = Select DGN File
|
||||
.select-acis-file = Select ACIS File
|
||||
.pdfattach = PDFATTACH: __ocs_fmt_0__
|
||||
.select-external-reference-file = Select External Reference File
|
||||
.cad-files = CAD Files
|
||||
@@ -6670,6 +6671,7 @@ dgn-import =
|
||||
.import-file = Import File
|
||||
.dgn-import = DGN Import
|
||||
.import-dgn-file = Import DGN File
|
||||
.import-wmf = Import WMF
|
||||
.help-dgn = Imports a DGN model as drawing objects: levels as layers, cells as blocks.
|
||||
.help-import = Imports a file of the chosen format with the format's importer.
|
||||
.blank = Filename cannot be blank
|
||||
|
||||
@@ -1238,6 +1238,7 @@ common =
|
||||
.dgn-files = DGN Files
|
||||
.select-dwf-file = Select DWF File
|
||||
.select-dgn-file = Select DGN File
|
||||
.select-acis-file = Seleccionar archivo ACIS
|
||||
.pdfattach = PDFATTACH: __ocs_fmt_0__
|
||||
.select-external-reference-file = { draw.select } · { properties.external-reference } · { common.files }
|
||||
.cad-files = { common.files } (CAD)
|
||||
@@ -6671,6 +6672,7 @@ dgn-import =
|
||||
.import-file = Import File
|
||||
.dgn-import = DGN Import
|
||||
.import-dgn-file = Import DGN File
|
||||
.import-wmf = Importar WMF
|
||||
.help-dgn = Imports a DGN model as drawing objects: levels as layers, cells as blocks.
|
||||
.help-import = Imports a file of the chosen format with the format's importer.
|
||||
.blank = Filename cannot be blank
|
||||
|
||||
@@ -1237,6 +1237,7 @@ common =
|
||||
.dgn-files = DGN Files
|
||||
.select-dwf-file = Select DWF File
|
||||
.select-dgn-file = Select DGN File
|
||||
.select-acis-file = Valitse ACIS-tiedosto
|
||||
.pdfattach = PDFATTACH: __ocs_fmt_0__
|
||||
.select-external-reference-file = { draw.select } · { properties.external-reference } · { common.files }
|
||||
.cad-files = { common.files } (CAD)
|
||||
@@ -6668,6 +6669,7 @@ dgn-import =
|
||||
.import-file = Import File
|
||||
.dgn-import = DGN Import
|
||||
.import-dgn-file = Import DGN File
|
||||
.import-wmf = Tuo WMF
|
||||
.help-dgn = Imports a DGN model as drawing objects: levels as layers, cells as blocks.
|
||||
.help-import = Imports a file of the chosen format with the format's importer.
|
||||
.blank = Filename cannot be blank
|
||||
|
||||
@@ -1238,6 +1238,7 @@ common =
|
||||
.dgn-files = DGN Files
|
||||
.select-dwf-file = Select DWF File
|
||||
.select-dgn-file = Select DGN File
|
||||
.select-acis-file = Sélectionner un fichier ACIS
|
||||
.pdfattach = PDFATTACH: __ocs_fmt_0__
|
||||
.select-external-reference-file = { draw.select } · { properties.external-reference } · { common.files }
|
||||
.cad-files = { common.files } (CAD)
|
||||
@@ -6671,6 +6672,7 @@ dgn-import =
|
||||
.import-file = Import File
|
||||
.dgn-import = DGN Import
|
||||
.import-dgn-file = Import DGN File
|
||||
.import-wmf = Importer un WMF
|
||||
.help-dgn = Imports a DGN model as drawing objects: levels as layers, cells as blocks.
|
||||
.help-import = Imports a file of the chosen format with the format's importer.
|
||||
.blank = Filename cannot be blank
|
||||
|
||||
@@ -1226,6 +1226,7 @@ common =
|
||||
.dgn-files = DGN Files
|
||||
.select-dwf-file = Select DWF File
|
||||
.select-dgn-file = Select DGN File
|
||||
.select-acis-file = ACIS फ़ाइल चुनें
|
||||
.pdfattach = PDFATTACH: __ocs_fmt_0__
|
||||
.select-external-reference-file = { draw.select } · { properties.external-reference } · { common.files }
|
||||
.cad-files = { common.files } (CAD)
|
||||
@@ -6659,6 +6660,7 @@ dgn-import =
|
||||
.import-file = Import File
|
||||
.dgn-import = DGN Import
|
||||
.import-dgn-file = Import DGN File
|
||||
.import-wmf = WMF आयात करें
|
||||
.help-dgn = Imports a DGN model as drawing objects: levels as layers, cells as blocks.
|
||||
.help-import = Imports a file of the chosen format with the format's importer.
|
||||
.blank = Filename cannot be blank
|
||||
|
||||
@@ -1242,6 +1242,7 @@ common =
|
||||
.dgn-files = DGN-fájlok
|
||||
.select-dwf-file = DWF-fájl kiválasztása
|
||||
.select-dgn-file = DGN-fájl kiválasztása
|
||||
.select-acis-file = ACIS-fájl kiválasztása
|
||||
.pdfattach = PDFATTACH: __ocs_fmt_0__
|
||||
.select-external-reference-file = { draw.select } · { properties.external-reference } · { common.files }
|
||||
.cad-files = { common.files } (CAD)
|
||||
@@ -6655,6 +6656,7 @@ dgn-import =
|
||||
.import-file = Import File
|
||||
.dgn-import = DGN Import
|
||||
.import-dgn-file = Import DGN File
|
||||
.import-wmf = WMF importálása
|
||||
.help-dgn = Imports a DGN model as drawing objects: levels as layers, cells as blocks.
|
||||
.help-import = Imports a file of the chosen format with the format's importer.
|
||||
.blank = Filename cannot be blank
|
||||
|
||||
@@ -1237,6 +1237,7 @@ common =
|
||||
.dgn-files = DGN Files
|
||||
.select-dwf-file = Select DWF File
|
||||
.select-dgn-file = Select DGN File
|
||||
.select-acis-file = Seleziona file ACIS
|
||||
.pdfattach = PDFATTACH: __ocs_fmt_0__
|
||||
.select-external-reference-file = { draw.select } · { properties.external-reference } · { common.files }
|
||||
.cad-files = { common.files } (CAD)
|
||||
@@ -6670,6 +6671,7 @@ dgn-import =
|
||||
.import-file = Import File
|
||||
.dgn-import = DGN Import
|
||||
.import-dgn-file = Import DGN File
|
||||
.import-wmf = Importa WMF
|
||||
.help-dgn = Imports a DGN model as drawing objects: levels as layers, cells as blocks.
|
||||
.help-import = Imports a file of the chosen format with the format's importer.
|
||||
.blank = Filename cannot be blank
|
||||
|
||||
@@ -1228,6 +1228,7 @@ common =
|
||||
.dgn-files = DGN Files
|
||||
.select-dwf-file = Select DWF File
|
||||
.select-dgn-file = Select DGN File
|
||||
.select-acis-file = ACIS ファイルを選択
|
||||
.pdfattach = PDFATTACH: __ocs_fmt_0__
|
||||
.select-external-reference-file = { draw.select } · { properties.external-reference } · { common.files }
|
||||
.cad-files = { common.files } (CAD)
|
||||
@@ -6659,6 +6660,7 @@ dgn-import =
|
||||
.import-file = Import File
|
||||
.dgn-import = DGN Import
|
||||
.import-dgn-file = Import DGN File
|
||||
.import-wmf = WMF を読み込み
|
||||
.help-dgn = Imports a DGN model as drawing objects: levels as layers, cells as blocks.
|
||||
.help-import = Imports a file of the chosen format with the format's importer.
|
||||
.blank = Filename cannot be blank
|
||||
|
||||
@@ -1237,6 +1237,7 @@ common =
|
||||
.dgn-files = DGN Files
|
||||
.select-dwf-file = Select DWF File
|
||||
.select-dgn-file = Select DGN File
|
||||
.select-acis-file = ACIS 파일 선택
|
||||
.pdfattach = PDFATTACH: __ocs_fmt_0__
|
||||
.select-external-reference-file = { draw.select } · { properties.external-reference } · { common.files }
|
||||
.cad-files = { common.files } (CAD)
|
||||
@@ -6670,6 +6671,7 @@ dgn-import =
|
||||
.import-file = Import File
|
||||
.dgn-import = DGN Import
|
||||
.import-dgn-file = Import DGN File
|
||||
.import-wmf = WMF 가져오기
|
||||
.help-dgn = Imports a DGN model as drawing objects: levels as layers, cells as blocks.
|
||||
.help-import = Imports a file of the chosen format with the format's importer.
|
||||
.blank = Filename cannot be blank
|
||||
|
||||
@@ -1243,6 +1243,7 @@ common =
|
||||
.dgn-files = ໄຟລ໌ DGN
|
||||
.select-dwf-file = ເລືອກໄຟລ໌ DWF
|
||||
.select-dgn-file = ເລືອກໄຟລ໌ DGN
|
||||
.select-acis-file = ເລືອກໄຟລ໌ ACIS
|
||||
.pdfattach = PDFATTACH: __ocs_fmt_0__
|
||||
.select-external-reference-file = ເລືອກໄຟລ໌ອ້າງອີງພາຍນອກ
|
||||
.cad-files = ໄຟລ໌ CAD
|
||||
@@ -6671,6 +6672,7 @@ dgn-import =
|
||||
.import-file = Import File
|
||||
.dgn-import = DGN Import
|
||||
.import-dgn-file = Import DGN File
|
||||
.import-wmf = ນຳເຂົ້າ WMF
|
||||
.help-dgn = Imports a DGN model as drawing objects: levels as layers, cells as blocks.
|
||||
.help-import = Imports a file of the chosen format with the format's importer.
|
||||
.blank = Filename cannot be blank
|
||||
|
||||
@@ -1236,6 +1236,7 @@ common =
|
||||
.dgn-files = DGN Files
|
||||
.select-dwf-file = Select DWF File
|
||||
.select-dgn-file = Select DGN File
|
||||
.select-acis-file = ACIS-bestand selecteren
|
||||
.pdfattach = PDFATTACH: __ocs_fmt_0__
|
||||
.select-external-reference-file = { draw.select } · { properties.external-reference } · { common.files }
|
||||
.cad-files = { common.files } (CAD)
|
||||
@@ -6669,6 +6670,7 @@ dgn-import =
|
||||
.import-file = Import File
|
||||
.dgn-import = DGN Import
|
||||
.import-dgn-file = Import DGN File
|
||||
.import-wmf = WMF importeren
|
||||
.help-dgn = Imports a DGN model as drawing objects: levels as layers, cells as blocks.
|
||||
.help-import = Imports a file of the chosen format with the format's importer.
|
||||
.blank = Filename cannot be blank
|
||||
|
||||
@@ -1237,6 +1237,7 @@ common =
|
||||
.dgn-files = DGN Files
|
||||
.select-dwf-file = Select DWF File
|
||||
.select-dgn-file = Select DGN File
|
||||
.select-acis-file = Wybierz plik ACIS
|
||||
.pdfattach = PDFATTACH: __ocs_fmt_0__
|
||||
.select-external-reference-file = { draw.select } · { properties.external-reference } · { common.files }
|
||||
.cad-files = { common.files } (CAD)
|
||||
@@ -6670,6 +6671,7 @@ dgn-import =
|
||||
.import-file = Import File
|
||||
.dgn-import = DGN Import
|
||||
.import-dgn-file = Import DGN File
|
||||
.import-wmf = Importuj WMF
|
||||
.help-dgn = Imports a DGN model as drawing objects: levels as layers, cells as blocks.
|
||||
.help-import = Imports a file of the chosen format with the format's importer.
|
||||
.blank = Filename cannot be blank
|
||||
|
||||
@@ -1238,6 +1238,7 @@ common =
|
||||
.dgn-files = DGN Files
|
||||
.select-dwf-file = Select DWF File
|
||||
.select-dgn-file = Select DGN File
|
||||
.select-acis-file = Selecionar arquivo ACIS
|
||||
.pdfattach = PDFATTACH: __ocs_fmt_0__
|
||||
.select-external-reference-file = { draw.select } · { properties.external-reference } · { common.files }
|
||||
.cad-files = { common.files } (CAD)
|
||||
@@ -6671,6 +6672,7 @@ dgn-import =
|
||||
.import-file = Import File
|
||||
.dgn-import = DGN Import
|
||||
.import-dgn-file = Import DGN File
|
||||
.import-wmf = Importar WMF
|
||||
.help-dgn = Imports a DGN model as drawing objects: levels as layers, cells as blocks.
|
||||
.help-import = Imports a file of the chosen format with the format's importer.
|
||||
.blank = Filename cannot be blank
|
||||
|
||||
@@ -1241,6 +1241,7 @@ common =
|
||||
.dgn-files = DGN Files
|
||||
.select-dwf-file = Select DWF File
|
||||
.select-dgn-file = Select DGN File
|
||||
.select-acis-file = Выбор файла ACIS
|
||||
.pdfattach = PDFATTACH: __ocs_fmt_0__
|
||||
.select-external-reference-file = { draw.select } · { properties.external-reference } · { common.files }
|
||||
.cad-files = { common.files } (CAD)
|
||||
@@ -6672,6 +6673,7 @@ dgn-import =
|
||||
.import-file = Import File
|
||||
.dgn-import = DGN Import
|
||||
.import-dgn-file = Import DGN File
|
||||
.import-wmf = Импорт WMF
|
||||
.help-dgn = Imports a DGN model as drawing objects: levels as layers, cells as blocks.
|
||||
.help-import = Imports a file of the chosen format with the format's importer.
|
||||
.blank = Filename cannot be blank
|
||||
|
||||
@@ -1224,6 +1224,7 @@ common =
|
||||
.dgn-files = DGN Dosyaları
|
||||
.select-dwf-file = DWF Dosyası Seç
|
||||
.select-dgn-file = DGN Dosyası Seç
|
||||
.select-acis-file = ACIS Dosyası Seç
|
||||
.pdfattach = PDFATTACH: __ocs_fmt_0__
|
||||
.select-external-reference-file = { draw.select } · { properties.external-reference } · { common.files }
|
||||
.cad-files = { common.files } (CAD)
|
||||
@@ -6592,6 +6593,7 @@ dgn-import =
|
||||
.import-file = Dosya İçe Aktar
|
||||
.dgn-import = DGN İçe Aktar
|
||||
.import-dgn-file = DGN Dosyası İçe Aktar
|
||||
.import-wmf = WMF İçe Aktar
|
||||
.help-dgn = DGN modelini çizim nesneleri olarak içe aktarır: seviyeler katman, hücreler blok olur.
|
||||
.help-import = Seçilen biçimdeki dosyayı o biçimin içe aktarıcısıyla içe aktarır.
|
||||
.blank = Dosya adı boş olamaz
|
||||
|
||||
@@ -1225,6 +1225,7 @@ common =
|
||||
.dgn-files = DGN Files
|
||||
.select-dwf-file = Select DWF File
|
||||
.select-dgn-file = Select DGN File
|
||||
.select-acis-file = 选择 ACIS 文件
|
||||
.pdfattach = PDFATTACH: __ocs_fmt_0__
|
||||
.select-external-reference-file = { draw.select } · { properties.external-reference } · { common.files }
|
||||
.cad-files = { common.files } (CAD)
|
||||
@@ -6654,6 +6655,7 @@ dgn-import =
|
||||
.import-file = Import File
|
||||
.dgn-import = DGN Import
|
||||
.import-dgn-file = Import DGN File
|
||||
.import-wmf = 导入 WMF
|
||||
.help-dgn = Imports a DGN model as drawing objects: levels as layers, cells as blocks.
|
||||
.help-import = Imports a file of the chosen format with the format's importer.
|
||||
.blank = Filename cannot be blank
|
||||
|
||||
@@ -1237,6 +1237,7 @@ common =
|
||||
.dgn-files = DGN Files
|
||||
.select-dwf-file = Select DWF File
|
||||
.select-dgn-file = Select DGN File
|
||||
.select-acis-file = 選擇 ACIS 檔案
|
||||
.pdfattach = PDFATTACH: __ocs_fmt_0__
|
||||
.select-external-reference-file = { draw.select } · { properties.external-reference } · { common.files }
|
||||
.cad-files = { common.files } (CAD)
|
||||
@@ -6666,6 +6667,7 @@ dgn-import =
|
||||
.import-file = Import File
|
||||
.dgn-import = DGN Import
|
||||
.import-dgn-file = Import DGN File
|
||||
.import-wmf = 匯入 WMF
|
||||
.help-dgn = Imports a DGN model as drawing objects: levels as layers, cells as blocks.
|
||||
.help-import = Imports a file of the chosen format with the format's importer.
|
||||
.blank = Filename cannot be blank
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
//! as one undo step.
|
||||
|
||||
use super::*;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::io::model_import::{self, ModelFormat};
|
||||
|
||||
impl OpenCADStudio {
|
||||
@@ -36,9 +38,8 @@ impl OpenCADStudio {
|
||||
let here = std::env::current_dir()
|
||||
.map(|d| d.display().to_string())
|
||||
.unwrap_or_default();
|
||||
self.command_line.push_error(&format!(
|
||||
"\"{name}\": Can't find file in search path:\n {here}\\ (current directory)"
|
||||
));
|
||||
self.command_line
|
||||
.push_error(&format!("\"{name}\": file not found in the current directory, {here}"));
|
||||
return Some(Task::none());
|
||||
}
|
||||
Some(self.import_model(i, rest, ModelFormat::Sat))
|
||||
@@ -82,40 +83,48 @@ impl OpenCADStudio {
|
||||
.push_error(&format!("File {path} does not exist."));
|
||||
return Task::none();
|
||||
};
|
||||
// The translated formats report the job as they run.
|
||||
let translated = format != ModelFormat::Sat;
|
||||
if translated {
|
||||
self.command_line
|
||||
.push_output("Import job in progress. Press Esc key to cancel.");
|
||||
}
|
||||
let drawing_mm = crate::app::properties::insunits_to_mm(
|
||||
self.tabs[i].scene.document.header.insertion_units,
|
||||
);
|
||||
let Some(imported) = model_import::read(format, &bytes, drawing_mm) else {
|
||||
let shown = path.replace('/', "\\");
|
||||
match format {
|
||||
ModelFormat::Sat => {
|
||||
self.command_line.push_error("Modeling Operation Error:");
|
||||
self.command_line.push_error("Error Code Number is 58000");
|
||||
self.command_line
|
||||
.push_error(&format!("Unable to read the ACIS file {shown}."));
|
||||
}
|
||||
_ => self
|
||||
.command_line
|
||||
.push_error(&format!("Import failed: {shown} has no model the importer reads.")),
|
||||
}
|
||||
self.command_line
|
||||
.push_error(&format!("Import failed: {path} has no model the importer reads."));
|
||||
return Task::none();
|
||||
};
|
||||
self.push_undo_snapshot(i, "IMPORT");
|
||||
let scene = &mut self.tabs[i].scene;
|
||||
// A block the drawing already has keeps its definition and is
|
||||
// inserted as it is.
|
||||
// Each block goes under a name the drawing does not use yet (a
|
||||
// product named like a block already there gets a new name, not the
|
||||
// old definition), and the inserts are pointed at the names the
|
||||
// blocks got. An insert of a block that could not be made is left
|
||||
// out.
|
||||
fn retarget(entities: Vec<codec::EntityType>, defined: &HashMap<String, String>) -> Vec<codec::EntityType> {
|
||||
entities
|
||||
.into_iter()
|
||||
.filter_map(|entity| match entity {
|
||||
codec::EntityType::Insert(mut insert) => {
|
||||
insert.block_name = defined.get(&insert.block_name)?.clone();
|
||||
Some(codec::EntityType::Insert(insert))
|
||||
}
|
||||
other => Some(other),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
let mut taken: Vec<String> = scene.document.block_records.iter().map(|b| b.name.clone()).collect();
|
||||
let mut defined: HashMap<String, String> = HashMap::new();
|
||||
for (name, entities) in imported.blocks {
|
||||
if scene.document.block_records.get(&name).is_none() {
|
||||
let _ = scene.define_block_from_owned_entities(entities, &name, glam::DVec3::ZERO);
|
||||
let unique = crate::modules::insert::xattach::unique_block_name(&name, &taken);
|
||||
let entities = retarget(entities, &defined);
|
||||
match scene.define_block_from_owned_entities(entities, &unique, glam::DVec3::ZERO) {
|
||||
Ok(_) => {
|
||||
taken.push(unique.clone());
|
||||
defined.insert(name, unique);
|
||||
}
|
||||
Err(error) => self.command_line.push_error(&format!("{name}: {error}")),
|
||||
}
|
||||
}
|
||||
for entity in imported.entities {
|
||||
for entity in retarget(imported.entities, &defined) {
|
||||
scene.add_entity(entity);
|
||||
}
|
||||
self.tabs[i].dirty = true;
|
||||
@@ -140,7 +149,7 @@ impl OpenCADStudio {
|
||||
crate::io::wmf_import::read(&bytes, [0.5 * size[0], 0.5 * size[1]])
|
||||
else {
|
||||
self.command_line
|
||||
.push_error(&format!("{} is not a valid Windows metafile.", path.replace('/', "\\")));
|
||||
.push_error(&format!("{path} is not a valid Windows metafile."));
|
||||
return Task::none();
|
||||
};
|
||||
self.push_undo_snapshot(i, if prompt { "WMFIN" } else { "IMPORT" });
|
||||
@@ -152,7 +161,8 @@ impl OpenCADStudio {
|
||||
let mut styles: Vec<String> = Vec::new();
|
||||
for (k, face) in picture.fonts.iter().enumerate() {
|
||||
let n = picture.fonts[..k].iter().filter(|f| f.name == face.name).count();
|
||||
let name = format!("WMF-{}{n}", face.name);
|
||||
// The face name comes from the file.
|
||||
let name = crate::scene::sanitized_table_name(&format!("WMF-{}{n}", face.name), "WMF");
|
||||
if !scene.document.text_styles.contains(&name) {
|
||||
let mut style = codec::tables::TextStyle::new(name.as_str());
|
||||
style.handle = scene.document.allocate_handle();
|
||||
@@ -210,7 +220,7 @@ impl OpenCADStudio {
|
||||
pub(in crate::app) fn open_tds_import_dialog(&mut self, path: &str) -> Result<(), String> {
|
||||
let scene = crate::scene::model::pdf_raster::source_bytes(path)
|
||||
.and_then(|bytes| crate::io::tds_import::read(&bytes))
|
||||
.ok_or_else(|| format!("{} is not a valid 3D Studio file.", path.replace('/', "\\")))?;
|
||||
.ok_or_else(|| format!("{path} is not a valid 3D Studio file."))?;
|
||||
let mut objects: Vec<(String, &'static str, usize)> = scene
|
||||
.objects
|
||||
.iter()
|
||||
@@ -322,10 +332,8 @@ impl OpenCADStudio {
|
||||
let Some(scene) = crate::scene::model::pdf_raster::source_bytes(&request.path)
|
||||
.and_then(|bytes| crate::io::tds_import::read(&bytes))
|
||||
else {
|
||||
self.command_line.push_error(&format!(
|
||||
"{} is not a valid 3D Studio file.",
|
||||
request.path.replace('/', "\\")
|
||||
));
|
||||
self.command_line
|
||||
.push_error(&format!("{} is not a valid 3D Studio file.", request.path));
|
||||
return;
|
||||
};
|
||||
let import = crate::io::tds_import::convert(
|
||||
@@ -335,10 +343,18 @@ impl OpenCADStudio {
|
||||
layers: request.layers,
|
||||
multi: request.multi.clone(),
|
||||
compact: request.compact,
|
||||
single_layer: self.tabs[i].scene.document.header.current_layer_name.clone(),
|
||||
},
|
||||
);
|
||||
// Named views take the current view's proportions.
|
||||
let (_, size, _) = self.tabs[i].scene.view_window();
|
||||
let aspect = if size[0] > 0.0 && size[1] > 0.0 { size[0] / size[1] } else { 1.0 };
|
||||
self.push_undo_snapshot(i, "IMPORT");
|
||||
let document = &mut self.tabs[i].scene.document;
|
||||
// Materials live in the material dictionary; a drawing without one
|
||||
// imports the meshes without materials.
|
||||
let dictionary = document.header.acad_material_dict_handle;
|
||||
let has_dictionary = matches!(document.objects.get(&dictionary), Some(ObjectType::Dictionary(_)));
|
||||
// Materials by name: the drawing's own when it has one, else one
|
||||
// made from the file's colours.
|
||||
let mut materials: std::collections::HashMap<String, codec::Handle> = Default::default();
|
||||
@@ -349,20 +365,18 @@ impl OpenCADStudio {
|
||||
});
|
||||
let handle = match existing {
|
||||
Some(h) => h,
|
||||
None if !has_dictionary => continue,
|
||||
None => {
|
||||
let source = scene.materials.iter().find(|m| m.name == *name);
|
||||
let mut material = codec::objects::Material::new();
|
||||
material.handle = document.allocate_handle();
|
||||
material.owner = document.header.acad_material_dict_handle;
|
||||
material.owner = dictionary;
|
||||
material.name = name.clone();
|
||||
let color = |rgb: Option<[u8; 3]>| match rgb {
|
||||
Some([r, g, b]) => codec::objects::MaterialColor {
|
||||
flag: 1,
|
||||
factor: 1.0,
|
||||
rgb: Some(
|
||||
((0xC2u32 << 24) | (u32::from(r) << 16) | (u32::from(g) << 8) | u32::from(b))
|
||||
as i32,
|
||||
),
|
||||
rgb: Some(crate::scene::convert::acis_import::packed_true_colour(r, g, b)),
|
||||
},
|
||||
None => codec::objects::MaterialColor::default(),
|
||||
};
|
||||
@@ -370,7 +384,6 @@ impl OpenCADStudio {
|
||||
material.diffuse_color = color(source.and_then(|m| m.diffuse));
|
||||
material.specular_color = color(source.and_then(|m| m.specular));
|
||||
let handle = material.handle;
|
||||
let dictionary = document.header.acad_material_dict_handle;
|
||||
if let Some(ObjectType::Dictionary(dictionary)) = document.objects.get_mut(&dictionary) {
|
||||
dictionary.add_entry(name.clone(), handle);
|
||||
}
|
||||
@@ -393,8 +406,9 @@ impl OpenCADStudio {
|
||||
named.direction =
|
||||
codec::types::Vector3::new(view.direction[0], view.direction[1], view.direction[2]);
|
||||
named.height = view.height;
|
||||
named.width = view.height;
|
||||
named.width = view.height * aspect;
|
||||
named.lens_length = view.lens;
|
||||
named.perspective = true;
|
||||
named.handle = match document.views.get(&view.name) {
|
||||
Some(old) => old.handle,
|
||||
None => document.allocate_handle(),
|
||||
|
||||
+39
-21
@@ -30,19 +30,14 @@ fn ratio(file_mm: f64, drawing_mm: Option<f64>) -> f64 {
|
||||
/// A block name from a product's, with the characters names cannot hold
|
||||
/// replaced.
|
||||
fn block_name(name: &str) -> String {
|
||||
let name: String = name
|
||||
.trim()
|
||||
.chars()
|
||||
.map(|c| if "<>/\\\":;?*|,=`".contains(c) { '_' } else { c })
|
||||
.collect();
|
||||
if name.is_empty() { "Part".to_string() } else { name }
|
||||
crate::scene::sanitized_table_name(name, "Part")
|
||||
}
|
||||
|
||||
/// A STEP presentation colour as the drawing colour it imports as: the
|
||||
/// standard index colour it is close to — red, yellow, green, cyan, blue,
|
||||
/// magenta, white, or the dark grey 250 (which black also becomes) — and
|
||||
/// otherwise the true colour.
|
||||
// ponytail: the closeness bands are fitted to measured imports (saturation
|
||||
// ponytail: the closeness bands are fitted to sample files (saturation
|
||||
// at least 0.75, value at least 0.825 and hue within 7.5 degrees for the six
|
||||
// hues; greys from 0.16 to 0.39 for 250 and from 0.935 for white); refit
|
||||
// them if a colour on a band edge comes out differently.
|
||||
@@ -114,22 +109,43 @@ fn step_blocks(model: crate::io::step_import::StepModel, scale: f64) -> Imported
|
||||
}
|
||||
names.push(name);
|
||||
}
|
||||
// Children before parents.
|
||||
// Children before parents. A link that closes a loop in the file's
|
||||
// assembly (a product that would contain itself) is dropped, so no
|
||||
// block ends up inserting itself.
|
||||
let mut order = Vec::new();
|
||||
let mut dropped: std::collections::HashSet<(usize, usize)> = Default::default();
|
||||
let mut state = vec![0u8; model.products.len()];
|
||||
fn visit(k: usize, model: &crate::io::step_import::StepModel, state: &mut [u8], order: &mut Vec<usize>) {
|
||||
if state[k] != 0 {
|
||||
return;
|
||||
}
|
||||
state[k] = 1;
|
||||
for (child, _) in &model.products[k].children {
|
||||
visit(*child, model, state, order);
|
||||
}
|
||||
state[k] = 2;
|
||||
order.push(k);
|
||||
}
|
||||
for &root in &model.roots {
|
||||
visit(root, &model, &mut state, &mut order);
|
||||
if state[root] != 0 {
|
||||
continue;
|
||||
}
|
||||
state[root] = 1;
|
||||
// Each product being walked and the next of its children to visit.
|
||||
let mut stack = vec![(root, 0usize)];
|
||||
while let Some((k, next)) = stack.last_mut() {
|
||||
let k = *k;
|
||||
match model.products[k].children.get(*next) {
|
||||
Some(&(child, _)) => {
|
||||
let link = (k, *next);
|
||||
*next += 1;
|
||||
match state[child] {
|
||||
0 => {
|
||||
state[child] = 1;
|
||||
stack.push((child, 0));
|
||||
}
|
||||
1 => {
|
||||
dropped.insert(link);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
state[k] = 2;
|
||||
order.push(k);
|
||||
stack.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let blocks = order
|
||||
.iter()
|
||||
@@ -147,7 +163,9 @@ fn step_blocks(model: crate::io::step_import::StepModel, scale: f64) -> Imported
|
||||
product
|
||||
.children
|
||||
.iter()
|
||||
.map(|(child, placement)| {
|
||||
.enumerate()
|
||||
.filter(|(n, _)| !dropped.contains(&(k, *n)))
|
||||
.map(|(_, (child, placement))| {
|
||||
let mut insert = placed_insert(&names[*child], placement);
|
||||
insert.common_mut().color = Color::ByBlock;
|
||||
insert
|
||||
|
||||
+42
-21
@@ -8,6 +8,8 @@ use std::collections::HashMap;
|
||||
use codec::entities::{PolyfaceFace, PolyfaceMesh, PolyfaceVertex};
|
||||
use codec::types::Vector3;
|
||||
|
||||
use crate::scene::sanitized_table_name;
|
||||
|
||||
pub struct TdsMaterial {
|
||||
pub name: String,
|
||||
pub ambient: Option<[u8; 3]>,
|
||||
@@ -258,6 +260,8 @@ pub struct TdsOptions {
|
||||
/// they first use them — as they do when the import splits by
|
||||
/// material or asks — rather than the file's vertex list.
|
||||
pub compact: bool,
|
||||
/// The layer every mesh goes on with [`LayerMode::Single`].
|
||||
pub single_layer: String,
|
||||
}
|
||||
|
||||
/// One imported mesh.
|
||||
@@ -284,14 +288,24 @@ pub struct TdsImport {
|
||||
pub messages: Vec<String>,
|
||||
}
|
||||
|
||||
/// A polyface mesh of `faces` of `mesh`: its vertices in file order, or
|
||||
/// Polyface meshes of `faces` of `mesh`: its vertices in file order, or
|
||||
/// (`compact`) only those the faces use, in the order they first use them.
|
||||
fn polyface(mesh: &TdsMesh, faces: &[usize], compact: bool) -> PolyfaceMesh {
|
||||
let mut out = PolyfaceMesh::new();
|
||||
/// A polyface mesh numbers its vertices in 16 bits, so a mesh with more
|
||||
/// vertices than that is always compact, and the faces that need further
|
||||
/// vertices go on further meshes.
|
||||
fn polyfaces(mesh: &TdsMesh, faces: &[usize], compact: bool) -> Vec<PolyfaceMesh> {
|
||||
const MAX_VERTICES: usize = i16::MAX as usize;
|
||||
let compact = compact || mesh.vertices.len() > MAX_VERTICES;
|
||||
let vertex = |v: usize| {
|
||||
let p = mesh.vertices[v];
|
||||
PolyfaceVertex::new(Vector3::new(p[0], p[1], p[2]))
|
||||
};
|
||||
let mut out = Vec::new();
|
||||
let mut current = PolyfaceMesh::new();
|
||||
let mut index: HashMap<usize, i16> = HashMap::new();
|
||||
if !compact {
|
||||
for (k, v) in mesh.vertices.iter().enumerate() {
|
||||
index.insert(k, out.add_vertex(PolyfaceVertex::new(Vector3::new(v[0], v[1], v[2]))));
|
||||
for k in 0..mesh.vertices.len() {
|
||||
index.insert(k, current.add_vertex(vertex(k)));
|
||||
}
|
||||
}
|
||||
for &f in faces {
|
||||
@@ -299,14 +313,15 @@ fn polyface(mesh: &TdsMesh, faces: &[usize], compact: bool) -> PolyfaceMesh {
|
||||
if face.iter().any(|v| *v >= mesh.vertices.len()) {
|
||||
continue;
|
||||
}
|
||||
let ids = face.map(|v| {
|
||||
*index.entry(v).or_insert_with(|| {
|
||||
let p = mesh.vertices[v];
|
||||
out.add_vertex(PolyfaceVertex::new(Vector3::new(p[0], p[1], p[2])))
|
||||
})
|
||||
});
|
||||
out.add_face(PolyfaceFace::triangle(ids[0], ids[1], ids[2]));
|
||||
let new = face.iter().filter(|v| !index.contains_key(v)).count();
|
||||
if current.vertices.len() + new > MAX_VERTICES {
|
||||
out.push(std::mem::replace(&mut current, PolyfaceMesh::new()));
|
||||
index.clear();
|
||||
}
|
||||
let ids = face.map(|v| *index.entry(v).or_insert_with(|| current.add_vertex(vertex(v))));
|
||||
current.add_face(PolyfaceFace::triangle(ids[0], ids[1], ids[2]));
|
||||
}
|
||||
out.push(current);
|
||||
out
|
||||
}
|
||||
|
||||
@@ -365,24 +380,30 @@ pub fn convert(scene: &TdsScene, options: &TdsOptions) -> TdsImport {
|
||||
out.materials.push(m.clone());
|
||||
}
|
||||
}
|
||||
// Object and material names come from the file, so they
|
||||
// are made fit to name a layer.
|
||||
let layer = match options.layers {
|
||||
LayerMode::ByObject => name.to_uppercase(),
|
||||
LayerMode::ByMaterial => material.as_deref().unwrap_or("0").to_uppercase(),
|
||||
LayerMode::ByObject => sanitized_table_name(&name.to_uppercase(), "0"),
|
||||
LayerMode::ByMaterial => {
|
||||
sanitized_table_name(&material.as_deref().unwrap_or("0").to_uppercase(), "0")
|
||||
}
|
||||
LayerMode::ByObjectColor => match mesh.color {
|
||||
Some(c) => format!("COLOR{c:02}"),
|
||||
None => "COLORNONE".to_string(),
|
||||
},
|
||||
LayerMode::Single => "AVLAYER".to_string(),
|
||||
LayerMode::Single => options.single_layer.clone(),
|
||||
};
|
||||
out.parts.push(TdsPart {
|
||||
layer,
|
||||
material,
|
||||
mesh: polyface(mesh, &faces, compact),
|
||||
});
|
||||
for polyface in polyfaces(mesh, &faces, compact) {
|
||||
out.parts.push(TdsPart {
|
||||
layer: layer.clone(),
|
||||
material: material.clone(),
|
||||
mesh: polyface,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
TdsObject::Camera(camera) => {
|
||||
let name = name.to_uppercase();
|
||||
let name = sanitized_table_name(&name.to_uppercase(), "CAMERA");
|
||||
out.messages.push(format!("Creating camera {name}"));
|
||||
let direction = [0, 1, 2].map(|k| camera.position[k] - camera.target[k]);
|
||||
let distance = direction.iter().map(|d| d * d).sum::<f64>().sqrt();
|
||||
|
||||
@@ -3501,6 +3501,7 @@ pub(super) fn message_attribute(source: &str) -> Option<(&'static str, &'static
|
||||
"Import File" => Some(("dgn-import", "import-file")),
|
||||
"DGN Import" => Some(("dgn-import", "dgn-import")),
|
||||
"Import DGN File" => Some(("dgn-import", "import-dgn-file")),
|
||||
"Import WMF" => Some(("dgn-import", "import-wmf")),
|
||||
"Imports a DGN model as drawing objects: levels as layers, cells as blocks." => Some(("dgn-import", "help-dgn")),
|
||||
"Imports a file of the chosen format with the format's importer." => Some(("dgn-import", "help-import")),
|
||||
"Filename cannot be blank" => Some(("dgn-import", "blank")),
|
||||
@@ -4425,6 +4426,7 @@ pub(super) fn message_attribute(source: &str) -> Option<(&'static str, &'static
|
||||
"DGN Files" => Some(("common", "dgn-files")),
|
||||
"Select DWF File" => Some(("common", "select-dwf-file")),
|
||||
"Select DGN File" => Some(("common", "select-dgn-file")),
|
||||
"Select ACIS File" => Some(("common", "select-acis-file")),
|
||||
"PDFATTACH: {e}" => Some(("common", "pdfattach")),
|
||||
"Select External Reference File" => Some(("common", "select-external-reference-file")),
|
||||
"CAD Files" => Some(("common", "cad-files")),
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
//! ACIS text (SAT) files and kernel bodies as drawing objects.
|
||||
//!
|
||||
//! Each body of a file becomes one object: a closed solid a 3DSOLID, a sheet
|
||||
//! whose faces all lie on planes a REGION, any other sheet a SURFACE — the
|
||||
//! classes the ACIS import of the reference application produces.
|
||||
//! whose faces all lie on planes a REGION, any other sheet a SURFACE.
|
||||
|
||||
use codec::entities::acis::{SatDocument, SatFace, Sidedness};
|
||||
use codec::entities::{AcisData, Region, Solid3D, Surface, SurfaceKind};
|
||||
@@ -82,14 +81,19 @@ fn attribute_index(color: codec::types::Color) -> i64 {
|
||||
}
|
||||
}
|
||||
|
||||
/// A true colour packed as a 32-bit colour value: the true-colour flag
|
||||
/// byte, then red, green and blue.
|
||||
pub(crate) fn packed_true_colour(r: u8, g: u8, b: u8) -> i32 {
|
||||
((0xC2u32 << 24) | (u32::from(r) << 16) | (u32::from(g) << 8) | u32::from(b)) as i32
|
||||
}
|
||||
|
||||
/// Hangs colour attributes on a record: a true colour first, then its
|
||||
/// index, as imported models carry them.
|
||||
fn paint_record(document: &mut codec::entities::acis::SatDocument, owner: usize, color: codec::types::Color) {
|
||||
use codec::entities::acis::{SatPointer, SatRecord, SatToken};
|
||||
let mut chain: Vec<(&str, i64)> = Vec::new();
|
||||
if let codec::types::Color::Rgb { r, g, b } = color {
|
||||
let packed = (0xC2u32 << 24) | (u32::from(r) << 16) | (u32::from(g) << 8) | u32::from(b);
|
||||
chain.push(("truecolor-adesk-attrib", i64::from(packed as i32)));
|
||||
chain.push(("truecolor-adesk-attrib", i64::from(packed_true_colour(r, g, b))));
|
||||
}
|
||||
chain.push(("color-adesk-attrib", attribute_index(color)));
|
||||
let Some(existing) = document.record(owner).map(|record| record.attribute) else {
|
||||
@@ -116,9 +120,9 @@ fn paint_record(document: &mut codec::entities::acis::SatDocument, owner: usize,
|
||||
}
|
||||
|
||||
/// A kernel body as its drawing object in `colour` (by block when `None`),
|
||||
/// with the faces given a colour of their own carrying it — and the edges
|
||||
/// bounding them too, as the reference modeler stores them. A `sheet` is a
|
||||
/// SURFACE however flat it is.
|
||||
/// with the faces given a colour of their own carrying it, and the edges
|
||||
/// bounding those faces carrying it too. A `sheet` is a SURFACE however
|
||||
/// flat it is.
|
||||
pub fn painted_body_entity(
|
||||
body: &kernel::brep::Body,
|
||||
colour: Option<codec::types::Color>,
|
||||
|
||||
@@ -1208,6 +1208,20 @@ pub(crate) fn valid_block_name(name: &str) -> bool {
|
||||
})
|
||||
}
|
||||
|
||||
/// `raw` made fit to name a table entry (a block, layer, style or view):
|
||||
/// characters [`valid_block_name`] refuses become `_`, the name is cut to
|
||||
/// 255 characters, and `fallback` stands in when nothing is left. For names
|
||||
/// that come from outside files.
|
||||
pub(crate) fn sanitized_table_name(raw: &str, fallback: &str) -> String {
|
||||
let name: String = raw
|
||||
.trim()
|
||||
.chars()
|
||||
.map(|c| if valid_block_name(c.encode_utf8(&mut [0; 4])) { c } else { '_' })
|
||||
.take(255)
|
||||
.collect();
|
||||
block_service::validate_block_name(&name).unwrap_or_else(|_| fallback.to_string())
|
||||
}
|
||||
|
||||
/// Pre-built entity caches returned by [`build_derived_caches`].
|
||||
/// Produced in the file-load background task so the UI thread only assigns.
|
||||
#[derive(Debug, Clone)]
|
||||
|
||||
Reference in new issue
Block a user