poincare-lib 0.5.0

GPU-accelerated 3D plotting library for mathematical functions and scientific visualisation
Documentation
use crate::{Domain, PlotDefinition, PlotSpec, PlotStyle, Resolution, TablePlotTarget};

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct StyleCapabilities {
    pub mesh: bool,
    pub line: bool,
    pub point: bool,
    pub glyph: bool,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum DomainEditorMetadata {
    Fixed,
    One { primary: String },
    Two { primary: String, secondary: String },
    Three { x: String, y: String, z: String },
}

impl DomainEditorMetadata {
    pub fn editable_axis_count(&self) -> usize {
        match self {
            Self::Fixed => 0,
            Self::One { .. } => 1,
            Self::Two { .. } => 2,
            Self::Three { .. } => 3,
        }
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CoordinateSemantics {
    Fixed,
    CartesianSurface,
    CartesianVolume,
    ParametricSurface,
    ParametricCurve,
    SphericalSurface,
    CylindricalSurface,
    PolarCurve,
    ImportedData,
}

#[derive(Clone, Debug)]
pub struct PlotMetadata {
    pub style_caps: StyleCapabilities,
    pub domain_editor: DomainEditorMetadata,
    pub default_domain: Option<Domain>,
    pub default_resolution: Option<Resolution>,
    pub default_style: PlotStyle,
    pub uses_resolution: bool,
    pub uses_seed_resolution: bool,
    pub supports_surface_intersection: bool,
    pub coordinate_semantics: CoordinateSemantics,
    pub required_variables: Vec<String>,
}

impl PlotDefinition {
    pub fn metadata(&self) -> PlotMetadata {
        let default_style = PlotStyle::default();
        let default_domain = Some(Domain::default());
        let default_resolution = Some(Resolution::default());

        match self {
            Self::ContouredSurface { .. } | Self::GridSurface | Self::ExprCartesian { .. } => PlotMetadata {
                style_caps: StyleCapabilities {
                    mesh: true,
                    line: false,
                    point: false,
                    glyph: false,
                },
                domain_editor: DomainEditorMetadata::Two {
                    primary: "X".to_string(),
                    secondary: "Y".to_string(),
                },
                default_domain,
                default_resolution,
                default_style,
                uses_resolution: true,
                uses_seed_resolution: false,
                supports_surface_intersection: true,
                coordinate_semantics: CoordinateSemantics::CartesianSurface,
                required_variables: vec!["x".to_string(), "y".to_string()],
            },
            Self::SphericalHarmonic | Self::ExprSpherical { .. } => PlotMetadata {
                style_caps: StyleCapabilities {
                    mesh: true,
                    line: false,
                    point: false,
                    glyph: false,
                },
                domain_editor: DomainEditorMetadata::Two {
                    primary: "theta".to_string(),
                    secondary: "phi".to_string(),
                },
                default_domain,
                default_resolution,
                default_style,
                uses_resolution: true,
                uses_seed_resolution: false,
                supports_surface_intersection: true,
                coordinate_semantics: CoordinateSemantics::SphericalSurface,
                required_variables: vec!["theta".to_string(), "phi".to_string()],
            },
            Self::ExprCylindrical { .. } => PlotMetadata {
                style_caps: StyleCapabilities {
                    mesh: true,
                    line: false,
                    point: false,
                    glyph: false,
                },
                domain_editor: DomainEditorMetadata::Two {
                    primary: "theta".to_string(),
                    secondary: "z".to_string(),
                },
                default_domain,
                default_resolution,
                default_style,
                uses_resolution: true,
                uses_seed_resolution: false,
                supports_surface_intersection: true,
                coordinate_semantics: CoordinateSemantics::CylindricalSurface,
                required_variables: vec!["theta".to_string(), "z".to_string()],
            },
            Self::ExprPolar { .. } => PlotMetadata {
                style_caps: StyleCapabilities {
                    mesh: true,
                    line: false,
                    point: false,
                    glyph: false,
                },
                domain_editor: DomainEditorMetadata::One {
                    primary: "theta".to_string(),
                },
                default_domain,
                default_resolution,
                default_style,
                uses_resolution: true,
                uses_seed_resolution: false,
                supports_surface_intersection: true,
                coordinate_semantics: CoordinateSemantics::PolarCurve,
                required_variables: vec!["theta".to_string()],
            },
            Self::ExprParametricSurface { .. } => PlotMetadata {
                style_caps: StyleCapabilities {
                    mesh: true,
                    line: false,
                    point: false,
                    glyph: false,
                },
                domain_editor: DomainEditorMetadata::Two {
                    primary: "U".to_string(),
                    secondary: "V".to_string(),
                },
                default_domain,
                default_resolution,
                default_style,
                uses_resolution: true,
                uses_seed_resolution: false,
                supports_surface_intersection: true,
                coordinate_semantics: CoordinateSemantics::ParametricSurface,
                required_variables: vec!["u".to_string(), "v".to_string()],
            },
            Self::HelixCurve | Self::ExprCurve { .. } => PlotMetadata {
                style_caps: StyleCapabilities {
                    mesh: false,
                    line: true,
                    point: false,
                    glyph: false,
                },
                domain_editor: DomainEditorMetadata::One {
                    primary: "T".to_string(),
                },
                default_domain,
                default_resolution,
                default_style,
                uses_resolution: true,
                uses_seed_resolution: false,
                supports_surface_intersection: false,
                coordinate_semantics: CoordinateSemantics::ParametricCurve,
                required_variables: vec!["t".to_string()],
            },
            Self::ExprCartesianLine { ind_var, .. } => PlotMetadata {
                style_caps: StyleCapabilities {
                    mesh: false,
                    line: true,
                    point: false,
                    glyph: false,
                },
                domain_editor: DomainEditorMetadata::One {
                    primary: ind_var.to_uppercase(),
                },
                default_domain,
                default_resolution,
                default_style,
                uses_resolution: true,
                uses_seed_resolution: false,
                supports_surface_intersection: false,
                coordinate_semantics: CoordinateSemantics::ParametricCurve,
                required_variables: vec![ind_var.clone()],
            },
            Self::ScatterCloud | Self::PointAnnotations { .. } => PlotMetadata {
                style_caps: StyleCapabilities {
                    mesh: false,
                    line: false,
                    point: true,
                    glyph: false,
                },
                domain_editor: DomainEditorMetadata::Fixed,
                default_domain: None,
                default_resolution: None,
                default_style,
                uses_resolution: false,
                uses_seed_resolution: false,
                supports_surface_intersection: false,
                coordinate_semantics: CoordinateSemantics::Fixed,
                required_variables: Vec::new(),
            },
            Self::VectorField
            | Self::ExprVectorField { .. }
            | Self::VectorSlice { .. }
            | Self::GradientField { .. }
            | Self::CurlField { .. }
            | Self::ArrowAnnotations { .. } => PlotMetadata {
                style_caps: StyleCapabilities {
                    mesh: false,
                    line: false,
                    point: false,
                    glyph: true,
                },
                domain_editor: match self {
                    Self::VectorSlice { .. } | Self::ArrowAnnotations { .. } => DomainEditorMetadata::Fixed,
                    _ => DomainEditorMetadata::Three {
                        x: "X".to_string(),
                        y: "Y".to_string(),
                        z: "Z".to_string(),
                    },
                },
                default_domain: match self {
                    Self::VectorSlice { .. } | Self::ArrowAnnotations { .. } => None,
                    _ => default_domain,
                },
                default_resolution,
                default_style,
                uses_resolution: !matches!(self, Self::ArrowAnnotations { .. }),
                uses_seed_resolution: matches!(
                    self,
                    Self::VectorField | Self::ExprVectorField { .. } | Self::VectorSlice { .. } | Self::GradientField { .. } | Self::CurlField { .. }
                ),
                supports_surface_intersection: false,
                coordinate_semantics: match self {
                    Self::ArrowAnnotations { .. } => CoordinateSemantics::Fixed,
                    _ => CoordinateSemantics::CartesianVolume,
                },
                required_variables: match self {
                    Self::ArrowAnnotations { .. } => Vec::new(),
                    _ => vec!["x".to_string(), "y".to_string(), "z".to_string()],
                },
            },
            Self::Streamlines { .. } | Self::VolumeRender { .. } | Self::Isosurface { .. } => PlotMetadata {
                style_caps: StyleCapabilities {
                    mesh: matches!(self, Self::Isosurface { .. }),
                    line: matches!(self, Self::Streamlines { .. }),
                    point: false,
                    glyph: false,
                },
                domain_editor: DomainEditorMetadata::Three {
                    x: "X".to_string(),
                    y: "Y".to_string(),
                    z: "Z".to_string(),
                },
                default_domain,
                default_resolution,
                default_style,
                uses_resolution: !matches!(self, Self::Streamlines { .. }),
                uses_seed_resolution: false,
                supports_surface_intersection: matches!(self, Self::Isosurface { .. }),
                coordinate_semantics: CoordinateSemantics::CartesianVolume,
                required_variables: Vec::new(),
            },
            Self::ExprVolume { .. } | Self::ExprIsosurface { .. } | Self::ExprStreamlines { .. } | Self::DivergenceField { .. } => PlotMetadata {
                style_caps: StyleCapabilities {
                    mesh: matches!(self, Self::ExprIsosurface { .. }),
                    line: matches!(self, Self::ExprStreamlines { .. }),
                    point: false,
                    glyph: false,
                },
                domain_editor: DomainEditorMetadata::Three {
                    x: "X".to_string(),
                    y: "Y".to_string(),
                    z: "Z".to_string(),
                },
                default_domain,
                default_resolution,
                default_style,
                uses_resolution: !matches!(self, Self::ExprStreamlines { .. }),
                uses_seed_resolution: false,
                supports_surface_intersection: matches!(self, Self::ExprIsosurface { .. }),
                coordinate_semantics: CoordinateSemantics::CartesianVolume,
                required_variables: vec!["x".to_string(), "y".to_string(), "z".to_string()],
            },
            Self::ImportedTable { definition } => PlotMetadata {
                style_caps: match definition.target {
                    TablePlotTarget::SurfaceGrid => StyleCapabilities {
                        mesh: true,
                        line: false,
                        point: false,
                        glyph: false,
                    },
                    TablePlotTarget::Curve => StyleCapabilities {
                        mesh: false,
                        line: true,
                        point: false,
                        glyph: false,
                    },
                    TablePlotTarget::Scatter => StyleCapabilities {
                        mesh: false,
                        line: false,
                        point: true,
                        glyph: false,
                    },
                    TablePlotTarget::VectorField => StyleCapabilities {
                        mesh: false,
                        line: false,
                        point: false,
                        glyph: true,
                    },
                },
                domain_editor: DomainEditorMetadata::Fixed,
                default_domain: None,
                default_resolution: None,
                default_style,
                uses_resolution: false,
                uses_seed_resolution: false,
                supports_surface_intersection: matches!(definition.target, TablePlotTarget::SurfaceGrid),
                coordinate_semantics: CoordinateSemantics::ImportedData,
                required_variables: Vec::new(),
            },
            Self::ScalarSlice { .. }
            | Self::DerivedPolylineGroups { .. }
            | Self::InterpolatedCurve { .. } => PlotMetadata {
                style_caps: match self {
                    Self::ScalarSlice { .. } => StyleCapabilities {
                        mesh: true,
                        line: false,
                        point: false,
                        glyph: false,
                    },
                    _ => StyleCapabilities {
                        mesh: false,
                        line: true,
                        point: false,
                        glyph: false,
                    },
                },
                domain_editor: DomainEditorMetadata::Fixed,
                default_domain: None,
                default_resolution: match self {
                    Self::ScalarSlice { .. } => default_resolution,
                    _ => None,
                },
                default_style,
                uses_resolution: matches!(self, Self::ScalarSlice { .. }),
                uses_seed_resolution: false,
                supports_surface_intersection: matches!(self, Self::ScalarSlice { .. }),
                coordinate_semantics: CoordinateSemantics::Fixed,
                required_variables: match self {
                    Self::ScalarSlice { .. } => vec!["x".to_string(), "y".to_string(), "z".to_string()],
                    _ => Vec::new(),
                },
            },
        }
    }
}

impl PlotSpec {
    pub fn metadata(&self) -> PlotMetadata {
        self.definition.metadata()
    }
}