void BooleanTool::pathObjectToPolygons( const PathObject* object, ClipperLib::Paths& polygons, PolyMap& polymap) { object->update(); polygons.reserve(polygons.size() + object->parts().size()); for (const auto& part : object->parts()) { const PathCoordVector& path_coords = part.path_coords; auto path_coords_end = path_coords.size(); if (part.isClosed()) --path_coords_end; ClipperLib::Path polygon; for (auto i = 0u; i < path_coords_end; ++i) { auto point = MapCoord { path_coords[i].pos }; polygon.push_back(ClipperLib::IntPoint(point.nativeX(), point.nativeY())); polymap.insertMulti(polygon.back(), std::make_pair(&part, &path_coords[i])); } bool orientation = Orientation(polygon); if ( (&part == &object->parts().front()) != orientation ) { std::reverse(polygon.begin(), polygon.end()); } // Push_back shall move the polygon. static_assert(std::is_nothrow_move_constructible<ClipperLib::Path>::value, "ClipperLib::Path must be nothrow move constructible"); polygons.push_back(polygon); } }
ClipperLib::Paths ClipperHelpers::convert( const QVector<Path>& paths, const PositiveLength& maxArcTolerance) noexcept { ClipperLib::Paths p; p.reserve(paths.size()); foreach (const Path& path, paths) { p.push_back(convert(path, maxArcTolerance)); }
// 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; }
// 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); }