static void traverse_pt(ClipperLib::PolyNodes &nodes, Slic3r::Polygons &retval) { /* use a nearest neighbor search to order these children TODO: supply start_near to chained_path() too? */ // collect ordering points Points ordering_points; ordering_points.reserve(nodes.size()); for (ClipperLib::PolyNodes::const_iterator it = nodes.begin(); it != nodes.end(); ++it) { Point p((*it)->Contour.front().X, (*it)->Contour.front().Y); ordering_points.push_back(p); } // perform the ordering ClipperLib::PolyNodes ordered_nodes; Slic3r::Geometry::chained_path_items(ordering_points, nodes, ordered_nodes); // push results recursively for (ClipperLib::PolyNodes::iterator it = ordered_nodes.begin(); it != ordered_nodes.end(); ++it) { // traverse the next depth traverse_pt((*it)->Childs, retval); Polygon p; ClipperPath_to_Slic3rMultiPoint((*it)->Contour, p); retval.push_back(p); if ((*it)->IsHole()) retval.back().reverse(); // ccw } }
Slic3r::Polygons ClipperPaths_to_Slic3rPolygons(const ClipperLib::Paths &input) { Slic3r::Polygons retval; retval.reserve(input.size()); for (ClipperLib::Paths::const_iterator it = input.begin(); it != input.end(); ++it) retval.emplace_back(ClipperPath_to_Slic3rPolygon(*it)); return retval; }
void Slic3rPolygons_to_ClipperPolygons(const Slic3r::Polygons &input, ClipperLib::Polygons &output) { output.clear(); for (Slic3r::Polygons::const_iterator it = input.begin(); it != input.end(); ++it) { ClipperLib::Polygon p; Slic3rPolygon_to_ClipperPolygon(*it, p); output.push_back(p); } }
void offset2(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float delta1, const float delta2, const ClipperLib::JoinType joinType, const double miterLimit) { if (delta1 * delta2 >= 0) { // Both deltas are the same signum offset(polygons, retval, delta1 + delta2, joinType, miterLimit); return; } #ifdef CLIPPER_UTILS_DEBUG BoundingBox bbox = get_extents(polygons); coordf_t stroke_width = scale_(0.005); static int iRun = 0; ++ iRun; bool flipY = false; SVG svg(debug_out_path("offset2-%d.svg", iRun), bbox, scale_(1.), flipY); for (Slic3r::Polygons::const_iterator it = polygons.begin(); it != polygons.end(); ++ it) svg.draw(it->lines(), "gray", stroke_width); #endif /* CLIPPER_UTILS_DEBUG */ // read input ClipperLib::Paths input; Slic3rMultiPoints_to_ClipperPaths(polygons, &input); // scale input scaleClipperPolygons(input); // prepare ClipperOffset object ClipperLib::ClipperOffset co; if (joinType == jtRound) { co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE); } else { co.MiterLimit = miterLimit; } // perform first offset ClipperLib::Paths output1; co.AddPaths(input, joinType, ClipperLib::etClosedPolygon); co.Execute(output1, delta1 * float(CLIPPER_OFFSET_SCALE)); #ifdef CLIPPER_UTILS_DEBUG svg.draw(output1, 1. / double(CLIPPER_OFFSET_SCALE), "red", stroke_width); #endif /* CLIPPER_UTILS_DEBUG */ // perform second offset co.Clear(); co.AddPaths(output1, joinType, ClipperLib::etClosedPolygon); co.Execute(*retval, delta2 * float(CLIPPER_OFFSET_SCALE)); #ifdef CLIPPER_UTILS_DEBUG svg.draw(*retval, 1. / double(CLIPPER_OFFSET_SCALE), "green", stroke_width); #endif /* CLIPPER_UTILS_DEBUG */ // unscale output unscaleClipperPolygons(*retval); }
void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polylines* retval, bool safety_offset_) { // transform input polygons into polylines Slic3r::Polylines polylines; polylines.reserve(subject.size()); for (Slic3r::Polygons::const_iterator polygon = subject.begin(); polygon != subject.end(); ++polygon) polylines.push_back(*polygon); // implicit call to split_at_first_point() // perform clipping _clipper(clipType, polylines, clip, retval, safety_offset_); /* If the split_at_first_point() call above happens to split the polygon inside the clipping area we would get two consecutive polylines instead of a single one, so we go through them in order to recombine continuous polylines. */ for (size_t i = 0; i < retval->size(); ++i) { for (size_t j = i+1; j < retval->size(); ++j) { if ((*retval)[i].points.back().coincides_with((*retval)[j].points.front())) { /* If last point of i coincides with first point of j, append points of j to i and delete j */ (*retval)[i].points.insert((*retval)[i].points.end(), (*retval)[j].points.begin()+1, (*retval)[j].points.end()); retval->erase(retval->begin() + j); --j; } else if ((*retval)[i].points.front().coincides_with((*retval)[j].points.back())) { /* If first point of i coincides with last point of j, prepend points of j to i and delete j */ (*retval)[i].points.insert((*retval)[i].points.begin(), (*retval)[j].points.begin(), (*retval)[j].points.end()-1); retval->erase(retval->begin() + j); --j; } else if ((*retval)[i].points.front().coincides_with((*retval)[j].points.front())) { /* Since Clipper does not preserve orientation of polylines, also check the case when first point of i coincides with first point of j. */ (*retval)[j].reverse(); (*retval)[i].points.insert((*retval)[i].points.begin(), (*retval)[j].points.begin(), (*retval)[j].points.end()-1); retval->erase(retval->begin() + j); --j; } else if ((*retval)[i].points.back().coincides_with((*retval)[j].points.back())) { /* Since Clipper does not preserve orientation of polylines, also check the case when last point of i coincides with last point of j. */ (*retval)[j].reverse(); (*retval)[i].points.insert((*retval)[i].points.end(), (*retval)[j].points.begin()+1, (*retval)[j].points.end()); retval->erase(retval->begin() + j); --j; } } } }
void diff(const SubjectType &subject, const Slic3r::ExPolygons &clip, ResultType* retval, bool safety_offset_) { Slic3r::Polygons pp; for (Slic3r::ExPolygons::const_iterator ex = clip.begin(); ex != clip.end(); ++ex) { Slic3r::Polygons ppp = *ex; pp.insert(pp.end(), ppp.begin(), ppp.end()); } diff(subject, pp, retval, safety_offset_); }
void union_(const Slic3r::Polygons &subject1, const Slic3r::Polygons &subject2, Slic3r::Polygons* retval, bool safety_offset) { Polygons pp = subject1; pp.insert(pp.end(), subject2.begin(), subject2.end()); union_(pp, retval, safety_offset); }