use serde::{Deserialize, Serialize};
pub use sharira::allometry;
pub use sharira::biomechanics;
pub use sharira::body;
pub use sharira::fatigue as phys_fatigue;
pub use sharira::gait;
pub use sharira::ik;
pub use sharira::joint;
pub use sharira::kinematics;
pub use sharira::morphology;
pub use sharira::muscle;
pub use sharira::pose;
pub use sharira::preset as body_preset;
pub use sharira::skeleton;
pub use sharira::ShariraError;
pub use jivanu::biofilm;
pub use jivanu::epidemiology;
pub use jivanu::genetics;
pub use jivanu::growth;
pub use jivanu::metabolism as micro_metabolism;
pub use jivanu::resistance;
pub use jivanu::taxonomy;
pub use jivanu::JivanuError;
pub use rasayan::calcium;
pub use rasayan::enzyme;
pub use rasayan::etc;
pub use rasayan::glycolysis;
pub use rasayan::hormonal;
pub use rasayan::membrane;
pub use rasayan::metabolism as cell_metabolism;
pub use rasayan::neurotransmitter as biochem_nt;
pub use rasayan::pathway;
pub use rasayan::protein;
pub use rasayan::signal;
pub use rasayan::tca;
pub use rasayan::RasayanError;
pub use vanaspati::biomass;
pub use vanaspati::fire;
pub use vanaspati::growth as plant_growth;
pub use vanaspati::herbivory;
pub use vanaspati::mortality;
pub use vanaspati::mycorrhiza;
pub use vanaspati::nitrogen;
pub use vanaspati::pft;
pub use vanaspati::phenology;
pub use vanaspati::photosynthesis;
pub use vanaspati::reproduction;
pub use vanaspati::root;
pub use vanaspati::season;
pub use vanaspati::succession;
pub use vanaspati::water as soil_water;
pub use vanaspati::VanaspatiError;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Physiology {
pub gait_type: PhysiologyGait,
pub speed: f32,
pub fatigue: f32,
pub mass: f32,
pub active: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
#[non_exhaustive]
pub enum PhysiologyGait {
#[default]
Walk,
Run,
Trot,
Gallop,
Crawl,
Swim,
Fly,
}
impl Physiology {
pub fn new(mass: f32) -> Self {
tracing::trace!(mass, "created physiology");
Self {
gait_type: PhysiologyGait::Walk,
speed: 0.0,
fatigue: 0.0,
mass,
active: true,
}
}
pub fn with_gait(mut self, gait: PhysiologyGait) -> Self {
self.gait_type = gait;
self
}
pub fn with_speed(mut self, speed: f32) -> Self {
self.speed = speed;
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Microbe {
pub species: String,
pub population: f64,
pub carrying_capacity: f64,
pub growth_rate: f64,
pub phase: MicrobePhase,
pub active: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
#[non_exhaustive]
pub enum MicrobePhase {
#[default]
Lag,
Exponential,
Stationary,
Death,
}
impl Microbe {
pub fn new(species: impl Into<String>, population: f64, carrying_capacity: f64) -> Self {
let species = species.into();
tracing::trace!(
%species, population, carrying_capacity,
"created microbe"
);
Self {
species,
population,
carrying_capacity,
growth_rate: 0.5,
phase: MicrobePhase::Lag,
active: true,
}
}
pub fn with_growth_rate(mut self, rate: f64) -> Self {
self.growth_rate = rate;
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MetabolicProfile {
pub atp: f64,
pub glucose: f64,
pub oxygen: f64,
pub lactate: f64,
pub active: bool,
}
impl MetabolicProfile {
pub fn new() -> Self {
Self {
atp: 1.0,
glucose: 1.0,
oxygen: 1.0,
lactate: 0.0,
active: true,
}
}
#[must_use]
#[inline]
pub fn is_energy_crisis(&self) -> bool {
self.atp < 0.2
}
#[must_use]
#[inline]
pub fn is_anaerobic(&self) -> bool {
self.oxygen < 0.1
}
}
impl Default for MetabolicProfile {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PlantState {
pub species: String,
pub height: f64,
pub stage: PlantStage,
pub leaf_mass: f64,
pub stem_mass: f64,
pub root_mass: f64,
pub soil_water: f64,
pub active: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
#[non_exhaustive]
pub enum PlantStage {
#[default]
Seed,
Germination,
Seedling,
Vegetative,
Flowering,
Fruiting,
Senescence,
Dormant,
}
impl PlantState {
pub fn seed(species: impl Into<String>) -> Self {
let species = species.into();
tracing::trace!(%species, "created plant seed");
Self {
species,
height: 0.0,
stage: PlantStage::Seed,
leaf_mass: 0.0,
stem_mass: 0.0,
root_mass: 0.0,
soil_water: 0.5,
active: true,
}
}
pub fn mature(species: impl Into<String>, height: f64) -> Self {
let species = species.into();
tracing::trace!(%species, height, "created mature plant");
Self {
species,
height,
stage: PlantStage::Vegetative,
leaf_mass: height * 0.1,
stem_mass: height * 0.5,
root_mass: height * 0.3,
soil_water: 0.5,
active: true,
}
}
#[must_use]
#[inline]
pub fn above_ground_mass(&self) -> f64 {
self.leaf_mass + self.stem_mass
}
#[must_use]
#[inline]
pub fn total_mass(&self) -> f64 {
self.leaf_mass + self.stem_mass + self.root_mass
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn physiology_builder() {
let p = Physiology::new(70.0)
.with_gait(PhysiologyGait::Run)
.with_speed(3.0);
assert_eq!(p.mass, 70.0);
assert_eq!(p.gait_type, PhysiologyGait::Run);
assert_eq!(p.speed, 3.0);
}
#[test]
fn microbe_builder() {
let m = Microbe::new("E. coli", 1000.0, 1e9).with_growth_rate(0.8);
assert_eq!(m.species, "E. coli");
assert_eq!(m.growth_rate, 0.8);
assert_eq!(m.phase, MicrobePhase::Lag);
}
#[test]
fn metabolic_homeostasis() {
let m = MetabolicProfile::new();
assert!(!m.is_energy_crisis());
assert!(!m.is_anaerobic());
}
#[test]
fn metabolic_crisis() {
let m = MetabolicProfile {
atp: 0.1,
oxygen: 0.05,
..Default::default()
};
assert!(m.is_energy_crisis());
assert!(m.is_anaerobic());
}
#[test]
fn plant_seed() {
let p = PlantState::seed("Oak");
assert_eq!(p.stage, PlantStage::Seed);
assert_eq!(p.height, 0.0);
assert_eq!(p.total_mass(), 0.0);
}
#[test]
fn plant_mature() {
let p = PlantState::mature("Pine", 10.0);
assert_eq!(p.stage, PlantStage::Vegetative);
assert!(p.total_mass() > 0.0);
assert!(p.above_ground_mass() > 0.0);
}
#[test]
fn physiology_as_component() {
let mut world = crate::World::new();
let e = world.spawn();
world.insert_component(e, Physiology::new(80.0)).unwrap();
assert!(world.has_component::<Physiology>(e));
}
#[test]
fn microbe_as_component() {
let mut world = crate::World::new();
let e = world.spawn();
world
.insert_component(e, Microbe::new("S. aureus", 500.0, 1e8))
.unwrap();
let m = world.get_component::<Microbe>(e).unwrap();
assert_eq!(m.species, "S. aureus");
}
#[test]
fn plant_as_component() {
let mut world = crate::World::new();
let e = world.spawn();
world.insert_component(e, PlantState::seed("Fern")).unwrap();
assert!(world.has_component::<PlantState>(e));
}
}