L
ling-sys
↓ 0 downloadsSystem activity and performance monitor in the Ling Design System — dials, meshes, drums and crosshatch for CPU, GPU, memory, disk, network, board, processes and audio, with every subsystem given a voice. A LingOS app for rpi5: live board/CPU/memory/process stats from the kernel (stats-lingos.ling).
lingfu install ling-sysCode / Files (13)
lds.ling
# ============================================================
# lds.ling — the Ling Design System, drawn natively.
# Everything is line work: outline plus crosshatch. Nothing is
# filled except glyph interiors, which stand in for single-angle
# hatch at a dimmed weight. Colour is semantic, never decorative.
# Angles called "phi" are clock angles: 0 at twelve, clockwise,
# radians. Hatch angles are in degrees to match px_hatch.
# ============================================================
# ---------- palette (screen theme) ----------
fn rgbpack(r, g, b) { return r * 65536.0 + g * 256.0 + b }
fn pal_init() {
st_set("c_paper", 0.0)
st_set("c_ink", rgbpack(123.0, 135.0, 151.0))
st_set("c_asm", rgbpack(235.0, 100.0, 102.0))
st_set("c_lit", rgbpack(249.0, 155.0, 56.0))
st_set("c_bind", rgbpack(219.0, 177.0, 56.0))
st_set("c_ctl", rgbpack(89.0, 179.0, 113.0))
st_set("c_call", rgbpack(80.0, 143.0, 223.0))
st_set("c_fn", rgbpack(115.0, 105.0, 209.0))
st_set("c_mod", rgbpack(177.0, 105.0, 213.0))
}
fn pcol(key) { return st_get("c_" + key, 8421504.0) }
fn col_r(c) { return floor(c / 65536.0) }
fn col_g(c) { return floor((c % 65536.0) / 256.0) }
fn col_b(c) { return c % 256.0 }
# dim a packed colour toward black (alpha on paper)
fn dim(c, k) {
return rgbpack(floor(col_r(c) * k), floor(col_g(c) * k), floor(col_b(c) * k))
}
# set the pen to a packed colour scaled by a (also remembered as a packed
# colour, for arcs drawn from line segments where there is no draw_arc)
fn setcola(c, a) {
bind rr = floor(col_r(c) * a)
bind gg = floor(col_g(c) * a)
bind bb = floor(col_b(c) * a)
set_color(rr, gg, bb)
st_set("pen", rgbpack(rr, gg, bb))
}
# ---------- clock-angle geometry ----------
fn ckx(cx, r, phi) { return cx + r * sin(phi) }
fn cky(cy, r, phi) { return cy - r * cos(phi) }
# An arc in the pen colour, math angles (radians, clockwise on screen). The
# host has draw_arc; LingOS does not, so there it is a run of short lines.
fn arcpx(cx, cy, r, a0, a1) {
if st_get("os_win", 1.0) > 0.5 {
draw_arc(cx, cy, r, a0, a1)
return 0
}
bind da = a1 - a0
bind n = ceil(abs(da) * r / 9.0)
if n < 6.0 { bind n = 6.0 }
if n > 56.0 { bind n = 56.0 }
bind pen = st_get("pen", 8421504.0)
bind x0 = cx + r * cos(a0)
bind y0 = cy + r * sin(a0)
bind k = 1.0
while k <= n {
bind a = a0 + da * k / n
bind x1 = cx + r * cos(a)
bind y1 = cy + r * sin(a)
px_line(x0, y0, x1, y1, pen, 1.0)
bind x0 = x1
bind y0 = y1
bind k = k + 1.0
}
return 0
}
# arcs take math angles; clock phi - pi/2 converts
fn arc_clock(cx, cy, r, phi0, sweep) {
arcpx(cx, cy, r, phi0 - 1.5707963, phi0 + sweep - 1.5707963)
}
# ---------- lines ----------
fn thick(x0, y0, x1, y1, c, a) {
px_line(x0, y0, x1, y1, c, a)
bind dx = x1 - x0
bind dy = y1 - y0
bind l = sqrt(dx * dx + dy * dy)
if l > 0.01 {
bind nx = 0.0 - dy / l
bind ny = dx / l
px_line(x0 + nx, y0 + ny, x1 + nx, y1 + ny, c, a)
}
}
fn dashline(x0, y0, x1, y1, c, a) {
bind dx = x1 - x0
bind dy = y1 - y0
bind l = sqrt(dx * dx + dy * dy)
if l < 1.0 { return 0 }
bind ux = dx / l
bind uy = dy / l
bind s = 0.0
while s < l {
bind e = s + 6.0
if e > l { bind e = l }
px_line(x0 + ux * s, y0 + uy * s, x0 + ux * e, y0 + uy * e, c, a)
bind s = s + 11.0
}
}
# ---------- hatch ----------
# density 1 (open) .. 4 (dense): spacing multiplier, as the system specifies
fn den_mul(d) {
if d <= 1.0 { return 3.4 }
if d <= 2.0 { return 2.4 }
if d <= 3.0 { return 1.6 }
return 1.0
}
fn hatch_circle(cx, cy, r, c, angdeg, sp, a) {
bind A = angdeg * 0.017453293
bind nx = sin(A)
bind ny = cos(A)
bind tx = cos(A)
bind ty = 0.0 - sin(A)
bind d = 0.0 - r + sp * 0.5
while d < r {
bind h2 = r * r - d * d
if h2 > 0.25 {
bind hl = sqrt(h2) - sp * 0.4
if hl > 0.5 {
bind mx = cx + nx * d
bind my = cy + ny * d
px_line(mx - tx * hl, my - ty * hl, mx + tx * hl, my + ty * hl, c, a)
}
}
bind d = d + sp
}
return 0
}
fn hatch_ring(cx, cy, r0, r1, c, angdeg, sp, a) {
bind A = angdeg * 0.017453293
bind nx = sin(A)
bind ny = cos(A)
bind tx = cos(A)
bind ty = 0.0 - sin(A)
bind d = 0.0 - r1 + sp * 0.5
while d < r1 {
bind h2 = r1 * r1 - d * d
if h2 > 0.25 {
bind ho = sqrt(h2) - sp * 0.3
bind mx = cx + nx * d
bind my = cy + ny * d
bind ad = abs(d)
if ad >= r0 {
if ho > 0.5 {
px_line(mx - tx * ho, my - ty * ho, mx + tx * ho, my + ty * ho, c, a)
}
} else {
bind hi2 = r0 * r0 - d * d
bind hi = 0.0
if hi2 > 0.0 { bind hi = sqrt(hi2) + sp * 0.3 }
if ho > hi {
px_line(mx - tx * ho, my - ty * ho, mx - tx * hi, my - ty * hi, c, a)
px_line(mx + tx * hi, my + ty * hi, mx + tx * ho, my + ty * ho, c, a)
}
}
}
bind d = d + sp
}
return 0
}
# annular sector between clock angles p0 .. p0+sw, sampled runs
fn hatch_sector(cx, cy, r0, r1, p0, sw, c, angdeg, sp, a) {
bind A = angdeg * 0.017453293
bind nx = sin(A)
bind ny = cos(A)
bind tx = cos(A)
bind ty = 0.0 - sin(A)
bind d = 0.0 - r1 + sp * 0.5
while d < r1 {
bind h2 = r1 * r1 - d * d
if h2 > 0.25 {
bind hl = sqrt(h2)
bind mx = cx + nx * d
bind my = cy + ny * d
bind s = 0.0 - hl
bind inrun = 0
bind runx = 0.0
bind runy = 0.0
bind lx = 0.0
bind ly = 0.0
while s <= hl {
bind qx = mx + tx * s
bind qy = my + ty * s
bind ddx = qx - cx
bind ddy = qy - cy
bind rr = sqrt(ddx * ddx + ddy * ddy)
bind ok = 0
if rr >= r0 {
if rr <= r1 - sp * 0.2 { bind ok = 1 }
}
if ok == 1 {
bind phi = atan2(ddx, 0.0 - ddy)
if phi < 0.0 { bind phi = phi + 6.2831853 }
bind rel = phi - p0
while rel < 0.0 { bind rel = rel + 6.2831853 }
if rel > sw { bind ok = 0 }
}
if ok == 1 {
if inrun == 0 {
bind runx = qx
bind runy = qy
bind inrun = 1
}
} else {
if inrun == 1 {
px_line(runx, runy, lx, ly, c, a)
bind inrun = 0
}
}
bind lx = qx
bind ly = qy
bind s = s + 2.5
}
if inrun == 1 { px_line(runx, runy, lx, ly, c, a) }
}
bind d = d + sp
}
return 0
}
# convex polygon from a flat [x0,y0,x1,y1,...] list
fn hatch_poly(pts, c, angdeg, sp, a) {
bind n = len(pts) / 2
if n < 3 { return 0 }
bind A = angdeg * 0.017453293
bind nx = sin(A)
bind ny = cos(A)
bind tx = cos(A)
bind ty = 0.0 - sin(A)
bind cmin = 999999.0
bind cmax = 0.0 - 999999.0
bind i = 0
while i < n {
bind pxx = list_get(pts, i * 2)
bind pyy = list_get(pts, i * 2 + 1)
bind cv = pxx * nx + pyy * ny
if cv < cmin { bind cmin = cv }
if cv > cmax { bind cmax = cv }
bind i = i + 1
}
bind cc = cmin + sp * 0.6
while cc < cmax - sp * 0.1 {
bind smin = 999999.0
bind smax = 0.0 - 999999.0
bind hits = 0
bind i = 0
while i < n {
bind j = i + 1
if j >= n { bind j = 0 }
bind pxx = list_get(pts, i * 2)
bind pyy = list_get(pts, i * 2 + 1)
bind qxx = list_get(pts, j * 2)
bind qyy = list_get(pts, j * 2 + 1)
bind pc = pxx * nx + pyy * ny
bind qc = qxx * nx + qyy * ny
bind dc = qc - pc
if abs(dc) > 0.0001 {
bind u = (cc - pc) / dc
if u >= 0.0 {
if u <= 1.0 {
bind ix = pxx + (qxx - pxx) * u
bind iy = pyy + (qyy - pyy) * u
bind sv = ix * tx + iy * ty
if sv < smin { bind smin = sv }
if sv > smax { bind smax = sv }
bind hits = hits + 1
}
}
}
bind i = i + 1
}
if hits >= 2 {
bind s0 = smin + sp * 0.35
bind s1 = smax - sp * 0.35
if s1 > s0 {
px_line(nx * cc + tx * s0, ny * cc + ty * s0, nx * cc + tx * s1, ny * cc + ty * s1, c, a)
}
}
bind cc = cc + sp
}
return 0
}
# ---------- polygons ----------
fn mkngon(cx, cy, r, sides, rotdeg) {
bind pts = list_new()
bind k = 0
while k < sides {
bind phi = rotdeg * 0.017453293 + k * 6.2831853 / sides
bind pts = list_push(pts, ckx(cx, r, phi))
bind pts = list_push(pts, cky(cy, r, phi))
bind k = k + 1
}
return pts
}
fn poly_outline(pts, c, a) {
bind n = len(pts) / 2
bind i = 0
while i < n {
bind j = i + 1
if j >= n { bind j = 0 }
thick(list_get(pts, i * 2), list_get(pts, i * 2 + 1), list_get(pts, j * 2), list_get(pts, j * 2 + 1), c, a)
bind i = i + 1
}
return 0
}
# ---------- node: the typed shapes ----------
# asm octagon · lit cross · bind circle · ctl triangle · call diamond ·
# fn hexagon · mod square. Interior crosshatched; density carries activity.
fn node(key, cx, cy, r, den, a) {
bind c = pcol(key)
bind sp = r * 0.3 * den_mul(den)
if sp < 2.6 { bind sp = 2.6 }
if key == "bind" {
setcola(c, a)
arcpx(cx, cy, r, 0.0, 6.2831853)
arcpx(cx, cy, r - 1.0, 0.0, 6.2831853)
hatch_circle(cx, cy, r - 1.5, c, 45.0, sp, 0.55 * a)
hatch_circle(cx, cy, r - 1.5, c, 315.0, sp, 0.55 * a)
return 0
}
if key == "lit" {
bind w = r * 0.38
px_line(cx - w, cy - r, cx + w, cy - r, c, a)
px_line(cx + w, cy - r, cx + w, cy - w, c, a)
px_line(cx + w, cy - w, cx + r, cy - w, c, a)
px_line(cx + r, cy - w, cx + r, cy + w, c, a)
px_line(cx + r, cy + w, cx + w, cy + w, c, a)
px_line(cx + w, cy + w, cx + w, cy + r, c, a)
px_line(cx + w, cy + r, cx - w, cy + r, c, a)
px_line(cx - w, cy + r, cx - w, cy + w, c, a)
px_line(cx - w, cy + w, cx - r, cy + w, c, a)
px_line(cx - r, cy + w, cx - r, cy - w, c, a)
px_line(cx - r, cy - w, cx - w, cy - w, c, a)
px_line(cx - w, cy - w, cx - w, cy - r, c, a)
px_hatch(cx - w + 1.0, cy - r + 1.0, w * 2.0 - 2.0, r * 2.0 - 2.0, c, 45.0, sp, 0.5 * a)
px_hatch(cx - r + 1.0, cy - w + 1.0, r - w - 2.0, w * 2.0 - 2.0, c, 45.0, sp, 0.5 * a)
px_hatch(cx + w + 1.0, cy - w + 1.0, r - w - 2.0, w * 2.0 - 2.0, c, 45.0, sp, 0.5 * a)
return 0
}
bind sides = 4.0
bind rot = 45.0
if key == "asm" { bind sides = 8.0 bind rot = 22.5 }
if key == "ctl" { bind sides = 3.0 bind rot = 0.0 }
if key == "call" { bind sides = 4.0 bind rot = 0.0 }
if key == "fn" { bind sides = 6.0 bind rot = 0.0 }
bind pts = mkngon(cx, cy, r, sides, rot)
poly_outline(pts, c, a)
hatch_poly(pts, c, 45.0, sp, 0.55 * a)
hatch_poly(pts, c, 315.0, sp, 0.55 * a)
return 0
}
# ---------- text scale: every word in the app obeys this ----------
fn tsize(s) { return s * st_get("tscale", 1.0) }
# ---------- flat Daemon: data text without the tile brackets ----------
# Same character advance as the English face (measured from it), so switching
# faces never reflows a line. Set by stext around its Daemon calls only; titles
# and headings keep their bracketed tiles.
fn flat(on) {
st_set("tflat", on)
return 0
}
fn flat_adv(size) {
bind w = font_width(st_get("fe", 0.0), size * 1.35, "0")
if w < 4.0 { return size * 0.74 }
return w
}
# ---------- tile and word (Daemon; English face in text mode 2) ----------
fn tile(ch, x, y, rawsize, key, a) {
bind size = tsize(rawsize)
bind c = pcol(key)
if st_get("tflat", 0.0) > 0.5 {
if ch == " " { return 0 }
bind adv = flat_adv(size)
bind gpx = size * 1.3
bind w = font_width(st_get("fd", 0.0), gpx, ch)
bind lim = adv * 0.88
if w > lim {
if w > 0.0 {
bind gpx = gpx * lim / w
bind w = font_width(st_get("fd", 0.0), gpx, ch)
}
}
bind gx = x + (adv - w) * 0.5
bind gy = y + size * 0.5 - gpx * st_get("voff_d", 0.35)
setcola(c, a)
font_text(st_get("fd", 0.0), gx, gy, gpx, ch)
font_text(st_get("fd", 0.0), gx + 0.55, gy, gpx, ch)
return 0
}
if ch == " " {
px_disc(x + size * 0.5, y + size * 0.5, size * 0.04 + 0.8, c, 0.7 * a)
return 0
}
px_outline(x + 0.5, y + 0.5, size - 1.0, size - 1.0, c, 0.38 * a)
bind ins = size * 0.055
bind arm = size * 0.21
bind x1 = x + size
bind y1 = y + size
px_line(x + ins, y + ins, x + ins + arm, y + ins, c, a)
px_line(x + ins, y + ins, x + ins, y + ins + arm, c, a)
px_line(x1 - ins, y + ins, x1 - ins - arm, y + ins, c, a)
px_line(x1 - ins, y + ins, x1 - ins, y + ins + arm, c, a)
px_line(x + ins, y1 - ins, x + ins + arm, y1 - ins, c, a)
px_line(x + ins, y1 - ins, x + ins, y1 - ins - arm, c, a)
px_line(x1 - ins, y1 - ins, x1 - ins - arm, y1 - ins, c, a)
px_line(x1 - ins, y1 - ins, x1 - ins, y1 - ins - arm, c, a)
bind fd = st_get("fd", 0.0)
bind voff = st_get("voff_d", 0.35)
if st_get("p_txt", 1.0) >= 2.0 && st_get("fe", 0.0) >= 0.0 {
bind fd = st_get("fe", 0.0)
bind voff = st_get("voff_e", 0.45)
}
bind target = size * 0.60
bind gpx = target * st_get("fit_d", 1.3)
bind w = font_width(fd, gpx, ch)
if w > target {
if w > 0.0 {
bind gpx = gpx * target / w
bind w = font_width(fd, gpx, ch)
}
}
bind gx = x + (size - w) * 0.5
bind gy = y + size * 0.5 - gpx * voff
setcola(c, a)
font_text(fd, gx, gy, gpx, ch)
font_text(fd, gx + 0.55, gy, gpx, ch)
return 0
}
fn word_w(text, rawsize) {
if want_eng2() {
return etext_w(text, rawsize)
}
bind size = tsize(rawsize)
if st_get("tflat", 0.0) > 0.5 { return len(text) * flat_adv(size) }
return len(text) * size * 1.14 - size * 0.14
}
fn word(text, x, y, rawsize, key, a) {
if want_eng2() {
etext(text, x, y, rawsize, key, a)
return 0
}
bind size = tsize(rawsize)
bind step = size * 1.14
if st_get("tflat", 0.0) > 0.5 { bind step = flat_adv(size) }
bind n = len(text)
bind i = 0
while i < n {
tile(str_slice(text, i, 1), x + i * step, y, rawsize, key, a)
bind i = i + 1
}
return 0
}
fn wordc(text, cx, y, size, key, a) {
word(text, cx - word_w(text, size) * 0.5, y, size, key, a)
return 0
}
fn wordr(text, xr, y, size, key, a) {
word(text, xr - word_w(text, size), y, size, key, a)
return 0
}
# Can English text be drawn at all? Yes with a loaded font (Consolas and friends on a
# desktop), and yes on LingOS (the kernel's 8x8 pixel font). A desktop host that found
# no English font has neither, so it keeps the Daemon text instead of drawing nothing.
fn eng_ok() { return st_get("fe", 0.0) >= 0.0 || st_get("os_win", 1.0) < 0.5 }
fn want_eng() { return st_get("p_txt", 1.0) >= 1.0 && eng_ok() }
fn want_eng2() { return st_get("p_txt", 1.0) >= 2.0 && eng_ok() }
# ---------- English text, for stats and readouts ----------
# Consolas on Windows. LingOS has no such file, and px_text draws NOTHING for a
# font that did not load, so there the English text falls back to the kernel's
# own 8x8 pixel font (px_text8): always present, instant, and the OS's UI face.
fn e8on() { return st_get("fe", 0.0) < 0.0 }
# the 8x8 glyph's integer scale for a word of this size
fn e8sc(size) {
bind s = round(tsize(size) * 0.123)
if s < 1.0 { return 1.0 }
return s
}
# Sized so its cap height roughly matches a tile word of the same size.
fn etext_px(rawsize) { return tsize(rawsize) * 1.35 }
fn etext_w(text, size) {
if e8on() { return len(text) * 8.0 * e8sc(size) }
return font_width(st_get("fe", 0.0), etext_px(size), text)
}
fn etext(text, x, y, size, key, a) {
if e8on() {
bind sc = e8sc(size)
px_text8(x, y + tsize(size) * 0.5 - 4.0 * sc, dim(pcol(key), a), text, sc)
return 0
}
bind fe = st_get("fe", 0.0)
bind gpx = etext_px(size)
bind gy = y + tsize(size) * 0.5 - gpx * st_get("voff_e", 0.45)
px_text(fe, x, gy, gpx, dim(pcol(key), a), text, 1.0)
return 0
}
fn etextc(text, cx, y, size, key, a) {
etext(text, cx - etext_w(text, size) * 0.5, y, size, key, a)
return 0
}
fn etextr(text, xr, y, size, key, a) {
etext(text, xr - etext_w(text, size), y, size, key, a)
return 0
}
# ---------- stats text: obeys the TXT switch ----------
# p_txt 0 = all Daemon/Chesilin · 1 = stats in English (default) ·
# p_txt 2 = everything in English
fn stext(text, x, y, size, key, a) {
if want_eng() {
etext(text, x, y, size, key, a)
} else {
flat(1.0)
word(text, x, y, size, key, a)
flat(0.0)
}
return 0
}
fn stextc(text, cx, y, size, key, a) {
if want_eng() {
etextc(text, cx, y, size, key, a)
} else {
flat(1.0)
wordc(text, cx, y, size, key, a)
flat(0.0)
}
return 0
}
fn stextr(text, xr, y, size, key, a) {
if want_eng() {
etextr(text, xr, y, size, key, a)
} else {
flat(1.0)
wordr(text, xr, y, size, key, a)
flat(0.0)
}
return 0
}
fn stext_w(text, size) {
if want_eng() { return etext_w(text, size) }
flat(1.0)
bind w = word_w(text, size)
flat(0.0)
return w
}
# ---------- ring text (Chesilin) ----------
# One glyph per annular cell between r0..r1, marching clockwise from phi0.
# mark >= 0 lights that cell (marquee). Glyphs stay upright and orbit.
fn ringtext(cx, cy, r0, r1, text, phi0, sweep, key, mark, a) {
bind c = pcol(key)
bind n = len(text)
if n < 1 { return 0 }
bind fc = st_get("fc", 1.0)
bind voffc = st_get("voff_c", 0.25)
bind fitc = st_get("fit_c", 0.96)
if st_get("p_txt", 1.0) >= 2.0 && st_get("fe", 0.0) >= 0.0 {
bind fc = st_get("fe", 1.0)
bind voffc = st_get("voff_e", 0.45)
bind fitc = 1.2
}
bind band = r1 - r0
bind rmid = (r0 + r1) * 0.5
bind cell = sweep / n
bind arcw = cell * rmid
bind gmax = band * 0.62 * fitc
setcola(c, 0.5 * a)
arcpx(cx, cy, r0, 0.0, 6.2831853)
arcpx(cx, cy, r1, 0.0, 6.2831853)
bind i = 0
while i < n {
bind pb = phi0 + cell * i
px_line(ckx(cx, r0 + 1.5, pb), cky(cy, r0 + 1.5, pb), ckx(cx, r1 - 1.5, pb), cky(cy, r1 - 1.5, pb), c, 0.2 * a)
bind i = i + 1
}
bind i = 0
while i < n {
bind ch = str_slice(text, i, 1)
bind phi = phi0 + cell * (i + 0.5)
if ch != " " {
bind gpx = gmax
bind w = font_width(fc, gpx, ch)
bind wlim = arcw * 0.7
if w > wlim {
if w > 0.0 {
bind gpx = gpx * wlim / w
}
}
if gpx < 7.0 { bind gpx = 7.0 }
bind w = font_width(fc, gpx, ch)
bind gx = ckx(cx, rmid, phi) - w * 0.5
bind gy = cky(cy, rmid, phi) - gpx * voffc
bind glow = 0.62
if i == mark { bind glow = 1.0 }
setcola(c, a * glow)
font_text(fc, gx, gy, gpx, ch)
font_text(fc, gx + 0.55, gy, gpx, ch)
}
bind i = i + 1
}
return 0
}
# ---------- segment ring: alternating hatched arc segments ----------
# phase animates it; neighbouring rings should turn opposite ways.
# den 1..4 sets stroke spacing: density as data.
fn segring(cx, cy, r0, r1, nseg, phase, key, den, a) {
bind c = pcol(key)
setcola(c, 0.5 * a)
arcpx(cx, cy, r0, 0.0, 6.2831853)
arcpx(cx, cy, r1, 0.0, 6.2831853)
bind rm = (r0 + r1) * 0.5
bind band = r1 - r0
bind sw = 6.2831853 / nseg
bind sp = band * 0.5 * den_mul(den) * 0.55
if sp < 3.5 { bind sp = 3.5 }
bind astep = sp / rm
bind tilt = band / rm
bind k = 0.0
while k < nseg {
bind p0 = phase + k * sw
px_line(ckx(cx, r0 + 1.0, p0), cky(cy, r0 + 1.0, p0), ckx(cx, r1 - 1.0, p0), cky(cy, r1 - 1.0, p0), c, 0.3 * a)
if (k % 2.0) == 0.0 {
bind s = p0 + astep * 0.6
bind pend = p0 + sw * 0.94
while s < pend {
px_line(ckx(cx, r0 + 1.5, s), cky(cy, r0 + 1.5, s), ckx(cx, r1 - 1.5, s + tilt), cky(cy, r1 - 1.5, s + tilt), c, 0.45 * a)
bind s = s + astep
}
}
bind k = k + 1.0
}
return 0
}
# ---------- dial parts ----------
fn dialbezel(cx, cy, r, key, a) {
bind c = pcol(key)
setcola(c, 0.95 * a)
arcpx(cx, cy, r, 0.0, 6.2831853)
arcpx(cx, cy, r - 1.0, 0.0, 6.2831853)
setcola(c, 0.6 * a)
arcpx(cx, cy, r - 9.0, 0.0, 6.2831853)
hatch_ring(cx, cy, r - 9.0, r - 1.0, c, 45.0, 5.0, 0.38 * a)
bind k = 0
while k < 60 {
bind phi = k * 0.10471976
bind tl = 4.0
if k % 12 == 0 { bind tl = 9.0 }
bind ri = r - 12.0 - tl
bind ro = r - 12.0
px_line(ckx(cx, ri, phi), cky(cy, ri, phi), ckx(cx, ro, phi), cky(cy, ro, phi), c, 0.6 * a)
bind k = k + 1
}
bind k = 0
while k < 4 {
bind phi = 0.7853982 + k * 1.5707963
bind dx = ckx(cx, r, phi)
bind dy = cky(cy, r, phi)
bind dpts = mkngon(dx, dy, 6.5, 4.0, 0.0)
poly_outline(dpts, c, a)
hatch_poly(dpts, c, 45.0, 2.2, 0.8 * a)
bind k = k + 1
}
return 0
}
fn hand(cx, cy, reach, phi, wid, key, a) {
bind c = pcol(key)
bind tipx = ckx(cx, reach, phi)
bind tipy = cky(cy, reach, phi)
bind bphi = phi + 1.5707963
bind bx = sin(bphi) * wid * 0.5
bind by = 0.0 - cos(bphi) * wid * 0.5
bind tailx = ckx(cx, 0.0 - reach * 0.18, phi)
bind taily = cky(cy, 0.0 - reach * 0.18, phi)
bind pts = list_new()
bind pts = list_push(pts, tipx)
bind pts = list_push(pts, tipy)
bind pts = list_push(pts, cx + bx)
bind pts = list_push(pts, cy + by)
bind pts = list_push(pts, tailx)
bind pts = list_push(pts, taily)
bind pts = list_push(pts, cx - bx)
bind pts = list_push(pts, cy - by)
poly_outline(pts, c, a)
hatch_poly(pts, c, 45.0, 3.2, 0.7 * a)
setcola(c, a)
arcpx(cx, cy, wid * 0.75, 0.0, 6.2831853)
px_disc(cx, cy, 1.6, c, a)
return 0
}
# ---------- connector with flowing packets ----------
# The dot uses the diamond: solid dot at the origin, open diamond at
# the destination, coloured by the source. kind 1 primary, 0 secondary.
fn connector(x0, y0, x1, y1, key, kind, a) {
bind c = pcol(key)
if kind == 1 {
thick(x0, y0, x1, y1, c, 0.8 * a)
} else {
dashline(x0, y0, x1, y1, c, 0.6 * a)
}
px_disc(x0, y0, 3.0, c, 0.9 * a)
bind dpts = mkngon(x1, y1, 5.0, 4.0, 0.0)
poly_outline(dpts, c, a)
return 0
}
# n packets travel origin -> destination at constant speed.
# Returns the number of arrivals since the last call (0..3), for sound.
fn packets(id, x0, y0, x1, y1, key, n, t, a) {
if n < 1 { return 0 }
bind c = pcol(key)
bind dx = x1 - x0
bind dy = y1 - y0
bind l = sqrt(dx * dx + dy * dy)
if l < 8.0 { return 0 }
bind spd = 62.0 * st_get("motion", 1.0)
bind base = t * spd / l
bind k = 0
while k < n {
bind u = base + k / n
bind u = u - floor(u)
bind qx = x0 + dx * u
bind qy = y0 + dy * u
px_outline(qx - 2.5, qy - 2.5, 5.0, 5.0, c, a)
px_rect(qx - 1.5, qy - 1.5, 3.0, 3.0, dim(c, 0.4), a)
bind k = k + 1
}
bind ph = floor(base * n)
bind prev = st_get("cph_" + id, ph)
st_set("cph_" + id, ph)
bind ev = ph - prev
if ev < 0.0 { bind ev = 0.0 }
if ev > 3.0 { bind ev = 3.0 }
return ev
}
# ---------- frame ----------
fn framebox(x, y, w, h, title, key, a) {
bind c = pcol(key)
px_outline(x, y, w, h, c, 0.9 * a)
px_outline(x + 1.0, y + 1.0, w - 2.0, h - 2.0, c, 0.5 * a)
px_outline(x + 8.0, y + 8.0, w - 16.0, h - 16.0, c, 0.55 * a)
px_hatch(x + 2.0, y + 2.0, w - 4.0, 6.0, c, 45.0, 7.0, 0.17 * a)
px_hatch(x + 2.0, y + h - 8.0, w - 4.0, 6.0, c, 45.0, 7.0, 0.17 * a)
px_hatch(x + 2.0, y + 8.0, 6.0, h - 16.0, c, 45.0, 7.0, 0.17 * a)
px_hatch(x + w - 8.0, y + 8.0, 6.0, h - 16.0, c, 45.0, 7.0, 0.17 * a)
bind arm = 24.0
thick(x, y, x + arm, y, c, a)
thick(x, y, x, y + arm, c, a)
thick(x + w - arm, y, x + w, y, c, a)
thick(x + w, y, x + w, y + arm, c, a)
thick(x, y + h - arm, x, y + h, c, a)
thick(x, y + h, x + arm, y + h, c, a)
thick(x + w, y + h - arm, x + w, y + h, c, a)
thick(x + w - arm, y + h, x + w, y + h, c, a)
if len(title) > 0 {
bind ts = 15.0
bind tw = word_w(title, ts) + 14.0
bind tx = x + 20.0
px_rect(tx, y - 11.0, tw, 22.0, 0.0, 1.0)
px_outline(tx, y - 11.0, tw, 22.0, c, 0.8 * a)
word(title, tx + 7.0, y - 7.0, ts, key, a)
}
return 0
}