starfall/astronomy/satellite_systems/constraints/
mod.rs1use rand::prelude::*;
2use std::default::Default;
3
4use crate::astronomy::host_star::HostStar;
5use crate::astronomy::satellite_system::constraints::Constraints as SatelliteSystemConstraints;
6use crate::astronomy::satellite_systems::constants::*;
7use crate::astronomy::satellite_systems::error::Error;
8use crate::astronomy::satellite_systems::SatelliteSystems;
9
10#[derive(Clone, Copy, Debug, PartialEq)]
12pub struct Constraints {
13 pub minimum_count: Option<usize>,
15 pub maximum_count: Option<usize>,
17 pub satellite_system_constraints: Option<SatelliteSystemConstraints>,
19 pub generate_primary_gas_giant: bool,
21 pub generate_habitable: bool,
23}
24
25impl Constraints {
26 #[named]
28 pub fn habitable() -> Self {
29 trace_enter!();
30 let generate_primary_gas_giant = true;
31 let generate_habitable = true;
32 let satellite_system_constraints = Some(SatelliteSystemConstraints::habitable());
33 let result = Self {
34 generate_primary_gas_giant,
35 generate_habitable,
36 satellite_system_constraints,
37 ..Constraints::default()
38 };
39 trace_var!(result);
40 trace_exit!();
41 result
42 }
43
44 #[named]
46 pub fn generate<R: Rng + ?Sized>(&self, rng: &mut R, host_star: &HostStar) -> Result<SatelliteSystems, Error> {
47 trace_enter!();
48 let minimum_count = self.minimum_count.unwrap_or(MINIMUM_SATELLITE_SYSTEMS);
49 trace_var!(minimum_count);
50 let maximum_count = self.maximum_count.unwrap_or(MAXIMUM_SATELLITE_SYSTEMS);
51 trace_var!(maximum_count);
52 let satellite_system_constraints = self.satellite_system_constraints.unwrap_or_default();
53 trace_var!(satellite_system_constraints);
54 let mut satellite_systems = Vec::new();
55 let orbits = self.generate_orbits(rng, host_star)?;
56 for orbit in orbits.into_iter() {
57 let satellite_system = satellite_system_constraints.generate(rng, host_star, orbit)?;
58 trace_var!(satellite_system);
59 satellite_systems.push(satellite_system);
60 }
61 trace_var!(satellite_systems);
62 let result = SatelliteSystems { satellite_systems };
63 trace_var!(result);
64 trace_exit!();
65 Ok(result)
66 }
67
68 #[named]
70 pub fn generate_orbits<R: Rng + ?Sized>(&self, rng: &mut R, host_star: &HostStar) -> Result<Vec<f64>, Error> {
71 trace_enter!();
72 trace_var!(host_star);
73 let mut result = Vec::new();
74 if self.generate_primary_gas_giant {
75 let orbit = rng.gen_range(1.0..1.25) + host_star.get_frost_line();
76 result.push(orbit);
77 }
78 if self.generate_habitable {
79 let habitable_zone = host_star.get_habitable_zone();
80 let orbit = rng.gen_range(habitable_zone.0..habitable_zone.1);
81 result.push(orbit);
82 }
83 let satellite_zone = host_star.get_satellite_zone();
84 trace_var!(satellite_zone);
85 let innermost_orbit = satellite_zone.0;
86 trace_var!(innermost_orbit);
87 let outermost_orbit = satellite_zone.1;
88 trace_var!(outermost_orbit);
89 let minimum = 40.0 * innermost_orbit;
90 trace_var!(minimum);
91 let distance_limit = outermost_orbit;
92 trace_var!(distance_limit);
93 let growth_factor = 0.3;
94 trace_var!(growth_factor);
95 let mut orbital_distance = minimum;
96 let mut index = 0;
97 loop {
98 let min_unwrapped = 0.80 * orbital_distance;
99 let max_unwrapped = 1.25 * orbital_distance;
100 if !result
101 .iter()
102 .any(|&orbit| orbit > min_unwrapped && orbit < max_unwrapped)
103 {
104 let orbit = rng.gen_range(min_unwrapped..max_unwrapped);
105 result.push(orbit);
106 }
107 orbital_distance = minimum + growth_factor * (2.0_f64).powf(index as f64);
108 index += 1;
109 if orbital_distance > distance_limit {
110 break;
111 }
112 }
113 result.sort_by(|a, b| a.partial_cmp(b).unwrap());
114 trace_var!(result);
115 trace_exit!();
116 Ok(result)
117 }
118}
119
120impl Default for Constraints {
121 fn default() -> Self {
123 let minimum_count = None;
124 let maximum_count = None;
125 let satellite_system_constraints = None;
126 let generate_primary_gas_giant = false;
127 let generate_habitable = false;
128 Self {
129 minimum_count,
130 maximum_count,
131 satellite_system_constraints,
132 generate_primary_gas_giant,
133 generate_habitable,
134 }
135 }
136}
137
138#[cfg(test)]
139pub mod test {
140
141 use rand::prelude::*;
142
143 use crate::astronomy::host_star::constraints::Constraints as HostStarConstraints;
144
145 use super::*;
146 use crate::test::*;
147
148 #[named]
149 #[test]
150 pub fn test_generate() -> Result<(), Error> {
151 init();
152 trace_enter!();
153 let mut rng = thread_rng();
154 trace_var!(rng);
155 let host_star = &HostStarConstraints::default().generate(&mut rng)?;
156 trace_var!(host_star);
157 let satellite_systems = &Constraints::default().generate(&mut rng, host_star)?;
158 trace_var!(satellite_systems);
159 print_var!(satellite_systems);
160 trace_exit!();
161 Ok(())
162 }
163
164 #[named]
165 #[test]
166 pub fn test_habitable() -> Result<(), Error> {
167 init();
168 trace_enter!();
169 let mut rng = thread_rng();
170 trace_var!(rng);
171 let host_star = &HostStarConstraints::habitable().generate(&mut rng)?;
172 trace_var!(host_star);
173 let satellite_systems = &Constraints::habitable().generate(&mut rng, host_star)?;
174 trace_var!(satellite_systems);
175 print_var!(satellite_systems);
176 trace_exit!();
177 Ok(())
178 }
179}