From f485df763606aa054dca7f1ba62ac016ef082aea Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Wed, 7 Oct 2026 08:49:55 +0300 Subject: [PATCH] feat(3drotate): rotate about an axis picked in space MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 3DROTATE (ROTATE3D) took the drawing's rotation path, so a polyline only turned in its own plane. It now asks for a base point, an axis — X, Y, Z or a second point — and an angle, and rotates the selection about that axis in space. Fixes #1641 Co-Authored-By: Claude Opus 5.5 --- locales/ar-SA/opencadstudio.ftl | 3 ++ locales/bg-BG/opencadstudio.ftl | 3 ++ locales/cs-CZ/opencadstudio.ftl | 3 ++ locales/de-DE/opencadstudio.ftl | 3 ++ locales/el-GR/opencadstudio.ftl | 3 ++ locales/en-US/opencadstudio.ftl | 3 ++ locales/es-ES/opencadstudio.ftl | 3 ++ locales/fi-FI/opencadstudio.ftl | 3 ++ locales/fr-FR/opencadstudio.ftl | 3 ++ locales/hi-IN/opencadstudio.ftl | 3 ++ locales/hu-HU/opencadstudio.ftl | 3 ++ locales/it-IT/opencadstudio.ftl | 3 ++ locales/ja-JP/opencadstudio.ftl | 3 ++ locales/ko-KR/opencadstudio.ftl | 3 ++ locales/nl-NL/opencadstudio.ftl | 3 ++ locales/pl-PL/opencadstudio.ftl | 3 ++ locales/pt-BR/opencadstudio.ftl | 3 ++ locales/ru-RU/opencadstudio.ftl | 3 ++ locales/tr-TR/opencadstudio.ftl | 3 ++ locales/zh-CN/opencadstudio.ftl | 3 ++ locales/zh-TW/opencadstudio.ftl | 3 ++ src/app/commands/draw.rs | 33 +++++++----- src/locale_catalog.rs | 3 ++ src/modules/draw/modify/rotate.rs | 87 ++++++++++++++++++++++++++++++- 24 files changed, 170 insertions(+), 16 deletions(-) diff --git a/locales/ar-SA/opencadstudio.ftl b/locales/ar-SA/opencadstudio.ftl index 9fe9d9e73..16590511c 100644 --- a/locales/ar-SA/opencadstudio.ftl +++ b/locales/ar-SA/opencadstudio.ftl @@ -2349,6 +2349,9 @@ common = .chspace-to-paper = CHSPACE: تم نقل __ocs_fmt_0__ كائن إلى فضاء الورق. .chspace-to-model = CHSPACE: تم نقل __ocs_fmt_0__ كائن إلى فضاء النموذج. .select-template-title = تحديد قالب + .threedrotate-specify-base-point-count-objects = 3DROTATE حدد نقطة الأساس [__ocs_arg_count__ كائنات]: + .threedrotate-specify-rotation-axis = 3DROTATE حدد محور الدوران [X/Y/Z] أو نقطة ثانية عليه : + .threedrotate-specify-rotation-angle-a = 3DROTATE حدد زاوية الدوران <__ocs_arg_a__>: errors = .save-warning = تحذير الحفظ .unable-to-save-drawing = تعذر حفظ الرسم diff --git a/locales/bg-BG/opencadstudio.ftl b/locales/bg-BG/opencadstudio.ftl index 88d257df5..fb12e4dbc 100644 --- a/locales/bg-BG/opencadstudio.ftl +++ b/locales/bg-BG/opencadstudio.ftl @@ -2336,6 +2336,9 @@ common = .chspace-to-paper = CHSPACE: __ocs_fmt_0__ обекта преместени в пространството на листа. .chspace-to-model = CHSPACE: __ocs_fmt_0__ обекта преместени в пространството на модела. .select-template-title = Избор на шаблон + .threedrotate-specify-base-point-count-objects = 3DROTATE Посочи базова точка [__ocs_arg_count__ обекта]: + .threedrotate-specify-rotation-axis = 3DROTATE Посочете ос на завъртане [X/Y/Z] или втора точка от нея : + .threedrotate-specify-rotation-angle-a = 3DROTATE Посочи ъгъл на завъртане <__ocs_arg_a__>: errors = .save-warning = Предупреждение при запазване .unable-to-save-drawing = Чертежът не може да бъде запазен diff --git a/locales/cs-CZ/opencadstudio.ftl b/locales/cs-CZ/opencadstudio.ftl index a0b004045..808612974 100644 --- a/locales/cs-CZ/opencadstudio.ftl +++ b/locales/cs-CZ/opencadstudio.ftl @@ -2336,6 +2336,9 @@ common = .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 + .threedrotate-specify-base-point-count-objects = 3DROTATE Zadejte základní bod [objekty __ocs_arg_count__]: + .threedrotate-specify-rotation-axis = 3DROTATE Zadejte osu otáčení [X/Y/Z] nebo druhý bod na ní : + .threedrotate-specify-rotation-angle-a = 3DROTATE Zadejte úhel natočení <__ocs_arg_a__>: errors = .save-warning = Uložit varování .unable-to-save-drawing = Nákres nelze uložit diff --git a/locales/de-DE/opencadstudio.ftl b/locales/de-DE/opencadstudio.ftl index b4303789d..c8abcfdfe 100644 --- a/locales/de-DE/opencadstudio.ftl +++ b/locales/de-DE/opencadstudio.ftl @@ -2337,6 +2337,9 @@ common = .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 + .threedrotate-specify-base-point-count-objects = 3DROTATE Basispunkt angeben [__ocs_arg_count__ Objekte]: + .threedrotate-specify-rotation-axis = 3DROTATE Drehachse angeben [X/Y/Z] oder einen zweiten Punkt darauf : + .threedrotate-specify-rotation-angle-a = 3DROTATE Drehwinkel angeben <__ocs_arg_a__>: errors = .save-warning = Warnhinweise speichern .unable-to-save-drawing = Zeichnung konnte nicht gespeichert werden diff --git a/locales/el-GR/opencadstudio.ftl b/locales/el-GR/opencadstudio.ftl index 20506457b..5ebbc4005 100644 --- a/locales/el-GR/opencadstudio.ftl +++ b/locales/el-GR/opencadstudio.ftl @@ -2336,6 +2336,9 @@ common = .chspace-to-paper = CHSPACE: __ocs_fmt_0__ αντικείμενα μεταφέρθηκαν στον χώρο χαρτιού. .chspace-to-model = CHSPACE: __ocs_fmt_0__ αντικείμενα μεταφέρθηκαν στον χώρο μοντέλου. .select-template-title = Επιλογή προτύπου + .threedrotate-specify-base-point-count-objects = 3DROTATE Ορίστε σημείο βάσης [__ocs_arg_count__ αντικείμενα]: + .threedrotate-specify-rotation-axis = 3DROTATE Ορίστε άξονα περιστροφής [X/Y/Z] ή δεύτερο σημείο του : + .threedrotate-specify-rotation-angle-a = 3DROTATE Ορίστε γωνία περιστροφής <__ocs_arg_a__>: errors = .save-warning = Προειδοποίηση αποθήκευσης .unable-to-save-drawing = Αδυναμία αποθήκευσης σχεδίου diff --git a/locales/en-US/opencadstudio.ftl b/locales/en-US/opencadstudio.ftl index 56ee9179a..99a7b15c2 100644 --- a/locales/en-US/opencadstudio.ftl +++ b/locales/en-US/opencadstudio.ftl @@ -2337,6 +2337,9 @@ common = .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 + .threedrotate-specify-base-point-count-objects = 3DROTATE Specify base point [__ocs_arg_count__ objects]: + .threedrotate-specify-rotation-axis = 3DROTATE Specify rotation axis [X/Y/Z] or a second point on it : + .threedrotate-specify-rotation-angle-a = 3DROTATE Specify rotation angle <__ocs_arg_a__>: errors = .save-warning = Save Warning .unable-to-save-drawing = Unable to Save Drawing diff --git a/locales/es-ES/opencadstudio.ftl b/locales/es-ES/opencadstudio.ftl index 8695475e0..a53f4e3ae 100644 --- a/locales/es-ES/opencadstudio.ftl +++ b/locales/es-ES/opencadstudio.ftl @@ -2337,6 +2337,9 @@ common = .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 + .threedrotate-specify-base-point-count-objects = 3DROTATE Especifique el punto base [__ocs_arg_count__ objetos]: + .threedrotate-specify-rotation-axis = 3DROTATE Precise el eje de giro [X/Y/Z] o un segundo punto en él : + .threedrotate-specify-rotation-angle-a = 3DROTATE Precise el ángulo de giro <__ocs_arg_a__>: errors = .save-warning = Advertencia de guardado .unable-to-save-drawing = No se puede guardar el dibujo diff --git a/locales/fi-FI/opencadstudio.ftl b/locales/fi-FI/opencadstudio.ftl index 36e88e99f..be72d4cd1 100644 --- a/locales/fi-FI/opencadstudio.ftl +++ b/locales/fi-FI/opencadstudio.ftl @@ -2336,6 +2336,9 @@ common = .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 + .threedrotate-specify-base-point-count-objects = 3DROTATE Määritä peruspiste [__ocs_arg_count__ objektit]: + .threedrotate-specify-rotation-axis = 3DROTATE Määritä kiertoakseli [X/Y/Z] tai sen toinen piste : + .threedrotate-specify-rotation-angle-a = 3DROTATE Määritä kiertokulma <__ocs_arg_a__>: errors = .save-warning = Tallenna varoitus .unable-to-save-drawing = Piirustusta ei voi tallentaa diff --git a/locales/fr-FR/opencadstudio.ftl b/locales/fr-FR/opencadstudio.ftl index ebbad5798..d9753bbb0 100644 --- a/locales/fr-FR/opencadstudio.ftl +++ b/locales/fr-FR/opencadstudio.ftl @@ -2337,6 +2337,9 @@ common = .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 + .threedrotate-specify-base-point-count-objects = 3DROTATE Spécifier le point de base [__ocs_arg_count__ objets]: + .threedrotate-specify-rotation-axis = 3DROTATE Spécifiez l'axe de rotation [X/Y/Z] ou un second point sur celui-ci : + .threedrotate-specify-rotation-angle-a = 3DROTATE Spécifier l'angle de rotation <__ocs_arg_a__: errors = .save-warning = Enregistrer l' avertissement .unable-to-save-drawing = Impossible de sauvegarder le dessin diff --git a/locales/hi-IN/opencadstudio.ftl b/locales/hi-IN/opencadstudio.ftl index ef13bc3b3..04bbe36f2 100644 --- a/locales/hi-IN/opencadstudio.ftl +++ b/locales/hi-IN/opencadstudio.ftl @@ -2325,6 +2325,9 @@ common = .chspace-to-paper = CHSPACE: __ocs_fmt_0__ ऑब्जेक्ट पेपर स्पेस में बदले गए। .chspace-to-model = CHSPACE: __ocs_fmt_0__ ऑब्जेक्ट मॉडल स्पेस में बदले गए। .select-template-title = टेम्पलेट चुनें + .threedrotate-specify-base-point-count-objects = 3DROTATE आधार बिंदु निर्दिष्ट करें [__ocs_arg_count__ ऑब्जेक्ट]: + .threedrotate-specify-rotation-axis = 3DROTATE घूर्णन अक्ष [X/Y/Z] या उस पर दूसरा बिंदु निर्दिष्ट करें : + .threedrotate-specify-rotation-angle-a = 3DROTATE घूर्णन कोण को निर्दिष्ट करें <__ocs_arg_a__: errors = .save-warning = चेतावनी सहेजें .unable-to-save-drawing = ड्रॉइंग सहेजी नहीं जा सकी diff --git a/locales/hu-HU/opencadstudio.ftl b/locales/hu-HU/opencadstudio.ftl index d8996fbff..f44f32c93 100644 --- a/locales/hu-HU/opencadstudio.ftl +++ b/locales/hu-HU/opencadstudio.ftl @@ -2336,6 +2336,9 @@ common = .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 + .threedrotate-specify-base-point-count-objects = 3DROTATE Alappont megadása [__ocs_arg_count__ objektumok]: + .threedrotate-specify-rotation-axis = 3DROTATE Adja meg a forgástengelyt [X/Y/Z] vagy egy második pontját : + .threedrotate-specify-rotation-angle-a = 3DROTATE Forgatási szög megadása <__ocs_arg_a__>: errors = .save-warning = Figyelmeztetés mentése .unable-to-save-drawing = Nem sikerült menteni a rajzot diff --git a/locales/it-IT/opencadstudio.ftl b/locales/it-IT/opencadstudio.ftl index ab3cff89e..5dcea532e 100644 --- a/locales/it-IT/opencadstudio.ftl +++ b/locales/it-IT/opencadstudio.ftl @@ -2336,6 +2336,9 @@ common = .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 + .threedrotate-specify-base-point-count-objects = 3DROTATE Specificare il punto base [oggetti __ocs_arg_count__]: + .threedrotate-specify-rotation-axis = 3DROTATE Specificare l'asse di rotazione [X/Y/Z] o un secondo punto su di esso : + .threedrotate-specify-rotation-angle-a = 3DROTATE Specificare l'angolo di rotazione <__ocs_arg_a__>: errors = .save-warning = Salva avviso .unable-to-save-drawing = Impossibile salvare il disegno diff --git a/locales/ja-JP/opencadstudio.ftl b/locales/ja-JP/opencadstudio.ftl index 32bfca35e..319fc1bbb 100644 --- a/locales/ja-JP/opencadstudio.ftl +++ b/locales/ja-JP/opencadstudio.ftl @@ -2327,6 +2327,9 @@ common = .chspace-to-paper = CHSPACE: __ocs_fmt_0__ 個のオブジェクトをペーパー空間に移動しました。 .chspace-to-model = CHSPACE: __ocs_fmt_0__ 個のオブジェクトをモデル空間に移動しました。 .select-template-title = テンプレートを選択 + .threedrotate-specify-base-point-count-objects = 3DROTATE 基点を指定 [__ocs_arg_count__ 個]: + .threedrotate-specify-rotation-axis = 3DROTATE 回転軸 [X/Y/Z] または軸上の 2 点目を指定 : + .threedrotate-specify-rotation-angle-a = 3DROTATE 回転角度を指定 <__ocs_arg_a__>: errors = .save-warning = 保存の警告 .unable-to-save-drawing = 図面を保存できません diff --git a/locales/ko-KR/opencadstudio.ftl b/locales/ko-KR/opencadstudio.ftl index 1981cebce..461e6c85c 100644 --- a/locales/ko-KR/opencadstudio.ftl +++ b/locales/ko-KR/opencadstudio.ftl @@ -2336,6 +2336,9 @@ common = .chspace-to-paper = CHSPACE: 객체 __ocs_fmt_0__개를 도면 공간으로 옮겼습니다. .chspace-to-model = CHSPACE: 객체 __ocs_fmt_0__개를 모형 공간으로 옮겼습니다. .select-template-title = 템플릿 선택 + .threedrotate-specify-base-point-count-objects = 3DROTATE 기준점 지정 [__ocs_arg_count__ 객체]: + .threedrotate-specify-rotation-axis = 3DROTATE 회전 축 [X/Y/Z] 또는 축 위의 두 번째 점 지정 : + .threedrotate-specify-rotation-angle-a = 3DROTATE 회전 각도 <__ocs_arg_a__> 지정: errors = .save-warning = 경고 저장 .unable-to-save-drawing = 도면을 저장할 수 없습니다 diff --git a/locales/nl-NL/opencadstudio.ftl b/locales/nl-NL/opencadstudio.ftl index cfd5a9ef2..541dee671 100644 --- a/locales/nl-NL/opencadstudio.ftl +++ b/locales/nl-NL/opencadstudio.ftl @@ -2337,6 +2337,9 @@ common = .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 + .threedrotate-specify-base-point-count-objects = 3DROTATE Basispunt [__ocs_arg_count__ objecten] opgeven: + .threedrotate-specify-rotation-axis = 3DROTATE Geef de rotatieas op [X/Y/Z] of een tweede punt erop : + .threedrotate-specify-rotation-angle-a = 3DROTATE Rotatiehoek <__ocs_arg_a__> opgeven: errors = .save-warning = Waarschuwing opslaan .unable-to-save-drawing = Kan tekening niet opslaan diff --git a/locales/pl-PL/opencadstudio.ftl b/locales/pl-PL/opencadstudio.ftl index f6193a88b..46587c5f5 100644 --- a/locales/pl-PL/opencadstudio.ftl +++ b/locales/pl-PL/opencadstudio.ftl @@ -2336,6 +2336,9 @@ common = .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 + .threedrotate-specify-base-point-count-objects = 3DROTATE Określ punkt bazowy [obiekty __ocs_arg_count__]: + .threedrotate-specify-rotation-axis = 3DROTATE Określ oś obrotu [X/Y/Z] lub drugi punkt na niej : + .threedrotate-specify-rotation-angle-a = 3DROTATE Określ kąt obrotu <__ocs_arg_a__>: errors = .save-warning = Zapisz ostrzeżenie .unable-to-save-drawing = Nie można zapisać rysunku diff --git a/locales/pt-BR/opencadstudio.ftl b/locales/pt-BR/opencadstudio.ftl index 461c90074..558279ebc 100644 --- a/locales/pt-BR/opencadstudio.ftl +++ b/locales/pt-BR/opencadstudio.ftl @@ -2337,6 +2337,9 @@ common = .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 + .threedrotate-specify-base-point-count-objects = 3DROTATE Especifique o ponto base [__ocs_arg_count__ objetos]: + .threedrotate-specify-rotation-axis = 3DROTATE Especifique o eixo de rotação [X/Y/Z] ou um segundo ponto nele : + .threedrotate-specify-rotation-angle-a = 3DROTATE Especifique o ângulo de rotação <__ocs_arg_a__>: errors = .save-warning = Aviso de salvamento .unable-to-save-drawing = Não foi possível salvar o desenho diff --git a/locales/ru-RU/opencadstudio.ftl b/locales/ru-RU/opencadstudio.ftl index 34cee593c..30e6767bf 100644 --- a/locales/ru-RU/opencadstudio.ftl +++ b/locales/ru-RU/opencadstudio.ftl @@ -2340,6 +2340,9 @@ common = .chspace-to-paper = CHSPACE: перенесено в пространство листа объектов: __ocs_fmt_0__. .chspace-to-model = CHSPACE: перенесено в пространство модели объектов: __ocs_fmt_0__. .select-template-title = Выбор шаблона + .threedrotate-specify-base-point-count-objects = 3DROTATE Укажите базовую точку [__ocs_arg_count__ объекты]: + .threedrotate-specify-rotation-axis = 3DROTATE Укажите ось поворота [X/Y/Z] или вторую точку на ней : + .threedrotate-specify-rotation-angle-a = 3DROTATE Укажите угол поворота <__ocs_arg_a__> errors = .save-warning = Спасти предостережение .unable-to-save-drawing = Не удалось сохранить чертёж diff --git a/locales/tr-TR/opencadstudio.ftl b/locales/tr-TR/opencadstudio.ftl index ca21b0681..9441fb8c3 100644 --- a/locales/tr-TR/opencadstudio.ftl +++ b/locales/tr-TR/opencadstudio.ftl @@ -2321,6 +2321,9 @@ common = .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ç + .threedrotate-specify-base-point-count-objects = 3DROTATE Taban noktasını belirtin [__ocs_arg_count__ nesne]: + .threedrotate-specify-rotation-axis = 3DROTATE Döndürme eksenini [X/Y/Z] ya da eksen üzerinde ikinci bir noktayı belirtin : + .threedrotate-specify-rotation-angle-a = 3DROTATE Döndürme açısını belirtin <__ocs_arg_a__>: errors = .save-warning = Kaydetme Uyarısı .unable-to-save-drawing = Çizim Kaydedilemiyor diff --git a/locales/zh-CN/opencadstudio.ftl b/locales/zh-CN/opencadstudio.ftl index e46fff4f3..ebf534a3c 100644 --- a/locales/zh-CN/opencadstudio.ftl +++ b/locales/zh-CN/opencadstudio.ftl @@ -2324,6 +2324,9 @@ common = .chspace-to-paper = CHSPACE: 已将 __ocs_fmt_0__ 个对象移到图纸空间。 .chspace-to-model = CHSPACE: 已将 __ocs_fmt_0__ 个对象移到模型空间。 .select-template-title = 选择样板 + .threedrotate-specify-base-point-count-objects = 3DROTATE 指定基点 [__ocs_arg_count__ 个对象]: + .threedrotate-specify-rotation-axis = 3DROTATE 指定旋转轴 [X/Y/Z] 或轴上的第二点 : + .threedrotate-specify-rotation-angle-a = 3DROTATE 指定旋转角度 <__ocs_arg_a__>: errors = .save-warning = 保存警告 .unable-to-save-drawing = 无法保存图形 diff --git a/locales/zh-TW/opencadstudio.ftl b/locales/zh-TW/opencadstudio.ftl index 2516309b2..76f95d1a5 100644 --- a/locales/zh-TW/opencadstudio.ftl +++ b/locales/zh-TW/opencadstudio.ftl @@ -2336,6 +2336,9 @@ common = .chspace-to-paper = CHSPACE: 已將 __ocs_fmt_0__ 個物件移到圖紙空間。 .chspace-to-model = CHSPACE: 已將 __ocs_fmt_0__ 個物件移到模型空間。 .select-template-title = 選取樣板 + .threedrotate-specify-base-point-count-objects = 3DROTATE 指定基點 [__ocs_arg_count__ 個物件]: + .threedrotate-specify-rotation-axis = 3DROTATE 指定旋轉軸 [X/Y/Z] 或軸上的第二點 : + .threedrotate-specify-rotation-angle-a = 3DROTATE 指定旋轉角度 <__ocs_arg_a__>: errors = .save-warning = 儲存警告 .unable-to-save-drawing = 無法儲存圖形 diff --git a/src/app/commands/draw.rs b/src/app/commands/draw.rs index bb529a6ff..72f9a5358 100644 --- a/src/app/commands/draw.rs +++ b/src/app/commands/draw.rs @@ -2484,21 +2484,26 @@ impl OpenCADStudio { } // 3DROTATE [X|Y|Z] — rotate the selected solid about an axis. + // 3DROTATE — any objects about an axis through a base point. "3DROTATE" | "ROTATE3D" => { - use crate::command::SelectThenKeywordCommand; - let has_sel = !self.tabs[i].scene.selected_entities().is_empty(); - let c = SelectThenKeywordCommand::new( - "3DROTATE", - "3DROTATE rotation axis [X / Y / Z]:", - vec![ - ("X", "X", Some("3DROTATE angle in degrees:")), - ("Y", "Y", Some("3DROTATE angle in degrees:")), - ("Z", "Z", Some("3DROTATE angle in degrees:")), - ], - has_sel, - ); - self.command_line.push_info(&c.prompt()); - self.tabs[i].active_cmd = Some(Box::new(c)); + let handles: Vec<_> = self.tabs[i] + .scene + .selected_entities() + .into_iter() + .map(|(h, _)| h) + .collect(); + if handles.is_empty() { + use crate::modules::draw::select::SelectObjectsCommand; + let cmd = SelectObjectsCommand::new("3DROTATE"); + self.command_line.push_info(&cmd.prompt()); + self.tabs[i].active_cmd = Some(Box::new(cmd)); + } else { + use crate::modules::draw::modify::rotate::RotateCommand; + let wires = self.tabs[i].scene.wire_models_for(&handles); + let new_cmd = RotateCommand::new_3d(handles, wires); + self.command_line.push_info(&new_cmd.prompt()); + self.tabs[i].active_cmd = Some(Box::new(new_cmd)); + } } cmd if cmd.starts_with("3DROTATE ") || cmd.starts_with("ROTATE3D ") => { let parts: Vec = cmd diff --git a/src/locale_catalog.rs b/src/locale_catalog.rs index be44a3ebd..22e5abc54 100644 --- a/src/locale_catalog.rs +++ b/src/locale_catalog.rs @@ -4878,6 +4878,9 @@ 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")), + "3DROTATE Specify base point [%{count} objects]:" => Some(("common", "threedrotate-specify-base-point-count-objects")), + "3DROTATE Specify rotation axis [X/Y/Z] or a second point on it :" => Some(("common", "threedrotate-specify-rotation-axis")), + "3DROTATE Specify rotation angle <%{a}>:" => Some(("common", "threedrotate-specify-rotation-angle-a")), "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")), diff --git a/src/modules/draw/modify/rotate.rs b/src/modules/draw/modify/rotate.rs index e0a2dd267..8884a4f29 100644 --- a/src/modules/draw/modify/rotate.rs +++ b/src/modules/draw/modify/rotate.rs @@ -36,6 +36,8 @@ pub fn tool() -> ToolDef { enum Step { Center, + /// 3DROTATE: the axis through the base point. + Axis { center: DVec3 }, Angle { center: DVec3 }, RefFirst { center: DVec3 }, RefSecond { center: DVec3, first: DVec3 }, @@ -48,6 +50,11 @@ pub struct RotateCommand { step: Step, default_angle: f64, // degrees plane: WorkingPlane, + /// 3DROTATE: ask for the axis, then measure the angle in the plane square + /// to it. + spatial: bool, + /// The plane is the one square to the chosen axis, not the UCS. + axis_chosen: bool, } impl RotateCommand { @@ -58,9 +65,35 @@ impl RotateCommand { step: Step::Center, default_angle: defaults::get_rotate_angle(), plane: WorkingPlane::default(), + spatial: false, + axis_chosen: false, } } + /// 3DROTATE: any objects, about any axis through a base point. + pub fn new_3d(handles: Vec, wire_models: Vec) -> Self { + Self { spatial: true, ..Self::new(handles, wire_models) } + } + + /// Turn about `axis` through `center`: the angle is then read in the plane + /// square to it, so every later step works as in a plain rotation. + fn choose_axis(&mut self, center: DVec3, axis: DVec3) -> CmdResult { + let Some(axis) = axis.try_normalize() else { + return CmdResult::NeedPoint; + }; + // The UCS X laid into that plane, or Y when X runs along the axis. + let reference = [self.plane.x, self.plane.y] + .into_iter() + .map(|direction| direction - axis * direction.dot(axis)) + .find(|direction| direction.length() > 1e-6) + .unwrap_or(DVec3::X); + let x = reference.normalize(); + self.plane = WorkingPlane::new(center, x, axis.cross(x)); + self.axis_chosen = true; + self.step = Step::Angle { center }; + CmdResult::NeedPoint + } + fn commit(&self, center: DVec3, angle_rad: f64) -> CmdResult { defaults::set_rotate_angle(angle_rad.to_degrees()); CmdResult::TransformSelected( @@ -138,20 +171,38 @@ impl RotateCommand { impl CadCommand for RotateCommand { fn set_working_plane(&mut self, plane: WorkingPlane) { - self.plane = plane; + if !self.axis_chosen { + self.plane = plane; + } } fn name(&self) -> &'static str { - "ROTATE" + if self.spatial { + "3DROTATE" + } else { + "ROTATE" + } } fn prompt(&self) -> String { match &self.step { + Step::Center if self.spatial => t!( + "3DROTATE Specify base point [%{count} objects]:", + count = self.handles.len() + ) + .into_owned(), Step::Center => t!( "ROTATE Specify rotation center [%{count} objects]:", count = self.handles.len() ) .into_owned(), + Step::Axis { .. } => { + t!("3DROTATE Specify rotation axis [X/Y/Z] or a second point on it :").into_owned() + } + Step::Angle { .. } if self.spatial => { + let a = format!("{:.4}", self.default_angle); + t!("3DROTATE Specify rotation angle <%{a}>:", a = a).into_owned() + } Step::Angle { .. } => { let a = format!("{:.4}", self.default_angle); t!("ROTATE Specify rotation angle <%{a}>:", a = a).into_owned() @@ -172,6 +223,7 @@ impl CadCommand for RotateCommand { fn options(&self) -> Vec { use crate::command::CmdOption; match self.step { + Step::Axis { .. } => ["X", "Y", "Z"].map(|axis| CmdOption::new(axis, axis)).to_vec(), Step::Angle { .. } => vec![CmdOption::new(t!("Reference").as_ref(), "R")], _ => vec![], } @@ -179,10 +231,18 @@ impl CadCommand for RotateCommand { fn on_point(&mut self, pt: DVec3) -> CmdResult { match &self.step { + Step::Center if self.spatial => { + self.step = Step::Axis { center: pt }; + CmdResult::NeedPoint + } Step::Center => { self.step = Step::Angle { center: pt }; CmdResult::NeedPoint } + Step::Axis { center } => { + let center = *center; + self.choose_axis(center, pt - center) + } Step::Angle { center } => { let center = *center; let Some(angle_rad) = self.plane.angle(center, pt) else { @@ -214,6 +274,11 @@ impl CadCommand for RotateCommand { } fn on_enter(&mut self) -> CmdResult { + // The axis defaults to the UCS Z. + if let Step::Axis { center } = &self.step { + let center = *center; + return self.choose_axis(center, self.plane.z); + } // At the normal angle step, Enter uses the stored default angle. if let Step::Angle { center } = &self.step { let center = *center; @@ -228,6 +293,16 @@ impl CadCommand for RotateCommand { fn on_text_input(&mut self, text: &str) -> Option { let t = text.trim(); match &self.step { + Step::Axis { center } => { + let center = *center; + let axis = match t.to_ascii_uppercase().as_str() { + "X" => self.plane.x, + "Y" => self.plane.y, + "Z" => self.plane.z, + _ => return None, + }; + Some(self.choose_axis(center, axis)) + } Step::Angle { center } => { let center = *center; let low = t.to_ascii_lowercase(); @@ -260,6 +335,14 @@ impl CadCommand for RotateCommand { fn on_preview_wires(&mut self, pt: DVec3) -> Vec { let (center, angle_rad, guide_angle) = match &self.step { + Step::Axis { center } => { + return vec![WireModel::solid_f64( + "rotate_axis".into(), + vec![center.to_array(), pt.to_array()], + WireModel::CYAN, + false, + )]; + } Step::Angle { center } => { let Some(angle) = self.plane.angle(*center, pt) else { return vec![];