livesplit-core 0.13.0

livesplit-core is a library that provides a lot of functionality for creating a speedrun timer.
Documentation
use crate::{
    component::graph::State,
    layout::LayoutState,
    rendering::{PathBuilder, RenderContext, ResourceAllocator},
    settings::Gradient,
};

pub(in crate::rendering) fn render(
    context: &mut RenderContext<'_, impl ResourceAllocator>,
    [width, height]: [f32; 2],
    component: &State,
    _layout_state: &LayoutState,
) {
    let old_transform = context.transform;
    context.scale(height);
    let width = width / height;

    const GRID_LINE_WIDTH: f32 = 0.015;
    const LINE_WIDTH: f32 = 0.025;
    const CIRCLE_RADIUS: f32 = 0.035;

    context.render_top_rectangle(
        [0.0, 0.0],
        [width, component.middle],
        &Gradient::Plain(component.top_background_color),
    );
    context.render_top_rectangle(
        [0.0, component.middle],
        [width, 1.0],
        &Gradient::Plain(component.bottom_background_color),
    );

    for &y in &component.horizontal_grid_lines {
        context.render_top_rectangle(
            [0.0, y - GRID_LINE_WIDTH],
            [width, y + GRID_LINE_WIDTH],
            &Gradient::Plain(component.grid_lines_color),
        );
    }

    for &x in &component.vertical_grid_lines {
        context.render_top_rectangle(
            [width * x - GRID_LINE_WIDTH, 0.0],
            [width * x + GRID_LINE_WIDTH, 1.0],
            &Gradient::Plain(component.grid_lines_color),
        );
    }

    let len = if component.is_live_delta_active {
        let p1 = &component.points[component.points.len() - 2];
        let p2 = &component.points[component.points.len() - 1];

        let mut builder = context.handles.path_builder();
        builder.move_to(width * p1.x, component.middle);
        builder.line_to(width * p1.x, p1.y);
        builder.line_to(width * p2.x, p2.y);
        builder.line_to(width * p2.x, component.middle);
        builder.close();
        let partial_fill_path = builder.finish();
        context.top_layer_path(partial_fill_path, component.partial_fill_color);

        component.points.len() - 1
    } else {
        component.points.len()
    };

    let mut builder = context.handles.path_builder();
    builder.move_to(0.0, component.middle);
    for p in &component.points[..len] {
        builder.line_to(width * p.x, p.y);
    }
    builder.line_to(width * component.points[len - 1].x, component.middle);
    builder.close();
    let fill_path = builder.finish();
    context.top_layer_path(fill_path, component.complete_fill_color);

    for points in component.points.windows(2) {
        let mut builder = context.handles.path_builder();
        builder.move_to(width * points[0].x, points[0].y);
        builder.line_to(width * points[1].x, points[1].y);

        let color = if points[1].is_best_segment {
            component.best_segment_color
        } else {
            component.graph_lines_color
        };

        let line_path = builder.finish();
        context.top_layer_stroke_path(line_path, color, LINE_WIDTH);
    }

    for (i, point) in component.points.iter().enumerate().skip(1) {
        if i != component.points.len() - 1 || !component.is_live_delta_active {
            let color = if point.is_best_segment {
                component.best_segment_color
            } else {
                component.graph_lines_color
            };

            let circle_path = context
                .handles
                .build_circle(width * point.x, point.y, CIRCLE_RADIUS);
            context.top_layer_path(circle_path, color);
        }
    }

    context.transform = old_transform;
}