little update on extrusion width

* first_layer_extrusion_width moved from print to object supermerill/SuperSlicer#1225
* now unset skirt width will only use the first layer width if it's one layer high
This commit is contained in:
remi durand 2021-06-02 13:10:06 +02:00
parent c206555a96
commit 4eb7fdce58
11 changed files with 38 additions and 33 deletions

View File

@ -225,7 +225,7 @@ Flow support_material_flow(const PrintObject *object, float layer_height)
Flow support_material_1st_layer_flow(const PrintObject *object, float layer_height) Flow support_material_1st_layer_flow(const PrintObject *object, float layer_height)
{ {
const auto &width = (object->print()->config().first_layer_extrusion_width.value > 0) ? object->print()->config().first_layer_extrusion_width : object->config().support_material_extrusion_width; const auto &width = (object->config().first_layer_extrusion_width.value > 0) ? object->config().first_layer_extrusion_width : object->config().support_material_extrusion_width;
float slice_height = layer_height; float slice_height = layer_height;
if (layer_height <= 0.f && !object->print()->config().nozzle_diameter.empty()){ if (layer_height <= 0.f && !object->print()->config().nozzle_diameter.empty()){
slice_height = (float)(object->config().first_layer_height.get_abs_value(object->print()->config().nozzle_diameter.get_at(0))); slice_height = (float)(object->config().first_layer_height.get_abs_value(object->print()->config().nozzle_diameter.get_at(0)));

View File

@ -1151,7 +1151,7 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
_write_format(file, "; top infill extrusion width = %.2fmm\n", region->flow(frTopSolidInfill, layer_height, false, false, -1., *first_object).width); _write_format(file, "; top infill extrusion width = %.2fmm\n", region->flow(frTopSolidInfill, layer_height, false, false, -1., *first_object).width);
if (print.has_support_material()) if (print.has_support_material())
_write_format(file, "; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width); _write_format(file, "; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width);
if (print.config().first_layer_extrusion_width.value > 0) if (first_object->config().first_layer_extrusion_width.value > 0)
_write_format(file, "; first layer extrusion width = %.2fmm\n", region->flow(frPerimeter, first_layer_height, false, true, -1., *first_object).width); _write_format(file, "; first layer extrusion width = %.2fmm\n", region->flow(frPerimeter, first_layer_height, false, true, -1., *first_object).width);
_write_format(file, "\n"); _write_format(file, "\n");
} }

View File

@ -620,8 +620,9 @@ WipeTower::ToolChangeResult WipeTower::construct_tcr(WipeTowerWriter& writer,
WipeTower::WipeTower(const PrintConfig& config, const std::vector<std::vector<float>>& wiping_matrix, size_t initial_tool) : WipeTower::WipeTower(const PrintConfig& config, const PrintObjectConfig& object_config, const std::vector<std::vector<float>>& wiping_matrix, size_t initial_tool) :
m_config(&config), m_config(&config),
m_object_config(&object_config),
m_semm(config.single_extruder_multi_material.value), m_semm(config.single_extruder_multi_material.value),
m_wipe_tower_pos(config.wipe_tower_x, config.wipe_tower_y), m_wipe_tower_pos(config.wipe_tower_x, config.wipe_tower_y),
m_wipe_tower_width(float(config.wipe_tower_width)), m_wipe_tower_width(float(config.wipe_tower_width)),
@ -932,7 +933,7 @@ WipeTower::ToolChangeResult WipeTower::toolchange_Brim(bool sideOnly, float y_of
m_wipe_tower_width, m_wipe_tower_width,
m_wipe_tower_depth); m_wipe_tower_depth);
double unscaled_brim_width = m_config->wipe_tower_brim.get_abs_value(m_nozzle_diameter); double unscaled_brim_width = m_config->wipe_tower_brim.get_abs_value(m_nozzle_diameter);
Slic3r::Flow brim_flow = Flow::new_from_config_width(FlowRole::frPerimeter, m_config->first_layer_extrusion_width, m_nozzle_diameter, m_layer_height); Slic3r::Flow brim_flow = Flow::new_from_config_width(FlowRole::frPerimeter, m_object_config->first_layer_extrusion_width, m_nozzle_diameter, m_layer_height);
WipeTowerWriter writer(m_layer_height, brim_flow.width, m_gcode_flavor, m_filpar); WipeTowerWriter writer(m_layer_height, brim_flow.width, m_gcode_flavor, m_filpar);
writer.set_extrusion_flow(brim_flow.mm3_per_mm()) writer.set_extrusion_flow(brim_flow.mm3_per_mm())

View File

@ -14,6 +14,7 @@ namespace Slic3r
class WipeTowerWriter; class WipeTowerWriter;
class PrintConfig; class PrintConfig;
class PrintObjectConfig;
enum GCodeFlavor : unsigned char; enum GCodeFlavor : unsigned char;
@ -94,7 +95,7 @@ public:
// y -- y coordinates of wipe tower in mm ( left bottom corner ) // y -- y coordinates of wipe tower in mm ( left bottom corner )
// width -- width of wipe tower in mm ( default 60 mm - leave as it is ) // 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
WipeTower(const PrintConfig& config, const std::vector<std::vector<float>>& wiping_matrix, size_t initial_tool); WipeTower(const PrintConfig& config, const PrintObjectConfig& object_config, const std::vector<std::vector<float>>& wiping_matrix, size_t initial_tool);
// Set the extruder properties. // Set the extruder properties.
void set_extruder(size_t idx); void set_extruder(size_t idx);
@ -229,7 +230,8 @@ private:
return m_filpar[0].filament_area; // all extruders are assumed to have the same filament diameter at this point return m_filpar[0].filament_area; // all extruders are assumed to have the same filament diameter at this point
} }
const PrintConfig *m_config; const PrintConfig* m_config;
const PrintObjectConfig* m_object_config;
bool m_semm = true; // Are we using a single extruder multimaterial printer? bool m_semm = true; // Are we using a single extruder multimaterial printer?
Vec2f m_wipe_tower_pos; // Left front corner of the wipe tower in mm. Vec2f m_wipe_tower_pos; // Left front corner of the wipe tower in mm.
float m_wipe_tower_width; // Width of the wipe tower. float m_wipe_tower_width; // Width of the wipe tower.

View File

@ -1652,13 +1652,13 @@ double Print::skirt_first_layer_height() const
return m_objects.front()->config().get_abs_value("first_layer_height"); return m_objects.front()->config().get_abs_value("first_layer_height");
} }
Flow Print::brim_flow(size_t extruder_id) const Flow Print::brim_flow(size_t extruder_id, const PrintObjectConfig& brim_config) const
{ {
ConfigOptionFloatOrPercent width = m_config.first_layer_extrusion_width; ConfigOptionFloatOrPercent width = brim_config.first_layer_extrusion_width;
if (width.value <= 0) if (width.value <= 0)
width = m_regions.front()->config().perimeter_extrusion_width; width = m_default_region_config.perimeter_extrusion_width;
if (width.value <= 0) if (width.value <= 0)
width = m_objects.front()->config().extrusion_width; width = brim_config.extrusion_width;
/* We currently use a random region's perimeter extruder. /* We currently use a random region's perimeter extruder.
While this works for most cases, we should probably consider all of the perimeter While this works for most cases, we should probably consider all of the perimeter
@ -1676,12 +1676,13 @@ Flow Print::brim_flow(size_t extruder_id) const
Flow Print::skirt_flow(size_t extruder_id) const Flow Print::skirt_flow(size_t extruder_id) const
{ {
ConfigOptionFloatOrPercent width = m_config.skirt_extrusion_width; ConfigOptionFloatOrPercent width = m_config.skirt_extrusion_width;
if (width.value <= 0 && m_config.first_layer_extrusion_width.value > 0) if (width.value <= 0 && m_default_object_config.first_layer_extrusion_width.value > 0
width = m_config.first_layer_extrusion_width; && m_config.skirt_height == 1 && !m_config.draft_shield)
width = m_default_object_config.first_layer_extrusion_width;
if (width.value <= 0) if (width.value <= 0)
width = m_regions.front()->config().perimeter_extrusion_width; width = m_default_region_config.perimeter_extrusion_width;
if (width.value <= 0) if (width.value <= 0)
width = m_objects.front()->config().extrusion_width; width = m_default_object_config.extrusion_width;
/* We currently use a random object's support material extruder. /* We currently use a random object's support material extruder.
While this works for most cases, we should probably consider all of the support material While this works for most cases, we should probably consider all of the support material
@ -1791,7 +1792,8 @@ void Print::process()
&& obj_group.front()->config().brim_inside_holes.value == obj->config().brim_inside_holes.value && obj_group.front()->config().brim_inside_holes.value == obj->config().brim_inside_holes.value
&& obj_group.front()->config().brim_offset.value == obj->config().brim_offset.value && obj_group.front()->config().brim_offset.value == obj->config().brim_offset.value
&& obj_group.front()->config().brim_width.value == obj->config().brim_width.value && obj_group.front()->config().brim_width.value == obj->config().brim_width.value
&& obj_group.front()->config().brim_width_interior.value == obj->config().brim_width_interior.value) { && obj_group.front()->config().brim_width_interior.value == obj->config().brim_width_interior.value
&& obj_group.front()->config().first_layer_extrusion_width.value == obj->config().first_layer_extrusion_width.value) {
added = true; added = true;
obj_group.push_back(obj); obj_group.push_back(obj);
} }
@ -1824,7 +1826,7 @@ void Print::process()
std::vector<uint16_t> set_extruders = this->object_extruders({ obj }); std::vector<uint16_t> set_extruders = this->object_extruders({ obj });
append(set_extruders, this->support_material_extruders()); append(set_extruders, this->support_material_extruders());
sort_remove_duplicates(set_extruders); sort_remove_duplicates(set_extruders);
Flow flow = this->brim_flow(set_extruders.empty() ? m_regions.front()->config().perimeter_extruder - 1 : set_extruders.front()); Flow flow = this->brim_flow(set_extruders.empty() ? m_regions.front()->config().perimeter_extruder - 1 : set_extruders.front(), obj->config());
//don't consider other objects/instances. It's not possible because it's duplicated by some code afterward... i think. //don't consider other objects/instances. It's not possible because it's duplicated by some code afterward... i think.
brim_area.clear(); brim_area.clear();
//create a brim "pattern" (one per object) //create a brim "pattern" (one per object)
@ -1849,7 +1851,7 @@ void Print::process()
std::vector<uint16_t> set_extruders = this->object_extruders(m_objects); std::vector<uint16_t> set_extruders = this->object_extruders(m_objects);
append(set_extruders, this->support_material_extruders()); append(set_extruders, this->support_material_extruders());
sort_remove_duplicates(set_extruders); sort_remove_duplicates(set_extruders);
Flow flow = this->brim_flow(set_extruders.empty() ? m_regions.front()->config().perimeter_extruder - 1 : set_extruders.front()); Flow flow = this->brim_flow(set_extruders.empty() ? m_regions.front()->config().perimeter_extruder - 1 : set_extruders.front(), obj_group.front()->config());
if (brim_config.brim_ears) if (brim_config.brim_ears)
this->_make_brim_ears(flow, obj_group, brim_area, m_brim); this->_make_brim_ears(flow, obj_group, brim_area, m_brim);
else else
@ -1969,7 +1971,6 @@ void Print::_make_skirt(const PrintObjectPtrs &objects, ExtrusionEntityCollectio
extruders_e_per_mm.reserve(set_extruders.size()); extruders_e_per_mm.reserve(set_extruders.size());
for (unsigned int extruder_id : set_extruders) { for (unsigned int extruder_id : set_extruders) {
Flow flow = this->skirt_flow(extruder_id); Flow flow = this->skirt_flow(extruder_id);
float spacing = flow.spacing();
double mm3_per_mm = flow.mm3_per_mm(); double mm3_per_mm = flow.mm3_per_mm();
extruders.push_back(extruder_id); extruders.push_back(extruder_id);
extruders_e_per_mm.push_back(Extruder((unsigned int)extruder_id, &m_config).e_per_mm(mm3_per_mm)); extruders_e_per_mm.push_back(Extruder((unsigned int)extruder_id, &m_config).e_per_mm(mm3_per_mm));
@ -2753,7 +2754,7 @@ void Print::_make_wipe_tower()
this->throw_if_canceled(); this->throw_if_canceled();
// Initialize the wipe tower. // Initialize the wipe tower.
WipeTower wipe_tower(m_config, wipe_volumes, m_wipe_tower_data.tool_ordering.first_extruder()); WipeTower wipe_tower(m_config, m_default_object_config, wipe_volumes, m_wipe_tower_data.tool_ordering.first_extruder());
//wipe_tower.set_retract(); //wipe_tower.set_retract();

View File

@ -429,7 +429,7 @@ public:
// Returns an empty string if valid, otherwise returns an error message. // Returns an empty string if valid, otherwise returns an error message.
std::pair<PrintValidationError, std::string> validate() const override; std::pair<PrintValidationError, std::string> validate() const override;
double skirt_first_layer_height() const; double skirt_first_layer_height() const;
Flow brim_flow(size_t extruder_id) const; Flow brim_flow(size_t extruder_id, const PrintObjectConfig &brim_config) const;
Flow skirt_flow(size_t extruder_id) const; Flow skirt_flow(size_t extruder_id) const;
std::vector<uint16_t> object_extruders(const PrintObjectPtrs &objects) const; std::vector<uint16_t> object_extruders(const PrintObjectPtrs &objects) const;

View File

@ -3240,7 +3240,9 @@ void PrintConfigDef::init_fff_params()
def->label = L("Skirt"); def->label = L("Skirt");
def->full_label = L("Skirt width"); def->full_label = L("Skirt width");
def->category = OptionCategory::width; def->category = OptionCategory::width;
def->tooltip = L("Horizontal width of the skirt that will be printed around each object."); def->tooltip = L("Horizontal width of the skirt that will be printed around each object."
" If left zero, first layer extrusion width will be used if set and the skirt is only 1 layer height"
", or perimeter extrusion width will be used (using the computed value if not set).");
def->sidetext = L("mm"); def->sidetext = L("mm");
def->min = 0; def->min = 0;
def->max = 1000; def->max = 1000;
@ -5653,7 +5655,7 @@ double PrintConfig::min_object_distance(const ConfigBase *config, double ref_hei
//add the skirt //add the skirt
if (config->option("skirts")->getInt() > 0 && config->option("skirt_height")->getInt() == 1 && ref_height == 0) { if (config->option("skirts")->getInt() > 0 && config->option("skirt_height")->getInt() == 1 && ref_height == 0) {
skirt_dist = config->option("skirt_distance")->getFloat(); skirt_dist = config->option("skirt_distance")->getFloat();
const double first_layer_width = config->get_abs_value("first_layer_extrusion_width"); const double first_layer_width = config->get_abs_value("skirt_extrusion_width");
Flow flow(first_layer_width, first_layer_height, max_nozzle_diam); Flow flow(first_layer_width, first_layer_height, max_nozzle_diam);
skirt_dist += first_layer_width + (flow.spacing() * ((double)config->option("skirts")->getInt() - 1)); skirt_dist += first_layer_width + (flow.spacing() * ((double)config->option("skirts")->getInt() - 1));
base_dist = std::max(base_dist, skirt_dist + 1); base_dist = std::max(base_dist, skirt_dist + 1);
@ -5662,7 +5664,7 @@ double PrintConfig::min_object_distance(const ConfigBase *config, double ref_hei
double skirt_height = ((double)config->option("skirt_height")->getInt() - 1) * config->get_abs_value("layer_height") + first_layer_height; double skirt_height = ((double)config->option("skirt_height")->getInt() - 1) * config->get_abs_value("layer_height") + first_layer_height;
if (ref_height <= skirt_height) { if (ref_height <= skirt_height) {
skirt_dist = config->option("skirt_distance")->getFloat(); skirt_dist = config->option("skirt_distance")->getFloat();
const double first_layer_width = config->get_abs_value("first_layer_extrusion_width"); const double first_layer_width = config->get_abs_value("skirt_extrusion_width");
Flow flow(first_layer_width, first_layer_height, max_nozzle_diam); Flow flow(first_layer_width, first_layer_height, max_nozzle_diam);
skirt_dist += first_layer_width + (flow.spacing() * ((double)config->option("skirts")->getInt() - 1)); skirt_dist += first_layer_width + (flow.spacing() * ((double)config->option("skirts")->getInt() - 1));
//std::cout << " Set skirt_dist=" << config->option("skirt_distance")->getFloat() << " => " << skirt_dist << "\n"; //std::cout << " Set skirt_dist=" << config->option("skirt_distance")->getFloat() << " => " << skirt_dist << "\n";

View File

@ -621,6 +621,7 @@ public:
ConfigOptionBool exact_last_layer_height; ConfigOptionBool exact_last_layer_height;
ConfigOptionFloatOrPercent extrusion_width; ConfigOptionFloatOrPercent extrusion_width;
ConfigOptionFloatOrPercent first_layer_height; ConfigOptionFloatOrPercent first_layer_height;
ConfigOptionFloatOrPercent first_layer_extrusion_width;
ConfigOptionFloat first_layer_size_compensation; ConfigOptionFloat first_layer_size_compensation;
ConfigOptionFloat hole_size_compensation; ConfigOptionFloat hole_size_compensation;
ConfigOptionFloat hole_size_threshold; ConfigOptionFloat hole_size_threshold;
@ -690,6 +691,7 @@ protected:
OPT_PTR(hole_size_compensation); OPT_PTR(hole_size_compensation);
OPT_PTR(hole_size_threshold); OPT_PTR(hole_size_threshold);
OPT_PTR(first_layer_height); OPT_PTR(first_layer_height);
OPT_PTR(first_layer_extrusion_width);
OPT_PTR(first_layer_size_compensation); OPT_PTR(first_layer_size_compensation);
OPT_PTR(infill_only_where_needed); OPT_PTR(infill_only_where_needed);
OPT_PTR(interface_shells); OPT_PTR(interface_shells);
@ -1277,7 +1279,6 @@ public:
ConfigOptionPercents filament_shrink; ConfigOptionPercents filament_shrink;
ConfigOptionFloatOrPercent first_layer_acceleration; ConfigOptionFloatOrPercent first_layer_acceleration;
ConfigOptionInts first_layer_bed_temperature; ConfigOptionInts first_layer_bed_temperature;
ConfigOptionFloatOrPercent first_layer_extrusion_width;
ConfigOptionPercent first_layer_flow_ratio; ConfigOptionPercent first_layer_flow_ratio;
ConfigOptionFloatOrPercent first_layer_speed; ConfigOptionFloatOrPercent first_layer_speed;
ConfigOptionFloatOrPercent first_layer_infill_speed; ConfigOptionFloatOrPercent first_layer_infill_speed;
@ -1346,7 +1347,8 @@ protected:
this->GCodeConfig::initialize(cache, base_ptr); this->GCodeConfig::initialize(cache, base_ptr);
OPT_PTR(allow_empty_layers); OPT_PTR(allow_empty_layers);
OPT_PTR(avoid_crossing_perimeters); OPT_PTR(avoid_crossing_perimeters);
OPT_PTR(avoid_crossing_not_first_layer); OPT_PTR(avoid_crossing_perimeters_max_detour); OPT_PTR(avoid_crossing_not_first_layer);
OPT_PTR(avoid_crossing_perimeters_max_detour);
OPT_PTR(bed_shape); OPT_PTR(bed_shape);
OPT_PTR(bed_temperature); OPT_PTR(bed_temperature);
OPT_PTR(bridge_acceleration); OPT_PTR(bridge_acceleration);
@ -1372,7 +1374,6 @@ protected:
OPT_PTR(filament_shrink); OPT_PTR(filament_shrink);
OPT_PTR(first_layer_acceleration); OPT_PTR(first_layer_acceleration);
OPT_PTR(first_layer_bed_temperature); OPT_PTR(first_layer_bed_temperature);
OPT_PTR(first_layer_extrusion_width);
OPT_PTR(first_layer_flow_ratio); OPT_PTR(first_layer_flow_ratio);
OPT_PTR(first_layer_speed); OPT_PTR(first_layer_speed);
OPT_PTR(first_layer_infill_speed); OPT_PTR(first_layer_infill_speed);

View File

@ -746,7 +746,6 @@ namespace Slic3r {
|| opt_key == "support_material_threshold" || opt_key == "support_material_threshold"
|| opt_key == "support_material_with_sheath" || opt_key == "support_material_with_sheath"
|| opt_key == "dont_support_bridges" || opt_key == "dont_support_bridges"
|| opt_key == "first_layer_extrusion_width"
|| opt_key == "support_material_solid_first_layer") { || opt_key == "support_material_solid_first_layer") {
steps.emplace_back(posSupportMaterial); steps.emplace_back(posSupportMaterial);
} else if (opt_key == "bottom_solid_layers") { } else if (opt_key == "bottom_solid_layers") {
@ -788,7 +787,6 @@ namespace Slic3r {
|| opt_key == "fill_top_flow_ratio" || opt_key == "fill_top_flow_ratio"
|| opt_key == "fill_smooth_width" || opt_key == "fill_smooth_width"
|| opt_key == "fill_smooth_distribution" || opt_key == "fill_smooth_distribution"
|| opt_key == "first_layer_extrusion_width"
|| opt_key == "infill_anchor" || opt_key == "infill_anchor"
|| opt_key == "infill_anchor_max" || opt_key == "infill_anchor_max"
|| opt_key == "infill_connection" || opt_key == "infill_connection"
@ -802,7 +800,6 @@ namespace Slic3r {
|| opt_key == "extra_perimeters_odd_layers" || opt_key == "extra_perimeters_odd_layers"
|| opt_key == "external_infill_margin" || opt_key == "external_infill_margin"
|| opt_key == "external_perimeter_overlap" || opt_key == "external_perimeter_overlap"
|| opt_key == "first_layer_extrusion_width"
|| opt_key == "gap_fill_overlap" || opt_key == "gap_fill_overlap"
|| opt_key == "no_perimeter_unsupported_algo" || opt_key == "no_perimeter_unsupported_algo"
|| opt_key == "perimeters" || opt_key == "perimeters"
@ -815,7 +812,8 @@ namespace Slic3r {
|| opt_key == "perimeter_extruder") { || opt_key == "perimeter_extruder") {
steps.emplace_back(posPerimeters); steps.emplace_back(posPerimeters);
steps.emplace_back(posSupportMaterial); steps.emplace_back(posSupportMaterial);
} else if (opt_key == "bridge_flow_ratio") { } else if (opt_key == "bridge_flow_ratio"
|| opt_key == "first_layer_extrusion_width") {
//if (m_config.support_material_contact_distance > 0.) { //if (m_config.support_material_contact_distance > 0.) {
// Only invalidate due to bridging if bridging is enabled. // Only invalidate due to bridging if bridging is enabled.
// If later "support_material_contact_distance" is modified, the complete PrintObject is invalidated anyway. // If later "support_material_contact_distance" is modified, the complete PrintObject is invalidated anyway.

View File

@ -28,8 +28,8 @@ Flow PrintRegion::flow(FlowRole role, double layer_height, bool bridge, bool fir
} else { } else {
// otherwise, get extrusion width from configuration // otherwise, get extrusion width from configuration
// (might be an absolute value, or a percent value, or zero for auto) // (might be an absolute value, or a percent value, or zero for auto)
if (first_layer && m_print->config().first_layer_extrusion_width.value > 0) { if (first_layer && object.config().first_layer_extrusion_width.value > 0) {
config_width = m_print->config().first_layer_extrusion_width; config_width = object.config().first_layer_extrusion_width;
} else if (role == frExternalPerimeter) { } else if (role == frExternalPerimeter) {
config_width = m_config.external_perimeter_extrusion_width; config_width = m_config.external_perimeter_extrusion_width;
} else if (role == frPerimeter) { } else if (role == frPerimeter) {

View File

@ -1978,7 +1978,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
"complete_objects_sort", "complete_objects_sort",
"complete_objects_one_skirt", "complete_objects_one_skirt",
"duplicate_distance", "extruder_clearance_radius", "duplicate_distance", "extruder_clearance_radius",
"first_layer_extrusion_width", "skirt_extrusion_width",
"skirts", "skirt_distance", "skirt_height", "skirts", "skirt_distance", "skirt_height",
"brim_width", "variable_layer_height", "nozzle_diameter", "single_extruder_multi_material", "brim_width", "variable_layer_height", "nozzle_diameter", "single_extruder_multi_material",
"wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle",