#include #include #include #include #include #include #include #include #include #include #include #include #include #include "libpoly.h" //############################################################ GLOBALS std::condition_variable can_pop; std::condition_variable can_push; std::atomic_uint32_t active_producents; std::atomic_uint32_t active_consuments; std::ostream& os = std::cout; std::mutex os_l; //############################# MODIFY HERE constexpr unsigned NUM_PRODUCENTS = 2; constexpr unsigned NUM_WORKERS = 5; //############################################################ COMMON STRUCTURES struct Temp_Task: public std::enable_shared_from_this< Temp_Task > { LP_Polygon p; double minimal_triangulation; Temp_Task(LP_Polygon&& _p): p(_p){} }; using Temp_Task_p = std::shared_ptr< Temp_Task >; class Task { private: Temp_Task_p task; double triangulate_basic(LP_Polygon* p); double triangulate(); double triangulate_subpolys(std::vector poly_key, std::map< std::vector, double>* m); std::vector create_key(std::vector* indices); public: Task(): task(nullptr){}; Task(const Task&) = delete; Task(Task&& t): task(t.task){ t.task.reset(); } Task(Temp_Task_p&& tp): task(tp){} double solution() const { return task->minimal_triangulation; } bool is_empty() const { return task.get() == nullptr; } void solve_stupid(){ task->minimal_triangulation = triangulate_basic(&(task->p)); } void solve_less_stupid(){ task->minimal_triangulation = triangulate(); } Temp_Task_p unwrap(){ // TODO: vymyslet jinak, zaručit po tomto kroku selfdestruct return std::move(task); } }; class Queue{ std::queue q; std::mutex q_l; const size_t MAX_LENGHT = 100'000; public: void push(Task&& t){ std::unique_lock pushlock(q_l); can_push.wait(pushlock, [this](){ return (q.size() < MAX_LENGHT); }); q.push(std::move(t)); pushlock.unlock(); can_pop.notify_one(); } Task pop(){ std::unique_lock poplock(q_l); can_pop.wait(poplock, [this](){ return !is_empty(); }); Task t(std::move(q.front())); q.pop(); poplock.unlock(); can_push.notify_one(); return t; } bool is_empty( ){ return q.empty(); } }; using task_queue_ptr = std::shared_ptr; //############################################################ DEBUG HELPERS void print_point(LP_Point* p){ std::cout << "{ " << p->x << ", " << p->y << " }"; } void print_polygon(LP_Polygon* p){ std::cout << "{\n"; for(size_t i = 0; i < p->points.size(); i++){ std::cout << " "; print_point(&(p->points[i])); if(i < p->points.size() - 1) std::cout << ",\n"; else std::cout << "\n"; } std::cout << "}\n"; } void task_test(){ std::vector< LP_Point > v1 = {{0,0}, {1,1}, {3,1}, {2,0}}; std::vector< LP_Point > v2 = {{0,0}, {0,1}, {1,2}, {2,1}, {2,0}}; std::vector< LP_Point > v3 = {{0,0}, {1,1}, {2,2}, {3,1}, {2,0}, {1,-1}}; LP_Polygon p1(std::move(v1)); LP_Polygon p2(std::move(v2)); LP_Polygon p3(std::move(v3)); Temp_Task_p tp1 = std::make_shared(std::move(p1)); Temp_Task_p tp2 = std::make_shared(std::move(p2)); Temp_Task_p tp3 = std::make_shared(std::move(p3)); Task t1(std::move(tp1)); Task t2(std::move(tp2)); Task t3(std::move(tp3)); auto start1 = std::chrono::high_resolution_clock::now(); t1.solve_stupid(); auto end1 = std::chrono::high_resolution_clock::now(); auto start2 = std::chrono::high_resolution_clock::now(); t2.solve_stupid(); auto end2 = std::chrono::high_resolution_clock::now(); auto start3 = std::chrono::high_resolution_clock::now(); t3.solve_stupid(); auto end3 = std::chrono::high_resolution_clock::now(); auto duration1 = std::chrono::duration_cast(end1 - start1); auto duration2 = std::chrono::duration_cast(end2 - start2); auto duration3 = std::chrono::duration_cast(end3 - start3); std::cout << "4 vertices:\ncorrect: calculated:\n" << "1.414 " << t1.solution() << " taking " << duration1.count() << "ms" << std::endl; std::cout << "5 vertices:\ncorrect: calculated:\n" << "4.236 " << t2.solution() << " taking " << duration2.count() << "ms" << std::endl; std::cout << "6 vertices:\ncorrect: calculated:\n" << "5.414 " << t3.solution() << " taking " << duration3.count() << "ms" << std::endl << std::endl; start1 = std::chrono::high_resolution_clock::now(); t1.solve_less_stupid(); end1 = std::chrono::high_resolution_clock::now(); start2 = std::chrono::high_resolution_clock::now(); t2.solve_less_stupid(); end2 = std::chrono::high_resolution_clock::now(); start3 = std::chrono::high_resolution_clock::now(); t3.solve_less_stupid(); end3 = std::chrono::high_resolution_clock::now(); duration1 = std::chrono::duration_cast(end1 - start1); duration2 = std::chrono::duration_cast(end2 - start2); duration3 = std::chrono::duration_cast(end3 - start3); std::cout << "SECOND VERSION:\n\n"; std::cout << "4 vertices:\ncorrect: calculated:\n" << "1.414 " << t1.solution() << " taking " << duration1.count() << "ms" << std::endl; std::cout << "5 vertices:\ncorrect: calculated:\n" << "4.236 " << t2.solution() << " taking " << duration2.count() << "ms" << std::endl; std::cout << "6 vertices:\ncorrect: calculated:\n" << "5.414 " << t3.solution() << " taking " << duration3.count() << "ms" << std::endl << std::endl; } //############################################################ PRODUCE THREAD /* class Producent{ private: task_queue_ptr task_queue; public: Producent(task_queue_ptr tq): task_queue(tq){ active_producents++; } Producent(const Producent&) = delete; ~Producent(){ active_producents--; } Task get_task(){ //LP_Task_p tp = pickup_task(); os_l.lock(); os << "picked up" << std::endl; os_l.unlock(); //#########################3TEMP_PART: // std::vector< LP_Point > points; // srand(static_cast(std::chrono::system_clock::now().time_since_epoch().count())); // int num_of_points = (rand()%10) + 1; // for(int i = 0; i < num_of_points; i++){ // LP_Point p; // p.x = rand()%199 +1; // p.y = rand()%199 +1; // points.push_back(p); // } // LP_Polygon p(std::move(points)); // Temp_Task_p tp = std::make_shared(std::move( p )); return Task(std::move(tp)); } void fill_queue(){ while(1){ Task t = get_task(); if(t.is_empty()) break; else{ task_queue->push(std::move(t)); os_l.lock(); os << "queued" << std::endl; os_l.unlock(); } } os_l.lock(); os << "p ready to join" << std::endl; os_l.unlock(); } }; void produce_thread(task_queue_ptr task_queue){ Producent p(task_queue); p.fill_queue(); } //############################################################ WORKER THREAD class Consument { private: task_queue_ptr task_queue; public: Consument(task_queue_ptr tq): task_queue(tq){ active_consuments++; } Consument(const Consument&) = delete; ~Consument() { active_consuments--; } Task get_task(){ // might be redundant os_l.lock(); os << "processing" << std::endl; os_l.unlock(); return task_queue->pop(); } void work(){ while(active_producents || ! task_queue->is_empty()){ Task t = get_task(); t.solve_stupid(); submit_task(std::move(t.unwrap())); os_l.lock(); os << t.solution() << std::endl; os_l.unlock(); } os_l.lock(); os << "c ready to join" << std::endl; os_l.unlock(); } }; void consume_thread(task_queue_ptr task_queue){ Consument c(task_queue); c.work(); } */ //############################################################ TRIANGULATION struct Line{ LP_Point a; LP_Point b; double length; Line() = delete; Line(LP_Point _a, LP_Point _b): a(_a), b(_b){ LP_Point vec_ba; vec_ba.x = b.x - a.x; vec_ba.y = b.y - a.y; length = sqrt(vec_ba.x*vec_ba.x + vec_ba.y*vec_ba.y); } Line(Line& l): a(l.a), b(l.b), length(l.length){} }; double Task::triangulate_basic(LP_Polygon* p){ size_t poly_size = p->points.size(); if(poly_size == 3) return 0.; if(poly_size == 4){ Line cut1(p->points[0], p->points[2]); Line cut2(p->points[1], p->points[3]); return std::min(cut1.length, cut2.length); } double triangulation = INFINITY; double min_triangulation = INFINITY; for (size_t point_idx = 0; point_idx < poly_size; point_idx++){ Line cut(p->points.at( (point_idx + poly_size - 1) % poly_size ), p->points.at( (point_idx + poly_size + 1) % poly_size )); std::vector smaller_poly; for(size_t i = 0; i < poly_size; i++){ if(i != point_idx) smaller_poly.push_back(p->points.at(i)); } LP_Polygon s_p(std::move(smaller_poly)); // if(poly_size == 6){ // std::cout << "From poly:\n"; // print_polygon(p); // std::cout << "created:\n"; // print_polygon(&s_p); // std::cout << "by cut " << (point_idx + poly_size - 1) % poly_size << "->" << (point_idx + poly_size + 1) % poly_size << "\n\n"; // } double s_triang = triangulate_basic(&s_p); triangulation = s_triang + cut.length; min_triangulation = std::min(triangulation, min_triangulation); // if(triangulation < min_triangulation) min_triangulation = triangulation; // std::cout << "Poly size = " << poly_size << // "\nCutted point = "; // print_point(&(p->points[point_idx])); // std::cout << " by "; // print_point(&(cut.a)); // std::cout << " -> "; // print_point(&(cut.b)); // std::cout << "\nTriang = " << triangulation << // "\nPrev min triang = " << prev_min_t << // "\nMin triang = " << min_triangulation << "\n\n"; } return min_triangulation; } //########################### attempt of thought // build a list (array?) of all cut lengths ... n // build a list of "quatrogones"-> pentagones -> hexagones -> ....; each of them carries its min_triangulation // end up with our polygone already w min triang. // not really better i guess... //########################### dynamic something //use std::map to store triangulations of polygons // ! only for small polygons: int is too small double Task::triangulate(){ std::map, double> m; std::vector poly_key(task->p.points.size(), true); return triangulate_subpolys(poly_key, &m); // // return triangulate_subpolys_but_better(); } std::vector Task::create_key(std::vector* indices){ size_t key_length = task->p.points.size(); std::vector key(key_length, false); for(size_t i = 0; i < indices->size(); i++){ key[indices->at(i)] = true; } return key; } double Task::triangulate_subpolys(std::vector poly_key, std::map< std::vector, double>* m){ if (m->contains(poly_key)) return m->at(poly_key); // ! contains() is c++20 // SET VARIABLES std::vector curr_points; std::vector curr_indices; for(size_t i = 0; i < task->p.points.size(); i++){ if(poly_key.at(i)){ curr_points.push_back(task->p.points.at(i)); curr_indices.push_back(i); } } LP_Polygon poly(std::move(curr_points)); size_t poly_size = poly.points.size(); double triangulation = INFINITY; double min_triangulation = INFINITY; // EDGE CASES if(poly_size == 3){ (*m)[poly_key] = 0.; return 0.; } if(poly_size == 4){ Line cut1(curr_points[0], curr_points[2]); Line cut2(curr_points[1], curr_points[3]); min_triangulation = std::min(cut1.length, cut2.length); (*m)[poly_key] = min_triangulation; return min_triangulation; } for (size_t point_idx = 0; point_idx < poly_size; point_idx++){ Line cut(poly.points.at( (point_idx + poly_size - 1) % poly_size ), poly.points.at( (point_idx + poly_size + 1) % poly_size )); std::vector sub_indices = curr_indices; sub_indices.erase(sub_indices.begin() + point_idx); triangulation = triangulate_subpolys( create_key( &sub_indices ), m ) + cut.length; min_triangulation = std::min(triangulation, min_triangulation); // if(triangulation < min_triangulation) min_triangulation = triangulation; } (*m)[poly_key] = min_triangulation; return min_triangulation; } //########################### dynamic something but better //############################################################ MAIN int main(){ //############################### MULTI THREAD // active_producents = active_consuments = 0; // task_queue_ptr queue_ptr = std::make_shared(); // std::vector threads; // for(unsigned int i = 0; i < NUM_PRODUCENTS; i++){ // threads.emplace_back(produce_thread, queue_ptr); // } // for(unsigned int i = 0; i < NUM_WORKERS; i++){ // threads.emplace_back(consume_thread, queue_ptr); // } // for(auto& t : threads){ // t.join(); // os_l.lock(); // os << "joined" << std::endl; // os_l.unlock(); // } //############################### SINGLE THREAD // while(1){ // LP_Task_p tp = pickup_task(); // if(!tp) break; // Task t(std::move(tp)); // t.solve_stupid(); // submit_task(std::move(t.unwrap())); // } //############################### CONNECTION TEST // test(); //############################### TRIANGULATION TEST task_test(); }