nx_morf_eval_core.nx source
↩ module page · 144 lines · 6135 B
1// ORIGINAL. Pure MORF arithmetic extracted from the admitted mf_eval path.
2// No file parser, allocations, globals, hardware access or rendering policy.
3// The owner admits NXA through nxa_section_entry/nxa_counted_section and the
4// existing MORF shape/membership checks, then lends immutable arrays here.
5const MVC_I64_MAX: i64 = 9223372036854775807
6const MVC_WORD: i64 = 8
7const MVC_AXES: i64 = 3
8const MVC_E_SHAPE: i64 = 0 - 1
9const MVC_E_ALIAS: i64 = 0 - 2
10const MVC_E_PRODUCT: i64 = 0 - 3
11const MVC_E_SUM: i64 = 0 - 4
12
13struct MvcView {
14 bind: *i64
15 bind_words: i64
16 face: *i64
17 face_words: i64
18 delta: *i64
19 delta_words: i64
20 vertices: i64
21 faces: i64
22 channels: i64
23 unit: i64
24}
25
26// Representation checks precede address arithmetic. Empty borrowed slices may be null.
27func mvc_span(p: *i64, words: i64) -> i64 {
28 if words < 0 { return 0 }
29 if words == 0 { return 1 }
30 if words > MVC_I64_MAX/MVC_WORD { return 0 }
31 let address: i64 = p as i64
32 if address <= 0 { return 0 }
33 if address > MVC_I64_MAX-words*MVC_WORD { return 0 }
34 return 1
35}
36func mvc_overlap(a: *i64, an: i64, b: *i64, bn: i64) -> i64 {
37 if an == 0 { return 0 }; if bn == 0 { return 0 }
38 let ap: i64 = a as i64; let bp: i64 = b as i64
39 if ap < bp+bn*MVC_WORD { if bp < ap+an*MVC_WORD { return 1 } }
40 return 0
41}
42// These signed product branches preserve the current producer's exact admission.
43func mvc_product_ok(dv: i64, weight: i64) -> i64 {
44 let imax: i64 = MVC_I64_MAX; let imin: i64 = 0-imax-1
45 if dv > 0 {
46 if weight > 0 { if dv > imax/weight { return 0 } }
47 if weight < 0 { if weight < imin/dv { return 0 } }
48 }
49 if dv < 0 {
50 if weight > 0 { if dv < imin/weight { return 0 } }
51 if weight < 0 { if dv < imax/weight { return 0 } }
52 }
53 return 1
54}
55func mvc_sum_ok(old: i64, add: i64) -> i64 {
56 let imax: i64 = MVC_I64_MAX; let imin: i64 = 0-imax-1
57 if add > 0 { if old > imax-add { return 0 } }
58 if add < 0 { if old < imin-add { return 0 } }
59 return 1
60}
61
62// Caller retains ownership and must not mutate borrowed inputs during the call.
63// FACE uniqueness is established at asset admission, not re-scanned every frame.
64// Channels accumulate in declared order, truncating each contribution separately.
65// Return changed FACE vertex count, or a named negative error; errors never alter output.
66func mvc_apply(v: *MvcView, weights: *i64, weight_words: i64, out: *i64, out_words: i64) -> i64 {
67 if (v as i64) <= 0 { return MVC_E_SHAPE }
68 if v.vertices < 1 { return MVC_E_SHAPE }
69 if v.vertices > MVC_I64_MAX/MVC_AXES { return MVC_E_SHAPE }
70 let coords: i64 = v.vertices*MVC_AXES
71 if v.bind_words != coords { return MVC_E_SHAPE }
72 if out_words != coords { return MVC_E_SHAPE }
73 if v.faces < 0 { return MVC_E_SHAPE }; if v.faces > v.vertices { return MVC_E_SHAPE }
74 if v.face_words != v.faces { return MVC_E_SHAPE }
75 if v.channels < 0 { return MVC_E_SHAPE }
76 if weight_words != v.channels { return MVC_E_SHAPE }
77 if v.delta_words < 0 { return MVC_E_SHAPE }
78 if v.channels == 0 {
79 if v.faces != 0 { return MVC_E_SHAPE }
80 if v.delta_words != 0 { return MVC_E_SHAPE }
81 } else {
82 if v.faces == 0 { return MVC_E_SHAPE }
83 if v.unit <= 0 { return MVC_E_SHAPE }
84 if v.delta_words % v.channels != 0 { return MVC_E_SHAPE }
85 let per: i64 = v.delta_words/v.channels
86 if per % MVC_AXES != 0 { return MVC_E_SHAPE }
87 if per/MVC_AXES != v.faces { return MVC_E_SHAPE }
88 }
89 if mvc_span(v.bind,v.bind_words) == 0 { return MVC_E_SHAPE }
90 if mvc_span(v.face,v.face_words) == 0 { return MVC_E_SHAPE }
91 if mvc_span(v.delta,v.delta_words) == 0 { return MVC_E_SHAPE }
92 if mvc_span(weights,weight_words) == 0 { return MVC_E_SHAPE }
93 if mvc_span(out,out_words) == 0 { return MVC_E_SHAPE }
94 if mvc_overlap(out,out_words,v.bind,v.bind_words) == 1 { return MVC_E_ALIAS }
95 if mvc_overlap(out,out_words,v.face,v.face_words) == 1 { return MVC_E_ALIAS }
96 if mvc_overlap(out,out_words,v.delta,v.delta_words) == 1 { return MVC_E_ALIAS }
97 if mvc_overlap(out,out_words,weights,weight_words) == 1 { return MVC_E_ALIAS }
98 // The view itself is caller-owned too; output may not overwrite its pointer fields.
99 if mvc_overlap(out,out_words,v as *i64,__size_of(MvcView)/MVC_WORD) == 1 { return MVC_E_ALIAS }
100 var row: i64 = 0; var moved: i64 = 0
101 while row < v.faces {
102 let vi: i64 = v.face[row]
103 if vi < 0 { return MVC_E_SHAPE }; if vi >= v.vertices { return MVC_E_SHAPE }
104 var axis: i64 = 0; var changed: i64 = 0
105 while axis < MVC_AXES {
106 let original: i64 = v.bind[vi*MVC_AXES+axis]
107 var value: i64 = original; var ch: i64 = 0
108 while ch < v.channels {
109 let weight: i64 = weights[ch]
110 if weight != 0 {
111 let dv: i64 = v.delta[(ch*v.faces+row)*MVC_AXES+axis]
112 if mvc_product_ok(dv,weight) == 0 { return MVC_E_PRODUCT }
113 let add: i64 = dv*weight/v.unit
114 if mvc_sum_ok(value,add) == 0 { return MVC_E_SUM }
115 value = value+add
116 }
117 ch = ch+1
118 }
119 if value != original { changed = 1 }; axis = axis+1
120 }
121 if changed == 1 { moved = moved+1 }; row = row+1
122 }
123 // Commit only after all vertex/channel arithmetic has passed.
124 var at: i64 = 0
125 while at < coords { out[at] = v.bind[at]; at = at+1 }
126 row = 0
127 while row < v.faces {
128 let vi: i64 = v.face[row]; var axis: i64 = 0
129 while axis < MVC_AXES {
130 var value: i64 = v.bind[vi*MVC_AXES+axis]; var ch: i64 = 0
131 while ch < v.channels {
132 let weight: i64 = weights[ch]
133 if weight != 0 {
134 let dv: i64 = v.delta[(ch*v.faces+row)*MVC_AXES+axis]
135 value = value+dv*weight/v.unit
136 }
137 ch = ch+1
138 }
139 out[vi*MVC_AXES+axis] = value; axis = axis+1
140 }
141 row = row+1
142 }
143 return moved
144}