yog 0.0.5

yog: a balls-oriented session manager for lernie loops (egui frontend)
Documentation
//! The mark as scalable vector source (DESIGN §11) — the *second* rendering of
//! the one walk in [`super`], never a second drawing. Every shape is flat, so
//! this is a `<polygon>` or a `<circle>` with a solid fill and nothing else:
//! no gradient to drift from what the rasterizer computes. `assets/yog.svg` is
//! this function's output verbatim, regenerated only by `make icon`.

use super::mark;
use super::shape::Shape;

/// Side of the SVG viewBox — round numbers in the emitted source.
const CANVAS: f32 = 256.0;

/// `#rrggbb` for an SVG `fill`.
fn hex(hue: egui::Color32) -> String {
    format!("#{:02x}{:02x}{:02x}", hue.r(), hue.g(), hue.b())
}

/// One point in viewBox coordinates.
fn at((x, y): (f32, f32)) -> String {
    format!("{:.2},{:.2}", x * CANVAS, y * CANVAS)
}

impl Shape {
    /// This shape as one SVG element. A ribbon's outline is its ribs' outer
    /// edges out and their back edges home — the same ribs the rasterizer
    /// builds its quads from, off the same [`Shape::ribs`] doorway, so the two
    /// bound the same region.
    fn element(&self) -> String {
        match self {
            Self::Trace { fill, .. } | Self::Lune { fill, .. } => {
                let ribs = self.ribs();
                let mut points: Vec<String> = ribs.iter().map(|rib| at(rib.out)).collect();
                points.extend(ribs.iter().rev().map(|rib| at(rib.back)));
                format!(
                    "  <polygon points=\"{}\" fill=\"{}\"/>\n",
                    points.join(" "),
                    hex(*fill)
                )
            }
            Self::Disc {
                cx,
                cy,
                radius,
                fill,
            } => format!(
                "  <circle cx=\"{:.2}\" cy=\"{:.2}\" r=\"{:.2}\" fill=\"{}\"/>\n",
                cx * CANVAS,
                cy * CANVAS,
                radius * CANVAS,
                hex(*fill)
            ),
        }
    }
}

/// The triskelion as scalable vector source, in the order the rasterizer
/// composites it.
pub fn svg() -> String {
    let body: String = mark().iter().map(Shape::element).collect();
    format!(
        "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 {CANVAS:.0} {CANVAS:.0}\" \
         width=\"{CANVAS:.0}\" height=\"{CANVAS:.0}\">\n\
         \x20 <title>yog — the key and the gate</title>\n\
         \x20 <!-- Generated by yog::theme::icon::svg(). Edit the walk constants, not this file. -->\n\
         {body}</svg>\n"
    )
}