1#[inline]
12pub fn ashrae(aoi: f64, b0: f64) -> f64 {
13 let aoi_rad = aoi.to_radians();
14 if aoi_rad >= std::f64::consts::PI / 2.0 {
15 return 0.0;
16 }
17 let cos_aoi = aoi_rad.cos();
18 if cos_aoi <= 0.0 {
19 return 0.0;
20 }
21
22 let iam = 1.0 - b0 * (1.0 / cos_aoi - 1.0);
23 iam.clamp(0.0, 1.0)
24}
25
26#[inline]
35pub fn martin_ruiz(aoi: f64, a_r: f64) -> f64 {
36 if aoi >= 90.0 {
37 return 0.0;
38 }
39 let aoi_rad = aoi.to_radians();
40 let numerator = 1.0 - (-aoi_rad.cos() / a_r).exp();
42 let denominator = 1.0 - (-1.0 / a_r).exp();
43 if denominator == 0.0 {
44 return 1.0;
45 }
46 (numerator / denominator).clamp(0.0, 1.0)
47}
48
49#[inline]
63pub fn physical(aoi: f64, n: f64, k: f64, l: f64) -> f64 {
64 if aoi.is_nan() {
65 return f64::NAN;
66 }
67 if aoi.abs() >= 90.0 {
68 return 0.0;
69 }
70
71 let aoi_rad = aoi.to_radians();
72 let cos_aoi = aoi_rad.cos().max(0.0);
73 let sin_aoi = (1.0 - cos_aoi * cos_aoi).sqrt();
74
75 let sin_refr = sin_aoi / n;
77 let cos_refr = (1.0 - sin_refr * sin_refr).sqrt();
78
79 let n1_cos1 = cos_aoi; let n2_cos1 = n * cos_aoi;
82 let n1_cos2 = cos_refr; let n2_cos2 = n * cos_refr;
84
85 let rho_s = ((n1_cos1 - n2_cos2) / (n1_cos1 + n2_cos2)).powi(2);
86 let rho_p = ((n1_cos2 - n2_cos1) / (n1_cos2 + n2_cos1)).powi(2);
87 let rho_0 = ((1.0 - n) / (1.0 + n)).powi(2);
88
89 let tau_s = 1.0 - rho_s;
91 let tau_p = 1.0 - rho_p;
92 let tau_0 = 1.0 - rho_0;
93
94 let tau_s = tau_s * (-k * l / cos_refr).exp();
96 let tau_p = tau_p * (-k * l / cos_refr).exp();
97 let tau_0 = tau_0 * (-k * l).exp();
98
99 (tau_s + tau_p) / (2.0 * tau_0)
101}
102
103#[inline]
113pub fn schlick(aoi: f64) -> f64 {
114 if aoi.abs() >= 90.0 {
115 return 0.0;
116 }
117 let cos_aoi = aoi.to_radians().cos();
118 1.0 - (1.0 - cos_aoi).powi(5)
119}
120
121#[inline]
132pub fn schlick_diffuse(surface_tilt: f64) -> (f64, f64) {
133 let cos_b = surface_tilt.to_radians().cos();
134 let sin_b = surface_tilt.to_radians().sin();
135 let beta_rad = surface_tilt.to_radians();
136 let pi = std::f64::consts::PI;
137
138 let cuk = (2.0 / (pi * (1.0 + cos_b)))
140 * ((30.0 / 7.0) * pi - (160.0 / 21.0) * beta_rad - (10.0 / 3.0) * pi * cos_b
141 + (160.0 / 21.0) * cos_b * sin_b
142 - (5.0 / 3.0) * pi * cos_b * sin_b.powi(2)
143 + (20.0 / 7.0) * cos_b * sin_b.powi(3)
144 - (5.0 / 16.0) * pi * cos_b * sin_b.powi(4)
145 + (16.0 / 105.0) * cos_b * sin_b.powi(5));
146
147 let cug = if surface_tilt < 1e-6 {
149 0.0
150 } else {
151 40.0 / (21.0 * (1.0 - cos_b)) - (1.0 + cos_b) / (1.0 - cos_b) * cuk
152 };
153
154 (cuk, cug)
155}
156
157#[inline]
168pub fn martin_ruiz_diffuse(surface_tilt: f64, a_r: f64) -> (f64, f64) {
169 let pi = std::f64::consts::PI;
170
171 let tilt = if surface_tilt == 0.0 {
173 1e-6
174 } else if surface_tilt == 180.0 {
175 180.0 - 1e-6
176 } else {
177 surface_tilt
178 };
179
180 let c1 = 4.0 / 3.0 / pi; let c2 = 0.5 * a_r - 0.154;
182
183 let beta = tilt.to_radians();
184 let cos_beta = beta.cos();
185 let sin_beta = if tilt < 90.0 {
186 beta.sin()
187 } else {
188 (pi - beta).sin()
189 };
190
191 let trig_sky = sin_beta + (pi - beta - sin_beta) / (1.0 + cos_beta);
192 let trig_gnd = sin_beta + (beta - sin_beta) / (1.0 - cos_beta);
193
194 let iam_sky = 1.0 - (-(c1 + c2 * trig_sky) * trig_sky / a_r).exp();
195 let iam_gnd = 1.0 - (-(c1 + c2 * trig_gnd) * trig_gnd / a_r).exp();
196
197 (iam_sky, iam_gnd)
198}
199
200#[allow(clippy::too_many_arguments)]
205#[inline]
206pub fn sapm(aoi: f64, b0: f64, b1: f64, b2: f64, b3: f64, b4: f64, b5: f64) -> f64 {
207 if !(0.0..=90.0).contains(&aoi) { return 0.0; }
208
209 let iam = b0
210 + b1 * aoi
211 + b2 * aoi.powi(2)
212 + b3 * aoi.powi(3)
213 + b4 * aoi.powi(4)
214 + b5 * aoi.powi(5);
215
216 iam.clamp(0.0, 1.0)
217}