//Hidden Lines work #include #include #include #include #include #include #include #include #include "gnuplot-iostream.h" #include "HiddenLines.h" HL::HL() { //Read from CSV and construct the problem from the given lines lines = construct_HWprob(); } //Print the solution and convert the double::max and double::lowest to inf strings void print_sol(std::vector& lines){ for(auto itr = lines.begin(); itr != lines.end(); itr++){ Line line = *itr; double start_range = line.get_vis_start(); std::string start_print = std::to_string(start_range); if(start_range == std::numeric_limits::lowest()) start_print = "-inf"; double stop_range = line.get_vis_end(); std::string stop_print = std::to_string(stop_range); if(stop_range == std::numeric_limits::max()) stop_print = "inf"; if (start_range == stop_range) continue; std::cout << "Line ID " << line.get_id() << " visible from x=" << start_print << " to x=" << stop_print << std::endl; } } //Divide and Conquer Strategy using recursive call to divide ls and merge using merge() std::vector HL::gen_sol(std::vector& ls){ HL inst = *this; //Case 1: We need to divide more if (ls.size() > 2){ //Create left and right half sets int split = ceil(ls.size()/2); std::vector lh; std::vector rh; //Get iterator for the split point location auto end_itr = ls.begin(); std::advance(end_itr, split); //Create left half for(std::vector::iterator itr = ls.begin(); itr != end_itr; itr++){ lh.push_back(*itr); } //Create right half for(std::vector::iterator itr = end_itr; itr != ls.end(); itr++){ rh.push_back(*itr); } //Recursive call auto merged = merge(inst.gen_sol(lh), inst.gen_sol(rh)); return merged; } //Set with 2 lines is a base case: both are visible at +-inf respectively, and intersection is where they change //visibility else if (ls.size() == 2){ //Get non-const iterators to the beginning and end of our vector std::vector::iterator bit = ls.begin(); std::vector::iterator eit = std::prev(ls.end()); Line l1 = *(bit); Line l2 = *(eit); //Get intersection of our two lines double isec = l1.get_isec(l2); //If Line 1 has smaller slope, its visibility end in comparison to Line 2 is the intersection. Vice versa for Line 2. if (l1.get_slope() < l2.get_slope()){ l1.set_vis_end(isec); *bit = l1; l2.set_vis_start(isec); *eit = l2; } else{ l2.set_vis_end(isec); l1.set_vis_start(isec); *bit = l1; *eit = l2; } return ls; } //Set with 1 line: Just return - no comparisons to make at this point. else{ return ls; } } //Read from CSV file to construct this problem. Use std::set to order std::vector HL::construct_HWprob(){ //Set to hold all our Lines std::set sorted_lines; std::vector lines; //Hold our doubles from each line std::vector tmp_data; std::ifstream data("../data/data.csv"); if(data.is_open()){ std::string csvline; //Throw header row away std::getline(data, csvline); while(std::getline(data, csvline, ',')){ if(!csvline.empty()){ tmp_data.emplace_back(std::stod(csvline)); } } } //Close file data.close(); while(!tmp_data.empty()){ double slope = *tmp_data.begin(); tmp_data.erase(tmp_data.begin()); if(tmp_data.empty()){ std::cout << "Error in CSV file: Number of slope entries does not match the number of y-intercept entries. "; std::cout << "Please correct the CSV file and try again." << std::endl; exit(-1); } double ycept = *tmp_data.begin(); tmp_data.erase(tmp_data.begin()); Line newline = Line(slope, ycept); newline.set_id(); sorted_lines.insert(newline); } //Copy set to vector std::copy(sorted_lines.begin(), sorted_lines.end(), std::back_inserter(lines)); //Display Sorted Vector std::cout << "---------------Sorted Lines by slope:---------------" << std::endl; for(std::vector::iterator it = lines.begin(); it != lines.end(); it++) { Line line = *it; std::cout << "Line " << line.get_id() << " has slope " << line.get_slope() << " and a y-intercept of " << line.get_ycept() << std::endl; if (it == lines.begin()){ line.set_vis_start(std::numeric_limits::lowest()); *it = line; } if (it == (std::prev(lines.end()))){ line.set_vis_end(std::numeric_limits::max()); *it = line; } } std::cout << "-----------------------------------------------------" << std::endl; return lines; } //Merge the two halves and set visible start and end points std::vector merge(std::vector lh, std::vector rh){ std::vector merged; //Get non-const iterators to the start of both vectors std::vector::iterator litr = lh.begin(); std::vector::iterator ritr = rh.begin(); while(litr != lh.end() && ritr != rh.end()){ Line l1 = *litr; Line l2 = *ritr; //3 Cases: lh slope is less, slopes are equal, and lh slope is greater //Case 1: if (l1.get_slope() < l2.get_slope()){ double isec = (l2.get_ycept() - l1.get_ycept())/(l1.get_slope() - l2.get_slope()); if(isec < l1.get_vis_end()){ l1.set_vis_end(isec); l2.set_vis_start(isec); *litr = l1; *ritr = l2; } merged.push_back(l1); litr++; } else if (l1.get_slope() == l2.get_slope() && l1.get_id() != l2.get_id()){ if (l1.get_ycept() > l2.get_ycept()){ l2.set_vis_start(0); l2.set_vis_end(0); *ritr = l2; merged.push_back(l1); } else{ l1.set_vis_start(0); l1.set_vis_end(0); *litr = l1; merged.push_back(l2); } litr++; ritr++; } //lh slope > rh else{ double isec = (l2.get_ycept() - l1.get_ycept())/(l1.get_slope() - l2.get_slope()); if(isec < l1.get_vis_start()){ l1.set_vis_start(isec); l2.set_vis_end(isec); *litr = l1; *ritr = l2; } merged.push_back(l2); ritr++; } } while (litr != lh.end()){ merged.push_back(*litr); litr++; } while (ritr != rh.end()){ merged.push_back(*ritr); ritr++; } //Remove invisible lines return remove_invis(merged); } std::vector HL::get_lines(){ return lines; } /* // Line merging process merges the two halves and keeps the order, but does not check for // when intersection points fall to the left (more negative) of other intersection points. // Need to remove the invisible lines caused by this case. */ std::vector remove_invis(std::vector &merged){ //Create separate visible line holder std::vector tmp_vis; //Since we call this function from a merge, we can safely add two elements. tmp_vis.push_back(merged.front()); merged.erase(merged.begin()); tmp_vis.push_back(merged.front()); merged.erase(merged.begin()); //Loop through the remainder of the original vector for(Line &line : merged){ //Pull out the back two lines Line last = tmp_vis.at(tmp_vis.size()-1); Line seclast = tmp_vis.at(tmp_vis.size()-2); //Intersection of our "known" visible lines vs the intersection of the 2nd to last vis line and the original vec double vis_isec = last.get_isec(seclast); double merg_isec = line.get_isec(seclast); while(merg_isec < vis_isec){ //Adjust visibility start and end points line.set_vis_start(merg_isec); seclast.set_vis_end(merg_isec); tmp_vis.at(tmp_vis.size()-2) = seclast; //line intersects before the last line, and since line slope > last line slope, remove it tmp_vis.erase(tmp_vis.begin() + (tmp_vis.size()-1)); //We only have 1 line visible, no more comparisons needed to see if we have something out of order if(tmp_vis.size() == 1) break; //Otherwise, get our next lines to make sure we don't have something last = tmp_vis.at(tmp_vis.size()-1); seclast = tmp_vis.at(tmp_vis.size()-2); vis_isec = last.get_isec(seclast); merg_isec = line.get_isec(seclast); } tmp_vis.push_back(line); } return tmp_vis; }