From ac39683c548655d9cd089011b9b6da7c9700b920 Mon Sep 17 00:00:00 2001
From: Pascal <engelerp@phys.ethz.ch>
Date: Mon, 21 Feb 2022 17:41:11 +0100
Subject: [PATCH] Work Done!

---
 Arm Designer/Arm Designer.vcxproj |  14 +-
 include/arm_designer.hpp          |  15 +-
 include/gui_parameters.hpp        |  13 +-
 include/render_parameters.hpp     |   2 +
 include/segment.hpp               |   5 +
 include/segments.hpp              |   3 +
 src/arm_designer.cpp              | 283 +++++++++++++++++++++++++++---
 src/segment.cpp                   |  23 ++-
 src/segments.cpp                  |   9 +
 9 files changed, 329 insertions(+), 38 deletions(-)

diff --git a/Arm Designer/Arm Designer.vcxproj b/Arm Designer/Arm Designer.vcxproj
index 3ae37c2..d64bc92 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 3f76105..0531f3d 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 463fc1b..9900e6d 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 6c0cef9..a75ecf1 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 a7caa38..a894b17 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 1a872cc..8ff81b0 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 a3770ca..1fd1b3a 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 0a84581..e7d1ce5 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 4e41875..73fb8de 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
-- 
GitLab