// Area.cpp // Copyright 2011, Dan Heeks // This program is released under the BSD license. See the file COPYING for details. #include "Area.h" #include "AreaOrderer.h" #include double CArea::m_accuracy = 0.01; double CArea::m_units = 1.0; bool CArea::m_clipper_simple = false; double CArea::m_clipper_clean_distance = 0.0; bool CArea::m_fit_arcs = true; int CArea::m_min_arc_points = 4; int CArea::m_max_arc_points = 100; double CArea::m_single_area_processing_length = 0.0; double CArea::m_processing_done = 0.0; bool CArea::m_please_abort = false; double CArea::m_MakeOffsets_increment = 0.0; double CArea::m_split_processing_length = 0.0; bool CArea::m_set_processing_length_in_split = false; double CArea::m_after_MakeOffsets_length = 0.0; //static const double PI = 3.1415926535897932; #define _CAREA_PARAM_DEFINE(_class,_type,_name) \ _type CArea::get_##_name() {return _class::_name;}\ void CArea::set_##_name(_type _name) {_class::_name = _name;} #define CAREA_PARAM_DEFINE(_type,_name) \ _type CArea::get_##_name() {return m_##_name;}\ void CArea::set_##_name(_type _name) {m_##_name = _name;} _CAREA_PARAM_DEFINE(Point,double,tolerance); CAREA_PARAM_DEFINE(bool,fit_arcs) CAREA_PARAM_DEFINE(bool,clipper_simple); CAREA_PARAM_DEFINE(double,clipper_clean_distance); CAREA_PARAM_DEFINE(double,accuracy); CAREA_PARAM_DEFINE(double,units); CAREA_PARAM_DEFINE(short,min_arc_points); CAREA_PARAM_DEFINE(short,max_arc_points); CAREA_PARAM_DEFINE(double,clipper_scale); void CArea::append(const CCurve& curve) { m_curves.push_back(curve); } void CArea::FitArcs(){ for(std::list::iterator It = m_curves.begin(); It != m_curves.end(); It++) { CCurve& curve = *It; curve.FitArcs(); } } Point CArea::NearestPoint(const Point& p)const { double best_dist = 0.0; Point best_point = Point(0, 0); for(std::list::const_iterator It = m_curves.begin(); It != m_curves.end(); It++) { const CCurve& curve = *It; Point near_point = curve.NearestPoint(p); double dist = near_point.dist(p); if(It == m_curves.begin() || dist < best_dist) { best_dist = dist; best_point = near_point; } } return best_point; } void CArea::ChangeStartToNearest(const Point *point, double min_dist) { for(std::list::iterator It=m_curves.begin(),ItNext=It; It != m_curves.end(); It=ItNext) { ++ItNext; if(It->m_vertices.size()<=1) m_curves.erase(It); } if(m_curves.empty()) return; std::list curves; Point p; if(point) p =*point; if(min_dist < Point::tolerance) min_dist = Point::tolerance; while(m_curves.size()) { std::list::iterator It=m_curves.begin(); std::list::iterator ItBest=It++; Point best_point = ItBest->NearestPoint(p); double best_dist = p.dist(best_point); for(; It != m_curves.end(); ++It) { const CCurve& curve = *It; Point near_point; double dist; if(min_dist>Point::tolerance && !curve.IsClosed()) { double d1 = curve.m_vertices.front().m_p.dist(p); double d2 = curve.m_vertices.back().m_p.dist(p); if(d1IsClosed()) { ItBest->ChangeStart(best_point); }else{ double dfront = ItBest->m_vertices.front().m_p.dist(best_point); double dback = ItBest->m_vertices.back().m_p.dist(best_point); if(min_dist>Point::tolerance && dfront>min_dist && dback>min_dist) { ItBest->Break(best_point); m_curves.push_back(*ItBest); m_curves.back().ChangeEnd(best_point); ItBest->ChangeStart(best_point); }else if(dfront>dback) ItBest->Reverse(); } curves.splice(curves.end(),m_curves,ItBest); p = curves.back().m_vertices.back().m_p; } m_curves.splice(m_curves.end(),curves); } void CArea::GetBox(CBox2D &box) { for(std::list::iterator It = m_curves.begin(); It != m_curves.end(); It++) { CCurve& curve = *It; curve.GetBox(box); } } void CArea::Reorder() { // curves may have been added with wrong directions // test all kurves to see which one are outsides and which are insides and // make sure outsides are anti-clockwise and insides are clockwise // returns 0, if the curves are OK // returns 1, if the curves are overlapping CAreaOrderer ao; for(std::list::iterator It = m_curves.begin(), ItNext=It; It != m_curves.end(); It=ItNext) { ++ItNext; CCurve& curve = *It; if(!It->IsClosed()) continue; ao.Insert(make_shared(curve)); if(m_set_processing_length_in_split) { CArea::m_processing_done += (m_split_processing_length / m_curves.size()); } m_curves.erase(It); } if(ao.m_top_level) ao.m_top_level->GetArea(*this); } class ZigZag { public: CCurve zig; CCurve zag; ZigZag(const CCurve& Zig, const CCurve& Zag):zig(Zig), zag(Zag){} }; static double stepover_for_pocket = 0.0; static std::list zigzag_list_for_zigs; static std::list *curve_list_for_zigs = NULL; static bool rightward_for_zigs = true; static double sin_angle_for_zigs = 0.0; static double cos_angle_for_zigs = 0.0; static double sin_minus_angle_for_zigs = 0.0; static double cos_minus_angle_for_zigs = 0.0; static double one_over_units = 0.0; static Point rotated_point(const Point &p) { return Point(p.x * cos_angle_for_zigs - p.y * sin_angle_for_zigs, p.x * sin_angle_for_zigs + p.y * cos_angle_for_zigs); } static Point unrotated_point(const Point &p) { return Point(p.x * cos_minus_angle_for_zigs - p.y * sin_minus_angle_for_zigs, p.x * sin_minus_angle_for_zigs + p.y * cos_minus_angle_for_zigs); } static CVertex rotated_vertex(const CVertex &v) { if(v.m_type) { return CVertex(v.m_type, rotated_point(v.m_p), rotated_point(v.m_c)); } return CVertex(v.m_type, rotated_point(v.m_p), Point(0, 0)); } static CVertex unrotated_vertex(const CVertex &v) { if(v.m_type) { return CVertex(v.m_type, unrotated_point(v.m_p), unrotated_point(v.m_c)); } return CVertex(v.m_type, unrotated_point(v.m_p), Point(0, 0)); } static void rotate_area(CArea &a) { for(std::list::iterator It = a.m_curves.begin(); It != a.m_curves.end(); It++) { CCurve& curve = *It; for(std::list::iterator CIt = curve.m_vertices.begin(); CIt != curve.m_vertices.end(); CIt++) { CVertex& vt = *CIt; vt = rotated_vertex(vt); } } } void test_y_point(int i, const Point& p, Point& best_p, bool &found, int &best_index, double y, bool left_not_right) { // only consider points at y if(fabs(p.y - y) < 0.002 * one_over_units) { if(found) { // equal high point if(left_not_right) { // use the furthest left point if(p.x < best_p.x) { best_p = p; best_index = i; } } else { // use the furthest right point if(p.x > best_p.x) { best_p = p; best_index = i; } } } else { best_p = p; best_index = i; found = true; } } } static void make_zig_curve(const CCurve& input_curve, double y0, double y) { CCurve curve(input_curve); if(rightward_for_zigs) { if(curve.IsClockwise()) curve.Reverse(); } else { if(!curve.IsClockwise()) curve.Reverse(); } // find a high point to start looking from Point top_left; int top_left_index = 0; bool top_left_found = false; Point top_right; int top_right_index = 0; bool top_right_found = false; Point bottom_left; int bottom_left_index = 0; bool bottom_left_found = false; int i =0; for(std::list::const_iterator VIt = curve.m_vertices.begin(); VIt != curve.m_vertices.end(); VIt++, i++) { const CVertex& vertex = *VIt; test_y_point(i, vertex.m_p, top_right, top_right_found, top_right_index, y, !rightward_for_zigs); test_y_point(i, vertex.m_p, top_left, top_left_found, top_left_index, y, rightward_for_zigs); test_y_point(i, vertex.m_p, bottom_left, bottom_left_found, bottom_left_index, y0, rightward_for_zigs); } int start_index = 0; int end_index = 0; int zag_end_index = 0; if(bottom_left_found)start_index = bottom_left_index; else if(top_left_found)start_index = top_left_index; if(top_right_found) { end_index = top_right_index; zag_end_index = top_left_index; } else { end_index = bottom_left_index; zag_end_index = bottom_left_index; } if(end_index <= start_index)end_index += (i-1); if(zag_end_index <= start_index)zag_end_index += (i-1); CCurve zig, zag; bool zig_started = false; bool zig_finished = false; bool zag_finished = false; int v_index = 0; for(int i = 0; i < 2; i++) { // process the curve twice because we don't know where it will start if(zag_finished) break; for(std::list::const_iterator VIt = curve.m_vertices.begin(); VIt != curve.m_vertices.end(); VIt++) { if(i == 1 && VIt == curve.m_vertices.begin()) { continue; } const CVertex& vertex = *VIt; if(zig_finished) { zag.m_vertices.push_back(unrotated_vertex(vertex)); if(v_index == zag_end_index) { zag_finished = true; break; } } else if(zig_started) { zig.m_vertices.push_back(unrotated_vertex(vertex)); if(v_index == end_index) { zig_finished = true; if(v_index == zag_end_index) { zag_finished = true; break; } zag.m_vertices.push_back(unrotated_vertex(vertex)); } } else { if(v_index == start_index) { zig.m_vertices.push_back(unrotated_vertex(vertex)); zig_started = true; } } v_index++; } } if(zig_finished) zigzag_list_for_zigs.push_back(ZigZag(zig, zag)); } void make_zig(const CArea &a, double y0, double y) { for(std::list::const_iterator It = a.m_curves.begin(); It != a.m_curves.end(); It++) { const CCurve &curve = *It; make_zig_curve(curve, y0, y); } } std::list< std::list > reorder_zig_list_list; void add_reorder_zig(ZigZag &zigzag) { // look in existing lists // see if the zag is part of an existing zig if(zigzag.zag.m_vertices.size() > 1) { const Point& zag_e = zigzag.zag.m_vertices.front().m_p; bool zag_removed = false; for(std::list< std::list >::iterator It = reorder_zig_list_list.begin(); It != reorder_zig_list_list.end() && !zag_removed; It++) { std::list &zigzag_list = *It; for(std::list::iterator It2 = zigzag_list.begin(); It2 != zigzag_list.end() && !zag_removed; It2++) { const ZigZag& z = *It2; for(std::list::const_iterator It3 = z.zig.m_vertices.begin(); It3 != z.zig.m_vertices.end() && !zag_removed; It3++) { const CVertex &v = *It3; if((fabs(zag_e.x - v.m_p.x) < (0.002 * one_over_units)) && (fabs(zag_e.y - v.m_p.y) < (0.002 * one_over_units))) { // remove zag from zigzag zigzag.zag.m_vertices.clear(); zag_removed = true; } } } } } // see if the zigzag can join the end of an existing list const Point& zig_s = zigzag.zig.m_vertices.front().m_p; for(std::list< std::list >::iterator It = reorder_zig_list_list.begin(); It != reorder_zig_list_list.end(); It++) { std::list &zigzag_list = *It; const ZigZag& last_zigzag = zigzag_list.back(); const Point& e = last_zigzag.zig.m_vertices.back().m_p; if((fabs(zig_s.x - e.x) < (0.002 * one_over_units)) && (fabs(zig_s.y - e.y) < (0.002 * one_over_units))) { zigzag_list.push_back(zigzag); return; } } // else add a new list std::list zigzag_list; zigzag_list.push_back(zigzag); reorder_zig_list_list.push_back(zigzag_list); } void reorder_zigs() { for(std::list::iterator It = zigzag_list_for_zigs.begin(); It != zigzag_list_for_zigs.end(); It++) { ZigZag &zigzag = *It; add_reorder_zig(zigzag); } zigzag_list_for_zigs.clear(); for(std::list< std::list >::iterator It = reorder_zig_list_list.begin(); It != reorder_zig_list_list.end(); It++) { std::list &zigzag_list = *It; if(zigzag_list.size() == 0)continue; curve_list_for_zigs->push_back(CCurve()); for(std::list::const_iterator It = zigzag_list.begin(); It != zigzag_list.end();) { const ZigZag &zigzag = *It; for(std::list::const_iterator It2 = zigzag.zig.m_vertices.begin(); It2 != zigzag.zig.m_vertices.end(); It2++) { if(It2 == zigzag.zig.m_vertices.begin() && It != zigzag_list.begin())continue; // only add the first vertex if doing the first zig const CVertex &v = *It2; curve_list_for_zigs->back().m_vertices.push_back(v); } It++; if(It == zigzag_list.end()) { for(std::list::const_iterator It2 = zigzag.zag.m_vertices.begin(); It2 != zigzag.zag.m_vertices.end(); It2++) { if(It2 == zigzag.zag.m_vertices.begin())continue; // don't add the first vertex of the zag const CVertex &v = *It2; curve_list_for_zigs->back().m_vertices.push_back(v); } } } } reorder_zig_list_list.clear(); } static void zigzag(const CArea &input_a) { if(input_a.m_curves.size() == 0) { CArea::m_processing_done += CArea::m_single_area_processing_length; return; } one_over_units = 1 / CArea::m_units; CArea a(input_a); rotate_area(a); CBox2D b; a.GetBox(b); double x0 = b.MinX() - 1.0; double x1 = b.MaxX() + 1.0; double height = b.MaxY() - b.MinY(); int num_steps = int(height / stepover_for_pocket + 1); double y = b.MinY();// + 0.1 * one_over_units; Point null_point(0, 0); rightward_for_zigs = true; if(CArea::m_please_abort)return; double step_percent_increment = 0.8 * CArea::m_single_area_processing_length / num_steps; for(int i = 0; i &curve_list, const CAreaPocketParams ¶ms)const { CArea::m_processing_done = 0.0; double save_units = CArea::m_units; CArea::m_units = 1.0; std::list areas; m_split_processing_length = 50.0; // jump to 50 percent after split m_set_processing_length_in_split = true; Split(areas); m_set_processing_length_in_split = false; CArea::m_processing_done = m_split_processing_length; CArea::m_units = save_units; if(areas.size() == 0)return; double single_area_length = 50.0 / areas.size(); for(std::list::iterator It = areas.begin(); It != areas.end(); It++) { CArea::m_single_area_processing_length = single_area_length; CArea &ar = *It; ar.MakePocketToolpath(curve_list, params); } } void CArea::MakePocketToolpath(std::list &curve_list, const CAreaPocketParams ¶ms)const { double radians_angle = params.zig_angle * PI / 180; sin_angle_for_zigs = sin(-radians_angle); cos_angle_for_zigs = cos(-radians_angle); sin_minus_angle_for_zigs = sin(radians_angle); cos_minus_angle_for_zigs = cos(radians_angle); stepover_for_pocket = params.stepover; CArea a_offset = *this; double current_offset = params.tool_radius + params.extra_offset; a_offset.Offset(current_offset); if(params.mode == ZigZagPocketMode || params.mode == ZigZagThenSingleOffsetPocketMode) { curve_list_for_zigs = &curve_list; zigzag(a_offset); } else if(params.mode == SpiralPocketMode) { std::list m_areas; a_offset.Split(m_areas); if(CArea::m_please_abort)return; if(m_areas.size() == 0) { CArea::m_processing_done += CArea::m_single_area_processing_length; return; } CArea::m_single_area_processing_length /= m_areas.size(); for(std::list::iterator It = m_areas.begin(); It != m_areas.end(); It++) { CArea &a2 = *It; a2.MakeOnePocketCurve(curve_list, params); } } if(params.mode == SingleOffsetPocketMode || params.mode == ZigZagThenSingleOffsetPocketMode) { // add the single offset too for(std::list::iterator It = a_offset.m_curves.begin(); It != a_offset.m_curves.end(); It++) { CCurve& curve = *It; curve_list.push_back(curve); } } } void CArea::Split(std::list &m_areas)const { if(HolesLinked()) { for(std::list::const_iterator It = m_curves.begin(); It != m_curves.end(); It++) { const CCurve& curve = *It; m_areas.push_back(CArea()); m_areas.back().m_curves.push_back(curve); } } else { CArea a = *this; a.Reorder(); if(CArea::m_please_abort)return; for(std::list::const_iterator It = a.m_curves.begin(); It != a.m_curves.end(); It++) { const CCurve& curve = *It; if(curve.IsClockwise()) { if(m_areas.size() > 0) m_areas.back().m_curves.push_back(curve); } else { m_areas.push_back(CArea()); m_areas.back().m_curves.push_back(curve); } } } } double CArea::GetArea(bool always_add)const { // returns the area of the area double area = 0.0; for(std::list::const_iterator It = m_curves.begin(); It != m_curves.end(); It++) { const CCurve& curve = *It; double a = curve.GetArea(); if(always_add)area += fabs(a); else area += a; } return area; } eOverlapType GetOverlapType(const CCurve& c1, const CCurve& c2) { CArea a1; a1.m_curves.push_back(c1); CArea a2; a2.m_curves.push_back(c2); return GetOverlapType(a1, a2); } eOverlapType GetOverlapType(const CArea& a1, const CArea& a2) { CArea A1(a1); A1.Subtract(a2); if(A1.m_curves.size() == 0) { return eInside; } CArea A2(a2); A2.Subtract(a1); if(A2.m_curves.size() == 0) { return eOutside; } A1 = a1; A1.Intersect(a2); if(A1.m_curves.size() == 0) { return eSiblings; } return eCrossing; } bool IsInside(const Point& p, const CCurve& c) { CArea a; a.m_curves.push_back(c); return IsInside(p, a); } bool IsInside(const Point& p, const CArea& a) { CArea a2; CCurve c; c.m_vertices.push_back(CVertex(Point(p.x - 0.01, p.y - 0.01))); c.m_vertices.push_back(CVertex(Point(p.x + 0.01, p.y - 0.01))); c.m_vertices.push_back(CVertex(Point(p.x + 0.01, p.y + 0.01))); c.m_vertices.push_back(CVertex(Point(p.x - 0.01, p.y + 0.01))); c.m_vertices.push_back(CVertex(Point(p.x - 0.01, p.y - 0.01))); a2.m_curves.push_back(c); a2.Intersect(a); if(fabs(a2.GetArea()) < 0.0004)return false; return true; } void CArea::SpanIntersections(const Span& span, std::list &pts)const { // this returns all the intersections of this area with the given span, ordered along the span // get all points where this area's curves intersect the span std::list pts2; for(std::list::const_iterator It = m_curves.begin(); It != m_curves.end(); It++) { const CCurve &c = *It; c.SpanIntersections(span, pts2); } // order them along the span std::multimap ordered_points; for(std::list::iterator It = pts2.begin(); It != pts2.end(); It++) { Point &p = *It; double t; if(span.On(p, &t)) { ordered_points.insert(std::make_pair(t, p)); } } // add them to the given list of points for(std::multimap::iterator It = ordered_points.begin(); It != ordered_points.end(); It++) { Point p = It->second; pts.push_back(p); } } void CArea::CurveIntersections(const CCurve& curve, std::list &pts)const { // this returns all the intersections of this area with the given curve, ordered along the curve std::list spans; curve.GetSpans(spans); for(std::list::iterator It = spans.begin(); It != spans.end(); It++) { Span& span = *It; std::list pts2; SpanIntersections(span, pts2); for(std::list::iterator It = pts2.begin(); It != pts2.end(); It++) { Point &pt = *It; if(pts.size() == 0) { pts.push_back(pt); } else { if(pt != pts.back())pts.push_back(pt); } } } } class ThickLine { public: CArea m_area; CCurve m_curve; ThickLine(const CCurve& curve) { m_curve = curve; m_area.append(curve); m_area.Thicken(0.001); } }; void CArea::InsideCurves(const CCurve& curve, std::list &curves_inside)const { //1. find the intersectionpoints between these two curves. std::list pts; CurveIntersections(curve, pts); //2.seperate curve2 in multiple curves between these intersections. std::list separate_curves; curve.ExtractSeparateCurves(pts, separate_curves); //3. if the midpoint of a seperate curve lies in a1, then we return it. for(std::list::iterator It = separate_curves.begin(); It != separate_curves.end(); It++) { CCurve &curve = *It; double length = curve.Perim(); Point mid_point = curve.PerimToPoint(length * 0.5); if(IsInside(mid_point, *this))curves_inside.push_back(curve); } }