Merge remote-tracking branch 'remotes/origin/wipe_tower_improvements'
This commit is contained in:
commit
c7f59aca7d
@ -322,7 +322,13 @@ sub selection_changed {
|
|||||||
}
|
}
|
||||||
# get default values
|
# get default values
|
||||||
my $default_config = Slic3r::Config::new_from_defaults_keys(\@opt_keys);
|
my $default_config = Slic3r::Config::new_from_defaults_keys(\@opt_keys);
|
||||||
|
|
||||||
|
# decide which settings will be shown by default
|
||||||
|
if ($itemData->{type} eq 'object') {
|
||||||
|
$config->set_ifndef('wipe_into_objects', 0);
|
||||||
|
$config->set_ifndef('wipe_into_infill', 0);
|
||||||
|
}
|
||||||
|
|
||||||
# append default extruder
|
# append default extruder
|
||||||
push @opt_keys, 'extruder';
|
push @opt_keys, 'extruder';
|
||||||
$default_config->set('extruder', 0);
|
$default_config->set('extruder', 0);
|
||||||
@ -330,7 +336,14 @@ sub selection_changed {
|
|||||||
$self->{settings_panel}->set_default_config($default_config);
|
$self->{settings_panel}->set_default_config($default_config);
|
||||||
$self->{settings_panel}->set_config($config);
|
$self->{settings_panel}->set_config($config);
|
||||||
$self->{settings_panel}->set_opt_keys(\@opt_keys);
|
$self->{settings_panel}->set_opt_keys(\@opt_keys);
|
||||||
$self->{settings_panel}->set_fixed_options([qw(extruder)]);
|
|
||||||
|
# disable minus icon to remove the settings
|
||||||
|
if ($itemData->{type} eq 'object') {
|
||||||
|
$self->{settings_panel}->set_fixed_options([qw(extruder), qw(wipe_into_infill), qw(wipe_into_objects)]);
|
||||||
|
} else {
|
||||||
|
$self->{settings_panel}->set_fixed_options([qw(extruder)]);
|
||||||
|
}
|
||||||
|
|
||||||
$self->{settings_panel}->enable;
|
$self->{settings_panel}->enable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,6 +61,7 @@ plan tests => 8;
|
|||||||
$config->set('infill_every_layers', 2);
|
$config->set('infill_every_layers', 2);
|
||||||
$config->set('perimeter_extruder', 1);
|
$config->set('perimeter_extruder', 1);
|
||||||
$config->set('infill_extruder', 2);
|
$config->set('infill_extruder', 2);
|
||||||
|
$config->set('wipe_into_infill', 0);
|
||||||
$config->set('support_material_extruder', 3);
|
$config->set('support_material_extruder', 3);
|
||||||
$config->set('support_material_interface_extruder', 3);
|
$config->set('support_material_interface_extruder', 3);
|
||||||
$config->set('top_solid_layers', 0);
|
$config->set('top_solid_layers', 0);
|
||||||
|
1
t/fill.t
1
t/fill.t
@ -201,6 +201,7 @@ for my $pattern (qw(rectilinear honeycomb hilbertcurve concentric)) {
|
|||||||
$config->set('bottom_solid_layers', 0);
|
$config->set('bottom_solid_layers', 0);
|
||||||
$config->set('infill_extruder', 2);
|
$config->set('infill_extruder', 2);
|
||||||
$config->set('infill_extrusion_width', 0.5);
|
$config->set('infill_extrusion_width', 0.5);
|
||||||
|
$config->set('wipe_into_infill', 0);
|
||||||
$config->set('fill_density', 40);
|
$config->set('fill_density', 40);
|
||||||
$config->set('cooling', [ 0 ]); # for preventing speeds from being altered
|
$config->set('cooling', [ 0 ]); # for preventing speeds from being altered
|
||||||
$config->set('first_layer_speed', '100%'); # for preventing speeds from being altered
|
$config->set('first_layer_speed', '100%'); # for preventing speeds from being altered
|
||||||
|
@ -92,6 +92,7 @@ public:
|
|||||||
virtual double min_mm3_per_mm() const = 0;
|
virtual double min_mm3_per_mm() const = 0;
|
||||||
virtual Polyline as_polyline() const = 0;
|
virtual Polyline as_polyline() const = 0;
|
||||||
virtual double length() const = 0;
|
virtual double length() const = 0;
|
||||||
|
virtual double total_volume() const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::vector<ExtrusionEntity*> ExtrusionEntitiesPtr;
|
typedef std::vector<ExtrusionEntity*> ExtrusionEntitiesPtr;
|
||||||
@ -148,6 +149,7 @@ public:
|
|||||||
// Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
|
// Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
|
||||||
double min_mm3_per_mm() const { return this->mm3_per_mm; }
|
double min_mm3_per_mm() const { return this->mm3_per_mm; }
|
||||||
Polyline as_polyline() const { return this->polyline; }
|
Polyline as_polyline() const { return this->polyline; }
|
||||||
|
virtual double total_volume() const { return mm3_per_mm * unscale(length()); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void _inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const;
|
void _inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const;
|
||||||
@ -194,6 +196,7 @@ public:
|
|||||||
// Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
|
// Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
|
||||||
double min_mm3_per_mm() const;
|
double min_mm3_per_mm() const;
|
||||||
Polyline as_polyline() const;
|
Polyline as_polyline() const;
|
||||||
|
virtual double total_volume() const { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; }
|
||||||
};
|
};
|
||||||
|
|
||||||
// Single continuous extrusion loop, possibly with varying extrusion thickness, extrusion height or bridging / non bridging.
|
// Single continuous extrusion loop, possibly with varying extrusion thickness, extrusion height or bridging / non bridging.
|
||||||
@ -241,6 +244,7 @@ public:
|
|||||||
// Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
|
// Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
|
||||||
double min_mm3_per_mm() const;
|
double min_mm3_per_mm() const;
|
||||||
Polyline as_polyline() const { return this->polygon().split_at_first_point(); }
|
Polyline as_polyline() const { return this->polygon().split_at_first_point(); }
|
||||||
|
virtual double total_volume() const { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ExtrusionLoopRole m_loop_role;
|
ExtrusionLoopRole m_loop_role;
|
||||||
|
@ -125,6 +125,7 @@ void ExtrusionEntityCollection::chained_path_from(Point start_near, ExtrusionEnt
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ExtrusionEntity* entity = (*it)->clone();
|
ExtrusionEntity* entity = (*it)->clone();
|
||||||
my_paths.push_back(entity);
|
my_paths.push_back(entity);
|
||||||
if (orig_indices != NULL) indices_map[entity] = it - this->entities.begin();
|
if (orig_indices != NULL) indices_map[entity] = it - this->entities.begin();
|
||||||
|
@ -79,6 +79,7 @@ public:
|
|||||||
void flatten(ExtrusionEntityCollection* retval) const;
|
void flatten(ExtrusionEntityCollection* retval) const;
|
||||||
ExtrusionEntityCollection flatten() const;
|
ExtrusionEntityCollection flatten() const;
|
||||||
double min_mm3_per_mm() const;
|
double min_mm3_per_mm() const;
|
||||||
|
virtual double total_volume() const {double volume=0.; for (const auto& ent : entities) volume+=ent->total_volume(); return volume; }
|
||||||
|
|
||||||
// Following methods shall never be called on an ExtrusionEntityCollection.
|
// Following methods shall never be called on an ExtrusionEntityCollection.
|
||||||
Polyline as_polyline() const {
|
Polyline as_polyline() const {
|
||||||
|
@ -764,7 +764,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
|
|||||||
}
|
}
|
||||||
// Extrude the layers.
|
// Extrude the layers.
|
||||||
for (auto &layer : layers_to_print) {
|
for (auto &layer : layers_to_print) {
|
||||||
const ToolOrdering::LayerTools &layer_tools = tool_ordering.tools_for_layer(layer.first);
|
const LayerTools &layer_tools = tool_ordering.tools_for_layer(layer.first);
|
||||||
if (m_wipe_tower && layer_tools.has_wipe_tower)
|
if (m_wipe_tower && layer_tools.has_wipe_tower)
|
||||||
m_wipe_tower->next_layer();
|
m_wipe_tower->next_layer();
|
||||||
this->process_layer(file, print, layer.second, layer_tools, size_t(-1));
|
this->process_layer(file, print, layer.second, layer_tools, size_t(-1));
|
||||||
@ -1009,7 +1009,7 @@ void GCode::process_layer(
|
|||||||
const Print &print,
|
const Print &print,
|
||||||
// Set of object & print layers of the same PrintObject and with the same print_z.
|
// Set of object & print layers of the same PrintObject and with the same print_z.
|
||||||
const std::vector<LayerToPrint> &layers,
|
const std::vector<LayerToPrint> &layers,
|
||||||
const ToolOrdering::LayerTools &layer_tools,
|
const LayerTools &layer_tools,
|
||||||
// If set to size_t(-1), then print all copies of all objects.
|
// If set to size_t(-1), then print all copies of all objects.
|
||||||
// Otherwise print a single copy of a single object.
|
// Otherwise print a single copy of a single object.
|
||||||
const size_t single_object_idx)
|
const size_t single_object_idx)
|
||||||
@ -1147,7 +1147,6 @@ void GCode::process_layer(
|
|||||||
|
|
||||||
// Group extrusions by an extruder, then by an object, an island and a region.
|
// Group extrusions by an extruder, then by an object, an island and a region.
|
||||||
std::map<unsigned int, std::vector<ObjectByExtruder>> by_extruder;
|
std::map<unsigned int, std::vector<ObjectByExtruder>> by_extruder;
|
||||||
|
|
||||||
for (const LayerToPrint &layer_to_print : layers) {
|
for (const LayerToPrint &layer_to_print : layers) {
|
||||||
if (layer_to_print.support_layer != nullptr) {
|
if (layer_to_print.support_layer != nullptr) {
|
||||||
const SupportLayer &support_layer = *layer_to_print.support_layer;
|
const SupportLayer &support_layer = *layer_to_print.support_layer;
|
||||||
@ -1224,70 +1223,66 @@ void GCode::process_layer(
|
|||||||
if (layerm == nullptr)
|
if (layerm == nullptr)
|
||||||
continue;
|
continue;
|
||||||
const PrintRegion ®ion = *print.regions[region_id];
|
const PrintRegion ®ion = *print.regions[region_id];
|
||||||
|
|
||||||
// process perimeters
|
|
||||||
for (const ExtrusionEntity *ee : layerm->perimeters.entities) {
|
// Now we must process perimeters and infills and create islands of extrusions in by_region std::map.
|
||||||
// perimeter_coll represents perimeter extrusions of a single island.
|
// It is also necessary to save which extrusions are part of MM wiping and which are not.
|
||||||
const auto *perimeter_coll = dynamic_cast<const ExtrusionEntityCollection*>(ee);
|
// The process is almost the same for perimeters and infills - we will do it in a cycle that repeats twice:
|
||||||
if (perimeter_coll->entities.empty())
|
for (std::string entity_type("infills") ; entity_type != "done" ; entity_type = entity_type=="infills" ? "perimeters" : "done") {
|
||||||
// This shouldn't happen but first_point() would fail.
|
|
||||||
continue;
|
const ExtrusionEntitiesPtr& source_entities = entity_type=="infills" ? layerm->fills.entities : layerm->perimeters.entities;
|
||||||
// Init by_extruder item only if we actually use the extruder.
|
|
||||||
std::vector<ObjectByExtruder::Island> &islands = object_islands_by_extruder(
|
for (const ExtrusionEntity *ee : source_entities) {
|
||||||
by_extruder,
|
// fill represents infill extrusions of a single island.
|
||||||
std::max<int>(region.config.perimeter_extruder.value - 1, 0),
|
const auto *fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
|
||||||
&layer_to_print - layers.data(),
|
if (fill->entities.empty()) // This shouldn't happen but first_point() would fail.
|
||||||
layers.size(), n_slices+1);
|
continue;
|
||||||
for (size_t i = 0; i <= n_slices; ++ i)
|
|
||||||
if (// perimeter_coll->first_point does not fit inside any slice
|
// This extrusion is part of certain Region, which tells us which extruder should be used for it:
|
||||||
i == n_slices ||
|
int correct_extruder_id = Print::get_extruder(*fill, region); entity_type=="infills" ? std::max<int>(0, (is_solid_infill(fill->entities.front()->role()) ? region.config.solid_infill_extruder : region.config.infill_extruder) - 1) :
|
||||||
// perimeter_coll->first_point fits inside ith slice
|
std::max<int>(region.config.perimeter_extruder.value - 1, 0);
|
||||||
point_inside_surface(i, perimeter_coll->first_point())) {
|
|
||||||
if (islands[i].by_region.empty())
|
// Let's recover vector of extruder overrides:
|
||||||
islands[i].by_region.assign(print.regions.size(), ObjectByExtruder::Island::Region());
|
const ExtruderPerCopy* entity_overrides = const_cast<LayerTools&>(layer_tools).wiping_extrusions().get_extruder_overrides(fill, correct_extruder_id, layer_to_print.object()->_shifted_copies.size());
|
||||||
islands[i].by_region[region_id].perimeters.append(perimeter_coll->entities);
|
|
||||||
break;
|
// Now we must add this extrusion into the by_extruder map, once for each extruder that will print it:
|
||||||
}
|
for (unsigned int extruder : layer_tools.extruders)
|
||||||
}
|
{
|
||||||
|
// Init by_extruder item only if we actually use the extruder:
|
||||||
// process infill
|
if (std::find(entity_overrides->begin(), entity_overrides->end(), extruder) != entity_overrides->end() || // at least one copy is overridden to use this extruder
|
||||||
// layerm->fills is a collection of Slic3r::ExtrusionPath::Collection objects (C++ class ExtrusionEntityCollection),
|
std::find(entity_overrides->begin(), entity_overrides->end(), -extruder-1) != entity_overrides->end() || // at least one copy would normally be printed with this extruder (see get_extruder_overrides function for explanation)
|
||||||
// each one containing the ExtrusionPath objects of a certain infill "group" (also called "surface"
|
(std::find(layer_tools.extruders.begin(), layer_tools.extruders.end(), correct_extruder_id) == layer_tools.extruders.end() && extruder == layer_tools.extruders.back())) // this entity is not overridden, but its extruder is not in layer_tools - we'll print it
|
||||||
// throughout the code). We can redefine the order of such Collections but we have to
|
//by last extruder on this layer (could happen e.g. when a wiping object is taller than others - dontcare extruders are eradicated from layer_tools)
|
||||||
// do each one completely at once.
|
{
|
||||||
for (const ExtrusionEntity *ee : layerm->fills.entities) {
|
std::vector<ObjectByExtruder::Island> &islands = object_islands_by_extruder(
|
||||||
// fill represents infill extrusions of a single island.
|
by_extruder,
|
||||||
const auto *fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
|
extruder,
|
||||||
if (fill->entities.empty())
|
&layer_to_print - layers.data(),
|
||||||
// This shouldn't happen but first_point() would fail.
|
layers.size(), n_slices+1);
|
||||||
continue;
|
for (size_t i = 0; i <= n_slices; ++i)
|
||||||
// init by_extruder item only if we actually use the extruder
|
if (// fill->first_point does not fit inside any slice
|
||||||
int extruder_id = std::max<int>(0, (is_solid_infill(fill->entities.front()->role()) ? region.config.solid_infill_extruder : region.config.infill_extruder) - 1);
|
i == n_slices ||
|
||||||
// Init by_extruder item only if we actually use the extruder.
|
// fill->first_point fits inside ith slice
|
||||||
std::vector<ObjectByExtruder::Island> &islands = object_islands_by_extruder(
|
point_inside_surface(i, fill->first_point())) {
|
||||||
by_extruder,
|
if (islands[i].by_region.empty())
|
||||||
extruder_id,
|
islands[i].by_region.assign(print.regions.size(), ObjectByExtruder::Island::Region());
|
||||||
&layer_to_print - layers.data(),
|
islands[i].by_region[region_id].append(entity_type, fill, entity_overrides, layer_to_print.object()->_shifted_copies.size());
|
||||||
layers.size(), n_slices+1);
|
break;
|
||||||
for (size_t i = 0; i <= n_slices; ++i)
|
}
|
||||||
if (// fill->first_point does not fit inside any slice
|
}
|
||||||
i == n_slices ||
|
|
||||||
// fill->first_point fits inside ith slice
|
|
||||||
point_inside_surface(i, fill->first_point())) {
|
|
||||||
if (islands[i].by_region.empty())
|
|
||||||
islands[i].by_region.assign(print.regions.size(), ObjectByExtruder::Island::Region());
|
|
||||||
islands[i].by_region[region_id].infills.append(fill->entities);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} // for regions
|
} // for regions
|
||||||
}
|
}
|
||||||
} // for objects
|
} // for objects
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Extrude the skirt, brim, support, perimeters, infill ordered by the extruders.
|
// Extrude the skirt, brim, support, perimeters, infill ordered by the extruders.
|
||||||
std::vector<std::unique_ptr<EdgeGrid::Grid>> lower_layer_edge_grids(layers.size());
|
std::vector<std::unique_ptr<EdgeGrid::Grid>> lower_layer_edge_grids(layers.size());
|
||||||
for (unsigned int extruder_id : layer_tools.extruders)
|
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()) :
|
m_wipe_tower->tool_change(*this, extruder_id, extruder_id == layer_tools.extruders.back()) :
|
||||||
this->set_extruder(extruder_id);
|
this->set_extruder(extruder_id);
|
||||||
@ -1312,7 +1307,7 @@ void GCode::process_layer(
|
|||||||
for (ExtrusionPath &path : loop.paths) {
|
for (ExtrusionPath &path : loop.paths) {
|
||||||
path.height = (float)layer.height;
|
path.height = (float)layer.height;
|
||||||
path.mm3_per_mm = mm3_per_mm;
|
path.mm3_per_mm = mm3_per_mm;
|
||||||
}
|
}
|
||||||
gcode += this->extrude_loop(loop, "skirt", m_config.support_material_speed.value);
|
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;
|
||||||
@ -1321,7 +1316,7 @@ void GCode::process_layer(
|
|||||||
m_avoid_crossing_perimeters.disable_once = true;
|
m_avoid_crossing_perimeters.disable_once = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extrude brim with the extruder of the 1st region.
|
// Extrude brim with the extruder of the 1st region.
|
||||||
if (! m_brim_done) {
|
if (! m_brim_done) {
|
||||||
this->set_origin(0., 0.);
|
this->set_origin(0., 0.);
|
||||||
@ -1334,49 +1329,61 @@ void GCode::process_layer(
|
|||||||
m_avoid_crossing_perimeters.disable_once = true;
|
m_avoid_crossing_perimeters.disable_once = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
auto objects_by_extruder_it = by_extruder.find(extruder_id);
|
auto objects_by_extruder_it = by_extruder.find(extruder_id);
|
||||||
if (objects_by_extruder_it == by_extruder.end())
|
if (objects_by_extruder_it == by_extruder.end())
|
||||||
continue;
|
continue;
|
||||||
for (const ObjectByExtruder &object_by_extruder : objects_by_extruder_it->second) {
|
|
||||||
const size_t layer_id = &object_by_extruder - objects_by_extruder_it->second.data();
|
|
||||||
const PrintObject *print_object = layers[layer_id].object();
|
|
||||||
if (print_object == nullptr)
|
|
||||||
// This layer is empty for this particular object, it has neither object extrusions nor support extrusions at this print_z.
|
|
||||||
continue;
|
|
||||||
|
|
||||||
m_config.apply(print_object->config, true);
|
// We are almost ready to print. However, we must go through all the objects twice to print the the overridden extrusions first (infill/perimeter wiping feature):
|
||||||
m_layer = layers[layer_id].layer();
|
for (int print_wipe_extrusions=const_cast<LayerTools&>(layer_tools).wiping_extrusions().is_anything_overridden(); print_wipe_extrusions>=0; --print_wipe_extrusions) {
|
||||||
if (m_config.avoid_crossing_perimeters)
|
if (print_wipe_extrusions == 0)
|
||||||
m_avoid_crossing_perimeters.init_layer_mp(union_ex(m_layer->slices, true));
|
gcode+="; PURGING FINISHED\n";
|
||||||
Points copies;
|
|
||||||
if (single_object_idx == size_t(-1))
|
for (ObjectByExtruder &object_by_extruder : objects_by_extruder_it->second) {
|
||||||
copies = print_object->_shifted_copies;
|
const size_t layer_id = &object_by_extruder - objects_by_extruder_it->second.data();
|
||||||
else
|
const PrintObject *print_object = layers[layer_id].object();
|
||||||
copies.push_back(print_object->_shifted_copies[single_object_idx]);
|
if (print_object == nullptr)
|
||||||
// Sort the copies by the closest point starting with the current print position.
|
// This layer is empty for this particular object, it has neither object extrusions nor support extrusions at this print_z.
|
||||||
|
continue;
|
||||||
for (const Point © : copies) {
|
|
||||||
// When starting a new object, use the external motion planner for the first travel move.
|
m_config.apply(print_object->config, true);
|
||||||
std::pair<const PrintObject*, Point> this_object_copy(print_object, copy);
|
m_layer = layers[layer_id].layer();
|
||||||
if (m_last_obj_copy != this_object_copy)
|
if (m_config.avoid_crossing_perimeters)
|
||||||
m_avoid_crossing_perimeters.use_external_mp_once = true;
|
m_avoid_crossing_perimeters.init_layer_mp(union_ex(m_layer->slices, true));
|
||||||
m_last_obj_copy = this_object_copy;
|
Points copies;
|
||||||
this->set_origin(unscale(copy.x), unscale(copy.y));
|
if (single_object_idx == size_t(-1))
|
||||||
if (object_by_extruder.support != nullptr) {
|
copies = print_object->_shifted_copies;
|
||||||
m_layer = layers[layer_id].support_layer;
|
else
|
||||||
gcode += this->extrude_support(
|
copies.push_back(print_object->_shifted_copies[single_object_idx]);
|
||||||
// support_extrusion_role is erSupportMaterial, erSupportMaterialInterface or erMixed for all extrusion paths.
|
// Sort the copies by the closest point starting with the current print position.
|
||||||
object_by_extruder.support->chained_path_from(m_last_pos, false, object_by_extruder.support_extrusion_role));
|
|
||||||
m_layer = layers[layer_id].layer();
|
unsigned int copy_id = 0;
|
||||||
}
|
for (const Point © : copies) {
|
||||||
for (const ObjectByExtruder::Island &island : object_by_extruder.islands) {
|
// When starting a new object, use the external motion planner for the first travel move.
|
||||||
if (print.config.infill_first) {
|
std::pair<const PrintObject*, Point> this_object_copy(print_object, copy);
|
||||||
gcode += this->extrude_infill(print, island.by_region);
|
if (m_last_obj_copy != this_object_copy)
|
||||||
gcode += this->extrude_perimeters(print, island.by_region, lower_layer_edge_grids[layer_id]);
|
m_avoid_crossing_perimeters.use_external_mp_once = true;
|
||||||
} else {
|
m_last_obj_copy = this_object_copy;
|
||||||
gcode += this->extrude_perimeters(print, island.by_region, lower_layer_edge_grids[layer_id]);
|
this->set_origin(unscale(copy.x), unscale(copy.y));
|
||||||
gcode += this->extrude_infill(print, island.by_region);
|
if (object_by_extruder.support != nullptr && !print_wipe_extrusions) {
|
||||||
|
m_layer = layers[layer_id].support_layer;
|
||||||
|
gcode += this->extrude_support(
|
||||||
|
// support_extrusion_role is erSupportMaterial, erSupportMaterialInterface or erMixed for all extrusion paths.
|
||||||
|
object_by_extruder.support->chained_path_from(m_last_pos, false, object_by_extruder.support_extrusion_role));
|
||||||
|
m_layer = layers[layer_id].layer();
|
||||||
}
|
}
|
||||||
|
for (ObjectByExtruder::Island &island : object_by_extruder.islands) {
|
||||||
|
const auto& by_region_specific = const_cast<LayerTools&>(layer_tools).wiping_extrusions().is_anything_overridden() ? island.by_region_per_copy(copy_id, extruder_id, print_wipe_extrusions) : island.by_region;
|
||||||
|
|
||||||
|
if (print.config.infill_first) {
|
||||||
|
gcode += this->extrude_infill(print, by_region_specific);
|
||||||
|
gcode += this->extrude_perimeters(print, by_region_specific, lower_layer_edge_grids[layer_id]);
|
||||||
|
} else {
|
||||||
|
gcode += this->extrude_perimeters(print, by_region_specific, lower_layer_edge_grids[layer_id]);
|
||||||
|
gcode += this->extrude_infill(print,by_region_specific);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++copy_id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2445,4 +2452,62 @@ Point GCode::gcode_to_point(const Pointf &point) const
|
|||||||
scale_(point.y - m_origin.y + extruder_offset.y));
|
scale_(point.y - m_origin.y + extruder_offset.y));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Goes through by_region std::vector and returns reference to a subvector of entities, that are to be printed
|
||||||
|
// during infill/perimeter wiping, or normally (depends on wiping_entities parameter)
|
||||||
|
// Returns a reference to member to avoid copying.
|
||||||
|
const std::vector<GCode::ObjectByExtruder::Island::Region>& GCode::ObjectByExtruder::Island::by_region_per_copy(unsigned int copy, int extruder, bool wiping_entities)
|
||||||
|
{
|
||||||
|
by_region_per_copy_cache.clear();
|
||||||
|
|
||||||
|
for (const auto& reg : by_region) {
|
||||||
|
by_region_per_copy_cache.push_back(ObjectByExtruder::Island::Region()); // creates a region in the newly created Island
|
||||||
|
|
||||||
|
// Now we are going to iterate through perimeters and infills and pick ones that are supposed to be printed
|
||||||
|
// References are used so that we don't have to repeat the same code
|
||||||
|
for (int iter = 0; iter < 2; ++iter) {
|
||||||
|
const ExtrusionEntitiesPtr& entities = (iter ? reg.infills.entities : reg.perimeters.entities);
|
||||||
|
ExtrusionEntityCollection& target_eec = (iter ? by_region_per_copy_cache.back().infills : by_region_per_copy_cache.back().perimeters);
|
||||||
|
const std::vector<const ExtruderPerCopy*>& overrides = (iter ? reg.infills_overrides : reg.perimeters_overrides);
|
||||||
|
|
||||||
|
// Now the most important thing - which extrusion should we print.
|
||||||
|
// See function ToolOrdering::get_extruder_overrides for details about the negative numbers hack.
|
||||||
|
int this_extruder_mark = wiping_entities ? extruder : -extruder-1;
|
||||||
|
|
||||||
|
for (unsigned int i=0;i<entities.size();++i)
|
||||||
|
if (overrides[i]->at(copy) == this_extruder_mark) // this copy should be printed with this extruder
|
||||||
|
target_eec.append((*entities[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return by_region_per_copy_cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// This function takes the eec and appends its entities to either perimeters or infills of this Region (depending on the first parameter)
|
||||||
|
// It also saves pointer to ExtruderPerCopy struct (for each entity), that holds information about which extruders should be used for which copy.
|
||||||
|
void GCode::ObjectByExtruder::Island::Region::append(const std::string& type, const ExtrusionEntityCollection* eec, const ExtruderPerCopy* copies_extruder, unsigned int object_copies_num)
|
||||||
|
{
|
||||||
|
// We are going to manipulate either perimeters or infills, exactly in the same way. Let's create pointers to the proper structure to not repeat ourselves:
|
||||||
|
ExtrusionEntityCollection* perimeters_or_infills = &infills;
|
||||||
|
std::vector<const ExtruderPerCopy*>* perimeters_or_infills_overrides = &infills_overrides;
|
||||||
|
|
||||||
|
if (type == "perimeters") {
|
||||||
|
perimeters_or_infills = &perimeters;
|
||||||
|
perimeters_or_infills_overrides = &perimeters_overrides;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (type != "infills") {
|
||||||
|
CONFESS("Unknown parameter!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// First we append the entities, there are eec->entities.size() of them:
|
||||||
|
perimeters_or_infills->append(eec->entities);
|
||||||
|
|
||||||
|
for (unsigned int i=0;i<eec->entities.size();++i)
|
||||||
|
perimeters_or_infills_overrides->push_back(copies_extruder);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Slic3r
|
||||||
|
@ -185,7 +185,7 @@ protected:
|
|||||||
const Print &print,
|
const Print &print,
|
||||||
// Set of object & print layers of the same PrintObject and with the same print_z.
|
// Set of object & print layers of the same PrintObject and with the same print_z.
|
||||||
const std::vector<LayerToPrint> &layers,
|
const std::vector<LayerToPrint> &layers,
|
||||||
const ToolOrdering::LayerTools &layer_tools,
|
const LayerTools &layer_tools,
|
||||||
// If set to size_t(-1), then print all copies of all objects.
|
// If set to size_t(-1), then print all copies of all objects.
|
||||||
// Otherwise print a single copy of a single object.
|
// Otherwise print a single copy of a single object.
|
||||||
const size_t single_object_idx = size_t(-1));
|
const size_t single_object_idx = size_t(-1));
|
||||||
@ -200,6 +200,7 @@ protected:
|
|||||||
std::string extrude_multi_path(ExtrusionMultiPath multipath, std::string description = "", double speed = -1.);
|
std::string extrude_multi_path(ExtrusionMultiPath multipath, std::string description = "", double speed = -1.);
|
||||||
std::string extrude_path(ExtrusionPath path, std::string description = "", double speed = -1.);
|
std::string extrude_path(ExtrusionPath path, std::string description = "", double speed = -1.);
|
||||||
|
|
||||||
|
typedef std::vector<int> ExtruderPerCopy;
|
||||||
// Extruding multiple objects with soluble / non-soluble / combined supports
|
// Extruding multiple objects with soluble / non-soluble / combined supports
|
||||||
// on a multi-material printer, trying to minimize tool switches.
|
// on a multi-material printer, trying to minimize tool switches.
|
||||||
// Following structures sort extrusions by the extruder ID, by an order of objects and object islands.
|
// Following structures sort extrusions by the extruder ID, by an order of objects and object islands.
|
||||||
@ -215,11 +216,24 @@ protected:
|
|||||||
struct Region {
|
struct Region {
|
||||||
ExtrusionEntityCollection perimeters;
|
ExtrusionEntityCollection perimeters;
|
||||||
ExtrusionEntityCollection infills;
|
ExtrusionEntityCollection infills;
|
||||||
|
|
||||||
|
std::vector<const ExtruderPerCopy*> infills_overrides;
|
||||||
|
std::vector<const ExtruderPerCopy*> perimeters_overrides;
|
||||||
|
|
||||||
|
// Appends perimeter/infill entities and writes don't indices of those that are not to be extruder as part of perimeter/infill wiping
|
||||||
|
void append(const std::string& type, const ExtrusionEntityCollection* eec, const ExtruderPerCopy* copy_extruders, unsigned int object_copies_num);
|
||||||
};
|
};
|
||||||
std::vector<Region> by_region;
|
|
||||||
|
std::vector<Region> by_region; // all extrusions for this island, grouped by regions
|
||||||
|
const std::vector<Region>& by_region_per_copy(unsigned int copy, int extruder, bool wiping_entities = false); // returns reference to subvector of by_region
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<Region> by_region_per_copy_cache; // caches vector generated by function above to avoid copying and recalculating
|
||||||
};
|
};
|
||||||
std::vector<Island> islands;
|
std::vector<Island> islands;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
std::string extrude_perimeters(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region, std::unique_ptr<EdgeGrid::Grid> &lower_layer_edge_grid);
|
std::string extrude_perimeters(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region, std::unique_ptr<EdgeGrid::Grid> &lower_layer_edge_grid);
|
||||||
std::string extrude_infill(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region);
|
std::string extrude_infill(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region);
|
||||||
std::string extrude_support(const ExtrusionEntityCollection &support_fills);
|
std::string extrude_support(const ExtrusionEntityCollection &support_fills);
|
||||||
|
@ -15,6 +15,24 @@
|
|||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
|
||||||
|
// Returns true in case that extruder a comes before b (b does not have to be present). False otherwise.
|
||||||
|
bool LayerTools::is_extruder_order(unsigned int a, unsigned int b) const
|
||||||
|
{
|
||||||
|
if (a==b)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (auto extruder : extruders) {
|
||||||
|
if (extruder == a)
|
||||||
|
return true;
|
||||||
|
if (extruder == b)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// For the use case when each object is printed separately
|
// For the use case when each object is printed separately
|
||||||
// (print.config.complete_objects is true).
|
// (print.config.complete_objects is true).
|
||||||
ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extruder, bool prime_multi_material)
|
ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extruder, bool prime_multi_material)
|
||||||
@ -48,6 +66,7 @@ ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extrude
|
|||||||
// (print.config.complete_objects is false).
|
// (print.config.complete_objects is false).
|
||||||
ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool prime_multi_material)
|
ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool prime_multi_material)
|
||||||
{
|
{
|
||||||
|
m_print_config_ptr = &print.config;
|
||||||
// Initialize the print layers for all objects and all layers.
|
// Initialize the print layers for all objects and all layers.
|
||||||
coordf_t object_bottom_z = 0.;
|
coordf_t object_bottom_z = 0.;
|
||||||
{
|
{
|
||||||
@ -76,9 +95,10 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool
|
|||||||
this->collect_extruder_statistics(prime_multi_material);
|
this->collect_extruder_statistics(prime_multi_material);
|
||||||
}
|
}
|
||||||
|
|
||||||
ToolOrdering::LayerTools& ToolOrdering::tools_for_layer(coordf_t print_z)
|
|
||||||
|
LayerTools& ToolOrdering::tools_for_layer(coordf_t print_z)
|
||||||
{
|
{
|
||||||
auto it_layer_tools = std::lower_bound(m_layer_tools.begin(), m_layer_tools.end(), ToolOrdering::LayerTools(print_z - EPSILON));
|
auto it_layer_tools = std::lower_bound(m_layer_tools.begin(), m_layer_tools.end(), LayerTools(print_z - EPSILON));
|
||||||
assert(it_layer_tools != m_layer_tools.end());
|
assert(it_layer_tools != m_layer_tools.end());
|
||||||
coordf_t dist_min = std::abs(it_layer_tools->print_z - print_z);
|
coordf_t dist_min = std::abs(it_layer_tools->print_z - print_z);
|
||||||
for (++ it_layer_tools; it_layer_tools != m_layer_tools.end(); ++it_layer_tools) {
|
for (++ it_layer_tools; it_layer_tools != m_layer_tools.end(); ++it_layer_tools) {
|
||||||
@ -102,7 +122,7 @@ void ToolOrdering::initialize_layers(std::vector<coordf_t> &zs)
|
|||||||
coordf_t zmax = zs[i] + EPSILON;
|
coordf_t zmax = zs[i] + EPSILON;
|
||||||
for (; j < zs.size() && zs[j] <= zmax; ++ j) ;
|
for (; j < zs.size() && zs[j] <= zmax; ++ j) ;
|
||||||
// Assign an average print_z to the set of layers with nearly equal print_z.
|
// Assign an average print_z to the set of layers with nearly equal print_z.
|
||||||
m_layer_tools.emplace_back(LayerTools(0.5 * (zs[i] + zs[j-1])));
|
m_layer_tools.emplace_back(LayerTools(0.5 * (zs[i] + zs[j-1]), m_print_config_ptr));
|
||||||
i = j;
|
i = j;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -134,12 +154,29 @@ void ToolOrdering::collect_extruders(const PrintObject &object)
|
|||||||
if (layerm == nullptr)
|
if (layerm == nullptr)
|
||||||
continue;
|
continue;
|
||||||
const PrintRegion ®ion = *object.print()->regions[region_id];
|
const PrintRegion ®ion = *object.print()->regions[region_id];
|
||||||
|
|
||||||
if (! layerm->perimeters.entities.empty()) {
|
if (! layerm->perimeters.entities.empty()) {
|
||||||
layer_tools.extruders.push_back(region.config.perimeter_extruder.value);
|
bool something_nonoverriddable = true;
|
||||||
|
|
||||||
|
if (m_print_config_ptr) { // in this case complete_objects is false (see ToolOrdering constructors)
|
||||||
|
something_nonoverriddable = false;
|
||||||
|
for (const auto& eec : layerm->perimeters.entities) // let's check if there are nonoverriddable entities
|
||||||
|
if (!layer_tools.wiping_extrusions().is_overriddable(dynamic_cast<const ExtrusionEntityCollection&>(*eec), *m_print_config_ptr, object, region)) {
|
||||||
|
something_nonoverriddable = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (something_nonoverriddable)
|
||||||
|
layer_tools.extruders.push_back(region.config.perimeter_extruder.value);
|
||||||
|
|
||||||
layer_tools.has_object = true;
|
layer_tools.has_object = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool has_infill = false;
|
bool has_infill = false;
|
||||||
bool has_solid_infill = false;
|
bool has_solid_infill = false;
|
||||||
|
bool something_nonoverriddable = false;
|
||||||
for (const ExtrusionEntity *ee : layerm->fills.entities) {
|
for (const ExtrusionEntity *ee : layerm->fills.entities) {
|
||||||
// fill represents infill extrusions of a single island.
|
// fill represents infill extrusions of a single island.
|
||||||
const auto *fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
|
const auto *fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
|
||||||
@ -148,19 +185,33 @@ void ToolOrdering::collect_extruders(const PrintObject &object)
|
|||||||
has_solid_infill = true;
|
has_solid_infill = true;
|
||||||
else if (role != erNone)
|
else if (role != erNone)
|
||||||
has_infill = true;
|
has_infill = true;
|
||||||
|
|
||||||
|
if (m_print_config_ptr) {
|
||||||
|
if (!something_nonoverriddable && !layer_tools.wiping_extrusions().is_overriddable(*fill, *m_print_config_ptr, object, region))
|
||||||
|
something_nonoverriddable = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (something_nonoverriddable || !m_print_config_ptr)
|
||||||
|
{
|
||||||
|
if (has_solid_infill)
|
||||||
|
layer_tools.extruders.push_back(region.config.solid_infill_extruder);
|
||||||
|
if (has_infill)
|
||||||
|
layer_tools.extruders.push_back(region.config.infill_extruder);
|
||||||
}
|
}
|
||||||
if (has_solid_infill)
|
|
||||||
layer_tools.extruders.push_back(region.config.solid_infill_extruder);
|
|
||||||
if (has_infill)
|
|
||||||
layer_tools.extruders.push_back(region.config.infill_extruder);
|
|
||||||
if (has_solid_infill || has_infill)
|
if (has_solid_infill || has_infill)
|
||||||
layer_tools.has_object = true;
|
layer_tools.has_object = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort and remove duplicates
|
for (auto& layer : m_layer_tools) {
|
||||||
for (LayerTools < : m_layer_tools)
|
// Sort and remove duplicates
|
||||||
sort_remove_duplicates(lt.extruders);
|
sort_remove_duplicates(layer.extruders);
|
||||||
|
|
||||||
|
// make sure that there are some tools for each object layer (e.g. tall wiping object will result in empty extruders vector)
|
||||||
|
if (layer.extruders.empty() && layer.has_object)
|
||||||
|
layer.extruders.push_back(0); // 0="dontcare" extruder - it will be taken care of in reorder_extruders
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reorder extruders to minimize layer changes.
|
// Reorder extruders to minimize layer changes.
|
||||||
@ -217,6 +268,8 @@ void ToolOrdering::reorder_extruders(unsigned int last_extruder_id)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z)
|
void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z)
|
||||||
{
|
{
|
||||||
if (m_layer_tools.empty())
|
if (m_layer_tools.empty())
|
||||||
@ -327,4 +380,250 @@ void ToolOrdering::collect_extruder_statistics(bool prime_multi_material)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// This function is called from Print::mark_wiping_extrusions and sets extruder this entity should be printed with (-1 .. as usual)
|
||||||
|
void WipingExtrusions::set_extruder_override(const ExtrusionEntity* entity, unsigned int copy_id, int extruder, unsigned int num_of_copies)
|
||||||
|
{
|
||||||
|
something_overridden = true;
|
||||||
|
|
||||||
|
auto entity_map_it = (entity_map.insert(std::make_pair(entity, std::vector<int>()))).first; // (add and) return iterator
|
||||||
|
auto& copies_vector = entity_map_it->second;
|
||||||
|
if (copies_vector.size() < num_of_copies)
|
||||||
|
copies_vector.resize(num_of_copies, -1);
|
||||||
|
|
||||||
|
if (copies_vector[copy_id] != -1)
|
||||||
|
std::cout << "ERROR: Entity extruder overriden multiple times!!!\n"; // A debugging message - this must never happen.
|
||||||
|
|
||||||
|
copies_vector[copy_id] = extruder;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Finds first non-soluble extruder on the layer
|
||||||
|
int WipingExtrusions::first_nonsoluble_extruder_on_layer(const PrintConfig& print_config) const
|
||||||
|
{
|
||||||
|
const LayerTools& lt = *m_layer_tools;
|
||||||
|
for (auto extruders_it = lt.extruders.begin(); extruders_it != lt.extruders.end(); ++extruders_it)
|
||||||
|
if (!print_config.filament_soluble.get_at(*extruders_it))
|
||||||
|
return (*extruders_it);
|
||||||
|
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finds last non-soluble extruder on the layer
|
||||||
|
int WipingExtrusions::last_nonsoluble_extruder_on_layer(const PrintConfig& print_config) const
|
||||||
|
{
|
||||||
|
const LayerTools& lt = *m_layer_tools;
|
||||||
|
for (auto extruders_it = lt.extruders.rbegin(); extruders_it != lt.extruders.rend(); ++extruders_it)
|
||||||
|
if (!print_config.filament_soluble.get_at(*extruders_it))
|
||||||
|
return (*extruders_it);
|
||||||
|
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Decides whether this entity could be overridden
|
||||||
|
bool WipingExtrusions::is_overriddable(const ExtrusionEntityCollection& eec, const PrintConfig& print_config, const PrintObject& object, const PrintRegion& region) const
|
||||||
|
{
|
||||||
|
if (print_config.filament_soluble.get_at(Print::get_extruder(eec, region)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (object.config.wipe_into_objects)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (!region.config.wipe_into_infill || eec.role() != erInternalInfill)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Following function iterates through all extrusions on the layer, remembers those that could be used for wiping after toolchange
|
||||||
|
// and returns volume that is left to be wiped on the wipe tower.
|
||||||
|
float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int new_extruder, float volume_to_wipe)
|
||||||
|
{
|
||||||
|
const LayerTools& lt = *m_layer_tools;
|
||||||
|
const float min_infill_volume = 0.f; // ignore infill with smaller volume than this
|
||||||
|
|
||||||
|
if (print.config.filament_soluble.get_at(new_extruder))
|
||||||
|
return volume_to_wipe; // Soluble filament cannot be wiped in a random infill
|
||||||
|
|
||||||
|
// we will sort objects so that dedicated for wiping are at the beginning:
|
||||||
|
PrintObjectPtrs object_list = print.objects;
|
||||||
|
std::sort(object_list.begin(), object_list.end(), [](const PrintObject* a, const PrintObject* b) { return a->config.wipe_into_objects; });
|
||||||
|
|
||||||
|
|
||||||
|
// We will now iterate through
|
||||||
|
// - first the dedicated objects to mark perimeters or infills (depending on infill_first)
|
||||||
|
// - second through the dedicated ones again to mark infills or perimeters (depending on infill_first)
|
||||||
|
// - then all the others to mark infills (in case that !infill_first, we must also check that the perimeter is finished already
|
||||||
|
// this is controlled by the following variable:
|
||||||
|
bool perimeters_done = false;
|
||||||
|
|
||||||
|
for (int i=0 ; i<(int)object_list.size() + (perimeters_done ? 0 : 1); ++i) {
|
||||||
|
if (!perimeters_done && (i==(int)object_list.size() || !object_list[i]->config.wipe_into_objects)) { // we passed the last dedicated object in list
|
||||||
|
perimeters_done = true;
|
||||||
|
i=-1; // let's go from the start again
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& object = object_list[i];
|
||||||
|
|
||||||
|
// Finds this layer:
|
||||||
|
auto this_layer_it = std::find_if(object->layers.begin(), object->layers.end(), [<](const Layer* lay) { return std::abs(lt.print_z - lay->print_z)<EPSILON; });
|
||||||
|
if (this_layer_it == object->layers.end())
|
||||||
|
continue;
|
||||||
|
const Layer* this_layer = *this_layer_it;
|
||||||
|
unsigned int num_of_copies = object->_shifted_copies.size();
|
||||||
|
|
||||||
|
for (unsigned int copy = 0; copy < num_of_copies; ++copy) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
|
||||||
|
|
||||||
|
for (size_t region_id = 0; region_id < object->print()->regions.size(); ++ region_id) {
|
||||||
|
const auto& region = *object->print()->regions[region_id];
|
||||||
|
|
||||||
|
if (!region.config.wipe_into_infill && !object->config.wipe_into_objects)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
|
||||||
|
if ((!print.config.infill_first ? perimeters_done : !perimeters_done) || (!object->config.wipe_into_objects && region.config.wipe_into_infill)) {
|
||||||
|
for (const ExtrusionEntity* ee : this_layer->regions[region_id]->fills.entities) { // iterate through all infill Collections
|
||||||
|
auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
|
||||||
|
|
||||||
|
if (!is_overriddable(*fill, print.config, *object, region))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// What extruder would this normally be printed with?
|
||||||
|
unsigned int correct_extruder = Print::get_extruder(*fill, region);
|
||||||
|
|
||||||
|
if (volume_to_wipe<=0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!object->config.wipe_into_objects && !print.config.infill_first && region.config.wipe_into_infill)
|
||||||
|
// In this case we must check that the original extruder is used on this layer before the one we are overridding
|
||||||
|
// (and the perimeters will be finished before the infill is printed):
|
||||||
|
if (!lt.is_extruder_order(region.config.perimeter_extruder - 1, new_extruder))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if ((!is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume)) { // this infill will be used to wipe this extruder
|
||||||
|
set_extruder_override(fill, copy, new_extruder, num_of_copies);
|
||||||
|
volume_to_wipe -= fill->total_volume();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now the same for perimeters - see comments above for explanation:
|
||||||
|
if (object->config.wipe_into_objects && (print.config.infill_first ? perimeters_done : !perimeters_done))
|
||||||
|
{
|
||||||
|
for (const ExtrusionEntity* ee : this_layer->regions[region_id]->perimeters.entities) {
|
||||||
|
auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
|
||||||
|
if (!is_overriddable(*fill, print.config, *object, region))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (volume_to_wipe<=0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if ((!is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume)) {
|
||||||
|
set_extruder_override(fill, copy, new_extruder, num_of_copies);
|
||||||
|
volume_to_wipe -= fill->total_volume();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return std::max(0.f, volume_to_wipe);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Called after all toolchanges on a layer were mark_infill_overridden. There might still be overridable entities,
|
||||||
|
// that were not actually overridden. If they are part of a dedicated object, printing them with the extruder
|
||||||
|
// they were initially assigned to might mean violating the perimeter-infill order. We will therefore go through
|
||||||
|
// them again and make sure we override it.
|
||||||
|
void WipingExtrusions::ensure_perimeters_infills_order(const Print& print)
|
||||||
|
{
|
||||||
|
const LayerTools& lt = *m_layer_tools;
|
||||||
|
unsigned int first_nonsoluble_extruder = first_nonsoluble_extruder_on_layer(print.config);
|
||||||
|
unsigned int last_nonsoluble_extruder = last_nonsoluble_extruder_on_layer(print.config);
|
||||||
|
|
||||||
|
for (const PrintObject* object : print.objects) {
|
||||||
|
// Finds this layer:
|
||||||
|
auto this_layer_it = std::find_if(object->layers.begin(), object->layers.end(), [<](const Layer* lay) { return std::abs(lt.print_z - lay->print_z)<EPSILON; });
|
||||||
|
if (this_layer_it == object->layers.end())
|
||||||
|
continue;
|
||||||
|
const Layer* this_layer = *this_layer_it;
|
||||||
|
unsigned int num_of_copies = object->_shifted_copies.size();
|
||||||
|
|
||||||
|
for (unsigned int copy = 0; copy < num_of_copies; ++copy) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
|
||||||
|
for (size_t region_id = 0; region_id < object->print()->regions.size(); ++ region_id) {
|
||||||
|
const auto& region = *object->print()->regions[region_id];
|
||||||
|
|
||||||
|
if (!region.config.wipe_into_infill && !object->config.wipe_into_objects)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (const ExtrusionEntity* ee : this_layer->regions[region_id]->fills.entities) { // iterate through all infill Collections
|
||||||
|
auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
|
||||||
|
|
||||||
|
if (!is_overriddable(*fill, print.config, *object, region)
|
||||||
|
|| is_entity_overridden(fill, copy) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// This infill could have been overridden but was not - unless we do something, it could be
|
||||||
|
// printed before its perimeter, or not be printed at all (in case its original extruder has
|
||||||
|
// not been added to LayerTools
|
||||||
|
// Either way, we will now force-override it with something suitable:
|
||||||
|
if (print.config.infill_first
|
||||||
|
|| object->config.wipe_into_objects // in this case the perimeter is overridden, so we can override by the last one safely
|
||||||
|
|| lt.is_extruder_order(region.config.perimeter_extruder - 1, last_nonsoluble_extruder // !infill_first, but perimeter is already printed when last extruder prints
|
||||||
|
|| std::find(lt.extruders.begin(), lt.extruders.end(), region.config.infill_extruder - 1) == lt.extruders.end()) // we have to force override - this could violate infill_first (FIXME)
|
||||||
|
)
|
||||||
|
set_extruder_override(fill, copy, (print.config.infill_first ? first_nonsoluble_extruder : last_nonsoluble_extruder), num_of_copies);
|
||||||
|
else {
|
||||||
|
// In this case we can (and should) leave it to be printed normally.
|
||||||
|
// Force overriding would mean it gets printed before its perimeter.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now the same for perimeters - see comments above for explanation:
|
||||||
|
for (const ExtrusionEntity* ee : this_layer->regions[region_id]->perimeters.entities) { // iterate through all perimeter Collections
|
||||||
|
auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
|
||||||
|
if (!is_overriddable(*fill, print.config, *object, region)
|
||||||
|
|| is_entity_overridden(fill, copy) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
set_extruder_override(fill, copy, (print.config.infill_first ? last_nonsoluble_extruder : first_nonsoluble_extruder), num_of_copies);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Following function is called from process_layer and returns pointer to vector with information about which extruders should be used for given copy of this entity.
|
||||||
|
// It first makes sure the pointer is valid (creates the vector if it does not exist) and contains a record for each copy
|
||||||
|
// It also modifies the vector in place and changes all -1 to correct_extruder_id (at the time the overrides were created, correct extruders were not known,
|
||||||
|
// so -1 was used as "print as usual".
|
||||||
|
// The resulting vector has to keep track of which extrusions are the ones that were overridden and which were not. In the extruder is used as overridden,
|
||||||
|
// its number is saved as it is (zero-based index). Usual extrusions are saved as -number-1 (unfortunately there is no negative zero).
|
||||||
|
const std::vector<int>* WipingExtrusions::get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, int num_of_copies)
|
||||||
|
{
|
||||||
|
auto entity_map_it = entity_map.find(entity);
|
||||||
|
if (entity_map_it == entity_map.end())
|
||||||
|
entity_map_it = (entity_map.insert(std::make_pair(entity, std::vector<int>()))).first;
|
||||||
|
|
||||||
|
// Now the entity_map_it should be valid, let's make sure the vector is long enough:
|
||||||
|
entity_map_it->second.resize(num_of_copies, -1);
|
||||||
|
|
||||||
|
// Each -1 now means "print as usual" - we will replace it with actual extruder id (shifted it so we don't lose that information):
|
||||||
|
std::replace(entity_map_it->second.begin(), entity_map_it->second.end(), -1, -correct_extruder_id-1);
|
||||||
|
|
||||||
|
return &(entity_map_it->second);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
@ -9,38 +9,97 @@ namespace Slic3r {
|
|||||||
|
|
||||||
class Print;
|
class Print;
|
||||||
class PrintObject;
|
class PrintObject;
|
||||||
|
class LayerTools;
|
||||||
|
|
||||||
class ToolOrdering
|
|
||||||
|
|
||||||
|
// Object of this class holds information about whether an extrusion is printed immediately
|
||||||
|
// after a toolchange (as part of infill/perimeter wiping) or not. One extrusion can be a part
|
||||||
|
// of several copies - this has to be taken into account.
|
||||||
|
class WipingExtrusions
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
struct LayerTools
|
bool is_anything_overridden() const { // if there are no overrides, all the agenda can be skipped - this function can tell us if that's the case
|
||||||
{
|
return something_overridden;
|
||||||
LayerTools(const coordf_t z) :
|
}
|
||||||
print_z(z),
|
|
||||||
has_object(false),
|
|
||||||
has_support(false),
|
|
||||||
has_wipe_tower(false),
|
|
||||||
wipe_tower_partitions(0),
|
|
||||||
wipe_tower_layer_height(0.) {}
|
|
||||||
|
|
||||||
bool operator< (const LayerTools &rhs) const { return print_z < rhs.print_z; }
|
// This is called from GCode::process_layer - see implementation for further comments:
|
||||||
bool operator==(const LayerTools &rhs) const { return print_z == rhs.print_z; }
|
const std::vector<int>* get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, int num_of_copies);
|
||||||
|
|
||||||
coordf_t print_z;
|
// This function goes through all infill entities, decides which ones will be used for wiping and
|
||||||
bool has_object;
|
// marks them by the extruder id. Returns volume that remains to be wiped on the wipe tower:
|
||||||
bool has_support;
|
float mark_wiping_extrusions(const Print& print, unsigned int new_extruder, float volume_to_wipe);
|
||||||
// Zero based extruder IDs, ordered to minimize tool switches.
|
|
||||||
std::vector<unsigned int> extruders;
|
|
||||||
// Will there be anything extruded on this layer for the wipe tower?
|
|
||||||
// Due to the support layers possibly interleaving the object layers,
|
|
||||||
// wipe tower will be disabled for some support only layers.
|
|
||||||
bool has_wipe_tower;
|
|
||||||
// Number of wipe tower partitions to support the required number of tool switches
|
|
||||||
// and to support the wipe tower partitions above this one.
|
|
||||||
size_t wipe_tower_partitions;
|
|
||||||
coordf_t wipe_tower_layer_height;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
void ensure_perimeters_infills_order(const Print& print);
|
||||||
|
|
||||||
|
bool is_overriddable(const ExtrusionEntityCollection& ee, const PrintConfig& print_config, const PrintObject& object, const PrintRegion& region) const;
|
||||||
|
|
||||||
|
void set_layer_tools_ptr(const LayerTools* lt) { m_layer_tools = lt; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
int first_nonsoluble_extruder_on_layer(const PrintConfig& print_config) const;
|
||||||
|
int last_nonsoluble_extruder_on_layer(const PrintConfig& print_config) const;
|
||||||
|
|
||||||
|
// This function is called from mark_wiping_extrusions and sets extruder that it should be printed with (-1 .. as usual)
|
||||||
|
void set_extruder_override(const ExtrusionEntity* entity, unsigned int copy_id, int extruder, unsigned int num_of_copies);
|
||||||
|
|
||||||
|
// Returns true in case that entity is not printed with its usual extruder for a given copy:
|
||||||
|
bool is_entity_overridden(const ExtrusionEntity* entity, int copy_id) const {
|
||||||
|
return (entity_map.find(entity) == entity_map.end() ? false : entity_map.at(entity).at(copy_id) != -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<const ExtrusionEntity*, std::vector<int>> entity_map; // to keep track of who prints what
|
||||||
|
bool something_overridden = false;
|
||||||
|
const LayerTools* m_layer_tools; // so we know which LayerTools object this belongs to
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class LayerTools
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LayerTools(const coordf_t z, const PrintConfig* print_config_ptr = nullptr) :
|
||||||
|
print_z(z),
|
||||||
|
has_object(false),
|
||||||
|
has_support(false),
|
||||||
|
has_wipe_tower(false),
|
||||||
|
wipe_tower_partitions(0),
|
||||||
|
wipe_tower_layer_height(0.) {}
|
||||||
|
|
||||||
|
bool operator< (const LayerTools &rhs) const { return print_z - EPSILON < rhs.print_z; }
|
||||||
|
bool operator==(const LayerTools &rhs) const { return std::abs(print_z - rhs.print_z) < EPSILON; }
|
||||||
|
|
||||||
|
bool is_extruder_order(unsigned int a, unsigned int b) const;
|
||||||
|
|
||||||
|
coordf_t print_z;
|
||||||
|
bool has_object;
|
||||||
|
bool has_support;
|
||||||
|
// Zero based extruder IDs, ordered to minimize tool switches.
|
||||||
|
std::vector<unsigned int> extruders;
|
||||||
|
// Will there be anything extruded on this layer for the wipe tower?
|
||||||
|
// Due to the support layers possibly interleaving the object layers,
|
||||||
|
// wipe tower will be disabled for some support only layers.
|
||||||
|
bool has_wipe_tower;
|
||||||
|
// Number of wipe tower partitions to support the required number of tool switches
|
||||||
|
// and to support the wipe tower partitions above this one.
|
||||||
|
size_t wipe_tower_partitions;
|
||||||
|
coordf_t wipe_tower_layer_height;
|
||||||
|
|
||||||
|
WipingExtrusions& wiping_extrusions() {
|
||||||
|
m_wiping_extrusions.set_layer_tools_ptr(this);
|
||||||
|
return m_wiping_extrusions;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// This object holds list of extrusion that will be used for extruder wiping
|
||||||
|
WipingExtrusions m_wiping_extrusions;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ToolOrdering
|
||||||
|
{
|
||||||
|
public:
|
||||||
ToolOrdering() {}
|
ToolOrdering() {}
|
||||||
|
|
||||||
// For the use case when each object is printed separately
|
// For the use case when each object is printed separately
|
||||||
@ -72,7 +131,7 @@ public:
|
|||||||
std::vector<LayerTools>::const_iterator begin() const { return m_layer_tools.begin(); }
|
std::vector<LayerTools>::const_iterator begin() const { return m_layer_tools.begin(); }
|
||||||
std::vector<LayerTools>::const_iterator end() const { return m_layer_tools.end(); }
|
std::vector<LayerTools>::const_iterator end() const { return m_layer_tools.end(); }
|
||||||
bool empty() const { return m_layer_tools.empty(); }
|
bool empty() const { return m_layer_tools.empty(); }
|
||||||
const std::vector<LayerTools>& layer_tools() const { return m_layer_tools; }
|
std::vector<LayerTools>& layer_tools() { return m_layer_tools; }
|
||||||
bool has_wipe_tower() const { return ! m_layer_tools.empty() && m_first_printing_extruder != (unsigned int)-1 && m_layer_tools.front().wipe_tower_partitions > 0; }
|
bool has_wipe_tower() const { return ! m_layer_tools.empty() && m_first_printing_extruder != (unsigned int)-1 && m_layer_tools.front().wipe_tower_partitions > 0; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -80,17 +139,22 @@ private:
|
|||||||
void collect_extruders(const PrintObject &object);
|
void collect_extruders(const PrintObject &object);
|
||||||
void reorder_extruders(unsigned int last_extruder_id);
|
void reorder_extruders(unsigned int last_extruder_id);
|
||||||
void fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z);
|
void fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z);
|
||||||
void collect_extruder_statistics(bool prime_multi_material);
|
void collect_extruder_statistics(bool prime_multi_material);
|
||||||
|
|
||||||
std::vector<LayerTools> m_layer_tools;
|
std::vector<LayerTools> m_layer_tools;
|
||||||
// First printing extruder, including the multi-material priming sequence.
|
// First printing extruder, including the multi-material priming sequence.
|
||||||
unsigned int m_first_printing_extruder = (unsigned int)-1;
|
unsigned int m_first_printing_extruder = (unsigned int)-1;
|
||||||
// Final printing extruder.
|
// Final printing extruder.
|
||||||
unsigned int m_last_printing_extruder = (unsigned int)-1;
|
unsigned int m_last_printing_extruder = (unsigned int)-1;
|
||||||
// All extruders, which extrude some material over m_layer_tools.
|
// All extruders, which extrude some material over m_layer_tools.
|
||||||
std::vector<unsigned int> m_all_printing_extruders;
|
std::vector<unsigned int> m_all_printing_extruders;
|
||||||
|
|
||||||
|
|
||||||
|
const PrintConfig* m_print_config_ptr = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace SLic3r
|
} // namespace SLic3r
|
||||||
|
|
||||||
#endif /* slic3r_ToolOrdering_hpp_ */
|
#endif /* slic3r_ToolOrdering_hpp_ */
|
||||||
|
@ -21,7 +21,6 @@ TODO LIST
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
#include "Analyzer.hpp"
|
#include "Analyzer.hpp"
|
||||||
|
|
||||||
@ -138,7 +137,7 @@ public:
|
|||||||
width += m_layer_height * float(1. - M_PI / 4.);
|
width += m_layer_height * float(1. - M_PI / 4.);
|
||||||
if (m_extrusions.empty() || m_extrusions.back().pos != rotated_current_pos)
|
if (m_extrusions.empty() || m_extrusions.back().pos != rotated_current_pos)
|
||||||
m_extrusions.emplace_back(WipeTower::Extrusion(rotated_current_pos, 0, m_current_tool));
|
m_extrusions.emplace_back(WipeTower::Extrusion(rotated_current_pos, 0, m_current_tool));
|
||||||
m_extrusions.emplace_back(WipeTower::Extrusion(WipeTower::xy(rot.x, rot.y), width, m_current_tool));
|
m_extrusions.emplace_back(WipeTower::Extrusion(WipeTower::xy(rot.x, rot.y), width, m_current_tool));
|
||||||
}
|
}
|
||||||
|
|
||||||
m_gcode += "G1";
|
m_gcode += "G1";
|
||||||
@ -231,6 +230,17 @@ public:
|
|||||||
Writer& retract(float e, float f = 0.f)
|
Writer& retract(float e, float f = 0.f)
|
||||||
{ return load(-e, f); }
|
{ return load(-e, f); }
|
||||||
|
|
||||||
|
// Loads filament while also moving towards given points in x-axis (x feedrate is limited by cutting the distance short if necessary)
|
||||||
|
Writer& load_move_x_advanced(float farthest_x, float loading_dist, float loading_speed, float max_x_speed = 50.f)
|
||||||
|
{
|
||||||
|
float time = std::abs(loading_dist / loading_speed);
|
||||||
|
float x_speed = std::min(max_x_speed, std::abs(farthest_x - x()) / time);
|
||||||
|
float feedrate = 60.f * std::hypot(x_speed, loading_speed);
|
||||||
|
|
||||||
|
float end_point = x() + (farthest_x > x() ? 1.f : -1.f) * x_speed * time;
|
||||||
|
return extrude_explicit(end_point, y(), loading_dist, feedrate);
|
||||||
|
}
|
||||||
|
|
||||||
// Elevate the extruder head above the current print_z position.
|
// Elevate the extruder head above the current print_z position.
|
||||||
Writer& z_hop(float hop, float f = 0.f)
|
Writer& z_hop(float hop, float f = 0.f)
|
||||||
{
|
{
|
||||||
@ -276,12 +286,9 @@ public:
|
|||||||
// Set extruder temperature, don't wait by default.
|
// Set extruder temperature, don't wait by default.
|
||||||
Writer& set_extruder_temp(int temperature, bool wait = false)
|
Writer& set_extruder_temp(int temperature, bool wait = false)
|
||||||
{
|
{
|
||||||
if (temperature != current_temp) {
|
char buf[128];
|
||||||
char buf[128];
|
sprintf(buf, "M%d S%d\n", wait ? 109 : 104, temperature);
|
||||||
sprintf(buf, "M%d S%d\n", wait ? 109 : 104, temperature);
|
m_gcode += buf;
|
||||||
m_gcode += buf;
|
|
||||||
current_temp = temperature;
|
|
||||||
}
|
|
||||||
return *this;
|
return *this;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -399,8 +406,7 @@ private:
|
|||||||
int current_temp = -1;
|
int current_temp = -1;
|
||||||
const float m_default_analyzer_line_width;
|
const float m_default_analyzer_line_width;
|
||||||
|
|
||||||
std::string
|
std::string set_format_X(float x)
|
||||||
set_format_X(float x)
|
|
||||||
{
|
{
|
||||||
char buf[64];
|
char buf[64];
|
||||||
sprintf(buf, " X%.3f", x);
|
sprintf(buf, " X%.3f", x);
|
||||||
@ -475,7 +481,6 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
|
|||||||
// If false, the last priming are will be large enough to wipe the last extruder sufficiently.
|
// If false, the last priming are will be large enough to wipe the last extruder sufficiently.
|
||||||
bool last_wipe_inside_wipe_tower)
|
bool last_wipe_inside_wipe_tower)
|
||||||
{
|
{
|
||||||
|
|
||||||
this->set_layer(first_layer_height, first_layer_height, tools.size(), true, false);
|
this->set_layer(first_layer_height, first_layer_height, tools.size(), true, false);
|
||||||
this->m_current_tool = tools.front();
|
this->m_current_tool = tools.front();
|
||||||
|
|
||||||
@ -558,7 +563,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
|
|||||||
{
|
{
|
||||||
for (const auto &b : m_layer_info->tool_changes)
|
for (const auto &b : m_layer_info->tool_changes)
|
||||||
if ( b.new_tool == tool ) {
|
if ( b.new_tool == tool ) {
|
||||||
wipe_volume = wipe_volumes[b.old_tool][b.new_tool];
|
wipe_volume = b.wipe_volume;
|
||||||
if (tool == m_layer_info->tool_changes.back().new_tool)
|
if (tool == m_layer_info->tool_changes.back().new_tool)
|
||||||
last_change_in_layer = true;
|
last_change_in_layer = true;
|
||||||
wipe_area = b.required_depth * m_layer_info->extra_spacing;
|
wipe_area = b.required_depth * m_layer_info->extra_spacing;
|
||||||
@ -783,51 +788,44 @@ void WipeTowerPrusaMM::toolchange_Unload(
|
|||||||
WipeTower::xy end_of_ramming(writer.x(),writer.y());
|
WipeTower::xy end_of_ramming(writer.x(),writer.y());
|
||||||
writer.change_analyzer_line_width(m_perimeter_width); // so the next lines are not affected by ramming_line_width_multiplier
|
writer.change_analyzer_line_width(m_perimeter_width); // so the next lines are not affected by ramming_line_width_multiplier
|
||||||
|
|
||||||
// Pull the filament end to the BEGINNING of the cooling tube while still moving the print head
|
// Retraction:
|
||||||
float oldx = writer.x();
|
float old_x = writer.x();
|
||||||
float turning_point = (!m_left_to_right ? std::max(xl,oldx-15.f) : std::min(xr,oldx+15.f) ); // so it's not too far
|
float turning_point = (!m_left_to_right ? xl : xr );
|
||||||
float xdist = std::abs(oldx-turning_point);
|
float total_retraction_distance = m_cooling_tube_retraction + m_cooling_tube_length/2.f - 15.f; // the 15mm is reserved for the first part after ramming
|
||||||
float edist = -(m_cooling_tube_retraction+m_cooling_tube_length/2.f-42);
|
|
||||||
|
|
||||||
writer.suppress_preview()
|
writer.suppress_preview()
|
||||||
.load_move_x(turning_point,-15 , 60.f * std::hypot(xdist,15)/15 * 83 ) // fixed speed after ramming
|
.load_move_x_advanced(turning_point, -15.f, 83.f, 50.f) // this is done at fixed speed
|
||||||
.load_move_x(oldx ,edist , 60.f * std::hypot(xdist,edist)/std::abs(edist) * m_filpar[m_current_tool].unloading_speed )
|
.load_move_x_advanced(old_x, -0.70f * total_retraction_distance, 1.0f * m_filpar[m_current_tool].unloading_speed)
|
||||||
.load_move_x(turning_point,-15 , 60.f * std::hypot(xdist,15)/15 * m_filpar[m_current_tool].unloading_speed*0.55f )
|
.load_move_x_advanced(turning_point, -0.20f * total_retraction_distance, 0.5f * m_filpar[m_current_tool].unloading_speed)
|
||||||
.load_move_x(oldx ,-12 , 60.f * std::hypot(xdist,12)/12 * m_filpar[m_current_tool].unloading_speed*0.35f )
|
.load_move_x_advanced(old_x, -0.10f * total_retraction_distance, 0.3f * m_filpar[m_current_tool].unloading_speed)
|
||||||
|
.travel(old_x, writer.y()) // in case previous move was shortened to limit feedrate
|
||||||
.resume_preview();
|
.resume_preview();
|
||||||
|
|
||||||
if (new_temperature != 0) // Set the extruder temperature, but don't wait.
|
if (new_temperature != 0 && new_temperature != m_old_temperature ) { // Set the extruder temperature, but don't wait.
|
||||||
writer.set_extruder_temp(new_temperature, false);
|
writer.set_extruder_temp(new_temperature, false);
|
||||||
|
m_old_temperature = new_temperature;
|
||||||
|
}
|
||||||
|
|
||||||
// cooling:
|
// Cooling:
|
||||||
writer.suppress_preview();
|
const int& number_of_moves = m_filpar[m_current_tool].cooling_moves;
|
||||||
writer.travel(writer.x(), writer.y() + y_step);
|
if (number_of_moves > 0) {
|
||||||
const float start_x = writer.x();
|
const float& initial_speed = m_filpar[m_current_tool].cooling_initial_speed;
|
||||||
turning_point = ( xr-start_x > start_x-xl ? xr : xl );
|
const float& final_speed = m_filpar[m_current_tool].cooling_final_speed;
|
||||||
const float max_x_dist = 2*std::abs(start_x-turning_point);
|
|
||||||
const unsigned int N = 4 + std::max(0.f, (m_filpar[m_current_tool].cooling_time-14)/3);
|
|
||||||
float time = m_filpar[m_current_tool].cooling_time / float(N);
|
|
||||||
|
|
||||||
i = 0;
|
float speed_inc = (final_speed - initial_speed) / (2.f * number_of_moves - 1.f);
|
||||||
while (i<N) {
|
|
||||||
const float speed = std::min(3.4,2.2 + i*0.3 + (i==0 ? 0 : 0.3)); // mm per second: 2.2, 2.8, 3.1, 3.4, 3.4, 3.4, ...
|
|
||||||
const float e_dist = std::min(speed * time,2*m_cooling_tube_length); // distance to travel
|
|
||||||
|
|
||||||
// this move is the last one at this speed or someone set tube_length to zero
|
|
||||||
if (speed * time < 2*m_cooling_tube_length || m_cooling_tube_length<WT_EPSILON) {
|
|
||||||
++i;
|
|
||||||
time = m_filpar[m_current_tool].cooling_time / float(N);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
time -= e_dist / speed; // subtract time this part will really take
|
|
||||||
|
|
||||||
// as for x, we will make sure the feedrate is at most 2000
|
writer.suppress_preview()
|
||||||
float x_dist = (turning_point - WT_EPSILON < xl ? -1.f : 1.f) * std::min(e_dist * (float)sqrt(pow(2000 / (60 * speed), 2) - 1),max_x_dist);
|
.travel(writer.x(), writer.y() + y_step);
|
||||||
const float feedrate = std::hypot(e_dist, x_dist) / ((e_dist / speed) / 60.f);
|
old_x = writer.x();
|
||||||
writer.cool(start_x+x_dist/2.f,start_x,e_dist/2.f,-e_dist/2.f, feedrate);
|
turning_point = xr-old_x > old_x-xl ? xr : xl;
|
||||||
}
|
for (int i=0; i<number_of_moves; ++i) {
|
||||||
|
float speed = initial_speed + speed_inc * 2*i;
|
||||||
|
writer.load_move_x_advanced(turning_point, m_cooling_tube_length, speed);
|
||||||
|
speed += speed_inc;
|
||||||
|
writer.load_move_x_advanced(old_x, -m_cooling_tube_length, speed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// let's wait is necessary
|
// let's wait is necessary:
|
||||||
writer.wait(m_filpar[m_current_tool].delay);
|
writer.wait(m_filpar[m_current_tool].delay);
|
||||||
// we should be at the beginning of the cooling tube again - let's move to parking position:
|
// we should be at the beginning of the cooling tube again - let's move to parking position:
|
||||||
writer.retract(-m_cooling_tube_length/2.f+m_parking_pos_retraction-m_cooling_tube_retraction, 2000);
|
writer.retract(-m_cooling_tube_length/2.f+m_parking_pos_retraction-m_cooling_tube_retraction, 2000);
|
||||||
@ -871,16 +869,16 @@ void WipeTowerPrusaMM::toolchange_Load(
|
|||||||
float oldx = writer.x(); // the nozzle is in place to do the first wiping moves, we will remember the position
|
float oldx = writer.x(); // the nozzle is in place to do the first wiping moves, we will remember the position
|
||||||
|
|
||||||
// Load the filament while moving left / right, so the excess material will not create a blob at a single position.
|
// Load the filament while moving left / right, so the excess material will not create a blob at a single position.
|
||||||
float loading_speed = m_filpar[m_current_tool].loading_speed; // mm/s in e axis
|
|
||||||
float turning_point = ( oldx-xl < xr-oldx ? xr : xl );
|
float turning_point = ( oldx-xl < xr-oldx ? xr : xl );
|
||||||
float dist = std::abs(oldx-turning_point);
|
float edist = m_parking_pos_retraction+m_extra_loading_move;
|
||||||
float edist = m_parking_pos_retraction-50-2; // loading is 2mm shorter that previous retraction, 50mm reserved for acceleration/deceleration
|
|
||||||
writer.append("; CP TOOLCHANGE LOAD\n")
|
writer.append("; CP TOOLCHANGE LOAD\n")
|
||||||
.suppress_preview()
|
.suppress_preview()
|
||||||
.load_move_x(turning_point, 20, 60*std::hypot(dist,20.f)/20.f * loading_speed*0.3f) // Acceleration
|
.load_move_x_advanced(turning_point, 0.2f * edist, 0.3f * m_filpar[m_current_tool].loading_speed) // Acceleration
|
||||||
.load_move_x(oldx,edist,60*std::hypot(dist,edist)/edist * loading_speed) // Fast phase
|
.load_move_x_advanced(oldx, 0.5f * edist, m_filpar[m_current_tool].loading_speed) // Fast phase
|
||||||
.load_move_x(turning_point, 20, 60*std::hypot(dist,20.f)/20.f * loading_speed*0.3f) // Slowing down
|
.load_move_x_advanced(turning_point, 0.2f * edist, 0.3f * m_filpar[m_current_tool].loading_speed) // Slowing down
|
||||||
.load_move_x(oldx, 10, 60*std::hypot(dist,10.f)/10.f * loading_speed*0.1f) // Super slow
|
.load_move_x_advanced(oldx, 0.1f * edist, 0.1f * m_filpar[m_current_tool].loading_speed) // Super slow
|
||||||
|
.travel(oldx, writer.y()) // in case last move was shortened to limit x feedrate
|
||||||
.resume_preview();
|
.resume_preview();
|
||||||
|
|
||||||
// Reset the extruder current to the normal value.
|
// Reset the extruder current to the normal value.
|
||||||
@ -1057,7 +1055,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Appends a toolchange into m_plan and calculates neccessary depth of the corresponding box
|
// Appends a toolchange into m_plan and calculates neccessary depth of the corresponding box
|
||||||
void WipeTowerPrusaMM::plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool,bool brim)
|
void WipeTowerPrusaMM::plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool, bool brim, float wipe_volume)
|
||||||
{
|
{
|
||||||
assert(m_plan.back().z <= z_par + WT_EPSILON ); // refuses to add a layer below the last one
|
assert(m_plan.back().z <= z_par + WT_EPSILON ); // refuses to add a layer below the last one
|
||||||
|
|
||||||
@ -1082,13 +1080,13 @@ void WipeTowerPrusaMM::plan_toolchange(float z_par, float layer_height_par, unsi
|
|||||||
float ramming_depth = depth;
|
float ramming_depth = depth;
|
||||||
length_to_extrude = width*((length_to_extrude / width)-int(length_to_extrude / width)) - width;
|
length_to_extrude = width*((length_to_extrude / width)-int(length_to_extrude / width)) - width;
|
||||||
float first_wipe_line = -length_to_extrude;
|
float first_wipe_line = -length_to_extrude;
|
||||||
length_to_extrude += volume_to_length(wipe_volumes[old_tool][new_tool], m_perimeter_width, layer_height_par);
|
length_to_extrude += volume_to_length(wipe_volume, m_perimeter_width, layer_height_par);
|
||||||
length_to_extrude = std::max(length_to_extrude,0.f);
|
length_to_extrude = std::max(length_to_extrude,0.f);
|
||||||
|
|
||||||
depth += (int(length_to_extrude / width) + 1) * m_perimeter_width;
|
depth += (int(length_to_extrude / width) + 1) * m_perimeter_width;
|
||||||
depth *= m_extra_spacing;
|
depth *= m_extra_spacing;
|
||||||
|
|
||||||
m_plan.back().tool_changes.push_back(WipeTowerInfo::ToolChange(old_tool, new_tool, depth, ramming_depth,first_wipe_line));
|
m_plan.back().tool_changes.push_back(WipeTowerInfo::ToolChange(old_tool, new_tool, depth, ramming_depth, first_wipe_line, wipe_volume));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1128,7 +1126,7 @@ void WipeTowerPrusaMM::save_on_last_wipe()
|
|||||||
|
|
||||||
float width = m_wipe_tower_width - 3*m_perimeter_width; // width we draw into
|
float width = m_wipe_tower_width - 3*m_perimeter_width; // width we draw into
|
||||||
float length_to_save = 2*(m_wipe_tower_width+m_wipe_tower_depth) + (!layer_finished() ? finish_layer().total_extrusion_length_in_plane() : 0.f);
|
float length_to_save = 2*(m_wipe_tower_width+m_wipe_tower_depth) + (!layer_finished() ? finish_layer().total_extrusion_length_in_plane() : 0.f);
|
||||||
float length_to_wipe = volume_to_length(wipe_volumes[m_layer_info->tool_changes.back().old_tool][m_layer_info->tool_changes.back().new_tool],
|
float length_to_wipe = volume_to_length(m_layer_info->tool_changes.back().wipe_volume,
|
||||||
m_perimeter_width,m_layer_info->height) - m_layer_info->tool_changes.back().first_wipe_line - length_to_save;
|
m_perimeter_width,m_layer_info->height) - m_layer_info->tool_changes.back().first_wipe_line - length_to_save;
|
||||||
|
|
||||||
length_to_wipe = std::max(length_to_wipe,0.f);
|
length_to_wipe = std::max(length_to_wipe,0.f);
|
||||||
@ -1145,7 +1143,8 @@ void WipeTowerPrusaMM::save_on_last_wipe()
|
|||||||
void WipeTowerPrusaMM::generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &result)
|
void WipeTowerPrusaMM::generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &result)
|
||||||
{
|
{
|
||||||
if (m_plan.empty())
|
if (m_plan.empty())
|
||||||
return;
|
|
||||||
|
return;
|
||||||
|
|
||||||
m_extra_spacing = 1.f;
|
m_extra_spacing = 1.f;
|
||||||
|
|
||||||
@ -1165,8 +1164,6 @@ void WipeTowerPrusaMM::generate(std::vector<std::vector<WipeTower::ToolChangeRes
|
|||||||
for (auto layer : m_plan)
|
for (auto layer : m_plan)
|
||||||
{
|
{
|
||||||
set_layer(layer.z,layer.height,0,layer.z == m_plan.front().z,layer.z == m_plan.back().z);
|
set_layer(layer.z,layer.height,0,layer.z == m_plan.front().z,layer.z == m_plan.back().z);
|
||||||
|
|
||||||
|
|
||||||
if (m_peters_wipe_tower)
|
if (m_peters_wipe_tower)
|
||||||
m_wipe_tower_rotation_angle += 90.f;
|
m_wipe_tower_rotation_angle += 90.f;
|
||||||
else
|
else
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include "WipeTower.hpp"
|
#include "WipeTower.hpp"
|
||||||
|
|
||||||
@ -43,8 +44,8 @@ public:
|
|||||||
// width -- width of wipe tower in mm ( default 60 mm - leave as it is )
|
// width -- width of wipe tower in mm ( default 60 mm - leave as it is )
|
||||||
// wipe_area -- space available for one toolchange in mm
|
// wipe_area -- space available for one toolchange in mm
|
||||||
WipeTowerPrusaMM(float x, float y, float width, float rotation_angle, float cooling_tube_retraction,
|
WipeTowerPrusaMM(float x, float y, float width, float rotation_angle, float cooling_tube_retraction,
|
||||||
float cooling_tube_length, float parking_pos_retraction, float bridging, const std::vector<float>& wiping_matrix,
|
float cooling_tube_length, float parking_pos_retraction, float extra_loading_move, float bridging,
|
||||||
unsigned int initial_tool) :
|
const std::vector<std::vector<float>>& wiping_matrix, unsigned int initial_tool) :
|
||||||
m_wipe_tower_pos(x, y),
|
m_wipe_tower_pos(x, y),
|
||||||
m_wipe_tower_width(width),
|
m_wipe_tower_width(width),
|
||||||
m_wipe_tower_rotation_angle(rotation_angle),
|
m_wipe_tower_rotation_angle(rotation_angle),
|
||||||
@ -54,20 +55,19 @@ public:
|
|||||||
m_cooling_tube_retraction(cooling_tube_retraction),
|
m_cooling_tube_retraction(cooling_tube_retraction),
|
||||||
m_cooling_tube_length(cooling_tube_length),
|
m_cooling_tube_length(cooling_tube_length),
|
||||||
m_parking_pos_retraction(parking_pos_retraction),
|
m_parking_pos_retraction(parking_pos_retraction),
|
||||||
|
m_extra_loading_move(extra_loading_move),
|
||||||
m_bridging(bridging),
|
m_bridging(bridging),
|
||||||
m_current_tool(initial_tool)
|
m_current_tool(initial_tool),
|
||||||
{
|
wipe_volumes(wiping_matrix)
|
||||||
unsigned int number_of_extruders = (unsigned int)(sqrt(wiping_matrix.size())+WT_EPSILON);
|
{}
|
||||||
for (unsigned int i = 0; i<number_of_extruders; ++i)
|
|
||||||
wipe_volumes.push_back(std::vector<float>(wiping_matrix.begin()+i*number_of_extruders,wiping_matrix.begin()+(i+1)*number_of_extruders));
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~WipeTowerPrusaMM() {}
|
virtual ~WipeTowerPrusaMM() {}
|
||||||
|
|
||||||
|
|
||||||
// Set the extruder properties.
|
// Set the extruder properties.
|
||||||
void set_extruder(size_t idx, material_type material, int temp, int first_layer_temp, float loading_speed,
|
void set_extruder(size_t idx, material_type material, int temp, int first_layer_temp, float loading_speed,
|
||||||
float unloading_speed, float delay, std::string ramming_parameters, float nozzle_diameter)
|
float unloading_speed, float delay, int cooling_moves, float cooling_initial_speed,
|
||||||
|
float cooling_final_speed, std::string ramming_parameters, float nozzle_diameter)
|
||||||
{
|
{
|
||||||
//while (m_filpar.size() < idx+1) // makes sure the required element is in the vector
|
//while (m_filpar.size() < idx+1) // makes sure the required element is in the vector
|
||||||
m_filpar.push_back(FilamentParameters());
|
m_filpar.push_back(FilamentParameters());
|
||||||
@ -78,7 +78,9 @@ public:
|
|||||||
m_filpar[idx].loading_speed = loading_speed;
|
m_filpar[idx].loading_speed = loading_speed;
|
||||||
m_filpar[idx].unloading_speed = unloading_speed;
|
m_filpar[idx].unloading_speed = unloading_speed;
|
||||||
m_filpar[idx].delay = delay;
|
m_filpar[idx].delay = delay;
|
||||||
m_filpar[idx].cooling_time = 14.f; // let's fix it for now, cooling moves will be reworked for 1.41 anyway
|
m_filpar[idx].cooling_moves = cooling_moves;
|
||||||
|
m_filpar[idx].cooling_initial_speed = cooling_initial_speed;
|
||||||
|
m_filpar[idx].cooling_final_speed = cooling_final_speed;
|
||||||
m_filpar[idx].nozzle_diameter = nozzle_diameter; // to be used in future with (non-single) multiextruder MM
|
m_filpar[idx].nozzle_diameter = nozzle_diameter; // to be used in future with (non-single) multiextruder MM
|
||||||
|
|
||||||
m_perimeter_width = nozzle_diameter * Width_To_Nozzle_Ratio; // all extruders are now assumed to have the same diameter
|
m_perimeter_width = nozzle_diameter * Width_To_Nozzle_Ratio; // all extruders are now assumed to have the same diameter
|
||||||
@ -95,7 +97,7 @@ public:
|
|||||||
|
|
||||||
// Appends into internal structure m_plan containing info about the future wipe tower
|
// Appends into internal structure m_plan containing info about the future wipe tower
|
||||||
// to be used before building begins. The entries must be added ordered in z.
|
// to be used before building begins. The entries must be added ordered in z.
|
||||||
void plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool, bool brim);
|
void plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool, bool brim, float wipe_volume = 0.f);
|
||||||
|
|
||||||
// Iterates through prepared m_plan, generates ToolChangeResults and appends them to "result"
|
// Iterates through prepared m_plan, generates ToolChangeResults and appends them to "result"
|
||||||
void generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &result);
|
void generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &result);
|
||||||
@ -192,11 +194,13 @@ private:
|
|||||||
float m_layer_height = 0.f; // Current layer height.
|
float m_layer_height = 0.f; // Current layer height.
|
||||||
size_t m_max_color_changes = 0; // Maximum number of color changes per layer.
|
size_t m_max_color_changes = 0; // Maximum number of color changes per layer.
|
||||||
bool m_is_first_layer = false;// Is this the 1st layer of the print? If so, print the brim around the waste tower.
|
bool m_is_first_layer = false;// Is this the 1st layer of the print? If so, print the brim around the waste tower.
|
||||||
|
int m_old_temperature = -1; // To keep track of what was the last temp that we set (so we don't issue the command when not neccessary)
|
||||||
|
|
||||||
// G-code generator parameters.
|
// G-code generator parameters.
|
||||||
float m_cooling_tube_retraction = 0.f;
|
float m_cooling_tube_retraction = 0.f;
|
||||||
float m_cooling_tube_length = 0.f;
|
float m_cooling_tube_length = 0.f;
|
||||||
float m_parking_pos_retraction = 0.f;
|
float m_parking_pos_retraction = 0.f;
|
||||||
|
float m_extra_loading_move = 0.f;
|
||||||
float m_bridging = 0.f;
|
float m_bridging = 0.f;
|
||||||
bool m_adhesion = true;
|
bool m_adhesion = true;
|
||||||
|
|
||||||
@ -211,7 +215,9 @@ private:
|
|||||||
float loading_speed = 0.f;
|
float loading_speed = 0.f;
|
||||||
float unloading_speed = 0.f;
|
float unloading_speed = 0.f;
|
||||||
float delay = 0.f ;
|
float delay = 0.f ;
|
||||||
float cooling_time = 0.f;
|
int cooling_moves = 0;
|
||||||
|
float cooling_initial_speed = 0.f;
|
||||||
|
float cooling_final_speed = 0.f;
|
||||||
float ramming_line_width_multiplicator = 0.f;
|
float ramming_line_width_multiplicator = 0.f;
|
||||||
float ramming_step_multiplicator = 0.f;
|
float ramming_step_multiplicator = 0.f;
|
||||||
std::vector<float> ramming_speed;
|
std::vector<float> ramming_speed;
|
||||||
@ -229,14 +235,13 @@ private:
|
|||||||
bool m_print_brim = true;
|
bool m_print_brim = true;
|
||||||
// A fill-in direction (positive Y, negative Y) alternates with each layer.
|
// A fill-in direction (positive Y, negative Y) alternates with each layer.
|
||||||
wipe_shape m_current_shape = SHAPE_NORMAL;
|
wipe_shape m_current_shape = SHAPE_NORMAL;
|
||||||
unsigned int m_current_tool;
|
unsigned int m_current_tool = 0;
|
||||||
std::vector<std::vector<float>> wipe_volumes;
|
const std::vector<std::vector<float>> wipe_volumes;
|
||||||
|
|
||||||
float m_depth_traversed = 0.f; // Current y position at the wipe tower.
|
float m_depth_traversed = 0.f; // Current y position at the wipe tower.
|
||||||
bool m_left_to_right = true;
|
bool m_left_to_right = true;
|
||||||
float m_extra_spacing = 1.f;
|
float m_extra_spacing = 1.f;
|
||||||
|
|
||||||
|
|
||||||
// Calculates extrusion flow needed to produce required line width for given layer height
|
// Calculates extrusion flow needed to produce required line width for given layer height
|
||||||
float extrusion_flow(float layer_height = -1.f) const // negative layer_height - return current m_extrusion_flow
|
float extrusion_flow(float layer_height = -1.f) const // negative layer_height - return current m_extrusion_flow
|
||||||
{
|
{
|
||||||
@ -247,7 +252,7 @@ private:
|
|||||||
|
|
||||||
// Calculates length of extrusion line to extrude given volume
|
// Calculates length of extrusion line to extrude given volume
|
||||||
float volume_to_length(float volume, float line_width, float layer_height) const {
|
float volume_to_length(float volume, float line_width, float layer_height) const {
|
||||||
return volume / (layer_height * (line_width - layer_height * (1. - M_PI / 4.)));
|
return std::max(0., volume / (layer_height * (line_width - layer_height * (1. - M_PI / 4.))));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculates depth for all layers and propagates them downwards
|
// Calculates depth for all layers and propagates them downwards
|
||||||
@ -300,8 +305,9 @@ private:
|
|||||||
float required_depth;
|
float required_depth;
|
||||||
float ramming_depth;
|
float ramming_depth;
|
||||||
float first_wipe_line;
|
float first_wipe_line;
|
||||||
ToolChange(unsigned int old,unsigned int newtool,float depth=0.f,float ramming_depth=0.f,float fwl=0.f)
|
float wipe_volume;
|
||||||
: old_tool{old}, new_tool{newtool}, required_depth{depth}, ramming_depth{ramming_depth},first_wipe_line{fwl} {}
|
ToolChange(unsigned int old, unsigned int newtool, float depth=0.f, float ramming_depth=0.f, float fwl=0.f, float wv=0.f)
|
||||||
|
: old_tool{old}, new_tool{newtool}, required_depth{depth}, ramming_depth{ramming_depth}, first_wipe_line{fwl}, wipe_volume{wv} {}
|
||||||
};
|
};
|
||||||
float z; // z position of the layer
|
float z; // z position of the layer
|
||||||
float height; // layer height
|
float height; // layer height
|
||||||
|
@ -165,6 +165,11 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
|
|||||||
std::vector<PrintStep> steps;
|
std::vector<PrintStep> steps;
|
||||||
std::vector<PrintObjectStep> osteps;
|
std::vector<PrintObjectStep> osteps;
|
||||||
bool invalidated = false;
|
bool invalidated = false;
|
||||||
|
|
||||||
|
// Always invalidate the wipe tower. This is probably necessary because of the wipe_into_infill / wipe_into_objects
|
||||||
|
// features - nearly anything can influence what should (and could) be wiped into.
|
||||||
|
steps.emplace_back(psWipeTower);
|
||||||
|
|
||||||
for (const t_config_option_key &opt_key : opt_keys) {
|
for (const t_config_option_key &opt_key : opt_keys) {
|
||||||
if (steps_ignore.find(opt_key) != steps_ignore.end()) {
|
if (steps_ignore.find(opt_key) != steps_ignore.end()) {
|
||||||
// These options only affect G-code export or they are just notes without influence on the generated G-code,
|
// These options only affect G-code export or they are just notes without influence on the generated G-code,
|
||||||
@ -191,6 +196,9 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
|
|||||||
|| opt_key == "filament_loading_speed"
|
|| opt_key == "filament_loading_speed"
|
||||||
|| opt_key == "filament_unloading_speed"
|
|| opt_key == "filament_unloading_speed"
|
||||||
|| opt_key == "filament_toolchange_delay"
|
|| opt_key == "filament_toolchange_delay"
|
||||||
|
|| opt_key == "filament_cooling_moves"
|
||||||
|
|| opt_key == "filament_cooling_initial_speed"
|
||||||
|
|| opt_key == "filament_cooling_final_speed"
|
||||||
|| opt_key == "filament_ramming_parameters"
|
|| opt_key == "filament_ramming_parameters"
|
||||||
|| opt_key == "gcode_flavor"
|
|| opt_key == "gcode_flavor"
|
||||||
|| opt_key == "single_extruder_multi_material"
|
|| opt_key == "single_extruder_multi_material"
|
||||||
@ -206,6 +214,7 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
|
|||||||
|| opt_key == "parking_pos_retraction"
|
|| opt_key == "parking_pos_retraction"
|
||||||
|| opt_key == "cooling_tube_retraction"
|
|| opt_key == "cooling_tube_retraction"
|
||||||
|| opt_key == "cooling_tube_length"
|
|| opt_key == "cooling_tube_length"
|
||||||
|
|| opt_key == "extra_loading_move"
|
||||||
|| opt_key == "z_offset") {
|
|| opt_key == "z_offset") {
|
||||||
steps.emplace_back(psWipeTower);
|
steps.emplace_back(psWipeTower);
|
||||||
} else if (
|
} else if (
|
||||||
@ -217,7 +226,6 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
|
|||||||
osteps.emplace_back(posSupportMaterial);
|
osteps.emplace_back(posSupportMaterial);
|
||||||
steps.emplace_back(psSkirt);
|
steps.emplace_back(psSkirt);
|
||||||
steps.emplace_back(psBrim);
|
steps.emplace_back(psBrim);
|
||||||
steps.emplace_back(psWipeTower);
|
|
||||||
} else {
|
} else {
|
||||||
// for legacy, if we can't handle this option let's invalidate all steps
|
// for legacy, if we can't handle this option let's invalidate all steps
|
||||||
//FIXME invalidate all steps of all objects as well?
|
//FIXME invalidate all steps of all objects as well?
|
||||||
@ -1028,6 +1036,14 @@ void Print::_make_wipe_tower()
|
|||||||
if (! this->has_wipe_tower())
|
if (! this->has_wipe_tower())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Get wiping matrix to get number of extruders and convert vector<double> to vector<float>:
|
||||||
|
std::vector<float> wiping_matrix((this->config.wiping_volumes_matrix.values).begin(),(this->config.wiping_volumes_matrix.values).end());
|
||||||
|
// Extract purging volumes for each extruder pair:
|
||||||
|
std::vector<std::vector<float>> wipe_volumes;
|
||||||
|
const unsigned int number_of_extruders = (unsigned int)(sqrt(wiping_matrix.size())+EPSILON);
|
||||||
|
for (unsigned int i = 0; i<number_of_extruders; ++i)
|
||||||
|
wipe_volumes.push_back(std::vector<float>(wiping_matrix.begin()+i*number_of_extruders, wiping_matrix.begin()+(i+1)*number_of_extruders));
|
||||||
|
|
||||||
// Let the ToolOrdering class know there will be initial priming extrusions at the start of the print.
|
// Let the ToolOrdering class know there will be initial priming extrusions at the start of the print.
|
||||||
m_tool_ordering = ToolOrdering(*this, (unsigned int)-1, true);
|
m_tool_ordering = ToolOrdering(*this, (unsigned int)-1, true);
|
||||||
if (! m_tool_ordering.has_wipe_tower())
|
if (! m_tool_ordering.has_wipe_tower())
|
||||||
@ -1043,7 +1059,7 @@ void Print::_make_wipe_tower()
|
|||||||
size_t idx_end = m_tool_ordering.layer_tools().size();
|
size_t idx_end = m_tool_ordering.layer_tools().size();
|
||||||
// Find the first wipe tower layer, which does not have a counterpart in an object or a support layer.
|
// Find the first wipe tower layer, which does not have a counterpart in an object or a support layer.
|
||||||
for (size_t i = 0; i < idx_end; ++ i) {
|
for (size_t i = 0; i < idx_end; ++ i) {
|
||||||
const ToolOrdering::LayerTools < = m_tool_ordering.layer_tools()[i];
|
const LayerTools < = m_tool_ordering.layer_tools()[i];
|
||||||
if (lt.has_wipe_tower && ! lt.has_object && ! lt.has_support) {
|
if (lt.has_wipe_tower && ! lt.has_object && ! lt.has_support) {
|
||||||
idx_begin = i;
|
idx_begin = i;
|
||||||
break;
|
break;
|
||||||
@ -1057,7 +1073,7 @@ void Print::_make_wipe_tower()
|
|||||||
for (; it_layer != it_end && (*it_layer)->print_z - EPSILON < wipe_tower_new_layer_print_z_first; ++ it_layer);
|
for (; it_layer != it_end && (*it_layer)->print_z - EPSILON < wipe_tower_new_layer_print_z_first; ++ it_layer);
|
||||||
// Find the stopper of the sequence of wipe tower layers, which do not have a counterpart in an object or a support layer.
|
// Find the stopper of the sequence of wipe tower layers, which do not have a counterpart in an object or a support layer.
|
||||||
for (size_t i = idx_begin; i < idx_end; ++ i) {
|
for (size_t i = idx_begin; i < idx_end; ++ i) {
|
||||||
ToolOrdering::LayerTools < = const_cast<ToolOrdering::LayerTools&>(m_tool_ordering.layer_tools()[i]);
|
LayerTools < = const_cast<LayerTools&>(m_tool_ordering.layer_tools()[i]);
|
||||||
if (! (lt.has_wipe_tower && ! lt.has_object && ! lt.has_support))
|
if (! (lt.has_wipe_tower && ! lt.has_object && ! lt.has_support))
|
||||||
break;
|
break;
|
||||||
lt.has_support = true;
|
lt.has_support = true;
|
||||||
@ -1072,22 +1088,20 @@ void Print::_make_wipe_tower()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get wiping matrix to get number of extruders and convert vector<double> to vector<float>:
|
|
||||||
std::vector<float> wiping_volumes((this->config.wiping_volumes_matrix.values).begin(),(this->config.wiping_volumes_matrix.values).end());
|
|
||||||
|
|
||||||
// Initialize the wipe tower.
|
// Initialize the wipe tower.
|
||||||
WipeTowerPrusaMM wipe_tower(
|
WipeTowerPrusaMM wipe_tower(
|
||||||
float(this->config.wipe_tower_x.value), float(this->config.wipe_tower_y.value),
|
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_width.value),
|
||||||
float(this->config.wipe_tower_rotation_angle.value), float(this->config.cooling_tube_retraction.value),
|
float(this->config.wipe_tower_rotation_angle.value), float(this->config.cooling_tube_retraction.value),
|
||||||
float(this->config.cooling_tube_length.value), float(this->config.parking_pos_retraction.value),
|
float(this->config.cooling_tube_length.value), float(this->config.parking_pos_retraction.value),
|
||||||
float(this->config.wipe_tower_bridging), wiping_volumes, m_tool_ordering.first_extruder());
|
float(this->config.extra_loading_move.value), float(this->config.wipe_tower_bridging), wipe_volumes,
|
||||||
|
m_tool_ordering.first_extruder());
|
||||||
|
|
||||||
//wipe_tower.set_retract();
|
//wipe_tower.set_retract();
|
||||||
//wipe_tower.set_zhop();
|
//wipe_tower.set_zhop();
|
||||||
|
|
||||||
// Set the extruder & material properties at the wipe tower object.
|
// Set the extruder & material properties at the wipe tower object.
|
||||||
for (size_t i = 0; i < (int)(sqrt(wiping_volumes.size())+EPSILON); ++ i)
|
for (size_t i = 0; i < number_of_extruders; ++ i)
|
||||||
wipe_tower.set_extruder(
|
wipe_tower.set_extruder(
|
||||||
i,
|
i,
|
||||||
WipeTowerPrusaMM::parse_material(this->config.filament_type.get_at(i).c_str()),
|
WipeTowerPrusaMM::parse_material(this->config.filament_type.get_at(i).c_str()),
|
||||||
@ -1096,91 +1110,44 @@ void Print::_make_wipe_tower()
|
|||||||
this->config.filament_loading_speed.get_at(i),
|
this->config.filament_loading_speed.get_at(i),
|
||||||
this->config.filament_unloading_speed.get_at(i),
|
this->config.filament_unloading_speed.get_at(i),
|
||||||
this->config.filament_toolchange_delay.get_at(i),
|
this->config.filament_toolchange_delay.get_at(i),
|
||||||
|
this->config.filament_cooling_moves.get_at(i),
|
||||||
|
this->config.filament_cooling_initial_speed.get_at(i),
|
||||||
|
this->config.filament_cooling_final_speed.get_at(i),
|
||||||
this->config.filament_ramming_parameters.get_at(i),
|
this->config.filament_ramming_parameters.get_at(i),
|
||||||
this->config.nozzle_diameter.get_at(i));
|
this->config.nozzle_diameter.get_at(i));
|
||||||
|
|
||||||
// When printing the first layer's wipe tower, the first extruder is expected to be active and primed.
|
|
||||||
// Therefore the number of wipe sections at the wipe tower will be (m_tool_ordering.front().extruders-1) at the 1st layer.
|
|
||||||
// The following variable is true if the last priming section cannot be squeezed inside the wipe tower.
|
|
||||||
bool last_priming_wipe_full = m_tool_ordering.front().extruders.size() > m_tool_ordering.front().wipe_tower_partitions;
|
|
||||||
|
|
||||||
m_wipe_tower_priming = Slic3r::make_unique<WipeTower::ToolChangeResult>(
|
m_wipe_tower_priming = Slic3r::make_unique<WipeTower::ToolChangeResult>(
|
||||||
wipe_tower.prime(this->skirt_first_layer_height(), m_tool_ordering.all_extruders(), ! last_priming_wipe_full));
|
wipe_tower.prime(this->skirt_first_layer_height(), m_tool_ordering.all_extruders(), false));
|
||||||
|
|
||||||
|
|
||||||
// Lets go through the wipe tower layers and determine pairs of extruder changes for each
|
// Lets go through the wipe tower layers and determine pairs of extruder changes for each
|
||||||
// to pass to wipe_tower (so that it can use it for planning the layout of the tower)
|
// to pass to wipe_tower (so that it can use it for planning the layout of the tower)
|
||||||
{
|
{
|
||||||
unsigned int current_extruder_id = m_tool_ordering.all_extruders().back();
|
unsigned int current_extruder_id = m_tool_ordering.all_extruders().back();
|
||||||
for (const auto &layer_tools : m_tool_ordering.layer_tools()) { // for all layers
|
for (auto &layer_tools : m_tool_ordering.layer_tools()) { // for all layers
|
||||||
if (!layer_tools.has_wipe_tower) continue;
|
if (!layer_tools.has_wipe_tower) continue;
|
||||||
bool first_layer = &layer_tools == &m_tool_ordering.front();
|
bool first_layer = &layer_tools == &m_tool_ordering.front();
|
||||||
wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, current_extruder_id,false);
|
wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, current_extruder_id,false);
|
||||||
for (const auto extruder_id : layer_tools.extruders) {
|
for (const auto extruder_id : layer_tools.extruders) {
|
||||||
if ((first_layer && extruder_id == m_tool_ordering.all_extruders().back()) || extruder_id != current_extruder_id) {
|
if ((first_layer && extruder_id == m_tool_ordering.all_extruders().back()) || extruder_id != current_extruder_id) {
|
||||||
wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, extruder_id, first_layer && extruder_id == m_tool_ordering.all_extruders().back());
|
float volume_to_wipe = wipe_volumes[current_extruder_id][extruder_id]; // total volume to wipe after this toolchange
|
||||||
|
|
||||||
|
// try to assign some infills/objects for the wiping:
|
||||||
|
volume_to_wipe = layer_tools.wiping_extrusions().mark_wiping_extrusions(*this, extruder_id, wipe_volumes[current_extruder_id][extruder_id]);
|
||||||
|
|
||||||
|
wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, extruder_id, first_layer && extruder_id == m_tool_ordering.all_extruders().back(), volume_to_wipe);
|
||||||
current_extruder_id = extruder_id;
|
current_extruder_id = extruder_id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
layer_tools.wiping_extrusions().ensure_perimeters_infills_order(*this);
|
||||||
if (&layer_tools == &m_tool_ordering.back() || (&layer_tools + 1)->wipe_tower_partitions == 0)
|
if (&layer_tools == &m_tool_ordering.back() || (&layer_tools + 1)->wipe_tower_partitions == 0)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Generate the wipe tower layers.
|
// Generate the wipe tower layers.
|
||||||
m_wipe_tower_tool_changes.reserve(m_tool_ordering.layer_tools().size());
|
m_wipe_tower_tool_changes.reserve(m_tool_ordering.layer_tools().size());
|
||||||
wipe_tower.generate(m_wipe_tower_tool_changes);
|
wipe_tower.generate(m_wipe_tower_tool_changes);
|
||||||
|
|
||||||
// Set current_extruder_id to the last extruder primed.
|
|
||||||
/*unsigned int current_extruder_id = m_tool_ordering.all_extruders().back();
|
|
||||||
|
|
||||||
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(layer_tools.wipe_tower_layer_height),
|
|
||||||
layer_tools.wipe_tower_partitions,
|
|
||||||
first_layer,
|
|
||||||
last_layer);
|
|
||||||
std::vector<WipeTower::ToolChangeResult> tool_changes;
|
|
||||||
for (unsigned int extruder_id : layer_tools.extruders)
|
|
||||||
// Call the wipe_tower.tool_change() at the first layer for the initial extruder
|
|
||||||
// to extrude the wipe tower brim,
|
|
||||||
if ((first_layer && extruder_id == m_tool_ordering.all_extruders().back()) ||
|
|
||||||
// or when an extruder shall be switched.
|
|
||||||
extruder_id != current_extruder_id) {
|
|
||||||
tool_changes.emplace_back(wipe_tower.tool_change(extruder_id, extruder_id == layer_tools.extruders.back(), 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;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
// Unload the current filament over the purge tower.
|
// Unload the current filament over the purge tower.
|
||||||
coordf_t layer_height = this->objects.front()->config.layer_height.value;
|
coordf_t layer_height = this->objects.front()->config.layer_height.value;
|
||||||
if (m_tool_ordering.back().wipe_tower_partitions > 0) {
|
if (m_tool_ordering.back().wipe_tower_partitions > 0) {
|
||||||
@ -1201,6 +1168,10 @@ void Print::_make_wipe_tower()
|
|||||||
wipe_tower.tool_change((unsigned int)-1, false));
|
wipe_tower.tool_change((unsigned int)-1, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
std::string Print::output_filename()
|
std::string Print::output_filename()
|
||||||
{
|
{
|
||||||
this->placeholder_parser.update_timestamp();
|
this->placeholder_parser.update_timestamp();
|
||||||
@ -1239,4 +1210,13 @@ void Print::set_status(int percent, const std::string &message)
|
|||||||
printf("Print::status %d => %s\n", percent, message.c_str());
|
printf("Print::status %d => %s\n", percent, message.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Returns extruder this eec should be printed with, according to PrintRegion config
|
||||||
|
int Print::get_extruder(const ExtrusionEntityCollection& fill, const PrintRegion ®ion)
|
||||||
|
{
|
||||||
|
return is_infill(fill.role()) ? std::max<int>(0, (is_solid_infill(fill.entities.front()->role()) ? region.config.solid_infill_extruder : region.config.infill_extruder) - 1) :
|
||||||
|
std::max<int>(region.config.perimeter_extruder.value - 1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ class Print;
|
|||||||
class PrintObject;
|
class PrintObject;
|
||||||
class ModelObject;
|
class ModelObject;
|
||||||
|
|
||||||
|
|
||||||
// Print step IDs for keeping track of the print state.
|
// Print step IDs for keeping track of the print state.
|
||||||
enum PrintStep {
|
enum PrintStep {
|
||||||
psSkirt, psBrim, psWipeTower, psCount,
|
psSkirt, psBrim, psWipeTower, psCount,
|
||||||
@ -285,6 +286,9 @@ public:
|
|||||||
bool has_support_material() const;
|
bool has_support_material() const;
|
||||||
void auto_assign_extruders(ModelObject* model_object) const;
|
void auto_assign_extruders(ModelObject* model_object) const;
|
||||||
|
|
||||||
|
// Returns extruder this eec should be printed with, according to PrintRegion config:
|
||||||
|
static int get_extruder(const ExtrusionEntityCollection& fill, const PrintRegion ®ion);
|
||||||
|
|
||||||
void _make_skirt();
|
void _make_skirt();
|
||||||
void _make_brim();
|
void _make_brim();
|
||||||
|
|
||||||
@ -311,7 +315,8 @@ public:
|
|||||||
void restart() { m_canceled = false; }
|
void restart() { m_canceled = false; }
|
||||||
// Has the calculation been canceled?
|
// Has the calculation been canceled?
|
||||||
bool canceled() { return m_canceled; }
|
bool canceled() { return m_canceled; }
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
|
bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
|
||||||
PrintRegionConfig _region_config_from_model_volume(const ModelVolume &volume);
|
PrintRegionConfig _region_config_from_model_volume(const ModelVolume &volume);
|
||||||
@ -320,6 +325,7 @@ private:
|
|||||||
tbb::atomic<bool> m_canceled;
|
tbb::atomic<bool> m_canceled;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#define FOREACH_BASE(type, container, iterator) for (type::const_iterator iterator = (container).begin(); iterator != (container).end(); ++iterator)
|
#define FOREACH_BASE(type, container, iterator) for (type::const_iterator iterator = (container).begin(); iterator != (container).end(); ++iterator)
|
||||||
#define FOREACH_REGION(print, region) FOREACH_BASE(PrintRegionPtrs, (print)->regions, region)
|
#define FOREACH_REGION(print, region) FOREACH_BASE(PrintRegionPtrs, (print)->regions, region)
|
||||||
#define FOREACH_OBJECT(print, object) FOREACH_BASE(PrintObjectPtrs, (print)->objects, object)
|
#define FOREACH_OBJECT(print, object) FOREACH_BASE(PrintObjectPtrs, (print)->objects, object)
|
||||||
|
@ -352,6 +352,7 @@ PrintConfigDef::PrintConfigDef()
|
|||||||
def->enum_labels.push_back("2");
|
def->enum_labels.push_back("2");
|
||||||
def->enum_labels.push_back("3");
|
def->enum_labels.push_back("3");
|
||||||
def->enum_labels.push_back("4");
|
def->enum_labels.push_back("4");
|
||||||
|
def->enum_labels.push_back("5");
|
||||||
|
|
||||||
def = this->add("extruder_clearance_height", coFloat);
|
def = this->add("extruder_clearance_height", coFloat);
|
||||||
def->label = L("Height");
|
def->label = L("Height");
|
||||||
@ -491,6 +492,31 @@ PrintConfigDef::PrintConfigDef()
|
|||||||
def->min = 0;
|
def->min = 0;
|
||||||
def->default_value = new ConfigOptionFloats { 0. };
|
def->default_value = new ConfigOptionFloats { 0. };
|
||||||
|
|
||||||
|
def = this->add("filament_cooling_moves", coInts);
|
||||||
|
def->label = L("Number of cooling moves");
|
||||||
|
def->tooltip = L("Filament is cooled by being moved back and forth in the "
|
||||||
|
"cooling tubes. Specify desired number of these moves ");
|
||||||
|
def->cli = "filament-cooling-moves=i@";
|
||||||
|
def->max = 0;
|
||||||
|
def->max = 20;
|
||||||
|
def->default_value = new ConfigOptionInts { 4 };
|
||||||
|
|
||||||
|
def = this->add("filament_cooling_initial_speed", coFloats);
|
||||||
|
def->label = L("Speed of the first cooling move");
|
||||||
|
def->tooltip = L("Cooling moves are gradually accelerating beginning at this speed. ");
|
||||||
|
def->cli = "filament-cooling-initial-speed=i@";
|
||||||
|
def->sidetext = L("mm/s");
|
||||||
|
def->min = 0;
|
||||||
|
def->default_value = new ConfigOptionFloats { 2.2f };
|
||||||
|
|
||||||
|
def = this->add("filament_cooling_final_speed", coFloats);
|
||||||
|
def->label = L("Speed of the last cooling move");
|
||||||
|
def->tooltip = L("Cooling moves are gradually accelerating towards this speed. ");
|
||||||
|
def->cli = "filament-cooling-final-speed=i@";
|
||||||
|
def->sidetext = L("mm/s");
|
||||||
|
def->min = 0;
|
||||||
|
def->default_value = new ConfigOptionFloats { 3.4f };
|
||||||
|
|
||||||
def = this->add("filament_ramming_parameters", coStrings);
|
def = this->add("filament_ramming_parameters", coStrings);
|
||||||
def->label = L("Ramming parameters");
|
def->label = L("Ramming parameters");
|
||||||
def->tooltip = L("This string is edited by RammingDialog and contains ramming specific parameters ");
|
def->tooltip = L("This string is edited by RammingDialog and contains ramming specific parameters ");
|
||||||
@ -1129,6 +1155,15 @@ PrintConfigDef::PrintConfigDef()
|
|||||||
def->min = 0;
|
def->min = 0;
|
||||||
def->default_value = new ConfigOptionFloat(92.f);
|
def->default_value = new ConfigOptionFloat(92.f);
|
||||||
|
|
||||||
|
def = this->add("extra_loading_move", coFloat);
|
||||||
|
def->label = L("Extra loading distance");
|
||||||
|
def->tooltip = L("When set to zero, the distance the filament is moved from parking position during load "
|
||||||
|
"is exactly the same as it was moved back during unload. When positive, it is loaded further, "
|
||||||
|
" if negative, the loading move is shorter than unloading. ");
|
||||||
|
def->sidetext = L("mm");
|
||||||
|
def->cli = "extra_loading_move=f";
|
||||||
|
def->default_value = new ConfigOptionFloat(-2.f);
|
||||||
|
|
||||||
def = this->add("perimeter_acceleration", coFloat);
|
def = this->add("perimeter_acceleration", coFloat);
|
||||||
def->label = L("Perimeters");
|
def->label = L("Perimeters");
|
||||||
def->tooltip = L("This is the acceleration your printer will use for perimeters. "
|
def->tooltip = L("This is the acceleration your printer will use for perimeters. "
|
||||||
@ -1942,7 +1977,25 @@ PrintConfigDef::PrintConfigDef()
|
|||||||
def->sidetext = L("degrees");
|
def->sidetext = L("degrees");
|
||||||
def->cli = "wipe-tower-rotation-angle=f";
|
def->cli = "wipe-tower-rotation-angle=f";
|
||||||
def->default_value = new ConfigOptionFloat(0.);
|
def->default_value = new ConfigOptionFloat(0.);
|
||||||
|
|
||||||
|
def = this->add("wipe_into_infill", coBool);
|
||||||
|
def->category = L("Extruders");
|
||||||
|
def->label = L("Purging into infill");
|
||||||
|
def->tooltip = L("Wiping after toolchange will be preferentially done inside infills. "
|
||||||
|
"This lowers the amount of waste but may result in longer print time "
|
||||||
|
" due to additional travel moves.");
|
||||||
|
def->cli = "wipe-into-infill!";
|
||||||
|
def->default_value = new ConfigOptionBool(false);
|
||||||
|
|
||||||
|
def = this->add("wipe_into_objects", coBool);
|
||||||
|
def->category = L("Extruders");
|
||||||
|
def->label = L("Purging into objects");
|
||||||
|
def->tooltip = L("Objects will be used to wipe the nozzle after a toolchange to save material "
|
||||||
|
"that would otherwise end up in the wipe tower and decrease print time. "
|
||||||
|
"Colours of the objects will be mixed as a result.");
|
||||||
|
def->cli = "wipe-into-objects!";
|
||||||
|
def->default_value = new ConfigOptionBool(false);
|
||||||
|
|
||||||
def = this->add("wipe_tower_bridging", coFloat);
|
def = this->add("wipe_tower_bridging", coFloat);
|
||||||
def->label = L("Maximal bridging distance");
|
def->label = L("Maximal bridging distance");
|
||||||
def->tooltip = L("Maximal distance between supports on sparse infill sections. ");
|
def->tooltip = L("Maximal distance between supports on sparse infill sections. ");
|
||||||
|
@ -336,7 +336,8 @@ public:
|
|||||||
ConfigOptionBool support_material_with_sheath;
|
ConfigOptionBool support_material_with_sheath;
|
||||||
ConfigOptionFloatOrPercent support_material_xy_spacing;
|
ConfigOptionFloatOrPercent support_material_xy_spacing;
|
||||||
ConfigOptionFloat xy_size_compensation;
|
ConfigOptionFloat xy_size_compensation;
|
||||||
|
ConfigOptionBool wipe_into_objects;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void initialize(StaticCacheBase &cache, const char *base_ptr)
|
void initialize(StaticCacheBase &cache, const char *base_ptr)
|
||||||
{
|
{
|
||||||
@ -372,6 +373,7 @@ protected:
|
|||||||
OPT_PTR(support_material_threshold);
|
OPT_PTR(support_material_threshold);
|
||||||
OPT_PTR(support_material_with_sheath);
|
OPT_PTR(support_material_with_sheath);
|
||||||
OPT_PTR(xy_size_compensation);
|
OPT_PTR(xy_size_compensation);
|
||||||
|
OPT_PTR(wipe_into_objects);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -414,7 +416,8 @@ public:
|
|||||||
ConfigOptionFloatOrPercent top_infill_extrusion_width;
|
ConfigOptionFloatOrPercent top_infill_extrusion_width;
|
||||||
ConfigOptionInt top_solid_layers;
|
ConfigOptionInt top_solid_layers;
|
||||||
ConfigOptionFloatOrPercent top_solid_infill_speed;
|
ConfigOptionFloatOrPercent top_solid_infill_speed;
|
||||||
|
ConfigOptionBool wipe_into_infill;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void initialize(StaticCacheBase &cache, const char *base_ptr)
|
void initialize(StaticCacheBase &cache, const char *base_ptr)
|
||||||
{
|
{
|
||||||
@ -452,6 +455,7 @@ protected:
|
|||||||
OPT_PTR(top_infill_extrusion_width);
|
OPT_PTR(top_infill_extrusion_width);
|
||||||
OPT_PTR(top_solid_infill_speed);
|
OPT_PTR(top_solid_infill_speed);
|
||||||
OPT_PTR(top_solid_layers);
|
OPT_PTR(top_solid_layers);
|
||||||
|
OPT_PTR(wipe_into_infill);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -526,6 +530,9 @@ public:
|
|||||||
ConfigOptionFloats filament_loading_speed;
|
ConfigOptionFloats filament_loading_speed;
|
||||||
ConfigOptionFloats filament_unloading_speed;
|
ConfigOptionFloats filament_unloading_speed;
|
||||||
ConfigOptionFloats filament_toolchange_delay;
|
ConfigOptionFloats filament_toolchange_delay;
|
||||||
|
ConfigOptionInts filament_cooling_moves;
|
||||||
|
ConfigOptionFloats filament_cooling_initial_speed;
|
||||||
|
ConfigOptionFloats filament_cooling_final_speed;
|
||||||
ConfigOptionStrings filament_ramming_parameters;
|
ConfigOptionStrings filament_ramming_parameters;
|
||||||
ConfigOptionBool gcode_comments;
|
ConfigOptionBool gcode_comments;
|
||||||
ConfigOptionEnum<GCodeFlavor> gcode_flavor;
|
ConfigOptionEnum<GCodeFlavor> gcode_flavor;
|
||||||
@ -555,6 +562,7 @@ public:
|
|||||||
ConfigOptionFloat cooling_tube_retraction;
|
ConfigOptionFloat cooling_tube_retraction;
|
||||||
ConfigOptionFloat cooling_tube_length;
|
ConfigOptionFloat cooling_tube_length;
|
||||||
ConfigOptionFloat parking_pos_retraction;
|
ConfigOptionFloat parking_pos_retraction;
|
||||||
|
ConfigOptionFloat extra_loading_move;
|
||||||
|
|
||||||
|
|
||||||
std::string get_extrusion_axis() const
|
std::string get_extrusion_axis() const
|
||||||
@ -583,6 +591,9 @@ protected:
|
|||||||
OPT_PTR(filament_loading_speed);
|
OPT_PTR(filament_loading_speed);
|
||||||
OPT_PTR(filament_unloading_speed);
|
OPT_PTR(filament_unloading_speed);
|
||||||
OPT_PTR(filament_toolchange_delay);
|
OPT_PTR(filament_toolchange_delay);
|
||||||
|
OPT_PTR(filament_cooling_moves);
|
||||||
|
OPT_PTR(filament_cooling_initial_speed);
|
||||||
|
OPT_PTR(filament_cooling_final_speed);
|
||||||
OPT_PTR(filament_ramming_parameters);
|
OPT_PTR(filament_ramming_parameters);
|
||||||
OPT_PTR(gcode_comments);
|
OPT_PTR(gcode_comments);
|
||||||
OPT_PTR(gcode_flavor);
|
OPT_PTR(gcode_flavor);
|
||||||
@ -612,6 +623,7 @@ protected:
|
|||||||
OPT_PTR(cooling_tube_retraction);
|
OPT_PTR(cooling_tube_retraction);
|
||||||
OPT_PTR(cooling_tube_length);
|
OPT_PTR(cooling_tube_length);
|
||||||
OPT_PTR(parking_pos_retraction);
|
OPT_PTR(parking_pos_retraction);
|
||||||
|
OPT_PTR(extra_loading_move);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -93,6 +93,7 @@ bool PrintObject::set_copies(const Points &points)
|
|||||||
|
|
||||||
bool invalidated = this->_print->invalidate_step(psSkirt);
|
bool invalidated = this->_print->invalidate_step(psSkirt);
|
||||||
invalidated |= this->_print->invalidate_step(psBrim);
|
invalidated |= this->_print->invalidate_step(psBrim);
|
||||||
|
invalidated |= this->_print->invalidate_step(psWipeTower);
|
||||||
return invalidated;
|
return invalidated;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,7 +233,10 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
|
|||||||
|| opt_key == "perimeter_speed"
|
|| opt_key == "perimeter_speed"
|
||||||
|| opt_key == "small_perimeter_speed"
|
|| opt_key == "small_perimeter_speed"
|
||||||
|| opt_key == "solid_infill_speed"
|
|| opt_key == "solid_infill_speed"
|
||||||
|| opt_key == "top_solid_infill_speed") {
|
|| opt_key == "top_solid_infill_speed"
|
||||||
|
|| opt_key == "wipe_into_infill" // when these these two are changed, we only need to invalidate the wipe tower,
|
||||||
|
|| opt_key == "wipe_into_objects" // which we already did at the very beginning - nothing more to be done
|
||||||
|
) {
|
||||||
// these options only affect G-code export, so nothing to invalidate
|
// these options only affect G-code export, so nothing to invalidate
|
||||||
} else {
|
} else {
|
||||||
// for legacy, if we can't handle this option let's invalidate all steps
|
// for legacy, if we can't handle this option let's invalidate all steps
|
||||||
@ -272,6 +276,8 @@ bool PrintObject::invalidate_step(PrintObjectStep step)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Wipe tower depends on the ordering of extruders, which in turn depends on everything.
|
// Wipe tower depends on the ordering of extruders, which in turn depends on everything.
|
||||||
|
// It also decides about what the wipe_into_infill / wipe_into_object features will do,
|
||||||
|
// and that too depends on many of the settings.
|
||||||
invalidated |= this->_print->invalidate_step(psWipeTower);
|
invalidated |= this->_print->invalidate_step(psWipeTower);
|
||||||
return invalidated;
|
return invalidated;
|
||||||
}
|
}
|
||||||
|
@ -298,7 +298,8 @@ const std::vector<std::string>& Preset::print_options()
|
|||||||
"perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width",
|
"perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width",
|
||||||
"top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "bridge_flow_ratio", "clip_multipart_objects",
|
"top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "bridge_flow_ratio", "clip_multipart_objects",
|
||||||
"elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y",
|
"elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y",
|
||||||
"wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_bridging", "compatible_printers", "compatible_printers_condition","inherits"
|
"wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_bridging", "compatible_printers",
|
||||||
|
"compatible_printers_condition","inherits"
|
||||||
};
|
};
|
||||||
return s_opts;
|
return s_opts;
|
||||||
}
|
}
|
||||||
@ -308,10 +309,10 @@ const std::vector<std::string>& Preset::filament_options()
|
|||||||
static std::vector<std::string> s_opts {
|
static std::vector<std::string> s_opts {
|
||||||
"filament_colour", "filament_diameter", "filament_type", "filament_soluble", "filament_notes", "filament_max_volumetric_speed",
|
"filament_colour", "filament_diameter", "filament_type", "filament_soluble", "filament_notes", "filament_max_volumetric_speed",
|
||||||
"extrusion_multiplier", "filament_density", "filament_cost", "filament_loading_speed", "filament_unloading_speed", "filament_toolchange_delay",
|
"extrusion_multiplier", "filament_density", "filament_cost", "filament_loading_speed", "filament_unloading_speed", "filament_toolchange_delay",
|
||||||
"filament_ramming_parameters", "temperature", "first_layer_temperature", "bed_temperature",
|
"filament_cooling_moves", "filament_cooling_initial_speed", "filament_cooling_final_speed", "filament_ramming_parameters", "temperature",
|
||||||
"first_layer_bed_temperature", "fan_always_on", "cooling", "min_fan_speed", "max_fan_speed", "bridge_fan_speed", "disable_fan_first_layers",
|
"first_layer_temperature", "bed_temperature", "first_layer_bed_temperature", "fan_always_on", "cooling", "min_fan_speed", "max_fan_speed",
|
||||||
"fan_below_layer_time", "slowdown_below_layer_time", "min_print_speed", "start_filament_gcode", "end_filament_gcode","compatible_printers",
|
"bridge_fan_speed", "disable_fan_first_layers", "fan_below_layer_time", "slowdown_below_layer_time", "min_print_speed",
|
||||||
"compatible_printers_condition", "inherits"
|
"start_filament_gcode", "end_filament_gcode","compatible_printers", "compatible_printers_condition", "inherits"
|
||||||
};
|
};
|
||||||
return s_opts;
|
return s_opts;
|
||||||
}
|
}
|
||||||
@ -325,7 +326,7 @@ const std::vector<std::string>& Preset::printer_options()
|
|||||||
"octoprint_host", "octoprint_apikey", "octoprint_cafile", "use_firmware_retraction", "use_volumetric_e", "variable_layer_height",
|
"octoprint_host", "octoprint_apikey", "octoprint_cafile", "use_firmware_retraction", "use_volumetric_e", "variable_layer_height",
|
||||||
"single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode",
|
"single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode",
|
||||||
"between_objects_gcode", "printer_vendor", "printer_model", "printer_variant", "printer_notes", "cooling_tube_retraction",
|
"between_objects_gcode", "printer_vendor", "printer_model", "printer_variant", "printer_notes", "cooling_tube_retraction",
|
||||||
"cooling_tube_length", "parking_pos_retraction", "max_print_height", "default_print_profile", "inherits",
|
"cooling_tube_length", "parking_pos_retraction", "extra_loading_move", "max_print_height", "default_print_profile", "inherits",
|
||||||
};
|
};
|
||||||
s_opts.insert(s_opts.end(), Preset::nozzle_options().begin(), Preset::nozzle_options().end());
|
s_opts.insert(s_opts.end(), Preset::nozzle_options().begin(), Preset::nozzle_options().end());
|
||||||
}
|
}
|
||||||
|
@ -1292,6 +1292,10 @@ void TabFilament::build()
|
|||||||
optgroup->append_single_option_line("filament_loading_speed");
|
optgroup->append_single_option_line("filament_loading_speed");
|
||||||
optgroup->append_single_option_line("filament_unloading_speed");
|
optgroup->append_single_option_line("filament_unloading_speed");
|
||||||
optgroup->append_single_option_line("filament_toolchange_delay");
|
optgroup->append_single_option_line("filament_toolchange_delay");
|
||||||
|
optgroup->append_single_option_line("filament_cooling_moves");
|
||||||
|
optgroup->append_single_option_line("filament_cooling_initial_speed");
|
||||||
|
optgroup->append_single_option_line("filament_cooling_final_speed");
|
||||||
|
|
||||||
line = { _(L("Ramming")), "" };
|
line = { _(L("Ramming")), "" };
|
||||||
line.widget = [this](wxWindow* parent){
|
line.widget = [this](wxWindow* parent){
|
||||||
auto ramming_dialog_btn = new wxButton(parent, wxID_ANY, _(L("Ramming settings"))+dots, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
|
auto ramming_dialog_btn = new wxButton(parent, wxID_ANY, _(L("Ramming settings"))+dots, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
|
||||||
@ -1704,6 +1708,7 @@ void TabPrinter::build_extruder_pages(){
|
|||||||
optgroup->append_single_option_line("cooling_tube_retraction");
|
optgroup->append_single_option_line("cooling_tube_retraction");
|
||||||
optgroup->append_single_option_line("cooling_tube_length");
|
optgroup->append_single_option_line("cooling_tube_length");
|
||||||
optgroup->append_single_option_line("parking_pos_retraction");
|
optgroup->append_single_option_line("parking_pos_retraction");
|
||||||
|
optgroup->append_single_option_line("extra_loading_move");
|
||||||
m_pages.insert(m_pages.end() - n_after_single_extruder_MM, page);
|
m_pages.insert(m_pages.end() - n_after_single_extruder_MM, page);
|
||||||
m_has_single_extruder_MM_page = true;
|
m_has_single_extruder_MM_page = true;
|
||||||
}
|
}
|
||||||
@ -1757,7 +1762,6 @@ void TabPrinter::build_extruder_pages(){
|
|||||||
m_pages.begin() + n_before_extruders + m_extruders_count_old);
|
m_pages.begin() + n_before_extruders + m_extruders_count_old);
|
||||||
|
|
||||||
m_extruders_count_old = m_extruders_count;
|
m_extruders_count_old = m_extruders_count;
|
||||||
|
|
||||||
rebuild_page_tree();
|
rebuild_page_tree();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user