1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
use crate::{
geom::{Emit, Mesh, Ray},
math::{rand_isotropic_dir, Pos3},
};
use rand::Rng;
pub enum Emitter {
Beam(Ray),
Points(Vec<Pos3>),
WeightedPoints(Vec<Pos3>, Vec<f64>),
Surface(Mesh),
}
impl Emitter {
#[inline]
#[must_use]
pub const fn new_beam(ray: Ray) -> Self {
Self::Beam(ray)
}
#[inline]
#[must_use]
pub fn new_points(points: Vec<Pos3>) -> Self {
debug_assert!(!points.is_empty());
Self::Points(points)
}
#[inline]
#[must_use]
pub fn new_weighted_points(points: Vec<Pos3>, weights: &[f64]) -> Self {
debug_assert!(!points.is_empty());
debug_assert!(points.len() == weights.len());
let sum: f64 = weights.iter().sum();
let mut cumulative_weight = Vec::with_capacity(weights.len());
let mut total = 0.0;
for w in weights {
total += w;
cumulative_weight.push(total / sum);
}
Self::WeightedPoints(points, cumulative_weight)
}
#[inline]
#[must_use]
pub const fn new_surface(mesh: Mesh) -> Self {
Self::Surface(mesh)
}
#[inline]
#[must_use]
pub fn emit<R: Rng>(&self, rng: &mut R) -> Ray {
match *self {
Self::Beam(ref ray) => ray.clone(),
Self::Points(ref ps) => {
Ray::new(ps[rng.gen_range(0, ps.len())], rand_isotropic_dir(rng))
}
Self::WeightedPoints(ref ps, ref ws) => {
let r: f64 = rng.gen();
for (p, w) in ps.iter().zip(ws) {
if r <= *w {
return Ray::new(*p, rand_isotropic_dir(rng));
}
}
unreachable!("Failed to determine weighted point to emit from.");
}
Self::Surface(ref mesh) => mesh.cast(rng),
}
}
}