- More Documentation

This commit is contained in:
2025-08-18 19:41:08 -04:00
parent 733fca41ef
commit 7cd38604a7
4 changed files with 239 additions and 35 deletions

View File

@@ -41,6 +41,12 @@ namespace fennec
/// ///
/// \brief Graph Data Structure, describes sets of arbitrarily connected nodes /// \brief Graph Data Structure, describes sets of arbitrarily connected nodes
///
/// \details
/// Graphs contain nodes (sometimes called vertices) and connections. Graphs are either directed
/// or undirected. This structure allows the creation of both directed and undirected connections. As
/// far as what that means; a directed graph means that connections have direction, where there are connections
/// that are "to" and "from," rather than "between" which is used in undirected graphs There are .
/// \tparam NodeT The type associated with each node /// \tparam NodeT The type associated with each node
/// \tparam WeightT The type associated with each connection /// \tparam WeightT The type associated with each connection
template<typename NodeT, typename WeightT = nullptr_t> template<typename NodeT, typename WeightT = nullptr_t>
@@ -78,8 +84,19 @@ public:
/// \name Assignment Operators /// \name Assignment Operators
/// @{ /// @{
///
/// \brief Copy Assignment Operator
/// \param g The graph to copy
/// \returns A reference to this after assigning g
constexpr graph& operator=(const graph& g) = default; constexpr graph& operator=(const graph& g) = default;
///
/// \brief Move Assignment Operator
/// \param g The graph to copy
/// \returns A reference to this after assigning g
constexpr graph& operator=(graph&& g) = default; constexpr graph& operator=(graph&& g) = default;
/// @} /// @}
@@ -87,66 +104,169 @@ public:
/// \name Properties /// \name Properties
/// @{ /// @{
///
/// \returns The number of nodes in the graph
constexpr size_t num_nodes() const { constexpr size_t num_nodes() const {
return _node_pool.size(); return _node_pool.size();
} }
///
/// \returns The number of connections in the graph
constexpr size_t num_connections() const { constexpr size_t num_connections() const {
return _conn_pool.size(); return _conn_pool.size();
} }
///
/// \returns The capacity of the node pool
constexpr size_t capacity() const { constexpr size_t capacity() const {
return _node_pool.capacity(); return _node_pool.capacity();
} }
///
/// \returns `true` when there are no nodes in the graph, `false` otherwise
constexpr bool empty() const { constexpr bool empty() const {
return num_nodes() == 0; return num_nodes() == 0;
} }
/// @} /// @}
// Nodes =============================================================================================================== // Access ==============================================================================================================
/// \name Access
/// @{
///
/// \brief Node Access Operator
/// \param node The id of the node
/// \returns A reference to the value stored in the node
constexpr node_t& operator[](size_t node) {
return _node_pool[node];
}
///
/// \brief Node Const Access Operator
/// \param node The id of the node
/// \returns A reference to the value stored in the node
constexpr const node_t& operator[](size_t node) const {
return _node_pool[node];
}
///
/// \brief Connection Access Operator
/// \param a The id of the first node
/// \param b The id of the second node
/// \returns A reference to the value stored in the connection
constexpr weight_t& operator[](size_t a, size_t b) {
weight_t* it = _conn_map[a][b];
assertd(it, "Element not Found!");
return _conn_pool[*it];
}
///
/// \brief Connection Const Access Operator
/// \param a The id of the first node
/// \param b The id of the second node
/// \returns A const-qualified reference to the value stored in the connection
constexpr const weight_t& operator[](size_t a, size_t b) const {
weight_t* it = _conn_map[a][b];
assertd(it, "Element not Found!");
return _conn_pool[*it];
}
///
/// \brief Getter for a list of nodes that `node` has outgoing connections to
/// \param node The id of the node
/// \returns A list containing all nodes `x` with connections from `node` to `x...`
list<size_t> outgoing(size_t node) {
list<size_t> res;
for (const auto& it : _conn_map[node]) {
res.push_back(it.first);
}
return res;
}
///
/// \brief Getter for a list of nodes that `node` has incoming connections from
/// \param node The id of the node
/// \returns A list containing all nodes `x` with connections from `x...` to `node`
list<size_t> incoming(size_t node) {
list<size_t> res;
for (int n = 0; n < _conn_map.size(); ++n) {
if (_conn_map[n][node]) {
res.push_back(n);
}
}
return res;
}
///
/// \brief
/// \param node A list of all nodes `x` that have symmetric connections with `node`, i.e. `node` has a connection
/// both to and from `x...`
/// \returns A list containing all nodes `x` that have symmetric connections with `node`
list<size_t> symmetric(size_t node) {
list<size_t> res;
for (const auto& it : _conn_map[node]) {
if (_conn_map[it.first][node]) {
res.push_back(it.first);
}
}
return res;
}
/// @}
// Modifiers ===========================================================================================================
/// \name Modifiers
/// @{
///
/// \brief Move a new node into the graph
/// \param node The node to move into the graph
/// \returns The id of the new node
constexpr size_t insert(node_t&& node) { constexpr size_t insert(node_t&& node) {
return this->_insert(fennec::forward<node_t>(node)); return this->_insert(fennec::forward<node_t>(node));
} }
///
/// \brief Copy a new node into the graph
/// \param node The node to copy into the graph
/// \returns The id of the new node
constexpr size_t insert(const node_t& node) { constexpr size_t insert(const node_t& node) {
return this->_insert(node); return this->_insert(node);
} }
///
/// \brief Construct a new node in the graph
/// \tparam ArgsT The types of the arguments
/// \param args The arguments to construct the node with
/// \returns The id of the new node
template<typename...ArgsT> template<typename...ArgsT>
constexpr size_t emplace(ArgsT&&...args) { constexpr size_t emplace(ArgsT&&...args) {
return this->_insert(fennec::forward<ArgsT>(args)...); return this->_insert(fennec::forward<ArgsT>(args)...);
} }
///
/// \brief Erase a node from the graph
/// \param node The id of the node to erase
constexpr void erase(size_t node) { constexpr void erase(size_t node) {
disconnect(node); disconnect(node);
_node_pool.erase(node); _node_pool.erase(node);
} }
constexpr node_t& operator[](size_t node) { ///
return _node_pool[node]; /// \brief Form a connection from node `a` to node `b`
} /// \tparam ArgsT The argument types
/// \param a The first node id
constexpr const node_t& operator[](size_t node) const { /// \param b The second node id
return _node_pool[node]; /// \param args The arguments to construct the connection with
}
// Connections =========================================================================================================
list<pair<size_t, size_t>> connections(size_t n) {
list<pair<size_t, size_t>> conns;
for (size_t i = 0; i < _conn_map.size(); ++i) {
for (auto it : _conn_map[i]) {
if (i == n || it.first == n) {
conns.emplace_back(i, n);
}
}
}
return conns;
}
template<typename...ArgsT> template<typename...ArgsT>
constexpr void connect(size_t a, size_t b, ArgsT&&...args) { constexpr void connect(size_t a, size_t b, ArgsT&&...args) {
if (a == b) { if (a == b) {
@@ -165,11 +285,66 @@ public:
} else { } else {
conn = _conn_pool.emplace(fennec::forward<ArgsT>(args)...); conn = _conn_pool.emplace(fennec::forward<ArgsT>(args)...);
} }
_conn_map[a].emplace(b, conn); _conn_map[a].emplace(b, conn);
} }
///
/// \brief Form a bidirectional connection between node `a` and node `b`
/// \tparam ArgsT The argument types
/// \param a The first node id
/// \param b The second node id
/// \param args The arguments to construct the connection with
template<typename...ArgsT>
constexpr void connect2(size_t a, size_t b, ArgsT&&...args) {
if (a == b) {
return;
}
if (_conn_map.size() < _node_pool.capacity()) {
_conn_map.resize(_node_pool.capacity());
}
auto it = _conn_map[a][b];
size_t conn;
if (it != nullptr) {
conn = *it;
_conn_pool[conn] = node_t(fennec::forward<ArgsT>(args)...);
} else {
conn = _conn_pool.emplace(fennec::forward<ArgsT>(args)...);
}
_conn_map[a].emplace(b, conn);
_conn_map[b].emplace(a, conn);
}
///
/// \brief Disconnect a connection from node `a` to node `b`
/// \param a The first node id
/// \param b The second node id
constexpr void disconnect(size_t a, size_t b) { constexpr void disconnect(size_t a, size_t b) {
// Find the connection object
const auto* it = _conn_map[a][b];
if (not it) {
return;
}
size_t c = *it;
// Check if bi-directional
const auto* at = _conn_map[b][a];
if (not at || *at != c) {
_conn_pool.erase(c);
}
// Erase the connection mapping
_conn_map[a].erase(b);
}
///
/// \brief Disconnect a bidirectional connection between nodes `a` and `b`
/// \param a The first node id
/// \param b The second node id
constexpr void disconnect2(size_t a, size_t b) {
const auto* it = _conn_map[a][b]; const auto* it = _conn_map[a][b];
if (not it) { if (not it) {
return; return;
@@ -177,24 +352,25 @@ public:
size_t c = *it; size_t c = *it;
_conn_pool.erase(c); _conn_pool.erase(c);
_conn_map[a].erase(b); _conn_map[a].erase(b);
_conn_map[b].erase(a);
} }
///
/// \brief Break *all* connections to and from `n`
/// \param n The node id
void disconnect(size_t n) { void disconnect(size_t n) {
const auto conns = connections(n); for (const auto it : outgoing(n)) {
for (const auto& conn : conns) { disconnect(n, it);
disconnect(conn.first, conn.second); }
for (const auto it : incoming(n)) {
disconnect(it, n);
} }
} }
constexpr weight_t& operator[](size_t a, size_t b) { /// @}
weight_t* it = _conn_map[a][b];
assertd(it, "Element not Found!");
return _conn_pool[*it];
}
constexpr const weight_t& operator[](size_t a, size_t b) const {
return _conn_pool[_conn_map[a][b]]; // Connections =========================================================================================================
}
private: private:

View File

@@ -297,6 +297,11 @@ public:
} }
// Printing Operations =================================================================================================
// Error Handling ====================================================================================================== // Error Handling ======================================================================================================
const char* get_error() const { return _error; } const char* get_error() const { return _error; }

View File

@@ -6,6 +6,7 @@ set(CMAKE_C_STANDARD 23)
add_executable(fennec-test add_executable(fennec-test
main.cpp main.cpp
tests/containers/performance/test_iterator_visitor.h
) )
target_compile_definitions(fennec-test PUBLIC FENNEC_TEST_CWD="${CMAKE_SOURCE_DIR}/bin/${FENNEC_BUILD_NAME}" target_compile_definitions(fennec-test PUBLIC FENNEC_TEST_CWD="${CMAKE_SOURCE_DIR}/bin/${FENNEC_BUILD_NAME}"

View File

@@ -0,0 +1,22 @@
// =====================================================================================================================
// fennec, a free and open source game engine
// Copyright © 2025 Medusa Slockbower
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// =====================================================================================================================
#ifndef FENNEC_TEST_CONTAINERS_PERFORMANCE_ITERATOR_VISITOR_H
#define FENNEC_TEST_CONTAINERS_PERFORMANCE_ITERATOR_VISITOR_H
#endif // FENNEC_TEST_CONTAINERS_PERFORMANCE_ITERATOR_VISITOR_H