3680 lines
200 KiB
Text
3680 lines
200 KiB
Text
use mod.prelude.widgets_internal.*
|
|
use mod.sdf.Cap
|
|
|
|
// Automotive: a racing dash, a deck of dark brushed steel between bands of
|
|
// carbon fibre. Satin graphite caps, chrome bezels round every instrument,
|
|
// wells of dark brushed steel behind silver frames, plates of lighter steel,
|
|
// neon tubes under chrome slider caps, a rainbow bar of light cells,
|
|
// tachometer dials with a red line, amber-lit gold tabs, a red safety cover
|
|
// over the Delete switch. Every face here is drawn in true
|
|
// points (one device pixel is `px`), and every shadow, lip and glow is drawn
|
|
// OUTSIDE the layout box: the quad is grown in `vertex`, the templates that
|
|
// throw a shadow stop clipping, and no padding is spent on relief.
|
|
|
|
// ---------------------------------------------------------------- helpers
|
|
|
|
// One device pixel in points.
|
|
let au_px = fn(dpi: float) -> float {
|
|
return 1.0 / max(dpi, 0.5)
|
|
}
|
|
|
|
// An edge rounded to a whole device pixel in window space, handed back in
|
|
// box space: a hairline on it lands on one pixel row.
|
|
let au_snap = fn(v: float, o: float, px: float) -> float {
|
|
return Finish.snap(o + v, px) - o
|
|
}
|
|
|
|
// Distance from p to the segment a..b, in points.
|
|
let au_seg = fn(p: vec2, a: vec2, b: vec2) -> float {
|
|
let pa = p - a
|
|
let ba = b - a
|
|
let t = clamp(dot(pa, ba) / max(dot(ba, ba), 0.0001), 0.0, 1.0)
|
|
return length(pa - ba * t)
|
|
}
|
|
|
|
// A hash that does not line up: the library's hash nearly repeats every 97
|
|
// cells, so each cell is first sent to a far cell picked by its own hash.
|
|
let au_hash = fn(i: vec2) -> float {
|
|
let j = i + vec2(Finish.hash(i) * 419.0, Finish.hash(i + vec2(5.31, 9.17)) * 263.0)
|
|
return Finish.hash(j)
|
|
}
|
|
// Value noise on that hash, one unit per cell.
|
|
let au_noise = fn(q: vec2) -> float {
|
|
let i = floor(q)
|
|
let f = q - i
|
|
let u = f * f * (vec2(3.0, 3.0) - f * 2.0)
|
|
let a = au_hash(i)
|
|
let b = au_hash(i + vec2(1.0, 0.0))
|
|
let c = au_hash(i + vec2(0.0, 1.0))
|
|
let d = au_hash(i + vec2(1.0, 1.0))
|
|
return mix(mix(a, b, u.x), mix(c, d, u.x), u.y)
|
|
}
|
|
// Horizontal brush streaks, -0.5..0.5: fine lines `s` points apart across,
|
|
// long along, over a slower wander so no two strokes run the same.
|
|
let au_brush = fn(q: vec2, s: float) -> float {
|
|
let a = au_noise(vec2(q.x / (s * 60.0), q.y / s))
|
|
let b = au_noise(vec2(q.x / (s * 260.0) + 3.1, q.y / (s * 5.0) + 7.7))
|
|
return a * 0.7 + b * 0.3 - 0.5
|
|
}
|
|
// Grain, one value per device pixel, sd about 0.004 per 0.01 of `amt`.
|
|
let au_grain_at = fn(q: vec2, px: float, amt: float) -> float {
|
|
let i = floor(q / px)
|
|
return (au_hash(i) - au_hash(i + vec2(17.13, 3.71))) * amt
|
|
}
|
|
// A brushed-metal plate's own texture, in the plate's coordinates so it
|
|
// moves with it: fine streaks across, the unrepeating grain over them.
|
|
let au_plate_tex = fn(p: vec2, px: float) -> float {
|
|
return au_brush(p, 0.7) * 0.030 + au_grain_at(p, px, 0.010)
|
|
}
|
|
|
|
// THE WEAVE. A two-over-two twill of tows `s` points wide, as carbon fibre
|
|
// lies under its resin: every tow floats over two cells, round across and
|
|
// dipping where it dives under at either end. Returned: the float's
|
|
// brightness 0..1 (its roundness, its dip, its fibres and a shade of its
|
|
// own), whether it runs across (1) or down (0), and how near its crown the
|
|
// point lies.
|
|
let au_weave = fn(w: vec2, s: float) -> vec3 {
|
|
let q = w / s
|
|
let cell = floor(q)
|
|
let f = q - cell
|
|
let k = cell.x + cell.y
|
|
let n = k - 4.0 * floor(k * 0.25)
|
|
let horiz = step(n, 1.5)
|
|
let run = mix(n - 2.0, n, horiz)
|
|
let across = mix(f.x, f.y, horiz)
|
|
let along = (run + mix(f.y, f.x, horiz)) * 0.5
|
|
let first = cell - mix(vec2(0.0, run), vec2(run, 0.0), horiz)
|
|
let round = sin(clamp(across, 0.0, 1.0) * PI)
|
|
let dip = smoothstep(0.0, 0.17, along) * smoothstep(0.0, 0.17, 1.0 - along)
|
|
let own = au_hash(first + vec2(horiz * 31.0, 7.0))
|
|
let fib = au_noise(vec2(along * 3.0 + first.x * 1.7, across * 3.5 + first.y * 2.3))
|
|
let b = pow(round, 0.75) * (0.50 + 0.50 * dip) * (0.84 + 0.16 * fib) * (0.82 + 0.32 * own)
|
|
return vec3(b, horiz, round * dip)
|
|
}
|
|
// Carbon fibre at window point `w`: the tows that run across catch the light
|
|
// and the ones that run down stay in shade, so the weave reads as rising
|
|
// diagonal ribs; a lit float carries a highlight along its crown. `lift`
|
|
// scales the light (a well's weave lies deeper than the ground's).
|
|
let au_carbon = fn(w: vec2, s: float, lift: float) -> vec3 {
|
|
let v = au_weave(w, s)
|
|
let lit = mix(0.38, 1.0, v.y)
|
|
let crown = pow(v.z, 5.0) * v.y
|
|
// The light lies along the ribs: brightest down the middle of each
|
|
// rising band of lit floats, so every float shades off toward its far
|
|
// corners and the bands read as chains of diamonds.
|
|
let rib = 0.5 + 0.5 * cos(2.0 * PI * ((w.x + w.y) / s - 1.5) * 0.25)
|
|
let c = vec3(0.011, 0.012, 0.014) + (vec3(0.150, 0.154, 0.164) * v.x * lit + vec3(0.050, 0.052, 0.056) * crown * v.x) * lift * (0.50 + 0.50 * rib)
|
|
return c
|
|
}
|
|
|
|
// BRUSHED METAL at point `w` (points): every device-pixel row its own
|
|
// streaks, long ones and short fine ones, changing from row to row, so the
|
|
// grain stays crisp at the device pixel however it is scaled; a rare bright
|
|
// scratch along a row; under them a slower drift of a few rows at a time
|
|
// and a grain of single pixels. Nothing is tiled: the streaks come from the
|
|
// unaligned hash. -0.6..0.6 about.
|
|
let au_brushed = fn(w: vec2, px: float) -> float {
|
|
let row = floor(w.y / px)
|
|
let a = au_noise(vec2(w.x / 70.0 + au_hash(vec2(row, 3.0)) * 40.0, row))
|
|
let f = au_noise(vec2(w.x / 13.0 + au_hash(vec2(row, 11.0)) * 90.0, row + 517.0))
|
|
let b = au_noise(vec2(w.x / 380.0 + 7.0, w.y / (px * 9.0)))
|
|
let c = au_hash(vec2(floor(w.x / px), row)) - 0.5
|
|
let seg = floor(w.x / 50.0 + au_hash(vec2(row, 7.0)) * 3.0)
|
|
let scr = step(0.993, au_hash(vec2(row, seg))) * smoothstep(0.0, 0.3, au_noise(vec2(w.x / 9.0, row + 91.0)))
|
|
return (a - 0.5) * 0.80 + (f - 0.5) * 0.50 + (b - 0.5) * 0.22 + c * 0.18 + scr * 0.9
|
|
}
|
|
|
|
// The deck the controls stand on: charcoal steel, #2a2c30 at its darkest to
|
|
// #33363b where the light lies, brushed across in fine crisp streaks, with
|
|
// a broad soft sheen running down from the upper left and a slow cloud of
|
|
// light over it. `ww`, `wh` the window's size; `w` a window point.
|
|
let au_deck = fn(w: vec2, px: float, ww: float, wh: float) -> vec3 {
|
|
let u = w.x / max(ww, 1.0)
|
|
let v = w.y / max(wh, 1.0)
|
|
let s = u - 0.38 + 0.45 * (v - 0.4)
|
|
let sheen = exp(-s * s / 0.07)
|
|
let cloud = au_noise(w / 260.0 + vec2(5.0, 1.0)) - 0.5
|
|
let lv = 0.168 + 0.030 * sheen + 0.010 * cloud
|
|
let br = au_brushed(w, px) * 0.040
|
|
let k = lv + br
|
|
return vec3(k, k * 1.045, k * 1.13)
|
|
}
|
|
|
|
// A PLATE OF STEEL lighter than the deck, at point `p` of the plate (so it
|
|
// moves with it), `t` 0..1 down it and `u` 0..1 across: #535659 under its
|
|
// top edge falling to #43464a at its foot, brushed across in the deck's
|
|
// crisp streaks, a broad soft sheen sweeping down from the upper middle.
|
|
// Held under #555 everywhere, so light words keep 5:1 on it.
|
|
let au_steel = fn(p: vec2, px: float, t: float, u: float) -> vec3 {
|
|
let k0 = mix(0.312, 0.262, smoothstep(0.0, 1.0, t))
|
|
let s = u - 0.62 + 0.55 * t
|
|
let sheen = exp(-s * s / 0.05) * (1.0 - 0.5 * t)
|
|
let k = min(k0 + 0.020 * sheen + au_brushed(p, px) * 0.036, 0.334)
|
|
return vec3(k, k * 1.03, k * 1.075)
|
|
}
|
|
// A dark well of the same brushed steel, for the floors of fields.
|
|
let au_dark_steel = fn(p: vec2, px: float, lift: float) -> vec3 {
|
|
let k = 0.082 + 0.022 * lift + au_brushed(p, px) * 0.032
|
|
return vec3(k, k * 1.04, k * 1.12)
|
|
}
|
|
|
|
// A HEXAGON MESH at point `m` (points, the mesh's own origin a hole's
|
|
// middle): hexagonal holes `s` points apart on a hex lattice, each hole's
|
|
// flat sides left and right, in a web `web` points wide. The web is `webc`
|
|
// steel; its edge round each hole catches the light where it faces up (the
|
|
// rim over the hole) and falls dark where it faces down; down in a hole
|
|
// the lower wall shows a little light. Crisp at the device pixel.
|
|
let au_mesh = fn(m: vec2, s: float, web: float, px: float, webc: vec3, lit: float) -> vec3 {
|
|
let r = vec2(s, s * 1.7320508)
|
|
let hh = r * 0.5
|
|
let a = m - r * floor(m / r) - hh
|
|
let b0 = m - hh
|
|
let b = b0 - r * floor(b0 / r) - hh
|
|
var q = b
|
|
if dot(a, a) < dot(b, b) {
|
|
q = a
|
|
}
|
|
let aq = abs(q)
|
|
let dh = max(aq.x, dot(aq, vec2(0.5, 0.8660254)))
|
|
let ap = s * 0.5 - web * 0.5
|
|
let d = dh - ap
|
|
let facing = -q.y / max(length(q), 0.001)
|
|
var wc = webc
|
|
let rim = Finish.ring_out(d, 0.0, px, px)
|
|
let rim2 = Finish.ring_out(d, px, px * 2.0, px)
|
|
wc = mix(wc, min(webc * 1.9 + vec3(0.10, 0.10, 0.105), vec3(1.0, 1.0, 1.0)), rim * smoothstep(0.15, 0.85, facing) * lit)
|
|
wc = mix(wc, min(webc * 1.35 + vec3(0.03, 0.03, 0.03), vec3(1.0, 1.0, 1.0)), rim2 * smoothstep(0.3, 0.9, facing) * lit * 0.6)
|
|
wc = mix(wc, webc * 0.45, rim * smoothstep(0.15, 0.85, -facing))
|
|
let floor_lit = smoothstep(0.2, 1.0, q.y / max(ap, 0.1))
|
|
let hc = vec3(0.010, 0.010, 0.012) + vec3(0.050, 0.051, 0.054) * floor_lit * lit
|
|
return mix(wc, hc, Finish.cover(d, px))
|
|
}
|
|
|
|
// The window ground at a window point `w` (points) in a window `ww` by `wh`
|
|
// points. The deck of brushed steel takes the middle of the window; the
|
|
// carbon weave shows only in a band along the top (under the caption and
|
|
// the tool bar) and a band along the bottom. The deck stands a little proud
|
|
// of the weave: its corners are cut at 45 degrees, its top edge is a lit
|
|
// bevel two device pixels deep over a black seam, its foot a dark bevel
|
|
// over the black seam, and it throws a short shadow down onto the weave
|
|
// under it, gone within six points. Nothing here moves with a scrolled view.
|
|
let au_ground = fn(w: vec2, px: float, ww: float, wh: float) -> vec3 {
|
|
let top = Finish.snap(88.0, px)
|
|
let bot = Finish.snap(wh - min(215.0, 0.172 * wh), px)
|
|
let c = vec2(ww * 0.5, (top + bot) * 0.5)
|
|
let h = vec2(ww * 0.5 + px * 0.5, (bot - top) * 0.5)
|
|
let d = Finish.sd_chamfer(w, c, h, 22.0)
|
|
// The weave, under a slow light lying toward the top of the window.
|
|
let dx = (w.x - ww * 0.42) / max(ww * 0.55, 1.0)
|
|
let cloud = au_noise(w / 190.0) * 0.6 + au_noise(w / 63.0 + vec2(11.0, 3.0)) * 0.4
|
|
let light = exp(-dx * dx) * (0.85 + 0.35 * cloud)
|
|
var cb = au_carbon(w, 4.2, light)
|
|
let g = au_grain_at(w, px, 0.006)
|
|
cb = cb + vec3(g, g, g)
|
|
// The deck's shadow on the weave: under its foot and its lower cuts.
|
|
let sh = Finish.drop(w, c + vec2(0.0, 2.0), h, 2.0, vec2(0.0, 0.0), 2.6) * 0.7
|
|
cb = cb * (1.0 - sh * step(c.y, w.y))
|
|
// The deck and its bevel, lit by the way its edge faces.
|
|
var dk = au_deck(w, px, ww, wh)
|
|
let e = px * 0.5
|
|
let gx = Finish.sd_chamfer(w + vec2(e, 0.0), c, h, 22.0) - Finish.sd_chamfer(w - vec2(e, 0.0), c, h, 22.0)
|
|
let gy = Finish.sd_chamfer(w + vec2(0.0, e), c, h, 22.0) - Finish.sd_chamfer(w - vec2(0.0, e), c, h, 22.0)
|
|
let n = vec2(gx, gy) / max(length(vec2(gx, gy)), 0.0001)
|
|
let up = smoothstep(0.2, 0.9, dot(n, vec2(-0.30, -0.95)))
|
|
let down = smoothstep(0.2, 0.9, n.y)
|
|
let r1 = Finish.band(d, px, px * 2.0, px)
|
|
let r2 = Finish.band(d, px * 2.0, px * 3.0, px)
|
|
let r3 = Finish.band(d, px * 3.0, px * 4.0, px)
|
|
dk = mix(dk, vec3(0.540, 0.552, 0.575), r1 * up)
|
|
dk = mix(dk, vec3(0.380, 0.390, 0.410), r2 * up)
|
|
dk = mix(dk, vec3(0.270, 0.278, 0.295), r3 * up)
|
|
dk = mix(dk, vec3(0.085, 0.088, 0.095), (r1 + r2) * down)
|
|
dk = mix(dk, vec3(0.120, 0.124, 0.132), r3 * down)
|
|
dk = mix(dk, vec3(0.006, 0.006, 0.008), Finish.band(d, 0.0, px, px))
|
|
return mix(cb, dk, Finish.cover(d, px))
|
|
}
|
|
|
|
// The room a chrome bezel mirrors, fixed on the screen so the pattern stands
|
|
// still as a knob turns: grey walls, pale toward the viewer, darker at the
|
|
// height of the bezel's middle, paler overhead than below; a big soft box up
|
|
// and to the left and a bright window down and to the right; a dark doorway
|
|
// on either side; a pale lamp low down and a dim shelf up and to the left.
|
|
// Each shape is an oval with a sharp edge, so on a turned band it lies as a
|
|
// crisp crescent, as reflections lie in polished chrome. `r` the mirrored
|
|
// direction (x right, y down, z toward the viewer); `wide` 1 on a band wide
|
|
// enough for the detail, 0 on a narrow one.
|
|
let au_room_patch = fn(a: float, z: float, a0: float, wa: float, z0: float, wz: float, soft: float) -> float {
|
|
let da = atan2(sin(a - a0), cos(a - a0)) / wa
|
|
let dz = (z - z0) / wz
|
|
return 1.0 - smoothstep(1.0 - soft, 1.0, da * da + dz * dz)
|
|
}
|
|
let au_chrome_room = fn(r: vec3, wide: float) -> vec3 {
|
|
let a = atan2(r.y, r.x)
|
|
let z = r.z
|
|
let k = mix(0.45, 0.25, wide)
|
|
var w = mix(0.36, 0.66, smoothstep(0.0, 0.6, z))
|
|
w = mix(w, 0.52, 1.0 - smoothstep(-0.70, -0.35, z))
|
|
var c = vec3(w, w + 0.005, w + 0.015) + vec3(0.07, 0.07, 0.07) * clamp(-r.y, -1.0, 1.0)
|
|
c = mix(c, vec3(1.0, 1.0, 1.0), au_room_patch(a, z, -2.25, 0.62, 0.05, 0.50, k))
|
|
c = mix(c, vec3(0.96, 0.96, 0.95), au_room_patch(a, z, 0.80, 0.55, -0.15, 0.55, k))
|
|
c = mix(c, vec3(0.05, 0.05, 0.055), au_room_patch(a, z, 0.0, 0.42, -0.30, 0.55, k))
|
|
c = mix(c, vec3(0.08, 0.08, 0.09), au_room_patch(a, z, 3.14159265, 0.50, 0.25, 0.60, k))
|
|
c = mix(c, vec3(0.70, 0.71, 0.73), au_room_patch(a, z, 1.80, 0.40, 0.30, 0.42, k) * wide)
|
|
c = mix(c, vec3(0.40, 0.41, 0.43), au_room_patch(a, z, -2.80, 0.22, 0.45, 0.30, k) * wide)
|
|
return c * mix(1.0, 0.9, smoothstep(0.55, 0.90, z))
|
|
}
|
|
|
|
// A polished chrome ring round `c` from `r0` to `r1`: the band turns from
|
|
// facing the viewer at its inner edge to facing out at its outer edge, and
|
|
// mirrors the room along that turn; a black line at either edge.
|
|
// Premultiplied.
|
|
let au_chrome_ring = fn(p: vec2, c: vec2, r0: float, r1: float, px: float, bright: float) -> vec4 {
|
|
let q = p - c
|
|
let rl = length(q)
|
|
let nk = q / max(rl, 0.001)
|
|
let w = max(r1 - r0, px)
|
|
let u = clamp((rl - r0) / w, 0.0, 1.0)
|
|
let wide = smoothstep(3.0, 9.0, w / px)
|
|
let th = mix(mix(0.62, 0.30, wide), mix(0.98, 1.20, wide), u)
|
|
var col = au_chrome_room(vec3(nk * sin(2.0 * th), cos(2.0 * th)), wide)
|
|
col = min(col * (1.0 + 0.10 * bright) + vec3(0.04, 0.04, 0.04) * bright, vec3(1.0, 1.0, 1.0))
|
|
let d = abs(rl - (r0 + r1) * 0.5) - w * 0.5
|
|
col = mix(col, vec3(0.02, 0.02, 0.025), Finish.band(d, 0.0, px, px))
|
|
let a = Finish.cover(d, px)
|
|
return vec4(col * a, a)
|
|
}
|
|
|
|
// The five neon colours of the slider tubes: blue, green, yellow, orange,
|
|
// purple, picked by `k` round the five.
|
|
let au_neon = fn(k: float) -> vec3 {
|
|
let i = k - 5.0 * floor(k / 5.0 + 0.001)
|
|
if i < 0.5 {
|
|
return vec3(0.22, 0.80, 1.0)
|
|
}
|
|
if i < 1.5 {
|
|
return vec3(0.22, 1.0, 0.46)
|
|
}
|
|
if i < 2.5 {
|
|
return vec3(1.0, 0.93, 0.24)
|
|
}
|
|
if i < 3.5 {
|
|
return vec3(1.0, 0.56, 0.18)
|
|
}
|
|
return vec3(0.80, 0.34, 1.0)
|
|
}
|
|
// Every slider's tube burns the same neon blue. A colour picked from where
|
|
// a slider stands would change as its view scrolls, and a slider has
|
|
// nothing else to tell it apart from its neighbours; the rainbow is kept
|
|
// for the feedback bars.
|
|
let au_neon_at = fn(o: vec2) -> vec3 {
|
|
return au_neon(0.0)
|
|
}
|
|
|
|
// The rainbow of the feedback bars at `t` 0..1: violet, blue, cyan, green,
|
|
// yellow, orange.
|
|
let au_rainbow = fn(t: float) -> vec3 {
|
|
let x = clamp(t, 0.0, 1.0) * 5.0
|
|
let a = vec3(0.70, 0.30, 1.0)
|
|
let b = vec3(0.24, 0.48, 1.0)
|
|
let c = vec3(0.16, 0.90, 1.0)
|
|
let d = vec3(0.26, 1.0, 0.36)
|
|
let e = vec3(1.0, 0.92, 0.22)
|
|
let f = vec3(1.0, 0.56, 0.16)
|
|
var col = mix(a, b, clamp(x, 0.0, 1.0))
|
|
col = mix(col, c, clamp(x - 1.0, 0.0, 1.0))
|
|
col = mix(col, d, clamp(x - 2.0, 0.0, 1.0))
|
|
col = mix(col, e, clamp(x - 3.0, 0.0, 1.0))
|
|
return mix(col, f, clamp(x - 4.0, 0.0, 1.0))
|
|
}
|
|
|
|
// 1 when bit `b` (a power of two) of `m` is set.
|
|
let au_bit = fn(m: float, b: float) -> float {
|
|
return step(0.25, fract(floor(m / b) * 0.5))
|
|
}
|
|
// A gauge numeral: the distance to the centre line of digit `d` drawn as a
|
|
// squared dash type, in glyph units (0.56 wide, 1 tall, top at 0). The
|
|
// strokes meet in round joints; the one, the four and the seven are drawn
|
|
// as type draws them, not as segments.
|
|
let au_glyph = fn(q: vec2, d: float) -> float {
|
|
let n = floor(d + 0.5)
|
|
let tl = vec2(0.0, 0.0)
|
|
let tr = vec2(0.52, 0.0)
|
|
let ml = vec2(0.0, 0.5)
|
|
let mr = vec2(0.52, 0.5)
|
|
let bl = vec2(0.0, 1.0)
|
|
let br = vec2(0.52, 1.0)
|
|
var m = 63.0
|
|
if n > 1.5 {
|
|
m = 91.0
|
|
}
|
|
if n > 2.5 {
|
|
m = 79.0
|
|
}
|
|
if n > 4.5 {
|
|
m = 109.0
|
|
}
|
|
if n > 5.5 {
|
|
m = 125.0
|
|
}
|
|
if n > 7.5 {
|
|
m = 127.0
|
|
}
|
|
if n > 8.5 {
|
|
m = 111.0
|
|
}
|
|
var r = 10.0
|
|
r = min(r, au_seg(q, tl, tr) + (1.0 - au_bit(m, 1.0)) * 10.0)
|
|
r = min(r, au_seg(q, tr, mr) + (1.0 - au_bit(m, 2.0)) * 10.0)
|
|
r = min(r, au_seg(q, mr, br) + (1.0 - au_bit(m, 4.0)) * 10.0)
|
|
r = min(r, au_seg(q, bl, br) + (1.0 - au_bit(m, 8.0)) * 10.0)
|
|
r = min(r, au_seg(q, ml, bl) + (1.0 - au_bit(m, 16.0)) * 10.0)
|
|
r = min(r, au_seg(q, tl, ml) + (1.0 - au_bit(m, 32.0)) * 10.0)
|
|
r = min(r, au_seg(q, ml, mr) + (1.0 - au_bit(m, 64.0)) * 10.0)
|
|
if n > 0.5 && n < 1.5 {
|
|
r = min(au_seg(q, vec2(0.30, 0.0), vec2(0.30, 1.0)), au_seg(q, vec2(0.30, 0.0), vec2(0.08, 0.20)))
|
|
}
|
|
if n > 3.5 && n < 4.5 {
|
|
r = min(min(au_seg(q, vec2(0.40, 0.0), vec2(0.40, 1.0)), au_seg(q, vec2(0.40, 0.0), vec2(0.0, 0.66))), au_seg(q, vec2(0.0, 0.66), vec2(0.56, 0.66)))
|
|
}
|
|
if n > 6.5 && n < 7.5 {
|
|
r = min(au_seg(q, tl, tr), au_seg(q, tr, vec2(0.16, 1.0)))
|
|
}
|
|
return r
|
|
}
|
|
// A number 0..99 set at `c` (its middle), `h` points tall, leaning like
|
|
// dash type; the distance to its strokes in points less half the stroke.
|
|
let au_number = fn(p: vec2, c: vec2, h: float, v: float, stroke: float) -> float {
|
|
let n = floor(v + 0.5)
|
|
let two = step(9.5, n)
|
|
let gw = 0.56 * h
|
|
let gap = 0.22 * h
|
|
let tw = gw + two * (gw + gap)
|
|
var q = (p - c + vec2(tw * 0.5, h * 0.5)) / h
|
|
q = vec2(q.x - (0.5 - q.y) * 0.16, q.y)
|
|
let tens = floor(n / 10.0)
|
|
let ones = n - tens * 10.0
|
|
var d = au_glyph(q - vec2(two * (0.56 + 0.22), 0.0), ones)
|
|
if two > 0.5 {
|
|
d = min(d, au_glyph(q, tens))
|
|
}
|
|
return d * h - stroke * 0.5
|
|
}
|
|
|
|
// A raised graphite cap: the drop and contact shadows under it, the black
|
|
// outline, the satin face falling from the light at the top, the soft top
|
|
// edge, the faint side light, the bottom rim. `lift` 1 casts the drop
|
|
// shadow, 0 seats the cap on the ground with the contact shadow only. `rim`
|
|
// is the bottom rim's ink. Premultiplied.
|
|
let au_cap = fn(p: vec2, lo: vec2, hi: vec2, r: float, px: float, hover: float, down: float, disabled: float, focus: float, lift: float, tint: vec3, rim: vec4, gloss: float) -> vec4 {
|
|
let c = (lo + hi) * 0.5
|
|
let h = (hi - lo) * 0.5
|
|
let hh = hi.y - lo.y
|
|
let sh_h = min(hh, 24.0)
|
|
let d = Material.sd_box(p, c, h, r)
|
|
let up = (1.0 - down) * (1.0 - disabled)
|
|
let small = 1.0 - step(23.5, hh)
|
|
|
|
// What the cap throws: a dense shadow under its foot over a soft fall
|
|
// gone within half an 8 point gap to the sides.
|
|
let drop1 = Finish.drop(p, c, h - vec2(0.06 * sh_h, 0.0), r, vec2(0.0, 0.06 * sh_h), 0.05 * sh_h) * 0.92
|
|
let drop2 = Finish.drop(p, c, h - vec2(0.14 * sh_h, 0.02 * sh_h), r, vec2(0.0, 0.12 * sh_h), 0.15 * sh_h) * 0.8
|
|
let drop = (1.0 - (1.0 - drop1) * (1.0 - drop2)) * lift * up
|
|
let contact = Finish.drop(p, c, h, r, vec2(0.0, px), px * 0.6) * 0.9 * (1.0 - 0.6 * disabled)
|
|
let sh = 1.0 - (1.0 - drop) * (1.0 - contact)
|
|
var under = vec4(0.0, 0.0, 0.0, sh)
|
|
// A held cap sits in the ground: a 1 dp lip shows under its foot.
|
|
let lip = Finish.ring_out(d, 0.0, px, px) * step(c.y + h.y * 0.5, p.y) * down * (1.0 - disabled)
|
|
under = Finish.over(under, vec4(0.30, 0.31, 0.33, 1.0) * lip)
|
|
|
|
// The face: satin graphite, #45474b under the top edge falling to
|
|
// #222326 at the foot, brushed across.
|
|
let t = clamp((p.y - lo.y) / max(hh, 1.0), 0.0, 1.0)
|
|
let ft = 1.0 - pow(1.0 - t, 1.4)
|
|
var face = mix(vec3(0.270, 0.276, 0.290), vec3(0.133, 0.137, 0.148), ft) + tint
|
|
let tx = au_plate_tex(p, px)
|
|
face = face + vec3(tx, tx, tx) * 0.7 * up
|
|
face = face + vec3(0.045, 0.046, 0.049) * (1.0 + small) * hover * up
|
|
// Held or chosen: sunk, a floor that falls a little and a shade under
|
|
// the top edge.
|
|
let shade = 0.6 * (1.0 - smoothstep(0.0, 0.24 * min(hh, 24.0), p.y - lo.y - px))
|
|
let held = mix(mix(vec3(0.098, 0.100, 0.108), vec3(0.074, 0.076, 0.082), t) + tint, vec3(0.0, 0.0, 0.0), shade)
|
|
face = mix(face, held, down * (1.0 - disabled))
|
|
face = mix(face, mix(vec3(0.115, 0.117, 0.124), vec3(0.090, 0.092, 0.098), t), disabled)
|
|
|
|
let s = vec2(step(0.0, p.x - c.x) * 2.0 - 1.0, step(0.0, p.y - c.y) * 2.0 - 1.0)
|
|
let q = abs(p - c) - (h - vec2(r, r))
|
|
let qc = max(q, vec2(0.0, 0.0))
|
|
var n = vec2(0.0, s.y)
|
|
if q.x > q.y {
|
|
n = vec2(s.x, 0.0)
|
|
}
|
|
if q.x > 0.0 && q.y > 0.0 {
|
|
n = qc / max(length(qc), 0.0001) * s
|
|
}
|
|
let r1 = Finish.band(d, px, px * 2.0, px)
|
|
let r2 = Finish.band(d, px * 2.0, px * 3.0, px)
|
|
// A satin sheen over the upper part of the face, and a soft bloom on
|
|
// its upper left where the light lands.
|
|
let uv = clamp((p - lo) / max(hi - lo, vec2(1.0, 1.0)), vec2(0.0, 0.0), vec2(1.0, 1.0))
|
|
face = face + vec3(0.036, 0.037, 0.040) * Finish.sheen(uv, 0.0, 0.5, 0.25) * up * (0.6 + 0.4 * gloss)
|
|
let sw = max(min(0.30 * (hi.x - lo.x), 20.0), 2.0)
|
|
let sq = (p - vec2(lo.x + min(0.24 * (hi.x - lo.x), 16.0), lo.y + 0.10 * hh)) / vec2(sw, max(0.24 * hh, 1.5))
|
|
face = face + vec3(0.050, 0.051, 0.054) * exp(-dot(sq, sq)) * up
|
|
// The edge: a light row along the top (#8a8c90), half of it under, a
|
|
// white catch on the top-left corner, a quieter line down the sides.
|
|
let topw = smoothstep(0.45, 0.97, -n.y)
|
|
let botw = smoothstep(0.45, 0.97, n.y)
|
|
let sidew = smoothstep(0.6, 1.0, abs(n.x))
|
|
let hot = mix(vec3(0.500, 0.508, 0.528), vec3(0.600, 0.608, 0.626), max(gloss, hover))
|
|
face = mix(face, hot, r1 * topw * up)
|
|
face = mix(face, hot * 0.6, r2 * topw * up * 0.6)
|
|
let nl = dot(n, vec2(-0.62, -0.78))
|
|
face = mix(face, vec3(0.86, 0.87, 0.89), r1 * smoothstep(0.93, 0.995, nl) * up * 0.8)
|
|
let sidec = mix(vec3(0.330, 0.336, 0.352), vec3(0.180, 0.184, 0.196), t)
|
|
face = mix(face, sidec, r1 * sidew * (0.35 + 0.65 * up))
|
|
face = mix(face, rim.rgb, (r1 + 0.35 * r2) * botw * rim.a * (1.0 - disabled))
|
|
let ol = mix(vec3(0.024, 0.024, 0.028), vec3(0.008, 0.008, 0.010), t)
|
|
face = mix(face, mix(ol, vec3(0.030, 0.031, 0.034), disabled), Finish.band(d, 0.0, px, px))
|
|
|
|
let a = Finish.cover(d, px) * (1.0 - 0.25 * disabled)
|
|
var o = Finish.over(under, vec4(face * a, a))
|
|
// Focus: a thin blue ring just outside the outline, and a breath of it.
|
|
let ring = Finish.ring_out(d, 0.0, px, px) * 0.9 + Finish.glow(d - px, 0.7) * 0.3 * (1.0 - Finish.cover(d, px))
|
|
o = Finish.over(o, vec4(0.227, 0.612, 1.0, 1.0) * clamp(ring, 0.0, 1.0) * focus * (1.0 - disabled))
|
|
return o
|
|
}
|
|
|
|
// An LED bar or dot laid over `dst`: a white-hot core, a 1 dp saturated rim
|
|
// and a short halo when lit (`halo` scales it); a dark lens when not (a bar:
|
|
// one dark glass). `hs` is the half size; the ends are round.
|
|
let au_led = fn(dst: vec4, p: vec2, c: vec2, hs: vec2, lit: float, halo: float, px: float, core: vec3, rimc: vec3) -> vec4 {
|
|
let d = Finish.sd_pill(p, c, hs)
|
|
let slot = Finish.cover(d, px)
|
|
let tl = clamp((p.y - (c.y - hs.y)) / max(2.0 * hs.y, px), 0.0, 1.0)
|
|
var lens = mix(vec3(0.150, 0.152, 0.165), vec3(0.060, 0.062, 0.068), smoothstep(0.2, 0.9, tl))
|
|
lens = mix(lens, rimc * 0.22, 0.25)
|
|
let sp = p - (c + vec2(-0.3 * hs.x, -0.35 * hs.y))
|
|
lens = lens + vec3(0.16, 0.16, 0.17) * exp(-dot(sp, sp) / max(2.0 * 0.09 * hs.y * hs.y, 0.001)) * step(1.5, hs.y)
|
|
let bar = step(hs.y * 2.5, hs.x)
|
|
let gc = c + vec2(0.0, px * 0.5)
|
|
let gd = Finish.sd_pill(p, gc, vec2(hs.x + px * 0.5, px * 1.5))
|
|
let gt = clamp((p.y - (gc.y - px * 1.5)) / (px * 3.0), 0.0, 1.0)
|
|
var glass = mix(vec3(0.110, 0.112, 0.122), vec3(0.070, 0.072, 0.078), gt)
|
|
glass = mix(glass, rimc, 0.08) + vec3(0.03, 0.03, 0.03) * (1.0 - step(gc.y - px * 0.5, p.y))
|
|
var o = Finish.over(dst, vec4(0.016, 0.016, 0.020, 1.0) * Finish.ring_out(d, 0.0, px, px) * 0.55 * (1.0 - lit) * (1.0 - bar))
|
|
o = Finish.over(o, vec4(lens, 1.0) * slot * (1.0 - lit) * (1.0 - bar))
|
|
o = Finish.over(o, vec4(glass, 1.0) * Finish.cover(gd, px) * (1.0 - lit) * bar)
|
|
let x = max(d - px, 0.0)
|
|
let g = (0.45 * exp(-(x * x) / (2.0 * 0.5 * 0.5)) + 0.22 * exp(-(x * x) / (2.0 * 1.4 * 1.4))) * (1.0 - slot)
|
|
o = Finish.over(o, vec4(rimc, 1.0) * g * 0.8 * halo * lit)
|
|
let rim = Finish.ring_out(d, 0.0, px, px)
|
|
o = Finish.over(o, vec4(rimc, 1.0) * rim * lit)
|
|
let hot = 1.0 - smoothstep(0.0, hs.y * 1.2, p.y - (c.y - hs.y))
|
|
let col = mix(core, vec3(1.0, 1.0, 1.0), hot * 0.5)
|
|
o = Finish.over(o, vec4(col, 1.0) * slot * lit)
|
|
return o
|
|
}
|
|
|
|
// A lamp lying in the bottom rim of a lit cap: a white-hot bar three device
|
|
// pixels tall and `hw` half wide whose last row is `yb`, the glow's colour
|
|
// on the face round it, a pale halo hugging the bar, and a spill that runs
|
|
// out past the outline onto the ground under the cap. `inside` is the cap's
|
|
// cover, `below` 1 on the ground under it. Premultiplied over `dst`.
|
|
let au_rim_lamp = fn(dst: vec4, p: vec2, cx: float, hw: float, yb: float, px: float, lit: float, halo: float, core: vec3, glow: vec3, inside: float, below: float) -> vec4 {
|
|
let cy = yb - px
|
|
let d = Finish.sd_pill(p, vec2(cx, cy), vec2(hw, px * 1.5))
|
|
let ex = max(abs(p.x - cx) - hw, 0.0)
|
|
let dy = max(cy - p.y, 0.0)
|
|
let fg = exp(-ex / 4.0) * exp(-dy / 2.0)
|
|
var o = Finish.over(dst, vec4(glow, 1.0) * clamp(fg * 0.6 * halo, 0.0, 1.0) * inside * lit)
|
|
let x = max(d, 0.0)
|
|
let tight = exp(-(x * x) / (2.0 * 0.7 * 0.7)) * (1.0 - Finish.cover(d, px))
|
|
o = Finish.over(o, vec4(mix(glow, core, 0.55), 1.0) * tight * 0.85 * halo * lit * max(inside, below))
|
|
let sy = max(p.y - yb, 0.0)
|
|
let sp = exp(-(ex * ex) / (2.0 * 5.0 * 5.0)) * (0.35 * exp(-sy / 1.6) + 0.30 * exp(-sy / 5.0))
|
|
o = Finish.over(o, vec4(glow * 0.75, 1.0) * sp * below * halo * lit)
|
|
return Finish.over(o, vec4(core, 1.0) * Finish.cover(d, px) * lit)
|
|
}
|
|
|
|
// The three-quarter arc a waiting button turns in place of its label.
|
|
let au_spinner = fn(dst: vec4, p: vec2, c: vec2, rad: float, t: float, amt: float, stroke: float) -> vec4 {
|
|
let sdf = Sdf2d.viewport(p)
|
|
let a0 = t * 2.0 * PI
|
|
sdf.arc_round_caps(c.x, c.y, rad, a0, a0 + 1.5 * PI, stroke)
|
|
sdf.fill(vec4(0.94, 0.98, 1.0, amt))
|
|
return Finish.over(dst, sdf.result)
|
|
}
|
|
|
|
// ---------------------------------------------------------------- buttons
|
|
|
|
// Every button is the same slim cap: 26 points on a 13 point line, 10
|
|
// points either side. Its shadow falls outside the box, so the box is the
|
|
// outline and nothing is paid in padding.
|
|
mod.widgets.ButtonFlat.padding = mod.turtle.Inset{left: 10.0 right: 10.0 top: 4.0 bottom: 4.0}
|
|
mod.widgets.ButtonFlat.min_height = 26.0
|
|
// Glyphs sit at 14 points so an icon never makes a cap taller than the row;
|
|
// an icon alone gets a square cap.
|
|
mod.widgets.ButtonFlat.icon_walk.width = 14.0
|
|
mod.widgets.ButtonIcon.padding = mod.turtle.Inset{left: 6.0 right: 6.0 top: 4.0 bottom: 4.0}
|
|
mod.widgets.ButtonFlatIcon.padding = mod.turtle.Inset{left: 6.0 right: 6.0 top: 4.0 bottom: 4.0}
|
|
mod.widgets.ButtonFlatterIcon.padding = mod.turtle.Inset{left: 6.0 right: 6.0 top: 4.0 bottom: 4.0}
|
|
mod.widgets.ButtonPrimary.padding = mod.turtle.Inset{left: 10.0 right: 10.0 top: 4.0 bottom: 4.0}
|
|
mod.widgets.ButtonSecondary.padding = mod.turtle.Inset{left: 10.0 right: 10.0 top: 4.0 bottom: 4.0}
|
|
// The safety cover's hinge takes the first five points of its box: the
|
|
// legend keeps ten points clear of it.
|
|
mod.widgets.ButtonDanger.padding = mod.turtle.Inset{left: 15.0 right: 10.0 top: 4.0 bottom: 4.0}
|
|
// The short rungs keep their own heights under the row minimum.
|
|
mod.widgets.ButtonXs.min_height = 0.0
|
|
mod.widgets.ButtonSm.min_height = 0.0
|
|
// The smallest rung's label one step down, so its cap height is a third of
|
|
// its cap like every other rung's.
|
|
mod.widgets.ButtonXs.draw_text.text_style.font_size = 7.0
|
|
mod.widgets.ButtonFlat.draw_bg.border_radius = uniform(1.0)
|
|
mod.widgets.ButtonFlat.draw_bg.border_size = uniform(0.34)
|
|
mod.widgets.ButtonFlat.clip_x = false
|
|
mod.widgets.ButtonFlat.clip_y = false
|
|
mod.widgets.ButtonFlat.draw_bg.vertex = fn() {
|
|
let m = 14.0
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
|
|
}
|
|
|
|
// THE SAFETY COVER: the Delete cap is a flip-up cover of red moulded plastic
|
|
// over its switch, hinged on the left. A dark base plate shows round the
|
|
// hinge, a chrome hinge barrel stands on it, and the cover runs from the
|
|
// barrel to the right end: glossy red falling from the light at the top, a
|
|
// hard white reflection across its upper part, a raised lip at the far end
|
|
// where a finger lifts it, a dark rim at its foot, and its own shadow on
|
|
// the plate. Under the hand it brightens; held, it presses down and dims.
|
|
let au_cover = fn(p: vec2, lo: vec2, hi: vec2, px: float, hover: float, down: float, disabled: float, focus: float) -> vec4 {
|
|
let up = (1.0 - down) * (1.0 - disabled)
|
|
var res = au_cap(p, lo, hi, 3.0, px, 0.0, down, disabled, 0.0, 1.0, vec3(-0.04, -0.04, -0.04), vec4(0.20, 0.20, 0.21, 1.0), 0.0)
|
|
// The cover's box, from the hinge to the right end.
|
|
let clo = vec2(lo.x + 4.5, lo.y + 1.5)
|
|
let chi = vec2(hi.x - 1.5, hi.y - 2.0)
|
|
let cc = (clo + chi) * 0.5
|
|
let ch = (chi - clo) * 0.5
|
|
let rc = 3.5
|
|
let d = Material.sd_box(p, cc, ch, rc)
|
|
// Its shadow on the plate: it stands a point and a half above it.
|
|
let sh = Finish.drop(p, cc, ch, rc, vec2(0.8, 1.2) * (1.0 - 0.6 * down), 1.0) * 0.7 * (1.0 - Finish.cover(d, px))
|
|
res = Finish.over(res, vec4(0.0, 0.0, 0.0, sh * Finish.cover(Material.sd_box(p, (lo + hi) * 0.5, (hi - lo) * 0.5, 3.0), px)))
|
|
let t = clamp((p.y - clo.y) / max(chi.y - clo.y, 1.0), 0.0, 1.0)
|
|
let u = clamp((p.x - clo.x) / max(chi.x - clo.x, 1.0), 0.0, 1.0)
|
|
var red = mix(vec3(0.800, 0.070, 0.060), vec3(0.380, 0.020, 0.020), smoothstep(0.0, 1.0, t))
|
|
red = red + vec3(0.12, 0.03, 0.03) * hover * up
|
|
// The moulding: the body a shade darker toward the hinge, the lip at the
|
|
// far end standing up into the light.
|
|
red = red * (0.86 + 0.14 * u)
|
|
let lipx = smoothstep(chi.x - 7.0, chi.x - 4.5, p.x)
|
|
red = mix(red, mix(vec3(0.95, 0.16, 0.13), vec3(0.50, 0.03, 0.03), t), lipx * 0.85)
|
|
red = mix(red, red * 0.55, smoothstep(chi.x - 7.5, chi.x - 6.5, p.x) * (1.0 - smoothstep(chi.x - 6.5, chi.x - 5.5, p.x)) * 0.8)
|
|
// The reflection: a hard pale band across the upper third of the
|
|
// gloss, fading toward the hinge, and a white catch on the lip.
|
|
let band = smoothstep(0.06, 0.12, t) * (1.0 - smoothstep(0.24, 0.34, t)) * smoothstep(0.0, 0.25, u)
|
|
red = mix(red, vec3(1.0, 0.86, 0.84), band * 0.34 * up)
|
|
let lc = (p - vec2(chi.x - 4.0, clo.y + 3.0)) / vec2(1.6, 1.4)
|
|
red = mix(red, vec3(1.0, 0.92, 0.90), exp(-dot(lc, lc)) * 0.8 * up)
|
|
// The rims: a pink top edge, a deep red foot, the black outline.
|
|
let r1 = Finish.band(d, px, px * 2.0, px)
|
|
red = mix(red, vec3(1.0, 0.50, 0.46), r1 * (1.0 - step(cc.y, p.y)) * 0.7 * up)
|
|
red = mix(red, vec3(0.22, 0.01, 0.01), r1 * step(cc.y, p.y) * 0.8)
|
|
red = red * (1.0 - 0.35 * down)
|
|
let gl = Finish.lum(red)
|
|
red = mix(red, vec3(gl, gl, gl) * 0.7 + vec3(0.05, 0.02, 0.02), disabled)
|
|
red = mix(red, vec3(0.06, 0.0, 0.0), Finish.band(d, 0.0, px, px))
|
|
let a = Finish.cover(d, px)
|
|
res = Finish.over(res, vec4(red * a, a))
|
|
// The hinge: a chrome barrel down the left of the cover, round across,
|
|
// with a dark seam at either end.
|
|
let hlo = vec2(lo.x + 2.0, lo.y + 3.0)
|
|
let hhi = vec2(lo.x + 6.5, hi.y - 3.5)
|
|
let hd = Material.sd_box(p, (hlo + hhi) * 0.5, (hhi - hlo) * 0.5, 1.5)
|
|
let hx = clamp((p.x - hlo.x) / max(hhi.x - hlo.x, 0.5), 0.0, 1.0)
|
|
var hc = mix(vec3(0.30, 0.30, 0.32), vec3(0.95, 0.95, 0.96), exp(-(hx - 0.35) * (hx - 0.35) / 0.04))
|
|
hc = mix(hc, vec3(0.12, 0.12, 0.13), smoothstep(0.75, 1.0, hx) * 0.8)
|
|
let hm = (hlo.y + hhi.y) * 0.5
|
|
let seam = (1.0 - smoothstep(px * 0.5, px * 1.5, abs(p.y - (hm - 2.5)))) + (1.0 - smoothstep(px * 0.5, px * 1.5, abs(p.y - (hm + 2.5))))
|
|
hc = mix(hc, vec3(0.05, 0.05, 0.06), clamp(seam, 0.0, 1.0) * 0.8)
|
|
hc = mix(hc, vec3(0.03, 0.03, 0.035), Finish.band(hd, 0.0, px, px))
|
|
hc = mix(hc, hc * 0.5, disabled)
|
|
let ha = Finish.cover(hd, px)
|
|
res = Finish.over(res, vec4(hc * ha, ha))
|
|
let ring = Finish.ring_out(d, 0.0, px, px) * 0.9 + Finish.glow(d - px, 0.7) * 0.3 * (1.0 - a)
|
|
return Finish.over(res, vec4(0.227, 0.612, 1.0, 1.0) * clamp(ring, 0.0, 1.0) * focus * (1.0 - disabled))
|
|
}
|
|
|
|
// The cap and its states, shared by every button template; `kind` picks the
|
|
// variant: 0 flat (a legend on the ground, a cap only under the hand), 1
|
|
// raised cap, 2 primary (the blue lit rim), 3 secondary (a chrome trim), 4
|
|
// tertiary (a carbon inlay), 5 danger (the safety cover), 6 outline, 7
|
|
// flatter.
|
|
let au_button = fn(pos: vec2, size: vec2, o: vec2, dpi: float, hover: float, down: float, disabled: float, focus: float, loading: float, anim: float, kind: float, dash: float) -> vec4 {
|
|
let m = 14.0
|
|
let px = au_px(dpi)
|
|
let p = pos * (size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let lo = vec2(au_snap(0.0, o.x, px), au_snap(0.0, o.y, px))
|
|
let hi = vec2(au_snap(size.x, o.x, px), au_snap(size.y, o.y, px))
|
|
let c = (lo + hi) * 0.5
|
|
let h = (hi - lo) * 0.5
|
|
let r = 3.5
|
|
let d = Material.sd_box(p, c, h, r)
|
|
let inside = Finish.cover(d, px)
|
|
var res = vec4(0.0, 0.0, 0.0, 0.0)
|
|
if kind > 4.5 && kind < 5.5 {
|
|
res = au_cover(p, lo, hi, px, hover, down, disabled, focus)
|
|
} else {
|
|
if kind < 0.5 || kind > 5.5 {
|
|
// Flat, outline, flatter: no cap at rest. Under the hand a faint
|
|
// cap shows; held, it sinks.
|
|
if kind > 5.5 && kind < 6.5 {
|
|
// Outline: a frame of silver round a window on the ground,
|
|
// the weave under a light smoke.
|
|
var ring = Finish.band(d, 0.0, px, px)
|
|
if dash > 0.0 {
|
|
ring = ring * step(0.5, fract((p.x + p.y) / (dash * 2.0)))
|
|
}
|
|
let t = clamp((p.y - lo.y) / max(hi.y - lo.y, 1.0), 0.0, 1.0)
|
|
res = vec4(0.0, 0.0, 0.0, 0.32) * inside
|
|
let sil = mix(vec3(0.62, 0.63, 0.65), vec3(0.34, 0.35, 0.37), t) * (1.0 - 0.5 * disabled)
|
|
res = Finish.over(res, vec4(0.0, 0.0, 0.0, 1.0) * Finish.band(d, px, px * 2.0, px) * 0.7)
|
|
res = Finish.over(res, vec4(sil, 1.0) * ring)
|
|
res = Finish.over(res, vec4(0.0, 0.0, 0.0, 1.0) * Finish.ring_out(d, 0.0, px, px) * 0.6)
|
|
}
|
|
if hover + down > 0.001 && kind < 6.5 {
|
|
let k = mix(0.75, 1.0, down)
|
|
res = Finish.over(res, au_cap(p, lo, hi, r, px, hover, down, disabled, 0.0, 0.0, vec3(0.0, 0.0, 0.0), vec4(0.20, 0.20, 0.21, 1.0), 1.0) * clamp(max(hover, down), 0.0, 1.0) * k)
|
|
}
|
|
if kind > 5.5 && kind < 6.5 && hover + down > 0.001 {
|
|
res = Finish.over(res, au_cap(p, lo, hi, r, px, hover, down, disabled, 0.0, 0.0, vec3(0.0, 0.0, 0.0), vec4(0.20, 0.20, 0.21, 1.0), 1.0) * clamp(max(hover, down), 0.0, 1.0) * 0.8)
|
|
}
|
|
let fr = Finish.ring_out(d, 0.0, px, px) * 0.9 + Finish.glow(d - px, 0.7) * 0.3 * (1.0 - inside)
|
|
res = Finish.over(res, vec4(0.227, 0.612, 1.0, 1.0) * clamp(fr, 0.0, 1.0) * focus * (1.0 - disabled))
|
|
} else {
|
|
var tint = vec3(0.0, 0.0, 0.0)
|
|
let lit = 1.0 - disabled
|
|
if kind > 1.5 && kind < 2.5 {
|
|
// Primary: the face cast deep blue by its own rim light.
|
|
tint = vec3(-0.070, -0.022, 0.030) * lit
|
|
}
|
|
if kind > 3.5 && kind < 4.5 {
|
|
tint = vec3(-0.040, -0.040, -0.040)
|
|
}
|
|
let prim = step(1.5, kind) * step(kind, 2.5)
|
|
res = au_cap(p, lo, hi, r, px, hover, down, disabled, focus * (1.0 - prim), 1.0, tint, vec4(0.20, 0.20, 0.21, 1.0), 1.0)
|
|
let up = (1.0 - down) * lit
|
|
if kind > 3.5 && kind < 4.5 {
|
|
// Tertiary: a panel of the weave let into the face under a
|
|
// satin coat, the cap's own frame two points wide round it.
|
|
let wd = Material.sd_box(p, c, h - vec2(2.5, 2.5), max(r - 2.0, 1.0))
|
|
let wv = au_carbon(p + o, 2.5, 1.5) + vec3(0.03, 0.03, 0.032)
|
|
let wa = Finish.cover(wd, px) * (1.0 - 0.6 * disabled)
|
|
var win = mix(wv, wv * 0.6, down)
|
|
win = mix(win, vec3(0.0, 0.0, 0.0), Finish.band(wd, 0.0, px, px) * 0.8)
|
|
res = Finish.over(res, vec4(win * wa, wa))
|
|
let rl = Finish.ring_out(wd, 0.0, px, px) * step(c.y, p.y)
|
|
res = Finish.over(res, vec4(0.36, 0.37, 0.39, 1.0) * rl * 0.7 * up)
|
|
}
|
|
if kind > 2.5 && kind < 3.5 {
|
|
// Secondary: trimmed in polished chrome, a bright hairline
|
|
// just outside the black outline, lit on the top.
|
|
let t = clamp((p.y - lo.y) / max(hi.y - lo.y, 1.0), 0.0, 1.0)
|
|
let trim = mix(vec3(0.78, 0.79, 0.81), vec3(0.40, 0.41, 0.43), smoothstep(0.0, 1.0, t))
|
|
res = Finish.over(res, vec4(trim, 1.0) * Finish.ring_out(d, 0.0, px, px) * (0.4 + 0.6 * lit))
|
|
}
|
|
if kind > 1.5 && kind < 2.5 {
|
|
// Primary: a blue light runs round the rim, two device pixels
|
|
// of it inside the outline (the outer one nearly white), a
|
|
// glow on the face inside it, and a halo on the ground that
|
|
// is gone within half the gap to the next cap.
|
|
let on = lit * (1.0 - 0.35 * down) * (1.0 + 0.15 * hover)
|
|
let depth = max(-d, 0.0)
|
|
let inner = exp(-max(depth - px * 2.0, 0.0) / 2.2) * inside
|
|
res = Finish.over(res, vec4(0.20, 0.52, 1.0, 1.0) * clamp(inner * 0.30 * on, 0.0, 1.0))
|
|
let b1 = Finish.band(d, 0.0, px, px)
|
|
let b2 = Finish.band(d, px, px * 2.0, px)
|
|
let b3 = Finish.band(d, px * 2.0, px * 3.0, px)
|
|
res = Finish.over(res, vec4(0.25, 0.60, 1.0, 1.0) * b1 * on)
|
|
res = Finish.over(res, vec4(0.72, 0.88, 1.0, 1.0) * b2 * on)
|
|
res = Finish.over(res, vec4(0.30, 0.64, 1.0, 1.0) * b3 * 0.8 * on)
|
|
let x = max(d, 0.0)
|
|
let halo = (0.55 * exp(-(x * x) / (2.0 * 0.8 * 0.8)) + 0.25 * exp(-(x * x) / (2.0 * 1.8 * 1.8))) * (1.0 - smoothstep(2.5, 4.0, x)) * (1.0 - inside)
|
|
res = Finish.over(res, vec4(0.20, 0.55, 1.0, 1.0) * clamp(halo * on, 0.0, 1.0))
|
|
// Focus brightens the light rather than ringing it.
|
|
res = Finish.over(res, vec4(0.80, 0.92, 1.0, 1.0) * b2 * 0.5 * focus * lit)
|
|
}
|
|
}
|
|
}
|
|
if loading > 0.001 {
|
|
let rad = max(min(h.x, h.y) - 7.25, 2.0)
|
|
res = au_spinner(res, p, c - vec2(0.0, px), rad, anim, loading, 1.5)
|
|
}
|
|
return res
|
|
}
|
|
|
|
mod.widgets.ButtonFlat.draw_bg.pixel = fn() {
|
|
return self.host_over(au_button(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, self.hover, self.down, self.disabled, self.focus, self.loading, self.anim_time, 0.0, 0.0), 14.0)
|
|
}
|
|
mod.widgets.Button.draw_bg.pixel = fn() {
|
|
return self.host_over(au_button(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, self.hover, self.down, self.disabled, self.focus, self.loading, self.anim_time, 1.0, 0.0), 14.0)
|
|
}
|
|
mod.widgets.ButtonPrimary.draw_bg.pixel = fn() {
|
|
return self.host_over(au_button(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, self.hover, self.down, self.disabled, self.focus, self.loading, self.anim_time, 2.0, 0.0), 14.0)
|
|
}
|
|
mod.widgets.ButtonSecondary.draw_bg.pixel = fn() {
|
|
return self.host_over(au_button(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, self.hover, self.down, self.disabled, self.focus, self.loading, self.anim_time, 3.0, 0.0), 14.0)
|
|
}
|
|
mod.widgets.ButtonTertiary.draw_bg.pixel = fn() {
|
|
return self.host_over(au_button(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, self.hover, self.down, self.disabled, self.focus, self.loading, self.anim_time, 4.0, 0.0), 14.0)
|
|
}
|
|
mod.widgets.ButtonDanger.draw_bg.pixel = fn() {
|
|
return self.host_over(au_button(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, self.hover, self.down, self.disabled, self.focus, self.loading, self.anim_time, 5.0, 0.0), 14.0)
|
|
}
|
|
mod.widgets.ButtonOutline.draw_bg.pixel = fn() {
|
|
return self.host_over(au_button(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, self.hover, self.down, self.disabled, self.focus, self.loading, self.anim_time, 6.0, self.border_dash), 14.0)
|
|
}
|
|
// The size rungs are made from the flat template; here they carry the cap.
|
|
mod.widgets.ButtonXs.draw_bg.pixel = fn() {
|
|
return self.host_over(au_button(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, self.hover, self.down, self.disabled, self.focus, self.loading, self.anim_time, 1.0, 0.0), 14.0)
|
|
}
|
|
mod.widgets.ButtonSm.draw_bg.pixel = fn() {
|
|
return self.host_over(au_button(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, self.hover, self.down, self.disabled, self.focus, self.loading, self.anim_time, 1.0, 0.0), 14.0)
|
|
}
|
|
mod.widgets.ButtonLg.draw_bg.pixel = fn() {
|
|
return self.host_over(au_button(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, self.hover, self.down, self.disabled, self.focus, self.loading, self.anim_time, 1.0, 0.0), 14.0)
|
|
}
|
|
mod.widgets.ButtonXl.draw_bg.pixel = fn() {
|
|
return self.host_over(au_button(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, self.hover, self.down, self.disabled, self.focus, self.loading, self.anim_time, 1.0, 0.0), 14.0)
|
|
}
|
|
mod.widgets.ButtonFlatter.draw_bg.pixel = fn() {
|
|
return self.host_over(au_button(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, self.hover, self.down, self.disabled, self.focus, self.loading, self.anim_time, 7.0, 0.0), 14.0)
|
|
}
|
|
|
|
// Label ink per state: caps #dcdde0, the primary's lit legend blue-white,
|
|
// the cover's legend white on its red, every variant #63666b disabled.
|
|
mod.widgets.ButtonPrimary.draw_text.color = #eef6ff
|
|
mod.widgets.ButtonPrimary.draw_text.color_hover = #f6faff
|
|
mod.widgets.ButtonPrimary.draw_text.color_down = #e2eefc
|
|
mod.widgets.ButtonPrimary.draw_text.color_focus = #eef6ff
|
|
mod.widgets.ButtonPrimary.draw_text.color_disabled = #63666b
|
|
mod.widgets.ButtonPrimary.draw_icon.color = #eef6ff
|
|
mod.widgets.ButtonSecondary.draw_text.color = #dcdde0
|
|
mod.widgets.ButtonSecondary.draw_text.color_hover = #e8e9eb
|
|
mod.widgets.ButtonSecondary.draw_text.color_down = #c9cbcf
|
|
mod.widgets.ButtonSecondary.draw_text.color_focus = #dcdde0
|
|
mod.widgets.ButtonSecondary.draw_icon.color = #dcdde0
|
|
mod.widgets.ButtonTertiary.draw_text.color = #dcdde0
|
|
mod.widgets.ButtonTertiary.draw_text.color_hover = #e8e9eb
|
|
mod.widgets.ButtonTertiary.draw_text.color_down = #c9cbcf
|
|
mod.widgets.ButtonTertiary.draw_text.color_focus = #dcdde0
|
|
mod.widgets.ButtonTertiary.draw_icon.color = #dcdde0
|
|
mod.widgets.ButtonDanger.draw_text.color = #ffffff
|
|
mod.widgets.ButtonDanger.draw_text.color_hover = #ffffff
|
|
mod.widgets.ButtonDanger.draw_text.color_down = #f2e6e5
|
|
mod.widgets.ButtonDanger.draw_text.color_focus = #ffffff
|
|
mod.widgets.ButtonDanger.draw_icon.color = #ffffff
|
|
mod.widgets.ButtonOutline.draw_text.color = #c9cbcf
|
|
mod.widgets.ButtonOutline.draw_text.color_hover = #e2e3e6
|
|
mod.widgets.ButtonOutline.draw_text.color_down = #c9cbcf
|
|
mod.widgets.ButtonOutline.draw_text.color_focus = #dcdde0
|
|
mod.widgets.ButtonOutline.draw_text.color_disabled = #63666b
|
|
mod.widgets.ButtonOutline.draw_icon.color = #c9cbcf
|
|
// The trailing icon takes the cap's ink too, never the drawing's own black.
|
|
mod.widgets.ButtonFlat.draw_icon_end.color = #dcdde0
|
|
|
|
// ---------------------------------------------------------------- selection
|
|
|
|
// A recessed well: no outer shadow, a 1 dp inner top shade, the black
|
|
// outline, and a 1 dp light lip on the ground under its lower edge.
|
|
// Premultiplied; `fill` is the well's own ink, `lip` 0 drops the lip.
|
|
let au_well = fn(p: vec2, lo: vec2, hi: vec2, r: float, px: float, fill: vec3, focus: float, disabled: float, lip: float) -> vec4 {
|
|
let c = (lo + hi) * 0.5
|
|
let h = (hi - lo) * 0.5
|
|
let d = Material.sd_box(p, c, h, r)
|
|
let t = clamp((p.y - lo.y) / max(hi.y - lo.y, 1.0), 0.0, 1.0)
|
|
// The bezel round the well: a quiet light row over its top half and a
|
|
// bright lip under its foot, where the light from the top lands.
|
|
let bzc = mix(vec3(0.200, 0.204, 0.214), vec3(0.420, 0.428, 0.446), step(c.y, p.y))
|
|
var res = vec4(bzc, 1.0) * Finish.ring_out(d, 0.0, px, px) * lip * (1.0 - disabled * 0.6)
|
|
// The lower half a shade lighter, the first device pixels under the top
|
|
// outline a shade darker.
|
|
var col = fill + vec3(0.008, 0.008, 0.009) * step(0.5, t)
|
|
let top = p.y - lo.y
|
|
// Whole device-pixel rows (the edges are snapped): the row under the
|
|
// outline #060607, the next half way to it.
|
|
col = mix(col, vec3(0.022, 0.023, 0.026), 1.0 - step(px * 2.0, top))
|
|
col = mix(col, vec3(0.022, 0.023, 0.026), 0.5 * step(px * 2.0, top) * (1.0 - step(px * 3.0, top)))
|
|
col = mix(col, mix(vec3(0.016, 0.016, 0.019), vec3(0.010, 0.010, 0.012), t), Finish.band(d, 0.0, px, px))
|
|
let a = Finish.cover(d, px)
|
|
res = Finish.over(res, vec4(col * a, a))
|
|
let ring = Finish.ring_out(d, 0.0, px, px) * 0.9 + Finish.glow(d - px, 0.7) * 0.3 * (1.0 - a)
|
|
res = Finish.over(res, vec4(0.227, 0.612, 1.0, 1.0) * clamp(ring, 0.0, 1.0) * focus * (1.0 - disabled))
|
|
return res
|
|
}
|
|
|
|
// The quad of a small control grows by this much so its shadow and focus
|
|
// ring can fall outside the box.
|
|
mod.widgets.CheckBoxFlat.draw_bg.vertex = fn() {
|
|
let m = 5.0
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
|
|
}
|
|
mod.widgets.CheckBoxFlat.clip_x = false
|
|
mod.widgets.CheckBoxFlat.clip_y = false
|
|
mod.widgets.CheckBoxFlat.padding = mod.turtle.Inset{left: 8.0 right: 8.0 top: 4.0 bottom: 4.0}
|
|
mod.widgets.CheckBoxFlat.draw_bg.size = uniform(16.0)
|
|
mod.widgets.CheckBoxFlat.draw_bg.border_radius = uniform(1.0)
|
|
mod.widgets.CheckBoxFlat.label_walk.margin.left = 16.0
|
|
|
|
// A lit mark: a pale core along the stroke's middle, the accent at its
|
|
// edges and a soft halo of the lamp's blue round it. `dk` is the distance
|
|
// to the stroke's edge, `w` its half width.
|
|
let au_lit_mark = fn(dst: vec4, dk: float, w: float, px: float, on: float, disabled: float, error: float) -> vec4 {
|
|
let edge = mix(mix(vec3(0.320, 0.660, 1.0), vec3(0.94, 0.32, 0.23), error), vec3(0.294, 0.306, 0.341), disabled)
|
|
let core = mix(mix(vec3(0.88, 0.96, 1.0), vec3(1.0, 0.86, 0.82), error), vec3(0.36, 0.37, 0.40), disabled)
|
|
let glow = mix(vec3(0.227, 0.612, 1.0), vec3(0.72, 0.25, 0.18), error)
|
|
let x = max(dk, 0.0)
|
|
let g = (0.30 * exp(-(x * x) / (2.0 * 0.45 * 0.45)) + 0.14 * exp(-(x * x) / (2.0 * 1.3 * 1.3))) * (1.0 - Finish.cover(dk, px)) * (1.0 - disabled)
|
|
var res = Finish.over(dst, vec4(glow, 1.0) * g * on)
|
|
let ink = mix(edge, core, 1.0 - smoothstep(0.15 * w, 0.8 * w, dk + w))
|
|
return Finish.over(res, vec4(ink, 1.0) * Finish.cover(dk, px) * on)
|
|
}
|
|
|
|
// The check box: a 16 point well cut into the ground, its floor dark graphite.
|
|
// Checked, a blue light pools on its floor under the lit mark and glows
|
|
// onto the ground round the box.
|
|
mod.widgets.CheckBoxFlat.draw_bg.pixel = fn() {
|
|
let m = 5.0
|
|
let px = au_px(self.draw_pass.dpi_factor)
|
|
let o = self.rect_pos
|
|
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let sz = self.size
|
|
let x0 = (self.rect_size.x - sz) * self.mark_at_end
|
|
let y0 = self.rect_size.y * 0.5 - sz * 0.5
|
|
let lo = vec2(au_snap(x0, o.x, px), au_snap(y0, o.y, px))
|
|
let hi = vec2(au_snap(x0 + sz, o.x, px), au_snap(y0 + sz, o.y, px))
|
|
let c = (lo + hi) * 0.5
|
|
let r = min(self.border_radius * 2.0, sz * 0.5)
|
|
let on = max(self.active, self.mixed)
|
|
// Under the hand the floor lifts; held, it falls a little.
|
|
let held = max(self.down, self.pressed)
|
|
let fill = vec3(0.050, 0.051, 0.056) + vec3(0.030, 0.031, 0.034) * self.hover * (1.0 - self.disabled) - vec3(0.02, 0.02, 0.02) * held
|
|
var res = au_well(p, lo, hi, r, px, fill, self.focus, self.disabled, 1.0)
|
|
// The error: the outline turns the alt red.
|
|
let d = Material.sd_box(p, c, (hi - lo) * 0.5, r)
|
|
// The floor is lit from under the mark: a soft blue pool.
|
|
let gq = (p - c - vec2(0.0, sz * 0.2)) / (sz * 0.34)
|
|
res = Finish.over(res, vec4(0.020, 0.330, 0.700, 1.0) * exp(-dot(gq, gq)) * 0.40 * on * (1.0 - self.disabled) * Finish.cover(d + px * 2.0, px))
|
|
// Lit, the well glows through its outline: a blue halo on the ground
|
|
// round the box, gone within four points.
|
|
res = Finish.over(res, vec4(0.227, 0.612, 1.0, 1.0) * Finish.glow(d, 0.9) * 0.32 * (1.0 - Finish.cover(d, px)) * on * (1.0 - self.disabled))
|
|
res = Finish.over(res, vec4(0.94, 0.32, 0.23, 1.0) * Finish.band(d, 0.0, px, px) * self.error)
|
|
// The mark: a check 8 points across in a 1.7 point stroke, lit; mixed
|
|
// is a dash of three device pixel rows, a pale core row between two of
|
|
// the accent, on the row through the middle of the well. The check
|
|
// stands one device pixel lower, in the well.
|
|
let cm = c + vec2(0.0, px * 0.5)
|
|
let k = sz / 14.0
|
|
let dm = min(au_seg(p, cm + vec2(-3.6, 0.1) * k, cm + vec2(-1.1, 2.6) * k), au_seg(p, cm + vec2(-1.1, 2.6) * k, cm + vec2(3.7, -2.8) * k))
|
|
let cy = au_snap((lo.y + hi.y) * 0.5, o.y, px) + px * 0.5
|
|
let dd = au_seg(p, vec2(c.x - 3.5 * k, cy), vec2(c.x + 3.5 * k, cy))
|
|
let dk = mix(dm - 0.85, dd - px * 1.5, self.mixed)
|
|
return au_lit_mark(res, dk, mix(0.85, px * 1.5, self.mixed), px, on, self.disabled, self.error)
|
|
}
|
|
|
|
// The toggle: a 34 x 18 rocker, a darker raised graphite body, a satin thumb
|
|
// half its length; the thumb carries an LED bar, lit blue when on, a dark
|
|
// slot when off.
|
|
mod.widgets.ToggleFlat.draw_bg.size = uniform(18.0)
|
|
mod.widgets.ToggleFlat.draw_bg.pill_aspect = uniform(1.89)
|
|
mod.widgets.ToggleFlat.label_walk.margin.left = 34.0
|
|
mod.widgets.ToggleFlat.draw_bg.pixel = fn() {
|
|
let m = 5.0
|
|
let px = au_px(self.draw_pass.dpi_factor)
|
|
let o = self.rect_pos
|
|
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let tw = self.size * self.pill_aspect
|
|
let th = self.size
|
|
let x0 = (self.rect_size.x - tw) * self.mark_at_end
|
|
let y0 = self.rect_size.y * 0.5 - th * 0.5
|
|
let lo = vec2(au_snap(x0, o.x, px), au_snap(y0, o.y, px))
|
|
let hi = vec2(au_snap(x0 + tw, o.x, px), au_snap(y0 + th, o.y, px))
|
|
let on = self.active * (1.0 - self.disabled)
|
|
// The body a darker raised cap, a breath of blue in it when on; the
|
|
// thumb a brighter cap standing on it.
|
|
var res = au_cap(p, lo, hi, 3.0, px, 0.0, 0.0, self.disabled, self.focus, 1.0, vec3(-0.075, -0.075, -0.078) + vec3(0.0, 0.012, 0.03) * on, vec4(0.17, 0.17, 0.18, 1.0), 0.0)
|
|
// The thumb travels half the track.
|
|
let kt = mix(self.active, self.drag_pos, self.drag)
|
|
let kw = au_snap(x0 + tw * 0.5, o.x, px) - lo.x
|
|
let kx = au_snap(x0 + (tw - tw * 0.5) * kt, o.x, px)
|
|
let klo = vec2(kx, lo.y)
|
|
let khi = vec2(kx + kw, hi.y)
|
|
let thumb = au_cap(p, klo, khi, 3.0, px, self.hover, max(self.pressed, self.down), self.disabled, 0.0, 0.6, vec3(0.02, 0.02, 0.021), vec4(0.20, 0.20, 0.21, 1.0), 1.0)
|
|
res = Finish.over(res, thumb)
|
|
// The LED bar, 8 x 1.5 points, in the middle of the thumb.
|
|
let lc = vec2((klo.x + khi.x) * 0.5, au_snap((lo.y + hi.y) * 0.5, o.y, px))
|
|
res = au_led(res, p, lc, vec2(4.0, px), on, 0.8, px, vec3(0.94, 0.984, 1.0), vec3(0.227, 0.612, 1.0))
|
|
return res
|
|
}
|
|
|
|
// The radio: the check box's well made round, a socket. Chosen, an LED dot
|
|
// is lit in its middle and its blue pools round it; not chosen, the dot is
|
|
// a dark lens.
|
|
mod.widgets.RadioButtonFlat.draw_bg.vertex = fn() {
|
|
let m = 5.0
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
|
|
}
|
|
mod.widgets.RadioButtonFlat.clip_x = false
|
|
mod.widgets.RadioButtonFlat.clip_y = false
|
|
mod.widgets.RadioButtonFlat.draw_bg.size = uniform(16.0)
|
|
mod.widgets.RadioButtonFlat.padding = mod.turtle.Inset{left: 8.0 right: 8.0 top: 4.0 bottom: 4.0}
|
|
mod.widgets.RadioButtonFlat.label_walk.margin.left = 16.0
|
|
mod.widgets.RadioButtonFlat.draw_bg.pixel = fn() {
|
|
let m = 5.0
|
|
let px = au_px(self.draw_pass.dpi_factor)
|
|
let o = self.rect_pos
|
|
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let sz = self.size
|
|
let y0 = self.rect_size.y * 0.5 - sz * 0.5
|
|
let lo = vec2(au_snap(0.0, o.x, px), au_snap(y0, o.y, px))
|
|
let hi = vec2(au_snap(sz, o.x, px), au_snap(y0 + sz, o.y, px))
|
|
let c = (lo + hi) * 0.5
|
|
let on = self.active * (1.0 - self.disabled)
|
|
let fill = vec3(0.050, 0.051, 0.056) + vec3(0.030, 0.031, 0.034) * self.hover * (1.0 - self.disabled)
|
|
var res = au_well(p, lo, hi, sz * 0.5, px, fill, self.focus, self.disabled, 1.0)
|
|
// Chosen, the socket is lit from the dot: blue round it, strongest
|
|
// under it.
|
|
let gq = (p - c - vec2(0.0, sz * 0.15)) / (sz * 0.36)
|
|
res = Finish.over(res, vec4(0.030, 0.360, 0.760, 1.0) * exp(-dot(gq, gq)) * 0.45 * on * Finish.cover(length(p - c) - sz * 0.5 + px * 2.0, px))
|
|
let dro = length(p - c) - sz * 0.5
|
|
res = Finish.over(res, vec4(0.227, 0.612, 1.0, 1.0) * Finish.glow(dro, 0.9) * 0.34 * (1.0 - Finish.cover(dro, px)) * on)
|
|
// The dot: 5.5 points, one device pixel lower in the well when lit.
|
|
let cd = c + vec2(0.0, px * 0.5 * on)
|
|
res = au_led(res, p, cd, vec2(3.0 - px, 3.0 - px), on, 1.0, px, vec3(0.96, 0.99, 1.0), vec3(0.227, 0.612, 1.0))
|
|
return res
|
|
}
|
|
|
|
// A tab: a dark cap rimmed in brushed gold, as a tuning panel marks the
|
|
// pages of its menu. The rim is a band of gold a point and a half wide just
|
|
// inside the black outline, lit from the top. The tab in use is lit from
|
|
// within: its rim burns amber, a warm glow lies on the face along it and a
|
|
// short halo on the ground over and under it, gone within four points; a quiet tab
|
|
// keeps its gold dim. Under the hand the rim brightens; pressed, the tab
|
|
// sinks. Premultiplied.
|
|
let au_gold_tab = fn(p: vec2, lo: vec2, hi: vec2, px: float, hover: float, down: float, active: float, disabled: float, focus: float) -> vec4 {
|
|
let r = 3.5
|
|
let c = (lo + hi) * 0.5
|
|
let h = (hi - lo) * 0.5
|
|
let d = Material.sd_box(p, c, h, r)
|
|
let inside = Finish.cover(d, px)
|
|
var res = au_cap(p, lo, hi, r, px, hover * (1.0 - active), down * (1.0 - active), disabled, focus, 1.0, vec3(-0.075, -0.080, -0.090) + vec3(0.02, 0.012, 0.0) * active, vec4(0.20, 0.20, 0.21, 1.0), 0.0)
|
|
let t = clamp((p.y - lo.y) / max(hi.y - lo.y, 1.0), 0.0, 1.0)
|
|
let on = active * (1.0 - disabled)
|
|
// The warm light inside the lit rim.
|
|
let depth = max(-d, 0.0)
|
|
res = Finish.over(res, vec4(1.0, 0.62, 0.16, 1.0) * clamp(exp(-max(depth - px * 3.0, 0.0) / 2.4) * inside * 0.26 * on, 0.0, 1.0))
|
|
// The gold band.
|
|
let gold = mix(vec3(0.840, 0.660, 0.330), vec3(0.470, 0.330, 0.140), smoothstep(0.0, 1.0, t))
|
|
let lit = mix(vec3(1.0, 0.86, 0.52), vec3(0.95, 0.60, 0.20), smoothstep(0.0, 1.0, t))
|
|
var gc = mix(gold * (0.62 + 0.25 * hover), lit, on)
|
|
gc = mix(gc, vec3(0.30, 0.28, 0.25), disabled)
|
|
let band = Finish.band(d, px, px * 2.5, px)
|
|
res = Finish.over(res, vec4(gc, 1.0) * band)
|
|
res = Finish.over(res, vec4(vec3(1.0, 0.95, 0.80), 1.0) * Finish.band(d, px, px * 2.0, px) * step(p.y, lo.y + r) * 0.35 * (0.5 + 0.5 * on))
|
|
let x = max(d, 0.0)
|
|
// Tabs stand a point or two apart, so the halo keeps to the ground over
|
|
// and under the tab and is gone within a point to the sides.
|
|
let side = 1.0 - smoothstep(0.0, 1.0, max(lo.x - p.x, p.x - hi.x))
|
|
let halo = (0.45 * exp(-(x * x) / (2.0 * 0.8 * 0.8)) + 0.2 * exp(-(x * x) / (2.0 * 1.8 * 1.8))) * (1.0 - smoothstep(2.5, 4.0, x)) * (1.0 - inside) * side
|
|
return Finish.over(res, vec4(1.0, 0.62, 0.18, 1.0) * clamp(halo * on, 0.0, 1.0))
|
|
}
|
|
|
|
// The radio tab is the same gold-rimmed tab: chosen, its rim lit.
|
|
mod.widgets.RadioButtonTabFlat.draw_bg.vertex = fn() {
|
|
let m = 13.0
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
|
|
}
|
|
mod.widgets.RadioButtonTabFlat.clip_x = false
|
|
mod.widgets.RadioButtonTabFlat.clip_y = false
|
|
mod.widgets.RadioButtonTabFlat.padding = mod.turtle.Inset{left: 0.0 right: 8.0 top: 4.0 bottom: 4.0}
|
|
mod.widgets.RadioButtonTabFlat.min_height = 26.0
|
|
mod.widgets.RadioButtonTabFlat.draw_bg.pixel = fn() {
|
|
let m = 13.0
|
|
let px = au_px(self.draw_pass.dpi_factor)
|
|
let o = self.rect_pos
|
|
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let lo = vec2(au_snap(0.0, o.x, px), au_snap(0.0, o.y, px))
|
|
let hi = vec2(au_snap(self.rect_size.x, o.x, px), au_snap(self.rect_size.y, o.y, px))
|
|
return au_gold_tab(p, lo, hi, px, self.hover, self.down, self.active, self.disabled, self.focus)
|
|
}
|
|
mod.widgets.RadioButtonTabFlat.draw_text.color = #cfae6c
|
|
mod.widgets.RadioButtonTabFlat.draw_text.color_hover = #e6c684
|
|
mod.widgets.RadioButtonTabFlat.draw_text.color_active = #ffe2a8
|
|
mod.widgets.RadioButtonTabFlat.draw_text.color_disabled = #63666b
|
|
|
|
// ---------------------------------------------------------------- sliders
|
|
|
|
// THE GROOVE a value runs in: a slot cut into the ground with round ends, a
|
|
// silver lip under its foot and a dark one over its top, the black outline,
|
|
// and a floor of near-black hexagon mesh (`au_mesh`, holes 3.2 points
|
|
// apart, a row of them along the groove's middle, a thin charcoal web with
|
|
// only a faint cool light on each upper rim) under the shadow of its upper
|
|
// wall: a texture noticed up close. The
|
|
// tube lies over the mesh, so the mesh shows on the unlit part and dimly
|
|
// under the glow. Premultiplied.
|
|
let au_groove = fn(p: vec2, o: vec2, lo: vec2, hi: vec2, px: float, disabled: float) -> vec4 {
|
|
let r = (hi.y - lo.y) * 0.5
|
|
var res = au_well(p, lo, hi, r, px, vec3(0.030, 0.031, 0.034), 0.0, disabled, 1.0)
|
|
let c = (lo + hi) * 0.5
|
|
let d = Material.sd_box(p, c, (hi - lo) * 0.5, r)
|
|
var mc = au_mesh(p - vec2(lo.x + r, c.y), 3.2, 0.6, px, vec3(0.118, 0.126, 0.140), 0.40 * (1.0 - 0.6 * disabled))
|
|
mc = mc * (1.0 - 0.75 * Finish.inset(p, c, (hi - lo) * 0.5 - vec2(px * 2.0, px * 2.0), max(r - px * 2.0, 0.5), vec2(0.0, 1.4), 1.4))
|
|
mc = mc * (1.0 - 0.45 * disabled)
|
|
let a = Finish.cover(d + px * 2.0, px)
|
|
return Finish.over(res, vec4(mc * a, a))
|
|
}
|
|
|
|
// A NEON TUBE lying in the groove from `x0` to `x1` on the line `cy`, `rt`
|
|
// points round: a white-hot core along its middle a third of its width,
|
|
// its colour at full strength toward its glass, and its light blooming on
|
|
// the groove's floor and out over the ground round it in two falls, gone
|
|
// within nine points. `col` is its colour. Premultiplied over `dst`.
|
|
let au_tube = fn(dst: vec4, p: vec2, x0: float, x1: float, cy: float, rt: float, px: float, col: vec3, disabled: float) -> vec4 {
|
|
let on = step(0.5, x1 - x0)
|
|
let hl = max((x1 - x0) * 0.5, rt)
|
|
let d = Finish.sd_pill(p, vec2(x0 + hl, cy), vec2(hl, rt))
|
|
let live = 1.0 - disabled
|
|
let x = max(d, 0.0)
|
|
let halo = (0.70 * exp(-(x * x) / (2.0 * 1.1 * 1.1)) + 0.34 * exp(-(x * x) / (2.0 * 3.4 * 3.4))) * (1.0 - smoothstep(6.0, 9.0, x)) * (1.0 - Finish.cover(d, px))
|
|
var res = Finish.over(dst, vec4(col, 1.0) * clamp(halo * live * on, 0.0, 1.0))
|
|
let k = clamp(abs(p.y - cy) / max(rt, 0.5), 0.0, 1.0)
|
|
var tc = mix(mix(col, vec3(1.0, 1.0, 1.0), 0.92), mix(col, vec3(1.0, 1.0, 1.0), 0.25), smoothstep(0.18, 0.55, k))
|
|
tc = mix(tc, col, smoothstep(0.55, 0.85, k))
|
|
tc = mix(tc, col * 0.75, smoothstep(0.88, 1.0, k))
|
|
let g = Finish.lum(col)
|
|
tc = mix(tc, vec3(g, g, g) * 0.35 + vec3(0.05, 0.05, 0.055), disabled)
|
|
return Finish.over(res, vec4(tc, 1.0) * Finish.cover(d, px) * on)
|
|
}
|
|
|
|
// THE SLIDER CAP: a chrome barrel lying across the groove, its axis along
|
|
// the travel. Round about that axis it mirrors the room (a bright band over
|
|
// its top, a dark one under its middle, the floor's light along its foot);
|
|
// three ring grooves are turned into its right end; its ends are rounded
|
|
// over. The tube's light catches its outline and lies round it on the
|
|
// ground; held, that light is stronger. `hs` is the half size, `col` the
|
|
// tube's colour. Premultiplied over `dst`.
|
|
let au_barrel = fn(dst: vec4, p: vec2, c: vec2, hs: vec2, px: float, hover: float, held: float, disabled: float, col: vec3) -> vec4 {
|
|
let r = min(2.5, hs.x * 0.4)
|
|
let d = Material.sd_box(p, c, hs, r)
|
|
let live = 1.0 - disabled
|
|
// Its shadow on the groove and the ground.
|
|
let sh = Finish.drop(p, c + vec2(0.0, 1.2), hs - vec2(0.5, 0.5), r, vec2(0.0, 0.8), 1.4) * 0.8 * (1.0 - Finish.cover(d, px))
|
|
var res = Finish.over(dst, vec4(0.0, 0.0, 0.0, sh))
|
|
// The tube's light round it.
|
|
let x = max(d, 0.0)
|
|
let gl = (0.42 * exp(-(x * x) / (2.0 * 0.8 * 0.8)) + 0.16 * exp(-(x * x) / (2.0 * 2.0 * 2.0))) * (1.0 - smoothstep(3.0, 4.5, x)) * (1.0 - Finish.cover(d, px))
|
|
res = Finish.over(res, vec4(col, 1.0) * clamp(gl * (0.7 + 0.5 * held) * live, 0.0, 1.0))
|
|
// The chrome: the normal round the axis, mirrored into the room.
|
|
let ny = clamp((p.y - c.y) / max(hs.y, 0.5), -1.0, 1.0)
|
|
let ex = clamp((abs(p.x - c.x) - (hs.x - r * 1.6)) / max(r * 1.6, 0.5), 0.0, 1.0)
|
|
var ch = au_chrome_bar(ny) * (1.0 - 0.35 * ex * ex)
|
|
ch = ch + col * 0.22 * smoothstep(0.45, 0.9, ny) * live
|
|
ch = ch * (1.0 + 0.10 * hover * live)
|
|
// The ring grooves at the right end, each a dark cut with a lit edge
|
|
// after it.
|
|
let gx = c.x + hs.x - 2.4
|
|
let k1 = abs(p.x - gx)
|
|
let k2 = abs(p.x - (gx - 2.0))
|
|
let k3 = abs(p.x - (gx - 4.0))
|
|
let cut = (1.0 - smoothstep(px * 0.3, px * 0.9, min(min(k1, k2), k3))) * step(abs(p.y - c.y), hs.y - 0.6)
|
|
let lit = (1.0 - smoothstep(px * 0.3, px * 0.9, min(min(abs(p.x - gx - px), abs(p.x - gx + 2.0 - px)), abs(p.x - gx + 4.0 - px)))) * step(abs(p.y - c.y), hs.y - 0.6)
|
|
ch = mix(ch, ch * 0.22, cut * 0.9)
|
|
ch = mix(ch, min(ch * 1.25 + vec3(0.12, 0.12, 0.12), vec3(1.0, 1.0, 1.0)), lit * 0.6)
|
|
let gg = Finish.lum(ch)
|
|
ch = mix(ch, vec3(gg, gg, gg) * 0.45 + vec3(0.04, 0.04, 0.045), disabled)
|
|
// The outline: black, with the tube's colour lit along it.
|
|
ch = mix(ch, mix(col, vec3(1.0, 1.0, 1.0), 0.25), Finish.band(d, px, px * 2.0, px) * 0.55 * live)
|
|
ch = mix(ch, vec3(0.02, 0.02, 0.025), Finish.band(d, 0.0, px, px))
|
|
let a = Finish.cover(d, px)
|
|
return Finish.over(res, vec4(ch * a, a))
|
|
}
|
|
|
|
// Polished chrome on a cylinder lying across the light, by `u` -1 (its top
|
|
// edge) to 1 (its foot): a dark rim, a white band over the upper third, the
|
|
// deep horizon under the middle, the floor's grey below and a pale
|
|
// reflection near the foot.
|
|
let au_chrome_bar = fn(u: float) -> vec3 {
|
|
var v = 0.50
|
|
v = mix(v, 0.98, exp(-(u + 0.42) * (u + 0.42) / 0.030))
|
|
v = mix(v, 0.10, exp(-(u - 0.14) * (u - 0.14) / 0.028))
|
|
v = mix(v, 0.74, exp(-(u - 0.70) * (u - 0.70) / 0.018))
|
|
v = v * mix(1.0, 0.40, smoothstep(0.82, 1.0, abs(u)))
|
|
return vec3(v, v * 1.005, v * 1.02)
|
|
}
|
|
|
|
// A round thumb (the Band's): a chrome ball in a ring of the tube's light.
|
|
let au_ball = fn(dst: vec4, p: vec2, c: vec2, rad: float, px: float, hover: float, held: float, disabled: float, col: vec3) -> vec4 {
|
|
let q = p - c
|
|
let rl = length(q)
|
|
let d = rl - rad
|
|
let live = 1.0 - disabled
|
|
let sh = Finish.drop(p, c, vec2(rad, rad) * 0.92, rad * 0.92, vec2(0.0, min(0.2 * rad, 3.0)), min(0.15 * rad, 2.0)) * 0.85 * (1.0 - Finish.cover(d, px))
|
|
var res = Finish.over(dst, vec4(0.0, 0.0, 0.0, sh))
|
|
let x = max(d, 0.0)
|
|
let gl = (0.42 * exp(-(x * x) / (2.0 * 0.8 * 0.8)) + 0.16 * exp(-(x * x) / (2.0 * 2.0 * 2.0))) * (1.0 - smoothstep(3.0, 4.5, x)) * (1.0 - Finish.cover(d, px))
|
|
res = Finish.over(res, vec4(col, 1.0) * clamp(gl * (0.7 + 0.5 * held) * live, 0.0, 1.0))
|
|
let nxy = q / max(rad, 0.5)
|
|
let nz = sqrt(max(1.0 - dot(nxy, nxy), 0.0))
|
|
let n = normalize(vec3(nxy, nz + 0.001))
|
|
let rf = vec3(2.0 * n.z * n.x, 2.0 * n.z * n.y, 2.0 * n.z * n.z - 1.0)
|
|
var ch = au_chrome_room(rf, 1.0) * (1.0 + 0.10 * hover * live)
|
|
let gg = Finish.lum(ch)
|
|
ch = mix(ch, vec3(gg, gg, gg) * 0.45 + vec3(0.04, 0.04, 0.045), disabled)
|
|
ch = mix(ch, mix(col, vec3(1.0, 1.0, 1.0), 0.25), Finish.band(d, px, px * 2.0, px) * 0.55 * live)
|
|
ch = mix(ch, vec3(0.02, 0.02, 0.025), Finish.band(d, 0.0, px, px))
|
|
let a = Finish.cover(d, px)
|
|
return Finish.over(res, vec4(ch * a, a))
|
|
}
|
|
|
|
// A turned-metal disc (the spinner's middle): dark steel, lathe rings, a
|
|
// specular up and to the left, a lit rim on its top edge. Premultiplied.
|
|
let au_metal = fn(p: vec2, c: vec2, rad: float, px: float, hover: float, disabled: float, bright: float) -> vec4 {
|
|
let q = p - c
|
|
let rl = length(q)
|
|
let d = rl - rad
|
|
let t = clamp(q.y / max(rad, 0.5) * 0.5 + 0.5, 0.0, 1.0)
|
|
var m = mix(vec3(0.560, 0.565, 0.580), vec3(0.160, 0.162, 0.170), smoothstep(0.05, 0.95, t))
|
|
m = m * Finish.spun(p, c, vec2(-0.35, -0.94))
|
|
let sp = (q - vec2(-0.32 * rad, -0.40 * rad)) / max(0.36 * rad, 0.8)
|
|
m = m + vec3(0.16, 0.16, 0.165) * exp(-dot(sp, sp)) + vec3(0.03, 0.03, 0.03) * hover
|
|
m = mix(m, vec3(0.80, 0.81, 0.83), Finish.band(d, px, px * 2.0, px) * smoothstep(0.1, 0.9, -q.y / max(rl, 0.001)) * 0.9)
|
|
m = mix(m, mix(m, vec3(0.12, 0.12, 0.13), 0.65), disabled)
|
|
m = mix(m, vec3(0.012, 0.012, 0.016), Finish.band(d, 0.0, px, px))
|
|
let a = Finish.cover(d, px)
|
|
return vec4(m * a, a)
|
|
}
|
|
|
|
// The bar slider: an 11 point groove under its label row, the neon tube from
|
|
// its start to the cap, and the chrome barrel cap riding it, 18 points
|
|
// along and 20 across.
|
|
mod.widgets.SliderFlat.clip_x = false
|
|
mod.widgets.SliderFlat.clip_y = false
|
|
mod.widgets.SliderFlat.draw_bg.vertex = fn() {
|
|
let m = 12.0
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
|
|
}
|
|
mod.widgets.SliderFlat.draw_bg.pixel = fn() {
|
|
let m = 12.0
|
|
let px = au_px(self.draw_pass.dpi_factor)
|
|
let o = self.rect_pos
|
|
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let hw = 9.0
|
|
let cy = au_snap(20.0 + (self.rect_size.y - 20.0) * 0.5, o.y, px)
|
|
let lo = vec2(au_snap(0.0, o.x, px), cy - 5.5)
|
|
let hi = vec2(au_snap(self.rect_size.x, o.x, px), cy + 5.5)
|
|
let col = au_neon_at(o)
|
|
var res = au_groove(p, o, lo, hi, px, self.disabled)
|
|
let span = self.rect_size.x - hw * 2.0
|
|
let hx = hw + self.slide_pos * span
|
|
let ox = hw + self.origin_pos * span
|
|
let f0 = mix(lo.x + 3.0, min(ox, hx), self.arc_origin)
|
|
let f1 = mix(hx, max(ox, hx), self.arc_origin)
|
|
res = au_tube(res, p, f0, f1, cy, 2.7, px, col, self.disabled)
|
|
let held = max(self.drag, self.focus)
|
|
return au_barrel(res, p, vec2(Finish.snap(o.x + hx, px) - o.x, cy), vec2(hw, 10.0), px, self.hover, held, self.disabled, col)
|
|
}
|
|
|
|
// A slider's value is a text input, and the field row's 26 point minimum
|
|
// pushed it half a row below its label: here it is only as tall as its line.
|
|
mod.widgets.SliderMinimal.text_input.min_height = 0.0
|
|
mod.widgets.SliderMinimal.text_input.padding = mod.turtle.Inset{left: 0.0 right: 0.0 top: 0.0 bottom: 0.0}
|
|
mod.widgets.SliderFlat.text_input.min_height = 0.0
|
|
mod.widgets.SliderFlat.text_input.padding = mod.turtle.Inset{left: 0.0 right: 0.0 top: 0.0 bottom: 0.0}
|
|
mod.widgets.Slider.text_input.min_height = 0.0
|
|
mod.widgets.Slider.text_input.padding = mod.turtle.Inset{left: 0.0 right: 0.0 top: 0.0 bottom: 0.0}
|
|
mod.widgets.SliderRoundFlat.text_input.min_height = 0.0
|
|
mod.widgets.SliderRoundFlat.text_input.padding = mod.turtle.Inset{left: 0.0 right: 0.0 top: 0.0 bottom: 0.0}
|
|
mod.widgets.SliderRound.text_input.min_height = 0.0
|
|
mod.widgets.SliderRound.text_input.padding = mod.turtle.Inset{left: 0.0 right: 0.0 top: 0.0 bottom: 0.0}
|
|
// A dial's value box stands in the 20 point legend row above the ring: at
|
|
// the field row's 26 points it hung over the ring's bezel, so here it is
|
|
// its line and two points either side, and clears the ring.
|
|
mod.widgets.RotaryFlat.text_input.min_height = 0.0
|
|
mod.widgets.RotaryFlat.text_input.padding = mod.turtle.Inset{left: 6.0 right: 6.0 top: 2.0 bottom: 2.0}
|
|
mod.widgets.Rotary.text_input.min_height = 0.0
|
|
mod.widgets.Rotary.text_input.padding = mod.turtle.Inset{left: 6.0 right: 6.0 top: 2.0 bottom: 2.0}
|
|
mod.widgets.RotaryGradientY.text_input.min_height = 0.0
|
|
mod.widgets.RotaryGradientY.text_input.padding = mod.turtle.Inset{left: 6.0 right: 6.0 top: 2.0 bottom: 2.0}
|
|
|
|
// The minimal slider: a slimmer groove and tube under its label, and a
|
|
// smaller barrel riding it.
|
|
mod.widgets.SliderMinimal.clip_x = false
|
|
mod.widgets.SliderMinimal.clip_y = false
|
|
mod.widgets.SliderMinimal.draw_bg.vertex = fn() {
|
|
let m = 10.0
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
|
|
}
|
|
mod.widgets.SliderMinimal.draw_bg.pixel = fn() {
|
|
let m = 10.0
|
|
let px = au_px(self.draw_pass.dpi_factor)
|
|
let o = self.rect_pos
|
|
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let hw = 7.0
|
|
let cy = au_snap(self.offset_y + (self.rect_size.y - self.offset_y) * 0.5, o.y, px)
|
|
let lo = vec2(au_snap(0.0, o.x, px), cy - 4.0)
|
|
let hi = vec2(au_snap(self.rect_size.x, o.x, px), cy + 4.0)
|
|
let col = au_neon_at(o)
|
|
var res = au_groove(p, o, lo, hi, px, self.disabled)
|
|
let span = self.rect_size.x - hw * 2.0
|
|
let hx = hw + self.slide_pos * span
|
|
let ox = hw + self.origin_pos * span
|
|
let f0 = mix(lo.x + 2.5, min(ox, hx), self.arc_origin)
|
|
let f1 = mix(hx, max(ox, hx), self.arc_origin)
|
|
res = au_tube(res, p, f0, f1, cy, 2.0, px, col, self.disabled)
|
|
let held = max(self.drag, self.hover)
|
|
return au_barrel(res, p, vec2(Finish.snap(o.x + hx, px) - o.x, cy), vec2(hw, 7.5), px, self.hover, held, self.disabled, col)
|
|
}
|
|
|
|
// The round slider: a label column, then the bar slider's groove, tube and
|
|
// barrel. The groove ends short of the right edge, so the value the widget
|
|
// writes there stands on the ground, not on the track.
|
|
mod.widgets.SliderRoundFlat.clip_x = false
|
|
mod.widgets.SliderRoundFlat.clip_y = false
|
|
mod.widgets.SliderRoundFlat.draw_bg.vertex = fn() {
|
|
let m = 12.0
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
|
|
}
|
|
mod.widgets.SliderRoundFlat.draw_bg.pixel = fn() {
|
|
let m = 12.0
|
|
let px = au_px(self.draw_pass.dpi_factor)
|
|
let o = self.rect_pos
|
|
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let hw = 9.0
|
|
let cy = au_snap(self.rect_size.y * 0.5, o.y, px)
|
|
let x0 = au_snap(self.label_size, o.x, px)
|
|
let lo = vec2(x0, cy - 5.5)
|
|
let hi = vec2(au_snap(self.rect_size.x - 37.0, o.x, px), cy + 5.5)
|
|
let col = au_neon_at(o)
|
|
var res = au_groove(p, o, lo, hi, px, self.disabled)
|
|
let span = hi.x - lo.x - hw * 2.0
|
|
let hx = lo.x + hw + self.slide_pos * span
|
|
res = au_tube(res, p, lo.x + 3.0, hx, cy, 2.7, px, col, self.disabled)
|
|
let held = max(self.drag, self.focus)
|
|
return au_barrel(res, p, vec2(Finish.snap(o.x + hx, px) - o.x, cy), vec2(hw, 10.0), px, self.hover, held, self.disabled, col)
|
|
}
|
|
|
|
// The fader: a 3 point slot with a black centre line, a ladder of LED dots
|
|
// beside it lit green from the bottom up to the value (amber over the last
|
|
// fifth), a tick scale on the other side, and a chrome cap: the slider
|
|
// bench's crossfader block drawn by the cap engine (`mod.sdf.Cap`), a low
|
|
// block of polished chrome 30 points along the travel and 0.55 of the
|
|
// control across, 6.5 points high, its ends chamfered up to a level top,
|
|
// its sides falling over a rounded shoulder, six ribs cut across its top.
|
|
// Hovered, it lifts; held, it sinks half a point and the lift goes;
|
|
// focused, the sheet's blue ring; disabled, it dulls and throws no shadow.
|
|
mod.widgets.SliderFader.cap_size = 30.0
|
|
mod.widgets.SliderFader.draw_bg.track_size = uniform(3.0)
|
|
mod.widgets.SliderFader.draw_bg.cap_cross = uniform(0.55)
|
|
mod.widgets.SliderFader.clip_x = false
|
|
mod.widgets.SliderFader.clip_y = false
|
|
// The block's material as uniforms, so the tweaker moves it
|
|
// live: the key light (screen space, x right, y down, z toward the viewer)
|
|
// and its intensity; the highlight, its roughness, the sheen and the rim;
|
|
// the occlusion and its reach; the plain sky's reflection and how near the
|
|
// eye is; metal, spun grain and glow; the exposure in stops and the
|
|
// highlight roll-off; the cast shadow, its blur and the contact shadow; the
|
|
// slot's inner shadow (unused here) and the block's height in points.
|
|
mod.widgets.SliderFader.draw_bg.cap_light = uniform(vec3(-0.3, -0.62, 0.72))
|
|
mod.widgets.SliderFader.draw_bg.cap_light_intensity = uniform(1.35)
|
|
mod.widgets.SliderFader.draw_bg.cap_specular = uniform(1.0)
|
|
mod.widgets.SliderFader.draw_bg.cap_roughness = uniform(0.72)
|
|
mod.widgets.SliderFader.draw_bg.cap_gloss = uniform(1.0)
|
|
mod.widgets.SliderFader.draw_bg.cap_rim = uniform(1.0)
|
|
mod.widgets.SliderFader.draw_bg.cap_ao = uniform(1.0)
|
|
mod.widgets.SliderFader.draw_bg.cap_ao_reach = uniform(4.0)
|
|
mod.widgets.SliderFader.draw_bg.cap_reflection = uniform(0.55)
|
|
mod.widgets.SliderFader.draw_bg.cap_reflection_perspective = uniform(0.67)
|
|
mod.widgets.SliderFader.draw_bg.cap_metal = uniform(1.0)
|
|
mod.widgets.SliderFader.draw_bg.cap_spun = uniform(0.0)
|
|
mod.widgets.SliderFader.draw_bg.cap_glow = uniform(0.0)
|
|
mod.widgets.SliderFader.draw_bg.cap_exposure = uniform(0.0)
|
|
mod.widgets.SliderFader.draw_bg.cap_rolloff = uniform(0.6)
|
|
mod.widgets.SliderFader.draw_bg.cap_shadow = uniform(0.36)
|
|
mod.widgets.SliderFader.draw_bg.cap_shadow_blur = uniform(2.5)
|
|
mod.widgets.SliderFader.draw_bg.cap_contact = uniform(0.4)
|
|
mod.widgets.SliderFader.draw_bg.cap_inner = uniform(0.42)
|
|
mod.widgets.SliderFader.draw_bg.cap_height = uniform(6.5)
|
|
// The inks: bright chrome, white light and its shadow multiplier; the
|
|
// ground the chrome mirrors is this sheet's.
|
|
mod.widgets.SliderFader.draw_bg.ground_color = uniform(theme.color_bg_app)
|
|
mod.widgets.SliderFader.draw_bg.handle_color = uniform(#6b6e73)
|
|
mod.widgets.SliderFader.draw_bg.light_color = uniform(#e6e8eb)
|
|
mod.widgets.SliderFader.draw_bg.shadow_color = uniform(#5c6066)
|
|
|
|
// How far the fader's quad reaches past its rect, in points: the throw of
|
|
// the block's shadow from its height along the key light, the blur at its
|
|
// tip (the engine holds it between 0.35 and 1 times the height), the glow's
|
|
// reach when there is one, and four points for the contact shadow's tail
|
|
// and the focus ring's breath; never less than the 5 the slot's lip needs.
|
|
let au_fader_room = fn(l: vec3, height: float, blur: float, glow: float, cap: float) -> float {
|
|
let n = normalize(l)
|
|
let rise = max(height, 0.0)
|
|
let reach = rise * 0.7 * length(n.xy) / max(n.z, 0.25)
|
|
let tip = clamp(blur, max(rise * 0.35, 1.0), max(rise, 1.0))
|
|
return max(reach + tip + step(0.001, glow) * max(cap * 0.4, 1.0) * 4.0 + 4.0, 5.0)
|
|
}
|
|
|
|
mod.widgets.SliderFader.draw_bg.vertex = fn() {
|
|
let m = au_fader_room(self.cap_light, self.cap_height, self.cap_shadow_blur, self.cap_glow, self.cap_px)
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
|
|
}
|
|
mod.widgets.SliderFader.draw_bg.pixel = fn() {
|
|
let m = au_fader_room(self.cap_light, self.cap_height, self.cap_shadow_blur, self.cap_glow, self.cap_px)
|
|
let px = au_px(self.draw_pass.dpi_factor)
|
|
let o = self.rect_pos
|
|
let sz = self.rect_size
|
|
let p = self.pos * (sz + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let v = self.is_vertical
|
|
let along = mix(sz.x, sz.y, v)
|
|
let across = mix(sz.y, sz.x, v)
|
|
let travel = max(along - self.inset_px * 2.0 - self.cap_px, 1.0)
|
|
let foot = self.inset_px + self.cap_px * 0.5
|
|
// Track space: `a` from the value's zero, `b` across.
|
|
let a = mix(p.x, sz.y - p.y, v)
|
|
let b = mix(p.y, p.x, v)
|
|
let mid = across * 0.5
|
|
// The slot, snapped on screen.
|
|
let slo = mix(vec2(self.inset_px, mid - 1.5), vec2(mid - 1.5, self.inset_px), v)
|
|
let shi = mix(vec2(sz.x - self.inset_px, mid + 1.5), vec2(mid + 1.5, sz.y - self.inset_px), v)
|
|
let lo = vec2(au_snap(slo.x, o.x, px), au_snap(slo.y, o.y, px))
|
|
let hi = vec2(au_snap(shi.x, o.x, px), au_snap(shi.y, o.y, px))
|
|
var res = au_well(p, lo, hi, 0.0, px, vec3(0.030, 0.031, 0.034), 0.0, self.disabled, 1.0)
|
|
let cl = 1.0 - smoothstep(px * 0.4, px * 0.6, abs(b - mid))
|
|
let inslot = step(self.inset_px + px, a) * step(a, along - self.inset_px - px)
|
|
res = Finish.over(res, vec4(0.010, 0.010, 0.012, 1.0) * cl * inslot)
|
|
// The ladder: 2.5 point dots at a 5 point pitch, lit up to the value.
|
|
let pitch = 5.0
|
|
let k = floor((a - foot) / pitch + 0.5)
|
|
let ka = foot + k * pitch
|
|
let dc = mid - 9.0
|
|
let inrange = step(0.0, k) * step(ka, foot + travel + 0.1)
|
|
let dotp = mix(vec2(ka, dc), vec2(dc, sz.y - ka), v)
|
|
let lit = step(ka, foot + self.slide_pos * travel + 0.1) * (1.0 - self.disabled)
|
|
let lc = mix(vec3(0.30, 1.0, 0.40), vec3(1.0, 0.78, 0.20), step(0.8, (ka - foot) / max(travel, 1.0)))
|
|
res = mix(res, au_led(res, p, dotp, vec2(1.25 - px, 1.25 - px), lit, 0.0, px, mix(lc, vec3(1.0, 1.0, 1.0), 0.55), lc), inrange)
|
|
// The scale: a 1 dp tick at every dot, the ends and middle longer.
|
|
let tk = mid + 7.5
|
|
let major = step(abs(k - floor(travel / pitch * 0.5 + 0.5)), 0.1) + step(k, 0.1) + step(abs(ka - (foot + travel)), 0.1)
|
|
let tlen = mix(2.5, 4.5, clamp(major, 0.0, 1.0))
|
|
let tick = (1.0 - smoothstep(px * 0.4, px * 0.6, abs(a - ka))) * step(tk, b) * step(b, tk + tlen) * inrange
|
|
res = Finish.over(res, vec4(0.52, 0.53, 0.55, 1.0) * tick)
|
|
// THE CAP, the bench's crossfader block: corner 0.08 of the smaller
|
|
// half, edge 0.2, blend 0.02, the height less the half point a press
|
|
// sinks it; six ribs 0.3 deep over the middle 0.8 of its length. Its
|
|
// leading end (the left one lying, the top one standing) sits on the
|
|
// device pixel grid, so its ends stand crisp as it moves.
|
|
let live = 1.0 - self.disabled
|
|
let ab = vec2(a, b)
|
|
let hs = vec2(self.cap_px * 0.5, min(max(across * self.cap_cross, 4.0), across) * 0.5)
|
|
let at_free = foot + self.slide_pos * travel
|
|
let lead = Finish.snap(mix(o.x + at_free - hs.x, o.y + sz.y - at_free - hs.x, v), px)
|
|
let at = mix(lead - o.x + hs.x, o.y + sz.y - lead - hs.x, v)
|
|
let l = normalize(self.cap_light)
|
|
let lt = Cap.light(l, v)
|
|
let ground = self.ground_color.rgb
|
|
let light_ink = self.light_color.rgb
|
|
let shadow_ink = self.shadow_color.rgb
|
|
let body = self.handle_color.rgb
|
|
let ev = pow(2.0, self.cap_exposure)
|
|
let ro = self.cap_rolloff
|
|
let press = self.drag * live
|
|
let shape = vec4(0.08, 0.2, 0.02, max(self.cap_height - 0.5 * press, 0.5))
|
|
let rib = vec4(6.0, 0.3, 0.1, 0.9)
|
|
let zero4 = vec4(0.0, 0.0, 0.0, 0.0)
|
|
let q = ab - vec2(at, mid)
|
|
let r = shape.x * min(hs.x, hs.y)
|
|
let fp = Cap.sd_cap(q, hs, r, vec2(1.0, 0.0))
|
|
let cov = Cap.cover(fp.x, px)
|
|
var cap = body
|
|
var under = 0.0
|
|
var halo = 0.0
|
|
// Only a pixel within reach of the cap and what it throws runs the rest.
|
|
let far = abs(q) - hs
|
|
if max(far.x, far.y) < m + 2.0 {
|
|
// The bench's curves: along, chamfered up from each end to the
|
|
// level top; across, level then over the rounded shoulder; the
|
|
// two-step edge; the rib profile tiled over the rib band
|
|
let c_along = Cap.curve((q.x / hs.x + 1.0) * 0.5, 6.0, vec2(0.0, 0.0), vec2(0.0, 0.0), vec2(0.0, 0.0), vec2(0.07, 0.8), vec2(0.0, 0.0), vec2(-0.0233, 0.0), vec2(0.14, 1.0), vec2(0.0233, 0.0), vec2(-0.0233, 0.0), vec2(0.86, 1.0), vec2(0.0233, 0.0), vec2(0.0, 0.0), vec2(0.93, 0.8), vec2(0.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 0.0), vec2(0.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 0.0), vec2(0.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 0.0))
|
|
let c_across = Cap.curve(abs(q.y) / hs.y, 4.0, vec2(0.0, 1.0), vec2(0.1333, 0.0), vec2(-0.0433, 0.014), vec2(0.8, 0.98), vec2(0.0433, -0.014), vec2(-0.0233, 0.1143), vec2(0.93, 0.7), vec2(0.0233, -0.1143), vec2(0.0, 0.0), vec2(1.0, 0.0), vec2(0.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 0.0), vec2(0.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 0.0), vec2(0.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 0.0), vec2(0.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 0.0))
|
|
let c_edge = Cap.curve(Cap.edge_x(fp.x, hs, shape.y), 3.0, vec2(0.0, 0.0), vec2(0.0, 0.0), vec2(0.0, 0.0), vec2(0.5, 0.55), vec2(0.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 1.0), vec2(0.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 1.0), vec2(0.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 1.0), vec2(0.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 1.0), vec2(0.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 1.0), vec2(0.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 1.0))
|
|
let c_tooth = Cap.curve(Cap.rib_x(q, hs, rib), 4.0, vec2(0.0, 1.0), vec2(0.0917, 0.0), vec2(-0.05, 0.0), vec2(0.55, 1.0), vec2(0.05, 0.0), vec2(0.0, 0.0), vec2(0.7, 0.2), vec2(0.0, 0.0), vec2(-0.05, 0.0), vec2(1.0, 0.0), vec2(0.05, 0.0), vec2(0.0, 0.0), vec2(1.0, 0.0), vec2(0.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 0.0), vec2(0.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 0.0), vec2(0.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 0.0))
|
|
let f = Cap.field(q, hs, fp, c_along, c_across, c_edge, c_tooth, shape, rib, zero4, zero4, zero4)
|
|
let n = Cap.normal(f.yz, shape.w, v)
|
|
let eye = Cap.view(q, hs, self.cap_reflection_perspective, v)
|
|
cap = Cap.shade(n, l, eye, f.x, body, 0.0, 0.0, 1.0, self.cap_specular, self.cap_roughness, self.cap_gloss, self.cap_rim, self.cap_ao, self.cap_ao_reach, self.cap_reflection, self.cap_metal, self.cap_light_intensity, body, light_ink, shadow_ink, ground)
|
|
let grain = Cap.spun(q, hs, lt.xy) * self.cap_spun
|
|
cap = Cap.in_shadow(Cap.in_light(cap, clamp(grain, 0.0, 1.0) * 0.55, light_ink), clamp(-grain, 0.0, 1.0) * 0.42, shadow_ink)
|
|
cap = Cap.emit(cap, f.x, body, light_ink, self.cap_glow)
|
|
// Under it, the cast shadow from the block's top outline (0.862 of
|
|
// the length, 0.8655 of the width, its corner 0.253) and the
|
|
// contact shade; disabled, the cast shadow goes and the contact
|
|
// thins.
|
|
let sh = Cap.shadow(q, hs, r, vec3(0.862, 0.8655, 0.253), lt, shape.w, self.cap_shadow_blur, self.cap_shadow * live, px)
|
|
let ct = Cap.contact(fp.x, max(shape.w * 0.3, 1.0), self.cap_contact * (1.0 - 0.6 * self.disabled))
|
|
under = clamp(sh + ct, 0.0, 1.0) * (1.0 - cov)
|
|
halo = Cap.glow(fp.x, hs, self.cap_glow) * 0.9 * (1.0 - cov)
|
|
}
|
|
// The block toned as the bench tones. Hovered, it lifts, twice as much
|
|
// when it stands less than 23.5 points tall on screen; held, the lift
|
|
// goes; disabled, it dulls, grey drawn into it.
|
|
var face = Cap.tone(cap, ev, ro)
|
|
let small = 1.0 - step(23.5, mix(hs.y, hs.x, v) * 2.0)
|
|
face = face + vec3(0.04, 0.04, 0.042) * (1.0 + small) * self.hover * (1.0 - press) * live
|
|
let fl = Finish.lum(face)
|
|
face = mix(face, mix(face, vec3(fl, fl, fl), 0.45) * 0.8, self.disabled)
|
|
// Laid over the slot, the ladder and the scale and whatever the fader
|
|
// lies on, premultiplied: the shadow darkens all of it by the shadow
|
|
// ink's mean, a glow adds its light, the block covers it.
|
|
res = Finish.over(res, vec4(0.0, 0.0, 0.0, under * (1.0 - (shadow_ink.x + shadow_ink.y + shadow_ink.z) / 3.0)))
|
|
res = res + vec4(Cap.tone(body * halo, ev, ro), 0.0)
|
|
res = mix(res, vec4(face, 1.0), cov)
|
|
// Focus: the sheet's blue ring just outside the outline, and a breath
|
|
// of it.
|
|
let ring = Finish.ring_out(fp.x, 0.0, px, px) * 0.9 + Finish.glow(fp.x - px, 0.7) * 0.35 * (1.0 - Finish.cover(fp.x, px))
|
|
return Finish.over(res, vec4(0.227, 0.612, 1.0, 1.0) * clamp(ring, 0.0, 1.0) * self.focus * live)
|
|
}
|
|
|
|
// ---------------------------------------------------------------- knobs
|
|
|
|
// The angle a knob's value stands at, and the direction it points: angles
|
|
// run from straight down, clockwise, the gap at the bottom.
|
|
let au_dir = fn(a: float) -> vec2 {
|
|
return vec2(-sin(a), cos(a))
|
|
}
|
|
let au_angle = fn(q: vec2) -> float {
|
|
var ang = atan2(-q.x, q.y)
|
|
if ang < 0.0 {
|
|
ang = ang + 2.0 * PI
|
|
}
|
|
return ang
|
|
}
|
|
|
|
// A SMALL KNOB, as a dash has them: a machined aluminium knob in a
|
|
// chrome-rimmed socket. Out to in: a polished chrome ring on the ground, a
|
|
// narrow dark socket whose floor is lit amber from the start of the scale
|
|
// up to the value (from the origin, on a knob that has one), the knob's
|
|
// soft shadow in the socket, its edge knurled bright silver with fine
|
|
// ridges that turn with it, a thin dark groove, a light spun-aluminium cap
|
|
// (the lathe's fan of light along the light's axis over fine concentric
|
|
// rings, brightest up and to the left), a small dark centre, and a dark
|
|
// index line on the cap toward the value, red while held. Angles from
|
|
// straight down, clockwise.
|
|
let au_knurl = fn(p: vec2, c: vec2, rr: float, px: float, slide: float, origin: float, arc_origin: float, gap: float, hover: float, held: float, focus: float, disabled: float) -> vec4 {
|
|
let q = p - c
|
|
let rl = length(q)
|
|
let nk = q / max(rl, 0.001)
|
|
let on = 1.0 - disabled
|
|
let rb = Finish.snap(rr, px)
|
|
let bw = max(Finish.snap(0.08 * rb, px), px * 2.0)
|
|
let rs = rb - bw
|
|
let R = Finish.snap(0.82 * rb, px)
|
|
let sh = Finish.drop(p, c, vec2(rb, rb), rb, vec2(0.0, 1.2), 1.6) * 0.75 * (1.0 - smoothstep(rb + 3.0, rb + 6.0, rl))
|
|
var res = vec4(0.0, 0.0, 0.0, sh)
|
|
res = Finish.over(res, au_chrome_ring(p, c, rs, rb, px, hover))
|
|
// The socket, and the amber light on its floor over the value's arc.
|
|
let one = PI / 180.0
|
|
let sweep = 2.0 * PI - gap * one
|
|
let start = gap * one * 0.5
|
|
let ve = start + sweep * slide
|
|
let oe = start + sweep * origin
|
|
let a0 = mix(start, min(oe, ve), arc_origin)
|
|
let a1 = mix(ve, max(oe, ve), arc_origin)
|
|
let ang = au_angle(q)
|
|
let ac = clamp(ang, a0, a1)
|
|
let ap = c + au_dir(ac) * ((R + rs) * 0.5)
|
|
let da = length(p - ap)
|
|
let live = step(0.002, a1 - a0) * on
|
|
let sd = rl - rs
|
|
var sc = mix(vec3(0.010, 0.010, 0.012), vec3(0.045, 0.046, 0.050), smoothstep(-0.6, 0.9, nk.y))
|
|
let aw = max((rs - R) * 0.32, px * 0.8)
|
|
let arc = Finish.cover(da - aw, px) * live
|
|
let ag = exp(-max(da - aw, 0.0) / 1.2) * 0.5 * live
|
|
sc = mix(sc, vec3(1.0, 0.55, 0.12), clamp(ag, 0.0, 1.0))
|
|
sc = mix(sc, vec3(1.0, 0.86, 0.55), arc)
|
|
let sa = Finish.cover(sd, px)
|
|
res = Finish.over(res, vec4(sc * sa, sa))
|
|
let ks = Finish.drop(p, c, vec2(R, R), R, vec2(0.0, 0.08 * R), 0.10 * R) * 0.8 * sa
|
|
res = Finish.over(res, vec4(0.0, 0.0, 0.0, ks))
|
|
// The edge: bright silver knurling, the ridges turning with the knob,
|
|
// lit on its upper side and greyer toward its foot.
|
|
let up = smoothstep(-1.0, 1.0, -nk.y)
|
|
let nr = floor(R * 3.2)
|
|
let ridge = 0.5 + 0.5 * cos(nr * (ang - ve))
|
|
var kc = mix(vec3(0.30, 0.305, 0.32), vec3(0.88, 0.885, 0.90), pow(ridge, 1.5) * (0.35 + 0.65 * up))
|
|
kc = kc * (0.62 + 0.38 * up)
|
|
kc = mix(kc, vec3(0.010, 0.010, 0.012), Finish.band(rl - R, 0.0, px, px))
|
|
// A thin dark groove between the knurling and the cap.
|
|
let rt = Finish.snap(0.78 * R, px)
|
|
let gw = max(px, Finish.snap(0.035 * R, px))
|
|
kc = mix(kc, vec3(0.06, 0.062, 0.068), Finish.cover(rl - rt, px))
|
|
// The cap: light spun aluminium, the lathe's fan of light along the
|
|
// light's axis over fine concentric rings, the upper left brightest.
|
|
let rc = rt - gw
|
|
let t = clamp((p.y - (c.y - rc)) / max(2.0 * rc, 1.0), 0.0, 1.0)
|
|
var top = mix(vec3(0.800, 0.805, 0.815), vec3(0.560, 0.565, 0.580), smoothstep(0.0, 1.0, t))
|
|
let fan = cos(2.0 * (atan2(q.y, q.x) - atan2(-0.94, -0.35)))
|
|
let rings = au_noise(vec2(rl * 3.0, 0.37)) - 0.5 + 0.6 * (au_noise(vec2(rl * 0.9, 9.1)) - 0.5)
|
|
top = top * (1.0 + smoothstep(0.0, 1.5, rl) * (0.16 * fan + 0.10 * rings))
|
|
let sp = (q - vec2(-0.30 * rc, -0.35 * rc)) / max(0.45 * rc, 0.8)
|
|
top = min(top + vec3(0.10, 0.10, 0.10) * exp(-dot(sp, sp)) + vec3(0.03, 0.03, 0.03) * hover * on, vec3(1.0, 1.0, 1.0))
|
|
top = mix(top, vec3(0.97, 0.975, 0.98), Finish.band(rl - rc, px, px * 2.0, px) * smoothstep(0.2, 0.9, -nk.y) * 0.7)
|
|
kc = mix(kc, top, Finish.cover(rl - rc, px))
|
|
// A small dark centre where the cap was held on the lathe.
|
|
kc = mix(kc, vec3(0.22, 0.225, 0.235), Finish.cover(rl - max(0.07 * rc, px * 1.2), px))
|
|
// The index: a dark line on the cap toward the value, red while held.
|
|
let vd = au_dir(ve)
|
|
let pl = dot(q, vd)
|
|
let pa = abs(q.x * vd.y - q.y * vd.x)
|
|
let pw = max(0.06 * R, px * 1.1)
|
|
let ptr = Finish.cover(pa - pw, px) * Finish.cover(abs(pl - 0.58 * rc) - 0.32 * rc, px)
|
|
kc = mix(kc, mix(vec3(0.07, 0.072, 0.08), vec3(0.85, 0.08, 0.05), held), ptr * (0.5 + 0.5 * on))
|
|
let kl = Finish.lum(kc)
|
|
kc = mix(kc, vec3(kl, kl, kl) * 0.6 + vec3(0.03, 0.03, 0.035), disabled)
|
|
let ka = Finish.cover(rl - R, px)
|
|
res = Finish.over(res, vec4(kc * ka, ka))
|
|
let fr = Finish.ring_out(rl - rb, 0.0, px, px) * 0.9 + Finish.glow(rl - rb - px, 0.7) * 0.3 * (1.0 - Finish.cover(rl - rb, px))
|
|
return Finish.over(res, vec4(0.227, 0.612, 1.0, 1.0) * clamp(fr, 0.0, 1.0) * focus * on)
|
|
}
|
|
|
|
// A DIAL is a tachometer: a polished chrome bezel round a black face, a
|
|
// scale of ten with a tick at every half, numerals 0 to 10 in a squared
|
|
// dash type, the red line over the last fifth (its ticks and numerals red
|
|
// too), and a red needle on a black hub, its light lying on the face round
|
|
// it. The needle stands at the value; held, its glow swells. A faint glass
|
|
// reflection lies over the upper face. Angles from straight down,
|
|
// clockwise, the gap at the bottom.
|
|
let au_tach = fn(p: vec2, c: vec2, rr: float, px: float, slide: float, gap: float, hover: float, held: float, focus: float, disabled: float) -> vec4 {
|
|
let q = p - c
|
|
let rl = length(q)
|
|
let nk = q / max(rl, 0.001)
|
|
let on = 1.0 - disabled
|
|
let rb = Finish.snap(rr, px)
|
|
let bw = Finish.snap(max(0.10 * rb, 2.0), px)
|
|
let F = rb - bw - Finish.snap(max(0.025 * rb, px), px)
|
|
// The gauge's shadow on the ground.
|
|
let sh = Finish.drop(p, c, vec2(rb, rb), rb, vec2(0.0, 2.0), 2.4) * 0.8 * (1.0 - smoothstep(rb + 4.0, rb + 8.0, rl))
|
|
var res = vec4(0.0, 0.0, 0.0, sh)
|
|
res = Finish.over(res, au_chrome_ring(p, c, rb - bw, rb, px, hover))
|
|
// The step inside the bezel, and the face.
|
|
let sa = Finish.cover(rl - (rb - bw) - px * 0.5, px)
|
|
res = Finish.over(res, vec4(0.008, 0.008, 0.010, 1.0) * sa)
|
|
let one = PI / 180.0
|
|
let sweep = 2.0 * PI - gap * one
|
|
let start = gap * one * 0.5
|
|
let ve = start + sweep * slide
|
|
let ang = au_angle(q)
|
|
let tp = (ang - start) / max(sweep, 0.01)
|
|
let inscale = step(-0.02, tp) * step(tp, 1.02)
|
|
let zone = step(0.795, tp) * inscale
|
|
let tf = rl / max(F, 1.0)
|
|
var face = mix(vec3(0.090, 0.091, 0.096), vec3(0.030, 0.030, 0.033), smoothstep(0.0, 1.0, tf))
|
|
// A lighter sweep of the face's own satin under the hub, as on the
|
|
// pictures' gauges, and the bezel's shadow under its top edge.
|
|
let lw = (q - vec2(0.0, 0.35 * F)) / vec2(0.75 * F, 0.45 * F)
|
|
face = face + vec3(0.040, 0.041, 0.044) * exp(-dot(lw, lw))
|
|
face = face * (1.0 - 0.55 * Finish.inset(p, c, vec2(F, F), F, vec2(0.0, 1.5), 2.0))
|
|
// The red line: a band at the rim of the face over the last fifth.
|
|
let zr = Finish.cover(abs(rl - 0.90 * F) - 0.06 * F, px) * zone
|
|
let zc = mix(vec3(0.90, 0.07, 0.05), vec3(0.55, 0.02, 0.02), smoothstep(0.84, 0.96, tf))
|
|
face = mix(face, zc, zr * (0.55 + 0.45 * on))
|
|
// The scale: a tick at every half, the whole numbers longer.
|
|
let j = clamp(floor(tp * 20.0 + 0.5), 0.0, 20.0)
|
|
let aj = start + sweep * j / 20.0
|
|
let dj = au_dir(aj)
|
|
let along = dot(q, dj)
|
|
let across = abs(q.x * dj.y - q.y * dj.x)
|
|
let major = 1.0 - step(0.5, j - 2.0 * floor(j * 0.5 + 0.01))
|
|
let tw = mix(max(0.012 * F, px * 0.6), max(0.024 * F, px), major)
|
|
let t0 = mix(0.85 * F, 0.77 * F, major)
|
|
let tick = Finish.cover(across - tw, px) * Finish.cover(abs(along - (t0 + 0.955 * F) * 0.5) - (0.955 * F - t0) * 0.5, px) * inscale
|
|
let tz = step(0.795, j / 20.0)
|
|
let ink = mix(vec3(0.93, 0.94, 0.95), vec3(1.0, 0.24, 0.18), tz)
|
|
face = mix(face, ink, tick * (0.45 + 0.55 * on))
|
|
// The numerals, upright, set on a circle inside the ticks.
|
|
let i = clamp(floor(tp * 10.0 + 0.5), 0.0, 10.0)
|
|
let ai = start + sweep * i / 10.0
|
|
let hgt = 0.15 * F
|
|
let nc = c + au_dir(ai) * (0.62 * F)
|
|
let nd = au_number(p, nc, hgt, i, max(0.13 * hgt, px * 1.3))
|
|
let nz = step(7.5, i)
|
|
face = mix(face, mix(vec3(0.93, 0.94, 0.95), vec3(1.0, 0.24, 0.18), nz), Finish.cover(nd, px) * (0.45 + 0.55 * on))
|
|
let fa = Finish.cover(rl - F, px)
|
|
res = Finish.over(res, vec4(face * fa, fa))
|
|
// The needle: a tapered red blade from a short tail behind the hub to
|
|
// the ticks, its shadow down and to the right, a glow round it.
|
|
let vd = au_dir(ve)
|
|
let nl = dot(q, vd)
|
|
let na = abs(q.x * vd.y - q.y * vd.x)
|
|
let u = clamp((nl + 0.18 * F) / (1.10 * F), 0.0, 1.0)
|
|
let hw = mix(0.038 * F, 0.010 * F, u) + px * 0.25
|
|
let blade = max(na - hw, max(-0.18 * F - nl, nl - 0.92 * F))
|
|
let qs = q - vec2(0.8, 1.4)
|
|
let nls = dot(qs, vd)
|
|
let nas = abs(qs.x * vd.y - qs.y * vd.x)
|
|
let us = clamp((nls + 0.18 * F) / (1.10 * F), 0.0, 1.0)
|
|
let bs = max(nas - mix(0.038 * F, 0.010 * F, us), max(-0.18 * F - nls, nls - 0.92 * F))
|
|
res = Finish.over(res, vec4(0.0, 0.0, 0.0, 0.55 * Finish.cover(bs, 1.2) * fa))
|
|
let gx = max(blade, 0.0)
|
|
let glow = (0.40 * exp(-(gx * gx) / (2.0 * 0.9 * 0.9)) + 0.18 * exp(-(gx * gx) / (2.0 * 2.4 * 2.4))) * (1.0 - Finish.cover(blade, px)) * fa * on * (0.8 + 0.5 * held)
|
|
res = Finish.over(res, vec4(1.0, 0.12, 0.06, 1.0) * clamp(glow, 0.0, 1.0))
|
|
var nc2 = mix(vec3(1.0, 0.20, 0.12), vec3(0.75, 0.04, 0.03), smoothstep(0.0, 1.0, na / max(hw, 0.01)))
|
|
nc2 = mix(nc2, vec3(1.0, 0.62, 0.55), (1.0 - smoothstep(0.0, 0.5, na / max(hw, 0.01))) * 0.5)
|
|
let ng = Finish.lum(nc2)
|
|
nc2 = mix(nc2, vec3(ng, ng, ng) * 0.7, disabled)
|
|
let ba = Finish.cover(blade, px)
|
|
res = Finish.over(res, vec4(nc2 * ba, ba))
|
|
// The hub: a black dome in a thin chrome ring, a highlight up and left.
|
|
let hr = Finish.snap(max(0.12 * F, 2.5), px)
|
|
res = Finish.over(res, au_chrome_ring(p, c, hr - max(px * 1.5, 0.025 * F), hr, px, hover))
|
|
let hq = (q - vec2(-0.3 * hr, -0.35 * hr)) / max(0.45 * hr, 0.5)
|
|
let hc = mix(vec3(0.10, 0.10, 0.11), vec3(0.02, 0.02, 0.022), smoothstep(-1.0, 1.0, q.y / max(hr, 0.5))) + vec3(0.30, 0.30, 0.32) * exp(-dot(hq, hq))
|
|
let hd = rl - (hr - max(px * 1.5, 0.025 * F))
|
|
let ha = Finish.cover(hd, px)
|
|
res = Finish.over(res, vec4(hc * ha, ha))
|
|
// The glass: a faint pale crescent over the upper face.
|
|
let gq = (q - vec2(-0.1 * F, -0.55 * F)) / vec2(0.85 * F, 0.38 * F)
|
|
let gls = exp(-dot(gq, gq) * 1.5) * smoothstep(-0.1, 0.4, -q.y / max(F, 1.0)) * 0.05 * fa
|
|
res = vec4(res.rgb + vec3(gls, gls, gls) * res.a, res.a)
|
|
let fr = Finish.ring_out(rl - rb, 0.0, px, px) * 0.9 + Finish.glow(rl - rb - px, 0.8) * 0.35 * (1.0 - Finish.cover(rl - rb, px))
|
|
return Finish.over(res, vec4(0.227, 0.612, 1.0, 1.0) * clamp(fr, 0.0, 1.0) * focus * on)
|
|
}
|
|
|
|
// The quad runs 10 points past the box, room for the knob's shadow.
|
|
mod.widgets.RotaryKnob.clip_x = false
|
|
mod.widgets.RotaryKnob.clip_y = false
|
|
mod.widgets.RotaryKnob.draw_bg.vertex = fn() {
|
|
let m = 10.0
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
|
|
}
|
|
mod.widgets.RotaryKnob.draw_bg.pixel = fn() {
|
|
let m = 10.0
|
|
let px = au_px(self.draw_pass.dpi_factor)
|
|
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let dia = min(self.rect_size.x, self.rect_size.y)
|
|
let c = vec2(Finish.snap(self.rect_pos.x + self.rect_size.x * 0.5, px) - self.rect_pos.x, Finish.snap(self.rect_pos.y + self.rect_size.y - dia * 0.5, px) - self.rect_pos.y)
|
|
return au_knurl(p, c, dia * 0.5 - 1.0, px, self.slide_pos, self.origin_pos, self.arc_origin, self.gap, self.hover, self.drag, self.focus, self.disabled)
|
|
}
|
|
mod.widgets.RotaryFlat.clip_x = false
|
|
mod.widgets.RotaryFlat.clip_y = false
|
|
mod.widgets.RotaryFlat.draw_bg.vertex = fn() {
|
|
let m = 10.0
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
|
|
}
|
|
mod.widgets.RotaryFlat.draw_bg.pixel = fn() {
|
|
let m = 10.0
|
|
let px = au_px(self.draw_pass.dpi_factor)
|
|
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let dia = min(self.rect_size.x, self.rect_size.y - 22.0)
|
|
let c = vec2(Finish.snap(self.rect_pos.x + self.rect_size.x * 0.5, px) - self.rect_pos.x, Finish.snap(self.rect_pos.y + 20.0 + dia * 0.5, px) - self.rect_pos.y)
|
|
return au_tach(p, c, dia * 0.5 - 1.0, px, self.slide_pos, self.gap, self.hover, self.drag, self.focus, self.disabled)
|
|
}
|
|
|
|
// ---------------------------------------------------------------- fields
|
|
|
|
// A FIELD is a well of dark carbon weave behind a polished silver frame, as
|
|
// the pictures draw every entry box. The frame is two device pixels inside
|
|
// the box's edge: the outer row a darker silver, the inner one bright where
|
|
// it faces the light (along the top and the foot), and inside it a black
|
|
// line where the floor drops away. The floor is the carbon weave held dark,
|
|
// so the words and the placeholder keep their contrast on it, under the
|
|
// frame's shadow along its top. Under
|
|
// the hand the floor lifts a little; focused, the frame takes the blue and
|
|
// a blue light lies along its inside. A text field clips what it holds, so
|
|
// everything keeps inside its own box.
|
|
let au_field = fn(p: vec2, size: vec2, o: vec2, px: float, fill: vec3, hover: float, focus: float, disabled: float) -> vec4 {
|
|
let lo = vec2(au_snap(0.0, o.x, px), au_snap(0.0, o.y, px))
|
|
let hi = vec2(au_snap(size.x - px * 0.49, o.x, px), au_snap(size.y - px * 0.49, o.y, px))
|
|
let c = (lo + hi) * 0.5
|
|
let r = 3.0
|
|
let d = Material.sd_box(p, c, (hi - lo) * 0.5, r)
|
|
let t = clamp((p.y - lo.y) / max(hi.y - lo.y, 1.0), 0.0, 1.0)
|
|
let live = 1.0 - disabled
|
|
// The floor.
|
|
var fl = au_carbon(p + o, 2.5, 0.50 + 0.10 * hover * live) * (1.0 - 0.35 * disabled) + fill * 0.25
|
|
let fd = d + px * 2.0
|
|
fl = fl * (1.0 - 0.75 * Finish.inset(p, c, (hi - lo) * 0.5 - vec2(px * 2.0, px * 2.0), r - px * 2.0, vec2(0.0, 1.2), 1.4))
|
|
fl = mix(fl, vec3(0.0, 0.0, 0.0), Finish.band(fd, 0.0, px, px) * 0.85)
|
|
// The frame: a polished silver band two device pixels wide.
|
|
let o1 = Finish.band(d, 0.0, px, px)
|
|
let o2 = Finish.band(d, px, px * 2.0, px)
|
|
let lit = max(1.0 - smoothstep(0.0, 0.25, t), smoothstep(0.75, 1.0, t))
|
|
var s1 = mix(vec3(0.240, 0.244, 0.254), vec3(0.460, 0.466, 0.480), lit)
|
|
var s2 = mix(vec3(0.400, 0.405, 0.418), vec3(0.720, 0.726, 0.738), lit)
|
|
s1 = mix(s1, vec3(0.22, 0.42, 0.70), focus * live * 0.8)
|
|
s2 = mix(s2, vec3(0.55, 0.80, 1.0), focus * live * 0.85)
|
|
s1 = s1 * (1.0 - 0.45 * disabled)
|
|
s2 = s2 * (1.0 - 0.45 * disabled)
|
|
var col = mix(fl, s2, o2)
|
|
col = mix(col, s1, o1)
|
|
// The focus light along the frame's inside.
|
|
let depth = max(-fd, 0.0)
|
|
col = col + vec3(0.10, 0.36, 0.80) * exp(-depth / 1.6) * Finish.cover(fd, px) * focus * live * 0.6
|
|
let a = Finish.cover(d, px)
|
|
return vec4(col * a, a)
|
|
}
|
|
|
|
// Text fields: the framed well, 26 points tall. An input with no fill of
|
|
// its own (the one a slider, a combo box or a well holds) draws nothing.
|
|
mod.widgets.TextInputFlat.draw_bg.pixel = fn() {
|
|
if self.color.a < 0.01 {
|
|
return vec4(0.0, 0.0, 0.0, 0.0)
|
|
}
|
|
let px = au_px(self.draw_pass.dpi_factor)
|
|
return au_field(self.pos * self.rect_size, self.rect_size, self.rect_pos, px, vec3(0.030, 0.031, 0.034), self.hover, self.focus, self.disabled)
|
|
}
|
|
// A field well (a box that holds an input between a leading and a trailing
|
|
// slot) is the same framed well.
|
|
mod.widgets.FieldWell.draw_bg.pixel = fn() {
|
|
let px = au_px(self.draw_pass.dpi_factor)
|
|
return au_field(self.pos * self.rect_size, self.rect_size, self.rect_pos, px, vec3(0.030, 0.031, 0.034), self.hover, self.focus, self.disabled)
|
|
}
|
|
mod.widgets.TextInputFlat.padding = mod.turtle.Inset{left: 8.0 right: 8.0 top: 4.0 bottom: 4.0}
|
|
mod.widgets.TextInputFlat.min_height = 26.0
|
|
mod.widgets.TextInputFlat.draw_cursor.color = #f4fbff
|
|
mod.widgets.TextInputFlat.draw_text.color = #dcdde0
|
|
mod.widgets.TextInputFlat.draw_text.color_empty = #868a90
|
|
mod.widgets.TextInputFlat.draw_text.color_disabled = #63666b
|
|
|
|
// The number field: the same framed well, its stepper a pair of small caps
|
|
// standing in it.
|
|
mod.widgets.NumberField.draw_bg.pixel = fn() {
|
|
let px = au_px(self.draw_pass.dpi_factor)
|
|
return au_field(self.pos * self.rect_size, self.rect_size, self.rect_pos, px, vec3(0.030, 0.031, 0.034), self.hover, self.focus, self.disabled)
|
|
}
|
|
mod.widgets.NumberField.spin_width = 16.0
|
|
mod.widgets.NumberSpin.width = 16.0
|
|
// The widget's own filled triangles are hidden; the face strokes a 5 x 3
|
|
// point chevron on each cap instead.
|
|
mod.widgets.NumberSpin.draw_text.color = #00000000
|
|
mod.widgets.NumberSpin.draw_text.color_disabled = #00000000
|
|
mod.widgets.NumberSpin.draw_bg.pixel = fn() {
|
|
let px = au_px(self.draw_pass.dpi_factor)
|
|
let o = self.rect_pos
|
|
let p = self.pos * self.rect_size
|
|
// Two small caps, one over the other, three device pixels in from the
|
|
// frame and a device pixel of floor between them. Under the hand a cap
|
|
// lifts; pressed, it sinks.
|
|
let x0 = au_snap(0.0, o.x, px) + px
|
|
let xr = au_snap(self.rect_size.x - px * 0.49, o.x, px) - px * 4.0
|
|
let yt = au_snap(0.0, o.y, px) + px * 4.0
|
|
let yb = au_snap(self.rect_size.y - px * 0.49, o.y, px) - px * 4.0
|
|
let mid = au_snap(self.rect_size.y * 0.5, o.y, px)
|
|
let live = 1.0 - self.disabled
|
|
let hu = max(0.0, self.hot)
|
|
let hd = max(0.0, -self.hot)
|
|
let du = max(0.0, self.down)
|
|
let dd = max(0.0, -self.down)
|
|
var res = au_cap(p, vec2(x0, yt), vec2(xr, mid - px), 1.5, px, hu, du, self.disabled, 0.0, 0.0, vec3(0.0, 0.0, 0.0), vec4(0.20, 0.20, 0.21, 1.0), 1.0)
|
|
res = Finish.over(res, au_cap(p, vec2(x0, mid + px), vec2(xr, yb), 1.5, px, hd, dd, self.disabled, 0.0, 0.0, vec3(0.0, 0.0, 0.0), vec4(0.20, 0.20, 0.21, 1.0), 1.0))
|
|
let ink = mix(vec3(0.80, 0.81, 0.83), vec3(0.36, 0.37, 0.4), self.disabled)
|
|
let cx = (x0 + xr) * 0.5
|
|
let cu = (yt + mid) * 0.5 + px * 0.5 * du
|
|
let cd = (mid + yb) * 0.5 + px * 0.5 * dd
|
|
let du2 = min(au_seg(p, vec2(cx - 2.5, cu + 1.25), vec2(cx, cu - 1.25)), au_seg(p, vec2(cx, cu - 1.25), vec2(cx + 2.5, cu + 1.25)))
|
|
let dd2 = min(au_seg(p, vec2(cx - 2.5, cd - 1.25), vec2(cx, cd + 1.25)), au_seg(p, vec2(cx, cd + 1.25), vec2(cx + 2.5, cd - 1.25)))
|
|
let k = min(du2, dd2) - px * 0.8
|
|
return Finish.over(res, vec4(ink, 1.0) * Finish.cover(k, px) * (0.4 + 0.6 * live))
|
|
}
|
|
|
|
// The engraved divider and the chevron, laid on a cap.
|
|
let au_chevron_zone = fn(dst: vec4, p: vec2, lo: vec2, hi: vec2, px: float, disabled: float) -> vec4 {
|
|
let dx = hi.x - 18.0
|
|
let inside = step(lo.y + 4.0, p.y) * step(p.y, hi.y - 4.0)
|
|
let dark = step(dx - px, p.x) * step(p.x, dx) * inside
|
|
let light = step(dx, p.x) * step(p.x, dx + px) * inside
|
|
var res = Finish.over(dst, vec4(0.0, 0.0, 0.0, 1.0) * dark)
|
|
res = Finish.over(res, vec4(0.42, 0.43, 0.45, 1.0) * light * 0.8)
|
|
// The chevron: 7 x 4 points, a 1.5 point stroke, a dark line under it.
|
|
let cc = vec2(dx + 9.0, (lo.y + hi.y) * 0.5)
|
|
let sd = Sdf2d.viewport(p - vec2(0.0, px))
|
|
sd.move_to(cc.x - 3.5, cc.y - 2.0)
|
|
sd.line_to(cc.x, cc.y + 2.0)
|
|
sd.line_to(cc.x + 3.5, cc.y - 2.0)
|
|
sd.stroke(vec4(0.0, 0.0, 0.0, 0.6), 1.0)
|
|
res = Finish.over(res, sd.result)
|
|
let sdf = Sdf2d.viewport(p)
|
|
sdf.move_to(cc.x - 3.5, cc.y - 2.0)
|
|
sdf.line_to(cc.x, cc.y + 2.0)
|
|
sdf.line_to(cc.x + 3.5, cc.y - 2.0)
|
|
sdf.stroke(vec4(mix(vec3(0.85, 0.86, 0.88), vec3(0.36, 0.37, 0.4), disabled), 1.0), 1.0)
|
|
return Finish.over(res, sdf.result)
|
|
}
|
|
// A choice box: a graphite cap with a panel of dark brushed steel let into
|
|
// its face, a black seam round the panel, the chevron zone at its right.
|
|
// Open, the cap is held down.
|
|
let au_choice = fn(p: vec2, o: vec2, lo: vec2, hi: vec2, px: float, hover: float, down: float, disabled: float, focus: float) -> vec4 {
|
|
let r = 3.5
|
|
var res = au_cap(p, lo, hi, r, px, hover, down, disabled, focus, 1.0, vec3(-0.03, -0.03, -0.03), vec4(0.20, 0.20, 0.21, 1.0), 1.0)
|
|
let c = (lo + hi) * 0.5
|
|
let h = (hi - lo) * 0.5
|
|
let wd = Material.sd_box(p, c, h - vec2(2.0, 2.0), max(r - 1.5, 1.0))
|
|
var wv = au_dark_steel(p + o, px, 0.4 + 0.8 * hover) + vec3(0.012, 0.012, 0.013)
|
|
wv = wv * (1.0 - 0.5 * down) * (1.0 - 0.5 * disabled)
|
|
wv = wv * (1.0 - 0.6 * Finish.inset(p, c, h - vec2(2.0, 2.0), max(r - 1.5, 1.0), vec2(0.0, 1.0), 1.2))
|
|
wv = mix(wv, vec3(0.0, 0.0, 0.0), Finish.band(wd, 0.0, px, px) * 0.8)
|
|
let wa = Finish.cover(wd, px)
|
|
res = Finish.over(res, vec4(wv * wa, wa))
|
|
return au_chevron_zone(res, p, lo, hi, px, disabled)
|
|
}
|
|
|
|
// The drop-down: the choice box, 26 points, its chevron zone 18 points.
|
|
mod.widgets.DropDownFlat.padding = mod.turtle.Inset{left: 8.0 right: 26.0 top: 4.0 bottom: 4.0}
|
|
mod.widgets.DropDownFlat.clip_x = false
|
|
mod.widgets.DropDownFlat.clip_y = false
|
|
mod.widgets.DropDownFlat.draw_bg.vertex = fn() {
|
|
let m = 7.0
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
|
|
}
|
|
mod.widgets.DropDownFlat.draw_bg.pixel = fn() {
|
|
let m = 7.0
|
|
let px = au_px(self.draw_pass.dpi_factor)
|
|
let o = self.rect_pos
|
|
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let lo = vec2(au_snap(0.0, o.x, px), au_snap(0.0, o.y, px))
|
|
let hi = vec2(au_snap(self.rect_size.x, o.x, px), au_snap(self.rect_size.y, o.y, px))
|
|
return au_choice(p, o, lo, hi, px, self.hover, max(self.down, self.active), self.disabled, self.focus)
|
|
}
|
|
|
|
// The combo box: the same choice box; its list opens from the chevron zone.
|
|
mod.widgets.ComboBoxFlat.padding = mod.turtle.Inset{left: 8.0 right: 26.0 top: 4.0 bottom: 4.0}
|
|
mod.widgets.ComboBoxFlat.input.min_height = 0.0
|
|
mod.widgets.ComboBoxFlat.clip_x = false
|
|
mod.widgets.ComboBoxFlat.clip_y = false
|
|
mod.widgets.ComboBoxFlat.draw_bg.vertex = fn() {
|
|
let m = 7.0
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
|
|
}
|
|
mod.widgets.ComboBoxFlat.draw_bg.pixel = fn() {
|
|
let m = 7.0
|
|
let px = au_px(self.draw_pass.dpi_factor)
|
|
let o = self.rect_pos
|
|
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let lo = vec2(au_snap(0.0, o.x, px), au_snap(0.0, o.y, px))
|
|
let hi = vec2(au_snap(self.rect_size.x, o.x, px), au_snap(self.rect_size.y, o.y, px))
|
|
return au_choice(p, o, lo, hi, px, self.hover, 0.0, self.disabled, self.focus)
|
|
}
|
|
|
|
// Open lists: a plate of dark brushed graphite in a silver edge, rows that
|
|
// lift under the hand, and the chosen row lit blue -- the one fill in this
|
|
// family -- with a pale line along its top.
|
|
let au_menu = fn(p: vec2, size: vec2, px: float) -> vec4 {
|
|
let lo = vec2(0.0, 0.0)
|
|
let hi = size
|
|
let c = (lo + hi) * 0.5
|
|
let d = Material.sd_box(p, c, (hi - lo) * 0.5, 3.0)
|
|
var col = mix(vec3(0.120, 0.122, 0.130), vec3(0.075, 0.076, 0.082), p.y / max(size.y, 1.0))
|
|
let tx = au_plate_tex(p, px)
|
|
col = col + vec3(tx, tx, tx)
|
|
col = mix(col, vec3(0.50, 0.51, 0.53), Finish.band(d, px, px * 2.0, px) * step(p.y, c.y) * step(3.0, p.y))
|
|
col = mix(col, vec3(0.45, 0.46, 0.48), Finish.band(d, 0.0, px, px) * 0.5)
|
|
col = mix(col, vec3(0.010, 0.010, 0.012), Finish.band(d, 0.0, px, px) * 0.5)
|
|
let a = Finish.cover(d, px)
|
|
return vec4(col * a, a)
|
|
}
|
|
let au_row = fn(p: vec2, size: vec2, px: float, hover: float, active: float, disabled: float) -> vec4 {
|
|
let top = 1.0 - step(px, p.y)
|
|
var col = vec4(1.0, 1.0, 1.0, 1.0) * 0.045 * hover * (1.0 - disabled)
|
|
let t = clamp(p.y / max(size.y, 1.0), 0.0, 1.0)
|
|
let sel = mix(vec3(0.200, 0.470, 0.820), vec3(0.120, 0.320, 0.620), t)
|
|
let fill = vec4(mix(sel, vec3(0.55, 0.76, 1.0), top), 1.0)
|
|
return mix(col, fill, active)
|
|
}
|
|
// The open list is clipped to its own box by the popup that holds it, so
|
|
// its shadow cannot be drawn from here.
|
|
mod.widgets.PopupMenuFlat.draw_bg.pixel = fn() {
|
|
let px = au_px(self.draw_pass.dpi_factor)
|
|
return au_menu(self.pos * self.rect_size, self.rect_size, px)
|
|
}
|
|
mod.widgets.PopupMenuItem.draw_bg.pixel = fn() {
|
|
let px = au_px(self.draw_pass.dpi_factor)
|
|
return au_row(self.pos * self.rect_size, self.rect_size, px, self.hover, self.active, self.disabled)
|
|
}
|
|
mod.widgets.PopupMenuItem.draw_text.color_active = #ffffff
|
|
mod.widgets.ComboBoxFlat.draw_popup_bg.pixel = fn() {
|
|
let px = au_px(self.draw_pass.dpi_factor)
|
|
return au_menu(self.pos * self.rect_size, self.rect_size, px)
|
|
}
|
|
mod.widgets.ComboBoxFlat.draw_item.pixel = fn() {
|
|
let px = au_px(self.draw_pass.dpi_factor)
|
|
return au_row(self.pos * self.rect_size, self.rect_size, px, self.hover, self.active, 0.0)
|
|
}
|
|
mod.widgets.ComboBoxFlat.draw_item_text.color_active = #ffffff
|
|
|
|
// THE DASH WINDOW: the value box of a dial and a count are small displays,
|
|
// black glass in a polished silver frame, the number white on it, as the
|
|
// readouts and the screen are.
|
|
let au_glass_box = fn(p: vec2, lo: vec2, hi: vec2, px: float, r: float) -> vec4 {
|
|
let c = (lo + hi) * 0.5
|
|
let h = (hi - lo) * 0.5
|
|
let d = Material.sd_box(p, c, h, r)
|
|
let t = clamp((p.y - lo.y) / max(hi.y - lo.y, 1.0), 0.0, 1.0)
|
|
var col = mix(vec3(0.030, 0.031, 0.034), vec3(0.012, 0.012, 0.014), t)
|
|
col = col * (1.0 - 0.6 * Finish.inset(p, c, h - vec2(px * 2.0, px * 2.0), max(r - px * 2.0, 0.5), vec2(0.0, 1.0), 1.0))
|
|
let lit = max(1.0 - smoothstep(0.0, 0.3, t), smoothstep(0.7, 1.0, t))
|
|
col = mix(col, vec3(0.0, 0.0, 0.0), Finish.band(d, px * 2.0, px * 3.0, px))
|
|
col = mix(col, mix(vec3(0.34, 0.345, 0.36), vec3(0.64, 0.645, 0.66), lit), Finish.band(d, px, px * 2.0, px))
|
|
col = mix(col, mix(vec3(0.18, 0.185, 0.20), vec3(0.36, 0.365, 0.38), lit), Finish.band(d, 0.0, px, px))
|
|
// The glass: a faint sheen across its upper half.
|
|
let u = (p - lo) / max(hi - lo, vec2(1.0, 1.0))
|
|
let band = smoothstep(0.0, 0.15, u.x + u.y * 0.6) * (1.0 - smoothstep(0.3, 0.55, u.x + u.y * 0.6))
|
|
col = col + vec3(0.025, 0.025, 0.027) * band * Finish.cover(d + px * 3.0, px)
|
|
let a = Finish.cover(d, px)
|
|
return vec4(col * a, a)
|
|
}
|
|
mod.widgets.RotaryFlat.text_input.draw_bg.pixel = fn() {
|
|
let px = au_px(self.draw_pass.dpi_factor)
|
|
let o = self.rect_pos
|
|
let p = self.pos * self.rect_size
|
|
let lo = vec2(au_snap(0.0, o.x, px), au_snap(0.0, o.y, px))
|
|
let hi = vec2(au_snap(self.rect_size.x - px * 0.49, o.x, px), au_snap(self.rect_size.y - px * 0.49, o.y, px))
|
|
return au_glass_box(p, lo, hi, px, 2.5)
|
|
}
|
|
mod.widgets.RotaryFlat.text_input.draw_text.color = #f2f5f8
|
|
mod.widgets.Rotary.text_input.draw_bg.pixel = mod.widgets.RotaryFlat.text_input.draw_bg.pixel
|
|
mod.widgets.Rotary.text_input.draw_text.color = #f2f5f8
|
|
mod.widgets.RotaryGradientY.text_input.draw_bg.pixel = mod.widgets.RotaryFlat.text_input.draw_bg.pixel
|
|
mod.widgets.RotaryGradientY.text_input.draw_text.color = #f2f5f8
|
|
|
|
// ---------------------------------------------------------------- tabs
|
|
|
|
// The segmented control: the whole strip one raised graphite cap; the
|
|
// chosen answer is pushed in, its legend lit and a little shift light of
|
|
// three LEDs -- green, amber, red -- burning over it.
|
|
mod.widgets.SegmentedControlFlat.draw_bg.vertex = fn() {
|
|
let m = 7.0
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
|
|
}
|
|
mod.widgets.SegmentedControlFlat.draw_bg.pixel = fn() {
|
|
let m = 7.0
|
|
let px = au_px(self.draw_pass.dpi_factor)
|
|
let o = self.rect_pos
|
|
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let lo = vec2(au_snap(0.0, o.x, px), au_snap(0.0, o.y, px))
|
|
let hi = vec2(au_snap(self.rect_size.x, o.x, px), au_snap(self.rect_size.y, o.y, px))
|
|
return au_cap(p, lo, hi, 3.5, px, 0.0, 0.0, 0.0, 0.0, 1.0, vec3(0.0, 0.0, 0.0), vec4(0.20, 0.20, 0.21, 1.0), 1.0)
|
|
}
|
|
// The chosen answer is a push button pressed in: its well runs from under
|
|
// the strip's top outline to its bottom outline, black under the top edge
|
|
// and shaded along both walls, and the neighbours' ends stand lit over it.
|
|
// The strip lays the answers 2 points in from its edge; the quad grows so
|
|
// the well can reach the outline and the neighbours' ends.
|
|
mod.widgets.SegmentedControlFlat.draw_pill.vertex = fn() {
|
|
let m = 13.0
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
|
|
}
|
|
// On the column (its pill's `radius`, unused by this face, set to -1 as the
|
|
// mark) the well keeps to its own segment's height and reaches the strip's
|
|
// side outlines instead.
|
|
mod.widgets.SegmentedControlFlat.draw_pill.pixel = fn() {
|
|
let m = 13.0
|
|
let v = 1.0 - step(0.0, self.radius)
|
|
let hz = 1.0 - v
|
|
let px = au_px(self.draw_pass.dpi_factor)
|
|
let o = self.rect_pos
|
|
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let lo = vec2(au_snap(-2.0 * v, o.x, px) + px * v, au_snap(-2.0 * hz, o.y, px) + px * hz)
|
|
let hi = vec2(au_snap(self.rect_size.x + 2.0 * v, o.x, px) - px * v, au_snap(self.rect_size.y + 2.0 * hz, o.y, px) - px * hz)
|
|
let hh = hi.y - lo.y
|
|
let t = clamp((p.y - lo.y) / max(hh, 1.0), 0.0, 1.0)
|
|
// The pressed-in cap: dark graphite, darker under the top edge where
|
|
// the neighbours stand over it, the walls shaded.
|
|
var col = mix(vec3(0.060, 0.061, 0.066), vec3(0.090, 0.091, 0.098), smoothstep(0.0, 1.0, t))
|
|
let tx = au_plate_tex(p, px) * 0.6
|
|
col = col + vec3(tx, tx, tx)
|
|
let top = 0.75 * exp(-max(p.y - lo.y, 0.0) / (1.3 * px))
|
|
let wl = 0.6 * exp(-max(p.x - lo.x, 0.0) / (1.6 * px))
|
|
let wr = 0.6 * exp(-max(hi.x - px - p.x, 0.0) / (1.6 * px))
|
|
let dk = 1.0 - (1.0 - top) * (1.0 - wl) * (1.0 - wr)
|
|
col = mix(col, vec3(0.0, 0.0, 0.0), dk)
|
|
let inside = step(lo.x, p.x) * (1.0 - step(hi.x, p.x)) * step(lo.y, p.y) * (1.0 - step(hi.y, p.y))
|
|
var res = vec4(col, 1.0) * inside
|
|
// The lit lip along its foot.
|
|
res = Finish.over(res, vec4(0.34, 0.35, 0.37, 1.0) * step(hi.y - px, p.y) * inside * 0.6)
|
|
// The neighbours' ends standing over the well.
|
|
let ey = step(lo.y + px, p.y) * (1.0 - step(hi.y - px, p.y))
|
|
let el = step(lo.x - px, p.x) * (1.0 - step(lo.x, p.x))
|
|
let er = step(hi.x, p.x) * (1.0 - step(hi.x + px, p.x))
|
|
res = Finish.over(res, vec4(0.36, 0.37, 0.39, 1.0) * (el + er) * ey * hz)
|
|
let eb = step(hi.y, p.y) * (1.0 - step(hi.y + px, p.y)) * step(lo.x + px, p.x) * (1.0 - step(hi.x - px, p.x))
|
|
res = Finish.over(res, vec4(0.36, 0.37, 0.39, 1.0) * eb * v)
|
|
// The shift light: three LEDs in a row over the legend.
|
|
let cx = (lo.x + hi.x) * 0.5 - 1.0
|
|
let ly = lo.y + max(4.0, hh * 0.17)
|
|
let lr = vec2(1.6, 1.25)
|
|
res = au_led(res, p, vec2(cx - 7.0, ly), lr, 1.0, 1.0, px, vec3(0.86, 1.0, 0.88), vec3(0.20, 0.95, 0.30))
|
|
res = au_led(res, p, vec2(cx, ly), lr, 1.0, 1.0, px, vec3(1.0, 0.98, 0.84), vec3(1.0, 0.78, 0.12))
|
|
return au_led(res, p, vec2(cx + 7.0, ly), lr, 1.0, 1.0, px, vec3(1.0, 0.88, 0.85), vec3(1.0, 0.18, 0.12))
|
|
}
|
|
// Cap labels at the family's 8.5 points, the chosen one included; the
|
|
// chosen one stands a little lower, under its shift light.
|
|
mod.widgets.SegmentedControlFlat.draw_text.text_style.font_size = 8.5
|
|
mod.widgets.SegmentedControlFlat.draw_text_selected.text_style.font_size = 8.5
|
|
mod.widgets.SegmentedControlFlat.draw_text.color = #c9cbcf
|
|
mod.widgets.SegmentedControlFlat.draw_text_selected.color = #ffffff
|
|
mod.widgets.SegmentedControlVertical.draw_pill.radius = -1.0
|
|
|
|
// Tabs: a row of gold-rimmed caps standing side by side; the tab in use is
|
|
// lit amber from within (`au_gold_tab`).
|
|
mod.widgets.Tabs.height = 28.0
|
|
mod.widgets.Tabs.clip_x = false
|
|
mod.widgets.Tabs.clip_y = false
|
|
let au_tab = fn(pos: vec2, size: vec2, o: vec2, dpi: float, hover: float, active: float) -> vec4 {
|
|
let m = 13.0
|
|
let px = au_px(dpi)
|
|
let p = pos * (size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let lo = vec2(au_snap(1.0, o.x, px), au_snap(0.0, o.y, px))
|
|
let hi = vec2(au_snap(size.x - 1.0, o.x, px), au_snap(size.y, o.y, px))
|
|
return au_gold_tab(p, lo, hi, px, hover, 0.0, active, 0.0, 0.0)
|
|
}
|
|
mod.widgets.Tabs.draw_tab.vertex = fn() {
|
|
let m = 13.0
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
|
|
}
|
|
mod.widgets.Tabs.draw_tab_hover.vertex = fn() {
|
|
let m = 13.0
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
|
|
}
|
|
mod.widgets.Tabs.draw_tab_active.vertex = fn() {
|
|
let m = 13.0
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
|
|
}
|
|
mod.widgets.Tabs.draw_bg.pixel = fn() {
|
|
return vec4(0.0, 0.0, 0.0, 0.0)
|
|
}
|
|
mod.widgets.Tabs.draw_tab.pixel = fn() {
|
|
return au_tab(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, 0.0, 0.0)
|
|
}
|
|
mod.widgets.Tabs.draw_tab_hover.pixel = fn() {
|
|
return au_tab(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, 1.0, 0.0)
|
|
}
|
|
mod.widgets.Tabs.draw_tab_active.pixel = fn() {
|
|
return au_tab(self.pos, self.rect_size, self.rect_pos, self.draw_pass.dpi_factor, 0.0, 1.0)
|
|
}
|
|
mod.widgets.Tabs.draw_sep.pixel = fn() {
|
|
return vec4(0.0, 0.0, 0.0, 0.0)
|
|
}
|
|
// Gold legends; the tab in use burns pale amber, in semibold.
|
|
mod.widgets.Tabs.draw_mark.color = #cfae6c
|
|
mod.widgets.Tabs.draw_text.color = #cfae6c
|
|
mod.widgets.Tabs.draw_text_active.color = #ffe2a8
|
|
mod.widgets.Tabs.draw_text.text_style.font_size = 8.5
|
|
mod.widgets.Tabs.draw_text_active.text_style = mod.theme.font_bold{font_size: 8.5}
|
|
|
|
// ---------------------------------------------------------------- feedback
|
|
|
|
// THE FEEDBACK BAR: a window of black glass in its well, behind it a row of
|
|
// light cells three device pixels wide with a dark device pixel between
|
|
// them, lit from the start up to the value. In the sheet's own accent the
|
|
// cells run through the rainbow along the bar, violet to orange, as a
|
|
// tuning panel's bar does; given a role colour (a success, a warning) every
|
|
// cell takes that colour. An unlit cell is its own colour at a sixth, dark
|
|
// glass with the hue still in it. Still: the unknown value is a lit run in
|
|
// the middle with its ends fading.
|
|
let au_cells = fn(dst: vec4, p: vec2, lo: vec2, hi: vec2, px: float, pitch: float, cellw: float, value: float, col: vec3, rainbow: float, hot: float, unknown: float) -> vec4 {
|
|
let span = max(hi.x - lo.x, 1.0)
|
|
let k = floor((p.x - lo.x) / pitch)
|
|
let cx = lo.x + (k + 0.5) * pitch
|
|
let f = (p.x - lo.x) - k * pitch
|
|
let cell = step(f, cellw) * step(lo.x, p.x) * step(p.x, hi.x - (pitch - cellw)) * step(lo.y, p.y) * step(p.y, hi.y)
|
|
let t = clamp((cx - lo.x) / span, 0.0, 1.0)
|
|
var c = mix(col, au_rainbow(t), rainbow)
|
|
c = mix(c, vec3(1.0, 0.18, 0.12), step(hot, t))
|
|
var on = step(t, value) * step(0.0005, value)
|
|
if unknown > 0.5 {
|
|
on = smoothstep(0.22, 0.34, t) * (1.0 - smoothstep(0.66, 0.78, t))
|
|
}
|
|
// A lit cell burns toward white down its middle and keeps its hue at
|
|
// its ends, and its light leaks into the dark gaps beside it. An unlit
|
|
// cell is dark glass with its hue faintly in it and a glint on its top
|
|
// row.
|
|
let ty = clamp((p.y - lo.y) / max(hi.y - lo.y, px), 0.0, 1.0)
|
|
let mid = exp(-(ty - 0.45) * (ty - 0.45) / 0.08)
|
|
let litc = mix(c, mix(c, vec3(1.0, 1.0, 1.0), 0.6), mid * 0.75)
|
|
var dark = c * 0.13 + vec3(0.012, 0.012, 0.014)
|
|
dark = dark + vec3(0.035, 0.035, 0.038) * (1.0 - step(lo.y + px, p.y))
|
|
let inside = step(lo.x, p.x) * step(p.x, hi.x) * step(lo.y, p.y) * step(p.y, hi.y)
|
|
let leak = vec4(c * 0.25, 0.25) * on * (1.0 - cell) * inside
|
|
return Finish.over(Finish.over(dst, leak), vec4(mix(dark, litc, on), 1.0) * cell)
|
|
}
|
|
|
|
// The progress bar: a 14 point well of black glass holding a row of tall
|
|
// cells, four device pixels wide with two dark ones between.
|
|
mod.widgets.ProgressBarFlat.height = 14.0
|
|
mod.widgets.ProgressBarFlat.draw_bg.thickness = uniform(14.0)
|
|
mod.widgets.ProgressBar.height = 14.0
|
|
mod.widgets.ProgressBar.draw_bg.thickness = uniform(14.0)
|
|
mod.widgets.ProgressBarFlat.draw_bg.pixel = fn() {
|
|
let px = au_px(self.draw_pass.dpi_factor)
|
|
let o = self.rect_pos
|
|
let p = self.pos * self.rect_size
|
|
let th = min(self.thickness, self.rect_size.y)
|
|
let y0 = au_snap((self.rect_size.y - th) * 0.5, o.y, px)
|
|
let x0 = au_snap(self.rect_size.x * self.start, o.x, px)
|
|
let x1 = au_snap(self.rect_size.x * self.end, o.x, px)
|
|
let lo = vec2(x0, y0)
|
|
let hi = vec2(x1, y0 + au_snap(th, 0.0, px) - px)
|
|
var res = vec4(0.0, 0.0, 0.0, 0.0)
|
|
if self.track > 0.5 {
|
|
res = au_well(p, lo, hi, 1.5, px, vec3(0.016, 0.017, 0.019), 0.0, 0.0, 1.0)
|
|
}
|
|
let fill = self.fill_color()
|
|
// The sheet's own accent asks for the rainbow; any other colour is a
|
|
// role and lights the cells in its own hue.
|
|
let dc = fill.rgb - vec3(0.235, 0.769, 1.0)
|
|
let rainbow = 1.0 - step(0.05, dot(dc, dc))
|
|
let v = clamp((self.value - self.start) / max(self.end - self.start, 0.001), 0.0, 1.0)
|
|
let clo = vec2(lo.x + px * 2.0, lo.y + px * 2.0)
|
|
let chi = vec2(hi.x - px * 2.0, hi.y - px * 2.0)
|
|
res = au_cells(res, p, clo, chi, px, px * 6.0, px * 4.0, v, fill.rgb, rainbow, 2.0, step(self.value, -0.001))
|
|
return res * self.opacity * fill.a
|
|
}
|
|
mod.widgets.ProgressBarFlat.draw_bg.color_primary = #3cc4ff
|
|
|
|
// The level meter: across, the feedback bar's rainbow in cells of four
|
|
// device pixels and a gap of two, red over its last stretch; standing up,
|
|
// the classic ladder, green to amber to red, a cell of three device pixels
|
|
// and a gap of one. Unlit cells are dark glass with their hue in it. The
|
|
// clip lamp is an LED. 8 points thick.
|
|
mod.widgets.LevelMeter.height = 22.0
|
|
mod.widgets.LevelMeterColumn.width = 8.0
|
|
mod.widgets.LevelMeter.draw_bg.pixel = fn() {
|
|
let px = au_px(self.draw_pass.dpi_factor)
|
|
let o = self.rect_pos
|
|
let sz = self.rect_size
|
|
let p = self.pos * sz
|
|
let v = self.vertical
|
|
let along = mix(sz.x, sz.y, v)
|
|
let across = mix(sz.y, sz.x, v)
|
|
let a = mix(p.x, sz.y - p.y, v)
|
|
let b = mix(p.y, p.x, v)
|
|
let lamp_room = self.lamp * (across + 3.0)
|
|
let bar = max(4.0, along - lamp_room)
|
|
let wlo = mix(vec2(0.0, 0.0), vec2(0.0, sz.y - bar), v)
|
|
let whi = mix(vec2(bar, sz.y), vec2(sz.x, sz.y), v)
|
|
let lo = vec2(au_snap(wlo.x, o.x, px), au_snap(wlo.y, o.y, px))
|
|
let hi = vec2(au_snap(whi.x, o.x, px), au_snap(whi.y, o.y, px) - px)
|
|
var res = au_well(p, lo, hi, 1.0, px, vec3(0.014, 0.015, 0.017), 0.0, 0.0, 1.0)
|
|
let pad = 2.0 * px
|
|
let span = max(1.0, bar - pad * 2.0)
|
|
let pitch = mix(px * mix(4.0, 7.0, step(11.0, across)), px * 4.0, v)
|
|
let cw = mix(mix(0.67, 0.71, step(11.0, across)), 0.75, v)
|
|
let cell = step(fract((a - pad) / pitch), cw)
|
|
let k = floor((a - pad) / pitch)
|
|
let share = clamp((k + 0.5) * pitch / span, 0.0, 1.0)
|
|
let inb = step(pad, a) * step(a, bar - pad) * step(pad, b) * step(b, across - pad) * step(p.y, hi.y - px)
|
|
// The ladder's colours, and the rainbow's.
|
|
var ladder = vec3(0.30, 0.95, 0.30)
|
|
ladder = mix(ladder, vec3(1.0, 0.78, 0.18), step(self.hot_at, share))
|
|
ladder = mix(ladder, vec3(1.0, 0.20, 0.14), step(self.hot_to, share))
|
|
var bow = au_rainbow(share / max(self.hot_to, 0.01))
|
|
bow = mix(bow, vec3(1.0, 0.20, 0.14), step(self.hot_to, share))
|
|
let hue = mix(bow, ladder, v)
|
|
let on = step(share, self.level) * step(0.001, self.level)
|
|
let bt = clamp((b - pad) / max(across - pad * 2.0, px), 0.0, 1.0)
|
|
let mid = exp(-(bt - 0.45) * (bt - 0.45) / 0.07)
|
|
let litc = mix(hue, mix(hue, vec3(1.0, 1.0, 1.0), 0.5), mid * 0.6)
|
|
let unlit = hue * 0.15 + vec3(0.012, 0.012, 0.014)
|
|
res = Finish.over(res, vec4(mix(unlit, litc, on), 1.0) * cell * inb * self.opacity)
|
|
// The hold: one cell at full strength.
|
|
let hold_at = pad + span * self.hold
|
|
let hc = step(abs(a - hold_at), pitch * 0.5) * step(0.002, self.hold)
|
|
res = Finish.over(res, vec4(hue, 1.0) * hc * cell * inb)
|
|
if self.lamp > 0.5 {
|
|
let lc = mix(vec2(bar + 3.0 + across * 0.5, sz.y * 0.5), vec2(sz.x * 0.5, across * 0.5), v)
|
|
// The clip lamp: a dot on a slim meter, a short red bar standing
|
|
// across a tall one.
|
|
let rl = clamp(across * 0.5 - 1.5, 1.0, 1.75)
|
|
let hs = mix(vec2(rl, rl), mix(vec2(across * 0.38, 1.6), vec2(1.6, across * 0.38), 1.0 - v), step(11.0, across))
|
|
res = au_led(res, p, lc, hs, step(0.5, self.over), 0.45, px, vec3(1.0, 0.957, 0.941), vec3(1.0, 0.20, 0.14))
|
|
}
|
|
return res * self.opacity
|
|
}
|
|
|
|
// The spinner: a small turned-metal disc in a dark bezel whose ring is lit
|
|
// three quarters of the way round, as the pictures draw it. A sheet may not
|
|
// read the pass clock and the spinner carries no other, so it stands still.
|
|
mod.widgets.SpinnerFlat.size = 20.0
|
|
mod.widgets.SpinnerFlat.draw_bg.pixel = fn() {
|
|
let px = au_px(self.draw_pass.dpi_factor)
|
|
let p = self.pos * self.rect_size
|
|
let o = self.rect_pos
|
|
let c = vec2(Finish.snap(o.x + self.rect_size.x * 0.5, px), Finish.snap(o.y + self.rect_size.y * 0.5, px)) - o
|
|
let size = min(self.rect_size.x, self.rect_size.y)
|
|
let ro = size * 0.5 - 0.5
|
|
let rw = 2.2
|
|
let rm = ro - rw * 0.5
|
|
let rl = length(p - c)
|
|
let dr = abs(rl - rm) - rw * 0.5
|
|
var res = vec4(0.020, 0.021, 0.024, 1.0) * Finish.cover(dr, px)
|
|
let q = p - c
|
|
var ang = atan2(-q.x, q.y)
|
|
if ang < 0.0 {
|
|
ang = ang + 2.0 * PI
|
|
}
|
|
let ac = clamp(ang, 0.8, 0.8 + 1.5 * PI)
|
|
let ap = c + vec2(-sin(ac), cos(ac)) * rm
|
|
let da = length(p - ap)
|
|
let xa = max(da - 0.7, 0.0)
|
|
let g = (0.4 * exp(-(xa * xa) / (2.0 * 0.6 * 0.6)) + 0.18 * exp(-(xa * xa) / (2.0 * 1.4 * 1.4))) * (1.0 - Finish.cover(da - 0.7, px))
|
|
res = Finish.over(res, vec4(0.227, 0.612, 1.0, 1.0) * g)
|
|
let acol = mix(vec3(0.86, 0.94, 1.0), vec3(0.32, 0.66, 1.0), smoothstep(0.1, 0.7, da))
|
|
res = Finish.over(res, vec4(acol, 1.0) * Finish.cover(da - 0.7, px))
|
|
res = Finish.over(res, au_metal(p, c, ro - rw - 0.8, px, 0.0, 0.0, 1.0))
|
|
return res * self.alpha
|
|
}
|
|
|
|
// ---------------------------------------------------------------- surfaces
|
|
|
|
// The scroll bar: an 8 point well, the thumb a 6 point graphite cap inset
|
|
// 1 point.
|
|
mod.widgets.ScrollBar.bar_size = 8.0
|
|
mod.widgets.ScrollBar.bar_side_margin = 1.0
|
|
mod.widgets.ScrollBar.draw_bg.pixel = fn() {
|
|
let px = au_px(self.draw_pass.dpi_factor)
|
|
let o = self.rect_pos
|
|
let sz = self.rect_size
|
|
let p = self.pos * sz
|
|
let v = self.is_vertical
|
|
let lo = vec2(au_snap(0.0, o.x, px), au_snap(0.0, o.y, px))
|
|
let hi = vec2(au_snap(sz.x, o.x, px), au_snap(sz.y, o.y, px))
|
|
var res = au_well(p, lo, hi, 2.0, px, vec3(0.030, 0.031, 0.034), 0.0, 0.0, 0.0)
|
|
let t0 = mix(sz.x * self.norm_scroll, sz.y * self.norm_scroll, v)
|
|
let t1 = t0 + mix(sz.x * self.norm_handle, sz.y * self.norm_handle, v)
|
|
let tlo = mix(vec2(t0, 1.0), vec2(1.0, t0), v)
|
|
let thi = mix(vec2(t1, sz.y - 1.0), vec2(sz.x - 1.0, t1), v)
|
|
let slo = vec2(au_snap(tlo.x, o.x, px), au_snap(tlo.y, o.y, px))
|
|
let shi = vec2(au_snap(thi.x, o.x, px), au_snap(thi.y, o.y, px))
|
|
res = Finish.over(res, au_cap(p, slo, shi, 3.0, px, max(self.hover, self.drag), 0.0, 0.0, 0.0, 0.0, vec3(0.0, 0.0, 0.0), vec4(0.20, 0.20, 0.21, 1.0), 1.0))
|
|
return res * self.opacity
|
|
}
|
|
|
|
// A RAISED PANEL is a plate of brushed steel, lighter than the deck, with
|
|
// two corners cut on the diagonal (the top right and the bottom left), as a
|
|
// race dash cuts its panels: a firm shadow under it that stays within the
|
|
// gap to its neighbours, the black outline, the steel (`au_steel`), and a
|
|
// bevel two device pixels deep lit where it faces up (the top edge and the
|
|
// cut at the top right) and dark at the foot.
|
|
let au_panel = fn(p: vec2, size: vec2, o: vec2, px: float, hover: float) -> vec4 {
|
|
let lo = vec2(au_snap(0.0, o.x, px), au_snap(0.0, o.y, px))
|
|
let hi = vec2(au_snap(size.x, o.x, px), au_snap(size.y, o.y, px))
|
|
let c = (lo + hi) * 0.5
|
|
let h = (hi - lo) * 0.5
|
|
let k = Finish.snap(min(10.0, 0.22 * min(h.x, h.y) * 2.0), px)
|
|
let d = Finish.sd_notch(p, c, h, vec4(1.5, k, 1.5, k))
|
|
let sh1 = Finish.drop(p, c, h - vec2(1.0, 0.0), 3.0, vec2(0.0, 1.5), 1.3) * 0.9
|
|
let sh2 = Finish.drop(p, c, h - vec2(5.0, 2.0), 3.0, vec2(0.0, 4.5), 4.0) * 0.65
|
|
let sh = (1.0 - (1.0 - sh1) * (1.0 - sh2)) * (1.0 - Finish.cover(d, px))
|
|
var res = vec4(0.0, 0.0, 0.0, sh)
|
|
let t = clamp((p.y - lo.y) / max(hi.y - lo.y, 1.0), 0.0, 1.0)
|
|
let top = p.y - lo.y
|
|
let u = (p.x - lo.x) / max(hi.x - lo.x, 1.0)
|
|
var col = au_steel(p, px, t, u) + vec3(0.012, 0.012, 0.013) * hover
|
|
// The bevel, lit by the way the edge faces: up (the top and the cut at
|
|
// the top right) bright, the sides a quiet line, the foot dark.
|
|
let e = 0.5 * px
|
|
let gx = Finish.sd_notch(p + vec2(e, 0.0), c, h, vec4(1.5, k, 1.5, k)) - Finish.sd_notch(p - vec2(e, 0.0), c, h, vec4(1.5, k, 1.5, k))
|
|
let gy = Finish.sd_notch(p + vec2(0.0, e), c, h, vec4(1.5, k, 1.5, k)) - Finish.sd_notch(p - vec2(0.0, e), c, h, vec4(1.5, k, 1.5, k))
|
|
let g = vec2(gx, gy) / max(length(vec2(gx, gy)), 0.0001)
|
|
let facing = dot(g, vec2(-0.35, -0.94))
|
|
let r1 = Finish.band(d, px, px * 2.0, px)
|
|
let r2 = Finish.band(d, px * 2.0, px * 3.0, px)
|
|
let lit = smoothstep(0.2, 0.9, facing)
|
|
col = mix(col, mix(vec3(0.30, 0.305, 0.32), vec3(0.74, 0.75, 0.77), lit), r1 * (0.55 + 0.45 * lit))
|
|
col = mix(col, mix(vec3(0.16, 0.162, 0.17), vec3(0.40, 0.405, 0.42), lit), r2 * 0.7)
|
|
col = mix(col, vec3(0.004, 0.004, 0.006), Finish.band(d, 0.0, px, px))
|
|
let a = Finish.cover(d, px)
|
|
return Finish.over(res, vec4(col * a, a))
|
|
}
|
|
|
|
// AN INSET is a hexagon grille sunk in a polished chrome frame: the frame
|
|
// three and a half points wide, mirroring the room across its width and lit
|
|
// along its top and foot, a black line where the grille drops away, and the
|
|
// perforated mesh in it.
|
|
let au_tray = fn(p: vec2, size: vec2, o: vec2, px: float, r: float) -> vec4 {
|
|
let lo = vec2(au_snap(0.0, o.x, px), au_snap(0.0, o.y, px))
|
|
let hi = vec2(au_snap(size.x, o.x, px), au_snap(size.y, o.y, px) - px)
|
|
let c = (lo + hi) * 0.5
|
|
let h = (hi - lo) * 0.5
|
|
let d = Material.sd_box(p, c, h, r)
|
|
let t = clamp((p.y - lo.y) / max(hi.y - lo.y, 1.0), 0.0, 1.0)
|
|
// The frame: chrome, its profile across the band like a rounded rail.
|
|
let fw = au_snap(3.5, 0.0, px)
|
|
let fu = clamp(-d / max(fw, px), 0.0, 1.0)
|
|
let ny = clamp(fu * 2.0 - 1.0, -1.0, 1.0)
|
|
let lit = max(1.0 - smoothstep(0.0, 0.2, t), smoothstep(0.85, 1.0, t))
|
|
var fc = mix(vec3(0.30, 0.305, 0.32), vec3(0.86, 0.865, 0.875), exp(-(ny + 0.30) * (ny + 0.30) / 0.06))
|
|
fc = mix(fc, fc * 0.45, smoothstep(0.2, 1.0, ny))
|
|
fc = mix(fc * 0.72, min(fc * 1.08, vec3(1.0, 1.0, 1.0)), lit)
|
|
// The grille: a hexagon mesh, holes six points apart, its web dark
|
|
// steel with a fine light on the rim over each hole, a broad soft sheen
|
|
// across the middle, under the frame's shadow along its top. The web
|
|
// is held dark enough that light words keep 5:1 on every part of it.
|
|
let flo = lo + vec2(fw, fw)
|
|
let fhi = hi - vec2(fw, fw)
|
|
let fd = Material.sd_box(p, (flo + fhi) * 0.5, (fhi - flo) * 0.5, max(r - 2.0, 1.0))
|
|
let fu2 = clamp((p.x - flo.x) / max(fhi.x - flo.x, 1.0), 0.0, 1.0)
|
|
let webc = vec3(0.262, 0.266, 0.278) + vec3(0.014, 0.014, 0.015) * exp(-(fu2 - 0.55) * (fu2 - 0.55) / 0.05)
|
|
var pl = au_mesh(p - flo - vec2(3.0, 3.0), 6.0, 1.5, px, webc, 0.16)
|
|
pl = min(pl, vec3(0.33, 0.335, 0.35))
|
|
pl = pl * (1.0 - 0.7 * Finish.inset(p, (flo + fhi) * 0.5, (fhi - flo) * 0.5, max(r - 2.0, 1.0), vec2(0.0, 1.5), 1.8))
|
|
pl = mix(pl, vec3(0.010, 0.010, 0.012), Finish.band(fd, 0.0, px, px))
|
|
var col = mix(fc, pl, Finish.cover(fd, px))
|
|
col = mix(col, vec3(0.008, 0.008, 0.010), Finish.band(d, 0.0, px, px))
|
|
let a = Finish.cover(d, px)
|
|
var res = vec4(col * a, a)
|
|
// A breath of the frame's light on the ground under its foot.
|
|
let below = step(hi.y + px, p.y) * (1.0 - a) * smoothstep(0.0, 6.0, min(p.x - lo.x, hi.x - p.x))
|
|
return Finish.over(vec4(0.50, 0.51, 0.53, 1.0) * exp(-max(p.y - hi.y - px, 0.0) / 1.4) * 0.14 * below, res)
|
|
}
|
|
|
|
mod.widgets.PanelView.clip_x = false
|
|
mod.widgets.PanelView.clip_y = false
|
|
mod.widgets.PanelView.padding = mod.turtle.Inset{left: 12.0 right: 12.0 top: 12.0 bottom: 12.0}
|
|
mod.widgets.PanelView.draw_bg.vertex = fn() {
|
|
let m = 12.0
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
|
|
}
|
|
mod.widgets.PanelView.draw_bg.pixel = fn() {
|
|
let m = 12.0
|
|
let px = au_px(self.draw_pass.dpi_factor)
|
|
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
if self.sunken > 0.5 {
|
|
return au_tray(p, self.rect_size, self.rect_pos, px, 4.0)
|
|
}
|
|
return au_panel(p, self.rect_size, self.rect_pos, px, 0.0)
|
|
}
|
|
|
|
// The rounded view keeps the colour its caller gives it, square to the
|
|
// family: a true 2 point corner and, when it is opaque, a 1 dp outline.
|
|
mod.widgets.RoundedView.draw_bg.pixel = fn() {
|
|
let px = au_px(self.draw_pass.dpi_factor)
|
|
let p = self.pos * self.rect_size
|
|
let lo = vec2(self.border_inset.x, self.border_inset.y)
|
|
let hi = self.rect_size - vec2(self.border_inset.z, self.border_inset.w)
|
|
let c = (lo + hi) * 0.5
|
|
let h = max((hi - lo) * 0.5, vec2(0.5, 0.5))
|
|
let r = min(2.0 * max(1.0, self.border_radius), min(h.x, h.y))
|
|
let d = Material.sd_box(p, c, h, r)
|
|
var fill = self.color
|
|
if self.color_2.x > -0.5 {
|
|
fill = mix(self.color, self.color_2, mix(self.pos.y, self.pos.x, self.gradient_fill_horizontal))
|
|
}
|
|
var ink = self.border_color
|
|
if self.border_size < 0.01 {
|
|
ink = vec4(0.039, 0.039, 0.047, fill.a)
|
|
}
|
|
let a = Finish.cover(d, px)
|
|
var col = mix(fill.rgb, ink.rgb, Finish.band(d, 0.0, max(self.border_size, px), px) * ink.a)
|
|
return vec4(col * a * fill.a, a * fill.a) + vec4(ink.rgb * ink.a, ink.a) * Finish.band(d, 0.0, max(self.border_size, px), px) * a * (1.0 - fill.a)
|
|
}
|
|
|
|
// A countersunk screw at `c`: a chrome head `r` across in a dark ring, a
|
|
// slot cut across it at the angle `a`. Premultiplied over `dst`.
|
|
let au_screw = fn(dst: vec4, p: vec2, c: vec2, r: float, a: float, px: float) -> vec4 {
|
|
let q = p - c
|
|
let rl = length(q)
|
|
var res = Finish.over(dst, vec4(0.0, 0.0, 0.0, 0.55) * Finish.cover(rl - r - px, px))
|
|
let nq = q / max(r, 0.5)
|
|
let nz = sqrt(max(1.0 - dot(nq, nq), 0.0))
|
|
let n = normalize(vec3(nq * 0.6, nz + 0.001))
|
|
let rf = vec3(2.0 * n.z * n.x, 2.0 * n.z * n.y, 2.0 * n.z * n.z - 1.0)
|
|
var hc = au_chrome_room(rf, 0.0) * 0.85
|
|
let sd = vec2(cos(a), sin(a))
|
|
let slot = abs(q.x * sd.y - q.y * sd.x)
|
|
hc = mix(hc, vec3(0.04, 0.04, 0.045), Finish.cover(slot - max(r * 0.16, px * 0.6), px) * step(rl, r - px))
|
|
hc = mix(hc, vec3(0.02, 0.02, 0.025), Finish.band(rl - r, 0.0, px, px))
|
|
let ha = Finish.cover(rl - r, px)
|
|
return Finish.over(res, vec4(hc * ha, ha))
|
|
}
|
|
|
|
// THE CARD is a plate of the same brushed steel with rounded corners, held
|
|
// by a countersunk screw in each corner: the shadow under it, the black
|
|
// outline, a bevel lit along its top and dark at its foot. A pressable card
|
|
// lifts under the hand.
|
|
let au_card = fn(p: vec2, size: vec2, o: vec2, px: float, hover: float) -> vec4 {
|
|
let lo = vec2(au_snap(0.0, o.x, px), au_snap(0.0, o.y, px))
|
|
let hi = vec2(au_snap(size.x, o.x, px), au_snap(size.y, o.y, px))
|
|
let c = (lo + hi) * 0.5
|
|
let h = (hi - lo) * 0.5
|
|
let r = 5.0
|
|
let d = Material.sd_box(p, c, h, r)
|
|
let sh1 = Finish.drop(p, c, h - vec2(1.0, 0.0), r, vec2(0.0, 1.5), 1.3) * 0.9
|
|
let sh2 = Finish.drop(p, c, h - vec2(5.0, 2.0), r, vec2(0.0, 4.5), 4.0) * 0.6
|
|
let sh = (1.0 - (1.0 - sh1) * (1.0 - sh2)) * (1.0 - Finish.cover(d, px))
|
|
var res = vec4(0.0, 0.0, 0.0, sh)
|
|
let t = clamp((p.y - lo.y) / max(hi.y - lo.y, 1.0), 0.0, 1.0)
|
|
let u = clamp((p.x - lo.x) / max(hi.x - lo.x, 1.0), 0.0, 1.0)
|
|
var col = au_steel(p, px, t, u) + vec3(0.012, 0.012, 0.013) * hover
|
|
let r1 = Finish.band(d, px, px * 2.0, px)
|
|
let r2 = Finish.band(d, px * 2.0, px * 3.0, px)
|
|
let up = 1.0 - smoothstep(0.0, r + px * 3.0, p.y - lo.y)
|
|
let foot = step(hi.y - r - px * 2.0, p.y)
|
|
col = mix(col, vec3(0.74, 0.75, 0.77), r1 * up)
|
|
col = mix(col, vec3(0.44, 0.45, 0.47), r2 * up)
|
|
col = mix(col, vec3(0.40, 0.41, 0.43), r1 * (1.0 - up) * (1.0 - foot) * 0.6)
|
|
col = mix(col, vec3(0.16, 0.165, 0.175), (r1 + r2) * foot)
|
|
col = mix(col, vec3(0.004, 0.004, 0.006), Finish.band(d, 0.0, px, px))
|
|
let a = Finish.cover(d, px)
|
|
res = Finish.over(res, vec4(col * a, a))
|
|
let si = 5.5
|
|
res = au_screw(res, p, vec2(lo.x + si, lo.y + si), 2.2, 0.6, px)
|
|
res = au_screw(res, p, vec2(hi.x - si, lo.y + si), 2.2, 2.1, px)
|
|
res = au_screw(res, p, vec2(lo.x + si, hi.y - si), 2.2, 1.3, px)
|
|
return au_screw(res, p, vec2(hi.x - si, hi.y - si), 2.2, 2.7, px)
|
|
}
|
|
mod.widgets.Card.clip_x = false
|
|
mod.widgets.Card.clip_y = false
|
|
mod.widgets.Card.padding = mod.turtle.Inset{left: 16.0 right: 16.0 top: 14.0 bottom: 14.0}
|
|
mod.widgets.Card.draw_bg.vertex = fn() {
|
|
let m = 12.0
|
|
return self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
|
|
}
|
|
mod.widgets.Card.draw_bg.pixel = fn() {
|
|
let m = 12.0
|
|
let px = au_px(self.draw_pass.dpi_factor)
|
|
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
return au_card(p, self.rect_size, self.rect_pos, px, self.hover)
|
|
}
|
|
|
|
// List rows: 20 points each, every row its own slab of graphite with a
|
|
// point and a half of the ground between them, the text 10 points in.
|
|
// Under the hand the slab lifts; the chosen row is cast blue and carries a
|
|
// lit blue bar at its left end.
|
|
mod.widgets.ListItem.height = 20.0
|
|
mod.widgets.ListItemOne.height = 20.0
|
|
mod.widgets.ListItem.padding = mod.turtle.Inset{left: 10.0 right: 8.0 top: 0.0 bottom: 0.0}
|
|
mod.widgets.ListItemOne.padding = mod.turtle.Inset{left: 10.0 right: 8.0 top: 0.0 bottom: 0.0}
|
|
mod.widgets.ListItem.draw_bg.pixel = fn() {
|
|
let px = au_px(self.draw_pass.dpi_factor)
|
|
let o = self.rect_pos
|
|
let p = self.pos * self.rect_size
|
|
let live = 1.0 - self.disabled
|
|
let y0 = au_snap(0.0, o.y, px)
|
|
let y1 = au_snap(self.rect_size.y - 1.5, o.y, px)
|
|
let x0 = au_snap(0.0, o.x, px)
|
|
let x1 = au_snap(self.rect_size.x, o.x, px)
|
|
let t = clamp((p.y - y0) / max(y1 - y0, 1.0), 0.0, 1.0)
|
|
let cb = vec2((x0 + x1) * 0.5, (y0 + y1) * 0.5)
|
|
let d = Material.sd_box(p, cb, vec2((x1 - x0) * 0.5, (y1 - y0) * 0.5), 2.5)
|
|
let sel = self.selected
|
|
var col = mix(vec3(0.165, 0.167, 0.176), vec3(0.115, 0.117, 0.124), t)
|
|
col = col + vec3(0.030, 0.031, 0.033) * self.hover * live - vec3(0.02, 0.02, 0.02) * self.down
|
|
col = mix(col, mix(vec3(0.150, 0.215, 0.300), vec3(0.100, 0.150, 0.215), t), sel)
|
|
let tx = au_plate_tex(p, px) * 0.7
|
|
col = col + vec3(tx, tx, tx)
|
|
col = mix(col, mix(vec3(0.40, 0.405, 0.42), vec3(0.45, 0.60, 0.78), sel), Finish.band(d, px, px * 2.0, px) * (1.0 - step(cb.y, p.y)) * 0.8)
|
|
col = mix(col, vec3(0.012, 0.012, 0.014), Finish.band(d, 0.0, px, px))
|
|
var res = vec4(col, 1.0) * Finish.cover(d, px)
|
|
let lc = vec2(au_snap(3.5, o.x, px) - px * 0.5, cb.y)
|
|
let bar = au_led(res, p, lc, vec2(px * 0.5 + 0.01, max((y1 - y0) * 0.24, 2.0)), live, 0.8, px, vec3(0.88, 0.96, 1.0), vec3(0.227, 0.612, 1.0))
|
|
return mix(res, bar, sel)
|
|
}
|
|
mod.widgets.ListItem.draw_text.color = #dcdde0
|
|
mod.widgets.ListItem.draw_text_2.color = #8f9399
|
|
|
|
// THE START BUTTON: a chip is an illuminated push button of the kind a dash
|
|
// starts its engine with. A polished chrome bezel, a face of dark blue
|
|
// glass lit from behind (a pale bloom low in its middle and a blue light
|
|
// round its rim), and a halo of the same blue on the ground round it, gone
|
|
// within half the gap. A chip given a role colour burns in that colour.
|
|
// Under the hand it brightens; pressed, it sinks and dims.
|
|
mod.widgets.ChipFlat.draw_text.text_style.font_size = 8.5
|
|
mod.widgets.ChipFlat.draw_bg.pixel = fn() {
|
|
let px = au_px(self.draw_pass.dpi_factor)
|
|
let o = self.rect_pos
|
|
let p = self.pos * self.rect_size
|
|
let lo = vec2(au_snap(0.0, o.x, px), au_snap(0.0, o.y, px))
|
|
let hi = vec2(au_snap(self.rect_size.x, o.x, px), au_snap(self.rect_size.y, o.y, px) - px)
|
|
let c = (lo + hi) * 0.5
|
|
let h = (hi - lo) * 0.5
|
|
let r = min(4.0, min(h.x, h.y))
|
|
let d = Material.sd_box(p, c, h, r)
|
|
let t = clamp((p.y - lo.y) / max(hi.y - lo.y, 1.0), 0.0, 1.0)
|
|
// The light's colour: the sheet's blue, or the role's own hue.
|
|
let l = Finish.lum(self.color.rgb)
|
|
let ch = length(self.color.rgb - vec3(l, l, l))
|
|
let role = smoothstep(0.08, 0.16, ch) * self.color.a
|
|
let lc = mix(vec3(0.20, 0.58, 1.0), self.color.rgb / max(max(self.color.x, max(self.color.y, self.color.z)), 0.01), role)
|
|
let on = (0.85 + 0.25 * self.hover) * (1.0 - 0.45 * self.down)
|
|
// The bezel: two device pixels of chrome, bright on the top and foot.
|
|
let lit = max(1.0 - smoothstep(0.0, 0.3, t), smoothstep(0.7, 1.0, t))
|
|
var col = mix(vec3(0.45, 0.455, 0.47), vec3(0.90, 0.905, 0.915), lit)
|
|
let gd = d + px * 2.0
|
|
// The glass.
|
|
var gl = mix(lc * 0.10, lc * 0.05, t) + vec3(0.015, 0.016, 0.02)
|
|
let bq = (p - vec2(c.x, hi.y - h.y * 0.35)) / vec2(max(h.x * 0.8, 1.0), max(h.y * 0.8, 1.0))
|
|
gl = gl + mix(lc, vec3(1.0, 1.0, 1.0), 0.3) * exp(-dot(bq, bq)) * 0.22 * on
|
|
let depth = max(-gd, 0.0)
|
|
gl = gl + lc * exp(-depth / 1.4) * 0.55 * on
|
|
gl = mix(gl, mix(lc, vec3(1.0, 1.0, 1.0), 0.5), Finish.band(gd, 0.0, px, px) * 0.8 * on)
|
|
col = mix(col, gl, Finish.cover(gd, px))
|
|
col = mix(col, vec3(0.012, 0.012, 0.016), Finish.band(d, 0.0, px, px))
|
|
let a = Finish.cover(d, px)
|
|
var res = vec4(col * a, a)
|
|
let x = max(d, 0.0)
|
|
let halo = (0.40 * exp(-(x * x) / (2.0 * 0.9 * 0.9)) + 0.16 * exp(-(x * x) / (2.0 * 2.0 * 2.0))) * (1.0 - smoothstep(2.5, 4.0, x)) * (1.0 - a)
|
|
res = Finish.over(vec4(lc, 1.0) * clamp(halo * on, 0.0, 1.0), res)
|
|
if self.keyed > 0.0 {
|
|
res = Finish.over(res, vec4(0.227, 0.612, 1.0, 1.0) * Finish.ring_out(d, 0.0, px, px) * self.keyed)
|
|
}
|
|
return res
|
|
}
|
|
mod.widgets.ChipFlat.draw_text.color = #dff0ff
|
|
|
|
// ---------------------------------------------------------------- ink
|
|
|
|
// Words sit embossed-dark: a 1 dp shadow of black at half strength directly
|
|
// under every glyph of a label. Read from the glyph one device pixel above,
|
|
// so it costs one more sample and nothing else.
|
|
// On a cap the label also sinks with it: held, the glyphs are read one
|
|
// device pixel higher in the atlas, so they stand one pixel lower in the
|
|
// well. Small labels come from the grayscale atlas, large ones from the
|
|
// multi-channel one; both paths carry the shadow and the nudge.
|
|
mod.widgets.ButtonFlat.draw_text.pixel = fn() {
|
|
let nudge = dFdy(self.t.xy) * step(0.5, self.down)
|
|
let c = self.get_color()
|
|
let dxt = length(dFdx(self.t))
|
|
let dyt = length(dFdy(self.t))
|
|
if self.texture_index < 0.5 {
|
|
let tex_size = self.grayscale_texture.size()
|
|
let scale = (dxt + dyt) * tex_size.x * 0.5
|
|
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
|
|
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
|
|
let main = self.sdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
|
|
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
|
|
let s = self.sdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
|
|
return main + vec4(0.0, 0.0, 0.0, s * 0.5 * c.a) * (1.0 - main.a)
|
|
}
|
|
if self.texture_index > 1.5 && self.texture_index < 2.5 {
|
|
let tex_size = self.msdf_texture.size()
|
|
let scale = (dxt + dyt) * tex_size.x * 0.5
|
|
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
|
|
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
|
|
let main = self.msdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
|
|
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
|
|
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
|
|
return main + vec4(0.0, 0.0, 0.0, s * 0.5 * c.a) * (1.0 - main.a)
|
|
}
|
|
return self.sample_text_pixel()
|
|
}
|
|
// A template made from another before this sheet ran keeps its own copy
|
|
// of the label's draw, so each cap template takes the literal.
|
|
mod.widgets.Button.draw_text.pixel = fn() {
|
|
let nudge = dFdy(self.t.xy) * step(0.5, self.down)
|
|
let c = self.get_color()
|
|
let dxt = length(dFdx(self.t))
|
|
let dyt = length(dFdy(self.t))
|
|
if self.texture_index < 0.5 {
|
|
let tex_size = self.grayscale_texture.size()
|
|
let scale = (dxt + dyt) * tex_size.x * 0.5
|
|
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
|
|
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
|
|
let main = self.sdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
|
|
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
|
|
let s = self.sdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
|
|
return main + vec4(0.0, 0.0, 0.0, s * 0.5 * c.a) * (1.0 - main.a)
|
|
}
|
|
if self.texture_index > 1.5 && self.texture_index < 2.5 {
|
|
let tex_size = self.msdf_texture.size()
|
|
let scale = (dxt + dyt) * tex_size.x * 0.5
|
|
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
|
|
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
|
|
let main = self.msdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
|
|
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
|
|
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
|
|
return main + vec4(0.0, 0.0, 0.0, s * 0.5 * c.a) * (1.0 - main.a)
|
|
}
|
|
return self.sample_text_pixel()
|
|
}
|
|
mod.widgets.ButtonPrimary.draw_text.pixel = fn() {
|
|
let nudge = dFdy(self.t.xy) * step(0.5, self.down)
|
|
let c = self.get_color()
|
|
let dxt = length(dFdx(self.t))
|
|
let dyt = length(dFdy(self.t))
|
|
if self.texture_index < 0.5 {
|
|
let tex_size = self.grayscale_texture.size()
|
|
let scale = (dxt + dyt) * tex_size.x * 0.5
|
|
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
|
|
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
|
|
let main = self.sdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
|
|
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
|
|
let s = self.sdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
|
|
return main + vec4(0.0, 0.0, 0.0, s * 0.5 * c.a) * (1.0 - main.a)
|
|
}
|
|
if self.texture_index > 1.5 && self.texture_index < 2.5 {
|
|
let tex_size = self.msdf_texture.size()
|
|
let scale = (dxt + dyt) * tex_size.x * 0.5
|
|
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
|
|
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
|
|
let main = self.msdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
|
|
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
|
|
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
|
|
return main + vec4(0.0, 0.0, 0.0, s * 0.5 * c.a) * (1.0 - main.a)
|
|
}
|
|
return self.sample_text_pixel()
|
|
}
|
|
mod.widgets.ButtonSecondary.draw_text.pixel = fn() {
|
|
let nudge = dFdy(self.t.xy) * step(0.5, self.down)
|
|
let c = self.get_color()
|
|
let dxt = length(dFdx(self.t))
|
|
let dyt = length(dFdy(self.t))
|
|
if self.texture_index < 0.5 {
|
|
let tex_size = self.grayscale_texture.size()
|
|
let scale = (dxt + dyt) * tex_size.x * 0.5
|
|
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
|
|
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
|
|
let main = self.sdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
|
|
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
|
|
let s = self.sdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
|
|
return main + vec4(0.0, 0.0, 0.0, s * 0.5 * c.a) * (1.0 - main.a)
|
|
}
|
|
if self.texture_index > 1.5 && self.texture_index < 2.5 {
|
|
let tex_size = self.msdf_texture.size()
|
|
let scale = (dxt + dyt) * tex_size.x * 0.5
|
|
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
|
|
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
|
|
let main = self.msdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
|
|
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
|
|
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
|
|
return main + vec4(0.0, 0.0, 0.0, s * 0.5 * c.a) * (1.0 - main.a)
|
|
}
|
|
return self.sample_text_pixel()
|
|
}
|
|
mod.widgets.ButtonTertiary.draw_text.pixel = fn() {
|
|
let nudge = dFdy(self.t.xy) * step(0.5, self.down)
|
|
let c = self.get_color()
|
|
let dxt = length(dFdx(self.t))
|
|
let dyt = length(dFdy(self.t))
|
|
if self.texture_index < 0.5 {
|
|
let tex_size = self.grayscale_texture.size()
|
|
let scale = (dxt + dyt) * tex_size.x * 0.5
|
|
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
|
|
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
|
|
let main = self.sdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
|
|
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
|
|
let s = self.sdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
|
|
return main + vec4(0.0, 0.0, 0.0, s * 0.5 * c.a) * (1.0 - main.a)
|
|
}
|
|
if self.texture_index > 1.5 && self.texture_index < 2.5 {
|
|
let tex_size = self.msdf_texture.size()
|
|
let scale = (dxt + dyt) * tex_size.x * 0.5
|
|
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
|
|
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
|
|
let main = self.msdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
|
|
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
|
|
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
|
|
return main + vec4(0.0, 0.0, 0.0, s * 0.5 * c.a) * (1.0 - main.a)
|
|
}
|
|
return self.sample_text_pixel()
|
|
}
|
|
mod.widgets.ButtonDanger.draw_text.pixel = fn() {
|
|
let nudge = dFdy(self.t.xy) * step(0.5, self.down)
|
|
let c = self.get_color()
|
|
let dxt = length(dFdx(self.t))
|
|
let dyt = length(dFdy(self.t))
|
|
if self.texture_index < 0.5 {
|
|
let tex_size = self.grayscale_texture.size()
|
|
let scale = (dxt + dyt) * tex_size.x * 0.5
|
|
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
|
|
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
|
|
let main = self.sdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
|
|
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
|
|
let s = self.sdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
|
|
return main + vec4(0.0, 0.0, 0.0, s * 0.5 * c.a) * (1.0 - main.a)
|
|
}
|
|
if self.texture_index > 1.5 && self.texture_index < 2.5 {
|
|
let tex_size = self.msdf_texture.size()
|
|
let scale = (dxt + dyt) * tex_size.x * 0.5
|
|
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
|
|
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
|
|
let main = self.msdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
|
|
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
|
|
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
|
|
return main + vec4(0.0, 0.0, 0.0, s * 0.5 * c.a) * (1.0 - main.a)
|
|
}
|
|
return self.sample_text_pixel()
|
|
}
|
|
mod.widgets.ButtonOutline.draw_text.pixel = fn() {
|
|
let nudge = dFdy(self.t.xy) * step(0.5, self.down)
|
|
let c = self.get_color()
|
|
let dxt = length(dFdx(self.t))
|
|
let dyt = length(dFdy(self.t))
|
|
if self.texture_index < 0.5 {
|
|
let tex_size = self.grayscale_texture.size()
|
|
let scale = (dxt + dyt) * tex_size.x * 0.5
|
|
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
|
|
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
|
|
let main = self.sdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
|
|
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
|
|
let s = self.sdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
|
|
return main + vec4(0.0, 0.0, 0.0, s * 0.5 * c.a) * (1.0 - main.a)
|
|
}
|
|
if self.texture_index > 1.5 && self.texture_index < 2.5 {
|
|
let tex_size = self.msdf_texture.size()
|
|
let scale = (dxt + dyt) * tex_size.x * 0.5
|
|
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
|
|
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
|
|
let main = self.msdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
|
|
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
|
|
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
|
|
return main + vec4(0.0, 0.0, 0.0, s * 0.5 * c.a) * (1.0 - main.a)
|
|
}
|
|
return self.sample_text_pixel()
|
|
}
|
|
mod.widgets.ButtonFlatter.draw_text.pixel = fn() {
|
|
let nudge = dFdy(self.t.xy) * step(0.5, self.down)
|
|
let c = self.get_color()
|
|
let dxt = length(dFdx(self.t))
|
|
let dyt = length(dFdy(self.t))
|
|
if self.texture_index < 0.5 {
|
|
let tex_size = self.grayscale_texture.size()
|
|
let scale = (dxt + dyt) * tex_size.x * 0.5
|
|
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
|
|
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
|
|
let main = self.sdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
|
|
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
|
|
let s = self.sdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
|
|
return main + vec4(0.0, 0.0, 0.0, s * 0.5 * c.a) * (1.0 - main.a)
|
|
}
|
|
if self.texture_index > 1.5 && self.texture_index < 2.5 {
|
|
let tex_size = self.msdf_texture.size()
|
|
let scale = (dxt + dyt) * tex_size.x * 0.5
|
|
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
|
|
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
|
|
let main = self.msdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
|
|
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
|
|
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
|
|
return main + vec4(0.0, 0.0, 0.0, s * 0.5 * c.a) * (1.0 - main.a)
|
|
}
|
|
return self.sample_text_pixel()
|
|
}
|
|
mod.widgets.ButtonXs.draw_text.pixel = fn() {
|
|
let nudge = dFdy(self.t.xy) * step(0.5, self.down)
|
|
let c = self.get_color()
|
|
let dxt = length(dFdx(self.t))
|
|
let dyt = length(dFdy(self.t))
|
|
if self.texture_index < 0.5 {
|
|
let tex_size = self.grayscale_texture.size()
|
|
let scale = (dxt + dyt) * tex_size.x * 0.5
|
|
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
|
|
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
|
|
let main = self.sdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
|
|
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
|
|
let s = self.sdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
|
|
return main + vec4(0.0, 0.0, 0.0, s * 0.5 * c.a) * (1.0 - main.a)
|
|
}
|
|
if self.texture_index > 1.5 && self.texture_index < 2.5 {
|
|
let tex_size = self.msdf_texture.size()
|
|
let scale = (dxt + dyt) * tex_size.x * 0.5
|
|
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
|
|
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
|
|
let main = self.msdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
|
|
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
|
|
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
|
|
return main + vec4(0.0, 0.0, 0.0, s * 0.5 * c.a) * (1.0 - main.a)
|
|
}
|
|
return self.sample_text_pixel()
|
|
}
|
|
mod.widgets.ButtonSm.draw_text.pixel = fn() {
|
|
let nudge = dFdy(self.t.xy) * step(0.5, self.down)
|
|
let c = self.get_color()
|
|
let dxt = length(dFdx(self.t))
|
|
let dyt = length(dFdy(self.t))
|
|
if self.texture_index < 0.5 {
|
|
let tex_size = self.grayscale_texture.size()
|
|
let scale = (dxt + dyt) * tex_size.x * 0.5
|
|
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
|
|
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
|
|
let main = self.sdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
|
|
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
|
|
let s = self.sdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
|
|
return main + vec4(0.0, 0.0, 0.0, s * 0.5 * c.a) * (1.0 - main.a)
|
|
}
|
|
if self.texture_index > 1.5 && self.texture_index < 2.5 {
|
|
let tex_size = self.msdf_texture.size()
|
|
let scale = (dxt + dyt) * tex_size.x * 0.5
|
|
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
|
|
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
|
|
let main = self.msdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
|
|
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
|
|
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
|
|
return main + vec4(0.0, 0.0, 0.0, s * 0.5 * c.a) * (1.0 - main.a)
|
|
}
|
|
return self.sample_text_pixel()
|
|
}
|
|
mod.widgets.ButtonLg.draw_text.pixel = fn() {
|
|
let nudge = dFdy(self.t.xy) * step(0.5, self.down)
|
|
let c = self.get_color()
|
|
let dxt = length(dFdx(self.t))
|
|
let dyt = length(dFdy(self.t))
|
|
if self.texture_index < 0.5 {
|
|
let tex_size = self.grayscale_texture.size()
|
|
let scale = (dxt + dyt) * tex_size.x * 0.5
|
|
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
|
|
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
|
|
let main = self.sdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
|
|
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
|
|
let s = self.sdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
|
|
return main + vec4(0.0, 0.0, 0.0, s * 0.5 * c.a) * (1.0 - main.a)
|
|
}
|
|
if self.texture_index > 1.5 && self.texture_index < 2.5 {
|
|
let tex_size = self.msdf_texture.size()
|
|
let scale = (dxt + dyt) * tex_size.x * 0.5
|
|
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
|
|
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
|
|
let main = self.msdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
|
|
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
|
|
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
|
|
return main + vec4(0.0, 0.0, 0.0, s * 0.5 * c.a) * (1.0 - main.a)
|
|
}
|
|
return self.sample_text_pixel()
|
|
}
|
|
mod.widgets.ButtonXl.draw_text.pixel = fn() {
|
|
let nudge = dFdy(self.t.xy) * step(0.5, self.down)
|
|
let c = self.get_color()
|
|
let dxt = length(dFdx(self.t))
|
|
let dyt = length(dFdy(self.t))
|
|
if self.texture_index < 0.5 {
|
|
let tex_size = self.grayscale_texture.size()
|
|
let scale = (dxt + dyt) * tex_size.x * 0.5
|
|
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
|
|
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
|
|
let main = self.sdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
|
|
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
|
|
let s = self.sdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
|
|
return main + vec4(0.0, 0.0, 0.0, s * 0.5 * c.a) * (1.0 - main.a)
|
|
}
|
|
if self.texture_index > 1.5 && self.texture_index < 2.5 {
|
|
let tex_size = self.msdf_texture.size()
|
|
let scale = (dxt + dyt) * tex_size.x * 0.5
|
|
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
|
|
let pm = clamp(self.t.xy - nudge, self.t_min + half_texel, self.t_max - half_texel)
|
|
let main = self.msdf(scale, pm, c) * vec4(c.rgb * c.a, c.a)
|
|
let ps = clamp(self.t.xy - nudge - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
|
|
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
|
|
return main + vec4(0.0, 0.0, 0.0, s * 0.5 * c.a) * (1.0 - main.a)
|
|
}
|
|
return self.sample_text_pixel()
|
|
}
|
|
|
|
// The same shadow on every label that stands on the ground or a cap; one
|
|
// literal per template, as the handbook advises. A lit legend (the tab or
|
|
// segment in use) takes a 2 dp halo of the LED's glow instead.
|
|
mod.widgets.Label.draw_text.pixel = fn() {
|
|
let main = self.sample_text_pixel()
|
|
if self.texture_index > 1.5 && self.texture_index < 2.5 {
|
|
let dxt = length(dFdx(self.t))
|
|
let dyt = length(dFdy(self.t))
|
|
let tex_size = self.msdf_texture.size()
|
|
let scale = (dxt + dyt) * tex_size.x * 0.5
|
|
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
|
|
let ps = clamp(self.t.xy - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
|
|
let c = self.get_color()
|
|
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
|
|
return main + vec4(0.0, 0.0, 0.0, s * 0.5 * c.a) * (1.0 - main.a)
|
|
}
|
|
return main
|
|
}
|
|
mod.widgets.CheckBoxFlat.draw_text.pixel = fn() {
|
|
let main = self.sample_text_pixel()
|
|
if self.texture_index > 1.5 && self.texture_index < 2.5 {
|
|
let dxt = length(dFdx(self.t))
|
|
let dyt = length(dFdy(self.t))
|
|
let tex_size = self.msdf_texture.size()
|
|
let scale = (dxt + dyt) * tex_size.x * 0.5
|
|
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
|
|
let ps = clamp(self.t.xy - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
|
|
let c = self.get_color()
|
|
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
|
|
return main + vec4(0.0, 0.0, 0.0, s * 0.5 * c.a) * (1.0 - main.a)
|
|
}
|
|
return main
|
|
}
|
|
mod.widgets.RadioButtonFlat.draw_text.pixel = fn() {
|
|
let main = self.sample_text_pixel()
|
|
if self.texture_index > 1.5 && self.texture_index < 2.5 {
|
|
let dxt = length(dFdx(self.t))
|
|
let dyt = length(dFdy(self.t))
|
|
let tex_size = self.msdf_texture.size()
|
|
let scale = (dxt + dyt) * tex_size.x * 0.5
|
|
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
|
|
let ps = clamp(self.t.xy - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
|
|
let c = self.get_color()
|
|
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
|
|
return main + vec4(0.0, 0.0, 0.0, s * 0.5 * c.a) * (1.0 - main.a)
|
|
}
|
|
return main
|
|
}
|
|
mod.widgets.DropDownFlat.draw_text.pixel = fn() {
|
|
let main = self.sample_text_pixel()
|
|
if self.texture_index > 1.5 && self.texture_index < 2.5 {
|
|
let dxt = length(dFdx(self.t))
|
|
let dyt = length(dFdy(self.t))
|
|
let tex_size = self.msdf_texture.size()
|
|
let scale = (dxt + dyt) * tex_size.x * 0.5
|
|
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
|
|
let ps = clamp(self.t.xy - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
|
|
let c = self.get_color()
|
|
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
|
|
return main + vec4(0.0, 0.0, 0.0, s * 0.5 * c.a) * (1.0 - main.a)
|
|
}
|
|
return main
|
|
}
|
|
mod.widgets.SegmentedControlFlat.draw_text.pixel = fn() {
|
|
let main = self.sample_text_pixel()
|
|
if self.texture_index > 1.5 && self.texture_index < 2.5 {
|
|
let dxt = length(dFdx(self.t))
|
|
let dyt = length(dFdy(self.t))
|
|
let tex_size = self.msdf_texture.size()
|
|
let scale = (dxt + dyt) * tex_size.x * 0.5
|
|
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
|
|
let ps = clamp(self.t.xy - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
|
|
let c = self.get_color()
|
|
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
|
|
return main + vec4(0.0, 0.0, 0.0, s * 0.5 * c.a) * (1.0 - main.a)
|
|
}
|
|
return main
|
|
}
|
|
mod.widgets.SegmentedControlFlat.draw_text_selected.pixel = fn() {
|
|
let main = self.sample_text_pixel()
|
|
if self.texture_index > 1.5 && self.texture_index < 2.5 {
|
|
let dxt = length(dFdx(self.t))
|
|
let dyt = length(dFdy(self.t))
|
|
let tex_size = self.msdf_texture.size()
|
|
let scale = (dxt + dyt) * tex_size.x * 0.5
|
|
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
|
|
let ex = dFdx(self.t.xy) * 1.5
|
|
let ey = dFdy(self.t.xy) * 1.5
|
|
let k = vec4(0.0, 0.0, 0.0, 1.0)
|
|
var g = self.msdf(scale, clamp(self.t.xy + ex, self.t_min + half_texel, self.t_max - half_texel), k)
|
|
g = g + self.msdf(scale, clamp(self.t.xy - ex, self.t_min + half_texel, self.t_max - half_texel), k)
|
|
g = g + self.msdf(scale, clamp(self.t.xy + ey, self.t_min + half_texel, self.t_max - half_texel), k)
|
|
g = g + self.msdf(scale, clamp(self.t.xy - ey, self.t_min + half_texel, self.t_max - half_texel), k)
|
|
let c = self.get_color()
|
|
let a = clamp(g * 0.25, 0.0, 1.0) * 0.4 * c.a
|
|
return main + vec4(0.32 * a, 0.33 * a, 0.36 * a, a) * (1.0 - main.a)
|
|
}
|
|
return main
|
|
}
|
|
mod.widgets.Tabs.draw_text.pixel = fn() {
|
|
let main = self.sample_text_pixel()
|
|
if self.texture_index > 1.5 && self.texture_index < 2.5 {
|
|
let dxt = length(dFdx(self.t))
|
|
let dyt = length(dFdy(self.t))
|
|
let tex_size = self.msdf_texture.size()
|
|
let scale = (dxt + dyt) * tex_size.x * 0.5
|
|
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
|
|
let ps = clamp(self.t.xy - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
|
|
let c = self.get_color()
|
|
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
|
|
return main + vec4(0.0, 0.0, 0.0, s * 0.5 * c.a) * (1.0 - main.a)
|
|
}
|
|
return main
|
|
}
|
|
mod.widgets.Tabs.draw_text_active.pixel = fn() {
|
|
let main = self.sample_text_pixel()
|
|
if self.texture_index > 1.5 && self.texture_index < 2.5 {
|
|
let dxt = length(dFdx(self.t))
|
|
let dyt = length(dFdy(self.t))
|
|
let tex_size = self.msdf_texture.size()
|
|
let scale = (dxt + dyt) * tex_size.x * 0.5
|
|
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
|
|
let ex = dFdx(self.t.xy) * 1.5
|
|
let ey = dFdy(self.t.xy) * 1.5
|
|
let k = vec4(0.0, 0.0, 0.0, 1.0)
|
|
var g = self.msdf(scale, clamp(self.t.xy + ex, self.t_min + half_texel, self.t_max - half_texel), k)
|
|
g = g + self.msdf(scale, clamp(self.t.xy - ex, self.t_min + half_texel, self.t_max - half_texel), k)
|
|
g = g + self.msdf(scale, clamp(self.t.xy + ey, self.t_min + half_texel, self.t_max - half_texel), k)
|
|
g = g + self.msdf(scale, clamp(self.t.xy - ey, self.t_min + half_texel, self.t_max - half_texel), k)
|
|
let c = self.get_color()
|
|
let a = clamp(g * 0.25, 0.0, 1.0) * 0.5 * c.a
|
|
return main + vec4(0.62 * a, 0.36 * a, 0.08 * a, a) * (1.0 - main.a)
|
|
}
|
|
return main
|
|
}
|
|
mod.widgets.SliderMinimal.draw_text.pixel = fn() {
|
|
let main = self.sample_text_pixel()
|
|
if self.texture_index > 1.5 && self.texture_index < 2.5 {
|
|
let dxt = length(dFdx(self.t))
|
|
let dyt = length(dFdy(self.t))
|
|
let tex_size = self.msdf_texture.size()
|
|
let scale = (dxt + dyt) * tex_size.x * 0.5
|
|
let half_texel = vec2(0.5 / tex_size.x, 0.5 / tex_size.y)
|
|
let ps = clamp(self.t.xy - dFdy(self.t.xy), self.t_min + half_texel, self.t_max - half_texel)
|
|
let c = self.get_color()
|
|
let s = self.msdf(scale, ps, vec4(0.0, 0.0, 0.0, 1.0))
|
|
return main + vec4(0.0, 0.0, 0.0, s * 0.5 * c.a) * (1.0 - main.a)
|
|
}
|
|
return main
|
|
}
|
|
|
|
// ---------------------------------------------------------------- field row
|
|
|
|
// A ROW OF FIELDS IS ONE HEIGHT. Left to themselves the library's fields each
|
|
// come out at a size of their own -- in the widget catalogue's toolbar the
|
|
// search box, the number beside it and the drop down after it stood at three
|
|
// different heights -- and a sheet that reshapes the text box alone widens
|
|
// that gap instead of closing it. So the box metrics are stated ONCE here and
|
|
// handed to each field below, and a field added to the library later joins
|
|
// the row by being named here rather than by having these numbers copied
|
|
// into it.
|
|
// Fields and buttons share the one 26 point row.
|
|
let field_height = 26.0
|
|
let field_pad_v = 4.0
|
|
let field_radius = 1.5
|
|
// The outline is one device pixel; the faces below draw it themselves.
|
|
let field_border = 0.34
|
|
|
|
// The room a Fit field takes. A minimum height rather than a height,
|
|
// because a line of text is shorter than a row, and the padding with it:
|
|
// where a field is Fit, the padding IS its height, and two fields padded
|
|
// differently part company again the moment the type grows.
|
|
let field_room = fn(w) {
|
|
w.min_height = field_height
|
|
w.padding.top = field_pad_v
|
|
w.padding.bottom = field_pad_v
|
|
}
|
|
// The box a field draws. Given the draw object and not the widget, because
|
|
// not every field draws its own box, and because writing a corner onto a
|
|
// draw struct that has no corner would add a slot its shader never reads.
|
|
let field_face = fn(d) {
|
|
d.border_radius = field_radius
|
|
d.border_size = field_border
|
|
}
|
|
// A field that states a frame and lays its own parts out inside it: a number
|
|
// with a stepper, a date, a time. The height is all these take. A padding
|
|
// here would NOT hold the line off the box -- each of them measures its
|
|
// parts against the turtle's whole rect and draws them from the content
|
|
// origin, so the padding displaces the line instead of insetting it, and the
|
|
// number came out sitting on the bottom edge with its arrows split around it.
|
|
let field_frame = fn(w) {
|
|
w.min_height = field_height
|
|
}
|
|
|
|
field_room(mod.widgets.TextInput)
|
|
field_face(mod.widgets.TextInput.draw_bg)
|
|
field_room(mod.widgets.ComboBox)
|
|
field_face(mod.widgets.ComboBox.draw_bg)
|
|
field_room(mod.widgets.DropDown)
|
|
field_face(mod.widgets.DropDown.draw_bg)
|
|
field_room(mod.widgets.DropDown2)
|
|
field_face(mod.widgets.DropDown2.draw_bg)
|
|
field_room(mod.widgets.FieldWell)
|
|
field_face(mod.widgets.FieldWell.draw_bg)
|
|
field_room(mod.widgets.TagField)
|
|
field_face(mod.widgets.TagField.draw_bg)
|
|
field_frame(mod.widgets.NumberField)
|
|
// The number's box strokes its own hairline and declares no border size.
|
|
mod.widgets.NumberField.draw_bg.border_radius = field_radius
|
|
// The drag-number's corner is written into its own shader, so it takes the
|
|
// room and keeps the box it draws.
|
|
field_frame(mod.widgets.ValueInput)
|
|
field_frame(mod.widgets.DateField)
|
|
field_frame(mod.widgets.TimeField)
|
|
// A picker is a date field in an anchor: the field inside fills the frame.
|
|
field_frame(mod.widgets.DatePicker)
|
|
field_frame(mod.widgets.DateRangePicker)
|
|
// The chrome-less input a well holds must impose no height of its own. It is
|
|
// a TextInput, so it inherits the minimum above, and a minimum as tall as the
|
|
// whole field, applied inside a well already that tall, pushed the line out
|
|
// through the bottom of the box.
|
|
mod.widgets.WellInput.min_height = 0.0
|
|
|
|
// ---------------------------------------------------------------- rules
|
|
|
|
// A rule is engraved: one device pixel of the outline's black over one of
|
|
// light, across the middle of its room, no shadow.
|
|
mod.widgets.Hr.draw_bg.pixel = fn() {
|
|
let px = au_px(self.draw_pass.dpi_factor)
|
|
let p = self.pos * self.rect_size
|
|
let y0 = au_snap(self.rect_size.y * 0.5 - px, self.rect_pos.y, px)
|
|
let dark = step(y0, p.y) * step(p.y, y0 + px)
|
|
let light = step(y0 + px, p.y) * step(p.y, y0 + px * 2.0)
|
|
return vec4(0.010, 0.010, 0.012, 1.0) * dark + vec4(0.26, 0.265, 0.28, 1.0) * light
|
|
}
|
|
mod.widgets.Vr.draw_bg.pixel = fn() {
|
|
let px = au_px(self.draw_pass.dpi_factor)
|
|
let p = self.pos * self.rect_size
|
|
let x0 = au_snap(self.rect_size.x * 0.5 - px, self.rect_pos.x, px)
|
|
let dark = step(x0, p.x) * step(p.x, x0 + px)
|
|
let light = step(x0 + px, p.x) * step(p.x, x0 + px * 2.0)
|
|
return vec4(0.010, 0.010, 0.012, 1.0) * dark + vec4(0.26, 0.265, 0.28, 1.0) * light
|
|
}
|
|
|
|
// ---------------------------------------------------------------- ground
|
|
|
|
// A splitter is a seam cut into the deck: the deck shows through its strip,
|
|
// and down its middle a device pixel of black over one of light. Under the
|
|
// hand, held or focused, the seam lights blue along the grip's length.
|
|
mod.widgets.Splitter.draw_bg.pixel = fn() {
|
|
let px = au_px(self.draw_pass.dpi_factor)
|
|
let p = self.pos * self.rect_size
|
|
let v = self.is_vertical
|
|
let across = mix(p.y, p.x, v)
|
|
let along = mix(p.x, p.y, v)
|
|
let thick = mix(self.rect_size.y, self.rect_size.x, v)
|
|
let span = mix(self.rect_size.x, self.rect_size.y, v)
|
|
let o = mix(self.rect_pos.y, self.rect_pos.x, v)
|
|
let m = au_snap(thick * 0.5, o, px) - px
|
|
let dark = step(m, across) * (1.0 - step(m + px, across))
|
|
let light = step(m + px, across) * (1.0 - step(m + px * 2.0, across))
|
|
let res = vec4(0.008, 0.008, 0.010, 1.0) * dark * 0.85 + vec4(0.40, 0.41, 0.43, 1.0) * light * 0.45
|
|
let act = max(max(self.hover, self.drag), self.focus)
|
|
let inbar = 1.0 - step(self.bar_size * 0.5, abs(along - span * 0.5))
|
|
return Finish.over(res, vec4(0.227, 0.612, 1.0, 1.0) * (dark + light) * act * inbar)
|
|
}
|
|
|
|
// The rows of a tree stand on the deck, clear; the open row is a slab of
|
|
// graphite cast blue, with a lit bar at its left end, as the list's chosen
|
|
// row.
|
|
mod.widgets.FileTreeNode.draw_bg.pixel = fn() {
|
|
let px = au_px(self.draw_pass.dpi_factor)
|
|
let p = self.pos * self.rect_size
|
|
let o = self.rect_pos
|
|
let y0 = au_snap(0.0, o.y, px)
|
|
let y1 = au_snap(self.rect_size.y, o.y, px) - px
|
|
let x0 = au_snap(0.0, o.x, px)
|
|
let x1 = au_snap(self.rect_size.x, o.x, px)
|
|
let t = clamp((p.y - y0) / max(y1 - y0, 1.0), 0.0, 1.0)
|
|
let cb = vec2((x0 + x1) * 0.5, (y0 + y1) * 0.5)
|
|
let d = Material.sd_box(p, cb, vec2((x1 - x0) * 0.5, (y1 - y0) * 0.5), 2.5)
|
|
var col = mix(vec3(0.150, 0.215, 0.300), vec3(0.100, 0.150, 0.215), t)
|
|
col = mix(col, vec3(0.45, 0.60, 0.78), Finish.band(d, px, px * 2.0, px) * (1.0 - step(cb.y, p.y)) * 0.8)
|
|
col = mix(col, vec3(0.012, 0.012, 0.014), Finish.band(d, 0.0, px, px))
|
|
var res = vec4(col, 1.0) * Finish.cover(d, px)
|
|
let lc = vec2(au_snap(3.5, o.x, px) - px * 0.5, cb.y)
|
|
res = au_led(res, p, lc, vec2(px * 0.5 + 0.01, max((y1 - y0) * 0.24, 2.0)), 1.0, 0.8, px, vec3(0.88, 0.96, 1.0), vec3(0.227, 0.612, 1.0))
|
|
return res * self.active
|
|
}
|
|
|
|
// Plain views stop clipping so a shadow can fall past their edge; scroll
|
|
// views keep clipping their content.
|
|
mod.widgets.View.clip_x = false
|
|
mod.widgets.View.clip_y = false
|
|
mod.widgets.Window.pass.clear_color = #2c2e32
|
|
// The window's own ground (`au_ground`): a brushed steel deck between bands
|
|
// of carbon weave.
|
|
mod.widgets.Window.show_bg = true
|
|
mod.widgets.Window.draw_bg.pixel = fn() {
|
|
let px = au_px(self.draw_pass.dpi_factor)
|
|
let p = self.pos * self.rect_size
|
|
return vec4(au_ground(p + self.rect_pos, px, self.rect_size.x, self.rect_size.y), 1.0)
|
|
}
|
|
|
|
// ---------------------------------------------------------------- badge
|
|
|
|
// The badge is drawn with the screens, as a small dash window (below).
|
|
|
|
// ---------------------------------------------------------------- states
|
|
|
|
// Every held state is written as a plain value that cuts in at once (the
|
|
// library writes some as snap() values, which a state held as a default --
|
|
// the states page -- never applies). Live, the cut is the same as the snap.
|
|
mod.widgets.ButtonFlat.animator.hover.on.from = {all: Snap}
|
|
mod.widgets.ButtonFlat.animator.hover.on.apply = {draw_bg: {down: 0.0 hover: 1.0} draw_text: {down: 0.0 hover: 1.0}}
|
|
mod.widgets.ButtonFlat.animator.hover.down.from = {all: Snap}
|
|
mod.widgets.ButtonFlat.animator.hover.down.apply = {draw_bg: {down: 1.0 hover: 1.0} draw_text: {down: 1.0 hover: 1.0}}
|
|
mod.widgets.CheckBoxFlat.animator.hover.down.from = {all: Snap}
|
|
mod.widgets.CheckBoxFlat.animator.hover.down.apply = {draw_bg: {down: 1.0 hover: 1.0} draw_text: {down: 1.0 hover: 1.0}}
|
|
mod.widgets.RadioButtonFlat.animator.hover.down.from = {all: Snap}
|
|
mod.widgets.RadioButtonFlat.animator.hover.down.apply = {draw_bg: {down: 1.0 hover: 1.0} draw_text: {down: 1.0 hover: 1.0}}
|
|
mod.widgets.TextInputFlat.animator.hover.on.from = {all: Snap}
|
|
mod.widgets.TextInputFlat.animator.hover.on.apply = {draw_bg: {down: 0.0 hover: 1.0} draw_text: {down: 0.0 hover: 1.0}}
|
|
mod.widgets.TextInputFlat.animator.hover.down.from = {all: Snap}
|
|
mod.widgets.TextInputFlat.animator.hover.down.apply = {draw_bg: {down: 1.0 hover: 1.0} draw_text: {down: 1.0 hover: 1.0}}
|
|
mod.widgets.DropDownFlat.animator.hover.on.from = {all: Snap}
|
|
mod.widgets.DropDownFlat.animator.hover.on.apply = {draw_bg: {down: 0.0 hover: 1.0} draw_text: {down: 0.0 hover: 1.0}}
|
|
mod.widgets.DropDownFlat.animator.hover.down.from = {all: Snap}
|
|
mod.widgets.DropDownFlat.animator.hover.down.apply = {draw_bg: {down: 1.0 hover: 1.0} draw_text: {down: 1.0 hover: 1.0}}
|
|
|
|
|
|
// ---------------------------------------------------------------- band
|
|
|
|
// THE BAND is the sheet's own slider with two thumbs: the same groove and
|
|
// the same neon tube as the Slider, lit between two round chrome thumbs.
|
|
// The widget's grab geometry is set to the thumbs' own, so what is drawn is
|
|
// what is dragged.
|
|
mod.widgets.RangeSliderFlat.clip_x = false
|
|
mod.widgets.RangeSliderFlat.clip_y = false
|
|
mod.widgets.RangeSliderFlat.track_inset = 0.0
|
|
mod.widgets.RangeSliderFlat.handle_size = 16.0
|
|
mod.widgets.RangeSliderFlat.draw_bg.vertex = fn() {
|
|
let m = 10.0
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
|
|
}
|
|
mod.widgets.RangeSliderFlat.draw_bg.pixel = fn() {
|
|
let m = 10.0
|
|
let px = au_px(self.draw_pass.dpi_factor)
|
|
let o = self.rect_pos
|
|
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let rad = 8.0
|
|
let cy = au_snap(self.label_px + (self.rect_size.y - self.label_px) * 0.5, o.y, px)
|
|
let lo = vec2(au_snap(0.0, o.x, px), cy - 5.5)
|
|
let hi = vec2(au_snap(self.rect_size.x, o.x, px), cy + 5.5)
|
|
let col = au_neon_at(o)
|
|
var res = au_groove(p, o, lo, hi, px, self.disabled)
|
|
let span = self.rect_size.x - rad * 2.0
|
|
let h0 = rad + self.start_pos * span
|
|
let h1 = rad + self.end_pos * span
|
|
res = au_tube(res, p, h0, h1, cy, 2.7, px, col, self.disabled)
|
|
res = au_ball(res, p, vec2(h0, cy), rad, px, self.hot_start, max(self.drag * self.hot_start, self.focus), self.disabled, col)
|
|
return au_ball(res, p, vec2(h1, cy), rad, px, self.hot_end, max(self.drag * self.hot_end, self.focus), self.disabled, col)
|
|
}
|
|
|
|
// ---------------------------------------------------------------- switches and meter
|
|
|
|
// THE ROCKER is a round illuminated push button, as a dash has for its
|
|
// ignition: an 18 point button in a polished chrome bezel, its cap of dark
|
|
// glass domed toward the light. On, a ring of red light burns round the cap
|
|
// inside the bezel and the glass glows red from within, its light lying on
|
|
// the ground round the bezel; off, the ring is dark tinted glass. Pressed,
|
|
// the cap sinks and dims. It flips on a click and does not drag.
|
|
mod.widgets.ToggleRocker.draw_bg.size = uniform(18.0)
|
|
mod.widgets.ToggleRocker.draw_bg.pill_aspect = uniform(1.0)
|
|
mod.widgets.ToggleRocker.label_walk.margin.left = 25.0
|
|
mod.widgets.ToggleRocker.draw_bg.pixel = fn() {
|
|
let m = 5.0
|
|
let px = au_px(self.draw_pass.dpi_factor)
|
|
let o = self.rect_pos
|
|
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let R = self.size * 0.5
|
|
let c = vec2(au_snap(R, o.x, px), au_snap(self.rect_size.y * 0.5, o.y, px))
|
|
let q = p - c
|
|
let rl = length(q)
|
|
let live = 1.0 - self.disabled
|
|
let on = self.active * live
|
|
let held = max(self.down, self.pressed)
|
|
let red = vec3(1.0, 0.16, 0.10)
|
|
let sh = Finish.drop(p, c, vec2(R, R), R, vec2(0.0, 1.0), 1.3) * 0.8 * (1.0 - Finish.cover(rl - R, px))
|
|
var res = vec4(0.0, 0.0, 0.0, sh)
|
|
let x = max(rl - R, 0.0)
|
|
let halo = (0.38 * exp(-(x * x) / (2.0 * 0.9 * 0.9)) + 0.16 * exp(-(x * x) / (2.0 * 2.0 * 2.0))) * (1.0 - smoothstep(1.4, 2.5, x)) * (1.0 - Finish.cover(rl - R, px))
|
|
res = Finish.over(res, vec4(red, 1.0) * clamp(halo * on, 0.0, 1.0))
|
|
res = Finish.over(res, au_chrome_ring(p, c, R - 1.8, R, px, self.hover))
|
|
// The light ring between the bezel and the cap.
|
|
let ri = R - 1.8
|
|
let rc = R - 3.2
|
|
let ringd = abs(rl - (ri + rc) * 0.5) - (ri - rc) * 0.5
|
|
let ra = Finish.cover(ringd, px)
|
|
let rt = clamp((rl - rc) / max(ri - rc, 0.5), 0.0, 1.0)
|
|
let lit = mix(mix(red, vec3(1.0, 0.85, 0.80), 0.55 * (1.0 - abs(rt - 0.5) * 2.0)), red * 0.8, 0.2)
|
|
let dark = mix(vec3(0.10, 0.03, 0.03), vec3(0.05, 0.02, 0.02), rt)
|
|
res = Finish.over(res, vec4(mix(dark, lit, on) * ra, ra))
|
|
// The cap: dark glass domed toward the light, sunk when pressed.
|
|
let t = clamp((p.y - (c.y - rc)) / max(2.0 * rc, 1.0), 0.0, 1.0)
|
|
var cap = mix(vec3(0.20, 0.205, 0.215), vec3(0.06, 0.061, 0.066), smoothstep(0.0, 1.0, t))
|
|
cap = mix(cap, cap * 0.6, held)
|
|
cap = cap + red * on * (0.30 * exp(-(rl * rl) / max(rc * rc * 0.6, 0.5)) + 0.12)
|
|
let sp = (q - vec2(-0.3 * rc, -0.42 * rc)) / vec2(max(0.5 * rc, 0.5), max(0.3 * rc, 0.4))
|
|
cap = cap + vec3(0.30, 0.30, 0.31) * exp(-dot(sp, sp)) * (1.0 - 0.6 * held) * live
|
|
cap = cap + vec3(0.03, 0.03, 0.03) * self.hover * live
|
|
let cg = Finish.lum(cap)
|
|
cap = mix(cap, vec3(cg, cg, cg) * 0.6, self.disabled)
|
|
cap = mix(cap, vec3(0.0, 0.0, 0.0), Finish.band(rl - rc, 0.0, px, px) * 0.85)
|
|
let ca = Finish.cover(rl - rc, px)
|
|
res = Finish.over(res, vec4(cap * ca, ca))
|
|
let fr = Finish.ring_out(rl - R, 0.0, px, px) * 0.9 + Finish.glow(rl - R - px, 0.7) * 0.3 * (1.0 - Finish.cover(rl - R, px))
|
|
return Finish.over(res, vec4(0.227, 0.612, 1.0, 1.0) * clamp(fr, 0.0, 1.0) * self.focus * live)
|
|
}
|
|
|
|
// THE SLIDE SWITCH is a chrome bat-handle toggle on a graphite plate, as a
|
|
// race car's switch panel has them: a chrome bushing in the middle of the
|
|
// plate and the lever standing from it toward its state, left off and
|
|
// right on, a tapered rod of polished chrome ending in a ball, its shadow
|
|
// on the plate. Dragged, the lever swings through upright (pointing at the
|
|
// viewer, only its ball over the bushing). A small green LED in the plate's
|
|
// right corner is lit while it is on.
|
|
mod.widgets.ToggleSlide.draw_bg.size = uniform(18.0)
|
|
mod.widgets.ToggleSlide.draw_bg.pill_aspect = uniform(1.89)
|
|
mod.widgets.ToggleSlide.label_walk.margin.left = 34.0
|
|
mod.widgets.ToggleSlide.draw_bg.pixel = fn() {
|
|
let m = 5.0
|
|
let px = au_px(self.draw_pass.dpi_factor)
|
|
let o = self.rect_pos
|
|
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let tw = self.size * self.pill_aspect
|
|
let th = self.size
|
|
let y0 = self.rect_size.y * 0.5 - th * 0.5
|
|
let lo = vec2(au_snap(0.0, o.x, px), au_snap(y0, o.y, px))
|
|
let hi = vec2(au_snap(tw, o.x, px), au_snap(y0 + th, o.y, px))
|
|
let live = 1.0 - self.disabled
|
|
let on = self.active * live
|
|
var res = au_cap(p, lo, hi, 3.0, px, 0.0, 0.0, self.disabled, self.focus, 1.0, vec3(-0.07, -0.07, -0.072), vec4(0.17, 0.17, 0.18, 1.0), 0.0)
|
|
let c = vec2(au_snap((lo.x + hi.x) * 0.5, o.x, px), au_snap((lo.y + hi.y) * 0.5, o.y, px))
|
|
// The LED.
|
|
res = au_led(res, p, vec2(hi.x - 3.5, lo.y + 3.5), vec2(1.2, 1.2), on, 0.8, px, vec3(0.86, 1.0, 0.88), vec3(0.20, 0.95, 0.30))
|
|
// The bushing: a chrome nut round a dark bore.
|
|
res = Finish.over(res, Finish.drop(p, c, vec2(5.0, 5.0), 5.0, vec2(0.0, 0.8), 0.8) * vec4(0.0, 0.0, 0.0, 0.6))
|
|
res = Finish.over(res, au_chrome_ring(p, c, 2.4, 5.0, px, self.hover))
|
|
let bd = length(p - c) - 2.4
|
|
res = Finish.over(res, vec4(0.02, 0.02, 0.025, 1.0) * Finish.cover(bd, px))
|
|
// The lever.
|
|
let kt = mix(self.active, self.drag_pos, self.drag)
|
|
let s = kt * 2.0 - 1.0
|
|
let L = 12.0 * abs(s)
|
|
let dir = vec2(sign(s + 0.0001), 0.0)
|
|
let tip = c + dir * L
|
|
let rb = 3.2
|
|
// Its shadow on the plate, down and to the right.
|
|
let sq = p - vec2(1.0, 1.6)
|
|
let sdl = min(au_seg(sq, c, tip) - mix(2.2, 1.4, clamp(dot(sq - c, dir) / max(L, 0.5), 0.0, 1.0)), length(sq - tip) - rb)
|
|
res = Finish.over(res, vec4(0.0, 0.0, 0.0, 0.55 * Finish.cover(sdl, 1.0)))
|
|
let u = clamp(dot(p - c, dir) / max(L, 0.5), 0.0, 1.0)
|
|
let rw = mix(2.2, 1.4, u)
|
|
let rod = au_seg(p, c, tip) - rw
|
|
let across = clamp(((p - c).y) / max(rw, 0.3), -1.0, 1.0)
|
|
var ch = mix(vec3(0.95, 0.95, 0.96), vec3(0.25, 0.25, 0.27), smoothstep(-0.6, 0.8, across))
|
|
ch = mix(ch, vec3(0.55, 0.555, 0.57), smoothstep(0.6, 1.0, across) * 0.6)
|
|
ch = mix(ch, vec3(0.03, 0.03, 0.035), Finish.band(rod, 0.0, px, px) * 0.8)
|
|
let ra = Finish.cover(rod, px)
|
|
res = Finish.over(res, vec4(ch * ra, ra))
|
|
// The ball at its end: a chrome sphere.
|
|
let bq = (p - tip) / rb
|
|
let bz = sqrt(max(1.0 - dot(bq, bq), 0.0))
|
|
let n = normalize(vec3(bq, bz + 0.001))
|
|
let rf = vec3(2.0 * n.z * n.x, 2.0 * n.z * n.y, 2.0 * n.z * n.z - 1.0)
|
|
var bc = au_chrome_room(rf, 1.0) * (1.0 + 0.1 * self.hover * live)
|
|
let bg = Finish.lum(bc)
|
|
bc = mix(bc, vec3(bg, bg, bg) * 0.5, self.disabled)
|
|
let bdd = length(p - tip) - rb
|
|
bc = mix(bc, vec3(0.03, 0.03, 0.035), Finish.band(bdd, 0.0, px, px))
|
|
let ba = Finish.cover(bdd, px)
|
|
return Finish.over(res, vec4(bc * ba, ba))
|
|
}
|
|
|
|
// THE METER is a gauge in the same family as the dials and the readouts: a
|
|
// polished chrome frame round black glass, its scale and numbers white, the
|
|
// red line red, a red needle on a chrome pivot with its glow on the face, a
|
|
// faint glass sheen. The scale, its divisions and the needle's travel are
|
|
// the library's own.
|
|
mod.widgets.NeedleMeter.draw_bg.border_radius = uniform(3.0)
|
|
mod.widgets.NeedleMeter.draw_bg.color_bezel = uniform(theme.color_d_05)
|
|
mod.widgets.NeedleMeter.draw_bg.color_lip = uniform(theme.color_u_15)
|
|
mod.widgets.NeedleMeter.draw_bg.pixel = fn() {
|
|
let p = self.pos * self.rect_size
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let lo = vec2(Finish.snap(self.rect_pos.x, px), Finish.snap(self.rect_pos.y, px)) - self.rect_pos
|
|
let hi = vec2(Finish.snap(self.rect_pos.x + self.rect_size.x, px), Finish.snap(self.rect_pos.y + self.rect_size.y, px) - px) - self.rect_pos
|
|
let c = (lo + hi) * 0.5
|
|
let ho = (hi - lo) * 0.5
|
|
let r_out = min(self.border_radius, min(ho.x, ho.y))
|
|
let outer = Material.sd_box(p, c, ho, r_out)
|
|
let pv = self.pivot()
|
|
let rr = self.scale_radius()
|
|
let q = p - pv
|
|
let r = length(q)
|
|
let ang = atan2(q.x, 0.0 - q.y)
|
|
let sweep = self.sweep * 0.017453293
|
|
let t = ang / sweep + 0.5
|
|
let on_scale = step(-0.0001, t) * step(t, 1.0001)
|
|
// The face: black, a little lighter round the pivot.
|
|
let ft = clamp(r / max(rr * 1.3, 1.0), 0.0, 1.0)
|
|
var face = mix(vec3(0.075, 0.076, 0.080), vec3(0.022, 0.022, 0.025), ft)
|
|
let arc = Finish.cover(abs(r - rr) - px * 0.5, px) * on_scale
|
|
let n = max(floor(self.majors + 0.5), 1.0) * max(floor(self.minors + 0.5), 1.0)
|
|
let k = floor(clamp(t, 0.0, 1.0) * n + 0.5)
|
|
let ak = (k / n - 0.5) * sweep
|
|
let perp = abs(q.x * cos(ak) + q.y * sin(ak))
|
|
let mn = max(floor(self.minors + 0.5), 1.0)
|
|
let major = 1.0 - step(0.5, k - mn * floor(k / mn + 0.0001))
|
|
let len = mix(self.tick_minor, self.tick_major, major)
|
|
let radial = max(rr - len - r, r - rr)
|
|
let tick = Finish.cover(perp - px * mix(0.5, 1.0, major), px) * Finish.cover(radial, px) * step(0.0, 0.0 - q.y * cos(ak) + q.x * sin(ak))
|
|
let inz = step(self.zone, k / n)
|
|
face = mix(face, mix(vec3(0.92, 0.93, 0.94), vec3(1.0, 0.25, 0.18), inz), max(arc * (1.0 - step(self.zone, t)), tick))
|
|
if self.zone < 0.999 {
|
|
let zone_t = Finish.cover((self.zone - t) * sweep * r, px) * Finish.cover((t - 1.0) * sweep * r, px)
|
|
let zone_r = Finish.cover(max(rr + px - r, r - rr - 2.5), px)
|
|
face = mix(face, vec3(0.90, 0.07, 0.05), zone_t * zone_r)
|
|
}
|
|
// The needle: red, its glow on the face round it.
|
|
let na = self.needle_angle(self.value)
|
|
let nd = vec2(sin(na), 0.0 - cos(na))
|
|
let along = dot(q, nd)
|
|
let across = abs(q.x * nd.y - q.y * nd.x)
|
|
let nb = max(across - px * 0.7, max(along - (rr + 3.0), 0.0 - along - max(self.pivot_size - px, 0.0)))
|
|
let gx = max(nb, 0.0)
|
|
face = mix(face, vec3(1.0, 0.12, 0.06), (0.35 * exp(-(gx * gx) / (2.0 * 0.8 * 0.8)) + 0.12 * exp(-(gx * gx) / (2.0 * 2.0 * 2.0))) * (1.0 - Finish.cover(nb, px)))
|
|
face = mix(face, vec3(1.0, 0.22, 0.14), Finish.cover(nb, px))
|
|
// The pivot: a chrome cap.
|
|
let pd = r - self.pivot_size
|
|
let pq = (q - vec2(-0.3, -0.4) * self.pivot_size) / max(self.pivot_size * 0.6, 0.5)
|
|
let pc = mix(vec3(0.35, 0.35, 0.37), vec3(0.95, 0.95, 0.96), exp(-dot(pq, pq)))
|
|
face = mix(face, pc, Finish.cover(pd, px))
|
|
face = mix(face, vec3(0.02, 0.02, 0.025), Finish.band(pd, 0.0, px, px))
|
|
// The frame: two and a half points of polished chrome, bright on the
|
|
// top and the foot, a black line where the glass meets it.
|
|
let b = Finish.snap(2.5, px)
|
|
let hf = max(ho - vec2(b, b), vec2(0.5, 0.5))
|
|
let rf = max(r_out - b * 0.5, 0.5)
|
|
let face_d = Material.sd_box(p, c, hf, rf)
|
|
face = face * (1.0 - 0.6 * Finish.inset(p, c, hf, rf, vec2(0.0, 2.0), 1.8))
|
|
face = mix(face, vec3(0.0, 0.0, 0.0), Finish.band(face_d, 0.0, px, px))
|
|
let u = (p - (c - hf)) / max(hf * 2.0, vec2(1.0, 1.0))
|
|
let diag = u.x + u.y * 0.6
|
|
let band = smoothstep(0.0, 0.12, diag) * (1.0 - smoothstep(0.28, 0.5, diag))
|
|
face = face + vec3(0.025, 0.025, 0.027) * band
|
|
let fu = clamp(-outer / max(b, px), 0.0, 1.0)
|
|
let ny = fu * 2.0 - 1.0
|
|
let ty = clamp((p.y - lo.y) / max(hi.y - lo.y, 1.0), 0.0, 1.0)
|
|
let lit = max(1.0 - smoothstep(0.0, 0.2, ty), smoothstep(0.8, 1.0, ty))
|
|
var fc = mix(vec3(0.40, 0.405, 0.42), vec3(0.92, 0.925, 0.935), exp(-(ny + 0.2) * (ny + 0.2) / 0.12))
|
|
fc = mix(fc * 0.75, min(fc * 1.12, vec3(1.0, 1.0, 1.0)), lit)
|
|
var col = mix(fc, face, Finish.cover(face_d, px))
|
|
col = mix(col, vec3(0.008, 0.008, 0.010), Finish.band(outer, 0.0, px, px))
|
|
let a = Finish.cover(outer, px)
|
|
return vec4(col * a, a) * self.color.a * self.opacity
|
|
}
|
|
|
|
// THE SCREENS: a readout's window and the screen are the dash window, black
|
|
// glass in a polished silver frame (`au_glass_box`); the segments are the
|
|
// library's, lit white.
|
|
mod.widgets.ScreenView.draw_bg.border_radius = uniform(3.0)
|
|
mod.widgets.ScreenView.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * self.rect_size
|
|
let o = self.rect_pos
|
|
let lo = vec2(Finish.snap(o.x, px), Finish.snap(o.y, px)) - o
|
|
let hi = vec2(Finish.snap(o.x + self.rect_size.x, px), Finish.snap(o.y + self.rect_size.y, px)) - o
|
|
var res = au_glass_box(p, lo, hi, px, 3.5)
|
|
if self.scan > 0.0 {
|
|
let s = Finish.scan(p.y - lo.y, px, 3.0 * px, self.scan)
|
|
res = vec4(res.rgb * s, res.a)
|
|
}
|
|
return res
|
|
}
|
|
mod.widgets.ScreenView.sheen = 0.0
|
|
|
|
// The badge (a count) is the same small dash window, its number white.
|
|
mod.widgets.BadgePalette.neutral = #060708
|
|
mod.widgets.BadgePalette.on_neutral = #f2f5f8
|
|
mod.widgets.BadgeFlat.shape = mod.widgets.BadgeShape.Rounded
|
|
mod.widgets.BadgeFlat.radius = 2.5
|
|
mod.widgets.BadgeFlat.draw_bg.pixel = fn() {
|
|
let px = au_px(self.draw_pass.dpi_factor)
|
|
let o = self.rect_pos
|
|
let p = self.pos * self.rect_size
|
|
let lo = vec2(au_snap(0.0, o.x, px), au_snap(0.0, o.y, px))
|
|
let hi = vec2(au_snap(self.rect_size.x - px * 0.49, o.x, px), au_snap(self.rect_size.y - px * 0.49, o.y, px))
|
|
let l = Finish.lum(self.color.rgb)
|
|
let ch = length(self.color.rgb - vec3(l, l, l))
|
|
var res = au_glass_box(p, lo, hi, px, 2.5)
|
|
// A badge given a role colour keeps it, as a tint in its glass.
|
|
let d = Material.sd_box(p, (lo + hi) * 0.5, (hi - lo) * 0.5 - vec2(px * 3.0, px * 3.0), 1.0)
|
|
res = Finish.over(res, vec4(self.color.rgb, 1.0) * Finish.cover(d, px) * smoothstep(0.08, 0.16, ch) * 0.5 * self.color.a)
|
|
return res
|
|
}
|
|
|
|
// ---------------------------------------------------------------- lamps
|
|
// LAMPS: an LED, as the hardware in his pictures has them. The emitter is
|
|
// the light: at full current its middle burns toward white and its rim is
|
|
// its most saturated colour; at lower current the middle is no longer
|
|
// white, the colour deeper, the glow smaller; out, it is dark tinted glass,
|
|
// its colour barely there. Round it a bezel: a raised ring lit from the
|
|
// upper left, or a socket cut into the surface, its upper wall in shadow
|
|
// and a catch light on its lower rim. Over the lens one crisp highlight of
|
|
// a dome. Its light is added, never painted: a tight hot halo and a short
|
|
// bloom in its own hue, screened over whatever is there and gone in about
|
|
// two radii; on a pale ground, where light can barely brighten, its hue is
|
|
// also laid round it as a tint that keeps the ground's luminance (the
|
|
// library's halo rule: at most 0.45 one device pixel out, halving every
|
|
// 0.4 of its reach, gone at the reach), so it never darkens or greys its
|
|
// ground. A bar is the same light drawn out into a light pipe.
|
|
let au_led_lamp_add = fn(res: vec4, light: vec3, ground: vec3) -> vec4 {
|
|
let est = res.rgb + ground * (1.0 - res.a)
|
|
let add = (vec3(1.0, 1.0, 1.0) - exp(-light)) * max(vec3(1.0, 1.0, 1.0) - est, vec3(0.0, 0.0, 0.0))
|
|
return vec4(res.rgb + add, res.a)
|
|
}
|
|
// An LED's colour at full strength: its brightest channel at one.
|
|
let au_led_lamp_full = fn(ink: vec3) -> vec3 {
|
|
return ink / max(max(ink.x, max(ink.y, ink.z)), 0.01)
|
|
}
|
|
// The same colour at the emitter's own chroma: the hue kept and its
|
|
// saturation raised by `vivid` (0 leaves a pale colour pale; a colour with
|
|
// next to no hue, a white or a plain lamp, stays neutral either way).
|
|
let au_led_lamp_hue = fn(ink: vec3, vivid: float) -> vec3 {
|
|
let f = au_led_lamp_full(ink)
|
|
let mn = min(f.x, min(f.y, f.z))
|
|
let ch = 1.0 - mn
|
|
let pure = (f - vec3(mn, mn, mn)) / max(ch, 0.001)
|
|
return mix(f, pure, clamp(vivid, 0.0, 1.0) * 0.72 * smoothstep(0.03, 0.12, ch))
|
|
}
|
|
// The lamp: its lens a rounded box `hb` either side of `c` (round when the
|
|
// two are equal), `lit` its current 0..1, `ink` its colour and `vivid` how
|
|
// far its chroma is raised, `ground` what shows through where nothing is
|
|
// drawn, `room` how far its light may reach, `bez` and `raised` its bezel
|
|
// (raised 0: a recess cut in what is under it; 1: a raised ring of `bez`;
|
|
// 2: the same ring polished metal). Premultiplied over `dst`.
|
|
let au_led_lamp = fn(dst: vec4, p: vec2, c: vec2, hb: vec2, lit: float, ink: vec3, vivid: float, px: float, ground: vec3, room: float, bez: vec3, raised: float) -> vec4 {
|
|
let rt = max(min(hb.x, hb.y), 0.4)
|
|
let d = Material.sd_box(p, c, hb, rt)
|
|
let bw = clamp(0.3 * rt, 0.7, 2.4)
|
|
let ds = d - bw
|
|
let L = clamp(lit, 0.0, 1.0)
|
|
let b = au_led_lamp_full(ink)
|
|
let hue = au_led_lamp_hue(ink, vivid)
|
|
let pw = L * sqrt(L)
|
|
let white = vec3(1.0, 1.0, 1.0)
|
|
let lum = vec3(0.2126, 0.7152, 0.0722)
|
|
let pale = smoothstep(0.35, 0.75, dot(ground, lum))
|
|
var res = dst
|
|
let x = max(ds, 0.0)
|
|
let reach = max(room, 1.0)
|
|
let win = 1.0 - smoothstep(0.55 * reach, reach, x)
|
|
// The lens's own frame: `q` in lens radii from its centre line, `t` its
|
|
// distance out, `nrm` the way its outline faces.
|
|
let ex = max(hb.x - hb.y, 0.0)
|
|
let ey = max(hb.y - hb.x, 0.0)
|
|
let qx = p.x - c.x
|
|
let qy = p.y - c.y
|
|
let q = vec2(qx - clamp(qx, 0.0 - ex, ex), qy - clamp(qy, 0.0 - ey, ey)) / rt
|
|
let t = clamp(length(q), 0.0, 1.0)
|
|
let nrm = q / max(length(q), 0.001)
|
|
// What the bead and its bezel throw on the ground: a soft shadow, down.
|
|
let shd = Finish.drop(p, c, hb + vec2(bw, bw), rt + bw, vec2(0.0, 0.22 * rt + 0.3), 0.32 * rt + px) * mix(0.18, 0.34, min(raised, 1.0)) * (1.0 - 0.6 * pw)
|
|
res = Finish.over(res, vec4(ground * 0.4 * shd, shd))
|
|
if L > 0.001 && pale > 0.001 {
|
|
// The tint, on a pale ground only: its hue laid over, never darker.
|
|
let h = clamp(0.42 * pw * pow(2.0, -x / max(0.4 * reach, 0.5)) * win, 0.0, 0.45) * pale
|
|
let est = res.rgb + ground * (1.0 - res.a)
|
|
let w = clamp(dot(hue, lum) / max(dot(est, lum), 0.001), 0.0, 1.0)
|
|
res = Finish.over(res, vec4(hue * h * w, h * w))
|
|
}
|
|
// The bezel: raised, a ring of `bez` rounded over, lit from the upper
|
|
// left; or a socket cut into what is under it, its upper wall in shadow
|
|
// and a catch light on its lower rim.
|
|
let a_s = Finish.cover(ds, px)
|
|
let u = clamp(d / max(bw, 0.01), 0.0, 1.0)
|
|
let facing = dot(nrm * (u - 0.5) * 2.0, vec2(-0.45, -0.89))
|
|
let metal = clamp(raised - 1.0, 0.0, 1.0)
|
|
var rc = bez * (0.94 + mix(0.12, 0.55, metal) * facing)
|
|
rc = mix(rc, min(bez + vec3(0.30, 0.30, 0.30), white), smoothstep(mix(0.35, 0.55, metal), 0.9, facing) * mix(0.6, 0.85, metal))
|
|
rc = mix(rc, rc * 0.8, Finish.band(ds, 0.0, max(px, 0.45), px) * smoothstep(0.0, 0.8, nrm.y) * 0.8)
|
|
let under = res.rgb / max(res.a, 0.001) * step(0.001, res.a) + ground * (1.0 - step(0.001, res.a))
|
|
let gy = clamp(nrm.y * t, -1.0, 1.0)
|
|
var sc = under * mix(0.55, 0.92, smoothstep(-0.9, 0.9, gy))
|
|
sc = mix(sc, min(under * 1.06 + vec3(0.28, 0.28, 0.28), white), Finish.band(ds, 0.0, max(px, 0.45), px) * smoothstep(0.2, 0.95, nrm.y) * 0.85)
|
|
sc = min(mix(sc, rc, min(raised, 1.0)), white)
|
|
res = Finish.over(res, vec4(sc * a_s, a_s))
|
|
// The light: a tight hot halo and a short bloom in its hue, gone in
|
|
// about two radii, falling on the bezel too.
|
|
if L > 0.001 {
|
|
let xl = max(d, 0.0)
|
|
let halo = exp(-xl / max(0.30 * rt, 0.4)) * 1.5 + exp(-xl / max(0.65 * rt, 0.7)) * 0.32
|
|
let hwin = 1.0 - smoothstep(1.1 * rt, 2.0 * rt + 1.0, xl)
|
|
res = au_led_lamp_add(res, mix(hue, white, 0.15 * pw) * halo * pw * hwin * win, ground)
|
|
}
|
|
// THE BEAD: a glossy dome of coloured glass. Its colour deepens toward
|
|
// the top; light pools low inside, where it comes through the dome; lit,
|
|
// its middle burns toward white at full current and its rim is its most
|
|
// saturated colour.
|
|
let top = smoothstep(-1.0, 0.6, q.y)
|
|
let pool = exp(-(q.y - 0.55) * (q.y - 0.55) / 0.09 - q.x * q.x / 0.30)
|
|
var on = mix(hue * mix(0.55, 0.8, L), mix(b, hue, 0.5), top)
|
|
on = mix(on, min(mix(b, hue, 0.4) + vec3(0.35, 0.35, 0.35), white), pool * 0.5)
|
|
on = mix(on, vec3(1.0, 0.99, 0.975), exp(-t * t / 0.20) * smoothstep(0.5, 1.0, L) * 0.85)
|
|
on = mix(on, hue * (0.82 + 0.18 * L), smoothstep(0.62, 1.0, t))
|
|
on = on * (0.42 + 0.58 * L)
|
|
// Out: dark tinted glass on a dark ground, a grey bead on a pale one,
|
|
// its colour barely there, the same dome, a catch light along its lower
|
|
// rim.
|
|
let offc = mix(mix(vec3(0.085, 0.09, 0.115), ground * 0.62, pale), b * 0.4, 0.12)
|
|
var off = mix(offc * 0.62, offc * 1.08, top)
|
|
off = mix(off, min(offc + vec3(0.18, 0.18, 0.18), white), pool * 0.35)
|
|
off = off * mix(1.0, 0.75, smoothstep(0.75, 1.0, t))
|
|
let catchl = smoothstep(0.62, 0.9, t) * (1.0 - smoothstep(0.9, 1.0, t)) * smoothstep(0.2, 0.9, nrm.y)
|
|
off = off + vec3(0.30, 0.30, 0.32) * catchl * (1.0 - 0.5 * pale)
|
|
var col = mix(off, min(on, white), smoothstep(0.0, 0.35, L))
|
|
// One crisp highlight: a white oval across its upper third.
|
|
let e = length(vec2(q.x / 0.62, (q.y + 0.46) / 0.30))
|
|
let spec = (1.0 - smoothstep(0.55, 1.0, e)) * mix(1.0, 0.3, clamp((q.y + 0.76) / 0.6, 0.0, 1.0))
|
|
col = mix(col, white, spec * mix(0.8, 0.55, L) * smoothstep(0.9, 2.2, rt))
|
|
// Out, a faint seam where the dome meets its bezel; lit, none: the rim
|
|
// is the colour.
|
|
col = mix(col, col * 0.7, Finish.band(d, 0.0, px * 0.6, px) * 0.6 * (1.0 - smoothstep(0.0, 0.25, L)))
|
|
col = min(col, white)
|
|
let a_l = Finish.cover(d, px)
|
|
return Finish.over(res, vec4(col * a_l, a_l))
|
|
}
|
|
|
|
// The Lamp widget and the bar lamp: the lamp above, in a polished chrome ring.
|
|
// The halo's room is held in the lamp's own box (the library reserves a
|
|
// lens thickness either side once a halo is asked for), so the light is
|
|
// never cut at its edge.
|
|
mod.widgets.Lamp.halo = 0.3
|
|
mod.widgets.Lamp.halo_reach = 1.0
|
|
mod.widgets.Lamp.draw_bg.vertex = fn() {
|
|
let m = 2.0
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(m, m), self.rect_size + vec2(2.0 * m, 2.0 * m))
|
|
}
|
|
mod.widgets.Lamp.draw_bg.pixel = fn() {
|
|
let m = 2.0
|
|
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let reach = clamp(self.reach, 0.0, 1.0)
|
|
let t = self.rect_size.y / (1.0 + 2.0 * reach)
|
|
let room = t * reach
|
|
// A round lamp: the lens the lamp's size in the middle of its box, its
|
|
// socket and its light in the room round it. A bar: a short light pipe
|
|
// three quarters of a round lens thick and at most two of its
|
|
// thicknesses long, set three points in from the start of its box,
|
|
// its light gone within two points; so in a row of lamps it never
|
|
// reaches past its box toward whatever stands after it.
|
|
let tb = t * 0.75
|
|
let blen = min(max(self.rect_size.x - 2.0 * room, tb), 2.0 * tb)
|
|
let cb = vec2(min(room, 2.0) + 1.0 + blen * 0.5, self.rect_size.y * 0.5)
|
|
let c = mix(self.rect_size * 0.5, cb, self.bar)
|
|
let hb = mix(vec2(t * 0.5, t * 0.5), vec2(blen * 0.5, tb * 0.5), self.bar)
|
|
let reach_px = mix(max(room - 0.5, 2.0), 1.8, self.bar)
|
|
let res = au_led_lamp(vec4(0.0, 0.0, 0.0, 0.0), p, c, hb, self.lit, self.intent_color().rgb, 1.0 - step(3.5, self.intent), px, vec3(0.045, 0.046, 0.050), reach_px, vec3(0.62, 0.63, 0.65), 2.0)
|
|
return res * self.opacity
|
|
}
|
|
true
|