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.
This commit is contained in:
parent
c5fe70627a
commit
7293bec1b9
69 changed files with 823 additions and 221 deletions
|
|
@ -3073,7 +3073,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__)
|
||||||
|
|
|
||||||
|
|
@ -3061,7 +3061,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__)
|
||||||
|
|
|
||||||
|
|
@ -3060,7 +3060,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__)
|
||||||
|
|
|
||||||
|
|
@ -3059,7 +3059,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__)
|
||||||
|
|
|
||||||
|
|
@ -3087,7 +3087,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__)
|
||||||
|
|
|
||||||
|
|
@ -3061,7 +3061,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__)
|
||||||
|
|
|
||||||
|
|
@ -3060,7 +3060,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__)
|
||||||
|
|
|
||||||
|
|
@ -3061,7 +3061,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__)
|
||||||
|
|
|
||||||
|
|
@ -3049,7 +3049,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__)
|
||||||
|
|
|
||||||
|
|
@ -3060,7 +3060,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__)
|
||||||
|
|
|
||||||
|
|
@ -3060,7 +3060,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__)
|
||||||
|
|
|
||||||
|
|
@ -3049,7 +3049,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__)
|
||||||
|
|
|
||||||
|
|
@ -3060,7 +3060,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__)
|
||||||
|
|
|
||||||
|
|
@ -3061,7 +3061,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__)
|
||||||
|
|
|
||||||
|
|
@ -3060,7 +3060,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__)
|
||||||
|
|
|
||||||
|
|
@ -3061,7 +3061,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__)
|
||||||
|
|
|
||||||
|
|
@ -3064,7 +3064,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__)
|
||||||
|
|
|
||||||
|
|
@ -3027,7 +3027,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__)
|
||||||
|
|
|
||||||
|
|
@ -3044,7 +3044,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__)
|
||||||
|
|
|
||||||
|
|
@ -3056,7 +3056,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__)
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -541,9 +541,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,
|
||||||
|
|
|
||||||
|
|
@ -1842,6 +1842,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")),
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -181,6 +181,7 @@ impl CadCommand for MlineCommand {
|
||||||
.collect();
|
.collect();
|
||||||
pts.push([pt.x, pt.y, pt.z]);
|
pts.push([pt.x, pt.y, pt.z]);
|
||||||
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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
148
src/scene/cache/block_cache.rs
vendored
148
src/scene/cache/block_cache.rs
vendored
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -748,6 +748,7 @@ fn tessellate_entity_inner(
|
||||||
Vec::new()
|
Vec::new()
|
||||||
};
|
};
|
||||||
return vec![WireModel {
|
return vec![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,
|
||||||
|
|
@ -1107,6 +1108,7 @@ fn tessellate_entity_inner(
|
||||||
(ins.insert_point.z) as f32,
|
(ins.insert_point.z) as f32,
|
||||||
);
|
);
|
||||||
let marker = WireModel {
|
let marker = 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,
|
||||||
|
|
@ -1504,6 +1506,7 @@ fn lod_stub_wire(
|
||||||
// LOD boundary. #19.
|
// LOD boundary. #19.
|
||||||
let stored_color = if selected { WireModel::SELECTED } else { color };
|
let stored_color = if selected { WireModel::SELECTED } else { color };
|
||||||
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,
|
||||||
|
|
@ -1597,6 +1600,7 @@ fn lod_stub_wire_3d(
|
||||||
}
|
}
|
||||||
let stored_color = if selected { WireModel::SELECTED } else { color };
|
let stored_color = if selected { WireModel::SELECTED } else { color };
|
||||||
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,
|
||||||
|
|
|
||||||
|
|
@ -145,6 +145,7 @@ fn point_cloud_wires(
|
||||||
};
|
};
|
||||||
let (points, points_low) = points_to_ds(body_points);
|
let (points, points_low) = points_to_ds(body_points);
|
||||||
let mut wires = vec![WireModel {
|
let mut wires = vec![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,
|
||||||
|
|
@ -500,6 +501,7 @@ pub fn tessellate(
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
elem_wires.push(WireModel {
|
elem_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,
|
||||||
|
|
@ -807,6 +809,7 @@ pub fn tessellate(
|
||||||
ftl.push(lo);
|
ftl.push(lo);
|
||||||
}
|
}
|
||||||
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,
|
||||||
|
|
@ -856,6 +859,7 @@ pub fn tessellate(
|
||||||
fpl.push(lo);
|
fpl.push(lo);
|
||||||
}
|
}
|
||||||
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,
|
||||||
|
|
@ -907,6 +911,7 @@ pub fn tessellate(
|
||||||
low.push(ll);
|
low.push(ll);
|
||||||
}
|
}
|
||||||
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,
|
||||||
|
|
@ -939,6 +944,7 @@ pub fn tessellate(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
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,
|
||||||
|
|
@ -1001,6 +1007,7 @@ pub fn tessellate(
|
||||||
(Vec::new(), Vec::new(), Vec::new())
|
(Vec::new(), Vec::new(), Vec::new())
|
||||||
};
|
};
|
||||||
out.push(WireModel {
|
out.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,
|
||||||
|
|
@ -1045,6 +1052,7 @@ pub fn tessellate(
|
||||||
(Vec::new(), Vec::new(), Vec::new())
|
(Vec::new(), Vec::new(), Vec::new())
|
||||||
};
|
};
|
||||||
out.push(WireModel {
|
out.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,
|
||||||
|
|
@ -1086,6 +1094,7 @@ pub fn tessellate(
|
||||||
// are empty → the early-return path above).
|
// are empty → the early-return path above).
|
||||||
if !sdf_verts.is_empty() {
|
if !sdf_verts.is_empty() {
|
||||||
out.push(WireModel {
|
out.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,
|
||||||
|
|
@ -1120,6 +1129,7 @@ pub fn tessellate(
|
||||||
|
|
||||||
if out.is_empty() {
|
if out.is_empty() {
|
||||||
out.push(WireModel {
|
out.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,
|
||||||
|
|
@ -1188,6 +1198,7 @@ pub fn tessellate(
|
||||||
.map(|[kx, ky, kz]| [kx, ky, kz])
|
.map(|[kx, ky, kz]| [kx, ky, kz])
|
||||||
.collect();
|
.collect();
|
||||||
return vec![WireModel {
|
return vec![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,
|
||||||
|
|
@ -1310,20 +1321,12 @@ pub fn tessellate(
|
||||||
} else {
|
} else {
|
||||||
(Vec::new(), Vec::new(), Vec::new())
|
(Vec::new(), Vec::new(), Vec::new())
|
||||||
};
|
};
|
||||||
let relative_marker =
|
let point_marker =
|
||||||
crate::entities::point::relative_marker_spec(entity, document);
|
crate::entities::point::relative_marker_spec(entity, document);
|
||||||
let (taper_widths, world_width) = if let Some((percent, normal)) = relative_marker {
|
|
||||||
// A negative width is reserved for viewport-relative
|
|
||||||
// point markers. The first three taper entries carry
|
|
||||||
// the marker-plane normal; normal taper processing is
|
|
||||||
// disabled for the sentinel by the wire uploader.
|
|
||||||
(normal.to_vec(), -percent)
|
|
||||||
} else {
|
|
||||||
(Vec::new(), polyline_band_width(entity))
|
|
||||||
};
|
|
||||||
out.push(WireModel {
|
out.push(WireModel {
|
||||||
taper_widths,
|
point_marker,
|
||||||
world_width,
|
taper_widths: Vec::new(),
|
||||||
|
world_width: polyline_band_width(entity),
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
display_visible: true,
|
display_visible: true,
|
||||||
plot_visible: true,
|
plot_visible: true,
|
||||||
|
|
@ -1365,6 +1368,7 @@ pub fn tessellate(
|
||||||
(Vec::new(), Vec::new(), Vec::new())
|
(Vec::new(), Vec::new(), Vec::new())
|
||||||
};
|
};
|
||||||
out.push(WireModel {
|
out.push(WireModel {
|
||||||
|
point_marker: None,
|
||||||
taper_widths: Vec::new(),
|
taper_widths: Vec::new(),
|
||||||
world_width: 0.0,
|
world_width: 0.0,
|
||||||
pick_tris: Vec::new(),
|
pick_tris: Vec::new(),
|
||||||
|
|
@ -1399,6 +1403,7 @@ pub fn tessellate(
|
||||||
|
|
||||||
if out.is_empty() {
|
if out.is_empty() {
|
||||||
out.push(WireModel {
|
out.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,
|
||||||
|
|
@ -1446,6 +1451,7 @@ pub fn tessellate(
|
||||||
// treatment as the Contour arm, restarting the dash per segment.
|
// treatment as the Contour arm, restarting the dash per segment.
|
||||||
let (pick_tris, pick_tris_low) = points_to_ds(te.pick_tris);
|
let (pick_tris, pick_tris_low) = points_to_ds(te.pick_tris);
|
||||||
return vec![WireModel {
|
return vec![WireModel {
|
||||||
|
point_marker: None,
|
||||||
taper_widths: Vec::new(),
|
taper_widths: Vec::new(),
|
||||||
world_width: polyline_band_width(entity),
|
world_width: polyline_band_width(entity),
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
|
|
@ -1494,6 +1500,7 @@ pub fn tessellate(
|
||||||
let (pick_tris, pick_tris_low) = points_to_ds(te.pick_tris);
|
let (pick_tris, pick_tris_low) = points_to_ds(te.pick_tris);
|
||||||
let world_width = widths.iter().copied().fold(0.0f32, f32::max);
|
let world_width = widths.iter().copied().fold(0.0f32, f32::max);
|
||||||
return vec![WireModel {
|
return vec![WireModel {
|
||||||
|
point_marker: None,
|
||||||
taper_widths: widths,
|
taper_widths: widths,
|
||||||
world_width,
|
world_width,
|
||||||
depth_override: None,
|
depth_override: None,
|
||||||
|
|
@ -1615,6 +1622,7 @@ pub fn tessellate(
|
||||||
_ => (Vec::new(), Vec::new()),
|
_ => (Vec::new(), Vec::new()),
|
||||||
};
|
};
|
||||||
vec![WireModel {
|
vec![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,
|
||||||
|
|
|
||||||
|
|
@ -7359,14 +7359,30 @@ impl Scene {
|
||||||
&self,
|
&self,
|
||||||
wires: &Arc<Vec<WireModel>>,
|
wires: &Arc<Vec<WireModel>>,
|
||||||
base_radius_px: f32,
|
base_radius_px: f32,
|
||||||
|
viewport_height_px: f32,
|
||||||
|
include_line_weight: bool,
|
||||||
) -> f32 {
|
) -> f32 {
|
||||||
if let Some((base_epoch, base, changes)) = self.interaction_overlay_base() {
|
if let Some((base_epoch, base, changes)) = self.interaction_overlay_base() {
|
||||||
let (_, changed) =
|
let (_, changed) =
|
||||||
self.interaction_overlay_changed_index(base_epoch, &changes);
|
self.interaction_overlay_changed_index(base_epoch, &changes);
|
||||||
base.pick_radius_px(changed.pick_radius_px(base_radius_px))
|
base.pick_radius_px(
|
||||||
|
changed.pick_radius_px(
|
||||||
|
base_radius_px,
|
||||||
|
viewport_height_px,
|
||||||
|
include_line_weight,
|
||||||
|
),
|
||||||
|
viewport_height_px,
|
||||||
|
include_line_weight,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
self.cached_interaction_index(wires)
|
self.cached_interaction_index(wires)
|
||||||
.map_or(base_radius_px, |index| index.pick_radius_px(base_radius_px))
|
.map_or(base_radius_px, |index| {
|
||||||
|
index.pick_radius_px(
|
||||||
|
base_radius_px,
|
||||||
|
viewport_height_px,
|
||||||
|
include_line_weight,
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -7524,11 +7540,12 @@ impl Scene {
|
||||||
{
|
{
|
||||||
return crate::scene::pick::interaction_index::InteractionCandidates::all(wires);
|
return crate::scene::pick::interaction_index::InteractionCandidates::all(wires);
|
||||||
}
|
}
|
||||||
let radius_px = if include_line_weight {
|
let radius_px = self.indexed_interaction_pick_radius(
|
||||||
self.indexed_interaction_pick_radius(&wires, radius_px)
|
&wires,
|
||||||
} else {
|
radius_px,
|
||||||
radius_px
|
bounds.height,
|
||||||
};
|
include_line_weight,
|
||||||
|
);
|
||||||
let flat_ortho = view_rot.z_axis.x.abs() < 1e-9
|
let flat_ortho = view_rot.z_axis.x.abs() < 1e-9
|
||||||
&& view_rot.z_axis.y.abs() < 1e-9
|
&& view_rot.z_axis.y.abs() < 1e-9
|
||||||
&& (view_rot.w_axis.w - 1.0).abs() < 1e-6;
|
&& (view_rot.w_axis.w - 1.0).abs() < 1e-6;
|
||||||
|
|
@ -7597,12 +7614,35 @@ impl Scene {
|
||||||
{
|
{
|
||||||
return crate::scene::pick::interaction_index::InteractionCandidates::all(wires);
|
return crate::scene::pick::interaction_index::InteractionCandidates::all(wires);
|
||||||
}
|
}
|
||||||
|
let pad_px =
|
||||||
|
self.indexed_interaction_pick_radius(&wires, 0.0, bounds.height, true);
|
||||||
if flat_ortho {
|
if flat_ortho {
|
||||||
self.indexed_interaction_candidates_xy(wires, aabb, false)
|
let world_x_px = ((view_rot.x_axis.x * bounds.width * 0.5).powi(2)
|
||||||
|
+ (view_rot.x_axis.y * bounds.height * 0.5).powi(2))
|
||||||
|
.sqrt();
|
||||||
|
let world_y_px = ((view_rot.y_axis.x * bounds.width * 0.5).powi(2)
|
||||||
|
+ (view_rot.y_axis.y * bounds.height * 0.5).powi(2))
|
||||||
|
.sqrt();
|
||||||
|
let scale = world_x_px.min(world_y_px);
|
||||||
|
let pad = if scale > 1e-6 {
|
||||||
|
pad_px as f64 / scale as f64
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
};
|
||||||
|
self.indexed_interaction_candidates_xy(
|
||||||
|
wires,
|
||||||
|
[aabb[0] - pad, aabb[1] - pad, aabb[2] + pad, aabb[3] + pad],
|
||||||
|
false,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
self.indexed_interaction_candidates_screen(
|
self.indexed_interaction_candidates_screen(
|
||||||
wires,
|
wires,
|
||||||
screen_rect,
|
[
|
||||||
|
screen_rect[0] - pad_px,
|
||||||
|
screen_rect[1] - pad_px,
|
||||||
|
screen_rect[2] + pad_px,
|
||||||
|
screen_rect[3] + pad_px,
|
||||||
|
],
|
||||||
view_rot,
|
view_rot,
|
||||||
eye,
|
eye,
|
||||||
bounds,
|
bounds,
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,26 @@ pub enum TangentGeom {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Viewport-relative display transform for a point marker.
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
pub struct PointMarker {
|
||||||
|
pub origin: glam::DVec3,
|
||||||
|
pub normal: glam::DVec3,
|
||||||
|
pub axis_x: glam::DVec3,
|
||||||
|
pub axis_y: glam::DVec3,
|
||||||
|
pub viewport_percent: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PointMarker {
|
||||||
|
pub fn resolve(self, point: glam::DVec3, view_height: f64) -> glam::DVec3 {
|
||||||
|
let normal = self.normal.normalize_or(glam::DVec3::Z);
|
||||||
|
let delta = point - self.origin;
|
||||||
|
let axial = normal * delta.dot(normal);
|
||||||
|
let scale = self.viewport_percent.max(0.0) as f64 * 0.01 * view_height.max(0.0);
|
||||||
|
self.origin + axial + (delta - axial) * scale
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A 1-D entity (line, arc, polyline) represented as an ordered set of
|
/// A 1-D entity (line, arc, polyline) represented as an ordered set of
|
||||||
/// world-space points rendered as a quad strip (TriangleList).
|
/// world-space points rendered as a quad strip (TriangleList).
|
||||||
///
|
///
|
||||||
|
|
@ -66,6 +86,8 @@ pub struct WireModel {
|
||||||
/// "all-zero residual" (interactive draw / preview wires whose coordinates
|
/// "all-zero residual" (interactive draw / preview wires whose coordinates
|
||||||
/// don't need sub-f32 precision). Tessellation from CAD f64 fills it.
|
/// don't need sub-f32 precision). Tessellation from CAD f64 fills it.
|
||||||
pub points_low: Vec<[f32; 3]>,
|
pub points_low: Vec<[f32; 3]>,
|
||||||
|
/// Point-marker scaling shared by the GPU, picking, and plotting paths.
|
||||||
|
pub point_marker: Option<PointMarker>,
|
||||||
/// RGBA colour in [0, 1].
|
/// RGBA colour in [0, 1].
|
||||||
pub color: [f32; 4],
|
pub color: [f32; 4],
|
||||||
/// Whether this wire is currently selected.
|
/// Whether this wire is currently selected.
|
||||||
|
|
@ -225,6 +247,7 @@ impl WireModel {
|
||||||
/// Create a solid wire (no dash pattern, 1px weight).
|
/// Create a solid wire (no dash pattern, 1px weight).
|
||||||
pub fn solid(name: String, points: Vec<[f32; 3]>, color: [f32; 4], selected: bool) -> Self {
|
pub fn solid(name: String, points: Vec<[f32; 3]>, color: [f32; 4], selected: bool) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
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,
|
||||||
|
|
@ -263,10 +286,11 @@ impl WireModel {
|
||||||
out.name = format!("preview_{}", self.name);
|
out.name = format!("preview_{}", self.name);
|
||||||
out.color = Self::CYAN;
|
out.color = Self::CYAN;
|
||||||
out.selected = false;
|
out.selected = false;
|
||||||
for p in &mut out.points {
|
map_points(&mut out.points, &mut out.points_low, |point| {
|
||||||
p[0] += delta.x;
|
point + delta.as_dvec3()
|
||||||
p[1] += delta.y;
|
});
|
||||||
p[2] += delta.z;
|
if let Some(marker) = &mut out.point_marker {
|
||||||
|
marker.origin += delta.as_dvec3();
|
||||||
}
|
}
|
||||||
if !out.text_verts.is_empty() {
|
if !out.text_verts.is_empty() {
|
||||||
let (dx, dy, dz) = (delta.x as f64, delta.y as f64, delta.z as f64);
|
let (dx, dy, dz) = (delta.x as f64, delta.y as f64, delta.z as f64);
|
||||||
|
|
@ -301,6 +325,17 @@ impl WireModel {
|
||||||
for p in &mut out.points_low {
|
for p in &mut out.points_low {
|
||||||
*p = (rotation * glam::Vec3::from_array(*p)).to_array();
|
*p = (rotation * glam::Vec3::from_array(*p)).to_array();
|
||||||
}
|
}
|
||||||
|
if let Some(marker) = &mut out.point_marker {
|
||||||
|
let center = center.as_dvec3();
|
||||||
|
let rotation = glam::DQuat::from_axis_angle(
|
||||||
|
axis.as_dvec3().normalize_or(glam::DVec3::Z),
|
||||||
|
angle_rad as f64,
|
||||||
|
);
|
||||||
|
marker.origin = center + rotation * (marker.origin - center);
|
||||||
|
marker.normal = (rotation * marker.normal).normalize_or(glam::DVec3::Z);
|
||||||
|
marker.axis_x = (rotation * marker.axis_x).normalize_or(glam::DVec3::X);
|
||||||
|
marker.axis_y = (rotation * marker.axis_y).normalize_or(glam::DVec3::Y);
|
||||||
|
}
|
||||||
if !out.text_verts.is_empty() {
|
if !out.text_verts.is_empty() {
|
||||||
let center = center.as_dvec3();
|
let center = center.as_dvec3();
|
||||||
let axis = axis.as_dvec3().normalize_or_zero();
|
let axis = axis.as_dvec3().normalize_or_zero();
|
||||||
|
|
@ -323,10 +358,23 @@ impl WireModel {
|
||||||
out.name = format!("preview_{}", self.name);
|
out.name = format!("preview_{}", self.name);
|
||||||
out.color = Self::CYAN;
|
out.color = Self::CYAN;
|
||||||
out.selected = false;
|
out.selected = false;
|
||||||
for p in &mut out.points {
|
if let Some(marker) = out.point_marker {
|
||||||
p[0] = center.x + (p[0] - center.x) * factor;
|
let center = center.as_dvec3();
|
||||||
p[1] = center.y + (p[1] - center.y) * factor;
|
let normal = marker.normal.normalize_or(glam::DVec3::Z);
|
||||||
p[2] = center.z + (p[2] - center.z) * factor;
|
let target_origin = center + (marker.origin - center) * factor as f64;
|
||||||
|
map_points(&mut out.points, &mut out.points_low, |point| {
|
||||||
|
let delta = point - marker.origin;
|
||||||
|
let axial = normal * delta.dot(normal);
|
||||||
|
target_origin + (delta - axial) + axial * factor as f64
|
||||||
|
});
|
||||||
|
if let Some(target) = &mut out.point_marker {
|
||||||
|
target.origin = target_origin;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let center = center.as_dvec3();
|
||||||
|
map_points(&mut out.points, &mut out.points_low, |point| {
|
||||||
|
center + (point - center) * factor as f64
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if !out.text_verts.is_empty() {
|
if !out.text_verts.is_empty() {
|
||||||
let (cx, cy, cz) = (center.x as f64, center.y as f64, center.z as f64);
|
let (cx, cy, cz) = (center.x as f64, center.y as f64, center.z as f64);
|
||||||
|
|
@ -373,11 +421,22 @@ pub fn stretched_windows(
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
for p in &mut out.points {
|
if let Some(marker) = out.point_marker {
|
||||||
if inside(p[0], p[1]) {
|
if inside(marker.origin.x as f32, marker.origin.y as f32) {
|
||||||
p[0] += delta.x;
|
map_points(&mut out.points, &mut out.points_low, |point| {
|
||||||
p[1] += delta.y;
|
point + delta.as_dvec3()
|
||||||
p[2] += delta.z;
|
});
|
||||||
|
if let Some(target) = &mut out.point_marker {
|
||||||
|
target.origin += delta.as_dvec3();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for p in &mut out.points {
|
||||||
|
if inside(p[0], p[1]) {
|
||||||
|
p[0] += delta.x;
|
||||||
|
p[1] += delta.y;
|
||||||
|
p[2] += delta.z;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -438,6 +497,20 @@ pub fn stretched_windows(
|
||||||
let point = glam::Vec3::from_array(*p);
|
let point = glam::Vec3::from_array(*p);
|
||||||
*p = (point - 2.0 * plane_normal.dot(point) * plane_normal).to_array();
|
*p = (point - 2.0 * plane_normal.dot(point) * plane_normal).to_array();
|
||||||
}
|
}
|
||||||
|
if let Some(marker) = &mut out.point_marker {
|
||||||
|
let p1 = p1.as_dvec3();
|
||||||
|
let normal = plane_normal.as_dvec3();
|
||||||
|
marker.origin -= 2.0 * normal.dot(marker.origin - p1) * normal;
|
||||||
|
marker.normal =
|
||||||
|
(marker.normal - 2.0 * normal.dot(marker.normal) * normal)
|
||||||
|
.normalize_or(glam::DVec3::Z);
|
||||||
|
marker.axis_x =
|
||||||
|
(marker.axis_x - 2.0 * normal.dot(marker.axis_x) * normal)
|
||||||
|
.normalize_or(glam::DVec3::X);
|
||||||
|
marker.axis_y =
|
||||||
|
(marker.axis_y - 2.0 * normal.dot(marker.axis_y) * normal)
|
||||||
|
.normalize_or(glam::DVec3::Y);
|
||||||
|
}
|
||||||
// Glyph quads reflect wholesale (true mirror) — the caller only routes
|
// Glyph quads reflect wholesale (true mirror) — the caller only routes
|
||||||
// text through here for MIRRTEXT-on; MIRRTEXT-off relocates via
|
// text through here for MIRRTEXT-on; MIRRTEXT-off relocates via
|
||||||
// `translated` so glyphs stay readable.
|
// `translated` so glyphs stay readable.
|
||||||
|
|
@ -466,6 +539,44 @@ pub fn stretched_windows(
|
||||||
})
|
})
|
||||||
.sum()
|
.sum()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn point_world(&self, index: usize, view_height: f64) -> glam::DVec3 {
|
||||||
|
let high = self.points[index];
|
||||||
|
let low = self.points_low.get(index).copied().unwrap_or([0.0; 3]);
|
||||||
|
let point = glam::DVec3::new(
|
||||||
|
high[0] as f64 + low[0] as f64,
|
||||||
|
high[1] as f64 + low[1] as f64,
|
||||||
|
high[2] as f64 + low[2] as f64,
|
||||||
|
);
|
||||||
|
self.point_marker
|
||||||
|
.map_or(point, |marker| marker.resolve(point, view_height))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn map_points(
|
||||||
|
points: &mut [[f32; 3]],
|
||||||
|
points_low: &mut Vec<[f32; 3]>,
|
||||||
|
map: impl Fn(glam::DVec3) -> glam::DVec3,
|
||||||
|
) {
|
||||||
|
let old_low = std::mem::take(points_low);
|
||||||
|
points_low.reserve(points.len());
|
||||||
|
for (index, high) in points.iter_mut().enumerate() {
|
||||||
|
if !high[0].is_finite() || !high[1].is_finite() || !high[2].is_finite() {
|
||||||
|
points_low.push([0.0; 3]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let low = old_low.get(index).copied().unwrap_or([0.0; 3]);
|
||||||
|
let mapped = map(glam::DVec3::new(
|
||||||
|
high[0] as f64 + low[0] as f64,
|
||||||
|
high[1] as f64 + low[1] as f64,
|
||||||
|
high[2] as f64 + low[2] as f64,
|
||||||
|
));
|
||||||
|
let (hx, lx) = WireModel::split_ds(mapped.x);
|
||||||
|
let (hy, ly) = WireModel::split_ds(mapped.y);
|
||||||
|
let (hz, lz) = WireModel::split_ds(mapped.z);
|
||||||
|
*high = [hx, hy, hz];
|
||||||
|
points_low.push([lx, ly, lz]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Map every glyph vertex's double-single world position through `f`, re-
|
/// Map every glyph vertex's double-single world position through `f`, re-
|
||||||
|
|
@ -502,6 +613,7 @@ pub(crate) fn map_text_verts(
|
||||||
impl Default for WireModel {
|
impl Default for WireModel {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
point_marker: None,
|
||||||
text_verts: Vec::new(),
|
text_verts: Vec::new(),
|
||||||
name: String::new(),
|
name: String::new(),
|
||||||
points: Vec::new(),
|
points: Vec::new(),
|
||||||
|
|
|
||||||
|
|
@ -208,6 +208,31 @@ pub fn click_hit<'a, W: WireSource + ?Sized>(
|
||||||
let mut best_dist = f32::MAX;
|
let mut best_dist = f32::MAX;
|
||||||
let mut best: Option<&str> = None;
|
let mut best: Option<&str> = None;
|
||||||
|
|
||||||
|
for wire in wires.iter().filter(|wire| wire.point_marker.is_some()) {
|
||||||
|
let tolerance = pick_tolerance_px(wire, lw_display, base_radius_px);
|
||||||
|
let mut previous = None;
|
||||||
|
for (index, point) in wire.points.iter().enumerate() {
|
||||||
|
if !point[0].is_finite() {
|
||||||
|
previous = None;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let screen = world_to_screen(
|
||||||
|
wire_point_world(wire, index, view_rot, eye),
|
||||||
|
view_rot,
|
||||||
|
eye,
|
||||||
|
bounds,
|
||||||
|
);
|
||||||
|
if let Some(start) = previous {
|
||||||
|
let distance = dist_point_to_segment(cursor, start, screen);
|
||||||
|
if distance < tolerance && distance < best_dist {
|
||||||
|
best_dist = distance;
|
||||||
|
best = Some(wire.name.as_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
previous = Some(screen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// World z only shifts the *screen* x/y when the view is tilted (orbit /
|
// World z only shifts the *screen* x/y when the view is tilted (orbit /
|
||||||
// perspective). In the flat top-down ortho view — the case where hover lag
|
// perspective). In the flat top-down ortho view — the case where hover lag
|
||||||
// on large drawings actually bites — a wire's screen position depends only
|
// on large drawings actually bites — a wire's screen position depends only
|
||||||
|
|
@ -224,18 +249,21 @@ pub fn click_hit<'a, W: WireSource + ?Sized>(
|
||||||
let Some(wire) = wires.source_wire(segment.wire) else {
|
let Some(wire) = wires.source_wire(segment.wire) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
if wire.point_marker.is_some() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let start = segment.start as usize;
|
let start = segment.start as usize;
|
||||||
if start + 1 >= wire.points.len() {
|
if start + 1 >= wire.points.len() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let p0 = world_to_screen(
|
let p0 = world_to_screen(
|
||||||
wp64(wire.points[start], &wire.points_low, start),
|
wire_point_world(wire, start, view_rot, eye),
|
||||||
view_rot,
|
view_rot,
|
||||||
eye,
|
eye,
|
||||||
bounds,
|
bounds,
|
||||||
);
|
);
|
||||||
let p1 = world_to_screen(
|
let p1 = world_to_screen(
|
||||||
wp64(wire.points[start + 1], &wire.points_low, start + 1),
|
wire_point_world(wire, start + 1, view_rot, eye),
|
||||||
view_rot,
|
view_rot,
|
||||||
eye,
|
eye,
|
||||||
bounds,
|
bounds,
|
||||||
|
|
@ -249,23 +277,27 @@ pub fn click_hit<'a, W: WireSource + ?Sized>(
|
||||||
} else {
|
} else {
|
||||||
// Q: lazy projection — no Vec allocation per wire; NaN resets the segment chain.
|
// Q: lazy projection — no Vec allocation per wire; NaN resets the segment chain.
|
||||||
for wire in wires.iter() {
|
for wire in wires.iter() {
|
||||||
|
if wire.point_marker.is_some() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let tol = pick_tolerance_px(wire, lw_display, base_radius_px);
|
let tol = pick_tolerance_px(wire, lw_display, base_radius_px);
|
||||||
// Cheap AABB pre-reject (flat view only; never for the unbounded
|
// Cheap AABB pre-reject (flat view only; never for the unbounded
|
||||||
// sentinel used by previews / greeked text).
|
// sentinel used by previews / greeked text).
|
||||||
if z_flat
|
if z_flat
|
||||||
|
&& wire.point_marker.is_none()
|
||||||
&& wire.aabb != WireModel::UNBOUNDED_AABB
|
&& wire.aabb != WireModel::UNBOUNDED_AABB
|
||||||
&& aabb_rejects(wire.aabb, cursor, tol, view_rot, eye, bounds)
|
&& aabb_rejects(wire.aabb, cursor, tol, view_rot, eye, bounds)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let mut prev: Option<Point> = None;
|
let mut prev: Option<Point> = None;
|
||||||
for (i, &[px, py, pz]) in wire.points.iter().enumerate() {
|
for (i, &[px, _, _]) in wire.points.iter().enumerate() {
|
||||||
if px.is_nan() {
|
if px.is_nan() {
|
||||||
prev = None;
|
prev = None;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let cur = world_to_screen(
|
let cur = world_to_screen(
|
||||||
wp64([px, py, pz], &wire.points_low, i),
|
wire_point_world(wire, i, view_rot, eye),
|
||||||
view_rot,
|
view_rot,
|
||||||
eye,
|
eye,
|
||||||
bounds,
|
bounds,
|
||||||
|
|
@ -450,24 +482,55 @@ pub fn click_hits_all<'a, W: WireSource + ?Sized>(
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
}
|
}
|
||||||
let mut hits: Vec<(f32, &str)> = Vec::new();
|
let mut hits: Vec<(f32, &str)> = Vec::new();
|
||||||
|
let mut marker_hits: HashMap<&str, f32> = HashMap::default();
|
||||||
|
for wire in wires.iter().filter(|wire| wire.point_marker.is_some()) {
|
||||||
|
let tolerance = pick_tolerance_px(wire, lw_display, base_radius_px);
|
||||||
|
let mut previous = None;
|
||||||
|
for (index, point) in wire.points.iter().enumerate() {
|
||||||
|
if !point[0].is_finite() {
|
||||||
|
previous = None;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let screen = world_to_screen(
|
||||||
|
wire_point_world(wire, index, view_rot, eye),
|
||||||
|
view_rot,
|
||||||
|
eye,
|
||||||
|
bounds,
|
||||||
|
);
|
||||||
|
if let Some(start) = previous {
|
||||||
|
let distance = dist_point_to_segment(cursor, start, screen);
|
||||||
|
if distance < tolerance {
|
||||||
|
marker_hits
|
||||||
|
.entry(wire.name.as_str())
|
||||||
|
.and_modify(|best| *best = best.min(distance))
|
||||||
|
.or_insert(distance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
previous = Some(screen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hits.extend(marker_hits.into_iter().map(|(name, distance)| (distance, name)));
|
||||||
if let Some(segments) = wires.segments() {
|
if let Some(segments) = wires.segments() {
|
||||||
let mut best_by_wire: HashMap<u32, f32> = HashMap::default();
|
let mut best_by_wire: HashMap<u32, f32> = HashMap::default();
|
||||||
for segment in segments {
|
for segment in segments {
|
||||||
let Some(wire) = wires.source_wire(segment.wire) else {
|
let Some(wire) = wires.source_wire(segment.wire) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
if wire.point_marker.is_some() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let start = segment.start as usize;
|
let start = segment.start as usize;
|
||||||
if start + 1 >= wire.points.len() {
|
if start + 1 >= wire.points.len() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let p0 = world_to_screen(
|
let p0 = world_to_screen(
|
||||||
wp64(wire.points[start], &wire.points_low, start),
|
wire_point_world(wire, start, view_rot, eye),
|
||||||
view_rot,
|
view_rot,
|
||||||
eye,
|
eye,
|
||||||
bounds,
|
bounds,
|
||||||
);
|
);
|
||||||
let p1 = world_to_screen(
|
let p1 = world_to_screen(
|
||||||
wp64(wire.points[start + 1], &wire.points_low, start + 1),
|
wire_point_world(wire, start + 1, view_rot, eye),
|
||||||
view_rot,
|
view_rot,
|
||||||
eye,
|
eye,
|
||||||
bounds,
|
bounds,
|
||||||
|
|
@ -487,17 +550,20 @@ pub fn click_hits_all<'a, W: WireSource + ?Sized>(
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
for wire in wires.iter() {
|
for wire in wires.iter() {
|
||||||
|
if wire.point_marker.is_some() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let tol = pick_tolerance_px(wire, lw_display, base_radius_px);
|
let tol = pick_tolerance_px(wire, lw_display, base_radius_px);
|
||||||
let mut prev: Option<Point> = None;
|
let mut prev: Option<Point> = None;
|
||||||
let mut best_for_wire = tol;
|
let mut best_for_wire = tol;
|
||||||
let mut hit = false;
|
let mut hit = false;
|
||||||
for (i, &[px, py, pz]) in wire.points.iter().enumerate() {
|
for (i, &[px, _, _]) in wire.points.iter().enumerate() {
|
||||||
if px.is_nan() {
|
if px.is_nan() {
|
||||||
prev = None;
|
prev = None;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let cur = world_to_screen(
|
let cur = world_to_screen(
|
||||||
wp64([px, py, pz], &wire.points_low, i),
|
wire_point_world(wire, i, view_rot, eye),
|
||||||
view_rot,
|
view_rot,
|
||||||
eye,
|
eye,
|
||||||
bounds,
|
bounds,
|
||||||
|
|
@ -1104,13 +1170,13 @@ fn indexed_box_crossing_hits<'a, W: WireSource + ?Sized>(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let a = world_to_screen(
|
let a = world_to_screen(
|
||||||
wp64(wire.points[start], &wire.points_low, start),
|
wire_point_world(wire, start, view_rot, eye),
|
||||||
view_rot,
|
view_rot,
|
||||||
eye,
|
eye,
|
||||||
bounds,
|
bounds,
|
||||||
);
|
);
|
||||||
let b = world_to_screen(
|
let b = world_to_screen(
|
||||||
wp64(wire.points[start + 1], &wire.points_low, start + 1),
|
wire_point_world(wire, start + 1, view_rot, eye),
|
||||||
view_rot,
|
view_rot,
|
||||||
eye,
|
eye,
|
||||||
bounds,
|
bounds,
|
||||||
|
|
@ -1119,6 +1185,14 @@ fn indexed_box_crossing_hits<'a, W: WireSource + ?Sized>(
|
||||||
out.push(wire.name.as_str());
|
out.push(wire.name.as_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for wire in wires.iter().filter(|wire| wire.point_marker.is_some()) {
|
||||||
|
if !seen.contains(wire.name.as_str())
|
||||||
|
&& marker_segment_hit(wire, view_rot, eye, bounds, |a, b| segment_hits(a, b))
|
||||||
|
&& seen.insert(wire.name.as_str())
|
||||||
|
{
|
||||||
|
out.push(wire.name.as_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
for (triangles, pick_only) in [
|
for (triangles, pick_only) in [
|
||||||
(wires.fill_triangles().unwrap_or_default(), false),
|
(wires.fill_triangles().unwrap_or_default(), false),
|
||||||
(wires.pick_triangles().unwrap_or_default(), true),
|
(wires.pick_triangles().unwrap_or_default(), true),
|
||||||
|
|
@ -1181,7 +1255,7 @@ fn indexed_box_crossing_hits<'a, W: WireSource + ?Sized>(
|
||||||
if wire.points.iter().enumerate().any(|(index, &point)| {
|
if wire.points.iter().enumerate().any(|(index, &point)| {
|
||||||
point[0].is_finite()
|
point[0].is_finite()
|
||||||
&& inside(world_to_screen(
|
&& inside(world_to_screen(
|
||||||
wp64(point, &wire.points_low, index),
|
wire_point_world(wire, index, view_rot, eye),
|
||||||
view_rot,
|
view_rot,
|
||||||
eye,
|
eye,
|
||||||
bounds,
|
bounds,
|
||||||
|
|
@ -1231,13 +1305,13 @@ pub fn poly_fence_hit<'a, W: WireSource + ?Sized>(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let a = world_to_screen(
|
let a = world_to_screen(
|
||||||
wp64(wire.points[start], &wire.points_low, start),
|
wire_point_world(wire, start, view_rot, eye),
|
||||||
view_rot,
|
view_rot,
|
||||||
eye,
|
eye,
|
||||||
bounds,
|
bounds,
|
||||||
);
|
);
|
||||||
let b = world_to_screen(
|
let b = world_to_screen(
|
||||||
wp64(wire.points[start + 1], &wire.points_low, start + 1),
|
wire_point_world(wire, start + 1, view_rot, eye),
|
||||||
view_rot,
|
view_rot,
|
||||||
eye,
|
eye,
|
||||||
bounds,
|
bounds,
|
||||||
|
|
@ -1246,6 +1320,14 @@ pub fn poly_fence_hit<'a, W: WireSource + ?Sized>(
|
||||||
out.push(wire.name.as_str());
|
out.push(wire.name.as_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for wire in wires.iter().filter(|wire| wire.point_marker.is_some()) {
|
||||||
|
if !seen.contains(wire.name.as_str())
|
||||||
|
&& marker_segment_hit(wire, view_rot, eye, bounds, |a, b| cuts(a, b))
|
||||||
|
&& seen.insert(wire.name.as_str())
|
||||||
|
{
|
||||||
|
out.push(wire.name.as_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
for wire in wires.iter() {
|
for wire in wires.iter() {
|
||||||
|
|
@ -1254,13 +1336,13 @@ pub fn poly_fence_hit<'a, W: WireSource + ?Sized>(
|
||||||
}
|
}
|
||||||
let hit = (0..wire.points.len() - 1).any(|k| {
|
let hit = (0..wire.points.len() - 1).any(|k| {
|
||||||
let a = world_to_screen(
|
let a = world_to_screen(
|
||||||
wp64(wire.points[k], &wire.points_low, k),
|
wire_point_world(wire, k, view_rot, eye),
|
||||||
view_rot,
|
view_rot,
|
||||||
eye,
|
eye,
|
||||||
bounds,
|
bounds,
|
||||||
);
|
);
|
||||||
let b = world_to_screen(
|
let b = world_to_screen(
|
||||||
wp64(wire.points[k + 1], &wire.points_low, k + 1),
|
wire_point_world(wire, k + 1, view_rot, eye),
|
||||||
view_rot,
|
view_rot,
|
||||||
eye,
|
eye,
|
||||||
bounds,
|
bounds,
|
||||||
|
|
@ -1292,13 +1374,13 @@ fn indexed_polygon_crossing_hits<'a, W: WireSource + ?Sized>(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let a = world_to_screen(
|
let a = world_to_screen(
|
||||||
wp64(wire.points[start], &wire.points_low, start),
|
wire_point_world(wire, start, view_rot, eye),
|
||||||
view_rot,
|
view_rot,
|
||||||
eye,
|
eye,
|
||||||
bounds,
|
bounds,
|
||||||
);
|
);
|
||||||
let b = world_to_screen(
|
let b = world_to_screen(
|
||||||
wp64(wire.points[start + 1], &wire.points_low, start + 1),
|
wire_point_world(wire, start + 1, view_rot, eye),
|
||||||
view_rot,
|
view_rot,
|
||||||
eye,
|
eye,
|
||||||
bounds,
|
bounds,
|
||||||
|
|
@ -1311,6 +1393,18 @@ fn indexed_polygon_crossing_hits<'a, W: WireSource + ?Sized>(
|
||||||
out.push(wire.name.as_str());
|
out.push(wire.name.as_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for wire in wires.iter().filter(|wire| wire.point_marker.is_some()) {
|
||||||
|
if !seen.contains(wire.name.as_str())
|
||||||
|
&& marker_segment_hit(wire, view_rot, eye, bounds, |a, b| {
|
||||||
|
point_in_polygon(a, poly)
|
||||||
|
|| point_in_polygon(b, poly)
|
||||||
|
|| segment_crosses_polygon(a, b, poly)
|
||||||
|
})
|
||||||
|
&& seen.insert(wire.name.as_str())
|
||||||
|
{
|
||||||
|
out.push(wire.name.as_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
for (triangles, pick_only) in [
|
for (triangles, pick_only) in [
|
||||||
(wires.fill_triangles().unwrap_or_default(), false),
|
(wires.fill_triangles().unwrap_or_default(), false),
|
||||||
(wires.pick_triangles().unwrap_or_default(), true),
|
(wires.pick_triangles().unwrap_or_default(), true),
|
||||||
|
|
@ -1371,7 +1465,12 @@ fn indexed_polygon_crossing_hits<'a, W: WireSource + ?Sized>(
|
||||||
if wire.points.iter().enumerate().any(|(index, &point)| {
|
if wire.points.iter().enumerate().any(|(index, &point)| {
|
||||||
point[0].is_finite()
|
point[0].is_finite()
|
||||||
&& point_in_polygon(
|
&& point_in_polygon(
|
||||||
world_to_screen(wp64(point, &wire.points_low, index), view_rot, eye, bounds),
|
world_to_screen(
|
||||||
|
wire_point_world(wire, index, view_rot, eye),
|
||||||
|
view_rot,
|
||||||
|
eye,
|
||||||
|
bounds,
|
||||||
|
),
|
||||||
poly,
|
poly,
|
||||||
)
|
)
|
||||||
}) && seen.insert(wire.name.as_str())
|
}) && seen.insert(wire.name.as_str())
|
||||||
|
|
@ -1473,7 +1572,12 @@ pub fn box_hit<'a, W: WireSource + ?Sized>(
|
||||||
prev = None;
|
prev = None;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let sp = world_to_screen(wp64([px, py, pz], low, i), view_rot, eye, bounds);
|
let world = if !wire.points.is_empty() {
|
||||||
|
wire_point_world(wire, i, view_rot, eye)
|
||||||
|
} else {
|
||||||
|
wp64([px, py, pz], low, i)
|
||||||
|
};
|
||||||
|
let sp = world_to_screen(world, view_rot, eye, bounds);
|
||||||
if crossing {
|
if crossing {
|
||||||
if inside(sp) {
|
if inside(sp) {
|
||||||
hit = true;
|
hit = true;
|
||||||
|
|
@ -1595,7 +1699,12 @@ pub fn poly_hit<'a, W: WireSource + ?Sized>(
|
||||||
prev = None;
|
prev = None;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let sp = world_to_screen(wp64([px, py, pz], low, i), view_rot, eye, bounds);
|
let world = if !wire.points.is_empty() {
|
||||||
|
wire_point_world(wire, i, view_rot, eye)
|
||||||
|
} else {
|
||||||
|
wp64([px, py, pz], low, i)
|
||||||
|
};
|
||||||
|
let sp = world_to_screen(world, view_rot, eye, bounds);
|
||||||
// Reject points the GPU scissored out of a floating viewport so
|
// Reject points the GPU scissored out of a floating viewport so
|
||||||
// the lasso can't reach clipped geometry. No-op in model space.
|
// the lasso can't reach clipped geometry. No-op in model space.
|
||||||
if sp.x < 0.0 || sp.x > bounds.width || sp.y < 0.0 || sp.y > bounds.height {
|
if sp.x < 0.0 || sp.x > bounds.width || sp.y < 0.0 || sp.y > bounds.height {
|
||||||
|
|
@ -1694,6 +1803,53 @@ fn wp64(hi: [f32; 3], low: &[[f32; 3]], i: usize) -> glam::DVec3 {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn wire_point_world(
|
||||||
|
wire: &WireModel,
|
||||||
|
index: usize,
|
||||||
|
view_rot: Mat4,
|
||||||
|
eye: glam::DVec3,
|
||||||
|
) -> glam::DVec3 {
|
||||||
|
let Some(marker) = wire.point_marker else {
|
||||||
|
return wire.point_world(index, 0.0);
|
||||||
|
};
|
||||||
|
let row_y = glam::DVec3::new(
|
||||||
|
view_rot.x_axis.y as f64,
|
||||||
|
view_rot.y_axis.y as f64,
|
||||||
|
view_rot.z_axis.y as f64,
|
||||||
|
);
|
||||||
|
let projection_scale = row_y.length().max(1.0e-12);
|
||||||
|
let relative = (marker.origin - eye).as_vec3().extend(1.0);
|
||||||
|
let clip_w = (view_rot * relative).w.abs().max(1.0e-6);
|
||||||
|
wire.point_world(index, 2.0 * clip_w as f64 / projection_scale)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn marker_segment_hit(
|
||||||
|
wire: &WireModel,
|
||||||
|
view_rot: Mat4,
|
||||||
|
eye: glam::DVec3,
|
||||||
|
bounds: Rectangle,
|
||||||
|
mut hit: impl FnMut(Point, Point) -> bool,
|
||||||
|
) -> bool {
|
||||||
|
let mut previous = None;
|
||||||
|
for (index, point) in wire.points.iter().enumerate() {
|
||||||
|
if !point[0].is_finite() {
|
||||||
|
previous = None;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let screen = world_to_screen(
|
||||||
|
wire_point_world(wire, index, view_rot, eye),
|
||||||
|
view_rot,
|
||||||
|
eye,
|
||||||
|
bounds,
|
||||||
|
);
|
||||||
|
if previous.is_some_and(|start| hit(start, screen)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
previous = Some(screen);
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
/// Even-odd ray-casting test: is `p` inside the polygon?
|
/// Even-odd ray-casting test: is `p` inside the polygon?
|
||||||
///
|
///
|
||||||
/// Handles multi-path boundaries: NaN points (used as path separators by
|
/// Handles multi-path boundaries: NaN points (used as path separators by
|
||||||
|
|
|
||||||
|
|
@ -557,7 +557,9 @@ pub struct InteractionIndex {
|
||||||
pick_triangles: SpatialSet<TriangleRef>,
|
pick_triangles: SpatialSet<TriangleRef>,
|
||||||
glyphs: SpatialSet<GlyphRef>,
|
glyphs: SpatialSet<GlyphRef>,
|
||||||
unbounded_wires: Vec<u32>,
|
unbounded_wires: Vec<u32>,
|
||||||
|
screen_unbounded_wires: Vec<u32>,
|
||||||
max_line_half_width_px: f32,
|
max_line_half_width_px: f32,
|
||||||
|
max_marker_radius_fraction: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct InteractionHandleIndex {
|
pub struct InteractionHandleIndex {
|
||||||
|
|
@ -574,7 +576,9 @@ struct WireIndexEntries {
|
||||||
pick_triangles: Vec<Entry3<TriangleRef>>,
|
pick_triangles: Vec<Entry3<TriangleRef>>,
|
||||||
glyphs: Vec<Entry3<GlyphRef>>,
|
glyphs: Vec<Entry3<GlyphRef>>,
|
||||||
unbounded: bool,
|
unbounded: bool,
|
||||||
|
screen_unbounded: bool,
|
||||||
max_line_half_width_px: f32,
|
max_line_half_width_px: f32,
|
||||||
|
max_marker_radius_fraction: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_wire_index_entries(wire_idx: u32, wire: &WireModel) -> WireIndexEntries {
|
fn collect_wire_index_entries(wire_idx: u32, wire: &WireModel) -> WireIndexEntries {
|
||||||
|
|
@ -591,28 +595,34 @@ fn collect_wire_index_entries(wire_idx: u32, wire: &WireModel) -> WireIndexEntri
|
||||||
pick_triangles: Vec::with_capacity(wire.pick_tris.len() / 3),
|
pick_triangles: Vec::with_capacity(wire.pick_tris.len() / 3),
|
||||||
glyphs: Vec::with_capacity(wire.text_verts.len() / 6),
|
glyphs: Vec::with_capacity(wire.text_verts.len() / 6),
|
||||||
unbounded: false,
|
unbounded: false,
|
||||||
|
screen_unbounded: wire.point_marker.is_some(),
|
||||||
max_line_half_width_px: if wire.line_weight_px.is_finite() {
|
max_line_half_width_px: if wire.line_weight_px.is_finite() {
|
||||||
(wire.line_weight_px * 0.5).max(0.0)
|
(wire.line_weight_px * 0.5).max(0.0)
|
||||||
} else {
|
} else {
|
||||||
0.0
|
0.0
|
||||||
},
|
},
|
||||||
|
max_marker_radius_fraction: wire.point_marker.map_or(0.0, |marker| {
|
||||||
|
marker.viewport_percent.max(0.0) * 0.01 * std::f32::consts::SQRT_2
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
entries.unbounded = entries.wire.is_none();
|
entries.unbounded = entries.wire.is_none();
|
||||||
|
|
||||||
for start in 0..wire.points.len().saturating_sub(1) {
|
if wire.point_marker.is_none() {
|
||||||
let Some(aabb) = points_aabb3([
|
for start in 0..wire.points.len().saturating_sub(1) {
|
||||||
wire_point(wire, start),
|
let Some(aabb) = points_aabb3([
|
||||||
wire_point(wire, start + 1),
|
wire_point(wire, start),
|
||||||
]) else {
|
wire_point(wire, start + 1),
|
||||||
continue;
|
]) else {
|
||||||
};
|
continue;
|
||||||
entries.segments.push(Entry3 {
|
};
|
||||||
aabb,
|
entries.segments.push(Entry3 {
|
||||||
value: SegmentRef {
|
aabb,
|
||||||
wire: wire_idx,
|
value: SegmentRef {
|
||||||
start: start as u32,
|
wire: wire_idx,
|
||||||
},
|
start: start as u32,
|
||||||
});
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for (index, (point, _)) in wire.snap_pts.iter().enumerate() {
|
for (index, (point, _)) in wire.snap_pts.iter().enumerate() {
|
||||||
if point.is_finite() {
|
if point.is_finite() {
|
||||||
|
|
@ -773,7 +783,9 @@ impl InteractionIndex {
|
||||||
let collect_started = iced::time::Instant::now();
|
let collect_started = iced::time::Instant::now();
|
||||||
let mut wire_entries = Vec::with_capacity(wires.len());
|
let mut wire_entries = Vec::with_capacity(wires.len());
|
||||||
let mut unbounded_wires = Vec::new();
|
let mut unbounded_wires = Vec::new();
|
||||||
|
let mut screen_unbounded_wires = Vec::new();
|
||||||
let mut max_line_half_width_px = 0.0f32;
|
let mut max_line_half_width_px = 0.0f32;
|
||||||
|
let mut max_marker_radius_fraction = 0.0f32;
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
let per_wire: Vec<WireIndexEntries> = {
|
let per_wire: Vec<WireIndexEntries> = {
|
||||||
|
|
@ -804,12 +816,17 @@ impl InteractionIndex {
|
||||||
for (wire_idx, entries) in per_wire.into_iter().enumerate() {
|
for (wire_idx, entries) in per_wire.into_iter().enumerate() {
|
||||||
max_line_half_width_px =
|
max_line_half_width_px =
|
||||||
max_line_half_width_px.max(entries.max_line_half_width_px);
|
max_line_half_width_px.max(entries.max_line_half_width_px);
|
||||||
|
max_marker_radius_fraction =
|
||||||
|
max_marker_radius_fraction.max(entries.max_marker_radius_fraction);
|
||||||
if let Some(entry) = entries.wire {
|
if let Some(entry) = entries.wire {
|
||||||
wire_entries.push(entry);
|
wire_entries.push(entry);
|
||||||
}
|
}
|
||||||
if entries.unbounded {
|
if entries.unbounded {
|
||||||
unbounded_wires.push(wire_idx as u32);
|
unbounded_wires.push(wire_idx as u32);
|
||||||
}
|
}
|
||||||
|
if entries.screen_unbounded {
|
||||||
|
screen_unbounded_wires.push(wire_idx as u32);
|
||||||
|
}
|
||||||
segment_parts.push(entries.segments);
|
segment_parts.push(entries.segments);
|
||||||
snap_point_parts.push(entries.snap_points);
|
snap_point_parts.push(entries.snap_points);
|
||||||
key_vertex_parts.push(entries.key_vertices);
|
key_vertex_parts.push(entries.key_vertices);
|
||||||
|
|
@ -952,7 +969,9 @@ impl InteractionIndex {
|
||||||
pick_triangles,
|
pick_triangles,
|
||||||
glyphs,
|
glyphs,
|
||||||
unbounded_wires,
|
unbounded_wires,
|
||||||
|
screen_unbounded_wires,
|
||||||
max_line_half_width_px,
|
max_line_half_width_px,
|
||||||
|
max_marker_radius_fraction,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1002,12 +1021,30 @@ impl InteractionIndex {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pick_radius_px(&self, base_radius_px: f32) -> f32 {
|
pub fn pick_radius_px(
|
||||||
base_radius_px.max(self.max_line_half_width_px)
|
&self,
|
||||||
|
base_radius_px: f32,
|
||||||
|
viewport_height_px: f32,
|
||||||
|
include_line_weight: bool,
|
||||||
|
) -> f32 {
|
||||||
|
let radius = base_radius_px
|
||||||
|
.max(self.max_marker_radius_fraction * viewport_height_px.max(0.0));
|
||||||
|
if include_line_weight {
|
||||||
|
radius.max(self.max_line_half_width_px)
|
||||||
|
} else {
|
||||||
|
radius
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn queried_wire_keys(&self, mut indices: Vec<u32>) -> Vec<(u64, u32)> {
|
fn queried_wire_keys(
|
||||||
|
&self,
|
||||||
|
mut indices: Vec<u32>,
|
||||||
|
include_screen_unbounded: bool,
|
||||||
|
) -> Vec<(u64, u32)> {
|
||||||
indices.extend_from_slice(&self.unbounded_wires);
|
indices.extend_from_slice(&self.unbounded_wires);
|
||||||
|
if include_screen_unbounded {
|
||||||
|
indices.extend_from_slice(&self.screen_unbounded_wires);
|
||||||
|
}
|
||||||
let mut keys: Vec<(u64, u32)> = indices
|
let mut keys: Vec<(u64, u32)> = indices
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|index| {
|
.filter_map(|index| {
|
||||||
|
|
@ -1029,7 +1066,7 @@ impl InteractionIndex {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn query_wire_keys_xy(&self, aabb: [f64; 4]) -> Vec<(u64, u32)> {
|
pub fn query_wire_keys_xy(&self, aabb: [f64; 4]) -> Vec<(u64, u32)> {
|
||||||
self.queried_wire_keys(self.wires.query_xy(aabb))
|
self.queried_wire_keys(self.wires.query_xy(aabb), false)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn query_wire_keys_screen(
|
pub fn query_wire_keys_screen(
|
||||||
|
|
@ -1039,7 +1076,10 @@ impl InteractionIndex {
|
||||||
eye: DVec3,
|
eye: DVec3,
|
||||||
bounds: Rectangle,
|
bounds: Rectangle,
|
||||||
) -> Vec<(u64, u32)> {
|
) -> Vec<(u64, u32)> {
|
||||||
self.queried_wire_keys(self.wires.query_screen(screen_rect, view_rot, eye, bounds))
|
self.queried_wire_keys(
|
||||||
|
self.wires.query_screen(screen_rect, view_rot, eye, bounds),
|
||||||
|
true,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remap_wire_index(
|
fn remap_wire_index(
|
||||||
|
|
@ -1064,8 +1104,12 @@ impl InteractionIndex {
|
||||||
&self,
|
&self,
|
||||||
mut indices: Vec<u32>,
|
mut indices: Vec<u32>,
|
||||||
slots: &rustc_hash::FxHashMap<(u64, u32), u32>,
|
slots: &rustc_hash::FxHashMap<(u64, u32), u32>,
|
||||||
|
include_screen_unbounded: bool,
|
||||||
) -> Vec<u32> {
|
) -> Vec<u32> {
|
||||||
indices.extend_from_slice(&self.unbounded_wires);
|
indices.extend_from_slice(&self.unbounded_wires);
|
||||||
|
if include_screen_unbounded {
|
||||||
|
indices.extend_from_slice(&self.screen_unbounded_wires);
|
||||||
|
}
|
||||||
let mut local: Vec<u32> = indices
|
let mut local: Vec<u32> = indices
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|index| self.remap_wire_index(index, slots))
|
.filter_map(|index| self.remap_wire_index(index, slots))
|
||||||
|
|
@ -1100,7 +1144,11 @@ impl InteractionIndex {
|
||||||
) -> InteractionCandidates {
|
) -> InteractionCandidates {
|
||||||
InteractionCandidates {
|
InteractionCandidates {
|
||||||
wires,
|
wires,
|
||||||
wire_indices: Some(self.remap_wire_indices(self.wires.query_xy(aabb), slots)),
|
wire_indices: Some(self.remap_wire_indices(
|
||||||
|
self.wires.query_xy(aabb),
|
||||||
|
slots,
|
||||||
|
false,
|
||||||
|
)),
|
||||||
segments: Some(self.remap_refs(
|
segments: Some(self.remap_refs(
|
||||||
self.segments.query_xy(aabb),
|
self.segments.query_xy(aabb),
|
||||||
slots,
|
slots,
|
||||||
|
|
@ -1164,6 +1212,7 @@ impl InteractionIndex {
|
||||||
self.wires
|
self.wires
|
||||||
.query_screen(screen_rect, view_rot, eye, bounds),
|
.query_screen(screen_rect, view_rot, eye, bounds),
|
||||||
slots,
|
slots,
|
||||||
|
true,
|
||||||
)),
|
)),
|
||||||
segments: Some(self.remap_refs(
|
segments: Some(self.remap_refs(
|
||||||
self.segments
|
self.segments
|
||||||
|
|
@ -1251,6 +1300,7 @@ impl InteractionIndex {
|
||||||
) -> InteractionCandidates {
|
) -> InteractionCandidates {
|
||||||
let mut wire_indices = self.wires.query_screen(screen_rect, view_rot, eye, bounds);
|
let mut wire_indices = self.wires.query_screen(screen_rect, view_rot, eye, bounds);
|
||||||
wire_indices.extend_from_slice(&self.unbounded_wires);
|
wire_indices.extend_from_slice(&self.unbounded_wires);
|
||||||
|
wire_indices.extend_from_slice(&self.screen_unbounded_wires);
|
||||||
wire_indices.sort_unstable();
|
wire_indices.sort_unstable();
|
||||||
wire_indices.dedup();
|
wire_indices.dedup();
|
||||||
InteractionCandidates {
|
InteractionCandidates {
|
||||||
|
|
@ -1601,6 +1651,26 @@ impl<'a> Iterator for WireIter<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finite_wire_aabb3(wire: &WireModel) -> Option<[f64; 6]> {
|
fn finite_wire_aabb3(wire: &WireModel) -> Option<[f64; 6]> {
|
||||||
|
if let Some(marker) = wire.point_marker {
|
||||||
|
if !marker.origin.is_finite() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let normal = marker.normal.try_normalize().unwrap_or(DVec3::Z);
|
||||||
|
let mut min_axial = 0.0f64;
|
||||||
|
let mut max_axial = 0.0f64;
|
||||||
|
for index in 0..wire.points.len() {
|
||||||
|
let point = DVec3::from_array(wire_point(wire, index));
|
||||||
|
if point.is_finite() {
|
||||||
|
let axial = (point - marker.origin).dot(normal);
|
||||||
|
min_axial = min_axial.min(axial);
|
||||||
|
max_axial = max_axial.max(axial);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return points_aabb3([
|
||||||
|
(marker.origin + normal * min_axial).to_array(),
|
||||||
|
(marker.origin + normal * max_axial).to_array(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
let [min_x, min_y, max_x, max_y] = wire.aabb;
|
let [min_x, min_y, max_x, max_y] = wire.aabb;
|
||||||
if !min_x.is_finite() || !min_y.is_finite() || !max_x.is_finite() || !max_y.is_finite() {
|
if !min_x.is_finite() || !min_y.is_finite() || !max_x.is_finite() || !max_y.is_finite() {
|
||||||
return None;
|
return None;
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ use acadrust::entities::Insert;
|
||||||
use acadrust::objects::{ObjectType, SpatialFilter};
|
use acadrust::objects::{ObjectType, SpatialFilter};
|
||||||
use acadrust::types::{Handle, Transform, Vector3};
|
use acadrust::types::{Handle, Transform, Vector3};
|
||||||
use acadrust::CadDocument;
|
use acadrust::CadDocument;
|
||||||
|
use cadkernel::geom2d::{contains, Curve, Line, Tolerance};
|
||||||
|
|
||||||
use crate::scene::model::wire_model::WireModel;
|
use crate::scene::model::wire_model::WireModel;
|
||||||
|
|
||||||
|
|
@ -116,6 +117,12 @@ pub fn clip_wires(wires: &mut Vec<WireModel>, poly: &[[f64; 2]]) {
|
||||||
.iter()
|
.iter()
|
||||||
.map(|&[x, y]| [(x - rx) as f32, (y - ry) as f32])
|
.map(|&[x, y]| [(x - rx) as f32, (y - ry) as f32])
|
||||||
.collect();
|
.collect();
|
||||||
|
let boundary: Vec<Curve> = poly
|
||||||
|
.iter()
|
||||||
|
.zip(poly.iter().cycle().skip(1))
|
||||||
|
.take(poly.len())
|
||||||
|
.map(|(&start, &end)| Curve::Line(Line { start, end }))
|
||||||
|
.collect();
|
||||||
|
|
||||||
// Reconstruct an absolute-f64 wire point from its high/low pair, NaN-safe.
|
// Reconstruct an absolute-f64 wire point from its high/low pair, NaN-safe.
|
||||||
let abs = |hi: [f32; 3], lo: [f32; 3]| -> [f64; 3] {
|
let abs = |hi: [f32; 3], lo: [f32; 3]| -> [f64; 3] {
|
||||||
|
|
@ -127,7 +134,18 @@ pub fn clip_wires(wires: &mut Vec<WireModel>, poly: &[[f64; 2]]) {
|
||||||
};
|
};
|
||||||
|
|
||||||
for w in wires.iter_mut() {
|
for w in wires.iter_mut() {
|
||||||
if !w.points.is_empty() {
|
let keep_marker = w.point_marker.map(|marker| {
|
||||||
|
contains(
|
||||||
|
&boundary,
|
||||||
|
[marker.origin.x, marker.origin.y],
|
||||||
|
Tolerance::default(),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
if keep_marker == Some(false) {
|
||||||
|
w.points.clear();
|
||||||
|
w.points_low.clear();
|
||||||
|
w.point_marker = None;
|
||||||
|
} else if keep_marker.is_none() && !w.points.is_empty() {
|
||||||
// Absolute → local f32 (NaN separators preserved).
|
// Absolute → local f32 (NaN separators preserved).
|
||||||
let local: Vec<[f32; 3]> = (0..w.points.len())
|
let local: Vec<[f32; 3]> = (0..w.points.len())
|
||||||
.map(|i| {
|
.map(|i| {
|
||||||
|
|
@ -257,6 +275,7 @@ pub fn frame_wire(
|
||||||
lo.push([lx, ly, 0.0]);
|
lo.push([lx, ly, 0.0]);
|
||||||
}
|
}
|
||||||
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,
|
||||||
|
|
|
||||||
|
|
@ -521,29 +521,18 @@ fn finite3(p: [f32; 3]) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn marker_metadata(wire: &WireModel) -> ([f32; 4], [f32; 4], [f32; 4]) {
|
fn marker_metadata(wire: &WireModel) -> ([f32; 4], [f32; 4], [f32; 4]) {
|
||||||
if wire.world_width >= 0.0 {
|
let Some(marker) = wire.point_marker else {
|
||||||
return ([0.0; 4], [0.0; 4], [0.0; 4]);
|
return ([0.0; 4], [0.0; 4], [0.0; 4]);
|
||||||
}
|
};
|
||||||
let origin = wire
|
let origin = marker.origin;
|
||||||
.snap_pts
|
|
||||||
.iter()
|
|
||||||
.find_map(|(point, hint)| {
|
|
||||||
matches!(hint, crate::scene::model::wire_model::SnapHint::Node).then_some(*point)
|
|
||||||
})
|
|
||||||
.unwrap_or(glam::DVec3::ZERO);
|
|
||||||
let (hx, lx) = WireModel::split_ds(origin.x);
|
let (hx, lx) = WireModel::split_ds(origin.x);
|
||||||
let (hy, ly) = WireModel::split_ds(origin.y);
|
let (hy, ly) = WireModel::split_ds(origin.y);
|
||||||
let (hz, lz) = WireModel::split_ds(origin.z);
|
let (hz, lz) = WireModel::split_ds(origin.z);
|
||||||
let normal = glam::Vec3::new(
|
let normal = marker.normal.as_vec3().normalize_or(glam::Vec3::Z);
|
||||||
wire.taper_widths.first().copied().unwrap_or(0.0),
|
|
||||||
wire.taper_widths.get(1).copied().unwrap_or(0.0),
|
|
||||||
wire.taper_widths.get(2).copied().unwrap_or(1.0),
|
|
||||||
)
|
|
||||||
.normalize_or(glam::Vec3::Z);
|
|
||||||
(
|
(
|
||||||
[hx, hy, hz, 0.0],
|
[hx, hy, hz, 0.0],
|
||||||
[lx, ly, lz, 0.0],
|
[lx, ly, lz, 0.0],
|
||||||
[normal.x, normal.y, normal.z, -wire.world_width],
|
[normal.x, normal.y, normal.z, marker.viewport_percent],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -565,13 +554,7 @@ pub(crate) fn emit_wire_packed(
|
||||||
let (dists, align_end, align_total) = wire_distances(wire);
|
let (dists, align_end, align_total) = wire_distances(wire);
|
||||||
let (marker_origin_high, marker_origin_low, marker_normal_scale) = marker_metadata(wire);
|
let (marker_origin_high, marker_origin_low, marker_normal_scale) = marker_metadata(wire);
|
||||||
let low = |i: usize| -> [f32; 3] { wire.points_low.get(i).copied().unwrap_or([0.0; 3]) };
|
let low = |i: usize| -> [f32; 3] { wire.points_low.get(i).copied().unwrap_or([0.0; 3]) };
|
||||||
let tw = |i: usize| -> f32 {
|
let tw = |i: usize| -> f32 { wire.taper_widths.get(i).copied().unwrap_or(0.0) * 0.5 };
|
||||||
if wire.world_width < 0.0 {
|
|
||||||
0.0
|
|
||||||
} else {
|
|
||||||
wire.taper_widths.get(i).copied().unwrap_or(0.0) * 0.5
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let mut instances: Vec<PackedWireInstance> = Vec::with_capacity(seg_count);
|
let mut instances: Vec<PackedWireInstance> = Vec::with_capacity(seg_count);
|
||||||
for i in 0..seg_count {
|
for i in 0..seg_count {
|
||||||
let a = wire.points[i];
|
let a = wire.points[i];
|
||||||
|
|
@ -594,7 +577,7 @@ pub(crate) fn emit_wire_packed(
|
||||||
draw_depth,
|
draw_depth,
|
||||||
align_end,
|
align_end,
|
||||||
align_total,
|
align_total,
|
||||||
world_half_width: wire.world_width.max(0.0) * 0.5,
|
world_half_width: wire.world_width * 0.5,
|
||||||
world_hw_a: tw(i),
|
world_hw_a: tw(i),
|
||||||
world_hw_b: tw(i + 1),
|
world_hw_b: tw(i + 1),
|
||||||
marker_origin_high,
|
marker_origin_high,
|
||||||
|
|
@ -624,7 +607,7 @@ pub(crate) fn emit_wire_native(
|
||||||
draw_depth,
|
draw_depth,
|
||||||
align_end,
|
align_end,
|
||||||
align_total,
|
align_total,
|
||||||
world_half_width: wire.world_width.max(0.0) * 0.5,
|
world_half_width: wire.world_width * 0.5,
|
||||||
_pad1: 0.0,
|
_pad1: 0.0,
|
||||||
_pad2: 0.0,
|
_pad2: 0.0,
|
||||||
marker_origin_high,
|
marker_origin_high,
|
||||||
|
|
|
||||||
|
|
@ -160,6 +160,22 @@ impl Scene {
|
||||||
let in_vp = |x: f32, y: f32| x >= vp_x0 && x <= vp_x1 && y >= vp_y0 && y <= vp_y1;
|
let in_vp = |x: f32, y: f32| x >= vp_x0 && x <= vp_x1 && y >= vp_y0 && y <= vp_y1;
|
||||||
|
|
||||||
for wire in model_wires.iter() {
|
for wire in model_wires.iter() {
|
||||||
|
let marker_view_height = wire.point_marker.map_or(view_height_eff, |marker| {
|
||||||
|
if !use_perspective {
|
||||||
|
return view_height_eff;
|
||||||
|
}
|
||||||
|
let relative = marker.origin
|
||||||
|
- glam::DVec3::new(
|
||||||
|
display_center_x,
|
||||||
|
display_center_y,
|
||||||
|
display_center_z,
|
||||||
|
);
|
||||||
|
let depth = relative.x * view_fwd_d.0
|
||||||
|
+ relative.y * view_fwd_d.1
|
||||||
|
+ relative.z * view_fwd_d.2;
|
||||||
|
view_height_eff * (camera_dist_d - depth).max(0.001)
|
||||||
|
/ camera_dist_d.max(0.001)
|
||||||
|
});
|
||||||
let projected_pts: Vec<[f32; 3]> = wire
|
let projected_pts: Vec<[f32; 3]> = wire
|
||||||
.points
|
.points
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -168,11 +184,8 @@ impl Scene {
|
||||||
if mx.is_nan() || my.is_nan() || mz.is_nan() {
|
if mx.is_nan() || my.is_nan() || mz.is_nan() {
|
||||||
return [f32::NAN; 3];
|
return [f32::NAN; 3];
|
||||||
}
|
}
|
||||||
// Reconstruct absolute WCS from the double-single high
|
let point = wire.point_world(pi, marker_view_height);
|
||||||
// (`points`) + low (`points_low`) pair — the high f32
|
proj_abs(point.x, point.y, point.z)
|
||||||
// alone is ~0.5 m off at UTM scale.
|
|
||||||
let lo = wire.points_low.get(pi).copied().unwrap_or([0.0; 3]);
|
|
||||||
proj_abs(mx as f64 + lo[0] as f64, my as f64 + lo[1] as f64, mz as f64 + lo[2] as f64)
|
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
|
@ -304,6 +317,7 @@ impl Scene {
|
||||||
// residual is needed, and keeping the model wire's points_low
|
// residual is needed, and keeping the model wire's points_low
|
||||||
// here would add a model-scale offset to the paper points.
|
// here would add a model-scale offset to the paper points.
|
||||||
out.points_low = Vec::new();
|
out.points_low = Vec::new();
|
||||||
|
out.point_marker = None;
|
||||||
out.snap_pts = snap_pts;
|
out.snap_pts = snap_pts;
|
||||||
out.key_vertices = key_vertices;
|
out.key_vertices = key_vertices;
|
||||||
// Tangent geometry is in model space and can't be trivially
|
// Tangent geometry is in model space and can't be trivially
|
||||||
|
|
|
||||||
|
|
@ -309,6 +309,7 @@ pub fn apply_along(
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|s| s.len() >= 2)
|
.filter(|s| s.len() >= 2)
|
||||||
.map(|pts| WireModel {
|
.map(|pts| 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,
|
||||||
|
|
@ -355,6 +356,7 @@ pub fn apply_along(
|
||||||
xy = xy.max(y);
|
xy = xy.max(y);
|
||||||
}
|
}
|
||||||
out.push(WireModel {
|
out.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,
|
||||||
|
|
|
||||||
|
|
@ -88,8 +88,14 @@ fn marker_relative(
|
||||||
let normal = normalize(wire_const.marker_normal_scale.xyz);
|
let normal = normalize(wire_const.marker_normal_scale.xyz);
|
||||||
let axial = normal * dot(delta, normal);
|
let axial = normal * dot(delta, normal);
|
||||||
let planar = delta - axial;
|
let planar = delta - axial;
|
||||||
let world_size = wire_const.marker_normal_scale.w * 0.01
|
let origin_clip = u.view_rot * vec4<f32>(origin_relative, 1.0);
|
||||||
* u.world_per_pixel * u.viewport_size.y;
|
let projection_scale = max(length(vec3<f32>(
|
||||||
|
u.view_rot[0].y,
|
||||||
|
u.view_rot[1].y,
|
||||||
|
u.view_rot[2].y,
|
||||||
|
)), 1e-12);
|
||||||
|
let view_height = 2.0 * max(abs(origin_clip.w), 1e-6) / projection_scale;
|
||||||
|
let world_size = wire_const.marker_normal_scale.w * 0.01 * view_height;
|
||||||
return origin_relative + axial + planar * world_size;
|
return origin_relative + axial + planar * world_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -116,7 +116,14 @@ fn marker_relative(position_high: vec3<f32>, position_low: vec3<f32>, instance:
|
||||||
let normal = normalize(instance.marker_normal_scale.xyz);
|
let normal = normalize(instance.marker_normal_scale.xyz);
|
||||||
let axial = normal * dot(delta, normal);
|
let axial = normal * dot(delta, normal);
|
||||||
let planar = delta - axial;
|
let planar = delta - axial;
|
||||||
let world_size = instance.marker_normal_scale.w * 0.01 * u.world_per_pixel * u.viewport_size.y;
|
let origin_clip = u.view_rot * vec4<f32>(origin_relative, 1.0);
|
||||||
|
let projection_scale = max(length(vec3<f32>(
|
||||||
|
u.view_rot[0].y,
|
||||||
|
u.view_rot[1].y,
|
||||||
|
u.view_rot[2].y,
|
||||||
|
)), 1e-12);
|
||||||
|
let view_height = 2.0 * max(abs(origin_clip.w), 1e-6) / projection_scale;
|
||||||
|
let world_size = instance.marker_normal_scale.w * 0.01 * view_height;
|
||||||
return origin_relative + axial + planar * world_size;
|
return origin_relative + axial + planar * world_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,14 @@ fn marker_relative(position_high: vec3<f32>, position_low: vec3<f32>, c: WireCon
|
||||||
let normal = normalize(c.marker_normal_scale.xyz);
|
let normal = normalize(c.marker_normal_scale.xyz);
|
||||||
let axial = normal * dot(delta, normal);
|
let axial = normal * dot(delta, normal);
|
||||||
let planar = delta - axial;
|
let planar = delta - axial;
|
||||||
let world_size = c.marker_normal_scale.w * 0.01 * u.world_per_pixel * u.viewport_size.y;
|
let origin_clip = u.view_rot * vec4<f32>(origin_relative, 1.0);
|
||||||
|
let projection_scale = max(length(vec3<f32>(
|
||||||
|
u.view_rot[0].y,
|
||||||
|
u.view_rot[1].y,
|
||||||
|
u.view_rot[2].y,
|
||||||
|
)), 1e-12);
|
||||||
|
let view_height = 2.0 * max(abs(origin_clip.w), 1e-6) / projection_scale;
|
||||||
|
let world_size = c.marker_normal_scale.w * 0.01 * view_height;
|
||||||
return origin_relative + axial + planar * world_size;
|
return origin_relative + axial + planar * world_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue