Repository: typst/typst
File: crates/typst-library/src/visualize/gradient.rs (Rust)

Code before:
            }
            Self::Conic(conic) => {
                Arc::make_mut(conic).relative = Smart::Custom(relative);
            }
        }

        self
    }
    /// Returns a reference to the stops of this gradient.
    pub fn stops_ref(&self) -> &[(Color, Ratio)] {
        match self {
            Gradient::Linear(linear) => &linear.stops,
            Gradient::Radial(radial) => &radial.stops,
            Gradient::Conic(conic) => &conic.stops,
        }
    }

    /// Generates additional stops to replicate a gradient in
    /// rgb color space interpolation by bisecting
    pub fn generate_intermediate_stops_for_rgb_interpolation(
        &self,
        first: GradientStop,
        second: GradientStop,
    ) -> impl Iterator<Item = (Color, Ratio)> {
        let threshold = 0.001;
        let max_subdivisions = 64;

        let t0 = first.offset.unwrap().get();
        let dt = second.offset.unwrap().get() - t0;
        let mut prev_color = first.color;
        let mut next_stop = second.offset.unwrap();
        let mut next_color = second.color;
        let mut n: u64 = 1; // number of subdivisions
        let mut i: u64 = 1; // next stop at this subdivision

        std::iter::from_fn(move || {
            let mut intermediate_stop = None;
            loop {
                // linear interpolation error at midpoint
                let euclidean_error = {
                    let mid = Ratio::new(t0 + dt * (i as f64 - 0.5) / n as f64);
                    let exact = self.sample(RatioOrAngle::Ratio(mid));
                    let approx = Color::mix_iter(
                        [prev_color, next_color].map(|c| WeightedColor::new(c, 0.5)),
                        ColorSpace::LinearRgb,
                    )
                    .unwrap();
                    let exact_color = exact.to_linear_rgb().premultiply().color;
                    let approx_color = approx.to_linear_rgb().premultiply().color;
                    let delta = (exact_color - approx_color).into_components();
                    (delta.0 * delta.0 + delta.1 * delta.1 + delta.2 * delta.2).sqrt()

Review comment (suggestion):
I think making these `const`s would improve readability. They could just stay here in the function.

Code after:
            }
            Self::Conic(conic) => {
                Arc::make_mut(conic).relative = Smart::Custom(relative);
            }
        }

        self
    }
    /// Returns a reference to the stops of this gradient.
    pub fn stops_ref(&self) -> &[(Color, Ratio)] {
        match self {
            Gradient::Linear(linear) => &linear.stops,
            Gradient::Radial(radial) => &radial.stops,
            Gradient::Conic(conic) => &conic.stops,
        }
    }

    /// Generates additional stops to replicate a gradient in
    /// rgb color space interpolation by bisecting
    pub fn generate_intermediate_stops_for_rgb_interpolation(
        &self,
        first: GradientStop,
        second: GradientStop,
    ) -> impl Iterator<Item = (Color, Ratio)> {
        const THRESHOLD: f32 = 0.001;
        const MAX_SUBDIVISIONS: u64 = 64;

        let t0 = first.offset.unwrap().get();
        let dt = second.offset.unwrap().get() - t0;
        let mut prev_color = first.color;
        let mut next_stop = second.offset.unwrap();
        let mut next_color = second.color;
        let mut n: u64 = 1; // number of subdivisions
        let mut i: u64 = 1; // next stop at this subdivision

        std::iter::from_fn(move || {
            let mut intermediate_stop = None;
            loop {
                // linear interpolation error at midpoint
                let euclidean_error = {
                    let mid = Ratio::new(t0 + dt * (i as f64 - 0.5) / n as f64);
                    let exact = self.sample(RatioOrAngle::Ratio(mid));
                    let approx = Color::mix_iter(
                        [prev_color, next_color].map(|c| WeightedColor::new(c, 0.5)),
                        ColorSpace::LinearRgb,
                    )
                    .unwrap();
                    let exact_color = exact.to_linear_rgb().premultiply().color;
                    let approx_color = approx.to_linear_rgb().premultiply().color;
                    let delta = (exact_color - approx_color).into_components();
                    (delta.0 * delta.0 + delta.1 * delta.1 + delta.2 * delta.2).sqrt()