Hidden_Line_Problem/src/HiddenLines.cpp
2022-03-09 15:46:56 -06:00

296 lines
7.9 KiB
C++

//Hidden Lines work
#include <iostream>
#include <set>
#include <string>
#include <fstream>
#include <limits>
#include <math.h>
#include <utility>
#include <algorithm>
#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<Line>& 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<double>::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<double>::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<Line> HL::gen_sol(std::vector<Line>& 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<Line> lh;
std::vector<Line> rh;
//Get iterator for the split point location
auto end_itr = ls.begin();
std::advance(end_itr, split);
//Create left half
for(std::vector<Line>::iterator itr = ls.begin(); itr != end_itr; itr++){
lh.push_back(*itr);
}
//Create right half
for(std::vector<Line>::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<Line>::iterator bit = ls.begin();
std::vector<Line>::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<Line> HL::construct_HWprob(){
//Set to hold all our Lines
std::set<Line> sorted_lines;
std::vector<Line> lines;
//Hold our doubles from each line
std::vector<double> 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<Line>::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<double>::lowest());
*it = line;
}
if (it == (std::prev(lines.end()))){
line.set_vis_end(std::numeric_limits<double>::max());
*it = line;
}
}
std::cout << "-----------------------------------------------------" << std::endl;
return lines;
}
//Merge the two halves and set visible start and end points
std::vector<Line> merge(std::vector<Line> lh, std::vector<Line> rh){
std::vector<Line> merged;
//Get non-const iterators to the start of both vectors
std::vector<Line>::iterator litr = lh.begin();
std::vector<Line>::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<Line> 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<Line> remove_invis(std::vector<Line> &merged){
//Create separate visible line holder
std::vector<Line> 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;
}