rustmotion-components 0.8.0

Component library for rustmotion (61 components)
Documentation
use rustmotion_core::error::Result;
use skia_safe::{Canvas, PaintStyle, PathBuilder, Rect};

use rustmotion_core::engine::renderer::paint_from_hex;

use super::Chart;

const FULL_SWEEP: f32 = 359.99;

impl Chart {
    pub(super) fn render_pie(&self, canvas: &Canvas, w: f32, h: f32, progress: f32) -> Result<()> {
        self.render_pie_ring(canvas, w, h, progress, 0.0)
    }

    pub(super) fn render_donut(
        &self,
        canvas: &Canvas,
        w: f32,
        h: f32,
        progress: f32,
    ) -> Result<()> {
        let inner = self.inner_radius.clamp(0.1, 0.95) as f32;
        self.render_pie_ring(canvas, w, h, progress, inner)
    }

    fn render_pie_ring(
        &self,
        canvas: &Canvas,
        w: f32,
        h: f32,
        progress: f32,
        inner_ratio: f32,
    ) -> Result<()> {
        let values: Vec<f64> = self.data.iter().map(|d| d.value.max(0.0)).collect();
        let total: f64 = values.iter().sum();
        if total <= 0.0 {
            return Ok(());
        }

        let cx = w / 2.0;
        let cy = h / 2.0;
        let outer_r = cx.min(cy) - 8.0;
        if outer_r <= 0.0 {
            return Ok(());
        }
        let inner_r = outer_r * inner_ratio;

        let outer = Rect::from_xywh(cx - outer_r, cy - outer_r, outer_r * 2.0, outer_r * 2.0);
        let inner = Rect::from_xywh(cx - inner_r, cy - inner_r, inner_r * 2.0, inner_r * 2.0);

        let total_sweep = 360.0 * progress;
        let mut start_angle = -90.0_f32;

        for (i, &value) in values.iter().enumerate() {
            let sweep = (value / total) as f32 * total_sweep;
            if sweep <= 0.0 {
                continue;
            }
            let color = self.data[i]
                .color
                .as_deref()
                .unwrap_or_else(|| self.get_color(i));

            let mut paint = paint_from_hex(color);
            paint.set_style(PaintStyle::Fill);
            paint.set_anti_alias(true);

            if sweep >= FULL_SWEEP {
                if inner_r > 0.0 {
                    let mut ring = paint;
                    ring.set_style(PaintStyle::Stroke);
                    ring.set_stroke_width(outer_r - inner_r);
                    canvas.draw_circle((cx, cy), (outer_r + inner_r) / 2.0, &ring);
                } else {
                    canvas.draw_circle((cx, cy), outer_r, &paint);
                }
            } else {
                let mut path = PathBuilder::new();
                if inner_r > 0.0 {
                    path.arc_to(outer, start_angle, sweep, false);
                    path.arc_to(inner, start_angle + sweep, -sweep, false);
                } else {
                    path.move_to((cx, cy));
                    path.arc_to(outer, start_angle, sweep, false);
                }
                path.close();
                canvas.draw_path(&path.detach(), &paint);
            }

            start_angle += sweep;
        }

        Ok(())
    }
}