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; }
    }
}