diff --git a/Makefile b/Makefile index a1e5bc8..ec1b968 100644 --- a/Makefile +++ b/Makefile @@ -30,7 +30,7 @@ rebuild_imnodes: # writes ttf fonts fron ./fonts/ to C compressed arrays in C source files # builds static library from font arrays definitions embed_fonts: - @cbuild/call_task.sh embed_fonts + @cbuild/call_task.sh embed_fonts 2>&1 | tee make_raw.log rebuild_all: rebuild_kerep rebuild_imgui rebuild_imnodes embed_fonts diff --git a/src/GraphModel/Attribute.cpp b/src/GraphModel/Attribute.cpp index e707fd1..5ffb1e3 100644 --- a/src/GraphModel/Attribute.cpp +++ b/src/GraphModel/Attribute.cpp @@ -3,7 +3,15 @@ namespace GraphC::GraphModel { Attribute::Attribute(id_t _id, Node* _parent_node, Attribute::Type _type, std::string _title) - : id(_id), parent_node(_parent_node), type(_type), title(_title) + : type(_type), id(_id), parent_node(_parent_node), title(_title) {} +const RBTree& Attribute::getIncomingEdges() const { + return incoming_edges; +} + +const RBTree& Attribute::getOutgoingEdges() const { + return outgoing_edges; +} + } diff --git a/src/GraphModel/Edge.cpp b/src/GraphModel/Edge.cpp index 92cbe65..59988c5 100644 --- a/src/GraphModel/Edge.cpp +++ b/src/GraphModel/Edge.cpp @@ -3,7 +3,7 @@ namespace GraphC::GraphModel { Edge::Edge(id_t _id, id_t _from_attr_id, id_t _to_attr_id) - : id(_id), from_attr_id(_from_attr_id), to_attr_id(_to_attr_id) + : id(_id), to_attr_id(_to_attr_id), from_attr_id(_from_attr_id) {} } diff --git a/src/GraphModel/Graph.cpp b/src/GraphModel/Graph.cpp index a0774f8..b379fc8 100644 --- a/src/GraphModel/Graph.cpp +++ b/src/GraphModel/Graph.cpp @@ -1,47 +1,168 @@ #include "GraphModel.hpp" +#include "../UsefulException.hpp" +#include "../format.hpp" namespace GraphC::GraphModel { Graph::Graph() {} -Graph::Graph(RBTree& _nodes, RBTree& _edges) +Graph::Graph(RBTree&& _nodes, RBTree&& _edges) : nodes(_nodes), edges(_edges) {} -RBTree& Graph::getNodes(){ + +const RBTree& Graph::getNodes() const { return nodes; } -RBTree& Graph::getEdges(){ +Node* Graph::createNode(Node& n){ + Node* result = nullptr; + useful_assert(nodes.tryAdd(n.id, n, &result), + format("can't create node with id %i", n.id)); + return result; +} +Node* Graph::createNode(Node&& n){ + return createNode(n); +} + +bool Graph::tryGetNode(id_t node_id, Node** result) const { + return nodes.tryGet(node_id, result); +} + +void Graph::deleteNode(Node& n){ + for(const auto& attr_p : n.attributes) + deleteAttribute(*attr_p.second); + useful_assert(nodes.tryDelete(n.id), + format("can't delete node with id %i", n.id)); +} + +void Graph::deleteNode(Node&& n){ + deleteNode(n); +} + +bool Graph::tryDeleteNode(id_t node_id){ + Node* n = nullptr; + if(!nodes.tryGet(node_id, &n)) + return false; + + deleteNode(*n); + return true; +} + + +const RBTree& Graph::getEdges() const { return edges; } - -bool tryCreateEdge(Attribute& from, Attribute& to, Edge&& result); -bool tryGetEdge(id_t edge_id, Edge&& result); -bool tryDeleteEdge(id_t edge_id); - -void Graph::deleteNode(id_t node_id){ - if(nodes.erase(node_id) < 1) - throw UsefulException("can't erase node with id"+toString_i64(node_id)); +Edge* Graph::createEdge(Attribute& from, Attribute& to){ + id_t id = id_gen.getNext(); + Edge* e = nullptr; + useful_assert(edges.tryAdd(id, Edge(id, from.id, to.id), &e), + format("can't create edge with id %i",id)); + + useful_assert(from.incoming_edges.tryAdd(id, e, nullptr), + format("edge with id %i already exists in Attribute::incoming_edges", id)); + + useful_assert(to.outgoing_edges.tryAdd(id, e, nullptr), + format("edge with id %i already exists in Attribute::outgoing_edges", id)); + return e; } -void Graph::deleteEdge(id_t edge_id){ +Edge* Graph::createEdge(Attribute&& from, Attribute&& to){ + return createEdge(from, to); +} +bool Graph::tryCreateEdge(id_t from_attr_id, id_t to_attr_id, Edge** result){ + Attribute* from; + if(!tryGetAttribute(from_attr_id, &from)) + return false; + Attribute* to; + if(!tryGetAttribute(to_attr_id, &to)) + return false; + Edge* e = createEdge(*from, *to); + if(result) + *result = e; + return true; +} + +bool Graph::tryGetEdge(id_t edge_id, Edge** result) const { + return edges.tryGet(edge_id, result); +} + +void Graph::deleteEdge(Edge& e){ + Attribute* a = nullptr; + useful_assert(tryGetAttribute(e.from_attr_id, &a), + format("source attribute with id %i was not found in Graph::nodes", e.from_attr_id)); + a->outgoing_edges.tryDelete(e.id); + + useful_assert(tryGetAttribute(e.to_attr_id, &a), + format("destination attribute with id %i was not found in Graph::nodes", e.to_attr_id)); + a->incoming_edges.tryDelete(e.id); + + useful_assert(edges.tryDelete(e.id), + format("can't delete attribute with id %i", e.id)); +} + +void Graph::deleteEdge(Edge&& e){ + deleteEdge(e); +} + +bool Graph::tryDeleteEdge(id_t edge_id){ + Edge* e; + if(!edges.tryGet(edge_id, &e)) + return false; + + deleteEdge(*e); + return true; } -bool Graph::tryGetAttribute(id_t attr_id, Attribute*& result){ - for(auto&& p_node : nodes){ - auto&& node_attrs = p_node.second.getAttributes(); - auto&& it = node_attrs.find(attr_id); - if(it != node_attrs.end()){ - result = &it->second; - return true; - } +const RBTree& Graph::getAttributes() const { + return attributes; +} + +bool Graph::tryGetAttribute(id_t attr_id, Attribute** result) const { + return attributes.tryGet(attr_id, result); +} + +Attribute* Graph::createAttribute(Attribute& a){ + Attribute* result = nullptr; + useful_assert(attributes.tryAdd(a.id, a, &result), + format("can't create attribute with id %i to Graph", a.id)); + useful_assert(a.parent_node->attributes.tryAdd(a.id, result, nullptr), + format("can't add attribute with id %i to Node", a.id)); + return result; +} + +Attribute* Graph::createAttribute(Attribute&& a){ + return createAttribute(a); +} + +void Graph::deleteAttribute(Attribute& a){ + for(const auto& edge_p : a.incoming_edges){ + useful_assert(edges.tryDelete(edge_p.second->id), + format("can't find edge with id %i", edge_p.second->id)); } - return false; + for(const auto& edge_p : a.outgoing_edges){ + useful_assert(edges.tryDelete(edge_p.second->id), + format("can't find edge with id %i", edge_p.second->id)); + } + useful_assert(attributes.tryDelete(a.id), + format("can't delete attribute with id %i", a.id)); +} + +void Graph::deleteAttribute(Attribute&& a){ + deleteAttribute(a); +} + +bool Graph::tryDeleteAttribute(id_t attr_id){ + Attribute* a; + if(!attributes.tryGet(attr_id, &a)) + return false; + + deleteAttribute(*a); + return true; } } \ No newline at end of file diff --git a/src/GraphModel/GraphModel.hpp b/src/GraphModel/GraphModel.hpp index 436919f..b71432a 100644 --- a/src/GraphModel/GraphModel.hpp +++ b/src/GraphModel/GraphModel.hpp @@ -17,28 +17,38 @@ public: id_t getNext(); }; -typedef struct Node Node; +typedef class Node Node; +typedef class Attribute Attribute; typedef struct Edge Edge; +typedef class Graph Graph; class Attribute { - std::vector> incoming_edges; - std::vector> outgoing_edges; + friend class Graph; + + // pointers to instances in RBTree::edges + RBTree incoming_edges; + RBTree outgoing_edges; public: enum class Type { - Input, Output, Static + Static, Input, Output }; const Attribute::Type type; const id_t id; - const Node* parent_node; + Node *const parent_node; std::string title; Attribute(id_t _id, Node* _parent_node, Attribute::Type _type, std::string _title); + + const RBTree& getIncomingEdges() const; + const RBTree& getOutgoingEdges() const; }; class Node { - RBTree attributes; + friend class Graph; + // pointers to Attributes in Graph::attributes + RBTree attributes; public: const id_t id; @@ -46,10 +56,7 @@ public: Node(id_t _id, std::string _title); - RBTree& getAttributes(); - bool tryCreateAttribute(IdGenerator id_gen, Attribute*& result); - bool tryGetAttribute(id_t attr_id, Attribute*& result); - bool tryDeleteAttribute(id_t attr_id); + const RBTree& getAttributes() const; }; struct Edge { @@ -61,30 +68,57 @@ struct Edge { }; class Graph { - friend class Attribute; - friend class Node; - friend class Edge; - RBTree nodes; RBTree edges; + RBTree attributes; public: IdGenerator id_gen; Graph(); - Graph(RBTree& _nodes, RBTree& _edges); + Graph(RBTree&& _nodes, RBTree&& _edges); - RBTree& getNodes(); - bool tryCreateNode(std::string _title, Node*& result); - bool tryGetNode(id_t node_id, Node*& result); + + const RBTree& getNodes() const; + + Node* createNode(Node& n); + Node* createNode(Node&& n); + + ///@param result nullable + bool tryGetNode(id_t node_id, Node** result) const; + + void deleteNode(Node& n); + void deleteNode(Node&& n); bool tryDeleteNode(id_t node_id); - RBTree& getEdges(); - bool tryCreateEdge(Attribute& from, Attribute& to, Edge*& result); - bool tryGetEdge(id_t edge_id, Edge*& result); + + const RBTree& getEdges() const; + + Edge* createEdge(Attribute& from, Attribute& to); + Edge* createEdge(Attribute&& from, Attribute&& to); + + ///@param result nullable + bool tryCreateEdge(id_t from_attr_id, id_t to_attr_id, Edge** result); + + ///@param result nullable + bool tryGetEdge(id_t edge_id, Edge** result) const; + + void deleteEdge(Edge& e); + void deleteEdge(Edge&& e); bool tryDeleteEdge(id_t edge_id); - bool tryGetAttribute(id_t attr_id, Attribute*& result); + + const RBTree& getAttributes() const; + + Attribute* createAttribute(Attribute& attr); + Attribute* createAttribute(Attribute&& attr); + + ///@param result nullable + bool tryGetAttribute(id_t attr_id, Attribute** result) const; + + void deleteAttribute(Attribute& a); + void deleteAttribute(Attribute&& a); + bool tryDeleteAttribute(id_t attr_id); }; } \ No newline at end of file diff --git a/src/GraphModel/Node.cpp b/src/GraphModel/Node.cpp index cc497b9..b72eba5 100644 --- a/src/GraphModel/Node.cpp +++ b/src/GraphModel/Node.cpp @@ -5,5 +5,9 @@ namespace GraphC::GraphModel { Node::Node(id_t _id, std::string _title) : id(_id), title(_title) {} + +const RBTree& Node::getAttributes() const { + return attributes; +} } diff --git a/src/RBTree.hpp b/src/RBTree.hpp index 76dde77..f46c2e1 100644 --- a/src/RBTree.hpp +++ b/src/RBTree.hpp @@ -2,7 +2,7 @@ #include "../../dependencies/kerep/src/base/base.h" #include -#include +#include // typedef char* TKey; // typedef char* TVal; @@ -69,57 +69,6 @@ class RBTree { } }; - template - struct TreeIterator : std::iterator { - Node* n; - - TreeIterator(TreeIterator const& src){ - n = src.n; - } - - TreeIterator(Node* ptr){ - n = ptr; - } - - bool operator!=(TreeIterator const& other) const { - return n == other.n; - } - - bool operator==(TreeIterator const& other) const { - return n == other.n; - } - - reference operator*() const { - if(n == nullptr) - throw "RBTree::TreeIterator::operator*() error: n == nullptr"; - return *((pointer)(void*)n); - } - - TreeIterator& operator++() { - if(n == nullptr) - return *this; - Node* prev_n = nullptr; - while(n->parent != nullptr && (n->right == nullptr || n->right == prev_n)){ - prev_n = n; - n = n->parent; - } - n = n->right; - return *this; - } - - TreeIterator& operator--() { - if(n == nullptr) - return *this; - Node* prev_n = nullptr; - while(n->parent != nullptr && (n->left == nullptr || n->left == prev_n)){ - prev_n = n; - n = n->parent; - } - n = n->left; - return *this; - } - }; - Node* root = nullptr; @@ -133,7 +82,7 @@ class RBTree { n = n->left; else if(key > n->key) n = n->right; - else return nullptr; // key == n->key + else return n->parent; // key == n->key } return parent; } @@ -147,8 +96,8 @@ class RBTree { if(x == x->parent->left) x->parent->left = y; else x->parent->right = y; - // TODO: maybe should set root to y? } + else root = y; // 3. move y.left to x.right if it exists x->right = y->left; if (x->right != nullptr) @@ -168,6 +117,7 @@ class RBTree { x->parent->left = y; else x->parent->right = y; } + else root = y; // 3. move y.right to x.left if it exists x->left = y->right; if (x->left != nullptr) @@ -178,12 +128,13 @@ class RBTree { } void transplantNode(Node* old, Node* neww){ - neww->parent = old->parent; if(old->parent == nullptr) root = neww; else if(old->parent->left == old) old->parent->left = neww; else old->parent->right = neww; + if(neww != nullptr) + neww->parent = old->parent; } void fixupInsertion(Node* n){ @@ -297,6 +248,48 @@ class RBTree { } + template + struct TreeIterator : std::iterator { + Node* n; + + TreeIterator(TreeIterator const& src){ + n = src.n; + } + + TreeIterator(Node* ptr){ + n = ptr; + } + + bool operator!=(TreeIterator const& other) const { + return n != other.n; + } + + bool operator==(TreeIterator const& other) const { + return n == other.n; + } + + TIteratorValue& operator*() const { + if(n == nullptr) + throw "RBTree::TreeIterator::operator*() error: n == nullptr"; + return *((TIteratorValue*)(void*)n); + } + + void operator++() { + if(n == nullptr) + return; + if(n->right) + n = n->right->getMinChild(); + else { + Node* p = n->parent; + while(p != nullptr && n == p->right){ + n = p; + p = p->parent; + } + n = p; + } + } + }; + public: using iterator = TreeIterator>; using const_iterator = TreeIterator>; @@ -425,8 +418,10 @@ public: if(!resultPtr) return false; Node* parent = findParentForKey(key); - Node* n; - if(key < parent->key) + Node* n = nullptr; + if(parent == nullptr) + n = root; + else if(key < parent->key) n = parent->left; else n = parent->right; // if there is no node with the given key @@ -442,8 +437,10 @@ public: bool tryDelete(TKey key){ Node* parent = findParentForKey(key); - Node* n; - if(key < parent->key) + Node* n = nullptr; + if(parent == nullptr) + n = root; + else if(key < parent->key) n = parent->left; else n = parent->right; // key not found @@ -453,12 +450,12 @@ public: if(n->left == nullptr){ transplantNode(n, n->right); - if(n->color == Color::Black) + if(n->color == Color::Black && n->right != nullptr) fixupDeletion(n->right); } else if(n->right == nullptr){ transplantNode(n, n->left); - if(n->color == Color::Black) + if(n->color == Color::Black && n->left != nullptr) fixupDeletion(n->left); } else { @@ -477,6 +474,7 @@ public: } delete n; + return true; } @@ -499,8 +497,37 @@ public: } const_iterator end() const { - if(root == nullptr) return const_iterator(nullptr); - return const_iterator(root->getMaxChild()); + } + + void _generateGraphVizCodeForChildren(std::stringstream& ss, Node* n) const { + if(!n) + return; + if(n->color == Color::Red) + ss<<" \""<key<<"\" [color=red]\n"; + if(n->left){ + ss<<" \""<key<<"\" -> \""<left->key<<"\" [side=L]\n"; + _generateGraphVizCodeForChildren(ss, n->left); + } + else ss<<" \""<key<<"\" -> \"null\" [side=L]\n"; + if(n->right){ + ss<<" \""<key<<"\" -> \""<right->key<<"\" [side=R]\n"; + _generateGraphVizCodeForChildren(ss, n->right); + } + else ss<<" \""<key<<"\" -> \"null\" [side=R]\n"; + } + + std::string generateGraphVizCode() const { + std::stringstream ss; + ss<<"digraph {\n" + " node [style=filled,color=gray];\n"; + if(root == nullptr) + ss<<" \"null\"\n"; + else { + ss<<" \"null-parent\" -> \""<key<<"\"\n"; + _generateGraphVizCodeForChildren(ss, root); + } + ss<<"}"; + return ss.str(); } }; diff --git a/src/UsefulException.hpp b/src/UsefulException.hpp index ae6f668..de53447 100644 --- a/src/UsefulException.hpp +++ b/src/UsefulException.hpp @@ -18,10 +18,4 @@ public: virtual char const* what() const noexcept; }; -#if DEBUG == 1 -/// Use this macro to find bugs in debug build. Release build could be compiled without asserts. Define DEBUG=1 to enable assert compilation. - #define assert(EXPR, ERRMSG) if(!EXPR) throw UsefulException(ERRMSG); -#else -/// Use this macro to find bugs in debug build. Release build could be compiled without asserts. Define DEBUG=1 to enable assert compilation. - #define assert(EXPR, ERRMSG) EXPR -#endif +#define useful_assert(EXPR, ERRMSG) if(!EXPR) throw UsefulException(ERRMSG); diff --git a/src/gui/NodeEditor.cpp b/src/gui/NodeEditor.cpp index fa0bb5c..055f68b 100644 --- a/src/gui/NodeEditor.cpp +++ b/src/gui/NodeEditor.cpp @@ -4,16 +4,15 @@ namespace GraphC::gui { -void drawNode(GraphModel::Node& node){ +void NodeEditor::drawNode(const GraphModel::Node& node){ ImNodes::BeginNode(node.id); ImNodes::BeginNodeTitleBar(); ImGui::TextUnformatted(node.title.c_str()); ImNodes::EndNodeTitleBar(); - for(GraphModel::Attribute& a : node.attributes) - { - switch (a.type) - { + for(auto& a_p : node.getAttributes()){ + auto&& a = *a_p.second; + switch (a.type){ case GraphModel::Attribute::Type::Input: ImNodes::BeginInputAttribute(a.id); ImGui::Text("%s", a.title.c_str()); @@ -35,19 +34,72 @@ void drawNode(GraphModel::Node& node){ break; } } + + const char* items[]={ + "static","input", "output" + }; + static int selected_item_i = 0; + if(ImGui::Button("add attribute")) + ImGui::OpenPopup("new_attribute_popup"); + + if(ImGui::BeginPopup("new_attribute_popup")){ + ImGui::SeparatorText("New attribute properties"); + + static char buf[256]; + ImGui::InputText("label", buf, sizeof(buf)); + + if(ImGui::Button("select type")) + ImGui::OpenPopup("select_type_popup"); + ImGui::SameLine(); + ImGui::TextUnformatted(items[selected_item_i]); + + if(ImGui::BeginPopup("select_type_popup")){ + for (int i = 0; i < IM_ARRAYSIZE(items); i++) + if (ImGui::Selectable(items[i])) + selected_item_i = i; + ImGui::EndPopup(); + } + + if(ImGui::Button("Create attribute")){ + graph.createAttribute(GraphModel::Attribute( + graph.id_gen.getNext(), + const_cast(&node), + (GraphModel::Attribute::Type)selected_item_i, + std::string(buf))); + + std::cout<<"ATTRIBUTES:\n"; + std::cout<getNodes()){ - drawNode(p.second); + for(auto& n_p : graph.getNodes()){ + drawNode(n_p.second); } // draw edges - const auto& edges = graph->getEdges(); - edges.empl = GraphModel::Edge(); - for(auto&& p : edges){ + for(auto& p : graph.getEdges()){ ImNodes::Link(p.second.id, p.second.to_attr_id, p.second.from_attr_id); } + ImNodes::EndNodeEditor(); // handle edge creation - GraphModel::id_t to_attr_id; - GraphModel::id_t from_attr_id; + GraphModel::id_t from_attr_id, to_attr_id; if (ImNodes::IsLinkCreated(&from_attr_id, &to_attr_id)) { - graph->createEdge(graph->getEdge(from_attr_id), graph->getEdge(to_attr_id)); + if(!graph.tryCreateEdge(from_attr_id, to_attr_id, nullptr)) + throw UsefulException(format("can't create edge from attribute %i to attribute %i", from_attr_id, to_attr_id)); + std::cout<<"EDGES:\n"; + std::cout< bool { - return edge.id == edge_id; - }); - assert(iter != edges.end()); - edges.erase(iter); + if(!graph.tryDeleteEdge(edge_id)) + throw UsefulException(format("can't delete edge with id %i", edge_id)); + std::cout<<"EDGES:\n"; + std::cout< graph; + GraphModel::Graph graph; + void drawNode(const GraphModel::Node& node); + GraphModel::Node* CreateExampleNode(std::string title); + public: NodeEditor(std::string _title); diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 546f69b..bb554d5 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -75,8 +75,8 @@ void GUI::init(const char* window_title){ ImNodes::CreateContext(); ImNodes::StyleColorsDark(); ImNodes::PushAttributeFlag(ImNodesAttributeFlags_EnableLinkDetachWithDragClick); - node_editor=NodeEditor("node editor"); - node_editor.show(); + node_editor = new NodeEditor("node editor"); + node_editor->show(); } // Wait, poll and handle events (inputs, window resize, etc.) @@ -118,7 +118,7 @@ void GUI::draw_frame(){ // Draw UI draw_bg_window(); draw_debug_window(io, &main_loop_wait_for_input); - node_editor.draw(); + node_editor->draw(); // Rendering ImGui::Render(); diff --git a/src/gui/gui.hpp b/src/gui/gui.hpp index 9e1c4a2..1b380b2 100644 --- a/src/gui/gui.hpp +++ b/src/gui/gui.hpp @@ -1,6 +1,7 @@ #pragma once -#include "../../dependencies/kerep/src/base/base.h" +#include "../../dependencies/kerep/src/base/std.h" +#include "../../dependencies/kerep/src/kprint/kprintf.h" #include "../../dependencies/SDL2/include/SDL.h" #include "../../dependencies/SDL2/include/SDL_opengl.h" #include "../../dependencies/imgui/imgui.h" @@ -9,6 +10,8 @@ #include "imgui_extensions.hpp" #include "fonts.hpp" #include "exceptions.hpp" +#include +#include /// converts hex color to float vector #define RGBAHexToF(R8,G8,B8,A8) ImVec4(((u8)35)/255.0f, ((u8)35)/255.0f, ((u8)50)/255.0f, ((u8)255)/255.0f) @@ -33,7 +36,7 @@ private: bool show_metrics_window = false; SDL_Window* sdl_window; SDL_GLContext gl_context; - NodeEditor node_editor = NodeEditor("new editor"); + NodeEditor* node_editor = nullptr; public: void init(const char* window_title);