mirror of
https://github.com/QIDITECH/QIDISlicer.git
synced 2026-02-02 17:08:42 +03:00
update to latest version
This commit is contained in:
@@ -480,10 +480,8 @@ std::vector<Polygons> expand_expolygons(const ExPolygons &src, const ExPolygons
|
||||
return out;
|
||||
}
|
||||
|
||||
std::vector<ExPolygon> expand_merge_expolygons(ExPolygons &&src, const ExPolygons &boundary, const RegionExpansionParameters ¶ms)
|
||||
std::vector<ExPolygon> merge_expansions_into_expolygons(ExPolygons &&src, std::vector<RegionExpansion> &&expanded)
|
||||
{
|
||||
// expanded regions are sorted by boundary id and source id
|
||||
std::vector<RegionExpansion> expanded = propagate_waves(src, boundary, params);
|
||||
// expanded regions will be merged into source regions, thus they will be re-sorted by source id.
|
||||
std::sort(expanded.begin(), expanded.end(), [](const auto &l, const auto &r) { return l.src_id < r.src_id; });
|
||||
uint32_t last = 0;
|
||||
@@ -535,5 +533,12 @@ std::vector<ExPolygon> expand_merge_expolygons(ExPolygons &&src, const ExPolygon
|
||||
return out;
|
||||
}
|
||||
|
||||
std::vector<ExPolygon> expand_merge_expolygons(ExPolygons &&src, const ExPolygons &boundary, const RegionExpansionParameters ¶ms)
|
||||
{
|
||||
// expanded regions are sorted by boundary id and source id
|
||||
std::vector<RegionExpansion> expanded = propagate_waves(src, boundary, params);
|
||||
return merge_expansions_into_expolygons(std::move(src), std::move(expanded));
|
||||
}
|
||||
|
||||
} // Algorithm
|
||||
} // Slic3r
|
||||
|
||||
@@ -72,6 +72,7 @@ struct RegionExpansion
|
||||
};
|
||||
|
||||
std::vector<RegionExpansion> propagate_waves(const WaveSeeds &seeds, const ExPolygons &boundary, const RegionExpansionParameters ¶ms);
|
||||
std::vector<RegionExpansion> propagate_waves(const ExPolygons &src, const ExPolygons &boundary, const RegionExpansionParameters ¶ms);
|
||||
|
||||
std::vector<RegionExpansion> propagate_waves(const ExPolygons &src, const ExPolygons &boundary,
|
||||
// Scaled expansion value
|
||||
@@ -106,6 +107,9 @@ std::vector<Polygons> expand_expolygons(const ExPolygons &src, const ExPolygons
|
||||
// Don't take more than max_nr_steps for small expansion_step.
|
||||
size_t max_nr_steps);
|
||||
|
||||
// Merge src with expansions, return the merged expolygons.
|
||||
std::vector<ExPolygon> merge_expansions_into_expolygons(ExPolygons &&src, std::vector<RegionExpansion> &&expanded);
|
||||
|
||||
std::vector<ExPolygon> expand_merge_expolygons(ExPolygons &&src, const ExPolygons &boundary, const RegionExpansionParameters ¶ms);
|
||||
|
||||
} // Algorithm
|
||||
|
||||
@@ -750,10 +750,26 @@ void arrange(ArrangePolygons &items,
|
||||
d += corr;
|
||||
|
||||
for (auto &itm : items)
|
||||
if (itm.bed_idx == bedidx)
|
||||
if (itm.bed_idx == int(bedidx))
|
||||
itm.translation += d;
|
||||
}
|
||||
}
|
||||
|
||||
BoundingBox bounding_box(const InfiniteBed &bed)
|
||||
{
|
||||
BoundingBox ret;
|
||||
using C = coord_t;
|
||||
|
||||
// It is important for Mx and My to be strictly less than half of the
|
||||
// range of type C. width(), height() and area() will not overflow this way.
|
||||
C Mx = C((std::numeric_limits<C>::lowest() + 2 * bed.center.x()) / 4.01);
|
||||
C My = C((std::numeric_limits<C>::lowest() + 2 * bed.center.y()) / 4.01);
|
||||
|
||||
ret.max = bed.center - Point{Mx, My};
|
||||
ret.min = bed.center + Point{Mx, My};
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace arr
|
||||
} // namespace Slic3r
|
||||
|
||||
@@ -55,6 +55,25 @@ struct IrregularBed {
|
||||
|
||||
using ArrangeBed = boost::variant<InfiniteBed, RectangleBed, CircleBed, SegmentedRectangleBed, IrregularBed>;
|
||||
|
||||
BoundingBox bounding_box(const InfiniteBed &bed);
|
||||
inline BoundingBox bounding_box(const RectangleBed &b) { return b.bb; }
|
||||
inline BoundingBox bounding_box(const SegmentedRectangleBed &b) { return b.bb; }
|
||||
inline BoundingBox bounding_box(const CircleBed &b)
|
||||
{
|
||||
auto r = static_cast<coord_t>(std::round(b.radius()));
|
||||
Point R{r, r};
|
||||
|
||||
return {b.center() - R, b.center() + R};
|
||||
}
|
||||
inline BoundingBox bounding_box(const ArrangeBed &b)
|
||||
{
|
||||
BoundingBox ret;
|
||||
auto visitor = [&ret](const auto &b) { ret = bounding_box(b); };
|
||||
boost::apply_visitor(visitor, b);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ArrangeBed to_arrange_bed(const Points &bedpts);
|
||||
|
||||
/// A logical bed representing an object not being arranged. Either the arrange
|
||||
|
||||
@@ -138,6 +138,8 @@ set(SLIC3R_SOURCES
|
||||
GCode/ThumbnailData.hpp
|
||||
GCode/Thumbnails.cpp
|
||||
GCode/Thumbnails.hpp
|
||||
GCode/ConflictChecker.cpp
|
||||
GCode/ConflictChecker.hpp
|
||||
GCode/CoolingBuffer.cpp
|
||||
GCode/CoolingBuffer.hpp
|
||||
GCode/FindReplace.cpp
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <cstdint>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
||||
@@ -468,11 +468,8 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
|
||||
f->print_config = &this->object()->print()->config();
|
||||
f->print_object_config = &this->object()->config();
|
||||
|
||||
if (surface_fill.params.pattern == ipLightning) {
|
||||
auto *lf = dynamic_cast<FillLightning::Filler*>(f.get());
|
||||
lf->generator = lightning_generator;
|
||||
lf->num_raft_layers = this->object()->slicing_parameters().raft_layers();
|
||||
}
|
||||
if (surface_fill.params.pattern == ipLightning)
|
||||
dynamic_cast<FillLightning::Filler*>(f.get())->generator = lightning_generator;
|
||||
|
||||
if (surface_fill.params.pattern == ipEnsuring) {
|
||||
auto *fill_ensuring = dynamic_cast<FillEnsuring *>(f.get());
|
||||
@@ -649,18 +646,15 @@ Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Oc
|
||||
// Create the filler object.
|
||||
std::unique_ptr<Fill> f = std::unique_ptr<Fill>(Fill::new_from_type(surface_fill.params.pattern));
|
||||
f->set_bounding_box(bbox);
|
||||
f->layer_id = this->id();
|
||||
f->layer_id = this->id() - this->object()->get_layer(0)->id(); // We need to subtract raft layers.
|
||||
f->z = this->print_z;
|
||||
f->angle = surface_fill.params.angle;
|
||||
f->adapt_fill_octree = (surface_fill.params.pattern == ipSupportCubic) ? support_fill_octree : adaptive_fill_octree;
|
||||
f->print_config = &this->object()->print()->config();
|
||||
f->print_object_config = &this->object()->config();
|
||||
|
||||
if (surface_fill.params.pattern == ipLightning) {
|
||||
auto *lf = dynamic_cast<FillLightning::Filler *>(f.get());
|
||||
lf->generator = lightning_generator;
|
||||
lf->num_raft_layers = this->object()->slicing_parameters().raft_layers();
|
||||
}
|
||||
if (surface_fill.params.pattern == ipLightning)
|
||||
dynamic_cast<FillLightning::Filler *>(f.get())->generator = lightning_generator;
|
||||
|
||||
// calculate flow spacing for infill pattern generation
|
||||
double link_max_length = 0.;
|
||||
|
||||
@@ -13,7 +13,7 @@ void Filler::_fill_surface_single(
|
||||
ExPolygon expolygon,
|
||||
Polylines &polylines_out)
|
||||
{
|
||||
const Layer &layer = generator->getTreesForLayer(this->layer_id - this->num_raft_layers);
|
||||
const Layer &layer = generator->getTreesForLayer(this->layer_id);
|
||||
Polylines fill_lines = layer.convertToLines(to_polygons(expolygon), scaled<coord_t>(0.5 * this->spacing - this->overlap));
|
||||
|
||||
if (params.dont_connect() || fill_lines.size() <= 1) {
|
||||
|
||||
@@ -22,7 +22,6 @@ public:
|
||||
~Filler() override = default;
|
||||
|
||||
Generator *generator { nullptr };
|
||||
size_t num_raft_layers { 0 };
|
||||
protected:
|
||||
Fill* clone() const override { return new Filler(*this); }
|
||||
|
||||
|
||||
@@ -11,20 +11,17 @@
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
static std::mutex arch_mtx;
|
||||
|
||||
class Registry {
|
||||
static std::unique_ptr<Registry> registry;
|
||||
|
||||
std::set<ArchiveEntry> entries;
|
||||
public:
|
||||
|
||||
Registry ()
|
||||
{
|
||||
entries = {
|
||||
{
|
||||
"SL1", // id
|
||||
L("SL1 archive format"), // description
|
||||
L("SL1 archive"), // description
|
||||
"sl1", // main extension
|
||||
{"sl1s", "zip"}, // extension aliases
|
||||
|
||||
@@ -38,22 +35,14 @@ public:
|
||||
},
|
||||
{
|
||||
"SL1SVG",
|
||||
L("SL1SVG archive files"),
|
||||
L("SL1 SVG archive"),
|
||||
"sl1_svg",
|
||||
{},
|
||||
{"zip"},
|
||||
[] (const auto &cfg) { return std::make_unique<SL1_SVGArchive>(cfg); },
|
||||
[] (const std::string &fname, SLAImportQuality quality, const ProgrFn &progr) {
|
||||
return std::make_unique<SL1_SVGReader>(fname, quality, progr);
|
||||
}
|
||||
},
|
||||
{
|
||||
"SL2",
|
||||
"",
|
||||
"sl1_svg",
|
||||
{},
|
||||
[] (const auto &cfg) { return std::make_unique<SL1_SVGArchive>(cfg); },
|
||||
nullptr
|
||||
},
|
||||
anycubic_sla_format("pwmo", "Photon Mono"),
|
||||
anycubic_sla_format("pwmx", "Photon Mono X"),
|
||||
anycubic_sla_format("pwms", "Photon Mono SE"),
|
||||
@@ -85,28 +74,26 @@ public:
|
||||
};
|
||||
}
|
||||
|
||||
static Registry& get_instance()
|
||||
public:
|
||||
|
||||
static const Registry& get_instance()
|
||||
{
|
||||
if (!registry)
|
||||
registry = std::make_unique<Registry>();
|
||||
registry.reset(new Registry());
|
||||
|
||||
return *registry;
|
||||
}
|
||||
|
||||
static std::set<ArchiveEntry>& get()
|
||||
static const std::set<ArchiveEntry>& get()
|
||||
{
|
||||
return get_instance().entries;
|
||||
}
|
||||
|
||||
std::set<ArchiveEntry>& get_entries() { return entries; }
|
||||
};
|
||||
|
||||
std::unique_ptr<Registry> Registry::registry = nullptr;
|
||||
|
||||
std::set<ArchiveEntry> registered_sla_archives()
|
||||
const std::set<ArchiveEntry>& registered_sla_archives()
|
||||
{
|
||||
std::lock_guard lk{arch_mtx};
|
||||
|
||||
return Registry::get();
|
||||
}
|
||||
|
||||
@@ -123,8 +110,6 @@ std::vector<std::string> get_extensions(const ArchiveEntry &entry)
|
||||
|
||||
ArchiveWriterFactory get_writer_factory(const char *formatid)
|
||||
{
|
||||
std::lock_guard lk{arch_mtx};
|
||||
|
||||
ArchiveWriterFactory ret;
|
||||
auto entry = Registry::get().find(ArchiveEntry{formatid});
|
||||
if (entry != Registry::get().end())
|
||||
@@ -135,7 +120,6 @@ ArchiveWriterFactory get_writer_factory(const char *formatid)
|
||||
|
||||
ArchiveReaderFactory get_reader_factory(const char *formatid)
|
||||
{
|
||||
std::lock_guard lk{arch_mtx};
|
||||
|
||||
ArchiveReaderFactory ret;
|
||||
auto entry = Registry::get().find(ArchiveEntry{formatid});
|
||||
@@ -145,4 +129,28 @@ ArchiveReaderFactory get_reader_factory(const char *formatid)
|
||||
return ret;
|
||||
}
|
||||
|
||||
const char *get_default_extension(const char *formatid)
|
||||
{
|
||||
static constexpr const char *Empty = "";
|
||||
|
||||
const char * ret = Empty;
|
||||
|
||||
auto entry = Registry::get().find(ArchiveEntry{formatid});
|
||||
if (entry != Registry::get().end())
|
||||
ret = entry->ext;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
const ArchiveEntry * get_archive_entry(const char *formatid)
|
||||
{
|
||||
const ArchiveEntry *ret = nullptr;
|
||||
|
||||
auto entry = Registry::get().find(ArchiveEntry{formatid});
|
||||
if (entry != Registry::get().end())
|
||||
ret = &(*entry);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace Slic3r::sla
|
||||
|
||||
@@ -48,9 +48,9 @@ struct ArchiveEntry {
|
||||
: id{formatid}
|
||||
, desc{description}
|
||||
, ext{extension}
|
||||
, ext_aliases{extaliases}
|
||||
, wrfactoryfn{wrfn}
|
||||
, rdfactoryfn{rdfn}
|
||||
, ext_aliases{extaliases}
|
||||
{}
|
||||
|
||||
bool operator <(const ArchiveEntry &other) const
|
||||
@@ -61,8 +61,10 @@ struct ArchiveEntry {
|
||||
|
||||
std::vector<std::string> get_extensions(const ArchiveEntry &entry);
|
||||
|
||||
std::set<ArchiveEntry> registered_sla_archives();
|
||||
const std::set<ArchiveEntry>& registered_sla_archives();
|
||||
|
||||
const ArchiveEntry * get_archive_entry(const char *formatid);
|
||||
const char * get_default_extension(const char *formatid);
|
||||
ArchiveWriterFactory get_writer_factory(const char *formatid);
|
||||
ArchiveReaderFactory get_reader_factory(const char *formatid);
|
||||
|
||||
|
||||
@@ -312,7 +312,7 @@ namespace Slic3r {
|
||||
std::string gcode_out;
|
||||
std::string line;
|
||||
Vec2f pos = tcr.start_pos;
|
||||
Vec2f transformed_pos = pos;
|
||||
Vec2f transformed_pos = Eigen::Rotation2Df(angle) * pos + translation;
|
||||
Vec2f old_pos(-1000.1f, -1000.1f);
|
||||
|
||||
while (gcode_str) {
|
||||
@@ -901,6 +901,7 @@ namespace DoExport {
|
||||
silent_time_estimator_enabled = (config.gcode_flavor == gcfMarlinLegacy || config.gcode_flavor == gcfMarlinFirmware)
|
||||
&& config.silent_mode;
|
||||
processor.reset();
|
||||
processor.initialize_result_moves();
|
||||
processor.apply_config(config);
|
||||
processor.enable_stealth_time_estimator(silent_time_estimator_enabled);
|
||||
}
|
||||
@@ -1296,8 +1297,12 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
||||
this->placeholder_parser().set("first_layer_print_min", new ConfigOptionFloats({ bbox.min.x(), bbox.min.y() }));
|
||||
this->placeholder_parser().set("first_layer_print_max", new ConfigOptionFloats({ bbox.max.x(), bbox.max.y() }));
|
||||
this->placeholder_parser().set("first_layer_print_size", new ConfigOptionFloats({ bbox.size().x(), bbox.size().y() }));
|
||||
|
||||
std::vector<unsigned char> is_extruder_used(print.config().nozzle_diameter.size(), 0);
|
||||
this->placeholder_parser().set("num_extruders", int(print.config().nozzle_diameter.values.size()));
|
||||
// PlaceholderParser currently substitues non-existent vector values with the zero'th value, which is harmful in the case of "is_extruder_used[]"
|
||||
// as Slicer may lie about availability of such non-existent extruder.
|
||||
// We rather sacrifice 256B of memory before we change the behavior of the PlaceholderParser, which should really only fill in the non-existent
|
||||
// vector elements for filament parameters.
|
||||
std::vector<unsigned char> is_extruder_used(std::max(size_t(255), print.config().nozzle_diameter.size()), 0);
|
||||
for (unsigned int extruder_id : tool_ordering.all_extruders())
|
||||
is_extruder_used[extruder_id] = true;
|
||||
this->placeholder_parser().set("is_extruder_used", new ConfigOptionBools(is_extruder_used));
|
||||
@@ -2510,7 +2515,7 @@ void GCode::process_layer_single_object(
|
||||
int extruder_override_id = is_anything_overridden ? layer_tools.wiping_extrusions().get_extruder_override(eec, instance_id) : -1;
|
||||
return print_wipe_extrusions ?
|
||||
extruder_override_id == int(extruder_id) :
|
||||
extruder_override_id < 0 && extruder_id == correct_extruder_id;
|
||||
extruder_override_id < 0 && int(extruder_id) == correct_extruder_id;
|
||||
};
|
||||
|
||||
ExtrusionEntitiesPtr temp_fill_extrusions;
|
||||
|
||||
290
src/libslic3r/GCode/ConflictChecker.cpp
Normal file
290
src/libslic3r/GCode/ConflictChecker.cpp
Normal file
@@ -0,0 +1,290 @@
|
||||
#include "libslic3r.h"
|
||||
#include "ConflictChecker.hpp"
|
||||
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <tbb/concurrent_vector.h>
|
||||
|
||||
#include <map>
|
||||
#include <functional>
|
||||
#include <atomic>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
namespace RasterizationImpl {
|
||||
using IndexPair = std::pair<int64_t, int64_t>;
|
||||
using Grids = std::vector<IndexPair>;
|
||||
|
||||
inline constexpr int64_t RasteXDistance = scale_(1);
|
||||
inline constexpr int64_t RasteYDistance = scale_(1);
|
||||
|
||||
inline IndexPair point_map_grid_index(const Point &pt, int64_t xdist, int64_t ydist)
|
||||
{
|
||||
auto x = pt.x() / xdist;
|
||||
auto y = pt.y() / ydist;
|
||||
return std::make_pair(x, y);
|
||||
}
|
||||
|
||||
inline bool nearly_equal(const Point &p1, const Point &p2) { return std::abs(p1.x() - p2.x()) < SCALED_EPSILON && std::abs(p1.y() - p2.y()) < SCALED_EPSILON; }
|
||||
|
||||
inline Grids line_rasterization(const Line &line, int64_t xdist = RasteXDistance, int64_t ydist = RasteYDistance)
|
||||
{
|
||||
Grids res;
|
||||
Point rayStart = line.a;
|
||||
Point rayEnd = line.b;
|
||||
IndexPair currentVoxel = point_map_grid_index(rayStart, xdist, ydist);
|
||||
IndexPair lastVoxel = point_map_grid_index(rayEnd, xdist, ydist);
|
||||
|
||||
Point ray = rayEnd - rayStart;
|
||||
|
||||
double stepX = ray.x() >= 0 ? 1 : -1;
|
||||
double stepY = ray.y() >= 0 ? 1 : -1;
|
||||
|
||||
double nextVoxelBoundaryX = (currentVoxel.first + stepX) * xdist;
|
||||
double nextVoxelBoundaryY = (currentVoxel.second + stepY) * ydist;
|
||||
|
||||
if (stepX < 0) { nextVoxelBoundaryX += xdist; }
|
||||
if (stepY < 0) { nextVoxelBoundaryY += ydist; }
|
||||
|
||||
double tMaxX = ray.x() != 0 ? (nextVoxelBoundaryX - rayStart.x()) / ray.x() : DBL_MAX;
|
||||
double tMaxY = ray.y() != 0 ? (nextVoxelBoundaryY - rayStart.y()) / ray.y() : DBL_MAX;
|
||||
|
||||
double tDeltaX = ray.x() != 0 ? static_cast<double>(xdist) / ray.x() * stepX : DBL_MAX;
|
||||
double tDeltaY = ray.y() != 0 ? static_cast<double>(ydist) / ray.y() * stepY : DBL_MAX;
|
||||
|
||||
res.push_back(currentVoxel);
|
||||
|
||||
double tx = tMaxX;
|
||||
double ty = tMaxY;
|
||||
|
||||
while (lastVoxel != currentVoxel) {
|
||||
if (lastVoxel.first == currentVoxel.first) {
|
||||
for (int64_t i = currentVoxel.second; i != lastVoxel.second; i += (int64_t) stepY) {
|
||||
currentVoxel.second += (int64_t) stepY;
|
||||
res.push_back(currentVoxel);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (lastVoxel.second == currentVoxel.second) {
|
||||
for (int64_t i = currentVoxel.first; i != lastVoxel.first; i += (int64_t) stepX) {
|
||||
currentVoxel.first += (int64_t) stepX;
|
||||
res.push_back(currentVoxel);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (tx < ty) {
|
||||
currentVoxel.first += (int64_t) stepX;
|
||||
tx += tDeltaX;
|
||||
} else {
|
||||
currentVoxel.second += (int64_t) stepY;
|
||||
ty += tDeltaY;
|
||||
}
|
||||
res.push_back(currentVoxel);
|
||||
if (res.size() >= 100000) { // bug
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
} // namespace RasterizationImpl
|
||||
|
||||
void LinesBucketQueue::emplace_back_bucket(std::vector<ExtrusionPaths> &&paths, const void *objPtr, Points offsets)
|
||||
{
|
||||
if (_objsPtrToId.find(objPtr) == _objsPtrToId.end()) {
|
||||
_objsPtrToId.insert({objPtr, _objsPtrToId.size()});
|
||||
_idToObjsPtr.insert({_objsPtrToId.size() - 1, objPtr});
|
||||
}
|
||||
_buckets.emplace_back(std::move(paths), _objsPtrToId[objPtr], offsets);
|
||||
}
|
||||
|
||||
void LinesBucketQueue::build_queue()
|
||||
{
|
||||
assert(_pq.empty());
|
||||
for (LinesBucket &bucket : _buckets)
|
||||
_pq.push(&bucket);
|
||||
}
|
||||
|
||||
double LinesBucketQueue::removeLowests()
|
||||
{
|
||||
auto lowest = _pq.top();
|
||||
_pq.pop();
|
||||
double curHeight = lowest->curHeight();
|
||||
std::vector<LinesBucket *> lowests;
|
||||
lowests.push_back(lowest);
|
||||
|
||||
while (_pq.empty() == false && std::abs(_pq.top()->curHeight() - lowest->curHeight()) < EPSILON) {
|
||||
lowests.push_back(_pq.top());
|
||||
_pq.pop();
|
||||
}
|
||||
|
||||
for (LinesBucket *bp : lowests) {
|
||||
bp->raise();
|
||||
if (bp->valid()) { _pq.push(bp); }
|
||||
}
|
||||
return curHeight;
|
||||
}
|
||||
|
||||
LineWithIDs LinesBucketQueue::getCurLines() const
|
||||
{
|
||||
LineWithIDs lines;
|
||||
for (const LinesBucket &bucket : _buckets) {
|
||||
if (bucket.valid()) {
|
||||
LineWithIDs tmpLines = bucket.curLines();
|
||||
lines.insert(lines.end(), tmpLines.begin(), tmpLines.end());
|
||||
}
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
void getExtrusionPathsFromEntity(const ExtrusionEntityCollection *entity, ExtrusionPaths &paths)
|
||||
{
|
||||
std::function<void(const ExtrusionEntityCollection *, ExtrusionPaths &)> getExtrusionPathImpl = [&](const ExtrusionEntityCollection *entity, ExtrusionPaths &paths) {
|
||||
for (auto entityPtr : entity->entities) {
|
||||
if (const ExtrusionEntityCollection *collection = dynamic_cast<ExtrusionEntityCollection *>(entityPtr)) {
|
||||
getExtrusionPathImpl(collection, paths);
|
||||
} else if (const ExtrusionPath *path = dynamic_cast<ExtrusionPath *>(entityPtr)) {
|
||||
paths.push_back(*path);
|
||||
} else if (const ExtrusionMultiPath *multipath = dynamic_cast<ExtrusionMultiPath *>(entityPtr)) {
|
||||
for (const ExtrusionPath &path : multipath->paths) { paths.push_back(path); }
|
||||
} else if (const ExtrusionLoop *loop = dynamic_cast<ExtrusionLoop *>(entityPtr)) {
|
||||
for (const ExtrusionPath &path : loop->paths) { paths.push_back(path); }
|
||||
}
|
||||
}
|
||||
};
|
||||
getExtrusionPathImpl(entity, paths);
|
||||
}
|
||||
|
||||
ExtrusionPaths getExtrusionPathsFromLayer(LayerRegionPtrs layerRegionPtrs)
|
||||
{
|
||||
ExtrusionPaths paths;
|
||||
for (auto regionPtr : layerRegionPtrs) {
|
||||
getExtrusionPathsFromEntity(®ionPtr->perimeters(), paths);
|
||||
if (!regionPtr->perimeters().empty()) { getExtrusionPathsFromEntity(®ionPtr->fills(), paths); }
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
|
||||
ExtrusionPaths getExtrusionPathsFromSupportLayer(SupportLayer *supportLayer)
|
||||
{
|
||||
ExtrusionPaths paths;
|
||||
getExtrusionPathsFromEntity(&supportLayer->support_fills, paths);
|
||||
return paths;
|
||||
}
|
||||
|
||||
std::pair<std::vector<ExtrusionPaths>, std::vector<ExtrusionPaths>> getAllLayersExtrusionPathsFromObject(PrintObject *obj)
|
||||
{
|
||||
std::vector<ExtrusionPaths> objPaths, supportPaths;
|
||||
|
||||
for (auto layerPtr : obj->layers()) { objPaths.push_back(getExtrusionPathsFromLayer(layerPtr->regions())); }
|
||||
|
||||
for (auto supportLayerPtr : obj->support_layers()) { supportPaths.push_back(getExtrusionPathsFromSupportLayer(supportLayerPtr)); }
|
||||
|
||||
return {std::move(objPaths), std::move(supportPaths)};
|
||||
}
|
||||
|
||||
ConflictComputeOpt ConflictChecker::find_inter_of_lines(const LineWithIDs &lines)
|
||||
{
|
||||
using namespace RasterizationImpl;
|
||||
std::map<IndexPair, std::vector<int>> indexToLine;
|
||||
|
||||
for (int i = 0; i < (int)lines.size(); ++i) {
|
||||
const LineWithID &l1 = lines[i];
|
||||
auto indexes = line_rasterization(l1._line);
|
||||
for (auto index : indexes) {
|
||||
const auto &possibleIntersectIdxs = indexToLine[index];
|
||||
for (auto possibleIntersectIdx : possibleIntersectIdxs) {
|
||||
const LineWithID &l2 = lines[possibleIntersectIdx];
|
||||
if (auto interRes = line_intersect(l1, l2); interRes.has_value()) { return interRes; }
|
||||
}
|
||||
indexToLine[index].push_back(i);
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
ConflictResultOpt ConflictChecker::find_inter_of_lines_in_diff_objs(PrintObjectPtrs objs,
|
||||
std::optional<const FakeWipeTower *> wtdptr) // find the first intersection point of lines in different objects
|
||||
{
|
||||
if (objs.empty() || (objs.size() == 1 && objs.front()->instances().size() == 1)) { return {}; }
|
||||
|
||||
LinesBucketQueue conflictQueue;
|
||||
if (wtdptr.has_value()) { // wipe tower at 0 by default
|
||||
std::vector<ExtrusionPaths> wtpaths = (*wtdptr)->getFakeExtrusionPathsFromWipeTower();
|
||||
conflictQueue.emplace_back_bucket(std::move(wtpaths), *wtdptr, Points{Point((*wtdptr)->plate_origin)});
|
||||
}
|
||||
for (PrintObject *obj : objs) {
|
||||
std::pair<std::vector<ExtrusionPaths>, std::vector<ExtrusionPaths>> layers = getAllLayersExtrusionPathsFromObject(obj);
|
||||
|
||||
Points instances_shifts;
|
||||
for (const PrintInstance& inst : obj->instances())
|
||||
instances_shifts.emplace_back(inst.shift);
|
||||
|
||||
conflictQueue.emplace_back_bucket(std::move(layers.first), obj, instances_shifts);
|
||||
conflictQueue.emplace_back_bucket(std::move(layers.second), obj, instances_shifts);
|
||||
}
|
||||
conflictQueue.build_queue();
|
||||
|
||||
std::vector<LineWithIDs> layersLines;
|
||||
std::vector<double> heights;
|
||||
while (conflictQueue.valid()) {
|
||||
LineWithIDs lines = conflictQueue.getCurLines();
|
||||
double curHeight = conflictQueue.removeLowests();
|
||||
heights.push_back(curHeight);
|
||||
layersLines.push_back(std::move(lines));
|
||||
}
|
||||
|
||||
bool find = false;
|
||||
tbb::concurrent_vector<std::pair<ConflictComputeResult,double>> conflict;
|
||||
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, layersLines.size()), [&](tbb::blocked_range<size_t> range) {
|
||||
for (size_t i = range.begin(); i < range.end(); i++) {
|
||||
auto interRes = find_inter_of_lines(layersLines[i]);
|
||||
if (interRes.has_value()) {
|
||||
find = true;
|
||||
conflict.emplace_back(*interRes, heights[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (find) {
|
||||
std::sort(conflict.begin(), conflict.end(), [](const std::pair<ConflictComputeResult, double>& i1, const std::pair<ConflictComputeResult, double>& i2) {
|
||||
return i1.second < i2.second;
|
||||
});
|
||||
|
||||
const void *ptr1 = conflictQueue.idToObjsPtr(conflict[0].first._obj1);
|
||||
const void *ptr2 = conflictQueue.idToObjsPtr(conflict[0].first._obj2);
|
||||
double conflictHeight = conflict[0].second;
|
||||
if (wtdptr.has_value()) {
|
||||
const FakeWipeTower* wtdp = *wtdptr;
|
||||
if (ptr1 == wtdp || ptr2 == wtdp) {
|
||||
if (ptr2 == wtdp) { std::swap(ptr1, ptr2); }
|
||||
const PrintObject *obj2 = reinterpret_cast<const PrintObject *>(ptr2);
|
||||
return std::make_optional<ConflictResult>("WipeTower", obj2->model_object()->name, conflictHeight, nullptr, ptr2);
|
||||
}
|
||||
}
|
||||
const PrintObject *obj1 = reinterpret_cast<const PrintObject *>(ptr1);
|
||||
const PrintObject *obj2 = reinterpret_cast<const PrintObject *>(ptr2);
|
||||
return std::make_optional<ConflictResult>(obj1->model_object()->name, obj2->model_object()->name, conflictHeight, ptr1, ptr2);
|
||||
} else
|
||||
return {};
|
||||
}
|
||||
|
||||
ConflictComputeOpt ConflictChecker::line_intersect(const LineWithID &l1, const LineWithID &l2)
|
||||
{
|
||||
if (l1._obj_id == l2._obj_id && l1._inst_id == l2._inst_id) { return {}; } // lines are from same instance
|
||||
|
||||
Point inter;
|
||||
bool intersect = l1._line.intersection(l2._line, &inter);
|
||||
if (intersect) {
|
||||
auto dist1 = std::min(unscale(Point(l1._line.a - inter)).norm(), unscale(Point(l1._line.b - inter)).norm());
|
||||
auto dist2 = std::min(unscale(Point(l2._line.a - inter)).norm(), unscale(Point(l2._line.b - inter)).norm());
|
||||
auto dist = std::min(dist1, dist2);
|
||||
if (dist > 0.01) { return std::make_optional<ConflictComputeResult>(l1._obj_id, l2._obj_id); } // the two lines intersects if dist>0.01mm
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
129
src/libslic3r/GCode/ConflictChecker.hpp
Normal file
129
src/libslic3r/GCode/ConflictChecker.hpp
Normal file
@@ -0,0 +1,129 @@
|
||||
#ifndef slic3r_ConflictChecker_hpp_
|
||||
#define slic3r_ConflictChecker_hpp_
|
||||
|
||||
#include "../Utils.hpp"
|
||||
#include "../Model.hpp"
|
||||
#include "../Print.hpp"
|
||||
#include "../Layer.hpp"
|
||||
|
||||
#include <queue>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
struct LineWithID
|
||||
{
|
||||
Line _line;
|
||||
int _obj_id;
|
||||
int _inst_id;
|
||||
ExtrusionRole _role;
|
||||
|
||||
LineWithID(const Line& line, int obj_id, int inst_id, const ExtrusionRole& role) :
|
||||
_line(line), _obj_id(obj_id), _inst_id(inst_id), _role(role) {}
|
||||
};
|
||||
|
||||
using LineWithIDs = std::vector<LineWithID>;
|
||||
|
||||
class LinesBucket
|
||||
{
|
||||
private:
|
||||
double _curHeight = 0.0;
|
||||
unsigned _curPileIdx = 0;
|
||||
|
||||
std::vector<ExtrusionPaths> _piles;
|
||||
int _id;
|
||||
Points _offsets;
|
||||
|
||||
public:
|
||||
LinesBucket(std::vector<ExtrusionPaths> &&paths, int id, Points offsets) : _piles(paths), _id(id), _offsets(offsets) {}
|
||||
LinesBucket(LinesBucket &&) = default;
|
||||
|
||||
bool valid() const { return _curPileIdx < _piles.size(); }
|
||||
void raise()
|
||||
{
|
||||
if (valid()) {
|
||||
if (_piles[_curPileIdx].empty() == false) { _curHeight += _piles[_curPileIdx].front().height; }
|
||||
_curPileIdx++;
|
||||
}
|
||||
}
|
||||
double curHeight() const { return _curHeight; }
|
||||
LineWithIDs curLines() const
|
||||
{
|
||||
LineWithIDs lines;
|
||||
for (const ExtrusionPath &path : _piles[_curPileIdx]) {
|
||||
Polyline check_polyline;
|
||||
for (int i = 0; i < (int)_offsets.size(); ++i) {
|
||||
check_polyline = path.polyline;
|
||||
check_polyline.translate(_offsets[i]);
|
||||
Lines tmpLines = check_polyline.lines();
|
||||
for (const Line& line : tmpLines) { lines.emplace_back(line, _id, i, path.role()); }
|
||||
}
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
friend bool operator>(const LinesBucket &left, const LinesBucket &right) { return left._curHeight > right._curHeight; }
|
||||
friend bool operator<(const LinesBucket &left, const LinesBucket &right) { return left._curHeight < right._curHeight; }
|
||||
friend bool operator==(const LinesBucket &left, const LinesBucket &right) { return left._curHeight == right._curHeight; }
|
||||
};
|
||||
|
||||
struct LinesBucketPtrComp
|
||||
{
|
||||
bool operator()(const LinesBucket *left, const LinesBucket *right) { return *left > *right; }
|
||||
};
|
||||
|
||||
class LinesBucketQueue
|
||||
{
|
||||
private:
|
||||
std::vector<LinesBucket> _buckets;
|
||||
std::priority_queue<LinesBucket *, std::vector<LinesBucket *>, LinesBucketPtrComp> _pq;
|
||||
std::map<int, const void *> _idToObjsPtr;
|
||||
std::map<const void *, int> _objsPtrToId;
|
||||
|
||||
public:
|
||||
void emplace_back_bucket(std::vector<ExtrusionPaths> &&paths, const void *objPtr, Points offset);
|
||||
void build_queue();
|
||||
bool valid() const { return _pq.empty() == false; }
|
||||
const void *idToObjsPtr(int id)
|
||||
{
|
||||
if (_idToObjsPtr.find(id) != _idToObjsPtr.end())
|
||||
return _idToObjsPtr[id];
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
double removeLowests();
|
||||
LineWithIDs getCurLines() const;
|
||||
};
|
||||
|
||||
void getExtrusionPathsFromEntity(const ExtrusionEntityCollection *entity, ExtrusionPaths &paths);
|
||||
|
||||
ExtrusionPaths getExtrusionPathsFromLayer(LayerRegionPtrs layerRegionPtrs);
|
||||
|
||||
ExtrusionPaths getExtrusionPathsFromSupportLayer(SupportLayer *supportLayer);
|
||||
|
||||
std::pair<std::vector<ExtrusionPaths>, std::vector<ExtrusionPaths>> getAllLayersExtrusionPathsFromObject(PrintObject *obj);
|
||||
|
||||
struct ConflictComputeResult
|
||||
{
|
||||
int _obj1;
|
||||
int _obj2;
|
||||
|
||||
ConflictComputeResult(int o1, int o2) : _obj1(o1), _obj2(o2) {}
|
||||
ConflictComputeResult() = default;
|
||||
};
|
||||
|
||||
using ConflictComputeOpt = std::optional<ConflictComputeResult>;
|
||||
|
||||
using ConflictObjName = std::optional<std::pair<std::string, std::string>>;
|
||||
|
||||
struct ConflictChecker
|
||||
{
|
||||
static ConflictResultOpt find_inter_of_lines_in_diff_objs(PrintObjectPtrs objs, std::optional<const FakeWipeTower *> wtdptr);
|
||||
static ConflictComputeOpt find_inter_of_lines(const LineWithIDs &lines);
|
||||
static ConflictComputeOpt line_intersect(const LineWithID &l1, const LineWithID &l2);
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif
|
||||
@@ -189,13 +189,13 @@ std::vector<ExtendedPoint> estimate_points_properties(const POINTS
|
||||
head_window_acc -= distances_for_curvature[point_idx - 1];
|
||||
head_angle_acc -= angles_for_curvature[point_idx - 1];
|
||||
}
|
||||
while (tail_window_acc > window_size * 0.5 && tail_point < point_idx) {
|
||||
while (tail_window_acc > window_size * 0.5 && int(tail_point) < point_idx) {
|
||||
tail_window_acc -= distances_for_curvature[tail_point];
|
||||
tail_angle_acc -= angles_for_curvature[tail_point];
|
||||
tail_point++;
|
||||
}
|
||||
|
||||
while (head_window_acc < window_size * 0.5 && head_point < int(points.size()) - 1) {
|
||||
while (head_window_acc < window_size * 0.5 && int(head_point) < int(points.size()) - 1) {
|
||||
head_window_acc += distances_for_curvature[head_point];
|
||||
head_angle_acc += angles_for_curvature[head_point];
|
||||
head_point++;
|
||||
|
||||
@@ -450,6 +450,7 @@ void GCodeProcessorResult::reset() {
|
||||
filament_cost = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_COST);
|
||||
custom_gcode_per_print_z = std::vector<CustomGCode::Item>();
|
||||
spiral_vase_layers = std::vector<std::pair<float, std::pair<size_t, size_t>>>();
|
||||
conflict_result = std::nullopt;
|
||||
time = 0;
|
||||
}
|
||||
#else
|
||||
@@ -468,6 +469,7 @@ void GCodeProcessorResult::reset() {
|
||||
filament_cost = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_COST);
|
||||
custom_gcode_per_print_z = std::vector<CustomGCode::Item>();
|
||||
spiral_vase_layers = std::vector<std::pair<float, std::pair<size_t, size_t>>>();
|
||||
conflict_result = std::nullopt;
|
||||
}
|
||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||
|
||||
@@ -1063,14 +1065,16 @@ void GCodeProcessor::process_file(const std::string& filename, std::function<voi
|
||||
apply_config_superslicer(filename);
|
||||
else if (m_producer == EProducer::KissSlicer)
|
||||
apply_config_kissslicer(filename);
|
||||
|
||||
if (m_result.extruders_count == 0)
|
||||
m_result.extruders_count = MIN_EXTRUDERS_COUNT;
|
||||
}
|
||||
}
|
||||
|
||||
// process gcode
|
||||
m_result.filename = filename;
|
||||
m_result.id = ++s_result_id;
|
||||
// 1st move must be a dummy move
|
||||
m_result.moves.emplace_back(GCodeProcessorResult::MoveVertex());
|
||||
initialize_result_moves();
|
||||
size_t parse_line_callback_cntr = 10000;
|
||||
m_parser.parse_file(filename, [this, cancel_callback, &parse_line_callback_cntr](GCodeReader& reader, const GCodeReader::GCodeLine& line) {
|
||||
if (-- parse_line_callback_cntr == 0) {
|
||||
@@ -1097,8 +1101,6 @@ void GCodeProcessor::initialize(const std::string& filename)
|
||||
// process gcode
|
||||
m_result.filename = filename;
|
||||
m_result.id = ++s_result_id;
|
||||
// 1st move must be a dummy move
|
||||
m_result.moves.emplace_back(GCodeProcessorResult::MoveVertex());
|
||||
}
|
||||
|
||||
void GCodeProcessor::process_buffer(const std::string &buffer)
|
||||
@@ -2353,28 +2355,58 @@ void GCodeProcessor::process_G0(const GCodeReader::GCodeLine& line)
|
||||
}
|
||||
|
||||
void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
||||
{
|
||||
std::array<std::optional<double>, 4> g1_axes = { std::nullopt, std::nullopt, std::nullopt, std::nullopt };
|
||||
if (line.has_x()) g1_axes[X] = (double)line.x();
|
||||
if (line.has_y()) g1_axes[Y] = (double)line.y();
|
||||
if (line.has_z()) g1_axes[Z] = (double)line.z();
|
||||
if (line.has_e()) g1_axes[E] = (double)line.e();
|
||||
std::optional<double> g1_feedrate = std::nullopt;
|
||||
if (line.has_f()) g1_feedrate = (double)line.f();
|
||||
std::optional<std::string> g1_cmt = std::nullopt;
|
||||
if (!line.comment().empty()) g1_cmt = line.comment();
|
||||
|
||||
process_G1(g1_axes, g1_feedrate, g1_cmt);
|
||||
}
|
||||
|
||||
void GCodeProcessor::process_G1(const std::array<std::optional<double>, 4>& axes, std::optional<double> feedrate, std::optional<std::string> cmt)
|
||||
{
|
||||
const float filament_diameter = (static_cast<size_t>(m_extruder_id) < m_result.filament_diameters.size()) ? m_result.filament_diameters[m_extruder_id] : m_result.filament_diameters.back();
|
||||
const float filament_radius = 0.5f * filament_diameter;
|
||||
const float area_filament_cross_section = static_cast<float>(M_PI) * sqr(filament_radius);
|
||||
|
||||
auto move_type = [this](const AxisCoords& delta_pos) {
|
||||
EMoveType type = EMoveType::Noop;
|
||||
|
||||
if (m_wiping)
|
||||
type = EMoveType::Wipe;
|
||||
return EMoveType::Wipe;
|
||||
else if (delta_pos[E] < 0.0f)
|
||||
type = (delta_pos[X] != 0.0f || delta_pos[Y] != 0.0f || delta_pos[Z] != 0.0f) ? EMoveType::Travel : EMoveType::Retract;
|
||||
return (delta_pos[X] != 0.0f || delta_pos[Y] != 0.0f || delta_pos[Z] != 0.0f) ? EMoveType::Travel : EMoveType::Retract;
|
||||
else if (delta_pos[E] > 0.0f) {
|
||||
if (delta_pos[X] == 0.0f && delta_pos[Y] == 0.0f)
|
||||
type = (delta_pos[Z] == 0.0f) ? EMoveType::Unretract : EMoveType::Travel;
|
||||
return (delta_pos[Z] == 0.0f) ? EMoveType::Unretract : EMoveType::Travel;
|
||||
else if (delta_pos[X] != 0.0f || delta_pos[Y] != 0.0f)
|
||||
type = EMoveType::Extrude;
|
||||
}
|
||||
return EMoveType::Extrude;
|
||||
}
|
||||
else if (delta_pos[X] != 0.0f || delta_pos[Y] != 0.0f || delta_pos[Z] != 0.0f)
|
||||
type = EMoveType::Travel;
|
||||
return EMoveType::Travel;
|
||||
|
||||
return type;
|
||||
return EMoveType::Noop;
|
||||
};
|
||||
|
||||
auto extract_absolute_position_on_axis = [&](Axis axis, std::optional<double> value, double area_filament_cross_section)
|
||||
{
|
||||
if (value.has_value()) {
|
||||
bool is_relative = (m_global_positioning_type == EPositioningType::Relative);
|
||||
if (axis == E)
|
||||
is_relative |= (m_e_local_positioning_type == EPositioningType::Relative);
|
||||
|
||||
const double lengthsScaleFactor = (m_units == EUnits::Inches) ? double(INCHES_TO_MM) : 1.0;
|
||||
double ret = *value * lengthsScaleFactor;
|
||||
if (axis == E && m_use_volumetric_e)
|
||||
ret /= area_filament_cross_section;
|
||||
return is_relative ? m_start_position[axis] + ret : m_origin[axis] + ret;
|
||||
}
|
||||
else
|
||||
return m_start_position[axis];
|
||||
};
|
||||
|
||||
++m_g1_line_id;
|
||||
@@ -2384,27 +2416,26 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
||||
|
||||
// updates axes positions from line
|
||||
for (unsigned char a = X; a <= E; ++a) {
|
||||
m_end_position[a] = extract_absolute_position_on_axis((Axis)a, line, double(area_filament_cross_section));
|
||||
m_end_position[a] = extract_absolute_position_on_axis((Axis)a, axes[a], double(area_filament_cross_section));
|
||||
}
|
||||
|
||||
// updates feedrate from line, if present
|
||||
if (line.has_f())
|
||||
m_feedrate = m_feed_multiply.current * line.f() * MMMIN_TO_MMSEC;
|
||||
if (feedrate.has_value())
|
||||
m_feedrate = m_feed_multiply.current * (*feedrate) * MMMIN_TO_MMSEC;
|
||||
|
||||
// calculates movement deltas
|
||||
AxisCoords delta_pos;
|
||||
// calculates movement deltas
|
||||
AxisCoords delta_pos;
|
||||
for (unsigned char a = X; a <= E; ++a)
|
||||
delta_pos[a] = m_end_position[a] - m_start_position[a];
|
||||
|
||||
delta_pos[a] = m_end_position[a] - m_start_position[a];
|
||||
|
||||
if (std::all_of(delta_pos.begin(), delta_pos.end(), [](double d) { return d == 0.; }))
|
||||
return;
|
||||
|
||||
return;
|
||||
|
||||
const float volume_extruded_filament = area_filament_cross_section * delta_pos[E];
|
||||
|
||||
if (volume_extruded_filament != 0.)
|
||||
m_used_filaments.increase_caches(volume_extruded_filament,
|
||||
m_extruder_id, area_filament_cross_section * m_parking_position,
|
||||
area_filament_cross_section * m_extra_loading_move);
|
||||
m_used_filaments.increase_caches(volume_extruded_filament, m_extruder_id, area_filament_cross_section * m_parking_position,
|
||||
area_filament_cross_section * m_extra_loading_move);
|
||||
|
||||
const EMoveType type = move_type(delta_pos);
|
||||
if (type == EMoveType::Extrude) {
|
||||
@@ -2421,7 +2452,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
||||
m_height = m_forced_height;
|
||||
else if (m_layer_id == 0)
|
||||
m_height = m_first_layer_height + m_z_offset;
|
||||
else if (line.comment() != INTERNAL_G2G3_TAG){
|
||||
else if (!cmt.has_value() || *cmt != INTERNAL_G2G3_TAG) {
|
||||
if (m_end_position[Z] > m_extruded_last_z + EPSILON && delta_pos[Z] == 0.0)
|
||||
m_height = m_end_position[Z] - m_extruded_last_z;
|
||||
}
|
||||
@@ -2432,7 +2463,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
||||
if (m_end_position[Z] == 0.0f || (m_extrusion_role == GCodeExtrusionRole::Custom && m_layer_id == 0))
|
||||
m_end_position[Z] = m_height;
|
||||
|
||||
if (line.comment() != INTERNAL_G2G3_TAG)
|
||||
if (!cmt.has_value() || *cmt != INTERNAL_G2G3_TAG)
|
||||
m_extruded_last_z = m_end_position[Z];
|
||||
m_options_z_corrector.update(m_height);
|
||||
|
||||
@@ -2465,7 +2496,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
||||
|
||||
// time estimate section
|
||||
auto move_length = [](const AxisCoords& delta_pos) {
|
||||
float sq_xyz_length = sqr(delta_pos[X]) + sqr(delta_pos[Y]) + sqr(delta_pos[Z]);
|
||||
const float sq_xyz_length = sqr(delta_pos[X]) + sqr(delta_pos[Y]) + sqr(delta_pos[Z]);
|
||||
return (sq_xyz_length > 0.0f) ? std::sqrt(sq_xyz_length) : std::abs(delta_pos[E]);
|
||||
};
|
||||
|
||||
@@ -2486,8 +2517,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
||||
TimeMachine::State& prev = machine.prev;
|
||||
std::vector<TimeBlock>& blocks = machine.blocks;
|
||||
|
||||
curr.feedrate = (delta_pos[E] == 0.0f) ?
|
||||
minimum_travel_feedrate(static_cast<PrintEstimatedStatistics::ETimeMode>(i), m_feedrate) :
|
||||
curr.feedrate = (delta_pos[E] == 0.0f) ? minimum_travel_feedrate(static_cast<PrintEstimatedStatistics::ETimeMode>(i), m_feedrate) :
|
||||
minimum_feedrate(static_cast<PrintEstimatedStatistics::ETimeMode>(i), m_feedrate);
|
||||
|
||||
TimeBlock block;
|
||||
@@ -2522,11 +2552,9 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
||||
}
|
||||
|
||||
// calculates block acceleration
|
||||
float acceleration =
|
||||
(type == EMoveType::Travel) ? get_travel_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i)) :
|
||||
(is_extrusion_only_move(delta_pos) ?
|
||||
get_retract_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i)) :
|
||||
get_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i)));
|
||||
float acceleration = (type == EMoveType::Travel) ? get_travel_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i)) :
|
||||
(is_extrusion_only_move(delta_pos) ? get_retract_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i)) :
|
||||
get_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i)));
|
||||
|
||||
for (unsigned char a = X; a <= E; ++a) {
|
||||
const float axis_max_acceleration = get_axis_max_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a));
|
||||
@@ -2552,8 +2580,8 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
||||
// calculates block entry feedrate
|
||||
float vmax_junction = curr.safe_feedrate;
|
||||
if (!blocks.empty() && prev.feedrate > PREVIOUS_FEEDRATE_THRESHOLD) {
|
||||
bool prev_speed_larger = prev.feedrate > block.feedrate_profile.cruise;
|
||||
float smaller_speed_factor = prev_speed_larger ? (block.feedrate_profile.cruise / prev.feedrate) : (prev.feedrate / block.feedrate_profile.cruise);
|
||||
const bool prev_speed_larger = prev.feedrate > block.feedrate_profile.cruise;
|
||||
const float smaller_speed_factor = prev_speed_larger ? (block.feedrate_profile.cruise / prev.feedrate) : (prev.feedrate / block.feedrate_profile.cruise);
|
||||
// Pick the smaller of the nominal speeds. Higher speed shall not be achieved at the junction during coasting.
|
||||
vmax_junction = prev_speed_larger ? block.feedrate_profile.cruise : prev.feedrate;
|
||||
|
||||
@@ -2575,18 +2603,18 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
||||
|
||||
// Calculate the jerk depending on whether the axis is coasting in the same direction or reversing a direction.
|
||||
const float jerk =
|
||||
(v_exit > v_entry) ?
|
||||
((v_entry > 0.0f || v_exit < 0.0f) ?
|
||||
// coasting
|
||||
(v_exit - v_entry) :
|
||||
// axis reversal
|
||||
std::max(v_exit, -v_entry)) :
|
||||
// v_exit <= v_entry
|
||||
((v_entry < 0.0f || v_exit > 0.0f) ?
|
||||
// coasting
|
||||
(v_entry - v_exit) :
|
||||
// axis reversal
|
||||
std::max(-v_exit, v_entry));
|
||||
(v_exit > v_entry) ?
|
||||
((v_entry > 0.0f || v_exit < 0.0f) ?
|
||||
// coasting
|
||||
(v_exit - v_entry) :
|
||||
// axis reversal
|
||||
std::max(v_exit, -v_entry)) :
|
||||
// v_exit <= v_entry
|
||||
((v_entry < 0.0f || v_exit > 0.0f) ?
|
||||
// coasting
|
||||
(v_entry - v_exit) :
|
||||
// axis reversal
|
||||
std::max(-v_exit, v_entry));
|
||||
|
||||
const float axis_max_jerk = get_axis_max_jerk(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a));
|
||||
if (jerk > axis_max_jerk) {
|
||||
@@ -2665,7 +2693,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
||||
}
|
||||
|
||||
// store move
|
||||
store_move_vertex(type, line.comment() == INTERNAL_G2G3_TAG);
|
||||
store_move_vertex(type, cmt.has_value() && *cmt == INTERNAL_G2G3_TAG);
|
||||
}
|
||||
|
||||
void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line, bool clockwise)
|
||||
@@ -2694,7 +2722,7 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line, bool cloc
|
||||
double delta_z() const { return end.z() - start.z(); }
|
||||
|
||||
double length() const { return angle * start_radius(); }
|
||||
double travel_length() const { return std::sqrt(sqr(length() + sqr(delta_z()))); }
|
||||
double travel_length() const { return std::sqrt(sqr(length()) + sqr(delta_z())); }
|
||||
double start_radius() const { return (start - center).norm(); }
|
||||
double end_radius() const { return (end - center).norm(); }
|
||||
|
||||
@@ -2779,18 +2807,18 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line, bool cloc
|
||||
return ret;
|
||||
};
|
||||
|
||||
auto internal_only_g1_line = [](const AxisCoords& target, bool has_z, const std::optional<float>& feedrate, const std::optional<float>& extrusion) {
|
||||
std::string ret = (boost::format("G1 X%1% Y%2%") % target[X] % target[Y]).str();
|
||||
auto internal_only_g1_line = [this](const AxisCoords& target, bool has_z, const std::optional<float>& feedrate, const std::optional<float>& extrusion) {
|
||||
std::array<std::optional<double>, 4> g1_axes = { target[X], target[Y], std::nullopt, std::nullopt };
|
||||
std::optional<double> g1_feedrate = std::nullopt;
|
||||
if (has_z)
|
||||
ret += (boost::format(" Z%1%") % target[Z]).str();
|
||||
g1_axes[Z] = target[Z];
|
||||
if (feedrate.has_value())
|
||||
ret += (boost::format(" F%1%") % *feedrate).str();
|
||||
g1_feedrate = (double)*feedrate;
|
||||
if (extrusion.has_value())
|
||||
ret += (boost::format(" E%1%") % target[E]).str();
|
||||
g1_axes[E] = target[E];
|
||||
std::optional<std::string> g1_cmt = INTERNAL_G2G3_TAG;
|
||||
|
||||
ret += (boost::format(" ;%1%\n") % INTERNAL_G2G3_TAG).str();
|
||||
|
||||
return ret;
|
||||
process_G1(g1_axes, g1_feedrate, g1_cmt);
|
||||
};
|
||||
|
||||
// calculate arc segments
|
||||
@@ -2799,17 +2827,16 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line, bool cloc
|
||||
// https://github.com/qidi3d/QIDI-Firmware/blob/MK3/Firmware/motion_control.cpp
|
||||
|
||||
// segments count
|
||||
static const double MM_PER_ARC_SEGMENT = 0.5;
|
||||
const size_t segments = std::ceil(travel_length / MM_PER_ARC_SEGMENT);
|
||||
assert(segments >= 1);
|
||||
static const double MM_PER_ARC_SEGMENT = 1.0;
|
||||
const size_t segments = std::max<size_t>(std::floor(travel_length / MM_PER_ARC_SEGMENT), 1);
|
||||
|
||||
const double theta_per_segment = arc.angle / double(segments);
|
||||
const double z_per_segment = arc.delta_z() / double(segments);
|
||||
const double extruder_per_segment = (extrusion.has_value()) ? *extrusion / double(segments) : 0.0;
|
||||
const double inv_segment = 1.0 / double(segments);
|
||||
const double theta_per_segment = arc.angle * inv_segment;
|
||||
const double z_per_segment = arc.delta_z() * inv_segment;
|
||||
const double extruder_per_segment = (extrusion.has_value()) ? *extrusion * inv_segment : 0.0;
|
||||
|
||||
const double sq_theta_per_segment = sqr(theta_per_segment);
|
||||
const double cos_T = 1.0 - 0.5 * sq_theta_per_segment; // Small angle approximation
|
||||
const double sin_T = theta_per_segment - sq_theta_per_segment * theta_per_segment / 6.0; // Small angle approximation
|
||||
const double cos_T = 1.0 - 0.5 * sqr(theta_per_segment); // Small angle approximation
|
||||
const double sin_T = theta_per_segment;
|
||||
|
||||
AxisCoords prev_target = m_start_position;
|
||||
AxisCoords arc_target;
|
||||
@@ -2821,28 +2848,25 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line, bool cloc
|
||||
arc_target[E] = m_start_position[E];
|
||||
|
||||
static const size_t N_ARC_CORRECTION = 25;
|
||||
|
||||
Vec3d curr_rel_arc_start = arc.relative_start();
|
||||
|
||||
std::string gcode;
|
||||
|
||||
size_t n_arc_correction = N_ARC_CORRECTION;
|
||||
size_t count = 0;
|
||||
|
||||
for (size_t i = 1; i < segments; ++i) {
|
||||
if (n_arc_correction-- == 0) {
|
||||
// Calculate the actual position for r_axis_x and r_axis_y
|
||||
const double cos_Ti = ::cos((double)i * theta_per_segment);
|
||||
const double sin_Ti = ::sin((double)i * theta_per_segment);
|
||||
curr_rel_arc_start.x() = -double(rel_center.x()) * cos_Ti + double(rel_center.y()) * sin_Ti;
|
||||
curr_rel_arc_start.y() = -double(rel_center.x()) * sin_Ti - double(rel_center.y()) * cos_Ti;
|
||||
// reset n_arc_correction
|
||||
n_arc_correction = N_ARC_CORRECTION;
|
||||
}
|
||||
else {
|
||||
// Calculate X and Y using the small angle approximation
|
||||
if (count < N_ARC_CORRECTION) {
|
||||
// Apply vector rotation matrix
|
||||
const float r_axisi = curr_rel_arc_start.x() * sin_T + curr_rel_arc_start.y() * cos_T;
|
||||
curr_rel_arc_start.x() = curr_rel_arc_start.x() * cos_T - curr_rel_arc_start.y() * sin_T;
|
||||
curr_rel_arc_start.y() = r_axisi;
|
||||
++count;
|
||||
}
|
||||
else {
|
||||
// Arc correction to radius vector. Computed only every N_ARC_CORRECTION increments.
|
||||
// Compute exact location by applying transformation matrix from initial radius vector(=-offset).
|
||||
const double cos_Ti = ::cos(i * theta_per_segment);
|
||||
const double sin_Ti = ::sin(i * theta_per_segment);
|
||||
curr_rel_arc_start.x() = -double(rel_center.x()) * cos_Ti + double(rel_center.y()) * sin_Ti;
|
||||
curr_rel_arc_start.y() = -double(rel_center.x()) * sin_Ti - double(rel_center.y()) * cos_Ti;
|
||||
count = 0;
|
||||
}
|
||||
|
||||
// Update arc_target location
|
||||
@@ -2851,23 +2875,14 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line, bool cloc
|
||||
arc_target[Z] += z_per_segment;
|
||||
arc_target[E] += extruder_per_segment;
|
||||
|
||||
gcode += internal_only_g1_line(adjust_target(arc_target, prev_target), z_per_segment != 0.0, feedrate, extrusion);
|
||||
m_start_position = m_end_position; // this is required because we are skipping the call to process_gcode_line()
|
||||
internal_only_g1_line(adjust_target(arc_target, prev_target), z_per_segment != 0.0, (i == 1) ? feedrate : std::nullopt, extrusion);
|
||||
prev_target = arc_target;
|
||||
|
||||
// feedrate is constant, we do not need to repeat it
|
||||
feedrate.reset();
|
||||
}
|
||||
|
||||
// Ensure last segment arrives at target location.
|
||||
gcode += internal_only_g1_line(adjust_target(end_position, prev_target), arc.delta_z() != 0.0, feedrate, extrusion);
|
||||
|
||||
// process fake gcode lines
|
||||
GCodeReader parser;
|
||||
parser.parse_buffer(gcode, [this](GCodeReader&, const GCodeReader::GCodeLine& line) {
|
||||
// force all lines to share the same id
|
||||
--m_line_id;
|
||||
process_gcode_line(line, false);
|
||||
});
|
||||
m_start_position = m_end_position; // this is required because we are skipping the call to process_gcode_line()
|
||||
internal_only_g1_line(adjust_target(end_position, prev_target), arc.delta_z() != 0.0, (segments == 1) ? feedrate : std::nullopt, extrusion);
|
||||
}
|
||||
|
||||
void GCodeProcessor::process_G10(const GCodeReader::GCodeLine& line)
|
||||
@@ -3590,14 +3605,16 @@ void GCodeProcessor::post_process()
|
||||
auto rev_it = m_lines.rbegin() + rev_it_dist;
|
||||
auto start_rev_it = rev_it;
|
||||
|
||||
std::string curr_cmd = GCodeReader::GCodeLine::extract_cmd(rev_it->line);
|
||||
// backtrace into the cache to find the place where to insert the line
|
||||
while (rev_it != m_lines.rend() && rev_it->time > time_threshold_i && GCodeReader::GCodeLine::extract_cmd(rev_it->line) != cmd) {
|
||||
while (rev_it != m_lines.rend() && rev_it->time > time_threshold_i && curr_cmd != cmd && curr_cmd != "G28" && curr_cmd != "G29") {
|
||||
rev_it->line = line_replacer(rev_it->line);
|
||||
++rev_it;
|
||||
curr_cmd = GCodeReader::GCodeLine::extract_cmd(rev_it->line);
|
||||
}
|
||||
|
||||
// we met the previous evenience of cmd. stop inserting lines
|
||||
if (rev_it != m_lines.rend() && GCodeReader::GCodeLine::extract_cmd(rev_it->line) == cmd)
|
||||
// we met the previous evenience of cmd, or a G28/G29 command. stop inserting lines
|
||||
if (rev_it != m_lines.rend() && (curr_cmd == cmd || curr_cmd == "G28" || curr_cmd == "G29"))
|
||||
break;
|
||||
|
||||
// insert the line for the current step
|
||||
|
||||
@@ -82,6 +82,22 @@ namespace Slic3r {
|
||||
}
|
||||
};
|
||||
|
||||
struct ConflictResult
|
||||
{
|
||||
std::string _objName1;
|
||||
std::string _objName2;
|
||||
double _height;
|
||||
const void* _obj1; // nullptr means wipe tower
|
||||
const void* _obj2;
|
||||
int layer = -1;
|
||||
ConflictResult(const std::string& objName1, const std::string& objName2, double height, const void* obj1, const void* obj2)
|
||||
: _objName1(objName1), _objName2(objName2), _height(height), _obj1(obj1), _obj2(obj2)
|
||||
{}
|
||||
ConflictResult() = default;
|
||||
};
|
||||
|
||||
using ConflictResultOpt = std::optional<ConflictResult>;
|
||||
|
||||
struct GCodeProcessorResult
|
||||
{
|
||||
struct SettingsIds
|
||||
@@ -137,6 +153,8 @@ namespace Slic3r {
|
||||
std::vector<CustomGCode::Item> custom_gcode_per_print_z;
|
||||
std::vector<std::pair<float, std::pair<size_t, size_t>>> spiral_vase_layers;
|
||||
|
||||
ConflictResultOpt conflict_result;
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
int64_t time{ 0 };
|
||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||
@@ -621,6 +639,11 @@ namespace Slic3r {
|
||||
|
||||
// Streaming interface, for processing G-codes just generated by QIDISlicer in a pipelined fashion.
|
||||
void initialize(const std::string& filename);
|
||||
void initialize_result_moves() {
|
||||
// 1st move must be a dummy move
|
||||
assert(m_result.moves.empty());
|
||||
m_result.moves.emplace_back(GCodeProcessorResult::MoveVertex());
|
||||
}
|
||||
void process_buffer(const std::string& buffer);
|
||||
void finalize(bool post_process);
|
||||
|
||||
@@ -657,6 +680,8 @@ namespace Slic3r {
|
||||
// Move
|
||||
void process_G0(const GCodeReader::GCodeLine& line);
|
||||
void process_G1(const GCodeReader::GCodeLine& line);
|
||||
void process_G1(const std::array<std::optional<double>, 4>& axes = { std::nullopt, std::nullopt, std::nullopt, std::nullopt },
|
||||
std::optional<double> feedrate = std::nullopt, std::optional<std::string> cmt = std::nullopt);
|
||||
|
||||
// Arc Move
|
||||
void process_G2_G3(const GCodeReader::GCodeLine& line, bool clockwise);
|
||||
|
||||
@@ -581,7 +581,7 @@ void WipeTower::set_extruder(size_t idx, const PrintConfig& config)
|
||||
m_filpar.push_back(FilamentParameters());
|
||||
|
||||
m_filpar[idx].material = config.filament_type.get_at(idx);
|
||||
m_filpar[idx].is_soluble = config.wipe_tower_extruder == 0 ? config.filament_soluble.get_at(idx) : (idx != config.wipe_tower_extruder - 1);
|
||||
m_filpar[idx].is_soluble = config.wipe_tower_extruder == 0 ? config.filament_soluble.get_at(idx) : (idx != size_t(config.wipe_tower_extruder - 1));
|
||||
m_filpar[idx].temperature = config.temperature.get_at(idx);
|
||||
m_filpar[idx].first_layer_temperature = config.first_layer_temperature.get_at(idx);
|
||||
|
||||
@@ -1292,7 +1292,6 @@ WipeTower::ToolChangeResult WipeTower::finish_layer()
|
||||
|
||||
// brim (first layer only)
|
||||
if (first_layer) {
|
||||
box_coordinates box = wt_box;
|
||||
size_t loops_num = (m_wipe_tower_brim_width + spacing/2.f) / spacing;
|
||||
|
||||
for (size_t i = 0; i < loops_num; ++ i) {
|
||||
@@ -1563,4 +1562,19 @@ void WipeTower::generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::vector<std::pair<float, float>> WipeTower::get_z_and_depth_pairs() const
|
||||
{
|
||||
std::vector<std::pair<float, float>> out = {{0.f, m_wipe_tower_depth}};
|
||||
for (const WipeTowerInfo& wti : m_plan) {
|
||||
assert(wti.depth < wti.depth + WT_EPSILON);
|
||||
if (wti.depth < out.back().second - WT_EPSILON)
|
||||
out.emplace_back(wti.z, wti.depth);
|
||||
}
|
||||
if (out.back().first < m_wipe_tower_height - WT_EPSILON)
|
||||
out.emplace_back(m_wipe_tower_height, 0.f);
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
@@ -143,6 +143,7 @@ public:
|
||||
void generate(std::vector<std::vector<ToolChangeResult>> &result);
|
||||
|
||||
float get_depth() const { return m_wipe_tower_depth; }
|
||||
std::vector<std::pair<float, float>> get_z_and_depth_pairs() const;
|
||||
float get_brim_width() const { return m_wipe_tower_brim_width_real; }
|
||||
float get_wipe_tower_height() const { return m_wipe_tower_height; }
|
||||
|
||||
|
||||
@@ -243,9 +243,10 @@ bool GCodeReader::GCodeLine::has_value(char axis, float &value) const
|
||||
if (c == nullptr)
|
||||
return false;
|
||||
// Try to parse the numeric value.
|
||||
char *pend = nullptr;
|
||||
double v = strtod(++ c, &pend);
|
||||
if (pend != nullptr && is_end_of_word(*pend)) {
|
||||
double v = 0.;
|
||||
const char* end = m_raw.c_str() + m_raw.size();
|
||||
auto [pend, ec] = fast_float::from_chars(++c, end, v);
|
||||
if (pend != c && is_end_of_word(*pend)) {
|
||||
// The axis value has been parsed correctly.
|
||||
value = float(v);
|
||||
return true;
|
||||
|
||||
@@ -656,12 +656,22 @@ void Layer::make_perimeters()
|
||||
layer_region_ids.push_back(region_id);
|
||||
for (LayerRegionPtrs::const_iterator it = layerm + 1; it != m_regions.end(); ++it)
|
||||
if (! (*it)->slices().empty()) {
|
||||
LayerRegion* other_layerm = *it;
|
||||
const PrintRegionConfig &other_config = other_layerm->region().config();
|
||||
if (config.perimeter_extruder == other_config.perimeter_extruder
|
||||
LayerRegion *other_layerm = *it;
|
||||
const PrintRegionConfig &other_config = other_layerm->region().config();
|
||||
bool dynamic_overhang_speed_compatibility = config.enable_dynamic_overhang_speeds ==
|
||||
other_config.enable_dynamic_overhang_speeds;
|
||||
if (dynamic_overhang_speed_compatibility && config.enable_dynamic_overhang_speeds) {
|
||||
dynamic_overhang_speed_compatibility = config.overhang_speed_0 == other_config.overhang_speed_0 &&
|
||||
config.overhang_speed_1 == other_config.overhang_speed_1 &&
|
||||
config.overhang_speed_2 == other_config.overhang_speed_2 &&
|
||||
config.overhang_speed_3 == other_config.overhang_speed_3;
|
||||
}
|
||||
|
||||
if (config.perimeter_extruder == other_config.perimeter_extruder
|
||||
&& config.perimeters == other_config.perimeters
|
||||
&& config.perimeter_speed == other_config.perimeter_speed
|
||||
&& config.external_perimeter_speed == other_config.external_perimeter_speed
|
||||
&& dynamic_overhang_speed_compatibility
|
||||
&& (config.gap_fill_enabled ? config.gap_fill_speed.value : 0.) ==
|
||||
(other_config.gap_fill_enabled ? other_config.gap_fill_speed.value : 0.)
|
||||
&& config.overhangs == other_config.overhangs
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "ExPolygon.hpp"
|
||||
#include "Flow.hpp"
|
||||
#include "Layer.hpp"
|
||||
#include "BridgeDetector.hpp"
|
||||
#include "ClipperUtils.hpp"
|
||||
@@ -169,7 +170,10 @@ static ExPolygons fill_surfaces_extract_expolygons(Surfaces &surfaces, std::init
|
||||
Surfaces expand_bridges_detect_orientations(
|
||||
Surfaces &surfaces,
|
||||
ExPolygons &shells,
|
||||
const Algorithm::RegionExpansionParameters &expansion_params)
|
||||
const Algorithm::RegionExpansionParameters &expansion_params_into_solid_infill,
|
||||
ExPolygons &sparse,
|
||||
const Algorithm::RegionExpansionParameters &expansion_params_into_sparse_infill,
|
||||
const float closing_radius)
|
||||
{
|
||||
using namespace Slic3r::Algorithm;
|
||||
|
||||
@@ -179,8 +183,23 @@ Surfaces expand_bridges_detect_orientations(
|
||||
return {};
|
||||
|
||||
// Calculate bridge anchors and their expansions in their respective shell region.
|
||||
WaveSeeds bridge_anchors = wave_seeds(bridges_ex, shells, expansion_params.tiny_expansion, true);
|
||||
std::vector<RegionExpansionEx> bridge_expansions = propagate_waves_ex(bridge_anchors, shells, expansion_params);
|
||||
WaveSeeds bridge_anchors = wave_seeds(bridges_ex, shells, expansion_params_into_solid_infill.tiny_expansion, true);
|
||||
std::vector<RegionExpansionEx> bridge_expansions = propagate_waves_ex(bridge_anchors, shells, expansion_params_into_solid_infill);
|
||||
bool expanded_into_shells = ! bridge_expansions.empty();
|
||||
bool expanded_into_sparse = false;
|
||||
{
|
||||
WaveSeeds bridge_anchors_sparse = wave_seeds(bridges_ex, sparse, expansion_params_into_sparse_infill.tiny_expansion, true);
|
||||
std::vector<RegionExpansionEx> bridge_expansions_sparse = propagate_waves_ex(bridge_anchors_sparse, sparse, expansion_params_into_sparse_infill);
|
||||
if (! bridge_expansions_sparse.empty()) {
|
||||
expanded_into_sparse = true;
|
||||
for (WaveSeed &seed : bridge_anchors_sparse)
|
||||
seed.boundary += uint32_t(shells.size());
|
||||
for (RegionExpansionEx &expansion : bridge_expansions_sparse)
|
||||
expansion.boundary_id += uint32_t(shells.size());
|
||||
append(bridge_anchors, std::move(bridge_anchors_sparse));
|
||||
append(bridge_expansions, std::move(bridge_expansions_sparse));
|
||||
}
|
||||
}
|
||||
|
||||
// Cache for detecting bridge orientation and merging regions with overlapping expansions.
|
||||
struct Bridge {
|
||||
@@ -257,7 +276,7 @@ Surfaces expand_bridges_detect_orientations(
|
||||
for (; it_bridge_anchor != bridge_anchors.end() && it_bridge_anchor->src == bridge_id; ++ it_bridge_anchor) {
|
||||
if (last_anchor_id != int(it_bridge_anchor->boundary)) {
|
||||
last_anchor_id = int(it_bridge_anchor->boundary);
|
||||
append(anchor_areas, to_polygons(shells[last_anchor_id]));
|
||||
append(anchor_areas, to_polygons(last_anchor_id < int32_t(shells.size()) ? shells[last_anchor_id] : sparse[last_anchor_id - int32_t(shells.size())]));
|
||||
}
|
||||
// if (Points &polyline = it_bridge_anchor->path; polyline.size() >= 2) {
|
||||
// reserve_more_power_of_2(lines, polyline.size() - 1);
|
||||
@@ -268,17 +287,18 @@ Surfaces expand_bridges_detect_orientations(
|
||||
lines = to_lines(diff_pl(to_polylines(bridge.expolygon), expand(anchor_areas, float(SCALED_EPSILON))));
|
||||
auto [bridging_dir, unsupported_dist] = detect_bridging_direction(lines, to_polygons(bridge.expolygon));
|
||||
bridge.angle = M_PI + std::atan2(bridging_dir.y(), bridging_dir.x());
|
||||
// #if 1
|
||||
// coordf_t stroke_width = scale_(0.06);
|
||||
// BoundingBox bbox = get_extents(initial);
|
||||
// bbox.offset(scale_(1.));
|
||||
// ::Slic3r::SVG
|
||||
// svg(debug_out_path(("bridge"+std::to_string(bridges[idx_last].bridge_angle)+"_"+std::to_string(this->layer()->bottom_z())).c_str()),
|
||||
// bbox);
|
||||
|
||||
// svg.draw(initial, "cyan");
|
||||
// svg.draw(to_lines(lower_layer->lslices), "green", stroke_width);
|
||||
// #endif
|
||||
#if 0
|
||||
coordf_t stroke_width = scale_(0.06);
|
||||
BoundingBox bbox = get_extents(anchor_areas);
|
||||
bbox.merge(get_extents(bridge.expolygon));
|
||||
bbox.offset(scale_(1.));
|
||||
::Slic3r::SVG
|
||||
svg(debug_out_path(("bridge" + std::to_string(bridge.angle) + "_" /* + std::to_string(this->layer()->bottom_z())*/).c_str()),
|
||||
bbox);
|
||||
svg.draw(bridge.expolygon, "cyan");
|
||||
svg.draw(lines, "green", stroke_width);
|
||||
svg.draw(anchor_areas, "red");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -309,14 +329,22 @@ Surfaces expand_bridges_detect_orientations(
|
||||
}
|
||||
//FIXME try to be smart and pick the best bridging angle for all?
|
||||
templ.bridge_angle = bridges[bridge_id].angle;
|
||||
//NOTE: The current regularization of the shells can create small unasigned regions in the object (E.G. benchy)
|
||||
// without the following closing operation, those regions will stay unfilled and cause small holes in the expanded surface.
|
||||
// look for narrow_ensure_vertical_wall_thickness_region_radius filter.
|
||||
ExPolygons final = closing_ex(acc, closing_radius);
|
||||
// without safety offset, artifacts are generated (GH #2494)
|
||||
for (ExPolygon &ex : union_safety_offset_ex(acc))
|
||||
// union_safety_offset_ex(acc)
|
||||
for (ExPolygon &ex : final)
|
||||
out.emplace_back(templ, std::move(ex));
|
||||
}
|
||||
}
|
||||
|
||||
// Clip the shells by the expanded bridges.
|
||||
shells = diff_ex(shells, out);
|
||||
// Clip by the expanded bridges.
|
||||
if (expanded_into_shells)
|
||||
shells = diff_ex(shells, out);
|
||||
if (expanded_into_sparse)
|
||||
sparse = diff_ex(sparse, out);
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -325,18 +353,43 @@ Surfaces expand_bridges_detect_orientations(
|
||||
static Surfaces expand_merge_surfaces(
|
||||
Surfaces &surfaces,
|
||||
SurfaceType surface_type,
|
||||
ExPolygons &shells,
|
||||
const Algorithm::RegionExpansionParameters ¶ms,
|
||||
ExPolygons &shells,
|
||||
const Algorithm::RegionExpansionParameters &expansion_params_into_solid_infill,
|
||||
ExPolygons &sparse,
|
||||
const Algorithm::RegionExpansionParameters &expansion_params_into_sparse_infill,
|
||||
const float closing_radius,
|
||||
const double bridge_angle = -1.)
|
||||
{
|
||||
using namespace Slic3r::Algorithm;
|
||||
|
||||
double thickness;
|
||||
ExPolygons src = fill_surfaces_extract_expolygons(surfaces, {surface_type}, thickness);
|
||||
if (src.empty())
|
||||
return {};
|
||||
|
||||
std::vector<ExPolygon> expanded = expand_merge_expolygons(std::move(src), shells, params);
|
||||
std::vector<RegionExpansion> expansions = propagate_waves(src, shells, expansion_params_into_solid_infill);
|
||||
bool expanded_into_shells = !expansions.empty();
|
||||
bool expanded_into_sparse = false;
|
||||
{
|
||||
std::vector<RegionExpansion> expansions2 = propagate_waves(src, sparse, expansion_params_into_sparse_infill);
|
||||
if (! expansions2.empty()) {
|
||||
expanded_into_sparse = true;
|
||||
for (RegionExpansion &expansion : expansions2)
|
||||
expansion.boundary_id += uint32_t(shells.size());
|
||||
append(expansions, std::move(expansions2));
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<ExPolygon> expanded = merge_expansions_into_expolygons(std::move(src), std::move(expansions));
|
||||
//NOTE: The current regularization of the shells can create small unasigned regions in the object (E.G. benchy)
|
||||
// without the following closing operation, those regions will stay unfilled and cause small holes in the expanded surface.
|
||||
// look for narrow_ensure_vertical_wall_thickness_region_radius filter.
|
||||
expanded = closing_ex(expanded, closing_radius);
|
||||
// Trim the shells by the expanded expolygons.
|
||||
shells = diff_ex(shells, expanded);
|
||||
if (expanded_into_shells)
|
||||
shells = diff_ex(shells, expanded);
|
||||
if (expanded_into_sparse)
|
||||
sparse = diff_ex(sparse, expanded);
|
||||
|
||||
Surface templ{ surface_type, {} };
|
||||
templ.bridge_angle = bridge_angle;
|
||||
@@ -349,20 +402,25 @@ static Surfaces expand_merge_surfaces(
|
||||
|
||||
void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Polygons *lower_layer_covered)
|
||||
{
|
||||
using namespace Slic3r::Algorithm;
|
||||
|
||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||
export_region_fill_surfaces_to_svg_debug("4_process_external_surfaces-initial");
|
||||
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
|
||||
|
||||
// Width of the perimeters.
|
||||
float shell_width = 0;
|
||||
float expansion_min = 0;
|
||||
if (int num_perimeters = this->region().config().perimeters; num_perimeters > 0) {
|
||||
Flow external_perimeter_flow = this->flow(frExternalPerimeter);
|
||||
Flow perimeter_flow = this->flow(frPerimeter);
|
||||
shell_width += 0.5f * external_perimeter_flow.scaled_width() + external_perimeter_flow.scaled_spacing();
|
||||
shell_width = 0.5f * external_perimeter_flow.scaled_width() + external_perimeter_flow.scaled_spacing();
|
||||
shell_width += perimeter_flow.scaled_spacing() * (num_perimeters - 1);
|
||||
expansion_min = perimeter_flow.scaled_spacing();
|
||||
} else {
|
||||
// TODO: Maybe there is better solution when printing with zero perimeters, but this works reasonably well, given the situation
|
||||
shell_width = float(SCALED_EPSILON);
|
||||
shell_width = float(SCALED_EPSILON);
|
||||
expansion_min = float(SCALED_EPSILON);;
|
||||
}
|
||||
|
||||
// Scaled expansions of the respective external surfaces.
|
||||
@@ -373,19 +431,23 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
|
||||
static constexpr const float expansion_step = scaled<float>(0.1);
|
||||
// Don't take more than max_nr_steps for small expansion_step.
|
||||
static constexpr const size_t max_nr_expansion_steps = 5;
|
||||
// Radius (with added epsilon) to absorb empty regions emering from regularization of ensuring, viz const float narrow_ensure_vertical_wall_thickness_region_radius = 0.5f * 0.65f * min_perimeter_infill_spacing;
|
||||
const float closing_radius = 0.55f * 0.65f * 1.05f * this->flow(frSolidInfill).scaled_spacing();
|
||||
|
||||
// Expand the top / bottom / bridge surfaces into the shell thickness solid infills.
|
||||
double layer_thickness;
|
||||
ExPolygons shells = union_ex(fill_surfaces_extract_expolygons(m_fill_surfaces.surfaces, {stInternalSolid}, layer_thickness));
|
||||
ExPolygons shells = union_ex(fill_surfaces_extract_expolygons(m_fill_surfaces.surfaces, { stInternalSolid }, layer_thickness));
|
||||
ExPolygons sparse = union_ex(fill_surfaces_extract_expolygons(m_fill_surfaces.surfaces, { stInternal }, layer_thickness));
|
||||
|
||||
SurfaceCollection bridges;
|
||||
const auto expansion_params_into_sparse_infill = RegionExpansionParameters::build(expansion_min, expansion_step, max_nr_expansion_steps);
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace) << "Processing external surface, detecting bridges. layer" << this->layer()->print_z;
|
||||
const double custom_angle = this->region().config().bridge_angle.value;
|
||||
const auto params = Algorithm::RegionExpansionParameters::build(expansion_bottom_bridge, expansion_step, max_nr_expansion_steps);
|
||||
const auto expansion_params_into_solid_infill = RegionExpansionParameters::build(expansion_bottom_bridge, expansion_step, max_nr_expansion_steps);
|
||||
bridges.surfaces = custom_angle > 0 ?
|
||||
expand_merge_surfaces(m_fill_surfaces.surfaces, stBottomBridge, shells, params, Geometry::deg2rad(custom_angle)) :
|
||||
expand_bridges_detect_orientations(m_fill_surfaces.surfaces, shells, params);
|
||||
expand_merge_surfaces(m_fill_surfaces.surfaces, stBottomBridge, shells, expansion_params_into_solid_infill, sparse, expansion_params_into_sparse_infill, closing_radius, Geometry::deg2rad(custom_angle)) :
|
||||
expand_bridges_detect_orientations(m_fill_surfaces.surfaces, shells, expansion_params_into_solid_infill, sparse, expansion_params_into_sparse_infill, closing_radius);
|
||||
BOOST_LOG_TRIVIAL(trace) << "Processing external surface, detecting bridges - done";
|
||||
#if 0
|
||||
{
|
||||
@@ -396,15 +458,25 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
|
||||
}
|
||||
|
||||
Surfaces bottoms = expand_merge_surfaces(m_fill_surfaces.surfaces, stBottom, shells,
|
||||
Algorithm::RegionExpansionParameters::build(expansion_bottom, expansion_step, max_nr_expansion_steps));
|
||||
RegionExpansionParameters::build(expansion_bottom, expansion_step, max_nr_expansion_steps),
|
||||
sparse, expansion_params_into_sparse_infill, closing_radius);
|
||||
Surfaces tops = expand_merge_surfaces(m_fill_surfaces.surfaces, stTop, shells,
|
||||
Algorithm::RegionExpansionParameters::build(expansion_top, expansion_step, max_nr_expansion_steps));
|
||||
RegionExpansionParameters::build(expansion_top, expansion_step, max_nr_expansion_steps),
|
||||
sparse, expansion_params_into_sparse_infill, closing_radius);
|
||||
|
||||
m_fill_surfaces.remove_types({ stBottomBridge, stBottom, stTop, stInternalSolid });
|
||||
reserve_more(m_fill_surfaces.surfaces, shells.size() + bridges.size() + bottoms.size() + tops.size());
|
||||
Surface solid_templ(stInternalSolid, {});
|
||||
solid_templ.thickness = layer_thickness;
|
||||
m_fill_surfaces.append(std::move(shells), solid_templ);
|
||||
// m_fill_surfaces.remove_types({ stBottomBridge, stBottom, stTop, stInternal, stInternalSolid });
|
||||
m_fill_surfaces.clear();
|
||||
reserve_more(m_fill_surfaces.surfaces, shells.size() + sparse.size() + bridges.size() + bottoms.size() + tops.size());
|
||||
{
|
||||
Surface solid_templ(stInternalSolid, {});
|
||||
solid_templ.thickness = layer_thickness;
|
||||
m_fill_surfaces.append(std::move(shells), solid_templ);
|
||||
}
|
||||
{
|
||||
Surface sparse_templ(stInternal, {});
|
||||
sparse_templ.thickness = layer_thickness;
|
||||
m_fill_surfaces.append(std::move(sparse), sparse_templ);
|
||||
}
|
||||
m_fill_surfaces.append(std::move(bridges.surfaces));
|
||||
m_fill_surfaces.append(std::move(bottoms));
|
||||
m_fill_surfaces.append(std::move(tops));
|
||||
|
||||
@@ -1353,37 +1353,6 @@ void ModelObject::delete_connectors()
|
||||
}
|
||||
}
|
||||
|
||||
void ModelObject::synchronize_model_after_cut()
|
||||
{
|
||||
for (ModelObject* obj : m_model->objects) {
|
||||
if (obj == this || obj->cut_id.is_equal(this->cut_id))
|
||||
continue;
|
||||
if (obj->is_cut() && obj->cut_id.has_same_id(this->cut_id))
|
||||
obj->cut_id.copy(this->cut_id);
|
||||
}
|
||||
this->invalidate_cut();
|
||||
}
|
||||
|
||||
void ModelObject::apply_cut_attributes(ModelObjectCutAttributes attributes)
|
||||
{
|
||||
// we don't save cut information, if result will not contains all parts of initial object
|
||||
if (!attributes.has(ModelObjectCutAttribute::KeepUpper) ||
|
||||
!attributes.has(ModelObjectCutAttribute::KeepLower) ||
|
||||
attributes.has(ModelObjectCutAttribute::InvalidateCutInfo))
|
||||
return;
|
||||
|
||||
if (cut_id.id().invalid())
|
||||
cut_id.init();
|
||||
{
|
||||
int cut_obj_cnt = -1;
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepUpper)) cut_obj_cnt++;
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepLower)) cut_obj_cnt++;
|
||||
if (attributes.has(ModelObjectCutAttribute::CreateDowels)) cut_obj_cnt++;
|
||||
if (cut_obj_cnt > 0)
|
||||
cut_id.increase_check_sum(size_t(cut_obj_cnt));
|
||||
}
|
||||
}
|
||||
|
||||
void ModelObject::clone_for_cut(ModelObject** obj)
|
||||
{
|
||||
(*obj) = ModelObject::new_clone(*this);
|
||||
@@ -1636,9 +1605,6 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Transform3d& cut_matrix,
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - start";
|
||||
|
||||
// apply cut attributes for object
|
||||
apply_cut_attributes(attributes);
|
||||
|
||||
// Clone the object to duplicate instances, materials etc.
|
||||
ModelObject* upper{ nullptr };
|
||||
if (attributes.has(ModelObjectCutAttribute::KeepUpper))
|
||||
@@ -1709,8 +1675,6 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Transform3d& cut_matrix,
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "ModelObject::cut - end";
|
||||
|
||||
synchronize_model_after_cut();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
@@ -469,13 +469,7 @@ public:
|
||||
void delete_connectors();
|
||||
void clone_for_cut(ModelObject **obj);
|
||||
|
||||
void apply_cut_attributes(ModelObjectCutAttributes attributes);
|
||||
private:
|
||||
// FIXME: These functions would best not be here at all. It might make sense to separate the
|
||||
// cut-related methods elsewhere. Same holds for cut_connectors data member, which is currently
|
||||
// just a temporary variable used by cut gizmo only.
|
||||
void synchronize_model_after_cut();
|
||||
|
||||
void process_connector_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix,
|
||||
ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower,
|
||||
std::vector<ModelObject*>& dowels);
|
||||
@@ -515,6 +509,8 @@ public:
|
||||
bool has_solid_mesh() const;
|
||||
// Detect if object has at least one negative volume mash
|
||||
bool has_negative_volume_mesh() const;
|
||||
// Detect if object has at least one sla drain hole
|
||||
bool has_sla_drain_holes() const { return !sla_drain_holes.empty(); }
|
||||
bool is_cut() const { return cut_id.id().valid(); }
|
||||
bool has_connectors() const;
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <istream>
|
||||
#include <cstdint>
|
||||
|
||||
namespace Slic3r { namespace png {
|
||||
|
||||
|
||||
@@ -1090,6 +1090,7 @@ namespace client
|
||||
|
||||
static void scalar_variable_assign_scalar(const MyContext *ctx, OptWithPos &lhs, const expr &rhs)
|
||||
{
|
||||
assert(! ctx->skipping());
|
||||
assert(lhs.opt->is_scalar());
|
||||
check_writable(ctx, lhs);
|
||||
ConfigOption *wropt = const_cast<ConfigOption*>(lhs.opt);
|
||||
@@ -1121,6 +1122,7 @@ namespace client
|
||||
|
||||
static void vector_variable_element_assign_scalar(const MyContext *ctx, OptWithPos &lhs, const expr &rhs)
|
||||
{
|
||||
assert(! ctx->skipping());
|
||||
assert(lhs.opt->is_vector());
|
||||
check_writable(ctx, lhs);
|
||||
if (! lhs.has_index())
|
||||
@@ -1158,6 +1160,7 @@ namespace client
|
||||
|
||||
static void vector_variable_assign_expr_with_count(const MyContext *ctx, OptWithPos &lhs, const expr &rhs_count, const expr &rhs_value)
|
||||
{
|
||||
assert(! ctx->skipping());
|
||||
size_t count = evaluate_count(rhs_count);
|
||||
auto *opt = const_cast<ConfigOption*>(lhs.opt);
|
||||
switch (lhs.opt->type()) {
|
||||
|
||||
@@ -2282,7 +2282,7 @@ namespace PresetUtils {
|
||||
{
|
||||
const VendorProfile::PrinterModel *out = nullptr;
|
||||
if (preset.vendor != nullptr) {
|
||||
auto *printer_model = preset.config.opt<ConfigOptionString>("printer_model");
|
||||
const auto *printer_model = preset.config.opt<ConfigOptionString>("printer_model");
|
||||
if (printer_model != nullptr && ! printer_model->value.empty()) {
|
||||
auto it = std::find_if(preset.vendor->models.begin(), preset.vendor->models.end(), [printer_model](const VendorProfile::PrinterModel &pm) { return pm.id == printer_model->value; });
|
||||
if (it != preset.vendor->models.end())
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "Thread.hpp"
|
||||
#include "GCode.hpp"
|
||||
#include "GCode/WipeTower.hpp"
|
||||
#include "GCode/ConflictChecker.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "BuildVolume.hpp"
|
||||
#include "format.hpp"
|
||||
@@ -26,7 +27,6 @@
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/regex.hpp>
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
template class PrintState<PrintStep, psCount>;
|
||||
@@ -964,6 +964,18 @@ void Print::process()
|
||||
this->finalize_first_layer_convex_hull();
|
||||
this->set_done(psSkirtBrim);
|
||||
}
|
||||
|
||||
std::optional<const FakeWipeTower*> wipe_tower_opt = {};
|
||||
if (this->has_wipe_tower()) {
|
||||
m_fake_wipe_tower.set_pos_and_rotation({ m_config.wipe_tower_x, m_config.wipe_tower_y }, m_config.wipe_tower_rotation_angle);
|
||||
wipe_tower_opt = std::make_optional<const FakeWipeTower*>(&m_fake_wipe_tower);
|
||||
}
|
||||
auto conflictRes = ConflictChecker::find_inter_of_lines_in_diff_objs(m_objects, wipe_tower_opt);
|
||||
|
||||
m_conflict_result = conflictRes;
|
||||
if (conflictRes.has_value())
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("gcode path conflicts found between %1% and %2%") % conflictRes->_objName1 % conflictRes->_objName2;
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Slicing process finished." << log_memory_info();
|
||||
}
|
||||
|
||||
@@ -989,6 +1001,10 @@ std::string Print::export_gcode(const std::string& path_template, GCodeProcessor
|
||||
// Create GCode on heap, it has quite a lot of data.
|
||||
std::unique_ptr<GCode> gcode(new GCode);
|
||||
gcode->do_export(this, path.c_str(), result, thumbnail_cb);
|
||||
|
||||
if (m_conflict_result.has_value())
|
||||
result->conflict_result = *m_conflict_result;
|
||||
|
||||
return path.c_str();
|
||||
}
|
||||
|
||||
@@ -1487,6 +1503,7 @@ void Print::_make_wipe_tower()
|
||||
m_wipe_tower_data.tool_changes.reserve(m_wipe_tower_data.tool_ordering.layer_tools().size());
|
||||
wipe_tower.generate(m_wipe_tower_data.tool_changes);
|
||||
m_wipe_tower_data.depth = wipe_tower.get_depth();
|
||||
m_wipe_tower_data.z_and_depth_pairs = wipe_tower.get_z_and_depth_pairs();
|
||||
m_wipe_tower_data.brim_width = wipe_tower.get_brim_width();
|
||||
m_wipe_tower_data.height = wipe_tower.get_wipe_tower_height();
|
||||
|
||||
@@ -1511,6 +1528,10 @@ void Print::_make_wipe_tower()
|
||||
|
||||
m_wipe_tower_data.used_filament = wipe_tower.get_used_filament();
|
||||
m_wipe_tower_data.number_of_toolchanges = wipe_tower.get_number_of_toolchanges();
|
||||
const Vec3d origin = Vec3d::Zero();
|
||||
m_fake_wipe_tower.set_fake_extrusion_data(wipe_tower.position(), wipe_tower.width(), wipe_tower.get_wipe_tower_height(), config().first_layer_height, m_wipe_tower_data.depth,
|
||||
m_wipe_tower_data.z_and_depth_pairs, m_wipe_tower_data.brim_width, config().wipe_tower_rotation_angle, config().wipe_tower_cone_angle, {scale_(origin.x()), scale_(origin.y())});
|
||||
|
||||
}
|
||||
|
||||
// Generate a recommended G-code output file name based on the format template, default extension, and template parameters
|
||||
@@ -1522,6 +1543,7 @@ std::string Print::output_filename(const std::string &filename_base) const
|
||||
// These values will be just propagated into the output file name.
|
||||
DynamicConfig config = this->finished() ? this->print_statistics().config() : this->print_statistics().placeholders();
|
||||
config.set_key_value("num_extruders", new ConfigOptionInt((int)m_config.nozzle_diameter.size()));
|
||||
config.set_key_value("default_output_extension", new ConfigOptionString(".gcode"));
|
||||
return this->PrintBase::output_filename(m_config.output_filename_format.value, ".gcode", filename_base, &config);
|
||||
}
|
||||
|
||||
@@ -1578,4 +1600,80 @@ std::string PrintStatistics::finalize_output_path(const std::string &path_in) co
|
||||
return final_path;
|
||||
}
|
||||
|
||||
std::vector<ExtrusionPaths> FakeWipeTower::getFakeExtrusionPathsFromWipeTower() const
|
||||
{
|
||||
float h = height;
|
||||
float lh = layer_height;
|
||||
int d = scale_(depth);
|
||||
int w = scale_(width);
|
||||
int bd = scale_(brim_width);
|
||||
Point minCorner = { -bd, -bd };
|
||||
Point maxCorner = { minCorner.x() + w + bd, minCorner.y() + d + bd };
|
||||
|
||||
const auto [cone_base_R, cone_scale_x] = WipeTower::get_wipe_tower_cone_base(width, height, depth, cone_angle);
|
||||
|
||||
std::vector<ExtrusionPaths> paths;
|
||||
for (float hh = 0.f; hh < h; hh += lh) {
|
||||
|
||||
if (hh != 0.f) {
|
||||
// The wipe tower may be getting smaller. Find the depth for this layer.
|
||||
size_t i = 0;
|
||||
for (i=0; i<z_and_depth_pairs.size()-1; ++i)
|
||||
if (hh >= z_and_depth_pairs[i].first && hh < z_and_depth_pairs[i+1].first)
|
||||
break;
|
||||
d = scale_(z_and_depth_pairs[i].second);
|
||||
minCorner = {0.f, -d/2 + scale_(z_and_depth_pairs.front().second/2.f)};
|
||||
maxCorner = { minCorner.x() + w, minCorner.y() + d };
|
||||
}
|
||||
|
||||
|
||||
ExtrusionPath path(ExtrusionRole::WipeTower, 0.0, 0.0, lh);
|
||||
path.polyline = { minCorner, {maxCorner.x(), minCorner.y()}, maxCorner, {minCorner.x(), maxCorner.y()}, minCorner };
|
||||
paths.push_back({ path });
|
||||
|
||||
// We added the border, now add several parallel lines so we can detect an object that is fully inside the tower.
|
||||
// For now, simply use fixed spacing of 3mm.
|
||||
for (coord_t y=minCorner.y()+scale_(3.); y<maxCorner.y(); y+=scale_(3.)) {
|
||||
path.polyline = { {minCorner.x(), y}, {maxCorner.x(), y} };
|
||||
paths.back().emplace_back(path);
|
||||
}
|
||||
|
||||
// And of course the stabilization cone and its base...
|
||||
if (cone_base_R > 0.) {
|
||||
path.polyline.clear();
|
||||
double r = cone_base_R * (1 - hh/height);
|
||||
for (double alpha=0; alpha<2.01*M_PI; alpha+=2*M_PI/20.)
|
||||
path.polyline.points.emplace_back(Point::new_scale(width/2. + r * std::cos(alpha)/cone_scale_x, depth/2. + r * std::sin(alpha)));
|
||||
paths.back().emplace_back(path);
|
||||
if (hh == 0.f) { // Cone brim.
|
||||
for (float bw=brim_width; bw>0.f; bw-=3.f) {
|
||||
path.polyline.clear();
|
||||
for (double alpha=0; alpha<2.01*M_PI; alpha+=2*M_PI/20.) // see load_wipe_tower_preview, where the same is a bit clearer
|
||||
path.polyline.points.emplace_back(Point::new_scale(
|
||||
width/2. + cone_base_R * std::cos(alpha)/cone_scale_x * (1. + cone_scale_x*bw/cone_base_R),
|
||||
depth/2. + cone_base_R * std::sin(alpha) * (1. + bw/cone_base_R))
|
||||
);
|
||||
paths.back().emplace_back(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only the first layer has brim.
|
||||
if (hh == 0.f) {
|
||||
minCorner = minCorner + Point(bd, bd);
|
||||
maxCorner = maxCorner - Point(bd, bd);
|
||||
}
|
||||
}
|
||||
|
||||
// Rotate and translate the tower into the final position.
|
||||
for (ExtrusionPaths& ps : paths) {
|
||||
for (ExtrusionPath& p : ps) {
|
||||
p.polyline.rotate(Geometry::deg2rad(rotation_angle));
|
||||
p.polyline.translate(scale_(pos.x()), scale_(pos.y()));
|
||||
}
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
@@ -418,6 +418,39 @@ private:
|
||||
FillLightning::GeneratorPtr m_lightning_generator;
|
||||
};
|
||||
|
||||
struct FakeWipeTower
|
||||
{
|
||||
// generate fake extrusion
|
||||
Vec2f pos;
|
||||
float width;
|
||||
float height;
|
||||
float layer_height;
|
||||
float depth;
|
||||
std::vector<std::pair<float, float>> z_and_depth_pairs;
|
||||
float brim_width;
|
||||
float rotation_angle;
|
||||
float cone_angle;
|
||||
Vec2d plate_origin;
|
||||
|
||||
void set_fake_extrusion_data(const Vec2f& p, float w, float h, float lh, float d, const std::vector<std::pair<float, float>>& zad, float bd, float ra, float ca, const Vec2d& o)
|
||||
{
|
||||
pos = p;
|
||||
width = w;
|
||||
height = h;
|
||||
layer_height = lh;
|
||||
depth = d;
|
||||
z_and_depth_pairs = zad;
|
||||
brim_width = bd;
|
||||
rotation_angle = ra;
|
||||
cone_angle = ca;
|
||||
plate_origin = o;
|
||||
}
|
||||
|
||||
void set_pos_and_rotation(const Vec2f& p, float rotation) { pos = p; rotation_angle = rotation; }
|
||||
|
||||
std::vector<ExtrusionPaths> getFakeExtrusionPathsFromWipeTower() const;
|
||||
};
|
||||
|
||||
struct WipeTowerData
|
||||
{
|
||||
// Following section will be consumed by the GCodeGenerator.
|
||||
@@ -433,6 +466,7 @@ struct WipeTowerData
|
||||
|
||||
// Depth of the wipe tower to pass to GLCanvas3D for exact bounding box:
|
||||
float depth;
|
||||
std::vector<std::pair<float, float>> z_and_depth_pairs;
|
||||
float brim_width;
|
||||
float height;
|
||||
|
||||
@@ -668,6 +702,9 @@ private:
|
||||
friend class GCodeProcessor;
|
||||
// Allow PrintObject to access m_mutex and m_cancel_callback.
|
||||
friend class PrintObject;
|
||||
|
||||
ConflictResultOpt m_conflict_result;
|
||||
FakeWipeTower m_fake_wipe_tower;
|
||||
};
|
||||
|
||||
} /* slic3r_Print_hpp_ */
|
||||
|
||||
@@ -68,7 +68,7 @@ static const t_config_enum_values s_keys_map_PrintHostType {
|
||||
{ "qidilink", htQIDILink },
|
||||
{ "qidiconnect", htQIDIConnect },
|
||||
{ "octoprint", htOctoPrint },
|
||||
{ "mainsail", htMainSail },
|
||||
{ "moonraker", htMoonraker },
|
||||
{ "duet", htDuet },
|
||||
{ "flashair", htFlashAir },
|
||||
{ "astrobox", htAstroBox },
|
||||
@@ -736,7 +736,6 @@ void PrintConfigDef::init_fff_params()
|
||||
"and fan speed according to layer printing time.");
|
||||
def->set_default_value(new ConfigOptionBools { true });
|
||||
|
||||
|
||||
def = this->add("cooling_tube_retraction", coFloat);
|
||||
def->label = L("Cooling tube position");
|
||||
def->tooltip = L("Distance of the center-point of the cooling tube from the extruder tip.");
|
||||
@@ -1535,8 +1534,6 @@ void PrintConfigDef::init_fff_params()
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionBool(true));
|
||||
|
||||
|
||||
|
||||
def = this->add("gap_fill_speed", coFloat);
|
||||
def->label = L("Gap fill");
|
||||
def->category = L("Speed");
|
||||
@@ -2134,7 +2131,7 @@ void PrintConfigDef::init_fff_params()
|
||||
{ "qidilink", "QIDILink" },
|
||||
{ "qidiconnect", "QIDIConnect" },
|
||||
{ "octoprint", "OctoPrint" },
|
||||
{ "mainsail", "Mainsail/Fluidd" },
|
||||
{ "moonraker", "Klipper (via Moonraker)" },
|
||||
{ "duet", "Duet" },
|
||||
{ "flashair", "FlashAir" },
|
||||
{ "astrobox", "AstroBox" },
|
||||
@@ -2164,7 +2161,7 @@ void PrintConfigDef::init_fff_params()
|
||||
def->tooltip = L("You can use all configuration options as variables inside this template. "
|
||||
"For example: [layer_height], [fill_density] etc. You can also use [timestamp], "
|
||||
"[year], [month], [day], [hour], [minute], [second], [version], [input_filename], "
|
||||
"[input_filename_base].");
|
||||
"[input_filename_base], [default_output_extension].");
|
||||
def->full_width = true;
|
||||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionString("[input_filename_base].gcode"));
|
||||
@@ -4420,6 +4417,9 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
|
||||
else if (value == "marlinfirmware")
|
||||
// the "new" marlin firmware flavor used to be called "marlinfirmware" for some time during QIDISlicer 2.4.0-alpha development.
|
||||
value = "marlin2";
|
||||
} else if (opt_key == "host_type" && value == "mainsail") {
|
||||
// the "mainsail" key (introduced in 2.6.0-alpha6) was renamed to "moonraker" (in 2.6.0-rc1).
|
||||
value = "moonraker";
|
||||
} else if (opt_key == "fill_density" && value.find("%") == std::string::npos) {
|
||||
try {
|
||||
// fill_density was turned into a percent value
|
||||
@@ -5185,21 +5185,21 @@ std::string get_sla_suptree_prefix(const DynamicPrintConfig &config)
|
||||
return slatree;
|
||||
}
|
||||
|
||||
static bool is_XL_printer(const std::string& printer_model)
|
||||
static bool is_XL_printer(const std::string& printer_notes)
|
||||
{
|
||||
static constexpr const char *ALIGN_ONLY_FOR = "XL";
|
||||
return boost::algorithm::contains(printer_model, ALIGN_ONLY_FOR);
|
||||
return boost::algorithm::contains(printer_notes, "PRINTER_VENDOR_PRUSA3D")
|
||||
&& boost::algorithm::contains(printer_notes, "PRINTER_MODEL_XL");
|
||||
}
|
||||
|
||||
bool is_XL_printer(const DynamicPrintConfig &cfg)
|
||||
{
|
||||
auto *printer_model = cfg.opt<ConfigOptionString>("printer_model");
|
||||
return printer_model && is_XL_printer(printer_model->value);
|
||||
auto *printer_notes = cfg.opt<ConfigOptionString>("printer_notes");
|
||||
return printer_notes && is_XL_printer(printer_notes->value);
|
||||
}
|
||||
|
||||
bool is_XL_printer(const PrintConfig &cfg)
|
||||
{
|
||||
return is_XL_printer(cfg.printer_model.value);
|
||||
return is_XL_printer(cfg.printer_notes.value);
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
@@ -44,7 +44,7 @@ enum class MachineLimitsUsage {
|
||||
};
|
||||
|
||||
enum PrintHostType {
|
||||
htQIDILink, htQIDIConnect, htOctoPrint, htMainSail, htDuet, htFlashAir, htAstroBox, htRepetier, htMKS
|
||||
htPrusaLink, htPrusaConnect, htOctoPrint, htMoonraker, htDuet, htFlashAir, htAstroBox, htRepetier, htMKS
|
||||
};
|
||||
|
||||
enum AuthorizationType {
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include "CSGMesh/PerformCSGMeshBooleans.hpp"
|
||||
#include "format.hpp"
|
||||
|
||||
#include "Format/SLAArchiveFormatRegistry.hpp"
|
||||
|
||||
#include "Geometry.hpp"
|
||||
#include "Thread.hpp"
|
||||
|
||||
@@ -522,6 +524,7 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, DynamicPrintConfig con
|
||||
#endif /* _DEBUG */
|
||||
|
||||
m_full_print_config = std::move(config);
|
||||
|
||||
return static_cast<ApplyStatus>(apply_status);
|
||||
}
|
||||
|
||||
@@ -531,7 +534,16 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, DynamicPrintConfig con
|
||||
std::string SLAPrint::output_filename(const std::string &filename_base) const
|
||||
{
|
||||
DynamicConfig config = this->finished() ? this->print_statistics().config() : this->print_statistics().placeholders();
|
||||
return this->PrintBase::output_filename(m_print_config.output_filename_format.value, ".sl1", filename_base, &config);
|
||||
std::string default_ext = get_default_extension(m_printer_config.sla_archive_format.value.c_str());
|
||||
if (default_ext.empty())
|
||||
default_ext = "sl1";
|
||||
|
||||
default_ext.insert(default_ext.begin(), '.');
|
||||
|
||||
config.set_key_value("default_output_extension",
|
||||
new ConfigOptionString(default_ext));
|
||||
|
||||
return this->PrintBase::output_filename(m_print_config.output_filename_format.value, default_ext, filename_base, &config);
|
||||
}
|
||||
|
||||
std::string SLAPrint::validate(std::vector<std::string>*) const
|
||||
|
||||
@@ -650,7 +650,7 @@ std::tuple<ObjectPart, float> build_object_part_from_slice(const size_t &slice_i
|
||||
new_object_part.volume += volume;
|
||||
new_object_part.volume_centroid_accumulator += to_3d(Vec2f((line.a + line.b) / 2.0f), slice_z) * volume;
|
||||
|
||||
if (l->id() == params.raft_layers_count) { // layer attached on bed/raft
|
||||
if (int(l->id()) == params.raft_layers_count) { // layer attached on bed/raft
|
||||
new_object_part.connected_to_bed = true;
|
||||
float sticking_area = line.len * flow_width;
|
||||
new_object_part.sticking_area += sticking_area;
|
||||
@@ -673,7 +673,7 @@ std::tuple<ObjectPart, float> build_object_part_from_slice(const size_t &slice_i
|
||||
|
||||
for (const auto &island : slice.islands) {
|
||||
const LayerRegion *perimeter_region = layer->get_region(island.perimeters.region());
|
||||
for (const auto &perimeter_idx : island.perimeters) {
|
||||
for (size_t perimeter_idx : island.perimeters) {
|
||||
for (const ExtrusionEntity *perimeter :
|
||||
static_cast<const ExtrusionEntityCollection *>(perimeter_region->perimeters().entities[perimeter_idx])->entities) {
|
||||
add_extrusions_to_object(perimeter, perimeter_region);
|
||||
@@ -681,20 +681,20 @@ std::tuple<ObjectPart, float> build_object_part_from_slice(const size_t &slice_i
|
||||
}
|
||||
for (const LayerExtrusionRange &fill_range : island.fills) {
|
||||
const LayerRegion *fill_region = layer->get_region(fill_range.region());
|
||||
for (const auto &fill_idx : fill_range) {
|
||||
for (size_t fill_idx : fill_range) {
|
||||
for (const ExtrusionEntity *fill :
|
||||
static_cast<const ExtrusionEntityCollection *>(fill_region->fills().entities[fill_idx])->entities) {
|
||||
add_extrusions_to_object(fill, fill_region);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const auto &thin_fill_idx : island.thin_fills) {
|
||||
for (size_t thin_fill_idx : island.thin_fills) {
|
||||
add_extrusions_to_object(perimeter_region->thin_fills().entities[thin_fill_idx], perimeter_region);
|
||||
}
|
||||
}
|
||||
|
||||
// BRIM HANDLING
|
||||
if (layer->id() == params.raft_layers_count && params.raft_layers_count == 0 && params.brim_type != BrimType::btNoBrim &&
|
||||
if (int(layer->id()) == params.raft_layers_count && params.raft_layers_count == 0 && params.brim_type != BrimType::btNoBrim &&
|
||||
params.brim_width > 0.0) {
|
||||
// TODO: The algorithm here should take into account that multiple slices may have coliding Brim areas and the final brim area is
|
||||
// smaller,
|
||||
@@ -911,7 +911,7 @@ std::tuple<SupportPoints, PartialObjects> check_stability(const PrintObject
|
||||
for (const auto &island : slice.islands) {
|
||||
for (const LayerExtrusionRange &fill_range : island.fills) {
|
||||
const LayerRegion *fill_region = layer->get_region(fill_range.region());
|
||||
for (const auto &fill_idx : fill_range) {
|
||||
for (size_t fill_idx : fill_range) {
|
||||
for (const ExtrusionEntity *e : get_flat_entities(fill_region->fills().entities[fill_idx])) {
|
||||
if (e->role() == ExtrusionRole::BridgeInfill) {
|
||||
entities_to_check.push_back({e, fill_region, slice_idx});
|
||||
@@ -921,7 +921,7 @@ std::tuple<SupportPoints, PartialObjects> check_stability(const PrintObject
|
||||
}
|
||||
|
||||
const LayerRegion *perimeter_region = layer->get_region(island.perimeters.region());
|
||||
for (const size_t &perimeter_idx : island.perimeters) {
|
||||
for (size_t perimeter_idx : island.perimeters) {
|
||||
for (const ExtrusionEntity *e : get_flat_entities(perimeter_region->perimeters().entities[perimeter_idx])) {
|
||||
entities_to_check.push_back({e, perimeter_region, slice_idx});
|
||||
}
|
||||
|
||||
@@ -211,6 +211,12 @@ bool is_main_thread_active()
|
||||
return get_main_thread_id() == boost::this_thread::get_id();
|
||||
}
|
||||
|
||||
static thread_local ThreadData s_thread_data;
|
||||
ThreadData& thread_data()
|
||||
{
|
||||
return s_thread_data;
|
||||
}
|
||||
|
||||
// Spawn (n - 1) worker threads on Intel TBB thread pool and name them by an index and a system thread ID.
|
||||
// Also it sets locale of the worker threads to "C" for the G-code generator to produce "." as a decimal separator.
|
||||
void name_tbb_thread_pool_threads_set_locale()
|
||||
@@ -274,16 +280,16 @@ void set_current_thread_qos()
|
||||
#endif // __APPLE__
|
||||
}
|
||||
|
||||
void TBBLocalesSetter::on_scheduler_entry(bool is_worker)
|
||||
void ThreadData::tbb_worker_thread_set_c_locales()
|
||||
{
|
||||
// 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) {
|
||||
if (! m_tbb_worker_thread_c_locales_set) {
|
||||
// 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;
|
||||
m_tbb_worker_thread_c_locales_set = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <utility>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <random>
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
#include <tbb/task_scheduler_observer.h>
|
||||
@@ -67,6 +68,27 @@ template<class Fn> inline boost::thread create_thread(Fn &&fn)
|
||||
return create_thread(attrs, std::forward<Fn>(fn));
|
||||
}
|
||||
|
||||
class ThreadData {
|
||||
public:
|
||||
std::mt19937& random_generator() {
|
||||
if (! m_random_generator_initialized) {
|
||||
std::random_device rd;
|
||||
m_random_generator.seed(rd());
|
||||
m_random_generator_initialized = true;
|
||||
}
|
||||
return m_random_generator;
|
||||
}
|
||||
|
||||
void tbb_worker_thread_set_c_locales();
|
||||
|
||||
private:
|
||||
std::mt19937 m_random_generator;
|
||||
bool m_random_generator_initialized { false };
|
||||
bool m_tbb_worker_thread_c_locales_set { false };
|
||||
};
|
||||
|
||||
ThreadData& thread_data();
|
||||
|
||||
// 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
|
||||
@@ -79,11 +101,7 @@ 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 };
|
||||
void on_scheduler_entry(bool /* is_worker */) override { thread_data().tbb_worker_thread_set_c_locales(); }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -903,7 +903,7 @@ Polygon its_convex_hull_2d_above(const indexed_triangle_set& its, const Transfor
|
||||
chs.push_back(collect_mesh_projection_points_above(range));
|
||||
});
|
||||
|
||||
const Polygons polygons(chs.begin(), chs.end());
|
||||
const Polygons polygons(std::make_move_iterator(chs.begin()), std::make_move_iterator(chs.end()));
|
||||
return Geometry::convex_hull(polygons);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "Platform.hpp"
|
||||
#include "Time.hpp"
|
||||
#include "format.hpp"
|
||||
#include "libslic3r.h"
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
Reference in New Issue
Block a user