Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/dwarves

dwarves-gui/src/trace_replay.rs

//! **The 3D replay of a traced run** — the room plays a recorded execution back:
//! a function's head GLOWS while it is open on some thread (its colour the
//! thread's), each call runs as a PULSE along the spline from the caller's head,
//! and a TIMELINE under the map scrubs to any time (the open stacks of that
//! moment come from the server, `stacks: true`).
//!
//! The server side is `Viz.CodeMap` lod `trace_runs` (the traced runs of a
//! repo) and lod `trace` (a window of one run's events, each on a head of THIS
//! map, an entry with its caller's head). This module is the pure half: parsing,
//! the replay clock, and what is drawn — the tab holds a [`TraceUi`] and wires
//! the asks ([`crate::coderoom3d`]).

use egui::Color32;
use serde_json::Value;

use crate::coderoom3d::{arc_points, lift, Line, Quad, Scene};
use crate::room::RoomAnswer;

/// A pulse runs its spline in this many UI seconds.
pub const PULSE_S: f64 = 0.5;
/// A glow quad's half width (world units).
pub const GLOW_R: f32 = 4.5;
/// Events asked per window page.
pub const PAGE: u64 = 4096;

// ------------------------------------------------------------------ the answers

/// One traced run (lod `trace_runs`).
#[derive(Debug, Clone, PartialEq, Default)]
pub struct RunRow {
    pub run_id: String,
    pub target: String,
    pub commit: String,
    pub source: String,
    pub events: u64,
    pub threads: u64,
    pub duration_ns: u64,
    /// Of the run's events, how many landed on functions this room knows.
    pub mapped_events: u64,
}

/// A head of the trace answer: where a traced function stands on this map.
#[derive(Debug, Clone, PartialEq, Default)]
pub struct TraceHead {
    pub card: u64,
    /// Its head's place on the plane; the layout the window was asked with.
    pub at: [f32; 2],
    pub stable_id: String,
    pub path: String,
    pub symbol: String,
}

/// **Append a later page**: its heads are interned per answer, so each of its
/// events is re-pointed at the held window's heads (grown as needed).
pub fn append_page(held: &mut TraceView, page: TraceView) {
    let to_held: Vec<usize> = page
        .heads
        .into_iter()
        .map(|h| match held.heads.iter().position(|x| x.stable_id == h.stable_id) {
            Some(i) => i,
            None => {
                held.heads.push(h);
                held.heads.len() - 1
            }
        })
        .collect();
    let point = |i: usize| to_held.get(i).copied().unwrap_or(i);
    held.events.extend(page.events.into_iter().map(|e| TraceEvent { head: point(e.head), caller: e.caller.map(point), ..e }));
    held.unplaced += page.unplaced;
    held.more = page.more;
    held.next_after_seq = page.next_after_seq;
}

/// One event of the window: `[seq, t_ns, thread, exit, head, caller]`.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct TraceEvent {
    pub seq: u64,
    pub t_ns: u64,
    pub thread: u64,
    pub exit: bool,
    pub head: usize,
    /// An entry's caller head (the innermost placed fn open on its thread).
    pub caller: Option<usize>,
}

/// A landed window of one run.
#[derive(Debug, Clone, PartialEq, Default)]
pub struct TraceView {
    pub run_id: String,
    pub from_ns: u64,
    pub events: Vec<TraceEvent>,
    pub heads: Vec<TraceHead>,
    pub unplaced: u64,
    /// The window has more events past its last page.
    pub more: bool,
    pub next_after_seq: Option<u64>,
    /// Each thread's open heads at `from_ns` (outermost first), when asked.
    pub stacks: Vec<(u64, Vec<usize>)>,
}

/// The runs of lod `trace_runs`.
pub fn runs_of(a: &RoomAnswer) -> Result<Vec<RunRow>, String> {
    if !a.is_measured() {
        return Err(format!("[{}] {}", a.state, a.evidence_text()));
    }
    let s = |v: &Value| v.as_str().unwrap_or("").to_string();
    let n = |v: &Value| v.as_u64().unwrap_or(0);
    Ok(a.body["runs"]
        .as_array()
        .into_iter()
        .flatten()
        .map(|r| RunRow {
            run_id: s(&r["run_id"]),
            target: s(&r["target"]),
            commit: s(&r["commit"]),
            source: s(&r["source"]),
            events: n(&r["events"]),
            threads: n(&r["threads"]),
            duration_ns: n(&r["duration_ns"]),
            mapped_events: n(&r["mapped_events"]),
        })
        .collect())
}

/// The window of lod `trace`; pages after the first APPEND (`after_seq` asks).
pub fn trace_of(a: &RoomAnswer) -> Result<TraceView, String> {
    if !a.is_measured() {
        return Err(format!("[{}] {}", a.state, a.evidence_text()));
    }
    let b = &a.body;
    let s = |v: &Value| v.as_str().unwrap_or("").to_string();
    let heads = b["heads"]
        .as_array()
        .into_iter()
        .flatten()
        .map(|h| TraceHead {
            card: h["card"].as_u64().unwrap_or(0),
            at: [h["at"][0].as_f64().unwrap_or(0.0) as f32, h["at"][1].as_f64().unwrap_or(0.0) as f32],
            stable_id: s(&h["stable_id"]),
            path: s(&h["path"]),
            symbol: s(&h["symbol"]),
        })
        .collect();
    let events = b["events"]
        .as_array()
        .into_iter()
        .flatten()
        .filter_map(|e| {
            Some(TraceEvent {
                seq: e[0].as_u64()?,
                t_ns: e[1].as_u64()?,
                thread: e[2].as_u64()?,
                exit: e[3].as_u64()? == 1,
                head: e[4].as_u64()? as usize,
                caller: e[5].as_u64().map(|c| c as usize),
            })
        })
        .collect();
    let stacks = b["stacks"]
        .as_array()
        .into_iter()
        .flatten()
        .filter_map(|r| {
            Some((r["thread"].as_u64()?, r["heads"].as_array()?.iter().filter_map(|h| h.as_u64().map(|x| x as usize)).collect()))
        })
        .collect();
    Ok(TraceView {
        run_id: s(&b["run_id"]),
        from_ns: b["from_ns"].as_u64().unwrap_or(0),
        events,
        heads,
        unplaced: b["unplaced"].as_u64().unwrap_or(0),
        more: b["more"].as_bool().unwrap_or(false),
        next_after_seq: b["next_after_seq"].as_u64(),
        stacks,
    })
}

// ------------------------------------------------------------------ the replay

/// An open function on some thread: its head glows.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Glow {
    pub head: usize,
    pub thread: u64,
    pub since_ns: u64,
}

/// A call under way: a dot runs the spline caller → callee.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Pulse {
    pub from: usize,
    pub to: usize,
    pub thread: u64,
    /// UI seconds when it left.
    pub born: f64,
}

/// The replay's state on the tab.
#[derive(Default)]
pub struct TraceUi {
    /// The strip is shown (the 🎬 toggle).
    pub open: bool,
    pub repo: String,
    pub runs: Vec<RunRow>,
    pub runs_err: Option<String>,
    pub picked: Option<RunRow>,
    pub view: Option<TraceView>,
    pub err: Option<String>,
    pub playing: bool,
    /// Trace nanoseconds per UI second.
    pub speed: f32,
    /// The replay clock (trace time).
    pub t_ns: f64,
    /// Events applied so far (an index into the view's events).
    pub applied: usize,
    /// Every thread's open heads, in enter order.
    pub glows: Vec<Glow>,
    pub pulses: Vec<Pulse>,
}

/// A thread's colour: stable per id, never grey.
pub fn thread_color(thread: u64) -> Color32 {
    let h = thread.to_le_bytes().iter().fold(2166136261u32, |h, b| (h ^ *b as u32).wrapping_mul(16777619));
    // the golden-angle walk keeps near ids far apart in hue
    let hue = ((thread as f32 * 137.508) + (h % 360) as f32) % 360.0;
    Color32::from(egui::ecolor::Hsva::new(hue / 360.0, 0.8, 0.95, 1.0))
}

/// **Advance the replay** by `dt` UI seconds (at `now` on the UI clock): the
/// clock moves when playing, the events up to it are applied — an enter glows
/// its head and sends a pulse from its caller, an exit ends the glow — and
/// spent pulses go. True = the page is exhausted and more events exist (ask the
/// next page with `next_after_seq`).
pub fn advance(t: &mut TraceUi, dt: f64, now: f64) -> bool {
    if t.playing {
        t.t_ns += dt * t.speed as f64;
        if let Some(r) = &t.picked {
            t.t_ns = t.t_ns.min(r.duration_ns as f64);
        }
    }
    t.pulses.retain(|p| now - p.born < PULSE_S);
    let Some(v) = &t.view else { return false };
    while let Some(e) = v.events.get(t.applied) {
        if e.t_ns as f64 > t.t_ns {
            break;
        }
        if e.exit {
            if let Some(i) = t.glows.iter().rposition(|g| g.thread == e.thread && g.head == e.head) {
                t.glows.remove(i);
            }
        } else {
            t.glows.push(Glow { head: e.head, thread: e.thread, since_ns: e.t_ns });
            if let Some(from) = e.caller {
                if t.pulses.len() >= 128 {
                    t.pulses.remove(0);
                }
                t.pulses.push(Pulse { from, to: e.head, thread: e.thread, born: now });
            }
        }
        t.applied += 1;
    }
    t.applied == v.events.len() && v.more && v.next_after_seq.is_some()
}

/// **Scrub to `to_ns`**: the applied state is dropped, the clock set; the tab
/// asks a fresh window from here with `stacks: true` and seeds [`land_stacks`].
pub fn scrub(t: &mut TraceUi, to_ns: u64) {
    t.t_ns = to_ns as f64;
    t.applied = 0;
    t.glows.clear();
    t.pulses.clear();
    t.view = None;
}

/// The scrubbed-to window landed: its stacks become the open glows.
pub fn land_stacks(t: &mut TraceUi) {
    t.applied = 0;
    t.glows.clear();
    t.pulses.clear();
    let Some(v) = &t.view else { return };
    let at = v.from_ns;
    for (thread, heads) in &v.stacks {
        t.glows.extend(heads.iter().map(|&head| Glow { head, thread: *thread, since_ns: at }));
    }
}

/// **What the replay draws**: a translucent quad in the thread's colour on every
/// glowing head (the innermost of a thread brightest), and each pulse as a short
/// lit segment at its fraction of the arc caller → callee.
pub fn trace_scene(sc: &mut Scene, t: &TraceUi, now: f64) {
    let Some(v) = &t.view else { return };
    for (i, g) in t.glows.iter().enumerate() {
        let Some(h) = v.heads.get(g.head) else { continue };
        // the innermost of a thread (its last glow) brightest
        let top = !t.glows[i + 1..].iter().any(|x| x.thread == g.thread);
        let c = thread_color(g.thread);
        sc.quads.push(Quad {
            origin: [h.at[0] - GLOW_R, 0.07, h.at[1] - GLOW_R],
            u: [GLOW_R * 2.0, 0.0, 0.0],
            v: [0.0, 0.0, GLOW_R * 2.0],
            // the thread's rgb kept whole: only the alpha says inner/outer
            color: Color32::from_rgba_premultiplied(c.r(), c.g(), c.b(), if top { 140 } else { 77 }),
        });
    }
    for p in &t.pulses {
        let (Some(f), Some(to)) = (v.heads.get(p.from), v.heads.get(p.to)) else { continue };
        let frac = (((now - p.born) / PULSE_S) as f32).clamp(0.0, 1.0);
        let (a, b) = pulse_at(f.at, to.at, frac);
        sc.lines.push(Line { a, b, width_px: 3.5, color: thread_color(p.thread) });
    }
}

/// Where a pulse stands at `frac` of its arc (the scene test's oracle).
pub fn pulse_at(from: [f32; 2], to: [f32; 2], frac: f32) -> ([f32; 3], [f32; 3]) {
    let pts = arc_points(lift([from[0], 0.0, from[1]], 0.15), lift([to[0], 0.0, to[1]], 0.15), 24, 0.18);
    let k = ((frac * 24.0) as usize).min(23);
    (pts[k], pts[k + 1])
}

/// **The strip**: the traced runs of the repo (one row each — target, commit,
/// events, threads, length); a picked run gets ▶/⏸, the speed, the clock, and
/// the SCRUBBER — a bar over the run's length whose drag sets `scrubbed`.
/// `picked` lands a clicked run's id; refusals are said in place.
pub fn strip(ui: &mut egui::Ui, t: &mut TraceUi, picked: &mut Option<String>, scrubbed: &mut Option<u64>) {
    ui.horizontal_wrapped(|ui| {
        if let Some(e) = &t.runs_err {
            ui.label(egui::RichText::new(e.clone()).small().color(Color32::LIGHT_RED));
            return;
        }
        if t.runs.is_empty() {
            ui.label(egui::RichText::new("no traced runs of this repo yet — a Runa XRay Trace step files one").small());
            return;
        }
        for r in &t.runs {
            let label = format!("▶ {} · {} · {} ev · {} thr · {:.1} ms", r.target, &r.commit.get(..8).unwrap_or(&r.commit), r.events, r.threads, r.duration_ns as f64 / 1e6);
            let on = t.picked.as_ref().is_some_and(|p| p.run_id == r.run_id);
            if ui.selectable_label(on, label).on_hover_text(format!("{} of {} events land on this map", r.mapped_events, r.events)).clicked() && !on {
                *picked = Some(r.run_id.clone());
            }
        }
    });
    let Some(r) = t.picked.clone() else { return };
    ui.horizontal(|ui| {
        if ui.selectable_label(t.playing, if t.playing { "⏸" } else { "▶" }).clicked() {
            t.playing = !t.playing;
        }
        for (label, per_s) in [("slow", r.duration_ns as f32 / 60.0), ("1:10", r.duration_ns as f32 / 10.0), ("fast", r.duration_ns as f32 / 2.0)] {
            if ui.selectable_label((t.speed - per_s).abs() < 1.0, label).clicked() {
                t.speed = per_s;
            }
        }
        ui.label(egui::RichText::new(format!("{:.2} / {:.2} ms", t.t_ns / 1e6, r.duration_ns as f64 / 1e6)).small().monospace());
        if let Some(e) = &t.err {
            ui.label(egui::RichText::new(e.clone()).small().color(Color32::LIGHT_RED));
        }
        if let Some(v) = &t.view {
            if v.unplaced > 0 {
                ui.label(egui::RichText::new(format!("{} events off this map", v.unplaced)).small());
            }
        }
    });
    // the scrubber: the run's whole length; a drag sets the clock, a release asks the stacks
    let (resp, p) = ui.allocate_painter(egui::vec2(ui.available_width().max(80.0), 14.0), egui::Sense::click_and_drag());
    let rect = resp.rect;
    p.rect_filled(rect, 3.0, ui.visuals().extreme_bg_color);
    let frac = (t.t_ns / r.duration_ns.max(1) as f64).clamp(0.0, 1.0) as f32;
    let x = rect.left() + rect.width() * frac;
    p.rect_filled(egui::Rect::from_min_max(rect.min, egui::pos2(x, rect.bottom())), 3.0, ui.visuals().selection.bg_fill);
    p.line_segment([egui::pos2(x, rect.top()), egui::pos2(x, rect.bottom())], egui::Stroke::new(2.0, ui.visuals().strong_text_color()));
    if resp.dragged() || resp.clicked() {
        if let Some(at) = resp.interact_pointer_pos() {
            let f = ((at.x - rect.left()) / rect.width()).clamp(0.0, 1.0);
            t.t_ns = f as f64 * r.duration_ns as f64;
        }
    }
    if resp.drag_stopped() || resp.clicked() {
        *scrubbed = Some(t.t_ns as u64);
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use serde_json::json;

    fn answer(body: Value) -> RoomAnswer {
        RoomAnswer::from_body("Viz.CodeMap", "canned trace", body)
    }

    fn canned_runs() -> RoomAnswer {
        answer(json!({
            "state": "complete", "lod": "trace_runs", "repo": "shop",
            "runs": [{
                "run_id": "r-1", "repo": "shop", "commit": "bbbb1111", "target": "shop-bin", "source": "xray",
                "ts_micros": 1, "chunks": 1, "chunks_filed": 1,
                "events": 6, "total_events": 6, "truncated": false,
                "threads": 2, "duration_ns": 1_000_000,
                "functions": 3, "mapped_functions": 3, "local_functions": 3, "mapped_local_functions": 3,
                "mapped_events": 6
            }],
            "evidence": { "notes": [] }
        }))
    }

    /// Two threads: t0 enters checkout (seq 1), checkout calls total (2) and it
    /// returns (3); t1 enters render (4). total's enter carries checkout's head
    /// as the caller.
    fn canned_window(from_ns: u64, stacks: bool) -> RoomAnswer {
        let mut body = json!({
            "state": "complete", "lod": "trace", "run_id": "r-1",
            "from_ns": from_ns, "to_ns": null,
            "events": [
                [1u64, 100_000u64, 0u64, 0u8, 0usize, null],
                [2u64, 300_000u64, 0u64, 0u8, 1usize, 0usize],
                [3u64, 500_000u64, 0u64, 1u8, 1usize, null],
                [4u64, 600_000u64, 1u64, 0u8, 2usize, null]
            ],
            "unplaced": 1,
            "more": false,
            "next_after_seq": 4,
            "heads": [
                { "card": 0, "at": [15.0, 28.0], "stable_id": "f shop::checkout().", "path": "shop::checkout", "symbol": "checkout" },
                { "card": 1, "at": [26.5, 28.0], "stable_id": "f shop::total().", "path": "shop::total", "symbol": "total" },
                { "card": 9, "at": [40.0, 60.0], "stable_id": "f web::render().", "path": "web::render", "symbol": "render" }
            ],
            "evidence": { "notes": [] }
        });
        if stacks {
            body["stacks"] = json!([{ "thread": 0, "heads": [0] }]);
        }
        answer(body)
    }

    fn picked() -> TraceUi {
        let mut t = TraceUi { open: true, repo: "shop".into(), speed: 1_000_000.0, ..TraceUi::default() };
        t.runs = runs_of(&canned_runs()).unwrap();
        t.picked = Some(t.runs[0].clone());
        t.view = Some(trace_of(&canned_window(0, false)).unwrap());
        t
    }

    /// **The answers parse whole**: every run field the strip shows, every
    /// event with its caller, every head with its place.
    #[test]
    fn the_trace_answers_parse_whole() {
        let runs = runs_of(&canned_runs()).unwrap();
        assert_eq!(runs.len(), 1);
        let r = &runs[0];
        assert_eq!((r.run_id.as_str(), r.target.as_str(), r.events, r.threads, r.duration_ns, r.mapped_events), ("r-1", "shop-bin", 6, 2, 1_000_000, 6));
        let v = trace_of(&canned_window(0, true)).unwrap();
        assert_eq!(v.events.len(), 4);
        assert_eq!(v.unplaced, 1);
        assert_eq!(v.events[1], TraceEvent { seq: 2, t_ns: 300_000, thread: 0, exit: false, head: 1, caller: Some(0) });
        assert_eq!(v.heads[2].at, [40.0, 60.0]);
        assert_eq!(v.heads[1].path, "shop::total");
        assert_eq!(v.stacks, vec![(0, vec![0])]);
        assert_eq!(v.next_after_seq, Some(4));
        assert!(!v.more);
    }

    /// **The replay applies what the clock passed**: at 400 µs checkout and
    /// total glow on thread 0 (total entered with a pulse from checkout's
    /// head); at 550 µs total's exit ended its glow; at 700 µs render glows on
    /// thread 1 in ANOTHER colour.
    #[test]
    fn the_replay_glows_entered_heads_and_pulses_their_calls() {
        let mut t = picked();
        t.playing = true;
        assert!(!advance(&mut t, 0.4, 10.0), "no more pages");
        assert_eq!(t.applied, 2, "{:?}", t.glows);
        assert_eq!(t.glows.iter().map(|g| (g.head, g.thread)).collect::<Vec<_>>(), vec![(0, 0), (1, 0)]);
        assert_eq!(t.pulses.iter().map(|p| (p.from, p.to)).collect::<Vec<_>>(), vec![(0, 1)]);
        advance(&mut t, 0.15, 10.1);
        assert_eq!(t.glows.iter().map(|g| g.head).collect::<Vec<_>>(), vec![0], "total's exit ends its glow");
        advance(&mut t, 0.15, 10.2);
        assert_eq!(t.glows.iter().map(|g| (g.head, g.thread)).collect::<Vec<_>>(), vec![(0, 0), (2, 1)]);
        assert_ne!(thread_color(0), thread_color(1), "thread colours tell threads apart");
        // the pulse dies after its run; paused, the clock stands still
        advance(&mut t, 0.0, 10.0 + PULSE_S + 0.1);
        assert!(t.pulses.is_empty(), "a pulse lives {PULSE_S} UI seconds");
        t.playing = false;
        let at = t.t_ns;
        advance(&mut t, 1.0, 12.0);
        assert_eq!(t.t_ns, at, "paused");
    }

    /// **The clock never passes the run's end**, and a full page asks the next.
    #[test]
    fn the_clock_stops_at_the_end_and_a_full_page_asks_more() {
        let mut t = picked();
        t.playing = true;
        advance(&mut t, 100.0, 10.0);
        assert_eq!(t.t_ns as u64, t.picked.as_ref().unwrap().duration_ns, "clamped to the run");
        t.view.as_mut().unwrap().more = true;
        assert!(advance(&mut t, 0.0, 10.1), "a spent page with more asks the next");
    }

    /// **A scrub drops the applied state and the landing stacks seed the glows.**
    #[test]
    fn a_scrub_lands_the_open_stacks_of_that_moment() {
        let mut t = picked();
        t.playing = true;
        advance(&mut t, 0.7, 10.0);
        assert!(!t.glows.is_empty());
        scrub(&mut t, 200_000);
        assert_eq!((t.t_ns as u64, t.applied, t.glows.len(), t.view.is_none()), (200_000, 0, 0, true));
        t.view = Some(trace_of(&canned_window(200_000, true)).unwrap());
        land_stacks(&mut t);
        assert_eq!(t.glows.iter().map(|g| (g.head, g.thread)).collect::<Vec<_>>(), vec![(0, 0)], "checkout is open at 200 µs");
    }

    /// **A later page appends re-pointed**: its own heads list is interned per
    /// answer, so its events land pointing at the HELD window's heads, grown by
    /// the ones it brings along.
    #[test]
    fn a_later_page_appends_with_its_heads_repointed() {
        let mut held = trace_of(&canned_window(0, false)).unwrap();
        held.more = true;
        let page = TraceView {
            run_id: "r-1".into(),
            from_ns: 0,
            // this page's head 0 is web::render (held head 2), head 1 is new
            events: vec![TraceEvent { seq: 5, t_ns: 700_000, thread: 1, exit: false, head: 1, caller: Some(0) }],
            heads: vec![
                TraceHead { card: 9, at: [40.0, 60.0], stable_id: "f web::render().".into(), path: "web::render".into(), symbol: "render".into() },
                TraceHead { card: 4, at: [44.0, 61.0], stable_id: "f web::row().".into(), path: "web::row".into(), symbol: "row".into() },
            ],
            unplaced: 0,
            more: false,
            next_after_seq: Some(5),
            stacks: Vec::new(),
        };
        append_page(&mut held, page);
        assert_eq!(held.events.len(), 5);
        let e = held.events[4];
        assert_eq!(held.heads[e.head].path, "web::row", "the new head joined the held list");
        assert_eq!(held.heads[e.caller.unwrap()].path, "web::render", "the caller re-pointed at the held head");
        assert_eq!(held.heads.len(), 4);
        assert!(!held.more);
    }

    /// **The scene**: every glowing head a translucent quad in its thread's
    /// colour at the head's place, the pulse a lit segment ON its arc at its
    /// fraction.
    #[test]
    fn the_scene_holds_the_glow_quads_and_the_pulse_on_its_arc() {
        let mut t = picked();
        t.playing = true;
        advance(&mut t, 0.4, 10.0);
        let mut sc = Scene::default();
        trace_scene(&mut sc, &t, 10.2);
        let quads = sc.quads.len();
        assert_eq!(quads, 2, "two glowing heads");
        let g = &sc.quads[0];
        let head = &t.view.as_ref().unwrap().heads[0];
        assert!((g.origin[0] + g.u[0] * 0.5 + g.v[0] * 0.5 - head.at[0]).abs() < 1e-3, "centred on the head");
        assert!((g.origin[2] + g.u[2] * 0.5 + g.v[2] * 0.5 - head.at[1]).abs() < 1e-3);
        assert!(g.origin[1] > 0.0, "above the plane");
        assert_eq!(g.color.r(), thread_color(0).r(), "the thread's colour");
        assert!(g.color.a() < 255, "translucent");
        let (a, b) = pulse_at(t.view.as_ref().unwrap().heads[0].at, t.view.as_ref().unwrap().heads[1].at, ((10.2 - 10.0) / PULSE_S) as f32);
        let seg = sc.lines.iter().find(|l| l.width_px >= 3.0).expect("the pulse segment");
        assert!((seg.a[0] - a[0]).abs() < 1e-3 && (seg.a[2] - a[2]).abs() < 1e-3 && (seg.b[0] - b[0]).abs() < 1e-3, "on the arc at its fraction");
        assert_eq!(seg.color.r(), thread_color(0).r());
        assert!(seg.a[1] > 0.0, "the arc rises above the plane");
    }
}