Merge remote-tracking branch 'PRIVATE/master' into ys_cut

This commit is contained in:
YuSanka 2022-10-03 11:30:49 +02:00
commit b4f38883a8
40 changed files with 2703 additions and 3057 deletions

View file

@ -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; }

View file

@ -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)

View file

@ -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);

View file

@ -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 &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

@ -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

View file

@ -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.

View 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);

View file

@ -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

View file

@ -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;

View file

@ -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);

View file

@ -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

View file

@ -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;
}
}
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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;