code wiki / _hdl_build / nx_facs.nx
nx_facs.nx
buildroot/runtime/_hdl_build/nx_facs.nx
about
nx_facs.nx -- ★FACIAL ACTION CODING AS DATA: the expression basis, separated from identity.
★THE DEFECT THIS FIXES, and this codebase has cured it once already. nx_anatstack drives expressions
from `AS_EXPR`, a SINGLE ENUM (0 neutral, 1 smile, 2 surprise, 3 frown) fed to an if-chain of
hardcoded part indices and magnitudes. Two consequences, and the second is structural:
(a) expressions are CODE, not data -- adding one means editing a function;
(b) ★★ONLY ONE EXPRESSION CAN EXIST AT A TIME. You cannot smile AND raise a brow. A real face is
always a COMBINATION, so the enum cannot represent even ordinary human faces.
★nx_skelgen already wrote the cure for exactly this shape: "THE BODY PLAN AS DATA -- until now the
plan WAS the code: a chain of if b == n ... that works for exactly one species and one subject."
Same disease, same fix: a TABLE.
★★★AND THE RIGHT TABLE IS FACS, BECAUSE OUR EXPRESSIONS ARE MUSCLE-DRIVEN. An Action Unit IS a named
muscle action (AU12 = zygomaticus major, AU4 = corrugator), so for an anatomically-driven stack the
standard basis and the physical basis are THE SAME THING. Google's GNM spends 150 statistical
parameters on the lower face; a causal system needs ~30 named muscle actions to span the same space,
because each one means something. **That is our exceed axis: expression by CAUSE, not by fit.**
SEPARATION OF CONCERNS, which is GNM's first architectural idea: IDENTITY is the anthropometric
parameter set (who the face is); EXPRESSION is an AU WEIGHT VECTOR (what it is doing). They no
longer share a representation and can be solved independently.
nx_facs selftest
license_tier: ORIGINAL expect_exit: 0 No hw writes (Rule 26).
dependencies 2 imports · 0 importers
imports: nx_anatstack.nxnx_gate_verdict.nx
imported by: nobody (leaf or entry point)
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 29 | const FA_SHIFTY: i64 = 0 |
| 30 | const FA_SHIFTZ: i64 = 1 |
| 31 | const FA_GROWX: i64 = 2 |
| 32 | const FA_GROWY: i64 = 3 |
| 34 | const AU1: i64 = 1 // inner brow raiser -- frontalis, medial |
| 35 | const AU2: i64 = 2 // outer brow raiser -- frontalis, lateral |
| 36 | const AU4: i64 = 4 // brow lowerer -- corrugator supercilii |
| 37 | const AU5: i64 = 5 // upper lid raiser -- levator palpebrae |
| 38 | const AU6: i64 = 6 // cheek raiser -- orbicularis oculi, pars orbitalis |
| 39 | const AU12: i64 = 12 // lip corner puller -- zygomaticus major |
| 40 | const AU15: i64 = 15 // lip corner depressor-- depressor anguli oris |
| 41 | const AU26: i64 = 26 // jaw drop -- masseter relaxation / lateral pterygoid |
| 42 | const FA_MAXAU: i64 = 64 // weight slots, indexed by FACS number |
| 43 | const FA_NEFF: i64 = 21 // rows in the effect table |
| 44 | const FA_ES: i64 = 4 // effect stride: au, part, channel, amount-at-full-intensity |
| 45 | const FA_FULL: i64 = 1024 |
| 114 | const FA_NP: i64 = 64 |
functions
| 50 | func fa_table(E: *i64) -> i64 called by 1: main |
| 82 | func fa_clear(W: *i64) -> i64 { var i: i64 = 0; while i < FA_MAXAU { W[i] = 0; i = i + 1 } return 0 } called by 1: main |
| 83 | func fa_set(W: *i64, au: i64, w: i64) -> i64 |
| 91 | func fa_apply(P: *i64, W: *i64, E: *i64) -> i64 |
| 110 | func fa_smile(W: *i64, t: i64) -> i64 { fa_set(W,AU6,t); fa_set(W,AU12,t); return 0 } |
| 111 | func fa_surprise(W: *i64, t: i64) -> i64 { fa_set(W,AU1,t); fa_set(W,AU2,t); fa_set(W,AU5,t); fa_set(W,AU26,t); return 0 } |
| 112 | func fa_frown(W: *i64, t: i64) -> i64 { fa_set(W,AU4,t); fa_set(W,AU15,t); return 0 } |
| 116 | func fa_snap(P: *i64, np: i64, out: *i64) -> i64 called by 1: main |
| 134 | func fa_diff(a: *i64, b: *i64, n: i64) -> i64 called by 1: main |
| 141 | func main(argc: i64, argv: *i64) -> i64 |