Merge remote-tracking branch 'remote/master' into feature/merge_upstream

# Conflicts:
#	bbl/i18n/OrcaSlicer.pot
#	bbl/i18n/de/OrcaSlicer_de.po
#	bbl/i18n/en/OrcaSlicer_en.po
#	bbl/i18n/es/OrcaSlicer_es.po
#	bbl/i18n/fr/OrcaSlicer_fr.po
#	bbl/i18n/hu/OrcaSlicer_hu.po
#	bbl/i18n/it/OrcaSlicer_it.po
#	bbl/i18n/ja/OrcaSlicer_ja.po
#	bbl/i18n/nl/OrcaSlicer_nl.po
#	bbl/i18n/sv/OrcaSlicer_sv.po
#	bbl/i18n/zh_cn/OrcaSlicer_zh_CN.po
#	resources/config.json
#	resources/i18n/de/BambuStudio.mo
#	resources/i18n/en/BambuStudio.mo
#	resources/i18n/es/BambuStudio.mo
#	resources/i18n/fr/BambuStudio.mo
#	resources/i18n/hu/BambuStudio.mo
#	resources/i18n/it/BambuStudio.mo
#	resources/i18n/ja/OrcaSlicer.mo
#	resources/i18n/nl/BambuStudio.mo
#	resources/i18n/sv/BambuStudio.mo
#	resources/i18n/zh_cn/BambuStudio.mo
#	resources/images/ams_humidity_2.svg
#	resources/images/ams_humidity_3.svg
#	resources/images/ams_humidity_4.svg
#	resources/images/ams_humidity_tips.svg
#	resources/images/monitor_state_on.svg
#	resources/images/sdcard_state_normal.svg
#	resources/profiles/BBL.json
#	resources/profiles/BBL/filament/Bambu PETG-CF @base.json
#	resources/profiles/BBL/filament/Generic PETG-CF @base.json
#	resources/profiles/BBL/machine/Bambu Lab P1P 0.4 nozzle.json
#	resources/web/data/text.js
#	resources/web/guide/3/index.html
#	resources/web/guide/31/index.html
#	src/BambuStudio.cpp
#	src/libslic3r/AABBTreeLines.hpp
#	src/libslic3r/Brim.cpp
#	src/libslic3r/CMakeLists.txt
#	src/libslic3r/ExPolygon.hpp
#	src/libslic3r/Fill/FillBase.hpp
#	src/libslic3r/Format/bbs_3mf.cpp
#	src/libslic3r/GCodeWriter.cpp
#	src/libslic3r/Line.hpp
#	src/libslic3r/PerimeterGenerator.cpp
#	src/libslic3r/Preset.cpp
#	src/libslic3r/Print.cpp
#	src/libslic3r/Print.hpp
#	src/libslic3r/PrintConfig.cpp
#	src/libslic3r/PrintConfig.hpp
#	src/libslic3r/TreeSupport.cpp
#	src/slic3r/GUI/AmsMappingPopup.cpp
#	src/slic3r/GUI/BackgroundSlicingProcess.cpp
#	src/slic3r/GUI/ConfigManipulation.cpp
#	src/slic3r/GUI/GCodeViewer.cpp
#	src/slic3r/GUI/GCodeViewer.hpp
#	src/slic3r/GUI/GLCanvas3D.cpp
#	src/slic3r/GUI/GUI_App.cpp
#	src/slic3r/GUI/MainFrame.cpp
#	src/slic3r/GUI/PartPlate.cpp
#	src/slic3r/GUI/Plater.cpp
#	src/slic3r/GUI/Preferences.cpp
#	src/slic3r/GUI/SelectMachine.cpp
#	src/slic3r/GUI/Widgets/AMSControl.cpp
#	src/slic3r/GUI/wxMediaCtrl2.cpp
#	src/slic3r/Utils/Process.cpp
#	version.inc
This commit is contained in:
SoftFever 2023-04-19 08:48:07 +08:00
commit 9f598046d1
658 changed files with 70312 additions and 4877 deletions

View file

@ -490,7 +490,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
params.config = &layerm->region().config();
for (ExPolygon& expoly : surface_fill.expolygons) {
f->no_overlap_expolygons = intersection_ex(surface_fill.no_overlap_expolygons, ExPolygons() = {expoly});
f->no_overlap_expolygons = intersection_ex(surface_fill.no_overlap_expolygons, ExPolygons() = {expoly}, ApplySafetyOffset::Yes);
// Spacing is modified by the filler to indicate adjustments. Reset it for each expolygon.
f->spacing = surface_fill.params.spacing;
surface_fill.surface.expolygon = std::move(expoly);

View file

@ -1636,11 +1636,13 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
double arc_length;
};
std::vector<Arc> arches;
arches.reserve(graph.map_infill_end_point_to_boundary.size());
for (ContourIntersectionPoint &cp : graph.map_infill_end_point_to_boundary)
if (cp.contour_idx != boundary_idx_unconnected && cp.next_on_contour != &cp && cp.could_connect_next())
arches.push_back({ &cp, path_length_along_contour_ccw(&cp, cp.next_on_contour, graph.boundary_params[cp.contour_idx].back()) });
std::sort(arches.begin(), arches.end(), [](const auto &l, const auto &r) { return l.arc_length < r.arc_length; });
if (!params.dont_sort) {
arches.reserve(graph.map_infill_end_point_to_boundary.size());
for (ContourIntersectionPoint& cp : graph.map_infill_end_point_to_boundary)
if (cp.contour_idx != boundary_idx_unconnected && cp.next_on_contour != &cp && cp.could_connect_next())
arches.push_back({ &cp, path_length_along_contour_ccw(&cp, cp.next_on_contour, graph.boundary_params[cp.contour_idx].back()) });
std::sort(arches.begin(), arches.end(), [](const auto& l, const auto& r) { return l.arc_length < r.arc_length; });
}
//FIXME improve the Traveling Salesman problem with 2-opt and 3-opt local optimization.
for (Arc &arc : arches)

View file

@ -77,7 +77,7 @@ struct FillParams
//BBS: only used for new top surface pattern
float no_extrusion_overlap{ 0.0 };
const PrintRegionConfig* config{ nullptr };
bool dont_sort{ false }; // do not sort the lines, just simply connect them
};
static_assert(IsTriviallyCopyable<FillParams>::value, "FillParams class is not POD (and it should be - see constructor).");

View file

@ -6,6 +6,66 @@
namespace Slic3r {
class InfillPolylineClipper : public InfillPolylineOutput {
public:
InfillPolylineClipper(const BoundingBox bbox, const double scale_out) : InfillPolylineOutput(scale_out), m_bbox(bbox) {}
void add_point(const Vec2d &pt);
Points&& result() { return std::move(m_out); }
bool clips() const override { return true; }
private:
enum class Side {
Left = 1,
Right = 2,
Top = 4,
Bottom = 8
};
int sides(const Point &p) const {
return int(p.x() < m_bbox.min.x()) * int(Side::Left) +
int(p.x() > m_bbox.max.x()) * int(Side::Right) +
int(p.y() < m_bbox.min.y()) * int(Side::Bottom) +
int(p.y() > m_bbox.max.y()) * int(Side::Top);
};
// Bounding box to clip the polyline with.
BoundingBox m_bbox;
// Classification of the two last points processed.
int m_sides_prev;
int m_sides_this;
};
void InfillPolylineClipper::add_point(const Vec2d &fpt)
{
const Point pt{ this->scaled(fpt) };
if (m_out.size() < 2) {
// Collect the two first points and their status.
(m_out.empty() ? m_sides_prev : m_sides_this) = sides(pt);
m_out.emplace_back(pt);
} else {
// Classify the last inserted point, possibly remove it.
int sides_next = sides(pt);
if (// This point is inside. Take it.
m_sides_this == 0 ||
// Either this point is outside and previous or next is inside, or
// the edge possibly cuts corner of the bounding box.
(m_sides_prev & m_sides_this & sides_next) == 0) {
// Keep the last point.
m_sides_prev = m_sides_this;
} else {
// All the three points (this, prev, next) are outside at the same side.
// Ignore the last point.
m_out.pop_back();
}
// And save the current point.
m_out.emplace_back(pt);
m_sides_this = sides_next;
}
}
void FillPlanePath::_fill_surface_single(
const FillParams &params,
unsigned int thickness_layers,
@ -13,37 +73,52 @@ void FillPlanePath::_fill_surface_single(
ExPolygon expolygon,
Polylines &polylines_out)
{
expolygon.rotate(- direction.first);
expolygon.rotate(-direction.first);
coord_t distance_between_lines = coord_t(scale_(this->spacing) / params.density);
// align infill across layers using the object's bounding box
// Rotated bounding box of the whole object.
BoundingBox bounding_box = this->bounding_box.rotated(- direction.first);
Point shift = this->_centered() ?
//FIXME Vojtech: We are not sure whether the user expects the fill patterns on visible surfaces to be aligned across all the islands of a single layer.
// One may align for this->centered() to align the patterns for Archimedean Chords and Octagram Spiral patterns.
const bool align = params.density < 0.995;
BoundingBox snug_bounding_box = get_extents(expolygon).inflated(SCALED_EPSILON);
// Rotated bounding box of the area to fill in with the pattern.
BoundingBox bounding_box = align ?
// Sparse infill needs to be aligned across layers. Align infill across layers using the object's bounding box.
this->bounding_box.rotated(-direction.first) :
// Solid infill does not need to be aligned across layers, generate the infill pattern
// around the clipping expolygon only.
snug_bounding_box;
Point shift = this->centered() ?
bounding_box.center() :
bounding_box.min;
expolygon.translate(-shift.x(), -shift.y());
bounding_box.translate(-shift.x(), -shift.y());
Pointfs pts = _generate(
coord_t(ceil(coordf_t(bounding_box.min.x()) / distance_between_lines)),
coord_t(ceil(coordf_t(bounding_box.min.y()) / distance_between_lines)),
coord_t(ceil(coordf_t(bounding_box.max.x()) / distance_between_lines)),
coord_t(ceil(coordf_t(bounding_box.max.y()) / distance_between_lines)),
params.resolution);
Polyline polyline;
{
auto distance_between_lines = scaled<double>(this->spacing) / params.density;
auto min_x = coord_t(ceil(coordf_t(bounding_box.min.x()) / distance_between_lines));
auto min_y = coord_t(ceil(coordf_t(bounding_box.min.y()) / distance_between_lines));
auto max_x = coord_t(ceil(coordf_t(bounding_box.max.x()) / distance_between_lines));
auto max_y = coord_t(ceil(coordf_t(bounding_box.max.y()) / distance_between_lines));
auto resolution = scaled<double>(params.resolution) / distance_between_lines;
if (align) {
// Filling in a bounding box over the whole object, clip generated polyline against the snug bounding box.
snug_bounding_box.translate(-shift.x(), -shift.y());
InfillPolylineClipper output(snug_bounding_box, distance_between_lines);
this->generate(min_x, min_y, max_x, max_y, resolution, output);
polyline.points = std::move(output.result());
} else {
// Filling in a snug bounding box, no need to clip.
InfillPolylineOutput output(distance_between_lines);
this->generate(min_x, min_y, max_x, max_y, resolution, output);
polyline.points = std::move(output.result());
}
}
if (pts.size() >= 2) {
// Convert points to a polyline, upscale.
Polylines polylines(1, Polyline());
Polyline &polyline = polylines.front();
polyline.points.reserve(pts.size());
for (const Vec2d &pt : pts)
polyline.points.emplace_back(
coord_t(floor(pt.x() * distance_between_lines + 0.5)),
coord_t(floor(pt.y() * distance_between_lines + 0.5)));
polylines = intersection_pl(polylines, expolygon);
if (polyline.size() >= 2) {
Polylines polylines = intersection_pl(polyline, expolygon);
Polylines chained;
if (params.dont_connect() || params.density > 0.5 || polylines.size() <= 1)
chained = chain_polylines(std::move(polylines));
@ -59,7 +134,8 @@ void FillPlanePath::_fill_surface_single(
}
// Follow an Archimedean spiral, in polar coordinates: r=a+b\theta
Pointfs FillArchimedeanChords::_generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double resolution)
template<typename Output>
static void generate_archimedean_chords(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double resolution, Output &output)
{
// Radius to achieve.
coordf_t rmax = std::sqrt(coordf_t(max_x)*coordf_t(max_x)+coordf_t(max_y)*coordf_t(max_y)) * std::sqrt(2.) + 1.5;
@ -70,15 +146,22 @@ Pointfs FillArchimedeanChords::_generate(coord_t min_x, coord_t min_y, coord_t m
coordf_t r = 1;
Pointfs out;
//FIXME Vojtech: If used as a solid infill, there is a gap left at the center.
out.emplace_back(0, 0);
out.emplace_back(1, 0);
output.add_point({ 0, 0 });
output.add_point({ 1, 0 });
while (r < rmax) {
// Discretization angle to achieve a discretization error lower than resolution.
theta += 2. * acos(1. - resolution / r);
r = a + b * theta;
out.emplace_back(r * cos(theta), r * sin(theta));
output.add_point({ r * cos(theta), r * sin(theta) });
}
return out;
}
void FillArchimedeanChords::generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double resolution, InfillPolylineOutput &output)
{
if (output.clips())
generate_archimedean_chords(min_x, min_y, max_x, max_y, resolution, static_cast<InfillPolylineClipper&>(output));
else
generate_archimedean_chords(min_x, min_y, max_x, max_y, resolution, output);
}
// Adapted from
@ -126,7 +209,8 @@ static inline Point hilbert_n_to_xy(const size_t n)
return Point(x, y);
}
Pointfs FillHilbertCurve::_generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double /* resolution */)
template<typename Output>
static void generate_hilbert_curve(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, Output &output)
{
// Minimum power of two square to fit the domain.
size_t sz = 2;
@ -140,46 +224,59 @@ Pointfs FillHilbertCurve::_generate(coord_t min_x, coord_t min_y, coord_t max_x,
}
size_t sz2 = sz * sz;
Pointfs line;
line.reserve(sz2);
output.reserve(sz2);
for (size_t i = 0; i < sz2; ++ i) {
Point p = hilbert_n_to_xy(i);
line.emplace_back(p.x() + min_x, p.y() + min_y);
output.add_point({ p.x() + min_x, p.y() + min_y });
}
return line;
}
Pointfs FillOctagramSpiral::_generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double /* resolution */)
void FillHilbertCurve::generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double /* resolution */, InfillPolylineOutput &output)
{
if (output.clips())
generate_hilbert_curve(min_x, min_y, max_x, max_y, static_cast<InfillPolylineClipper&>(output));
else
generate_hilbert_curve(min_x, min_y, max_x, max_y, output);
}
template<typename Output>
static void generate_octagram_spiral(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, Output &output)
{
// Radius to achieve.
coordf_t rmax = std::sqrt(coordf_t(max_x)*coordf_t(max_x)+coordf_t(max_y)*coordf_t(max_y)) * std::sqrt(2.) + 1.5;
// Now unwind the spiral.
coordf_t r = 0;
coordf_t r_inc = sqrt(2.);
Pointfs out;
out.emplace_back(0., 0.);
output.add_point({ 0., 0. });
while (r < rmax) {
r += r_inc;
coordf_t rx = r / sqrt(2.);
coordf_t r2 = r + rx;
out.emplace_back( r, 0.);
out.emplace_back( r2, rx);
out.emplace_back( rx, rx);
out.emplace_back( rx, r2);
out.emplace_back( 0., r);
out.emplace_back(-rx, r2);
out.emplace_back(-rx, rx);
out.emplace_back(-r2, rx);
out.emplace_back(- r, 0.);
out.emplace_back(-r2, -rx);
out.emplace_back(-rx, -rx);
out.emplace_back(-rx, -r2);
out.emplace_back( 0., -r);
out.emplace_back( rx, -r2);
out.emplace_back( rx, -rx);
out.emplace_back( r2+r_inc, -rx);
output.add_point({ r, 0. });
output.add_point({ r2, rx });
output.add_point({ rx, rx });
output.add_point({ rx, r2 });
output.add_point({ 0., r });
output.add_point({-rx, r2 });
output.add_point({-rx, rx });
output.add_point({-r2, rx });
output.add_point({- r, 0. });
output.add_point({-r2, -rx });
output.add_point({-rx, -rx });
output.add_point({-rx, -r2 });
output.add_point({ 0., -r });
output.add_point({ rx, -r2 });
output.add_point({ rx, -rx });
output.add_point({ r2+r_inc, -rx });
}
return out;
}
void FillOctagramSpiral::generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double /* resolution */, InfillPolylineOutput &output)
{
if (output.clips())
generate_octagram_spiral(min_x, min_y, max_x, max_y, static_cast<InfillPolylineClipper&>(output));
else
generate_octagram_spiral(min_x, min_y, max_x, max_y, output);
}
} // namespace Slic3r

View file

@ -13,6 +13,26 @@ namespace Slic3r {
// http://user42.tuxfamily.org/math-planepath/
// http://user42.tuxfamily.org/math-planepath/gallery.html
class InfillPolylineOutput {
public:
InfillPolylineOutput(const double scale_out) : m_scale_out(scale_out) {}
void reserve(size_t n) { m_out.reserve(n); }
void add_point(const Vec2d& pt) { m_out.emplace_back(this->scaled(pt)); }
Points&& result() { return std::move(m_out); }
virtual bool clips() const { return false; }
protected:
const Point scaled(const Vec2d& fpt) const { return { coord_t(floor(fpt.x() * m_scale_out + 0.5)), coord_t(floor(fpt.y() * m_scale_out + 0.5)) }; }
// Output polyline.
Points m_out;
private:
// Scaling coefficient of the generated points before tested against m_bbox and clipped by bbox.
double m_scale_out;
};
class FillPlanePath : public Fill
{
public:
@ -27,8 +47,11 @@ protected:
Polylines &polylines_out) override;
float _layer_angle(size_t idx) const override { return 0.f; }
virtual bool _centered() const = 0;
virtual Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double resolution) = 0;
virtual bool centered() const = 0;
friend class InfillPolylineClipper;
virtual void generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double resolution, InfillPolylineOutput &output) = 0;
};
class FillArchimedeanChords : public FillPlanePath
@ -38,8 +61,8 @@ public:
~FillArchimedeanChords() override = default;
protected:
bool _centered() const override { return true; }
Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double resolution) override;
bool centered() const override { return true; }
void generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double resolution, InfillPolylineOutput &output) override;
};
class FillHilbertCurve : public FillPlanePath
@ -49,8 +72,8 @@ public:
~FillHilbertCurve() override = default;
protected:
bool _centered() const override { return false; }
Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double resolution) override;
bool centered() const override { return false; }
void generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double resolution, InfillPolylineOutput &output) override;
};
class FillOctagramSpiral : public FillPlanePath
@ -60,8 +83,8 @@ public:
~FillOctagramSpiral() override = default;
protected:
bool _centered() const override { return true; }
Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double resolution) override;
bool centered() const override { return true; }
void generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y, const double resolution, InfillPolylineOutput &output) override;
};
} // namespace Slic3r

View file

@ -415,7 +415,7 @@ public:
// bool sticks_removed =
remove_sticks(polygons_src);
// if (sticks_removed) BOOST_LOG_TRIVIAL(error) << "Sticks removed!";
polygons_outer = aoffset1 == 0 ? polygons_src : offset(polygons_src, float(aoffset1), ClipperLib::jtMiter, miterLimit);
polygons_outer = aoffset1 == 0 ? to_polygons(polygons_src) : offset(polygons_src, float(aoffset1), ClipperLib::jtMiter, miterLimit);
if (aoffset2 < 0)
polygons_inner = shrink(polygons_outer, float(aoffset1 - aoffset2), ClipperLib::jtMiter, miterLimit);
// Filter out contours with zero area or small area, contours with 2 points only.
@ -3165,7 +3165,7 @@ void FillMonotonicLineWGapFill::fill_surface_extrusion(const Surface* surface, c
for (ExPolygon& ex : gaps_ex_sorted) {
//BBS: Use DP simplify to avoid duplicated points and accelerate medial-axis calculation as well.
ex.douglas_peucker(SCALED_RESOLUTION * 0.1);
ex.medial_axis(max, min, &polylines);
ex.medial_axis(min, max, &polylines);
}
if (!polylines.empty() && !is_bridge(params.extrusion_role)) {