update to latest version

This commit is contained in:
QIDI TECH
2023-06-27 11:07:34 +08:00
parent afe5c54367
commit dd0d4c8c4a
80 changed files with 1931 additions and 599 deletions

View File

@@ -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 &params)
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 &params)
{
// 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

View File

@@ -72,6 +72,7 @@ struct RegionExpansion
};
std::vector<RegionExpansion> propagate_waves(const WaveSeeds &seeds, const ExPolygons &boundary, const RegionExpansionParameters &params);
std::vector<RegionExpansion> propagate_waves(const ExPolygons &src, const ExPolygons &boundary, const RegionExpansionParameters &params);
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 &params);
} // Algorithm

View File

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

View File

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

View File

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

View File

@@ -5,6 +5,7 @@
#include <string>
#include <string_view>
#include <cstdint>
namespace Slic3r {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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(&regionPtr->perimeters(), paths);
if (!regionPtr->perimeters().empty()) { getExtrusionPathsFromEntity(&regionPtr->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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 &params,
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));

View File

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

View File

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

View File

@@ -4,6 +4,7 @@
#include <vector>
#include <string>
#include <istream>
#include <cstdint>
namespace Slic3r { namespace png {

View File

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

View File

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

View File

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

View File

@@ -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_ */

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -9,6 +9,7 @@
#include "Platform.hpp"
#include "Time.hpp"
#include "format.hpp"
#include "libslic3r.h"
#ifdef WIN32