// This is a safe variant of the polygon offset, tailored for a single ExPolygon: // a single polygon with multiple non-overlapping holes. // Each contour and hole is offsetted separately, then the holes are subtracted from the outer contours. ClipperLib::Paths _offset(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType, double miterLimit) { // printf("new ExPolygon offset\n"); // 1) Offset the outer contour. const float delta_scaled = delta * float(CLIPPER_OFFSET_SCALE); ClipperLib::Paths contours; { ClipperLib::Path input = Slic3rMultiPoint_to_ClipperPath(expolygon.contour); scaleClipperPolygon(input); ClipperLib::ClipperOffset co; if (joinType == jtRound) co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE); else co.MiterLimit = miterLimit; co.ShortestEdgeLength = double(std::abs(delta_scaled * CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR)); co.AddPath(input, joinType, ClipperLib::etClosedPolygon); co.Execute(contours, delta_scaled); } // 2) Offset the holes one by one, collect the results. ClipperLib::Paths holes; { holes.reserve(expolygon.holes.size()); for (Polygons::const_iterator it_hole = expolygon.holes.begin(); it_hole != expolygon.holes.end(); ++ it_hole) { ClipperLib::Path input = Slic3rMultiPoint_to_ClipperPath_reversed(*it_hole); scaleClipperPolygon(input); ClipperLib::ClipperOffset co; if (joinType == jtRound) co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE); else co.MiterLimit = miterLimit; co.ShortestEdgeLength = double(std::abs(delta_scaled * CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR)); co.AddPath(input, joinType, ClipperLib::etClosedPolygon); ClipperLib::Paths out; co.Execute(out, - delta_scaled); holes.insert(holes.end(), out.begin(), out.end()); } } // 3) Subtract holes from the contours. ClipperLib::Paths output; if (holes.empty()) { output = std::move(contours); } else { ClipperLib::Clipper clipper; clipper.Clear(); clipper.AddPaths(contours, ClipperLib::ptSubject, true); clipper.AddPaths(holes, ClipperLib::ptClip, true); clipper.Execute(ClipperLib::ctDifference, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero); } // 4) Unscale the output. unscaleClipperPolygons(output); return output; }
T ClipperPaths_to_Slic3rMultiPoints(const ClipperLib::Paths &input) { T retval; for (ClipperLib::Paths::const_iterator it = input.begin(); it != input.end(); ++it) retval.push_back(ClipperPath_to_Slic3rMultiPoint<typename T::value_type>(*it)); return retval; }
Slic3r::Polylines ClipperPaths_to_Slic3rPolylines(const ClipperLib::Paths &input) { Slic3r::Polylines retval; retval.reserve(input.size()); for (ClipperLib::Paths::const_iterator it = input.begin(); it != input.end(); ++it) retval.emplace_back(ClipperPath_to_Slic3rPolyline(*it)); return retval; }
void scaleClipperPolygons(ClipperLib::Paths &polygons) { PROFILE_FUNC(); for (ClipperLib::Paths::iterator it = polygons.begin(); it != polygons.end(); ++it) for (ClipperLib::Path::iterator pit = (*it).begin(); pit != (*it).end(); ++pit) { pit->X <<= CLIPPER_OFFSET_POWER_OF_2; pit->Y <<= CLIPPER_OFFSET_POWER_OF_2; } }
void scaleClipperPolygons(ClipperLib::Paths &polygons, const double scale) { for (ClipperLib::Paths::iterator it = polygons.begin(); it != polygons.end(); ++it) { for (ClipperLib::Path::iterator pit = (*it).begin(); pit != (*it).end(); ++pit) { (*pit).X *= scale; (*pit).Y *= scale; } } }
void ClipperPaths_to_Slic3rMultiPoints(const ClipperLib::Paths &input, T &output) { output.clear(); for (ClipperLib::Paths::const_iterator it = input.begin(); it != input.end(); ++it) { typename T::value_type p; ClipperPath_to_Slic3rMultiPoint(*it, p); output.push_back(p); } }
void unscaleClipperPolygons(ClipperLib::Paths &polygons) { PROFILE_FUNC(); for (ClipperLib::Paths::iterator it = polygons.begin(); it != polygons.end(); ++it) for (ClipperLib::Path::iterator pit = (*it).begin(); pit != (*it).end(); ++pit) { pit->X += CLIPPER_OFFSET_SCALE_ROUNDING_DELTA; pit->Y += CLIPPER_OFFSET_SCALE_ROUNDING_DELTA; pit->X >>= CLIPPER_OFFSET_POWER_OF_2; pit->Y >>= CLIPPER_OFFSET_POWER_OF_2; } }
void ClipperPaths_to_Slic3rMultiPoints(const ClipperLib::Paths &input, T* output) { PROFILE_FUNC(); output->clear(); output->reserve(input.size()); for (ClipperLib::Paths::const_iterator it = input.begin(); it != input.end(); ++it) { typename T::value_type p; ClipperPath_to_Slic3rMultiPoint(*it, &p); output->push_back(p); } }
std::vector<polygon> polygon::from(const ClipperLib::Paths& paths, base_int maxDenom) { std::vector<polygon> ret; for(auto iter = paths.begin(); iter != paths.end(); iter++) { ret.push_back(polygon()); auto& polyRef = ret.back(); for(auto point = iter->begin(); point != iter->end(); point++) { polyRef.vertexes.push_back(vec2d(int_frac(point->X, maxDenom), int_frac(point->Y, maxDenom))); } } return ret; }
DLL_PUBLIC void CDECL add_offset_paths(ClipperLib::ClipperOffset *ptr, ClipperLib::IntPoint** paths, size_t* path_counts, size_t count, ClipperLib::JoinType joinType, ClipperLib::EndType endType) { ClipperLib::Paths vs = ClipperLib::Paths(); for(size_t i = 0; i < count; i++) { auto it = vs.emplace(vs.end()); for(size_t j = 0; j < path_counts[i]; j++) { it->emplace(it->end(), paths[i][j].X, paths[i][j].Y); } } try { ptr->AddPaths(vs, joinType, endType); } catch(ClipperLib::clipperException e) { printf(e.what()); } }
void ElementGeometryClipper::visitArea(const Area& area) { ClipperLib::Path areaShape; PointLocation pointLocation = setPath(quadKeyBbox_, area, areaShape); // 1. all geometry inside current quadkey: no need to truncate. if (pointLocation == PointLocation::AllInside) { callback_(area, quadKey_); return; } // 2. all geometry outside: skip if (pointLocation == PointLocation::AllOutside) { return; } ClipperLib::Paths solution; clipper_.AddPath(areaShape, ClipperLib::ptSubject, true); clipper_.AddPath(createPathFromBoundingBox(), ClipperLib::ptClip, true); clipper_.Execute(ClipperLib::ctIntersection, solution); clipper_.Clear(); // 3. way intersects border only once: store a copy with clipped geometry if (solution.size() == 1) { Area clippedArea; setData(clippedArea, area, solution[0]); callback_(clippedArea, quadKey_); } // 4. in this case, result should be stored as relation (collection of areas) else { Relation relation; relation.id = area.id; relation.tags = area.tags; relation.elements.reserve(solution.size()); for (auto it = solution.begin(); it != solution.end(); ++it) { auto clippedArea = std::make_shared<Area> (); clippedArea->id = area.id; setCoordinates(*clippedArea, *it); relation.elements.push_back(clippedArea); } callback_(relation, quadKey_); } }
DLL_PUBLIC bool CDECL add_paths(ClipperLib::Clipper *ptr, ClipperLib::IntPoint** paths, size_t* path_counts, size_t count, ClipperLib::PolyType polyType, bool closed) { ClipperLib::Paths vs = ClipperLib::Paths(); for(size_t i = 0; i < count; i++) { auto it = vs.emplace(vs.end()); for(size_t j = 0; j < path_counts[i]; j++) { it->emplace(it->end(), paths[i][j].X, paths[i][j].Y); } } bool result = false; try { result = ptr->AddPaths(vs, polyType, closed); } catch(ClipperLib::clipperException e) { printf(e.what()); } return result; }
// This is a safe variant of the polygon offset, tailored for a single ExPolygon: // a single polygon with multiple non-overlapping holes. // Each contour and hole is offsetted separately, then the holes are subtracted from the outer contours. void offset(const Slic3r::ExPolygons &expolygons, ClipperLib::Paths* retval, const float delta, ClipperLib::JoinType joinType, double miterLimit) { // printf("new ExPolygon offset\n"); const float delta_scaled = delta * float(CLIPPER_OFFSET_SCALE); ClipperLib::Paths contours; ClipperLib::Paths holes; contours.reserve(expolygons.size()); { size_t n_holes = 0; for (size_t i = 0; i < expolygons.size(); ++ i) n_holes += expolygons[i].holes.size(); holes.reserve(n_holes); } for (Slic3r::ExPolygons::const_iterator it_expoly = expolygons.begin(); it_expoly != expolygons.end(); ++ it_expoly) { // 1) Offset the outer contour. { ClipperLib::Path input; Slic3rMultiPoint_to_ClipperPath(it_expoly->contour, &input); scaleClipperPolygon(input); ClipperLib::ClipperOffset co; if (joinType == jtRound) co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE); else co.MiterLimit = miterLimit; co.AddPath(input, joinType, ClipperLib::etClosedPolygon); ClipperLib::Paths out; co.Execute(out, delta_scaled); contours.insert(contours.end(), out.begin(), out.end()); } // 2) Offset the holes one by one, collect the results. { for (Polygons::const_iterator it_hole = it_expoly->holes.begin(); it_hole != it_expoly->holes.end(); ++ it_hole) { ClipperLib::Path input; Slic3rMultiPoint_to_ClipperPath_reversed(*it_hole, &input); scaleClipperPolygon(input); ClipperLib::ClipperOffset co; if (joinType == jtRound) co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE); else co.MiterLimit = miterLimit; co.AddPath(input, joinType, ClipperLib::etClosedPolygon); ClipperLib::Paths out; co.Execute(out, - delta_scaled); holes.insert(holes.end(), out.begin(), out.end()); } } } // 3) Subtract holes from the contours. ClipperLib::Paths output; { ClipperLib::Clipper clipper; clipper.Clear(); clipper.AddPaths(contours, ClipperLib::ptSubject, true); clipper.AddPaths(holes, ClipperLib::ptClip, true); clipper.Execute(ClipperLib::ctDifference, *retval, ClipperLib::pftNonZero, ClipperLib::pftNonZero); } // 4) Unscale the output. unscaleClipperPolygons(*retval); }
void SVG::draw(const ClipperLib::Paths &polygons, double scale, std::string stroke, coordf_t stroke_width) { for (ClipperLib::Paths::const_iterator it = polygons.begin(); it != polygons.end(); ++ it) draw(*it, scale, stroke, stroke_width); }
// This is a safe variant of the polygons offset, tailored for multiple ExPolygons. // It is required, that the input expolygons do not overlap and that the holes of each ExPolygon don't intersect with their respective outer contours. // Each ExPolygon is offsetted separately, then the offsetted ExPolygons are united. ClipperLib::Paths _offset(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::JoinType joinType, double miterLimit) { const float delta_scaled = delta * float(CLIPPER_OFFSET_SCALE); // Offsetted ExPolygons before they are united. ClipperLib::Paths contours_cummulative; contours_cummulative.reserve(expolygons.size()); // How many non-empty offsetted expolygons were actually collected into contours_cummulative? // If only one, then there is no need to do a final union. size_t expolygons_collected = 0; for (Slic3r::ExPolygons::const_iterator it_expoly = expolygons.begin(); it_expoly != expolygons.end(); ++ it_expoly) { // 1) Offset the outer contour. ClipperLib::Paths contours; { ClipperLib::Path input = Slic3rMultiPoint_to_ClipperPath(it_expoly->contour); scaleClipperPolygon(input); ClipperLib::ClipperOffset co; if (joinType == jtRound) co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE); else co.MiterLimit = miterLimit; co.ShortestEdgeLength = double(std::abs(delta_scaled * CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR)); co.AddPath(input, joinType, ClipperLib::etClosedPolygon); co.Execute(contours, delta_scaled); } if (contours.empty()) // No need to try to offset the holes. continue; if (it_expoly->holes.empty()) { // No need to subtract holes from the offsetted expolygon, we are done. contours_cummulative.insert(contours_cummulative.end(), contours.begin(), contours.end()); ++ expolygons_collected; } else { // 2) Offset the holes one by one, collect the offsetted holes. ClipperLib::Paths holes; { for (Polygons::const_iterator it_hole = it_expoly->holes.begin(); it_hole != it_expoly->holes.end(); ++ it_hole) { ClipperLib::Path input = Slic3rMultiPoint_to_ClipperPath_reversed(*it_hole); scaleClipperPolygon(input); ClipperLib::ClipperOffset co; if (joinType == jtRound) co.ArcTolerance = miterLimit * double(CLIPPER_OFFSET_SCALE); else co.MiterLimit = miterLimit; co.ShortestEdgeLength = double(std::abs(delta_scaled * CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR)); co.AddPath(input, joinType, ClipperLib::etClosedPolygon); ClipperLib::Paths out; co.Execute(out, - delta_scaled); holes.insert(holes.end(), out.begin(), out.end()); } } // 3) Subtract holes from the contours. if (holes.empty()) { // No hole remaining after an offset. Just copy the outer contour. contours_cummulative.insert(contours_cummulative.end(), contours.begin(), contours.end()); ++ expolygons_collected; } else if (delta < 0) { // Negative offset. There is a chance, that the offsetted hole intersects the outer contour. // Subtract the offsetted holes from the offsetted contours. ClipperLib::Clipper clipper; clipper.Clear(); clipper.AddPaths(contours, ClipperLib::ptSubject, true); clipper.AddPaths(holes, ClipperLib::ptClip, true); ClipperLib::Paths output; clipper.Execute(ClipperLib::ctDifference, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero); if (! output.empty()) { contours_cummulative.insert(contours_cummulative.end(), output.begin(), output.end()); ++ expolygons_collected; } else { // The offsetted holes have eaten up the offsetted outer contour. } } else { // Positive offset. As long as the Clipper offset does what one expects it to do, the offsetted hole will have a smaller // area than the original hole or even disappear, therefore there will be no new intersections. // Just collect the reversed holes. contours_cummulative.reserve(contours.size() + holes.size()); contours_cummulative.insert(contours_cummulative.end(), contours.begin(), contours.end()); // Reverse the holes in place. for (size_t i = 0; i < holes.size(); ++ i) std::reverse(holes[i].begin(), holes[i].end()); contours_cummulative.insert(contours_cummulative.end(), holes.begin(), holes.end()); ++ expolygons_collected; } } } // 4) Unite the offsetted expolygons. ClipperLib::Paths output; if (expolygons_collected > 1 && delta > 0) { // There is a chance that the outwards offsetted expolygons may intersect. Perform a union. ClipperLib::Clipper clipper; clipper.Clear(); clipper.AddPaths(contours_cummulative, ClipperLib::ptSubject, true); clipper.Execute(ClipperLib::ctUnion, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero); } else { // Negative offset. The shrunk expolygons shall not mutually intersect. Just copy the output. output = std::move(contours_cummulative); } // 4) Unscale the output. unscaleClipperPolygons(output); return output; }