use super::ribbon::{PROFILE_SIDES, SplineProfile};
use molgfx_core::{SecondaryStructure, TubeRadiusMapping};
use molgfx_math::Rgba8;
pub(super) const PROFILE: [[f32; 2]; PROFILE_SIDES] = [
[1.0, 0.0],
[0.707_106_77, 0.707_106_77],
[0.0, 1.0],
[-0.707_106_77, 0.707_106_77],
[-1.0, 0.0],
[-0.707_106_77, -0.707_106_77],
[0.0, -1.0],
[0.707_106_77, -0.707_106_77],
];
pub(super) const BOX_PROFILE: [[f32; 4]; PROFILE_SIDES] = [
[1.0, 1.0, 0.0, 1.0],
[-1.0, 1.0, 0.0, 1.0],
[-1.0, 1.0, -1.0, 0.0],
[-1.0, -1.0, -1.0, 0.0],
[-1.0, -1.0, 0.0, -1.0],
[1.0, -1.0, 0.0, -1.0],
[1.0, -1.0, 1.0, 0.0],
[1.0, 1.0, 1.0, 0.0],
];
pub(super) fn cross_section(
flat: bool,
side: usize,
width: f32,
thickness: f32,
) -> (f32, f32, [f32; 2]) {
if flat {
let lane = BOX_PROFILE[side % PROFILE_SIDES];
return (lane[0], lane[1], [lane[2], lane[3]]);
}
let [x, y] = PROFILE[side % PROFILE_SIDES];
(x, y, [x * thickness, y * width])
}
pub(super) fn profile_color(profile: SplineProfile, thickness_axis: f32, base: Rgba8) -> Rgba8 {
if profile != SplineProfile::Twister {
return base;
}
if thickness_axis > 0.0 {
Rgba8::new(226, 232, 244, base.a)
} else {
Rgba8::new(28, 74, 168, base.a)
}
}
pub(super) const BOX_FACE_SIDES: [bool; PROFILE_SIDES] =
[true, false, true, false, true, false, true, false];
pub(super) const BOX_CORNERS: [[f32; 2]; 4] = [[1.0, 1.0], [-1.0, 1.0], [-1.0, -1.0], [1.0, -1.0]];
#[must_use]
pub fn variable_tube_radius(
mapping: TubeRadiusMapping,
properties: &[f32],
controls: [u32; 2],
amount: f32,
fallback: f32,
) -> f32 {
let left = properties
.get(
usize::try_from(controls[0])
.into_iter()
.fold(usize::MAX, |_, value| value),
)
.copied()
.filter(|value| value.is_finite());
let right = properties
.get(
usize::try_from(controls[1])
.into_iter()
.fold(usize::MAX, |_, value| value),
)
.copied()
.filter(|value| value.is_finite());
match left.zip(right) {
Some((left, right)) => mapping.radius(left + (right - left) * amount, fallback),
None => left
.or(right)
.map_or(fallback, |value| mapping.radius(value, fallback)),
}
}
pub(super) fn rocket_scale(
style: SecondaryStructure,
parameter: f32,
styles: &[SecondaryStructure],
segment: usize,
) -> (f32, f32) {
match style {
SecondaryStructure::Helix => (1.35, 1.35),
SecondaryStructure::Strand => {
let (width, _) = profile_scale(style, parameter, styles, segment);
(width, 0.34)
}
SecondaryStructure::Turn | SecondaryStructure::Coil | SecondaryStructure::Unknown => {
(0.3, 0.3)
}
}
}
pub(super) fn profile_scale(
style: SecondaryStructure,
parameter: f32,
styles: &[SecondaryStructure],
segment: usize,
) -> (f32, f32) {
match style {
SecondaryStructure::Helix => (1.0, 1.6),
SecondaryStructure::Strand => {
let terminal = !matches!(styles.get(segment + 1), Some(SecondaryStructure::Strand));
let arrow = if terminal {
if parameter < 0.65 {
1.0 + parameter * (0.6 / 0.65)
} else {
(1.6 * (1.0 - parameter) / 0.35).max(0.08)
}
} else {
1.0
};
(arrow * 1.2, 0.58)
}
SecondaryStructure::Turn => (0.55, 1.0),
SecondaryStructure::Coil | SecondaryStructure::Unknown => (0.48, 1.0),
}
}