starfall/astronomy/satellite_systems/constraints/
mod.rs

1use 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/// Constraints for creating satellite systems.
11#[derive(Clone, Copy, Debug, PartialEq)]
12pub struct Constraints {
13  /// The minimum number to generate.
14  pub minimum_count: Option<usize>,
15  /// The maximum number to generate.
16  pub maximum_count: Option<usize>,
17  /// Satellite System constraints.
18  pub satellite_system_constraints: Option<SatelliteSystemConstraints>,
19  /// Generate a primary gas giant.
20  pub generate_primary_gas_giant: bool,
21  /// Generate a habitable planet.
22  pub generate_habitable: bool,
23}
24
25impl Constraints {
26  /// Generate a habitable star subsystem.
27  #[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  /// Generate.
45  #[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  /// Generate orbits.
69  #[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  /// No constraints, just let it all hang out.
122  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}