diff --git a/Cargo.lock b/Cargo.lock index 8ec00d60f..5b9999644 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -72,7 +72,7 @@ checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" [[package]] name = "acadrust" version = "0.4.1" -source = "git+https://git@github.com/HakanSeven12/cadcodec.git?rev=211f830#211f830e1478c2f38d22591957cc69a4d4dd1edc" +source = "git+https://git@github.com/HakanSeven12/cadcodec.git?rev=711be3b#711be3b5151e25e7ed9931f37b0f7c4f16c0243d" dependencies = [ "ahash 0.8.12", "anyhow", diff --git a/Cargo.toml b/Cargo.toml index 4255d3e71..b6426a7ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,7 @@ glam = { version = "0.33", features = ["bytemuck"] } rfd = "0.17" clap = { version = "4", features = ["derive"] } env_logger = "0.11" -acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "211f830", features = ["serde"] } +acadrust = { git = "https://github.com/HakanSeven12/cadcodec.git", rev = "711be3b", features = ["serde"] } cadkernel = { git = "https://github.com/HakanSeven12/cadkernel.git", rev = "b2b1d4b", features = ["acis", "offset"] } dwg-thumbnailer = { path = "crates/dwg-thumbnailer" } flate2 = "1" @@ -61,7 +61,7 @@ iced_core = { git = "https://github.com/iced-rs/iced.git", rev = "23604ff22ab0aa iced_widget = { git = "https://github.com/iced-rs/iced.git", rev = "23604ff22ab0aad9e00b9327cb7b8546ed84db39" } [patch."https://github.com/HakanSeven12/cadcodec.git"] -acadrust = { git = "https://git@github.com/HakanSeven12/cadcodec.git", rev = "211f830" } +acadrust = { git = "https://git@github.com/HakanSeven12/cadcodec.git", rev = "711be3b" } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] ocs_plugin_api = { path = "crates/ocs_plugin_api", features = ["host"] } diff --git a/locales/ar-SA/opencadstudio.ftl b/locales/ar-SA/opencadstudio.ftl index 532f3b07f..f546f620b 100644 --- a/locales/ar-SA/opencadstudio.ftl +++ b/locales/ar-SA/opencadstudio.ftl @@ -1856,8 +1856,8 @@ annotate = .dimordinate-specify-feature-location = DIMORDINATE حدد موقع المعلم: .dimordinate-specify-leader-endpoint = DIMORDINATE حدد نقطة نهاية الخط القائد: .dimradius-enter-dimension-text-blank-measured = DIMRADIUS أدخل نص البُعد (فارغ = القيمة المقاسة): - .dimradius-specify-center-point = DIMRADIUS حدد نقطة المركز: - .dimradius-specify-dimension-line-location-text = DIMRADIUS حدد موقع خط البُعد [نص/زاوية]: + .dimradius-select-arc-circle-or-polyline-arc = DIMRADIUS حدد قوسًا أو دائرة أو قوسًا متعدد الخطوط: + .dimradius-specify-dimension-line-location-text = DIMRADIUS حدد موقع خط البُعد [نص متعدد الأسطر/نص/زاوية]: .dimradius-specify-radius-point = DIMRADIUS حدد نقطة نصف القطر: .dimradius-specify-text-angle-degrees = DIMRADIUS حدد زاوية النص (بالدرجات): .dimspace-enter-value-0-auto = DIMSPACE أدخل القيمة (0 = تلقائي): diff --git a/locales/bg-BG/opencadstudio.ftl b/locales/bg-BG/opencadstudio.ftl index 4e5bdcfb3..eb154aa29 100644 --- a/locales/bg-BG/opencadstudio.ftl +++ b/locales/bg-BG/opencadstudio.ftl @@ -1843,8 +1843,8 @@ annotate = .dimordinate-specify-feature-location = DIMORDINATE Посочи местоположение на елемента: .dimordinate-specify-leader-endpoint = DIMORDINATE Посочи крайна точка на водача: .dimradius-enter-dimension-text-blank-measured = DIMRADIUS Въведи текст на оразмеряване (празно = измерена стойност): - .dimradius-specify-center-point = DIMRADIUS Посочи централна точка: - .dimradius-specify-dimension-line-location-text = DIMRADIUS Посочи местоположение на линията за оразмеряване [Текст/Ъгъл]: + .dimradius-select-arc-circle-or-polyline-arc = DIMRADIUS Избери дъга, окръжност или дъгов сегмент на полилиния: + .dimradius-specify-dimension-line-location-text = DIMRADIUS Посочи местоположение на линията за оразмеряване [Мтекст/Текст/Ъгъл]: .dimradius-specify-radius-point = DIMRADIUS Посочи точка на радиус: .dimradius-specify-text-angle-degrees = DIMRADIUS Посочи ъгъл на текста (градуси): .dimspace-enter-value-0-auto = DIMSPACE Въведи стойност (0 = авто): diff --git a/locales/cs-CZ/opencadstudio.ftl b/locales/cs-CZ/opencadstudio.ftl index 47f98e8d5..ef7c7b0f0 100644 --- a/locales/cs-CZ/opencadstudio.ftl +++ b/locales/cs-CZ/opencadstudio.ftl @@ -1843,8 +1843,8 @@ annotate = .dimordinate-specify-feature-location = DIMORDINATE Zadejte umístění funkce: .dimordinate-specify-leader-endpoint = DIMORDINATE Zadejte koncový bod odkazu: .dimradius-enter-dimension-text-blank-measured = Text rozměru DIMRADIUS Enter (prázdná naměřená hodnota =): - .dimradius-specify-center-point = DIMRADIUS Zadejte středový bod: - .dimradius-specify-dimension-line-location-text = DIMRADIUS Určete umístění kótovací čáry [Text/Úhel]: + .dimradius-select-arc-circle-or-polyline-arc = DIMRADIUS Vyberte oblouk, kružnici nebo obloukový segment křivky: + .dimradius-specify-dimension-line-location-text = DIMRADIUS Určete umístění kótovací čáry [MText/Text/Úhel]: .dimradius-specify-radius-point = DIMRADIUS Zadejte bod poloměru: .dimradius-specify-text-angle-degrees = DIMRADIUS Zadejte úhel textu (stupně): .dimspace-enter-value-0-auto = Hodnota DIMSPACE Enter (0 = auto): diff --git a/locales/de-DE/opencadstudio.ftl b/locales/de-DE/opencadstudio.ftl index 9c48f2aff..a6b0c7750 100644 --- a/locales/de-DE/opencadstudio.ftl +++ b/locales/de-DE/opencadstudio.ftl @@ -1844,8 +1844,8 @@ annotate = .dimordinate-specify-feature-location = DIMORDINATE Bezugspunkt angeben: .dimordinate-specify-leader-endpoint = DIMORDINATE Endpunkt der Führungslinie angeben: .dimradius-enter-dimension-text-blank-measured = DIMRADIUS Bemaßungstext eingeben (leer = Messwert): - .dimradius-specify-center-point = DIMRADIUS Mittelpunkt angeben: - .dimradius-specify-dimension-line-location-text = DIMRADIUS Lage der Dimensionslinie angeben [Text/Winkel]: + .dimradius-select-arc-circle-or-polyline-arc = DIMRADIUS Bogen, Kreis oder Polylinienbogen auswählen: + .dimradius-specify-dimension-line-location-text = DIMRADIUS Lage der Dimensionslinie angeben [MText/Text/Winkel]: .dimradius-specify-radius-point = DIMRADIUS Radiuspunkt angeben: .dimradius-specify-text-angle-degrees = DIMRADIUS Textwinkel angeben (Grad): .dimspace-enter-value-0-auto = DIMSPACE Abstand eingeben (0 = automatisch): diff --git a/locales/en-US/opencadstudio.ftl b/locales/en-US/opencadstudio.ftl index 67485420f..cefa4e5e0 100644 --- a/locales/en-US/opencadstudio.ftl +++ b/locales/en-US/opencadstudio.ftl @@ -1874,8 +1874,8 @@ annotate = .dimordinate-specify-feature-location = DIMORDINATE Specify feature location: .dimordinate-specify-leader-endpoint = DIMORDINATE Specify leader endpoint: .dimradius-enter-dimension-text-blank-measured = DIMRADIUS Enter dimension text (blank = measured value): - .dimradius-specify-center-point = DIMRADIUS Specify center point: - .dimradius-specify-dimension-line-location-text = DIMRADIUS Specify dimension line location [Text/Angle]: + .dimradius-select-arc-circle-or-polyline-arc = DIMRADIUS Select arc, circle, or polyline arc: + .dimradius-specify-dimension-line-location-text = DIMRADIUS Specify dimension line location [Mtext/Text/Angle]: .dimradius-specify-radius-point = DIMRADIUS Specify radius point: .dimradius-specify-text-angle-degrees = DIMRADIUS Specify text angle (degrees): .dimspace-enter-value-0-auto = DIMSPACE Enter value (0 = auto): diff --git a/locales/es-ES/opencadstudio.ftl b/locales/es-ES/opencadstudio.ftl index e9c664b45..34d4f9800 100644 --- a/locales/es-ES/opencadstudio.ftl +++ b/locales/es-ES/opencadstudio.ftl @@ -1844,8 +1844,8 @@ annotate = .dimordinate-specify-feature-location = DIMORDINATE Especifique la ubicación del elemento: .dimordinate-specify-leader-endpoint = DIMORDINATE Especifique el punto final de la directriz: .dimradius-enter-dimension-text-blank-measured = DIMRADIUS Introduzca el texto de cota (vacío = valor medido): - .dimradius-specify-center-point = DIMRADIUS Especifique el centro: - .dimradius-specify-dimension-line-location-text = DIMRADIUS Especifique la ubicación de la línea de cota [Texto/Ángulo]: + .dimradius-select-arc-circle-or-polyline-arc = DIMRADIUS Seleccione un arco, círculo o arco de polilínea: + .dimradius-specify-dimension-line-location-text = DIMRADIUS Especifique la ubicación de la línea de cota [Mtexto/Texto/Ángulo]: .dimradius-specify-radius-point = DIMRADIUS Especifique el punto del radio: .dimradius-specify-text-angle-degrees = DIMRADIUS Especifique el ángulo del texto (grados): .dimspace-enter-value-0-auto = DIMSPACE Introduzca el valor (0 = automático): diff --git a/locales/fi-FI/opencadstudio.ftl b/locales/fi-FI/opencadstudio.ftl index 108abf86a..ac118187d 100644 --- a/locales/fi-FI/opencadstudio.ftl +++ b/locales/fi-FI/opencadstudio.ftl @@ -1843,8 +1843,8 @@ annotate = .dimordinate-specify-feature-location = DIMORDINATE Määritä ominaisuuden sijainti: .dimordinate-specify-leader-endpoint = DIMORDINATE Määritä johtajan päätepiste: .dimradius-enter-dimension-text-blank-measured = DIMRADIUS Enter mittateksti (tyhjä = mitattu arvo): - .dimradius-specify-center-point = DIMRADIUS Määritä keskipiste: - .dimradius-specify-dimension-line-location-text = DIMRADIUS Määritä mittaviivan sijainti [teksti/kulma]: + .dimradius-select-arc-circle-or-polyline-arc = DIMRADIUS Valitse kaari, ympyrä tai murtoviivan kaari: + .dimradius-specify-dimension-line-location-text = DIMRADIUS Määritä mittaviivan sijainti [Mteksti/teksti/kulma]: .dimradius-specify-radius-point = DIMRADIUS Määritä sädepiste: .dimradius-specify-text-angle-degrees = DIMRADIUS Määritä tekstin kulma (asteita): .dimspace-enter-value-0-auto = DIMSPACE Enter-arvo (0 = automaattinen): diff --git a/locales/fr-FR/opencadstudio.ftl b/locales/fr-FR/opencadstudio.ftl index 62593cc60..877df641c 100644 --- a/locales/fr-FR/opencadstudio.ftl +++ b/locales/fr-FR/opencadstudio.ftl @@ -1844,8 +1844,8 @@ annotate = .dimordinate-specify-feature-location = DIMORDINATE Spécifiez le point à coter : .dimordinate-specify-leader-endpoint = DIMORDINATE Spécifiez l’extrémité du repère : .dimradius-enter-dimension-text-blank-measured = DIMRADIUS Entrer le texte dimensionnel (blanc = valeur mesurée): - .dimradius-specify-center-point = DIMRADIUS Spécifier le point central: - .dimradius-specify-dimension-line-location-text = DIMRADIUS Spécifiez la position de la ligne de cote [Texte / Angle] : + .dimradius-select-arc-circle-or-polyline-arc = DIMRADIUS Sélectionnez un arc, un cercle ou un arc de polyligne : + .dimradius-specify-dimension-line-location-text = DIMRADIUS Spécifiez la position de la ligne de cote [Mtexte/Texte/Angle] : .dimradius-specify-radius-point = DIMRADIUS Spécifier le point de rayon: .dimradius-specify-text-angle-degrees = DIMRADIUS Spécifiez l'angle de texte (degrés): .dimspace-enter-value-0-auto = DIMSPACE Entrer la valeur (0 = automatique) : diff --git a/locales/hi-IN/opencadstudio.ftl b/locales/hi-IN/opencadstudio.ftl index 67c172be3..89c986b2e 100644 --- a/locales/hi-IN/opencadstudio.ftl +++ b/locales/hi-IN/opencadstudio.ftl @@ -1832,8 +1832,8 @@ annotate = .dimordinate-specify-feature-location = DIMORDINATE सुविधा स्थान निर्दिष्ट करें: .dimordinate-specify-leader-endpoint = DIMORDINATE लीडर समापन बिंदु निर्दिष्ट करें: .dimradius-enter-dimension-text-blank-measured = DIMRADIUS आयाम टेक्स्ट दर्ज करें (blank = मापा मान): - .dimradius-specify-center-point = DIMRADIUS केंद्र बिंदु निर्दिष्ट करें: - .dimradius-specify-dimension-line-location-text = DIMRADIUS आयाम लाइन स्थान निर्दिष्ट करें [टेक्स्ट / एंगल]: + .dimradius-select-arc-circle-or-polyline-arc = DIMRADIUS आर्क, सर्कल या पॉलीलाइन आर्क चुनें: + .dimradius-specify-dimension-line-location-text = DIMRADIUS आयाम लाइन स्थान निर्दिष्ट करें [एमटेक्स्ट/टेक्स्ट/एंगल]: .dimradius-specify-radius-point = DIMRADIUS त्रिज्या बिंदु निर्दिष्ट करें: .dimradius-specify-text-angle-degrees = DIMRADIUS पाठ कोण निर्दिष्ट करें (डिग्री): .dimspace-enter-value-0-auto = DIMSPACE मान दर्ज करें (0 = ऑटो): diff --git a/locales/hu-HU/opencadstudio.ftl b/locales/hu-HU/opencadstudio.ftl index 30dd2649f..1ce1a22d9 100644 --- a/locales/hu-HU/opencadstudio.ftl +++ b/locales/hu-HU/opencadstudio.ftl @@ -1843,8 +1843,8 @@ annotate = .dimordinate-specify-feature-location = DIMORDINATE Jellemző helyének megadása: .dimordinate-specify-leader-endpoint = DIMORDINATE Vezető végpont megadása: .dimradius-enter-dimension-text-blank-measured = DIMRADIUS Enter méretszöveg (üres = mért érték): - .dimradius-specify-center-point = DIMRADIUS Középpont megadása: - .dimradius-specify-dimension-line-location-text = DIMRADIUS A méretvonal helyének megadása [szöveg/szög]: + .dimradius-select-arc-circle-or-polyline-arc = DIMRADIUS Válasszon ívet, kört vagy vonalláncívet: + .dimradius-specify-dimension-line-location-text = DIMRADIUS A méretvonal helyének megadása [Mszöveg/szöveg/szög]: .dimradius-specify-radius-point = DIMRADIUS Adja meg a sugárpontot: .dimradius-specify-text-angle-degrees = DIMRADIUS Adja meg a szöveg szögét (fokban): .dimspace-enter-value-0-auto = DIMSPACE Enter érték (0 = automatikus): diff --git a/locales/it-IT/opencadstudio.ftl b/locales/it-IT/opencadstudio.ftl index cd493211d..814c1f5a1 100644 --- a/locales/it-IT/opencadstudio.ftl +++ b/locales/it-IT/opencadstudio.ftl @@ -1843,8 +1843,8 @@ annotate = .dimordinate-specify-feature-location = DIMORDINATE Specificare la posizione dell'elemento: .dimordinate-specify-leader-endpoint = DIMORDINATE Specificare il punto finale della guida: .dimradius-enter-dimension-text-blank-measured = Testo della dimensione DIMRADIUS Enter (valore misurato = vuoto): - .dimradius-specify-center-point = DIMRADIUS Specificare il punto centrale: - .dimradius-specify-dimension-line-location-text = DIMRADIUS Specificare la posizione della linea di quota [Testo/Angolo]: + .dimradius-select-arc-circle-or-polyline-arc = DIMRADIUS Selezionare un arco, un cerchio o un arco di polilinea: + .dimradius-specify-dimension-line-location-text = DIMRADIUS Specificare la posizione della linea di quota [Mtesto/Testo/Angolo]: .dimradius-specify-radius-point = DIMRADIUS Specificare il punto del raggio: .dimradius-specify-text-angle-degrees = DIMRADIUS Specificare l'angolo del testo (gradi): .dimspace-enter-value-0-auto = Valore DIMSPACE Enter (0 = automatico): diff --git a/locales/ja-JP/opencadstudio.ftl b/locales/ja-JP/opencadstudio.ftl index c0f4a8686..605f2fddc 100644 --- a/locales/ja-JP/opencadstudio.ftl +++ b/locales/ja-JP/opencadstudio.ftl @@ -1834,8 +1834,8 @@ annotate = .dimordinate-specify-feature-location = DIMORDINATE フィーチャ位置を指定: .dimordinate-specify-leader-endpoint = DIMORDINATE 引出線の終点を指定: .dimradius-enter-dimension-text-blank-measured = DIMRADIUS 寸法値を入力(空白 = 計測値): - .dimradius-specify-center-point = DIMRADIUS 中心点を指定: - .dimradius-specify-dimension-line-location-text = DIMRADIUS 寸法線の位置を指定 [文字/角度]: + .dimradius-select-arc-circle-or-polyline-arc = DIMRADIUS 円弧、円、またはポリライン円弧を選択: + .dimradius-specify-dimension-line-location-text = DIMRADIUS 寸法線の位置を指定 [マルチテキスト/文字/角度]: .dimradius-specify-radius-point = DIMRADIUS 半径点を指定: .dimradius-specify-text-angle-degrees = DIMRADIUS 文字角度を指定(度): .dimspace-enter-value-0-auto = DIMSPACE 値を入力(0 = 自動): diff --git a/locales/ko-KR/opencadstudio.ftl b/locales/ko-KR/opencadstudio.ftl index 63a9253d7..05b50f338 100644 --- a/locales/ko-KR/opencadstudio.ftl +++ b/locales/ko-KR/opencadstudio.ftl @@ -1843,8 +1843,8 @@ annotate = .dimordinate-specify-feature-location = DIMORDINATE 기능 위치 지정: .dimordinate-specify-leader-endpoint = DIMORDINATE 리더 끝점 지정: .dimradius-enter-dimension-text-blank-measured = DIMRADIUS Enter 치수 문자(빈 = 측정 값): - .dimradius-specify-center-point = DIMRADIUS 중심점 지정: - .dimradius-specify-dimension-line-location-text = DIMRADIUS 치수선 위치 지정 [문자/각도]: + .dimradius-select-arc-circle-or-polyline-arc = DIMRADIUS 호, 원 또는 폴리선 호 선택: + .dimradius-specify-dimension-line-location-text = DIMRADIUS 치수선 위치 지정 [여러 줄 문자/문자/각도]: .dimradius-specify-radius-point = DIMRADIUS 반경 점 지정: .dimradius-specify-text-angle-degrees = DIMRADIUS 텍스트 각도(도) 지정: .dimspace-enter-value-0-auto = DIMSPACE Enter 값(0 = 자동): diff --git a/locales/nl-NL/opencadstudio.ftl b/locales/nl-NL/opencadstudio.ftl index 368f91286..ca8ac95ca 100644 --- a/locales/nl-NL/opencadstudio.ftl +++ b/locales/nl-NL/opencadstudio.ftl @@ -1844,8 +1844,8 @@ annotate = .dimordinate-specify-feature-location = DIMORDINATE Geef functielocatie op: .dimordinate-specify-leader-endpoint = DIMORDINATE Geef het eindpunt van de verwijslijn op: .dimradius-enter-dimension-text-blank-measured = DIMRADIUS Voer maattekst in (leeg = gemeten waarde): - .dimradius-specify-center-point = DIMRADIUS Centrumpunt opgeven: - .dimradius-specify-dimension-line-location-text = DIMRADIUS Geef de positie van de maatlijn op [Tekst/Hoek]: + .dimradius-select-arc-circle-or-polyline-arc = DIMRADIUS Selecteer boog, cirkel of polylijnboog: + .dimradius-specify-dimension-line-location-text = DIMRADIUS Geef de positie van de maatlijn op [M-tekst/Tekst/Hoek]: .dimradius-specify-radius-point = DIMRADIUS radiuspunt specificeren: .dimradius-specify-text-angle-degrees = DIMRADIUS Teksthoek (graden) specificeren: .dimspace-enter-value-0-auto = DIMSPACE Waarde invoeren (0 = auto): diff --git a/locales/pl-PL/opencadstudio.ftl b/locales/pl-PL/opencadstudio.ftl index d16788f1e..aadb8ef21 100644 --- a/locales/pl-PL/opencadstudio.ftl +++ b/locales/pl-PL/opencadstudio.ftl @@ -1843,8 +1843,8 @@ annotate = .dimordinate-specify-feature-location = DIMORDINATE Określ lokalizację elementu: .dimordinate-specify-leader-endpoint = DIMORDINATE Określ punkt końcowy linii odniesienia: .dimradius-enter-dimension-text-blank-measured = DIMRADIUS Enter tekst wymiarowy (pusta wartość zmierzona =): - .dimradius-specify-center-point = DIMRADIUS Określ punkt środkowy: - .dimradius-specify-dimension-line-location-text = DIMRADIUS Określ położenie linii wymiarowej [Tekst/Kąt]: + .dimradius-select-arc-circle-or-polyline-arc = DIMRADIUS Wybierz łuk, okrąg lub łuk polilinii: + .dimradius-specify-dimension-line-location-text = DIMRADIUS Określ położenie linii wymiarowej [Mtekst/Tekst/Kąt]: .dimradius-specify-radius-point = DIMRADIUS Określ punkt promienia: .dimradius-specify-text-angle-degrees = DIMRADIUS Określ kąt tekstu (w stopniach): .dimspace-enter-value-0-auto = Wartość DIMSPACE Enter (0 = auto): diff --git a/locales/pt-BR/opencadstudio.ftl b/locales/pt-BR/opencadstudio.ftl index 8c401c557..b0fe5868e 100644 --- a/locales/pt-BR/opencadstudio.ftl +++ b/locales/pt-BR/opencadstudio.ftl @@ -1844,8 +1844,8 @@ annotate = .dimordinate-specify-feature-location = DIMORDINATE Especifique a localização da característica: .dimordinate-specify-leader-endpoint = DIMORDINATE Especifique a extremidade da linha de chamada: .dimradius-enter-dimension-text-blank-measured = DIMRADIUS Insira o texto da cota (vazio = valor medido): - .dimradius-specify-center-point = DIMRADIUS Especifique o ponto central: - .dimradius-specify-dimension-line-location-text = DIMRADIUS Especifique a localização da linha de cota [Texto/Ângulo]: + .dimradius-select-arc-circle-or-polyline-arc = DIMRADIUS Selecione um arco, círculo ou arco de polilinha: + .dimradius-specify-dimension-line-location-text = DIMRADIUS Especifique a localização da linha de cota [Mtexto/Texto/Ângulo]: .dimradius-specify-radius-point = DIMRADIUS Especifique o ponto do raio: .dimradius-specify-text-angle-degrees = DIMRADIUS Especifique o ângulo do texto (graus): .dimspace-enter-value-0-auto = DIMSPACE Insira o valor (0 = automático): diff --git a/locales/ru-RU/opencadstudio.ftl b/locales/ru-RU/opencadstudio.ftl index 0b7904580..3846025b8 100644 --- a/locales/ru-RU/opencadstudio.ftl +++ b/locales/ru-RU/opencadstudio.ftl @@ -1847,8 +1847,8 @@ annotate = .dimordinate-specify-feature-location = DIMORDINATE Укажите местоположение: .dimordinate-specify-leader-endpoint = DIMORDINATE Укажите конечную точку выноски: .dimradius-enter-dimension-text-blank-measured = DIMRADIUS Введите текст размера (пусто = измеренное значение): - .dimradius-specify-center-point = DIMRADIUS Укажите центральную точку: - .dimradius-specify-dimension-line-location-text = DIMRADIUS Укажите положение размерной линии [Текст/Угол]: + .dimradius-select-arc-circle-or-polyline-arc = DIMRADIUS Выберите дугу, окружность или дуговой сегмент полилинии: + .dimradius-specify-dimension-line-location-text = DIMRADIUS Укажите положение размерной линии [Мтекст/Текст/Угол]: .dimradius-specify-radius-point = DIMRADIUS Укажите точку радиуса: .dimradius-specify-text-angle-degrees = DIMRADIUS Укажите угол текста (градусы): .dimspace-enter-value-0-auto = DIMSPACE Введите значение (0 = авто): diff --git a/locales/tr-TR/opencadstudio.ftl b/locales/tr-TR/opencadstudio.ftl index 6f778ce81..371bc4476 100644 --- a/locales/tr-TR/opencadstudio.ftl +++ b/locales/tr-TR/opencadstudio.ftl @@ -1854,8 +1854,8 @@ annotate = .dimordinate-specify-feature-location = DIMORDINATE Unsur konumunu belirtin: .dimordinate-specify-leader-endpoint = DIMORDINATE Kılavuzun uç noktasını belirtin: .dimradius-enter-dimension-text-blank-measured = DIMRADIUS Ölçü metnini girin (boş = ölçülen değer): - .dimradius-specify-center-point = DIMRADIUS Merkez noktasını belirtin: - .dimradius-specify-dimension-line-location-text = DIMRADIUS Ölçü çizgisinin konumunu belirtin [Metin/Açı]: + .dimradius-select-arc-circle-or-polyline-arc = DIMRADIUS Yay, daire veya çoklu çizgi yayı seçin: + .dimradius-specify-dimension-line-location-text = DIMRADIUS Ölçü çizgisinin konumunu belirtin [Çok satırlı metin/Metin/Açı]: .dimradius-specify-radius-point = DIMRADIUS Yarıçap noktasını belirtin: .dimradius-specify-text-angle-degrees = DIMRADIUS Metin açısını belirtin (derece): .dimspace-enter-value-0-auto = DIMSPACE Değeri girin (0 = otomatik): diff --git a/locales/zh-CN/opencadstudio.ftl b/locales/zh-CN/opencadstudio.ftl index b7ed375e0..e94e3930c 100644 --- a/locales/zh-CN/opencadstudio.ftl +++ b/locales/zh-CN/opencadstudio.ftl @@ -1831,8 +1831,8 @@ annotate = .dimordinate-specify-feature-location = DIMORDINATE 指定特征位置: .dimordinate-specify-leader-endpoint = DIMORDINATE 指定引线端点: .dimradius-enter-dimension-text-blank-measured = DIMRADIUS 输入标注文字(空白 = 测量值): - .dimradius-specify-center-point = DIMRADIUS 指定中心点: - .dimradius-specify-dimension-line-location-text = DIMRADIUS 指定尺寸线位置 [文字/角度]: + .dimradius-select-arc-circle-or-polyline-arc = DIMRADIUS 选择圆弧、圆或多段线圆弧: + .dimradius-specify-dimension-line-location-text = DIMRADIUS 指定尺寸线位置 [多行文字/文字/角度]: .dimradius-specify-radius-point = DIMRADIUS 指定半径点: .dimradius-specify-text-angle-degrees = DIMRADIUS 指定文字角度(度): .dimspace-enter-value-0-auto = DIMSPACE 输入值(0 = 自动): diff --git a/locales/zh-TW/opencadstudio.ftl b/locales/zh-TW/opencadstudio.ftl index 877550b1e..254e85e64 100644 --- a/locales/zh-TW/opencadstudio.ftl +++ b/locales/zh-TW/opencadstudio.ftl @@ -1843,8 +1843,8 @@ annotate = .dimordinate-specify-feature-location = DIMORDINATE 指定特徵位置: .dimordinate-specify-leader-endpoint = DIMORDINATE 指定引線端點: .dimradius-enter-dimension-text-blank-measured = DIMRADIUS 輸入標註文字(空白 = 測量值): - .dimradius-specify-center-point = DIMRADIUS 指定中心點: - .dimradius-specify-dimension-line-location-text = DIMRADIUS 指定尺寸線位置 [文字/角度]: + .dimradius-select-arc-circle-or-polyline-arc = DIMRADIUS 選擇圓弧、圓或聚合線圓弧: + .dimradius-specify-dimension-line-location-text = DIMRADIUS 指定尺寸線位置 [多行文字/文字/角度]: .dimradius-specify-radius-point = DIMRADIUS 指定半徑點: .dimradius-specify-text-angle-degrees = DIMRADIUS 指定文字角度(度): .dimspace-enter-value-0-auto = DIMSPACE 輸入值(0 = 自動): diff --git a/src/entities/dim_override.rs b/src/entities/dim_override.rs index aa568dcb4..9aad267e4 100644 --- a/src/entities/dim_override.rs +++ b/src/entities/dim_override.rs @@ -357,6 +357,7 @@ fn inherited_real(doc: &CadDocument, handle: Handle, code: i16) -> f64 { }; match code { DIMGAP => style.dimgap, + DIMCEN => style.dimcen, DIMTP => style.dimtp, DIMTM => style.dimtm, _ => 0.0, @@ -395,6 +396,30 @@ pub fn set_property( value: &str, ) -> bool { let trimmed = value.trim(); + if field == "dim_center_type" { + let current = inherited_real(doc, handle, DIMCEN); + let size = current.abs().max(0.01); + let value = match trimmed.to_ascii_lowercase().as_str() { + "none" => 0.0, + "center marks" => size, + "centerlines" => -size, + _ => return false, + }; + set(doc, handle, DIMCEN, Some(XDataValue::Real(value))); + return true; + } + if field == "dim_center_size" { + let Ok(size) = trimmed.parse::() else { + return false; + }; + if !size.is_finite() || size < 0.0 { + return false; + } + let current = inherited_real(doc, handle, DIMCEN); + let value = if current < 0.0 { -size } else { size }; + set(doc, handle, DIMCEN, Some(XDataValue::Real(value))); + return true; + } let handle_field = match field { "dim_arrowhead_1" => Some(DIMBLK1), "dim_arrowhead_2" => Some(DIMBLK2), diff --git a/src/entities/dimension.rs b/src/entities/dimension.rs index ec2dcda70..a3fa70034 100644 --- a/src/entities/dimension.rs +++ b/src/entities/dimension.rs @@ -86,6 +86,23 @@ fn base_props(base: &DimensionBase) -> Vec Vec { + if let Dimension::Radius(radius) = dim { + return vec![PropSection { + title: t!("Misc").into_owned(), + props: vec![ + Property { + label: t!("Dimension style").into_owned(), + field: "style_name", + value: PropValue::PlainText(radius.base.style_name.clone()), + }, + edit( + t!("Leader Length").as_ref(), + "leader_length", + radius.leader_length, + ), + ], + }]; + } let compact_linear = match dim { Dimension::Linear(d) => Some(( &d.base, @@ -1668,7 +1685,23 @@ pub fn style_sections( let linetype = |code, inherited| { linetype_name(document, ov::handle(data, code).unwrap_or(inherited)) }; - let arrow_1 = arrow_name(ov::DIMBLK1, s.dimblk1, &s.dimblk1_name); + let arrow_1 = if matches!(dimension, Dimension::Radius(_)) { + let inherited = if s.dimblk1.is_null() { s.dimblk } else { s.dimblk1 }; + let inherited_name = if s.dimblk1.is_null() { + &s.dimblk_name + } else { + &s.dimblk1_name + }; + block_name( + document, + ov::handle(data, ov::DIMBLK1) + .or_else(|| ov::handle(data, ov::DIMBLK)) + .unwrap_or(inherited), + inherited_name, + ) + } else { + arrow_name(ov::DIMBLK1, s.dimblk1, &s.dimblk1_name) + }; let arrow_2 = arrow_name(ov::DIMBLK2, s.dimblk2, &s.dimblk2_name); for current in [&arrow_1, &arrow_2] { if !arrow_options.contains(current) { @@ -2346,6 +2379,63 @@ pub fn style_sections( }, ]; + if matches!(dimension, Dimension::Radius(_)) { + let dimcen = real(ov::DIMCEN, s.dimcen); + let center_type = if dimcen > 1e-12 { + "Center marks" + } else if dimcen < -1e-12 { + "Centerlines" + } else { + "None" + }; + if let Some(lines) = sections + .iter_mut() + .find(|section| section.title == t!("Lines & Arrows").as_ref()) + { + const RADIUS_LINE_FIELDS: &[&str] = &[ + "dim_arrowhead_1", + "dim_arrow_size", + "dim_line_lineweight", + "dim_ext_line_lineweight", + "dim_line_1", + "dim_line_color", + "dim_linetype", + "dim_ext_linetype_1", + "dim_ext_line_1", + "dim_ext_line_color", + "dim_ext_line_ext", + "dim_ext_line_offset", + ]; + lines + .props + .retain(|property| RADIUS_LINE_FIELDS.contains(&property.field)); + lines.props.push(choice( + t!("Center mark").as_ref(), + "dim_center_type", + center_type, + &["None", "Center marks", "Centerlines"], + true, + )); + lines.props.push(number( + t!("Center mark size").as_ref(), + "dim_center_size", + dimcen.abs(), + center_type != "None", + )); + } + if let Some(primary_units) = sections + .iter_mut() + .find(|section| section.title == t!("Primary Units").as_ref()) + { + primary_units.props.retain(|property| { + !matches!( + property.field, + "dim_sub_units_suffix" | "dim_sub_units_scale" + ) + }); + } + } + if matches!(dimension, Dimension::Angular2Ln(_) | Dimension::Angular3Pt(_)) { if let Some(text_section) = sections .iter_mut() @@ -2573,7 +2663,7 @@ use acadrust::{CadDocument, EntityType, Handle}; use crate::scene::convert::tess_util::aci_to_rgba; use crate::scene::convert::tessellate::{ - add_segment, append_arrow, arrow_from_block_with_deferred_hatch, + add_polyline, add_segment, append_arrow, arrow_from_block_with_deferred_hatch, normalized_or, ArrowKind, DimGeom, }; use crate::scene::model::wire_model::{SnapHint, WireModel}; @@ -2950,7 +3040,16 @@ fn tessellate_dimension_inner( }; (t.clone(), t) } else if let Some(s) = style { - if dimsah { + if matches!(dim, Dimension::Radius(_)) { + let handle = if s.dimblk1.is_null() { s.dimblk } else { s.dimblk1 }; + let arrow = arrow_from_block_with_deferred_hatch( + document, + handle, + dimasz, + defer_arrow_hatches, + ); + (arrow.clone(), arrow) + } else if dimsah { ( arrow_from_block_with_deferred_hatch( document, @@ -3009,6 +3108,11 @@ fn tessellate_dimension_inner( None } }; + let text_position = vec3_local(dimension_text_pos_f64(dim, style, dim_txt, dim_scale)); + let text_is_outside = dimension_text_is_outside(dim, style); + let horizontal_text = style.is_some_and(|style| { + (text_is_outside && style.dimtoh) || (!text_is_outside && style.dimtih) + }); let mut geom = dimension_geometry( dim, @@ -3027,6 +3131,8 @@ fn tessellate_dimension_inner( text_width, dimatfit: style.map(|s| s.dimatfit).unwrap_or(3), dimtofl: style.map(|s| s.dimtofl).unwrap_or(false), + text_position, + horizontal_text, text_break, }, SuppressFlags { @@ -3037,6 +3143,18 @@ fn tessellate_dimension_inner( }, ); + if !dimse1 { + if let Some(points) = crate::scene::dimension_assoc::radial_extension_points( + document, + handle, + dimexo as f64, + dimexe as f64, + ) { + let points: Vec = points.into_iter().map(vec3_local).collect(); + add_polyline(&mut geom.ext_lines, &points); + } + } + // DIMTMOVE = 1: when the saved text_middle_point sits far from the // dim-line anchor, draw a short leader connecting them. (=0 anchors text // to the dim line — no leader; =2 frees text without a leader.) @@ -3523,7 +3641,7 @@ fn dimtmove_leader_endpoints(dim: &Dimension) -> Option<(Vec3, Vec3)> { let off2 = def.dot(perp) - second.dot(perp); (first + perp * off1 + second + perp * off2) * 0.5 } - Dimension::Radius(d) => lv(d.definition_point), + Dimension::Radius(_) => return None, Dimension::Diameter(d) => (lv(d.angle_vertex) + lv(d.definition_point)) * 0.5, Dimension::Angular2Ln(d) => lv(d.dimension_arc), Dimension::Angular3Pt(d) => lv(d.definition_point), @@ -3596,6 +3714,8 @@ struct DimLineParams { text_width: f32, dimatfit: i16, dimtofl: bool, + text_position: Vec3, + horizontal_text: bool, /// Text box (local centre, half-width, half-height) used to break the /// dimension line where the text sits on it, so a DIMTFILL background reads /// over the line. None when the text doesn't overlap the line. @@ -3650,9 +3770,8 @@ fn dimension_geometry( Dimension::Radius(d) => { let center = lv(d.angle_vertex); let point = lv(d.definition_point); - let text = dimension_text_position(dim); - // A jogged radius dim (marked via XData) replaces the straight radial - // leader with a foreshortened zig-zag at ~45° near its midpoint. + let text = params.text_position; + // Jogged radius dimensions use a shortened zig-zag leader. let jogged = dim .base() .common @@ -3668,29 +3787,50 @@ fn dimension_geometry( let mid = center + u * (dist * 0.5); let a = mid - u * half + perp * half; let b = mid + u * half - perp * half; - add_segment(&mut g.dim_lines, center, a); - add_segment(&mut g.dim_lines, a, b); - add_segment(&mut g.dim_lines, b, point); - } else { + if !suppress.dim1 { + add_segment(&mut g.dim_lines, center, a); + add_segment(&mut g.dim_lines, a, b); + add_segment(&mut g.dim_lines, b, point); + } + } else if !suppress.dim1 { add_segment(&mut g.dim_lines, center, point); } - // Honour leader_length: extend from the arrow tip past it - // toward the text by that distance along (text - point). - let leader_dir = normalized_or(text - point, Vec3::X); - let leader = if d.leader_length.abs() > 1e-9 { - point + leader_dir * (d.leader_length as f32) - } else { - text - }; - add_segment(&mut g.dim_lines, point, leader); + let radius = (point - center).length(); + let text_is_outside = text.distance(center) > radius + 1e-5; + if text_is_outside && !suppress.dim1 { + let radial = normalized_or(point - center, Vec3::X); + let angle_from_horizontal = radial.y.abs().atan2(radial.x.abs()); + if params.horizontal_text + && angle_from_horizontal > 15.0_f32.to_radians() + && radial.y.abs() > 1e-6 + { + let travel = (text.y - point.y) / radial.y; + if travel > 0.0 { + let elbow = point + radial * travel; + let landing_gap = params.text_width * 0.5 + params.arrow_len; + let landing = Vec3::new( + text.x - radial.x.signum() * landing_gap, + text.y, + text.z, + ); + add_segment(&mut g.dim_lines, point, elbow); + add_segment(&mut g.dim_lines, elbow, landing); + } else { + add_segment(&mut g.dim_lines, point, text); + } + } else { + add_segment(&mut g.dim_lines, point, text); + } + } append_arrow( &mut g, point, normalized_or(center - point, Vec3::X), arrow1, ); - let radius = (point - center).length(); - append_center_mark(&mut g, center, params.dimcen, radius); + if text_is_outside { + append_center_mark(&mut g, center, params.dimcen, radius); + } } Dimension::Diameter(d) => { // angle_vertex is the circle centre and definition_point a point on @@ -4631,6 +4771,34 @@ fn dimension_text_is_outside(dim: &Dimension, style: Option<&DimStyle>) -> bool _ => text_width > span, }; } + if let Dimension::Radius(radius) = dim { + let dx = radius.definition_point.x - radius.angle_vertex.x; + let dy = radius.definition_point.y - radius.angle_vertex.y; + let available = dx.hypot(dy); + if dim.base().text_user_positioned { + let text = dim.base().text_middle_point; + return (text.x - radius.angle_vertex.x) + .hypot(text.y - radius.angle_vertex.y) + > available + 1e-9; + } + if style.dimtix { + return false; + } + let scale = if style.dimscale > 1e-9 { style.dimscale } else { 1.0 }; + let height = style.dimtxt * scale; + let gap = style.dimgap.abs() * scale; + let text_width = dimension_text_value(dim, Some(style)) + .map(|value| value.chars().count() as f64 * height * 0.6 + gap * 2.0) + .unwrap_or(0.0); + let arrow = style.dimasz * scale; + let insufficient = text_width + arrow > available; + return insufficient + && match style.dimatfit { + 0 | 2 => true, + 1 | 3 => text_width > available, + _ => text_width > available, + }; + } let (first, second, axis) = match dim { Dimension::Linear(d) => ( d.first_point, @@ -4686,6 +4854,8 @@ fn dimension_text_natural_rotation(dim: &Dimension) -> f64 { ((start + end) * 0.5 + std::f32::consts::FRAC_PI_2) as f64 }) .unwrap_or(0.0), + Dimension::Radius(d) => (d.definition_point.y - d.angle_vertex.y) + .atan2(d.definition_point.x - d.angle_vertex.x), _ => 0.0, }; // Clamp to (-π/2, π/2] so text never appears upside-down. @@ -5501,6 +5671,32 @@ fn dimension_text_pos_f64( vertex.z as f64, ) } + Dimension::Radius(d) => { + let dx = d.definition_point.x - d.angle_vertex.x; + let dy = d.definition_point.y - d.angle_vertex.y; + let radius = dx.hypot(dy).max(1e-12); + let ux = dx / radius; + let uy = dy / radius; + let outside = dimension_text_is_outside(dim, style); + let (mut x, mut y) = if outside { + let distance = arrow + text_w * 0.5 + dimgap; + ( + d.definition_point.x + ux * distance, + d.definition_point.y + uy * distance, + ) + } else { + ( + d.angle_vertex.x + ux * radius * 0.5, + d.angle_vertex.y + uy * radius * 0.5, + ) + }; + if dimtad != 0 { + let sign = if dimtad == 4 { -1.0 } else { 1.0 }; + x += -uy * perp_off * sign; + y += ux * perp_off * sign; + } + Vector3::new(x, y, d.definition_point.z) + } _ => { // Non-linear (radius / diameter / angular / ordinate): lift the // natural mid point straight up by the style offset. A user-dragged @@ -5509,11 +5705,6 @@ fn dimension_text_pos_f64( // here — that would ignore the style placement for auto-placed dims // and make a re-style a no-op. (#181) let mid = match dim { - Dimension::Radius(d) => Vector3::new( - (d.angle_vertex.x + d.definition_point.x) * 0.5, - (d.angle_vertex.y + d.definition_point.y) * 0.5, - (d.angle_vertex.z + d.definition_point.z) * 0.5, - ), Dimension::Diameter(d) => Vector3::new( (d.angle_vertex.x + d.definition_point.x) * 0.5, (d.angle_vertex.y + d.definition_point.y) * 0.5, diff --git a/src/locale_catalog.rs b/src/locale_catalog.rs index 3ce5d6137..e4f7abd6a 100644 --- a/src/locale_catalog.rs +++ b/src/locale_catalog.rs @@ -778,8 +778,8 @@ pub(super) fn message_attribute(source: &str) -> Option<(&'static str, &'static "DIMORDINATE Specify feature location:" => Some(("annotate", "dimordinate-specify-feature-location")), "DIMORDINATE Specify leader endpoint:" => Some(("annotate", "dimordinate-specify-leader-endpoint")), "DIMRADIUS Enter dimension text (blank = measured value):" => Some(("annotate", "dimradius-enter-dimension-text-blank-measured")), - "DIMRADIUS Specify center point:" => Some(("annotate", "dimradius-specify-center-point")), - "DIMRADIUS Specify dimension line location [Text/Angle]:" => Some(("annotate", "dimradius-specify-dimension-line-location-text")), + "DIMRADIUS Select arc, circle, or polyline arc:" => Some(("annotate", "dimradius-select-arc-circle-or-polyline-arc")), + "DIMRADIUS Specify dimension line location [Mtext/Text/Angle]:" => Some(("annotate", "dimradius-specify-dimension-line-location-text")), "DIMRADIUS Specify radius point:" => Some(("annotate", "dimradius-specify-radius-point")), "DIMRADIUS Specify text angle (degrees):" => Some(("annotate", "dimradius-specify-text-angle-degrees")), "DIMSPACE Enter value (0 = auto):" => Some(("annotate", "dimspace-enter-value-0-auto")), diff --git a/src/modules/annotate/radius_dim.rs b/src/modules/annotate/radius_dim.rs index 6bb9ec58e..62a833489 100644 --- a/src/modules/annotate/radius_dim.rs +++ b/src/modules/annotate/radius_dim.rs @@ -1,12 +1,14 @@ use acadrust::entities::{Dimension, DimensionRadius}; -use acadrust::types::Vector3; +use acadrust::types::{Handle, Vector3}; use acadrust::EntityType; -use crate::command::{CadCommand, CmdResult, WorkingPlane}; +use crate::command::{ + CadCommand, CmdOption, CmdResult, DimensionAssociationInput, WorkingPlane, +}; use crate::modules::{IconKind, ModuleEvent, ToolDef}; use crate::scene::model::wire_model::WireModel; -use glam::{DVec3, Vec3}; use crate::t; +use glam::{DVec3, Vec3}; pub const ICON: IconKind = IconKind::Svg(include_bytes!("../../../assets/icons/dim_radius.svg")); @@ -20,40 +22,43 @@ pub fn tool() -> ToolDef { } enum Step { - CenterPoint, - RadiusPoint(DVec3), - TextPoint { center: DVec3, point: DVec3 }, + SelectObject, + DimLine(crate::scene::dimension_assoc::RadialSourceGeometry), } pub struct RadiusDimensionCommand { step: Step, - plane: WorkingPlane, - /// Optional text that replaces the measured value (None = measurement). text_override: Option, - /// True while the next typed line is captured as the text override. awaiting_text: bool, - /// Explicit text rotation in radians (None = follow the UCS/style). text_angle: Option, - /// True while the next typed value is captured as the text angle. awaiting_angle: bool, + picked_entity: Option, + source_handle: Option, } impl RadiusDimensionCommand { pub fn new() -> Self { Self { - step: Step::CenterPoint, - plane: WorkingPlane::default(), + step: Step::SelectObject, text_override: None, awaiting_text: false, text_angle: None, awaiting_angle: false, + picked_entity: None, + source_handle: None, + } + } + + fn editor_anchor(&self) -> DVec3 { + match self.step { + Step::SelectObject => DVec3::ZERO, + Step::DimLine(source) => dvec(source.point_at_angle(source.start_angle)), } } } impl CadCommand for RadiusDimensionCommand { - fn set_working_plane(&mut self, plane: WorkingPlane) { - self.plane = plane; + fn set_working_plane(&mut self, _plane: WorkingPlane) { } fn name(&self) -> &'static str { @@ -68,48 +73,50 @@ impl CadCommand for RadiusDimensionCommand { return t!("DIMRADIUS Specify text angle (degrees):").into_owned(); } match self.step { - Step::CenterPoint => t!("DIMRADIUS Specify center point:").into_owned(), - Step::RadiusPoint(_) => t!("DIMRADIUS Specify radius point:").into_owned(), - Step::TextPoint { .. } => { - t!("DIMRADIUS Specify dimension line location [Text/Angle]:").into_owned() + Step::SelectObject => t!("DIMRADIUS Select arc, circle, or polyline arc:").into_owned(), + Step::DimLine(_) => { + t!("DIMRADIUS Specify dimension line location [Mtext/Text/Angle]:").into_owned() } } } fn on_point(&mut self, pt: DVec3) -> CmdResult { match self.step { - Step::CenterPoint => { - self.step = Step::RadiusPoint(pt); - CmdResult::NeedPoint - } - Step::RadiusPoint(center) => { - self.step = Step::TextPoint { center, point: pt }; - CmdResult::NeedPoint - } - Step::TextPoint { center, point } => { - let center = self.plane.to_local(center); - let point = self.plane.to_local(point); - let pt = self.plane.to_local(pt); + Step::SelectObject => CmdResult::NeedPoint, + Step::DimLine(source) => { + let source_plane = WorkingPlane::new( + DVec3::from_array(source.plane.origin), + DVec3::from_array(source.plane.x_axis), + DVec3::from_array(source.plane.y_axis), + ); + let center_world = dvec(source.center_world()); + let point_world = dvec(source.chord_at(pt.to_array())); + let center = source_plane.to_local(center_world); + let point = source_plane.to_local(point_world); + let pt = source_plane.to_local(pt); let mut dim = DimensionRadius::new(v3(center), v3(point)); dim.base.definition_point = v3(point); dim.base.text_middle_point = v3(pt); dim.base.insertion_point = v3(pt); + dim.base.text_user_positioned = true; dim.leader_length = point.distance(pt); dim.base.actual_measurement = dim.measurement(); - dim.base.user_text = self.text_override.clone(); - // An explicit text angle overrides the default rotation. + crate::entities::dimension::set_dimension_text_override( + &mut dim.base, + self.text_override.clone(), + ); if let Some(a) = self.text_angle { dim.base.text_rotation = a; } - CmdResult::CommitAndExit(self.plane.place_entity(EntityType::Dimension( - Dimension::Radius(dim), - ))) + CmdResult::CommitDimension { + entity: source_plane.place_entity(EntityType::Dimension(Dimension::Radius(dim))), + association: DimensionAssociationInput::Infer(self.source_handle), + } } } } fn on_enter(&mut self) -> CmdResult { - // A bare Enter while entering override text accepts the measured value. if self.awaiting_text { self.awaiting_text = false; return CmdResult::NeedPoint; @@ -130,19 +137,28 @@ impl CadCommand for RadiusDimensionCommand { } fn point_step_accepts_keywords(&self) -> bool { - // While entering the override text or angle it is a value, not a point step. !self.awaiting_text && !self.awaiting_angle } fn wants_text_with_spaces(&self) -> bool { - // The override text may contain spaces. self.awaiting_text } + fn options(&self) -> Vec { + if matches!(self.step, Step::DimLine(_)) && !self.awaiting_text && !self.awaiting_angle { + vec![ + CmdOption::new("MText", "MTEXT"), + CmdOption::new("Text", "TEXT"), + CmdOption::new("Angle", "ANGLE"), + ] + } else { + Vec::new() + } + } + fn on_text_input(&mut self, text: &str) -> Option { if self.awaiting_text { let t = text.trim(); - // Blank (or the "<>" placeholder) keeps the measured value. self.text_override = if t.is_empty() || t == "<>" { None } else { @@ -153,7 +169,6 @@ impl CadCommand for RadiusDimensionCommand { } if self.awaiting_angle { let t = text.trim(); - // Blank clears any explicit angle (follow the UCS/style again). self.text_angle = if t.is_empty() { None } else { @@ -162,11 +177,21 @@ impl CadCommand for RadiusDimensionCommand { self.awaiting_angle = false; return Some(CmdResult::NeedPoint); } - match text.trim().to_uppercase().as_str() { - "T" | "TEXT" | "M" | "MTEXT" => { + if !matches!(self.step, Step::DimLine(_)) { + return None; + } + match text.trim().to_ascii_uppercase().as_str() { + "T" | "TEXT" => { self.awaiting_text = true; Some(CmdResult::NeedPoint) } + "M" | "MTEXT" => { + Some(CmdResult::SuspendForMTextInput { + pos: self.editor_anchor(), + initial: self.text_override.clone().unwrap_or_default(), + height: 2.5, + }) + } "A" | "ANGLE" => { self.awaiting_angle = true; Some(CmdResult::NeedPoint) @@ -175,18 +200,67 @@ impl CadCommand for RadiusDimensionCommand { } } + fn on_editor_text(&mut self, value: String) { + let value = value.trim(); + self.text_override = if value.is_empty() || value == "<>" { + None + } else { + Some(value.to_string()) + }; + } + + fn on_editor_closed(&mut self, _committed: bool) -> CmdResult { + CmdResult::NeedPoint + } + + fn needs_entity_pick(&self) -> bool { + matches!(self.step, Step::SelectObject) + } + + fn entity_pick_highlights_hover(&self) -> bool { + true + } + + fn inject_before_entity_pick(&self) -> bool { + true + } + + fn inject_picked_entity(&mut self, entity: EntityType) { + self.picked_entity = Some(entity); + } + + fn on_entity_pick(&mut self, handle: Handle, point: DVec3) -> CmdResult { + let Some(entity) = self.picked_entity.take() else { + return CmdResult::NeedPoint; + }; + let Some(source) = crate::scene::dimension_assoc::radial_source_at( + &entity, + Vector3::new(point.x, point.y, point.z), + ) else { + return CmdResult::NeedPoint; + }; + if !source.radius.is_finite() || source.radius <= 1e-12 { + return CmdResult::NeedPoint; + } + self.source_handle = Some(handle); + self.step = Step::DimLine(source); + CmdResult::NeedPoint + } + fn on_mouse_move(&mut self, pt: DVec3) -> Option { - let pt = pt.as_vec3(); match self.step { - Step::CenterPoint => None, - Step::RadiusPoint(center) => Some(preview_wire(vec![center.as_vec3(), pt])), - Step::TextPoint { center, point } => Some(preview_wire(vec![ - center.as_vec3(), - point.as_vec3(), - Vec3::new(f32::NAN, f32::NAN, f32::NAN), - point.as_vec3(), - pt, - ])), + Step::SelectObject => None, + Step::DimLine(source) => { + let center = dvec(source.center_world()).as_vec3(); + let point = dvec(source.chord_at(pt.to_array())).as_vec3(); + Some(preview_wire(vec![ + center, + point, + Vec3::new(f32::NAN, f32::NAN, f32::NAN), + point, + pt.as_vec3(), + ])) + } } } } @@ -195,6 +269,10 @@ fn v3(pt: DVec3) -> Vector3 { Vector3::new(pt.x, pt.y, pt.z) } +fn dvec(point: Vector3) -> DVec3 { + DVec3::new(point.x, point.y, point.z) +} + fn preview_wire(points: Vec) -> WireModel { WireModel { point_marker: None, @@ -209,9 +287,9 @@ fn preview_wire(points: Vec) -> WireModel { render_instance: None, pick_tris: Vec::new(), pick_tris_low: Vec::new(), - dash_from_start: false, - dash_align_end: None, - text_verts: Vec::new(), + dash_from_start: false, + dash_align_end: None, + text_verts: Vec::new(), name: "dimradius_preview".to_string(), points: points.into_iter().map(|p| [p.x, p.y, p.z]).collect(), points_low: Vec::new(), @@ -231,6 +309,4 @@ fn preview_wire(points: Vec) -> WireModel { } } - -// ── Autocomplete registry ───────────────────────────────── -inventory::submit!(crate::command::CommandRegistration { names: &["DIMRADIUS"] }); // RadiusDimensionCommand +inventory::submit!(crate::command::CommandRegistration { names: &["DIMRADIUS"] }); diff --git a/src/scene/dimension_assoc.rs b/src/scene/dimension_assoc.rs index 019a46e44..a96423974 100644 --- a/src/scene/dimension_assoc.rs +++ b/src/scene/dimension_assoc.rs @@ -6,14 +6,170 @@ use acadrust::objects::{ use acadrust::types::{Handle, Vector3}; use acadrust::EntityType; use cadkernel::geom2d::{ - closest_point, BulgeArc, Circle as KernelCircle, Curve as KernelCurve, + angle_within_arc, arc as tessellate_arc, arc_span, closest_point, Arc as KernelArc, + BulgeArc, Circle as KernelCircle, Curve as KernelCurve, DEFAULT_SEGMENTS_PER_RADIAN, }; +use cadkernel::space::Plane; use std::f64::consts::TAU; use crate::command::DimensionAssociationSource; use super::{ChangeKind, Scene}; +#[derive(Clone, Copy, Debug)] +pub(crate) struct RadialSourceGeometry { + pub plane: Plane, + pub center: [f64; 2], + pub radius: f64, + pub start_angle: f64, + pub end_angle: f64, + pub limited: bool, + pub marker: i32, +} + +impl RadialSourceGeometry { + pub(crate) fn center_world(self) -> Vector3 { + vector3(self.plane.point_at(self.center)) + } + + pub(crate) fn point_at_angle(self, angle: f64) -> Vector3 { + vector3(self.plane.point_at([ + self.center[0] + self.radius * angle.cos(), + self.center[1] + self.radius * angle.sin(), + ])) + } + + pub(crate) fn angle_at(self, point: DPoint) -> f64 { + let projected = self.plane.project(point).unwrap_or(self.center); + (projected[1] - self.center[1]).atan2(projected[0] - self.center[0]) + } + + pub(crate) fn chord_at(self, point: DPoint) -> Vector3 { + self.point_at_angle(self.angle_at(point)) + } + + fn contains_angle(self, angle: f64) -> bool { + !self.limited || angle_within_arc(angle, self.start_angle, self.end_angle) + } + + fn curve(self) -> KernelCurve { + if self.limited { + KernelCurve::Arc(KernelArc { + centre: self.center, + radius: self.radius, + start_angle: self.start_angle, + end_angle: self.end_angle, + }) + } else { + KernelCurve::Circle(KernelCircle { + centre: self.center, + radius: self.radius, + }) + } + } + + fn distance_squared_to(self, point: DPoint) -> f64 { + let Some(projected) = self.plane.project(point) else { + return f64::INFINITY; + }; + let planar = closest_point(&self.curve(), projected).distance; + let world = self.plane.point_at(projected); + let dx = world[0] - point[0]; + let dy = world[1] - point[1]; + let dz = world[2] - point[2]; + planar * planar + dx * dx + dy * dy + dz * dz + } +} + +type DPoint = [f64; 3]; + +fn vector3(point: DPoint) -> Vector3 { + Vector3::new(point[0], point[1], point[2]) +} + +fn dpoint(point: Vector3) -> DPoint { + [point.x, point.y, point.z] +} + +fn radial_candidates(entity: &EntityType) -> Vec { + let Some(planar) = crate::entities::curve::entity_curve(entity) else { + return Vec::new(); + }; + match planar.curve { + KernelCurve::Circle(circle) => vec![RadialSourceGeometry { + plane: planar.plane, + center: circle.centre, + radius: circle.radius, + start_angle: 0.0, + end_angle: 0.0, + limited: false, + marker: 0, + }], + KernelCurve::Arc(arc) => vec![RadialSourceGeometry { + plane: planar.plane, + center: arc.centre, + radius: arc.radius, + start_angle: arc.start_angle, + end_angle: arc.end_angle, + limited: true, + marker: 0, + }], + KernelCurve::Polyline(polyline) => (0..polyline.vertices.len()) + .filter_map(|index| { + let arc = polyline.segment_arc(index)?; + let (start_angle, end_angle) = if arc.sweep >= 0.0 { + (arc.start_angle, arc.end_angle) + } else { + (arc.end_angle, arc.start_angle) + }; + Some(RadialSourceGeometry { + plane: planar.plane, + center: arc.center, + radius: arc.radius, + start_angle, + end_angle, + limited: true, + marker: index as i32, + }) + }) + .collect(), + _ => Vec::new(), + } +} + +pub(crate) fn radial_source_at( + entity: &EntityType, + point: Vector3, +) -> Option { + radial_candidates(entity).into_iter().min_by(|first, second| { + first + .distance_squared_to(dpoint(point)) + .total_cmp(&second.distance_squared_to(dpoint(point))) + }) +} + +fn radial_source_for_marker(entity: &EntityType, marker: i32) -> Option { + radial_candidates(entity) + .into_iter() + .find(|candidate| candidate.marker == marker) +} + +fn radial_source_matching( + entity: &EntityType, + center: Vector3, + radius: f64, + chord: Vector3, +) -> Option { + radial_candidates(entity).into_iter().min_by(|first, second| { + let score = |candidate: &RadialSourceGeometry| { + point_distance_squared(candidate.center_world(), center) + + (candidate.radius - radius).powi(2) + + candidate.distance_squared_to(dpoint(chord)) * 1e-6 + }; + score(first).total_cmp(&score(second)) + }) +} + fn point_distance_squared(first: Vector3, second: Vector3) -> f64 { let dx = first.x - second.x; let dy = first.y - second.y; @@ -286,6 +442,75 @@ pub(crate) fn dimension_is_associative( }) } +pub(crate) fn radial_extension_points( + document: &acadrust::CadDocument, + dimension: Handle, + gap: f64, + extension: f64, +) -> Option> { + let EntityType::Dimension(Dimension::Radius(radius_dimension)) = + document.get_entity(dimension)? + else { + return None; + }; + let association = document.objects.values().find_map(|object| { + let ObjectType::Associative(object) = object else { + return None; + }; + let AssociativeData::DimensionAssociation(association) = &object.data else { + return None; + }; + (association.dimension == dimension).then_some(association) + })?; + let reference = association.references[0].first()?; + let source = document.get_entity(*reference.xrefs.first()?)?; + let radial = radial_source_for_marker(source, reference.main_gs_marker)?; + if !radial.limited || radial.radius <= 1e-12 { + return None; + } + let target = radial.angle_at(dpoint(radius_dimension.definition_point)); + if radial.contains_angle(target) { + return None; + } + + let from_start = arc_span(target, radial.start_angle); + let from_end = arc_span(radial.end_angle, target); + let gap_angle = gap.max(0.0) / radial.radius; + let extension_angle = extension.max(0.0) / radial.radius; + let span = from_start.min(from_end); + if span <= gap_angle + 1e-10 { + return None; + } + let (start, end, reverse) = if from_start <= from_end { + ( + target - extension_angle, + radial.start_angle - gap_angle, + true, + ) + } else { + ( + radial.end_angle + gap_angle, + target + extension_angle, + false, + ) + }; + let mut points: Vec<_> = tessellate_arc( + radial.center, + radial.radius, + start, + end, + 0.0, + DEFAULT_SEGMENTS_PER_RADIAN, + ) + .into_iter() + .map(|point| vector3(radial.plane.point_at([point[0], point[1]]))) + .collect(); + if reverse { + points.reverse(); + } + Some(points) +} + impl Scene { pub(crate) fn attach_dimension_association( &mut self, @@ -311,6 +536,51 @@ impl Scene { else { return; }; + if let Dimension::Radius(radius_dimension) = dimension_entity { + let Some(source) = sources.iter().flatten().next().copied() else { + return; + }; + let Some(source_entity) = self.document.get_entity(source.handle) else { + return; + }; + let measured_radius = radius_dimension.measurement(); + let radial = source + .marker + .and_then(|marker| radial_source_for_marker(source_entity, marker)) + .or_else(|| { + radial_source_matching( + source_entity, + radius_dimension.angle_vertex, + measured_radius, + radius_dimension.definition_point, + ) + }); + let Some(radial) = radial else { + return; + }; + let angle = if source.marker.is_some() && source.parameter.is_finite() { + source.parameter + } else { + radial.angle_at(dpoint(radius_dimension.definition_point)) + }; + let reference = AssocDimensionReference { + class_name: "AcDbOsnapPointRef".to_string(), + osnap_type: 10, + xrefs: vec![source.handle], + main_subent_type: 1, + main_gs_marker: radial.marker, + osnap_distance: angle, + osnap_point: radius_dimension.definition_point, + ..AssocDimensionReference::default() + }; + self.store_dimension_association( + dimension, + [vec![reference], Vec::new(), Vec::new(), Vec::new()], + 1, + sources, + ); + return; + } let source_data = dimension_reference_points(dimension_entity); if source_data.is_empty() { return; @@ -371,6 +641,16 @@ impl Scene { } } + self.store_dimension_association(dimension, references, associativity, sources); + } + + fn store_dimension_association( + &mut self, + dimension: Handle, + references: [Vec; 4], + associativity: i32, + sources: Vec>, + ) { let association_handle = self.document.allocate_handle(); let mut object = AssociativeObject::new("DIMASSOC", "AcDbDimAssoc"); object.handle = association_handle; @@ -406,6 +686,34 @@ impl Scene { else { return [None, None]; }; + if let Dimension::Radius(radius) = entity { + let measurement = radius.measurement(); + let source = self + .document + .entities() + .filter(|candidate| candidate.common().handle != dimension) + .filter_map(|candidate| { + let radial = radial_source_matching( + candidate, + radius.angle_vertex, + measurement, + radius.definition_point, + )?; + let center_error = point_distance_squared( + radial.center_world(), + radius.angle_vertex, + ); + let radius_error = (radial.radius - measurement).powi(2); + let tolerance = measurement.abs().max(1.0) * 1e-9; + (center_error + radius_error <= tolerance * tolerance).then_some(( + center_error + radius_error, + candidate.common().handle, + )) + }) + .min_by(|first, second| first.0.total_cmp(&second.0)) + .map(|(_, handle)| handle); + return [source, None]; + } let Some(points) = dimension_inference_points(entity) else { return [None, None]; }; @@ -459,7 +767,13 @@ impl Scene { .first() .and_then(|reference| resolve_reference(self, reference)) }); - if resolved.iter().all(Option::is_none) { + let radial_source = association.references[0].first().and_then(|reference| { + let source = *reference.xrefs.first()?; + let entity = self.document.get_entity(source)?; + let radial = radial_source_for_marker(entity, reference.main_gs_marker)?; + Some((radial, reference.osnap_distance)) + }); + if radial_source.is_none() && resolved.iter().all(Option::is_none) { continue; } let Some(EntityType::Dimension(dimension)) = @@ -513,6 +827,27 @@ impl Scene { } angular.base.definition_point = angular.definition_point; } + Dimension::Radius(radius) => { + let Some((radial, angle)) = radial_source else { + continue; + }; + let old_chord = radius.definition_point; + let new_center = radial.center_world(); + let new_chord = radial.point_at_angle(angle); + let delta = Vector3::new( + new_chord.x - old_chord.x, + new_chord.y - old_chord.y, + new_chord.z - old_chord.z, + ); + radius.angle_vertex = new_center; + radius.definition_point = new_chord; + radius.base.definition_point = new_chord; + if radius.base.text_user_positioned { + radius.base.text_middle_point = radius.base.text_middle_point + delta; + radius.base.insertion_point = radius.base.insertion_point + delta; + } + radius.base.actual_measurement = radius.measurement(); + } _ => continue, } dimension.base_mut().actual_measurement = dimension.measurement();