Cargo: codec 76d2a1c, kernel e652b4d, graph 0545fa9; text styles, templates, solid edges

- Text styles keep their TrueType typeface through save and reopen (codec
  reads and writes the STYLE record's ACAD extended data) (#1633)
- Unnamed shape-file entries no longer show in text style lists (#1634)
- Save As offers DWT 2018; NEW asks for the template to start from, while
  Ctrl+N and the quick-access New run QNEW with the default template (#1635)
- An edge next to an earlier fillet of the same radius rounds together with
  it (kernel); the solid stays drawn and pickable while edges are chosen
  (#1625, #1107)
- A full cache rebuild drops the B-rep cache too, so an undone fillet does
  not come back with the next edge operation (#1107)
- Selected solids in shaded styles highlight only their visible edges (#1031)
- VIEW ISO / SWISO / SEISO / NEISO / NWISO snap to the isometric corners

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Hakan SevenandClaude Opus 5.5 committed 2026-10-06 18:32:43 +03:00
1 parent 1cbcc8f4c5
commit 1eae36e726
43 files changed
+152 -34

No files matched your search

Generated
+4 -4
View File
@@ -4205,7 +4205,7 @@ dependencies = [
[[package]]
name = "opencadcodec"
version = "0.6.0"
source = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=aad7053#aad70533211bd9192545729f7eb3bccb827bcf30"
source = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=76d2a1c#76d2a1c66702d9aaf3ef8d4ff7dc06f4a025da82"
dependencies = [
"anyhow",
"bitflags 2.13.1",
@@ -4228,7 +4228,7 @@ dependencies = [
[[package]]
name = "opencadgraph"
version = "0.1.0"
source = "git+https://github.com/HakanSeven12/opencadgraph.git?rev=263de59#263de5924e3fffcbb78377db5ed1117f50620666"
source = "git+https://github.com/HakanSeven12/opencadgraph.git?rev=0545fa9#0545fa9a9944160ac1a941c91ba3d4501de80a88"
dependencies = [
"opencadkernel",
"serde_json",
@@ -4237,7 +4237,7 @@ dependencies = [
[[package]]
name = "opencadkernel"
version = "0.1.0"
source = "git+https://github.com/HakanSeven12/opencadkernel.git?rev=1a52eaf#1a52eaf237949a4492a7b56907922218ab2d1ea9"
source = "git+https://github.com/HakanSeven12/opencadkernel.git?rev=e652b4d#e652b4dfcd8b1055a113a5818ecfa9af5e420be4"
dependencies = [
"cavalier_contours",
"opencadcodec",
@@ -4248,7 +4248,7 @@ dependencies = [
[[package]]
name = "opencadkernel-constraints"
version = "0.1.0"
source = "git+https://github.com/HakanSeven12/opencadkernel.git?rev=1a52eaf#1a52eaf237949a4492a7b56907922218ab2d1ea9"
source = "git+https://github.com/HakanSeven12/opencadkernel.git?rev=e652b4d#e652b4dfcd8b1055a113a5818ecfa9af5e420be4"
dependencies = [
"nalgebra",
]
+4 -5
View File
@@ -34,10 +34,10 @@ rfd = "0.17"
clap = { version = "4", features = ["derive", "string"] }
env_logger = "0.11"
log = "0.4"
codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "aad7053", features = ["serde"] }
kernel = { package = "opencadkernel", git = "https://github.com/HakanSeven12/opencadkernel.git", rev = "1a52eaf", features = ["acis", "offset"] }
kernel-constraints = { package = "opencadkernel-constraints", git = "https://github.com/HakanSeven12/opencadkernel.git", rev = "1a52eaf" }
graph = { package = "opencadgraph", git = "https://github.com/HakanSeven12/opencadgraph.git", rev = "263de59" }
codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "76d2a1c", features = ["serde"] }
kernel = { package = "opencadkernel", git = "https://github.com/HakanSeven12/opencadkernel.git", rev = "e652b4d", features = ["acis", "offset"] }
kernel-constraints = { package = "opencadkernel-constraints", git = "https://github.com/HakanSeven12/opencadkernel.git", rev = "e652b4d" }
graph = { package = "opencadgraph", git = "https://github.com/HakanSeven12/opencadgraph.git", rev = "0545fa9" }
dwg-thumbnailer = { path = "crates/dwg-thumbnailer" }
flate2 = "1"
image = { version = "0.25", default-features = false, features = ["png", "jpeg", "bmp", "tiff"] }
@@ -181,4 +181,3 @@ harness = false
[[test]]
name = "integration"
path = "tests/integration.rs"
+2 -2
View File
@@ -15,7 +15,7 @@ serde_json = { version = "1", optional = true }
# Pulled in only by the `host` feature, which adds the `opencadcodec`-typed
# `HostApi` runtime surface. The default crate stays dependency-free so engine
# crates and external tooling can depend on the manifest/ribbon contract cheaply.
codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "aad7053", optional = true, features = ["serde"] }
codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "76d2a1c", optional = true, features = ["serde"] }
# Runtime IPC and serialization (host feature only).
interprocess = { version = "2", optional = true }
@@ -39,7 +39,7 @@ serde_json = "1"
serde = { version = "1", features = ["derive"] }
cargo-lock = "11"
# opencadcodec is scanned at build time to generate the embedded type registry.
codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "aad7053", features = ["serde"] }
codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "76d2a1c", features = ["serde"] }
[dev-dependencies]
serde_json = "1"
+1 -1
View File
@@ -8,7 +8,7 @@ publish = false
crate-type = ["cdylib"]
[dependencies]
codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "aad7053", features = ["serde"] }
codec = { package = "opencadcodec", git = "https://github.com/HakanSeven12/opencadcodec.git", rev = "76d2a1c", features = ["serde"] }
bincode = "1.3"
serde = { version = "1", features = ["derive"] }
console_error_panic_hook = "0.1"
+1
View File
@@ -2348,6 +2348,7 @@ common =
.chspace-no-viewport = CHSPACE: لا توجد نافذة عرض مسقطية لتغيير الفضاء من خلالها.
.chspace-to-paper = CHSPACE: تم نقل __ocs_fmt_0__ كائن إلى فضاء الورق.
.chspace-to-model = CHSPACE: تم نقل __ocs_fmt_0__ كائن إلى فضاء النموذج.
.select-template-title = تحديد قالب
errors =
.save-warning = تحذير الحفظ
.unable-to-save-drawing = تعذر حفظ الرسم
+1
View File
@@ -2335,6 +2335,7 @@ common =
.chspace-no-viewport = CHSPACE: Няма прозорец в план, през който да се смени пространството.
.chspace-to-paper = CHSPACE: __ocs_fmt_0__ обекта преместени в пространството на листа.
.chspace-to-model = CHSPACE: __ocs_fmt_0__ обекта преместени в пространството на модела.
.select-template-title = Избор на шаблон
errors =
.save-warning = Предупреждение при запазване
.unable-to-save-drawing = Чертежът не може да бъде запазен
+1
View File
@@ -2335,6 +2335,7 @@ common =
.chspace-no-viewport = CHSPACE: Není výřez v půdorysném pohledu, přes který změnit prostor.
.chspace-to-paper = CHSPACE: __ocs_fmt_0__ objektů přesunuto do výkresového prostoru.
.chspace-to-model = CHSPACE: __ocs_fmt_0__ objektů přesunuto do modelového prostoru.
.select-template-title = Vybrat šablonu
errors =
.save-warning = Uložit varování
.unable-to-save-drawing = Nákres nelze uložit
+1
View File
@@ -2336,6 +2336,7 @@ common =
.chspace-no-viewport = CHSPACE: Kein Ansichtsfenster in Draufsicht zum Wechseln des Bereichs.
.chspace-to-paper = CHSPACE: __ocs_fmt_0__ Objekt(e) in den Papierbereich verschoben.
.chspace-to-model = CHSPACE: __ocs_fmt_0__ Objekt(e) in den Modellbereich verschoben.
.select-template-title = Vorlage auswählen
errors =
.save-warning = Warnhinweise speichern
.unable-to-save-drawing = Zeichnung konnte nicht gespeichert werden
+1
View File
@@ -2335,6 +2335,7 @@ common =
.chspace-no-viewport = CHSPACE: Δεν υπάρχει παράθυρο προβολής κάτοψης για αλλαγή χώρου.
.chspace-to-paper = CHSPACE: __ocs_fmt_0__ αντικείμενα μεταφέρθηκαν στον χώρο χαρτιού.
.chspace-to-model = CHSPACE: __ocs_fmt_0__ αντικείμενα μεταφέρθηκαν στον χώρο μοντέλου.
.select-template-title = Επιλογή προτύπου
errors =
.save-warning = Προειδοποίηση αποθήκευσης
.unable-to-save-drawing = Αδυναμία αποθήκευσης σχεδίου
+1
View File
@@ -2336,6 +2336,7 @@ common =
.chspace-no-viewport = CHSPACE: No plan-view viewport to change space through.
.chspace-to-paper = CHSPACE: __ocs_fmt_0__ object(s) changed to paper space.
.chspace-to-model = CHSPACE: __ocs_fmt_0__ object(s) changed to model space.
.select-template-title = Select Template
errors =
.save-warning = Save Warning
.unable-to-save-drawing = Unable to Save Drawing
+1
View File
@@ -2336,6 +2336,7 @@ common =
.chspace-no-viewport = CHSPACE: No hay ventana gráfica en vista en planta para cambiar de espacio.
.chspace-to-paper = CHSPACE: __ocs_fmt_0__ objeto(s) pasado(s) al espacio papel.
.chspace-to-model = CHSPACE: __ocs_fmt_0__ objeto(s) pasado(s) al espacio modelo.
.select-template-title = Seleccionar plantilla
errors =
.save-warning = Advertencia de guardado
.unable-to-save-drawing = No se puede guardar el dibujo
+1
View File
@@ -2335,6 +2335,7 @@ common =
.chspace-no-viewport = CHSPACE: Ei tasokuvan näkymäikkunaa tilan vaihtoon.
.chspace-to-paper = CHSPACE: __ocs_fmt_0__ objektia siirretty paperitilaan.
.chspace-to-model = CHSPACE: __ocs_fmt_0__ objektia siirretty mallitilaan.
.select-template-title = Valitse malli
errors =
.save-warning = Tallenna varoitus
.unable-to-save-drawing = Piirustusta ei voi tallentaa
+1
View File
@@ -2336,6 +2336,7 @@ common =
.chspace-no-viewport = CHSPACE : aucune fenêtre en vue en plan pour changer d'espace.
.chspace-to-paper = CHSPACE : __ocs_fmt_0__ objet(s) déplacé(s) dans l'espace papier.
.chspace-to-model = CHSPACE : __ocs_fmt_0__ objet(s) déplacé(s) dans l'espace objet.
.select-template-title = Sélectionner un gabarit
errors =
.save-warning = Enregistrer l' avertissement
.unable-to-save-drawing = Impossible de sauvegarder le dessin
+1
View File
@@ -2324,6 +2324,7 @@ common =
.chspace-no-viewport = CHSPACE: स्पेस बदलने के लिए कोई प्लान-व्यू व्यूपोर्ट नहीं।
.chspace-to-paper = CHSPACE: __ocs_fmt_0__ ऑब्जेक्ट पेपर स्पेस में बदले गए।
.chspace-to-model = CHSPACE: __ocs_fmt_0__ ऑब्जेक्ट मॉडल स्पेस में बदले गए।
.select-template-title = टेम्पलेट चुनें
errors =
.save-warning = चेतावनी सहेजें
.unable-to-save-drawing = ड्रॉइंग सहेजी नहीं जा सकी
+1
View File
@@ -2335,6 +2335,7 @@ common =
.chspace-no-viewport = CHSPACE: Nincs felülnézeti nézetablak a tér váltásához.
.chspace-to-paper = CHSPACE: __ocs_fmt_0__ objektum papírtérbe helyezve.
.chspace-to-model = CHSPACE: __ocs_fmt_0__ objektum modelltérbe helyezve.
.select-template-title = Sablon kiválasztása
errors =
.save-warning = Figyelmeztetés mentése
.unable-to-save-drawing = Nem sikerült menteni a rajzot
+1
View File
@@ -2335,6 +2335,7 @@ common =
.chspace-no-viewport = CHSPACE: nessuna finestra in vista in pianta attraverso cui cambiare spazio.
.chspace-to-paper = CHSPACE: __ocs_fmt_0__ oggetto/i spostato/i nello spazio carta.
.chspace-to-model = CHSPACE: __ocs_fmt_0__ oggetto/i spostato/i nello spazio modello.
.select-template-title = Seleziona modello
errors =
.save-warning = Salva avviso
.unable-to-save-drawing = Impossibile salvare il disegno
+1
View File
@@ -2326,6 +2326,7 @@ common =
.chspace-no-viewport = CHSPACE: 空間を変更できる平面ビューのビューポートがありません。
.chspace-to-paper = CHSPACE: __ocs_fmt_0__ 個のオブジェクトをペーパー空間に移動しました。
.chspace-to-model = CHSPACE: __ocs_fmt_0__ 個のオブジェクトをモデル空間に移動しました。
.select-template-title = テンプレートを選択
errors =
.save-warning = 保存の警告
.unable-to-save-drawing = 図面を保存できません
+1
View File
@@ -2335,6 +2335,7 @@ common =
.chspace-no-viewport = CHSPACE: 공간을 변경할 평면 뷰포트가 없습니다.
.chspace-to-paper = CHSPACE: 객체 __ocs_fmt_0__개를 도면 공간으로 옮겼습니다.
.chspace-to-model = CHSPACE: 객체 __ocs_fmt_0__개를 모형 공간으로 옮겼습니다.
.select-template-title = 템플릿 선택
errors =
.save-warning = 경고 저장
.unable-to-save-drawing = 도면을 저장할 수 없습니다
+1
View File
@@ -2336,6 +2336,7 @@ common =
.chspace-no-viewport = CHSPACE: geen viewport in bovenaanzicht om de ruimte via te wisselen.
.chspace-to-paper = CHSPACE: __ocs_fmt_0__ object(en) naar de papierruimte verplaatst.
.chspace-to-model = CHSPACE: __ocs_fmt_0__ object(en) naar de modelruimte verplaatst.
.select-template-title = Sjabloon selecteren
errors =
.save-warning = Waarschuwing opslaan
.unable-to-save-drawing = Kan tekening niet opslaan
+1
View File
@@ -2335,6 +2335,7 @@ common =
.chspace-no-viewport = CHSPACE: brak rzutni w widoku z góry do zmiany obszaru.
.chspace-to-paper = CHSPACE: przeniesiono obiekty do obszaru papieru: __ocs_fmt_0__.
.chspace-to-model = CHSPACE: przeniesiono obiekty do obszaru modelu: __ocs_fmt_0__.
.select-template-title = Wybierz szablon
errors =
.save-warning = Zapisz ostrzeżenie
.unable-to-save-drawing = Nie można zapisać rysunku
+1
View File
@@ -2336,6 +2336,7 @@ common =
.chspace-no-viewport = CHSPACE: nenhuma janela de vista em planta para mudar de espaço.
.chspace-to-paper = CHSPACE: __ocs_fmt_0__ objeto(s) movido(s) para o espaço do papel.
.chspace-to-model = CHSPACE: __ocs_fmt_0__ objeto(s) movido(s) para o espaço do modelo.
.select-template-title = Selecionar modelo
errors =
.save-warning = Aviso de salvamento
.unable-to-save-drawing = Não foi possível salvar o desenho
+1
View File
@@ -2339,6 +2339,7 @@ common =
.chspace-no-viewport = CHSPACE: нет видового экрана в плане для смены пространства.
.chspace-to-paper = CHSPACE: перенесено в пространство листа объектов: __ocs_fmt_0__.
.chspace-to-model = CHSPACE: перенесено в пространство модели объектов: __ocs_fmt_0__.
.select-template-title = Выбор шаблона
errors =
.save-warning = Спасти предостережение
.unable-to-save-drawing = Не удалось сохранить чертёж
+1
View File
@@ -2320,6 +2320,7 @@ common =
.chspace-no-viewport = CHSPACE: Alan değiştirmek için plan görünümlü görünüm penceresi yok.
.chspace-to-paper = CHSPACE: __ocs_fmt_0__ nesne kağıt alanına taşındı.
.chspace-to-model = CHSPACE: __ocs_fmt_0__ nesne model alanına taşındı.
.select-template-title = Şablon Seç
errors =
.save-warning = Kaydetme Uyarısı
.unable-to-save-drawing = Çizim Kaydedilemiyor
+1
View File
@@ -2323,6 +2323,7 @@ common =
.chspace-no-viewport = CHSPACE: 没有可用于更改空间的平面视图视口。
.chspace-to-paper = CHSPACE: 已将 __ocs_fmt_0__ 个对象移到图纸空间。
.chspace-to-model = CHSPACE: 已将 __ocs_fmt_0__ 个对象移到模型空间。
.select-template-title = 选择样板
errors =
.save-warning = 保存警告
.unable-to-save-drawing = 无法保存图形
+1
View File
@@ -2335,6 +2335,7 @@ common =
.chspace-no-viewport = CHSPACE: 沒有可用於變更空間的平面視圖視埠。
.chspace-to-paper = CHSPACE: 已將 __ocs_fmt_0__ 個物件移到圖紙空間。
.chspace-to-model = CHSPACE: 已將 __ocs_fmt_0__ 個物件移到模型空間。
.select-template-title = 選取樣板
errors =
.save-warning = 儲存警告
.unable-to-save-drawing = 無法儲存圖形
+1 -1
View File
@@ -1264,7 +1264,7 @@ dependencies = [
[[package]]
name = "opencadcodec"
version = "0.6.0"
source = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=aad7053#aad70533211bd9192545729f7eb3bccb827bcf30"
source = "git+https://github.com/HakanSeven12/opencadcodec.git?rev=76d2a1c#76d2a1c66702d9aaf3ef8d4ff7dc06f4a025da82"
dependencies = [
"anyhow",
"bitflags",
+19 -1
View File
@@ -3,7 +3,25 @@ use super::*;
impl OpenCADStudio {
pub(super) fn dispatch_fileops(&mut self, cmd: &str, i: usize) -> Option<Task<Message>> {
match cmd {
"NEW" => return Some(Task::done(Message::TabNew)),
// QNEW starts from the default template (Options > Files); NEW
// asks which template to start from (#1635).
"QNEW" => return Some(Task::done(Message::TabNew)),
"NEW" => {
return Some(Task::perform(
async {
crate::sys::file_dialog()
.set_title(crate::t!("Select Template").as_ref())
.add_filter(
crate::t!("Drawing Template (*.dwt)").as_ref(),
&["dwt", "DWT", "dwg", "DWG", "dxf", "DXF"],
)
.pick_file()
.await
.map(|handle| crate::sys::handle_path(&handle))
},
Message::NewFromTemplate,
));
}
"OPEN" => return Some(Task::done(Message::OpenFile)),
"SAVE" | "QSAVE" => return Some(Task::done(Message::SaveFile)),
// SAVEALL — write every open drawing that already has a file path.
+19 -2
View File
@@ -875,8 +875,25 @@ impl OpenCADStudio {
crate::scene::CubeRegion::Face(face),
)));
}
"ISO" | "ISOMETRIC" | "SWISO" => {
return Some(Task::done(Message::ViewCubeHome));
// Isometric presets snap to the matching ViewCube corner;
// ISO alone is the south-west one.
"ISO" | "ISOMETRIC" | "SWISO" | "SEISO" | "NEISO" | "NWISO" => {
let (x, y) = match sub.as_str() {
"SEISO" => (1.0, -1.0),
"NEISO" => (1.0, 1.0),
"NWISO" => (-1.0, 1.0),
_ => (-1.0, -1.0),
};
let want = glam::Vec3::new(x, y, 1.0).normalize();
let corner = (18..26)
.map(crate::scene::CubeRegion::Corner)
.max_by(|a, b| {
a.snap_direction()
.dot(want)
.total_cmp(&b.snap_direction().dot(want))
})
.expect("the cube has corners");
return Some(Task::done(Message::ViewCubeSnap(corner)));
}
// VIEW <name> shortcut for restore
_ => {
+1
View File
@@ -850,6 +850,7 @@ inventory::submit!(crate::command::CommandRegistration {
"CHSPACE",
"NAVVCUBE",
"NEW",
"QNEW",
"OBJIMPORT",
"OPEN",
"ORTHO",
+6 -1
View File
@@ -149,7 +149,12 @@ impl OpenCADStudio {
}
let doc = &self.tabs[i].scene.document;
let text_names: Vec<String> = doc.text_styles.iter().map(|s| s.name.clone()).collect();
let text_names: Vec<String> = doc
.text_styles
.iter()
.filter(|s| !s.is_shape_file)
.map(|s| s.name.clone())
.collect();
let active_text = doc.header.current_text_style_name.clone();
let active_text = if text_names.contains(&active_text) {
active_text
+2
View File
@@ -2299,6 +2299,8 @@ pub enum Message {
/// Options → Files: the default template for new drawings.
QnewTemplateChanged(String),
QnewTemplateBrowse,
/// NEW: the picked template a new drawing starts from (`None` = cancelled).
NewFromTemplate(Option<std::path::PathBuf>),
/// Set the default type/version used when first saving a new drawing.
DefaultSaveFormatChanged(String),
/// Select one of Iced's built-in themes or the editable Custom theme.
+1 -1
View File
@@ -27,7 +27,7 @@ pub(super) fn default_bindings() -> BTreeMap<String, String> {
("F12".to_string(), "DYNINPUT"),
(format!("{ACCEL}+0"), "CLEANSCREEN"),
(format!("{ACCEL}+1"), "PROPERTIES"),
(format!("{ACCEL}+N"), "NEW"),
(format!("{ACCEL}+N"), "QNEW"),
(format!("{ACCEL}+O"), "OPEN"),
(format!("{ACCEL}+P"), "PLOT"),
(format!("{ACCEL}+Q"), "QUIT"),
+8 -1
View File
@@ -234,7 +234,14 @@ impl OpenCADStudio {
v
};
match kind {
StyleKind::Text => doc.text_styles.iter().map(|s| s.name.clone()).collect(),
// Shape-file entries (complex linetype glyphs) are not text styles to
// pick or edit; they have no name (#1634).
StyleKind::Text => doc
.text_styles
.iter()
.filter(|s| !s.is_shape_file)
.map(|s| s.name.clone())
.collect(),
StyleKind::Dim => doc.dim_styles.iter().map(|s| s.name.clone()).collect(),
StyleKind::Table => from_objects(|o| match o {
ObjectType::TableStyle(s) => Some(s.name.as_str()),
+2
View File
@@ -3137,6 +3137,8 @@ impl OpenCADStudio {
let default_name = filename;
let (filter_label, filter_ext): (&str, &str) = if ext.eq_ignore_ascii_case("dxf") {
("DXF Files", "dxf")
} else if ext.eq_ignore_ascii_case("dwt") {
("Drawing Template (*.dwt)", "dwt")
} else {
("DWG Files", "dwg")
};
+16
View File
@@ -8028,6 +8028,22 @@ impl OpenCADStudio {
self.persist_settings_if_changed();
Task::none()
}
Message::NewFromTemplate(Some(path)) => {
let task = self.update(Message::TabNew);
let idx = self.active_tab;
match self.apply_template(&path.to_string_lossy()) {
Ok(_) => {
self.tabs[idx].dirty = false;
self.refresh_layer_panel();
self.sync_ribbon_styles();
}
Err(error) => self
.command_line
.push_error(error["error"].as_str().unwrap_or("template")),
}
task
}
Message::NewFromTemplate(None) => Task::none(),
Message::QnewTemplateBrowse => Task::perform(
async {
crate::sys::file_dialog()
+8 -2
View File
@@ -1152,7 +1152,9 @@ impl OpenCADStudio {
let mut lt_opts: Vec<String> = vec!["ByBlock".to_string()];
lt_opts.extend(doc.line_types.iter().map(|lt| lt.name.clone()));
let mut textstyle_opts: Vec<String> = vec!["None".to_string()];
textstyle_opts.extend(doc.text_styles.iter().map(|t| t.name.clone()));
textstyle_opts.extend(
doc.text_styles.iter().filter(|t| !t.is_shape_file).map(|t| t.name.clone()),
);
let opt_block = |h: Option<codec::types::Handle>| -> String {
match h {
Some(h) => doc
@@ -1416,7 +1418,11 @@ impl OpenCADStudio {
let mut lt_opts: Vec<String> = vec!["ByBlock".to_string()];
lt_opts.extend(doc.line_types.iter().map(|lt| lt.name.clone()));
let text_style_opts: Vec<String> =
doc.text_styles.iter().map(|style| style.name.clone()).collect();
doc.text_styles
.iter()
.filter(|style| !style.is_shape_file)
.map(|style| style.name.clone())
.collect();
let text_style_fixed_height = doc
.text_styles
.get(&self.ds_dimtxsty)
+12 -5
View File
@@ -1402,7 +1402,7 @@ pub const DEFAULT_SAVE_FORMAT: &str = "DWG 2018";
pub const SAVE_FORMAT_OPTIONS: &[&str] = &[
"DWG 2018", "DWG 2013", "DWG 2010", "DWG 2007", "DWG 2004", "DWG 2000", "DWG R14", "DXF 2018",
"DXF 2013", "DXF 2010", "DXF 2007", "DXF 2004", "DXF 2000", "DXF R14",
"DXF 2013", "DXF 2010", "DXF 2007", "DXF 2004", "DXF 2000", "DXF R14", "DWT 2018",
];
pub fn canonical_save_format(format: &str) -> &'static str {
@@ -1432,7 +1432,14 @@ pub fn parse_save_format(format: &str) -> (&'static str, codec::DxfVersion) {
use codec::DxfVersion;
let f = format.to_ascii_uppercase();
let is_dxf = f.starts_with("DXF");
let ext = if is_dxf { "dxf" } else { "dwg" };
// A drawing template (.dwt) is a DWG under another extension (#1635).
let ext = if is_dxf {
"dxf"
} else if f.starts_with("DWT") {
"dwt"
} else {
"dwg"
};
let version = if f.contains("2013") {
DxfVersion::AC1027
} else if f.contains("2010") {
@@ -1611,14 +1618,14 @@ impl std::fmt::Display for SaveFailure {
impl std::error::Error for SaveFailure {}
const SUPPORTED_SAVE_FORMATS: &str = ".dwg, .dxf";
const SUPPORTED_SAVE_FORMATS: &str = ".dwg, .dxf, .dwt";
fn validate_save_extension(path: &Path) -> Result<(), SaveFailure> {
let extension = path
.extension()
.map(|value| value.to_string_lossy().to_lowercase())
.unwrap_or_default();
if matches!(extension.as_str(), "dwg" | "dxf" | "sv$") {
if matches!(extension.as_str(), "dwg" | "dxf" | "dwt" | "sv$") {
return Ok(());
}
@@ -1684,7 +1691,7 @@ mod save_failure_tests {
assert_eq!(
error,
"unsupported output format .pdf; supported formats: .dwg, .dxf"
"unsupported output format .pdf; supported formats: .dwg, .dxf, .dwt"
);
assert!(!path.exists(), "unsupported export created an output file");
}
+1
View File
@@ -4878,6 +4878,7 @@ pub(super) fn message_attribute(source: &str) -> Option<(&'static str, &'static
"Unknown command: {}" => Some(("command", "unknown-command-cmd")),
"Unsaved changes will be discarded." => Some(("common", "unsaved-changes-will-be-discarded")),
"Discard & close" => Some(("common", "discard-close")),
"Select Template" => Some(("common", "select-template-title")),
"CHSPACE: Command not allowed in the Model tab." => Some(("common", "chspace-model-tab")),
"CHSPACE: No plan-view viewport to change space through." => Some(("common", "chspace-no-viewport")),
"CHSPACE: {count} object(s) changed to paper space." => Some(("common", "chspace-to-paper")),
+9 -1
View File
@@ -230,7 +230,15 @@ impl SolidEdgeCommand {
self.preview_for_values(self.default_value, self.other_value)
{
self.preview_wires = wires;
self.preview_hidden.push(handle);
// While edges are still being picked the source stays drawn: a
// hidden solid leaves the pick meshes, so the next edge could not
// be selected (#1625). The preview overlays it until confirmed.
if !matches!(
self.step,
EdgeStep::Selecting | EdgeStep::PickingLoop | EdgeStep::LoopConfirm
) {
self.preview_hidden.push(handle);
}
}
}
+4
View File
@@ -2575,6 +2575,10 @@ impl Scene {
if !incremental {
self.meshes.clear();
self.block_meshes.clear();
// The B-rep cache follows the entities too: an undo that rebuilds
// everything left the undone fillet's body here, and the next
// edge operation brought it back (#1107). Restored on demand.
self.solid_models.clear();
}
// BLOCK-entity handles of the layout (model + paper) blocks. A solid
// owned by one of these is top-level; anything else lives in a block
+8 -5
View File
@@ -4960,7 +4960,10 @@ analytic={:.1} regular={:.1} blocks={:.1}",
// ── Pass 7: selection overlay pass ───────────────────────────────
// Redraws selected wires and text with depth_compare=Always so both
// appear on top of all other geometry at full brightness.
// appear on top of all other geometry at full brightness — unless
// faces are drawn: then edges behind a surface stay hidden while
// selected, as they are when not (#1031).
let occluded = hidden_line || !mesh_wireframe;
let have_text_highlight =
(!self.text_highlight_gpu.is_empty())
|| !self.block_text_highlight_gpu.is_empty();
@@ -4997,7 +5000,7 @@ analytic={:.1} regular={:.1} blocks={:.1}",
pass.set_bind_group(0, &self.uniform_bind_group, &[]);
pass.set_stencil_reference(stencil_ref);
if !self.gpu_selected_wires.is_empty() {
pass.set_pipeline(if hidden_line {
pass.set_pipeline(if occluded {
&self.wire_pipeline
} else {
&self.wire_xray_pipeline
@@ -5016,7 +5019,7 @@ analytic={:.1} regular={:.1} blocks={:.1}",
}
}
if !self.gpu_selected_block_wires.is_empty() {
pass.set_pipeline(if hidden_line {
pass.set_pipeline(if occluded {
&self.block_wire_pipeline
} else {
&self.block_wire_xray_pipeline
@@ -5029,7 +5032,7 @@ analytic={:.1} regular={:.1} blocks={:.1}",
}
}
if !self.gpu_selected_circles.is_empty() {
pass.set_pipeline(if hidden_line {
pass.set_pipeline(if occluded {
&self.circle_pipeline
} else {
&self.circle_xray_pipeline
@@ -5043,7 +5046,7 @@ analytic={:.1} regular={:.1} blocks={:.1}",
}
}
if !self.gpu_selected_ellipses.is_empty() {
pass.set_pipeline(if hidden_line {
pass.set_pipeline(if occluded {
&self.ellipse_pipeline
} else {
&self.ellipse_xray_pipeline
+1 -1
View File
@@ -521,7 +521,7 @@ impl Ribbon {
) -> Element<'_, Message> {
// ── Quick-access file commands + undo/redo, one merged flow ────────
let lead = iced::widget::Row::with_children(vec![
quick_access_btn(crate::ui::icons::DOC_NEW, "New", "NEW").into(),
quick_access_btn(crate::ui::icons::DOC_NEW, "New", "QNEW").into(),
quick_access_btn(crate::ui::icons::FOLDER_OPEN, "Open", "OPEN").into(),
quick_access_btn(crate::ui::icons::SAVE, "Save", "SAVE").into(),
quick_access_btn(crate::ui::icons::FILE_EXPORT, "Save As", "SAVEAS").into(),
+2 -1
View File
@@ -425,7 +425,8 @@ pub fn view_window<'a>(
text(crate::tr!("options", "default-save-format-label")).size(12).width(150),
iced::widget::pick_list(
selected_format,
crate::io::SAVE_FORMAT_OPTIONS,
// Drawings, not the template entry (the list's last).
&crate::io::SAVE_FORMAT_OPTIONS[..crate::io::SAVE_FORMAT_OPTIONS.len() - 1],
|value| value.to_string(),
)
.on_select(|format: &str| Message::DefaultSaveFormatChanged(format.to_string()))