From d91f59379b3468f3cced6cd9fccf63e06dacaf67 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 6 Mar 2018 12:12:00 +0100 Subject: [PATCH 01/22] GCode Preview - Fixed values in range labels of legend texture --- xs/src/libslic3r/GCode/Analyzer.cpp | 12 +++++++ xs/src/libslic3r/GCode/PreviewData.cpp | 47 +++++++++++++++++++++----- xs/src/libslic3r/GCode/PreviewData.hpp | 16 +++++---- 3 files changed, 60 insertions(+), 15 deletions(-) diff --git a/xs/src/libslic3r/GCode/Analyzer.cpp b/xs/src/libslic3r/GCode/Analyzer.cpp index 6530806c4..d4041ac19 100644 --- a/xs/src/libslic3r/GCode/Analyzer.cpp +++ b/xs/src/libslic3r/GCode/Analyzer.cpp @@ -717,6 +717,10 @@ void GCodeAnalyzer::_calc_gcode_preview_travel(GCodePreviewData& preview_data) float feedrate = FLT_MAX; unsigned int extruder_id = -1; + GCodePreviewData::Range height_range; + GCodePreviewData::Range width_range; + GCodePreviewData::Range feedrate_range; + // constructs the polylines while traversing the moves for (const GCodeMove& move : travel_moves->second) { @@ -745,11 +749,19 @@ void GCodeAnalyzer::_calc_gcode_preview_travel(GCodePreviewData& preview_data) type = move_type; feedrate = move.data.feedrate; extruder_id = move.data.extruder_id; + height_range.update_from(move.data.height); + width_range.update_from(move.data.width); + feedrate_range.update_from(move.data.feedrate); } // store last polyline polyline.remove_duplicate_points(); Helper::store_polyline(polyline, type, direction, feedrate, extruder_id, preview_data); + + // updates preview ranges data + preview_data.travel.ranges.height.set_from(height_range); + preview_data.travel.ranges.width.set_from(width_range); + preview_data.travel.ranges.feedrate.set_from(feedrate_range); } void GCodeAnalyzer::_calc_gcode_preview_retractions(GCodePreviewData& preview_data) diff --git a/xs/src/libslic3r/GCode/PreviewData.cpp b/xs/src/libslic3r/GCode/PreviewData.cpp index 1923505e4..73ec30eef 100644 --- a/xs/src/libslic3r/GCode/PreviewData.cpp +++ b/xs/src/libslic3r/GCode/PreviewData.cpp @@ -85,6 +85,12 @@ void GCodePreviewData::Range::update_from(float value) max = std::max(max, value); } +void GCodePreviewData::Range::update_from(const Range& other) +{ + min = std::min(min, other.min); + max = std::max(max, other.max); +} + void GCodePreviewData::Range::set_from(const Range& other) { min = other.min; @@ -198,6 +204,11 @@ void GCodePreviewData::Travel::set_default() width = Default_Width; height = Default_Height; ::memcpy((void*)type_colors, (const void*)Default_Type_Colors, Num_Types * sizeof(Color)); + + ::memcpy((void*)ranges.height.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color)); + ::memcpy((void*)ranges.width.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color)); + ::memcpy((void*)ranges.feedrate.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color)); + is_visible = false; } @@ -345,15 +356,26 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std:: { struct Helper { - static void FillListFromRange(LegendItemsList& list, const Range& range, unsigned int decimals, float scale_factor) + static void FillListFromRange(LegendItemsList& list, const std::vector& ranges, unsigned int decimals, float scale_factor) { + if (ranges.empty()) + return; + list.reserve(Range::Colors_Count); - float step = range.step_size(); + + Range total_range; + for (const Range* range : ranges) + { + if (range != nullptr) + total_range.update_from(*range); + } + + float step = total_range.step_size(); for (unsigned int i = 0; i < Range::Colors_Count; ++i) { - char buf[32]; - sprintf(buf, "%.*f/%.*f", decimals, scale_factor * (range.min + (float)i * step), decimals, scale_factor * (range.min + (float)(i + 1) * step)); - list.emplace_back(buf, range.colors[i]); + char buf[1024]; + sprintf(buf, "%.*f/%.*f", decimals, scale_factor * (total_range.min + (float)i * step), decimals, scale_factor * (total_range.min + (float)(i + 1) * step)); + list.emplace_back(buf, ranges[0]->colors[i]); } } }; @@ -377,17 +399,26 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std:: } case Extrusion::Height: { - Helper::FillListFromRange(items, extrusion.ranges.height, 3, 1.0f); + std::vector ranges; + ranges.push_back(&extrusion.ranges.height); + ranges.push_back(&travel.ranges.height); + Helper::FillListFromRange(items, ranges, 3, 1.0f); break; } case Extrusion::Width: { - Helper::FillListFromRange(items, extrusion.ranges.width, 3, 1.0f); + std::vector ranges; + ranges.push_back(&extrusion.ranges.width); + ranges.push_back(&travel.ranges.width); + Helper::FillListFromRange(items, ranges, 3, 1.0f); break; } case Extrusion::Feedrate: { - Helper::FillListFromRange(items, extrusion.ranges.feedrate, 0, 1.0f); + std::vector ranges; + ranges.push_back(&extrusion.ranges.feedrate); + ranges.push_back(&travel.ranges.feedrate); + Helper::FillListFromRange(items, ranges, 0, 1.0f); break; } case Extrusion::Tool: diff --git a/xs/src/libslic3r/GCode/PreviewData.hpp b/xs/src/libslic3r/GCode/PreviewData.hpp index 9fb2dc464..9579bf900 100644 --- a/xs/src/libslic3r/GCode/PreviewData.hpp +++ b/xs/src/libslic3r/GCode/PreviewData.hpp @@ -37,6 +37,7 @@ public: void reset(); bool empty() const; void update_from(float value); + void update_from(const Range& other); void set_from(const Range& other); float step_size() const; @@ -44,6 +45,13 @@ public: const Color& get_color_at_max() const; }; + struct Ranges + { + Range height; + Range width; + Range feedrate; + }; + struct LegendItem { std::string text; @@ -71,13 +79,6 @@ public: static const std::string Default_Extrusion_Role_Names[Num_Extrusion_Roles]; static const EViewType Default_View_Type; - struct Ranges - { - Range height; - Range width; - Range feedrate; - }; - struct Layer { float z; @@ -140,6 +141,7 @@ public: float height; Color type_colors[Num_Types]; bool is_visible; + Ranges ranges; void set_default(); }; From fe59958ea84539c2252bc1a009247f997f4686b6 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 7 Mar 2018 09:17:59 +0100 Subject: [PATCH 02/22] GCode Preview - Unified preview data ranges to ensure proper paths colors --- xs/src/libslic3r/GCode/Analyzer.cpp | 12 +++--- xs/src/libslic3r/GCode/PreviewData.cpp | 59 +++++++++----------------- xs/src/libslic3r/GCode/PreviewData.hpp | 9 ++-- xs/src/slic3r/GUI/3DScene.cpp | 8 ++-- 4 files changed, 34 insertions(+), 54 deletions(-) diff --git a/xs/src/libslic3r/GCode/Analyzer.cpp b/xs/src/libslic3r/GCode/Analyzer.cpp index d4041ac19..ee1e55beb 100644 --- a/xs/src/libslic3r/GCode/Analyzer.cpp +++ b/xs/src/libslic3r/GCode/Analyzer.cpp @@ -688,9 +688,9 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ Helper::store_polyline(polyline, data, z, preview_data); // updates preview ranges data - preview_data.extrusion.ranges.height.set_from(height_range); - preview_data.extrusion.ranges.width.set_from(width_range); - preview_data.extrusion.ranges.feedrate.set_from(feedrate_range); + preview_data.ranges.height.set_from(height_range); + preview_data.ranges.width.set_from(width_range); + preview_data.ranges.feedrate.set_from(feedrate_range); } void GCodeAnalyzer::_calc_gcode_preview_travel(GCodePreviewData& preview_data) @@ -759,9 +759,9 @@ void GCodeAnalyzer::_calc_gcode_preview_travel(GCodePreviewData& preview_data) Helper::store_polyline(polyline, type, direction, feedrate, extruder_id, preview_data); // updates preview ranges data - preview_data.travel.ranges.height.set_from(height_range); - preview_data.travel.ranges.width.set_from(width_range); - preview_data.travel.ranges.feedrate.set_from(feedrate_range); + preview_data.ranges.height.set_from(height_range); + preview_data.ranges.width.set_from(width_range); + preview_data.ranges.feedrate.set_from(feedrate_range); } void GCodeAnalyzer::_calc_gcode_preview_retractions(GCodePreviewData& preview_data) diff --git a/xs/src/libslic3r/GCode/PreviewData.cpp b/xs/src/libslic3r/GCode/PreviewData.cpp index 73ec30eef..b9be6643b 100644 --- a/xs/src/libslic3r/GCode/PreviewData.cpp +++ b/xs/src/libslic3r/GCode/PreviewData.cpp @@ -164,9 +164,6 @@ void GCodePreviewData::Extrusion::set_default() view_type = Default_View_Type; ::memcpy((void*)role_colors, (const void*)Default_Extrusion_Role_Colors, Num_Extrusion_Roles * sizeof(Color)); - ::memcpy((void*)ranges.height.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color)); - ::memcpy((void*)ranges.width.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color)); - ::memcpy((void*)ranges.feedrate.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color)); for (unsigned int i = 0; i < Num_Extrusion_Roles; ++i) { @@ -205,10 +202,6 @@ void GCodePreviewData::Travel::set_default() height = Default_Height; ::memcpy((void*)type_colors, (const void*)Default_Type_Colors, Num_Types * sizeof(Color)); - ::memcpy((void*)ranges.height.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color)); - ::memcpy((void*)ranges.width.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color)); - ::memcpy((void*)ranges.feedrate.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color)); - is_visible = false; } @@ -239,6 +232,10 @@ GCodePreviewData::GCodePreviewData() void GCodePreviewData::set_default() { + ::memcpy((void*)ranges.height.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color)); + ::memcpy((void*)ranges.width.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color)); + ::memcpy((void*)ranges.feedrate.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color)); + extrusion.set_default(); travel.set_default(); retraction.set_default(); @@ -248,6 +245,9 @@ void GCodePreviewData::set_default() void GCodePreviewData::reset() { + ranges.width.reset(); + ranges.height.reset(); + ranges.feedrate.reset(); extrusion.layers.clear(); travel.polylines.clear(); retraction.positions.clear(); @@ -264,19 +264,19 @@ const GCodePreviewData::Color& GCodePreviewData::get_extrusion_role_color(Extrus return extrusion.role_colors[role]; } -const GCodePreviewData::Color& GCodePreviewData::get_extrusion_height_color(float height) const +const GCodePreviewData::Color& GCodePreviewData::get_height_color(float height) const { - return extrusion.ranges.height.get_color_at(height); + return ranges.height.get_color_at(height); } -const GCodePreviewData::Color& GCodePreviewData::get_extrusion_width_color(float width) const +const GCodePreviewData::Color& GCodePreviewData::get_width_color(float width) const { - return extrusion.ranges.width.get_color_at(width); + return ranges.width.get_color_at(width); } -const GCodePreviewData::Color& GCodePreviewData::get_extrusion_feedrate_color(float feedrate) const +const GCodePreviewData::Color& GCodePreviewData::get_feedrate_color(float feedrate) const { - return extrusion.ranges.feedrate.get_color_at(feedrate); + return ranges.feedrate.get_color_at(feedrate); } void GCodePreviewData::set_extrusion_role_color(const std::string& role_name, float red, float green, float blue, float alpha) @@ -356,26 +356,16 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std:: { struct Helper { - static void FillListFromRange(LegendItemsList& list, const std::vector& ranges, unsigned int decimals, float scale_factor) + static void FillListFromRange(LegendItemsList& list, const Range& range, unsigned int decimals, float scale_factor) { - if (ranges.empty()) - return; - list.reserve(Range::Colors_Count); - Range total_range; - for (const Range* range : ranges) - { - if (range != nullptr) - total_range.update_from(*range); - } - - float step = total_range.step_size(); + float step = range.step_size(); for (unsigned int i = 0; i < Range::Colors_Count; ++i) { char buf[1024]; - sprintf(buf, "%.*f/%.*f", decimals, scale_factor * (total_range.min + (float)i * step), decimals, scale_factor * (total_range.min + (float)(i + 1) * step)); - list.emplace_back(buf, ranges[0]->colors[i]); + sprintf(buf, "%.*f/%.*f", decimals, scale_factor * (range.min + (float)i * step), decimals, scale_factor * (range.min + (float)(i + 1) * step)); + list.emplace_back(buf, range.colors[i]); } } }; @@ -399,26 +389,17 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std:: } case Extrusion::Height: { - std::vector ranges; - ranges.push_back(&extrusion.ranges.height); - ranges.push_back(&travel.ranges.height); - Helper::FillListFromRange(items, ranges, 3, 1.0f); + Helper::FillListFromRange(items, ranges.height, 3, 1.0f); break; } case Extrusion::Width: { - std::vector ranges; - ranges.push_back(&extrusion.ranges.width); - ranges.push_back(&travel.ranges.width); - Helper::FillListFromRange(items, ranges, 3, 1.0f); + Helper::FillListFromRange(items, ranges.width, 3, 1.0f); break; } case Extrusion::Feedrate: { - std::vector ranges; - ranges.push_back(&extrusion.ranges.feedrate); - ranges.push_back(&travel.ranges.feedrate); - Helper::FillListFromRange(items, ranges, 0, 1.0f); + Helper::FillListFromRange(items, ranges.feedrate, 0, 1.0f); break; } case Extrusion::Tool: diff --git a/xs/src/libslic3r/GCode/PreviewData.hpp b/xs/src/libslic3r/GCode/PreviewData.hpp index 9579bf900..6375af5f0 100644 --- a/xs/src/libslic3r/GCode/PreviewData.hpp +++ b/xs/src/libslic3r/GCode/PreviewData.hpp @@ -92,7 +92,6 @@ public: EViewType view_type; Color role_colors[Num_Extrusion_Roles]; std::string role_names[Num_Extrusion_Roles]; - Ranges ranges; LayersList layers; unsigned int role_flags; @@ -141,7 +140,6 @@ public: float height; Color type_colors[Num_Types]; bool is_visible; - Ranges ranges; void set_default(); }; @@ -180,6 +178,7 @@ public: Retraction retraction; Retraction unretraction; Shell shell; + Ranges ranges; GCodePreviewData(); @@ -188,9 +187,9 @@ public: bool empty() const; const Color& get_extrusion_role_color(ExtrusionRole role) const; - const Color& get_extrusion_height_color(float height) const; - const Color& get_extrusion_width_color(float width) const; - const Color& get_extrusion_feedrate_color(float feedrate) const; + const Color& get_height_color(float height) const; + const Color& get_width_color(float width) const; + const Color& get_feedrate_color(float feedrate) const; void set_extrusion_role_color(const std::string& role_name, float red, float green, float blue, float alpha); void set_extrusion_paths_colors(const std::vector& colors); diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 04e7f7dc6..8a1b19c51 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -1777,11 +1777,11 @@ void _3DScene::_load_gcode_extrusion_paths(const GCodePreviewData& preview_data, case GCodePreviewData::Extrusion::FeatureType: return data.get_extrusion_role_color((ExtrusionRole)(int)value); case GCodePreviewData::Extrusion::Height: - return data.get_extrusion_height_color(value); + return data.get_height_color(value); case GCodePreviewData::Extrusion::Width: - return data.get_extrusion_width_color(value); + return data.get_width_color(value); case GCodePreviewData::Extrusion::Feedrate: - return data.get_extrusion_feedrate_color(value); + return data.get_feedrate_color(value); case GCodePreviewData::Extrusion::Tool: { static GCodePreviewData::Color color; @@ -2061,7 +2061,7 @@ bool _3DScene::_travel_paths_by_feedrate(const GCodePreviewData& preview_data, G // creates a new volume for each feedrate for (Feedrate& feedrate : feedrates) { - GLVolume* volume = new GLVolume(preview_data.get_extrusion_feedrate_color(feedrate.value).rgba); + GLVolume* volume = new GLVolume(preview_data.get_feedrate_color(feedrate.value).rgba); if (volume == nullptr) return false; else From d68804772aaa20e7b4e37c3a880896ed3782d238 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 9 Mar 2018 15:27:38 +0100 Subject: [PATCH 03/22] GCode Preview - Added handling of G10 and G11 commands --- xs/src/libslic3r/GCode/Analyzer.cpp | 22 ++++++++++++++++++++++ xs/src/libslic3r/GCode/Analyzer.hpp | 6 ++++++ 2 files changed, 28 insertions(+) diff --git a/xs/src/libslic3r/GCode/Analyzer.cpp b/xs/src/libslic3r/GCode/Analyzer.cpp index ee1e55beb..15b99b2fb 100644 --- a/xs/src/libslic3r/GCode/Analyzer.cpp +++ b/xs/src/libslic3r/GCode/Analyzer.cpp @@ -177,6 +177,16 @@ void GCodeAnalyzer::_process_gcode_line(GCodeReader&, const GCodeReader::GCodeLi _processG1(line); break; } + case 10: // Retract + { + _processG10(line); + break; + } + case 11: // Unretract + { + _processG11(line); + break; + } case 22: // Firmware controlled Retract { _processG22(line); @@ -305,6 +315,18 @@ void GCodeAnalyzer::_processG1(const GCodeReader::GCodeLine& line) _store_move(type); } +void GCodeAnalyzer::_processG10(const GCodeReader::GCodeLine& line) +{ + // stores retract move + _store_move(GCodeMove::Retract); +} + +void GCodeAnalyzer::_processG11(const GCodeReader::GCodeLine& line) +{ + // stores unretract move + _store_move(GCodeMove::Unretract); +} + void GCodeAnalyzer::_processG22(const GCodeReader::GCodeLine& line) { // stores retract move diff --git a/xs/src/libslic3r/GCode/Analyzer.hpp b/xs/src/libslic3r/GCode/Analyzer.hpp index 7939d432d..e2f38292e 100644 --- a/xs/src/libslic3r/GCode/Analyzer.hpp +++ b/xs/src/libslic3r/GCode/Analyzer.hpp @@ -127,6 +127,12 @@ private: // Move void _processG1(const GCodeReader::GCodeLine& line); + // Retract + void _processG10(const GCodeReader::GCodeLine& line); + + // Unretract + void _processG11(const GCodeReader::GCodeLine& line); + // Firmware controlled Retract void _processG22(const GCodeReader::GCodeLine& line); From 1f3f109263af5a7dccde6371bc7e75da335ea79f Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 20 Mar 2018 09:31:42 +0100 Subject: [PATCH 04/22] Out of bed detection - Fixed false detection due to float precision --- xs/src/libslic3r/Model.cpp | 4 ++++ xs/src/slic3r/GUI/3DScene.cpp | 2 ++ 2 files changed, 6 insertions(+) diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp index 6f1ce1ce5..ad2ce54cd 100644 --- a/xs/src/libslic3r/Model.cpp +++ b/xs/src/libslic3r/Model.cpp @@ -450,6 +450,8 @@ bool Model::fits_print_volume(const DynamicPrintConfig* config) const BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values)); BoundingBoxf3 print_volume(Pointf3(unscale(bed_box_2D.min.x), unscale(bed_box_2D.min.y), 0.0), Pointf3(unscale(bed_box_2D.max.x), unscale(bed_box_2D.max.y), config->opt_float("max_print_height"))); + // Allow the objects to protrude below the print bed + print_volume.min.z = -1e10; return print_volume.contains(transformed_bounding_box()); } @@ -459,6 +461,8 @@ bool Model::fits_print_volume(const FullPrintConfig &config) const return true; BoundingBox bed_box_2D = get_extents(Polygon::new_scale(config.bed_shape.values)); BoundingBoxf3 print_volume(Pointf3(unscale(bed_box_2D.min.x), unscale(bed_box_2D.min.y), 0.0), Pointf3(unscale(bed_box_2D.max.x), unscale(bed_box_2D.max.y), config.max_print_height)); + // Allow the objects to protrude below the print bed + print_volume.min.z = -1e10; return print_volume.contains(transformed_bounding_box()); } diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index f6be96a78..cb5d580c1 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -627,6 +627,8 @@ void GLVolumeCollection::update_outside_state(const DynamicPrintConfig* config, BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values)); BoundingBoxf3 print_volume(Pointf3(unscale(bed_box_2D.min.x), unscale(bed_box_2D.min.y), 0.0), Pointf3(unscale(bed_box_2D.max.x), unscale(bed_box_2D.max.y), config->opt_float("max_print_height"))); + // Allow the objects to protrude below the print bed + print_volume.min.z = -1e10; for (GLVolume* volume : this->volumes) { From 6298a2849484def97e5af36943647c84497812ab Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 20 Mar 2018 11:59:33 +0100 Subject: [PATCH 05/22] Disabled back face culling to show broken geometry --- lib/Slic3r/GUI/3DScene.pm | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 75a154281..b4f1849e8 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -1316,12 +1316,18 @@ sub Render { $self->mark_volumes_for_layer_height; $self->volumes->set_print_box($self->bed_bounding_box->x_min, $self->bed_bounding_box->y_min, 0.0, $self->bed_bounding_box->x_max, $self->bed_bounding_box->y_max, $self->{config}->get('max_print_height')); $self->volumes->update_outside_state($self->{config}, 0); + # do not cull backfaces to show broken geometry, if any + glDisable(GL_CULL_FACE); } $self->{plain_shader}->enable if $self->{plain_shader}; $self->volumes->render_VBOs; $self->{plain_shader}->disable; + glEnable(GL_CULL_FACE) if ($self->enable_picking); } else { + # do not cull backfaces to show broken geometry, if any + glDisable(GL_CULL_FACE) if ($self->enable_picking); $self->volumes->render_legacy; + glEnable(GL_CULL_FACE) if ($self->enable_picking); } # draw cutting plane @@ -1358,6 +1364,9 @@ sub draw_volumes { # $fakecolor is a boolean indicating, that the objects shall be rendered in a color coding the object index for picking. my ($self, $fakecolor) = @_; + # do not cull backfaces to show broken geometry, if any + glDisable(GL_CULL_FACE); + glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -1385,6 +1394,8 @@ sub draw_volumes { } glDisableClientState(GL_NORMAL_ARRAY); glDisable(GL_BLEND); + + glEnable(GL_CULL_FACE); if (defined $self->cutting_plane_z) { glLineWidth(2); From f99aaa1191245a61ded3e8c70ffdd6548612a8dd Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 20 Mar 2018 13:01:50 +0100 Subject: [PATCH 06/22] Out of bed detection - New colors for out of bed state --- lib/Slic3r/GUI/3DScene.pm | 11 +++++------ xs/src/slic3r/GUI/3DScene.cpp | 4 ++-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index b4f1849e8..420ea493a 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -1823,6 +1823,7 @@ sub _fragment_shader_Gouraud { return <<'FRAGMENT'; #version 110 +const vec4 OUTSIDE_COLOR = vec4(0.24, 0.42, 0.62, 1.0); const vec3 ZERO = vec3(0.0, 0.0, 0.0); // x = tainted, y = specular; @@ -1835,13 +1836,11 @@ uniform vec4 uniform_color; void main() { - gl_FragColor = vec4(intensity.y, intensity.y, intensity.y, 0.0) + uniform_color * intensity.x; + // if the fragment is outside the print volume use predefined color + vec4 color = (any(lessThan(delta_box_min, ZERO)) || any(greaterThan(delta_box_max, ZERO))) ? OUTSIDE_COLOR : uniform_color; - // if the fragment is outside the print volume darken it and set it as transparent - if (any(lessThan(delta_box_min, ZERO)) || any(greaterThan(delta_box_max, ZERO))) - gl_FragColor = vec4(mix(gl_FragColor.xyz, ZERO, 0.5), 0.5 * uniform_color.a); - else - gl_FragColor.a = uniform_color.a; + gl_FragColor = vec4(intensity.y, intensity.y, intensity.y, 0.0) + color * intensity.x; + gl_FragColor.a = color.a; } FRAGMENT diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index cb5d580c1..0bd7008d2 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -194,8 +194,8 @@ void GLIndexedVertexArray::render( const float GLVolume::SELECTED_COLOR[4] = { 0.0f, 1.0f, 0.0f, 1.0f }; const float GLVolume::HOVER_COLOR[4] = { 0.4f, 0.9f, 0.1f, 1.0f }; -const float GLVolume::OUTSIDE_COLOR[4] = { 0.75f, 0.0f, 0.75f, 1.0f }; -const float GLVolume::SELECTED_OUTSIDE_COLOR[4] = { 1.0f, 0.0f, 1.0f, 1.0f }; +const float GLVolume::OUTSIDE_COLOR[4] = { 0.0f, 0.38f, 0.8f, 1.0f }; +const float GLVolume::SELECTED_OUTSIDE_COLOR[4] = { 0.19f, 0.58f, 1.0f, 1.0f }; void GLVolume::set_render_color(float r, float g, float b, float a) { From 4a179c81d29eaad4dc3b4a107a31809b68615076 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 21 Mar 2018 10:03:10 +0100 Subject: [PATCH 07/22] GCode Preview - Added visualization of volumetric flow rate --- lib/Slic3r/GUI/Plater/3DPreview.pm | 1 + xs/src/libslic3r/GCode/Analyzer.cpp | 7 ++++++- xs/src/libslic3r/GCode/PreviewData.cpp | 14 ++++++++++++++ xs/src/libslic3r/GCode/PreviewData.hpp | 3 +++ xs/src/slic3r/GUI/3DScene.cpp | 4 ++++ 5 files changed, 28 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/Plater/3DPreview.pm b/lib/Slic3r/GUI/Plater/3DPreview.pm index 8f3fa49f5..537cb0c8f 100644 --- a/lib/Slic3r/GUI/Plater/3DPreview.pm +++ b/lib/Slic3r/GUI/Plater/3DPreview.pm @@ -69,6 +69,7 @@ sub new { $choice_view_type->Append(L("Height")); $choice_view_type->Append(L("Width")); $choice_view_type->Append(L("Speed")); + $choice_view_type->Append(L("Volumetric flow rate")); $choice_view_type->Append(L("Tool")); $choice_view_type->SetSelection(0); diff --git a/xs/src/libslic3r/GCode/Analyzer.cpp b/xs/src/libslic3r/GCode/Analyzer.cpp index 15b99b2fb..e20f6589f 100644 --- a/xs/src/libslic3r/GCode/Analyzer.cpp +++ b/xs/src/libslic3r/GCode/Analyzer.cpp @@ -670,14 +670,16 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ float z = FLT_MAX; Polyline polyline; Pointf3 position(FLT_MAX, FLT_MAX, FLT_MAX); + float volumetric_rate = FLT_MAX; GCodePreviewData::Range height_range; GCodePreviewData::Range width_range; GCodePreviewData::Range feedrate_range; + GCodePreviewData::Range volumetric_rate_range; // constructs the polylines while traversing the moves for (const GCodeMove& move : extrude_moves->second) { - if ((data != move.data) || (data.feedrate != move.data.feedrate) || (z != move.start_position.z) || (position != move.start_position)) + if ((data != move.data) || (z != move.start_position.z) || (position != move.start_position) || (volumetric_rate != move.data.feedrate * (float)move.data.mm3_per_mm)) { // store current polyline polyline.remove_duplicate_points(); @@ -693,9 +695,11 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ // update current values data = move.data; z = move.start_position.z; + volumetric_rate = move.data.feedrate * (float)move.data.mm3_per_mm; height_range.update_from(move.data.height); width_range.update_from(move.data.width); feedrate_range.update_from(move.data.feedrate); + volumetric_rate_range.update_from(volumetric_rate); } else // append end vertex of the move to current polyline @@ -713,6 +717,7 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ preview_data.ranges.height.set_from(height_range); preview_data.ranges.width.set_from(width_range); preview_data.ranges.feedrate.set_from(feedrate_range); + preview_data.ranges.volumetric_rate.set_from(volumetric_rate_range); } void GCodeAnalyzer::_calc_gcode_preview_travel(GCodePreviewData& preview_data) diff --git a/xs/src/libslic3r/GCode/PreviewData.cpp b/xs/src/libslic3r/GCode/PreviewData.cpp index b9be6643b..69fd3524d 100644 --- a/xs/src/libslic3r/GCode/PreviewData.cpp +++ b/xs/src/libslic3r/GCode/PreviewData.cpp @@ -235,6 +235,7 @@ void GCodePreviewData::set_default() ::memcpy((void*)ranges.height.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color)); ::memcpy((void*)ranges.width.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color)); ::memcpy((void*)ranges.feedrate.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color)); + ::memcpy((void*)ranges.volumetric_rate.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color)); extrusion.set_default(); travel.set_default(); @@ -248,6 +249,7 @@ void GCodePreviewData::reset() ranges.width.reset(); ranges.height.reset(); ranges.feedrate.reset(); + ranges.volumetric_rate.reset(); extrusion.layers.clear(); travel.polylines.clear(); retraction.positions.clear(); @@ -279,6 +281,11 @@ const GCodePreviewData::Color& GCodePreviewData::get_feedrate_color(float feedra return ranges.feedrate.get_color_at(feedrate); } +const GCodePreviewData::Color& GCodePreviewData::get_volumetric_rate_color(float rate) const +{ + return ranges.volumetric_rate.get_color_at(rate); +} + void GCodePreviewData::set_extrusion_role_color(const std::string& role_name, float red, float green, float blue, float alpha) { for (unsigned int i = 0; i < Extrusion::Num_Extrusion_Roles; ++i) @@ -345,6 +352,8 @@ std::string GCodePreviewData::get_legend_title() const return L("Width (mm)"); case Extrusion::Feedrate: return L("Speed (mm/s)"); + case Extrusion::VolumetricRate: + return L("Volumetric flow rate (mm3/s)"); case Extrusion::Tool: return L("Tool"); } @@ -402,6 +411,11 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std:: Helper::FillListFromRange(items, ranges.feedrate, 0, 1.0f); break; } + case Extrusion::VolumetricRate: + { + Helper::FillListFromRange(items, ranges.volumetric_rate, 3, 1.0f); + break; + } case Extrusion::Tool: { unsigned int tools_colors_count = tool_colors.size() / 4; diff --git a/xs/src/libslic3r/GCode/PreviewData.hpp b/xs/src/libslic3r/GCode/PreviewData.hpp index 6375af5f0..e9c5f7515 100644 --- a/xs/src/libslic3r/GCode/PreviewData.hpp +++ b/xs/src/libslic3r/GCode/PreviewData.hpp @@ -50,6 +50,7 @@ public: Range height; Range width; Range feedrate; + Range volumetric_rate; }; struct LegendItem @@ -70,6 +71,7 @@ public: Height, Width, Feedrate, + VolumetricRate, Tool, Num_View_Types }; @@ -190,6 +192,7 @@ public: const Color& get_height_color(float height) const; const Color& get_width_color(float width) const; const Color& get_feedrate_color(float feedrate) const; + const Color& get_volumetric_rate_color(float rate) const; void set_extrusion_role_color(const std::string& role_name, float red, float green, float blue, float alpha); void set_extrusion_paths_colors(const std::vector& colors); diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 4762fa54c..eafca6cd1 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -2039,6 +2039,8 @@ void _3DScene::_load_gcode_extrusion_paths(const GCodePreviewData& preview_data, return path.width; case GCodePreviewData::Extrusion::Feedrate: return path.feedrate; + case GCodePreviewData::Extrusion::VolumetricRate: + return path.feedrate * (float)path.mm3_per_mm; case GCodePreviewData::Extrusion::Tool: return (float)path.extruder_id; } @@ -2058,6 +2060,8 @@ void _3DScene::_load_gcode_extrusion_paths(const GCodePreviewData& preview_data, return data.get_width_color(value); case GCodePreviewData::Extrusion::Feedrate: return data.get_feedrate_color(value); + case GCodePreviewData::Extrusion::VolumetricRate: + return data.get_volumetric_rate_color(value); case GCodePreviewData::Extrusion::Tool: { static GCodePreviewData::Color color; From ebb2d457610d1dc67f17e1911198738a1bb756c7 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 21 Mar 2018 15:21:03 +0100 Subject: [PATCH 08/22] Out of bed detection - Disabled GUI buttons when object outside bed --- lib/Slic3r/GUI/Plater.pm | 11 +++++++++++ lib/Slic3r/GUI/Plater/3D.pm | 9 ++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index d3513897f..c63f8b8b7 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -98,6 +98,16 @@ sub new { $self->update; }; + # callback to enable/disable action buttons + my $enable_action_buttons = sub { + my ($enable) = @_; + $self->{btn_export_gcode}->Enable($enable); + $self->{btn_reslice}->Enable($enable); + $self->{btn_print}->Enable($enable); + $self->{btn_send_gcode}->Enable($enable); + $self->{btn_export_stl}->Enable($enable); + }; + # Initialize 3D plater if ($Slic3r::GUI::have_OpenGL) { $self->{canvas3D} = Slic3r::GUI::Plater::3D->new($self->{preview_notebook}, $self->{objects}, $self->{model}, $self->{print}, $self->{config}); @@ -113,6 +123,7 @@ sub new { $self->{canvas3D}->set_on_decrease_objects(sub { $self->decrease() }); $self->{canvas3D}->set_on_remove_object(sub { $self->remove() }); $self->{canvas3D}->set_on_instances_moved($on_instances_moved); + $self->{canvas3D}->set_on_enable_action_buttons($enable_action_buttons); $self->{canvas3D}->use_plain_shader(1); $self->{canvas3D}->set_on_wipe_tower_moved(sub { my ($new_pos_3f) = @_; diff --git a/lib/Slic3r/GUI/Plater/3D.pm b/lib/Slic3r/GUI/Plater/3D.pm index 37e1321ae..09cc02930 100644 --- a/lib/Slic3r/GUI/Plater/3D.pm +++ b/lib/Slic3r/GUI/Plater/3D.pm @@ -12,7 +12,7 @@ use Wx::Locale gettext => 'L'; __PACKAGE__->mk_accessors(qw( on_arrange on_rotate_object_left on_rotate_object_right on_scale_object_uniformly - on_remove_object on_increase_objects on_decrease_objects)); + on_remove_object on_increase_objects on_decrease_objects on_enable_action_buttons)); sub new { my $class = shift; @@ -176,6 +176,11 @@ sub set_on_model_update { $self->on_model_update($cb); } +sub set_on_enable_action_buttons { + my ($self, $cb) = @_; + $self->on_enable_action_buttons($cb); +} + sub reload_scene { my ($self, $force) = @_; @@ -217,10 +222,12 @@ sub reload_scene { if (!$self->{model}->fits_print_volume($self->{config})) { $self->set_warning_enabled(1); Slic3r::GUI::_3DScene::generate_warning_texture(L("Detected object outside print volume")); + $self->on_enable_action_buttons->(0) if ($self->on_enable_action_buttons); } else { $self->set_warning_enabled(0); $self->volumes->update_outside_state($self->{config}, 1); Slic3r::GUI::_3DScene::reset_warning_texture(); + $self->on_enable_action_buttons->(1) if ($self->on_enable_action_buttons); } } } From 4b8bd48663106c133914d4a89c80d89d57fe7ed7 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 22 Mar 2018 13:49:48 +0100 Subject: [PATCH 09/22] AMF and 3MF export - Export of print config customizable by user in the select file dialog --- lib/Slic3r/GUI/Plater.pm | 6 +++-- xs/src/libslic3r/Format/3mf.cpp | 25 ++++++++++-------- xs/src/libslic3r/Format/3mf.hpp | 2 +- xs/src/libslic3r/Format/AMF.cpp | 14 +++++----- xs/src/libslic3r/Format/AMF.hpp | 2 +- xs/src/slic3r/GUI/GUI.cpp | 45 +++++++++++++++++++++++++++++++++ xs/src/slic3r/GUI/GUI.hpp | 3 +++ xs/xsp/GUI.xsp | 7 +++++ xs/xsp/Model.xsp | 8 +++--- 9 files changed, 87 insertions(+), 25 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index d3513897f..1b26857e9 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -1560,7 +1560,7 @@ sub export_amf { return if !@{$self->{objects}}; # Ask user for a file name to write into. my $output_file = $self->_get_export_file('AMF') or return; - my $res = $self->{model}->store_amf($output_file, $self->{print}); + my $res = $self->{model}->store_amf($output_file, $self->{print}, $self->{export_option}); if ($res) { $self->statusbar->SetStatusText(L("AMF file exported to ").$output_file); @@ -1576,7 +1576,7 @@ sub export_3mf { return if !@{$self->{objects}}; # Ask user for a file name to write into. my $output_file = $self->_get_export_file('3MF') or return; - my $res = $self->{model}->store_3mf($output_file, $self->{print}); + my $res = $self->{model}->store_3mf($output_file, $self->{print}, $self->{export_option}); if ($res) { $self->statusbar->SetStatusText(L("3MF file exported to ").$output_file); @@ -1618,11 +1618,13 @@ sub _get_export_file { $output_file =~ s/\.[gG][cC][oO][dD][eE]$/$suffix/; my $dlg = Wx::FileDialog->new($self, L("Save ").$format.L(" file as:"), dirname($output_file), basename($output_file), &Slic3r::GUI::FILE_WILDCARDS->{$wildcard}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT); + Slic3r::GUI::add_export_option($dlg, $format); if ($dlg->ShowModal != wxID_OK) { $dlg->Destroy; return undef; } $output_file = $dlg->GetPath; + $self->{export_option} = Slic3r::GUI::get_export_option($dlg); $dlg->Destroy; return $output_file; } diff --git a/xs/src/libslic3r/Format/3mf.cpp b/xs/src/libslic3r/Format/3mf.cpp index b34b8989e..89f9b277f 100644 --- a/xs/src/libslic3r/Format/3mf.cpp +++ b/xs/src/libslic3r/Format/3mf.cpp @@ -1443,10 +1443,10 @@ namespace Slic3r { IdToObjectDataMap m_objects_data; public: - bool save_model_to_file(const std::string& filename, Model& model, const Print& print); + bool save_model_to_file(const std::string& filename, Model& model, const Print& print, bool export_print_config); private: - bool _save_model_to_file(const std::string& filename, Model& model, const Print& print); + bool _save_model_to_file(const std::string& filename, Model& model, const Print& print, bool export_print_config); bool _add_content_types_file_to_archive(mz_zip_archive& archive); bool _add_relationships_file_to_archive(mz_zip_archive& archive); bool _add_model_file_to_archive(mz_zip_archive& archive, Model& model); @@ -1457,13 +1457,13 @@ namespace Slic3r { bool _add_model_config_file_to_archive(mz_zip_archive& archive, const Model& model); }; - bool _3MF_Exporter::save_model_to_file(const std::string& filename, Model& model, const Print& print) + bool _3MF_Exporter::save_model_to_file(const std::string& filename, Model& model, const Print& print, bool export_print_config) { clear_errors(); - return _save_model_to_file(filename, model, print); + return _save_model_to_file(filename, model, print, export_print_config); } - bool _3MF_Exporter::_save_model_to_file(const std::string& filename, Model& model, const Print& print) + bool _3MF_Exporter::_save_model_to_file(const std::string& filename, Model& model, const Print& print, bool export_print_config) { mz_zip_archive archive; mz_zip_zero_struct(&archive); @@ -1502,11 +1502,14 @@ namespace Slic3r { } // adds slic3r print config file - if (!_add_print_config_file_to_archive(archive, print)) + if (export_print_config) { - mz_zip_writer_end(&archive); - boost::filesystem::remove(filename); - return false; + if (!_add_print_config_file_to_archive(archive, print)) + { + mz_zip_writer_end(&archive); + boost::filesystem::remove(filename); + return false; + } } // adds slic3r model config file @@ -1863,13 +1866,13 @@ namespace Slic3r { return res; } - bool store_3mf(const char* path, Model* model, Print* print) + bool store_3mf(const char* path, Model* model, Print* print, bool export_print_config) { if ((path == nullptr) || (model == nullptr) || (print == nullptr)) return false; _3MF_Exporter exporter; - bool res = exporter.save_model_to_file(path, *model, *print); + bool res = exporter.save_model_to_file(path, *model, *print, export_print_config); if (!res) exporter.log_errors(); diff --git a/xs/src/libslic3r/Format/3mf.hpp b/xs/src/libslic3r/Format/3mf.hpp index 9b48c860b..85bc812e3 100644 --- a/xs/src/libslic3r/Format/3mf.hpp +++ b/xs/src/libslic3r/Format/3mf.hpp @@ -12,7 +12,7 @@ namespace Slic3r { // Save the given model and the config data contained in the given Print into a 3mf file. // The model could be modified during the export process if meshes are not repaired or have no shared vertices - extern bool store_3mf(const char* path, Model* model, Print* print); + extern bool store_3mf(const char* path, Model* model, Print* print, bool export_print_config); }; // namespace Slic3r diff --git a/xs/src/libslic3r/Format/AMF.cpp b/xs/src/libslic3r/Format/AMF.cpp index a52dd532a..8c08b4673 100644 --- a/xs/src/libslic3r/Format/AMF.cpp +++ b/xs/src/libslic3r/Format/AMF.cpp @@ -576,8 +576,7 @@ bool load_amf_archive(const char *path, PresetBundle* bundle, Model *model) return false; } - std::string internal_amf_filename = boost::ireplace_last_copy(boost::filesystem::path(path).filename().string(), ".zip.amf", ".amf"); - if (internal_amf_filename != stat.m_filename) + if (!boost::iends_with(stat.m_filename, ".amf")) { printf("Found invalid internal filename\n"); mz_zip_reader_end(&archive); @@ -644,7 +643,7 @@ bool load_amf(const char *path, PresetBundle* bundle, Model *model) return false; } -bool store_amf(const char *path, Model *model, Print* print) +bool store_amf(const char *path, Model *model, Print* print, bool export_print_config) { if ((path == nullptr) || (model == nullptr) || (print == nullptr)) return false; @@ -661,9 +660,12 @@ bool store_amf(const char *path, Model *model, Print* print) stream << "\n"; stream << "Slic3r " << SLIC3R_VERSION << "\n"; - std::string config = "\n"; - GCode::append_full_config(*print, config); - stream << "" << config << "\n"; + if (export_print_config) + { + std::string config = "\n"; + GCode::append_full_config(*print, config); + stream << "" << config << "\n"; + } for (const auto &material : model->materials) { if (material.first.empty()) diff --git a/xs/src/libslic3r/Format/AMF.hpp b/xs/src/libslic3r/Format/AMF.hpp index 027ebdab3..4779e9a51 100644 --- a/xs/src/libslic3r/Format/AMF.hpp +++ b/xs/src/libslic3r/Format/AMF.hpp @@ -12,7 +12,7 @@ extern bool load_amf(const char *path, PresetBundle* bundle, Model *model); // Save the given model and the config data contained in the given Print into an amf file. // The model could be modified during the export process if meshes are not repaired or have no shared vertices -extern bool store_amf(const char *path, Model *model, Print* print); +extern bool store_amf(const char *path, Model *model, Print* print, bool export_print_config); }; // namespace Slic3r diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp index 0410b7969..454852639 100644 --- a/xs/src/slic3r/GUI/GUI.cpp +++ b/xs/src/slic3r/GUI/GUI.cpp @@ -686,4 +686,49 @@ ConfigOptionsGroup* get_optgroup() return m_optgroup.get(); } +wxWindow* export_option_creator(wxWindow* parent) +{ + wxPanel* panel = new wxPanel(parent, -1); + wxSizer* sizer = new wxBoxSizer(wxHORIZONTAL); + wxCheckBox* cbox = new wxCheckBox(panel, wxID_HIGHEST + 1, L("Export print config")); + sizer->AddSpacer(5); + sizer->Add(cbox, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5); + panel->SetSizer(sizer); + sizer->SetSizeHints(panel); + return panel; +} + +void add_export_option(wxFileDialog* dlg, const std::string& format) +{ + if ((dlg != nullptr) && (format == "AMF") || (format == "3MF")) + { + if (dlg->SupportsExtraControl()) + dlg->SetExtraControlCreator(export_option_creator); + } +} + +int get_export_option(wxFileDialog* dlg) +{ + if (dlg != nullptr) + { + wxWindow* wnd = dlg->GetExtraControl(); + if (wnd != nullptr) + { + wxPanel* panel = dynamic_cast(wnd); + if (panel != nullptr) + { + wxWindow* child = panel->FindWindow(wxID_HIGHEST + 1); + if (child != nullptr) + { + wxCheckBox* cbox = dynamic_cast(child); + if (cbox != nullptr) + return cbox->IsChecked() ? 1 : 0; + } + } + } + } + + return 0; +} + } } diff --git a/xs/src/slic3r/GUI/GUI.hpp b/xs/src/slic3r/GUI/GUI.hpp index 084b6de46..7b23702b2 100644 --- a/xs/src/slic3r/GUI/GUI.hpp +++ b/xs/src/slic3r/GUI/GUI.hpp @@ -18,6 +18,7 @@ class wxArrayLong; class wxColour; class wxBoxSizer; class wxFlexGridSizer; +class wxFileDialog; namespace Slic3r { @@ -130,6 +131,8 @@ void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFl ConfigOptionsGroup* get_optgroup(); +void add_export_option(wxFileDialog* dlg, const std::string& format); +int get_export_option(wxFileDialog* dlg); } } diff --git a/xs/xsp/GUI.xsp b/xs/xsp/GUI.xsp index d306f12ce..1376ff164 100644 --- a/xs/xsp/GUI.xsp +++ b/xs/xsp/GUI.xsp @@ -67,3 +67,10 @@ void add_frequently_changed_parameters(SV *ui_parent, SV *ui_sizer, SV *ui_p_siz std::string fold_utf8_to_ascii(const char *src) %code%{ RETVAL = Slic3r::fold_utf8_to_ascii(src); %}; + +void add_export_option(SV *ui, std::string format) + %code%{ Slic3r::GUI::add_export_option((wxFileDialog*)wxPli_sv_2_object(aTHX_ ui, "Wx::FileDialog"), format); %}; + +int get_export_option(SV *ui) + %code%{ RETVAL = Slic3r::GUI::get_export_option((wxFileDialog*)wxPli_sv_2_object(aTHX_ ui, "Wx::FileDialog")); %}; + \ No newline at end of file diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp index 78c94661e..702839537 100644 --- a/xs/xsp/Model.xsp +++ b/xs/xsp/Model.xsp @@ -104,10 +104,10 @@ bool store_stl(char *path, bool binary) %code%{ TriangleMesh mesh = THIS->mesh(); RETVAL = Slic3r::store_stl(path, &mesh, binary); %}; - bool store_amf(char *path, Print* print) - %code%{ RETVAL = Slic3r::store_amf(path, THIS, print); %}; - bool store_3mf(char *path, Print* print) - %code%{ RETVAL = Slic3r::store_3mf(path, THIS, print); %}; + bool store_amf(char *path, Print* print, bool export_print_config) + %code%{ RETVAL = Slic3r::store_amf(path, THIS, print, export_print_config); %}; + bool store_3mf(char *path, Print* print, bool export_print_config) + %code%{ RETVAL = Slic3r::store_3mf(path, THIS, print, export_print_config); %}; %{ From c6623bb2583a05dece819bd533d378533c51e16b Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 23 Mar 2018 15:40:26 +0100 Subject: [PATCH 10/22] Increased camera theta max to 180 degrees. Fixes #666 --- lib/Slic3r/GUI/3DScene.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 75a154281..c00484fde 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -96,7 +96,7 @@ use constant MANIPULATION_IDLE => 0; use constant MANIPULATION_DRAGGING => 1; use constant MANIPULATION_LAYER_HEIGHT => 2; -use constant GIMBALL_LOCK_THETA_MAX => 170; +use constant GIMBALL_LOCK_THETA_MAX => 180; use constant VARIABLE_LAYER_THICKNESS_BAR_WIDTH => 70; use constant VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT => 22; From 903a90f37a0aea3a7e8f93835f403e236e7e38da Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 26 Mar 2018 08:58:44 +0200 Subject: [PATCH 11/22] AMF I/O - Forces .zip.amf extension on export --- xs/src/libslic3r/Format/AMF.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/xs/src/libslic3r/Format/AMF.cpp b/xs/src/libslic3r/Format/AMF.cpp index 8c08b4673..98683cd8a 100644 --- a/xs/src/libslic3r/Format/AMF.cpp +++ b/xs/src/libslic3r/Format/AMF.cpp @@ -648,10 +648,15 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c if ((path == nullptr) || (model == nullptr) || (print == nullptr)) return false; + // forces ".zip.amf" extension + std::string export_path = path; + if (!boost::iends_with(export_path, ".zip.amf")) + export_path = boost::filesystem::path(export_path).replace_extension(".zip.amf").string(); + mz_zip_archive archive; mz_zip_zero_struct(&archive); - mz_bool res = mz_zip_writer_init_file(&archive, path, 0); + mz_bool res = mz_zip_writer_init_file(&archive, export_path.c_str(), 0); if (res == 0) return false; @@ -769,20 +774,20 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c } stream << "\n"; - std::string internal_amf_filename = boost::ireplace_last_copy(boost::filesystem::path(path).filename().string(), ".zip.amf", ".amf"); + std::string internal_amf_filename = boost::ireplace_last_copy(boost::filesystem::path(export_path).filename().string(), ".zip.amf", ".amf"); std::string out = stream.str(); if (!mz_zip_writer_add_mem(&archive, internal_amf_filename.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) { mz_zip_writer_end(&archive); - boost::filesystem::remove(path); + boost::filesystem::remove(export_path); return false; } if (!mz_zip_writer_finalize_archive(&archive)) { mz_zip_writer_end(&archive); - boost::filesystem::remove(path); + boost::filesystem::remove(export_path); return false; } From c166af5ccec2c46ff7c030aa7bbbf7173d5e7e0f Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 27 Mar 2018 11:35:48 +0200 Subject: [PATCH 12/22] GCode Preview - Fixed z values set on sliders --- xs/src/slic3r/GUI/3DScene.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index eafca6cd1..95a739353 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -643,14 +643,18 @@ void GLVolumeCollection::update_outside_state(const DynamicPrintConfig* config, std::vector GLVolumeCollection::get_current_print_zs() const { std::vector print_zs; + std::vector rounded_print_zs; for (GLVolume *vol : this->volumes) { for (coordf_t z : vol->print_zs) { - double round_z = (double)round(z * 100000.0f) / 100000.0f; - if (std::find(print_zs.begin(), print_zs.end(), round_z) == print_zs.end()) - print_zs.push_back(round_z); + double round_z = ::round(z * 100000.0 + 0.5) / 100000.0; + if (std::find(rounded_print_zs.begin(), rounded_print_zs.end(), round_z) == rounded_print_zs.end()) + { + print_zs.push_back(z); + rounded_print_zs.push_back(round_z); + } } } From bf295b903968d75845bc9898fa880e2b91e4121d Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 29 Mar 2018 14:46:11 +0200 Subject: [PATCH 13/22] Time Estimator and GCode Analyzer - Fixed handling of commands G90, G91, M82 and M83 - Fixes #812 --- xs/src/libslic3r/GCode/Analyzer.cpp | 38 ++++++++++++----------- xs/src/libslic3r/GCode/Analyzer.hpp | 12 ++++---- xs/src/libslic3r/GCodeTimeEstimator.cpp | 40 +++++++++++++------------ xs/src/libslic3r/GCodeTimeEstimator.hpp | 12 ++++---- 4 files changed, 54 insertions(+), 48 deletions(-) diff --git a/xs/src/libslic3r/GCode/Analyzer.cpp b/xs/src/libslic3r/GCode/Analyzer.cpp index 6530806c4..209874180 100644 --- a/xs/src/libslic3r/GCode/Analyzer.cpp +++ b/xs/src/libslic3r/GCode/Analyzer.cpp @@ -97,8 +97,8 @@ GCodeAnalyzer::GCodeAnalyzer() void GCodeAnalyzer::reset() { _set_units(Millimeters); - _set_positioning_xyz_type(Absolute); - _set_positioning_e_type(Relative); + _set_global_positioning_type(Absolute); + _set_e_local_positioning_type(Absolute); _set_extrusion_role(erNone); _set_extruder_id(DEFAULT_EXTRUDER_ID); _set_mm3_per_mm(Default_mm3_per_mm); @@ -237,13 +237,13 @@ void GCodeAnalyzer::_process_gcode_line(GCodeReader&, const GCodeReader::GCodeLi } // Returns the new absolute position on the given axis in dependence of the given parameters -float axis_absolute_position_from_G1_line(GCodeAnalyzer::EAxis axis, const GCodeReader::GCodeLine& lineG1, GCodeAnalyzer::EUnits units, GCodeAnalyzer::EPositioningType type, float current_absolute_position) +float axis_absolute_position_from_G1_line(GCodeAnalyzer::EAxis axis, const GCodeReader::GCodeLine& lineG1, GCodeAnalyzer::EUnits units, bool is_relative, float current_absolute_position) { float lengthsScaleFactor = (units == GCodeAnalyzer::Inches) ? INCHES_TO_MM : 1.0f; if (lineG1.has(Slic3r::Axis(axis))) { float ret = lineG1.value(Slic3r::Axis(axis)) * lengthsScaleFactor; - return (type == GCodeAnalyzer::Absolute) ? ret : current_absolute_position + ret; + return is_relative ? current_absolute_position + ret : ret; } else return current_absolute_position; @@ -256,7 +256,11 @@ void GCodeAnalyzer::_processG1(const GCodeReader::GCodeLine& line) float new_pos[Num_Axis]; for (unsigned char a = X; a < Num_Axis; ++a) { - new_pos[a] = axis_absolute_position_from_G1_line((EAxis)a, line, units, (a == E) ? _get_positioning_e_type() : _get_positioning_xyz_type(), _get_axis_position((EAxis)a)); + bool is_relative = (_get_global_positioning_type() == Relative); + if (a == E) + is_relative |= (_get_e_local_positioning_type() == Relative); + + new_pos[a] = axis_absolute_position_from_G1_line((EAxis)a, line, units, is_relative, _get_axis_position((EAxis)a)); } // updates feedrate from line, if present @@ -319,12 +323,12 @@ void GCodeAnalyzer::_processG23(const GCodeReader::GCodeLine& line) void GCodeAnalyzer::_processG90(const GCodeReader::GCodeLine& line) { - _set_positioning_xyz_type(Absolute); + _set_global_positioning_type(Absolute); } void GCodeAnalyzer::_processG91(const GCodeReader::GCodeLine& line) { - _set_positioning_xyz_type(Relative); + _set_global_positioning_type(Relative); } void GCodeAnalyzer::_processG92(const GCodeReader::GCodeLine& line) @@ -367,12 +371,12 @@ void GCodeAnalyzer::_processG92(const GCodeReader::GCodeLine& line) void GCodeAnalyzer::_processM82(const GCodeReader::GCodeLine& line) { - _set_positioning_e_type(Absolute); + _set_e_local_positioning_type(Absolute); } void GCodeAnalyzer::_processM83(const GCodeReader::GCodeLine& line) { - _set_positioning_e_type(Relative); + _set_e_local_positioning_type(Relative); } void GCodeAnalyzer::_processT(const GCodeReader::GCodeLine& line) @@ -466,24 +470,24 @@ GCodeAnalyzer::EUnits GCodeAnalyzer::_get_units() const return m_state.units; } -void GCodeAnalyzer::_set_positioning_xyz_type(GCodeAnalyzer::EPositioningType type) +void GCodeAnalyzer::_set_global_positioning_type(GCodeAnalyzer::EPositioningType type) { - m_state.positioning_xyz_type = type; + m_state.global_positioning_type = type; } -GCodeAnalyzer::EPositioningType GCodeAnalyzer::_get_positioning_xyz_type() const +GCodeAnalyzer::EPositioningType GCodeAnalyzer::_get_global_positioning_type() const { - return m_state.positioning_xyz_type; + return m_state.global_positioning_type; } -void GCodeAnalyzer::_set_positioning_e_type(GCodeAnalyzer::EPositioningType type) +void GCodeAnalyzer::_set_e_local_positioning_type(GCodeAnalyzer::EPositioningType type) { - m_state.positioning_e_type = type; + m_state.e_local_positioning_type = type; } -GCodeAnalyzer::EPositioningType GCodeAnalyzer::_get_positioning_e_type() const +GCodeAnalyzer::EPositioningType GCodeAnalyzer::_get_e_local_positioning_type() const { - return m_state.positioning_e_type; + return m_state.e_local_positioning_type; } void GCodeAnalyzer::_set_extrusion_role(ExtrusionRole extrusion_role) diff --git a/xs/src/libslic3r/GCode/Analyzer.hpp b/xs/src/libslic3r/GCode/Analyzer.hpp index 7939d432d..39e67b89a 100644 --- a/xs/src/libslic3r/GCode/Analyzer.hpp +++ b/xs/src/libslic3r/GCode/Analyzer.hpp @@ -90,8 +90,8 @@ private: struct State { EUnits units; - EPositioningType positioning_xyz_type; - EPositioningType positioning_e_type; + EPositioningType global_positioning_type; + EPositioningType e_local_positioning_type; Metadata data; Pointf3 start_position; float start_extrusion; @@ -170,11 +170,11 @@ private: void _set_units(EUnits units); EUnits _get_units() const; - void _set_positioning_xyz_type(EPositioningType type); - EPositioningType _get_positioning_xyz_type() const; + void _set_global_positioning_type(EPositioningType type); + EPositioningType _get_global_positioning_type() const; - void _set_positioning_e_type(EPositioningType type); - EPositioningType _get_positioning_e_type() const; + void _set_e_local_positioning_type(EPositioningType type); + EPositioningType _get_e_local_positioning_type() const; void _set_extrusion_role(ExtrusionRole extrusion_role); ExtrusionRole _get_extrusion_role() const; diff --git a/xs/src/libslic3r/GCodeTimeEstimator.cpp b/xs/src/libslic3r/GCodeTimeEstimator.cpp index 912799ca9..176159ff5 100644 --- a/xs/src/libslic3r/GCodeTimeEstimator.cpp +++ b/xs/src/libslic3r/GCodeTimeEstimator.cpp @@ -369,24 +369,24 @@ namespace Slic3r { return _state.units; } - void GCodeTimeEstimator::set_positioning_xyz_type(GCodeTimeEstimator::EPositioningType type) + void GCodeTimeEstimator::set_global_positioning_type(GCodeTimeEstimator::EPositioningType type) { - _state.positioning_xyz_type = type; + _state.global_positioning_type = type; } - GCodeTimeEstimator::EPositioningType GCodeTimeEstimator::get_positioning_xyz_type() const + GCodeTimeEstimator::EPositioningType GCodeTimeEstimator::get_global_positioning_type() const { - return _state.positioning_xyz_type; + return _state.global_positioning_type; } - void GCodeTimeEstimator::set_positioning_e_type(GCodeTimeEstimator::EPositioningType type) + void GCodeTimeEstimator::set_e_local_positioning_type(GCodeTimeEstimator::EPositioningType type) { - _state.positioning_e_type = type; + _state.e_local_positioning_type = type; } - GCodeTimeEstimator::EPositioningType GCodeTimeEstimator::get_positioning_e_type() const + GCodeTimeEstimator::EPositioningType GCodeTimeEstimator::get_e_local_positioning_type() const { - return _state.positioning_e_type; + return _state.e_local_positioning_type; } void GCodeTimeEstimator::add_additional_time(float timeSec) @@ -408,8 +408,8 @@ namespace Slic3r { { set_units(Millimeters); set_dialect(gcfRepRap); - set_positioning_xyz_type(Absolute); - set_positioning_e_type(Relative); + set_global_positioning_type(Absolute); + set_e_local_positioning_type(Absolute); set_feedrate(DEFAULT_FEEDRATE); set_acceleration(DEFAULT_ACCELERATION); @@ -628,13 +628,13 @@ namespace Slic3r { } // Returns the new absolute position on the given axis in dependence of the given parameters - float axis_absolute_position_from_G1_line(GCodeTimeEstimator::EAxis axis, const GCodeReader::GCodeLine& lineG1, GCodeTimeEstimator::EUnits units, GCodeTimeEstimator::EPositioningType type, float current_absolute_position) + float axis_absolute_position_from_G1_line(GCodeTimeEstimator::EAxis axis, const GCodeReader::GCodeLine& lineG1, GCodeTimeEstimator::EUnits units, bool is_relative, float current_absolute_position) { float lengthsScaleFactor = (units == GCodeTimeEstimator::Inches) ? INCHES_TO_MM : 1.0f; if (lineG1.has(Slic3r::Axis(axis))) { float ret = lineG1.value(Slic3r::Axis(axis)) * lengthsScaleFactor; - return (type == GCodeTimeEstimator::Absolute) ? ret : current_absolute_position + ret; + return is_relative ? current_absolute_position + ret : ret; } else return current_absolute_position; @@ -647,7 +647,11 @@ namespace Slic3r { float new_pos[Num_Axis]; for (unsigned char a = X; a < Num_Axis; ++a) { - new_pos[a] = axis_absolute_position_from_G1_line((EAxis)a, line, units, (a == E) ? get_positioning_e_type() : get_positioning_xyz_type(), get_axis_position((EAxis)a)); + bool is_relative = (get_global_positioning_type() == Relative); + if (a == E) + is_relative |= (get_e_local_positioning_type() == Relative); + + new_pos[a] = axis_absolute_position_from_G1_line((EAxis)a, line, units, is_relative, get_axis_position((EAxis)a)); } // updates feedrate from line, if present @@ -865,14 +869,12 @@ namespace Slic3r { void GCodeTimeEstimator::_processG90(const GCodeReader::GCodeLine& line) { - set_positioning_xyz_type(Absolute); + set_global_positioning_type(Absolute); } void GCodeTimeEstimator::_processG91(const GCodeReader::GCodeLine& line) { - // TODO: THERE ARE DIALECT VARIANTS - - set_positioning_xyz_type(Relative); + set_global_positioning_type(Relative); } void GCodeTimeEstimator::_processG92(const GCodeReader::GCodeLine& line) @@ -922,12 +924,12 @@ namespace Slic3r { void GCodeTimeEstimator::_processM82(const GCodeReader::GCodeLine& line) { - set_positioning_e_type(Absolute); + set_e_local_positioning_type(Absolute); } void GCodeTimeEstimator::_processM83(const GCodeReader::GCodeLine& line) { - set_positioning_e_type(Relative); + set_e_local_positioning_type(Relative); } void GCodeTimeEstimator::_processM109(const GCodeReader::GCodeLine& line) diff --git a/xs/src/libslic3r/GCodeTimeEstimator.hpp b/xs/src/libslic3r/GCodeTimeEstimator.hpp index 5ad5b8d0c..8f948abd1 100644 --- a/xs/src/libslic3r/GCodeTimeEstimator.hpp +++ b/xs/src/libslic3r/GCodeTimeEstimator.hpp @@ -61,8 +61,8 @@ namespace Slic3r { { GCodeFlavor dialect; EUnits units; - EPositioningType positioning_xyz_type; - EPositioningType positioning_e_type; + EPositioningType global_positioning_type; + EPositioningType e_local_positioning_type; Axis axis[Num_Axis]; float feedrate; // mm/s float acceleration; // mm/s^2 @@ -257,11 +257,11 @@ namespace Slic3r { void set_units(EUnits units); EUnits get_units() const; - void set_positioning_xyz_type(EPositioningType type); - EPositioningType get_positioning_xyz_type() const; + void set_global_positioning_type(EPositioningType type); + EPositioningType get_global_positioning_type() const; - void set_positioning_e_type(EPositioningType type); - EPositioningType get_positioning_e_type() const; + void set_e_local_positioning_type(EPositioningType type); + EPositioningType get_e_local_positioning_type() const; void add_additional_time(float timeSec); void set_additional_time(float timeSec); From 2f6fbfb3381edcca6f81794c1fd2c76439c89a44 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 4 Apr 2018 11:00:25 +0200 Subject: [PATCH 14/22] Fix of SPE-183: Incorrect z values set into GCode Preview sliders. Rework to replace layer top heights with averages of EPSILON intervals. --- xs/src/slic3r/GUI/3DScene.cpp | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index 95a739353..0db81d99b 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -642,24 +642,25 @@ void GLVolumeCollection::update_outside_state(const DynamicPrintConfig* config, std::vector GLVolumeCollection::get_current_print_zs() const { + // Collect layer top positions of all volumes. std::vector print_zs; - std::vector rounded_print_zs; - for (GLVolume *vol : this->volumes) - { - for (coordf_t z : vol->print_zs) - { - double round_z = ::round(z * 100000.0 + 0.5) / 100000.0; - if (std::find(rounded_print_zs.begin(), rounded_print_zs.end(), round_z) == rounded_print_zs.end()) - { - print_zs.push_back(z); - rounded_print_zs.push_back(round_z); - } - } - } - + append(print_zs, vol->print_zs); std::sort(print_zs.begin(), print_zs.end()); + // Replace intervals of layers with similar top positions with their average value. + int n = int(print_zs.size()); + int k = 0; + for (int i = 0; i < n;) { + int j = i + 1; + coordf_t zmax = print_zs[i] + EPSILON; + for (; j < n && print_zs[j] <= zmax; ++ j) ; + print_zs[k ++] = (j > i + 1) ? (0.5 * (print_zs[i] + print_zs[j - 1])) : print_zs[i]; + i = j; + } + if (k < n) + print_zs.erase(print_zs.begin() + k, print_zs.end()); + return print_zs; } From b0840065ed84f4006207b38de3c9c6a5e15a7071 Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Wed, 4 Apr 2018 11:18:22 +0200 Subject: [PATCH 15/22] Octoprint (#804) * Octoprint progress dialog * Fix curl version on Windows --- doc/How to build - Windows.md | 7 +- doc/deps-build/windows/slic3r-makedeps.ps1 | 2 +- lib/Slic3r/GUI/Plater.pm | 6 +- xs/src/slic3r/Utils/Http.cpp | 75 +++++++++++++++++++--- xs/src/slic3r/Utils/Http.hpp | 17 +++++ xs/src/slic3r/Utils/OctoPrint.cpp | 66 ++++++++++++------- xs/src/slic3r/Utils/OctoPrint.hpp | 2 +- xs/xsp/Utils_OctoPrint.xsp | 2 +- 8 files changed, 136 insertions(+), 41 deletions(-) diff --git a/doc/How to build - Windows.md b/doc/How to build - Windows.md index 104720b34..8b7d37cdd 100644 --- a/doc/How to build - Windows.md +++ b/doc/How to build - Windows.md @@ -1,8 +1,9 @@ # Building Slic3r PE on Microsoft Windows -The currently supported way of building Slic3r PE on Windows is with MS Visual Studio 2013 +The currently supported way of building Slic3r PE on Windows is with CMake and MS Visual Studio 2013 using our Perl binary distribution (compiled from official Perl sources). You can use the free [Visual Studio 2013 Community Edition](https://www.visualstudio.com/vs/older-downloads/). +CMake installer can be downloaded from [the official website](https://cmake.org/download/). Other setups (such as mingw + Strawberry Perl) _may_ work, but we cannot guarantee this will work and cannot provide guidance. @@ -26,8 +27,8 @@ Apart from wxWidgets and Perl, you will also need additional dependencies: We have prepared a binary package of the listed libraries: - - 32 bit: [slic3r-destdir-32.7z](https://bintray.com/vojtechkral/Slic3r-PE/download_file?file_path=slic3r-destdir-32.7z) - - 64 bit: [slic3r-destdir-64.7z](https://bintray.com/vojtechkral/Slic3r-PE/download_file?file_path=slic3r-destdir-64.7z) + - 32 bit: [slic3r-destdir-32.7z](https://bintray.com/vojtechkral/Slic3r-PE/download_file?file_path=2%2Fslic3r-destdir-32.7z) + - 64 bit: [slic3r-destdir-64.7z](https://bintray.com/vojtechkral/Slic3r-PE/download_file?file_path=2%2Fslic3r-destdir-64.7z) It is recommended you unpack this package into `C:\local\` as the environment setup script expects it there. diff --git a/doc/deps-build/windows/slic3r-makedeps.ps1 b/doc/deps-build/windows/slic3r-makedeps.ps1 index 8b39cae30..e256d61e4 100644 --- a/doc/deps-build/windows/slic3r-makedeps.ps1 +++ b/doc/deps-build/windows/slic3r-makedeps.ps1 @@ -37,7 +37,7 @@ if ($destdir -eq "") { } $BOOST = 'boost_1_63_0' -$CURL = 'curl-7.28.0' +$CURL = 'curl-7.58.0' $TBB_SHA = 'a0dc9bf76d0120f917b641ed095360448cabc85b' $TBB = "tbb-$TBB_SHA" diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 0138d3f39..14c2d66ae 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -1481,7 +1481,11 @@ sub on_export_completed { # Send $self->{send_gcode_file} to OctoPrint. if ($send_gcode) { my $op = Slic3r::OctoPrint->new($self->{config}); - $op->send_gcode($self->GetId(), $PROGRESS_BAR_EVENT, $ERROR_EVENT, $self->{send_gcode_file}); + if ($op->send_gcode($self->{send_gcode_file})) { + $self->statusbar->SetStatusText(L("OctoPrint upload finished.")); + } else { + $self->statusbar->SetStatusText(""); + } } $self->{print_file} = undef; diff --git a/xs/src/slic3r/Utils/Http.cpp b/xs/src/slic3r/Utils/Http.cpp index de28904e2..0826284d8 100644 --- a/xs/src/slic3r/Utils/Http.cpp +++ b/xs/src/slic3r/Utils/Http.cpp @@ -36,16 +36,20 @@ struct Http::priv ::curl_slist *headerlist; std::string buffer; size_t limit; + bool cancel; std::thread io_thread; Http::CompleteFn completefn; Http::ErrorFn errorfn; + Http::ProgressFn progressfn; priv(const std::string &url); ~priv(); static bool ca_file_supported(::CURL *curl); static size_t writecb(void *data, size_t size, size_t nmemb, void *userp); + static int xfercb(void *userp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow); + static int xfercb_legacy(void *userp, double dltotal, double dlnow, double ultotal, double ulnow); std::string curl_error(CURLcode curlcode); std::string body_size_error(); void http_perform(); @@ -55,7 +59,8 @@ Http::priv::priv(const std::string &url) : curl(::curl_easy_init()), form(nullptr), form_end(nullptr), - headerlist(nullptr) + headerlist(nullptr), + cancel(false) { if (curl == nullptr) { throw std::runtime_error(std::string("Could not construct Curl object")); @@ -112,6 +117,24 @@ size_t Http::priv::writecb(void *data, size_t size, size_t nmemb, void *userp) return realsize; } +int Http::priv::xfercb(void *userp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) +{ + auto self = static_cast(userp); + bool cb_cancel = false; + + if (self->progressfn) { + Progress progress(dltotal, dlnow, ultotal, ulnow); + self->progressfn(progress, cb_cancel); + } + + return self->cancel || cb_cancel; +} + +int Http::priv::xfercb_legacy(void *userp, double dltotal, double dlnow, double ultotal, double ulnow) +{ + return xfercb(userp, dltotal, dlnow, ultotal, ulnow); +} + std::string Http::priv::curl_error(CURLcode curlcode) { return (boost::format("%1% (%2%)") @@ -132,6 +155,16 @@ void Http::priv::http_perform() ::curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writecb); ::curl_easy_setopt(curl, CURLOPT_WRITEDATA, static_cast(this)); + ::curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); +#if LIBCURL_VERSION_MAJOR >= 7 && LIBCURL_VERSION_MINOR >= 32 + ::curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, xfercb); + ::curl_easy_setopt(curl, CURLOPT_XFERINFODATA, static_cast(this)); + (void)xfercb_legacy; // prevent unused function warning +#else + ::curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, xfercb); + ::curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, static_cast(this)); +#endif + #ifndef NDEBUG ::curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); #endif @@ -149,16 +182,16 @@ void Http::priv::http_perform() ::curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_status); if (res != CURLE_OK) { - std::string error; - if (res == CURLE_WRITE_ERROR) { - error = std::move(body_size_error()); - } else { - error = std::move(curl_error(res)); - }; - - if (errorfn) { - errorfn(std::move(buffer), std::move(error), http_status); + if (res == CURLE_ABORTED_BY_CALLBACK) { + Progress dummyprogress(0, 0, 0, 0); + bool cancel = true; + if (progressfn) { progressfn(dummyprogress, cancel); } } + else if (res == CURLE_WRITE_ERROR) { + if (errorfn) { errorfn(std::move(buffer), std::move(body_size_error()), http_status); } + } else { + if (errorfn) { errorfn(std::move(buffer), std::move(curl_error(res)), http_status); } + }; } else { if (completefn) { completefn(std::move(buffer), http_status); @@ -258,6 +291,12 @@ Http& Http::on_error(ErrorFn fn) return *this; } +Http& Http::on_progress(ProgressFn fn) +{ + if (p) { p->progressfn = std::move(fn); } + return *this; +} + Http::Ptr Http::perform() { auto self = std::make_shared(std::move(*this)); @@ -277,6 +316,11 @@ void Http::perform_sync() if (p) { p->http_perform(); } } +void Http::cancel() +{ + if (p) { p->cancel = true; } +} + Http Http::get(std::string url) { return std::move(Http{std::move(url)}); @@ -297,5 +341,16 @@ bool Http::ca_file_supported() return res; } +std::ostream& operator<<(std::ostream &os, const Http::Progress &progress) +{ + os << "Http::Progress(" + << "dltotal = " << progress.dltotal + << ", dlnow = " << progress.dlnow + << ", ultotal = " << progress.ultotal + << ", ulnow = " << progress.ulnow + << ")"; + return os; +} + } diff --git a/xs/src/slic3r/Utils/Http.hpp b/xs/src/slic3r/Utils/Http.hpp index 6ac5fcce1..7ed8196e6 100644 --- a/xs/src/slic3r/Utils/Http.hpp +++ b/xs/src/slic3r/Utils/Http.hpp @@ -14,9 +14,22 @@ class Http : public std::enable_shared_from_this { private: struct priv; public: + struct Progress + { + size_t dltotal; + size_t dlnow; + size_t ultotal; + size_t ulnow; + + Progress(size_t dltotal, size_t dlnow, size_t ultotal, size_t ulnow) : + dltotal(dltotal), dlnow(dlnow), ultotal(ultotal), ulnow(ulnow) + {} + }; + typedef std::shared_ptr Ptr; typedef std::function CompleteFn; typedef std::function ErrorFn; + typedef std::function ProgressFn; Http(Http &&other); @@ -37,9 +50,11 @@ public: Http& on_complete(CompleteFn fn); Http& on_error(ErrorFn fn); + Http& on_progress(ProgressFn fn); Ptr perform(); void perform_sync(); + void cancel(); static bool ca_file_supported(); private: @@ -48,6 +63,8 @@ private: std::unique_ptr p; }; +std::ostream& operator<<(std::ostream &, const Http::Progress &); + } diff --git a/xs/src/slic3r/Utils/OctoPrint.cpp b/xs/src/slic3r/Utils/OctoPrint.cpp index 5bf51f470..e63a16c38 100644 --- a/xs/src/slic3r/Utils/OctoPrint.cpp +++ b/xs/src/slic3r/Utils/OctoPrint.cpp @@ -1,10 +1,11 @@ #include "OctoPrint.hpp" -#include +#include #include #include #include +#include #include "libslic3r/PrintConfig.hpp" #include "slic3r/GUI/GUI.hpp" @@ -39,36 +40,53 @@ bool OctoPrint::test(wxString &msg) const return res; } -void OctoPrint::send_gcode(int windowId, int completeEvt, int errorEvt, const std::string &filename, bool print) const +bool OctoPrint::send_gcode(const std::string &filename, bool print) const { + enum { PROGRESS_RANGE = 1000 }; + + const auto errortitle = _(L("Error while uploading to the OctoPrint server")); + + wxProgressDialog progress_dialog( + _(L("OctoPrint upload")), + _(L("Sending G-code file to the OctoPrint server...")), + PROGRESS_RANGE, nullptr, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT); + progress_dialog.Pulse(); + + wxString test_msg; + if (!test(test_msg)) { + auto errormsg = wxString::Format("%s: %s", errortitle, test_msg); + GUI::show_error(&progress_dialog, std::move(errormsg)); + return false; + } + + bool res = true; + auto http = Http::post(std::move(make_url("api/files/local"))); set_auth(http); http.form_add("print", print ? "true" : "false") .form_add_file("file", filename) - .on_complete([=](std::string body, unsigned status) { - wxWindow *window = wxWindow::FindWindowById(windowId); - if (window == nullptr) { return; } - - wxCommandEvent* evt = new wxCommandEvent(completeEvt); - evt->SetString(_(L("G-code file successfully uploaded to the OctoPrint server"))); - evt->SetInt(100); - wxQueueEvent(window, evt); + .on_complete([&](std::string body, unsigned status) { + progress_dialog.Update(PROGRESS_RANGE); }) - .on_error([=](std::string body, std::string error, unsigned status) { - wxWindow *window = wxWindow::FindWindowById(windowId); - if (window == nullptr) { return; } - - wxCommandEvent* evt_complete = new wxCommandEvent(completeEvt); - evt_complete->SetInt(100); - wxQueueEvent(window, evt_complete); - - wxCommandEvent* evt_error = new wxCommandEvent(errorEvt); - evt_error->SetString(wxString::Format("%s: %s", - _(L("Error while uploading to the OctoPrint server")), - format_error(error, status))); - wxQueueEvent(window, evt_error); + .on_error([&](std::string body, std::string error, unsigned status) { + auto errormsg = wxString::Format("%s: %s", errortitle, format_error(error, status)); + GUI::show_error(&progress_dialog, std::move(errormsg)); + res = false; }) - .perform(); + .on_progress([&](Http::Progress progress, bool &cancel) { + if (cancel) { + // Upload was canceled + res = false; + } else if (progress.ultotal > 0) { + int value = PROGRESS_RANGE * progress.ulnow / progress.ultotal; + cancel = !progress_dialog.Update(std::min(value, PROGRESS_RANGE - 1)); // Cap the value to prevent premature dialog closing + } else { + cancel = !progress_dialog.Pulse(); + } + }) + .perform_sync(); + + return res; } void OctoPrint::set_auth(Http &http) const diff --git a/xs/src/slic3r/Utils/OctoPrint.hpp b/xs/src/slic3r/Utils/OctoPrint.hpp index 1f544295c..744b4fcc1 100644 --- a/xs/src/slic3r/Utils/OctoPrint.hpp +++ b/xs/src/slic3r/Utils/OctoPrint.hpp @@ -17,7 +17,7 @@ public: OctoPrint(DynamicPrintConfig *config); bool test(wxString &curl_msg) const; - void send_gcode(int windowId, int completeEvt, int errorEvt, const std::string &filename, bool print = false) const; + bool send_gcode(const std::string &filename, bool print = false) const; private: std::string host; std::string apikey; diff --git a/xs/xsp/Utils_OctoPrint.xsp b/xs/xsp/Utils_OctoPrint.xsp index 124f66cb5..282a3055d 100644 --- a/xs/xsp/Utils_OctoPrint.xsp +++ b/xs/xsp/Utils_OctoPrint.xsp @@ -9,5 +9,5 @@ OctoPrint(DynamicPrintConfig *config); ~OctoPrint(); - void send_gcode(int windowId, int completeEvt, int errorEvt, std::string filename, bool print = false) const; + bool send_gcode(std::string filename, bool print = false) const; }; From 4611b5094e87c93faf95d36a2690ec232dc5e962 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 5 Apr 2018 10:31:53 +0200 Subject: [PATCH 16/22] Fixed regression of the gyroid infill. --- xs/src/libslic3r/Fill/FillGyroid.cpp | 21 ++++++++++++--------- xs/src/libslic3r/Fill/FillGyroid.hpp | 4 ---- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/xs/src/libslic3r/Fill/FillGyroid.cpp b/xs/src/libslic3r/Fill/FillGyroid.cpp index e63ce0bfd..a3920caab 100644 --- a/xs/src/libslic3r/Fill/FillGyroid.cpp +++ b/xs/src/libslic3r/Fill/FillGyroid.cpp @@ -41,7 +41,7 @@ static inline Polyline make_wave_horizontal( polyline.points.emplace_back(Point(0, coord_t(clamp(0., height, y0) * scaleFactor))); double phase_offset_sin = (z_sin < 0 ? M_PI : 0) + (flip ? 0 : M_PI); double phase_offset_cos = z_sin < 0 ? M_PI : 0.; - for (double x=0.; x < width + segmentSize; x += segmentSize) { + for (double x = 0.; x < width + segmentSize; x += segmentSize) { x = std::min(x, width); double a = cos(x + phase_offset_cos); double b = - z_sin; @@ -55,10 +55,10 @@ static inline Polyline make_wave_horizontal( return polyline; } -static Polylines make_gyroid_waves(double gridZ, double density, double layer_width, double width, double height) +static Polylines make_gyroid_waves(double gridZ, double density_adjusted, double line_spacing, double width, double height) { - double scaleFactor = scale_(layer_width) / density; - double segmentSize = 0.5 * density; + double scaleFactor = scale_(line_spacing) / density_adjusted; + double segmentSize = 0.5 * density_adjusted; //scale factor for 5% : 8 712 388 // 1z = 10^-6 mm ? double z = gridZ / scaleFactor; @@ -74,7 +74,7 @@ static Polylines make_gyroid_waves(double gridZ, double density, double layer_wi } else { // Horizontal wave bool flip = true; - for (double y0 = 0.; y0 < width; y0 += M_PI, flip = !flip) + for (double y0 = 0.; y0 < height; y0 += M_PI, flip = !flip) result.emplace_back(make_wave_horizontal(width, height, y0, segmentSize, scaleFactor, z_cos, z_sin, flip)); } return result; @@ -87,17 +87,20 @@ void FillGyroid::_fill_surface_single( ExPolygon &expolygon, Polylines &polylines_out) { - // no rotation is supported for this infill pattern + // no rotation is supported for this infill pattern (yet) BoundingBox bb = expolygon.contour.bounding_box(); - coord_t distance = coord_t(scale_(this->spacing) / (params.density*this->scaling)); + // Density adjusted to have a good %of weight. + double density_adjusted = params.density * 1.75; + // Distance between the gyroid waves in scaled coordinates. + coord_t distance = coord_t(scale_(this->spacing) / density_adjusted); // align bounding box to a multiple of our grid module - bb.merge(_align_to_grid(bb.min, Point(2*M_PI*distance, 2*M_PI*distance))); + bb.merge(_align_to_grid(bb.min, Point(2.*M_PI*distance, 2.*M_PI*distance))); // generate pattern Polylines polylines = make_gyroid_waves( scale_(this->z), - params.density*this->scaling, + density_adjusted, this->spacing, ceil(bb.size().x / distance) + 1., ceil(bb.size().y / distance) + 1.); diff --git a/xs/src/libslic3r/Fill/FillGyroid.hpp b/xs/src/libslic3r/Fill/FillGyroid.hpp index 758652a5c..17924b5ab 100644 --- a/xs/src/libslic3r/Fill/FillGyroid.hpp +++ b/xs/src/libslic3r/Fill/FillGyroid.hpp @@ -17,10 +17,6 @@ public: virtual bool use_bridge_flow() const { return true; } protected: - - // mult of density, to have a good %of weight for each density parameter - float scaling = 1.75; - virtual void _fill_surface_single( const FillParams ¶ms, unsigned int thickness_layers, From 2b8da333efa035966afb54a120b0e9d795c219dc Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Thu, 5 Apr 2018 14:22:11 +0200 Subject: [PATCH 17/22] Semver: Semantic version parsing and arithmetics --- xs/CMakeLists.txt | 5 + xs/src/semver/semver.c | 617 +++++++++++++++++++++++++++++++++ xs/src/semver/semver.h | 105 ++++++ xs/src/slic3r/Utils/Semver.hpp | 90 +++++ 4 files changed, 817 insertions(+) create mode 100644 xs/src/semver/semver.c create mode 100644 xs/src/semver/semver.h create mode 100644 xs/src/slic3r/Utils/Semver.hpp diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index 63b99a835..ca398da5a 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -299,6 +299,11 @@ add_library(Shiny STATIC ${LIBDIR}/Shiny/ShinyZone.h ) +add_library(semver STATIC + ${LIBDIR}/semver/semver.h + ${LIBDIR}/semver/semver.c +) + # Generate the Slic3r Perl module (XS) typemap file. set(MyTypemap ${CMAKE_CURRENT_BINARY_DIR}/typemap) add_custom_command( diff --git a/xs/src/semver/semver.c b/xs/src/semver/semver.c new file mode 100644 index 000000000..29bc1868d --- /dev/null +++ b/xs/src/semver/semver.c @@ -0,0 +1,617 @@ +/* + * semver.c + * + * Copyright (c) 2015-2017 Tomas Aparicio + * MIT licensed + */ + +#include +#include +#include +#include "semver.h" + +#define SLICE_SIZE 50 +#define DELIMITER "." +#define PR_DELIMITER "-" +#define MT_DELIMITER "+" +#define NUMBERS "0123456789" +#define ALPHA "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" +#define DELIMITERS DELIMITER PR_DELIMITER MT_DELIMITER +#define VALID_CHARS NUMBERS ALPHA DELIMITERS + +static const size_t MAX_SIZE = sizeof(char) * 255; +static const int MAX_SAFE_INT = (unsigned int) -1 >> 1; + +/** + * Define comparison operators, storing the + * ASCII code per each symbol in hexadecimal notation. + */ + +enum operators { + SYMBOL_GT = 0x3e, + SYMBOL_LT = 0x3c, + SYMBOL_EQ = 0x3d, + SYMBOL_TF = 0x7e, + SYMBOL_CF = 0x5e +}; + +/** + * Private helpers + */ + +/* + * Remove [begin:len-begin] from str by moving len data from begin+len to begin. + * If len is negative cut out to the end of the string. + */ +static int +strcut (char *str, int begin, int len) { + size_t l; + l = strlen(str); + + if((int)l < 0 || (int)l > MAX_SAFE_INT) return -1; + + if (len < 0) len = l - begin + 1; + if (begin + len > (int)l) len = l - begin; + memmove(str + begin, str + begin + len, l - len + 1 - begin); + + return len; +} + +static int +contains (const char c, const char *matrix, size_t len) { + size_t x; + for (x = 0; x < len; x++) + if ((char) matrix[x] == c) return 1; + return 0; +} + +static int +has_valid_chars (const char *str, const char *matrix) { + size_t i, len, mlen; + len = strlen(str); + mlen = strlen(matrix); + + for (i = 0; i < len; i++) + if (contains(str[i], matrix, mlen) == 0) + return 0; + + return 1; +} + +static int +binary_comparison (int x, int y) { + if (x == y) return 0; + if (x > y) return 1; + return -1; +} + +static int +parse_int (const char *s) { + int valid, num; + valid = has_valid_chars(s, NUMBERS); + if (valid == 0) return -1; + + num = strtol(s, NULL, 10); + if (num > MAX_SAFE_INT) return -1; + + return num; +} + +/* + * Return a string allocated on the heap with the content from sep to end and + * terminate buf at sep. + */ +static char * +parse_slice (char *buf, char sep) { + char *pr, *part; + int plen; + + /* Find separator in buf */ + pr = strchr(buf, sep); + if (pr == NULL) return NULL; + /* Length from separator to end of buf */ + plen = strlen(pr); + + /* Copy from buf into new string */ + part = calloc(plen + 1, sizeof(*part)); + if (part == NULL) return NULL; + memcpy(part, pr + 1, plen); + /* Null terminate new string */ + part[plen] = '\0'; + + /* Terminate buf where separator was */ + *pr = '\0'; + + return part; +} + +/** + * Parses a string as semver expression. + * + * Returns: + * + * `0` - Parsed successfully + * `-1` - In case of error + */ + +int +semver_parse (const char *str, semver_t *ver) { + int valid, res; + size_t len; + char *buf; + valid = semver_is_valid(str); + if (!valid) return -1; + + len = strlen(str); + buf = calloc(len + 1, sizeof(*buf)); + if (buf == NULL) return -1; + strcpy(buf, str); + + ver->metadata = parse_slice(buf, MT_DELIMITER[0]); + ver->prerelease = parse_slice(buf, PR_DELIMITER[0]); + + res = semver_parse_version(buf, ver); + free(buf); +#if DEBUG > 0 + printf("[debug] semver.c %s = %d.%d.%d, %s %s\n", str, ver->major, ver->minor, ver->patch, ver->prerelease, ver->metadata); +#endif + return res; +} + +/** + * Parses a given string as semver expression. + * + * Returns: + * + * `0` - Parsed successfully + * `-1` - Parse error or invalid + */ + +int +semver_parse_version (const char *str, semver_t *ver) { + size_t len; + int index, value; + char *slice, *next, *endptr; + slice = (char *) str; + index = 0; + + while (slice != NULL && index++ < 4) { + next = strchr(slice, DELIMITER[0]); + if (next == NULL) + len = strlen(slice); + else + len = next - slice; + if (len > SLICE_SIZE) return -1; + + /* Cast to integer and store */ + value = strtol(slice, &endptr, 10); + if (endptr != next && *endptr != '\0') return -1; + + switch (index) { + case 1: ver->major = value; break; + case 2: ver->minor = value; break; + case 3: ver->patch = value; break; + } + + /* Continue with the next slice */ + if (next == NULL) + slice = NULL; + else + slice = next + 1; + } + + return 0; +} + +static int +compare_prerelease (char *x, char *y) { + char *lastx, *lasty, *xptr, *yptr, *endptr; + int xlen, ylen, xisnum, yisnum, xnum, ynum; + int xn, yn, min, res; + if (x == NULL && y == NULL) return 0; + if (y == NULL && x) return -1; + if (x == NULL && y) return 1; + + lastx = x; + lasty = y; + xlen = strlen(x); + ylen = strlen(y); + + while (1) { + if ((xptr = strchr(lastx, DELIMITER[0])) == NULL) + xptr = x + xlen; + if ((yptr = strchr(lasty, DELIMITER[0])) == NULL) + yptr = y + ylen; + + xnum = strtol(lastx, &endptr, 10); + xisnum = endptr == xptr ? 1 : 0; + ynum = strtol(lasty, &endptr, 10); + yisnum = endptr == yptr ? 1 : 0; + + if (xisnum && !yisnum) return -1; + if (!xisnum && yisnum) return 1; + + if (xisnum && yisnum) { + /* Numerical comparison */ + if (xnum != ynum) return xnum < ynum ? -1 : 1; + } else { + /* String comparison */ + xn = xptr - lastx; + yn = yptr - lasty; + min = xn < yn ? xn : yn; + if ((res = strncmp(lastx, lasty, min))) return res < 0 ? -1 : 1; + if (xn != yn) return xn < yn ? -1 : 1; + } + + lastx = xptr + 1; + lasty = yptr + 1; + if (lastx == x + xlen + 1 && lasty == y + ylen + 1) break; + if (lastx == x + xlen + 1) return -1; + if (lasty == y + ylen + 1) return 1; + } + + return 0; +} + +int +semver_compare_prerelease (semver_t x, semver_t y) { + return compare_prerelease(x.prerelease, y.prerelease); +} + +/** + * Performs a major, minor and patch binary comparison (x, y). + * This function is mostly used internally + * + * Returns: + * + * `0` - If versiona are equal + * `1` - If x is higher than y + * `-1` - If x is lower than y + */ + +int +semver_compare_version (semver_t x, semver_t y) { + int res; + + if ((res = binary_comparison(x.major, y.major)) == 0) { + if ((res = binary_comparison(x.minor, y.minor)) == 0) { + return binary_comparison(x.patch, y.patch); + } + } + + return res; +} + +/** + * Compare two semantic versions (x, y). + * + * Returns: + * - `1` if x is higher than y + * - `0` if x is equal to y + * - `-1` if x is lower than y + */ + +int +semver_compare (semver_t x, semver_t y) { + int res; + + if ((res = semver_compare_version(x, y)) == 0) { + return semver_compare_prerelease(x, y); + } + + return res; +} + +/** + * Performs a `greater than` comparison + */ + +int +semver_gt (semver_t x, semver_t y) { + return semver_compare(x, y) == 1; +} + +/** + * Performs a `lower than` comparison + */ + +int +semver_lt (semver_t x, semver_t y) { + return semver_compare(x, y) == -1; +} + +/** + * Performs a `equality` comparison + */ + +int +semver_eq (semver_t x, semver_t y) { + return semver_compare(x, y) == 0; +} + +/** + * Performs a `non equal to` comparison + */ + +int +semver_neq (semver_t x, semver_t y) { + return semver_compare(x, y) != 0; +} + +/** + * Performs a `greater than or equal` comparison + */ + +int +semver_gte (semver_t x, semver_t y) { + return semver_compare(x, y) >= 0; +} + +/** + * Performs a `lower than or equal` comparison + */ + +int +semver_lte (semver_t x, semver_t y) { + return semver_compare(x, y) <= 0; +} + +/** + * Checks if version `x` can be satisfied by `y` + * performing a comparison with caret operator. + * + * See: https://docs.npmjs.com/misc/semver#caret-ranges-1-2-3-0-2-5-0-0-4 + * + * Returns: + * + * `1` - Can be satisfied + * `0` - Cannot be satisfied + */ + +int +semver_satisfies_caret (semver_t x, semver_t y) { + if (x.major == y.major) { + if (x.major == 0) { + return x.minor >= y.minor; + } + return 1; + } + return 0; +} + +/** + * Checks if version `x` can be satisfied by `y` + * performing a comparison with tilde operator. + * + * See: https://docs.npmjs.com/misc/semver#tilde-ranges-1-2-3-1-2-1 + * + * Returns: + * + * `1` - Can be satisfied + * `0` - Cannot be satisfied + */ + +int +semver_satisfies_patch (semver_t x, semver_t y) { + return x.major == y.major + && x.minor == y.minor; +} + +/** + * Checks if both versions can be satisfied + * based on the given comparison operator. + * + * Allowed operators: + * + * - `=` - Equality + * - `>=` - Higher or equal to + * - `<=` - Lower or equal to + * - `<` - Lower than + * - `>` - Higher than + * - `^` - Caret comparison (see https://docs.npmjs.com/misc/semver#caret-ranges-1-2-3-0-2-5-0-0-4) + * - `~` - Tilde comparison (see https://docs.npmjs.com/misc/semver#tilde-ranges-1-2-3-1-2-1) + * + * Returns: + * + * `1` - Can be satisfied + * `0` - Cannot be satisfied + */ + +int +semver_satisfies (semver_t x, semver_t y, const char *op) { + int first, second; + /* Extract the comparison operator */ + first = op[0]; + second = op[1]; + + /* Caret operator */ + if (first == SYMBOL_CF) + return semver_satisfies_caret(x, y); + + /* Tilde operator */ + if (first == SYMBOL_TF) + return semver_satisfies_patch(x, y); + + /* Strict equality */ + if (first == SYMBOL_EQ) + return semver_eq(x, y); + + /* Greater than or equal comparison */ + if (first == SYMBOL_GT) { + if (second == SYMBOL_EQ) { + return semver_gte(x, y); + } + return semver_gt(x, y); + } + + /* Lower than or equal comparison */ + if (first == SYMBOL_LT) { + if (second == SYMBOL_EQ) { + return semver_lte(x, y); + } + return semver_lt(x, y); + } + + return 0; +} + +/** + * Free heep allocated memory of a given semver. + * This is just a convenient function that you + * should call when you're done. + */ + +void +semver_free (semver_t *x) { + if (x->metadata) { + free(x->metadata); + x->metadata = NULL; + } + if (x->prerelease) { + free(x->prerelease); + x->prerelease = NULL; + } +} + +/** + * Renders + */ + +static void +concat_num (char * str, int x, char * sep) { + char buf[SLICE_SIZE] = {0}; + if (sep == NULL) sprintf(buf, "%d", x); + else sprintf(buf, "%s%d", sep, x); + strcat(str, buf); +} + +static void +concat_char (char * str, char * x, char * sep) { + char buf[SLICE_SIZE] = {0}; + sprintf(buf, "%s%s", sep, x); + strcat(str, buf); +} + +/** + * Render a given semver as string + */ + +void +semver_render (semver_t *x, char *dest) { + if (x->major) concat_num(dest, x->major, NULL); + if (x->minor) concat_num(dest, x->minor, DELIMITER); + if (x->patch) concat_num(dest, x->patch, DELIMITER); + if (x->prerelease) concat_char(dest, x->prerelease, PR_DELIMITER); + if (x->metadata) concat_char(dest, x->metadata, MT_DELIMITER); +} + +/** + * Version bump helpers + */ + +void +semver_bump (semver_t *x) { + x->major++; +} + +void +semver_bump_minor (semver_t *x) { + x->minor++; +} + +void +semver_bump_patch (semver_t *x) { + x->patch++; +} + +/** + * Helpers + */ + +static int +has_valid_length (const char *s) { + return strlen(s) <= MAX_SIZE; +} + +/** + * Checks if a given semver string is valid + * + * Returns: + * + * `1` - Valid expression + * `0` - Invalid + */ + +int +semver_is_valid (const char *s) { + return has_valid_length(s) + && has_valid_chars(s, VALID_CHARS); +} + +/** + * Removes non-valid characters in the given string. + * + * Returns: + * + * `0` - Valid + * `-1` - Invalid input + */ + +int +semver_clean (char *s) { + size_t i, len, mlen; + int res; + if (has_valid_length(s) == 0) return -1; + + len = strlen(s); + mlen = strlen(VALID_CHARS); + + for (i = 0; i < len; i++) { + if (contains(s[i], VALID_CHARS, mlen) == 0) { + res = strcut(s, i, 1); + if(res == -1) return -1; + --len; --i; + } + } + + return 0; +} + +static int +char_to_int (const char * str) { + int buf; + size_t i,len, mlen; + buf = 0; + len = strlen(str); + mlen = strlen(VALID_CHARS); + + for (i = 0; i < len; i++) + if (contains(str[i], VALID_CHARS, mlen)) + buf += (int) str[i]; + + return buf; +} + +/** + * Render a given semver as numeric value. + * Useful for ordering and filtering. + */ + +int +semver_numeric (semver_t *x) { + int num; + char buf[SLICE_SIZE * 3]; + memset(&buf, 0, SLICE_SIZE * 3); + + if (x->major) concat_num(buf, x->major, NULL); + if (x->minor) concat_num(buf, x->minor, NULL); + if (x->patch) concat_num(buf, x->patch, NULL); + + num = parse_int(buf); + if(num == -1) return -1; + + if (x->prerelease) num += char_to_int(x->prerelease); + if (x->metadata) num += char_to_int(x->metadata); + + return num; +} diff --git a/xs/src/semver/semver.h b/xs/src/semver/semver.h new file mode 100644 index 000000000..1b48670ca --- /dev/null +++ b/xs/src/semver/semver.h @@ -0,0 +1,105 @@ +/* + * semver.h + * + * Copyright (c) 2015-2017 Tomas Aparicio + * MIT licensed + */ + +#ifndef __SEMVER_H +#define __SEMVER_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef SEMVER_VERSION +#define SEMVER_VERSION "0.2.0" +#endif + +/** + * semver_t struct + */ + +typedef struct semver_version_s { + int major; + int minor; + int patch; + char * metadata; + char * prerelease; +} semver_t; + +/** + * Set prototypes + */ + +int +semver_satisfies (semver_t x, semver_t y, const char *op); + +int +semver_satisfies_caret (semver_t x, semver_t y); + +int +semver_satisfies_patch (semver_t x, semver_t y); + +int +semver_compare (semver_t x, semver_t y); + +int +semver_compare_version (semver_t x, semver_t y); + +int +semver_compare_prerelease (semver_t x, semver_t y); + +int +semver_gt (semver_t x, semver_t y); + +int +semver_gte (semver_t x, semver_t y); + +int +semver_lt (semver_t x, semver_t y); + +int +semver_lte (semver_t x, semver_t y); + +int +semver_eq (semver_t x, semver_t y); + +int +semver_neq (semver_t x, semver_t y); + +int +semver_parse (const char *str, semver_t *ver); + +int +semver_parse_version (const char *str, semver_t *ver); + +void +semver_render (semver_t *x, char *dest); + +int +semver_numeric (semver_t *x); + +void +semver_bump (semver_t *x); + +void +semver_bump_minor (semver_t *x); + +void +semver_bump_patch (semver_t *x); + +void +semver_free (semver_t *x); + +int +semver_is_valid (const char *s); + +int +semver_clean (char *s); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/xs/src/slic3r/Utils/Semver.hpp b/xs/src/slic3r/Utils/Semver.hpp new file mode 100644 index 000000000..3606e90e4 --- /dev/null +++ b/xs/src/slic3r/Utils/Semver.hpp @@ -0,0 +1,90 @@ +#ifndef slic3r_Semver_hpp_ +#define slic3r_Semver_hpp_ + +#include +#include +#include +#include + +#include "semver/semver.h" + +namespace Slic3r { + + +class Semver +{ +public: + struct Major { const int i; Major(int i) : i(i) {} }; + struct Minor { const int i; Minor(int i) : i(i) {} }; + struct Patch { const int i; Patch(int i) : i(i) {} }; + + static boost::optional parse(const std::string &str) + { + semver_t ver; + if (::semver_parse(str.c_str(), &ver) == 0) { + return Semver(ver); + } else { + return boost::none; + } + } + + Semver(Semver &&other) { *this = std::move(other); } + Semver(const Semver &other) { *this = other; } + + Semver &operator=(Semver &&other) + { + ver = other.ver; + other.ver.major = other.ver.minor = other.ver.patch = 0; + other.ver.metadata = other.ver.prerelease = nullptr; + } + + Semver &operator=(const Semver &other) + { + ver = other.ver; + if (other.ver.metadata != nullptr) { std::strcpy(ver.metadata, other.ver.metadata); } + if (other.ver.prerelease != nullptr) { std::strcpy(ver.prerelease, other.ver.prerelease); } + } + + ~Semver() { ::semver_free(&ver); } + + // Comparison + bool operator<(const Semver &b) const { return ::semver_compare(ver, b.ver) == -1; } + bool operator<=(const Semver &b) const { return ::semver_compare(ver, b.ver) <= 0; } + bool operator==(const Semver &b) const { return ::semver_compare(ver, b.ver) == 0; } + bool operator!=(const Semver &b) const { return ::semver_compare(ver, b.ver) != 0; } + bool operator>=(const Semver &b) const { return ::semver_compare(ver, b.ver) >= 0; } + bool operator>(const Semver &b) const { return ::semver_compare(ver, b.ver) == 1; } + // We're using '&' instead of the '~' operator here as '~' is unary-only: + bool operator&(const Semver &b) const { return ::semver_satisfies_patch(ver, b.ver); } + bool operator^(const Semver &b) const { return ::semver_satisfies_caret(ver, b.ver); } + + // Conversion + std::string to_string() const { + auto res = (boost::format("%1%.%2%.%3%") % ver.major % ver.minor % ver.patch).str(); + if (ver.prerelease != nullptr) { res += '-'; res += ver.prerelease; } + if (ver.metadata != nullptr) { res += '+'; res += ver.metadata; } + return res; + } + + // Arithmetics + Semver& operator+=(const Major &b) { ver.major += b.i; return *this; } + Semver& operator+=(const Minor &b) { ver.minor += b.i; return *this; } + Semver& operator+=(const Patch &b) { ver.patch += b.i; return *this; } + Semver& operator-=(const Major &b) { ver.major -= b.i; return *this; } + Semver& operator-=(const Minor &b) { ver.minor -= b.i; return *this; } + Semver& operator-=(const Patch &b) { ver.patch -= b.i; return *this; } + Semver operator+(const Major &b) const { Semver res(*this); return res += b; } + Semver operator+(const Minor &b) const { Semver res(*this); return res += b; } + Semver operator+(const Patch &b) const { Semver res(*this); return res += b; } + Semver operator-(const Major &b) const { Semver res(*this); return res -= b; } + Semver operator-(const Minor &b) const { Semver res(*this); return res -= b; } + Semver operator-(const Patch &b) const { Semver res(*this); return res -= b; } +private: + semver_t ver; + + Semver(semver_t ver) : ver(ver) {} +}; + + +} +#endif From abe94706f69a45714541330f02c8a558e3844576 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 6 Apr 2018 13:27:51 +0200 Subject: [PATCH 18/22] Gyroid infill bug fix: abs vs std::abs --- xs/src/libslic3r/Fill/FillGyroid.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/src/libslic3r/Fill/FillGyroid.cpp b/xs/src/libslic3r/Fill/FillGyroid.cpp index a3920caab..dbe6ec896 100644 --- a/xs/src/libslic3r/Fill/FillGyroid.cpp +++ b/xs/src/libslic3r/Fill/FillGyroid.cpp @@ -65,7 +65,7 @@ static Polylines make_gyroid_waves(double gridZ, double density_adjusted, double double z_sin = sin(z); double z_cos = cos(z); Polylines result; - if (abs(z_sin) <= abs(z_cos)) { + if (std::abs(z_sin) <= std::abs(z_cos)) { // Vertical wave double x0 = M_PI * (int)((- 0.5 * M_PI) / M_PI - 1.); bool flip = ((int)(x0 / M_PI + 1.) & 1) != 0; From 670061ac333cf96007b3c599fc8981bd7f2150a6 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 6 Apr 2018 16:49:33 +0200 Subject: [PATCH 19/22] Initial implementation of configuration snapshotting. --- xs/CMakeLists.txt | 5 + xs/src/libslic3r/FileParserError.hpp | 48 +++++ xs/src/libslic3r/Utils.hpp | 3 - xs/src/libslic3r/utils.cpp | 28 --- xs/src/slic3r/Config/Snapshot.cpp | 308 +++++++++++++++++++++++++++ xs/src/slic3r/Config/Snapshot.hpp | 106 +++++++++ xs/src/slic3r/Config/Version.cpp | 136 ++++++++++++ xs/src/slic3r/Config/Version.hpp | 75 +++++++ xs/src/slic3r/Utils/Semver.hpp | 24 +++ xs/src/slic3r/Utils/Time.cpp | 63 ++++++ xs/src/slic3r/Utils/Time.hpp | 22 ++ 11 files changed, 787 insertions(+), 31 deletions(-) create mode 100644 xs/src/libslic3r/FileParserError.hpp create mode 100644 xs/src/slic3r/Config/Snapshot.cpp create mode 100644 xs/src/slic3r/Config/Snapshot.hpp create mode 100644 xs/src/slic3r/Config/Version.cpp create mode 100644 xs/src/slic3r/Config/Version.hpp create mode 100644 xs/src/slic3r/Utils/Time.cpp create mode 100644 xs/src/slic3r/Utils/Time.hpp diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index ca398da5a..501c90db2 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -54,6 +54,7 @@ add_library(libslic3r STATIC ${LIBDIR}/libslic3r/ExtrusionEntityCollection.hpp ${LIBDIR}/libslic3r/ExtrusionSimulator.cpp ${LIBDIR}/libslic3r/ExtrusionSimulator.hpp + ${LIBDIR}/libslic3r/FileParserError.hpp ${LIBDIR}/libslic3r/Fill/Fill.cpp ${LIBDIR}/libslic3r/Fill/Fill.hpp ${LIBDIR}/libslic3r/Fill/Fill3DHoneycomb.cpp @@ -201,6 +202,10 @@ add_library(libslic3r_gui STATIC ${LIBDIR}/slic3r/GUI/wxExtensions.hpp ${LIBDIR}/slic3r/GUI/BonjourDialog.cpp ${LIBDIR}/slic3r/GUI/BonjourDialog.hpp + ${LIBDIR}/slic3r/Config/Snapshot.cpp + ${LIBDIR}/slic3r/Config/Snapshot.hpp + ${LIBDIR}/slic3r/Config/Version.cpp + ${LIBDIR}/slic3r/Config/Version.hpp ${LIBDIR}/slic3r/Utils/ASCIIFolding.cpp ${LIBDIR}/slic3r/Utils/ASCIIFolding.hpp ${LIBDIR}/slic3r/Utils/Http.cpp diff --git a/xs/src/libslic3r/FileParserError.hpp b/xs/src/libslic3r/FileParserError.hpp new file mode 100644 index 000000000..82a6b328e --- /dev/null +++ b/xs/src/libslic3r/FileParserError.hpp @@ -0,0 +1,48 @@ +#ifndef slic3r_FileParserError_hpp_ +#define slic3r_FileParserError_hpp_ + +#include "libslic3r.h" + +#include +#include + +namespace Slic3r { + +// Generic file parser error, mostly copied from boost::property_tree::file_parser_error +class file_parser_error: public std::runtime_error +{ +public: + file_parser_error(const std::string &msg, const std::string &file, unsigned long line = 0) : + std::runtime_error(format_what(msg, file, line)), + m_message(msg), m_filename(file), m_line(line) {} + // gcc 3.4.2 complains about lack of throw specifier on compiler + // generated dtor + ~file_parser_error() throw() {} + + // Get error message (without line and file - use what() to get full message) + std::string message() const { return m_message; } + // Get error filename + std::string filename() const { return m_filename; } + // Get error line number + unsigned long line() const { return m_line; } + +private: + std::string m_message; + std::string m_filename; + unsigned long m_line; + + // Format error message to be returned by std::runtime_error::what() + static std::string format_what(const std::string &msg, const std::string &file, unsigned long l) + { + std::stringstream stream; + stream << (file.empty() ? "" : file.c_str()); + if (l > 0) + stream << '(' << l << ')'; + stream << ": " << msg; + return stream.str(); + } +}; + +}; // Slic3r + +#endif // slic3r_FileParserError_hpp_ diff --git a/xs/src/libslic3r/Utils.hpp b/xs/src/libslic3r/Utils.hpp index 27e7fad6b..5727d6c89 100644 --- a/xs/src/libslic3r/Utils.hpp +++ b/xs/src/libslic3r/Utils.hpp @@ -60,9 +60,6 @@ extern std::string timestamp_str(); // to be placed at the top of Slic3r generated files. inline std::string header_slic3r_generated() { return std::string("generated by " SLIC3R_FORK_NAME " " SLIC3R_VERSION " " ) + timestamp_str(); } -// Encode a file into a multi-part HTTP response with a given boundary. -std::string octoprint_encode_file_send_request_content(const char *path, bool select, bool print, const char *boundary); - // Compute the next highest power of 2 of 32-bit v // http://graphics.stanford.edu/~seander/bithacks.html template diff --git a/xs/src/libslic3r/utils.cpp b/xs/src/libslic3r/utils.cpp index 34b9eaa9f..733757e25 100644 --- a/xs/src/libslic3r/utils.cpp +++ b/xs/src/libslic3r/utils.cpp @@ -263,7 +263,6 @@ namespace PerlUtils { std::string timestamp_str() { const auto now = boost::posix_time::second_clock::local_time(); - const auto date = now.date(); char buf[2048]; sprintf(buf, "on %04d-%02d-%02d at %02d:%02d:%02d", // Local date in an ANSII format. @@ -272,31 +271,4 @@ std::string timestamp_str() return buf; } -std::string octoprint_encode_file_send_request_content(const char *cpath, bool select, bool print, const char *boundary) -{ - // Read the complete G-code string into a string buffer. - // It will throw if the file cannot be open or read. - std::stringstream str_stream; - { - boost::nowide::ifstream ifs(cpath); - str_stream << ifs.rdbuf(); - } - - boost::filesystem::path path(cpath); - std::string request = boundary + '\n'; - request += "Content-Disposition: form-data; name=\""; - request += path.stem().string() + "\"; filename=\"" + path.filename().string() + "\"\n"; - request += "Content-Type: application/octet-stream\n\n"; - request += str_stream.str(); - request += boundary + '\n'; - request += "Content-Disposition: form-data; name=\"select\"\n\n"; - request += select ? "true\n" : "false\n"; - request += boundary + '\n'; - request += "Content-Disposition: form-data; name=\"print\"\n\n"; - request += print ? "true\n" : "false\n"; - request += boundary + '\n'; - - return request; -} - }; // namespace Slic3r diff --git a/xs/src/slic3r/Config/Snapshot.cpp b/xs/src/slic3r/Config/Snapshot.cpp new file mode 100644 index 000000000..559e4c63c --- /dev/null +++ b/xs/src/slic3r/Config/Snapshot.cpp @@ -0,0 +1,308 @@ +#include "Snapshot.hpp" +#include "../GUI/AppConfig.hpp" +#include "../Utils/Time.hpp" + +#include + +#include +#include +#include +#include +#include + +#include "../../libslic3r/libslic3r.h" +#include "../../libslic3r/Config.hpp" +#include "../../libslic3r/FileParserError.hpp" +#include "../../libslic3r/Utils.hpp" + +#define SLIC3R_SNAPSHOTS_DIR "snapshots" +#define SLIC3R_SNAPSHOT_FILE "snapshot.ini" + +namespace Slic3r { +namespace GUI { +namespace Config { + +void Snapshot::clear() +{ + this->id.clear(); + this->time_captured = 0; + this->slic3r_version_captured = Semver::invalid(); + this->comment.clear(); + this->reason = SNAPSHOT_UNKNOWN; + this->print.clear(); + this->filaments.clear(); + this->printer.clear(); +} + +void Snapshot::load_ini(const std::string &path) +{ + this->clear(); + + auto throw_on_parse_error = [&path](const std::string &msg) { + throw file_parser_error(std::string("Failed loading the snapshot file. Reason: ") + msg, path); + }; + + // Load the snapshot.ini file. + boost::property_tree::ptree tree; + try { + boost::nowide::ifstream ifs(path); + boost::property_tree::read_ini(ifs, tree); + } catch (const std::ifstream::failure &err) { + throw file_parser_error(std::string("The snapshot file cannot be loaded. Reason: ") + err.what(), path); + } catch (const std::runtime_error &err) { + throw_on_parse_error(err.what()); + } + + // Parse snapshot.ini + std::string group_name_vendor = "Vendor:"; + std::string key_filament = "filament"; + for (auto §ion : tree) { + if (section.first == "snapshot") { + // Parse the common section. + for (auto &kvp : section.second) { + if (kvp.first == "id") + this->id = kvp.second.data(); + else if (kvp.first == "time_captured") { + this->time_captured = Slic3r::Utils::parse_time_ISO8601Z(kvp.second.data()); + if (this->time_captured == (time_t)-1) + throw_on_parse_error("invalid timestamp"); + } else if (kvp.first == "slic3r_version_captured") { + auto semver = Semver::parse(kvp.second.data()); + if (! semver) + throw_on_parse_error("invalid slic3r_version_captured semver"); + this->slic3r_version_captured = *semver; + } else if (kvp.first == "comment") { + this->comment = kvp.second.data(); + } else if (kvp.first == "reason") { + std::string rsn = kvp.second.data(); + if (rsn == "upgrade") + this->reason = SNAPSHOT_UPGRADE; + else if (rsn == "downgrade") + this->reason = SNAPSHOT_DOWNGRADE; + else if (rsn == "user") + this->reason = SNAPSHOT_USER; + else + this->reason = SNAPSHOT_UNKNOWN; + } + } + } else if (section.first == "presets") { + // Load the names of the active presets. + for (auto &kvp : section.second) { + if (kvp.first == "print") { + this->print = kvp.second.data(); + } else if (boost::starts_with(kvp.first, "filament")) { + int idx = 0; + if (kvp.first == "filament" || sscanf(kvp.first.c_str(), "filament_%d", &idx) == 1) { + if (int(this->filaments.size()) <= idx) + this->filaments.resize(idx + 1, std::string()); + this->filaments[idx] = kvp.second.data(); + } + } else if (kvp.first == "printer") { + this->printer = kvp.second.data(); + } + } + } else if (boost::starts_with(section.first, group_name_vendor) && section.first.size() > group_name_vendor.size()) { + // Vendor specific section. + VendorConfig vc; + vc.name = section.first.substr(group_name_vendor.size()); + for (auto &kvp : section.second) { + if (boost::starts_with(kvp.first, "model_")) { + //model:MK2S = 0.4;xxx + //model:MK3 = 0.4;xxx + } else if (kvp.first == "version" || kvp.first == "min_slic3r_version" || kvp.first == "max_slic3r_version") { + // Version of the vendor specific config bundle bundled with this snapshot. + auto semver = Semver::parse(kvp.second.data()); + if (! semver) + throw_on_parse_error("invalid " + kvp.first + " format for " + section.first); + if (kvp.first == "version") + vc.version = *semver; + else if (kvp.first == "min_slic3r_version") + vc.min_slic3r_version = *semver; + else + vc.max_slic3r_version = *semver; + } + } + } + } +} + +void Snapshot::save_ini(const std::string &path) +{ + boost::nowide::ofstream c; + c.open(path, std::ios::out | std::ios::trunc); + c << "# " << Slic3r::header_slic3r_generated() << std::endl; + + // Export the common "snapshot". + c << std::endl << "[snapshot]" << std::endl; + c << "id = " << this->id << std::endl; + c << "time_captured = " << Slic3r::Utils::format_time_ISO8601Z(this->time_captured) << std::endl; + c << "slic3r_version_captured = " << this->slic3r_version_captured.to_string() << std::endl; + c << "comment = " << this->comment << std::endl; + c << "reason = " << this->reason << std::endl; + + // Export the active presets at the time of the snapshot. + c << std::endl << "[presets]" << std::endl; + c << "print = " << this->print << std::endl; + c << "filament = " << this->filaments.front() << std::endl; + for (size_t i = 1; i < this->filaments.size(); ++ i) + c << "filament_" << std::to_string(i) << " = " << this->filaments[i] << std::endl; + c << "printer = " << this->printer << std::endl; + + // Export the vendor configs. + for (const VendorConfig &vc : this->vendor_configs) { + c << std::endl << "[Vendor:" << vc.name << "]" << std::endl; + c << "version = " << vc.version.to_string() << std::endl; + c << "min_slic3r_version = " << vc.min_slic3r_version.to_string() << std::endl; + c << "max_slic3r_version = " << vc.max_slic3r_version.to_string() << std::endl; + } + c.close(); +} + +void Snapshot::export_selections(AppConfig &config) const +{ + assert(filaments.size() >= 1); + config.clear_section("presets"); + config.set("presets", "print", print); + config.set("presets", "filament", filaments.front()); + for (int i = 1; i < filaments.size(); ++i) { + char name[64]; + sprintf(name, "filament_%d", i); + config.set("presets", name, filaments[i]); + } + config.set("presets", "printer", printer); +} + +size_t SnapshotDB::load_db() +{ + boost::filesystem::path snapshots_dir = SnapshotDB::create_db_dir(); + + m_snapshots.clear(); + + // Walk over the snapshot directories and load their index. + std::string errors_cummulative; + for (auto &dir_entry : boost::filesystem::directory_iterator(snapshots_dir)) + if (boost::filesystem::is_directory(dir_entry.status())) { + // Try to read "snapshot.ini". + boost::filesystem::path path_ini = dir_entry.path() / SLIC3R_SNAPSHOT_FILE; + Snapshot snapshot; + try { + snapshot.load_ini(path_ini.string()); + } catch (const std::runtime_error &err) { + errors_cummulative += err.what(); + errors_cummulative += "\n"; + continue; + } + // Check that the name of the snapshot directory matches the snapshot id stored in the snapshot.ini file. + if (dir_entry.path().filename().string() != snapshot.id) { + errors_cummulative += std::string("Snapshot ID ") + snapshot.id + " does not match the snapshot directory " + dir_entry.path().filename().string() + "\n"; + continue; + } + m_snapshots.emplace_back(std::move(snapshot)); + } + + if (! errors_cummulative.empty()) + throw std::runtime_error(errors_cummulative); + return m_snapshots.size(); +} + +static void copy_config_dir_single_level(const boost::filesystem::path &path_src, const boost::filesystem::path &path_dst) +{ + if (! boost::filesystem::is_directory(path_dst) && + ! boost::filesystem::create_directory(path_dst)) + throw std::runtime_error(std::string("Slic3r was unable to create a directory at ") + path_dst.string()); + + for (auto &dir_entry : boost::filesystem::directory_iterator(path_src)) + if (boost::filesystem::is_regular_file(dir_entry.status()) && boost::algorithm::iends_with(dir_entry.path().filename().string(), ".ini")) + boost::filesystem::copy_file(dir_entry.path(), path_dst / dir_entry.path().filename(), boost::filesystem::copy_option::overwrite_if_exists); +} + +static void delete_existing_ini_files(const boost::filesystem::path &path) +{ + if (! boost::filesystem::is_directory(path)) + return; + for (auto &dir_entry : boost::filesystem::directory_iterator(path)) + if (boost::filesystem::is_regular_file(dir_entry.status()) && boost::algorithm::iends_with(dir_entry.path().filename().string(), ".ini")) + boost::filesystem::remove(dir_entry.path()); +} + +const Snapshot& SnapshotDB::make_snapshot(const AppConfig &app_config, Snapshot::Reason reason, const std::string &comment) +{ + boost::filesystem::path data_dir = boost::filesystem::path(Slic3r::data_dir()); + boost::filesystem::path snapshot_db_dir = SnapshotDB::create_db_dir(); + + // 1) Prepare the snapshot structure. + Snapshot snapshot; + // Snapshot header. + snapshot.time_captured = Slic3r::Utils::get_current_time_utc(); + snapshot.id = Slic3r::Utils::format_time_ISO8601Z(snapshot.time_captured); + snapshot.slic3r_version_captured = *Semver::parse(SLIC3R_VERSION); + snapshot.comment = comment; + snapshot.reason = reason; + // Active presets at the time of the snapshot. + snapshot.print = app_config.get("presets", "print"); + snapshot.filaments.emplace_back(app_config.get("presets", "filament")); + snapshot.printer = app_config.get("presets", "printer"); + for (unsigned int i = 1; i < 1000; ++ i) { + char name[64]; + sprintf(name, "filament_%d", i); + if (! app_config.has("presets", name)) + break; + snapshot.filaments.emplace_back(app_config.get("presets", name)); + } + // Vendor specific config bundles and installed printers. + + // Backup the presets. + boost::filesystem::path snapshot_dir = snapshot_db_dir / snapshot.id; + for (const char *subdir : { "print", "filament", "printer", "vendor" }) + copy_config_dir_single_level(data_dir / subdir, snapshot_dir / subdir); + snapshot.save_ini((snapshot_dir / "snapshot.ini").string()); + m_snapshots.emplace_back(std::move(snapshot)); + return m_snapshots.back(); +} + +void SnapshotDB::restore_snapshot(const std::string &id, AppConfig &app_config) +{ + for (const Snapshot &snapshot : m_snapshots) + if (snapshot.id == id) { + this->restore_snapshot(snapshot, app_config); + return; + } + throw std::runtime_error(std::string("Snapshot with id " + id + " was not found.")); +} + +void SnapshotDB::restore_snapshot(const Snapshot &snapshot, AppConfig &app_config) +{ + boost::filesystem::path data_dir = boost::filesystem::path(Slic3r::data_dir()); + boost::filesystem::path snapshot_db_dir = SnapshotDB::create_db_dir(); + boost::filesystem::path snapshot_dir = snapshot_db_dir / snapshot.id; + + // Remove existing ini files and restore the ini files from the snapshot. + for (const char *subdir : { "print", "filament", "printer", "vendor" }) { + delete_existing_ini_files(data_dir / subdir); + copy_config_dir_single_level(snapshot_dir / subdir, data_dir / subdir); + } + + // Update app_config from the snapshot. + snapshot.export_selections(app_config); + + // Store information about the snapshot. + +} + +boost::filesystem::path SnapshotDB::create_db_dir() +{ + boost::filesystem::path data_dir = boost::filesystem::path(Slic3r::data_dir()); + boost::filesystem::path snapshots_dir = data_dir / SLIC3R_SNAPSHOTS_DIR; + for (const boost::filesystem::path &path : { data_dir, snapshots_dir }) { + boost::filesystem::path subdir = path; + subdir.make_preferred(); + if (! boost::filesystem::is_directory(subdir) && + ! boost::filesystem::create_directory(subdir)) + throw std::runtime_error(std::string("Slic3r was unable to create a directory at ") + subdir.string()); + } + return snapshots_dir; +} + +} // namespace Config +} // namespace GUI +} // namespace Slic3r diff --git a/xs/src/slic3r/Config/Snapshot.hpp b/xs/src/slic3r/Config/Snapshot.hpp new file mode 100644 index 000000000..358797bf7 --- /dev/null +++ b/xs/src/slic3r/Config/Snapshot.hpp @@ -0,0 +1,106 @@ +#ifndef slic3r_GUI_Snapshot_ +#define slic3r_GUI_Snapshot_ + +#include +#include + +#include + +#include "../Utils/Semver.hpp" + +namespace Slic3r { + +class AppConfig; + +namespace GUI { +namespace Config { + +// A snapshot contains: +// Slic3r.ini +// vendor/ +// print/ +// filament/ +// printer/ +class Snapshot +{ +public: + enum Reason { + SNAPSHOT_UNKNOWN, + SNAPSHOT_UPGRADE, + SNAPSHOT_DOWNGRADE, + SNAPSHOT_USER, + }; + + Snapshot() { clear(); } + + void clear(); + void load_ini(const std::string &path); + void save_ini(const std::string &path); + + // Export the print / filament / printer selections to be activated into the AppConfig. + void export_selections(AppConfig &config) const; + + // ID of a snapshot should equal to the name of the snapshot directory. + // The ID contains the date/time, reason and comment to be human readable. + std::string id; + std::time_t time_captured; + // Which Slic3r version captured this snapshot? + Semver slic3r_version_captured = Semver::invalid(); + // Comment entered by the user at the start of the snapshot capture. + std::string comment; + Reason reason; + + // Active presets at the time of the snapshot. + std::string print; + std::vector filaments; + std::string printer; + + // Annotation of the vendor configuration stored in the snapshot. + // This information is displayed to the user and used to decide compatibility + // of the configuration stored in the snapshot with the running Slic3r version. + struct VendorConfig { + // Name of the vendor contained in this snapshot. + std::string name; + // Version of the vendor config contained in this snapshot. + Semver version = Semver::invalid(); + // Minimum Slic3r version compatible with this vendor configuration. + Semver min_slic3r_version = Semver::zero(); + // Maximum Slic3r version compatible with this vendor configuration, or empty. + Semver max_slic3r_version = Semver::inf(); + }; + // List of vendor configs contained in this snapshot. + std::vector vendor_configs; +}; + +class SnapshotDB +{ +public: + typedef std::vector::const_iterator const_iterator; + + // Load the snapshot database from the snapshots directory. + // If the snapshot directory or its parent does not exist yet, it will be created. + // Returns a number of snapshots loaded. + size_t load_db(); + + // Create a snapshot directory, copy the vendor config bundles, user print/filament/printer profiles, + // create an index. + const Snapshot& make_snapshot(const AppConfig &app_config, Snapshot::Reason reason, const std::string &comment); + void restore_snapshot(const std::string &id, AppConfig &app_config); + void restore_snapshot(const Snapshot &snapshot, AppConfig &app_config); + + const_iterator begin() const { return m_snapshots.begin(); } + const_iterator end() const { return m_snapshots.end(); } + const std::vector& snapshots() const { return m_snapshots; } + +private: + // Create the snapshots directory if it does not exist yet. + static boost::filesystem::path create_db_dir(); + + std::vector m_snapshots; +}; + +} // namespace Config +} // namespace GUI +} // namespace Slic3r + +#endif /* slic3r_GUI_Snapshot_ */ diff --git a/xs/src/slic3r/Config/Version.cpp b/xs/src/slic3r/Config/Version.cpp new file mode 100644 index 000000000..1102f3149 --- /dev/null +++ b/xs/src/slic3r/Config/Version.cpp @@ -0,0 +1,136 @@ +#include "Version.hpp" + +#include +#include +#include + +#include "../../libslic3r/libslic3r.h" +#include "../../libslic3r/Config.hpp" + +namespace Slic3r { +namespace GUI { +namespace Config { + +static boost::optional s_current_slic3r_semver = Semver::parse(SLIC3R_VERSION); + +bool Version::is_current_slic3r_supported() const +{ + return this->is_slic3r_supported(*s_current_slic3r_semver); +} + +inline char* left_trim(char *c) +{ + for (; *c == ' ' || *c == '\t'; ++ c); + return c; +} + +inline char* right_trim(char *start) +{ + char *end = start + strlen(start) - 1; + for (; end >= start && (*end == ' ' || *end == '\t'); -- end); + *(++ end) = 0; + return end; +} + +inline std::string unquote_value(char *value, char *end, const std::string &path, int idx_line) +{ + std::string svalue; + if (value == end) { + // Empty string is a valid string. + } else if (*value == '"') { + if (++ value < -- end || *end != '"') + throw file_parser_error("String not enquoted correctly", path, idx_line); + *end = 0; + if (! unescape_string_cstyle(value, svalue)) + throw file_parser_error("Invalid escape sequence inside a quoted string", path, idx_line); + } + return svalue; +} + +inline std::string unquote_version_comment(char *value, char *end, const std::string &path, int idx_line) +{ + std::string svalue; + if (value == end) { + // Empty string is a valid string. + } else if (*value == '"') { + if (++ value < -- end || *end != '"') + throw file_parser_error("Version comment not enquoted correctly", path, idx_line); + *end = 0; + if (! unescape_string_cstyle(value, svalue)) + throw file_parser_error("Invalid escape sequence inside a quoted version comment", path, idx_line); + } + return svalue; +} + +size_t Index::load(const std::string &path) +{ + m_configs.clear(); + + boost::nowide::ifstream ifs(path); + std::string line; + size_t idx_line = 0; + Version ver; + while (std::getline(ifs, line)) { + ++ idx_line; + // Skip the initial white spaces. + char *key = left_trim(const_cast(line.data())); + // Right trim the line. + char *end = right_trim(key); + // Keyword may only contain alphanumeric characters. Semantic version may in addition contain "+.-". + char *key_end = key; + bool maybe_semver = false; + for (;; ++ key) { + if (strchr("+.-", *key) != nullptr) + maybe_semver = true; + else if (! std::isalnum(*key)) + break; + } + if (*key != 0 && *key != ' ' && *key != '\t' && *key != '=') + throw file_parser_error("Invalid keyword or semantic version", path, idx_line); + *key_end = 0; + boost::optional semver; + if (maybe_semver) + semver = Semver::parse(key); + char *value = left_trim(key_end); + if (*value == '=') { + if (semver) + throw file_parser_error("Key cannot be a semantic version", path, idx_line); + // Verify validity of the key / value pair. + std::string svalue = unquote_value(left_trim(++ value), end, path, idx_line); + if (key == "min_sic3r_version" || key == "max_slic3r_version") { + if (! svalue.empty()) + semver = Semver::parse(key); + if (! semver) + throw file_parser_error(std::string(key) + " must referece a valid semantic version", path, idx_line); + if (key == "min_sic3r_version") + ver.min_slic3r_version = *semver; + else + ver.max_slic3r_version = *semver; + } else { + // Ignore unknown keys, as there may come new keys in the future. + } + } + if (! semver) + throw file_parser_error("Invalid semantic version", path, idx_line); + ver.config_version = *semver; + ver.comment = (end <= key_end) ? "" : unquote_version_comment(value, end, path, idx_line); + m_configs.emplace_back(ver); + } + + return m_configs.size(); +} + +Index::const_iterator Index::recommended() const +{ + int idx = -1; + const_iterator highest = m_configs.end(); + for (const_iterator it = this->begin(); it != this->end(); ++ it) + if (it->is_current_slic3r_supported() && + (highest == this->end() || highest->max_slic3r_version < it->max_slic3r_version)) + highest = it; + return highest; +} + +} // namespace Config +} // namespace GUI +} // namespace Slic3r diff --git a/xs/src/slic3r/Config/Version.hpp b/xs/src/slic3r/Config/Version.hpp new file mode 100644 index 000000000..7af1d4b5b --- /dev/null +++ b/xs/src/slic3r/Config/Version.hpp @@ -0,0 +1,75 @@ +#ifndef slic3r_GUI_ConfigIndex_ +#define slic3r_GUI_ConfigIndex_ + +#include +#include + +#include "../../libslic3r/FileParserError.hpp" +#include "../Utils/Semver.hpp" + +namespace Slic3r { +namespace GUI { +namespace Config { + +// Configuration bundle version. +struct Version +{ + // Version of this config. + Semver config_version = Semver::invalid(); + // Minimum Slic3r version, for which this config is applicable. + Semver min_slic3r_version = Semver::zero(); + // Maximum Slic3r version, for which this config is recommended. + // Slic3r should read older configuration and upgrade to a newer format, + // but likely there has been a better configuration published, using the new features. + Semver max_slic3r_version = Semver::inf(); + // Single comment line. + std::string comment; + + bool is_slic3r_supported(const Semver &slicer_version) const { return slicer_version.in_range(min_slic3r_version, max_slic3r_version); } + bool is_current_slic3r_supported() const; +}; + +// Index of vendor specific config bundle versions and Slic3r compatibilities. +// The index is being downloaded from the internet, also an initial version of the index +// is contained in the Slic3r installation. +// +// The index has a simple format: +// +// min_sic3r_version = +// max_slic3r_version = +// config_version "comment" +// config_version "comment" +// ... +// min_slic3r_version = +// max_slic3r_version = +// config_version comment +// config_version comment +// ... +// +// The min_slic3r_version, max_slic3r_version keys are applied to the config versions below, +// empty slic3r version means an open interval. +class Index +{ +public: + typedef std::vector::const_iterator const_iterator; + // Read a config index file in the simple format described in the Index class comment. + // Throws Slic3r::file_parser_error and the standard std file access exceptions. + size_t load(const std::string &path); + + const_iterator begin() const { return m_configs.begin(); } + const_iterator end() const { return m_configs.end(); } + const std::vector& configs() const { return m_configs; } + // Finds a recommended config to be installed for the current Slic3r version. + // Returns configs().end() if such version does not exist in the index. This shall never happen + // if the index is valid. + const_iterator recommended() const; + +private: + std::vector m_configs; +}; + +} // namespace Config +} // namespace GUI +} // namespace Slic3r + +#endif /* slic3r_GUI_ConfigIndex_ */ diff --git a/xs/src/slic3r/Utils/Semver.hpp b/xs/src/slic3r/Utils/Semver.hpp index 3606e90e4..7fc3b8033 100644 --- a/xs/src/slic3r/Utils/Semver.hpp +++ b/xs/src/slic3r/Utils/Semver.hpp @@ -28,6 +28,24 @@ public: } } + static const Semver zero() + { + static semver_t ver = { 0, 0, 0, nullptr, nullptr }; + return Semver(ver); + } + + static const Semver inf() + { + static semver_t ver = { std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max(), nullptr, nullptr }; + return Semver(ver); + } + + static const Semver invalid() + { + static semver_t ver = { -1, 0, 0, nullptr, nullptr }; + return Semver(ver); + } + Semver(Semver &&other) { *this = std::move(other); } Semver(const Semver &other) { *this = other; } @@ -36,13 +54,16 @@ public: ver = other.ver; other.ver.major = other.ver.minor = other.ver.patch = 0; other.ver.metadata = other.ver.prerelease = nullptr; + return *this; } Semver &operator=(const Semver &other) { + ::semver_free(&ver); ver = other.ver; if (other.ver.metadata != nullptr) { std::strcpy(ver.metadata, other.ver.metadata); } if (other.ver.prerelease != nullptr) { std::strcpy(ver.prerelease, other.ver.prerelease); } + return *this; } ~Semver() { ::semver_free(&ver); } @@ -55,8 +76,10 @@ public: bool operator>=(const Semver &b) const { return ::semver_compare(ver, b.ver) >= 0; } bool operator>(const Semver &b) const { return ::semver_compare(ver, b.ver) == 1; } // We're using '&' instead of the '~' operator here as '~' is unary-only: + // Satisfies patch if Major and minor are equal. bool operator&(const Semver &b) const { return ::semver_satisfies_patch(ver, b.ver); } bool operator^(const Semver &b) const { return ::semver_satisfies_caret(ver, b.ver); } + bool in_range(const Semver &low, const Semver &high) const { return low <= *this && *this <= high; } // Conversion std::string to_string() const { @@ -79,6 +102,7 @@ public: Semver operator-(const Major &b) const { Semver res(*this); return res -= b; } Semver operator-(const Minor &b) const { Semver res(*this); return res -= b; } Semver operator-(const Patch &b) const { Semver res(*this); return res -= b; } + private: semver_t ver; diff --git a/xs/src/slic3r/Utils/Time.cpp b/xs/src/slic3r/Utils/Time.cpp new file mode 100644 index 000000000..c4123c7bb --- /dev/null +++ b/xs/src/slic3r/Utils/Time.cpp @@ -0,0 +1,63 @@ +#include "Time.hpp" + +namespace Slic3r { +namespace Utils { + +time_t parse_time_ISO8601Z(const std::string &sdate) +{ + int y, M, d, h, m; + float s; + if (sscanf(sdate.c_str(), "%d-%d-%dT%d:%d:%fZ", &y, &M, &d, &h, &m, &s) != 6) + return (time_t)-1; + struct tm tms; + tms.tm_year = y - 1900; // Year since 1900 + tms.tm_mon = M - 1; // 0-11 + tms.tm_mday = d; // 1-31 + tms.tm_hour = h; // 0-23 + tms.tm_min = m; // 0-59 + tms.tm_sec = (int)s; // 0-61 (0-60 in C++11) + return mktime(&tms); +} + +std::string format_time_ISO8601Z(time_t time) +{ + struct tm tms; +#ifdef WIN32 + gmtime_s(time, &tms); +#else + gmtime_r(&tms, time); +#endif + char buf[128]; + sprintf(buf, "%d-%d-%dT%d:%d:%fZ", + tms.tm_year + 1900 + tms.tm_mon + 1 + tms.tm_mday + tms.tm_hour + tms.tm_min + tms.tm_sec); + return buf; +} + +time_t get_current_time_utc() +{ +#ifdef WIN32 + SYSTEMTIME st; + ::GetSystemTime(&st); + std::tm tm; + tm.tm_sec = st.wSecond; + tm.tm_min = st.wMinute; + tm.tm_hour = st.wHour; + tm.tm_mday = st.wDay; + tm.tm_mon = st.wMonth - 1; + tm.tm_year = st.wYear - 1900; + tm.tm_isdst = -1; + return mktime(&tm); +#else + return gmtime(); +#endif +} + +}; // namespace Utils +}; // namespace Slic3r + +#endif /* slic3r_Utils_Time_hpp_ */ diff --git a/xs/src/slic3r/Utils/Time.hpp b/xs/src/slic3r/Utils/Time.hpp new file mode 100644 index 000000000..6b2fbf893 --- /dev/null +++ b/xs/src/slic3r/Utils/Time.hpp @@ -0,0 +1,22 @@ +#ifndef slic3r_Utils_Time_hpp_ +#define slic3r_Utils_Time_hpp_ + +#include +#include + +namespace Slic3r { +namespace Utils { + +// Utilities to convert an UTC time_t to/from an ISO8601 time format, +// useful for putting timestamps into file and directory names. +// Returns (time_t)-1 on error. +extern time_t parse_time_ISO8601Z(const std::string &s); +extern std::string format_time_ISO8601Z(time_t time); + +// There is no gmtime() on windows. +time_t get_current_time_utc(); + +}; // namespace Utils +}; // namespace Slic3r + +#endif /* slic3r_Utils_Time_hpp_ */ From 388deb71ab5d03116ef5ec1266c39eea8fb51520 Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Mon, 9 Apr 2018 14:41:55 +0200 Subject: [PATCH 20/22] Adapt settings label colors to light vs dark UI themes --- xs/src/slic3r/GUI/GUI.cpp | 38 ++++++++++++++++++++++++++++++++++---- xs/src/slic3r/GUI/GUI.hpp | 6 ++++-- xs/src/slic3r/GUI/Tab.cpp | 14 +++++++------- 3 files changed, 45 insertions(+), 13 deletions(-) diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp index 3eca4e707..9109e8b5c 100644 --- a/xs/src/slic3r/GUI/GUI.cpp +++ b/xs/src/slic3r/GUI/GUI.cpp @@ -1,6 +1,7 @@ #include "GUI.hpp" #include +#include #include #include @@ -37,6 +38,7 @@ #include #include #include +#include #include "wxExtensions.hpp" @@ -174,6 +176,8 @@ wxFrame *g_wxMainFrame = nullptr; wxNotebook *g_wxTabPanel = nullptr; AppConfig *g_AppConfig = nullptr; PresetBundle *g_PresetBundle= nullptr; +wxColour g_color_label_modified; +wxColour g_color_label_sys; std::vector g_tabs_list; @@ -182,9 +186,22 @@ wxLocale* g_wxLocale; std::shared_ptr m_optgroup; double m_brim_width = 0.0; +static void init_label_colours() +{ + auto luma = get_colour_approx_luma(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); + if (luma >= 128) { + g_color_label_modified = wxColour(253, 88, 0); + g_color_label_sys = wxColour(26, 132, 57); + } else { + g_color_label_modified = wxColour(253, 111, 40); + g_color_label_sys = wxColour(115, 220, 103); + } +} + void set_wxapp(wxApp *app) { g_wxApp = app; + init_label_colours(); } void set_main_frame(wxFrame *main_frame) @@ -514,12 +531,25 @@ wxApp* get_app(){ return g_wxApp; } -wxColour* get_modified_label_clr(){ - return new wxColour(253, 88, 0); +const wxColour& get_modified_label_clr() { + return g_color_label_modified; } -wxColour* get_sys_label_clr(){ - return new wxColour(26, 132, 57); +const wxColour& get_sys_label_clr() { + return g_color_label_sys; +} + +unsigned get_colour_approx_luma(const wxColour &colour) +{ + double r = colour.Red(); + double g = colour.Green(); + double b = colour.Blue(); + + std::round(std::sqrt( + r * r * .241 + + g * g * .691 + + b * b * .068 + )); } void create_combochecklist(wxComboCtrl* comboCtrl, std::string text, std::string items, bool initial_value) diff --git a/xs/src/slic3r/GUI/GUI.hpp b/xs/src/slic3r/GUI/GUI.hpp index 362b15307..24c3ec3f4 100644 --- a/xs/src/slic3r/GUI/GUI.hpp +++ b/xs/src/slic3r/GUI/GUI.hpp @@ -79,8 +79,10 @@ void set_preset_bundle(PresetBundle *preset_bundle); AppConfig* get_app_config(); wxApp* get_app(); -wxColour* get_modified_label_clr(); -wxColour* get_sys_label_clr(); + +const wxColour& get_modified_label_clr(); +const wxColour& get_sys_label_clr(); +unsigned get_colour_approx_luma(const wxColour &colour); void add_debug_menu(wxMenuBar *menu, int event_language_change); diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp index babff7d80..7db35f5cf 100644 --- a/xs/src/slic3r/GUI/Tab.cpp +++ b/xs/src/slic3r/GUI/Tab.cpp @@ -265,14 +265,14 @@ void Tab::update_changed_ui() bool is_modified_value = true; std::string sys_icon = wxMSW ? "sys_lock.png" : "lock.png"; std::string icon = wxMSW ? "action_undo.png" : "arrow_undo.png"; - wxColour& color = *get_sys_label_clr(); + wxColour color = get_sys_label_clr(); if (find(m_sys_options.begin(), m_sys_options.end(), opt_key) == m_sys_options.end()) { is_nonsys_value = true; sys_icon = m_nonsys_btn_icon; if(find(m_dirty_options.begin(), m_dirty_options.end(), opt_key) == m_dirty_options.end()) - color = wxSYS_COLOUR_WINDOWTEXT; + color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); else - color = *get_modified_label_clr(); + color = get_modified_label_clr(); } if (find(m_dirty_options.begin(), m_dirty_options.end(), opt_key) == m_dirty_options.end()) { @@ -343,7 +343,7 @@ void Tab::update_sys_ui_after_sel_preset() field->m_Undo_to_sys_btn->SetBitmap(wxBitmap(from_u8(var(m_nonsys_btn_icon)), wxBITMAP_TYPE_PNG)); field->m_is_nonsys_value = true; if (field->m_Label != nullptr){ - field->m_Label->SetForegroundColour(wxSYS_COLOUR_WINDOWTEXT); + field->m_Label->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); field->m_Label->Refresh(true); } } @@ -386,11 +386,11 @@ void Tab::update_changed_tree_ui() break; } if (sys_page) - m_treectrl->SetItemTextColour(cur_item, *get_sys_label_clr()); + m_treectrl->SetItemTextColour(cur_item, get_sys_label_clr()); else if (modified_page) - m_treectrl->SetItemTextColour(cur_item, *get_modified_label_clr()); + m_treectrl->SetItemTextColour(cur_item, get_modified_label_clr()); else - m_treectrl->SetItemTextColour(cur_item, wxSYS_COLOUR_WINDOWTEXT); + m_treectrl->SetItemTextColour(cur_item, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); page->m_is_nonsys_values = !sys_page; page->m_is_modified_values = modified_page; From a8e831e04a433ac41c95937d6e3843e9e51ee881 Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Mon, 9 Apr 2018 14:52:57 +0200 Subject: [PATCH 21/22] rm stray file --- My Settings | 27 --------------------------- 1 file changed, 27 deletions(-) delete mode 100644 My Settings diff --git a/My Settings b/My Settings deleted file mode 100644 index f896db767..000000000 --- a/My Settings +++ /dev/null @@ -1,27 +0,0 @@ -# generated by Slic3r Prusa Edition 1.38.4 on 2017-12-20 at 11:02:03 -bed_temperature = 0 -bridge_fan_speed = 100 -compatible_printers = -compatible_printers_condition = -cooling = 1 -disable_fan_first_layers = 3 -end_filament_gcode = "; Filament-specific end gcode \n;END gcode for filament\n" -extrusion_multiplier = 1 -fan_always_on = 0 -fan_below_layer_time = 60 -filament_colour = #29b2b2 -filament_cost = 0 -filament_density = 0 -filament_diameter = 3 -filament_max_volumetric_speed = 0 -filament_notes = "" -filament_soluble = 0 -filament_type = PLA -first_layer_bed_temperature = 0 -first_layer_temperature = 205 -max_fan_speed = 100 -min_fan_speed = 35 -min_print_speed = 10 -slowdown_below_layer_time = 5 -start_filament_gcode = "; Filament gcode\n" -temperature = 200 From a541f5dfe1d3b7b0dc51e38d09b3883a3a14aa12 Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Tue, 10 Apr 2018 11:43:04 +0200 Subject: [PATCH 22/22] Fix of the fix --- xs/src/slic3r/GUI/GUI.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp index 9109e8b5c..9450f44ae 100644 --- a/xs/src/slic3r/GUI/GUI.cpp +++ b/xs/src/slic3r/GUI/GUI.cpp @@ -545,7 +545,7 @@ unsigned get_colour_approx_luma(const wxColour &colour) double g = colour.Green(); double b = colour.Blue(); - std::round(std::sqrt( + return std::round(std::sqrt( r * r * .241 + g * g * .691 + b * b * .068