// ===================================================================================================================== // imnode-graph, and open source extension for Dear ImGui that adds functionality for drawing a node graph. // Copyright (C) 2024 Medusa Slockbower // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // ===================================================================================================================== #ifndef IMGUI_NODES_H #define IMGUI_NODES_H #include #include // ===================================================================================================================== // Math // ===================================================================================================================== #include "stdint.h" #include "math.h" template T ImIsPrime(T x) { if(x <= 1) return false; if(x == 2 || x == 3) return true; if(x % 2 == 0 || x % 3 == 0) return false; uint64_t limit = static_cast(sqrt(static_cast(x))); for(T i = 5; i <= limit; i += 6) { if(x % i == 0 || x % (i + 2) == 0) return false; } return true; } // ===================================================================================================================== // Type & Forward Definitions // ===================================================================================================================== // Data Structures ----------------------------------------------------------------------------------------------------- struct ImNodeGraphContext; struct ImPinPtr; struct ImPinConnection; template struct ImObjectPool; template struct ImOptional; // Typedefs ------------------------------------------------------------------------------------------------------------ // Graph Types using ImNodeGraphColor = int; using ImNodeGraphFlags = int; // Pin Types using ImPinType = int; using ImPinFlags = int; using ImPinDirection = bool; // Connections using ImConnectionValidation = bool(*)(ImPinPtr, ImPinPtr); // ===================================================================================================================== // Enums // ===================================================================================================================== enum ImNodeGraphFlags_ { ImNodeGraphFlags_None = 0 , ImNodeGraphFlags_NoHeader = 1 << 0 }; enum ImNodeGraphColor_ { ImNodeGraphColor_GridBackground , ImNodeGraphColor_GridPrimaryLines , ImNodeGraphColor_GridSecondaryLines , ImNodeGraphColor_NodeBackground , ImNodeGraphColor_NodeHoveredBackground , ImNodeGraphColor_NodeActiveBackground , ImNodeGraphColor_NodeHeaderColor , ImNodeGraphColor_NodeHeaderHoveredColor , ImNodeGraphColor_NodeHeaderActiveColor , ImNodeGraphColor_NodeOutline , ImNodeGraphColor_NodeOutlineSelected , ImNodeGraphColor_PinBackground , ImNodeGraphColor_SelectRegionBackground , ImNodeGraphColor_SelectRegionOutline , ImNodeGraphColor_COUNT }; enum ImPinDirection_ { ImPinDirection_Input = false , ImPinDirection_Output = true }; enum ImPinFlags_ { ImPinFlags_None = 0 , ImPinFlags_NoPadding = 1 }; // ===================================================================================================================== // Data Structures // ===================================================================================================================== /** * \brief Optional value, similar to std::optional * \tparam T Value Type */ template struct ImOptional { using value_type = T; using reference = T&; using const_reference = const T&; T Value; bool Set; ImOptional() : Set(false) { } ImOptional(const T& value) : Value(value), Set(true) { } ImOptional(const ImOptional&) = default; ImOptional(ImOptional&&) = default; ~ImOptional() = default; ImOptional& operator=(const ImOptional& other) = default; ImOptional& operator=(ImOptional&& other) = default; ImOptional& operator=(const T& value) { Value = value; Set = true; return *this; } ImOptional& operator=(T&& value) { Value = value; Set = true; return *this; } bool operator==(const ImOptional &) const = default; bool operator==(const T& o) const { return Set && Value == o; } operator T&() { IM_ASSERT(Set); return Value; } operator const T&() const { IM_ASSERT(Set); return Value; } T* operator->() { IM_ASSERT(Set); return &Value; } const T* operator->() const { IM_ASSERT(Set); return &Value; } bool operator()() const { return Set; } void Reset() { Set = false; } }; /** * \brief Equivalent to std::deque, may be used as a queue, stack, etc. * \tparam T Value type */ template struct ImDeque { class iterator; struct Node; using NodePtr = Node*; using value_type = T; using reference = T&; using const_reference = const T&; using pointer = T*; using const_pointer = const T*; using node_index = int; struct Node { T Value; NodePtr Next, Prev; }; NodePtr First, Last; int Size; ImDeque() : First(nullptr), Last(nullptr) { } ImDeque(const ImDeque&); ImDeque(ImDeque&&) = default; ~ImDeque(); bool Empty() const { return First == nullptr; } void Clear(); void PushFront(const T& v); void PushBack(const T& v); void PopFront(); void PopBack(); T& Front() { IM_ASSERT(First); return First->Value; } const T& Front() const { IM_ASSERT(First); return First->Value; } T& Back() { IM_ASSERT(Last); return Last->Value; } const T& Back() const { IM_ASSERT(Last); return Last->Value; } }; #ifndef IMSET_MIN_CAPACITY #define IMSET_MIN_CAPACITY 7 // Should be prime and >= 5 #endif template size_t ImHash(T v) noexcept; template<> inline size_t ImHash(uint64_t x) noexcept { x ^= x >> 33U; x *= UINT64_C(0xff51afd7ed558ccd); x ^= x >> 33U; x *= UINT64_C(0xc4ceb9fe1a85ec53); x ^= x >> 33U; return x; } template<> inline size_t ImHash(int x) noexcept { return ImHash(static_cast(x)); } template<> inline size_t ImHash(ImGuiID x) noexcept { return ImHash(static_cast(x)); } template<> inline size_t ImHash(const char* str) noexcept { return ImHash(ImHashStr(str)); } /** * \brief Set of values, based on Robin Hood Hashing * \tparam T Value type */ template struct ImSet { struct Node { T Value; bool Set; int PSL; Node() : Value(), Set(false), PSL(0) { } }; class iterator; using value_type = T; using reference = T&; using const_reference = const T&; using pointer = T*; using const_pointer = const T*; using iterator_type = iterator; int Size; int Capacity; Node* Table; float LoadFactor; ImSet(); ImSet(const ImSet&); ImSet(ImSet&&) = default; ~ImSet(); void Clear(); void Insert(const T& v); void Erase(const T& v); bool Contains(const T& v) { return _Find(v) != -1; } int _Find(const T& v); bool _CheckLoadFactor(); void _IncreaseCapacity(); int _NextIndex(int i) { return (i + 1) % Capacity; } int _PrevIndex(int i) { return (i - 1 + Capacity) % Capacity; } // These operate on the principle of (6n +/- 1) int _NextPrime(int x); int _PrevPrime(int x); class iterator { public: iterator(const ImSet* set, int idx); iterator(const iterator&) = default; iterator(iterator&&) = default; ~iterator() = default; iterator& operator++(); iterator operator++(int); bool operator==(const iterator& o) const = default; bool operator!=(const iterator& o) const = default; const_reference operator*() const { return set_->Table[idx_].Value; } const_pointer operator->() const { return &set_->Table[idx_].Value; } private: const ImSet* set_; int idx_; }; iterator begin() { return iterator(this, 0); } iterator end() { return iterator(this, Capacity); } iterator cbegin() const { return iterator(this, 0); } iterator cend() const { return iterator(this, Capacity); } }; /** * \brief Set of values, based on a Red-Black Tree * \tparam T Value type */ template struct ImOrderedSet { class iterator; struct Node; using NodePtr = Node*; using NodeColor = bool; using value_type = T; using reference = T&; using const_reference = const T&; using pointer = T*; using const_pointer = const T*; using node_index = int; using iterator_type = iterator; enum NodeColor_ { NodeColor_Red = true , NodeColor_Black = false }; struct Node { T Value; NodeColor Color; NodePtr Parent, Left, Right; Node(const T& v, NodePtr p) : Value(v), Color(NodeColor_Red), Parent(p), Left(nullptr), Right(nullptr) { } }; Node* Root; int Size; ImOrderedSet() : Root(nullptr), Size(0) { } ImOrderedSet(const ImOrderedSet&) = default; ImOrderedSet(ImOrderedSet&&) = default; ~ImOrderedSet(); ImOrderedSet& operator=(const ImOrderedSet&) = default; ImOrderedSet& operator=(ImOrderedSet&& other) = default; void Clear(); void Insert(const T& v); void Erase(const T& v); Node* _Find(const T& v); static NodePtr _LeftMost(NodePtr x); void _RotateLeft(NodePtr x); void _RotateRight(NodePtr x); NodePtr _InsertBST(const T& v); void _FixInsert(NodePtr x); NodePtr _EraseBST(const T& v); void _FixErase(NodePtr x); class iterator { public: iterator(Node* node) : current_(node), visit_queue_() { } iterator(const iterator&) = default; iterator(iterator&&) = default; ~iterator() = default; iterator& operator++(); iterator operator++(int); bool operator==(const iterator& o) const { return current_ == o.current_; } bool operator!=(const iterator& o) const { return current_ != o.current_; } const_reference operator*() const { return current_->Value; } const_pointer operator->() const { return ¤t_->Value; } private: NodePtr current_; ImDeque visit_queue_; }; iterator begin() { return iterator(_LeftMost(Root)); } iterator end() { return iterator(nullptr); } }; /** * \brief Data Structure for holding a pool of objects with generated ids * \tparam T Value Type */ template struct ImObjectList { class iterator; using value_type = T; using reference = T&; using const_reference = const T&; using pointer = T*; using const_pointer = const T*; using iterator_type = iterator; ImVector Data; ImVector Active; ImVector Freed; ImObjectList() = default; ImObjectList(const ImObjectList&) = default; ImObjectList(ImObjectList&&) = default; ~ImObjectList() = default; [[nodiscard]] inline size_t Size() const { return Active.Size; } ImGuiID Insert(const T& v); void Erase(ImGuiID id); void Clear() { Data.clear(); Active.clear(); Freed.clear(); } void Reset() { memset(Active.Data, false, Active.size_in_bytes()); } void Cleanup(); T& operator[](ImGuiID id) { IM_ASSERT(Active[id]); return Data[id]; } const T& operator[](ImGuiID id) const { IM_ASSERT(Active[id]); return Data[id]; } bool operator()(ImGuiID id) const { return Active[id]; } class iterator { public: iterator(ImObjectList& pool, int idx); iterator& operator++(); iterator operator++(int); bool operator==(const iterator&) const = default; bool operator!=(const iterator&) const = default; reference operator*() const { return (*pool_)[idx_]; } pointer operator->() const { return &(*pool_)[idx_]; } private: ImObjectList* pool_; int idx_; }; iterator begin() { return iterator(*this, 0); } iterator end() { return iterator(*this, Active.size()); } class const_iterator { public: const_iterator(const ImObjectList& pool, int idx); const_iterator& operator++(); const_iterator operator++(int); bool operator==(const const_iterator&) const = default; bool operator!=(const const_iterator&) const = default; const_reference operator*() const { return (*pool_)[idx_]; } const_pointer operator->() const { return &(*pool_)[idx_]; } private: const ImObjectList* pool_; int idx_; }; const_iterator begin() const { return const_iterator(*this, 0); } const_iterator end() const { return const_iterator(*this, Active.size()); } }; /** * \brief Data Structure for holding a pool of objects with user provided ids * \tparam T Value Type */ template struct ImObjectPool { class iterator; class reverse_iterator; static constexpr int nullidx = -1; using value_type = T; using reference = T&; using const_reference = const T&; using pointer = T*; using const_pointer = const T*; using iterator_type = iterator; ImVector Data; ImVector Active; ImVector Freed; ImVector Order; ImVector IdxToID; ImGuiStorage IDToIdx; ImObjectPool() = default; ImObjectPool(const ImObjectPool&) = default; ImObjectPool(ImObjectPool&&) = default; ~ImObjectPool() = default; [[nodiscard]] inline size_t Size() const { return Order.Size; } void Clear() { Data.clear(); Active.clear(); Freed.clear(); Order.clear(); IDToIdx.Clear(); IdxToID.clear(); } void Reset() { memset(Active.Data, false, Active.size_in_bytes()); } int Cleanup(); void PushToTop(ImGuiID id); ImObjectPool& operator=(const ImObjectPool&) = default; T& operator[](ImGuiID id); const T& operator[](ImGuiID id) const { int idx = IDToIdx.GetInt(id, nullidx); IM_ASSERT(idx != nullidx && Active[idx]); return Data[idx]; }; bool operator()(ImGuiID id) const { int idx = IDToIdx.GetInt(id, nullidx); return idx != nullidx && Active[idx]; } T& operator[](int idx) { IM_ASSERT(idx >= 0 && idx < Data.Size); return Data[Order[idx]]; } const T& operator[](int idx) const { IM_ASSERT(idx >= 0 && idx < Data.Size); return Data[Order[idx]]; } bool operator()(int idx) const { IM_ASSERT(idx >= 0 && idx < Data.Size); return Active[Order[idx]]; } class iterator { public: iterator(ImObjectPool& pool, int idx); iterator& operator++(); iterator operator++(int); bool operator==(const iterator&) const = default; bool operator!=(const iterator&) const = default; reference operator*() const { return (*pool_)[idx_]; } pointer operator->() const { return &(*pool_)[idx_]; } private: ImObjectPool* pool_; int idx_; }; iterator begin() { return iterator(*this, 0); } iterator end() { return iterator(*this, Order.size()); } class reverse_iterator { public: reverse_iterator(ImObjectPool& pool, int idx); reverse_iterator& operator++(); reverse_iterator operator++(int); bool operator==(const reverse_iterator&) const = default; bool operator!=(const reverse_iterator&) const = default; reference operator*() const { return (*pool_)[idx_ - 1]; } pointer operator->() const { return &(*pool_)[idx_ - 1]; } private: ImObjectPool* pool_; int idx_; }; reverse_iterator rbegin() { return reverse_iterator(*this, Order.size()); } reverse_iterator rend() { return reverse_iterator(*this, 0); } private: int _GetNextIndex(ImGuiID id); void _PushBack(ImGuiID id); }; struct ImGraphCamera { ImVec2 Position; float Scale; ImGraphCamera(); }; struct ImNodeGraphStyle { float GridPrimaryStep; float GridPrimaryThickness; float GridSecondaryThickness; float NodeRounding; float NodePadding; float NodeOutlineThickness; float NodeOutlineSelectedThickness; float SelectRegionRounding; float SelectRegionOutlineThickness; float ItemSpacing; float PinRadius; float PinOutlineThickness; float ConnectionThickness; ImColor Colors[ImNodeGraphColor_COUNT]; const ImColor* PinColors; ImNodeGraphStyle(); ImNodeGraphStyle(const ImNodeGraphStyle&) = default; ImU32 GetColorU32(ImNodeGraphColor idx) const { return Colors[idx]; } ImVec4 GetColorVec4(ImNodeGraphColor idx) const { return Colors[idx]; } }; struct ImNodeGraphSettings { float ZoomRate; float ZoomSmoothing; ImVec2 ZoomBounds; ImNodeGraphSettings(); }; struct ImPinPtr { ImGuiID Node; ImGuiID Pin; ImPinDirection Direction; bool operator==(const ImPinPtr&) const = default; }; struct ImUserPinPtr { int Node; int Pin; ImPinDirection Direction; bool operator==(const ImUserPinPtr&) const = default; ImUserPinPtr() : Node(0), Pin(0), Direction(ImPinDirection_Input) { } ImUserPinPtr(int n, int p, ImPinDirection d) : Node(n), Pin(p), Direction(d) { } }; struct ImPinConnection { ImPinPtr A, B; }; // ===================================================================================================================== // Functionality // ===================================================================================================================== namespace ImNodeGraph { // Context ------------------------------------------------------------------------------------------------------------- /** * \brief Setup the ImNodeGraph Context, must be called after ImGui::CreateContext() */ ImNodeGraphContext* CreateContext(); /** * \brief Cleanup the ImNodeGraph Context, must be called before ImGui::DestroyContext() */ void DestroyContext(ImNodeGraphContext* ctx = NULL); /** * \brief Getter for the current context * \return Pointer to the current context */ ImNodeGraphContext* GetCurrentContext(); void SetCurrentContext(ImNodeGraphContext* ctx); void AddFont(const char* path, float size = 0, const ImWchar* glyph_ranges = nullptr); // Graph --------------------------------------------------------------------------------------------------------------- /** * \brief Push a new graph to a window * \param title Title for the graph * \param size_arg Size of the graph, */ void BeginGraph(const char* title, const ImVec2& size_arg = { 0, 0 }); void EndGraph(); /** * \brief Begin an operation on the graph after it has already been drawn * \param title Title of the graph to operate on */ bool BeginGraphPostOp(const char *title); void EndGraphPostOp(); /** * \brief Set a validation function to check the validity of connections * \param validation */ void SetGraphValidation(ImConnectionValidation validation); /** * \brief Helper to get the scale of the camera for the current graph * \return */ float GetCameraScale(); // Functions to transform locations ImVec2 GridToWindow(const ImVec2& pos); ImVec2 WindowToScreen(const ImVec2& pos); ImVec2 GridToScreen(const ImVec2& pos); ImVec2 ScreenToGrid(const ImVec2& pos); ImVec2 ScreenToWindow(const ImVec2& pos); ImVec2 WindowToGrid(const ImVec2& pos); ImVec2 SnapToGrid(const ImVec2& pos); // Camera correct ImGui::PushItemWidth() void PushItemWidth(float width); const ImObjectList& GetConnections(); void ResetGraph(); void ResetGraph(const char* graph); // Nodes --------------------------------------------------------------------------------------------------------------- /** * \brief Push a new node to add widgets to in a window * \param id Id of the node * \return False if the node is collapsed */ void BeginNode(int id, ImVec2& pos); void EndNode(); /** * \brief Write a header for the node * \param title Title of the header * \param color Base color * \param hovered Color for when hovered by mouse * \param active Color for when clicked on by mouse */ void BeginNodeHeader(const char* title, ImColor color, ImColor hovered, ImColor active); void BeginNodeHeader(int id, ImColor color, ImColor hovered, ImColor active); void EndNodeHeader(); /** * \brief Get a set of the currently selected nodes * \return */ ImSet* GetSelected(); ImSet* GetSelected(const char* title); /** * \brief Get the user id for a given node id * \param id ImNodeGraph id for the node * \return User provided id for the node */ int GetUserID(ImGuiID id); int GetUserID(const char* graph, ImGuiID id); // Pins ---------------------------------------------------------------------------------------------------------------- /** * \brief Set the colors attributed to each pin type * \param colors Array of color values */ void SetPinColors(const ImColor* colors); /** * \brief Add a pin to the node * \param id Id of the pin * \param type Type of the pin * \param direction Direction of the pin */ bool BeginPin(int id, ImPinType type, ImPinDirection direction, ImPinFlags flags = 0); void EndPin(); // Functions to check connection status bool IsPinConnected(); bool IsPinConnected(ImPinPtr pin); const ImVector& GetPinConnections(); const ImVector& GetPinConnections(ImPinPtr pin); const ImVector& GetNewConnections(); const ImVector& GetErasedConnections(); // Get qualified pointer to the currently selected pin ImPinPtr GetPinPtr(); /** * \brief Get the user id for a given pin id * \param ptr ImNodeGraph id for the pin * \return User provided id for the pin */ int GetUserID(ImPinPtr ptr); int GetUserID(const char* graph, ImPinPtr ptr); // Connections --------------------------------------------------------------------------------------------------------- // Create and break connections between pins bool MakeConnection(const ImPinPtr& a, const ImPinPtr& b); bool MakeConnection(const ImUserPinPtr& a, const ImUserPinPtr& b); void BreakConnection(ImGuiID connection); void BreakConnections(const ImPinPtr& pin); bool Exists(ImPinConnection connection); // Get the pins associated with the provided connection id ImPinConnection GetConnection(ImGuiID connection); } // ===================================================================================================================== // Template Implementations // ===================================================================================================================== // ImDeque ------------------------------------------------------------------------------------------------------------- template ImDeque::~ImDeque() { Clear(); } template void ImDeque::Clear() { while(First) { NodePtr x = First; First = First->Next; delete x; } First = Last = nullptr; Size = 0; } template void ImDeque::PushFront(const T &v) { First = new Node{ v, First, nullptr }; if(Last == nullptr) Last = First; ++Size; } template void ImDeque::PushBack(const T &v) { Last = new Node{ v, nullptr, Last }; if(First == nullptr) First = Last; ++Size; } template void ImDeque::PopFront() { Node* erase = First; First = First->Next; if(First) First->Prev = nullptr; else Last = nullptr; delete erase; --Size; } template void ImDeque::PopBack() { Node* erase = Last; Last = Last->Prev; if(Last) Last->Prev = nullptr; else First = nullptr; delete erase; --Size; } // ImSet -------------------------------------------------------------------------------------------------------- template ImSet::ImSet() : Table(nullptr) , Size(0) , Capacity(0) , LoadFactor(0.8f) { } template ImSet::ImSet(const ImSet& o) : Table(nullptr) , Size(o.Size) , Capacity(o.Capacity) , LoadFactor(o.LoadFactor) { Table = (Node*)IM_ALLOC(Capacity * sizeof(Node)); memcpy(Table, o.Table, Capacity * sizeof(Node)); } template ImSet::~ImSet() { Clear(); } template void ImSet::Clear() { if(Table) IM_FREE(Table); Table = nullptr; Size = Capacity = 0; } template void ImSet::Insert(const T &v) { if(_CheckLoadFactor()) _IncreaseCapacity(); int idx = ImHash(v) % Capacity; int PSL = 0; T Value = v; while(Table[idx].Set) { Node& node = Table[idx]; if(Table[idx].Value == v) return; if(PSL > node.PSL) // Higher PSL, Swap { ImSwap(PSL, node.PSL); ImSwap(Value, node.Value); } idx = _NextIndex(idx); ++PSL; } Table[idx].Value = Value; Table[idx].Set = true; Table[idx].PSL = PSL; ++Size; } template void ImSet::Erase(const T &v) { int idx = _Find(v); if(idx == -1) return; Table[idx].Set = false; --Size; int prev = idx; idx = _NextIndex(idx); while(Table[idx].Set && Table[idx].PSL > 0) { Node &a = Table[prev], &b = Table[idx]; ImSwap(a, b); --a.PSL; prev = idx; idx = _NextIndex(idx); } } template int ImSet::_Find(const T &v) { // Can be improved, not necessary for the needs of this library if(Capacity == 0) return -1; int idx = ImHash(v) % Capacity; int PSL = 0; while(Table[idx].Set) { Node& node = Table[idx]; if(node.PSL > PSL) return -1; if(node.Value == v) return idx; idx = _NextIndex(idx); ++PSL; } return -1; } template bool ImSet::_CheckLoadFactor() { if(Capacity == 0) return true; float load = Size / static_cast(Capacity); return load >= LoadFactor; } template void ImSet::_IncreaseCapacity() { Node* old = Table; int old_capacity = Capacity; Capacity = _NextPrime(Capacity); Table = static_cast(IM_ALLOC(Capacity * sizeof(Node))); memset(Table, 0, Capacity * sizeof(Node)); Size = 0; for(int i = 0; i < old_capacity; ++i) { if(old[i].Set) Insert(old[i].Value); } IM_FREE(old); } template int ImSet::_NextPrime(int x) { int n = (x + 1) / 6; n *= 2; while(true) { x = (n * 6) - 1; if(!ImIsPrime(x)) x = (n * 6) + 1; if(!ImIsPrime(x)) { ++n; continue; } return x < IMSET_MIN_CAPACITY ? IMSET_MIN_CAPACITY : x; } } template int ImSet::_PrevPrime(int x) { int n = (x + 1) / 6; n /= 2; while(true) { x = (n * 6) - 1; if(!ImIsPrime(x)) x = (n * 6) + 1; if(!ImIsPrime(x)) { --n; continue; } return x < IMSET_MIN_CAPACITY ? IMSET_MIN_CAPACITY : x; } } template ImSet::iterator::iterator(const ImSet* set, int idx) : set_(set), idx_(idx) { while(idx_ < set_->Capacity && set_->Table[idx_].Set == false) { ++idx_; } } template typename ImSet::iterator & ImSet::iterator::operator++() { ++idx_; while(idx_ < set_->Capacity && set_->Table[idx_].Set == false) { ++idx_; } return *this; } template typename ImSet::iterator ImSet::iterator::operator++(int) { iterator ret = *this; ++idx_; while(idx_ < set_->Capacity && set_->Table[idx_].Set == false) { ++idx_; } return ret; } // ImOrderedSet -------------------------------------------------------------------------------------------------------- template ImOrderedSet::~ImOrderedSet() { Clear(); } template void ImOrderedSet::Clear() { ImDeque queue; queue.PushBack(Root); while(queue.Empty() == false) { NodePtr x = queue.Front(); queue.PopFront(); if(x->Left) queue.PushBack(x->Left); if(x->Right) queue.PushBack(x->Right); delete x; } Root = nullptr; } template void ImOrderedSet::Insert(const T& value) { NodePtr node = _InsertBST(value); if(node) _FixInsert(node); } template void ImOrderedSet::Erase(const T &v) { } template typename ImOrderedSet::Node* ImOrderedSet::_Find(const T &v) { NodePtr x = Root; while(x) { if(v < x->Value) x = x->Left; else if(x->Value < v) x = x->Right; else return x; } return x; } template ImOrderedSet::NodePtr ImOrderedSet::_LeftMost(NodePtr x) { if(x == nullptr) return nullptr; while(x->Left) x = x->Left; return x; } template void ImOrderedSet::_RotateLeft(NodePtr x) { NodePtr y = x->Right; x->Right = y->Left; if(y->Left) y->Left->Parent = x; y->Parent = x->Parent; if(x->Parent == nullptr) Root = y; else if(x == x->Parent->Left) x->Parent->Left = y; else x->Parent->Right = y; y->Left = x; x->Parent = y; } template void ImOrderedSet::_RotateRight(NodePtr x) { NodePtr y = x->Left; x->Left = y->Right; if(y->Right) y->Right->Parent = x; y->Parent = x->Parent; if(x->Parent == nullptr) Root = y; else if(x == x->Parent->Right) x->Parent->Right = y; else x->Parent->Left = y; y->Right = x; x->Parent = y; } template typename ImOrderedSet::NodePtr ImOrderedSet::_InsertBST(const T &v) { NodePtr *x = &Root; NodePtr p = nullptr; while(*x) { NodePtr n = *x; if(v < n->Value) x = &n->Left; else if(n->Value < v) x = &n->Right; else return nullptr; p = n; } return *x = new Node(v, p); } template void ImOrderedSet::_FixInsert(NodePtr x) { while (x != Root && x->Parent->Color == NodeColor_Red) { NodePtr p = x->Parent; NodePtr g = p->Parent; bool s = p == g->Left; NodePtr u = s ? g->Right : g->Left; if (u->Color == NodeColor_Red) { p->Color = NodeColor_Black; u->Color = NodeColor_Black; g->Color = NodeColor_Red; x = g; } else if(s) // Best for readability and reducing the number of branches { if (x == p->Right) { x = x->Parent; _RotateLeft(x); } x->Parent->Color = NodeColor_Black; x->Parent->Parent->Color = NodeColor_Red; _RotateRight(x->Parent->Parent); } else { if (x == p->Left) { x = x->Parent; _RotateRight(x); } x->Parent->Color = NodeColor_Black; x->Parent->Parent->Color = NodeColor_Red; _RotateLeft(x->Parent->Parent); } } Root->Color = NodeColor_Black; } template typename ImOrderedSet::NodePtr ImOrderedSet::_EraseBST(const T &v) { NodePtr x = _Find(v); } template typename ImOrderedSet::iterator & ImOrderedSet::iterator::operator++() { NodePtr x = current_; NodePtr p = x->Parent; if(p && x == p->Left) visit_queue_.PushBack(p); if(x->Right) visit_queue_.PushBack(_LeftMost(x->Right)); if(visit_queue_.Empty()) current_ = nullptr; else { current_ = visit_queue_.Back(); visit_queue_.PopBack(); } return *this; } template typename ImOrderedSet::iterator ImOrderedSet::iterator::operator++(int) { iterator ret = *this; NodePtr x = current_; NodePtr p = x->Parent; if(p && x == p->Left) visit_queue_.PushBack(p); if(x->Right) visit_queue_.PushBack(_LeftMost(x->Right)); if(visit_queue_.Empty()) current_ = nullptr; else { current_ = visit_queue_.Back(); visit_queue_.PopBack(); } return ret; } // ImObjectList -------------------------------------------------------------------------------------------------------- template ImGuiID ImObjectList::Insert(const T &v) { if(Freed.empty()) { Data.push_back(v); Active.push_back(true); return Data.Size - 1; } ImGuiID id = Freed.back(); Freed.pop_back(); Data[id] = v; Active[id] = true; return id; } template void ImObjectList::Erase(ImGuiID id) { Active[id] = false; Freed.push_back(id); Data[id] = T(); } template void ImObjectList::Cleanup() { Freed.Size = 0; for(int i = 0; i < Active.Size; ++i) { if(Active[i]) continue; Freed.push_back(i); } } template ImObjectList::iterator::iterator(ImObjectList &pool, int idx) : pool_(&pool) , idx_(idx) { while(idx_ < pool_->Size() && !(*pool_)(idx_)) ++idx_; } template typename ImObjectList::iterator & ImObjectList::iterator::operator++() { ++idx_; while(idx_ < pool_->Size() && !(*pool_)(idx_)) ++idx_; return *this; } template typename ImObjectList::iterator ImObjectList::iterator::operator++(int) { const_iterator retval = *this; ++idx_; while(idx_ < pool_->Size() && !(*pool_)(idx_)) ++idx_; return retval; } template ImObjectList::const_iterator::const_iterator(const ImObjectList &pool, int idx) : pool_(&pool) , idx_(idx) { while(idx_ < pool_->Size() && !(*pool_)(idx_)) ++idx_; } template typename ImObjectList::const_iterator & ImObjectList::const_iterator::operator++() { ++idx_; while(idx_ < pool_->Size() && !(*pool_)(idx_)) ++idx_; return *this; } template typename ImObjectList::const_iterator ImObjectList::const_iterator::operator++(int) { const_iterator retval = *this; ++idx_; while(idx_ < pool_->Size() && !(*pool_)(idx_)) ++idx_; return retval; } // ImObjectPool -------------------------------------------------------------------------------------------------------- template int ImObjectPool::Cleanup() { int cnt = Freed.Size; Freed.Size = 0; for(int i = 0; i < Active.Size; ++i) { if(Active[i]) continue; Freed.push_back(i); Order.find_erase(i); IDToIdx.SetInt(IdxToID[i], nullidx); IdxToID[i] = 0; } return Freed.Size - cnt; } template T& ImObjectPool::operator[](ImGuiID id) { int idx = IDToIdx.GetInt(id, nullidx); // Get the mapped index if(idx == nullidx) { idx = _GetNextIndex(id); // If it is unassigned, get the next available index Order.push_back(idx); } Active[idx] = true; return Data[idx]; } template void ImObjectPool::PushToTop(ImGuiID id) // Should always be O(n) { int idx = IDToIdx.GetInt(id, nullidx); if(idx == nullidx) return; for(int i = Order.find_index(idx); i < Order.size() - 1; ++i) { ImSwap(Order[i], Order[i + 1]); } } template int ImObjectPool::_GetNextIndex(ImGuiID id) { int idx = Data.Size; // Default to size of data array if(!Freed.empty()) // If there are freed indices, pop one { idx = Freed.back(); Freed.pop_back(); Data[idx] = T(); Active[idx] = true; IdxToID[idx] = id; // Reset index values } else _PushBack(id); // Otherwise, push back new index IDToIdx.SetInt(id, idx); return idx; } template void ImObjectPool::_PushBack(ImGuiID id) { Data.push_back(T()); Active.push_back(true); IdxToID.push_back(id); } template ImObjectPool::iterator::iterator(ImObjectPool &pool, int idx) : pool_(&pool) , idx_(idx) { while(idx_ < pool_->Size() && !(*pool_)(idx_)) ++idx_; } template typename ImObjectPool::iterator & ImObjectPool::iterator::operator++() { ++idx_; while(idx_ < pool_->Size() && !(*pool_)(idx_)) ++idx_; return *this; } template typename ImObjectPool::iterator ImObjectPool::iterator::operator++(int) { iterator retval = *this; ++idx_; while(idx_ < pool_->Size() && !(*pool_)(idx_)) ++idx_; return retval; } template ImObjectPool::reverse_iterator::reverse_iterator(ImObjectPool &pool, int idx) : pool_(&pool) , idx_(idx) { while(idx_ > 0 && !(*pool_)(idx_ - 1)) --idx_; } template typename ImObjectPool::reverse_iterator & ImObjectPool::reverse_iterator::operator++() { --idx_; while(idx_ > 0 && !(*pool_)(idx_ - 1)) --idx_; return *this; } template typename ImObjectPool::reverse_iterator ImObjectPool::reverse_iterator::operator++(int) { iterator retval = *this; --idx_; while(idx_ > 0 && !(*pool_)(idx_ - 1)) --idx_; return retval; } #endif //IMGUI_NODES_H