diff options
author | HampusM <hampus@hampusmat.com> | 2025-03-23 20:01:28 +0100 |
---|---|---|
committer | HampusM <hampus@hampusmat.com> | 2025-03-23 20:01:56 +0100 |
commit | fd3e5efa4609b1eabd3d982099293e04a80a1ee7 (patch) | |
tree | 780ae2b19a0bc6e2df4c03b923a9d0e8e424a42d | |
parent | 98c9c63f2471fb6662e8c542762d4f6caf4fb7cd (diff) |
-rw-r--r-- | Cargo.lock | 66 | ||||
-rw-r--r-- | ecs/Cargo.toml | 4 | ||||
-rw-r--r-- | ecs/src/component/storage.rs | 170 | ||||
-rw-r--r-- | ecs/src/component/storage/graph.rs | 12 | ||||
-rw-r--r-- | ecs/src/lib.rs | 53 |
5 files changed, 303 insertions, 2 deletions
@@ -42,6 +42,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] name = "bindgen" version = "0.68.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -56,7 +62,27 @@ dependencies = [ "proc-macro2", "quote", "regex", - "rustc-hash", + "rustc-hash 1.1.0", + "shlex", + "syn", +] + +[[package]] +name = "bindgen" +version = "0.71.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" +dependencies = [ + "bitflags 2.9.0", + "cexpr", + "clang-sys", + "itertools", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash 2.1.1", "shlex", "syn", ] @@ -238,6 +264,7 @@ dependencies = [ "thiserror", "tracing", "util-macros", + "vizoxide", ] [[package]] @@ -336,7 +363,7 @@ dependencies = [ name = "glfw" version = "0.1.0" dependencies = [ - "bindgen", + "bindgen 0.68.1", "bitflags 2.9.0", "libc", "thiserror", @@ -350,6 +377,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] +name = "graphviz-sys" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97dbac5b269c7160b78812a9873f548668aa34b62f28f7a64328a6cd94feb47d" +dependencies = [ + "bindgen 0.71.1", +] + +[[package]] name = "half" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -635,6 +671,16 @@ dependencies = [ ] [[package]] +name = "prettyplease" +version = "0.2.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5316f57387668042f561aae71480de936257848f9c43ce528e311d89a07cadeb" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] name = "proc-macro2" version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -712,6 +758,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] name = "ryu" version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -955,6 +1007,16 @@ dependencies = [ ] [[package]] +name = "vizoxide" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec190047a5e02a6423f147cf40890be1d37bbc68b9eba441689acbcd255f942" +dependencies = [ + "base64", + "graphviz-sys", +] + +[[package]] name = "walkdir" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/ecs/Cargo.toml b/ecs/Cargo.toml index 4945171..cf35a74 100644 --- a/ecs/Cargo.toml +++ b/ecs/Cargo.toml @@ -3,6 +3,9 @@ name = "ecs" version = "0.1.0" edition = "2021" +[features] +vizoxide = ["dep:vizoxide"] + [dependencies] seq-macro = "0.3.5" paste = "1.0.14" @@ -13,6 +16,7 @@ hashbrown = "0.15.2" parking_lot = "0.12.3" ecs-macros = { path = "../ecs-macros" } util-macros = { path = "../util-macros" } +vizoxide = { version = "1.0.5", optional = true } [dev-dependencies.criterion] version = "0.5.1" diff --git a/ecs/src/component/storage.rs b/ecs/src/component/storage.rs index 4b2b6d8..c70e7e7 100644 --- a/ecs/src/component/storage.rs +++ b/ecs/src/component/storage.rs @@ -421,6 +421,176 @@ impl TypeName for Storage } } +#[cfg(feature = "vizoxide")] +impl Storage +{ + pub fn create_vizoxide_archetype_graph( + &self, + graph_name: impl AsRef<str>, + params: VizoxideArchetypeGraphParams, + ) -> Result<vizoxide::Graph, vizoxide::GraphvizError> + { + let viz_graph = vizoxide::Graph::builder(graph_name.as_ref()) + .strict(true) + .directed(true) + .build()?; + + let mut viz_node_lookup = HashMap::new(); + + for node in self.graph.iter_nodes() { + let id = node.archetype().id(); + + if !viz_node_lookup.contains_key(&id) { + let node = self.graph.get_node_by_id(id).unwrap(); + + let viz_node = (params.create_node_cb)( + node.archetype(), + ArchetypeMetadata { is_imaginary: false }, + viz_graph.create_node(&(params.create_node_name)( + node.archetype(), + ArchetypeMetadata { is_imaginary: false }, + )), + ) + .build()?; + + viz_node_lookup.insert(id, viz_node); + } + + for (edge_comp_id, edges) in node.iter_edges() { + if let Some(add_edge) = edges.add { + if !viz_node_lookup.contains_key(&add_edge) { + let viz_node = self.create_vizoxide_archetype_graph_edge_node( + &viz_graph, + node, + add_edge, + *edge_comp_id, + ¶ms, + )?; + + viz_node_lookup.insert(add_edge, viz_node); + } + + (params.create_edge_cb)( + node.archetype(), + *edge_comp_id, + VizoxideArchetypeGraphEdgeKind::Add, + viz_graph.create_edge( + viz_node_lookup.get(&id).unwrap(), + viz_node_lookup.get(&add_edge).unwrap(), + Some(&format!("Add {}", edge_comp_id.id())), + ), + ) + .build()?; + } + + if let Some(remove_edge) = edges.remove { + if !viz_node_lookup.contains_key(&remove_edge) { + let viz_node = self.create_vizoxide_archetype_graph_edge_node( + &viz_graph, + node, + remove_edge, + *edge_comp_id, + ¶ms, + )?; + + viz_node_lookup.insert(remove_edge, viz_node); + } + + (params.create_edge_cb)( + node.archetype(), + *edge_comp_id, + VizoxideArchetypeGraphEdgeKind::Remove, + viz_graph.create_edge( + viz_node_lookup.get(&id).unwrap(), + viz_node_lookup.get(&remove_edge).unwrap(), + Some(&format!("Remove {}", edge_comp_id.id())), + ), + ) + .build()?; + } + } + } + + drop(viz_node_lookup); + + Ok(viz_graph) + } + + fn create_vizoxide_archetype_graph_edge_node<'vizoxide_graph>( + &self, + viz_graph: &'vizoxide_graph vizoxide::Graph, + node: &graph::ArchetypeNode, + edge_id: ArchetypeId, + edge_comp_id: Uid, + params: &VizoxideArchetypeGraphParams, + ) -> Result<vizoxide::Node<'vizoxide_graph>, vizoxide::GraphvizError> + { + match self.graph.get_node_by_id(edge_id) { + Some(edge_node) => (params.create_node_cb)( + edge_node.archetype(), + ArchetypeMetadata { is_imaginary: false }, + viz_graph.create_node(&(params.create_node_name)( + edge_node.archetype(), + ArchetypeMetadata { is_imaginary: false }, + )), + ) + .build(), + None => { + let mut comp_ids = + node.archetype().component_ids_sorted().collect::<Vec<_>>(); + + let insert_index = comp_ids.partition_point(|cid| *cid <= edge_comp_id); + + comp_ids.insert(insert_index, edge_comp_id); + + let imaginary_edge_archetype = Archetype::new(edge_id, comp_ids); + + (params.create_node_cb)( + &imaginary_edge_archetype, + ArchetypeMetadata { is_imaginary: true }, + viz_graph.create_node(&(params.create_node_name)( + &imaginary_edge_archetype, + ArchetypeMetadata { is_imaginary: true }, + )), + ) + .build() + } + } + } +} + +#[cfg(feature = "vizoxide")] +pub struct VizoxideArchetypeGraphParams +{ + pub create_node_name: fn(&Archetype, ArchetypeMetadata) -> std::borrow::Cow<'_, str>, + pub create_node_cb: for<'storage, 'graph> fn( + &'storage Archetype, + ArchetypeMetadata, + vizoxide::NodeBuilder<'graph>, + ) -> vizoxide::NodeBuilder<'graph>, + pub create_edge_cb: for<'storage, 'graph> fn( + &'storage Archetype, + Uid, + VizoxideArchetypeGraphEdgeKind, + vizoxide::EdgeBuilder<'graph>, + ) -> vizoxide::EdgeBuilder<'graph>, +} + +#[cfg(feature = "vizoxide")] +#[derive(Debug, Clone)] +pub struct ArchetypeMetadata +{ + pub is_imaginary: bool, +} + +#[cfg(feature = "vizoxide")] +#[derive(Debug, Clone, Copy)] +pub enum VizoxideArchetypeGraphEdgeKind +{ + Add, + Remove, +} + #[derive(Debug)] pub struct ArchetypeRefIter<'storage, 'search_terms> { diff --git a/ecs/src/component/storage/graph.rs b/ecs/src/component/storage/graph.rs index a221877..11160e7 100644 --- a/ecs/src/component/storage/graph.rs +++ b/ecs/src/component/storage/graph.rs @@ -71,6 +71,12 @@ impl Graph })) } + #[cfg(feature = "vizoxide")] + pub fn iter_nodes(&self) -> impl Iterator<Item = &ArchetypeNode> + { + self.nodes.iter() + } + pub fn dfs_archetype_add_edges( &self, archetype_id: ArchetypeId, @@ -233,6 +239,12 @@ impl ArchetypeNode self.edges.entry(component_id).or_insert_with(insert_fn) } + #[cfg(feature = "vizoxide")] + pub fn iter_edges(&self) -> impl Iterator<Item = (&Uid, &ArchetypeEdges)> + { + self.edges.iter() + } + pub fn get_edges_mut(&mut self, component_id: Uid) -> Option<&mut ArchetypeEdges> { debug_assert_eq!(component_id.kind(), UidKind::Component); diff --git a/ecs/src/lib.rs b/ecs/src/lib.rs index 9b787a2..3adc415 100644 --- a/ecs/src/lib.rs +++ b/ecs/src/lib.rs @@ -246,6 +246,59 @@ impl World while let StepResult::Continue = self.step() {} } + #[cfg(feature = "vizoxide")] + pub fn create_vizoxide_archetype_graph( + &self, + name: impl AsRef<str>, + ) -> Result<vizoxide::Graph, vizoxide::GraphvizError> + { + use std::borrow::Cow; + + use crate::component::storage::{ + VizoxideArchetypeGraphEdgeKind, + VizoxideArchetypeGraphParams, + }; + + let component_storage_lock = self + .data + .component_storage + .read_nonblock() + .expect("Failed to acquire read-only component storage lock"); + + component_storage_lock.create_vizoxide_archetype_graph( + name, + VizoxideArchetypeGraphParams { + create_node_name: |archetype, _| { + Cow::Owned(format!( + "[{}]", + archetype + .component_ids_sorted() + .into_iter() + .map(|comp_id| comp_id.id().to_string()) + .collect::<Vec<_>>() + .join(", ") + )) + }, + create_node_cb: |_archetype, archetype_metadata, node_builder| { + if archetype_metadata.is_imaginary { + return node_builder.attribute("shape", "ellipse"); + } + + node_builder.attribute("shape", "box") + }, + create_edge_cb: |_, _, edge_kind, edge_builder| { + edge_builder.attribute( + "color", + match edge_kind { + VizoxideArchetypeGraphEdgeKind::Add => "green", + VizoxideArchetypeGraphEdgeKind::Remove => "red", + }, + ) + }, + }, + ) + } + fn query_and_run_systems(&self, phase_euid: Uid) { let system_comps_query = |