Merge remote-tracking branch 'PRIVATE/master' into ys_cut
This commit is contained in:
commit
b4f38883a8
40 changed files with 2703 additions and 3057 deletions
|
@ -83,6 +83,13 @@ public:
|
|||
// to split around.
|
||||
template<typename SourceNode>
|
||||
void build(std::vector<SourceNode> &&input)
|
||||
{
|
||||
this->build_modify_input(input);
|
||||
input.clear();
|
||||
}
|
||||
|
||||
template<typename SourceNode>
|
||||
void build_modify_input(std::vector<SourceNode> &input)
|
||||
{
|
||||
if (input.empty())
|
||||
clear();
|
||||
|
@ -91,7 +98,6 @@ public:
|
|||
m_nodes.assign(next_highest_power_of_2(input.size()) * 2 - 1, Node());
|
||||
build_recursive(input, 0, 0, input.size() - 1);
|
||||
}
|
||||
input.clear();
|
||||
}
|
||||
|
||||
const std::vector<Node>& nodes() const { return m_nodes; }
|
||||
|
|
|
@ -710,6 +710,8 @@ Slic3r::Polylines diff_pl(const Slic3r::Polygons &subject, const Slic3r::Polygon
|
|||
{ return _clipper_pl_closed(ClipperLib::ctDifference, ClipperUtils::PolygonsProvider(subject), ClipperUtils::PolygonsProvider(clip)); }
|
||||
Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygon &clip)
|
||||
{ return _clipper_pl_open(ClipperLib::ctIntersection, ClipperUtils::PolylinesProvider(subject), ClipperUtils::SinglePathProvider(clip.points)); }
|
||||
Slic3r::Polylines intersection_pl(const Slic3r::Polyline &subject, const Slic3r::ExPolygon &clip)
|
||||
{ return _clipper_pl_open(ClipperLib::ctIntersection, ClipperUtils::SinglePathProvider(subject.points), ClipperUtils::ExPolygonProvider(clip)); }
|
||||
Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygon &clip)
|
||||
{ return _clipper_pl_open(ClipperLib::ctIntersection, ClipperUtils::PolylinesProvider(subject), ClipperUtils::ExPolygonProvider(clip)); }
|
||||
Slic3r::Polylines intersection_pl(const Slic3r::Polyline &subject, const Slic3r::Polygons &clip)
|
||||
|
|
|
@ -437,6 +437,7 @@ Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r
|
|||
Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r::Surfaces &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
Slic3r::ExPolygons intersection_ex(const Slic3r::SurfacesPtr &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
|
||||
Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygon &clip);
|
||||
Slic3r::Polylines intersection_pl(const Slic3r::Polyline &subject, const Slic3r::ExPolygon &clip);
|
||||
Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygon &clip);
|
||||
Slic3r::Polylines intersection_pl(const Slic3r::Polyline &subject, const Slic3r::Polygons &clip);
|
||||
Slic3r::Polylines intersection_pl(const Slic3r::Polyline &subject, const Slic3r::ExPolygons &clip);
|
||||
|
|
|
@ -6,6 +6,66 @@
|
|||
|
||||
namespace Slic3r {
|
||||
|
||||
class InfillPolylineClipper : public FillPlanePath::InfillPolylineOutput {
|
||||
public:
|
||||
InfillPolylineClipper(const BoundingBox bbox, const double scale_out) : FillPlanePath::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 ¶ms,
|
||||
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
|
||||
|
|
|
@ -27,8 +27,30 @@ 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;
|
||||
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;
|
||||
};
|
||||
|
||||
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 +60,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 +71,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 +82,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
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "GCode/WipeTower.hpp"
|
||||
#include "ShortestPath.hpp"
|
||||
#include "Print.hpp"
|
||||
#include "Thread.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "libslic3r.h"
|
||||
|
@ -34,8 +35,6 @@
|
|||
#include "SVG.hpp"
|
||||
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <tbb/task_scheduler_observer.h>
|
||||
#include <tbb/enumerable_thread_specific.h>
|
||||
|
||||
// Intel redesigned some TBB interface considerably when merging TBB with their oneAPI set of libraries, see GH #7332.
|
||||
// We are using quite an old TBB 2017 U7. Before we update our build servers, let's use the old API, which is deprecated in up to date TBB.
|
||||
|
@ -1469,32 +1468,6 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
|||
print.throw_if_canceled();
|
||||
}
|
||||
|
||||
// For unknown reasons and in sporadic cases when GCode export is processing, some participating thread
|
||||
// in tbb::parallel_pipeline has not set locales to "C", probably because this thread is newly spawned.
|
||||
// So in this class method on_scheduler_entry is called for every thread before it starts participating
|
||||
// in tbb::parallel_pipeline to ensure that locales are set correctly
|
||||
|
||||
// For tbb::parallel_pipeline, it seems that on_scheduler_entry is called for every layer and every filter.
|
||||
// We ensure using thread-local storage that locales will be set to "C" just once for any participating thread.
|
||||
class TBBLocalesSetter : public tbb::task_scheduler_observer
|
||||
{
|
||||
public:
|
||||
TBBLocalesSetter() { this->observe(true); }
|
||||
~TBBLocalesSetter() override { this->observe(false); };
|
||||
|
||||
void on_scheduler_entry(bool is_worker) override
|
||||
{
|
||||
if (bool &is_locales_sets = m_is_locales_sets.local(); !is_locales_sets) {
|
||||
// Set locales of the worker thread to "C".
|
||||
set_c_locales();
|
||||
is_locales_sets = true;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
tbb::enumerable_thread_specific<bool, tbb::cache_aligned_allocator<bool>, tbb::ets_key_usage_type::ets_key_per_instance> m_is_locales_sets{false};
|
||||
};
|
||||
|
||||
// Process all layers of all objects (non-sequential mode) with a parallel pipeline:
|
||||
// Generate G-code, run the filters (vase mode, cooling buffer), run the G-code analyser
|
||||
// and export G-code into file.
|
||||
|
|
|
@ -199,9 +199,7 @@ void GCodeProcessor::TimeMachine::reset()
|
|||
max_travel_acceleration = 0.0f;
|
||||
extrude_factor_override_percentage = 1.0f;
|
||||
time = 0.0f;
|
||||
#if ENABLE_TRAVEL_TIME
|
||||
travel_time = 0.0f;
|
||||
#endif // ENABLE_TRAVEL_TIME
|
||||
stop_times = std::vector<StopTime>();
|
||||
curr.reset();
|
||||
prev.reset();
|
||||
|
@ -317,17 +315,12 @@ void GCodeProcessor::TimeMachine::calculate_time(size_t keep_last_n_blocks, floa
|
|||
block_time += additional_time;
|
||||
|
||||
time += block_time;
|
||||
#if ENABLE_TRAVEL_TIME
|
||||
if (block.move_type == EMoveType::Travel)
|
||||
travel_time += block_time;
|
||||
else
|
||||
roles_time[static_cast<size_t>(block.role)] += block_time;
|
||||
#endif // ENABLE_TRAVEL_TIME
|
||||
gcode_time.cache += block_time;
|
||||
moves_time[static_cast<size_t>(block.move_type)] += block_time;
|
||||
#if !ENABLE_TRAVEL_TIME
|
||||
roles_time[static_cast<size_t>(block.role)] += block_time;
|
||||
#endif // !ENABLE_TRAVEL_TIME
|
||||
if (block.layer_id >= layers_time.size()) {
|
||||
const size_t curr_size = layers_time.size();
|
||||
layers_time.resize(block.layer_id);
|
||||
|
@ -1465,7 +1458,6 @@ std::string GCodeProcessor::get_time_dhm(PrintEstimatedStatistics::ETimeMode mod
|
|||
return (mode < PrintEstimatedStatistics::ETimeMode::Count) ? short_time(get_time_dhms(m_time_processor.machines[static_cast<size_t>(mode)].time)) : std::string("N/A");
|
||||
}
|
||||
|
||||
#if ENABLE_TRAVEL_TIME
|
||||
float GCodeProcessor::get_travel_time(PrintEstimatedStatistics::ETimeMode mode) const
|
||||
{
|
||||
return (mode < PrintEstimatedStatistics::ETimeMode::Count) ? m_time_processor.machines[static_cast<size_t>(mode)].travel_time : 0.0f;
|
||||
|
@ -1475,7 +1467,6 @@ std::string GCodeProcessor::get_travel_time_dhm(PrintEstimatedStatistics::ETimeM
|
|||
{
|
||||
return (mode < PrintEstimatedStatistics::ETimeMode::Count) ? short_time(get_time_dhms(m_time_processor.machines[static_cast<size_t>(mode)].travel_time)) : std::string("N/A");
|
||||
}
|
||||
#endif // ENABLE_TRAVEL_TIME
|
||||
|
||||
std::vector<std::pair<CustomGCode::Type, std::pair<float, float>>> GCodeProcessor::get_custom_gcode_times(PrintEstimatedStatistics::ETimeMode mode, bool include_remaining) const
|
||||
{
|
||||
|
@ -1769,7 +1760,7 @@ void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line, bool
|
|||
switch (cmd[1]) {
|
||||
case '1':
|
||||
switch (cmd[2]) {
|
||||
case '0': { process_G10(line); break; } // Retract
|
||||
case '0': { process_G10(line); break; } // Retract or Set tool temperature
|
||||
case '1': { process_G11(line); break; } // Unretract
|
||||
default: break;
|
||||
}
|
||||
|
@ -3232,6 +3223,23 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line, bool cloc
|
|||
|
||||
void GCodeProcessor::process_G10(const GCodeReader::GCodeLine& line)
|
||||
{
|
||||
if (m_flavor == gcfRepRapFirmware) {
|
||||
// similar to M104/M109
|
||||
float new_temp;
|
||||
if (line.has_value('S', new_temp)) {
|
||||
size_t id = m_extruder_id;
|
||||
float val;
|
||||
if (line.has_value('P', val)) {
|
||||
const size_t eid = static_cast<size_t>(val);
|
||||
if (eid < m_extruder_temps.size())
|
||||
id = eid;
|
||||
}
|
||||
|
||||
m_extruder_temps[id] = new_temp;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// stores retract move
|
||||
store_move_vertex(EMoveType::Retract);
|
||||
}
|
||||
|
@ -3441,18 +3449,22 @@ void GCodeProcessor::process_M108(const GCodeReader::GCodeLine& line)
|
|||
void GCodeProcessor::process_M109(const GCodeReader::GCodeLine& line)
|
||||
{
|
||||
float new_temp;
|
||||
size_t id = (size_t)-1;
|
||||
if (line.has_value('R', new_temp)) {
|
||||
float val;
|
||||
if (line.has_value('T', val)) {
|
||||
const size_t eid = static_cast<size_t>(val);
|
||||
if (eid < m_extruder_temps.size())
|
||||
m_extruder_temps[eid] = new_temp;
|
||||
id = eid;
|
||||
}
|
||||
else
|
||||
m_extruder_temps[m_extruder_id] = new_temp;
|
||||
id = m_extruder_id;
|
||||
}
|
||||
else if (line.has_value('S', new_temp))
|
||||
m_extruder_temps[m_extruder_id] = new_temp;
|
||||
id = m_extruder_id;
|
||||
|
||||
if (id != (size_t)-1)
|
||||
m_extruder_temps[id] = new_temp;
|
||||
}
|
||||
|
||||
void GCodeProcessor::process_M132(const GCodeReader::GCodeLine& line)
|
||||
|
@ -4306,9 +4318,7 @@ void GCodeProcessor::update_estimated_times_stats()
|
|||
auto update_mode = [this](PrintEstimatedStatistics::ETimeMode mode) {
|
||||
PrintEstimatedStatistics::Mode& data = m_result.print_statistics.modes[static_cast<size_t>(mode)];
|
||||
data.time = get_time(mode);
|
||||
#if ENABLE_TRAVEL_TIME
|
||||
data.travel_time = get_travel_time(mode);
|
||||
#endif // ENABLE_TRAVEL_TIME
|
||||
data.custom_gcode_times = get_custom_gcode_times(mode, true);
|
||||
data.moves_times = get_moves_time(mode);
|
||||
data.roles_times = get_roles_time(mode);
|
||||
|
|
|
@ -44,9 +44,7 @@ namespace Slic3r {
|
|||
struct Mode
|
||||
{
|
||||
float time;
|
||||
#if ENABLE_TRAVEL_TIME
|
||||
float travel_time;
|
||||
#endif // ENABLE_TRAVEL_TIME
|
||||
std::vector<std::pair<CustomGCode::Type, std::pair<float, float>>> custom_gcode_times;
|
||||
std::vector<std::pair<EMoveType, float>> moves_times;
|
||||
std::vector<std::pair<ExtrusionRole, float>> roles_times;
|
||||
|
@ -54,9 +52,7 @@ namespace Slic3r {
|
|||
|
||||
void reset() {
|
||||
time = 0.0f;
|
||||
#if ENABLE_TRAVEL_TIME
|
||||
travel_time = 0.0f;
|
||||
#endif // ENABLE_TRAVEL_TIME
|
||||
custom_gcode_times.clear();
|
||||
moves_times.clear();
|
||||
roles_times.clear();
|
||||
|
@ -307,9 +303,7 @@ namespace Slic3r {
|
|||
float max_travel_acceleration; // mm/s^2
|
||||
float extrude_factor_override_percentage;
|
||||
float time; // s
|
||||
#if ENABLE_TRAVEL_TIME
|
||||
float travel_time; // s
|
||||
#endif // ENABLE_TRAVEL_TIME
|
||||
struct StopTime
|
||||
{
|
||||
unsigned int g1_line_id;
|
||||
|
@ -635,10 +629,8 @@ namespace Slic3r {
|
|||
|
||||
float get_time(PrintEstimatedStatistics::ETimeMode mode) const;
|
||||
std::string get_time_dhm(PrintEstimatedStatistics::ETimeMode mode) const;
|
||||
#if ENABLE_TRAVEL_TIME
|
||||
float get_travel_time(PrintEstimatedStatistics::ETimeMode mode) const;
|
||||
std::string get_travel_time_dhm(PrintEstimatedStatistics::ETimeMode mode) const;
|
||||
#endif // ENABLE_TRAVEL_TIME
|
||||
std::vector<std::pair<CustomGCode::Type, std::pair<float, float>>> get_custom_gcode_times(PrintEstimatedStatistics::ETimeMode mode, bool include_remaining) const;
|
||||
|
||||
std::vector<std::pair<EMoveType, float>> get_moves_time(PrintEstimatedStatistics::ETimeMode mode) const;
|
||||
|
@ -674,7 +666,7 @@ namespace Slic3r {
|
|||
void process_G2_G3(const GCodeReader::GCodeLine& line, bool clockwise);
|
||||
#endif // ENABLE_PROCESS_G2_G3_LINES
|
||||
|
||||
// Retract
|
||||
// Retract or Set tool temperature
|
||||
void process_G10(const GCodeReader::GCodeLine& line);
|
||||
|
||||
// Unretract
|
||||
|
|
|
@ -1619,7 +1619,7 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
|
|||
size_t counter = 1;
|
||||
for (TriangleMesh &mesh : meshes) {
|
||||
// FIXME: crashes if not satisfied
|
||||
if (mesh.facets_count() < 3)
|
||||
if (mesh.facets_count() < 3 || mesh.has_zero_volume())
|
||||
continue;
|
||||
|
||||
// XXX: this seems to be the only real usage of m_model, maybe refactor this so that it's not needed?
|
||||
|
@ -2093,7 +2093,7 @@ size_t ModelVolume::split(unsigned int max_extruders)
|
|||
const Vec3d offset = this->get_offset();
|
||||
|
||||
for (TriangleMesh &mesh : meshes) {
|
||||
if (mesh.empty())
|
||||
if (mesh.empty() || mesh.has_zero_volume())
|
||||
// Repair may have removed unconnected triangles, thus emptying the mesh.
|
||||
continue;
|
||||
|
||||
|
|
|
@ -2198,8 +2198,7 @@ void PrintObject::combine_infill()
|
|||
void PrintObject::_generate_support_material()
|
||||
{
|
||||
if (m_config.support_material_style == smsTree) {
|
||||
TreeSupport tree_support;
|
||||
tree_support.generateSupportAreas(*this);
|
||||
fff_tree_support_generate(*this, std::function<void()>([this](){ this->throw_if_canceled(); }));
|
||||
} else {
|
||||
PrintObjectSupportMaterial support_material(this, m_slicing_params);
|
||||
support_material.generate(*this);
|
||||
|
|
|
@ -39,14 +39,6 @@
|
|||
//====================
|
||||
#define ENABLE_2_5_0_ALPHA1 1
|
||||
|
||||
// Enable changes in preview layout
|
||||
#define ENABLE_PREVIEW_LAYOUT (1 && ENABLE_2_5_0_ALPHA1)
|
||||
// Enable drawing the items in legend toolbar using icons
|
||||
#define ENABLE_LEGEND_TOOLBAR_ICONS (1 && ENABLE_PREVIEW_LAYOUT)
|
||||
// Enable coloring of toolpaths in preview by layer time
|
||||
#define ENABLE_PREVIEW_LAYER_TIME (1 && ENABLE_2_5_0_ALPHA1)
|
||||
// Enable showing time estimate for travel moves in legend
|
||||
#define ENABLE_TRAVEL_TIME (1 && ENABLE_2_5_0_ALPHA1)
|
||||
// Enable removal of wipe tower magic object_id equal to 1000
|
||||
#define ENABLE_WIPETOWER_OBJECTID_1000_REMOVAL (1 && ENABLE_2_5_0_ALPHA1)
|
||||
// Enable removal of legacy OpenGL calls
|
||||
|
@ -61,8 +53,6 @@
|
|||
#define ENABLE_GLMODEL_STATISTICS (0 && ENABLE_LEGACY_OPENGL_REMOVAL)
|
||||
// Enable rework of Reload from disk command
|
||||
#define ENABLE_RELOAD_FROM_DISK_REWORK (1 && ENABLE_2_5_0_ALPHA1)
|
||||
// Enable recalculating toolpaths when switching to/from volumetric rate visualization
|
||||
#define ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC (1 && ENABLE_2_5_0_ALPHA1)
|
||||
// Enable editing volumes transformation in world coordinates and instances in local coordinates
|
||||
#define ENABLE_WORLD_COORDINATE (1 && ENABLE_2_5_0_ALPHA1)
|
||||
// Enable modified camera control using mouse
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
#else
|
||||
// any posix system
|
||||
#include <pthread.h>
|
||||
#ifdef __APPLE__
|
||||
#include <pthread/qos.h>
|
||||
#endif // __APPLE__
|
||||
#endif
|
||||
|
||||
#include <atomic>
|
||||
|
@ -241,4 +244,26 @@ void name_tbb_thread_pool_threads_set_locale()
|
|||
});
|
||||
}
|
||||
|
||||
void set_current_thread_qos()
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
// OSX specific: Set Quality of Service to "user initiated", so that the threads will be scheduled to high performance
|
||||
// cores if available.
|
||||
pthread_set_qos_class_self_np(QOS_CLASS_USER_INITIATED, 0);
|
||||
#endif // __APPLE__
|
||||
}
|
||||
|
||||
void TBBLocalesSetter::on_scheduler_entry(bool is_worker)
|
||||
{
|
||||
// static std::atomic<int> cnt = 0;
|
||||
// std::cout << "TBBLocalesSetter Entering " << cnt ++ << " ID " << std::this_thread::get_id() << "\n";
|
||||
if (bool& is_locales_sets = m_is_locales_sets.local(); !is_locales_sets) {
|
||||
// Set locales of the worker thread to "C".
|
||||
set_c_locales();
|
||||
// OSX specific: Elevate QOS on Apple Silicon.
|
||||
set_current_thread_qos();
|
||||
is_locales_sets = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
#include <thread>
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
#include <tbb/task_scheduler_observer.h>
|
||||
#include <tbb/enumerable_thread_specific.h>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
// Set / get thread name.
|
||||
|
@ -26,6 +29,10 @@ inline bool set_thread_name(boost::thread &thread, const std::string &thread_nam
|
|||
bool set_current_thread_name(const char *thread_name);
|
||||
inline bool set_current_thread_name(const std::string &thread_name) { return set_current_thread_name(thread_name.c_str()); }
|
||||
|
||||
// OSX specific: Set Quality of Service to "user initiated", so that the threads will be scheduled to high performance
|
||||
// cores if available.
|
||||
void set_current_thread_qos();
|
||||
|
||||
// Returns nullopt if not supported.
|
||||
// Not supported by OSX.
|
||||
// Naming threads is only supported on newer Windows 10.
|
||||
|
@ -53,6 +60,25 @@ template<class Fn> inline boost::thread create_thread(Fn &&fn)
|
|||
return create_thread(attrs, std::forward<Fn>(fn));
|
||||
}
|
||||
|
||||
// For unknown reasons and in sporadic cases when GCode export is processing, some participating thread
|
||||
// in tbb::parallel_pipeline has not set locales to "C", probably because this thread is newly spawned.
|
||||
// So in this class method on_scheduler_entry is called for every thread before it starts participating
|
||||
// in tbb::parallel_pipeline to ensure that locales are set correctly
|
||||
//
|
||||
// For tbb::parallel_pipeline, it seems that on_scheduler_entry is called for every layer and every filter.
|
||||
// We ensure using thread-local storage that locales will be set to "C" just once for any participating thread.
|
||||
class TBBLocalesSetter : public tbb::task_scheduler_observer
|
||||
{
|
||||
public:
|
||||
TBBLocalesSetter() { this->observe(true); }
|
||||
~TBBLocalesSetter() override { this->observe(false); };
|
||||
|
||||
void on_scheduler_entry(bool is_worker) override;
|
||||
|
||||
private:
|
||||
tbb::enumerable_thread_specific<bool, tbb::cache_aligned_allocator<bool>, tbb::ets_key_usage_type::ets_key_per_instance> m_is_locales_sets{ false };
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // GUI_THREAD_HPP
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
#include <tbb/parallel_for.h>
|
||||
#include <tbb/task_group.h>
|
||||
|
||||
namespace Slic3r
|
||||
namespace Slic3r::FFFTreeSupport
|
||||
{
|
||||
|
||||
// or warning
|
||||
|
@ -106,7 +106,10 @@ TreeModelVolumes::TreeModelVolumes(
|
|||
const PrintObject &print_object,
|
||||
const BuildVolume &build_volume,
|
||||
const coord_t max_move, const coord_t max_move_slow, size_t current_mesh_idx,
|
||||
double progress_multiplier, double progress_offset, const std::vector<Polygons>& additional_excluded_areas) :
|
||||
#ifdef SLIC3R_TREESUPPORTS_PROGRESS
|
||||
double progress_multiplier, double progress_offset,
|
||||
#endif // SLIC3R_TREESUPPORTS_PROGRESS
|
||||
const std::vector<Polygons>& additional_excluded_areas) :
|
||||
// -2 to avoid rounding errors
|
||||
m_max_move{ std::max<coord_t>(max_move - 2, 0) }, m_max_move_slow{ std::max<coord_t>(max_move_slow - 2, 0) },
|
||||
#ifdef SLIC3R_TREESUPPORTS_PROGRESS
|
||||
|
@ -161,7 +164,7 @@ TreeModelVolumes::TreeModelVolumes(
|
|||
m_min_resolution = std::min(m_min_resolution, data_pair.first.resolution);
|
||||
}
|
||||
|
||||
const TreeSupport::TreeSupportSettings config{ m_layer_outlines[m_current_outline_idx].first };
|
||||
const TreeSupportSettings config{ m_layer_outlines[m_current_outline_idx].first };
|
||||
m_current_min_xy_dist = config.xy_min_distance;
|
||||
m_current_min_xy_dist_delta = config.xy_distance - m_current_min_xy_dist;
|
||||
assert(m_current_min_xy_dist_delta >= 0);
|
||||
|
@ -206,7 +209,7 @@ void TreeModelVolumes::precalculate(const coord_t max_layer)
|
|||
// Get the config corresponding to one mesh that is in the current group. Which one has to be irrelevant.
|
||||
// Not the prettiest way to do this, but it ensures some calculations that may be a bit more complex
|
||||
// like inital layer diameter are only done in once.
|
||||
TreeSupport::TreeSupportSettings config(m_layer_outlines[m_current_outline_idx].first);
|
||||
TreeSupportSettings config(m_layer_outlines[m_current_outline_idx].first);
|
||||
|
||||
{
|
||||
// calculate which radius each layer in the tip may have.
|
||||
|
@ -297,7 +300,7 @@ const Polygons& TreeModelVolumes::getCollision(const coord_t orig_radius, LayerI
|
|||
return (*result).get();
|
||||
if (m_precalculated) {
|
||||
BOOST_LOG_TRIVIAL(error_level_not_in_cache) << "Had to calculate collision at radius " << radius << " and layer " << layer_idx << ", but precalculate was called. Performance may suffer!";
|
||||
TreeSupport::showError("Not precalculated Collision requested.", false);
|
||||
tree_supports_show_error("Not precalculated Collision requested.", false);
|
||||
}
|
||||
const_cast<TreeModelVolumes*>(this)->calculateCollision(radius, layer_idx);
|
||||
return getCollision(orig_radius, layer_idx, min_xy_dist);
|
||||
|
@ -312,7 +315,7 @@ const Polygons& TreeModelVolumes::getCollisionHolefree(coord_t radius, LayerInde
|
|||
return (*result).get();
|
||||
if (m_precalculated) {
|
||||
BOOST_LOG_TRIVIAL(error_level_not_in_cache) << "Had to calculate collision holefree at radius " << radius << " and layer " << layer_idx << ", but precalculate was called. Performance may suffer!";
|
||||
TreeSupport::showError("Not precalculated Holefree Collision requested.", false);
|
||||
tree_supports_show_error("Not precalculated Holefree Collision requested.", false);
|
||||
}
|
||||
const_cast<TreeModelVolumes*>(this)->calculateCollisionHolefree({ radius, layer_idx });
|
||||
return getCollisionHolefree(radius, layer_idx);
|
||||
|
@ -336,10 +339,10 @@ const Polygons& TreeModelVolumes::getAvoidance(const coord_t orig_radius, LayerI
|
|||
if (m_precalculated) {
|
||||
if (to_model) {
|
||||
BOOST_LOG_TRIVIAL(error_level_not_in_cache) << "Had to calculate Avoidance to model at radius " << radius << " and layer " << layer_idx << ", but precalculate was called. Performance may suffer!";
|
||||
TreeSupport::showError("Not precalculated Avoidance(to model) requested.", false);
|
||||
tree_supports_show_error("Not precalculated Avoidance(to model) requested.", false);
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(error_level_not_in_cache) << "Had to calculate Avoidance at radius " << radius << " and layer " << layer_idx << ", but precalculate was called. Performance may suffer!";
|
||||
TreeSupport::showError("Not precalculated Avoidance(to buildplate) requested.", false);
|
||||
tree_supports_show_error("Not precalculated Avoidance(to buildplate) requested.", false);
|
||||
}
|
||||
}
|
||||
const_cast<TreeModelVolumes*>(this)->calculateAvoidance({ radius, layer_idx }, ! to_model, to_model);
|
||||
|
@ -357,7 +360,7 @@ const Polygons& TreeModelVolumes::getPlaceableAreas(const coord_t orig_radius, L
|
|||
return (*result).get();
|
||||
if (m_precalculated) {
|
||||
BOOST_LOG_TRIVIAL(error_level_not_in_cache) << "Had to calculate Placeable Areas at radius " << radius << " and layer " << layer_idx << ", but precalculate was called. Performance may suffer!";
|
||||
TreeSupport::showError("Not precalculated Placeable areas requested.", false);
|
||||
tree_supports_show_error("Not precalculated Placeable areas requested.", false);
|
||||
}
|
||||
const_cast<TreeModelVolumes*>(this)->calculatePlaceables(radius, layer_idx);
|
||||
return getPlaceableAreas(orig_radius, layer_idx);
|
||||
|
@ -380,7 +383,7 @@ const Polygons& TreeModelVolumes::getWallRestriction(const coord_t orig_radius,
|
|||
return (*result).get();
|
||||
if (m_precalculated) {
|
||||
BOOST_LOG_TRIVIAL(error_level_not_in_cache) << "Had to calculate Wall restricions at radius " << radius << " and layer " << layer_idx << ", but precalculate was called. Performance may suffer!";
|
||||
TreeSupport::showError(
|
||||
tree_supports_show_error(
|
||||
min_xy_dist ?
|
||||
"Not precalculated Wall restriction of minimum xy distance requested )." :
|
||||
"Not precalculated Wall restriction requested )."
|
||||
|
@ -774,4 +777,4 @@ coord_t TreeModelVolumes::ceilRadius(const coord_t radius) const
|
|||
return out;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace Slic3r::FFFTreeSupport
|
||||
|
|
|
@ -22,11 +22,14 @@
|
|||
namespace Slic3r
|
||||
{
|
||||
|
||||
using LayerIndex = int;
|
||||
|
||||
class BuildVolume;
|
||||
class PrintObject;
|
||||
|
||||
namespace FFFTreeSupport
|
||||
{
|
||||
|
||||
using LayerIndex = int;
|
||||
|
||||
struct TreeSupportMeshGroupSettings {
|
||||
TreeSupportMeshGroupSettings() = default;
|
||||
explicit TreeSupportMeshGroupSettings(const PrintObject &print_object);
|
||||
|
@ -194,15 +197,19 @@ class TreeModelVolumes
|
|||
public:
|
||||
TreeModelVolumes() = default;
|
||||
explicit TreeModelVolumes(const PrintObject &print_object, const BuildVolume &build_volume,
|
||||
coord_t max_move, coord_t max_move_slow, size_t current_mesh_idx, double progress_multiplier,
|
||||
double progress_offset, const std::vector<Polygons> &additional_excluded_areas = {});
|
||||
coord_t max_move, coord_t max_move_slow, size_t current_mesh_idx,
|
||||
#ifdef SLIC3R_TREESUPPORTS_PROGRESS
|
||||
double progress_multiplier,
|
||||
double progress_offset,
|
||||
#endif // SLIC3R_TREESUPPORTS_PROGRESS
|
||||
const std::vector<Polygons> &additional_excluded_areas = {});
|
||||
TreeModelVolumes(TreeModelVolumes&&) = default;
|
||||
TreeModelVolumes& operator=(TreeModelVolumes&&) = default;
|
||||
|
||||
TreeModelVolumes(const TreeModelVolumes&) = delete;
|
||||
TreeModelVolumes& operator=(const TreeModelVolumes&) = delete;
|
||||
|
||||
enum class AvoidanceType
|
||||
enum class AvoidanceType : int8_t
|
||||
{
|
||||
Slow,
|
||||
FastSafe,
|
||||
|
@ -605,6 +612,7 @@ private:
|
|||
#endif // SLIC3R_TREESUPPORTS_PROGRESS
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace FFFTreeSupport
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif //slic3r_TreeModelVolumes_hpp
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -378,6 +378,14 @@ bool TriangleMesh::is_splittable() const
|
|||
return its_is_splittable(this->its);
|
||||
}
|
||||
|
||||
bool TriangleMesh::has_zero_volume() const
|
||||
{
|
||||
const Vec3d sz = size();
|
||||
const double volume_val = sz.x() * sz.y() * sz.z();
|
||||
|
||||
return is_approx(volume_val, 0.0);
|
||||
}
|
||||
|
||||
std::vector<TriangleMesh> TriangleMesh::split() const
|
||||
{
|
||||
std::vector<indexed_triangle_set> itss = its_split(this->its);
|
||||
|
|
|
@ -139,6 +139,7 @@ public:
|
|||
bool empty() const { return this->facets_count() == 0; }
|
||||
bool repaired() const;
|
||||
bool is_splittable() const;
|
||||
bool has_zero_volume() const;
|
||||
// Estimate of the memory occupied by this structure, important for keeping an eye on the Undo / Redo stack allocation.
|
||||
size_t memsize() const;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue