use super::profile_numeric::{finite_clamp, lerp, unit};
use molgfx_core::SelectionHandle;
use molgfx_math::Vec3;
use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, PartialEq, Debug, Default, Serialize, Deserialize)]
pub enum FocusTarget {
#[default]
CameraTarget,
Distance(f32),
WorldPoint(Vec3),
Selection(SelectionHandle),
}
impl FocusTarget {
pub(super) fn sanitize(self) -> Self {
match self {
Self::Distance(distance) if distance.is_finite() && distance > 0.0 => self,
Self::WorldPoint(point) if point.is_finite() => self,
Self::Selection(_) | Self::CameraTarget => self,
Self::Distance(_) | Self::WorldPoint(_) => Self::CameraTarget,
}
}
}
#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize)]
pub struct DepthOfField {
pub focal_length_mm: f32,
pub f_number: f32,
pub sensor_width_mm: f32,
pub max_blur_pixels: f32,
pub blade_count: u8,
pub focus: FocusTarget,
}
impl DepthOfField {
#[must_use]
pub const fn cinematic() -> Self {
Self {
focal_length_mm: 50.0,
f_number: 8.0,
sensor_width_mm: 36.0,
max_blur_pixels: 14.0,
blade_count: 7,
focus: FocusTarget::CameraTarget,
}
}
pub(super) fn sanitize(self) -> Self {
Self {
focal_length_mm: finite_clamp(self.focal_length_mm, 1.0, 300.0, 50.0),
f_number: finite_clamp(self.f_number, 0.7, 64.0, 4.0),
sensor_width_mm: finite_clamp(self.sensor_width_mm, 1.0, 100.0, 36.0),
max_blur_pixels: finite_clamp(self.max_blur_pixels, 0.0, 64.0, 0.0),
blade_count: self.blade_count.clamp(3, 12),
focus: self.focus.sanitize(),
}
}
pub(super) fn blend(self, other: Self, weight: f32) -> Self {
let weight = unit(weight);
Self {
focal_length_mm: lerp(self.focal_length_mm, other.focal_length_mm, weight),
f_number: lerp(self.f_number, other.f_number, weight),
sensor_width_mm: lerp(self.sensor_width_mm, other.sensor_width_mm, weight),
max_blur_pixels: lerp(self.max_blur_pixels, other.max_blur_pixels, weight),
blade_count: if weight >= 0.5 {
other.blade_count
} else {
self.blade_count
},
focus: if weight >= 0.5 {
other.focus
} else {
self.focus
},
}
.sanitize()
}
pub(crate) fn packed(self, focus_distance: f32) -> [f32; 4] {
let settings = self.sanitize();
[
focus_distance.max(1.0e-3),
settings.focal_length_mm / (settings.f_number * settings.sensor_width_mm),
settings.max_blur_pixels,
f32::from(settings.blade_count),
]
}
}
#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize)]
pub struct MotionBlur {
pub shutter: f32,
pub max_blur_pixels: f32,
}
impl MotionBlur {
#[must_use]
pub const fn cinematic() -> Self {
Self {
shutter: 0.55,
max_blur_pixels: 18.0,
}
}
pub(super) fn sanitize(self) -> Self {
Self {
shutter: unit(self.shutter),
max_blur_pixels: finite_clamp(self.max_blur_pixels, 0.0, 96.0, 18.0),
}
}
pub(super) fn blend(self, other: Self, weight: f32) -> Self {
let weight = unit(weight);
Self {
shutter: lerp(self.shutter, other.shutter, weight),
max_blur_pixels: lerp(self.max_blur_pixels, other.max_blur_pixels, weight),
}
.sanitize()
}
pub(crate) fn packed(self) -> [f32; 4] {
let value = self.sanitize();
[value.shutter, value.max_blur_pixels, 0.0, 0.0]
}
}