From 68da2e7422120f953819082b32334d25fa490499 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Henry?= Date: Wed, 6 Dec 2023 23:46:04 +0100 Subject: [PATCH] Test release 4.0-beta --- examples/default-viewer-body/cfg.yaml | 9 + .../cfg/cfg.yaml | 57 ++++ .../cfg/cfg.yaml | 37 ++- .../cfg/cfg.yaml | 25 +- preferences.yaml | 5 +- src/body/orbit.rs | 46 ++- src/cfg/body.rs | 37 +-- src/cfg/config.rs | 57 ++-- src/cfg/scene.rs | 120 ++++---- src/cfg/simulation.rs | 17 +- src/cfg/spice.rs | 10 +- src/simu/body.rs | 15 +- src/simu/export.rs | 22 +- src/simu/routines/core.rs | 291 +++++++++++------- src/simu/routines/thermal.rs | 13 +- src/simu/scenario.rs | 26 +- src/simu/util.rs | 4 +- 17 files changed, 488 insertions(+), 303 deletions(-) create mode 100644 examples/default-viewer-body/cfg.yaml create mode 100644 examples/ground-based-lightcurve-didymos-spice-dart-impact/cfg/cfg.yaml diff --git a/examples/default-viewer-body/cfg.yaml b/examples/default-viewer-body/cfg.yaml new file mode 100644 index 0000000..8ca2166 --- /dev/null +++ b/examples/default-viewer-body/cfg.yaml @@ -0,0 +1,9 @@ +window: + ambient: [0.0, 0.0, 0.0] + +scene: + sun: [1.0, 0.0, 0.0] + camera: [5.0, 0.0, 0.0] + +bodies: + - id: 0 \ No newline at end of file diff --git a/examples/ground-based-lightcurve-didymos-spice-dart-impact/cfg/cfg.yaml b/examples/ground-based-lightcurve-didymos-spice-dart-impact/cfg/cfg.yaml new file mode 100644 index 0000000..4eef462 --- /dev/null +++ b/examples/ground-based-lightcurve-didymos-spice-dart-impact/cfg/cfg.yaml @@ -0,0 +1,57 @@ +window: + export_frames: true + +spice: + kernel: C:/data/SPICE/dart-520/mk/d520_v01.tm + frame: J2000 + +simulation: + start: 2022-09-26 23:14:24.000 + step: 60 + duration: 8136 + export: + step: 60 + duration: 8136 + pause_after_first_iteration: true + +scene: + sun: + position: + !spice + camera: + position: + !spice Earth + distance: 5.0 + +bodies: + - id: didymos + mesh: + shape: sphere + factor: [0.4095, 0.4005, 0.3035] + smooth: true + material: + albedo: 0.1 + state: + !spice + frame_from: didymos_fixed + + - id: dimorphos + mesh: + shape: sphere + factor: [0.0895165, 0.0825, 0.0575] + smooth: true + material: + albedo: 0.1 + state: + !spice + origin: didymos + frame_from: dimorphos_fixed + + - id: dart + mesh: + shape: cube + factor: [0.01, 0.01, 0.01] + state: + !cartesian + position: [-0.00831, 0.0832, 0.013] + reference: dimorphos diff --git a/examples/ground-based-lightcurve-didymos-spice/cfg/cfg.yaml b/examples/ground-based-lightcurve-didymos-spice/cfg/cfg.yaml index da502a4..c17ea26 100644 --- a/examples/ground-based-lightcurve-didymos-spice/cfg/cfg.yaml +++ b/examples/ground-based-lightcurve-didymos-spice/cfg/cfg.yaml @@ -1,28 +1,47 @@ window: export_frames: true - + +spice: + kernel: C:/data/SPICE/dart-520/mk/d520_v01.tm + frame: J2000 + simulation: - start: 2022-09-26 + start: 2022-09-26 22:30:00 step: 60 duration: 8136 export: step: 60 duration: 8136 - pause_after_first_iteration: true scene: - spice: /Users/gregoireh/data/spice/dart/mk/d420.tm + sun: + position: + !spice camera: - from: Earth - distance: 2 + position: + !spice Earth + distance: 5.0 bodies: - - id: Didymos + - id: didymos mesh: shape: sphere factor: [0.4095, 0.4005, 0.3035] smooth: true material: albedo: 0.1 - spin: - period: 8136 \ No newline at end of file + state: + !spice + frame_from: didymos_fixed + + - id: dimorphos + mesh: + shape: sphere + factor: [0.0895165, 0.0825, 0.0575] + smooth: true + material: + albedo: 0.1 + state: + !spice + origin: didymos + frame_from: dimorphos_fixed \ No newline at end of file diff --git a/examples/ground-based-lightcurve-didymos/cfg/cfg.yaml b/examples/ground-based-lightcurve-didymos/cfg/cfg.yaml index 84fa96d..e136449 100644 --- a/examples/ground-based-lightcurve-didymos/cfg/cfg.yaml +++ b/examples/ground-based-lightcurve-didymos/cfg/cfg.yaml @@ -1,31 +1,32 @@ window: export_frames: true - + simulation: step: 60 duration: 8136 export: step: 60 duration: 8136 + pause_after_first_iteration: true scene: - camera: - !earth 2.0 sun: - type: equatorial - ra: 12h12m22.42s - dec: -01°20'20.3" + position: + !equatorial + ra: 12h12m22.42s + dec: -01°20'20.3" + camera: + !reference bodies: - - mesh: + - id: didymos + mesh: shape: sphere factor: [0.4095, 0.4005, 0.3035] smooth: true material: albedo: 0.1 - spin: - period: 8136 state: - type: equatorial - ra: 03h21m43.477s - dec: -33°22'44.57" \ No newline at end of file + !equatorial + ra: 03h21m43.477s + dec: -33°22'44.57" \ No newline at end of file diff --git a/preferences.yaml b/preferences.yaml index c52908e..f12905d 100644 --- a/preferences.yaml +++ b/preferences.yaml @@ -1 +1,4 @@ -no_check: true \ No newline at end of file +no_check: true + +debug_cfg: true +debug: false \ No newline at end of file diff --git a/src/body/orbit.rs b/src/body/orbit.rs index 48f87b9..4dad06c 100644 --- a/src/body/orbit.rs +++ b/src/body/orbit.rs @@ -12,7 +12,8 @@ use uom::{ str::ParseQuantityError, }; -pub type OrbitResult = std::result::Result; +pub type AstronomicalAngleResult = + std::result::Result; #[derive(Debug, Snafu)] pub enum AstronomicalAngleConversionError { @@ -43,6 +44,13 @@ pub enum AstronomicalAngleConversionError { }, } +pub type EquatorialResult = std::result::Result; + +#[derive(Debug, Snafu)] +pub enum EquatorialError { + DistanceNotDefined {}, +} + #[derive(Debug, Clone)] pub enum DMS { D, @@ -74,7 +82,7 @@ impl AstronomicalAngle { Self(angle) } - pub fn parse(s: &str) -> OrbitResult { + pub fn parse(s: &str) -> AstronomicalAngleResult { match Self::from_value(s) { Ok(angle) => return Ok(angle), Err(_) => {} @@ -95,13 +103,13 @@ impl AstronomicalAngle { }) } - pub fn from_value(s: &str) -> OrbitResult { + pub fn from_value(s: &str) -> AstronomicalAngleResult { s.parse::() .context(AngleParsingSnafu {}) .and_then(|angle| Ok(Self::new(angle))) } - pub fn from_hms(s: &str) -> OrbitResult { + pub fn from_hms(s: &str) -> AstronomicalAngleResult { // To find the int hours: \d{1,2}h // To find the int minutes: \d{1,2}m // To find the float seconds: [+-]?(\d+([.]\d*)?([eE][+-]?\d+)?|[.]\d+([eE][+-]?\d+)?)s @@ -136,7 +144,7 @@ impl AstronomicalAngle { Ok(Self::new(Angle::new::(radians))) } - pub fn from_dms(s: &str) -> OrbitResult { + pub fn from_dms(s: &str) -> AstronomicalAngleResult { // To find the int degrees: [+-]?\d{1,2}° // To find the int minutes: \d{1,2}' // To find the float seconds: [+-]?(\d+([.]\d*)?([eE][+-]?\d+)?|[.]\d+([eE][+-]?\d+)?)" @@ -175,7 +183,7 @@ impl AstronomicalAngle { impl FromStr for AstronomicalAngle { type Err = AstronomicalAngleConversionError; - fn from_str(s: &str) -> OrbitResult { + fn from_str(s: &str) -> AstronomicalAngleResult { Self::parse(s) } } @@ -227,16 +235,38 @@ orientation: [ */ #[derive(Debug, Clone, Default, Deserialize, Serialize)] pub struct Equatorial { + #[serde(default)] pub ra: AstronomicalAngle, + + #[serde(default)] pub dec: AstronomicalAngle, + + #[serde(default)] + pub distance: Option, } impl Equatorial { pub fn new(ra: AstronomicalAngle, dec: AstronomicalAngle) -> Self { - Self { ra, dec } + Self { + ra, + dec, + distance: None, + } + } + + pub fn with_distance(self, distance: Float) -> Self { + Self { + ra: self.ra, + dec: self.dec, + distance: Some(distance), + } + } + + pub fn xyz(&self) -> EquatorialResult { + Ok(self.xyz_with_distance(self.distance.context(DistanceNotDefinedSnafu {})?)) } - pub fn xyz(&self, distance: Float) -> Vec3 { + pub fn xyz_with_distance(&self, distance: Float) -> Vec3 { distance * vec3( (self.ra.cos() * self.dec.cos()).value, diff --git a/src/cfg/body.rs b/src/cfg/body.rs index f8398e8..92c6bf8 100644 --- a/src/cfg/body.rs +++ b/src/cfg/body.rs @@ -3,7 +3,6 @@ use crate::{ Shapes, }; -use core::panic; use serde::{Deserialize, Serialize}; use serde_yaml::Value; use snafu::{prelude::*, Location}; @@ -518,7 +517,6 @@ e: 0.5 */ #[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(tag = "type")] pub enum CfgState { #[serde(rename = "cartesian")] Cartesian(CfgStateCartesian), @@ -527,7 +525,7 @@ pub enum CfgState { Equatorial(Equatorial), #[serde(rename = "orbit")] - Orbit(CfgOrbitKepler), + Orbit(CfgStateOrbit), #[serde(rename = "file")] File(PathBuf), @@ -536,15 +534,6 @@ pub enum CfgState { Spice(CfgStateSpice), } -impl CfgState { - pub fn as_equatorial(&self) -> BodyResult<&Equatorial> { - match self { - Self::Equatorial(coords) => Ok(coords), - _ => panic!("nono"), - } - } -} - impl Default for CfgState { fn default() -> Self { Self::Cartesian(CfgStateCartesian::default()) @@ -577,22 +566,9 @@ pub struct CfgStateCartesian { /// Default is: `[1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0]` #[serde(default = "default_orientation")] pub orientation: Mat3, -} -impl CfgStateCartesian { - pub fn position_only(position: Vec3) -> Self { - Self { - position, - orientation: Mat3::identity(), - } - } - - pub fn orientation_only(orientation: Mat3) -> Self { - Self { - position: Vec3::zeros(), - orientation, - } - } + #[serde(default)] + pub reference: Option, } impl Default for CfgStateCartesian { @@ -600,6 +576,7 @@ impl Default for CfgStateCartesian { Self { position: Vec3::zeros(), orientation: default_orientation(), + reference: None, } } } @@ -633,7 +610,7 @@ In this sense, just [`a`][CfgOrbitKepler::a] and [`e`][CfgOrbitKepler::e] are us */ #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct CfgOrbitKepler { +pub struct CfgStateOrbit { /// Heliocentric distance. /// The units are determined automatically depending on the center of the frame: /// @@ -674,7 +651,7 @@ pub struct CfgOrbitKepler { pub control: CfgOrbitSpeedControl, } -impl Default for CfgOrbitKepler { +impl Default for CfgStateOrbit { fn default() -> Self { Self { a: default_orbit_a(), @@ -766,7 +743,7 @@ pub struct CfgStateSpice { pub origin: Option, #[serde(default)] - pub frame: Option, + pub frame_from: Option, #[serde(default)] pub frame_to: Option, diff --git a/src/cfg/config.rs b/src/cfg/config.rs index 12da710..2cc0282 100644 --- a/src/cfg/config.rs +++ b/src/cfg/config.rs @@ -1,4 +1,7 @@ -use crate::{CfgBody, CfgBodyError, CfgPreferences, CfgScene, CfgSimulation, CfgSpice, CfgWindow}; +use crate::{CfgBody, CfgBodyError, CfgPreferences, CfgScene, CfgSimulation, CfgWindow}; + +#[cfg(feature = "spice")] +use crate::CfgSpice; use itertools::Itertools; use serde::{de::DeserializeOwned, Deserialize, Serialize}; @@ -19,6 +22,30 @@ pub const CFG_VIEWER_STR: &str = include_str!("../../examples/viewer/cfg/cfg.yam pub const CFG_VIEWER_PICKER_STR: &str = include_str!("../../examples/viewer-picker/cfg/cfg.yaml"); pub const CFG_VIEWER_SMOOTH_STR: &str = include_str!("../../examples/viewer-smooth/cfg/cfg.yaml"); +pub type CfgResult = std::result::Result; + +/// Errors related to Kalast config. +#[derive(Debug, Snafu)] +pub enum CfgError { + CfgFileNotFound { + source: io::Error, + path: PathBuf, + }, + + CfgReading { + source: serde_yaml::Error, + path: PathBuf, + }, + + CfgParsingEquatorial { + source: CfgBodyError, + location: Location, + }, + + #[snafu(display("Feature `spice` is not enabled."))] + FeatureSpiceNotEnabled {}, +} + #[derive(Clone, Debug, Serialize, Deserialize)] pub enum Cfgs { #[serde(rename = "empty")] @@ -65,25 +92,6 @@ pub fn cfg_viewer() -> Cfg { serde_yaml::from_str(CFG_VIEWER_STR).unwrap() } -pub type CfgResult = std::result::Result; - -/// Errors related to Kalast config. -#[derive(Debug, Snafu)] -pub enum CfgError { - CfgFileNotFound { - source: io::Error, - path: PathBuf, - }, - CfgReading { - source: serde_yaml::Error, - path: PathBuf, - }, - CfgParsingEquatorial { - source: CfgBodyError, - location: Location, - }, -} - pub fn read_cfg(path: P) -> CfgResult where P: AsRef, @@ -297,8 +305,13 @@ impl Cfg { &self.extra } - pub fn using_spice(&self) -> bool { - self.scene.spice.is_some() + #[cfg(feature = "spice")] + pub fn is_spice_loaded(&self) -> bool { + self.spice.kernel.is_some() + } + + pub fn body_index(&self, id: &str) -> Option { + self.bodies.iter().position(|body| body.id == id) } } diff --git a/src/cfg/scene.rs b/src/cfg/scene.rs index e73e2df..6f83f17 100644 --- a/src/cfg/scene.rs +++ b/src/cfg/scene.rs @@ -2,8 +2,6 @@ use crate::{util::*, CfgBodyError, Configuration, Equatorial}; use serde::{Deserialize, Serialize}; use snafu::{prelude::*, Location}; -use std::path::PathBuf; -use strum::{Display, EnumString}; pub type SceneResult = std::result::Result; @@ -19,21 +17,17 @@ pub enum CfgSceneError { #[derive(Clone, Debug, Serialize, Deserialize)] pub struct CfgScene { #[serde(default)] - pub spice: Option, + pub sun: CfgSun, #[serde(default)] pub camera: CfgCamera, - - #[serde(default)] - pub sun: CfgSun, } impl Default for CfgScene { fn default() -> Self { Self { - spice: None, - camera: CfgCamera::default(), sun: CfgSun::default(), + camera: CfgCamera::default(), } } } @@ -41,92 +35,102 @@ impl Default for CfgScene { impl Configuration for CfgScene {} #[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(untagged)] -pub enum CfgCamera { - #[serde(rename = "position")] - Position(Vec3), - - /// Camera in from the direction of the Sun (Sun behind camera). - /// The float is the distance from the center of frame to the camera. - /// - /// TODO: COMPLETE DOC - #[serde(rename = "from")] - From(CfgCameraFrom), +pub struct CfgSun { + #[serde(default)] + pub position: CfgSunPosition, } -impl CfgCamera { +impl CfgSun { pub fn default_position() -> Vec3 { - Vec3::x() * 5.0 + Vec3::x() * Self::default_distance() + } + + pub fn default_distance() -> Float { + 1.0 } } -impl Default for CfgCamera { +impl Default for CfgSun { fn default() -> Self { - Self::Position(Self::default_position()) + Self { + position: CfgSunPosition::default(), + } } } #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct CfgCameraFrom { - #[serde(default)] - pub from: CfgCameraFromOptions, +pub enum CfgSunPosition { + #[serde(rename = "cartesian")] + Cartesian(Vec3), - #[serde(default)] - #[serde(alias = "distance")] - pub distance_origin: Float, -} + #[serde(rename = "equatorial")] + Equatorial(Equatorial), -#[derive(Clone, Debug, Serialize, Deserialize, EnumString, Display)] -pub enum CfgCameraFromOptions { - #[serde(rename = "sun")] - #[serde(alias = "Sun")] - Sun, + #[serde(rename = "spice")] + #[serde(alias = "from_spice")] + Spice, - #[serde(rename = "earth")] - #[serde(alias = "Earth")] - Earth, + #[serde(rename = "body")] + #[serde(alias = "from_body")] + FromBody, } -impl Default for CfgCameraFromOptions { +impl Default for CfgSunPosition { fn default() -> Self { - Self::Sun + Self::Cartesian(CfgSun::default_position()) } } #[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(untagged)] -pub enum CfgSun { - #[serde(rename = "position")] - Position(Vec3), - - #[serde(rename = "equatorial")] - Equatorial(Equatorial), +pub struct CfgCamera { + #[serde(default)] + pub position: CfgCameraPosition, - #[serde(rename = "from")] - From(CfgSunFrom), + #[serde(default)] + #[serde(alias = "distance")] + pub distance_origin: Option, } -impl CfgSun { +impl CfgCamera { pub fn default_position() -> Vec3 { Vec3::x() * Self::default_distance() } pub fn default_distance() -> Float { - 1.0 + 5.0 } } -impl Default for CfgSun { +impl Default for CfgCamera { fn default() -> Self { - Self::Position(Self::default_position()) + Self { + position: CfgCameraPosition::default(), + distance_origin: None, + } } } #[derive(Clone, Debug, Serialize, Deserialize)] -pub enum CfgSunFrom { - #[serde(rename = "from_spice")] - Spice, +pub enum CfgCameraPosition { + #[serde(rename = "cartesian")] + Cartesian(Vec3), + + #[serde(rename = "sun")] + #[serde(alias = "Sun")] + #[serde(alias = "from_sun")] + #[serde(alias = "from_Sun")] + FromSun, + + #[serde(rename = "spice")] + #[serde(alias = "from_spice")] + Spice(String), + + #[serde(rename = "reference")] + Reference, +} - #[serde(rename = "from_orbit_body")] - OrbitBody, +impl Default for CfgCameraPosition { + fn default() -> Self { + Self::Cartesian(CfgCamera::default_position()) + } } diff --git a/src/cfg/simulation.rs b/src/cfg/simulation.rs index 5a0b99c..7d37403 100644 --- a/src/cfg/simulation.rs +++ b/src/cfg/simulation.rs @@ -1,15 +1,20 @@ -use crate::{util::*, Configuration}; +use crate::{util::*, CfgError, Configuration}; use serde::{Deserialize, Serialize}; use snafu::prelude::*; -pub type SimulationResult = std::result::Result; +pub type CfgSimulationResult = std::result::Result; /// Errors related to Kalast config. #[derive(Debug, Snafu)] pub enum CfgSimulationError { - #[snafu(display("Feature `spice` is not enabled."))] - FeatureSpiceNotEnabled {}, + CfgSpiceError { source: CfgError }, +} + +impl From for CfgSimulationError { + fn from(value: CfgError) -> Self { + Self::CfgSpiceError { source: value } + } } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -60,14 +65,14 @@ pub enum TimeOption { } impl TimeOption { - pub fn seconds(&self) -> SimulationResult { + pub fn seconds(&self) -> CfgSimulationResult { match self { Self::Seconds(v) => Ok(*v), Self::String(_s) => { #[cfg(feature = "spice")] return Ok(spice::str2et(_s)); #[cfg(not(feature = "spice"))] - return Err(CfgSimulationError::FeatureSpiceNotEnabled {}); + return Err(CfgError::FeatureSpiceNotEnabled {}).context(CfgSpiceSnafu); } } } diff --git a/src/cfg/spice.rs b/src/cfg/spice.rs index 694f208..5a4536f 100644 --- a/src/cfg/spice.rs +++ b/src/cfg/spice.rs @@ -15,17 +15,21 @@ pub struct CfgSpice { #[serde(default)] pub kernel: Option, - #[serde(default)] - pub frame: Option, + #[serde(default = "default_frame")] + pub frame: String, } impl Default for CfgSpice { fn default() -> Self { Self { kernel: None, - frame: None, + frame: default_frame(), } } } impl Configuration for CfgSpice {} + +pub fn default_frame() -> String { + "ECLIPJ2000".to_string() +} diff --git a/src/simu/body.rs b/src/simu/body.rs index badca39..0cb26f1 100644 --- a/src/simu/body.rs +++ b/src/simu/body.rs @@ -3,15 +3,14 @@ use crate::{matrix_orientation_obliquity, util::*, AirlessBody, CfgBody}; use itertools::Itertools; #[derive(Debug, Clone)] -pub struct PreComputedBody { - pub mat_orient: Mat4, +pub struct BodyData { pub normals: Matrix3xX, + pub translation: Mat4, + pub orientation: Mat4, } -impl PreComputedBody { +impl BodyData { pub fn new(asteroid: &AirlessBody, cb: &CfgBody) -> Self { - let mat_orient = matrix_orientation_obliquity(0.0, cb.spin.obliquity * RPD); - let normals = Matrix3xX::from_columns( &asteroid .surface @@ -21,9 +20,13 @@ impl PreComputedBody { .collect_vec(), ); + let translation = Mat4::identity(); + let orientation = matrix_orientation_obliquity(0.0, cb.spin.obliquity * RPD); + Self { - mat_orient, normals, + translation, + orientation, } } } diff --git a/src/simu/export.rs b/src/simu/export.rs index 15c6aa6..b0a22ab 100644 --- a/src/simu/export.rs +++ b/src/simu/export.rs @@ -1,6 +1,6 @@ use crate::{ - simu::Scene, util::*, AirlessBody, Cfg, CfgTimeExport, FoldersRun, PreComputedBody, - Routines, Time, Window, + simu::Scene, util::*, AirlessBody, BodyData, Cfg, CfgTimeExport, FoldersRun, Routines, Time, + Window, }; use polars::prelude::{df, CsvWriter, NamedFrom, SerWriter}; @@ -31,7 +31,7 @@ impl Export { &mut self, cfg: &Cfg, bodies: &mut [AirlessBody], - precomputed: &[PreComputedBody], + body_data: &[BodyData], time: &mut Time, scene: &Scene, win: &Window, @@ -92,13 +92,7 @@ impl Export { for body in 0..cfg.bodies.len() { if self.is_first_it_export { self.iteration_body_export_start_generic( - cfg, - body, - bodies, - precomputed, - time, - scene, - folders, + cfg, body, bodies, body_data, time, scene, folders, ); } routines.fn_export_iteration_period( @@ -136,8 +130,8 @@ impl Export { &self, cfg: &Cfg, body: usize, - _bodies: &mut [AirlessBody], - precomputed: &[PreComputedBody], + bodies: &mut [AirlessBody], + body_data: &[BodyData], time: &Time, scene: &Scene, folders: &FoldersRun, @@ -183,7 +177,9 @@ impl Export { CsvWriter::new(&mut file).finish(&mut df).unwrap(); let mut df = df!( - "spinrot" => precomputed[body].mat_orient.as_slice(), + "translation" => body_data[body].translation.as_slice(), + "orientation" => body_data[body].orientation.as_slice(), + "model" => bodies[body].matrix_model.as_slice(), ) .unwrap(); let mut file = std::fs::File::options() diff --git a/src/simu/routines/core.rs b/src/simu/routines/core.rs index d4c9ba0..9351853 100644 --- a/src/simu/routines/core.rs +++ b/src/simu/routines/core.rs @@ -1,14 +1,10 @@ use crate::{ compute_cosine_emission_angle, compute_cosine_incidence_angle, compute_cosine_phase_angle, find_ref_orbit, matrix_spin, position_in_inertial_frame, simu::Scene, update_colormap_scalar, - util::*, AirlessBody, Cfg, CfgBody, CfgCamera, CfgCameraFrom, CfgCameraFromOptions, - CfgFrameCenter, CfgScalar, CfgState, CfgStateCartesian, CfgSun, CfgSunFrom, FoldersRun, - PreComputedBody, Time, Window, + util::*, AirlessBody, BodyData, Cfg, CfgBody, CfgCamera, CfgCameraPosition, CfgFrameCenter, + CfgScalar, CfgState, CfgStateCartesian, CfgSun, CfgSunPosition, FoldersRun, Time, Window, }; -#[cfg(feature = "spice")] -use crate::CfgStateSpice; - use downcast_rs::{impl_downcast, DowncastSync}; use itertools::{izip, Itertools}; @@ -20,102 +16,146 @@ pub trait Routines: DowncastSync { fn fn_update_scene(&self, cfg: &Cfg, time: &Time, _scene: &Scene) -> Scene { let elapsed_from_start = time.elapsed_seconds_from_start(); - let sun = match &cfg.scene.sun { - CfgSun::Position(p) => *p, - CfgSun::Equatorial(coords) => coords.xyz(CfgSun::default_distance()), - CfgSun::From(from) => match from { - CfgSunFrom::Spice => { - if cfg.using_spice() { - #[cfg(not(feature = "spice"))] - { - panic!("Feature `spice` is not enabled. The feature is required to compute the position of the Sun.") - } + if cfg.preferences.debug { + println!("Routine default fn_update_scene"); + println!("Iteration: {}", time.iteration()); + } - #[cfg(feature = "spice")] - { - if let Some(body) = cfg.bodies.first() { - let (position, _lt) = spice::spkpos( - "Sun", - elapsed_from_start as f64, - "ECLIPJ2000", - "none", - &body.id, - ); - Vec3::from_row_slice(&position) - } else { - panic!("A body must be loaded to compute the position of the Sun.") - } + let mut sun = match &cfg.scene.sun.position { + CfgSunPosition::Cartesian(p) => *p, + CfgSunPosition::Equatorial(coords) => { + coords.xyz_with_distance(coords.distance.unwrap_or(CfgSun::default_distance())) + } + CfgSunPosition::Spice => { + #[cfg(not(feature = "spice"))] + { + panic!("Feature `spice` is not enabled. The feature is required to compute the position of the Sun.") + } + + #[cfg(feature = "spice")] + { + if cfg.is_spice_loaded() { + if let Some(body) = cfg.bodies.first() { + let (position, _lt) = spice::spkpos( + "Sun", + elapsed_from_start as f64, + &cfg.spice.frame, + "none", + &body.id, + ); + Vec3::from_row_slice(&position) + } else { + panic!("A body must be loaded to compute the position of the Sun.") } } else { panic!("Spice is not being used and is needed to compute the position of the Sun. Try loading a spice kernel to enable spice.") } } - CfgSunFrom::OrbitBody => { - if let Some(body) = cfg.bodies.first() { - match &body.state { - CfgState::Orbit(orbit) => match &orbit.frame { - CfgFrameCenter::Sun => -position_in_inertial_frame( - orbit.a * AU, - orbit.e, - orbit.i * RPD, - orbit.node * RPD, - orbit.peri * RPD, - elapsed_from_start as Float, - orbit.tp, - MU_SUN, - ), - CfgFrameCenter::Body(_) => CfgSun::default_position(), - }, - CfgState::Cartesian(_) - | CfgState::Equatorial(_) - | CfgState::File(_) - | CfgState::Spice(_) => CfgSun::default_position(), + } + CfgSunPosition::FromBody => { + if let Some(body) = cfg.bodies.first() { + match &body.state { + CfgState::Orbit(orbit) => match &orbit.frame { + CfgFrameCenter::Sun => -position_in_inertial_frame( + orbit.a * AU, + orbit.e, + orbit.i * RPD, + orbit.node * RPD, + orbit.peri * RPD, + elapsed_from_start as Float, + orbit.tp, + MU_SUN, + ), + CfgFrameCenter::Body(_) => { + if time.iteration() == 0 { + println!("Warning: The Sun is set to be configured from the state of the primary body but only works if the state is an orbit centered on the Sun."); + } + CfgSun::default_position() + } + }, + CfgState::Cartesian(_) + | CfgState::Equatorial(_) + | CfgState::File(_) + | CfgState::Spice(_) => { + if time.iteration() == 0 { + println!("Warning: The Sun is set to be configured from the state of the primary body but only works if the state is an orbit centered on the Sun."); + } + + CfgSun::default_position() } - } else { - panic!("A body must be loaded to compute the position of the Sun.") } + } else { + panic!("A body must be loaded to compute the position of the Sun.") } - }, + } }; - let camera = match &cfg.scene.camera { - CfgCamera::Position(p) => *p, - CfgCamera::From(CfgCameraFrom { - from, - distance_origin, - }) => match &from { - CfgCameraFromOptions::Sun => sun.normalize() * *distance_origin, - CfgCameraFromOptions::Earth => { - if let Some(_body) = cfg.bodies.first() { - if cfg.using_spice() { - #[cfg(not(feature = "spice"))] - { - panic!("Feature `spice` is not enabled. The feature is required to compute the position of the camera from Earth direction.") - } + let camera = match &cfg.scene.camera.position { + CfgCameraPosition::Cartesian(p) => *p, + CfgCameraPosition::FromSun => { + sun.normalize() + * cfg + .scene + .camera + .distance_origin + .unwrap_or(CfgCamera::default_distance()) + } + CfgCameraPosition::Spice(_name) => { + if let Some(_body) = cfg.bodies.first() { + #[cfg(not(feature = "spice"))] + { + panic!("Feature `spice` is not enabled. The feature is required to compute the position of the camera from Earth direction.") + } - #[cfg(feature = "spice")] - { - let (position, _lt) = spice::spkpos( - "Earth", - elapsed_from_start as f64, - "ECLIPJ2000", - "none", - &_body.id, - ); - let position = Vec3::from_row_slice(&position); - - position.normalize() * *distance_origin - } + #[cfg(feature = "spice")] + { + if cfg.is_spice_loaded() { + let (position, _lt) = spice::spkpos( + _name, + elapsed_from_start as f64, + &cfg.spice.frame, + "none", + &_body.id, + ); + let position = Vec3::from_row_slice(&position); + + position.normalize() + * cfg + .scene + .camera + .distance_origin + .unwrap_or(CfgCamera::default_distance()) } else { panic!("Spice is not being used and is needed to compute the position of the camera from Earth direction. Try loading a spice kernel to enable spice.") } - } else { - panic!("A body must be loaded to compute the position of the camera from Earth direction. Visualisation is centered on body") } + } else { + panic!("A body must be loaded to compute the position of the camera from Earth direction. Visualisation is centered on body") + } + } + CfgCameraPosition::Reference => { + if let Some(body) = cfg.bodies.first() { + match &body.state { + CfgState::Equatorial(coords) => { + let position = -coords.xyz_with_distance( + coords.distance.unwrap_or(CfgCamera::default_distance()), + ); + sun += position; + position + } + _ => panic!("Camera on reference mode only work with primary body state equatorial."), + } + } else { + panic!("No body has been loaded to compute camera position.") } - }, + } }; + if cfg.preferences.debug { + println!("camera: {:?}", camera.as_slice()); + println!("sun: {:?}", sun.as_slice()); + } + Scene { camera, sun } } @@ -123,32 +163,27 @@ pub trait Routines: DowncastSync { &self, cfg: &Cfg, body: usize, - pre_computed_bodies: &mut [PreComputedBody], + bodies_data: &mut [BodyData], time: &Time, _scene: &Scene, ) -> Mat4 { let elapsed_from_start = time.elapsed_seconds_from_start(); match &cfg.bodies[body].state { - CfgState::Spice(_from) => { + CfgState::Spice(_spice) => { #[cfg(not(feature = "spice"))] panic!("Feature `spice` is not enabled. The feature is required to compute the position of the camera from Earth direction."); #[cfg(feature = "spice")] { - let CfgStateSpice { - origin, - frame, - frame_to, - } = _from; - let frame = frame.clone().unwrap_or("J2000".to_string()); - let position = { - if let Some(origin) = origin { + if let Some(origin) = &_spice.origin { + let frame_to = + _spice.frame_to.clone().unwrap_or(cfg.spice.frame.clone()); let (position, _lt) = spice::spkpos( &cfg.bodies[body].id, elapsed_from_start as f64, - &frame, + &frame_to, "none", &origin, ); @@ -159,31 +194,36 @@ pub trait Routines: DowncastSync { }; let rotation = { - if let Some(frame_to) = frame_to { + if let Some(frame) = &_spice.frame_from { + let frame_to = + _spice.frame_to.clone().unwrap_or(cfg.spice.frame.clone()); let rotation = - spice::pxform(&frame, frame_to, elapsed_from_start as f64); + spice::pxform(&frame, &frame_to, elapsed_from_start as f64); Mat3::from_row_slice(&rotation.iter().cloned().flatten().collect_vec()) } else { Mat3::identity() } }; - let mut matrix_model = Mat4::new_translation(&position); - - for (e, new) in izip!( - matrix_model.fixed_view_mut::<3, 3>(1, 1).iter_mut(), - rotation.iter() - ) { - *e = *new; + if cfg.preferences.debug { + println!("Body state with spice"); + println!("position: {:?}", position.as_slice()); + println!("rotation: {}", rotation); } - matrix_model + let matrix_translation = Mat4::new_translation(&position); + let matrix_orientation = glm::mat3_to_mat4(&rotation); + + bodies_data[body].translation = matrix_translation; + bodies_data[body].orientation = matrix_orientation; + + matrix_translation * matrix_orientation } } anything_else => { - let mut matrix_orientation_reference = Mat4::identity(); + let mut matrix_model_reference = Mat4::identity(); - let mut matrix_orientation = pre_computed_bodies[body].mat_orient; + let mut matrix_orientation = bodies_data[body].orientation; let elapsed = time.elapsed_seconds(); let np_elapsed = if cfg.bodies[body].spin.period == 0.0 { @@ -208,10 +248,31 @@ pub trait Routines: DowncastSync { CfgState::Cartesian(CfgStateCartesian { position, orientation, + reference, }) => { - matrix_orientation = glm::mat3_to_mat4(orientation); matrix_translation = Mat4::new_translation(position); + matrix_orientation = glm::mat3_to_mat4(orientation); + + if let Some(reference) = reference { + let ref_id = cfg + .body_index(reference) + .expect(&format!("No body loaded with this id {}", reference)); + + // matrix_orientation = matrix_orientation * bodies_data[ref_id].orientation; + + matrix_model_reference = + bodies_data[ref_id].translation * bodies_data[ref_id].orientation; + } + + if cfg.preferences.debug { + println!("Body state with manual cartesian"); + println!("position: {:?}", position.as_slice()); + println!("rotation: {}", orientation); + println!("matrix model reference: {}", matrix_model_reference); + } } + + CfgState::Equatorial(_) => {} CfgState::Orbit(orbit) => { let (mu_ref, factor) = find_ref_orbit(&orbit, &other_bodies); if mu_ref != MU_SUN { @@ -231,22 +292,24 @@ pub trait Routines: DowncastSync { match &orbit.frame { CfgFrameCenter::Sun => {} CfgFrameCenter::Body(id) => { - for (pre, cb) in izip!(pre_computed_bodies.iter_mut(), &cfg.bodies) - { + for (pre, cb) in izip!(bodies_data.iter_mut(), &cfg.bodies) { if cb.id == *id { - matrix_orientation_reference = pre.mat_orient; + matrix_model_reference = pre.orientation; break; } } } } } - CfgState::Equatorial(_) | CfgState::File(_) => {} + CfgState::File(_) => {} _ => panic!("tempo"), }; + bodies_data[body].translation = matrix_translation; + bodies_data[body].orientation = matrix_orientation; + let matrix_body = matrix_translation * matrix_orientation * matrix_spin; - matrix_orientation_reference * matrix_body + matrix_model_reference * matrix_body } } } @@ -255,7 +318,7 @@ pub trait Routines: DowncastSync { &mut self, _body: usize, _bodies: &mut [AirlessBody], - _pre_computed_bodies: &mut [PreComputedBody], + _pre_computed_bodies: &mut [BodyData], _time: &Time, _scene: &Scene, ) { @@ -265,7 +328,7 @@ pub trait Routines: DowncastSync { &self, body: usize, bodies: &mut [AirlessBody], - pre_computed_bodies: &[PreComputedBody], + pre_computed_bodies: &[BodyData], cfg: &Cfg, scene: &Scene, win: &Window, diff --git a/src/simu/routines/thermal.rs b/src/simu/routines/thermal.rs index f6fc1cf..2ea28ec 100644 --- a/src/simu/routines/thermal.rs +++ b/src/simu/routines/thermal.rs @@ -1,9 +1,8 @@ use crate::{ compute_cosine_emission_angle, compute_cosine_incidence_angle, compute_cosine_phase_angle, effective_temperature, flux_solar_radiation, newton_method_temperature, read_surface_low, - shadows, simu::Scene, update_colormap_scalar, util::*, AirlessBody, Cfg, CfgBody, CfgScalar, - CfgTemperatureInit, FoldersRun, PreComputedBody, Routines, RoutinesData, Surface, - Time, Window, + shadows, simu::Scene, update_colormap_scalar, util::*, AirlessBody, BodyData, Cfg, CfgBody, + CfgScalar, CfgTemperatureInit, FoldersRun, Routines, RoutinesData, Surface, Time, Window, }; use itertools::Itertools; @@ -125,7 +124,7 @@ pub trait RoutinesThermal: Routines { fn fn_compute_solar_flux( &self, body: &AirlessBody, - precomputed: &PreComputedBody, + precomputed: &BodyData, body_info: &ThermalData, scene: &Scene, ) -> DRVector; @@ -170,7 +169,7 @@ impl Routines for RoutinesThermalDefault { &mut self, body: usize, bodies: &mut [AirlessBody], - pre_computed_bodies: &mut [PreComputedBody], + pre_computed_bodies: &mut [BodyData], time: &Time, scene: &Scene, ) { @@ -227,7 +226,7 @@ impl Routines for RoutinesThermalDefault { &self, body: usize, bodies: &mut [AirlessBody], - pre_computed_bodies: &[PreComputedBody], + pre_computed_bodies: &[BodyData], cfg: &Cfg, scene: &Scene, win: &Window, @@ -428,7 +427,7 @@ impl RoutinesThermal for RoutinesThermalDefault { fn fn_compute_solar_flux( &self, body: &AirlessBody, - precomputed: &PreComputedBody, + precomputed: &BodyData, body_info: &ThermalData, scene: &Scene, ) -> DRVector { diff --git a/src/simu/scenario.rs b/src/simu/scenario.rs index 243a73a..f63e5fc 100644 --- a/src/simu/scenario.rs +++ b/src/simu/scenario.rs @@ -1,7 +1,7 @@ use crate::{ - check_if_latest_version, read_surface_main, simu::Scene, util::*, AirlessBody, Cfg, CfgCamera, - CfgInterior, CfgInteriorGrid1D, CfgRoutines, CfgSun, Export, FoldersRun, FrameEvent, - PreComputedBody, Result, Routines, RoutinesThermalDefault, RoutinesViewerDefault, Time, Window, + check_if_latest_version, read_surface_main, simu::Scene, util::*, AirlessBody, BodyData, Cfg, + CfgCamera, CfgInterior, CfgInteriorGrid1D, CfgRoutines, CfgSun, Export, FoldersRun, FrameEvent, + Result, Routines, RoutinesThermalDefault, RoutinesViewerDefault, Time, Window, }; use chrono::Utc; @@ -16,7 +16,7 @@ pub struct Scenario { pub win: Window, pub folders: FoldersRun, pub routines: Box, - pub pre_computed_bodies: Vec, + pub pre_computed_bodies: Vec, } impl Scenario { @@ -62,8 +62,12 @@ impl Scenario { }; #[cfg(feature = "spice")] - if let Some(path) = &cfg.scene.spice { - spice::furnsh(path.to_str().unwrap()); + { + spice::kclear(); + + if let Some(path) = &cfg.spice.kernel { + spice::furnsh(path.to_str().unwrap()); + } } let scene = Scene { @@ -140,8 +144,7 @@ impl Scenario { }, }; - self.pre_computed_bodies - .push(PreComputedBody::new(&asteroid, &cb)); + self.pre_computed_bodies.push(BodyData::new(&asteroid, &cb)); self.bodies.push(asteroid); } @@ -280,6 +283,9 @@ impl Scenario { it ); + #[cfg(feature = "spice")] + spice::kclear(); + if paused_stop { paused_stop = false; self.win.toggle_pause(); @@ -294,8 +300,4 @@ impl Scenario { Ok(()) } - - pub fn orientation_reference(&self, body: usize) -> &Mat4 { - &self.pre_computed_bodies[body].mat_orient - } } diff --git a/src/simu/util.rs b/src/simu/util.rs index 51ca8a2..067a0c3 100644 --- a/src/simu/util.rs +++ b/src/simu/util.rs @@ -1,6 +1,6 @@ use crate::{ cosine_angle, simu::Scene, util::*, AirlessBody, Cfg, CfgBody, CfgFrameCenter, CfgMeshKind, - CfgMeshSource, CfgOrbitKepler, CfgOrbitSpeedControl, Material, Result, Surface, Window, + CfgMeshSource, CfgOrbitSpeedControl, CfgStateOrbit, Material, Result, Surface, Window, }; use itertools::{izip, Itertools}; @@ -48,7 +48,7 @@ pub fn read_surface_low(cb: &CfgBody) -> Result { } /// return MU and factor for distances. -pub fn find_ref_orbit(orbit: &CfgOrbitKepler, cfgs: &[&CfgBody]) -> (Float, Float) { +pub fn find_ref_orbit(orbit: &CfgStateOrbit, cfgs: &[&CfgBody]) -> (Float, Float) { match &orbit.frame { CfgFrameCenter::Sun => (MU_SUN, AU), CfgFrameCenter::Body(id) => (