node_tree 0.15.0

An extendable scene graph made up of autonomous execution services known as nodes organized in a tree of processes. Inspired by Godot!
Documentation
# NodeTree
[![Static Badge](https://img.shields.io/badge/GITHUB-LunaticWyrm467%2Fnode_tree-LunaticWyrm467%2Fnode_tree?style=for-the-badge&logo=github)](https://github.com/LunaticWyrm467/node_tree)
[![Crates.io Version](https://img.shields.io/crates/v/node_tree?style=for-the-badge&logo=rust)](https://crates.io/crates/node_tree)
[![Static Badge](https://img.shields.io/badge/DOCS.RS-node_tree-66c2a5?style=for-the-badge&logo=docs.rs)](https://docs.rs/node_tree)
![Crates.io License](https://img.shields.io/crates/l/node_tree?color=green&style=for-the-badge)

**NodeTree** is a scene graph framework to create large scalable programs and games through a tree of processes. Each process is fully autonomous and is capable of storing its own state or data, and communicating with other processes. These processes are known as Nodes.

**⚠️WARNING⚠️**<br>
This crate is in early development. Beware of possible bugs or safety violations.<br>

## Getting Started!
Simply either run `cargo add node_tree` at the terminal directed towards the directory of your project, or add `node_tree = X.X` to your `cargo.toml` file.

To begin creating a program in Rust that utilizes a `NodeTree`, we must first create a root `Node`. In order to reduce boilerplate, we will use the included `class!` macro to implement the required `Dynamic`, `NodeAbstract`, and `Node` traits.
```rust
use std::ffi::c_void;
use node_tree::prelude::*;


class! {

    // Standard procedure for declaring a class. This supports doc comments.
    declare NodeA;

    // Fields are declared as such:
    let id: u32;

    // Fields can have custom attributes, and can also have default values (either implicit via `default` or explicit).
    default let default_field: u8; // Initialized as its default value.
    export  let savable_field: String      = "Hello, World!".to_string(); // Can also be `export default` if the value supports it.
    unique  let unique_field:  *mut c_void = todo!(); // Value that is not cloned with the node.
    
    // Overrideable system functions are known as hooks and start with `hk`.

    /// Constructors are declared via `_init()`. These will automatically generate a `new()` function.
    /// Fields are initialized by introducing a variable of the same name into scope.
    hk _init(id: u32) {}
    
    /// Runs right before the `ready()` function for a `Node` that was loaded from the disk,
    /// when said node is added back to the scene tree.
    hk loaded(&mut self) {
        // Run set up code here to reinitialize unique or non-export/default fields...
    }

    /// Runs once the Node is added to the NodeTree.
    hk ready(&mut self) {

        // To show off how you could add children nodes.
        if self.depth() < 3 {
            let new_depth: usize = self.depth() + 1;
            
            self.add_child(NodeA::new(new_depth as u32));
            self.add_child(NodeA::new(new_depth as u32));
            self.add_child(NodeA::new(new_depth as u32));
        }

        if self.is_root() {
            println!("{:?}", self.children());
        }
    }

    /// Runs once per frame. Provides a delta value in seconds between frames.
    hk process(&mut self, delta: f32) {

        // Example of using the delta value to calculate the current framerate.
        println!("{} | {}", self.name(), 1f32 / delta);

        // Using the NodePath and TreePointer, you can reference other nodes in the NodeTree from this node.
        if self.is_root() {
            match self.get_node::<NodeA>(nodepath!("1_Node/2_Node1/3_Node2")).to_option() {
                Some(node) => println!("{:?}", node),
                None       => ()
            }
        }

        // Nodes can be destroyed. When destroyed, their references from the NodeTree are cleaned up as well.
        // If the root node is destroyed, then the program automatically exits. (There are other ways to
        // terminate the program such as the queue_termination() function on the NodeTree instance).
        if self.children().is_empty() {
            self.free();   // We test the progressive destruction of nodes from the tip of the tree
                           // to the base.
        }
    }

    /// Runs once a Node is removed from the NodeTree, whether that is from the program itself terminating or not.
    hk terminal(&mut self, reason: TerminationReason) {}   // We do not do anything here for this example.

    /// Returns this node's process mode.
    /// Each process mode controls how the process() function behaves when the NodeTree is paused or not.
    /// (The NodeTree can be paused or unpaused with the pause() or unpause() functions respectively.)
    hk process_mode(&self) -> ProcessMode {
        ProcessMode::Inherit    // We will return the default value, which inherits the behaviour from
                                // the parent node.
    }
}
```

Finally, in order to activate our `NodeTree`, we must instance the root `Node` and feed it into the `NodeTree` constructor.
```rust,ignore
// ...previous implementations
use node_tree::trees::TreeSimple;


fn main() -> () {

    // Create the tree.
    let root: NodeA           = NodeA::new(0);
    let tree: Box<TreeSimple> = TreeSimple::new(root, LoggerVerbosity::NoDebug);

    // Begin operations on the tree.
    while tree.process().is_active() {}
}
```

## Node Scenes
You may also input a `NodeScene` when initializing a `NodeTree` or adding a child via `add_child`:
```rust, ignore
use node_tree::prelude::*;


let child_scene: NodeScene = scene! {
    NodeA(3): "2_Node" [ // Arguments can be fed right in the scene! macro, followed by the name (optional) and children.
        NodeA(4): "3_Node",
        NodeA(5): "3_Node",
        NodeA(6): "3_Node" [
            NodeA(7): "4_Node",
            NodeA(8): "4_Node"
        ]
    ]
};
let parent_scene: NodeScene = scene! {
    NodeA(2): "1_Node" [
        $child_scene, // You can use `$` to reference other scenes as children.
        $child_scene,
        $child_scene
    ]
};
let scene: NodeScene = scene! {
    NodeA(1): "Root" {

        // You can also initialise the node's public fields.
        default_field:  1,
        saveable_field: "Hello World!".to_string(),
        unique_field:   todo!(),
        
        // And the children can be initialised along with the fields.
        [
            $parent_scene,
            $parent_scene,
            $parent_scene
        ]
    }
};

// Scenes can also be cloned, stored, and reused.
// 
// # Note
// Saved node scenes are stored in .scn files, with a toml format.
let cloned_scene: NodeScene = scene.clone();
    cloned_scene.save(Path::new(""), "foo").unwrap(); // Pass the directory and the scene name.
let loaded_scene: NodeScene = NodeScene::load(Path::new("foo.scn")).unwrap();

// A built in hashing function allows for structural integrity of scenes to be checked.
// (`NodeScene` has a custom implementation for `std::hash::Hash`.)
// 
// # Note
// This only hashes the tree's layout, note types, and ownership.
// This does not hash or keep any reference to the node's fields.
assert_eq!(scene.structural_hash(), loaded_scene.structural_hash());
```

## Logging
Logging is also supported. Here is an example setup with an output of a warning and a crash. Note that the crash header/footer are customizable, and that the output is actually colored in a real terminal.
```rust, should_panic
use node_tree::prelude::*;
use node_tree::trees::TreeSimple;


class! {
    declare NodeA;

    hk ready(&mut self) {
        if self.depth() == 2 && self.name() == "NodeA1" {
            warn!(self, "Failed to Initialize!");
        }
        
        if self.depth() == 1 && self.name() == "NodeA" {
            self.get_node::<NodeA>(nodepath!("Foo/Bar")).unwrap();
        }
    }
}


fn main() {
    let scene: NodeScene = scene! {
        NodeA [
            NodeA,
            NodeA,
            NodeA [
                NodeA,
                NodeA,
                NodeA
            ]
        ]
    };

    let mut tree: Box<TreeSimple> = TreeSimple::new(scene, LoggerVerbosity::All);
    while !tree.process().has_terminated() {}
}
```

![](dynamic_logger.png)

## Signals
Signals are introduced in order to allow for easy communication between various nodes. An example is shown below:
```rust, should_panic
use node_tree::prelude::*;
use node_tree::trees::TreeSimple;


class! {
    declare NodeA;
    
    signal on_event(count: u8);
    
    let count: u8 = 0;
    
    hk ready(&mut self) {
        let child: Tp<NodeB> = self.get_child(0).unwrap();
        connect! { on_event -> child.listener }; // You can also use `~>` which designates a one-shot connection!
    }
    
    hk process(&mut self, _delta: f32) {
        self.on_event.emit(self.count);
        self.count += 1;
    }
}


class! {
    declare NodeB;

    fn listener(&self, count: &u8) {
        if *count == 3 {
            panic!("This was successful!");
        }
    }
}


fn main() {
    let scene: NodeScene = scene! {
        NodeA [
            NodeB
        ]
    };

    let mut tree: Box<TreeSimple> = TreeSimple::new(scene, LoggerVerbosity::All);
    while tree.process().is_active() {}
}
```

## Proto-Inheritance with Traits
Rust does not support inheritance in a typical sense, so this crate attempts to emulate core aspects of it that are important to scene graphs.
```rust
use node_tree::prelude::*;


// We define a trait that has a specific behaviour for a group of node classes. This can be useful for UI nodes, physics nodes, or nodes
// that we would wish to iterate in a generic setting and otherwise access some sort of generalised behaviour.
trait Attribute {
    fn say_something(&self) -> &'static str;
}

// We then declare the nodes that will extend from this trait and implement the trait for all of them outside of the class macro.
class! {
    declare AttributeNode1 extends Attribute;
}

class! {
    declare AttributeNode2 extends Attribute;
}

class! {
    declare AttributeNode3 extends Attribute;
}

impl Attribute for AttributeNode1 {
    fn say_something(&self) -> &'static str {
        "Foo!"
    }
}

impl Attribute for AttributeNode2 {
    fn say_something(&self) -> &'static str {
        "Bar!"
    }
}

impl Attribute for AttributeNode3 {
    fn say_something(&self) -> &'static str {
        "Baz!"
    }
}

// We then define a controller node that will iterate through its children and run the generalised behaviour without knowing the concrete node
// type.
class! {
    declare ControlNode;

    hk process(&mut self, _: f32) {
        let mut log: Vec<&'static str> = Vec::with_capacity(3);

        for i in 0..self.num_children() {
            let child:  TpDyn          = self.get_child_dyn(i).unwrap(); // Only generic Tree Pointers support trait casting.
            let casted: &dyn Attribute = child.cast().unwrap();

            log.push(casted.say_something());
        }

        assert_eq!(log, vec!["Foo!", "Bar!", "Baz!"]);

        // ...
    }
}
```
In order for this specific example to work, the scene is assumed to be in this layout:
```rust,ignore
let scene: NodeScene = scene! {
    ControlNode [
        AttributeNode1,
        AttributeNode2,
        AttributeNode3
    ]
};
```

## Supported Features
- `glam` - Enables support with glam's (v0.29.*) types when it comes with saving and loading.

## Highlights
- πŸ—οΈ An easy abstraction framework for different processes to communicate and interact with each other in a scalable manner. Inspired by Godot!
- ⏯️ The ability to `pause()` and `unpause()` the `NodeTree`, and fine tune individual `Node` behaviours for when a tree is paused/unpaused.
- πŸ“‘ Various methods to communicate with other nodes, such as `owner()`, `parent()`, `get_child()`, `children()`, and `get_node()`, as well as methods to automate the process such as signals.
- πŸ”— An abstracted smart pointer known as `Tp<T>` and `TpDyn` which clones implicitly to reduce syntax noise and allows for low boilerplate.
- πŸ—ƒοΈ A way to cast `TpDyn` into dynamic trait objects of traits the tree pointer's node extends from.
- πŸ“š A caching system hosted on the `NodeTree` to act as a safe interface to ensure `Tp<T>`/`TpDyn` soundness, and increase performance!
- πŸ‘ͺ The ability to manage nodes with `add_child()` and `remove_child()`.
- πŸ“ Includes a dynamic logging and error handling system that is deeply integrated with the node framework.
- 🌲 Allows for the direct referencing of the `NodeTree` through a node's `root()` function.
- πŸ“œ Includes functionality to save, load and handle individual node scenes, such as the handy visual macro `scene!`.