Merge remote-tracking branch 'remotes/remi/CI' into dev

in particular, move the repetier stuff to physicalprinter
This commit is contained in:
supermerill 2020-10-27 17:52:46 +01:00
commit 160b509a46
39 changed files with 782 additions and 174 deletions

View File

@ -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`)_

View File

@ -35,7 +35,7 @@ This calibration will help you to choose the right retraction length for your ex
<h3>Remove filament slowdown</h3>
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").
<h3>Results</h3>
<p>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.</p>
<p>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.</p>
<p>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.
<h2>Example</h2>
<table width="100%">

View File

@ -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

View File

@ -598,7 +598,8 @@ Fill::do_gap_fill(const ExPolygons &gapfill_areas, const FillParams &params, 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 &params, 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();

View File

@ -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);

View File

@ -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<uint8_t>{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<uint8_t>{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,8 +3305,16 @@ 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;
@ -3574,6 +3592,8 @@ std::string GCode::extrude_perimeters(const Print &print, const std::vector<Obje
m_writer.apply_print_region_config(print.regions()[&region - &by_region.front()]->config());
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::vector<ObjectBy
m_writer.apply_print_region_config(print.regions()[&region - &by_region.front()]->config());
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;

View File

@ -31,6 +31,44 @@ void GCodeWriter::apply_print_region_config(const PrintRegionConfig& print_regio
config_region = &print_region_config;
}
std::vector<uint16_t> GCodeWriter::extruder_ids() const {
std::vector<uint16_t> out;
out.reserve(m_extruders.size());
for (const Extruder& e : m_extruders)
out.push_back(e.id());
return out;
}
std::vector<uint16_t> GCodeWriter::mill_ids() const {
std::vector<uint16_t> 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<uint16_t> 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

View File

@ -34,41 +34,21 @@ public:
// Extruders are expected to be sorted in an increasing order.
void set_extruders(std::vector<uint16_t> extruder_ids);
const std::vector<Extruder>& extruders() const { return m_extruders; }
std::vector<uint16_t> extruder_ids() const {
std::vector<uint16_t> out;
out.reserve(m_extruders.size());
for (const Extruder& e : m_extruders)
out.push_back(e.id());
return out;
}
std::vector<uint16_t> extruder_ids() const;
void set_mills(std::vector<uint16_t> extruder_ids);
const std::vector<Mill>& mills() const { return m_millers; }
std::vector<uint16_t> mill_ids() const {
std::vector<uint16_t> out;
out.reserve(m_millers.size());
for (const Tool& e : m_millers)
out.push_back(e.id());
return out;
}
std::vector<uint16_t> 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);

View File

@ -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

View File

@ -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<typename LINE>
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;

View File

@ -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<typename LINE>
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;

View File

@ -442,8 +442,9 @@ const std::vector<std::string>& 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<std::string>& 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<std::string>& 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<std::string>& 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<ConfigOptionString>("print_host");
auto opt_printhost_apikey_old = cfg_old.option<ConfigOptionString>("printhost_apikey");
auto opt_printhost_cafile_old = cfg_old.option<ConfigOptionString>("printhost_cafile");
auto opt_printhost_slug_old = cfg_old.option<ConfigOptionString>("printhost_slug");
auto opt_print_host_new = cfg_new.option<ConfigOptionString>("print_host");
auto opt_printhost_apikey_new = cfg_new.option<ConfigOptionString>("printhost_apikey");
auto opt_printhost_cafile_new = cfg_new.option<ConfigOptionString>("printhost_cafile");
auto opt_printhost_slug_new = cfg_new.option<ConfigOptionString>("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;
@ -926,7 +934,8 @@ static ProfileHostParams profile_host_params_same_or_anonymized(const DynamicPri
(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;
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.
@ -1567,6 +1577,7 @@ const std::vector<std::string>& PhysicalPrinter::printer_options()
"print_host",
"printhost_apikey",
"printhost_cafile",
"printhost_slug",
"printhost_authorization_type",
// HTTP digest authentization (RFC 2617)
"printhost_user",
@ -1583,7 +1594,8 @@ const std::vector<std::string>& 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()

View File

@ -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;
}

View File

@ -151,6 +151,13 @@ void PrintConfigDef::init_common_params()
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<PrintHostType>(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<std::string> ignore = {

View File

@ -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<PrintHostType>::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<NoPerimeterUnsupportedAlgo> 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<InfillPattern> 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);
}

View File

@ -682,7 +682,7 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
opt_key == "gap_fill"
|| opt_key == "gap_fill_min_area"
|| opt_key == "only_one_perimeter_top"
|| opt_key == "overhangs"
|| opt_key == "overhangs_width_speed"
|| opt_key == "overhangs_width"
|| opt_key == "overhangs_reverse"
|| opt_key == "overhangs_reverse_threshold"
@ -822,6 +822,8 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
|| opt_key == "seam_angle_cost"
|| opt_key == "seam_travel_cost"
|| opt_key == "small_perimeter_speed"
|| opt_key == "small_perimeter_min_length"
|| opt_key == "small_perimeter_max_length"
|| opt_key == "solid_infill_speed"
|| opt_key == "support_material_interface_speed"
|| opt_key == "support_material_speed"

View File

@ -2141,7 +2141,7 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object(
polygons_append(polygons_trimming,
offset(to_expolygons(region->fill_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)

View File

@ -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)); }

View File

@ -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

View File

@ -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 });
}

View File

@ -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<ConfigOptionInts>("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<ModelObject*>> 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<InfillPattern>(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));

View File

@ -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<ConfigOptionFloatOrPercent>("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<ConfigOptionFloatOrPercent>("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"));

View File

@ -862,9 +862,12 @@ void Choice::BUILD() {
*
* Note: Set bitmap height to the Font size because of OSX rendering.
*/
// 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());

View File

@ -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<DenseInfillAlgo>(boost::any_cast<DenseInfillAlgo>(value)));
else if (opt_key.compare("no_perimeter_unsupported_algo") == 0)
config.set_key_value(opt_key, new ConfigOptionEnum<DenseInfillAlgo>(boost::any_cast<DenseInfillAlgo>(value)));
config.set_key_value(opt_key, new ConfigOptionEnum<NoPerimeterUnsupportedAlgo>(boost::any_cast<NoPerimeterUnsupportedAlgo>(value)));
else if (opt_key.compare("infill_connection") == 0)
config.set_key_value(opt_key, new ConfigOptionEnum<InfillConnection>(boost::any_cast<InfillConnection>(value)));
else if (opt_key.compare("wipe_advanced_algo") == 0)

View File

@ -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<PrintHost> 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<PrintHost> host(PrintHost::get_print_host(m_config));
wxArrayString printers;
Field* rs = m_optgroup->get_field("printhost_slug");
if (!host->get_printers(printers)) {
std::vector<std::string> slugs;
Choice* choice = dynamic_cast<Choice*>(rs);
choice->set_values(slugs);
rs->disable();
} else {
std::vector<std::string> slugs;
for (int i = 0; i < printers.size(); i++) {
slugs.push_back(printers[i].ToStdString());
}
Choice* choice = dynamic_cast<Choice*>(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<ConfigOptionString>("printhost_slug")->value);
if ((val.empty() || (any_string_type.type() == val.type() && boost::any_cast<std::string>(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<ConfigOptionString>("printhost_slug")->value, false);
}
rs->enable();
}
}
wxString PhysicalPrinterDialog::get_printer_name()
{

View File

@ -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; }

View File

@ -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));
}

View File

@ -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();
@ -50,6 +52,20 @@ PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, bool can_start_pr
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));
@ -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);

View File

@ -7,6 +7,8 @@
#include <wx/string.h>
#include <wx/event.h>
#include <wx/dialog.h>
#include <wx/combobox.h>
#include <wx/arrstr.h>
#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;
};

View File

@ -2312,6 +2312,7 @@ void TabPrinter::build_sla()
}
void TabPrinter::extruders_count_changed(size_t extruders_count)
{
bool is_count_changed = false;

View File

@ -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();

View File

@ -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<std::string> &version_text) const;

View File

@ -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;

View File

@ -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;

View File

@ -3,6 +3,7 @@
#include <string>
#include <wx/string.h>
#include <wx/arrstr.h>
#include <boost/optional.hpp>
#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<std::string> &version_text) const;

View File

@ -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 {

View File

@ -7,6 +7,7 @@
#include <boost/filesystem/path.hpp>
#include <wx/string.h>
#include <wx/arrstr.h>
#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);

View File

@ -0,0 +1,263 @@
#include "Repetier.hpp"
#include <algorithm>
#include <sstream>
#include <exception>
#include <boost/foreach.hpp>
#include <boost/format.hpp>
#include <boost/log/trivial.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <wx/progdlg.h>
#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<std::string>("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<std::string> &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<std::string>("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<std::string>("slug");
printers.push_back(slug);
}
//res = !printers.empty();
}
}
catch (const std::exception &) {
res = false;
}
})
.perform_sync();
return res;
}
}

View File

@ -0,0 +1,52 @@
#ifndef slic3r_Repetier_hpp_
#define slic3r_Repetier_hpp_
#include <string>
#include <wx/string.h>
#include <boost/optional.hpp>
#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<std::string> &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