nx_uv_quality_candidate_t282.nx source
↩ module page · 77 lines · 6130 B
1// nx_uv_quality_candidate_t282.nx -- Evaluates UV quality metrics for triangles in a mesh, tracking orientation, area, and anisotropy.
2import "nx_uv_gram_solver_candidate_t280.nx"
3// Additive candidate for nx_uvunwrap_lib's distortion referee. Source
4// identity validation belongs to the input adapter, numerical coverage here.
5struct UvQuality{
6 stage:i64,triangles:i64,positive:i64,negative:i64,collapsed:i64,unknown:i64,
7 maxAnisotropy:f64,worstTriangle:i64,minPositiveArea:f64
8}
9struct UvQualityPolicy{
10 expectedTriangles:i64,expectedOrientation:i64,maxCollapsed:i64,maxReversed:i64,maxAnisotropy:f64
11}
12const UVQ_PASS:i64=0
13const UVQ_FAIL:i64=1
14const UVQ_UNKNOWN:i64=2
15const UVQ_INVALID:i64=3
16// IEEE754 binary64 unit roundoff. This is the format, not a quality policy.
17const UVQ_ROUNDOFF:f64=0.00000000000000011102230246251565404236316680908203125
18// 2*(2^31-1)^2 fits signed i64, certifying integer determinant arithmetic.
19const UVQ_DET_EDGE:i64=2147483647
20func uvq_abs(x:f64)->f64{if x<0.0{return 0.0-x};return x}
21func uvq_subnormal(x:f64)->i64{let bits:*i64=(&x) as *i64;if x==0.0{return 0};if (bits[0]&0x7ff0000000000000)==0{return 1};return 0}
22func uvq_reset(q:*UvQuality,stage:i64)->i64{
23 if q==(0 as *UvQuality){return UVQ_INVALID};q.stage=stage;q.triangles=0;q.positive=0;q.negative=0;q.collapsed=0;q.unknown=0;q.maxAnisotropy=0.0;q.worstTriangle=0-1;q.minPositiveArea=0.0;return UVQ_PASS
24}
25// uv contains [u0,v0,u1,v1,u2,v2], xyz three source vertices.
26// Integer mode is allowed only for exact integer-valued doubles supplied
27// by a validated i64 adapter. Ambiguous floating orientation stays unknown.
28func uvq_triangle(q:*UvQuality,xyz:*f64,uv:*f64,integerMode:i64,sourceTriangle:i64)->i64{
29 if q==(0 as *UvQuality){return UVQ_INVALID};if xyz==(0 as *f64){return UVQ_INVALID};if uv==(0 as *f64){return UVQ_INVALID}
30 if integerMode!=0{if integerMode!=1{return UVQ_INVALID}}
31 q.triangles=q.triangles+1;var k:i64=0;while k<9{if uls_finite(xyz[k])==0{q.unknown=q.unknown+1;return UVQ_UNKNOWN};k=k+1};k=0;while k<6{if uls_finite(uv[k])==0{q.unknown=q.unknown+1;return UVQ_UNKNOWN};k=k+1}
32 let du:f64=uv[2]-uv[0];let dv:f64=uv[3]-uv[1];let eu:f64=uv[4]-uv[0];let ev:f64=uv[5]-uv[1]
33 var area:f64=du*ev-dv*eu;var orientation:i64=0
34 if integerMode==1{
35 k=0;while k<6{if uvq_abs(uv[k])>9007199254740992.0{q.unknown=q.unknown+1;return UVQ_UNKNOWN};if __f64_from_i64(__f64_to_i64(uv[k]))!=uv[k]{q.unknown=q.unknown+1;return UVQ_UNKNOWN};k=k+1}
36 let bound:f64=__f64_from_i64(UVQ_DET_EDGE)
37 if uvq_abs(du)>bound{q.unknown=q.unknown+1;return UVQ_UNKNOWN};if uvq_abs(dv)>bound{q.unknown=q.unknown+1;return UVQ_UNKNOWN};if uvq_abs(eu)>bound{q.unknown=q.unknown+1;return UVQ_UNKNOWN};if uvq_abs(ev)>bound{q.unknown=q.unknown+1;return UVQ_UNKNOWN}
38 let d:i64=__f64_to_i64(du)*__f64_to_i64(ev)-__f64_to_i64(dv)*__f64_to_i64(eu);area=__f64_from_i64(d);if d>0{orientation=1};if d<0{orientation=0-1}
39 }else{
40 let product0:f64=du*ev;let product1:f64=dv*eu
41 if product0==0.0{if du!=0.0{if ev!=0.0{q.unknown=q.unknown+1;return UVQ_UNKNOWN}}}
42 if product1==0.0{if dv!=0.0{if eu!=0.0{q.unknown=q.unknown+1;return UVQ_UNKNOWN}}}
43 if uvq_subnormal(product0)==1{q.unknown=q.unknown+1;return UVQ_UNKNOWN};if uvq_subnormal(product1)==1{q.unknown=q.unknown+1;return UVQ_UNKNOWN}
44 let err:f64=(3.0+16.0*UVQ_ROUNDOFF)*UVQ_ROUNDOFF*(uvq_abs(du*ev)+uvq_abs(dv*eu))
45 if err==0.0{if product0!=0.0{q.unknown=q.unknown+1;return UVQ_UNKNOWN};if product1!=0.0{q.unknown=q.unknown+1;return UVQ_UNKNOWN}}
46 if uls_finite(area)==0{q.unknown=q.unknown+1;return UVQ_UNKNOWN}
47 if uvq_abs(area)<=err{
48 if area==0.0{q.collapsed=q.collapsed+1};q.unknown=q.unknown+1;return UVQ_UNKNOWN
49 };if area>0.0{orientation=1}else{orientation=0-1}
50 }
51 if orientation==0{q.collapsed=q.collapsed+1;return UVQ_PASS}
52 if orientation>0{q.positive=q.positive+1}else{q.negative=q.negative+1}
53 let ax:f64=xyz[3]-xyz[0];let ay:f64=xyz[4]-xyz[1];let az:f64=xyz[5]-xyz[2]
54 let bx:f64=xyz[6]-xyz[0];let by:f64=xyz[7]-xyz[1];let bz:f64=xyz[8]-xyz[2]
55 let l:f64=uls_sqrt(ax*ax+ay*ay+az*az);let cx:f64=ay*bz-az*by;let cy:f64=az*bx-ax*bz;let cz:f64=ax*by-ay*bx;let a3:f64=uls_sqrt(cx*cx+cy*cy+cz*cz)
56 if l<=0.0{q.unknown=q.unknown+1;return UVQ_UNKNOWN};if a3<=0.0{q.unknown=q.unknown+1;return UVQ_UNKNOWN};if uls_finite(a3)==0{q.unknown=q.unknown+1;return UVQ_UNKNOWN}
57 let px:f64=(ax*bx+ay*by+az*bz)/l;let py:f64=a3/l
58 var j00:f64=du/l;var j10:f64=dv/l;var j01:f64=(eu-j00*px)/py;var j11:f64=(ev-j10*px)/py
59 var scale:f64=uvq_abs(j00);if uvq_abs(j10)>scale{scale=uvq_abs(j10)};if uvq_abs(j01)>scale{scale=uvq_abs(j01)};if uvq_abs(j11)>scale{scale=uvq_abs(j11)}
60 if scale<=0.0{q.unknown=q.unknown+1;return UVQ_UNKNOWN};if uls_finite(scale)==0{q.unknown=q.unknown+1;return UVQ_UNKNOWN}
61 j00=j00/scale;j10=j10/scale;j01=j01/scale;j11=j11/scale
62 let det:f64=uvq_abs(j00*j11-j01*j10);let trace:f64=j00*j00+j01*j01+j10*j10+j11*j11
63 if det<=0.0{q.unknown=q.unknown+1;return UVQ_UNKNOWN}
64 var disc:f64=trace*trace-4.0*det*det;if disc<0.0{disc=0.0}
65 var ratio:f64=(trace+uls_sqrt(disc))/(2.0*det);if ratio<1.0{ratio=1.0};if uls_finite(ratio)==0{q.unknown=q.unknown+1;return UVQ_UNKNOWN}
66 if ratio>q.maxAnisotropy{q.maxAnisotropy=ratio;q.worstTriangle=sourceTriangle}
67 if area>0.0{if q.minPositiveArea==0.0{q.minPositiveArea=area};if area<q.minPositiveArea{q.minPositiveArea=area}}
68 return UVQ_PASS
69}
70func uvq_qualify(q:*UvQuality,p:*UvQualityPolicy)->i64{
71 if q==(0 as *UvQuality){return UVQ_INVALID};if p==(0 as *UvQualityPolicy){return UVQ_INVALID}
72 if p.expectedTriangles<1{return UVQ_INVALID};if p.expectedOrientation!=1{if p.expectedOrientation!=(0-1){return UVQ_INVALID}}
73 if p.maxCollapsed<0{return UVQ_INVALID};if p.maxReversed<0{return UVQ_INVALID};if p.maxAnisotropy<1.0{return UVQ_INVALID};if uls_finite(p.maxAnisotropy)==0{return UVQ_INVALID}
74 if q.triangles!=p.expectedTriangles{return UVQ_UNKNOWN};if q.unknown>0{return UVQ_UNKNOWN}
75 if q.collapsed>p.maxCollapsed{return UVQ_FAIL};var reversed:i64=q.negative;if p.expectedOrientation<0{reversed=q.positive}
76 if reversed>p.maxReversed{return UVQ_FAIL};if q.maxAnisotropy>p.maxAnisotropy{return UVQ_FAIL};return UVQ_PASS
77}