Merge pull request #859 from ramox81/codex/align-rectangle-behavior

Align rectangle command and property behavior
This commit is contained in:
Hakan Seven authored and GitHub committed 2026-08-22 23:04:02 +03:00
commit d38f4cbc28
28 files changed
+1037 -83

No files matched your search

+13
View File
@@ -326,6 +326,7 @@ common =
.block-rotation = دوران الكتلة
.blocks = الكتل
.blue = أزرق
.bulge = التحدب
.boolean-failed-the-solids-may-not-overlap = فشلت العملية المنطقية — قد لا تتداخل المجسمات.
.boolean-select-exactly-two-solids-created-this = العملية المنطقية: حدد مجسمين بالضبط أُنشئا في هذه الجلسة.
.bottom = أسفل
@@ -1341,6 +1342,18 @@ draw =
.ray-specify-through-point = RAY حدد نقطة العبور:
.rect-specify-first-corner = RECT حدد الزاوية الأولى:
.rect-specify-opposite-corner = RECT حدد الزاوية المقابلة:
.rect-calculate-dimensions-based-on-length-width = RECT احسب الأبعاد استنادًا إلى [الطول / العرض] <الطول>:
.rect-specify-elevation = RECT حدد الارتفاع <__ocs_fmt_0__>:
.rect-specify-fillet-radius = RECT حدد نصف قطر التدوير <__ocs_fmt_0__>:
.rect-specify-first-chamfer-distance = RECT حدد مسافة الشطف الأولى <__ocs_fmt_0__>:
.rect-specify-orientation-from-the-first-corner = RECT حدد الاتجاه من الزاوية الأولى:
.rect-specify-rectangle-area = RECT حدد مساحة المستطيل:
.rect-specify-rectangle-length = RECT حدد طول المستطيل:
.rect-specify-rectangle-width = RECT حدد عرض المستطيل:
.rect-specify-rotation-angle = RECT حدد زاوية الدوران <__ocs_fmt_0__>:
.rect-specify-second-chamfer-distance = RECT حدد مسافة الشطف الثانية <__ocs_fmt_0__>:
.rect-specify-thickness = RECT حدد السمك <__ocs_fmt_0__>:
.rect-specify-width = RECT حدد العرض <__ocs_fmt_0__>:
.rect-cen-specify-center-point = RECT CEN حدد نقطة المركز:
.rect-cen-specify-corner-point = RECT CEN حدد نقطة الزاوية:
.rect-rot-specify-adjacent-corner-defines-edge = RECT ROT حدد الزاوية المجاورة (تحدد اتجاه الحافة):
+13
View File
@@ -313,6 +313,7 @@ common =
.block-rotation = Завъртане на блок
.blocks = Блокове
.blue = Синьо
.bulge = Изпъкналост
.boolean-failed-the-solids-may-not-overlap = Булева операция неуспешна — телата може да не се припокриват.
.boolean-select-exactly-two-solids-created-this = Булева операция: избери точно две тела, създадени в тази сесия.
.bottom = Долу
@@ -1328,6 +1329,18 @@ draw =
.ray-specify-through-point = RAY Посочи точка на преминаване:
.rect-specify-first-corner = RECT Посочи първи ъгъл:
.rect-specify-opposite-corner = RECT Посочи противоположен ъгъл:
.rect-calculate-dimensions-based-on-length-width = RECT Изчисли размерите по [Дължина / Ширина] <Дължина>:
.rect-specify-elevation = RECT Посочи кота <__ocs_fmt_0__>:
.rect-specify-fillet-radius = RECT Посочи радиус на закръгляне <__ocs_fmt_0__>:
.rect-specify-first-chamfer-distance = RECT Посочи първо разстояние на фаската <__ocs_fmt_0__>:
.rect-specify-orientation-from-the-first-corner = RECT Посочи ориентация от първия ъгъл:
.rect-specify-rectangle-area = RECT Посочи площ на правоъгълника:
.rect-specify-rectangle-length = RECT Посочи дължина на правоъгълника:
.rect-specify-rectangle-width = RECT Посочи ширина на правоъгълника:
.rect-specify-rotation-angle = RECT Посочи ъгъл на завъртане <__ocs_fmt_0__>:
.rect-specify-second-chamfer-distance = RECT Посочи второ разстояние на фаската <__ocs_fmt_0__>:
.rect-specify-thickness = RECT Посочи дебелина <__ocs_fmt_0__>:
.rect-specify-width = RECT Посочи ширина <__ocs_fmt_0__>:
.rect-cen-specify-center-point = RECT CEN Посочи централна точка:
.rect-cen-specify-corner-point = RECT CEN Посочи ъглова точка:
.rect-rot-specify-adjacent-corner-defines-edge = RECT ROT Посочи съседен ъгъл (определя посоката на страната):
+13
View File
@@ -313,6 +313,7 @@ common =
.block-rotation = Rotace bloku
.blocks = Bloky
.blue = Modrá
.bulge = Vyklenutí
.boolean-failed-the-solids-may-not-overlap = Logická hodnota selhala – tělesa se nemusí překrývat.
.boolean-select-exactly-two-solids-created-this = Boolean: vyberte přesně dvě tělesa vytvořená v této relaci.
.bottom = Spodní
@@ -1328,6 +1329,18 @@ draw =
.ray-specify-through-point = RAY Určete průchozí bod:
.rect-specify-first-corner = RECT Určete první roh:
.rect-specify-opposite-corner = RECT Určete opačný roh:
.rect-calculate-dimensions-based-on-length-width = RECT Vypočítat rozměry podle [Délky / Šířky] <Délka>:
.rect-specify-elevation = RECT Určete výšku <__ocs_fmt_0__>:
.rect-specify-fillet-radius = RECT Určete poloměr zaoblení <__ocs_fmt_0__>:
.rect-specify-first-chamfer-distance = RECT Určete první vzdálenost zkosení <__ocs_fmt_0__>:
.rect-specify-orientation-from-the-first-corner = RECT Určete orientaci od prvního rohu:
.rect-specify-rectangle-area = RECT Určete plochu obdélníku:
.rect-specify-rectangle-length = RECT Určete délku obdélníku:
.rect-specify-rectangle-width = RECT Určete šířku obdélníku:
.rect-specify-rotation-angle = RECT Určete úhel otočení <__ocs_fmt_0__>:
.rect-specify-second-chamfer-distance = RECT Určete druhou vzdálenost zkosení <__ocs_fmt_0__>:
.rect-specify-thickness = RECT Určete tloušťku <__ocs_fmt_0__>:
.rect-specify-width = RECT Určete šířku <__ocs_fmt_0__>:
.rect-cen-specify-center-point = RECT CEN Zadejte střed:
.rect-cen-specify-corner-point =RECT CEN Zadejte rohový bod:
.rect-rot-specify-adjacent-corner-defines-edge = RECT ROT Určete sousední roh (definuje směr hrany):
+13
View File
@@ -314,6 +314,7 @@ common =
.block-rotation = Blockdrehung
.blocks = Blöcke
.blue = Blau
.bulge = Wölbung
.boolean-failed-the-solids-may-not-overlap = Boolean versagte - die Feststoffe überlappen sich möglicherweise nicht.
.boolean-select-exactly-two-solids-created-this = Boolean: Wählen Sie genau zwei Feststoffe erstellt diese Sitzung.
.bottom = Unten
@@ -1329,6 +1330,18 @@ draw =
.ray-specify-through-point = RAY Geben Sie durch Punkt an:
.rect-specify-first-corner = RECT Geben Sie die erste Ecke an:
.rect-specify-opposite-corner = RECT Geben Sie die gegenüberliegende Ecke an:
.rect-calculate-dimensions-based-on-length-width = RECT Abmessungen anhand von [Länge / Breite] berechnen <Länge>:
.rect-specify-elevation = RECT Erhebung angeben <__ocs_fmt_0__>:
.rect-specify-fillet-radius = RECT Abrundungsradius angeben <__ocs_fmt_0__>:
.rect-specify-first-chamfer-distance = RECT Ersten Fasenabstand angeben <__ocs_fmt_0__>:
.rect-specify-orientation-from-the-first-corner = RECT Ausrichtung von der ersten Ecke angeben:
.rect-specify-rectangle-area = RECT Rechteckfläche angeben:
.rect-specify-rectangle-length = RECT Rechtecklänge angeben:
.rect-specify-rectangle-width = RECT Rechteckbreite angeben:
.rect-specify-rotation-angle = RECT Drehwinkel angeben <__ocs_fmt_0__>:
.rect-specify-second-chamfer-distance = RECT Zweiten Fasenabstand angeben <__ocs_fmt_0__>:
.rect-specify-thickness = RECT Stärke angeben <__ocs_fmt_0__>:
.rect-specify-width = RECT Breite angeben <__ocs_fmt_0__>:
.rect-cen-specify-center-point = RECT CEN Mittelpunkt angeben:
.rect-cen-specify-corner-point = RECT CEN Eckpunkt angeben:
.rect-rot-specify-adjacent-corner-defines-edge = RECT ROT Angrenzende Ecke angeben (Kantenrichtung definieren):
+13
View File
@@ -314,6 +314,7 @@ common =
.block-rotation = Block rotation
.blocks = Blocks
.blue = Blue
.bulge = Bulge
.boolean-failed-the-solids-may-not-overlap = Boolean failed — the solids may not overlap.
.boolean-select-exactly-two-solids-created-this = Boolean: select exactly two solids created this session.
.bottom = Bottom
@@ -1329,6 +1330,18 @@ draw =
.ray-specify-through-point = RAY Specify through point:
.rect-specify-first-corner = RECT Specify first corner:
.rect-specify-opposite-corner = RECT Specify opposite corner:
.rect-calculate-dimensions-based-on-length-width = RECT Calculate dimensions based on [Length / Width] <Length>:
.rect-specify-elevation = RECT Specify elevation <__ocs_fmt_0__>:
.rect-specify-fillet-radius = RECT Specify fillet radius <__ocs_fmt_0__>:
.rect-specify-first-chamfer-distance = RECT Specify first chamfer distance <__ocs_fmt_0__>:
.rect-specify-orientation-from-the-first-corner = RECT Specify orientation from the first corner:
.rect-specify-rectangle-area = RECT Specify rectangle area:
.rect-specify-rectangle-length = RECT Specify rectangle length:
.rect-specify-rectangle-width = RECT Specify rectangle width:
.rect-specify-rotation-angle = RECT Specify rotation angle <__ocs_fmt_0__>:
.rect-specify-second-chamfer-distance = RECT Specify second chamfer distance <__ocs_fmt_0__>:
.rect-specify-thickness = RECT Specify thickness <__ocs_fmt_0__>:
.rect-specify-width = RECT Specify width <__ocs_fmt_0__>:
.rect-cen-specify-center-point = RECT CEN Specify center point:
.rect-cen-specify-corner-point = RECT CEN Specify corner point:
.rect-rot-specify-adjacent-corner-defines-edge = RECT ROT Specify adjacent corner (defines edge direction):
+13
View File
@@ -314,6 +314,7 @@ common =
.block-rotation = Rotación del bloque
.blocks = Bloques
.blue = Azul
.bulge = Curvatura
.boolean-failed-the-solids-may-not-overlap = Falló la operación booleana; es posible que los sólidos no se solapen.
.boolean-select-exactly-two-solids-created-this = Operación booleana: seleccione exactamente dos sólidos creados en esta sesión.
.bottom = Inferior
@@ -1329,6 +1330,18 @@ draw =
.ray-specify-through-point = RAY Precise el punto de paso:
.rect-specify-first-corner = RECT Precise la primera esquina:
.rect-specify-opposite-corner = RECT Precise la esquina opuesta:
.rect-calculate-dimensions-based-on-length-width = RECT Calcule las dimensiones según [Longitud / Anchura] <Longitud>:
.rect-specify-elevation = RECT Precise la elevación <__ocs_fmt_0__>:
.rect-specify-fillet-radius = RECT Precise el radio de empalme <__ocs_fmt_0__>:
.rect-specify-first-chamfer-distance = RECT Precise la primera distancia de chaflán <__ocs_fmt_0__>:
.rect-specify-orientation-from-the-first-corner = RECT Precise la orientación desde la primera esquina:
.rect-specify-rectangle-area = RECT Precise el área del rectángulo:
.rect-specify-rectangle-length = RECT Precise la longitud del rectángulo:
.rect-specify-rectangle-width = RECT Precise la anchura del rectángulo:
.rect-specify-rotation-angle = RECT Precise el ángulo de rotación <__ocs_fmt_0__>:
.rect-specify-second-chamfer-distance = RECT Precise la segunda distancia de chaflán <__ocs_fmt_0__>:
.rect-specify-thickness = RECT Precise el espesor <__ocs_fmt_0__>:
.rect-specify-width = RECT Precise la anchura <__ocs_fmt_0__>:
.rect-cen-specify-center-point = RECT CEN Precise el punto central:
.rect-cen-specify-corner-point = RECT CEN Precise el punto de esquina:
.rect-rot-specify-adjacent-corner-defines-edge = RECT ROT Precise la esquina adyacente (define la dirección de la arista):
+13
View File
@@ -313,6 +313,7 @@ common =
.block-rotation = Lohkon kierto
.blocks = Lohkot
.blue = Sininen
.bulge = Kaarevuus
.boolean-failed-the-solids-may-not-overlap = Boolen arvo epäonnistui – kiinteät aineet eivät ehkä mene päällekkäin.
.boolean-select-exactly-two-solids-created-this = Boolen arvo: valitse täsmälleen kaksi tässä istunnossa luotua solidia.
.bottom = Pohja
@@ -1328,6 +1329,18 @@ draw =
.ray-specify-through-point = RAY Määritä pisteen kautta:
.rect-specify-first-corner = RECT Määritä ensimmäinen kulma:
.rect-specify-opposite-corner = RECT Määritä vastakkainen kulma:
.rect-calculate-dimensions-based-on-length-width = RECT Laske mitat [Pituuden / Leveyden] perusteella <Pituus>:
.rect-specify-elevation = RECT Määritä korkeusasema <__ocs_fmt_0__>:
.rect-specify-fillet-radius = RECT Määritä pyöristyssäde <__ocs_fmt_0__>:
.rect-specify-first-chamfer-distance = RECT Määritä ensimmäinen viiste-etäisyys <__ocs_fmt_0__>:
.rect-specify-orientation-from-the-first-corner = RECT Määritä suunta ensimmäisestä kulmasta:
.rect-specify-rectangle-area = RECT Määritä suorakulmion pinta-ala:
.rect-specify-rectangle-length = RECT Määritä suorakulmion pituus:
.rect-specify-rectangle-width = RECT Määritä suorakulmion leveys:
.rect-specify-rotation-angle = RECT Määritä kiertokulma <__ocs_fmt_0__>:
.rect-specify-second-chamfer-distance = RECT Määritä toinen viiste-etäisyys <__ocs_fmt_0__>:
.rect-specify-thickness = RECT Määritä paksuus <__ocs_fmt_0__>:
.rect-specify-width = RECT Määritä leveys <__ocs_fmt_0__>:
.rect-cen-specify-center-point = RECT CEN Määritä keskipiste:
.rect-cen-specify-corner-point =RECT CEN Määritä kulmapiste:
.rect-rot-specify-adjacent-corner-defines-edge = RECT ROT Määritä viereinen kulma (määrittää reunasuunnan):
+13
View File
@@ -314,6 +314,7 @@ common =
.block-rotation = Rotation par blocs
.blocks = Blocs
.blue = Bleu
.bulge = Courbure
.boolean-failed-the-solids-may-not-overlap = Booléenne échoué — les solides ne peuvent pas se chevaucher.
.boolean-select-exactly-two-solids-created-this = Booléen : sélectionnez exactement deux solides créés cette session.
.bottom = En bas
@@ -1329,6 +1330,18 @@ draw =
.ray-specify-through-point = RAY Spécifiez par le point:
.rect-specify-first-corner = RECT Spécifiez le premier coin :
.rect-specify-opposite-corner = RECT Spécifiez le coin opposé :
.rect-calculate-dimensions-based-on-length-width = RECT Calculer les dimensions selon [Longueur / Largeur] <Longueur> :
.rect-specify-elevation = RECT Spécifiez l’élévation <__ocs_fmt_0__> :
.rect-specify-fillet-radius = RECT Spécifiez le rayon de raccord <__ocs_fmt_0__> :
.rect-specify-first-chamfer-distance = RECT Spécifiez la première distance de chanfrein <__ocs_fmt_0__> :
.rect-specify-orientation-from-the-first-corner = RECT Spécifiez l’orientation depuis le premier coin :
.rect-specify-rectangle-area = RECT Spécifiez l’aire du rectangle :
.rect-specify-rectangle-length = RECT Spécifiez la longueur du rectangle :
.rect-specify-rectangle-width = RECT Spécifiez la largeur du rectangle :
.rect-specify-rotation-angle = RECT Spécifiez l’angle de rotation <__ocs_fmt_0__> :
.rect-specify-second-chamfer-distance = RECT Spécifiez la deuxième distance de chanfrein <__ocs_fmt_0__> :
.rect-specify-thickness = RECT Spécifiez l’épaisseur <__ocs_fmt_0__> :
.rect-specify-width = RECT Spécifiez la largeur <__ocs_fmt_0__> :
.rect-cen-specify-center-point = RECT CEN Spécifier le point central:
.rect-cen-specify-corner-point = RECT CEN Spécifiez le point d'angle:
.rect-rot-specify-adjacent-corner-defines-edge = RECT ROT Spécifiez le coin adjacent (définit la direction du bord):
+13
View File
@@ -302,6 +302,7 @@ common =
.block-rotation = ब्लॉक रोटेशन
.blocks = ब्लॉक
.blue = नीला
.bulge = उभार
.boolean-failed-the-solids-may-not-overlap = बोओलेन विफल रहा - ठोस ओवरलैप नहीं हो सकता।
.boolean-select-exactly-two-solids-created-this = बोओलान: इस सत्र को बनाने वाले दो ठोस चुनें।
.bottom = नीचे
@@ -1317,6 +1318,18 @@ draw =
.ray-specify-through-point = RAY पॉइंट के माध्यम से निर्दिष्ट करें:
.rect-specify-first-corner = RECT पहले कोने को निर्दिष्ट करें:
.rect-specify-opposite-corner = RECT विपरीत कोने निर्दिष्ट करें:
.rect-calculate-dimensions-based-on-length-width = RECT [लंबाई / चौड़ाई] के आधार पर आयाम निकालें <लंबाई>:
.rect-specify-elevation = RECT ऊंचाई निर्दिष्ट करें <__ocs_fmt_0__>:
.rect-specify-fillet-radius = RECT फिलेट त्रिज्या निर्दिष्ट करें <__ocs_fmt_0__>:
.rect-specify-first-chamfer-distance = RECT पहली चैम्फर दूरी निर्दिष्ट करें <__ocs_fmt_0__>:
.rect-specify-orientation-from-the-first-corner = RECT पहले कोने से दिशा निर्दिष्ट करें:
.rect-specify-rectangle-area = RECT आयत का क्षेत्रफल निर्दिष्ट करें:
.rect-specify-rectangle-length = RECT आयत की लंबाई निर्दिष्ट करें:
.rect-specify-rectangle-width = RECT आयत की चौड़ाई निर्दिष्ट करें:
.rect-specify-rotation-angle = RECT घूर्णन कोण निर्दिष्ट करें <__ocs_fmt_0__>:
.rect-specify-second-chamfer-distance = RECT दूसरी चैम्फर दूरी निर्दिष्ट करें <__ocs_fmt_0__>:
.rect-specify-thickness = RECT मोटाई निर्दिष्ट करें <__ocs_fmt_0__>:
.rect-specify-width = RECT चौड़ाई निर्दिष्ट करें <__ocs_fmt_0__>:
.rect-cen-specify-center-point = RECT CEN केंद्र बिंदु निर्दिष्ट करें:
.rect-cen-specify-corner-point = RECT CEN कोने बिंदु निर्दिष्ट करें:
.rect-rot-specify-adjacent-corner-defines-edge = RECT ROT निकटवर्ती कोने (डिफ़ाइन किनारे दिशा) निर्दिष्ट करें:
+13
View File
@@ -313,6 +313,7 @@ common =
.block-rotation = Blokkforgatás
.blocks = Blokkok
.blue = kék
.bulge = Kidudorodás
.boolean-failed-the-solids-may-not-overlap = A logikai elemzés sikertelen – előfordulhat, hogy a szilárdtestek nem fedik át egymást.
.boolean-select-exactly-two-solids-created-this = Logikai: jelöljön ki pontosan két szilárdtestet, amelyek ebben a munkamenetben jöttek létre.
.bottom = Alul
@@ -1328,6 +1329,18 @@ draw =
.ray-specify-through-point = RAY Pont megadása:
.rect-specify-first-corner = RECT Adja meg az első sarkot:
.rect-specify-opposite-corner = RECT Adja meg a szemközti sarkot:
.rect-calculate-dimensions-based-on-length-width = RECT Méretek számítása [Hossz / Szélesség] alapján <Hossz>:
.rect-specify-elevation = RECT Adja meg a magasságot <__ocs_fmt_0__>:
.rect-specify-fillet-radius = RECT Adja meg a lekerekítés sugarát <__ocs_fmt_0__>:
.rect-specify-first-chamfer-distance = RECT Adja meg az első letörési távolságot <__ocs_fmt_0__>:
.rect-specify-orientation-from-the-first-corner = RECT Adja meg az irányt az első saroktól:
.rect-specify-rectangle-area = RECT Adja meg a téglalap területét:
.rect-specify-rectangle-length = RECT Adja meg a téglalap hosszát:
.rect-specify-rectangle-width = RECT Adja meg a téglalap szélességét:
.rect-specify-rotation-angle = RECT Adja meg az elforgatási szöget <__ocs_fmt_0__>:
.rect-specify-second-chamfer-distance = RECT Adja meg a második letörési távolságot <__ocs_fmt_0__>:
.rect-specify-thickness = RECT Adja meg a vastagságot <__ocs_fmt_0__>:
.rect-specify-width = RECT Adja meg a szélességet <__ocs_fmt_0__>:
.rect-cen-specify-center-point = RECT CEN Középpont megadása:
.rect-cen-specify-corner-point =RECT CEN Sarokpont megadása:
.rect-rot-specify-adjacent-corner-defines-edge = RECT ROT Adja meg a szomszédos sarkot (meghatározza az él irányát):
+13
View File
@@ -313,6 +313,7 @@ common =
.block-rotation = Bloccare la rotazione
.blocks = Blocchi
.blue = Blu
.bulge = Curvatura
.boolean-failed-the-solids-may-not-overlap = Booleano non riuscito: i solidi potrebbero non sovrapporsi.
.boolean-select-exactly-two-solids-created-this = Booleano: seleziona esattamente due solidi creati in questa sessione.
.bottom = In basso
@@ -1328,6 +1329,18 @@ draw =
.ray-specify-through-point = RAY Specificare il punto di passaggio:
.rect-specify-first-corner = RECT Specificare il primo angolo:
.rect-specify-opposite-corner = RECT Specificare l'angolo opposto:
.rect-calculate-dimensions-based-on-length-width = RECT Calcolare le dimensioni in base a [Lunghezza / Larghezza] <Lunghezza>:
.rect-specify-elevation = RECT Specificare l’elevazione <__ocs_fmt_0__>:
.rect-specify-fillet-radius = RECT Specificare il raggio di raccordo <__ocs_fmt_0__>:
.rect-specify-first-chamfer-distance = RECT Specificare la prima distanza di smusso <__ocs_fmt_0__>:
.rect-specify-orientation-from-the-first-corner = RECT Specificare l’orientamento dal primo angolo:
.rect-specify-rectangle-area = RECT Specificare l’area del rettangolo:
.rect-specify-rectangle-length = RECT Specificare la lunghezza del rettangolo:
.rect-specify-rectangle-width = RECT Specificare la larghezza del rettangolo:
.rect-specify-rotation-angle = RECT Specificare l’angolo di rotazione <__ocs_fmt_0__>:
.rect-specify-second-chamfer-distance = RECT Specificare la seconda distanza di smusso <__ocs_fmt_0__>:
.rect-specify-thickness = RECT Specificare lo spessore <__ocs_fmt_0__>:
.rect-specify-width = RECT Specificare la larghezza <__ocs_fmt_0__>:
.rect-cen-specify-center-point = RECT CEN Specificare il punto centrale:
.rect-cen-specify-corner-point =RECT CEN Specificare il punto d'angolo:
.rect-rot-specify-adjacent-corner-defines-edge = RECT ROT Specificare l'angolo adiacente (definisce la direzione del bordo):
+13
View File
@@ -304,6 +304,7 @@ common =
.block-rotation = ブロック回転
.blocks = ブロック
.blue = 青
.bulge = ふくらみ
.boolean-failed-the-solids-may-not-overlap = ブール演算に失敗しました — ソリッドが重なっていない可能性があります。
.boolean-select-exactly-two-solids-created-this = ブール演算: このセッションで作成したソリッドを 2 つ選択してください。
.bottom = 下
@@ -1319,6 +1320,18 @@ draw =
.ray-specify-through-point = RAY 通過点を指定:
.rect-specify-first-corner = RECT 最初のコーナーを指定:
.rect-specify-opposite-corner = RECT 反対側のコーナーを指定:
.rect-calculate-dimensions-based-on-length-width = RECT [長さ / 幅] に基づいて寸法を計算 <長さ>:
.rect-specify-elevation = RECT 高度を指定 <__ocs_fmt_0__>:
.rect-specify-fillet-radius = RECT フィレット半径を指定 <__ocs_fmt_0__>:
.rect-specify-first-chamfer-distance = RECT 1 番目の面取り距離を指定 <__ocs_fmt_0__>:
.rect-specify-orientation-from-the-first-corner = RECT 最初のコーナーから方向を指定:
.rect-specify-rectangle-area = RECT 長方形の面積を指定:
.rect-specify-rectangle-length = RECT 長方形の長さを指定:
.rect-specify-rectangle-width = RECT 長方形の幅を指定:
.rect-specify-rotation-angle = RECT 回転角度を指定 <__ocs_fmt_0__>:
.rect-specify-second-chamfer-distance = RECT 2 番目の面取り距離を指定 <__ocs_fmt_0__>:
.rect-specify-thickness = RECT 厚さを指定 <__ocs_fmt_0__>:
.rect-specify-width = RECT 幅を指定 <__ocs_fmt_0__>:
.rect-cen-specify-center-point = RECT CEN 中心点を指定:
.rect-cen-specify-corner-point = RECT CEN コーナー点を指定:
.rect-rot-specify-adjacent-corner-defines-edge = RECT ROT 隣接コーナーを指定(エッジ方向を定義):
+13
View File
@@ -313,6 +313,7 @@ common =
.block-rotation = 블록 회전
.blocks = 블록
.blue = 블루
.bulge = 벌지
.boolean-failed-the-solids-may-not-overlap = 부울 실패 - 솔리드가 겹치지 않을 수 있습니다.
.boolean-select-exactly-two-solids-created-this = 부울: 이번 세션에서 생성된 정확히 두 개의 솔리드를 선택합니다.
.bottom = 바닥
@@ -1328,6 +1329,18 @@ draw =
.ray-specify-through-point = RAY 통과점 지정:
.rect-specify-first-corner = RECT 첫 번째 구석 지정:
.rect-specify-opposite-corner = RECT 반대편 모서리 지정:
.rect-calculate-dimensions-based-on-length-width = RECT [길이 / 폭]을 기준으로 치수 계산 <길이>:
.rect-specify-elevation = RECT 고도 지정 <__ocs_fmt_0__>:
.rect-specify-fillet-radius = RECT 모깎기 반지름 지정 <__ocs_fmt_0__>:
.rect-specify-first-chamfer-distance = RECT 첫 번째 모따기 거리 지정 <__ocs_fmt_0__>:
.rect-specify-orientation-from-the-first-corner = RECT 첫 번째 구석에서 방향 지정:
.rect-specify-rectangle-area = RECT 직사각형 면적 지정:
.rect-specify-rectangle-length = RECT 직사각형 길이 지정:
.rect-specify-rectangle-width = RECT 직사각형 폭 지정:
.rect-specify-rotation-angle = RECT 회전 각도 지정 <__ocs_fmt_0__>:
.rect-specify-second-chamfer-distance = RECT 두 번째 모따기 거리 지정 <__ocs_fmt_0__>:
.rect-specify-thickness = RECT 두께 지정 <__ocs_fmt_0__>:
.rect-specify-width = RECT 폭 지정 <__ocs_fmt_0__>:
.rect-cen-specify-center-point = RECT CEN 중심점 지정:
.rect-cen-specify-corner-point =RECT CEN 코너점 지정:
.rect-rot-specify-adjacent-corner-defines-edge = RECT ROT 인접 모서리 지정(모서리 방향 정의):
+13
View File
@@ -314,6 +314,7 @@ common =
.block-rotation = Blokrotatie
.blocks = Blokken
.blue = Blauw
.bulge = Bolling
.boolean-failed-the-solids-may-not-overlap = Boolean mislukt . . de vaste stoffen mogen niet overlappen.
.boolean-select-exactly-two-solids-created-this = Boolean: selecteer precies twee vaste stoffen die deze sessie hebben gemaakt.
.bottom = Onderkant
@@ -1329,6 +1330,18 @@ draw =
.ray-specify-through-point = RAY Door het punt opgeven:
.rect-specify-first-corner = RECT Eerste hoek opgeven:
.rect-specify-opposite-corner = RECT Geef de andere hoek op:
.rect-calculate-dimensions-based-on-length-width = RECT Bereken afmetingen op basis van [Lengte / Breedte] <Lengte>:
.rect-specify-elevation = RECT Geef hoogte op <__ocs_fmt_0__>:
.rect-specify-fillet-radius = RECT Geef afrondingsstraal op <__ocs_fmt_0__>:
.rect-specify-first-chamfer-distance = RECT Geef eerste afschuiningsafstand op <__ocs_fmt_0__>:
.rect-specify-orientation-from-the-first-corner = RECT Geef richting vanaf de eerste hoek op:
.rect-specify-rectangle-area = RECT Geef oppervlakte van rechthoek op:
.rect-specify-rectangle-length = RECT Geef lengte van rechthoek op:
.rect-specify-rectangle-width = RECT Geef breedte van rechthoek op:
.rect-specify-rotation-angle = RECT Geef rotatiehoek op <__ocs_fmt_0__>:
.rect-specify-second-chamfer-distance = RECT Geef tweede afschuiningsafstand op <__ocs_fmt_0__>:
.rect-specify-thickness = RECT Geef dikte op <__ocs_fmt_0__>:
.rect-specify-width = RECT Geef breedte op <__ocs_fmt_0__>:
.rect-cen-specify-center-point = RECT CEN Centrumpunt opgeven:
.rect-cen-specify-corner-point = RECT CEN Geef hoekpunt op:
.rect-rot-specify-adjacent-corner-defines-edge = RECT ROT Aangrenzende hoek opgeven (randrichting definiëren):
+13
View File
@@ -313,6 +313,7 @@ common =
.block-rotation = Obrót bloku
.blocks = Bloki
.blue = Niebieski
.bulge = Wybrzuszenie
.boolean-failed-the-solids-may-not-overlap = Wartość logiczna nie powiodła się — bryły nie mogą się nakładać.
.boolean-select-exactly-two-solids-created-this = Boolean: wybierz dokładnie dwie bryły utworzone w tej sesji.
.bottom = Dół
@@ -1328,6 +1329,18 @@ draw =
.ray-specify-through-point = RAY Określ punkt przelotowy:
.rect-specify-first-corner = RECT Określ pierwszy narożnik:
.rect-specify-opposite-corner = RECT Określ przeciwny narożnik:
.rect-calculate-dimensions-based-on-length-width = RECT Oblicz wymiary na podstawie [Długości / Szerokości] <Długość>:
.rect-specify-elevation = RECT Określ rzędną <__ocs_fmt_0__>:
.rect-specify-fillet-radius = RECT Określ promień zaokrąglenia <__ocs_fmt_0__>:
.rect-specify-first-chamfer-distance = RECT Określ pierwszą odległość fazowania <__ocs_fmt_0__>:
.rect-specify-orientation-from-the-first-corner = RECT Określ orientację od pierwszego narożnika:
.rect-specify-rectangle-area = RECT Określ pole prostokąta:
.rect-specify-rectangle-length = RECT Określ długość prostokąta:
.rect-specify-rectangle-width = RECT Określ szerokość prostokąta:
.rect-specify-rotation-angle = RECT Określ kąt obrotu <__ocs_fmt_0__>:
.rect-specify-second-chamfer-distance = RECT Określ drugą odległość fazowania <__ocs_fmt_0__>:
.rect-specify-thickness = RECT Określ grubość <__ocs_fmt_0__>:
.rect-specify-width = RECT Określ szerokość <__ocs_fmt_0__>:
.rect-cen-specify-center-point = RECT CEN Określ punkt środkowy:
.rect-cen-specify-corner-point =RECT CEN Określ punkt narożny:
.rect-rot-specify-adjacent-corner-defines-edge = RECT ROT Określ sąsiadujący narożnik (określa kierunek krawędzi):
+13
View File
@@ -314,6 +314,7 @@ common =
.block-rotation = Rotação do bloco
.blocks = Blocos
.blue = Azul
.bulge = Curvatura
.boolean-failed-the-solids-may-not-overlap = A operação booleana falhou — os sólidos podem não se sobrepor.
.boolean-select-exactly-two-solids-created-this = Operação booleana: selecione exatamente dois sólidos criados nesta sessão.
.bottom = Inferior
@@ -1329,6 +1330,18 @@ draw =
.ray-specify-through-point = RAY Especifique o ponto de passagem:
.rect-specify-first-corner = RECT Especifique o primeiro canto:
.rect-specify-opposite-corner = RECT Especifique o canto oposto:
.rect-calculate-dimensions-based-on-length-width = RECT Calcule as dimensões com base em [Comprimento / Largura] <Comprimento>:
.rect-specify-elevation = RECT Especifique a elevação <__ocs_fmt_0__>:
.rect-specify-fillet-radius = RECT Especifique o raio de concordância <__ocs_fmt_0__>:
.rect-specify-first-chamfer-distance = RECT Especifique a primeira distância do chanfro <__ocs_fmt_0__>:
.rect-specify-orientation-from-the-first-corner = RECT Especifique a orientação a partir do primeiro canto:
.rect-specify-rectangle-area = RECT Especifique a área do retângulo:
.rect-specify-rectangle-length = RECT Especifique o comprimento do retângulo:
.rect-specify-rectangle-width = RECT Especifique a largura do retângulo:
.rect-specify-rotation-angle = RECT Especifique o ângulo de rotação <__ocs_fmt_0__>:
.rect-specify-second-chamfer-distance = RECT Especifique a segunda distância do chanfro <__ocs_fmt_0__>:
.rect-specify-thickness = RECT Especifique a espessura <__ocs_fmt_0__>:
.rect-specify-width = RECT Especifique a largura <__ocs_fmt_0__>:
.rect-cen-specify-center-point = RECT CEN Especifique o ponto central:
.rect-cen-specify-corner-point = RECT CEN Especifique o ponto de canto:
.rect-rot-specify-adjacent-corner-defines-edge = RECT ROT Especifique o canto adjacente (define a direção da aresta):
+13
View File
@@ -317,6 +317,7 @@ common =
.block-rotation = Вращение блока
.blocks = Блоки
.blue = Синий
.bulge = Выпуклость
.boolean-failed-the-solids-may-not-overlap = Булевый не сработал — твердые вещества могут не перекрываться.
.boolean-select-exactly-two-solids-created-this = Булев: выберите ровно два твердых тела, созданных на этом сеансе.
.bottom = Снизу
@@ -1332,6 +1333,18 @@ draw =
.ray-specify-through-point = RAY Укажите через точку:
.rect-specify-first-corner = RECT Укажите первый угол:
.rect-specify-opposite-corner = RECT Укажите противоположный угол:
.rect-calculate-dimensions-based-on-length-width = RECT Рассчитать размеры по [Длине / Ширине] <Длина>:
.rect-specify-elevation = RECT Укажите отметку <__ocs_fmt_0__>:
.rect-specify-fillet-radius = RECT Укажите радиус скругления <__ocs_fmt_0__>:
.rect-specify-first-chamfer-distance = RECT Укажите первое расстояние фаски <__ocs_fmt_0__>:
.rect-specify-orientation-from-the-first-corner = RECT Укажите направление от первого угла:
.rect-specify-rectangle-area = RECT Укажите площадь прямоугольника:
.rect-specify-rectangle-length = RECT Укажите длину прямоугольника:
.rect-specify-rectangle-width = RECT Укажите ширину прямоугольника:
.rect-specify-rotation-angle = RECT Укажите угол поворота <__ocs_fmt_0__>:
.rect-specify-second-chamfer-distance = RECT Укажите второе расстояние фаски <__ocs_fmt_0__>:
.rect-specify-thickness = RECT Укажите толщину <__ocs_fmt_0__>:
.rect-specify-width = RECT Укажите ширину <__ocs_fmt_0__>:
.rect-cen-specify-center-point = RECT CEN Укажите центральную точку:
.rect-cen-specify-corner-point = RECT CEN Укажите угловую точку:
.rect-rot-specify-adjacent-corner-defines-edge = RECT ROT Укажите смежный угол (определите направление кромки):
+13
View File
@@ -300,6 +300,7 @@ common =
.block-rotation = Blok döndürmesi
.blocks = Bloklar
.blue = Mavi
.bulge = Kavis
.boolean-failed-the-solids-may-not-overlap = Boolean işlemi başarısız oldu; katılar çakışmıyor olabilir.
.boolean-select-exactly-two-solids-created-this = Boolean: bu oturumda oluşturulmuş tam iki katı seçin.
.bottom = Alt
@@ -1311,6 +1312,18 @@ draw =
.ray-specify-through-point = RAY Geçiş noktasını belirtin:
.rect-specify-first-corner = RECT İlk köşeyi belirtin:
.rect-specify-opposite-corner = RECT Karşı köşeyi belirtin:
.rect-calculate-dimensions-based-on-length-width = RECT Boyutları [Uzunluk / Genişlik] temelinde hesaplayın <Uzunluk>:
.rect-specify-elevation = RECT Kotu belirtin <__ocs_fmt_0__>:
.rect-specify-fillet-radius = RECT Yuvarlatma yarıçapını belirtin <__ocs_fmt_0__>:
.rect-specify-first-chamfer-distance = RECT İlk pah mesafesini belirtin <__ocs_fmt_0__>:
.rect-specify-orientation-from-the-first-corner = RECT İlk köşeden yönü belirtin:
.rect-specify-rectangle-area = RECT Dikdörtgen alanını belirtin:
.rect-specify-rectangle-length = RECT Dikdörtgen uzunluğunu belirtin:
.rect-specify-rectangle-width = RECT Dikdörtgen genişliğini belirtin:
.rect-specify-rotation-angle = RECT Döndürme açısını belirtin <__ocs_fmt_0__>:
.rect-specify-second-chamfer-distance = RECT İkinci pah mesafesini belirtin <__ocs_fmt_0__>:
.rect-specify-thickness = RECT Kalınlığı belirtin <__ocs_fmt_0__>:
.rect-specify-width = RECT Genişliği belirtin <__ocs_fmt_0__>:
.rect-cen-specify-center-point = RECT CEN Merkez noktasını belirtin:
.rect-cen-specify-corner-point = RECT CEN Köşe noktasını belirtin:
.rect-rot-specify-adjacent-corner-defines-edge = RECT ROT Bitişik köşeyi belirtin (kenar yönünü tanımlar):
+13
View File
@@ -301,6 +301,7 @@ common =
.block-rotation = 块旋转
.blocks = 块状物
.blue = 蓝色
.bulge = 凸度
.boolean-failed-the-solids-may-not-overlap = 布尔失败——固件可能不会重叠.
.boolean-select-exactly-two-solids-created-this = 布尔: 选择创建此会话的正中两个固件 。
.bottom = 底部
@@ -1316,6 +1317,18 @@ draw =
.ray-specify-through-point = RAY通过点指定 :
.rect-specify-first-corner = RECT 指定第一角 :
.rect-specify-opposite-corner = RECT 指定相向角 :
.rect-calculate-dimensions-based-on-length-width = RECT 根据 [长度 / 宽度] 计算尺寸 <长度>:
.rect-specify-elevation = RECT 指定标高 <__ocs_fmt_0__>:
.rect-specify-fillet-radius = RECT 指定圆角半径 <__ocs_fmt_0__>:
.rect-specify-first-chamfer-distance = RECT 指定第一个倒角距离 <__ocs_fmt_0__>:
.rect-specify-orientation-from-the-first-corner = RECT 从第一个角点指定方向:
.rect-specify-rectangle-area = RECT 指定矩形面积:
.rect-specify-rectangle-length = RECT 指定矩形长度:
.rect-specify-rectangle-width = RECT 指定矩形宽度:
.rect-specify-rotation-angle = RECT 指定旋转角度 <__ocs_fmt_0__>:
.rect-specify-second-chamfer-distance = RECT 指定第二个倒角距离 <__ocs_fmt_0__>:
.rect-specify-thickness = RECT 指定厚度 <__ocs_fmt_0__>:
.rect-specify-width = RECT 指定宽度 <__ocs_fmt_0__>:
.rect-cen-specify-center-point = RECT CEN 指定中心点:
.rect-cen-specify-corner-point = RECT CEN 指定角点:
.rect-rot-specify-adjacent-corner-defines-edge = RECTROT指定相邻的角( 定边方向) :
+13
View File
@@ -313,6 +313,7 @@ common =
.block-rotation = 圖塊旋轉
.blocks = 圖塊
.blue = 藍色
.bulge = 凸度
.boolean-failed-the-solids-may-not-overlap = 布爾失敗——固件可能不會重疊.
.boolean-select-exactly-two-solids-created-this = 布爾: 選擇建立此會話的正中兩個固件 。
.bottom = 底部
@@ -1328,6 +1329,18 @@ draw =
.ray-specify-through-point = RAY通過點指定 :
.rect-specify-first-corner = RECT 指定第一角 :
.rect-specify-opposite-corner = RECT 指定相向角 :
.rect-calculate-dimensions-based-on-length-width = RECT 根據 [長度 / 寬度] 計算尺寸 <長度>:
.rect-specify-elevation = RECT 指定高程 <__ocs_fmt_0__>:
.rect-specify-fillet-radius = RECT 指定圓角半徑 <__ocs_fmt_0__>:
.rect-specify-first-chamfer-distance = RECT 指定第一個倒角距離 <__ocs_fmt_0__>:
.rect-specify-orientation-from-the-first-corner = RECT 從第一個角點指定方向:
.rect-specify-rectangle-area = RECT 指定矩形面積:
.rect-specify-rectangle-length = RECT 指定矩形長度:
.rect-specify-rectangle-width = RECT 指定矩形寬度:
.rect-specify-rotation-angle = RECT 指定旋轉角度 <__ocs_fmt_0__>:
.rect-specify-second-chamfer-distance = RECT 指定第二個倒角距離 <__ocs_fmt_0__>:
.rect-specify-thickness = RECT 指定厚度 <__ocs_fmt_0__>:
.rect-specify-width = RECT 指定寬度 <__ocs_fmt_0__>:
.rect-cen-specify-center-point = RECT CEN 指定中心點:
.rect-cen-specify-corner-point = RECT CEN 指定角點:
.rect-rot-specify-adjacent-corner-defines-edge = RECTROT指定相鄰的角( 定邊方向) :
+14 -4
View File
@@ -668,9 +668,8 @@ impl OpenCADStudio {
true
}
/// Add is delta-safe on an already-existing layer unless it creates block
/// records. RasterImage definitions are captured as exact object-map deltas;
/// Viewport commits explicitly record their added entity handle.
/// Add is delta-safe when its layer and XData application IDs already exist
/// and it creates no block records.
pub(super) fn delta_add_safe(&self, i: usize, entity: &EntityType) -> bool {
if matches!(
entity,
@@ -680,7 +679,18 @@ impl OpenCADStudio {
return false;
}
let layer = entity.common().layer.clone();
layer.trim().is_empty() || self.tabs[i].scene.document.layers.contains(&layer)
let doc = &self.tabs[i].scene.document;
let layer_exists = layer.trim().is_empty() || doc.layers.contains(&layer);
let app_ids_exist = entity
.common()
.extended_data
.records()
.iter()
.all(|record| {
record.application_name.trim().is_empty()
|| doc.app_ids.contains(&record.application_name)
});
layer_exists && app_ids_exist
}
/// Close the delta transaction opened by [`OpenCADStudio::begin_undo`]:
+17
View File
@@ -148,6 +148,18 @@ pub fn format_length(value: f64) -> String {
}
}
/// Format an area with the drawing's linear precision. Area stays a decimal
/// scalar even when linear distances use architectural or fractional notation.
pub fn format_area(value: f64) -> String {
let ctx = unit_context();
let precision = ctx.luprec.max(0).min(15) as usize;
if ctx.lunits == 1 {
format!("{:.*e}", precision, value)
} else {
format!("{:.*}", precision, value)
}
}
/// Format an angle (input in radians) using AUNITS / AUPREC.
pub fn format_angle(value_rad: f64) -> String {
let ctx = unit_context();
@@ -420,6 +432,11 @@ pub fn parse_direction(text: &str) -> Option<f64> {
Some(relative + ctx.angbase)
}
/// Read a direction typed at a command prompt, accepting a decimal comma.
pub fn parse_typed_direction(text: &str) -> Option<f64> {
parse_direction(&text.replace(',', "."))
}
/// Two interior triangles covering a quad (flat list, 6 vertices) — the
/// click-anywhere pick surface for frame-like entities (image, OLE frame,
/// underlay, wipeout). Corners in ring order.
+54 -16
View File
@@ -5,8 +5,8 @@ use crate::t;
use crate::command::EntityTransform;
use crate::entities::common::{
edit_prop as edit, parse_f64, rectangle_grip, ro_prop as ro, square_grip,
stepper_prop as stepper,
edit_prop as edit, edit_scalar_prop as edit_scalar, format_area, format_length, parse_f64,
rectangle_grip, ro_prop as ro, square_grip, stepper_prop as stepper,
};
use crate::entities::traits::RenderConvertible;
use crate::scene::convert::acad_to_render::{extrusion_wall_tris, RenderEntity, RenderObject};
@@ -814,6 +814,33 @@ fn set_revision_cloud_arc_length(pline: &mut LwPolyline, requested: f64) {
pline.vertices = cloud_vertices(&points, magnitude * sign, width_ratios);
}
pub(crate) fn is_rectangle(pline: &LwPolyline) -> bool {
if pline.common.extended_data.get_record("OCS_RECTANGLE").is_some() {
return true;
}
if !pline.is_closed
|| pline.vertices.len() != 4
|| pline.vertices.iter().any(|vertex| vertex.bulge.abs() > 1.0e-9)
{
return false;
}
let edges: Vec<Vec2> = (0..4)
.map(|index| {
let from = pline.vertices[index].location;
let to = pline.vertices[(index + 1) % 4].location;
Vec2::new(to.x - from.x, to.y - from.y)
})
.collect();
let lengths: Vec<f64> = edges.iter().map(|edge| edge.length()).collect();
if lengths.iter().any(|length| *length <= 1.0e-12) {
return false;
}
let tolerance = 1.0e-9;
edges[0].dot(edges[1]).abs() <= tolerance * lengths[0] * lengths[1]
&& edges[0].cross(edges[2]).abs() <= tolerance * lengths[0] * lengths[2]
&& edges[1].cross(edges[3]).abs() <= tolerance * lengths[1] * lengths[3]
}
fn properties(pline: &LwPolyline) -> Vec<PropSection> {
let n = pline.vertices.len();
// The panel's Current Vertex focus, clamped to this polyline's range.
@@ -831,10 +858,8 @@ fn properties(pline: &LwPolyline) -> Vec<PropSection> {
let cloud_arc_length = revision_cloud_arc_length(pline);
let vertex_label = if n == 0 {
"—".to_string()
} else if cloud_arc_length.is_some() {
format!("{}", vi + 1)
} else {
format!("{} / {}", vi + 1, n)
format!("{}", vi + 1)
};
let mut misc_props = vec![
Property {
@@ -861,20 +886,26 @@ fn properties(pline: &LwPolyline) -> Vec<PropSection> {
arc_length,
));
}
let mut geometry_props = vec![
stepper(t!("Current Vertex").as_ref(), "current_vertex", vertex_label),
edit(t!("Vertex X").as_ref(), "vertex_x", vx),
edit(t!("Vertex Y").as_ref(), "vertex_y", vy),
edit_scalar(t!("Bulge").as_ref(), "bulge", v.map_or(0.0, |vertex| vertex.bulge)),
];
if !is_rectangle(pline) {
geometry_props.push(edit(t!("Start segment width").as_ref(), "start_width", start_w));
geometry_props.push(edit(t!("End segment width").as_ref(), "end_width", end_w));
}
geometry_props.extend([
edit(t!("Global width").as_ref(), "global_width", pline.constant_width),
edit(t!("Elevation").as_ref(), "elevation", pline.elevation),
ro(t!("Area").as_ref(), "area", format_area(mp.area)),
ro(t!("Length").as_ref(), "length", format_length(mp.perimeter)),
]);
vec![
PropSection {
title: t!("Geometry").into_owned(),
props: vec![
stepper(t!("Current Vertex").as_ref(), "current_vertex", vertex_label),
edit(t!("Vertex X").as_ref(), "vertex_x", vx),
edit(t!("Vertex Y").as_ref(), "vertex_y", vy),
edit(t!("Start segment width").as_ref(), "start_width", start_w),
edit(t!("End segment width").as_ref(), "end_width", end_w),
edit(t!("Global width").as_ref(), "global_width", pline.constant_width),
edit(t!("Elevation").as_ref(), "elevation", pline.elevation),
ro(t!("Area").as_ref(), "area", format!("{:.4}", mp.area)),
ro(t!("Length").as_ref(), "length", format!("{:.4}", mp.perimeter)),
],
props: geometry_props,
},
PropSection {
title: t!("Misc").into_owned(),
@@ -954,6 +985,13 @@ fn apply_geom_prop(pline: &mut LwPolyline, field: &str, value: &str) {
vtx.end_width = v;
}
}
"bulge" => {
if v.is_finite() {
if let Some(vtx) = pline.vertices.get_mut(vi) {
vtx.bulge = v.clamp(-1.0e6, 1.0e6);
}
}
}
_ => {}
}
}
+13
View File
@@ -428,6 +428,7 @@ pub(super) fn message_attribute(source: &str) -> Option<(&'static str, &'static
"Blocks ({}) — insert with INSERT <name>: {}" => Some(("insert", "blocks-insert-with-insert-name")),
"Blocks ({}): {}" => Some(("insert", "blocks")),
"Blue" => Some(("common", "blue")),
"Bulge" => Some(("common", "bulge")),
"Body" => Some(("properties", "body")),
"Bold" => Some(("properties", "bold")),
"Book" => Some(("properties", "book")),
@@ -2051,8 +2052,20 @@ pub(super) fn message_attribute(source: &str) -> Option<(&'static str, &'static
"RAY Specify start point:" => Some(("draw", "ray-specify-start-point")),
"RAY Specify through point:" => Some(("draw", "ray-specify-through-point")),
"README could not be loaded" => Some(("plugins", "readme-could-not-be-loaded")),
"RECT Calculate dimensions based on [Length / Width] <Length>:" => Some(("draw", "rect-calculate-dimensions-based-on-length-width")),
"RECT Specify elevation <{}>:" => Some(("draw", "rect-specify-elevation")),
"RECT Specify fillet radius <{}>:" => Some(("draw", "rect-specify-fillet-radius")),
"RECT Specify first chamfer distance <{}>:" => Some(("draw", "rect-specify-first-chamfer-distance")),
"RECT Specify first corner:" => Some(("draw", "rect-specify-first-corner")),
"RECT Specify orientation from the first corner:" => Some(("draw", "rect-specify-orientation-from-the-first-corner")),
"RECT Specify opposite corner:" => Some(("draw", "rect-specify-opposite-corner")),
"RECT Specify rectangle area:" => Some(("draw", "rect-specify-rectangle-area")),
"RECT Specify rectangle length:" => Some(("draw", "rect-specify-rectangle-length")),
"RECT Specify rectangle width:" => Some(("draw", "rect-specify-rectangle-width")),
"RECT Specify rotation angle <{}>:" => Some(("draw", "rect-specify-rotation-angle")),
"RECT Specify second chamfer distance <{}>:" => Some(("draw", "rect-specify-second-chamfer-distance")),
"RECT Specify thickness <{}>:" => Some(("draw", "rect-specify-thickness")),
"RECT Specify width <{}>:" => Some(("draw", "rect-specify-width")),
"RECT CEN Specify center point:" => Some(("draw", "rect-cen-specify-center-point")),
"RECT CEN Specify corner point:" => Some(("draw", "rect-cen-specify-corner-point")),
"RECT ROT Specify adjacent corner (defines edge direction):" => Some(("draw", "rect-rot-specify-adjacent-corner-defines-edge")),
+14
View File
@@ -19,6 +19,13 @@ thread_local! {
static ARRAY_P_ANGLE: Cell<f64> = Cell::new(360.0); // degrees
static ARRAY_PATH_COUNT: Cell<f64> = Cell::new(6.0);
static POLYGON_SIDES: Cell<f64> = Cell::new(6.0);
static RECT_ELEVATION: Cell<f64> = Cell::new(0.0);
static RECT_THICKNESS: Cell<f64> = Cell::new(0.0);
static RECT_WIDTH: Cell<f64> = Cell::new(0.0);
static RECT_ROTATION: Cell<f64> = Cell::new(0.0); // degrees
static RECT_CHAMFER1: Cell<f64> = Cell::new(0.0);
static RECT_CHAMFER2: Cell<f64> = Cell::new(0.0);
static RECT_FILLET: Cell<f64> = Cell::new(0.0);
}
macro_rules! accessors {
@@ -54,3 +61,10 @@ accessors!(get_array_p_count, set_array_p_count, ARRAY_P_COUNT);
accessors!(get_array_p_angle, set_array_p_angle, ARRAY_P_ANGLE);
accessors!(get_array_path_count, set_array_path_count, ARRAY_PATH_COUNT);
accessors!(get_polygon_sides, set_polygon_sides, POLYGON_SIDES);
accessors!(get_rect_elevation, set_rect_elevation, RECT_ELEVATION);
accessors!(get_rect_thickness, set_rect_thickness, RECT_THICKNESS);
accessors!(get_rect_width, set_rect_width, RECT_WIDTH);
accessors!(get_rect_rotation, set_rect_rotation, RECT_ROTATION);
accessors!(get_rect_chamfer1, set_rect_chamfer1, RECT_CHAMFER1);
accessors!(get_rect_chamfer2, set_rect_chamfer2, RECT_CHAMFER2);
accessors!(get_rect_fillet, set_rect_fillet, RECT_FILLET);
+636 -59
View File
@@ -13,6 +13,11 @@
use acadrust::entities::LwVertex;
use acadrust::types::Vector2;
use acadrust::{EntityType, LwPolyline};
use cadkernel::geom2d::{
arc_span, fillet_between_rays, Curve as KernelCurve, Frame as KernelFrame,
Polyline as KernelPolyline, PolylineVertex as KernelVertex, Ray as KernelRay,
Transform as KernelTransform, Vec2 as KernelVec2,
};
use crate::t;
use crate::command::{CadCommand, CmdResult, WorkingPlane};
@@ -21,21 +26,6 @@ use crate::modules::IconKind;
use crate::scene::model::wire_model::WireModel;
use glam::DVec3;
/// Build the four corners of an axis-aligned box between opposite corners `a`
/// and `b`, axis-aligned in the active UCS (`ucs` = UCS→wire affine, identity =
/// world). The two given corners stay put; the other two are placed square to
/// the UCS axes instead of the world axes.
fn ucs_box_corners(a: DVec3, b: DVec3, plane: WorkingPlane) -> [DVec3; 4] {
let au = plane.to_local(a);
let bu = plane.to_local(b);
[
plane.to_world(DVec3::new(au.x, au.y, au.z)),
plane.to_world(DVec3::new(bu.x, au.y, au.z)),
plane.to_world(DVec3::new(bu.x, bu.y, au.z)),
plane.to_world(DVec3::new(au.x, bu.y, au.z)),
]
}
/// Four corners of a box centred at `c` with half-extents taken from `corner`,
/// axis-aligned in the active UCS (`ucs` = UCS→wire affine, identity = world).
fn ucs_box_around_center(c: DVec3, corner: DVec3, plane: WorkingPlane) -> [DVec3; 4] {
@@ -96,6 +86,235 @@ fn make_pline(points: &[DVec3], plane: WorkingPlane) -> EntityType {
}))
}
#[derive(Clone, Copy)]
struct RectStyle {
chamfer_first: f64,
chamfer_second: f64,
fillet_radius: f64,
width: f64,
thickness: f64,
}
fn rectangle_corners(
first: DVec3,
cursor: DVec3,
plane: WorkingPlane,
rotation_deg: f64,
fixed_dimensions: Option<(f64, f64)>,
) -> Option<[DVec3; 4]> {
let first_local = plane.to_local(first);
let cursor_local = plane.to_local(cursor);
if !rotation_deg.is_finite()
|| !first_local.is_finite()
|| !cursor_local.is_finite()
{
return None;
}
let rotation = KernelTransform::rotation(rotation_deg.to_radians());
let axis_x = KernelVec2::from(rotation.apply_vector([1.0, 0.0]));
let axis_y = KernelVec2::from(rotation.apply_vector([0.0, 1.0]));
let first_2d = KernelVec2::new(first_local.x, first_local.y);
let delta = KernelVec2::new(
cursor_local.x - first_local.x,
cursor_local.y - first_local.y,
);
let raw_width = delta.dot(axis_x);
let raw_height = delta.dot(axis_y);
let (width, height) = fixed_dimensions.map_or((raw_width, raw_height), |(w, h)| {
(w.copysign(raw_width), h.copysign(raw_height))
});
if !width.is_finite()
|| !height.is_finite()
|| width.abs() <= 1.0e-9
|| height.abs() <= 1.0e-9
{
return None;
}
let local = [
first_2d,
first_2d + axis_x * width,
first_2d + axis_x * width + axis_y * height,
first_2d + axis_y * height,
];
Some(local.map(|point| {
plane.to_world(DVec3::new(point.x, point.y, first_local.z))
}))
}
fn rectangle_polyline(
corners: [DVec3; 4],
plane: WorkingPlane,
style: RectStyle,
) -> Option<(KernelPolyline, f64)> {
if !style.chamfer_first.is_finite()
|| !style.chamfer_second.is_finite()
|| !style.fillet_radius.is_finite()
|| !style.width.is_finite()
|| !style.thickness.is_finite()
|| style.chamfer_first < 0.0
|| style.chamfer_second < 0.0
|| style.fillet_radius < 0.0
|| style.width < 0.0
{
return None;
}
let local = corners.map(|point| plane.to_local(point));
let elevation = local[0].z;
let frame_points = local.map(|point| [point.x, point.y, 0.0]);
let frame = KernelFrame::around(frame_points.iter());
let points = frame_points.map(|point| {
let lifted = frame.lift(point);
KernelVec2::new(lifted[0], lifted[1])
});
let use_fillet = style.fillet_radius > 1.0e-9;
let use_chamfer = !use_fillet
&& (style.chamfer_first > 1.0e-9 || style.chamfer_second > 1.0e-9);
if !use_fillet && !use_chamfer {
return Some((
KernelPolyline {
vertices: local
.iter()
.map(|point| KernelVertex::straight([point.x, point.y]))
.collect(),
closed: true,
},
elevation,
));
}
let mut trims = Vec::with_capacity(4);
for index in 0..4 {
let corner = points[index];
let previous = points[(index + 3) % 4];
let next = points[(index + 1) % 4];
let incoming = (previous - corner).normalize()?;
let outgoing = (next - corner).normalize()?;
let (incoming_point, outgoing_point, bulge) = if use_fillet {
let fillet = fillet_between_rays(
corner.to_array(),
incoming.to_array(),
outgoing.to_array(),
style.fillet_radius,
)?;
let sweep = arc_span(fillet.start_angle, fillet.end_angle);
(
KernelVec2::from(fillet.tangent1),
KernelVec2::from(fillet.tangent2),
(sweep * 0.25)
.tan()
.copysign(-incoming.cross(outgoing)),
)
} else {
let incoming_ray = KernelCurve::Ray(KernelRay {
origin: corner.to_array(),
direction: incoming.to_array(),
});
let outgoing_ray = KernelCurve::Ray(KernelRay {
origin: corner.to_array(),
direction: outgoing.to_array(),
});
let incoming_point = incoming_ray.point_at(style.chamfer_first);
let outgoing_point = outgoing_ray.point_at(style.chamfer_second);
(
KernelVec2::from(incoming_point),
KernelVec2::from(outgoing_point),
0.0,
)
};
let incoming_distance = corner.distance(incoming_point);
let outgoing_distance = corner.distance(outgoing_point);
if !incoming_point.x.is_finite()
|| !incoming_point.y.is_finite()
|| !outgoing_point.x.is_finite()
|| !outgoing_point.y.is_finite()
|| !incoming_distance.is_finite()
|| !outgoing_distance.is_finite()
|| !bulge.is_finite()
{
return None;
}
trims.push((
incoming_point,
outgoing_point,
incoming_distance,
outgoing_distance,
bulge,
));
}
for index in 0..4 {
let next = (index + 1) % 4;
let side_length = points[index].distance(points[next]);
let used = trims[index].3 + trims[next].2;
if used + side_length * 1.0e-9 >= side_length {
return None;
}
}
let mut vertices = Vec::with_capacity(8);
for (incoming_point, outgoing_point, _, _, bulge) in trims {
let incoming = frame.lower([incoming_point.x, incoming_point.y, 0.0]);
let outgoing = frame.lower([outgoing_point.x, outgoing_point.y, 0.0]);
vertices.push(KernelVertex::curved([incoming[0], incoming[1]], bulge));
vertices.push(KernelVertex::straight([outgoing[0], outgoing[1]]));
}
Some((
KernelPolyline {
vertices,
closed: true,
},
elevation,
))
}
fn make_rect_pline(
corners: [DVec3; 4],
plane: WorkingPlane,
style: RectStyle,
) -> Option<EntityType> {
let (geometry, elevation) = rectangle_polyline(corners, plane, style)?;
let mut polyline = LwPolyline {
vertices: geometry
.vertices
.iter()
.map(|point| {
let mut vertex =
LwVertex::new(Vector2::new(point.position[0], point.position[1]));
vertex.bulge = point.bulge;
vertex
})
.collect(),
elevation,
is_closed: true,
constant_width: style.width,
thickness: style.thickness,
..Default::default()
};
let mut marker = acadrust::xdata::ExtendedDataRecord::new("OCS_RECTANGLE");
marker.add_value(acadrust::xdata::XDataValue::Integer16(1));
polyline.common.extended_data.add_record(marker);
Some(plane.place_entity(EntityType::LwPolyline(polyline)))
}
fn rectangle_wire(
corners: [DVec3; 4],
plane: WorkingPlane,
style: RectStyle,
) -> Option<WireModel> {
let (polyline, elevation) = rectangle_polyline(corners, plane, style)?;
let mut points = KernelCurve::Polyline(polyline).tessellate(8.0);
if points.len() > 1 && points.first() == points.last() {
points.pop();
}
let points = points
.into_iter()
.map(|point| {
let world = plane.to_world(DVec3::new(point[0], point[1], elevation));
[world.x, world.y, world.z]
})
.collect();
Some(wire_loop(points))
}
fn wire_loop(pts: Vec<[f64; 3]>) -> WireModel {
let mut p = pts;
if let Some(&first) = p.first() {
@@ -155,18 +374,81 @@ fn plane_distance(from: DVec3, to: DVec3, plane: WorkingPlane) -> f64 {
// ── Command: Rectangle — Two Corners (RECT) ──────────────────────────────
#[derive(Clone, Copy)]
enum RectStep {
FirstCorner,
Opposite,
ChamferFirst,
ChamferSecond,
Elevation,
Fillet,
Thickness,
Width,
Rotation,
AreaValue,
AreaBasis(f64),
AreaDimension { area: f64, by_length: bool },
DimensionsLength,
DimensionsWidth(f64),
PlaceSized { width: f64, height: f64 },
}
pub struct RectCommand {
a: Option<DVec3>,
step: RectStep,
first: Option<DVec3>,
plane: WorkingPlane,
chamfer_first: f64,
chamfer_second: f64,
fillet_radius: f64,
elevation: f64,
thickness: f64,
width: f64,
rotation_deg: f64,
}
impl RectCommand {
pub fn new() -> Self {
Self {
a: None,
step: RectStep::FirstCorner,
first: None,
plane: WorkingPlane::default(),
chamfer_first: defaults::get_rect_chamfer1().max(0.0),
chamfer_second: defaults::get_rect_chamfer2().max(0.0),
fillet_radius: defaults::get_rect_fillet().max(0.0),
elevation: defaults::get_rect_elevation(),
thickness: defaults::get_rect_thickness(),
width: defaults::get_rect_width().max(0.0),
rotation_deg: defaults::get_rect_rotation(),
}
}
fn style(&self) -> RectStyle {
RectStyle {
chamfer_first: self.chamfer_first,
chamfer_second: self.chamfer_second,
fillet_radius: self.fillet_radius,
width: self.width,
thickness: self.thickness,
}
}
fn finish(&self, cursor: DVec3, fixed_dimensions: Option<(f64, f64)>) -> CmdResult {
let Some(first) = self.first else {
return CmdResult::NeedPoint;
};
let Some(corners) = rectangle_corners(
first,
cursor,
self.plane,
self.rotation_deg,
fixed_dimensions,
) else {
return CmdResult::NeedPoint;
};
make_rect_pline(corners, self.plane, self.style())
.map(CmdResult::CommitAndExit)
.unwrap_or(CmdResult::NeedPoint)
}
}
impl CadCommand for RectCommand {
@@ -177,78 +459,363 @@ impl CadCommand for RectCommand {
self.plane = plane;
}
fn prompt(&self) -> String {
if self.a.is_none() {
crate::t!("RECT Specify first corner:").into_owned()
} else {
crate::t!("RECT Specify opposite corner:").into_owned()
match self.step {
RectStep::FirstCorner => crate::t!("RECT Specify first corner:").into_owned(),
RectStep::Opposite => crate::t!("RECT Specify opposite corner:").into_owned(),
RectStep::ChamferFirst => crate::tf!(
"RECT Specify first chamfer distance <{}>:",
crate::entities::common::format_length(self.chamfer_first)
)
.into_owned(),
RectStep::ChamferSecond => crate::tf!(
"RECT Specify second chamfer distance <{}>:",
crate::entities::common::format_length(self.chamfer_second)
)
.into_owned(),
RectStep::Elevation => {
crate::tf!(
"RECT Specify elevation <{}>:",
crate::entities::common::format_length(self.elevation)
)
.into_owned()
}
RectStep::Fillet => {
crate::tf!(
"RECT Specify fillet radius <{}>:",
crate::entities::common::format_length(self.fillet_radius)
)
.into_owned()
}
RectStep::Thickness => {
crate::tf!(
"RECT Specify thickness <{}>:",
crate::entities::common::format_length(self.thickness)
)
.into_owned()
}
RectStep::Width => crate::tf!(
"RECT Specify width <{}>:",
crate::entities::common::format_length(self.width)
)
.into_owned(),
RectStep::Rotation => {
crate::tf!(
"RECT Specify rotation angle <{}>:",
crate::entities::common::format_direction(self.rotation_deg.to_radians())
)
.into_owned()
}
RectStep::AreaValue => crate::t!("RECT Specify rectangle area:").into_owned(),
RectStep::AreaBasis(_) => crate::t!(
"RECT Calculate dimensions based on [Length / Width] <Length>:"
)
.into_owned(),
RectStep::AreaDimension { by_length: true, .. } => {
crate::t!("RECT Specify rectangle length:").into_owned()
}
RectStep::AreaDimension { by_length: false, .. } => {
crate::t!("RECT Specify rectangle width:").into_owned()
}
RectStep::DimensionsLength => {
crate::t!("RECT Specify rectangle length:").into_owned()
}
RectStep::DimensionsWidth(_) => {
crate::t!("RECT Specify rectangle width:").into_owned()
}
RectStep::PlaceSized { .. } => crate::t!(
"RECT Specify orientation from the first corner:"
)
.into_owned(),
}
}
fn options(&self) -> Vec<crate::command::CmdOption> {
use crate::command::CmdOption;
// First corner also offers the alternate rectangle methods; later steps
// are plain point picks. (#304)
if self.a.is_none() {
vec![
match self.step {
RectStep::FirstCorner => vec![
CmdOption::new("Chamfer", "CHAMFER"),
CmdOption::new("Elevation", "ELEVATION"),
CmdOption::new("Fillet", "FILLET"),
CmdOption::new("Thickness", "THICKNESS"),
CmdOption::new("Width", "WIDTH"),
],
RectStep::Opposite => vec![
CmdOption::new("Area", "AREA"),
CmdOption::new("Dimensions", "DIMENSIONS"),
CmdOption::new("Rotation", "ROTATION"),
CmdOption::new("Center", "CENTER"),
]
} else {
vec![]
],
RectStep::AreaBasis(_) => vec![
CmdOption::new("Length", "LENGTH"),
CmdOption::new("Width", "WIDTH"),
],
_ => vec![],
}
}
fn point_step_accepts_keywords(&self) -> bool {
self.a.is_none()
matches!(self.step, RectStep::FirstCorner | RectStep::Opposite)
}
fn wants_text_input(&self) -> bool {
!matches!(self.step, RectStep::PlaceSized { .. })
}
fn dyn_field(&self) -> crate::command::DynField {
match self.step {
RectStep::Rotation => crate::command::DynField::Angle,
RectStep::ChamferFirst
| RectStep::ChamferSecond
| RectStep::Elevation
| RectStep::Fillet
| RectStep::Thickness
| RectStep::Width
| RectStep::AreaValue
| RectStep::AreaBasis(_)
| RectStep::AreaDimension { .. }
| RectStep::DimensionsLength
| RectStep::DimensionsWidth(_) => crate::command::DynField::Scalar,
_ => crate::command::DynField::Point,
}
}
fn on_text_input(&mut self, text: &str) -> Option<CmdResult> {
// At the first corner, keyword options hand off to the dedicated
// variant command. (#304)
if self.a.is_none() {
return match text.trim().to_uppercase().as_str() {
"R" | "ROTATION" => Some(CmdResult::Dispatch("RECT_ROT".into())),
"C" | "CENTER" => Some(CmdResult::Dispatch("RECT_CEN".into())),
_ => None,
};
let upper = text.trim().to_uppercase();
match self.step {
RectStep::FirstCorner => {
return match upper.as_str() {
"C" | "CHAMFER" => {
self.step = RectStep::ChamferFirst;
Some(CmdResult::NeedPoint)
}
"E" | "ELEVATION" => {
self.step = RectStep::Elevation;
Some(CmdResult::NeedPoint)
}
"F" | "FILLET" => {
self.step = RectStep::Fillet;
Some(CmdResult::NeedPoint)
}
"T" | "THICKNESS" => {
self.step = RectStep::Thickness;
Some(CmdResult::NeedPoint)
}
"W" | "WIDTH" => {
self.step = RectStep::Width;
Some(CmdResult::NeedPoint)
}
_ => None,
};
}
RectStep::Opposite => {
return match upper.as_str() {
"A" | "AREA" => {
self.step = RectStep::AreaValue;
Some(CmdResult::NeedPoint)
}
"D" | "DIMENSIONS" => {
self.step = RectStep::DimensionsLength;
Some(CmdResult::NeedPoint)
}
"R" | "ROTATION" => {
self.step = RectStep::Rotation;
Some(CmdResult::NeedPoint)
}
_ => None,
};
}
RectStep::AreaBasis(area) => {
return match upper.as_str() {
"L" | "LENGTH" => {
self.step = RectStep::AreaDimension {
area,
by_length: true,
};
Some(CmdResult::NeedPoint)
}
"W" | "WIDTH" => {
self.step = RectStep::AreaDimension {
area,
by_length: false,
};
Some(CmdResult::NeedPoint)
}
_ => None,
};
}
_ => {}
}
None
if matches!(self.step, RectStep::Rotation) {
let angle = crate::entities::common::parse_typed_direction(text)?;
if !angle.is_finite() {
return None;
}
self.rotation_deg = angle.to_degrees();
defaults::set_rect_rotation(self.rotation_deg);
self.step = RectStep::Opposite;
return Some(CmdResult::NeedPoint);
}
let value = if matches!(self.step, RectStep::AreaValue) {
text.trim().replace(',', ".").parse().ok()?
} else {
crate::entities::common::parse_typed_length(text)?
};
if !value.is_finite() {
return None;
}
match self.step {
RectStep::ChamferFirst if value >= 0.0 => {
self.chamfer_first = value;
defaults::set_rect_chamfer1(value);
self.step = RectStep::ChamferSecond;
}
RectStep::ChamferSecond if value >= 0.0 => {
self.chamfer_second = value;
self.fillet_radius = 0.0;
defaults::set_rect_chamfer2(value);
defaults::set_rect_fillet(0.0);
self.step = RectStep::FirstCorner;
}
RectStep::Elevation => {
self.elevation = value;
defaults::set_rect_elevation(value);
self.step = RectStep::FirstCorner;
}
RectStep::Fillet if value >= 0.0 => {
self.fillet_radius = value;
self.chamfer_first = 0.0;
self.chamfer_second = 0.0;
defaults::set_rect_fillet(value);
defaults::set_rect_chamfer1(0.0);
defaults::set_rect_chamfer2(0.0);
self.step = RectStep::FirstCorner;
}
RectStep::Thickness => {
self.thickness = value;
defaults::set_rect_thickness(value);
self.step = RectStep::FirstCorner;
}
RectStep::Width if value >= 0.0 => {
self.width = value;
defaults::set_rect_width(value);
self.step = RectStep::FirstCorner;
}
RectStep::AreaValue if value > 0.0 => {
self.step = RectStep::AreaBasis(value);
}
RectStep::AreaDimension { area, by_length } if value > 0.0 => {
let (width, height) = if by_length {
(value, area / value)
} else {
(area / value, value)
};
if !width.is_finite() || !height.is_finite() {
return None;
}
self.step = RectStep::PlaceSized { width, height };
}
RectStep::DimensionsLength if value > 0.0 => {
self.step = RectStep::DimensionsWidth(value);
}
RectStep::DimensionsWidth(length) if value > 0.0 => {
self.step = RectStep::PlaceSized {
width: length,
height: value,
};
}
_ => return None,
}
Some(CmdResult::NeedPoint)
}
fn on_point(&mut self, pt: DVec3) -> CmdResult {
match self.a {
None => {
self.a = Some(pt);
match self.step {
RectStep::FirstCorner => {
let mut local = self.plane.to_local(pt);
local.z = self.elevation;
self.first = Some(self.plane.to_world(local));
self.step = RectStep::Opposite;
CmdResult::NeedPoint
}
Some(a) => {
let c = ucs_box_corners(a, pt, self.plane);
CmdResult::CommitAndExit(make_pline(&c, self.plane))
RectStep::Opposite => self.finish(pt, None),
RectStep::PlaceSized { width, height } => {
self.finish(pt, Some((width, height)))
}
RectStep::Rotation => {
let Some(first) = self.first else {
return CmdResult::NeedPoint;
};
let delta = self.plane.vector_to_local(pt - first);
if delta.x.hypot(delta.y) <= 1.0e-9 {
return CmdResult::NeedPoint;
}
self.rotation_deg = delta.y.atan2(delta.x).to_degrees();
defaults::set_rect_rotation(self.rotation_deg);
self.step = RectStep::Opposite;
CmdResult::NeedPoint
}
_ => CmdResult::NeedPoint,
}
}
fn on_enter(&mut self) -> CmdResult {
CmdResult::Cancel
match self.step {
RectStep::FirstCorner => CmdResult::Cancel,
RectStep::ChamferFirst => {
self.step = RectStep::ChamferSecond;
CmdResult::NeedPoint
}
RectStep::ChamferSecond => {
self.fillet_radius = 0.0;
defaults::set_rect_fillet(0.0);
self.step = RectStep::FirstCorner;
CmdResult::NeedPoint
}
RectStep::Elevation
| RectStep::Fillet
| RectStep::Thickness
| RectStep::Width => {
self.step = RectStep::FirstCorner;
CmdResult::NeedPoint
}
RectStep::Rotation => {
self.step = RectStep::Opposite;
CmdResult::NeedPoint
}
RectStep::AreaBasis(area) => {
self.step = RectStep::AreaDimension {
area,
by_length: true,
};
CmdResult::NeedPoint
}
_ => CmdResult::NeedPoint,
}
}
fn on_escape(&mut self) -> CmdResult {
CmdResult::Cancel
}
fn on_mouse_move(&mut self, pt: DVec3) -> Option<WireModel> {
let a = self.a?;
let c = ucs_box_corners(a, pt, self.plane);
Some(wire_loop(vec![
[c[0].x, c[0].y, c[0].z],
[c[1].x, c[1].y, c[1].z],
[c[2].x, c[2].y, c[2].z],
[c[3].x, c[3].y, c[3].z],
]))
let first = self.first?;
let fixed_dimensions = match self.step {
RectStep::PlaceSized { width, height } => Some((width, height)),
RectStep::Opposite => None,
_ => return None,
};
let corners = rectangle_corners(
first,
pt,
self.plane,
self.rotation_deg,
fixed_dimensions,
)?;
rectangle_wire(corners, self.plane, self.style())
}
fn dyn_spec(&self) -> Option<crate::command::DynSpec> {
use crate::command::{DynAnchor, DynFieldSpec, DynGuide, DynRole, DynSpec};
// Opposite corner: enter width and height (signed deltas from the first
// corner), with the rectangle drawn as the guide. First corner is a
// normal point pick.
self.a.map(|a| DynSpec {
anchor: DynAnchor::Point(a),
if !matches!(self.step, RectStep::Opposite) || self.rotation_deg.abs() > 1.0e-9 {
return None;
}
self.first.map(|first| DynSpec {
anchor: DynAnchor::Point(first),
fields: vec![
DynFieldSpec::new(DynRole::Width),
DynFieldSpec::new(DynRole::Height),
@@ -305,6 +872,9 @@ impl CadCommand for RectRotCommand {
CmdResult::NeedPoint
}
1 => {
if plane_distance(self.a, pt, self.plane) <= 1.0e-9 {
return CmdResult::NeedPoint;
}
self.b = pt;
self.step = 2;
CmdResult::NeedPoint
@@ -318,6 +888,9 @@ impl CadCommand for RectRotCommand {
let dir = (b - a).normalize_or_zero();
let perp = DVec3::new(-dir.y, dir.x, 0.0);
let h = (pt - b).dot(perp); // signed height
if h.abs() <= 1.0e-9 {
return CmdResult::NeedPoint;
}
let c = b + perp * h;
let d = a + perp * h;
let corners = [a, b, c, d].map(|point| self.plane.to_world(point));
@@ -420,6 +993,10 @@ impl CadCommand for RectCenCommand {
CmdResult::NeedPoint
}
Some(c) => {
let delta = self.plane.vector_to_local(pt - c);
if delta.x.abs() <= 1.0e-9 || delta.y.abs() <= 1.0e-9 {
return CmdResult::NeedPoint;
}
let q = ucs_box_around_center(c, pt, self.plane);
CmdResult::CommitAndExit(make_pline(&q, self.plane))
}
+4
View File
@@ -91,6 +91,10 @@ pub fn general_section(entity: &EntityType) -> PropSection {
],
};
if matches!(entity, EntityType::LwPolyline(polyline) if crate::entities::lwpolyline::is_rectangle(polyline)) {
section.props.retain(|prop| prop.field != "handle");
}
if matches!(entity, EntityType::Point(_)) {
section.props.retain(|prop| prop.field != "handle");
let hyperlink = section.props.iter().position(|prop| prop.field == "hyperlink");
+25 -4
View File
@@ -134,6 +134,15 @@ impl Scene {
let _ = self.document.layers.add(layer);
}
fn ensure_app_id(&mut self, name: &str) {
if name.trim().is_empty() || self.document.app_ids.contains(name) {
return;
}
let mut app_id = acadrust::tables::AppId::new(name);
app_id.handle = self.document.allocate_handle();
let _ = self.document.app_ids.add(app_id);
}
pub fn add_entity(&mut self, entity: EntityType) -> Handle {
self.add_entity_internal(entity, true)
}
@@ -256,6 +265,20 @@ impl Scene {
let creates_layer =
self.is_recording_undo() && !layer.trim().is_empty() && !self.document.layers.contains(&layer);
self.ensure_layer(&layer);
let app_ids: Vec<String> = entity
.common()
.extended_data
.records()
.iter()
.map(|record| record.application_name.clone())
.collect();
let creates_app_id = self.is_recording_undo()
&& app_ids.iter().any(|name| {
!name.trim().is_empty() && !self.document.app_ids.contains(name)
});
for name in &app_ids {
self.ensure_app_id(name);
}
// Route to the correct block based on current editing mode:
// - BEDIT block editor: geometry belongs to the edited block record,
@@ -308,12 +331,10 @@ impl Scene {
}
// Delta-undo: the new handle's before-image is "nothing" (it did not
// exist). Poison the recording if this add also mutated non-entity
// state (a new layer / block) so the app knows a pure-entity delta
// would be incomplete. Raster image definitions are captured as
// targeted object before-images above.
// state (a new layer, application ID, or block).
if self.is_recording_undo() {
self.record_undo_before(handle, None);
if creates_layer || mutates_block_structure {
if creates_layer || creates_app_id || mutates_block_structure {
self.poison_undo_recording();
}
}