mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-13 01:37:53 -06:00
Merge branch 'master' of https://github.com/prusa3d/PrusaSlicer
This commit is contained in:
commit
b7ac74bc42
53 changed files with 1789 additions and 915 deletions
|
@ -10,6 +10,7 @@
|
|||
#include "ShortestPath.hpp"
|
||||
#include "Print.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "libslic3r.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
@ -89,94 +90,6 @@ namespace Slic3r {
|
|||
return ok;
|
||||
}
|
||||
|
||||
void AvoidCrossingPerimeters::init_external_mp(const Print& print)
|
||||
{
|
||||
m_external_mp = Slic3r::make_unique<MotionPlanner>(union_ex(this->collect_contours_all_layers(print.objects())));
|
||||
}
|
||||
|
||||
// Plan a travel move while minimizing the number of perimeter crossings.
|
||||
// point is in unscaled coordinates, in the coordinate system of the current active object
|
||||
// (set by gcodegen.set_origin()).
|
||||
Polyline AvoidCrossingPerimeters::travel_to(const GCode& gcodegen, const Point& point)
|
||||
{
|
||||
// If use_external, then perform the path planning in the world coordinate system (correcting for the gcodegen offset).
|
||||
// Otherwise perform the path planning in the coordinate system of the active object.
|
||||
bool use_external = this->use_external_mp || this->use_external_mp_once;
|
||||
Point scaled_origin = use_external ? Point::new_scale(gcodegen.origin()(0), gcodegen.origin()(1)) : Point(0, 0);
|
||||
Polyline result = (use_external ? m_external_mp.get() : m_layer_mp.get())->
|
||||
shortest_path(gcodegen.last_pos() + scaled_origin, point + scaled_origin);
|
||||
if (use_external)
|
||||
result.translate(-scaled_origin);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Collect outer contours of all objects over all layers.
|
||||
// Discard objects only containing thin walls (offset would fail on an empty polygon).
|
||||
// Used by avoid crossing perimeters feature.
|
||||
Polygons AvoidCrossingPerimeters::collect_contours_all_layers(const PrintObjectPtrs& objects)
|
||||
{
|
||||
Polygons islands;
|
||||
for (const PrintObject* object : objects) {
|
||||
// Reducing all the object slices into the Z projection in a logarithimc fashion.
|
||||
// First reduce to half the number of layers.
|
||||
std::vector<Polygons> polygons_per_layer((object->layers().size() + 1) / 2);
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, object->layers().size() / 2),
|
||||
[&object, &polygons_per_layer](const tbb::blocked_range<size_t>& range) {
|
||||
for (size_t i = range.begin(); i < range.end(); ++i) {
|
||||
const Layer* layer1 = object->layers()[i * 2];
|
||||
const Layer* layer2 = object->layers()[i * 2 + 1];
|
||||
Polygons polys;
|
||||
polys.reserve(layer1->lslices.size() + layer2->lslices.size());
|
||||
for (const ExPolygon& expoly : layer1->lslices)
|
||||
//FIXME no holes?
|
||||
polys.emplace_back(expoly.contour);
|
||||
for (const ExPolygon& expoly : layer2->lslices)
|
||||
//FIXME no holes?
|
||||
polys.emplace_back(expoly.contour);
|
||||
polygons_per_layer[i] = union_(polys);
|
||||
}
|
||||
});
|
||||
if (object->layers().size() & 1) {
|
||||
const Layer* layer = object->layers().back();
|
||||
Polygons polys;
|
||||
polys.reserve(layer->lslices.size());
|
||||
for (const ExPolygon& expoly : layer->lslices)
|
||||
//FIXME no holes?
|
||||
polys.emplace_back(expoly.contour);
|
||||
polygons_per_layer.back() = union_(polys);
|
||||
}
|
||||
// Now reduce down to a single layer.
|
||||
size_t cnt = polygons_per_layer.size();
|
||||
while (cnt > 1) {
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, cnt / 2),
|
||||
[&polygons_per_layer](const tbb::blocked_range<size_t>& range) {
|
||||
for (size_t i = range.begin(); i < range.end(); ++i) {
|
||||
Polygons polys;
|
||||
polys.reserve(polygons_per_layer[i * 2].size() + polygons_per_layer[i * 2 + 1].size());
|
||||
polygons_append(polys, polygons_per_layer[i * 2]);
|
||||
polygons_append(polys, polygons_per_layer[i * 2 + 1]);
|
||||
polygons_per_layer[i * 2] = union_(polys);
|
||||
}
|
||||
});
|
||||
for (size_t i = 1; i < cnt / 2; ++i)
|
||||
polygons_per_layer[i] = std::move(polygons_per_layer[i * 2]);
|
||||
if (cnt & 1)
|
||||
polygons_per_layer[cnt / 2] = std::move(polygons_per_layer[cnt - 1]);
|
||||
cnt = (cnt + 1) / 2;
|
||||
}
|
||||
// And collect copies of the objects.
|
||||
for (const PrintInstance& instance : object->instances()) {
|
||||
// All the layers were reduced to the 1st item of polygons_per_layer.
|
||||
size_t i = islands.size();
|
||||
polygons_append(islands, polygons_per_layer.front());
|
||||
for (; i < islands.size(); ++i)
|
||||
islands[i].translate(instance.shift);
|
||||
}
|
||||
}
|
||||
return islands;
|
||||
}
|
||||
|
||||
|
||||
std::string OozePrevention::pre_toolchange(GCode& gcodegen)
|
||||
{
|
||||
std::string gcode;
|
||||
|
@ -322,7 +235,7 @@ namespace Slic3r {
|
|||
// Move over the wipe tower.
|
||||
// Retract for a tool change, using the toolchange retract value and setting the priming extra length.
|
||||
gcode += gcodegen.retract(true);
|
||||
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true;
|
||||
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once();
|
||||
gcode += gcodegen.travel_to(
|
||||
wipe_tower_point_to_object_point(gcodegen, start_pos),
|
||||
erMixed,
|
||||
|
@ -419,7 +332,7 @@ namespace Slic3r {
|
|||
}
|
||||
|
||||
// Let the planner know we are traveling between objects.
|
||||
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true;
|
||||
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once();
|
||||
return gcode;
|
||||
}
|
||||
|
||||
|
@ -1240,13 +1153,6 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
|
|||
// Set other general things.
|
||||
_write(file, this->preamble());
|
||||
|
||||
// Initialize a motion planner for object-to-object travel moves.
|
||||
m_avoid_crossing_perimeters.reset();
|
||||
if (print.config().avoid_crossing_perimeters.value) {
|
||||
m_avoid_crossing_perimeters.init_external_mp(print);
|
||||
print.throw_if_canceled();
|
||||
}
|
||||
|
||||
// Calculate wiping points if needed
|
||||
DoExport::init_ooze_prevention(print, m_ooze_prevention);
|
||||
print.throw_if_canceled();
|
||||
|
@ -1282,12 +1188,12 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
|
|||
// Move to the origin position for the copy we're going to print.
|
||||
// This happens before Z goes down to layer 0 again, so that no collision happens hopefully.
|
||||
m_enable_cooling_markers = false; // we're not filtering these moves through CoolingBuffer
|
||||
m_avoid_crossing_perimeters.use_external_mp_once = true;
|
||||
m_avoid_crossing_perimeters.use_external_mp_once();
|
||||
_write(file, this->retract());
|
||||
_write(file, this->travel_to(Point(0, 0), erNone, "move to origin position for next object"));
|
||||
m_enable_cooling_markers = true;
|
||||
// Disable motion planner when traveling to first object point.
|
||||
m_avoid_crossing_perimeters.disable_once = true;
|
||||
m_avoid_crossing_perimeters.disable_once();
|
||||
// Ff we are printing the bottom layer of an object, and we have already finished
|
||||
// another one, set first layer temperatures. This happens before the Z move
|
||||
// is triggered, so machine has more time to reach such temperatures.
|
||||
|
@ -2113,7 +2019,7 @@ void GCode::process_layer(
|
|||
if (auto loops_it = skirt_loops_per_extruder.find(extruder_id); loops_it != skirt_loops_per_extruder.end()) {
|
||||
const std::pair<size_t, size_t> loops = loops_it->second;
|
||||
this->set_origin(0., 0.);
|
||||
m_avoid_crossing_perimeters.use_external_mp = true;
|
||||
m_avoid_crossing_perimeters.use_external_mp();
|
||||
Flow layer_skirt_flow(print.skirt_flow());
|
||||
layer_skirt_flow.height = float(m_skirt_done.back() - (m_skirt_done.size() == 1 ? 0. : m_skirt_done[m_skirt_done.size() - 2]));
|
||||
double mm3_per_mm = layer_skirt_flow.mm3_per_mm();
|
||||
|
@ -2127,23 +2033,23 @@ void GCode::process_layer(
|
|||
//FIXME using the support_material_speed of the 1st object printed.
|
||||
gcode += this->extrude_loop(loop, "skirt", m_config.support_material_speed.value);
|
||||
}
|
||||
m_avoid_crossing_perimeters.use_external_mp = false;
|
||||
m_avoid_crossing_perimeters.use_external_mp(false);
|
||||
// Allow a straight travel move to the first object point if this is the first layer (but don't in next layers).
|
||||
if (first_layer && loops.first == 0)
|
||||
m_avoid_crossing_perimeters.disable_once = true;
|
||||
m_avoid_crossing_perimeters.disable_once();
|
||||
}
|
||||
|
||||
// Extrude brim with the extruder of the 1st region.
|
||||
if (! m_brim_done) {
|
||||
this->set_origin(0., 0.);
|
||||
m_avoid_crossing_perimeters.use_external_mp = true;
|
||||
m_avoid_crossing_perimeters.use_external_mp();
|
||||
for (const ExtrusionEntity *ee : print.brim().entities) {
|
||||
gcode += this->extrude_entity(*ee, "brim", m_config.support_material_speed.value);
|
||||
}
|
||||
m_brim_done = true;
|
||||
m_avoid_crossing_perimeters.use_external_mp = false;
|
||||
m_avoid_crossing_perimeters.use_external_mp(false);
|
||||
// Allow a straight travel move to the first object point.
|
||||
m_avoid_crossing_perimeters.disable_once = true;
|
||||
m_avoid_crossing_perimeters.disable_once();
|
||||
}
|
||||
|
||||
|
||||
|
@ -2163,15 +2069,14 @@ void GCode::process_layer(
|
|||
m_config.apply(instance_to_print.print_object.config(), true);
|
||||
m_layer = layers[instance_to_print.layer_id].layer();
|
||||
if (m_config.avoid_crossing_perimeters)
|
||||
m_avoid_crossing_perimeters.init_layer_mp(union_ex(m_layer->lslices, true));
|
||||
|
||||
m_avoid_crossing_perimeters.init_layer(*m_layer);
|
||||
if (this->config().gcode_label_objects)
|
||||
gcode += std::string("; printing object ") + instance_to_print.print_object.model_object()->name + " id:" + std::to_string(instance_to_print.layer_id) + " copy " + std::to_string(instance_to_print.instance_id) + "\n";
|
||||
// When starting a new object, use the external motion planner for the first travel move.
|
||||
const Point &offset = instance_to_print.print_object.instances()[instance_to_print.instance_id].shift;
|
||||
std::pair<const PrintObject*, Point> this_object_copy(&instance_to_print.print_object, offset);
|
||||
if (m_last_obj_copy != this_object_copy)
|
||||
m_avoid_crossing_perimeters.use_external_mp_once = true;
|
||||
m_avoid_crossing_perimeters.use_external_mp_once();
|
||||
m_last_obj_copy = this_object_copy;
|
||||
this->set_origin(unscale(offset));
|
||||
if (instance_to_print.object_by_extruder.support != nullptr && !print_wipe_extrusions) {
|
||||
|
@ -2773,43 +2678,51 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string
|
|||
/* Define the travel move as a line between current position and the taget point.
|
||||
This is expressed in print coordinates, so it will need to be translated by
|
||||
this->origin in order to get G-code coordinates. */
|
||||
Polyline travel;
|
||||
travel.append(this->last_pos());
|
||||
travel.append(point);
|
||||
Polyline travel { this->last_pos(), point };
|
||||
|
||||
// check whether a straight travel move would need retraction
|
||||
bool needs_retraction = this->needs_retraction(travel, role);
|
||||
bool needs_retraction = this->needs_retraction(travel, role);
|
||||
// check whether wipe could be disabled without causing visible stringing
|
||||
bool could_be_wipe_disabled = false;
|
||||
|
||||
// if a retraction would be needed, try to use avoid_crossing_perimeters to plan a
|
||||
// multi-hop travel path inside the configuration space
|
||||
if (needs_retraction
|
||||
&& m_config.avoid_crossing_perimeters
|
||||
&& ! m_avoid_crossing_perimeters.disable_once) {
|
||||
travel = m_avoid_crossing_perimeters.travel_to(*this, point);
|
||||
|
||||
&& ! m_avoid_crossing_perimeters.disabled_once()) {
|
||||
travel = m_avoid_crossing_perimeters.travel_to(*this, point, &could_be_wipe_disabled);
|
||||
// check again whether the new travel path still needs a retraction
|
||||
needs_retraction = this->needs_retraction(travel, role);
|
||||
//if (needs_retraction && m_layer_index > 1) exit(0);
|
||||
}
|
||||
|
||||
// Re-allow avoid_crossing_perimeters for the next travel moves
|
||||
m_avoid_crossing_perimeters.disable_once = false;
|
||||
m_avoid_crossing_perimeters.use_external_mp_once = false;
|
||||
m_avoid_crossing_perimeters.reset_once_modifiers();
|
||||
|
||||
// generate G-code for the travel move
|
||||
std::string gcode;
|
||||
if (needs_retraction)
|
||||
if (needs_retraction) {
|
||||
if (m_config.avoid_crossing_perimeters && could_be_wipe_disabled)
|
||||
m_wipe.reset_path();
|
||||
|
||||
Point last_post_before_retract = this->last_pos();
|
||||
gcode += this->retract();
|
||||
else
|
||||
// When "Wipe while retracting" is enabled, then extruder moves to another position, and travel from this position can cross perimeters.
|
||||
// Because of it, it is necessary to call avoid crossing perimeters for the path between previous last_post and last_post after calling retraction()
|
||||
if (last_post_before_retract != this->last_pos() && m_config.avoid_crossing_perimeters) {
|
||||
Polyline retract_travel = m_avoid_crossing_perimeters.travel_to(*this, last_post_before_retract);
|
||||
append(retract_travel.points, travel.points);
|
||||
travel = std::move(retract_travel);
|
||||
}
|
||||
} else
|
||||
// Reset the wipe path when traveling, so one would not wipe along an old path.
|
||||
m_wipe.reset_path();
|
||||
|
||||
// use G1 because we rely on paths being straight (G0 may make round paths)
|
||||
Lines lines = travel.lines();
|
||||
if (! lines.empty()) {
|
||||
for (const Line &line : lines)
|
||||
gcode += m_writer.travel_to_xy(this->point_to_gcode(line.b), comment);
|
||||
this->set_last_pos(lines.back().b);
|
||||
if (travel.size() >= 2) {
|
||||
for (size_t i = 1; i < travel.size(); ++ i)
|
||||
gcode += m_writer.travel_to_xy(this->point_to_gcode(travel.points[i]), comment);
|
||||
this->set_last_pos(travel.points.back());
|
||||
}
|
||||
return gcode;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue