diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
index 0eb36a64c..98efa899a 100644
--- a/.github/ISSUE_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE.md
@@ -1,13 +1,8 @@
### Version
-_Version of SupserSlicer used goes here_
-
-_Use `About->About SuperSlicer` for release versions_
-
-_For -dev versions, use `git describe --tag` or get the hash value for the version you downloaded or `git rev-parse HEAD`_
+_Version of SupserSlicer used goes here, Use `About->About SuperSlicer`_
### Operating system type + version
-_What OS are you using, and state any version #s_
-_In case of 3D rendering issues, please attach the content of menu Help -> System Info dialog_
+_What OS are you using (and version)_
### 3D printer brand / version + firmware version (if known)
_What 3D printer brand / version are you printing on, is it a stock model or did you modify the printer, what firmware is running on your printer, version of the firmware #s_
@@ -20,7 +15,5 @@ _What 3D printer brand / version are you printing on, is it a stock model or did
* _Actual Results_
* _Screenshots from __*SuperSlicer*__ preview are preferred_
-_Is this a new feature request?_
-
#### Project File (.3MF) where problem occurs
-_Upload a SuperSlicer Project File (.3MF) (`Plater -> Export plate as 3MF` for Slic3r PE 1.41.2 and older, `File -> Save` / `Save Project` for SuperSlicer, Slic3r PE 1.42.0-alpha and newer)_
+_Upload a SuperSlicer Project File (.3MF) (`File -> Save` / `Save Project`)_
diff --git a/resources/calibration/retraction/retraction.html b/resources/calibration/retraction/retraction.html
index 4beeeab61..5570dfc2b 100644
--- a/resources/calibration/retraction/retraction.html
+++ b/resources/calibration/retraction/retraction.html
@@ -35,7 +35,7 @@ This calibration will help you to choose the right retraction length for your ex
Remove filament slowdown
This button will change your settings in filament/cooling to disable all features that trigger when a layer is too small to be able to cool down in time. With these algorithms disabled, your print will be more challenging in terms of retraction, so it's a good stress test. To re-enable them, remove all modifications from the filament tab (the little orange 'back arrow").
Results
-When the test pieces are printed, you can count the millimetre with the ridges on the sides of the print towers. I count them with my nail. When you're at the height where there are no more strings, you can write down the millimetre number. Multiply it by your step parameter (0.2, 0.2, 0.5 or 1) and you have your retraction length number. You can add 20% more length to have a little margin.
+When the test pieces are printed, you can count the millimetre with the ridges on the sides of the print towers. I count them with my nail. When you're at the height where there are no more strings, you can write down the millimetre number. Multiply it by your step parameter (0.1, 0.2, 0.5 or 1) and you have your retraction length number. You can add 20% more length to have a little margin.
If you have multiple prints with different temperatures, you need to choose the best print, the one with the least amount of stringing, unless the decrease in stringing isn't big enough to warrant the decrease in temperature.
Example
diff --git a/resources/ui_layout/print.ui b/resources/ui_layout/print.ui
index ccfaccdff..948cd6ff3 100644
--- a/resources/ui_layout/print.ui
+++ b/resources/ui_layout/print.ui
@@ -40,9 +40,9 @@ group:Quality
setting:thin_walls_merge
end_line
group:Overhangs
- line:On perimeters
- setting:sidetext_width$1:overhangs
- setting:width$5:overhangs_width
+ line:threshold for
+ setting:label$bridge speed and fan:width$5:overhangs_width_speed
+ setting:label$bridge flow:width$5:overhangs_width
end_line
line:Extrusion direction
setting:sidetext_width$1:overhangs_reverse
@@ -202,7 +202,6 @@ group:label_width$8:Speed for print moves
line:Perimeter speed
setting:width$4:perimeter_speed
setting:width$4:external_perimeter_speed
- setting:width$4:small_perimeter_speed
end_line
line:Infill speed
setting:width$4:infill_speed
@@ -228,6 +227,10 @@ group:Modifiers
setting:label_width$8:width$4:first_layer_speed
setting:label_width$8:width$4:first_layer_infill_speed
end_line
+ line:Small perimeter speed
+ setting:label_width$8:width$4:small_perimeter_min_length
+ setting:label_width$8:width$4:small_perimeter_max_length
+ setting:label_width$8:width$4:small_perimeter_speed
group:Acceleration control (advanced)
setting:perimeter_acceleration
setting:infill_acceleration
diff --git a/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp
index 0896e5466..77764a6da 100644
--- a/src/libslic3r/Fill/FillBase.cpp
+++ b/src/libslic3r/Fill/FillBase.cpp
@@ -598,7 +598,8 @@ Fill::do_gap_fill(const ExPolygons &gapfill_areas, const FillParams ¶ms, Ext
// offset2_ex(gapfill_areas, double(-max / 2), double(+max / 2)),
// true);
ExPolygons gapfill_areas_collapsed = offset2_ex(gapfill_areas, double(-min / 2), double(+min / 2));
- const double minarea = scale_(params.config->gap_fill_min_area.get_abs_value(params.flow.width) ) * params.flow.scaled_width();
+ double minarea = params.flow.scaled_width() * params.flow.scaled_width();
+ if (params.config != nullptr) minarea = scale_(params.config->gap_fill_min_area.get_abs_value(params.flow.width)) * params.flow.scaled_width();
for (const ExPolygon &ex : gapfill_areas_collapsed) {
//remove too small gaps that are too hard to fill.
//ie one that are smaller than an extrusion with width of min and a length of max.
@@ -620,10 +621,10 @@ Fill::do_gap_fill(const ExPolygons &gapfill_areas, const FillParams ¶ms, Ext
ExtrusionEntityCollection gap_fill = thin_variable_width(polylines_gapfill, erGapFill, params.flow);
//set role if needed
- if (params.role != erSolidInfill) {
+ /*if (params.role != erSolidInfill) {
ExtrusionSetRole set_good_role(params.role);
gap_fill.visit(set_good_role);
- }
+ }*/
//move them into the collection
if (!gap_fill.entities.empty()) {
ExtrusionEntityCollection *coll_gapfill = new ExtrusionEntityCollection();
diff --git a/src/libslic3r/Fill/FillConcentric.cpp b/src/libslic3r/Fill/FillConcentric.cpp
index d109428cd..2ddd2471b 100644
--- a/src/libslic3r/Fill/FillConcentric.cpp
+++ b/src/libslic3r/Fill/FillConcentric.cpp
@@ -174,7 +174,8 @@ FillConcentricWGapFill::fill_surface_extrusion(
ExPolygons gapfill_areas = diff_ex({ surface->expolygon }, offset_ex(expp, double(scale_(0.5 * this->get_spacing()))));
gapfill_areas = union_ex(gapfill_areas, true);
if (gapfill_areas.size() > 0) {
- const double minarea = scale_(params.config->gap_fill_min_area.get_abs_value(params.flow.width)) * params.flow.scaled_width();
+ double minarea = params.flow.scaled_width() * params.flow.scaled_width();
+ if (params.config != nullptr) minarea = scale_(params.config->gap_fill_min_area.get_abs_value(params.flow.width)) * params.flow.scaled_width();
for (int i = 0; i < gapfill_areas.size(); i++) {
if (gapfill_areas[i].area() < minarea) {
gapfill_areas.erase(gapfill_areas.begin() + i);
diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp
index 53883f03b..e64c2013d 100644
--- a/src/libslic3r/GCode.cpp
+++ b/src/libslic3r/GCode.cpp
@@ -1490,7 +1490,6 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
print.throw_if_canceled();
m_cooling_buffer->set_current_extruder(initial_extruder_id);
- m_writer.toolchange(initial_extruder_id);
// Emit machine envelope limits for the Marlin firmware.
this->print_machine_envelope(file, print);
@@ -1498,7 +1497,7 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
// Disable fan.
if ( print.config().disable_fan_first_layers.get_at(initial_extruder_id)
&& config().gcode_flavor != gcfKlipper)
- _write(file, m_writer.set_fan(0, true));
+ _write(file, m_writer.set_fan(0, true, initial_extruder_id));
// Let the start-up script prime the 1st printing tool.
m_placeholder_parser.set("initial_tool", initial_extruder_id);
@@ -1946,7 +1945,7 @@ void GCode::print_machine_envelope(FILE *file, Print &print)
int(print.config().machine_max_acceleration_z.values.front() + 0.5),
int(print.config().machine_max_acceleration_e.values.front() + 0.5));
if (std::set{gcfRepetier}.count(print.config().gcode_flavor.value) > 0)
- fprintf(file, "M202 X%d Y%d ; sets maximum travel speed\n",
+ fprintf(file, "M202 X%d Y%d ; sets maximum travel acceleration\n",
int(print.config().machine_max_acceleration_travel.values.front() + 0.5),
int(print.config().machine_max_acceleration_travel.values.front() + 0.5));
if (std::set{gcfMarlin, gcfLerdge, gcfRepetier, gcfRepRap, gcfSmoothie, gcfSprinter}.count(print.config().gcode_flavor.value) > 0)
@@ -2400,7 +2399,6 @@ void GCode::process_layer(
// In single extruder multi material mode, set the temperature for the current extruder only.
continue;
int temperature = print.config().temperature.get_at(extruder.id());
- if (temperature > 0 && temperature != print.config().first_layer_temperature.get_at(extruder.id()))
gcode += m_writer.set_temperature(temperature, false, extruder.id());
}
gcode += m_writer.set_bed_temperature(print.config().bed_temperature.get_at(first_extruder_id));
@@ -2698,6 +2696,9 @@ void GCode::process_layer(
this->set_origin(unscale(offset));
if (instance_to_print.object_by_extruder.support != nullptr && !print_wipe_extrusions) {
m_layer = layers[instance_to_print.layer_id].support_layer;
+ if (m_layer != nullptr && m_layer->bottom_z() < EPSILON)
+ gcode += m_writer.set_temperature(m_config.first_layer_temperature.get_at(m_writer.tool()->id()), false, m_writer.tool()->id());
+ else
gcode += m_writer.set_temperature(m_config.temperature.get_at(m_writer.tool()->id()), false, m_writer.tool()->id());
gcode += this->extrude_support(
// support_extrusion_role is erSupportMaterial, erSupportMaterialInterface or erMixed for all extrusion paths.
@@ -2861,7 +2862,8 @@ void GCode::append_full_config(const Print &print, std::string &str)
"compatible_prints",
"print_host",
"printhost_apikey",
- "printhost_cafile"
+ "printhost_cafile",
+ "printhost_slug"
};
assert(std::is_sorted(banned_keys.begin(), banned_keys.end()));
auto is_banned = [banned_keys](const std::string &key) {
@@ -2985,8 +2987,16 @@ std::string GCode::extrude_loop_vase(const ExtrusionLoop &original_loop, const s
if (paths.empty()) return "";
// apply the small/external? perimeter speed
- if (is_perimeter(paths.front().role()) && loop.length() <= SMALL_PERIMETER_LENGTH && speed == -1)
- speed = m_config.external_perimeter_speed.get_abs_value(m_config.perimeter_speed);
+ if (speed == -1 && is_perimeter(paths.front().role()) && loop.length() <=
+ scale_(this->m_config.small_perimeter_max_length.get_abs_value(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0)))) {
+ double min_length = scale_(this->m_config.small_perimeter_min_length.get_abs_value(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0)));
+ double max_length = scale_(this->m_config.small_perimeter_max_length.get_abs_value(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0)));
+ if (loop.length() <= min_length) {
+ speed = m_config.small_perimeter_speed.get_abs_value(m_config.perimeter_speed);
+ } else {
+ speed = - (loop.length() - min_length) / (max_length - min_length);
+ }
+ }
//get extrusion length
coordf_t length = 0;
@@ -3295,9 +3305,17 @@ std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::s
if (paths.empty()) return "";
// apply the small perimeter speed
- if (is_perimeter(paths.front().role()) && loop.length() <= SMALL_PERIMETER_LENGTH && speed == -1)
+ if (speed == -1 && is_perimeter(paths.front().role()) && loop.length() <=
+ scale_(this->m_config.small_perimeter_max_length.get_abs_value(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0)))) {
+ double min_length = scale_(this->m_config.small_perimeter_min_length.get_abs_value(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0)));
+ double max_length = scale_(this->m_config.small_perimeter_max_length.get_abs_value(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0)));
+ if (loop.length() <= min_length) {
speed = m_config.small_perimeter_speed.get_abs_value(m_config.perimeter_speed);
-
+ } else {
+ speed = -(loop.length() - min_length) / (max_length - min_length);
+ }
+ }
+
// extrude along the path
std::string gcode;
for (ExtrusionPaths::iterator path = paths.begin(); path != paths.end(); ++path) {
@@ -3574,6 +3592,8 @@ std::string GCode::extrude_perimeters(const Print &print, const std::vectorconfig());
if (m_config.print_temperature > 0)
gcode += m_writer.set_temperature(m_config.print_temperature.value, false, m_writer.tool()->id());
+ else if (m_layer != nullptr && m_layer->bottom_z() < EPSILON)
+ gcode += m_writer.set_temperature(m_config.first_layer_temperature.get_at(m_writer.tool()->id()), false, m_writer.tool()->id());
else
gcode += m_writer.set_temperature(m_config.temperature.get_at(m_writer.tool()->id()), false, m_writer.tool()->id());
for (const ExtrusionEntity *ee : region.perimeters)
@@ -3600,6 +3620,8 @@ std::string GCode::extrude_infill(const Print &print, const std::vectorconfig());
if (m_config.print_temperature > 0)
gcode += m_writer.set_temperature(m_config.print_temperature.value, false, m_writer.tool()->id());
+ else if(m_layer!=nullptr && m_layer->bottom_z() < EPSILON)
+ gcode += m_writer.set_temperature(m_config.first_layer_temperature.get_at(m_writer.tool()->id()), false, m_writer.tool()->id());
else
gcode += m_writer.set_temperature(m_config.temperature.get_at(m_writer.tool()->id()), false, m_writer.tool()->id());
ExtrusionEntitiesPtr extrusions { region.infills };
@@ -3745,7 +3767,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string &descri
double e_per_mm = path.mm3_per_mm
* m_writer.tool()->e_per_mm3()
* this->config().print_extrusion_multiplier.get_abs_value(1);
- if (std::abs(this->m_layer->height - this->m_layer->print_z) < EPSILON) e_per_mm *= this->config().first_layer_flow_ratio.get_abs_value(1);
+ if (m_layer->bottom_z() < EPSILON) e_per_mm *= this->config().first_layer_flow_ratio.get_abs_value(1);
if (m_writer.extrusion_axis().empty()) e_per_mm = 0;
if (path.polyline.lines().size() > 0) {
//get last direction //TODO: save it
@@ -3889,7 +3911,10 @@ std::string GCode::_before_extrude(const ExtrusionPath &path, const std::string
// set speed
- if (speed == -1) {
+ if (speed < 0) {
+ //if speed == -1, then it's means "choose yourself, but if it's -1 < speed <0 , then it's a scaling from small_periemter.
+ //it's a bit hacky, so if you want to rework it, help yourself.
+ float factor = (-speed);
if (path.role() == erPerimeter) {
speed = m_config.get_abs_value("perimeter_speed");
} else if (path.role() == erExternalPerimeter) {
@@ -3915,6 +3940,12 @@ std::string GCode::_before_extrude(const ExtrusionPath &path, const std::string
} else {
throw Slic3r::InvalidArgument("Invalid speed");
}
+ //don't modify bridge speed
+ if (factor < 1 && !(path.role() == erOverhangPerimeter || path.role() == erBridgeInfill)) {
+ float small_speed = m_config.small_perimeter_speed.get_abs_value(m_config.perimeter_speed);
+ //apply factor between feature speed and small speed
+ speed = speed * factor + (1 - factor) * small_speed;
+ }
}
if (m_volumetric_speed != 0. && speed == 0)
speed = m_volumetric_speed / path.mm3_per_mm;
diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp
index fbb3e765a..7f1752626 100644
--- a/src/libslic3r/GCodeWriter.cpp
+++ b/src/libslic3r/GCodeWriter.cpp
@@ -31,6 +31,44 @@ void GCodeWriter::apply_print_region_config(const PrintRegionConfig& print_regio
config_region = &print_region_config;
}
+std::vector GCodeWriter::extruder_ids() const {
+ std::vector out;
+ out.reserve(m_extruders.size());
+ for (const Extruder& e : m_extruders)
+ out.push_back(e.id());
+ return out;
+}
+
+std::vector GCodeWriter::mill_ids() const {
+ std::vector out;
+ out.reserve(m_millers.size());
+ for (const Tool& e : m_millers)
+ out.push_back(e.id());
+ return out;
+}
+
+uint16_t GCodeWriter::first_mill() const {
+ if (m_millers.empty()) {
+ uint16_t max = 0;
+ for (const Extruder& e : m_extruders)
+ max = std::max(max, e.id());
+ max++;
+ return (uint16_t)max;
+ } else return m_millers.front().id();
+}
+bool GCodeWriter::tool_is_extruder() const {
+ return m_tool->id() < first_mill();
+}
+const Tool* GCodeWriter::get_tool(uint16_t id) const{
+ for (const Extruder& e : m_extruders)
+ if (id == e.id())
+ return &e;
+ for (const Tool& e : m_millers)
+ if (id == e.id())
+ return &e;
+ return nullptr;
+}
+
void GCodeWriter::set_extruders(std::vector extruder_ids)
{
std::sort(extruder_ids.begin(), extruder_ids.end());
@@ -89,12 +127,16 @@ std::string GCodeWriter::postamble() const
std::string GCodeWriter::set_temperature(const unsigned int temperature, bool wait, int tool)
{
+ //use m_tool if tool isn't set
+ if (tool < 0 && m_tool != nullptr)
+ tool = m_tool->id();
+
//add offset
int16_t temp_w_offset = int16_t(temperature);
- temp_w_offset += int16_t(m_tool->temp_offset());
+ temp_w_offset += int16_t(get_tool(tool)->temp_offset());
temp_w_offset = std::max(int16_t(0), std::min(int16_t(2000), temp_w_offset));
- if (m_last_temperature_with_offset == temp_w_offset)
+ if (m_last_temperature_with_offset == temp_w_offset && !wait)
return "";
if (wait && (FLAVOR_IS(gcfMakerWare) || FLAVOR_IS(gcfSailfish)))
return "";
@@ -177,13 +219,15 @@ std::string GCodeWriter::set_bed_temperature(unsigned int temperature, bool wait
return gcode.str();
}
-std::string GCodeWriter::set_fan(const unsigned int speed, bool dont_save)
+std::string GCodeWriter::set_fan(const unsigned int speed, bool dont_save, uint16_t default_tool)
{
std::ostringstream gcode;
+ const Tool *tool = m_tool == nullptr ? get_tool(default_tool) : m_tool;
//add fan_offset
int16_t fan_speed = int16_t(speed);
- fan_speed += int8_t(m_tool->fan_offset());
+ if (tool != nullptr)
+ fan_speed += int8_t(tool->fan_offset());
fan_speed = std::max(int16_t(0), std::min(int16_t(100), fan_speed));
//test if it's useful to write it
diff --git a/src/libslic3r/GCodeWriter.hpp b/src/libslic3r/GCodeWriter.hpp
index 87aced07f..0d8c7b42c 100644
--- a/src/libslic3r/GCodeWriter.hpp
+++ b/src/libslic3r/GCodeWriter.hpp
@@ -34,41 +34,21 @@ public:
// Extruders are expected to be sorted in an increasing order.
void set_extruders(std::vector extruder_ids);
const std::vector& extruders() const { return m_extruders; }
- std::vector extruder_ids() const {
- std::vector out;
- out.reserve(m_extruders.size());
- for (const Extruder& e : m_extruders)
- out.push_back(e.id());
- return out;
- }
+ std::vector extruder_ids() const;
void set_mills(std::vector extruder_ids);
const std::vector& mills() const { return m_millers; }
- std::vector mill_ids() const {
- std::vector out;
- out.reserve(m_millers.size());
- for (const Tool& e : m_millers)
- out.push_back(e.id());
- return out;
- }
+ std::vector mill_ids() const;
//give the first mill id or an id after the last extruder. Can be used to see if an id is an extruder or a mill
- uint16_t first_mill() const {
- if (m_millers.empty()) {
- uint16_t max = 0;
- for (const Extruder& e : m_extruders)
- max = std::max(max, e.id());
- max++;
- return (uint16_t)max;
- }else return m_millers.front().id();
- }
- bool tool_is_extruder() const {
- return m_tool->id() < first_mill();
- }
+ uint16_t first_mill() const;
+ bool tool_is_extruder() const;
+ const Tool* get_tool(uint16_t id) const;
std::string preamble();
std::string postamble() const;
std::string set_temperature(unsigned int temperature, bool wait = false, int tool = -1);
std::string set_bed_temperature(unsigned int temperature, bool wait = false);
unsigned int get_fan() { return m_last_fan_speed; }
- std::string set_fan(unsigned int speed, bool dont_save = false);
+ /// set fan at speed. Save it as current fan speed if !dont_save, and use tool default_tool if the internal m_tool is null (no toolchange done yet).
+ std::string set_fan(unsigned int speed, bool dont_save = false, uint16_t default_tool = 0);
void set_acceleration(unsigned int acceleration);
std::string write_acceleration();
std::string reset_e(bool force = false);
diff --git a/src/libslic3r/Layer.cpp b/src/libslic3r/Layer.cpp
index dce75c2d6..dba935730 100644
--- a/src/libslic3r/Layer.cpp
+++ b/src/libslic3r/Layer.cpp
@@ -153,7 +153,7 @@ void Layer::make_perimeters()
&& config.infill_dense_algo == other_config.infill_dense_algo
&& config.no_perimeter_unsupported_algo == other_config.no_perimeter_unsupported_algo
&& config.only_one_perimeter_top == other_config.only_one_perimeter_top
- && config.overhangs == other_config.overhangs
+ && config.overhangs_width_speed == other_config.overhangs_width_speed
&& config.overhangs_width == other_config.overhangs_width
&& config.overhangs_reverse == other_config.overhangs_reverse
&& config.overhangs_reverse_threshold == other_config.overhangs_reverse_threshold
@@ -163,6 +163,8 @@ void Layer::make_perimeters()
&& config.perimeter_overlap == other_config.perimeter_overlap
&& config.perimeter_speed == other_config.perimeter_speed // it os mandatory? can't this be set at gcode.cpp?
&& config.small_perimeter_speed == other_config.small_perimeter_speed
+ && config.small_perimeter_min_length == other_config.small_perimeter_min_length
+ && config.small_perimeter_max_length == other_config.small_perimeter_max_length
&& config.thin_walls == other_config.thin_walls
&& config.thin_walls_min_width == other_config.thin_walls_min_width
&& config.thin_walls_overlap == other_config.thin_walls_overlap
diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp
index 1d818b639..65bf2ae98 100644
--- a/src/libslic3r/PerimeterGenerator.cpp
+++ b/src/libslic3r/PerimeterGenerator.cpp
@@ -97,12 +97,16 @@ void PerimeterGenerator::process()
coord_t ext_min_spacing = (coord_t)( ext_perimeter_spacing2 * (1 - 0.05/*INSET_OVERLAP_TOLERANCE*/) );
// prepare grown lower layer slices for overhang detection
- if (this->lower_slices != NULL && this->config->overhangs) {
+ if (this->lower_slices != NULL && this->config->overhangs_width > 0) {
// We consider overhang any part where the entire nozzle diameter is not supported by the
- // lower layer, so we take lower slices and offset them by half the nozzle diameter used
+ // lower layer, so we take lower slices and offset them by overhangs_width of the nozzle diameter used
// in the current layer
double offset_val = double(scale_(config->overhangs_width.get_abs_value(nozzle_diameter))) - (float)(ext_perimeter_width / 2);
- this->_lower_slices_p = offset(*this->lower_slices, offset_val);
+ this->_lower_slices_bridge_flow = offset(*this->lower_slices, offset_val);
+ }
+ if (this->lower_slices != NULL && this->config->overhangs_width_speed > 0) {
+ double offset_val = double(scale_(config->overhangs_width_speed.get_abs_value(nozzle_diameter))) - (float)(ext_perimeter_width / 2);
+ this->_lower_slices_bridge_speed = offset(*this->lower_slices, offset_val);
}
// have to grown the perimeters if mill post-process
@@ -942,6 +946,69 @@ void PerimeterGenerator::process()
} // for each island
}
+template
+ExtrusionPaths PerimeterGenerator::create_overhangs(LINE loop_polygons, ExtrusionRole role, bool is_external) const {
+ ExtrusionPaths paths;
+ double nozzle_diameter = this->print_config->nozzle_diameter.get_at(this->config->perimeter_extruder - 1);
+ if (this->config->overhangs_width.get_abs_value(nozzle_diameter) > this->config->overhangs_width_speed.get_abs_value(nozzle_diameter)) {
+ // get non-overhang paths by intersecting this loop with the grown lower slices
+ extrusion_paths_append(
+ paths,
+ intersection_pl(loop_polygons, this->_lower_slices_bridge_speed),
+ role,
+ is_external ? this->_ext_mm3_per_mm : this->_mm3_per_mm,
+ is_external ? this->ext_perimeter_flow.width : this->perimeter_flow.width,
+ (float)this->layer->height);
+
+ // get overhang paths by checking what parts of this loop fall
+ // outside the grown lower slices
+ Polylines poly_speed = diff_pl(loop_polygons, this->_lower_slices_bridge_speed);
+
+ extrusion_paths_append(
+ paths,
+ intersection_pl(poly_speed, this->_lower_slices_bridge_flow),
+ erOverhangPerimeter,
+ is_external ? this->_ext_mm3_per_mm : this->_mm3_per_mm,
+ is_external ? this->ext_perimeter_flow.width : this->perimeter_flow.width,
+ (float)this->layer->height);
+
+ extrusion_paths_append(
+ paths,
+ diff_pl(poly_speed, this->_lower_slices_bridge_flow),
+ erOverhangPerimeter,
+ this->_mm3_per_mm_overhang,
+ this->overhang_flow.width,
+ this->overhang_flow.height);
+
+ } else {
+
+ // get non-overhang paths by intersecting this loop with the grown lower slices
+ extrusion_paths_append(
+ paths,
+ intersection_pl(loop_polygons, this->_lower_slices_bridge_flow),
+ role,
+ is_external ? this->_ext_mm3_per_mm : this->_mm3_per_mm,
+ is_external ? this->ext_perimeter_flow.width : this->perimeter_flow.width,
+ (float)this->layer->height);
+
+ // get overhang paths by checking what parts of this loop fall
+ // outside the grown lower slices
+ extrusion_paths_append(
+ paths,
+ diff_pl(loop_polygons, this->_lower_slices_bridge_flow),
+ erOverhangPerimeter,
+ this->_mm3_per_mm_overhang,
+ this->overhang_flow.width,
+ this->overhang_flow.height);
+ }
+
+ // reapply the nearest point search for starting point
+ // We allow polyline reversal because Clipper may have randomly reversed polylines during clipping.
+ if(!paths.empty())
+ chain_and_reorder_extrusion_paths(paths, &paths.front().first_point());
+ return paths;
+}
+
ExtrusionEntityCollection PerimeterGenerator::_traverse_loops(
const PerimeterGeneratorLoops &loops, ThickPolylines &thin_walls) const
{
@@ -971,31 +1038,9 @@ ExtrusionEntityCollection PerimeterGenerator::_traverse_loops(
// detect overhanging/bridging perimeters
ExtrusionPaths paths;
- if (this->config->overhangs && this->layer->id() > 0
+ if ( this->config->overhangs_width_speed > 0 && this->layer->id() > 0
&& !(this->object_config->support_material && this->object_config->support_material_contact_distance_type.value == zdNone)) {
- // get non-overhang paths by intersecting this loop with the grown lower slices
- extrusion_paths_append(
- paths,
- intersection_pl(loop.polygon, this->_lower_slices_p),
- role,
- is_external ? this->_ext_mm3_per_mm : this->_mm3_per_mm,
- is_external ? this->ext_perimeter_flow.width : this->perimeter_flow.width,
- (float) this->layer->height);
-
- // get overhang paths by checking what parts of this loop fall
- // outside the grown lower slices (thus where the distance between
- // the loop centerline and original lower slices is >= half nozzle diameter
- extrusion_paths_append(
- paths,
- diff_pl(loop.polygon, this->_lower_slices_p),
- erOverhangPerimeter,
- this->_mm3_per_mm_overhang,
- this->overhang_flow.width,
- this->overhang_flow.height);
-
- // reapply the nearest point search for starting point
- // We allow polyline reversal because Clipper may have randomly reversed polylines during clipping.
- chain_and_reorder_extrusion_paths(paths, &paths.front().first_point());
+ paths = this->create_overhangs(loop.polygon, role, is_external);
} else {
ExtrusionPath path(role);
path.polyline = loop.polygon.split_at_first_point();
@@ -1423,35 +1468,9 @@ PerimeterGenerator::_extrude_and_cut_loop(const PerimeterGeneratorLoop &loop, co
}
// detect overhanging/bridging perimeters
- if (this->config->overhangs && this->layer->id() > 0
+ if ( this->config->overhangs_width_speed > 0 && this->layer->id() > 0
&& !(this->object_config->support_material && this->object_config->support_material_contact_distance_type.value == zdNone)) {
- ExtrusionPaths paths;
- // get non-overhang paths by intersecting this loop with the grown lower slices
- extrusion_paths_append(
- paths,
- intersection_pl(initial_polyline, this->_lower_slices_p),
- role,
- is_external ? this->_ext_mm3_per_mm : this->_mm3_per_mm,
- is_external ? this->ext_perimeter_flow.width : this->perimeter_flow.width,
- (float) this->layer->height);
-
- // get overhang paths by checking what parts of this loop fall
- // outside the grown lower slices (thus where the distance between
- // the loop centerline and original lower slices is >= half nozzle diameter
- extrusion_paths_append(
- paths,
- diff_pl(initial_polyline, this->_lower_slices_p),
- erOverhangPerimeter,
- this->_mm3_per_mm_overhang,
- this->overhang_flow.width,
- this->overhang_flow.height);
-
- // reapply the nearest point search for starting point
- // We allow polyline reversal because Clipper may have randomly
- // reversed polylines during clipping.
- if (!paths.empty())
- chain_and_reorder_extrusion_paths(paths, &paths.front().first_point());
-
+ ExtrusionPaths paths = this->create_overhangs(initial_polyline, role, is_external);
if (direction.length() > 0) {
Polyline direction_polyline;
diff --git a/src/libslic3r/PerimeterGenerator.hpp b/src/libslic3r/PerimeterGenerator.hpp
index 363e4d492..0ae116fb7 100644
--- a/src/libslic3r/PerimeterGenerator.hpp
+++ b/src/libslic3r/PerimeterGenerator.hpp
@@ -96,7 +96,11 @@ private:
double _ext_mm3_per_mm;
double _mm3_per_mm;
double _mm3_per_mm_overhang;
- Polygons _lower_slices_p;
+ Polygons _lower_slices_bridge_flow;
+ Polygons _lower_slices_bridge_speed;
+
+ template
+ ExtrusionPaths create_overhangs(LINE loop_polygons, ExtrusionRole role, bool is_external) const;
// transform loops into ExtrusionEntityCollection, adding also thin walls into it.
ExtrusionEntityCollection _traverse_loops(const PerimeterGeneratorLoops &loops, ThickPolylines &thin_walls) const;
diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp
index 5df4aa63c..e5a15a42c 100644
--- a/src/libslic3r/Preset.cpp
+++ b/src/libslic3r/Preset.cpp
@@ -442,8 +442,9 @@ const std::vector& Preset::print_options()
"avoid_crossing_perimeters",
"avoid_crossing_not_first_layer",
"thin_perimeters", "thin_perimeters_all",
- "thin_walls", "overhangs",
+ "thin_walls",
"overhangs_width",
+ "overhangs_width_speed",
"overhangs_reverse",
"overhangs_reverse_threshold",
"seam_position",
@@ -475,7 +476,11 @@ const std::vector& Preset::print_options()
"max_volumetric_extrusion_rate_slope_positive", "max_volumetric_extrusion_rate_slope_negative",
#endif /* HAS_PRESSURE_EQUALIZER */
"min_width_top_surface",
- "perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "infill_speed", "solid_infill_speed",
+ "perimeter_speed",
+ "small_perimeter_speed",
+ "small_perimeter_min_length",
+ "small_perimeter_max_length",
+ "external_perimeter_speed", "infill_speed", "solid_infill_speed",
"top_solid_infill_speed", "support_material_speed", "support_material_xy_spacing", "support_material_interface_speed",
"bridge_speed",
"gap_fill",
@@ -639,7 +644,7 @@ const std::vector& Preset::printer_options()
"bed_shape", "bed_custom_texture", "bed_custom_model", "z_offset", "gcode_flavor", "use_relative_e_distances", "serial_port", "serial_speed",
"use_firmware_retraction", "use_volumetric_e", "variable_layer_height",
"min_length",
- "host_type", "print_host", "printhost_apikey", "printhost_cafile",
+ "host_type", "print_host", "printhost_apikey", "printhost_cafile", "printhost_slug",
"single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode",
"color_change_gcode", "pause_print_gcode", "template_custom_gcode", "feature_gcode",
@@ -776,7 +781,7 @@ const std::vector& Preset::sla_printer_options()
"gamma_correction",
"min_exposure_time", "max_exposure_time",
"min_initial_exposure_time", "max_initial_exposure_time",
- "print_host", "printhost_apikey", "printhost_cafile",
+ "print_host", "printhost_apikey", "printhost_cafile", "printhost_slug",
"printer_notes",
"inherits",
"thumbnails",
@@ -909,15 +914,18 @@ static ProfileHostParams profile_host_params_same_or_anonymized(const DynamicPri
auto opt_print_host_old = cfg_old.option("print_host");
auto opt_printhost_apikey_old = cfg_old.option("printhost_apikey");
auto opt_printhost_cafile_old = cfg_old.option("printhost_cafile");
+ auto opt_printhost_slug_old = cfg_old.option("printhost_slug");
auto opt_print_host_new = cfg_new.option("print_host");
auto opt_printhost_apikey_new = cfg_new.option("printhost_apikey");
auto opt_printhost_cafile_new = cfg_new.option("printhost_cafile");
+ auto opt_printhost_slug_new = cfg_new.option("printhost_slug");
// If the new print host data is undefined, use the old data.
bool new_print_host_undefined = (opt_print_host_new == nullptr || opt_print_host_new ->empty()) &&
(opt_printhost_apikey_new == nullptr || opt_printhost_apikey_new ->empty()) &&
- (opt_printhost_cafile_new == nullptr || opt_printhost_cafile_new ->empty());
+ (opt_printhost_cafile_new == nullptr || opt_printhost_cafile_new ->empty()) &&
+ (opt_printhost_slug_new == nullptr || opt_printhost_slug_new ->empty());
if (new_print_host_undefined)
return ProfileHostParams::Anonymized;
@@ -925,8 +933,9 @@ static ProfileHostParams profile_host_params_same_or_anonymized(const DynamicPri
return ((l == nullptr || l->empty()) && (r == nullptr || r->empty())) ||
(l != nullptr && r != nullptr && l->value == r->value);
};
- return (opt_same(opt_print_host_old, opt_print_host_new) && opt_same(opt_printhost_apikey_old, opt_printhost_apikey_new) &&
- opt_same(opt_printhost_cafile_old, opt_printhost_cafile_new)) ? ProfileHostParams::Same : ProfileHostParams::Different;
+ return (opt_same(opt_print_host_old, opt_print_host_new) && opt_same(opt_printhost_apikey_old, opt_printhost_apikey_new) &&
+ opt_same(opt_printhost_cafile_old, opt_printhost_cafile_new) && opt_same(opt_printhost_slug_old, opt_printhost_slug_new))
+ ? ProfileHostParams::Same : ProfileHostParams::Different;
}
static bool profile_print_params_same(const DynamicPrintConfig &cfg_old, const DynamicPrintConfig &cfg_new)
@@ -938,7 +947,7 @@ static bool profile_print_params_same(const DynamicPrintConfig &cfg_old, const D
"compatible_printers", "compatible_printers_condition", "inherits",
"print_settings_id", "filament_settings_id", "sla_print_settings_id", "sla_material_settings_id", "printer_settings_id",
"printer_model", "printer_variant", "default_print_profile", "default_filament_profile", "default_sla_print_profile", "default_sla_material_profile",
- "print_host", "printhost_apikey", "printhost_cafile" })
+ "print_host", "printhost_apikey", "printhost_slug", "printhost_cafile" })
diff.erase(std::remove(diff.begin(), diff.end(), key), diff.end());
// Preset with the same name as stored inside the config exists.
return diff.empty() && profile_host_params_same_or_anonymized(cfg_old, cfg_new) != ProfileHostParams::Different;
@@ -988,6 +997,7 @@ Preset& PresetCollection::load_external_preset(
opt_update("print_host");
opt_update("printhost_apikey");
opt_update("printhost_cafile");
+ opt_update("printhost_slug");
}
}
// Update the "inherits" field.
@@ -1565,8 +1575,9 @@ const std::vector& PhysicalPrinter::printer_options()
// "printer_model",
"host_type",
"print_host",
- "printhost_apikey",
+ "printhost_apikey",
"printhost_cafile",
+ "printhost_slug",
"printhost_authorization_type",
// HTTP digest authentization (RFC 2617)
"printhost_user",
@@ -1583,7 +1594,8 @@ const std::vector& PhysicalPrinter::print_host_options()
s_opts = {
"print_host",
"printhost_apikey",
- "printhost_cafile"
+ "printhost_cafile",
+ "printhost_slug"
};
}
return s_opts;
@@ -1619,7 +1631,8 @@ bool PhysicalPrinter::has_empty_config() const
config.opt_string("printhost_apikey" ).empty() &&
config.opt_string("printhost_cafile" ).empty() &&
config.opt_string("printhost_user" ).empty() &&
- config.opt_string("printhost_password").empty();
+ config.opt_string("printhost_password").empty() &&
+ config.opt_string("printhost_slug" ).empty();
}
void PhysicalPrinter::update_preset_names_in_config()
diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp
index a22142e6c..e4ddc25fd 100644
--- a/src/libslic3r/PresetBundle.cpp
+++ b/src/libslic3r/PresetBundle.cpp
@@ -499,6 +499,7 @@ DynamicPrintConfig PresetBundle::full_config_secure() const
config.erase("print_host");
config.erase("printhost_apikey");
config.erase("printhost_cafile");
+ config.erase("printhost_slug");
return config;
}
diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp
index 070c67278..3fae61267 100644
--- a/src/libslic3r/PrintConfig.cpp
+++ b/src/libslic3r/PrintConfig.cpp
@@ -150,7 +150,14 @@ void PrintConfigDef::init_common_params()
"the API Key or the password required for authentication.");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionString(""));
-
+
+ def = this->add("printhost_slug", coString);
+ def->label = L("Printer");
+ def->tooltip = L("Name of the printer");
+ def->mode = comAdvanced;
+ def->gui_type = "select_open";
+ def->set_default_value(new ConfigOptionString(""));
+
def = this->add("printhost_cafile", coString);
def->label = L("HTTPS CA File");
def->category = OptionCategory::general;
@@ -2380,10 +2387,12 @@ void PrintConfigDef::init_fff_params()
def->enum_values.push_back("duet");
def->enum_values.push_back("flashair");
def->enum_values.push_back("astrobox");
+ def->enum_values.push_back("repetier");
def->enum_labels.push_back("OctoPrint");
def->enum_labels.push_back("Duet");
def->enum_labels.push_back("FlashAir");
def->enum_labels.push_back("AstroBox");
+ def->enum_labels.push_back("Repetier");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionEnum(htOctoPrint));
@@ -2448,23 +2457,29 @@ void PrintConfigDef::init_fff_params()
def->mode = comExpert;
def->set_default_value(new ConfigOptionString("[input_filename_base].gcode"));
- def = this->add("overhangs", coBool);
- def->label = L("As bridge");
- def->full_label = L("Overhangs as bridge");
+ def = this->add("overhangs_width_speed", coFloatOrPercent);
+ def->label = L("'As bridge' speed threshold");
+ def->full_label = L("Overhang bridge speed threshold");
def->category = OptionCategory::perimeter;
- def->tooltip = L("Option to adjust flow for overhangs (bridge flow will be used), "
- "to apply bridge speed to them and enable fan.");
+ def->tooltip = L("Minimum unsupported width for an extrusion to apply the bridge speed & fan to this overhang."
+ " Can be in mm or in a % of the nozzle diameter."
+ " Set to 0 to deactivate.");
+ def->ratio_over = "nozzle_diameter";
+ def->min = 0;
def->mode = comExpert;
- def->set_default_value(new ConfigOptionBool(true));
+ def->set_default_value(new ConfigOptionFloatOrPercent(50,true));
def = this->add("overhangs_width", coFloatOrPercent);
- def->label = L("'As bridge' threshold");
- def->full_label = L("Overhang bridge threshold");
+ def->label = L("'As bridge' flow threshold");
+ def->full_label = L("Overhang bridge flow threshold");
def->category = OptionCategory::perimeter;
- def->tooltip = L("Minimum unsupported width for an extrusion to be considered an overhang. Can be in mm or in a % of the nozzle diameter.");
+ def->tooltip = L("Minimum unsupported width for an extrusion to apply the bridge flow to this overhang."
+ " Can be in mm or in a % of the nozzle diameter."
+ " Set to 0 to deactivate.");
def->ratio_over = "nozzle_diameter";
+ def->min = 0;
def->mode = comExpert;
- def->set_default_value(new ConfigOptionFloatOrPercent(50, true));
+ def->set_default_value(new ConfigOptionFloatOrPercent(75, true));
def = this->add("overhangs_reverse", coBool);
def->label = L("Reverse on odd");
@@ -2934,7 +2949,7 @@ void PrintConfigDef::init_fff_params()
def->set_default_value(new ConfigOptionInts{ 5 });
def = this->add("small_perimeter_speed", coFloatOrPercent);
- def->label = L("Small");
+ def->label = L("Speed");
def->full_label = ("Small perimeters speed");
def->category = OptionCategory::speed;
def->tooltip = L("This separate setting will affect the speed of perimeters having radius <= 6.5mm "
@@ -2946,6 +2961,32 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloatOrPercent(15, false));
+ def = this->add("small_perimeter_min_length", coFloatOrPercent);
+ def->label = L("Min length");
+ def->full_label = ("Min small perimeters length");
+ def->category = OptionCategory::speed;
+ def->tooltip = L("This set the threshold for small periemter length. Every loop with a length lower than that will be printed at small perimeter speed"
+ "\nCan be a mm or a % of the nozzle diameter.");
+ def->sidetext = L("mm or %");
+ def->ratio_over = "nozzle_diameter";
+ def->min = 0;
+ def->mode = comAdvanced;
+ def->set_default_value(new ConfigOptionFloatOrPercent(6, false));
+
+
+ def = this->add("small_perimeter_max_length", coFloatOrPercent);
+ def->label = L("Max length");
+ def->full_label = ("Max small perimeters length");
+ def->category = OptionCategory::speed;
+ def->tooltip = L("This set the end of the threshold for small periemter length."
+ " Every periemter loop lower than that will see their speed reduced a bit, from their normal spee at this length down to small perimeter speed."
+ "\nCan be a mm or a % of the nozzle diameter.");
+ def->sidetext = L("mm or %");
+ def->ratio_over = "nozzle_diameter";
+ def->min = 0;
+ def->mode = comAdvanced;
+ def->set_default_value(new ConfigOptionFloatOrPercent(20, false));
+
def = this->add("curve_smoothing_angle_convex", coFloat);
def->label = L("Min convex angle");
def->full_label = L("Curve smoothing minimum angle (convex)");
@@ -4789,6 +4830,12 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
opt_key = "seam_travel_cost";
value = "20%";
}
+ } else if (opt_key == "overhangs") {
+ opt_key = "overhangs_width_speed";
+ if (value == "1")
+ value = "50%";
+ else
+ value = "0";
}
// Ignore the following obsolete configuration keys:
static std::set ignore = {
diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp
index 03f54a927..d663c9ed9 100644
--- a/src/libslic3r/PrintConfig.hpp
+++ b/src/libslic3r/PrintConfig.hpp
@@ -71,7 +71,7 @@ enum class MachineLimitsUsage {
};
enum PrintHostType {
- htOctoPrint, htDuet, htFlashAir, htAstroBox
+ htOctoPrint, htDuet, htFlashAir, htAstroBox, htRepetier
};
enum AuthorizationType {
@@ -204,6 +204,7 @@ template<> inline const t_config_enum_values& ConfigOptionEnum::g
keys_map["duet"] = htDuet;
keys_map["flashair"] = htFlashAir;
keys_map["astrobox"] = htAstroBox;
+ keys_map["repetier"] = htRepetier;
}
return keys_map;
}
@@ -753,8 +754,8 @@ public:
ConfigOptionFloat milling_speed;
ConfigOptionFloatOrPercent min_width_top_surface;
// Detect bridging perimeters
- ConfigOptionBool overhangs;
ConfigOptionFloatOrPercent overhangs_width;
+ ConfigOptionFloatOrPercent overhangs_width_speed;
ConfigOptionBool overhangs_reverse;
ConfigOptionFloatOrPercent overhangs_reverse_threshold;
ConfigOptionEnum no_perimeter_unsupported_algo;
@@ -769,6 +770,8 @@ public:
ConfigOptionPercent print_extrusion_multiplier;
ConfigOptionFloat print_retract_length;
ConfigOptionFloatOrPercent small_perimeter_speed;
+ ConfigOptionFloatOrPercent small_perimeter_min_length;
+ ConfigOptionFloatOrPercent small_perimeter_max_length;
ConfigOptionEnum solid_fill_pattern;
ConfigOptionFloat solid_infill_below_area;
ConfigOptionInt solid_infill_extruder;
@@ -850,8 +853,8 @@ protected:
OPT_PTR(milling_post_process);
OPT_PTR(milling_speed);
OPT_PTR(min_width_top_surface);
- OPT_PTR(overhangs);
OPT_PTR(overhangs_width);
+ OPT_PTR(overhangs_width_speed);
OPT_PTR(overhangs_reverse);
OPT_PTR(overhangs_reverse_threshold);
OPT_PTR(no_perimeter_unsupported_algo);
@@ -865,6 +868,8 @@ protected:
OPT_PTR(print_extrusion_multiplier);
OPT_PTR(print_retract_length);
OPT_PTR(small_perimeter_speed);
+ OPT_PTR(small_perimeter_min_length);
+ OPT_PTR(small_perimeter_max_length);
OPT_PTR(solid_fill_pattern);
OPT_PTR(solid_infill_below_area);
OPT_PTR(solid_infill_extruder);
@@ -1349,6 +1354,7 @@ public:
ConfigOptionString print_host;
ConfigOptionString printhost_apikey;
ConfigOptionString printhost_cafile;
+ ConfigOptionString printhost_slug;
ConfigOptionString serial_port;
ConfigOptionInt serial_speed;
@@ -1359,6 +1365,7 @@ protected:
OPT_PTR(print_host);
OPT_PTR(printhost_apikey);
OPT_PTR(printhost_cafile);
+ OPT_PTR(printhost_slug);
OPT_PTR(serial_port);
OPT_PTR(serial_speed);
}
diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp
index add088467..873ab67cd 100644
--- a/src/libslic3r/PrintObject.cpp
+++ b/src/libslic3r/PrintObject.cpp
@@ -682,7 +682,7 @@ bool PrintObject::invalidate_state_by_config_options(const std::vectorfill_surfaces.filter_by_type(stPosBottom | stDensSolid | stModBridge)),
gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS));
- if (region->region()->config().overhangs.value)
+ if (region->region()->config().overhangs_width.value > 0)
SupportMaterialInternal::collect_bridging_perimeter_areas(region->perimeters.entities, gap_xy_scaled, polygons_trimming);
}
if (! some_region_overlaps)
diff --git a/src/libslic3r/libslic3r.h b/src/libslic3r/libslic3r.h
index b8b787546..9cfe35165 100644
--- a/src/libslic3r/libslic3r.h
+++ b/src/libslic3r/libslic3r.h
@@ -56,7 +56,7 @@ static constexpr double SCALING_FACTOR = 0.000001;
// When extruding a closed loop, the loop is interrupted and shortened a bit to reduce the seam.
static constexpr double LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER = 0.15;
// Maximum perimeter length for the loop to apply the small perimeter speed.
-#define SMALL_PERIMETER_LENGTH ((6.5 / SCALING_FACTOR) * 2 * PI)
+//#define SMALL_PERIMETER_LENGTH ((6.5 / SCALING_FACTOR) * 2 * PI)
static constexpr double INSET_OVERLAP_TOLERANCE = 0.4;
//FIXME Better to use an inline function with an explicit return type.
//inline coord_t scale_(coordf_t v) { return coord_t(floor(v / SCALING_FACTOR + 0.5f)); }
diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt
index eb7d1c6a0..97d202461 100644
--- a/src/slic3r/CMakeLists.txt
+++ b/src/slic3r/CMakeLists.txt
@@ -211,6 +211,8 @@ set(SLIC3R_GUI_SOURCES
Utils/FlashAir.hpp
Utils/AstroBox.cpp
Utils/AstroBox.hpp
+ Utils/Repetier.cpp
+ Utils/Repetier.hpp
Utils/PrintHost.cpp
Utils/PrintHost.hpp
Utils/Bonjour.cpp
diff --git a/src/slic3r/GUI/CalibrationBridgeDialog.cpp b/src/slic3r/GUI/CalibrationBridgeDialog.cpp
index f7c87a778..36e8c9902 100644
--- a/src/slic3r/GUI/CalibrationBridgeDialog.cpp
+++ b/src/slic3r/GUI/CalibrationBridgeDialog.cpp
@@ -86,7 +86,7 @@ void CalibrationBridgeDialog::create_geometry(std::string setting_to_test, bool
int start = bridge_flow_ratio->value;
float zshift = 2.3 * (1 - z_scale);
for (size_t i = 0; i < nb_items; i++) {
- if((start + (add ? 1 : -1) * i * step) < 180 && start + (start + (add ? 1 : -1) * i * step) > 20)
+ if((start + (add ? 1 : -1) * i * step) < 180 && (start + (add ? 1 : -1) * i * step) > 20)
add_part(model.objects[objs_idx[i]], Slic3r::resources_dir() + "/calibration/bridge_flow/f"+std::to_string(start + (add ? 1 : -1) * i * step)+".amf", Vec3d{ -10,0, zshift + 4.6 * z_scale }, Vec3d{ 1,1,z_scale });
}
diff --git a/src/slic3r/GUI/CalibrationRetractionDialog.cpp b/src/slic3r/GUI/CalibrationRetractionDialog.cpp
index 28173e0f8..843fb858b 100644
--- a/src/slic3r/GUI/CalibrationRetractionDialog.cpp
+++ b/src/slic3r/GUI/CalibrationRetractionDialog.cpp
@@ -26,11 +26,11 @@ namespace GUI {
void CalibrationRetractionDialog::create_buttons(wxStdDialogButtonSizer* buttons){
wxString choices_steps[] = { "0.1","0.2","0.5","1" };
steps = new wxComboBox(this, wxID_ANY, wxString{ "0.2" }, wxDefaultPosition, wxDefaultSize, 4, choices_steps);
- steps->SetToolTip(_(L("Each militer add this value to the retraction value. ")));
+ steps->SetToolTip(_L("Each militer add this value to the retraction value. "));
steps->SetSelection(1);
wxString choices_nb[] = { "2","4","6","8","10","15","20","25" };
nb_steps = new wxComboBox(this, wxID_ANY, wxString{ "15" }, wxDefaultPosition, wxDefaultSize, 8, choices_nb);
- nb_steps->SetToolTip(_(L("Select the number milimeters for the tower.")));
+ nb_steps->SetToolTip(_L("Select the number milimeters for the tower."));
nb_steps->SetSelection(5);
//wxString choices_start[] = { "current","260","250","240","230","220","210" };
//start_step = new wxComboBox(this, wxID_ANY, wxString{ "current" }, wxDefaultPosition, wxDefaultSize, 7, choices_start);
@@ -40,9 +40,10 @@ void CalibrationRetractionDialog::create_buttons(wxStdDialogButtonSizer* buttons
int temp = int((2 + filament_config->option("temperature")->get_at(0)) / 5) * 5;
auto size = wxSize(4 * em_unit(), wxDefaultCoord);
temp_start = new wxTextCtrl(this, wxID_ANY, std::to_string(temp), wxDefaultPosition, size);
- wxString choices_decr[] = { "one test","2x10°","3x10°","4x10°","3x5°","5x5°" };
+ wxString degree = L'°' ;
+ wxString choices_decr[] = { "one test","2x10"+ degree,"3x10"+ degree, L"4x10°", _("3x50°"), _L("5x5°") };
decr_temp = new wxComboBox(this, wxID_ANY, wxString{ "current" }, wxDefaultPosition, wxDefaultSize, 6, choices_decr);
- decr_temp->SetToolTip(_(L("Select the number tower to print, and by how many degrees C to decrease each time.")));
+ decr_temp->SetToolTip(_L("Select the number tower to print, and by how many degrees C to decrease each time."));
decr_temp->SetSelection(0);
buttons->Add(new wxStaticText(this, wxID_ANY, wxString{ "step:" }));
@@ -163,7 +164,6 @@ void CalibrationRetractionDialog::create_geometry(wxCommandEvent& event_args) {
}
//add sub-part after scale
- float zshift = (1 - scale) / 2 + 0.4 * scale;
float zscale_number = (first_layer_height + layer_height) / 0.4;
std::vector < std::vector> part_tower;
for (size_t id_item = 0; id_item < nb_items; id_item++) {
@@ -171,11 +171,14 @@ void CalibrationRetractionDialog::create_geometry(wxCommandEvent& event_args) {
int mytemp = temp - temp_decr * id_item;
if (mytemp > 285) mytemp = 285;
if (mytemp < 180) mytemp = 180;
- add_part(model.objects[objs_idx[id_item]], Slic3r::resources_dir() + "/calibration/filament_temp/t"+ std::to_string(mytemp) + ".amf", Vec3d{ 0,0,zshift - 5.2 * scale }, Vec3d{ scale,scale,scale });
+ add_part(model.objects[objs_idx[id_item]], Slic3r::resources_dir() + "/calibration/filament_temp/t"+ std::to_string(mytemp) + ".amf",
+ Vec3d{ 0,0, scale * 0.2 -4.8 }, Vec3d{ scale,scale,scale });
model.objects[objs_idx[id_item]]->volumes[1]->rotate(PI / 2, Vec3d(0, 0, 1));
model.objects[objs_idx[id_item]]->volumes[1]->rotate(-PI / 2, Vec3d(1, 0, 0));
for (int num_retract = 0; num_retract < nb_retract; num_retract++) {
- part_tower.back().push_back(add_part(model.objects[objs_idx[id_item]], Slic3r::resources_dir() + "/calibration/retraction/retraction_calibration_pillar.amf", Vec3d{ 0,0,zshift + scale * num_retract }, Vec3d{ scale,scale,scale }));
+ part_tower.back().push_back(add_part(model.objects[objs_idx[id_item]],
+ Slic3r::resources_dir() + "/calibration/retraction/retraction_calibration_pillar.amf",
+ Vec3d{ 0,0,scale * 0.7 - 0.3 + scale * num_retract }, Vec3d{ scale,scale,scale }));
}
}
@@ -212,7 +215,7 @@ void CalibrationRetractionDialog::create_geometry(wxCommandEvent& event_args) {
model.objects[objs_idx[i]]->config.set_key_value("fill_density", new ConfigOptionPercent(0));
//model.objects[objs_idx[i]]->config.set_key_value("fill_pattern", new ConfigOptionEnum(ipRectilinear));
model.objects[objs_idx[i]]->config.set_key_value("only_one_perimeter_top", new ConfigOptionBool(false));
- model.objects[objs_idx[i]]->config.set_key_value("overhangs", new ConfigOptionBool(false));
+ model.objects[objs_idx[i]]->config.set_key_value("overhangs_width_speed", new ConfigOptionFloatOrPercent(0,false));
model.objects[objs_idx[i]]->config.set_key_value("thin_walls", new ConfigOptionBool(true));
model.objects[objs_idx[i]]->config.set_key_value("thin_walls_min_width", new ConfigOptionFloatOrPercent(2,true));
model.objects[objs_idx[i]]->config.set_key_value("gap_fill", new ConfigOptionBool(false));
diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp
index 2e6149046..181f3b37e 100644
--- a/src/slic3r/GUI/ConfigManipulation.cpp
+++ b/src/slic3r/GUI/ConfigManipulation.cpp
@@ -275,9 +275,9 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
// Ask only once.
if (!support_material_overhangs_queried) {
support_material_overhangs_queried = true;
- if (!config->opt_bool("overhangs")/* != 1*/) {
+ if (config->option("overhangs_width_speed") == 0) {
wxString msg_text = _(L("Supports work better, if the following feature is enabled:\n"
- "- Detect bridging perimeters"));
+ "- overhangs with bridge speed & fan"));
if (is_global_config) {
msg_text += "\n\n" + _(L("Shall I adjust those settings for supports?"));
wxMessageDialog dialog(nullptr, msg_text, _(L("Support Generator")),
@@ -286,7 +286,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
auto answer = dialog.ShowModal();
if (!is_global_config || answer == wxID_YES) {
// Enable "detect bridging perimeters".
- new_conf.set_key_value("overhangs", new ConfigOptionBool(true));
+ new_conf.set_key_value("overhangs_width_speed", new ConfigOptionFloatOrPercent(50, true));
} else if (answer == wxID_NO) {
// Do nothing, leave supports on and "detect bridging perimeters" off.
} else if (answer == wxID_CANCEL) {
@@ -358,10 +358,10 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
"only_one_perimeter_top", "ensure_vertical_shell_thickness", "thin_walls",
"overhangs", "overhangs_reverse",
"seam_position", "external_perimeters_first", "external_perimeters_vase", "external_perimeter_extrusion_width",
- "perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "perimeter_loop", "perimeter_loop_seam" })
+ "perimeter_speed", "small_perimeter_speed", "small_perimeter_min_length", " small_perimeter_max_length", "external_perimeter_speed", "perimeter_loop", "perimeter_loop_seam" })
toggle_field(el, have_perimeters);
- toggle_field("overhangs_width", config->opt_bool("overhangs"));
+ toggle_field("overhangs_width", config->option("overhangs_width_speed")->value > 0);
toggle_field("overhangs_reverse_threshold", config->opt_bool("overhangs_reverse"));
toggle_field("min_width_top_surface", config->opt_bool("only_one_perimeter_top"));
toggle_field("thin_perimeters_all", config->opt_bool("thin_perimeters"));
diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp
index a26d88bef..5793f6055 100644
--- a/src/slic3r/GUI/Field.cpp
+++ b/src/slic3r/GUI/Field.cpp
@@ -862,9 +862,12 @@ void Choice::BUILD() {
*
* Note: Set bitmap height to the Font size because of OSX rendering.
*/
- wxBitmap empty_bmp(1, temp->GetFont().GetPixelSize().y + 2);
- empty_bmp.SetWidth(0);
- temp->SetItemBitmap(0, empty_bmp);
+ // Welll, it makes wx freak out... (in debug) if select_open
+ if (m_opt.gui_type != "select_open") {
+ wxBitmap empty_bmp(1, temp->GetFont().GetPixelSize().y + 2);
+ empty_bmp.SetWidth(0);
+ temp->SetItemBitmap(0, empty_bmp);
+ }
#endif
// temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { on_change_field(); }), temp->GetId());
diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp
index fb7dceffd..6cf1ab372 100644
--- a/src/slic3r/GUI/GUI.cpp
+++ b/src/slic3r/GUI/GUI.cpp
@@ -201,7 +201,7 @@ void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt
else if (opt_key.compare("infill_dense_algo") == 0)
config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value)));
else if (opt_key.compare("no_perimeter_unsupported_algo") == 0)
- config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value)));
+ config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value)));
else if (opt_key.compare("infill_connection") == 0)
config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value)));
else if (opt_key.compare("wipe_advanced_algo") == 0)
diff --git a/src/slic3r/GUI/PhysicalPrinterDialog.cpp b/src/slic3r/GUI/PhysicalPrinterDialog.cpp
index 7456ee800..56464efcd 100644
--- a/src/slic3r/GUI/PhysicalPrinterDialog.cpp
+++ b/src/slic3r/GUI/PhysicalPrinterDialog.cpp
@@ -251,7 +251,7 @@ PhysicalPrinterDialog::~PhysicalPrinterDialog()
void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgroup)
{
m_optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
- if (opt_key == "printhost_authorization_type")
+ if (opt_key == "printhost_authorization_type" || opt_key == "host_type")
this->update();
};
@@ -302,6 +302,13 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr
return sizer;
};
+ auto print_host_printers = [=](wxWindow* parent) {
+ auto sizer = create_sizer_with_btn(parent, &m_printhost_slug_browse_btn, "browse", _L("Refresh Printers"));
+
+ m_printhost_slug_browse_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent e) { update_printers(); });
+ return sizer;
+ };
+
// Set a wider width for a better alignment
Option option = m_optgroup->get_option("print_host");
option.opt.width = Field::def_width_wider();
@@ -316,6 +323,12 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr
option.opt.width = Field::def_width_wider();
m_optgroup->append_single_option_line(option);
+ option = m_optgroup->get_option("printhost_slug");
+ option.opt.width = Field::def_width_wider();
+ Line slug_line = m_optgroup->create_single_option_line(option);
+ slug_line.append_widget(print_host_printers);
+ m_optgroup->append_line(slug_line);
+
const auto ca_file_hint = _u8L("HTTPS CA file is optional. It is only needed if you use HTTPS with a self-signed certificate.");
if (Http::ca_file_supported()) {
@@ -405,9 +418,61 @@ void PhysicalPrinterDialog::update()
m_optgroup->show_field(opt_key, auth_type == AuthorizationType::atUserPassword);
}
+ {
+ std::unique_ptr host(PrintHost::get_print_host(m_config));
+ if (m_printhost_test_btn)
+ m_printhost_test_btn->Enable(host->can_test());
+
+ if (m_printhost_browse_btn)
+ m_printhost_browse_btn->Enable(host->has_auto_discovery());
+
+ m_printhost_slug_browse_btn->Enable(host->can_support_multiple_printers());
+
+ Field* rs = m_optgroup->get_field("printhost_slug");
+ if (host->can_support_multiple_printers()) {
+ update_printers();
+ rs->enable();
+ } else {
+ rs->disable();
+ }
+ }
+
this->Layout();
}
+void PhysicalPrinterDialog::update_printers()
+{
+ std::unique_ptr host(PrintHost::get_print_host(m_config));
+
+ wxArrayString printers;
+ Field* rs = m_optgroup->get_field("printhost_slug");
+ if (!host->get_printers(printers)) {
+ std::vector slugs;
+
+ Choice* choice = dynamic_cast(rs);
+ choice->set_values(slugs);
+
+ rs->disable();
+ } else {
+ std::vector slugs;
+ for (int i = 0; i < printers.size(); i++) {
+ slugs.push_back(printers[i].ToStdString());
+ }
+
+ Choice* choice = dynamic_cast(rs);
+ choice->set_values(slugs);
+ boost::any val = choice->get_value();
+ boost::any any_string_type = std::string("");
+ auto value_idx = std::find(slugs.begin(), slugs.end(), m_config->opt("printhost_slug")->value);
+ if ((val.empty() || (any_string_type.type() == val.type() && boost::any_cast(val) == "")) && !slugs.empty() && value_idx == slugs.end()) {
+ change_opt_value(*m_config, "printhost_slug", slugs[0]);
+ choice->set_value(slugs[0],false);
+ } else if(value_idx != slugs.end() ){
+ choice->set_value(m_config->option("printhost_slug")->value, false);
+ }
+ rs->enable();
+ }
+}
wxString PhysicalPrinterDialog::get_printer_name()
{
diff --git a/src/slic3r/GUI/PhysicalPrinterDialog.hpp b/src/slic3r/GUI/PhysicalPrinterDialog.hpp
index 3d0cf2d9f..47337096b 100644
--- a/src/slic3r/GUI/PhysicalPrinterDialog.hpp
+++ b/src/slic3r/GUI/PhysicalPrinterDialog.hpp
@@ -72,6 +72,7 @@ class PhysicalPrinterDialog : public DPIDialog
ScalableButton* m_add_preset_btn {nullptr};
ScalableButton* m_printhost_browse_btn {nullptr};
ScalableButton* m_printhost_test_btn {nullptr};
+ ScalableButton* m_printhost_slug_browse_btn {nullptr};
ScalableButton* m_printhost_cafile_browse_btn {nullptr};
wxBoxSizer* m_presets_sizer {nullptr};
@@ -85,6 +86,7 @@ public:
~PhysicalPrinterDialog();
void update();
+ void update_printers();
wxString get_printer_name();
void update_full_printer_names();
PhysicalPrinter* get_printer() {return &m_printer; }
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index dbef136f3..091f2ea42 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -1850,7 +1850,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
"complete_objects_one_skirt",
"duplicate_distance", "extruder_clearance_radius", "skirts", "skirt_distance", "skirt_height",
"brim_width", "variable_layer_height", "serial_port", "serial_speed", "host_type", "print_host",
- "printhost_apikey", "printhost_cafile", "nozzle_diameter", "single_extruder_multi_material",
+ "printhost_apikey", "printhost_cafile", "printhost_slug", "nozzle_diameter", "single_extruder_multi_material",
"wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim",
"extruder_colour", "filament_colour", "max_print_height", "printer_model", "printer_technology",
// These values are necessary to construct SlicingParameters by the Canvas3D variable layer height editor.
@@ -5376,10 +5376,14 @@ void Plater::send_gcode()
}
default_output_file = fs::path(Slic3r::fold_utf8_to_ascii(default_output_file.string()));
- PrintHostSendDialog dlg(default_output_file, upload_job.printhost->can_start_print());
+ wxArrayString groups;
+ upload_job.printhost->get_groups(groups);
+
+ PrintHostSendDialog dlg(default_output_file, upload_job.printhost->can_start_print(), groups);
if (dlg.ShowModal() == wxID_OK) {
upload_job.upload_data.upload_path = dlg.filename();
upload_job.upload_data.start_print = dlg.start_print();
+ upload_job.upload_data.group = dlg.group();
p->export_gcode(fs::path(), false, std::move(upload_job));
}
diff --git a/src/slic3r/GUI/PrintHostDialogs.cpp b/src/slic3r/GUI/PrintHostDialogs.cpp
index 96299c381..edbb3eb34 100644
--- a/src/slic3r/GUI/PrintHostDialogs.cpp
+++ b/src/slic3r/GUI/PrintHostDialogs.cpp
@@ -28,11 +28,13 @@ namespace GUI {
static const char *CONFIG_KEY_PATH = "printhost_path";
static const char *CONFIG_KEY_PRINT = "printhost_print";
+static const char *CONFIG_KEY_GROUP = "printhost_group";
-PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, bool can_start_print)
+PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, bool can_start_print, wxArrayString& groups)
: MsgDialog(nullptr, _(L("Send G-Code to printer host")), _(L("Upload to Printer Host with the following filename:")), wxID_NONE)
, txt_filename(new wxTextCtrl(this, wxID_ANY))
, box_print(can_start_print ? new wxCheckBox(this, wxID_ANY, _(L("Start printing after upload"))) : nullptr)
+ , combo_groups(!groups.IsEmpty() ? new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, groups, wxCB_READONLY) : nullptr)
{
#ifdef __APPLE__
txt_filename->OSXDisableAllSmartSubstitutions();
@@ -49,6 +51,20 @@ PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, bool can_start_pr
content_sizer->Add(box_print, 0, wxBOTTOM, 2*VERT_SPACING);
box_print->SetValue(app_config->get("recent", CONFIG_KEY_PRINT) == "1");
}
+
+ if (combo_groups != nullptr) {
+ auto *label_group = new wxStaticText(this, wxID_ANY, _(L("Group")));
+ content_sizer->Add(label_group);
+
+ content_sizer->Add(combo_groups, 0, wxBOTTOM, 2*VERT_SPACING);
+
+ wxString recent_group = from_u8(app_config->get("recent", CONFIG_KEY_GROUP));
+ if (recent_group.Length() > 0) {
+ combo_groups->SetValue(recent_group);
+ } else {
+ combo_groups->SetSelection(0);
+ }
+ }
btn_sizer->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL));
@@ -64,7 +80,7 @@ PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, bool can_start_pr
txt_filename->SetValue(recent_path);
txt_filename->SetFocus();
-
+
Fit();
Bind(wxEVT_SHOW, [=](const wxShowEvent &) {
@@ -86,6 +102,16 @@ bool PrintHostSendDialog::start_print() const
return box_print != nullptr ? box_print->GetValue() : false;
}
+std::string PrintHostSendDialog::group() const
+{
+ if (combo_groups == nullptr) {
+ return "";
+ } else {
+ wxString group = combo_groups->GetValue();
+ return into_u8(group);
+ }
+}
+
void PrintHostSendDialog::EndModal(int ret)
{
if (ret == wxID_OK) {
@@ -96,9 +122,15 @@ void PrintHostSendDialog::EndModal(int ret)
path.clear();
else
path = path.SubString(0, last_slash);
+
AppConfig *app_config = wxGetApp().app_config;
app_config->set("recent", CONFIG_KEY_PATH, into_u8(path));
app_config->set("recent", CONFIG_KEY_PRINT, start_print() ? "1" : "0");
+
+ if (combo_groups != nullptr) {
+ wxString group = combo_groups->GetValue();
+ app_config->set("recent", CONFIG_KEY_GROUP, into_u8(group));
+ }
}
MsgDialog::EndModal(ret);
diff --git a/src/slic3r/GUI/PrintHostDialogs.hpp b/src/slic3r/GUI/PrintHostDialogs.hpp
index e5f96f2b6..0f7308c18 100644
--- a/src/slic3r/GUI/PrintHostDialogs.hpp
+++ b/src/slic3r/GUI/PrintHostDialogs.hpp
@@ -7,6 +7,8 @@
#include
#include
#include
+#include
+#include
#include "GUI.hpp"
#include "GUI_Utils.hpp"
@@ -29,14 +31,16 @@ namespace GUI {
class PrintHostSendDialog : public GUI::MsgDialog
{
public:
- PrintHostSendDialog(const boost::filesystem::path &path, bool can_start_print);
+ PrintHostSendDialog(const boost::filesystem::path &path, bool can_start_print, wxArrayString& groups);
boost::filesystem::path filename() const;
bool start_print() const;
+ std::string group() const;
virtual void EndModal(int ret) override;
private:
wxTextCtrl *txt_filename;
wxCheckBox *box_print;
+ wxComboBox *combo_groups;
};
diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp
index 205e79af9..668b86b6c 100644
--- a/src/slic3r/GUI/Tab.cpp
+++ b/src/slic3r/GUI/Tab.cpp
@@ -2312,6 +2312,7 @@ void TabPrinter::build_sla()
}
+
void TabPrinter::extruders_count_changed(size_t extruders_count)
{
bool is_count_changed = false;
diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp
index 6620b50c1..47661996a 100644
--- a/src/slic3r/GUI/Tab.hpp
+++ b/src/slic3r/GUI/Tab.hpp
@@ -444,6 +444,8 @@ public:
wxButton* m_serial_test_btn = nullptr;
ScalableButton* m_print_host_test_btn = nullptr;
ScalableButton* m_printhost_browse_btn = nullptr;
+ ScalableButton* m_printhost_browse_printer_btn = nullptr;
+ ScalableButton* m_printhost_slug_browse_btn = nullptr;
ScalableButton* m_reset_to_filament_color = nullptr;
size_t m_extruders_count = 0;
@@ -476,6 +478,7 @@ public:
void update_fff();
void update_sla();
void update_pages(); // update m_pages according to printer technology
+ void update_printers();
void extruders_count_changed(size_t extruders_count);
void milling_count_changed(size_t extruders_count);
PageShp build_kinematics_page();
diff --git a/src/slic3r/Utils/AstroBox.hpp b/src/slic3r/Utils/AstroBox.hpp
index 38542275c..85430e893 100644
--- a/src/slic3r/Utils/AstroBox.hpp
+++ b/src/slic3r/Utils/AstroBox.hpp
@@ -27,8 +27,12 @@ public:
bool has_auto_discovery() const override { return true; }
bool can_test() const override { return true; }
bool can_start_print() const override { return true; }
+ bool can_support_multiple_printers() const override { return false; }
std::string get_host() const override { return host; }
-
+
+ bool get_groups(wxArrayString &groups) const override { return false; }
+ bool get_printers(wxArrayString &printers) const override { return false; }
+
protected:
bool validate_version_text(const boost::optional &version_text) const;
diff --git a/src/slic3r/Utils/Duet.hpp b/src/slic3r/Utils/Duet.hpp
index 7fdd8ea10..24ef502f7 100644
--- a/src/slic3r/Utils/Duet.hpp
+++ b/src/slic3r/Utils/Duet.hpp
@@ -26,8 +26,12 @@ public:
bool has_auto_discovery() const override { return false; }
bool can_test() const override { return true; }
bool can_start_print() const override { return true; }
+ bool can_support_multiple_printers() const override { return false; }
std::string get_host() const override { return host; }
-
+
+ bool get_groups(wxArrayString &groups) const override { return false; }
+ bool get_printers(wxArrayString &printers) const override { return false; }
+
private:
enum class ConnectionType { rrf, dsf, error };
std::string host;
diff --git a/src/slic3r/Utils/FlashAir.hpp b/src/slic3r/Utils/FlashAir.hpp
index 40af48da1..15efc0912 100644
--- a/src/slic3r/Utils/FlashAir.hpp
+++ b/src/slic3r/Utils/FlashAir.hpp
@@ -27,8 +27,12 @@ public:
bool has_auto_discovery() const override { return false; }
bool can_test() const override { return true; }
bool can_start_print() const override { return false; }
+ bool can_support_multiple_printers() const override { return false; }
std::string get_host() const override { return host; }
-
+
+ bool get_groups(wxArrayString &groups) const override { return false; }
+ bool get_printers(wxArrayString &printers) const override { return false; }
+
private:
std::string host;
diff --git a/src/slic3r/Utils/OctoPrint.hpp b/src/slic3r/Utils/OctoPrint.hpp
index ed1c61bd6..190c90ee0 100644
--- a/src/slic3r/Utils/OctoPrint.hpp
+++ b/src/slic3r/Utils/OctoPrint.hpp
@@ -3,6 +3,7 @@
#include
#include
+#include
#include
#include "PrintHost.hpp"
@@ -29,10 +30,14 @@ public:
bool has_auto_discovery() const override { return true; }
bool can_test() const override { return true; }
bool can_start_print() const override { return true; }
+ bool can_support_multiple_printers() const override { return false; }
std::string get_host() const override { return host; }
const std::string& get_apikey() const { return apikey; }
const std::string& get_cafile() const { return cafile; }
+ bool get_groups(wxArrayString &groups) const override { return false; }
+ bool get_printers(wxArrayString &printers) const override { return false; }
+
protected:
virtual bool validate_version_text(const boost::optional &version_text) const;
diff --git a/src/slic3r/Utils/PrintHost.cpp b/src/slic3r/Utils/PrintHost.cpp
index 0a49b7815..4598db241 100644
--- a/src/slic3r/Utils/PrintHost.cpp
+++ b/src/slic3r/Utils/PrintHost.cpp
@@ -16,6 +16,7 @@
#include "Duet.hpp"
#include "FlashAir.hpp"
#include "AstroBox.hpp"
+#include "Repetier.hpp"
#include "../GUI/PrintHostDialogs.hpp"
namespace fs = boost::filesystem;
@@ -47,6 +48,7 @@ PrintHost* PrintHost::get_print_host(DynamicPrintConfig *config)
case htDuet: return new Duet(config);
case htFlashAir: return new FlashAir(config);
case htAstroBox: return new AstroBox(config);
+ case htRepetier: return new Repetier(config);
default: return nullptr;
}
} else {
diff --git a/src/slic3r/Utils/PrintHost.hpp b/src/slic3r/Utils/PrintHost.hpp
index e0aeb463c..6c835b9b5 100644
--- a/src/slic3r/Utils/PrintHost.hpp
+++ b/src/slic3r/Utils/PrintHost.hpp
@@ -7,6 +7,7 @@
#include
#include
+#include
#include "Http.hpp"
@@ -20,6 +21,9 @@ struct PrintHostUpload
{
boost::filesystem::path source_path;
boost::filesystem::path upload_path;
+
+ std::string group;
+
bool start_print = false;
};
@@ -41,7 +45,10 @@ public:
virtual bool has_auto_discovery() const = 0;
virtual bool can_test() const = 0;
virtual bool can_start_print() const = 0;
+ virtual bool can_support_multiple_printers() const = 0;
virtual std::string get_host() const = 0;
+ virtual bool get_groups(wxArrayString& groups) const = 0;
+ virtual bool get_printers(wxArrayString &printers) const = 0;
static PrintHost* get_print_host(DynamicPrintConfig *config);
diff --git a/src/slic3r/Utils/Repetier.cpp b/src/slic3r/Utils/Repetier.cpp
new file mode 100644
index 000000000..57feb9f12
--- /dev/null
+++ b/src/slic3r/Utils/Repetier.cpp
@@ -0,0 +1,263 @@
+#include "Repetier.hpp"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include "libslic3r/PrintConfig.hpp"
+#include "slic3r/GUI/I18N.hpp"
+#include "slic3r/GUI/GUI.hpp"
+#include "Http.hpp"
+
+
+namespace fs = boost::filesystem;
+namespace pt = boost::property_tree;
+
+
+namespace Slic3r {
+
+Repetier::Repetier(DynamicPrintConfig *config) :
+ host(config->opt_string("print_host")),
+ apikey(config->opt_string("printhost_apikey")),
+ cafile(config->opt_string("printhost_cafile")),
+ slug(config->opt_string("printhost_slug"))
+{}
+
+const char* Repetier::get_name() const { return "Repetier"; }
+
+bool Repetier::test(wxString &msg) const
+{
+ // Since the request is performed synchronously here,
+ // it is ok to refer to `msg` from within the closure
+
+ const char *name = get_name();
+
+ bool res = true;
+ auto url = make_url("printer/info");
+
+ BOOST_LOG_TRIVIAL(info) << boost::format("%1%: List version at: %2%") % name % url;
+
+ auto http = Http::get(std::move(url));
+ set_auth(http);
+
+ http.on_error([&](std::string body, std::string error, unsigned status) {
+ BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error getting version: %2%, HTTP %3%, body: `%4%`") % name % error % status % body;
+ res = false;
+ msg = format_error(body, error, status);
+ })
+ .on_complete([&, this](std::string body, unsigned) {
+ BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Got version: %2%") % name % body;
+
+ try {
+ std::stringstream ss(body);
+ pt::ptree ptree;
+ pt::read_json(ss, ptree);
+
+ const auto text = ptree.get_optional("name");
+ res = validate_version_text(text);
+ if (! res) {
+ msg = GUI::from_u8((boost::format(_utf8(L("Mismatched type of print host: %s"))) % (text ? *text : "Repetier")).str());
+ }
+ }
+ catch (const std::exception &) {
+ res = false;
+ msg = "Could not parse server response";
+ }
+ })
+ .perform_sync();
+
+ return res;
+}
+
+wxString Repetier::get_test_ok_msg () const
+{
+ return _(L("Connection to Repetier works correctly."));
+}
+
+wxString Repetier::get_test_failed_msg (wxString &msg) const
+{
+ return GUI::from_u8((boost::format("%s: %s\n\n%s")
+ % _utf8(L("Could not connect to Repetier"))
+ % std::string(msg.ToUTF8())
+ % _utf8(L("Note: Repetier version at least 0.90.0 is required."))).str());
+}
+
+bool Repetier::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const
+{
+ const char *name = get_name();
+
+ const auto upload_filename = upload_data.upload_path.filename();
+ const auto upload_parent_path = upload_data.upload_path.parent_path();
+
+ wxString test_msg;
+ if (! test(test_msg)) {
+ error_fn(std::move(test_msg));
+ return false;
+ }
+
+ bool res = true;
+
+ auto url = make_url((boost::format("printer/model/%1%") % slug).str());
+
+ BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Uploading file %2% at %3%, filename: %4%, path: %5%, print: %6%, group: %7%")
+ % name
+ % upload_data.source_path
+ % url
+ % upload_filename.string()
+ % upload_parent_path.string()
+ % upload_data.start_print
+ % upload_data.group;
+
+ auto http = Http::post(std::move(url));
+ set_auth(http);
+
+ if (! upload_data.group.empty() && upload_data.group != _utf8(L("Default"))) {
+ http.form_add("group", upload_data.group);
+ }
+
+ http.form_add("a", "upload")
+ .form_add_file("filename", upload_data.source_path.string(), upload_filename.string())
+ .on_complete([&](std::string body, unsigned status) {
+ BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: File uploaded: HTTP %2%: %3%") % name % status % body;
+ })
+ .on_error([&](std::string body, std::string error, unsigned status) {
+ BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error uploading file: %2%, HTTP %3%, body: `%4%`") % name % error % status % body;
+ error_fn(format_error(body, error, status));
+ res = false;
+ })
+ .on_progress([&](Http::Progress progress, bool &cancel) {
+ prorgess_fn(std::move(progress), cancel);
+ if (cancel) {
+ // Upload was canceled
+ BOOST_LOG_TRIVIAL(info) << "Repetier: Upload canceled";
+ res = false;
+ }
+ })
+ .perform_sync();
+
+ return res;
+}
+
+bool Repetier::validate_version_text(const boost::optional &version_text) const
+{
+ return version_text ? boost::starts_with(*version_text, "Repetier") : true;
+}
+
+void Repetier::set_auth(Http &http) const
+{
+ http.header("X-Api-Key", apikey);
+
+ if (! cafile.empty()) {
+ http.ca_file(cafile);
+ }
+}
+
+std::string Repetier::make_url(const std::string &path) const
+{
+ if (host.find("http://") == 0 || host.find("https://") == 0) {
+ if (host.back() == '/') {
+ return (boost::format("%1%%2%") % host % path).str();
+ } else {
+ return (boost::format("%1%/%2%") % host % path).str();
+ }
+ } else {
+ return (boost::format("http://%1%/%2%") % host % path).str();
+ }
+}
+
+bool Repetier::get_groups(wxArrayString& groups) const
+{
+ bool res = true;
+
+ const char *name = get_name();
+ auto url = make_url((boost::format("printer/api/%1%") % slug).str());
+
+ BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get groups at: %2%") % name % url;
+
+ auto http = Http::get(std::move(url));
+ set_auth(http);
+ http.form_add("a", "listModelGroups");
+ http.on_error([&](std::string body, std::string error, unsigned status) {
+ BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error getting version: %2%, HTTP %3%, body: `%4%`") % name % error % status % body;
+ })
+ .on_complete([&, this](std::string body, unsigned) {
+ BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Got groups: %2%") % name % body;
+
+ try {
+ std::stringstream ss(body);
+ pt::ptree ptree;
+ pt::read_json(ss, ptree);
+
+ BOOST_FOREACH(boost::property_tree::ptree::value_type &v, ptree.get_child("groupNames.")) {
+ if (v.second.data() == "#") {
+ groups.push_back(_utf8(L("Default")));
+ } else {
+ groups.push_back(v.second.data());
+ }
+ }
+ }
+ catch (const std::exception &) {
+ //msg = "Could not parse server response";
+ res = false;
+ }
+ })
+ .perform_sync();
+
+ return res;
+}
+
+bool Repetier::get_printers(wxArrayString& printers) const
+{
+ const char *name = get_name();
+
+ bool res = true;
+ auto url = make_url("printer/list");
+
+ BOOST_LOG_TRIVIAL(info) << boost::format("%1%: List printers at: %2%") % name % url;
+
+ auto http = Http::get(std::move(url));
+ set_auth(http);
+
+ auto &slugs = printers;
+ http.on_error([&](std::string body, std::string error, unsigned status) {
+ BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error listing printers: %2%, HTTP %3%, body: `%4%`") % name % error % status % body;
+ res = false;
+ })
+ .on_complete([&, this](std::string body, unsigned) {
+ BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Got printers: %2%") % name % body;
+
+ try {
+ std::stringstream ss(body);
+ pt::ptree ptree;
+ pt::read_json(ss, ptree);
+
+ const auto error = ptree.get_optional("error");
+ if (error) {
+ res = false;
+ } else {
+ BOOST_FOREACH(boost::property_tree::ptree::value_type &v, ptree.get_child("data.")) {
+ const auto slug = v.second.get("slug");
+ printers.push_back(slug);
+ }
+
+ //res = !printers.empty();
+ }
+ }
+ catch (const std::exception &) {
+ res = false;
+ }
+ })
+ .perform_sync();
+
+ return res;
+}
+
+}
diff --git a/src/slic3r/Utils/Repetier.hpp b/src/slic3r/Utils/Repetier.hpp
new file mode 100644
index 000000000..da58eafa3
--- /dev/null
+++ b/src/slic3r/Utils/Repetier.hpp
@@ -0,0 +1,52 @@
+#ifndef slic3r_Repetier_hpp_
+#define slic3r_Repetier_hpp_
+
+#include
+#include
+#include
+
+#include "PrintHost.hpp"
+
+
+namespace Slic3r {
+
+class DynamicPrintConfig;
+class Http;
+
+class Repetier : public PrintHost
+{
+public:
+ Repetier(DynamicPrintConfig *config);
+ ~Repetier() override = default;
+
+ const char* get_name() const;
+
+ bool test(wxString &curl_msg) const override;
+ wxString get_test_ok_msg () const override;
+ wxString get_test_failed_msg (wxString &msg) const override;
+ bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const override;
+ bool has_auto_discovery() const override { return false; }
+ bool can_test() const override { return true; }
+ bool can_start_print() const override { return false; }
+ bool can_support_multiple_printers() const override { return true; }
+ std::string get_host() const override { return host; }
+
+ bool get_groups(wxArrayString &groups) const override;
+ bool get_printers(wxArrayString &printers) const override;
+
+protected:
+ virtual bool validate_version_text(const boost::optional &version_text) const;
+
+private:
+ std::string host;
+ std::string apikey;
+ std::string cafile;
+ std::string slug;
+
+ void set_auth(Http &http) const;
+ std::string make_url(const std::string &path) const;
+};
+
+}
+
+#endif