Updates to libraries

This commit is contained in:
Maddie Slockbower 2024-08-17 00:19:28 -04:00
parent 593760dd92
commit 8a514da303
16 changed files with 1182 additions and 674 deletions

View File

@ -10,6 +10,12 @@ set(OPENCPPUTILS_HEADERS
startup.h startup.h
template_utils.h template_utils.h
unique_id.h unique_id.h
dynarray.h
redec.h
hash_table.h
math.h
set.h
map.h
) )
add_library(open-cpp-utils INTERFACE add_library(open-cpp-utils INTERFACE

View File

@ -65,10 +65,17 @@ public:
public: public:
// Assignment operators ------------------------------------------------------------------------------------------------ // Assignment operators ------------------------------------------------------------------------------------------------
any& operator=(const any&) = default; any& operator=(const any&) = default;
any& operator=(any&&) = default; any& operator=(any&&) = default;
// Access --------------------------------------------------------------------------------------------------------------
template<typename V>
V& get() { static_assert(std::disjunction<std::is_same<V, T>, std::is_same<V, Rest>...>{}); return static_cast<V&>(*this); }
// Cast operators ------------------------------------------------------------------------------------------------------ // Cast operators ------------------------------------------------------------------------------------------------------
operator reference() { return Value; } operator reference() { return Value; }

View File

@ -42,7 +42,7 @@ public:
class unordered; class unordered;
private: private:
struct director; struct Node_;
// Typedefs ============================================================================================================ // Typedefs ============================================================================================================
@ -53,7 +53,7 @@ public:
using node_queue = std::deque<node>; using node_queue = std::deque<node>;
private: private:
using hierarchy = std::vector<director>; using hierarchy = std::vector<Node_>;
using storage = std::vector<data_type>; using storage = std::vector<data_type>;
@ -66,7 +66,7 @@ public:
// Data Structures ===================================================================================================== // Data Structures =====================================================================================================
private: private:
struct director struct Node_
{ {
enum flags enum flags
{ {
@ -76,7 +76,7 @@ private:
node parent, child, prev_sibling, next_sibling; node parent, child, prev_sibling, next_sibling;
uint32_t flags, depth; uint32_t flags, depth;
director() : parent(0), child(0), prev_sibling(0), next_sibling(0), flags(VALID), depth(0) { } Node_() : parent(0), child(0), prev_sibling(0), next_sibling(0), flags(VALID), depth(0) { }
}; };
@ -89,7 +89,7 @@ public:
/** /**
* \brief Default constructor, creates tree with empty root * \brief Default constructor, creates tree with empty root
*/ */
directed_tree() : graph_{ director() }, data_{ data_type() }, freed_{ } { } directed_tree() : graph_{ Node_() }, data_{ data_type() }, freed_{ } { }
// Tree Navigation ----------------------------------------------------------------------------------------------------- // Tree Navigation -----------------------------------------------------------------------------------------------------
@ -99,7 +99,7 @@ public:
* \param id Node id to reference * \param id Node id to reference
* \return Whether the valid flag is true in the node * \return Whether the valid flag is true in the node
*/ */
[[nodiscard]] bool valid(node id) const { return graph_[id].flags & director::VALID; } [[nodiscard]] bool valid(node id) const { return graph_[id].flags & Node_::VALID; }
/** /**
* \brief Get the parent of a node. O(1) * \brief Get the parent of a node. O(1)
@ -115,6 +115,20 @@ public:
*/ */
[[nodiscard]] node first_child(node id) const { return graph_[id].child; } [[nodiscard]] node first_child(node id) const { return graph_[id].child; }
/**
* \brief Get the first child of a node. O(1)
* \param id Node id to reference
* \return Node id of the first child
*/
[[nodiscard]] node last_child(node id) const
{
node c = first_child(id);
while(c != 0) { if(graph_[c].next_sibling == 0) break; c = graph_[c].next_sibling; }
return c;
}
/** /**
* \brief Get the previous sibling of a node. O(1) * \brief Get the previous sibling of a node. O(1)
* \param id Node id to reference * \param id Node id to reference
@ -164,47 +178,63 @@ public:
* \brief Insert a node into the tree as a child of the provided node * \brief Insert a node into the tree as a child of the provided node
* \param data Value to insert * \param data Value to insert
* \param p_id Id of the parent node * \param p_id Id of the parent node
* \param last Whether to insert at the back of the array
* \return Id of the inserted node * \return Id of the inserted node
*/ */
node insert(const data_type& data, node p_id) node insert(const data_type& data, node p_id, bool last = true)
{ {
// If there are no freed nodes, create a new node and mark it as freed // If there are no freed nodes, create a new node and mark it as freed
if(freed_.empty()) if(freed_.empty())
{ {
freed_.push_back(static_cast<node>(graph_.size())); freed_.push_back(static_cast<node>(graph_.size()));
graph_.push_back(director()); data_.push_back(data); graph_.push_back(Node_()); data_.push_back(data);
} }
// Pop a freed node from the stack // Pop a freed node from the stack
node id = freed_.front(); freed_.pop_front(); node id = freed_.front(); freed_.pop_front();
director& node = graph_[id]; Node_& node = graph_[id];
director& parent = graph_[p_id]; Node_& parent = graph_[p_id];
// If the parent has a child, update the child's references // If the parent has a child, update the child's references
if(parent.child) if(parent.child && !last)
{ {
// Update the next child // Update the next child
director& nchild = graph_[parent.child]; Node_& nchild = graph_[parent.child];
node.prev_sibling = nchild.prev_sibling; node.prev_sibling = nchild.prev_sibling;
nchild.prev_sibling = id; nchild.prev_sibling = id;
// If present, update the previous child // If present, update the previous child
if(nchild.prev_sibling) if(nchild.prev_sibling)
{ {
director& pchild = graph_[nchild.prev_sibling]; Node_& pchild = graph_[nchild.prev_sibling];
pchild.next_sibling = id; pchild.next_sibling = id;
} }
} }
// Setup node // Setup node
node.parent = p_id; node.parent = p_id;
node.next_sibling = parent.child; node.next_sibling = last ? 0 : parent.child;
node.child = 0; node.child = 0;
node.flags = director::VALID; node.flags = Node_::VALID;
node.depth = parent.depth + 1; node.depth = parent.depth + 1;
// Set parent's child // Set parent's child
parent.child = id; if(last)
{
directed_tree::node idx = last_child(p_id);
if(idx)
{
Node_& lchild = graph_[idx];
lchild.next_sibling = id;
node.prev_sibling = idx;
}
else
{
parent.child = id;
}
}
else parent.child = id;
// Set the data // Set the data
data_[id] = data; data_[id] = data;
@ -221,8 +251,8 @@ public:
if(id == 0) return; if(id == 0) return;
// Mark node as invalid and push it to the freed list // Mark node as invalid and push it to the freed list
director& erased = graph_[id]; Node_& erased = graph_[id];
erased.Flags &= ~director::VALID; erased.Flags &= ~Node_::VALID;
freed_.push_back(id); freed_.push_back(id);
// Update the parent's child // Update the parent's child
@ -237,8 +267,8 @@ public:
while(stack.empty() == false) while(stack.empty() == false)
{ {
node next = stack.front(); stack.pop_front(); node next = stack.front(); stack.pop_front();
director& child = graph_[next]; Node_& child = graph_[next];
child.Flags &= ~director::VALID; child.Flags &= ~Node_::VALID;
freed_.push_back(next); freed_.push_back(next);
if(child.Sibling) stack.push_front(child.Sibling); if(child.Sibling) stack.push_front(child.Sibling);
@ -326,7 +356,7 @@ public:
node operator()(node id) node operator()(node id)
{ {
id = visit_queue_.back(); visit_queue_.pop_back(); id = visit_queue_.back(); visit_queue_.pop_back();
director& current = graph_.graph_[id]; Node_& current = graph_.graph_[id];
if(current.next_sibling) visit_queue_.push_back(current.next_sibling); if(current.next_sibling) visit_queue_.push_back(current.next_sibling);
if(current.child) visit_queue_.push_front(current.child); if(current.child) visit_queue_.push_front(current.child);
@ -351,7 +381,7 @@ public:
node operator()(node id) node operator()(node id)
{ {
director& current = graph_.graph_[id]; Node_& current = graph_.graph_[id];
if(current.next_sibling) visit_queue_.push_front(current.next_sibling); if(current.next_sibling) visit_queue_.push_front(current.next_sibling);
if(current.child) visit_queue_.push_front(current.child); if(current.child) visit_queue_.push_front(current.child);
@ -375,12 +405,12 @@ public:
public: public:
in_order(directed_tree& graph) : graph_(graph) { } in_order(directed_tree& graph) : graph_(graph) { }
node operator()(node node) node operator()(node id)
{ {
if(node == 0) visit_queue_.push_back(graph_.left_most(node)); if(id == 0) visit_queue_.push_back(graph_.left_most(id));
node = visit_queue_.front(); visit_queue_.pop_front(); id = visit_queue_.front(); visit_queue_.pop_front();
director& current = graph_.graph_[node]; Node_& current = graph_.graph_[id];
if(current.Sibling) if(current.Sibling)
{ {
@ -388,7 +418,7 @@ public:
visit_queue_.push_back(graph_.left_most(current.Sibling)); visit_queue_.push_back(graph_.left_most(current.Sibling));
} }
return node; return id;
} }
private: private:
@ -405,17 +435,17 @@ public:
public: public:
post_order(directed_tree& graph) : graph_(graph) { } post_order(directed_tree& graph) : graph_(graph) { }
node operator()(node node) node operator()(node id)
{ {
if(visit_queue_.empty()) visit_queue_.push_back(graph_.left_most(node)); if(visit_queue_.empty()) visit_queue_.push_back(graph_.left_most(id));
node = visit_queue_.front(); visit_queue_.pop_front(); id = visit_queue_.front(); visit_queue_.pop_front();
if(node == 0) return node; if(id == 0) return id;
director& current = graph_.graph_[node]; Node_& current = graph_.graph_[id];
visit_queue_.push_back(current.Sibling ? graph_.left_most(current.Sibling) : graph_.parent(node)); visit_queue_.push_back(current.Sibling ? graph_.left_most(current.Sibling) : graph_.parent(id));
return node; return id;
} }
private: private:
@ -438,10 +468,10 @@ public:
void operator()() void operator()()
{ {
node node = 0; node id = 0;
while(node = order_(node)) while(id = order_(id))
{ {
if(visitor_(graph_[node], node)) break; if(visitor_(graph_[id], id)) break;
} }
} }

30
External/open-cpp-utils/dynarray.h vendored Normal file
View File

@ -0,0 +1,30 @@
// =====================================================================================================================
// Copyright 2024 Medusa Slockbower
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// =====================================================================================================================
#ifndef DYNARRAY_H
#define DYNARRAY_H
#include <memory>
#include <vector>
namespace open_cpp_utils
{
template<typename T, class Alloc = std::allocator<T>>
using dynarray = std::vector<T, Alloc>;
}
#endif //DYNARRAY_H

300
External/open-cpp-utils/hash_table.h vendored Normal file
View File

@ -0,0 +1,300 @@
// =====================================================================================================================
// Copyright 2024 Medusa Slockbower
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// =====================================================================================================================
#ifndef HASH_TABLE_H
#define HASH_TABLE_H
#include <memory>
#include <type_traits>
#include "math.h"
#include "optional.h"
namespace open_cpp_utils
{
template<typename T, class Hash = std::hash<T>, class Alloc = std::allocator<T>>
class hash_table
{
// Typedefs ============================================================================================================
public:
friend class iterator;
private:
struct _Node;
public:
using value_type = T;
using pointer = T*;
using const_pointer = const T*;
using reference = T&;
using const_reference = const T&;
using hash_type = Hash;
using allocator_type = typename std::allocator_traits<Alloc>::template rebind_alloc<_Node>;
using size_type = size_t;
using iterator_type = iterator;
private:
using table_type = _Node*;
using node = int64_t;
static constexpr node nullnode = std::integral_constant<node, -1>{};
// Structs =============================================================================================================
private:
struct _Node
{
optional<T> value;
int psl;
_Node() : value(), psl(0) { }
};
// Functions ===========================================================================================================
public:
// Constructors & Destructor -------------------------------------------------------------------------------------------
hash_table() : table_(nullptr), capacity_(0), size_(0), load_factor_(0.8), hash_(), alloc_() { }
hash_table(const hash_table&);
hash_table(hash_table&&) = default; // Default Move Constructor should suffice
~hash_table() { clear(); }
// Modifiers -----------------------------------------------------------------------------------------------------------
void clear();
void insert(const_reference x);
void erase(const_reference x);
bool contains(const_reference x);
iterator find(const_reference x) const { node res = _find(x); return iterator(this, res == nullnode ? capacity_ : res); }
// Modifiers -----------------------------------------------------------------------------------------------------------
[[nodiscard]] size_type capacity() const { return capacity_; }
[[nodiscard]] size_type size() const { return size_; }
[[nodiscard]] bool empty() const { return size_ == 0; }
[[nodiscard]] double occupancy() const { return size_ / static_cast<double>(capacity_); }
// Helpers -------------------------------------------------------------------------------------------------------------
private:
void _increase_capacity();
[[nodiscard]] node _hash(const_reference v) const;
[[nodiscard]] node _find(const_reference x) const;
[[nodiscard]] node _next(node n) const { return (n + 1) % capacity_; }
[[nodiscard]] node _prev(node n) const { return (n - 1 + capacity_) % capacity_; }
static size_type _next_prime(size_type x);
static size_type _prev_prime(size_type x);
// Iterators ===========================================================================================================
class iterator
{
public:
iterator(hash_table* table, int idx) : table_(table), idx_(idx) { _next_index(); }
iterator(const iterator&) = default;
iterator(iterator&&) = default;
~iterator() = default;
iterator& operator++() { ++idx_; _next_index(); return *this; }
iterator operator++(int) { iterator ret = *this; ++idx_; _next_index(); return ret; }
bool operator==(const iterator& o) const = default;
bool operator!=(const iterator& o) const = default;
reference operator*() const { return table_->table_[idx_].value; }
pointer operator->() const { return &table_->table_[idx_].value; }
private:
void _next_index() { while(idx_ < table_->capacity_ && table_[idx_].value()) ++idx_; }
hash_table* table_;
int idx_;
};
iterator begin() { return iterator(this, 0); }
iterator end() { return iterator(this, capacity_); }
// Variables ===========================================================================================================
private:
table_type table_;
size_type capacity_, size_;
double load_factor_;
hash_type hash_;
allocator_type alloc_;
};
template<typename T, class Hash, class Alloc>
hash_table<T, Hash, Alloc>::hash_table(const hash_table& other)
: table_(nullptr)
, capacity_(other.capacity_)
, size_(other.size_)
, load_factor_(0.8)
{
}
template<typename T, class Hash, class Alloc>
void hash_table<T, Hash, Alloc>::clear()
{
alloc_.deallocate(table_, capacity_);
capacity_ = size_ = 0;
}
template<typename T, class Hash, class Alloc>
void hash_table<T, Hash, Alloc>::insert(const_reference x)
{
if(occupancy() > load_factor_ || capacity_ == 0) _increase_capacity();
node idx = _hash(x);
int psl = 0;
T value = x;
while(table_[idx].value())
{
_Node& node = table_[idx];
if(node.value == x) return;
if(psl > node.psl) { std::swap(psl, node.psl); std::swap(value, node.value); }
idx = _next(idx);
++psl;
}
table_[idx].value = value;
table_[idx].psl = psl;
++size_;
}
template<typename T, class Hash, class Alloc>
void hash_table<T, Hash, Alloc>::erase(const_reference x)
{
node idx = _find(x);
if(idx == nullnode) return;
table_[idx].value.reset();
--size_;
node prev = idx; idx = _next(idx);
while(table_[idx].value() && table_[idx].psl > 0)
{
_Node &a = table_[prev], &b = table_[idx];
std::swap(a, b);
--a.psl; prev = idx; idx = _next(idx);
}
}
template<typename T, class Hash, class Alloc>
bool hash_table<T, Hash, Alloc>::contains(const_reference x)
{
return _find(x) != nullnode;
}
template<typename T, class Hash, class Alloc>
void hash_table<T, Hash, Alloc>::_increase_capacity()
{
table_type old = table_;
size_type old_capacity = capacity_;
capacity_ = _next_prime(capacity_);
table_ = alloc_.allocate(capacity_);
memset(table_, 0, capacity_ * sizeof(_Node));
size_ = 0;
for(node i = 0; i < old_capacity; ++i)
{
if(old[i].value()) insert(old[i].value);
}
alloc_.deallocate(old, old_capacity);
}
template<typename T, class Hash, class Alloc>
typename hash_table<T, Hash, Alloc>::node hash_table<T, Hash, Alloc>::_hash(const_reference v) const
{
node x = hash_(v);
x ^= x >> 33U;
x *= UINT64_C(0xff51afd7ed558ccd);
x ^= x >> 33U;
x *= UINT64_C(0xc4ceb9fe1a85ec53);
x ^= x >> 33U;
return x;
}
template<typename T, class Hash, class Alloc>
typename hash_table<T, Hash, Alloc>::node hash_table<T, Hash, Alloc>::_find(const_reference x) const
{
if(capacity_ == 0) return nullnode;
node idx = _hash(x);
int psl = 0;
while(table_[idx].value())
{
_Node& node = table_[idx];
if(node.psl > psl) return nullnode;
if(node.value == x) return idx;
idx = _next(idx); ++psl;
}
return nullnode;
}
template<typename T, class Hash, class Alloc>
typename hash_table<T, Hash, Alloc>::size_type hash_table<T, Hash, Alloc>::_next_prime(size_type x)
{
size_type n = (x + 1) / 6;
n *= 2;
while(true)
{
x = (n * 6) - 1;
if(!is_prime(x)) x = (n * 6) + 1;
if(!is_prime(x)) { ++n; continue; }
return std::max(x, 7ull);
}
}
template<typename T, class Hash, class Alloc>
typename hash_table<T, Hash, Alloc>::size_type hash_table<T, Hash, Alloc>::_prev_prime(size_type x)
{
size_type n = (x + 1) / 6;
n *= 2;
while(true)
{
x = (n * 6) - 1;
if(!is_prime(x)) x = (n * 6) + 1;
if(!is_prime(x)) { --n; continue; }
return std::max(x, 7ull);
}
}
}
#endif //HASH_TABLE_H

100
External/open-cpp-utils/map.h vendored Normal file
View File

@ -0,0 +1,100 @@
// =====================================================================================================================
// Copyright 2024 Medusa Slockbower
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// =====================================================================================================================
#ifndef MAP_H
#define MAP_H
#include "set.h"
namespace open_cpp_utils
{
template<typename Key, typename Value, class Alloc = std::allocator<std::pair<Key, Value>>>
class map
{
// Typedefs ============================================================================================================
public:
struct hash { size_t operator()(const pair_type& pair) const { return std::hash<key_type>{}(pair.first); } };
using key_type = Key;
using value_type = Value;
using pair_type = std::pair<key_type, value_type>;
using table_type = set<pair_type, hash, Alloc>;
using key_pointer = key_type*;
using const_key_pointer = const key_type*;
using key_reference = key_type&;
using const_key_reference = const key_type&;
using value_pointer = value_type*;
using const_value_pointer = const value_type*;
using value_reference = value_type&;
using const_value_reference = const value_type&;
using iterator = typename table_type::iterator;
// Functions ===========================================================================================================
// Constructors & Destructor -------------------------------------------------------------------------------------------
public:
map() = default;
map(const map&) = default;
map(map&&) = default;
~map() = default;
void insert(const_key_reference key, const_value_reference value);
void erase(const_key_reference key);
value_reference& operator[](const_key_reference key);
iterator find() { return table_->find(); }
bool contains(const_key_reference key) { return table_.contains({ key, value_type() }); }
iterator begin() { return table_.begin(); }
iterator end() { return table_.end(); }
// Variables ===========================================================================================================
private:
set<pair_type, hash, Alloc> table_;
};
template<typename Key, typename Value, class Alloc>
void map<Key, Value, Alloc>::insert(const_key_reference key, const_value_reference value)
{
iterator it = find({ key, value });
if(it != end()) table_.insert({ key, value });
}
template<typename Key, typename Value, class Alloc>
void map<Key, Value, Alloc>::erase(const_key_reference key)
{
table_.erase({ key, value_type() });
}
template<typename Key, typename Value, class Alloc>
typename map<Key, Value, Alloc>::value_reference map<Key, Value, Alloc>::operator[](const_key_reference key)
{
iterator it = table_.find({ key, value_type() });
if(it == table_.end())
{
table_.insert({ key, value_type() });
it = table_.find({ key, value_type() });
}
return it->second;
}
}
#endif //MAP_H

42
External/open-cpp-utils/math.h vendored Normal file
View File

@ -0,0 +1,42 @@
// =====================================================================================================================
// Copyright 2024 Medusa Slockbower
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// =====================================================================================================================
#ifndef MATH_H
#define MATH_H
#include <cmath>
namespace open_cpp_utils
{
template<typename T>
bool is_prime(T x)
{
if(x <= 1) return false;
if(x == 2 || x == 3) return true;
if(x % 2 == 0 || x % 3 == 0) return false;
T limit = sqrt(x);
for(T i = 5; i <= limit; i += 6)
{
if(x % i == 0 || x % (i + 2) == 0) return false;
}
return true;
}
}
#endif //MATH_H

View File

@ -16,18 +16,18 @@
#ifndef OBJECT_POOL_H #ifndef OBJECT_POOL_H
#define OBJECT_POOL_H #define OBJECT_POOL_H
#include <stack> #include "dynarray.h"
#include <vector> #include "optional.h"
#include <tuple>
#include <unordered_map>
#include "template_utils.h"
namespace open_cpp_utils namespace open_cpp_utils
{ {
template<typename T, typename _Hash = std::unordered_map<uint64_t, int64_t>> /**
class object_pool * \brief
* \tparam T
*/
template<typename T>
class object_list
{ {
// Typedefs ============================================================================================================ // Typedefs ============================================================================================================
@ -38,17 +38,10 @@ public:
using reference = T&; using reference = T&;
using const_reference = const T&; using const_reference = const T&;
using index_type = int64_t;
using uuid_type = uint64_t; using uuid_type = uint64_t;
private: private:
using node = std::tuple<value_type, bool>; using node = optional<value_type>;
// Constants ===========================================================================================================
public:
static constexpr std::integral_constant<index_type, -1> nullidx{};
// Functions =========================================================================================================== // Functions ===========================================================================================================
@ -57,33 +50,62 @@ public:
// Constructors & Destructor ------------------------------------------------------------------------------------------- // Constructors & Destructor -------------------------------------------------------------------------------------------
object_pool(); object_list() = default;
object_list(const object_list& other) = default;
object_list(object_list&& other) = default;
~object_list() = default;
void clear();
void reset(); // Modifiers -----------------------------------------------------------------------------------------------------------
void cleanup();
size_t size() { return data_.size(); }
// Modifiers -----------------------------------------------------------------------------------------------------------
void clear() { data_.clear(); freed_.clear(); }
uuid_type insert(const_reference& value); uuid_type insert(const_reference& value);
void erase(uuid_type id); void erase(uuid_type id);
void erase(index_type idx);
object_list& operator=(const object_list&) = default;
object_list& operator=(object_list&&) = default;
// Accessors ----------------------------------------------------------------------------------------------------------- // Accessors -----------------------------------------------------------------------------------------------------------
reference operator[](uuid_type id); reference operator[](uuid_type id) { assert(data_[id]()); return data_[id]; }
const_reference operator[](uuid_type id) const; const_reference operator[](uuid_type id) const { assert(data_[id]()); return data_[id]; }
bool operator()(uuid_type id) const; bool operator()(uuid_type id) const { return data_[id](); }
reference operator[](index_type idx); typename dynarray<node>::iterator begin() { return data_.begin(); }
const_reference operator[](index_type idx) const; typename dynarray<node>::iterator end() { return data_.end(); }
bool operator()(index_type idx) const;
// Variables ===========================================================================================================
private: private:
std::vector<node> data_; dynarray<node> data_;
_Hash map_; dynarray<uuid_type> freed_;
std::stack<index_type> freed_;
}; };
template<typename T>
typename object_list<T>::uuid_type object_list<T>::insert(const_reference value)
{
if(freed_.empty()) { data_.push_back(value); return data_.size() - 1; }
uuid_type id = freed_.back(); freed_.pop_back();
data_[id] = value;
return id;
}
template<typename T>
void object_list<T>::erase(uuid_type id)
{
data_[id].reset();
freed_.push_back(id);
}
} // open_cpp_utils } // open_cpp_utils
#endif //OBJECT_POOL_H #endif //OBJECT_POOL_H

View File

@ -26,7 +26,7 @@ namespace open_cpp_utils
public: public:
using value_type = T; using value_type = T;
optional() : data_(), valid_(false) { } optional() : valid_(false) { }
optional(const value_type& data) : data_(data), valid_(true) { } optional(const value_type& data) : data_(data), valid_(true) { }
optional(value_type&& data) : data_(data), valid_(true) { } optional(value_type&& data) : data_(data), valid_(true) { }
optional(const optional& other) = default; optional(const optional& other) = default;

21
External/open-cpp-utils/redec.h vendored Normal file
View File

@ -0,0 +1,21 @@
// =====================================================================================================================
// Copyright 2024 Medusa Slockbower
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// =====================================================================================================================
#ifndef REDEC_H
#define REDEC_H
#include "dynarray.h"
#endif //REDEC_H

28
External/open-cpp-utils/set.h vendored Normal file
View File

@ -0,0 +1,28 @@
// =====================================================================================================================
// Copyright 2024 Medusa Slockbower
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// =====================================================================================================================
#ifndef SET_H
#define SET_H
#include "hash_table.h"
namespace open_cpp_utils
{
template<typename T, class Hash = std::hash<T>, class Alloc = std::allocator<T>> using set = hash_table<T, Hash, Alloc>;
}
#endif //SET_H

View File

@ -24,33 +24,111 @@ namespace ocu = open_cpp_utils;
namespace OpenShaderDesigner::Nodes::Math namespace OpenShaderDesigner::Nodes::Math
{ {
inline static constexpr ImColor HeaderColor = ImColor(0x92, 0x16, 0x16); // Header Colors =======================================================================================================
struct Constant : public Node inline static constexpr ImColor HeaderColor = ImColor(0xA7, 0x62, 0x53);
{ inline static constexpr ImColor HeaderHoveredColor = ImColor(0xC5, 0x79, 0x67);
using ValueType = ocu::any<int, unsigned int, float, glm::vec4>; inline static constexpr ImColor HeaderActiveColor = ImColor(0x82, 0x4C, 0x40);
Constant(ShaderGraph& graph, ImVec2 pos); inline static const std::string HeaderMarker = "\uF3B9 ";
virtual ~Constant() = default;
[[nodiscard]] Node* Copy(ShaderGraph& graph) const override;
void Inspect() override;
ValueType Value; // =====================================================================================================================
}; // Integral
// =====================================================================================================================
RegisterNode("Math/Constant", Constant); struct Integral : public Node
{
using ValueType = ocu::any<int, unsigned int, float, glm::vec4>;
struct Add : public Node Integral(ShaderGraph& graph, ImVec2 pos);
{ virtual ~Integral() = default;
Add(ShaderGraph& graph, ImVec2 pos);
virtual ~Add() = default;
[[nodiscard]] Node* Copy(ShaderGraph& graph) const override; [[nodiscard]] Node* Copy(ShaderGraph& graph) const override;
void Inspect() override; void Inspect() override;
};
ValueType Value;
};
RegisterNode("Math/Constants/Integral", Integral);
// =====================================================================================================================
// Unsigned Integral
// =====================================================================================================================
struct UnsignedIntegral : public Node
{
using ValueType = ocu::any<int, unsigned int, float, glm::vec4>;
UnsignedIntegral(ShaderGraph& graph, ImVec2 pos);
virtual ~UnsignedIntegral() = default;
[[nodiscard]] Node* Copy(ShaderGraph& graph) const override;
void Inspect() override;
ValueType Value;
};
RegisterNode("Math/Constants/Unsigned Integral", UnsignedIntegral);
// =====================================================================================================================
// Scalar
// =====================================================================================================================
struct Scalar : public Node
{
using ValueType = ocu::any<int, unsigned int, float, glm::vec4>;
Scalar(ShaderGraph& graph, ImVec2 pos);
virtual ~Scalar() = default;
[[nodiscard]] Node* Copy(ShaderGraph& graph) const override;
void Inspect() override;
ValueType Value;
};
RegisterNode("Math/Constants/Scalar", Scalar);
// =====================================================================================================================
// Vector
// =====================================================================================================================
struct Vector : public Node
{
using ValueType = ocu::any<int, unsigned int, float, glm::vec4>;
Vector(ShaderGraph& graph, ImVec2 pos);
virtual ~Vector() = default;
[[nodiscard]] Node* Copy(ShaderGraph& graph) const override;
void Inspect() override;
ValueType Value;
};
RegisterNode("Math/Constants/Vector", Vector);
// =====================================================================================================================
// Add
// =====================================================================================================================
struct Add : public Node
{
Add(ShaderGraph& graph, ImVec2 pos);
virtual ~Add() = default;
[[nodiscard]] Node* Copy(ShaderGraph& graph) const override;
void Inspect() override;
};
RegisterNode("Math/Operators/Add", Add);
RegisterNode("Math/Add", Add);
} }
#endif //MATH_H #endif //MATH_H

View File

@ -29,6 +29,11 @@
#include <open-cpp-utils/directed_tree.h> #include <open-cpp-utils/directed_tree.h>
#include <open-cpp-utils/optional.h> #include <open-cpp-utils/optional.h>
#include <imnode-graph/imnode_graph.h>
#include "open-cpp-utils/any.h"
#include "open-cpp-utils/object_pool.h"
namespace ocu = open_cpp_utils; namespace ocu = open_cpp_utils;
#define RegisterNode(Name, Type) \ #define RegisterNode(Name, Type) \
@ -38,68 +43,58 @@ namespace ocu = open_cpp_utils;
namespace OpenShaderDesigner namespace OpenShaderDesigner
{ {
class ShaderGraph; class ShaderGraph;
using PinId = uint16_t;
using NodeId = uint32_t;
struct PinPtr using PinType = int;
{ enum PinType_
struct Hash {
{ PinType_Int = 0
size_t operator()(const PinPtr& p) const , PinType_UInt
{ , PinType_Float
return p.hash(); , PinType_Vector
}
};
NodeId Node; , PinType_Any
PinId Pin; , PinType_COUNT
bool Input; };
size_t hash() const { return (Input ? 0 : 0x8000000) | static_cast<size_t>(Node) << 32 | static_cast<size_t>(Pin & 0x7FFFFFFF); } using PinFlags = unsigned int;
enum PinFlags_
bool operator<(const PinPtr& o) const { return hash() < o.hash(); } {
bool operator==(const PinPtr& o) const { return hash() == o.hash(); } PinFlags_None = 0
}; , PinFlags_NoCollapse = 1 << 0
, PinFlags_NoPadding = 1 << 1
};
struct Pin struct Pin
{ {
enum PinType inline const static ImColor Colors[PinType_COUNT] = {
{ ImColor(0x64, 0x94, 0xAA) // Int
INT = 0 , ImColor(0x7A, 0x9F, 0x82) // Unsigned Int
, UINT , ImColor(0xA6, 0x3D, 0x40) // Float
, FLOAT , ImColor(0xE9, 0xB8, 0x72) // Vector
, VECTOR , ImColor(0xD2, 0xD5, 0xD3) // Any
, ANY
, COUNT
}; };
enum PinDirection inline const static std::string TypeNames[PinType_COUNT] = {
{
INPUT
, OUTPUT
};
inline const static ImColor Colors[COUNT] = {
ImColor(0xB9, 0xF5, 0x94)
, ImColor(0x8C, 0xC0, 0x8C)
, ImColor(0x37, 0x95, 0x85)
, ImColor(0xE3, 0x7D, 0xDC)
// , ImColor(0xD2, 0x6E, 0x46)
, ImColor(0xD2, 0xD5, 0xD3)
};
inline const static std::string TypeNames[COUNT] = {
"Int" "Int"
, "Unsigned Int" , "Unsigned Int"
, "Float" , "Float"
, "Vector" , "Vector"
, "Any"
}; };
std::string Name; using Ambiguous = ocu::any<int, unsigned int, float, ImVec4>;
PinType Type;
PinDirection Direction; std::string Name;
}; PinType Type;
PinFlags Flags;
Ambiguous Value;
Pin(const std::string& name, PinType type, PinFlags flags = PinFlags_None)
: Name(name)
, Type(type)
, Flags(flags)
{ }
};
struct Node struct Node
{ {
@ -108,31 +103,28 @@ namespace OpenShaderDesigner
struct struct
{ {
std::string Title = "Node"; std::string Title;
ImColor Color = Pin::Colors[Pin::VECTOR]; ImColor Color, HoveredColor, ActiveColor;
bool Enabled = true; bool Enabled;
} Header; } Header;
struct struct
{ {
std::vector<Pin> Inputs, Outputs; std::vector<Pin> Inputs, Outputs;
bool DynamicInputs = false; bool DynamicInputs;
} IO; } IO;
struct struct
{ {
ImVec2 Size;
bool Const; bool Const;
} Info; } Info;
Node( Node(ShaderGraph& graph, ImVec2 pos);
ShaderGraph& graph, ImVec2 pos
, const std::string& title, ImColor color
, const std::vector<Pin>& inputs, bool dyn_inputs
, const std::vector<Pin>& outputs
, bool constant = false);
~Node() = default; ~Node() = default;
void DrawPin(ImGuiID id, Pin& pin, ImPinDirection direction);
void Draw(ImGuiID id);
virtual Node* Copy(ShaderGraph& graph) const = 0; virtual Node* Copy(ShaderGraph& graph) const = 0;
virtual void Inspect() = 0; virtual void Inspect() = 0;
}; };
@ -143,15 +135,6 @@ namespace OpenShaderDesigner
private: private:
friend Node; friend Node;
using Connection = std::pair<const PinPtr, PinPtr>;
using ConnectionMap = std::unordered_multimap<PinPtr, PinPtr, PinPtr::Hash>;
struct Line
{
ImColor Color;
float Thickness;
};
using ConstructorPtr = Node*(*)(ShaderGraph&, ImVec2); using ConstructorPtr = Node*(*)(ShaderGraph&, ImVec2);
struct ContextMenuItem struct ContextMenuItem
{ {
@ -161,10 +144,8 @@ namespace OpenShaderDesigner
struct GraphState struct GraphState
{ {
ShaderGraph& Parent; ShaderGraph& Parent;
std::vector<Node*> Nodes; ocu::object_list<Node*> Nodes;
std::set<PinId> Erased;
ConnectionMap Connections;
GraphState(ShaderGraph& parent); GraphState(ShaderGraph& parent);
GraphState(const GraphState& other); GraphState(const GraphState& other);
@ -177,55 +158,22 @@ namespace OpenShaderDesigner
using ContextID = ContextMenuHierarchy::node; using ContextID = ContextMenuHierarchy::node;
inline static ContextMenuHierarchy ContextMenu; inline static ContextMenuHierarchy ContextMenu;
// Helper functions
float CalculateWidth(Node& node);
float CalculateHeight(Node& node);
// Base Draw and Input functions
void HandleInput();
void DrawGrid();
void DrawNode(Node& node, NodeId id);
void DrawPin(NodeId node_id, Pin& pin, PinId pin_id, ImVec2 location, bool input);
void DrawContextMenu();
// Connection functions
void DrawConnections();
void DrawConnection(const PinPtr& a, const PinPtr& b);
auto StartConnection(const PinPtr& ptr) -> void;
void StopConnection();
void CreateConnection(const PinPtr& a, const PinPtr& b);
void EraseConnection(const PinPtr& a, const PinPtr& b);
void EraseConnections(const PinPtr& a);
NodeId AddNode(Node* node);
void RemoveNode(NodeId id);
// Clipboard functionality
void ClearClipboard();
void Copy();
void Paste(const ImVec2& location);
void EraseSelection();
// History Functionality // History Functionality
void PushState(); void PushState();
void PopState(); void PopState();
// Helper functions
float BezierOffset(const ImVec2& out, const ImVec2& in);
bool AABB(const ImVec2& a0, const ImVec2& a1, const ImVec2& b0, const ImVec2& b1);
ImVec2 GridToScreen(const ImVec2& position);
ImVec2 ScreenToGrid(const ImVec2& position);
ImVec2 SnapToGrid(const ImVec2& position);
Pin& GetPin(const PinPtr& ptr);
public: public:
ShaderGraph(); ShaderGraph();
~ShaderGraph(); ~ShaderGraph();
void OnOpen() override; void OnOpen() override;
void DrawWindow() override; void DrawWindow() override;
void DrawContextMenu();
void Copy();
void Erase();
void Paste(ImVec2 pos);
static void Register(const std::filesystem::path& path, ConstructorPtr constructor); static void Register(const std::filesystem::path& path, ConstructorPtr constructor);
@ -233,86 +181,9 @@ namespace OpenShaderDesigner
bool GrabFocus; bool GrabFocus;
GraphState State; GraphState State;
std::stack<GraphState> History; std::stack<GraphState> History;
struct ImVec2 ContextMenuPosition;
{
struct
{
ImColor BackgroundColor;
struct
{
Line Thin, Thick;
float Padding;
} Lines;
} Grid;
struct
{
float Rounding;
Line Border, SelectedBorder;
ImColor Content;
ImColor Title;
struct
{
float Padding;
float BorderThickness;
ImColor Background;
ImColor Text;
Line Connections;
} Pins;
} Nodes;
struct
{
ImColor Background;
Line Border;
} Selection;
float FontSize;
} Style;
struct
{
struct
{
struct
{
float Rate, Smoothing;
} Scroll;
} Input;
} Settings;
struct
{
ImVec2 Location, ScreenLocation, Delta;
float Scroll;
bool ClickedSomething;
ocu::optional<NodeId> FocusedNode;
std::unordered_map<NodeId, ImVec2> Locks;
std::unordered_set<NodeId> DragSelect;
bool LocksDragged, NodeHovered;
ocu::optional<PinPtr> NewConnection;
std::unordered_set<NodeId> Selected;
} Mouse;
struct
{
ImVec2 Location;
float Zoom, Scroll;
} Camera;
struct
{
std::vector<Node*> Nodes;
ConnectionMap Connections;
} Clipboard;
bool Focused;
ImVec2 ContextMenuPosition;
friend class Inspector; friend class Inspector;
}; };

View File

@ -19,73 +19,131 @@
using namespace OpenShaderDesigner; using namespace OpenShaderDesigner;
using namespace OpenShaderDesigner::Nodes::Math; using namespace OpenShaderDesigner::Nodes::Math;
Constant::Constant(ShaderGraph& graph, ImVec2 pos)
: Node( // =====================================================================================================================
graph, pos // Integral
, "Constant", HeaderColor // =====================================================================================================================
, { }, false
, { { "Out", Pin::FLOAT, Pin::OUTPUT } } Integral::Integral(ShaderGraph& graph, ImVec2 pos)
) : Node(graph, pos)
{ {
Header.Title = HeaderMarker + "Integral";
Header.Color = HeaderColor;
Header.HoveredColor = HeaderHoveredColor;
Header.ActiveColor = HeaderActiveColor;
IO.Outputs.emplace_back("Out", PinType_Float, PinFlags_NoCollapse | PinFlags_NoPadding);
} }
Node* Constant::Copy(ShaderGraph& graph) const Node* Integral::Copy(ShaderGraph& graph) const
{ {
return new Constant(graph, Position); return new Integral(graph, Position);
} }
void Constant::Inspect() void Integral::Inspect()
{ {
Pin::PinType& Type = IO.Outputs[0].Type;
if(ImGui::BeginCombo("Type", Pin::TypeNames[Type].c_str()))
{
for(int i = 0; i < Pin::ANY; ++i)
{
Pin::PinType t = static_cast<Pin::PinType>(i);
if(ImGui::Selectable(Pin::TypeNames[t].c_str(), t == Type))
{
Type = t;
}
}
ImGui::EndCombo();
}
glm::vec4& v = Value;
switch(Type)
{
case Pin::INT:
ImGui::InputInt("Value", Value);
break;
case Pin::UINT:
ImGui::InputUInt("Value", Value);
break;
case Pin::FLOAT:
ImGui::InputFloat("Value", Value);
break;
case Pin::VECTOR:
ImGui::ColorEdit4("Value", &v.x);
break;
default:
break;
}
} }
// =====================================================================================================================
// Scalar
// =====================================================================================================================
UnsignedIntegral::UnsignedIntegral(ShaderGraph& graph, ImVec2 pos)
: Node(graph, pos)
{
Header.Title = HeaderMarker + "Unsigned Integral";
Header.Color = HeaderColor;
Header.HoveredColor = HeaderHoveredColor;
Header.ActiveColor = HeaderActiveColor;
IO.Outputs.emplace_back("Out", PinType_Float, PinFlags_NoCollapse | PinFlags_NoPadding);
}
Node* UnsignedIntegral::Copy(ShaderGraph& graph) const
{
return new UnsignedIntegral(graph, Position);
}
void UnsignedIntegral::Inspect()
{
}
// =====================================================================================================================
// Scalar
// =====================================================================================================================
Scalar::Scalar(ShaderGraph& graph, ImVec2 pos)
: Node(graph, pos)
{
Header.Title = HeaderMarker + "Scalar";
Header.Color = HeaderColor;
Header.HoveredColor = HeaderHoveredColor;
Header.ActiveColor = HeaderActiveColor;
IO.Outputs.emplace_back("Out", PinType_Float, PinFlags_NoCollapse | PinFlags_NoPadding);
}
Node* Scalar::Copy(ShaderGraph& graph) const
{
return new Scalar(graph, Position);
}
void Scalar::Inspect()
{
}
// =====================================================================================================================
// Vector
// =====================================================================================================================
Vector::Vector(ShaderGraph &graph, ImVec2 pos)
: Node(graph, pos)
{
Header.Title = HeaderMarker + "Vector";
Header.Color = HeaderColor;
Header.HoveredColor = HeaderHoveredColor;
Header.ActiveColor = HeaderActiveColor;
IO.Outputs.emplace_back("Out", PinType_Vector, PinFlags_NoCollapse | PinFlags_NoPadding);
IO.Outputs[0].Value.get<ImVec4>() = ImVec4(0, 0, 0, 1);
}
Node* Vector::Copy(ShaderGraph &graph) const
{
return new Vector(graph, Position);
}
void Vector::Inspect()
{
}
// =====================================================================================================================
// Add
// =====================================================================================================================
Add::Add(ShaderGraph& graph, ImVec2 pos) Add::Add(ShaderGraph& graph, ImVec2 pos)
: Node( : Node(graph, pos)
graph, pos {
, "Add", HeaderColor Header.Title = HeaderMarker + "Add";
, { { "A", Pin::ANY, Pin::INPUT }, { "B", Pin::ANY, Pin::INPUT } }, true Header.Color = HeaderColor;
, { { "Out", Pin::ANY, Pin::OUTPUT } } Header.HoveredColor = HeaderHoveredColor;
) Header.ActiveColor = HeaderActiveColor;
{ }
IO.Inputs.emplace_back("A", PinType_Any);
IO.Inputs.emplace_back("B", PinType_Any);
IO.DynamicInputs = true;
IO.Outputs.emplace_back("B", PinType_Any);
}
Node* Add::Copy(ShaderGraph& graph) const Node* Add::Copy(ShaderGraph& graph) const
{ {

View File

@ -17,15 +17,15 @@
#include <filesystem> #include <filesystem>
#include <stack> #include <stack>
#include <Core/Engine.h>
#include <Core/Console.h> #include <Core/Console.h>
#include <Editor/EditorSystem.h> #include <Editor/EditorSystem.h>
#include <glm/common.hpp>
#include <Graph/ShaderGraph.h> #include <Graph/ShaderGraph.h>
#include <imgui-docking/imgui_internal.h> #include <imgui-docking/imgui_internal.h>
#include <imnode-graph/imnode_graph.h> #include "imgui-extras/imgui_extras.h"
using namespace OpenShaderDesigner; using namespace OpenShaderDesigner;
@ -41,16 +41,12 @@ ShaderGraph::GraphState::GraphState(ShaderGraph& parent)
ShaderGraph::GraphState::GraphState(const GraphState& other) ShaderGraph::GraphState::GraphState(const GraphState& other)
: Parent(other.Parent) : Parent(other.Parent)
, Nodes(other.Nodes.size(), nullptr) , Nodes(other.Nodes)
, Connections(other.Connections)
, Erased(other.Erased)
{ {
NodeId id = 0; for(Node*& node : Nodes)
for(const Node* node : other.Nodes) {
{ node = node->Copy(Parent);
if(node) Nodes[id] = node->Copy(Parent); }
++id;
}
} }
ShaderGraph::GraphState::~GraphState() ShaderGraph::GraphState::~GraphState()
@ -63,153 +59,107 @@ ShaderGraph::GraphState::~GraphState()
ShaderGraph::GraphState& ShaderGraph::GraphState::operator=(const GraphState& other) ShaderGraph::GraphState& ShaderGraph::GraphState::operator=(const GraphState& other)
{ {
for(Node* node : Nodes) Nodes = other.Nodes;
{
if(node) delete node;
}
Nodes.clear(); for(Node*& node : Nodes) if(node) node = node->Copy(Parent);
Nodes.resize(other.Nodes.size(), nullptr);
NodeId id = 0; return *this;
for(const Node* node : other.Nodes)
{
if(node) Nodes[id] = node->Copy(Parent);
++id;
}
Connections = other.Connections;
Erased = other.Erased;
return *this;
}
float ShaderGraph::CalculateWidth(Node& node)
{
const float GridSize = Style.FontSize + Style.Grid.Lines.Padding;
const float HeaderHeight = Style.FontSize;
const float HeaderWidth = ImGui::CalcTextSize(node.Header.Title.c_str()).x;
float InputWidth = 0.0f, OutputWidth = 0.0f;
// Longest Input Pin
for(const Pin& pin : node.IO.Inputs)
{
InputWidth = glm::max(InputWidth, HeaderHeight + ImGui::CalcTextSize(pin.Name.c_str()).x);
}
// Longest Output Pin
for(const Pin& pin : node.IO.Outputs)
{
OutputWidth = glm::max(OutputWidth, HeaderHeight + ImGui::CalcTextSize(pin.Name.c_str()).x);
}
float Width = glm::max(InputWidth + OutputWidth, HeaderWidth) + HeaderHeight;
Width += GridSize - std::fmod(1.0f + Style.Grid.Lines.Padding + Width, GridSize);
return Width;
}
float ShaderGraph::CalculateHeight(Node& node)
{
const float HeaderHeight = Style.FontSize;
const float PinHeight = HeaderHeight * static_cast<float>(1 + glm::max(node.IO.Inputs.size(), node.IO.Outputs.size()));
return glm::max(HeaderHeight + PinHeight, 2 * HeaderHeight);
} }
Node::Node( Node::Node(
ShaderGraph& graph, ImVec2 pos ShaderGraph& graph, ImVec2 pos)
, const std::string& title, ImColor color
, const std::vector<Pin>& inputs, bool dyn_inputs
, const std::vector<Pin>& outputs, bool constant)
: Position(pos) : Position(pos)
, Header , Header
{ {
.Title = title .Title = "Node"
, .Color = color , .Color = ImColor(0xA7, 0x62, 0x53)
, .Enabled = true , .HoveredColor = ImColor(0xC5, 0x79, 0x67)
, .ActiveColor = ImColor(0x82, 0x4C, 0x40)
, .Enabled = true
} }
, IO , IO
{ {
.Inputs = inputs .DynamicInputs = false
, .Outputs = outputs
, .DynamicInputs = dyn_inputs
} }
, Info , Info
{ {
.Size = ImVec2(graph.CalculateWidth(*this), graph.CalculateHeight(*this)) .Const = false
, .Const = constant
} }
{ } { }
void Node::DrawPin(ImGuiID id, Pin& pin, ImPinDirection direction)
{
ImPinFlags flags = 0;
if(pin.Flags & PinFlags_NoPadding) flags |= ImPinFlags_NoPadding;
ImNodeGraph::BeginPin(std::format("{}##{}", pin.Name, id).c_str(), pin.Type, direction, flags);
bool connected = ImNodeGraph::IsPinConnected();
if((connected || pin.Type == PinType_Any) && !(pin.Flags & PinFlags_NoCollapse))
{
ImGui::Text(pin.Name.c_str());
}
else
{
switch (pin.Type)
{
case PinType_Int:
ImNodeGraph::PushItemWidth(200.0f);
ImGui::InputInt(std::format("##in{}{}", pin.Name, id).c_str(), pin.Value); break;
case PinType_UInt:
ImNodeGraph::PushItemWidth(200.0f);
ImGui::InputUInt(std::format("##in{}{}", pin.Name, id).c_str(), pin.Value); break;
case PinType_Float:
ImNodeGraph::PushItemWidth(100.0f);
ImGui::InputFloat(std::format("##in{}{}", pin.Name, id).c_str(), pin.Value); break;
case PinType_Vector:
ImGui::BeginGroup();
// Color Picker
ImNodeGraph::PushItemWidth(150.0f);
ImGui::ColorPicker4(
std::format("##in{}{}", pin.Name, id).c_str(), &pin.Value.get<ImVec4>().x
, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_Float
);
ImNodeGraph::PushItemWidth(150.0f);
ImGui::ColorPreview(
std::format("##invec{}{}", pin.Name, id).c_str(), &pin.Value.get<ImVec4>().x
, ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_Float
);
ImGui::EndGroup();
break;
}
}
ImNodeGraph::EndPin();
}
void Node::Draw(ImGuiID id)
{
ImNodeGraph::BeginNode(id, Position);
if(Header.Enabled)
{
ImNodeGraph::BeginNodeHeader(id, Header.Color, Header.HoveredColor, Header.ActiveColor);
ImGui::Text(Header.Title.c_str());
ImNodeGraph::EndNodeHeader();
}
for(Pin& pin : IO.Inputs) DrawPin(id, pin, ImPinDirection_Input);
for(Pin& pin : IO.Outputs) DrawPin(id, pin, ImPinDirection_Output);
ImNodeGraph::EndNode();
}
ShaderGraph::ShaderGraph() ShaderGraph::ShaderGraph()
: EditorWindow("\uED46 Shader Graph", 0) : EditorWindow("\uED46 Shader Graph", 0)
, GrabFocus(false) , GrabFocus(false)
, State(*this) , State(*this)
, Style
{
.Grid
{
.BackgroundColor = ImColor(0x11, 0x11, 0x11)
, .Lines
{
.Thin
{
.Color = ImColor(0x44, 0x44, 0x44)
, .Thickness = 1.0
}
, .Thick
{
.Color = ImColor(0x88, 0x88, 0x88)
, .Thickness = 2.0
}
, .Padding = 2.0f
}
}
, .Nodes
{
.Rounding = 5.0f
, .Border = { ImColor(0x33, 0x33, 0x33), 2.0f }
, .SelectedBorder = { ImColor(0xEF, 0xAE, 0x4B), 4.0f }
, .Content = ImColor(0x88, 0x88, 0x88)
, .Title = ImColor(0xCC, 0xCC, 0xCC)
, .Pins
{
.Padding = 2.0f
, .BorderThickness = 3.0f
, .Background = ImColor(0x22, 0x22, 0x22)
, .Text = ImColor(0x22, 0x22, 0x22)
, .Connections
{
.Color = ImColor(0x00, 0x00, 0x00)
, .Thickness = 2.0f
}
}
}
, .Selection
{
.Background = ImColor(0xC9, 0x8E, 0x36, 0x44)
, .Border
{
.Color = ImColor(0xEF, 0xAE, 0x4B, 0xBB)
, .Thickness = 2.0f
}
}
, .FontSize = 20.0f
}
, Settings
{
.Input
{
.Scroll
{
.Rate = 0.2f
, .Smoothing = 8.0f
}
}
}
, Mouse({ 0, 0 }, { 0, 0 })
, Camera({ 0, 0 }, 1)
, Focused(false)
{ {
} }
@ -219,9 +169,6 @@ ShaderGraph::~ShaderGraph()
void ShaderGraph::OnOpen() void ShaderGraph::OnOpen()
{ {
Mouse.Location = ImGui::GetMousePos();
Camera.Scroll = Camera.Zoom = 1.0f;
EditorSystem::Open<Inspector>()->Graph = this; EditorSystem::Open<Inspector>()->Graph = this;
GrabFocus = true; GrabFocus = true;
@ -230,7 +177,6 @@ void ShaderGraph::OnOpen()
void ShaderGraph::DrawWindow() void ShaderGraph::DrawWindow()
{ {
static int test_in, test_out;
ImNodeGraph::BeginGraph("ShaderGraph"); ImNodeGraph::BeginGraph("ShaderGraph");
ImNodeGraph::SetPinColors(Pin::Colors); ImNodeGraph::SetPinColors(Pin::Colors);
@ -241,56 +187,173 @@ void ShaderGraph::DrawWindow()
ImGui::SetNavWindow(ImGui::GetCurrentWindow()); ImGui::SetNavWindow(ImGui::GetCurrentWindow());
} }
// First Test Node for(ImGuiID id = 0; id < State.Nodes.size(); ++id)
{ {
ImVec2 pos = { 0, 0 }; if(State.Nodes(id) == false) continue;
ImNodeGraph::BeginNode(0, pos); State.Nodes[id]->Draw(id);
ImNodeGraph::BeginNodeHeader(-1, ImColor(0xA7, 0x62, 0x53), ImColor(0xC5, 0x79, 0x67), ImColor(0x82, 0x4C, 0x40));
ImGui::Text("\uf1f5 Hello");
ImNodeGraph::EndNodeHeader();
ImNodeGraph::BeginPin(1, Pin::INT, ImPinDirection_Input);
ImGui::PushItemWidth(100 * ImNodeGraph::GetCameraScale());
ImGui::InputInt("##In", &test_in, 0);
//ImGui::Text("In");
ImNodeGraph::EndPin();
ImNodeGraph::BeginPin(2, Pin::ANY, ImPinDirection_Output);
ImGui::Text("Out");
ImNodeGraph::EndPin();
ImNodeGraph::EndNode();
} }
// Second Test Node DrawContextMenu();
{
ImVec2 pos = { 300, 0 };
ImNodeGraph::BeginNode(3, pos);
ImNodeGraph::BeginNodeHeader(-1, ImColor(0xA7, 0x62, 0x53), ImColor(0xC5, 0x79, 0x67), ImColor(0x82, 0x4C, 0x40));
ImGui::Text("\uf1f5 Hello");
ImNodeGraph::EndNodeHeader();
ImNodeGraph::BeginPin(4, Pin::INT, ImPinDirection_Input);
ImGui::PushItemWidth(100 * ImNodeGraph::GetCameraScale());
ImGui::InputInt("##In", &test_in, 0);
//ImGui::Text("In");
ImNodeGraph::EndPin();
ImNodeGraph::BeginPin(5, Pin::ANY, ImPinDirection_Output);
ImGui::Text("Out");
ImNodeGraph::EndPin();
ImNodeGraph::EndNode();
}
ImNodeGraph::EndGraph(); ImNodeGraph::EndGraph();
} }
/*
void ShaderGraph::DrawContextMenu()
{
if(ImGui::IsMouseClicked(ImGuiMouseButton_Right))
{
ContextMenuPosition = ImNodeGraph::ScreenToGrid(ImGui::GetMousePos());
}
if(ImGui::BeginPopupContextWindow())
{
if(ImGui::MenuItem("Copy", "Ctrl+C", false, false)) Copy();
if(ImGui::MenuItem("Cut", "Ctrl+X", false, false))
{
Copy();
Erase();
}
if(ImGui::MenuItem("Paste", "Ctrl+V", false, false)) Paste(ContextMenuPosition);
ImGui::Separator();
ImGui::Text("Create");
ImGui::Separator();
// Create Nodes
ImVec2 position = ContextMenuPosition;
std::stack<ContextID> context; context.push(0);
struct Visitor
{
bool operator()(ContextMenuItem& item, ContextID id)
{
const auto depth = Graph.ContextMenu.depth(id);
if(depth > Context.size()) return false;
while(depth < Context.size())
{
Context.pop();
ImGui::EndMenu();
}
if(Context.top() != Graph.ContextMenu.parent(id)) return false;
std::string name = std::format("{}##{}", item.Name, id);
if(item.Constructor)
{
if(ImGui::MenuItem(item.Name.c_str()))
{
Graph.State.Nodes.insert(item.Constructor(Graph, Location));
}
}
else
{
if(ImGui::BeginMenu(item.Name.c_str()))
{
Context.push(id);
}
else
{
return false;
}
}
return false;
}
ShaderGraph& Graph;
const ImVec2 Location;
std::stack<ContextID>& Context;
} MenuVisitor
{
.Graph = *this
, .Location = position
, .Context = context
};
ContextMenu.traverse<ContextMenuHierarchy::pre_order>(MenuVisitor);
context.pop();
while(context.empty() == false)
{
ImGui::EndMenu();
context.pop();
}
ImGui::EndPopup();
}
}
void ShaderGraph::Copy() {}
void ShaderGraph::Erase() {}
void ShaderGraph::Paste(ImVec2) {}
void ShaderGraph::Register(const std::filesystem::path& path, ConstructorPtr constructor)
{
const std::string name = path.filename().string();
std::stack<std::string> decomp;
std::filesystem::path current = path.parent_path();
while(current.empty() == false)
{
decomp.push(current.filename().string());
current = current.parent_path();
}
ContextID node = 0;
while(decomp.empty() == false)
{
ContextID child = ContextMenu.first_child(node);
while(child)
{
if(ContextMenu[child].Name == decomp.top())
{
node = child;
decomp.pop();
break;
}
child = ContextMenu.next_sibling(child);
}
if(node == 0 || node != child)
{
node = ContextMenu.insert({ decomp.top(), nullptr }, node);
decomp.pop();
}
}
ContextMenu.insert({ name, constructor }, node);
}
Inspector::Inspector()
: EditorWindow("Inspector", 0)
, Graph(nullptr)
{
}
void Inspector::DrawWindow()
{
/*
if(Graph->Mouse.Selected.size() != 1)
{
ImGui::Text("Selected %d nodes.", Graph->Mouse.Selected.size());
return;
}
Graph->State.Nodes[*Graph->Mouse.Selected.begin()]->Inspect();
*/
}
/** DEPRECATED
void ShaderGraph::DrawWindow() void ShaderGraph::DrawWindow()
{ {
HandleInput(); HandleInput();
@ -340,7 +403,6 @@ void ShaderGraph::DrawWindow()
Mouse.LocksDragged = false; Mouse.LocksDragged = false;
} }
} }
*/
void ShaderGraph::HandleInput() void ShaderGraph::HandleInput()
@ -732,98 +794,6 @@ void ShaderGraph::DrawPin(NodeId node_id, Pin& pin, PinId pin_id, ImVec2 locatio
} }
} }
void ShaderGraph::DrawContextMenu()
{
const float GridSize = (Style.FontSize + Style.Grid.Lines.Padding);
if(ImGui::IsMouseClicked(ImGuiMouseButton_Right))
{
ContextMenuPosition = Mouse.Location;
}
if(ImGui::BeginPopupContextWindow())
{
if(ImGui::MenuItem("Copy", "Ctrl+C", false, !Mouse.Selected.empty())) Copy();
if(ImGui::MenuItem("Cut", "Ctrl+X", false, !Mouse.Selected.empty()))
{
Copy();
EraseSelection();
}
if(ImGui::MenuItem("Paste", "Ctrl+V", false, !Clipboard.Nodes.empty())) Paste(ContextMenuPosition);
ImGui::Separator();
ImGui::Text("Create");
ImGui::Separator();
// Create Nodes
ImVec2 position = ContextMenuPosition;
position = SnapToGrid(position);
std::stack<ContextID> context; context.push(0);
struct Visitor
{
bool operator()(ContextMenuItem& item, ContextID id)
{
const auto depth = Graph.ContextMenu.depth(id);
if(depth > Context.size()) return false;
while(depth < Context.size())
{
Context.pop();
ImGui::EndMenu();
}
if(Context.top() != Graph.ContextMenu.parent(id)) return false;
std::string name = std::format("{}##{}", item.Name, id);
if(item.Constructor)
{
if(ImGui::MenuItem(item.Name.c_str()))
{
Graph.AddNode(item.Constructor(Graph, Location));
}
}
else
{
if(ImGui::BeginMenu(item.Name.c_str()))
{
Context.push(id);
}
else
{
return false;
}
}
return false;
}
ShaderGraph& Graph;
const ImVec2 Location;
std::stack<ContextID>& Context;
} MenuVisitor
{
.Graph = *this
, .Location = position
, .Context = context
};
ContextMenu.traverse<ContextMenuHierarchy::pre_order>(MenuVisitor);
context.pop();
while(context.empty() == false)
{
ImGui::EndMenu();
context.pop();
}
ImGui::EndPopup();
}
}
void ShaderGraph::DrawConnections() void ShaderGraph::DrawConnections()
{ {
// Vars ============================================================================================================ // Vars ============================================================================================================
@ -1177,59 +1147,4 @@ Pin& ShaderGraph::GetPin(const PinPtr &ptr)
Node* node = State.Nodes[ptr.Node]; Node* node = State.Nodes[ptr.Node];
return (ptr.Input ? node->IO.Inputs : node->IO.Outputs)[ptr.Pin]; return (ptr.Input ? node->IO.Inputs : node->IO.Outputs)[ptr.Pin];
} }
*/
void ShaderGraph::Register(const std::filesystem::path& path, ConstructorPtr constructor)
{
const std::string name = path.filename().string();
std::stack<std::string> decomp;
std::filesystem::path current = path.parent_path();
while(current.empty() == false)
{
decomp.push(current.filename().string());
current = current.parent_path();
}
ContextID node = 0;
while(decomp.empty() == false)
{
ContextID child = ContextMenu.first_child(node);
while(child)
{
if(ContextMenu[child].Name == decomp.top())
{
node = child;
decomp.pop();
break;
}
child = ContextMenu.next_sibling(child);
}
if(node == 0 || node != child)
{
node = ContextMenu.insert({ decomp.top(), nullptr }, node);
decomp.pop();
}
}
ContextMenu.insert({ name, constructor }, node);
}
Inspector::Inspector()
: EditorWindow("Inspector", 0)
, Graph(nullptr)
{
}
void Inspector::DrawWindow()
{
if(Graph->Mouse.Selected.size() != 1)
{
ImGui::Text("Selected %d nodes.", Graph->Mouse.Selected.size());
return;
}
Graph->State.Nodes[*Graph->Mouse.Selected.begin()]->Inspect();
}

View File

@ -8,31 +8,31 @@ Size=400,400
Collapsed=0 Collapsed=0
[Window][Dear ImGui Demo] [Window][Dear ImGui Demo]
Pos=359,275 Pos=171,229
Size=375,341 Size=375,341
Collapsed=0 Collapsed=0
[Window][ Console] [Window][ Console]
Pos=303,905 Pos=303,953
Size=3137,464 Size=3137,464
Collapsed=0 Collapsed=0
DockId=0x00000004,0 DockId=0x00000004,0
[Window][ Profiler] [Window][ Profiler]
Pos=0,905 Pos=0,953
Size=301,464 Size=301,464
Collapsed=0 Collapsed=0
DockId=0x00000003,0 DockId=0x00000003,0
[Window][ Shader Graph] [Window][ Shader Graph]
Pos=0,0 Pos=0,0
Size=3068,903 Size=3068,951
Collapsed=0 Collapsed=0
DockId=0x00000005,0 DockId=0x00000005,0
[Window][WindowOverViewport_11111111] [Window][WindowOverViewport_11111111]
Pos=0,0 Pos=0,0
Size=3440,1369 Size=3440,1417
Collapsed=0 Collapsed=0
[Window][Example: Custom rendering] [Window][Example: Custom rendering]
@ -65,12 +65,12 @@ Collapsed=0
[Window][Inspector] [Window][Inspector]
Pos=3070,0 Pos=3070,0
Size=370,903 Size=370,951
Collapsed=0 Collapsed=0
DockId=0x00000006,0 DockId=0x00000006,0
[Docking][Data] [Docking][Data]
DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=3440,1369 Split=Y DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=3440,1417 Split=Y
DockNode ID=0x00000001 Parent=0x7C6B3D9B SizeRef=3440,974 Split=X DockNode ID=0x00000001 Parent=0x7C6B3D9B SizeRef=3440,974 Split=X
DockNode ID=0x00000005 Parent=0x00000001 SizeRef=3068,1417 CentralNode=1 Selected=0xD4C89E35 DockNode ID=0x00000005 Parent=0x00000001 SizeRef=3068,1417 CentralNode=1 Selected=0xD4C89E35
DockNode ID=0x00000006 Parent=0x00000001 SizeRef=370,1417 Selected=0xE7039252 DockNode ID=0x00000006 Parent=0x00000001 SizeRef=370,1417 Selected=0xE7039252