klyff_msdf 0.1.3

MSDF generation library with optional GPU acceleration.
Documentation
//! SoA layout of segments consumed by the per-pixel MSDF search hot path.
//!
//! Populated once per glyph from `&[Segment]`. Holds precomputed `tangent`,
//! `inv_tangent_len_sq`, and `tangent_unit` so the hot loop avoids redundant
//! divs/sqrts per pixel.

use glam::Vec2;

use crate::math_segment::{Segment, SegmentColor};

#[derive(Default)]
pub(crate) struct SegmentSoa {
    pub(crate) from: Vec<Vec2>,
    pub(crate) to: Vec<Vec2>,
    pub(crate) tangent: Vec<Vec2>,
    pub(crate) inv_tangent_len_sq: Vec<f32>,
    pub(crate) tangent_unit: Vec<Vec2>,
    /// 3-bit channel mask. Bit `c` is set when the segment contributes to channel `c`.
    pub(crate) color_mask: Vec<u8>,
}

impl SegmentSoa {
    pub(crate) fn new() -> Self {
        Self::default()
    }

    pub(crate) fn len(&self) -> usize {
        self.from.len()
    }

    pub(crate) fn populate(&mut self, segs: &[Segment]) {
        let n = segs.len();
        self.from.clear();
        self.to.clear();
        self.tangent.clear();
        self.inv_tangent_len_sq.clear();
        self.tangent_unit.clear();
        self.color_mask.clear();
        self.from.reserve(n);
        self.to.reserve(n);
        self.tangent.reserve(n);
        self.inv_tangent_len_sq.reserve(n);
        self.tangent_unit.reserve(n);
        self.color_mask.reserve(n);

        for seg in segs {
            let tangent = seg.to - seg.from;
            let len_sq = tangent.length_squared();
            // Guard degenerate zero-length segments: 0.0 makes `t` clamp to 0,
            // yielding the point distance from `from` — same behavior as the AoS
            // path's `if tangent.length_squared() <= 1e-6` branch.
            let (inv, unit) = if len_sq > 1e-6 {
                let len = len_sq.sqrt();
                (1.0 / len_sq, tangent / len)
            } else {
                (0.0, Vec2::ZERO)
            };

            self.from.push(seg.from);
            self.to.push(seg.to);
            self.tangent.push(tangent);
            self.inv_tangent_len_sq.push(inv);
            self.tangent_unit.push(unit);
            self.color_mask.push(channel_mask(seg.color));
        }
    }
}

#[inline]
fn channel_mask(c: SegmentColor) -> u8 {
    // Bit 0 = R, bit 1 = G, bit 2 = B
    match c {
        SegmentColor::White => 0b111,
        SegmentColor::RedGreen => 0b011,
        SegmentColor::GreenBlue => 0b110,
        SegmentColor::BlueRed => 0b101,
    }
}