void GerberImporter::merge_paths(multi_linestring_type &destination, const linestring_type& source) { if (!destination.empty()) { multi_linestring_type::reverse_iterator ls; for (ls = destination.rbegin(); ls != destination.rend(); ls++) { if (bg::equals(ls->back(), source.front())) { ls->insert(ls->end(), source.begin() + 1, source.end()); break; } else if (bg::equals(ls->back(), source.back())) { ls->insert(ls->end(), source.rbegin() + 1, source.rend()); break; } /* * The following two cases does not happen very often, and they * usually happen when the size of destination is small (often 2), * therefore there shouldn't be the need to replace a standard * linestring_type (std::vector) with a std::deque */ else if (bg::equals(ls->front(), source.front())) { reverse(ls->begin(), ls->end()); ls->insert(ls->end(), source.begin() + 1, source.end()); break; } else if (bg::equals(ls->front(), source.back())) { reverse(ls->begin(), ls->end()); ls->insert(ls->end(), source.rbegin() + 1, source.rend()); break; } } if (ls == destination.rend()) { destination.push_back(source); } } else { destination.push_back(source); } }
void GerberImporter::simplify_paths(multi_linestring_type &paths) { for (auto path1 = paths.begin(); path1 != paths.end(); path1++) { if (!path1->empty()) { for (auto path2 = paths.begin(); path2 != paths.end(); path2++) { if (!path2->empty() && path1 != path2) { if (bg::equals(path1->back(), path2->front())) { path1->insert(path1->end(), make_move_iterator(next(path2->begin())), make_move_iterator(path2->end())); path2->clear(); } else if (bg::equals(path1->back(), path2->back())) { if (path1->size() < path2->size()) { path2->insert(path2->end(), make_move_iterator(next(path1->rbegin())), make_move_iterator(path1->rend())); path1->swap(*path2); } else path1->insert(path1->end(), make_move_iterator(next(path2->rbegin())), make_move_iterator(path2->rend())); path2->clear(); } else if (bg::equals(path1->front(), path2->back())) { path2->insert(path2->end(), make_move_iterator(next(path1->begin())), make_move_iterator(path1->end())); path1->swap(*path2); path2->clear(); } else if (bg::equals(path1->front(), path2->front())) { std::reverse(path1->begin(), path1->end()); path1->insert(path1->end(), make_move_iterator(next(path2->begin())), make_move_iterator(path2->end())); path2->clear(); } } } } } auto isEmpty = [&](const linestring_type& ls) { return ls.empty(); }; paths.erase(std::remove_if(paths.begin(), paths.end(), isEmpty), paths.end()); }
unique_ptr<vector<polygon_type> > Surface_vectorial::offset_polygon(const multi_polygon_type& input, const multi_polygon_type_fp& voronoi_polygons, multi_linestring_type& toolpath, bool& contentions, coordinate_type offset, size_t index, unsigned int steps, bool do_voronoi) { unique_ptr<vector<polygon_type> > polygons (new vector<polygon_type>(steps)); list<list<const ring_type *> > rings (steps); auto ring_i = rings.begin(); point_type last_point; auto push_point = [&](const point_type& point) { toolpath.back().push_back(point); }; auto copy_ring_to_toolpath = [&](const ring_type& ring, unsigned int start) { const auto size_minus_1 = ring.size() - 1; unsigned int i = start; do { push_point(ring[i]); i = (i + 1) % size_minus_1; } while (i != start); push_point(ring[i]); last_point = ring[i]; }; auto find_first_nonempty = [&]() { for (auto i = rings.begin(); i != rings.end(); i++) if (!i->empty()) return i; return rings.end(); }; auto find_closest_point_index = [&](const ring_type& ring) { const unsigned int size = ring.size(); auto min_distance = bg::comparable_distance(ring[0], last_point); unsigned int index = 0; for (unsigned int i = 1; i < size; i++) { const auto distance = bg::comparable_distance(ring[i], last_point); if (distance < min_distance) { min_distance = distance; index = i; } } return index; }; toolpath.push_back(linestring_type()); bool outer_collapsed = false; for (unsigned int i = 0; i < steps; i++) { coordinate_type expand_by; if (!do_voronoi) { // Number of rings is the same as the number of steps. expand_by = offset * (i+1); } else { // Voronoi lines are on the boundary and shared between // multi_polygons so we only need half as many of them. double factor = ((1-double(steps))/2 + i); if (factor > 0) { continue; // Don't need it. } expand_by = offset * factor; } multi_polygon_type integral_voronoi_polygons; bg::convert(voronoi_polygons, integral_voronoi_polygons); polygon_type masked_milling_poly = do_voronoi ? integral_voronoi_polygons[index] : input[index]; multi_polygon_type masked_milling_polys; if (mask) { bg::intersection(masked_milling_poly, *(mask->vectorial_surface), masked_milling_polys); } else { bg::convert(masked_milling_poly, masked_milling_polys); } if (expand_by == 0) { (*polygons)[i] = masked_milling_polys[0]; } else { multi_polygon_type_fp mpoly_temp_fp; polygon_type_fp input_fp; bg::convert(masked_milling_polys[0], input_fp); // Buffer should be done on floating point polygons. bg::buffer(input_fp, mpoly_temp_fp, bg::strategy::buffer::distance_symmetric<coordinate_type>(expand_by), bg::strategy::buffer::side_straight(), bg::strategy::buffer::join_round(points_per_circle), //bg::strategy::buffer::join_miter(numeric_limits<coordinate_type>::max()), bg::strategy::buffer::end_flat(), bg::strategy::buffer::point_circle(30)); auto mpoly_fp = make_shared<multi_polygon_type_fp>(); if (!do_voronoi) { bg::intersection(mpoly_temp_fp[0], voronoi_polygons[index], *mpoly_fp); } else { polygon_type_fp min_shape; bg::convert(input[index], min_shape); bg::union_(mpoly_temp_fp[0], min_shape, *mpoly_fp); } bg::convert((*mpoly_fp)[0], (*polygons)[i]); if (!bg::equals((*mpoly_fp)[0], mpoly_temp_fp[0])) contentions = true; } if (i == 0) copy_ring_to_toolpath((*polygons)[i].outer(), 0); else { if (!outer_collapsed && bg::equals((*polygons)[i].outer(), (*polygons)[i - 1].outer())) outer_collapsed = true; if (!outer_collapsed) copy_ring_to_toolpath((*polygons)[i].outer(), find_closest_point_index((*polygons)[i].outer())); } for (const ring_type& ring : (*polygons)[i].inners()) ring_i->push_back(&ring); ++ring_i; } ring_i = find_first_nonempty(); while (ring_i != rings.end()) { const ring_type *biggest = ring_i->front(); const ring_type *prev = biggest; auto ring_j = next(ring_i); toolpath.push_back(linestring_type()); copy_ring_to_toolpath(*biggest, 0); while (ring_j != rings.end()) { list<const ring_type *>::iterator j; for (j = ring_j->begin(); j != ring_j->end(); j++) { if (bg::equals(**j, *prev)) { ring_j->erase(j); break; } else { if (bg::covered_by(**j, *prev)) { auto index = find_closest_point_index(**j); ring_type ring (prev->rbegin(), prev->rend()); linestring_type segment; segment.push_back((**j)[index]); segment.push_back(last_point); if (bg::covered_by(segment, ring)) { copy_ring_to_toolpath(**j, index); prev = *j; ring_j->erase(j); break; } } } } if (j == ring_j->end()) ring_j = rings.end(); else ++ring_j; } ring_i->erase(ring_i->begin()); ring_i = find_first_nonempty(); } return polygons; }