1use super::profile_numeric::{finite_clamp, lerp, unit};
4use molgfx_core::SelectionHandle;
5use molgfx_math::Vec3;
6use serde::{Deserialize, Serialize};
7
8#[derive(Clone, Copy, PartialEq, Debug, Default, Serialize, Deserialize)]
10pub enum FocusTarget {
11 #[default]
13 CameraTarget,
14 Distance(f32),
16 WorldPoint(Vec3),
18 Selection(SelectionHandle),
20}
21
22impl FocusTarget {
23 pub(super) fn sanitize(self) -> Self {
24 match self {
25 Self::Distance(distance) if distance.is_finite() && distance > 0.0 => self,
26 Self::WorldPoint(point) if point.is_finite() => self,
27 Self::Selection(_) | Self::CameraTarget => self,
28 Self::Distance(_) | Self::WorldPoint(_) => Self::CameraTarget,
29 }
30 }
31}
32
33#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize)]
35pub struct DepthOfField {
36 pub focal_length_mm: f32,
38 pub f_number: f32,
40 pub sensor_width_mm: f32,
42 pub max_blur_pixels: f32,
44 pub blade_count: u8,
46 pub focus: FocusTarget,
48}
49
50impl DepthOfField {
51 #[must_use]
53 pub const fn cinematic() -> Self {
54 Self {
55 focal_length_mm: 50.0,
56 f_number: 8.0,
63 sensor_width_mm: 36.0,
64 max_blur_pixels: 14.0,
65 blade_count: 7,
66 focus: FocusTarget::CameraTarget,
67 }
68 }
69
70 pub(super) fn sanitize(self) -> Self {
71 Self {
72 focal_length_mm: finite_clamp(self.focal_length_mm, 1.0, 300.0, 50.0),
73 f_number: finite_clamp(self.f_number, 0.7, 64.0, 4.0),
74 sensor_width_mm: finite_clamp(self.sensor_width_mm, 1.0, 100.0, 36.0),
75 max_blur_pixels: finite_clamp(self.max_blur_pixels, 0.0, 64.0, 0.0),
76 blade_count: self.blade_count.clamp(3, 12),
77 focus: self.focus.sanitize(),
78 }
79 }
80
81 pub(super) fn blend(self, other: Self, weight: f32) -> Self {
82 let weight = unit(weight);
83 Self {
84 focal_length_mm: lerp(self.focal_length_mm, other.focal_length_mm, weight),
85 f_number: lerp(self.f_number, other.f_number, weight),
86 sensor_width_mm: lerp(self.sensor_width_mm, other.sensor_width_mm, weight),
87 max_blur_pixels: lerp(self.max_blur_pixels, other.max_blur_pixels, weight),
88 blade_count: if weight >= 0.5 {
89 other.blade_count
90 } else {
91 self.blade_count
92 },
93 focus: if weight >= 0.5 {
94 other.focus
95 } else {
96 self.focus
97 },
98 }
99 .sanitize()
100 }
101
102 pub(crate) fn packed(self, focus_distance: f32) -> [f32; 4] {
103 let settings = self.sanitize();
104 [
105 focus_distance.max(1.0e-3),
106 settings.focal_length_mm / (settings.f_number * settings.sensor_width_mm),
107 settings.max_blur_pixels,
108 f32::from(settings.blade_count),
109 ]
110 }
111}
112
113#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize)]
115pub struct MotionBlur {
116 pub shutter: f32,
118 pub max_blur_pixels: f32,
120}
121
122impl MotionBlur {
123 #[must_use]
125 pub const fn cinematic() -> Self {
126 Self {
127 shutter: 0.55,
128 max_blur_pixels: 18.0,
129 }
130 }
131
132 pub(super) fn sanitize(self) -> Self {
133 Self {
134 shutter: unit(self.shutter),
135 max_blur_pixels: finite_clamp(self.max_blur_pixels, 0.0, 96.0, 18.0),
136 }
137 }
138
139 pub(super) fn blend(self, other: Self, weight: f32) -> Self {
140 let weight = unit(weight);
141 Self {
142 shutter: lerp(self.shutter, other.shutter, weight),
143 max_blur_pixels: lerp(self.max_blur_pixels, other.max_blur_pixels, weight),
144 }
145 .sanitize()
146 }
147
148 pub(crate) fn packed(self) -> [f32; 4] {
149 let value = self.sanitize();
150 [value.shutter, value.max_blur_pixels, 0.0, 0.0]
151 }
152}