use super::{ColorScheme, Material, Representation, RepresentationKind, RepresentationParams};
use crate::{
ClipSet, CoreError, PropertyAppearance, RepresentationTarget, SegmentationStyle,
SurfaceComponentPolicy, SurfaceKind, SurfaceScalarOverlay, SurfaceStyle, TubeRadiusMapping,
VisualStyle, VolumeStyle,
};
#[derive(Clone, PartialEq, Debug)]
pub struct RepresentationConfig {
pub(crate) kind: RepresentationKind,
pub(crate) color: ColorScheme,
pub(crate) material: Material,
pub(crate) params: RepresentationParams,
pub(crate) clipping: ClipSet,
appearance: Option<PropertyAppearance>,
volume: VolumeStyle,
segmentation: SegmentationStyle,
surface_scalar: Option<SurfaceScalarOverlay>,
visible: bool,
order: u16,
visual: Option<VisualStyle>,
}
impl RepresentationConfig {
#[must_use]
pub fn new(kind: RepresentationKind) -> Self {
Self {
kind,
color: ColorScheme::default(),
material: Material::default(),
params: super::kinds::params_for_kind(kind),
clipping: ClipSet::default(),
appearance: None,
volume: VolumeStyle::default(),
segmentation: SegmentationStyle::default(),
surface_scalar: None,
visible: true,
order: 0,
visual: None,
}
}
#[must_use]
pub const fn kind(&self) -> RepresentationKind {
self.kind
}
#[must_use]
pub const fn color(mut self, color: ColorScheme) -> Self {
self.color = color;
self
}
#[must_use]
pub const fn material(mut self, material: Material) -> Self {
self.material = material;
self
}
#[must_use]
pub const fn clipping(mut self, clipping: ClipSet) -> Self {
self.clipping = clipping;
self
}
#[must_use]
pub const fn radius_scale(mut self, scale: f32) -> Self {
self.params.radius_scale = scale;
self
}
#[must_use]
pub const fn bond_radius(mut self, radius: f32) -> Self {
self.params.bond_radius = radius;
self
}
#[must_use]
pub const fn ribbon_width(mut self, width: f32) -> Self {
self.params.ribbon_width = width;
self
}
#[must_use]
pub const fn point_size(mut self, pixels: f32) -> Self {
self.params.point_size_pixels = pixels;
self
}
#[must_use]
pub const fn line_width(mut self, pixels: f32) -> Self {
self.params.line_width_pixels = pixels;
self
}
#[must_use]
pub const fn probe_radius(mut self, radius: f32) -> Self {
self.params.probe_radius = radius;
self
}
pub fn tube_radius(mut self, radius: f32) -> Result<Self, CoreError> {
self.validate_tube_kind()?;
if !radius.is_finite() || radius <= 0.0 {
return Err(CoreError::InvalidProperty {
reason: "tube radius must be finite and strictly positive",
});
}
self.params.tube_radius = radius;
self.params.tube_radius_mapping = TubeRadiusMapping::Constant;
Ok(self)
}
pub fn putty_b_factor(mut self, domain: [f32; 2], radii: [f32; 2]) -> Result<Self, CoreError> {
self.validate_tube_kind()?;
self.params.tube_radius_mapping = TubeRadiusMapping::b_factor(domain, radii)?;
Ok(self)
}
fn validate_tube_kind(&self) -> Result<(), CoreError> {
if matches!(
self.kind,
RepresentationKind::Trace | RepresentationKind::Tube
) {
return Ok(());
}
Err(CoreError::InvalidProperty {
reason: "tube radius controls require a trace or tube representation",
})
}
#[must_use]
pub const fn isolevel(mut self, level: f32) -> Self {
self.params.isolevel = level;
self
}
#[must_use]
pub const fn surface(mut self, kind: SurfaceKind, style: SurfaceStyle) -> Self {
self.params.surface_kind = kind;
self.params.surface_style = style;
self
}
pub fn hide_small_disconnected_components(
mut self,
policy: SurfaceComponentPolicy,
) -> Result<Self, CoreError> {
if self.kind != RepresentationKind::Surface {
return Err(CoreError::InvalidProperty {
reason: "surface component filtering requires a surface representation",
});
}
self.params.surface_components = policy;
Ok(self)
}
#[must_use]
pub const fn appearance(mut self, appearance: PropertyAppearance) -> Self {
self.appearance = Some(appearance);
self
}
#[must_use]
pub const fn volume_style(mut self, style: VolumeStyle) -> Self {
self.volume = style;
self
}
#[must_use]
pub fn segmentation_style(mut self, style: SegmentationStyle) -> Self {
self.segmentation = style;
self
}
#[must_use]
pub const fn surface_scalar(mut self, overlay: SurfaceScalarOverlay) -> Self {
self.surface_scalar = Some(overlay);
self
}
#[must_use]
pub const fn order(mut self, order: u16) -> Self {
self.order = order;
self
}
#[must_use]
pub fn visual(mut self, visual: VisualStyle) -> Self {
self.visual = Some(visual);
self
}
pub(crate) fn visual_style(&self) -> Option<&VisualStyle> {
self.visual.as_ref()
}
pub(crate) fn bind(self, target: RepresentationTarget) -> Representation {
let mut representation = Representation::new(target, self.kind);
representation.color = self.color;
representation.material = self.material;
representation.params = self.params;
representation.clipping = self.clipping;
representation.appearance = self.appearance;
representation.volume = self.volume;
representation.segmentation = self.segmentation;
representation.surface_scalar = self.surface_scalar;
representation.visible = self.visible;
representation.order = self.order;
representation.visual = self.visual;
representation
}
}
impl From<RepresentationKind> for RepresentationConfig {
fn from(kind: RepresentationKind) -> Self {
Self::new(kind)
}
}
impl Representation {
#[must_use]
pub fn spacefill() -> RepresentationConfig {
RepresentationKind::Spacefill.into()
}
#[must_use]
pub fn ball_and_stick() -> RepresentationConfig {
RepresentationKind::BallAndStick.into()
}
#[must_use]
pub fn lines() -> RepresentationConfig {
RepresentationKind::Lines.into()
}
#[must_use]
pub fn licorice() -> RepresentationConfig {
RepresentationKind::Licorice.into()
}
#[must_use]
pub fn cartoon() -> RepresentationConfig {
RepresentationKind::Cartoon.into()
}
#[must_use]
pub fn trace() -> RepresentationConfig {
RepresentationKind::Trace.into()
}
#[must_use]
pub fn tube() -> RepresentationConfig {
RepresentationKind::Tube.into()
}
pub fn putty(domain: [f32; 2], radii: [f32; 2]) -> Result<RepresentationConfig, CoreError> {
Self::tube().putty_b_factor(domain, radii)
}
#[must_use]
pub fn surface() -> RepresentationConfig {
RepresentationKind::Surface.into()
}
#[must_use]
pub fn points() -> RepresentationConfig {
RepresentationKind::Points.into()
}
#[must_use]
pub fn beads() -> RepresentationConfig {
RepresentationKind::Beads.into()
}
#[must_use]
pub fn rocket() -> RepresentationConfig {
RepresentationKind::Rocket.into()
}
#[must_use]
pub fn twister() -> RepresentationConfig {
RepresentationKind::Twister.into()
}
#[must_use]
pub fn paper_chain() -> RepresentationConfig {
RepresentationKind::PaperChain.into()
}
#[must_use]
pub fn volume() -> RepresentationConfig {
RepresentationKind::Volume.into()
}
#[must_use]
pub fn segmentation() -> RepresentationConfig {
RepresentationKind::Segmentation.into()
}
}