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 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
#![warn(missing_docs)]
//! # tahga::growth::hyphae
//!
//! The growth::hyphae module implements a hypha growth algorithm
//!
//! An attempt to head off some confusion: This module is called `hyphae`, and it has a submodule called `hypha`. These terms are from biology and refer to fungus growth. Hyphae is the plural of hypha. So the `hypha` module implements a struct defining a single hypha, while the `hyphae` module implements a struct that manages multiple hypha instances.
use nannou::App;
use uuid::Uuid;
use crate::growth::hyphae::hypha::{Factory, Hypha};
use crate::stroke::line::Line;
pub mod hypha;
/// Defines a system of Hyphae
#[derive(Debug)]
pub struct Hyphae {
/// A list of Hyphae in the system
pub hyphae: Vec<Hypha>,
/// A list of [`Line`]s and associated [`Hypha`] ids in the system, used for faster collision detection
pub lines: Vec<(Uuid, Line)>,
/// If false, all hyphae have died
pub living: bool,
/// A Hypha generator
pub factory: Factory,
}
impl Hyphae {
/// Create a new [`Hyphae`] instance with the default factory
///
pub fn new() -> Self {
Hyphae {
hyphae: Vec::new(),
lines: Vec::new(),
living: true,
factory: Factory::new(),
}
}
/// Create a new [`Hyphae`] instance with a custom [`Hypha`] factory
///
/// # Arguments
///
/// `factory` - the [`Factory`] instance
pub fn new_with_factory(factory: Factory) -> Self {
Hyphae {
hyphae: Vec::new(),
lines: Vec::new(),
living: true,
factory: factory,
}
}
/// Use the factory to generate a new [`Hypha`] instance
///
pub fn new_hypha(&self) -> Hypha {
self.factory.generate()
}
/// Add a [`Hypha`] to the list of hyphae
///
/// # Arguments
///
/// `hypha` - The [`Hypha`] to add
///
pub fn add_hypha(&mut self, hypha: Hypha) {
self.hyphae.push(hypha);
}
/// Update all the living hyphae
///
/// # Arguments
///
/// `app` - The Nannou app instance
///
pub fn update(&mut self, app: &App) {
let mut living = false;
for i in 0..self.hyphae.len() {
let hypha = &mut self.hyphae[i];
if hypha.dead { continue; }
living = true;
// Perform a cycle of growth this hypha
// This potentially yields a new, branched hypha
let branch_hypha = hypha.update(&self.factory);
// If the hypha would leave the canvas, it dies
let rect = app.window_rect();
if hypha.position.x > rect.right()
|| hypha.position.x < rect.left()
|| hypha.position.y > rect.top()
|| hypha.position.y < rect.bottom() {
hypha.dead = true;
continue;
}
// Check to make sure we haven't hit another line
let new_line = hypha.path.last().unwrap();
for (id, line) in &self.lines {
// If the line is for the current hypha, ignore it
if id == &hypha.id { continue; }
// If this line is the first segment if a new hypha, and we are looking at it's parent, ignore it
if hypha.path.len() == 1 {
if let Some(pid) = hypha.parent_id {
if id == &pid { continue; }
}
}
// See whether the new line collides with this line, if so, this hypha dies
if new_line.collides_with_line(line) {
hypha.dead = true;
break;
}
}
// We successfully updated the hypha without it dying; add its most recent line to the Line list
self.lines.push((hypha.id, new_line.clone()));
// Take no further action if this is just the first frame
// No branching allowed from the root
if app.elapsed_frames() < 2 { continue; }
// If a new hypha was created in the update, add it to the list
match branch_hypha {
Some(p) => {
self.hyphae.push(p);
}
None => {}
}
}
if !living { self.living = false; }
}
}