dear-implot 0.12.0

High-level Rust bindings to ImPlot with dear-imgui-rs integration
Documentation
//! Stairs plot implementation

use super::{
    PlotData, PlotError, PlotItemStyle, plot_spec_with_style, validate_data_lengths,
    with_plot_str_or_empty,
};
use crate::{ItemFlags, StairsFlags, sys};

/// Builder for stairs plots with extensive customization options
pub struct StairsPlot<'a> {
    label: &'a str,
    x_data: &'a [f64],
    y_data: &'a [f64],
    style: PlotItemStyle,
    flags: StairsFlags,
    item_flags: ItemFlags,
    offset: i32,
    stride: i32,
}

impl<'a> super::PlotItemStyled for StairsPlot<'a> {
    fn style_mut(&mut self) -> &mut PlotItemStyle {
        &mut self.style
    }
}

impl<'a> StairsPlot<'a> {
    /// Create a new stairs plot with the given label and data
    pub fn new(label: &'a str, x_data: &'a [f64], y_data: &'a [f64]) -> Self {
        Self {
            label,
            x_data,
            y_data,
            style: PlotItemStyle::default(),
            flags: StairsFlags::NONE,
            item_flags: ItemFlags::NONE,
            offset: 0,
            stride: std::mem::size_of::<f64>() as i32,
        }
    }

    /// Set stairs flags for customization
    pub fn with_flags(mut self, flags: StairsFlags) -> Self {
        self.flags = flags;
        self
    }

    /// Set common item flags for this plot item (applies to all plot types)
    pub fn with_item_flags(mut self, flags: ItemFlags) -> Self {
        self.item_flags = flags;
        self
    }

    /// Enable pre-step mode (step before the point instead of after)
    pub fn pre_step(mut self) -> Self {
        self.flags |= StairsFlags::PRE_STEP;
        self
    }

    /// Enable shaded stairs (fill area under stairs)
    pub fn shaded(mut self) -> Self {
        self.flags |= StairsFlags::SHADED;
        self
    }

    /// Set data offset for partial plotting
    pub fn with_offset(mut self, offset: i32) -> Self {
        self.offset = offset;
        self
    }

    /// Set data stride for non-contiguous data
    pub fn with_stride(mut self, stride: i32) -> Self {
        self.stride = stride;
        self
    }

    /// Validate the plot data
    pub fn validate(&self) -> Result<(), PlotError> {
        validate_data_lengths(self.x_data, self.y_data)
    }

    /// Plot the stairs
    pub fn plot(self) {
        let Ok(count) = i32::try_from(self.x_data.len()) else {
            return;
        };
        with_plot_str_or_empty(self.label, |label_ptr| unsafe {
            let spec = plot_spec_with_style(
                self.style,
                self.flags.bits() | self.item_flags.bits(),
                self.offset,
                self.stride,
            );
            sys::ImPlot_PlotStairs_doublePtrdoublePtr(
                label_ptr,
                self.x_data.as_ptr(),
                self.y_data.as_ptr(),
                count,
                spec,
            );
        })
    }
}

impl<'a> PlotData for StairsPlot<'a> {
    fn label(&self) -> &str {
        self.label
    }

    fn data_len(&self) -> usize {
        self.x_data.len().min(self.y_data.len())
    }
}

/// Simple stairs plot for f32 data
pub struct StairsPlotF32<'a> {
    label: &'a str,
    x_data: &'a [f32],
    y_data: &'a [f32],
    style: PlotItemStyle,
    flags: StairsFlags,
    item_flags: ItemFlags,
}

impl<'a> super::PlotItemStyled for StairsPlotF32<'a> {
    fn style_mut(&mut self) -> &mut PlotItemStyle {
        &mut self.style
    }
}

impl<'a> StairsPlotF32<'a> {
    /// Create a new stairs plot with f32 data
    pub fn new(label: &'a str, x_data: &'a [f32], y_data: &'a [f32]) -> Self {
        Self {
            label,
            x_data,
            y_data,
            style: PlotItemStyle::default(),
            flags: StairsFlags::NONE,
            item_flags: ItemFlags::NONE,
        }
    }

    /// Set stairs flags for customization
    pub fn with_flags(mut self, flags: StairsFlags) -> Self {
        self.flags = flags;
        self
    }

    /// Set common item flags for this plot item (applies to all plot types)
    pub fn with_item_flags(mut self, flags: ItemFlags) -> Self {
        self.item_flags = flags;
        self
    }

    /// Enable pre-step mode
    pub fn pre_step(mut self) -> Self {
        self.flags |= StairsFlags::PRE_STEP;
        self
    }

    /// Enable shaded stairs
    pub fn shaded(mut self) -> Self {
        self.flags |= StairsFlags::SHADED;
        self
    }

    /// Validate the plot data
    pub fn validate(&self) -> Result<(), PlotError> {
        if self.x_data.len() != self.y_data.len() {
            return Err(PlotError::DataLengthMismatch {
                x_len: self.x_data.len(),
                y_len: self.y_data.len(),
            });
        }
        if self.x_data.is_empty() {
            return Err(PlotError::EmptyData);
        }
        Ok(())
    }

    /// Plot the stairs
    pub fn plot(self) {
        let Ok(count) = i32::try_from(self.x_data.len()) else {
            return;
        };
        with_plot_str_or_empty(self.label, |label_ptr| unsafe {
            let spec = plot_spec_with_style(
                self.style,
                self.flags.bits() | self.item_flags.bits(),
                0,
                std::mem::size_of::<f32>() as i32,
            );
            sys::ImPlot_PlotStairs_FloatPtrFloatPtr(
                label_ptr,
                self.x_data.as_ptr(),
                self.y_data.as_ptr(),
                count,
                spec,
            );
        })
    }
}

impl<'a> PlotData for StairsPlotF32<'a> {
    fn label(&self) -> &str {
        self.label
    }

    fn data_len(&self) -> usize {
        self.x_data.len().min(self.y_data.len())
    }
}

/// Simple stairs plot for single array data (y values only, x is auto-generated)
pub struct SimpleStairsPlot<'a> {
    label: &'a str,
    y_data: &'a [f64],
    style: PlotItemStyle,
    flags: StairsFlags,
    item_flags: ItemFlags,
    x_scale: f64,
    x_start: f64,
}

impl<'a> super::PlotItemStyled for SimpleStairsPlot<'a> {
    fn style_mut(&mut self) -> &mut PlotItemStyle {
        &mut self.style
    }
}

impl<'a> SimpleStairsPlot<'a> {
    /// Create a new simple stairs plot with only y data
    pub fn new(label: &'a str, y_data: &'a [f64]) -> Self {
        Self {
            label,
            y_data,
            style: PlotItemStyle::default(),
            flags: StairsFlags::NONE,
            item_flags: ItemFlags::NONE,
            x_scale: 1.0,
            x_start: 0.0,
        }
    }

    /// Set the x scale (spacing between points)
    pub fn with_x_scale(mut self, x_scale: f64) -> Self {
        self.x_scale = x_scale;
        self
    }

    /// Set the x start value
    pub fn with_x_start(mut self, x_start: f64) -> Self {
        self.x_start = x_start;
        self
    }

    /// Set stairs flags
    pub fn with_flags(mut self, flags: StairsFlags) -> Self {
        self.flags = flags;
        self
    }

    /// Set common item flags for this plot item (applies to all plot types)
    pub fn with_item_flags(mut self, flags: ItemFlags) -> Self {
        self.item_flags = flags;
        self
    }

    /// Enable pre-step mode
    pub fn pre_step(mut self) -> Self {
        self.flags |= StairsFlags::PRE_STEP;
        self
    }

    /// Enable shaded stairs
    pub fn shaded(mut self) -> Self {
        self.flags |= StairsFlags::SHADED;
        self
    }

    /// Validate the plot data
    pub fn validate(&self) -> Result<(), PlotError> {
        if self.y_data.is_empty() {
            return Err(PlotError::EmptyData);
        }
        Ok(())
    }

    /// Plot the stairs
    pub fn plot(self) {
        let Ok(count) = i32::try_from(self.y_data.len()) else {
            return;
        };
        with_plot_str_or_empty(self.label, |label_ptr| unsafe {
            let spec = plot_spec_with_style(
                self.style,
                self.flags.bits() | self.item_flags.bits(),
                0,
                std::mem::size_of::<f64>() as i32,
            );
            sys::ImPlot_PlotStairs_doublePtrInt(
                label_ptr,
                self.y_data.as_ptr(),
                count,
                self.x_scale,
                self.x_start,
                spec,
            );
        })
    }
}

impl<'a> PlotData for SimpleStairsPlot<'a> {
    fn label(&self) -> &str {
        self.label
    }

    fn data_len(&self) -> usize {
        self.y_data.len()
    }
}