From cccea4008b6b062f1bfd94edb228b1c838b95f62 Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Thu, 15 Oct 2026 11:40:39 +0300 Subject: [PATCH] ucs: dynamic UCS on planar solid faces With dynamic UCS on (UCSDETECT 1, toggled with F6), a command's first point picked on a planar face of a solid sets the UCS to that face for the rest of the command, so 2D commands draw on the face (#1711). An unsnapped first point lands on the face itself; a snapped point off the face's plane belongs to other geometry and leaves the UCS alone. The previous UCS comes back when the command ends. UCS commands themselves are left out. Co-Authored-By: Claude Opus 5.5 --- locales/ar-SA/opencadstudio.ftl | 2 ++ locales/bg-BG/opencadstudio.ftl | 2 ++ locales/cs-CZ/opencadstudio.ftl | 2 ++ locales/de-DE/opencadstudio.ftl | 2 ++ locales/el-GR/opencadstudio.ftl | 2 ++ locales/en-US/opencadstudio.ftl | 2 ++ locales/es-ES/opencadstudio.ftl | 2 ++ locales/fi-FI/opencadstudio.ftl | 2 ++ locales/fr-FR/opencadstudio.ftl | 2 ++ locales/hi-IN/opencadstudio.ftl | 2 ++ locales/hu-HU/opencadstudio.ftl | 2 ++ locales/it-IT/opencadstudio.ftl | 2 ++ locales/ja-JP/opencadstudio.ftl | 2 ++ locales/ko-KR/opencadstudio.ftl | 2 ++ locales/lo-LA/opencadstudio.ftl | 2 ++ locales/nl-NL/opencadstudio.ftl | 2 ++ locales/pl-PL/opencadstudio.ftl | 2 ++ locales/pt-BR/opencadstudio.ftl | 2 ++ locales/ru-RU/opencadstudio.ftl | 2 ++ locales/tr-TR/opencadstudio.ftl | 2 ++ locales/zh-CN/opencadstudio.ftl | 2 ++ locales/zh-TW/opencadstudio.ftl | 2 ++ src/app/command_driver/utilities.rs | 45 +++++++++++++++++++++++++++++ src/app/commands/mod.rs | 1 + src/app/commands/styleprops.rs | 25 ++++++++++++++++ src/app/mod.rs | 10 +++++++ src/app/settings.rs | 4 +++ src/app/shortcuts.rs | 3 ++ src/app/update/file.rs | 2 ++ src/app/update/mod.rs | 32 ++++++++++++++++++++ src/app/update/viewport.rs | 6 ++++ src/locale_catalog.rs | 2 ++ src/scene/mod.rs | 19 ++++++++++++ 33 files changed, 193 insertions(+) diff --git a/locales/ar-SA/opencadstudio.ftl b/locales/ar-SA/opencadstudio.ftl index fb0f0f506..8701aca70 100644 --- a/locales/ar-SA/opencadstudio.ftl +++ b/locales/ar-SA/opencadstudio.ftl @@ -1567,6 +1567,8 @@ common = .nav-style-cad = CAD: ‏Shift + الزر الأوسط للدوران .nav-style-blender = Blender: الزر الأوسط للدوران، Shift للتحريك .nav-style-catia = CATIA: الزر الأوسط + الأيسر للدوران + .dynamic-ucs-on = نظام الإحداثيات الديناميكي: تشغيل + .dynamic-ucs-off = نظام الإحداثيات الديناميكي: إيقاف .touchpad-scroll-pans-ctrl-scroll-or-pinch-zooms = لوحة اللمس: التمرير يحرك العرض، Ctrl+تمرير أو القرص يكبّر .interface-scale = مقياس الواجهة .zoom-factor = معامل التكبير diff --git a/locales/bg-BG/opencadstudio.ftl b/locales/bg-BG/opencadstudio.ftl index 59cfd1106..9021c3cb6 100644 --- a/locales/bg-BG/opencadstudio.ftl +++ b/locales/bg-BG/opencadstudio.ftl @@ -1554,6 +1554,8 @@ common = .nav-style-cad = CAD: Shift + среден бутон завърта .nav-style-blender = Blender: среден бутон завърта, Shift премества .nav-style-catia = CATIA: среден + ляв бутон завърта + .dynamic-ucs-on = Динамична ПКС: вкл. + .dynamic-ucs-off = Динамична ПКС: изкл. .touchpad-scroll-pans-ctrl-scroll-or-pinch-zooms = Тъчпад: превъртането мести изгледа, Ctrl+превъртане или щипване мащабира .interface-scale = Мащаб на интерфейса .zoom-factor = Коефициент на мащабиране diff --git a/locales/cs-CZ/opencadstudio.ftl b/locales/cs-CZ/opencadstudio.ftl index 1c3e36f2b..f0023b21a 100644 --- a/locales/cs-CZ/opencadstudio.ftl +++ b/locales/cs-CZ/opencadstudio.ftl @@ -1554,6 +1554,8 @@ common = .nav-style-cad = CAD: Shift + prostřední tlačítko otáčí .nav-style-blender = Blender: prostřední tlačítko otáčí, Shift posouvá .nav-style-catia = CATIA: prostřední + levé tlačítko otáčí + .dynamic-ucs-on = Dynamický USS: zapnuto + .dynamic-ucs-off = Dynamický USS: vypnuto .touchpad-scroll-pans-ctrl-scroll-or-pinch-zooms = Touchpad: posun posouvá pohled, Ctrl+posun nebo sevření zvětšuje .interface-scale = Měřítko rozhraní .zoom-factor = Faktor zoomu diff --git a/locales/de-DE/opencadstudio.ftl b/locales/de-DE/opencadstudio.ftl index 3e321eea9..59f8b9705 100644 --- a/locales/de-DE/opencadstudio.ftl +++ b/locales/de-DE/opencadstudio.ftl @@ -1555,6 +1555,8 @@ common = .nav-style-cad = CAD: Umschalt + mittlere Taste dreht .nav-style-blender = Blender: mittlere Taste dreht, Umschalt verschiebt .nav-style-catia = CATIA: mittlere + linke Taste dreht + .dynamic-ucs-on = Dynamisches BKS: ein + .dynamic-ucs-off = Dynamisches BKS: aus .touchpad-scroll-pans-ctrl-scroll-or-pinch-zooms = Touchpad: Scrollen verschiebt, Strg+Scrollen oder Zusammenziehen zoomt .interface-scale = Oberflächenskalierung .zoom-factor = Zoomfaktor diff --git a/locales/el-GR/opencadstudio.ftl b/locales/el-GR/opencadstudio.ftl index 56e99e5c4..60a8c6969 100644 --- a/locales/el-GR/opencadstudio.ftl +++ b/locales/el-GR/opencadstudio.ftl @@ -1556,6 +1556,8 @@ common = .nav-style-cad = CAD: Shift + μεσαίο πλήκτρο περιστρέφει .nav-style-blender = Blender: μεσαίο πλήκτρο περιστρέφει, Shift μετακινεί .nav-style-catia = CATIA: μεσαίο + αριστερό πλήκτρο περιστρέφει + .dynamic-ucs-on = Δυναμικό UCS: ενεργό + .dynamic-ucs-off = Δυναμικό UCS: ανενεργό .touchpad-scroll-pans-ctrl-scroll-or-pinch-zooms = Επιφάνεια αφής: η κύλιση μετακινεί, Ctrl+κύλιση ή τσίμπημα κάνει ζουμ .interface-scale = Κλίμακα διεπαφής .zoom-factor = Συντελεστής ζουμ diff --git a/locales/en-US/opencadstudio.ftl b/locales/en-US/opencadstudio.ftl index 38da05fc8..a2ffad1c1 100644 --- a/locales/en-US/opencadstudio.ftl +++ b/locales/en-US/opencadstudio.ftl @@ -1555,6 +1555,8 @@ common = .nav-style-cad = CAD: Shift + middle button orbits .nav-style-blender = Blender: middle button orbits, Shift pans .nav-style-catia = CATIA: middle + left button orbits + .dynamic-ucs-on = Dynamic UCS: on + .dynamic-ucs-off = Dynamic UCS: off .touchpad-scroll-pans-ctrl-scroll-or-pinch-zooms = Touchpad: scroll pans, Ctrl+scroll or pinch zooms .interface-scale = Interface scale .zoom-factor = Zoom factor diff --git a/locales/es-ES/opencadstudio.ftl b/locales/es-ES/opencadstudio.ftl index cd1336f55..919bc64cb 100644 --- a/locales/es-ES/opencadstudio.ftl +++ b/locales/es-ES/opencadstudio.ftl @@ -1555,6 +1555,8 @@ common = .nav-style-cad = CAD: Mayús + botón central orbita .nav-style-blender = Blender: botón central orbita, Mayús desplaza .nav-style-catia = CATIA: botón central + izquierdo orbita + .dynamic-ucs-on = SCP dinámico: activado + .dynamic-ucs-off = SCP dinámico: desactivado .touchpad-scroll-pans-ctrl-scroll-or-pinch-zooms = Panel táctil: el desplazamiento encuadra, Ctrl+desplazamiento o pellizco hace zoom .interface-scale = Escala de la interfaz .zoom-factor = Factor de zoom diff --git a/locales/fi-FI/opencadstudio.ftl b/locales/fi-FI/opencadstudio.ftl index 4be36fa15..fb3e1d5d1 100644 --- a/locales/fi-FI/opencadstudio.ftl +++ b/locales/fi-FI/opencadstudio.ftl @@ -1554,6 +1554,8 @@ common = .nav-style-cad = CAD: Vaihto + keskipainike kiertää .nav-style-blender = Blender: keskipainike kiertää, Vaihto panoroi .nav-style-catia = CATIA: keski- + vasen painike kiertää + .dynamic-ucs-on = Dynaaminen UCS: päällä + .dynamic-ucs-off = Dynaaminen UCS: pois .touchpad-scroll-pans-ctrl-scroll-or-pinch-zooms = Kosketuslevy: vieritys panoroi, Ctrl+vieritys tai nipistys zoomaa .interface-scale = Käyttöliittymän skaalaus .zoom-factor = Zoomauskerroin diff --git a/locales/fr-FR/opencadstudio.ftl b/locales/fr-FR/opencadstudio.ftl index ff19da37c..e9dcfdcde 100644 --- a/locales/fr-FR/opencadstudio.ftl +++ b/locales/fr-FR/opencadstudio.ftl @@ -1555,6 +1555,8 @@ common = .nav-style-cad = CAO : Maj + bouton du milieu fait pivoter .nav-style-blender = Blender : bouton du milieu fait pivoter, Maj déplace .nav-style-catia = CATIA : boutons du milieu + gauche font pivoter + .dynamic-ucs-on = SCU dynamique: activé + .dynamic-ucs-off = SCU dynamique: désactivé .touchpad-scroll-pans-ctrl-scroll-or-pinch-zooms = Pavé tactile : le défilement déplace, Ctrl+défilement ou pincement zoome .interface-scale = Échelle de l'interface .zoom-factor = Facteur de zoom diff --git a/locales/hi-IN/opencadstudio.ftl b/locales/hi-IN/opencadstudio.ftl index 94f157985..45dfa6060 100644 --- a/locales/hi-IN/opencadstudio.ftl +++ b/locales/hi-IN/opencadstudio.ftl @@ -1543,6 +1543,8 @@ common = .nav-style-cad = CAD: Shift + मध्य बटन घुमाता है .nav-style-blender = Blender: मध्य बटन घुमाता है, Shift खिसकाता है .nav-style-catia = CATIA: मध्य + बायाँ बटन घुमाता है + .dynamic-ucs-on = डायनामिक UCS: चालू + .dynamic-ucs-off = डायनामिक UCS: बंद .touchpad-scroll-pans-ctrl-scroll-or-pinch-zooms = टचपैड: स्क्रॉल पैन करता है, Ctrl+स्क्रॉल या पिंच ज़ूम करता है .interface-scale = इंटरफ़ेस स्केल .zoom-factor = ज़ूम गुणक diff --git a/locales/hu-HU/opencadstudio.ftl b/locales/hu-HU/opencadstudio.ftl index 76248da27..e61af14b8 100644 --- a/locales/hu-HU/opencadstudio.ftl +++ b/locales/hu-HU/opencadstudio.ftl @@ -1560,6 +1560,8 @@ common = .nav-style-cad = CAD: Shift + középső gomb forgat .nav-style-blender = Blender: középső gomb forgat, Shift mozgat .nav-style-catia = CATIA: középső + bal gomb forgat + .dynamic-ucs-on = Dinamikus FKR: be + .dynamic-ucs-off = Dinamikus FKR: ki .touchpad-scroll-pans-ctrl-scroll-or-pinch-zooms = Érintőpad: a görgetés mozgat, a Ctrl+görgetés vagy csippentés nagyít .interface-scale = Felület mérete .zoom-factor = Nagyítási tényező diff --git a/locales/it-IT/opencadstudio.ftl b/locales/it-IT/opencadstudio.ftl index 6c19f8bdb..d20780e4b 100644 --- a/locales/it-IT/opencadstudio.ftl +++ b/locales/it-IT/opencadstudio.ftl @@ -1554,6 +1554,8 @@ common = .nav-style-cad = CAD: Maiusc + tasto centrale ruota .nav-style-blender = Blender: tasto centrale ruota, Maiusc sposta .nav-style-catia = CATIA: tasto centrale + sinistro ruota + .dynamic-ucs-on = UCS dinamico: attivo + .dynamic-ucs-off = UCS dinamico: disattivo .touchpad-scroll-pans-ctrl-scroll-or-pinch-zooms = Touchpad: lo scorrimento sposta la vista, Ctrl+scorrimento o pizzico esegue lo zoom .interface-scale = Scala dell'interfaccia .zoom-factor = Fattore di zoom diff --git a/locales/ja-JP/opencadstudio.ftl b/locales/ja-JP/opencadstudio.ftl index f2a8dbf96..da6ef89fe 100644 --- a/locales/ja-JP/opencadstudio.ftl +++ b/locales/ja-JP/opencadstudio.ftl @@ -1545,6 +1545,8 @@ common = .nav-style-cad = CAD: Shift + 中ボタンで回転 .nav-style-blender = Blender: 中ボタンで回転、Shift で画面移動 .nav-style-catia = CATIA: 中 + 左ボタンで回転 + .dynamic-ucs-on = ダイナミック UCS: オン + .dynamic-ucs-off = ダイナミック UCS: オフ .touchpad-scroll-pans-ctrl-scroll-or-pinch-zooms = タッチパッド: スクロールで画面移動、Ctrl+スクロールまたはピンチでズーム .interface-scale = インターフェースの拡大率 .zoom-factor = ズーム倍率 diff --git a/locales/ko-KR/opencadstudio.ftl b/locales/ko-KR/opencadstudio.ftl index ddc8d622a..e8fec7108 100644 --- a/locales/ko-KR/opencadstudio.ftl +++ b/locales/ko-KR/opencadstudio.ftl @@ -1554,6 +1554,8 @@ common = .nav-style-cad = CAD: Shift + 가운데 버튼으로 회전 .nav-style-blender = Blender: 가운데 버튼으로 회전, Shift로 이동 .nav-style-catia = CATIA: 가운데 + 왼쪽 버튼으로 회전 + .dynamic-ucs-on = 동적 UCS: 켜짐 + .dynamic-ucs-off = 동적 UCS: 꺼짐 .touchpad-scroll-pans-ctrl-scroll-or-pinch-zooms = 터치패드: 스크롤은 화면 이동, Ctrl+스크롤 또는 핀치는 확대/축소 .interface-scale = 인터페이스 배율 .zoom-factor = 줌 배율 diff --git a/locales/lo-LA/opencadstudio.ftl b/locales/lo-LA/opencadstudio.ftl index a1af519e0..1fe49c396 100644 --- a/locales/lo-LA/opencadstudio.ftl +++ b/locales/lo-LA/opencadstudio.ftl @@ -1560,6 +1560,8 @@ common = .nav-style-cad = CAD: Shift + ປຸ່ມກາງ ໝຸນ .nav-style-blender = Blender: ປຸ່ມກາງ ໝຸນ, Shift ເລື່ອນ .nav-style-catia = CATIA: ປຸ່ມກາງ + ຊ້າຍ ໝຸນ + .dynamic-ucs-on = UCS ແບບໄດນາມິກ: ເປີດ + .dynamic-ucs-off = UCS ແບບໄດນາມິກ: ປິດ .touchpad-scroll-pans-ctrl-scroll-or-pinch-zooms = ທັດແພດ: ເລື່ອນເພື່ອຍ້າຍມຸມມອງ, Ctrl+ເລື່ອນ ຫຼື ບີບເພື່ອຊູມ .interface-scale = ຂະໜາດສ່ວນຕິດຕໍ່ .zoom-factor = ຕົວຄູນການຊູມ diff --git a/locales/nl-NL/opencadstudio.ftl b/locales/nl-NL/opencadstudio.ftl index 7aa044e93..6e34190f4 100644 --- a/locales/nl-NL/opencadstudio.ftl +++ b/locales/nl-NL/opencadstudio.ftl @@ -1553,6 +1553,8 @@ common = .nav-style-cad = CAD: Shift + middelste knop draait .nav-style-blender = Blender: middelste knop draait, Shift verschuift .nav-style-catia = CATIA: middelste + linkerknop draait + .dynamic-ucs-on = Dynamisch GCS: aan + .dynamic-ucs-off = Dynamisch GCS: uit .touchpad-scroll-pans-ctrl-scroll-or-pinch-zooms = Touchpad: scrollen verschuift, Ctrl+scrollen of knijpen zoomt .interface-scale = Interfaceschaal .zoom-factor = Zoomfactor diff --git a/locales/pl-PL/opencadstudio.ftl b/locales/pl-PL/opencadstudio.ftl index 23ca1cfbd..5afc7c700 100644 --- a/locales/pl-PL/opencadstudio.ftl +++ b/locales/pl-PL/opencadstudio.ftl @@ -1554,6 +1554,8 @@ common = .nav-style-cad = CAD: Shift + środkowy przycisk obraca .nav-style-blender = Blender: środkowy przycisk obraca, Shift przesuwa .nav-style-catia = CATIA: środkowy + lewy przycisk obraca + .dynamic-ucs-on = Dynamiczny LUW: wł. + .dynamic-ucs-off = Dynamiczny LUW: wył. .touchpad-scroll-pans-ctrl-scroll-or-pinch-zooms = Touchpad: przewijanie przesuwa widok, Ctrl+przewijanie lub szczypanie powiększa .interface-scale = Skala interfejsu .zoom-factor = Współczynnik powiększenia diff --git a/locales/pt-BR/opencadstudio.ftl b/locales/pt-BR/opencadstudio.ftl index 00dc78403..ecbf12fe2 100644 --- a/locales/pt-BR/opencadstudio.ftl +++ b/locales/pt-BR/opencadstudio.ftl @@ -1555,6 +1555,8 @@ common = .nav-style-cad = CAD: Shift + botão do meio orbita .nav-style-blender = Blender: botão do meio orbita, Shift desloca .nav-style-catia = CATIA: botão do meio + esquerdo orbita + .dynamic-ucs-on = SCU dinâmico: ativado + .dynamic-ucs-off = SCU dinâmico: desativado .touchpad-scroll-pans-ctrl-scroll-or-pinch-zooms = Touchpad: rolar move a vista, Ctrl+rolar ou pinçar aplica zoom .interface-scale = Escala da interface .zoom-factor = Fator de zoom diff --git a/locales/ru-RU/opencadstudio.ftl b/locales/ru-RU/opencadstudio.ftl index 771dfbcb9..0c48d9ed1 100644 --- a/locales/ru-RU/opencadstudio.ftl +++ b/locales/ru-RU/opencadstudio.ftl @@ -1558,6 +1558,8 @@ common = .nav-style-cad = CAD: Shift + средняя кнопка — вращение .nav-style-blender = Blender: средняя кнопка — вращение, Shift — сдвиг .nav-style-catia = CATIA: средняя + левая кнопка — вращение + .dynamic-ucs-on = Динамическая ПСК: вкл. + .dynamic-ucs-off = Динамическая ПСК: выкл. .touchpad-scroll-pans-ctrl-scroll-or-pinch-zooms = Тачпад: прокрутка панорамирует, Ctrl+прокрутка или щипок масштабирует .interface-scale = Масштаб интерфейса .zoom-factor = Коэффициент зумирования diff --git a/locales/tr-TR/opencadstudio.ftl b/locales/tr-TR/opencadstudio.ftl index 95ab5cff8..62ce2a837 100644 --- a/locales/tr-TR/opencadstudio.ftl +++ b/locales/tr-TR/opencadstudio.ftl @@ -1541,6 +1541,8 @@ common = .nav-style-cad = CAD: Shift + orta tuş döndürür .nav-style-blender = Blender: orta tuş döndürür, Shift kaydırır .nav-style-catia = CATIA: orta + sol tuş döndürür + .dynamic-ucs-on = Dinamik UCS: açık + .dynamic-ucs-off = Dinamik UCS: kapalı .touchpad-scroll-pans-ctrl-scroll-or-pinch-zooms = Dokunmatik yüzey: kaydırma kaydırır, Ctrl+kaydırma veya sıkıştırma yakınlaştırır .interface-scale = Arayüz ölçeği .zoom-factor = Yakınlaştırma çarpanı diff --git a/locales/zh-CN/opencadstudio.ftl b/locales/zh-CN/opencadstudio.ftl index 0f3e81c96..0554f148a 100644 --- a/locales/zh-CN/opencadstudio.ftl +++ b/locales/zh-CN/opencadstudio.ftl @@ -1542,6 +1542,8 @@ common = .nav-style-cad = CAD:Shift + 中键旋转 .nav-style-blender = Blender:中键旋转,Shift 平移 .nav-style-catia = CATIA:中键 + 左键旋转 + .dynamic-ucs-on = 动态 UCS:开 + .dynamic-ucs-off = 动态 UCS:关 .touchpad-scroll-pans-ctrl-scroll-or-pinch-zooms = 触控板:滚动平移,Ctrl+滚动或捏合缩放 .interface-scale = 界面缩放 .zoom-factor = 缩放系数 diff --git a/locales/zh-TW/opencadstudio.ftl b/locales/zh-TW/opencadstudio.ftl index 14a67c52d..d8510b264 100644 --- a/locales/zh-TW/opencadstudio.ftl +++ b/locales/zh-TW/opencadstudio.ftl @@ -1554,6 +1554,8 @@ common = .nav-style-cad = CAD:Shift + 中鍵旋轉 .nav-style-blender = Blender:中鍵旋轉,Shift 平移 .nav-style-catia = CATIA:中鍵 + 左鍵旋轉 + .dynamic-ucs-on = 動態 UCS:開 + .dynamic-ucs-off = 動態 UCS:關 .touchpad-scroll-pans-ctrl-scroll-or-pinch-zooms = 觸控板:捲動平移,Ctrl+捲動或捏合縮放 .interface-scale = 介面縮放 .zoom-factor = 縮放係數 diff --git a/src/app/command_driver/utilities.rs b/src/app/command_driver/utilities.rs index 5be26f15d..a850e3283 100644 --- a/src/app/command_driver/utilities.rs +++ b/src/app/command_driver/utilities.rs @@ -624,6 +624,48 @@ impl OpenCADStudio { Some(key) } + /// Dynamic UCS: a command's first point picked on a planar solid face sets + /// the UCS to that face for the rest of the command. Returns the picked + /// point on the face. A snapped point (`snapped`) off the face's plane + /// belongs to other geometry, so it leaves the UCS alone. + pub(in crate::app) fn apply_dynamic_ucs( + &mut self, + snapped: Option, + ) -> Option { + let i = self.active_tab; + if !self.dynamic_ucs + || self.dynamic_ucs_restore.is_some() + || self.last_point.is_some() + || !self.tabs[i].editing_model_space() + || self.tabs[i] + .active_cmd + .as_ref() + .is_none_or(|command| command.name().starts_with("UCS")) + { + return None; + } + let cursor = self.tabs[i].scene.selection.borrow().input.last_move_pos?; + let (vw, vh) = self.tabs[i].scene.selection.borrow().view.vp_size; + let bounds = self.tabs[i].scene.active_model_tile_bounds(vw, vh); + let local = iced::Point::new(cursor.x - bounds.x, cursor.y - bounds.y); + let (ucs, pick) = self.tabs[i].scene.planar_face_ucs_at(bounds, local)?; + if let Some(point) = snapped { + let normal = glam::DVec3::new(ucs.x_axis.x, ucs.x_axis.y, ucs.x_axis.z) + .cross(glam::DVec3::new(ucs.y_axis.x, ucs.y_axis.y, ucs.y_axis.z)) + .normalize_or_zero(); + let off_plane = (point - pick).dot(normal).abs(); + if off_plane > 1.0e-6 * (1.0 + point.length().max(pick.length())) { + return None; + } + } + let saved = self.tabs[i].active_ucs.take(); + self.dynamic_ucs_restore = Some((self.tabs[i].id, saved)); + self.tabs[i].active_ucs = Some(ucs); + self.tabs[i].sync_ucs_to_scene(); + self.push_ucs_to_cmd(i); + Some(pick) + } + /// `feed_command`, also reporting whether the step actually took the input. /// Only a `Text` token can come back unclaimed (`on_text_input` returning /// `None`), which is what lets the caller read it as something else — a @@ -632,6 +674,9 @@ impl OpenCADStudio { &mut self, input: StepInput, ) -> (Task, bool) { + if matches!(input, StepInput::Point(_)) { + let _ = self.apply_dynamic_ucs(None); + } let gathering = self.report_selection_found(&input); // A command that takes associative dimensions only keeps just those: // the rest leave the selection and the count of what went is reported, diff --git a/src/app/commands/mod.rs b/src/app/commands/mod.rs index 1b98f6377..d2739f9e3 100644 --- a/src/app/commands/mod.rs +++ b/src/app/commands/mod.rs @@ -768,6 +768,7 @@ inventory::submit!(crate::command::CommandRegistration { "FILEDIA", "TRIMEXTENDMODE", "SELECTIONCYCLING", + "UCSDETECT", "SURFACEASSOCIATIVITY", "LAYOUTCREATEVIEWPORT", "FIELDEVAL", diff --git a/src/app/commands/styleprops.rs b/src/app/commands/styleprops.rs index 42d3d8d9c..f99c20694 100644 --- a/src/app/commands/styleprops.rs +++ b/src/app/commands/styleprops.rs @@ -1216,6 +1216,7 @@ impl OpenCADStudio { | "FILEDIA" | "TRIMEXTENDMODE" | "SELECTIONCYCLING" + | "UCSDETECT" | "SURFACEASSOCIATIVITY" | "UCSORTHO" | "UCSVIEW" @@ -1656,6 +1657,30 @@ impl OpenCADStudio { } return Some(self.finish_dispatch(cmd)); } + if name == "UCSDETECT" { + let current = i16::from(self.dynamic_ucs); + let prompt = format!("Enter new value for UCSDETECT <{current}>:"); + match value + .as_deref() + .map(|v| v.trim().parse::().ok().filter(|v| (0..=1).contains(v))) + { + Some(Some(mode)) => { + self.dynamic_ucs = mode == 1; + self.persist_settings_if_changed(); + } + Some(None) => { + self.command_line + .push_error(crate::t!("Requires 0 or 1 only.").as_ref()); + self.command_line.push_output(&prompt); + self.pending_setvar = Some(name.clone()); + } + None => { + self.command_line.push_output(&prompt); + self.pending_setvar = Some(name.clone()); + } + } + return Some(self.finish_dispatch(cmd)); + } if name == "SELECTIONCYCLING" { // 1 and 2 both turn cycling on; the candidate list // always shows. diff --git a/src/app/mod.rs b/src/app/mod.rs index 8837df446..4558ca2f6 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -655,6 +655,12 @@ pub(super) struct OpenCADStudio { /// CATIA navigation: the left button went down while the middle one was /// held, so the middle drag orbits until it is released. nav_left_with_middle: bool, + /// UCSDETECT: a command's first point on a planar solid face sets the + /// UCS to that face until the command ends. + dynamic_ucs: bool, + /// The UCS a dynamic UCS replaced, by tab id, restored when that tab's + /// command ends. + dynamic_ucs_restore: Option<(u64, Option)>, /// Scroll pans and Ctrl+scroll zooms (touchpads); see `scroll_intent`. touchpad_pan: bool, /// Multi-finger gestures on a touch screen. @@ -2663,6 +2669,8 @@ pub enum Message { /// Toggle reversing the mouse-wheel zoom direction (ZOOMWHEEL). ZoomWheelReversedChanged(bool), NavigationStyleChanged(crate::app::settings::NavigationStyle), + /// F6 / UCSDETECT: toggle the dynamic UCS. + ToggleDynamicUcs, TouchpadPanChanged(bool), /// Options: show the pull-down menus (MENUBAR). MenuBarChanged(bool), @@ -4640,6 +4648,8 @@ impl OpenCADStudio { zoom_wheel_reversed: false, navigation_style: Default::default(), nav_left_with_middle: false, + dynamic_ucs: true, + dynamic_ucs_restore: None, touchpad_pan: false, touch: Default::default(), touch_offset: None, diff --git a/src/app/settings.rs b/src/app/settings.rs index a5b625c18..2ffde4850 100644 --- a/src/app/settings.rs +++ b/src/app/settings.rs @@ -416,6 +416,9 @@ pub struct UserSettings { pub polar_increment_deg: f32, pub zoom_wheel_reversed: bool, pub navigation_style: NavigationStyle, + /// UCSDETECT: a command's first point on a planar solid face sets the + /// UCS to that face until the command ends. + pub dynamic_ucs: bool, /// Scrolling pans and Ctrl+scroll (a touchpad pinch) zooms, for a /// touchpad that reports two-finger drags as wheel scrolls. pub touchpad_pan: bool, @@ -920,6 +923,7 @@ impl Default for UserSettings { polar_increment_deg: 45.0, zoom_wheel_reversed: false, navigation_style: NavigationStyle::Cad, + dynamic_ucs: true, touchpad_pan: false, ui_scale_percent: 100, zoom_factor: 60, diff --git a/src/app/shortcuts.rs b/src/app/shortcuts.rs index a43b0286d..f67120ede 100644 --- a/src/app/shortcuts.rs +++ b/src/app/shortcuts.rs @@ -41,6 +41,7 @@ pub(super) fn default_bindings() -> BTreeMap { ("F3".to_string(), "TOGGLEOSNAP"), ("F4".to_string(), "TOGGLE3DOSNAP"), ("F5".to_string(), "ISOPLANE"), + ("F6".to_string(), "TOGGLEDUCS"), ("F7".to_string(), "GRID"), ("F8".to_string(), "ORTHO"), ("F9".to_string(), "SNAP"), @@ -185,6 +186,7 @@ pub(super) const INPUT_ACTIONS: &[&str] = &[ "COMMANDHISTORY", "TOGGLEOSNAP", "TOGGLE3DOSNAP", + "TOGGLEDUCS", "OTRACK", "DYNINPUT", "SELECTALL", @@ -343,6 +345,7 @@ impl OpenCADStudio { "COMMANDHISTORY" => Message::CommandHistoryToggle, "TOGGLEOSNAP" => Message::ToggleSnapEnabled, "TOGGLE3DOSNAP" => Message::ToggleSnap3dEnabled, + "TOGGLEDUCS" => Message::ToggleDynamicUcs, "OTRACK" => Message::ToggleOTrack, "DYNINPUT" => Message::ToggleDynInput, "SELECTALL" => Message::SelectAllShortcut, diff --git a/src/app/update/file.rs b/src/app/update/file.rs index 63171f8a3..f14bbd8a6 100644 --- a/src/app/update/file.rs +++ b/src/app/update/file.rs @@ -779,6 +779,7 @@ impl OpenCADStudio { polar_increment_deg: self.polar_increment_deg, zoom_wheel_reversed: self.zoom_wheel_reversed, navigation_style: self.navigation_style, + dynamic_ucs: self.dynamic_ucs, touchpad_pan: self.touchpad_pan, ui_scale_percent: self.ui_scale_percent, zoom_factor: self.zoom_factor, @@ -915,6 +916,7 @@ impl OpenCADStudio { self.polar_increment_deg = s.polar_increment_deg; self.zoom_wheel_reversed = s.zoom_wheel_reversed; self.navigation_style = s.navigation_style; + self.dynamic_ucs = s.dynamic_ucs; self.touchpad_pan = s.touchpad_pan; self.ui_scale_percent = crate::app::settings::clamp_ui_scale(s.ui_scale_percent); self.zoom_factor = crate::app::settings::clamp_zoom_factor(s.zoom_factor); diff --git a/src/app/update/mod.rs b/src/app/update/mod.rs index 8c304ff27..263bba5e6 100644 --- a/src/app/update/mod.rs +++ b/src/app/update/mod.rs @@ -374,6 +374,7 @@ impl OpenCADStudio { // document's selection. let before = self.tabs.get(self.active_tab).map(|tab| tab.id); let task = self.update_message(msg); + self.restore_dynamic_ucs_after_command(); if self.tabs.get(self.active_tab).map(|tab| tab.id) != before { self.sync_underlay_tab(); } @@ -396,6 +397,25 @@ impl OpenCADStudio { task } + /// A dynamic UCS lasts for the command that set it: once that tab's + /// command has ended, the UCS it replaced comes back. + fn restore_dynamic_ucs_after_command(&mut self) { + let Some((tab_id, _)) = self.dynamic_ucs_restore else { + return; + }; + let Some(i) = self.tabs.iter().position(|tab| tab.id == tab_id) else { + self.dynamic_ucs_restore = None; + return; + }; + if self.tabs[i].active_cmd.is_some() { + return; + } + if let Some((_, saved)) = self.dynamic_ucs_restore.take() { + self.tabs[i].active_ucs = saved; + self.tabs[i].sync_ucs_to_scene(); + } + } + /// The Frames list on the ribbon shows the active drawing's frame state /// (FRAME), however it was changed: the list, a command, undo or another /// drawing. @@ -9245,6 +9265,18 @@ impl OpenCADStudio { Task::none() } + Message::ToggleDynamicUcs => { + self.dynamic_ucs = !self.dynamic_ucs; + self.command_line.push_output(if self.dynamic_ucs { + crate::t!("Dynamic UCS: on") + } else { + crate::t!("Dynamic UCS: off") + } + .as_ref()); + self.persist_settings_if_changed(); + Task::none() + } + Message::NavigationStyleChanged(style) => { self.navigation_style = style; self.persist_settings_if_changed(); diff --git a/src/app/update/viewport.rs b/src/app/update/viewport.rs index 107bdb7c4..e343f07a3 100644 --- a/src/app/update/viewport.rs +++ b/src/app/update/viewport.rs @@ -4325,6 +4325,7 @@ impl OpenCADStudio { }; let snap_taken = self.tabs[i].snap_result.take(); + let snapped = snap_taken.is_some(); let tangent_obj_at_click = snap_taken.and_then(|s| s.tangent_obj); let world_pt = { @@ -5019,6 +5020,11 @@ impl OpenCADStudio { self.finish_command_click(i); return Task::none(); } + // An unsnapped first point on a solid face lands on the face. + let world_pt = match self.apply_dynamic_ucs(snapped.then_some(world_pt)) { + Some(on_face) if !snapped => on_face, + _ => world_pt, + }; self.last_point = Some(world_pt); // The one-shot snap override is spent by this pick — // restore the running osnap configuration (#337). diff --git a/src/locale_catalog.rs b/src/locale_catalog.rs index b7d956730..00ff2ac57 100644 --- a/src/locale_catalog.rs +++ b/src/locale_catalog.rs @@ -5074,6 +5074,8 @@ pub(super) fn message_attribute(source: &str) -> Option<(&'static str, &'static "CAD: Shift + middle button orbits" => Some(("common", "nav-style-cad")), "Blender: middle button orbits, Shift pans" => Some(("common", "nav-style-blender")), "CATIA: middle + left button orbits" => Some(("common", "nav-style-catia")), + "Dynamic UCS: on" => Some(("common", "dynamic-ucs-on")), + "Dynamic UCS: off" => Some(("common", "dynamic-ucs-off")), "Touchpad: scroll pans, Ctrl+scroll or pinch zooms" => Some(("common", "touchpad-scroll-pans-ctrl-scroll-or-pinch-zooms")), "Interface scale" => Some(("common", "interface-scale")), "Zoom factor" => Some(("common", "zoom-factor")), diff --git a/src/scene/mod.rs b/src/scene/mod.rs index 074b35015..380663425 100644 --- a/src/scene/mod.rs +++ b/src/scene/mod.rs @@ -11363,6 +11363,25 @@ impl Scene { ) } + /// The UCS of the planar solid face under `local` (relative to `bounds`), + /// Z along the face normal, and the picked point on the face. + pub fn planar_face_ucs_at( + &mut self, + bounds: iced::Rectangle, + local: iced::Point, + ) -> Option<(codec::tables::Ucs, glam::DVec3)> { + let camera = self.camera.borrow(); + let view_rot = camera.view_proj_rte(bounds); + let eye = camera.eye(); + drop(camera); + let (handle, pick) = self.solid_click_hit_point(local, view_rot, eye, bounds)?; + self.restore_solid_models(&[handle]); + let body = self.solid_models.get(&handle)?; + let face = crate::scene::model::solid_model::nearest_planar_face(body, pick.to_array())?; + crate::scene::model::solid_model::planar_face_ucs(body, face, pick.to_array()) + .map(|ucs| (ucs, pick)) + } + /// The nearest solid or mesh surface under `local` (relative to `bounds`). pub fn surface_pivot_at( &self,