// Phone-first session-0 document. All dimensions are logical points: Android // dp = pixels / (densityDpi / 160), desktop points = pixels / backing scale. // The host supplies real work, measured phase durations and the shell environment. use mod.pod.* use mod.math.* use mod.shader.* let rounded = fn(p: vec2, size: vec2, radius: float) -> float { let q = abs(p - size * 0.5) - size * 0.5 + vec2(radius) return length(max(q, vec2(0.0))) + min(max(q.x, q.y), 0.0) - radius } // A triangular dither of +-1 step of an 8-bit target (two taps of interleaved gradient noise at // pixel `px`), added to every smooth gradient before the target quantises it: no bands at 8 bpp. let dither = fn(px: vec2) -> float { let k = vec2(0.06711056, 0.00583715) let a = fract(52.9829189 * fract(dot(px, k))) let b = fract(52.9829189 * fract(dot(px.yx + vec2(47.0, 19.0), k))) return (a + b - 1.0) / 255.0 } // ---- the Ignition Field: every star, dust grain and flare is one instance of one pipeline. // Instance: rect = (angle, radius, size, born), info = (kind, arm or dust order, twinkle + 1, 0). // kinds: 0 star, 1 dust, 2 O3 star, 3 main() glow, 4 swap ring, 5 corner brackets, 6 backdrop, // 7 warm-up arc. A star twinkles (a short white glint) each time a function of it is compiled: // info.z is that moment + 1 (0: never). // Colours are the concept's tokens: ink #ece6ff, base #9d8cff, O3 #ffcf6a, dim #6e6593, bg #08060f; // failure is ember #ff7a5c. A halted field (`fault` 1: compile errors, a parked panic) stops // turning, its stars cool to dim, the core glows ember and the frame's brackets turn ember. let project = fn(a: float, r: float, view: vec4, spin: float) -> vec2 { return vec2(view.x + cos(a + spin) * r * view.z, view.y + sin(a + spin) * r * view.z * view.w) } // coverage of an axis-aligned square of half-size h (points) around the origin, anti-aliased at pixel scale let box_cov = fn(d: vec2, h: float, dp: float) -> float { let q = vec2(h * dp + 0.5) - abs(d) * dp return clamp(q.x, 0.0, 1.0) * clamp(q.y, 0.0, 1.0) } // coverage of a one-pixel stroke along a segment of the x axis [-l, l] let bar = fn(d: vec2, l: float, dp: float) -> float { let e = length(vec2(d.x - clamp(d.x, -l, l), d.y)) return clamp((0.5 - e) * dp + 0.5, 0.0, 1.0) } let star_px = fn(d: vec2, size: float, age: float, hot: float, scale: float, dp: float, fault: float, twinkle: float) -> vec4 { let body = box_cov(d, size * scale * 0.5, dp) * mix(mix(0.42, 0.8, hot), 0.34, fault) let f = clamp(1.0 - age / 0.16, 0.0, 1.0) * (1.0 - fault) let flash = box_cov(d, 1.1 * scale, dp) * 0.45 * f // a compile's glint: a four-point sparkle that shrinks away in 0.45 s let t = clamp(1.0 - twinkle / 0.45, 0.0, 1.0) * step(0.0, twinkle) * (1.0 - fault) let tl = (4.5 * t + 1.0) * scale let spark = max(bar(d, tl, dp), bar(vec2(d.y, d.x), tl, dp)) * t * t * 0.85 let core = box_cov(d, 0.9 * scale, dp) * t * 0.7 // over, not additive: a dense arm tends to the arm colour instead of burning to white; // the birth flash and the glint are the only additive parts let c = mix(#9d8cff, #8a7fb8, fault) * body + #ffffff * (flash + max(spark, core)) return vec4(c.rgb, body) } let dust_px = fn(d: vec2, landed: float, fade: float, dp: float) -> vec4 { let cover = box_cov(d, 0.6, dp) * fade let c = mix(#ece6ff * 0.5, #6e6593 * 0.22, landed) return vec4(c.rgb * cover, 0.0) } let o3_px = fn(d: vec2, age: float, scale: float, dp: float, fault: float) -> vec4 { let f = clamp(1.0 - age / 0.5, 0.0, 1.0) * (1.0 - fault) let l = (6.0 * f + 2.0) * scale let arms = max(bar(d, l, dp), bar(vec2(d.y, d.x), l, dp)) * f let core = box_cov(d, scale, dp) * 0.85 let c = #ffcf6a * max(arms, core) return vec4(c.rgb, 0.0) } // main()'s ignition glow; a halted field glows ember at its core instead let glow_px = fn(d: vec2, main_age: float, radius: float, fault: float, px: vec2) -> vec4 { let f = clamp(main_age / 0.5, 0.0, 1.0) let reach = radius * (0.15 + 0.4 * f) let g = clamp(1.0 - length(d) / max(reach, 1.0), 0.0, 1.0) let a = (0.5 * (1.0 - f) + 0.12) * g * step(0.0, main_age) * (1.0 - fault) let e = clamp(1.0 - length(d) / max(radius * 0.42, 1.0), 0.0, 1.0) let ea = e * e * 0.55 * fault let c = mix(#9d8cff, #ffffff, g) * a + mix(#ff7a5c, #ffd2c4, e * e) * ea let alpha = max(a, ea) return vec4(c.rgb + vec3(dither(px) * step(0.0001, alpha)), alpha) } let ring_px = fn(d: vec2, swap_age: float, view: vec4, px: vec2) -> vec4 { let f = clamp(swap_age, 0.0, 1.0) let rr = view.z * (0.9 + f * 0.6) let e = length(vec2(d.x, d.y / view.w)) let grad = length(vec2(d.x, d.y / (view.w * view.w))) / max(e, 0.001) let dist = abs(e - rr) / max(grad, 0.001) let a = clamp(1.0 - (dist - 0.75) / 0.75, 0.0, 1.0) * (1.0 - f) * 0.7 * step(0.0, swap_age) let c = #ece6ff * a return vec4(c.rgb + vec3(dither(px) * step(0.0001, a)), 0.0) } // the warm-up arc around the galaxy: the share of the functions queued to build ahead after the // first present that are built, from the top clockwise over a faint track; at `resolve` >= 0 (s // since the app is ready) it closes, flares and widens away let arc_px = fn(d: vec2, view: vec4, progress: float, resolve: float, dp: float, px: vec2) -> vec4 { let r = clamp(resolve, 0.0, 1.0) * step(0.0, resolve) let rr = view.z * (1.16 + 0.10 * r * r) let e = length(vec2(d.x, d.y / view.w)) let grad = length(vec2(d.x, d.y / (view.w * view.w))) / max(e, 0.001) let dist = abs(e - rr) / max(grad, 0.001) let line = clamp(1.0 - (dist * dp - 0.6) / 1.2, 0.0, 1.0) let soft = clamp(1.0 - dist / 7.0, 0.0, 1.0) let f = fract(atan2(d.x, -d.y / view.w) / 6.2831853 + 1.0) let p = max(progress, r * 4.0) let done = clamp((p - f) * 400.0, 0.0, 1.0) let head = clamp(1.0 - abs(f - p) * 30.0, 0.0, 1.0) * (1.0 - step(1.0, p)) let flare = (1.0 - r) * step(0.0, resolve) let a = (line * (0.10 + 0.62 * done + 0.28 * head) + soft * soft * (0.10 * head + 0.35 * flare * done)) * (1.0 - r * r) * step(0.0, progress) let c = mix(#9d8cff, #ece6ff, max(head, flare)) * a return vec4(c.rgb + vec3(dither(px) * step(0.0001, a)), 0.0) } // corner brackets of the frame (half size `half`), ember when the field is halted let brackets_px = fn(d: vec2, half: vec2, dp: float, fault: float) -> vec4 { let e = half - vec2(10.0) - abs(d) let l = 16.0 let ex = length(vec2(e.x - clamp(e.x, 0.0, l), e.y)) let ey = length(vec2(e.x, e.y - clamp(e.y, 0.0, l))) let a = clamp((0.5 - min(ex, ey)) * dp + 0.5, 0.0, 1.0) * 0.8 let c = mix(#6e6593, #ff7a5c, fault) * a return vec4(c.rgb, 0.0) } // the deep field behind everything: a faint haze around the galaxy (ember when halted), rounded // to `corner` points (0: the halted view takes the whole window, over the app too); dithered, it // is the one large gradient of the window let backdrop_px = fn(d: vec2, half: vec2, rel: vec2, view: vec4, fault: float, corner: float, dp: float, px: vec2) -> vec4 { let q = length(vec2(rel.x, rel.y / max(view.w, 0.3))) / max(view.z * 1.7, 1.0) let h = exp(-2.4 * q * q) let haze = mix(#150f2c, #2a0d12, fault) * h let c = #08060f + haze let cover = clamp(0.5 - rounded(d + half, half * 2.0, corner) * dp, 0.0, 1.0) return vec4((c.rgb + vec3(dither(px))) * cover, cover) } // a window button of the caption, drawn as the stock caption's (widgets' DesktopButton): the // glyph a one-pixel line in a 9-point square, the face lit on hover and press; close lights red. // glyph: 0 minimise, 1 maximise / restore, 2 close // stock: 1 on the app's caption ground (under a makepad WM): drawn as the app's own stock // buttons there (widgets' window.rs `windows_buttons`): glyph in `tint` at rest, hover face // #e9e9e9 (pressed #cccccc) with a black glyph, close #e81123 (pressed #f1707a) with a white one. let caption_button_px = fn(p: vec2, s: vec2, glyph: float, hover: float, down: float, stock: float, tint: vec4, dp: float, px: vec2) -> vec4 { let d = p - s * 0.5 let h = 4.5 let minimise = bar(d, h, dp) let q = abs(d) let outline = clamp((0.5 - abs(max(q.x, q.y) - h)) * dp + 0.5, 0.0, 1.0) * box_cov(d, h + 1.0, dp) let r = vec2(d.x + d.y, d.x - d.y) * 0.70710678 let cross = max(bar(r, h * 1.41421356, dp), bar(vec2(r.y, r.x), h * 1.41421356, dp)) let close = step(1.5, glyph) let g = mix(mix(minimise, outline, step(0.5, glyph)), cross, close) let lit = max(hover, down) let field_a = mix(mix(0.0, 0.10, hover), 0.18, down) * (1.0 - close) + mix(mix(0.0, 1.0, hover), 0.75, down) * close let face_a = mix(field_a, lit, stock) let face = mix(mix(#ece6ff, mix(#e9e9e9, #cccccc, down), stock), mix(#e81123, #f1707a, down), close) let field_ink = mix(#ece6ff * 0.85, #ffffff, close * lit) let stock_ink = mix(tint, mix(#000000, #ffffff, close), lit) let ink = mix(field_ink, stock_ink, stock) var col = face.rgb * face_a var alpha = face_a col = col * (1.0 - g) + ink.rgb * g alpha = alpha * (1.0 - g) + g return vec4(col + vec3(dither(px) * step(0.001, alpha)), alpha) } // a traffic light of the caption under a macOS-like window manager, drawn as the app's own // caption draws it there (widgets' DesktopButton MacClose / MacMin / MacMax in a 20-point // square): a disc of radius 6 in its colour, darker pressed, grey in a window without focus // unless hovered; its glyph (1.1-point strokes) shows on hover. glyph: 3 close, 4 minimise, // 5 maximise. focus: the window has focus. Premultiplied. let caption_light_px = fn(p: vec2, s: vec2, glyph: float, hover: float, down: float, focus: float, dp: float) -> vec4 { let d = p - s * 0.5 // the soft edge of Sdf2d with `aa * 3` (widgets' DesktopButton) let aa = dp * 2.12132034 let disc = clamp((6.0 - length(d)) * aa, 0.0, 1.0) let close = 1.0 - step(3.5, glyph) let maxi = step(4.5, glyph) let mini = 1.0 - close - maxi let lit = mix(#ff5f57, #e0443e, down) * close + mix(#febc2e, #d9a020, down) * mini + mix(#28c840, #1fa733, down) * maxi let face = mix(#b5b5b5, lit, max(focus, hover)) let ink = #4d0000 * close + #985700 * mini + #006500 * maxi let r = vec2(d.x + d.y, d.x - d.y) * 0.70710678 let l = 2.6 * 1.41421356 let cross = min(length(vec2(r.x - clamp(r.x, -l, l), r.y)), length(vec2(r.y - clamp(r.y, -l, l), r.x))) let dash = length(vec2(d.x - clamp(d.x, -3.2, 3.2), d.y)) let plus = min(length(vec2(d.x - clamp(d.x, -3.0, 3.0), d.y)), length(vec2(d.y - clamp(d.y, -3.0, 3.0), d.x))) let e = cross * close + dash * mini + plus * maxi let g = clamp((1.1 - e) * aa, 0.0, 1.0) * hover let col = ink.rgb * g + face.rgb * disc * (1.0 - g) return vec4(col, g + disc * (1.0 - g)) } // the JIT ring in the running app's caption (makepad run; ring.rs), in its 24 pt badge: a ring // 14 pt across that moves whenever the JIT builds. A head with a fading tail turns around it // (spin: its turn so far) over a faint track; under it a progress arc from the top covers `fill` // of the ring (0: none). mode 0: work the program waits for -- bright, a 2 pt stroke; 1: // background tier-up -- the same motion, dimmer and 1.6 pt (0..1 blends them); 2 / 3: the program // is stopped on invalidated code / a panic -- a solid amber / red dot. hover 0..1 lifts it on a // faint disc. ink 1: on a dark caption, 0: on a light one. a: opacity. Premultiplied. GPU-only. let jit_ring_px = fn(p: vec2, s: vec2, fill: float, mode: float, a: float, spin: float, hover: float, ink: float, dp: float, px: vec2) -> vec4 { let d = p - s * 0.5 let r = length(d) let dot = step(1.5, mode) let back = clamp(mode, 0.0, 1.0) * (1.0 - dot) // the ring's centre line and stroke let rad = 6.0 let w = mix(2.0, 1.6, back) let band = clamp((w * 0.5 - abs(r - rad)) * dp + 0.5, 0.0, 1.0) // where this pixel is on the ring: turns clockwise from the top let t = fract(atan2(d.x, 0.0 - d.y) / 6.2831853 + 1.0) let arc_px = 6.2831853 * rad * dp // the progress arc: from the top to `fill` (quiet: the head is what says busy) let f = clamp(fill, 0.0, 1.0) let on_fill = clamp((f - t) * arc_px + 0.5, 0.0, 1.0) * step(0.004, f) // the head and its tail: full at the head, fading over `tail` turns behind it, a little // wider than the stroke let tail = 0.35 let behind = fract(fract(spin) - t + 1.0) let head_in = clamp(behind * arc_px + 0.5, 0.0, 1.0) let fade = clamp(1.0 - behind / tail, 0.0, 1.0) let head = head_in * fade * fade let head_band = clamp(((w + 0.8) * 0.5 - abs(r - rad)) * dp + 0.5, 0.0, 1.0) let ink_c = mix(#2a2638, #ece6ff, ink) let arc_c = mix(#5b4fd0, #b8acff, ink) let head_c = mix(#4433d6, #d4ccff, ink) let track_a = mix(0.10, 0.13, ink) let fill_a = mix(mix(0.26, 0.3, ink), mix(0.18, 0.22, ink), back) let head_a = mix(1.0, 0.8, back) var col = ink_c.rgb * track_a * band var alpha = track_a * band let fa = band * on_fill * fill_a col = col * (1.0 - fa) + arc_c.rgb * fa alpha = alpha * (1.0 - fa) + fa let ha = head_band * head * head_a col = col * (1.0 - ha) + head_c.rgb * ha alpha = alpha * (1.0 - ha) + ha // the dot (replaces the ring) col = col * (1.0 - dot) alpha = alpha * (1.0 - dot) let dot_c = mix(#f5a524, #e5484d, step(2.5, mode)) let disc = clamp((5.0 - r) * dp + 0.5, 0.0, 1.0) * dot let rim = clamp((0.6 - abs(r - 4.6)) * dp, 0.0, 1.0) * dot * (1.0 - ink) * 0.35 col = col + dot_c.rgb * (1.0 - rim) * disc alpha = alpha + disc // the hover disc under it all let lift = clamp((10.5 - r) * dp + 0.5, 0.0, 1.0) * hover * mix(0.09, 0.14, ink) col = col + ink_c.rgb * lift * (1.0 - alpha) alpha = alpha + lift * (1.0 - alpha) return vec4(col * a, alpha * a) } // the JIT ring's tooltip plate (its line is drawn over it): a rounded plate with a hairline. let jit_tip_px = fn(p: vec2, s: vec2, a: float, ink: float, dp: float) -> vec4 { let e = rounded(p, s, 5.0) let inside = clamp(0.5 - e * dp, 0.0, 1.0) let hair = clamp(1.0 - abs(e + 0.5) * dp, 0.0, 1.0) let plate = mix(#ffffff, #1f1b2e, ink) let line = mix(vec4(0.0, 0.0, 0.0, 0.16), vec4(1.0, 1.0, 1.0, 0.14), ink) let pa = inside * mix(0.97, 0.95, ink) var col = plate.rgb * pa var alpha = pa let h = hair * line.w col = col * (1.0 - h) + line.rgb * h alpha = alpha * (1.0 - h) + h return vec4(col * a, alpha * a) } let Quad = { geom: vertex_buffer(vec2f) rect: instance(vec4(0.0)) clip_rect: instance(vec4(0.0)) viewport: uniform(vec2(1.0)) dpi: uniform(1.0) pos: varying(vec2f) vertex_pos: vertex_position(vec4f) fb0: fragment_output(0, vec4f) vertex: fn() { self.pos = self.geom let p = self.rect.xy + self.geom * self.rect.zw self.vertex_pos = vec4(p.x / self.viewport.x * 2.0 - 1.0, 1.0 - p.y / self.viewport.y * 2.0, 0.0, 1.0) } fragment: fn() { let p = self.rect.xy + self.pos * self.rect.zw let clip = if self.clip_rect.z > 0.0 { self.clip_rect } else { vec4(0.0, 0.0, self.viewport.x, self.viewport.y) } let inside = step(clip.x, p.x) * step(clip.y, p.y) * step(p.x, clip.x + clip.z) * step(p.y, clip.y + clip.w) self.fb0 = self.pixel() * inside } } { // the halted view's card sizes, in the boot window and over the app (Overlay: the halted // view takes the whole window too); margin / max_width / max_height / corner are not read boot: {margin: 22 max_width: 0 max_height: 0 corner: 0 card_padding: 12 action_height: 48 action_width: 152 action_gap: 8} overlay: {margin: 16 max_width: 900 max_height: 560 corner: 14 card_padding: 12 action_height: 48 action_width: 152 action_gap: 8} lines: [ {role: @app size: 10 height: 14 gap: 0 color: #6e6593} {role: @status size: 10 height: 14 gap: 6 color: #ff7a5c} {role: @current size: 13 height: 18 gap: 0 color: #ece6ff} {role: @domain size: 22 height: 28 gap: 4 color: #ece6ff} {role: @location size: 11 height: 16 gap: 4 color: #9d8cff} {role: @errors size: 14 height: 20 gap: 4 color: #ece6ff} {role: @panic size: 15 height: 21 gap: 4 color: #ffd2c4} {role: @hints size: 10 height: 15 gap: 0 color: #8a80b4} ] hints: { tap_restart: "Restart" tap_copy: "Copy" tap_open: "Open on dev machine" tap_dismiss: "Dismiss" tap_submit: "Submit" tap_cancel: "Cancel" tap_resume: "Resume" input_label: "Return value" paused: "Paused after a panic" halted: "Compile halted" follow_on: "follow-on" root_cause: "root cause" resolve: "Resolving dependencies" parse: "Reading source" typecheck: "Checking types" codegen: "Compiling" link: "Linking" first_frame: "Opening your app" restart: "R restart" retry: "R retry compile" copy: "C copy error" open: "E open in editor" dismiss: "Esc dismiss" errors: "↑/↓ select error" scroll: "PgUp/PgDn scroll" return_value: "Return value: " submit: "Enter submit · Esc cancel" } // a card of the halted view, a row of a collapsed group, a touch button, or a window button // of the caption. accent: 0 follow-on card, 1 root cause / panic card, 2 member row, 3 button, // 4 caption window button (action: its glyph, 3..5 a traffic light; selected: hover; // block: the window has focus), 7 the caption's ground (tint), 5 the JIT ring (selected: // its fill, block: its mode, pressed: opacity, action: its spin, beat: hover, ink: on a dark // caption), 6 the ring's tooltip plate (pressed: opacity, ink) error_band: Quad{ time: uniform(0.0) reduce_motion: uniform(0.0) selected: instance(0.0) action: instance(0.0) pressed: instance(0.0) accent: instance(0.0) block: instance(0.0) beat: instance(0.0) ink: instance(0.0) tint: instance(vec4(0.0)) pixel: fn() { let s = self.rect.zw let p = self.pos * s let dp = max(self.dpi, 1.0) let px = (self.rect.xy + p) * dp if self.accent > 6.5 { // the caption's ground: the app's caption bar colour (`tint`, opaque) return vec4(self.tint.rgb * self.tint.a, self.tint.a) } if self.accent > 5.5 { return jit_tip_px(p, s, self.pressed, self.ink, dp) } if self.accent > 4.5 { return jit_ring_px(p, s, self.selected, self.block, self.pressed, self.action, self.beat, self.ink, dp, px) } if self.accent > 3.5 { if self.action > 2.5 { return caption_light_px(p, s, self.action, self.selected, self.pressed, self.block, dp) } return caption_button_px(p, s, self.action, self.selected, self.pressed, self.beat, self.tint, dp, px) } let row = step(1.5, self.accent) * (1.0 - step(2.5, self.accent)) let button = step(2.5, self.accent) let root = step(0.5, self.accent) * (1.0 - step(1.5, self.accent)) let edge = min(min(p.x, s.x - p.x), min(p.y, s.y - p.y)) let hair = clamp(1.0 - edge * dp, 0.0, 1.0) let c = min(p, s - p) let tick = clamp(1.6 - edge * dp, 0.0, 1.0) * step(c.x, 9.0) * step(c.y, 9.0) let bar = clamp((2.0 - p.x) * dp, 0.0, 1.0) * (1.0 - button) // fill let fill_a = mix(mix(mix(0.80, 0.90, self.selected), 0.10 * self.selected, row), mix(0.06, 0.24, self.pressed), button) let fill = mix(mix(mix(#0d0a1a, #171030, self.selected), #ece6ff, row), mix(#ece6ff, #9d8cff, self.pressed), button) var col = fill.rgb * fill_a var alpha = fill_a // hairline frame (cards and buttons), corner ticks on the selected card and buttons let line_a = hair * mix(mix(0.10, 0.20, self.selected), 0.0, row) + hair * button * 0.35 let tick_a = tick * max(self.selected * (1.0 - row), button) let accent = mix(#6e6593, #ff7a5c, max(root, self.selected * row)) col = col * (1.0 - line_a) + #ece6ff.rgb * line_a alpha = alpha * (1.0 - line_a) + line_a col = col * (1.0 - tick_a) + mix(#ece6ff, #ff7a5c, root).rgb * tick_a alpha = alpha * (1.0 - tick_a) + tick_a // the accent bar on the left: ember for a root cause, dim for follow-on, a selected row let bar_a = bar * mix(mix(0.9, 1.0, self.selected), self.selected, row) col = col * (1.0 - bar_a) + accent.rgb * bar_a alpha = alpha * (1.0 - bar_a) + bar_a return vec4(col + vec3(dither(px) * step(0.001, alpha)), alpha) } } ignition: { geom: vertex_buffer(vec2f) rect: instance(vec4(0.0)) info: instance(vec4(0.0)) viewport: uniform(vec2(1.0)) dpi: uniform(1.0) reduce_motion: uniform(0.0) time: uniform(0.0) view: uniform(vec4(0.0)) field_params: uniform(vec4(0.0)) field_fall: uniform(vec4(0.0)) // the frame the backdrop and brackets fill (points; zero: the window) and // (fault, corner radius, caption strip height, 0) field_frame: uniform(vec4(0.0)) field_fault: uniform(vec4(0.0)) // over a running app (the window layer): (on: 1, galaxy opacity, backdrop opacity, arc // progress or -1); zero: the boot window field_layer: uniform(vec4(0.0)) d: varying(vec2f) px: varying(vec2f) vertex_pos: vertex_position(vec4f) fb0: fragment_output(0, vec4f) vertex: fn() { let k = self.info.x let dust = step(0.5, k) * step(k, 1.5) let fall = clamp((self.field_fall.x - self.info.y) / 0.03, 0.0, 1.0) let eased = fall * fall * (3.0 - 2.0 * fall) let r = mix(self.rect.y, 0.92, eased * dust) let a = self.rect.x + dust * self.time * 0.07 * r * (1.0 - self.reduce_motion) * (1.0 - self.field_fault.x) let at = project(a, r, self.view, self.field_params.x) let vp = self.viewport let frame = if self.field_frame.z > 0.0 { self.field_frame } else { vec4(0.0, 0.0, vp.x, vp.y) } let center = if k < 2.5 { at } else if k < 4.5 { self.view.xy } else if k < 6.5 { frame.xy + frame.zw * 0.5 } else { self.view.xy } // a twinkling star reaches out to its glint; a layer's backdrop it does not draw is empty let twinkling = step(0.5, self.info.z) * step(self.time - self.info.z + 1.0, 0.45) let gone = step(0.5, self.field_layer.x) * (1.0 - step(0.001, self.field_layer.z)) * step(5.5, k) * (1.0 - step(6.5, k)) let arc = vec2(self.view.z * 1.45 + 10.0, self.view.z * 1.45 * self.view.w + 10.0) * step(-0.5, self.field_layer.w) * step(0.5, self.field_layer.x) let extent = if k < 0.5 { vec2(mix(3.5, 7.0 * self.field_params.y + 1.0, twinkling)) } else if k < 1.5 { vec2(2.0) } else if k < 2.5 { vec2(10.0 * self.field_params.y) } else if k < 3.5 { vec2(self.view.z) } else if k < 4.5 { vec2(self.view.z * 1.7 + 6.0, self.view.z * 1.7 * self.view.w + 6.0) * (1.0 - gone) } else if k < 6.5 { frame.zw * 0.5 * (1.0 - gone) } else { arc } let p = center + (self.geom * 2.0 - vec2(1.0)) * extent self.d = p - center self.px = p * self.dpi self.vertex_pos = vec4(p.x / vp.x * 2.0 - 1.0, 1.0 - p.y / vp.y * 2.0, 0.0, 1.0) } fragment: fn() { let k = self.info.x let dp = max(self.dpi, 1.0) let age = self.time - self.rect.w let scale = self.field_params.y let fault = self.field_fault.x let fall = clamp((self.field_fall.x - self.info.y) / 0.03, 0.0, 1.0) let hot = (1.0 - step(0.5, abs(self.info.y - self.field_fall.z))) * (1.0 - fault) let vp = self.viewport let frame = if self.field_frame.z > 0.0 { self.field_frame } else { vec4(0.0, 0.0, vp.x, vp.y) } let half = frame.zw * 0.5 let twinkle = if self.info.z > 0.5 { self.time - self.info.z + 1.0 } else { -1.0 } let layer = self.field_layer let on = step(0.5, layer.x) // over the app: the galaxy at its opacity, the backdrop at its own let opacity = if k > 5.5 && k < 6.5 { mix(1.0, layer.z, on) } else { mix(1.0, layer.y, on) } self.fb0 = opacity * if k < 0.5 { star_px(self.d, self.rect.z, age, hot, scale, dp, fault, twinkle) } else if k < 1.5 { dust_px(self.d, step(1.0, fall), self.field_fall.y, dp) } else if k < 2.5 { o3_px(self.d, age, scale, dp, fault) } else if k < 3.5 { glow_px(self.d, self.field_params.z, self.view.z, fault, self.px) } else if k < 4.5 { ring_px(self.d, self.field_params.w, self.view, self.px) } else if k < 5.5 { // the frame's brackets start below the caption strip (field_fault.z points) let below = vec2(0.0, self.field_fault.z * 0.5) brackets_px(self.d - below, half - below, dp, fault) } else if k < 6.5 { backdrop_px(self.d, half, frame.xy + half + self.d - self.view.xy, self.view, fault, self.field_fault.y, dp, self.px) } else { arc_px(self.d, self.view, layer.w, self.field_fall.w, dp, self.px) } } } // the running app's frame under the window layer (`image_texture`: the app image, or a blur // stage of it). mode 0: shown at `opacity`, `dim` darkens it towards the field's deep violet; // mode 1 / 2: one direction of the separable Gaussian (sigma `sigma` texels of the target // along `dir`, the source's texel size), the first from the full image at quarter size layer: Quad{ image_texture: texture_2d(float) opacity: instance(1.0) mode: instance(0.0) dim: instance(0.0) dir: instance(vec2(0.0)) sigma: instance(4.0) pixel: fn() { let uv = self.pos if self.mode < 0.5 { let c = self.image_texture.sample(uv) let px = (self.rect.xy + self.pos * self.rect.zw) * max(self.dpi, 1.0) let tone = mix(c.rgb, #0c0918.rgb * 0.6 + c.rgb * 0.4, self.dim) let n = vec3(dither(px) * step(0.001, self.dim)) return vec4((tone + n) * self.opacity, self.opacity) } let s = self.dir let q = 1.0 / (2.0 * self.sigma * self.sigma) let w1 = exp(-1.0 * q) let w2 = exp(-4.0 * q) let w3 = exp(-9.0 * q) let w4 = exp(-16.0 * q) let w5 = exp(-25.0 * q) let w6 = exp(-36.0 * q) let w7 = exp(-49.0 * q) let w8 = exp(-64.0 * q) var c = self.image_texture.sample(uv) c = c + (self.image_texture.sample(uv + s) + self.image_texture.sample(uv - s)) * w1 c = c + (self.image_texture.sample(uv + s * 2.0) + self.image_texture.sample(uv - s * 2.0)) * w2 c = c + (self.image_texture.sample(uv + s * 3.0) + self.image_texture.sample(uv - s * 3.0)) * w3 c = c + (self.image_texture.sample(uv + s * 4.0) + self.image_texture.sample(uv - s * 4.0)) * w4 c = c + (self.image_texture.sample(uv + s * 5.0) + self.image_texture.sample(uv - s * 5.0)) * w5 c = c + (self.image_texture.sample(uv + s * 6.0) + self.image_texture.sample(uv - s * 6.0)) * w6 c = c + (self.image_texture.sample(uv + s * 7.0) + self.image_texture.sample(uv - s * 7.0)) * w7 c = c + (self.image_texture.sample(uv + s * 8.0) + self.image_texture.sample(uv - s * 8.0)) * w8 let total = 1.0 + 2.0 * (w1 + w2 + w3 + w4 + w5 + w6 + w7 + w8) return vec4(c.rgb / total, 1.0) } } icon: Quad{ image_texture: texture_2d(float) pixel: fn() { let c = self.image_texture.sample(self.pos) return vec4(c.rgb * c.a, c.a) } } glyph: Quad{ ..mod.glyph_coverage.AtlasCoverage uv: instance(vec4(0.0)) atlas_plane: instance(0.0) color_glyph: instance(0.0) color: instance(#fff) grayscale_texture: texture_2d(float) radius: uniform(8.0) cutoff: uniform(0.25) sdf_sharpness: uniform(1.0) sdf_luma_bias: uniform(0.03) pixel: fn() { let t = mix(self.uv.xy, self.uv.zw, self.pos) let tex_size = self.grayscale_texture.size() let half_texel = vec2(0.5) / tex_size let p = clamp(t, self.uv.xy + half_texel, self.uv.zw - half_texel) if self.color_glyph > 0.5 { let c = self.grayscale_texture.sample(p) return vec4(c.rgb * c.a, c.a) } let scale = (length(dFdx(t) * tex_size) + length(dFdy(t) * tex_size)) * 0.5 let coverage = self.sdf(scale, p, self.color) return coverage * vec4(self.color.rgb * self.color.a, self.color.a) } } }