feat: complete freehand sketch workflow
Co-authored-by: ramox81 <184937705+ramox81@users.noreply.github.com>
This commit is contained in:
commit
464971969c
32 changed files with 922 additions and 109 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
|
@ -72,7 +72,7 @@ checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618"
|
|||
[[package]]
|
||||
name = "acadrust"
|
||||
version = "0.4.1"
|
||||
source = "git+https://github.com/HakanSeven12/cadcodec.git?rev=cf46d04#cf46d047211d1adcb37a079812bc38baa45c98f6"
|
||||
source = "git+https://github.com/HakanSeven12/cadcodec.git?rev=0908da7#0908da7b6e4f702a6c78359a57f53e2b79cf39eb"
|
||||
dependencies = [
|
||||
"ahash 0.8.12",
|
||||
"anyhow",
|
||||
|
|
@ -878,7 +878,7 @@ checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
|
|||
[[package]]
|
||||
name = "cadkernel"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/HakanSeven12/cadkernel.git?rev=b3f3122#b3f3122176e3f3860dd3bbc23f12c92b56f06095"
|
||||
source = "git+https://github.com/HakanSeven12/cadkernel.git?rev=ebeb2ec#ebeb2ecc2d17d92c588b0fa8bbe3864b156bc71f"
|
||||
dependencies = [
|
||||
"acadrust",
|
||||
"cavalier_contours",
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@ glam = { version = "0.33", features = ["bytemuck"] }
|
|||
rfd = "0.17"
|
||||
clap = { version = "4", features = ["derive"] }
|
||||
env_logger = "0.11"
|
||||
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "cf46d04", features = ["serde"] }
|
||||
cadkernel = { git = "https://github.com/HakanSeven12/cadkernel.git", rev = "b3f3122", features = ["acis", "offset"] }
|
||||
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "0908da7", features = ["serde"] }
|
||||
cadkernel = { git = "https://github.com/HakanSeven12/cadkernel.git", rev = "ebeb2ec", features = ["acis", "offset"] }
|
||||
dwg-thumbnailer = { path = "crates/dwg-thumbnailer" }
|
||||
flate2 = "1"
|
||||
image = { version = "0.25", default-features = false, features = ["png", "jpeg", "bmp", "tiff"] }
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ serde = { version = "1", features = ["derive"] }
|
|||
# Pulled in only by the `host` feature, which adds the `acadrust`-typed
|
||||
# `HostApi` runtime surface. The default crate stays dependency-free so engine
|
||||
# crates and external tooling can depend on the manifest/ribbon contract cheaply.
|
||||
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "cf46d04", optional = true, features = ["serde"] }
|
||||
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "0908da7", optional = true, features = ["serde"] }
|
||||
|
||||
# Runtime IPC and serialization (host feature only).
|
||||
interprocess = { version = "2", optional = true }
|
||||
|
|
@ -37,7 +37,7 @@ serde_json = "1"
|
|||
serde = { version = "1", features = ["derive"] }
|
||||
cargo-lock = "11"
|
||||
# acadrust is scanned at build time to generate the embedded type registry.
|
||||
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "cf46d04", features = ["serde"] }
|
||||
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "0908da7", features = ["serde"] }
|
||||
|
||||
[dev-dependencies]
|
||||
serde_json = "1"
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ publish = false
|
|||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "cf46d04", features = ["serde"] }
|
||||
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "0908da7", features = ["serde"] }
|
||||
bincode = "1.3"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
console_error_panic_hook = "0.1"
|
||||
|
|
|
|||
|
|
@ -1385,6 +1385,19 @@ draw =
|
|||
.section-offset-along-z = SECTION الإزاحة على Z:
|
||||
.sketch-pen-down-move-to-sketch-click-to-lift-e = SKETCH القلم لأسفل — حرّك للرسم، انقر لرفعه، Enter للتسجيل:
|
||||
.sketch-pen-up-click-to-lower-the-pen-enter-to = SKETCH القلم لأعلى — انقر لإنزاله، Enter للتسجيل:
|
||||
.sketch-pen = القلم
|
||||
.sketch-increment = الزيادة
|
||||
.sketch-record = تسجيل
|
||||
.sketch-stop-erasing = إيقاف المسح
|
||||
.sketch-connect = توصيل
|
||||
.sketch-exit = خروج
|
||||
.sketch-quit = إلغاء
|
||||
.sketch-object-type = SKETCH نوع الكائن [خط/متعدد الخطوط/Spline]:
|
||||
.sketch-record-increment = SKETCH زيادة التسجيل <__ocs_arg_value__>:
|
||||
.sketch-spline-fit-tolerance = SKETCH تفاوت ملاءمة منحنى Spline <__ocs_arg_value__>:
|
||||
.sketch-erase-move-backward = SKETCH المسح — تحرّك للخلف فوق الرسم المؤقت:
|
||||
.sketch-pen-down-move = SKETCH (__ocs_arg_type__، الزيادة __ocs_arg_increment__) القلم لأسفل — حرّك للرسم:
|
||||
.sketch-pen-up-click = SKETCH (__ocs_arg_type__، الزيادة __ocs_arg_increment__) القلم لأعلى — انقر لإنزاله:
|
||||
.slice-cutting-plane-axis-x-y-z-add-top-bottom = SLICE محور مستوى القطع [X / Y / Z] (أضف TOP/BOTTOM بالكتابة):
|
||||
.slice-offset-along-x = SLICE الإزاحة على X:
|
||||
.slice-offset-along-y = SLICE الإزاحة على Y:
|
||||
|
|
|
|||
|
|
@ -1372,6 +1372,19 @@ draw =
|
|||
.section-offset-along-z = SECTION отместване по Z:
|
||||
.sketch-pen-down-move-to-sketch-click-to-lift-e = SKETCH Перо долу — премести за скициране, кликни за вдигане, Enter за записване:
|
||||
.sketch-pen-up-click-to-lower-the-pen-enter-to = SKETCH Перо горе — кликни за сваляне на перото, Enter за записване:
|
||||
.sketch-pen = Перо
|
||||
.sketch-increment = Стъпка
|
||||
.sketch-record = Запис
|
||||
.sketch-stop-erasing = Спри изтриването
|
||||
.sketch-connect = Свържи
|
||||
.sketch-exit = Изход
|
||||
.sketch-quit = Отказ
|
||||
.sketch-object-type = SKETCH Тип обект [Линия/Полилиния/Spline]:
|
||||
.sketch-record-increment = SKETCH Стъпка на запис <__ocs_arg_value__>:
|
||||
.sketch-spline-fit-tolerance = SKETCH Толеранс на напасване на Spline <__ocs_arg_value__>:
|
||||
.sketch-erase-move-backward = SKETCH Изтриване — движете се назад върху временната скица:
|
||||
.sketch-pen-down-move = SKETCH (__ocs_arg_type__, стъпка __ocs_arg_increment__) Перо долу — движете за скициране:
|
||||
.sketch-pen-up-click = SKETCH (__ocs_arg_type__, стъпка __ocs_arg_increment__) Перо горе — кликнете за сваляне:
|
||||
.slice-cutting-plane-axis-x-y-z-add-top-bottom = SLICE ос на режещата равнина [X / Y / Z] (добави TOP/BOTTOM чрез въвеждане):
|
||||
.slice-offset-along-x = SLICE отместване по X:
|
||||
.slice-offset-along-y = SLICE отместване по Y:
|
||||
|
|
|
|||
|
|
@ -1372,6 +1372,19 @@ draw =
|
|||
.section-offset-along-z = Odsazení SECTION podél Z:
|
||||
.sketch-pen-down-move-to-sketch-click-to-lift-e = SKETCH Perem dolů – pohybem na skicu, kliknutím zvedněte, Enter zaznamenáte:
|
||||
.sketch-pen-up-click-to-lower-the-pen-enter-to = SKETCH Pero nahoru – kliknutím spustíte pero, Enter zaznamenáte:
|
||||
.sketch-pen = Pero
|
||||
.sketch-increment = Přírůstek
|
||||
.sketch-record = Zaznamenat
|
||||
.sketch-stop-erasing = Zastavit mazání
|
||||
.sketch-connect = Připojit
|
||||
.sketch-exit = Ukončit
|
||||
.sketch-quit = Zahodit
|
||||
.sketch-object-type = SKETCH Typ objektu [Úsečka/Polylinie/Spline]:
|
||||
.sketch-record-increment = SKETCH Krok záznamu <__ocs_arg_value__>:
|
||||
.sketch-spline-fit-tolerance = SKETCH Tolerance proložení spline <__ocs_arg_value__>:
|
||||
.sketch-erase-move-backward = SKETCH Mazání — pohybujte se zpět po dočasné skice:
|
||||
.sketch-pen-down-move = SKETCH (__ocs_arg_type__, krok __ocs_arg_increment__) Pero dole — kreslete pohybem:
|
||||
.sketch-pen-up-click = SKETCH (__ocs_arg_type__, krok __ocs_arg_increment__) Pero nahoře — kliknutím jej spusťte:
|
||||
.slice-cutting-plane-axis-x-y-z-add-top-bottom = SLICE osa řezné roviny [X / Y / Z] (přidejte TOP/BOTTOM zadáním):
|
||||
.slice-offset-along-x = Odsazení SLICE podél X:
|
||||
.slice-offset-along-y = Odsazení SLICE podél Y:
|
||||
|
|
|
|||
|
|
@ -1373,6 +1373,19 @@ draw =
|
|||
.section-offset-along-z = SECTION Versetzt entlang Z:
|
||||
.sketch-pen-down-move-to-sketch-click-to-lift-e = SKETCH Pen down — zum Skizzieren bewegen, zum Liften klicken, zum Aufzeichnen eingeben:
|
||||
.sketch-pen-up-click-to-lower-the-pen-enter-to = SKETCH Pen up — klicken Sie auf den Stift zu senken, geben Sie ein, um aufzuzeichnen:
|
||||
.sketch-pen = Stift
|
||||
.sketch-increment = Schrittweite
|
||||
.sketch-record = Aufzeichnen
|
||||
.sketch-stop-erasing = Radieren beenden
|
||||
.sketch-connect = Verbinden
|
||||
.sketch-exit = Beenden
|
||||
.sketch-quit = Verwerfen
|
||||
.sketch-object-type = SKETCH Objekttyp [Linie/Polylinie/Spline]:
|
||||
.sketch-record-increment = SKETCH Aufzeichnungsschritt <__ocs_arg_value__>:
|
||||
.sketch-spline-fit-tolerance = SKETCH Spline-Anpassungstoleranz <__ocs_arg_value__>:
|
||||
.sketch-erase-move-backward = SKETCH Radieren — rückwärts über die temporäre Skizze bewegen:
|
||||
.sketch-pen-down-move = SKETCH (__ocs_arg_type__, Schrittweite __ocs_arg_increment__) Stift unten — zum Skizzieren bewegen:
|
||||
.sketch-pen-up-click = SKETCH (__ocs_arg_type__, Schrittweite __ocs_arg_increment__) Stift oben — zum Absenken klicken:
|
||||
.slice-cutting-plane-axis-x-y-z-add-top-bottom = SLICE Schnittebenenachse [X / Y / Z] (fügen Sie TOP/BOTTOM durch Eingabe hinzu):
|
||||
.slice-offset-along-x = SLICE versetzt entlang X:
|
||||
.slice-offset-along-y = SLICE längs Y versetzt:
|
||||
|
|
|
|||
|
|
@ -1373,6 +1373,19 @@ draw =
|
|||
.section-offset-along-z = SECTION offset along Z:
|
||||
.sketch-pen-down-move-to-sketch-click-to-lift-e = SKETCH Pen down — move to sketch, click to lift, Enter to record:
|
||||
.sketch-pen-up-click-to-lower-the-pen-enter-to = SKETCH Pen up — click to lower the pen, Enter to record:
|
||||
.sketch-pen = Pen
|
||||
.sketch-increment = Increment
|
||||
.sketch-record = Record
|
||||
.sketch-stop-erasing = Stop erasing
|
||||
.sketch-connect = Connect
|
||||
.sketch-exit = Exit
|
||||
.sketch-quit = Quit
|
||||
.sketch-object-type = SKETCH Object type [Line/Polyline/Spline]:
|
||||
.sketch-record-increment = SKETCH Record increment <__ocs_arg_value__>:
|
||||
.sketch-spline-fit-tolerance = SKETCH Spline fit tolerance <__ocs_arg_value__>:
|
||||
.sketch-erase-move-backward = SKETCH Erase — move backward over the temporary sketch:
|
||||
.sketch-pen-down-move = SKETCH (__ocs_arg_type__, increment __ocs_arg_increment__) Pen down — move to sketch:
|
||||
.sketch-pen-up-click = SKETCH (__ocs_arg_type__, increment __ocs_arg_increment__) Pen up — click to lower the pen:
|
||||
.slice-cutting-plane-axis-x-y-z-add-top-bottom = SLICE cutting-plane axis [X / Y / Z] (add TOP/BOTTOM by typing):
|
||||
.slice-offset-along-x = SLICE offset along X:
|
||||
.slice-offset-along-y = SLICE offset along Y:
|
||||
|
|
|
|||
|
|
@ -1373,6 +1373,19 @@ draw =
|
|||
.section-offset-along-z = SECTION desfase a lo largo de Z:
|
||||
.sketch-pen-down-move-to-sketch-click-to-lift-e = SKETCH Lápiz abajo: mueva para dibujar, pulse para levantarlo, Intro para registrar:
|
||||
.sketch-pen-up-click-to-lower-the-pen-enter-to = SKETCH Lápiz arriba: pulse para bajarlo, Intro para registrar:
|
||||
.sketch-pen = Lápiz
|
||||
.sketch-increment = Incremento
|
||||
.sketch-record = Registrar
|
||||
.sketch-stop-erasing = Detener borrado
|
||||
.sketch-connect = Conectar
|
||||
.sketch-exit = Salir
|
||||
.sketch-quit = Descartar
|
||||
.sketch-object-type = SKETCH Tipo de objeto [Línea/Polilínea/Spline]:
|
||||
.sketch-record-increment = SKETCH Incremento de registro <__ocs_arg_value__>:
|
||||
.sketch-spline-fit-tolerance = SKETCH Tolerancia de ajuste de spline <__ocs_arg_value__>:
|
||||
.sketch-erase-move-backward = SKETCH Borrar: retroceda sobre el boceto temporal:
|
||||
.sketch-pen-down-move = SKETCH (__ocs_arg_type__, incremento __ocs_arg_increment__) Lápiz abajo: mueva para dibujar:
|
||||
.sketch-pen-up-click = SKETCH (__ocs_arg_type__, incremento __ocs_arg_increment__) Lápiz arriba: pulse para bajarlo:
|
||||
.slice-cutting-plane-axis-x-y-z-add-top-bottom = SLICE eje del plano de corte [X / Y / Z] (escriba TOP/BOTTOM para añadirlo):
|
||||
.slice-offset-along-x = SLICE desfase a lo largo de X:
|
||||
.slice-offset-along-y = SLICE desfase a lo largo de Y:
|
||||
|
|
|
|||
|
|
@ -1372,6 +1372,19 @@ draw =
|
|||
.section-offset-along-z = SECTION offset pitkin Z:
|
||||
.sketch-pen-down-move-to-sketch-click-to-lift-e = SKETCH Kynä alas — siirrä luonnos, napsauta nostaaksesi, Enter tallentaa:
|
||||
.sketch-pen-up-click-to-lower-the-pen-enter-to = SKETCH Kynä ylös — napsauta laskeaksesi kynää, Enter tallentaaksesi:
|
||||
.sketch-pen = Kynä
|
||||
.sketch-increment = Askel
|
||||
.sketch-record = Tallenna
|
||||
.sketch-stop-erasing = Lopeta pyyhkiminen
|
||||
.sketch-connect = Yhdistä
|
||||
.sketch-exit = Lopeta
|
||||
.sketch-quit = Hylkää
|
||||
.sketch-object-type = SKETCH Kohdetyyppi [Viiva/Moniviiva/Spline]:
|
||||
.sketch-record-increment = SKETCH Tallennusaskel <__ocs_arg_value__>:
|
||||
.sketch-spline-fit-tolerance = SKETCH Splinen sovitustoleranssi <__ocs_arg_value__>:
|
||||
.sketch-erase-move-backward = SKETCH Pyyhi — liiku taaksepäin väliaikaisen luonnoksen päällä:
|
||||
.sketch-pen-down-move = SKETCH (__ocs_arg_type__, askel __ocs_arg_increment__) Kynä alhaalla — piirrä liikuttamalla:
|
||||
.sketch-pen-up-click = SKETCH (__ocs_arg_type__, askel __ocs_arg_increment__) Kynä ylhäällä — laske napsauttamalla:
|
||||
.slice-cutting-plane-axis-x-y-z-add-top-bottom = SLICE leikkaustason akseli [X / Y / Z] (lisää TOP/BOTTOM kirjoittamalla):
|
||||
.slice-offset-along-x = SLICE offset pitkin X:
|
||||
.slice-offset-along-y = SLICE offset pitkin Y:
|
||||
|
|
|
|||
|
|
@ -1373,6 +1373,19 @@ draw =
|
|||
.section-offset-along-z = SECTION décalage le long de Z:
|
||||
.sketch-pen-down-move-to-sketch-click-to-lift-e = SKETCH Pen vers le bas — déplacer vers le croquis, cliquer pour soulever, Entrer pour enregistrer:
|
||||
.sketch-pen-up-click-to-lower-the-pen-enter-to = SKETCH Pen up — cliquez pour baisser le stylo, entrez pour enregistrer:
|
||||
.sketch-pen = Stylo
|
||||
.sketch-increment = Incrément
|
||||
.sketch-record = Enregistrer
|
||||
.sketch-stop-erasing = Arrêter d’effacer
|
||||
.sketch-connect = Relier
|
||||
.sketch-exit = Terminer
|
||||
.sketch-quit = Abandonner
|
||||
.sketch-object-type = SKETCH Type d’objet [Ligne/Polyligne/Spline] :
|
||||
.sketch-record-increment = SKETCH Pas d’enregistrement <__ocs_arg_value__> :
|
||||
.sketch-spline-fit-tolerance = SKETCH Tolérance d’ajustement de la spline <__ocs_arg_value__> :
|
||||
.sketch-erase-move-backward = SKETCH Effacer — revenez en arrière sur l’esquisse temporaire :
|
||||
.sketch-pen-down-move = SKETCH (__ocs_arg_type__, incrément __ocs_arg_increment__) Stylo baissé — déplacez pour dessiner :
|
||||
.sketch-pen-up-click = SKETCH (__ocs_arg_type__, incrément __ocs_arg_increment__) Stylo levé — cliquez pour l’abaisser :
|
||||
.slice-cutting-plane-axis-x-y-z-add-top-bottom = SLICE axe du plan de coupe [X / Y / Z] (ajouter TOP/BOTTOM par typage):
|
||||
.slice-offset-along-x = SLICE décalage le long de X:
|
||||
.slice-offset-along-y = SLICE décalage le long de Y:
|
||||
|
|
|
|||
|
|
@ -1361,6 +1361,19 @@ draw =
|
|||
.section-offset-along-z = SECTION Z के साथ ऑफसेट:
|
||||
.sketch-pen-down-move-to-sketch-click-to-lift-e = SKETCH पेन नीचे — स्केच करने के लिए चलाएँ, पेन उठाने के लिए क्लिक करें, रिकॉर्ड करने के लिए Enter दबाएँ:
|
||||
.sketch-pen-up-click-to-lower-the-pen-enter-to = SKETCH पेन ऊपर — पेन नीचे करने के लिए क्लिक करें, रिकॉर्ड करने के लिए Enter दबाएँ:
|
||||
.sketch-pen = पेन
|
||||
.sketch-increment = वृद्धि
|
||||
.sketch-record = रिकॉर्ड
|
||||
.sketch-stop-erasing = मिटाना रोकें
|
||||
.sketch-connect = जोड़ें
|
||||
.sketch-exit = बाहर निकलें
|
||||
.sketch-quit = छोड़ें
|
||||
.sketch-object-type = SKETCH ऑब्जेक्ट प्रकार [रेखा/पॉलीलाइन/Spline]:
|
||||
.sketch-record-increment = SKETCH रिकॉर्ड वृद्धि <__ocs_arg_value__>:
|
||||
.sketch-spline-fit-tolerance = SKETCH Spline फ़िट सहनशीलता <__ocs_arg_value__>:
|
||||
.sketch-erase-move-backward = SKETCH मिटाएँ — अस्थायी स्केच पर पीछे की ओर चलें:
|
||||
.sketch-pen-down-move = SKETCH (__ocs_arg_type__, वृद्धि __ocs_arg_increment__) पेन नीचे — स्केच करने के लिए चलाएँ:
|
||||
.sketch-pen-up-click = SKETCH (__ocs_arg_type__, वृद्धि __ocs_arg_increment__) पेन ऊपर — नीचे करने के लिए क्लिक करें:
|
||||
.slice-cutting-plane-axis-x-y-z-add-top-bottom = SLICE कटिंग-प्लेन अक्ष [X / Y / Z] (Typing द्वारा शीर्ष/BOTTOM को शामिल किया गया):
|
||||
.slice-offset-along-x = SLICE X के साथ ऑफसेट:
|
||||
.slice-offset-along-y = SLICE वाई के साथ ऑफसेट:
|
||||
|
|
|
|||
|
|
@ -1372,6 +1372,19 @@ draw =
|
|||
.section-offset-along-z = SECTION eltolás a Z mentén:
|
||||
.sketch-pen-down-move-to-sketch-click-to-lift-e = SKETCH Toll lefelé – mozgassa a vázlatot, kattintson az emeléshez, Enter a rögzítéshez:
|
||||
.sketch-pen-up-click-to-lower-the-pen-enter-to = SKETCH Toll felfelé – kattintson a toll leengedéséhez, Enter a rögzítéshez:
|
||||
.sketch-pen = Toll
|
||||
.sketch-increment = Lépésköz
|
||||
.sketch-record = Rögzítés
|
||||
.sketch-stop-erasing = Törlés leállítása
|
||||
.sketch-connect = Csatlakoztatás
|
||||
.sketch-exit = Kilépés
|
||||
.sketch-quit = Elvetés
|
||||
.sketch-object-type = SKETCH Objektumtípus [Vonal/Vonallánc/Spline]:
|
||||
.sketch-record-increment = SKETCH Rögzítési lépés <__ocs_arg_value__>:
|
||||
.sketch-spline-fit-tolerance = SKETCH Spline illesztési tűrése <__ocs_arg_value__>:
|
||||
.sketch-erase-move-backward = SKETCH Törlés — haladjon visszafelé az ideiglenes vázlaton:
|
||||
.sketch-pen-down-move = SKETCH (__ocs_arg_type__, lépés __ocs_arg_increment__) Toll lent — mozgassa a rajzoláshoz:
|
||||
.sketch-pen-up-click = SKETCH (__ocs_arg_type__, lépés __ocs_arg_increment__) Toll fent — kattintson a leengedéshez:
|
||||
.slice-cutting-plane-axis-x-y-z-add-top-bottom = SLICE vágósík tengelye [X / Y / Z] (adja hozzá a TOP/BOTTOM-t beírással):
|
||||
.slice-offset-along-x = SLICE eltolás a X mentén:
|
||||
.slice-offset-along-y = SLICE eltolás a Y mentén:
|
||||
|
|
|
|||
|
|
@ -1372,6 +1372,19 @@ draw =
|
|||
.section-offset-along-z = Scostamento SECTION lungo Z:
|
||||
.sketch-pen-down-move-to-sketch-click-to-lift-e = SKETCH Penna giù: sposta sullo schizzo, fai clic per sollevare, Enter per registrare:
|
||||
.sketch-pen-up-click-to-lower-the-pen-enter-to = SKETCH Penna su: fare clic per abbassare la penna, Enter per registrare:
|
||||
.sketch-pen = Penna
|
||||
.sketch-increment = Incremento
|
||||
.sketch-record = Registra
|
||||
.sketch-stop-erasing = Interrompi cancellazione
|
||||
.sketch-connect = Connetti
|
||||
.sketch-exit = Esci
|
||||
.sketch-quit = Abbandona
|
||||
.sketch-object-type = SKETCH Tipo di oggetto [Linea/Polilinea/Spline]:
|
||||
.sketch-record-increment = SKETCH Incremento di registrazione <__ocs_arg_value__>:
|
||||
.sketch-spline-fit-tolerance = SKETCH Tolleranza di adattamento spline <__ocs_arg_value__>:
|
||||
.sketch-erase-move-backward = SKETCH Cancella — spostati indietro sullo schizzo temporaneo:
|
||||
.sketch-pen-down-move = SKETCH (__ocs_arg_type__, incremento __ocs_arg_increment__) Penna abbassata — spostati per disegnare:
|
||||
.sketch-pen-up-click = SKETCH (__ocs_arg_type__, incremento __ocs_arg_increment__) Penna sollevata — fai clic per abbassarla:
|
||||
.slice-cutting-plane-axis-x-y-z-add-top-bottom = Asse del piano di taglio SLICE [X / Y / Z] (aggiungere TOP/BOTTOM digitando):
|
||||
.slice-offset-along-x = Scostamento SLICE lungo X:
|
||||
.slice-offset-along-y = Scostamento SLICE lungo Y:
|
||||
|
|
|
|||
|
|
@ -1363,6 +1363,19 @@ draw =
|
|||
.section-offset-along-z = SECTION Z 方向オフセット:
|
||||
.sketch-pen-down-move-to-sketch-click-to-lift-e = SKETCH ペンを下げました — 移動してスケッチ、クリックで上げ、Enter で記録:
|
||||
.sketch-pen-up-click-to-lower-the-pen-enter-to = SKETCH ペンを上げました — クリックで下げ、Enter で記録:
|
||||
.sketch-pen = ペン
|
||||
.sketch-increment = 増分
|
||||
.sketch-record = 記録
|
||||
.sketch-stop-erasing = 消去を停止
|
||||
.sketch-connect = 接続
|
||||
.sketch-exit = 終了
|
||||
.sketch-quit = 破棄
|
||||
.sketch-object-type = SKETCH オブジェクト タイプ [線分/ポリライン/スプライン]:
|
||||
.sketch-record-increment = SKETCH 記録増分 <__ocs_arg_value__>:
|
||||
.sketch-spline-fit-tolerance = SKETCH スプライン フィット許容差 <__ocs_arg_value__>:
|
||||
.sketch-erase-move-backward = SKETCH 消去 — 一時スケッチ上を後方へ移動してください:
|
||||
.sketch-pen-down-move = SKETCH (__ocs_arg_type__、増分 __ocs_arg_increment__) ペンを下げました — 移動してスケッチ:
|
||||
.sketch-pen-up-click = SKETCH (__ocs_arg_type__、増分 __ocs_arg_increment__) ペンを上げました — クリックで下げます:
|
||||
.slice-cutting-plane-axis-x-y-z-add-top-bottom = SLICE 切断面の軸 [X / Y / Z](TOP/BOTTOM を追加可能):
|
||||
.slice-offset-along-x = SLICE X 方向オフセット:
|
||||
.slice-offset-along-y = SLICE Y 方向オフセット:
|
||||
|
|
|
|||
|
|
@ -1372,6 +1372,19 @@ draw =
|
|||
.section-offset-along-z = Z를 따라 SECTION 오프셋:
|
||||
.sketch-pen-down-move-to-sketch-click-to-lift-e = SKETCH 펜 아래로 — 스케치로 이동하고 클릭하여 올리며 Enter로 기록:
|
||||
.sketch-pen-up-click-to-lower-the-pen-enter-to = SKETCH 펜 업 — 펜을 내리려면 클릭하고 기록하려면 Enter를 클릭하세요.
|
||||
.sketch-pen = 펜
|
||||
.sketch-increment = 증분
|
||||
.sketch-record = 기록
|
||||
.sketch-stop-erasing = 지우기 중지
|
||||
.sketch-connect = 연결
|
||||
.sketch-exit = 종료
|
||||
.sketch-quit = 취소
|
||||
.sketch-object-type = SKETCH 객체 유형 [선/폴리선/스플라인]:
|
||||
.sketch-record-increment = SKETCH 기록 증분 <__ocs_arg_value__>:
|
||||
.sketch-spline-fit-tolerance = SKETCH 스플라인 맞춤 공차 <__ocs_arg_value__>:
|
||||
.sketch-erase-move-backward = SKETCH 지우기 — 임시 스케치 위를 뒤로 이동하십시오:
|
||||
.sketch-pen-down-move = SKETCH (__ocs_arg_type__, 증분 __ocs_arg_increment__) 펜 내림 — 이동하여 스케치:
|
||||
.sketch-pen-up-click = SKETCH (__ocs_arg_type__, 증분 __ocs_arg_increment__) 펜 올림 — 클릭하여 내리기:
|
||||
.slice-cutting-plane-axis-x-y-z-add-top-bottom = SLICE 절단 평면 축 [X / Y / Z] (입력하여 TOP/BOTTOM 추가):
|
||||
.slice-offset-along-x = X를 따라 SLICE 오프셋:
|
||||
.slice-offset-along-y = Y를 따라 SLICE 오프셋:
|
||||
|
|
|
|||
|
|
@ -1373,6 +1373,19 @@ draw =
|
|||
.section-offset-along-z = SECTION offset langs Z:
|
||||
.sketch-pen-down-move-to-sketch-click-to-lift-e = SKETCH Pen naar beneden
|
||||
.sketch-pen-up-click-to-lower-the-pen-enter-to = SKETCH Pen omhoog
|
||||
.sketch-pen = Pen
|
||||
.sketch-increment = Stapgrootte
|
||||
.sketch-record = Vastleggen
|
||||
.sketch-stop-erasing = Wissen stoppen
|
||||
.sketch-connect = Verbinden
|
||||
.sketch-exit = Afsluiten
|
||||
.sketch-quit = Annuleren
|
||||
.sketch-object-type = SKETCH Objecttype [Lijn/Polylijn/Spline]:
|
||||
.sketch-record-increment = SKETCH Opnamestap <__ocs_arg_value__>:
|
||||
.sketch-spline-fit-tolerance = SKETCH Spline-aanpassingstolerantie <__ocs_arg_value__>:
|
||||
.sketch-erase-move-backward = SKETCH Wissen — beweeg terug over de tijdelijke schets:
|
||||
.sketch-pen-down-move = SKETCH (__ocs_arg_type__, stap __ocs_arg_increment__) Pen omlaag — beweeg om te schetsen:
|
||||
.sketch-pen-up-click = SKETCH (__ocs_arg_type__, stap __ocs_arg_increment__) Pen omhoog — klik om de pen neer te zetten:
|
||||
.slice-cutting-plane-axis-x-y-z-add-top-bottom = SLICE snijvlakas [X / Y / Z] (voeg TOP/BOTTOM toe door te typen):
|
||||
.slice-offset-along-x = SLICE offset langs X:
|
||||
.slice-offset-along-y = SLICE offset langs Y:
|
||||
|
|
|
|||
|
|
@ -1372,6 +1372,19 @@ draw =
|
|||
.section-offset-along-z = Przesunięcie SECTION wzdłuż Z:
|
||||
.sketch-pen-down-move-to-sketch-click-to-lift-e = SKETCH Opuść pióro — przejdź do szkicu, kliknij, aby podnieść, Enter, aby nagrać:
|
||||
.sketch-pen-up-click-to-lower-the-pen-enter-to = SKETCH Pióro w górę — kliknij, aby opuścić pióro, Enter, aby nagrać:
|
||||
.sketch-pen = Pióro
|
||||
.sketch-increment = Przyrost
|
||||
.sketch-record = Zapisz
|
||||
.sketch-stop-erasing = Zatrzymaj wymazywanie
|
||||
.sketch-connect = Połącz
|
||||
.sketch-exit = Zakończ
|
||||
.sketch-quit = Porzuć
|
||||
.sketch-object-type = SKETCH Typ obiektu [Linia/Polilinia/Spline]:
|
||||
.sketch-record-increment = SKETCH Przyrost zapisu <__ocs_arg_value__>:
|
||||
.sketch-spline-fit-tolerance = SKETCH Tolerancja dopasowania spline <__ocs_arg_value__>:
|
||||
.sketch-erase-move-backward = SKETCH Wymazywanie — przesuń się wstecz po szkicu tymczasowym:
|
||||
.sketch-pen-down-move = SKETCH (__ocs_arg_type__, przyrost __ocs_arg_increment__) Pióro opuszczone — przesuń, aby szkicować:
|
||||
.sketch-pen-up-click = SKETCH (__ocs_arg_type__, przyrost __ocs_arg_increment__) Pióro podniesione — kliknij, aby opuścić:
|
||||
.slice-cutting-plane-axis-x-y-z-add-top-bottom = Oś płaszczyzny cięcia SLICE [X / Y / Z] (dodaj TOP/BOTTOM wpisując):
|
||||
.slice-offset-along-x = Przesunięcie SLICE wzdłuż X:
|
||||
.slice-offset-along-y = Przesunięcie SLICE wzdłuż Y:
|
||||
|
|
|
|||
|
|
@ -1373,6 +1373,19 @@ draw =
|
|||
.section-offset-along-z = SECTION deslocamento ao longo de Z:
|
||||
.sketch-pen-down-move-to-sketch-click-to-lift-e = SKETCH Caneta abaixada — mova para esboçar, clique para levantar, Enter para registrar:
|
||||
.sketch-pen-up-click-to-lower-the-pen-enter-to = SKETCH Caneta levantada — clique para abaixar, Enter para registrar:
|
||||
.sketch-pen = Caneta
|
||||
.sketch-increment = Incremento
|
||||
.sketch-record = Registrar
|
||||
.sketch-stop-erasing = Parar de apagar
|
||||
.sketch-connect = Conectar
|
||||
.sketch-exit = Sair
|
||||
.sketch-quit = Descartar
|
||||
.sketch-object-type = SKETCH Tipo de objeto [Linha/Polilinha/Spline]:
|
||||
.sketch-record-increment = SKETCH Incremento de registro <__ocs_arg_value__>:
|
||||
.sketch-spline-fit-tolerance = SKETCH Tolerância de ajuste da spline <__ocs_arg_value__>:
|
||||
.sketch-erase-move-backward = SKETCH Apagar — mova para trás sobre o esboço temporário:
|
||||
.sketch-pen-down-move = SKETCH (__ocs_arg_type__, incremento __ocs_arg_increment__) Caneta abaixada — mova para esboçar:
|
||||
.sketch-pen-up-click = SKETCH (__ocs_arg_type__, incremento __ocs_arg_increment__) Caneta levantada — clique para abaixar:
|
||||
.slice-cutting-plane-axis-x-y-z-add-top-bottom = SLICE eixo do plano de corte [X / Y / Z] (digite TOP/BOTTOM para adicionar):
|
||||
.slice-offset-along-x = SLICE deslocamento ao longo de X:
|
||||
.slice-offset-along-y = SLICE deslocamento ao longo de Y:
|
||||
|
|
|
|||
|
|
@ -1376,6 +1376,19 @@ draw =
|
|||
.section-offset-along-z = SECTION Смещение вдоль Z:
|
||||
.sketch-pen-down-move-to-sketch-click-to-lift-e = SKETCH Нажмите вниз — перейдите к эскизу, нажмите, чтобы поднять, Введите, чтобы записать:
|
||||
.sketch-pen-up-click-to-lower-the-pen-enter-to = SKETCH Pen up — нажмите, чтобы опустить ручку, Введите для записи:
|
||||
.sketch-pen = Перо
|
||||
.sketch-increment = Шаг
|
||||
.sketch-record = Записать
|
||||
.sketch-stop-erasing = Остановить стирание
|
||||
.sketch-connect = Соединить
|
||||
.sketch-exit = Завершить
|
||||
.sketch-quit = Отменить
|
||||
.sketch-object-type = SKETCH Тип объекта [Отрезок/Полилиния/Сплайн]:
|
||||
.sketch-record-increment = SKETCH Шаг записи <__ocs_arg_value__>:
|
||||
.sketch-spline-fit-tolerance = SKETCH Допуск аппроксимации сплайна <__ocs_arg_value__>:
|
||||
.sketch-erase-move-backward = SKETCH Стирание — двигайтесь назад по временному эскизу:
|
||||
.sketch-pen-down-move = SKETCH (__ocs_arg_type__, шаг __ocs_arg_increment__) Перо опущено — двигайте для рисования:
|
||||
.sketch-pen-up-click = SKETCH (__ocs_arg_type__, шаг __ocs_arg_increment__) Перо поднято — щёлкните, чтобы опустить:
|
||||
.slice-cutting-plane-axis-x-y-z-add-top-bottom = SLICE ось режущей плоскости [X/Y/Z] (добавить TOP/BOTTOM путем набора текста):
|
||||
.slice-offset-along-x = SLICE Смещение вдоль X:
|
||||
.slice-offset-along-y = SLICE смещение вдоль Y:
|
||||
|
|
|
|||
|
|
@ -1355,6 +1355,19 @@ draw =
|
|||
.section-offset-along-z = SECTION Z boyunca ofset:
|
||||
.sketch-pen-down-move-to-sketch-click-to-lift-e = SKETCH Kalem aşağıda — çizmek için hareket ettirin, kaldırmak için tıklayın, kaydetmek için Enter:
|
||||
.sketch-pen-up-click-to-lower-the-pen-enter-to = SKETCH Kalem yukarıda — indirmek için tıklayın, kaydetmek için Enter:
|
||||
.sketch-pen = Kalem
|
||||
.sketch-increment = Artış
|
||||
.sketch-record = Kaydet
|
||||
.sketch-stop-erasing = Silmeyi durdur
|
||||
.sketch-connect = Bağla
|
||||
.sketch-exit = Çıkış
|
||||
.sketch-quit = Vazgeç
|
||||
.sketch-object-type = SKETCH Nesne türü [Çizgi/Çoklu çizgi/Spline]:
|
||||
.sketch-record-increment = SKETCH Kayıt artışı <__ocs_arg_value__>:
|
||||
.sketch-spline-fit-tolerance = SKETCH Spline uyum toleransı <__ocs_arg_value__>:
|
||||
.sketch-erase-move-backward = SKETCH Sil — geçici çizim üzerinde geriye doğru hareket edin:
|
||||
.sketch-pen-down-move = SKETCH (__ocs_arg_type__, artış __ocs_arg_increment__) Kalem aşağıda — çizmek için hareket ettirin:
|
||||
.sketch-pen-up-click = SKETCH (__ocs_arg_type__, artış __ocs_arg_increment__) Kalem yukarıda — indirmek için tıklayın:
|
||||
.slice-cutting-plane-axis-x-y-z-add-top-bottom = SLICE Kesme düzlemi ekseni [X / Y / Z] (TOP/BOTTOM yazarak ekleyin):
|
||||
.slice-offset-along-x = SLICE X boyunca ofset:
|
||||
.slice-offset-along-y = SLICE Y boyunca ofset:
|
||||
|
|
|
|||
|
|
@ -1360,6 +1360,19 @@ draw =
|
|||
.section-offset-along-z = SECTION 与 Z 相冲:
|
||||
.sketch-pen-down-move-to-sketch-click-to-lift-e = SKETCH 落笔 — 移动以绘图,单击抬笔,按 Enter 记录:
|
||||
.sketch-pen-up-click-to-lower-the-pen-enter-to = SKETCH 抬笔 — 单击落笔,按 Enter 记录:
|
||||
.sketch-pen = 画笔
|
||||
.sketch-increment = 增量
|
||||
.sketch-record = 记录
|
||||
.sketch-stop-erasing = 停止擦除
|
||||
.sketch-connect = 连接
|
||||
.sketch-exit = 退出
|
||||
.sketch-quit = 放弃
|
||||
.sketch-object-type = SKETCH 对象类型 [直线/多段线/样条曲线]:
|
||||
.sketch-record-increment = SKETCH 记录增量 <__ocs_arg_value__>:
|
||||
.sketch-spline-fit-tolerance = SKETCH 样条曲线拟合公差 <__ocs_arg_value__>:
|
||||
.sketch-erase-move-backward = SKETCH 擦除 — 沿临时草图向后移动:
|
||||
.sketch-pen-down-move = SKETCH(__ocs_arg_type__,增量 __ocs_arg_increment__) 落笔 — 移动以绘图:
|
||||
.sketch-pen-up-click = SKETCH(__ocs_arg_type__,增量 __ocs_arg_increment__) 抬笔 — 单击落笔:
|
||||
.slice-cutting-plane-axis-x-y-z-add-top-bottom = SLICE 剖切平面轴 [X / Y / Z](输入 TOP/BOTTOM 可追加):
|
||||
.slice-offset-along-x = SLICE 页:1
|
||||
.slice-offset-along-y = SLICE 与 Y 相冲:
|
||||
|
|
|
|||
|
|
@ -1372,6 +1372,19 @@ draw =
|
|||
.section-offset-along-z = SECTION 與 Z 相衝:
|
||||
.sketch-pen-down-move-to-sketch-click-to-lift-e = SKETCH 落筆 — 移動以繪圖,按一下抬筆,按 Enter 記錄:
|
||||
.sketch-pen-up-click-to-lower-the-pen-enter-to = SKETCH 抬筆 — 按一下落筆,按 Enter 記錄:
|
||||
.sketch-pen = 畫筆
|
||||
.sketch-increment = 增量
|
||||
.sketch-record = 記錄
|
||||
.sketch-stop-erasing = 停止擦除
|
||||
.sketch-connect = 連接
|
||||
.sketch-exit = 結束
|
||||
.sketch-quit = 放棄
|
||||
.sketch-object-type = SKETCH 物件類型 [直線/聚合線/雲形線]:
|
||||
.sketch-record-increment = SKETCH 記錄增量 <__ocs_arg_value__>:
|
||||
.sketch-spline-fit-tolerance = SKETCH 雲形線擬合公差 <__ocs_arg_value__>:
|
||||
.sketch-erase-move-backward = SKETCH 擦除 — 沿暫存草圖向後移動:
|
||||
.sketch-pen-down-move = SKETCH(__ocs_arg_type__,增量 __ocs_arg_increment__) 落筆 — 移動以繪圖:
|
||||
.sketch-pen-up-click = SKETCH(__ocs_arg_type__,增量 __ocs_arg_increment__) 抬筆 — 按一下落筆:
|
||||
.slice-cutting-plane-axis-x-y-z-add-top-bottom = SLICE 剖切平面軸 [X / Y / Z](輸入 TOP/BOTTOM 可追加):
|
||||
.slice-offset-along-x = SLICE 頁:1
|
||||
.slice-offset-along-y = SLICE 與 Y 相衝:
|
||||
|
|
|
|||
|
|
@ -642,6 +642,26 @@ impl OpenCADStudio {
|
|||
/// Pure-selection commands (SELECTALL, QSELECT, …) run without an active
|
||||
/// command, so `was_active` is false and their selection is preserved.
|
||||
pub(super) fn apply_cmd_result(&mut self, result: CmdResult) -> Task<Message> {
|
||||
let settings = self.tabs[self.active_tab]
|
||||
.active_cmd
|
||||
.as_ref()
|
||||
.and_then(|command| command.sketch_settings());
|
||||
if let Some((sketch_type, increment, tolerance)) = settings {
|
||||
let tab = &mut self.tabs[self.active_tab];
|
||||
let changed = tab.scene.document.header.sketch_type != sketch_type
|
||||
|| (tab.scene.document.header.sketch_increment - increment).abs() > f64::EPSILON
|
||||
|| (tab.scene.document.header.sketch_tolerance - tolerance).abs()
|
||||
> f64::EPSILON;
|
||||
if changed {
|
||||
crate::io::set_sketch_settings(
|
||||
&mut tab.scene.document,
|
||||
sketch_type,
|
||||
increment,
|
||||
tolerance,
|
||||
);
|
||||
tab.dirty = true;
|
||||
}
|
||||
}
|
||||
let was_active = self.tabs[self.active_tab].active_cmd.is_some();
|
||||
let preserve_selection =
|
||||
matches!(result, CmdResult::Relaunch(..) | CmdResult::Dispatch(..));
|
||||
|
|
@ -785,6 +805,7 @@ impl OpenCADStudio {
|
|||
self.commit_entity(entity);
|
||||
}
|
||||
self.tabs[i].dirty = true;
|
||||
self.tabs[i].scene.clear_preview_wire();
|
||||
let prompt = self.tabs[i].active_cmd.as_ref().map(|c| c.prompt());
|
||||
if let Some(p) = prompt {
|
||||
self.command_line.push_info(&p);
|
||||
|
|
|
|||
|
|
@ -487,7 +487,12 @@ impl OpenCADStudio {
|
|||
|
||||
"SKETCH" => {
|
||||
use crate::modules::draw::draw::sketch::SketchCommand;
|
||||
let new_cmd = SketchCommand::new();
|
||||
let header = &self.tabs[i].scene.document.header;
|
||||
let new_cmd = SketchCommand::new(
|
||||
header.sketch_type,
|
||||
header.sketch_increment,
|
||||
header.sketch_tolerance,
|
||||
);
|
||||
self.command_line.push_info(&new_cmd.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -526,6 +526,8 @@ inventory::submit!(crate::command::CommandRegistration {
|
|||
"HALOGAP",
|
||||
"TRACEWID",
|
||||
"SKETCHINC",
|
||||
"SKPOLY",
|
||||
"SKTOLERANCE",
|
||||
// Reset selected entities' overrides to follow their layer.
|
||||
"SETBYLAYER",
|
||||
// Remove duplicate objects; set drawing base point; audit integrity;
|
||||
|
|
|
|||
|
|
@ -931,6 +931,8 @@ impl OpenCADStudio {
|
|||
| "HALOGAP"
|
||||
| "TRACEWID"
|
||||
| "SKETCHINC"
|
||||
| "SKPOLY"
|
||||
| "SKTOLERANCE"
|
||||
) =>
|
||||
{
|
||||
return self.dispatch_styleprops(&format!("SETVAR {cmd}"), i);
|
||||
|
|
@ -953,7 +955,7 @@ impl OpenCADStudio {
|
|||
let value = it.next().map(|s| s.trim().to_string());
|
||||
if name.is_empty() || name == "?" {
|
||||
self.command_line.push_info(
|
||||
"SETVAR: LTSCALE CELTSCALE PDMODE PDSIZE TEXTSIZE ORTHOMODE FILLMODE MIRRTEXT FRAME IMAGEFRAME PDFFRAME WIPEOUTFRAME XCLIPFRAME POINTCLOUDCLIPFRAME ZOOMWHEEL ZOOMFACTOR CURSORSIZE PICKBOX CURSORTYPE SNAPANG ATTREQ ATTDIA DIMASSOC ANGBASE ANGDIR | CLAYER CELTYPE TEXTSTYLE (read-only)",
|
||||
"SETVAR: LTSCALE CELTSCALE PDMODE PDSIZE TEXTSIZE ORTHOMODE FILLMODE MIRRTEXT FRAME IMAGEFRAME PDFFRAME WIPEOUTFRAME XCLIPFRAME POINTCLOUDCLIPFRAME ZOOMWHEEL ZOOMFACTOR CURSORSIZE PICKBOX CURSORTYPE SNAPANG ATTREQ ATTDIA DIMASSOC ANGBASE ANGDIR SKETCHINC SKPOLY SKTOLERANCE | CLAYER CELTYPE TEXTSTYLE (read-only)",
|
||||
);
|
||||
} else {
|
||||
let frame_kind = crate::scene::frame::kind_for_name(&name);
|
||||
|
|
@ -1700,15 +1702,41 @@ impl OpenCADStudio {
|
|||
None => Ok((format!("TRACEWID = {}", h.trace_width), false)),
|
||||
},
|
||||
"SKETCHINC" => match &value {
|
||||
Some(v) => v
|
||||
.parse::<f64>()
|
||||
.map(|x| {
|
||||
Some(v) => match v.parse::<f64>() {
|
||||
Ok(x) if x.is_finite() && x > 0.0 => {
|
||||
let changed = h.sketch_increment != x;
|
||||
h.sketch_increment = x;
|
||||
(format!("SKETCHINC = {x}"), true)
|
||||
})
|
||||
.map_err(|_| "SETVAR: numeric value required.".into()),
|
||||
Ok((format!("SKETCHINC = {x}"), changed))
|
||||
}
|
||||
_ => Err("SETVAR: positive numeric value required.".into()),
|
||||
},
|
||||
None => Ok((format!("SKETCHINC = {}", h.sketch_increment), false)),
|
||||
},
|
||||
"SKPOLY" => match &value {
|
||||
Some(v) => match v.parse::<i16>() {
|
||||
Ok(x @ 0..=2) => {
|
||||
let changed = h.sketch_type != x;
|
||||
h.sketch_type = x;
|
||||
Ok((format!("SKPOLY = {x}"), changed))
|
||||
}
|
||||
_ => Err("SETVAR: integer value from 0 to 2 required.".into()),
|
||||
},
|
||||
None => Ok((format!("SKPOLY = {}", h.sketch_type), false)),
|
||||
},
|
||||
"SKTOLERANCE" => match &value {
|
||||
Some(v) => match v.parse::<f64>() {
|
||||
Ok(x) if x.is_finite() && (0.0..=1.0).contains(&x) => {
|
||||
let changed = h.sketch_tolerance != x;
|
||||
h.sketch_tolerance = x;
|
||||
Ok((format!("SKTOLERANCE = {x}"), changed))
|
||||
}
|
||||
_ => Err("SETVAR: numeric value from 0 to 1 required.".into()),
|
||||
},
|
||||
None => Ok((
|
||||
format!("SKTOLERANCE = {}", h.sketch_tolerance),
|
||||
false,
|
||||
)),
|
||||
},
|
||||
"CLAYER" => match &value {
|
||||
Some(_) => Err(
|
||||
"SETVAR: CLAYER is read-only here — use the CLAYER command."
|
||||
|
|
|
|||
|
|
@ -1822,6 +1822,11 @@ pub trait CadCommand: Send {
|
|||
false
|
||||
}
|
||||
|
||||
/// Current drawing-persisted SKETCH settings.
|
||||
fn sketch_settings(&self) -> Option<(i16, f64, f64)> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Returns `true` when the active text prompt expects free-form prose
|
||||
/// that can legitimately contain whitespace (the body of a TEXT /
|
||||
/// MTEXT / DDEDIT entity, an attribute default value, etc.). For
|
||||
|
|
|
|||
|
|
@ -1784,6 +1784,7 @@ fn fix_current_style_names(doc: &mut CadDocument) {
|
|||
doc.header.current_mleader_style_name = v;
|
||||
}
|
||||
}
|
||||
reflect_sketch_settings(doc);
|
||||
}
|
||||
|
||||
/// Find the handle of the `DictionaryVariable` registered under `name` in any
|
||||
|
|
@ -1818,6 +1819,32 @@ pub(crate) fn drawing_variable(doc: &CadDocument, name: &str) -> Option<String>
|
|||
vardict_value(doc, name)
|
||||
}
|
||||
|
||||
fn reflect_sketch_settings(doc: &mut CadDocument) {
|
||||
let sketch_type = vardict_value(doc, "SKPOLY")
|
||||
.and_then(|value| value.parse::<i16>().ok())
|
||||
.unwrap_or(doc.header.sketch_type)
|
||||
.clamp(0, 2);
|
||||
let increment = vardict_value(doc, "SKETCHINC")
|
||||
.and_then(|value| value.parse::<f64>().ok())
|
||||
.filter(|value| value.is_finite() && *value > 0.0)
|
||||
.unwrap_or(doc.header.sketch_increment);
|
||||
let tolerance = vardict_value(doc, "SKTOLERANCE")
|
||||
.and_then(|value| value.parse::<f64>().ok())
|
||||
.filter(|value| value.is_finite() && (0.0..=1.0).contains(value))
|
||||
.unwrap_or(doc.header.sketch_tolerance);
|
||||
doc.header.sketch_type = sketch_type;
|
||||
doc.header.sketch_increment = if increment.is_finite() && increment > 0.0 {
|
||||
increment
|
||||
} else {
|
||||
0.1
|
||||
};
|
||||
doc.header.sketch_tolerance = if tolerance.is_finite() {
|
||||
tolerance.clamp(0.0, 1.0)
|
||||
} else {
|
||||
0.5
|
||||
};
|
||||
}
|
||||
|
||||
/// Write a drawing variable, creating the variable dictionary and record when
|
||||
/// needed so new drawings preserve the value too.
|
||||
pub(crate) fn set_drawing_variable(doc: &mut CadDocument, name: &str, value: &str) {
|
||||
|
|
@ -1859,6 +1886,31 @@ pub(crate) fn set_drawing_variable(doc: &mut CadDocument, name: &str, value: &st
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_sketch_settings(
|
||||
doc: &mut CadDocument,
|
||||
sketch_type: i16,
|
||||
increment: f64,
|
||||
tolerance: f64,
|
||||
) {
|
||||
let sketch_type = sketch_type.clamp(0, 2);
|
||||
let increment = if increment.is_finite() && increment > 0.0 {
|
||||
increment
|
||||
} else {
|
||||
0.1
|
||||
};
|
||||
let tolerance = if tolerance.is_finite() {
|
||||
tolerance.clamp(0.0, 1.0)
|
||||
} else {
|
||||
0.5
|
||||
};
|
||||
doc.header.sketch_type = sketch_type;
|
||||
doc.header.sketch_increment = increment;
|
||||
doc.header.sketch_tolerance = tolerance;
|
||||
set_drawing_variable(doc, "SKPOLY", &sketch_type.to_string());
|
||||
set_drawing_variable(doc, "SKETCHINC", &increment.to_string());
|
||||
set_drawing_variable(doc, "SKTOLERANCE", &tolerance.to_string());
|
||||
}
|
||||
|
||||
/// The layout tab that was active when the drawing was saved — the `CTAB`
|
||||
/// system variable (stored in the variable dictionary as a `DICTIONARYVAR`).
|
||||
/// `None` when the file recorded no current tab.
|
||||
|
|
@ -1919,6 +1971,10 @@ fn sync_current_styles_on_save(doc: &mut CadDocument) {
|
|||
set_drawing_variable(doc, "CMLEADERSTYLE", &mleader);
|
||||
let annotation = doc.header.current_annotation_scale.clone();
|
||||
set_drawing_variable(doc, "CANNOSCALE", &annotation);
|
||||
let sketch_type = doc.header.sketch_type;
|
||||
let increment = doc.header.sketch_increment;
|
||||
let tolerance = doc.header.sketch_tolerance;
|
||||
set_sketch_settings(doc, sketch_type, increment, tolerance);
|
||||
}
|
||||
|
||||
// ── Corrupt-entity guard ──────────────────────────────────────────────────
|
||||
|
|
|
|||
|
|
@ -2230,6 +2230,19 @@ pub(super) fn message_attribute(source: &str) -> Option<(&'static str, &'static
|
|||
"SETVAR: {name} requires 0, 1, or 2." => Some(("draw", "setvar-frame-range")),
|
||||
"SHEETSET: Sheet Set Manager not yet implemented." => Some(("view", "sheetset-sheet-set-manager-not-yet-implemented")),
|
||||
"SHX" => Some(("properties", "shx")),
|
||||
"Connect" => Some(("draw", "sketch-connect")),
|
||||
"Exit" => Some(("draw", "sketch-exit")),
|
||||
"Increment" => Some(("draw", "sketch-increment")),
|
||||
"Pen" => Some(("draw", "sketch-pen")),
|
||||
"Quit" => Some(("draw", "sketch-quit")),
|
||||
"Record" => Some(("draw", "sketch-record")),
|
||||
"Stop erasing" => Some(("draw", "sketch-stop-erasing")),
|
||||
"SKETCH Object type [Line/Polyline/Spline]:" => Some(("draw", "sketch-object-type")),
|
||||
"SKETCH Record increment <%{value}>:" => Some(("draw", "sketch-record-increment")),
|
||||
"SKETCH Spline fit tolerance <%{value}>:" => Some(("draw", "sketch-spline-fit-tolerance")),
|
||||
"SKETCH Erase — move backward over the temporary sketch:" => Some(("draw", "sketch-erase-move-backward")),
|
||||
"SKETCH (%{type}, increment %{increment}) Pen down — move to sketch:" => Some(("draw", "sketch-pen-down-move")),
|
||||
"SKETCH (%{type}, increment %{increment}) Pen up — click to lower the pen:" => Some(("draw", "sketch-pen-up-click")),
|
||||
"SKETCH Pen down — move to sketch, click to lift, Enter to record:" => Some(("draw", "sketch-pen-down-move-to-sketch-click-to-lift-e")),
|
||||
"SKETCH Pen up — click to lower the pen, Enter to record:" => Some(("draw", "sketch-pen-up-click-to-lower-the-pen-enter-to")),
|
||||
"SLICE cutting-plane axis [X / Y / Z] (add TOP/BOTTOM by typing):" => Some(("draw", "slice-cutting-plane-axis-x-y-z-add-top-bottom")),
|
||||
|
|
|
|||
|
|
@ -1,33 +1,19 @@
|
|||
// Freehand sketch tool — interactive command.
|
||||
//
|
||||
// Command: SKETCH — freehand sketching. A click toggles the pen down/up. While
|
||||
// the pen is DOWN, moving the cursor records its position into the current
|
||||
// stroke whenever it has travelled more than a small fixed threshold, and a
|
||||
// cyan preview wire of the current stroke is shown. A click while the pen is
|
||||
// down lifts it, ending the current stroke; the next click starts a fresh one.
|
||||
// Enter commits every recorded stroke (each stroke of two or more points
|
||||
// becomes one lightweight polyline of straight segments); Esc cancels.
|
||||
//
|
||||
// Geometry is built like the wide-line / ring tools: each stroke is one
|
||||
// LwPolyline whose vertices carry the sampled XY positions and whose elevation
|
||||
// is the first sample's Z.
|
||||
// Freehand sketch tool.
|
||||
|
||||
use acadrust::entities::{LwPolyline, LwVertex};
|
||||
use acadrust::entities::{Line, LwPolyline, LwVertex, Spline};
|
||||
use acadrust::types::{Vector2, Vector3};
|
||||
use acadrust::EntityType;
|
||||
use crate::t;
|
||||
|
||||
use crate::command::{CadCommand, CmdResult, WorkingPlane};
|
||||
use crate::modules::{IconKind, ModuleEvent, ToolDef};
|
||||
use crate::scene::model::wire_model::WireModel;
|
||||
use cadkernel::geom2d::NurbsCurve;
|
||||
use glam::DVec3;
|
||||
|
||||
// Minimum cursor travel (drawing units) before a new sample is recorded.
|
||||
const SAMPLE_EPSILON: f64 = 0.5;
|
||||
use crate::command::{CadCommand, CmdOption, CmdResult, WorkingPlane};
|
||||
use crate::modules::{IconKind, ModuleEvent, ToolDef};
|
||||
use crate::scene::model::wire_model::WireModel;
|
||||
use crate::t;
|
||||
|
||||
// ── Ribbon definition ─────────────────────────────────────────────────────
|
||||
const MIN_INCREMENT: f64 = 1.0e-9;
|
||||
|
||||
#[allow(dead_code)] // ribbon definition ready for wiring; command works via the command line
|
||||
#[allow(dead_code)]
|
||||
pub fn tool() -> ToolDef {
|
||||
ToolDef {
|
||||
id: "SKETCH",
|
||||
|
|
@ -37,54 +23,336 @@ pub fn tool() -> ToolDef {
|
|||
}
|
||||
}
|
||||
|
||||
// ── Command implementation ────────────────────────────────────────────────
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
enum SketchType {
|
||||
Line,
|
||||
Polyline,
|
||||
Spline,
|
||||
}
|
||||
|
||||
impl SketchType {
|
||||
fn from_header(value: i16) -> Self {
|
||||
match value {
|
||||
0 => Self::Line,
|
||||
2 => Self::Spline,
|
||||
_ => Self::Polyline,
|
||||
}
|
||||
}
|
||||
|
||||
fn header_value(self) -> i16 {
|
||||
match self {
|
||||
Self::Line => 0,
|
||||
Self::Polyline => 1,
|
||||
Self::Spline => 2,
|
||||
}
|
||||
}
|
||||
|
||||
fn label(self) -> &'static str {
|
||||
match self {
|
||||
Self::Line => "Line",
|
||||
Self::Polyline => "Polyline",
|
||||
Self::Spline => "Spline",
|
||||
}
|
||||
}
|
||||
|
||||
fn parse(value: &str) -> Option<Self> {
|
||||
match value.trim().to_ascii_uppercase().as_str() {
|
||||
"L" | "LINE" => Some(Self::Line),
|
||||
"P" | "PLINE" | "POLYLINE" => Some(Self::Polyline),
|
||||
"S" | "SPLINE" => Some(Self::Spline),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
enum InputStage {
|
||||
Drawing,
|
||||
Type,
|
||||
Increment,
|
||||
Tolerance,
|
||||
}
|
||||
|
||||
pub struct SketchCommand {
|
||||
/// All completed strokes plus, while the pen is down, the one being drawn
|
||||
/// as the last element. Each stroke is its own list of sampled points.
|
||||
strokes: Vec<Vec<DVec3>>,
|
||||
/// True while the pen is down and samples accumulate into the current stroke.
|
||||
pen_down: bool,
|
||||
erasing: bool,
|
||||
erase_engaged: bool,
|
||||
stage: InputStage,
|
||||
sketch_type: SketchType,
|
||||
increment: f64,
|
||||
tolerance: f64,
|
||||
last_cursor: Option<DVec3>,
|
||||
last_recorded_endpoint: Option<DVec3>,
|
||||
plane: WorkingPlane,
|
||||
}
|
||||
|
||||
impl SketchCommand {
|
||||
pub fn new() -> Self {
|
||||
pub fn new(sketch_type: i16, increment: f64, tolerance: f64) -> Self {
|
||||
Self {
|
||||
strokes: Vec::new(),
|
||||
pen_down: false,
|
||||
erasing: false,
|
||||
erase_engaged: false,
|
||||
stage: InputStage::Drawing,
|
||||
sketch_type: SketchType::from_header(sketch_type),
|
||||
increment: valid_increment(increment),
|
||||
tolerance: valid_tolerance(tolerance),
|
||||
last_cursor: None,
|
||||
last_recorded_endpoint: None,
|
||||
plane: WorkingPlane::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Build one lightweight polyline from a stroke's sampled points, or `None`
|
||||
/// when the stroke has too few points to form a segment.
|
||||
fn build_stroke(&self, points: &[DVec3]) -> Option<EntityType> {
|
||||
if points.len() < 2 {
|
||||
return None;
|
||||
fn begin_stroke(&mut self, point: DVec3) {
|
||||
self.strokes.push(vec![point]);
|
||||
self.pen_down = true;
|
||||
self.erasing = false;
|
||||
self.erase_engaged = false;
|
||||
}
|
||||
|
||||
/// Record real pointer positions at the configured minimum spacing.
|
||||
fn sample_to(&mut self, point: DVec3) {
|
||||
let Some(stroke) = self.strokes.last_mut() else {
|
||||
return;
|
||||
};
|
||||
let Some(cursor) = stroke.last().copied() else {
|
||||
stroke.push(point);
|
||||
return;
|
||||
};
|
||||
let distance = cursor.distance(point);
|
||||
if distance.is_finite() && distance + 1.0e-12 >= self.increment {
|
||||
stroke.push(point);
|
||||
}
|
||||
let points: Vec<DVec3> = points
|
||||
}
|
||||
|
||||
fn finish_stroke(&mut self, point: DVec3) {
|
||||
self.sample_to(point);
|
||||
if let Some(stroke) = self.strokes.last_mut() {
|
||||
if stroke
|
||||
.last()
|
||||
.is_some_and(|last| last.distance(point) > 1.0e-9)
|
||||
{
|
||||
stroke.push(point);
|
||||
}
|
||||
}
|
||||
self.pen_down = false;
|
||||
}
|
||||
|
||||
fn lift_pen(&mut self) {
|
||||
if !self.pen_down {
|
||||
return;
|
||||
}
|
||||
if let Some(cursor) = self.last_cursor {
|
||||
self.finish_stroke(cursor);
|
||||
} else {
|
||||
self.pen_down = false;
|
||||
}
|
||||
}
|
||||
|
||||
fn connect(&mut self) {
|
||||
self.lift_pen();
|
||||
let endpoint = self
|
||||
.strokes
|
||||
.iter()
|
||||
.rev()
|
||||
.find_map(|stroke| stroke.last().copied())
|
||||
.or(self.last_recorded_endpoint);
|
||||
if let Some(endpoint) = endpoint {
|
||||
self.begin_stroke(endpoint);
|
||||
}
|
||||
}
|
||||
|
||||
/// Erasing is armed when the pointer reaches the latest temporary sample.
|
||||
/// Moving back over the recorded path then removes samples one by one.
|
||||
fn erase_at(&mut self, point: DVec3) {
|
||||
let threshold = (self.increment * 0.45).max(1.0e-6);
|
||||
loop {
|
||||
let Some(stroke) = self.strokes.last_mut() else {
|
||||
self.erase_engaged = false;
|
||||
break;
|
||||
};
|
||||
let Some(last) = stroke.last().copied() else {
|
||||
self.strokes.pop();
|
||||
continue;
|
||||
};
|
||||
if !self.erase_engaged {
|
||||
if last.distance(point) <= threshold {
|
||||
self.erase_engaged = true;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if last.distance(point) <= threshold {
|
||||
stroke.pop();
|
||||
if stroke.is_empty() {
|
||||
self.strokes.pop();
|
||||
self.erase_engaged = false;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fit_curve(&self, points: &[DVec3]) -> Option<(NurbsCurve, f64)> {
|
||||
let local: Vec<DVec3> = points
|
||||
.iter()
|
||||
.map(|point| self.plane.to_local(*point))
|
||||
.collect();
|
||||
let elevation = points[0].z;
|
||||
let mut pl = LwPolyline::new();
|
||||
pl.is_closed = false;
|
||||
pl.elevation = elevation;
|
||||
pl.vertices = points
|
||||
.iter()
|
||||
.map(|p| LwVertex::new(Vector2::new(p.x, p.y)))
|
||||
.collect();
|
||||
pl.normal = Vector3::UNIT_Z;
|
||||
Some(self.plane.place_entity(EntityType::LwPolyline(pl)))
|
||||
let elevation = local.first()?.z;
|
||||
let points: Vec<[f64; 2]> = local.iter().map(|point| [point.x, point.y]).collect();
|
||||
NurbsCurve::fit_polyline(&points, self.fit_distance())
|
||||
.map(|curve| (curve, elevation))
|
||||
}
|
||||
|
||||
/// All strokes that have enough points, as committable entities.
|
||||
fn build_all(&self) -> Vec<EntityType> {
|
||||
self.strokes
|
||||
fn fit_distance(&self) -> f64 {
|
||||
self.tolerance * self.increment
|
||||
}
|
||||
|
||||
fn build_stroke(&self, points: &[DVec3]) -> Vec<EntityType> {
|
||||
if points.len() < 2 {
|
||||
return Vec::new();
|
||||
}
|
||||
let local: Vec<DVec3> = points
|
||||
.iter()
|
||||
.filter_map(|stroke| self.build_stroke(stroke))
|
||||
.collect()
|
||||
.map(|point| self.plane.to_local(*point))
|
||||
.collect();
|
||||
match self.sketch_type {
|
||||
SketchType::Line => local
|
||||
.windows(2)
|
||||
.map(|pair| {
|
||||
self.plane.place_entity(EntityType::Line(Line::from_points(
|
||||
to_vector3(pair[0]),
|
||||
to_vector3(pair[1]),
|
||||
)))
|
||||
})
|
||||
.collect(),
|
||||
SketchType::Polyline => {
|
||||
let mut polyline = LwPolyline::new();
|
||||
polyline.elevation = local[0].z;
|
||||
polyline.normal = Vector3::UNIT_Z;
|
||||
polyline.vertices = local
|
||||
.iter()
|
||||
.map(|point| LwVertex::new(Vector2::new(point.x, point.y)))
|
||||
.collect();
|
||||
vec![self
|
||||
.plane
|
||||
.place_entity(EntityType::LwPolyline(polyline))]
|
||||
}
|
||||
SketchType::Spline => {
|
||||
let Some((curve, elevation)) = self.fit_curve(points) else {
|
||||
return Vec::new();
|
||||
};
|
||||
let mut spline = Spline {
|
||||
degree: curve.degree() as i32,
|
||||
knots: curve.knots().to_vec(),
|
||||
control_points: curve
|
||||
.control_points()
|
||||
.iter()
|
||||
.map(|point| Vector3::new(point[0], point[1], elevation))
|
||||
.collect(),
|
||||
fit_tolerance: self.fit_distance(),
|
||||
normal: Vector3::UNIT_Z,
|
||||
..Default::default()
|
||||
};
|
||||
spline.flags.planar = true;
|
||||
vec![self.plane.place_entity(EntityType::Spline(spline))]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn take_entities(&mut self) -> Vec<EntityType> {
|
||||
self.lift_pen();
|
||||
let mut entities = Vec::new();
|
||||
let mut endpoint = None;
|
||||
for stroke in &self.strokes {
|
||||
let built = self.build_stroke(stroke);
|
||||
if !built.is_empty() {
|
||||
endpoint = stroke.last().copied();
|
||||
entities.extend(built);
|
||||
}
|
||||
}
|
||||
if !entities.is_empty() {
|
||||
self.last_recorded_endpoint = endpoint;
|
||||
}
|
||||
self.strokes.clear();
|
||||
self.pen_down = false;
|
||||
self.erasing = false;
|
||||
self.erase_engaged = false;
|
||||
entities
|
||||
}
|
||||
|
||||
fn preview(&self, cursor: Option<DVec3>) -> Option<WireModel> {
|
||||
let mut combined = Vec::<[f64; 3]>::new();
|
||||
for (index, stroke) in self.strokes.iter().enumerate() {
|
||||
let mut points = stroke.clone();
|
||||
if self.pen_down && index + 1 == self.strokes.len() {
|
||||
if let Some(cursor) = cursor {
|
||||
if points
|
||||
.last()
|
||||
.is_some_and(|last| last.distance(cursor) > 1.0e-9)
|
||||
{
|
||||
points.push(cursor);
|
||||
}
|
||||
}
|
||||
}
|
||||
if points.len() < 2 {
|
||||
continue;
|
||||
}
|
||||
let display = if self.sketch_type == SketchType::Spline {
|
||||
match self.fit_curve(&points) {
|
||||
Some((curve, elevation)) => curve
|
||||
.tessellate(16)
|
||||
.into_iter()
|
||||
.map(|point| {
|
||||
self.plane
|
||||
.to_world(DVec3::new(point[0], point[1], elevation))
|
||||
})
|
||||
.collect(),
|
||||
None => points,
|
||||
}
|
||||
} else {
|
||||
points
|
||||
};
|
||||
if display.len() < 2 {
|
||||
continue;
|
||||
}
|
||||
if !combined.is_empty() {
|
||||
combined.push([f64::NAN; 3]);
|
||||
}
|
||||
combined.extend(display.into_iter().map(|point| [point.x, point.y, point.z]));
|
||||
}
|
||||
(combined.len() >= 2).then(|| {
|
||||
WireModel::solid_f64("sketch_preview".to_string(), combined, WireModel::CYAN, false)
|
||||
})
|
||||
}
|
||||
|
||||
fn toggle_pen(&mut self) {
|
||||
if self.pen_down {
|
||||
self.lift_pen();
|
||||
} else if let Some(cursor) = self.last_cursor {
|
||||
self.begin_stroke(cursor);
|
||||
}
|
||||
}
|
||||
|
||||
fn drawing_options(&self) -> Vec<CmdOption> {
|
||||
let mut options = vec![
|
||||
CmdOption::new("Pen", "PEN"),
|
||||
CmdOption::new("Type", "TYPE"),
|
||||
CmdOption::new("Increment", "INCREMENT"),
|
||||
];
|
||||
if self.sketch_type == SketchType::Spline {
|
||||
options.push(CmdOption::new("Tolerance", "TOLERANCE"));
|
||||
}
|
||||
options.extend([
|
||||
CmdOption::new("Record", "RECORD"),
|
||||
CmdOption::new(if self.erasing { "Stop erasing" } else { "Erase" }, "ERASE"),
|
||||
CmdOption::new("Connect", "CONNECT"),
|
||||
CmdOption::new("Exit", "EXIT"),
|
||||
CmdOption::new("Quit", "QUIT"),
|
||||
]);
|
||||
options
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -98,72 +366,227 @@ impl CadCommand for SketchCommand {
|
|||
}
|
||||
|
||||
fn prompt(&self) -> String {
|
||||
if self.pen_down {
|
||||
t!("SKETCH Pen down — move to sketch, click to lift, Enter to record:").into_owned()
|
||||
} else {
|
||||
t!("SKETCH Pen up — click to lower the pen, Enter to record:").into_owned()
|
||||
match self.stage {
|
||||
InputStage::Type => t!("SKETCH Object type [Line/Polyline/Spline]:").into_owned(),
|
||||
InputStage::Increment => t!(
|
||||
"SKETCH Record increment <%{value}>:",
|
||||
value = self.increment
|
||||
)
|
||||
.into_owned(),
|
||||
InputStage::Tolerance => t!(
|
||||
"SKETCH Spline fit tolerance <%{value}>:",
|
||||
value = self.tolerance
|
||||
)
|
||||
.into_owned(),
|
||||
InputStage::Drawing if self.erasing => {
|
||||
t!("SKETCH Erase — move backward over the temporary sketch:").into_owned()
|
||||
}
|
||||
InputStage::Drawing if self.pen_down => t!(
|
||||
"SKETCH (%{type}, increment %{increment}) Pen down — move to sketch:",
|
||||
type = t!(self.sketch_type.label()),
|
||||
increment = self.increment
|
||||
)
|
||||
.into_owned(),
|
||||
InputStage::Drawing => t!(
|
||||
"SKETCH (%{type}, increment %{increment}) Pen up — click to lower the pen:",
|
||||
type = t!(self.sketch_type.label()),
|
||||
increment = self.increment
|
||||
)
|
||||
.into_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
fn on_point(&mut self, pt: DVec3) -> CmdResult {
|
||||
if self.pen_down {
|
||||
// Lift the pen: the current stroke is finished. A new click later
|
||||
// begins a fresh stroke.
|
||||
self.pen_down = false;
|
||||
fn options(&self) -> Vec<CmdOption> {
|
||||
match self.stage {
|
||||
InputStage::Drawing => self.drawing_options(),
|
||||
InputStage::Type => vec![
|
||||
CmdOption::new("Line", "LINE"),
|
||||
CmdOption::new("Polyline", "POLYLINE"),
|
||||
CmdOption::new("Spline", "SPLINE"),
|
||||
],
|
||||
InputStage::Increment | InputStage::Tolerance => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn on_point(&mut self, point: DVec3) -> CmdResult {
|
||||
self.last_cursor = Some(point);
|
||||
if self.stage != InputStage::Drawing {
|
||||
return CmdResult::NeedPoint;
|
||||
}
|
||||
if self.erasing {
|
||||
self.erasing = false;
|
||||
self.erase_engaged = false;
|
||||
} else if self.pen_down {
|
||||
self.finish_stroke(point);
|
||||
} else {
|
||||
// Lower the pen: start a new stroke seeded with this point.
|
||||
self.strokes.push(vec![pt]);
|
||||
self.pen_down = true;
|
||||
self.begin_stroke(point);
|
||||
}
|
||||
CmdResult::NeedPoint
|
||||
}
|
||||
|
||||
fn on_enter(&mut self) -> CmdResult {
|
||||
let entities = self.build_all();
|
||||
match entities.len() {
|
||||
0 => CmdResult::Cancel,
|
||||
1 => CmdResult::CommitAndExit(entities.into_iter().next().unwrap()),
|
||||
_ => CmdResult::ReplaceMany(vec![], entities),
|
||||
if self.stage != InputStage::Drawing {
|
||||
self.stage = InputStage::Drawing;
|
||||
return CmdResult::NeedPoint;
|
||||
}
|
||||
let entities = self.take_entities();
|
||||
if entities.is_empty() {
|
||||
CmdResult::Cancel
|
||||
} else {
|
||||
CmdResult::CommitEntitiesAndExit(entities)
|
||||
}
|
||||
}
|
||||
|
||||
fn on_escape(&mut self) -> CmdResult {
|
||||
self.strokes.clear();
|
||||
CmdResult::Cancel
|
||||
}
|
||||
|
||||
fn on_mouse_move(&mut self, pt: DVec3) -> Option<WireModel> {
|
||||
if !self.pen_down {
|
||||
return None;
|
||||
}
|
||||
// The current stroke is the last one pushed while the pen is down.
|
||||
let stroke = self.strokes.last_mut()?;
|
||||
// Record the sample only once it has moved past the threshold from the
|
||||
// last recorded point, so the polyline isn't flooded with near-duplicate
|
||||
// vertices.
|
||||
let record = match stroke.last() {
|
||||
Some(last) => last.distance(pt) > SAMPLE_EPSILON,
|
||||
None => true,
|
||||
};
|
||||
if record {
|
||||
stroke.push(pt);
|
||||
}
|
||||
// Preview the current stroke, including the (possibly unrecorded) cursor
|
||||
// position so the line tracks the pointer smoothly.
|
||||
let mut pts: Vec<[f64; 3]> = stroke.iter().map(|p| [p.x, p.y, p.z]).collect();
|
||||
if !record {
|
||||
pts.push([pt.x, pt.y, pt.z]);
|
||||
}
|
||||
if pts.len() < 2 {
|
||||
return None;
|
||||
}
|
||||
Some(WireModel::solid_f64(
|
||||
"rubber_band".to_string(),
|
||||
pts,
|
||||
WireModel::CYAN,
|
||||
false,
|
||||
fn wants_text_input(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn point_step_accepts_keywords(&self) -> bool {
|
||||
self.stage == InputStage::Drawing
|
||||
}
|
||||
|
||||
fn sketch_settings(&self) -> Option<(i16, f64, f64)> {
|
||||
Some((
|
||||
self.sketch_type.header_value(),
|
||||
self.increment,
|
||||
self.tolerance,
|
||||
))
|
||||
}
|
||||
|
||||
fn on_text_input(&mut self, text: &str) -> Option<CmdResult> {
|
||||
let input = text.trim();
|
||||
if input.is_empty() {
|
||||
return None;
|
||||
}
|
||||
match self.stage {
|
||||
InputStage::Type => {
|
||||
if let Some(sketch_type) = SketchType::parse(input) {
|
||||
self.sketch_type = sketch_type;
|
||||
self.stage = InputStage::Drawing;
|
||||
}
|
||||
Some(CmdResult::NeedPoint)
|
||||
}
|
||||
InputStage::Increment => {
|
||||
if let Ok(value) = input.parse::<f64>() {
|
||||
if value.is_finite() && value > 0.0 {
|
||||
self.increment = value.max(MIN_INCREMENT);
|
||||
self.stage = InputStage::Drawing;
|
||||
}
|
||||
}
|
||||
Some(CmdResult::NeedPoint)
|
||||
}
|
||||
InputStage::Tolerance => {
|
||||
if let Ok(value) = input.parse::<f64>() {
|
||||
if value.is_finite() && (0.0..=1.0).contains(&value) {
|
||||
self.tolerance = value;
|
||||
self.stage = InputStage::Drawing;
|
||||
}
|
||||
}
|
||||
Some(CmdResult::NeedPoint)
|
||||
}
|
||||
InputStage::Drawing => {
|
||||
let keyword = input.to_ascii_uppercase();
|
||||
let result = match keyword.as_str() {
|
||||
"P" | "PEN" => {
|
||||
self.toggle_pen();
|
||||
CmdResult::NeedPoint
|
||||
}
|
||||
"T" | "TYPE" => {
|
||||
self.lift_pen();
|
||||
self.erasing = false;
|
||||
self.stage = InputStage::Type;
|
||||
CmdResult::NeedPoint
|
||||
}
|
||||
"I" | "INCREMENT" => {
|
||||
self.lift_pen();
|
||||
self.erasing = false;
|
||||
self.stage = InputStage::Increment;
|
||||
CmdResult::NeedPoint
|
||||
}
|
||||
"TO" | "TOLERANCE" if self.sketch_type == SketchType::Spline => {
|
||||
self.lift_pen();
|
||||
self.erasing = false;
|
||||
self.stage = InputStage::Tolerance;
|
||||
CmdResult::NeedPoint
|
||||
}
|
||||
"R" | "RECORD" => {
|
||||
let entities = self.take_entities();
|
||||
if entities.is_empty() {
|
||||
CmdResult::NeedPoint
|
||||
} else {
|
||||
CmdResult::CommitEntities(entities)
|
||||
}
|
||||
}
|
||||
"E" | "ERASE" => {
|
||||
self.lift_pen();
|
||||
self.erasing = !self.erasing;
|
||||
self.erase_engaged = false;
|
||||
CmdResult::NeedPoint
|
||||
}
|
||||
"C" | "CONNECT" => {
|
||||
self.connect();
|
||||
CmdResult::NeedPoint
|
||||
}
|
||||
"X" | "EXIT" => {
|
||||
let entities = self.take_entities();
|
||||
if entities.is_empty() {
|
||||
CmdResult::Cancel
|
||||
} else {
|
||||
CmdResult::CommitEntitiesAndExit(entities)
|
||||
}
|
||||
}
|
||||
"Q" | "QUIT" => {
|
||||
self.strokes.clear();
|
||||
CmdResult::Cancel
|
||||
}
|
||||
_ => {
|
||||
if let Some(sketch_type) = SketchType::parse(&keyword) {
|
||||
self.sketch_type = sketch_type;
|
||||
}
|
||||
CmdResult::NeedPoint
|
||||
}
|
||||
};
|
||||
Some(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn on_mouse_move(&mut self, point: DVec3) -> Option<WireModel> {
|
||||
self.last_cursor = Some(point);
|
||||
if self.stage == InputStage::Drawing {
|
||||
if self.pen_down {
|
||||
self.sample_to(point);
|
||||
} else if self.erasing {
|
||||
self.erase_at(point);
|
||||
}
|
||||
}
|
||||
self.preview(Some(point))
|
||||
}
|
||||
}
|
||||
|
||||
// ── Autocomplete registry ─────────────────────────────────
|
||||
inventory::submit!(crate::command::CommandRegistration { names: &["SKETCH"] }); // SketchCommand
|
||||
fn valid_increment(value: f64) -> f64 {
|
||||
if value.is_finite() && value > 0.0 {
|
||||
value.max(MIN_INCREMENT)
|
||||
} else {
|
||||
0.1
|
||||
}
|
||||
}
|
||||
|
||||
fn valid_tolerance(value: f64) -> f64 {
|
||||
if value.is_finite() && (0.0..=1.0).contains(&value) {
|
||||
value
|
||||
} else {
|
||||
0.5
|
||||
}
|
||||
}
|
||||
|
||||
fn to_vector3(point: DVec3) -> Vector3 {
|
||||
Vector3::new(point.x, point.y, point.z)
|
||||
}
|
||||
|
||||
inventory::submit!(crate::command::CommandRegistration { names: &["SKETCH"] });
|
||||
|
|
|
|||
Loading…
Reference in a new issue