nx_editstack_lib.nx
buildroot/runtime/nx_editstack_lib.nx
about
nx_editstack_lib.nx -- THE NON-DESTRUCTIVE OPERATION STACK: record, undo, redo, replay, re-evaluate.
This is the spine every content-creation tool is built on (Blender's modifier stack, ZBrush's layers,
Houdini's graph) and the one thing this estate has never had. /compare/dcc DC1 and DC2.
CHECK-BEFORE-BUILD, and the incumbent was READ, not guessed. nx_spendgate named nx_editor_canvas.nx as
the nearest candidate and it is NOT this: it is a 2D page-layout canvas ([kind,x,y,w,h] elements --
heading, text, button, image) whose undo is ec_save/ec_undo, a ONE-DEEP FULL-ARRAY SNAPSHOT copied in
and out. That cannot redo, cannot replay, cannot re-evaluate a parameter at depth, and costs O(state)
per step. nx_siteedit_history.nx is file REVISION history, a different layer again. So the estate's only
existing undo is one-deep and snapshot-shaped; this is the log-shaped primitive underneath, and
nx_editor_canvas is a candidate consumer of it rather than a rival to it.
WHY UNDO IS REPLAY-FROM-BASE AND NOT AN INVERSE OP. Applying an inverse operation is how a float editor
loses precision: scale by 3/7 then by 7/3 does not return you to where you started, so undo DRIFTS and
the drift is invisible because nobody digests the state. Replaying the log from the base cannot drift,
by construction, and it is what makes the bit-exact claim on the /compare/dcc board checkable rather
than asserted. The cost is O(cells x ops) per undo, paid deliberately and named here so the next reader
does not mistake it for an oversight.
THE STATE IS AN i64 CELL VECTOR ON PURPOSE. A consumer maps its own data onto cells (a mesh maps vertex
components, the canvas above would map [kind,x,y,w,h]), so there is ONE stack in the estate rather than
one per subject. All integer: no float enters, which is why replay is bit-exact.
100% sovereign. No hardware writes (Rule 26). license_tier: ORIGINAL
dependencies 1 imports · 6 importers
imports: nx_syscalls.nx
imported by: nx_editor_canvas.nxnx_editor_canvas_gate.nxnx_editstack_gate.nxnx_texpaint_gate.nxnx_texpaint_lib.nxnx_vpick_lib.nx
structs
| none |
consts
| 31 | const ES_H_CAP: i64 = 0 |
| 32 | const ES_H_NCELL: i64 = 1 |
| 33 | const ES_H_COUNT: i64 = 2 |
| 34 | const ES_H_HEAD: i64 = 3 |
| 35 | const ES_H_ERR: i64 = 4 |
| 36 | const ES_HDR: i64 = 5 |
| 37 | const ES_REC: i64 = 5 // [kind, a0, a1, a2, a3] -- one word per field, each named below |
| 38 | const ES_I64_BYTES: i64 = 8 |
| 46 | const ES_F_KIND: i64 = 0 |
| 47 | const ES_F_A0: i64 = 1 |
| 48 | const ES_F_A1: i64 = 2 |
| 49 | const ES_F_A2: i64 = 3 |
| 50 | const ES_F_A3: i64 = 4 |
| 54 | const ES_OK: i64 = 0 |
| 55 | const ES_E_CELL: i64 = 0 - 1 |
| 56 | const ES_E_FULL: i64 = 0 - 2 |
| 57 | const ES_E_ATBASE: i64 = 0 - 3 |
| 58 | const ES_E_ATHEAD: i64 = 0 - 4 |
| 59 | const ES_E_DIV: i64 = 0 - 5 |
| 60 | const ES_E_KIND: i64 = 0 - 6 |
| 61 | const ES_E_IDX: i64 = 0 - 7 |
| 62 | const ES_E_RANGE: i64 = 0 - 8 |
| 63 | const ES_E_ARGS: i64 = 0 - 9 |
| 64 | const ES_E_RESERVED: i64 = 0 - 10 |
| 66 | const ES_OP_SET: i64 = 1 |
| 67 | const ES_OP_ADD: i64 = 2 |
| 68 | const ES_OP_SCALE: i64 = 3 // rational scale: cell = cell * a1 / a2, integer, a2 == 0 REFUSES |
| 69 | const ES_OP_RADD: i64 = 4 // range add over [a0, a1) |
| 77 | const ES_OP_CONSUMER_BASE: i64 = 1024 |
| 83 | const ES_HASH_MUL: i64 = 1099511628211 |
functions
| 85 | func es_live_off(st: *i64) -> i64 { return ES_HDR + st[ES_H_NCELL] } |
| 86 | func es_ops_off(st: *i64) -> i64 { return ES_HDR + st[ES_H_NCELL] + st[ES_H_NCELL] } |
| 88 | func es_ncells(st: *i64) -> i64 { return st[ES_H_NCELL] } |
| 89 | func es_cap(st: *i64) -> i64 { return st[ES_H_CAP] } |
| 90 | func es_count(st: *i64) -> i64 { return st[ES_H_COUNT] } |
| 91 | func es_head(st: *i64) -> i64 { return st[ES_H_HEAD] } |
| 92 | func es_err(st: *i64) -> i64 { return st[ES_H_ERR] } |
| 94 | func es_err_name(e: i64) -> *u8 |
| 109 | func es_words_for(cap_ops: i64, ncells: i64) -> i64 called by 1: es_new |
| 114 | func es_new(cap_ops: i64, ncells: i64) -> *i64 |
| 127 | func es_base_set(st: *i64, i: i64, v: i64) -> i64 |
| 137 | func es_cell(st: *i64, i: i64) -> i64 |
| 143 | func es_op_field(st: *i64, idx: i64, f: i64) -> i64 |
| 154 | func es_apply(st: *i64, kind: i64, a0: i64, a1: i64, a2: i64, a3: i64) -> i64 |
| 191 | func es_restore_base(st: *i64) -> i64 |
| 203 | func es_live_ptr(st: *i64) -> *i64 |
| 208 | func es_replay(st: *i64) -> i64 |
| 230 | func es_record(st: *i64, kind: i64, a0: i64, a1: i64, a2: i64, a3: i64) -> i64 |
| 244 | func es_push(st: *i64, kind: i64, a0: i64, a1: i64, a2: i64, a3: i64) -> i64 |
| 259 | func es_push_raw(st: *i64, kind: i64, a0: i64, a1: i64, a2: i64, a3: i64) -> i64 |
| 267 | func es_head_set(st: *i64, h: i64) -> i64 |
| 274 | func es_undo(st: *i64) -> i64 |
| 280 | func es_redo(st: *i64) -> i64 |
| 292 | func es_op_args_set(st: *i64, idx: i64, a0: i64, a1: i64, a2: i64, a3: i64) -> i64 |
| 303 | func es_reeval(st: *i64, idx: i64, a0: i64, a1: i64, a2: i64, a3: i64) -> i64 |
| 309 | func es_digest(st: *i64) -> i64 |