gr 0.3.4

Rust wrapper for the GR framework
Documentation
use super::util::impl_each;
use super::{ActiveGks, Gks, SegmentGks};
use crate::ffi::gkscore::MAX_COLOR;
use crate::util::f64range::F64Range;
use core::ffi::{c_int, CStr};
use core::fmt;
use core::marker::PhantomData;
use core::num::{NonZeroUsize, TryFromIntError};
use gr_sys::gks::*;

pub const MAX_COLOR_INDEX: usize = MAX_COLOR as _;

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum GksPrimitive {
    Path = GKS_K_GDP_DRAW_PATH as _,
    Lines = GKS_K_GDP_DRAW_LINES as _,
    Markers = GKS_K_GDP_DRAW_MARKERS as _,
    Triangles = GKS_K_GDP_DRAW_TRIANGLES as _,
    Polygons = GKS_K_GDP_FILL_POLYGONS as _,
}

#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct GksError;
type Result<T> = ::core::result::Result<T, GksError>;

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct GksColorIndexArray<'a> {
    pub(crate) data: *mut c_int,
    pub(crate) dimensions: (c_int, c_int),
    pub(crate) slice: PhantomData<&'a [c_int]>,
}

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum GksLinetype {
    Solid = GKS_K_LINETYPE_SOLID as _,
    Dashed = GKS_K_LINETYPE_DASHED as _,
    Dotted = GKS_K_LINETYPE_DOTTED as _,
    DashedDotted = GKS_K_LINETYPE_DASHED_DOTTED as _,
    Dash2Dot = GKS_K_LINETYPE_DASH_2_DOT as _,
    Dash3Dot = GKS_K_LINETYPE_DASH_3_DOT as _,
    LongDash = GKS_K_LINETYPE_LONG_DASH as _,
    LongShortDash = GKS_K_LINETYPE_LONG_SHORT_DASH as _,
    SpacedDash = GKS_K_LINETYPE_SPACED_DASH as _,
    SpacedDot = GKS_K_LINETYPE_SPACED_DOT as _,
    DoubleDot = GKS_K_LINETYPE_DOUBLE_DOT as _,
    TripleDot = GKS_K_LINETYPE_TRIPLE_DOT as _,
}

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum GksMarkertype {
    Dot = GKS_K_MARKERTYPE_DOT as _,
    Plus = GKS_K_MARKERTYPE_PLUS as _,
    Asterisk = GKS_K_MARKERTYPE_ASTERISK as _,
    Circle = GKS_K_MARKERTYPE_CIRCLE as _,
    DiagonalCross = GKS_K_MARKERTYPE_DIAGONAL_CROSS as _,
    SolidCircle = GKS_K_MARKERTYPE_SOLID_CIRCLE as _,
    TriangleUp = GKS_K_MARKERTYPE_TRIANGLE_UP as _,
    SolidTriUp = GKS_K_MARKERTYPE_SOLID_TRI_UP as _,
    TriangleDown = GKS_K_MARKERTYPE_TRIANGLE_DOWN as _,
    SolidTriDown = GKS_K_MARKERTYPE_SOLID_TRI_DOWN as _,
    Square = GKS_K_MARKERTYPE_SQUARE as _,
    SolidSquare = GKS_K_MARKERTYPE_SOLID_SQUARE as _,
    Bowtie = GKS_K_MARKERTYPE_BOWTIE as _,
    SolidBowtie = GKS_K_MARKERTYPE_SOLID_BOWTIE as _,
    Hourglass = GKS_K_MARKERTYPE_HOURGLASS as _,
    SolidHglass = GKS_K_MARKERTYPE_SOLID_HGLASS as _,
    Diamond = GKS_K_MARKERTYPE_DIAMOND as _,
    SolidDiamond = GKS_K_MARKERTYPE_SOLID_DIAMOND as _,
    Star = GKS_K_MARKERTYPE_STAR as _,
    SolidStar = GKS_K_MARKERTYPE_SOLID_STAR as _,
    TriUpDown = GKS_K_MARKERTYPE_TRI_UP_DOWN as _,
    SolidTriRight = GKS_K_MARKERTYPE_SOLID_TRI_RIGHT as _,
    SolidTriLeft = GKS_K_MARKERTYPE_SOLID_TRI_LEFT as _,
    HollowPlus = GKS_K_MARKERTYPE_HOLLOW_PLUS as _,
    SolidPlus = GKS_K_MARKERTYPE_SOLID_PLUS as _,
    Pentagon = GKS_K_MARKERTYPE_PENTAGON as _,
    Hexagon = GKS_K_MARKERTYPE_HEXAGON as _,
    Heptagon = GKS_K_MARKERTYPE_HEPTAGON as _,
    Octagon = GKS_K_MARKERTYPE_OCTAGON as _,
    Star4 = GKS_K_MARKERTYPE_STAR_4 as _,
    Star5 = GKS_K_MARKERTYPE_STAR_5 as _,
    Star6 = GKS_K_MARKERTYPE_STAR_6 as _,
    Star7 = GKS_K_MARKERTYPE_STAR_7 as _,
    Star8 = GKS_K_MARKERTYPE_STAR_8 as _,
    Vline = GKS_K_MARKERTYPE_VLINE as _,
    Hline = GKS_K_MARKERTYPE_HLINE as _,
    Omark = GKS_K_MARKERTYPE_OMARK as _,
}

fn check_that(cond: bool) -> Result<()> {
    match cond {
        true => Ok(()),
        false => Err(Default::default()),
    }
}

impl_each! {(Gks, ActiveGks, SegmentGks) {
    pub fn set_transparency(alpha: f64) {
        unsafe { gks_set_transparency(alpha) }
    }

    pub fn set_window(&mut self, tnr: impl Into<c_int>, x: F64Range, y: F64Range) {
        let tnr = tnr.into();
        let (xmin, xmax) = x.into();
        let (ymin, ymax) = y.into();
        unsafe { gks_set_window(tnr, xmin, xmax, ymin, ymax) }
    }

    pub fn set_viewport(&mut self, tnr: impl Into<c_int>, x: F64Range, y: F64Range) {
        let tnr = tnr.into();
        let (xmin, xmax) = x.into();
        let (ymin, ymax) = y.into();
        unsafe { gks_set_viewport(tnr, xmin, xmax, ymin, ymax) }
    }

    pub fn set_nominal_size(&mut self, factor: f64) {
        unsafe { gks_set_nominal_size(factor) }
    }

    pub fn set_polyline_type(&mut self, ltype: impl Into<c_int>) {
        let ltype = ltype.into();
        unsafe { gks_set_pline_linetype(ltype) }
    }

    pub fn set_polyline_width(&mut self, width: f64) {
        unsafe { gks_set_pline_linewidth(width) }
    }

    pub fn set_polyline_color_index(&mut self, coli: impl Into<c_int>) {
        let coli = coli.into();
        unsafe { gks_set_pline_color_index(coli) }
    }

    pub fn set_polymark_type(&mut self, mtype: impl Into<c_int>) {
        let mtype = mtype.into();
        unsafe { gks_set_pmark_type(mtype) }
    }

    pub fn set_polymark_size(&mut self, size: f64) {
        unsafe { gks_set_pmark_size(size) }
    }

    pub fn set_polymark_color_index(&mut self, coli: impl Into<c_int>) {
        let coli = coli.into();
        unsafe { gks_set_pmark_color_index(coli) }
    }

    pub fn set_text_fontprec(&mut self, font: impl Into<c_int>, prec: impl Into<c_int>) {
        let font = font.into();
        let prec = prec.into();
        unsafe { gks_set_text_fontprec(font, prec) }
    }

    pub fn set_text_expansion_factor(&mut self, expfac: f64) {
        unsafe { gks_set_text_expfac(expfac) }
    }

    pub fn set_text_spacing(&mut self, spacing: f64) {
        unsafe { gks_set_text_spacing(spacing) }
    }

    pub fn set_text_color_index(&mut self, coli: impl Into<c_int>) {
        let coli = coli.into();
        unsafe { gks_set_text_color_index(coli) }
    }

    pub fn set_text_height(&mut self, height: f64) {
        unsafe { gks_set_text_height(height) }
    }
}}

impl_each! {(ActiveGks, SegmentGks) {
    #[allow(clippy::unit_arg)]
    pub fn polyline(&mut self, n: usize, x: &[f64], y: &[f64]) -> Result<()> {
        check_that(n <= x.len() && n <= y.len())?;
        let n = n.try_into()?;
        let x = x.as_ptr().cast_mut();
        let y = y.as_ptr().cast_mut();
        Ok(unsafe { gks_polyline(n, x, y) })
    }

    #[allow(clippy::unit_arg)]
    pub fn polymarker(&mut self, n: usize, x: &[f64], y: &[f64]) -> Result<()> {
        check_that(n <= x.len() && n <= y.len())?;
        let n = n.try_into()?;
        let x = x.as_ptr().cast_mut();
        let y = y.as_ptr().cast_mut();
        Ok(unsafe { gks_polymarker(n, x, y) })
    }

    pub fn text(&mut self, (x, y): (f64, f64), s: impl AsRef<CStr>) {
        let p = s.as_ref().as_ptr().cast_mut();
        unsafe { gks_text(x, y, p) }
    }

    #[allow(clippy::unit_arg)]
    pub fn fillarea(&mut self, n: usize, x: &[f64], y: &[f64]) -> Result<()> {
        check_that(n <= x.len() && n <= y.len())?;
        let n = n.try_into()?;
        let x = x.as_ptr().cast_mut();
        let y = y.as_ptr().cast_mut();
        Ok(unsafe { gks_fillarea(n, x, y) })
    }

    #[allow(clippy::unit_arg)]
    pub fn cellarray(
        &mut self,
        ((px, py), start): ((f64, f64), (usize, usize)),
        ((qx, qy), end): ((f64, f64), (usize, usize)),
        color_array: GksColorIndexArray,
    ) -> Result<()> {
        let sx = (start.0 + 1).try_into()?;
        let sy = (start.1 + 1).try_into()?;
        let nx = (end.0 - start.0).try_into()?;
        let ny = (end.1 - start.1).try_into()?;
        let GksColorIndexArray {
            data,
            dimensions: (dx, dy),
            slice: _,
        } = color_array;
        Ok(unsafe { gks_cellarray(px, py, qx, qy, dx, dy, sx, sy, nx, ny, data) })
    }
}}

macro_rules! impl_gdp {
    ($f:ident, $cf:ident) => {
        #[allow(clippy::unit_arg)]
        pub fn $f(
            &mut self,
            n: usize,
            x: &[f64],
            y: &[f64],
            primitive: GksPrimitive,
            data_records: &[c_int],
        ) -> Result<()> {
            check_that(n <= x.len() && n <= y.len())?;
            let n = n.try_into()?;
            let x = x.as_ptr().cast_mut();
            let y = y.as_ptr().cast_mut();
            let prim = primitive as c_int;
            let ldr = data_records.len().try_into()?;
            let dr = data_records.as_ptr().cast_mut();
            Ok(unsafe { $cf(n, x, y, prim, ldr, dr) })
        }
    };
}

impl_each! {(ActiveGks, SegmentGks) {
    impl_gdp! { gdp, gks_gdp }
    impl_gdp! { ft_gdp, gks_ft_gdp }
}}

impl<'a> GksColorIndexArray<'a> {
    pub fn with_stride(data: &'a [c_int], stride: NonZeroUsize) -> Result<Self> {
        let rows = data.len() / stride;
        let stride = stride.get().try_into()?;
        let rows = rows.try_into()?;
        check_that(rows != 0)?;
        Ok(Self {
            data: data.as_ptr().cast_mut(),
            dimensions: (stride, rows),
            slice: PhantomData,
        })
    }

    /// # Safety
    ///
    /// `data` must point to at least `columns * rows` elements, consecutively stored in memory.
    pub unsafe fn new_unchecked(
        data: *const c_int,
        columns: impl Into<c_int>,
        rows: impl Into<c_int>,
    ) -> Self {
        Self {
            data: data.cast_mut(),
            dimensions: (columns.into(), rows.into()),
            slice: PhantomData,
        }
    }
}

impl<'a, const N: usize> From<&'a [[c_int; N]]> for GksColorIndexArray<'a> {
    fn from(value: &'a [[c_int; N]]) -> Self {
        Self {
            data: value.as_flattened().as_ptr().cast_mut(),
            dimensions: (value.len() as c_int, N as c_int),
            slice: PhantomData,
        }
    }
}

impl fmt::Display for GksError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str("error in GKS")
    }
}

impl std::error::Error for GksError {}

#[doc(hidden)]
impl From<TryFromIntError> for GksError {
    fn from(_value: TryFromIntError) -> Self {
        GksError
    }
}

impl From<GksLinetype> for c_int {
    fn from(value: GksLinetype) -> Self {
        value as Self
    }
}

impl From<GksMarkertype> for c_int {
    fn from(value: GksMarkertype) -> Self {
        value as Self
    }
}