2352 lines
136 KiB
Text
2352 lines
136 KiB
Text
use mod.prelude.widgets_internal.*
|
|
use mod.sdf.Cap
|
|
|
|
// Futuristic plastic: slabs of translucent blue-grey plastic standing on a
|
|
// dark slate ground traced with faint circuits.
|
|
// A slab has thickness: a lit shoulder along its head, a frost sheen over
|
|
// its top third, one device pixel of pale rim, and along its foot the light
|
|
// that lives inside the plastic: a pale cyan edge that glows up into the
|
|
// face and leaks a little onto the ground under it. A dark drop and that
|
|
// cyan underglow fall outside the layout box and are gone within half the
|
|
// gap to the next control. Pushed in, the slab settles, its head goes dark
|
|
// and its light dims. What is on is lit from within.
|
|
mod.widgets.Button.padding = mod.turtle.Inset{left: 12.0 right: 12.0 top: 4.0 bottom: 4.0}
|
|
mod.widgets.Button.draw_bg.border_radius = uniform(3.5)
|
|
mod.widgets.Button.draw_bg.border_size = uniform(0.5)
|
|
// A ROW OF FIELDS IS ONE HEIGHT: the box metrics are stated once here and
|
|
// handed to each field below, and the buttons take the same row.
|
|
let field_height = 26.0
|
|
let field_pad_v = 4.0
|
|
let field_radius = 3.5
|
|
let field_border = 0.5
|
|
|
|
// The room a Fit field takes: where a field is Fit, the padding IS its
|
|
// height.
|
|
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.
|
|
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 takes
|
|
// the height alone: a padding would displace its line.
|
|
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.TextInputFlat)
|
|
field_face(mod.widgets.TextInputFlat.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)
|
|
mod.widgets.NumberField.draw_bg.border_radius = field_radius
|
|
field_frame(mod.widgets.ValueInput)
|
|
field_frame(mod.widgets.DateField)
|
|
field_frame(mod.widgets.TimeField)
|
|
field_frame(mod.widgets.DatePicker)
|
|
field_frame(mod.widgets.DateRangePicker)
|
|
// The chrome-less input a well holds must impose no height of its own.
|
|
mod.widgets.WellInput.min_height = 0.0
|
|
mod.widgets.Window.pass.clear_color = #121a22
|
|
|
|
// Shadows and light leave the layout box: plain views stop clipping so a
|
|
// shadow can fall into the gap under a control. Scroll views still clip.
|
|
mod.widgets.View.clip_x = false
|
|
mod.widgets.View.clip_y = false
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// THE INKS.
|
|
// Shadow ink #03070c; cool white; the accent cyan #56d4f2; the light's pale
|
|
// core #c8f4ff; the lit edge #9ff0ff; the halo blue-cyan #3fb6e0.
|
|
let fp_ink = fn() -> vec3 { return vec3(0.012, 0.027, 0.047) }
|
|
let fp_white = fn() -> vec3 { return vec3(0.94, 0.97, 0.99) }
|
|
let fp_acc = fn() -> vec3 { return vec3(0.337, 0.831, 0.949) }
|
|
let fp_acc_rim = fn() -> vec3 { return vec3(0.784, 0.957, 1.0) }
|
|
let fp_acc_line = fn() -> vec3 { return vec3(0.624, 0.941, 1.0) }
|
|
let fp_acc_glow = fn() -> vec3 { return vec3(0.247, 0.714, 0.878) }
|
|
// Rose, for error and danger only.
|
|
let fp_rose = fn() -> vec3 { return vec3(0.941, 0.416, 0.541) }
|
|
|
|
// Grain that does not repeat: the library's hash nearly repeats every 97
|
|
// cells along an axis, so it is fed a skewed lattice on which no short
|
|
// shift lines up again. Signed, triangular, one value per cell.
|
|
let fp_grain = fn(p: vec2, amount: float) -> float {
|
|
let i = floor(p)
|
|
let q = vec2(i.x + i.y * 0.4142, i.y - i.x * 0.2718)
|
|
return (Finish.hash(q) - Finish.hash(q.yx * 1.3107 + vec2(17.13, 3.71))) * amount
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// THE PLASTIC. Distances in points, negative inside; px is one device pixel.
|
|
|
|
// The box snapped to device pixels, as (lo, hi) relative to the quad's
|
|
// layout origin, so every rim lands on one pixel row.
|
|
let fp_snap = fn(o: vec2, s: vec2, px: float) -> vec4 {
|
|
let lo = vec2(Finish.snap(o.x, px), Finish.snap(o.y, px)) - o
|
|
let hi = vec2(Finish.snap(o.x + s.x, px), Finish.snap(o.y + s.y, px)) - o
|
|
return vec4(lo, hi)
|
|
}
|
|
|
|
// Rounded box with a corner per quadrant: (tl, tr, br, bl).
|
|
let fp_box = fn(p: vec2, c: vec2, h: vec2, rr: vec4) -> float {
|
|
let q = p - c
|
|
var r = rr.x
|
|
if q.x > 0.0 {
|
|
r = rr.y
|
|
if q.y > 0.0 {
|
|
r = rr.z
|
|
}
|
|
} else {
|
|
if q.y > 0.0 {
|
|
r = rr.w
|
|
}
|
|
}
|
|
return Material.sd_box(p, c, h, min(r, min(h.x, h.y)))
|
|
}
|
|
|
|
// Distance to a segment (a zero-length segment is its point).
|
|
let fp_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.000001), 0.0, 1.0)
|
|
return length(pa - ba * t)
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// THE CIRCUIT. One function draws the traces on the window's ground and,
|
|
// softened, behind every frosted face, both at the same window position, so
|
|
// a trace seems to run on under the plastic.
|
|
|
|
// Where the traces gather: the lower half of the window and its corners,
|
|
// stirred by a slow cloud. 0..1. `q` and `ws` in window points.
|
|
let fp_dens = fn(q: vec2, ws: vec2) -> float {
|
|
let s = max(ws, vec2(200.0, 200.0))
|
|
let lower = smoothstep(0.30, 0.90, q.y / s.y)
|
|
let dx = min(q.x, s.x - q.x)
|
|
let dy = min(q.y, s.y - q.y)
|
|
let corner = 1.0 - smoothstep(0.0, 0.34 * min(s.x, s.y), length(vec2(dx, dy)))
|
|
let edge = 1.0 - smoothstep(0.0, 0.10 * s.x, dx)
|
|
let cloud = Finish.fbm(q / 260.0 + vec2(1.7, 4.3))
|
|
let base = max(lower, max(corner, edge * 0.55))
|
|
return clamp(base * 0.85 + (cloud - 0.5) * mix(0.35, 0.9, base) + 0.06, 0.0, 1.0)
|
|
}
|
|
// Whether cell (i, j) of a bus carries its run; the density lowers the bar.
|
|
let fp_cell = fn(i: float, j: float, cw: float, rh: float, seed: float, ws: vec2, vert: float) -> float {
|
|
let cc = vec2((i + 0.5) * cw, (j + 0.5) * rh)
|
|
let q = mix(cc, cc.yx, vert)
|
|
let dn = fp_dens(q, ws)
|
|
return step(mix(0.975, 0.40, dn * dn), Finish.hash(vec2(i * 0.917 + seed * 1.31, j * 1.531 + seed * 0.77 + 7.7)))
|
|
}
|
|
// The lane's height at the left edge of cell i: one of seven steps of 4 points.
|
|
let fp_lane_y = fn(i: float, j: float, rh: float, seed: float) -> float {
|
|
return (j + 0.5) * rh + (floor(Finish.hash(vec2(i * 1.371 + seed, j * 2.113 - seed * 0.53)) * 7.0) - 3.0) * 4.0
|
|
}
|
|
// A run from (x0, yl) to (x1, yr) that changes height by a 45 degree jog at xm.
|
|
let fp_jog = fn(p: vec2, x0: float, x1: float, xm: float, yl: float, yr: float) -> float {
|
|
let dd = abs(yr - yl) * 0.5
|
|
let a = vec2(x0, yl)
|
|
let b = vec2(max(xm - dd, x0), yl)
|
|
let c = vec2(min(xm + dd, x1), yr)
|
|
let e = vec2(x1, yr)
|
|
return min(fp_seg(p, a, b), min(fp_seg(p, b, c), fp_seg(p, c, e)))
|
|
}
|
|
// A bus of one to three parallel traces along u, in rows `rh` tall cut into
|
|
// cells `cw` long; a run ends in ring pads, some cells carry a via. Returns
|
|
// (distance to a line, distance to a ring, brightness, 0).
|
|
let fp_bus = fn(u0: float, v: float, cw: float, rh: float, seed: float, ws: vec2, vert: float) -> vec4 {
|
|
let j = floor(v / rh)
|
|
// Each row's cells are slid by their own amount, so run ends never line
|
|
// up down the page.
|
|
let u = u0 + Finish.hash(vec2(j * 0.37 + seed, 4.2)) * cw
|
|
let i = floor(u / cw)
|
|
let on = fp_cell(i, j, cw, rh, seed, ws, vert)
|
|
let onl = fp_cell(i - 1.0, j, cw, rh, seed, ws, vert)
|
|
let onr = fp_cell(i + 1.0, j, cw, rh, seed, ws, vert)
|
|
let nb = floor(Finish.hash(vec2(j * 0.713 + seed, seed * 1.37 + 2.9)) * 2.99)
|
|
let s = 3.0
|
|
let yl = fp_lane_y(i, j, rh, seed) - nb * s * 0.5
|
|
let yr = fp_lane_y(i + 1.0, j, rh, seed) - nb * s * 0.5
|
|
let x0 = i * cw + mix(4.0, -0.5, onl)
|
|
let x1 = (i + 1.0) * cw - mix(4.0, -0.5, onr)
|
|
let xm = (i + 0.32 + 0.36 * Finish.hash(vec2(i * 3.17 + seed, j * 0.61 + 1.9))) * cw
|
|
let q = vec2(u, v)
|
|
let d0 = fp_jog(q, x0, x1, xm, yl, yr)
|
|
let d1 = mix(99.0, fp_jog(q - vec2(0.0, s), x0, x1, xm, yl, yr), step(0.5, nb))
|
|
let d2 = mix(99.0, fp_jog(q - vec2(0.0, 2.0 * s), x0, x1, xm, yl, yr), step(1.5, nb))
|
|
let dl = mix(99.0, min(d0, min(d1, d2)), on)
|
|
let kl = clamp(floor((v - yl) / s + 0.5), 0.0, nb)
|
|
let pl = abs(length(vec2(u - x0, v - yl - kl * s)) - 1.4) + (1.0 - on * (1.0 - onl)) * 99.0
|
|
let kr = clamp(floor((v - yr) / s + 0.5), 0.0, nb)
|
|
let pr = abs(length(vec2(u - x1, v - yr - kr * s)) - 1.4) + (1.0 - on * (1.0 - onr)) * 99.0
|
|
let vh = Finish.hash(vec2(i * 2.31 + seed, j * 1.77 + 3.3))
|
|
let pv = abs(length(vec2(u - (x1 - 8.0), v - yr)) - 1.2) + (1.0 - on * step(0.70, vh)) * 99.0
|
|
let t = u / cw - i
|
|
let bi = Finish.hash(vec2(i * 0.53 + seed, j * 0.29 + 1.1))
|
|
let bn = Finish.hash(vec2((i + 1.0) * 0.53 + seed, j * 0.29 + 1.1))
|
|
let bp = Finish.hash(vec2((i - 1.0) * 0.53 + seed, j * 0.29 + 1.1))
|
|
let bb = mix(mix(bp, bi, t + 0.5), mix(bi, bn, t - 0.5), step(0.5, t))
|
|
let hot = step(0.80, Finish.hash(vec2(j * 0.37 + seed * 2.1, 8.8)))
|
|
return vec4(dl, min(pl, min(pr, pv)), 0.28 + 0.42 * bb * bb + 0.35 * hot, 0.0)
|
|
}
|
|
// The traces at window point w: (line, ring pads, glow, light spot), each
|
|
// 0..1. `soft` 1 blurs them, as seen through frosted plastic.
|
|
let fp_circuit = fn(w: vec2, ws: vec2, px: float, soft: float) -> vec4 {
|
|
let h = fp_bus(w.x, w.y, 84.0, 56.0, 1.0, ws, 0.0)
|
|
let v = fp_bus(w.y, w.x, 90.0, 64.0, 7.0, ws, 1.0)
|
|
let k = 0.50 + 0.50 * fp_dens(w, ws)
|
|
let hw = 0.42
|
|
let lh = mix(Finish.cover(h.x - hw, px), exp(0.0 - h.x * h.x / 2.2), soft) * h.z
|
|
let lv = mix(Finish.cover(v.x - hw, px), exp(0.0 - v.x * v.x / 2.2), soft) * v.z
|
|
let rh = mix(Finish.cover(h.y - 0.40, px), exp(0.0 - h.y * h.y / 1.6), soft) * h.z
|
|
let rv = mix(Finish.cover(v.y - 0.40, px), exp(0.0 - v.y * v.y / 1.6), soft) * v.z
|
|
let gh = (0.45 * exp(0.0 - h.x / 0.8) + 0.14 * exp(0.0 - h.x / 3.0)) * h.z
|
|
let gv = (0.45 * exp(0.0 - v.x / 0.8) + 0.14 * exp(0.0 - v.x / 3.0)) * v.z
|
|
// Soft spots of light far behind: one in some cells of a loose lattice.
|
|
let bc = floor(w / 110.0)
|
|
let bh = Finish.hash(bc * 1.137 + vec2(2.9, 5.3))
|
|
let bp = (bc + vec2(0.2, 0.2) + 0.6 * vec2(Finish.hash(bc + vec2(7.1, 1.3)), Finish.hash(bc + vec2(3.7, 9.9)))) * 110.0
|
|
let bd = length(w - bp)
|
|
let br = 7.0 + 9.0 * Finish.hash(bc + vec2(5.5, 2.2))
|
|
let spot = exp(0.0 - bd * bd / (br * br)) * step(0.55, bh) * (0.5 + 0.5 * k)
|
|
return vec4(max(lh, lv) * k, max(rh, rv) * k, max(gh, gv) * k, spot)
|
|
}
|
|
// Where a face stands in the window: (window position of the face's own
|
|
// origin, window size), from the pass's projection.
|
|
let fp_wd = fn(world: vec4, p: vec2, a: vec4, b: vec4) -> vec4 {
|
|
let ws = vec2(2.0 / max(abs(b.x - a.x), 0.000001), 2.0 / max(abs(b.y - a.y), 0.000001))
|
|
return vec4(world.xy - p, ws)
|
|
}
|
|
|
|
// The plastic's face down a slab: pale frosted blue-grey, #9fb8ca at the
|
|
// head, #90abbe in the middle, #7d98ac at the foot (T10 reads #8faabd).
|
|
let fp_body = fn(yn: float) -> vec3 {
|
|
let a = mix(vec3(0.624, 0.722, 0.792), vec3(0.561, 0.667, 0.741), smoothstep(0.0, 0.55, yn))
|
|
return mix(a, vec3(0.490, 0.596, 0.675), smoothstep(0.55, 1.0, yn))
|
|
}
|
|
// The frost white alpha a panel's head carries, for the chips and keys.
|
|
let fp_lift = fn(yn: float) -> float {
|
|
return mix(mix(0.20, 0.13, clamp(yn * 2.0, 0.0, 1.0)), 0.09, clamp(yn * 2.0 - 1.0, 0.0, 1.0))
|
|
}
|
|
// The rim's alpha round the edge, as a share of `top`.
|
|
let fp_rim = fn(yn: float, xn: float, top: float) -> float {
|
|
let k = top / 0.60
|
|
let v = mix(0.60, 0.30, smoothstep(0.0, 1.0, yn))
|
|
let side = (1.0 - abs(yn * 2.0 - 1.0)) * (0.0 - 0.03 - 0.06 * xn)
|
|
return (v + side) * k
|
|
}
|
|
// The rim's pale tint, #e0f2fb.
|
|
let fp_rimc = fn() -> vec3 { return vec3(0.878, 0.949, 0.984) }
|
|
|
|
// A slab of frosted plastic on the snapped box b = (lo, hi), premultiplied.
|
|
// It stands up: a top face with a bright rounded head edge, and under it a
|
|
// wall `wt` tall, the thickness of the slab, where the light inside the
|
|
// plastic comes out: pale cyan, brightest at the foot, glowing onto what
|
|
// lies under it. A soft shadow falls below. Pressed, the face sinks into
|
|
// the wall, its head shades and its light dims.
|
|
// face the plastic's strength (1 a button, 0 an outline)
|
|
// amb the shadow's strength
|
|
// rim the rim's strength
|
|
// tone a tint laid over the body, premultiplied (a = its share)
|
|
// edge the colour of the light inside, and cau its strength
|
|
// hero lit from within: a cyan plastic, its edge lit all round
|
|
// lit on: the rim turns cyan all round with a halo
|
|
// wd where the face stands in the window (fp_wd)
|
|
let fp_plate = fn(p: vec2, b: vec4, rr: vec4, px: float, hover: float, down: float, focus: float, disabled: float, face: float, amb: float, rim: float, tone: vec4, rim_col: vec3, edge: vec3, cau: float, hero: float, lit: float, wd: vec4) -> vec4 {
|
|
let lo = b.xy
|
|
let hi = b.zw
|
|
let c = (lo + hi) * 0.5
|
|
let h = (hi - lo) * 0.5
|
|
let d = fp_box(p, c, h, rr)
|
|
let r = min(max(max(rr.x, rr.y), max(rr.z, rr.w)), min(h.x, h.y))
|
|
let live = 1.0 - disabled
|
|
let inside = Finish.cover(d, px)
|
|
let outside = 1.0 - inside
|
|
let hv = hover * (1.0 - down)
|
|
let up = 1.0 - down
|
|
// The wall under the top face; a press sinks the face into it.
|
|
let wt = Finish.snap(min(3.0, h.y * 0.24) * min(face * 1.5, 1.0) * mix(0.55, 1.0, clamp(amb, 0.0, 1.0)), px)
|
|
let sink = wt * 0.8 * down
|
|
let flo = lo + vec2(0.0, sink)
|
|
let fhi = hi - vec2(0.0, wt - sink)
|
|
let fc = (flo + fhi) * 0.5
|
|
let fh = max((fhi - flo) * 0.5, vec2(0.5, 0.5))
|
|
let df = fp_box(p, fc, fh, rr)
|
|
let fin = Finish.cover(df, px)
|
|
let yn = clamp((p.y - flo.y) / max(fhi.y - flo.y, 1.0), 0.0, 1.0)
|
|
// The light inside the plastic.
|
|
let el = (0.62 + 0.45 * hv + 0.55 * hero + 0.35 * lit) * cau * live * (1.0 - 0.65 * down)
|
|
// OUTSIDE. A soft shadow two points down, the dark contact line, the
|
|
// light from the lit foot pooling on the ground below it, and a focus
|
|
// or lit halo all round; each gone within three points to the side.
|
|
let fa = Finish.drop(p, c, h, r, vec2(0.0, mix(2.2, 0.6, down)), mix(2.6, 1.2, down)) * amb * 0.70 * live
|
|
let ct = Finish.ring_out(d, 0.0, px, px) * 0.45 * min(rim, 1.0) * mix(1.0, 0.5, disabled)
|
|
let sh = (1.0 - (1.0 - fa) * (1.0 - ct)) * outside
|
|
var o = vec4(fp_ink() * sh, sh)
|
|
let ug = Finish.drop(p, vec2(c.x, hi.y - 1.5), vec2(max(h.x - 4.0, 1.0), 1.5), 1.5, vec2(0.0, 2.0), 3.8) * el * 2.2 * outside
|
|
let hk = max(focus * 0.80, max(lit * 0.55, hero * 0.50)) * live
|
|
let halo = Finish.drop(p, c, h, r, vec2(0.0, 0.4), 1.5) * hk * outside
|
|
let gl = min(ug + halo, 0.9)
|
|
o = Finish.over(o, vec4(mix(edge, vec3(0.85, 0.97, 1.0), 0.30) * gl, gl))
|
|
// THE TOP FACE: pale frost, lighter above, with what lies behind
|
|
// showing through it softly (the same traces, blurred, a point lower as
|
|
// if bent by the plastic's thickness).
|
|
var col = fp_body(yn)
|
|
col = mix(col, tone.rgb / max(tone.a, 0.001), tone.a)
|
|
col = mix(col, vec3(0.60, 0.85, 0.94), hero * 0.50)
|
|
let wp = wd.xy + p + vec2(0.0, 1.0)
|
|
let cb = fp_circuit(wp, wd.zw, px, 1.0)
|
|
let mott = (Finish.fbm(wp / 16.0) - 0.5) * 0.09
|
|
col = col * (1.0 + mott)
|
|
col = col + vec3(0.20, 0.62, 0.78) * (cb.x * 0.42 + cb.y * 0.30 + cb.z * 0.22) * face
|
|
col = col + vec3(0.45, 0.80, 0.92) * cb.w * 0.24 * face
|
|
col = col * (1.0 + 0.07 * hv - 0.11 * down)
|
|
// The bright rounded head edge: a soft highlight rolling in from the
|
|
// face's head, and a sheen over its upper part.
|
|
let fdep = max(0.0 - df, 0.0)
|
|
let headw = 1.0 - smoothstep(0.15, 0.60, yn)
|
|
col = mix(col, vec3(0.90, 0.96, 0.99), exp(0.0 - fdep / 0.8) * headw * 0.38 * up)
|
|
col = mix(col, vec3(1.0, 1.0, 1.0), (1.0 - smoothstep(0.0, 0.40, yn)) * 0.05 * up)
|
|
// The frame: the slab's rounded shoulder, a band two points wide round
|
|
// a very slightly sunk plateau, paler on the head than down the sides;
|
|
// the plateau's edge is a soft shade on its head side and a pale line
|
|
// on its foot side, as light crossing thick frosted plastic does.
|
|
let bw = min(2.0, fh.y * 0.3)
|
|
let pl = Finish.cover(bw - fdep, px)
|
|
let band = (1.0 - pl) * up * min(face * 1.2, 1.0)
|
|
col = mix(col, vec3(0.80, 0.89, 0.94), band * mix(0.50, 0.22, smoothstep(0.1, 0.8, yn)))
|
|
let pe = Finish.band(fdep - bw, 0.0, px, px) * up * min(face * 1.2, 1.0)
|
|
col = mix(col, vec3(0.36, 0.47, 0.56), pe * headw * 0.35)
|
|
col = mix(col, vec3(0.88, 0.95, 0.99), pe * smoothstep(0.55, 0.9, yn) * 0.40)
|
|
// Light rising into the face from the lit wall below it.
|
|
col = col + edge * exp(0.0 - max(fhi.y - p.y, 0.0) / 3.0) * el * 0.16
|
|
// Pressed: a shadow falls inside from the head.
|
|
let ish = Finish.inset(p, fc, fh, r, vec2(0.0, 1.8), 2.2) * down
|
|
col = mix(col, vec3(0.04, 0.07, 0.10), ish * 0.60)
|
|
// The face's rim: pale on the head, quieter down the sides, a darker
|
|
// line along its foot where it stands over the wall.
|
|
let rb = Finish.band(df, 0.0, px, px)
|
|
let footf = smoothstep(0.55, 0.9, yn)
|
|
let ra = mix(1.0, 0.45, smoothstep(0.1, 0.6, yn)) * (1.0 - footf) * rim * mix(1.0, 0.45, down) * (1.0 + 0.3 * hv)
|
|
col = mix(col, mix(rim_col, vec3(1.0, 1.0, 1.0), 0.5), rb * min(ra, 1.0) * 0.95)
|
|
col = mix(col, vec3(0.30, 0.40, 0.48), rb * footf * 0.40 * step(0.3, wt))
|
|
// THE WALL: plastic seen edge-on, the light inside coming out of it,
|
|
// pale cyan and brightest at the foot.
|
|
let wy = clamp((p.y - fhi.y) / max(hi.y - fhi.y, 0.5), 0.0, 1.0)
|
|
var wc = mix(vec3(0.46, 0.59, 0.68), vec3(0.58, 0.72, 0.80), wy)
|
|
wc = mix(wc, mix(edge, vec3(1.0, 1.0, 1.0), 0.50), min(el * 1.15, 1.0) * mix(0.45, 0.95, smoothstep(0.0, 0.8, wy)))
|
|
wc = mix(wc, vec3(0.08, 0.12, 0.16), step(p.y, fc.y))
|
|
col = mix(col, wc, 1.0 - fin)
|
|
col = mix(col, vec3(0.16, 0.20, 0.24), disabled * 0.78)
|
|
let fa2 = min(face, 1.0) * mix(1.0, 0.60, disabled)
|
|
var f = vec4(col * fa2, fa2)
|
|
// The slab's outline: a thin pale line up the sides, and along the foot
|
|
// the lit core of the edge.
|
|
let ob = Finish.band(d, 0.0, px, px)
|
|
let sidew = (1.0 - footf) * (1.0 - headw)
|
|
f = Finish.over(f, vec4(rim_col * ob * sidew * 0.35 * rim, ob * sidew * 0.35 * rim))
|
|
let core = mix(edge, vec3(1.0, 1.0, 1.0), 0.60)
|
|
f = mix(f, vec4(core, 1.0), ob * smoothstep(0.5, 0.9, (p.y - lo.y) / max(hi.y - lo.y, 1.0)) * min(el * 1.4, 1.0))
|
|
// A focus, a lit state or the hero rings the whole edge in cyan light.
|
|
let fr = ob * max(max(focus, lit * 0.95), hero * 0.85) * live
|
|
f = mix(f, vec4(fp_acc_rim(), 1.0), fr)
|
|
let g = fp_grain(p / px, 0.9 / 255.0) * f.a
|
|
f = vec4(f.rgb + vec3(g, g, g), f.a) * inside
|
|
return Finish.over(o, f)
|
|
}
|
|
|
|
// A recessed well of darker plastic on the shape d: black at `a` over what
|
|
// is under it, a dark pixel along its head, a light pixel along its foot
|
|
// and quieter light up its sides. Premultiplied.
|
|
let fp_well_h = fn(d: float, yn: float, px: float, a: float, foot: float, side: float, hd: float) -> vec4 {
|
|
let inside = Finish.cover(d, px)
|
|
let rb = Finish.band(d, 0.0, px, px)
|
|
let head = rb * (1.0 - smoothstep(0.25, 0.5, yn))
|
|
let low = rb * smoothstep(0.5, 0.75, yn)
|
|
let mid = rb * (1.0 - max(1.0 - smoothstep(0.25, 0.5, yn), smoothstep(0.5, 0.75, yn)))
|
|
var o = vec4(0.0, 0.0, 0.0, a)
|
|
o = Finish.over(o, vec4(0.0, 0.0, 0.0, hd * head))
|
|
let l = foot * low + side * mid
|
|
o = Finish.over(o, vec4(vec3(0.75, 0.88, 0.95) * l, l))
|
|
return o * inside
|
|
}
|
|
let fp_well = fn(d: float, yn: float, px: float, a: float, foot: float, side: float) -> vec4 {
|
|
return fp_well_h(d, yn, px, a, foot, side, 0.4)
|
|
}
|
|
|
|
|
|
// A lit line along the segment a..b, w points across: a near-white core,
|
|
// the saturated cyan body and a halo in two parts, a bright near glow and a
|
|
// soft far one gone by about eight points, lighting what is round it.
|
|
// Premultiplied, laid over `under`.
|
|
let fp_beam = fn(under: vec4, p: vec2, a: vec2, b: vec2, w: float, px: float, k: float, hue: vec3) -> vec4 {
|
|
let d = fp_seg(p, a, b) - w * 0.5
|
|
let dd = max(d, 0.0)
|
|
let hg = min(exp(0.0 - dd / 1.1) * 0.62 + exp(0.0 - dd * dd / 18.0) * 0.30, 0.85) * k * (1.0 - Finish.cover(d, px))
|
|
var o = Finish.over(under, vec4(mix(hue, vec3(0.16, 0.55, 0.80), 0.30) * hg, hg))
|
|
let body = Finish.cover(d, px) * k
|
|
o = Finish.over(o, vec4(mix(hue, vec3(0.75, 0.97, 1.0), 0.25) * body, body))
|
|
let cd = fp_seg(p, a, b) - max(w * 0.5 - 0.9, px * 0.6)
|
|
let cr = Finish.cover(cd, px) * k
|
|
return Finish.over(o, vec4(mix(hue, vec3(1.0, 1.0, 1.0), 0.80) * cr, cr))
|
|
}
|
|
|
|
// A glass bead: a handle, a thumb or a lamp. Its own drop, a pale sphere
|
|
// lit from the upper left with a specular spot, cyan light caught in its
|
|
// lower half and a thin pale rim. Lit, it glows: a white core, a cyan body
|
|
// and a halo round it. Premultiplied, laid over `under`.
|
|
let fp_bead = fn(under: vec4, p: vec2, c: vec2, r: float, px: float, hover: float, down: float, disabled: float, lit: float) -> vec4 {
|
|
let d = length(p - c) - r
|
|
let q = (p - c) / max(r, 0.5)
|
|
let live = 1.0 - disabled
|
|
let outside = 1.0 - Finish.cover(d, px)
|
|
let amb = Finish.drop(p, c, vec2(r, r), r, vec2(0.0, 1.0), 1.2) * 0.45 * live * outside * (1.0 - 0.7 * lit)
|
|
var o = Finish.over(under, vec4(fp_ink() * amb, amb))
|
|
let dd = max(d, 0.0)
|
|
let hg = (exp(0.0 - dd / 1.2) * 0.55 + exp(0.0 - dd * dd / (r * 2.0 + 6.0)) * 0.35) * lit * live * outside
|
|
o = Finish.over(o, vec4(fp_acc_glow() * hg, hg))
|
|
let hv = hover * (1.0 - down)
|
|
let l = length(q)
|
|
var col = mix(vec3(0.86, 0.92, 0.95), vec3(0.42, 0.53, 0.61), smoothstep(-0.9, 0.9, q.y))
|
|
col = col * (1.0 - 0.28 * smoothstep(0.55, 1.0, l))
|
|
col = col + fp_acc() * 0.30 * smoothstep(0.1, 0.9, q.y) * smoothstep(0.35, 0.95, l)
|
|
col = col * (1.0 + 0.06 * hv - 0.10 * down)
|
|
// Lit: the bead is a lamp, white at its core.
|
|
let lamp = mix(fp_acc(), vec3(0.94, 0.99, 1.0), 1.0 - smoothstep(0.0, 0.85, l))
|
|
col = mix(col, lamp, lit * live)
|
|
let spec = exp(0.0 - dot(q - vec2(-0.30, -0.42), q - vec2(-0.30, -0.42)) / 0.05) * 0.85 * (1.0 - 0.5 * lit)
|
|
col = mix(col, vec3(1.0, 1.0, 1.0), spec)
|
|
let rb = Finish.band(d, 0.0, px, px)
|
|
col = mix(col, mix(vec3(0.92, 0.97, 1.0), fp_acc_rim(), smoothstep(-0.2, 0.8, q.y)), rb * 0.6)
|
|
col = mix(col, vec3(0.30, 0.35, 0.40), disabled * 0.6)
|
|
let a = Finish.cover(d, px)
|
|
return Finish.over(o, vec4(col * a, a))
|
|
}
|
|
|
|
// The wait spinner a replaced face draws itself: a pale cyan comet turning
|
|
// once a second on a ring where the label was.
|
|
let fp_wait = fn(under: vec4, p: vec2, size: vec2, px: float, loading: float, t: float) -> vec4 {
|
|
let c = size * 0.5
|
|
let r = max(min(size.x, size.y) * 0.5 - 6.0, 3.0)
|
|
let q = p - c
|
|
let ring = abs(length(q) - r)
|
|
let a = fract(atan2(q.y, q.x) / 6.2831853 - t)
|
|
let body = Finish.cover(ring - 0.75, px) * a * a * loading
|
|
let track = Finish.cover(ring - 0.5, px) * 0.15 * loading
|
|
let o = Finish.over(under, vec4(fp_white() * track, track))
|
|
return mix(o, vec4(fp_acc_rim(), 1.0), body)
|
|
}
|
|
|
|
// A plastic button: the slab on the snapped box.
|
|
let fp_button = fn(p: vec2, o: vec2, s: vec2, px: float, rr: vec4, hover: float, down: float, focus: float, disabled: float, face: float, amb: float, rim: float, edge: vec3, hero: float, wd: vec4) -> vec4 {
|
|
let b = fp_snap(o, s, px)
|
|
return fp_plate(p, b, rr, px, hover, down, focus, disabled, face, amb, rim, vec4(0.0, 0.0, 0.0, 0.0), fp_rimc(), edge, 1.0, hero, 0.0, wd)
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// BUTTONS. Slabs 26 points tall, corners of 3.5. The drop and the light
|
|
// fall outside the box: the quad grows by `glow_room` and the button stops
|
|
// clipping.
|
|
mod.widgets.ButtonFlat.clip_x = false
|
|
mod.widgets.ButtonFlat.clip_y = false
|
|
mod.widgets.ButtonFlat.padding = mod.turtle.Inset{left: 12.0 right: 12.0 top: 4.0 bottom: 4.0}
|
|
mod.widgets.ButtonFlat.icon_walk = Walk{width: 14.0, height: Fit}
|
|
mod.widgets.ButtonFlat.min_height = 26.0
|
|
mod.widgets.ButtonXs.min_height = 0.0
|
|
mod.widgets.ButtonSm.min_height = 0.0
|
|
mod.widgets.ButtonIcon.padding = mod.turtle.Inset{left: 8.0 right: 8.0 top: 4.0 bottom: 4.0}
|
|
mod.widgets.ButtonFlatIcon.padding = mod.turtle.Inset{left: 8.0 right: 8.0 top: 4.0 bottom: 4.0}
|
|
mod.widgets.ButtonFlatterIcon.padding = mod.turtle.Inset{left: 8.0 right: 8.0 top: 4.0 bottom: 4.0}
|
|
mod.widgets.ButtonFlat.draw_bg.border_radius = uniform(3.5)
|
|
mod.widgets.ButtonFlat.draw_bg.glow_room = uniform(6.0)
|
|
mod.widgets.ButtonFlat.draw_bg.corners = fn() -> vec4 {
|
|
let r = self.border_radius
|
|
return vec4(
|
|
mix(r, self.border_radius_tl, step(0.0, self.border_radius_tl)),
|
|
mix(r, self.border_radius_tr, step(0.0, self.border_radius_tr)),
|
|
mix(r, self.border_radius_br, step(0.0, self.border_radius_br)),
|
|
mix(r, self.border_radius_bl, step(0.0, self.border_radius_bl))
|
|
)
|
|
}
|
|
mod.widgets.ButtonFlat.draw_bg.vertex = fn() {
|
|
let m = self.glow_room
|
|
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 flat button lies on the ground: the slab without its drop. The size
|
|
// ladder wears it too.
|
|
mod.widgets.ButtonFlat.draw_bg.pixel = fn() {
|
|
let m = self.glow_room
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let wd = fp_wd(self.world, p, self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(0.0, 0.0, 0.0, 1.0)), self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(1.0, 1.0, 0.0, 1.0)))
|
|
let f = fp_button(p, self.rect_pos, self.rect_size, px, self.corners(), self.hover, self.down, self.focus, self.disabled, 0.85, 0.3, 0.9, fp_acc_line(), 0.0, wd)
|
|
return self.host_over(fp_wait(f, p, self.rect_size, px, self.loading, self.anim_time), m)
|
|
}
|
|
// The standard button: the whole slab.
|
|
mod.widgets.Button.draw_bg.pixel = fn() {
|
|
let m = self.glow_room
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let wd = fp_wd(self.world, p, self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(0.0, 0.0, 0.0, 1.0)), self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(1.0, 1.0, 0.0, 1.0)))
|
|
let f = fp_button(p, self.rect_pos, self.rect_size, px, self.corners(), self.hover, self.down, self.focus, self.disabled, 1.0, 1.0, 1.0, fp_acc_line(), 0.0, wd)
|
|
return self.host_over(fp_wait(f, p, self.rect_size, px, self.loading, self.anim_time), m)
|
|
}
|
|
// The one hero: the slab lit from within, cyan through its body.
|
|
mod.widgets.ButtonPrimary.draw_bg.pixel = fn() {
|
|
let m = self.glow_room
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let wd = fp_wd(self.world, p, self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(0.0, 0.0, 0.0, 1.0)), self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(1.0, 1.0, 0.0, 1.0)))
|
|
let f = fp_button(p, self.rect_pos, self.rect_size, px, self.corners(), self.hover, self.down, self.focus, self.disabled, 1.0, 1.0, 1.1, fp_acc_line(), 1.0, wd)
|
|
return self.host_over(fp_wait(f, p, self.rect_size, px, self.loading, self.anim_time), m)
|
|
}
|
|
// The second voice: the standard slab.
|
|
mod.widgets.ButtonSecondary.draw_bg.pixel = fn() {
|
|
let m = self.glow_room
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let wd = fp_wd(self.world, p, self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(0.0, 0.0, 0.0, 1.0)), self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(1.0, 1.0, 0.0, 1.0)))
|
|
let f = fp_button(p, self.rect_pos, self.rect_size, px, self.corners(), self.hover, self.down, self.focus, self.disabled, 1.0, 1.0, 1.0, fp_acc_line(), 0.0, wd)
|
|
return self.host_over(fp_wait(f, p, self.rect_size, px, self.loading, self.anim_time), m)
|
|
}
|
|
// Tonal: thinner plastic, a lighter drop.
|
|
mod.widgets.ButtonTertiary.draw_bg.pixel = fn() {
|
|
let m = self.glow_room
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let wd = fp_wd(self.world, p, self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(0.0, 0.0, 0.0, 1.0)), self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(1.0, 1.0, 0.0, 1.0)))
|
|
let f = fp_button(p, self.rect_pos, self.rect_size, px, self.corners(), self.hover, self.down, self.focus, self.disabled, 0.8, 0.6, 0.85, fp_acc_line(), 0.0, wd)
|
|
return self.host_over(fp_wait(f, p, self.rect_size, px, self.loading, self.anim_time), m)
|
|
}
|
|
// Danger: the standard slab with rose light inside it.
|
|
mod.widgets.ButtonDanger.draw_bg.pixel = fn() {
|
|
let m = self.glow_room
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let wd = fp_wd(self.world, p, self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(0.0, 0.0, 0.0, 1.0)), self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(1.0, 1.0, 0.0, 1.0)))
|
|
let f = fp_button(p, self.rect_pos, self.rect_size, px, self.corners(), self.hover, self.down, self.focus, self.disabled, 1.0, 1.0, 1.0, fp_rose(), 0.0, wd)
|
|
return self.host_over(fp_wait(f, p, self.rect_size, px, self.loading, self.anim_time), m)
|
|
}
|
|
// Outline: the rim and the lit edge alone over a faint film; the hand
|
|
// fills it in.
|
|
mod.widgets.ButtonOutline.draw_bg.pixel = fn() {
|
|
let m = self.glow_room
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let wd = fp_wd(self.world, p, self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(0.0, 0.0, 0.0, 1.0)), self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(1.0, 1.0, 0.0, 1.0)))
|
|
let face = 0.18 + 0.5 * max(self.hover, self.down)
|
|
let f = fp_button(p, self.rect_pos, self.rect_size, px, self.corners(), self.hover, self.down, self.focus, self.disabled, face, 0.0, 1.2, fp_acc_line(), 0.0, wd)
|
|
return self.host_over(fp_wait(f, p, self.rect_size, px, self.loading, self.anim_time), m)
|
|
}
|
|
// Flatter: a thin frosted film at rest, lying flat without a wall or a
|
|
// shadow (it shares the flat button's dark ink); the slab rises with the
|
|
// pointer.
|
|
mod.widgets.ButtonFlatter.draw_bg.pixel = fn() {
|
|
let m = self.glow_room
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let wd = fp_wd(self.world, p, self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(0.0, 0.0, 0.0, 1.0)), self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(1.0, 1.0, 0.0, 1.0)))
|
|
let k = max(max(self.hover, self.down), self.focus)
|
|
let b = fp_snap(self.rect_pos, self.rect_size, px)
|
|
let f = fp_plate(p, b, self.corners(), px, self.hover, self.down, self.focus, self.disabled, 0.66 + 0.34 * max(self.hover, self.down), 0.5 * k, 0.5 + 0.5 * k, vec4(0.0, 0.0, 0.0, 0.0), fp_rimc(), fp_acc_line(), 0.3 + 0.7 * k, 0.0, 0.0, wd)
|
|
return self.host_over(fp_wait(f, p, self.rect_size, px, self.loading, self.anim_time), m)
|
|
}
|
|
|
|
// Button inks. The faces are pale frosted plastic, so their words are a
|
|
// deep blue-grey (#13212d reads 6.6 on the #8faabd middle, 5.4 on the
|
|
// #7d98ac foot), darker under the hand; the primary's pale cyan plastic
|
|
// takes a deep sea blue, the danger slab a deep wine. The outline has no
|
|
// plastic at rest and keeps the near-white. The flatter button shares the
|
|
// flat one's ink, so it lies as a thin frosted film even at rest.
|
|
let fp_dark_ink = fn(t) {
|
|
t.color = #13212d
|
|
t.color_hover = #0b1720
|
|
t.color_down = #0b1720
|
|
t.color_focus = #0b1720
|
|
t.color_disabled = #6a7885
|
|
}
|
|
fp_dark_ink(mod.widgets.ButtonFlat.draw_text)
|
|
fp_dark_ink(mod.widgets.ButtonSecondary.draw_text)
|
|
fp_dark_ink(mod.widgets.ButtonTertiary.draw_text)
|
|
fp_dark_ink(mod.widgets.ButtonPrimary.draw_text)
|
|
mod.widgets.ButtonPrimary.draw_text.color = #0c2230
|
|
mod.widgets.ButtonPrimary.draw_icon.color = #0c2230
|
|
fp_dark_ink(mod.widgets.ButtonDanger.draw_text)
|
|
mod.widgets.ButtonDanger.draw_text.color = #3a1020
|
|
mod.widgets.ButtonDanger.draw_text.color_hover = #2c0a17
|
|
mod.widgets.ButtonDanger.draw_text.color_down = #2c0a17
|
|
mod.widgets.ButtonDanger.draw_text.color_focus = #2c0a17
|
|
mod.widgets.ButtonDanger.draw_icon.color = #3a1020
|
|
mod.widgets.ButtonSecondary.draw_icon.color = #13212d
|
|
mod.widgets.ButtonTertiary.draw_icon.color = #13212d
|
|
mod.widgets.ButtonOutline.draw_text.color = #eef6fb
|
|
mod.widgets.ButtonOutline.draw_text.color_hover = #ffffff
|
|
mod.widgets.ButtonOutline.draw_text.color_down = #ffffff
|
|
mod.widgets.ButtonOutline.draw_text.color_focus = #eef6fb
|
|
mod.widgets.ButtonOutline.draw_text.color_disabled = #5d6b78
|
|
mod.widgets.ButtonPrimary.draw_bg.loading_color = #c8f4ff
|
|
mod.widgets.ButtonFlat.draw_icon.color = #13212d
|
|
mod.widgets.ButtonOutline.draw_icon.color = #e2edf4
|
|
// The faces built before the sheet (the select's) by their own path.
|
|
fp_dark_ink(mod.widgets.Select.face.draw_text)
|
|
fp_dark_ink(mod.widgets.MultiSelect.face.draw_text)
|
|
|
|
// The glint on a top rim: full over the middle 35 % of the width.
|
|
let fp_glint = fn(xn: float) -> float {
|
|
let e = clamp((abs(xn - 0.5) - 0.175) / 0.25, 0.0, 1.0)
|
|
return 0.5 + 0.5 * cos(3.14159265 * e)
|
|
}
|
|
|
|
// Templates that draw a face of their own on top of these roots keep the
|
|
// plain quad: the grown one is only for the faces written here.
|
|
mod.widgets.CopyButton.draw_bg.vertex = fn() {
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos, self.rect_size)
|
|
}
|
|
mod.widgets.ButtonFloating.draw_bg.vertex = fn() {
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos, self.rect_size)
|
|
}
|
|
mod.widgets.ButtonFloatingSm.draw_bg.vertex = fn() {
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos, self.rect_size)
|
|
}
|
|
mod.widgets.CheckBoxCustom.draw_bg.vertex = fn() {
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos, self.rect_size)
|
|
}
|
|
mod.widgets.glass.CutButton.draw_bg.vertex = fn() {
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos, self.rect_size)
|
|
}
|
|
mod.widgets.glass.RadioButton.draw_bg.vertex = fn() {
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos, self.rect_size)
|
|
}
|
|
mod.widgets.glass.Toggle.draw_bg.vertex = fn() {
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos, self.rect_size)
|
|
}
|
|
mod.widgets.glass.Slider.draw_bg.vertex = fn() {
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos, self.rect_size)
|
|
}
|
|
mod.widgets.glass.TextInput.draw_bg.vertex = fn() {
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos, self.rect_size)
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// CHECK BOXES AND RADIOS. A 15 point cube of the plastic, corners of 3, or
|
|
// a disc. On, the cube is lit from within: cyan through its body, its rim
|
|
// cyan all round, and a white tick with a little bloom. Mixed is a short lit
|
|
// bar; error turns the light inside rose.
|
|
let fp_check = fn(p: vec2, o: vec2, rs: vec2, px: float, sz: float, r: float, at_end: float, hover: float, down: float, focus: float, active: float, mixed: float, err: float, disabled: float, wd: vec4) -> vec4 {
|
|
let x0 = (rs.x - sz) * at_end
|
|
let y0 = rs.y * 0.5 - sz * 0.5
|
|
let lo = vec2(Finish.snap(o.x + x0, px), Finish.snap(o.y + y0, px)) - o
|
|
let hi = lo + vec2(Finish.snap(sz, px), Finish.snap(sz, px))
|
|
let on = max(active, mixed)
|
|
let tk = max(active, mixed * 0.6) * (1.0 - err)
|
|
let tone = vec4(vec3(0.20, 0.50, 0.62) * 0.75 * tk, 0.75 * tk)
|
|
let edge = mix(fp_acc_line(), fp_rose(), err)
|
|
let f = fp_plate(p, vec4(lo, hi), vec4(r, r, r, r), px, hover, down, focus, disabled, 1.0, 0.8, 1.0, tone, fp_rimc(), edge, mix(0.55, 1.0, max(on, err)), 0.0, on * (1.0 - err) * 0.8, wd)
|
|
let q = (p - lo) / sz
|
|
let c = (lo + hi) * 0.5
|
|
let live = mix(1.0, 0.45, disabled)
|
|
// The tick: 1.6 points, round ends, white with a cyan bloom.
|
|
let tick = min(fp_seg(q, vec2(0.27, 0.52), vec2(0.44, 0.69)), fp_seg(q, vec2(0.44, 0.69), vec2(0.75, 0.33))) * sz
|
|
let tb = exp(0.0 - max(tick - 0.8, 0.0) * max(tick - 0.8, 0.0) / 1.2) * 0.45 * active * (1.0 - mixed) * live
|
|
var g = Finish.over(f, vec4(fp_acc() * tb, tb))
|
|
let ta = Finish.cover(tick - 0.8, px) * active * (1.0 - mixed) * live
|
|
g = Finish.over(g, vec4(ta, ta, ta, ta))
|
|
// Mixed: a lit bar 7 by 2.
|
|
return fp_beam(g, p, c - vec2(3.0, 0.0), c + vec2(3.0, 0.0), 2.0, px, mixed * live, fp_acc())
|
|
}
|
|
|
|
mod.widgets.CheckBoxFlat.clip_x = false
|
|
mod.widgets.CheckBoxFlat.clip_y = false
|
|
mod.widgets.CheckBoxFlat.label_walk.margin.left = 14.0
|
|
mod.widgets.CheckBoxFlat.draw_bg.size = uniform(15.0)
|
|
mod.widgets.CheckBoxFlat.draw_bg.border_radius = uniform(3.0)
|
|
mod.widgets.CheckBoxFlat.draw_bg.vertex = fn() {
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(6.0, 6.0), self.rect_size + vec2(12.0, 12.0))
|
|
}
|
|
mod.widgets.CheckBoxFlat.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * (self.rect_size + vec2(12.0, 12.0)) - vec2(6.0, 6.0)
|
|
let wd = fp_wd(self.world, p, self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(0.0, 0.0, 0.0, 1.0)), self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(1.0, 1.0, 0.0, 1.0)))
|
|
return fp_check(p, self.rect_pos, self.rect_size, px, self.size, self.border_radius, self.mark_at_end, self.hover, self.down, self.focus, self.active, self.mixed, self.error, self.disabled, wd)
|
|
}
|
|
mod.widgets.CheckBoxCircle.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * (self.rect_size + vec2(12.0, 12.0)) - vec2(6.0, 6.0)
|
|
let wd = fp_wd(self.world, p, self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(0.0, 0.0, 0.0, 1.0)), self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(1.0, 1.0, 0.0, 1.0)))
|
|
return fp_check(p, self.rect_pos, self.rect_size, px, self.size, self.size * 0.5, self.mark_at_end, self.hover, self.down, self.focus, self.active, self.mixed, self.error, self.disabled, wd)
|
|
}
|
|
|
|
// RADIOS. The plastic as a disc; on, a lamp bead 8 points across glows in
|
|
// its middle and the rim takes the cyan light.
|
|
mod.widgets.RadioButtonFlat.clip_x = false
|
|
mod.widgets.RadioButtonFlat.clip_y = false
|
|
mod.widgets.RadioButtonFlat.label_walk.margin.left = 14.0
|
|
mod.widgets.RadioButtonFlat.draw_bg.size = uniform(15.0)
|
|
mod.widgets.RadioButtonFlat.draw_bg.vertex = fn() {
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(6.0, 6.0), self.rect_size + vec2(12.0, 12.0))
|
|
}
|
|
mod.widgets.RadioButtonFlat.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * (self.rect_size + vec2(12.0, 12.0)) - vec2(6.0, 6.0)
|
|
let wd = fp_wd(self.world, p, self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(0.0, 0.0, 0.0, 1.0)), self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(1.0, 1.0, 0.0, 1.0)))
|
|
let o = self.rect_pos
|
|
let sz = self.size
|
|
let lo = vec2(Finish.snap(o.x, px), Finish.snap(o.y + self.rect_size.y * 0.5 - sz * 0.5, px)) - o
|
|
let hi = lo + vec2(Finish.snap(sz, px), Finish.snap(sz, px))
|
|
let on = self.active
|
|
let r = sz * 0.5
|
|
let f = fp_plate(p, vec4(lo, hi), vec4(r, r, r, r), px, self.hover, self.down, self.focus, self.disabled, 1.0, 0.8, 1.0, vec4(0.0, 0.0, 0.0, 0.0), fp_rimc(), fp_acc_line(), mix(0.55, 0.9, on), 0.0, on * 0.6, wd)
|
|
let c = (lo + hi) * 0.5
|
|
// A socket ring round the lamp bead, which is dark glass off and lit on.
|
|
let sd = abs(length(p - c) - 4.6) - 0.6
|
|
let sa = Finish.cover(sd, px) * 0.30
|
|
let g = Finish.over(f, vec4(0.0, 0.0, 0.0, sa))
|
|
return fp_bead(g, p, c, 4.0, px, self.hover, self.down, self.disabled, on)
|
|
}
|
|
|
|
// TOGGLES. A plastic slab 28 by 16 with corners of 4 and a dark slot sunk
|
|
// in it; the thumb is a glass bead that rides the slot and, on, glows as a
|
|
// lamp, lighting the slot cyan round it.
|
|
mod.widgets.ToggleFlat.label_walk.margin.left = 28.0
|
|
mod.widgets.ToggleFlat.draw_bg.size = uniform(16.0)
|
|
mod.widgets.ToggleFlat.draw_bg.pill_aspect = uniform(1.75)
|
|
mod.widgets.ToggleFlat.draw_bg.vertex = fn() {
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(6.0, 6.0), self.rect_size + vec2(12.0, 12.0))
|
|
}
|
|
mod.widgets.ToggleFlat.clip_x = false
|
|
mod.widgets.ToggleFlat.clip_y = false
|
|
mod.widgets.ToggleFlat.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * (self.rect_size + vec2(12.0, 12.0)) - vec2(6.0, 6.0)
|
|
let wd = fp_wd(self.world, p, self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(0.0, 0.0, 0.0, 1.0)), self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(1.0, 1.0, 0.0, 1.0)))
|
|
let o = self.rect_pos
|
|
let sz = vec2(self.size * self.pill_aspect, self.size)
|
|
let x0 = (self.rect_size.x - sz.x) * self.mark_at_end
|
|
let lo = vec2(Finish.snap(o.x + x0, px), Finish.snap(o.y + self.rect_size.y * 0.5 - sz.y * 0.5, px)) - o
|
|
let hi = lo + vec2(Finish.snap(sz.x, px), Finish.snap(sz.y, px))
|
|
let c = (lo + hi) * 0.5
|
|
let live = 1.0 - self.disabled
|
|
let on = self.active
|
|
var f = fp_plate(p, vec4(lo, hi), vec4(4.0, 4.0, 4.0, 4.0), px, self.hover, self.down, self.focus, self.disabled, 1.0, 0.8, 1.0, vec4(0.0, 0.0, 0.0, 0.0), fp_rimc(), fp_acc_line(), mix(0.5, 1.0, on), 0.0, on * 0.8, wd)
|
|
// The slot: 3 points in, dark plastic, lit cyan along the way the bead
|
|
// has come when on.
|
|
let sh = vec2(sz.x * 0.5 - 2.5, sz.y * 0.5 - 2.5)
|
|
let sd = Material.sd_box(p, c, sh, 2.5)
|
|
let yn = clamp((p.y - c.y + sh.y) / max(2.0 * sh.y, 1.0), 0.0, 1.0)
|
|
f = Finish.over(f, fp_well_h(sd, yn, px, 0.55 - 0.05 * self.hover, 0.20, 0.10, 0.55))
|
|
let t = mix(on, self.drag_pos, self.drag)
|
|
let kr = sz.y * 0.5 - 3.0
|
|
let kc = vec2(mix(lo.x + sz.y * 0.5, hi.x - sz.y * 0.5, t), c.y)
|
|
let pool = exp(0.0 - length(p - kc) / 6.0) * 0.85 * on * live * Finish.cover(sd, px)
|
|
f = Finish.over(f, vec4(fp_acc() * pool, pool))
|
|
f = fp_bead(f, p, kc, kr * (1.0 - 0.06 * self.down), px, self.hover, self.down, self.disabled, on)
|
|
return f * mix(1.0, 0.5, self.disabled)
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SLIDERS AND FADERS. Every slider here, lying or standing, is one drop: a
|
|
// drop of the cyan light the controls glow with, lying on a slot of dark
|
|
// plastic 4 points wide: round at rest, it lights the ground round it and
|
|
// throws a soft shadow; the value is a lit line in the slot with a halo of
|
|
// its own beside the drop. A press spreads the drop and settles it a little
|
|
// lower; the pointer over it lifts its glow. Drawn by the cap engine `Cap`
|
|
// as the slider bench draws its liquid neon drop, in this sheet's accent on
|
|
// this sheet's ground. The fader lays it along its whole box, either way
|
|
// up; the bar, minimal and round sliders lay it along their own track, under
|
|
// or beside their label and value. Every face is a SliderMinimal underneath,
|
|
// so the drop, its material and its inks are set once, on SliderMinimal,
|
|
// and each face says only where its track lies.
|
|
//
|
|
// The drop is 14 points and stops 6 points in from its track's ends on
|
|
// every face, and the drag divides by that same travel, so the finger and
|
|
// the drop arrive together. The cap is a liquid: its tail follows the head
|
|
// on a viscous spring, a press spreads it, a jump sends it travelling.
|
|
mod.widgets.SliderMinimal.cap_size = 14.0
|
|
mod.widgets.SliderMinimal.track_inset = 6.0
|
|
mod.widgets.SliderMinimal.cap_viscosity = 0.5
|
|
mod.widgets.SliderFlat.cap_size = 14.0
|
|
mod.widgets.SliderFlat.track_inset = 6.0
|
|
mod.widgets.SliderFlat.cap_viscosity = 0.5
|
|
mod.widgets.SliderRoundFlat.cap_size = 14.0
|
|
mod.widgets.SliderRoundFlat.track_inset = 6.0
|
|
mod.widgets.SliderRoundFlat.cap_viscosity = 0.5
|
|
mod.widgets.SliderFader.cap_size = 14.0
|
|
mod.widgets.SliderFader.track_inset = 6.0
|
|
mod.widgets.SliderFader.cap_viscosity = 0.5
|
|
mod.widgets.SliderMinimal.draw_bg.track_size = uniform(4.0)
|
|
mod.widgets.SliderFader.draw_bg.track_size = uniform(4.0)
|
|
// The glow, the shadow and the lit value's halo reach far past the box:
|
|
// each face's quad grows by `fp_fader_margin` and stops clipping.
|
|
mod.widgets.SliderMinimal.clip_x = false
|
|
mod.widgets.SliderMinimal.clip_y = false
|
|
mod.widgets.SliderFlat.clip_x = false
|
|
mod.widgets.SliderFlat.clip_y = false
|
|
mod.widgets.SliderRoundFlat.clip_x = false
|
|
mod.widgets.SliderRoundFlat.clip_y = false
|
|
mod.widgets.SliderFader.clip_x = false
|
|
mod.widgets.SliderFader.clip_y = false
|
|
// The drop's material, number for number as the bench tuned it and here to
|
|
// be tuned live: the key light (screen space, from the upper left) and its
|
|
// intensity; the shine; the occlusion and the sky reflection (and how near
|
|
// the eye is for it); how strongly the body emits its own light; the
|
|
// exposure in stops and the highlight roll-off; the cast shadow, the
|
|
// contact shadow round the foot and the slot's inner shadow. `cap_height`
|
|
// is the drop's height in points.
|
|
mod.widgets.SliderMinimal.draw_bg.cap_light = uniform(vec3(-0.3, -0.58, 0.78))
|
|
mod.widgets.SliderMinimal.draw_bg.cap_light_intensity = uniform(0.6)
|
|
mod.widgets.SliderMinimal.draw_bg.cap_specular = uniform(0.5)
|
|
mod.widgets.SliderMinimal.draw_bg.cap_roughness = uniform(0.35)
|
|
mod.widgets.SliderMinimal.draw_bg.cap_gloss = uniform(0.3)
|
|
mod.widgets.SliderMinimal.draw_bg.cap_rim = uniform(0.3)
|
|
mod.widgets.SliderMinimal.draw_bg.cap_ao = uniform(0.55)
|
|
mod.widgets.SliderMinimal.draw_bg.cap_ao_reach = uniform(1.0)
|
|
mod.widgets.SliderMinimal.draw_bg.cap_reflection = uniform(0.15)
|
|
mod.widgets.SliderMinimal.draw_bg.cap_reflection_perspective = uniform(0.35)
|
|
mod.widgets.SliderMinimal.draw_bg.cap_metal = uniform(0.0)
|
|
mod.widgets.SliderMinimal.draw_bg.cap_spun = uniform(0.0)
|
|
mod.widgets.SliderMinimal.draw_bg.cap_glow = uniform(0.9)
|
|
mod.widgets.SliderMinimal.draw_bg.cap_exposure = uniform(0.0)
|
|
mod.widgets.SliderMinimal.draw_bg.cap_rolloff = uniform(0.6)
|
|
mod.widgets.SliderMinimal.draw_bg.cap_shadow = uniform(0.5)
|
|
mod.widgets.SliderMinimal.draw_bg.cap_shadow_blur = uniform(8.0)
|
|
mod.widgets.SliderMinimal.draw_bg.cap_contact = uniform(0.45)
|
|
mod.widgets.SliderMinimal.draw_bg.cap_inner = uniform(0.62)
|
|
mod.widgets.SliderMinimal.draw_bg.cap_height = uniform(8.0)
|
|
// The inks. The drop, its glow and the value are the accent the buttons and
|
|
// toggles light with; the ground is the window's, and the slot is this
|
|
// sheet's groove, black at half over that ground. The light and the shade
|
|
// are the bench's own: this sheet names no material inks, and the base
|
|
// theme's black shade would blacken the drop's shaded side.
|
|
mod.widgets.SliderMinimal.draw_bg.cap_body = uniform(theme.color_ctrl_selected)
|
|
mod.widgets.SliderMinimal.draw_bg.cap_fill = uniform(theme.color_val)
|
|
mod.widgets.SliderMinimal.draw_bg.cap_ground = uniform(theme.color_bg_app)
|
|
mod.widgets.SliderMinimal.draw_bg.cap_track = uniform(#090d11)
|
|
mod.widgets.SliderMinimal.draw_bg.cap_light_ink = uniform(#ffffff)
|
|
mod.widgets.SliderMinimal.draw_bg.cap_shadow_ink = uniform(#1c1c24)
|
|
// How far a fader's quad reaches past its box: as far as the drop's glow
|
|
// and shadow and the lit value's halo still move the ground by a level of
|
|
// 255, less the room the box already gives them. The glow, 0.9 of the
|
|
// body's ink times its strength times exp(-d / 0.8r), is under a level
|
|
// past 0.8r ln(275 strength) from the drop's outline, which a press
|
|
// spreads 8% and the pointer lifts a fifth; the shadow ends at the outline
|
|
// thrown by the light, plus its blur; the value's far halo, 0.09
|
|
// exp(-d / max(4 tw, 5)), after about ln 23 of its falloffs from the slot.
|
|
// The vertex and the pixel stage each pass it what they read of the
|
|
// fader: a draw method that reads `self` is compiled once, for the vertex
|
|
// stage, and the pixel stage reads its fields as zeros.
|
|
let fp_fader_margin = fn(across: float, r: float, inset: float, tw: float, key: vec3, height: float, glow_k: float, blur: float) -> float {
|
|
let l = normalize(key)
|
|
let thrown = length(l.xy) / max(l.z, 0.25) * height * 0.7
|
|
let glow_reach = r * (1.08 + 0.8 * log(max(275.0 * glow_k, 1.0)))
|
|
let shadow_reach = r + thrown + blur
|
|
let drop_reach = max(glow_reach, shadow_reach) - min(inset + r, across * 0.5)
|
|
let halo_reach = tw * 0.5 + max(tw * 4.0, 5.0) * 3.14 - min(inset + tw * 0.5, across * 0.5)
|
|
return max(max(drop_reach, halo_reach), 1.0)
|
|
}
|
|
// THE DROP along a track: `ab` is the point in the track's own frame, `a`
|
|
// along it from the value's zero (up the screen when it stands) and `b`
|
|
// across it, `along` the track's length, `mid` its centre line across,
|
|
// `vert` 1 when the track stands. Called by the pixel stage only.
|
|
mod.widgets.SliderMinimal.draw_bg.fp_drop = fn(ab: vec2, along: float, mid: float, vert: float, px: float) -> vec4 {
|
|
let live = 1.0 - self.disabled
|
|
// Disabled, the light inside goes down to a third, as the lit line's did.
|
|
let lamp = mix(1.0, 0.35, self.disabled)
|
|
let hv = max(self.hover, self.drag)
|
|
let travel = max(along - self.inset_px * 2.0 - self.cap_px, 1.0)
|
|
let foot = self.inset_px + self.cap_px * 0.5
|
|
let at = foot + self.slide_pos * travel
|
|
let l = normalize(self.cap_light)
|
|
let lt = Cap.light(l, vert)
|
|
let ground = self.cap_ground.rgb
|
|
let body = self.cap_body.rgb
|
|
let fill = self.cap_fill.rgb * lamp
|
|
let light_ink = self.cap_light_ink.rgb
|
|
let shadow_ink = self.cap_shadow_ink.rgb
|
|
// THE SLOT, its ends round, and the value lit in it: from the track's
|
|
// own end when it starts at the stop, to its other end when it reaches
|
|
// that one.
|
|
let tw = self.track_size
|
|
let tlen = max(along - self.inset_px * 2.0, 1.0)
|
|
let tbox = vec4(self.inset_px + tlen * 0.5, mid, tlen * 0.5, tw * 0.5)
|
|
var lo = foot + self.fill_lo * travel
|
|
var hi = foot + self.fill_hi * travel
|
|
if self.fill_lo < 0.0005 {
|
|
lo = self.inset_px
|
|
}
|
|
if self.fill_hi > 0.9995 {
|
|
hi = along - self.inset_px
|
|
}
|
|
let r = self.cap_px * 0.5
|
|
let hs = vec2(r, r)
|
|
var col = Cap.slot(ground, ab, tbox, tw * 0.5, 2.0, lo, hi, at, r, lt, px, 0.0, 0.0, self.cap_track.rgb, fill, light_ink, shadow_ink, self.cap_inner, self.cap_ao)
|
|
// THE DROP, round at rest, as long as the cap the drag divides by. A
|
|
// press spreads it 8% and lowers it by a seventh, as the bench's does.
|
|
let height = self.cap_height * (1.0 - 0.14 * self.drag)
|
|
let shape = vec4(1.0, 1.0, 0.02, height)
|
|
let q = ab - vec2(at, mid)
|
|
let fp = Cap.sd_drop(q, r, self.cap_stretch, vec2(self.cap_dir, 0.0), self.drag, self.cap_squash_along, self.cap_squash_across)
|
|
let cov = Cap.cover(fp.x, px)
|
|
// Shaded only where it lies: beside it the lit value's light on its
|
|
// ends grows as exp(distance) and overflows along a long track, and
|
|
// even a zero cover would carry that into the ground.
|
|
var cap = col
|
|
if fp.x < px {
|
|
// Its profile: flat along and across, a quarter round in from the
|
|
// outline over its whole radius, so the drop is a dome.
|
|
let c_along = Cap.curve((q.x / hs.x + 1.0) * 0.5, 2.0, vec2(0.0, 1.0), vec2(0.1667, 0.0), vec2(-0.1667, 0.0), vec2(1.0, 1.0), vec2(0.1667, 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_across = Cap.curve(abs(q.y) / hs.y, 2.0, vec2(0.0, 1.0), vec2(0.1667, 0.0), vec2(-0.1667, 0.0), vec2(1.0, 1.0), vec2(0.1667, 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_edge = Cap.curve(Cap.edge_x(fp.x, hs, 1.0), 2.0, vec2(0.0, 0.0), vec2(0.0, 0.5523), vec2(-0.5523, 0.0), vec2(1.0, 1.0), vec2(0.5523, 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 f = Cap.field(q, hs, fp, c_along, c_across, c_edge, vec2(1.0, 0.0), shape, vec4(0.0, 0.0, 0.0, 1.0), vec4(0.0, 0.0, 0.0, 0.0), vec4(0.0, 0.5, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 0.0))
|
|
let n = Cap.normal(f.yz, height, vert)
|
|
let v = Cap.view(q, hs, self.cap_reflection_perspective, vert)
|
|
cap = Cap.shade(n, l, v, 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, light_ink, light_ink, shadow_ink, ground)
|
|
if self.cap_spun > 0.001 {
|
|
let sp = Cap.spun(q, hs, lt.xy) * self.cap_spun
|
|
cap = Cap.in_shadow(Cap.in_light(cap, clamp(sp, 0.0, 1.0) * 0.55, light_ink), clamp(-sp, 0.0, 1.0) * 0.42, shadow_ink)
|
|
}
|
|
cap = Cap.emit(cap, f.x, body, light_ink, self.cap_glow * lamp)
|
|
cap = cap + fill * Cap.fill_light(q, hs, f.yz * height, lo, hi, at, tw)
|
|
// The pointer over it brightens the drop a little, as it did the
|
|
// plastic cap.
|
|
cap = cap * (1.0 + 0.05 * hv)
|
|
}
|
|
// UNDER IT: the cast shadow and the contact round its foot, gone when
|
|
// disabled as the plastic cap's shadow went, then the light it throws
|
|
// on the ground, lifted a little by the pointer.
|
|
let sh = Cap.shadow(q, hs, r, vec3(0.6902, 0.6902, 1.0), lt, height, self.cap_shadow_blur, self.cap_shadow, px) + Cap.contact(fp.x, max(height * 0.3, 1.0), self.cap_contact)
|
|
col = Cap.in_shadow(col, clamp(sh, 0.0, 1.0) * live * (1.0 - cov), shadow_ink)
|
|
col = col + body * (Cap.glow(fp.x, hs, self.cap_glow * lamp * (1.0 + 0.2 * hv)) * 0.9 * (1.0 - cov))
|
|
// The drop over all at its cover, then toned, as the bench makes its
|
|
// frame. Laid over the window without painting it: the light the fader
|
|
// adds to `cap_ground` and the share of that ground it darkens, as this
|
|
// sheet lays its grooves and glows over the circuits. The drop and the
|
|
// lit value are solid, as this sheet's beads and lit lines are, so they
|
|
// are painted as they are and no trace shows through them. Over a
|
|
// ground of `cap_ground` this is the bench's own frame.
|
|
let ev = pow(2.0, self.cap_exposure)
|
|
let toned = Cap.tone(mix(col, cap, cov), ev, self.cap_rolloff)
|
|
let value_lit = Cap.cover(Cap.sd_cap(ab - tbox.xy, tbox.zw, tw * 0.5, vec2(1.0, 0.0)).x, px) * step(lo, ab.x) * step(ab.x, hi)
|
|
let res = mix(Cap.on_ground(toned, Cap.tone(ground, ev, self.cap_rolloff)), vec4(toned, 1.0), max(value_lit, cov))
|
|
return res * mix(1.0, 0.5, self.disabled)
|
|
}
|
|
|
|
// The fader: the drop along its whole box, lying or standing.
|
|
mod.widgets.SliderFader.draw_bg.vertex = fn() {
|
|
let across = mix(self.rect_size.y, self.rect_size.x, self.is_vertical)
|
|
let m = fp_fader_margin(across, self.cap_px * 0.5, self.inset_px, self.track_size, self.cap_light, self.cap_height, self.cap_glow, self.cap_shadow_blur)
|
|
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 vert = self.is_vertical
|
|
let along = mix(self.rect_size.x, self.rect_size.y, vert)
|
|
let across = mix(self.rect_size.y, self.rect_size.x, vert)
|
|
let m = fp_fader_margin(across, self.cap_px * 0.5, self.inset_px, self.track_size, self.cap_light, self.cap_height, self.cap_glow, self.cap_shadow_blur)
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
// The fader's own frame: `a` along the track from the value's zero, up
|
|
// the screen when it stands, and `b` across it.
|
|
let ab = vec2(mix(p.x, self.rect_size.y - p.y, vert), mix(p.y, p.x, vert))
|
|
return self.fp_drop(ab, along, Finish.snap(across * 0.5, px), vert, px)
|
|
}
|
|
|
|
// The minimal slider: the drop on its track under the label row, the whole
|
|
// width long. The room the drop has across is the track's row.
|
|
mod.widgets.SliderMinimal.draw_bg.vertex = fn() {
|
|
let room = max(self.rect_size.y - self.offset_y, 1.0)
|
|
let m = fp_fader_margin(room, self.cap_px * 0.5, self.inset_px, self.track_size, self.cap_light, self.cap_height, self.cap_glow, self.cap_shadow_blur)
|
|
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 px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let room = max(self.rect_size.y - self.offset_y, 1.0)
|
|
let m = fp_fader_margin(room, self.cap_px * 0.5, self.inset_px, self.track_size, self.cap_light, self.cap_height, self.cap_glow, self.cap_shadow_blur)
|
|
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let cy = Finish.snap(self.rect_pos.y + self.offset_y + room * 0.5, px) - self.rect_pos.y
|
|
return self.fp_drop(p, self.rect_size.x, cy, 0.0, px)
|
|
}
|
|
// The bar slider: label and value above, the drop on its track below, the
|
|
// whole width long.
|
|
mod.widgets.SliderFlat.height = 36.0
|
|
mod.widgets.SliderFlat.draw_bg.vertex = fn() {
|
|
let room = max(self.rect_size.y - 20.0, 1.0)
|
|
let m = fp_fader_margin(room, self.cap_px * 0.5, self.inset_px, self.track_size, self.cap_light, self.cap_height, self.cap_glow, self.cap_shadow_blur)
|
|
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 px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let room = max(self.rect_size.y - 20.0, 1.0)
|
|
let m = fp_fader_margin(room, self.cap_px * 0.5, self.inset_px, self.track_size, self.cap_light, self.cap_height, self.cap_glow, self.cap_shadow_blur)
|
|
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let cy = Finish.snap(self.rect_pos.y + 20.0 + room * 0.5, px) - self.rect_pos.y
|
|
return self.fp_drop(p, self.rect_size.x, cy, 0.0, px)
|
|
}
|
|
// The round slider: the drop on its track beside the label, from the label
|
|
// column to the value's 44 points at the end.
|
|
mod.widgets.SliderRoundFlat.draw_bg.vertex = fn() {
|
|
let m = fp_fader_margin(self.rect_size.y, self.cap_px * 0.5, self.inset_px, self.track_size, self.cap_light, self.cap_height, self.cap_glow, self.cap_shadow_blur)
|
|
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 px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let m = fp_fader_margin(self.rect_size.y, self.cap_px * 0.5, self.inset_px, self.track_size, self.cap_light, self.cap_height, self.cap_glow, self.cap_shadow_blur)
|
|
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let o = self.rect_pos
|
|
let cy = Finish.snap(o.y + self.rect_size.y * 0.5, px) - o.y
|
|
let x0 = Finish.snap(o.x + self.label_size, px) - o.x
|
|
let x1 = Finish.snap(o.x + self.rect_size.x - 44.0, px) - o.x
|
|
return self.fp_drop(vec2(p.x - x0, p.y), max(x1 - x0, 1.0), cy, 0.0, px)
|
|
}
|
|
|
|
// ROTARIES. A crystal knob: a skirt of clear plastic cut in twelve facets,
|
|
// its rim lit cyan, the value a lit arc running in a dark groove round the
|
|
// skirt's edge; in its middle a cap of turned metal with a dark pointer
|
|
// line and a lit tick on the skirt where it points.
|
|
let fp_arc = fn(q: vec2, r: float, a0: float, a1: float) -> float {
|
|
var a = atan2(0.0 - q.x, q.y)
|
|
if a < 0.0 {
|
|
a = a + 6.2831853
|
|
}
|
|
let ring = abs(length(q) - r)
|
|
let e0 = vec2(0.0 - sin(a0), cos(a0)) * r
|
|
let e1 = vec2(0.0 - sin(a1), cos(a1)) * r
|
|
let ends = min(length(q - e0), length(q - e1))
|
|
return mix(ends, ring, step(a0, a) * step(a, a1))
|
|
}
|
|
let fp_knob = fn(p: vec2, c: vec2, rad: float, px: float, gap: float, pos: float, org: float, arc_org: float, hover: float, focus: float, drag: float, disabled: float) -> vec4 {
|
|
let live = 1.0 - disabled
|
|
let dia = rad * 2.0
|
|
let q = p - c
|
|
let l = length(q)
|
|
let d = l - rad
|
|
let inside = Finish.cover(d, px)
|
|
let outside = 1.0 - inside
|
|
let hv = max(hover, drag)
|
|
// Outside: the drop and the light in the skirt leaking round it.
|
|
let sh = Finish.drop(p, c, vec2(rad, rad), rad, vec2(0.0, 1.6), 2.0) * 0.55 * live * outside
|
|
var f = vec4(fp_ink() * sh, sh)
|
|
let dd = max(d, 0.0)
|
|
let hg = (exp(0.0 - dd / 1.2) * 0.50 + exp(0.0 - dd * dd / 14.0) * 0.30) * (1.05 + 0.20 * hv + 0.25 * focus) * live * outside
|
|
f = Finish.over(f, vec4(fp_acc_glow() * hg, hg))
|
|
// The skirt: clear blue-grey plastic, paler on top, twelve facets that
|
|
// catch the light in turn, a cyan light caught in its lower rim.
|
|
let yn = clamp((p.y - c.y + rad) / dia, 0.0, 1.0)
|
|
let ang = atan2(q.y, q.x)
|
|
let facet = 0.5 + 0.5 * cos(ang * 12.0)
|
|
var col = mix(vec3(0.60, 0.70, 0.77), vec3(0.30, 0.39, 0.46), yn)
|
|
let lightside = clamp(0.5 - 0.5 * dot(q / max(l, 0.001), vec2(0.35, 0.94)), 0.0, 1.0)
|
|
col = col + vec3(0.16, 0.19, 0.21) * (facet - 0.35) * (0.5 + 0.5 * lightside) * smoothstep(rad * 0.62, rad * 0.75, l)
|
|
col = col + fp_acc() * 0.22 * smoothstep(0.55, 1.0, yn) * smoothstep(rad * 0.7, rad, l)
|
|
col = col * (1.0 + 0.05 * hv)
|
|
var g = vec4(col * 0.95, 0.95)
|
|
// The groove the value runs in, 0.1 of the diameter, just inside the rim.
|
|
let wr = rad - 1.5 - dia * 0.05
|
|
let wd = abs(l - wr) - dia * 0.05
|
|
g = Finish.over(g, fp_well_h(wd, 1.0 - yn, px, 0.45, 0.12, 0.08, 0.3))
|
|
// The rim: pale on top, cyan light below.
|
|
let rb = Finish.band(d, 0.0, px, px)
|
|
g = mix(g, vec4(mix(vec3(0.92, 0.97, 1.0), fp_acc_line(), smoothstep(0.1, 0.7, yn)), 1.0), rb * (0.85 + 0.15 * hv))
|
|
let rin = exp(0.0 - max(0.0 - d, 0.0) / 2.0) * mix(0.35, 1.0, smoothstep(0.2, 1.0, yn)) * 0.55 * live
|
|
g = Finish.over(g, vec4(fp_acc() * rin, rin))
|
|
f = Finish.over(f, g * inside)
|
|
// The value arc, lit.
|
|
let span = 6.2831853 - gap
|
|
let a0 = gap * 0.5
|
|
let av = a0 + span * pos
|
|
let ao = a0 + span * org
|
|
let s0 = mix(a0, min(ao, av), arc_org)
|
|
let s1 = mix(av, max(ao, av), arc_org)
|
|
let ad = fp_arc(q, wr, s0, s1) - dia * 0.032
|
|
let ak = step(0.001, s1 - s0) * mix(1.0, 0.35, disabled)
|
|
let add = max(ad, 0.0)
|
|
let ahg = min(exp(0.0 - add / 0.9) * 0.70 + exp(0.0 - add * add / 6.0) * 0.35, 0.9) * ak * (1.0 - Finish.cover(ad, px))
|
|
f = Finish.over(f, vec4(fp_acc() * ahg, ahg))
|
|
let aa = Finish.cover(ad, px) * ak
|
|
let acore = Finish.cover(ad + dia * 0.016, px)
|
|
f = Finish.over(f, vec4(mix(fp_acc(), vec3(0.95, 1.0, 1.0), acore * 0.85) * aa, aa))
|
|
// The cap of turned metal, 0.6 of the disc, with its own drop.
|
|
let cr = rad * 0.6
|
|
let cd = l - cr
|
|
let cout = 1.0 - Finish.cover(cd, px)
|
|
let csh = Finish.drop(p, c, vec2(cr, cr), cr, vec2(0.0, 1.0), 1.4) * 0.45 * live * cout * inside
|
|
f = Finish.over(f, vec4(fp_ink() * csh, csh))
|
|
var cap = vec3(0.66, 0.72, 0.77) * Finish.spun(p, c, vec2(-0.3, -0.9))
|
|
let cyn = clamp((p.y - c.y + cr) / (2.0 * cr), 0.0, 1.0)
|
|
cap = cap * mix(1.05, 0.80, cyn) * (1.0 + 0.04 * hv)
|
|
let crb = Finish.band(cd, 0.0, px, px)
|
|
cap = mix(cap, mix(vec3(0.95, 0.98, 1.0), vec3(0.35, 0.42, 0.48), cyn), crb * 0.7)
|
|
cap = mix(cap, vec3(0.30, 0.34, 0.38), disabled * 0.6)
|
|
f = Finish.over(f, vec4(cap, 1.0) * Finish.cover(cd, px))
|
|
// The pointer: a dark line on the cap, a lit tick on the skirt.
|
|
let dir = vec2(0.0 - sin(av), cos(av))
|
|
let pd = fp_seg(q, dir * (cr * 0.25), dir * (cr - 1.5)) - 0.8
|
|
let pa = Finish.cover(pd, px) * 0.85
|
|
f = Finish.over(f, vec4(vec3(0.06, 0.09, 0.12) * pa, pa))
|
|
let td = fp_seg(q, dir * (cr + 1.5), dir * (wr - dia * 0.07)) - 0.8
|
|
let ta = Finish.cover(td, px) * mix(1.0, 0.4, disabled)
|
|
return Finish.over(f, vec4(fp_acc_rim() * ta, ta))
|
|
}
|
|
// The rotaries are SliderMinimals underneath too, so they would take the
|
|
// sliders' drop room, cap and liquid motion: they keep their own 6 point
|
|
// room and the library's zero cap, inset and viscosity.
|
|
mod.widgets.RotaryFlat.cap_size = 0.0
|
|
mod.widgets.RotaryFlat.track_inset = 0.0
|
|
mod.widgets.RotaryFlat.cap_viscosity = 0.0
|
|
mod.widgets.RotaryKnob.cap_size = 0.0
|
|
mod.widgets.RotaryKnob.track_inset = 0.0
|
|
mod.widgets.RotaryKnob.cap_viscosity = 0.0
|
|
mod.widgets.RotaryFlat.draw_bg.vertex = fn() {
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(6.0, 6.0), self.rect_size + vec2(12.0, 12.0))
|
|
}
|
|
mod.widgets.RotaryKnob.draw_bg.vertex = fn() {
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(6.0, 6.0), self.rect_size + vec2(12.0, 12.0))
|
|
}
|
|
mod.widgets.RotaryFlat.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * (self.rect_size + vec2(12.0, 12.0)) - vec2(6.0, 6.0)
|
|
let dia = min(self.rect_size.x, self.rect_size.y - 22.0) - 8.0
|
|
let c = vec2(Finish.snap(self.rect_pos.x + self.rect_size.x * 0.5, px), Finish.snap(self.rect_pos.y + 22.0 + 4.0 + dia * 0.5, px)) - self.rect_pos
|
|
return fp_knob(p, c, dia * 0.5, px, self.gap * 0.017453, self.slide_pos, self.origin_pos, self.arc_origin, self.hover, self.focus, self.drag, self.disabled)
|
|
}
|
|
mod.widgets.RotaryKnob.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * (self.rect_size + vec2(12.0, 12.0)) - vec2(6.0, 6.0)
|
|
let dia = min(self.rect_size.x, self.rect_size.y) - 6.0
|
|
let c = vec2(Finish.snap(self.rect_pos.x + self.rect_size.x * 0.5, px), Finish.snap(self.rect_pos.y + self.rect_size.y - 3.0 - dia * 0.5, px)) - self.rect_pos
|
|
return fp_knob(p, c, dia * 0.5, px, self.gap * 0.017453, self.slide_pos, self.origin_pos, self.arc_origin, self.hover, self.focus, self.drag, self.disabled)
|
|
}
|
|
mod.widgets.RotaryKnob.draw_text.color = #bfd0dc
|
|
mod.widgets.RotaryKnob.draw_text.color_hover = #eef6fb
|
|
mod.widgets.RotaryKnob.draw_text.color_focus = #eef6fb
|
|
mod.widgets.RotaryKnob.draw_text.color_drag = #ffffff
|
|
mod.widgets.RotaryKnob.draw_text.color_disabled = #5d6b78
|
|
mod.widgets.RotaryKnob.draw_text.text_style = mod.theme.font_regular{font_size: 7.5}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// TEXT FIELDS. A flat slab of the same plastic, lying on the ground: no
|
|
// drop, its shoulder and rim, and a quieter light inside its foot edge. A
|
|
// focus rings it in cyan light with a halo. A field whose frame is switched
|
|
// off (the value inside a slider) draws nothing.
|
|
let fp_field = fn(p: vec2, o: vec2, s: vec2, px: float, r: float, hover: float, focus: float, disabled: float, wd: vec4) -> vec4 {
|
|
let b = fp_snap(o, s, px)
|
|
return fp_plate(p, b, vec4(r, r, r, r), px, hover * 0.7, 0.0, focus, disabled, 1.0, 0.25, 0.9, vec4(vec3(0.46, 0.60, 0.69) * 0.40, 0.40), fp_rimc(), fp_acc_line(), 0.45 + 0.35 * focus, 0.0, 0.0, wd)
|
|
}
|
|
// A darker well of the plastic, for numbers: dark body, a pale foot line.
|
|
let fp_field_dark = fn(p: vec2, o: vec2, s: vec2, px: float, r: float, hover: float, focus: float, disabled: float, wd: vec4) -> vec4 {
|
|
let b = fp_snap(o, s, px)
|
|
return fp_plate(p, b, vec4(r, r, r, r), px, hover * 0.7, 0.0, focus, disabled, 1.0, 0.0, 0.7, vec4(vec3(0.10, 0.15, 0.20) * 0.80, 0.80), fp_rimc(), fp_acc_line(), 0.35 + 0.35 * focus, 0.0, 0.0, wd)
|
|
}
|
|
mod.widgets.TextInputFlat.clip_x = false
|
|
mod.widgets.TextInputFlat.clip_y = false
|
|
mod.widgets.TextInputFlat.draw_bg.vertex = fn() {
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(6.0, 6.0), self.rect_size + vec2(12.0, 12.0))
|
|
}
|
|
mod.widgets.TextInputFlat.draw_bg.pixel = fn() {
|
|
if self.border_size < 0.01 {
|
|
return vec4(0.0, 0.0, 0.0, 0.0)
|
|
}
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * (self.rect_size + vec2(12.0, 12.0)) - vec2(6.0, 6.0)
|
|
let wd = fp_wd(self.world, p, self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(0.0, 0.0, 0.0, 1.0)), self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(1.0, 1.0, 0.0, 1.0)))
|
|
return fp_field(p, self.rect_pos, self.rect_size, px, self.border_radius, self.hover, self.focus, self.disabled, wd)
|
|
}
|
|
// The field's ink on the pale plastic: deep blue-grey, the placeholder a
|
|
// shade lighter (#223341 reads 4.7 on the field's #7fa0b4 foot).
|
|
let fp_field_ink = fn(t) {
|
|
t.color = #0f1c27
|
|
t.color_hover = #0b1720
|
|
t.color_focus = #0b1720
|
|
t.color_down = #0b1720
|
|
t.color_disabled = #6a7885
|
|
t.color_empty = #223341
|
|
t.color_empty_hover = #1a2a37
|
|
t.color_empty_focus = #1a2a37
|
|
}
|
|
fp_field_ink(mod.widgets.TextInputFlat.draw_text)
|
|
fp_field_ink(mod.widgets.NumberField.input.draw_text)
|
|
fp_field_ink(mod.widgets.ComboBoxFlat.input.draw_text)
|
|
mod.widgets.NumberField.clip_x = false
|
|
mod.widgets.NumberField.clip_y = false
|
|
mod.widgets.NumberField.draw_bg.vertex = fn() {
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(6.0, 6.0), self.rect_size + vec2(12.0, 12.0))
|
|
}
|
|
mod.widgets.NumberField.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * (self.rect_size + vec2(12.0, 12.0)) - vec2(6.0, 6.0)
|
|
let wd = fp_wd(self.world, p, self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(0.0, 0.0, 0.0, 1.0)), self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(1.0, 1.0, 0.0, 1.0)))
|
|
return fp_field(p, self.rect_pos, self.rect_size, px, self.border_radius, self.hover, self.focus, self.disabled, wd)
|
|
}
|
|
// The stepper: two frost keys joined at the field's right end, a white
|
|
// hairline at 10 % between them; the key under the hand lifts, the held
|
|
// key settles.
|
|
mod.widgets.NumberSpin.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 b = fp_snap(o + vec2(1.0, 2.0), self.rect_size - vec2(3.0, 4.0), px) + vec4(1.0, 2.0, 1.0, 2.0)
|
|
let c = (b.xy + b.zw) * 0.5
|
|
let h = (b.zw - b.xy) * 0.5
|
|
let d = Material.sd_box(p, c, h, 3.0)
|
|
let mid = Finish.snap(o.y + self.rect_size.y * 0.5, px) - o.y
|
|
let upper = step(p.y, mid)
|
|
let live = 1.0 - self.disabled
|
|
let hot = mix(max(0.0, 0.0 - self.hot), max(0.0, self.hot), upper) * live
|
|
let down = mix(max(0.0, 0.0 - self.down), max(0.0, self.down), upper) * live
|
|
let yn = clamp((p.y - b.y) / max(b.w - b.y, 1.0), 0.0, 1.0)
|
|
let wa = fp_lift(yn) * 0.8 + 0.06 * hot * (1.0 - down) - 0.05 * down
|
|
var f = vec4(fp_white() * wa, wa)
|
|
let rb = Finish.band(d, 0.0, px, px) * fp_rim(yn, 0.5, 0.55)
|
|
f = Finish.over(f, vec4(fp_white() * rb, rb))
|
|
let dv = Finish.cover(abs(p.y - mid) - px * 0.5, px) * step(b.x + 3.0, p.x) * step(p.x, b.z - 3.0) * 0.10
|
|
f = Finish.over(f, vec4(fp_white() * dv, dv))
|
|
// The marks: stroked chevrons 6 by 3.5 points in a 1.25 point stroke,
|
|
// #e2edf4, each centred in its own key (the font's solid triangles are
|
|
// switched off below).
|
|
let cx = (b.x + b.z) * 0.5
|
|
let cu = vec2(cx, (b.y + mid) * 0.5)
|
|
let cl = vec2(cx, (mid + b.w) * 0.5)
|
|
let up = min(fp_seg(p, cu + vec2(-3.0, 1.75), cu + vec2(0.0, -1.75)), fp_seg(p, cu + vec2(0.0, -1.75), cu + vec2(3.0, 1.75)))
|
|
let dn = min(fp_seg(p, cl + vec2(-3.0, -1.75), cl + vec2(0.0, 1.75)), fp_seg(p, cl + vec2(0.0, 1.75), cl + vec2(3.0, -1.75)))
|
|
let ca = Finish.cover(min(up, dn) - 0.625, px) * mix(1.0, 0.45, self.disabled)
|
|
f = Finish.over(f, vec4(vec3(0.075, 0.129, 0.176) * ca, ca))
|
|
return f * Finish.cover(d, px) * mix(1.0, 0.4, self.disabled)
|
|
}
|
|
mod.widgets.NumberSpin.width = 20.0
|
|
mod.widgets.NumberField.spin_width = 20.0
|
|
mod.widgets.NumberSpin.draw_text.color = #0000
|
|
mod.widgets.NumberSpin.draw_text.color_disabled = #0000
|
|
|
|
// The drag-number of a toolbar: the recessed frost, the dragged amount a
|
|
// wash of accent light across it, the digits near-white.
|
|
mod.widgets.ValueInput.clip_x = false
|
|
mod.widgets.ValueInput.clip_y = false
|
|
mod.widgets.ValueInput.draw_bg.vertex = fn() {
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(6.0, 6.0), self.rect_size + vec2(12.0, 12.0))
|
|
}
|
|
mod.widgets.ValueInput.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * (self.rect_size + vec2(12.0, 12.0)) - vec2(6.0, 6.0)
|
|
let wd = fp_wd(self.world, p, self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(0.0, 0.0, 0.0, 1.0)), self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(1.0, 1.0, 0.0, 1.0)))
|
|
let f = fp_field(p, self.rect_pos, self.rect_size, px, 4.0, max(self.hover, self.drag), self.focus, 0.0, wd)
|
|
let d = Material.sd_box(p, self.rect_size * 0.5, self.rect_size * 0.5 - vec2(px, px), 4.0)
|
|
let wash = step(0.0, self.fill) * step(p.x, (self.rect_size.x) * max(self.fill, 0.0)) * Finish.cover(d, px) * 0.20
|
|
return Finish.over(f, vec4(fp_acc() * wash, wash))
|
|
}
|
|
mod.widgets.ValueInput.draw_text.color = #13212d
|
|
mod.widgets.ValueInput.arrow_color = #2a3c4a
|
|
fp_field_ink(mod.widgets.ValueInput.text_input.draw_text)
|
|
mod.widgets.ValueInput.text_input.draw_bg.color = #00000000
|
|
|
|
// DROP-DOWNS. The button's frost at a 4 point corner, with a chevron 8 by
|
|
// 4.5 in a 1.25 point stroke, 10 points in from the right; open, the foot
|
|
// lights as a focus does.
|
|
let fp_chevron = fn(under: vec4, p: vec2, s: vec2, px: float, live: float) -> vec4 {
|
|
let c = vec2(s.x - 14.0, s.y * 0.5)
|
|
let ch = min(fp_seg(p, c + vec2(-4.0, -1.75), c + vec2(0.0, 1.75)), fp_seg(p, c + vec2(0.0, 1.75), c + vec2(4.0, -1.75)))
|
|
let a = Finish.cover(ch - 0.625, px) * mix(0.4, 1.0, live)
|
|
return Finish.over(under, vec4(vec3(0.075, 0.129, 0.176) * a, a))
|
|
}
|
|
fp_dark_ink(mod.widgets.DropDownFlat.draw_text)
|
|
mod.widgets.DropDownFlat.clip_x = false
|
|
mod.widgets.DropDownFlat.clip_y = false
|
|
mod.widgets.DropDownFlat.padding.left = 10.0
|
|
mod.widgets.DropDownFlat.padding.right = 26.0
|
|
mod.widgets.DropDownFlat.draw_bg.vertex = fn() {
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(8.0, 8.0), self.rect_size + vec2(16.0, 16.0))
|
|
}
|
|
mod.widgets.DropDownFlat.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * (self.rect_size + vec2(16.0, 16.0)) - vec2(8.0, 8.0)
|
|
let wd = fp_wd(self.world, p, self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(0.0, 0.0, 0.0, 1.0)), self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(1.0, 1.0, 0.0, 1.0)))
|
|
let r = self.border_radius
|
|
let f = fp_button(p, self.rect_pos, self.rect_size, px, vec4(r, r, r, r), self.hover, self.down * self.hover, max(self.focus, self.active), self.disabled, 1.0, 1.0, 1.0, fp_rimc(), 0.0, wd)
|
|
return fp_chevron(f, p, self.rect_size, px, 1.0 - self.disabled)
|
|
}
|
|
// The combo box is a field one types in, so the recessed frost, with the
|
|
// same chevron.
|
|
mod.widgets.ComboBoxFlat.clip_x = false
|
|
mod.widgets.ComboBoxFlat.clip_y = false
|
|
mod.widgets.ComboBoxFlat.padding.left = 8.0
|
|
mod.widgets.ComboBoxFlat.padding.right = 26.0
|
|
mod.widgets.ComboBoxFlat.draw_bg.vertex = fn() {
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(6.0, 6.0), self.rect_size + vec2(12.0, 12.0))
|
|
}
|
|
mod.widgets.ComboBoxFlat.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * (self.rect_size + vec2(12.0, 12.0)) - vec2(6.0, 6.0)
|
|
let wd = fp_wd(self.world, p, self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(0.0, 0.0, 0.0, 1.0)), self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(1.0, 1.0, 0.0, 1.0)))
|
|
let f = fp_field(p, self.rect_pos, self.rect_size, px, self.border_radius, self.hover, self.focus, self.disabled, wd)
|
|
return fp_chevron(f, p, self.rect_size, px, 1.0 - self.disabled)
|
|
}
|
|
mod.widgets.ComboBoxFlat.input.min_height = 0.0
|
|
mod.widgets.ComboBoxFlat.draw_popup_bg.border_radius = 6.0
|
|
mod.widgets.ComboBoxFlat.draw_popup_bg.color = #1f2633
|
|
mod.widgets.ComboBoxFlat.draw_popup_bg.border_color = #59616f
|
|
mod.widgets.ComboBoxFlat.draw_item.color_hover = #2c3441
|
|
mod.widgets.ComboBoxFlat.draw_item.color_active = #34404f
|
|
|
|
// The select wears a button of its own, built before this sheet: it is
|
|
// given the drop-down's face and the grown quad here.
|
|
mod.widgets.Select.clip_x = false
|
|
mod.widgets.Select.clip_y = false
|
|
mod.widgets.MultiSelect.clip_x = false
|
|
mod.widgets.MultiSelect.clip_y = false
|
|
mod.widgets.Select.face.clip_x = false
|
|
mod.widgets.Select.face.clip_y = false
|
|
mod.widgets.MultiSelect.face.clip_x = false
|
|
mod.widgets.MultiSelect.face.clip_y = false
|
|
mod.widgets.Select.face.padding = mod.turtle.Inset{left: 10.0 right: 26.0 top: 4.0 bottom: 4.0}
|
|
mod.widgets.MultiSelect.face.padding = mod.turtle.Inset{left: 10.0 right: 26.0 top: 4.0 bottom: 4.0}
|
|
mod.widgets.Select.face.min_height = 26.0
|
|
mod.widgets.MultiSelect.face.min_height = 26.0
|
|
mod.widgets.Select.face.draw_bg.vertex = fn() {
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(8.0, 8.0), self.rect_size + vec2(16.0, 16.0))
|
|
}
|
|
mod.widgets.MultiSelect.face.draw_bg.vertex = fn() {
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(8.0, 8.0), self.rect_size + vec2(16.0, 16.0))
|
|
}
|
|
mod.widgets.Select.face.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * (self.rect_size + vec2(16.0, 16.0)) - vec2(8.0, 8.0)
|
|
let wd = fp_wd(self.world, p, self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(0.0, 0.0, 0.0, 1.0)), self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(1.0, 1.0, 0.0, 1.0)))
|
|
let f = fp_button(p, self.rect_pos, self.rect_size, px, vec4(4.0, 4.0, 4.0, 4.0), self.hover, self.down, self.focus, self.disabled, 1.0, 1.0, 1.0, fp_rimc(), 0.0, wd)
|
|
return fp_chevron(f, p, self.rect_size, px, 1.0 - self.disabled)
|
|
}
|
|
mod.widgets.MultiSelect.face.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * (self.rect_size + vec2(16.0, 16.0)) - vec2(8.0, 8.0)
|
|
let wd = fp_wd(self.world, p, self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(0.0, 0.0, 0.0, 1.0)), self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(1.0, 1.0, 0.0, 1.0)))
|
|
let f = fp_button(p, self.rect_pos, self.rect_size, px, vec4(4.0, 4.0, 4.0, 4.0), self.hover, self.down, self.focus, self.disabled, 1.0, 1.0, 1.0, fp_rimc(), 0.0, wd)
|
|
return fp_chevron(f, p, self.rect_size, px, 1.0 - self.disabled)
|
|
}
|
|
|
|
// THE OPEN LIST. A frost panel over the page, nearly opaque, since a face
|
|
// cannot blur what it covers; the row under the hand is white 6 %, the
|
|
// chosen row white 10 % with a top rim and a line of foot light.
|
|
mod.widgets.PopupMenuFlat.clip_x = false
|
|
mod.widgets.PopupMenuFlat.clip_y = false
|
|
mod.widgets.PopupMenuFlat.draw_bg.vertex = fn() {
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(12.0, 12.0), self.rect_size + vec2(24.0, 24.0))
|
|
}
|
|
mod.widgets.PopupMenuFlat.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * (self.rect_size + vec2(24.0, 24.0)) - vec2(12.0, 12.0)
|
|
let wd = fp_wd(self.world, p, self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(0.0, 0.0, 0.0, 1.0)), self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(1.0, 1.0, 0.0, 1.0)))
|
|
let b = fp_snap(self.rect_pos, self.rect_size, px)
|
|
let tone = vec4(vec3(0.16, 0.21, 0.26) * 0.85, 0.85)
|
|
return fp_plate(p, b, vec4(4.0, 4.0, 4.0, 4.0), px, 0.0, 0.0, 0.0, 0.0, 1.0, 1.3, 1.0, tone, fp_rimc(), fp_acc_line(), 0.5, 0.0, 0.0, wd)
|
|
}
|
|
mod.widgets.PopupMenuItem.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * self.rect_size
|
|
let c = self.rect_size * 0.5
|
|
let h = self.rect_size * 0.5 - vec2(3.0, 1.0)
|
|
let d = Material.sd_box(p, c, h, 3.0)
|
|
let live = 1.0 - self.disabled
|
|
let wa = (0.06 * self.hover + 0.10 * self.active) * live
|
|
var f = vec4(fp_white() * wa, wa)
|
|
let top = Finish.band(d, 0.0, px, px) * step(p.y, c.y) * 0.30 * self.active
|
|
f = Finish.over(f, vec4(fp_white() * top, top))
|
|
let xn = p.x / max(self.rect_size.x, 1.0)
|
|
let lw = (1.0 - smoothstep(0.3, 0.45, abs(xn - 0.5))) * step(c.y, p.y) * self.active * live
|
|
f = mix(f, vec4(fp_acc_line(), 1.0), Finish.band(d, 0.0, px, px) * lw)
|
|
return f * Finish.cover(d, px)
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// PROGRESS. A 6 point groove of dark plastic; the amount a lit line inside
|
|
// it, a pale core in the cyan body with its halo on the groove's floor.
|
|
// The unknown amount is held still: a short lit stretch in the middle of
|
|
// the groove, fading at both ends (a face may not read the pass clock, so
|
|
// it does not sweep).
|
|
mod.widgets.ProgressBarFlat.clip_x = false
|
|
mod.widgets.ProgressBarFlat.clip_y = false
|
|
mod.widgets.ProgressBarFlat.draw_bg.vertex = fn() {
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(8.0, 8.0), self.rect_size + vec2(16.0, 16.0))
|
|
}
|
|
mod.widgets.ProgressBarFlat.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * (self.rect_size + vec2(16.0, 16.0)) - vec2(8.0, 8.0)
|
|
let o = self.rect_pos
|
|
let th = min(6.0, self.rect_size.y)
|
|
let y0 = Finish.snap(o.y + (self.rect_size.y - th) * 0.5, px) - o.y
|
|
let y1 = y0 + Finish.snap(th, px)
|
|
let x0 = Finish.snap(o.x + self.rect_size.x * self.start, px) - o.x
|
|
let x1 = Finish.snap(o.x + self.rect_size.x * self.end, px) - o.x
|
|
let c = vec2((x0 + x1) * 0.5, (y0 + y1) * 0.5)
|
|
let h = vec2((x1 - x0) * 0.5, (y1 - y0) * 0.5)
|
|
let d = Material.sd_box(p, c, h, 3.0)
|
|
let yn = clamp((p.y - y0) / max(y1 - y0, 1.0), 0.0, 1.0)
|
|
var f = fp_well_h(d, yn, px, 0.50 * self.track, 0.22 * self.track, 0.12 * self.track, 0.5 * self.track)
|
|
// The accent for a plain bar, the meaning's colour for an intent.
|
|
let hue = mix(fp_acc(), self.fill_color().rgb, step(0.5, self.intent))
|
|
let fi = d
|
|
if self.value >= 0.0 {
|
|
let xv = mix(x0, x1, clamp((self.value - self.start) / max(self.end - self.start, 0.0001), 0.0, 1.0))
|
|
let lw = max(th - 2.0, 1.5)
|
|
f = fp_beam(f, p, vec2(x0 + 1.0 + lw * 0.5, c.y), vec2(max(xv - 1.0 - lw * 0.5, x0 + 1.0 + lw * 0.5), c.y), lw, px, step(x0 + 0.5, xv), hue)
|
|
} else {
|
|
let xn = clamp((p.x - x0) / max(x1 - x0, 1.0), 0.0, 1.0)
|
|
let k = 1.0 - smoothstep(0.05, 0.18, abs(xn - 0.5))
|
|
let lw = max(th - 3.0, 1.5)
|
|
f = fp_beam(f, p, vec2(mix(x0, x1, 0.34), c.y), vec2(mix(x0, x1, 0.66), c.y), lw, px, 1.0, hue)
|
|
f = mix(fp_well_h(d, yn, px, 0.50 * self.track, 0.22 * self.track, 0.12 * self.track, 0.5 * self.track), f, k)
|
|
}
|
|
return f * self.opacity
|
|
}
|
|
|
|
// LEVEL METERS. A ladder of 3 point cells with a 1 point gap, radius 1, in
|
|
// a well of darker glass: the accent to 70 %, amber to 90 %, rose above; an
|
|
// unlit cell is white at 10 %; the held peak is one point of white at
|
|
// 80 %; the over lamp beside the ladder lights rose. Lit cells have white
|
|
// cores and light the floor between them; the lit stretch throws a halo
|
|
// onto the ground round the meter.
|
|
mod.widgets.LevelMeter.clip_x = false
|
|
mod.widgets.LevelMeter.clip_y = false
|
|
mod.widgets.LevelMeter.draw_bg.vertex = fn() {
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(7.0, 7.0), self.rect_size + vec2(14.0, 14.0))
|
|
}
|
|
mod.widgets.LevelMeter.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p0 = self.pos * (self.rect_size + vec2(14.0, 14.0)) - vec2(7.0, 7.0)
|
|
let along = mix(self.rect_size.x, self.rect_size.y, self.vertical)
|
|
let across = mix(self.rect_size.y, self.rect_size.x, self.vertical)
|
|
let p = vec2(mix(p0.x, self.rect_size.y - p0.y, self.vertical), mix(p0.y, p0.x, self.vertical))
|
|
let lamp_room = self.lamp * (self.lamp_size + self.lamp_gap)
|
|
let bar = max(4.0, along - lamp_room)
|
|
let d = Material.sd_box(p, vec2(bar * 0.5, across * 0.5), vec2(bar * 0.5, across * 0.5), 2.0)
|
|
let yn = clamp(p.y / max(across, 1.0), 0.0, 1.0)
|
|
var f = fp_well_h(d, mix(yn, 0.5, self.vertical), px, 0.55, 0.20, 0.12, 0.5)
|
|
let pad = 2.0
|
|
// The lit stretch glows as one lamp: a halo round it that lights the
|
|
// ground beside the meter, gone within six points.
|
|
let lend = pad + max(1.0, bar - pad * 2.0) * self.level
|
|
let ld0 = Material.sd_box(p, vec2((pad + lend) * 0.5, across * 0.5), vec2(max((lend - pad) * 0.5, 0.01), across * 0.5), 1.5)
|
|
let ldd = max(ld0, 0.0)
|
|
let lh = (exp(0.0 - ldd / 1.5) * 0.60 + exp(0.0 - ldd * ldd / 14.0) * 0.35) * step(0.01, self.level) * (1.0 - Finish.cover(d, px))
|
|
f = Finish.over(vec4(fp_acc_glow() * lh, lh), f)
|
|
let span = max(1.0, bar - pad * 2.0)
|
|
let pitch = 4.0
|
|
let ci = floor((p.x - pad) / pitch)
|
|
let cx0 = pad + ci * pitch
|
|
let share = clamp((cx0 + 1.5) / span, 0.0, 1.0)
|
|
var hue = fp_acc()
|
|
if share > 0.7 {
|
|
hue = vec3(0.961, 0.827, 0.420)
|
|
}
|
|
if share > 0.9 {
|
|
hue = fp_rose()
|
|
}
|
|
let cell = Material.sd_box(p, vec2(cx0 + 1.5, across * 0.5), vec2(1.5, min(3.0, across * 0.5 - pad)), 1.0)
|
|
let inbar = step(0.0, ci) * step(cx0 + 3.0, bar - pad + 0.01)
|
|
let lit = step(cx0 + 1.5, pad + span * self.level)
|
|
let ca = Finish.cover(cell, px) * inbar
|
|
// A lit cell is a small lamp: a pale core and a glow onto the floor
|
|
// beside it; an unlit one the dark plastic's ghost.
|
|
let cg = exp(0.0 - max(cell, 0.0) / 0.9) * 0.70 * lit * inbar * (1.0 - ca)
|
|
f = Finish.over(f, vec4(hue * cg, cg))
|
|
let ia = ca * mix(0.12, 1.0, lit)
|
|
let core = Finish.cover(cell + 0.5, px) * lit * 0.85
|
|
f = Finish.over(f, vec4(mix(vec3(0.55, 0.66, 0.74), mix(hue, vec3(1.0, 1.0, 1.0), core), lit) * ia, ia))
|
|
// The held peak: one point of white at 80 %.
|
|
let hx = pad + span * self.hold
|
|
let hd = max(abs(p.x - hx) - 0.5, abs(p.y - across * 0.5) - min(3.0, across * 0.5 - pad))
|
|
let ha = Finish.cover(hd, px) * 0.8 * step(0.002, self.hold)
|
|
f = Finish.over(f, vec4(ha, ha, ha, ha))
|
|
if self.lamp > 0.5 {
|
|
let lc = vec2(bar + self.lamp_gap + self.lamp_size * 0.5, across * 0.5)
|
|
let ld = Material.sd_box(p, lc, vec2(self.lamp_size * 0.5, across * 0.5), 1.5)
|
|
let on = step(0.5, self.over)
|
|
let lw = fp_well(ld, 0.5, px, 0.28, 0.12, 0.12)
|
|
f = Finish.over(f, lw)
|
|
let la = Finish.cover(ld - px, px) * on
|
|
f = Finish.over(f, vec4(fp_rose() * la, la))
|
|
}
|
|
return f * self.opacity
|
|
}
|
|
|
|
// SCROLL BARS. No track at rest; under the hand a dark groove. The thumb
|
|
// is a lit line 3 points across, a pale core in a cyan body; brighter under
|
|
// the hand.
|
|
mod.widgets.ScrollBar.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p0 = self.pos * self.rect_size
|
|
let along = mix(self.rect_size.x, self.rect_size.y, self.is_vertical)
|
|
let across = mix(self.rect_size.y, self.rect_size.x, self.is_vertical)
|
|
let p = vec2(mix(p0.x, p0.y, self.is_vertical), mix(p0.y, p0.x, self.is_vertical))
|
|
let mid = across * 0.5
|
|
let warm = max(self.hover, self.drag)
|
|
let td = Finish.sd_pill(p, vec2(along * 0.5, mid), vec2(along * 0.5, min(3.0, mid)))
|
|
let ta = Finish.cover(td, px) * 0.35 * warm
|
|
var f = vec4(0.0, 0.0, 0.0, ta)
|
|
let a = along * self.norm_scroll
|
|
let b = a + along * self.norm_handle
|
|
f = fp_beam(f, p, vec2(a + 3.0, mid), vec2(max(b - 3.0, a + 3.0), mid), 3.0, px, mix(0.65, 1.0, warm), fp_acc())
|
|
return f * self.opacity
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// TABS AND SEGMENTS. Each quiet tab is a raised key of the plastic; the
|
|
// tab in use, and the chosen segment, is a key pushed in: dark plastic sunk
|
|
// below its neighbours, a shadow falling from its head, and at its foot a
|
|
// pool of cyan light with a lit line, so it is lit from within; its word
|
|
// answers in cyan with a small bloom.
|
|
let fp_sunk = fn(p: vec2, b: vec4, r: float, px: float, hover: float, live: float) -> vec4 {
|
|
let lo = b.xy
|
|
let hi = b.zw
|
|
let c = (lo + hi) * 0.5
|
|
let h = (hi - lo) * 0.5
|
|
let d = Material.sd_box(p, c, h, r)
|
|
let yn = clamp((p.y - lo.y) / max(hi.y - lo.y, 1.0), 0.0, 1.0)
|
|
let xn = clamp((p.x - lo.x) / max(hi.x - lo.x, 1.0), 0.0, 1.0)
|
|
let inside = Finish.cover(d, px)
|
|
// The lip round the hole catching the light, below it only.
|
|
let lip = Finish.ring_out(d, 0.0, px, px) * smoothstep(0.5, 0.9, yn) * 0.35
|
|
var o = vec4(vec3(0.75, 0.88, 0.95) * lip, lip)
|
|
let dd = max(d, 0.0)
|
|
let lg = exp(0.0 - dd / 1.0) * smoothstep(0.55, 1.0, yn) * 0.55 * live * (1.0 - inside)
|
|
o = Finish.over(o, vec4(fp_acc_glow() * lg, lg))
|
|
var col = mix(vec3(0.071, 0.110, 0.145), vec3(0.094, 0.145, 0.188), yn) * (1.0 + 0.10 * hover)
|
|
let ish = Finish.inset(p, c, h, r, vec2(0.0, 1.6), 1.8)
|
|
col = mix(col, vec3(0.0, 0.0, 0.0), ish * 0.65)
|
|
let mid = 1.0 - smoothstep(0.30, 0.48, abs(xn - 0.5))
|
|
let pool = exp(0.0 - max(hi.y - p.y, 0.0) / 4.5) * 0.85 * mid * live
|
|
col = mix(col, fp_acc_glow(), pool * 0.75)
|
|
col = col + fp_acc() * exp(0.0 - length((p - vec2(c.x, hi.y)) / vec2(max(h.x, 1.0), 6.0)) * 1.2) * 0.35 * live
|
|
let rb = Finish.band(d, 0.0, px, px)
|
|
col = mix(col, vec3(0.0, 0.0, 0.0), rb * (1.0 - smoothstep(0.2, 0.5, yn)) * 0.5)
|
|
col = mix(col, mix(fp_acc(), vec3(1.0, 1.0, 1.0), 0.45), rb * smoothstep(0.6, 0.9, yn) * mid * live)
|
|
return Finish.over(o, vec4(col, 1.0) * inside)
|
|
}
|
|
// A raised key, or the sunk one; `inset` keeps a key off its neighbours.
|
|
let fp_key = fn(p: vec2, o: vec2, s: vec2, px: float, inset: vec2, r: float, hover: float, down: float, focus: float, active: float, live: float, wd: vec4) -> vec4 {
|
|
let b = fp_snap(o + inset, s - inset * 2.0, px)
|
|
let raised = fp_plate(p, b, vec4(r, r, r, r), px, hover, down, focus, 1.0 - live, 1.0, 0.8, 1.0, vec4(0.0, 0.0, 0.0, 0.0), fp_rimc(), fp_acc_line(), 0.8, 0.0, 0.0, wd)
|
|
let sunk = fp_sunk(p, b, r, px, hover, live)
|
|
return mix(raised, sunk, active)
|
|
}
|
|
mod.widgets.Tabs.height = 26.0
|
|
mod.widgets.Tabs.clip_x = false
|
|
mod.widgets.Tabs.clip_y = false
|
|
mod.widgets.Tabs.draw_tab.vertex = fn() {
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(5.0, 5.0), self.rect_size + vec2(10.0, 10.0))
|
|
}
|
|
mod.widgets.Tabs.draw_tab_hover.vertex = fn() {
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(5.0, 5.0), self.rect_size + vec2(10.0, 10.0))
|
|
}
|
|
mod.widgets.Tabs.draw_tab_active.vertex = fn() {
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(5.0, 5.0), self.rect_size + vec2(10.0, 10.0))
|
|
}
|
|
mod.widgets.Tabs.draw_bg.pixel = fn() {
|
|
return vec4(0.0, 0.0, 0.0, 0.0)
|
|
}
|
|
mod.widgets.Tabs.draw_tab_active.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * (self.rect_size + vec2(10.0, 10.0)) - vec2(5.0, 5.0)
|
|
let wd = fp_wd(self.world, p, self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(0.0, 0.0, 0.0, 1.0)), self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(1.0, 1.0, 0.0, 1.0)))
|
|
return fp_key(p, self.rect_pos, self.rect_size, px, vec2(1.5, 0.0), 3.0, 0.0, 0.0, 0.0, 1.0, 1.0, wd)
|
|
}
|
|
mod.widgets.Tabs.draw_tab_hover.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * (self.rect_size + vec2(10.0, 10.0)) - vec2(5.0, 5.0)
|
|
let wd = fp_wd(self.world, p, self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(0.0, 0.0, 0.0, 1.0)), self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(1.0, 1.0, 0.0, 1.0)))
|
|
return fp_key(p, self.rect_pos, self.rect_size, px, vec2(1.5, 0.0), 3.0, 1.0, 0.0, 0.0, 0.0, 1.0, wd)
|
|
}
|
|
mod.widgets.Tabs.draw_tab.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * (self.rect_size + vec2(10.0, 10.0)) - vec2(5.0, 5.0)
|
|
let wd = fp_wd(self.world, p, self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(0.0, 0.0, 0.0, 1.0)), self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(1.0, 1.0, 0.0, 1.0)))
|
|
return fp_key(p, self.rect_pos, self.rect_size, px, vec2(1.5, 0.0), 3.0, 0.0, 0.0, 0.0, 0.0, 1.0, wd)
|
|
}
|
|
mod.widgets.Tabs.draw_sep.pixel = fn() {
|
|
return vec4(0.0, 0.0, 0.0, 0.0)
|
|
}
|
|
mod.widgets.Tabs.draw_text.color = #13212d
|
|
mod.widgets.Tabs.draw_text_active.color = #8fe8fc
|
|
mod.widgets.Tabs.draw_text_active.text_style = mod.theme.font_bold{font_size: mod.theme.font_size_p}
|
|
mod.widgets.Tabs.draw_mark.color = #e6eff5
|
|
|
|
// The dock's tab: the same keys.
|
|
mod.widgets.Tab.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let wd = fp_wd(self.world, self.pos * self.rect_size, self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(0.0, 0.0, 0.0, 1.0)), self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(1.0, 1.0, 0.0, 1.0)))
|
|
return fp_key(self.pos * self.rect_size, self.rect_pos, self.rect_size, px, vec2(1.5, 1.5), 3.0, self.hover, 0.0, 0.0, self.active, 1.0, wd)
|
|
}
|
|
mod.widgets.Tab.draw_text.color = #13212d
|
|
mod.widgets.Tab.draw_text.color_hover = #0b1720
|
|
mod.widgets.Tab.draw_text.color_active = #8fe8fc
|
|
|
|
// The radio tab: a key, pushed in when chosen.
|
|
mod.widgets.RadioButtonTabFlat.clip_x = false
|
|
mod.widgets.RadioButtonTabFlat.clip_y = false
|
|
mod.widgets.RadioButtonTabFlat.draw_bg.vertex = fn() {
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(5.0, 5.0), self.rect_size + vec2(10.0, 10.0))
|
|
}
|
|
mod.widgets.RadioButtonTabFlat.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * (self.rect_size + vec2(10.0, 10.0)) - vec2(5.0, 5.0)
|
|
let wd = fp_wd(self.world, p, self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(0.0, 0.0, 0.0, 1.0)), self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(1.0, 1.0, 0.0, 1.0)))
|
|
let live = 1.0 - self.disabled
|
|
return fp_key(p, self.rect_pos, self.rect_size, px, vec2(1.0, 0.0), 3.0, self.hover, self.down, self.focus, self.active, live, wd)
|
|
}
|
|
mod.widgets.RadioButtonTabFlat.draw_text.color = #13212d
|
|
mod.widgets.RadioButtonTabFlat.draw_text.color_hover = #0b1720
|
|
mod.widgets.RadioButtonTabFlat.draw_text.color_focus = #0b1720
|
|
mod.widgets.RadioButtonTabFlat.draw_text.color_down = #0b1720
|
|
mod.widgets.RadioButtonTabFlat.draw_text.color_active = #8fe8fc
|
|
mod.widgets.RadioButtonTabFlat.draw_text.color_disabled = #5d6b78
|
|
|
|
// SEGMENTED. The strip is one raised bar of the plastic; the chosen segment
|
|
// is pushed into it, the bar cut on either side of it so its neighbours
|
|
// stand as caps of their own. Segments 22 tall in a 26 point control.
|
|
mod.widgets.SegmentedControl.segment_height = 22.0
|
|
mod.widgets.SegmentedControlFlat.segment_height = 22.0
|
|
mod.widgets.SegmentedControlFlat.draw_bg.vertex = fn() {
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(6.0, 6.0), self.rect_size + vec2(12.0, 12.0))
|
|
}
|
|
mod.widgets.SegmentedControlFlat.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * (self.rect_size + vec2(12.0, 12.0)) - vec2(6.0, 6.0)
|
|
let wd = fp_wd(self.world, p, self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(0.0, 0.0, 0.0, 1.0)), self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(1.0, 1.0, 0.0, 1.0)))
|
|
let b = fp_snap(self.rect_pos, self.rect_size, px)
|
|
return fp_plate(p, b, vec4(3.5, 3.5, 3.5, 3.5), px, 0.0, 0.0, 0.0, 0.0, 1.0, 0.8, 1.0, vec4(0.0, 0.0, 0.0, 0.0), fp_rimc(), fp_acc_line(), 0.8, 0.0, 0.0, wd)
|
|
}
|
|
mod.widgets.SegmentedControlFlat.draw_pill.vertex = fn() {
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(4.0, 4.0), self.rect_size + vec2(8.0, 8.0))
|
|
}
|
|
mod.widgets.SegmentedControlFlat.draw_pill.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * (self.rect_size + vec2(8.0, 8.0)) - vec2(4.0, 4.0)
|
|
let o = self.rect_pos
|
|
let b = fp_snap(o + vec2(0.0, -1.0), self.rect_size + vec2(0.0, 2.0), px)
|
|
// The cuts: a dark joint a point and a half wide at each end, with the
|
|
// neighbour's lit end beside it.
|
|
let iny = step(b.y, p.y) * step(p.y, b.w)
|
|
let jl = step(b.x - 1.5, p.x) * step(p.x, b.x)
|
|
let jr = step(b.z, p.x) * step(p.x, b.z + 1.5)
|
|
let el = step(b.x - 1.5 - px, p.x) * step(p.x, b.x - 1.5)
|
|
let er = step(b.z + 1.5, p.x) * step(p.x, b.z + 1.5 + px)
|
|
var f = vec4(fp_ink() * 0.9, 0.9) * (jl + jr) * iny
|
|
f = Finish.over(f, vec4(vec3(0.62, 0.74, 0.82), 1.0) * (el + er) * iny * 0.8)
|
|
return Finish.over(f, fp_sunk(p, b, 2.0, px, 0.0, 1.0))
|
|
}
|
|
mod.widgets.SegmentedControlFlat.draw_text.color = #13212d
|
|
mod.widgets.SegmentedControlFlat.draw_text_selected.color = #8fe8fc
|
|
// The chosen word's bloom: its own glyphs laid wider in cyan at 40 %
|
|
// under it.
|
|
mod.widgets.SegmentedControlFlat.draw_text_selected.pixel = fn() {
|
|
let m0 = self.sample_text_pixel()
|
|
let main = vec4(vec3(0.561, 0.910, 0.988) * m0.a, m0.a)
|
|
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.40 * c.a
|
|
return main + vec4(0.25 * a, 0.71 * a, 0.88 * a, 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.40 * c.a
|
|
return main + vec4(0.25 * a, 0.71 * a, 0.88 * a, a) * (1.0 - main.a)
|
|
}
|
|
return main
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// THE GROUND under everything: the window's own face, in two depths, all laid
|
|
// in window points. Dark slate falling from #202b36 at the head to #0b1116 at
|
|
// the foot, a pale haze behind the upper middle where the controls stand, a
|
|
// very slow cloud.
|
|
// FAR: a circuit of buses of one to five traces twelve points apart, with 45
|
|
// degree jogs, seen out of focus some way behind the frosted sheet: each
|
|
// trace a soft band (a point of core fading over four, sigma 2.6), pads and
|
|
// vias soft discs with a bloom round them, a wide dim halo along the buses,
|
|
// and soft spots of light further back.
|
|
// NEAR: the earlier ground's lanes, sharp-ish and faint: fine lanes every 26
|
|
// and 42 points cut into runs that end in small ring pads, a line of a
|
|
// point and a half, lightly softened (sigma 0.6), gathered where a slow cloud lies. A grain that does not repeat.
|
|
// A near lane: `u` along it, `v` across. Lanes every `pitch` points, each
|
|
// jogged by a hash; cut in cells `cw` long, each on or off by a hash; a run
|
|
// ends in a ring pad. `sg` is the line's softness. Returns (line, pad, the
|
|
// lane's brightness).
|
|
let fp_lane = fn(u: float, v: float, sg: float, pitch: float, cw: float, seed: float, thr: float) -> vec3 {
|
|
let k = floor(v / pitch)
|
|
let lv = k * pitch + pitch * 0.5 + (floor(Finish.hash(vec2(k * 1.713 + seed, seed * 3.1 + 0.37)) * 5.0) - 2.0) * 3.0
|
|
let i = floor(u / cw)
|
|
let kk = k * 2.113 + seed * 5.3
|
|
let on = step(thr, Finish.hash(vec2(i * 1.371 + seed, kk)))
|
|
let onl = step(thr, Finish.hash(vec2((i - 1.0) * 1.371 + seed, kk)))
|
|
let onr = step(thr, Finish.hash(vec2((i + 1.0) * 1.371 + seed, kk)))
|
|
let x0 = i * cw + mix(4.0, 0.0, onl)
|
|
let x1 = (i + 1.0) * cw - mix(4.0, 0.0, onr)
|
|
let dl = max(abs(v - lv), max(x0 - u, u - x1))
|
|
let line = exp(0.0 - dl * dl / (2.0 * sg * sg)) * on
|
|
let pl = length(vec2(u - (i * cw + 2.0), v - lv))
|
|
let pr = length(vec2(u - ((i + 1.0) * cw - 2.0), v - lv))
|
|
let ring = min(abs(pl - 1.6) + (1.0 - on * (1.0 - onl)) * 9.0, abs(pr - 1.6) + (1.0 - on * (1.0 - onr)) * 9.0)
|
|
let pad = exp(0.0 - ring * ring / (2.0 * sg * sg))
|
|
let bright = step(0.78, Finish.hash(vec2(k * 0.917 + seed * 1.9, 4.41)))
|
|
return vec3(line, pad, bright)
|
|
}
|
|
// Whether cell (i, j) of a far bus carries its run: like fp_cell, but runs
|
|
// reach the upper middle too, fewer there.
|
|
let fp_fcell = fn(i: float, j: float, cw: float, rh: float, seed: float, ws: vec2, vert: float) -> float {
|
|
let cc = vec2((i + 0.5) * cw, (j + 0.5) * rh)
|
|
let q = mix(cc, cc.yx, vert)
|
|
let dn = fp_dens(q, ws)
|
|
return step(mix(0.80, 0.38, dn), Finish.hash(vec2(i * 0.917 + seed * 1.31, j * 1.531 + seed * 0.77 + 7.7)))
|
|
}
|
|
// A far bus: fp_bus drawn for the far depth, its traces `s` points apart
|
|
// (one to five of them) so they stay apart when blurred, lanes stepping by
|
|
// 8 points. Returns (distance to a line, distance to a pad centre,
|
|
// brightness, 0).
|
|
let fp_fbus = fn(u0: float, v: float, cw: float, rh: float, seed: float, ws: vec2, vert: float, s: float) -> vec4 {
|
|
let j = floor(v / rh)
|
|
let u = u0 + Finish.hash(vec2(j * 0.37 + seed, 4.2)) * cw
|
|
let i = floor(u / cw)
|
|
let on = fp_fcell(i, j, cw, rh, seed, ws, vert)
|
|
let onl = fp_fcell(i - 1.0, j, cw, rh, seed, ws, vert)
|
|
let onr = fp_fcell(i + 1.0, j, cw, rh, seed, ws, vert)
|
|
let nb = floor(Finish.hash(vec2(j * 0.713 + seed, seed * 1.37 + 2.9)) * 4.99)
|
|
let yl = (j + 0.5) * rh + (floor(Finish.hash(vec2(i * 1.371 + seed, j * 2.113 - seed * 0.53)) * 7.0) - 3.0) * 8.0 - nb * s * 0.5
|
|
let yr = (j + 0.5) * rh + (floor(Finish.hash(vec2((i + 1.0) * 1.371 + seed, j * 2.113 - seed * 0.53)) * 7.0) - 3.0) * 8.0 - nb * s * 0.5
|
|
let x0 = i * cw + mix(8.0, -0.5, onl)
|
|
let x1 = (i + 1.0) * cw - mix(8.0, -0.5, onr)
|
|
let xm = (i + 0.32 + 0.36 * Finish.hash(vec2(i * 3.17 + seed, j * 0.61 + 1.9))) * cw
|
|
let q = vec2(u, v)
|
|
// The nearest of the parallel traces: the one whose lane is closest.
|
|
let kc = clamp(floor((v - mix(yl, yr, step(xm, u))) / s + 0.5), 0.0, nb)
|
|
let d0 = fp_jog(q - vec2(0.0, kc * s), x0, x1, xm, yl, yr)
|
|
let da = fp_jog(q - vec2(0.0, clamp(kc - 1.0, 0.0, nb) * s), x0, x1, xm, yl, yr)
|
|
let db = fp_jog(q - vec2(0.0, clamp(kc + 1.0, 0.0, nb) * s), x0, x1, xm, yl, yr)
|
|
let dl = mix(99.0, min(d0, min(da, db)), on)
|
|
let kl = clamp(floor((v - yl) / s + 0.5), 0.0, nb)
|
|
let pl = length(vec2(u - x0, v - yl - kl * s)) + (1.0 - on * (1.0 - onl)) * 99.0
|
|
let kr = clamp(floor((v - yr) / s + 0.5), 0.0, nb)
|
|
let pr = length(vec2(u - x1, v - yr - kr * s)) + (1.0 - on * (1.0 - onr)) * 99.0
|
|
let vh = Finish.hash(vec2(i * 2.31 + seed, j * 1.77 + 3.3))
|
|
let pv = length(vec2(u - (x1 - 16.0), v - yr)) + (1.0 - on * step(0.70, vh)) * 99.0
|
|
let t = u / cw - i
|
|
let bi = Finish.hash(vec2(i * 0.53 + seed, j * 0.29 + 1.1))
|
|
let bn = Finish.hash(vec2((i + 1.0) * 0.53 + seed, j * 0.29 + 1.1))
|
|
let bp = Finish.hash(vec2((i - 1.0) * 0.53 + seed, j * 0.29 + 1.1))
|
|
let bb = mix(mix(bp, bi, t + 0.5), mix(bi, bn, t - 0.5), step(0.5, t))
|
|
let hot = step(0.80, Finish.hash(vec2(j * 0.37 + seed * 2.1, 8.8)))
|
|
return vec4(dl, min(pl, min(pr, pv)), 0.35 + 0.40 * bb * bb + 0.30 * hot, 0.0)
|
|
}
|
|
// The far circuit at window point w: (trace, pad, halo, 0), each about 0..1.
|
|
// A trace is a gaussian of sigma 2.6 points, a pad a soft disc of radius 3
|
|
// with a bloom of sigma 5.5 round it.
|
|
let fp_far = fn(w: vec2, ws: vec2) -> vec4 {
|
|
let h = fp_fbus(w.x, w.y, 176.0, 120.0, 1.0, ws, 0.0, 12.0)
|
|
let v = fp_fbus(w.y, w.x, 190.0, 136.0, 7.0, ws, 1.0, 12.0)
|
|
let dn = 0.45 + 0.55 * fp_dens(w, ws)
|
|
let s2 = 2.0 * 2.6 * 2.6
|
|
let lh = exp(0.0 - h.x * h.x / s2) * h.z
|
|
let lv = exp(0.0 - v.x * v.x / s2) * v.z
|
|
let rh = max(h.y - 2.0, 0.0)
|
|
let rv = max(v.y - 2.0, 0.0)
|
|
let ph = (exp(0.0 - rh * rh / s2) + 0.55 * exp(0.0 - h.y * h.y / 60.0)) * h.z
|
|
let pv = (exp(0.0 - rv * rv / s2) + 0.55 * exp(0.0 - v.y * v.y / 60.0)) * v.z
|
|
let gh = exp(0.0 - h.x * h.x / 72.0) * h.z
|
|
let gv = exp(0.0 - v.x * v.x / 72.0) * v.z
|
|
return vec4(max(lh, lv) * dn, max(ph, pv) * dn, max(gh, gv) * dn, 0.0)
|
|
}
|
|
let fp_ground = fn(p: vec2, size: vec2, px: float) -> vec3 {
|
|
let sz = max(size, vec2(1.0, 1.0))
|
|
let t = clamp(p.y / sz.y, 0.0, 1.0)
|
|
var col = mix(vec3(0.125, 0.170, 0.212), vec3(0.043, 0.066, 0.086), smoothstep(0.0, 1.0, t))
|
|
let l = length((p - vec2(0.5 * sz.x, 0.22 * sz.y)) / vec2(0.46 * sz.x, 0.44 * sz.y))
|
|
col = mix(col, vec3(0.235, 0.298, 0.349), 0.50 * (1.0 - smoothstep(0.0, 1.0, l)))
|
|
col = col * (0.98 + 0.04 * Finish.fbm(p / 180.0))
|
|
// FAR.
|
|
let fb = fp_far(p, sz)
|
|
col = col + vec3(0.16, 0.50, 0.66) * fb.z * 0.14
|
|
col = col + vec3(0.30, 0.66, 0.80) * min(fb.x, 1.0) * 0.19
|
|
col = col + vec3(0.40, 0.76, 0.90) * min(fb.y, 1.2) * 0.16
|
|
let bc = floor(p / 110.0)
|
|
let bh = Finish.hash(bc * 1.137 + vec2(2.9, 5.3))
|
|
let bp = (bc + vec2(0.2, 0.2) + 0.6 * vec2(Finish.hash(bc + vec2(7.1, 1.3)), Finish.hash(bc + vec2(3.7, 9.9)))) * 110.0
|
|
let bd = length(p - bp)
|
|
let br = 7.0 + 9.0 * Finish.hash(bc + vec2(5.5, 2.2))
|
|
let spot = exp(0.0 - bd * bd / (br * br)) * step(0.55, bh)
|
|
col = col + vec3(0.25, 0.62, 0.78) * spot * 0.22
|
|
// NEAR.
|
|
let mask = smoothstep(0.40, 0.70, Finish.fbm(p / 240.0 + vec2(3.1, 7.7)))
|
|
let sg = 0.6
|
|
let hl = fp_lane(p.x, p.y, sg, 26.0, 34.0, 1.0, 0.55)
|
|
let vl = fp_lane(p.y, p.x, sg, 42.0, 30.0, 7.0, 0.72)
|
|
let ln = max(hl.x * (0.6 + 0.8 * hl.z), vl.x * (0.6 + 0.8 * vl.z))
|
|
let pd = max(hl.y * (0.6 + 0.8 * hl.z), vl.y * (0.6 + 0.8 * vl.z))
|
|
let a = (ln * 0.17 + pd * 0.24) * (0.30 + 0.70 * mask)
|
|
col = mix(col, vec3(0.37, 0.80, 0.92), a)
|
|
let g = fp_grain(p / px, 3.0 / 255.0)
|
|
return col + vec3(g, g, g)
|
|
}
|
|
mod.widgets.Window.show_bg = true
|
|
mod.widgets.Window.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
return vec4(fp_ground(self.pos * self.rect_size, self.rect_size, px), 1.0)
|
|
}
|
|
|
|
// SEPARATORS: a single hairline of white at 10 %, eight points short of each
|
|
// end; no dark partner line, which would make a groove.
|
|
mod.widgets.Hr.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * self.rect_size
|
|
let y = Finish.snap(self.rect_pos.y + self.rect_size.y * 0.5, px) - self.rect_pos.y
|
|
let l = step(y, p.y) * step(p.y, y + px) * step(8.0, p.x) * step(p.x, self.rect_size.x - 8.0) * 0.10
|
|
return vec4(fp_white() * l, l)
|
|
}
|
|
mod.widgets.Vr.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * self.rect_size
|
|
let x = Finish.snap(self.rect_pos.x + self.rect_size.x * 0.5, px) - self.rect_pos.x
|
|
let l = step(x, p.x) * step(p.x, x + px) * step(8.0, p.y) * step(p.y, self.rect_size.y - 8.0) * 0.10
|
|
return vec4(fp_white() * l, l)
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// SURFACES. A panel is a large slab of the plastic, corners of 8: the body
|
|
// #627789 at the head to #465b6d at the foot at 88 %, so the traces behind
|
|
// it show through a little; a pale shoulder band three points wide lit
|
|
// along the head; a frost sheen over its head; the rim pale on top; along
|
|
// its foot the lit edge, quieter than a control's; a drop four points down
|
|
// and the dark contact line. An inset panel is a well of dark plastic sunk
|
|
// into the page, darker than a panel, a shadow falling from its head and a
|
|
// pale lip at its foot.
|
|
let fp_panel = fn(p: vec2, o: vec2, s: vec2, px: float, r: float, sunken: float, lift: float, frost: float, wd: vec4) -> vec4 {
|
|
let b = fp_snap(o, s, px)
|
|
let lo = b.xy
|
|
let hi = b.zw
|
|
let c = (lo + hi) * 0.5
|
|
let h = (hi - lo) * 0.5
|
|
let d = Material.sd_box(p, c, h, r)
|
|
let yn = clamp((p.y - lo.y) / max(hi.y - lo.y, 1.0), 0.0, 1.0)
|
|
let xn = clamp((p.x - lo.x) / max(hi.x - lo.x, 1.0), 0.0, 1.0)
|
|
let inside = Finish.cover(d, px)
|
|
let outside = 1.0 - inside
|
|
let raised = 1.0 - sunken
|
|
let fa = Finish.drop(p, c, h, r, vec2(0.0, 3.0), 4.0) * 0.50 * raised * lift
|
|
let ct = Finish.ring_out(d, 0.0, px, px) * mix(0.40, 0.10, sunken)
|
|
let lipo = Finish.ring_out(d, 0.0, px, px) * smoothstep(0.6, 0.95, yn) * 0.30 * sunken
|
|
let sh = (1.0 - (1.0 - fa) * (1.0 - ct)) * outside
|
|
var f = vec4(fp_ink() * sh, sh)
|
|
f = Finish.over(f, vec4(vec3(0.75, 0.88, 0.95) * lipo, lipo))
|
|
// The light under the foot, on the ground.
|
|
let ug = Finish.drop(p, c + vec2(0.0, 2.0), h - vec2(8.0, 1.5), r, vec2(0.0, 0.0), 2.0) * 0.30 * raised * frost * outside
|
|
f = Finish.over(f, vec4(mix(fp_acc_line(), vec3(1.0, 1.0, 1.0), 0.2) * ug, ug))
|
|
let depth = 0.0 - d
|
|
// The raised body.
|
|
var col = mix(vec3(0.322, 0.412, 0.486), vec3(0.263, 0.341, 0.408), smoothstep(0.0, 1.0, yn))
|
|
// What lies behind shows through the frost: the same traces, blurred,
|
|
// quieter in the middle where the words are.
|
|
let wp = wd.xy + p + vec2(0.0, 1.5)
|
|
let cb = fp_circuit(wp, wd.zw, px, 1.0)
|
|
let edgy = 1.0 - smoothstep(4.0, 22.0, depth)
|
|
let seen = mix(0.35, 1.0, edgy) * mix(1.0, 0.30, sunken)
|
|
col = col * (1.0 + (Finish.fbm(wp / 22.0) - 0.5) * 0.10)
|
|
col = col + vec3(0.20, 0.62, 0.78) * (cb.x * 0.26 + cb.y * 0.20 + cb.z * 0.14) * seen
|
|
col = col + vec3(0.40, 0.78, 0.90) * cb.w * 0.14 * seen
|
|
let sho = Finish.cover(depth - 3.5, px)
|
|
let headw = 1.0 - smoothstep(0.0, 0.08, yn)
|
|
col = mix(col, vec3(0.66, 0.77, 0.84), sho * mix(0.80, 0.42, smoothstep(0.0, 0.1, yn)))
|
|
col = mix(col, vec3(0.30, 0.40, 0.48), Finish.band(3.5 - depth, 0.0, px, px) * 0.5)
|
|
col = mix(col, vec3(0.90, 0.96, 0.99), exp(0.0 - max(depth, 0.0) / 1.2) * headw * 0.45)
|
|
col = mix(col, vec3(1.0, 1.0, 1.0), (1.0 - smoothstep(0.0, 0.18, yn)) * 0.07 * (1.0 - sho))
|
|
let foot = smoothstep(0.85, 1.0, yn)
|
|
col = col + fp_acc_line() * exp(0.0 - max(depth, 0.0) / 3.5) * foot * 0.30 * frost
|
|
let rb = Finish.band(d, 0.0, px, px)
|
|
col = mix(col, vec3(0.86, 0.94, 0.98), rb * mix(0.75, 0.35, smoothstep(0.0, 0.3, yn)))
|
|
col = mix(col, mix(fp_acc_line(), vec3(1.0, 1.0, 1.0), 0.5), rb * foot * 0.6 * frost)
|
|
let ra = mix(0.97, 0.25, 1.0 - min(frost, 1.0))
|
|
var g = vec4(col * ra, ra)
|
|
// The sunken body.
|
|
var sc = mix(vec3(0.180, 0.235, 0.282), vec3(0.235, 0.298, 0.349), yn)
|
|
sc = sc + vec3(0.20, 0.62, 0.78) * (cb.x * 0.30 + cb.y * 0.2 + cb.z * 0.18)
|
|
let ish = Finish.inset(p, c, h, r, vec2(0.0, 2.5), 3.0)
|
|
sc = mix(sc, vec3(0.0, 0.0, 0.0), ish * 0.6)
|
|
sc = mix(sc, vec3(0.0, 0.0, 0.0), rb * (1.0 - smoothstep(0.0, 0.1, yn)) * 0.5)
|
|
sc = mix(sc, vec3(0.62, 0.74, 0.82), rb * smoothstep(0.6, 1.0, yn) * 0.35)
|
|
g = mix(g, vec4(sc * 0.94, 0.94), sunken)
|
|
let gr = fp_grain(p / px, 0.9 / 255.0) * g.a
|
|
g = vec4(g.rgb + vec3(gr, gr, gr), g.a)
|
|
return Finish.over(f, g * inside)
|
|
}
|
|
mod.widgets.PanelView.clip_x = false
|
|
mod.widgets.PanelView.clip_y = false
|
|
mod.widgets.PanelView.draw_bg.vertex = fn() {
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(14.0, 14.0), self.rect_size + vec2(28.0, 28.0))
|
|
}
|
|
mod.widgets.PanelView.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * (self.rect_size + vec2(28.0, 28.0)) - vec2(14.0, 14.0)
|
|
let wd = fp_wd(self.world, p, self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(0.0, 0.0, 0.0, 1.0)), self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(1.0, 1.0, 0.0, 1.0)))
|
|
return fp_panel(p, self.rect_pos, self.rect_size, px, 6.0, self.sunken, 1.0, 1.0, wd)
|
|
}
|
|
// A card is a panel too: elevated it floats with the far shadow; filled
|
|
// it lies on the page with a denser frost; outlined it is the rim alone.
|
|
// A pressable one lifts under the pointer.
|
|
mod.widgets.Card.radius = 6.0
|
|
// The panel's pad on every side, and the title on the body's left edge.
|
|
mod.widgets.Card.padding = mod.turtle.Inset{left: 12.0 right: 12.0 top: 10.0 bottom: 12.0}
|
|
mod.widgets.CardHeader.padding = mod.turtle.Inset{left: 5.0 right: 0.0 top: 0.0 bottom: 0.0}
|
|
mod.widgets.Card.clip_x = false
|
|
mod.widgets.Card.clip_y = false
|
|
mod.widgets.Card.draw_bg.vertex = fn() {
|
|
self.vertex_pos = self.clip_and_transform_vertex(self.rect_pos - vec2(14.0, 14.0), self.rect_size + vec2(28.0, 28.0))
|
|
}
|
|
mod.widgets.Card.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * (self.rect_size + vec2(28.0, 28.0)) - vec2(14.0, 14.0)
|
|
let wd = fp_wd(self.world, p, self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(0.0, 0.0, 0.0, 1.0)), self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(1.0, 1.0, 0.0, 1.0)))
|
|
let lift = clamp(self.shadow_radius / 4.0, 0.0, 1.0)
|
|
let outlined = step(0.01, self.border_size) * (1.0 - lift)
|
|
let frost = mix(mix(1.5, 1.0, lift), 0.0, outlined)
|
|
let f = fp_panel(p, self.rect_pos, self.rect_size, px, self.border_radius, 0.0, lift, frost, wd)
|
|
let c = self.rect_size * 0.5
|
|
let d = Material.sd_box(p, c, c, self.border_radius)
|
|
let layer = (0.05 * self.hover - 0.03 * self.down) * Finish.cover(d, px)
|
|
return Finish.over(f, vec4(fp_white() * max(layer, 0.0), max(layer, 0.0)))
|
|
}
|
|
// The rounded view keeps its caller's colours; its edge is a true device
|
|
// pixel and its fill is dithered.
|
|
mod.widgets.RoundedView.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * self.rect_size
|
|
let lower = vec2(self.border_inset.x, self.border_inset.y)
|
|
let upper = self.rect_size - vec2(self.border_inset.z, self.border_inset.w)
|
|
let c = (lower + upper) * 0.5
|
|
let h = max((upper - lower) * 0.5, vec2(0.5, 0.5))
|
|
let d = Material.sd_box(p, c, h, min(2.0 * max(1.0, self.border_radius), min(h.x, h.y)))
|
|
var fill = self.color
|
|
if self.color_2.x > -0.5 {
|
|
let dir = if self.gradient_fill_horizontal > 0.5 self.pos.x else self.pos.y
|
|
fill = mix(self.color, self.color_2, dir)
|
|
}
|
|
var stroke = self.border_color
|
|
if self.border_color_2.x > -0.5 {
|
|
let dir = if self.gradient_border_horizontal > 0.5 self.pos.x else self.pos.y
|
|
stroke = mix(self.border_color, self.border_color_2, dir)
|
|
}
|
|
let g = fp_grain(p / px, 1.0 / 255.0) * fill.a
|
|
var col = vec4(fill.rgb * fill.a + vec3(g, g, g), fill.a)
|
|
let bw = max(self.border_size, px) * step(0.001, self.border_size)
|
|
let edge = Finish.band(d, 0.0, bw, px) * stroke.a
|
|
col = mix(col, vec4(stroke.rgb, 1.0), edge)
|
|
return col * Finish.cover(d, px)
|
|
}
|
|
|
|
// LIST ROWS. 24 points, contiguous, no separators: the row under the hand
|
|
// is a film of the plastic at 8 %; the chosen row the plastic at 22 % with
|
|
// a pale head rim, and a small lit bar at its left end, lit from within.
|
|
mod.widgets.ListItem.height = 24.0
|
|
mod.widgets.ListItem.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * self.rect_size
|
|
let b = fp_snap(self.rect_pos + vec2(3.0, 1.0), self.rect_size - vec2(6.0, 2.0), px)
|
|
let c = (b.xy + b.zw) * 0.5
|
|
let h = (b.zw - b.xy) * 0.5
|
|
let d = Material.sd_box(p, c, h, 3.0)
|
|
let live = 1.0 - self.disabled
|
|
let wa = max(0.08 * self.hover + 0.05 * self.down, 0.22 * self.selected) * live
|
|
var f = vec4(vec3(0.55, 0.66, 0.74) * wa, wa)
|
|
let top = Finish.band(d, 0.0, px, px) * (1.0 - smoothstep(0.0, 0.3, (p.y - b.y) / max(b.w - b.y, 1.0))) * 0.35 * self.selected * live
|
|
f = Finish.over(f, vec4(vec3(0.86, 0.94, 0.98) * top, top))
|
|
f = f * Finish.cover(d, px)
|
|
return fp_beam(f, p, vec2(b.x + 3.0, c.y - h.y * 0.45), vec2(b.x + 3.0, c.y + h.y * 0.45), 2.0, px, self.selected * live, fp_acc())
|
|
}
|
|
mod.widgets.ListItem.draw_text.color = #eef6fb
|
|
|
|
// The value field beside a dial is a small frost readout: the recessed
|
|
// frost of a field, the number in the base monospace, no glow.
|
|
mod.widgets.RotaryFlat.text_input.min_height = 0.0
|
|
mod.widgets.RotaryFlat.text_input.padding = mod.turtle.Inset{left: 5.0 right: 5.0 top: 3.0 bottom: 3.0}
|
|
mod.widgets.RotaryFlat.text_input.draw_text.color = #aeeefc
|
|
mod.widgets.RotaryFlat.text_input.draw_text.color_hover = #c8f4ff
|
|
mod.widgets.RotaryFlat.text_input.draw_text.color_focus = #c8f4ff
|
|
mod.widgets.RotaryFlat.text_input.draw_text.color_down = #c8f4ff
|
|
mod.widgets.RotaryFlat.text_input.draw_text.text_style = mod.theme.font_code{font_size: 8.0}
|
|
mod.widgets.RotaryFlat.text_input.draw_selection.color = #0000
|
|
mod.widgets.RotaryFlat.text_input.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let p = self.pos * (self.rect_size + vec2(12.0, 12.0)) - vec2(6.0, 6.0)
|
|
let wd = fp_wd(self.world, p, self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(0.0, 0.0, 0.0, 1.0)), self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(1.0, 1.0, 0.0, 1.0)))
|
|
return fp_field_dark(p, self.rect_pos, self.rect_size, px, 3.0, self.hover, self.focus, self.disabled, wd)
|
|
}
|
|
// The value inside a slider is bare: the field row's height must not reach
|
|
// it, or its text drops below the groove's line.
|
|
mod.widgets.SliderMinimal.text_input.min_height = 0.0
|
|
mod.widgets.SliderFlat.text_input.min_height = 0.0
|
|
mod.widgets.SliderRoundFlat.text_input.min_height = 0.0
|
|
// The bar sliders' values in the pale cyan of the screens.
|
|
mod.widgets.SliderMinimal.text_input.draw_text.color = #aeeefc
|
|
mod.widgets.SliderFlat.text_input.draw_text.color = #aeeefc
|
|
mod.widgets.SliderRoundFlat.text_input.draw_text.color = #bfd0dc
|
|
|
|
// CHIPS AND BADGES. A chip is a small pill of the plastic; the pointer
|
|
// lifts it, a press settles it. A badge is a plastic pill too, tinted a
|
|
// little by its meaning, with the near-white ink.
|
|
let fp_pill = fn(p: vec2, o: vec2, s: vec2, px: float, tint: vec4, hover: float, down: float, wd: vec4) -> vec4 {
|
|
let b = fp_snap(o + vec2(0.5, 0.5), s - vec2(1.0, 1.0), px)
|
|
let r = (b.w - b.y) * 0.5
|
|
return fp_plate(p, b, vec4(r, r, r, r), px, hover, down, 0.0, 0.0, 1.0, 0.6, 1.0, tint, fp_rimc(), fp_acc_line(), 0.6, 0.0, 0.0, wd)
|
|
}
|
|
mod.widgets.ChipFlat.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let wd = fp_wd(self.world, self.pos * self.rect_size, self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(0.0, 0.0, 0.0, 1.0)), self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(1.0, 1.0, 0.0, 1.0)))
|
|
let a = self.color.a * 0.25
|
|
return fp_pill(self.pos * self.rect_size, self.rect_pos, self.rect_size, px, vec4(self.color.rgb * a, a), self.hover, self.down, wd)
|
|
}
|
|
mod.widgets.BadgeFlat.palette.neutral = #3a5262
|
|
mod.widgets.BadgeFlat.palette.on_neutral = #13212d
|
|
mod.widgets.ChipFlat.palette.on_neutral_container = #13212d
|
|
mod.widgets.ChipFlat.palette.on_neutral = #13212d
|
|
mod.widgets.BadgeFlat.draw_bg.pixel = fn() {
|
|
let px = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let wd = fp_wd(self.world, self.pos * self.rect_size, self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(0.0, 0.0, 0.0, 1.0)), self.draw_pass.camera_projection * (self.draw_pass.camera_view * vec4(1.0, 1.0, 0.0, 1.0)))
|
|
let a = self.color.a * 0.30
|
|
return fp_pill(self.pos * self.rect_size, self.rect_pos, self.rect_size, px, vec4(self.color.rgb * a, a), 0.0, 0.0, wd)
|
|
}
|
|
// The spinner keeps its own turning face, in the accent.
|
|
mod.widgets.SpinnerFlat.draw_bg.color = #56d4f2
|
|
|
|
|
|
// ---------------------------------------------------------------- band
|
|
|
|
// THE BAND is the sheet's own slider with two thumbs: the same track, the
|
|
// same lit value and the same thumb as the Slider, the value lit between
|
|
// the two 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 = 6.0
|
|
mod.widgets.RangeSliderFlat.handle_size = 14.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 = 1.0 / max(self.draw_pass.dpi_factor, 0.5)
|
|
let o = self.rect_pos
|
|
let p = self.pos * (self.rect_size + vec2(2.0 * m, 2.0 * m)) - vec2(m, m)
|
|
let live = 1.0 - self.disabled
|
|
let room = max(self.rect_size.y - self.label_px, 1.0)
|
|
let y = Finish.snap(o.y + self.label_px + room * 0.5, px) - o.y
|
|
// The slot: a recessed well of darker plastic, as the slider's.
|
|
let x0 = Finish.snap(o.x + 6.0, px) - o.x
|
|
let x1 = Finish.snap(o.x + self.rect_size.x - 6.0, px) - o.x
|
|
let gd = Finish.sd_pill(p, vec2((x0 + x1) * 0.5, y), vec2((x1 - x0) * 0.5, 2.5))
|
|
var out = fp_well_h(gd, clamp((p.y - y + 2.5) / 5.0, 0.0, 1.0), px, 0.55, 0.30, 0.12, 0.5)
|
|
// The span lit between the thumbs, a beam of the accent, and the two
|
|
// thumbs the sheet's glass beads.
|
|
let travel = max(self.rect_size.x - 12.0 - 14.0, 1.0)
|
|
let k0 = 13.0 + self.start_pos * travel
|
|
let k1 = 13.0 + self.end_pos * travel
|
|
out = fp_beam(out, p, vec2(k0, y), vec2(k1, y), 2.0, px, mix(0.85, 0.25, self.disabled), fp_acc())
|
|
out = fp_bead(out, p, vec2(k0, y), 7.0, px, self.hot_start, self.drag * self.hot_start, self.disabled, 0.0)
|
|
return fp_bead(out, p, vec2(k1, y), 7.0, px, self.hot_end, self.drag * self.hot_end, self.disabled, 0.0)
|
|
}
|
|
|
|
// ---------------------------------------------------------------- switches and meter
|
|
|
|
// THE SWITCHES are the sheet's own toggle, in proportion with the
|
|
// labels and the controls beside them: the rocker and the slide switch
|
|
// take the toggle's face, its size and shape, its quad and its label
|
|
// gap (the library's own faces for them are flat boxes sized for
|
|
// another face). The rocker turns on a click; the slide switch follows a
|
|
// drag, as the toggle does.
|
|
mod.widgets.ToggleRocker.label_walk.margin.left = 28.0
|
|
mod.widgets.ToggleSlide.label_walk.margin.left = 28.0
|
|
mod.widgets.ToggleRocker.draw_bg.size = uniform(16.0)
|
|
mod.widgets.ToggleSlide.draw_bg.size = uniform(16.0)
|
|
mod.widgets.ToggleRocker.draw_bg.pill_aspect = uniform(1.75)
|
|
mod.widgets.ToggleSlide.draw_bg.pill_aspect = uniform(1.75)
|
|
mod.widgets.ToggleRocker.draw_bg.vertex = mod.widgets.ToggleFlat.draw_bg.vertex
|
|
mod.widgets.ToggleSlide.draw_bg.vertex = mod.widgets.ToggleFlat.draw_bg.vertex
|
|
mod.widgets.ToggleRocker.clip_x = false
|
|
mod.widgets.ToggleSlide.clip_x = false
|
|
mod.widgets.ToggleRocker.clip_y = false
|
|
mod.widgets.ToggleSlide.clip_y = false
|
|
mod.widgets.ToggleRocker.draw_bg.pixel = mod.widgets.ToggleFlat.draw_bg.pixel
|
|
mod.widgets.ToggleSlide.draw_bg.pixel = mod.widgets.ToggleFlat.draw_bg.pixel
|
|
|
|
// THE METER sits in the same window as the readouts and the screen: the
|
|
// library's display housing drawn round its face -- the bezel ring, a shade
|
|
// of whatever ground it is set in with a device pixel of dark at its edge;
|
|
// the face with the surround's shadow falling across it from the top and a
|
|
// device pixel of black where the glass meets the bezel; the glass's quiet
|
|
// diagonal band and the light line inside its top edge; the lip of light on
|
|
// the ground under it -- with the same corner, so the meter and the
|
|
// readouts read as one family. The scale, the red zone, the needle and its
|
|
// pivot are the library's own.
|
|
mod.widgets.NeedleMeter.draw_bg.border_radius = uniform(theme.corner_radius * 0.5)
|
|
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 b = Finish.snap(2.0, px)
|
|
let hf = max(ho - vec2(b, b), vec2(0.5, 0.5))
|
|
let rf = max(r_out - b, 0.0)
|
|
let face_d = Material.sd_box(p, c, hf, rf)
|
|
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)
|
|
var face = self.color.rgb
|
|
// The scale: a hairline arc and a tick at every division.
|
|
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))
|
|
face = mix(face, self.color_scale.rgb, max(arc, 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, self.color_zone.rgb, zone_t * zone_r)
|
|
}
|
|
// The needle, from under the pivot cover to just past the scale.
|
|
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 needle = Finish.cover(across - px * 0.5, px) * Finish.cover(along - (rr + 3.0), px) * Finish.cover(0.0 - along - max(self.pivot_size - px, 0.0), px)
|
|
face = mix(face, self.color_needle.rgb, needle)
|
|
let pd = r - self.pivot_size
|
|
face = mix(face, mix(self.color_needle.rgb, self.color.rgb, 0.35), Finish.cover(pd, px))
|
|
face = mix(face, face * 0.55, Finish.band(pd, 0.0, px, px))
|
|
// The housing: the surround's shadow across the face from the top, the
|
|
// black device pixel at the glass's edge, and the glass over it all.
|
|
let insh = Finish.inset(p, c, hf, rf, vec2(0.0, 2.0), 1.8)
|
|
face = face * (1.0 - 0.55 * insh)
|
|
face = mix(face, face * 0.4, 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))
|
|
let top = Finish.cover(abs(p.y - (c.y - hf.y) - px * 1.5) - px * 0.5, px) * 0.6
|
|
let ga = clamp(0.03 * (band + top), 0.0, 1.0) * Finish.cover(face_d + px, px)
|
|
face = face * (1.0 - ga) + vec3(ga, ga, ga)
|
|
let bz = self.color_bezel
|
|
var col = vec4(bz.rgb * bz.a, bz.a)
|
|
col = Finish.over(col, vec4(0.0, 0.0, 0.0, 0.45 * Finish.band(outer, 0.0, px, px)))
|
|
col = mix(col, vec4(face, 1.0), Finish.cover(face_d, px))
|
|
col = col * Finish.cover(outer, px)
|
|
let lip = Finish.ring_out(outer, 0.0, px, px) * step(hi.y, p.y)
|
|
let la = self.color_lip.a * lip
|
|
return Finish.over(vec4(self.color_lip.rgb * la, la), col) * self.color.a * self.opacity
|
|
}
|
|
|
|
// ---------------------------------------------------------------- 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 fp_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 fp_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 fp_led_lamp_hue = fn(ink: vec3, vivid: float) -> vec3 {
|
|
let f = fp_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 fp_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 = fp_led_lamp_full(ink)
|
|
let hue = fp_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 = fp_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, here in a ring of the same moulded plastic.
|
|
// 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
|
|
let c = self.rect_size * 0.5
|
|
let half_len = max((self.rect_size.x - 2.0 * room) * 0.5, t * 0.5)
|
|
// The lens is the lamp's size; its socket stands round it in the room.
|
|
let hb = vec2(mix(t * 0.5, half_len, self.bar), t * 0.5)
|
|
let res = fp_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.071, 0.102, 0.133), max(room - 0.5, 2.0), vec3(0.25, 0.30, 0.36), 1.0)
|
|
return res * self.opacity
|
|
}
|
|
true
|