nx_chartlay_lib.nx source
↩ module page · 387 lines · 20952 B
1// nx_chartlay_lib.nx -- THE ONE chart-layout ruler for every SVG chart the compare emitters draw (datavis DV1, 2026-09-15).
2// Domain-fit scales with nice ticks, integer linear scaling, a colour-blind-safe categorical palette (Okabe-Ito, the
3// house accent in slot 0), text-width estimates, greedy collision-free label placement with leader lines, and the
4// READABILITY CRITIC (label-label, label-mark and mark-mark overlaps, off-canvas labels, data spread) so a render
5// GRADES ITSELF before it ships and prints the grade beside the picture. Born of the operator's 2026-09-15 junk verdict
6// on the position map: five marks in one corner of a fixed 0..1000 canvas, labels overlapping and clipped at the right
7// edge, a cube nobody could read. Pure integer arithmetic, struct-free, so the laptop toolchain proves it too.
8// The field's practice this encodes (Datawrapper, Observable Plot, Vega-Lite: 4..7 gridlines, axes fit to the data with
9// a breathing pad, direct labels that never collide, one palette that survives colour-vision deficiency) is the ORACLE;
10// nothing here links, forks or shells any of them -- they stay outside the build path as liar-killers and benchmarks.
11// license_tier: ORIGINAL
12import "nx_syscalls.nx"
13import "nx_uiq_color.nx" // THE ONE WCAG 2.x luminance + contrast kernel (KAT'd to the reference); cl_contrast composes it, never a second one
14
15const CL_PERMIL: i64 = 1000
16const CL_I64_BYTES: i64 = 8
17const CL_TWO: i64 = 2
18const CL_TICKS_MAX: i64 = 7 // the most ticks an axis wants: pick the smallest nice step that needs no more
19const CL_PAD_PERMIL: i64 = 120 // breathing pad each side of the data span, permil of the span
20const CL_PAD_MIN: i64 = 20 // ...never less than this many permil, so a tight cluster still breathes
21const CL_STEP_N: i64 = 9
22const CL_STEP_10: i64 = 10
23const CL_STEP_20: i64 = 20
24const CL_STEP_25: i64 = 25
25const CL_STEP_50: i64 = 50
26const CL_STEP_100: i64 = 100
27const CL_STEP_200: i64 = 200
28const CL_STEP_250: i64 = 250
29const CL_STEP_500: i64 = 500
30const CL_STEP_1000: i64 = 1000
31const CL_S0: i64 = 0
32const CL_S1: i64 = 1
33const CL_S2: i64 = 2
34const CL_S3: i64 = 3
35const CL_S4: i64 = 4
36const CL_S5: i64 = 5
37const CL_S6: i64 = 6
38const CL_S7: i64 = 7
39const CL_DOM_W: i64 = 3 // a domain record: lo, hi, step
40const CL_D_LO: i64 = 0
41const CL_D_HI: i64 = 1
42const CL_D_STEP: i64 = 2
43const CL_TEXT_W_PERMIL: i64 = 560 // mean glyph advance of a sans face at 1 em (digits and lower case read 0.50..0.61 em)
44const CL_LINE_H_PERMIL: i64 = 1250 // a one-line label box is 1.25 em tall
45const CL_GAP: i64 = 6 // px between a mark's edge and its label box
46const CL_LEAD_MULT: i64 = 3 // a leader-line candidate sits CL_GAP * this from the mark
47const CL_CAND_N: i64 = 8
48const CL_CAND_RIGHT: i64 = 0
49const CL_CAND_LEFT: i64 = 1
50const CL_CAND_ABOVE: i64 = 2
51const CL_CAND_BELOW: i64 = 3
52const CL_CAND_RU: i64 = 4 // right-up with a leader
53const CL_CAND_LU: i64 = 5
54const CL_CAND_RD: i64 = 6
55const CL_CAND_LD: i64 = 7
56const CL_CAND_LEADER_FROM: i64 = 4 // candidates at or past this index need a leader line
57const CL_CRIT_W: i64 = 6
58const CL_C_LL: i64 = 0 // label-label overlapping pairs
59const CL_C_LM: i64 = 1 // label-over-mark overlaps
60const CL_C_OFF: i64 = 2 // labels outside the canvas
61const CL_C_MM: i64 = 3 // mark-mark overlapping pairs (a property of the data, reported never hidden)
62const CL_C_SPREAD: i64 = 4 // permil of the plot the data bounding box spans, mean of the two axes
63const CL_C_DEFECTS: i64 = 5 // LL + LM + OFF: what a reader cannot read
64const CL_PAL_N: i64 = 8
65const CL_PAL_ACCENT: i64 = 0
66const CL_PAL_RIVALS: i64 = 7
67
68func chl_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } // chl_, not cl_: nx_catalog_lib owns cl_slen in the emitter's closure
69func cl_min(a: i64, b: i64) -> i64 { if a < b { return a } return b }
70func cl_max(a: i64, b: i64) -> i64 { if a > b { return a } return b }
71func cl_step_at(i: i64) -> i64 {
72 if i == CL_S0 { return CL_STEP_10 }
73 if i == CL_S1 { return CL_STEP_20 }
74 if i == CL_S2 { return CL_STEP_25 }
75 if i == CL_S3 { return CL_STEP_50 }
76 if i == CL_S4 { return CL_STEP_100 }
77 if i == CL_S5 { return CL_STEP_200 }
78 if i == CL_S6 { return CL_STEP_250 }
79 if i == CL_S7 { return CL_STEP_500 }
80 return CL_STEP_1000
81}
82// the smallest nice step that covers span with no more than CL_TICKS_MAX intervals
83func cl_nice_step(span: i64) -> i64 {
84 var i: i64 = 0
85 while i < CL_STEP_N { let s: i64 = cl_step_at(i); if span <= s * CL_TICKS_MAX { return s } i = i + 1 }
86 return CL_STEP_1000
87}
88func cl_floor_to(v: i64, step: i64) -> i64 { return v - (v % step) }
89func cl_ceil_to(v: i64, step: i64) -> i64 { let r: i64 = v % step; if r == 0 { return v } return v + step - r }
90// THE DOMAIN: the data span padded, clamped to 0..1000 permil, snapped outward to a nice step; returns the tick count.
91// A single value (span 0) still gets a readable window; the window never leaves 0..1000 because every step divides 1000.
92func cl_domain(vmin: i64, vmax: i64, dom: *i64) -> i64 {
93 var lo0: i64 = vmin
94 var hi0: i64 = vmax
95 if hi0 < lo0 { lo0 = vmax; hi0 = vmin }
96 let span: i64 = hi0 - lo0
97 var pad: i64 = span * CL_PAD_PERMIL / CL_PERMIL
98 if pad < CL_PAD_MIN { pad = CL_PAD_MIN }
99 lo0 = lo0 - pad
100 hi0 = hi0 + pad
101 if lo0 < 0 { lo0 = 0 }
102 if hi0 > CL_PERMIL { hi0 = CL_PERMIL }
103 let step: i64 = cl_nice_step(hi0 - lo0)
104 var lo: i64 = cl_floor_to(lo0, step)
105 var hi: i64 = cl_ceil_to(hi0, step)
106 if hi > CL_PERMIL { hi = CL_PERMIL }
107 if hi <= lo { hi = lo + step; if hi > CL_PERMIL { hi = CL_PERMIL; lo = hi - step } }
108 dom[CL_D_LO] = lo
109 dom[CL_D_HI] = hi
110 dom[CL_D_STEP] = step
111 return (hi - lo) / step + 1
112}
113func cl_tick(dom: *i64, i: i64) -> i64 { return dom[CL_D_LO] + i * dom[CL_D_STEP] }
114func cl_ticks(dom: *i64) -> i64 { return (dom[CL_D_HI] - dom[CL_D_LO]) / dom[CL_D_STEP] + 1 }
115// integer linear scale; p1 < p0 is the inverted (screen-y) case
116func cl_scale(v: i64, lo: i64, hi: i64, p0: i64, p1: i64) -> i64 { if hi == lo { return p0 } return p0 + (v - lo) * (p1 - p0) / (hi - lo) }
117func cl_scale_dom(v: i64, dom: *i64, p0: i64, p1: i64) -> i64 { return cl_scale(v, dom[CL_D_LO], dom[CL_D_HI], p0, p1) }
118func cl_text_w(nchars: i64, font_px: i64) -> i64 { return nchars * font_px * CL_TEXT_W_PERMIL / CL_PERMIL }
119func cl_text_h(font_px: i64) -> i64 { return font_px * CL_LINE_H_PERMIL / CL_PERMIL }
120func cl_box_hit(ax: i64, ay: i64, aw: i64, ah: i64, bx: i64, by: i64, bw: i64, bh: i64) -> i64 {
121 if ax + aw <= bx { return 0 }
122 if bx + bw <= ax { return 0 }
123 if ay + ah <= by { return 0 }
124 if by + bh <= ay { return 0 }
125 return 1
126}
127func cl_leader(k: i64) -> i64 { if k >= CL_CAND_LEADER_FROM { return 1 } return 0 }
128// candidate k for a label of lw x lh beside a mark at (mx, my) radius mr; writes the box's top-left, returns cl_leader(k)
129func cl_cand(k: i64, mx: i64, my: i64, mr: i64, lw: i64, lh: i64, xy: *i64) -> i64 {
130 let near: i64 = mr + CL_GAP
131 let far: i64 = mr + CL_GAP * CL_LEAD_MULT
132 if k == CL_CAND_RIGHT { xy[0] = mx + near; xy[1] = my - lh / CL_TWO; return 0 }
133 if k == CL_CAND_LEFT { xy[0] = mx - near - lw; xy[1] = my - lh / CL_TWO; return 0 }
134 if k == CL_CAND_ABOVE { xy[0] = mx - lw / CL_TWO; xy[1] = my - near - lh; return 0 }
135 if k == CL_CAND_BELOW { xy[0] = mx - lw / CL_TWO; xy[1] = my + near; return 0 }
136 if k == CL_CAND_RU { xy[0] = mx + far; xy[1] = my - far - lh; return 1 }
137 if k == CL_CAND_LU { xy[0] = mx - far - lw; xy[1] = my - far - lh; return 1 }
138 if k == CL_CAND_RD { xy[0] = mx + far; xy[1] = my + far; return 1 }
139 xy[0] = mx - far - lw
140 xy[1] = my + far
141 return 1
142}
143// does label i at (x, y) fit: inside the canvas, clear of every mark but its own, clear of every label already placed
144func cl_fits(i: i64, x: i64, y: i64, n: i64, mx: *i64, my: *i64, mr: *i64, lx: *i64, ly: *i64, lw: *i64, lh: *i64, placed: *i64, x0: i64, y0: i64, x1: i64, y1: i64) -> i64 {
145 if x < x0 { return 0 }
146 if y < y0 { return 0 }
147 if x + lw[i] > x1 { return 0 }
148 if y + lh[i] > y1 { return 0 }
149 var j: i64 = 0
150 while j < n {
151 if j != i {
152 if cl_box_hit(x, y, lw[i], lh[i], mx[j] - mr[j], my[j] - mr[j], mr[j] * CL_TWO, mr[j] * CL_TWO) == 1 { return 0 }
153 if placed[j] == 1 { if cl_box_hit(x, y, lw[i], lh[i], lx[j], ly[j], lw[j], lh[j]) == 1 { return 0 } }
154 }
155 j = j + 1
156 }
157 return 1
158}
159// THE PLACER: greedy, in caller order (put the house player first so it takes the best seat); the first fitting
160// candidate wins; a label nothing fits is clamped inside the canvas at the right seat and COUNTED as unresolved --
161// the critic then reports its overlaps rather than hiding a label. Returns the unresolved count.
162func cl_place(n: i64, mx: *i64, my: *i64, mr: *i64, lw: *i64, lh: *i64, x0: i64, y0: i64, x1: i64, y1: i64, lx: *i64, ly: *i64, lside: *i64, lres: *i64) -> i64 {
163 return cl_place_from(n, 0, mx, my, mr, lw, lh, x0, y0, x1, y1, lx, ly, lside, lres)
164}
165// THE SAME PLACER WITH FIXED OBSTACLES (2026-09-15): the first nfixed entries are ALREADY seated (their lx/ly stand) and
166// are never moved; entries from nfixed on are seated around them. This is how a second class of text -- the quadrant
167// words of the position map -- takes the seats the players and notes left, instead of being painted first and covered
168// (measured on the live search page: a note box over CONVENTIONAL, a player label over MAINSTREAM). nfixed=0 is the
169// original placer, byte for byte.
170func cl_place_from(n: i64, nfixed: i64, mx: *i64, my: *i64, mr: *i64, lw: *i64, lh: *i64, x0: i64, y0: i64, x1: i64, y1: i64, lx: *i64, ly: *i64, lside: *i64, lres: *i64) -> i64 {
171 let placed: *i64 = sys_mmap((n + 1) * CL_I64_BYTES) as *i64
172 let xy: *i64 = sys_mmap(CL_TWO * CL_I64_BYTES) as *i64
173 var unresolved: i64 = 0
174 var i: i64 = 0
175 while i < n { placed[i] = 0; if i < nfixed { placed[i] = 1 } i = i + 1 }
176 i = nfixed
177 while i < n {
178 var k: i64 = 0
179 var done: i64 = 0
180 while k < CL_CAND_N {
181 if done == 0 {
182 cl_cand(k, mx[i], my[i], mr[i], lw[i], lh[i], xy)
183 if cl_fits(i, xy[0], xy[1], n, mx, my, mr, lx, ly, lw, lh, placed, x0, y0, x1, y1) == 1 { lx[i] = xy[0]; ly[i] = xy[1]; lside[i] = k; lres[i] = 1; done = 1 }
184 }
185 k = k + 1
186 }
187 if done == 0 {
188 cl_cand(CL_CAND_RIGHT, mx[i], my[i], mr[i], lw[i], lh[i], xy)
189 lx[i] = cl_max(x0, cl_min(xy[0], x1 - lw[i]))
190 ly[i] = cl_max(y0, cl_min(xy[1], y1 - lh[i]))
191 lside[i] = CL_CAND_RIGHT
192 lres[i] = 0
193 unresolved = unresolved + 1
194 }
195 placed[i] = 1
196 i = i + 1
197 }
198 return unresolved
199}
200// THE CRITIC: counts what a reader cannot read. out = [LL, LM, OFF, MM, SPREAD, DEFECTS]; returns DEFECTS.
201func cl_critic(n: i64, mx: *i64, my: *i64, mr: *i64, lx: *i64, ly: *i64, lw: *i64, lh: *i64, x0: i64, y0: i64, x1: i64, y1: i64, out: *i64) -> i64 {
202 var k: i64 = 0
203 while k < CL_CRIT_W { out[k] = 0; k = k + 1 }
204 var bx0: i64 = 0
205 var bx1: i64 = 0
206 var by0: i64 = 0
207 var by1: i64 = 0
208 var i: i64 = 0
209 while i < n {
210 if i == 0 { bx0 = mx[0]; bx1 = mx[0]; by0 = my[0]; by1 = my[0] }
211 bx0 = cl_min(bx0, mx[i])
212 bx1 = cl_max(bx1, mx[i])
213 by0 = cl_min(by0, my[i])
214 by1 = cl_max(by1, my[i])
215 var off: i64 = 0
216 if lx[i] < x0 { off = 1 }
217 if ly[i] < y0 { off = 1 }
218 if lx[i] + lw[i] > x1 { off = 1 }
219 if ly[i] + lh[i] > y1 { off = 1 }
220 out[CL_C_OFF] = out[CL_C_OFF] + off
221 var j: i64 = 0
222 while j < n {
223 if j != i { if cl_box_hit(lx[i], ly[i], lw[i], lh[i], mx[j] - mr[j], my[j] - mr[j], mr[j] * CL_TWO, mr[j] * CL_TWO) == 1 { out[CL_C_LM] = out[CL_C_LM] + 1 } }
224 if j > i {
225 if cl_box_hit(lx[i], ly[i], lw[i], lh[i], lx[j], ly[j], lw[j], lh[j]) == 1 { out[CL_C_LL] = out[CL_C_LL] + 1 }
226 let dx: i64 = mx[i] - mx[j]
227 let dy: i64 = my[i] - my[j]
228 let rr: i64 = mr[i] + mr[j]
229 if dx * dx + dy * dy < rr * rr { out[CL_C_MM] = out[CL_C_MM] + 1 }
230 }
231 j = j + 1
232 }
233 i = i + 1
234 }
235 if n > 0 {
236 let pw: i64 = cl_max(1, x1 - x0)
237 let ph: i64 = cl_max(1, y1 - y0)
238 out[CL_C_SPREAD] = ((bx1 - bx0) * CL_PERMIL / pw + (by1 - by0) * CL_PERMIL / ph) / CL_TWO
239 }
240 out[CL_C_DEFECTS] = out[CL_C_LL] + out[CL_C_LM] + out[CL_C_OFF]
241 return out[CL_C_DEFECTS]
242}
243// THE PALETTE: slot 0 is the house accent token (theme-aware through the page's CSS variables); rivals cycle the
244// Okabe-Ito set, distinguishable under the common colour-vision deficiencies and legible on both themes.
245func cl_palette(i: i64) -> *u8 {
246 if i == CL_PAL_ACCENT { return "var(--nx-color-accent)" as *u8 }
247 let k: i64 = (i - 1) % CL_PAL_RIVALS
248 if k == CL_S0 { return "rgb(230,159,0)" as *u8 }
249 if k == CL_S1 { return "rgb(86,180,233)" as *u8 }
250 if k == CL_S2 { return "rgb(0,158,115)" as *u8 }
251 if k == CL_S3 { return "rgb(213,94,0)" as *u8 }
252 if k == CL_S4 { return "rgb(204,121,167)" as *u8 }
253 if k == CL_S5 { return "rgb(0,114,178)" as *u8 }
254 return "rgb(240,228,66)" as *u8
255}
256// ---- RESEAT INSIDE A REGION (2026-09-15) ---------------------------------------------------------------------
257// When all eight candidate seats around an anchor collide (measured on the live search map: CONVENTIONAL, whose anchor
258// sat under a note box), a box that only needs to be SOMEWHERE in its region -- a quadrant word -- may take the region's
259// corners or its centre. Every other entry counts as placed; returns 1 and writes the seat, or 0 and leaves the box
260// where the placer clamped it, so the caller's unseated count stays honest.
261const CL_RESEAT_N: i64 = 5
262func cl_reseat_in(i: i64, n: i64, mx: *i64, my: *i64, mr: *i64, lx: *i64, ly: *i64, lw: *i64, lh: *i64, rx0: i64, ry0: i64, rx1: i64, ry1: i64) -> i64 {
263 let placed: *i64 = sys_mmap((n + 1) * CL_I64_BYTES) as *i64
264 var j: i64 = 0
265 while j < n { placed[j] = 1; j = j + 1 }
266 placed[i] = 0
267 var k: i64 = 0
268 while k < CL_RESEAT_N {
269 var x: i64 = rx0 + CL_GAP
270 var y: i64 = ry0 + CL_GAP
271 if k == 1 { x = rx1 - CL_GAP - lw[i] }
272 if k == CL_TWO { y = ry1 - CL_GAP - lh[i] }
273 if k == 3 { x = rx1 - CL_GAP - lw[i]; y = ry1 - CL_GAP - lh[i] }
274 if k == 4 { x = (rx0 + rx1 - lw[i]) / CL_TWO; y = (ry0 + ry1 - lh[i]) / CL_TWO }
275 if cl_fits(i, x, y, n, mx, my, mr, lx, ly, lw, lh, placed, rx0, ry0, rx1, ry1) == 1 {
276 lx[i] = x; ly[i] = y
277 sys_munmap(placed as *u8, (n + 1) * CL_I64_BYTES)
278 return 1
279 }
280 k = k + 1
281 }
282 sys_munmap(placed as *u8, (n + 1) * CL_I64_BYTES)
283 return 0
284}
285
286// ---- CONTRAST (datavis DV3, 2026-09-15) ----------------------------------------------------------------------
287// THE ONE contrast ruler the figures grade themselves with. The arithmetic is the estate's WCAG 2.x kernel
288// (nx_uiq_color.uiq_ratio100, KAT'd to the reference: black on white 21.00, #767676 on white 4.54). The FLOORS and the
289// size-and-weight tiers are DATA (knowledge/compare/contrast.conf): the 10 September 2026 WCAG 3 draft makes text contrast
290// a Core requirement while leaving its algorithm to be determined, so the conf's algorithm row NAMES what is measured
291// today and is the one row that moves when the draft names one; a conf naming an algorithm this ruler does not implement
292// falls back to the defaults AND SAYS SO (CL_CT_LOADED = 2), never silently. Defaults are the WCAG 2.x AA tiers.
293const CL_CT_W: i64 = 5
294const CL_CT_SMALL: i64 = 0 // floor x100 for text below the large tier
295const CL_CT_LARGE: i64 = 1 // floor x100 for large text
296const CL_CT_LARGE_PX: i64 = 2 // regular text at or past this px is large
297const CL_CT_LARGE_BOLD_PX: i64 = 3 // bold text at or past this px is large
298const CL_CT_LOADED: i64 = 4 // 0 defaults (no conf) / 1 conf read / 2 conf names an unimplemented algorithm (defaults, announced)
299const CL_CT_DEFAULT_SMALL: i64 = 450
300const CL_CT_DEFAULT_LARGE: i64 = 300
301const CL_CT_DEFAULT_LARGE_PX: i64 = 18
302const CL_CT_DEFAULT_LARGE_BOLD_PX: i64 = 14
303const CL_CT_ALGORITHM: *u8 = "wcag2-ratio"
304const CL_CT_UNKNOWN_ALG: i64 = 2
305const CL_CT_CH_PIPE: i64 = 124
306const CL_CT_CH_NL: i64 = 10
307const CL_CT_CH_0: i64 = 48
308const CL_CT_CH_9: i64 = 57
309const CL_CT_BASE10: i64 = 10
310func cl_contrast(fr: i64, fg: i64, fb: i64, br: i64, bg: i64, bb: i64) -> i64 { return uiq_ratio100(fr, fg, fb, br, bg, bb) }
311// one channel of `top` painted at a_permil over `under`: the colour a reader actually sees under an opacity
312func cl_blend(top: i64, under: i64, a_permil: i64) -> i64 { return (top * a_permil + under * (CL_PERMIL - a_permil)) / CL_PERMIL }
313func cl_contrast_defaults(ct: *i64) -> i64 {
314 ct[CL_CT_SMALL] = CL_CT_DEFAULT_SMALL
315 ct[CL_CT_LARGE] = CL_CT_DEFAULT_LARGE
316 ct[CL_CT_LARGE_PX] = CL_CT_DEFAULT_LARGE_PX
317 ct[CL_CT_LARGE_BOLD_PX] = CL_CT_DEFAULT_LARGE_BOLD_PX
318 ct[CL_CT_LOADED] = 0
319 return 0
320}
321func cl_contrast_floor(font_px: i64, bold: i64, ct: *i64) -> i64 {
322 if bold == 1 { if font_px >= ct[CL_CT_LARGE_BOLD_PX] { return ct[CL_CT_LARGE] } return ct[CL_CT_SMALL] }
323 if font_px >= ct[CL_CT_LARGE_PX] { return ct[CL_CT_LARGE] }
324 return ct[CL_CT_SMALL]
325}
326func cl_contrast_ok(x100: i64, floor: i64) -> i64 { if x100 >= floor { return 1 } return 0 }
327// field f of the row b[s..e) (pipe-separated): its start, or e when absent
328func cl_ct_field(b: *u8, s: i64, e: i64, f: i64) -> i64 {
329 var p: i64 = s
330 var k: i64 = 0
331 while k < f { while p < e { if (b[p] as i64) == CL_CT_CH_PIPE { p = e + p } else { p = p + 1 } } if p > e { p = p - e + 1 } else { return e } k = k + 1 }
332 return p
333}
334func cl_ct_field_is(b: *u8, s: i64, e: i64, f: i64, lit: *u8) -> i64 {
335 let p: i64 = cl_ct_field(b, s, e, f)
336 if p >= e { return 0 }
337 var i: i64 = 0
338 while lit[i] != (0 as u8) { if p + i >= e { return 0 } if b[p + i] != lit[i] { return 0 } i = i + 1 }
339 if p + i < e { let c: i64 = b[p + i] as i64; if c != CL_CT_CH_PIPE { if c != CL_CT_CH_NL { return 0 } } }
340 return 1
341}
342func cl_ct_field_int(b: *u8, s: i64, e: i64, f: i64) -> i64 {
343 var p: i64 = cl_ct_field(b, s, e, f)
344 var v: i64 = 0
345 var nd: i64 = 0
346 while p < e { let c: i64 = b[p] as i64; if c >= CL_CT_CH_0 { if c <= CL_CT_CH_9 { v = v * CL_CT_BASE10 + (c - CL_CT_CH_0); nd = nd + 1; p = p + 1 } else { p = e + p } } else { p = e + p } }
347 if nd == 0 { return 0 - 1 }
348 return v
349}
350// read the policy: 1 conf read, 0 absent (defaults), CL_CT_UNKNOWN_ALG conf names an algorithm this ruler does not implement (defaults)
351func cl_contrast_conf(path: *u8, ct: *i64) -> i64 {
352 cl_contrast_defaults(ct)
353 let nb: *i64 = sys_mmap(CL_TWO * CL_I64_BYTES) as *i64
354 nb[0] = 0
355 let b: *u8 = sys_read_file(path, nb)
356 let n: i64 = nb[0]
357 if (b as i64) == 0 { return 0 }
358 if n <= 0 { return 0 }
359 var alg_seen: i64 = 0
360 var alg_ok: i64 = 0
361 var s: i64 = 0
362 while s < n {
363 var e: i64 = s
364 while e < n { if (b[e] as i64) == CL_CT_CH_NL { e = n + e } else { e = e + 1 } }
365 if e > n { e = e - n }
366 if cl_ct_field_is(b, s, e, 0, "algorithm" as *u8) == 1 { alg_seen = 1; if cl_ct_field_is(b, s, e, 1, CL_CT_ALGORITHM) == 1 { alg_ok = 1 } }
367 if cl_ct_field_is(b, s, e, 0, "floor_small_x100" as *u8) == 1 { let v: i64 = cl_ct_field_int(b, s, e, 1); if v >= 0 { ct[CL_CT_SMALL] = v } }
368 if cl_ct_field_is(b, s, e, 0, "floor_large_x100" as *u8) == 1 { let v: i64 = cl_ct_field_int(b, s, e, 1); if v >= 0 { ct[CL_CT_LARGE] = v } }
369 if cl_ct_field_is(b, s, e, 0, "large_px" as *u8) == 1 { let v: i64 = cl_ct_field_int(b, s, e, 1); if v >= 0 { ct[CL_CT_LARGE_PX] = v } }
370 if cl_ct_field_is(b, s, e, 0, "large_bold_px" as *u8) == 1 { let v: i64 = cl_ct_field_int(b, s, e, 1); if v >= 0 { ct[CL_CT_LARGE_BOLD_PX] = v } }
371 s = e + 1
372 }
373 if alg_seen == 1 { if alg_ok == 0 { cl_contrast_defaults(ct); ct[CL_CT_LOADED] = CL_CT_UNKNOWN_ALG; return CL_CT_UNKNOWN_ALG } }
374 ct[CL_CT_LOADED] = 1
375 return 1
376}
377func cl_palette_name(i: i64) -> *u8 {
378 if i == CL_PAL_ACCENT { return "house accent" as *u8 }
379 let k: i64 = (i - 1) % CL_PAL_RIVALS
380 if k == CL_S0 { return "orange" as *u8 }
381 if k == CL_S1 { return "sky blue" as *u8 }
382 if k == CL_S2 { return "bluish green" as *u8 }
383 if k == CL_S3 { return "vermillion" as *u8 }
384 if k == CL_S4 { return "reddish purple" as *u8 }
385 if k == CL_S5 { return "blue" as *u8 }
386 return "yellow" as *u8
387}