diff --git a/Arm Designer/Arm Designer.vcxproj b/Arm Designer/Arm Designer.vcxproj index 3ae37c20b525b105046483ae6f911a2aeef2cc27..d64bc92c342ff19040a4f1600d5fb4297f77249e 100644 --- a/Arm Designer/Arm Designer.vcxproj +++ b/Arm Designer/Arm Designer.vcxproj @@ -42,13 +42,13 @@ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <UseDebugLibraries>true</UseDebugLibraries> - <PlatformToolset>v142</PlatformToolset> + <PlatformToolset>v143</PlatformToolset> <CharacterSet>Unicode</CharacterSet> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> <ConfigurationType>Application</ConfigurationType> <UseDebugLibraries>false</UseDebugLibraries> - <PlatformToolset>v142</PlatformToolset> + <PlatformToolset>v143</PlatformToolset> <WholeProgramOptimization>true</WholeProgramOptimization> <CharacterSet>Unicode</CharacterSet> </PropertyGroup> @@ -80,13 +80,13 @@ </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <LinkIncremental>true</LinkIncremental> - <IncludePath>C:\Users\engel\repos\arm-designer\include;C:\Users\engel\repos\arm-designer\include\imgui;C:\Users\engel\repos\arm-designer\WindowsSDL\SDL2-2.0.14\include;$(IncludePath)</IncludePath> - <LibraryPath>C:\Users\engel\repos\arm-designer\WindowsSDL\SDL2-2.0.14\lib\x64;$(LibraryPath)</LibraryPath> + <IncludePath>C:\Users\engel\repos\arm-designer\include;C:\Users\engel\repos\arm-designer\include\imgui;C:\Users\engel\repos\arm-designer\WindowsSDL\SDL2-2.0.14\include;C:\Users\Pascal\repos\arm-designer\include;C:\Users\Pascal\repos\arm-designer\include\imgui;C:\Users\Pascal\repos\arm-designer\WindowsSDL\SDL2-2.0.14\include;$(IncludePath)</IncludePath> + <LibraryPath>C:\Users\engel\repos\arm-designer\WindowsSDL\SDL2-2.0.14\lib\x64;C:\Users\Pascal\repos\arm-designer\WindowsSDL\SDL2-2.0.14\lib\x64;$(LibraryPath)</LibraryPath> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <LinkIncremental>false</LinkIncremental> - <IncludePath>C:\Users\engel\repos\arm-designer\include;C:\Users\engel\repos\arm-designer\include\imgui;C:\Users\engel\repos\arm-designer\WindowsSDL\SDL2-2.0.14\include;$(IncludePath)</IncludePath> - <LibraryPath>C:\Users\engel\repos\arm-designer\WindowsSDL\SDL2-2.0.14\lib\x64;$(LibraryPath)</LibraryPath> + <IncludePath>C:\Users\engel\repos\arm-designer\include;C:\Users\engel\repos\arm-designer\include\imgui;C:\Users\engel\repos\arm-designer\WindowsSDL\SDL2-2.0.14\include;C:\Users\Pascal\repos\arm-designer\include;C:\Users\Pascal\repos\arm-designer\include\imgui;C:\Users\Pascal\repos\arm-designer\WindowsSDL\SDL2-2.0.14\include;$(IncludePath)</IncludePath> + <LibraryPath>C:\Users\engel\repos\arm-designer\WindowsSDL\SDL2-2.0.14\lib\x64;C:\Users\Pascal\repos\arm-designer\WindowsSDL\SDL2-2.0.14\lib\x64;$(LibraryPath)</LibraryPath> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> @@ -123,6 +123,7 @@ <SDLCheck>true</SDLCheck> <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <ConformanceMode>true</ConformanceMode> + <LanguageStandard>stdcpp20</LanguageStandard> </ClCompile> <Link> <SubSystem>Console</SubSystem> @@ -138,6 +139,7 @@ <SDLCheck>true</SDLCheck> <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <ConformanceMode>true</ConformanceMode> + <LanguageStandard>stdcpp20</LanguageStandard> </ClCompile> <Link> <SubSystem>Console</SubSystem> diff --git a/include/arm_designer.hpp b/include/arm_designer.hpp index 3f7610530b265dfdd94f60746bddaba4a3b2ec95..0531f3d36ede0b140ee683f1c1a32f269fa8e056 100644 --- a/include/arm_designer.hpp +++ b/include/arm_designer.hpp @@ -19,8 +19,10 @@ #define GL_GPU_MEM_INFO_TOTAL_AVAILABLE_MEM_NVX 0x9048 #define GL_GPU_MEM_INFO_CURRENT_AVAILABLE_MEM_NVX 0x9049 /*Paths*/ -#define RESOURCEPATH "C:\\Users\\engel\\repos\\arm-designer\\resources\\" - +/*HOME*/ +//#define RESOURCEPATH "C:\\Users\\engel\\repos\\arm-designer\\resources\\" +/*ZYGOTE*/ +#define RESOURCEPATH "C:\\Users\\Pascal\\repos\\arm-designer\\resources\\" class ArmDesigner { public: ArmDesigner(); @@ -36,8 +38,11 @@ private: /*Parameter Containers*/ RenderParameters render_params_; GuiParameters gui_params_; - std::vector<std::tuple<size_t, size_t, float, float> > selected_nodes_; //segment index, node index, x, y - std::array<std::tuple<size_t, size_t, float, float>, 2> closest_nodes_; //node selection: closest node in [0], line selection: end nodes of closest segment in [0] and [1] + std::vector<std::pair<size_t, size_t> > selected_nodes_; //segment index, node index, x, y + std::vector<std::array<float, 2> > selected_nodes_inipos_; //positions of selected nodes when dragging begins + /*Mouse info*/ + std::array<float, 2> mouse_press_loc_; + /*Member Functions*/ /*DEBUG Opengl*/ @@ -51,6 +56,8 @@ private: void gui_draw_debug_window_(); /*GUI Drawn Controller Window*/ void gui_draw_controller_window_(); + /*GUI Draw Log Window*/ + void gui_draw_log_window_(); /*GUI Finish Drawing*/ void gui_finish_drawing_(); diff --git a/include/gui_parameters.hpp b/include/gui_parameters.hpp index 463fc1bcf80fde6a64e35f54f78a2b2f544ce50e..9900e6dbae8d6f931717b2f4974ea4209b74de75 100644 --- a/include/gui_parameters.hpp +++ b/include/gui_parameters.hpp @@ -1,7 +1,7 @@ #pragma once #include <string> -enum class SEL_MODE{NONE=0, NODE, LINE, SEGMENT}; +enum class SEL_MODE : int {NONE=0, NODE, LINE, SEGMENT}; struct GuiParameters { /*Path from which the next segment is loaded*/ @@ -19,9 +19,12 @@ struct GuiParameters { SEL_MODE selection_mode = SEL_MODE::NONE; /*Closest Object*/ - float mindist_en = false; + bool mindist_en = false; float mindist_dist; - float mindist_seg_i; - float mindist_node_i; - float mindist_node_i2; //used when selecting lines + size_t mindist_seg_i; + size_t mindist_node_i; + size_t mindist_node_i2; //used when selecting lines + + /*Log*/ + std::vector<std::string> log_messages; }; diff --git a/include/render_parameters.hpp b/include/render_parameters.hpp index 6c0cef92de069674c69219d57f0ea883c2660197..a75ecf1a7a120830549c90eea5924fc957e7581f 100644 --- a/include/render_parameters.hpp +++ b/include/render_parameters.hpp @@ -17,6 +17,8 @@ struct RenderParameters { glm::vec3 color_segment_rep_points; glm::vec3 color_segment_sel_lines; glm::vec3 color_segment_sel_points; + glm::vec3 color_segment_clo_points; + glm::vec3 color_segment_clo_lines; std::vector<float> grid_coords; GLuint vbo_grid_nodes; diff --git a/include/segment.hpp b/include/segment.hpp index a7caa38ae877280f0f03fbe1aa64e8bab2069101..a894b178cdac2c5e0a43369b4e3807dc90dd7650 100644 --- a/include/segment.hpp +++ b/include/segment.hpp @@ -6,10 +6,15 @@ class Segment { public: Segment(std::string filename); + Segment(std::vector<std::array<float, 2> > pts); void save(std::string filename) const; + void move_to(size_t i, const std::array<float, 2> pos); + void insert(size_t i, const std::array<float, 2> pos); const std::vector<std::array<float, 2> >& nodes() const; + std::vector<std::array<float, 2> > split(size_t i, size_t j); + private: /*Vector of points, pts[i] is connected to pts[i-1] and pts[i+1]*/ std::vector<std::array<float, 2> > pts_; diff --git a/include/segments.hpp b/include/segments.hpp index 1a872cceac91607741c0028b775f0f72904f9df4..8ff81b05078f1370bf75b0f43864ba3f5508ea3f 100644 --- a/include/segments.hpp +++ b/include/segments.hpp @@ -11,8 +11,11 @@ public: bool save(std::string filename) const; void clear(); const std::vector<Segment>& get_segments() const; + std::vector<Segment>& get_segments(); size_t size() const; + void split(size_t segment_i, size_t node1_i, size_t node2_i); + private: std::vector<Segment> segms_; }; \ No newline at end of file diff --git a/src/arm_designer.cpp b/src/arm_designer.cpp index a3770ca14a4dd1601fe8ed3071c0b7664d2ca407..1fd1b3af9f56b2f8983f633937887041d45f5bc1 100644 --- a/src/arm_designer.cpp +++ b/src/arm_designer.cpp @@ -3,6 +3,7 @@ #include <cmath> #include <utility.hpp> #include <cassert> +#include <algorithm>//std::sort ArmDesigner::ArmDesigner() { infra_.init("Arm Designer", WIDTH, HEIGHT); @@ -26,6 +27,7 @@ bool ArmDesigner::run(){ gui_new_frame_(); gui_draw_debug_window_(); gui_draw_controller_window_(); + gui_draw_log_window_(); gui_finish_drawing_(); SDL_GL_SwapWindow(infra_.window()); @@ -93,13 +95,34 @@ void ArmDesigner::gui_draw_debug_window_() { ImGui::Text("FOV: %1.f", render_params_.fov); ImGui::Text("Horizontal Distance: %.1f um", std::abs(render_params_.zoffset) * 2.f * 1000.f * std::tan(glm::radians(render_params_.fov/2.f))); ImGui::Text("Center Coordinates: (%.3f, %.3f)", render_params_.offset.x, render_params_.offset.y); + if (gui_params_.selection_mode == SEL_MODE::NODE && gui_params_.mindist_en) { + ImGui::Text("Closest Node: Segment %u, Node %u, Distance %.4fmm", gui_params_.mindist_seg_i, gui_params_.mindist_node_i, gui_params_.mindist_dist); + } + else if (gui_params_.selection_mode == SEL_MODE::LINE && gui_params_.mindist_en) { + ImGui::Text("Closest Line: Segment %u, Node1 %u, Node2 %u, Distance %.4fmm", gui_params_.mindist_seg_i, gui_params_.mindist_node_i, gui_params_.mindist_node_i2, gui_params_.mindist_dist); + } + else if (gui_params_.selection_mode == SEL_MODE::SEGMENT && gui_params_.mindist_en) { + ImGui::Text("Not Implemented."); + } + else if (gui_params_.selection_mode == SEL_MODE::NONE || !gui_params_.mindist_en) { + ImGui::Text("Closest: Disabled or none found."); + } + for (auto n : selected_nodes_) { + ImGui::Text("Selected: Segment %u, Node %u", n.first, n.second); + } ImGui::End(); } /*GUI Draw Controller Window*/ void ArmDesigner::gui_draw_controller_window_() { + SEL_MODE last_sel_mode = gui_params_.selection_mode; //to track if selection mode changes this frame ImGui::Begin("Controller", nullptr, ImGuiWindowFlags_AlwaysAutoResize); if (ImGui::Button("Load")) { - segments_.load(RESOURCEPATH + std::string("designs\\") + gui_params_.segment_load_path); + try { + segments_.load(RESOURCEPATH + std::string("designs\\") + gui_params_.segment_load_path); + } + catch (...) { + gui_params_.log_messages.push_back("Loading from file " + std::string(RESOURCEPATH) + std::string("designs\\") + gui_params_.segment_load_path + " failed."); + } gui_params_.segments_shown.push_back(true); gui_params_.segments_repetitions.push_back(1); } @@ -116,6 +139,13 @@ void ArmDesigner::gui_draw_controller_window_() { if (ImGui::Button("Reset View")) { reset_view_(); } + ImGui::Text("Selection Mode:"); + ImGui::SameLine(); + ImGui::RadioButton("None", (int*) &gui_params_.selection_mode, 0); + ImGui::SameLine(); + ImGui::RadioButton("Node", (int*)&gui_params_.selection_mode, 1); + ImGui::SameLine(); + ImGui::RadioButton("Line", (int*)&gui_params_.selection_mode, 2); ImGui::Checkbox("Draw Drum Contour", &gui_params_.enable_drum_contour); ImGui::Text("Segment Info"); for (size_t i = 0; i < segments_.size(); ++i) { @@ -131,6 +161,20 @@ void ArmDesigner::gui_draw_controller_window_() { gui_params_.segments_shown[i] = shown; } ImGui::End(); + if (last_sel_mode != gui_params_.selection_mode) {//selection mode changed + selected_nodes_.clear(); + gui_params_.mindist_en = false; + } +} +/*GUI Draw Log Window*/ +void ArmDesigner::gui_draw_log_window_() { + ImGui::SetNextWindowSize(ImVec2(250, 700)); + ImGui::Begin("Log", nullptr); + for (auto s : gui_params_.log_messages) { + ImGui::Text(s.c_str()); + ImGui::SetScrollHere(1.f); + } + ImGui::End(); } /*GUI Finish Drawing*/ void ArmDesigner::gui_finish_drawing_() { @@ -216,6 +260,8 @@ void ArmDesigner::renderer_init_() { render_params_.color_segment_rep_points = glm::vec3(0.3f, 0.3f, 0.3f); render_params_.color_segment_sel_lines = glm::vec3(1.f, 1.f, 0.f); render_params_.color_segment_sel_points = glm::vec3(1.f, 1.f, 0.f); + render_params_.color_segment_clo_lines = glm::vec3(1.f, 1.f, 1.f); + render_params_.color_segment_clo_points = glm::vec3(1.f, 1.f, 1.f); render_params_.color_grid_lines = glm::vec3(0.25f, 0.25f, 0.25f); render_params_.color_drum_lines = glm::vec3(0.7f, 0.0f, 0.0f); @@ -270,13 +316,14 @@ void ArmDesigner::renderer_render_() { for (size_t k = 0; k < gui_params_.segments_repetitions[i]; ++k) { float rot_angle = k * 2. * M_PI / gui_params_.segments_repetitions[i]; render_params_.shader_segment_lines.setFloat("rot_angle", rot_angle); + glLineWidth(1.f); if (k == 0) { render_params_.shader_segment_lines.setVec3("color", render_params_.color_segment_lines); } else { render_params_.shader_segment_lines.setVec3("color", render_params_.color_segment_rep_lines); } - glDrawArrays(GL_LINE_LOOP, 0, nodes.size()); + glDrawArrays(GL_LINE_STRIP, 0, nodes.size()); glPointSize(4.f); if (k == 0) { render_params_.shader_segment_lines.setVec3("color", render_params_.color_segment_points); @@ -292,6 +339,61 @@ void ArmDesigner::renderer_render_() { float rot_angle = 0; render_params_.shader_segment_lines.setFloat("rot_angle", rot_angle); } + + //Now we draw the selected nodes + if (selected_nodes_.size() > 0) { + render_params_.shader_segment_lines.use(); + glBindVertexArray(render_params_.vao_segment_nodes); + glBindBuffer(GL_ARRAY_BUFFER, render_params_.vbo_segment_nodes); + //collect coordinates + std::vector<std::array<float,2> > coords; + if (gui_params_.selection_mode == SEL_MODE::NODE) { + coords.reserve(2 * selected_nodes_.size()); + for (auto n : selected_nodes_) { + coords.push_back(segments_.get_segments()[n.first].nodes()[n.second]); + } + glBufferData(GL_ARRAY_BUFFER, 2 * sizeof(float) * coords.size(), coords.data(), GL_STATIC_DRAW); + render_params_.shader_segment_lines.setVec3("color", render_params_.color_segment_sel_points); + glPointSize(7.f); + glDrawArrays(GL_POINTS, 0, coords.size()); + } + else if (gui_params_.selection_mode == SEL_MODE::LINE) { + coords.reserve(4 * selected_nodes_.size()); + for (auto n : selected_nodes_) { + coords.push_back(segments_.get_segments()[n.first].nodes()[n.second]); + coords.push_back(segments_.get_segments()[n.first].nodes()[n.second+1]); + } + glBufferData(GL_ARRAY_BUFFER, 2 * sizeof(float) * coords.size(), coords.data(), GL_STATIC_DRAW); + render_params_.shader_segment_lines.setVec3("color", render_params_.color_segment_sel_lines); + glLineWidth(2.f); + glDrawArrays(GL_LINES, 0, coords.size()); + } + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + + //Now we draw the close nodes/lines + if (gui_params_.mindist_en) { + render_params_.shader_segment_lines.use(); + glBindVertexArray(render_params_.vao_segment_nodes); + glBindBuffer(GL_ARRAY_BUFFER, render_params_.vbo_segment_nodes); + if (gui_params_.selection_mode == SEL_MODE::NODE) { + auto nodes = segments_.get_segments()[gui_params_.mindist_seg_i].nodes(); + glBufferData(GL_ARRAY_BUFFER, 2 * sizeof(float), nodes[gui_params_.mindist_node_i].data(), GL_STATIC_DRAW); + render_params_.shader_segment_lines.setVec3("color", render_params_.color_segment_clo_points); + glPointSize(7.f); + glDrawArrays(GL_POINTS, 0, 1); + } + else if (gui_params_.selection_mode == SEL_MODE::LINE) { + auto nodes = segments_.get_segments()[gui_params_.mindist_seg_i].nodes(); + glBufferData(GL_ARRAY_BUFFER, 4 * sizeof(float), nodes[gui_params_.mindist_node_i].data(), GL_STATIC_DRAW); + render_params_.shader_segment_lines.setVec3("color", render_params_.color_segment_clo_lines); + glLineWidth(2.f); + glDrawArrays(GL_LINES, 0, 2); + } + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + } } /*INPUT handle*/ @@ -299,6 +401,11 @@ bool ArmDesigner::input_handle_() { SDL_Event event; while (SDL_PollEvent(&event)) { ImGui_ImplSDL2_ProcessEvent(&event); + //If IMGUI wants the mouse, we skip + if (ImGui::GetIO().WantCaptureMouse) { + gui_params_.mindist_en = false; + return true; + } switch (event.type) { case SDL_QUIT: return false; @@ -317,31 +424,170 @@ bool ArmDesigner::input_handle_() { if (render_params_.fov > 90.f) { render_params_.fov = 90.f; } - if (render_params_.fov < 1.f) { - render_params_.fov = 1.f; + if (render_params_.fov < 0.2f) { + render_params_.fov = 0.2f; } - } break; + } case SDL_MOUSEMOTION: + { if (gui_params_.selection_mode == SEL_MODE::NONE) { + gui_params_.mindist_en = true; break; } + + auto IO = ImGui::GetIO(); + if (IO.KeyShift && IO.MouseDown[0] && gui_params_.selection_mode == SEL_MODE::NODE) {//We're actually dragging the selection (only for nodes) + if (selected_nodes_.size() > 0) {//actually have a selection to drag + for (size_t i = 0; i < selected_nodes_.size(); ++i) { + auto curr_mouse = get_real_mouse_coords_(); + std::array<float, 2> new_pos = {selected_nodes_inipos_[i][0] + curr_mouse[0] - mouse_press_loc_[0], selected_nodes_inipos_[i][1] + curr_mouse[1] - mouse_press_loc_[1] }; + segments_.get_segments()[selected_nodes_[i].first].move_to(selected_nodes_[i].second, new_pos); + } + } + } + /*Find node with minimal distance (or the one that appears first for equal distances)*/ auto mouse_coords = get_real_mouse_coords_(); - if(gui_params_.selection_mode == SEL_MODE::NODE){ + if (gui_params_.selection_mode == SEL_MODE::NODE) { auto tup = find_closest_seg_node_dist_(mouse_coords[0], mouse_coords[1]); - float tol = 0.01 * std::abs(render_params_.zoffset) * 2.f * 1000.f * std::tan(glm::radians(render_params_.fov / 2.f)); //1 percent of the horizontal view + float tol = 0.01 * std::abs(render_params_.zoffset) * 2.f * /*1000.f **/ std::tan(glm::radians(render_params_.fov / 2.f)); //1 percent of the horizontal view if (std::get<2>(tup) <= tol) { //found point within tolerance gui_params_.mindist_en = true; gui_params_.mindist_seg_i = std::get<0>(tup); gui_params_.mindist_node_i = std::get<1>(tup); gui_params_.mindist_dist = std::get<2>(tup); + auto ns = segments_.get_segments()[gui_params_.mindist_seg_i].nodes(); } else { //no point within tolerance gui_params_.mindist_en = false; } } + else if (gui_params_.selection_mode == SEL_MODE::LINE) { + auto tup = find_closest_seg_line_dist_(mouse_coords[0], mouse_coords[1]); + float tol = 0.01 * std::abs(render_params_.zoffset) * 2.f * /*1000.f **/ std::tan(glm::radians(render_params_.fov / 2.f)); //1 percent of the horizontal view + if (std::get<3>(tup) >= 0.f && std::get<3>(tup) <= tol) { + gui_params_.mindist_en = true; + gui_params_.mindist_seg_i = std::get<0>(tup); + gui_params_.mindist_node_i = std::get<1>(tup); + gui_params_.mindist_node_i2 = std::get<2>(tup); + gui_params_.mindist_dist = std::get<3>(tup); + } + else { + gui_params_.mindist_en = false; + } + } + break; } + case SDL_MOUSEBUTTONDOWN: + { + //If shift is pressed, we're moving the selection and skip this step + auto IO = ImGui::GetIO(); + mouse_press_loc_ = get_real_mouse_coords_();//update click location + if (IO.KeyShift) { + selected_nodes_inipos_.resize(0); + selected_nodes_inipos_.reserve(selected_nodes_.size()); + for (auto n : selected_nodes_) { + selected_nodes_inipos_.push_back(segments_.get_segments()[n.first].nodes()[n.second]); + } + continue; + } + //If selecting, add closest node to selection + if (gui_params_.selection_mode == SEL_MODE::NODE) { + if (IO.KeyCtrl) { //add to selection + if (gui_params_.mindist_en) { + bool handled = false; + //Check if we're deselecting + for (auto it = selected_nodes_.begin(); it != selected_nodes_.end(); ++it) { + if (it->first == gui_params_.mindist_seg_i && it->second == gui_params_.mindist_node_i) { + handled = true; + selected_nodes_.erase(it); + break; + } + } + if (!handled) { + selected_nodes_.push_back({ gui_params_.mindist_seg_i, gui_params_.mindist_node_i }); + } + } + } + else {//clear selection & new addition + selected_nodes_.clear(); + if (gui_params_.mindist_en) { + selected_nodes_.push_back({ gui_params_.mindist_seg_i, gui_params_.mindist_node_i }); + } + } + } + else if (gui_params_.selection_mode == SEL_MODE::LINE) { + if (IO.KeyCtrl) { //add to selection + if (gui_params_.mindist_en) { + bool handled = false; + //Check if we're deselecting + for (auto it = selected_nodes_.begin(); it != selected_nodes_.end(); ++it) { + if (it->first == gui_params_.mindist_seg_i && it->second == gui_params_.mindist_node_i) { + handled = true; + selected_nodes_.erase(it); + break; + } + } + if (!handled) { + selected_nodes_.push_back({ gui_params_.mindist_seg_i, gui_params_.mindist_node_i }); + } + } + } + else {//clear selection & new addition + selected_nodes_.clear(); + if (gui_params_.mindist_en) { + selected_nodes_.push_back({ gui_params_.mindist_seg_i, gui_params_.mindist_node_i }); + } + } + } + break; + } + case SDL_KEYDOWN: + { + //key presses + if (event.key.keysym.sym == SDLK_s) {//s + if (gui_params_.selection_mode == SEL_MODE::LINE){//only in line selection mode + //split all selected lines + //first sort the selected segments + std::sort(selected_nodes_.begin(), selected_nodes_.end(), [](auto a, auto b) {return a.second < b.second; }); + std::stable_sort(selected_nodes_.begin(), selected_nodes_.end(), [](auto a, auto b) {return a.first < b.first; }); + std::cout << "selection: "; + for (auto n : selected_nodes_) { + std::cout << "(" << n.first << ", " << n.second << "), "; + } + std::cout << std::endl; + std::vector < std::pair<size_t, size_t> > new_selection; + new_selection.reserve(2 * selected_nodes_.size()); + std::vector<size_t> seg_increments(segments_.get_segments().size(), 0); + for (size_t i = 0; i < selected_nodes_.size(); ++i) { + auto node1 = segments_.get_segments()[selected_nodes_[i].first].nodes()[selected_nodes_[i].second + seg_increments[selected_nodes_[i].first]]; + auto node2 = segments_.get_segments()[selected_nodes_[i].first].nodes()[selected_nodes_[i].second + seg_increments[selected_nodes_[i].first] + 1]; + std::array<float, 2> new_node{ (node1[0] + node2[0]) / 2.f, (node1[1] + node2[1]) / 2.f }; + segments_.get_segments()[selected_nodes_[i].first].insert(selected_nodes_[i].second + seg_increments[selected_nodes_[i].first], new_node); + new_selection.push_back({ selected_nodes_[i].first, selected_nodes_[i].second + seg_increments[selected_nodes_[i].first] }); + new_selection.push_back({ selected_nodes_[i].first, selected_nodes_[i].second + seg_increments[selected_nodes_[i].first] + 1 }); + seg_increments[selected_nodes_[i].first]++; + } + selected_nodes_ = new_selection; + } + } + else if (event.key.keysym.sym == SDLK_x) {//x + if (selected_nodes_.size() != 2 || selected_nodes_[0].first != selected_nodes_[1].second) { + continue; + } + else { + segments_.split(selected_nodes_[0].first, selected_nodes_[0].second, selected_nodes_[1].second); + gui_params_.segments_shown.push_back(true); + gui_params_.segments_repetitions.push_back(gui_params_.segments_repetitions[selected_nodes_[0].first]); + gui_params_.selection_mode = SEL_MODE::NONE; + selected_nodes_.clear(); + } + } + break; + } + } + } return true; } @@ -378,6 +624,7 @@ std::tuple<size_t, size_t, float> ArmDesigner::find_closest_seg_node_dist_(const } /*Find closest segment, line and distance*/ +/*Positive distance means result is valid*/ std::tuple<size_t, size_t, size_t, float> ArmDesigner::find_closest_seg_line_dist_(const float x, const float y) const{ auto segs = segments_.get_segments(); @@ -396,23 +643,17 @@ std::tuple<size_t, size_t, size_t, float> ArmDesigner::find_closest_seg_line_dis /*Normalize*/ float dirvlen = std::sqrt(dirv[0] * dirv[0] + dirv[1] * dirv[1]); assert(dirvlen > 0.f, "Nodes are on top of each other, zero distance between them"); - dirv[0] /= dirvlen; - dirv[1] /= dirvlen; - float norvlen = std::sqrt(norv[0] * norv[0] + norv[1] * norv[1]); - norv[0] /= norvlen; - norv[1] /= norvlen; - auto xg = nodes[k][0]; - auto yg = nodes[k][0]; - auto m = get_real_mouse_coords_(); - auto xh = m[0]; - auto yh = m[1]; + float xg = nodes[k][0]; + float yg = nodes[k][1]; + float xh = x; + float yh = y; /*Calculate crossing*/ - float s = (yg - yh + (xh - xg) / dirv[0]) / (norv[1] - (norv[0] * dirv[1]) / (dirv[0])); + float s = (yg - yh + (xh - xg) * dirv[1] / dirv[0]) / (norv[1] - (norv[0] * dirv[1]) / (dirv[0])); float t = (xh - xg + s * norv[0]) / dirv[0]; /*Check if we're straight above*/ - if (t < 1.) { - if (s < min_dist) { - min_dist = s; //true because norv is normalized + if (t < 1.f && t > 0.f) { + if (std::abs(s) * dirvlen < min_dist) { + min_dist = std::abs(s) * dirvlen; min_seg_i = i; min_node_i = k; } diff --git a/src/segment.cpp b/src/segment.cpp index 0a845814454e5e9fb87f1b6969320ad794f5f1a8..e7d1ce5023270b1c85b12a1887890513898a3a46 100644 --- a/src/segment.cpp +++ b/src/segment.cpp @@ -7,8 +7,7 @@ Segment::Segment(std::string filename){ std::ifstream infile(filename); if (!infile.is_open()) { - std::cerr << "File " << filename << " can't be read.\n"; - std::exit(1); + throw("IO ERROR"); } else { float x = 0.; @@ -28,6 +27,8 @@ Segment::Segment(std::string filename){ infile.close(); } +Segment::Segment(std::vector<std::array<float, 2> > pts): pts_(pts) {} + void Segment::save(std::string filename) const { std::ofstream outfile(filename, std::ios::out); if (!outfile.is_open()) { @@ -43,6 +44,24 @@ void Segment::save(std::string filename) const { outfile.close(); } +void Segment::move_to(size_t i, const std::array<float, 2> pos) { + pts_[i] = pos; +} + +void Segment::insert(size_t i, const std::array<float, 2> pos) { + pts_.insert(pts_.begin() + i + 1, pos); +} + const std::vector<std::array<float, 2> >& Segment::nodes() const{ return pts_; +} + +std::vector<std::array<float, 2> > Segment::split(size_t i, size_t j) { + std::vector<std::array<float, 2> > retvec(pts_.begin() + std::min(i, j) + 1, pts_.begin() + std::max(i, j)); + std::vector<std::array<float, 2> > new_pts(pts_.begin(), pts_.begin() + std::min(i, j) + 1); + for (size_t k = std::max(i, j); k < pts_.size(); ++k) { + new_pts.push_back(pts_[k]); + } + pts_ = new_pts; + return retvec; } \ No newline at end of file diff --git a/src/segments.cpp b/src/segments.cpp index 4e4187566e54acbfd0f1a68e9d84e2351e87e26b..73fb8de75feea65345deccf39d73d64efae4f00c 100644 --- a/src/segments.cpp +++ b/src/segments.cpp @@ -27,6 +27,15 @@ const std::vector<Segment>& Segments::get_segments() const{ return segms_; } +std::vector<Segment>& Segments::get_segments() { + return segms_; +} + size_t Segments::size() const { return segms_.size(); +} + +void Segments::split(size_t segment_i, size_t node1_i, size_t node2_i) { + segms_.push_back(Segment(segms_[segment_i].split(node1_i, node2_i))); + } \ No newline at end of file