Implemented wipe tower print path preview.
This commit is contained in:
parent
7d64c465c0
commit
e000b22578
19 changed files with 615 additions and 190 deletions
|
@ -1888,6 +1888,15 @@ sub load_print_object_toolpaths {
|
|||
Slic3r::GUI::_3DScene::_load_print_object_toolpaths($object, $self->volumes, $colors, $self->UseVBOs);
|
||||
}
|
||||
|
||||
# Create 3D thick extrusion lines for wipe tower extrusions.
|
||||
sub load_wipe_tower_toolpaths {
|
||||
my ($self, $print, $colors) = @_;
|
||||
|
||||
$self->SetCurrent($self->GetContext) if $self->UseVBOs;
|
||||
Slic3r::GUI::_3DScene::_load_wipe_tower_toolpaths($print, $self->volumes, $colors, $self->UseVBOs)
|
||||
if ($print->step_done(STEP_WIPE_TOWER));
|
||||
}
|
||||
|
||||
sub set_toolpaths_range {
|
||||
my ($self, $min_z, $max_z) = @_;
|
||||
$self->volumes->set_range($min_z, $max_z);
|
||||
|
|
|
@ -206,6 +206,7 @@ sub load_print {
|
|||
if ($self->IsShown) {
|
||||
# load skirt and brim
|
||||
$self->canvas->load_print_toolpaths($self->print, \@colors);
|
||||
$self->canvas->load_wipe_tower_toolpaths($self->print, \@colors);
|
||||
|
||||
foreach my $object (@{$self->print->objects}) {
|
||||
$self->canvas->load_print_object_toolpaths($object, \@colors);
|
||||
|
|
|
@ -44,6 +44,7 @@ sub process {
|
|||
$_->generate_support_material for @{$self->objects};
|
||||
$self->make_skirt;
|
||||
$self->make_brim; # must come after make_skirt
|
||||
$self->make_wipe_tower;
|
||||
|
||||
# time to make some statistics
|
||||
if (0) {
|
||||
|
@ -207,19 +208,13 @@ sub make_skirt {
|
|||
$_->generate_support_material for @{$self->objects};
|
||||
|
||||
return if $self->step_done(STEP_SKIRT);
|
||||
$self->set_step_started(STEP_SKIRT);
|
||||
|
||||
# since this method must be idempotent, we clear skirt paths *before*
|
||||
# checking whether we need to generate them
|
||||
$self->skirt->clear;
|
||||
|
||||
if (!$self->has_skirt) {
|
||||
$self->set_step_done(STEP_SKIRT);
|
||||
return;
|
||||
}
|
||||
|
||||
$self->status_cb->(88, "Generating skirt");
|
||||
$self->_make_skirt();
|
||||
$self->set_step_started(STEP_SKIRT);
|
||||
$self->skirt->clear;
|
||||
if ($self->has_skirt) {
|
||||
$self->status_cb->(88, "Generating skirt");
|
||||
$self->_make_skirt();
|
||||
}
|
||||
$self->set_step_done(STEP_SKIRT);
|
||||
}
|
||||
|
||||
|
@ -292,6 +287,27 @@ sub make_brim {
|
|||
$self->set_step_done(STEP_BRIM);
|
||||
}
|
||||
|
||||
sub make_wipe_tower {
|
||||
my $self = shift;
|
||||
|
||||
# prerequisites
|
||||
$_->make_perimeters for @{$self->objects};
|
||||
$_->infill for @{$self->objects};
|
||||
$_->generate_support_material for @{$self->objects};
|
||||
$self->make_skirt;
|
||||
$self->make_brim;
|
||||
|
||||
return if $self->step_done(STEP_WIPE_TOWER);
|
||||
|
||||
$self->set_step_started(STEP_WIPE_TOWER);
|
||||
$self->_clear_wipe_tower;
|
||||
if ($self->has_wipe_tower) {
|
||||
# $self->status_cb->(95, "Generating wipe tower");
|
||||
$self->_make_wipe_tower;
|
||||
}
|
||||
$self->set_step_done(STEP_WIPE_TOWER);
|
||||
}
|
||||
|
||||
# Wrapper around the C++ Slic3r::Print::validate()
|
||||
# to produce a Perl exception without a hang-up on some Strawberry perls.
|
||||
sub validate
|
||||
|
|
|
@ -6,7 +6,7 @@ use warnings;
|
|||
require Exporter;
|
||||
our @ISA = qw(Exporter);
|
||||
our @EXPORT_OK = qw(STEP_SLICE STEP_PERIMETERS STEP_PREPARE_INFILL
|
||||
STEP_INFILL STEP_SUPPORTMATERIAL STEP_SKIRT STEP_BRIM);
|
||||
STEP_INFILL STEP_SUPPORTMATERIAL STEP_SKIRT STEP_BRIM STEP_WIPE_TOWER);
|
||||
our %EXPORT_TAGS = (steps => \@EXPORT_OK);
|
||||
|
||||
1;
|
||||
|
|
|
@ -142,115 +142,79 @@ Wipe::wipe(GCode &gcodegen, bool toolchange)
|
|||
return gcode;
|
||||
}
|
||||
|
||||
WipeTowerIntegration::WipeTowerIntegration(const PrintConfig &print_config) : m_brim_done(false)
|
||||
{
|
||||
// Initialize the wipe tower.
|
||||
auto *wipe_tower = new WipeTowerPrusaMM(
|
||||
float(print_config.wipe_tower_x.value), float(print_config.wipe_tower_y.value),
|
||||
float(print_config.wipe_tower_width.value), float(print_config.wipe_tower_per_color_wipe.value));
|
||||
//wipe_tower->set_retract();
|
||||
//wipe_tower->set_zhop();
|
||||
//wipe_tower->set_zhop();
|
||||
// Set the extruder & material properties at the wipe tower object.
|
||||
for (size_t i = 0; i < 4; ++ i)
|
||||
wipe_tower->set_extruder(
|
||||
i,
|
||||
WipeTowerPrusaMM::parse_material(print_config.filament_type.get_at(i).c_str()),
|
||||
print_config.temperature.get_at(i),
|
||||
print_config.first_layer_temperature.get_at(i));
|
||||
m_impl.reset(wipe_tower);
|
||||
}
|
||||
|
||||
static inline Point wipe_tower_point_to_object_point(GCode &gcodegen, const WipeTower::xy &wipe_tower_pt)
|
||||
{
|
||||
return Point(scale_(wipe_tower_pt.x - gcodegen.origin().x), scale_(wipe_tower_pt.y - gcodegen.origin().y));
|
||||
}
|
||||
|
||||
std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id) const
|
||||
{
|
||||
std::string gcode;
|
||||
|
||||
// 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;
|
||||
gcode += gcodegen.travel_to(
|
||||
wipe_tower_point_to_object_point(gcodegen, tcr.start_pos),
|
||||
erMixed,
|
||||
"Travel to a Wipe Tower");
|
||||
gcode += gcodegen.unretract();
|
||||
|
||||
// Let the tool change be executed by the wipe tower class.
|
||||
// Inform the G-code writer about the changes done behind its back.
|
||||
gcode += tcr.gcode;
|
||||
// Accumulate the elapsed time for the correct calculation of layer cooling.
|
||||
//FIXME currently disabled as Slic3r PE needs to be updated to differentiate the moves it could slow down
|
||||
// from the moves it could not.
|
||||
// gcodegen.m_elapsed_time += tcr.elapsed_time;
|
||||
// Let the m_writer know the current extruder_id, but ignore the generated G-code.
|
||||
if (new_extruder_id >= 0 && gcodegen.writer().need_toolchange(new_extruder_id))
|
||||
gcodegen.writer().toolchange(new_extruder_id);
|
||||
// A phony move to the end position at the wipe tower.
|
||||
gcodegen.writer().travel_to_xy(Pointf(tcr.end_pos.x, tcr.end_pos.y));
|
||||
gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, tcr.end_pos));
|
||||
|
||||
// Prepare a future wipe.
|
||||
gcodegen.m_wipe.path.points.clear();
|
||||
if (new_extruder_id >= 0) {
|
||||
// Start the wipe at the current position.
|
||||
gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, tcr.end_pos));
|
||||
// Wipe end point: Wipe direction away from the closer tower edge to the further tower edge.
|
||||
gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen,
|
||||
WipeTower::xy((std::abs(m_left - tcr.end_pos.x) < std::abs(m_right - tcr.end_pos.x)) ? m_right : m_left,
|
||||
tcr.end_pos.y)));
|
||||
}
|
||||
|
||||
// Let the planner know we are traveling between objects.
|
||||
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true;
|
||||
return gcode;
|
||||
}
|
||||
|
||||
std::string WipeTowerIntegration::tool_change(GCode &gcodegen, int extruder_id, bool finish_layer)
|
||||
{
|
||||
bool over_wipe_tower = false;
|
||||
std::string gcode;
|
||||
|
||||
if (! m_brim_done || gcodegen.writer().need_toolchange(extruder_id)) {
|
||||
// Move over the wipe tower.
|
||||
gcode += this->travel_to(gcodegen, m_impl->tool_change(extruder_id, WipeTower::PURPOSE_MOVE_TO_TOWER).second);
|
||||
// Let the tool change be executed by the wipe tower class.
|
||||
//FIXME calculate time at the wipe tower and add it to m_elapsed_time
|
||||
std::pair<std::string, WipeTower::xy> code_and_pos = m_impl->tool_change(extruder_id, WipeTower::PURPOSE_EXTRUDE);
|
||||
// Inform the G-code writer about the changes done behind its back.
|
||||
gcode += code_and_pos.first;
|
||||
// Let the m_writer know the current extruder_id, but ignore the generated G-code.
|
||||
gcodegen.writer().toolchange(extruder_id);
|
||||
// A phony move to the end position at the wipe tower.
|
||||
gcodegen.writer().travel_to_xy(Pointf(code_and_pos.second.x, code_and_pos.second.y));
|
||||
gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, code_and_pos.second));
|
||||
this->prepare_wipe(gcodegen, code_and_pos.second);
|
||||
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true;
|
||||
over_wipe_tower = true;
|
||||
assert(m_layer_idx >= 0 && m_layer_idx <= m_tool_changes.size());
|
||||
if (! m_brim_done || gcodegen.writer().need_toolchange(extruder_id) || finish_layer) {
|
||||
if (m_layer_idx < m_tool_changes.size()) {
|
||||
assert(m_tool_change_idx < m_tool_changes[m_layer_idx].size());
|
||||
gcode += append_tcr(gcodegen, m_tool_changes[m_layer_idx][m_tool_change_idx++], extruder_id);
|
||||
}
|
||||
m_brim_done = true;
|
||||
}
|
||||
|
||||
if (finish_layer && ! m_impl->layer_finished()) {
|
||||
// Last extruder change on the layer or no extruder change at all.
|
||||
if (! over_wipe_tower)
|
||||
gcode += this->travel_to(gcodegen, m_impl->finish_layer(WipeTower::PURPOSE_MOVE_TO_TOWER).second);
|
||||
// Let the tool change be executed by the wipe tower class.
|
||||
//FIXME calculate time at the wipe tower and add it to m_elapsed_time
|
||||
std::pair<std::string, WipeTower::xy> code_and_pos = m_impl->finish_layer(WipeTower::PURPOSE_EXTRUDE);
|
||||
// Inform the G-code writer about the changes done behind its back.
|
||||
gcode += code_and_pos.first;
|
||||
// A phony move to the end position at the wipe tower.
|
||||
gcodegen.writer().travel_to_xy(Pointf(code_and_pos.second.x, code_and_pos.second.y));
|
||||
gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, code_and_pos.second));
|
||||
this->prepare_wipe(gcodegen, code_and_pos.second);
|
||||
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true;
|
||||
}
|
||||
|
||||
return gcode;
|
||||
}
|
||||
|
||||
// Print is finished. Now it remains to unload the filament safely with ramming over the wipe tower.
|
||||
std::string WipeTowerIntegration::finalize(GCode &gcodegen, const Print &print, bool current_layer_full)
|
||||
std::string WipeTowerIntegration::finalize(GCode &gcodegen)
|
||||
{
|
||||
std::string gcode;
|
||||
// Unload the current filament over the purge tower.
|
||||
if (current_layer_full) {
|
||||
// There is not enough space on the wipe tower to purge the nozzle into. Lift Z to the next layer.
|
||||
coordf_t new_print_z = gcodegen.writer().get_position().z + print.objects.front()->config.layer_height.value;
|
||||
gcode += gcodegen.change_layer(new_print_z);
|
||||
m_impl->set_layer(float(new_print_z), float(print.objects.front()->config.layer_height.value), 0, false, true);
|
||||
}
|
||||
gcode += this->tool_change(gcodegen, -1, false);
|
||||
m_impl.release();
|
||||
if (std::abs(gcodegen.writer().get_position().z - m_final_purge.print_z) > EPSILON)
|
||||
gcode += gcodegen.change_layer(m_final_purge.print_z);
|
||||
gcode += append_tcr(gcodegen, m_final_purge, -1);
|
||||
return gcode;
|
||||
}
|
||||
|
||||
std::string WipeTowerIntegration::travel_to(GCode &gcodegen, const WipeTower::xy &dest)
|
||||
{
|
||||
// Retract for a tool change, using the toolchange retract value and setting the priming extra length.
|
||||
std::string gcode = gcodegen.retract(true);
|
||||
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true;
|
||||
gcode += gcodegen.travel_to(
|
||||
wipe_tower_point_to_object_point(gcodegen, dest),
|
||||
erMixed,
|
||||
"Travel to a Wipe Tower");
|
||||
gcode += gcodegen.unretract();
|
||||
return gcode;
|
||||
}
|
||||
|
||||
void WipeTowerIntegration::prepare_wipe(GCode &gcodegen, const WipeTower::xy ¤t_position)
|
||||
{
|
||||
gcodegen.m_wipe.path.points.clear();
|
||||
// Start the wipe at the current position.
|
||||
gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, current_position));
|
||||
// Wipe end point: Wipe direction away from the closer tower edge to the further tower edge.
|
||||
float l = m_impl->position().x;
|
||||
float r = l + m_impl->width();
|
||||
gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen,
|
||||
WipeTower::xy((std::abs(l - current_position.x) < std::abs(r - current_position.x)) ? r : l,
|
||||
current_position.y)));
|
||||
}
|
||||
|
||||
#define EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(m_writer.extruder()->id)
|
||||
|
||||
inline void write(FILE *file, const std::string &what)
|
||||
|
@ -498,7 +462,10 @@ bool GCode::do_export(FILE *file, Print &print)
|
|||
}
|
||||
} else {
|
||||
// Find tool ordering for all the objects at once, and the initial extruder ID.
|
||||
tool_ordering = ToolOrdering(print, initial_extruder_id);
|
||||
// If the tool ordering has been pre-calculated by Print class for wipe tower already, reuse it.
|
||||
tool_ordering = print.m_tool_ordering.empty() ?
|
||||
ToolOrdering(print, initial_extruder_id) :
|
||||
print.m_tool_ordering;
|
||||
initial_extruder_id = tool_ordering.first_extruder();
|
||||
}
|
||||
if (initial_extruder_id == (unsigned int)-1) {
|
||||
|
@ -642,35 +609,24 @@ bool GCode::do_export(FILE *file, Print &print)
|
|||
// All extrusion moves with the same top layer height are extruded uninterrupted.
|
||||
std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> layers_to_print = collect_layers_to_print(print);
|
||||
// Prusa Multi-Material wipe tower.
|
||||
if (print.config.single_extruder_multi_material.value && print.config.wipe_tower.value &&
|
||||
if (print.has_wipe_tower() &&
|
||||
! tool_ordering.empty() && tool_ordering.front().wipe_tower_partitions > 0)
|
||||
m_wipe_tower.reset(new WipeTowerIntegration(print.config));
|
||||
m_wipe_tower.reset(new WipeTowerIntegration(print.config, print.m_wipe_tower_tool_changes, *print.m_wipe_tower_final_purge.get()));
|
||||
// Extrude the layers.
|
||||
for (auto &layer : layers_to_print) {
|
||||
// layer.second is of type std::vector<LayerToPrint>,
|
||||
// wher the objects are sorted by their sorted order given by object_indices.
|
||||
const ToolOrdering::LayerTools &layer_tools = tool_ordering.tools_for_layer(layer.first);
|
||||
if (layer_tools.has_wipe_tower && m_wipe_tower) {
|
||||
bool first_layer = &layer == layers_to_print.data();
|
||||
m_wipe_tower->set_layer(
|
||||
layer.first,
|
||||
first_layer ?
|
||||
print.objects.front()->config.first_layer_height.get_abs_value(print.objects.front()->config.layer_height.value) :
|
||||
print.objects.front()->config.layer_height.value,
|
||||
layer_tools.wipe_tower_partitions,
|
||||
first_layer,
|
||||
layer_tools.wipe_tower_partitions == 0 || (&layer_tools == &tool_ordering.back() || (&layer_tools + 1)->wipe_tower_partitions == 0));
|
||||
}
|
||||
if (m_wipe_tower && layer_tools.has_wipe_tower)
|
||||
m_wipe_tower->next_layer();
|
||||
this->process_layer(file, print, layer.second, layer_tools, size_t(-1));
|
||||
}
|
||||
write(file, this->filter(m_cooling_buffer->flush(), true));
|
||||
if (m_wipe_tower)
|
||||
// Purge the extruder, pull out the active filament.
|
||||
write(file, m_wipe_tower->finalize(*this));
|
||||
}
|
||||
|
||||
// write end commands to file
|
||||
if (m_wipe_tower)
|
||||
write(file, m_wipe_tower->finalize(*this, print, tool_ordering.back().wipe_tower_partitions > 0 && m_wipe_tower->layer_finished()));
|
||||
else
|
||||
write(file, this->retract()); // TODO: process this retract through PressureRegulator in order to discharge fully
|
||||
write(file, this->retract());
|
||||
write(file, m_writer.set_fan(false));
|
||||
writeln(file, m_placeholder_parser.process(print.config.end_gcode));
|
||||
write(file, m_writer.update_progress(m_layer_count, m_layer_count, true)); // 100%
|
||||
|
@ -1036,7 +992,7 @@ void GCode::process_layer(
|
|||
std::vector<std::unique_ptr<EdgeGrid::Grid>> lower_layer_edge_grids(layers.size());
|
||||
for (unsigned int extruder_id : layer_tools.extruders)
|
||||
{
|
||||
gcode += (layer_tools.has_wipe_tower && m_wipe_tower) ?
|
||||
gcode += (layer_tools.has_wipe_tower && m_wipe_tower) ?
|
||||
m_wipe_tower->tool_change(*this, extruder_id, extruder_id == layer_tools.extruders.back()) :
|
||||
this->set_extruder(extruder_id);
|
||||
|
||||
|
|
|
@ -75,18 +75,36 @@ public:
|
|||
|
||||
class WipeTowerIntegration {
|
||||
public:
|
||||
WipeTowerIntegration(const PrintConfig &config);
|
||||
void set_layer(coordf_t print_z, coordf_t layer_height, size_t max_tool_changes, bool is_first_layer, bool is_last_layer)
|
||||
{ m_impl->set_layer(float(print_z), float(layer_height), max_tool_changes, is_first_layer, is_last_layer); }
|
||||
bool layer_finished() const { return m_impl->layer_finished(); }
|
||||
WipeTowerIntegration(
|
||||
const PrintConfig &print_config,
|
||||
const std::vector<std::vector<WipeTower::ToolChangeResult>> &tool_changes,
|
||||
const WipeTower::ToolChangeResult &final_purge) :
|
||||
m_left(float(print_config.wipe_tower_x.value)),
|
||||
m_right(float(print_config.wipe_tower_x.value + print_config.wipe_tower_width.value)),
|
||||
m_tool_changes(tool_changes),
|
||||
m_final_purge(final_purge),
|
||||
m_layer_idx(-1),
|
||||
m_tool_change_idx(0),
|
||||
m_brim_done(false) {}
|
||||
|
||||
void next_layer() { ++ m_layer_idx; m_tool_change_idx = 0; }
|
||||
std::string tool_change(GCode &gcodegen, int extruder_id, bool finish_layer);
|
||||
std::string finalize(GCode &gcodegen, const Print &print, bool current_layer_full);
|
||||
std::string finalize(GCode &gcodegen);
|
||||
|
||||
private:
|
||||
std::string travel_to(GCode &codegen, const WipeTower::xy &dest);
|
||||
void prepare_wipe(GCode &gcodegen, const WipeTower::xy ¤t_position);
|
||||
std::unique_ptr<WipeTower> m_impl;
|
||||
bool m_brim_done;
|
||||
WipeTowerIntegration& operator=(const WipeTowerIntegration&);
|
||||
std::string append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id) const;
|
||||
|
||||
// Left / right edges of the wipe tower, for the planning of wipe moves.
|
||||
const float m_left;
|
||||
const float m_right;
|
||||
// Reference to cached values at the Printer class.
|
||||
const std::vector<std::vector<WipeTower::ToolChangeResult>> &m_tool_changes;
|
||||
const WipeTower::ToolChangeResult &m_final_purge;
|
||||
// Current layer index.
|
||||
int m_layer_idx;
|
||||
int m_tool_change_idx;
|
||||
bool m_brim_done;
|
||||
};
|
||||
|
||||
class GCode {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#include "Print.hpp"
|
||||
#include "ToolOrdering.hpp"
|
||||
|
||||
#include <assert.h>
|
||||
|
|
|
@ -4,10 +4,12 @@
|
|||
#define slic3r_ToolOrdering_hpp_
|
||||
|
||||
#include "libslic3r.h"
|
||||
#include "Print.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class Print;
|
||||
class PrintObject;
|
||||
|
||||
class ToolOrdering
|
||||
{
|
||||
public:
|
||||
|
@ -47,6 +49,8 @@ public:
|
|||
// (print.config.complete_objects is false).
|
||||
ToolOrdering(const Print &print, unsigned int first_extruder = (unsigned int)-1);
|
||||
|
||||
void clear() { m_layer_tools.clear(); }
|
||||
|
||||
// Get the first extruder printing the layer_tools, returns -1 if there is no layer printed.
|
||||
unsigned int first_extruder() const;
|
||||
|
||||
|
@ -61,6 +65,7 @@ public:
|
|||
const LayerTools& front() const { return m_layer_tools.front(); }
|
||||
const LayerTools& back() const { return m_layer_tools.back(); }
|
||||
bool empty() const { return m_layer_tools.empty(); }
|
||||
const std::vector<LayerTools>& layer_tools() const { return m_layer_tools; }
|
||||
|
||||
private:
|
||||
void initialize_layers(std::vector<coordf_t> &zs);
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <utility>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
|
@ -20,6 +21,8 @@ public:
|
|||
xy operator-(const xy &rhs) const { xy out(*this); out.x -= rhs.x; out.y -= rhs.y; return out; }
|
||||
xy& operator+=(const xy &rhs) { x += rhs.x; y += rhs.y; return *this; }
|
||||
xy& operator-=(const xy &rhs) { x -= rhs.x; y -= rhs.y; return *this; }
|
||||
bool operator==(const xy &rhs) { return x == rhs.x && y == rhs.y; }
|
||||
bool operator!=(const xy &rhs) { return x != rhs.x || y != rhs.y; }
|
||||
float x;
|
||||
float y;
|
||||
};
|
||||
|
@ -55,18 +58,50 @@ public:
|
|||
PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE,
|
||||
};
|
||||
|
||||
// Extrusion path of the wipe tower, for 3D preview of the generated tool paths.
|
||||
struct Extrusion
|
||||
{
|
||||
Extrusion(const xy &pos, float width, unsigned int tool) : pos(pos), width(width), tool(tool) {}
|
||||
// End position of this extrusion.
|
||||
xy pos;
|
||||
// Width of a squished extrusion, corrected for the roundings of the squished extrusions.
|
||||
// This is left zero if it is a travel move.
|
||||
float width;
|
||||
// Current extruder index.
|
||||
unsigned int tool;
|
||||
};
|
||||
|
||||
struct ToolChangeResult
|
||||
{
|
||||
// Print heigh of this tool change.
|
||||
float print_z;
|
||||
// G-code section to be directly included into the output G-code.
|
||||
std::string gcode;
|
||||
// For path preview.
|
||||
std::vector<Extrusion> extrusions;
|
||||
// Initial position, at which the wipe tower starts its action.
|
||||
// At this position the extruder is loaded and there is no Z-hop applied.
|
||||
xy start_pos;
|
||||
// Last point, at which the normal G-code generator of Slic3r shall continue.
|
||||
// At this position the extruder is loaded and there is no Z-hop applied.
|
||||
xy end_pos;
|
||||
// Time elapsed over this tool change.
|
||||
// This is useful not only for the print time estimation, but also for the control of layer cooling.
|
||||
float elapsed_time;
|
||||
};
|
||||
|
||||
// Returns gcode for toolchange and the end position.
|
||||
// if new_tool == -1, just unload the current filament over the wipe tower.
|
||||
virtual std::pair<std::string, xy> tool_change(int new_tool, Purpose purpose = PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) = 0;
|
||||
virtual ToolChangeResult tool_change(int new_tool, Purpose purpose = PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) = 0;
|
||||
|
||||
// Close the current wipe tower layer with a perimeter and possibly fill the unfilled space with a zig-zag.
|
||||
// Call this method only if layer_finished() is false.
|
||||
virtual std::pair<std::string, xy> finish_layer(Purpose purpose = PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) = 0;
|
||||
virtual ToolChangeResult finish_layer(Purpose purpose = PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) = 0;
|
||||
|
||||
// Is the current layer finished? A layer is finished if either the wipe tower is finished, or
|
||||
// the wipe tower has been completely covered by the tool change extrusions,
|
||||
// or the rest of the tower has been filled by a sparse infill with the finish_layer() method.
|
||||
virtual bool layer_finished() const = 0;
|
||||
virtual bool layer_finished() const = 0;
|
||||
};
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <math.h>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#ifdef __linux
|
||||
#include <strings.h>
|
||||
|
@ -25,16 +26,34 @@ public:
|
|||
m_current_pos(std::numeric_limits<float>::max(), std::numeric_limits<float>::max()),
|
||||
m_current_z(0.f),
|
||||
m_current_feedrate(0.f),
|
||||
m_extrusion_flow(0.f) {}
|
||||
m_extrusion_flow(0.f),
|
||||
m_layer_height(0.f),
|
||||
m_preview_suppressed(false),
|
||||
m_elapsed_time(0.f) {}
|
||||
|
||||
Writer& set_initial_position(const WipeTower::xy &pos) { m_current_pos = pos; return *this; }
|
||||
Writer& set_initial_position(const WipeTower::xy &pos) {
|
||||
m_start_pos = pos;
|
||||
m_current_pos = pos;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Writer& set_initial_tool(const unsigned int tool) { m_current_tool = tool; return *this; }
|
||||
|
||||
Writer& set_z(float z)
|
||||
{ m_current_z = z; return *this; }
|
||||
|
||||
Writer& set_layer_height(float layer_height)
|
||||
{ m_layer_height = layer_height; return *this; }
|
||||
|
||||
Writer& set_extrusion_flow(float flow)
|
||||
{ m_extrusion_flow = flow; return *this; }
|
||||
|
||||
// Suppress / resume G-code preview in Slic3r. Slic3r will have difficulty to differentiate the various
|
||||
// filament loading and cooling moves from normal extrusion moves. Therefore the writer
|
||||
// is asked to suppres output of some lines, which look like extrusions.
|
||||
Writer& suppress_preview() { m_preview_suppressed = true; return *this; }
|
||||
Writer& resume_preview() { m_preview_suppressed = false; return *this; }
|
||||
|
||||
Writer& feedrate(float f)
|
||||
{
|
||||
if (f != m_current_feedrate)
|
||||
|
@ -43,24 +62,48 @@ public:
|
|||
}
|
||||
|
||||
const std::string& gcode() const { return m_gcode; }
|
||||
const std::vector<WipeTower::Extrusion>& extrusions() const { return m_extrusions; }
|
||||
float x() const { return m_current_pos.x; }
|
||||
float y() const { return m_current_pos.y; }
|
||||
const WipeTower::xy& start_pos() const { return m_start_pos; }
|
||||
const WipeTower::xy& pos() const { return m_current_pos; }
|
||||
float elapsed_time() const { return m_elapsed_time; }
|
||||
|
||||
// Extrude with an explicitely provided amount of extrusion.
|
||||
Writer& extrude_explicit(float x, float y, float e, float f = 0.f)
|
||||
{
|
||||
if (x == m_current_pos.x && y == m_current_pos.y && e == 0.f && (f == 0.f || f == m_current_feedrate))
|
||||
// Neither extrusion nor a travel move.
|
||||
return *this;
|
||||
|
||||
float dx = x - m_current_pos.x;
|
||||
float dy = y - m_current_pos.y;
|
||||
double len = sqrt(dx*dx+dy*dy);
|
||||
if (! m_preview_suppressed && e > 0.f && len > 0.) {
|
||||
// Width of a squished extrusion, corrected for the roundings of the squished extrusions.
|
||||
// This is left zero if it is a travel move.
|
||||
float width = float(double(e) * m_filament_area / (len * m_layer_height));
|
||||
// Correct for the roundings of a squished extrusion.
|
||||
width += float(m_layer_height * (1. - M_PI / 4.));
|
||||
if (m_extrusions.empty() || m_extrusions.back().pos != m_current_pos)
|
||||
m_extrusions.emplace_back(WipeTower::Extrusion(m_current_pos, 0, m_current_tool));
|
||||
m_extrusions.emplace_back(WipeTower::Extrusion(WipeTower::xy(x, y), width, m_current_tool));
|
||||
}
|
||||
|
||||
m_gcode += "G1";
|
||||
if (x != m_current_pos.x)
|
||||
m_gcode += set_format_X(x);
|
||||
if (y != m_current_pos.y)
|
||||
m_gcode += set_format_Y(y);
|
||||
|
||||
if (e != 0.f)
|
||||
m_gcode += set_format_E(e);
|
||||
|
||||
if (f != 0.f && f != m_current_feedrate)
|
||||
m_gcode += set_format_F(f);
|
||||
|
||||
// Update the elapsed time with a rough estimate.
|
||||
m_elapsed_time += ((len == 0) ? std::abs(e) : len) / m_current_feedrate * 60.f;
|
||||
m_gcode += "\n";
|
||||
return *this;
|
||||
}
|
||||
|
@ -146,6 +189,7 @@ public:
|
|||
char buf[64];
|
||||
sprintf(buf, "T%d\n", tool);
|
||||
m_gcode += buf;
|
||||
m_current_tool = tool;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -222,11 +266,18 @@ public:
|
|||
Writer& append(const char *text) { m_gcode += text; return *this; }
|
||||
|
||||
private:
|
||||
WipeTower::xy m_start_pos;
|
||||
WipeTower::xy m_current_pos;
|
||||
float m_current_z;
|
||||
float m_current_feedrate;
|
||||
unsigned int m_current_tool;
|
||||
float m_layer_height;
|
||||
float m_extrusion_flow;
|
||||
bool m_preview_suppressed;
|
||||
std::string m_gcode;
|
||||
std::vector<WipeTower::Extrusion> m_extrusions;
|
||||
float m_elapsed_time;
|
||||
const double m_filament_area = 0.25*M_PI*1.75*1.75;
|
||||
|
||||
std::string set_format_X(float x) {
|
||||
char buf[64];
|
||||
|
@ -260,6 +311,8 @@ private:
|
|||
m_current_feedrate = f;
|
||||
return buf;
|
||||
}
|
||||
|
||||
Writer& operator=(const Writer &rhs);
|
||||
};
|
||||
|
||||
} // namespace PrusaMultiMaterial
|
||||
|
@ -287,7 +340,7 @@ WipeTowerPrusaMM::material_type WipeTowerPrusaMM::parse_material(const char *nam
|
|||
return INVALID;
|
||||
}
|
||||
|
||||
std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::tool_change(int tool, Purpose purpose)
|
||||
WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(int tool, Purpose purpose)
|
||||
{
|
||||
// Either it is the last tool unload,
|
||||
// or there must be a nonzero wipe tower partitions available.
|
||||
|
@ -306,10 +359,12 @@ std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::tool_change(int tool, Pu
|
|||
PrusaMultiMaterial::Writer writer;
|
||||
writer.set_extrusion_flow(m_extrusion_flow)
|
||||
.set_z(m_z_pos)
|
||||
.set_layer_height(m_layer_height)
|
||||
.set_initial_tool(m_current_tool)
|
||||
.append(";--------------------\n"
|
||||
"; CP TOOLCHANGE START\n")
|
||||
.comment_with_value(" toolchange #", m_num_tool_changes)
|
||||
.comment_material(m_current_material)
|
||||
.comment_material(m_material[m_current_tool])
|
||||
.append(";--------------------\n")
|
||||
.speed_override(100);
|
||||
|
||||
|
@ -318,7 +373,7 @@ std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::tool_change(int tool, Pu
|
|||
|
||||
if (purpose == PURPOSE_MOVE_TO_TOWER || purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) {
|
||||
// Scaffold leaks terribly, reduce leaking by a full retract when going to the wipe tower.
|
||||
float initial_retract = ((m_current_material == SCAFF) ? 1.f : 0.5f) * m_retract;
|
||||
float initial_retract = ((m_material[m_current_tool] == SCAFF) ? 1.f : 0.5f) * m_retract;
|
||||
writer // Lift for a Z hop.
|
||||
.z_hop(m_zhop, 7200)
|
||||
// Additional retract on move to tower.
|
||||
|
@ -339,7 +394,7 @@ std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::tool_change(int tool, Pu
|
|||
// Increase the extruder driver current to allow fast ramming.
|
||||
writer.set_extruder_trimpot(750);
|
||||
// Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool.
|
||||
toolchange_Unload(writer, cleaning_box, m_current_material,
|
||||
toolchange_Unload(writer, cleaning_box, m_material[m_current_tool],
|
||||
m_is_first_layer ? m_first_layer_temperature[tool] : m_temperature[tool]);
|
||||
|
||||
if (tool >= 0) {
|
||||
|
@ -379,10 +434,17 @@ std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::tool_change(int tool, Pu
|
|||
m_current_wipe_start_y += m_wipe_area;
|
||||
}
|
||||
|
||||
return std::pair<std::string, xy>(writer.gcode(), writer.pos());
|
||||
ToolChangeResult result;
|
||||
result.print_z = this->m_z_pos;
|
||||
result.gcode = writer.gcode();
|
||||
result.elapsed_time = writer.elapsed_time();
|
||||
result.extrusions = writer.extrusions();
|
||||
result.start_pos = writer.start_pos();
|
||||
result.end_pos = writer.pos();
|
||||
return result;
|
||||
}
|
||||
|
||||
std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::toolchange_Brim(Purpose purpose, bool sideOnly, float y_offset)
|
||||
WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(Purpose purpose, bool sideOnly, float y_offset)
|
||||
{
|
||||
const box_coordinates wipeTower_box(
|
||||
m_wipe_tower_pos,
|
||||
|
@ -393,6 +455,8 @@ std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::toolchange_Brim(Purpose
|
|||
writer.set_extrusion_flow(m_extrusion_flow * 1.1f)
|
||||
// Let the writer know the current Z position as a base for Z-hop.
|
||||
.set_z(m_z_pos)
|
||||
.set_layer_height(m_layer_height)
|
||||
.set_initial_tool(m_current_tool)
|
||||
.append(
|
||||
";-------------------------------------\n"
|
||||
"; CP WIPE TOWER FIRST LAYER BRIM START\n");
|
||||
|
@ -453,7 +517,14 @@ std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::toolchange_Brim(Purpose
|
|||
m_idx_tool_change_in_layer = 0;
|
||||
}
|
||||
|
||||
return std::pair<std::string, xy>(writer.gcode(), writer.pos());
|
||||
ToolChangeResult result;
|
||||
result.print_z = this->m_z_pos;
|
||||
result.gcode = writer.gcode();
|
||||
result.elapsed_time = writer.elapsed_time();
|
||||
result.extrusions = writer.extrusions();
|
||||
result.start_pos = writer.start_pos();
|
||||
result.end_pos = writer.pos();
|
||||
return result;
|
||||
}
|
||||
|
||||
// Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool.
|
||||
|
@ -510,7 +581,8 @@ void WipeTowerPrusaMM::toolchange_Unload(
|
|||
if (std::abs(writer.x() - xl) < std::abs(writer.x() - xr))
|
||||
std::swap(xl, xr);
|
||||
// Horizontal cooling moves will be performed at the following Y coordinate:
|
||||
writer.travel(xr, writer.y() + y_step * 0.8f, 7200);
|
||||
writer.travel(xr, writer.y() + y_step * 0.8f, 7200)
|
||||
.suppress_preview();
|
||||
switch (current_material)
|
||||
{
|
||||
case ABS:
|
||||
|
@ -541,7 +613,8 @@ void WipeTowerPrusaMM::toolchange_Unload(
|
|||
.cool(xl, xr, 5, -3, 2400);
|
||||
}
|
||||
|
||||
writer.flush_planner_queue();
|
||||
writer.resume_preview()
|
||||
.flush_planner_queue();
|
||||
}
|
||||
|
||||
// Change the tool, set a speed override for solube and flex materials.
|
||||
|
@ -561,7 +634,7 @@ void WipeTowerPrusaMM::toolchange_Change(
|
|||
writer.set_tool(new_tool)
|
||||
.speed_override(speed_override)
|
||||
.flush_planner_queue();
|
||||
m_current_material = new_material;
|
||||
m_current_tool = new_tool;
|
||||
}
|
||||
|
||||
void WipeTowerPrusaMM::toolchange_Load(
|
||||
|
@ -574,10 +647,12 @@ void WipeTowerPrusaMM::toolchange_Load(
|
|||
writer.append("; CP TOOLCHANGE LOAD\n")
|
||||
// Load the filament while moving left / right,
|
||||
// so the excess material will not create a blob at a single position.
|
||||
.suppress_preview()
|
||||
.load_move_x(xr, 20, 1400)
|
||||
.load_move_x(xl, 40, 3000)
|
||||
.load_move_x(xr, 20, 1600)
|
||||
.load_move_x(xl, 10, 1000);
|
||||
.load_move_x(xl, 10, 1000)
|
||||
.resume_preview();
|
||||
|
||||
// Extrude first five lines (just three lines if colorInit is set).
|
||||
writer.extrude(xr, writer.y(), 1600);
|
||||
|
@ -634,7 +709,7 @@ void WipeTowerPrusaMM::toolchange_Wipe(
|
|||
writer.set_extrusion_flow(m_extrusion_flow);
|
||||
}
|
||||
|
||||
std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::finish_layer(Purpose purpose)
|
||||
WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer(Purpose purpose)
|
||||
{
|
||||
// This should only be called if the layer is not finished yet.
|
||||
// Otherwise the caller would likely travel to the wipe tower in vain.
|
||||
|
@ -643,6 +718,8 @@ std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::finish_layer(Purpose pur
|
|||
PrusaMultiMaterial::Writer writer;
|
||||
writer.set_extrusion_flow(m_extrusion_flow)
|
||||
.set_z(m_z_pos)
|
||||
.set_layer_height(m_layer_height)
|
||||
.set_initial_tool(m_current_tool)
|
||||
.append(";--------------------\n"
|
||||
"; CP EMPTY GRID START\n")
|
||||
// m_num_layer_changes is incremented by set_z, so it is 1 based.
|
||||
|
@ -676,7 +753,7 @@ std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::finish_layer(Purpose pur
|
|||
} else {
|
||||
// The print head is inside the wipe tower. Rather move to the start of the following extrusion.
|
||||
// writer.set_initial_position(fill_box.ld);
|
||||
writer.travel(fill_box.ld, 7000);
|
||||
writer.set_initial_position(fill_box.ld);
|
||||
}
|
||||
|
||||
if (purpose == PURPOSE_EXTRUDE || purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) {
|
||||
|
@ -734,7 +811,14 @@ std::pair<std::string, WipeTower::xy> WipeTowerPrusaMM::finish_layer(Purpose pur
|
|||
m_idx_tool_change_in_layer = (unsigned int)m_max_color_changes;
|
||||
}
|
||||
|
||||
return std::pair<std::string, xy>(writer.gcode(), writer.pos());
|
||||
ToolChangeResult result;
|
||||
result.print_z = this->m_z_pos;
|
||||
result.gcode = writer.gcode();
|
||||
result.elapsed_time = writer.elapsed_time();
|
||||
result.extrusions = writer.extrusions();
|
||||
result.start_pos = writer.start_pos();
|
||||
result.end_pos = writer.pos();
|
||||
return result;
|
||||
}
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
|
|
@ -39,11 +39,13 @@ public:
|
|||
// y -- y coordinates of wipe tower in mm ( left bottom corner )
|
||||
// width -- width of wipe tower in mm ( default 60 mm - leave as it is )
|
||||
// wipe_area -- space available for one toolchange in mm
|
||||
WipeTowerPrusaMM(float x, float y, float width, float wipe_area) :
|
||||
WipeTowerPrusaMM(float x, float y, float width, float wipe_area, unsigned int initial_tool) :
|
||||
m_wipe_tower_pos(x, y),
|
||||
m_wipe_tower_width(width),
|
||||
m_wipe_area(wipe_area),
|
||||
m_z_pos(0.f) {
|
||||
m_z_pos(0.f),
|
||||
m_current_tool(initial_tool)
|
||||
{
|
||||
for (size_t i = 0; i < 4; ++ i) {
|
||||
// Extruder specific parameters.
|
||||
m_material[i] = PLA;
|
||||
|
@ -51,6 +53,7 @@ public:
|
|||
m_first_layer_temperature[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~WipeTowerPrusaMM() {}
|
||||
|
||||
// _retract - retract value in mm
|
||||
|
@ -81,6 +84,7 @@ public:
|
|||
bool is_last_layer)
|
||||
{
|
||||
m_z_pos = print_z;
|
||||
m_layer_height = layer_height;
|
||||
m_max_color_changes = max_tool_changes;
|
||||
m_is_first_layer = is_first_layer;
|
||||
m_is_last_layer = is_last_layer;
|
||||
|
@ -105,24 +109,24 @@ public:
|
|||
}
|
||||
|
||||
// Return the wipe tower position.
|
||||
virtual const xy& position() const { return m_wipe_tower_pos; }
|
||||
virtual const xy& position() const { return m_wipe_tower_pos; }
|
||||
// Return the wipe tower width.
|
||||
virtual float width() const { return m_wipe_tower_width; }
|
||||
virtual float width() const { return m_wipe_tower_width; }
|
||||
// The wipe tower is finished, there should be no more tool changes or wipe tower prints.
|
||||
virtual bool finished() const { return m_max_color_changes == 0; }
|
||||
virtual bool finished() const { return m_max_color_changes == 0; }
|
||||
|
||||
// Returns gcode for a toolchange and a final print head position.
|
||||
// On the first layer, extrude a brim around the future wipe tower first.
|
||||
virtual std::pair<std::string, xy> tool_change(int new_tool, Purpose purpose);
|
||||
virtual ToolChangeResult tool_change(int new_tool, Purpose purpose);
|
||||
|
||||
// Close the current wipe tower layer with a perimeter and possibly fill the unfilled space with a zig-zag.
|
||||
// Call this method only if layer_finished() is false.
|
||||
virtual std::pair<std::string, xy> finish_layer(Purpose purpose);
|
||||
virtual ToolChangeResult finish_layer(Purpose purpose);
|
||||
|
||||
// Is the current layer finished? A layer is finished if either the wipe tower is finished, or
|
||||
// the wipe tower has been completely covered by the tool change extrusions,
|
||||
// or the rest of the tower has been filled by a sparse infill with the finish_layer() method.
|
||||
virtual bool layer_finished() const
|
||||
virtual bool layer_finished() const
|
||||
{ return m_idx_tool_change_in_layer == m_max_color_changes; }
|
||||
|
||||
private:
|
||||
|
@ -143,6 +147,8 @@ private:
|
|||
float m_wipe_area;
|
||||
// Current Z position.
|
||||
float m_z_pos = 0.f;
|
||||
// Current layer height.
|
||||
float m_layer_height = 0.f;
|
||||
// Maximum number of color changes per layer.
|
||||
size_t m_max_color_changes = 0;
|
||||
// Is this the 1st layer of the print? If so, print the brim around the waste tower.
|
||||
|
@ -172,7 +178,7 @@ private:
|
|||
unsigned int m_idx_tool_change_in_layer = 0;
|
||||
// A fill-in direction (positive Y, negative Y) alternates with each layer.
|
||||
wipe_shape m_current_shape = SHAPE_NORMAL;
|
||||
material_type m_current_material = PLA;
|
||||
unsigned int m_current_tool = 0;
|
||||
// Current y position at the wipe tower.
|
||||
float m_current_wipe_start_y = 0.f;
|
||||
|
||||
|
@ -210,7 +216,7 @@ private:
|
|||
// Returns gcode for wipe tower brim
|
||||
// sideOnly -- set to false -- experimental, draw brim on sides of wipe tower
|
||||
// offset -- set to 0 -- experimental, offset to replace brim in front / rear of wipe tower
|
||||
std::pair<std::string, WipeTower::xy> toolchange_Brim(Purpose purpose, bool sideOnly = false, float y_offset = 0.f);
|
||||
ToolChangeResult toolchange_Brim(Purpose purpose, bool sideOnly = false, float y_offset = 0.f);
|
||||
|
||||
void toolchange_Unload(
|
||||
PrusaMultiMaterial::Writer &writer,
|
||||
|
|
|
@ -31,6 +31,8 @@ public:
|
|||
SurfaceCollection slices;
|
||||
|
||||
// collection of extrusion paths/loops filling gaps
|
||||
// These fills are generated by the perimeter generator.
|
||||
// They are not printed on their own, but they are copied to this->fills during infill generation.
|
||||
ExtrusionEntityCollection thin_fills;
|
||||
|
||||
// Unspecified fill polygons, used for overhang detection ("ensure vertical wall thickness feature")
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "Flow.hpp"
|
||||
#include "Geometry.hpp"
|
||||
#include "SupportMaterial.hpp"
|
||||
#include "GCode/WipeTowerPrusaMM.hpp"
|
||||
#include <algorithm>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
@ -117,7 +118,7 @@ Print::invalidate_state_by_config_options(const std::vector<t_config_option_key>
|
|||
|
||||
// this method only accepts PrintConfig option keys
|
||||
for (std::vector<t_config_option_key>::const_iterator opt_key = opt_keys.begin(); opt_key != opt_keys.end(); ++opt_key) {
|
||||
if (*opt_key == "skirts"
|
||||
if ( *opt_key == "skirts"
|
||||
|| *opt_key == "skirt_height"
|
||||
|| *opt_key == "skirt_distance"
|
||||
|| *opt_key == "min_skirt_length"
|
||||
|
@ -129,12 +130,26 @@ Print::invalidate_state_by_config_options(const std::vector<t_config_option_key>
|
|||
} else if (*opt_key == "nozzle_diameter"
|
||||
|| *opt_key == "resolution") {
|
||||
osteps.insert(posSlice);
|
||||
} else if (
|
||||
*opt_key == "complete_objects"
|
||||
|| *opt_key == "filament_type"
|
||||
|| *opt_key == "first_layer_temperature"
|
||||
|| *opt_key == "gcode_flavor"
|
||||
|| *opt_key == "single_extruder_multi_material"
|
||||
|| *opt_key == "spiral_vase"
|
||||
|| *opt_key == "temperature"
|
||||
|| *opt_key == "wipe_tower"
|
||||
|| *opt_key == "wipe_tower_x"
|
||||
|| *opt_key == "wipe_tower_y"
|
||||
|| *opt_key == "wipe_tower_width"
|
||||
|| *opt_key == "wipe_tower_per_color_wipe"
|
||||
|| *opt_key == "z_offset") {
|
||||
steps.insert(psWipeTower);
|
||||
} else if (*opt_key == "avoid_crossing_perimeters"
|
||||
|| *opt_key == "bed_shape"
|
||||
|| *opt_key == "bed_temperature"
|
||||
|| *opt_key == "bridge_acceleration"
|
||||
|| *opt_key == "bridge_fan_speed"
|
||||
|| *opt_key == "complete_objects"
|
||||
|| *opt_key == "cooling"
|
||||
|| *opt_key == "default_acceleration"
|
||||
|| *opt_key == "disable_fan_first_layers"
|
||||
|
@ -150,14 +165,11 @@ Print::invalidate_state_by_config_options(const std::vector<t_config_option_key>
|
|||
|| *opt_key == "fan_below_layer_time"
|
||||
|| *opt_key == "filament_diameter"
|
||||
|| *opt_key == "filament_notes"
|
||||
|| *opt_key == "filament_type"
|
||||
|| *opt_key == "filament_soluble"
|
||||
|| *opt_key == "first_layer_acceleration"
|
||||
|| *opt_key == "first_layer_bed_temperature"
|
||||
|| *opt_key == "first_layer_speed"
|
||||
|| *opt_key == "first_layer_temperature"
|
||||
|| *opt_key == "gcode_comments"
|
||||
|| *opt_key == "gcode_flavor"
|
||||
|| *opt_key == "infill_acceleration"
|
||||
|| *opt_key == "infill_first"
|
||||
|| *opt_key == "layer_gcode"
|
||||
|
@ -181,24 +193,15 @@ Print::invalidate_state_by_config_options(const std::vector<t_config_option_key>
|
|||
|| *opt_key == "retract_restart_extra_toolchange"
|
||||
|| *opt_key == "retract_speed"
|
||||
|| *opt_key == "deretract_speed"
|
||||
|| *opt_key == "single_extruder_multi_material"
|
||||
|| *opt_key == "slowdown_below_layer_time"
|
||||
|| *opt_key == "spiral_vase"
|
||||
|| *opt_key == "standby_temperature_delta"
|
||||
|| *opt_key == "start_gcode"
|
||||
|| *opt_key == "temperature"
|
||||
|| *opt_key == "threads"
|
||||
|| *opt_key == "toolchange_gcode"
|
||||
|| *opt_key == "travel_speed"
|
||||
|| *opt_key == "use_firmware_retraction"
|
||||
|| *opt_key == "use_relative_e_distances"
|
||||
|| *opt_key == "wipe"
|
||||
|| *opt_key == "wipe_tower"
|
||||
|| *opt_key == "wipe_tower_x"
|
||||
|| *opt_key == "wipe_tower_y"
|
||||
|| *opt_key == "wipe_tower_width"
|
||||
|| *opt_key == "wipe_tower_per_color_wipe"
|
||||
|| *opt_key == "z_offset"
|
||||
|| *opt_key == "max_volumetric_extrusion_rate_slope_negative"
|
||||
|| *opt_key == "max_volumetric_extrusion_rate_slope_positive") {
|
||||
// these options only affect G-code export, so nothing to invalidate
|
||||
|
@ -208,6 +211,7 @@ Print::invalidate_state_by_config_options(const std::vector<t_config_option_key>
|
|||
osteps.insert(posSupportMaterial);
|
||||
steps.insert(psSkirt);
|
||||
steps.insert(psBrim);
|
||||
steps.insert(psWipeTower);
|
||||
} else {
|
||||
// for legacy, if we can't handle this option let's invalidate all steps
|
||||
return this->invalidate_all_steps();
|
||||
|
@ -233,23 +237,22 @@ Print::invalidate_step(PrintStep step)
|
|||
bool invalidated = this->state.invalidate(step);
|
||||
|
||||
// propagate to dependent steps
|
||||
if (step == psSkirt) {
|
||||
if (step == psSkirt)
|
||||
this->invalidate_step(psBrim);
|
||||
}
|
||||
|
||||
return invalidated;
|
||||
}
|
||||
|
||||
bool
|
||||
Print::invalidate_all_steps()
|
||||
bool Print::invalidate_all_steps()
|
||||
{
|
||||
// make a copy because when invalidating steps the iterators are not working anymore
|
||||
std::set<PrintStep> steps = this->state.started;
|
||||
|
||||
bool invalidated = false;
|
||||
for (std::set<PrintStep>::const_iterator step = steps.begin(); step != steps.end(); ++step) {
|
||||
if (this->invalidate_step(*step)) invalidated = true;
|
||||
}
|
||||
for (PrintStep step : steps)
|
||||
if (this->invalidate_step(step))
|
||||
invalidated = true;
|
||||
|
||||
return invalidated;
|
||||
}
|
||||
|
||||
|
@ -369,6 +372,7 @@ void Print::add_model_object(ModelObject* model_object, int idx)
|
|||
// invalidate steps
|
||||
this->invalidate_step(psSkirt);
|
||||
this->invalidate_step(psBrim);
|
||||
this->invalidate_step(psWipeTower);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -991,15 +995,119 @@ void Print::_make_skirt()
|
|||
this->skirt.reverse();
|
||||
}
|
||||
|
||||
std::string
|
||||
Print::output_filename()
|
||||
// Wipe tower support.
|
||||
bool Print::has_wipe_tower()
|
||||
{
|
||||
return
|
||||
this->config.single_extruder_multi_material.value &&
|
||||
! this->config.spiral_vase.value &&
|
||||
this->config.wipe_tower.value &&
|
||||
this->config.nozzle_diameter.values.size() > 1;
|
||||
}
|
||||
|
||||
void Print::_clear_wipe_tower()
|
||||
{
|
||||
m_tool_ordering.clear();
|
||||
m_wipe_tower_tool_changes.clear();
|
||||
m_wipe_tower_final_purge.reset(nullptr);
|
||||
}
|
||||
|
||||
void Print::_make_wipe_tower()
|
||||
{
|
||||
this->_clear_wipe_tower();
|
||||
if (! this->has_wipe_tower())
|
||||
return;
|
||||
|
||||
m_tool_ordering = ToolOrdering(*this, (unsigned int)-1);
|
||||
unsigned int initial_extruder_id = m_tool_ordering.first_extruder();
|
||||
if (initial_extruder_id == (unsigned int)-1)
|
||||
return;
|
||||
|
||||
// Initialize the wipe tower.
|
||||
WipeTowerPrusaMM wipe_tower(
|
||||
float(this->config.wipe_tower_x.value), float(this->config.wipe_tower_y.value),
|
||||
float(this->config.wipe_tower_width.value), float(this->config.wipe_tower_per_color_wipe.value),
|
||||
initial_extruder_id);
|
||||
|
||||
//wipe_tower.set_retract();
|
||||
//wipe_tower.set_zhop();
|
||||
//wipe_tower.set_zhop();
|
||||
|
||||
// Set the extruder & material properties at the wipe tower object.
|
||||
for (size_t i = 0; i < 4; ++ i)
|
||||
wipe_tower.set_extruder(
|
||||
i,
|
||||
WipeTowerPrusaMM::parse_material(this->config.filament_type.get_at(i).c_str()),
|
||||
this->config.temperature.get_at(i),
|
||||
this->config.first_layer_temperature.get_at(i));
|
||||
|
||||
// Generate the wipe tower layers.
|
||||
m_wipe_tower_tool_changes.reserve(m_tool_ordering.layer_tools().size());
|
||||
unsigned int current_extruder_id = initial_extruder_id;
|
||||
for (const ToolOrdering::LayerTools &layer_tools : m_tool_ordering.layer_tools()) {
|
||||
if (! layer_tools.has_wipe_tower)
|
||||
// This is a support only layer, or the wipe tower does not reach to this height.
|
||||
continue;
|
||||
bool first_layer = &layer_tools == &m_tool_ordering.front();
|
||||
bool last_layer = &layer_tools == &m_tool_ordering.back() || (&layer_tools + 1)->wipe_tower_partitions == 0;
|
||||
wipe_tower.set_layer(
|
||||
float(layer_tools.print_z),
|
||||
float(first_layer ?
|
||||
this->objects.front()->config.first_layer_height.get_abs_value(this->objects.front()->config.layer_height.value) :
|
||||
this->objects.front()->config.layer_height.value),
|
||||
layer_tools.wipe_tower_partitions,
|
||||
first_layer,
|
||||
last_layer);
|
||||
std::vector<WipeTower::ToolChangeResult> tool_changes;
|
||||
for (unsigned int extruder_id : layer_tools.extruders)
|
||||
if ((first_layer && extruder_id == initial_extruder_id) || extruder_id != current_extruder_id) {
|
||||
tool_changes.emplace_back(wipe_tower.tool_change(extruder_id, WipeTower::PURPOSE_EXTRUDE));
|
||||
current_extruder_id = extruder_id;
|
||||
}
|
||||
if (! wipe_tower.layer_finished()) {
|
||||
tool_changes.emplace_back(wipe_tower.finish_layer(WipeTower::PURPOSE_EXTRUDE));
|
||||
if (tool_changes.size() > 1) {
|
||||
// Merge the two last tool changes into one.
|
||||
WipeTower::ToolChangeResult &tc1 = tool_changes[tool_changes.size() - 2];
|
||||
WipeTower::ToolChangeResult &tc2 = tool_changes.back();
|
||||
if (tc1.end_pos != tc2.start_pos) {
|
||||
// Add a travel move from tc1.end_pos to tc2.start_pos.
|
||||
char buf[2048];
|
||||
sprintf(buf, "G1 X%.3f Y%.3f F7200\n", tc2.start_pos.x, tc2.start_pos.y);
|
||||
tc1.gcode += buf;
|
||||
}
|
||||
tc1.gcode += tc2.gcode;
|
||||
append(tc1.extrusions, tc2.extrusions);
|
||||
tc1.end_pos = tc2.end_pos;
|
||||
tool_changes.pop_back();
|
||||
}
|
||||
}
|
||||
m_wipe_tower_tool_changes.emplace_back(std::move(tool_changes));
|
||||
if (last_layer)
|
||||
break;
|
||||
}
|
||||
|
||||
// Tower is printed to the top and it has no empty space for the final extruder purge.
|
||||
bool tower_full = m_tool_ordering.back().wipe_tower_partitions > 0 && wipe_tower.layer_finished();
|
||||
coordf_t last_print_z = m_tool_ordering.back().print_z;
|
||||
|
||||
// Unload the current filament over the purge tower.
|
||||
if (tower_full) {
|
||||
// There is not enough space on the wipe tower to purge the nozzle into. Lift Z to the next layer.
|
||||
coordf_t layer_height = this->objects.front()->config.layer_height.value;
|
||||
wipe_tower.set_layer(float(last_print_z + layer_height), float(layer_height), 0, false, true);
|
||||
}
|
||||
m_wipe_tower_final_purge = Slic3r::make_unique<WipeTower::ToolChangeResult>(
|
||||
wipe_tower.tool_change(-1, WipeTower::PURPOSE_EXTRUDE));
|
||||
}
|
||||
|
||||
std::string Print::output_filename()
|
||||
{
|
||||
this->placeholder_parser.update_timestamp();
|
||||
return this->placeholder_parser.process(this->config.output_filename_format.value);
|
||||
}
|
||||
|
||||
std::string
|
||||
Print::output_filepath(const std::string &path)
|
||||
std::string Print::output_filepath(const std::string &path)
|
||||
{
|
||||
// if we were supplied no path, generate an automatic one based on our first object's input file
|
||||
if (path.empty()) {
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
#include "Model.hpp"
|
||||
#include "PlaceholderParser.hpp"
|
||||
#include "Slicing.hpp"
|
||||
#include "GCode/ToolOrdering.hpp"
|
||||
#include "GCode/WipeTower.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
@ -22,7 +24,7 @@ class ModelObject;
|
|||
|
||||
// Print step IDs for keeping track of the print state.
|
||||
enum PrintStep {
|
||||
psSkirt, psBrim,
|
||||
psSkirt, psBrim, psWipeTower
|
||||
};
|
||||
enum PrintObjectStep {
|
||||
posSlice, posPerimeters, posPrepareInfill,
|
||||
|
@ -258,6 +260,18 @@ public:
|
|||
void auto_assign_extruders(ModelObject* model_object) const;
|
||||
|
||||
void _make_skirt();
|
||||
|
||||
// Wipe tower support.
|
||||
bool has_wipe_tower();
|
||||
void _clear_wipe_tower();
|
||||
void _make_wipe_tower();
|
||||
// Tool ordering of a non-sequential print has to be known to calculate the wipe tower.
|
||||
// Cache it here, so it does not need to be recalculated during the G-code generation.
|
||||
ToolOrdering m_tool_ordering;
|
||||
// Cache of tool changes per print layer.
|
||||
std::vector<std::vector<WipeTower::ToolChangeResult>> m_wipe_tower_tool_changes;
|
||||
std::unique_ptr<WipeTower::ToolChangeResult> m_wipe_tower_final_purge;
|
||||
|
||||
std::string output_filename();
|
||||
std::string output_filepath(const std::string &path);
|
||||
|
||||
|
|
|
@ -316,17 +316,21 @@ PrintObject::invalidate_step(PrintObjectStep step)
|
|||
this->invalidate_step(posPrepareInfill);
|
||||
this->_print->invalidate_step(psSkirt);
|
||||
this->_print->invalidate_step(psBrim);
|
||||
this->_print->invalidate_step(psWipeTower);
|
||||
} else if (step == posPrepareInfill) {
|
||||
this->invalidate_step(posInfill);
|
||||
} else if (step == posInfill) {
|
||||
this->_print->invalidate_step(psSkirt);
|
||||
this->_print->invalidate_step(psBrim);
|
||||
this->_print->invalidate_step(psWipeTower);
|
||||
} else if (step == posSlice) {
|
||||
this->invalidate_step(posPerimeters);
|
||||
this->invalidate_step(posSupportMaterial);
|
||||
this->_print->invalidate_step(psWipeTower);
|
||||
} else if (step == posSupportMaterial) {
|
||||
this->_print->invalidate_step(psSkirt);
|
||||
this->_print->invalidate_step(psBrim);
|
||||
this->_print->invalidate_step(psWipeTower);
|
||||
}
|
||||
|
||||
return invalidated;
|
||||
|
@ -1055,7 +1059,7 @@ void PrintObject::_slice()
|
|||
{
|
||||
BOOST_LOG_TRIVIAL(info) << "Slicing objects...";
|
||||
|
||||
#if 0
|
||||
#if 1
|
||||
// Disable parallelization for debugging purposes.
|
||||
static tbb::task_scheduler_init *tbb_init = nullptr;
|
||||
tbb_init = new tbb::task_scheduler_init(1);
|
||||
|
|
|
@ -962,4 +962,148 @@ void _3DScene::_load_print_object_toolpaths(
|
|||
BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - end";
|
||||
}
|
||||
|
||||
void _3DScene::_load_wipe_tower_toolpaths(
|
||||
const Print *print,
|
||||
GLVolumeCollection *volumes,
|
||||
const std::vector<std::string> &tool_colors_str,
|
||||
bool use_VBOs)
|
||||
{
|
||||
std::vector<float> tool_colors = parse_colors(tool_colors_str);
|
||||
|
||||
struct Ctxt
|
||||
{
|
||||
const Print *print;
|
||||
const std::vector<float> *tool_colors;
|
||||
|
||||
// Number of vertices (each vertex is 6x4=24 bytes long)
|
||||
static const size_t alloc_size_max () { return 131072; } // 3.15MB
|
||||
static const size_t alloc_size_reserve() { return alloc_size_max() * 2; }
|
||||
|
||||
static const float* color_support () { static float color[4] = { 0.5f, 1.0f, 0.5f, 1.f }; return color; } // greenish
|
||||
|
||||
// For cloring by a tool, return a parsed color.
|
||||
bool color_by_tool() const { return tool_colors != nullptr; }
|
||||
size_t number_tools() const { return this->color_by_tool() ? tool_colors->size() / 4 : 0; }
|
||||
const float* color_tool(size_t tool) const { return tool_colors->data() + tool * 4; }
|
||||
} ctxt;
|
||||
|
||||
ctxt.print = print;
|
||||
ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors;
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - start";
|
||||
|
||||
//FIXME Improve the heuristics for a grain size.
|
||||
size_t n_layers = print->m_wipe_tower_tool_changes.size();
|
||||
size_t grain_size = std::max(n_layers / 128, size_t(1));
|
||||
tbb::spin_mutex new_volume_mutex;
|
||||
auto new_volume = [volumes, &new_volume_mutex](const float *color) -> GLVolume* {
|
||||
auto *volume = new GLVolume(color);
|
||||
new_volume_mutex.lock();
|
||||
volumes->volumes.emplace_back(volume);
|
||||
new_volume_mutex.unlock();
|
||||
return volume;
|
||||
};
|
||||
const size_t volumes_cnt_initial = volumes->volumes.size();
|
||||
std::vector<GLVolumeCollection> volumes_per_thread(n_layers);
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<size_t>(0, n_layers, grain_size),
|
||||
[&ctxt, &new_volume](const tbb::blocked_range<size_t>& range) {
|
||||
// Bounding box of this slab of a wipe tower.
|
||||
BoundingBoxf3 bbox;
|
||||
bbox.min = Pointf3(
|
||||
ctxt.print->config.wipe_tower_x.value - 10.,
|
||||
ctxt.print->config.wipe_tower_y.value - 10.,
|
||||
ctxt.print->m_wipe_tower_tool_changes[range.begin()].front().print_z - 3.);
|
||||
bbox.max = Pointf3(
|
||||
ctxt.print->config.wipe_tower_x.value + ctxt.print->config.wipe_tower_width.value + 10.,
|
||||
ctxt.print->config.wipe_tower_y.value + ctxt.print->config.wipe_tower_per_color_wipe.value *
|
||||
ctxt.print->m_tool_ordering.layer_tools()[range.begin()].wipe_tower_partitions + 10.,
|
||||
ctxt.print->m_wipe_tower_tool_changes[range.end() - 1].front().print_z + 0.1);
|
||||
std::vector<GLVolume*> vols;
|
||||
if (ctxt.color_by_tool()) {
|
||||
for (size_t i = 0; i < ctxt.number_tools(); ++ i)
|
||||
vols.emplace_back(new_volume(ctxt.color_tool(i)));
|
||||
} else
|
||||
vols = { new_volume(ctxt.color_support()) };
|
||||
for (size_t i = 0; i < vols.size(); ++ i) {
|
||||
GLVolume &volume = *vols[i];
|
||||
volume.bounding_box = bbox;
|
||||
volume.indexed_vertex_array.reserve(ctxt.alloc_size_reserve());
|
||||
}
|
||||
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
|
||||
const std::vector<WipeTower::ToolChangeResult> &layer = ctxt.print->m_wipe_tower_tool_changes[idx_layer];
|
||||
coordf_t layer_height = (idx_layer == 0) ?
|
||||
ctxt.print->objects.front()->config.first_layer_height.get_abs_value(ctxt.print->objects.front()->config.layer_height.value) :
|
||||
ctxt.print->objects.front()->config.layer_height.value;
|
||||
for (size_t i = 0; i < vols.size(); ++ i) {
|
||||
GLVolume &vol = *vols[i];
|
||||
if (vol.print_zs.empty() || vol.print_zs.back() != layer.front().print_z) {
|
||||
vol.print_zs.push_back(layer.front().print_z);
|
||||
vol.offsets.push_back(vol.indexed_vertex_array.quad_indices.size());
|
||||
vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size());
|
||||
}
|
||||
}
|
||||
for (const WipeTower::ToolChangeResult &extrusions : layer) {
|
||||
for (size_t i = 1; i < extrusions.extrusions.size();) {
|
||||
const WipeTower::Extrusion &e = extrusions.extrusions[i];
|
||||
if (e.width == 0.) {
|
||||
++ i;
|
||||
continue;
|
||||
}
|
||||
size_t j = i + 1;
|
||||
if (ctxt.color_by_tool())
|
||||
for (; j < extrusions.extrusions.size() && extrusions.extrusions[j].tool == e.tool && extrusions.extrusions[j].width > 0.f; ++ j) ;
|
||||
else
|
||||
for (; j < extrusions.extrusions.size() && extrusions.extrusions[j].width > 0.f; ++ j) ;
|
||||
size_t n_lines = j - i;
|
||||
Lines lines;
|
||||
std::vector<double> widths;
|
||||
std::vector<double> heights;
|
||||
lines.reserve(n_lines);
|
||||
widths.reserve(n_lines);
|
||||
heights.assign(n_lines, layer_height);
|
||||
for (; i < j; ++ i) {
|
||||
const WipeTower::Extrusion &e = extrusions.extrusions[i];
|
||||
assert(e.width > 0.f);
|
||||
const WipeTower::Extrusion &e_prev = *(&e - 1);
|
||||
lines.emplace_back(Point::new_scale(e_prev.pos.x, e_prev.pos.y), Point::new_scale(e.pos.x, e.pos.y));
|
||||
widths.emplace_back(e.width);
|
||||
}
|
||||
thick_lines_to_verts(lines, widths, heights, lines.front().a == lines.back().b, extrusions.print_z, *vols[ctxt.color_by_tool() ? e.tool : 0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < vols.size(); ++ i) {
|
||||
GLVolume &vol = *vols[i];
|
||||
if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() / 6 > ctxt.alloc_size_max()) {
|
||||
// Store the vertex arrays and restart their containers,
|
||||
vols[i] = new_volume(vol.color);
|
||||
GLVolume &vol_new = *vols[i];
|
||||
vol_new.bounding_box = bbox;
|
||||
// Assign the large pre-allocated buffers to the new GLVolume.
|
||||
vol_new.indexed_vertex_array = std::move(vol.indexed_vertex_array);
|
||||
// Copy the content back to the old GLVolume.
|
||||
vol.indexed_vertex_array = vol_new.indexed_vertex_array;
|
||||
// Clear the buffers, but keep them pre-allocated.
|
||||
vol_new.indexed_vertex_array.clear();
|
||||
// Just make sure that clear did not clear the reserved memory.
|
||||
vol_new.indexed_vertex_array.reserve(ctxt.alloc_size_reserve());
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < vols.size(); ++ i)
|
||||
vols[i]->indexed_vertex_array.shrink_to_fit();
|
||||
});
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - finalizing results";
|
||||
// Remove empty volumes from the newly added volumes.
|
||||
volumes->volumes.erase(
|
||||
std::remove_if(volumes->volumes.begin() + volumes_cnt_initial, volumes->volumes.end(),
|
||||
[](const GLVolume *volume) { return volume->empty(); }),
|
||||
volumes->volumes.end());
|
||||
for (size_t i = volumes_cnt_initial; i < volumes->volumes.size(); ++ i)
|
||||
volumes->volumes[i]->indexed_vertex_array.finalize_geometry(use_VBOs);
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - end";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -354,6 +354,13 @@ public:
|
|||
GLVolumeCollection *volumes,
|
||||
const std::vector<std::string> &tool_colors,
|
||||
bool use_VBOs);
|
||||
|
||||
|
||||
static void _3DScene::_load_wipe_tower_toolpaths(
|
||||
const Print *print,
|
||||
GLVolumeCollection *volumes,
|
||||
const std::vector<std::string> &tool_colors_str,
|
||||
bool use_VBOs);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -135,4 +135,13 @@ _load_print_object_toolpaths(print_object, volumes, tool_colors, use_VBOs)
|
|||
CODE:
|
||||
_3DScene::_load_print_object_toolpaths(print_object, volumes, tool_colors, use_VBOs != 0);
|
||||
|
||||
void
|
||||
_load_wipe_tower_toolpaths(print, volumes, tool_colors, use_VBOs)
|
||||
Print *print;
|
||||
GLVolumeCollection *volumes;
|
||||
std::vector<std::string> tool_colors;
|
||||
int use_VBOs;
|
||||
CODE:
|
||||
_3DScene::_load_wipe_tower_toolpaths(print, volumes, tool_colors, use_VBOs != 0);
|
||||
|
||||
%}
|
||||
|
|
|
@ -19,6 +19,7 @@ _constant()
|
|||
STEP_SUPPORTMATERIAL = posSupportMaterial
|
||||
STEP_SKIRT = psSkirt
|
||||
STEP_BRIM = psBrim
|
||||
STEP_WIPE_TOWER = psWipeTower
|
||||
PROTOTYPE:
|
||||
CODE:
|
||||
RETVAL = ix;
|
||||
|
@ -238,6 +239,11 @@ _constant()
|
|||
Clone<Flow> skirt_flow();
|
||||
|
||||
void _make_skirt();
|
||||
|
||||
bool has_wipe_tower();
|
||||
void _clear_wipe_tower();
|
||||
void _make_wipe_tower();
|
||||
|
||||
%{
|
||||
|
||||
double
|
||||
|
|
Loading…
Reference in a new issue