Compare commits

...
Author SHA1 Message Date
Hakan Seven
52432af928 build(deps): align codec and kernel revisions 2026-08-23 01:45:29 +03:00
Hakan Seven
d133fcb42e Merge remote-tracking branch 'origin/main' 2026-08-23 00:31:08 +03:00
Hakan Seven
343b6cda7d feat(mline): align creation and editing 2026-08-23 00:30:41 +03:00
gianlucafiore
43c150199e
Merge pull request #861 from gianlucafiore/fix/ltscale-current-value
fix(ltscale): show current value before prompt
2026-08-22 17:24:48 -03:00
gianlucafiore
6ef5d66d9e fix(ltscale): show current value before prompt 2026-08-22 17:14:22 -03:00
Hakan Seven
d38f4cbc28
Merge pull request #859 from ramox81/codex/align-rectangle-behavior
Align rectangle command and property behavior
2026-08-22 23:04:02 +03:00
Hakan Seven
e74d5d78fc fix(rect): merge current main
Preserve both Point and Rectangle property layouts where the two pull requests changed the same section.
2026-08-22 23:03:26 +03:00
Hakan Seven
339d9afcfe
Merge pull request #857 from ramox81/codex/align-point-behavior
Align point command, properties, and display behavior
2026-08-22 22:58:42 +03:00
Hakan Seven
27cae9467a fix(rect): harden rectangle behavior
Reject invalid trim geometry instead of silently changing dimensions.\nRegister XData application IDs so rectangle metadata survives saves.
2026-08-22 22:55:36 +03:00
Hakan Seven
7293bec1b9 fix(point): preserve relative marker sizing
Keep rendering, picking, block transforms, and plotted output on the
same viewport-relative PDSIZE geometry without overloading wire width
fields.
2026-08-22 22:22:01 +03:00
ramox81
fc047b3b38 Add multiline editing workflows 2026-08-22 20:20:35 +03:00
ramox81
2263168a2a Complete multiline command and properties behavior 2026-08-22 20:20:34 +03:00
ramox81
8f3ba6dd40 Use rebuilt multiline geometry core 2026-08-22 20:20:34 +03:00
ramox81
8dc9803c02 Align rectangle property data 2026-08-22 19:26:47 +03:00
ramox81
be40b76922 Complete rectangle command behavior 2026-08-22 19:26:46 +03:00
Hakan Seven
7aa19f6869 docs: update workspace screenshot 2026-08-22 15:51:15 +03:00
Hakan Seven
e9a3a357ee docs: update modeling screenshot 2026-08-22 15:47:52 +03:00
Hakan Seven
e5b84ab8de fix(plot): honor wipeout draw order 2026-08-22 15:33:43 +03:00
114 changed files with 4897 additions and 698 deletions

4
Cargo.lock generated
View file

@ -72,7 +72,7 @@ checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618"
[[package]] [[package]]
name = "acadrust" name = "acadrust"
version = "0.4.1" version = "0.4.1"
source = "git+https://github.com/HakanSeven12/cadcodec.git?rev=64e098c#64e098cc719e93af1052d5aaa6e33a7d7a2c44f9" source = "git+https://github.com/HakanSeven12/cadcodec.git?rev=db73dab#db73dab540dbfb690c93f4416014262e6cf7bf42"
dependencies = [ dependencies = [
"ahash 0.8.12", "ahash 0.8.12",
"anyhow", "anyhow",
@ -878,7 +878,7 @@ checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
[[package]] [[package]]
name = "cadkernel" name = "cadkernel"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/HakanSeven12/cadkernel.git?rev=73ac7e0#73ac7e096c4f3013872f26372316ec83947bf71c" source = "git+https://github.com/HakanSeven12/cadkernel.git?rev=895f8b7#895f8b7b4d53845608689ead46c1a122e352b79b"
dependencies = [ dependencies = [
"acadrust", "acadrust",
"cavalier_contours", "cavalier_contours",

View file

@ -27,8 +27,8 @@ glam = { version = "0.33", features = ["bytemuck"] }
rfd = "0.17" rfd = "0.17"
clap = { version = "4", features = ["derive"] } clap = { version = "4", features = ["derive"] }
env_logger = "0.11" env_logger = "0.11"
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "64e098c", features = ["serde"] } acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "db73dab", features = ["serde"] }
cadkernel = { git = "https://github.com/HakanSeven12/cadkernel.git", rev = "73ac7e0", features = ["acis", "offset"] } cadkernel = { git = "https://github.com/HakanSeven12/cadkernel.git", rev = "895f8b7", features = ["acis", "offset"] }
dwg-thumbnailer = { path = "crates/dwg-thumbnailer" } dwg-thumbnailer = { path = "crates/dwg-thumbnailer" }
flate2 = "1" flate2 = "1"
image = { version = "0.25", default-features = false, features = ["png", "jpeg", "bmp", "tiff"] } image = { version = "0.25", default-features = false, features = ["png", "jpeg", "bmp", "tiff"] }

View file

@ -47,7 +47,7 @@
</p> </p>
<p align="center"> <p align="center">
<img src="https://github.com/user-attachments/assets/10635ad0-454b-4c87-935f-1a3a46f24ccb" alt="Open CAD Studio workspace" width="100%"> <img src="site/workspace.png" alt="Open CAD Studio workspace" width="100%">
</p> </p>
## Overview ## Overview
@ -66,7 +66,7 @@ The project is under active development. Keep backups of important production dr
- **Extensible workflows** — native plugins, command scripts, headless conversion, and a line-based JSON automation API. - **Extensible workflows** — native plugins, command scripts, headless conversion, and a line-based JSON automation API.
<p align="center"> <p align="center">
<img src="https://github.com/user-attachments/assets/2a037a09-e8e8-498c-8ed3-58ecb8ae958d" alt="3D model in Open CAD Studio" width="100%"> <img src="site/modeling.png" alt="3D model in Open CAD Studio" width="100%">
</p> </p>
## File workflows ## File workflows

View file

@ -14,7 +14,7 @@ serde = { version = "1", features = ["derive"] }
# Pulled in only by the `host` feature, which adds the `acadrust`-typed # Pulled in only by the `host` feature, which adds the `acadrust`-typed
# `HostApi` runtime surface. The default crate stays dependency-free so engine # `HostApi` runtime surface. The default crate stays dependency-free so engine
# crates and external tooling can depend on the manifest/ribbon contract cheaply. # crates and external tooling can depend on the manifest/ribbon contract cheaply.
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "64e098c", optional = true, features = ["serde"] } acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "db73dab", optional = true, features = ["serde"] }
# Runtime IPC and serialization (host feature only). # Runtime IPC and serialization (host feature only).
interprocess = { version = "2", optional = true } interprocess = { version = "2", optional = true }
@ -37,7 +37,7 @@ serde_json = "1"
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
cargo-lock = "11" cargo-lock = "11"
# acadrust is scanned at build time to generate the embedded type registry. # acadrust is scanned at build time to generate the embedded type registry.
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "64e098c", features = ["serde"] } acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "db73dab", features = ["serde"] }
[dev-dependencies] [dev-dependencies]
serde_json = "1" serde_json = "1"

View file

@ -8,7 +8,7 @@ publish = false
crate-type = ["cdylib"] crate-type = ["cdylib"]
[dependencies] [dependencies]
acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "64e098c", features = ["serde"] } acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "db73dab", features = ["serde"] }
bincode = "1.3" bincode = "1.3"
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
console_error_panic_hook = "0.1" console_error_panic_hook = "0.1"

View file

@ -19,7 +19,7 @@
<a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>الانضمام إلى النقاش</strong></a> <a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>الانضمام إلى النقاش</strong></a>
</p> </p>
<p align="center"><img src="https://github.com/user-attachments/assets/10635ad0-454b-4c87-935f-1a3a46f24ccb" alt="مساحة عمل Open CAD Studio" width="100%"></p> <p align="center"><img src="../../site/workspace.png" alt="مساحة عمل Open CAD Studio" width="100%"></p>
## نظرة عامة ## نظرة عامة
@ -36,7 +36,7 @@ Open CAD Studio تطبيق متعدد المنصات للرسم التقني و
- **تصيير عبر GPU** — إطارات عرض ثنائية وثلاثية الأبعاد مسرّعة بواسطة `wgpu` مع كاميرات متعامدة ومنظورية. - **تصيير عبر GPU** — إطارات عرض ثنائية وثلاثية الأبعاد مسرّعة بواسطة `wgpu` مع كاميرات متعامدة ومنظورية.
- **سير عمل قابل للتوسعة** — إضافات أصلية ونصوص أوامر وتحويل من دون واجهة وواجهة JSON للأتمتة تعتمد على الأسطر. - **سير عمل قابل للتوسعة** — إضافات أصلية ونصوص أوامر وتحويل من دون واجهة وواجهة JSON للأتمتة تعتمد على الأسطر.
<p align="center"><img src="https://github.com/user-attachments/assets/2a037a09-e8e8-498c-8ed3-58ecb8ae958d" alt="نموذج ثلاثي الأبعاد في Open CAD Studio" width="100%"></p> <p align="center"><img src="../../site/modeling.png" alt="نموذج ثلاثي الأبعاد في Open CAD Studio" width="100%"></p>
## سير عمل الملفات ## سير عمل الملفات

View file

@ -21,7 +21,7 @@
<a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>Присъединете се към дискусията</strong></a> <a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>Присъединете се към дискусията</strong></a>
</p> </p>
<p align="center"><img src="https://github.com/user-attachments/assets/10635ad0-454b-4c87-935f-1a3a46f24ccb" alt="Работно пространство на Open CAD Studio" width="100%"></p> <p align="center"><img src="../../site/workspace.png" alt="Работно пространство на Open CAD Studio" width="100%"></p>
## Общ преглед ## Общ преглед
@ -38,7 +38,7 @@ Open CAD Studio е междуплатформено приложение за т
- **GPU визуализация** — ускорени 2D и 3D изгледи чрез `wgpu`, с ортографски и перспективни камери. - **GPU визуализация** — ускорени 2D и 3D изгледи чрез `wgpu`, с ортографски и перспективни камери.
- **Разширяеми работни процеси** — локални приставки, командни скриптове, преобразуване без графичен интерфейс и редово ориентиран JSON API за автоматизация. - **Разширяеми работни процеси** — локални приставки, командни скриптове, преобразуване без графичен интерфейс и редово ориентиран JSON API за автоматизация.
<p align="center"><img src="https://github.com/user-attachments/assets/2a037a09-e8e8-498c-8ed3-58ecb8ae958d" alt="3D модел в Open CAD Studio" width="100%"></p> <p align="center"><img src="../../site/modeling.png" alt="3D модел в Open CAD Studio" width="100%"></p>
## Работа с файлове ## Работа с файлове

View file

@ -19,7 +19,7 @@
<a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>Zapojit se do diskuse</strong></a> <a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>Zapojit se do diskuse</strong></a>
</p> </p>
<p align="center"><img src="https://github.com/user-attachments/assets/10635ad0-454b-4c87-935f-1a3a46f24ccb" alt="Pracovní plocha Open CAD Studio" width="100%"></p> <p align="center"><img src="../../site/workspace.png" alt="Pracovní plocha Open CAD Studio" width="100%"></p>
## Přehled ## Přehled
@ -36,7 +36,7 @@ Projekt je aktivně vyvíjen. Důležité produkční výkresy zálohujte a repr
- **Vykreslování přes GPU** — akcelerované 2D a 3D pohledy pomocí `wgpu`, s ortografickou a perspektivní kamerou. - **Vykreslování přes GPU** — akcelerované 2D a 3D pohledy pomocí `wgpu`, s ortografickou a perspektivní kamerou.
- **Rozšiřitelné postupy** — nativní pluginy, příkazové skripty, bezobslužná konverze a řádkové JSON automatizační API. - **Rozšiřitelné postupy** — nativní pluginy, příkazové skripty, bezobslužná konverze a řádkové JSON automatizační API.
<p align="center"><img src="https://github.com/user-attachments/assets/2a037a09-e8e8-498c-8ed3-58ecb8ae958d" alt="3D model v Open CAD Studio" width="100%"></p> <p align="center"><img src="../../site/modeling.png" alt="3D model v Open CAD Studio" width="100%"></p>
## Práce se soubory ## Práce se soubory

View file

@ -19,7 +19,7 @@
<a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>An Diskussionen teilnehmen</strong></a> <a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>An Diskussionen teilnehmen</strong></a>
</p> </p>
<p align="center"><img src="https://github.com/user-attachments/assets/10635ad0-454b-4c87-935f-1a3a46f24ccb" alt="Arbeitsbereich von Open CAD Studio" width="100%"></p> <p align="center"><img src="../../site/workspace.png" alt="Arbeitsbereich von Open CAD Studio" width="100%"></p>
## Überblick ## Überblick
@ -36,7 +36,7 @@ Das Projekt wird aktiv entwickelt. Bewahre Sicherungskopien wichtiger Produktion
- **GPU-Darstellung** — beschleunigte 2D- und 3D-Ansichten über `wgpu` mit orthografischen und perspektivischen Kameras. - **GPU-Darstellung** — beschleunigte 2D- und 3D-Ansichten über `wgpu` mit orthografischen und perspektivischen Kameras.
- **Erweiterbare Abläufe** — native Plugins, Befehlsskripte, Konvertierung ohne Oberfläche und zeilenbasierte JSON-Automatisierungs-API. - **Erweiterbare Abläufe** — native Plugins, Befehlsskripte, Konvertierung ohne Oberfläche und zeilenbasierte JSON-Automatisierungs-API.
<p align="center"><img src="https://github.com/user-attachments/assets/2a037a09-e8e8-498c-8ed3-58ecb8ae958d" alt="3D-Modell in Open CAD Studio" width="100%"></p> <p align="center"><img src="../../site/modeling.png" alt="3D-Modell in Open CAD Studio" width="100%"></p>
## Dateiabläufe ## Dateiabläufe

View file

@ -19,7 +19,7 @@
<a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>Participar en la conversación</strong></a> <a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>Participar en la conversación</strong></a>
</p> </p>
<p align="center"><img src="https://github.com/user-attachments/assets/10635ad0-454b-4c87-935f-1a3a46f24ccb" alt="Espacio de trabajo de Open CAD Studio" width="100%"></p> <p align="center"><img src="../../site/workspace.png" alt="Espacio de trabajo de Open CAD Studio" width="100%"></p>
## Descripción general ## Descripción general
@ -36,7 +36,7 @@ El proyecto está en desarrollo activo. Conserva copias de seguridad de los dibu
- **Renderizado por GPU** — vistas 2D y 3D aceleradas mediante `wgpu`, con cámaras ortográfica y en perspectiva. - **Renderizado por GPU** — vistas 2D y 3D aceleradas mediante `wgpu`, con cámaras ortográfica y en perspectiva.
- **Flujos ampliables** — complementos nativos, scripts de comandos, conversión sin interfaz y una API de automatización JSON basada en líneas. - **Flujos ampliables** — complementos nativos, scripts de comandos, conversión sin interfaz y una API de automatización JSON basada en líneas.
<p align="center"><img src="https://github.com/user-attachments/assets/2a037a09-e8e8-498c-8ed3-58ecb8ae958d" alt="Modelo 3D en Open CAD Studio" width="100%"></p> <p align="center"><img src="../../site/modeling.png" alt="Modelo 3D en Open CAD Studio" width="100%"></p>
## Flujos de archivos ## Flujos de archivos

View file

@ -19,7 +19,7 @@
<a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>Osallistu keskusteluun</strong></a> <a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>Osallistu keskusteluun</strong></a>
</p> </p>
<p align="center"><img src="https://github.com/user-attachments/assets/10635ad0-454b-4c87-935f-1a3a46f24ccb" alt="Open CAD Studion työtila" width="100%"></p> <p align="center"><img src="../../site/workspace.png" alt="Open CAD Studion työtila" width="100%"></p>
## Yleiskatsaus ## Yleiskatsaus
@ -36,7 +36,7 @@ Projektia kehitetään aktiivisesti. Säilytä tärkeistä tuotantopiirustuksist
- **GPU-renderöinti**`wgpu`:n kiihdyttämät 2D- ja 3D-näkymät sekä ortografiset ja perspektiivikamerat. - **GPU-renderöinti**`wgpu`:n kiihdyttämät 2D- ja 3D-näkymät sekä ortografiset ja perspektiivikamerat.
- **Laajennettavat työnkulut** — natiiviliitännäiset, komentoskriptit, käyttöliittymätön muunnos ja rivipohjainen JSON-automaatiorajapinta. - **Laajennettavat työnkulut** — natiiviliitännäiset, komentoskriptit, käyttöliittymätön muunnos ja rivipohjainen JSON-automaatiorajapinta.
<p align="center"><img src="https://github.com/user-attachments/assets/2a037a09-e8e8-498c-8ed3-58ecb8ae958d" alt="3D-malli Open CAD Studiossa" width="100%"></p> <p align="center"><img src="../../site/modeling.png" alt="3D-malli Open CAD Studiossa" width="100%"></p>
## Tiedostotyönkulut ## Tiedostotyönkulut

View file

@ -19,7 +19,7 @@
<a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>Participer aux discussions</strong></a> <a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>Participer aux discussions</strong></a>
</p> </p>
<p align="center"><img src="https://github.com/user-attachments/assets/10635ad0-454b-4c87-935f-1a3a46f24ccb" alt="Espace de travail Open CAD Studio" width="100%"></p> <p align="center"><img src="../../site/workspace.png" alt="Espace de travail Open CAD Studio" width="100%"></p>
## Présentation ## Présentation
@ -36,7 +36,7 @@ Le projet est en développement actif. Conservez des sauvegardes des dessins de
- **Rendu GPU** — vues 2D et 3D accélérées par `wgpu`, avec caméras orthographique et perspective. - **Rendu GPU** — vues 2D et 3D accélérées par `wgpu`, avec caméras orthographique et perspective.
- **Flux extensibles** — plugins natifs, scripts de commandes, conversion sans interface et API dautomatisation JSON ligne par ligne. - **Flux extensibles** — plugins natifs, scripts de commandes, conversion sans interface et API dautomatisation JSON ligne par ligne.
<p align="center"><img src="https://github.com/user-attachments/assets/2a037a09-e8e8-498c-8ed3-58ecb8ae958d" alt="Modèle 3D dans Open CAD Studio" width="100%"></p> <p align="center"><img src="../../site/modeling.png" alt="Modèle 3D dans Open CAD Studio" width="100%"></p>
## Flux de fichiers ## Flux de fichiers

View file

@ -19,7 +19,7 @@
<a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>चर्चा में शामिल हों</strong></a> <a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>चर्चा में शामिल हों</strong></a>
</p> </p>
<p align="center"><img src="https://github.com/user-attachments/assets/10635ad0-454b-4c87-935f-1a3a46f24ccb" alt="Open CAD Studio कार्यक्षेत्र" width="100%"></p> <p align="center"><img src="../../site/workspace.png" alt="Open CAD Studio कार्यक्षेत्र" width="100%"></p>
## परिचय ## परिचय
@ -36,7 +36,7 @@ Open CAD Studio तकनीकी ड्राइंग, लेआउट का
- **GPU रेंडरिंग**`wgpu` से तेज़ किए गए 2D और 3D व्यूपोर्ट, ऑर्थोग्राफ़िक और पर्सपेक्टिव कैमरा सहित। - **GPU रेंडरिंग**`wgpu` से तेज़ किए गए 2D और 3D व्यूपोर्ट, ऑर्थोग्राफ़िक और पर्सपेक्टिव कैमरा सहित।
- **विस्तार योग्य कार्यप्रवाह** — मूल प्लगइन, कमांड स्क्रिप्ट, हेडलेस रूपांतरण और पंक्ति-आधारित JSON स्वचालन API। - **विस्तार योग्य कार्यप्रवाह** — मूल प्लगइन, कमांड स्क्रिप्ट, हेडलेस रूपांतरण और पंक्ति-आधारित JSON स्वचालन API।
<p align="center"><img src="https://github.com/user-attachments/assets/2a037a09-e8e8-498c-8ed3-58ecb8ae958d" alt="Open CAD Studio में 3D मॉडल" width="100%"></p> <p align="center"><img src="../../site/modeling.png" alt="Open CAD Studio में 3D मॉडल" width="100%"></p>
## फ़ाइल कार्यप्रवाह ## फ़ाइल कार्यप्रवाह

View file

@ -19,7 +19,7 @@
<a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>Csatlakozás a beszélgetéshez</strong></a> <a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>Csatlakozás a beszélgetéshez</strong></a>
</p> </p>
<p align="center"><img src="https://github.com/user-attachments/assets/10635ad0-454b-4c87-935f-1a3a46f24ccb" alt="Open CAD Studio munkaterület" width="100%"></p> <p align="center"><img src="../../site/workspace.png" alt="Open CAD Studio munkaterület" width="100%"></p>
## Áttekintés ## Áttekintés
@ -36,7 +36,7 @@ A projekt aktív fejlesztés alatt áll. A fontos gyártási rajzokról tarts bi
- **GPU-megjelenítés**`wgpu` által gyorsított 2D és 3D nézetek, ortografikus és perspektivikus kamerákkal. - **GPU-megjelenítés**`wgpu` által gyorsított 2D és 3D nézetek, ortografikus és perspektivikus kamerákkal.
- **Bővíthető munkafolyamatok** — natív bővítmények, parancsfájlok, felület nélküli konverzió és soralapú JSON automatizálási API. - **Bővíthető munkafolyamatok** — natív bővítmények, parancsfájlok, felület nélküli konverzió és soralapú JSON automatizálási API.
<p align="center"><img src="https://github.com/user-attachments/assets/2a037a09-e8e8-498c-8ed3-58ecb8ae958d" alt="3D modell az Open CAD Studióban" width="100%"></p> <p align="center"><img src="../../site/modeling.png" alt="3D modell az Open CAD Studióban" width="100%"></p>
## Fájlmunkafolyamatok ## Fájlmunkafolyamatok

View file

@ -19,7 +19,7 @@
<a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>Partecipa alla discussione</strong></a> <a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>Partecipa alla discussione</strong></a>
</p> </p>
<p align="center"><img src="https://github.com/user-attachments/assets/10635ad0-454b-4c87-935f-1a3a46f24ccb" alt="Area di lavoro di Open CAD Studio" width="100%"></p> <p align="center"><img src="../../site/workspace.png" alt="Area di lavoro di Open CAD Studio" width="100%"></p>
## Panoramica ## Panoramica
@ -36,7 +36,7 @@ Il progetto è in sviluppo attivo. Conserva copie di sicurezza dei disegni di pr
- **Rendering GPU** — viste 2D e 3D accelerate tramite `wgpu`, con telecamere ortografiche e prospettiche. - **Rendering GPU** — viste 2D e 3D accelerate tramite `wgpu`, con telecamere ortografiche e prospettiche.
- **Flussi estensibili** — plugin nativi, script di comandi, conversione headless e API di automazione JSON basata su righe. - **Flussi estensibili** — plugin nativi, script di comandi, conversione headless e API di automazione JSON basata su righe.
<p align="center"><img src="https://github.com/user-attachments/assets/2a037a09-e8e8-498c-8ed3-58ecb8ae958d" alt="Modello 3D in Open CAD Studio" width="100%"></p> <p align="center"><img src="../../site/modeling.png" alt="Modello 3D in Open CAD Studio" width="100%"></p>
## Flussi dei file ## Flussi dei file

View file

@ -19,7 +19,7 @@
<a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>ディスカッションに参加</strong></a> <a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>ディスカッションに参加</strong></a>
</p> </p>
<p align="center"><img src="https://github.com/user-attachments/assets/10635ad0-454b-4c87-935f-1a3a46f24ccb" alt="Open CAD Studio ワークスペース" width="100%"></p> <p align="center"><img src="../../site/workspace.png" alt="Open CAD Studio ワークスペース" width="100%"></p>
## 概要 ## 概要
@ -36,7 +36,7 @@ Open CAD Studio は、技術製図、レイアウト作業、ソリッドモデ
- **GPU レンダリング**`wgpu` による高速な 2D/3D ビューポートと、正投影・透視投影カメラ。 - **GPU レンダリング**`wgpu` による高速な 2D/3D ビューポートと、正投影・透視投影カメラ。
- **拡張可能なワークフロー** — ネイティブプラグイン、コマンドスクリプト、ヘッドレス変換、行単位の JSON 自動化 API。 - **拡張可能なワークフロー** — ネイティブプラグイン、コマンドスクリプト、ヘッドレス変換、行単位の JSON 自動化 API。
<p align="center"><img src="https://github.com/user-attachments/assets/2a037a09-e8e8-498c-8ed3-58ecb8ae958d" alt="Open CAD Studio の 3D モデル" width="100%"></p> <p align="center"><img src="../../site/modeling.png" alt="Open CAD Studio の 3D モデル" width="100%"></p>
## ファイルワークフロー ## ファイルワークフロー

View file

@ -19,7 +19,7 @@
<a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>토론 참여</strong></a> <a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>토론 참여</strong></a>
</p> </p>
<p align="center"><img src="https://github.com/user-attachments/assets/10635ad0-454b-4c87-935f-1a3a46f24ccb" alt="Open CAD Studio 작업 공간" width="100%"></p> <p align="center"><img src="../../site/workspace.png" alt="Open CAD Studio 작업 공간" width="100%"></p>
## 개요 ## 개요
@ -36,7 +36,7 @@ Open CAD Studio는 기술 도면 작성, 배치 작업, 솔리드 모델링을
- **GPU 렌더링**`wgpu`로 가속된 2D 및 3D 뷰포트와 직교·원근 카메라를 제공합니다. - **GPU 렌더링**`wgpu`로 가속된 2D 및 3D 뷰포트와 직교·원근 카메라를 제공합니다.
- **확장 가능한 작업 흐름** — 네이티브 플러그인, 명령 스크립트, 헤드리스 변환, 줄 단위 JSON 자동화 API를 지원합니다. - **확장 가능한 작업 흐름** — 네이티브 플러그인, 명령 스크립트, 헤드리스 변환, 줄 단위 JSON 자동화 API를 지원합니다.
<p align="center"><img src="https://github.com/user-attachments/assets/2a037a09-e8e8-498c-8ed3-58ecb8ae958d" alt="Open CAD Studio의 3D 모델" width="100%"></p> <p align="center"><img src="../../site/modeling.png" alt="Open CAD Studio의 3D 모델" width="100%"></p>
## 파일 작업 흐름 ## 파일 작업 흐름

View file

@ -19,7 +19,7 @@
<a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>Deelnemen aan discussies</strong></a> <a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>Deelnemen aan discussies</strong></a>
</p> </p>
<p align="center"><img src="https://github.com/user-attachments/assets/10635ad0-454b-4c87-935f-1a3a46f24ccb" alt="Werkruimte van Open CAD Studio" width="100%"></p> <p align="center"><img src="../../site/workspace.png" alt="Werkruimte van Open CAD Studio" width="100%"></p>
## Overzicht ## Overzicht
@ -36,7 +36,7 @@ Het project wordt actief ontwikkeld. Bewaar reservekopieën van belangrijke prod
- **GPU-rendering** — versnelde 2D- en 3D-viewports via `wgpu`, met orthografische en perspectiefcamera's. - **GPU-rendering** — versnelde 2D- en 3D-viewports via `wgpu`, met orthografische en perspectiefcamera's.
- **Uitbreidbare workflows** — native plug-ins, opdrachtscripts, headless conversie en een regelgebaseerde JSON-automatiserings-API. - **Uitbreidbare workflows** — native plug-ins, opdrachtscripts, headless conversie en een regelgebaseerde JSON-automatiserings-API.
<p align="center"><img src="https://github.com/user-attachments/assets/2a037a09-e8e8-498c-8ed3-58ecb8ae958d" alt="3D-model in Open CAD Studio" width="100%"></p> <p align="center"><img src="../../site/modeling.png" alt="3D-model in Open CAD Studio" width="100%"></p>
## Bestandsworkflows ## Bestandsworkflows

View file

@ -19,7 +19,7 @@
<a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>Dołącz do dyskusji</strong></a> <a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>Dołącz do dyskusji</strong></a>
</p> </p>
<p align="center"><img src="https://github.com/user-attachments/assets/10635ad0-454b-4c87-935f-1a3a46f24ccb" alt="Obszar roboczy Open CAD Studio" width="100%"></p> <p align="center"><img src="../../site/workspace.png" alt="Obszar roboczy Open CAD Studio" width="100%"></p>
## Przegląd ## Przegląd
@ -36,7 +36,7 @@ Projekt jest aktywnie rozwijany. Zachowuj kopie zapasowe ważnych rysunków prod
- **Renderowanie GPU** — przyspieszone widoki 2D i 3D przez `wgpu`, z kamerą ortograficzną i perspektywiczną. - **Renderowanie GPU** — przyspieszone widoki 2D i 3D przez `wgpu`, z kamerą ortograficzną i perspektywiczną.
- **Rozszerzalne przepływy pracy** — natywne wtyczki, skrypty poleceń, konwersja bez interfejsu i wierszowe API automatyzacji JSON. - **Rozszerzalne przepływy pracy** — natywne wtyczki, skrypty poleceń, konwersja bez interfejsu i wierszowe API automatyzacji JSON.
<p align="center"><img src="https://github.com/user-attachments/assets/2a037a09-e8e8-498c-8ed3-58ecb8ae958d" alt="Model 3D w Open CAD Studio" width="100%"></p> <p align="center"><img src="../../site/modeling.png" alt="Model 3D w Open CAD Studio" width="100%"></p>
## Przepływy plików ## Przepływy plików

View file

@ -19,7 +19,7 @@
<a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>Participar da discussão</strong></a> <a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>Participar da discussão</strong></a>
</p> </p>
<p align="center"><img src="https://github.com/user-attachments/assets/10635ad0-454b-4c87-935f-1a3a46f24ccb" alt="Área de trabalho do Open CAD Studio" width="100%"></p> <p align="center"><img src="../../site/workspace.png" alt="Área de trabalho do Open CAD Studio" width="100%"></p>
## Visão geral ## Visão geral
@ -36,7 +36,7 @@ O projeto está em desenvolvimento ativo. Mantenha cópias de segurança de dese
- **Renderização por GPU** — viewports 2D e 3D aceleradas por `wgpu`, com câmeras ortográfica e em perspectiva. - **Renderização por GPU** — viewports 2D e 3D aceleradas por `wgpu`, com câmeras ortográfica e em perspectiva.
- **Fluxos extensíveis** — plugins nativos, scripts de comandos, conversão sem interface e API de automação JSON baseada em linhas. - **Fluxos extensíveis** — plugins nativos, scripts de comandos, conversão sem interface e API de automação JSON baseada em linhas.
<p align="center"><img src="https://github.com/user-attachments/assets/2a037a09-e8e8-498c-8ed3-58ecb8ae958d" alt="Modelo 3D no Open CAD Studio" width="100%"></p> <p align="center"><img src="../../site/modeling.png" alt="Modelo 3D no Open CAD Studio" width="100%"></p>
## Fluxos de arquivos ## Fluxos de arquivos

View file

@ -19,7 +19,7 @@
<a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>Присоединиться к обсуждению</strong></a> <a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>Присоединиться к обсуждению</strong></a>
</p> </p>
<p align="center"><img src="https://github.com/user-attachments/assets/10635ad0-454b-4c87-935f-1a3a46f24ccb" alt="Рабочее пространство Open CAD Studio" width="100%"></p> <p align="center"><img src="../../site/workspace.png" alt="Рабочее пространство Open CAD Studio" width="100%"></p>
## Обзор ## Обзор
@ -36,7 +36,7 @@ Open CAD Studio — кроссплатформенное приложение д
- **Отрисовка на GPU** — ускоренные 2D- и 3D-виды через `wgpu`, ортографическая и перспективная камеры. - **Отрисовка на GPU** — ускоренные 2D- и 3D-виды через `wgpu`, ортографическая и перспективная камеры.
- **Расширяемые процессы** — нативные плагины, сценарии команд, преобразование без интерфейса и построчный JSON API автоматизации. - **Расширяемые процессы** — нативные плагины, сценарии команд, преобразование без интерфейса и построчный JSON API автоматизации.
<p align="center"><img src="https://github.com/user-attachments/assets/2a037a09-e8e8-498c-8ed3-58ecb8ae958d" alt="3D-модель в Open CAD Studio" width="100%"></p> <p align="center"><img src="../../site/modeling.png" alt="3D-модель в Open CAD Studio" width="100%"></p>
## Работа с файлами ## Работа с файлами

View file

@ -21,7 +21,7 @@
<a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>Tartışmalara katıl</strong></a> <a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>Tartışmalara katıl</strong></a>
</p> </p>
<p align="center"><img src="https://github.com/user-attachments/assets/10635ad0-454b-4c87-935f-1a3a46f24ccb" alt="Open CAD Studio çalışma alanı" width="100%"></p> <p align="center"><img src="../../site/workspace.png" alt="Open CAD Studio çalışma alanı" width="100%"></p>
## Genel bakış ## Genel bakış
@ -38,7 +38,7 @@ Proje aktif olarak geliştirilmektedir. Önemli üretim çizimlerinin yedeklerin
- **GPU ile görüntüleme**`wgpu` üzerinden hızlandırılmış 2B ve 3B görünüm pencereleri; ortografik ve perspektif kameralar. - **GPU ile görüntüleme**`wgpu` üzerinden hızlandırılmış 2B ve 3B görünüm pencereleri; ortografik ve perspektif kameralar.
- **Genişletilebilir iş akışları** — yerel eklentiler, komut betikleri, arayüzsüz dönüştürme ve satır tabanlı JSON otomasyon API'si. - **Genişletilebilir iş akışları** — yerel eklentiler, komut betikleri, arayüzsüz dönüştürme ve satır tabanlı JSON otomasyon API'si.
<p align="center"><img src="https://github.com/user-attachments/assets/2a037a09-e8e8-498c-8ed3-58ecb8ae958d" alt="Open CAD Studio içinde 3B model" width="100%"></p> <p align="center"><img src="../../site/modeling.png" alt="Open CAD Studio içinde 3B model" width="100%"></p>
## Dosya iş akışları ## Dosya iş akışları

View file

@ -19,7 +19,7 @@
<a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>参与讨论</strong></a> <a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>参与讨论</strong></a>
</p> </p>
<p align="center"><img src="https://github.com/user-attachments/assets/10635ad0-454b-4c87-935f-1a3a46f24ccb" alt="Open CAD Studio 工作区" width="100%"></p> <p align="center"><img src="../../site/workspace.png" alt="Open CAD Studio 工作区" width="100%"></p>
## 概览 ## 概览
@ -36,7 +36,7 @@ Open CAD Studio 是一款用于技术绘图、布局设计和实体建模的跨
- **GPU 渲染** — 通过 `wgpu` 加速二维和三维视口,并支持正交与透视相机。 - **GPU 渲染** — 通过 `wgpu` 加速二维和三维视口,并支持正交与透视相机。
- **可扩展工作流** — 支持原生插件、命令脚本、无界面转换和逐行 JSON 自动化 API。 - **可扩展工作流** — 支持原生插件、命令脚本、无界面转换和逐行 JSON 自动化 API。
<p align="center"><img src="https://github.com/user-attachments/assets/2a037a09-e8e8-498c-8ed3-58ecb8ae958d" alt="Open CAD Studio 中的三维模型" width="100%"></p> <p align="center"><img src="../../site/modeling.png" alt="Open CAD Studio 中的三维模型" width="100%"></p>
## 文件工作流 ## 文件工作流

View file

@ -19,7 +19,7 @@
<a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>參與討論</strong></a> <a href="https://github.com/HakanSeven12/OpenCADStudio/discussions"><strong>參與討論</strong></a>
</p> </p>
<p align="center"><img src="https://github.com/user-attachments/assets/10635ad0-454b-4c87-935f-1a3a46f24ccb" alt="Open CAD Studio 工作區" width="100%"></p> <p align="center"><img src="../../site/workspace.png" alt="Open CAD Studio 工作區" width="100%"></p>
## 概覽 ## 概覽
@ -36,7 +36,7 @@ Open CAD Studio 是用於技術繪圖、配置作業和實體建模的跨平台
- **GPU 轉譯** — 透過 `wgpu` 加速二維和三維視埠,並支援正投影與透視相機。 - **GPU 轉譯** — 透過 `wgpu` 加速二維和三維視埠,並支援正投影與透視相機。
- **可擴充工作流程** — 支援原生外掛程式、指令碼、無介面轉換和逐行 JSON 自動化 API。 - **可擴充工作流程** — 支援原生外掛程式、指令碼、無介面轉換和逐行 JSON 自動化 API。
<p align="center"><img src="https://github.com/user-attachments/assets/2a037a09-e8e8-498c-8ed3-58ecb8ae958d" alt="Open CAD Studio 中的三維模型" width="100%"></p> <p align="center"><img src="../../site/modeling.png" alt="Open CAD Studio 中的三維模型" width="100%"></p>
## 檔案工作流程 ## 檔案工作流程

View file

@ -326,6 +326,7 @@ common =
.block-rotation = دوران الكتلة .block-rotation = دوران الكتلة
.blocks = الكتل .blocks = الكتل
.blue = أزرق .blue = أزرق
.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 = العملية المنطقية: حدد مجسمين بالضبط أُنشئا في هذه الجلسة.
.bottom = أسفل .bottom = أسفل
@ -1341,6 +1342,18 @@ draw =
.ray-specify-through-point = RAY حدد نقطة العبور: .ray-specify-through-point = RAY حدد نقطة العبور:
.rect-specify-first-corner = RECT حدد الزاوية الأولى: .rect-specify-first-corner = RECT حدد الزاوية الأولى:
.rect-specify-opposite-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-center-point = RECT CEN حدد نقطة المركز:
.rect-cen-specify-corner-point = RECT CEN حدد نقطة الزاوية: .rect-cen-specify-corner-point = RECT CEN حدد نقطة الزاوية:
.rect-rot-specify-adjacent-corner-defines-edge = RECT ROT حدد الزاوية المجاورة (تحدد اتجاه الحافة): .rect-rot-specify-adjacent-corner-defines-edge = RECT ROT حدد الزاوية المجاورة (تحدد اتجاه الحافة):
@ -3073,7 +3086,7 @@ styles =
.pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE القيمة الجديدة [0=نقطة 1=لا شيء 2=+ 3=x 4=شرطة؛ +32 دائرة +64 مربع]: .pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE القيمة الجديدة [0=نقطة 1=لا شيء 2=+ 3=x 4=شرطة؛ +32 دائرة +64 مربع]:
.pdmode-v = PDMODE = __ocs_fmt_0__ .pdmode-v = PDMODE = __ocs_fmt_0__
.pdmode-set-to-v = عُيّن PDMODE إلى __ocs_fmt_0__ .pdmode-set-to-v = عُيّن PDMODE إلى __ocs_fmt_0__
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE حجم النقطة الجديد (0 = 5% من منفذ العرض، <0 = مطلق): .pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE حجم النقطة الجديد (0 = 5% من منفذ العرض، >0 = مطلق، <0 = نسبة مئوية من منفذ العرض):
.pdsize-v-4 = PDSIZE = __ocs_fmt_0__ .pdsize-v-4 = PDSIZE = __ocs_fmt_0__
.pdsize-set-to-v-4 = عُيّن PDSIZE إلى __ocs_fmt_0__ .pdsize-set-to-v-4 = عُيّن PDSIZE إلى __ocs_fmt_0__
.pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__) .pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__)

View file

@ -313,6 +313,7 @@ common =
.block-rotation = Завъртане на блок .block-rotation = Завъртане на блок
.blocks = Блокове .blocks = Блокове
.blue = Синьо .blue = Синьо
.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 = Булева операция: избери точно две тела, създадени в тази сесия.
.bottom = Долу .bottom = Долу
@ -1328,6 +1329,18 @@ draw =
.ray-specify-through-point = RAY Посочи точка на преминаване: .ray-specify-through-point = RAY Посочи точка на преминаване:
.rect-specify-first-corner = RECT Посочи първи ъгъл: .rect-specify-first-corner = RECT Посочи първи ъгъл:
.rect-specify-opposite-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-center-point = RECT CEN Посочи централна точка:
.rect-cen-specify-corner-point = RECT CEN Посочи ъглова точка: .rect-cen-specify-corner-point = RECT CEN Посочи ъглова точка:
.rect-rot-specify-adjacent-corner-defines-edge = RECT ROT Посочи съседен ъгъл (определя посоката на страната): .rect-rot-specify-adjacent-corner-defines-edge = RECT ROT Посочи съседен ъгъл (определя посоката на страната):
@ -3061,7 +3074,7 @@ styles =
.pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE нова стойност [0=точка 1=нищо 2=+ 3=x 4=чертичка; +32 окръжност +64 квадрат]: .pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE нова стойност [0=точка 1=нищо 2=+ 3=x 4=чертичка; +32 окръжност +64 квадрат]:
.pdmode-v = PDMODE = __ocs_fmt_0__ .pdmode-v = PDMODE = __ocs_fmt_0__
.pdmode-set-to-v = PDMODE зададен на __ocs_fmt_0__ .pdmode-set-to-v = PDMODE зададен на __ocs_fmt_0__
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE нов размер на точка (0 = 5% от изгледа, <0 = абсолютно): .pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE нов размер на точка (0 = 5% от изгледа, >0 = абсолютен, <0 = процент от изгледа):
.pdsize-v-4 = PDSIZE = __ocs_fmt_0__ .pdsize-v-4 = PDSIZE = __ocs_fmt_0__
.pdsize-set-to-v-4 = PDSIZE зададен на __ocs_fmt_0__ .pdsize-set-to-v-4 = PDSIZE зададен на __ocs_fmt_0__
.pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__) .pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__)

View file

@ -313,6 +313,7 @@ common =
.block-rotation = Rotace bloku .block-rotation = Rotace bloku
.blocks = Bloky .blocks = Bloky
.blue = Modrá .blue = Modrá
.bulge = Vyklenutí
.boolean-failed-the-solids-may-not-overlap = Logická hodnota selhala tělesa se nemusí překrývat. .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. .boolean-select-exactly-two-solids-created-this = Boolean: vyberte přesně dvě tělesa vytvořená v této relaci.
.bottom = Spodní .bottom = Spodní
@ -1328,6 +1329,18 @@ draw =
.ray-specify-through-point = RAY Určete průchozí bod: .ray-specify-through-point = RAY Určete průchozí bod:
.rect-specify-first-corner = RECT Určete první roh: .rect-specify-first-corner = RECT Určete první roh:
.rect-specify-opposite-corner = RECT Určete opačný 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-center-point = RECT CEN Zadejte střed:
.rect-cen-specify-corner-point =RECT CEN Zadejte rohový bod: .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): .rect-rot-specify-adjacent-corner-defines-edge = RECT ROT Určete sousední roh (definuje směr hrany):
@ -3060,7 +3073,7 @@ styles =
.pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE nová hodnota [0=dot 1=none 2=+ 3=x 4=tick; +32 kruh +64 čtverec]: .pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE nová hodnota [0=dot 1=none 2=+ 3=x 4=tick; +32 kruh +64 čtverec]:
.pdmode-v = PDMODE = __ocs_fmt_0__ .pdmode-v = PDMODE = __ocs_fmt_0__
.pdmode-set-to-v = PDMODE nastaveno na __ocs_fmt_0__ .pdmode-set-to-v = PDMODE nastaveno na __ocs_fmt_0__
.pdsize-new-point-size-0-5-of-viewport-0-absolu = Nová velikost bodu PDSIZE (0 = 5 % výřezu, <0 = absolutní): .pdsize-new-point-size-0-5-of-viewport-0-absolu = Nová velikost bodu PDSIZE (0 = 5 % výřezu, >0 = absolutní, <0 = procento výřezu):
.pdsize-v-4 = PDSIZE = __ocs_fmt_0__ .pdsize-v-4 = PDSIZE = __ocs_fmt_0__
.pdsize-set-to-v-4 = PDSIZE nastaveno na __ocs_fmt_0__ .pdsize-set-to-v-4 = PDSIZE nastaveno na __ocs_fmt_0__
.pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__) .pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__)

View file

@ -314,6 +314,7 @@ common =
.block-rotation = Blockdrehung .block-rotation = Blockdrehung
.blocks = Blöcke .blocks = Blöcke
.blue = Blau .blue = Blau
.bulge = Wölbung
.boolean-failed-the-solids-may-not-overlap = Boolean versagte - die Feststoffe überlappen sich möglicherweise nicht. .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. .boolean-select-exactly-two-solids-created-this = Boolean: Wählen Sie genau zwei Feststoffe erstellt diese Sitzung.
.bottom = Unten .bottom = Unten
@ -1329,6 +1330,18 @@ draw =
.ray-specify-through-point = RAY Geben Sie durch Punkt an: .ray-specify-through-point = RAY Geben Sie durch Punkt an:
.rect-specify-first-corner = RECT Geben Sie die erste Ecke 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-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-center-point = RECT CEN Mittelpunkt angeben:
.rect-cen-specify-corner-point = RECT CEN Eckpunkt angeben: .rect-cen-specify-corner-point = RECT CEN Eckpunkt angeben:
.rect-rot-specify-adjacent-corner-defines-edge = RECT ROT Angrenzende Ecke angeben (Kantenrichtung definieren): .rect-rot-specify-adjacent-corner-defines-edge = RECT ROT Angrenzende Ecke angeben (Kantenrichtung definieren):
@ -3059,7 +3072,7 @@ styles =
.pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE Neuer Wert [0=Punkt, 1=Kein Symbol, 2=+, 3=×, 4=Strich; +32=Kreis, +64=Quadrat]: .pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE Neuer Wert [0=Punkt, 1=Kein Symbol, 2=+, 3=×, 4=Strich; +32=Kreis, +64=Quadrat]:
.pdmode-v = PDMODE = __ocs_fmt_0__ .pdmode-v = PDMODE = __ocs_fmt_0__
.pdmode-set-to-v = PDMODE auf __ocs_fmt_0__ gesetzt .pdmode-set-to-v = PDMODE auf __ocs_fmt_0__ gesetzt
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE Neue Punktgröße (0 = 5 % des Ansichtsfensters, < 0 = absolut): .pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE Neue Punktgröße (0 = 5 % des Ansichtsfensters, > 0 = absolut, < 0 = Ansichtsfenster-Prozentwert):
.pdsize-v-4 = PDSIZE = __ocs_fmt_0__ .pdsize-v-4 = PDSIZE = __ocs_fmt_0__
.pdsize-set-to-v-4 = PDSIZE auf __ocs_fmt_0__ gesetzt .pdsize-set-to-v-4 = PDSIZE auf __ocs_fmt_0__ gesetzt
.pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__) .pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__)

View file

@ -314,6 +314,7 @@ common =
.block-rotation = Block rotation .block-rotation = Block rotation
.blocks = Blocks .blocks = Blocks
.blue = Blue .blue = Blue
.bulge = Bulge
.boolean-failed-the-solids-may-not-overlap = Boolean failed — the solids may not overlap. .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. .boolean-select-exactly-two-solids-created-this = Boolean: select exactly two solids created this session.
.bottom = Bottom .bottom = Bottom
@ -1169,6 +1170,15 @@ draw =
.tag-val = [__ocs_fmt_0__] = __ocs_fmt_1__ .tag-val = [__ocs_fmt_0__] = __ocs_fmt_1__
.no-closed-boundary-found = ⚠ No closed boundary found. .no-closed-boundary-found = ⚠ No closed boundary found.
.inverted = , inverted .inverted = , inverted
.loaded-multiline-styles = Loaded multiline styles: __ocs_fmt_0__
.mline-current-settings = MLINE Current settings: Justification = __ocs_fmt_0__, Scale = __ocs_fmt_1__, Style = __ocs_fmt_2__
Specify start point or [Justification/Scale/STyle]:
.mline-enter-justification = MLINE Enter justification type [Top/Zero/Bottom] <__ocs_fmt_0__>:
.mline-enter-scale-factor-default = MLINE Enter scale factor <__ocs_fmt_0__>:
.mline-enter-style-name = MLINE Enter style name or [?] <__ocs_fmt_0__>:
.mline-specify-next-point-close-undo = MLINE Specify next point or [Close/Undo]:
.mline-specify-next-point-undo = MLINE Specify next point or [Undo]:
.multiline-style-was-not-found = Multiline style "__ocs_arg_style__" was not found.
.value-2-point = 2-Point .value-2-point = 2-Point
.value-2d-solid = 2D Solid .value-2d-solid = 2D Solid
.value-3-point = 3-Point .value-3-point = 3-Point
@ -1329,6 +1339,18 @@ draw =
.ray-specify-through-point = RAY Specify through point: .ray-specify-through-point = RAY Specify through point:
.rect-specify-first-corner = RECT Specify first corner: .rect-specify-first-corner = RECT Specify first corner:
.rect-specify-opposite-corner = RECT Specify opposite 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-center-point = RECT CEN Specify center point:
.rect-cen-specify-corner-point = RECT CEN Specify corner 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): .rect-rot-specify-adjacent-corner-defines-edge = RECT ROT Specify adjacent corner (defines edge direction):
@ -1439,6 +1461,24 @@ draw =
modify = modify =
.count-objects = (__ocs_arg_count__ objects) .count-objects = (__ocs_arg_count__ objects)
.edge-extend-indented-title-case = [Edge: Extend] .edge-extend-indented-title-case = [Edge: Extend]
.add-vertex = Add Vertex
.closed-cross = Closed Cross
.closed-tee = Closed Tee
.corner-joint = Corner Joint
.cut-all = Cut All
.cut-single = Cut Single
.delete-vertex = Delete Vertex
.merged-cross = Merged Cross
.merged-tee = Merged Tee
.mledit-choose-an-edit-tool = MLEDIT Choose an edit tool:
.mledit-select-first-multiline = MLEDIT Select first multiline:
.mledit-select-second-multiline = MLEDIT Select second multiline:
.mledit-specify-end-of-weld-range = MLEDIT Specify the end of the weld range:
.mledit-specify-second-cut-point = MLEDIT Specify the second cut point:
.open-cross = Open Cross
.open-tee = Open Tee
.remove-vertex = Remove Vertex
.weld-all = Weld All
.align-scale-objects-based-on-alignment-points = ALIGN Scale objects based on alignment points? [Y/N]: .align-scale-objects-based-on-alignment-points = ALIGN Scale objects based on alignment points? [Y/N]:
.align-select-objects-count-selected-enter-when = ALIGN Select objects (__ocs_arg_count__ selected, Enter when done): .align-select-objects-count-selected-enter-when = ALIGN Select objects (__ocs_arg_count__ selected, Enter when done):
.align-specify-1st-destination-point = ALIGN Specify 1st destination point: .align-specify-1st-destination-point = ALIGN Specify 1st destination point:
@ -3087,7 +3127,7 @@ styles =
.pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE new value [0=dot 1=none 2=+ 3=x 4=tick; +32 circle +64 square]: .pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE new value [0=dot 1=none 2=+ 3=x 4=tick; +32 circle +64 square]:
.pdmode-v = PDMODE = __ocs_fmt_0__ .pdmode-v = PDMODE = __ocs_fmt_0__
.pdmode-set-to-v = PDMODE set to __ocs_fmt_0__ .pdmode-set-to-v = PDMODE set to __ocs_fmt_0__
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE new point size (0 = 5% of viewport, <0 = absolute): .pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE new point size (0 = 5% of viewport, >0 = absolute, <0 = viewport percentage):
.pdsize-v-4 = PDSIZE = __ocs_fmt_0__ .pdsize-v-4 = PDSIZE = __ocs_fmt_0__
.pdsize-set-to-v-4 = PDSIZE set to __ocs_fmt_0__ .pdsize-set-to-v-4 = PDSIZE set to __ocs_fmt_0__
.pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__) .pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__)

View file

@ -314,6 +314,7 @@ common =
.block-rotation = Rotación del bloque .block-rotation = Rotación del bloque
.blocks = Bloques .blocks = Bloques
.blue = Azul .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-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. .boolean-select-exactly-two-solids-created-this = Operación booleana: seleccione exactamente dos sólidos creados en esta sesión.
.bottom = Inferior .bottom = Inferior
@ -1329,6 +1330,18 @@ draw =
.ray-specify-through-point = RAY Precise el punto de paso: .ray-specify-through-point = RAY Precise el punto de paso:
.rect-specify-first-corner = RECT Precise la primera esquina: .rect-specify-first-corner = RECT Precise la primera esquina:
.rect-specify-opposite-corner = RECT Precise la esquina opuesta: .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-center-point = RECT CEN Precise el punto central:
.rect-cen-specify-corner-point = RECT CEN Precise el punto de esquina: .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): .rect-rot-specify-adjacent-corner-defines-edge = RECT ROT Precise la esquina adyacente (define la dirección de la arista):
@ -3061,7 +3074,7 @@ styles =
.pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE nuevo valor [0=punto 1=ninguno 2=+ 3=x 4=marca; +32 círculo +64 cuadrado]: .pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE nuevo valor [0=punto 1=ninguno 2=+ 3=x 4=marca; +32 círculo +64 cuadrado]:
.pdmode-v = PDMODE = __ocs_fmt_0__ .pdmode-v = PDMODE = __ocs_fmt_0__
.pdmode-set-to-v = PDMODE establecido en __ocs_fmt_0__ .pdmode-set-to-v = PDMODE establecido en __ocs_fmt_0__
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE nuevo tamaño de punto (0 = 5 % de la ventana gráfica, <0 = absoluto): .pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE nuevo tamaño de punto (0 = 5 % de la ventana gráfica, >0 = absoluto, <0 = porcentaje de la ventana gráfica):
.pdsize-v-4 = PDSIZE = __ocs_fmt_0__ .pdsize-v-4 = PDSIZE = __ocs_fmt_0__
.pdsize-set-to-v-4 = PDSIZE establecido en __ocs_fmt_0__ .pdsize-set-to-v-4 = PDSIZE establecido en __ocs_fmt_0__
.pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__) .pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__)

View file

@ -313,6 +313,7 @@ common =
.block-rotation = Lohkon kierto .block-rotation = Lohkon kierto
.blocks = Lohkot .blocks = Lohkot
.blue = Sininen .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-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. .boolean-select-exactly-two-solids-created-this = Boolen arvo: valitse täsmälleen kaksi tässä istunnossa luotua solidia.
.bottom = Pohja .bottom = Pohja
@ -1328,6 +1329,18 @@ draw =
.ray-specify-through-point = RAY Määritä pisteen kautta: .ray-specify-through-point = RAY Määritä pisteen kautta:
.rect-specify-first-corner = RECT Määritä ensimmäinen kulma: .rect-specify-first-corner = RECT Määritä ensimmäinen kulma:
.rect-specify-opposite-corner = RECT Määritä vastakkainen 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-center-point = RECT CEN Määritä keskipiste:
.rect-cen-specify-corner-point =RECT CEN Määritä kulmapiste: .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): .rect-rot-specify-adjacent-corner-defines-edge = RECT ROT Määritä viereinen kulma (määrittää reunasuunnan):
@ -3060,7 +3073,7 @@ styles =
.pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE uusi arvo [0=dot 1=none 2=+ 3=x 4=tick; +32 ympyrä +64 neliö]: .pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE uusi arvo [0=dot 1=none 2=+ 3=x 4=tick; +32 ympyrä +64 neliö]:
.pdmode-v = PDMODE = __ocs_fmt_0__ .pdmode-v = PDMODE = __ocs_fmt_0__
.pdmode-set-to-v = PDMODE asetettu arvoon __ocs_fmt_0__ .pdmode-set-to-v = PDMODE asetettu arvoon __ocs_fmt_0__
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE uusi pistekoko (0 = 5 % kuvaportista, <0 = absoluuttinen): .pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE uusi pistekoko (0 = 5 % kuvaportista, >0 = absoluuttinen, <0 = kuvaportin prosenttiosuus):
.pdsize-v-4 = PDSIZE = __ocs_fmt_0__ .pdsize-v-4 = PDSIZE = __ocs_fmt_0__
.pdsize-set-to-v-4 = PDSIZE asetettu arvoon __ocs_fmt_0__ .pdsize-set-to-v-4 = PDSIZE asetettu arvoon __ocs_fmt_0__
.pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__) .pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__)

View file

@ -314,6 +314,7 @@ common =
.block-rotation = Rotation par blocs .block-rotation = Rotation par blocs
.blocks = Blocs .blocks = Blocs
.blue = Bleu .blue = Bleu
.bulge = Courbure
.boolean-failed-the-solids-may-not-overlap = Booléenne échoué — les solides ne peuvent pas se chevaucher. .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. .boolean-select-exactly-two-solids-created-this = Booléen : sélectionnez exactement deux solides créés cette session.
.bottom = En bas .bottom = En bas
@ -1329,6 +1330,18 @@ draw =
.ray-specify-through-point = RAY Spécifiez par le point: .ray-specify-through-point = RAY Spécifiez par le point:
.rect-specify-first-corner = RECT Spécifiez le premier coin : .rect-specify-first-corner = RECT Spécifiez le premier coin :
.rect-specify-opposite-corner = RECT Spécifiez le coin opposé : .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 lorientation depuis le premier coin :
.rect-specify-rectangle-area = RECT Spécifiez laire 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 langle 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-center-point = RECT CEN Spécifier le point central:
.rect-cen-specify-corner-point = RECT CEN Spécifiez le point d'angle: .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): .rect-rot-specify-adjacent-corner-defines-edge = RECT ROT Spécifiez le coin adjacent (définit la direction du bord):
@ -3061,7 +3074,7 @@ styles =
.pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE nouvelle valeur [0=dot 1=none 2=+ 3=x 4=tick; +32 cercle +64 carré]: .pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE nouvelle valeur [0=dot 1=none 2=+ 3=x 4=tick; +32 cercle +64 carré]:
.pdmode-v = PDMODE = __ocs_fmt_0__ .pdmode-v = PDMODE = __ocs_fmt_0__
.pdmode-set-to-v = PDMODE réglé sur __ocs_fmt_0__ .pdmode-set-to-v = PDMODE réglé sur __ocs_fmt_0__
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE Nouvelle taille de point (0 = 5 % de la fenêtre, < 0 = absolue) : .pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE Nouvelle taille de point (0 = 5 % de la fenêtre, > 0 = absolue, < 0 = pourcentage de la fenêtre) :
.pdsize-v-4 = PDSIZE = __ocs_fmt_0__ .pdsize-v-4 = PDSIZE = __ocs_fmt_0__
.pdsize-set-to-v-4 = PDSIZE réglé sur __ocs_fmt_0__ .pdsize-set-to-v-4 = PDSIZE réglé sur __ocs_fmt_0__
.pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__) .pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__)

View file

@ -302,6 +302,7 @@ common =
.block-rotation = ब्लॉक रोटेशन .block-rotation = ब्लॉक रोटेशन
.blocks = ब्लॉक .blocks = ब्लॉक
.blue = नीला .blue = नीला
.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 = बोओलान: इस सत्र को बनाने वाले दो ठोस चुनें।
.bottom = नीचे .bottom = नीचे
@ -1317,6 +1318,18 @@ draw =
.ray-specify-through-point = RAY पॉइंट के माध्यम से निर्दिष्ट करें: .ray-specify-through-point = RAY पॉइंट के माध्यम से निर्दिष्ट करें:
.rect-specify-first-corner = RECT पहले कोने को निर्दिष्ट करें: .rect-specify-first-corner = RECT पहले कोने को निर्दिष्ट करें:
.rect-specify-opposite-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-center-point = RECT CEN केंद्र बिंदु निर्दिष्ट करें:
.rect-cen-specify-corner-point = RECT CEN कोने बिंदु निर्दिष्ट करें: .rect-cen-specify-corner-point = RECT CEN कोने बिंदु निर्दिष्ट करें:
.rect-rot-specify-adjacent-corner-defines-edge = RECT ROT निकटवर्ती कोने (डिफ़ाइन किनारे दिशा) निर्दिष्ट करें: .rect-rot-specify-adjacent-corner-defines-edge = RECT ROT निकटवर्ती कोने (डिफ़ाइन किनारे दिशा) निर्दिष्ट करें:
@ -3049,7 +3062,7 @@ styles =
.pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE नया मान [0=बिंदु 1=कोई नहीं 2=+ 3=× 4=टिक; +32 वृत्त +64 वर्ग]: .pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE नया मान [0=बिंदु 1=कोई नहीं 2=+ 3=× 4=टिक; +32 वृत्त +64 वर्ग]:
.pdmode-v = PDMODE = __ocs_fmt_0__ .pdmode-v = PDMODE = __ocs_fmt_0__
.pdmode-set-to-v = PDMODE सेट करने के लिए __ocs_fmt_0__ .pdmode-set-to-v = PDMODE सेट करने के लिए __ocs_fmt_0__
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE नए बिंदु आकार (0 = 5% व्यूपोर्ट, <0 = निरपेक्ष): .pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE नया बिंदु आकार (0 = व्यूपोर्ट का 5%, >0 = निरपेक्ष, <0 = व्यूपोर्ट प्रतिशत):
.pdsize-v-4 = PDSIZE = __ocs_fmt_0__ .pdsize-v-4 = PDSIZE = __ocs_fmt_0__
.pdsize-set-to-v-4 = PDSIZE सेट करने के लिए __ocs_fmt_0__ .pdsize-set-to-v-4 = PDSIZE सेट करने के लिए __ocs_fmt_0__
.pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__) .pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__)

View file

@ -313,6 +313,7 @@ common =
.block-rotation = Blokkforgatás .block-rotation = Blokkforgatás
.blocks = Blokkok .blocks = Blokkok
.blue = kék .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-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. .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 .bottom = Alul
@ -1328,6 +1329,18 @@ draw =
.ray-specify-through-point = RAY Pont megadása: .ray-specify-through-point = RAY Pont megadása:
.rect-specify-first-corner = RECT Adja meg az első sarkot: .rect-specify-first-corner = RECT Adja meg az első sarkot:
.rect-specify-opposite-corner = RECT Adja meg a szemközti 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-center-point = RECT CEN Középpont megadása:
.rect-cen-specify-corner-point =RECT CEN Sarokpont 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): .rect-rot-specify-adjacent-corner-defines-edge = RECT ROT Adja meg a szomszédos sarkot (meghatározza az él irányát):
@ -3060,7 +3073,7 @@ styles =
.pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE új érték [0=dot 1=none 2=+ 3=x 4=tick; +32 kör +64 négyzet]: .pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE új érték [0=dot 1=none 2=+ 3=x 4=tick; +32 kör +64 négyzet]:
.pdmode-v = PDMODE = __ocs_fmt_0__ .pdmode-v = PDMODE = __ocs_fmt_0__
.pdmode-set-to-v = A PDMODE __ocs_fmt_0__ értékre van állítva .pdmode-set-to-v = A PDMODE __ocs_fmt_0__ értékre van állítva
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE új pontméret (0 = a nézetablak 5%-a, <0 = abszolút): .pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE új pontméret (0 = a nézetablak 5%-a, >0 = abszolút, <0 = a nézetablak százaléka):
.pdsize-v-4 = PDSIZE = __ocs_fmt_0__ .pdsize-v-4 = PDSIZE = __ocs_fmt_0__
.pdsize-set-to-v-4 = A PDSIZE __ocs_fmt_0__ értékre van állítva .pdsize-set-to-v-4 = A PDSIZE __ocs_fmt_0__ értékre van állítva
.pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__) .pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__)

View file

@ -313,6 +313,7 @@ common =
.block-rotation = Bloccare la rotazione .block-rotation = Bloccare la rotazione
.blocks = Blocchi .blocks = Blocchi
.blue = Blu .blue = Blu
.bulge = Curvatura
.boolean-failed-the-solids-may-not-overlap = Booleano non riuscito: i solidi potrebbero non sovrapporsi. .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. .boolean-select-exactly-two-solids-created-this = Booleano: seleziona esattamente due solidi creati in questa sessione.
.bottom = In basso .bottom = In basso
@ -1328,6 +1329,18 @@ draw =
.ray-specify-through-point = RAY Specificare il punto di passaggio: .ray-specify-through-point = RAY Specificare il punto di passaggio:
.rect-specify-first-corner = RECT Specificare il primo angolo: .rect-specify-first-corner = RECT Specificare il primo angolo:
.rect-specify-opposite-corner = RECT Specificare l'angolo opposto: .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 lelevazione <__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 lorientamento dal primo angolo:
.rect-specify-rectangle-area = RECT Specificare larea 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 langolo 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-center-point = RECT CEN Specificare il punto centrale:
.rect-cen-specify-corner-point =RECT CEN Specificare il punto d'angolo: .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): .rect-rot-specify-adjacent-corner-defines-edge = RECT ROT Specificare l'angolo adiacente (definisce la direzione del bordo):
@ -3060,7 +3073,7 @@ styles =
.pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE nuovo valore [0=punto 1=none 2=+ 3=x 4=tick; +32 cerchio +64 quadrato]: .pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE nuovo valore [0=punto 1=none 2=+ 3=x 4=tick; +32 cerchio +64 quadrato]:
.pdmode-v = PDMODE = __ocs_fmt_0__ .pdmode-v = PDMODE = __ocs_fmt_0__
.pdmode-set-to-v = PDMODE impostato su __ocs_fmt_0__ .pdmode-set-to-v = PDMODE impostato su __ocs_fmt_0__
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE nuova dimensione in punti (0 = 5% dell'area visibile, <0 = assoluto): .pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE nuova dimensione del punto (0 = 5% dell'area visibile, >0 = assoluto, <0 = percentuale dell'area visibile):
.pdsize-v-4 = PDSIZE = __ocs_fmt_0__ .pdsize-v-4 = PDSIZE = __ocs_fmt_0__
.pdsize-set-to-v-4 = PDSIZE impostato su __ocs_fmt_0__ .pdsize-set-to-v-4 = PDSIZE impostato su __ocs_fmt_0__
.pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__) .pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__)

View file

@ -304,6 +304,7 @@ common =
.block-rotation = ブロック回転 .block-rotation = ブロック回転
.blocks = ブロック .blocks = ブロック
.blue = 青 .blue = 青
.bulge = ふくらみ
.boolean-failed-the-solids-may-not-overlap = ブール演算に失敗しました — ソリッドが重なっていない可能性があります。 .boolean-failed-the-solids-may-not-overlap = ブール演算に失敗しました — ソリッドが重なっていない可能性があります。
.boolean-select-exactly-two-solids-created-this = ブール演算: このセッションで作成したソリッドを 2 つ選択してください。 .boolean-select-exactly-two-solids-created-this = ブール演算: このセッションで作成したソリッドを 2 つ選択してください。
.bottom = 下 .bottom = 下
@ -1319,6 +1320,18 @@ draw =
.ray-specify-through-point = RAY 通過点を指定: .ray-specify-through-point = RAY 通過点を指定:
.rect-specify-first-corner = RECT 最初のコーナーを指定: .rect-specify-first-corner = RECT 最初のコーナーを指定:
.rect-specify-opposite-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-center-point = RECT CEN 中心点を指定:
.rect-cen-specify-corner-point = RECT CEN コーナー点を指定: .rect-cen-specify-corner-point = RECT CEN コーナー点を指定:
.rect-rot-specify-adjacent-corner-defines-edge = RECT ROT 隣接コーナーを指定(エッジ方向を定義): .rect-rot-specify-adjacent-corner-defines-edge = RECT ROT 隣接コーナーを指定(エッジ方向を定義):
@ -3049,7 +3062,7 @@ styles =
.pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE 新しい値 [0=点 1=なし 2=+ 3=x 4=目盛り、+32 円 +64 正方形]: .pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE 新しい値 [0=点 1=なし 2=+ 3=x 4=目盛り、+32 円 +64 正方形]:
.pdmode-v = PDMODE = __ocs_fmt_0__ .pdmode-v = PDMODE = __ocs_fmt_0__
.pdmode-set-to-v = PDMODE を __ocs_fmt_0__ に設定しました .pdmode-set-to-v = PDMODE を __ocs_fmt_0__ に設定しました
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE 新しい点サイズ0=ビューポートの 5%、<0=絶対値: .pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE 新しい点サイズ0=ビューポートの 5%、>0=絶対値、<0=ビューポートの割合:
.pdsize-v-4 = PDSIZE = __ocs_fmt_0__ .pdsize-v-4 = PDSIZE = __ocs_fmt_0__
.pdsize-set-to-v-4 = PDSIZE を __ocs_fmt_0__ に設定しました .pdsize-set-to-v-4 = PDSIZE を __ocs_fmt_0__ に設定しました
.pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__) .pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__)

View file

@ -313,6 +313,7 @@ common =
.block-rotation = 블록 회전 .block-rotation = 블록 회전
.blocks = 블록 .blocks = 블록
.blue = 블루 .blue = 블루
.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 = 부울: 이번 세션에서 생성된 정확히 두 개의 솔리드를 선택합니다.
.bottom = 바닥 .bottom = 바닥
@ -1328,6 +1329,18 @@ draw =
.ray-specify-through-point = RAY 통과점 지정: .ray-specify-through-point = RAY 통과점 지정:
.rect-specify-first-corner = RECT 첫 번째 구석 지정: .rect-specify-first-corner = RECT 첫 번째 구석 지정:
.rect-specify-opposite-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-center-point = RECT CEN 중심점 지정:
.rect-cen-specify-corner-point =RECT CEN 코너점 지정: .rect-cen-specify-corner-point =RECT CEN 코너점 지정:
.rect-rot-specify-adjacent-corner-defines-edge = RECT ROT 인접 모서리 지정(모서리 방향 정의): .rect-rot-specify-adjacent-corner-defines-edge = RECT ROT 인접 모서리 지정(모서리 방향 정의):
@ -3060,7 +3073,7 @@ styles =
.pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE 새 값 [0=dot 1=none 2=+ 3=x 4=tick; +32원 +64사각형]: .pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE 새 값 [0=dot 1=none 2=+ 3=x 4=tick; +32원 +64사각형]:
.pdmode-v = PDMODE = __ocs_fmt_0__ .pdmode-v = PDMODE = __ocs_fmt_0__
.pdmode-set-to-v = PDMODE가 __ocs_fmt_0__로 설정됨 .pdmode-set-to-v = PDMODE가 __ocs_fmt_0__로 설정됨
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE 새 포인트 크기(0 = 뷰포트의 5%, <0 = 절대값): .pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE 새 포인트 크기(0 = 뷰포트의 5%, >0 = 절대값, <0 = 뷰포트 백분율):
.pdsize-v-4 = PDSIZE = __ocs_fmt_0__ .pdsize-v-4 = PDSIZE = __ocs_fmt_0__
.pdsize-set-to-v-4 = PDSIZE가 __ocs_fmt_0__로 설정됨 .pdsize-set-to-v-4 = PDSIZE가 __ocs_fmt_0__로 설정됨
.pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__) .pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__)

View file

@ -314,6 +314,7 @@ common =
.block-rotation = Blokrotatie .block-rotation = Blokrotatie
.blocks = Blokken .blocks = Blokken
.blue = Blauw .blue = Blauw
.bulge = Bolling
.boolean-failed-the-solids-may-not-overlap = Boolean mislukt . . de vaste stoffen mogen niet overlappen. .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. .boolean-select-exactly-two-solids-created-this = Boolean: selecteer precies twee vaste stoffen die deze sessie hebben gemaakt.
.bottom = Onderkant .bottom = Onderkant
@ -1329,6 +1330,18 @@ draw =
.ray-specify-through-point = RAY Door het punt opgeven: .ray-specify-through-point = RAY Door het punt opgeven:
.rect-specify-first-corner = RECT Eerste hoek opgeven: .rect-specify-first-corner = RECT Eerste hoek opgeven:
.rect-specify-opposite-corner = RECT Geef de andere hoek op: .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-center-point = RECT CEN Centrumpunt opgeven:
.rect-cen-specify-corner-point = RECT CEN Geef hoekpunt op: .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): .rect-rot-specify-adjacent-corner-defines-edge = RECT ROT Aangrenzende hoek opgeven (randrichting definiëren):
@ -3061,7 +3074,7 @@ styles =
.pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE nieuwe waarde [0=punt 1=geen 2=+ 3=x 4=streepje; +32 cirkel +64 vierkant]: .pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE nieuwe waarde [0=punt 1=geen 2=+ 3=x 4=streepje; +32 cirkel +64 vierkant]:
.pdmode-v = PDMODE = __ocs_fmt_0__ .pdmode-v = PDMODE = __ocs_fmt_0__
.pdmode-set-to-v = PDMODE ingesteld op __ocs_fmt_0__ .pdmode-set-to-v = PDMODE ingesteld op __ocs_fmt_0__
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE nieuwe puntgrootte (0 = 5% van weergavevenster, <0 = absoluut): .pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE nieuwe puntgrootte (0 = 5% van weergavevenster, >0 = absoluut, <0 = percentage van weergavevenster):
.pdsize-v-4 = PDSIZE = __ocs_fmt_0__ .pdsize-v-4 = PDSIZE = __ocs_fmt_0__
.pdsize-set-to-v-4 = PDSIZE ingesteld op __ocs_fmt_0__ .pdsize-set-to-v-4 = PDSIZE ingesteld op __ocs_fmt_0__
.pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__) .pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__)

View file

@ -313,6 +313,7 @@ common =
.block-rotation = Obrót bloku .block-rotation = Obrót bloku
.blocks = Bloki .blocks = Bloki
.blue = Niebieski .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-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. .boolean-select-exactly-two-solids-created-this = Boolean: wybierz dokładnie dwie bryły utworzone w tej sesji.
.bottom = Dół .bottom = Dół
@ -1328,6 +1329,18 @@ draw =
.ray-specify-through-point = RAY Określ punkt przelotowy: .ray-specify-through-point = RAY Określ punkt przelotowy:
.rect-specify-first-corner = RECT Określ pierwszy narożnik: .rect-specify-first-corner = RECT Określ pierwszy narożnik:
.rect-specify-opposite-corner = RECT Określ przeciwny 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-center-point = RECT CEN Określ punkt środkowy:
.rect-cen-specify-corner-point =RECT CEN Określ punkt narożny: .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): .rect-rot-specify-adjacent-corner-defines-edge = RECT ROT Określ sąsiadujący narożnik (określa kierunek krawędzi):
@ -3060,7 +3073,7 @@ styles =
.pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE nowa wartość [0=dot 1=none 2=+ 3=x 4=tick; +32 koło +64 kwadraty]: .pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE nowa wartość [0=dot 1=none 2=+ 3=x 4=tick; +32 koło +64 kwadraty]:
.pdmode-v = PDMODE = __ocs_fmt_0__ .pdmode-v = PDMODE = __ocs_fmt_0__
.pdmode-set-to-v = PDMODE ustawione na __ocs_fmt_0__ .pdmode-set-to-v = PDMODE ustawione na __ocs_fmt_0__
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE nowy rozmiar punktu (0 = 5% rzutni, <0 = bezwzględnie): .pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE nowy rozmiar punktu (0 = 5% rzutni, >0 = bezwzględnie, <0 = procent rzutni):
.pdsize-v-4 = PDSIZE = __ocs_fmt_0__ .pdsize-v-4 = PDSIZE = __ocs_fmt_0__
.pdsize-set-to-v-4 = PDSIZE ustawione na __ocs_fmt_0__ .pdsize-set-to-v-4 = PDSIZE ustawione na __ocs_fmt_0__
.pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__) .pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__)

View file

@ -314,6 +314,7 @@ common =
.block-rotation = Rotação do bloco .block-rotation = Rotação do bloco
.blocks = Blocos .blocks = Blocos
.blue = Azul .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-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. .boolean-select-exactly-two-solids-created-this = Operação booleana: selecione exatamente dois sólidos criados nesta sessão.
.bottom = Inferior .bottom = Inferior
@ -1329,6 +1330,18 @@ draw =
.ray-specify-through-point = RAY Especifique o ponto de passagem: .ray-specify-through-point = RAY Especifique o ponto de passagem:
.rect-specify-first-corner = RECT Especifique o primeiro canto: .rect-specify-first-corner = RECT Especifique o primeiro canto:
.rect-specify-opposite-corner = RECT Especifique o canto oposto: .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-center-point = RECT CEN Especifique o ponto central:
.rect-cen-specify-corner-point = RECT CEN Especifique o ponto de canto: .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): .rect-rot-specify-adjacent-corner-defines-edge = RECT ROT Especifique o canto adjacente (define a direção da aresta):
@ -3061,7 +3074,7 @@ styles =
.pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE novo valor [0=ponto 1=nenhum 2=+ 3=x 4=marca; +32 círculo +64 quadrado]: .pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE novo valor [0=ponto 1=nenhum 2=+ 3=x 4=marca; +32 círculo +64 quadrado]:
.pdmode-v = PDMODE = __ocs_fmt_0__ .pdmode-v = PDMODE = __ocs_fmt_0__
.pdmode-set-to-v = PDMODE definido como __ocs_fmt_0__ .pdmode-set-to-v = PDMODE definido como __ocs_fmt_0__
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE novo tamanho do ponto (0 = 5% da janela de visualização, <0 = absoluto): .pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE novo tamanho do ponto (0 = 5% da janela de visualização, >0 = absoluto, <0 = porcentagem da janela de visualização):
.pdsize-v-4 = PDSIZE = __ocs_fmt_0__ .pdsize-v-4 = PDSIZE = __ocs_fmt_0__
.pdsize-set-to-v-4 = PDSIZE definido como __ocs_fmt_0__ .pdsize-set-to-v-4 = PDSIZE definido como __ocs_fmt_0__
.pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__) .pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__)

View file

@ -317,6 +317,7 @@ common =
.block-rotation = Вращение блока .block-rotation = Вращение блока
.blocks = Блоки .blocks = Блоки
.blue = Синий .blue = Синий
.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 = Булев: выберите ровно два твердых тела, созданных на этом сеансе.
.bottom = Снизу .bottom = Снизу
@ -1332,6 +1333,18 @@ draw =
.ray-specify-through-point = RAY Укажите через точку: .ray-specify-through-point = RAY Укажите через точку:
.rect-specify-first-corner = RECT Укажите первый угол: .rect-specify-first-corner = RECT Укажите первый угол:
.rect-specify-opposite-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-center-point = RECT CEN Укажите центральную точку:
.rect-cen-specify-corner-point = RECT CEN Укажите угловую точку: .rect-cen-specify-corner-point = RECT CEN Укажите угловую точку:
.rect-rot-specify-adjacent-corner-defines-edge = RECT ROT Укажите смежный угол (определите направление кромки): .rect-rot-specify-adjacent-corner-defines-edge = RECT ROT Укажите смежный угол (определите направление кромки):
@ -3064,7 +3077,7 @@ styles =
.pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE Новое значение [0=точка 1=нет 2=+ 3=× 4=штрих; +32 окружность +64 квадрат]: .pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE Новое значение [0=точка 1=нет 2=+ 3=× 4=штрих; +32 окружность +64 квадрат]:
.pdmode-v = PDMODE = __ocs_fmt_0__ .pdmode-v = PDMODE = __ocs_fmt_0__
.pdmode-set-to-v = PDMODE, установленный на __ocs_fmt_0__ .pdmode-set-to-v = PDMODE, установленный на __ocs_fmt_0__
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE новый размер точки (0 = 5% от площади обзора, <0 = абсолютный): .pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE новый размер точки (0 = 5% от области просмотра, >0 = абсолютный, <0 = процент области просмотра):
.pdsize-v-4 = PDSIZE = __ocs_fmt_0__ .pdsize-v-4 = PDSIZE = __ocs_fmt_0__
.pdsize-set-to-v-4 = PDSIZE, установленный на __ocs_fmt_0__ .pdsize-set-to-v-4 = PDSIZE, установленный на __ocs_fmt_0__
.pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__) .pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__)

View file

@ -300,6 +300,7 @@ common =
.block-rotation = Blok döndürmesi .block-rotation = Blok döndürmesi
.blocks = Bloklar .blocks = Bloklar
.blue = Mavi .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-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. .boolean-select-exactly-two-solids-created-this = Boolean: bu oturumda oluşturulmuş tam iki katı seçin.
.bottom = Alt .bottom = Alt
@ -1151,6 +1152,15 @@ draw =
.tag-val = [__ocs_fmt_0__] = __ocs_fmt_1__ .tag-val = [__ocs_fmt_0__] = __ocs_fmt_1__
.no-closed-boundary-found = ⚠ Kapalı sınır bulunamadı. .no-closed-boundary-found = ⚠ Kapalı sınır bulunamadı.
.inverted = , ters çevrilmiş .inverted = , ters çevrilmiş
.loaded-multiline-styles = Yüklü çoklu çizgi stilleri: __ocs_fmt_0__
.mline-current-settings = MLINE Geçerli ayarlar: Hizalama = __ocs_fmt_0__, Ölçek = __ocs_fmt_1__, Stil = __ocs_fmt_2__
Başlangıç noktasını belirtin veya [Hizalama/Ölçek/Stil]:
.mline-enter-justification = MLINE Hizalama türünü girin [Üst/Sıfır/Alt] <__ocs_fmt_0__>:
.mline-enter-scale-factor-default = MLINE Ölçek katsayısını girin <__ocs_fmt_0__>:
.mline-enter-style-name = MLINE Stil adını veya [?] girin <__ocs_fmt_0__>:
.mline-specify-next-point-close-undo = MLINE Sonraki noktayı belirtin veya [Kapat/Geri al]:
.mline-specify-next-point-undo = MLINE Sonraki noktayı belirtin veya [Geri al]:
.multiline-style-was-not-found = "__ocs_arg_style__" çoklu çizgi stili bulunamadı.
.value-2-point = 2 Nokta .value-2-point = 2 Nokta
.value-2d-solid = 2B Katı .value-2d-solid = 2B Katı
.value-3-point = 3 Nokta .value-3-point = 3 Nokta
@ -1311,6 +1321,18 @@ draw =
.ray-specify-through-point = RAY Geçiş noktasını belirtin: .ray-specify-through-point = RAY Geçiş noktasını belirtin:
.rect-specify-first-corner = RECT İlk köşeyi belirtin: .rect-specify-first-corner = RECT İlk köşeyi belirtin:
.rect-specify-opposite-corner = RECT Karşı 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-center-point = RECT CEN Merkez noktasını belirtin:
.rect-cen-specify-corner-point = RECT CEN Köşe 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): .rect-rot-specify-adjacent-corner-defines-edge = RECT ROT Bitişik köşeyi belirtin (kenar yönünü tanımlar):
@ -1421,6 +1443,24 @@ draw =
modify = modify =
.count-objects = (__ocs_arg_count__ nesne) .count-objects = (__ocs_arg_count__ nesne)
.edge-extend-indented-title-case = [Kenar: Uzat] .edge-extend-indented-title-case = [Kenar: Uzat]
.add-vertex = Tepe Noktası Ekle
.closed-cross = Kapalı Kesişim
.closed-tee = Kapalı T Birleşimi
.corner-joint = Köşe Birleşimi
.cut-all = Tümünü Kes
.cut-single = Tekini Kes
.delete-vertex = Tepe Noktasını Sil
.merged-cross = Birleşik Kesişim
.merged-tee = Birleşik T Birleşimi
.mledit-choose-an-edit-tool = MLEDIT Bir düzenleme aracı seçin:
.mledit-select-first-multiline = MLEDIT İlk çoklu çizgiyi seçin:
.mledit-select-second-multiline = MLEDIT İkinci çoklu çizgiyi seçin:
.mledit-specify-end-of-weld-range = MLEDIT Kaynak aralığının sonunu belirtin:
.mledit-specify-second-cut-point = MLEDIT İkinci kesim noktasını belirtin:
.open-cross = Açık Kesişim
.open-tee = Açık T Birleşimi
.remove-vertex = Tepe Noktasını Kaldır
.weld-all = Tümünü Birleştir
.align-scale-objects-based-on-alignment-points = ALIGN Nesneler hizalama noktalarına göre ölçeklensin mi? [E/H]: .align-scale-objects-based-on-alignment-points = ALIGN Nesneler hizalama noktalarına göre ölçeklensin mi? [E/H]:
.align-select-objects-count-selected-enter-when = ALIGN Nesneleri seçin (__ocs_arg_count__ seçili, bitirmek için Enter): .align-select-objects-count-selected-enter-when = ALIGN Nesneleri seçin (__ocs_arg_count__ seçili, bitirmek için Enter):
.align-specify-1st-destination-point = ALIGN 1. hedef noktasını belirtin: .align-specify-1st-destination-point = ALIGN 1. hedef noktasını belirtin:
@ -3027,7 +3067,7 @@ styles =
.pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE Yeni değer [0=nokta 1=yok 2=+ 3=x 4=çentik; +32 çember +64 kare]: .pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE Yeni değer [0=nokta 1=yok 2=+ 3=x 4=çentik; +32 çember +64 kare]:
.pdmode-v = PDMODE = __ocs_fmt_0__ .pdmode-v = PDMODE = __ocs_fmt_0__
.pdmode-set-to-v = PDMODE __ocs_fmt_0__ olarak ayarlandı .pdmode-set-to-v = PDMODE __ocs_fmt_0__ olarak ayarlandı
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE Yeni nokta boyutu (0 = görünüm alanının %5'i, <0 = mutlak): .pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE Yeni nokta boyutu (0 = görünüm alanının %5'i, >0 = mutlak, <0 = görünüm alanı yüzdesi):
.pdsize-v-4 = PDSIZE = __ocs_fmt_0__ .pdsize-v-4 = PDSIZE = __ocs_fmt_0__
.pdsize-set-to-v-4 = PDSIZE __ocs_fmt_0__ olarak ayarlandı .pdsize-set-to-v-4 = PDSIZE __ocs_fmt_0__ olarak ayarlandı
.pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__) .pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__)

View file

@ -301,6 +301,7 @@ common =
.block-rotation = 块旋转 .block-rotation = 块旋转
.blocks = 块状物 .blocks = 块状物
.blue = 蓝色 .blue = 蓝色
.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 = 布尔: 选择创建此会话的正中两个固件 。
.bottom = 底部 .bottom = 底部
@ -1316,6 +1317,18 @@ draw =
.ray-specify-through-point = RAY通过点指定 : .ray-specify-through-point = RAY通过点指定 :
.rect-specify-first-corner = RECT 指定第一角 : .rect-specify-first-corner = RECT 指定第一角 :
.rect-specify-opposite-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-center-point = RECT CEN 指定中心点:
.rect-cen-specify-corner-point = RECT CEN 指定角点: .rect-cen-specify-corner-point = RECT CEN 指定角点:
.rect-rot-specify-adjacent-corner-defines-edge = RECTROT指定相邻的角( 定边方向) : .rect-rot-specify-adjacent-corner-defines-edge = RECTROT指定相邻的角( 定边方向) :
@ -3044,7 +3057,7 @@ styles =
.pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE 新值 [0=点 1=无 2=+ 3=× 4=短划;+32 圆 +64 方框] .pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE 新值 [0=点 1=无 2=+ 3=× 4=短划;+32 圆 +64 方框]
.pdmode-v = PDMODE = __ocs_fmt_0__ 时间轴 : .pdmode-v = PDMODE = __ocs_fmt_0__ 时间轴 :
.pdmode-set-to-v = PDMODE 设置为__ocs_fmt_0__ .pdmode-set-to-v = PDMODE 设置为__ocs_fmt_0__
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE 指定新点大小0 = 视口的 5%<0 = 绝对值 .pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE 指定新点大小0 = 视口的 5%>0 = 绝对值,<0 = 视口百分比
.pdsize-v-4 = PDSIZE = __ocs_fmt_0__ 时间轴 : .pdsize-v-4 = PDSIZE = __ocs_fmt_0__ 时间轴 :
.pdsize-set-to-v-4 = PDSIZE 设置为__ocs_fmt_0__ .pdsize-set-to-v-4 = PDSIZE 设置为__ocs_fmt_0__
.pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__) .pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__)

View file

@ -313,6 +313,7 @@ common =
.block-rotation = 圖塊旋轉 .block-rotation = 圖塊旋轉
.blocks = 圖塊 .blocks = 圖塊
.blue = 藍色 .blue = 藍色
.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 = 布爾: 選擇建立此會話的正中兩個固件 。
.bottom = 底部 .bottom = 底部
@ -1328,6 +1329,18 @@ draw =
.ray-specify-through-point = RAY通過點指定 : .ray-specify-through-point = RAY通過點指定 :
.rect-specify-first-corner = RECT 指定第一角 : .rect-specify-first-corner = RECT 指定第一角 :
.rect-specify-opposite-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-center-point = RECT CEN 指定中心點:
.rect-cen-specify-corner-point = RECT CEN 指定角點: .rect-cen-specify-corner-point = RECT CEN 指定角點:
.rect-rot-specify-adjacent-corner-defines-edge = RECTROT指定相鄰的角( 定邊方向) : .rect-rot-specify-adjacent-corner-defines-edge = RECTROT指定相鄰的角( 定邊方向) :
@ -3056,7 +3069,7 @@ styles =
.pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE 新值 [0=點 1=無 2=+ 3=× 4=短划;+32 圓 +64 方框] .pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32 = PDMODE 新值 [0=點 1=無 2=+ 3=× 4=短划;+32 圓 +64 方框]
.pdmode-v = PDMODE = __ocs_fmt_0__ 時間軸 : .pdmode-v = PDMODE = __ocs_fmt_0__ 時間軸 :
.pdmode-set-to-v = PDMODE 設定為__ocs_fmt_0__ .pdmode-set-to-v = PDMODE 設定為__ocs_fmt_0__
.pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE 指定新點大小0 = 視口的 5%<0 = 絕對值 .pdsize-new-point-size-0-5-of-viewport-0-absolu = PDSIZE 指定新點大小0 = 視口的 5%>0 = 絕對值,<0 = 視口百分比
.pdsize-v-4 = PDSIZE = __ocs_fmt_0__ 時間軸 : .pdsize-v-4 = PDSIZE = __ocs_fmt_0__ 時間軸 :
.pdsize-set-to-v-4 = PDSIZE 設定為__ocs_fmt_0__ .pdsize-set-to-v-4 = PDSIZE 設定為__ocs_fmt_0__
.pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__) .pickadd = PICKADD = __ocs_fmt_0__ (__ocs_fmt_1__)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 213 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 576 KiB

After

Width:  |  Height:  |  Size: 690 KiB

Before After
Before After

View file

@ -662,6 +662,26 @@ impl OpenCADStudio {
tab.dirty = true; tab.dirty = true;
} }
} }
let mline_settings = self.tabs[self.active_tab]
.active_cmd
.as_ref()
.and_then(|command| command.mline_settings());
if let Some((scale, justification, style_name, style_handle)) = mline_settings {
let tab = &mut self.tabs[self.active_tab];
let header = &mut tab.scene.document.header;
let style_handle = style_handle.unwrap_or(acadrust::Handle::NULL);
let changed = (header.multiline_scale - scale).abs() > f64::EPSILON
|| header.multiline_justification != justification
|| !header.multiline_style.eq_ignore_ascii_case(&style_name)
|| header.current_multiline_style_handle != style_handle;
if changed {
header.multiline_scale = scale;
header.multiline_justification = justification;
header.multiline_style = style_name;
header.current_multiline_style_handle = style_handle;
tab.dirty = true;
}
}
let was_active = self.tabs[self.active_tab].active_cmd.is_some(); let was_active = self.tabs[self.active_tab].active_cmd.is_some();
let preserve_selection = let preserve_selection =
matches!(result, CmdResult::Relaunch(..) | CmdResult::Dispatch(..)); matches!(result, CmdResult::Relaunch(..) | CmdResult::Dispatch(..));

View file

@ -13,27 +13,27 @@ impl OpenCADStudio {
"MLINE" => { "MLINE" => {
use crate::modules::draw::draw::mline::MlineCommand; use crate::modules::draw::draw::mline::MlineCommand;
let style_name = self.tabs[i].scene.document.header.multiline_style.clone(); let header = &self.tabs[i].scene.document.header;
let style = self.tabs[i] let style_name = header.multiline_style.clone();
let scale = header.multiline_scale;
let justification = header.multiline_justification;
let styles = self.tabs[i]
.scene .scene
.document .document
.objects .objects
.iter() .iter()
.find_map(|(handle, object)| match object { .filter_map(|(handle, object)| match object {
acadrust::objects::ObjectType::MLineStyle(style) acadrust::objects::ObjectType::MLineStyle(style) => {
if style.name.eq_ignore_ascii_case(&style_name) => Some((*handle, style.clone()))
{
Some((*handle, style.elements.len()))
} }
_ => None, _ => None,
}); })
let (style_handle, element_count) = style .collect();
.map(|(handle, count)| (Some(handle), count)) let cmd_obj = MlineCommand::with_styles(
.unwrap_or((None, 2)); styles,
let cmd_obj = MlineCommand::with_style(
style_name, style_name,
style_handle, scale,
element_count, justification,
); );
self.command_line.push_info(&cmd_obj.prompt()); self.command_line.push_info(&cmd_obj.prompt());
self.tabs[i].active_cmd = Some(Box::new(cmd_obj)); self.tabs[i].active_cmd = Some(Box::new(cmd_obj));

View file

@ -286,6 +286,34 @@ impl OpenCADStudio {
self.tabs[i].active_cmd = Some(Box::new(cmd_obj)); self.tabs[i].active_cmd = Some(Box::new(cmd_obj));
} }
"MLEDIT" => {
use crate::modules::draw::modify::mledit::{
MlineEditCommand, MlineEditTarget,
};
let document = &self.tabs[i].scene.document;
let targets = document
.entities()
.filter_map(|entity| {
let acadrust::EntityType::MLine(mline) = entity else {
return None;
};
let style = crate::entities::mline::resolved_mline_style(mline, document)
.cloned()
.unwrap_or_else(acadrust::objects::MLineStyle::standard);
Some((
entity.common().handle.value(),
MlineEditTarget {
entity: mline.clone(),
style,
},
))
})
.collect();
let command = MlineEditCommand::new(targets);
self.command_line.push_info(&command.prompt());
self.tabs[i].active_cmd = Some(Box::new(command));
}
"SPLINEDIT" => { "SPLINEDIT" => {
use crate::modules::draw::modify::splinedit::SplineditCommand; use crate::modules::draw::modify::splinedit::SplineditCommand;
let cmd_obj = SplineditCommand::new(); let cmd_obj = SplineditCommand::new();

View file

@ -512,6 +512,8 @@ inventory::submit!(crate::command::CommandRegistration {
"SHADEDGE", "SHADEDGE",
"MAXACTVP", "MAXACTVP",
"CMLJUST", "CMLJUST",
"CMLSCALE",
"CMLSTYLE",
"TEXTQLTY", "TEXTQLTY",
"SORTENTS", "SORTENTS",
"FRAME", "FRAME",

View file

@ -920,6 +920,8 @@ impl OpenCADStudio {
| "SHADEDGE" | "SHADEDGE"
| "MAXACTVP" | "MAXACTVP"
| "CMLJUST" | "CMLJUST"
| "CMLSCALE"
| "CMLSTYLE"
| "TEXTQLTY" | "TEXTQLTY"
| "SORTENTS" | "SORTENTS"
| "FRAME" | "FRAME"
@ -1697,17 +1699,35 @@ impl OpenCADStudio {
} }
}, },
"CMLJUST" => match &value { "CMLJUST" => match &value {
Some(v) => v Some(v) => match v.parse::<i16>() {
.parse::<i16>() Ok(x @ 0..=2) => {
.map(|x| {
h.multiline_justification = x; h.multiline_justification = x;
(format!("CMLJUST = {x}"), true) Ok((format!("CMLJUST = {x}"), true))
}) }
.map_err(|_| "SETVAR: integer value required.".into()), _ => Err("SETVAR: integer value from 0 to 2 required.".into()),
},
None => { None => {
Ok((format!("CMLJUST = {}", h.multiline_justification), false)) Ok((format!("CMLJUST = {}", h.multiline_justification), false))
} }
}, },
"CMLSCALE" => match &value {
Some(v) => match v.parse::<f64>() {
Ok(x) if x.is_finite() => {
let changed = h.multiline_scale != x;
h.multiline_scale = x;
Ok((format!("CMLSCALE = {x}"), changed))
}
_ => Err("SETVAR: finite numeric value required.".into()),
},
None => Ok((format!("CMLSCALE = {}", h.multiline_scale), false)),
},
"CMLSTYLE" => match &value {
Some(_) => Err(
"SETVAR: CMLSTYLE is read-only here — use the MLSTYLE command."
.into(),
),
None => Ok((format!("CMLSTYLE = {}", h.multiline_style), false)),
},
"TEXTQLTY" => match &value { "TEXTQLTY" => match &value {
Some(v) => v Some(v) => v
.parse::<i16>() .parse::<i16>()
@ -2134,7 +2154,17 @@ impl OpenCADStudio {
} }
"LTSCALE" => { "LTSCALE" => {
use crate::command::ValuePromptCommand; use crate::command::ValuePromptCommand;
let c = ValuePromptCommand::new("LTSCALE", "LTSCALE new global line-type scale:");
let current = self.tabs[i].scene.document.header.linetype_scale;
self.command_line
.push_output(crate::tf!("LTSCALE = {current:.4}").as_ref());
let c = ValuePromptCommand::new(
"LTSCALE",
"LTSCALE new global line-type scale:",
);
self.command_line.push_info(&c.prompt()); self.command_line.push_info(&c.prompt());
self.tabs[i].active_cmd = Some(Box::new(c)); self.tabs[i].active_cmd = Some(Box::new(c));
} }

View file

@ -668,9 +668,8 @@ impl OpenCADStudio {
true true
} }
/// Add is delta-safe on an already-existing layer unless it creates block /// Add is delta-safe when its layer and XData application IDs already exist
/// records. RasterImage definitions are captured as exact object-map deltas; /// and it creates no block records.
/// Viewport commits explicitly record their added entity handle.
pub(super) fn delta_add_safe(&self, i: usize, entity: &EntityType) -> bool { pub(super) fn delta_add_safe(&self, i: usize, entity: &EntityType) -> bool {
if matches!( if matches!(
entity, entity,
@ -680,7 +679,18 @@ impl OpenCADStudio {
return false; return false;
} }
let layer = entity.common().layer.clone(); 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`]: /// Close the delta transaction opened by [`OpenCADStudio::begin_undo`]:

View file

@ -2195,6 +2195,20 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
crate::command::WorkingPlane::default() crate::command::WorkingPlane::default()
}; };
for &handle in &handles { for &handle in &handles {
let mline_style = self.tabs[i]
.scene
.document
.get_entity(handle)
.and_then(|entity| match entity {
acadrust::EntityType::MLine(mline) => {
crate::entities::mline::resolved_mline_style(
mline,
&self.tabs[i].scene.document,
)
.cloned()
}
_ => None,
});
if let Some(entity) = self.tabs[i].scene.document.get_entity_mut(handle) if let Some(entity) = self.tabs[i].scene.document.get_entity_mut(handle)
{ {
crate::scene::view::dispatch::apply_geom_prop_in_working_plane( crate::scene::view::dispatch::apply_geom_prop_in_working_plane(
@ -2203,6 +2217,17 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
&value, &value,
plane, plane,
); );
if matches!(field, "ml_justification" | "ml_scale") {
if let (
acadrust::EntityType::MLine(mline),
Some(style),
) = (entity, mline_style.as_ref())
{
crate::modules::draw::draw::mline::sync_mline_element_parameters(
mline, style,
);
}
}
} }
} }
} }
@ -2399,6 +2424,20 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
) )
.is_none() .is_none()
{ {
let mline_style = self.tabs[i]
.scene
.document
.get_entity(handle)
.and_then(|entity| match entity {
acadrust::EntityType::MLine(mline) => {
crate::entities::mline::resolved_mline_style(
mline,
&self.tabs[i].scene.document,
)
.cloned()
}
_ => None,
});
if let Some(entity) = self.tabs[i] if let Some(entity) = self.tabs[i]
.scene .scene
.document .document
@ -2410,6 +2449,17 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
&val, &val,
plane, plane,
); );
if field == "ml_scale" {
if let (
acadrust::EntityType::MLine(mline),
Some(style),
) = (entity, mline_style.as_ref())
{
crate::modules::draw::draw::mline::sync_mline_element_parameters(
mline, style,
);
}
}
} }
} }
} }

View file

@ -93,7 +93,7 @@ fn native_paths_match(left: &std::path::Path, right: &std::path::Path) -> bool {
} }
type LayoutPlotParams = ( type LayoutPlotParams = (
std::sync::Arc<Vec<crate::scene::WireModel>>, std::sync::Arc<Vec<crate::io::pdf_export::PlotWire>>,
Vec<crate::scene::model::hatch_model::HatchModel>, Vec<crate::scene::model::hatch_model::HatchModel>,
Vec<crate::scene::model::hatch_model::HatchModel>, Vec<crate::scene::model::hatch_model::HatchModel>,
crate::io::pdf_export::PlotGroupSplits, crate::io::pdf_export::PlotGroupSplits,
@ -107,7 +107,7 @@ type LayoutPlotParams = (
); );
type ClippedPlotParams = ( type ClippedPlotParams = (
Vec<crate::scene::WireModel>, Vec<crate::io::pdf_export::PlotWire>,
Vec<crate::scene::model::hatch_model::HatchModel>, Vec<crate::scene::model::hatch_model::HatchModel>,
Vec<crate::scene::model::hatch_model::HatchModel>, Vec<crate::scene::model::hatch_model::HatchModel>,
crate::io::pdf_export::PlotGroupSplits, crate::io::pdf_export::PlotGroupSplits,
@ -121,7 +121,7 @@ type ClippedPlotParams = (
); );
fn plot_content_extents( fn plot_content_extents(
wires: &[crate::scene::WireModel], wires: &[crate::io::pdf_export::PlotWire],
hatches: &[crate::scene::model::hatch_model::HatchModel], hatches: &[crate::scene::model::hatch_model::HatchModel],
wipeouts: &[crate::scene::model::hatch_model::HatchModel], wipeouts: &[crate::scene::model::hatch_model::HatchModel],
) -> Option<(f64, f64, f64, f64)> { ) -> Option<(f64, f64, f64, f64)> {
@ -163,7 +163,7 @@ fn plot_scene_content(
paper_space_last: bool, paper_space_last: bool,
render_mode_override: Option<acadrust::entities::ViewportRenderMode>, render_mode_override: Option<acadrust::entities::ViewportRenderMode>,
) -> ( ) -> (
std::sync::Arc<Vec<crate::scene::WireModel>>, std::sync::Arc<Vec<crate::io::pdf_export::PlotWire>>,
Vec<crate::scene::model::hatch_model::HatchModel>, Vec<crate::scene::model::hatch_model::HatchModel>,
Vec<crate::scene::model::hatch_model::HatchModel>, Vec<crate::scene::model::hatch_model::HatchModel>,
crate::io::pdf_export::PlotGroupSplits, crate::io::pdf_export::PlotGroupSplits,
@ -171,6 +171,16 @@ fn plot_scene_content(
let (mut paper_wires, mut model_wires) = scene.plot_wire_groups(render_mode_override); let (mut paper_wires, mut model_wires) = scene.plot_wire_groups(render_mode_override);
paper_wires.retain(|wire| wire.plot_visible); paper_wires.retain(|wire| wire.plot_visible);
model_wires.retain(|wire| wire.plot_visible); model_wires.retain(|wire| wire.plot_visible);
let with_depth = |wires: Vec<crate::scene::WireModel>| {
let depths = scene.plot_wire_depths(&wires);
wires
.into_iter()
.zip(depths)
.map(|(wire, draw_depth)| crate::io::pdf_export::PlotWire { wire, draw_depth })
.collect::<Vec<_>>()
};
let paper_wires = with_depth(paper_wires);
let model_wires = with_depth(model_wires);
let paper_hatches = scene.paper_plot_hatches().as_ref().clone(); let paper_hatches = scene.paper_plot_hatches().as_ref().clone();
let paper_wipeouts = scene.paper_plot_wipeouts().as_ref().clone(); let paper_wipeouts = scene.paper_plot_wipeouts().as_ref().clone();
if scene.current_layout == "Model" { if scene.current_layout == "Model" {
@ -188,7 +198,11 @@ fn plot_scene_content(
} }
let (mut model_pattern_wires, model_hatches, model_wipeouts) = let (mut model_pattern_wires, model_hatches, model_wipeouts) =
scene.viewport_plot_fills(); scene.viewport_plot_fills();
model_pattern_wires.retain(|wire| wire.plot_visible); model_pattern_wires.retain(|(wire, _)| wire.plot_visible);
let model_pattern_wires = model_pattern_wires
.into_iter()
.map(|(wire, draw_depth)| crate::io::pdf_export::PlotWire { wire, draw_depth })
.collect::<Vec<_>>();
let (wires, hatches, wipeouts, splits) = if paper_space_last { let (wires, hatches, wipeouts, splits) = if paper_space_last {
let splits = crate::io::pdf_export::PlotGroupSplits { let splits = crate::io::pdf_export::PlotGroupSplits {

View file

@ -2238,6 +2238,7 @@ impl OpenCADStudio {
let far_pos = base + dir * far; let far_pos = base + dir * far;
let far_neg = base - dir * far; let far_neg = base - dir * far;
let guide = crate::scene::WireModel { let guide = crate::scene::WireModel {
point_marker: None,
taper_widths: Vec::new(), taper_widths: Vec::new(),
world_width: 0.0, world_width: 0.0,
depth_override: None, depth_override: None,

View file

@ -1855,6 +1855,11 @@ pub trait CadCommand: Send {
None None
} }
/// Current drawing-persisted multiline creation settings.
fn mline_settings(&self) -> Option<(f64, i16, String, Option<Handle>)> {
None
}
/// Returns `true` when the active text prompt expects free-form prose /// Returns `true` when the active text prompt expects free-form prose
/// that can legitimately contain whitespace (the body of a TEXT / /// that can legitimately contain whitespace (the body of a TEXT /
/// MTEXT / DDEDIT entity, an attribute default value, etc.). For /// MTEXT / DDEDIT entity, an attribute default value, etc.). For

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. /// Format an angle (input in radians) using AUNITS / AUPREC.
pub fn format_angle(value_rad: f64) -> String { pub fn format_angle(value_rad: f64) -> String {
let ctx = unit_context(); let ctx = unit_context();
@ -420,6 +432,11 @@ pub fn parse_direction(text: &str) -> Option<f64> {
Some(relative + ctx.angbase) 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 /// Two interior triangles covering a quad (flat list, 6 vertices) — the
/// click-anywhere pick surface for frame-like entities (image, OLE frame, /// click-anywhere pick surface for frame-like entities (image, OLE frame,
/// underlay, wipeout). Corners in ring order. /// underlay, wipeout). Corners in ring order.

View file

@ -2066,6 +2066,7 @@ fn tessellate_dimension_inner(
let (ext1, ext2) = split_ext_lines(&geom.ext_lines); let (ext1, ext2) = split_ext_lines(&geom.ext_lines);
if !ext1.is_empty() { if !ext1.is_empty() {
wires.push(WireModel { wires.push(WireModel {
point_marker: None,
taper_widths: Vec::new(), taper_widths: Vec::new(),
world_width: 0.0, world_width: 0.0,
depth_override: None, depth_override: None,
@ -2099,6 +2100,7 @@ fn tessellate_dimension_inner(
} }
if !ext2.is_empty() { if !ext2.is_empty() {
wires.push(WireModel { wires.push(WireModel {
point_marker: None,
taper_widths: Vec::new(), taper_widths: Vec::new(),
world_width: 0.0, world_width: 0.0,
depth_override: None, depth_override: None,
@ -2132,6 +2134,7 @@ fn tessellate_dimension_inner(
} }
} else { } else {
wires.push(WireModel { wires.push(WireModel {
point_marker: None,
taper_widths: Vec::new(), taper_widths: Vec::new(),
world_width: 0.0, world_width: 0.0,
depth_override: None, depth_override: None,
@ -2166,6 +2169,7 @@ fn tessellate_dimension_inner(
} }
wires.push(WireModel { wires.push(WireModel {
point_marker: None,
taper_widths: Vec::new(), taper_widths: Vec::new(),
world_width: 0.0, world_width: 0.0,
depth_override: None, depth_override: None,
@ -2216,6 +2220,7 @@ fn tessellate_dimension_inner(
aci_to_rgba(&c) aci_to_rgba(&c)
}; };
wires.push(WireModel { wires.push(WireModel {
point_marker: None,
taper_widths: Vec::new(), taper_widths: Vec::new(),
world_width: 0.0, world_width: 0.0,
depth_override: None, depth_override: None,

View file

@ -674,6 +674,7 @@ impl LeaderTess for Leader {
if verts.len() < 2 { if verts.len() < 2 {
return WireModel { return WireModel {
point_marker: None,
taper_widths: Vec::new(), taper_widths: Vec::new(),
world_width: 0.0, world_width: 0.0,
depth_override: None, depth_override: None,
@ -852,6 +853,7 @@ impl LeaderTess for Leader {
crate::scene::convert::tessellate::points_to_ds(fill_tris); crate::scene::convert::tessellate::points_to_ds(fill_tris);
WireModel { WireModel {
point_marker: None,
taper_widths: Vec::new(), taper_widths: Vec::new(),
world_width: 0.0, world_width: 0.0,
depth_override: None, depth_override: None,

View file

@ -5,8 +5,8 @@ use crate::t;
use crate::command::EntityTransform; use crate::command::EntityTransform;
use crate::entities::common::{ use crate::entities::common::{
edit_prop as edit, parse_f64, rectangle_grip, ro_prop as ro, square_grip, edit_prop as edit, edit_scalar_prop as edit_scalar, format_area, format_length, parse_f64,
stepper_prop as stepper, rectangle_grip, ro_prop as ro, square_grip, stepper_prop as stepper,
}; };
use crate::entities::traits::RenderConvertible; use crate::entities::traits::RenderConvertible;
use crate::scene::convert::acad_to_render::{extrusion_wall_tris, RenderEntity, RenderObject}; 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); 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> { fn properties(pline: &LwPolyline) -> Vec<PropSection> {
let n = pline.vertices.len(); let n = pline.vertices.len();
// The panel's Current Vertex focus, clamped to this polyline's range. // 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 cloud_arc_length = revision_cloud_arc_length(pline);
let vertex_label = if n == 0 { let vertex_label = if n == 0 {
"".to_string() "".to_string()
} else if cloud_arc_length.is_some() {
format!("{}", vi + 1)
} else { } else {
format!("{} / {}", vi + 1, n) format!("{}", vi + 1)
}; };
let mut misc_props = vec![ let mut misc_props = vec![
Property { Property {
@ -861,20 +886,26 @@ fn properties(pline: &LwPolyline) -> Vec<PropSection> {
arc_length, 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![ vec![
PropSection { PropSection {
title: t!("Geometry").into_owned(), title: t!("Geometry").into_owned(),
props: vec![ props: geometry_props,
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)),
],
}, },
PropSection { PropSection {
title: t!("Misc").into_owned(), title: t!("Misc").into_owned(),
@ -954,6 +985,13 @@ fn apply_geom_prop(pline: &mut LwPolyline, field: &str, value: &str) {
vtx.end_width = v; 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);
}
}
}
_ => {} _ => {}
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -1503,6 +1503,7 @@ impl MultiLeaderTess for MultiLeader {
// WireModels so the renderer respects per-piece coloring. // WireModels so the renderer respects per-piece coloring.
let mut wires: Vec<WireModel> = Vec::new(); let mut wires: Vec<WireModel> = Vec::new();
wires.push(WireModel { wires.push(WireModel {
point_marker: None,
taper_widths: Vec::new(), taper_widths: Vec::new(),
world_width: 0.0, world_width: 0.0,
depth_override: None, depth_override: None,
@ -1842,6 +1843,7 @@ impl MultiLeaderTess for MultiLeader {
xy = xy.max(p[1] as f64); xy = xy.max(p[1] as f64);
} }
wires.push(WireModel { wires.push(WireModel {
point_marker: None,
taper_widths: Vec::new(), taper_widths: Vec::new(),
world_width: 0.0, world_width: 0.0,
depth_override: None, depth_override: None,
@ -1914,6 +1916,7 @@ impl MultiLeaderTess for MultiLeader {
pts.push([bx, by, z]); pts.push([bx, by, z]);
} }
wires.push(WireModel { wires.push(WireModel {
point_marker: None,
taper_widths: Vec::new(), taper_widths: Vec::new(),
world_width: 0.0, world_width: 0.0,
depth_override: None, depth_override: None,
@ -2001,6 +2004,7 @@ impl MultiLeaderTess for MultiLeader {
wcs_corners[3], wcs_corners[3],
]; ];
wires.push(WireModel { wires.push(WireModel {
point_marker: None,
taper_widths: Vec::new(), taper_widths: Vec::new(),
world_width: 0.0, world_width: 0.0,
depth_override: None, depth_override: None,
@ -2043,6 +2047,7 @@ impl MultiLeaderTess for MultiLeader {
wcs_corners[0], wcs_corners[0],
]; ];
wires.push(WireModel { wires.push(WireModel {
point_marker: None,
taper_widths: Vec::new(), taper_widths: Vec::new(),
world_width: 0.0, world_width: 0.0,
depth_override: None, depth_override: None,

View file

@ -1,13 +1,15 @@
use acadrust::entities::Point; use acadrust::entities::Point;
use acadrust::EntityType; use acadrust::EntityType;
use crate::t; use cadkernel::geom2d::{Circle, Curve, Line};
use cadkernel::space::{curve::bezier_points, PlanarCurve, Plane, Vec3};
use crate::t;
use crate::command::EntityTransform; use crate::command::EntityTransform;
use crate::entities::common::{edit_prop as edit, parse_f64, square_grip}; use crate::entities::common::{edit_prop as edit, parse_f64, square_grip};
use crate::entities::traits::RenderConvertible; use crate::entities::traits::RenderConvertible;
use crate::scene::convert::acad_to_render::{RenderEntity, RenderObject}; use crate::scene::convert::acad_to_render::{RenderEntity, RenderObject};
use crate::scene::model::object::{GripApply, GripDef, PropSection}; use crate::scene::model::object::{GripApply, GripDef, PropSection};
use crate::scene::model::wire_model::SnapHint; use crate::scene::model::wire_model::{PointMarker, SnapHint};
/// Resolve a positive (absolute) PDSIZE to a world size. Relative/zero PDSIZE /// Resolve a positive (absolute) PDSIZE to a world size. Relative/zero PDSIZE
/// is handled by [`relative_render`]. /// is handled by [`relative_render`].
@ -99,7 +101,7 @@ pub fn relative_world_size(pdsize: f64, wpp: f32, viewport_height_px: f32) -> f6
pub fn relative_marker_spec( pub fn relative_marker_spec(
entity: &EntityType, entity: &EntityType,
document: &acadrust::CadDocument, document: &acadrust::CadDocument,
) -> Option<(f32, [f32; 3])> { ) -> Option<PointMarker> {
let EntityType::Point(pt) = entity else { let EntityType::Point(pt) = entity else {
return None; return None;
}; };
@ -107,27 +109,41 @@ pub fn relative_marker_spec(
if size > 0.0 || effective_pdmode(pt, document.header.point_display_mode) == 0 { if size > 0.0 || effective_pdmode(pt, document.header.point_display_mode) == 0 {
return None; return None;
} }
let n = point_normal(pt); let plane = point_plane(pt);
Some(( let normal = Vec3::from(plane.normal().unwrap_or([0.0, 0.0, 1.0]));
if size == 0.0 { 5.0 } else { -size as f32 }, Some(PointMarker {
[n.x as f32, n.y as f32, n.z as f32], origin: glam::DVec3::new(pt.location.x, pt.location.y, pt.location.z),
)) normal: glam::DVec3::new(normal.x, normal.y, normal.z),
axis_x: glam::DVec3::from_array(plane.x_axis),
axis_y: glam::DVec3::from_array(plane.y_axis),
viewport_percent: if size == 0.0 { 5.0 } else { -size as f32 },
})
} }
fn point_normal(pt: &Point) -> glam::DVec3 { fn point_normal(pt: &Point) -> glam::DVec3 {
glam::DVec3::new(pt.normal.x, pt.normal.y, pt.normal.z).normalize_or(glam::DVec3::Z) let normal = Vec3::new(pt.normal.x, pt.normal.y, pt.normal.z)
.normalize()
.unwrap_or(Vec3::Z);
glam::DVec3::new(normal.x, normal.y, normal.z)
} }
fn point_axes(pt: &Point) -> (glam::DVec3, glam::DVec3, glam::DVec3) { fn point_plane(pt: &Point) -> Plane {
let n = point_normal(pt); let origin = [pt.location.x, pt.location.y, pt.location.z];
let (ax, ay) = crate::scene::view::transform::ocs_axes((n.x, n.y, n.z)); let normal = Vec3::new(pt.normal.x, pt.normal.y, pt.normal.z)
let base_x = glam::DVec3::new(ax.0, ax.1, ax.2); .normalize()
let base_y = glam::DVec3::new(ay.0, ay.1, ay.2); .unwrap_or(Vec3::Z);
let (sin, cos) = pt.x_axis_angle.to_radians().sin_cos(); let x_seed = if normal.x.abs() < 1.0 / 64.0 && normal.y.abs() < 1.0 / 64.0 {
( Vec3::Y.cross(normal)
base_x * cos + base_y * sin, } else {
-base_x * sin + base_y * cos, Vec3::Z.cross(normal)
n, };
let base = Plane::orthonormal(origin, x_seed.to_array(), normal.to_array())
.unwrap_or(Plane::XY);
let (sin, cos) = pt.x_axis_angle.sin_cos();
Plane::from_axes(
origin,
base.vector_at([cos, sin]),
base.vector_at([-sin, cos]),
) )
} }
@ -144,88 +160,68 @@ fn point_glyph(pt: &Point, pdmode: i16, s_half: f64) -> Vec<[f64; 3]> {
// The '+' and '×' arms reach the full PDSIZE (twice the radius), so the // The '+' and '×' arms reach the full PDSIZE (twice the radius), so the
// cross pokes out past any enclosing circle/square, which sit at the radius. // cross pokes out past any enclosing circle/square, which sit at the radius.
let arm = 2.0 * s_half; let arm = 2.0 * s_half;
let nan = [f64::NAN, f64::NAN, f64::NAN]; let mut curves = Vec::new();
let center = glam::DVec3::new(pt.location.x, pt.location.y, pt.location.z); let line = |start, end| Curve::Line(Line { start, end });
let (axis_x, axis_y, normal) = point_axes(pt);
let map = |x: f64, y: f64, height: f64| {
let p = center + axis_x * x + axis_y * y + normal * height;
[p.x, p.y, p.z]
};
let mut pts: Vec<[f64; 3]> = Vec::new();
let mut push_seg = |a: [f64; 3], b: [f64; 3]| {
if !pts.is_empty() {
pts.push(nan);
}
pts.push(a);
pts.push(b);
};
match shape { match shape {
// 0 = single dot — emit a tiny "+" so it's visible at any zoom.
0 => { 0 => {
let d = s * 0.05; let d = s * 0.05;
push_seg(map(-d, 0.0, 0.0), map(d, 0.0, 0.0)); curves.push(line([-d, 0.0], [d, 0.0]));
push_seg(map(0.0, -d, 0.0), map(0.0, d, 0.0)); curves.push(line([0.0, -d], [0.0, d]));
} }
1 => {} // explicit nothing 1 => {}
2 => { 2 => {
push_seg(map(-arm, 0.0, 0.0), map(arm, 0.0, 0.0)); curves.push(line([-arm, 0.0], [arm, 0.0]));
push_seg(map(0.0, -arm, 0.0), map(0.0, arm, 0.0)); curves.push(line([0.0, -arm], [0.0, arm]));
} }
3 => { 3 => {
push_seg(map(-arm, -arm, 0.0), map(arm, arm, 0.0)); curves.push(line([-arm, -arm], [arm, arm]));
push_seg(map(-arm, arm, 0.0), map(arm, -arm, 0.0)); curves.push(line([-arm, arm], [arm, -arm]));
} }
4 => { 4 => {
// Upward tick rising from the point (length = PDSIZE/2), not a curves.push(line([0.0, 0.0], [0.0, s]));
// vertical line centred on it.
push_seg(map(0.0, 0.0, 0.0), map(0.0, s, 0.0));
} }
_ => { _ => {
push_seg(map(-s, 0.0, 0.0), map(s, 0.0, 0.0)); curves.push(line([-s, 0.0], [s, 0.0]));
push_seg(map(0.0, -s, 0.0), map(0.0, s, 0.0)); curves.push(line([0.0, -s], [0.0, s]));
} }
} }
if circle { if circle {
const N: usize = 64; curves.push(Curve::Circle(Circle {
let mut ring: Vec<[f64; 3]> = Vec::with_capacity(N + 1); centre: [0.0, 0.0],
for i in 0..=N { radius: s,
let a = i as f64 * std::f64::consts::TAU / N as f64; }));
ring.push(map(a.cos() * s, a.sin() * s, 0.0));
}
if !pts.is_empty() {
pts.push(nan);
}
pts.extend(ring);
} }
if square { if square {
let p1 = map(-s, -s, 0.0); curves.push(line([-s, -s], [s, -s]));
let p2 = map(s, -s, 0.0); curves.push(line([s, -s], [s, s]));
let p3 = map(s, s, 0.0); curves.push(line([s, s], [-s, s]));
let p4 = map(-s, s, 0.0); curves.push(line([-s, s], [-s, -s]));
if !pts.is_empty() {
pts.push(nan);
}
pts.extend_from_slice(&[p1, p2, p3, p4, p1]);
} }
if pt.thickness.abs() > 1.0e-10 && !pts.is_empty() {
let base = pts.clone(); let plane = point_plane(pt);
pts.push(nan); let nan = [f64::NAN; 3];
pts.extend(base.iter().map(|p| { let mut paths: Vec<Vec<[f64; 3]>> = curves
if p[0].is_finite() { .iter()
[ .map(|curve| PlanarCurve::new(plane, curve.clone()).tessellate(64.0 / std::f64::consts::TAU))
p[0] + normal.x * pt.thickness, .collect();
p[1] + normal.y * pt.thickness, if pt.thickness.abs() > 1.0e-10 && !curves.is_empty() {
p[2] + normal.z * pt.thickness, let normal = Vec3::from(plane.normal().unwrap_or([0.0, 0.0, 1.0]));
] let top_origin = (Vec3::from(plane.origin) + normal * pt.thickness).to_array();
} else { let top_plane = Plane::from_axes(top_origin, plane.x_axis, plane.y_axis);
nan paths.extend(curves.iter().map(|curve| {
} PlanarCurve::new(top_plane, curve.clone())
.tessellate(64.0 / std::f64::consts::TAU)
})); }));
pts.push(nan); paths.push(bezier_points(&[plane.origin, top_origin], 1));
pts.push(map(0.0, 0.0, 0.0));
pts.push(map(0.0, 0.0, pt.thickness));
} }
pts let mut points = Vec::new();
for path in paths {
if !points.is_empty() {
points.push(nan);
}
points.extend(path);
}
points
} }
fn is_defpoints_layer(layer: &str) -> bool { fn is_defpoints_layer(layer: &str) -> bool {

View file

@ -1667,6 +1667,7 @@ pub fn tessellate_table(
let mk = let mk =
|color: [f32; 4], points: Vec<[f32; 3]>, fill_tris: Vec<[f32; 3]>, lw: f32| -> WireModel { |color: [f32; 4], points: Vec<[f32; 3]>, fill_tris: Vec<[f32; 3]>, lw: f32| -> WireModel {
WireModel { WireModel {
point_marker: None,
taper_widths: Vec::new(), taper_widths: Vec::new(),
world_width: 0.0, world_width: 0.0,
depth_override: None, depth_override: None,

View file

@ -24,12 +24,26 @@ use printpdf::{
use std::io::Write; use std::io::Write;
use std::path::Path; use std::path::Path;
#[derive(Clone, Debug)]
pub struct PlotWire {
pub wire: WireModel,
pub draw_depth: f32,
}
impl std::ops::Deref for PlotWire {
type Target = WireModel;
fn deref(&self) -> &Self::Target {
&self.wire
}
}
// The web build has no `printpdf` (it pulls a wasm-incompatible `memchr` via // The web build has no `printpdf` (it pulls a wasm-incompatible `memchr` via
// lopdf → nom_locate) and no filesystem, so PDF export is native-only; the web // lopdf → nom_locate) and no filesystem, so PDF export is native-only; the web
// build gets these stubs so the call sites still compile. // build gets these stubs so the call sites still compile.
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
pub fn export_pdf( pub fn export_pdf(
_wires: &[WireModel], _wires: &[PlotWire],
_hatches: &[HatchModel], _hatches: &[HatchModel],
_wipeouts: &[HatchModel], _wipeouts: &[HatchModel],
_paper_w: f64, _paper_w: f64,
@ -95,7 +109,7 @@ pub struct PlotGroupSplits {
/// Owned render data for one page in a multi-page PDF. /// Owned render data for one page in a multi-page PDF.
#[cfg_attr(target_arch = "wasm32", allow(dead_code))] #[cfg_attr(target_arch = "wasm32", allow(dead_code))]
pub struct PdfPageInput { pub struct PdfPageInput {
pub wires: std::sync::Arc<Vec<WireModel>>, pub wires: std::sync::Arc<Vec<PlotWire>>,
pub hatches: Vec<HatchModel>, pub hatches: Vec<HatchModel>,
pub wipeouts: Vec<HatchModel>, pub wipeouts: Vec<HatchModel>,
pub paper_w: f64, pub paper_w: f64,
@ -131,7 +145,7 @@ impl Default for PdfPlotOptions {
/// - `rotation_deg`: 0 | 90 | 180 | 270 — rotates the entire drawing on the page. /// - `rotation_deg`: 0 | 90 | 180 | 270 — rotates the entire drawing on the page.
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
pub fn export_pdf( pub fn export_pdf(
wires: &[WireModel], wires: &[PlotWire],
hatches: &[HatchModel], hatches: &[HatchModel],
wipeouts: &[HatchModel], wipeouts: &[HatchModel],
paper_w: f64, paper_w: f64,
@ -204,7 +218,7 @@ pub fn pick_pdf_path_owned(
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
fn build_pdf( fn build_pdf(
wires: &[WireModel], wires: &[PlotWire],
hatches: &[HatchModel], hatches: &[HatchModel],
wipeouts: &[HatchModel], wipeouts: &[HatchModel],
paper_w: f32, paper_w: f32,
@ -267,7 +281,7 @@ fn build_pdf_pages(pages: &[PdfPageInput], plot_style: Option<&PlotStyleTable>)
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
fn append_pdf_page( fn append_pdf_page(
doc: &mut PdfDocument, doc: &mut PdfDocument,
wires: &[WireModel], wires: &[PlotWire],
hatches: &[HatchModel], hatches: &[HatchModel],
wipeouts: &[HatchModel], wipeouts: &[HatchModel],
paper_w: f32, paper_w: f32,
@ -393,29 +407,92 @@ fn append_pdf_page(
(first_wires, first_hatches, first_wipeouts), (first_wires, first_hatches, first_wipeouts),
(second_wires, second_hatches, second_wipeouts), (second_wires, second_hatches, second_wipeouts),
] { ] {
emit_wire_fills(&mut ops, wires, ox, oy, plot_style, options); enum DrawItem<'a> {
WireFill(&'a PlotWire),
// Hatch / wipeout fills render before wires so linework stays visible. Hatch(&'a HatchModel),
for hatch in wipeouts.iter().chain(hatches.iter()) { Wire(&'a PlotWire),
emit_hatch( Text(&'a PlotWire),
&mut ops,
hatch,
ox,
oy,
plot_style,
scale,
options,
normal_blend.as_ref(),
);
} }
let mut draw_items = Vec::with_capacity(wires.len() * 2 + hatches.len() + wipeouts.len());
let mut sequence = 0usize;
for wire in wires {
if !wire.fill_tris.is_empty() {
draw_items.push((wire.draw_depth, 0u8, sequence, DrawItem::WireFill(wire)));
sequence += 1;
}
draw_items.push((wire.draw_depth, 2u8, sequence, DrawItem::Wire(wire)));
sequence += 1;
if !wire.text_verts.is_empty() {
draw_items.push((wire.draw_depth, 3u8, sequence, DrawItem::Text(wire)));
sequence += 1;
}
}
for hatch in wipeouts.iter().chain(hatches.iter()) {
draw_items.push((hatch.draw_depth, 1u8, sequence, DrawItem::Hatch(hatch)));
sequence += 1;
}
draw_items.sort_by(|a, b| {
a.0.total_cmp(&b.0)
.then_with(|| a.1.cmp(&b.1))
.then_with(|| a.2.cmp(&b.2))
});
let mut last_color: Option<[f32; 3]> = None; let mut last_color: Option<[f32; 3]> = None;
let mut last_lw: Option<f32> = None; let mut last_lw: Option<f32> = None;
// Current PDF dash array (empty = solid). Tracked so the dash op is only // Current PDF dash array (empty = solid). Tracked so the dash op is only
// re-emitted when it actually changes between wires. // re-emitted when it actually changes between wires.
let mut last_dash: Option<Vec<i64>> = None; let mut last_dash: Option<Vec<i64>> = None;
for wire in wires { for (_, _, _, item) in draw_items {
let wire = match item {
DrawItem::WireFill(wire) => {
emit_wire_fills(
&mut ops,
std::slice::from_ref(&wire.wire),
ox,
oy,
plot_style,
options,
);
last_color = None;
last_lw = None;
last_dash = None;
continue;
}
DrawItem::Hatch(hatch) => {
emit_hatch(
&mut ops,
hatch,
ox,
oy,
plot_style,
scale,
options,
normal_blend.as_ref(),
);
last_color = None;
last_lw = None;
last_dash = None;
continue;
}
DrawItem::Text(wire) => {
emit_text(
&mut ops,
std::slice::from_ref(&wire.wire),
ox,
oy,
scale,
plot_style,
options,
);
last_color = None;
last_lw = None;
last_dash = None;
continue;
}
DrawItem::Wire(wire) => wire,
};
let [mut r, mut g, mut b, a] = wire.color; let [mut r, mut g, mut b, a] = wire.color;
if a < 0.01 { if a < 0.01 {
continue; continue;
@ -541,9 +618,9 @@ fn append_pdf_page(
flush_line(&mut ops, &segment, dot_radius); flush_line(&mut ops, &segment, dot_radius);
segment.clear(); segment.clear();
} else { } else {
let lo = wire.points_low.get(pi).copied().unwrap_or([0.0; 3]); let point = wire.point_world(pi, paper_h as f64 / scale.max(1e-6) as f64);
let wx = (x as f64 + lo[0] as f64 + ox) as f32; let wx = (point.x + ox) as f32;
let wy = (y as f64 + lo[1] as f64 + oy) as f32; let wy = (point.y + oy) as f32;
segment.push(LinePoint { segment.push(LinePoint {
p: Point::new(Mm(wx), Mm(wy)), p: Point::new(Mm(wx), Mm(wy)),
bezier: false, bezier: false,
@ -552,13 +629,6 @@ fn append_pdf_page(
} }
flush_line(&mut ops, &segment, dot_radius); flush_line(&mut ops, &segment, dot_radius);
} }
// Text (SDF glyph quads) — re-emitted as vector strokes / fills. Text now
// renders on-screen only as textured SDF quads (`wire.text_verts`), which
// this CPU exporter can't sample, so without this pass all text — including
// dimension text — is missing from the PDF (issue #385). Drawn after the
// wires (on top) and under the same rotation/scale/clip CTM.
emit_text(&mut ops, wires, ox, oy, scale, plot_style, options);
} }
if needs_state { if needs_state {

View file

@ -11,7 +11,7 @@
use crate::io::pdf_export; use crate::io::pdf_export;
use crate::io::plot_style::PlotStyleTable; use crate::io::plot_style::PlotStyleTable;
use crate::scene::model::hatch_model::HatchModel; use crate::scene::model::hatch_model::HatchModel;
use crate::scene::WireModel; use crate::io::pdf_export::PlotWire;
/// Extra options for a print job. On CUPS (Linux/macOS) these map to `lp` /// Extra options for a print job. On CUPS (Linux/macOS) these map to `lp`
/// flags / `-o` options. On Windows the generated PDF already carries render /// flags / `-o` options. On Windows the generated PDF already carries render
@ -38,7 +38,7 @@ pub fn list_printers() -> Vec<String> {
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub async fn print_wires_with( pub async fn print_wires_with(
_wires: std::sync::Arc<Vec<WireModel>>, _wires: std::sync::Arc<Vec<PlotWire>>,
_hatches: Vec<HatchModel>, _hatches: Vec<HatchModel>,
_wipeouts: Vec<HatchModel>, _wipeouts: Vec<HatchModel>,
_paper_w: f64, _paper_w: f64,
@ -153,7 +153,7 @@ pub fn open_printer_properties(_printer: Option<&str>) -> Result<(), String> {
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub async fn print_wires_with( pub async fn print_wires_with(
wires: std::sync::Arc<Vec<WireModel>>, wires: std::sync::Arc<Vec<PlotWire>>,
hatches: Vec<HatchModel>, hatches: Vec<HatchModel>,
wipeouts: Vec<HatchModel>, wipeouts: Vec<HatchModel>,
paper_w: f64, paper_w: f64,

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 ({}) — insert with INSERT <name>: {}" => Some(("insert", "blocks-insert-with-insert-name")),
"Blocks ({}): {}" => Some(("insert", "blocks")), "Blocks ({}): {}" => Some(("insert", "blocks")),
"Blue" => Some(("common", "blue")), "Blue" => Some(("common", "blue")),
"Bulge" => Some(("common", "bulge")),
"Body" => Some(("properties", "body")), "Body" => Some(("properties", "body")),
"Bold" => Some(("properties", "bold")), "Bold" => Some(("properties", "bold")),
"Book" => Some(("properties", "book")), "Book" => Some(("properties", "book")),
@ -1535,7 +1536,28 @@ pub(super) fn message_attribute(source: &str) -> Option<(&'static str, &'static
"MLEADERSTYLE: '{}' not found." => Some(("layers", "mleaderstyle-not-found")), "MLEADERSTYLE: '{}' not found." => Some(("layers", "mleaderstyle-not-found")),
"MLEADERSTYLE: current style set to '{name}'." => Some(("layers", "mleaderstyle-current-style-set-to-name")), "MLEADERSTYLE: current style set to '{name}'." => Some(("layers", "mleaderstyle-current-style-set-to-name")),
"MLEADERSTYLE: unknown property '{}'. Try: text_height arrowhead_size landing_distance landing_gap" => Some(("layers", "mleaderstyle-unknown-property-try-text-height")), "MLEADERSTYLE: unknown property '{}'. Try: text_height arrowhead_size landing_distance landing_gap" => Some(("layers", "mleaderstyle-unknown-property-try-text-height")),
"Add Vertex" => Some(("modify", "add-vertex")),
"Closed Cross" => Some(("modify", "closed-cross")),
"Closed Tee" => Some(("modify", "closed-tee")),
"Corner Joint" => Some(("modify", "corner-joint")),
"Cut All" => Some(("modify", "cut-all")),
"Cut Single" => Some(("modify", "cut-single")),
"Delete Vertex" => Some(("modify", "delete-vertex")),
"Loaded multiline styles: {}" => Some(("draw", "loaded-multiline-styles")),
"Merged Cross" => Some(("modify", "merged-cross")),
"Merged Tee" => Some(("modify", "merged-tee")),
"MLEDIT Choose an edit tool:" => Some(("modify", "mledit-choose-an-edit-tool")),
"MLEDIT Select first multiline:" => Some(("modify", "mledit-select-first-multiline")),
"MLEDIT Select second multiline:" => Some(("modify", "mledit-select-second-multiline")),
"MLEDIT Specify the end of the weld range:" => Some(("modify", "mledit-specify-end-of-weld-range")),
"MLEDIT Specify the second cut point:" => Some(("modify", "mledit-specify-second-cut-point")),
"MLINE Current settings: Justification = {}, Scale = {}, Style = {}\nSpecify start point or [Justification/Scale/STyle]:" => Some(("draw", "mline-current-settings")),
"MLINE Enter justification type [Top/Zero/Bottom] <{}>:" => Some(("draw", "mline-enter-justification")),
"MLINE Enter scale factor <{}>:" => Some(("draw", "mline-enter-scale-factor-default")),
"MLINE Enter scale factor:" => Some(("draw", "mline-enter-scale-factor")), "MLINE Enter scale factor:" => Some(("draw", "mline-enter-scale-factor")),
"MLINE Enter style name or [?] <{}>:" => Some(("draw", "mline-enter-style-name")),
"MLINE Specify next point or [Close/Undo]:" => Some(("draw", "mline-specify-next-point-close-undo")),
"MLINE Specify next point or [Undo]:" => Some(("draw", "mline-specify-next-point-undo")),
"MLINE Specify next point (%{count} pts, Enter to finish, C to close, S to set scale):" => Some(("draw", "mline-specify-next-point-count-pts-enter-to-fi")), "MLINE Specify next point (%{count} pts, Enter to finish, C to close, S to set scale):" => Some(("draw", "mline-specify-next-point-count-pts-enter-to-fi")),
"MLINE Specify start point (scale=%{scale}):" => Some(("draw", "mline-specify-start-point-scale-scale")), "MLINE Specify start point (scale=%{scale}):" => Some(("draw", "mline-specify-start-point-scale-scale")),
"MLSTYLE: '{}' already exists." => Some(("layers", "mlstyle-already-exists")), "MLSTYLE: '{}' already exists." => Some(("layers", "mlstyle-already-exists")),
@ -1645,7 +1667,12 @@ pub(super) fn message_attribute(source: &str) -> Option<(&'static str, &'static
"Multileader Style Manager" => Some(("styles", "multileader-style-manager")), "Multileader Style Manager" => Some(("styles", "multileader-style-manager")),
"Multileader draw order:" => Some(("annotate", "multileader-draw-order")), "Multileader draw order:" => Some(("annotate", "multileader-draw-order")),
"Multileader style" => Some(("properties", "multileader-style")), "Multileader style" => Some(("properties", "multileader-style")),
"Multiline style \"%{style}\" was not found." => Some(("draw", "multiline-style-was-not-found")),
"Multiline\nText" => Some(("annotate", "multiline-text")), "Multiline\nText" => Some(("annotate", "multiline-text")),
"Open Cross" => Some(("modify", "open-cross")),
"Open Tee" => Some(("modify", "open-tee")),
"Remove Vertex" => Some(("modify", "remove-vertex")),
"Weld All" => Some(("modify", "weld-all")),
"Multiline Style Manager" => Some(("styles", "multiline-style-manager")), "Multiline Style Manager" => Some(("styles", "multiline-style-manager")),
"Multiple" => Some(("common", "multiple")), "Multiple" => Some(("common", "multiple")),
"Multiple lines" => Some(("properties", "multiple-lines")), "Multiple lines" => Some(("properties", "multiple-lines")),
@ -1842,6 +1869,7 @@ pub(super) fn message_attribute(source: &str) -> Option<(&'static str, &'static
"PDMODE new value [0=dot 1=none 2=+ 3=x 4=tick; +32 circle +64 square]:" => Some(("styles", "pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32")), "PDMODE new value [0=dot 1=none 2=+ 3=x 4=tick; +32 circle +64 square]:" => Some(("styles", "pdmode-new-value-0-dot-1-none-2-3-x-4-tick-32")),
"PDMODE = {v}" => Some(("styles", "pdmode-v")), "PDMODE = {v}" => Some(("styles", "pdmode-v")),
"PDMODE set to {v}" => Some(("styles", "pdmode-set-to-v")), "PDMODE set to {v}" => Some(("styles", "pdmode-set-to-v")),
"PDSIZE new point size (0 = 5% of viewport, >0 = absolute, <0 = viewport percentage):" => Some(("styles", "pdsize-new-point-size-0-5-of-viewport-0-absolu")),
"PDSIZE = {v:.4}" => Some(("styles", "pdsize-v-4")), "PDSIZE = {v:.4}" => Some(("styles", "pdsize-v-4")),
"PDSIZE set to {v:.4}" => Some(("styles", "pdsize-set-to-v-4")), "PDSIZE set to {v:.4}" => Some(("styles", "pdsize-set-to-v-4")),
"PEDIT Enter option:" => Some(("modify", "pedit-enter-option")), "PEDIT Enter option:" => Some(("modify", "pedit-enter-option")),
@ -2050,8 +2078,20 @@ pub(super) fn message_attribute(source: &str) -> Option<(&'static str, &'static
"RAY Specify start point:" => Some(("draw", "ray-specify-start-point")), "RAY Specify start point:" => Some(("draw", "ray-specify-start-point")),
"RAY Specify through point:" => Some(("draw", "ray-specify-through-point")), "RAY Specify through point:" => Some(("draw", "ray-specify-through-point")),
"README could not be loaded" => Some(("plugins", "readme-could-not-be-loaded")), "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 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 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 center point:" => Some(("draw", "rect-cen-specify-center-point")),
"RECT CEN Specify corner point:" => Some(("draw", "rect-cen-specify-corner-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")), "RECT ROT Specify adjacent corner (defines edge direction):" => Some(("draw", "rect-rot-specify-adjacent-corner-defines-edge")),

View file

@ -220,6 +220,7 @@ impl CadCommand for AlignedDimensionCommand {
let p1 = p1.as_vec3(); let p1 = p1.as_vec3();
let p2 = p2.as_vec3(); let p2 = p2.as_vec3();
Some(WireModel { Some(WireModel {
point_marker: None,
taper_widths: Vec::new(), taper_widths: Vec::new(),
world_width: 0.0, world_width: 0.0,
depth_override: None, depth_override: None,
@ -278,6 +279,7 @@ fn preview_aligned(p1: DVec3, p2: DVec3, dim_pt: DVec3) -> WireModel {
let d1 = d1.as_vec3(); let d1 = d1.as_vec3();
let d2 = d2.as_vec3(); let d2 = d2.as_vec3();
WireModel { WireModel {
point_marker: None,
taper_widths: Vec::new(), taper_widths: Vec::new(),
world_width: 0.0, world_width: 0.0,
depth_override: None, depth_override: None,

View file

@ -161,6 +161,7 @@ fn v3(pt: DVec3) -> Vector3 {
fn preview_wire(points: Vec<Vec3>) -> WireModel { fn preview_wire(points: Vec<Vec3>) -> WireModel {
WireModel { WireModel {
point_marker: None,
taper_widths: Vec::new(), taper_widths: Vec::new(),
world_width: 0.0, world_width: 0.0,
depth_override: None, depth_override: None,

View file

@ -192,6 +192,7 @@ fn v3(p: DVec3) -> Vector3 {
fn preview_line(a: Vec3, b: Vec3) -> WireModel { fn preview_line(a: Vec3, b: Vec3) -> WireModel {
WireModel { WireModel {
point_marker: None,
taper_widths: Vec::new(), taper_widths: Vec::new(),
world_width: 0.0, world_width: 0.0,
depth_override: None, depth_override: None,

View file

@ -180,6 +180,7 @@ impl CadCommand for DimBaselineCommand {
let dim_line_pt2 = dim_line_pt2.as_vec3(); let dim_line_pt2 = dim_line_pt2.as_vec3();
let pt = pt.as_vec3(); let pt = pt.as_vec3();
Some(WireModel { Some(WireModel {
point_marker: None,
taper_widths: Vec::new(), taper_widths: Vec::new(),
world_width: 0.0, world_width: 0.0,
depth_override: None, depth_override: None,

View file

@ -160,6 +160,7 @@ impl CadCommand for DimContinueCommand {
let dim_line_pt = p1 + perp * (dim_line_perp - p1.dot(perp)); let dim_line_pt = p1 + perp * (dim_line_perp - p1.dot(perp));
let dim_line_pt2 = pt + perp * (dim_line_perp - pt.dot(perp)); let dim_line_pt2 = pt + perp * (dim_line_perp - pt.dot(perp));
Some(WireModel { Some(WireModel {
point_marker: None,
taper_widths: Vec::new(), taper_widths: Vec::new(),
world_width: 0.0, world_width: 0.0,
depth_override: None, depth_override: None,

View file

@ -86,6 +86,7 @@ impl CadCommand for DimJogLineCommand {
} }
let d = 0.3_f32; let d = 0.3_f32;
Some(WireModel { Some(WireModel {
point_marker: None,
taper_widths: Vec::new(), taper_widths: Vec::new(),
world_width: 0.0, world_width: 0.0,
depth_override: None, depth_override: None,

View file

@ -103,6 +103,7 @@ impl CadCommand for DimTeditCommand {
} }
let d = 0.2_f32; let d = 0.2_f32;
Some(WireModel { Some(WireModel {
point_marker: None,
taper_widths: Vec::new(), taper_widths: Vec::new(),
world_width: 0.0, world_width: 0.0,
depth_override: None, depth_override: None,

View file

@ -300,6 +300,7 @@ fn preview_wire(pts: &[Vec3], arrow_size: f32) -> WireModel {
points.push([w2.x, w2.y, w2.z]); points.push([w2.x, w2.y, w2.z]);
} }
WireModel { WireModel {
point_marker: None,
taper_widths: Vec::new(), taper_widths: Vec::new(),
world_width: 0.0, world_width: 0.0,
depth_override: None, depth_override: None,

View file

@ -237,6 +237,7 @@ fn v3(pt: DVec3) -> Vector3 {
fn preview_wire(points: Vec<DVec3>) -> WireModel { fn preview_wire(points: Vec<DVec3>) -> WireModel {
WireModel { WireModel {
point_marker: None,
taper_widths: Vec::new(), taper_widths: Vec::new(),
world_width: 0.0, world_width: 0.0,
depth_override: None, depth_override: None,

View file

@ -227,6 +227,7 @@ fn preview_wire(pts: &[Vec3], arrow_size: f32) -> WireModel {
points.push([w2.x, w2.y, w2.z]); points.push([w2.x, w2.y, w2.z]);
} }
WireModel { WireModel {
point_marker: None,
taper_widths: Vec::new(), taper_widths: Vec::new(),
world_width: 0.0, world_width: 0.0,
depth_override: None, depth_override: None,

View file

@ -462,6 +462,7 @@ impl CadCommand for MLeaderCollectCommand {
fn preview_wire(pts: &[Vec3]) -> WireModel { fn preview_wire(pts: &[Vec3]) -> WireModel {
WireModel { WireModel {
point_marker: None,
taper_widths: Vec::new(), taper_widths: Vec::new(),
world_width: 0.0, world_width: 0.0,
depth_override: None, depth_override: None,

View file

@ -137,6 +137,7 @@ fn ordinate_elbow(feature: DVec3, leader: DVec3, is_x: bool) -> DVec3 {
fn preview_wire(points: Vec<Vec3>) -> WireModel { fn preview_wire(points: Vec<Vec3>) -> WireModel {
WireModel { WireModel {
point_marker: None,
taper_widths: Vec::new(), taper_widths: Vec::new(),
world_width: 0.0, world_width: 0.0,
depth_override: None, depth_override: None,

View file

@ -93,6 +93,7 @@ impl CadCommand for QdimCommand {
} }
let d = 0.5_f32; let d = 0.5_f32;
Some(WireModel { Some(WireModel {
point_marker: None,
taper_widths: Vec::new(), taper_widths: Vec::new(),
world_width: 0.0, world_width: 0.0,
depth_override: None, depth_override: None,

View file

@ -197,6 +197,7 @@ fn v3(pt: DVec3) -> Vector3 {
fn preview_wire(points: Vec<Vec3>) -> WireModel { fn preview_wire(points: Vec<Vec3>) -> WireModel {
WireModel { WireModel {
point_marker: None,
taper_widths: Vec::new(), taper_widths: Vec::new(),
world_width: 0.0, world_width: 0.0,
depth_override: None, depth_override: None,

View file

@ -199,6 +199,7 @@ impl CadCommand for TableCommand {
.map(|corner| self.plane.to_world(corner).as_vec3().to_array()); .map(|corner| self.plane.to_world(corner).as_vec3().to_array());
let [p0, p1, p2, p3] = corners; let [p0, p1, p2, p3] = corners;
Some(WireModel { Some(WireModel {
point_marker: None,
taper_widths: Vec::new(), taper_widths: Vec::new(),
world_width: 0.0, world_width: 0.0,
depth_override: None, depth_override: None,

View file

@ -103,6 +103,7 @@ impl CadCommand for ToleranceCommand {
pt + self.plane.y * d, pt + self.plane.y * d,
]; ];
Some(WireModel { Some(WireModel {
point_marker: None,
taper_widths: Vec::new(), taper_widths: Vec::new(),
world_width: 0.0, world_width: 0.0,
depth_override: None, depth_override: None,

View file

@ -19,6 +19,13 @@ thread_local! {
static ARRAY_P_ANGLE: Cell<f64> = Cell::new(360.0); // degrees static ARRAY_P_ANGLE: Cell<f64> = Cell::new(360.0); // degrees
static ARRAY_PATH_COUNT: Cell<f64> = Cell::new(6.0); static ARRAY_PATH_COUNT: Cell<f64> = Cell::new(6.0);
static POLYGON_SIDES: 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 { 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_p_angle, set_array_p_angle, ARRAY_P_ANGLE);
accessors!(get_array_path_count, set_array_path_count, ARRAY_PATH_COUNT); accessors!(get_array_path_count, set_array_path_count, ARRAY_PATH_COUNT);
accessors!(get_polygon_sides, set_polygon_sides, POLYGON_SIDES); 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);

View file

@ -201,6 +201,7 @@ impl CadCommand for AttdefCommand {
pt + self.plane.y * d, pt + self.plane.y * d,
]; ];
Some(WireModel { Some(WireModel {
point_marker: None,
taper_widths: Vec::new(), taper_widths: Vec::new(),
world_width: 0.0, world_width: 0.0,
depth_override: None, depth_override: None,

View file

@ -1,58 +1,217 @@
// MLINE command — create a multiline (parallel lines). // MLINE command — create a styled group of parallel lines.
//
// Workflow: pick vertices, Enter to finish.
// Text input (when >= 1 point picked):
// C / CLOSE → close and commit
// S <value> → set scale factor then continue picking
use acadrust::entities::MLine; use acadrust::entities::{MLine, MLineFlags, MLineJustification, MLineSegment, MLineVertex};
use acadrust::objects::MLineStyle;
use acadrust::types::Vector3; use acadrust::types::Vector3;
use acadrust::EntityType; use acadrust::{EntityType, Handle};
use glam::DVec3; use glam::DVec3;
use crate::t;
use crate::command::{CadCommand, CmdResult, WorkingPlane}; use crate::command::{CadCommand, CmdOption, CmdResult, WorkingPlane};
use crate::scene::model::wire_model::WireModel; use crate::scene::model::wire_model::WireModel;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum Step {
Start,
Next,
Justification,
Scale,
Style,
}
pub struct MlineCommand { pub struct MlineCommand {
points: Vec<DVec3>, points: Vec<DVec3>,
scale: f64, scale: f64,
waiting_scale: bool, justification: MLineJustification,
step: Step,
style_name: String, style_name: String,
style_handle: Option<acadrust::Handle>, style_handle: Option<Handle>,
style_element_count: usize, styles: Vec<(Handle, MLineStyle)>,
notice: Option<String>,
plane: WorkingPlane, plane: WorkingPlane,
} }
impl MlineCommand { impl MlineCommand {
#[allow(dead_code)] pub fn with_styles(
pub fn new() -> Self { mut styles: Vec<(Handle, MLineStyle)>,
style_name: impl Into<String>,
scale: f64,
justification: i16,
) -> Self {
let requested = style_name.into();
if styles.is_empty() {
styles.push((Handle::NULL, MLineStyle::standard()));
}
let selected = styles
.iter()
.find(|(_, style)| style.name.eq_ignore_ascii_case(&requested))
.or_else(|| styles.first());
let (style_handle, style_name) = selected
.map(|(handle, style)| {
(
(!handle.is_null()).then_some(*handle),
style.name.clone(),
)
})
.unwrap_or((None, requested));
Self { Self {
points: vec![], points: Vec::new(),
scale: 1.0, scale: if scale.is_finite() && scale != 0.0 {
waiting_scale: false, scale
style_name: "Standard".into(), } else {
style_handle: None, 1.0
style_element_count: 2, },
justification: MLineJustification::from(justification),
step: Step::Start,
style_name,
style_handle,
styles,
notice: None,
plane: WorkingPlane::default(), plane: WorkingPlane::default(),
} }
} }
pub fn with_style( fn selected_style(&self) -> Option<&MLineStyle> {
style_name: impl Into<String>, self.styles
style_handle: Option<acadrust::Handle>, .iter()
style_element_count: usize, .find(|(handle, style)| {
) -> Self { self.style_handle == Some(*handle)
Self { || style.name.eq_ignore_ascii_case(&self.style_name)
points: vec![], })
scale: 1.0, .map(|(_, style)| style)
waiting_scale: false, }
style_name: style_name.into(),
style_handle, fn justification_name(&self) -> String {
style_element_count: style_element_count.max(1), match self.justification {
plane: WorkingPlane::default(), MLineJustification::Top => crate::t!("Top").into_owned(),
MLineJustification::Zero => crate::t!("Zero").into_owned(),
MLineJustification::Bottom => crate::t!("Bottom").into_owned(),
} }
} }
fn select_style(&mut self, name: &str) -> bool {
let Some((handle, style)) = self
.styles
.iter()
.find(|(_, style)| style.name.eq_ignore_ascii_case(name.trim()))
else {
return false;
};
self.style_handle = (!handle.is_null()).then_some(*handle);
self.style_name = style.name.clone();
true
}
fn commit(&self, closed: bool) -> Option<EntityType> {
let style = self.selected_style()?;
let local: Vec<DVec3> = self
.points
.iter()
.map(|point| self.plane.to_local(*point))
.collect();
let entity = build_mline(
&local,
self.scale,
self.justification,
closed,
style,
self.style_handle,
);
Some(self.plane.place_entity(entity))
}
fn preview(&self, cursor: DVec3) -> Option<WireModel> {
let style = self.selected_style()?;
let mut local: Vec<DVec3> = self
.points
.iter()
.map(|point| self.plane.to_local(*point))
.collect();
let cursor_local = self.plane.to_local(cursor);
if local
.last()
.is_none_or(|last| last.distance_squared(cursor_local) > 1.0e-20)
{
local.push(cursor_local);
}
if local.len() < 2 {
return None;
}
let EntityType::MLine(mline) = build_mline(
&local,
self.scale,
self.justification,
false,
style,
self.style_handle,
) else {
return None;
};
let mut points = Vec::new();
for (line_index, line) in
crate::entities::mline::mline_lines_with_style(&mline, style)
.into_iter()
.enumerate()
{
if line_index > 0 {
points.push([f64::NAN; 3]);
}
points.extend(line.points.into_iter().map(|point| {
if point[0].is_nan() {
[f64::NAN; 3]
} else {
let world = self
.plane
.to_world(DVec3::new(point[0], point[1], point[2]));
world.to_array()
}
}));
}
let (points, points_low) =
crate::scene::convert::tessellate::points_to_ds(points);
let fill_tris = crate::entities::mline::mline_fill_triangles_with_style(&mline, style)
.into_iter()
.map(|point| {
let world = self
.plane
.to_world(DVec3::new(point[0], point[1], point[2]));
world.to_array()
})
.collect::<Vec<_>>();
let (fill_tris, fill_tris_low) =
crate::scene::convert::tessellate::points_to_ds(fill_tris);
Some(WireModel {
point_marker: None,
taper_widths: Vec::new(),
world_width: 0.0,
depth_override: None,
display_visible: true,
plot_visible: true,
fill_is_3d: false,
fill_is_2d_solid: true,
render_instance: None,
pick_tris: Vec::new(),
pick_tris_low: Vec::new(),
dash_from_start: false,
dash_align_end: None,
text_verts: Vec::new(),
name: "mline_preview".into(),
points,
points_low,
color: WireModel::CYAN,
selected: false,
pattern_length: 0.0,
pattern: [0.0; 8],
line_weight_px: 1.0,
snap_pts: Vec::new(),
tangent_geoms: Vec::new(),
aci: 0,
key_vertices: Vec::new(),
aabb: WireModel::UNBOUNDED_AABB,
plinegen: true,
fill_tris,
fill_tris_low,
})
}
} }
impl CadCommand for MlineCommand { impl CadCommand for MlineCommand {
@ -65,177 +224,256 @@ impl CadCommand for MlineCommand {
} }
fn prompt(&self) -> String { fn prompt(&self) -> String {
if self.waiting_scale { let notice = self
t!("MLINE Enter scale factor:").into_owned() .notice
} else if self.points.is_empty() { .as_ref()
t!( .map(|value| format!("{value}\n"))
"MLINE Specify start point (scale=%{scale}):", .unwrap_or_default();
scale = format!("{:.2}", self.scale) let body = match self.step {
Step::Start => crate::tf!(
"MLINE Current settings: Justification = {}, Scale = {}, Style = {}\nSpecify start point or [Justification/Scale/STyle]:",
self.justification_name(), self.scale, self.style_name
) )
.into_owned() .into_owned(),
} else { Step::Next if self.points.len() >= 3 => {
t!( crate::t!("MLINE Specify next point or [Close/Undo]:").into_owned()
"MLINE Specify next point (%{count} pts, Enter to finish, C to close, S to set scale):", }
count = self.points.len() Step::Next => crate::t!("MLINE Specify next point or [Undo]:").into_owned(),
Step::Justification => crate::tf!(
"MLINE Enter justification type [Top/Zero/Bottom] <{}>:",
self.justification_name()
) )
.into_owned() .into_owned(),
Step::Scale => {
crate::tf!("MLINE Enter scale factor <{}>:", self.scale).into_owned()
}
Step::Style => {
crate::tf!("MLINE Enter style name or [?] <{}>:", self.style_name).into_owned()
}
};
format!("{notice}{body}")
}
fn options(&self) -> Vec<CmdOption> {
match self.step {
Step::Start => vec![
CmdOption::new(crate::t!("Justification").as_ref(), "J"),
CmdOption::new(crate::t!("Scale").as_ref(), "S"),
CmdOption::new(crate::t!("Style").as_ref(), "ST"),
],
Step::Next if self.points.len() >= 3 => vec![
CmdOption::new(crate::t!("Close").as_ref(), "C"),
CmdOption::new(crate::t!("Undo").as_ref(), "U"),
],
Step::Next => vec![CmdOption::new(crate::t!("Undo").as_ref(), "U")],
Step::Justification => vec![
CmdOption::new(crate::t!("Top").as_ref(), "T"),
CmdOption::new(crate::t!("Zero").as_ref(), "Z"),
CmdOption::new(crate::t!("Bottom").as_ref(), "B"),
],
Step::Scale => Vec::new(),
Step::Style => self
.styles
.iter()
.map(|(_, style)| CmdOption::new(&style.name, &style.name))
.collect(),
} }
} }
fn wants_text_input(&self) -> bool { fn wants_text_input(&self) -> bool {
self.waiting_scale || !self.points.is_empty() true
} }
fn point_step_accepts_keywords(&self) -> bool { fn point_step_accepts_keywords(&self) -> bool {
// The vertex steps accept J / S keywords but are point picks, so keep matches!(self.step, Step::Start | Step::Next)
// polar dynamic input. The scale prompt (`waiting_scale`) is genuine }
// text entry and is excluded.
!self.waiting_scale && !self.points.is_empty() fn mline_settings(&self) -> Option<(f64, i16, String, Option<Handle>)> {
Some((
self.scale,
self.justification as i16,
self.style_name.clone(),
self.style_handle,
))
} }
fn on_text_input(&mut self, text: &str) -> Option<CmdResult> { fn on_text_input(&mut self, text: &str) -> Option<CmdResult> {
// Waiting for scale value let token = text.trim();
if self.waiting_scale { let upper = token.to_uppercase();
let v: f64 = text self.notice = None;
.trim() match self.step {
.replace(',', ".") Step::Start => match upper.as_str() {
.parse() "J" | "JUSTIFICATION" => self.step = Step::Justification,
.ok() "S" | "SCALE" => self.step = Step::Scale,
.filter(|&v: &f64| v > 0.0)?; "ST" | "STYLE" => self.step = Step::Style,
self.scale = v; _ => return None,
self.waiting_scale = false; },
return Some(CmdResult::NeedPoint); Step::Next => match upper.as_str() {
} "U" | "UNDO" => {
self.points.pop();
let up = text.trim().to_uppercase(); if self.points.is_empty() {
self.step = Step::Start;
// Close command }
if (up == "C" || up == "CLOSE") && self.points.len() >= 3 { }
let entity = build_mline( "C" | "CLOSE" if self.points.len() >= 3 => {
&self return self.commit(true).map(CmdResult::CommitAndExit);
.points }
.iter() _ => return None,
.map(|point| self.plane.to_local(*point)) },
.collect::<Vec<_>>(), Step::Justification => {
self.scale, self.justification = match upper.as_str() {
true, "T" | "TOP" => MLineJustification::Top,
&self.style_name, "Z" | "ZERO" => MLineJustification::Zero,
self.style_handle, "B" | "BOTTOM" => MLineJustification::Bottom,
self.style_element_count, _ => return None,
); };
return Some(CmdResult::CommitAndExit(self.plane.place_entity(entity))); self.step = Step::Start;
} }
Step::Scale => {
// Scale: "S" alone → prompt for value let value = token.replace(',', ".").parse::<f64>().ok()?;
if up == "S" { if !value.is_finite() || value == 0.0 {
self.waiting_scale = true; return None;
return Some(CmdResult::NeedPoint); }
} self.scale = value;
self.step = Step::Start;
// Scale: "S <value>" inline }
if let Some(rest) = up.strip_prefix("S ") { Step::Style => {
if let Ok(v) = rest.trim().replace(',', ".").parse::<f64>() { if token == "?" {
if v > 0.0 { self.notice = Some(crate::tf!(
self.scale = v; "Loaded multiline styles: {}",
self.styles
.iter()
.map(|(_, style)| style.name.as_str())
.collect::<Vec<_>>()
.join(", ")
)
.into_owned());
} else if self.select_style(token) {
self.step = Step::Start;
} else {
self.notice = Some(
crate::t!(
"Multiline style \"%{style}\" was not found.",
style = token
)
.into_owned(),
);
} }
return Some(CmdResult::NeedPoint);
} }
} }
Some(CmdResult::NeedPoint)
None
} }
fn on_point(&mut self, pt: DVec3) -> CmdResult { fn on_point(&mut self, point: DVec3) -> CmdResult {
self.points.push(pt); if !matches!(self.step, Step::Start | Step::Next) {
return CmdResult::NeedPoint;
}
if self
.points
.last()
.is_some_and(|last| last.distance_squared(point) <= 1.0e-20)
{
return CmdResult::NeedPoint;
}
self.points.push(point);
self.step = Step::Next;
CmdResult::NeedPoint CmdResult::NeedPoint
} }
fn on_enter(&mut self) -> CmdResult { fn on_enter(&mut self) -> CmdResult {
if self.points.len() < 2 { match self.step {
return CmdResult::Cancel; Step::Start if self.points.is_empty() => CmdResult::Cancel,
Step::Next if self.points.len() >= 2 => self
.commit(false)
.map(CmdResult::CommitAndExit)
.unwrap_or(CmdResult::Cancel),
Step::Justification | Step::Scale | Step::Style => {
self.step = Step::Start;
CmdResult::NeedPoint
}
_ => CmdResult::Cancel,
} }
let entity = build_mline(
&self
.points
.iter()
.map(|point| self.plane.to_local(*point))
.collect::<Vec<_>>(),
self.scale,
false,
&self.style_name,
self.style_handle,
self.style_element_count,
);
CmdResult::CommitAndExit(self.plane.place_entity(entity))
} }
fn on_mouse_move(&mut self, pt: DVec3) -> Option<WireModel> { fn on_undo_step(&mut self) -> Option<CmdResult> {
let pt = pt.as_vec3();
if self.points.is_empty() { if self.points.is_empty() {
return None; return None;
} }
let mut pts: Vec<[f32; 3]> = self self.points.pop();
.points if self.points.is_empty() {
.iter() self.step = Step::Start;
.map(|p| [p.x as f32, p.y as f32, p.z as f32]) }
.collect(); Some(CmdResult::NeedPoint)
pts.push([pt.x, pt.y, pt.z]);
Some(WireModel {
taper_widths: Vec::new(),
world_width: 0.0,
depth_override: None,
display_visible: true,
plot_visible: true,
fill_is_3d: false,
fill_is_2d_solid: false,
render_instance: None,
pick_tris: Vec::new(),
pick_tris_low: Vec::new(),
dash_from_start: false,
dash_align_end: None,
text_verts: Vec::new(),
name: "mline_preview".into(),
points: pts,
points_low: Vec::new(),
color: WireModel::CYAN,
selected: false,
pattern_length: 0.0,
pattern: [0.0; 8],
line_weight_px: 1.0,
snap_pts: vec![],
tangent_geoms: vec![],
aci: 0,
key_vertices: vec![],
aabb: WireModel::UNBOUNDED_AABB,
plinegen: true,
fill_tris: vec![],
fill_tris_low: Vec::new(),
})
} }
fn on_mouse_move(&mut self, point: DVec3) -> Option<WireModel> {
(!self.points.is_empty()).then(|| self.preview(point)).flatten()
}
}
pub(crate) fn sync_mline_element_parameters(mline: &mut MLine, style: &MLineStyle) {
let offsets: Vec<f64> = if style.elements.is_empty() {
vec![0.5, -0.5]
} else {
style.elements.iter().map(|element| element.offset).collect()
};
let minimum = offsets.iter().copied().fold(f64::INFINITY, f64::min);
let maximum = offsets
.iter()
.copied()
.fold(f64::NEG_INFINITY, f64::max);
let shift = match mline.justification {
MLineJustification::Top => -maximum,
MLineJustification::Zero => 0.0,
MLineJustification::Bottom => -minimum,
};
let normal = glam::DVec3::new(mline.normal.x, mline.normal.y, mline.normal.z)
.normalize_or(glam::DVec3::Z);
for vertex in &mut mline.vertices {
let direction = glam::DVec3::new(vertex.direction.x, vertex.direction.y, vertex.direction.z)
.normalize_or(glam::DVec3::X);
let miter = glam::DVec3::new(vertex.miter.x, vertex.miter.y, vertex.miter.z)
.normalize_or(glam::DVec3::Y);
let factor = miter.dot(normal.cross(direction)).abs().max(1.0e-9);
vertex
.segments
.resize_with(offsets.len(), MLineSegment::new);
vertex.segments.truncate(offsets.len());
for (segment, offset) in vertex.segments.iter_mut().zip(&offsets) {
let value = (offset + shift) * mline.scale_factor / factor;
if let Some(first) = segment.parameters.first_mut() {
*first = value;
} else {
segment.parameters.extend([value, 0.0]);
}
}
}
mline.style_element_count = offsets.len();
} }
fn build_mline( fn build_mline(
pts: &[DVec3], points: &[DVec3],
scale: f64, scale: f64,
justification: MLineJustification,
closed: bool, closed: bool,
style_name: &str, style: &MLineStyle,
style_handle: Option<acadrust::Handle>, style_handle: Option<Handle>,
style_element_count: usize,
) -> EntityType { ) -> EntityType {
let mut mline = MLine::new(); let mut mline = MLine::new();
mline.scale_factor = scale; mline.scale_factor = scale;
mline.style_name = style_name.to_string(); mline.justification = justification;
mline.style_name = style.name.clone();
mline.style_handle = style_handle; mline.style_handle = style_handle;
mline.style_element_count = style_element_count; mline.style_element_count = style.elements.len().max(1);
for point in pts { for point in points {
mline.add_vertex(Vector3::new(point.x, point.y, point.z)); let mut vertex = MLineVertex::new(Vector3::new(point.x, point.y, point.z));
} vertex.init_segments(mline.style_element_count);
if closed { mline.vertices.push(vertex);
mline.close();
} }
mline.flags.set(MLineFlags::CLOSED, closed);
crate::entities::mline::rebuild_mline_geometry(&mut mline);
sync_mline_element_parameters(&mut mline, style);
EntityType::MLine(mline) EntityType::MLine(mline)
} }
inventory::submit!(crate::command::CommandRegistration { names: &["MLINE"] });
// ── Autocomplete registry ─────────────────────────────────
inventory::submit!(crate::command::CommandRegistration { names: &["MLINE"] }); // MlineCommand

View file

@ -123,6 +123,7 @@ impl CadCommand for ImageCommand {
let [p0, p1, p2, p3] = corners; let [p0, p1, p2, p3] = corners;
Some(WireModel { Some(WireModel {
point_marker: None,
taper_widths: Vec::new(), taper_widths: Vec::new(),
world_width: 0.0, world_width: 0.0,
depth_override: None, depth_override: None,

View file

@ -85,6 +85,7 @@ impl CadCommand for RayCommand {
let dir = (pt - base).normalize_or_zero(); let dir = (pt - base).normalize_or_zero();
let far = base + dir * DISPLAY_EXTENT; let far = base + dir * DISPLAY_EXTENT;
Some(WireModel { Some(WireModel {
point_marker: None,
taper_widths: Vec::new(), taper_widths: Vec::new(),
world_width: 0.0, world_width: 0.0,
depth_override: None, depth_override: None,
@ -187,6 +188,7 @@ impl CadCommand for XLineCommand {
let far_pos = base + dir * DISPLAY_EXTENT; let far_pos = base + dir * DISPLAY_EXTENT;
let far_neg = base - dir * DISPLAY_EXTENT; let far_neg = base - dir * DISPLAY_EXTENT;
Some(WireModel { Some(WireModel {
point_marker: None,
taper_widths: Vec::new(), taper_widths: Vec::new(),
world_width: 0.0, world_width: 0.0,
depth_override: None, depth_override: None,

View file

@ -13,6 +13,11 @@
use acadrust::entities::LwVertex; use acadrust::entities::LwVertex;
use acadrust::types::Vector2; use acadrust::types::Vector2;
use acadrust::{EntityType, LwPolyline}; 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::t;
use crate::command::{CadCommand, CmdResult, WorkingPlane}; use crate::command::{CadCommand, CmdResult, WorkingPlane};
@ -21,21 +26,6 @@ use crate::modules::IconKind;
use crate::scene::model::wire_model::WireModel; use crate::scene::model::wire_model::WireModel;
use glam::DVec3; 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`, /// 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). /// 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] { 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 { fn wire_loop(pts: Vec<[f64; 3]>) -> WireModel {
let mut p = pts; let mut p = pts;
if let Some(&first) = p.first() { 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) ────────────────────────────── // ── 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 { pub struct RectCommand {
a: Option<DVec3>, step: RectStep,
first: Option<DVec3>,
plane: WorkingPlane, plane: WorkingPlane,
chamfer_first: f64,
chamfer_second: f64,
fillet_radius: f64,
elevation: f64,
thickness: f64,
width: f64,
rotation_deg: f64,
} }
impl RectCommand { impl RectCommand {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
a: None, step: RectStep::FirstCorner,
first: None,
plane: WorkingPlane::default(), 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 { impl CadCommand for RectCommand {
@ -177,78 +459,363 @@ impl CadCommand for RectCommand {
self.plane = plane; self.plane = plane;
} }
fn prompt(&self) -> String { fn prompt(&self) -> String {
if self.a.is_none() { match self.step {
crate::t!("RECT Specify first corner:").into_owned() RectStep::FirstCorner => crate::t!("RECT Specify first corner:").into_owned(),
} else { RectStep::Opposite => crate::t!("RECT Specify opposite corner:").into_owned(),
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> { fn options(&self) -> Vec<crate::command::CmdOption> {
use crate::command::CmdOption; use crate::command::CmdOption;
// First corner also offers the alternate rectangle methods; later steps match self.step {
// are plain point picks. (#304) RectStep::FirstCorner => vec![
if self.a.is_none() { CmdOption::new("Chamfer", "CHAMFER"),
vec![ 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("Rotation", "ROTATION"),
CmdOption::new("Center", "CENTER"), ],
] RectStep::AreaBasis(_) => vec![
} else { CmdOption::new("Length", "LENGTH"),
vec![] CmdOption::new("Width", "WIDTH"),
],
_ => vec![],
} }
} }
fn point_step_accepts_keywords(&self) -> bool { 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> { fn on_text_input(&mut self, text: &str) -> Option<CmdResult> {
// At the first corner, keyword options hand off to the dedicated let upper = text.trim().to_uppercase();
// variant command. (#304) match self.step {
if self.a.is_none() { RectStep::FirstCorner => {
return match text.trim().to_uppercase().as_str() { return match upper.as_str() {
"R" | "ROTATION" => Some(CmdResult::Dispatch("RECT_ROT".into())), "C" | "CHAMFER" => {
"C" | "CENTER" => Some(CmdResult::Dispatch("RECT_CEN".into())), self.step = RectStep::ChamferFirst;
_ => None, 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 { fn on_point(&mut self, pt: DVec3) -> CmdResult {
match self.a { match self.step {
None => { RectStep::FirstCorner => {
self.a = Some(pt); 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 CmdResult::NeedPoint
} }
Some(a) => { RectStep::Opposite => self.finish(pt, None),
let c = ucs_box_corners(a, pt, self.plane); RectStep::PlaceSized { width, height } => {
CmdResult::CommitAndExit(make_pline(&c, self.plane)) 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 { 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 { fn on_escape(&mut self) -> CmdResult {
CmdResult::Cancel CmdResult::Cancel
} }
fn on_mouse_move(&mut self, pt: DVec3) -> Option<WireModel> { fn on_mouse_move(&mut self, pt: DVec3) -> Option<WireModel> {
let a = self.a?; let first = self.first?;
let c = ucs_box_corners(a, pt, self.plane); let fixed_dimensions = match self.step {
Some(wire_loop(vec![ RectStep::PlaceSized { width, height } => Some((width, height)),
[c[0].x, c[0].y, c[0].z], RectStep::Opposite => None,
[c[1].x, c[1].y, c[1].z], _ => return None,
[c[2].x, c[2].y, c[2].z], };
[c[3].x, c[3].y, c[3].z], 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> { fn dyn_spec(&self) -> Option<crate::command::DynSpec> {
use crate::command::{DynAnchor, DynFieldSpec, DynGuide, DynRole, DynSpec}; use crate::command::{DynAnchor, DynFieldSpec, DynGuide, DynRole, DynSpec};
// Opposite corner: enter width and height (signed deltas from the first if !matches!(self.step, RectStep::Opposite) || self.rotation_deg.abs() > 1.0e-9 {
// corner), with the rectangle drawn as the guide. First corner is a return None;
// normal point pick. }
self.a.map(|a| DynSpec { self.first.map(|first| DynSpec {
anchor: DynAnchor::Point(a), anchor: DynAnchor::Point(first),
fields: vec![ fields: vec![
DynFieldSpec::new(DynRole::Width), DynFieldSpec::new(DynRole::Width),
DynFieldSpec::new(DynRole::Height), DynFieldSpec::new(DynRole::Height),
@ -305,6 +872,9 @@ impl CadCommand for RectRotCommand {
CmdResult::NeedPoint CmdResult::NeedPoint
} }
1 => { 1 => {
if plane_distance(self.a, pt, self.plane) <= 1.0e-9 {
return CmdResult::NeedPoint;
}
self.b = pt; self.b = pt;
self.step = 2; self.step = 2;
CmdResult::NeedPoint CmdResult::NeedPoint
@ -318,6 +888,9 @@ impl CadCommand for RectRotCommand {
let dir = (b - a).normalize_or_zero(); let dir = (b - a).normalize_or_zero();
let perp = DVec3::new(-dir.y, dir.x, 0.0); let perp = DVec3::new(-dir.y, dir.x, 0.0);
let h = (pt - b).dot(perp); // signed height let h = (pt - b).dot(perp); // signed height
if h.abs() <= 1.0e-9 {
return CmdResult::NeedPoint;
}
let c = b + perp * h; let c = b + perp * h;
let d = a + perp * h; let d = a + perp * h;
let corners = [a, b, c, d].map(|point| self.plane.to_world(point)); let corners = [a, b, c, d].map(|point| self.plane.to_world(point));
@ -420,6 +993,10 @@ impl CadCommand for RectCenCommand {
CmdResult::NeedPoint CmdResult::NeedPoint
} }
Some(c) => { 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); let q = ucs_box_around_center(c, pt, self.plane);
CmdResult::CommitAndExit(make_pline(&q, self.plane)) CmdResult::CommitAndExit(make_pline(&q, self.plane))
} }

View file

@ -362,6 +362,7 @@ impl CadCommand for AreaCommand {
points.push(to_render(point)); points.push(to_render(point));
points.push(to_render(self.points[0])); points.push(to_render(self.points[0]));
Some(WireModel { Some(WireModel {
point_marker: None,
taper_widths: Vec::new(), taper_widths: Vec::new(),
world_width: 0.0, world_width: 0.0,
depth_override: None, depth_override: None,

View file

@ -85,6 +85,7 @@ impl CadCommand for DistCommand {
let p1 = self.first?; let p1 = self.first?;
// The preview wire is purely visual, so f32 vertices are fine here. // The preview wire is purely visual, so f32 vertices are fine here.
Some(WireModel { Some(WireModel {
point_marker: None,
taper_widths: Vec::new(), taper_widths: Vec::new(),
world_width: 0.0, world_width: 0.0,
depth_override: None, depth_override: None,

View file

@ -70,6 +70,7 @@ impl MeasureGeomCommand {
/// Build a cyan preview wire connecting the picked points and the cursor. /// Build a cyan preview wire connecting the picked points and the cursor.
fn preview_wire(name: &str, pts: Vec<[f32; 3]>) -> WireModel { fn preview_wire(name: &str, pts: Vec<[f32; 3]>) -> WireModel {
WireModel { WireModel {
point_marker: None,
taper_widths: Vec::new(), taper_widths: Vec::new(),
world_width: 0.0, world_width: 0.0,
depth_override: None, depth_override: None,

File diff suppressed because it is too large Load diff

View file

@ -12,6 +12,7 @@ pub mod geom;
pub mod join; pub mod join;
pub mod lengthen; pub mod lengthen;
pub mod mirror; pub mod mirror;
pub mod mledit;
pub mod offset; pub mod offset;
pub mod pedit; pub mod pedit;
pub mod block_edit; pub mod block_edit;

View file

@ -3719,6 +3719,7 @@ fn collect_runs(pts: &[[f64; 2]], take: &dyn Fn([f64; 2]) -> bool, out: &mut Vec
/// Build a preview wire from a NaN-break point list. /// Build a preview wire from a NaN-break point list.
fn preview_wire(points: Vec<[f32; 3]>, color: [f32; 4], name: &str) -> WireModel { fn preview_wire(points: Vec<[f32; 3]>, color: [f32; 4], name: &str) -> WireModel {
WireModel { WireModel {
point_marker: None,
taper_widths: Vec::new(), taper_widths: Vec::new(),
world_width: 0.0, world_width: 0.0,
depth_override: None, depth_override: None,

View file

@ -511,6 +511,7 @@ inventory::submit!(crate::command::CommandRegistration {
fn wire(name: &str, points: Vec<[f32; 3]>) -> WireModel { fn wire(name: &str, points: Vec<[f32; 3]>) -> WireModel {
WireModel { WireModel {
point_marker: None,
taper_widths: Vec::new(), taper_widths: Vec::new(),
world_width: 0.0, world_width: 0.0,
depth_override: None, depth_override: None,

View file

@ -54,6 +54,7 @@ impl CadCommand for PlotWindowCommand {
let p1 = self.p1?.as_vec3(); let p1 = self.p1?.as_vec3();
// Draw the selection rectangle. // Draw the selection rectangle.
Some(WireModel { Some(WireModel {
point_marker: None,
taper_widths: Vec::new(), taper_widths: Vec::new(),
world_width: 0.0, world_width: 0.0,
depth_override: None, depth_override: None,

View file

@ -136,6 +136,7 @@ impl CadCommand for ZoomWindowCommand {
let max = p1.max(pt); let max = p1.max(pt);
// Draw a rectangle preview // Draw a rectangle preview
Some(WireModel { Some(WireModel {
point_marker: None,
taper_widths: Vec::new(), taper_widths: Vec::new(),
world_width: 0.0, world_width: 0.0,
depth_override: None, depth_override: None,

View file

@ -20,9 +20,10 @@ use std::sync::Arc;
use acadrust::types::{Color as AcadColor, LineWeight, Transform, Vector3}; use acadrust::types::{Color as AcadColor, LineWeight, Transform, Vector3};
use acadrust::{CadDocument, EntityType, Handle}; use acadrust::{CadDocument, EntityType, Handle};
use cadkernel::space::{Plane as KernelPlane, Vec3 as KernelVec3};
use crate::scene::convert::tessellate; use crate::scene::convert::tessellate;
use crate::scene::model::wire_model::{SnapHint, TangentGeom, WireModel}; use crate::scene::model::wire_model::{PointMarker, SnapHint, TangentGeom, WireModel};
const MAX_NESTING_DEPTH: usize = 32; const MAX_NESTING_DEPTH: usize = 32;
/// Skip wires whose world-AABB projects to fewer than this many pixels in /// Skip wires whose world-AABB projects to fewer than this many pixels in
@ -38,6 +39,7 @@ pub struct LocalWire {
/// Low-bit residual paired with `points` so block-instance wires keep /// Low-bit residual paired with `points` so block-instance wires keep
/// sub-f32 precision once the renderer translates them to world space. /// sub-f32 precision once the renderer translates them to world space.
pub points_low: Vec<[f32; 3]>, pub points_low: Vec<[f32; 3]>,
pub point_marker: Option<PointMarker>,
/// SDF glyph quads for block-internal text, in block-local coordinates. /// SDF glyph quads for block-internal text, in block-local coordinates.
/// Non-empty only when SDF text is on and this sub is a TEXT/MTEXT. The /// Non-empty only when SDF text is on and this sub is a TEXT/MTEXT. The
/// expand-time transform (`emit_wire`) maps each vertex to world exactly /// expand-time transform (`emit_wire`) maps each vertex to world exactly
@ -132,6 +134,7 @@ pub enum LocalSub {
pub struct BlockDefn { pub struct BlockDefn {
pub subs: Vec<LocalSub>, pub subs: Vec<LocalSub>,
pub base_point: Vector3, pub base_point: Vector3,
pub has_point_marker: bool,
/// Union of every sub's local AABB (including nested-INSERT contributions /// Union of every sub's local AABB (including nested-INSERT contributions
/// resolved at expand time via their own defn's `aabb_local`). XY only — /// resolved at expand time via their own defn's `aabb_local`). XY only —
/// the wire renderer is 2D-dominant. Expressed in this defn's *offset* /// the wire renderer is 2D-dominant. Expressed in this defn's *offset*
@ -312,23 +315,49 @@ impl BlockCache {
// many parents is re-walked per parent — real work on block-heavy // many parents is re-walked per parent — real work on block-heavy
// drawings — and the per-name walks are independent, so they fan out. // drawings — and the per-name walks are independent, so they fan out.
let this: &Self = self; let this: &Self = self;
let resolved: Vec<(&String, [f32; 4])> = names let resolved: Vec<(&String, [f32; 4], bool)> = names
.par_iter() .par_iter()
.map(|name| { .map(|name| {
let mut visited: Vec<String> = Vec::new(); let mut visited: Vec<String> = Vec::new();
(name, this.defn_aabb_recursive(name, &mut visited)) let aabb = this.defn_aabb_recursive(name, &mut visited);
visited.clear();
let has_marker = this.defn_has_point_marker_recursive(name, &mut visited);
(name, aabb, has_marker)
}) })
.collect(); .collect();
// Phase 2 (serial): store the AABB back into each defn. // Phase 2 (serial): store the AABB back into each defn.
for (name, aabb) in resolved { for (name, aabb, has_marker) in resolved {
if let Some(defn_arc) = self.defns.get_mut(name) { if let Some(defn_arc) = self.defns.get_mut(name) {
let mut defn = (**defn_arc).clone(); let mut defn = (**defn_arc).clone();
defn.aabb_local = aabb; defn.aabb_local = aabb;
defn.has_point_marker = has_marker;
*defn_arc = Arc::new(defn); *defn_arc = Arc::new(defn);
} }
} }
} }
fn defn_has_point_marker_recursive(
&self,
block_name: &str,
visited: &mut Vec<String>,
) -> bool {
if visited.iter().any(|name| name == block_name) {
return false;
}
let Some(defn) = self.defns.get(block_name) else {
return false;
};
visited.push(block_name.to_string());
let found = defn.subs.iter().any(|sub| match sub {
LocalSub::Wire(wire) => wire.point_marker.is_some(),
LocalSub::Nested(insert) => {
self.defn_has_point_marker_recursive(&insert.block_name, visited)
}
});
visited.pop();
found
}
/// Returns the union AABB for `block_name`'s defn, expressed in **that /// Returns the union AABB for `block_name`'s defn, expressed in **that
/// defn's offset frame** (so its caller can store it in /// defn's offset frame** (so its caller can store it in
/// `BlockDefn.aabb_local` without a coordinate-frame mismatch). /// `BlockDefn.aabb_local` without a coordinate-frame mismatch).
@ -619,6 +648,7 @@ fn build_defn(
BlockDefn { BlockDefn {
subs, subs,
base_point: crate::scene::render_graph::block_base_point(doc, block_name), base_point: crate::scene::render_graph::block_base_point(doc, block_name),
has_point_marker: false,
aabb_local: [0.0; 4], aabb_local: [0.0; 4],
child_count: br.entity_handles.len(), child_count: br.entity_handles.len(),
} }
@ -816,6 +846,7 @@ fn tessellate_sub_local(
result.push(LocalWire { result.push(LocalWire {
points: wire.points, points: wire.points,
points_low: wire.points_low, points_low: wire.points_low,
point_marker: wire.point_marker,
text_verts: wire.text_verts, text_verts: wire.text_verts,
key_vertices: wire.key_vertices, key_vertices: wire.key_vertices,
snap_pts: wire.snap_pts, snap_pts: wire.snap_pts,
@ -1069,7 +1100,7 @@ pub fn expand_insert(
// Whole-Insert pixel-size LOD: if the entire Insert footprint projects // Whole-Insert pixel-size LOD: if the entire Insert footprint projects
// to sub-pixel size, skip it entirely. // to sub-pixel size, skip it entirely.
if let Some(wpp) = world_per_pixel { if let Some(wpp) = world_per_pixel {
if aabb_pixel_size(insert_local, wpp) < MIN_PIXEL_SIZE { if !defn.has_point_marker && aabb_pixel_size(insert_local, wpp) < MIN_PIXEL_SIZE {
return Some(vec![]); return Some(vec![]);
} }
} }
@ -1244,6 +1275,9 @@ fn translated_prototype_wire(
translate_double_single(&mut wire.points, &mut wire.points_low, delta); translate_double_single(&mut wire.points, &mut wire.points_low, delta);
translate_double_single(&mut wire.fill_tris, &mut wire.fill_tris_low, delta); translate_double_single(&mut wire.fill_tris, &mut wire.fill_tris_low, delta);
translate_double_single(&mut wire.pick_tris, &mut wire.pick_tris_low, delta); translate_double_single(&mut wire.pick_tris, &mut wire.pick_tris_low, delta);
if let Some(marker) = &mut wire.point_marker {
marker.origin += glam::DVec3::from_array(delta);
}
for (point, _) in &mut wire.snap_pts { for (point, _) in &mut wire.snap_pts {
point.x += delta[0]; point.x += delta[0];
point.y += delta[1]; point.y += delta[1];
@ -1403,6 +1437,7 @@ struct StyleKey {
/// bands vs thin wires of the same colour/style — in separate batches so the /// bands vs thin wires of the same colour/style — in separate batches so the
/// finalized WireModel carries one correct `world_width`. /// finalized WireModel carries one correct `world_width`.
world_width: u32, world_width: u32,
point_marker: Option<PointMarkerKey>,
aci: u8, aci: u8,
plinegen: bool, plinegen: bool,
/// Marks batches that emit only `fill_tris` with no wire `points`. The /// Marks batches that emit only `fill_tris` with no wire `points`. The
@ -1422,6 +1457,15 @@ struct StyleKey {
depth_bits: u32, depth_bits: u32,
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
struct PointMarkerKey {
origin: [u64; 3],
normal: [u64; 3],
axis_x: [u64; 3],
axis_y: [u64; 3],
viewport_percent: u32,
}
#[derive(Default, Debug)] #[derive(Default, Debug)]
struct BatchEntry { struct BatchEntry {
color: [f32; 4], color: [f32; 4],
@ -1432,6 +1476,7 @@ struct BatchEntry {
pattern: [f32; 8], pattern: [f32; 8],
line_weight_px: f32, line_weight_px: f32,
world_width: f32, world_width: f32,
point_marker: Option<PointMarker>,
aci: u8, aci: u8,
plinegen: bool, plinegen: bool,
/// Composed block-local draw-order offset for a band batch (see /// Composed block-local draw-order offset for a band batch (see
@ -1511,6 +1556,7 @@ impl BatchEntry {
pat: [f32; 8], pat: [f32; 8],
lw_px: f32, lw_px: f32,
world_width: f32, world_width: f32,
point_marker: Option<PointMarker>,
aci: u8, aci: u8,
plinegen: bool, plinegen: bool,
_is_fill_only: bool, _is_fill_only: bool,
@ -1533,6 +1579,7 @@ impl BatchEntry {
pattern: pat, pattern: pat,
line_weight_px: lw_px, line_weight_px: lw_px,
world_width, world_width,
point_marker,
aci, aci,
plinegen, plinegen,
fill_is_2d_solid, fill_is_2d_solid,
@ -1582,6 +1629,7 @@ impl Batches {
} }
} }
WireModel { WireModel {
point_marker: b.point_marker,
taper_widths: Vec::new(), taper_widths: Vec::new(),
world_width: b.world_width, world_width: b.world_width,
depth_override: b.local_depth, depth_override: b.local_depth,
@ -1634,6 +1682,7 @@ fn style_key(
pat: [f32; 8], pat: [f32; 8],
lw_px: f32, lw_px: f32,
world_width: f32, world_width: f32,
point_marker: Option<PointMarker>,
aci: u8, aci: u8,
plinegen: bool, plinegen: bool,
is_fill_only: bool, is_fill_only: bool,
@ -1666,6 +1715,13 @@ fn style_key(
], ],
line_weight_px: lw_px.to_bits(), line_weight_px: lw_px.to_bits(),
world_width: world_width.to_bits(), world_width: world_width.to_bits(),
point_marker: point_marker.map(|marker| PointMarkerKey {
origin: marker.origin.to_array().map(f64::to_bits),
normal: marker.normal.to_array().map(f64::to_bits),
axis_x: marker.axis_x.to_array().map(f64::to_bits),
axis_y: marker.axis_y.to_array().map(f64::to_bits),
viewport_percent: marker.viewport_percent.to_bits(),
}),
aci, aci,
plinegen, plinegen,
is_fill_only, is_fill_only,
@ -2004,6 +2060,69 @@ fn transformed_wire_length_scale(lw: &LocalWire, xform: &Transform) -> f32 {
} }
} }
fn transformed_point_marker(marker: PointMarker, xform: &Transform) -> (PointMarker, f64) {
let origin = xform.apply(Vector3::new(marker.origin.x, marker.origin.y, marker.origin.z));
let map_direction = |direction: glam::DVec3| {
let mapped = xform.matrix.transform_direction(Vector3::new(
direction.x,
direction.y,
direction.z,
));
KernelVec3::new(mapped.x, mapped.y, mapped.z)
};
let mapped_normal = map_direction(marker.normal);
let normal_scale = mapped_normal.length().max(f64::MIN_POSITIVE);
let normal = mapped_normal.normalize().unwrap_or(KernelVec3::Z);
let mapped_x = map_direction(marker.axis_x);
let axis_x = (mapped_x - normal * mapped_x.dot(normal))
.normalize()
.unwrap_or(KernelVec3::X);
let mapped_y = map_direction(marker.axis_y);
let axis_y = (mapped_y - normal * mapped_y.dot(normal) - axis_x * mapped_y.dot(axis_x))
.normalize()
.or_else(|| normal.cross(axis_x).normalize())
.unwrap_or(KernelVec3::Y);
(
PointMarker {
origin: glam::DVec3::new(origin.x, origin.y, origin.z),
normal: glam::DVec3::from_array(normal.to_array()),
axis_x: glam::DVec3::from_array(axis_x.to_array()),
axis_y: glam::DVec3::from_array(axis_y.to_array()),
viewport_percent: marker.viewport_percent,
},
normal_scale,
)
}
fn transformed_marker_point(
point: KernelVec3,
source: PointMarker,
target: PointMarker,
axial_scale: f64,
) -> KernelVec3 {
let source_origin = KernelVec3::from(source.origin.to_array());
let source_normal = KernelVec3::from(source.normal.to_array())
.normalize()
.unwrap_or(KernelVec3::Z);
let delta = point - source_origin;
let axial = delta.dot(source_normal);
let source_plane = KernelPlane::from_axes(
source.origin.to_array(),
source.axis_x.to_array(),
source.axis_y.to_array(),
);
let uv = source_plane
.project_vector((delta - source_normal * axial).to_array())
.unwrap_or([0.0; 2]);
let target_plane = KernelPlane::from_axes(
target.origin.to_array(),
target.axis_x.to_array(),
target.axis_y.to_array(),
);
KernelVec3::from(target_plane.point_at(uv))
+ KernelVec3::from(target.normal.to_array()) * axial * axial_scale
}
fn emit_wire( fn emit_wire(
lw: &LocalWire, lw: &LocalWire,
accum_xform: &Transform, accum_xform: &Transform,
@ -2069,6 +2188,10 @@ fn emit_wire(
} else { } else {
0.0 0.0
}; };
let marker_transform = lw
.point_marker
.map(|marker| transformed_point_marker(marker, accum_xform));
let point_marker = marker_transform.map(|(marker, _)| marker);
// Only band wires take a per-child composed depth: their solid area is // Only band wires take a per-child composed depth: their solid area is
// what covers siblings, and their width already splits them into their own // what covers siblings, and their width already splits them into their own
@ -2089,6 +2212,7 @@ fn emit_wire(
final_pat, final_pat,
final_lw_px, final_lw_px,
final_world_width, final_world_width,
point_marker,
final_aci, final_aci,
lw.plinegen, lw.plinegen,
lw.is_fill_only, lw.is_fill_only,
@ -2118,6 +2242,7 @@ fn emit_wire(
final_pat, final_pat,
final_lw_px, final_lw_px,
final_world_width, final_world_width,
point_marker,
final_aci, final_aci,
lw.plinegen, lw.plinegen,
lw.is_fill_only, lw.is_fill_only,
@ -2151,11 +2276,20 @@ fn emit_wire(
let pl = lw.points_low.get(idx).copied().unwrap_or([0.0; 3]); let pl = lw.points_low.get(idx).copied().unwrap_or([0.0; 3]);
// Reconstruct the f64 source from (high, low) before applying the // Reconstruct the f64 source from (high, low) before applying the
// insert transform — otherwise the low half is silently dropped. // insert transform — otherwise the low half is silently dropped.
let v = accum_xform.apply(Vector3::new( let source = KernelVec3::new(
p[0] as f64 + pl[0] as f64, p[0] as f64 + pl[0] as f64,
p[1] as f64 + pl[1] as f64, p[1] as f64 + pl[1] as f64,
p[2] as f64 + pl[2] as f64, p[2] as f64 + pl[2] as f64,
)); );
let mapped = if let (Some(source_marker), Some((target_marker, axial_scale))) =
(lw.point_marker, marker_transform)
{
transformed_marker_point(source, source_marker, target_marker, axial_scale)
} else {
let point = accum_xform.apply(Vector3::new(source.x, source.y, source.z));
KernelVec3::new(point.x, point.y, point.z)
};
let v = Vector3::new(mapped.x, mapped.y, mapped.z);
let qx = (v.x) as f32; let qx = (v.x) as f32;
let qy = (v.y) as f32; let qy = (v.y) as f32;
let qz = (v.z) as f32; let qz = (v.z) as f32;

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(_)) { if matches!(entity, EntityType::Point(_)) {
section.props.retain(|prop| prop.field != "handle"); section.props.retain(|prop| prop.field != "handle");
let hyperlink = section.props.iter().position(|prop| prop.field == "hyperlink"); let hyperlink = section.props.iter().position(|prop| prop.field == "hyperlink");

Some files were not shown because too many files have changed in this diff Show more