diff --git a/External/open-cpp-utils/CMakeLists.txt b/External/open-cpp-utils/CMakeLists.txt index a4e9d33..3abd2cb 100644 --- a/External/open-cpp-utils/CMakeLists.txt +++ b/External/open-cpp-utils/CMakeLists.txt @@ -10,6 +10,12 @@ set(OPENCPPUTILS_HEADERS startup.h template_utils.h unique_id.h + dynarray.h + redec.h + hash_table.h + math.h + set.h + map.h ) add_library(open-cpp-utils INTERFACE diff --git a/External/open-cpp-utils/any.h b/External/open-cpp-utils/any.h index 7afdccd..cb5bbcf 100644 --- a/External/open-cpp-utils/any.h +++ b/External/open-cpp-utils/any.h @@ -65,10 +65,17 @@ public: public: // Assignment operators ------------------------------------------------------------------------------------------------ + any& operator=(const any&) = default; any& operator=(any&&) = default; +// Access -------------------------------------------------------------------------------------------------------------- + + template + V& get() { static_assert(std::disjunction, std::is_same...>{}); return static_cast(*this); } + + // Cast operators ------------------------------------------------------------------------------------------------------ operator reference() { return Value; } diff --git a/External/open-cpp-utils/directed_tree.h b/External/open-cpp-utils/directed_tree.h index ade9d89..1ac01ff 100644 --- a/External/open-cpp-utils/directed_tree.h +++ b/External/open-cpp-utils/directed_tree.h @@ -42,7 +42,7 @@ public: class unordered; private: - struct director; + struct Node_; // Typedefs ============================================================================================================ @@ -53,7 +53,7 @@ public: using node_queue = std::deque; private: - using hierarchy = std::vector; + using hierarchy = std::vector; using storage = std::vector; @@ -66,7 +66,7 @@ public: // Data Structures ===================================================================================================== private: - struct director + struct Node_ { enum flags { @@ -76,7 +76,7 @@ private: node parent, child, prev_sibling, next_sibling; 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 */ - directed_tree() : graph_{ director() }, data_{ data_type() }, freed_{ } { } + directed_tree() : graph_{ Node_() }, data_{ data_type() }, freed_{ } { } // Tree Navigation ----------------------------------------------------------------------------------------------------- @@ -99,7 +99,7 @@ public: * \param id Node id to reference * \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) @@ -115,6 +115,20 @@ public: */ [[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) * \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 * \param data Value to insert * \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 */ - 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(freed_.empty()) { freed_.push_back(static_cast(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 node id = freed_.front(); freed_.pop_front(); - director& node = graph_[id]; - director& parent = graph_[p_id]; + Node_& node = graph_[id]; + Node_& parent = graph_[p_id]; // If the parent has a child, update the child's references - if(parent.child) + if(parent.child && !last) { // Update the next child - director& nchild = graph_[parent.child]; + Node_& nchild = graph_[parent.child]; node.prev_sibling = nchild.prev_sibling; nchild.prev_sibling = id; // If present, update the previous child if(nchild.prev_sibling) { - director& pchild = graph_[nchild.prev_sibling]; + Node_& pchild = graph_[nchild.prev_sibling]; pchild.next_sibling = id; } } // Setup node node.parent = p_id; - node.next_sibling = parent.child; + node.next_sibling = last ? 0 : parent.child; node.child = 0; - node.flags = director::VALID; + node.flags = Node_::VALID; node.depth = parent.depth + 1; // 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 data_[id] = data; @@ -221,8 +251,8 @@ public: if(id == 0) return; // Mark node as invalid and push it to the freed list - director& erased = graph_[id]; - erased.Flags &= ~director::VALID; + Node_& erased = graph_[id]; + erased.Flags &= ~Node_::VALID; freed_.push_back(id); // Update the parent's child @@ -237,8 +267,8 @@ public: while(stack.empty() == false) { node next = stack.front(); stack.pop_front(); - director& child = graph_[next]; - child.Flags &= ~director::VALID; + Node_& child = graph_[next]; + child.Flags &= ~Node_::VALID; freed_.push_back(next); if(child.Sibling) stack.push_front(child.Sibling); @@ -326,7 +356,7 @@ public: node operator()(node id) { 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.child) visit_queue_.push_front(current.child); @@ -351,7 +381,7 @@ public: 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.child) visit_queue_.push_front(current.child); @@ -375,12 +405,12 @@ public: public: 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(); - director& current = graph_.graph_[node]; + id = visit_queue_.front(); visit_queue_.pop_front(); + Node_& current = graph_.graph_[id]; if(current.Sibling) { @@ -388,7 +418,7 @@ public: visit_queue_.push_back(graph_.left_most(current.Sibling)); } - return node; + return id; } private: @@ -405,17 +435,17 @@ public: public: 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(); - if(node == 0) return node; - director& current = graph_.graph_[node]; + id = visit_queue_.front(); visit_queue_.pop_front(); + if(id == 0) return id; + 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: @@ -438,10 +468,10 @@ public: void operator()() { - node node = 0; - while(node = order_(node)) + node id = 0; + while(id = order_(id)) { - if(visitor_(graph_[node], node)) break; + if(visitor_(graph_[id], id)) break; } } diff --git a/External/open-cpp-utils/dynarray.h b/External/open-cpp-utils/dynarray.h new file mode 100644 index 0000000..a0464ee --- /dev/null +++ b/External/open-cpp-utils/dynarray.h @@ -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 +#include + +namespace open_cpp_utils +{ + +template> +using dynarray = std::vector; + +} + +#endif //DYNARRAY_H diff --git a/External/open-cpp-utils/hash_table.h b/External/open-cpp-utils/hash_table.h new file mode 100644 index 0000000..78f8029 --- /dev/null +++ b/External/open-cpp-utils/hash_table.h @@ -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 +#include + +#include "math.h" +#include "optional.h" + +namespace open_cpp_utils +{ + +template, class Alloc = std::allocator> +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::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{}; + + +// Structs ============================================================================================================= + +private: + struct _Node + { + optional 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(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 +hash_table::hash_table(const hash_table& other) + : table_(nullptr) + , capacity_(other.capacity_) + , size_(other.size_) + , load_factor_(0.8) +{ + +} + +template +void hash_table::clear() +{ + alloc_.deallocate(table_, capacity_); + capacity_ = size_ = 0; +} + +template +void hash_table::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 +void hash_table::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 +bool hash_table::contains(const_reference x) +{ + return _find(x) != nullnode; +} + +template +void hash_table::_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 hash_table::node hash_table::_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 hash_table::node hash_table::_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 hash_table::size_type hash_table::_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 hash_table::size_type hash_table::_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 diff --git a/External/open-cpp-utils/map.h b/External/open-cpp-utils/map.h new file mode 100644 index 0000000..24ff4b6 --- /dev/null +++ b/External/open-cpp-utils/map.h @@ -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>> +class map +{ +// Typedefs ============================================================================================================ + +public: + struct hash { size_t operator()(const pair_type& pair) const { return std::hash{}(pair.first); } }; + + using key_type = Key; + using value_type = Value; + using pair_type = std::pair; + using table_type = set; + + 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 table_; +}; + +template +void map::insert(const_key_reference key, const_value_reference value) +{ + iterator it = find({ key, value }); + if(it != end()) table_.insert({ key, value }); +} + +template +void map::erase(const_key_reference key) +{ + table_.erase({ key, value_type() }); +} + +template +typename map::value_reference map::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 diff --git a/External/open-cpp-utils/math.h b/External/open-cpp-utils/math.h new file mode 100644 index 0000000..93af958 --- /dev/null +++ b/External/open-cpp-utils/math.h @@ -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 + +namespace open_cpp_utils +{ + +template +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 diff --git a/External/open-cpp-utils/object_pool.h b/External/open-cpp-utils/object_pool.h index 783c1ce..dc8e809 100644 --- a/External/open-cpp-utils/object_pool.h +++ b/External/open-cpp-utils/object_pool.h @@ -16,18 +16,18 @@ #ifndef OBJECT_POOL_H #define OBJECT_POOL_H -#include -#include -#include -#include - -#include "template_utils.h" +#include "dynarray.h" +#include "optional.h" namespace open_cpp_utils { -template> -class object_pool +/** + * \brief + * \tparam T + */ +template +class object_list { // Typedefs ============================================================================================================ @@ -38,17 +38,10 @@ public: using reference = T&; using const_reference = const T&; - using index_type = int64_t; using uuid_type = uint64_t; private: - using node = std::tuple; - - -// Constants =========================================================================================================== - -public: - static constexpr std::integral_constant nullidx{}; + using node = optional; // Functions =========================================================================================================== @@ -57,33 +50,62 @@ public: // 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(); - void cleanup(); + +// Modifiers ----------------------------------------------------------------------------------------------------------- + + size_t size() { return data_.size(); } + + +// Modifiers ----------------------------------------------------------------------------------------------------------- + + void clear() { data_.clear(); freed_.clear(); } uuid_type insert(const_reference& value); void erase(uuid_type id); - void erase(index_type idx); + + object_list& operator=(const object_list&) = default; + object_list& operator=(object_list&&) = default; + // Accessors ----------------------------------------------------------------------------------------------------------- - reference operator[](uuid_type id); - const_reference operator[](uuid_type id) const; - bool operator()(uuid_type id) const; + reference operator[](uuid_type id) { assert(data_[id]()); return data_[id]; } + const_reference operator[](uuid_type id) const { assert(data_[id]()); return data_[id]; } + bool operator()(uuid_type id) const { return data_[id](); } - reference operator[](index_type idx); - const_reference operator[](index_type idx) const; - bool operator()(index_type idx) const; + typename dynarray::iterator begin() { return data_.begin(); } + typename dynarray::iterator end() { return data_.end(); } +// Variables =========================================================================================================== + private: - std::vector data_; - _Hash map_; - std::stack freed_; + dynarray data_; + dynarray freed_; }; +template +typename object_list::uuid_type object_list::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 +void object_list::erase(uuid_type id) +{ + data_[id].reset(); + freed_.push_back(id); +} + } // open_cpp_utils #endif //OBJECT_POOL_H diff --git a/External/open-cpp-utils/optional.h b/External/open-cpp-utils/optional.h index c777dd6..2097cb9 100644 --- a/External/open-cpp-utils/optional.h +++ b/External/open-cpp-utils/optional.h @@ -26,7 +26,7 @@ namespace open_cpp_utils public: using value_type = T; - optional() : data_(), valid_(false) { } + optional() : valid_(false) { } optional(const value_type& data) : data_(data), valid_(true) { } optional(value_type&& data) : data_(data), valid_(true) { } optional(const optional& other) = default; diff --git a/External/open-cpp-utils/redec.h b/External/open-cpp-utils/redec.h new file mode 100644 index 0000000..1437764 --- /dev/null +++ b/External/open-cpp-utils/redec.h @@ -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 diff --git a/External/open-cpp-utils/set.h b/External/open-cpp-utils/set.h new file mode 100644 index 0000000..a4509f4 --- /dev/null +++ b/External/open-cpp-utils/set.h @@ -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, class Alloc = std::allocator> using set = hash_table; + +} + +#endif //SET_H diff --git a/Include/Graph/Nodes/Math.h b/Include/Graph/Nodes/Math.h index 81d8864..d163afe 100644 --- a/Include/Graph/Nodes/Math.h +++ b/Include/Graph/Nodes/Math.h @@ -24,33 +24,111 @@ namespace ocu = open_cpp_utils; namespace OpenShaderDesigner::Nodes::Math { - inline static constexpr ImColor HeaderColor = ImColor(0x92, 0x16, 0x16); +// Header Colors ======================================================================================================= - struct Constant : public Node - { - using ValueType = ocu::any; +inline static constexpr ImColor HeaderColor = ImColor(0xA7, 0x62, 0x53); +inline static constexpr ImColor HeaderHoveredColor = ImColor(0xC5, 0x79, 0x67); +inline static constexpr ImColor HeaderActiveColor = ImColor(0x82, 0x4C, 0x40); - Constant(ShaderGraph& graph, ImVec2 pos); - virtual ~Constant() = default; +inline static const std::string HeaderMarker = "\uF3B9 "; - [[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; - struct Add : public Node - { - Add(ShaderGraph& graph, ImVec2 pos); - virtual ~Add() = default; + Integral(ShaderGraph& graph, ImVec2 pos); + virtual ~Integral() = default; - [[nodiscard]] Node* Copy(ShaderGraph& graph) const override; - void Inspect() override; - }; + [[nodiscard]] Node* Copy(ShaderGraph& graph) const override; + void Inspect() override; + + ValueType Value; +}; + +RegisterNode("Math/Constants/Integral", Integral); + + +// ===================================================================================================================== +// Unsigned Integral +// ===================================================================================================================== + +struct UnsignedIntegral : public Node +{ + using ValueType = ocu::any; + + 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; + + 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; + + 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 diff --git a/Include/Graph/ShaderGraph.h b/Include/Graph/ShaderGraph.h index 7fb73c1..bf3114d 100644 --- a/Include/Graph/ShaderGraph.h +++ b/Include/Graph/ShaderGraph.h @@ -29,6 +29,11 @@ #include #include +#include + +#include "open-cpp-utils/any.h" +#include "open-cpp-utils/object_pool.h" + namespace ocu = open_cpp_utils; #define RegisterNode(Name, Type) \ @@ -38,68 +43,58 @@ namespace ocu = open_cpp_utils; namespace OpenShaderDesigner { class ShaderGraph; - using PinId = uint16_t; - using NodeId = uint32_t; - struct PinPtr - { - struct Hash - { - size_t operator()(const PinPtr& p) const - { - return p.hash(); - } - }; + using PinType = int; + enum PinType_ + { + PinType_Int = 0 + , PinType_UInt + , PinType_Float + , PinType_Vector - NodeId Node; - PinId Pin; - bool Input; + , PinType_Any + , PinType_COUNT + }; - size_t hash() const { return (Input ? 0 : 0x8000000) | static_cast(Node) << 32 | static_cast(Pin & 0x7FFFFFFF); } - - bool operator<(const PinPtr& o) const { return hash() < o.hash(); } - bool operator==(const PinPtr& o) const { return hash() == o.hash(); } - }; + using PinFlags = unsigned int; + enum PinFlags_ + { + PinFlags_None = 0 + , PinFlags_NoCollapse = 1 << 0 + , PinFlags_NoPadding = 1 << 1 + }; struct Pin { - enum PinType - { - INT = 0 - , UINT - , FLOAT - , VECTOR - - , ANY - , COUNT + inline const static ImColor Colors[PinType_COUNT] = { + ImColor(0x64, 0x94, 0xAA) // Int + , ImColor(0x7A, 0x9F, 0x82) // Unsigned Int + , ImColor(0xA6, 0x3D, 0x40) // Float + , ImColor(0xE9, 0xB8, 0x72) // Vector + , ImColor(0xD2, 0xD5, 0xD3) // Any }; - enum PinDirection - { - 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] = { + inline const static std::string TypeNames[PinType_COUNT] = { "Int" , "Unsigned Int" , "Float" , "Vector" + , "Any" }; - std::string Name; - PinType Type; - PinDirection Direction; - }; + using Ambiguous = ocu::any; + + 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 { @@ -108,31 +103,28 @@ namespace OpenShaderDesigner struct { - std::string Title = "Node"; - ImColor Color = Pin::Colors[Pin::VECTOR]; - bool Enabled = true; + std::string Title; + ImColor Color, HoveredColor, ActiveColor; + bool Enabled; } Header; struct { std::vector Inputs, Outputs; - bool DynamicInputs = false; + bool DynamicInputs; } IO; struct { - ImVec2 Size; bool Const; } Info; - Node( - ShaderGraph& graph, ImVec2 pos - , const std::string& title, ImColor color - , const std::vector& inputs, bool dyn_inputs - , const std::vector& outputs - , bool constant = false); + Node(ShaderGraph& graph, ImVec2 pos); ~Node() = default; + void DrawPin(ImGuiID id, Pin& pin, ImPinDirection direction); + void Draw(ImGuiID id); + virtual Node* Copy(ShaderGraph& graph) const = 0; virtual void Inspect() = 0; }; @@ -143,15 +135,6 @@ namespace OpenShaderDesigner private: friend Node; - using Connection = std::pair; - using ConnectionMap = std::unordered_multimap; - - struct Line - { - ImColor Color; - float Thickness; - }; - using ConstructorPtr = Node*(*)(ShaderGraph&, ImVec2); struct ContextMenuItem { @@ -161,10 +144,8 @@ namespace OpenShaderDesigner struct GraphState { - ShaderGraph& Parent; - std::vector Nodes; - std::set Erased; - ConnectionMap Connections; + ShaderGraph& Parent; + ocu::object_list Nodes; GraphState(ShaderGraph& parent); GraphState(const GraphState& other); @@ -177,55 +158,22 @@ namespace OpenShaderDesigner using ContextID = ContextMenuHierarchy::node; 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 void PushState(); 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: ShaderGraph(); ~ShaderGraph(); 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); @@ -233,86 +181,9 @@ namespace OpenShaderDesigner bool GrabFocus; GraphState State; - std::stack History; + std::stack History; - struct - { - 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 FocusedNode; - std::unordered_map Locks; - std::unordered_set DragSelect; - bool LocksDragged, NodeHovered; - ocu::optional NewConnection; - std::unordered_set Selected; - } Mouse; - - struct - { - ImVec2 Location; - float Zoom, Scroll; - } Camera; - - struct - { - std::vector Nodes; - ConnectionMap Connections; - } Clipboard; - - bool Focused; - ImVec2 ContextMenuPosition; + ImVec2 ContextMenuPosition; friend class Inspector; }; diff --git a/Source/Graph/Nodes/Math.cpp b/Source/Graph/Nodes/Math.cpp index 1dff91f..921fce1 100644 --- a/Source/Graph/Nodes/Math.cpp +++ b/Source/Graph/Nodes/Math.cpp @@ -19,73 +19,131 @@ using namespace OpenShaderDesigner; using namespace OpenShaderDesigner::Nodes::Math; -Constant::Constant(ShaderGraph& graph, ImVec2 pos) - : Node( - graph, pos - , "Constant", HeaderColor - , { }, false - , { { "Out", Pin::FLOAT, Pin::OUTPUT } } - ) + +// ===================================================================================================================== +// Integral +// ===================================================================================================================== + +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(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(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) - : Node( - graph, pos - , "Add", HeaderColor - , { { "A", Pin::ANY, Pin::INPUT }, { "B", Pin::ANY, Pin::INPUT } }, true - , { { "Out", Pin::ANY, Pin::OUTPUT } } - ) -{ } + : Node(graph, pos) +{ + Header.Title = HeaderMarker + "Add"; + Header.Color = HeaderColor; + 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 { diff --git a/Source/Graph/ShaderGraph.cpp b/Source/Graph/ShaderGraph.cpp index 3a511d2..51eb4cb 100644 --- a/Source/Graph/ShaderGraph.cpp +++ b/Source/Graph/ShaderGraph.cpp @@ -17,15 +17,15 @@ #include #include -#include #include #include -#include #include + #include -#include +#include "imgui-extras/imgui_extras.h" + using namespace OpenShaderDesigner; @@ -41,16 +41,12 @@ ShaderGraph::GraphState::GraphState(ShaderGraph& parent) ShaderGraph::GraphState::GraphState(const GraphState& other) : Parent(other.Parent) - , Nodes(other.Nodes.size(), nullptr) - , Connections(other.Connections) - , Erased(other.Erased) + , Nodes(other.Nodes) { - NodeId id = 0; - for(const Node* node : other.Nodes) - { - if(node) Nodes[id] = node->Copy(Parent); - ++id; - } + for(Node*& node : Nodes) + { + node = node->Copy(Parent); + } } ShaderGraph::GraphState::~GraphState() @@ -63,153 +59,107 @@ ShaderGraph::GraphState::~GraphState() ShaderGraph::GraphState& ShaderGraph::GraphState::operator=(const GraphState& other) { - for(Node* node : Nodes) - { - if(node) delete node; - } + Nodes = other.Nodes; - Nodes.clear(); - Nodes.resize(other.Nodes.size(), nullptr); + for(Node*& node : Nodes) if(node) node = node->Copy(Parent); - NodeId id = 0; - 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(1 + glm::max(node.IO.Inputs.size(), node.IO.Outputs.size())); - return glm::max(HeaderHeight + PinHeight, 2 * HeaderHeight); + return *this; } Node::Node( - ShaderGraph& graph, ImVec2 pos -, const std::string& title, ImColor color -, const std::vector& inputs, bool dyn_inputs -, const std::vector& outputs, bool constant) + ShaderGraph& graph, ImVec2 pos) : Position(pos) , Header { - .Title = title - , .Color = color - , .Enabled = true + .Title = "Node" + , .Color = ImColor(0xA7, 0x62, 0x53) + , .HoveredColor = ImColor(0xC5, 0x79, 0x67) + , .ActiveColor = ImColor(0x82, 0x4C, 0x40) + , .Enabled = true } , IO { - .Inputs = inputs - , .Outputs = outputs - , .DynamicInputs = dyn_inputs + .DynamicInputs = false } , Info { - .Size = ImVec2(graph.CalculateWidth(*this), graph.CalculateHeight(*this)) - , .Const = constant + .Const = false } { } +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().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().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() : EditorWindow("\uED46 Shader Graph", 0) , GrabFocus(false) , 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() { - Mouse.Location = ImGui::GetMousePos(); - Camera.Scroll = Camera.Zoom = 1.0f; - EditorSystem::Open()->Graph = this; GrabFocus = true; @@ -230,7 +177,6 @@ void ShaderGraph::OnOpen() void ShaderGraph::DrawWindow() { - static int test_in, test_out; ImNodeGraph::BeginGraph("ShaderGraph"); ImNodeGraph::SetPinColors(Pin::Colors); @@ -241,56 +187,173 @@ void ShaderGraph::DrawWindow() 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); - - 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(); + State.Nodes[id]->Draw(id); } - // Second Test Node - { - 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(); - } + DrawContextMenu(); 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 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& Context; + } MenuVisitor + { + .Graph = *this + , .Location = position + , .Context = context + }; + + ContextMenu.traverse(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 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() { HandleInput(); @@ -340,7 +403,6 @@ void ShaderGraph::DrawWindow() Mouse.LocksDragged = false; } } -*/ 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 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& Context; - } MenuVisitor - { - .Graph = *this - , .Location = position - , .Context = context - }; - - ContextMenu.traverse(MenuVisitor); - - context.pop(); - while(context.empty() == false) - { - ImGui::EndMenu(); - context.pop(); - } - - ImGui::EndPopup(); - } -} - void ShaderGraph::DrawConnections() { // Vars ============================================================================================================ @@ -1177,59 +1147,4 @@ Pin& ShaderGraph::GetPin(const PinPtr &ptr) Node* node = State.Nodes[ptr.Node]; 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 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(); -} +*/ \ No newline at end of file diff --git a/imgui.ini b/imgui.ini index 616b5a2..a0c2c58 100644 --- a/imgui.ini +++ b/imgui.ini @@ -8,31 +8,31 @@ Size=400,400 Collapsed=0 [Window][Dear ImGui Demo] -Pos=359,275 +Pos=171,229 Size=375,341 Collapsed=0 [Window][ Console] -Pos=303,905 +Pos=303,953 Size=3137,464 Collapsed=0 DockId=0x00000004,0 [Window][ Profiler] -Pos=0,905 +Pos=0,953 Size=301,464 Collapsed=0 DockId=0x00000003,0 [Window][ Shader Graph] Pos=0,0 -Size=3068,903 +Size=3068,951 Collapsed=0 DockId=0x00000005,0 [Window][WindowOverViewport_11111111] Pos=0,0 -Size=3440,1369 +Size=3440,1417 Collapsed=0 [Window][Example: Custom rendering] @@ -65,12 +65,12 @@ Collapsed=0 [Window][Inspector] Pos=3070,0 -Size=370,903 +Size=370,951 Collapsed=0 DockId=0x00000006,0 [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=0x00000005 Parent=0x00000001 SizeRef=3068,1417 CentralNode=1 Selected=0xD4C89E35 DockNode ID=0x00000006 Parent=0x00000001 SizeRef=370,1417 Selected=0xE7039252