// AreaPocket.cpp // Copyright 2011, Dan Heeks // This program is released under the BSD license. See the file COPYING for details. // implements CArea::MakeOnePocketCurve #include "Area.h" #include #include static const CAreaPocketParams* pocket_params = NULL; class IslandAndOffset { public: const CCurve* island; CArea offset; std::list island_inners; std::list touching_offsets; IslandAndOffset(const CCurve* Island) { island = Island; offset.m_curves.push_back(*island); offset.m_curves.back().Reverse(); offset.Offset(-pocket_params->stepover); if(offset.m_curves.size() > 1) { for(std::list::iterator It = offset.m_curves.begin(); It != offset.m_curves.end(); It++) { if(It == offset.m_curves.begin())continue; island_inners.push_back(*It); island_inners.back().Reverse(); } offset.m_curves.resize(1); } } }; class CurveTree { static std::list to_do_list_for_MakeOffsets; void MakeOffsets2(); static std::list islands_added; public: Point point_on_parent; CCurve curve; std::list inners; std::list offset_islands; CurveTree(const CCurve &c) { curve = c; } ~CurveTree(){} void MakeOffsets(); }; std::list CurveTree::islands_added; class GetCurveItem { public: CurveTree* curve_tree; std::list::iterator EndIt; static std::list to_do_list; GetCurveItem(CurveTree* ct, std::list::iterator EIt):curve_tree(ct), EndIt(EIt){} void GetCurve(CCurve& output); CVertex& back(){std::list::iterator It = EndIt; It--; return *It;} }; std::list GetCurveItem::to_do_list; std::list CurveTree::to_do_list_for_MakeOffsets; void GetCurveItem::GetCurve(CCurve& output) { // walk around the curve adding spans to output until we get to an inner's point_on_parent // then add a line from the inner's point_on_parent to inner's start point, then GetCurve from inner // add start point if(CArea::m_please_abort)return; output.m_vertices.insert(this->EndIt, CVertex(curve_tree->curve.m_vertices.front())); std::list inners_to_visit; for(std::list::iterator It2 = curve_tree->inners.begin(); It2 != curve_tree->inners.end(); It2++) { inners_to_visit.push_back(*It2); } const CVertex* prev_vertex = NULL; for(std::list::iterator It = curve_tree->curve.m_vertices.begin(); It != curve_tree->curve.m_vertices.end(); It++) { const CVertex& vertex = *It; if(prev_vertex) { Span span(prev_vertex->m_p, vertex); // order inners on this span std::multimap ordered_inners; for(std::list::iterator It2 = inners_to_visit.begin(); It2 != inners_to_visit.end();) { CurveTree *inner = *It2; double t; if(span.On(inner->point_on_parent, &t)) { ordered_inners.insert(std::make_pair(t, inner)); It2 = inners_to_visit.erase(It2); } else { It2++; } if(CArea::m_please_abort)return; } if(CArea::m_please_abort)return; for(std::multimap::iterator It2 = ordered_inners.begin(); It2 != ordered_inners.end(); It2++) { CurveTree& inner = *(It2->second); if(inner.point_on_parent.dist(back().m_p) > 0.01/CArea::m_units) { output.m_vertices.insert(this->EndIt, CVertex(vertex.m_type, inner.point_on_parent, vertex.m_c)); } if(CArea::m_please_abort)return; // vertex add after GetCurve std::list::iterator VIt = output.m_vertices.insert(this->EndIt, CVertex(inner.point_on_parent)); //inner.GetCurve(output); GetCurveItem::to_do_list.emplace_back(&inner, VIt); } if(back().m_p != vertex.m_p)output.m_vertices.insert(this->EndIt, vertex); } prev_vertex = &vertex; } if(CArea::m_please_abort)return; for(std::list::iterator It2 = inners_to_visit.begin(); It2 != inners_to_visit.end(); It2++) { CurveTree &inner = *(*It2); if(inner.point_on_parent != back().m_p) { output.m_vertices.insert(this->EndIt, CVertex(inner.point_on_parent)); } if(CArea::m_please_abort)return; // vertex add after GetCurve std::list::iterator VIt = output.m_vertices.insert(this->EndIt, CVertex(inner.point_on_parent)); //inner.GetCurve(output); GetCurveItem::to_do_list.emplace_back(&inner, VIt); } } class IslandAndOffsetLink { public: const IslandAndOffset* island_and_offset; CurveTree* add_to; IslandAndOffsetLink(const IslandAndOffset* i, CurveTree* a){island_and_offset = i; add_to = a;} }; static Point GetNearestPoint(CurveTree* curve_tree, std::list &islands_added, const CCurve &test_curve, CurveTree** best_curve_tree) { // find nearest point to test_curve, from curve and all the islands in double best_dist; Point best_point = curve_tree->curve.NearestPoint(test_curve, &best_dist); *best_curve_tree = curve_tree; for(std::list::iterator It = islands_added.begin(); It != islands_added.end(); It++) { CurveTree* island = *It; double dist; Point p = island->curve.NearestPoint(test_curve, &dist); if(dist < best_dist) { *best_curve_tree = island; best_point = p; best_dist = dist; } } return best_point; } void CurveTree::MakeOffsets2() { // make offsets if(CArea::m_please_abort)return; CArea smaller; smaller.m_curves.push_back(curve); smaller.Offset(pocket_params->stepover); if(CArea::m_please_abort)return; // test islands for(std::list::iterator It = offset_islands.begin(); It != offset_islands.end();) { const IslandAndOffset* island_and_offset = *It; if(GetOverlapType(island_and_offset->offset, smaller) == eInside) It++; // island is still inside else { inners.push_back(new CurveTree(*island_and_offset->island)); islands_added.push_back(inners.back()); inners.back()->point_on_parent = curve.NearestPoint(*island_and_offset->island); if(CArea::m_please_abort)return; Point island_point = island_and_offset->island->NearestPoint(inners.back()->point_on_parent); if(CArea::m_please_abort)return; inners.back()->curve.ChangeStart(island_point); if(CArea::m_please_abort)return; // add the island offset's inner curves for(std::list::const_iterator It2 = island_and_offset->island_inners.begin(); It2 != island_and_offset->island_inners.end(); It2++) { const CCurve& island_inner = *It2; inners.back()->inners.push_back(new CurveTree(island_inner)); inners.back()->inners.back()->point_on_parent = inners.back()->curve.NearestPoint(island_inner); if(CArea::m_please_abort)return; Point island_point = island_inner.NearestPoint(inners.back()->inners.back()->point_on_parent); if(CArea::m_please_abort)return; inners.back()->inners.back()->curve.ChangeStart(island_point); to_do_list_for_MakeOffsets.push_back(inners.back()->inners.back()); // do it later, in a while loop if(CArea::m_please_abort)return; } smaller.Subtract(island_and_offset->offset); std::set added; std::list touching_list; for(std::list::const_iterator It2 = island_and_offset->touching_offsets.begin(); It2 != island_and_offset->touching_offsets.end(); It2++) { const IslandAndOffset* touching = *It2; touching_list.emplace_back(touching, inners.back()); added.insert(touching); } while(touching_list.size() > 0) { IslandAndOffsetLink touching = touching_list.front(); touching_list.pop_front(); touching.add_to->inners.push_back(new CurveTree(*touching.island_and_offset->island)); islands_added.push_back(touching.add_to->inners.back()); touching.add_to->inners.back()->point_on_parent = touching.add_to->curve.NearestPoint(*touching.island_and_offset->island); Point island_point = touching.island_and_offset->island->NearestPoint(touching.add_to->inners.back()->point_on_parent); touching.add_to->inners.back()->curve.ChangeStart(island_point); smaller.Subtract(touching.island_and_offset->offset); // add the island offset's inner curves for(std::list::const_iterator It2 = touching.island_and_offset->island_inners.begin(); It2 != touching.island_and_offset->island_inners.end(); It2++) { const CCurve& island_inner = *It2; touching.add_to->inners.back()->inners.push_back(new CurveTree(island_inner)); touching.add_to->inners.back()->inners.back()->point_on_parent = touching.add_to->inners.back()->curve.NearestPoint(island_inner); if(CArea::m_please_abort)return; Point island_point = island_inner.NearestPoint(touching.add_to->inners.back()->inners.back()->point_on_parent); if(CArea::m_please_abort)return; touching.add_to->inners.back()->inners.back()->curve.ChangeStart(island_point); to_do_list_for_MakeOffsets.push_back(touching.add_to->inners.back()->inners.back()); // do it later, in a while loop if(CArea::m_please_abort)return; } for(std::list::const_iterator It2 = touching.island_and_offset->touching_offsets.begin(); It2 != touching.island_and_offset->touching_offsets.end(); It2++) { if(added.find(*It2)==added.end() && ((*It2) != island_and_offset)) { touching_list.emplace_back(*It2, touching.add_to->inners.back()); added.insert(*It2); } } } if(CArea::m_please_abort)return; It = offset_islands.erase(It); for(std::set::iterator It2 = added.begin(); It2 != added.end(); It2++) { const IslandAndOffset* i = *It2; offset_islands.remove(i); } if(offset_islands.size() == 0)break; It = offset_islands.begin(); } } CArea::m_processing_done += CArea::m_MakeOffsets_increment; if(CArea::m_processing_done > CArea::m_after_MakeOffsets_length)CArea::m_processing_done = CArea::m_after_MakeOffsets_length; std::list separate_areas; smaller.Split(separate_areas); if(CArea::m_please_abort)return; for(std::list::iterator It = separate_areas.begin(); It != separate_areas.end(); It++) { CArea& separate_area = *It; CCurve& first_curve = separate_area.m_curves.front(); CurveTree* nearest_curve_tree = NULL; Point near_point = GetNearestPoint(this, islands_added, first_curve, &nearest_curve_tree); nearest_curve_tree->inners.push_back(new CurveTree(first_curve)); for(std::list::iterator It = offset_islands.begin(); It != offset_islands.end();It++) { const IslandAndOffset* island_and_offset = *It; if(GetOverlapType(island_and_offset->offset, separate_area) == eInside) nearest_curve_tree->inners.back()->offset_islands.push_back(island_and_offset); if(CArea::m_please_abort)return; } nearest_curve_tree->inners.back()->point_on_parent = near_point; if(CArea::m_please_abort)return; Point first_curve_point = first_curve.NearestPoint(nearest_curve_tree->inners.back()->point_on_parent); if(CArea::m_please_abort)return; nearest_curve_tree->inners.back()->curve.ChangeStart(first_curve_point); if(CArea::m_please_abort)return; to_do_list_for_MakeOffsets.push_back(nearest_curve_tree->inners.back()); // do it later, in a while loop if(CArea::m_please_abort)return; } } void CurveTree::MakeOffsets() { to_do_list_for_MakeOffsets.push_back(this); islands_added.clear(); while(to_do_list_for_MakeOffsets.size() > 0) { CurveTree* curve_tree = to_do_list_for_MakeOffsets.front(); to_do_list_for_MakeOffsets.pop_front(); curve_tree->MakeOffsets2(); } } void recur(std::list &arealist, const CArea& a1, const CAreaPocketParams ¶ms, int level) { //if(level > 3)return; // this makes arealist by recursively offsetting a1 inwards if(a1.m_curves.size() == 0) return; if(params.from_center) arealist.push_front(a1); else arealist.push_back(a1); CArea a_offset = a1; a_offset.Offset(params.stepover); // split curves into new areas if(CArea::HolesLinked()) { for(std::list::iterator It = a_offset.m_curves.begin(); It != a_offset.m_curves.end(); It++) { CArea a2; a2.m_curves.push_back(*It); recur(arealist, a2, params, level + 1); } } else { // split curves into new areas a_offset.Reorder(); CArea* a2 = NULL; for(std::list::iterator It = a_offset.m_curves.begin(); It != a_offset.m_curves.end(); It++) { CCurve& curve = *It; if(curve.IsClockwise()) { if(a2 != NULL) a2->m_curves.push_back(curve); } else { if(a2 != NULL) recur(arealist, *a2, params, level + 1); else a2 = new CArea(); a2->m_curves.push_back(curve); } } if(a2 != NULL) recur(arealist, *a2, params, level + 1); } } void MarkOverlappingOffsetIslands(std::list &offset_islands) { for(std::list::iterator It1 = offset_islands.begin(); It1 != offset_islands.end(); It1++) { std::list::iterator It2 = It1; It2++; for(;It2 != offset_islands.end(); It2++) { IslandAndOffset &o1 = *It1; IslandAndOffset &o2 = *It2; if(GetOverlapType(o1.offset, o2.offset) == eCrossing) { o1.touching_offsets.push_back(&o2); o2.touching_offsets.push_back(&o1); } } } } void CArea::MakeOnePocketCurve(std::list &curve_list, const CAreaPocketParams ¶ms)const { if(CArea::m_please_abort)return; #if 0 // simple offsets with feed or rapid joins CArea area_for_feed_possible = *this; area_for_feed_possible.Offset(-params.tool_radius - 0.01); CArea a_offset = *this; std::list arealist; recur(arealist, a_offset, params, 0); bool first = true; for(std::list::iterator It = arealist.begin(); It != arealist.end(); It++) { CArea& area = *It; for(std::list::iterator It = area.m_curves.begin(); It != area.m_curves.end(); It++) { CCurve& curve = *It; if(!first) { // try to join these curves with a feed move, if possible and not too long CCurve &prev_curve = curve_list.back(); const Point &prev_p = prev_curve.m_vertices.back().m_p; const Point &next_p = curve.m_vertices.front().m_p; if(feed_possible(area_for_feed_possible, prev_p, next_p, params.tool_radius)) { // join curves prev_curve += curve; } else { curve_list.push_back(curve); } } else { curve_list.push_back(curve); } first = false; } } #else pocket_params = ¶ms; if(m_curves.size() == 0) { CArea::m_processing_done += CArea::m_single_area_processing_length; return; } CurveTree top_level(m_curves.front()); std::list offset_islands; for(std::list::const_iterator It = m_curves.begin(); It != m_curves.end(); It++) { const CCurve& c = *It; if(It != m_curves.begin()) { IslandAndOffset island_and_offset(&c); offset_islands.push_back(island_and_offset); top_level.offset_islands.push_back(&(offset_islands.back())); if(m_please_abort)return; } } MarkOverlappingOffsetIslands(offset_islands); CArea::m_processing_done += CArea::m_single_area_processing_length * 0.1; double MakeOffsets_processing_length = CArea::m_single_area_processing_length * 0.8; CArea::m_after_MakeOffsets_length = CArea::m_processing_done + MakeOffsets_processing_length; double guess_num_offsets = sqrt(GetArea(true)) * 0.5 / params.stepover; CArea::m_MakeOffsets_increment = MakeOffsets_processing_length / guess_num_offsets; top_level.MakeOffsets(); if(CArea::m_please_abort)return; CArea::m_processing_done = CArea::m_after_MakeOffsets_length; curve_list.emplace_back(); CCurve& output = curve_list.back(); GetCurveItem::to_do_list.emplace_back(&top_level, output.m_vertices.end()); while(GetCurveItem::to_do_list.size() > 0) { GetCurveItem item = GetCurveItem::to_do_list.front(); item.GetCurve(output); GetCurveItem::to_do_list.pop_front(); } // delete curve_trees non-recursively std::list CurveTreeDestructList; for(std::list::iterator It = top_level.inners.begin(); It != top_level.inners.end(); It++) { CurveTreeDestructList.push_back(*It); } while(CurveTreeDestructList.size() > 0) { CurveTree* curve_tree = CurveTreeDestructList.front(); CurveTreeDestructList.pop_front(); for(std::list::iterator It = curve_tree->inners.begin(); It != curve_tree->inners.end(); It++) { CurveTreeDestructList.push_back(*It); } delete curve_tree; } CArea::m_processing_done += CArea::m_single_area_processing_length * 0.1; #endif }