diff --git a/locales/ar-SA/opencadstudio.ftl b/locales/ar-SA/opencadstudio.ftl index 3294a3489..b4ef65ab0 100644 --- a/locales/ar-SA/opencadstudio.ftl +++ b/locales/ar-SA/opencadstudio.ftl @@ -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 diff --git a/locales/bg-BG/opencadstudio.ftl b/locales/bg-BG/opencadstudio.ftl index fc1bd29f7..3098e3c76 100644 --- a/locales/bg-BG/opencadstudio.ftl +++ b/locales/bg-BG/opencadstudio.ftl @@ -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 diff --git a/locales/cs-CZ/opencadstudio.ftl b/locales/cs-CZ/opencadstudio.ftl index 7a772cbe5..6ab862da4 100644 --- a/locales/cs-CZ/opencadstudio.ftl +++ b/locales/cs-CZ/opencadstudio.ftl @@ -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 diff --git a/locales/de-DE/opencadstudio.ftl b/locales/de-DE/opencadstudio.ftl index a02d69b58..4ed55bd97 100644 --- a/locales/de-DE/opencadstudio.ftl +++ b/locales/de-DE/opencadstudio.ftl @@ -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 diff --git a/locales/el-GR/opencadstudio.ftl b/locales/el-GR/opencadstudio.ftl index d628d3b69..352bb4ade 100644 --- a/locales/el-GR/opencadstudio.ftl +++ b/locales/el-GR/opencadstudio.ftl @@ -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 diff --git a/locales/en-US/opencadstudio.ftl b/locales/en-US/opencadstudio.ftl index b58b35734..59a064489 100644 --- a/locales/en-US/opencadstudio.ftl +++ b/locales/en-US/opencadstudio.ftl @@ -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 diff --git a/locales/es-ES/opencadstudio.ftl b/locales/es-ES/opencadstudio.ftl index 1dbe7a9d2..4f286a5ee 100644 --- a/locales/es-ES/opencadstudio.ftl +++ b/locales/es-ES/opencadstudio.ftl @@ -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 diff --git a/locales/fi-FI/opencadstudio.ftl b/locales/fi-FI/opencadstudio.ftl index f9c4ac4ba..8406bf191 100644 --- a/locales/fi-FI/opencadstudio.ftl +++ b/locales/fi-FI/opencadstudio.ftl @@ -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 diff --git a/locales/fr-FR/opencadstudio.ftl b/locales/fr-FR/opencadstudio.ftl index af28e9df6..7b7617457 100644 --- a/locales/fr-FR/opencadstudio.ftl +++ b/locales/fr-FR/opencadstudio.ftl @@ -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 diff --git a/locales/hi-IN/opencadstudio.ftl b/locales/hi-IN/opencadstudio.ftl index 2e8616557..5085c23e4 100644 --- a/locales/hi-IN/opencadstudio.ftl +++ b/locales/hi-IN/opencadstudio.ftl @@ -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 diff --git a/locales/hu-HU/opencadstudio.ftl b/locales/hu-HU/opencadstudio.ftl index 54b3e44a7..5c7437bff 100644 --- a/locales/hu-HU/opencadstudio.ftl +++ b/locales/hu-HU/opencadstudio.ftl @@ -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 diff --git a/locales/it-IT/opencadstudio.ftl b/locales/it-IT/opencadstudio.ftl index 7d8f063bd..2a62e6357 100644 --- a/locales/it-IT/opencadstudio.ftl +++ b/locales/it-IT/opencadstudio.ftl @@ -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 diff --git a/locales/ja-JP/opencadstudio.ftl b/locales/ja-JP/opencadstudio.ftl index 169061f1e..7ec514c93 100644 --- a/locales/ja-JP/opencadstudio.ftl +++ b/locales/ja-JP/opencadstudio.ftl @@ -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 diff --git a/locales/ko-KR/opencadstudio.ftl b/locales/ko-KR/opencadstudio.ftl index 880d58f4d..e88b54ec4 100644 --- a/locales/ko-KR/opencadstudio.ftl +++ b/locales/ko-KR/opencadstudio.ftl @@ -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 diff --git a/locales/lo-LA/opencadstudio.ftl b/locales/lo-LA/opencadstudio.ftl index 884e0e6d0..de3ebe1ba 100644 --- a/locales/lo-LA/opencadstudio.ftl +++ b/locales/lo-LA/opencadstudio.ftl @@ -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 diff --git a/locales/nl-NL/opencadstudio.ftl b/locales/nl-NL/opencadstudio.ftl index 107f0ad66..5f59b0c44 100644 --- a/locales/nl-NL/opencadstudio.ftl +++ b/locales/nl-NL/opencadstudio.ftl @@ -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 diff --git a/locales/pl-PL/opencadstudio.ftl b/locales/pl-PL/opencadstudio.ftl index d0826c8d3..3a5ac1cd6 100644 --- a/locales/pl-PL/opencadstudio.ftl +++ b/locales/pl-PL/opencadstudio.ftl @@ -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 diff --git a/locales/pt-BR/opencadstudio.ftl b/locales/pt-BR/opencadstudio.ftl index ac6f48463..6aeaf1e70 100644 --- a/locales/pt-BR/opencadstudio.ftl +++ b/locales/pt-BR/opencadstudio.ftl @@ -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 diff --git a/locales/ru-RU/opencadstudio.ftl b/locales/ru-RU/opencadstudio.ftl index ba7834740..b8ae4319a 100644 --- a/locales/ru-RU/opencadstudio.ftl +++ b/locales/ru-RU/opencadstudio.ftl @@ -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 diff --git a/locales/tr-TR/opencadstudio.ftl b/locales/tr-TR/opencadstudio.ftl index d38b23fcb..8ccbb75ba 100644 --- a/locales/tr-TR/opencadstudio.ftl +++ b/locales/tr-TR/opencadstudio.ftl @@ -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 diff --git a/locales/zh-CN/opencadstudio.ftl b/locales/zh-CN/opencadstudio.ftl index 2461ce671..8d2e6e14b 100644 --- a/locales/zh-CN/opencadstudio.ftl +++ b/locales/zh-CN/opencadstudio.ftl @@ -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 diff --git a/locales/zh-TW/opencadstudio.ftl b/locales/zh-TW/opencadstudio.ftl index f38b013f7..ea0d94fdb 100644 --- a/locales/zh-TW/opencadstudio.ftl +++ b/locales/zh-TW/opencadstudio.ftl @@ -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 diff --git a/src/app/commands/model_import.rs b/src/app/commands/model_import.rs index d66565b7c..66d2bc032 100644 --- a/src/app/commands/model_import.rs +++ b/src/app/commands/model_import.rs @@ -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, defined: &HashMap) -> Vec { + 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 = scene.document.block_records.iter().map(|b| b.name.clone()).collect(); + let mut defined: HashMap = 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 = 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 = 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(), diff --git a/src/io/model_import.rs b/src/io/model_import.rs index f5515a0ae..4b72358e6 100644 --- a/src/io/model_import.rs +++ b/src/io/model_import.rs @@ -30,19 +30,14 @@ fn ratio(file_mm: f64, drawing_mm: Option) -> 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) { - 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 diff --git a/src/io/tds_import.rs b/src/io/tds_import.rs index 46f4c2e27..56490406c 100644 --- a/src/io/tds_import.rs +++ b/src/io/tds_import.rs @@ -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, } -/// 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 { + 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 = 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::().sqrt(); diff --git a/src/locale_catalog.rs b/src/locale_catalog.rs index 4c5d5fbc3..fa7f4b9e0 100644 --- a/src/locale_catalog.rs +++ b/src/locale_catalog.rs @@ -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")), diff --git a/src/scene/convert/acis_import.rs b/src/scene/convert/acis_import.rs index f7485b328..e88746a2f 100644 --- a/src/scene/convert/acis_import.rs +++ b/src/scene/convert/acis_import.rs @@ -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, diff --git a/src/scene/mod.rs b/src/scene/mod.rs index 0b2958a82..3297b5433 100644 --- a/src/scene/mod.rs +++ b/src/scene/mod.rs @@ -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)]