use crate::error::CoreError;
use crate::handle::StructureHandle;
use crate::representation::relation::RelationPattern;
use molgfx_math::{Rgba8, Vec3};
#[cfg(test)]
#[path = "guide_tests.rs"]
mod tests;
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
pub enum GuideCap {
#[default]
None,
Arrow,
DoubleArrow,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
pub enum PolylineKind {
#[default]
Open,
Closed,
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct GuideStyle {
pub color: Rgba8,
pub pattern: RelationPattern,
pub width_pixels: f32,
pub opacity: f32,
pub period_pixels: f32,
pub duty_cycle: f32,
pub cap: GuideCap,
pub arrow_pixels: f32,
}
impl Default for GuideStyle {
fn default() -> Self {
Self {
color: Rgba8::opaque(226, 232, 240),
pattern: RelationPattern::Solid,
width_pixels: 1.6,
opacity: 1.0,
period_pixels: 8.0,
duty_cycle: 0.5,
cap: GuideCap::None,
arrow_pixels: 9.0,
}
}
}
impl GuideStyle {
#[must_use]
pub fn sanitized(self) -> Self {
let default = Self::default();
Self {
color: self.color,
pattern: self.pattern,
width_pixels: bounded(self.width_pixels, 0.1, 64.0, default.width_pixels),
opacity: bounded(self.opacity, 0.0, 1.0, default.opacity),
period_pixels: bounded(self.period_pixels, 1.0, 256.0, default.period_pixels),
duty_cycle: bounded(self.duty_cycle, 0.05, 1.0, default.duty_cycle),
cap: self.cap,
arrow_pixels: bounded(self.arrow_pixels, 0.0, 64.0, default.arrow_pixels),
}
}
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct Guide {
owner: StructureHandle,
start: Vec3,
end: Vec3,
style: GuideStyle,
visible: bool,
}
impl Guide {
pub fn new(
owner: StructureHandle,
start: Vec3,
end: Vec3,
style: GuideStyle,
) -> Result<Self, CoreError> {
if !start.is_finite() || !end.is_finite() {
return Err(CoreError::InvalidAnnotation {
reason: "guide endpoints must be finite",
});
}
if start.distance_squared(end) <= f32::EPSILON {
return Err(CoreError::InvalidAnnotation {
reason: "guide endpoints must be distinct",
});
}
Ok(Self {
owner,
start,
end,
style: style.sanitized(),
visible: true,
})
}
#[must_use]
pub const fn owner(&self) -> StructureHandle {
self.owner
}
#[must_use]
pub const fn start(&self) -> Vec3 {
self.start
}
#[must_use]
pub const fn end(&self) -> Vec3 {
self.end
}
#[must_use]
pub const fn style(&self) -> GuideStyle {
self.style
}
#[must_use]
pub const fn visible(&self) -> bool {
self.visible
}
pub const fn set_visible(&mut self, visible: bool) {
self.visible = visible;
}
pub fn set_style(&mut self, style: GuideStyle) {
self.style = style.sanitized();
}
}
fn bounded(value: f32, minimum: f32, maximum: f32, fallback: f32) -> f32 {
if value.is_finite() {
value.clamp(minimum, maximum)
} else {
fallback
}
}