//! Native background worker for reference-orbit computation. //! //! At deep zoom the high-precision reference can take many milliseconds (tens of //! thousands of `Big` iterations), which would stutter the UI if done inline. //! This runs it on a thread and coalesces bursts of requests (e.g. during a //! drag) down to the most recent one. On the web we compute inline instead //! (browsers need a Web Worker for threads); see `app.rs`. use std::sync::mpsc::{Receiver, Sender, TryRecvError, channel}; use std::thread; use crate::fractal::{FractalKind, RefOrbit, compute_reference, compute_set_reference}; use crate::view::{Big, Scale, big_from_f64}; pub struct RefRequest { pub center_re: Big, pub center_im: Big, pub half_height: Scale, pub julia: bool, pub julia_c: (f64, f64), pub max_iter: u32, pub precision: usize, pub kind: FractalKind, pub power: u32, /// Distortion constant for the Phoenix map (ignored by other kinds). pub phoenix_p: (f64, f64), /// Distortion constant for the Lambda map (ignored by other kinds). pub lambda_l: (f64, f64), /// Complex exponent for the Complex Multibrot kind (ignored by other kinds). pub complex_power: (f64, f64), /// Kind-switch morph: `(from_kind, weight)` blended into every step. pub morph: Option<(FractalKind, f32)>, } pub struct RefResult { pub center_re: Big, pub center_im: Big, pub half_height: Scale, pub points: RefOrbit, /// The kind and morph `points` was computed with (echoed from the /// request). pub kind: FractalKind, pub morph: Option<(FractalKind, f32)>, } pub struct RefWorker { req_tx: Sender, res_rx: Receiver, } impl RefWorker { pub fn spawn() -> Self { let (req_tx, req_rx) = channel::(); let (res_tx, res_rx) = channel::(); thread::Builder::new() .name("reference-orbit".into()) .spawn(move || { while let Ok(mut req) = req_rx.recv() { // Coalesce: if newer requests are already queued, skip to the // latest so a fast drag doesn't compute every intermediate view. loop { match req_rx.try_recv() { Ok(newer) => req = newer, Err(TryRecvError::Empty) => break, Err(TryRecvError::Disconnected) => return, } } let points = compute(&req); if res_tx .send(RefResult { center_re: req.center_re, center_im: req.center_im, half_height: req.half_height, points, kind: req.kind, morph: req.morph, }) .is_err() { return; } } }) .expect("spawn reference-orbit thread"); Self { req_tx, res_rx } } pub fn request(&self, req: RefRequest) { let _ = self.req_tx.send(req); } /// Drain all pending results, returning only the most recent. pub fn try_take_latest(&self) -> Option { let mut latest = None; while let Ok(res) = self.res_rx.try_recv() { latest = Some(res); } latest } } fn compute(req: &RefRequest) -> RefOrbit { if req.julia { let jr = big_from_f64(req.julia_c.0, req.precision); let ji = big_from_f64(req.julia_c.1, req.precision); compute_reference( &req.center_re, &req.center_im, &jr, &ji, req.max_iter, req.precision, req.kind, req.power, req.phoenix_p, req.lambda_l, req.complex_power, req.morph.map(|(k, w)| (k, w as f64)), ) } else { compute_set_reference( &req.center_re, &req.center_im, req.max_iter, req.precision, req.kind, req.power, req.phoenix_p, req.lambda_l, req.complex_power, req.morph.map(|(k, w)| (k, w as f64)), ) } }