From 9c062150b74af6ec5b6ae3d09e382c28e0fb8b14 Mon Sep 17 00:00:00 2001 From: Quentin Quadrat Date: Tue, 28 Nov 2023 00:28:28 +0100 Subject: [PATCH] Big refacto Editor --- imgui.ini | 61 ++++-- src/Editor/DearImGui/Drawable.cpp | 81 +++---- src/Editor/DearImGui/Drawable.hpp | 11 +- src/Editor/PetriEditor.cpp | 337 +++++++++++++++++++----------- src/Editor/PetriEditor.hpp | 15 +- 5 files changed, 322 insertions(+), 183 deletions(-) diff --git a/imgui.ini b/imgui.ini index 1a8ea0b..c459df7 100644 --- a/imgui.ini +++ b/imgui.ini @@ -1,6 +1,6 @@ [Window][DockSpaceViewport_11111111] Pos=0,19 -Size=1024,749 +Size=1920,1010 Collapsed=0 [Window][Debug##Default] @@ -10,44 +10,79 @@ Collapsed=0 [Window][Console] Pos=0,19 -Size=751,699 +Size=1647,960 Collapsed=0 DockId=0x00000001,1 [Window][Message] -Pos=0,720 -Size=1024,48 +Pos=0,981 +Size=1920,48 Collapsed=0 DockId=0x00000004,0 [Window][Places] -Pos=753,19 -Size=271,699 +Pos=1649,19 +Size=271,960 Collapsed=0 DockId=0x00000002,1 [Window][Transitions] -Pos=753,19 -Size=271,699 +Pos=1649,19 +Size=271,960 Collapsed=0 DockId=0x00000002,0 [Window][Arcs] -Pos=753,19 -Size=271,699 +Pos=1649,19 +Size=271,960 Collapsed=0 DockId=0x00000002,2 [Window][Petri net] Pos=0,19 -Size=751,699 +Size=1647,960 Collapsed=0 DockId=0x00000001,0 +[Window][Dear ImGui Demo] +Pos=0,19 +Size=1647,960 +Collapsed=0 +DockId=0x00000001,2 + +[Window][Example: Simple layout] +Pos=60,60 +Size=500,440 +Collapsed=0 + +[Window][Example: Simple layout/left pane_AED60EF8] +IsChild=1 +Size=150,386 + +[Window][Dear ImGui Style Editor] +Pos=0,19 +Size=751,699 +Collapsed=0 +DockId=0x00000001,3 + +[Window][Example: Property editor] +Pos=75,169 +Size=430,450 +Collapsed=0 + +[Window][Example: Custom rendering] +Pos=60,60 +Size=531,414 +Collapsed=0 + +[Table][0xD181190E,2] +Column 0 Weight=1.0000 +Column 1 Weight=1.0000 + [Docking][Data] -DockSpace ID=0x8B93E3BD Window=0xA787BDB4 Pos=0,19 Size=1024,749 Split=Y +DockSpace ID=0x8B93E3BD Window=0xA787BDB4 Pos=0,19 Size=1920,1010 Split=Y DockNode ID=0x00000003 Parent=0x8B93E3BD SizeRef=1920,960 Split=X DockNode ID=0x00000001 Parent=0x00000003 SizeRef=1647,1010 CentralNode=1 Selected=0x249E7C8C - DockNode ID=0x00000002 Parent=0x00000003 SizeRef=271,1010 Selected=0x6372E3B7 + DockNode ID=0x00000002 Parent=0x00000003 SizeRef=271,1010 Selected=0xBB186BEF DockNode ID=0x00000004 Parent=0x8B93E3BD SizeRef=1920,48 Selected=0x6B041B99 diff --git a/src/Editor/DearImGui/Drawable.cpp b/src/Editor/DearImGui/Drawable.cpp index fba23b7..170f5f9 100644 --- a/src/Editor/DearImGui/Drawable.cpp +++ b/src/Editor/DearImGui/Drawable.cpp @@ -25,58 +25,63 @@ namespace tpne { -#define FILL_COLOR(a) IM_COL32(255, 165, 0, (a)) -#define OUTLINE_COLOR IM_COL32(165, 42, 42, 255) // Arcs, Places, Transitions -#define CRITICAL_COLOR IM_COL32(255, 0, 0, 255) +#define FILL_COLOR(alpha) ImGui::GetColorU32(ImGuiCol_FrameBg, alpha) +#define OUTLINE_COLOR ImGui::GetColorU32(ImGuiCol_FrameBgActive) +#define CAPTION_COLOR ImGui::GetColorU32(ImGuiCol_Text) +#define DURATION_COLOR ImGui::GetColorU32(ImGuiCol_FrameBgActive) +#define TOKEN_COLOR ImGui::GetColorU32(ImGuiCol_Text) +#define CRITICAL_COLOR ImGui::GetColorU32(ImGuiCol_PlotLinesHovered) //------------------------------------------------------------------------------ static void drawArrow(ImDrawList* draw_list, ImVec2 const& A, ImVec2 const& B, const ImU32 color) { + constexpr float pi = 3.14159265358979323846f; + // Orientation - const float teta = (B.y - A.y) / (B.x - A.x); - const float arrowAngle = std::atan(teta); + const float arrowAngle = std::atan((B.y - A.y) / (B.x - A.x)); + // + (B.x < A.x) ? pi : ((B.y < A.y) ? (2.0f * pi) : 0.0f); const float cos_a = std::cos(arrowAngle); const float sin_a = std::sin(arrowAngle); - // Arc magnitude - const float arrowLength = norm(A, B); - + // Tail of the arrow. // Reduce the arrow magnitude to avoid entering in the place and having // a mush of pixels when multiple arrows are pointing on the same // position. To get full scaled arrow comment this block of code and // uncomment A.x, B.x, A.y, B.y and tailSize. - const float r = arrowLength - PLACE_RADIUS; - const float dx = ((B.x - A.x) * r) / arrowLength; - const float dy = ((B.y - A.y) * r) / arrowLength; - const float a1 = B.x - dx; - const float b1 = B.y - dy; - const float a2 = A.x + dx; - const float b2 = A.y + dy; + const float length = norm(A, B); + float r = length - PLACE_RADIUS - ARROW_SPACING; + float dx = ((B.x - A.x) * r) / length; + float dy = ((B.y - A.y) * r) / length; + const ImVec2 from(B.x - dx, B.y - dy); + const ImVec2 to(A.x + dx, A.y + dy); + + // Reduce the head size to avoid overlapping the line and head of the + // arrow. With the transparency this is noticable. + r = length - PLACE_RADIUS - ARROW_WIDTH - ARROW_SPACING; + dx = ((B.x - A.x) * r) / length; + dy = ((B.y - A.y) * r) / length; + const ImVec2 to2(A.x + dx, A.y + dy); + draw_list->AddLine(from, to2, color, 2.0f); // Head of the arrow - const ImVec2 arrowHeadSize(14.0f, 14.0f); + const ImVec2 head(-ARROW_WIDTH, -ARROW_WIDTH / 2.0f); std::vector points = { - ImVec2(a2, b2 /*B.x, B.y*/) + rotate(ImVec2(0.0f, 0.0f), cos_a, sin_a), - ImVec2(a2, b2 /*B.x, B.y*/) + rotate(ImVec2(arrowHeadSize.x, arrowHeadSize.y / 2.0f), cos_a, sin_a), - ImVec2(a2, b2 /*B.x, B.y*/) + rotate(ImVec2(0.0f, arrowHeadSize.y), cos_a, sin_a) + to + rotate(head + ImVec2(0.0f, 0.0f), cos_a, sin_a), + to + rotate(head + ImVec2(ARROW_WIDTH, ARROW_WIDTH / 2.0f), cos_a, sin_a), + to + rotate(head + ImVec2(0.0f, ARROW_WIDTH), cos_a, sin_a) }; draw_list->AddConvexPolyFilled(points.data(), points.size(), color); - - // Tail of the arrow. - //const sf::Vector2f tailSize{ arrowLength - arrowHeadSize.x, 2.f }; - const ImVec2 tailSize(r - arrowHeadSize.x - 15.0f, 2.0f); - draw_list->AddLine(A, B, color, 2.0f); } //------------------------------------------------------------------------------ -void drawArc(ImDrawList* draw_list, Arc const& arc, TypeOfNet const type, ImVec2 const& origin, uint8_t const alpha) +void drawArc(ImDrawList* draw_list, Arc const& arc, TypeOfNet const type, ImVec2 const& origin, float const alpha) { ImU32 color; - if (alpha >= 0u) + if (alpha >= 0.0f) { - color = FILL_COLOR(alpha); + color = OUTLINE_COLOR; // FIXME FILL_COLOR(alpha); } else { @@ -103,7 +108,7 @@ void drawArc(ImDrawList* draw_list, Arc const& arc, TypeOfNet const type, ImVec2 std::stringstream stream; stream << std::fixed << std::setprecision(2) << arc.duration << ", " << arc.to.key << "(" << reinterpret_cast(arc.to).tokens << ")"; - draw_list->AddText(ImVec2(x, y), IM_COL32(0, 0, 0, 255), stream.str().c_str()); + draw_list->AddText(ImVec2(x, y), DURATION_COLOR, stream.str().c_str()); } else { @@ -118,7 +123,7 @@ void drawArc(ImDrawList* draw_list, Arc const& arc, TypeOfNet const type, ImVec2 float y = origin.y + arc.from.y + (arc.to.y - arc.from.y) / 2.0f - 15.0f; std::stringstream stream; stream << std::fixed << std::setprecision(2) << arc.duration; - draw_list->AddText(ImVec2(x, y), IM_COL32(0, 0, 0, 255), stream.str().c_str()); + draw_list->AddText(ImVec2(x, y), DURATION_COLOR, stream.str().c_str()); } } } @@ -126,12 +131,12 @@ void drawArc(ImDrawList* draw_list, Arc const& arc, TypeOfNet const type, ImVec2 //------------------------------------------------------------------------------ void drawToken(ImDrawList* draw_list, float const x, float const y) { - draw_list->AddCircleFilled(ImVec2(x, y), TOKEN_RADIUS, IM_COL32(0, 0, 0, 255)); + draw_list->AddCircleFilled(ImVec2(x, y), TOKEN_RADIUS, TOKEN_COLOR); } // TODO a virer en utilisant un wrapper RenderablePlace avec fading (alpha) //------------------------------------------------------------------------------ -void drawPlace(ImDrawList* draw_list, Place const& place, TypeOfNet const type, ImVec2 const& origin, bool const show_caption, uint8_t const alpha) +void drawPlace(ImDrawList* draw_list, Place const& place, TypeOfNet const type, ImVec2 const& origin, bool const show_caption, float const alpha) { // In graph event we "compress" the graph by not displaying places. if (type == TypeOfNet::TimedEventGraph) @@ -142,12 +147,12 @@ void drawPlace(ImDrawList* draw_list, Place const& place, TypeOfNet const type, // Draw the place draw_list->AddCircleFilled(p, PLACE_RADIUS, FILL_COLOR(alpha), 64); - draw_list->AddCircle(p, PLACE_RADIUS, OUTLINE_COLOR, 64); + draw_list->AddCircle(p, PLACE_RADIUS, OUTLINE_COLOR, 64, 2.5f); // Draw the caption ImVec2 dim = ImGui::CalcTextSize(place.key.c_str()); ImVec2 ptext = p - ImVec2(dim.x / 2.0f, PLACE_RADIUS + dim.y); - draw_list->AddText(ptext, IM_COL32(0, 0, 0, 255), + draw_list->AddText(ptext, CAPTION_COLOR, show_caption ? place.caption.c_str() : place.key.c_str()); // Draw the number of tokens @@ -188,12 +193,12 @@ void drawPlace(ImDrawList* draw_list, Place const& place, TypeOfNet const type, else { std::string tokens = std::to_string(place.tokens); - draw_list->AddText(ImVec2(p.x, p.y), IM_COL32(0, 0, 0, 255), tokens.c_str()); + draw_list->AddText(ImVec2(p.x, p.y), CAPTION_COLOR, tokens.c_str()); } } //------------------------------------------------------------------------------ -void drawTransition(ImDrawList* draw_list, Transition const& transition, TypeOfNet const type, ImVec2 const& origin, bool const show_caption, uint8_t const alpha) +void drawTransition(ImDrawList* draw_list, Transition const& transition, TypeOfNet const type, ImVec2 const& origin, bool const show_caption, float const alpha) { //const uint8_t alpha = 255; // TODO m_fading[place.key] const ImVec2 p = origin + ImVec2(transition.x, transition.y); @@ -214,12 +219,12 @@ void drawTransition(ImDrawList* draw_list, Transition const& transition, TypeOfN const ImVec2 pmin(p.x - TRANS_WIDTH / 2.0f, p.y - TRANS_HEIGHT / 2.0f); const ImVec2 pmax(p.x + TRANS_WIDTH / 2.0f, p.y + TRANS_HEIGHT / 2.0f); draw_list->AddRectFilled(pmin, pmax, color); - draw_list->AddRect(pmin, pmax, OUTLINE_COLOR, 1.0f, ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersBottomRight, 1.0f); + draw_list->AddRect(pmin, pmax, OUTLINE_COLOR, 0.0f, ImDrawFlags_None, 2.5f); // Draw the caption ImVec2 dim = ImGui::CalcTextSize(transition.key.c_str()); - ImVec2 ptext = p - ImVec2(dim.x / 2.0f, TRANS_HEIGHT + dim.y); - draw_list->AddText(ptext, IM_COL32(0, 0, 0, 255), + ImVec2 ptext = p - ImVec2(dim.x / 2.0f, TRANS_HEIGHT / 2.0f + dim.y); + draw_list->AddText(ptext, CAPTION_COLOR, show_caption ? transition.caption.c_str() : transition.key.c_str()); } diff --git a/src/Editor/DearImGui/Drawable.hpp b/src/Editor/DearImGui/Drawable.hpp index bf172d4..3a4ea5e 100644 --- a/src/Editor/DearImGui/Drawable.hpp +++ b/src/Editor/DearImGui/Drawable.hpp @@ -26,15 +26,18 @@ namespace tpne { +static const float ARROW_WIDTH = 14.0f; +static const float ARROW_SPACING = 10.0f; static const float TRANS_WIDTH = 36.0f; // Rectangle width for rendering Transitions -static const float TRANS_HEIGHT = TRANS_WIDTH / 2.0f; // Rectangle height for rendering Transitions +static const float TRANS_HEIGHT = TRANS_WIDTH / 1.0f; // Rectangle height for rendering Transitions static const float PLACE_RADIUS = TRANS_WIDTH / 2.0f; // Circle radius for rendering Places static const float TOKEN_RADIUS = 2.0f; // Circle radius for rendering tokens -void drawArc(ImDrawList* draw_list, Arc const& arc, TypeOfNet const type, ImVec2 const& origin, uint8_t const alpha); + +void drawArc(ImDrawList* draw_list, Arc const& arc, TypeOfNet const type, ImVec2 const& origin, float const alpha); void drawToken(ImDrawList* draw_list, float const x, float const y); -void drawPlace(ImDrawList* draw_list, Place const& place, TypeOfNet const type, ImVec2 const& origin, bool const show_caption, uint8_t const alpha); -void drawTransition(ImDrawList* draw_list, Transition const& transition, TypeOfNet const type, ImVec2 const& origin, bool const show_caption, uint8_t const alpha); +void drawPlace(ImDrawList* draw_list, Place const& place, TypeOfNet const type, ImVec2 const& origin, bool const show_caption, float const alpha); +void drawTransition(ImDrawList* draw_list, Transition const& transition, TypeOfNet const type, ImVec2 const& origin, bool const show_caption, float const alpha); } // namespace tpne diff --git a/src/Editor/PetriEditor.cpp b/src/Editor/PetriEditor.cpp index be339ae..5aea80d 100644 --- a/src/Editor/PetriEditor.cpp +++ b/src/Editor/PetriEditor.cpp @@ -105,6 +105,7 @@ void Editor::onDraw() messagebox(); inspector(); view(); + ImGui::ShowDemoWindow(); } //------------------------------------------------------------------------------ @@ -126,6 +127,7 @@ void Editor::view() //------------------------------------------------------------------------------ void Editor::close() { + // TODO } //------------------------------------------------------------------------------ @@ -135,36 +137,50 @@ void Editor::menu() { if (ImGui::BeginMenu("File")) { - if (ImGui::MenuItem("New", nullptr, false)) { + if (ImGui::MenuItem("New", nullptr, false)) + { // TODO } ImGui::Separator(); - if (ImGui::MenuItem("Open", nullptr, false)) { + if (ImGui::MenuItem("Open", nullptr, false)) + { m_states.do_load = true; } - if (ImGui::BeginMenu("Import")) { - // TODO + if (ImGui::BeginMenu("Import")) + { + for (auto const& it: m_m_importers) + { + if (ImGui::MenuItem(it.format.c_str(), nullptr, false)) + { + m_states.do_import_to = ⁢ + } + } ImGui::EndMenu(); } ImGui::Separator(); if (ImGui::MenuItem("Save", nullptr, false)) { - if (m_net.filename() == "") { + if (m_net.filename() == "") + { m_states.do_save_as = true; - } else { + } + else + { m_net.saveAs(m_net.filename()); } } - if (ImGui::MenuItem("Save As", nullptr, false)) { + if (ImGui::MenuItem("Save As", nullptr, false)) + { m_states.do_save_as = true; } if (ImGui::BeginMenu("Export to")) { for (auto const& it: m_exporters) { - if (ImGui::MenuItem(it.format.c_str(), nullptr, false)) { + if (ImGui::MenuItem(it.format.c_str(), nullptr, false)) + { m_states.do_export_to = ⁢ } } @@ -172,7 +188,8 @@ void Editor::menu() } ImGui::Separator(); - if (ImGui::MenuItem("Exit", nullptr, false)) { + if (ImGui::MenuItem("Exit", nullptr, false)) + { this->close(); } ImGui::EndMenu(); @@ -191,20 +208,27 @@ void Editor::menu() } ImGui::Separator(); - if (ImGui::MenuItem("Clear net", nullptr, false)) { + if (ImGui::MenuItem("Clear net", nullptr, false)) + { clearNet(); } - if (ImGui::MenuItem("Align nodes", nullptr, false)) { + if (ImGui::MenuItem("Align nodes", nullptr, false)) + { //editor.align(); } - if (ImGui::MenuItem("Show grid", nullptr, false)) { + if (ImGui::MenuItem("Show grid", nullptr, false)) + { m_view.grid.show ^= true; } - if (ImGui::MenuItem("Take screenshot", nullptr, false)) { + if (ImGui::MenuItem("Take screenshot", nullptr, false)) + { m_states.do_screenshot = true; } ImGui::Separator(); - if (ImGui::MenuItem(m_simulation.running ? "Stop simulation" : "Start simulation", nullptr, false)) { + if (ImGui::MenuItem(m_simulation.running + ? "Stop simulation" + : "Start simulation", nullptr, false)) + { toogleStartSimulation(); } ImGui::EndMenu(); @@ -214,19 +238,24 @@ void Editor::menu() { if (ImGui::BeginMenu("Graph Events")) { - if (ImGui::MenuItem("Show critical circuit", nullptr, false)) { + if (ImGui::MenuItem("Show critical circuit", nullptr, false)) + { m_states.do_find_critical_cycle = true; } - if (ImGui::MenuItem("Show (max, +) dynamic linear system", nullptr, false)) { + if (ImGui::MenuItem("Show (max, +) dynamic linear system", nullptr, false)) + { m_states.do_syslin = true; } - if (ImGui::MenuItem("Show Dater equation", nullptr, false)) { + if (ImGui::MenuItem("Show Dater equation", nullptr, false)) + { m_states.do_dater = true; } - if (ImGui::MenuItem("Show Counter equation", nullptr, false)) { + if (ImGui::MenuItem("Show Counter equation", nullptr, false)) + { m_states.do_counter = true; } - if (ImGui::MenuItem("Show adjacency matrices", nullptr, false)) { + if (ImGui::MenuItem("Show adjacency matrices", nullptr, false)) + { m_states.do_adjency = true; } ImGui::EndMenu(); @@ -234,11 +263,13 @@ void Editor::menu() } if (ImGui::BeginMenu("Help")) { - if (ImGui::MenuItem("Help", nullptr, false)) { + if (ImGui::MenuItem("Help", nullptr, false)) + { m_states.show_help = true; } ImGui::Separator(); - if (ImGui::MenuItem("About", nullptr, false)) { + if (ImGui::MenuItem("About", nullptr, false)) + { m_states.show_about = true; } ImGui::EndMenu(); @@ -253,6 +284,7 @@ void Editor::menu() if (m_states.do_load) { loadNetFile(); } if (m_states.do_save_as) { saveNetAs(); } if (m_states.do_export_to != nullptr) { exportNetTo(*m_states.do_export_to); } + if (m_states.do_import_to != nullptr) { importNetTo(*m_states.do_import_to); } if (m_states.do_screenshot) { takeScreenshot(); } if (m_states.do_adjency) { showAdjacencyMatrices(); } if (m_states.do_counter || m_states.do_dater) { showCounterOrDaterequation(); } @@ -264,9 +296,10 @@ void Editor::menu() void Editor::showAdjacencyMatrices() const { ImGui::OpenPopup("Show adjacency matrices"); - ImGui::SetNextWindowPos(m_states.viewport_center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); + ImGui::SetNextWindowPos(m_states.viewport_center, ImGuiCond_Appearing, + ImVec2(0.5f, 0.5f)); if (ImGui::BeginPopupModal("Show adjacency matrices", - NULL, ImGuiWindowFlags_AlwaysAutoResize)) + NULL, ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); ImGui::Checkbox("Dense matrix", &SparseMatrix::display_as_dense); @@ -308,14 +341,17 @@ void Editor::showCounterOrDaterequation() const { const char* title = m_states.do_counter ? "Counter Equation": "Dater Equation"; ImGui::OpenPopup(title); - ImGui::SetNextWindowPos(m_states.viewport_center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); + ImGui::SetNextWindowPos(m_states.viewport_center, ImGuiCond_Appearing, + ImVec2(0.5f, 0.5f)); if (ImGui::BeginPopupModal(title, NULL, ImGuiWindowFlags_AlwaysAutoResize)) { static bool use_caption = false; static bool maxplus_notation = false; static bool show_matrix = false; ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); - ImGui::Checkbox(m_states.do_counter ? "Use (min,+) operator" : "Use (max,+) operator", &maxplus_notation); + ImGui::Checkbox(m_states.do_counter + ? "Use (min,+) operator" + : "Use (max,+) operator", &maxplus_notation); ImGui::SameLine(); ImGui::Checkbox("Use caption", &use_caption); ImGui::PopStyleVar(); @@ -324,12 +360,12 @@ void Editor::showCounterOrDaterequation() const if (m_states.do_counter) { ImGui::Text("%s", showCounterEquation( - m_net, "", use_caption, maxplus_notation).str().c_str()); + m_net, "", use_caption, maxplus_notation).str().c_str()); } else { ImGui::Text("%s", showDaterEquation( - m_net, "", use_caption, maxplus_notation).str().c_str()); + m_net, "", use_caption, maxplus_notation).str().c_str()); } if (ImGui::Button("OK", ImVec2(120, 0))) @@ -345,14 +381,17 @@ void Editor::showCounterOrDaterequation() const void Editor::showDynamicLinearSystem() const { ImGui::OpenPopup("(max, +) dynamic linear system"); - ImGui::SetNextWindowPos(m_states.viewport_center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); - if (ImGui::BeginPopupModal("(max, +) dynamic linear system", NULL, ImGuiWindowFlags_AlwaysAutoResize)) + ImGui::SetNextWindowPos(m_states.viewport_center, ImGuiCond_Appearing, + ImVec2(0.5f, 0.5f)); + if (ImGui::BeginPopupModal("(max, +) dynamic linear system", NULL, + ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); ImGui::Checkbox("Dense matrix", &SparseMatrix::display_as_dense); ImGui::PopStyleVar(); - SparseMatrix D; SparseMatrix A; SparseMatrix B; SparseMatrix C; + SparseMatrix D; SparseMatrix A; + SparseMatrix B; SparseMatrix C; toSysLin(m_net, D, A, B, C); SparseMatrix::display_for_julia = false; ImGui::Text(u8"%s", "X(n) = D . X(n) ⨁ A . X(n-1) ⨁ B . U(n)\nY(n) = C . X(n)"); @@ -399,7 +438,8 @@ void Editor::showDynamicLinearSystem() const void Editor::showCriticalCycles() const { ImGui::OpenPopup("Critical Cycle"); - ImGui::SetNextWindowPos(m_states.viewport_center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); + ImGui::SetNextWindowPos(m_states.viewport_center, ImGuiCond_Appearing, + ImVec2(0.5f, 0.5f)); if (ImGui::BeginPopupModal("Critical Cycle", NULL, ImGuiWindowFlags_AlwaysAutoResize)) { CriticalCycleResult critical_cycle = findCriticalCycle(m_net); @@ -431,7 +471,9 @@ void Editor::showCriticalCycles() const // Show transitions and places for (auto const& it: critical_cycle.arcs) { - txt << it->from.key << " -> " << it->to.key << std::endl; + txt << it->from.key << " -> " + << it->to.key + << std::endl; } } ImGui::Text("%s", txt.str().c_str()); @@ -479,7 +521,8 @@ void Editor::about() const ImVec2 center = ImGui::GetMainViewport()->GetCenter(); ImGui::OpenPopup("About TimedPetriNetEditor"); ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); - if (ImGui::BeginPopupModal("About TimedPetriNetEditor", NULL, ImGuiWindowFlags_AlwaysAutoResize)) + if (ImGui::BeginPopupModal("About TimedPetriNetEditor", NULL, + ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::Text("A timed Petri net and graph event editor and"); ImGui::Text("simulator combined to (max,+) algebra with"); @@ -518,7 +561,8 @@ void Editor::help() const ImVec2 center = ImGui::GetMainViewport()->GetCenter(); ImGui::OpenPopup("Help TimedPetriNetEditor"); ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); - if (ImGui::BeginPopupModal("Help TimedPetriNetEditor", NULL, ImGuiWindowFlags_AlwaysAutoResize)) + if (ImGui::BeginPopupModal("Help TimedPetriNetEditor", NULL, + ImGuiWindowFlags_AlwaysAutoResize)) { ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_None; if (ImGui::BeginTabBar("help", tab_bar_flags)) @@ -611,14 +655,15 @@ void Editor::inspector() { // Do not allow editing when running simulation const auto readonly = m_simulation.running ? - ImGuiInputTextFlags_ReadOnly : ImGuiInputTextFlags_None; + ImGuiInputTextFlags_ReadOnly : ImGuiInputTextFlags_None; // Place captions and tokens { ImGui::Begin("Places"); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); ImGui::Checkbox(m_states.show_place_captions ? - "Show place identifiers" : "Show place captions", &m_states.show_place_captions); + "Show place identifiers" : "Show place captions", + &m_states.show_place_captions); ImGui::PopStyleVar(); ImGui::Separator(); @@ -635,8 +680,8 @@ void Editor::inspector() for (auto& place: m_net.places()) { inputInteger(m_states.show_place_captions ? - place.caption.c_str() : place.key.c_str(), - Net::Settings::maxTokens, place.tokens); + place.caption.c_str() : place.key.c_str(), + Net::Settings::maxTokens, place.tokens); } ImGui::End(); @@ -647,9 +692,10 @@ void Editor::inspector() // FIXME parse and clear sensors if and only if we modified entrytext ImGui::Begin("Transitions"); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); - ImGui::Checkbox(m_states.show_transition_captions ? - "Show transition identifiers" : - "Show transition captions", &m_states.show_transition_captions); + ImGui::Checkbox(m_states.show_transition_captions + ? "Show transition identifiers" + : "Show transition captions", + &m_states.show_transition_captions); ImGui::PopStyleVar(); ImGui::Separator(); //if (!editor.m_simulation.running) @@ -725,23 +771,24 @@ bool Editor::switchOfNet(TypeOfNet const type) } //------------------------------------------------------------------------------ -Node* Editor::getNode(float const x, float const y) +Node* Editor::getNode(ImVec2 const& position) { - Node *n = getPlace(x, y); + Node *n = getPlace(position); if (n != nullptr) return n; - return getTransition(x, y); + return getTransition(position); } //------------------------------------------------------------------------------ -Place* Editor::getPlace(float const x, float const y) +Place* Editor::getPlace(ImVec2 const& position) { - for (auto &p : m_net.places()) + for (auto &place : m_net.places()) { - float d2 = (p.x - x) * (p.x - x) + (p.y - y) * (p.y - y); + float d2 = (place.x - position.x) * (place.x - position.x) + + (place.y - position.y) * (place.y - position.y); if (d2 < PLACE_RADIUS * PLACE_RADIUS) { - return &p; + return &place; } } @@ -749,14 +796,15 @@ Place* Editor::getPlace(float const x, float const y) } //------------------------------------------------------------------------------ -Transition* Editor::getTransition(float const x, float const y) +Transition* Editor::getTransition(ImVec2 const& position) { - for (auto &t : m_net.transitions()) + for (auto &transition : m_net.transitions()) { - float d2 = (t.x - x) * (t.x - x) + (t.y - y) * (t.y - y); + float d2 = (transition.x - position.x) * (transition.x - position.x) + + (transition.y - position.y) * (transition.y - position.y); if (d2 < TRANS_WIDTH * TRANS_WIDTH) { - return &t; + return &transition; } } @@ -764,7 +812,7 @@ Transition* Editor::getTransition(float const x, float const y) } //------------------------------------------------------------------------------ -void Editor::loadNetFile() +void Editor::importNetTo(Importer const& importer) { if (m_simulation.running) { @@ -772,10 +820,11 @@ void Editor::loadNetFile() return ; } - ImGuiFileDialog::Instance()->OpenDialog("ChooseFileDlgKey", - "Choose the Petri file to load", - ".json", ".", 1, nullptr, - ImGuiFileDialogFlags_Modal); + ImGuiFileDialog::Instance()->OpenDialog( + "ChooseFileDlgKey", + "Choose the Petri file to load", + importer.extensions.c_str(), ".", 1, nullptr, + ImGuiFileDialogFlags_Modal); if (ImGuiFileDialog::Instance()->Display("ChooseFileDlgKey")) { @@ -791,10 +840,6 @@ void Editor::loadNetFile() m_messages.setError(m_net.error()); } } - //else - //{ - // m_messages.setError("No selected file for loading"); - //} // close m_states.do_load = false; @@ -802,6 +847,13 @@ void Editor::loadNetFile() } } +//------------------------------------------------------------------------------ +void Editor::loadNetFile() +{ + static Importer importer{"TimedPetriNetEditor", ".json", importFromJSON}; + importNetTo(importer); +} + //------------------------------------------------------------------------------ void Editor::exportNetTo(Exporter const& exporter) { @@ -811,10 +863,11 @@ void Editor::exportNetTo(Exporter const& exporter) return ; } - ImGuiFileDialog::Instance()->OpenDialog("ChooseFileDlgKey", - "Choose the Petri file to save", - exporter.extensions.c_str(), ".", 1, nullptr, // nullptr, m_net.filename() + ".json", 1, nullptr, - ImGuiFileDialogFlags_Modal | ImGuiFileDialogFlags_ConfirmOverwrite); + ImGuiFileDialog::Instance()->OpenDialog( + "ChooseFileDlgKey", + "Choose the Petri file to save", + exporter.extensions.c_str(), ".", 1, nullptr, + ImGuiFileDialogFlags_Modal | ImGuiFileDialogFlags_ConfirmOverwrite); if (ImGuiFileDialog::Instance()->Display("ChooseFileDlgKey")) { @@ -839,21 +892,22 @@ void Editor::exportNetTo(Exporter const& exporter) } } -//------------------------------------------------------------------------------ +//-------------------------------------------------------------------------- void Editor::saveNetAs() { static Exporter exporter{"TimedPetriNetEditor", ".json", exportToJSON}; exportNetTo(exporter); } -//------------------------------------------------------------------------------ +//-------------------------------------------------------------------------- void Editor::takeScreenshot() { std::string path; - ImGuiFileDialog::Instance()->OpenDialog("ChooseFileDlgKey", - "Choose the PNG file to save the screenshot", - ".png", ".", 1, nullptr, - ImGuiFileDialogFlags_Modal | ImGuiFileDialogFlags_ConfirmOverwrite); + ImGuiFileDialog::Instance()->OpenDialog( + "ChooseFileDlgKey", + "Choose the PNG file to save the screenshot", + ".png", ".", 1, nullptr, + ImGuiFileDialogFlags_Modal | ImGuiFileDialogFlags_ConfirmOverwrite); if (ImGuiFileDialog::Instance()->Display("ChooseFileDlgKey")) { @@ -866,7 +920,8 @@ void Editor::takeScreenshot() } else { - m_messages.setError("Failed to save screenshot to file '" + path + "'"); + m_messages.setError("Failed to save screenshot to file '" + + path + "'"); } } @@ -876,15 +931,14 @@ void Editor::takeScreenshot() } } -//------------------------------------------------------------------------------ +//-------------------------------------------------------------------------- void Editor::clearNet() { m_simulation.running = false; m_net.clear(m_net.type()); - //TODO m_animated_tokens.clear(); } -//------------------------------------------------------------------------------ +//-------------------------------------------------------------------------- std::string Editor::getError() const { if (m_messages.getMessages().empty()) @@ -892,24 +946,24 @@ std::string Editor::getError() const return m_messages.getMessage().message; } -//------------------------------------------------------------------------------ +//-------------------------------------------------------------------------- std::vector const& Editor::getLogs() const { return m_messages.getMessages(); } -//------------------------------------------------------------------------------ +//-------------------------------------------------------------------------- void Editor::clearLogs() { m_messages.clear(); } -//------------------------------------------------------------------------------ +//-------------------------------------------------------------------------- Editor::PetriView::PetriView(Editor& editor) : m_editor(editor) {} -//------------------------------------------------------------------------------ +//-------------------------------------------------------------------------- void Editor::PetriView::Canvas::push() { draw_list = ImGui::GetWindowDrawList(); @@ -917,13 +971,13 @@ void Editor::PetriView::Canvas::push() draw_list->PushClipRect(corners[0], corners[1], true); } -//------------------------------------------------------------------------------ +//-------------------------------------------------------------------------- void Editor::PetriView::Canvas::pop() { draw_list->PopClipRect(); } -//------------------------------------------------------------------------------ +//-------------------------------------------------------------------------- void Editor::PetriView::Canvas::reshape() { // ImDrawList API uses screen coordinates! @@ -939,25 +993,27 @@ void Editor::PetriView::Canvas::reshape() origin = corners[0] + scrolling; } -//------------------------------------------------------------------------------ +//-------------------------------------------------------------------------- void Editor::PetriView::reshape() { m_canvas.reshape(); } -//------------------------------------------------------------------------------ +//-------------------------------------------------------------------------- ImVec2 Editor::PetriView::Canvas::getMousePosition() { ImGuiIO &io = ImGui::GetIO(); return io.MousePos - origin; } -//------------------------------------------------------------------------------ +//-------------------------------------------------------------------------- bool Editor::PetriView::isMouseReleased(ImGuiMouseButton& key) { ImGuiIO &io = ImGui::GetIO(); - if ((io.MousePos.x >= m_canvas.corners[0].x) && (io.MousePos.x <= m_canvas.corners[1].x) && - (io.MousePos.y >= m_canvas.corners[0].y) && (io.MousePos.y <= m_canvas.corners[1].y)) + if ((io.MousePos.x >= m_canvas.corners[0].x) && + (io.MousePos.x <= m_canvas.corners[1].x) && + (io.MousePos.y >= m_canvas.corners[0].y) && + (io.MousePos.y <= m_canvas.corners[1].y)) { if (ImGui::IsMouseReleased(ImGuiMouseButton_Middle)) { @@ -978,17 +1034,19 @@ bool Editor::PetriView::isMouseReleased(ImGuiMouseButton& key) return false; } -//------------------------------------------------------------------------------ +//-------------------------------------------------------------------------- bool Editor::PetriView::isMouseClicked(ImGuiMouseButton& key, bool& dragging) { ImGuiIO &io = ImGui::GetIO(); - if ((io.MousePos.x >= m_canvas.corners[0].x) && (io.MousePos.x <= m_canvas.corners[1].x) && - (io.MousePos.y >= m_canvas.corners[0].y) && (io.MousePos.y <= m_canvas.corners[1].y)) + if ((io.MousePos.x >= m_canvas.corners[0].x) && + (io.MousePos.x <= m_canvas.corners[1].x) && + (io.MousePos.y >= m_canvas.corners[0].y) && + (io.MousePos.y <= m_canvas.corners[1].y)) { if (ImGui::IsMouseClicked(ImGuiMouseButton_Middle)) { key = ImGuiMouseButton_Middle; - if (m_editor.getNode(m_mouse.position.x, m_mouse.position.y) != nullptr) + if (m_editor.getNode(m_mouse.position) != nullptr) m_mouse.disable_dragging = true; dragging = false; return true; @@ -1009,7 +1067,8 @@ bool Editor::PetriView::isMouseClicked(ImGuiMouseButton& key, bool& dragging) const float mouse_threshold_for_pan = grid.menu ? -1.0f : 0.0f; if (!m_mouse.disable_dragging) { - if (ImGui::IsMouseDragging(ImGuiMouseButton_Middle, mouse_threshold_for_pan)) + if (ImGui::IsMouseDragging(ImGuiMouseButton_Middle, + mouse_threshold_for_pan)) { key = ImGuiMouseButton_Middle; dragging = true; @@ -1021,14 +1080,14 @@ bool Editor::PetriView::isMouseClicked(ImGuiMouseButton& key, bool& dragging) return false; } -//------------------------------------------------------------------------------ +//-------------------------------------------------------------------------- void Editor::PetriView::handleArcOrigin() { // TODO m_marked_arcs.clear(); m_mouse.selection.clear(); // Get a place or a transition from the mouse cursor - m_mouse.from = m_editor.getNode(m_mouse.position.x, m_mouse.position.y); + m_mouse.from = m_editor.getNode(m_mouse.position); if (m_mouse.from == nullptr) { if ((m_editor.m_net.places().size() != 0u) || @@ -1045,12 +1104,12 @@ void Editor::PetriView::handleArcOrigin() m_mouse.to = nullptr; } -//------------------------------------------------------------------------------ +//-------------------------------------------------------------------------- void Editor::PetriView::handleMoveNode() { if (m_mouse.selection.size() == 0u) { - Node* node = m_editor.getNode(m_mouse.position.x, m_mouse.position.y); + Node* node = m_editor.getNode(m_mouse.position); if (node != nullptr) { m_mouse.selection.push_back(node); @@ -1063,7 +1122,7 @@ void Editor::PetriView::handleMoveNode() } } -//------------------------------------------------------------------------------ +//-------------------------------------------------------------------------- void Editor::PetriView::handleAddNode(ImGuiMouseButton button) { // TODO m_marked_arcs.clear(); @@ -1072,18 +1131,24 @@ void Editor::PetriView::handleAddNode(ImGuiMouseButton button) { // Add a new Place or a new Transition only if a node is not already // present. - if (m_editor.getNode(m_mouse.position.x, m_mouse.position.y) == nullptr) + if (m_editor.getNode(m_mouse.position) == nullptr) { if (button == ImGuiMouseButton_Left) - m_editor.m_net.addPlace(m_mouse.position.x, m_mouse.position.y); + { + m_editor.m_net.addPlace(m_mouse.position.x, + m_mouse.position.y); + } else if (button == ImGuiMouseButton_Right) - m_editor.m_net.addTransition(m_mouse.position.x, m_mouse.position.y); + { + m_editor.m_net.addTransition(m_mouse.position.x, + m_mouse.position.y); + } } } else if (m_editor.m_net.type() == TypeOfNet::PetriNet) { // Click to fire a transition - Transition* transition = m_editor.getTransition(m_mouse.position.x, m_mouse.position.y); + Transition* transition = m_editor.getTransition(m_mouse.position); if (transition != nullptr) { transition->receptivity ^= true; @@ -1091,11 +1156,11 @@ void Editor::PetriView::handleAddNode(ImGuiMouseButton button) } } -//------------------------------------------------------------------------------ +//-------------------------------------------------------------------------- void Editor::PetriView::handleArcDestination() { // Finish the creation of the arc (destination node) from the mouse cursor - m_mouse.to = m_editor.getNode(m_mouse.position.x, m_mouse.position.y); + m_mouse.to = m_editor.getNode(m_mouse.position); // The user grab no nodes: abort if ((m_mouse.from == nullptr) && (m_mouse.to == nullptr)) @@ -1145,9 +1210,15 @@ void Editor::PetriView::handleArcDestination() if (m_mouse.arc_from_unknown_node) { if (m_mouse.to->type == Node::Type::Place) - m_mouse.from = &m_editor.m_net.addTransition(m_mouse.click_position.x, m_mouse.click_position.y); + { + m_mouse.from = &m_editor.m_net.addTransition( + m_mouse.click_position.x, m_mouse.click_position.y); + } else - m_mouse.from = &m_editor.m_net.addPlace(m_mouse.click_position.x, m_mouse.click_position.y); + { + m_mouse.from = &m_editor.m_net.addPlace( + m_mouse.click_position.x, m_mouse.click_position.y); + } } } } @@ -1171,9 +1242,13 @@ void Editor::PetriView::handleArcDestination() m_mouse.from = &n; } if (m_mouse.from->type == Node::Type::Place) + { m_mouse.to = &m_editor.m_net.addTransition(x, y); + } else + { m_mouse.to = &m_editor.m_net.addPlace(x, y); + } } // Create the arc. Note: the duration value is only used // for arc Transition --> Place. @@ -1189,7 +1264,7 @@ void Editor::PetriView::handleArcDestination() m_mouse.arc_from_unknown_node = false; } -//------------------------------------------------------------------------------ +//-------------------------------------------------------------------------- void Editor::PetriView::onHandleInput() { // This will catch our interactions @@ -1204,13 +1279,16 @@ void Editor::PetriView::onHandleInput() { if (isMouseClicked(button, m_mouse.is_dragging)) { - // The 'M' key was pressed. Reset the state but do not add new node! + // The 'M' key was pressed. + // Reset the state but do not add new node! if (m_mouse.selection.size() != 0u) { m_mouse.from = m_mouse.to = nullptr; m_mouse.selection.clear(); if (button == ImGuiMouseButton_Middle) + { return; + } } if (m_mouse.is_dragging) @@ -1267,29 +1345,41 @@ void Editor::PetriView::onHandleInput() } } -//------------------------------------------------------------------------------ +//-------------------------------------------------------------------------- void Editor::PetriView::drawPetriNet(Net& net, Simulation& simulation) { - const uint8_t alpha = 255; // FIXME + const float alpha = 1.0f; // FIXME m_canvas.push(); // Draw the grid - ImU32 color = simulation.running ? IM_COL32(0, 255, 0, 255) : IM_COL32(255, 255, 255, 255); + ImU32 color = simulation.running + ? IM_COL32(0, 255, 0, 255) + : IM_COL32(255, 255, 255, 255); drawGrid(m_canvas.draw_list, color); // Draw the Petri net ImVec2 const& origin = m_canvas.origin; for (auto const& it: net.arcs()) + { drawArc(m_canvas.draw_list, it, net.type(), origin, alpha); + } for (auto const& it: net.places()) - drawPlace(m_canvas.draw_list, it, net.type(), origin, m_editor.m_states.show_place_captions, alpha); + { + drawPlace(m_canvas.draw_list, it, net.type(), origin, + m_editor.m_states.show_place_captions, alpha); + } for (auto const& it: net.transitions()) - drawTransition(m_canvas.draw_list, it, net.type(), origin, m_editor.m_states.show_transition_captions, alpha); + { + drawTransition(m_canvas.draw_list, it, net.type(), origin, + m_editor.m_states.show_transition_captions, alpha); + } // Draw all tokens transiting from Transitions to Places for (auto const& it: simulation.timedTokens()) + { drawToken(m_canvas.draw_list, origin.x + it.x, origin.y + it.y); + } // Update node positions the user is currently moving for (auto& it: m_mouse.selection) @@ -1305,28 +1395,33 @@ void Editor::PetriView::drawPetriNet(Net& net, Simulation& simulation) m_canvas.pop(); } -//------------------------------------------------------------------------------ +//-------------------------------------------------------------------------- void Editor::PetriView::drawGrid(ImDrawList* draw_list, ImU32 const& color) { draw_list->ChannelsSetCurrent(0); // Background - draw_list->AddRectFilled(m_canvas.corners[0], m_canvas.corners[1], IM_COL32(50, 50, 50, 255)); + draw_list->AddRectFilled(m_canvas.corners[0], m_canvas.corners[1], + IM_COL32(50, 50, 50, 255)); draw_list->AddRect(m_canvas.corners[0], m_canvas.corners[1], color); if (!grid.show) return ; - for (float x = fmodf(m_canvas.scrolling.x, grid.step); x < m_canvas.size.x; x += grid.step) + for (float x = fmodf(m_canvas.scrolling.x, grid.step); + x < m_canvas.size.x; x += grid.step) { - draw_list->AddLine(ImVec2(m_canvas.corners[0].x + x, m_canvas.corners[0].y), - ImVec2(m_canvas.corners[0].x + x, m_canvas.corners[1].y), - IM_COL32(200, 200, 200, 40)); + draw_list->AddLine( + ImVec2(m_canvas.corners[0].x + x, m_canvas.corners[0].y), + ImVec2(m_canvas.corners[0].x + x, m_canvas.corners[1].y), + IM_COL32(200, 200, 200, 40)); } - for (float y = fmodf(m_canvas.scrolling.y, grid.step); y < m_canvas.size.y; y += grid.step) + for (float y = fmodf(m_canvas.scrolling.y, grid.step); + y < m_canvas.size.y; y += grid.step) { - draw_list->AddLine(ImVec2(m_canvas.corners[0].x, m_canvas.corners[0].y + y), - ImVec2(m_canvas.corners[1].x, m_canvas.corners[0].y + y), - IM_COL32(200, 200, 200, 40)); + draw_list->AddLine( + ImVec2(m_canvas.corners[0].x, m_canvas.corners[0].y + y), + ImVec2(m_canvas.corners[1].x, m_canvas.corners[0].y + y), + IM_COL32(200, 200, 200, 40)); } } diff --git a/src/Editor/PetriEditor.hpp b/src/Editor/PetriEditor.hpp index eb6ca59..b15ceed 100644 --- a/src/Editor/PetriEditor.hpp +++ b/src/Editor/PetriEditor.hpp @@ -30,15 +30,14 @@ namespace tpne { // **************************************************************************** -//! \brief Graphical representation and manipulation of the Petri net using the -//! SFML library for the rendering. +//! \brief Graphical User interface for manipulating and simulating Petri net. // **************************************************************************** class Editor: public Application { public: //------------------------------------------------------------------------- - //! \brief Constructor. + //! \brief Constructor. No additional actions are made here. //------------------------------------------------------------------------- Editor(size_t const width, size_t const height, std::string const& title); @@ -74,12 +73,13 @@ class Editor: public Application private: // Petri net services - Node* getNode(float const x, float const y); - Place* getPlace(float const x, float const y); - Transition* getTransition(float const x, float const y); + Node* getNode(ImVec2 const& position); + Place* getPlace(ImVec2 const& position); + Transition* getTransition(ImVec2 const& position); bool switchOfNet(TypeOfNet const type); - void loadNetFile(); void exportNetTo(Exporter const& exporter); + void importNetTo(Importer const& importer); + void loadNetFile(); void saveNetAs(); void closeNet(); void toogleStartSimulation(); @@ -109,6 +109,7 @@ class Editor: public Application bool do_save_as = false; bool do_screenshot = false; Exporter const* do_export_to = nullptr; + Importer const* do_import_to = nullptr; bool show_about = false; bool show_help = false; bool show_place_captions = false;