From 19062b4d5f6c32f40a5b308671f6c8f69eb96cb0 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 13 Jul 2023 11:54:42 +0200 Subject: [PATCH 001/136] ArcWelder path interpolation based on the work by Brad Hochgesang @FormerLurker. WIP GCode/SmoothPath.cpp,hpp cache for interpolating extrusion path with arches. Removed Perl test t/geometry.t, replaced with C++ tests. Refactored ExtrusionEntity and derived classes to hold extrusion attributes in new ExtrusionFlow/ExtrusionAttributes classes. Reworked path ordering in G-code export to never copy polylines, but to work with a new "flipped" attribute. Reworked G-code export to interpolate extrusion paths with smooth paths and to extrude those smooth paths. New parameters: arc_fitting, arc_fitting_tolerance Renamed GCode class to GCodeGenerator Moved GCodeWriter.cpp/hpp to GCode/ Moved Wipe from from GCode.cpp,hpp to GCode/Wipe.cpp,hpp Moved WipeTowerIntegration from GCode.cpp,hpp to GCode/WipeTowerIntegration.cpp,hpp New variant of douglas_peucker() to simplify range of iterators in place. Refactored wipe in general and wipe on perimeters / hiding seams. WIP: Convert estimate_speed_from_extrusion_quality() and its application to smooth paths. WIP: Cooling buffer to process G2G3, disable arc fitting for filters that cannot process it. --- lib/Slic3r/Geometry.pm | 1 - src/libslic3r/Brim.cpp | 18 +- src/libslic3r/CMakeLists.txt | 12 +- src/libslic3r/CustomGCode.cpp | 2 +- src/libslic3r/Extruder.cpp | 2 +- src/libslic3r/ExtrusionEntity.cpp | 54 +- src/libslic3r/ExtrusionEntity.hpp | 211 ++-- src/libslic3r/ExtrusionEntityCollection.cpp | 14 +- src/libslic3r/ExtrusionEntityCollection.hpp | 5 +- src/libslic3r/ExtrusionSimulator.cpp | 4 +- src/libslic3r/Fill/Fill.cpp | 10 +- src/libslic3r/GCode.cpp | 933 ++++++------------ src/libslic3r/GCode.hpp | 126 +-- .../GCode/AvoidCrossingPerimeters.cpp | 6 +- .../GCode/AvoidCrossingPerimeters.hpp | 6 +- src/libslic3r/GCode/ConflictChecker.hpp | 2 +- src/libslic3r/GCode/CoolingBuffer.cpp | 2 +- src/libslic3r/GCode/CoolingBuffer.hpp | 6 +- src/libslic3r/GCode/ExtrusionProcessor.hpp | 19 +- src/libslic3r/GCode/GCodeProcessor.cpp | 2 +- src/libslic3r/{ => GCode}/GCodeWriter.cpp | 104 +- src/libslic3r/{ => GCode}/GCodeWriter.hpp | 66 +- src/libslic3r/GCode/PrintExtents.cpp | 6 +- src/libslic3r/GCode/SeamPlacer.cpp | 13 +- src/libslic3r/GCode/SeamPlacer.hpp | 2 +- src/libslic3r/GCode/SmoothPath.cpp | 258 +++++ src/libslic3r/GCode/SmoothPath.hpp | 70 ++ src/libslic3r/GCode/Wipe.cpp | 218 ++++ src/libslic3r/GCode/Wipe.hpp | 72 ++ src/libslic3r/GCode/WipeTower.hpp | 6 +- src/libslic3r/GCode/WipeTowerIntegration.cpp | 247 +++++ src/libslic3r/GCode/WipeTowerIntegration.hpp | 65 ++ src/libslic3r/Geometry/ArcWelder.cpp | 543 ++++++++++ src/libslic3r/Geometry/ArcWelder.hpp | 196 ++++ src/libslic3r/Geometry/Circle.hpp | 2 +- src/libslic3r/Model.cpp | 2 +- src/libslic3r/MultiPoint.cpp | 96 +- src/libslic3r/MultiPoint.hpp | 133 ++- src/libslic3r/PerimeterGenerator.cpp | 73 +- src/libslic3r/Point.cpp | 14 +- src/libslic3r/Point.hpp | 5 +- src/libslic3r/Polyline.cpp | 2 + src/libslic3r/Print.cpp | 20 +- src/libslic3r/Print.hpp | 4 +- src/libslic3r/PrintConfig.cpp | 16 + src/libslic3r/PrintConfig.hpp | 2 + src/libslic3r/ShortestPath.cpp | 39 +- src/libslic3r/ShortestPath.hpp | 14 +- src/libslic3r/Support/SupportCommon.cpp | 37 +- src/libslic3r/Support/SupportMaterial.cpp | 2 +- src/slic3r/GUI/3DScene.cpp | 12 +- src/slic3r/GUI/Tab.cpp | 2 +- t/geometry.t | 95 -- tests/fff_print/test_cooling.cpp | 16 +- tests/fff_print/test_extrusion_entity.cpp | 44 +- tests/fff_print/test_gcode.cpp | 2 +- tests/fff_print/test_gcodewriter.cpp | 2 +- tests/libslic3r/CMakeLists.txt | 1 + tests/libslic3r/test_arc_welder.cpp | 91 ++ tests/libslic3r/test_geometry.cpp | 21 +- tests/libslic3r/test_polyline.cpp | 19 + xs/src/perlglue.cpp | 2 +- xs/xsp/Geometry.xsp | 9 - xs/xsp/Polygon.xsp | 1 - 64 files changed, 2829 insertions(+), 1250 deletions(-) rename src/libslic3r/{ => GCode}/GCodeWriter.cpp (89%) rename src/libslic3r/{ => GCode}/GCodeWriter.hpp (81%) create mode 100644 src/libslic3r/GCode/SmoothPath.cpp create mode 100644 src/libslic3r/GCode/SmoothPath.hpp create mode 100644 src/libslic3r/GCode/Wipe.cpp create mode 100644 src/libslic3r/GCode/Wipe.hpp create mode 100644 src/libslic3r/GCode/WipeTowerIntegration.cpp create mode 100644 src/libslic3r/GCode/WipeTowerIntegration.hpp create mode 100644 src/libslic3r/Geometry/ArcWelder.cpp create mode 100644 src/libslic3r/Geometry/ArcWelder.hpp delete mode 100644 t/geometry.t create mode 100644 tests/libslic3r/test_arc_welder.cpp diff --git a/lib/Slic3r/Geometry.pm b/lib/Slic3r/Geometry.pm index 6a2161d288..ae0dc85a20 100644 --- a/lib/Slic3r/Geometry.pm +++ b/lib/Slic3r/Geometry.pm @@ -15,7 +15,6 @@ our @EXPORT_OK = qw( X Y Z convex_hull - chained_path_from deg2rad rad2deg ); diff --git a/src/libslic3r/Brim.cpp b/src/libslic3r/Brim.cpp index bdc1a19c27..672bb80726 100644 --- a/src/libslic3r/Brim.cpp +++ b/src/libslic3r/Brim.cpp @@ -490,8 +490,10 @@ static void make_inner_brim(const Print &print, loops = union_pt_chained_outside_in(loops); std::reverse(loops.begin(), loops.end()); - extrusion_entities_append_loops(brim.entities, std::move(loops), ExtrusionRole::Skirt, float(flow.mm3_per_mm()), - float(flow.width()), float(print.skirt_first_layer_height())); + extrusion_entities_append_loops(brim.entities, std::move(loops), + ExtrusionAttributes{ + ExtrusionRole::Skirt, + ExtrusionFlow{ float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height()) } }); } // Produce brim lines around those objects, that have the brim enabled. @@ -672,7 +674,9 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance if (i + 1 == j && first_path.size() > 3 && first_path.front().x() == first_path.back().x() && first_path.front().y() == first_path.back().y()) { auto *loop = new ExtrusionLoop(); brim.entities.emplace_back(loop); - loop->paths.emplace_back(ExtrusionRole::Skirt, float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height())); + loop->paths.emplace_back(ExtrusionAttributes{ + ExtrusionRole::Skirt, + ExtrusionFlow{ float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height()) } }); Points &points = loop->paths.front().polyline.points; points.reserve(first_path.size()); for (const ClipperLib_Z::IntPoint &pt : first_path) @@ -683,7 +687,9 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance ExtrusionEntityCollection this_loop_trimmed; this_loop_trimmed.entities.reserve(j - i); for (; i < j; ++ i) { - this_loop_trimmed.entities.emplace_back(new ExtrusionPath(ExtrusionRole::Skirt, float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height()))); + this_loop_trimmed.entities.emplace_back(new ExtrusionPath({ + ExtrusionRole::Skirt, + ExtrusionFlow{ float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height()) } })); const ClipperLib_Z::Path &path = *loops_trimmed_order[i].first; Points &points = dynamic_cast(this_loop_trimmed.entities.back())->polyline.points; points.reserve(path.size()); @@ -699,7 +705,9 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance } } } else { - extrusion_entities_append_loops_and_paths(brim.entities, std::move(all_loops), ExtrusionRole::Skirt, float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height())); + extrusion_entities_append_loops_and_paths(brim.entities, std::move(all_loops), + ExtrusionAttributes{ ExtrusionRole::Skirt, + ExtrusionFlow{ float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height()) } }); } make_inner_brim(print, top_level_objects_with_brim, bottom_layers_expolygons, brim); diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 0de0b4e517..10aa7bd38a 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -144,6 +144,8 @@ set(SLIC3R_SOURCES GCode/CoolingBuffer.hpp GCode/FindReplace.cpp GCode/FindReplace.hpp + GCode/GCodeWriter.cpp + GCode/GCodeWriter.hpp GCode/PostProcessor.cpp GCode/PostProcessor.hpp GCode/PressureEqualizer.cpp @@ -156,10 +158,16 @@ set(SLIC3R_SOURCES GCode/SpiralVase.hpp GCode/SeamPlacer.cpp GCode/SeamPlacer.hpp + GCode/SmoothPath.cpp + GCode/SmoothPath.hpp GCode/ToolOrdering.cpp GCode/ToolOrdering.hpp + GCode/Wipe.cpp + GCode/Wipe.hpp GCode/WipeTower.cpp GCode/WipeTower.hpp + GCode/WipeTowerIntegration.cpp + GCode/WipeTowerIntegration.hpp GCode/GCodeProcessor.cpp GCode/GCodeProcessor.hpp GCode/AvoidCrossingPerimeters.cpp @@ -170,10 +178,10 @@ set(SLIC3R_SOURCES GCodeReader.hpp # GCodeSender.cpp # GCodeSender.hpp - GCodeWriter.cpp - GCodeWriter.hpp Geometry.cpp Geometry.hpp + Geometry/ArcWelder.cpp + Geometry/ArcWelder.hpp Geometry/Bicubic.hpp Geometry/Circle.cpp Geometry/Circle.hpp diff --git a/src/libslic3r/CustomGCode.cpp b/src/libslic3r/CustomGCode.cpp index 213437782f..5aebf8ef8e 100644 --- a/src/libslic3r/CustomGCode.cpp +++ b/src/libslic3r/CustomGCode.cpp @@ -1,7 +1,7 @@ #include "CustomGCode.hpp" #include "Config.hpp" #include "GCode.hpp" -#include "GCodeWriter.hpp" +#include "GCode/GCodeWriter.hpp" namespace Slic3r { diff --git a/src/libslic3r/Extruder.cpp b/src/libslic3r/Extruder.cpp index b1a089d085..ea5a3b803b 100644 --- a/src/libslic3r/Extruder.cpp +++ b/src/libslic3r/Extruder.cpp @@ -1,5 +1,5 @@ #include "Extruder.hpp" -#include "GCodeWriter.hpp" +#include "GCode/GCodeWriter.hpp" #include "PrintConfig.hpp" namespace Slic3r { diff --git a/src/libslic3r/ExtrusionEntity.cpp b/src/libslic3r/ExtrusionEntity.cpp index 8a1a88b4b1..f66e8a0e43 100644 --- a/src/libslic3r/ExtrusionEntity.cpp +++ b/src/libslic3r/ExtrusionEntity.cpp @@ -2,6 +2,7 @@ #include "ExtrusionEntityCollection.hpp" #include "ExPolygon.hpp" #include "ClipperUtils.hpp" +#include "Exception.hpp" #include "Extruder.hpp" #include "Flow.hpp" #include @@ -9,7 +10,7 @@ #include namespace Slic3r { - + void ExtrusionPath::intersect_expolygons(const ExPolygons &collection, ExtrusionEntityCollection* retval) const { this->_inflate_collection(intersection_pl(Polylines{ polyline }, collection), retval); @@ -38,12 +39,12 @@ double ExtrusionPath::length() const void ExtrusionPath::_inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const { for (const Polyline &polyline : polylines) - collection->entities.emplace_back(new ExtrusionPath(polyline, *this)); + collection->entities.emplace_back(new ExtrusionPath(polyline, this->attributes())); } void ExtrusionPath::polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const { - polygons_append(out, offset(this->polyline, float(scale_(this->width/2)) + scaled_epsilon)); + polygons_append(out, offset(this->polyline, float(scale_(m_attributes.width/2)) + scaled_epsilon)); } void ExtrusionPath::polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const @@ -51,8 +52,8 @@ void ExtrusionPath::polygons_covered_by_spacing(Polygons &out, const float scale // Instantiating the Flow class to get the line spacing. // Don't know the nozzle diameter, setting to zero. It shall not matter it shall be optimized out by the compiler. bool bridge = this->role().is_bridge(); - assert(! bridge || this->width == this->height); - auto flow = bridge ? Flow::bridging_flow(this->width, 0.f) : Flow(this->width, this->height, 0.f); + assert(! bridge || m_attributes.width == m_attributes.height); + auto flow = bridge ? Flow::bridging_flow(m_attributes.width, 0.f) : Flow(m_attributes.width, m_attributes.height, 0.f); polygons_append(out, offset(this->polyline, 0.5f * float(flow.scaled_spacing()) + scaled_epsilon)); } @@ -87,7 +88,7 @@ double ExtrusionMultiPath::min_mm3_per_mm() const { double min_mm3_per_mm = std::numeric_limits::max(); for (const ExtrusionPath &path : this->paths) - min_mm3_per_mm = std::min(min_mm3_per_mm, path.mm3_per_mm); + min_mm3_per_mm = std::min(min_mm3_per_mm, path.min_mm3_per_mm()); return min_mm3_per_mm; } @@ -112,21 +113,34 @@ Polyline ExtrusionMultiPath::as_polyline() const return out; } -bool ExtrusionLoop::make_clockwise() +double ExtrusionLoop::area() const { - bool was_ccw = this->polygon().is_counter_clockwise(); - if (was_ccw) this->reverse(); - return was_ccw; -} - -bool ExtrusionLoop::make_counter_clockwise() -{ - bool was_cw = this->polygon().is_clockwise(); - if (was_cw) this->reverse(); - return was_cw; + double a = 0; + for (const ExtrusionPath &path : this->paths) { + assert(path.size() >= 2); + if (path.size() >= 2) { + // Assumming that the last point of one path segment is repeated at the start of the following path segment. + auto it = path.polyline.points.begin(); + Point prev = *it ++; + for (; it != path.polyline.points.end(); ++ it) { + a += cross2(prev.cast(), it->cast()); + prev = *it; + } + } + } + return a * 0.5; } void ExtrusionLoop::reverse() +{ +#if 0 + this->reverse_loop(); +#else + throw Slic3r::LogicError("ExtrusionLoop::reverse() must NOT be called"); +#endif +} + +void ExtrusionLoop::reverse_loop() { for (ExtrusionPath &path : this->paths) path.reverse(); @@ -248,8 +262,8 @@ void ExtrusionLoop::split_at(const Point &point, bool prefer_non_overhang, const // now split path_idx in two parts const ExtrusionPath &path = this->paths[path_idx]; - ExtrusionPath p1(path.role(), path.mm3_per_mm, path.width, path.height); - ExtrusionPath p2(path.role(), path.mm3_per_mm, path.width, path.height); + ExtrusionPath p1(path.attributes()); + ExtrusionPath p2(path.attributes()); path.polyline.split_at(p, &p1.polyline, &p2.polyline); if (this->paths.size() == 1) { @@ -316,7 +330,7 @@ double ExtrusionLoop::min_mm3_per_mm() const { double min_mm3_per_mm = std::numeric_limits::max(); for (const ExtrusionPath &path : this->paths) - min_mm3_per_mm = std::min(min_mm3_per_mm, path.mm3_per_mm); + min_mm3_per_mm = std::min(min_mm3_per_mm, path.min_mm3_per_mm()); return min_mm3_per_mm; } diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp index 277ac78242..1553d40b6e 100644 --- a/src/libslic3r/ExtrusionEntity.hpp +++ b/src/libslic3r/ExtrusionEntity.hpp @@ -3,6 +3,7 @@ #include "libslic3r.h" #include "ExtrusionRole.hpp" +#include "Flow.hpp" #include "Polygon.hpp" #include "Polyline.hpp" @@ -55,28 +56,80 @@ public: virtual double total_volume() const = 0; }; -typedef std::vector ExtrusionEntitiesPtr; +using ExtrusionEntitiesPtr = std::vector; + +// Const reference for ordering extrusion entities without having to modify them. +class ExtrusionEntityReference final +{ +public: + ExtrusionEntityReference() = delete; + ExtrusionEntityReference(const ExtrusionEntity &extrusion_entity, bool flipped) : m_extrusion_entity(&extrusion_entity), m_flipped(flipped) {} + ExtrusionEntityReference operator=(const ExtrusionEntityReference &rhs) { m_extrusion_entity = rhs.m_extrusion_entity; m_flipped = rhs.m_flipped; } + + const ExtrusionEntity& extrusion_entity() const { return *m_extrusion_entity; } + template + const Type* cast() const { return dynamic_cast(m_extrusion_entity); } + bool flipped() const { return m_flipped; } + +private: + const ExtrusionEntity *m_extrusion_entity; + bool m_flipped; +}; + +using ExtrusionEntityReferences = std::vector; + +struct ExtrusionFlow +{ + ExtrusionFlow() = default; + ExtrusionFlow(double mm3_per_mm, float width, float height) : + mm3_per_mm{ mm3_per_mm }, width{ width }, height{ height } {} + ExtrusionFlow(const Flow &flow) : + mm3_per_mm(flow.mm3_per_mm()), width(flow.width()), height(flow.height()) {} + + // Volumetric velocity. mm^3 of plastic per mm of linear head motion. Used by the G-code generator. + double mm3_per_mm{ -1. }; + // Width of the extrusion, used for visualization purposes. + float width{ -1.f }; + // Height of the extrusion, used for visualization purposes. + float height{ -1.f }; +}; + +inline bool operator==(const ExtrusionFlow &lhs, const ExtrusionFlow &rhs) +{ + return lhs.mm3_per_mm == rhs.mm3_per_mm && lhs.width == rhs.width && lhs.height == rhs.height; +} + +struct ExtrusionAttributes : ExtrusionFlow +{ + ExtrusionAttributes() = default; + ExtrusionAttributes(ExtrusionRole role) : role{ role } {} + ExtrusionAttributes(ExtrusionRole role, const Flow &flow) : role{ role }, ExtrusionFlow{ flow } {} + ExtrusionAttributes(ExtrusionRole role, const ExtrusionFlow &flow) : role{ role }, ExtrusionFlow{ flow } {} + + // What is the role / purpose of this extrusion? + ExtrusionRole role{ ExtrusionRole::None }; +}; + +inline bool operator==(const ExtrusionAttributes &lhs, const ExtrusionAttributes &rhs) +{ + return static_cast(lhs) == static_cast(rhs) && + lhs.role == rhs.role; +} class ExtrusionPath : public ExtrusionEntity { public: Polyline polyline; - // Volumetric velocity. mm^3 of plastic per mm of linear head motion. Used by the G-code generator. - double mm3_per_mm; - // Width of the extrusion, used for visualization purposes. - float width; - // Height of the extrusion, used for visualization purposes. - float height; - ExtrusionPath(ExtrusionRole role) : mm3_per_mm(-1), width(-1), height(-1), m_role(role) {} - ExtrusionPath(ExtrusionRole role, double mm3_per_mm, float width, float height) : mm3_per_mm(mm3_per_mm), width(width), height(height), m_role(role) {} - ExtrusionPath(const ExtrusionPath& rhs) : polyline(rhs.polyline), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), m_role(rhs.m_role) {} - ExtrusionPath(ExtrusionPath&& rhs) : polyline(std::move(rhs.polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), m_role(rhs.m_role) {} - ExtrusionPath(const Polyline &polyline, const ExtrusionPath &rhs) : polyline(polyline), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), m_role(rhs.m_role) {} - ExtrusionPath(Polyline &&polyline, const ExtrusionPath &rhs) : polyline(std::move(polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), m_role(rhs.m_role) {} + ExtrusionPath(ExtrusionRole role) : m_attributes{ role } {} + ExtrusionPath(const ExtrusionAttributes &attributes) : m_attributes(attributes) {} + ExtrusionPath(const ExtrusionPath &rhs) : polyline(rhs.polyline), m_attributes(rhs.m_attributes) {} + ExtrusionPath(ExtrusionPath &&rhs) : polyline(std::move(rhs.polyline)), m_attributes(rhs.m_attributes) {} + ExtrusionPath(const Polyline &polyline, const ExtrusionAttributes &attribs) : polyline(polyline), m_attributes(attribs) {} + ExtrusionPath(Polyline &&polyline, const ExtrusionAttributes &attribs) : polyline(std::move(polyline)), m_attributes(attribs) {} - ExtrusionPath& operator=(const ExtrusionPath& rhs) { m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->polyline = rhs.polyline; return *this; } - ExtrusionPath& operator=(ExtrusionPath&& rhs) { m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->polyline = std::move(rhs.polyline); return *this; } + ExtrusionPath& operator=(const ExtrusionPath &rhs) { this->polyline = rhs.polyline; m_attributes = rhs.m_attributes; return *this; } + ExtrusionPath& operator=(ExtrusionPath &&rhs) { this->polyline = std::move(rhs.polyline); m_attributes = rhs.m_attributes; return *this; } ExtrusionEntity* clone() const override { return new ExtrusionPath(*this); } // Create a new object, initialize it with this object using the move semantics. @@ -97,35 +150,45 @@ public: void clip_end(double distance); void simplify(double tolerance); double length() const override; - ExtrusionRole role() const override { return m_role; } + + const ExtrusionAttributes& attributes() const { return m_attributes; } + ExtrusionRole role() const override { return m_attributes.role; } + float width() const { return m_attributes.width; } + float height() const { return m_attributes.height; } + double mm3_per_mm() const { return m_attributes.mm3_per_mm; } + // Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm. + double min_mm3_per_mm() const override { return m_attributes.mm3_per_mm; } + // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width. // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps. - void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const override; + void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const override; // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion spacing. // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps. // Useful to calculate area of an infill, which has been really filled in by a 100% rectilinear infill. - void polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const override; - Polygons polygons_covered_by_width(const float scaled_epsilon = 0.f) const + void polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const override; + Polygons polygons_covered_by_width(const float scaled_epsilon = 0.f) const { Polygons out; this->polygons_covered_by_width(out, scaled_epsilon); return out; } - Polygons polygons_covered_by_spacing(const float scaled_epsilon = 0.f) const + Polygons polygons_covered_by_spacing(const float scaled_epsilon = 0.f) const { Polygons out; this->polygons_covered_by_spacing(out, scaled_epsilon); return out; } - // Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm. - double min_mm3_per_mm() const override { return this->mm3_per_mm; } - Polyline as_polyline() const override { return this->polyline; } - void collect_polylines(Polylines &dst) const override { if (! this->polyline.empty()) dst.emplace_back(this->polyline); } - void collect_points(Points &dst) const override { append(dst, this->polyline.points); } - double total_volume() const override { return mm3_per_mm * unscale(length()); } + + Polyline as_polyline() const override { return this->polyline; } + void collect_polylines(Polylines &dst) const override { if (! this->polyline.empty()) dst.emplace_back(this->polyline); } + void collect_points(Points &dst) const override { append(dst, this->polyline.points); } + double total_volume() const override { return m_attributes.mm3_per_mm * unscale(length()); } private: - void _inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const; + void _inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const; - ExtrusionRole m_role; + ExtrusionAttributes m_attributes; }; class ExtrusionPathOriented : public ExtrusionPath { public: - ExtrusionPathOriented(ExtrusionRole role, double mm3_per_mm, float width, float height) : ExtrusionPath(role, mm3_per_mm, width, height) {} + ExtrusionPathOriented(const ExtrusionAttributes &attribs) : ExtrusionPath(attribs) {} + ExtrusionPathOriented(const Polyline &polyline, const ExtrusionAttributes &attribs) : ExtrusionPath(polyline, attribs) {} + ExtrusionPathOriented(Polyline &&polyline, const ExtrusionAttributes &attribs) : ExtrusionPath(std::move(polyline), attribs) {} + ExtrusionEntity* clone() const override { return new ExtrusionPathOriented(*this); } // Create a new object, initialize it with this object using the move semantics. ExtrusionEntity* clone_move() override { return new ExtrusionPathOriented(std::move(*this)); } @@ -192,7 +255,8 @@ class ExtrusionLoop : public ExtrusionEntity public: ExtrusionPaths paths; - ExtrusionLoop(ExtrusionLoopRole role = elrDefault) : m_loop_role(role) {} + ExtrusionLoop() = default; + ExtrusionLoop(ExtrusionLoopRole role) : m_loop_role(role) {} ExtrusionLoop(const ExtrusionPaths &paths, ExtrusionLoopRole role = elrDefault) : paths(paths), m_loop_role(role) {} ExtrusionLoop(ExtrusionPaths &&paths, ExtrusionLoopRole role = elrDefault) : paths(std::move(paths)), m_loop_role(role) {} ExtrusionLoop(const ExtrusionPath &path, ExtrusionLoopRole role = elrDefault) : m_loop_role(role) @@ -204,16 +268,21 @@ public: ExtrusionEntity* clone() const override{ return new ExtrusionLoop (*this); } // Create a new object, initialize it with this object using the move semantics. ExtrusionEntity* clone_move() override { return new ExtrusionLoop(std::move(*this)); } - bool make_clockwise(); - bool make_counter_clockwise(); - void reverse() override; - const Point& first_point() const override { return this->paths.front().polyline.points.front(); } - const Point& last_point() const override { assert(this->first_point() == this->paths.back().polyline.points.back()); return this->first_point(); } - const Point& middle_point() const override { auto& path = this->paths[this->paths.size() / 2]; return path.polyline.points[path.polyline.size() / 2]; } - Polygon polygon() const; - double length() const override; - bool split_at_vertex(const Point &point, const double scaled_epsilon = scaled(0.001)); - void split_at(const Point &point, bool prefer_non_overhang, const double scaled_epsilon = scaled(0.001)); + double area() const; + bool is_counter_clockwise() const { return this->area() > 0; } + bool is_clockwise() const { return this->area() < 0; } + // Reverse shall never be called on ExtrusionLoop using a virtual function call, it is most likely never what one wants, + // as this->can_reverse() returns false for an ExtrusionLoop. + void reverse() override; + // Used by PerimeterGenerator to reorient extrusion loops. + void reverse_loop(); + const Point& first_point() const override { return this->paths.front().polyline.points.front(); } + const Point& last_point() const override { assert(this->first_point() == this->paths.back().polyline.points.back()); return this->first_point(); } + const Point& middle_point() const override { auto& path = this->paths[this->paths.size() / 2]; return path.polyline.points[path.polyline.size() / 2]; } + Polygon polygon() const; + double length() const override; + bool split_at_vertex(const Point &point, const double scaled_epsilon = scaled(0.001)); + void split_at(const Point &point, bool prefer_non_overhang, const double scaled_epsilon = scaled(0.001)); struct ClosestPathPoint { size_t path_idx; size_t segment_idx; @@ -259,59 +328,51 @@ public: #endif /* NDEBUG */ private: - ExtrusionLoopRole m_loop_role; + ExtrusionLoopRole m_loop_role{ elrDefault }; }; -inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &polylines, ExtrusionRole role, double mm3_per_mm, float width, float height) +inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &polylines, const ExtrusionAttributes &attributes) { dst.reserve(dst.size() + polylines.size()); for (Polyline &polyline : polylines) - if (polyline.is_valid()) { - dst.push_back(ExtrusionPath(role, mm3_per_mm, width, height)); - dst.back().polyline = polyline; - } + if (polyline.is_valid()) + dst.emplace_back(polyline, attributes); } -inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &&polylines, ExtrusionRole role, double mm3_per_mm, float width, float height) +inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &&polylines, const ExtrusionAttributes &attributes) { dst.reserve(dst.size() + polylines.size()); for (Polyline &polyline : polylines) - if (polyline.is_valid()) { - dst.push_back(ExtrusionPath(role, mm3_per_mm, width, height)); - dst.back().polyline = std::move(polyline); - } + if (polyline.is_valid()) + dst.emplace_back(std::move(polyline), attributes); polylines.clear(); } -inline void extrusion_entities_append_paths(ExtrusionEntitiesPtr &dst, const Polylines &polylines, ExtrusionRole role, double mm3_per_mm, float width, float height, bool can_reverse = true) +inline void extrusion_entities_append_paths(ExtrusionEntitiesPtr &dst, const Polylines &polylines, const ExtrusionAttributes &attributes, bool can_reverse = true) { dst.reserve(dst.size() + polylines.size()); for (const Polyline &polyline : polylines) - if (polyline.is_valid()) { - ExtrusionPath* extrusion_path = can_reverse ? new ExtrusionPath(role, mm3_per_mm, width, height) : new ExtrusionPathOriented(role, mm3_per_mm, width, height); - dst.push_back(extrusion_path); - extrusion_path->polyline = polyline; - } + if (polyline.is_valid()) + dst.emplace_back(can_reverse ? new ExtrusionPath(polyline, attributes) : new ExtrusionPathOriented(polyline, attributes)); } -inline void extrusion_entities_append_paths(ExtrusionEntitiesPtr &dst, Polylines &&polylines, ExtrusionRole role, double mm3_per_mm, float width, float height, bool can_reverse = true) +inline void extrusion_entities_append_paths(ExtrusionEntitiesPtr &dst, Polylines &&polylines, const ExtrusionAttributes &attributes, bool can_reverse = true) { dst.reserve(dst.size() + polylines.size()); for (Polyline &polyline : polylines) - if (polyline.is_valid()) { - ExtrusionPath *extrusion_path = can_reverse ? new ExtrusionPath(role, mm3_per_mm, width, height) : new ExtrusionPathOriented(role, mm3_per_mm, width, height); - dst.push_back(extrusion_path); - extrusion_path->polyline = std::move(polyline); - } + if (polyline.is_valid()) + dst.emplace_back(can_reverse ? + new ExtrusionPath(std::move(polyline), attributes) : + new ExtrusionPathOriented(std::move(polyline), attributes)); polylines.clear(); } -inline void extrusion_entities_append_loops(ExtrusionEntitiesPtr &dst, Polygons &&loops, ExtrusionRole role, double mm3_per_mm, float width, float height) +inline void extrusion_entities_append_loops(ExtrusionEntitiesPtr &dst, Polygons &&loops, const ExtrusionAttributes &attributes) { dst.reserve(dst.size() + loops.size()); for (Polygon &poly : loops) { if (poly.is_valid()) { - ExtrusionPath path(role, mm3_per_mm, width, height); + ExtrusionPath path(attributes); path.polyline.points = std::move(poly.points); path.polyline.points.push_back(path.polyline.points.front()); dst.emplace_back(new ExtrusionLoop(std::move(path))); @@ -320,22 +381,14 @@ inline void extrusion_entities_append_loops(ExtrusionEntitiesPtr &dst, Polygons loops.clear(); } -inline void extrusion_entities_append_loops_and_paths(ExtrusionEntitiesPtr &dst, Polylines &&polylines, ExtrusionRole role, double mm3_per_mm, float width, float height) +inline void extrusion_entities_append_loops_and_paths(ExtrusionEntitiesPtr &dst, Polylines &&polylines, const ExtrusionAttributes &attributes) { dst.reserve(dst.size() + polylines.size()); - for (Polyline &polyline : polylines) { - if (polyline.is_valid()) { - if (polyline.is_closed()) { - ExtrusionPath extrusion_path(role, mm3_per_mm, width, height); - extrusion_path.polyline = std::move(polyline); - dst.emplace_back(new ExtrusionLoop(std::move(extrusion_path))); - } else { - ExtrusionPath *extrusion_path = new ExtrusionPath(role, mm3_per_mm, width, height); - extrusion_path->polyline = std::move(polyline); - dst.emplace_back(extrusion_path); - } - } - } + for (Polyline &polyline : polylines) + if (polyline.is_valid()) + dst.emplace_back(polyline.is_closed() ? + static_cast(new ExtrusionLoop(ExtrusionPath{ std::move(polyline), attributes })) : + static_cast(new ExtrusionPath(std::move(polyline), attributes))); polylines.clear(); } diff --git a/src/libslic3r/ExtrusionEntityCollection.cpp b/src/libslic3r/ExtrusionEntityCollection.cpp index 55167861c3..3f49eaa032 100644 --- a/src/libslic3r/ExtrusionEntityCollection.cpp +++ b/src/libslic3r/ExtrusionEntityCollection.cpp @@ -6,6 +6,7 @@ namespace Slic3r { +#if 0 void filter_by_extrusion_role_in_place(ExtrusionEntitiesPtr &extrusion_entities, ExtrusionRole role) { if (role != ExtrusionRole::Mixed) { @@ -17,6 +18,7 @@ void filter_by_extrusion_role_in_place(ExtrusionEntitiesPtr &extrusion_entities, last); } } +#endif ExtrusionEntityCollection::ExtrusionEntityCollection(const ExtrusionPaths &paths) : no_sort(false) @@ -83,18 +85,6 @@ void ExtrusionEntityCollection::remove(size_t i) this->entities.erase(this->entities.begin() + i); } -ExtrusionEntityCollection ExtrusionEntityCollection::chained_path_from(const ExtrusionEntitiesPtr& extrusion_entities, const Point &start_near, ExtrusionRole role) -{ - // Return a filtered copy of the collection. - ExtrusionEntityCollection out; - out.entities = filter_by_extrusion_role(extrusion_entities, role); - // Clone the extrusion entities. - for (auto &ptr : out.entities) - ptr = ptr->clone(); - chain_and_reorder_extrusion_entities(out.entities, &start_near); - return out; -} - void ExtrusionEntityCollection::polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const { for (const ExtrusionEntity *entity : this->entities) diff --git a/src/libslic3r/ExtrusionEntityCollection.hpp b/src/libslic3r/ExtrusionEntityCollection.hpp index 676bdd891a..3d6ffba3fd 100644 --- a/src/libslic3r/ExtrusionEntityCollection.hpp +++ b/src/libslic3r/ExtrusionEntityCollection.hpp @@ -7,6 +7,7 @@ namespace Slic3r { +#if 0 // Remove those items from extrusion_entities, that do not match role. // Do nothing if role is mixed. // Removed elements are NOT being deleted. @@ -21,6 +22,7 @@ inline ExtrusionEntitiesPtr filter_by_extrusion_role(const ExtrusionEntitiesPtr filter_by_extrusion_role_in_place(out, role); return out; } +#endif class ExtrusionEntityCollection : public ExtrusionEntity { @@ -96,9 +98,6 @@ public: } void replace(size_t i, const ExtrusionEntity &entity); void remove(size_t i); - static ExtrusionEntityCollection chained_path_from(const ExtrusionEntitiesPtr &extrusion_entities, const Point &start_near, ExtrusionRole role = ExtrusionRole::Mixed); - ExtrusionEntityCollection chained_path_from(const Point &start_near, ExtrusionRole role = ExtrusionRole::Mixed) const - { return this->no_sort ? *this : chained_path_from(this->entities, start_near, role); } void reverse() override; const Point& first_point() const override { return this->entities.front()->first_point(); } const Point& last_point() const override { return this->entities.back()->last_point(); } diff --git a/src/libslic3r/ExtrusionSimulator.cpp b/src/libslic3r/ExtrusionSimulator.cpp index 6b1f76abea..a08b4d8b9a 100644 --- a/src/libslic3r/ExtrusionSimulator.cpp +++ b/src/libslic3r/ExtrusionSimulator.cpp @@ -957,9 +957,9 @@ void ExtrusionSimulator::extrude_to_accumulator(const ExtrusionPath &path, const polyline.reserve(path.polyline.points.size()); float scalex = float(viewport.size().x()) / float(bbox.size().x()); float scaley = float(viewport.size().y()) / float(bbox.size().y()); - float w = scale_(path.width) * scalex; + float w = scale_(path.width()) * scalex; //float h = scale_(path.height) * scalex; - w = scale_(path.mm3_per_mm / path.height) * scalex; + w = scale_(path.mm3_per_mm() / path.height()) * scalex; // printf("scalex: %f, scaley: %f\n", scalex, scaley); // printf("bbox: %d,%d %d,%d\n", bbox.min.x(), bbox.min.y, bbox.max.x(), bbox.max.y); for (Points::const_iterator it = path.polyline.points.begin(); it != path.polyline.points.end(); ++ it) { diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index 499e7b85af..7fa4c992f9 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -558,8 +558,9 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: } else { extrusion_entities_append_paths( eec->entities, std::move(polylines), - surface_fill.params.extrusion_role, - flow_mm3_per_mm, float(flow_width), surface_fill.params.flow.height()); + ExtrusionAttributes{ surface_fill.params.extrusion_role, + ExtrusionFlow{ flow_mm3_per_mm, float(flow_width), surface_fill.params.flow.height() } + }); } insert_fills_into_islands(*this, uint32_t(surface_fill.region_id), fill_begin, uint32_t(layerm.fills().size())); } @@ -904,8 +905,9 @@ void Layer::make_ironing() eec->no_sort = true; extrusion_entities_append_paths( eec->entities, std::move(polylines), - ExtrusionRole::Ironing, - flow_mm3_per_mm, extrusion_width, float(extrusion_height)); + ExtrusionAttributes{ ExtrusionRole::Ironing, + ExtrusionFlow{ flow_mm3_per_mm, extrusion_width, float(extrusion_height) } + }); insert_fills_into_islands(*this, ironing_params.region_id, fill_begin, uint32_t(ironing_params.layerm->fills().size())); } } diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 1f306c83c6..691cdea79d 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -9,6 +9,7 @@ #include "GCode/PrintExtents.hpp" #include "GCode/Thumbnails.hpp" #include "GCode/WipeTower.hpp" +#include "GCode/WipeTowerIntegration.hpp" #include "Point.hpp" #include "Polygon.hpp" #include "PrintConfig.hpp" @@ -106,7 +107,7 @@ namespace Slic3r { return ok; } - std::string OozePrevention::pre_toolchange(GCode& gcodegen) + std::string OozePrevention::pre_toolchange(GCodeGenerator &gcodegen) { std::string gcode; @@ -132,308 +133,25 @@ namespace Slic3r { return gcode; } - std::string OozePrevention::post_toolchange(GCode& gcodegen) + std::string OozePrevention::post_toolchange(GCodeGenerator &gcodegen) { return (gcodegen.config().standby_temperature_delta.value != 0) ? gcodegen.writer().set_temperature(this->_get_temp(gcodegen), true, gcodegen.writer().extruder()->id()) : std::string(); } - int OozePrevention::_get_temp(const GCode& gcodegen) const + int OozePrevention::_get_temp(const GCodeGenerator &gcodegen) const { return (gcodegen.layer() == nullptr || gcodegen.layer()->id() == 0) ? gcodegen.config().first_layer_temperature.get_at(gcodegen.writer().extruder()->id()) : gcodegen.config().temperature.get_at(gcodegen.writer().extruder()->id()); } - std::string Wipe::wipe(GCode& gcodegen, bool toolchange) - { - std::string gcode; - const Extruder &extruder = *gcodegen.writer().extruder(); - - // Remaining quantized retraction length. - if (double retract_length = extruder.retract_to_go(toolchange ? extruder.retract_length_toolchange() : extruder.retract_length()); - retract_length > 0 && this->path.size() >= 2) { - // Reduce feedrate a bit; travel speed is often too high to move on existing material. - // Too fast = ripping of existing material; too slow = short wipe path, thus more blob. - const double wipe_speed = gcodegen.writer().config.travel_speed.value * 0.8; - // Reduce retraction length a bit to avoid effective retraction speed to be greater than the configured one - // due to rounding (TODO: test and/or better math for this). - const double xy_to_e = 0.95 * extruder.retract_speed() / wipe_speed; - // Start with the current position, which may be different from the wipe path start in case of loop clipping. - Vec2d prev = gcodegen.point_to_gcode_quantized(gcodegen.last_pos()); - auto it = this->path.points.begin(); - Vec2d p = gcodegen.point_to_gcode_quantized(*(++ it)); - if (p != prev) { - gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Wipe_Start) + "\n"; - auto end = this->path.points.end(); - bool done = false; - for (; it != end && ! done; ++ it) { - p = gcodegen.point_to_gcode_quantized(*it); - double segment_length = (p - prev).norm(); - double dE = GCodeFormatter::quantize_e(xy_to_e * segment_length); - if (dE > retract_length - EPSILON) { - if (dE > retract_length + EPSILON) - // Shorten the segment. - p = prev + (p - prev) * (retract_length / dE); - dE = retract_length; - done = true; - } - //FIXME one shall not generate the unnecessary G1 Fxxx commands, here wipe_speed is a constant inside this cycle. - // Is it here for the cooling markers? Or should it be outside of the cycle? - gcode += gcodegen.writer().set_speed(wipe_speed * 60, {}, gcodegen.enable_cooling_markers() ? ";_WIPE" : ""); - gcode += gcodegen.writer().extrude_to_xy(p, -dE, "wipe and retract"); - prev = p; - retract_length -= dE; - } - // add tag for processor - gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Wipe_End) + "\n"; - gcodegen.set_last_pos(gcodegen.gcode_to_point(prev)); - } - } - - // Prevent wiping again on the same path. - this->reset_path(); - return gcode; - } - - static inline Point wipe_tower_point_to_object_point(GCode& gcodegen, const Vec2f& wipe_tower_pt) - { - return Point(scale_(wipe_tower_pt.x() - gcodegen.origin()(0)), scale_(wipe_tower_pt.y() - gcodegen.origin()(1))); - } - - std::string WipeTowerIntegration::append_tcr(GCode& gcodegen, const WipeTower::ToolChangeResult& tcr, int new_extruder_id, double z) const - { - if (new_extruder_id != -1 && new_extruder_id != tcr.new_tool) - throw Slic3r::InvalidArgument("Error: WipeTowerIntegration::append_tcr was asked to do a toolchange it didn't expect."); - - std::string gcode; - - // Toolchangeresult.gcode assumes the wipe tower corner is at the origin (except for priming lines) - // We want to rotate and shift all extrusions (gcode postprocessing) and starting and ending position - float alpha = m_wipe_tower_rotation / 180.f * float(M_PI); - - auto transform_wt_pt = [&alpha, this](const Vec2f& pt) -> Vec2f { - Vec2f out = Eigen::Rotation2Df(alpha) * pt; - out += m_wipe_tower_pos; - return out; - }; - - Vec2f start_pos = tcr.start_pos; - Vec2f end_pos = tcr.end_pos; - if (! tcr.priming) { - start_pos = transform_wt_pt(start_pos); - end_pos = transform_wt_pt(end_pos); - } - - Vec2f wipe_tower_offset = tcr.priming ? Vec2f::Zero() : m_wipe_tower_pos; - float wipe_tower_rotation = tcr.priming ? 0.f : alpha; - - std::string tcr_rotated_gcode = post_process_wipe_tower_moves(tcr, wipe_tower_offset, wipe_tower_rotation); - - gcode += gcodegen.writer().unlift(); // Make sure there is no z-hop (in most cases, there isn't). - - double current_z = gcodegen.writer().get_position().z(); - if (z == -1.) // in case no specific z was provided, print at current_z pos - z = current_z; - - const bool needs_toolchange = gcodegen.writer().need_toolchange(new_extruder_id); - const bool will_go_down = ! is_approx(z, current_z); - if (tcr.force_travel || ! needs_toolchange || (gcodegen.config().single_extruder_multi_material && ! tcr.priming)) { - // Move over the wipe tower. If this is not single-extruder MM, the first wipe tower move following the - // toolchange will travel there anyway (if there is a toolchange). - // FIXME: It would be better if the wipe tower set the force_travel flag for all toolchanges, - // then we could simplify the condition and make it more readable. - gcode += gcodegen.retract(); - gcodegen.m_avoid_crossing_perimeters.use_external_mp_once(); - gcode += gcodegen.travel_to( - wipe_tower_point_to_object_point(gcodegen, start_pos), - ExtrusionRole::Mixed, - "Travel to a Wipe Tower"); - gcode += gcodegen.unretract(); - } - - if (will_go_down) { - gcode += gcodegen.writer().retract(); - gcode += gcodegen.writer().travel_to_z(z, "Travel down to the last wipe tower layer."); - gcode += gcodegen.writer().unretract(); - } - - std::string toolchange_gcode_str; - std::string deretraction_str; - if (tcr.priming || (new_extruder_id >= 0 && needs_toolchange)) { - if (gcodegen.config().single_extruder_multi_material) - gcodegen.m_wipe.reset_path(); // We don't want wiping on the ramming lines. - toolchange_gcode_str = gcodegen.set_extruder(new_extruder_id, tcr.print_z); // TODO: toolchange_z vs print_z - if (gcodegen.config().wipe_tower) - deretraction_str = gcodegen.unretract(); - } - - - - - // Insert the toolchange and deretraction gcode into the generated gcode. - DynamicConfig config; - config.set_key_value("toolchange_gcode", new ConfigOptionString(toolchange_gcode_str)); - config.set_key_value("deretraction_from_wipe_tower_generator", new ConfigOptionString(deretraction_str)); - std::string tcr_gcode, tcr_escaped_gcode = gcodegen.placeholder_parser_process("tcr_rotated_gcode", tcr_rotated_gcode, new_extruder_id, &config); - unescape_string_cstyle(tcr_escaped_gcode, tcr_gcode); - gcode += tcr_gcode; - check_add_eol(toolchange_gcode_str); - - // A phony move to the end position at the wipe tower. - gcodegen.writer().travel_to_xy(end_pos.cast()); - gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, end_pos)); - if (!is_approx(z, current_z)) { - gcode += gcodegen.writer().retract(); - gcode += gcodegen.writer().travel_to_z(current_z, "Travel back up to the topmost object layer."); - gcode += gcodegen.writer().unretract(); - } - - else { - // Prepare a future wipe. - gcodegen.m_wipe.reset_path(); - for (const Vec2f& wipe_pt : tcr.wipe_path) - gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, transform_wt_pt(wipe_pt))); - } - - // Let the planner know we are traveling between objects. - gcodegen.m_avoid_crossing_perimeters.use_external_mp_once(); - return gcode; - } - - // This function postprocesses gcode_original, rotates and moves all G1 extrusions and returns resulting gcode - // Starting position has to be supplied explicitely (otherwise it would fail in case first G1 command only contained one coordinate) - std::string WipeTowerIntegration::post_process_wipe_tower_moves(const WipeTower::ToolChangeResult& tcr, const Vec2f& translation, float angle) const - { - Vec2f extruder_offset = m_extruder_offsets[tcr.initial_tool].cast(); - - std::istringstream gcode_str(tcr.gcode); - std::string gcode_out; - std::string line; - Vec2f pos = tcr.start_pos; - Vec2f transformed_pos = Eigen::Rotation2Df(angle) * pos + translation; - Vec2f old_pos(-1000.1f, -1000.1f); - - while (gcode_str) { - std::getline(gcode_str, line); // we read the gcode line by line - - // All G1 commands should be translated and rotated. X and Y coords are - // only pushed to the output when they differ from last time. - // WT generator can override this by appending the never_skip_tag - if (boost::starts_with(line, "G1 ")) { - bool never_skip = false; - auto it = line.find(WipeTower::never_skip_tag()); - if (it != std::string::npos) { - // remove the tag and remember we saw it - never_skip = true; - line.erase(it, it + WipeTower::never_skip_tag().size()); - } - std::ostringstream line_out; - std::istringstream line_str(line); - line_str >> std::noskipws; // don't skip whitespace - char ch = 0; - line_str >> ch >> ch; // read the "G1" - while (line_str >> ch) { - if (ch == 'X' || ch == 'Y') - line_str >> (ch == 'X' ? pos.x() : pos.y()); - else - line_out << ch; - } - - transformed_pos = Eigen::Rotation2Df(angle) * pos + translation; - - if (transformed_pos != old_pos || never_skip) { - line = line_out.str(); - boost::trim_left(line); // Remove leading spaces - std::ostringstream oss; - oss << std::fixed << std::setprecision(3) << "G1"; - if (transformed_pos.x() != old_pos.x() || never_skip) - oss << " X" << transformed_pos.x() - extruder_offset.x(); - if (transformed_pos.y() != old_pos.y() || never_skip) - oss << " Y" << transformed_pos.y() - extruder_offset.y(); - if (! line.empty()) - oss << " "; - line = oss.str() + line; - old_pos = transformed_pos; - } - } - - gcode_out += line + "\n"; - - // If this was a toolchange command, we should change current extruder offset - if (line == "[toolchange_gcode]") { - extruder_offset = m_extruder_offsets[tcr.new_tool].cast(); - - // If the extruder offset changed, add an extra move so everything is continuous - if (extruder_offset != m_extruder_offsets[tcr.initial_tool].cast()) { - std::ostringstream oss; - oss << std::fixed << std::setprecision(3) - << "G1 X" << transformed_pos.x() - extruder_offset.x() - << " Y" << transformed_pos.y() - extruder_offset.y() - << "\n"; - gcode_out += oss.str(); - } - } - } - return gcode_out; - } - - - std::string WipeTowerIntegration::prime(GCode& gcodegen) - { - std::string gcode; - for (const WipeTower::ToolChangeResult& tcr : m_priming) { - if (! tcr.extrusions.empty()) - gcode += append_tcr(gcodegen, tcr, tcr.new_tool); - } - return gcode; - } - - std::string WipeTowerIntegration::tool_change(GCode& gcodegen, int extruder_id, bool finish_layer) - { - std::string gcode; - assert(m_layer_idx >= 0); - if (gcodegen.writer().need_toolchange(extruder_id) || finish_layer) { - if (m_layer_idx < (int)m_tool_changes.size()) { - if (!(size_t(m_tool_change_idx) < m_tool_changes[m_layer_idx].size())) - throw Slic3r::RuntimeError("Wipe tower generation failed, possibly due to empty first layer."); - - // Calculate where the wipe tower layer will be printed. -1 means that print z will not change, - // resulting in a wipe tower with sparse layers. - double wipe_tower_z = -1; - bool ignore_sparse = false; - if (gcodegen.config().wipe_tower_no_sparse_layers.value) { - wipe_tower_z = m_last_wipe_tower_print_z; - ignore_sparse = (m_tool_changes[m_layer_idx].size() == 1 && m_tool_changes[m_layer_idx].front().initial_tool == m_tool_changes[m_layer_idx].front().new_tool && m_layer_idx != 0); - if (m_tool_change_idx == 0 && !ignore_sparse) - wipe_tower_z = m_last_wipe_tower_print_z + m_tool_changes[m_layer_idx].front().layer_height; - } - - if (!ignore_sparse) { - gcode += append_tcr(gcodegen, m_tool_changes[m_layer_idx][m_tool_change_idx++], extruder_id, wipe_tower_z); - m_last_wipe_tower_print_z = wipe_tower_z; - } - } - } - return gcode; - } - - // Print is finished. Now it remains to unload the filament safely with ramming over the wipe tower. - std::string WipeTowerIntegration::finalize(GCode& gcodegen) - { - std::string gcode; - if (std::abs(gcodegen.writer().get_position().z() - m_final_purge.print_z) > EPSILON) - gcode += gcodegen.change_layer(m_final_purge.print_z); - gcode += append_tcr(gcodegen, m_final_purge, -1); - return gcode; - } - const std::vector ColorPrintColors::Colors = { "#C0392B", "#E67E22", "#F1C40F", "#27AE60", "#1ABC9C", "#2980B9", "#9B59B6" }; #define EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(m_writer.extruder()->id()) -void GCode::PlaceholderParserIntegration::reset() +void GCodeGenerator::PlaceholderParserIntegration::reset() { this->failed_templates.clear(); this->output_config.clear(); @@ -453,7 +171,7 @@ void GCode::PlaceholderParserIntegration::reset() this->e_restart_extra.clear(); } -void GCode::PlaceholderParserIntegration::init(const GCodeWriter &writer) +void GCodeGenerator::PlaceholderParserIntegration::init(const GCodeWriter &writer) { this->reset(); const std::vector &extruders = writer.extruders(); @@ -489,7 +207,7 @@ void GCode::PlaceholderParserIntegration::init(const GCodeWriter &writer) this->parser.set("zhop", this->opt_zhop); } -void GCode::PlaceholderParserIntegration::update_from_gcodewriter(const GCodeWriter &writer) +void GCodeGenerator::PlaceholderParserIntegration::update_from_gcodewriter(const GCodeWriter &writer) { memcpy(this->position.data(), writer.get_position().data(), sizeof(double) * 3); this->opt_position->values = this->position; @@ -528,7 +246,7 @@ void GCode::PlaceholderParserIntegration::update_from_gcodewriter(const GCodeWri } // Throw if any of the output vector variables were resized by the script. -void GCode::PlaceholderParserIntegration::validate_output_vector_variables() +void GCodeGenerator::PlaceholderParserIntegration::validate_output_vector_variables() { if (this->opt_position->values.size() != 3) throw Slic3r::RuntimeError("\"position\" output variable must not be resized by the script."); @@ -544,9 +262,9 @@ void GCode::PlaceholderParserIntegration::validate_output_vector_variables() // Collect pairs of object_layer + support_layer sorted by print_z. // object_layer & support_layer are considered to be on the same print_z, if they are not further than EPSILON. -GCode::ObjectsLayerToPrint GCode::collect_layers_to_print(const PrintObject& object) +GCodeGenerator::ObjectsLayerToPrint GCodeGenerator::collect_layers_to_print(const PrintObject& object) { - GCode::ObjectsLayerToPrint layers_to_print; + GCodeGenerator::ObjectsLayerToPrint layers_to_print; layers_to_print.reserve(object.layers().size() + object.support_layers().size()); /* @@ -645,7 +363,7 @@ GCode::ObjectsLayerToPrint GCode::collect_layers_to_print(const PrintObject& obj // Prepare for non-sequential printing of multiple objects: Support resp. object layers with nearly identical print_z // will be printed for all objects at once. // Return a list of items. -std::vector> GCode::collect_layers_to_print(const Print& print) +std::vector> GCodeGenerator::collect_layers_to_print(const Print& print) { struct OrderingItem { coordf_t print_z; @@ -694,7 +412,7 @@ std::vector> GCode::collect_laye return layers_to_print; } -// free functions called by GCode::do_export() +// free functions called by GCodeGenerator::do_export() namespace DoExport { // static void update_print_estimated_times_stats(const GCodeProcessor& processor, PrintStatistics& print_statistics) // { @@ -799,7 +517,7 @@ namespace DoExport { } } // namespace DoExport -void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* result, ThumbnailsGeneratorCallback thumbnail_cb) +void GCodeGenerator::do_export(Print* print, const char* path, GCodeProcessorResult* result, ThumbnailsGeneratorCallback thumbnail_cb) { CNumericLocalesSetter locales_setter; @@ -894,7 +612,7 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu print->set_done(psGCodeExport); } -// free functions called by GCode::_do_export() +// free functions called by GCodeGenerator::_do_export() namespace DoExport { static void init_gcode_processor(const PrintConfig& config, GCodeProcessor& processor, bool& silent_time_estimator_enabled) { @@ -1080,7 +798,7 @@ std::vector sort_object_instances_by_model_order(const Pri return instances; } -void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGeneratorCallback thumbnail_cb) +void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGeneratorCallback thumbnail_cb) { // modifies m_silent_time_estimator_enabled DoExport::init_gcode_processor(print.config(), m_processor, m_silent_time_estimator_enabled); @@ -1397,7 +1115,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato std::vector> layers_to_print = collect_layers_to_print(print); // Prusa Multi-Material wipe tower. if (has_wipe_tower && ! layers_to_print.empty()) { - m_wipe_tower.reset(new WipeTowerIntegration(print.config(), *print.wipe_tower_data().priming.get(), print.wipe_tower_data().tool_changes, *print.wipe_tower_data().final_purge.get())); + m_wipe_tower = std::make_unique(print.config(), *print.wipe_tower_data().priming.get(), print.wipe_tower_data().tool_changes, *print.wipe_tower_data().final_purge.get()); file.write(m_writer.travel_to_z(first_layer_height + m_config.z_offset.value, "Move to the first layer height")); if (print.config().single_extruder_multi_material_priming) { file.write(m_wipe_tower->prime(*this)); @@ -1515,20 +1233,42 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato print.throw_if_canceled(); } +// Fill in cache of smooth paths for perimeters, fills and supports of the given object layers. +// Based on params, the paths are either decimated to sparser polylines, or interpolated with circular arches. +void GCodeGenerator::smooth_path_interpolate( + const ObjectLayerToPrint &object_layer_to_print, + const GCode::SmoothPathCache::InterpolationParameters ¶ms, + GCode::SmoothPathCache &out) +{ + if (const Layer *layer = object_layer_to_print.object_layer; layer) { + for (const LayerRegion *layerm : layer->regions()) { + out.interpolate_add(layerm->perimeters(), params); + out.interpolate_add(layerm->fills(), params); + } + } + if (const SupportLayer *layer = object_layer_to_print.support_layer; layer) + out.interpolate_add(layer->support_fills, params); +} + // Process all layers of all objects (non-sequential mode) with a parallel pipeline: // Generate G-code, run the filters (vase mode, cooling buffer), run the G-code analyser // and export G-code into file. -void GCode::process_layers( +void GCodeGenerator::process_layers( const Print &print, const ToolOrdering &tool_ordering, const std::vector &print_object_instances_ordering, const std::vector> &layers_to_print, GCodeOutputStream &output_stream) { - // The pipeline is variable: The vase mode filter is optional. size_t layer_to_print_idx = 0; - const auto generator = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, - [this, &print, &tool_ordering, &print_object_instances_ordering, &layers_to_print, &layer_to_print_idx](tbb::flow_control& fc) -> LayerResult { + const GCode::SmoothPathCache::InterpolationParameters interpolation_params { + scaled(print.config().gcode_resolution.value), + print.config().arc_fitting && ! print.config().spiral_vase ? + Geometry::ArcWelder::default_arc_length_percent_tolerance : + 0 + }; + const auto smooth_path_interpolator = tbb::make_filter>(slic3r_tbb_filtermode::serial_in_order, + [this, &print, &layers_to_print, &layer_to_print_idx, interpolation_params](tbb::flow_control &fc) -> std::pair { if (layer_to_print_idx >= layers_to_print.size()) { if ((!m_pressure_equalizer && layer_to_print_idx == layers_to_print.size()) || (m_pressure_equalizer && layer_to_print_idx == (layers_to_print.size() + 1))) { fc.stop(); @@ -1536,23 +1276,38 @@ void GCode::process_layers( } else { // Pressure equalizer need insert empty input. Because it returns one layer back. // Insert NOP (no operation) layer; - ++layer_to_print_idx; - return LayerResult::make_nop_layer_result(); + return { ++ layer_to_print_idx, {} }; } } else { - const std::pair &layer = layers_to_print[layer_to_print_idx++]; + print.throw_if_canceled(); + size_t idx = layer_to_print_idx ++; + GCode::SmoothPathCache smooth_path_cache; + for (const ObjectLayerToPrint &l : layers_to_print[idx].second) + GCodeGenerator::smooth_path_interpolate(l, interpolation_params, smooth_path_cache); + return { idx, std::move(smooth_path_cache) }; + } + }); + const auto generator = tbb::make_filter, LayerResult>(slic3r_tbb_filtermode::serial_in_order, + [this, &print, &tool_ordering, &print_object_instances_ordering, &layers_to_print](std::pair in) -> LayerResult { + size_t layer_to_print_idx = in.first; + if (layer_to_print_idx == layers_to_print.size()) { + // Pressure equalizer need insert empty input. Because it returns one layer back. + // Insert NOP (no operation) layer; + return LayerResult::make_nop_layer_result(); + } else { + const std::pair &layer = layers_to_print[layer_to_print_idx]; const LayerTools& layer_tools = tool_ordering.tools_for_layer(layer.first); if (m_wipe_tower && layer_tools.has_wipe_tower) m_wipe_tower->next_layer(); print.throw_if_canceled(); - return this->process_layer(print, layer.second, layer_tools, &layer == &layers_to_print.back(), &print_object_instances_ordering, size_t(-1)); + return this->process_layer(print, layer.second, layer_tools, &in.second, &layer == &layers_to_print.back(), &print_object_instances_ordering, size_t(-1)); } }); + // The pipeline is variable: The vase mode filter is optional. const auto spiral_vase = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, [spiral_vase = this->m_spiral_vase.get()](LayerResult in) -> LayerResult { if (in.nop_layer_result) return in; - spiral_vase->enable(in.spiral_vase_enable); return { spiral_vase->process_layer(std::move(in.gcode)), in.layer_id, in.spiral_vase_enable, in.cooling_buffer_flush}; }); @@ -1575,45 +1330,44 @@ void GCode::process_layers( [&output_stream](std::string s) { output_stream.write(s); } ); + tbb::filter pipeline_to_layerresult = smooth_path_interpolator & generator; + if (m_spiral_vase) + pipeline_to_layerresult = pipeline_to_layerresult & spiral_vase; + if (m_pressure_equalizer) + pipeline_to_layerresult = pipeline_to_layerresult & pressure_equalizer; + + tbb::filter pipeline_to_string = cooling; + if (m_find_replace) + pipeline_to_string = pipeline_to_string & find_replace; + // It registers a handler that sets locales to "C" before any TBB thread starts participating in tbb::parallel_pipeline. // Handler is unregistered when the destructor is called. TBBLocalesSetter locales_setter; - // The pipeline elements are joined using const references, thus no copying is performed. output_stream.find_replace_supress(); - if (m_spiral_vase && m_find_replace && m_pressure_equalizer) - tbb::parallel_pipeline(12, generator & spiral_vase & pressure_equalizer & cooling & find_replace & output); - else if (m_spiral_vase && m_find_replace) - tbb::parallel_pipeline(12, generator & spiral_vase & cooling & find_replace & output); - else if (m_spiral_vase && m_pressure_equalizer) - tbb::parallel_pipeline(12, generator & spiral_vase & pressure_equalizer & cooling & output); - else if (m_find_replace && m_pressure_equalizer) - tbb::parallel_pipeline(12, generator & pressure_equalizer & cooling & find_replace & output); - else if (m_spiral_vase) - tbb::parallel_pipeline(12, generator & spiral_vase & cooling & output); - else if (m_find_replace) - tbb::parallel_pipeline(12, generator & cooling & find_replace & output); - else if (m_pressure_equalizer) - tbb::parallel_pipeline(12, generator & pressure_equalizer & cooling & output); - else - tbb::parallel_pipeline(12, generator & cooling & output); + tbb::parallel_pipeline(12, pipeline_to_layerresult & pipeline_to_string & output); output_stream.find_replace_enable(); } // Process all layers of a single object instance (sequential mode) with a parallel pipeline: // Generate G-code, run the filters (vase mode, cooling buffer), run the G-code analyser // and export G-code into file. -void GCode::process_layers( +void GCodeGenerator::process_layers( const Print &print, const ToolOrdering &tool_ordering, ObjectsLayerToPrint layers_to_print, const size_t single_object_idx, GCodeOutputStream &output_stream) { - // The pipeline is variable: The vase mode filter is optional. size_t layer_to_print_idx = 0; - const auto generator = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, - [this, &print, &tool_ordering, &layers_to_print, &layer_to_print_idx, single_object_idx](tbb::flow_control& fc) -> LayerResult { + const GCode::SmoothPathCache::InterpolationParameters interpolation_params { + scaled(print.config().gcode_resolution.value), + print.config().arc_fitting && ! print.config().spiral_vase ? + Geometry::ArcWelder::default_arc_length_percent_tolerance : + 0 + }; + const auto smooth_path_interpolator = tbb::make_filter> (slic3r_tbb_filtermode::serial_in_order, + [this, &print, &layers_to_print, &layer_to_print_idx, interpolation_params](tbb::flow_control &fc) -> std::pair { if (layer_to_print_idx >= layers_to_print.size()) { if ((!m_pressure_equalizer && layer_to_print_idx == layers_to_print.size()) || (m_pressure_equalizer && layer_to_print_idx == (layers_to_print.size() + 1))) { fc.stop(); @@ -1621,15 +1375,30 @@ void GCode::process_layers( } else { // Pressure equalizer need insert empty input. Because it returns one layer back. // Insert NOP (no operation) layer; - ++layer_to_print_idx; - return LayerResult::make_nop_layer_result(); + return { ++ layer_to_print_idx, {} }; } } else { - ObjectLayerToPrint &layer = layers_to_print[layer_to_print_idx ++]; print.throw_if_canceled(); - return this->process_layer(print, { std::move(layer) }, tool_ordering.tools_for_layer(layer.print_z()), &layer == &layers_to_print.back(), nullptr, single_object_idx); + size_t idx = layer_to_print_idx ++; + GCode::SmoothPathCache smooth_path_cache; + GCodeGenerator::smooth_path_interpolate(layers_to_print[idx], interpolation_params, smooth_path_cache); + return { idx, std::move(smooth_path_cache) }; } }); + const auto generator = tbb::make_filter, LayerResult>(slic3r_tbb_filtermode::serial_in_order, + [this, &print, &tool_ordering, &layers_to_print, single_object_idx](std::pair in) -> LayerResult { + size_t layer_to_print_idx = in.first; + if (layer_to_print_idx == layers_to_print.size()) { + // Pressure equalizer need insert empty input. Because it returns one layer back. + // Insert NOP (no operation) layer; + return LayerResult::make_nop_layer_result(); + } else { + ObjectLayerToPrint &layer = layers_to_print[layer_to_print_idx]; + print.throw_if_canceled(); + return this->process_layer(print, { std::move(layer) }, tool_ordering.tools_for_layer(layer.print_z()), &in.second, &layer == &layers_to_print.back(), nullptr, single_object_idx); + } + }); + // The pipeline is variable: The vase mode filter is optional. const auto spiral_vase = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, [spiral_vase = this->m_spiral_vase.get()](LayerResult in)->LayerResult { if (in.nop_layer_result) @@ -1655,32 +1424,26 @@ void GCode::process_layers( [&output_stream](std::string s) { output_stream.write(s); } ); + tbb::filter pipeline_to_layerresult = smooth_path_interpolator & generator; + if (m_spiral_vase) + pipeline_to_layerresult = pipeline_to_layerresult & spiral_vase; + if (m_pressure_equalizer) + pipeline_to_layerresult = pipeline_to_layerresult & pressure_equalizer; + + tbb::filter pipeline_to_string = cooling; + if (m_find_replace) + pipeline_to_string = pipeline_to_string & find_replace; + // It registers a handler that sets locales to "C" before any TBB thread starts participating in tbb::parallel_pipeline. // Handler is unregistered when the destructor is called. TBBLocalesSetter locales_setter; - // The pipeline elements are joined using const references, thus no copying is performed. output_stream.find_replace_supress(); - if (m_spiral_vase && m_find_replace && m_pressure_equalizer) - tbb::parallel_pipeline(12, generator & spiral_vase & pressure_equalizer & cooling & find_replace & output); - else if (m_spiral_vase && m_find_replace) - tbb::parallel_pipeline(12, generator & spiral_vase & cooling & find_replace & output); - else if (m_spiral_vase && m_pressure_equalizer) - tbb::parallel_pipeline(12, generator & spiral_vase & pressure_equalizer & cooling & output); - else if (m_find_replace && m_pressure_equalizer) - tbb::parallel_pipeline(12, generator & pressure_equalizer & cooling & find_replace & output); - else if (m_spiral_vase) - tbb::parallel_pipeline(12, generator & spiral_vase & cooling & output); - else if (m_find_replace) - tbb::parallel_pipeline(12, generator & cooling & find_replace & output); - else if (m_pressure_equalizer) - tbb::parallel_pipeline(12, generator & pressure_equalizer & cooling & output); - else - tbb::parallel_pipeline(12, generator & cooling & output); + tbb::parallel_pipeline(12, pipeline_to_layerresult & pipeline_to_string & output); output_stream.find_replace_enable(); } -std::string GCode::placeholder_parser_process( +std::string GCodeGenerator::placeholder_parser_process( const std::string &name, const std::string &templ, unsigned int current_extruder_id, @@ -1794,7 +1557,7 @@ static bool custom_gcode_sets_temperature(const std::string &gcode, const int mc // Print the machine envelope G-code for the Marlin firmware based on the "machine_max_xxx" parameters. // Do not process this piece of G-code by the time estimator, it already knows the values through another sources. -void GCode::print_machine_envelope(GCodeOutputStream &file, Print &print) +void GCodeGenerator::print_machine_envelope(GCodeOutputStream &file, Print &print) { const GCodeFlavor flavor = print.config().gcode_flavor.value; if ( (flavor == gcfMarlinLegacy || flavor == gcfMarlinFirmware || flavor == gcfRepRapFirmware) @@ -1855,7 +1618,7 @@ void GCode::print_machine_envelope(GCodeOutputStream &file, Print &print) // Only do that if the start G-code does not already contain any M-code controlling an extruder temperature. // M140 - Set Extruder Temperature // M190 - Set Extruder Temperature and Wait -void GCode::_print_first_layer_bed_temperature(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait) +void GCodeGenerator::_print_first_layer_bed_temperature(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait) { bool autoemit = print.config().autoemit_temperature_commands; // Initial bed temperature based on the first extruder. @@ -1877,7 +1640,7 @@ void GCode::_print_first_layer_bed_temperature(GCodeOutputStream &file, Print &p // M104 - Set Extruder Temperature // M109 - Set Extruder Temperature and Wait // RepRapFirmware: G10 Sxx -void GCode::_print_first_layer_extruder_temperatures(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait) +void GCodeGenerator::_print_first_layer_extruder_temperatures(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait) { bool autoemit = print.config().autoemit_temperature_commands; // Is the bed temperature set by the provided custom G-code? @@ -1915,7 +1678,7 @@ void GCode::_print_first_layer_extruder_temperatures(GCodeOutputStream &file, Pr } } -std::vector GCode::sort_print_object_instances( +std::vector GCodeGenerator::sort_print_object_instances( const std::vector &object_layers, // Ordering must be defined for normal (non-sequential print). const std::vector *ordering, @@ -1956,7 +1719,7 @@ namespace ProcessLayer { static std::string emit_custom_gcode_per_print_z( - GCode &gcodegen, + GCodeGenerator &gcodegen, const CustomGCode::Item *custom_gcode, unsigned int current_extruder_id, // ID of the first extruder printing this layer. @@ -2109,11 +1872,12 @@ namespace Skirt { // In non-sequential mode, process_layer is called per each print_z height with all object and support layers accumulated. // For multi-material prints, this routine minimizes extruder switches by gathering extruder specific extrusion paths // and performing the extruder specific extrusions together. -LayerResult GCode::process_layer( +LayerResult GCodeGenerator::process_layer( const Print &print, // Set of object & print layers of the same PrintObject and with the same print_z. const ObjectsLayerToPrint &layers, const LayerTools &layer_tools, + const GCode::SmoothPathCache *smooth_path_cache, const bool last_layer, // Pairs of PrintObject index and its instance index. const std::vector *ordering, @@ -2280,13 +2044,13 @@ LayerResult GCode::process_layer( double mm3_per_mm = layer_skirt_flow.mm3_per_mm(); for (size_t i = loops.first; i < loops.second; ++i) { // Adjust flow according to this layer's layer height. - ExtrusionLoop loop = *dynamic_cast(print.skirt().entities[i]); - for (ExtrusionPath &path : loop.paths) { - path.height = layer_skirt_flow.height(); - path.mm3_per_mm = mm3_per_mm; - } + const ExtrusionLoop src = *dynamic_cast(print.skirt().entities[i]); + ExtrusionLoop loop(src.loop_role()); + loop.paths.reserve(src.paths.size()); + for (const ExtrusionPath &path : src.paths) + loop.paths.emplace_back(path.polyline, ExtrusionAttributes{ path.role(), ExtrusionFlow{ mm3_per_mm, path.width(), layer_skirt_flow.height() } }); //FIXME using the support_material_speed of the 1st object printed. - gcode += this->extrude_loop(loop, "skirt"sv, m_config.support_material_speed.value); + gcode += this->extrude_loop(loop, smooth_path_cache, "skirt"sv, m_config.support_material_speed.value); } m_avoid_crossing_perimeters.use_external_mp(false); // Allow a straight travel move to the first object point if this is the first layer (but don't in next layers). @@ -2298,9 +2062,8 @@ LayerResult GCode::process_layer( if (! m_brim_done) { this->set_origin(0., 0.); m_avoid_crossing_perimeters.use_external_mp(); - for (const ExtrusionEntity *ee : print.brim().entities) { - gcode += this->extrude_entity(*ee, "brim"sv, m_config.support_material_speed.value); - } + for (const ExtrusionEntity *ee : print.brim().entities) + gcode += this->extrude_entity({ *ee, false }, smooth_path_cache, "brim"sv, m_config.support_material_speed.value); m_brim_done = true; m_avoid_crossing_perimeters.use_external_mp(false); // Allow a straight travel move to the first object point. @@ -2317,7 +2080,7 @@ LayerResult GCode::process_layer( for (const InstanceToPrint &instance : instances_to_print) this->process_layer_single_object( gcode, extruder_id, instance, - layers[instance.object_layer_to_print_id], layer_tools, + layers[instance.object_layer_to_print_id], layer_tools, smooth_path_cache, is_anything_overridden, true /* print_wipe_extrusions */); if (gcode_size_old < gcode.size()) gcode+="; PURGING FINISHED\n"; @@ -2326,7 +2089,7 @@ LayerResult GCode::process_layer( for (const InstanceToPrint &instance : instances_to_print) this->process_layer_single_object( gcode, extruder_id, instance, - layers[instance.object_layer_to_print_id], layer_tools, + layers[instance.object_layer_to_print_id], layer_tools, smooth_path_cache, is_anything_overridden, false /* print_wipe_extrusions */); } @@ -2344,7 +2107,7 @@ static inline bool comment_is_perimeter(const std::string_view comment) { return comment.data() == comment_perimeter.data() && comment.size() == comment_perimeter.size(); } -void GCode::process_layer_single_object( +void GCodeGenerator::process_layer_single_object( // output std::string &gcode, // Index of the extruder currently active. @@ -2355,6 +2118,8 @@ void GCode::process_layer_single_object( const ObjectLayerToPrint &layer_to_print, // Container for extruder overrides (when wiping into object or infill). const LayerTools &layer_tools, + // Optional smooth path interpolating extrusion polylines. + const GCode::SmoothPathCache *smooth_path_cache, // Is any extrusion possibly marked as wiping extrusion? const bool is_anything_overridden, // Round 1 (wiping into object or infill) or round 2 (normal extrusions). @@ -2427,9 +2192,16 @@ void GCode::process_layer_single_object( init_layer_delayed(); m_layer = layer_to_print.support_layer; m_object_layer_over_raft = false; - gcode += this->extrude_support( - // support_extrusion_role is ExtrusionRole::SupportMaterial, ExtrusionRole::SupportMaterialInterface or ExtrusionRole::Mixed for all extrusion paths. - support_layer.support_fills.chained_path_from(m_last_pos, extrude_support ? (extrude_interface ? ExtrusionRole::Mixed : ExtrusionRole::SupportMaterial) : ExtrusionRole::SupportMaterialInterface)); + ExtrusionEntitiesPtr entities_cache; + const ExtrusionEntitiesPtr &entities = extrude_support && extrude_interface ? support_layer.support_fills.entities : entities_cache; + if (! extrude_support || ! extrude_interface) { + auto role = extrude_support ? ExtrusionRole::SupportMaterial : ExtrusionRole::SupportMaterialInterface; + entities_cache.reserve(support_layer.support_fills.entities.size()); + for (ExtrusionEntity *ee : support_layer.support_fills.entities) + if (ee->role() == role) + entities_cache.emplace_back(ee); + } + gcode += this->extrude_support(chain_extrusion_references(entities), smooth_path_cache); } } @@ -2487,16 +2259,13 @@ void GCode::process_layer_single_object( if (! temp_fill_extrusions.empty()) { init_layer_delayed(); m_config.apply(region.config()); - //FIXME The source extrusions may be reversed, thus modifying the extrusions! Is it a problem? How about the initial G-code preview? - // Will parallel access of initial G-code preview to these extrusions while reordering them at backend cause issues? - chain_and_reorder_extrusion_entities(temp_fill_extrusions, &m_last_pos); const auto extrusion_name = ironing ? "ironing"sv : "infill"sv; - for (const ExtrusionEntity *fill : temp_fill_extrusions) - if (auto *eec = dynamic_cast(fill); eec) { - for (const ExtrusionEntity *ee : eec->chained_path_from(m_last_pos).entities) - gcode += this->extrude_entity(*ee, extrusion_name); + for (const ExtrusionEntityReference &fill : chain_extrusion_references(temp_fill_extrusions, &m_last_pos)) + if (auto *eec = dynamic_cast(&fill.extrusion_entity()); eec) { + for (const ExtrusionEntityReference &ee : chain_extrusion_references(*eec, &m_last_pos, fill.flipped())) + gcode += this->extrude_entity(ee, smooth_path_cache, extrusion_name); } else - gcode += this->extrude_entity(*fill, extrusion_name); + gcode += this->extrude_entity(fill, smooth_path_cache, extrusion_name); } }; @@ -2510,6 +2279,8 @@ void GCode::process_layer_single_object( const PrintRegion ®ion = print.get_print_region(layerm.region().print_region_id()); bool first = true; for (uint32_t perimeter_id : island.perimeters) { + // Extrusions inside islands are expected to be ordered already. + // Don't reorder them. assert(dynamic_cast(layerm.perimeters().entities[perimeter_id])); if (const auto *eec = static_cast(layerm.perimeters().entities[perimeter_id]); shall_print_this_extrusion_collection(eec, region)) { @@ -2521,7 +2292,8 @@ void GCode::process_layer_single_object( m_config.apply(region.config()); } for (const ExtrusionEntity *ee : *eec) - gcode += this->extrude_entity(*ee, comment_perimeter, -1.); + // Don't reorder, don't flip. + gcode += this->extrude_entity({ *ee, false }, smooth_path_cache, comment_perimeter, -1.); } } }; @@ -2562,14 +2334,14 @@ void GCode::process_layer_single_object( gcode += std::string("; stop printing object ") + print_object.model_object()->name + " id:" + std::to_string(object_id) + " copy " + std::to_string(print_instance.instance_id) + "\n"; } -void GCode::apply_print_config(const PrintConfig &print_config) +void GCodeGenerator::apply_print_config(const PrintConfig &print_config) { m_writer.apply_print_config(print_config); m_config.apply(print_config); m_scaled_resolution = scaled(print_config.gcode_resolution.value); } -void GCode::append_full_config(const Print &print, std::string &str) +void GCodeGenerator::append_full_config(const Print &print, std::string &str) { const DynamicPrintConfig &cfg = print.full_print_config(); // Sorted list of config keys, which shall not be stored into the G-code. Initializer list. @@ -2590,32 +2362,22 @@ void GCode::append_full_config(const Print &print, std::string &str) str += "; " + key + " = " + cfg.opt_serialize(key) + "\n"; } -void GCode::set_extruders(const std::vector &extruder_ids) +void GCodeGenerator::set_extruders(const std::vector &extruder_ids) { m_writer.set_extruders(extruder_ids); - - // enable wipe path generation if any extruder has wipe enabled - m_wipe.enable = false; - for (auto id : extruder_ids) - if (m_config.wipe.get_at(id)) { - m_wipe.enable = true; - break; - } + m_wipe.init(this->config(), extruder_ids); } -void GCode::set_origin(const Vec2d &pointf) +void GCodeGenerator::set_origin(const Vec2d &pointf) { // if origin increases (goes towards right), last_pos decreases because it goes towards left - const Point translate( - scale_(m_origin(0) - pointf(0)), - scale_(m_origin(1) - pointf(1)) - ); - m_last_pos += translate; - m_wipe.path.translate(translate); + const auto offset = Point::new_scale(m_origin - pointf); + m_last_pos += offset; + m_wipe.offset_path(offset); m_origin = pointf; } -std::string GCode::preamble() +std::string GCodeGenerator::preamble() { std::string gcode = m_writer.preamble(); @@ -2628,8 +2390,8 @@ std::string GCode::preamble() return gcode; } -// called by GCode::process_layer() -std::string GCode::change_layer(coordf_t print_z) +// called by GCodeGenerator::process_layer() +std::string GCodeGenerator::change_layer(coordf_t print_z) { std::string gcode; if (m_layer_count > 0) @@ -2651,198 +2413,135 @@ std::string GCode::change_layer(coordf_t print_z) return gcode; } -std::string GCode::extrude_loop(ExtrusionLoop loop, const std::string_view description, double speed) +std::string GCodeGenerator::extrude_loop(const ExtrusionLoop &loop_src, const GCode::SmoothPathCache *smooth_path_cache, const std::string_view description, double speed) { - // get a copy; don't modify the orientation of the original loop object otherwise - // next copies (if any) would not detect the correct orientation - - // extrude all loops ccw - bool was_clockwise = loop.make_counter_clockwise(); - - // find the point of the loop that is closest to the current extruder position - // or randomize if requested - Point last_pos = this->last_pos(); - + // Extrude all loops CCW. + bool is_hole = loop_src.is_clockwise(); + Point seam_point = this->last_pos(); if (! m_config.spiral_vase && comment_is_perimeter(description)) { assert(m_layer != nullptr); - m_seam_placer.place_seam(m_layer, loop, m_config.external_perimeters_first, this->last_pos()); - } else - // Because the G-code export has 1um resolution, don't generate segments shorter than 1.5 microns, - // thus empty path segments will not be produced by G-code export. - loop.split_at(last_pos, false, scaled(0.0015)); - - for (auto it = std::next(loop.paths.begin()); it != loop.paths.end(); ++it) { - assert(it->polyline.points.size() >= 2); - assert(std::prev(it)->polyline.last_point() == it->polyline.first_point()); + seam_point = m_seam_placer.place_seam(m_layer, loop_src, m_config.external_perimeters_first, this->last_pos()); } - assert(loop.paths.front().first_point() == loop.paths.back().last_point()); + // Because the G-code export has 1um resolution, don't generate segments shorter than 1.5 microns, + // thus empty path segments will not be produced by G-code export. + GCode::SmoothPath smooth_path = smooth_path_cache->resolve_or_fit_split_with_seam( + loop_src, is_hole, m_scaled_resolution, seam_point, scaled(0.0015)); - // clip the path to avoid the extruder to get exactly on the first point of the loop; + // Clip the path to avoid the extruder to get exactly on the first point of the loop; // if polyline was shorter than the clipping distance we'd get a null polyline, so - // we discard it in that case - double clip_length = m_enable_loop_clipping ? - scale_(EXTRUDER_CONFIG(nozzle_diameter)) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER : - 0; + // we discard it in that case. + if (m_enable_loop_clipping) + clip_end(smooth_path, scale_(EXTRUDER_CONFIG(nozzle_diameter)) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER); - // get paths - ExtrusionPaths paths; - loop.clip_end(clip_length, &paths); - if (paths.empty()) return ""; + if (smooth_path.empty()) + return {}; - // apply the small perimeter speed - if (paths.front().role().is_perimeter() && loop.length() <= SMALL_PERIMETER_LENGTH && speed == -1) +#ifndef NDEBUG + for (auto it = std::next(smooth_path.begin()); it != smooth_path.end(); ++ it) { + assert(it->path.size() >= 2); + assert(std::prev(it)->path.back().point == it->path.front().point); + } + assert(m_enable_loop_clipping || smooth_path.front().path.front().point == smooth_path.back().path.back().point); +#endif //NDEBUG + + // Apply the small perimeter speed. + if (loop_src.paths.front().role().is_perimeter() && loop_src.length() <= SMALL_PERIMETER_LENGTH && speed == -1) speed = m_config.small_perimeter_speed.get_abs_value(m_config.perimeter_speed); - // extrude along the path + // Extrude along the smooth path. std::string gcode; - for (ExtrusionPath &path : paths) { - path.simplify(m_scaled_resolution); - gcode += this->_extrude(path, description, speed); - } + for (const GCode::SmoothPathElement &el : smooth_path) + gcode += this->_extrude(el.path_attributes, el.path, description, speed); // reset acceleration - gcode += m_writer.set_print_acceleration((unsigned int)(m_config.default_acceleration.value + 0.5)); + gcode += m_writer.set_print_acceleration(fast_round_up(m_config.default_acceleration.value)); - if (m_wipe.enable) { - m_wipe.path = paths.front().polyline; - - for (auto it = std::next(paths.begin()); it != paths.end(); ++it) { - if (it->role().is_bridge()) - break; // Don't perform a wipe on bridges. - - assert(it->polyline.points.size() >= 2); - assert(m_wipe.path.points.back() == it->polyline.first_point()); - if (m_wipe.path.points.back() != it->polyline.first_point()) - break; // ExtrusionLoop is interrupted in some place. - - m_wipe.path.points.insert(m_wipe.path.points.end(), it->polyline.points.begin() + 1, it->polyline.points.end()); - } - } - - // make a little move inwards before leaving loop - if (paths.back().role().is_external_perimeter() && m_layer != NULL && m_config.perimeters.value > 1 && paths.front().size() >= 2 && paths.back().polyline.points.size() >= 3) { - // detect angle between last and first segment - // the side depends on the original winding order of the polygon (left for contours, right for holes) - //FIXME improve the algorithm in case the loop is tiny. - //FIXME improve the algorithm in case the loop is split into segments with a low number of points (see the Point b query). - // Angle from the 2nd point to the last point. - double angle_inside = angle(paths.front().polyline.points[1] - paths.front().first_point(), - *(paths.back().polyline.points.end()-3) - paths.front().first_point()); - assert(angle_inside >= -M_PI && angle_inside <= M_PI); - // 3rd of this angle will be taken, thus make the angle monotonic before interpolation. - if (was_clockwise) { - if (angle_inside > 0) - angle_inside -= 2.0 * M_PI; - } else { - if (angle_inside < 0) - angle_inside += 2.0 * M_PI; - } - - // create the destination point along the first segment and rotate it - // we make sure we don't exceed the segment length because we don't know - // the rotation of the second segment so we might cross the object boundary - Vec2d p1 = paths.front().polyline.points.front().cast(); - Vec2d p2 = paths.front().polyline.points[1].cast(); - Vec2d v = p2 - p1; - double nd = scale_(EXTRUDER_CONFIG(nozzle_diameter)); - double l2 = v.squaredNorm(); - // Shift by no more than a nozzle diameter. - //FIXME Hiding the seams will not work nicely for very densely discretized contours! - Point pt = ((nd * nd >= l2) ? p2 : (p1 + v * (nd / sqrt(l2)))).cast(); - // Rotate pt inside around the seam point. - pt.rotate(angle_inside / 3., paths.front().polyline.points.front()); - // generate the travel move - gcode += m_writer.travel_to_xy(this->point_to_gcode(pt), "move inwards before travel"); + if (m_wipe.enabled()) { + m_wipe.set_path(std::move(smooth_path), false); + } else if (loop_src.paths.back().role().is_external_perimeter() && m_layer != nullptr && m_config.perimeters.value > 1) { + // Only wipe inside if the wipe along the perimeter is disabled. + // Make a little move inwards before leaving loop. + if (std::optional pt = wipe_hide_seam(smooth_path, is_hole, scale_(EXTRUDER_CONFIG(nozzle_diameter))); pt) + // Generate the seam hiding travel move. + gcode += m_writer.travel_to_xy(this->point_to_gcode(*pt), "move inwards before travel"); } return gcode; } -std::string GCode::extrude_multi_path(ExtrusionMultiPath multipath, const std::string_view description, double speed) +std::string GCodeGenerator::extrude_multi_path(const ExtrusionMultiPath &multipath, bool reverse, const GCode::SmoothPathCache *smooth_path_cache, const std::string_view description, double speed) { - for (auto it = std::next(multipath.paths.begin()); it != multipath.paths.end(); ++it) { +#ifndef NDEBUG + for (auto it = std::next(multipath.paths.begin()); it != multipath.paths.end(); ++ it) { assert(it->polyline.points.size() >= 2); assert(std::prev(it)->polyline.last_point() == it->polyline.first_point()); } +#endif // NDEBUG + GCode::SmoothPath smooth_path = smooth_path_cache->resolve_or_fit(multipath, reverse, m_scaled_resolution); + // extrude along the path std::string gcode; - for (ExtrusionPath path : multipath.paths) { - path.simplify(m_scaled_resolution); - gcode += this->_extrude(path, description, speed); - } - if (m_wipe.enable) { - m_wipe.path = std::move(multipath.paths.back().polyline); - m_wipe.path.reverse(); - - for (auto it = std::next(multipath.paths.rbegin()); it != multipath.paths.rend(); ++it) { - if (it->role().is_bridge()) - break; // Do not perform a wipe on bridges. - - assert(it->polyline.points.size() >= 2); - assert(m_wipe.path.points.back() == it->polyline.last_point()); - if (m_wipe.path.points.back() != it->polyline.last_point()) - break; // ExtrusionMultiPath is interrupted in some place. - - m_wipe.path.points.insert(m_wipe.path.points.end(), it->polyline.points.rbegin() + 1, it->polyline.points.rend()); - } - } + for (GCode::SmoothPathElement &el : smooth_path) + gcode += this->_extrude(el.path_attributes, el.path, description, speed); + m_wipe.set_path(std::move(smooth_path), true); // reset acceleration gcode += m_writer.set_print_acceleration((unsigned int)floor(m_config.default_acceleration.value + 0.5)); return gcode; } -std::string GCode::extrude_entity(const ExtrusionEntity &entity, const std::string_view description, double speed) +std::string GCodeGenerator::extrude_entity(const ExtrusionEntityReference &entity, const GCode::SmoothPathCache *smooth_path_cache, const std::string_view description, double speed) { - if (const ExtrusionPath* path = dynamic_cast(&entity)) - return this->extrude_path(*path, description, speed); - else if (const ExtrusionMultiPath* multipath = dynamic_cast(&entity)) - return this->extrude_multi_path(*multipath, description, speed); - else if (const ExtrusionLoop* loop = dynamic_cast(&entity)) - return this->extrude_loop(*loop, description, speed); + if (const ExtrusionPath *path = dynamic_cast(&entity.extrusion_entity())) + return this->extrude_path(*path, entity.flipped(), smooth_path_cache, description, speed); + else if (const ExtrusionMultiPath *multipath = dynamic_cast(&entity.extrusion_entity())) + return this->extrude_multi_path(*multipath, entity.flipped(), smooth_path_cache, description, speed); + else if (const ExtrusionLoop *loop = dynamic_cast(&entity.extrusion_entity())) + return this->extrude_loop(*loop, smooth_path_cache, description, speed); else throw Slic3r::InvalidArgument("Invalid argument supplied to extrude()"); - return ""; + return {}; } -std::string GCode::extrude_path(ExtrusionPath path, std::string_view description, double speed) +std::string GCodeGenerator::extrude_path(const ExtrusionPath &path, bool reverse, const GCode::SmoothPathCache *smooth_path_cache, std::string_view description, double speed) { - path.simplify(m_scaled_resolution); - std::string gcode = this->_extrude(path, description, speed); - if (m_wipe.enable) { - m_wipe.path = std::move(path.polyline); - m_wipe.path.reverse(); - } + Geometry::ArcWelder::Path smooth_path = smooth_path_cache->resolve_or_fit(path, reverse, m_scaled_resolution); + std::string gcode = this->_extrude(path.attributes(), smooth_path, description, speed); + Geometry::ArcWelder::reverse(smooth_path); + m_wipe.set_path(std::move(smooth_path)); // reset acceleration gcode += m_writer.set_print_acceleration((unsigned int)floor(m_config.default_acceleration.value + 0.5)); return gcode; } -std::string GCode::extrude_support(const ExtrusionEntityCollection &support_fills) +std::string GCodeGenerator::extrude_support(const ExtrusionEntityReferences &support_fills, const GCode::SmoothPathCache *smooth_path_cache) { static constexpr const auto support_label = "support material"sv; static constexpr const auto support_interface_label = "support material interface"sv; std::string gcode; - if (! support_fills.entities.empty()) { + if (! support_fills.empty()) { const double support_speed = m_config.support_material_speed.value; const double support_interface_speed = m_config.support_material_interface_speed.get_abs_value(support_speed); - for (const ExtrusionEntity *ee : support_fills.entities) { - ExtrusionRole role = ee->role(); + for (const ExtrusionEntityReference &eref : support_fills) { + ExtrusionRole role = eref.extrusion_entity().role(); assert(role == ExtrusionRole::SupportMaterial || role == ExtrusionRole::SupportMaterialInterface); const auto label = (role == ExtrusionRole::SupportMaterial) ? support_label : support_interface_label; const double speed = (role == ExtrusionRole::SupportMaterial) ? support_speed : support_interface_speed; - const ExtrusionPath *path = dynamic_cast(ee); + const ExtrusionPath *path = dynamic_cast(&eref.extrusion_entity()); if (path) - gcode += this->extrude_path(*path, label, speed); + gcode += this->extrude_path(*path, eref.flipped(), smooth_path_cache, label, speed); + else if (const ExtrusionMultiPath *multipath = dynamic_cast(&eref.extrusion_entity()); multipath) + gcode += this->extrude_multi_path(*multipath, eref.flipped(), smooth_path_cache, label, speed); else { - const ExtrusionMultiPath *multipath = dynamic_cast(ee); - if (multipath) - gcode += this->extrude_multi_path(*multipath, label, speed); - else { - const ExtrusionEntityCollection *eec = dynamic_cast(ee); - assert(eec); - if (eec) - gcode += this->extrude_support(*eec); + const ExtrusionEntityCollection *eec = dynamic_cast(&eref.extrusion_entity()); + assert(eec); + if (eec) { + //FIXME maybe order the support here? + ExtrusionEntityReferences refs; + refs.reserve(eec->entities.size()); + std::transform(eec->entities.begin(), eec->entities.end(), std::back_inserter(refs), + [flipped = eref.flipped()](const ExtrusionEntity *ee) { return ExtrusionEntityReference{ *ee, flipped }; }); + gcode += this->extrude_support(refs, smooth_path_cache); } } } @@ -2850,17 +2549,17 @@ std::string GCode::extrude_support(const ExtrusionEntityCollection &support_fill return gcode; } -bool GCode::GCodeOutputStream::is_error() const +bool GCodeGenerator::GCodeOutputStream::is_error() const { return ::ferror(this->f); } -void GCode::GCodeOutputStream::flush() +void GCodeGenerator::GCodeOutputStream::flush() { ::fflush(this->f); } -void GCode::GCodeOutputStream::close() +void GCodeGenerator::GCodeOutputStream::close() { if (this->f) { ::fclose(this->f); @@ -2868,7 +2567,7 @@ void GCode::GCodeOutputStream::close() } } -void GCode::GCodeOutputStream::write(const char *what) +void GCodeGenerator::GCodeOutputStream::write(const char *what) { if (what != nullptr) { //FIXME don't allocate a string, maybe process a batch of lines? @@ -2879,13 +2578,13 @@ void GCode::GCodeOutputStream::write(const char *what) } } -void GCode::GCodeOutputStream::writeln(const std::string &what) +void GCodeGenerator::GCodeOutputStream::writeln(const std::string &what) { if (! what.empty()) this->write(what.back() == '\n' ? what : what + '\n'); } -void GCode::GCodeOutputStream::write_format(const char* format, ...) +void GCodeGenerator::GCodeOutputStream::write_format(const char* format, ...) { va_list args; va_start(args, format); @@ -2917,18 +2616,22 @@ void GCode::GCodeOutputStream::write_format(const char* format, ...) va_end(args); } -std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view description, double speed) +std::string GCodeGenerator::_extrude( + const ExtrusionAttributes &path_attr, + const Geometry::ArcWelder::Path &path, + const std::string_view description, + double speed) { std::string gcode; - const std::string_view description_bridge = path.role().is_bridge() ? " (bridge)"sv : ""sv; + const std::string_view description_bridge = path_attr.role.is_bridge() ? " (bridge)"sv : ""sv; // go to first point of extrusion path - if (!m_last_pos_defined || m_last_pos != path.first_point()) { + if (!m_last_pos_defined || m_last_pos != path.front().point) { std::string comment = "move to first "; comment += description; comment += description_bridge; comment += " point"; - gcode += this->travel_to(path.first_point(), path.role(), comment); + gcode += this->travel_to(path.front().point, path_attr.role, comment); } // compensate retraction @@ -2941,17 +2644,17 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de acceleration = m_config.first_layer_acceleration.value; } else if (this->object_layer_over_raft() && m_config.first_layer_acceleration_over_raft.value > 0) { acceleration = m_config.first_layer_acceleration_over_raft.value; - } else if (m_config.bridge_acceleration.value > 0 && path.role().is_bridge()) { + } else if (m_config.bridge_acceleration.value > 0 && path_attr.role.is_bridge()) { acceleration = m_config.bridge_acceleration.value; - } else if (m_config.top_solid_infill_acceleration > 0 && path.role() == ExtrusionRole::TopSolidInfill) { + } else if (m_config.top_solid_infill_acceleration > 0 && path_attr.role == ExtrusionRole::TopSolidInfill) { acceleration = m_config.top_solid_infill_acceleration.value; - } else if (m_config.solid_infill_acceleration > 0 && path.role().is_solid_infill()) { + } else if (m_config.solid_infill_acceleration > 0 && path_attr.role.is_solid_infill()) { acceleration = m_config.solid_infill_acceleration.value; - } else if (m_config.infill_acceleration.value > 0 && path.role().is_infill()) { + } else if (m_config.infill_acceleration.value > 0 && path_attr.role.is_infill()) { acceleration = m_config.infill_acceleration.value; - } else if (m_config.external_perimeter_acceleration > 0 && path.role().is_external_perimeter()) { + } else if (m_config.external_perimeter_acceleration > 0 && path_attr.role.is_external_perimeter()) { acceleration = m_config.external_perimeter_acceleration.value; - } else if (m_config.perimeter_acceleration.value > 0 && path.role().is_perimeter()) { + } else if (m_config.perimeter_acceleration.value > 0 && path_attr.role.is_perimeter()) { acceleration = m_config.perimeter_acceleration.value; } else { acceleration = m_config.default_acceleration.value; @@ -2960,36 +2663,36 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de } // calculate extrusion length per distance unit - double e_per_mm = m_writer.extruder()->e_per_mm3() * path.mm3_per_mm; + double e_per_mm = m_writer.extruder()->e_per_mm3() * path_attr.mm3_per_mm; if (m_writer.extrusion_axis().empty()) // gcfNoExtrusion e_per_mm = 0; // set speed if (speed == -1) { - if (path.role() == ExtrusionRole::Perimeter) { + if (path_attr.role == ExtrusionRole::Perimeter) { speed = m_config.get_abs_value("perimeter_speed"); - } else if (path.role() == ExtrusionRole::ExternalPerimeter) { + } else if (path_attr.role == ExtrusionRole::ExternalPerimeter) { speed = m_config.get_abs_value("external_perimeter_speed"); - } else if (path.role().is_bridge()) { - assert(path.role().is_perimeter() || path.role() == ExtrusionRole::BridgeInfill); + } else if (path_attr.role.is_bridge()) { + assert(path_attr.role.is_perimeter() || path_attr.role == ExtrusionRole::BridgeInfill); speed = m_config.get_abs_value("bridge_speed"); - } else if (path.role() == ExtrusionRole::InternalInfill) { + } else if (path_attr.role == ExtrusionRole::InternalInfill) { speed = m_config.get_abs_value("infill_speed"); - } else if (path.role() == ExtrusionRole::SolidInfill) { + } else if (path_attr.role == ExtrusionRole::SolidInfill) { speed = m_config.get_abs_value("solid_infill_speed"); - } else if (path.role() == ExtrusionRole::TopSolidInfill) { + } else if (path_attr.role == ExtrusionRole::TopSolidInfill) { speed = m_config.get_abs_value("top_solid_infill_speed"); - } else if (path.role() == ExtrusionRole::Ironing) { + } else if (path_attr.role == ExtrusionRole::Ironing) { speed = m_config.get_abs_value("ironing_speed"); - } else if (path.role() == ExtrusionRole::GapFill) { + } else if (path_attr.role == ExtrusionRole::GapFill) { speed = m_config.get_abs_value("gap_fill_speed"); } else { throw Slic3r::InvalidArgument("Invalid speed"); } } if (m_volumetric_speed != 0. && speed == 0) - speed = m_volumetric_speed / path.mm3_per_mm; + speed = m_volumetric_speed / path_attr.mm3_per_mm; if (this->on_first_layer()) speed = m_config.get_abs_value("first_layer_speed", speed); else if (this->object_layer_over_raft()) @@ -2998,21 +2701,21 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de // cap speed with max_volumetric_speed anyway (even if user is not using autospeed) speed = std::min( speed, - m_config.max_volumetric_speed.value / path.mm3_per_mm + m_config.max_volumetric_speed.value / path_attr.mm3_per_mm ); } if (EXTRUDER_CONFIG(filament_max_volumetric_speed) > 0) { // cap speed with max_volumetric_speed anyway (even if user is not using autospeed) speed = std::min( speed, - EXTRUDER_CONFIG(filament_max_volumetric_speed) / path.mm3_per_mm + EXTRUDER_CONFIG(filament_max_volumetric_speed) / path_attr.mm3_per_mm ); } bool variable_speed_or_fan_speed = false; std::vector new_points{}; if ((this->m_config.enable_dynamic_overhang_speeds || this->config().enable_dynamic_fan_speeds.get_at(m_writer.extruder()->id())) && - !this->on_first_layer() && path.role().is_perimeter()) { + !this->on_first_layer() && path_attr.role.is_perimeter()) { std::vector> overhangs_with_speeds = {{100, ConfigOptionFloatOrPercent{speed, false}}}; if (this->m_config.enable_dynamic_overhang_speeds) { overhangs_with_speeds = {{0, m_config.overhang_speed_0}, @@ -3033,17 +2736,20 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de double external_perim_reference_speed = m_config.get_abs_value("external_perimeter_speed"); if (external_perim_reference_speed == 0) - external_perim_reference_speed = m_volumetric_speed / path.mm3_per_mm; + external_perim_reference_speed = m_volumetric_speed / path_attr.mm3_per_mm; if (m_config.max_volumetric_speed.value > 0) - external_perim_reference_speed = std::min(external_perim_reference_speed, m_config.max_volumetric_speed.value / path.mm3_per_mm); + external_perim_reference_speed = std::min(external_perim_reference_speed, m_config.max_volumetric_speed.value / path_attr.mm3_per_mm); if (EXTRUDER_CONFIG(filament_max_volumetric_speed) > 0) { external_perim_reference_speed = std::min(external_perim_reference_speed, - EXTRUDER_CONFIG(filament_max_volumetric_speed) / path.mm3_per_mm); + EXTRUDER_CONFIG(filament_max_volumetric_speed) / path_attr.mm3_per_mm); } - new_points = m_extrusion_quality_estimator.estimate_speed_from_extrusion_quality(path, overhangs_with_speeds, overhang_w_fan_speeds, - m_writer.extruder()->id(), external_perim_reference_speed, - speed); + new_points = m_extrusion_quality_estimator.estimate_speed_from_extrusion_quality( + //FIXME convert estimate_speed_from_extrusion_quality() to smooth paths or move it before smooth path interpolation. + Points{}, + path_attr, overhangs_with_speeds, overhang_w_fan_speeds, + m_writer.extruder()->id(), external_perim_reference_speed, + speed); variable_speed_or_fan_speed = std::any_of(new_points.begin(), new_points.end(), [speed](const ProcessedPoint &p) { return p.speed != speed || p.fan_speed != 0; }); } @@ -3053,7 +2759,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de // extrude arc or line if (m_enable_extrusion_role_markers) { - if (GCodeExtrusionRole role = extrusion_role_to_gcode_extrusion_role(path.role()); role != m_last_extrusion_role) + if (GCodeExtrusionRole role = extrusion_role_to_gcode_extrusion_role(path_attr.role); role != m_last_extrusion_role) { m_last_extrusion_role = role; if (m_enable_extrusion_role_markers) @@ -3071,29 +2777,29 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de bool last_was_wipe_tower = (m_last_processor_extrusion_role == GCodeExtrusionRole::WipeTower); assert(is_decimal_separator_point()); - if (GCodeExtrusionRole role = extrusion_role_to_gcode_extrusion_role(path.role()); role != m_last_processor_extrusion_role) { + if (GCodeExtrusionRole role = extrusion_role_to_gcode_extrusion_role(path_attr.role); role != m_last_processor_extrusion_role) { m_last_processor_extrusion_role = role; char buf[64]; sprintf(buf, ";%s%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role).c_str(), gcode_extrusion_role_to_string(m_last_processor_extrusion_role).c_str()); gcode += buf; } - if (last_was_wipe_tower || m_last_width != path.width) { - m_last_width = path.width; + if (last_was_wipe_tower || m_last_width != path_attr.width) { + m_last_width = path_attr.width; gcode += std::string(";") + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Width) + float_to_string_decimal_point(m_last_width) + "\n"; } #if ENABLE_GCODE_VIEWER_DATA_CHECKING - if (last_was_wipe_tower || (m_last_mm3_per_mm != path.mm3_per_mm)) { - m_last_mm3_per_mm = path.mm3_per_mm; + if (last_was_wipe_tower || (m_last_mm3_per_mm != path_attr.mm3_per_mm)) { + m_last_mm3_per_mm = path_attr.mm3_per_mm; gcode += std::string(";") + GCodeProcessor::Mm3_Per_Mm_Tag + float_to_string_decimal_point(m_last_mm3_per_mm) + "\n"; } #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING - if (last_was_wipe_tower || std::abs(m_last_height - path.height) > EPSILON) { - m_last_height = path.height; + if (last_was_wipe_tower || std::abs(m_last_height - path_attr.height) > EPSILON) { + m_last_height = path_attr.height; gcode += std::string(";") + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Height) + float_to_string_decimal_point(m_last_height) + "\n"; @@ -3101,15 +2807,16 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de std::string cooling_marker_setspeed_comments; if (m_enable_cooling_markers) { - if (path.role().is_bridge()) + if (path_attr.role.is_bridge()) gcode += ";_BRIDGE_FAN_START\n"; else cooling_marker_setspeed_comments = ";_EXTRUDE_SET_SPEED"; - if (path.role() == ExtrusionRole::ExternalPerimeter) + if (path_attr.role == ExtrusionRole::ExternalPerimeter) cooling_marker_setspeed_comments += ";_EXTERNAL_PERIMETER"; } - if (!variable_speed_or_fan_speed) { + //FIXME Variable speed on overhangs is inactive until the code is adapted to smooth path. + if (! variable_speed_or_fan_speed) { // F is mm per minute. gcode += m_writer.set_speed(F, "", cooling_marker_setspeed_comments); double path_length = 0.; @@ -3118,14 +2825,25 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de comment = description; comment += description_bridge; } - Vec2d prev = this->point_to_gcode_quantized(path.polyline.points.front()); - auto it = path.polyline.points.begin(); - auto end = path.polyline.points.end(); + Vec2d prev = this->point_to_gcode_quantized(path.front().point); + auto it = path.begin(); + auto end = path.end(); for (++ it; it != end; ++ it) { - Vec2d p = this->point_to_gcode_quantized(*it); - const double line_length = (p - prev).norm(); - path_length += line_length; - gcode += m_writer.extrude_to_xy(p, e_per_mm * line_length, comment); + Vec2d p = this->point_to_gcode_quantized(it->point); + if (it->radius == 0) { + // Extrude line segment. + const double line_length = (p - prev).norm(); + path_length += line_length; + gcode += m_writer.extrude_to_xy(p, e_per_mm * line_length, comment); + } else { + // Extrude an arc. + double radius = GCodeFormatter::quantize_xyzf(it->radius); + Vec2f center = Geometry::ArcWelder::arc_center(prev.cast(), p.cast(), float(radius), it->ccw()); + float angle = Geometry::ArcWelder::arc_angle(prev.cast(), p.cast(), float(radius)); + const double line_length = angle * std::abs(radius); + path_length += line_length; + gcode += m_writer.extrude_to_xy_G2G3R(p, radius, it->ccw(), e_per_mm * line_length, comment); + } prev = p; } } else { @@ -3159,14 +2877,14 @@ std::string GCode::_extrude(const ExtrusionPath &path, const std::string_view de } if (m_enable_cooling_markers) - gcode += path.role().is_bridge() ? ";_BRIDGE_FAN_END\n" : ";_EXTRUDE_END\n"; + gcode += path_attr.role.is_bridge() ? ";_BRIDGE_FAN_END\n" : ";_EXTRUDE_END\n"; - this->set_last_pos(path.last_point()); + this->set_last_pos(path.back().point); return gcode; } // This method accepts &point in print coordinates. -std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string comment) +std::string GCodeGenerator::travel_to(const Point &point, ExtrusionRole role, std::string comment) { /* Define the travel move as a line between current position and the taget point. This is expressed in print coordinates, so it will need to be translated by @@ -3248,7 +2966,7 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string return gcode; } -bool GCode::needs_retraction(const Polyline &travel, ExtrusionRole role) +bool GCodeGenerator::needs_retraction(const Polyline &travel, ExtrusionRole role) { if (travel.length() < scale_(EXTRUDER_CONFIG(retract_before_travel))) { // skip retraction if the move is shorter than the configured threshold @@ -3287,7 +3005,7 @@ bool GCode::needs_retraction(const Polyline &travel, ExtrusionRole role) return true; } -std::string GCode::retract(bool toolchange) +std::string GCodeGenerator::retract(bool toolchange) { std::string gcode; @@ -3313,7 +3031,7 @@ std::string GCode::retract(bool toolchange) return gcode; } -std::string GCode::set_extruder(unsigned int extruder_id, double print_z) +std::string GCodeGenerator::set_extruder(unsigned int extruder_id, double print_z) { if (!m_writer.need_toolchange(extruder_id)) return ""; @@ -3415,20 +3133,7 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z) } // convert a model-space scaled point into G-code coordinates -Vec2d GCode::point_to_gcode(const Point &point) const -{ - Vec2d extruder_offset = EXTRUDER_CONFIG(extruder_offset); - return unscaled(point) + m_origin - extruder_offset; -} - -Vec2d GCode::point_to_gcode_quantized(const Point &point) const -{ - Vec2d p = this->point_to_gcode(point); - return { GCodeFormatter::quantize_xyzf(p.x()), GCodeFormatter::quantize_xyzf(p.y()) }; -} - -// convert a model-space scaled point into G-code coordinates -Point GCode::gcode_to_point(const Vec2d &point) const +Point GCodeGenerator::gcode_to_point(const Vec2d &point) const { Vec2d pt = point - m_origin; if (const Extruder *extruder = m_writer.extruder(); extruder) diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 346ececba7..42d6996e6a 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -5,18 +5,22 @@ #include "JumpPointSearch.hpp" #include "libslic3r.h" #include "ExPolygon.hpp" -#include "GCodeWriter.hpp" #include "Layer.hpp" #include "Point.hpp" #include "PlaceholderParser.hpp" #include "PrintConfig.hpp" +#include "Geometry/ArcWelder.hpp" #include "GCode/AvoidCrossingPerimeters.hpp" #include "GCode/CoolingBuffer.hpp" #include "GCode/FindReplace.hpp" +#include "GCode/GCodeWriter.hpp" +#include "GCode/PressureEqualizer.hpp" #include "GCode/RetractWhenCrossingPerimeters.hpp" +#include "GCode/SmoothPath.hpp" #include "GCode/SpiralVase.hpp" #include "GCode/ToolOrdering.hpp" -#include "GCode/WipeTower.hpp" +#include "GCode/Wipe.hpp" +#include "GCode/WipeTowerIntegration.hpp" #include "GCode/SeamPlacer.hpp" #include "GCode/GCodeProcessor.hpp" #include "EdgeGrid.hpp" @@ -26,12 +30,10 @@ #include #include -#include "GCode/PressureEqualizer.hpp" - namespace Slic3r { // Forward declarations. -class GCode; +class GCodeGenerator; namespace { struct Item; } struct PrintInstance; @@ -41,71 +43,11 @@ public: bool enable; OozePrevention() : enable(false) {} - std::string pre_toolchange(GCode &gcodegen); - std::string post_toolchange(GCode &gcodegen); + std::string pre_toolchange(GCodeGenerator &gcodegen); + std::string post_toolchange(GCodeGenerator &gcodegen); private: - int _get_temp(const GCode &gcodegen) const; -}; - -class Wipe { -public: - bool enable; - Polyline path; - - Wipe() : enable(false) {} - bool has_path() const { return ! this->path.empty(); } - void reset_path() { this->path.clear(); } - std::string wipe(GCode &gcodegen, bool toolchange); -}; - -class WipeTowerIntegration { -public: - WipeTowerIntegration( - const PrintConfig &print_config, - const std::vector &priming, - const std::vector> &tool_changes, - const WipeTower::ToolChangeResult &final_purge) : - m_left(/*float(print_config.wipe_tower_x.value)*/ 0.f), - m_right(float(/*print_config.wipe_tower_x.value +*/ print_config.wipe_tower_width.value)), - m_wipe_tower_pos(float(print_config.wipe_tower_x.value), float(print_config.wipe_tower_y.value)), - m_wipe_tower_rotation(float(print_config.wipe_tower_rotation_angle)), - m_extruder_offsets(print_config.extruder_offset.values), - m_priming(priming), - m_tool_changes(tool_changes), - m_final_purge(final_purge), - m_layer_idx(-1), - m_tool_change_idx(0) - {} - - std::string prime(GCode &gcodegen); - void next_layer() { ++ m_layer_idx; m_tool_change_idx = 0; } - std::string tool_change(GCode &gcodegen, int extruder_id, bool finish_layer); - std::string finalize(GCode &gcodegen); - std::vector used_filament_length() const; - -private: - WipeTowerIntegration& operator=(const WipeTowerIntegration&); - std::string append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id, double z = -1.) const; - - // Postprocesses gcode: rotates and moves G1 extrusions and returns result - std::string post_process_wipe_tower_moves(const WipeTower::ToolChangeResult& tcr, const Vec2f& translation, float angle) const; - - // Left / right edges of the wipe tower, for the planning of wipe moves. - const float m_left; - const float m_right; - const Vec2f m_wipe_tower_pos; - const float m_wipe_tower_rotation; - const std::vector m_extruder_offsets; - - // Reference to cached values at the Printer class. - const std::vector &m_priming; - const std::vector> &m_tool_changes; - const WipeTower::ToolChangeResult &m_final_purge; - // Current layer index. - int m_layer_idx; - int m_tool_change_idx; - double m_last_wipe_tower_print_z = 0.f; + int _get_temp(const GCodeGenerator &gcodegen) const; }; class ColorPrintColors @@ -129,9 +71,9 @@ struct LayerResult { static LayerResult make_nop_layer_result() { return {"", std::numeric_limits::max(), false, false, true}; } }; -class GCode { +class GCodeGenerator { public: - GCode() : + GCodeGenerator() : m_origin(Vec2d::Zero()), m_enable_loop_clipping(true), m_enable_cooling_markers(false), @@ -153,7 +95,7 @@ public: m_silent_time_estimator_enabled(false), m_last_obj_copy(nullptr, Point(std::numeric_limits::max(), std::numeric_limits::max())) {} - ~GCode() = default; + ~GCodeGenerator() = default; // throws std::runtime_exception on error, // throws CanceledException through print->throw_if_canceled(). @@ -165,9 +107,19 @@ public: void set_origin(const coordf_t x, const coordf_t y) { this->set_origin(Vec2d(x, y)); } const Point& last_pos() const { return m_last_pos; } // Convert coordinates of the active object to G-code coordinates, possibly adjusted for extruder offset. - Vec2d point_to_gcode(const Point &point) const; + template + Vec2d point_to_gcode(const Eigen::MatrixBase &point) const { + static_assert(Derived::IsVectorAtCompileTime && int(Derived::SizeAtCompileTime) == 2, "GCodeGenerator::point_to_gcode(): first parameter is not a 2D vector"); + return Vec2d(unscaled(point.x()), unscaled(point.y())) + m_origin + - m_config.extruder_offset.get_at(m_writer.extruder()->id()); + } // Convert coordinates of the active object to G-code coordinates, possibly adjusted for extruder offset and quantized to G-code resolution. - Vec2d point_to_gcode_quantized(const Point &point) const; + template + Vec2d point_to_gcode_quantized(const Eigen::MatrixBase &point) const { + static_assert(Derived::IsVectorAtCompileTime && int(Derived::SizeAtCompileTime) == 2, "GCodeGenerator::point_to_gcode_quantized(): first parameter is not a 2D vector"); + Vec2d p = this->point_to_gcode(point); + return { GCodeFormatter::quantize_xyzf(p.x()), GCodeFormatter::quantize_xyzf(p.y()) }; + } Point gcode_to_point(const Vec2d &point) const; const FullPrintConfig &config() const { return m_config; } const Layer* layer() const { return m_layer; } @@ -250,6 +202,7 @@ private: // Set of object & print layers of the same PrintObject and with the same print_z. const ObjectsLayerToPrint &layers, const LayerTools &layer_tools, + const GCode::SmoothPathCache *smooth_path_cache, const bool last_layer, // Pairs of PrintObject index and its instance index. const std::vector *ordering, @@ -280,10 +233,10 @@ private: void set_extruders(const std::vector &extruder_ids); std::string preamble(); std::string change_layer(coordf_t print_z); - std::string extrude_entity(const ExtrusionEntity &entity, const std::string_view description, double speed = -1.); - std::string extrude_loop(ExtrusionLoop loop, const std::string_view description, double speed = -1.); - std::string extrude_multi_path(ExtrusionMultiPath multipath, const std::string_view description, double speed = -1.); - std::string extrude_path(ExtrusionPath path, const std::string_view description, double speed = -1.); + std::string extrude_entity(const ExtrusionEntityReference &entity, const GCode::SmoothPathCache *smooth_path_cache, const std::string_view description, double speed = -1.); + std::string extrude_loop(const ExtrusionLoop &loop, const GCode::SmoothPathCache *smooth_path_cache, const std::string_view description, double speed = -1.); + std::string extrude_multi_path(const ExtrusionMultiPath &multipath, bool reverse, const GCode::SmoothPathCache *smooth_path_cache, const std::string_view description, double speed = -1.); + std::string extrude_path(const ExtrusionPath &path, bool reverse, const GCode::SmoothPathCache *smooth_path_cache, const std::string_view description, double speed = -1.); struct InstanceToPrint { @@ -317,12 +270,14 @@ private: const ObjectLayerToPrint &layer_to_print, // Container for extruder overrides (when wiping into object or infill). const LayerTools &layer_tools, + // Optional smooth path interpolating extrusion polylines. + const GCode::SmoothPathCache *smooth_path_cache, // Is any extrusion possibly marked as wiping extrusion? const bool is_anything_overridden, // Round 1 (wiping into object or infill) or round 2 (normal extrusions). const bool print_wipe_extrusions); - std::string extrude_support(const ExtrusionEntityCollection &support_fills); + std::string extrude_support(const ExtrusionEntityReferences &support_fills, const GCode::SmoothPathCache *smooth_path_cache); std::string travel_to(const Point &point, ExtrusionRole role, std::string comment); bool needs_retraction(const Polyline &travel, ExtrusionRole role = ExtrusionRole::None); @@ -375,7 +330,7 @@ private: } m_placeholder_parser_integration; OozePrevention m_ooze_prevention; - Wipe m_wipe; + GCode::Wipe m_wipe; AvoidCrossingPerimeters m_avoid_crossing_perimeters; JPSPathFinder m_avoid_crossing_curled_overhangs; RetractWhenCrossingPerimeters m_retract_when_crossing_perimeters; @@ -418,7 +373,7 @@ private: std::unique_ptr m_spiral_vase; std::unique_ptr m_find_replace; std::unique_ptr m_pressure_equalizer; - std::unique_ptr m_wipe_tower; + std::unique_ptr m_wipe_tower; // Heights (print_z) at which the skirt has already been extruded. std::vector m_skirt_done; @@ -434,7 +389,8 @@ private: // Processor GCodeProcessor m_processor; - std::string _extrude(const ExtrusionPath &path, const std::string_view description, double speed = -1); + std::string _extrude( + const ExtrusionAttributes &attribs, const Geometry::ArcWelder::Path &path, const std::string_view description, double speed = -1); void print_machine_envelope(GCodeOutputStream &file, Print &print); void _print_first_layer_bed_temperature(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait); void _print_first_layer_extruder_temperatures(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait); @@ -443,8 +399,12 @@ private: // To control print speed of 1st object layer over raft interface. bool object_layer_over_raft() const { return m_object_layer_over_raft; } - friend class Wipe; - friend class WipeTowerIntegration; + // Fill in cache of smooth paths for perimeters, fills and supports of the given object layers. + // Based on params, the paths are either decimated to sparser polylines, or interpolated with circular arches. + static void smooth_path_interpolate(const ObjectLayerToPrint &layers, const GCode::SmoothPathCache::InterpolationParameters ¶ms, GCode::SmoothPathCache &out); + + friend class GCode::Wipe; + friend class GCode::WipeTowerIntegration; friend class PressureEqualizer; }; diff --git a/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp b/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp index c866e13e4f..c7dd09b785 100644 --- a/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp +++ b/src/libslic3r/GCode/AvoidCrossingPerimeters.cpp @@ -730,7 +730,7 @@ static bool any_expolygon_contains(const ExPolygons &ex_polygons, const std::vec return false; } -static bool need_wipe(const GCode &gcodegen, +static bool need_wipe(const GCodeGenerator &gcodegen, const ExPolygons &lslices_offset, const std::vector &lslices_offset_bboxes, const EdgeGrid::Grid &grid_lslices_offset, @@ -1167,7 +1167,7 @@ static void init_boundary(AvoidCrossingPerimeters::Boundary *boundary, Polygons } // Plan travel, which avoids perimeter crossings by following the boundaries of the layer. -Polyline AvoidCrossingPerimeters::travel_to(const GCode &gcodegen, const Point &point, bool *could_be_wipe_disabled) +Polyline AvoidCrossingPerimeters::travel_to(const GCodeGenerator &gcodegen, const Point &point, bool *could_be_wipe_disabled) { // If use_external, then perform the path planning in the world coordinate system (correcting for the gcodegen offset). // Otherwise perform the path planning in the coordinate system of the active object. @@ -1470,7 +1470,7 @@ static size_t avoid_perimeters(const AvoidCrossingPerimeters::Boundary &boundary } // Plan travel, which avoids perimeter crossings by following the boundaries of the layer. -Polyline AvoidCrossingPerimeters::travel_to(const GCode &gcodegen, const Point &point, bool *could_be_wipe_disabled) +Polyline AvoidCrossingPerimeters::travel_to(const GCodeGenerator &gcodegen, const Point &point, bool *could_be_wipe_disabled) { // If use_external, then perform the path planning in the world coordinate system (correcting for the gcodegen offset). // Otherwise perform the path planning in the coordinate system of the active object. diff --git a/src/libslic3r/GCode/AvoidCrossingPerimeters.hpp b/src/libslic3r/GCode/AvoidCrossingPerimeters.hpp index eb81c7972e..5e6d83f570 100644 --- a/src/libslic3r/GCode/AvoidCrossingPerimeters.hpp +++ b/src/libslic3r/GCode/AvoidCrossingPerimeters.hpp @@ -8,7 +8,7 @@ namespace Slic3r { // Forward declarations. -class GCode; +class GCodeGenerator; class Layer; class Point; @@ -25,13 +25,13 @@ public: void init_layer(const Layer &layer); - Polyline travel_to(const GCode& gcodegen, const Point& point) + Polyline travel_to(const GCodeGenerator &gcodegen, const Point& point) { bool could_be_wipe_disabled; return this->travel_to(gcodegen, point, &could_be_wipe_disabled); } - Polyline travel_to(const GCode& gcodegen, const Point& point, bool* could_be_wipe_disabled); + Polyline travel_to(const GCodeGenerator &gcodegen, const Point& point, bool* could_be_wipe_disabled); struct Boundary { // Collection of boundaries used for detection of crossing perimeters for travels diff --git a/src/libslic3r/GCode/ConflictChecker.hpp b/src/libslic3r/GCode/ConflictChecker.hpp index 344018f3d3..49ec3a4b13 100644 --- a/src/libslic3r/GCode/ConflictChecker.hpp +++ b/src/libslic3r/GCode/ConflictChecker.hpp @@ -43,7 +43,7 @@ public: void raise() { if (valid()) { - if (_piles[_curPileIdx].empty() == false) { _curHeight += _piles[_curPileIdx].front().height; } + if (_piles[_curPileIdx].empty() == false) { _curHeight += _piles[_curPileIdx].front().height(); } _curPileIdx++; } } diff --git a/src/libslic3r/GCode/CoolingBuffer.cpp b/src/libslic3r/GCode/CoolingBuffer.cpp index b574c95b37..9ac4233e00 100644 --- a/src/libslic3r/GCode/CoolingBuffer.cpp +++ b/src/libslic3r/GCode/CoolingBuffer.cpp @@ -19,7 +19,7 @@ namespace Slic3r { -CoolingBuffer::CoolingBuffer(GCode &gcodegen) : m_config(gcodegen.config()), m_toolchange_prefix(gcodegen.writer().toolchange_prefix()), m_current_extruder(0) +CoolingBuffer::CoolingBuffer(GCodeGenerator &gcodegen) : m_config(gcodegen.config()), m_toolchange_prefix(gcodegen.writer().toolchange_prefix()), m_current_extruder(0) { this->reset(gcodegen.writer().get_position()); diff --git a/src/libslic3r/GCode/CoolingBuffer.hpp b/src/libslic3r/GCode/CoolingBuffer.hpp index 91a81c7f31..b01a8ab985 100644 --- a/src/libslic3r/GCode/CoolingBuffer.hpp +++ b/src/libslic3r/GCode/CoolingBuffer.hpp @@ -7,7 +7,7 @@ namespace Slic3r { -class GCode; +class GCodeGenerator; class Layer; struct PerExtruderAdjustments; @@ -22,7 +22,7 @@ struct PerExtruderAdjustments; // class CoolingBuffer { public: - CoolingBuffer(GCode &gcodegen); + CoolingBuffer(GCodeGenerator &gcodegen); void reset(const Vec3d &position); void set_current_extruder(unsigned int extruder_id) { m_current_extruder = extruder_id; } std::string process_layer(std::string &&gcode, size_t layer_id, bool flush); @@ -51,7 +51,7 @@ private: // Highest of m_extruder_ids plus 1. unsigned int m_num_extruders { 0 }; const std::string m_toolchange_prefix; - // Referencs GCode::m_config, which is FullPrintConfig. While the PrintObjectConfig slice of FullPrintConfig is being modified, + // Referencs GCodeGenerator::m_config, which is FullPrintConfig. While the PrintObjectConfig slice of FullPrintConfig is being modified, // the PrintConfig slice of FullPrintConfig is constant, thus no thread synchronization is required. const PrintConfig &m_config; unsigned int m_current_extruder; diff --git a/src/libslic3r/GCode/ExtrusionProcessor.hpp b/src/libslic3r/GCode/ExtrusionProcessor.hpp index 968ba4024f..f6f75bef30 100644 --- a/src/libslic3r/GCode/ExtrusionProcessor.hpp +++ b/src/libslic3r/GCode/ExtrusionProcessor.hpp @@ -114,7 +114,7 @@ std::vector estimate_points_properties(const POINTS } new_points.push_back(next); } - points = new_points; + points = std::move(new_points); } if (max_line_length > 0) { @@ -140,7 +140,7 @@ std::vector estimate_points_properties(const POINTS } new_points.push_back(points.back()); } - points = new_points; + points = std::move(new_points); } std::vector angles_for_curvature(points.size()); @@ -241,7 +241,8 @@ public: } std::vector estimate_speed_from_extrusion_quality( - const ExtrusionPath &path, + const Points &path, + const ExtrusionFlow &flow, const std::vector> overhangs_w_speeds, const std::vector> overhangs_w_fan_speeds, size_t extruder_id, @@ -251,7 +252,7 @@ public: float speed_base = ext_perimeter_speed > 0 ? ext_perimeter_speed : original_speed; std::map speed_sections; for (size_t i = 0; i < overhangs_w_speeds.size(); i++) { - float distance = path.width * (1.0 - (overhangs_w_speeds[i].first / 100.0)); + float distance = flow.width * (1.0 - (overhangs_w_speeds[i].first / 100.0)); float speed = overhangs_w_speeds[i].second.percent ? (speed_base * overhangs_w_speeds[i].second.value / 100.0) : overhangs_w_speeds[i].second.value; if (speed < EPSILON) speed = speed_base; @@ -260,13 +261,13 @@ public: std::map fan_speed_sections; for (size_t i = 0; i < overhangs_w_fan_speeds.size(); i++) { - float distance = path.width * (1.0 - (overhangs_w_fan_speeds[i].first / 100.0)); + float distance = flow.width * (1.0 - (overhangs_w_fan_speeds[i].first / 100.0)); float fan_speed = overhangs_w_fan_speeds[i].second.get_at(extruder_id); fan_speed_sections[distance] = fan_speed; } std::vector extended_points = - estimate_points_properties(path.polyline.points, prev_layer_boundaries[current_object], path.width); + estimate_points_properties(path, prev_layer_boundaries[current_object], flow.width); std::vector processed_points; processed_points.reserve(extended_points.size()); @@ -276,7 +277,7 @@ public: // The following code artifically increases the distance to provide slowdown for extrusions that are over curled lines float artificial_distance_to_curled_lines = 0.0; - const double dist_limit = 10.0 * path.width; + const double dist_limit = 10.0 * flow.width; { Vec2d middle = 0.5 * (curr.position + next.position); auto line_indices = prev_curled_extrusions[current_object].all_lines_in_radius(Point::new_scale(middle), scale_(dist_limit)); @@ -314,9 +315,9 @@ public: for (size_t idx : line_indices) { const CurledLine &line = prev_curled_extrusions[current_object].get_line(idx); float distance_from_curled = unscaled(line_alg::distance_to(line, Point::new_scale(middle))); - float dist = path.width * (1.0 - (distance_from_curled / dist_limit)) * + float dist = flow.width * (1.0 - (distance_from_curled / dist_limit)) * (1.0 - (distance_from_curled / dist_limit)) * - (line.curled_height / (path.height * 10.0f)); // max_curled_height_factor from SupportSpotGenerator + (line.curled_height / (flow.height * 10.0f)); // max_curled_height_factor from SupportSpotGenerator artificial_distance_to_curled_lines = std::max(artificial_distance_to_curled_lines, dist); } } diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index f7fbb52a69..c8485a319c 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -4,7 +4,7 @@ #include "libslic3r/LocalesUtils.hpp" #include "libslic3r/format.hpp" #include "libslic3r/I18N.hpp" -#include "libslic3r/GCodeWriter.hpp" +#include "libslic3r/GCode/GCodeWriter.hpp" #include "libslic3r/I18N.hpp" #include "GCodeProcessor.hpp" diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCode/GCodeWriter.cpp similarity index 89% rename from src/libslic3r/GCodeWriter.cpp rename to src/libslic3r/GCode/GCodeWriter.cpp index 9c330c38e9..4dd155d5f3 100644 --- a/src/libslic3r/GCodeWriter.cpp +++ b/src/libslic3r/GCode/GCodeWriter.cpp @@ -1,10 +1,12 @@ #include "GCodeWriter.hpp" -#include "CustomGCode.hpp" +#include "../CustomGCode.hpp" + #include #include #include #include #include +#include #ifdef __APPLE__ #include @@ -13,6 +15,8 @@ #define FLAVOR_IS(val) this->config.gcode_flavor == val #define FLAVOR_IS_NOT(val) this->config.gcode_flavor != val +using namespace std::string_view_literals; + namespace Slic3r { // static @@ -90,17 +94,17 @@ std::string GCodeWriter::set_temperature(unsigned int temperature, bool wait, in if (wait && (FLAVOR_IS(gcfMakerWare) || FLAVOR_IS(gcfSailfish))) return {}; - std::string code, comment; + std::string_view code, comment; if (wait && FLAVOR_IS_NOT(gcfTeacup) && FLAVOR_IS_NOT(gcfRepRapFirmware)) { - code = "M109"; - comment = "set temperature and wait for it to be reached"; + code = "M109"sv; + comment = "set temperature and wait for it to be reached"sv; } else { if (FLAVOR_IS(gcfRepRapFirmware)) { // M104 is deprecated on RepRapFirmware - code = "G10"; + code = "G10"sv; } else { - code = "M104"; + code = "M104"sv; } - comment = "set temperature"; + comment = "set temperature"sv; } std::ostringstream gcode; @@ -130,22 +134,22 @@ std::string GCodeWriter::set_temperature(unsigned int temperature, bool wait, in std::string GCodeWriter::set_bed_temperature(unsigned int temperature, bool wait) { if (temperature == m_last_bed_temperature && (! wait || m_last_bed_temperature_reached)) - return std::string(); + return {}; m_last_bed_temperature = temperature; m_last_bed_temperature_reached = wait; - std::string code, comment; + std::string_view code, comment; if (wait && FLAVOR_IS_NOT(gcfTeacup)) { if (FLAVOR_IS(gcfMakerWare) || FLAVOR_IS(gcfSailfish)) { - code = "M109"; + code = "M109"sv; } else { - code = "M190"; + code = "M190"sv; } - comment = "set bed temperature and wait for it to be reached"; + comment = "set bed temperature and wait for it to be reached"sv; } else { - code = "M140"; - comment = "set bed temperature"; + code = "M140"sv; + comment = "set bed temperature"sv; } std::ostringstream gcode; @@ -176,7 +180,7 @@ std::string GCodeWriter::set_acceleration_internal(Acceleration type, unsigned i auto& last_value = separate_travel ? m_last_travel_acceleration : m_last_acceleration ; if (acceleration == 0 || acceleration == last_value) - return std::string(); + return {}; last_value = acceleration; @@ -245,7 +249,7 @@ std::string GCodeWriter::toolchange(unsigned int extruder_id) return gcode.str(); } -std::string GCodeWriter::set_speed(double F, const std::string &comment, const std::string &cooling_marker) const +std::string GCodeWriter::set_speed(double F, const std::string_view comment, const std::string_view cooling_marker) const { assert(F > 0.); assert(F < 100000.); @@ -257,10 +261,9 @@ std::string GCodeWriter::set_speed(double F, const std::string &comment, const s return w.string(); } -std::string GCodeWriter::travel_to_xy(const Vec2d &point, const std::string &comment) +std::string GCodeWriter::travel_to_xy(const Vec2d &point, const std::string_view comment) { - m_pos.x() = point.x(); - m_pos.y() = point.y(); + m_pos.head<2>() = point.head<2>(); GCodeG1Formatter w; w.emit_xy(point); @@ -269,7 +272,29 @@ std::string GCodeWriter::travel_to_xy(const Vec2d &point, const std::string &com return w.string(); } -std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &comment) +std::string GCodeWriter::travel_to_xy_G2G3IJ(const Vec2d &point, const Vec2d &ij, const bool ccw, const std::string_view comment) +{ + m_pos.head<2>() = point.head<2>(); + + GCodeG2G3Formatter w(ccw); + w.emit_xy(point); + w.emit_ij(ij); + w.emit_comment(this->config.gcode_comments, comment); + return w.string(); +} + +std::string GCodeWriter::travel_to_xy_G2G3R(const Vec2d &point, const double radius, const bool ccw, const std::string_view comment) +{ + m_pos.head<2>() = point.head<2>(); + + GCodeG2G3Formatter w(ccw); + w.emit_xy(point); + w.emit_radius(radius); + w.emit_comment(this->config.gcode_comments, comment); + return w.string(); +} + +std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string_view comment) { // FIXME: This function was not being used when travel_speed_z was separated (bd6badf). // Calculation of feedrate was not updated accordingly. If you want to use @@ -302,7 +327,7 @@ std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &co return w.string(); } -std::string GCodeWriter::travel_to_z(double z, const std::string &comment) +std::string GCodeWriter::travel_to_z(double z, const std::string_view comment) { /* If target Z is lower than current Z but higher than nominal Z we don't perform the move but we only adjust the nominal Z by @@ -321,7 +346,7 @@ std::string GCodeWriter::travel_to_z(double z, const std::string &comment) return this->_travel_to_z(z, comment); } -std::string GCodeWriter::_travel_to_z(double z, const std::string &comment) +std::string GCodeWriter::_travel_to_z(double z, const std::string_view comment) { m_pos.z() = z; @@ -348,10 +373,9 @@ bool GCodeWriter::will_move_z(double z) const return true; } -std::string GCodeWriter::extrude_to_xy(const Vec2d &point, double dE, const std::string &comment) +std::string GCodeWriter::extrude_to_xy(const Vec2d &point, double dE, const std::string_view comment) { - m_pos.x() = point.x(); - m_pos.y() = point.y(); + m_pos.head<2>() = point.head<2>(); GCodeG1Formatter w; w.emit_xy(point); @@ -360,8 +384,34 @@ std::string GCodeWriter::extrude_to_xy(const Vec2d &point, double dE, const std: return w.string(); } +std::string GCodeWriter::extrude_to_xy_G2G3IJ(const Vec2d &point, const Vec2d &ij, double dE, const bool ccw, const std::string_view comment) +{ + assert(dE > 0); + m_pos.head<2>() = point.head<2>(); + + GCodeG2G3Formatter w(ccw); + w.emit_xy(point); + w.emit_ij(ij); + w.emit_e(m_extrusion_axis, m_extruder->extrude(dE).second); + w.emit_comment(this->config.gcode_comments, comment); + return w.string(); +} + +std::string GCodeWriter::extrude_to_xy_G2G3R(const Vec2d &point, const double radius, double dE, const bool ccw, const std::string_view comment) +{ + assert(dE > 0); + m_pos.head<2>() = point.head<2>(); + + GCodeG2G3Formatter w(ccw); + w.emit_xy(point); + w.emit_radius(radius); + w.emit_e(m_extrusion_axis, m_extruder->extrude(dE).second); + w.emit_comment(this->config.gcode_comments, comment); + return w.string(); +} + #if 0 -std::string GCodeWriter::extrude_to_xyz(const Vec3d &point, double dE, const std::string &comment) +std::string GCodeWriter::extrude_to_xyz(const Vec3d &point, double dE, const std::string_view comment) { m_pos = point; m_lifted = 0; @@ -397,7 +447,7 @@ std::string GCodeWriter::retract_for_toolchange(bool before_wipe) ); } -std::string GCodeWriter::_retract(double length, double restart_extra, const std::string &comment) +std::string GCodeWriter::_retract(double length, double restart_extra, const std::string_view comment) { /* If firmware retraction is enabled, we use a fake value of 1 since we ignore the actual configured retract_length which diff --git a/src/libslic3r/GCodeWriter.hpp b/src/libslic3r/GCode/GCodeWriter.hpp similarity index 81% rename from src/libslic3r/GCodeWriter.hpp rename to src/libslic3r/GCode/GCodeWriter.hpp index 0d376cb159..904dac93dd 100644 --- a/src/libslic3r/GCodeWriter.hpp +++ b/src/libslic3r/GCode/GCodeWriter.hpp @@ -1,13 +1,15 @@ #ifndef slic3r_GCodeWriter_hpp_ #define slic3r_GCodeWriter_hpp_ -#include "libslic3r.h" +#include "../libslic3r.h" +#include "../Extruder.hpp" +#include "../Point.hpp" +#include "../PrintConfig.hpp" +#include "CoolingBuffer.hpp" + #include +#include #include -#include "Extruder.hpp" -#include "Point.hpp" -#include "PrintConfig.hpp" -#include "GCode/CoolingBuffer.hpp" namespace Slic3r { @@ -56,13 +58,17 @@ public: // printed with the same extruder. std::string toolchange_prefix() const; std::string toolchange(unsigned int extruder_id); - std::string set_speed(double F, const std::string &comment = std::string(), const std::string &cooling_marker = std::string()) const; - std::string travel_to_xy(const Vec2d &point, const std::string &comment = std::string()); - std::string travel_to_xyz(const Vec3d &point, const std::string &comment = std::string()); - std::string travel_to_z(double z, const std::string &comment = std::string()); + std::string set_speed(double F, const std::string_view comment = {}, const std::string_view cooling_marker = {}) const; + std::string travel_to_xy(const Vec2d &point, const std::string_view comment = {}); + std::string travel_to_xy_G2G3IJ(const Vec2d &point, const Vec2d &ij, const bool ccw, const std::string_view comment = {}); + std::string travel_to_xy_G2G3R(const Vec2d &point, const double radius, const bool ccw, const std::string_view comment = {}); + std::string travel_to_xyz(const Vec3d &point, const std::string_view comment = {}); + std::string travel_to_z(double z, const std::string_view comment = {}); bool will_move_z(double z) const; - std::string extrude_to_xy(const Vec2d &point, double dE, const std::string &comment = std::string()); -// std::string extrude_to_xyz(const Vec3d &point, double dE, const std::string &comment = std::string()); + std::string extrude_to_xy(const Vec2d &point, double dE, const std::string_view comment = {}); + std::string extrude_to_xy_G2G3IJ(const Vec2d &point, const Vec2d &ij, double dE, const bool ccw, const std::string_view comment); + std::string extrude_to_xy_G2G3R(const Vec2d &point, const double radius, double dE, const bool ccw, const std::string_view comment); +// std::string extrude_to_xyz(const Vec3d &point, double dE, const std::string_view comment = {}); std::string retract(bool before_wipe = false); std::string retract_for_toolchange(bool before_wipe = false); std::string unretract(); @@ -113,8 +119,8 @@ private: Print }; - std::string _travel_to_z(double z, const std::string &comment); - std::string _retract(double length, double restart_extra, const std::string &comment); + std::string _travel_to_z(double z, const std::string_view comment); + std::string _retract(double length, double restart_extra, const std::string_view comment); std::string set_acceleration_internal(Acceleration type, unsigned int acceleration); }; @@ -170,7 +176,18 @@ public: this->emit_axis('Z', z, XYZF_EXPORT_DIGITS); } - void emit_e(const std::string &axis, double v) { + void emit_ij(const Vec2d &point) { + this->emit_axis('I', point.x(), XYZF_EXPORT_DIGITS); + this->emit_axis('J', point.y(), XYZF_EXPORT_DIGITS); + } + + // Positive radius means a smaller arc, + // negative radius means a larger arc. + void emit_radius(const double radius) { + this->emit_axis('R', radius, XYZF_EXPORT_DIGITS); + } + + void emit_e(const std::string_view axis, double v) { if (! axis.empty()) { // not gcfNoExtrusion this->emit_axis(axis[0], v, E_EXPORT_DIGITS); @@ -181,12 +198,12 @@ public: this->emit_axis('F', speed, XYZF_EXPORT_DIGITS); } - void emit_string(const std::string &s) { - strncpy(ptr_err.ptr, s.c_str(), s.size()); + void emit_string(const std::string_view s) { + strncpy(ptr_err.ptr, s.data(), s.size()); ptr_err.ptr += s.size(); } - void emit_comment(bool allow_comments, const std::string &comment) { + void emit_comment(bool allow_comments, const std::string_view comment) { if (allow_comments && ! comment.empty()) { *ptr_err.ptr ++ = ' '; *ptr_err.ptr ++ = ';'; *ptr_err.ptr ++ = ' '; this->emit_string(comment); @@ -210,14 +227,25 @@ public: GCodeG1Formatter() { this->buf[0] = 'G'; this->buf[1] = '1'; - this->buf_end = buf + buflen; - this->ptr_err.ptr = this->buf + 2; + this->ptr_err.ptr += 2; } GCodeG1Formatter(const GCodeG1Formatter&) = delete; GCodeG1Formatter& operator=(const GCodeG1Formatter&) = delete; }; +class GCodeG2G3Formatter : public GCodeFormatter { +public: + GCodeG2G3Formatter(bool ccw) { + this->buf[0] = 'G'; + this->buf[1] = ccw ? '3' : '2'; + this->ptr_err.ptr += 2; + } + + GCodeG2G3Formatter(const GCodeG2G3Formatter&) = delete; + GCodeG2G3Formatter& operator=(const GCodeG2G3Formatter&) = delete; +}; + } /* namespace Slic3r */ #endif /* slic3r_GCodeWriter_hpp_ */ diff --git a/src/libslic3r/GCode/PrintExtents.cpp b/src/libslic3r/GCode/PrintExtents.cpp index 9a01dc8a6c..9bcaa27244 100644 --- a/src/libslic3r/GCode/PrintExtents.cpp +++ b/src/libslic3r/GCode/PrintExtents.cpp @@ -30,7 +30,7 @@ static inline BoundingBox extrusion_polyline_extents(const Polyline &polyline, c static inline BoundingBoxf extrusionentity_extents(const ExtrusionPath &extrusion_path) { - BoundingBox bbox = extrusion_polyline_extents(extrusion_path.polyline, coord_t(scale_(0.5 * extrusion_path.width))); + BoundingBox bbox = extrusion_polyline_extents(extrusion_path.polyline, coord_t(scale_(0.5 * extrusion_path.width()))); BoundingBoxf bboxf; if (! empty(bbox)) { bboxf.min = unscale(bbox.min); @@ -44,7 +44,7 @@ static inline BoundingBoxf extrusionentity_extents(const ExtrusionLoop &extrusio { BoundingBox bbox; for (const ExtrusionPath &extrusion_path : extrusion_loop.paths) - bbox.merge(extrusion_polyline_extents(extrusion_path.polyline, coord_t(scale_(0.5 * extrusion_path.width)))); + bbox.merge(extrusion_polyline_extents(extrusion_path.polyline, coord_t(scale_(0.5 * extrusion_path.width())))); BoundingBoxf bboxf; if (! empty(bbox)) { bboxf.min = unscale(bbox.min); @@ -58,7 +58,7 @@ static inline BoundingBoxf extrusionentity_extents(const ExtrusionMultiPath &ext { BoundingBox bbox; for (const ExtrusionPath &extrusion_path : extrusion_multi_path.paths) - bbox.merge(extrusion_polyline_extents(extrusion_path.polyline, coord_t(scale_(0.5 * extrusion_path.width)))); + bbox.merge(extrusion_polyline_extents(extrusion_path.polyline, coord_t(scale_(0.5 * extrusion_path.width())))); BoundingBoxf bboxf; if (! empty(bbox)) { bboxf.min = unscale(bbox.min); diff --git a/src/libslic3r/GCode/SeamPlacer.cpp b/src/libslic3r/GCode/SeamPlacer.cpp index 3bf1edf39e..db328997e7 100644 --- a/src/libslic3r/GCode/SeamPlacer.cpp +++ b/src/libslic3r/GCode/SeamPlacer.cpp @@ -1484,7 +1484,7 @@ void SeamPlacer::init(const Print &print, std::function throw_if_can } } -void SeamPlacer::place_seam(const Layer *layer, ExtrusionLoop &loop, bool external_first, +Point SeamPlacer::place_seam(const Layer *layer, const ExtrusionLoop &loop, bool external_first, const Point &last_pos) const { using namespace SeamPlacerImpl; const PrintObject *po = layer->object(); @@ -1587,7 +1587,7 @@ void SeamPlacer::place_seam(const Layer *layer, ExtrusionLoop &loop, bool extern //lastly, for internal perimeters, do the staggering if requested if (po->config().staggered_inner_seams && loop.length() > 0.0) { //fix depth, it is sometimes strongly underestimated - depth = std::max(loop.paths[projected_point.path_idx].width, depth); + depth = std::max(loop.paths[projected_point.path_idx].width(), depth); while (depth > 0.0f) { auto next_point = get_next_loop_point(projected_point); @@ -1605,14 +1605,7 @@ void SeamPlacer::place_seam(const Layer *layer, ExtrusionLoop &loop, bool extern } } - // Because the G-code export has 1um resolution, don't generate segments shorter than 1.5 microns, - // thus empty path segments will not be produced by G-code export. - if (!loop.split_at_vertex(seam_point, scaled(0.0015))) { - // The point is not in the original loop. - // Insert it. - loop.split_at(seam_point, true); - } - + return seam_point; } } // namespace Slic3r diff --git a/src/libslic3r/GCode/SeamPlacer.hpp b/src/libslic3r/GCode/SeamPlacer.hpp index 671f6bcce8..5603e99624 100644 --- a/src/libslic3r/GCode/SeamPlacer.hpp +++ b/src/libslic3r/GCode/SeamPlacer.hpp @@ -141,7 +141,7 @@ public: void init(const Print &print, std::function throw_if_canceled_func); - void place_seam(const Layer *layer, ExtrusionLoop &loop, bool external_first, const Point &last_pos) const; + Point place_seam(const Layer *layer, const ExtrusionLoop &loop, bool external_first, const Point &last_pos) const; private: void gather_seam_candidates(const PrintObject *po, const SeamPlacerImpl::GlobalModelInfo &global_model_info); diff --git a/src/libslic3r/GCode/SmoothPath.cpp b/src/libslic3r/GCode/SmoothPath.cpp new file mode 100644 index 0000000000..4eedd59011 --- /dev/null +++ b/src/libslic3r/GCode/SmoothPath.cpp @@ -0,0 +1,258 @@ +#include "SmoothPath.hpp" + +#include "../ExtrusionEntity.hpp" +#include "../ExtrusionEntityCollection.hpp" + +namespace Slic3r::GCode { + +// Length of a smooth path. +double length(const SmoothPath &path) +{ + double l = 0; + for (const SmoothPathElement &el : path) { + auto it = el.path.begin(); + auto end = el.path.end(); + Point prev_point = it->point; + for (++ it; it != end; ++ it) { + Point point = it->point; + l += it->linear() ? + (point - prev_point).cast().norm() : + Geometry::ArcWelder::arc_length(prev_point.cast(), point.cast(), it->radius); + prev_point = point; + } + } + return l; +} + +// Returns true if the smooth path is longer than a threshold. +bool longer_than(const SmoothPath &path, double length) +{ + for (const SmoothPathElement& el : path) { + auto it = el.path.begin(); + auto end = el.path.end(); + Point prev_point = it->point; + for (++it; it != end; ++it) { + Point point = it->point; + length -= it->linear() ? + (point - prev_point).cast().norm() : + Geometry::ArcWelder::arc_length(prev_point.cast(), point.cast(), it->radius); + if (length < 0) + return true; + prev_point = point; + } + } + return length < 0; +} + +std::optional sample_path_point_at_distance_from_start(const SmoothPath &path, double distance) +{ + if (distance >= 0) { + for (const SmoothPathElement &el : path) { + auto it = el.path.begin(); + auto end = el.path.end(); + Point prev_point = it->point; + for (++ it; it != end; ++ it) { + Point point = it->point; + if (it->linear()) { + // Linear segment + Vec2d v = (point - prev_point).cast(); + double lsqr = v.squaredNorm(); + if (lsqr > sqr(distance)) + return std::make_optional(prev_point + (v * (distance / sqrt(lsqr))).cast()); + distance -= sqrt(lsqr); + } else { + // Circular segment + float angle = Geometry::ArcWelder::arc_angle(prev_point.cast(), point.cast(), it->radius); + double len = std::abs(it->radius) * angle; + if (len > distance) { + // Rotate the segment end point in reverse towards the start point. + return std::make_optional(prev_point.rotated(- angle * (distance / len), + Geometry::ArcWelder::arc_center(prev_point.cast(), point.cast(), it->radius, it->ccw()).cast())); + } + distance -= len; + } + if (distance < 0) + return std::make_optional(point); + prev_point = point; + } + } + } + // Failed. + return {}; +} + +std::optional sample_path_point_at_distance_from_end(const SmoothPath &path, double distance) +{ + if (distance >= 0) { + for (const SmoothPathElement& el : path) { + auto it = el.path.begin(); + auto end = el.path.end(); + Point prev_point = it->point; + for (++it; it != end; ++it) { + Point point = it->point; + if (it->linear()) { + // Linear segment + Vec2d v = (point - prev_point).cast(); + double lsqr = v.squaredNorm(); + if (lsqr > sqr(distance)) + return std::make_optional(prev_point + (v * (distance / sqrt(lsqr))).cast()); + distance -= sqrt(lsqr); + } + else { + // Circular segment + float angle = Geometry::ArcWelder::arc_angle(prev_point.cast(), point.cast(), it->radius); + double len = std::abs(it->radius) * angle; + if (len > distance) { + // Rotate the segment end point in reverse towards the start point. + return std::make_optional(prev_point.rotated(-angle * (distance / len), + Geometry::ArcWelder::arc_center(prev_point.cast(), point.cast(), it->radius, it->ccw()).cast())); + } + distance -= len; + } + if (distance < 0) + return std::make_optional(point); + prev_point = point; + } + } + } + // Failed. + return {}; +} + +double clip_end(SmoothPath &path, double distance) +{ + while (! path.empty() && distance > 0) { + Geometry::ArcWelder::Path &p = path.back().path; + distance = clip_end(p, distance); + if (p.empty()) { + path.pop_back(); + } else { + assert(distance == 0); + return 0; + } + } + return distance; +} + +void SmoothPathCache::interpolate_add(const Polyline &polyline, const InterpolationParameters ¶ms) +{ + m_cache[&polyline] = Slic3r::Geometry::ArcWelder::fit_path(polyline.points, params.tolerance, params.fit_circle_tolerance); +} + +void SmoothPathCache::interpolate_add(const ExtrusionPath &path, const InterpolationParameters ¶ms) +{ + this->interpolate_add(path.polyline, params); +} + +void SmoothPathCache::interpolate_add(const ExtrusionMultiPath &multi_path, const InterpolationParameters ¶ms) +{ + for (const ExtrusionPath &path : multi_path.paths) + this->interpolate_add(path.polyline, params); +} + +void SmoothPathCache::interpolate_add(const ExtrusionLoop &loop, const InterpolationParameters ¶ms) +{ + for (const ExtrusionPath &path : loop.paths) + this->interpolate_add(path.polyline, params); +} + +void SmoothPathCache::interpolate_add(const ExtrusionEntityCollection &eec, const InterpolationParameters ¶ms) +{ + for (const ExtrusionEntity *ee : eec) { + if (ee->is_collection()) + this->interpolate_add(*static_cast(ee), params); + else if (const ExtrusionPath *path = dynamic_cast(ee); path) + this->interpolate_add(*path, params); + else if (const ExtrusionMultiPath *multi_path = dynamic_cast(ee); multi_path) + this->interpolate_add(*multi_path, params); + else if (const ExtrusionLoop *loop = dynamic_cast(ee); loop) + this->interpolate_add(*loop, params); + else + assert(false); + } +} + +const Geometry::ArcWelder::Path* SmoothPathCache::resolve(const Polyline *pl) const +{ + auto it = m_cache.find(pl); + return it == m_cache.end() ? nullptr : &it->second; +} + +const Geometry::ArcWelder::Path* SmoothPathCache::resolve(const ExtrusionPath &path) const +{ + return this->resolve(&path.polyline); +} + +Geometry::ArcWelder::Path SmoothPathCache::resolve_or_fit(const ExtrusionPath &path, bool reverse, double tolerance) const +{ + Geometry::ArcWelder::Path out; + if (const Geometry::ArcWelder::Path *cached = this->resolve(path); cached) + out = *cached; + else + out = Geometry::ArcWelder::fit_polyline(path.polyline.points, tolerance); + if (reverse) + Geometry::ArcWelder::reverse(out); + return out; +} + +SmoothPath SmoothPathCache::resolve_or_fit(const ExtrusionPaths &paths, bool reverse, double resolution) const +{ + SmoothPath out; + out.reserve(paths.size()); + if (reverse) { + for (auto it = paths.crbegin(); it != paths.crend(); ++ it) + out.push_back({ it->attributes(), this->resolve_or_fit(*it, true, resolution) }); + } else { + for (auto it = paths.cbegin(); it != paths.cend(); ++ it) + out.push_back({ it->attributes(), this->resolve_or_fit(*it, false, resolution) }); + } + return out; +} + +SmoothPath SmoothPathCache::resolve_or_fit(const ExtrusionMultiPath &multipath, bool reverse, double resolution) const +{ + return this->resolve_or_fit(multipath.paths, reverse, resolution); +} + +SmoothPath SmoothPathCache::resolve_or_fit_split_with_seam( + const ExtrusionLoop &loop, const bool reverse, const double resolution, + const Point &seam_point, const double seam_point_merge_distance_threshold) const +{ + SmoothPath out = this->resolve_or_fit(loop.paths, reverse, resolution); + assert(! out.empty()); + if (! out.empty()) { + Geometry::ArcWelder::PathSegmentProjection proj; + int proj_path = -1; + for (const SmoothPathElement &el : out) + if (Geometry::ArcWelder::PathSegmentProjection this_proj = Geometry::ArcWelder::point_to_path_projection(el.path, seam_point, proj.distance2); + this_proj.distance2 < proj.distance2) { + // Found a better (closer) projection. + proj = this_proj; + proj_path = &el - out.data(); + } + assert(proj_path >= 0); + // Split the path at the closest point. + Geometry::ArcWelder::Path &path = out[proj_path].path; + std::pair split = Geometry::ArcWelder::split_at( + path, proj, seam_point_merge_distance_threshold); + if (split.second.empty()) { + std::rotate(out.begin(), out.begin() + proj_path + 1, out.end()); + out.back().path = std::move(split.first); + } else { + ExtrusionAttributes attr = out[proj_path].path_attributes; + std::rotate(out.begin(), out.begin() + proj_path, out.end()); + out.front().path = std::move(split.second); + if (! split.first.empty()) { + if (out.back().path_attributes == attr) { + // Merge with the last segment. + out.back().path.insert(out.back().path.end(), std::next(split.first.begin()), split.first.end()); + } else + out.push_back({ attr, std::move(split.first) }); + } + } + } + + return out; +} + +} // namespace Slic3r::GCode diff --git a/src/libslic3r/GCode/SmoothPath.hpp b/src/libslic3r/GCode/SmoothPath.hpp new file mode 100644 index 0000000000..cd686564a9 --- /dev/null +++ b/src/libslic3r/GCode/SmoothPath.hpp @@ -0,0 +1,70 @@ +#ifndef slic3r_GCode_SmoothPath_hpp_ +#define slic3r_GCode_SmoothPath_hpp_ + +#include + +#include "../ExtrusionEntity.hpp" +#include "../Geometry/ArcWelder.hpp" + +namespace Slic3r { + +class ExtrusionEntityCollection; + +namespace GCode { + +struct SmoothPathElement +{ + ExtrusionAttributes path_attributes; + Geometry::ArcWelder::Path path; +}; + +using SmoothPath = std::vector; + +// Length of a smooth path. +double length(const SmoothPath &path); +// Returns true if the smooth path is longer than a threshold. +bool longer_than(const SmoothPath &path, const double length); + +std::optional sample_path_point_at_distance_from_start(const SmoothPath &path, double distance); +std::optional sample_path_point_at_distance_from_end(const SmoothPath &path, double distance); + +// Clip end of a smooth path, for seam hiding. +double clip_end(SmoothPath &path, double distance); + +class SmoothPathCache +{ +public: + struct InterpolationParameters { + double tolerance; + double fit_circle_tolerance; + }; + + void interpolate_add(const Polyline &pl, const InterpolationParameters ¶ms); + void interpolate_add(const ExtrusionPath &ee, const InterpolationParameters ¶ms); + void interpolate_add(const ExtrusionMultiPath &ee, const InterpolationParameters ¶ms); + void interpolate_add(const ExtrusionLoop &ee, const InterpolationParameters ¶ms); + void interpolate_add(const ExtrusionEntityCollection &eec, const InterpolationParameters ¶ms); + + const Geometry::ArcWelder::Path* resolve(const Polyline *pl) const; + const Geometry::ArcWelder::Path* resolve(const ExtrusionPath &path) const; + + // Look-up a smooth representation of path in the cache. If it does not exist, produce a simplified polyline. + Geometry::ArcWelder::Path resolve_or_fit(const ExtrusionPath &path, bool reverse, double resolution) const; + + // Look-up a smooth representation of path in the cache. If it does not exist, produce a simplified polyline. + SmoothPath resolve_or_fit(const ExtrusionPaths &paths, bool reverse, double resolution) const; + SmoothPath resolve_or_fit(const ExtrusionMultiPath &path, bool reverse, double resolution) const; + + // Look-up a smooth representation of path in the cache. If it does not exist, produce a simplified polyline. + SmoothPath resolve_or_fit_split_with_seam( + const ExtrusionLoop &path, const bool reverse, const double resolution, + const Point &seam_point, const double seam_point_merge_distance_threshold) const; + +private: + ankerl::unordered_dense::map m_cache; +}; + +} // namespace GCode +} // namespace Slic3r + +#endif // slic3r_GCode_SmoothPath_hpp_ diff --git a/src/libslic3r/GCode/Wipe.cpp b/src/libslic3r/GCode/Wipe.cpp new file mode 100644 index 0000000000..c77984d4da --- /dev/null +++ b/src/libslic3r/GCode/Wipe.cpp @@ -0,0 +1,218 @@ +#include "Wipe.hpp" +#include "../GCode.hpp" + +#include + +#include + +using namespace std::string_view_literals; + +namespace Slic3r::GCode { + +void Wipe::init(const PrintConfig &config, const std::vector &extruders) +{ + this->reset_path(); + + // Calculate maximum wipe length to accumulate by the wipe cache. + // Paths longer than wipe_xy should never be needed for the wipe move. + double wipe_xy = 0; + const bool multimaterial = extruders.size() > 1; + for (auto id : extruders) + if (config.wipe.get_at(id)) { + // Wipe length to extrusion ratio. + const double xy_to_e = this->calc_xy_to_e_ratio(config, id); + wipe_xy = std::max(wipe_xy, xy_to_e * config.retract_length.get_at(id)); + if (multimaterial) + wipe_xy = std::max(wipe_xy, xy_to_e * config.retract_length_toolchange.get_at(id)); + } + + if (wipe_xy == 0) + this->disable(); + else + this->enable(wipe_xy); +} + +void Wipe::set_path(SmoothPath &&path, bool reversed) +{ + this->reset_path(); + + if (this->enabled() && ! path.empty()) { + if (reversed) { + m_path = std::move(path.back().path); + Geometry::ArcWelder::reverse(m_path); + int64_t len = Geometry::ArcWelder::estimate_path_length(m_path); + for (auto it = std::next(path.rbegin()); len < m_wipe_len_max && it != path.rend(); ++ it) { + if (it->path_attributes.role.is_bridge()) + break; // Do not perform a wipe on bridges. + assert(it->path.size() >= 2); + assert(m_path.back().point == it->path.back().point); + if (m_path.back().point != it->path.back().point) + // ExtrusionMultiPath is interrupted in some place. This should not really happen. + break; + len += Geometry::ArcWelder::estimate_path_length(it->path); + m_path.insert(m_path.end(), it->path.rbegin() + 1, it->path.rend()); + } + } else { + m_path = std::move(path.front().path); + int64_t len = Geometry::ArcWelder::estimate_path_length(m_path); + for (auto it = std::next(path.begin()); len < m_wipe_len_max && it != path.end(); ++ it) { + if (it->path_attributes.role.is_bridge()) + break; // Do not perform a wipe on bridges. + assert(it->path.size() >= 2); + assert(m_path.back().point == it->path.front().point); + if (m_path.back().point != it->path.front().point) + // ExtrusionMultiPath is interrupted in some place. This should not really happen. + break; + len += Geometry::ArcWelder::estimate_path_length(it->path); + m_path.insert(m_path.end(), it->path.begin() + 1, it->path.end()); + } + } + } + + assert(m_path.empty() || m_path.size() > 1); +} + +std::string Wipe::wipe(GCodeGenerator &gcodegen, bool toolchange) +{ + std::string gcode; + const Extruder &extruder = *gcodegen.writer().extruder(); + + // Remaining quantized retraction length. + if (double retract_length = extruder.retract_to_go(toolchange ? extruder.retract_length_toolchange() : extruder.retract_length()); + retract_length > 0 && this->has_path()) { + // Delayed emitting of a wipe start tag. + bool wiped = false; + const double wipe_speed = this->calc_wipe_speed(gcodegen.writer().config); + auto start_wipe = [&wiped, &gcode, &gcodegen, wipe_speed](){ + if (! wiped) { + wiped = true; + gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Wipe_Start) + "\n"; + gcode += gcodegen.writer().set_speed(wipe_speed * 60, {}, gcodegen.enable_cooling_markers() ? ";_WIPE"sv : ""sv); + } + }; + const double xy_to_e = this->calc_xy_to_e_ratio(gcodegen.writer().config, extruder.id()); + auto wipe_linear = [&gcode, &gcodegen, &retract_length, xy_to_e](const Vec2d &prev, Vec2d &p) { + double segment_length = (p - prev).norm(); + // Quantize E axis as it is to be + double dE = GCodeFormatter::quantize_e(xy_to_e * segment_length); + bool done = false; + if (dE > retract_length - EPSILON) { + if (dE > retract_length + EPSILON) + // Shorten the segment. + p = gcodegen.point_to_gcode_quantized(prev + (p - prev) * (retract_length / dE)); + dE = retract_length; + done = true; + } + gcode += gcodegen.writer().extrude_to_xy(p, -dE, "wipe and retract"sv); + retract_length -= dE; + return done; + }; + auto wipe_arc = [&gcode, &gcodegen, &retract_length, xy_to_e](const Vec2d &prev, Vec2d &p, float radius_in, const bool ccw) { + double radius = GCodeFormatter::quantize_xyzf(radius_in); + Vec2f center = Geometry::ArcWelder::arc_center(prev.cast(), p.cast(), float(radius), ccw); + float angle = Geometry::ArcWelder::arc_angle(prev.cast(), p.cast(), float(radius)); + double segment_length = angle * std::abs(radius); + double dE = GCodeFormatter::quantize_e(xy_to_e * segment_length); + bool done = false; + if (dE > retract_length - EPSILON) { + if (dE > retract_length + EPSILON) + // Shorten the segment. + p = gcodegen.point_to_gcode_quantized(Point(prev).rotated((ccw ? angle : -angle) * (retract_length / dE), center.cast())); + dE = retract_length; + done = true; + } + gcode += gcodegen.writer().extrude_to_xy_G2G3R(p, radius, ccw, -dE, "wipe and retract"sv); + retract_length -= dE; + return done; + }; + // Start with the current position, which may be different from the wipe path start in case of loop clipping. + Vec2d prev = gcodegen.point_to_gcode_quantized(gcodegen.last_pos()); + auto it = this->path().begin(); + Vec2d p = gcodegen.point_to_gcode_quantized(it->point + m_offset); + ++ it; + bool done = false; + if (p != prev) { + start_wipe(); + done = wipe_linear(prev, p); + } + if (! done) { + prev = p; + auto end = this->path().end(); + for (; it != end && ! done; ++ it) { + p = gcodegen.point_to_gcode_quantized(it->point + m_offset); + if (p != prev) { + start_wipe(); + if (it->linear() ? + wipe_linear(prev, p) : + wipe_arc(prev, p, it->radius, it->ccw())) + break; + prev = p; + } + } + } + if (wiped) { + // add tag for processor + gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Wipe_End) + "\n"; + gcodegen.set_last_pos(gcodegen.gcode_to_point(p)); + } + } + + // Prevent wiping again on the same path. + this->reset_path(); + return gcode; +} + +// Make a little move inwards before leaving loop after path was extruded, +// thus the current extruder position is at the end of a path and the path +// may not be closed in case the loop was clipped to hide a seam. +std::optional wipe_hide_seam(const SmoothPath &path, bool is_hole, double wipe_length) +{ + assert(! path.empty()); + assert(path.front().path.size() >= 2); + assert(path.back().path.size() >= 2); + + // Heuristics for estimating whether there is a chance that the wipe move will fit inside a small perimeter + // or that the wipe move direction could be calculated with reasonable accuracy. + if (longer_than(path, 2.5 * wipe_length)) { + // The print head will be moved away from path end inside the island. + Point p_current = path.back().path.back().point; + Point p_next = path.front().path.front().point; + Point p_prev; + { + // Is the seam hiding gap large enough already? + double l = wipe_length - (p_next - p_current).cast().norm(); + if (l > 0) { + // Not yet. + std::optional n = sample_path_point_at_distance_from_start(path, l); + assert(n); + if (! n) + // Wipe move cannot be calculated, the loop is not long enough. This should not happen due to the longer_than() test above. + return {}; + } + if (std::optional p = sample_path_point_at_distance_from_end(path, wipe_length); p) + p_prev = *p; + else + // Wipe move cannot be calculated, the loop is not long enough. This should not happen due to the longer_than() test above. + return {}; + } + // Detect angle between last and first segment. + // The side depends on the original winding order of the polygon (left for contours, right for holes). + double angle_inside = angle(p_next - p_current, p_prev - p_current); + assert(angle_inside >= -M_PI && angle_inside <= M_PI); + // 3rd of this angle will be taken, thus make the angle monotonic before interpolation. + if (is_hole) { + if (angle_inside > 0) + angle_inside -= 2.0 * M_PI; + } else { + if (angle_inside < 0) + angle_inside += 2.0 * M_PI; + } + // Rotate the forward segment inside by 1/3 of the wedge angle. + auto v_rotated = Eigen::Rotation2D(angle_inside) * (p_next - p_current).cast().normalized(); + return std::make_optional(p_current + (v_rotated * wipe_length).cast()); + } + + return {}; +} + +} // namespace Slic3r::GCode diff --git a/src/libslic3r/GCode/Wipe.hpp b/src/libslic3r/GCode/Wipe.hpp new file mode 100644 index 0000000000..a088aa3904 --- /dev/null +++ b/src/libslic3r/GCode/Wipe.hpp @@ -0,0 +1,72 @@ +#ifndef slic3r_GCode_Wipe_hpp_ +#define slic3r_GCode_Wipe_hpp_ + +#include "Geometry/ArcWelder.hpp" +#include "SmoothPath.hpp" +#include "../Point.hpp" +#include "../PrintConfig.hpp" + +#include +#include + +namespace Slic3r { + +class GCodeGenerator; + +namespace GCode { + +class Wipe { +public: + using Path = Slic3r::Geometry::ArcWelder::Path; + + Wipe() = default; + + void init(const PrintConfig &config, const std::vector &extruders); + void enable(double wipe_len_max) { m_enabled = true; m_wipe_len_max = wipe_len_max; } + void disable() { m_enabled = false; } + bool enabled() const { return m_enabled; } + + const Path& path() const { return m_path; } + bool has_path() const { assert(m_path.empty() || m_path.size() > 1); return ! m_path.empty(); } + void reset_path() { m_path.clear(); m_offset = Point::Zero(); } + void set_path(const Path &path) { + assert(path.empty() || path.size() > 1); + this->reset_path(); + if (this->enabled() && path.size() > 1) + m_path = path; + } + void set_path(Path &&path) { + assert(path.empty() || path.size() > 1); + this->reset_path(); + if (this->enabled() && path.size() > 1) + m_path = std::move(path); + } + void set_path(SmoothPath &&path, bool reversed); + void offset_path(const Point &v) { m_offset += v; } + + std::string wipe(GCodeGenerator &gcodegen, bool toolchange); + + // Reduce feedrate a bit; travel speed is often too high to move on existing material. + // Too fast = ripping of existing material; too slow = short wipe path, thus more blob. + static double calc_wipe_speed(const GCodeConfig &config) { return config.travel_speed.value * 0.8; } + // Reduce retraction length a bit to avoid effective retraction speed to be greater than the configured one + // due to rounding (TODO: test and/or better math for this). + static double calc_xy_to_e_ratio(const GCodeConfig &config, unsigned int extruder_id) + { return 0.95 * floor(config.retract_speed.get_at(extruder_id) + 0.5) / calc_wipe_speed(config); } + +private: + bool m_enabled{ false }; + // Maximum length of a path to accumulate. Only wipes shorter than this threshold will be requested. + double m_wipe_len_max{ 0. }; + Path m_path; + // Offset from m_path to the current PrintObject active. + Point m_offset{ Point::Zero() }; +}; + +// Make a little move inwards before leaving loop. +std::optional wipe_hide_seam(const SmoothPath &path, bool is_hole, double wipe_length); + +} // namespace GCode +} // namespace Slic3r + +#endif // slic3r_GCode_Wipe_hpp_ diff --git a/src/libslic3r/GCode/WipeTower.hpp b/src/libslic3r/GCode/WipeTower.hpp index 969da848d0..3ff48e999a 100644 --- a/src/libslic3r/GCode/WipeTower.hpp +++ b/src/libslic3r/GCode/WipeTower.hpp @@ -1,5 +1,5 @@ -#ifndef WipeTower_ -#define WipeTower_ +#ifndef slic3r_GCode_WipeTower_hpp_ +#define slic3r_GCode_WipeTower_hpp_ #include #include @@ -403,4 +403,4 @@ private: } // namespace Slic3r -#endif // WipeTowerPrusaMM_hpp_ +#endif // slic3r_GCode_WipeTower_hpp_ diff --git a/src/libslic3r/GCode/WipeTowerIntegration.cpp b/src/libslic3r/GCode/WipeTowerIntegration.cpp new file mode 100644 index 0000000000..bea9641702 --- /dev/null +++ b/src/libslic3r/GCode/WipeTowerIntegration.cpp @@ -0,0 +1,247 @@ +#include "WipeTowerIntegration.hpp" + +#include "../GCode.hpp" +#include "../libslic3r.h" + +namespace Slic3r::GCode { + +static inline Point wipe_tower_point_to_object_point(GCodeGenerator &gcodegen, const Vec2f& wipe_tower_pt) +{ + return Point(scale_(wipe_tower_pt.x() - gcodegen.origin()(0)), scale_(wipe_tower_pt.y() - gcodegen.origin()(1))); +} + +std::string WipeTowerIntegration::append_tcr(GCodeGenerator &gcodegen, const WipeTower::ToolChangeResult& tcr, int new_extruder_id, double z) const +{ + if (new_extruder_id != -1 && new_extruder_id != tcr.new_tool) + throw Slic3r::InvalidArgument("Error: WipeTowerIntegration::append_tcr was asked to do a toolchange it didn't expect."); + + std::string gcode; + + // Toolchangeresult.gcode assumes the wipe tower corner is at the origin (except for priming lines) + // We want to rotate and shift all extrusions (gcode postprocessing) and starting and ending position + float alpha = m_wipe_tower_rotation / 180.f * float(M_PI); + + auto transform_wt_pt = [&alpha, this](const Vec2f& pt) -> Vec2f { + Vec2f out = Eigen::Rotation2Df(alpha) * pt; + out += m_wipe_tower_pos; + return out; + }; + + Vec2f start_pos = tcr.start_pos; + Vec2f end_pos = tcr.end_pos; + if (! tcr.priming) { + start_pos = transform_wt_pt(start_pos); + end_pos = transform_wt_pt(end_pos); + } + + Vec2f wipe_tower_offset = tcr.priming ? Vec2f::Zero() : m_wipe_tower_pos; + float wipe_tower_rotation = tcr.priming ? 0.f : alpha; + + std::string tcr_rotated_gcode = post_process_wipe_tower_moves(tcr, wipe_tower_offset, wipe_tower_rotation); + + gcode += gcodegen.writer().unlift(); // Make sure there is no z-hop (in most cases, there isn't). + + double current_z = gcodegen.writer().get_position().z(); + if (z == -1.) // in case no specific z was provided, print at current_z pos + z = current_z; + + const bool needs_toolchange = gcodegen.writer().need_toolchange(new_extruder_id); + const bool will_go_down = ! is_approx(z, current_z); + if (tcr.force_travel || ! needs_toolchange || (gcodegen.config().single_extruder_multi_material && ! tcr.priming)) { + // Move over the wipe tower. If this is not single-extruder MM, the first wipe tower move following the + // toolchange will travel there anyway (if there is a toolchange). + // FIXME: It would be better if the wipe tower set the force_travel flag for all toolchanges, + // then we could simplify the condition and make it more readable. + gcode += gcodegen.retract(); + gcodegen.m_avoid_crossing_perimeters.use_external_mp_once(); + gcode += gcodegen.travel_to( + wipe_tower_point_to_object_point(gcodegen, start_pos), + ExtrusionRole::Mixed, + "Travel to a Wipe Tower"); + gcode += gcodegen.unretract(); + } + + if (will_go_down) { + gcode += gcodegen.writer().retract(); + gcode += gcodegen.writer().travel_to_z(z, "Travel down to the last wipe tower layer."); + gcode += gcodegen.writer().unretract(); + } + + std::string toolchange_gcode_str; + std::string deretraction_str; + if (tcr.priming || (new_extruder_id >= 0 && needs_toolchange)) { + if (gcodegen.config().single_extruder_multi_material) + gcodegen.m_wipe.reset_path(); // We don't want wiping on the ramming lines. + toolchange_gcode_str = gcodegen.set_extruder(new_extruder_id, tcr.print_z); // TODO: toolchange_z vs print_z + if (gcodegen.config().wipe_tower) + deretraction_str = gcodegen.unretract(); + } + + + + + // Insert the toolchange and deretraction gcode into the generated gcode. + DynamicConfig config; + config.set_key_value("toolchange_gcode", new ConfigOptionString(toolchange_gcode_str)); + config.set_key_value("deretraction_from_wipe_tower_generator", new ConfigOptionString(deretraction_str)); + std::string tcr_gcode, tcr_escaped_gcode = gcodegen.placeholder_parser_process("tcr_rotated_gcode", tcr_rotated_gcode, new_extruder_id, &config); + unescape_string_cstyle(tcr_escaped_gcode, tcr_gcode); + gcode += tcr_gcode; + if (! toolchange_gcode_str.empty() && toolchange_gcode_str.back() != '\n') + toolchange_gcode_str += '\n'; + + // A phony move to the end position at the wipe tower. + gcodegen.writer().travel_to_xy(end_pos.cast()); + gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, end_pos)); + if (!is_approx(z, current_z)) { + gcode += gcodegen.writer().retract(); + gcode += gcodegen.writer().travel_to_z(current_z, "Travel back up to the topmost object layer."); + gcode += gcodegen.writer().unretract(); + } + + else { + // Prepare a future wipe. + // Convert to a smooth path. + Geometry::ArcWelder::Path path; + path.reserve(tcr.wipe_path.size()); + std::transform(tcr.wipe_path.begin(), tcr.wipe_path.end(), std::back_inserter(path), + [&gcodegen, &transform_wt_pt](const Vec2f &wipe_pt) { + return Geometry::ArcWelder::Segment{ wipe_tower_point_to_object_point(gcodegen, transform_wt_pt(wipe_pt)) }; + }); + // Pass to the wipe cache. + gcodegen.m_wipe.set_path(std::move(path)); + } + + // Let the planner know we are traveling between objects. + gcodegen.m_avoid_crossing_perimeters.use_external_mp_once(); + return gcode; +} + +// This function postprocesses gcode_original, rotates and moves all G1 extrusions and returns resulting gcode +// Starting position has to be supplied explicitely (otherwise it would fail in case first G1 command only contained one coordinate) +std::string WipeTowerIntegration::post_process_wipe_tower_moves(const WipeTower::ToolChangeResult& tcr, const Vec2f& translation, float angle) const +{ + Vec2f extruder_offset = m_extruder_offsets[tcr.initial_tool].cast(); + + std::istringstream gcode_str(tcr.gcode); + std::string gcode_out; + std::string line; + Vec2f pos = tcr.start_pos; + Vec2f transformed_pos = Eigen::Rotation2Df(angle) * pos + translation; + Vec2f old_pos(-1000.1f, -1000.1f); + + while (gcode_str) { + std::getline(gcode_str, line); // we read the gcode line by line + + // All G1 commands should be translated and rotated. X and Y coords are + // only pushed to the output when they differ from last time. + // WT generator can override this by appending the never_skip_tag + if (boost::starts_with(line, "G1 ")) { + bool never_skip = false; + auto it = line.find(WipeTower::never_skip_tag()); + if (it != std::string::npos) { + // remove the tag and remember we saw it + never_skip = true; + line.erase(it, it + WipeTower::never_skip_tag().size()); + } + std::ostringstream line_out; + std::istringstream line_str(line); + line_str >> std::noskipws; // don't skip whitespace + char ch = 0; + line_str >> ch >> ch; // read the "G1" + while (line_str >> ch) { + if (ch == 'X' || ch == 'Y') + line_str >> (ch == 'X' ? pos.x() : pos.y()); + else + line_out << ch; + } + + transformed_pos = Eigen::Rotation2Df(angle) * pos + translation; + + if (transformed_pos != old_pos || never_skip) { + line = line_out.str(); + boost::trim_left(line); // Remove leading spaces + std::ostringstream oss; + oss << std::fixed << std::setprecision(3) << "G1"; + if (transformed_pos.x() != old_pos.x() || never_skip) + oss << " X" << transformed_pos.x() - extruder_offset.x(); + if (transformed_pos.y() != old_pos.y() || never_skip) + oss << " Y" << transformed_pos.y() - extruder_offset.y(); + if (! line.empty()) + oss << " "; + line = oss.str() + line; + old_pos = transformed_pos; + } + } + + gcode_out += line + "\n"; + + // If this was a toolchange command, we should change current extruder offset + if (line == "[toolchange_gcode]") { + extruder_offset = m_extruder_offsets[tcr.new_tool].cast(); + + // If the extruder offset changed, add an extra move so everything is continuous + if (extruder_offset != m_extruder_offsets[tcr.initial_tool].cast()) { + std::ostringstream oss; + oss << std::fixed << std::setprecision(3) + << "G1 X" << transformed_pos.x() - extruder_offset.x() + << " Y" << transformed_pos.y() - extruder_offset.y() + << "\n"; + gcode_out += oss.str(); + } + } + } + return gcode_out; +} + + +std::string WipeTowerIntegration::prime(GCodeGenerator &gcodegen) +{ + std::string gcode; + for (const WipeTower::ToolChangeResult& tcr : m_priming) { + if (! tcr.extrusions.empty()) + gcode += append_tcr(gcodegen, tcr, tcr.new_tool); + } + return gcode; +} + +std::string WipeTowerIntegration::tool_change(GCodeGenerator &gcodegen, int extruder_id, bool finish_layer) +{ + std::string gcode; + assert(m_layer_idx >= 0); + if (gcodegen.writer().need_toolchange(extruder_id) || finish_layer) { + if (m_layer_idx < (int)m_tool_changes.size()) { + if (!(size_t(m_tool_change_idx) < m_tool_changes[m_layer_idx].size())) + throw Slic3r::RuntimeError("Wipe tower generation failed, possibly due to empty first layer."); + + // Calculate where the wipe tower layer will be printed. -1 means that print z will not change, + // resulting in a wipe tower with sparse layers. + double wipe_tower_z = -1; + bool ignore_sparse = false; + if (gcodegen.config().wipe_tower_no_sparse_layers.value) { + wipe_tower_z = m_last_wipe_tower_print_z; + ignore_sparse = (m_tool_changes[m_layer_idx].size() == 1 && m_tool_changes[m_layer_idx].front().initial_tool == m_tool_changes[m_layer_idx].front().new_tool && m_layer_idx != 0); + if (m_tool_change_idx == 0 && !ignore_sparse) + wipe_tower_z = m_last_wipe_tower_print_z + m_tool_changes[m_layer_idx].front().layer_height; + } + + if (!ignore_sparse) { + gcode += append_tcr(gcodegen, m_tool_changes[m_layer_idx][m_tool_change_idx++], extruder_id, wipe_tower_z); + m_last_wipe_tower_print_z = wipe_tower_z; + } + } + } + return gcode; +} + +// Print is finished. Now it remains to unload the filament safely with ramming over the wipe tower. +std::string WipeTowerIntegration::finalize(GCodeGenerator &gcodegen) +{ + std::string gcode; + if (std::abs(gcodegen.writer().get_position().z() - m_final_purge.print_z) > EPSILON) + gcode += gcodegen.change_layer(m_final_purge.print_z); + gcode += append_tcr(gcodegen, m_final_purge, -1); + return gcode; +} + +} // namespace Slic3r::GCode diff --git a/src/libslic3r/GCode/WipeTowerIntegration.hpp b/src/libslic3r/GCode/WipeTowerIntegration.hpp new file mode 100644 index 0000000000..52b27625bb --- /dev/null +++ b/src/libslic3r/GCode/WipeTowerIntegration.hpp @@ -0,0 +1,65 @@ +#ifndef slic3r_GCode_WipeTowerIntegration_hpp_ +#define slic3r_GCode_WipeTowerIntegration_hpp_ + +#include "WipeTower.hpp" +#include "../PrintConfig.hpp" + +namespace Slic3r { + +class GCodeGenerator; + +namespace GCode { + +class WipeTowerIntegration { +public: + WipeTowerIntegration( + const PrintConfig &print_config, + const std::vector &priming, + const std::vector> &tool_changes, + const WipeTower::ToolChangeResult &final_purge) : + m_left(/*float(print_config.wipe_tower_x.value)*/ 0.f), + m_right(float(/*print_config.wipe_tower_x.value +*/ print_config.wipe_tower_width.value)), + m_wipe_tower_pos(float(print_config.wipe_tower_x.value), float(print_config.wipe_tower_y.value)), + m_wipe_tower_rotation(float(print_config.wipe_tower_rotation_angle)), + m_extruder_offsets(print_config.extruder_offset.values), + m_priming(priming), + m_tool_changes(tool_changes), + m_final_purge(final_purge), + m_layer_idx(-1), + m_tool_change_idx(0) + {} + + std::string prime(GCodeGenerator &gcodegen); + void next_layer() { ++ m_layer_idx; m_tool_change_idx = 0; } + std::string tool_change(GCodeGenerator &gcodegen, int extruder_id, bool finish_layer); + std::string finalize(GCodeGenerator &gcodegen); + std::vector used_filament_length() const; + +private: + WipeTowerIntegration& operator=(const WipeTowerIntegration&); + std::string append_tcr(GCodeGenerator &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id, double z = -1.) const; + + // Postprocesses gcode: rotates and moves G1 extrusions and returns result + std::string post_process_wipe_tower_moves(const WipeTower::ToolChangeResult& tcr, const Vec2f& translation, float angle) const; + + // Left / right edges of the wipe tower, for the planning of wipe moves. + const float m_left; + const float m_right; + const Vec2f m_wipe_tower_pos; + const float m_wipe_tower_rotation; + const std::vector m_extruder_offsets; + + // Reference to cached values at the Printer class. + const std::vector &m_priming; + const std::vector> &m_tool_changes; + const WipeTower::ToolChangeResult &m_final_purge; + // Current layer index. + int m_layer_idx; + int m_tool_change_idx; + double m_last_wipe_tower_print_z = 0.f; +}; + +} // namespace GCode +} // namespace Slic3r + +#endif // slic3r_GCode_WipeTowerIntegration_hpp_ diff --git a/src/libslic3r/Geometry/ArcWelder.cpp b/src/libslic3r/Geometry/ArcWelder.cpp new file mode 100644 index 0000000000..f0b38a497e --- /dev/null +++ b/src/libslic3r/Geometry/ArcWelder.cpp @@ -0,0 +1,543 @@ +// The following code for merging circles into arches originates from https://github.com/FormerLurker/ArcWelderLib + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Arc Welder: Anti-Stutter Library +// +// Compresses many G0/G1 commands into G2/G3(arc) commands where possible, ensuring the tool paths stay within the specified resolution. +// This reduces file size and the number of gcodes per second. +// +// Uses the 'Gcode Processor Library' for gcode parsing, position processing, logging, and other various functionality. +// +// Copyright(C) 2021 - Brad Hochgesang +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// This program is free software : you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the +// GNU Affero General Public License for more details. +// +// +// You can contact the author at the following email address: +// FormerLurker@pm.me +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "ArcWelder.hpp" + +#include "../MultiPoint.hpp" +#include "../Polygon.hpp" + +#include +#include +#include + +namespace Slic3r { namespace Geometry { namespace ArcWelder { + +// Area of a parallelogram of two vectors to be considered collinear. +static constexpr const double Parallel_area_threshold = 0.0001; +static constexpr const auto Parallel_area_threshold_scaled = int64_t(Parallel_area_threshold / sqr(SCALING_FACTOR)); +// FIXME do we want to use EPSILON here? +static constexpr const double epsilon = 0.000005; + +struct Circle +{ + Point center; + double radius; +}; + +// Interpolate three points with a circle. +// Returns false if the three points are collinear or if the radius is bigger than maximum allowed radius. +//FIXME unit test! +static std::optional try_create_circle(const Point &p1, const Point &p2, const Point &p3, const double max_radius) +{ + // Use area of triangle to judge whether three points are considered collinear. + Vec2i64 v2 = (p2 - p1).cast(); + Vec2i64 v3 = (p3 - p2).cast(); + if (std::abs(cross2(v2, v3)) <= Parallel_area_threshold_scaled) + return {}; + + int64_t det = cross2(p2.cast(), p3.cast()) - cross2(p1.cast(), v3); + if (std::abs(det) < int64_t(SCALED_EPSILON)) + return {}; + + Point center = ((1. / 2.0 * double(det)) * + (double(p1.cast().squaredNorm()) * perp(v3).cast() + + double(p2.cast().squaredNorm()) * perp(p1 - p3).cast() + + double(p3.cast().squaredNorm()) * perp(v2).cast())).cast(); + double r = sqrt(double((center - p1).squaredNorm())); + return r > max_radius ? std::make_optional() : std::make_optional({ center, r }); +} + +// Returns a closest point on the segment. +// Returns false if the closest point is not inside the segment, but at its boundary. +static bool foot_pt_on_segment(const Point &p1, const Point &p2, const Point &c, Point &out) +{ + Vec2i64 v21 = (p2 - p1).cast(); + int64_t denom = v21.squaredNorm(); + if (denom > epsilon) { + if (double t = double((c - p1).cast().dot(v21)) / double(denom); + t >= epsilon && t < 1. - epsilon) { + out = p1 + (t * v21.cast()).cast(); + return true; + } + } + // The segment is short or the closest point is an end point. + return false; +} + +static inline bool circle_approximation_sufficient(const Circle &circle, const Points::const_iterator begin, const Points::const_iterator end, const double tolerance) +{ + // The circle was calculated from the 1st and last point of the point sequence, thus the fitting of those points does not need to be evaluated. + assert(std::abs((*begin - circle.center).cast().norm() - circle.radius) < epsilon); + assert(std::abs((*std::prev(end) - circle.center).cast().norm() - circle.radius) < epsilon); + assert(end - begin >= 3); + + for (auto it = begin; std::next(it) != end; ++ it) { + if (it != begin) { + if (double distance_from_center = (*it - circle.center).cast().norm(); + std::abs(distance_from_center - circle.radius) > tolerance) + return false; + } + Point closest_point; + if (foot_pt_on_segment(*it, *std::next(it), circle.center, closest_point)) { + if (double distance_from_center = (closest_point - circle.center).cast().norm(); + std::abs(distance_from_center - circle.radius) > tolerance) + return false; + } + } + return true; +} + +static inline bool get_deviation_sum_squared(const Circle &circle, const Points::const_iterator begin, const Points::const_iterator end, const double tolerance, double &total_deviation) +{ + // The circle was calculated from the 1st and last point of the point sequence, thus the fitting of those points does not need to be evaluated. + assert(std::abs((*begin - circle.center).cast().norm() - circle.radius) < epsilon); + assert(std::abs((*std::prev(end) - circle.center).cast().norm() - circle.radius) < epsilon); + assert(end - begin >= 3); + + total_deviation = 0; + + const double tolerance2 = sqr(tolerance); + for (auto it = std::next(begin); std::next(it) != end; ++ it) + if (double deviation2 = sqr((*it - circle.center).cast().norm() - circle.radius); deviation2 > tolerance2) + return false; + else + total_deviation += deviation2; + + for (auto it = begin; std::next(it) != end; ++ it) { + Point closest_point; + if (foot_pt_on_segment(*it, *std::next(it), circle.center, closest_point)) { + if (double deviation2 = sqr((closest_point - circle.center).cast().norm() - circle.radius); deviation2 > tolerance2) + return false; + else + total_deviation += deviation2; + } + } + + return true; +} + +static std::optional try_create_circle(const Points::const_iterator begin, const Points::const_iterator end, const double max_radius, const double tolerance) +{ + std::optional out; + size_t size = end - begin; + if (size == 3) { + out = try_create_circle(*begin, *std::next(begin), *std::prev(end), max_radius); + if (! circle_approximation_sufficient(*out, begin, end, tolerance)) + out.reset(); + } else { + size_t ipivot = size / 2; + // Take a center difference of points at the center of the path. + //FIXME does it really help? For short arches, the linear interpolation may be + Point pivot = (size % 2 == 0) ? (*(begin + ipivot) + *(begin + ipivot - 1)) / 2 : + (*(begin + ipivot - 1) + *(begin + ipivot + 1)) / 2; + if (std::optional circle = try_create_circle(*begin, pivot, *std::prev(end), max_radius); + circle_approximation_sufficient(*circle, begin, end, tolerance)) + return circle; + + // Find the circle with the least deviation, if one exists. + double least_deviation; + double current_deviation; + for (auto it = std::next(begin); std::next(it) != end; ++ it) + if (std::optional circle = try_create_circle(*begin, *it, *std::prev(end), max_radius); + circle && get_deviation_sum_squared(*circle, begin, end, tolerance, current_deviation)) { + if (! out || current_deviation < least_deviation) { + out = circle; + least_deviation = current_deviation; + } + } + } + return out; +} + +// ported from ArcWelderLib/ArcWelder/segmented/shape.h class "arc" +class Arc { +public: + + Arc() {} +#if 0 + Arc(Point center, double radius, Point start, Point end, Orientation dir) : + center(center), + radius(radius), + start_point(start), + end_point(end), + direction(dir) { + if (radius == 0.0 || + start_point == center || + end_point == center || + start_point == end_point) { + is_arc = false; + return; + } + is_arc = true; + } +#endif + + Point center; + double radius { 0 }; + bool is_arc { false }; + Point start_point{ 0, 0 }; + Point end_point{ 0, 0 }; + Orientation direction { Orientation::Unknown }; + + static std::optional try_create_arc( + const Points::const_iterator begin, + const Points::const_iterator end, + double max_radius = default_scaled_max_radius, + double tolerance = default_scaled_resolution, + double path_tolerance_percent = default_arc_length_percent_tolerance); + + bool is_valid() const { return is_arc; } +}; + +static inline int sign(const int64_t i) +{ + return i > 0 ? 1 : i < 0 ? -1 : 0; +} + +static inline std::optional try_create_arc_impl( + const Circle &circle, + const Points::const_iterator begin, + const Points::const_iterator end, + double path_tolerance_percent) +{ + assert(end - begin >= 3); + // Assumption: Two successive points of a single segment span an angle smaller than PI. + Vec2i64 vstart = (*begin - circle.center).cast(); + Vec2i64 vprev = vstart; + int arc_dir = 0; + for (auto it = std::next(begin); it != end; ++ it) { + Vec2i64 v = (*it - circle.center).cast(); + int dir = sign(cross2(vprev, v)); + if (dir == 0) { + // Ignore radial segments. + } else if (arc_dir * dir < 0) { + // The path turns back and overextrudes. Such path is likely invalid, but the arc interpolation should not cover it. + return {}; + } else { + // Success, moving in the same direction. + arc_dir = dir; + vprev = v; + } + } + + if (arc_dir == 0) + // All points were radial, this should not happen. + return {}; + + Vec2i64 vend = (*std::prev(end) - circle.center).cast(); + double angle = atan2(double(cross2(vstart, vend)), double(vstart.dot(vend))); + if (arc_dir > 0) { + if (angle < 0) + angle += 2. * M_PI; + } else { + if (angle > 0) + angle -= 2. * M_PI; + } + + // Check the length against the original length. + // This can trigger simply due to the differing path lengths + // but also could indicate that the vector calculation above + // got wrong direction + const double arc_length = std::abs(circle.radius * angle); + const double approximate_length = length(begin, end); + assert(approximate_length > 0); + const double arc_length_difference_relative = (arc_length - approximate_length) / approximate_length; + if (std::fabs(arc_length_difference_relative) >= path_tolerance_percent) + return {}; + + Arc out; + out.is_arc = true; + out.direction = arc_dir > 0 ? Orientation::CCW : Orientation::CW; + out.center = circle.center; + out.radius = circle.radius; + out.start_point = *begin; + out.end_point = *std::prev(end); + return std::make_optional(out); +} + +std::optional Arc::try_create_arc( + const Points::const_iterator begin, + const Points::const_iterator end, + double max_radius, + double tolerance, + double path_tolerance_percent) +{ + std::optional circle = try_create_circle(begin, end, max_radius, tolerance); + if (! circle) + return {}; + return try_create_arc_impl(*circle, begin, end, path_tolerance_percent); +} + +float arc_angle(const Vec2f &start_pos, const Vec2f &end_pos, Vec2f ¢er_pos, bool is_ccw) +{ + if ((end_pos - start_pos).squaredNorm() < sqr(1e-6)) { + // If start equals end, full circle is considered. + return float(2. * M_PI); + } else { + Vec2f v1 = start_pos - center_pos; + Vec2f v2 = end_pos - center_pos; + if (! is_ccw) + std::swap(v1, v2); + float radian = atan2(cross2(v1, v2), v1.dot(v2)); + return radian < 0 ? float(2. * M_PI) + radian : radian; + } +} + +float arc_length(const Vec2f &start_pos, const Vec2f &end_pos, Vec2f ¢er_pos, bool is_ccw) +{ + return (center_pos - start_pos).norm() * arc_angle(start_pos, end_pos, center_pos, is_ccw); +} + +// Reduces polyline in the (begin, end, begin, tolerance, [](const Segment &s) { return s.point; }); +} + +Path fit_path(const Points &src, double tolerance, double fit_circle_percent_tolerance) +{ + assert(tolerance >= 0); + assert(fit_circle_percent_tolerance >= 0); + + Path out; + out.reserve(src.size()); + if (tolerance <= 0 || src.size() <= 2) { + // No simplification, just convert. + std::transform(src.begin(), src.end(), std::back_inserter(out), [](const Point &p) -> Segment { return { p }; }); + } else if (fit_circle_percent_tolerance <= 0) { + // Convert and simplify to a polyline. + std::transform(src.begin(), src.end(), std::back_inserter(out), [](const Point &p) -> Segment { return { p }; }); + out.erase(douglas_peucker_in_place(out.begin(), out.end(), tolerance), out.end()); + } else { + // Perform simplification & fitting. + int begin_pl_idx = 0; + for (auto begin = src.begin(); begin < src.end();) { + // Minimum 3 points required for circle fitting. + auto end = begin + 3; + std::optional arc; + while (end <= src.end()) { + if (std::optional this_arc = ArcWelder::Arc::try_create_arc( + begin, end, + ArcWelder::default_scaled_max_radius, + tolerance, fit_circle_percent_tolerance); + this_arc) { + arc = this_arc; + ++ end; + } else + break; + } + if (arc) { + // If there is a trailing polyline, decimate it first before saving a new arc. + if (out.size() - begin_pl_idx > 2) + out.erase(douglas_peucker_in_place(out.begin() + begin_pl_idx, out.end(), tolerance), out.end()); + // Save the end of the last circle segment, which may become the first point of a possible future polyline. + begin_pl_idx = int(out.size()); + -- end; + out.push_back({ arc->end_point, float(arc->direction == Orientation::CCW ? arc->radius : - arc->radius) }); + } else + out.push_back({ arc->end_point, 0.f }); + } + } + + return out; +} + +void reverse(Path &path) +{ + if (path.size() > 1) { + std::reverse(path.begin(), path.end()); + auto prev = path.begin(); + for (auto it = std::next(prev); it != path.end(); ++ it) { + it->radius = prev->radius; + it->orientation = prev->orientation == Orientation::CCW ? Orientation::CW : Orientation::CCW; + prev = it; + } + path.front().radius = 0; + } +} + +double clip_start(Path &path, const double len) +{ + reverse(path); + double remaining = clip_end(path, len); + reverse(path); + // Return remaining distance to go. + return remaining; +} + +double clip_end(Path &path, double distance) +{ + while (distance > 0) { + Segment &last = path.back(); + path.pop_back(); + if (path.empty()) + break; + if (last.linear()) { + // Linear segment + Vec2d v = (path.back().point - last.point).cast(); + double lsqr = v.squaredNorm(); + if (lsqr > sqr(distance)) { + path.push_back({ last.point + (v * (distance / sqrt(lsqr))).cast(), 0.f, Orientation::CCW }); + return 0; + } + distance -= sqrt(lsqr); + } else { + // Circular segment + float angle = arc_angle(path.back().point.cast(), last.point.cast(), last.radius); + double len = std::abs(last.radius) * angle; + if (len > distance) { + // Rotate the segment end point in reverse towards the start point. + path.push_back({ + last.point.rotated(- angle * (distance / len), + arc_center(path.back().point.cast(), last.point.cast(), last.radius, last.ccw()).cast()), + last.radius, last.orientation }); + return 0; + } + distance -= len; + } + } + + // Return remaining distance to go. + assert(distance >= 0); + return distance; +} + +PathSegmentProjection point_to_path_projection(const Path &path, const Point &point, double search_radius2) +{ + assert(path.size() != 1); + PathSegmentProjection out; + out.distance2 = search_radius2; + if (path.size() < 2 || path.front().point == point) { + // First point is the closest point. + if (path.empty()) { + } else if (const Point p0 = path.front().point; p0 == point) { + out.segment_id = 0; + out.point = p0; + out.distance2 = 0; + } else if (double d2 = (p0 - point).cast().squaredNorm(); d2 < out.distance2) { + out.segment_id = 0; + out.point = p0; + out.distance2 = d2; + } + } else { + auto min_point_it = path.cbegin(); + Point prev = path.front().point; + for (auto it = path.cbegin() + 1; it != path.cend(); ++ it) { + if (it->linear()) { + // Linear segment + Point proj; + if (double d2 = line_alg::distance_to_squared(Line(prev, it->point), point, &proj); d2 < out.distance2) { + out.point = proj; + out.distance2 = d2; + min_point_it = it; + } + } else { + // Circular arc + Vec2i64 center = arc_center(prev.cast(), it->point.cast(), it->radius, it->ccw()).cast(); + // Test whether point is inside the wedge. + Vec2i64 v1 = prev.cast() - center; + Vec2i64 v2 = it->point.cast() - center; + Vec2i64 vp = point.cast() - center; + bool inside = it->radius > 0 ? + // Smaller (convex) wedge. + (it->ccw() ? + cross2(v1, vp) > 0 && cross2(vp, v2) > 0 : + cross2(v1, vp) < 0 && cross2(vp, v2) < 0) : + // Larger (concave) wedge. + (it->ccw() ? + cross2(v2, vp) < 0 || cross2(vp, v1) < 0 : + cross2(v2, vp) > 0 || cross2(vp, v1) > 0); + if (inside) { + // Distance of the radii. + if (double d2 = sqr(std::abs(it->radius) - sqrt(double(v1.squaredNorm()))); d2 < out.distance2) { + out.distance2 = d2; + min_point_it = it; + } + } else { + // Distance to the start point. + if (double d2 = double((v1 - vp).squaredNorm()); d2 < out.distance2) { + out.point = prev; + out.distance2 = d2; + min_point_it = it; + } + } + } + prev = it->point; + } + if (! path.back().linear()) { + // Calculate distance to the end point. + if (double d2 = (path.back().point - point).cast().norm(); d2 < out.distance2) { + out.point = path.back().point; + out.distance2 = d2; + min_point_it = std::prev(path.end()); + } + } + out.segment_id = min_point_it - path.begin(); + } + + return out; +} + +std::pair split_at(const Path &path, PathSegmentProjection proj, const double min_segment_length) +{ + std::pair out; + if (proj.segment_id == 0 && proj.point == path.front().point) + out.second = path; + else if (proj.segment_id + 1 == path.size() || (proj.segment_id + 2 == path.size() && proj.point == path.back().point)) + out.first = path; + else { + const Segment &start = path[proj.segment_id]; + const Segment &end = path[proj.segment_id + 1]; + bool split_segment = true; + if (int64_t d = (proj.point - start.point).cast().squaredNorm(); d < sqr(min_segment_length)) { + split_segment = false; + } else if (int64_t d = (proj.point - end.point).cast().squaredNorm(); d < sqr(min_segment_length)) { + ++ proj.segment_id; + split_segment = false; + } + if (split_segment) { + out.first.assign(path.begin(), path.begin() + proj.segment_id + 2); + out.second.assign(path.begin() + proj.segment_id, path.end()); + out.first.back().point = proj.point; + out.second.front().point = proj.point; + } else { + out.first.assign(path.begin(), path.begin() + proj.segment_id + 1); + out.second.assign(path.begin() + proj.segment_id, path.end()); + } + out.second.front().radius = 0; + } + + return out; +} + +std::pair split_at(const Path &path, const Point &point, const double min_segment_length) +{ + return split_at(path, point_to_path_projection(path, point), min_segment_length); +} + +} } } // namespace Slic3r::Geometry::ArcWelder diff --git a/src/libslic3r/Geometry/ArcWelder.hpp b/src/libslic3r/Geometry/ArcWelder.hpp new file mode 100644 index 0000000000..4974060c55 --- /dev/null +++ b/src/libslic3r/Geometry/ArcWelder.hpp @@ -0,0 +1,196 @@ +// The following code for merging circles into arches originates from https://github.com/FormerLurker/ArcWelderLib + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Arc Welder: Anti-Stutter Library +// +// Compresses many G0/G1 commands into G2/G3(arc) commands where possible, ensuring the tool paths stay within the specified resolution. +// This reduces file size and the number of gcodes per second. +// +// Uses the 'Gcode Processor Library' for gcode parsing, position processing, logging, and other various functionality. +// +// Copyright(C) 2021 - Brad Hochgesang +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// This program is free software : you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the +// GNU Affero General Public License for more details. +// +// +// You can contact the author at the following email address: +// FormerLurker@pm.me +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef slic3r_Geometry_ArcWelder_hpp_ +#define slic3r_Geometry_ArcWelder_hpp_ + +#include + +#include "../Point.hpp" + +namespace Slic3r { namespace Geometry { namespace ArcWelder { + +// Calculate center point of an arc given two points and a radius. +// positive radius: take shorter arc +// negative radius: take longer arc +// radius must NOT be zero! +template +inline Eigen::Matrix arc_center( + const Eigen::MatrixBase &start_pos, + const Eigen::MatrixBase &end_pos, + const typename Derived::Scalar radius, + const bool is_ccw) +{ + static_assert(Derived::IsVectorAtCompileTime && int(Derived::SizeAtCompileTime) == 2, "arc_center(): first parameter is not a 2D vector"); + static_assert(Derived2::IsVectorAtCompileTime && int(Derived2::SizeAtCompileTime) == 2, "arc_center(): second parameter is not a 2D vector"); + static_assert(std::is_same::value, "arc_center(): Both vectors must be of the same type."); + assert(radius != 0); + using Float = typename Derived::Scalar; + using Vector = Eigen::Matrix; + auto v = end_pos - start_pos; + Float q2 = v.squaredNorm(); + assert(q2 > 0); + Float t = sqrt(sqr(radius) / q2 - Float(.25f)); + auto mid = Float(0.5) * (start_pos + end_pos); + Vector vp{ -v.y() * t, v.x() * t }; + return (radius > Float(0)) == is_ccw ? (mid + vp).eval() : (mid - vp).eval(); +} + +// Calculate angle of an arc given two points and a radius. +// Returned angle is in the range <0, 2 PI) +// positive radius: take shorter arc +// negative radius: take longer arc +// radius must NOT be zero! +template +inline typename Derived::Scalar arc_angle( + const Eigen::MatrixBase &start_pos, + const Eigen::MatrixBase &end_pos, + const typename Derived::Scalar radius) +{ + static_assert(Derived::IsVectorAtCompileTime && int(Derived::SizeAtCompileTime) == 2, "arc_angle(): first parameter is not a 2D vector"); + static_assert(Derived2::IsVectorAtCompileTime && int(Derived2::SizeAtCompileTime) == 2, "arc_angle(): second parameter is not a 2D vector"); + static_assert(std::is_same::value, "arc_angle(): Both vectors must be of the same type."); + assert(radius != 0); + using Float = typename Derived::Scalar; + Float a = Float(2.) * asin(Float(0.5) * (end_pos - start_pos).norm() / radius); + return radius > Float(0) ? a : Float(2. * M_PI) + a; +} + +// Calculate positive length of an arc given two points and a radius. +// positive radius: take shorter arc +// negative radius: take longer arc +// radius must NOT be zero! +template +inline typename Derived::Scalar arc_length( + const Eigen::MatrixBase &start_pos, + const Eigen::MatrixBase &end_pos, + const typename Derived::Scalar radius) +{ + static_assert(Derived::IsVectorAtCompileTime && int(Derived::SizeAtCompileTime) == 2, "arc_length(): first parameter is not a 2D vector"); + static_assert(Derived2::IsVectorAtCompileTime && int(Derived2::SizeAtCompileTime) == 2, "arc_length(): second parameter is not a 2D vector"); + static_assert(std::is_same::value, "arc_length(): Both vectors must be of the same type."); + assert(radius != 0); + return arc_angle(start_pos, end_pos, radius) * std::abs(radius); +} + +// 1.2m diameter, maximum given by coord_t +static constexpr const double default_scaled_max_radius = scaled(600.); +// 0.05mm +static constexpr const double default_scaled_resolution = scaled(0.05); +// 5 percent +static constexpr const double default_arc_length_percent_tolerance = 0.05; + +enum class Orientation : unsigned char { + Unknown, + CCW, + CW, +}; + +// Single segment of a smooth path. +struct Segment +{ + // End point of a linear or circular segment. + // Start point is provided by the preceding segment. + Point point; + // Radius of a circular segment. Positive - take the shorter arc. Negative - take the longer arc. Zero - linear segment. + float radius{ 0.f }; + // CCW or CW. Ignored for zero radius (linear segment). + Orientation orientation{ Orientation::CCW }; + + bool linear() const { return radius == 0; } + bool ccw() const { return orientation == Orientation::CCW; } + bool cw() const { return orientation == Orientation::CW; } +}; + +using Segments = std::vector; +using Path = Segments; + +// Interpolate polyline path with a sequence of linear / circular segments given the interpolation tolerance. +// Only convert to polyline if zero tolerance. +// Convert to polyline and decimate polyline if zero fit_circle_percent_tolerance. +Path fit_path(const Points &points, double tolerance, double fit_circle_percent_tolerance); +inline Path fit_polyline(const Points &points, double tolerance) { return fit_path(points, tolerance, 0.); } + +inline double segment_length(const Segment &start, const Segment &end) +{ + return end.linear() ? + (end.point - start.point).cast().norm() : + arc_length(start.point.cast(), end.point.cast(), end.radius); +} + +// Estimate minimum path length of a segment cheaply without having to calculate center of an arc and it arc length. +// Used for caching a smooth path chunk that is certainly longer than a threshold. +inline int64_t estimate_min_segment_length(const Segment &start, const Segment &end) +{ + if (end.linear() || end.radius > 0) { + // Linear segment or convex wedge, take the larger X or Y component. + Point v = (end.point - start.point).cwiseAbs(); + return std::max(v.x(), v.y()); + } else { + // Arc with angle > PI. + // Returns estimate of PI * r + return - 3 * int64_t(end.radius); + } +} + +// Estimate minimum path length cheaply without having to calculate center of an arc and it arc length. +// Used for caching a smooth path chunk that is certainly longer than a threshold. +inline int64_t estimate_path_length(const Path &path) +{ + int64_t len = 0; + for (size_t i = 1; i < path.size(); ++ i) + len += Geometry::ArcWelder::estimate_min_segment_length(path[i - 1], path[i]); + return len; +} + +void reverse(Path &path); + +// Clip start / end of a smooth path by len. +// If path is shorter than len, remaining path length to trim will be returned. +double clip_start(Path &path, const double len); +double clip_end(Path &path, const double len); + +struct PathSegmentProjection +{ + // Start segment of a projection on the path. + size_t segment_id { std::numeric_limits::max() }; + Point point { 0, 0 }; + // Square of a distance of the projection. + double distance2 { std::numeric_limits::max() }; + + bool valid() const { return this->segment_id != std::numeric_limits::max(); } +}; +// Returns closest segment and a parameter along the closest segment of a path to a point. +PathSegmentProjection point_to_path_projection(const Path &path, const Point &point, double search_radius2 = std::numeric_limits::max()); +// Split a path into two paths at a segment point. Snap to an existing point if the projection of "point is closer than min_segment_length. +std::pair split_at(const Path &path, PathSegmentProjection proj, const double min_segment_length); +// Split a path into two paths at a point closest to "point". Snap to an existing point if the projection of "point is closer than min_segment_length. +std::pair split_at(const Path &path, const Point &point, const double min_segment_length); + +} } } // namespace Slic3r::Geometry::ArcWelder + +#endif // slic3r_Geometry_ArcWelder_hpp_ diff --git a/src/libslic3r/Geometry/Circle.hpp b/src/libslic3r/Geometry/Circle.hpp index 653102e2ab..ba048dcd63 100644 --- a/src/libslic3r/Geometry/Circle.hpp +++ b/src/libslic3r/Geometry/Circle.hpp @@ -65,7 +65,7 @@ struct Circle { Vector center; Scalar radius; - Circle() {} + Circle() = default; Circle(const Vector ¢er, const Scalar radius) : center(center), radius(radius) {} Circle(const Vector &a, const Vector &b) : center(Scalar(0.5) * (a + b)) { radius = (a - center).norm(); } Circle(const Vector &a, const Vector &b, const Vector &c, const Scalar epsilon) { *this = CircleSq(a, b, c, epsilon); } diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 16cc75225a..feb9e907ae 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -27,7 +27,7 @@ #include "SVG.hpp" #include -#include "GCodeWriter.hpp" +#include "GCode/GCodeWriter.hpp" namespace Slic3r { diff --git a/src/libslic3r/MultiPoint.cpp b/src/libslic3r/MultiPoint.cpp index fb4727abe5..f234064534 100644 --- a/src/libslic3r/MultiPoint.cpp +++ b/src/libslic3r/MultiPoint.cpp @@ -103,100 +103,6 @@ bool MultiPoint::remove_duplicate_points() return false; } -Points MultiPoint::douglas_peucker(const Points &pts, const double tolerance) -{ - Points result_pts; - auto tolerance_sq = int64_t(sqr(tolerance)); - if (! pts.empty()) { - const Point *anchor = &pts.front(); - size_t anchor_idx = 0; - const Point *floater = &pts.back(); - size_t floater_idx = pts.size() - 1; - result_pts.reserve(pts.size()); - result_pts.emplace_back(*anchor); - if (anchor_idx != floater_idx) { - assert(pts.size() > 1); - std::vector dpStack; - dpStack.reserve(pts.size()); - dpStack.emplace_back(floater_idx); - for (;;) { - int64_t max_dist_sq = 0; - size_t furthest_idx = anchor_idx; - // find point furthest from line seg created by (anchor, floater) and note it - { - const Point a = *anchor; - const Point f = *floater; - const Vec2i64 v = (f - a).cast(); - if (const int64_t l2 = v.squaredNorm(); l2 == 0) { - for (size_t i = anchor_idx + 1; i < floater_idx; ++ i) - if (int64_t dist_sq = (pts[i] - a).cast().squaredNorm(); dist_sq > max_dist_sq) { - max_dist_sq = dist_sq; - furthest_idx = i; - } - } else { - const double dl2 = double(l2); - const Vec2d dv = v.cast(); - for (size_t i = anchor_idx + 1; i < floater_idx; ++ i) { - const Point p = pts[i]; - const Vec2i64 va = (p - a).template cast(); - const int64_t t = va.dot(v); - int64_t dist_sq; - if (t <= 0) { - dist_sq = va.squaredNorm(); - } else if (t >= l2) { - dist_sq = (p - f).cast().squaredNorm(); - } else { - const Vec2i64 w = ((double(t) / dl2) * dv).cast(); - dist_sq = (w - va).squaredNorm(); - } - if (dist_sq > max_dist_sq) { - max_dist_sq = dist_sq; - furthest_idx = i; - } - } - } - } - // remove point if less than tolerance - if (max_dist_sq <= tolerance_sq) { - result_pts.emplace_back(*floater); - anchor_idx = floater_idx; - anchor = floater; - assert(dpStack.back() == floater_idx); - dpStack.pop_back(); - if (dpStack.empty()) - break; - floater_idx = dpStack.back(); - } else { - floater_idx = furthest_idx; - dpStack.emplace_back(floater_idx); - } - floater = &pts[floater_idx]; - } - } - assert(result_pts.front() == pts.front()); - assert(result_pts.back() == pts.back()); - -#if 0 - { - static int iRun = 0; - BoundingBox bbox(pts); - BoundingBox bbox2(result_pts); - bbox.merge(bbox2); - SVG svg(debug_out_path("douglas_peucker_%d.svg", iRun ++).c_str(), bbox); - if (pts.front() == pts.back()) - svg.draw(Polygon(pts), "black"); - else - svg.draw(Polyline(pts), "black"); - if (result_pts.front() == result_pts.back()) - svg.draw(Polygon(result_pts), "green", scale_(0.1)); - else - svg.draw(Polyline(result_pts), "green", scale_(0.1)); - } -#endif - } - return result_pts; -} - // Visivalingam simplification algorithm https://github.com/slic3r/Slic3r/pull/3825 // thanks to @fuchstraumer /* @@ -219,7 +125,7 @@ struct vis_node{ // other node if it's area is less than the other node's area bool operator<(const vis_node& other) { return (this->area < other.area); } }; -Points MultiPoint::visivalingam(const Points& pts, const double& tolerance) +Points MultiPoint::visivalingam(const Points &pts, const double tolerance) { // Make sure there's enough points in "pts" to bother with simplification. assert(pts.size() >= 2); diff --git a/src/libslic3r/MultiPoint.hpp b/src/libslic3r/MultiPoint.hpp index 62b53255b4..dcd192bac7 100644 --- a/src/libslic3r/MultiPoint.hpp +++ b/src/libslic3r/MultiPoint.hpp @@ -12,6 +12,123 @@ namespace Slic3r { class BoundingBox; class BoundingBox3; +// Reduces polyline in the +inline OutputIterator douglas_peucker(InputIterator begin, InputIterator end, OutputIterator out, const double tolerance, PointGetter point_getter) +{ + using InputIteratorCategory = typename std::iterator_traits::iterator_category; + static_assert(std::is_base_of_v); + using Point = typename InputIterator::value_type; + using Vector = Eigen::Matrix; + if (begin != end) { + // Supporting in-place reduction and the data type may be generic, thus we are always making a copy of the point value before there is a chance + // to override input by moving the data to the output. + auto a = point_getter(*begin); + *out ++ = std::move(*begin); + if (auto next = std::next(begin); next == end) { + // Single point input only. + } else if (std::next(next) == end) { + // Two points input. + *out ++ = std::move(*next); + } else { + const auto tolerance_sq = SquareLengthType(sqr(tolerance)); + InputIterator anchor = begin; + InputIterator floater = std::prev(end); + std::vector dpStack; + if constexpr (std::is_base_of_v) + dpStack.reserve(end - begin); + dpStack.emplace_back(floater); + auto f = point_getter(*floater); + for (;;) { + assert(anchor != floater); + bool take_floater = false; + InputIterator furthest = anchor; + if (std::next(anchor) == floater) { + // Two point segment. Accept the floater. + take_floater = true; + } else { + SquareLengthType max_dist_sq = 0; + // Find point furthest from line seg created by (anchor, floater) and note it. + const Vector v = (f - a).cast(); + if (const SquareLengthType l2 = v.squaredNorm(); l2 == 0) { + // Zero length segment, find the furthest point between anchor and floater. + for (auto it = std::next(anchor); it != floater; ++ it) + if (SquareLengthType dist_sq = (point_getter(*it) - a).cast().squaredNorm(); dist_sq > max_dist_sq) { + max_dist_sq = dist_sq; + furthest = it; + } + } else { + // Find Find the furthest point from the line . + const double dl2 = double(l2); + const Vec2d dv = v.cast(); + for (auto it = std::next(anchor); it != floater; ++ it) { + const auto p = point_getter(*it); + const Vector va = (p - a).template cast(); + const SquareLengthType t = va.dot(v); + SquareLengthType dist_sq; + if (t <= 0) { + dist_sq = va.squaredNorm(); + } else if (t >= l2) { + dist_sq = (p - f).cast().squaredNorm(); + } else if (double dt = double(t) / dl2; dt <= 0) { + dist_sq = va.squaredNorm(); + } else if (dt >= 1.) { + dist_sq = (p - f).cast().squaredNorm(); + } else { + const Vector w = (dt * dv).cast(); + dist_sq = (w - va).squaredNorm(); + } + if (dist_sq > max_dist_sq) { + max_dist_sq = dist_sq; + furthest = it; + } + } + } + // remove point if less than tolerance + take_floater = max_dist_sq <= tolerance_sq; + } + if (take_floater) { + // The points between anchor and floater are close to the line. + // Drop the points between them. + a = f; + *out ++ = std::move(*floater); + anchor = floater; + assert(dpStack.back() == floater); + dpStack.pop_back(); + if (dpStack.empty()) + break; + floater = dpStack.back(); + f = point_getter(*floater); + } else { + // The furthest point is too far from the segment . + // Divide recursively. + floater = furthest; + f = point_getter(*floater); + dpStack.emplace_back(floater); + } + } + } + } + return out; +} + +// Reduces polyline in the +inline OutputIterator douglas_peucker(Points::const_iterator begin, Points::const_iterator end, OutputIterator out, const double tolerance) +{ + return douglas_peucker(begin, end, out, tolerance, [](const Point &p) { return p; }); +} + +inline Points douglas_peucker(const Points &src, const double tolerance) +{ + Points out; + out.reserve(src.size()); + douglas_peucker(src.begin(), src.end(), std::back_inserter(out), tolerance); + return out; +} + class MultiPoint { public: @@ -81,8 +198,8 @@ public: } } - static Points douglas_peucker(const Points &points, const double tolerance); - static Points visivalingam(const Points& pts, const double& tolerance); + static Points douglas_peucker(const Points &src, const double tolerance) { return Slic3r::douglas_peucker(src, tolerance); } + static Points visivalingam(const Points &src, const double tolerance); inline auto begin() { return points.begin(); } inline auto begin() const { return points.begin(); } @@ -113,16 +230,20 @@ extern BoundingBox get_extents(const MultiPoint &mp); extern BoundingBox get_extents_rotated(const Points &points, double angle); extern BoundingBox get_extents_rotated(const MultiPoint &mp, double angle); -inline double length(const Points &pts) { +inline double length(const Points::const_iterator begin, const Points::const_iterator end) { double total = 0; - if (! pts.empty()) { - auto it = pts.begin(); - for (auto it_prev = it ++; it != pts.end(); ++ it, ++ it_prev) + if (begin != end) { + auto it = begin; + for (auto it_prev = it ++; it != end; ++ it, ++ it_prev) total += (*it - *it_prev).cast().norm(); } return total; } +inline double length(const Points &pts) { + return length(pts.begin(), pts.end()); +} + inline double area(const Points &polygon) { double area = 0.; for (size_t i = 0, j = polygon.size() - 1; i < polygon.size(); j = i ++) diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index b0fd7f35b8..a108f42de8 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -119,20 +119,18 @@ ExtrusionMultiPath PerimeterGenerator::thick_polyline_to_multi_path(const ThickP const double w = fmax(line.a_width, line.b_width); const Flow new_flow = (role.is_bridge() && flow.bridge()) ? flow : flow.with_width(unscale(w) + flow.height() * float(1. - 0.25 * PI)); - if (path.polyline.points.empty()) { - path.polyline.append(line.a); - path.polyline.append(line.b); + if (path.empty()) { // Convert from spacing to extrusion width based on the extrusion model // of a square extrusion ended with semi circles. + path = { ExtrusionAttributes{ path.role(), new_flow } }; + path.polyline.append(line.a); + path.polyline.append(line.b); #ifdef SLIC3R_DEBUG printf(" filling %f gap\n", flow.width); #endif - path.mm3_per_mm = new_flow.mm3_per_mm(); - path.width = new_flow.width(); - path.height = new_flow.height(); } else { - assert(path.width >= EPSILON); - thickness_delta = scaled(fabs(path.width - new_flow.width())); + assert(path.width() >= EPSILON); + thickness_delta = scaled(fabs(path.width() - new_flow.width())); if (thickness_delta <= merge_tolerance) { // the width difference between this line and the current flow // (of the previous line) width is within the accepted tolerance @@ -325,10 +323,12 @@ static ExtrusionEntityCollection traverse_loops_classic(const PerimeterGenerator extrusion_paths_append( paths, intersection_pl({ polygon }, lower_slices_polygons_clipped), - role_normal, - is_external ? params.ext_mm3_per_mm : params.mm3_per_mm, - is_external ? params.ext_perimeter_flow.width() : params.perimeter_flow.width(), - float(params.layer_height)); + ExtrusionAttributes{ + role_normal, + ExtrusionFlow{ is_external ? params.ext_mm3_per_mm : params.mm3_per_mm, + is_external ? params.ext_perimeter_flow.width() : params.perimeter_flow.width(), + float(params.layer_height) + } }); // get overhang paths by checking what parts of this loop fall // outside the grown lower slices (thus where the distance between @@ -336,23 +336,26 @@ static ExtrusionEntityCollection traverse_loops_classic(const PerimeterGenerator extrusion_paths_append( paths, diff_pl({ polygon }, lower_slices_polygons_clipped), - role_overhang, - params.mm3_per_mm_overhang, - params.overhang_flow.width(), - params.overhang_flow.height()); + ExtrusionAttributes{ + role_overhang, + ExtrusionFlow{ params.mm3_per_mm_overhang, params.overhang_flow.width(), params.overhang_flow.height() } + }); // Reapply the nearest point search for starting point. // We allow polyline reversal because Clipper may have randomly reversed polylines during clipping. chain_and_reorder_extrusion_paths(paths, &paths.front().first_point()); } else { - ExtrusionPath path(role_normal); - path.polyline = polygon.split_at_first_point(); - path.mm3_per_mm = is_external ? params.ext_mm3_per_mm : params.mm3_per_mm; - path.width = is_external ? params.ext_perimeter_flow.width() : params.perimeter_flow.width(); - path.height = float(params.layer_height); - paths.push_back(path); + paths.emplace_back(polygon.split_at_first_point(), + ExtrusionAttributes{ + role_normal, + ExtrusionFlow{ + is_external ? params.ext_mm3_per_mm : params.mm3_per_mm, + is_external ? params.ext_perimeter_flow.width() : params.perimeter_flow.width(), + float(params.layer_height) + } + }); } - + coll.append(ExtrusionLoop(std::move(paths), loop_role)); } @@ -383,11 +386,13 @@ static ExtrusionEntityCollection traverse_loops_classic(const PerimeterGenerator ExtrusionLoop *eloop = static_cast(coll.entities[idx.first]); coll.entities[idx.first] = nullptr; if (loop.is_contour) { - eloop->make_counter_clockwise(); + if (eloop->is_clockwise()) + eloop->reverse_loop(); out.append(std::move(children.entities)); out.entities.emplace_back(eloop); } else { - eloop->make_clockwise(); + if (eloop->is_counter_clockwise()) + eloop->reverse_loop(); out.entities.emplace_back(eloop); out.append(std::move(children.entities)); } @@ -603,10 +608,8 @@ static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator::P if (extrusion->is_closed) { ExtrusionLoop extrusion_loop(std::move(paths)); // Restore the orientation of the extrusion loop. - if (pg_extrusion.is_contour) - extrusion_loop.make_counter_clockwise(); - else - extrusion_loop.make_clockwise(); + if (pg_extrusion.is_contour == extrusion_loop.is_clockwise()) + extrusion_loop.reverse_loop(); for (auto it = std::next(extrusion_loop.paths.begin()); it != extrusion_loop.paths.end(); ++it) { assert(it->polyline.points.size() >= 2); @@ -960,11 +963,9 @@ std::tuple, Polygons> generate_extra_perimeters_over if (perimeter_polygon.empty()) { // fill possible gaps of single extrusion width Polygons shrinked = intersection(offset(prev, -0.3 * overhang_flow.scaled_spacing()), expanded_overhang_to_cover); - if (!shrinked.empty()) { + if (!shrinked.empty()) extrusion_paths_append(overhang_region, reconnect_polylines(perimeter, overhang_flow.scaled_spacing()), - ExtrusionRole::OverhangPerimeter, overhang_flow.mm3_per_mm(), overhang_flow.width(), - overhang_flow.height()); - } + ExtrusionAttributes{ ExtrusionRole::OverhangPerimeter, overhang_flow }); Polylines fills; ExPolygons gap = shrinked.empty() ? offset_ex(prev, overhang_flow.scaled_spacing() * 0.5) : to_expolygons(shrinked); @@ -975,14 +976,12 @@ std::tuple, Polygons> generate_extra_perimeters_over if (!fills.empty()) { fills = intersection_pl(fills, shrinked_overhang_to_cover); extrusion_paths_append(overhang_region, reconnect_polylines(fills, overhang_flow.scaled_spacing()), - ExtrusionRole::OverhangPerimeter, overhang_flow.mm3_per_mm(), overhang_flow.width(), - overhang_flow.height()); + ExtrusionAttributes{ ExtrusionRole::OverhangPerimeter, overhang_flow }); } break; } else { extrusion_paths_append(overhang_region, reconnect_polylines(perimeter, overhang_flow.scaled_spacing()), - ExtrusionRole::OverhangPerimeter, overhang_flow.mm3_per_mm(), overhang_flow.width(), - overhang_flow.height()); + ExtrusionAttributes{ExtrusionRole::OverhangPerimeter, overhang_flow }); } if (intersection(perimeter_polygon, real_overhang).empty()) { continuation_loops--; } diff --git a/src/libslic3r/Point.cpp b/src/libslic3r/Point.cpp index 457bb44cef..1e1de6ae08 100644 --- a/src/libslic3r/Point.cpp +++ b/src/libslic3r/Point.cpp @@ -47,14 +47,12 @@ Pointf3s transform(const Pointf3s& points, const Transform3d& t) void Point::rotate(double angle, const Point ¢er) { - double cur_x = (double)(*this)(0); - double cur_y = (double)(*this)(1); - double s = ::sin(angle); - double c = ::cos(angle); - double dx = cur_x - (double)center(0); - double dy = cur_y - (double)center(1); - (*this)(0) = (coord_t)round( (double)center(0) + c * dx - s * dy ); - (*this)(1) = (coord_t)round( (double)center(1) + c * dy + s * dx ); + Vec2d cur = this->cast(); + double s = ::sin(angle); + double c = ::cos(angle); + auto d = cur - center.cast(); + this->x() = fast_round_up(center.x() + c * d.x() - s * d.y()); + this->y() = fast_round_up(center.y() + c * d.y() + s * d.x()); } bool has_duplicate_points(Points &&pts) diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp index c4b821ca6e..34e9f4135c 100644 --- a/src/libslic3r/Point.hpp +++ b/src/libslic3r/Point.hpp @@ -166,11 +166,12 @@ public: Point(const Point &rhs) { *this = rhs; } explicit Point(const Vec2d& rhs) : Vec2crd(coord_t(std::round(rhs.x())), coord_t(std::round(rhs.y()))) {} // This constructor allows you to construct Point from Eigen expressions + // This constructor has to be implicit (non-explicit) to allow implicit conversion from Eigen expressions. template Point(const Eigen::MatrixBase &other) : Vec2crd(other) {} static Point new_scale(coordf_t x, coordf_t y) { return Point(coord_t(scale_(x)), coord_t(scale_(y))); } - static Point new_scale(const Vec2d &v) { return Point(coord_t(scale_(v.x())), coord_t(scale_(v.y()))); } - static Point new_scale(const Vec2f &v) { return Point(coord_t(scale_(v.x())), coord_t(scale_(v.y()))); } + template + static Point new_scale(const Eigen::MatrixBase &v) { return Point(coord_t(scale_(v.x())), coord_t(scale_(v.y()))); } // This method allows you to assign Eigen expressions to MyVectorType template diff --git a/src/libslic3r/Polyline.cpp b/src/libslic3r/Polyline.cpp index 5261a8cfce..ca4cc09f0e 100644 --- a/src/libslic3r/Polyline.cpp +++ b/src/libslic3r/Polyline.cpp @@ -146,8 +146,10 @@ void Polyline::split_at(const Point &point, Polyline* p1, Polyline* p2) const } if (this->points.front() == point) { + //FIXME why is p1 NOT empty as in the case above? *p1 = { point }; *p2 = *this; + return; } auto min_dist2 = std::numeric_limits::max(); diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 8d53dbb5a8..2dac942e9e 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -997,7 +997,7 @@ std::string Print::export_gcode(const std::string& path_template, GCodeProcessor this->set_status(90, message); // Create GCode on heap, it has quite a lot of data. - std::unique_ptr gcode(new GCode); + std::unique_ptr gcode(new GCodeGenerator); gcode->do_export(this, path.c_str(), result, thumbnail_cb); if (m_conflict_result.has_value()) @@ -1113,13 +1113,15 @@ void Print::_make_skirt() } // Extrude the skirt loop. ExtrusionLoop eloop(elrSkirt); - eloop.paths.emplace_back(ExtrusionPath( - ExtrusionPath( + eloop.paths.emplace_back( + ExtrusionAttributes{ ExtrusionRole::Skirt, - (float)mm3_per_mm, // this will be overridden at G-code export time - flow.width(), - (float)first_layer_height // this will be overridden at G-code export time - ))); + ExtrusionFlow{ + float(mm3_per_mm), // this will be overridden at G-code export time + flow.width(), + float(first_layer_height) // this will be overridden at G-code export time + } + }); eloop.paths.back().polyline = loop.split_at_first_point(); m_skirt.append(eloop); if (m_config.min_skirt_length.value > 0) { @@ -1625,8 +1627,8 @@ std::string PrintStatistics::finalize_output_path(const std::string &path_in) co } - ExtrusionPath path(ExtrusionRole::WipeTower, 0.0, 0.0, lh); - path.polyline = { minCorner, {maxCorner.x(), minCorner.y()}, maxCorner, {minCorner.x(), maxCorner.y()}, minCorner }; + ExtrusionPath path({ minCorner, {maxCorner.x(), minCorner.y()}, maxCorner, {minCorner.x(), maxCorner.y()}, minCorner }, + ExtrusionAttributes{ ExtrusionRole::WipeTower, ExtrusionFlow{ 0.0, 0.0, lh } }); paths.push_back({ path }); // We added the border, now add several parallel lines so we can detect an object that is fully inside the tower. diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 059491951e..0600b02fff 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -29,7 +29,7 @@ namespace Slic3r { -class GCode; +class GCodeGenerator; class Layer; class ModelObject; class Print; @@ -697,7 +697,7 @@ private: Polygons m_sequential_print_clearance_contours; // To allow GCode to set the Print's GCodeExport step status. - friend class GCode; + friend class GCodeGenerator; // To allow GCodeProcessor to emit warnings. friend class GCodeProcessor; // Allow PrintObject to access m_mutex and m_cancel_callback. diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 3da804066b..416ceb2802 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -397,6 +397,22 @@ void PrintConfigDef::init_fff_params() { ConfigOptionDef* def; + def = this->add("arc_fitting", coBool); + def->label = L("Arc fitting"); + def->tooltip = L("Enable this to get a G-code file which has G2 and G3 moves. " + "And the fitting tolerance is same with resolution"); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("arc_fitting_tolerance", coFloatOrPercent); + def->label = L("Arc fitting tolerance"); + def->sidetext = L("mm or %"); + def->tooltip = L("When using the arc_fitting option, allow the curve to deviate a cetain % from the collection of strait paths.\n" + "Can be a mm value or a percentage of the current extrusion width."); + def->mode = comAdvanced; + def->min = 0; + def->set_default_value(new ConfigOptionFloatOrPercent(5, true)); + // Maximum extruder temperature, bumped to 1500 to support printing of glass. const int max_temp = 1500; def = this->add("avoid_crossing_curled_overhangs", coBool); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 54a835fe79..60e4c81196 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -662,6 +662,8 @@ PRINT_CONFIG_CLASS_DEFINE( PRINT_CONFIG_CLASS_DEFINE( GCodeConfig, + ((ConfigOptionBool, arc_fitting)) + ((ConfigOptionFloatOrPercent, arc_fitting_tolerance)) ((ConfigOptionBool, autoemit_temperature_commands)) ((ConfigOptionString, before_layer_gcode)) ((ConfigOptionString, between_objects_gcode)) diff --git a/src/libslic3r/ShortestPath.cpp b/src/libslic3r/ShortestPath.cpp index da1a44ec75..d4433ded25 100644 --- a/src/libslic3r/ShortestPath.cpp +++ b/src/libslic3r/ShortestPath.cpp @@ -1006,16 +1006,20 @@ std::vector> chain_segments_greedy2(SegmentEndPointFunc return chain_segments_greedy_constrained_reversals2_(end_point_func, could_reverse_func, num_segments, start_near); } -std::vector> chain_extrusion_entities(std::vector &entities, const Point *start_near) +std::vector> chain_extrusion_entities(const std::vector &entities, const Point *start_near, const bool reversed) { - auto segment_end_point = [&entities](size_t idx, bool first_point) -> const Point& { return first_point ? entities[idx]->first_point() : entities[idx]->last_point(); }; - auto could_reverse = [&entities](size_t idx) { const ExtrusionEntity *ee = entities[idx]; return ee->is_loop() || ee->can_reverse(); }; - std::vector> out = chain_segments_greedy_constrained_reversals(segment_end_point, could_reverse, entities.size(), start_near); + auto segment_end_point = [&entities, reversed](size_t idx, bool first_point) -> const Point& { return first_point == reversed ? entities[idx]->last_point() : entities[idx]->first_point(); }; + auto could_reverse = [&entities](size_t idx) { const ExtrusionEntity *ee = entities[idx]; return ee->is_loop() || ee->can_reverse(); }; + std::vector> out = chain_segments_greedy_constrained_reversals( + segment_end_point, could_reverse, entities.size(), start_near); for (std::pair &segment : out) { ExtrusionEntity *ee = entities[segment.first]; if (ee->is_loop()) // Ignore reversals for loops, as the start point equals the end point. segment.second = false; + else if (reversed) + // Input was already reversed. + segment.second = ! segment.second; // Is can_reverse() respected by the reversals? assert(ee->can_reverse() || ! segment.second); } @@ -1041,6 +1045,33 @@ void chain_and_reorder_extrusion_entities(std::vector &entitie reorder_extrusion_entities(entities, chain_extrusion_entities(entities, start_near)); } +ExtrusionEntityReferences chain_extrusion_references(const std::vector &entities, const Point *start_near, const bool reversed) +{ + const std::vector> chain = chain_extrusion_entities(entities, start_near, reversed); + ExtrusionEntityReferences out; + out.reserve(chain.size()); + for (const std::pair &idx : chain) { + assert(entities[idx.first] != nullptr); + out.push_back({ *entities[idx.first], idx.second }); + } + return out; +} + +ExtrusionEntityReferences chain_extrusion_references(const ExtrusionEntityCollection &eec, const Point *start_near, const bool reversed) +{ + if (eec.no_sort) { + ExtrusionEntityReferences out; + out.reserve(eec.entities.size()); + for (const ExtrusionEntity *ee : eec.entities) { + assert(ee != nullptr); + // Never reverse a loop. + out.push_back({ *ee, ! ee->is_loop() && reversed }); + } + return out; + } else + return chain_extrusion_references(eec.entities, start_near, reversed); +} + std::vector> chain_extrusion_paths(std::vector &extrusion_paths, const Point *start_near) { auto segment_end_point = [&extrusion_paths](size_t idx, bool first_point) -> const Point& { return first_point ? extrusion_paths[idx].first_point() : extrusion_paths[idx].last_point(); }; diff --git a/src/libslic3r/ShortestPath.hpp b/src/libslic3r/ShortestPath.hpp index 1781c51889..89db309963 100644 --- a/src/libslic3r/ShortestPath.hpp +++ b/src/libslic3r/ShortestPath.hpp @@ -18,13 +18,25 @@ namespace Slic3r { class ExPolygon; using ExPolygons = std::vector; +// Used by chain_expolygons() std::vector chain_points(const Points &points, Point *start_near = nullptr); +// Used to give layer islands a print order. std::vector chain_expolygons(const ExPolygons &expolygons, Point *start_near = nullptr); -std::vector> chain_extrusion_entities(std::vector &entities, const Point *start_near = nullptr); +// Chain extrusion entities by a shortest distance. Returns the ordered extrusions together with a "reverse" flag. +// Set input "reversed" to true if the vector of "entities" is to be considered to be reversed once already. +std::vector> chain_extrusion_entities(const std::vector &entities, const Point *start_near = nullptr, const bool reversed = false); +// Reorder & reverse extrusion entities in place based on the "chain" ordering. void reorder_extrusion_entities(std::vector &entities, const std::vector> &chain); +// Reorder & reverse extrusion entities in place. void chain_and_reorder_extrusion_entities(std::vector &entities, const Point *start_near = nullptr); +// Chain extrusion entities by a shortest distance. Returns the ordered extrusions together with a "reverse" flag. +// Set input "reversed" to true if the vector of "entities" is to be considered to be reversed. +ExtrusionEntityReferences chain_extrusion_references(const std::vector &entities, const Point *start_near = nullptr, const bool reversed = false); +// The same as above, respect eec.no_sort flag. +ExtrusionEntityReferences chain_extrusion_references(const ExtrusionEntityCollection &eec, const Point *start_near = nullptr, const bool reversed = false); + std::vector> chain_extrusion_paths(std::vector &extrusion_paths, const Point *start_near = nullptr); void reorder_extrusion_paths(std::vector &extrusion_paths, std::vector> &chain); void chain_and_reorder_extrusion_paths(std::vector &extrusion_paths, const Point *start_near = nullptr); diff --git a/src/libslic3r/Support/SupportCommon.cpp b/src/libslic3r/Support/SupportCommon.cpp index dce57a72c5..bcd65320ca 100644 --- a/src/libslic3r/Support/SupportCommon.cpp +++ b/src/libslic3r/Support/SupportCommon.cpp @@ -485,8 +485,7 @@ static inline void fill_expolygon_generate_paths( extrusion_entities_append_paths( dst, std::move(polylines), - role, - flow.mm3_per_mm(), flow.width(), flow.height()); + { role, flow }); } static inline void fill_expolygons_generate_paths( @@ -644,7 +643,7 @@ static inline void tree_supports_generate_paths( ExPolygons level2 = offset2_ex({ expoly }, -1.5 * flow.scaled_width(), 0.5 * flow.scaled_width()); if (level2.size() == 1) { Polylines polylines; - extrusion_entities_append_paths(eec->entities, draw_perimeters(expoly, clip_length), ExtrusionRole::SupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height(), + extrusion_entities_append_paths(eec->entities, draw_perimeters(expoly, clip_length), { ExtrusionRole::SupportMaterial, flow }, // Disable reversal of the path, always start with the anchor, always print CCW. false); expoly = level2.front(); @@ -750,7 +749,7 @@ static inline void tree_supports_generate_paths( } ExtrusionEntitiesPtr &out = eec ? eec->entities : dst; - extrusion_entities_append_paths(out, std::move(polylines), ExtrusionRole::SupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height(), + extrusion_entities_append_paths(out, std::move(polylines), { ExtrusionRole::SupportMaterial, flow }, // Disable reversal of the path, always start with the anchor, always print CCW. false); if (eec) { @@ -794,7 +793,7 @@ static inline void fill_expolygons_with_sheath_generate_paths( eec->no_sort = true; } ExtrusionEntitiesPtr &out = no_sort ? eec->entities : dst; - extrusion_entities_append_paths(out, draw_perimeters(expoly, clip_length), ExtrusionRole::SupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height()); + extrusion_entities_append_paths(out, draw_perimeters(expoly, clip_length), { ExtrusionRole::SupportMaterial, flow }); // Fill in the rest. fill_expolygons_generate_paths(out, offset_ex(expoly, float(-0.4 * spacing)), filler, fill_params, density, role, flow); if (no_sort && ! eec->empty()) @@ -1106,7 +1105,7 @@ void LoopInterfaceProcessor::generate(SupportGeneratorLayerExtruded &top_contact extrusion_entities_append_paths( top_contact_layer.extrusions, std::move(loop_lines), - ExtrusionRole::SupportMaterialInterface, flow.mm3_per_mm(), flow.width(), flow.height()); + { ExtrusionRole::SupportMaterialInterface, flow }); } #ifdef SLIC3R_DEBUG @@ -1148,24 +1147,19 @@ static void modulate_extrusion_by_overlapping_layers( ExtrusionPath *extrusion_path_template = dynamic_cast(extrusions_in_out.front()); assert(extrusion_path_template != nullptr); ExtrusionRole extrusion_role = extrusion_path_template->role(); - float extrusion_width = extrusion_path_template->width; + float extrusion_width = extrusion_path_template->width(); struct ExtrusionPathFragment { - ExtrusionPathFragment() : mm3_per_mm(-1), width(-1), height(-1) {}; - ExtrusionPathFragment(double mm3_per_mm, float width, float height) : mm3_per_mm(mm3_per_mm), width(width), height(height) {}; - + ExtrusionFlow flow; Polylines polylines; - double mm3_per_mm; - float width; - float height; }; // Split the extrusions by the overlapping layers, reduce their extrusion rate. // The last path_fragment is from this_layer. std::vector path_fragments( n_overlapping_layers + 1, - ExtrusionPathFragment(extrusion_path_template->mm3_per_mm, extrusion_path_template->width, extrusion_path_template->height)); + ExtrusionPathFragment{ extrusion_path_template->attributes() }); // Don't use it, it will be released. extrusion_path_template = nullptr; @@ -1242,8 +1236,8 @@ static void modulate_extrusion_by_overlapping_layers( path_fragments.back().polylines = diff_pl(path_fragments.back().polylines, polygons_trimming); // Adjust the extrusion parameters for a reduced layer height and a non-bridging flow (nozzle_dmr = -1, does not matter). assert(this_layer.print_z > overlapping_layer.print_z); - frag.height = float(this_layer.print_z - overlapping_layer.print_z); - frag.mm3_per_mm = Flow(frag.width, frag.height, -1.f).mm3_per_mm(); + frag.flow.height = float(this_layer.print_z - overlapping_layer.print_z); + frag.flow.mm3_per_mm = Flow(frag.flow.width, frag.flow.height, -1.f).mm3_per_mm(); #ifdef SLIC3R_DEBUG svg.draw(frag.polylines, dbg_index_to_color(i_overlapping_layer), scale_(0.1)); #endif /* SLIC3R_DEBUG */ @@ -1326,15 +1320,14 @@ static void modulate_extrusion_by_overlapping_layers( ExtrusionPath *path = multipath.paths.empty() ? nullptr : &multipath.paths.back(); if (path != nullptr) { // Verify whether the path is compatible with the current fragment. - assert(this_layer.layer_type == SupporLayerType::BottomContact || path->height != frag.height || path->mm3_per_mm != frag.mm3_per_mm); - if (path->height != frag.height || path->mm3_per_mm != frag.mm3_per_mm) { + assert(this_layer.layer_type == SupporLayerType::BottomContact || path->height() != frag.flow.height || path->mm3_per_mm() != frag.flow.mm3_per_mm); + if (path->height() != frag.flow.height || path->mm3_per_mm() != frag.flow.mm3_per_mm) path = nullptr; - } // Merging with the previous path. This can only happen if the current layer was reduced by a base layer, which was split into a base and interface layer. } if (path == nullptr) { // Allocate a new path. - multipath.paths.push_back(ExtrusionPath(extrusion_role, frag.mm3_per_mm, frag.width, frag.height)); + multipath.paths.emplace_back(ExtrusionAttributes{ extrusion_role, frag.flow }); path = &multipath.paths.back(); } // The Clipper library may flip the order of the clipped polylines arbitrarily. @@ -1369,8 +1362,8 @@ static void modulate_extrusion_by_overlapping_layers( } // If there are any non-consumed fragments, add them separately. //FIXME this shall not happen, if the Clipper works as expected and all paths split to fragments could be re-connected. - for (auto it_fragment = path_fragments.begin(); it_fragment != path_fragments.end(); ++ it_fragment) - extrusion_entities_append_paths(extrusions_in_out, std::move(it_fragment->polylines), extrusion_role, it_fragment->mm3_per_mm, it_fragment->width, it_fragment->height); + for (ExtrusionPathFragment &fragment : path_fragments) + extrusion_entities_append_paths(extrusions_in_out, std::move(fragment.polylines), { extrusion_role, fragment.flow }); } // Support layer that is covered by some form of dense interface. diff --git a/src/libslic3r/Support/SupportMaterial.cpp b/src/libslic3r/Support/SupportMaterial.cpp index a21a48b9a3..d91d492d10 100644 --- a/src/libslic3r/Support/SupportMaterial.cpp +++ b/src/libslic3r/Support/SupportMaterial.cpp @@ -1023,7 +1023,7 @@ namespace SupportMaterialInternal { assert(expansion_scaled >= 0.f); for (const ExtrusionPath &ep : loop.paths) if (ep.role() == ExtrusionRole::OverhangPerimeter && ! ep.polyline.empty()) { - float exp = 0.5f * (float)scale_(ep.width) + expansion_scaled; + float exp = 0.5f * (float)scale_(ep.width()) + expansion_scaled; if (ep.is_closed()) { if (ep.size() >= 3) { // This is a complete loop. diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 679abb709d..2990c0fb56 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -1442,8 +1442,8 @@ void _3DScene::extrusionentity_to_verts(const ExtrusionPath& extrusion_path, flo polyline.remove_duplicate_points(); polyline.translate(copy); const Lines lines = polyline.lines(); - std::vector widths(lines.size(), extrusion_path.width); - std::vector heights(lines.size(), extrusion_path.height); + std::vector widths(lines.size(), extrusion_path.width()); + std::vector heights(lines.size(), extrusion_path.height()); thick_lines_to_verts(lines, widths, heights, false, print_z, geometry); } @@ -1459,8 +1459,8 @@ void _3DScene::extrusionentity_to_verts(const ExtrusionLoop& extrusion_loop, flo polyline.translate(copy); const Lines lines_this = polyline.lines(); append(lines, lines_this); - widths.insert(widths.end(), lines_this.size(), extrusion_path.width); - heights.insert(heights.end(), lines_this.size(), extrusion_path.height); + widths.insert(widths.end(), lines_this.size(), extrusion_path.width()); + heights.insert(heights.end(), lines_this.size(), extrusion_path.height()); } thick_lines_to_verts(lines, widths, heights, true, print_z, geometry); } @@ -1477,8 +1477,8 @@ void _3DScene::extrusionentity_to_verts(const ExtrusionMultiPath& extrusion_mult polyline.translate(copy); const Lines lines_this = polyline.lines(); append(lines, lines_this); - widths.insert(widths.end(), lines_this.size(), extrusion_path.width); - heights.insert(heights.end(), lines_this.size(), extrusion_path.height); + widths.insert(widths.end(), lines_this.size(), extrusion_path.width()); + heights.insert(heights.end(), lines_this.size(), extrusion_path.height()); } thick_lines_to_verts(lines, widths, heights, false, print_z, geometry); } diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index ad93afa879..8b00129a44 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -7,7 +7,7 @@ #include "libslic3r/Utils.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/GCode/GCodeProcessor.hpp" -#include "libslic3r/GCodeWriter.hpp" +#include "libslic3r/GCode/GCodeWriter.hpp" #include "slic3r/Utils/Http.hpp" #include "slic3r/Utils/PrintHost.hpp" diff --git a/t/geometry.t b/t/geometry.t deleted file mode 100644 index 83fb72eeab..0000000000 --- a/t/geometry.t +++ /dev/null @@ -1,95 +0,0 @@ -use Test::More; -use strict; -use warnings; - -plan tests => 10; - -BEGIN { - use FindBin; - use lib "$FindBin::Bin/../lib"; - use local::lib "$FindBin::Bin/../local-lib"; -} - -use Slic3r; -use Slic3r::Geometry qw(PI - chained_path_from epsilon scale); - -{ - # this test was failing on Windows (GH #1950) - my $polygon = Slic3r::Polygon->new( - [207802834,-57084522],[196528149,-37556190],[173626821,-25420928],[171285751,-21366123], - [118673592,-21366123],[116332562,-25420928],[93431208,-37556191],[82156517,-57084523], - [129714478,-84542120],[160244873,-84542120], - ); - my $point = Slic3r::Point->new(95706562, -57294774); - ok $polygon->contains_point($point), 'contains_point'; -} - -#========================================================== - -my $polygons = [ - Slic3r::Polygon->new( # contour, ccw - [45919000, 515273900], [14726100, 461246400], [14726100, 348753500], [33988700, 315389800], - [43749700, 343843000], [45422300, 352251500], [52362100, 362637800], [62748400, 369577600], - [75000000, 372014700], [87251500, 369577600], [97637800, 362637800], [104577600, 352251500], - [107014700, 340000000], [104577600, 327748400], [97637800, 317362100], [87251500, 310422300], - [82789200, 309534700], [69846100, 294726100], [254081000, 294726100], [285273900, 348753500], - [285273900, 461246400], [254081000, 515273900], - - ), - Slic3r::Polygon->new( # hole, cw - [75000000, 502014700], [87251500, 499577600], [97637800, 492637800], [104577600, 482251500], - [107014700, 470000000], [104577600, 457748400], [97637800, 447362100], [87251500, 440422300], - [75000000, 437985300], [62748400, 440422300], [52362100, 447362100], [45422300, 457748400], - [42985300, 470000000], [45422300, 482251500], [52362100, 492637800], [62748400, 499577600], - ), -]; - - -#========================================================== - -{ - my $polygon = Slic3r::Polygon->new([0, 0], [10, 0], [5, 5]); - my $result = $polygon->split_at_index(1); - is ref($result), 'Slic3r::Polyline', 'split_at_index returns polyline'; - is_deeply $result->pp, [ [10, 0], [5, 5], [0, 0], [10, 0] ], 'split_at_index'; -} - -#========================================================== - -#{ -# my $bb = Slic3r::Geometry::BoundingBox->new_from_points([ map Slic3r::Point->new(@$_), [0, 1], [10, 2], [20, 2] ]); -# $bb->scale(2); -# is_deeply [ $bb->min_point->pp, $bb->max_point->pp ], [ [0,2], [40,4] ], 'bounding box is scaled correctly'; -#} - -#========================================================== - -{ - # if chained_path() works correctly, these points should be joined with no diagonal paths - # (thus 26 units long) - my @points = map Slic3r::Point->new_scale(@$_), [26,26],[52,26],[0,26],[26,52],[26,0],[0,52],[52,52],[52,0]; - my @ordered = @points[@{chained_path_from(\@points, $points[0])}]; - ok !(grep { abs($ordered[$_]->distance_to($ordered[$_+1]) - scale 26) > epsilon } 0..$#ordered-1), 'chained_path'; -} - -#========================================================== - -{ - my $line = Slic3r::Line->new([0, 0], [20, 0]); - is +Slic3r::Point->new(10, 10)->distance_to_line($line), 10, 'distance_to'; - is +Slic3r::Point->new(50, 0)->distance_to_line($line), 30, 'distance_to'; - is +Slic3r::Point->new(0, 0)->distance_to_line($line), 0, 'distance_to'; - is +Slic3r::Point->new(20, 0)->distance_to_line($line), 0, 'distance_to'; - is +Slic3r::Point->new(10, 0)->distance_to_line($line), 0, 'distance_to'; -} - -{ - my $triangle = Slic3r::Polygon->new( - [16000170,26257364], [714223,461012], [31286371,461008], - ); - my $simplified = $triangle->simplify(250000)->[0]; - is scalar(@$simplified), 3, 'triangle is never simplified to less than 3 points'; -} - -__END__ diff --git a/tests/fff_print/test_cooling.cpp b/tests/fff_print/test_cooling.cpp index 778a7da401..1f560d53f0 100644 --- a/tests/fff_print/test_cooling.cpp +++ b/tests/fff_print/test_cooling.cpp @@ -14,7 +14,7 @@ using namespace Slic3r; std::unique_ptr make_cooling_buffer( - GCode &gcode, + GCodeGenerator &gcode, const DynamicPrintConfig &config = DynamicPrintConfig{}, const std::vector &extruder_ids = { 0 }) { @@ -65,7 +65,7 @@ SCENARIO("Cooling unit tests", "[Cooling]") { const double print_time = 100. / (3000. / 60.); //FIXME slowdown_below_layer_time is rounded down significantly from 1.8s to 1s. config.set_deserialize_strict({ { "slowdown_below_layer_time", { int(print_time * 0.999) } } }); - GCode gcodegen; + GCodeGenerator gcodegen; auto buffer = make_cooling_buffer(gcodegen, config); std::string gcode = buffer->process_layer("G1 F3000;_EXTRUDE_SET_SPEED\nG1 X100 E1", 0, true); bool speed_not_altered = gcode.find("F3000") != gcode.npos; @@ -83,7 +83,7 @@ SCENARIO("Cooling unit tests", "[Cooling]") { // Print time of gcode. const double print_time = 50. / (2500. / 60.) + 100. / (3000. / 60.) + 4. / (400. / 60.); config.set_deserialize_strict({ { "slowdown_below_layer_time", { int(print_time * 1.001) } } }); - GCode gcodegen; + GCodeGenerator gcodegen; auto buffer = make_cooling_buffer(gcodegen, config); std::string gcode = buffer->process_layer(gcode_src, 0, true); THEN("speed is altered when elapsed time is lower than slowdown threshold") { @@ -106,7 +106,7 @@ SCENARIO("Cooling unit tests", "[Cooling]") { { "fan_below_layer_time" , int(print_time1 * 0.88) }, { "slowdown_below_layer_time" , int(print_time1 * 0.99) } }); - GCode gcodegen; + GCodeGenerator gcodegen; auto buffer = make_cooling_buffer(gcodegen, config); std::string gcode = buffer->process_layer(gcode1, 0, true); bool fan_not_activated = gcode.find("M106") == gcode.npos; @@ -119,7 +119,7 @@ SCENARIO("Cooling unit tests", "[Cooling]") { { "fan_below_layer_time", { int(print_time2 + 1.), int(print_time2 + 1.) } }, { "slowdown_below_layer_time", { int(print_time2 + 2.), int(print_time2 + 2.) } } }); - GCode gcodegen; + GCodeGenerator gcodegen; auto buffer = make_cooling_buffer(gcodegen, config, { 0, 1 }); std::string gcode = buffer->process_layer(gcode1 + "T1\nG1 X0 E1 F3000\n", 0, true); THEN("fan is activated for the 1st tool") { @@ -134,7 +134,7 @@ SCENARIO("Cooling unit tests", "[Cooling]") { WHEN("G-code block 2") { THEN("slowdown is computed on all objects printing at the same Z") { config.set_deserialize_strict({ { "slowdown_below_layer_time", int(print_time2 * 0.99) } }); - GCode gcodegen; + GCodeGenerator gcodegen; auto buffer = make_cooling_buffer(gcodegen, config); std::string gcode = buffer->process_layer(gcode2, 0, true); bool ok = gcode.find("F3000") != gcode.npos; @@ -145,7 +145,7 @@ SCENARIO("Cooling unit tests", "[Cooling]") { { "fan_below_layer_time", int(print_time2 * 0.65) }, { "slowdown_below_layer_time", int(print_time2 * 0.7) } }); - GCode gcodegen; + GCodeGenerator gcodegen; auto buffer = make_cooling_buffer(gcodegen, config); // use an elapsed time which is < the threshold but greater than it when summed twice std::string gcode = buffer->process_layer(gcode2, 0, true) + buffer->process_layer(gcode2, 1, true); @@ -158,7 +158,7 @@ SCENARIO("Cooling unit tests", "[Cooling]") { { "fan_below_layer_time", int(print_time2 + 1) }, { "slowdown_below_layer_time", int(print_time2 + 1) } }); - GCode gcodegen; + GCodeGenerator gcodegen; auto buffer = make_cooling_buffer(gcodegen, config); // use an elapsed time which is < the threshold but greater than it when summed twice std::string gcode = buffer->process_layer(gcode2, 0, true) + buffer->process_layer(gcode2, 1, true); diff --git a/tests/fff_print/test_extrusion_entity.cpp b/tests/fff_print/test_extrusion_entity.cpp index 5f7de1f14a..0698f5eb90 100644 --- a/tests/fff_print/test_extrusion_entity.cpp +++ b/tests/fff_print/test_extrusion_entity.cpp @@ -21,7 +21,7 @@ static inline Slic3r::Point random_point(float LO=-50, float HI=50) // build a sample extrusion entity collection with random start and end points. static Slic3r::ExtrusionPath random_path(size_t length = 20, float LO = -50, float HI = 50) { - ExtrusionPath t { ExtrusionRole::Perimeter, 1.0, 1.0, 1.0 }; + ExtrusionPath t{ ExtrusionAttributes{ ExtrusionRole::Perimeter, ExtrusionFlow{ 1.0, 1.0, 1.0 } } }; for (size_t j = 0; j < length; ++ j) t.polyline.append(random_point(LO, HI)); return t; @@ -37,9 +37,8 @@ static Slic3r::ExtrusionPaths random_paths(size_t count = 10, size_t length = 20 SCENARIO("ExtrusionPath", "[ExtrusionEntity]") { GIVEN("Simple path") { - Slic3r::ExtrusionPath path{ ExtrusionRole::ExternalPerimeter }; - path.polyline = { { 100, 100 }, { 200, 100 }, { 200, 200 } }; - path.mm3_per_mm = 1.; + Slic3r::ExtrusionPath path{ { { 100, 100 }, { 200, 100 }, { 200, 200 } }, + ExtrusionAttributes{ ExtrusionRole::ExternalPerimeter, ExtrusionFlow{ 1., -1.f, -1.f } } }; THEN("first point") { REQUIRE(path.first_point() == path.polyline.front()); } @@ -52,10 +51,7 @@ SCENARIO("ExtrusionPath", "[ExtrusionEntity]") { static ExtrusionPath new_extrusion_path(const Polyline &polyline, ExtrusionRole role, double mm3_per_mm) { - ExtrusionPath path(role); - path.polyline = polyline; - path.mm3_per_mm = 1.; - return path; + return { polyline, ExtrusionAttributes{ role, ExtrusionFlow{ mm3_per_mm, -1.f, -1.f } } }; } SCENARIO("ExtrusionLoop", "[ExtrusionEntity]") @@ -67,6 +63,7 @@ SCENARIO("ExtrusionLoop", "[ExtrusionEntity]") loop.paths.emplace_back(new_extrusion_path(square.split_at_first_point(), ExtrusionRole::ExternalPerimeter, 1.)); THEN("polygon area") { REQUIRE(loop.polygon().area() == Approx(square.area())); + REQUIRE(loop.area() == Approx(square.area())); } THEN("loop length") { REQUIRE(loop.length() == Approx(square.length())); @@ -110,6 +107,9 @@ SCENARIO("ExtrusionLoop", "[ExtrusionEntity]") loop.paths.emplace_back(new_extrusion_path(polyline1, ExtrusionRole::ExternalPerimeter, 1.)); loop.paths.emplace_back(new_extrusion_path(polyline2, ExtrusionRole::OverhangPerimeter, 1.)); + THEN("area") { + REQUIRE(loop.area() == Approx(loop.polygon().area())); + } double tot_len = polyline1.length() + polyline2.length(); THEN("length") { REQUIRE(loop.length() == Approx(tot_len)); @@ -212,6 +212,9 @@ SCENARIO("ExtrusionLoop", "[ExtrusionEntity]") loop.paths.emplace_back(new_extrusion_path(polyline3, ExtrusionRole::ExternalPerimeter, 1.)); loop.paths.emplace_back(new_extrusion_path(polyline4, ExtrusionRole::OverhangPerimeter, 1.)); double len = loop.length(); + THEN("area") { + REQUIRE(loop.area() == Approx(loop.polygon().area())); + } WHEN("splitting at vertex") { Point point(4821067, 9321068); if (! loop.split_at_vertex(point)) @@ -234,6 +237,9 @@ SCENARIO("ExtrusionLoop", "[ExtrusionEntity]") Polyline { { 15896783, 15868739 }, { 24842049, 12117558 }, { 33853238, 15801279 }, { 37591780, 24780128 }, { 37591780, 24844970 }, { 33853231, 33825297 }, { 24842049, 37509013 }, { 15896798, 33757841 }, { 12211841, 24812544 }, { 15896783, 15868739 } }, ExtrusionRole::ExternalPerimeter, 1.)); + THEN("area") { + REQUIRE(loop.area() == Approx(loop.polygon().area())); + } double len = loop.length(); THEN("split_at() preserves total length") { loop.split_at({ 15896783, 15868739 }, false, 0); @@ -378,23 +384,27 @@ TEST_CASE("ExtrusionEntityCollection: Chained path", "[ExtrusionEntity]") { REQUIRE(chained == test.chained); ExtrusionEntityCollection unchained_extrusions; extrusion_entities_append_paths(unchained_extrusions.entities, test.unchained, - ExtrusionRole::InternalInfill, 0., 0.4f, 0.3f); + ExtrusionAttributes{ ExtrusionRole::InternalInfill, ExtrusionFlow{ 0., 0.4f, 0.3f } }); THEN("Chaining works") { - ExtrusionEntityCollection chained_extrusions = unchained_extrusions.chained_path_from(test.initial_point); - REQUIRE(chained_extrusions.entities.size() == test.chained.size()); - for (size_t i = 0; i < chained_extrusions.entities.size(); ++ i) { + ExtrusionEntityReferences chained_extrusions = chain_extrusion_references(unchained_extrusions, &test.initial_point); + REQUIRE(chained_extrusions.size() == test.chained.size()); + for (size_t i = 0; i < chained_extrusions.size(); ++ i) { const Points &p1 = test.chained[i].points; - const Points &p2 = dynamic_cast(chained_extrusions.entities[i])->polyline.points; + Points p2 = chained_extrusions[i].cast()->polyline.points; + if (chained_extrusions[i].flipped()) + std::reverse(p2.begin(), p2.end()); REQUIRE(p1 == p2); } } THEN("Chaining produces no change with no_sort") { unchained_extrusions.no_sort = true; - ExtrusionEntityCollection chained_extrusions = unchained_extrusions.chained_path_from(test.initial_point); - REQUIRE(chained_extrusions.entities.size() == test.unchained.size()); - for (size_t i = 0; i < chained_extrusions.entities.size(); ++ i) { + ExtrusionEntityReferences chained_extrusions = chain_extrusion_references(unchained_extrusions, &test.initial_point); + REQUIRE(chained_extrusions.size() == test.unchained.size()); + for (size_t i = 0; i < chained_extrusions.size(); ++ i) { const Points &p1 = test.unchained[i].points; - const Points &p2 = dynamic_cast(chained_extrusions.entities[i])->polyline.points; + Points p2 = chained_extrusions[i].cast()->polyline.points; + if (chained_extrusions[i].flipped()) + std::reverse(p2.begin(), p2.end()); REQUIRE(p1 == p2); } } diff --git a/tests/fff_print/test_gcode.cpp b/tests/fff_print/test_gcode.cpp index 34b40d1ff4..3ec1758b4e 100644 --- a/tests/fff_print/test_gcode.cpp +++ b/tests/fff_print/test_gcode.cpp @@ -7,7 +7,7 @@ using namespace Slic3r; SCENARIO("Origin manipulation", "[GCode]") { - Slic3r::GCode gcodegen; + Slic3r::GCodeGenerator gcodegen; WHEN("set_origin to (10,0)") { gcodegen.set_origin(Vec2d(10,0)); REQUIRE(gcodegen.origin() == Vec2d(10, 0)); diff --git a/tests/fff_print/test_gcodewriter.cpp b/tests/fff_print/test_gcodewriter.cpp index 15ffaebb48..35c70ad0c8 100644 --- a/tests/fff_print/test_gcodewriter.cpp +++ b/tests/fff_print/test_gcodewriter.cpp @@ -2,7 +2,7 @@ #include -#include "libslic3r/GCodeWriter.hpp" +#include "libslic3r/GCode/GCodeWriter.hpp" using namespace Slic3r; diff --git a/tests/libslic3r/CMakeLists.txt b/tests/libslic3r/CMakeLists.txt index 971db528cd..181fda6567 100644 --- a/tests/libslic3r/CMakeLists.txt +++ b/tests/libslic3r/CMakeLists.txt @@ -6,6 +6,7 @@ add_executable(${_TEST_NAME}_tests test_aabbindirect.cpp test_kdtreeindirect.cpp test_arachne.cpp + test_arc_welder.cpp test_clipper_offset.cpp test_clipper_utils.cpp test_color.cpp diff --git a/tests/libslic3r/test_arc_welder.cpp b/tests/libslic3r/test_arc_welder.cpp new file mode 100644 index 0000000000..8a700a24af --- /dev/null +++ b/tests/libslic3r/test_arc_welder.cpp @@ -0,0 +1,91 @@ +#include +#include + +#include + +TEST_CASE("arc_center", "[ArcWelder]") { + using namespace Slic3r; + using namespace Slic3r::Geometry; + + WHEN("arc from { 2000.f, 1000.f } to { 1000.f, 2000.f }") { + Vec2f p1{ 2000.f, 1000.f }; + Vec2f p2{ 1000.f, 2000.f }; + float r{ 1000.f }; + THEN("90 degrees arc, CCW") { + Vec2f c = ArcWelder::arc_center(p1, p2, r, true); + REQUIRE(is_approx(c, Vec2f{ 1000.f, 1000.f })); + REQUIRE(ArcWelder::arc_angle(p1, p2, r) == Approx(0.5 * M_PI)); + REQUIRE(ArcWelder::arc_length(p1, p2, r) == Approx(r * 0.5 * M_PI).epsilon(0.001)); + } + THEN("90 degrees arc, CW") { + Vec2f c = ArcWelder::arc_center(p1, p2, r, false); + REQUIRE(is_approx(c, Vec2f{ 2000.f, 2000.f })); + } + THEN("270 degrees arc, CCW") { + Vec2f c = ArcWelder::arc_center(p1, p2, - r, true); + REQUIRE(is_approx(c, Vec2f{ 2000.f, 2000.f })); + REQUIRE(ArcWelder::arc_angle(p1, p2, - r) == Approx(1.5 * M_PI)); + REQUIRE(ArcWelder::arc_length(p1, p2, - r) == Approx(r * 1.5 * M_PI).epsilon(0.001)); + } + THEN("270 degrees arc, CW") { + Vec2f c = ArcWelder::arc_center(p1, p2, - r, false); + REQUIRE(is_approx(c, Vec2f{ 1000.f, 1000.f })); + } + } + WHEN("arc from { 1707.11f, 1707.11f } to { 1000.f, 2000.f }") { + Vec2f p1{ 1707.11f, 1707.11f }; + Vec2f p2{ 1000.f, 2000.f }; + float r{ 1000.f }; + Vec2f center1 = Vec2f{ 1000.f, 1000.f }; + // Center on the other side of the CCW arch. + Vec2f center2 = center1 + 2. * (0.5 * (p1 + p2) - center1); + THEN("45 degrees arc, CCW") { + Vec2f c = ArcWelder::arc_center(p1, p2, r, true); + REQUIRE(is_approx(c, center1, 1.f)); + REQUIRE(ArcWelder::arc_angle(p1, p2, r) == Approx(0.25 * M_PI)); + REQUIRE(ArcWelder::arc_length(p1, p2, r) == Approx(r * 0.25 * M_PI).epsilon(0.001)); + } + THEN("45 degrees arc, CW") { + Vec2f c = ArcWelder::arc_center(p1, p2, r, false); + REQUIRE(is_approx(c, center2, 1.f)); + } + THEN("315 degrees arc, CCW") { + Vec2f c = ArcWelder::arc_center(p1, p2, - r, true); + REQUIRE(is_approx(c, center2, 1.f)); + REQUIRE(ArcWelder::arc_angle(p1, p2, - r) == Approx((2. - 0.25) * M_PI)); + REQUIRE(ArcWelder::arc_length(p1, p2, - r) == Approx(r * (2. - 0.25) * M_PI).epsilon(0.001)); + } + THEN("315 degrees arc, CW") { + Vec2f c = ArcWelder::arc_center(p1, p2, - r, false); + REQUIRE(is_approx(c, center1, 1.f)); + } + } + WHEN("arc from { 1866.f, 1500.f } to { 1000.f, 2000.f }") { + Vec2f p1{ 1866.f, 1500.f }; + Vec2f p2{ 1000.f, 2000.f }; + float r{ 1000.f }; + Vec2f center1 = Vec2f{ 1000.f, 1000.f }; + // Center on the other side of the CCW arch. + Vec2f center2 = center1 + 2. * (0.5 * (p1 + p2) - center1); + THEN("60 degrees arc, CCW") { + Vec2f c = ArcWelder::arc_center(p1, p2, r, true); + REQUIRE(is_approx(c, center1, 1.f)); + REQUIRE(is_approx(ArcWelder::arc_angle(p1, p2, r), float(M_PI / 3.), 0.001f)); + REQUIRE(ArcWelder::arc_length(p1, p2, r) == Approx(r * M_PI / 3.).epsilon(0.001)); + } + THEN("60 degrees arc, CW") { + Vec2f c = ArcWelder::arc_center(p1, p2, r, false); + REQUIRE(is_approx(c, center2, 1.f)); + } + THEN("300 degrees arc, CCW") { + Vec2f c = ArcWelder::arc_center(p1, p2, - r, true); + REQUIRE(is_approx(c, center2, 1.f)); + REQUIRE(is_approx(ArcWelder::arc_angle(p1, p2, - r), float((2. - 1./3.) * M_PI), 0.001f)); + REQUIRE(ArcWelder::arc_length(p1, p2, - r) == Approx(r * (2. - 1. / 3.) * M_PI).epsilon(0.001)); + } + THEN("300 degrees arc, CW") { + Vec2f c = ArcWelder::arc_center(p1, p2, - r, false); + REQUIRE(is_approx(c, center1, 1.f)); + } + } +} diff --git a/tests/libslic3r/test_geometry.cpp b/tests/libslic3r/test_geometry.cpp index 16a27665e8..48fc8fd72e 100644 --- a/tests/libslic3r/test_geometry.cpp +++ b/tests/libslic3r/test_geometry.cpp @@ -84,16 +84,16 @@ TEST_CASE("Line::perpendicular_to", "[Geometry]") { TEST_CASE("Polygon::contains works properly", "[Geometry]"){ // this test was failing on Windows (GH #1950) Slic3r::Polygon polygon(Points({ - Point(207802834,-57084522), - Point(196528149,-37556190), - Point(173626821,-25420928), - Point(171285751,-21366123), - Point(118673592,-21366123), - Point(116332562,-25420928), - Point(93431208,-37556191), - Point(82156517,-57084523), - Point(129714478,-84542120), - Point(160244873,-84542120) + {207802834,-57084522}, + {196528149,-37556190}, + {173626821,-25420928}, + {171285751,-21366123}, + {118673592,-21366123}, + {116332562,-25420928}, + {93431208,-37556191}, + {82156517,-57084523}, + {129714478,-84542120}, + {160244873,-84542120} })); Point point(95706562, -57294774); REQUIRE(polygon.contains(point)); @@ -310,6 +310,7 @@ SCENARIO("Path chaining", "[Geometry]") { GIVEN("A path") { Points points = { Point(26,26),Point(52,26),Point(0,26),Point(26,52),Point(26,0),Point(0,52),Point(52,52),Point(52,0) }; THEN("Chained with no diagonals (thus 26 units long)") { + // if chain_points() works correctly, these points should be joined with no diagonal paths std::vector indices = chain_points(points); for (Points::size_type i = 0; i + 1 < indices.size(); ++ i) { double dist = (points.at(indices.at(i)).cast() - points.at(indices.at(i+1)).cast()).norm(); diff --git a/tests/libslic3r/test_polyline.cpp b/tests/libslic3r/test_polyline.cpp index 8271554041..89a84d40a3 100644 --- a/tests/libslic3r/test_polyline.cpp +++ b/tests/libslic3r/test_polyline.cpp @@ -5,6 +5,25 @@ using namespace Slic3r; +SCENARIO("Simplify polyne, template", "[Polyline]") +{ + Points polyline{ {0,0}, {1000,0}, {2000,0}, {2000,1000}, {2000,2000}, {1000,2000}, {0,2000}, {0,1000}, {0,0} }; + WHEN("simplified with Douglas-Peucker with back inserter") { + Points out; + douglas_peucker(polyline.begin(), polyline.end(), std::back_inserter(out), 10, [](const Point &p) { return p; }); + THEN("simplified correctly") { + REQUIRE(out == Points{ {0,0}, {2000,0}, {2000,2000}, {0,2000}, {0,0} }); + } + } + WHEN("simplified with Douglas-Peucker in place") { + Points out{ polyline }; + out.erase(douglas_peucker(out.begin(), out.end(), out.begin(), 10, [](const Point &p) { return p; }), out.end()); + THEN("simplified correctly") { + REQUIRE(out == Points{ {0,0}, {2000,0}, {2000,2000}, {0,2000}, {0,0} }); + } + } +} + SCENARIO("Simplify polyline", "[Polyline]") { GIVEN("polyline 1") { diff --git a/xs/src/perlglue.cpp b/xs/src/perlglue.cpp index 3f75617dd5..500cbee836 100644 --- a/xs/src/perlglue.cpp +++ b/xs/src/perlglue.cpp @@ -4,7 +4,7 @@ namespace Slic3r { REGISTER_CLASS(ExPolygon, "ExPolygon"); -REGISTER_CLASS(GCode, "GCode"); +REGISTER_CLASS(GCodeGenerator, "GCode"); REGISTER_CLASS(Line, "Line"); REGISTER_CLASS(Polygon, "Polygon"); REGISTER_CLASS(Polyline, "Polyline"); diff --git a/xs/xsp/Geometry.xsp b/xs/xsp/Geometry.xsp index f6365a20a5..d31438c0ac 100644 --- a/xs/xsp/Geometry.xsp +++ b/xs/xsp/Geometry.xsp @@ -20,15 +20,6 @@ convex_hull(points) OUTPUT: RETVAL -std::vector -chained_path_from(points, start_from) - Points points - Point* start_from - CODE: - RETVAL = chain_points(points, start_from); - OUTPUT: - RETVAL - double rad2deg(angle) double angle diff --git a/xs/xsp/Polygon.xsp b/xs/xsp/Polygon.xsp index 7b1fbb83c7..95c1d2da3d 100644 --- a/xs/xsp/Polygon.xsp +++ b/xs/xsp/Polygon.xsp @@ -19,7 +19,6 @@ Lines lines(); Clone split_at_vertex(Point* point) %code{% RETVAL = THIS->split_at_vertex(*point); %}; - Clone split_at_index(int index); Clone split_at_first_point(); double length(); double area(); From 4bb5d45ec19dd306e3d079e1b35b5c55e8cf9c89 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 13 Jul 2023 16:15:38 +0200 Subject: [PATCH 002/136] ArcWelder: Integrated "arc_fitting" and "arc_fitting_tolerance" parameters to the UI and the slicing pipeline. --- src/libslic3r/Preset.cpp | 3 ++- src/libslic3r/PrintObject.cpp | 4 +++- src/slic3r/GUI/Tab.cpp | 2 ++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index f42b3f7708..7aa4f4e232 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -454,7 +454,8 @@ static std::vector s_Preset_print_options { "ooze_prevention", "standby_temperature_delta", "interface_shells", "extrusion_width", "first_layer_extrusion_width", "perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width", "top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "infill_anchor", "infill_anchor_max", "bridge_flow_ratio", - "elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "gcode_resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y", + "elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "gcode_resolution", "arc_fitting", "arc_fitting_tolerance", + "wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_cone_angle", "wipe_tower_rotation_angle", "wipe_tower_brim_width", "wipe_tower_bridging", "single_extruder_multi_material_priming", "mmu_segmented_region_max_width", "wipe_tower_extruder", "wipe_tower_no_sparse_layers", "wipe_tower_extra_spacing", "compatible_printers", "compatible_printers_condition", "inherits", "perimeter_generator", "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle", diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index ff7906da07..ebf6e8d48c 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -644,7 +644,9 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "first_layer_extrusion_width" || opt_key == "perimeter_extrusion_width" || opt_key == "infill_overlap" - || opt_key == "external_perimeters_first") { + || opt_key == "external_perimeters_first" + || opt_key == "arc_fitting" + || opt_key == "arc_fitting_tolerance") { steps.emplace_back(posPerimeters); } else if ( opt_key == "gap_fill_enabled" diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 8b00129a44..3ad27edeef 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1648,6 +1648,8 @@ void TabPrint::build() optgroup->append_single_option_line("slicing_mode"); optgroup->append_single_option_line("resolution"); optgroup->append_single_option_line("gcode_resolution"); + optgroup->append_single_option_line("arc_fitting"); + optgroup->append_single_option_line("arc_fitting_tolerance"); optgroup->append_single_option_line("xy_size_compensation"); optgroup->append_single_option_line("elefant_foot_compensation", "elephant-foot-compensation_114487"); From 3df8da662edb98554fbf5567594bea58cb3d4d25 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Fri, 14 Jul 2023 11:20:55 +0200 Subject: [PATCH 003/136] WIP Arc discretization, arc interpolation and unit tests. --- src/libslic3r/Geometry/ArcWelder.cpp | 126 ++++++++++++++++----------- src/libslic3r/Geometry/ArcWelder.hpp | 4 + src/libslic3r/Geometry/Circle.hpp | 39 ++++++++- tests/libslic3r/test_arc_welder.cpp | 114 +++++++++++++++++++++++- tests/libslic3r/test_geometry.cpp | 35 ++++++++ 5 files changed, 261 insertions(+), 57 deletions(-) diff --git a/src/libslic3r/Geometry/ArcWelder.cpp b/src/libslic3r/Geometry/ArcWelder.cpp index f0b38a497e..51c9ffe1f5 100644 --- a/src/libslic3r/Geometry/ArcWelder.cpp +++ b/src/libslic3r/Geometry/ArcWelder.cpp @@ -26,6 +26,7 @@ //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #include "ArcWelder.hpp" +#include "Circle.hpp" #include "../MultiPoint.hpp" #include "../Polygon.hpp" @@ -36,11 +37,25 @@ namespace Slic3r { namespace Geometry { namespace ArcWelder { -// Area of a parallelogram of two vectors to be considered collinear. -static constexpr const double Parallel_area_threshold = 0.0001; -static constexpr const auto Parallel_area_threshold_scaled = int64_t(Parallel_area_threshold / sqr(SCALING_FACTOR)); -// FIXME do we want to use EPSILON here? -static constexpr const double epsilon = 0.000005; +Points arc_discretize(const Point &p1, const Point &p2, const double radius, const bool ccw, const double deviation) +{ + Vec2d center = arc_center(p1.cast(), p2.cast(), radius, ccw); + double angle = arc_angle(p1.cast(), p2.cast(), radius); + + double r = std::abs(radius); + double angle_step = 2. * acos((r - deviation) / r); + size_t num_steps = size_t(ceil(angle / angle_step)); + + Points out; + out.reserve(num_steps + 1); + out.emplace_back(p1); + if (! ccw) + angle_step *= -1.; + for (size_t i = 1; i < num_steps; ++ i) + out.emplace_back(p1.rotated(angle_step * i, center.cast())); + out.emplace_back(p2); + return out; +} struct Circle { @@ -50,37 +65,26 @@ struct Circle // Interpolate three points with a circle. // Returns false if the three points are collinear or if the radius is bigger than maximum allowed radius. -//FIXME unit test! static std::optional try_create_circle(const Point &p1, const Point &p2, const Point &p3, const double max_radius) { - // Use area of triangle to judge whether three points are considered collinear. - Vec2i64 v2 = (p2 - p1).cast(); - Vec2i64 v3 = (p3 - p2).cast(); - if (std::abs(cross2(v2, v3)) <= Parallel_area_threshold_scaled) - return {}; - - int64_t det = cross2(p2.cast(), p3.cast()) - cross2(p1.cast(), v3); - if (std::abs(det) < int64_t(SCALED_EPSILON)) - return {}; - - Point center = ((1. / 2.0 * double(det)) * - (double(p1.cast().squaredNorm()) * perp(v3).cast() + - double(p2.cast().squaredNorm()) * perp(p1 - p3).cast() + - double(p3.cast().squaredNorm()) * perp(v2).cast())).cast(); - double r = sqrt(double((center - p1).squaredNorm())); - return r > max_radius ? std::make_optional() : std::make_optional({ center, r }); + if (auto center = Slic3r::Geometry::try_circle_center(p1.cast(), p2.cast(), p3.cast(), SCALED_EPSILON); center) { + Point c = center->cast(); + if (double r = sqrt(double((c - p1).cast().squaredNorm())); r <= max_radius) + return std::make_optional({ c, float(r) }); + } + return {}; } // Returns a closest point on the segment. // Returns false if the closest point is not inside the segment, but at its boundary. static bool foot_pt_on_segment(const Point &p1, const Point &p2, const Point &c, Point &out) { - Vec2i64 v21 = (p2 - p1).cast(); - int64_t denom = v21.squaredNorm(); - if (denom > epsilon) { - if (double t = double((c - p1).cast().dot(v21)) / double(denom); - t >= epsilon && t < 1. - epsilon) { - out = p1 + (t * v21.cast()).cast(); + Vec2i64 v21 = (p2 - p1).cast(); + int64_t l2 = v21.squaredNorm(); + if (l2 > int64_t(SCALED_EPSILON)) { + if (int64_t t = (c - p1).cast().dot(v21); + t >= int64_t(SCALED_EPSILON) && t < l2 - int64_t(SCALED_EPSILON)) { + out = p1 + ((double(t) / double(l2)) * v21.cast()).cast(); return true; } } @@ -91,16 +95,19 @@ static bool foot_pt_on_segment(const Point &p1, const Point &p2, const Point &c, static inline bool circle_approximation_sufficient(const Circle &circle, const Points::const_iterator begin, const Points::const_iterator end, const double tolerance) { // The circle was calculated from the 1st and last point of the point sequence, thus the fitting of those points does not need to be evaluated. - assert(std::abs((*begin - circle.center).cast().norm() - circle.radius) < epsilon); - assert(std::abs((*std::prev(end) - circle.center).cast().norm() - circle.radius) < epsilon); + assert(std::abs((*begin - circle.center).cast().norm() - circle.radius) < SCALED_EPSILON); + assert(std::abs((*std::prev(end) - circle.center).cast().norm() - circle.radius) < SCALED_EPSILON); assert(end - begin >= 3); - for (auto it = begin; std::next(it) != end; ++ it) { - if (it != begin) { - if (double distance_from_center = (*it - circle.center).cast().norm(); - std::abs(distance_from_center - circle.radius) > tolerance) - return false; - } + // Test the 1st point. + if (double distance_from_center = (*begin - circle.center).cast().norm(); + std::abs(distance_from_center - circle.radius) > tolerance) + return false; + + for (auto it = std::next(begin); std::next(it) != end; ++ it) { + if (double distance_from_center = (*it - circle.center).cast().norm(); + std::abs(distance_from_center - circle.radius) > tolerance) + return false; Point closest_point; if (foot_pt_on_segment(*it, *std::next(it), circle.center, closest_point)) { if (double distance_from_center = (closest_point - circle.center).cast().norm(); @@ -114,8 +121,8 @@ static inline bool circle_approximation_sufficient(const Circle &circle, const P static inline bool get_deviation_sum_squared(const Circle &circle, const Points::const_iterator begin, const Points::const_iterator end, const double tolerance, double &total_deviation) { // The circle was calculated from the 1st and last point of the point sequence, thus the fitting of those points does not need to be evaluated. - assert(std::abs((*begin - circle.center).cast().norm() - circle.radius) < epsilon); - assert(std::abs((*std::prev(end) - circle.center).cast().norm() - circle.radius) < epsilon); + assert(std::abs((*begin - circle.center).cast().norm() - circle.radius) < SCALED_EPSILON); + assert(std::abs((*std::prev(end) - circle.center).cast().norm() - circle.radius) < SCALED_EPSILON); assert(end - begin >= 3); total_deviation = 0; @@ -146,18 +153,19 @@ static std::optional try_create_circle(const Points::const_iterator begi size_t size = end - begin; if (size == 3) { out = try_create_circle(*begin, *std::next(begin), *std::prev(end), max_radius); - if (! circle_approximation_sufficient(*out, begin, end, tolerance)) + if (out && ! circle_approximation_sufficient(*out, begin, end, tolerance)) out.reset(); } else { +#if 0 size_t ipivot = size / 2; // Take a center difference of points at the center of the path. //FIXME does it really help? For short arches, the linear interpolation may be Point pivot = (size % 2 == 0) ? (*(begin + ipivot) + *(begin + ipivot - 1)) / 2 : (*(begin + ipivot - 1) + *(begin + ipivot + 1)) / 2; if (std::optional circle = try_create_circle(*begin, pivot, *std::prev(end), max_radius); - circle_approximation_sufficient(*circle, begin, end, tolerance)) + circle && circle_approximation_sufficient(*circle, begin, end, tolerance)) return circle; - +#endif // Find the circle with the least deviation, if one exists. double least_deviation; double current_deviation; @@ -335,19 +343,24 @@ Path fit_path(const Points &src, double tolerance, double fit_circle_percent_tol out.erase(douglas_peucker_in_place(out.begin(), out.end(), tolerance), out.end()); } else { // Perform simplification & fitting. + // Index of the start of a last polyline, which has not yet been decimated. int begin_pl_idx = 0; - for (auto begin = src.begin(); begin < src.end();) { - // Minimum 3 points required for circle fitting. - auto end = begin + 3; + out.push_back({ src.front(), 0.f }); + for (auto it = std::next(src.begin()); it != src.end();) { + // Minimum 2 additional points required for circle fitting. + auto begin = std::prev(it); + auto end = std::next(it); + assert(end <= src.end()); std::optional arc; - while (end <= src.end()) { + while (end != src.end()) { + auto next_end = std::next(end); if (std::optional this_arc = ArcWelder::Arc::try_create_arc( - begin, end, + begin, next_end, ArcWelder::default_scaled_max_radius, tolerance, fit_circle_percent_tolerance); this_arc) { arc = this_arc; - ++ end; + end = next_end; } else break; } @@ -355,13 +368,22 @@ Path fit_path(const Points &src, double tolerance, double fit_circle_percent_tol // If there is a trailing polyline, decimate it first before saving a new arc. if (out.size() - begin_pl_idx > 2) out.erase(douglas_peucker_in_place(out.begin() + begin_pl_idx, out.end(), tolerance), out.end()); - // Save the end of the last circle segment, which may become the first point of a possible future polyline. + // Save the index of an end of the new circle segment, which may become the first point of a possible future polyline. begin_pl_idx = int(out.size()); - -- end; - out.push_back({ arc->end_point, float(arc->direction == Orientation::CCW ? arc->radius : - arc->radius) }); - } else - out.push_back({ arc->end_point, 0.f }); + // This will be the next point to try to add. + it = end; + // Add the new arc. + assert(*begin == arc->start_point); + assert(*std::prev(it) == arc->end_point); + out.push_back({ arc->end_point, float(arc->radius), arc->direction }); + } else { + // Arc is not valid, append a linear segment. + out.push_back({ *it ++ }); + } } + if (out.size() - begin_pl_idx > 2) + // Do the final polyline decimation. + out.erase(douglas_peucker_in_place(out.begin() + begin_pl_idx, out.end(), tolerance), out.end()); } return out; diff --git a/src/libslic3r/Geometry/ArcWelder.hpp b/src/libslic3r/Geometry/ArcWelder.hpp index 4974060c55..00a1cc4397 100644 --- a/src/libslic3r/Geometry/ArcWelder.hpp +++ b/src/libslic3r/Geometry/ArcWelder.hpp @@ -97,6 +97,10 @@ inline typename Derived::Scalar arc_length( return arc_angle(start_pos, end_pos, radius) * std::abs(radius); } +// Discretize arc given the radius, orientation and maximum deviation from the arc. +// Returned polygon starts with p1, ends with p2 and it is discretized to guarantee the maximum deviation. +Points arc_discretize(const Point &p1, const Point &p2, const double radius, const bool ccw, const double deviation); + // 1.2m diameter, maximum given by coord_t static constexpr const double default_scaled_max_radius = scaled(600.); // 0.05mm diff --git a/src/libslic3r/Geometry/Circle.hpp b/src/libslic3r/Geometry/Circle.hpp index ba048dcd63..7b85916f5a 100644 --- a/src/libslic3r/Geometry/Circle.hpp +++ b/src/libslic3r/Geometry/Circle.hpp @@ -9,10 +9,17 @@ namespace Slic3r { namespace Geometry { // https://en.wikipedia.org/wiki/Circumscribed_circle // Circumcenter coordinates, Cartesian coordinates -template -Vector circle_center(const Vector &a, const Vector &bsrc, const Vector &csrc, typename Vector::Scalar epsilon) +// In case the three points are collinear, returns their centroid. +template +Eigen::Matrix circle_center(const Derived &a, const Derived2 &bsrc, const Derived3 &csrc, typename typename Derived::Scalar epsilon) { - using Scalar = typename Vector::Scalar; + static_assert(Derived ::IsVectorAtCompileTime && int(Derived ::SizeAtCompileTime) == 2, "circle_center(): 1st point is not a 2D vector"); + static_assert(Derived2::IsVectorAtCompileTime && int(Derived2::SizeAtCompileTime) == 2, "circle_center(): 2nd point is not a 2D vector"); + static_assert(Derived3::IsVectorAtCompileTime && int(Derived3::SizeAtCompileTime) == 2, "circle_center(): 3rd point is not a 2D vector"); + static_assert(std::is_same::value && std::is_same::value, + "circle_center(): All three points must be of the same type."); + using Scalar = typename Derived::Scalar; + using Vector = Eigen::Matrix; Vector b = bsrc - a; Vector c = csrc - a; Scalar lb = b.squaredNorm(); @@ -30,6 +37,32 @@ Vector circle_center(const Vector &a, const Vector &bsrc, const Vector &csrc, ty } } +// https://en.wikipedia.org/wiki/Circumscribed_circle +// Circumcenter coordinates, Cartesian coordinates +// Returns no value if the three points are collinear. +template +std::optional> try_circle_center(const Derived &a, const Derived2 &bsrc, const Derived3 &csrc, typename typename Derived::Scalar epsilon) +{ + static_assert(Derived ::IsVectorAtCompileTime && int(Derived ::SizeAtCompileTime) == 2, "try_circle_center(): 1st point is not a 2D vector"); + static_assert(Derived2::IsVectorAtCompileTime && int(Derived2::SizeAtCompileTime) == 2, "try_circle_center(): 2nd point is not a 2D vector"); + static_assert(Derived3::IsVectorAtCompileTime && int(Derived3::SizeAtCompileTime) == 2, "try_circle_center(): 3rd point is not a 2D vector"); + static_assert(std::is_same::value && std::is_same::value, + "try_circle_center(): All three points must be of the same type."); + using Scalar = typename Derived::Scalar; + using Vector = Eigen::Matrix; + Vector b = bsrc - a; + Vector c = csrc - a; + Scalar lb = b.squaredNorm(); + Scalar lc = c.squaredNorm(); + if (Scalar d = b.x() * c.y() - b.y() * c.x(); std::abs(d) < epsilon) { + // The three points are collinear. + return {}; + } else { + Vector v = lc * b - lb * c; + return std::make_optional(a + Vector(- v.y(), v.x()) / (2 * d)); + } +} + // 2D circle defined by its center and squared radius template struct CircleSq { diff --git a/tests/libslic3r/test_arc_welder.cpp b/tests/libslic3r/test_arc_welder.cpp index 8a700a24af..6c4b58e00f 100644 --- a/tests/libslic3r/test_arc_welder.cpp +++ b/tests/libslic3r/test_arc_welder.cpp @@ -2,9 +2,11 @@ #include #include +#include -TEST_CASE("arc_center", "[ArcWelder]") { - using namespace Slic3r; +using namespace Slic3r; + +TEST_CASE("arc basics", "[ArcWelder]") { using namespace Slic3r::Geometry; WHEN("arc from { 2000.f, 1000.f } to { 1000.f, 2000.f }") { @@ -89,3 +91,111 @@ TEST_CASE("arc_center", "[ArcWelder]") { } } } + +TEST_CASE("arc discretization", "[ArcWelder]") { + using namespace Slic3r::Geometry; + WHEN("arc from { 2, 1 } to { 1, 2 }") { + const Point p1 = Point::new_scale(2., 1.); + const Point p2 = Point::new_scale(1., 2.); + const Point center = Point::new_scale(1., 1.); + const float radius = scaled(1.); + const float resolution = scaled(0.002); + auto test = [center, resolution, radius](const Point &p1, const Point &p2, const float r, const bool ccw) { + Vec2f c = ArcWelder::arc_center(p1.cast(), p2.cast(), r, ccw); + REQUIRE((p1.cast() - c).norm() == Approx(radius)); + REQUIRE((c - center.cast()).norm() == Approx(0.)); + Points pts = ArcWelder::arc_discretize(p1, p2, r, ccw, resolution); + REQUIRE(pts.size() >= 2); + REQUIRE(pts.front() == p1); + REQUIRE(pts.back() == p2); + for (const Point &p : pts) + REQUIRE(std::abs((p.cast() - c.cast()).norm() - double(radius)) < double(resolution + SCALED_EPSILON)); + }; + THEN("90 degrees arc, CCW") { + test(p1, p2, radius, true); + } + THEN("270 degrees arc, CCW") { + test(p2, p1, - radius, true); + } + THEN("90 degrees arc, CW") { + test(p2, p1, radius, false); + } + THEN("270 degrees arc, CW") { + test(p1, p2, - radius, false); + } + } +} + +TEST_CASE("arc fitting", "[ArcWelder]") { + using namespace Slic3r::Geometry; + + WHEN("arc from { 2, 1 } to { 1, 2 }") { + const Point p1 = Point::new_scale(2., 1.); + const Point p2 = Point::new_scale(1., 2.); + const Point center = Point::new_scale(1., 1.); + const float radius = scaled(1.); + const float resolution = scaled(0.002); + auto test = [center, resolution, radius](const Point &p1, const Point &p2, const float r, const bool ccw) { + Points pts = ArcWelder::arc_discretize(p1, p2, r, ccw, resolution); + ArcWelder::Path path = ArcWelder::fit_path(pts, resolution + SCALED_EPSILON, ArcWelder::default_scaled_resolution); + REQUIRE(path.size() == 2); + REQUIRE(path.front().point == p1); + REQUIRE(path.front().radius == 0.f); + REQUIRE(path.back().point == p2); + REQUIRE(path.back().radius == Approx(radius)); + REQUIRE(path.back().ccw() == ccw); + }; + THEN("90 degrees arc, CCW is fitted") { + test(p1, p2, radius, true); + } + THEN("270 degrees arc, CCW is fitted") { + test(p2, p1, - radius, true); + } + THEN("90 degrees arc, CW is fitted") { + test(p2, p1, radius, false); + } + THEN("270 degrees arc, CW is fitted") { + test(p1, p2, - radius, false); + } + } + + WHEN("arc from { 2, 1 } to { 1, 2 }, another arc from { 2, 1 } to { 0, 2 }, tangentially connected") { + const Point p1 = Point::new_scale(2., 1.); + const Point p2 = Point::new_scale(1., 2.); + const Point p3 = Point::new_scale(0., 3.); + const Point center1 = Point::new_scale(1., 1.); + const Point center2 = Point::new_scale(1., 3.); + const float radius = scaled(1.); + const float resolution = scaled(0.002); + auto test = [center1, center2, resolution, radius](const Point &p1, const Point &p2, const Point &p3, const float r, const bool ccw) { + Points pts = ArcWelder::arc_discretize(p1, p2, r, ccw, resolution); + { + Points pts2 = ArcWelder::arc_discretize(p2, p3, - r, ! ccw, resolution); + REQUIRE(pts.back() == pts2.front()); + pts.insert(pts.end(), std::next(pts2.begin()), pts2.end()); + } + ArcWelder::Path path = ArcWelder::fit_path(pts, resolution + SCALED_EPSILON, ArcWelder::default_scaled_resolution); + REQUIRE(path.size() == 3); + REQUIRE(path.front().point == p1); + REQUIRE(path.front().radius == 0.f); + REQUIRE(path[1].point == p2); + REQUIRE(path[1].radius == Approx(radius)); + REQUIRE(path[1].ccw() == ccw); + REQUIRE(path.back().point == p3); + REQUIRE(path.back().radius == Approx(radius)); + REQUIRE(path.back().ccw() == ! ccw); + }; + THEN("90 degrees arches, CCW are fitted") { + test(p1, p2, p3, radius, true); + } + THEN("270 degrees arc, CCW is fitted") { + test(p3, p2, p1, -radius, true); + } + THEN("90 degrees arc, CW is fitted") { + test(p3, p2, p1, radius, false); + } + THEN("270 degrees arc, CW is fitted") { + test(p1, p2, p3, -radius, false); + } + } +} diff --git a/tests/libslic3r/test_geometry.cpp b/tests/libslic3r/test_geometry.cpp index 48fc8fd72e..c828465292 100644 --- a/tests/libslic3r/test_geometry.cpp +++ b/tests/libslic3r/test_geometry.cpp @@ -196,6 +196,41 @@ TEST_CASE("Offseting a line generates a polygon correctly", "[Geometry]"){ REQUIRE(area.area() == Slic3r::Polygon(Points({Point(10,5),Point(20,5),Point(20,15),Point(10,15)})).area()); } +SCENARIO("Circle Fit, 3 points", "[Geometry]") { + WHEN("Three points make a circle") { + double s1 = scaled(1.); + THEN("circle_center(): A center point { 0, 0 } is returned") { + Vec2d center = Geometry::circle_center(Vec2d{ s1, 0. }, Vec2d{ 0, s1 }, Vec2d{ -s1, 0. }, SCALED_EPSILON); + REQUIRE(is_approx(center, Vec2d(0, 0))); + } + THEN("circle_center(): A center point { 0, 0 } is returned for points in reverse") { + Vec2d center = Geometry::circle_center(Vec2d{ -s1, 0. }, Vec2d{ 0, s1 }, Vec2d{ s1, 0. }, SCALED_EPSILON); + REQUIRE(is_approx(center, Vec2d(0, 0))); + } + THEN("try_circle_center(): A center point { 0, 0 } is returned") { + std::optional center = Geometry::try_circle_center(Vec2d{ s1, 0. }, Vec2d{ 0, s1 }, Vec2d{ -s1, 0. }, SCALED_EPSILON); + REQUIRE(center); + REQUIRE(is_approx(*center, Vec2d(0, 0))); + } + THEN("try_circle_center(): A center point { 0, 0 } is returned for points in reverse") { + std::optional center = Geometry::try_circle_center(Vec2d{ -s1, 0. }, Vec2d{ 0, s1 }, Vec2d{ s1, 0. }, SCALED_EPSILON); + REQUIRE(center); + REQUIRE(is_approx(*center, Vec2d(0, 0))); + } + } + WHEN("Three points are collinear") { + double s1 = scaled(1.); + THEN("circle_center(): A center point { 2, 0 } is returned") { + Vec2d center = Geometry::circle_center(Vec2d{ s1, 0. }, Vec2d{ 2. * s1, 0. }, Vec2d{ 3. * s1, 0. }, SCALED_EPSILON); + REQUIRE(is_approx(center, Vec2d(2. * s1, 0))); + } + THEN("try_circle_center(): Fails for collinear points") { + std::optional center = Geometry::try_circle_center(Vec2d{ s1, 0. }, Vec2d{ 2. * s1, 0. }, Vec2d{ 3. * s1, 0. }, SCALED_EPSILON); + REQUIRE(! center); + } + } +} + SCENARIO("Circle Fit, TaubinFit with Newton's method", "[Geometry]") { GIVEN("A vector of Vec2ds arranged in a half-circle with approximately the same distance R from some point") { Vec2d expected_center(-6, 0); From 7551b4ffd310f428d56c024d771255dfd0566dc9 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Fri, 14 Jul 2023 16:22:03 +0200 Subject: [PATCH 004/136] ArcWelder: Bugfixes & switchable G2/3 R vs. IJ --- src/libslic3r/GCode.cpp | 26 ++++++++++++++++++++------ src/libslic3r/GCode/GCodeWriter.cpp | 15 +++++++++++---- src/libslic3r/GCode/GCodeWriter.hpp | 4 ++-- src/libslic3r/GCode/Wipe.cpp | 23 ++++++++++++++++------- src/libslic3r/Geometry/ArcWelder.cpp | 8 ++++++++ src/libslic3r/Geometry/ArcWelder.hpp | 5 ++++- src/libslic3r/PrintConfig.cpp | 16 ++++++++++++++-- src/libslic3r/PrintConfig.hpp | 9 ++++++++- 8 files changed, 83 insertions(+), 23 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 691cdea79d..6ae084772a 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1263,7 +1263,7 @@ void GCodeGenerator::process_layers( size_t layer_to_print_idx = 0; const GCode::SmoothPathCache::InterpolationParameters interpolation_params { scaled(print.config().gcode_resolution.value), - print.config().arc_fitting && ! print.config().spiral_vase ? + print.config().arc_fitting != ArcFittingType::Disabled && ! print.config().spiral_vase ? Geometry::ArcWelder::default_arc_length_percent_tolerance : 0 }; @@ -1362,7 +1362,7 @@ void GCodeGenerator::process_layers( size_t layer_to_print_idx = 0; const GCode::SmoothPathCache::InterpolationParameters interpolation_params { scaled(print.config().gcode_resolution.value), - print.config().arc_fitting && ! print.config().spiral_vase ? + print.config().arc_fitting != ArcFittingType::Disabled && ! print.config().spiral_vase ? Geometry::ArcWelder::default_arc_length_percent_tolerance : 0 }; @@ -2828,6 +2828,7 @@ std::string GCodeGenerator::_extrude( Vec2d prev = this->point_to_gcode_quantized(path.front().point); auto it = path.begin(); auto end = path.end(); + const bool emit_radius = m_config.arc_fitting == ArcFittingType::EmitRadius; for (++ it; it != end; ++ it) { Vec2d p = this->point_to_gcode_quantized(it->point); if (it->radius == 0) { @@ -2837,12 +2838,25 @@ std::string GCodeGenerator::_extrude( gcode += m_writer.extrude_to_xy(p, e_per_mm * line_length, comment); } else { // Extrude an arc. - double radius = GCodeFormatter::quantize_xyzf(it->radius); - Vec2f center = Geometry::ArcWelder::arc_center(prev.cast(), p.cast(), float(radius), it->ccw()); - float angle = Geometry::ArcWelder::arc_angle(prev.cast(), p.cast(), float(radius)); + assert(m_config.arc_fitting == ArcFittingType::EmitCenter || + m_config.arc_fitting == ArcFittingType::EmitRadius); + double radius = unscaled(it->radius); + if (emit_radius) + // Only quantize radius if emitting it directly into G-code. Otherwise use the exact radius for calculating the IJ values. + radius = GCodeFormatter::quantize_xyzf(radius); + Vec2d ij; + if (! emit_radius) { + // Calculate quantized IJ circle center offset. + Vec2d center_raw = Geometry::ArcWelder::arc_center(prev.cast(), p.cast(), double(radius), it->ccw()) - prev; + ij = Vec2d{ GCodeFormatter::quantize_xyzf(center_raw.x()), GCodeFormatter::quantize_xyzf(center_raw.y()) }; + } + double angle = Geometry::ArcWelder::arc_angle(prev.cast(), p.cast(), double(radius)); const double line_length = angle * std::abs(radius); path_length += line_length; - gcode += m_writer.extrude_to_xy_G2G3R(p, radius, it->ccw(), e_per_mm * line_length, comment); + const double dE = e_per_mm * line_length; + gcode += emit_radius ? + m_writer.extrude_to_xy_G2G3R(p, radius, it->ccw(), dE, comment) : + m_writer.extrude_to_xy_G2G3IJ(p, ij, it->ccw(), dE, comment); } prev = p; } diff --git a/src/libslic3r/GCode/GCodeWriter.cpp b/src/libslic3r/GCode/GCodeWriter.cpp index 4dd155d5f3..4375e2e1de 100644 --- a/src/libslic3r/GCode/GCodeWriter.cpp +++ b/src/libslic3r/GCode/GCodeWriter.cpp @@ -384,9 +384,13 @@ std::string GCodeWriter::extrude_to_xy(const Vec2d &point, double dE, const std: return w.string(); } -std::string GCodeWriter::extrude_to_xy_G2G3IJ(const Vec2d &point, const Vec2d &ij, double dE, const bool ccw, const std::string_view comment) +std::string GCodeWriter::extrude_to_xy_G2G3IJ(const Vec2d &point, const Vec2d &ij, const bool ccw, double dE, const std::string_view comment) { - assert(dE > 0); + assert(dE != 0); + assert(std::abs(point.x()) < 1200.); + assert(std::abs(point.y()) < 1200.); + assert(std::abs(ij.x()) < 1200.); + assert(std::abs(ij.y()) < 1200.); m_pos.head<2>() = point.head<2>(); GCodeG2G3Formatter w(ccw); @@ -397,9 +401,12 @@ std::string GCodeWriter::extrude_to_xy_G2G3IJ(const Vec2d &point, const Vec2d &i return w.string(); } -std::string GCodeWriter::extrude_to_xy_G2G3R(const Vec2d &point, const double radius, double dE, const bool ccw, const std::string_view comment) +std::string GCodeWriter::extrude_to_xy_G2G3R(const Vec2d &point, const double radius, const bool ccw, double dE, const std::string_view comment) { - assert(dE > 0); + assert(dE != 0); + assert(std::abs(point.x()) < 1200.); + assert(std::abs(point.y()) < 1200.); + assert(std::abs(radius) < 1800.); m_pos.head<2>() = point.head<2>(); GCodeG2G3Formatter w(ccw); diff --git a/src/libslic3r/GCode/GCodeWriter.hpp b/src/libslic3r/GCode/GCodeWriter.hpp index 904dac93dd..45f905efd4 100644 --- a/src/libslic3r/GCode/GCodeWriter.hpp +++ b/src/libslic3r/GCode/GCodeWriter.hpp @@ -66,8 +66,8 @@ public: std::string travel_to_z(double z, const std::string_view comment = {}); bool will_move_z(double z) const; std::string extrude_to_xy(const Vec2d &point, double dE, const std::string_view comment = {}); - std::string extrude_to_xy_G2G3IJ(const Vec2d &point, const Vec2d &ij, double dE, const bool ccw, const std::string_view comment); - std::string extrude_to_xy_G2G3R(const Vec2d &point, const double radius, double dE, const bool ccw, const std::string_view comment); + std::string extrude_to_xy_G2G3IJ(const Vec2d &point, const Vec2d &ij, const bool ccw, double dE, const std::string_view comment); + std::string extrude_to_xy_G2G3R(const Vec2d &point, const double radius, const bool ccw, double dE, const std::string_view comment); // std::string extrude_to_xyz(const Vec3d &point, double dE, const std::string_view comment = {}); std::string retract(bool before_wipe = false); std::string retract_for_toolchange(bool before_wipe = false); diff --git a/src/libslic3r/GCode/Wipe.cpp b/src/libslic3r/GCode/Wipe.cpp index c77984d4da..90ac401cc2 100644 --- a/src/libslic3r/GCode/Wipe.cpp +++ b/src/libslic3r/GCode/Wipe.cpp @@ -76,6 +76,7 @@ std::string Wipe::wipe(GCodeGenerator &gcodegen, bool toolchange) { std::string gcode; const Extruder &extruder = *gcodegen.writer().extruder(); + static constexpr const std::string_view wipe_retract_comment = "wipe and retract"sv; // Remaining quantized retraction length. if (double retract_length = extruder.retract_to_go(toolchange ? extruder.retract_length_toolchange() : extruder.retract_length()); @@ -103,14 +104,16 @@ std::string Wipe::wipe(GCodeGenerator &gcodegen, bool toolchange) dE = retract_length; done = true; } - gcode += gcodegen.writer().extrude_to_xy(p, -dE, "wipe and retract"sv); + gcode += gcodegen.writer().extrude_to_xy(p, -dE, wipe_retract_comment); retract_length -= dE; return done; }; - auto wipe_arc = [&gcode, &gcodegen, &retract_length, xy_to_e](const Vec2d &prev, Vec2d &p, float radius_in, const bool ccw) { - double radius = GCodeFormatter::quantize_xyzf(radius_in); - Vec2f center = Geometry::ArcWelder::arc_center(prev.cast(), p.cast(), float(radius), ccw); - float angle = Geometry::ArcWelder::arc_angle(prev.cast(), p.cast(), float(radius)); + const bool emit_radius = gcodegen.config().arc_fitting == ArcFittingType::EmitRadius; + auto wipe_arc = [&gcode, &gcodegen, &retract_length, xy_to_e, emit_radius](const Vec2d &prev, Vec2d &p, double radius_in, const bool ccw) { + // Only quantize radius if emitting it directly into G-code. Otherwise use the exact radius for calculating the IJ values. + double radius = emit_radius ? GCodeFormatter::quantize_xyzf(radius_in) : radius_in; + Vec2d center = Geometry::ArcWelder::arc_center(prev.cast(), p.cast(), double(radius), ccw); + float angle = Geometry::ArcWelder::arc_angle(prev.cast(), p.cast(), double(radius)); double segment_length = angle * std::abs(radius); double dE = GCodeFormatter::quantize_e(xy_to_e * segment_length); bool done = false; @@ -121,7 +124,13 @@ std::string Wipe::wipe(GCodeGenerator &gcodegen, bool toolchange) dE = retract_length; done = true; } - gcode += gcodegen.writer().extrude_to_xy_G2G3R(p, radius, ccw, -dE, "wipe and retract"sv); + if (emit_radius) { + gcode += gcodegen.writer().extrude_to_xy_G2G3R(p, radius, ccw, -dE, wipe_retract_comment); + } else { + // Calculate quantized IJ circle center offset. + Vec2d ij{ GCodeFormatter::quantize_xyzf(center.x() - prev.x()), GCodeFormatter::quantize_xyzf(center.y() - prev.y()) }; + gcode += gcodegen.writer().extrude_to_xy_G2G3IJ(p, ij, ccw, -dE, wipe_retract_comment); + } retract_length -= dE; return done; }; @@ -144,7 +153,7 @@ std::string Wipe::wipe(GCodeGenerator &gcodegen, bool toolchange) start_wipe(); if (it->linear() ? wipe_linear(prev, p) : - wipe_arc(prev, p, it->radius, it->ccw())) + wipe_arc(prev, p, unscaled(it->radius), it->ccw())) break; prev = p; } diff --git a/src/libslic3r/Geometry/ArcWelder.cpp b/src/libslic3r/Geometry/ArcWelder.cpp index 51c9ffe1f5..41c3b5f249 100644 --- a/src/libslic3r/Geometry/ArcWelder.cpp +++ b/src/libslic3r/Geometry/ArcWelder.cpp @@ -386,6 +386,14 @@ Path fit_path(const Points &src, double tolerance, double fit_circle_percent_tol out.erase(douglas_peucker_in_place(out.begin() + begin_pl_idx, out.end(), tolerance), out.end()); } +#if 0 + // Verify that all the source points are at tolerance distance from the interpolated path. + for (const Point &p : src) { + PathSegmentProjection proj = point_to_path_projection(out, p); + assert(proj.distance2 < sqr(tolerance + SCALED_EPSILON)); + } +#endif + return out; } diff --git a/src/libslic3r/Geometry/ArcWelder.hpp b/src/libslic3r/Geometry/ArcWelder.hpp index 00a1cc4397..86e4eb31a9 100644 --- a/src/libslic3r/Geometry/ArcWelder.hpp +++ b/src/libslic3r/Geometry/ArcWelder.hpp @@ -54,7 +54,10 @@ inline Eigen::Matrix arc_cente auto v = end_pos - start_pos; Float q2 = v.squaredNorm(); assert(q2 > 0); - Float t = sqrt(sqr(radius) / q2 - Float(.25f)); + Float t2 = sqr(radius) / q2 - Float(.25f); + // If the start_pos and end_pos are nearly antipodal, t2 may become slightly negative. + // In that case return a centroid of start_point & end_point. + Float t = t2 > 0 ? sqrt(t2) : Float(0); auto mid = Float(0.5) * (start_pos + end_pos); Vector vp{ -v.y() * t, v.x() * t }; return (radius > Float(0)) == is_ccw ? (mid + vp).eval() : (mid - vp).eval(); diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 416ceb2802..c1d8d68d83 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -34,6 +34,13 @@ static t_config_enum_names enum_names_from_keys_map(const t_config_enum_values & template<> const t_config_enum_values& ConfigOptionEnum::get_enum_values() { return s_keys_map_##NAME; } \ template<> const t_config_enum_names& ConfigOptionEnum::get_enum_names() { return s_keys_names_##NAME; } +static const t_config_enum_values s_keys_map_ArcFittingType { + { "disabled", int(ArcFittingType::Disabled) }, + { "emit_center", int(ArcFittingType::EmitCenter) }, + { "emit_radius", int(ArcFittingType::EmitRadius) } +}; +CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(ArcFittingType) + static t_config_enum_values s_keys_map_PrinterTechnology { { "FFF", ptFFF }, { "SLA", ptSLA } @@ -397,12 +404,17 @@ void PrintConfigDef::init_fff_params() { ConfigOptionDef* def; - def = this->add("arc_fitting", coBool); + def = this->add("arc_fitting", coEnum); def->label = L("Arc fitting"); def->tooltip = L("Enable this to get a G-code file which has G2 and G3 moves. " "And the fitting tolerance is same with resolution"); + def->set_enum({ + { "disabled", "Disabled" }, + { "emit_center", "Enabled: G2/3 I J" }, + { "emit_radius", "Enabled: G2/3 R" } + }); def->mode = comAdvanced; - def->set_default_value(new ConfigOptionBool(false)); + def->set_default_value(new ConfigOptionEnum(ArcFittingType::Disabled)); def = this->add("arc_fitting_tolerance", coFloatOrPercent); def->label = L("Arc fitting tolerance"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 60e4c81196..229aec7f75 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -31,6 +31,12 @@ namespace Slic3r { +enum class ArcFittingType { + Disabled, + EmitCenter, + EmitRadius, +}; + enum GCodeFlavor : unsigned char { gcfRepRapSprinter, gcfRepRapFirmware, gcfRepetier, gcfTeacup, gcfMakerWare, gcfMarlinLegacy, gcfMarlinFirmware, gcfKlipper, gcfSailfish, gcfMach3, gcfMachinekit, gcfSmoothie, gcfNoExtrusion, @@ -142,6 +148,7 @@ enum class GCodeThumbnailsFormat { template<> const t_config_enum_names& ConfigOptionEnum::get_enum_names(); \ template<> const t_config_enum_values& ConfigOptionEnum::get_enum_values(); +CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(ArcFittingType) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(PrinterTechnology) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(GCodeFlavor) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(MachineLimitsUsage) @@ -662,7 +669,7 @@ PRINT_CONFIG_CLASS_DEFINE( PRINT_CONFIG_CLASS_DEFINE( GCodeConfig, - ((ConfigOptionBool, arc_fitting)) + ((ConfigOptionEnum, arc_fitting)) ((ConfigOptionFloatOrPercent, arc_fitting_tolerance)) ((ConfigOptionBool, autoemit_temperature_commands)) ((ConfigOptionString, before_layer_gcode)) From 9fe36fc30098d5c17dd6e73434ed6590cc3f488f Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Sat, 15 Jul 2023 12:31:55 +0200 Subject: [PATCH 005/136] WIP Arc interpolation bugfixes --- src/libslic3r/GCode/SmoothPath.cpp | 38 ++---- src/libslic3r/Geometry/ArcWelder.cpp | 183 +++++++++++++++------------ src/libslic3r/Geometry/ArcWelder.hpp | 130 +++++++++++++------ tests/libslic3r/test_arc_welder.cpp | 63 +++++++++ 4 files changed, 275 insertions(+), 139 deletions(-) diff --git a/src/libslic3r/GCode/SmoothPath.cpp b/src/libslic3r/GCode/SmoothPath.cpp index 4eedd59011..7f4ee21785 100644 --- a/src/libslic3r/GCode/SmoothPath.cpp +++ b/src/libslic3r/GCode/SmoothPath.cpp @@ -9,36 +9,19 @@ namespace Slic3r::GCode { double length(const SmoothPath &path) { double l = 0; - for (const SmoothPathElement &el : path) { - auto it = el.path.begin(); - auto end = el.path.end(); - Point prev_point = it->point; - for (++ it; it != end; ++ it) { - Point point = it->point; - l += it->linear() ? - (point - prev_point).cast().norm() : - Geometry::ArcWelder::arc_length(prev_point.cast(), point.cast(), it->radius); - prev_point = point; - } - } + for (const SmoothPathElement &el : path) + l += Geometry::ArcWelder::path_length(el.path); return l; } // Returns true if the smooth path is longer than a threshold. bool longer_than(const SmoothPath &path, double length) { - for (const SmoothPathElement& el : path) { - auto it = el.path.begin(); - auto end = el.path.end(); - Point prev_point = it->point; - for (++it; it != end; ++it) { - Point point = it->point; - length -= it->linear() ? - (point - prev_point).cast().norm() : - Geometry::ArcWelder::arc_length(prev_point.cast(), point.cast(), it->radius); + for (const SmoothPathElement &el : path) { + for (auto it = std::next(el.path.begin()); it != el.path.end(); ++ it) { + length -= Geometry::ArcWelder::segment_length(*std::prev(it), *it); if (length < 0) return true; - prev_point = point; } } return length < 0; @@ -127,10 +110,14 @@ double clip_end(SmoothPath &path, double distance) if (p.empty()) { path.pop_back(); } else { + // Trailing path was trimmed and it is valid. + assert(path.back().path.size() > 1); assert(distance == 0); + // Distance to go is zero. return 0; } } + // Return distance to go after the whole smooth path was trimmed to zero. return distance; } @@ -221,12 +208,15 @@ SmoothPath SmoothPathCache::resolve_or_fit_split_with_seam( SmoothPath out = this->resolve_or_fit(loop.paths, reverse, resolution); assert(! out.empty()); if (! out.empty()) { + // Find a closest point on a vector of smooth paths. Geometry::ArcWelder::PathSegmentProjection proj; int proj_path = -1; for (const SmoothPathElement &el : out) if (Geometry::ArcWelder::PathSegmentProjection this_proj = Geometry::ArcWelder::point_to_path_projection(el.path, seam_point, proj.distance2); - this_proj.distance2 < proj.distance2) { + this_proj.valid()) { // Found a better (closer) projection. + assert(this_proj.distance2 < proj.distance2); + assert(this_proj.segment_id >= 0 && this_proj.segment_id < el.path.size()); proj = this_proj; proj_path = &el - out.data(); } @@ -237,7 +227,7 @@ SmoothPath SmoothPathCache::resolve_or_fit_split_with_seam( path, proj, seam_point_merge_distance_threshold); if (split.second.empty()) { std::rotate(out.begin(), out.begin() + proj_path + 1, out.end()); - out.back().path = std::move(split.first); + assert(out.back().path == split.first); } else { ExtrusionAttributes attr = out[proj_path].path_attributes; std::rotate(out.begin(), out.begin() + proj_path, out.end()); diff --git a/src/libslic3r/Geometry/ArcWelder.cpp b/src/libslic3r/Geometry/ArcWelder.cpp index 41c3b5f249..177c549b6d 100644 --- a/src/libslic3r/Geometry/ArcWelder.cpp +++ b/src/libslic3r/Geometry/ArcWelder.cpp @@ -184,41 +184,11 @@ static std::optional try_create_circle(const Points::const_iterator begi // ported from ArcWelderLib/ArcWelder/segmented/shape.h class "arc" class Arc { public: - - Arc() {} -#if 0 - Arc(Point center, double radius, Point start, Point end, Orientation dir) : - center(center), - radius(radius), - start_point(start), - end_point(end), - direction(dir) { - if (radius == 0.0 || - start_point == center || - end_point == center || - start_point == end_point) { - is_arc = false; - return; - } - is_arc = true; - } -#endif - - Point center; - double radius { 0 }; - bool is_arc { false }; Point start_point{ 0, 0 }; Point end_point{ 0, 0 }; + Point center; + double radius { 0 }; Orientation direction { Orientation::Unknown }; - - static std::optional try_create_arc( - const Points::const_iterator begin, - const Points::const_iterator end, - double max_radius = default_scaled_max_radius, - double tolerance = default_scaled_resolution, - double path_tolerance_percent = default_arc_length_percent_tolerance); - - bool is_valid() const { return is_arc; } }; static inline int sign(const int64_t i) @@ -274,25 +244,24 @@ static inline std::optional try_create_arc_impl( const double approximate_length = length(begin, end); assert(approximate_length > 0); const double arc_length_difference_relative = (arc_length - approximate_length) / approximate_length; - if (std::fabs(arc_length_difference_relative) >= path_tolerance_percent) - return {}; - Arc out; - out.is_arc = true; - out.direction = arc_dir > 0 ? Orientation::CCW : Orientation::CW; - out.center = circle.center; - out.radius = circle.radius; - out.start_point = *begin; - out.end_point = *std::prev(end); - return std::make_optional(out); + return std::fabs(arc_length_difference_relative) >= path_tolerance_percent ? + std::make_optional() : + std::make_optional(Arc{ + *begin, + *std::prev(end), + circle.center, + circle.radius, + arc_dir > 0 ? Orientation::CCW : Orientation::CW + }); } -std::optional Arc::try_create_arc( +static inline std::optional try_create_arc( const Points::const_iterator begin, const Points::const_iterator end, - double max_radius, - double tolerance, - double path_tolerance_percent) + double max_radius = default_scaled_max_radius, + double tolerance = default_scaled_resolution, + double path_tolerance_percent = default_arc_length_percent_tolerance) { std::optional circle = try_create_circle(begin, end, max_radius, tolerance); if (! circle) @@ -354,7 +323,7 @@ Path fit_path(const Points &src, double tolerance, double fit_circle_percent_tol std::optional arc; while (end != src.end()) { auto next_end = std::next(end); - if (std::optional this_arc = ArcWelder::Arc::try_create_arc( + if (std::optional this_arc = try_create_arc( begin, next_end, ArcWelder::default_scaled_max_radius, tolerance, fit_circle_percent_tolerance); @@ -432,20 +401,24 @@ double clip_end(Path &path, double distance) Vec2d v = (path.back().point - last.point).cast(); double lsqr = v.squaredNorm(); if (lsqr > sqr(distance)) { - path.push_back({ last.point + (v * (distance / sqrt(lsqr))).cast(), 0.f, Orientation::CCW }); + path.push_back({ last.point + (v * (distance / sqrt(lsqr))).cast() }); + // Length to go is zero. return 0; } distance -= sqrt(lsqr); } else { // Circular segment - float angle = arc_angle(path.back().point.cast(), last.point.cast(), last.radius); - double len = std::abs(last.radius) * angle; + double angle = arc_angle(path.back().point.cast(), last.point.cast(), last.radius); + double len = std::abs(last.radius) * angle; if (len > distance) { // Rotate the segment end point in reverse towards the start point. - path.push_back({ - last.point.rotated(- angle * (distance / len), - arc_center(path.back().point.cast(), last.point.cast(), last.radius, last.ccw()).cast()), + if (last.ccw()) + angle *= -1.; + path.push_back({ + last.point.rotated(angle * (distance / len), + arc_center(path.back().point.cast(), last.point.cast(), double(last.radius), last.ccw()).cast()), last.radius, last.orientation }); + // Length to go is zero. return 0; } distance -= len; @@ -460,11 +433,13 @@ double clip_end(Path &path, double distance) PathSegmentProjection point_to_path_projection(const Path &path, const Point &point, double search_radius2) { assert(path.size() != 1); + // initialized to "invalid" state. PathSegmentProjection out; out.distance2 = search_radius2; if (path.size() < 2 || path.front().point == point) { // First point is the closest point. if (path.empty()) { + // No closest point available. } else if (const Point p0 = path.front().point; p0 == point) { out.segment_id = 0; out.point = p0; @@ -475,12 +450,16 @@ PathSegmentProjection point_to_path_projection(const Path &path, const Point &po out.distance2 = d2; } } else { + assert(path.size() >= 2); + // min_point_it will contain an end point of a segment with a closest projection found + // or path.cbegin() if no such closest projection closer than search_radius2 was found. auto min_point_it = path.cbegin(); Point prev = path.front().point; - for (auto it = path.cbegin() + 1; it != path.cend(); ++ it) { + for (auto it = std::next(path.cbegin()); it != path.cend(); ++ it) { if (it->linear()) { // Linear segment Point proj; + // distance_to_squared() will possibly return the start or end point of a line segment. if (double d2 = line_alg::distance_to_squared(Line(prev, it->point), point, &proj); d2 < out.distance2) { out.point = proj; out.distance2 = d2; @@ -488,24 +467,25 @@ PathSegmentProjection point_to_path_projection(const Path &path, const Point &po } } else { // Circular arc - Vec2i64 center = arc_center(prev.cast(), it->point.cast(), it->radius, it->ccw()).cast(); + Vec2i64 center = arc_center(prev.cast(), it->point.cast(), double(it->radius), it->ccw()).cast(); // Test whether point is inside the wedge. Vec2i64 v1 = prev.cast() - center; Vec2i64 v2 = it->point.cast() - center; Vec2i64 vp = point.cast() - center; - bool inside = it->radius > 0 ? - // Smaller (convex) wedge. - (it->ccw() ? - cross2(v1, vp) > 0 && cross2(vp, v2) > 0 : - cross2(v1, vp) < 0 && cross2(vp, v2) < 0) : - // Larger (concave) wedge. - (it->ccw() ? - cross2(v2, vp) < 0 || cross2(vp, v1) < 0 : - cross2(v2, vp) > 0 || cross2(vp, v1) > 0); - if (inside) { + if (inside_arc_wedge_vectors(v1, v2, it->radius > 0, it->ccw(), vp)) { // Distance of the radii. - if (double d2 = sqr(std::abs(it->radius) - sqrt(double(v1.squaredNorm()))); d2 < out.distance2) { + const auto r = double(std::abs(it->radius)); + const auto rtest = sqrt(double(vp.squaredNorm())); + if (double d2 = sqr(rtest - r); d2 < out.distance2) { + if (rtest > SCALED_EPSILON) + // Project vp to the arc. + out.point = center.cast() + (vp.cast() * (r / rtest)).cast(); + else + // Test point is very close to the center of the radius. Any point of the arc is the closest. + // Pick the start. + out.point = prev; out.distance2 = d2; + out.center = center.cast(); min_point_it = it; } } else { @@ -521,44 +501,89 @@ PathSegmentProjection point_to_path_projection(const Path &path, const Point &po } if (! path.back().linear()) { // Calculate distance to the end point. - if (double d2 = (path.back().point - point).cast().norm(); d2 < out.distance2) { + if (double d2 = (path.back().point - point).cast().squaredNorm(); d2 < out.distance2) { out.point = path.back().point; out.distance2 = d2; min_point_it = std::prev(path.end()); } } - out.segment_id = min_point_it - path.begin(); + // If a new closes point was found, it is closer than search_radius2. + assert((min_point_it == path.cbegin()) == (out.distance2 == search_radius2)); + // Output is not valid yet. + assert(! out.valid()); + if (min_point_it != path.cbegin()) { + // Make it valid by setting the segment. + out.segment_id = std::prev(min_point_it) - path.begin(); + assert(out.valid()); + } } + assert(! out.valid() || (out.segment_id >= 0 && out.segment_id < path.size())); return out; } -std::pair split_at(const Path &path, PathSegmentProjection proj, const double min_segment_length) +std::pair split_at(const Path &path, const PathSegmentProjection &proj, const double min_segment_length) { + assert(proj.valid()); + assert(! proj.valid() || (proj.segment_id >= 0 && proj.segment_id < path.size())); std::pair out; - if (proj.segment_id == 0 && proj.point == path.front().point) - out.second = path; - else if (proj.segment_id + 1 == path.size() || (proj.segment_id + 2 == path.size() && proj.point == path.back().point)) + if (! proj.valid() || proj.segment_id + 1 == path.size() || (proj.segment_id + 2 == path.size() && proj.point == path.back().point)) out.first = path; + else if (proj.segment_id == 0 && proj.point == path.front().point) + out.second = path; else { + // Path will likely be split to two pieces. + assert(proj.valid() && proj.segment_id >= 0 && proj.segment_id + 1 < path.size()); const Segment &start = path[proj.segment_id]; const Segment &end = path[proj.segment_id + 1]; bool split_segment = true; - if (int64_t d = (proj.point - start.point).cast().squaredNorm(); d < sqr(min_segment_length)) { + int split_segment_id = proj.segment_id; + if (int64_t d2 = (proj.point - start.point).cast().squaredNorm(); d2 < sqr(min_segment_length)) { split_segment = false; - } else if (int64_t d = (proj.point - end.point).cast().squaredNorm(); d < sqr(min_segment_length)) { - ++ proj.segment_id; + int64_t d22 = (proj.point - end.point).cast().squaredNorm(); + if (d22 < d2) + // Split at the end of the segment. + ++ split_segment_id; + } else if (int64_t d2 = (proj.point - end.point).cast().squaredNorm(); d2 < sqr(min_segment_length)) { + ++ split_segment_id; split_segment = false; } if (split_segment) { - out.first.assign(path.begin(), path.begin() + proj.segment_id + 2); - out.second.assign(path.begin() + proj.segment_id, path.end()); + out.first.assign(path.begin(), path.begin() + split_segment_id + 2); + out.second.assign(path.begin() + split_segment_id, path.end()); + assert(out.first[out.first.size() - 2] == start); + assert(out.first.back() == end); + assert(out.second.front() == start); + assert(out.second[1] == end); + assert(out.first.size() + out.second.size() == path.size() + 2); + assert(out.first.back().radius == out.second[1].radius); out.first.back().point = proj.point; out.second.front().point = proj.point; + if (end.radius < 0) { + // A large arc (> PI) was split. + // At least one of the two arches that were created by splitting the original arch will become smaller. + // Make the radii of those arches that became < PI positive. + // In case of a projection onto an arc, proj.center should be filled in and valid. + auto vstart = (start.point - proj.center).cast(); + auto vend = (end.point - proj.center).cast(); + auto vproj = (proj.point - proj.center).cast(); + if (bool first_arc_minor = (cross2(vstart, vproj) > 0) == end.ccw()) + // Make the radius of a minor arc positive. + out.first.back().radius *= -1.f; + if (bool second_arc_minor = (cross2(vproj, vend) > 0) == end.ccw()) + // Make the radius of a minor arc positive. + out.second[1].radius *= -1.f; + } } else { - out.first.assign(path.begin(), path.begin() + proj.segment_id + 1); - out.second.assign(path.begin() + proj.segment_id, path.end()); + // Split at the start of proj.segment_id. + out.first.assign(path.begin(), path.begin() + split_segment_id + 1); + out.second.assign(path.begin() + split_segment_id, path.end()); + assert(out.first.size() + out.second.size() == path.size() + 1); + assert(out.first.back() == (split_segment_id == proj.segment_id ? start : end)); + assert(out.second.front() == (split_segment_id == proj.segment_id ? start : end)); } + assert(out.first.size() > 1); + assert(out.second.size() > 1); out.second.front().radius = 0; } diff --git a/src/libslic3r/Geometry/ArcWelder.hpp b/src/libslic3r/Geometry/ArcWelder.hpp index 86e4eb31a9..003e6baa0c 100644 --- a/src/libslic3r/Geometry/ArcWelder.hpp +++ b/src/libslic3r/Geometry/ArcWelder.hpp @@ -1,30 +1,3 @@ -// The following code for merging circles into arches originates from https://github.com/FormerLurker/ArcWelderLib - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Arc Welder: Anti-Stutter Library -// -// Compresses many G0/G1 commands into G2/G3(arc) commands where possible, ensuring the tool paths stay within the specified resolution. -// This reduces file size and the number of gcodes per second. -// -// Uses the 'Gcode Processor Library' for gcode parsing, position processing, logging, and other various functionality. -// -// Copyright(C) 2021 - Brad Hochgesang -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// This program is free software : you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published -// by the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the -// GNU Affero General Public License for more details. -// -// -// You can contact the author at the following email address: -// FormerLurker@pm.me -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - #ifndef slic3r_Geometry_ArcWelder_hpp_ #define slic3r_Geometry_ArcWelder_hpp_ @@ -38,18 +11,18 @@ namespace Slic3r { namespace Geometry { namespace ArcWelder { // positive radius: take shorter arc // negative radius: take longer arc // radius must NOT be zero! -template -inline Eigen::Matrix arc_center( +template +inline Eigen::Matrix arc_center( const Eigen::MatrixBase &start_pos, const Eigen::MatrixBase &end_pos, - const typename Derived::Scalar radius, + const typename Float radius, const bool is_ccw) { static_assert(Derived::IsVectorAtCompileTime && int(Derived::SizeAtCompileTime) == 2, "arc_center(): first parameter is not a 2D vector"); static_assert(Derived2::IsVectorAtCompileTime && int(Derived2::SizeAtCompileTime) == 2, "arc_center(): second parameter is not a 2D vector"); static_assert(std::is_same::value, "arc_center(): Both vectors must be of the same type."); + static_assert(std::is_same::value, "arc_center(): Radius must be of the same type as the vectors."); assert(radius != 0); - using Float = typename Derived::Scalar; using Vector = Eigen::Matrix; auto v = end_pos - start_pos; Float q2 = v.squaredNorm(); @@ -100,6 +73,69 @@ inline typename Derived::Scalar arc_length( return arc_angle(start_pos, end_pos, radius) * std::abs(radius); } +// Test whether a point is inside a wedge of an arc. +template +inline bool inside_arc_wedge_vectors( + const Eigen::MatrixBase &start_vec, + const Eigen::MatrixBase &end_vec, + const bool shorter_arc, + const bool ccw, + const Eigen::MatrixBase &query_vec) +{ + static_assert(Derived::IsVectorAtCompileTime && int(Derived::SizeAtCompileTime) == 2, "inside_arc_wedge_vectors(): start_vec is not a 2D vector"); + static_assert(Derived2::IsVectorAtCompileTime && int(Derived2::SizeAtCompileTime) == 2, "inside_arc_wedge_vectors(): end_vec is not a 2D vector"); + static_assert(Derived3::IsVectorAtCompileTime && int(Derived3::SizeAtCompileTime) == 2, "inside_arc_wedge_vectors(): query_vec is not a 2D vector"); + static_assert(std::is_same::value && + std::is_same::value, "inside_arc_wedge_vectors(): All vectors must be of the same type."); + return shorter_arc ? + // Smaller (convex) wedge. + (ccw ? + cross2(start_vec, query_vec) > 0 && cross2(query_vec, end_vec) > 0 : + cross2(start_vec, query_vec) < 0 && cross2(query_vec, end_vec) < 0) : + // Larger (concave) wedge. + (ccw ? + cross2(end_vec, query_vec) < 0 || cross2(query_vec, start_vec) < 0 : + cross2(end_vec, query_vec) > 0 || cross2(query_vec, start_vec) > 0); +} + +template +inline bool inside_arc_wedge( + const Eigen::MatrixBase &start_pt, + const Eigen::MatrixBase &end_pt, + const Eigen::MatrixBase ¢er_pt, + const bool shorter_arc, + const bool ccw, + const Eigen::MatrixBase &query_pt) +{ + static_assert(Derived::IsVectorAtCompileTime && int(Derived::SizeAtCompileTime) == 2, "inside_arc_wedge(): start_pt is not a 2D vector"); + static_assert(Derived2::IsVectorAtCompileTime && int(Derived2::SizeAtCompileTime) == 2, "inside_arc_wedge(): end_pt is not a 2D vector"); + static_assert(Derived2::IsVectorAtCompileTime && int(Derived3::SizeAtCompileTime) == 2, "inside_arc_wedge(): center_pt is not a 2D vector"); + static_assert(Derived3::IsVectorAtCompileTime && int(Derived4::SizeAtCompileTime) == 2, "inside_arc_wedge(): query_pt is not a 2D vector"); + static_assert(std::is_same::value && + std::is_same::value && + std::is_same::value, "inside_arc_wedge(): All vectors must be of the same type."); + return inside_arc_wedge_vectors(start_pt - center_pt, end_pt - center_pt, shorter_arc, ccw, query_pt - center_pt); +} + +template +inline bool inside_arc_wedge( + const Eigen::MatrixBase &start_pt, + const Eigen::MatrixBase &end_pt, + const Float radius, + const bool ccw, + const Eigen::MatrixBase &query_pt) +{ + static_assert(Derived::IsVectorAtCompileTime && int(Derived::SizeAtCompileTime) == 2, "inside_arc_wedge(): start_pt is not a 2D vector"); + static_assert(Derived2::IsVectorAtCompileTime && int(Derived2::SizeAtCompileTime) == 2, "inside_arc_wedge(): end_pt is not a 2D vector"); + static_assert(Derived3::IsVectorAtCompileTime && int(Derived3::SizeAtCompileTime) == 2, "inside_arc_wedge(): query_pt is not a 2D vector"); + static_assert(std::is_same::value && + std::is_same::value && + std::is_same::value, "inside_arc_wedge(): All vectors + radius must be of the same type."); + return inside_arc_wedge(start_pt, end_pt, + arc_center(start_pt, end_pt, radius, ccw), + radius > 0, ccw, query_pt); +} + // Discretize arc given the radius, orientation and maximum deviation from the arc. // Returned polygon starts with p1, ends with p2 and it is discretized to guarantee the maximum deviation. Points arc_discretize(const Point &p1, const Point &p2, const double radius, const bool ccw, const double deviation); @@ -133,20 +169,39 @@ struct Segment bool cw() const { return orientation == Orientation::CW; } }; +inline bool operator==(const Segment &lhs, const Segment &rhs) { + return lhs.point == rhs.point && lhs.radius == rhs.radius && lhs.orientation == rhs.orientation; +} + using Segments = std::vector; using Path = Segments; // Interpolate polyline path with a sequence of linear / circular segments given the interpolation tolerance. // Only convert to polyline if zero tolerance. // Convert to polyline and decimate polyline if zero fit_circle_percent_tolerance. +// Path fitting is inspired with the arc fitting algorithm in +// Arc Welder: Anti-Stutter Library by Brad Hochgesang FormerLurker@pm.me +// https://github.com/FormerLurker/ArcWelderLib Path fit_path(const Points &points, double tolerance, double fit_circle_percent_tolerance); + +// Decimate polyline into a smooth path structure using Douglas-Peucker polyline decimation algorithm. inline Path fit_polyline(const Points &points, double tolerance) { return fit_path(points, tolerance, 0.); } -inline double segment_length(const Segment &start, const Segment &end) +template +inline FloatType segment_length(const Segment &start, const Segment &end) { return end.linear() ? - (end.point - start.point).cast().norm() : - arc_length(start.point.cast(), end.point.cast(), end.radius); + (end.point - start.point).cast().norm() : + arc_length(start.point.cast(), end.point.cast(), FloatType(end.radius)); +} + +template +inline FloatType path_length(const Path &path) +{ + FloatType len = 0; + for (size_t i = 1; i < path.size(); ++ i) + len += segment_length(path[i - 1], path[i]); + return len; } // Estimate minimum path length of a segment cheaply without having to calculate center of an arc and it arc length. @@ -160,7 +215,7 @@ inline int64_t estimate_min_segment_length(const Segment &start, const Segment & } else { // Arc with angle > PI. // Returns estimate of PI * r - return - 3 * int64_t(end.radius); + return - int64_t(3) * int64_t(end.radius); } } @@ -185,7 +240,10 @@ struct PathSegmentProjection { // Start segment of a projection on the path. size_t segment_id { std::numeric_limits::max() }; + // Projection of the point on the segment. Point point { 0, 0 }; + // If the point lies on an arc, the arc center is cached here. + Point center { 0, 0 }; // Square of a distance of the projection. double distance2 { std::numeric_limits::max() }; @@ -194,7 +252,7 @@ struct PathSegmentProjection // Returns closest segment and a parameter along the closest segment of a path to a point. PathSegmentProjection point_to_path_projection(const Path &path, const Point &point, double search_radius2 = std::numeric_limits::max()); // Split a path into two paths at a segment point. Snap to an existing point if the projection of "point is closer than min_segment_length. -std::pair split_at(const Path &path, PathSegmentProjection proj, const double min_segment_length); +std::pair split_at(const Path &path, const PathSegmentProjection &proj, const double min_segment_length); // Split a path into two paths at a point closest to "point". Snap to an existing point if the projection of "point is closer than min_segment_length. std::pair split_at(const Path &path, const Point &point, const double min_segment_length); diff --git a/tests/libslic3r/test_arc_welder.cpp b/tests/libslic3r/test_arc_welder.cpp index 6c4b58e00f..3cc13d1bae 100644 --- a/tests/libslic3r/test_arc_welder.cpp +++ b/tests/libslic3r/test_arc_welder.cpp @@ -199,3 +199,66 @@ TEST_CASE("arc fitting", "[ArcWelder]") { } } } + +TEST_CASE("arc wedge test", "[ArcWelder]") { + using namespace Slic3r::Geometry; + + WHEN("test point inside wedge, arc from { 2, 1 } to { 1, 2 }") { + const int64_t s = 1000000; + const Vec2i64 p1{ 2 * s, s }; + const Vec2i64 p2{ s, 2 * s }; + const Vec2i64 center{ s, s }; + const int64_t radius{ s }; + auto test = [center]( + // Arc data + const Vec2i64 &p1, const Vec2i64 &p2, const int64_t r, const bool ccw, + // Test data + const Vec2i64 &ptest, const bool ptest_inside) { + const Vec2d c = ArcWelder::arc_center(p1.cast(), p2.cast(), double(r), ccw); + REQUIRE(is_approx(c, center.cast())); + REQUIRE(ArcWelder::inside_arc_wedge(p1, p2, center, r > 0, ccw, ptest) == ptest_inside); + REQUIRE(ArcWelder::inside_arc_wedge(p1.cast(), p2.cast(), double(r), ccw, ptest.cast()) == ptest_inside); + }; + auto test_quadrants = [center, test]( + // Arc data + const Vec2i64 &p1, const Vec2i64 &p2, const int64_t r, const bool ccw, + // Test data + const Vec2i64 &ptest1, const bool ptest_inside1, + const Vec2i64 &ptest2, const bool ptest_inside2, + const Vec2i64 &ptest3, const bool ptest_inside3, + const Vec2i64 &ptest4, const bool ptest_inside4) { + test(p1, p2, r, ccw, ptest1 + center, ptest_inside1); + test(p1, p2, r, ccw, ptest2 + center, ptest_inside2); + test(p1, p2, r, ccw, ptest3 + center, ptest_inside3); + test(p1, p2, r, ccw, ptest4 + center, ptest_inside4); + }; + THEN("90 degrees arc, CCW") { + test_quadrants(p1, p2, radius, true, + Vec2i64{ s, s }, true, + Vec2i64{ s, - s }, false, + Vec2i64{ - s, s }, false, + Vec2i64{ - s, - s }, false); + } + THEN("270 degrees arc, CCW") { + test_quadrants(p2, p1, -radius, true, + Vec2i64{ s, s }, false, + Vec2i64{ s, - s }, true, + Vec2i64{ - s, s }, true, + Vec2i64{ - s, - s }, true); + } + THEN("90 degrees arc, CW") { + test_quadrants(p2, p1, radius, false, + Vec2i64{ s, s }, true, + Vec2i64{ s, - s }, false, + Vec2i64{ - s, s }, false, + Vec2i64{ - s, - s }, false); + } + THEN("270 degrees arc, CW") { + test_quadrants(p1, p2, -radius, false, + Vec2i64{ s, s }, false, + Vec2i64{ s, - s }, true, + Vec2i64{ - s, s }, true, + Vec2i64{ - s, - s }, true); + } + } +} From 594e36c70ac03326f0800a5c20de042ebf38705f Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 17 Jul 2023 14:18:56 +0200 Subject: [PATCH 006/136] ArcWelder bugfixes --- src/libslic3r/GCode.cpp | 3 +- src/libslic3r/GCode/GCodeWriter.hpp | 4 + src/libslic3r/GCode/Wipe.cpp | 55 +++++++--- src/libslic3r/Geometry/ArcWelder.cpp | 148 +++++++++++++++++++-------- src/libslic3r/Geometry/ArcWelder.hpp | 9 ++ src/libslic3r/Point.cpp | 2 +- tests/libslic3r/test_arc_welder.cpp | 6 +- 7 files changed, 164 insertions(+), 63 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 6ae084772a..07c9849127 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2457,6 +2457,7 @@ std::string GCodeGenerator::extrude_loop(const ExtrusionLoop &loop_src, const GC gcode += m_writer.set_print_acceleration(fast_round_up(m_config.default_acceleration.value)); if (m_wipe.enabled()) { + // Wipe will hide the seam. m_wipe.set_path(std::move(smooth_path), false); } else if (loop_src.paths.back().role().is_external_perimeter() && m_layer != nullptr && m_config.perimeters.value > 1) { // Only wipe inside if the wipe along the perimeter is disabled. @@ -2848,7 +2849,7 @@ std::string GCodeGenerator::_extrude( if (! emit_radius) { // Calculate quantized IJ circle center offset. Vec2d center_raw = Geometry::ArcWelder::arc_center(prev.cast(), p.cast(), double(radius), it->ccw()) - prev; - ij = Vec2d{ GCodeFormatter::quantize_xyzf(center_raw.x()), GCodeFormatter::quantize_xyzf(center_raw.y()) }; + ij = GCodeFormatter::quantize(center_raw); } double angle = Geometry::ArcWelder::arc_angle(prev.cast(), p.cast(), double(radius)); const double line_length = angle * std::abs(radius); diff --git a/src/libslic3r/GCode/GCodeWriter.hpp b/src/libslic3r/GCode/GCodeWriter.hpp index 45f905efd4..09a68b92de 100644 --- a/src/libslic3r/GCode/GCodeWriter.hpp +++ b/src/libslic3r/GCode/GCodeWriter.hpp @@ -158,6 +158,10 @@ public: static double quantize(double v, size_t ndigits) { return std::round(v * pow_10[ndigits]) * pow_10_inv[ndigits]; } static double quantize_xyzf(double v) { return quantize(v, XYZF_EXPORT_DIGITS); } static double quantize_e(double v) { return quantize(v, E_EXPORT_DIGITS); } + static Vec2d quantize(const Vec2d &pt) + { return { quantize(pt.x(), XYZF_EXPORT_DIGITS), quantize(pt.y(), XYZF_EXPORT_DIGITS) }; } + static Vec3d quantize(const Vec3d &pt) + { return { quantize(pt.x(), XYZF_EXPORT_DIGITS), quantize(pt.y(), XYZF_EXPORT_DIGITS), quantize(pt.z(), XYZF_EXPORT_DIGITS) }; } void emit_axis(const char axis, const double v, size_t digits); diff --git a/src/libslic3r/GCode/Wipe.cpp b/src/libslic3r/GCode/Wipe.cpp index 90ac401cc2..561e86a835 100644 --- a/src/libslic3r/GCode/Wipe.cpp +++ b/src/libslic3r/GCode/Wipe.cpp @@ -92,44 +92,66 @@ std::string Wipe::wipe(GCodeGenerator &gcodegen, bool toolchange) } }; const double xy_to_e = this->calc_xy_to_e_ratio(gcodegen.writer().config, extruder.id()); - auto wipe_linear = [&gcode, &gcodegen, &retract_length, xy_to_e](const Vec2d &prev, Vec2d &p) { - double segment_length = (p - prev).norm(); - // Quantize E axis as it is to be + auto wipe_linear = [&gcode, &gcodegen, &retract_length, xy_to_e](const Vec2d &prev_quantized, Vec2d &p) { + Vec2d p_quantized = GCodeFormatter::quantize(p); + if (p_quantized == prev_quantized) { + p = p_quantized; + return false; + } + double segment_length = (p_quantized - prev_quantized).norm(); + // Quantize E axis as it is to be extruded as a whole segment. double dE = GCodeFormatter::quantize_e(xy_to_e * segment_length); bool done = false; if (dE > retract_length - EPSILON) { if (dE > retract_length + EPSILON) // Shorten the segment. - p = gcodegen.point_to_gcode_quantized(prev + (p - prev) * (retract_length / dE)); + p = GCodeFormatter::quantize(Vec2d(prev_quantized + (p - prev_quantized) * (retract_length / dE))); + else + p = p_quantized; dE = retract_length; done = true; - } + } else + p = p_quantized; gcode += gcodegen.writer().extrude_to_xy(p, -dE, wipe_retract_comment); retract_length -= dE; return done; }; const bool emit_radius = gcodegen.config().arc_fitting == ArcFittingType::EmitRadius; - auto wipe_arc = [&gcode, &gcodegen, &retract_length, xy_to_e, emit_radius](const Vec2d &prev, Vec2d &p, double radius_in, const bool ccw) { + auto wipe_arc = [&gcode, &gcodegen, &retract_length, xy_to_e, emit_radius]( + const Vec2d &prev_quantized, Vec2d &p, double radius_in, const bool ccw) { + Vec2d p_quantized = GCodeFormatter::quantize(p); + if (p_quantized == prev_quantized) { + p = p_quantized; + return false; + } // Only quantize radius if emitting it directly into G-code. Otherwise use the exact radius for calculating the IJ values. double radius = emit_radius ? GCodeFormatter::quantize_xyzf(radius_in) : radius_in; - Vec2d center = Geometry::ArcWelder::arc_center(prev.cast(), p.cast(), double(radius), ccw); - float angle = Geometry::ArcWelder::arc_angle(prev.cast(), p.cast(), double(radius)); + Vec2d center = Geometry::ArcWelder::arc_center(prev_quantized.cast(), p_quantized.cast(), double(radius), ccw); + float angle = Geometry::ArcWelder::arc_angle(prev_quantized.cast(), p_quantized.cast(), double(radius)); double segment_length = angle * std::abs(radius); double dE = GCodeFormatter::quantize_e(xy_to_e * segment_length); bool done = false; if (dE > retract_length - EPSILON) { - if (dE > retract_length + EPSILON) - // Shorten the segment. - p = gcodegen.point_to_gcode_quantized(Point(prev).rotated((ccw ? angle : -angle) * (retract_length / dE), center.cast())); + if (dE > retract_length + EPSILON) { + // Shorten the segment. Recalculate the arc from the unquantized end coordinate. + center = Geometry::ArcWelder::arc_center(prev_quantized.cast(), p.cast(), double(radius), ccw); + angle = Geometry::ArcWelder::arc_angle(prev_quantized.cast(), p.cast(), double(radius)); + segment_length = angle * std::abs(radius); + dE = xy_to_e * segment_length; + p = GCodeFormatter::quantize( + Vec2d(center + Eigen::Rotation2D((ccw ? angle : -angle) * (retract_length / dE)) * (prev_quantized - center))); + } else + p = p_quantized; dE = retract_length; done = true; - } + } else + p = p_quantized; if (emit_radius) { gcode += gcodegen.writer().extrude_to_xy_G2G3R(p, radius, ccw, -dE, wipe_retract_comment); } else { // Calculate quantized IJ circle center offset. - Vec2d ij{ GCodeFormatter::quantize_xyzf(center.x() - prev.x()), GCodeFormatter::quantize_xyzf(center.y() - prev.y()) }; - gcode += gcodegen.writer().extrude_to_xy_G2G3IJ(p, ij, ccw, -dE, wipe_retract_comment); + gcode += gcodegen.writer().extrude_to_xy_G2G3IJ( + p, GCodeFormatter::quantize(Vec2d(center - prev_quantized)), ccw, -dE, wipe_retract_comment); } retract_length -= dE; return done; @@ -137,7 +159,7 @@ std::string Wipe::wipe(GCodeGenerator &gcodegen, bool toolchange) // Start with the current position, which may be different from the wipe path start in case of loop clipping. Vec2d prev = gcodegen.point_to_gcode_quantized(gcodegen.last_pos()); auto it = this->path().begin(); - Vec2d p = gcodegen.point_to_gcode_quantized(it->point + m_offset); + Vec2d p = gcodegen.point_to_gcode(it->point + m_offset); ++ it; bool done = false; if (p != prev) { @@ -148,7 +170,7 @@ std::string Wipe::wipe(GCodeGenerator &gcodegen, bool toolchange) prev = p; auto end = this->path().end(); for (; it != end && ! done; ++ it) { - p = gcodegen.point_to_gcode_quantized(it->point + m_offset); + p = gcodegen.point_to_gcode(it->point + m_offset); if (p != prev) { start_wipe(); if (it->linear() ? @@ -161,6 +183,7 @@ std::string Wipe::wipe(GCodeGenerator &gcodegen, bool toolchange) } if (wiped) { // add tag for processor + assert(p == GCodeFormatter::quantize(p)); gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Wipe_End) + "\n"; gcodegen.set_last_pos(gcodegen.gcode_to_point(p)); } diff --git a/src/libslic3r/Geometry/ArcWelder.cpp b/src/libslic3r/Geometry/ArcWelder.cpp index 177c549b6d..3009305058 100644 --- a/src/libslic3r/Geometry/ArcWelder.cpp +++ b/src/libslic3r/Geometry/ArcWelder.cpp @@ -77,12 +77,12 @@ static std::optional try_create_circle(const Point &p1, const Point &p2, // Returns a closest point on the segment. // Returns false if the closest point is not inside the segment, but at its boundary. -static bool foot_pt_on_segment(const Point &p1, const Point &p2, const Point &c, Point &out) +static bool foot_pt_on_segment(const Point &p1, const Point &p2, const Point &pt, Point &out) { Vec2i64 v21 = (p2 - p1).cast(); int64_t l2 = v21.squaredNorm(); if (l2 > int64_t(SCALED_EPSILON)) { - if (int64_t t = (c - p1).cast().dot(v21); + if (int64_t t = (pt - p1).cast().dot(v21); t >= int64_t(SCALED_EPSILON) && t < l2 - int64_t(SCALED_EPSILON)) { out = p1 + ((double(t) / double(l2)) * v21.cast()).cast(); return true; @@ -104,12 +104,12 @@ static inline bool circle_approximation_sufficient(const Circle &circle, const P std::abs(distance_from_center - circle.radius) > tolerance) return false; - for (auto it = std::next(begin); std::next(it) != end; ++ it) { + for (auto it = std::next(begin); it != end; ++ it) { if (double distance_from_center = (*it - circle.center).cast().norm(); std::abs(distance_from_center - circle.radius) > tolerance) return false; Point closest_point; - if (foot_pt_on_segment(*it, *std::next(it), circle.center, closest_point)) { + if (foot_pt_on_segment(*std::prev(it), *it, circle.center, closest_point)) { if (double distance_from_center = (closest_point - circle.center).cast().norm(); std::abs(distance_from_center - circle.radius) > tolerance) return false; @@ -184,10 +184,10 @@ static std::optional try_create_circle(const Points::const_iterator begi // ported from ArcWelderLib/ArcWelder/segmented/shape.h class "arc" class Arc { public: - Point start_point{ 0, 0 }; - Point end_point{ 0, 0 }; + Point start_point; + Point end_point; Point center; - double radius { 0 }; + double radius; Orientation direction { Orientation::Unknown }; }; @@ -196,64 +196,84 @@ static inline int sign(const int64_t i) return i > 0 ? 1 : i < 0 ? -1 : 0; } -static inline std::optional try_create_arc_impl( - const Circle &circle, +// Return orientation of a polyline with regard to the center. +// Successive points are expected to take less than a PI angle step. +Orientation arc_orientation( + const Point ¢er, const Points::const_iterator begin, - const Points::const_iterator end, - double path_tolerance_percent) + const Points::const_iterator end) { assert(end - begin >= 3); // Assumption: Two successive points of a single segment span an angle smaller than PI. - Vec2i64 vstart = (*begin - circle.center).cast(); + Vec2i64 vstart = (*begin - center).cast(); Vec2i64 vprev = vstart; int arc_dir = 0; for (auto it = std::next(begin); it != end; ++ it) { - Vec2i64 v = (*it - circle.center).cast(); + Vec2i64 v = (*it - center).cast(); int dir = sign(cross2(vprev, v)); if (dir == 0) { // Ignore radial segments. } else if (arc_dir * dir < 0) { - // The path turns back and overextrudes. Such path is likely invalid, but the arc interpolation should not cover it. + // The path turns back and overextrudes. Such path is likely invalid, but the arc interpolation should + // rather maintain such an invalid path instead of covering it up. + // Don't replace such a path with an arc. return {}; } else { - // Success, moving in the same direction. + // Success, either establishing the direction for the first time, or moving in the same direction as the last time. arc_dir = dir; vprev = v; } } - - if (arc_dir == 0) - // All points were radial, this should not happen. + return arc_dir == 0 ? + // All points are radial wrt. the center, this is unexpected. + Orientation::Unknown : + // Arc is valid, either CCW or CW. + arc_dir > 0 ? Orientation::CCW : Orientation::CW; +} + +static inline std::optional try_create_arc_impl( + const Circle &circle, + const Points::const_iterator begin, + const Points::const_iterator end, + const double tolerance, + const double path_tolerance_percent) +{ + assert(end - begin >= 3); + // Assumption: Two successive points of a single segment span an angle smaller than PI. + Orientation orientation = arc_orientation(circle.center, begin, end); + if (orientation == Orientation::Unknown) return {}; - Vec2i64 vend = (*std::prev(end) - circle.center).cast(); - double angle = atan2(double(cross2(vstart, vend)), double(vstart.dot(vend))); - if (arc_dir > 0) { - if (angle < 0) - angle += 2. * M_PI; - } else { - if (angle > 0) - angle -= 2. * M_PI; - } + Vec2i64 vstart = (*begin - circle.center).cast(); + Vec2i64 vend = (*std::prev(end) - circle.center).cast(); + double angle = atan2(double(cross2(vstart, vend)), double(vstart.dot(vend))); + if (orientation == Orientation::CW) + angle *= -1.; + if (angle < 0) + angle += 2. * M_PI; + assert(angle >= 0. && angle < 2. * M_PI + EPSILON); // Check the length against the original length. // This can trigger simply due to the differing path lengths // but also could indicate that the vector calculation above // got wrong direction - const double arc_length = std::abs(circle.radius * angle); + const double arc_length = circle.radius * angle; const double approximate_length = length(begin, end); assert(approximate_length > 0); const double arc_length_difference_relative = (arc_length - approximate_length) / approximate_length; - return std::fabs(arc_length_difference_relative) >= path_tolerance_percent ? - std::make_optional() : - std::make_optional(Arc{ + if (std::fabs(arc_length_difference_relative) >= path_tolerance_percent) { + return {}; + } else { + assert(circle_approximation_sufficient(circle, begin, end, tolerance + SCALED_EPSILON)); + return std::make_optional(Arc{ *begin, *std::prev(end), circle.center, - circle.radius, - arc_dir > 0 ? Orientation::CCW : Orientation::CW + angle > M_PI ? - circle.radius : circle.radius, + orientation }); + } } static inline std::optional try_create_arc( @@ -266,7 +286,7 @@ static inline std::optional try_create_arc( std::optional circle = try_create_circle(begin, end, max_radius, tolerance); if (! circle) return {}; - return try_create_arc_impl(*circle, begin, end, path_tolerance_percent); + return try_create_arc_impl(*circle, begin, end, tolerance, path_tolerance_percent); } float arc_angle(const Vec2f &start_pos, const Vec2f &end_pos, Vec2f ¢er_pos, bool is_ccw) @@ -328,6 +348,7 @@ Path fit_path(const Points &src, double tolerance, double fit_circle_percent_tol ArcWelder::default_scaled_max_radius, tolerance, fit_circle_percent_tolerance); this_arc) { + assert(this_arc->direction != Orientation::Unknown); arc = this_arc; end = next_end; } else @@ -335,8 +356,12 @@ Path fit_path(const Points &src, double tolerance, double fit_circle_percent_tol } if (arc) { // If there is a trailing polyline, decimate it first before saving a new arc. - if (out.size() - begin_pl_idx > 2) + if (out.size() - begin_pl_idx > 2) { + // Decimating linear segmens only. + assert(std::all_of(out.begin() + begin_pl_idx + 1, out.end(), [](const Segment &seg) { return seg.linear(); })); out.erase(douglas_peucker_in_place(out.begin() + begin_pl_idx, out.end(), tolerance), out.end()); + assert(out.back().linear()); + } // Save the index of an end of the new circle segment, which may become the first point of a possible future polyline. begin_pl_idx = int(out.size()); // This will be the next point to try to add. @@ -344,7 +369,37 @@ Path fit_path(const Points &src, double tolerance, double fit_circle_percent_tol // Add the new arc. assert(*begin == arc->start_point); assert(*std::prev(it) == arc->end_point); + assert(out.back().point == arc->start_point); out.push_back({ arc->end_point, float(arc->radius), arc->direction }); +#if 0 + // Verify that all the source points are at tolerance distance from the interpolated path. + { + const Segment &seg_start = *std::prev(std::prev(out.end())); + const Segment &seg_end = out.back(); + const Vec2d center = arc_center(seg_start.point.cast(), seg_end.point.cast(), double(seg_end.radius), seg_end.ccw()); + assert(seg_start.point == *begin); + assert(seg_end.point == *std::prev(end)); + assert(arc_orientation(center.cast(), begin, end) == arc->direction); + for (auto it = std::next(begin); it != end; ++ it) { + Point ptstart = *std::prev(it); + Point ptend = *it; + Point closest_point; + if (foot_pt_on_segment(ptstart, ptend, center.cast(), closest_point)) { + double distance_from_center = (closest_point.cast() - center).norm(); + assert(std::abs(distance_from_center - std::abs(seg_end.radius)) < tolerance + SCALED_EPSILON); + } + Vec2d v = (ptend - ptstart).cast(); + double len = v.norm(); + auto num_segments = std::min(10, ceil(2. * len / fit_circle_percent_tolerance)); + for (size_t i = 0; i < num_segments; ++ i) { + Point p = ptstart + (v * (double(i) / double(num_segments))).cast(); + assert(i == 0 || inside_arc_wedge(seg_start.point.cast(), seg_end.point.cast(), center, seg_end.radius > 0, seg_end.ccw(), p.cast())); + double d2 = sqr((p.cast() - center).norm() - std::abs(seg_end.radius)); + assert(d2 < sqr(tolerance + SCALED_EPSILON)); + } + } + } +#endif } else { // Arc is not valid, append a linear segment. out.push_back({ *it ++ }); @@ -357,9 +412,18 @@ Path fit_path(const Points &src, double tolerance, double fit_circle_percent_tol #if 0 // Verify that all the source points are at tolerance distance from the interpolated path. - for (const Point &p : src) { - PathSegmentProjection proj = point_to_path_projection(out, p); - assert(proj.distance2 < sqr(tolerance + SCALED_EPSILON)); + for (auto it = std::next(src.begin()); it != src.end(); ++ it) { + Point start = *std::prev(it); + Point end = *it; + Vec2d v = (end - start).cast(); + double len = v.norm(); + auto num_segments = std::min(10, ceil(2. * len / fit_circle_percent_tolerance)); + for (size_t i = 0; i <= num_segments; ++ i) { + Point p = start + (v * (double(i) / double(num_segments))).cast(); + PathSegmentProjection proj = point_to_path_projection(out, p); + assert(proj.valid()); + assert(proj.distance2 < sqr(tolerance + SCALED_EPSILON)); + } } #endif @@ -369,14 +433,14 @@ Path fit_path(const Points &src, double tolerance, double fit_circle_percent_tol void reverse(Path &path) { if (path.size() > 1) { - std::reverse(path.begin(), path.end()); auto prev = path.begin(); for (auto it = std::next(prev); it != path.end(); ++ it) { - it->radius = prev->radius; - it->orientation = prev->orientation == Orientation::CCW ? Orientation::CW : Orientation::CCW; + prev->radius = it->radius; + prev->orientation = it->orientation == Orientation::CCW ? Orientation::CW : Orientation::CCW; prev = it; } - path.front().radius = 0; + path.back().radius = 0; + std::reverse(path.begin(), path.end()); } } diff --git a/src/libslic3r/Geometry/ArcWelder.hpp b/src/libslic3r/Geometry/ArcWelder.hpp index 003e6baa0c..dab73b532a 100644 --- a/src/libslic3r/Geometry/ArcWelder.hpp +++ b/src/libslic3r/Geometry/ArcWelder.hpp @@ -153,6 +153,15 @@ enum class Orientation : unsigned char { CW, }; +// Returns orientation of a polyline with regard to the center. +// Successive points are expected to take less than a PI angle step. +// Returns Orientation::Unknown if the orientation with regard to the center +// is not monotonous. +Orientation arc_orientation( + const Point ¢er, + const Points::const_iterator begin, + const Points::const_iterator end); + // Single segment of a smooth path. struct Segment { diff --git a/src/libslic3r/Point.cpp b/src/libslic3r/Point.cpp index 1e1de6ae08..4e579312d9 100644 --- a/src/libslic3r/Point.cpp +++ b/src/libslic3r/Point.cpp @@ -52,7 +52,7 @@ void Point::rotate(double angle, const Point ¢er) double c = ::cos(angle); auto d = cur - center.cast(); this->x() = fast_round_up(center.x() + c * d.x() - s * d.y()); - this->y() = fast_round_up(center.y() + c * d.y() + s * d.x()); + this->y() = fast_round_up(center.y() + s * d.x() + c * d.y()); } bool has_duplicate_points(Points &&pts) diff --git a/tests/libslic3r/test_arc_welder.cpp b/tests/libslic3r/test_arc_welder.cpp index 3cc13d1bae..39b06e7f1f 100644 --- a/tests/libslic3r/test_arc_welder.cpp +++ b/tests/libslic3r/test_arc_welder.cpp @@ -142,7 +142,7 @@ TEST_CASE("arc fitting", "[ArcWelder]") { REQUIRE(path.front().point == p1); REQUIRE(path.front().radius == 0.f); REQUIRE(path.back().point == p2); - REQUIRE(path.back().radius == Approx(radius)); + REQUIRE(path.back().radius == Approx(r)); REQUIRE(path.back().ccw() == ccw); }; THEN("90 degrees arc, CCW is fitted") { @@ -179,10 +179,10 @@ TEST_CASE("arc fitting", "[ArcWelder]") { REQUIRE(path.front().point == p1); REQUIRE(path.front().radius == 0.f); REQUIRE(path[1].point == p2); - REQUIRE(path[1].radius == Approx(radius)); + REQUIRE(path[1].radius == Approx(r)); REQUIRE(path[1].ccw() == ccw); REQUIRE(path.back().point == p3); - REQUIRE(path.back().radius == Approx(radius)); + REQUIRE(path.back().radius == Approx(- r)); REQUIRE(path.back().ccw() == ! ccw); }; THEN("90 degrees arches, CCW are fitted") { From 213c311cd8d981f5624890ef97e6aa3aa9856f99 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 17 Jul 2023 15:06:56 +0200 Subject: [PATCH 007/136] ArcWelder: Switching off arc_fitting_tolerance when arc_fitting is disabled. --- src/slic3r/GUI/ConfigManipulation.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index f645e8a0dd..b5ef9b71a1 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -338,6 +338,9 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) toggle_field("min_feature_size", have_arachne); toggle_field("min_bead_width", have_arachne); toggle_field("thin_walls", !have_arachne); + + bool has_arc_fitting = config->opt_enum("arc_fitting") != ArcFittingType::Disabled; + toggle_field("arc_fitting_tolerance", has_arc_fitting); } void ConfigManipulation::update_print_sla_config(DynamicPrintConfig* config, const bool is_global_config/* = false*/) From 6dc447503082f1e42d33e3dd6d8426aafe67ff59 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 17 Jul 2023 15:07:51 +0200 Subject: [PATCH 008/136] Fixed missing template keywords, compiled on MSVCC but not on other compilers. --- src/libslic3r/MultiPoint.hpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/MultiPoint.hpp b/src/libslic3r/MultiPoint.hpp index dcd192bac7..ece2ce2d90 100644 --- a/src/libslic3r/MultiPoint.hpp +++ b/src/libslic3r/MultiPoint.hpp @@ -50,18 +50,19 @@ inline OutputIterator douglas_peucker(InputIterator begin, InputIterator end, Ou } else { SquareLengthType max_dist_sq = 0; // Find point furthest from line seg created by (anchor, floater) and note it. - const Vector v = (f - a).cast(); + const Vector v = (f - a).template cast(); if (const SquareLengthType l2 = v.squaredNorm(); l2 == 0) { // Zero length segment, find the furthest point between anchor and floater. for (auto it = std::next(anchor); it != floater; ++ it) - if (SquareLengthType dist_sq = (point_getter(*it) - a).cast().squaredNorm(); dist_sq > max_dist_sq) { + if (SquareLengthType dist_sq = (point_getter(*it) - a).template cast().squaredNorm(); + dist_sq > max_dist_sq) { max_dist_sq = dist_sq; furthest = it; } } else { // Find Find the furthest point from the line . const double dl2 = double(l2); - const Vec2d dv = v.cast(); + const Vec2d dv = v.template cast(); for (auto it = std::next(anchor); it != floater; ++ it) { const auto p = point_getter(*it); const Vector va = (p - a).template cast(); @@ -70,11 +71,11 @@ inline OutputIterator douglas_peucker(InputIterator begin, InputIterator end, Ou if (t <= 0) { dist_sq = va.squaredNorm(); } else if (t >= l2) { - dist_sq = (p - f).cast().squaredNorm(); + dist_sq = (p - f).template cast().squaredNorm(); } else if (double dt = double(t) / dl2; dt <= 0) { dist_sq = va.squaredNorm(); } else if (dt >= 1.) { - dist_sq = (p - f).cast().squaredNorm(); + dist_sq = (p - f).template cast().squaredNorm(); } else { const Vector w = (dt * dv).cast(); dist_sq = (w - va).squaredNorm(); From 9319a6152fffc6accc93b873a61b20367b11ab43 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 17 Jul 2023 15:21:12 +0200 Subject: [PATCH 009/136] ArcWelder: Fixed some compiler errors. --- src/libslic3r/ExtrusionEntity.hpp | 6 ++++-- src/libslic3r/Geometry/Circle.hpp | 2 +- src/libslic3r/MultiPoint.hpp | 1 - 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp index 1553d40b6e..d7c9367ef5 100644 --- a/src/libslic3r/ExtrusionEntity.hpp +++ b/src/libslic3r/ExtrusionEntity.hpp @@ -63,8 +63,10 @@ class ExtrusionEntityReference final { public: ExtrusionEntityReference() = delete; - ExtrusionEntityReference(const ExtrusionEntity &extrusion_entity, bool flipped) : m_extrusion_entity(&extrusion_entity), m_flipped(flipped) {} - ExtrusionEntityReference operator=(const ExtrusionEntityReference &rhs) { m_extrusion_entity = rhs.m_extrusion_entity; m_flipped = rhs.m_flipped; } + ExtrusionEntityReference(const ExtrusionEntity &extrusion_entity, bool flipped) : + m_extrusion_entity(&extrusion_entity), m_flipped(flipped) {} + ExtrusionEntityReference operator=(const ExtrusionEntityReference &rhs) + { m_extrusion_entity = rhs.m_extrusion_entity; m_flipped = rhs.m_flipped; return *this; } const ExtrusionEntity& extrusion_entity() const { return *m_extrusion_entity; } template diff --git a/src/libslic3r/Geometry/Circle.hpp b/src/libslic3r/Geometry/Circle.hpp index 7b85916f5a..c05263fe08 100644 --- a/src/libslic3r/Geometry/Circle.hpp +++ b/src/libslic3r/Geometry/Circle.hpp @@ -11,7 +11,7 @@ namespace Slic3r { namespace Geometry { // Circumcenter coordinates, Cartesian coordinates // In case the three points are collinear, returns their centroid. template -Eigen::Matrix circle_center(const Derived &a, const Derived2 &bsrc, const Derived3 &csrc, typename typename Derived::Scalar epsilon) +Eigen::Matrix circle_center(const Derived &a, const Derived2 &bsrc, const Derived3 &csrc, typename Derived::Scalar epsilon) { static_assert(Derived ::IsVectorAtCompileTime && int(Derived ::SizeAtCompileTime) == 2, "circle_center(): 1st point is not a 2D vector"); static_assert(Derived2::IsVectorAtCompileTime && int(Derived2::SizeAtCompileTime) == 2, "circle_center(): 2nd point is not a 2D vector"); diff --git a/src/libslic3r/MultiPoint.hpp b/src/libslic3r/MultiPoint.hpp index ece2ce2d90..3c750af752 100644 --- a/src/libslic3r/MultiPoint.hpp +++ b/src/libslic3r/MultiPoint.hpp @@ -19,7 +19,6 @@ inline OutputIterator douglas_peucker(InputIterator begin, InputIterator end, Ou { using InputIteratorCategory = typename std::iterator_traits::iterator_category; static_assert(std::is_base_of_v); - using Point = typename InputIterator::value_type; using Vector = Eigen::Matrix; if (begin != end) { // Supporting in-place reduction and the data type may be generic, thus we are always making a copy of the point value before there is a chance From 5111a3d4cfe88a5cf2588d07aecd239e482ee964 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 17 Jul 2023 15:46:09 +0200 Subject: [PATCH 010/136] Fixed one more double "template template" --- src/libslic3r/Geometry/Circle.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/Geometry/Circle.hpp b/src/libslic3r/Geometry/Circle.hpp index c05263fe08..59d3c3b31b 100644 --- a/src/libslic3r/Geometry/Circle.hpp +++ b/src/libslic3r/Geometry/Circle.hpp @@ -41,7 +41,7 @@ Eigen::Matrix circle_center(co // Circumcenter coordinates, Cartesian coordinates // Returns no value if the three points are collinear. template -std::optional> try_circle_center(const Derived &a, const Derived2 &bsrc, const Derived3 &csrc, typename typename Derived::Scalar epsilon) +std::optional> try_circle_center(const Derived &a, const Derived2 &bsrc, const Derived3 &csrc, typename Derived::Scalar epsilon) { static_assert(Derived ::IsVectorAtCompileTime && int(Derived ::SizeAtCompileTime) == 2, "try_circle_center(): 1st point is not a 2D vector"); static_assert(Derived2::IsVectorAtCompileTime && int(Derived2::SizeAtCompileTime) == 2, "try_circle_center(): 2nd point is not a 2D vector"); From 7eca48b7553a48c1714ec3d8b9f748ea57b687ff Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 17 Jul 2023 16:56:08 +0200 Subject: [PATCH 011/136] ArcWelder: Extended cooling buffer with G2/G3 IJ/R --- src/libslic3r/GCode/CoolingBuffer.cpp | 108 ++++++++++++++++++-------- src/libslic3r/GCode/CoolingBuffer.hpp | 8 +- src/libslic3r/Geometry/ArcWelder.hpp | 28 ++++++- 3 files changed, 108 insertions(+), 36 deletions(-) diff --git a/src/libslic3r/GCode/CoolingBuffer.cpp b/src/libslic3r/GCode/CoolingBuffer.cpp index 9ac4233e00..11974a55f3 100644 --- a/src/libslic3r/GCode/CoolingBuffer.cpp +++ b/src/libslic3r/GCode/CoolingBuffer.cpp @@ -33,36 +33,45 @@ CoolingBuffer::CoolingBuffer(GCodeGenerator &gcodegen) : m_config(gcodegen.confi void CoolingBuffer::reset(const Vec3d &position) { - m_current_pos.assign(5, 0.f); - m_current_pos[0] = float(position.x()); - m_current_pos[1] = float(position.y()); - m_current_pos[2] = float(position.z()); - m_current_pos[4] = float(m_config.travel_speed.value); + assert(m_current_pos.size() == 5); + m_current_pos[AxisIdx::X] = float(position.x()); + m_current_pos[AxisIdx::Y] = float(position.y()); + m_current_pos[AxisIdx::Z] = float(position.z()); + m_current_pos[AxisIdx::E] = 0.f; + m_current_pos[AxisIdx::F] = float(m_config.travel_speed.value); m_fan_speed = -1; } struct CoolingLine { - enum Type { + enum Type : uint32_t { TYPE_SET_TOOL = 1 << 0, TYPE_EXTRUDE_END = 1 << 1, TYPE_BRIDGE_FAN_START = 1 << 2, TYPE_BRIDGE_FAN_END = 1 << 3, TYPE_G0 = 1 << 4, TYPE_G1 = 1 << 5, - TYPE_ADJUSTABLE = 1 << 6, - TYPE_EXTERNAL_PERIMETER = 1 << 7, + // G2 or G3: Arc interpolation + TYPE_G2G3 = 1 << 6, + TYPE_ADJUSTABLE = 1 << 7, + TYPE_EXTERNAL_PERIMETER = 1 << 8, + // Arc interpolation, counter-clockwise. + TYPE_G2G3_CCW = 1 << 9, + // Arc interpolation, arc defined by IJ (offset of arc center from its start position). + TYPE_G2G3_IJ = 1 << 10, + // Arc interpolation, arc defined by R (arc radius, positive - smaller, negative - larger). + TYPE_G2G3_R = 1 << 11, // The line sets a feedrate. - TYPE_HAS_F = 1 << 8, - TYPE_WIPE = 1 << 9, - TYPE_G4 = 1 << 10, - TYPE_G92 = 1 << 11, + TYPE_HAS_F = 1 << 12, + TYPE_WIPE = 1 << 13, + TYPE_G4 = 1 << 14, + TYPE_G92 = 1 << 15, // Would be TYPE_ADJUSTABLE, but the block of G-code lines has zero extrusion length, thus the block // cannot have its speed adjusted. This should not happen (sic!). - TYPE_ADJUSTABLE_EMPTY = 1 << 12, + TYPE_ADJUSTABLE_EMPTY = 1 << 16, // Custom fan speed (introduced for overhang fan speed) - TYPE_SET_FAN_SPEED = 1 << 13, - TYPE_RESET_FAN_SPEED = 1 << 14, + TYPE_SET_FAN_SPEED = 1 << 17, + TYPE_RESET_FAN_SPEED = 1 << 18, }; CoolingLine(unsigned int type, size_t line_start, size_t line_end) : @@ -324,7 +333,7 @@ std::string CoolingBuffer::process_layer(std::string &&gcode, size_t layer_id, b // Parse the layer G-code for the moves, which could be adjusted. // Return the list of parsed lines, bucketed by an extruder. -std::vector CoolingBuffer::parse_layer_gcode(const std::string &gcode, std::vector ¤t_pos) const +std::vector CoolingBuffer::parse_layer_gcode(const std::string &gcode, std::array ¤t_pos) const { std::vector per_extruder_adjustments(m_extruder_ids.size()); std::vector map_extruder_to_per_extruder_adjustment(m_num_extruders, 0); @@ -347,7 +356,7 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: // for a sequence of extrusion moves. size_t active_speed_modifier = size_t(-1); - std::vector new_pos; + std::array new_pos; for (; *line_start != 0; line_start = line_end) { while (*line_end != '\n' && *line_end != 0) @@ -362,12 +371,20 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: line.type = CoolingLine::TYPE_G0; else if (boost::starts_with(sline, "G1 ")) line.type = CoolingLine::TYPE_G1; + else if (boost::starts_with(sline, "G2 ")) + // Arc, clockwise. + line.type = CoolingLine::TYPE_G2G3; + else if (boost::starts_with(sline, "G3 ")) + // Arc, counter-clockwise. + line.type = CoolingLine::TYPE_G2G3 | CoolingLine::TYPE_G2G3_CCW; else if (boost::starts_with(sline, "G92 ")) line.type = CoolingLine::TYPE_G92; if (line.type) { - // G0, G1 or G92 + // G0, G1, G2, G3 or G92 + // Initialize current_pos from new_pos, set IJKR to zero. + std::fill(std::copy(std::begin(current_pos), std::end(current_pos), std::begin(new_pos)), + std::end(new_pos), 0.f); // Parse the G-code line. - new_pos = current_pos; for (auto c = sline.begin() + 3;;) { // Skip whitespaces. for (; c != sline.end() && (*c == ' ' || *c == '\t'); ++ c); @@ -376,21 +393,31 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: // Parse the axis. size_t axis = (*c >= 'X' && *c <= 'Z') ? (*c - 'X') : - (*c == extrusion_axis) ? 3 : (*c == 'F') ? 4 : size_t(-1); + (*c == extrusion_axis) ? AxisIdx::E : (*c == 'F') ? AxisIdx::F : + (*c >= 'I' && *c <= 'K') ? int(AxisIdx::I) + (*c - 'I') : + (*c == 'R') ? AxisIdx::R : size_t(-1); if (axis != size_t(-1)) { //auto [pend, ec] = fast_float::from_chars(&*(++ c), sline.data() + sline.size(), new_pos[axis]); - if (axis == 4) { + if (axis == AxisIdx::F) { // Convert mm/min to mm/sec. - new_pos[4] /= 60.f; + new_pos[AxisIdx::F] /= 60.f; if ((line.type & CoolingLine::TYPE_G92) == 0) // This is G0 or G1 line and it sets the feedrate. This mark is used for reducing the duplicate F calls. line.type |= CoolingLine::TYPE_HAS_F; - } + } else if (axis >= AxisIdx::I && axis <= AxisIdx::J) + line.type |= CoolingLine::TYPE_G2G3_IJ; + else if (axis == AxisIdx::R) + line.type |= CoolingLine::TYPE_G2G3_R; } // Skip this word. for (; c != sline.end() && *c != ' ' && *c != '\t'; ++ c); } + // If G2 or G3, then either center of the arc or radius has to be defined. + assert(! (line.type & CoolingLine::TYPE_G2G3) || + (line.type & (CoolingLine::TYPE_G2G3_IJ | CoolingLine::TYPE_G2G3_R))); + // Arc is defined either by IJ or by R, not by both. + assert(! ((line.type & CoolingLine::TYPE_G2G3_IJ) && (line.type & CoolingLine::TYPE_G2G3_R))); bool external_perimeter = boost::contains(sline, ";_EXTERNAL_PERIMETER"); bool wipe = boost::contains(sline, ";_WIPE"); if (external_perimeter) @@ -402,23 +429,40 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: active_speed_modifier = adjustment->lines.size(); } if ((line.type & CoolingLine::TYPE_G92) == 0) { - // G0 or G1. Calculate the duration. + // G0, G1, G2, G3. Calculate the duration. + assert((line.type & CoolingLine::TYPE_G0) + (line.type & CoolingLine::TYPE_G1) + (line.type & CoolingLine::TYPE_G2G3) == 1); if (m_config.use_relative_e_distances.value) // Reset extruder accumulator. - current_pos[3] = 0.f; + current_pos[AxisIdx::E] = 0.f; float dif[4]; for (size_t i = 0; i < 4; ++ i) dif[i] = new_pos[i] - current_pos[i]; - float dxy2 = dif[0] * dif[0] + dif[1] * dif[1]; - float dxyz2 = dxy2 + dif[2] * dif[2]; + float dxy2; + if (line.type & CoolingLine::TYPE_G2G3) { + // Measure arc length. + if (line.type & CoolingLine::TYPE_G2G3_IJ) { + dxy2 = sqr(Geometry::ArcWelder::arc_length( + Vec2d(current_pos[AxisIdx::X], current_pos[AxisIdx::Y]), + Vec2d(new_pos[AxisIdx::X], new_pos[AxisIdx::Y]), + Vec2d(current_pos[AxisIdx::X] + current_pos[AxisIdx::I], current_pos[AxisIdx::Y] + current_pos[AxisIdx::J]), + line.type & CoolingLine::TYPE_G2G3_CCW)); + } else if (line.type & CoolingLine::TYPE_G2G3_R) { + dxy2 = sqr(Geometry::ArcWelder::arc_length( + Vec2d(current_pos[AxisIdx::X], current_pos[AxisIdx::Y]), + Vec2d(new_pos[AxisIdx::X], new_pos[AxisIdx::Y]), + double(new_pos[AxisIdx::R]))); + } + } else + dxy2 = sqr(dif[AxisIdx::X]) + sqr(dif[AxisIdx::Y]); + float dxyz2 = dxy2 + sqr(dif[AxisIdx::Z]); if (dxyz2 > 0.f) { // Movement in xyz, calculate time from the xyz Euclidian distance. line.length = sqrt(dxyz2); - } else if (std::abs(dif[3]) > 0.f) { + } else if (std::abs(dif[AxisIdx::E]) > 0.f) { // Movement in the extruder axis. - line.length = std::abs(dif[3]); + line.length = std::abs(dif[AxisIdx::E]); } - line.feedrate = new_pos[4]; + line.feedrate = new_pos[AxisIdx::F]; assert((line.type & CoolingLine::TYPE_ADJUSTABLE) == 0 || line.feedrate > 0.f); if (line.length > 0) { assert(line.feedrate > 0); @@ -430,7 +474,7 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: assert(adjustment->min_print_speed >= 0); line.time_max = (adjustment->min_print_speed == 0.f) ? FLT_MAX : std::max(line.time, line.length / adjustment->min_print_speed); } - if (active_speed_modifier < adjustment->lines.size() && (line.type & CoolingLine::TYPE_G1)) { + if (active_speed_modifier < adjustment->lines.size() && (line.type & (CoolingLine::TYPE_G1 | CoolingLine::TYPE_G2G3))) { // Inside the ";_EXTRUDE_SET_SPEED" blocks, there must not be a G1 Fxx entry. assert((line.type & CoolingLine::TYPE_HAS_F) == 0); CoolingLine &sm = adjustment->lines[active_speed_modifier]; @@ -447,7 +491,7 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: line.type = 0; } } - current_pos = std::move(new_pos); + std::copy(std::begin(new_pos), std::begin(new_pos) + 5, std::begin(current_pos)); } else if (boost::starts_with(sline, ";_EXTRUDE_END")) { // Closing a block of non-zero length extrusion moves. line.type = CoolingLine::TYPE_EXTRUDE_END; diff --git a/src/libslic3r/GCode/CoolingBuffer.hpp b/src/libslic3r/GCode/CoolingBuffer.hpp index b01a8ab985..9950a5f7ef 100644 --- a/src/libslic3r/GCode/CoolingBuffer.hpp +++ b/src/libslic3r/GCode/CoolingBuffer.hpp @@ -31,7 +31,7 @@ public: private: CoolingBuffer& operator=(const CoolingBuffer&) = delete; - std::vector parse_layer_gcode(const std::string &gcode, std::vector ¤t_pos) const; + std::vector parse_layer_gcode(const std::string &gcode, std::array ¤t_pos) const; float calculate_layer_slowdown(std::vector &per_extruder_adjustments); // Apply slow down over G-code lines stored in per_extruder_adjustments, enable fan if needed. // Returns the adjusted G-code. @@ -40,9 +40,11 @@ private: // G-code snippet cached for the support layers preceding an object layer. std::string m_gcode; // Internal data. - // X,Y,Z,E,F std::vector m_axis; - std::vector m_current_pos; + enum AxisIdx : int { + X = 0, Y, Z, E, F, I, J, K, R, Count + }; + std::array m_current_pos; // Current known fan speed or -1 if not known yet. int m_fan_speed; // Cached from GCodeWriter. diff --git a/src/libslic3r/Geometry/ArcWelder.hpp b/src/libslic3r/Geometry/ArcWelder.hpp index dab73b532a..5bf806c702 100644 --- a/src/libslic3r/Geometry/ArcWelder.hpp +++ b/src/libslic3r/Geometry/ArcWelder.hpp @@ -15,7 +15,7 @@ template inline Eigen::Matrix arc_center( const Eigen::MatrixBase &start_pos, const Eigen::MatrixBase &end_pos, - const typename Float radius, + const Float radius, const bool is_ccw) { static_assert(Derived::IsVectorAtCompileTime && int(Derived::SizeAtCompileTime) == 2, "arc_center(): first parameter is not a 2D vector"); @@ -73,6 +73,32 @@ inline typename Derived::Scalar arc_length( return arc_angle(start_pos, end_pos, radius) * std::abs(radius); } +// Calculate positive length of an arc given two points, center and orientation. +template +inline typename Derived::Scalar arc_length( + const Eigen::MatrixBase &start_pos, + const Eigen::MatrixBase &end_pos, + const Eigen::MatrixBase ¢er_pos, + const bool ccw) +{ + static_assert(Derived::IsVectorAtCompileTime && int(Derived::SizeAtCompileTime) == 2, "arc_length(): first parameter is not a 2D vector"); + static_assert(Derived2::IsVectorAtCompileTime && int(Derived2::SizeAtCompileTime) == 2, "arc_length(): second parameter is not a 2D vector"); + static_assert(Derived3::IsVectorAtCompileTime && int(Derived2::SizeAtCompileTime) == 2, "arc_length(): third parameter is not a 2D vector"); + static_assert(std::is_same::value && + std::is_same::value, "arc_length(): All third points must be of the same type."); + using Float = typename Derived::Scalar; + auto vstart = start_pos - center_pos; + auto vend = end_pos - center_pos; + Float radius = vstart.norm(); + Float angle = atan2(double(cross2(vstart, vend)), double(vstart.dot(vend))); + if (! ccw) + angle *= Float(-1.); + if (angle < 0) + angle += Float(2. * M_PI); + assert(angle >= Float(0.) && angle < Float(2. * M_PI + EPSILON)); + return angle * radius; +} + // Test whether a point is inside a wedge of an arc. template inline bool inside_arc_wedge_vectors( From f9f49d2bb0d67c235f5ab7f72f7e9a6caf2005f6 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 17 Jul 2023 17:09:02 +0200 Subject: [PATCH 012/136] ArcWelder: Fixed some cooling buffer errors --- src/libslic3r/GCode/CoolingBuffer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/GCode/CoolingBuffer.cpp b/src/libslic3r/GCode/CoolingBuffer.cpp index 11974a55f3..178864f784 100644 --- a/src/libslic3r/GCode/CoolingBuffer.cpp +++ b/src/libslic3r/GCode/CoolingBuffer.cpp @@ -430,7 +430,7 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: } if ((line.type & CoolingLine::TYPE_G92) == 0) { // G0, G1, G2, G3. Calculate the duration. - assert((line.type & CoolingLine::TYPE_G0) + (line.type & CoolingLine::TYPE_G1) + (line.type & CoolingLine::TYPE_G2G3) == 1); + assert((line.type & CoolingLine::TYPE_G0) != 0 + (line.type & CoolingLine::TYPE_G1) != 0 + (line.type & CoolingLine::TYPE_G2G3) != 0 == 1); if (m_config.use_relative_e_distances.value) // Reset extruder accumulator. current_pos[AxisIdx::E] = 0.f; @@ -444,7 +444,7 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: dxy2 = sqr(Geometry::ArcWelder::arc_length( Vec2d(current_pos[AxisIdx::X], current_pos[AxisIdx::Y]), Vec2d(new_pos[AxisIdx::X], new_pos[AxisIdx::Y]), - Vec2d(current_pos[AxisIdx::X] + current_pos[AxisIdx::I], current_pos[AxisIdx::Y] + current_pos[AxisIdx::J]), + Vec2d(current_pos[AxisIdx::X] + new_pos[AxisIdx::I], current_pos[AxisIdx::Y] + new_pos[AxisIdx::J]), line.type & CoolingLine::TYPE_G2G3_CCW)); } else if (line.type & CoolingLine::TYPE_G2G3_R) { dxy2 = sqr(Geometry::ArcWelder::arc_length( From b4b9f2fb2b74541cf6e078f84551d638884b0218 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 17 Jul 2023 17:17:04 +0200 Subject: [PATCH 013/136] ArcWelder: Fixed some compiler warnings --- src/libslic3r/GCode/CoolingBuffer.cpp | 3 ++- src/libslic3r/Geometry/ArcWelder.cpp | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/GCode/CoolingBuffer.cpp b/src/libslic3r/GCode/CoolingBuffer.cpp index 178864f784..53eeb716e5 100644 --- a/src/libslic3r/GCode/CoolingBuffer.cpp +++ b/src/libslic3r/GCode/CoolingBuffer.cpp @@ -451,7 +451,8 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: Vec2d(current_pos[AxisIdx::X], current_pos[AxisIdx::Y]), Vec2d(new_pos[AxisIdx::X], new_pos[AxisIdx::Y]), double(new_pos[AxisIdx::R]))); - } + } else + dxy2 = 0; } else dxy2 = sqr(dif[AxisIdx::X]) + sqr(dif[AxisIdx::Y]); float dxyz2 = dxy2 + sqr(dif[AxisIdx::Z]); diff --git a/src/libslic3r/Geometry/ArcWelder.cpp b/src/libslic3r/Geometry/ArcWelder.cpp index 3009305058..25426d68fc 100644 --- a/src/libslic3r/Geometry/ArcWelder.cpp +++ b/src/libslic3r/Geometry/ArcWelder.cpp @@ -167,12 +167,12 @@ static std::optional try_create_circle(const Points::const_iterator begi return circle; #endif // Find the circle with the least deviation, if one exists. - double least_deviation; + double least_deviation = std::numeric_limits::max(); double current_deviation; for (auto it = std::next(begin); std::next(it) != end; ++ it) if (std::optional circle = try_create_circle(*begin, *it, *std::prev(end), max_radius); circle && get_deviation_sum_squared(*circle, begin, end, tolerance, current_deviation)) { - if (! out || current_deviation < least_deviation) { + if (current_deviation < least_deviation) { out = circle; least_deviation = current_deviation; } @@ -631,10 +631,10 @@ std::pair split_at(const Path &path, const PathSegmentProjection &pr auto vstart = (start.point - proj.center).cast(); auto vend = (end.point - proj.center).cast(); auto vproj = (proj.point - proj.center).cast(); - if (bool first_arc_minor = (cross2(vstart, vproj) > 0) == end.ccw()) + if ((cross2(vstart, vproj) > 0) == end.ccw()) // Make the radius of a minor arc positive. out.first.back().radius *= -1.f; - if (bool second_arc_minor = (cross2(vproj, vend) > 0) == end.ccw()) + if ((cross2(vproj, vend) > 0) == end.ccw()) // Make the radius of a minor arc positive. out.second[1].radius *= -1.f; } From 5d4bee74bc16256a82bea39fc0ff9a589683f22e Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 17 Jul 2023 17:58:10 +0200 Subject: [PATCH 014/136] ArcWelder: Fixed includes in Wipe.hpp --- src/libslic3r/GCode/Wipe.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/GCode/Wipe.hpp b/src/libslic3r/GCode/Wipe.hpp index a088aa3904..f02a77fa7f 100644 --- a/src/libslic3r/GCode/Wipe.hpp +++ b/src/libslic3r/GCode/Wipe.hpp @@ -1,8 +1,9 @@ #ifndef slic3r_GCode_Wipe_hpp_ #define slic3r_GCode_Wipe_hpp_ -#include "Geometry/ArcWelder.hpp" #include "SmoothPath.hpp" + +#include "../Geometry/ArcWelder.hpp" #include "../Point.hpp" #include "../PrintConfig.hpp" From 3d439c617c649c4b927c15b5495efa5b1c518dae Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 17 Jul 2023 19:12:10 +0200 Subject: [PATCH 015/136] ArcWelder: Fixed warnings in unit tests. --- tests/libslic3r/test_arc_welder.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/libslic3r/test_arc_welder.cpp b/tests/libslic3r/test_arc_welder.cpp index 39b06e7f1f..2686216954 100644 --- a/tests/libslic3r/test_arc_welder.cpp +++ b/tests/libslic3r/test_arc_welder.cpp @@ -135,7 +135,7 @@ TEST_CASE("arc fitting", "[ArcWelder]") { const Point center = Point::new_scale(1., 1.); const float radius = scaled(1.); const float resolution = scaled(0.002); - auto test = [center, resolution, radius](const Point &p1, const Point &p2, const float r, const bool ccw) { + auto test = [center, resolution](const Point &p1, const Point &p2, const float r, const bool ccw) { Points pts = ArcWelder::arc_discretize(p1, p2, r, ccw, resolution); ArcWelder::Path path = ArcWelder::fit_path(pts, resolution + SCALED_EPSILON, ArcWelder::default_scaled_resolution); REQUIRE(path.size() == 2); @@ -167,7 +167,7 @@ TEST_CASE("arc fitting", "[ArcWelder]") { const Point center2 = Point::new_scale(1., 3.); const float radius = scaled(1.); const float resolution = scaled(0.002); - auto test = [center1, center2, resolution, radius](const Point &p1, const Point &p2, const Point &p3, const float r, const bool ccw) { + auto test = [center1, center2, resolution](const Point &p1, const Point &p2, const Point &p3, const float r, const bool ccw) { Points pts = ArcWelder::arc_discretize(p1, p2, r, ccw, resolution); { Points pts2 = ArcWelder::arc_discretize(p2, p3, - r, ! ccw, resolution); From bde6fb25287e5264e97de8fd98a4b951813383c9 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 18 Jul 2023 11:31:23 +0200 Subject: [PATCH 016/136] ArcWelder: Smooth interpolation of skirt & brim Lower resolution (higher decimation rate) for sparse infill: 3x support: 4x skirt+brim: 4x --- src/libslic3r/ExtrusionRole.hpp | 4 + src/libslic3r/GCode.cpp | 134 ++++++++++++++++++++------- src/libslic3r/GCode.hpp | 19 ++-- src/libslic3r/GCode/SmoothPath.cpp | 25 +++-- src/libslic3r/GCode/SmoothPath.hpp | 19 +++- src/libslic3r/Geometry/ArcWelder.cpp | 29 ++++-- 6 files changed, 172 insertions(+), 58 deletions(-) diff --git a/src/libslic3r/ExtrusionRole.hpp b/src/libslic3r/ExtrusionRole.hpp index 986c139a24..140468f05a 100644 --- a/src/libslic3r/ExtrusionRole.hpp +++ b/src/libslic3r/ExtrusionRole.hpp @@ -82,6 +82,7 @@ struct ExtrusionRole : public ExtrusionRoleModifiers bool is_external_perimeter() const { return this->is_perimeter() && this->is_external(); } bool is_infill() const { return this->ExtrusionRoleModifiers::has(ExtrusionRoleModifier::Infill); } bool is_solid_infill() const { return this->is_infill() && this->ExtrusionRoleModifiers::has(ExtrusionRoleModifier::Solid); } + bool is_sparse_infill() const { return this->is_infill() && ! this->ExtrusionRoleModifiers::has(ExtrusionRoleModifier::Solid); } bool is_external() const { return this->ExtrusionRoleModifiers::has(ExtrusionRoleModifier::External); } bool is_bridge() const { return this->ExtrusionRoleModifiers::has(ExtrusionRoleModifier::Bridge); } @@ -89,6 +90,9 @@ struct ExtrusionRole : public ExtrusionRoleModifiers bool is_support_base() const { return this->is_support() && ! this->is_external(); } bool is_support_interface() const { return this->is_support() && this->is_external(); } bool is_mixed() const { return this->ExtrusionRoleModifiers::has(ExtrusionRoleModifier::Mixed); } + + // Brim is currently marked as skirt. + bool is_skirt() const { return this->ExtrusionRoleModifiers::has(ExtrusionRoleModifier::Skirt); } }; // Special flags describing loop diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 07c9849127..168dad84ce 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -798,6 +798,20 @@ std::vector sort_object_instances_by_model_order(const Pri return instances; } +static inline GCode::SmoothPathCache smooth_path_interpolate_global(const Print& print) +{ + const GCode::SmoothPathCache::InterpolationParameters interpolation_params { + scaled(print.config().gcode_resolution.value), + print.config().arc_fitting != ArcFittingType::Disabled && ! print.config().spiral_vase ? + Geometry::ArcWelder::default_arc_length_percent_tolerance : + 0 + }; + GCode::SmoothPathCache out; + out.interpolate_add(print.skirt(), interpolation_params); + out.interpolate_add(print.brim(), interpolation_params); + return out; +} + void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGeneratorCallback thumbnail_cb) { // modifies m_silent_time_estimator_enabled @@ -1058,6 +1072,8 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail file.write(this->set_extruder(initial_extruder_id, 0.)); } + GCode::SmoothPathCache smooth_path_cache_global = smooth_path_interpolate_global(print); + // Do all objects for each layer. if (print.config().complete_objects.value) { size_t finished_objects = 0; @@ -1102,7 +1118,9 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail // Process all layers of a single object instance (sequential mode) with a parallel pipeline: // Generate G-code, run the filters (vase mode, cooling buffer), run the G-code analyser // and export G-code into file. - this->process_layers(print, tool_ordering, collect_layers_to_print(object), *print_object_instance_sequential_active - object.instances().data(), file); + this->process_layers(print, tool_ordering, collect_layers_to_print(object), + *print_object_instance_sequential_active - object.instances().data(), + smooth_path_cache_global, file); ++ finished_objects; // Flag indicating whether the nozzle temperature changes from 1st to 2nd layer were performed. // Reset it when starting another object from 1st layer. @@ -1158,7 +1176,8 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail // Process all layers of all objects (non-sequential mode) with a parallel pipeline: // Generate G-code, run the filters (vase mode, cooling buffer), run the G-code analyser // and export G-code into file. - this->process_layers(print, tool_ordering, print_object_instances_ordering, layers_to_print, file); + this->process_layers(print, tool_ordering, print_object_instances_ordering, layers_to_print, + smooth_path_cache_global, file); if (m_wipe_tower) // Purge the extruder, pull out the active filament. file.write(m_wipe_tower->finalize(*this)); @@ -1258,6 +1277,7 @@ void GCodeGenerator::process_layers( const ToolOrdering &tool_ordering, const std::vector &print_object_instances_ordering, const std::vector> &layers_to_print, + const GCode::SmoothPathCache &smooth_path_cache_global, GCodeOutputStream &output_stream) { size_t layer_to_print_idx = 0; @@ -1268,7 +1288,7 @@ void GCodeGenerator::process_layers( 0 }; const auto smooth_path_interpolator = tbb::make_filter>(slic3r_tbb_filtermode::serial_in_order, - [this, &print, &layers_to_print, &layer_to_print_idx, interpolation_params](tbb::flow_control &fc) -> std::pair { + [this, &print, &layers_to_print, &layer_to_print_idx, &interpolation_params](tbb::flow_control &fc) -> std::pair { if (layer_to_print_idx >= layers_to_print.size()) { if ((!m_pressure_equalizer && layer_to_print_idx == layers_to_print.size()) || (m_pressure_equalizer && layer_to_print_idx == (layers_to_print.size() + 1))) { fc.stop(); @@ -1288,7 +1308,8 @@ void GCodeGenerator::process_layers( } }); const auto generator = tbb::make_filter, LayerResult>(slic3r_tbb_filtermode::serial_in_order, - [this, &print, &tool_ordering, &print_object_instances_ordering, &layers_to_print](std::pair in) -> LayerResult { + [this, &print, &tool_ordering, &print_object_instances_ordering, &layers_to_print, &smooth_path_cache_global]( + std::pair in) -> LayerResult { size_t layer_to_print_idx = in.first; if (layer_to_print_idx == layers_to_print.size()) { // Pressure equalizer need insert empty input. Because it returns one layer back. @@ -1300,7 +1321,9 @@ void GCodeGenerator::process_layers( if (m_wipe_tower && layer_tools.has_wipe_tower) m_wipe_tower->next_layer(); print.throw_if_canceled(); - return this->process_layer(print, layer.second, layer_tools, &in.second, &layer == &layers_to_print.back(), &print_object_instances_ordering, size_t(-1)); + return this->process_layer(print, layer.second, layer_tools, + GCode::SmoothPathCaches{ smooth_path_cache_global, in.second }, + &layer == &layers_to_print.back(), &print_object_instances_ordering, size_t(-1)); } }); // The pipeline is variable: The vase mode filter is optional. @@ -1357,6 +1380,7 @@ void GCodeGenerator::process_layers( const ToolOrdering &tool_ordering, ObjectsLayerToPrint layers_to_print, const size_t single_object_idx, + const GCode::SmoothPathCache &smooth_path_cache_global, GCodeOutputStream &output_stream) { size_t layer_to_print_idx = 0; @@ -1386,7 +1410,7 @@ void GCodeGenerator::process_layers( } }); const auto generator = tbb::make_filter, LayerResult>(slic3r_tbb_filtermode::serial_in_order, - [this, &print, &tool_ordering, &layers_to_print, single_object_idx](std::pair in) -> LayerResult { + [this, &print, &tool_ordering, &layers_to_print, &smooth_path_cache_global, single_object_idx](std::pair in) -> LayerResult { size_t layer_to_print_idx = in.first; if (layer_to_print_idx == layers_to_print.size()) { // Pressure equalizer need insert empty input. Because it returns one layer back. @@ -1395,7 +1419,9 @@ void GCodeGenerator::process_layers( } else { ObjectLayerToPrint &layer = layers_to_print[layer_to_print_idx]; print.throw_if_canceled(); - return this->process_layer(print, { std::move(layer) }, tool_ordering.tools_for_layer(layer.print_z()), &in.second, &layer == &layers_to_print.back(), nullptr, single_object_idx); + return this->process_layer(print, { std::move(layer) }, tool_ordering.tools_for_layer(layer.print_z()), + GCode::SmoothPathCaches{ smooth_path_cache_global, in.second }, + &layer == &layers_to_print.back(), nullptr, single_object_idx); } }); // The pipeline is variable: The vase mode filter is optional. @@ -1877,7 +1903,7 @@ LayerResult GCodeGenerator::process_layer( // Set of object & print layers of the same PrintObject and with the same print_z. const ObjectsLayerToPrint &layers, const LayerTools &layer_tools, - const GCode::SmoothPathCache *smooth_path_cache, + const GCode::SmoothPathCaches &smooth_path_caches, const bool last_layer, // Pairs of PrintObject index and its instance index. const std::vector *ordering, @@ -2044,13 +2070,11 @@ LayerResult GCodeGenerator::process_layer( double mm3_per_mm = layer_skirt_flow.mm3_per_mm(); for (size_t i = loops.first; i < loops.second; ++i) { // Adjust flow according to this layer's layer height. - const ExtrusionLoop src = *dynamic_cast(print.skirt().entities[i]); - ExtrusionLoop loop(src.loop_role()); - loop.paths.reserve(src.paths.size()); - for (const ExtrusionPath &path : src.paths) - loop.paths.emplace_back(path.polyline, ExtrusionAttributes{ path.role(), ExtrusionFlow{ mm3_per_mm, path.width(), layer_skirt_flow.height() } }); //FIXME using the support_material_speed of the 1st object printed. - gcode += this->extrude_loop(loop, smooth_path_cache, "skirt"sv, m_config.support_material_speed.value); + gcode += this->extrude_skirt(dynamic_cast(*print.skirt().entities[i]), + // Override of skirt extrusion parameters. extrude_skirt() will fill in the extrusion width. + ExtrusionFlow{ mm3_per_mm, 0., layer_skirt_flow.height() }, + smooth_path_caches.global(), "skirt"sv, m_config.support_material_speed.value); } m_avoid_crossing_perimeters.use_external_mp(false); // Allow a straight travel move to the first object point if this is the first layer (but don't in next layers). @@ -2063,7 +2087,7 @@ LayerResult GCodeGenerator::process_layer( this->set_origin(0., 0.); m_avoid_crossing_perimeters.use_external_mp(); for (const ExtrusionEntity *ee : print.brim().entities) - gcode += this->extrude_entity({ *ee, false }, smooth_path_cache, "brim"sv, m_config.support_material_speed.value); + gcode += this->extrude_entity({ *ee, false }, smooth_path_caches.global(), "brim"sv, m_config.support_material_speed.value); m_brim_done = true; m_avoid_crossing_perimeters.use_external_mp(false); // Allow a straight travel move to the first object point. @@ -2080,7 +2104,7 @@ LayerResult GCodeGenerator::process_layer( for (const InstanceToPrint &instance : instances_to_print) this->process_layer_single_object( gcode, extruder_id, instance, - layers[instance.object_layer_to_print_id], layer_tools, smooth_path_cache, + layers[instance.object_layer_to_print_id], layer_tools, smooth_path_caches.layer_local(), is_anything_overridden, true /* print_wipe_extrusions */); if (gcode_size_old < gcode.size()) gcode+="; PURGING FINISHED\n"; @@ -2089,7 +2113,7 @@ LayerResult GCodeGenerator::process_layer( for (const InstanceToPrint &instance : instances_to_print) this->process_layer_single_object( gcode, extruder_id, instance, - layers[instance.object_layer_to_print_id], layer_tools, smooth_path_cache, + layers[instance.object_layer_to_print_id], layer_tools, smooth_path_caches.layer_local(), is_anything_overridden, false /* print_wipe_extrusions */); } @@ -2119,7 +2143,7 @@ void GCodeGenerator::process_layer_single_object( // Container for extruder overrides (when wiping into object or infill). const LayerTools &layer_tools, // Optional smooth path interpolating extrusion polylines. - const GCode::SmoothPathCache *smooth_path_cache, + const GCode::SmoothPathCache &smooth_path_cache, // Is any extrusion possibly marked as wiping extrusion? const bool is_anything_overridden, // Round 1 (wiping into object or infill) or round 2 (normal extrusions). @@ -2413,7 +2437,19 @@ std::string GCodeGenerator::change_layer(coordf_t print_z) return gcode; } -std::string GCodeGenerator::extrude_loop(const ExtrusionLoop &loop_src, const GCode::SmoothPathCache *smooth_path_cache, const std::string_view description, double speed) +#ifndef NDEBUG +static inline bool validate_smooth_path(const GCode::SmoothPath &smooth_path, bool loop) +{ + for (auto it = std::next(smooth_path.begin()); it != smooth_path.end(); ++ it) { + assert(it->path.size() >= 2); + assert(std::prev(it)->path.back().point == it->path.front().point); + } + assert(! loop || smooth_path.front().path.front().point == smooth_path.back().path.back().point); + return true; +} +#endif //NDEBUG + +std::string GCodeGenerator::extrude_loop(const ExtrusionLoop &loop_src, const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed) { // Extrude all loops CCW. bool is_hole = loop_src.is_clockwise(); @@ -2424,7 +2460,7 @@ std::string GCodeGenerator::extrude_loop(const ExtrusionLoop &loop_src, const GC } // Because the G-code export has 1um resolution, don't generate segments shorter than 1.5 microns, // thus empty path segments will not be produced by G-code export. - GCode::SmoothPath smooth_path = smooth_path_cache->resolve_or_fit_split_with_seam( + GCode::SmoothPath smooth_path = smooth_path_cache.resolve_or_fit_split_with_seam( loop_src, is_hole, m_scaled_resolution, seam_point, scaled(0.0015)); // Clip the path to avoid the extruder to get exactly on the first point of the loop; @@ -2436,13 +2472,7 @@ std::string GCodeGenerator::extrude_loop(const ExtrusionLoop &loop_src, const GC if (smooth_path.empty()) return {}; -#ifndef NDEBUG - for (auto it = std::next(smooth_path.begin()); it != smooth_path.end(); ++ it) { - assert(it->path.size() >= 2); - assert(std::prev(it)->path.back().point == it->path.front().point); - } - assert(m_enable_loop_clipping || smooth_path.front().path.front().point == smooth_path.back().path.back().point); -#endif //NDEBUG + assert(validate_smooth_path(smooth_path, ! m_enable_loop_clipping)); // Apply the small perimeter speed. if (loop_src.paths.front().role().is_perimeter() && loop_src.length() <= SMALL_PERIMETER_LENGTH && speed == -1) @@ -2470,7 +2500,45 @@ std::string GCodeGenerator::extrude_loop(const ExtrusionLoop &loop_src, const GC return gcode; } -std::string GCodeGenerator::extrude_multi_path(const ExtrusionMultiPath &multipath, bool reverse, const GCode::SmoothPathCache *smooth_path_cache, const std::string_view description, double speed) +std::string GCodeGenerator::extrude_skirt( + const ExtrusionLoop &loop_src, const ExtrusionFlow &extrusion_flow_override, + const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed) +{ + assert(loop_src.is_counter_clockwise()); + GCode::SmoothPath smooth_path = smooth_path_cache.resolve_or_fit_split_with_seam( + loop_src, false, m_scaled_resolution, this->last_pos(), scaled(0.0015)); + + // Clip the path to avoid the extruder to get exactly on the first point of the loop; + // if polyline was shorter than the clipping distance we'd get a null polyline, so + // we discard it in that case. + if (m_enable_loop_clipping) + clip_end(smooth_path, scale_(EXTRUDER_CONFIG(nozzle_diameter)) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER); + + if (smooth_path.empty()) + return {}; + + assert(validate_smooth_path(smooth_path, ! m_enable_loop_clipping)); + + // Extrude along the smooth path. + std::string gcode; + for (GCode::SmoothPathElement &el : smooth_path) { + // Override extrusion parameters. + el.path_attributes.mm3_per_mm = extrusion_flow_override.mm3_per_mm; + el.path_attributes.height = extrusion_flow_override.height; + gcode += this->_extrude(el.path_attributes, el.path, description, speed); + } + + // reset acceleration + gcode += m_writer.set_print_acceleration(fast_round_up(m_config.default_acceleration.value)); + + if (m_wipe.enabled()) + // Wipe will hide the seam. + m_wipe.set_path(std::move(smooth_path), false); + + return gcode; +} + +std::string GCodeGenerator::extrude_multi_path(const ExtrusionMultiPath &multipath, bool reverse, const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed) { #ifndef NDEBUG for (auto it = std::next(multipath.paths.begin()); it != multipath.paths.end(); ++ it) { @@ -2478,7 +2546,7 @@ std::string GCodeGenerator::extrude_multi_path(const ExtrusionMultiPath &multipa assert(std::prev(it)->polyline.last_point() == it->polyline.first_point()); } #endif // NDEBUG - GCode::SmoothPath smooth_path = smooth_path_cache->resolve_or_fit(multipath, reverse, m_scaled_resolution); + GCode::SmoothPath smooth_path = smooth_path_cache.resolve_or_fit(multipath, reverse, m_scaled_resolution); // extrude along the path std::string gcode; @@ -2490,7 +2558,7 @@ std::string GCodeGenerator::extrude_multi_path(const ExtrusionMultiPath &multipa return gcode; } -std::string GCodeGenerator::extrude_entity(const ExtrusionEntityReference &entity, const GCode::SmoothPathCache *smooth_path_cache, const std::string_view description, double speed) +std::string GCodeGenerator::extrude_entity(const ExtrusionEntityReference &entity, const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed) { if (const ExtrusionPath *path = dynamic_cast(&entity.extrusion_entity())) return this->extrude_path(*path, entity.flipped(), smooth_path_cache, description, speed); @@ -2503,9 +2571,9 @@ std::string GCodeGenerator::extrude_entity(const ExtrusionEntityReference &entit return {}; } -std::string GCodeGenerator::extrude_path(const ExtrusionPath &path, bool reverse, const GCode::SmoothPathCache *smooth_path_cache, std::string_view description, double speed) +std::string GCodeGenerator::extrude_path(const ExtrusionPath &path, bool reverse, const GCode::SmoothPathCache &smooth_path_cache, std::string_view description, double speed) { - Geometry::ArcWelder::Path smooth_path = smooth_path_cache->resolve_or_fit(path, reverse, m_scaled_resolution); + Geometry::ArcWelder::Path smooth_path = smooth_path_cache.resolve_or_fit(path, reverse, m_scaled_resolution); std::string gcode = this->_extrude(path.attributes(), smooth_path, description, speed); Geometry::ArcWelder::reverse(smooth_path); m_wipe.set_path(std::move(smooth_path)); @@ -2514,7 +2582,7 @@ std::string GCodeGenerator::extrude_path(const ExtrusionPath &path, bool reverse return gcode; } -std::string GCodeGenerator::extrude_support(const ExtrusionEntityReferences &support_fills, const GCode::SmoothPathCache *smooth_path_cache) +std::string GCodeGenerator::extrude_support(const ExtrusionEntityReferences &support_fills, const GCode::SmoothPathCache &smooth_path_cache) { static constexpr const auto support_label = "support material"sv; static constexpr const auto support_interface_label = "support material interface"sv; diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 42d6996e6a..73e1a793fa 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -202,7 +202,7 @@ private: // Set of object & print layers of the same PrintObject and with the same print_z. const ObjectsLayerToPrint &layers, const LayerTools &layer_tools, - const GCode::SmoothPathCache *smooth_path_cache, + const GCode::SmoothPathCaches &smooth_path_caches, const bool last_layer, // Pairs of PrintObject index and its instance index. const std::vector *ordering, @@ -217,6 +217,7 @@ private: const ToolOrdering &tool_ordering, const std::vector &print_object_instances_ordering, const std::vector> &layers_to_print, + const GCode::SmoothPathCache &smooth_path_cache_global, GCodeOutputStream &output_stream); // Process all layers of a single object instance (sequential mode) with a parallel pipeline: // Generate G-code, run the filters (vase mode, cooling buffer), run the G-code analyser @@ -226,6 +227,7 @@ private: const ToolOrdering &tool_ordering, ObjectsLayerToPrint layers_to_print, const size_t single_object_idx, + const GCode::SmoothPathCache &smooth_path_cache_global, GCodeOutputStream &output_stream); void set_last_pos(const Point &pos) { m_last_pos = pos; m_last_pos_defined = true; } @@ -233,10 +235,13 @@ private: void set_extruders(const std::vector &extruder_ids); std::string preamble(); std::string change_layer(coordf_t print_z); - std::string extrude_entity(const ExtrusionEntityReference &entity, const GCode::SmoothPathCache *smooth_path_cache, const std::string_view description, double speed = -1.); - std::string extrude_loop(const ExtrusionLoop &loop, const GCode::SmoothPathCache *smooth_path_cache, const std::string_view description, double speed = -1.); - std::string extrude_multi_path(const ExtrusionMultiPath &multipath, bool reverse, const GCode::SmoothPathCache *smooth_path_cache, const std::string_view description, double speed = -1.); - std::string extrude_path(const ExtrusionPath &path, bool reverse, const GCode::SmoothPathCache *smooth_path_cache, const std::string_view description, double speed = -1.); + std::string extrude_entity(const ExtrusionEntityReference &entity, const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed = -1.); + std::string extrude_loop(const ExtrusionLoop &loop, const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed = -1.); + std::string extrude_skirt(const ExtrusionLoop &loop_src, const ExtrusionFlow &extrusion_flow_override, + const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed); + + std::string extrude_multi_path(const ExtrusionMultiPath &multipath, bool reverse, const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed = -1.); + std::string extrude_path(const ExtrusionPath &path, bool reverse, const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed = -1.); struct InstanceToPrint { @@ -271,13 +276,13 @@ private: // Container for extruder overrides (when wiping into object or infill). const LayerTools &layer_tools, // Optional smooth path interpolating extrusion polylines. - const GCode::SmoothPathCache *smooth_path_cache, + const GCode::SmoothPathCache &smooth_path_cache, // Is any extrusion possibly marked as wiping extrusion? const bool is_anything_overridden, // Round 1 (wiping into object or infill) or round 2 (normal extrusions). const bool print_wipe_extrusions); - std::string extrude_support(const ExtrusionEntityReferences &support_fills, const GCode::SmoothPathCache *smooth_path_cache); + std::string extrude_support(const ExtrusionEntityReferences &support_fills, const GCode::SmoothPathCache &smooth_path_cache); std::string travel_to(const Point &point, ExtrusionRole role, std::string comment); bool needs_retraction(const Polyline &travel, ExtrusionRole role = ExtrusionRole::None); diff --git a/src/libslic3r/GCode/SmoothPath.cpp b/src/libslic3r/GCode/SmoothPath.cpp index 7f4ee21785..ba793b465e 100644 --- a/src/libslic3r/GCode/SmoothPath.cpp +++ b/src/libslic3r/GCode/SmoothPath.cpp @@ -121,26 +121,32 @@ double clip_end(SmoothPath &path, double distance) return distance; } -void SmoothPathCache::interpolate_add(const Polyline &polyline, const InterpolationParameters ¶ms) -{ - m_cache[&polyline] = Slic3r::Geometry::ArcWelder::fit_path(polyline.points, params.tolerance, params.fit_circle_tolerance); -} - void SmoothPathCache::interpolate_add(const ExtrusionPath &path, const InterpolationParameters ¶ms) { - this->interpolate_add(path.polyline, params); + double tolerance = params.tolerance; + if (path.role().is_sparse_infill()) + // Use 3x lower resolution than the object fine detail for sparse infill. + tolerance *= 3.; + else if (path.role().is_support()) + // Use 4x lower resolution than the object fine detail for support. + tolerance *= 4.; + else if (path.role().is_skirt()) + // Brim is currently marked as skirt. + // Use 4x lower resolution than the object fine detail for skirt & brim. + tolerance *= 4.; + m_cache[&path.polyline] = Slic3r::Geometry::ArcWelder::fit_path(path.polyline.points, tolerance, params.fit_circle_tolerance); } void SmoothPathCache::interpolate_add(const ExtrusionMultiPath &multi_path, const InterpolationParameters ¶ms) { for (const ExtrusionPath &path : multi_path.paths) - this->interpolate_add(path.polyline, params); + this->interpolate_add(path, params); } void SmoothPathCache::interpolate_add(const ExtrusionLoop &loop, const InterpolationParameters ¶ms) { for (const ExtrusionPath &path : loop.paths) - this->interpolate_add(path.polyline, params); + this->interpolate_add(path, params); } void SmoothPathCache::interpolate_add(const ExtrusionEntityCollection &eec, const InterpolationParameters ¶ms) @@ -219,6 +225,9 @@ SmoothPath SmoothPathCache::resolve_or_fit_split_with_seam( assert(this_proj.segment_id >= 0 && this_proj.segment_id < el.path.size()); proj = this_proj; proj_path = &el - out.data(); + if (proj.distance2 == 0) + // There will be no better split point found than one with zero distance. + break; } assert(proj_path >= 0); // Split the path at the closest point. diff --git a/src/libslic3r/GCode/SmoothPath.hpp b/src/libslic3r/GCode/SmoothPath.hpp index cd686564a9..b2eb335a32 100644 --- a/src/libslic3r/GCode/SmoothPath.hpp +++ b/src/libslic3r/GCode/SmoothPath.hpp @@ -39,7 +39,6 @@ public: double fit_circle_tolerance; }; - void interpolate_add(const Polyline &pl, const InterpolationParameters ¶ms); void interpolate_add(const ExtrusionPath &ee, const InterpolationParameters ¶ms); void interpolate_add(const ExtrusionMultiPath &ee, const InterpolationParameters ¶ms); void interpolate_add(const ExtrusionLoop &ee, const InterpolationParameters ¶ms); @@ -64,6 +63,24 @@ private: ankerl::unordered_dense::map m_cache; }; +// Encapsulates references to global and layer local caches of smooth extrusion paths. +class SmoothPathCaches final +{ +public: + SmoothPathCaches() = delete; + SmoothPathCaches(const SmoothPathCache &global, const SmoothPathCache &layer_local) : + m_global(&global), m_layer_local(&layer_local) {} + SmoothPathCaches operator=(const SmoothPathCaches &rhs) + { m_global = rhs.m_global; m_layer_local = rhs.m_layer_local; return *this; } + + const SmoothPathCache& global() const { return *m_global; } + const SmoothPathCache& layer_local() const { return *m_layer_local; } + +private: + const SmoothPathCache *m_global; + const SmoothPathCache *m_layer_local; +}; + } // namespace GCode } // namespace Slic3r diff --git a/src/libslic3r/Geometry/ArcWelder.cpp b/src/libslic3r/Geometry/ArcWelder.cpp index 25426d68fc..bb23381de8 100644 --- a/src/libslic3r/Geometry/ArcWelder.cpp +++ b/src/libslic3r/Geometry/ArcWelder.cpp @@ -590,6 +590,7 @@ std::pair split_at(const Path &path, const PathSegmentProjection &pr { assert(proj.valid()); assert(! proj.valid() || (proj.segment_id >= 0 && proj.segment_id < path.size())); + assert(path.size() > 1); std::pair out; if (! proj.valid() || proj.segment_id + 1 == path.size() || (proj.segment_id + 2 == path.size() && proj.point == path.back().point)) out.first = path; @@ -637,18 +638,28 @@ std::pair split_at(const Path &path, const PathSegmentProjection &pr if ((cross2(vproj, vend) > 0) == end.ccw()) // Make the radius of a minor arc positive. out.second[1].radius *= -1.f; + assert(out.first.size() > 1); + assert(out.second.size() > 1); + out.second.front().radius = 0; } } else { - // Split at the start of proj.segment_id. - out.first.assign(path.begin(), path.begin() + split_segment_id + 1); - out.second.assign(path.begin() + split_segment_id, path.end()); - assert(out.first.size() + out.second.size() == path.size() + 1); - assert(out.first.back() == (split_segment_id == proj.segment_id ? start : end)); - assert(out.second.front() == (split_segment_id == proj.segment_id ? start : end)); + assert(split_segment_id >= 0 && split_segment_id < path.size()); + if (split_segment_id + 1 == path.size()) + out.first = path; + else if (split_segment_id == 0) + out.second = path; + else { + // Split at the start of proj.segment_id. + out.first.assign(path.begin(), path.begin() + split_segment_id + 1); + out.second.assign(path.begin() + split_segment_id, path.end()); + assert(out.first.size() + out.second.size() == path.size() + 1); + assert(out.first.back() == (split_segment_id == proj.segment_id ? start : end)); + assert(out.second.front() == (split_segment_id == proj.segment_id ? start : end)); + assert(out.first.size() > 1); + assert(out.second.size() > 1); + out.second.front().radius = 0; + } } - assert(out.first.size() > 1); - assert(out.second.size() > 1); - out.second.front().radius = 0; } return out; From 9fe42ecbdc7927aec643d7a9449b42ffbb7a2af8 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 18 Jul 2023 15:23:40 +0200 Subject: [PATCH 017/136] Method GCodeProcessor::process_G2_G3() modified to process lines G2/G3 R --- src/libslic3r/GCode/GCodeProcessor.cpp | 44 +++++++++++++++++++------- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index c8485a319c..e654d88f93 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -6,6 +6,7 @@ #include "libslic3r/I18N.hpp" #include "libslic3r/GCode/GCodeWriter.hpp" #include "libslic3r/I18N.hpp" +#include "libslic3r/Geometry/ArcWelder.hpp" #include "GCodeProcessor.hpp" #include @@ -2698,13 +2699,39 @@ void GCodeProcessor::process_G1(const std::array, 4>& axes void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line, bool clockwise) { - if (!line.has('I') || !line.has('J')) + enum class EFitting { None, IJ, R }; + const EFitting fitting = line.has('R') ? EFitting::R : (line.has('I') && line.has('J')) ? EFitting::IJ : EFitting::None; + + if (fitting == EFitting::None) return; + const float filament_diameter = (static_cast(m_extruder_id) < m_result.filament_diameters.size()) ? m_result.filament_diameters[m_extruder_id] : m_result.filament_diameters.back(); + const float filament_radius = 0.5f * filament_diameter; + const float area_filament_cross_section = static_cast(M_PI) * sqr(filament_radius); + + AxisCoords end_position = m_start_position; + for (unsigned char a = X; a <= E; ++a) { + end_position[a] = extract_absolute_position_on_axis((Axis)a, line, double(area_filament_cross_section)); + } + // relative center Vec3f rel_center = Vec3f::Zero(); - if (!line.has_value('I', rel_center.x()) || !line.has_value('J', rel_center.y())) - return; + double radius = 0.0; + if (fitting == EFitting::R) { + float r; + if (!line.has_value('R', r) || r == 0.0f) + return; + radius = (double)std::abs(r); + const Vec2f start_pos((float)m_start_position[X], (float)m_start_position[Y]); + const Vec2f end_pos((float)end_position[X], (float)end_position[Y]); + const Vec2f c = Geometry::ArcWelder::arc_center(start_pos, end_pos, r, !clockwise); + rel_center.x() = c.x() - m_start_position[X]; + rel_center.y() = c.y() - m_start_position[Y]; + } + else { + if (!line.has_value('I', rel_center.x()) || !line.has_value('J', rel_center.y())) + return; + } // scale center, if needed if (m_units == EUnits::Inches) @@ -2740,15 +2767,6 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line, bool cloc // arc center arc.center = arc.start + rel_center.cast(); - const float filament_diameter = (static_cast(m_extruder_id) < m_result.filament_diameters.size()) ? m_result.filament_diameters[m_extruder_id] : m_result.filament_diameters.back(); - const float filament_radius = 0.5f * filament_diameter; - const float area_filament_cross_section = static_cast(M_PI) * sqr(filament_radius); - - AxisCoords end_position = m_start_position; - for (unsigned char a = X; a <= E; ++a) { - end_position[a] = extract_absolute_position_on_axis((Axis)a, line, double(area_filament_cross_section)); - } - // arc end endpoint arc.end = Vec3d(end_position[X], end_position[Y], end_position[Z]); @@ -2757,6 +2775,8 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line, bool cloc // what to do ??? } + assert(fitting != EFitting::R || std::abs(radius - arc.start_radius()) < EPSILON); + // updates feedrate from line std::optional feedrate; if (line.has_f()) From ecb1a23edde9dc1f1dacc4928cfc6103f14c4d7e Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 19 Jul 2023 13:18:04 +0200 Subject: [PATCH 018/136] SPE-1784: New compressed (binary) gcode format integration 1st installment as part of tech ENABLE_BINARIZED_GCODE Still missing GCode Block save/load --- src/libslic3r/CMakeLists.txt | 2 + src/libslic3r/GCode.cpp | 303 +++++++-- src/libslic3r/GCode.hpp | 7 + src/libslic3r/GCode/GCodeBinarizer.cpp | 909 +++++++++++++++++++++++++ src/libslic3r/GCode/GCodeBinarizer.hpp | 330 +++++++++ src/libslic3r/GCode/GCodeProcessor.cpp | 264 ++++++- src/libslic3r/GCode/GCodeProcessor.hpp | 16 + src/libslic3r/GCode/Thumbnails.hpp | 32 + src/libslic3r/Preset.cpp | 4 + src/libslic3r/Print.cpp | 3 + src/libslic3r/PrintConfig.cpp | 8 + src/libslic3r/PrintConfig.hpp | 3 + src/libslic3r/Technologies.hpp | 9 + src/slic3r/GUI/Tab.cpp | 3 + 14 files changed, 1818 insertions(+), 75 deletions(-) create mode 100644 src/libslic3r/GCode/GCodeBinarizer.cpp create mode 100644 src/libslic3r/GCode/GCodeBinarizer.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 0de0b4e517..2c4a22fc5d 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -162,6 +162,8 @@ set(SLIC3R_SOURCES GCode/WipeTower.hpp GCode/GCodeProcessor.cpp GCode/GCodeProcessor.hpp + GCode/GCodeBinarizer.cpp + GCode/GCodeBinarizer.hpp GCode/AvoidCrossingPerimeters.cpp GCode/AvoidCrossingPerimeters.hpp GCode.cpp diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 1f306c83c6..6f148cc695 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -19,7 +19,10 @@ #include "ClipperUtils.hpp" #include "libslic3r.h" #include "LocalesUtils.hpp" -#include "libslic3r/format.hpp" +#include "format.hpp" +#if ENABLE_BINARIZED_GCODE +#include "libslic3r_version.h" +#endif // ENABLE_BINARIZED_GCODE #include #include @@ -837,6 +840,9 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu m_processor.initialize(path_tmp); m_processor.set_print(print); +#if ENABLE_BINARIZED_GCODE + m_processor.get_binary_data().reset(); +#endif // ENABLE_BINARIZED_GCODE GCodeOutputStream file(boost::nowide::fopen(path_tmp.c_str(), "wb"), m_processor); if (! file.is_open()) throw Slic3r::RuntimeError(std::string("G-code export to ") + path + " failed.\nCannot open the file for writing.\n"); @@ -969,35 +975,47 @@ namespace DoExport { } // Fill in print_statistics and return formatted string containing filament statistics to be inserted into G-code comment section. +#if ENABLE_BINARIZED_GCODE static std::string update_print_stats_and_format_filament_stats( const bool has_wipe_tower, - const WipeTowerData &wipe_tower_data, + const WipeTowerData &wipe_tower_data, + const FullPrintConfig &config, + const std::vector &extruders, + unsigned int initial_extruder_id, + PrintStatistics &print_statistics, + bool export_binary_data, + BinaryGCode::BinaryData &binary_data) +#else + static std::string update_print_stats_and_format_filament_stats( + const bool has_wipe_tower, + const WipeTowerData &wipe_tower_data, const FullPrintConfig &config, - const std::vector &extruders, + const std::vector &extruders, unsigned int initial_extruder_id, PrintStatistics &print_statistics) +#endif // ENABLE_BINARIZED_GCODE { - std::string filament_stats_string_out; + std::string filament_stats_string_out; - print_statistics.clear(); + print_statistics.clear(); print_statistics.total_toolchanges = std::max(0, wipe_tower_data.number_of_toolchanges); print_statistics.initial_extruder_id = initial_extruder_id; std::vector filament_types; - if (! extruders.empty()) { + if (! extruders.empty()) { std::pair out_filament_used_mm ("; filament used [mm] = ", 0); std::pair out_filament_used_cm3("; filament used [cm3] = ", 0); std::pair out_filament_used_g ("; filament used [g] = ", 0); std::pair out_filament_cost ("; filament cost = ", 0); for (const Extruder &extruder : extruders) { - print_statistics.printing_extruders.emplace_back(extruder.id()); - filament_types.emplace_back(config.filament_type.get_at(extruder.id())); + print_statistics.printing_extruders.emplace_back(extruder.id()); + filament_types.emplace_back(config.filament_type.get_at(extruder.id())); double used_filament = extruder.used_filament() + (has_wipe_tower ? wipe_tower_data.used_filament[extruder.id()] : 0.f); double extruded_volume = extruder.extruded_volume() + (has_wipe_tower ? wipe_tower_data.used_filament[extruder.id()] * 2.4052f : 0.f); // assumes 1.75mm filament diameter double filament_weight = extruded_volume * extruder.filament_density() * 0.001; double filament_cost = filament_weight * extruder.filament_cost() * 0.001; - auto append = [&extruder](std::pair &dst, const char *tmpl, double value) { - assert(is_decimal_separator_point()); + auto append = [&extruder](std::pair &dst, const char *tmpl, double value) { + assert(is_decimal_separator_point()); while (dst.second < extruder.id()) { // Fill in the non-printing extruders with zeros. dst.first += (dst.second > 0) ? ", 0" : "0"; @@ -1006,18 +1024,47 @@ namespace DoExport { if (dst.second > 0) dst.first += ", "; char buf[64]; - sprintf(buf, tmpl, value); + sprintf(buf, tmpl, value); dst.first += buf; ++ dst.second; - }; - append(out_filament_used_mm, "%.2lf", used_filament); - append(out_filament_used_cm3, "%.2lf", extruded_volume * 0.001); - if (filament_weight > 0.) { + }; +#if ENABLE_BINARIZED_GCODE + if (export_binary_data) { + char buf[128]; + sprintf(buf, "%.2lf", used_filament); + binary_data.print_metadata.raw_data.push_back({ "filament used [mm]", std::string(buf) }); + sprintf(buf, "%.2lf", extruded_volume * 0.001); + binary_data.print_metadata.raw_data.push_back({ "filament used [cm3]", std::string(buf) }); + } + else { +#endif // ENABLE_BINARIZED_GCODE + append(out_filament_used_mm, "%.2lf", used_filament); + append(out_filament_used_cm3, "%.2lf", extruded_volume * 0.001); +#if ENABLE_BINARIZED_GCODE + } +#endif // ENABLE_BINARIZED_GCODE + if (filament_weight > 0.) { print_statistics.total_weight = print_statistics.total_weight + filament_weight; - append(out_filament_used_g, "%.2lf", filament_weight); - if (filament_cost > 0.) { +#if ENABLE_BINARIZED_GCODE + if (export_binary_data) { + char buf[128]; + sprintf(buf, "%.2lf", filament_weight); + binary_data.print_metadata.raw_data.push_back({ "filament used [g]", std::string(buf) }); + } + else +#endif // ENABLE_BINARIZED_GCODE + append(out_filament_used_g, "%.2lf", filament_weight); + if (filament_cost > 0.) { print_statistics.total_cost = print_statistics.total_cost + filament_cost; - append(out_filament_cost, "%.2lf", filament_cost); +#if ENABLE_BINARIZED_GCODE + if (export_binary_data) { + char buf[128]; + sprintf(buf, "%.2lf", filament_cost); + binary_data.print_metadata.raw_data.push_back({ "filament cost", std::string(buf) }); + } + else +#endif // ENABLE_BINARIZED_GCODE + append(out_filament_cost, "%.2lf", filament_cost); } } print_statistics.total_used_filament += used_filament; @@ -1026,18 +1073,18 @@ namespace DoExport { print_statistics.total_wipe_tower_cost += has_wipe_tower ? (extruded_volume - extruder.extruded_volume())* extruder.filament_density() * 0.001 * extruder.filament_cost() * 0.001 : 0.; } filament_stats_string_out += out_filament_used_mm.first; - filament_stats_string_out += "\n" + out_filament_used_cm3.first; - if (out_filament_used_g.second) - filament_stats_string_out += "\n" + out_filament_used_g.first; - if (out_filament_cost.second) - filament_stats_string_out += "\n" + out_filament_cost.first; - print_statistics.initial_filament_type = config.filament_type.get_at(initial_extruder_id); - std::sort(filament_types.begin(), filament_types.end()); - print_statistics.printing_filament_types = filament_types.front(); - for (size_t i = 1; i < filament_types.size(); ++ i) { - print_statistics.printing_filament_types += ","; - print_statistics.printing_filament_types += filament_types[i]; - } + filament_stats_string_out += "\n" + out_filament_used_cm3.first; + if (out_filament_used_g.second) + filament_stats_string_out += "\n" + out_filament_used_g.first; + if (out_filament_cost.second) + filament_stats_string_out += "\n" + out_filament_cost.first; + print_statistics.initial_filament_type = config.filament_type.get_at(initial_extruder_id); + std::sort(filament_types.begin(), filament_types.end()); + print_statistics.printing_filament_types = filament_types.front(); + for (size_t i = 1; i < filament_types.size(); ++ i) { + print_statistics.printing_filament_types += ","; + print_statistics.printing_filament_types += filament_types[i]; + } } return filament_stats_string_out; } @@ -1082,8 +1129,42 @@ std::vector sort_object_instances_by_model_order(const Pri void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGeneratorCallback thumbnail_cb) { +#if ENABLE_BINARIZED_GCODE + const bool export_to_binary_gcode = print.full_print_config().option("gcode_binary")->value; + // if exporting gcode in binary format: + // we generate here the data to be passed to the post-processor, who is responsible to export them to file + // 1) generate the thumbnails + // 2) collect the config data + if (export_to_binary_gcode) { + BinaryGCode::BinaryData& binary_data = m_processor.get_binary_data(); + + // Unit tests or command line slicing may not define "thumbnails" or "thumbnails_format". + // If "thumbnails_format" is not defined, export to PNG. + if (const auto [thumbnails, thumbnails_format] = std::make_pair( + print.full_print_config().option("thumbnails"), + print.full_print_config().option>("thumbnails_format")); + thumbnails) { + GCodeThumbnails::generate_binary_thumbnails( + thumbnail_cb, binary_data.thumbnails, thumbnails->values, thumbnails_format ? thumbnails_format->value : GCodeThumbnailsFormat::PNG, + [&print]() { print.throw_if_canceled(); }); + } + + // file data + binary_data.file_metadata.encoding_type = (uint16_t)BinaryGCode::EMetadataEncodingType::INI; + binary_data.file_metadata.raw_data.emplace_back("Producer", std::string(SLIC3R_APP_NAME) + " " + std::string(SLIC3R_VERSION)); + // add here other key/value pairs + + // config data + binary_data.slicer_metadata.encoding_type = (uint16_t)BinaryGCode::EMetadataEncodingType::INI; + encode_full_config(print, binary_data.slicer_metadata.raw_data); + } + // modifies m_silent_time_estimator_enabled DoExport::init_gcode_processor(print.config(), m_processor, m_silent_time_estimator_enabled); +#else + // modifies m_silent_time_estimator_enabled + DoExport::init_gcode_processor(print.config(), m_processor, m_silent_time_estimator_enabled); +#endif // ENABLE_BINARIZED_GCODE if (! print.config().gcode_substitutions.values.empty()) { m_find_replace = make_unique(print.config()); @@ -1138,16 +1219,23 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // Write information on the generator. file.write_format("; %s\n\n", Slic3r::header_slic3r_generated().c_str()); - // Unit tests or command line slicing may not define "thumbnails" or "thumbnails_format". - // If "thumbnails_format" is not defined, export to PNG. - if (const auto [thumbnails, thumbnails_format] = std::make_pair( - print.full_print_config().option("thumbnails"), - print.full_print_config().option>("thumbnails_format")); - thumbnails) - GCodeThumbnails::export_thumbnails_to_file( - thumbnail_cb, thumbnails->values, thumbnails_format ? thumbnails_format->value : GCodeThumbnailsFormat::PNG, - [&file](const char* sz) { file.write(sz); }, - [&print]() { print.throw_if_canceled(); }); +#if ENABLE_BINARIZED_GCODE + // if exporting gcode in ascii format, generate the thumbnails here + if (!export_to_binary_gcode) { +#endif // ENABLE_BINARIZED_GCODE + // Unit tests or command line slicing may not define "thumbnails" or "thumbnails_format". + // If "thumbnails_format" is not defined, export to PNG. + if (const auto [thumbnails, thumbnails_format] = std::make_pair( + print.full_print_config().option("thumbnails"), + print.full_print_config().option>("thumbnails_format")); + thumbnails) + GCodeThumbnails::export_thumbnails_to_file( + thumbnail_cb, thumbnails->values, thumbnails_format ? thumbnails_format->value : GCodeThumbnailsFormat::PNG, + [&file](const char* sz) { file.write(sz); }, + [&print]() { print.throw_if_canceled(); }); +#if ENABLE_BINARIZED_GCODE + } +#endif // ENABLE_BINARIZED_GCODE // Write notes (content of the Print Settings tab -> Notes) { @@ -1169,20 +1257,26 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato const double layer_height = first_object->config().layer_height.value; assert(! print.config().first_layer_height.percent); const double first_layer_height = print.config().first_layer_height.value; - for (size_t region_id = 0; region_id < print.num_print_regions(); ++ region_id) { - const PrintRegion ®ion = print.get_print_region(region_id); - file.write_format("; external perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frExternalPerimeter, layer_height).width()); - file.write_format("; perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frPerimeter, layer_height).width()); - file.write_format("; infill extrusion width = %.2fmm\n", region.flow(*first_object, frInfill, layer_height).width()); - file.write_format("; solid infill extrusion width = %.2fmm\n", region.flow(*first_object, frSolidInfill, layer_height).width()); - file.write_format("; top infill extrusion width = %.2fmm\n", region.flow(*first_object, frTopSolidInfill, layer_height).width()); - if (print.has_support_material()) - file.write_format("; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width()); - if (print.config().first_layer_extrusion_width.value > 0) - file.write_format("; first layer extrusion width = %.2fmm\n", region.flow(*first_object, frPerimeter, first_layer_height, true).width()); - file.write_format("\n"); +#if ENABLE_BINARIZED_GCODE + if (!export_to_binary_gcode) { +#endif // ENABLE_BINARIZED_GCODE + for (size_t region_id = 0; region_id < print.num_print_regions(); ++ region_id) { + const PrintRegion ®ion = print.get_print_region(region_id); + file.write_format("; external perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frExternalPerimeter, layer_height).width()); + file.write_format("; perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frPerimeter, layer_height).width()); + file.write_format("; infill extrusion width = %.2fmm\n", region.flow(*first_object, frInfill, layer_height).width()); + file.write_format("; solid infill extrusion width = %.2fmm\n", region.flow(*first_object, frSolidInfill, layer_height).width()); + file.write_format("; top infill extrusion width = %.2fmm\n", region.flow(*first_object, frTopSolidInfill, layer_height).width()); + if (print.has_support_material()) + file.write_format("; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width()); + if (print.config().first_layer_extrusion_width.value > 0) + file.write_format("; first layer extrusion width = %.2fmm\n", region.flow(*first_object, frPerimeter, first_layer_height, true).width()); + file.write_format("\n"); + } + print.throw_if_canceled(); +#if ENABLE_BINARIZED_GCODE } - print.throw_if_canceled(); +#endif // ENABLE_BINARIZED_GCODE // adds tags for time estimators if (print.config().remaining_times.value) @@ -1487,31 +1581,66 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato print.throw_if_canceled(); // Get filament stats. +#if ENABLE_BINARIZED_GCODE file.write(DoExport::update_print_stats_and_format_filament_stats( - // Const inputs + // Const inputs has_wipe_tower, print.wipe_tower_data(), this->config(), m_writer.extruders(), initial_extruder_id, // Modifies - print.m_print_statistics)); - file.write("\n"); - file.write_format("; total filament used [g] = %.2lf\n", print.m_print_statistics.total_weight); - file.write_format("; total filament cost = %.2lf\n", print.m_print_statistics.total_cost); - if (print.m_print_statistics.total_toolchanges > 0) - file.write_format("; total toolchanges = %i\n", print.m_print_statistics.total_toolchanges); - file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Estimated_Printing_Time_Placeholder).c_str()); + print.m_print_statistics, + export_to_binary_gcode, + m_processor.get_binary_data() + )); - // Append full config, delimited by two 'phony' configuration keys prusaslicer_config = begin and prusaslicer_config = end. - // The delimiters are structured as configuration key / value pairs to be parsable by older versions of PrusaSlicer G-code viewer. - { - file.write("\n; prusaslicer_config = begin\n"); - std::string full_config; - append_full_config(print, full_config); - if (!full_config.empty()) - file.write(full_config); - file.write("; prusaslicer_config = end\n"); + if (export_to_binary_gcode) { + BinaryGCode::BinaryData& binary_data = m_processor.get_binary_data(); + char buf[128]; + sprintf(buf, "%.2lf", print.m_print_statistics.total_weight); + binary_data.print_metadata.raw_data.push_back({ "total filament used [g]", std::string(buf) }); + sprintf(buf, "%.2lf", print.m_print_statistics.total_cost); + binary_data.print_metadata.raw_data.push_back({ "total filament cost", std::string(buf) }); + if (print.m_print_statistics.total_toolchanges > 0) + binary_data.print_metadata.raw_data.push_back({ "total toolchanges", std::to_string(print.m_print_statistics.total_toolchanges) }); } + else { +#else + file.write(DoExport::update_print_stats_and_format_filament_stats( + // Const inputs + has_wipe_tower, print.wipe_tower_data(), + this->config(), + m_writer.extruders(), + initial_extruder_id, + // Modifies + print.m_print_statistics)); +#endif // ENABLE_BINARIZED_GCODE +#if ENABLE_BINARIZED_GCODE + // if exporting gcode in ascii format, statistics export is done here +#endif // ENABLE_BINARIZED_GCODE + file.write("\n"); + file.write_format("; total filament used [g] = %.2lf\n", print.m_print_statistics.total_weight); + file.write_format("; total filament cost = %.2lf\n", print.m_print_statistics.total_cost); + if (print.m_print_statistics.total_toolchanges > 0) + file.write_format("; total toolchanges = %i\n", print.m_print_statistics.total_toolchanges); + file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Estimated_Printing_Time_Placeholder).c_str()); + +#if ENABLE_BINARIZED_GCODE + // if exporting gcode in ascii format, config export is done here +#endif // ENABLE_BINARIZED_GCODE + // Append full config, delimited by two 'phony' configuration keys prusaslicer_config = begin and prusaslicer_config = end. + // The delimiters are structured as configuration key / value pairs to be parsable by older versions of PrusaSlicer G-code viewer. + { + file.write("\n; prusaslicer_config = begin\n"); + std::string full_config; + append_full_config(print, full_config); + if (!full_config.empty()) + file.write(full_config); + file.write("; prusaslicer_config = end\n"); + } +#if ENABLE_BINARIZED_GCODE + } +#endif // ENABLE_BINARIZED_GCODE print.throw_if_canceled(); } @@ -2571,6 +2700,13 @@ void GCode::apply_print_config(const PrintConfig &print_config) void GCode::append_full_config(const Print &print, std::string &str) { +#if ENABLE_BINARIZED_GCODE + std::vector> config; + encode_full_config(print, config); + for (const auto& [key, value] : config) { + str += "; " + key + " = " + value + "\n"; + } +#else const DynamicPrintConfig &cfg = print.full_print_config(); // Sorted list of config keys, which shall not be stored into the G-code. Initializer list. static constexpr auto banned_keys = { @@ -2588,8 +2724,35 @@ void GCode::append_full_config(const Print &print, std::string &str) for (const std::string &key : cfg.keys()) if (! is_banned(key) && ! cfg.option(key)->is_nil()) str += "; " + key + " = " + cfg.opt_serialize(key) + "\n"; +#endif // ENABLE_BINARIZED_GCODE } +#if ENABLE_BINARIZED_GCODE +void GCode::encode_full_config(const Print& print, std::vector>& config) +{ + const DynamicPrintConfig& cfg = print.full_print_config(); + // Sorted list of config keys, which shall not be stored into the G-code. Initializer list. + static constexpr auto banned_keys = { + "compatible_printers"sv, + "compatible_prints"sv, + //FIXME The print host keys should not be exported to full_print_config anymore. The following keys may likely be removed. + "print_host"sv, + "printhost_apikey"sv, + "printhost_cafile"sv + }; + assert(std::is_sorted(banned_keys.begin(), banned_keys.end())); + auto is_banned = [](const std::string& key) { + return std::binary_search(banned_keys.begin(), banned_keys.end(), key); + }; + config.reserve(config.size() + cfg.keys().size()); + for (const std::string& key : cfg.keys()) { + if (!is_banned(key) && !cfg.option(key)->is_nil()) + config.emplace_back(key, cfg.opt_serialize(key)); + } + config.shrink_to_fit(); +} +#endif // ENABLE_BINARIZED_GCODE + void GCode::set_extruders(const std::vector &extruder_ids) { m_writer.set_extruders(extruder_ids); diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 346ececba7..7358c15058 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -21,6 +21,9 @@ #include "GCode/GCodeProcessor.hpp" #include "EdgeGrid.hpp" #include "GCode/ThumbnailData.hpp" +#if ENABLE_BINARIZED_GCODE +#include "GCode/GCodeBinarizer.hpp" +#endif // ENABLE_BINARIZED_GCODE #include #include @@ -187,6 +190,10 @@ public: // append full config to the given string static void append_full_config(const Print& print, std::string& str); +#if ENABLE_BINARIZED_GCODE + // translate full config into a list of items + static void encode_full_config(const Print& print, std::vector>& config); +#endif // ENABLE_BINARIZED_GCODE // Object and support extrusions of the same PrintObject at the same print_z. // public, so that it could be accessed by free helper functions from GCode.cpp diff --git a/src/libslic3r/GCode/GCodeBinarizer.cpp b/src/libslic3r/GCode/GCodeBinarizer.cpp new file mode 100644 index 0000000000..9a205f5327 --- /dev/null +++ b/src/libslic3r/GCode/GCodeBinarizer.cpp @@ -0,0 +1,909 @@ +#include "GCodeBinarizer.hpp" + +#if ENABLE_BINARIZED_GCODE_DEBUG +#define NOMINMAX +#include +#include +#endif // ENABLE_BINARIZED_GCODE_DEBUG + +#include +#include + +namespace BinaryGCode { + +static size_t g_checksum_max_cache_size = 65536; + +std::string translate_result(BinaryGCode::EResult result) +{ + switch (result) + { + case BinaryGCode::EResult::Success: { return "Success"; } + case BinaryGCode::EResult::ReadError: { return "Read error"; } + case BinaryGCode::EResult::WriteError: { return "Write error"; } + case BinaryGCode::EResult::InvalidMagicNumber: { return "Invalid magic number"; } + case BinaryGCode::EResult::InvalidVersionNumber: { return "Invalid version number"; } + case BinaryGCode::EResult::InvalidChecksumType: { return "Invalid checksum type"; } + case BinaryGCode::EResult::InvalidBlockType: { return "Invalid block type"; } + case BinaryGCode::EResult::InvalidCompressionType: { return "Invalid compression type"; } + case BinaryGCode::EResult::InvalidMetadataEncodingType: { return "Invalid metadata encoding type"; } + case BinaryGCode::EResult::DataCompressionError: { return "Data compression error"; } + case BinaryGCode::EResult::DataUncompressionError: { return "Data uncompression error"; } + case BinaryGCode::EResult::MetadataEncodingError: { return "Data encoding error"; } + case BinaryGCode::EResult::MetadataDecodingError: { return "Data decoding error"; } + case BinaryGCode::EResult::BlockNotFound: { return "Block not found"; } + case BinaryGCode::EResult::InvalidChecksum: { return "Invalid checksum"; } + case BinaryGCode::EResult::InvalidThumbnailFormat: { return "Invalid thumbnail format"; } + case BinaryGCode::EResult::InvalidThumbnailWidth: { return "Invalid thumbnail width"; } + case BinaryGCode::EResult::InvalidThumbnailHeight: { return "Invalid thumbnail height"; } + case BinaryGCode::EResult::InvalidThumbnailDataSize: { return "Invalid thumbnail data size"; } + } + return std::string(); +} + +size_t get_checksum_max_cache_size() { return g_checksum_max_cache_size; } +void set_checksum_max_cache_size(size_t size) { g_checksum_max_cache_size = size; } + +static uint16_t checksum_types_count() { return 1 + (uint16_t)EChecksumType::CRC32; } +static uint16_t block_types_count() { return 1 + (uint16_t)EBlockType::Thumbnail; } +static uint16_t compression_types_count() { return 1 + (uint16_t)ECompressionType::None; } +static uint16_t thumbnail_formats_count() { return 1 + (uint16_t)EThumbnailFormat::QOI; } +static uint16_t metadata_encoding_types_count() { return 1 + (uint16_t)EMetadataEncodingType::INI; } + +static bool write_to_file(FILE& file, const void* data, size_t data_size) +{ + fwrite(data, 1, data_size, &file); + return !ferror(&file); +} + +static bool read_from_file(FILE& file, void* data, size_t data_size) +{ + fread(data, 1, data_size, &file); + return !ferror(&file); +} + +static bool encode_metadata(const std::vector>& data_in, std::vector& data_out, + EMetadataEncodingType encoding_type) +{ + for (const auto& [key, value] : data_in) { + switch (encoding_type) + { + case EMetadataEncodingType::INI: + { + data_out.insert(data_out.end(), key.begin(), key.end()); + data_out.emplace_back('='); + data_out.insert(data_out.end(), value.begin(), value.end()); + data_out.emplace_back('\n'); + break; + } + } + } + + return true; +} + +static bool decode_metadata(const std::vector& data_in, std::vector>& data_out, + EMetadataEncodingType encoding_type) +{ + switch (encoding_type) + { + case EMetadataEncodingType::INI: + { + auto start_it = data_in.begin(); + auto end_it = data_in.begin(); + while (end_it != data_in.end()) { + while (end_it != data_in.end() && *end_it != '\n') { + ++end_it; + } + const std::string item(start_it, end_it); + const size_t pos = item.find_first_of('='); + if (pos != std::string::npos) { + data_out.emplace_back(std::make_pair(item.substr(0, pos), item.substr(pos + 1))); + start_it = ++end_it; + } + } + break; + } + } + + return true; +} + +static bool compress(const std::vector& data_in, std::vector& data_out, ECompressionType compression_type) +{ + return true; +} + +static bool uncompress(const std::vector& data_in, std::vector& data_out, ECompressionType compression_type) +{ + return true; +} + +static uint32_t crc32_sw(const uint8_t* buffer, uint32_t length, uint32_t crc) +{ + uint32_t value = crc ^ 0xFFFFFFFF; + while (length--) { + value ^= (uint32_t)*buffer++; + for (int bit = 0; bit < 8; bit++) { + if (value & 1) + value = (value >> 1) ^ 0xEDB88320; + else + value >>= 1; + } + } + value ^= 0xFFFFFFFF; + return value; +} + +std::vector encode(const void* data, size_t data_size) +{ + std::vector ret(data_size); + memcpy(ret.data(), data, data_size); + return ret; +} + +Checksum::Checksum(EChecksumType type) +: m_type(type) +{ + if (m_type != EChecksumType::None) + m_checksum = std::vector(checksum_size(m_type), '\0'); +} + +EChecksumType Checksum::get_type() const +{ + return m_type; +} + +void Checksum::append(const std::vector& data) +{ + size_t remaining_data_size = std::distance(data.begin(), data.end()); + auto it_begin = data.begin(); + while (remaining_data_size + m_cache.size() > g_checksum_max_cache_size) { + update(); + if (remaining_data_size > g_checksum_max_cache_size) { + m_cache.insert(m_cache.end(), it_begin, it_begin + g_checksum_max_cache_size); + it_begin += g_checksum_max_cache_size; + remaining_data_size -= g_checksum_max_cache_size; + } + } + + m_cache.insert(m_cache.end(), it_begin, data.end()); +} + +bool Checksum::matches(Checksum& other) +{ + update(); + other.update(); + return m_checksum == other.m_checksum; +} + +EResult Checksum::write(FILE& file) +{ + if (m_type != EChecksumType::None) { + update(); + if (!write_to_file(file, (const void*)m_checksum.data(), m_checksum.size())) + return EResult::WriteError; + } + return EResult::Success; +} + +EResult Checksum::read(FILE& file) +{ + if (m_type != EChecksumType::None) { + if (!read_from_file(file, (void*)m_checksum.data(), m_checksum.size())) + return EResult::ReadError; + } + return EResult::Success; +} + +void Checksum::update() +{ + if (m_cache.empty()) + return; + + switch (m_type) + { + case EChecksumType::None: + { + break; + } + case EChecksumType::CRC32: + { + const uint32_t old_crc = *(uint32_t*)m_checksum.data(); + const uint32_t new_crc = crc32_sw(m_cache.data(), (uint32_t)m_cache.size(), old_crc); + *(uint32_t*)m_checksum.data() = new_crc; + break; + } + } + + m_cache.clear(); +} + +EResult FileHeader::write(FILE& file) const +{ + if (magic != *(uint32_t*)(MAGIC.data())) + return EResult::InvalidMagicNumber; + if (checksum_type >= checksum_types_count()) + return EResult::InvalidChecksumType; + + if (!write_to_file(file, (const void*)&magic, sizeof(magic))) + return EResult::WriteError; + if (!write_to_file(file, (const void*)&version, sizeof(version))) + return EResult::WriteError; + if (!write_to_file(file, (const void*)&checksum_type, sizeof(checksum_type))) + return EResult::WriteError; + + return EResult::Success; +} + +EResult FileHeader::read(FILE& file, const uint32_t* const max_version) +{ + if (!read_from_file(file, (void*)&magic, sizeof(magic))) + return EResult::ReadError; + if (magic != *(uint32_t*)(MAGIC.data())) + return EResult::InvalidMagicNumber; + + if (!read_from_file(file, (void*)&version, sizeof(version))) + return EResult::ReadError; + if (max_version != nullptr && version > *max_version) + return EResult::InvalidVersionNumber; + + if (!read_from_file(file, (void*)&checksum_type, sizeof(checksum_type))) + return EResult::ReadError; + if (checksum_type >= checksum_types_count()) + return EResult::InvalidChecksumType; + + return EResult::Success; +} + +void BlockHeader::update_checksum(Checksum& checksum) const +{ + checksum.append(encode((const void*)&type, sizeof(type))); + checksum.append(encode((const void*)&compression, sizeof(compression))); + checksum.append(encode((const void*)&uncompressed_size, sizeof(uncompressed_size))); + if (compression != (uint16_t)ECompressionType::None) + checksum.append(encode((const void*)&compressed_size, sizeof(compressed_size))); +} + +EResult BlockHeader::write(FILE& file) const +{ + if (!write_to_file(file, (const void*)&type, sizeof(type))) + return EResult::WriteError; + if (!write_to_file(file, (const void*)&compression, sizeof(compression))) + return EResult::WriteError; + if (!write_to_file(file, (const void*)&uncompressed_size, sizeof(uncompressed_size))) + return EResult::WriteError; + if (compression != (uint16_t)ECompressionType::None) { + if (!write_to_file(file, (const void*)&compressed_size, sizeof(compressed_size))) + return EResult::WriteError; + } + return EResult::Success; +} + +EResult BlockHeader::read(FILE& file) +{ + if (!read_from_file(file, (void*)&type, sizeof(type))) + return EResult::ReadError; + if (type >= block_types_count()) + return EResult::InvalidBlockType; + + if (!read_from_file(file, (void*)&compression, sizeof(compression))) + return EResult::ReadError; + if (compression >= compression_types_count()) + return EResult::InvalidCompressionType; + + if (!read_from_file(file, (void*)&uncompressed_size, sizeof(uncompressed_size))) + return EResult::ReadError; + if (compression != (uint16_t)ECompressionType::None) { + if (!read_from_file(file, (void*)&compressed_size, sizeof(compressed_size))) + return EResult::ReadError; + } + + return EResult::Success; +} + +EResult BaseMetadataBlock::write(FILE& file, EBlockType block_type, ECompressionType compression_type, Checksum& checksum) const +{ + if (encoding_type > metadata_encoding_types_count()) + return EResult::InvalidMetadataEncodingType; + + BlockHeader block_header = { (uint16_t)block_type, (uint16_t)compression_type, (uint32_t)0 }; + std::vector out_data; + if (!raw_data.empty()) { + // process payload encoding + std::vector uncompressed_data; + if (!encode_metadata(raw_data, uncompressed_data, (EMetadataEncodingType)encoding_type)) + return EResult::MetadataEncodingError; + // process payload compression + block_header.uncompressed_size = (uint32_t)uncompressed_data.size(); + std::vector compressed_data; + if (compression_type != ECompressionType::None) { + if (!compress(uncompressed_data, compressed_data, compression_type)) + return EResult::DataCompressionError; + block_header.compressed_size = (uint32_t)compressed_data.size(); + } + out_data.swap((compression_type == ECompressionType::None) ? uncompressed_data : compressed_data); + } + + // write block header + EResult res = block_header.write(file); + if (res != EResult::Success) + // propagate error + return res; + + // write block payload + if (!write_to_file(file, (const void*)&encoding_type, sizeof(encoding_type))) + return EResult::WriteError; + if (!out_data.empty()) { + if (!write_to_file(file, (const void*)out_data.data(), out_data.size())) + return EResult::WriteError; + } + + if (checksum.get_type() != EChecksumType::None) { + // update checksum with block header + block_header.update_checksum(checksum); + // update checksum with block payload + checksum.append(encode((const void*)&encoding_type, sizeof(encoding_type))); + if (!out_data.empty()) + checksum.append(out_data); + } + + return EResult::Success; +} + +EResult BaseMetadataBlock::read_data(FILE& file, const BlockHeader& block_header) +{ + const ECompressionType compression_type = (ECompressionType)block_header.compression; + + if (!read_from_file(file, (void*)&encoding_type, sizeof(encoding_type))) + return EResult::ReadError; + if (encoding_type > metadata_encoding_types_count()) + // Found invalid metadata encoding type + return EResult::InvalidMetadataEncodingType; + + std::vector data; + const size_t data_size = (compression_type == ECompressionType::None) ? block_header.uncompressed_size : block_header.compressed_size; + if (data_size > 0) { + data.resize(data_size); + if (!read_from_file(file, (void*)data.data(), data_size)) + return EResult::ReadError; + } + + std::vector uncompressed_data; + if (compression_type != ECompressionType::None) { + if (!uncompress(data, uncompressed_data, compression_type)) + return EResult::DataUncompressionError; + } + + if (!decode_metadata((compression_type == ECompressionType::None) ? data : uncompressed_data, raw_data, (EMetadataEncodingType)encoding_type)) + return EResult::MetadataDecodingError; + + return EResult::Success; +} + +EResult FileMetadataBlock::write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const +{ + Checksum cs(checksum_type); + + // write block header, payload + EResult res = BaseMetadataBlock::write(file, EBlockType::FileMetadata, compression_type, cs); + if (res != EResult::Success) + // propagate error + return res; + + // write block checksum + if (checksum_type != EChecksumType::None) + return cs.write(file); + + return EResult::Success; +} + +EResult FileMetadataBlock::read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header) +{ + // read block payload + EResult res = BaseMetadataBlock::read_data(file, block_header); + if (res != EResult::Success) + // propagate error + return res; + + const EChecksumType checksum_type = (EChecksumType)file_header.checksum_type; + if (checksum_type != EChecksumType::None) { + // read block checksum + Checksum cs(checksum_type); + res = cs.read(file); + if (res != EResult::Success) + // propagate error + return res; + } + + return EResult::Success; +} + +EResult ThumbnailBlock::write(FILE& file, EChecksumType checksum_type) const +{ + if (format >= thumbnail_formats_count()) + return EResult::InvalidThumbnailFormat; + if (width == 0) + return EResult::InvalidThumbnailWidth; + if (height == 0) + return EResult::InvalidThumbnailHeight; + if (data.size() == 0) + return EResult::InvalidThumbnailDataSize; + + // write block header + const BlockHeader block_header = { (uint16_t)EBlockType::Thumbnail, (uint16_t)ECompressionType::None, (uint32_t)data.size() }; + EResult res = block_header.write(file); + if (res != EResult::Success) + // propagate error + return res; + + // write block payload + if (!write_to_file(file, (const void*)&format, sizeof(format))) + return EResult::WriteError; + if (!write_to_file(file, (const void*)&width, sizeof(width))) + return EResult::WriteError; + if (!write_to_file(file, (const void*)&height, sizeof(height))) + return EResult::WriteError; + if (!write_to_file(file, (const void*)data.data(), data.size())) + return EResult::WriteError; + + if (checksum_type != EChecksumType::None) { + Checksum cs(checksum_type); + // update checksum with block header + block_header.update_checksum(cs); + // update checksum with block payload + update_checksum(cs); + // write block checksum + res = cs.write(file); + if (res != EResult::Success) + // propagate error + return res; + } + + return EResult::Success; +} + +EResult ThumbnailBlock::read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header) +{ + // read block payload + if (!read_from_file(file, (void*)&format, sizeof(format))) + return EResult::ReadError; + if (format >= thumbnail_formats_count()) + return EResult::InvalidThumbnailFormat; + if (!read_from_file(file, (void*)&width, sizeof(width))) + return EResult::ReadError; + if (width == 0) + return EResult::InvalidThumbnailWidth; + if (!read_from_file(file, (void*)&height, sizeof(height))) + return EResult::ReadError; + if (height == 0) + return EResult::InvalidThumbnailHeight; + if (block_header.uncompressed_size == 0) + return EResult::InvalidThumbnailDataSize; + data.resize(block_header.uncompressed_size); + if (!read_from_file(file, (void*)data.data(), block_header.uncompressed_size)) + return EResult::ReadError; + + const EChecksumType checksum_type = (EChecksumType)file_header.checksum_type; + if (checksum_type != EChecksumType::None) { + // read block checksum + Checksum cs(checksum_type); + const EResult res = cs.read(file); + if (res != EResult::Success) + // propagate error + return res; + } + + return EResult::Success; +} + +void ThumbnailBlock::update_checksum(Checksum& checksum) const +{ + checksum.append(encode((const void*)&format, sizeof(format))); + checksum.append(encode((const void*)&width, sizeof(width))); + checksum.append(encode((const void*)&height, sizeof(height))); + checksum.append(data); +} + +EResult PrinterMetadataBlock::write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const +{ + Checksum cs(checksum_type); + + // write block header, payload + EResult res = BaseMetadataBlock::write(file, EBlockType::PrinterMetadata, compression_type, cs); + if (res != EResult::Success) + // propagate error + return res; + + // write block checksum + if (checksum_type != EChecksumType::None) + return cs.write(file); + + return EResult::Success; +} + +EResult PrinterMetadataBlock::read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header) +{ + // read block payload + EResult res = BaseMetadataBlock::read_data(file, block_header); + if (res != EResult::Success) + // propagate error + return res; + + const EChecksumType checksum_type = (EChecksumType)file_header.checksum_type; + if (checksum_type != EChecksumType::None) { + // read block checksum + Checksum cs(checksum_type); + res = cs.read(file); + if (res != EResult::Success) + // propagate error + return res; + } + + return EResult::Success; +} + +EResult PrintMetadataBlock::write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const +{ + Checksum cs(checksum_type); + + // write block header, payload + EResult res = BaseMetadataBlock::write(file, EBlockType::PrintMetadata, compression_type, cs); + if (res != EResult::Success) + // propagate error + return res; + + // write block checksum + if (checksum_type != EChecksumType::None) + return cs.write(file); + + return EResult::Success; +} + +EResult PrintMetadataBlock::read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header) +{ + // read block payload + EResult res = BaseMetadataBlock::read_data(file, block_header); + if (res != EResult::Success) + // propagate error + return res; + + const EChecksumType checksum_type = (EChecksumType)file_header.checksum_type; + if (checksum_type != EChecksumType::None) { + // read block checksum + Checksum cs(checksum_type); + res = cs.read(file); + if (res != EResult::Success) + // propagate error + return res; + } + + return EResult::Success; +} + +EResult SlicerMetadataBlock::write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const +{ + Checksum cs(checksum_type); + + // write block header, payload + EResult res = BaseMetadataBlock::write(file, EBlockType::SlicerMetadata, compression_type, cs); + if (res != EResult::Success) + // propagate error + return res; + + // write block checksum + if (checksum_type != EChecksumType::None) + return cs.write(file); + + return EResult::Success; +} + +EResult SlicerMetadataBlock::read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header) +{ + // read block payload + EResult res = BaseMetadataBlock::read_data(file, block_header); + if (res != EResult::Success) + // propagate error + return res; + + const EChecksumType checksum_type = (EChecksumType)file_header.checksum_type; + if (checksum_type != EChecksumType::None) { + // read block checksum + Checksum cs(checksum_type); + res = cs.read(file); + if (res != EResult::Success) + // propagate error + return res; + } + + return EResult::Success; +} + +EResult GCodeBlock::write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const +{ + return EResult::Success; +} + +EResult GCodeBlock::read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header) +{ + return EResult::Success; +} + +#if ENABLE_CHECKSUM_BLOCK +EResult ChecksumBlock::write(FILE& file) const +{ + if (!data.empty()) { + const BlockHeader block_header = { (uint16_t)EBlockType::Checksum, (uint16_t)ECompressionType::None, (uint32_t)data.size() }; + // write block header + const EResult res = block_header.write(file); + if (res != EResult::Success) + // propagate error + return res; + // write block payload + if (!write_to_file(file, (const void*)data.data(), data.size())) + return EResult::WriteError; + } + + return EResult::Success; +} + +EResult ChecksumBlock::read_data(FILE& file, const BlockHeader& block_header) +{ + if (block_header.uncompressed_size > 0) { + data.resize(block_header.uncompressed_size); + if (!read_from_file(file, (void*)data.data(), block_header.uncompressed_size)) + return EResult::ReadError; + } + else + data.clear(); + + return EResult::Success; +} +#endif // ENABLE_CHECKSUM_BLOCK + +EResult Binarizer::initialize(FILE& file, EChecksumType checksum_type) +{ + if (!m_enabled) + return EResult::Success; + + // initialize checksum + m_checksum_type = checksum_type; +#if ENABLE_CHECKSUM_BLOCK + m_checksum = ChecksumBlock(); +#endif // ENABLE_CHECKSUM_BLOCK + + // save header + FileHeader file_header; + file_header.checksum_type = (uint16_t)m_checksum_type; + EResult res = file_header.write(file); + if (res != EResult::Success) + return res; + + // save file metadata block + res = m_binary_data.file_metadata.write(file, ECompressionType::None, m_checksum_type); + if (res != EResult::Success) + return res; + + // save printer metadata block + res = m_binary_data.printer_metadata.write(file, ECompressionType::None, m_checksum_type); + if (res != EResult::Success) + return res; + + // save thumbnail blocks + for (const ThumbnailBlock& block : m_binary_data.thumbnails) { + res = block.write(file, m_checksum_type); + if (res != EResult::Success) + return res; + } + + // save slicer metadata block + res = m_binary_data.slicer_metadata.write(file, ECompressionType::None, m_checksum_type); + if (res != EResult::Success) + return res; + + // save gcode block + + return EResult::Success; +} + +EResult Binarizer::finalize(FILE& file) +{ + if (!m_enabled) + return EResult::Success; + + // save print metadata block + EResult res = m_binary_data.print_metadata.write(file, ECompressionType::None, m_checksum_type); + if (res != EResult::Success) + return res; + +#if ENABLE_CHECKSUM_BLOCK + if (m_checksum_type != EChecksumType::None) { + // save checksum + // dummy checksum until it is not properly implemented + switch (m_checksum_type) + { + case EChecksumType::CRC32: + case EChecksumType::MD5: + { + m_checksum.data.clear(); + break; + } + } + + res = m_checksum.write(file); + if (res != EResult::Success) + return res; + } +#endif // ENABLE_CHECKSUM_BLOCK + + return EResult::Success; +} + +bool is_valid_binary_gcode(FILE& file) +{ + // cache file position + const long curr_pos = ftell(&file); + rewind(&file); + + std::array magic; + fread((void*)magic.data(), 1, magic.size(), &file); + if (ferror(&file)) + return false; + else { + // restore file position + fseek(&file, curr_pos, SEEK_SET); + return magic == MAGIC; + } +} + +EResult read_header(FILE& file, FileHeader& header, const uint32_t* const max_version) +{ + rewind(&file); + return header.read(file, max_version); +} + +static EResult checksums_match(FILE& file, const FileHeader& file_header, const BlockHeader& block_header) +{ + // cache file position + const long curr_pos = ftell(&file); + + Checksum curr_cs((EChecksumType)file_header.checksum_type); + // update block checksum block header + block_header.update_checksum(curr_cs); + + // read block payload + size_t remaining_payload_size = block_payload_size(block_header); + while (remaining_payload_size > 0) { + const size_t size_to_read = std::min(remaining_payload_size, g_checksum_max_cache_size); + std::vector payload(size_to_read); + if (!read_from_file(file, payload.data(), payload.size())) + return EResult::ReadError; + curr_cs.append(payload); + remaining_payload_size -= size_to_read; + } + + // read checksum + Checksum read_cs((EChecksumType)file_header.checksum_type); + EResult res = read_cs.read(file); + if (res != EResult::Success) + // propagate error + return res; + + // Verify checksum + if (!curr_cs.matches(read_cs)) + return EResult::InvalidChecksum; + + // restore file position + fseek(&file, curr_pos, SEEK_SET); + + return EResult::Success; +} + +EResult read_next_block_header(FILE& file, const FileHeader& file_header, BlockHeader& block_header, bool verify_checksum) +{ + if (verify_checksum && (EChecksumType)file_header.checksum_type != EChecksumType::None) { + const EResult res = block_header.read(file); + if (res != EResult::Success) + // propagate error + return res; + + return checksums_match(file, file_header, block_header); + } + else + return block_header.read(file); +} + +EResult read_next_block_header(FILE& file, const FileHeader& file_header, BlockHeader& block_header, EBlockType type, bool verify_checksum) +{ + // cache file position + const long curr_pos = ftell(&file); + + do { + EResult res = read_next_block_header(file, file_header, block_header, false); + if (res != EResult::Success) + // propagate error + return res; + else if (feof(&file)) { + // block not found + // restore file position + fseek(&file, curr_pos, SEEK_SET); + return EResult::BlockNotFound; + } + else if ((EBlockType)block_header.type == type) { + // block found + if (verify_checksum) { + res = checksums_match(file, file_header, block_header); + if (res != EResult::Success) + // propagate error + return res; + else + break; + } + } + + if (!feof(&file)) { + res = skip_block_content(file, file_header, block_header); + if (res != EResult::Success) + // propagate error + return res; + } + } while (true); + + return EResult::Success; +} + +EResult skip_block_payload(FILE& file, const BlockHeader& block_header) +{ + fseek(&file, (long)block_payload_size(block_header), SEEK_CUR); + return ferror(&file) ? EResult::ReadError : EResult::Success; +} + +EResult skip_block_content(FILE& file, const FileHeader& file_header, const BlockHeader& block_header) +{ + fseek(&file, (long)block_content_size(file_header, block_header), SEEK_CUR); + return ferror(&file) ? EResult::ReadError : EResult::Success; +} + +size_t block_parameters_size(EBlockType type) +{ + switch (type) + { + case EBlockType::FileMetadata: { return FileMetadataBlock::get_parameters_size(); } + case EBlockType::GCode: { return GCodeBlock::get_parameters_size(); } + case EBlockType::SlicerMetadata: { return SlicerMetadataBlock::get_parameters_size(); } + case EBlockType::PrinterMetadata: { return PrinterMetadataBlock::get_parameters_size(); } + case EBlockType::PrintMetadata: { return PrintMetadataBlock::get_parameters_size(); } + case EBlockType::Thumbnail: { return ThumbnailBlock::get_parameters_size(); } + } + return 0; +} + +size_t block_payload_size(const BlockHeader& block_header) +{ + size_t ret = block_parameters_size((EBlockType)block_header.type); + ret += ((ECompressionType)block_header.compression == ECompressionType::None) ? + block_header.uncompressed_size : block_header.compressed_size; + return ret; +} + +size_t checksum_size(EChecksumType type) +{ + switch (type) + { + case EChecksumType::None: { return 0; } + case EChecksumType::CRC32: { return 4; } + } + return 0; +} + +extern size_t block_content_size(const FileHeader& file_header, const BlockHeader& block_header) +{ +#if ENABLE_CHECKSUM_BLOCK + return ((EBlockType)block_header.type == EBlockType::Checksum) ? + block_payload_size(block_header) : block_payload_size(block_header) + checksum_size((EChecksumType)file_header.checksum_type); +#else + return block_payload_size(block_header) + checksum_size((EChecksumType)file_header.checksum_type); +#endif // ENABLE_CHECKSUM_BLOCK +} + +} // namespace BinaryGCode + diff --git a/src/libslic3r/GCode/GCodeBinarizer.hpp b/src/libslic3r/GCode/GCodeBinarizer.hpp new file mode 100644 index 0000000000..058cabd475 --- /dev/null +++ b/src/libslic3r/GCode/GCodeBinarizer.hpp @@ -0,0 +1,330 @@ +#ifndef slic3r_GCode_GCodeBinarizer_hpp_ +#define slic3r_GCode_GCodeBinarizer_hpp_ + +#ifdef _WIN32 +#define ENABLE_BINARIZED_GCODE_DEBUG 1 +#endif // _WIN32 + +#define ENABLE_CHECKSUM_BLOCK 0 + +#include +#include +#include +#include +#include + +namespace BinaryGCode { + +static const std::array MAGIC{ 'G', 'C', 'D', 'E' }; +static const uint32_t VERSION = 1; + +enum class EResult : uint16_t +{ + Success, + ReadError, + WriteError, + InvalidMagicNumber, + InvalidVersionNumber, + InvalidChecksumType, + InvalidBlockType, + InvalidCompressionType, + InvalidMetadataEncodingType, + DataCompressionError, + DataUncompressionError, + MetadataEncodingError, + MetadataDecodingError, + BlockNotFound, + InvalidChecksum, + InvalidThumbnailFormat, + InvalidThumbnailWidth, + InvalidThumbnailHeight, + InvalidThumbnailDataSize +}; + +// Returns a string description of the given result +extern std::string translate_result(BinaryGCode::EResult result); + +enum class EChecksumType : uint16_t +{ + None, + CRC32 +}; + +class Checksum +{ +public: + // Constructs a checksum of the given type. + // The checksum data are sized accordingly. + explicit Checksum(EChecksumType type); + + EChecksumType get_type() const; + + // Appends the given data to the cache and performs a checksum update if + // the size of the cache exceeds the max checksum cache size. + void append(const std::vector& data); + // Returns true if the given checksum is equal to this one + bool matches(Checksum& other); + + EResult write(FILE& file); + EResult read(FILE& file); + +private: + EChecksumType m_type; + std::vector m_cache; + std::vector m_checksum; + + void update(); +}; + +struct FileHeader +{ + uint32_t magic{ *(uint32_t*)(MAGIC.data()) }; + uint32_t version{ VERSION }; + uint16_t checksum_type{ (uint16_t)EChecksumType::None }; + + EResult write(FILE& file) const; + EResult read(FILE& file, const uint32_t* const max_version); +}; + +enum class EBlockType : uint16_t +{ +#if ENABLE_CHECKSUM_BLOCK + Checksum, +#endif // ENABLE_CHECKSUM_BLOCK + FileMetadata, + GCode, + SlicerMetadata, + PrinterMetadata, + PrintMetadata, + Thumbnail +}; + +enum class ECompressionType : uint16_t +{ + None, +}; + +struct BlockHeader +{ + uint16_t type{ 0 }; + uint16_t compression{ 0 }; + uint32_t uncompressed_size{ 0 }; + uint32_t compressed_size{ 0 }; + + // Updates the given checksum with the data of this BlockHeader + void update_checksum(Checksum& checksum) const; + + EResult write(FILE& file) const; + EResult read(FILE& file); +}; + +enum class EMetadataEncodingType : uint16_t +{ + INI, +}; + +struct BaseMetadataBlock +{ + // type of data encoding + uint16_t encoding_type{ 0 }; + // data in key/value form + std::vector> raw_data; + + // write block header and data in encoded format + EResult write(FILE& file, EBlockType block_type, ECompressionType compression_type, Checksum& checksum) const; + // read block data in encoded format + EResult read_data(FILE& file, const BlockHeader& block_header); + + static size_t get_parameters_size() { return sizeof(encoding_type); } +}; + +struct FileMetadataBlock : public BaseMetadataBlock +{ + // write block header and data + EResult write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const; + // read block data + EResult read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header); +}; + +enum class EThumbnailFormat : uint16_t +{ + PNG, + JPG, + QOI +}; + +struct ThumbnailBlock +{ + uint16_t format{ 0 }; + uint16_t width{ 0 }; + uint16_t height{ 0 }; + std::vector data; + + // write block header and data + EResult write(FILE& file, EChecksumType checksum_type) const; + // read block data + EResult read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header); + + static size_t get_parameters_size() { return sizeof(format) + sizeof(width) + sizeof(height); } + +private: + void update_checksum(Checksum& checksum) const; +}; + +struct PrinterMetadataBlock : public BaseMetadataBlock +{ + // write block header and data + EResult write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const; + // read block data + EResult read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header); +}; + +struct PrintMetadataBlock : public BaseMetadataBlock +{ + // write block header and data + EResult write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const; + // read block data + EResult read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header); +}; + +struct SlicerMetadataBlock : public BaseMetadataBlock +{ + // write block header and data + EResult write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const; + // read block data + EResult read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header); +}; + +struct GCodeBlock : public BaseMetadataBlock +{ + // write block header and data + EResult write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const; + // read block data + EResult read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header); +}; + +#if ENABLE_CHECKSUM_BLOCK +struct ChecksumBlock +{ + std::vector data; + + // write block header and data + EResult write(FILE& file) const; + // read block data + EResult read_data(FILE& file, const BlockHeader& block_header); +}; +#endif // ENABLE_CHECKSUM_BLOCK + +//===================================================================================================================================== +// +// PRUSASLICER INTERFACE +// +//===================================================================================================================================== + +struct BinaryData +{ + FileMetadataBlock file_metadata; + PrinterMetadataBlock printer_metadata; + std::vector thumbnails; + SlicerMetadataBlock slicer_metadata; + PrintMetadataBlock print_metadata; + + void reset() { + file_metadata.raw_data.clear(); + printer_metadata.raw_data.clear(); + thumbnails.clear(); + slicer_metadata.raw_data.clear(); + print_metadata.raw_data.clear(); + } +}; + +class Binarizer +{ +public: + bool is_enabled() const { return m_enabled; } + void set_enabled(bool enable) { m_enabled = enable; } + void set_compression_type(ECompressionType type) { m_compression_type = type; } + + BinaryData& get_binary_data() { return m_binary_data; } + const BinaryData& get_binary_data() const { return m_binary_data; } + + EResult initialize(FILE& file, EChecksumType checksum_type); + EResult finalize(FILE& file); + +private: + bool m_enabled{ false }; + + EChecksumType m_checksum_type{ EChecksumType::None }; + ECompressionType m_compression_type{ ECompressionType::None }; + + BinaryData m_binary_data; +#if ENABLE_CHECKSUM_BLOCK + ChecksumBlock m_checksum; +#endif // ENABLE_CHECKSUM_BLOCK +}; + +//===================================================================================================================================== +// +// FIRMWARE INTERFACE +// +//===================================================================================================================================== + +// Get the max size of the cache used to calculate checksums, in bytes +size_t get_checksum_max_cache_size(); +// Set the max size of the cache used to calculate checksums, in bytes +void set_checksum_max_cache_size(size_t size); + +// Returns true if the given file is a valid binary gcode +// Does not modify the file position +extern bool is_valid_binary_gcode(FILE& file); + +// Reads the file header. +// If max_version is not null, version is checked against the passed value +// If return == EResult::Success: +// - header will contain the file header +// - file position will be set at the start of the 1st block header +extern EResult read_header(FILE& file, FileHeader& header, const uint32_t* const max_version); + +// Reads next block header from the current file position. +// File position must be at the start of a block header. +// If return == EResult::Success: +// - block_header will contain the header of the block +// - file position will be set at the start of the block parameters data +extern EResult read_next_block_header(FILE& file, const FileHeader& file_header, BlockHeader& block_header, bool verify_checksum); + +// Searches and reads next block header with the given type from the current file position. +// File position must be at the start of a block header. +// If return == EResult::Success: +// - block_header will contain the header of the block with the required type +// - file position will be set at the start of the block parameters data +// otherwise: +// - file position will keep the current value +extern EResult read_next_block_header(FILE& file, const FileHeader& file_header, BlockHeader& block_header, EBlockType type, bool verify_checksum); + +// Skips the payload (parameters + data) of the block with the given block header. +// File position must be at the start of the block parameters. +// If return == EResult::Success: +// - file position will be set at the start of the block checksum, if present, or of next block header +extern EResult skip_block_payload(FILE& file, const BlockHeader& block_header); + +// Skips the content (parameters + data + checksum) of the block with the given block header. +// File position must be at the start of the block parameters. +// If return == EResult::Success: +// - file position will be set at the start of the next block header +extern EResult skip_block_content(FILE& file, const FileHeader& file_header, const BlockHeader& block_header); + +// Returns the size of the parameters of the given block type, in bytes. +extern size_t block_parameters_size(EBlockType type); + +// Returns the size of the payload (parameters + data) of the block with the given header, in bytes. +extern size_t block_payload_size(const BlockHeader& block_header); + +// Returns the size of the checksum of the given type, in bytes. +extern size_t checksum_size(EChecksumType type); + +// Returns the size of the content (parameters + data + checksum) of the block with the given header, in bytes. +extern size_t block_content_size(const FileHeader& file_header, const BlockHeader& block_header); + +} // namespace BinaryGCode + +#endif // slic3r_GCode_GCodeBinarizer_hpp_ diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index f7fbb52a69..e6d97a3f8c 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -25,6 +25,10 @@ #endif #include +#if ENABLE_BINARIZED_GCODE_DEBUG +#include +#include +#endif // ENABLE_BINARIZED_GCODE_DEBUG static const float DEFAULT_TOOLPATH_WIDTH = 0.4f; static const float DEFAULT_TOOLPATH_HEIGHT = 0.2f; @@ -554,6 +558,10 @@ void GCodeProcessor::apply_config(const PrintConfig& config) { m_parser.apply_config(config); +#if ENABLE_BINARIZED_GCODE + m_binarizer.set_enabled(config.gcode_binary); +#endif // ENABLE_BINARIZED_GCODE + m_producer = EProducer::PrusaSlicer; m_flavor = config.gcode_flavor; @@ -1023,6 +1031,23 @@ static inline const char* remove_eols(const char *begin, const char *end) { // Load a G-code into a stand-alone G-code viewer. // throws CanceledException through print->throw_if_canceled() (sent by the caller as callback). void GCodeProcessor::process_file(const std::string& filename, std::function cancel_callback) +#if ENABLE_BINARIZED_GCODE +{ + FILE* file = boost::nowide::fopen(filename.c_str(), "rb"); + if (file == nullptr) + throw Slic3r::RuntimeError("Error opening the file: " + filename + "\n"); + + const bool is_binary = BinaryGCode::is_valid_binary_gcode(*file); + fclose(file); + + if (is_binary) + process_binary_file(filename, cancel_callback); + else + process_ascii_file(filename, cancel_callback); +} + +void GCodeProcessor::process_ascii_file(const std::string& filename, std::function cancel_callback) +#endif // ENABLE_BINARIZED_GCODE { CNumericLocalesSetter locales_setter; @@ -1090,6 +1115,163 @@ void GCodeProcessor::process_file(const std::string& filename, std::functionfinalize(false); } +#if ENABLE_BINARIZED_GCODE +void GCodeProcessor::process_binary_file(const std::string& filename, std::function cancel_callback) +{ + class ScopedFile + { + public: + explicit ScopedFile(FILE* file) : m_file(file) {} + ~ScopedFile() { if (m_file != nullptr) fclose(m_file); } + private: + FILE* m_file{ nullptr }; + }; + + FILE* file = boost::nowide::fopen(filename.c_str(), "rb"); + if (file == nullptr) + throw Slic3r::RuntimeError("Unable to open file: " + filename + "\n"); + + ScopedFile scoped_file(file); + + BinaryGCode::set_checksum_max_cache_size(1024); + + // read file header + BinaryGCode::FileHeader file_header; + BinaryGCode::EResult res = BinaryGCode::read_header(*file, file_header, &BinaryGCode::VERSION); + if (res != BinaryGCode::EResult::Success) + throw Slic3r::RuntimeError("File: " + filename + "does not contain a valid binary gcode\n Error: " + BinaryGCode::translate_result(res) + "\n"); + + const long first_block_header_position = ftell(file); + const bool verify_checksum = true; + + // read file metadata block + BinaryGCode::BlockHeader block_header; + res = BinaryGCode::read_next_block_header(*file, file_header, block_header, verify_checksum); + if (res != BinaryGCode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); + if ((BinaryGCode::EBlockType)block_header.type != BinaryGCode::EBlockType::FileMetadata) + throw Slic3r::RuntimeError("Unable to find file metadata block in file: " + filename + "\n"); + BinaryGCode::FileMetadataBlock file_metadata_block; + res = file_metadata_block.read_data(*file, file_header, block_header); + if (res != BinaryGCode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); + auto producer_it = std::find_if(file_metadata_block.raw_data.begin(), file_metadata_block.raw_data.end(), + [](const std::pair& item) { return item.first == "Producer"; }); + if (producer_it != file_metadata_block.raw_data.end() && boost::starts_with(producer_it->second, std::string(SLIC3R_APP_NAME))) + m_producer = EProducer::PrusaSlicer; + else + m_producer = EProducer::Unknown; + + // read printer metadata block + res = BinaryGCode::read_next_block_header(*file, file_header, block_header, verify_checksum); + if (res != BinaryGCode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); + if ((BinaryGCode::EBlockType)block_header.type != BinaryGCode::EBlockType::PrinterMetadata) + throw Slic3r::RuntimeError("Unable to find printer metadata block in file: " + filename + "\n"); + BinaryGCode::PrinterMetadataBlock printer_metadata_block; + res = printer_metadata_block.read_data(*file, file_header, block_header); + if (res != BinaryGCode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); +#if ENABLE_BINARIZED_GCODE_DEBUG + OutputDebugStringA("Printer metadata:\n"); + for (const auto& [key, value] : printer_metadata_block.raw_data) { + OutputDebugStringA(key.c_str()); + OutputDebugStringA("->"); + OutputDebugStringA(value.c_str()); + OutputDebugStringA("\n"); + } +#endif // ENABLE_BINARIZED_GCODE_DEBUG + + // read thumbnail blocks + res = BinaryGCode::read_next_block_header(*file, file_header, block_header, verify_checksum); + if (res != BinaryGCode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); + + while ((BinaryGCode::EBlockType)block_header.type == BinaryGCode::EBlockType::Thumbnail) { + BinaryGCode::ThumbnailBlock thumbnail_block; + res = thumbnail_block.read_data(*file, file_header, block_header); + if (res != BinaryGCode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); + +#if ENABLE_BINARIZED_GCODE_DEBUG + if (thumbnail_block.data.size() > 0) { + auto format_filename = [](const std::string& stem, const BinaryGCode::ThumbnailBlock& block) { + std::string ret = stem + "_" + std::to_string(block.width) + "x" + std::to_string(block.height); + switch ((BinaryGCode::EThumbnailFormat)block.format) + { + case BinaryGCode::EThumbnailFormat::PNG: { ret += ".png"; break; } + case BinaryGCode::EThumbnailFormat::JPG: { ret += ".jpg"; break; } + case BinaryGCode::EThumbnailFormat::QOI: { ret += ".qoi"; break; } + } + return ret; + }; + + const boost::filesystem::path path(filename); + const std::string out_path = path.parent_path().string(); + const std::string out_filename = out_path + "\\" + format_filename(path.stem().string(), thumbnail_block); + FILE* outfile = boost::nowide::fopen(out_filename.c_str(), "wb"); + if (outfile != nullptr) { + fwrite((const void*)thumbnail_block.data.data(), 1, thumbnail_block.data.size(), outfile); + fclose(outfile); + } + } +#endif // ENABLE_BINARIZED_GCODE_DEBUG + + res = BinaryGCode::read_next_block_header(*file, file_header, block_header, verify_checksum); + if (res != BinaryGCode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); + } + + // read slicer metadata block + if ((BinaryGCode::EBlockType)block_header.type != BinaryGCode::EBlockType::SlicerMetadata) + throw Slic3r::RuntimeError("Unable to find slicer metadata block in file: " + filename + "\n"); + BinaryGCode::SlicerMetadataBlock slicer_metadata_block; + res = slicer_metadata_block.read_data(*file, file_header, block_header); + if (res != BinaryGCode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); +#if ENABLE_BINARIZED_GCODE_DEBUG + OutputDebugStringA("Slicer metadata:\n"); + for (const auto& [key, value] : slicer_metadata_block.raw_data) { + OutputDebugStringA(key.c_str()); + OutputDebugStringA("->"); + OutputDebugStringA(value.c_str()); + OutputDebugStringA("\n"); + } +#endif // ENABLE_BINARIZED_GCODE_DEBUG + DynamicPrintConfig config; + config.apply(FullPrintConfig::defaults()); + std::string str; + for (const auto& [key, value] : slicer_metadata_block.raw_data) { + str += key + " = " + value + "\n"; + } + // Silently substitute unknown values by new ones for loading configurations from PrusaSlicer's own G-code. + // Showing substitution log or errors may make sense, but we are not really reading many values from the G-code config, + // thus a probability of incorrect substitution is low and the G-code viewer is a consumer-only anyways. + config.load_from_ini_string(str, ForwardCompatibilitySubstitutionRule::EnableSilent); + apply_config(config); + + // read print metadata block + res = BinaryGCode::read_next_block_header(*file, file_header, block_header, verify_checksum); + if (res != BinaryGCode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); + if ((BinaryGCode::EBlockType)block_header.type != BinaryGCode::EBlockType::PrintMetadata) + throw Slic3r::RuntimeError("Unable to find print metadata block in file: " + filename + "\n"); + BinaryGCode::PrintMetadataBlock print_metadata_block; + res = print_metadata_block.read_data(*file, file_header, block_header); + if (res != BinaryGCode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); +#if ENABLE_BINARIZED_GCODE_DEBUG + OutputDebugStringA("Print metadata:\n"); + for (const auto& [key, value] : print_metadata_block.raw_data) { + OutputDebugStringA(key.c_str()); + OutputDebugStringA("->"); + OutputDebugStringA(value.c_str()); + OutputDebugStringA("\n"); + } +#endif // ENABLE_BINARIZED_GCODE_DEBUG +} +#endif // ENABLE_BINARIZED_GCODE + void GCodeProcessor::initialize(const std::string& filename) { assert(is_decimal_separator_point()); @@ -3439,6 +3621,15 @@ void GCodeProcessor::post_process() throw Slic3r::RuntimeError(std::string("GCode processor post process export failed.\nCannot open file for writing.\n")); } +#if ENABLE_BINARIZED_GCODE + if (m_binarizer.is_enabled()) { + BinaryGCode::set_checksum_max_cache_size(4096); + const BinaryGCode::EResult res = m_binarizer.initialize(*out.f, BinaryGCode::EChecksumType::CRC32); + if (res != BinaryGCode::EResult::Success) + throw Slic3r::RuntimeError(std::string("Unable to initialize the gcode binarizer.\n")); + } +#endif // ENABLE_BINARIZED_GCODE + auto time_in_minutes = [](float time_in_seconds) { assert(time_in_seconds >= 0.f); return int((time_in_seconds + 0.5f) / 60.0f); @@ -3555,13 +3746,26 @@ void GCodeProcessor::post_process() size_t m_curr_g1_id{ 0 }; size_t m_out_file_pos{ 0 }; +#if ENABLE_BINARIZED_GCODE + BinaryGCode::Binarizer& m_binarizer; +#endif // ENABLE_BINARIZED_GCODE + public: +#if ENABLE_BINARIZED_GCODE + ExportLines(BinaryGCode::Binarizer& binarizer, EWriteType type, TimeMachine& machine) +#ifndef NDEBUG + : m_statistics(*this), m_binarizer(binarizer), m_write_type(type), m_machine(machine) {} +#else + : m_binarizer(binarizer), m_write_type(type), m_machine(machine) {} +#endif // NDEBUG +#else ExportLines(EWriteType type, TimeMachine& machine) #ifndef NDEBUG : m_statistics(*this), m_write_type(type), m_machine(machine) {} #else : m_write_type(type), m_machine(machine) {} #endif // NDEBUG +#endif // ENABLE_BINARIZED_GCODE void update(size_t lines_counter, size_t g1_lines_counter) { m_gcode_lines_map.push_back({ lines_counter, 0 }); @@ -3707,12 +3911,18 @@ void GCodeProcessor::post_process() private: void write_to_file(FilePtr& out, const std::string& out_string, GCodeProcessorResult& result, const std::string& out_path) { if (!out_string.empty()) { - fwrite((const void*)out_string.c_str(), 1, out_string.length(), out.f); - if (ferror(out.f)) { - out.close(); - boost::nowide::remove(out_path.c_str()); - throw Slic3r::RuntimeError(std::string("GCode processor post process export failed.\nIs the disk full?\n")); +#if ENABLE_BINARIZED_GCODE + if (!m_binarizer.is_enabled()) { +#endif // ENABLE_BINARIZED_GCODE + fwrite((const void*)out_string.c_str(), 1, out_string.length(), out.f); + if (ferror(out.f)) { + out.close(); + boost::nowide::remove(out_path.c_str()); + throw Slic3r::RuntimeError(std::string("GCode processor post process export failed.\nIs the disk full?\n")); + } +#if ENABLE_BINARIZED_GCODE } +#endif // ENABLE_BINARIZED_GCODE for (size_t i = 0; i < out_string.size(); ++i) { if (out_string[i] == '\n') result.lines_ends.emplace_back(m_out_file_pos + i + 1); @@ -3722,7 +3932,11 @@ void GCodeProcessor::post_process() } }; +#if ENABLE_BINARIZED_GCODE + ExportLines export_lines(m_binarizer, m_result.backtrace_enabled ? ExportLines::EWriteType::ByTime : ExportLines::EWriteType::BySize, m_time_processor.machines[0]); +#else ExportLines export_lines(m_result.backtrace_enabled ? ExportLines::EWriteType::ByTime : ExportLines::EWriteType::BySize, m_time_processor.machines[0]); +#endif // ENABLE_BINARIZED_GCODE // replace placeholder lines with the proper final value // gcode_line is in/out parameter, to reduce expensive memory allocation @@ -4035,6 +4249,46 @@ void GCodeProcessor::post_process() export_lines.flush(out, m_result, out_path); +#if ENABLE_BINARIZED_GCODE + if (m_binarizer.is_enabled()) { + // update print metadata + auto update_value = [](std::string& value, const std::vector& values) { + char buf[1024]; + value.clear(); + for (size_t i = 0; i < values.size(); ++i) { + sprintf(buf, i == values.size() - 1 ? " %.2lf" : " %.2lf,", values[i]); + value += buf; + } + }; + + // update binary data + BinaryGCode::BinaryData& binary_data = m_binarizer.get_binary_data(); + for (auto& [key, value] : binary_data.print_metadata.raw_data) { + if (key == "filament used [mm]") update_value(value, filament_mm); + else if (key == "filament used [g]") update_value(value, filament_g); + else if (key == "total filament used [g]") update_value(value, { filament_total_g }); + else if (key == "filament used [cm3]") update_value(value, filament_cm3); + else if (key == "filament cost") update_value(value, filament_cost); + else if (key == "total filament cost") update_value(value, { filament_total_cost }); + } + + for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { + const TimeMachine& machine = m_time_processor.machines[i]; + PrintEstimatedStatistics::ETimeMode mode = static_cast(i); + if (mode == PrintEstimatedStatistics::ETimeMode::Normal || machine.enabled) { + char buf[128]; + sprintf(buf, "(%s mode)", (mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent"); + binary_data.print_metadata.raw_data.push_back({ "estimated printing time " + std::string(buf), get_time_dhms(machine.time) }); + binary_data.print_metadata.raw_data.push_back({ "estimated first layer printing time " + std::string(buf), get_time_dhms(machine.layers_time.empty() ? 0.f : machine.layers_time.front()) }); + } + } + + const BinaryGCode::EResult res = m_binarizer.finalize(*out.f); + if (res != BinaryGCode::EResult::Success) + throw Slic3r::RuntimeError(std::string("Error while finalizing the gcode binarizer.\n")); + } +#endif // ENABLE_BINARIZED_GCODE + out.close(); in.close(); diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index fcba4321ae..fe2db07f99 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -7,6 +7,10 @@ #include "libslic3r/PrintConfig.hpp" #include "libslic3r/CustomGCode.hpp" +#if ENABLE_BINARIZED_GCODE +#include "GCodeBinarizer.hpp" +#endif // ENABLE_BINARIZED_GCODE + #include #include #include @@ -525,6 +529,9 @@ namespace Slic3r { private: GCodeReader m_parser; +#if ENABLE_BINARIZED_GCODE + BinaryGCode::Binarizer m_binarizer; +#endif // ENABLE_BINARIZED_GCODE EUnits m_units; EPositioningType m_global_positioning_type; @@ -622,6 +629,10 @@ namespace Slic3r { void apply_config(const PrintConfig& config); void set_print(Print* print) { m_print = print; } +#if ENABLE_BINARIZED_GCODE + BinaryGCode::BinaryData& get_binary_data() { return m_binarizer.get_binary_data(); } + const BinaryGCode::BinaryData& get_binary_data() const { return m_binarizer.get_binary_data(); } +#endif // ENABLE_BINARIZED_GCODE void enable_stealth_time_estimator(bool enabled); bool is_stealth_time_estimator_enabled() const { @@ -664,6 +675,11 @@ namespace Slic3r { void apply_config_kissslicer(const std::string& filename); void process_gcode_line(const GCodeReader::GCodeLine& line, bool producers_enabled); +#if ENABLE_BINARIZED_GCODE + void process_ascii_file(const std::string& filename, std::function cancel_callback = nullptr); + void process_binary_file(const std::string& filename, std::function cancel_callback = nullptr); +#endif // ENABLE_BINARIZED_GCODE + // Process tags embedded into comments void process_tags(const std::string_view comment, bool producers_enabled); bool process_producers_tags(const std::string_view comment); diff --git a/src/libslic3r/GCode/Thumbnails.hpp b/src/libslic3r/GCode/Thumbnails.hpp index 30bb6b653b..b2b5d60cb1 100644 --- a/src/libslic3r/GCode/Thumbnails.hpp +++ b/src/libslic3r/GCode/Thumbnails.hpp @@ -4,6 +4,9 @@ #include "../Point.hpp" #include "../PrintConfig.hpp" #include "ThumbnailData.hpp" +#if ENABLE_BINARIZED_GCODE +#include "GCode/GCodeBinarizer.hpp" +#endif // ENABLE_BINARIZED_GCODE #include #include @@ -55,6 +58,35 @@ inline void export_thumbnails_to_file(ThumbnailsGeneratorCallback &thumbnail_cb, } } +#if ENABLE_BINARIZED_GCODE +template +inline void generate_binary_thumbnails(ThumbnailsGeneratorCallback& thumbnail_cb, std::vector& out_thumbnails, + const std::vector& sizes, GCodeThumbnailsFormat format, ThrowIfCanceledCallback throw_if_canceled) +{ + out_thumbnails.clear(); + if (thumbnail_cb != nullptr) { + ThumbnailsList thumbnails = thumbnail_cb(ThumbnailsParams{ sizes, true, true, true, true }); + for (const ThumbnailData& data : thumbnails) { + if (data.is_valid()) { + auto compressed = compress_thumbnail(data, format); + if (compressed->data != nullptr && compressed->size > 0) { + BinaryGCode::ThumbnailBlock& block = out_thumbnails.emplace_back(BinaryGCode::ThumbnailBlock()); + block.width = (uint16_t)data.width; + block.height = (uint16_t)data.height; + switch (format) { + case GCodeThumbnailsFormat::PNG: { block.format = (uint16_t)BinaryGCode::EThumbnailFormat::PNG; break; } + case GCodeThumbnailsFormat::JPG: { block.format = (uint16_t)BinaryGCode::EThumbnailFormat::JPG; break; } + case GCodeThumbnailsFormat::QOI: { block.format = (uint16_t)BinaryGCode::EThumbnailFormat::QOI; break; } + } + block.data.resize(compressed->size); + memcpy(block.data.data(), compressed->data, compressed->size); + } + } + } + } +} +#endif // ENABLE_BINARIZED_GCODE + } // namespace Slic3r::GCodeThumbnails #endif // slic3r_GCodeThumbnails_hpp_ diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index f42b3f7708..5b1e86c321 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -449,7 +449,11 @@ static std::vector s_Preset_print_options { "support_tree_angle", "support_tree_angle_slow", "support_tree_branch_diameter", "support_tree_branch_diameter_angle", "support_tree_branch_diameter_double_wall", "support_tree_top_rate", "support_tree_branch_distance", "support_tree_tip_diameter", "dont_support_bridges", "thick_bridges", "notes", "complete_objects", "extruder_clearance_radius", +#if ENABLE_BINARIZED_GCODE + "extruder_clearance_height", "gcode_comments", "gcode_label_objects", "output_filename_format", "post_process", "gcode_substitutions", "gcode_binary", "perimeter_extruder", +#else "extruder_clearance_height", "gcode_comments", "gcode_label_objects", "output_filename_format", "post_process", "gcode_substitutions", "perimeter_extruder", +#endif // ENABLE_BINARIZED_GCODE "infill_extruder", "solid_infill_extruder", "support_material_extruder", "support_material_interface_extruder", "ooze_prevention", "standby_temperature_delta", "interface_shells", "extrusion_width", "first_layer_extrusion_width", "perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width", diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 8d53dbb5a8..2bd92340d8 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -117,6 +117,9 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n "perimeter_acceleration", "post_process", "gcode_substitutions", +#if ENABLE_BINARIZED_GCODE + "gcode_binary", +#endif // ENABLE_BINARIZED_GCODE "printer_notes", "retract_before_travel", "retract_before_wipe", diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 3da804066b..dd9e4f0578 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -1431,6 +1431,14 @@ void PrintConfigDef::init_fff_params() def->mode = comExpert; def->set_default_value(new ConfigOptionStrings()); +#if ENABLE_BINARIZED_GCODE + def = this->add("gcode_binary", coBool); + def->label = L("Export as binary G-code"); + def->tooltip = L("Exports the G-code in binary format."); + def->mode = comExpert; + def->set_default_value(new ConfigOptionBool(0)); +#endif // ENABLE_BINARIZED_GCODE + def = this->add("high_current_on_filament_swap", coBool); def->label = L("High extruder current on filament swap"); def->tooltip = L("It may be beneficial to increase the extruder motor current during the filament exchange" diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 54a835fe79..806cdc500f 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -698,6 +698,9 @@ PRINT_CONFIG_CLASS_DEFINE( // i - case insensitive // w - whole word ((ConfigOptionStrings, gcode_substitutions)) +//#if ENABLE_BINARIZED_GCODE + ((ConfigOptionBool, gcode_binary)) +//#endif // ENABLE_BINARIZED_GCODE ((ConfigOptionString, layer_gcode)) ((ConfigOptionFloat, max_print_speed)) ((ConfigOptionFloat, max_volumetric_speed)) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 602654633c..92b6f8578c 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -62,4 +62,13 @@ #define ENABLE_ALTERNATIVE_FILE_WILDCARDS_GENERATOR (1 && ENABLE_2_6_0_ALPHA1) +//==================== +// 2.6.1.alpha1 techs +//==================== +#define ENABLE_2_6_1_ALPHA1 1 + +// Enable export of binarized gcode +#define ENABLE_BINARIZED_GCODE (1 && ENABLE_2_6_1_ALPHA1) + + #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index ad93afa879..5a3152df4c 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1673,6 +1673,9 @@ void TabPrint::build() Option option = optgroup->get_option("output_filename_format"); option.opt.full_width = true; optgroup->append_single_option_line(option); +#if ENABLE_BINARIZED_GCODE + optgroup->append_single_option_line("gcode_binary"); +#endif // ENABLE_BINARIZED_GCODE optgroup = page->new_optgroup(L("Other")); From 3f5de75bae62a08da849b875640d5b616f818158 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 20 Jul 2023 14:20:00 +0200 Subject: [PATCH 019/136] SPE-1784: New compressed (binary) gcode format integration Added GCode Block save/load with no encoding Changed blocks order to: File metadata|Printer metadata|Thumbnails[]|Print metadata|Slicer metadata|GCode[] --- src/libslic3r/GCode.cpp | 188 ++++++++++--------- src/libslic3r/GCode/GCodeBinarizer.cpp | 243 ++++++++++++++++++++++--- src/libslic3r/GCode/GCodeBinarizer.hpp | 24 ++- src/libslic3r/GCode/GCodeProcessor.cpp | 208 +++++++++++++++------ src/libslic3r/Print.cpp | 22 +++ src/libslic3r/Print.hpp | 17 ++ 6 files changed, 533 insertions(+), 169 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 6f148cc695..f6b772e946 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -997,94 +997,107 @@ namespace DoExport { { std::string filament_stats_string_out; - print_statistics.clear(); + print_statistics.clear(); print_statistics.total_toolchanges = std::max(0, wipe_tower_data.number_of_toolchanges); print_statistics.initial_extruder_id = initial_extruder_id; std::vector filament_types; - if (! extruders.empty()) { - std::pair out_filament_used_mm ("; filament used [mm] = ", 0); - std::pair out_filament_used_cm3("; filament used [cm3] = ", 0); - std::pair out_filament_used_g ("; filament used [g] = ", 0); - std::pair out_filament_cost ("; filament cost = ", 0); - for (const Extruder &extruder : extruders) { - print_statistics.printing_extruders.emplace_back(extruder.id()); - filament_types.emplace_back(config.filament_type.get_at(extruder.id())); + if (! extruders.empty()) { +#if ENABLE_BINARIZED_GCODE + std::pair out_filament_used_mm(PrintStatistics::FilamentUsedMmMask + " ", 0); + std::pair out_filament_used_cm3(PrintStatistics::FilamentUsedCm3Mask + " ", 0); + std::pair out_filament_used_g(PrintStatistics::FilamentUsedGMask + " ", 0); + std::pair out_filament_cost(PrintStatistics::FilamentCostMask + " ", 0); +#else + std::pair out_filament_used_mm("; filament used [mm] = ", 0); + std::pair out_filament_used_cm3("; filament used [cm3] = ", 0); + std::pair out_filament_used_g ("; filament used [g] = ", 0); + std::pair out_filament_cost("; filament cost = ", 0); +#endif // ENABLE_BINARIZED_GCODE + for (const Extruder &extruder : extruders) { + print_statistics.printing_extruders.emplace_back(extruder.id()); + filament_types.emplace_back(config.filament_type.get_at(extruder.id())); - double used_filament = extruder.used_filament() + (has_wipe_tower ? wipe_tower_data.used_filament[extruder.id()] : 0.f); - double extruded_volume = extruder.extruded_volume() + (has_wipe_tower ? wipe_tower_data.used_filament[extruder.id()] * 2.4052f : 0.f); // assumes 1.75mm filament diameter - double filament_weight = extruded_volume * extruder.filament_density() * 0.001; - double filament_cost = filament_weight * extruder.filament_cost() * 0.001; - auto append = [&extruder](std::pair &dst, const char *tmpl, double value) { - assert(is_decimal_separator_point()); - while (dst.second < extruder.id()) { - // Fill in the non-printing extruders with zeros. - dst.first += (dst.second > 0) ? ", 0" : "0"; - ++ dst.second; - } - if (dst.second > 0) - dst.first += ", "; - char buf[64]; - sprintf(buf, tmpl, value); - dst.first += buf; - ++ dst.second; - }; + double used_filament = extruder.used_filament() + (has_wipe_tower ? wipe_tower_data.used_filament[extruder.id()] : 0.f); + double extruded_volume = extruder.extruded_volume() + (has_wipe_tower ? wipe_tower_data.used_filament[extruder.id()] * 2.4052f : 0.f); // assumes 1.75mm filament diameter + double filament_weight = extruded_volume * extruder.filament_density() * 0.001; + double filament_cost = filament_weight * extruder.filament_cost() * 0.001; + auto append = [&extruder](std::pair &dst, const char *tmpl, double value) { + assert(is_decimal_separator_point()); + while (dst.second < extruder.id()) { + // Fill in the non-printing extruders with zeros. + dst.first += (dst.second > 0) ? ", 0" : "0"; + ++ dst.second; + } + if (dst.second > 0) + dst.first += ", "; + char buf[64]; + sprintf(buf, tmpl, value); + dst.first += buf; + ++ dst.second; + }; #if ENABLE_BINARIZED_GCODE - if (export_binary_data) { - char buf[128]; - sprintf(buf, "%.2lf", used_filament); - binary_data.print_metadata.raw_data.push_back({ "filament used [mm]", std::string(buf) }); - sprintf(buf, "%.2lf", extruded_volume * 0.001); - binary_data.print_metadata.raw_data.push_back({ "filament used [cm3]", std::string(buf) }); - } - else { + if (export_binary_data) { + char buf[128]; + sprintf(buf, "%.2lf", used_filament); + binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedMm, std::string(buf) }); + sprintf(buf, "%.2lf", extruded_volume * 0.001); + binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedCm3, std::string(buf) }); + } + else { #endif // ENABLE_BINARIZED_GCODE - append(out_filament_used_mm, "%.2lf", used_filament); - append(out_filament_used_cm3, "%.2lf", extruded_volume * 0.001); + append(out_filament_used_mm, "%.2lf", used_filament); + append(out_filament_used_cm3, "%.2lf", extruded_volume * 0.001); #if ENABLE_BINARIZED_GCODE - } + } #endif // ENABLE_BINARIZED_GCODE - if (filament_weight > 0.) { - print_statistics.total_weight = print_statistics.total_weight + filament_weight; + if (filament_weight > 0.) { + print_statistics.total_weight = print_statistics.total_weight + filament_weight; #if ENABLE_BINARIZED_GCODE - if (export_binary_data) { - char buf[128]; - sprintf(buf, "%.2lf", filament_weight); - binary_data.print_metadata.raw_data.push_back({ "filament used [g]", std::string(buf) }); - } - else + if (export_binary_data) { + char buf[128]; + sprintf(buf, "%.2lf", filament_weight); + binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedG, std::string(buf) }); + } + else #endif // ENABLE_BINARIZED_GCODE - append(out_filament_used_g, "%.2lf", filament_weight); - if (filament_cost > 0.) { - print_statistics.total_cost = print_statistics.total_cost + filament_cost; + append(out_filament_used_g, "%.2lf", filament_weight); + if (filament_cost > 0.) { + print_statistics.total_cost = print_statistics.total_cost + filament_cost; #if ENABLE_BINARIZED_GCODE - if (export_binary_data) { - char buf[128]; - sprintf(buf, "%.2lf", filament_cost); - binary_data.print_metadata.raw_data.push_back({ "filament cost", std::string(buf) }); - } - else + if (export_binary_data) { + char buf[128]; + sprintf(buf, "%.2lf", filament_cost); + binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentCost, std::string(buf) }); + } + else #endif // ENABLE_BINARIZED_GCODE - append(out_filament_cost, "%.2lf", filament_cost); - } - } - print_statistics.total_used_filament += used_filament; - print_statistics.total_extruded_volume += extruded_volume; - print_statistics.total_wipe_tower_filament += has_wipe_tower ? used_filament - extruder.used_filament() : 0.; - print_statistics.total_wipe_tower_cost += has_wipe_tower ? (extruded_volume - extruder.extruded_volume())* extruder.filament_density() * 0.001 * extruder.filament_cost() * 0.001 : 0.; - } - filament_stats_string_out += out_filament_used_mm.first; - filament_stats_string_out += "\n" + out_filament_used_cm3.first; - if (out_filament_used_g.second) - filament_stats_string_out += "\n" + out_filament_used_g.first; - if (out_filament_cost.second) - filament_stats_string_out += "\n" + out_filament_cost.first; - print_statistics.initial_filament_type = config.filament_type.get_at(initial_extruder_id); - std::sort(filament_types.begin(), filament_types.end()); - print_statistics.printing_filament_types = filament_types.front(); - for (size_t i = 1; i < filament_types.size(); ++ i) { - print_statistics.printing_filament_types += ","; - print_statistics.printing_filament_types += filament_types[i]; - } + append(out_filament_cost, "%.2lf", filament_cost); + } + } + print_statistics.total_used_filament += used_filament; + print_statistics.total_extruded_volume += extruded_volume; + print_statistics.total_wipe_tower_filament += has_wipe_tower ? used_filament - extruder.used_filament() : 0.; + print_statistics.total_wipe_tower_cost += has_wipe_tower ? (extruded_volume - extruder.extruded_volume())* extruder.filament_density() * 0.001 * extruder.filament_cost() * 0.001 : 0.; + } +#if ENABLE_BINARIZED_GCODE + if (!export_binary_data) { +#endif // ENABLE_BINARIZED_GCODE + filament_stats_string_out += out_filament_used_mm.first; + filament_stats_string_out += "\n" + out_filament_used_cm3.first; + if (out_filament_used_g.second) + filament_stats_string_out += "\n" + out_filament_used_g.first; + if (out_filament_cost.second) + filament_stats_string_out += "\n" + out_filament_cost.first; +#if ENABLE_BINARIZED_GCODE + } +#endif // ENABLE_BINARIZED_GCODE + print_statistics.initial_filament_type = config.filament_type.get_at(initial_extruder_id); + std::sort(filament_types.begin(), filament_types.end()); + print_statistics.printing_filament_types = filament_types.front(); + for (size_t i = 1; i < filament_types.size(); ++ i) { + print_statistics.printing_filament_types += ","; + print_statistics.printing_filament_types += filament_types[i]; + } } return filament_stats_string_out; } @@ -1216,8 +1229,11 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato this->m_avoid_crossing_curled_overhangs.init_bed_shape(get_bed_shape(print.config())); } - // Write information on the generator. - file.write_format("; %s\n\n", Slic3r::header_slic3r_generated().c_str()); +#if ENABLE_BINARIZED_GCODE + if (!export_to_binary_gcode) +#endif // ENABLE_BINARIZED_GCODE + // Write information on the generator. + file.write_format("; %s\n\n", Slic3r::header_slic3r_generated().c_str()); #if ENABLE_BINARIZED_GCODE // if exporting gcode in ascii format, generate the thumbnails here @@ -1582,7 +1598,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // Get filament stats. #if ENABLE_BINARIZED_GCODE - file.write(DoExport::update_print_stats_and_format_filament_stats( + const std::string filament_stats_string_out = DoExport::update_print_stats_and_format_filament_stats( // Const inputs has_wipe_tower, print.wipe_tower_data(), this->config(), @@ -1592,15 +1608,18 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato print.m_print_statistics, export_to_binary_gcode, m_processor.get_binary_data() - )); + ); + + if (!export_to_binary_gcode) + file.write(filament_stats_string_out); if (export_to_binary_gcode) { BinaryGCode::BinaryData& binary_data = m_processor.get_binary_data(); char buf[128]; sprintf(buf, "%.2lf", print.m_print_statistics.total_weight); - binary_data.print_metadata.raw_data.push_back({ "total filament used [g]", std::string(buf) }); + binary_data.print_metadata.raw_data.push_back({ PrintStatistics::TotalFilamentUsedG, std::string(buf) }); sprintf(buf, "%.2lf", print.m_print_statistics.total_cost); - binary_data.print_metadata.raw_data.push_back({ "total filament cost", std::string(buf) }); + binary_data.print_metadata.raw_data.push_back({ PrintStatistics::TotalFilamentCost, std::string(buf) }); if (print.m_print_statistics.total_toolchanges > 0) binary_data.print_metadata.raw_data.push_back({ "total toolchanges", std::to_string(print.m_print_statistics.total_toolchanges) }); } @@ -1619,8 +1638,13 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // if exporting gcode in ascii format, statistics export is done here #endif // ENABLE_BINARIZED_GCODE file.write("\n"); +#if ENABLE_BINARIZED_GCODE + file.write_format(PrintStatistics::TotalFilamentUsedGValueMask.c_str(), print.m_print_statistics.total_weight); + file.write_format(PrintStatistics::TotalFilamentCostValueMask.c_str(), print.m_print_statistics.total_cost); +#else file.write_format("; total filament used [g] = %.2lf\n", print.m_print_statistics.total_weight); file.write_format("; total filament cost = %.2lf\n", print.m_print_statistics.total_cost); +#endif // ENABLE_BINARIZED_GCODE if (print.m_print_statistics.total_toolchanges > 0) file.write_format("; total toolchanges = %i\n", print.m_print_statistics.total_toolchanges); file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Estimated_Printing_Time_Placeholder).c_str()); diff --git a/src/libslic3r/GCode/GCodeBinarizer.cpp b/src/libslic3r/GCode/GCodeBinarizer.cpp index 9a205f5327..ddb1816368 100644 --- a/src/libslic3r/GCode/GCodeBinarizer.cpp +++ b/src/libslic3r/GCode/GCodeBinarizer.cpp @@ -12,6 +12,7 @@ namespace BinaryGCode { static size_t g_checksum_max_cache_size = 65536; +static const size_t MAX_GCODE_CACHE_SIZE = 65536; std::string translate_result(BinaryGCode::EResult result) { @@ -26,10 +27,13 @@ std::string translate_result(BinaryGCode::EResult result) case BinaryGCode::EResult::InvalidBlockType: { return "Invalid block type"; } case BinaryGCode::EResult::InvalidCompressionType: { return "Invalid compression type"; } case BinaryGCode::EResult::InvalidMetadataEncodingType: { return "Invalid metadata encoding type"; } + case BinaryGCode::EResult::InvalidGCodeEncodingType: { return "Invalid gcode encoding type"; } case BinaryGCode::EResult::DataCompressionError: { return "Data compression error"; } case BinaryGCode::EResult::DataUncompressionError: { return "Data uncompression error"; } case BinaryGCode::EResult::MetadataEncodingError: { return "Data encoding error"; } case BinaryGCode::EResult::MetadataDecodingError: { return "Data decoding error"; } + case BinaryGCode::EResult::GCodeEncodingError: { return "GCode encoding error"; } + case BinaryGCode::EResult::GCodeDecodingError: { return "GCode decoding error"; } case BinaryGCode::EResult::BlockNotFound: { return "Block not found"; } case BinaryGCode::EResult::InvalidChecksum: { return "Invalid checksum"; } case BinaryGCode::EResult::InvalidThumbnailFormat: { return "Invalid thumbnail format"; } @@ -48,6 +52,7 @@ static uint16_t block_types_count() { return 1 + (uint16_t)EBlockTyp static uint16_t compression_types_count() { return 1 + (uint16_t)ECompressionType::None; } static uint16_t thumbnail_formats_count() { return 1 + (uint16_t)EThumbnailFormat::QOI; } static uint16_t metadata_encoding_types_count() { return 1 + (uint16_t)EMetadataEncodingType::INI; } +static uint16_t gcode_encoding_types_count() { return 1 + (uint16_t)EGCodeEncodingType::MeatPack; } static bool write_to_file(FILE& file, const void* data, size_t data_size) { @@ -61,43 +66,60 @@ static bool read_from_file(FILE& file, void* data, size_t data_size) return !ferror(&file); } -static bool encode_metadata(const std::vector>& data_in, std::vector& data_out, +static bool encode_metadata(const std::vector>& src, std::vector& dst, EMetadataEncodingType encoding_type) { - for (const auto& [key, value] : data_in) { + for (const auto& [key, value] : src) { switch (encoding_type) { case EMetadataEncodingType::INI: { - data_out.insert(data_out.end(), key.begin(), key.end()); - data_out.emplace_back('='); - data_out.insert(data_out.end(), value.begin(), value.end()); - data_out.emplace_back('\n'); + dst.insert(dst.end(), key.begin(), key.end()); + dst.emplace_back('='); + dst.insert(dst.end(), value.begin(), value.end()); + dst.emplace_back('\n'); break; } } } - return true; } -static bool decode_metadata(const std::vector& data_in, std::vector>& data_out, +static bool encode_gcode(const std::string& src, std::vector& dst, EGCodeEncodingType encoding_type) +{ + switch (encoding_type) + { + case EGCodeEncodingType::None: + { + dst.insert(dst.end(), src.begin(), src.end()); + break; + } + case EGCodeEncodingType::MeatPack: + { + // TODO + break; + } + } + return true; +} + +static bool decode_metadata(const std::vector& src, std::vector>& dst, EMetadataEncodingType encoding_type) { switch (encoding_type) { case EMetadataEncodingType::INI: { - auto start_it = data_in.begin(); - auto end_it = data_in.begin(); - while (end_it != data_in.end()) { - while (end_it != data_in.end() && *end_it != '\n') { + auto start_it = src.begin(); + auto end_it = src.begin(); + while (end_it != src.end()) { + while (end_it != src.end() && *end_it != '\n') { ++end_it; } const std::string item(start_it, end_it); const size_t pos = item.find_first_of('='); if (pos != std::string::npos) { - data_out.emplace_back(std::make_pair(item.substr(0, pos), item.substr(pos + 1))); + dst.emplace_back(std::make_pair(item.substr(0, pos), item.substr(pos + 1))); start_it = ++end_it; } } @@ -108,12 +130,30 @@ static bool decode_metadata(const std::vector& data_in, std::vector& data_in, std::vector& data_out, ECompressionType compression_type) +static bool decode_gcode(const std::vector& src, std::string& dst, EGCodeEncodingType encoding_type) +{ + switch (encoding_type) + { + case EGCodeEncodingType::None: + { + dst.insert(dst.end(), src.begin(), src.end()); + break; + } + case EGCodeEncodingType::MeatPack: + { + // TODO + break; + } + } + return true; +} + +static bool compress(const std::vector& src, std::vector& data_out, ECompressionType compression_type) { return true; } -static bool uncompress(const std::vector& data_in, std::vector& data_out, ECompressionType compression_type) +static bool uncompress(const std::vector& src, std::vector& data_out, ECompressionType compression_type) { return true; } @@ -357,7 +397,6 @@ EResult BaseMetadataBlock::read_data(FILE& file, const BlockHeader& block_header if (!read_from_file(file, (void*)&encoding_type, sizeof(encoding_type))) return EResult::ReadError; if (encoding_type > metadata_encoding_types_count()) - // Found invalid metadata encoding type return EResult::InvalidMetadataEncodingType; std::vector data; @@ -620,11 +659,99 @@ EResult SlicerMetadataBlock::read_data(FILE& file, const FileHeader& file_header EResult GCodeBlock::write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const { + if (encoding_type > gcode_encoding_types_count()) + return EResult::InvalidGCodeEncodingType; + + BlockHeader block_header = { (uint16_t)EBlockType::GCode, (uint16_t)compression_type, (uint32_t)0 }; + std::vector out_data; + if (!raw_data.empty()) { + // process payload encoding + std::vector uncompressed_data; + if (!encode_gcode(raw_data, uncompressed_data, (EGCodeEncodingType)encoding_type)) + return EResult::GCodeEncodingError; + // process payload compression + block_header.uncompressed_size = (uint32_t)uncompressed_data.size(); + std::vector compressed_data; + if (compression_type != ECompressionType::None) { + if (!compress(uncompressed_data, compressed_data, compression_type)) + return EResult::DataCompressionError; + block_header.compressed_size = (uint32_t)compressed_data.size(); + } + out_data.swap((compression_type == ECompressionType::None) ? uncompressed_data : compressed_data); + } + + // write block header + EResult res = block_header.write(file); + if (res != EResult::Success) + // propagate error + return res; + + // write block payload + if (!write_to_file(file, (const void*)&encoding_type, sizeof(encoding_type))) + return EResult::WriteError; + if (!out_data.empty()) { +#if ENABLE_BINARIZED_GCODE_DEBUG + const std::string out = "GCodeBlock data size:" + std::to_string(out_data.size()) + "\n"; + OutputDebugStringA(out.c_str()); +#endif // ENABLE_BINARIZED_GCODE_DEBUG + if (!write_to_file(file, (const void*)out_data.data(), out_data.size())) + return EResult::WriteError; + } + + // write checksum + if (checksum_type != EChecksumType::None) { + Checksum cs(checksum_type); + // update checksum with block header + block_header.update_checksum(cs); + // update checksum with block payload + cs.append(encode((const void*)&encoding_type, sizeof(encoding_type))); + if (!out_data.empty()) + cs.append(out_data); + res = cs.write(file); + if (res != EResult::Success) + // propagate error + return res; + } + return EResult::Success; } EResult GCodeBlock::read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header) { + const ECompressionType compression_type = (ECompressionType)block_header.compression; + + if (!read_from_file(file, (void*)&encoding_type, sizeof(encoding_type))) + return EResult::ReadError; + if (encoding_type > gcode_encoding_types_count()) + return EResult::InvalidGCodeEncodingType; + + std::vector data; + const size_t data_size = (compression_type == ECompressionType::None) ? block_header.uncompressed_size : block_header.compressed_size; + if (data_size > 0) { + data.resize(data_size); + if (!read_from_file(file, (void*)data.data(), data_size)) + return EResult::ReadError; + } + + std::vector uncompressed_data; + if (compression_type != ECompressionType::None) { + if (!uncompress(data, uncompressed_data, compression_type)) + return EResult::DataUncompressionError; + } + + if (!decode_gcode((compression_type == ECompressionType::None) ? data : uncompressed_data, raw_data, (EGCodeEncodingType)encoding_type)) + return EResult::GCodeDecodingError; + + const EChecksumType checksum_type = (EChecksumType)file_header.checksum_type; + if (checksum_type != EChecksumType::None) { + // read block checksum + Checksum cs(checksum_type); + const EResult res = cs.read(file); + if (res != EResult::Success) + // propagate error + return res; + } + return EResult::Success; } @@ -660,60 +787,116 @@ EResult ChecksumBlock::read_data(FILE& file, const BlockHeader& block_header) } #endif // ENABLE_CHECKSUM_BLOCK -EResult Binarizer::initialize(FILE& file, EChecksumType checksum_type) +EResult Binarizer::initialize(FILE& file, EGCodeEncodingType gcode_encoding_type, EChecksumType checksum_type) { if (!m_enabled) return EResult::Success; - // initialize checksum + m_file = &file; + + m_gcode_encoding_type = gcode_encoding_type; m_checksum_type = checksum_type; #if ENABLE_CHECKSUM_BLOCK + // initialize checksum m_checksum = ChecksumBlock(); #endif // ENABLE_CHECKSUM_BLOCK // save header FileHeader file_header; file_header.checksum_type = (uint16_t)m_checksum_type; - EResult res = file_header.write(file); + EResult res = file_header.write(*m_file); if (res != EResult::Success) return res; // save file metadata block - res = m_binary_data.file_metadata.write(file, ECompressionType::None, m_checksum_type); + res = m_binary_data.file_metadata.write(*m_file, m_compression_type, m_checksum_type); if (res != EResult::Success) return res; // save printer metadata block - res = m_binary_data.printer_metadata.write(file, ECompressionType::None, m_checksum_type); + res = m_binary_data.printer_metadata.write(*m_file, m_compression_type, m_checksum_type); if (res != EResult::Success) return res; // save thumbnail blocks for (const ThumbnailBlock& block : m_binary_data.thumbnails) { - res = block.write(file, m_checksum_type); + res = block.write(*m_file, m_checksum_type); if (res != EResult::Success) return res; } - // save slicer metadata block - res = m_binary_data.slicer_metadata.write(file, ECompressionType::None, m_checksum_type); + // save print metadata block + res = m_binary_data.print_metadata.write(*m_file, m_compression_type, m_checksum_type); if (res != EResult::Success) return res; - // save gcode block + // save slicer metadata block + res = m_binary_data.slicer_metadata.write(*m_file, m_compression_type, m_checksum_type); + if (res != EResult::Success) + return res; return EResult::Success; } -EResult Binarizer::finalize(FILE& file) +static EResult write_gcode_block(FILE& file, const std::string& raw_data, EGCodeEncodingType encoding_type, ECompressionType compression_type, + EChecksumType checksum_type) +{ + GCodeBlock block; + block.encoding_type = (uint16_t)encoding_type; + block.raw_data = raw_data; + return block.write(file, compression_type, checksum_type); +} + +EResult Binarizer::append_gcode(const std::string& gcode) +{ + if (gcode.empty()) + return EResult::Success; + + assert(m_file != nullptr); + if (m_file == nullptr) + return EResult::WriteError; + + auto it_begin = gcode.begin(); + do { + const size_t begin_pos = std::distance(gcode.begin(), it_begin); + const size_t end_line_pos = gcode.find_first_of('\n', begin_pos); + if (end_line_pos == std::string::npos) + return EResult::WriteError; + + const size_t line_size = 1 + end_line_pos - begin_pos; + if (line_size + m_gcode_cache.length() > MAX_GCODE_CACHE_SIZE) { + if (!m_gcode_cache.empty()) { + const EResult res = write_gcode_block(*m_file, m_gcode_cache, m_gcode_encoding_type, m_compression_type, m_checksum_type); + if (res != EResult::Success) + // propagate error + return res; + m_gcode_cache.clear(); + } + } + + if (line_size > MAX_GCODE_CACHE_SIZE) + return EResult::WriteError; + + m_gcode_cache.insert(m_gcode_cache.end(), it_begin, it_begin + line_size); + it_begin += line_size; + } + while (it_begin != gcode.end()); + + return EResult::Success; +} + +EResult Binarizer::finalize() { if (!m_enabled) return EResult::Success; - // save print metadata block - EResult res = m_binary_data.print_metadata.write(file, ECompressionType::None, m_checksum_type); - if (res != EResult::Success) - return res; + // save gcode cache, if not empty + if (!m_gcode_cache.empty()) { + const EResult res = write_gcode_block(*m_file, m_gcode_cache, m_gcode_encoding_type, m_compression_type, m_checksum_type); + if (res != EResult::Success) + // propagate error + return res; + } #if ENABLE_CHECKSUM_BLOCK if (m_checksum_type != EChecksumType::None) { diff --git a/src/libslic3r/GCode/GCodeBinarizer.hpp b/src/libslic3r/GCode/GCodeBinarizer.hpp index 058cabd475..3dd2d14ca7 100644 --- a/src/libslic3r/GCode/GCodeBinarizer.hpp +++ b/src/libslic3r/GCode/GCodeBinarizer.hpp @@ -29,10 +29,13 @@ enum class EResult : uint16_t InvalidBlockType, InvalidCompressionType, InvalidMetadataEncodingType, + InvalidGCodeEncodingType, DataCompressionError, DataUncompressionError, MetadataEncodingError, MetadataDecodingError, + GCodeEncodingError, + GCodeDecodingError, BlockNotFound, InvalidChecksum, InvalidThumbnailFormat, @@ -195,12 +198,23 @@ struct SlicerMetadataBlock : public BaseMetadataBlock EResult read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header); }; -struct GCodeBlock : public BaseMetadataBlock +enum class EGCodeEncodingType : uint16_t { + None, + MeatPack, +}; + +struct GCodeBlock +{ + uint16_t encoding_type{ 0 }; + std::string raw_data; + // write block header and data EResult write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const; // read block data EResult read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header); + + static size_t get_parameters_size() { return sizeof(encoding_type); } }; #if ENABLE_CHECKSUM_BLOCK @@ -248,16 +262,20 @@ public: BinaryData& get_binary_data() { return m_binary_data; } const BinaryData& get_binary_data() const { return m_binary_data; } - EResult initialize(FILE& file, EChecksumType checksum_type); - EResult finalize(FILE& file); + EResult initialize(FILE& file, EGCodeEncodingType gcode_encoding_type, EChecksumType checksum_type); + EResult append_gcode(const std::string& gcode); + EResult finalize(); private: bool m_enabled{ false }; EChecksumType m_checksum_type{ EChecksumType::None }; ECompressionType m_compression_type{ ECompressionType::None }; + EGCodeEncodingType m_gcode_encoding_type{ EGCodeEncodingType::None }; + FILE* m_file{ nullptr }; BinaryData m_binary_data; + std::string m_gcode_cache; #if ENABLE_CHECKSUM_BLOCK ChecksumBlock m_checksum; #endif // ENABLE_CHECKSUM_BLOCK diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index e6d97a3f8c..1cdcba4115 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -1127,10 +1127,18 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct FILE* m_file{ nullptr }; }; +#if ENABLE_GCODE_VIEWER_STATISTICS + m_start_time = std::chrono::high_resolution_clock::now(); +#endif // ENABLE_GCODE_VIEWER_STATISTICS + FILE* file = boost::nowide::fopen(filename.c_str(), "rb"); if (file == nullptr) throw Slic3r::RuntimeError("Unable to open file: " + filename + "\n"); + fseek(file, 0, SEEK_END); + const long file_size = ftell(file); + rewind(file); + ScopedFile scoped_file(file); BinaryGCode::set_checksum_max_cache_size(1024); @@ -1141,7 +1149,6 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct if (res != BinaryGCode::EResult::Success) throw Slic3r::RuntimeError("File: " + filename + "does not contain a valid binary gcode\n Error: " + BinaryGCode::translate_result(res) + "\n"); - const long first_block_header_position = ftell(file); const bool verify_checksum = true; // read file metadata block @@ -1222,7 +1229,27 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); } + // read print metadata block + if ((BinaryGCode::EBlockType)block_header.type != BinaryGCode::EBlockType::PrintMetadata) + throw Slic3r::RuntimeError("Unable to find print metadata block in file: " + filename + "\n"); + BinaryGCode::PrintMetadataBlock print_metadata_block; + res = print_metadata_block.read_data(*file, file_header, block_header); + if (res != BinaryGCode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); +#if ENABLE_BINARIZED_GCODE_DEBUG + OutputDebugStringA("Print metadata:\n"); + for (const auto& [key, value] : print_metadata_block.raw_data) { + OutputDebugStringA(key.c_str()); + OutputDebugStringA("->"); + OutputDebugStringA(value.c_str()); + OutputDebugStringA("\n"); + } +#endif // ENABLE_BINARIZED_GCODE_DEBUG + // read slicer metadata block + res = BinaryGCode::read_next_block_header(*file, file_header, block_header, verify_checksum); + if (res != BinaryGCode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); if ((BinaryGCode::EBlockType)block_header.type != BinaryGCode::EBlockType::SlicerMetadata) throw Slic3r::RuntimeError("Unable to find slicer metadata block in file: " + filename + "\n"); BinaryGCode::SlicerMetadataBlock slicer_metadata_block; @@ -1250,25 +1277,38 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct config.load_from_ini_string(str, ForwardCompatibilitySubstitutionRule::EnableSilent); apply_config(config); - // read print metadata block + m_result.filename = filename; + m_result.id = ++s_result_id; + initialize_result_moves(); + + // read gcodes block res = BinaryGCode::read_next_block_header(*file, file_header, block_header, verify_checksum); if (res != BinaryGCode::EResult::Success) throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); - if ((BinaryGCode::EBlockType)block_header.type != BinaryGCode::EBlockType::PrintMetadata) - throw Slic3r::RuntimeError("Unable to find print metadata block in file: " + filename + "\n"); - BinaryGCode::PrintMetadataBlock print_metadata_block; - res = print_metadata_block.read_data(*file, file_header, block_header); - if (res != BinaryGCode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); -#if ENABLE_BINARIZED_GCODE_DEBUG - OutputDebugStringA("Print metadata:\n"); - for (const auto& [key, value] : print_metadata_block.raw_data) { - OutputDebugStringA(key.c_str()); - OutputDebugStringA("->"); - OutputDebugStringA(value.c_str()); - OutputDebugStringA("\n"); + if ((BinaryGCode::EBlockType)block_header.type != BinaryGCode::EBlockType::GCode) + throw Slic3r::RuntimeError("Unable to find gcode block in file: " + filename + "\n"); + while ((BinaryGCode::EBlockType)block_header.type == BinaryGCode::EBlockType::GCode) { + BinaryGCode::GCodeBlock block; + res = block.read_data(*file, file_header, block_header); + if (res != BinaryGCode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); + + // TODO: Update m_result.lines_ends + + m_parser.parse_buffer(block.raw_data, [this](GCodeReader& reader, const GCodeReader::GCodeLine& line) { + this->process_gcode_line(line, true); + }); + + if (ftell(file) == file_size) + break; + + res = BinaryGCode::read_next_block_header(*file, file_header, block_header, verify_checksum); + if (res != BinaryGCode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); } -#endif // ENABLE_BINARIZED_GCODE_DEBUG + + // Don't post-process the G-code to update time stamps. + this->finalize(false); } #endif // ENABLE_BINARIZED_GCODE @@ -3622,9 +3662,57 @@ void GCodeProcessor::post_process() } #if ENABLE_BINARIZED_GCODE + std::vector filament_mm(m_result.extruders_count, 0.0); + std::vector filament_cm3(m_result.extruders_count, 0.0); + std::vector filament_g(m_result.extruders_count, 0.0); + std::vector filament_cost(m_result.extruders_count, 0.0); + + double filament_total_g = 0.0; + double filament_total_cost = 0.0; + + for (const auto& [id, volume] : m_result.print_statistics.volumes_per_extruder) { + filament_mm[id] = volume / (static_cast(M_PI) * sqr(0.5 * m_result.filament_diameters[id])); + filament_cm3[id] = volume * 0.001; + filament_g[id] = filament_cm3[id] * double(m_result.filament_densities[id]); + filament_cost[id] = filament_g[id] * double(m_result.filament_cost[id]) * 0.001; + filament_total_g += filament_g[id]; + filament_total_cost += filament_cost[id]; + } + if (m_binarizer.is_enabled()) { - BinaryGCode::set_checksum_max_cache_size(4096); - const BinaryGCode::EResult res = m_binarizer.initialize(*out.f, BinaryGCode::EChecksumType::CRC32); + // update print metadata + auto update_value = [](std::string& value, const std::vector& values) { + char buf[1024]; + value.clear(); + for (size_t i = 0; i < values.size(); ++i) { + sprintf(buf, i == values.size() - 1 ? " %.2lf" : " %.2lf,", values[i]); + value += buf; + } + }; + + // update binary data + BinaryGCode::BinaryData& binary_data = m_binarizer.get_binary_data(); + for (auto& [key, value] : binary_data.print_metadata.raw_data) { + if (key == PrintStatistics::FilamentUsedMm) update_value(value, filament_mm); + else if (key == PrintStatistics::FilamentUsedG) update_value(value, filament_g); + else if (key == PrintStatistics::TotalFilamentUsedG) update_value(value, { filament_total_g }); + else if (key == PrintStatistics::FilamentUsedCm3) update_value(value, filament_cm3); + else if (key == PrintStatistics::FilamentCost) update_value(value, filament_cost); + else if (key == PrintStatistics::TotalFilamentCost) update_value(value, { filament_total_cost }); + } + + for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { + const TimeMachine& machine = m_time_processor.machines[i]; + PrintEstimatedStatistics::ETimeMode mode = static_cast(i); + if (mode == PrintEstimatedStatistics::ETimeMode::Normal || machine.enabled) { + char buf[128]; + sprintf(buf, "(%s mode)", (mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent"); + binary_data.print_metadata.raw_data.push_back({ "estimated printing time " + std::string(buf), get_time_dhms(machine.time) }); + binary_data.print_metadata.raw_data.push_back({ "estimated first layer printing time " + std::string(buf), get_time_dhms(machine.layers_time.empty() ? 0.f : machine.layers_time.front()) }); + } + } + + const BinaryGCode::EResult res = m_binarizer.initialize(*out.f, BinaryGCode::EGCodeEncodingType::None, BinaryGCode::EChecksumType::CRC32); if (res != BinaryGCode::EResult::Success) throw Slic3r::RuntimeError(std::string("Unable to initialize the gcode binarizer.\n")); } @@ -3876,9 +3964,30 @@ void GCodeProcessor::post_process() } } - write_to_file(out, out_string, result, out_path); +#if ENABLE_BINARIZED_GCODE + if (m_binarizer.is_enabled()) { + BinaryGCode::EResult res = m_binarizer.append_gcode(out_string); + if (res != BinaryGCode::EResult::Success) + throw Slic3r::RuntimeError(std::string("Error while sending gcode to the binarizer.\n")); + } + else +#endif // ENABLE_BINARIZED_GCODE + write_to_file(out, out_string, result, out_path); +#if ENABLE_BINARIZED_GCODE + update_out_file_pos(out, out_string, result); +#endif // ENABLE_BINARIZED_GCODE } +#if ENABLE_BINARIZED_GCODE + void update_out_file_pos(FilePtr& out, const std::string& out_string, GCodeProcessorResult& result) { + for (size_t i = 0; i < out_string.size(); ++i) { + if (out_string[i] == '\n') + result.lines_ends.emplace_back(m_out_file_pos + i + 1); + } + m_out_file_pos += out_string.size(); + } +#endif // ENABLE_BINARIZED_GCODE + // flush the current content of the cache to file void flush(FilePtr& out, GCodeProcessorResult& result, const std::string& out_path) { // collect lines to flush into a single string @@ -3892,7 +4001,18 @@ void GCodeProcessor::post_process() m_statistics.remove_all_lines(); #endif // NDEBUG - write_to_file(out, out_string, result, out_path); +#if ENABLE_BINARIZED_GCODE + if (m_binarizer.is_enabled()) { + BinaryGCode::EResult res = m_binarizer.append_gcode(out_string); + if (res != BinaryGCode::EResult::Success) + throw Slic3r::RuntimeError(std::string("Error while sending gcode to the binarizer.\n")); + } + else +#endif // ENABLE_BINARIZED_GCODE + write_to_file(out, out_string, result, out_path); +#if ENABLE_BINARIZED_GCODE + update_out_file_pos(out, out_string, result); +#endif // ENABLE_BINARIZED_GCODE } void synchronize_moves(GCodeProcessorResult& result) const { @@ -3922,12 +4042,13 @@ void GCodeProcessor::post_process() } #if ENABLE_BINARIZED_GCODE } -#endif // ENABLE_BINARIZED_GCODE +#else for (size_t i = 0; i < out_string.size(); ++i) { if (out_string[i] == '\n') result.lines_ends.emplace_back(m_out_file_pos + i + 1); } m_out_file_pos += out_string.size(); +#endif // ENABLE_BINARIZED_GCODE } } }; @@ -3999,6 +4120,7 @@ void GCodeProcessor::post_process() return processed; }; +#if !ENABLE_BINARIZED_GCODE std::vector filament_mm(m_result.extruders_count, 0.0); std::vector filament_cm3(m_result.extruders_count, 0.0); std::vector filament_g(m_result.extruders_count, 0.0); @@ -4015,6 +4137,7 @@ void GCodeProcessor::post_process() filament_total_g += filament_g[id]; filament_total_cost += filament_cost[id]; } +#endif // !ENABLE_BINARIZED_GCODE auto process_used_filament = [&](std::string& gcode_line) { // Prefilter for parsing speed. @@ -4036,12 +4159,21 @@ void GCodeProcessor::post_process() }; bool ret = false; +#if ENABLE_BINARIZED_GCODE + ret |= process_tag(gcode_line, PrintStatistics::FilamentUsedMmMask, filament_mm); + ret |= process_tag(gcode_line, PrintStatistics::FilamentUsedGMask, filament_g); + ret |= process_tag(gcode_line, PrintStatistics::TotalFilamentUsedGMask, { filament_total_g }); + ret |= process_tag(gcode_line, PrintStatistics::FilamentUsedCm3Mask, filament_cm3); + ret |= process_tag(gcode_line, PrintStatistics::FilamentCostMask, filament_cost); + ret |= process_tag(gcode_line, PrintStatistics::TotalFilamentCostMask, { filament_total_cost }); +#else ret |= process_tag(gcode_line, "; filament used [mm] =", filament_mm); ret |= process_tag(gcode_line, "; filament used [g] =", filament_g); ret |= process_tag(gcode_line, "; total filament used [g] =", { filament_total_g }); ret |= process_tag(gcode_line, "; filament used [cm3] =", filament_cm3); ret |= process_tag(gcode_line, "; filament cost =", filament_cost); ret |= process_tag(gcode_line, "; total filament cost =", { filament_total_cost }); +#endif // ENABLE_BINARIZED_GCODE return ret; }; @@ -4251,39 +4383,7 @@ void GCodeProcessor::post_process() #if ENABLE_BINARIZED_GCODE if (m_binarizer.is_enabled()) { - // update print metadata - auto update_value = [](std::string& value, const std::vector& values) { - char buf[1024]; - value.clear(); - for (size_t i = 0; i < values.size(); ++i) { - sprintf(buf, i == values.size() - 1 ? " %.2lf" : " %.2lf,", values[i]); - value += buf; - } - }; - - // update binary data - BinaryGCode::BinaryData& binary_data = m_binarizer.get_binary_data(); - for (auto& [key, value] : binary_data.print_metadata.raw_data) { - if (key == "filament used [mm]") update_value(value, filament_mm); - else if (key == "filament used [g]") update_value(value, filament_g); - else if (key == "total filament used [g]") update_value(value, { filament_total_g }); - else if (key == "filament used [cm3]") update_value(value, filament_cm3); - else if (key == "filament cost") update_value(value, filament_cost); - else if (key == "total filament cost") update_value(value, { filament_total_cost }); - } - - for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { - const TimeMachine& machine = m_time_processor.machines[i]; - PrintEstimatedStatistics::ETimeMode mode = static_cast(i); - if (mode == PrintEstimatedStatistics::ETimeMode::Normal || machine.enabled) { - char buf[128]; - sprintf(buf, "(%s mode)", (mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent"); - binary_data.print_metadata.raw_data.push_back({ "estimated printing time " + std::string(buf), get_time_dhms(machine.time) }); - binary_data.print_metadata.raw_data.push_back({ "estimated first layer printing time " + std::string(buf), get_time_dhms(machine.layers_time.empty() ? 0.f : machine.layers_time.front()) }); - } - } - - const BinaryGCode::EResult res = m_binarizer.finalize(*out.f); + const BinaryGCode::EResult res = m_binarizer.finalize(); if (res != BinaryGCode::EResult::Success) throw Slic3r::RuntimeError(std::string("Error while finalizing the gcode binarizer.\n")); } diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 2bd92340d8..c8de08d107 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1548,6 +1548,28 @@ std::string Print::output_filename(const std::string &filename_base) const return this->PrintBase::output_filename(m_config.output_filename_format.value, ".gcode", filename_base, &config); } +#if ENABLE_BINARIZED_GCODE +const std::string PrintStatistics::FilamentUsedG = "filament used [g]"; +const std::string PrintStatistics::FilamentUsedGMask = "; " + PrintStatistics::FilamentUsedG + " ="; + +const std::string PrintStatistics::TotalFilamentUsedG = "total " + PrintStatistics::FilamentUsedG; +const std::string PrintStatistics::TotalFilamentUsedGMask = "; " + PrintStatistics::TotalFilamentUsedG + " ="; +const std::string PrintStatistics::TotalFilamentUsedGValueMask = TotalFilamentUsedGMask + " %.2lf\n"; + +const std::string PrintStatistics::FilamentUsedCm3 = "filament used [cm3]"; +const std::string PrintStatistics::FilamentUsedCm3Mask = "; " + PrintStatistics::FilamentUsedCm3 + " ="; + +const std::string PrintStatistics::FilamentUsedMm = "filament used [mm]"; +const std::string PrintStatistics::FilamentUsedMmMask = "; " + PrintStatistics::FilamentUsedMm + " ="; + +const std::string PrintStatistics::FilamentCost = "filament cost"; +const std::string PrintStatistics::FilamentCostMask = "; " + PrintStatistics::FilamentCost + " ="; + +const std::string PrintStatistics::TotalFilamentCost = "total " + PrintStatistics::FilamentCost; +const std::string PrintStatistics::TotalFilamentCostMask = "; " + PrintStatistics::TotalFilamentCost + " ="; +const std::string PrintStatistics::TotalFilamentCostValueMask = PrintStatistics::TotalFilamentCostMask + " %.2lf\n"; +#endif // ENABLE_BINARIZED_GCODE + DynamicConfig PrintStatistics::config() const { DynamicConfig config; diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 059491951e..9490e07c5d 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -528,6 +528,23 @@ struct PrintStatistics filament_stats.clear(); printing_extruders.clear(); } + +#if ENABLE_BINARIZED_GCODE + static const std::string FilamentUsedG; + static const std::string FilamentUsedGMask; + static const std::string TotalFilamentUsedG; + static const std::string TotalFilamentUsedGMask; + static const std::string TotalFilamentUsedGValueMask; + static const std::string FilamentUsedCm3; + static const std::string FilamentUsedCm3Mask; + static const std::string FilamentUsedMm; + static const std::string FilamentUsedMmMask; + static const std::string FilamentCost; + static const std::string FilamentCostMask; + static const std::string TotalFilamentCost; + static const std::string TotalFilamentCostMask; + static const std::string TotalFilamentCostValueMask; +#endif // ENABLE_BINARIZED_GCODE }; using PrintObjectPtrs = std::vector; From a11009c3e03884ac162b383fadd066b3f1e48d55 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 21 Jul 2023 08:29:13 +0200 Subject: [PATCH 020/136] SPE-1784: New compressed (binary) gcode format integration Populated printer metadata block Small optimization for print metadata --- src/libslic3r/GCode.cpp | 46 +++++++++++--------------- src/libslic3r/GCode/GCodeProcessor.cpp | 32 ++++++++++-------- src/libslic3r/GCode/GCodeProcessor.hpp | 8 ++--- 3 files changed, 42 insertions(+), 44 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index f6b772e946..c9136af7a4 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1036,14 +1036,7 @@ namespace DoExport { ++ dst.second; }; #if ENABLE_BINARIZED_GCODE - if (export_binary_data) { - char buf[128]; - sprintf(buf, "%.2lf", used_filament); - binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedMm, std::string(buf) }); - sprintf(buf, "%.2lf", extruded_volume * 0.001); - binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedCm3, std::string(buf) }); - } - else { + if (!export_binary_data) { #endif // ENABLE_BINARIZED_GCODE append(out_filament_used_mm, "%.2lf", used_filament); append(out_filament_used_cm3, "%.2lf", extruded_volume * 0.001); @@ -1053,23 +1046,13 @@ namespace DoExport { if (filament_weight > 0.) { print_statistics.total_weight = print_statistics.total_weight + filament_weight; #if ENABLE_BINARIZED_GCODE - if (export_binary_data) { - char buf[128]; - sprintf(buf, "%.2lf", filament_weight); - binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedG, std::string(buf) }); - } - else + if (!export_binary_data) #endif // ENABLE_BINARIZED_GCODE append(out_filament_used_g, "%.2lf", filament_weight); if (filament_cost > 0.) { print_statistics.total_cost = print_statistics.total_cost + filament_cost; #if ENABLE_BINARIZED_GCODE - if (export_binary_data) { - char buf[128]; - sprintf(buf, "%.2lf", filament_cost); - binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentCost, std::string(buf) }); - } - else + if (!export_binary_data) #endif // ENABLE_BINARIZED_GCODE append(out_filament_cost, "%.2lf", filament_cost); } @@ -1165,11 +1148,27 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // file data binary_data.file_metadata.encoding_type = (uint16_t)BinaryGCode::EMetadataEncodingType::INI; binary_data.file_metadata.raw_data.emplace_back("Producer", std::string(SLIC3R_APP_NAME) + " " + std::string(SLIC3R_VERSION)); - // add here other key/value pairs // config data binary_data.slicer_metadata.encoding_type = (uint16_t)BinaryGCode::EMetadataEncodingType::INI; encode_full_config(print, binary_data.slicer_metadata.raw_data); + + // printer data + binary_data.printer_metadata.raw_data.emplace_back("printer model" , print.config().printer_model.value); // duplicated into config data + std::string filament_types_str; + for (size_t i = 0; i < print.config().filament_type.values.size(); ++i) { + filament_types_str += print.config().filament_type.values[i]; + if (i < print.config().filament_type.values.size() - 1) + filament_types_str += ", "; + } + binary_data.printer_metadata.raw_data.emplace_back("filament type", filament_types_str); // duplicated into config data + std::string nozzle_diameters_str; + char buf[1024]; + for (size_t i = 0; i < print.config().nozzle_diameter.values.size(); ++i) { + sprintf(buf, i < print.config().nozzle_diameter.values.size() - 1 ? "%.2lf, " : "%.2lf", print.config().nozzle_diameter.values[i]); + nozzle_diameters_str += buf; + } + binary_data.printer_metadata.raw_data.emplace_back("nozzle diameter", nozzle_diameters_str); // duplicated into config data } // modifies m_silent_time_estimator_enabled @@ -1615,11 +1614,6 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato if (export_to_binary_gcode) { BinaryGCode::BinaryData& binary_data = m_processor.get_binary_data(); - char buf[128]; - sprintf(buf, "%.2lf", print.m_print_statistics.total_weight); - binary_data.print_metadata.raw_data.push_back({ PrintStatistics::TotalFilamentUsedG, std::string(buf) }); - sprintf(buf, "%.2lf", print.m_print_statistics.total_cost); - binary_data.print_metadata.raw_data.push_back({ PrintStatistics::TotalFilamentCost, std::string(buf) }); if (print.m_print_statistics.total_toolchanges > 0) binary_data.print_metadata.raw_data.push_back({ "total toolchanges", std::to_string(print.m_print_statistics.total_toolchanges) }); } diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 1cdcba4115..b66850fd07 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -1354,7 +1354,7 @@ void GCodeProcessor::finalize(bool perform_post_process) m_used_filaments.process_caches(this); - update_estimated_times_stats(); + update_estimated_statistics(); #if ENABLE_GCODE_VIEWER_DATA_CHECKING std::cout << "\n"; @@ -3681,25 +3681,27 @@ void GCodeProcessor::post_process() if (m_binarizer.is_enabled()) { // update print metadata - auto update_value = [](std::string& value, const std::vector& values) { + auto stringify = [](const std::vector& values) { + std::string ret; char buf[1024]; - value.clear(); for (size_t i = 0; i < values.size(); ++i) { - sprintf(buf, i == values.size() - 1 ? " %.2lf" : " %.2lf,", values[i]); - value += buf; + sprintf(buf, i < values.size() - 1 ? "%.2lf, " : "%.2lf", values[i]); + ret += buf; } + return ret; }; // update binary data BinaryGCode::BinaryData& binary_data = m_binarizer.get_binary_data(); - for (auto& [key, value] : binary_data.print_metadata.raw_data) { - if (key == PrintStatistics::FilamentUsedMm) update_value(value, filament_mm); - else if (key == PrintStatistics::FilamentUsedG) update_value(value, filament_g); - else if (key == PrintStatistics::TotalFilamentUsedG) update_value(value, { filament_total_g }); - else if (key == PrintStatistics::FilamentUsedCm3) update_value(value, filament_cm3); - else if (key == PrintStatistics::FilamentCost) update_value(value, filament_cost); - else if (key == PrintStatistics::TotalFilamentCost) update_value(value, { filament_total_cost }); - } + binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedMm, stringify(filament_mm) }); + binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedCm3, stringify(filament_cm3) }); + binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedG, stringify(filament_g) }); + binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentCost, stringify(filament_cost) }); + binary_data.print_metadata.raw_data.push_back({ PrintStatistics::TotalFilamentUsedG, stringify({ filament_total_g }) }); + binary_data.print_metadata.raw_data.push_back({ PrintStatistics::TotalFilamentCost, stringify({ filament_total_cost }) }); + + binary_data.printer_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedMm, stringify(filament_mm) }); // duplicated into print metadata + binary_data.printer_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedG, stringify(filament_g) }); // duplicated into print metadata for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { const TimeMachine& machine = m_time_processor.machines[i]; @@ -3709,6 +3711,8 @@ void GCodeProcessor::post_process() sprintf(buf, "(%s mode)", (mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent"); binary_data.print_metadata.raw_data.push_back({ "estimated printing time " + std::string(buf), get_time_dhms(machine.time) }); binary_data.print_metadata.raw_data.push_back({ "estimated first layer printing time " + std::string(buf), get_time_dhms(machine.layers_time.empty() ? 0.f : machine.layers_time.front()) }); + + binary_data.printer_metadata.raw_data.push_back({ "estimated printing time " + std::string(buf), get_time_dhms(machine.time) }); } } @@ -4596,7 +4600,7 @@ void GCodeProcessor::simulate_st_synchronize(float additional_time) } } -void GCodeProcessor::update_estimated_times_stats() +void GCodeProcessor::update_estimated_statistics() { auto update_mode = [this](PrintEstimatedStatistics::ETimeMode mode) { PrintEstimatedStatistics::Mode& data = m_result.print_statistics.modes[static_cast(mode)]; diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index fe2db07f99..9ca4455945 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -66,10 +66,10 @@ namespace Slic3r { } }; - std::vector volumes_per_color_change; - std::map volumes_per_extruder; + std::vector volumes_per_color_change; + std::map volumes_per_extruder; std::map> used_filaments_per_role; - std::map cost_per_extruder; + std::map cost_per_extruder; std::array(ETimeMode::Count)> modes; @@ -831,7 +831,7 @@ namespace Slic3r { // Simulates firmware st_synchronize() call void simulate_st_synchronize(float additional_time = 0.0f); - void update_estimated_times_stats(); + void update_estimated_statistics(); double extract_absolute_position_on_axis(Axis axis, const GCodeReader::GCodeLine& line, double area_filament_cross_section); }; From ee87536ff60d3ac7daacc7a26f3f9f0fd7c45e2a Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 21 Jul 2023 12:24:24 +0200 Subject: [PATCH 021/136] Added debug imgui dialog to allow to change binary gcode parameters at runtime --- src/libslic3r/GCode/GCodeBinarizer.cpp | 29 ++++++------ src/libslic3r/GCode/GCodeBinarizer.hpp | 16 ++++--- src/libslic3r/GCode/GCodeProcessor.cpp | 6 ++- src/libslic3r/GCode/GCodeProcessor.hpp | 5 +++ src/libslic3r/Technologies.hpp | 1 + src/slic3r/GUI/GLCanvas3D.cpp | 61 ++++++++++++++++++++++++++ src/slic3r/GUI/GLCanvas3D.hpp | 4 ++ src/slic3r/GUI/ImGuiWrapper.cpp | 5 ++- 8 files changed, 103 insertions(+), 24 deletions(-) diff --git a/src/libslic3r/GCode/GCodeBinarizer.cpp b/src/libslic3r/GCode/GCodeBinarizer.cpp index ddb1816368..4191d0c3a6 100644 --- a/src/libslic3r/GCode/GCodeBinarizer.cpp +++ b/src/libslic3r/GCode/GCodeBinarizer.cpp @@ -12,7 +12,7 @@ namespace BinaryGCode { static size_t g_checksum_max_cache_size = 65536; -static const size_t MAX_GCODE_CACHE_SIZE = 65536; +static constexpr size_t MAX_GCODE_CACHE_SIZE = 65536; std::string translate_result(BinaryGCode::EResult result) { @@ -110,17 +110,17 @@ static bool decode_metadata(const std::vector& src, std::vector MAX_GCODE_CACHE_SIZE) { if (!m_gcode_cache.empty()) { - const EResult res = write_gcode_block(*m_file, m_gcode_cache, m_gcode_encoding_type, m_compression_type, m_checksum_type); + const EResult res = write_gcode_block(*m_file, m_gcode_cache, m_config.gcode_encoding, m_config.compression, m_config.checksum); if (res != EResult::Success) // propagate error return res; @@ -892,7 +891,7 @@ EResult Binarizer::finalize() // save gcode cache, if not empty if (!m_gcode_cache.empty()) { - const EResult res = write_gcode_block(*m_file, m_gcode_cache, m_gcode_encoding_type, m_compression_type, m_checksum_type); + const EResult res = write_gcode_block(*m_file, m_gcode_cache, m_config.gcode_encoding, m_config.compression, m_config.checksum); if (res != EResult::Success) // propagate error return res; diff --git a/src/libslic3r/GCode/GCodeBinarizer.hpp b/src/libslic3r/GCode/GCodeBinarizer.hpp index 3dd2d14ca7..ad4d1c640c 100644 --- a/src/libslic3r/GCode/GCodeBinarizer.hpp +++ b/src/libslic3r/GCode/GCodeBinarizer.hpp @@ -252,27 +252,31 @@ struct BinaryData } }; +struct BinarizerConfig +{ + ECompressionType compression{ ECompressionType::None }; + EGCodeEncodingType gcode_encoding{ EGCodeEncodingType::None }; + EMetadataEncodingType metadata_encoding{ EMetadataEncodingType::INI }; + EChecksumType checksum{ EChecksumType::None }; +}; + class Binarizer { public: bool is_enabled() const { return m_enabled; } void set_enabled(bool enable) { m_enabled = enable; } - void set_compression_type(ECompressionType type) { m_compression_type = type; } BinaryData& get_binary_data() { return m_binary_data; } const BinaryData& get_binary_data() const { return m_binary_data; } - EResult initialize(FILE& file, EGCodeEncodingType gcode_encoding_type, EChecksumType checksum_type); + EResult initialize(FILE& file, const BinarizerConfig& config); EResult append_gcode(const std::string& gcode); EResult finalize(); private: bool m_enabled{ false }; - EChecksumType m_checksum_type{ EChecksumType::None }; - ECompressionType m_compression_type{ ECompressionType::None }; - EGCodeEncodingType m_gcode_encoding_type{ EGCodeEncodingType::None }; - + BinarizerConfig m_config; FILE* m_file{ nullptr }; BinaryData m_binary_data; std::string m_gcode_cache; diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index b66850fd07..e72745fac1 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -69,6 +69,10 @@ const std::vector GCodeProcessor::Reserved_Tags = { const float GCodeProcessor::Wipe_Width = 0.05f; const float GCodeProcessor::Wipe_Height = 0.05f; +#if ENABLE_BINARIZED_GCODE +BinaryGCode::BinarizerConfig GCodeProcessor::s_binarizer_config{}; +#endif // ENABLE_BINARIZED + #if ENABLE_GCODE_VIEWER_DATA_CHECKING const std::string GCodeProcessor::Mm3_Per_Mm_Tag = "MM3_PER_MM:"; #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING @@ -3716,7 +3720,7 @@ void GCodeProcessor::post_process() } } - const BinaryGCode::EResult res = m_binarizer.initialize(*out.f, BinaryGCode::EGCodeEncodingType::None, BinaryGCode::EChecksumType::CRC32); + const BinaryGCode::EResult res = m_binarizer.initialize(*out.f, s_binarizer_config); if (res != BinaryGCode::EResult::Success) throw Slic3r::RuntimeError(std::string("Unable to initialize the gcode binarizer.\n")); } diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 9ca4455945..333f3295e6 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -527,10 +527,15 @@ namespace Slic3r { }; #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING +#if ENABLE_BINARIZED_GCODE_DEBUG_WINDOW + static BinaryGCode::BinarizerConfig& get_binarizer_config() { return s_binarizer_config; } +#endif // ENABLE_BINARIZED_GCODE_DEBUG_WINDOW + private: GCodeReader m_parser; #if ENABLE_BINARIZED_GCODE BinaryGCode::Binarizer m_binarizer; + static BinaryGCode::BinarizerConfig s_binarizer_config; #endif // ENABLE_BINARIZED_GCODE EUnits m_units; diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 92b6f8578c..6af385e71a 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -69,6 +69,7 @@ // Enable export of binarized gcode #define ENABLE_BINARIZED_GCODE (1 && ENABLE_2_6_1_ALPHA1) +#define ENABLE_BINARIZED_GCODE_DEBUG_WINDOW (1 && ENABLE_BINARIZED_GCODE) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index debdc2054d..9b89596001 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2048,6 +2048,11 @@ void GLCanvas3D::render() #endif // ENABLE_SLA_VIEW_DEBUG_WINDOW } +#if ENABLE_BINARIZED_GCODE_DEBUG_WINDOW + if (wxGetApp().plater()->is_view3D_shown() && current_printer_technology() != ptSLA && fff_print()->config().gcode_binary) + show_binary_gcode_debug_window(); +#endif // ENABLE_BINARIZED_GCODE_DEBUG_WINDOW + std::string tooltip; // Negative coordinate means out of the window, likely because the window was deactivated. @@ -7866,6 +7871,62 @@ void GLCanvas3D::GizmoHighlighter::blink() invalidate(); } +#if ENABLE_BINARIZED_GCODE_DEBUG_WINDOW +void GLCanvas3D::show_binary_gcode_debug_window() +{ + BinaryGCode::BinarizerConfig& binarizer_config = GCodeProcessor::get_binarizer_config(); + + ImGuiWrapper& imgui = *wxGetApp().imgui(); + imgui.begin(std::string("Binary GCode"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + + if (ImGui::BeginTable("BinaryGCodeConfig", 2)) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Compression"); + ImGui::TableSetColumnIndex(1); + const std::vector gcode_compressions = { "None" }; + int compression = (int)binarizer_config.compression; + if (imgui.combo(std::string("##1"), gcode_compressions, compression, ImGuiComboFlags_HeightLargest, 0.0f, 100.0f)) + binarizer_config.compression = (BinaryGCode::ECompressionType)compression; + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "GGcode encoding"); + ImGui::TableSetColumnIndex(1); + const std::vector gcode_encodings = { "None", "MeatPack" }; + int gcode_encoding = (int)binarizer_config.gcode_encoding; + if (imgui.combo(std::string("##2"), gcode_encodings, gcode_encoding, ImGuiComboFlags_HeightLargest, 0.0f, 100.0f)) + binarizer_config.gcode_encoding = (BinaryGCode::EGCodeEncodingType)gcode_encoding; + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Metadata encoding"); + ImGui::TableSetColumnIndex(1); + const std::vector metadata_encodings = { "INI" }; + int metadata_encoding = (int)binarizer_config.metadata_encoding; + if (imgui.combo(std::string("##3"), metadata_encodings, metadata_encoding, ImGuiComboFlags_HeightLargest, 0.0f, 100.0f)) + binarizer_config.metadata_encoding = (BinaryGCode::EMetadataEncodingType)metadata_encoding; + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Checksum type"); + ImGui::TableSetColumnIndex(1); + const std::vector checksums = { "None", "CRC32" }; + int checksum = (int)binarizer_config.checksum; + if (imgui.combo(std::string("##4"), checksums, checksum, ImGuiComboFlags_HeightLargest, 0.0f, 100.0f)) + binarizer_config.checksum = (BinaryGCode::EChecksumType)checksum; + + ImGui::EndTable(); + + ImGui::Separator(); + imgui.text("!!! WARNING !!!"); + imgui.text("Changing values does NOT invalidate the current slice"); + } + + imgui.end(); +} +#endif // ENABLE_BINARIZED_GCODE_DEBUG_WINDOW + const ModelVolume *get_model_volume(const GLVolume &v, const Model &model) { const ModelVolume * ret = nullptr; diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 4d7368ed26..2ef9c8d189 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -1113,6 +1113,10 @@ private: bool _deactivate_arrange_menu(); float get_overlay_window_width() { return LayersEditing::get_overlay_window_width(); } + +#if ENABLE_BINARIZED_GCODE_DEBUG_WINDOW + void show_binary_gcode_debug_window(); +#endif // ENABLE_BINARIZED_GCODE_DEBUG_WINDOW }; const ModelVolume *get_model_volume(const GLVolume &v, const Model &model); diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 1a800eee42..d774aeb044 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -782,7 +782,8 @@ bool ImGuiWrapper::combo(const wxString& label, const std::vector& bool ImGuiWrapper::combo(const std::string& label, const std::vector& options, int& selection, ImGuiComboFlags flags/* = 0*/, float label_width/* = 0.0f*/, float item_width/* = 0.0f*/) { // this is to force the label to the left of the widget: - if (!label.empty()) { + const bool hidden_label = boost::starts_with(label, "##"); + if (!label.empty() && !hidden_label) { text(label); ImGui::SameLine(label_width); } @@ -792,7 +793,7 @@ bool ImGuiWrapper::combo(const std::string& label, const std::vector= 0 ? options[selection].c_str() : ""; - if (ImGui::BeginCombo(("##" + label).c_str(), selection_str, flags)) { + if (ImGui::BeginCombo(hidden_label ? label.c_str() : ("##" + label).c_str(), selection_str, flags)) { for (int i = 0; i < (int)options.size(); i++) { if (ImGui::Selectable(options[i].c_str(), i == selection)) { selection_out = i; From 8bb6224ba84093a53223e36afa2a8f93101f902a Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 21 Jul 2023 12:27:09 +0200 Subject: [PATCH 022/136] Attempt to fix perl test --- t/layers.t | 1 + 1 file changed, 1 insertion(+) diff --git a/t/layers.t b/t/layers.t index 4d958808a6..fef94457c2 100644 --- a/t/layers.t +++ b/t/layers.t @@ -62,6 +62,7 @@ use Slic3r::Test qw(_eq); { my $config = Slic3r::Config->new; $config->set('fill_density', 0); # just for making the test faster + $config->set('gcode_binary', 0); my $print = Slic3r::Test::init_print('20mm_cube', config => $config, scale => 2); my @z = (); From 63310776455f7b56e6e1a72a569fe944c1f163e2 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 24 Jul 2023 12:37:03 +0200 Subject: [PATCH 023/136] SPE-1784: New compressed (binary) gcode format integration Added MeatPack encoding to GCode Block --- src/libslic3r/GCode/GCodeBinarizer.cpp | 491 +++++++++++++++++++++++-- src/libslic3r/GCode/GCodeBinarizer.hpp | 3 +- src/slic3r/GUI/GLCanvas3D.cpp | 26 +- 3 files changed, 479 insertions(+), 41 deletions(-) diff --git a/src/libslic3r/GCode/GCodeBinarizer.cpp b/src/libslic3r/GCode/GCodeBinarizer.cpp index 4191d0c3a6..350905ecda 100644 --- a/src/libslic3r/GCode/GCodeBinarizer.cpp +++ b/src/libslic3r/GCode/GCodeBinarizer.cpp @@ -12,7 +12,429 @@ namespace BinaryGCode { static size_t g_checksum_max_cache_size = 65536; -static constexpr size_t MAX_GCODE_CACHE_SIZE = 65536; +static constexpr const size_t MAX_GCODE_CACHE_SIZE = 65536; + +namespace MeatPack { +static constexpr const uint8_t Command_None{ 0 }; +//#Command_TogglePacking = 253 -- Currently unused, byte 253 can be reused later. +static constexpr const uint8_t Command_EnablePacking{ 251 }; +static constexpr const uint8_t Command_DisablePacking{ 250 }; +static constexpr const uint8_t Command_ResetAll{ 249 }; +static constexpr const uint8_t Command_QueryConfig{ 248 }; +static constexpr const uint8_t Command_EnableNoSpaces{ 247 }; +static constexpr const uint8_t Command_DisableNoSpaces{ 246 }; +static constexpr const uint8_t Command_SignalByte{ 0xFF }; + +static constexpr const uint8_t Flag_OmitWhitespaces{ 0x01 }; +static constexpr const uint8_t Flag_RemoveComments{ 0x02 }; + +static constexpr const uint8_t BothUnpackable{ 0b11111111 }; +static constexpr const char SpaceReplacedCharacter{ 'E' }; + +static const std::unordered_map ReverseLookupTbl = { + { '0', 0b00000000 }, + { '1', 0b00000001 }, + { '2', 0b00000010 }, + { '3', 0b00000011 }, + { '4', 0b00000100 }, + { '5', 0b00000101 }, + { '6', 0b00000110 }, + { '7', 0b00000111 }, + { '8', 0b00001000 }, + { '9', 0b00001001 }, + { '.', 0b00001010 }, + { ' ', 0b00001011 }, + { '\n', 0b00001100 }, + { 'G', 0b00001101 }, + { 'X', 0b00001110 }, + { '\0', 0b00001111 } // never used, 0b1111 is used to indicate the next 8-bits is a full character +}; + +class MPBinarizer +{ +public: + explicit MPBinarizer(uint8_t flags = 0) : m_flags(flags) {} + + void initialize(std::vector& dst) { + initialize_lookup_tables(); + append_command(Command_EnablePacking, dst); + m_binarizing = true; + } + + void finalize(std::vector& dst) { + if ((m_flags & Flag_RemoveComments) != 0) { + assert(m_binarizing); + append_command(Command_ResetAll, dst); + m_binarizing = false; + } + } + + void binarize_line(const std::string& line, std::vector& dst) { + auto unified_method = [this](const std::string& line) { + const std::string::size_type g_idx = line.find('G'); + if (g_idx != std::string::npos) { + if (g_idx + 1 < line.size() && line[g_idx + 1] >= '0' && line[g_idx + 1] <= '9') { + if ((m_flags & Flag_OmitWhitespaces) != 0) { + std::string result = line; + std::replace(result.begin(), result.end(), 'e', 'E'); + std::replace(result.begin(), result.end(), 'x', 'X'); + std::replace(result.begin(), result.end(), 'g', 'G'); + result.erase(std::remove(result.begin(), result.end(), ' '), result.end()); + if (result.find('*') != std::string::npos) { + size_t checksum = 0; + result.erase(std::remove(result.begin(), result.end(), '*'), result.end()); + for (size_t i = 0; i < result.size(); ++i) { + checksum ^= static_cast(result[i]); + } + result += "*" + std::to_string(checksum); + } + result += '\n'; + return result; + } + else { + std::string result = line; + std::replace(result.begin(), result.end(), 'x', 'X'); + std::replace(result.begin(), result.end(), 'g', 'G'); + result.erase(std::remove(result.begin(), result.end(), ' '), result.end()); + if (result.find('*') != std::string::npos) { + size_t checksum = 0; + result.erase(std::remove(result.begin(), result.end(), '*'), result.end()); + for (size_t i = 0; i < result.size(); ++i) { + checksum ^= static_cast(result[i]); + } + result += "*" + std::to_string(checksum); + } + result += '\n'; + return result; + } + } + } + return line; + }; + auto is_packable = [this](char c) { + return (s_lookup_tables.packable[static_cast(c)] != 0); + }; + auto pack_chars = [this](char low, char high) { + return (((s_lookup_tables.value[static_cast(high)] & 0xF) << 4) | + (s_lookup_tables.value[static_cast(low)] & 0xF)); + }; + + if (!line.empty()) { + if ((m_flags & Flag_RemoveComments) == 0) { + if (line[0] == ';') { + if (m_binarizing) { + append_command(Command_DisablePacking, dst); + m_binarizing = false; + } + + dst.insert(dst.end(), line.begin(), line.end()); + return; + } + } + + if (line[0] == ';' || + line[0] == '\n' || + line[0] == '\r' || + line.size() < 2) + return; + + std::string modifiedLine = line.substr(0, line.find(';')); + if (modifiedLine.empty()) + return; + auto trim_right = [](const std::string& str) { + if (str.back() != ' ') + return str; + auto bit = str.rbegin(); + while (bit != str.rend() && *bit == ' ') { + ++bit; + } + return str.substr(0, std::distance(str.begin(), bit.base())); + }; + modifiedLine = trim_right(modifiedLine); + modifiedLine = unified_method(modifiedLine); + if (modifiedLine.back() != '\n') + modifiedLine.push_back('\n'); + const size_t line_len = modifiedLine.size(); + std::vector temp_buffer; + temp_buffer.reserve(line_len); + + for (size_t line_idx = 0; line_idx < line_len; line_idx += 2) { + const bool skip_last = line_idx == (line_len - 1); + const char char_1 = modifiedLine[line_idx]; + const char char_2 = (skip_last ? '\n' : modifiedLine[line_idx + 1]); + const bool c1_p = is_packable(char_1); + const bool c2_p = is_packable(char_2); + + if (c1_p) { + if (c2_p) + temp_buffer.emplace_back(static_cast(pack_chars(char_1, char_2))); + else { + temp_buffer.emplace_back(static_cast(pack_chars(char_1, '\0'))); + temp_buffer.emplace_back(static_cast(char_2)); + } + } + else { + if (c2_p) { + temp_buffer.emplace_back(static_cast(pack_chars('\0', char_2))); + temp_buffer.emplace_back(static_cast(char_1)); + } + else { + temp_buffer.emplace_back(static_cast(BothUnpackable)); + temp_buffer.emplace_back(static_cast(char_1)); + temp_buffer.emplace_back(static_cast(char_2)); + } + } + } + + if (!m_binarizing && !temp_buffer.empty()) { + append_command(Command_EnablePacking, dst); + m_binarizing = true; + } + + dst.insert(dst.end(), temp_buffer.begin(), temp_buffer.end()); + } + } + +private: + unsigned char m_flags{ 0 }; + bool m_binarizing{ false }; + + struct LookupTables + { + std::array packable; + std::array value; + bool initialized; + unsigned char flags; + }; + + static LookupTables s_lookup_tables; + + void append_command(unsigned char cmd, std::vector& dst) { + dst.emplace_back(Command_SignalByte); + dst.emplace_back(Command_SignalByte); + dst.emplace_back(cmd); + } + + void initialize_lookup_tables() { + if (s_lookup_tables.initialized && m_flags == s_lookup_tables.flags) + return; + + for (const auto& [c, value] : ReverseLookupTbl) { + const int index = static_cast(c); + s_lookup_tables.packable[index] = 1; + s_lookup_tables.value[index] = value; + } + + if ((m_flags & Flag_OmitWhitespaces) != 0) { + s_lookup_tables.value[static_cast(SpaceReplacedCharacter)] = ReverseLookupTbl.at(' '); + s_lookup_tables.packable[static_cast(SpaceReplacedCharacter)] = 1; + s_lookup_tables.packable[static_cast(' ')] = 0; + } + else { + s_lookup_tables.packable[static_cast(SpaceReplacedCharacter)] = 0; + s_lookup_tables.packable[static_cast(' ')] = 1; + } + + s_lookup_tables.initialized = true; + s_lookup_tables.flags = m_flags; + } +}; + +MPBinarizer::LookupTables MPBinarizer::s_lookup_tables = { { 0 }, { 0 }, false, 0 }; + +static constexpr const unsigned char SecondNotPacked{ 0b11110000 }; +static constexpr const unsigned char FirstNotPacked{ 0b00001111 }; +static constexpr const unsigned char NextPackedFirst{ 0b00000001 }; +static constexpr const unsigned char NextPackedSecond{ 0b00000010 }; + +// See for reference: https://github.com/scottmudge/Prusa-Firmware-MeatPack/blob/MK3_sm_MeatPack/Firmware/meatpack.cpp +static void unbinarize(const std::vector& src, std::string& dst) { + bool unbinarizing = false; + bool cmd_active = false; // Is a command pending + uint8_t char_buf = 0; // Buffers a character if dealing with out-of-sequence pairs + size_t cmd_count = 0; // Counts how many command bytes are received (need 2) + size_t full_char_queue = 0; // Counts how many full-width characters are to be received + std::array char_out_buf; // Output buffer for caching up to 2 characters + size_t char_out_count = 0; // Stores number of characters to be read out + + auto handle_command = [&](uint8_t c) { + switch (c) + { + case Command_EnablePacking: { unbinarizing = true; break; } + case Command_DisablePacking: { unbinarizing = false; break; } + case Command_ResetAll: { unbinarizing = false; break; } + default: + case Command_QueryConfig: { break; } + } + }; + + auto handle_output_char = [&](uint8_t c) { + char_out_buf[char_out_count++] = c; + }; + + auto get_char = [](uint8_t c) { + switch (c) + { + case 0b0000: { return '0'; } + case 0b0001: { return '1'; } + case 0b0010: { return '2'; } + case 0b0011: { return '3'; } + case 0b0100: { return '4'; } + case 0b0101: { return '5'; } + case 0b0110: { return '6'; } + case 0b0111: { return '7'; } + case 0b1000: { return '8'; } + case 0b1001: { return '9'; } + case 0b1010: { return '.'; } + case 0b1011: { return 'E'; } + case 0b1100: { return '\n'; } + case 0b1101: { return 'G'; } + case 0b1110: { return 'X'; } + } + return '\0'; + }; + + auto unpack_chars = [&](uint8_t pk, std::array& chars_out) { + uint8_t out = 0; + + // If lower 4 bytes is 0b1111, the higher 4 are unused, and next char is full. + if ((pk & FirstNotPacked) == FirstNotPacked) + out |= NextPackedFirst; + else + chars_out[0] = get_char(pk & 0xF); // Assign lower char + + // Check if upper 4 bytes is 0b1111... if so, we don't need the second char. + if ((pk & SecondNotPacked) == SecondNotPacked) + out |= NextPackedSecond; + else + chars_out[1] = get_char((pk >> 4) & 0xF); // Assign upper char + + return out; + }; + + auto handle_rx_char = [&](uint8_t c) { + if (unbinarizing) { + if (full_char_queue > 0) { + handle_output_char(c); + if (char_buf > 0) { + handle_output_char(char_buf); + char_buf = 0; + } + --full_char_queue; + } + else { + std::array buf = { 0, 0 }; + const uint8_t res = unpack_chars(c, buf); + + if ((res & NextPackedFirst) != 0) { + ++full_char_queue; + if ((res & NextPackedSecond) != 0) + ++full_char_queue; + else + char_buf = buf[1]; + } + else { + handle_output_char(buf[0]); + if (buf[0] != '\n') { + if ((res & NextPackedSecond) != 0) + ++full_char_queue; + else + handle_output_char(buf[1]); + } + } + } + } + else // Packing not enabled, just copy character to output + handle_output_char(c); + }; + + auto get_result_char = [&](std::array& chars_out) { + if (char_out_count > 0) { + const size_t res = char_out_count; + for (uint8_t i = 0; i < char_out_count; ++i) { + chars_out[i] = (char)char_out_buf[i]; + } + char_out_count = 0; + return res; + } + return (size_t)0; + }; + + std::vector unbin_buffer(2 * src.size(), 0); + auto it_unbin_end = unbin_buffer.begin(); + +#if ENABLE_BINARIZED_GCODE_DEBUG + size_t line_start = 0; +#endif // ENABLE_BINARIZED_GCODE_DEBUG + bool add_space = false; + + auto begin = src.begin(); + auto end = src.end(); + + auto it_bin = begin; + while (it_bin != end) { + uint8_t c_bin = *it_bin; + if (c_bin == Command_SignalByte) { + if (cmd_count > 0) { + cmd_active = true; + cmd_count = 0; + } + else + ++cmd_count; + } + else { + if (cmd_active) { + handle_command(c_bin); + cmd_active = false; + } + else { + if (cmd_count > 0) { + handle_rx_char(Command_SignalByte); + cmd_count = 0; + } + + handle_rx_char(c_bin); + } + } + + std::array c_unbin{ 0, 0 }; + const size_t char_count = get_result_char(c_unbin); + for (size_t i = 0; i < char_count; ++i) { + // GCodeReader::parse_line_internal() is unable to parse a G line where the data are not separated by spaces + // so we add them where needed + if (c_unbin[i] == 'G' && std::distance(unbin_buffer.begin(), it_unbin_end) > 0 && *std::prev(it_unbin_end, 1) == '\n') + add_space = true; + else if (c_unbin[i] == '\n') + add_space = false; + + if (add_space && *std::prev(it_unbin_end, 1) != ' ' && + (c_unbin[i] == 'X' || c_unbin[i] == 'Y' || c_unbin[i] == 'Z' || c_unbin[i] == 'E' || c_unbin[i] == 'F')) { + *it_unbin_end = ' '; + ++it_unbin_end; + } + + if (c_unbin[i] != '\n' || std::distance(unbin_buffer.begin(), it_unbin_end) == 0 || *std::prev(it_unbin_end, 1) != '\n') { + *it_unbin_end = c_unbin[i]; + ++it_unbin_end; + } + +#if ENABLE_BINARIZED_GCODE_DEBUG + if (c_unbin[i] == '\n') { + const std::string out(unbin_buffer.begin() + line_start, it_unbin_end); + if (!out.empty()) { + OutputDebugStringA(out.c_str()); + line_start = std::distance(unbin_buffer.begin(), it_unbin_end); + } + } +#endif // ENABLE_BINARIZED_GCODE_DEBUG + } + + ++it_bin; + } + + dst.insert(dst.end(), unbin_buffer.begin(), it_unbin_end); +} +} // namespace MeatPack std::string translate_result(BinaryGCode::EResult result) { @@ -52,7 +474,7 @@ static uint16_t block_types_count() { return 1 + (uint16_t)EBlockTyp static uint16_t compression_types_count() { return 1 + (uint16_t)ECompressionType::None; } static uint16_t thumbnail_formats_count() { return 1 + (uint16_t)EThumbnailFormat::QOI; } static uint16_t metadata_encoding_types_count() { return 1 + (uint16_t)EMetadataEncodingType::INI; } -static uint16_t gcode_encoding_types_count() { return 1 + (uint16_t)EGCodeEncodingType::MeatPack; } +static uint16_t gcode_encoding_types_count() { return 1 + (uint16_t)EGCodeEncodingType::MeatPackComments; } static bool write_to_file(FILE& file, const void* data, size_t data_size) { @@ -85,24 +507,6 @@ static bool encode_metadata(const std::vector& dst, EGCodeEncodingType encoding_type) -{ - switch (encoding_type) - { - case EGCodeEncodingType::None: - { - dst.insert(dst.end(), src.begin(), src.end()); - break; - } - case EGCodeEncodingType::MeatPack: - { - // TODO - break; - } - } - return true; -} - static bool decode_metadata(const std::vector& src, std::vector>& dst, EMetadataEncodingType encoding_type) { @@ -130,6 +534,39 @@ static bool decode_metadata(const std::vector& src, std::vector& dst, EGCodeEncodingType encoding_type) +{ + switch (encoding_type) + { + case EGCodeEncodingType::None: + { + dst.insert(dst.end(), src.begin(), src.end()); + break; + } + case EGCodeEncodingType::MeatPack: + case EGCodeEncodingType::MeatPackComments: + { + uint8_t binarizer_flags = (encoding_type == EGCodeEncodingType::MeatPack) ? MeatPack::Flag_RemoveComments : 0; + binarizer_flags |= MeatPack::Flag_OmitWhitespaces; + MeatPack::MPBinarizer binarizer(binarizer_flags); + binarizer.initialize(dst); + auto begin_it = src.begin(); + auto end_it = src.begin(); + while (end_it != src.end()) { + while (end_it != src.end() && *end_it != '\n') { + ++end_it; + } + const std::string line(begin_it, ++end_it); + binarizer.binarize_line(line, dst); + begin_it = end_it; + } + binarizer.finalize(dst); + break; + } + } + return true; +} + static bool decode_gcode(const std::vector& src, std::string& dst, EGCodeEncodingType encoding_type) { switch (encoding_type) @@ -140,8 +577,9 @@ static bool decode_gcode(const std::vector& src, std::string& dst, EGCo break; } case EGCodeEncodingType::MeatPack: + case EGCodeEncodingType::MeatPackComments: { - // TODO + MeatPack::unbinarize(src, dst); break; } } @@ -837,13 +1275,12 @@ EResult Binarizer::initialize(FILE& file, const BinarizerConfig& config) return EResult::Success; } -static EResult write_gcode_block(FILE& file, const std::string& raw_data, EGCodeEncodingType encoding_type, ECompressionType compression_type, - EChecksumType checksum_type) +static EResult write_gcode_block(FILE& file, const std::string& raw_data, const BinarizerConfig& config) { GCodeBlock block; - block.encoding_type = (uint16_t)encoding_type; + block.encoding_type = (uint16_t)config.gcode_encoding; block.raw_data = raw_data; - return block.write(file, compression_type, checksum_type); + return block.write(file, config.compression, config.checksum); } EResult Binarizer::append_gcode(const std::string& gcode) @@ -865,7 +1302,7 @@ EResult Binarizer::append_gcode(const std::string& gcode) const size_t line_size = 1 + end_line_pos - begin_pos; if (line_size + m_gcode_cache.length() > MAX_GCODE_CACHE_SIZE) { if (!m_gcode_cache.empty()) { - const EResult res = write_gcode_block(*m_file, m_gcode_cache, m_config.gcode_encoding, m_config.compression, m_config.checksum); + const EResult res = write_gcode_block(*m_file, m_gcode_cache, m_config); if (res != EResult::Success) // propagate error return res; @@ -891,7 +1328,7 @@ EResult Binarizer::finalize() // save gcode cache, if not empty if (!m_gcode_cache.empty()) { - const EResult res = write_gcode_block(*m_file, m_gcode_cache, m_config.gcode_encoding, m_config.compression, m_config.checksum); + const EResult res = write_gcode_block(*m_file, m_gcode_cache, m_config); if (res != EResult::Success) // propagate error return res; diff --git a/src/libslic3r/GCode/GCodeBinarizer.hpp b/src/libslic3r/GCode/GCodeBinarizer.hpp index ad4d1c640c..595f5da806 100644 --- a/src/libslic3r/GCode/GCodeBinarizer.hpp +++ b/src/libslic3r/GCode/GCodeBinarizer.hpp @@ -202,6 +202,7 @@ enum class EGCodeEncodingType : uint16_t { None, MeatPack, + MeatPackComments }; struct GCodeBlock @@ -257,7 +258,7 @@ struct BinarizerConfig ECompressionType compression{ ECompressionType::None }; EGCodeEncodingType gcode_encoding{ EGCodeEncodingType::None }; EMetadataEncodingType metadata_encoding{ EMetadataEncodingType::INI }; - EChecksumType checksum{ EChecksumType::None }; + EChecksumType checksum{ EChecksumType::CRC32 }; }; class Binarizer diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 9b89596001..1ee779e421 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -7885,36 +7885,36 @@ void GLCanvas3D::show_binary_gcode_debug_window() imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Compression"); ImGui::TableSetColumnIndex(1); const std::vector gcode_compressions = { "None" }; - int compression = (int)binarizer_config.compression; - if (imgui.combo(std::string("##1"), gcode_compressions, compression, ImGuiComboFlags_HeightLargest, 0.0f, 100.0f)) - binarizer_config.compression = (BinaryGCode::ECompressionType)compression; + int compressions_id = (int)binarizer_config.compression; + if (imgui.combo(std::string("##1"), gcode_compressions, compressions_id, ImGuiComboFlags_HeightLargest, 0.0f, 200.0f)) + binarizer_config.compression = (BinaryGCode::ECompressionType)compressions_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "GGcode encoding"); ImGui::TableSetColumnIndex(1); - const std::vector gcode_encodings = { "None", "MeatPack" }; - int gcode_encoding = (int)binarizer_config.gcode_encoding; - if (imgui.combo(std::string("##2"), gcode_encodings, gcode_encoding, ImGuiComboFlags_HeightLargest, 0.0f, 100.0f)) - binarizer_config.gcode_encoding = (BinaryGCode::EGCodeEncodingType)gcode_encoding; + const std::vector gcode_encodings = { "None", "MeatPack", "MeatPack Comments"}; + int gcode_encodings_id = (int)binarizer_config.gcode_encoding; + if (imgui.combo(std::string("##2"), gcode_encodings, gcode_encodings_id, ImGuiComboFlags_HeightLargest, 0.0f, 200.0f)) + binarizer_config.gcode_encoding = (BinaryGCode::EGCodeEncodingType)gcode_encodings_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Metadata encoding"); ImGui::TableSetColumnIndex(1); const std::vector metadata_encodings = { "INI" }; - int metadata_encoding = (int)binarizer_config.metadata_encoding; - if (imgui.combo(std::string("##3"), metadata_encodings, metadata_encoding, ImGuiComboFlags_HeightLargest, 0.0f, 100.0f)) - binarizer_config.metadata_encoding = (BinaryGCode::EMetadataEncodingType)metadata_encoding; + int metadata_encodings_id = (int)binarizer_config.metadata_encoding; + if (imgui.combo(std::string("##3"), metadata_encodings, metadata_encodings_id, ImGuiComboFlags_HeightLargest, 0.0f, 200.0f)) + binarizer_config.metadata_encoding = (BinaryGCode::EMetadataEncodingType)metadata_encodings_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Checksum type"); ImGui::TableSetColumnIndex(1); const std::vector checksums = { "None", "CRC32" }; - int checksum = (int)binarizer_config.checksum; - if (imgui.combo(std::string("##4"), checksums, checksum, ImGuiComboFlags_HeightLargest, 0.0f, 100.0f)) - binarizer_config.checksum = (BinaryGCode::EChecksumType)checksum; + int checksums_id = (int)binarizer_config.checksum; + if (imgui.combo(std::string("##4"), checksums, checksums_id, ImGuiComboFlags_HeightLargest, 0.0f, 200.0f)) + binarizer_config.checksum = (BinaryGCode::EChecksumType)checksums_id; ImGui::EndTable(); From 95641d026924d2446e388c232c47a12213eb37cb Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 24 Jul 2023 17:18:20 +0200 Subject: [PATCH 024/136] ArcWelder: Fixed arc_angle() in case the two arc points are further than a circle diameter. This may happen for a perfectly valid 180 degrees circle due to numerical errors. Added some asserts and comments elsewhere. --- src/libslic3r/GCode/GCodeWriter.cpp | 8 ++++++++ src/libslic3r/Geometry/ArcWelder.hpp | 6 ++++-- src/libslic3r/Geometry/Circle.cpp | 11 ++++++++--- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/GCode/GCodeWriter.cpp b/src/libslic3r/GCode/GCodeWriter.cpp index 4375e2e1de..2dad407caa 100644 --- a/src/libslic3r/GCode/GCodeWriter.cpp +++ b/src/libslic3r/GCode/GCodeWriter.cpp @@ -375,6 +375,9 @@ bool GCodeWriter::will_move_z(double z) const std::string GCodeWriter::extrude_to_xy(const Vec2d &point, double dE, const std::string_view comment) { + assert(dE != 0); + assert(std::abs(dE) < 1000.0); + m_pos.head<2>() = point.head<2>(); GCodeG1Formatter w; @@ -386,6 +389,7 @@ std::string GCodeWriter::extrude_to_xy(const Vec2d &point, double dE, const std: std::string GCodeWriter::extrude_to_xy_G2G3IJ(const Vec2d &point, const Vec2d &ij, const bool ccw, double dE, const std::string_view comment) { + assert(std::abs(dE) < 1000.0); assert(dE != 0); assert(std::abs(point.x()) < 1200.); assert(std::abs(point.y()) < 1200.); @@ -404,6 +408,7 @@ std::string GCodeWriter::extrude_to_xy_G2G3IJ(const Vec2d &point, const Vec2d &i std::string GCodeWriter::extrude_to_xy_G2G3R(const Vec2d &point, const double radius, const bool ccw, double dE, const std::string_view comment) { assert(dE != 0); + assert(std::abs(dE) < 1000.0); assert(std::abs(point.x()) < 1200.); assert(std::abs(point.y()) < 1200.); assert(std::abs(radius) < 1800.); @@ -456,6 +461,9 @@ std::string GCodeWriter::retract_for_toolchange(bool before_wipe) std::string GCodeWriter::_retract(double length, double restart_extra, const std::string_view comment) { + assert(std::abs(length) < 1000.0); + assert(std::abs(restart_extra) < 1000.0); + /* If firmware retraction is enabled, we use a fake value of 1 since we ignore the actual configured retract_length which might be 0, in which case the retraction logic gets skipped. */ diff --git a/src/libslic3r/Geometry/ArcWelder.hpp b/src/libslic3r/Geometry/ArcWelder.hpp index 5bf806c702..b328c12786 100644 --- a/src/libslic3r/Geometry/ArcWelder.hpp +++ b/src/libslic3r/Geometry/ArcWelder.hpp @@ -52,8 +52,10 @@ inline typename Derived::Scalar arc_angle( static_assert(std::is_same::value, "arc_angle(): Both vectors must be of the same type."); assert(radius != 0); using Float = typename Derived::Scalar; - Float a = Float(2.) * asin(Float(0.5) * (end_pos - start_pos).norm() / radius); - return radius > Float(0) ? a : Float(2. * M_PI) + a; + Float a = Float(0.5) * (end_pos - start_pos).norm() / radius; + return radius > Float(0) ? + (a > Float( 1.) ? Float( M_PI) : Float(2.) * std::asin(a)) : + (a < Float(-1.) ? Float( - M_PI) : Float(2. * M_PI) + Float(2.) * std::asin(a)); } // Calculate positive length of an arc given two points and a radius. diff --git a/src/libslic3r/Geometry/Circle.cpp b/src/libslic3r/Geometry/Circle.cpp index 6796671954..cc222aa155 100644 --- a/src/libslic3r/Geometry/Circle.cpp +++ b/src/libslic3r/Geometry/Circle.cpp @@ -17,9 +17,14 @@ Point circle_center_taubin_newton(const Points::const_iterator& input_begin, con return Point::new_scale(center.x(), center.y()); } -/// Adapted from work in "Circular and Linear Regression: Fitting circles and lines by least squares", pg 126 -/// Returns a point corresponding to the center of a circle for which all of the points from input_begin to input_end -/// lie on. +// Robust and accurate algebraic circle fit, which works well even if data points are observed only within a small arc. +// The method was proposed by G. Taubin in +// "Estimation Of Planar Curves, Surfaces And Nonplanar Space Curves Defined By Implicit Equations, +// With Applications To Edge And Range Image Segmentation", IEEE Trans. PAMI, Vol. 13, pages 1115-1138, (1991)." +// This particular implementation was adapted from +// "Circular and Linear Regression: Fitting circles and lines by least squares", pg 126" +// Returns a point corresponding to the center of a circle for which all of the points from input_begin to input_end +// lie on. Vec2d circle_center_taubin_newton(const Vec2ds::const_iterator& input_begin, const Vec2ds::const_iterator& input_end, size_t cycles) { // calculate the centroid of the data set From 7e56d807185348987026786c4f8607fcfdc84d6e Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 25 Jul 2023 13:30:18 +0200 Subject: [PATCH 025/136] SPE-1784: New compressed (binary) gcode format integration Added Heatshrink compression --- src/CMakeLists.txt | 1 + src/heatshrink/CMakeLists.txt | 11 + src/heatshrink/LICENSE | 14 + src/heatshrink/README.md | 7 + src/heatshrink/heatshrink_common.h | 20 + src/heatshrink/heatshrink_config.h | 26 ++ src/heatshrink/heatshrink_decoder.c | 367 +++++++++++++++ src/heatshrink/heatshrink_decoder.h | 100 ++++ src/heatshrink/heatshrink_encoder.c | 604 +++++++++++++++++++++++++ src/heatshrink/heatshrink_encoder.h | 109 +++++ src/libslic3r/CMakeLists.txt | 1 + src/libslic3r/GCode/GCodeBinarizer.cpp | 158 ++++++- src/libslic3r/GCode/GCodeBinarizer.hpp | 13 +- src/slic3r/GUI/GLCanvas3D.cpp | 85 +++- 14 files changed, 1482 insertions(+), 34 deletions(-) create mode 100644 src/heatshrink/CMakeLists.txt create mode 100644 src/heatshrink/LICENSE create mode 100644 src/heatshrink/README.md create mode 100644 src/heatshrink/heatshrink_common.h create mode 100644 src/heatshrink/heatshrink_config.h create mode 100644 src/heatshrink/heatshrink_decoder.c create mode 100644 src/heatshrink/heatshrink_decoder.h create mode 100644 src/heatshrink/heatshrink_encoder.c create mode 100644 src/heatshrink/heatshrink_encoder.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 50eccfc849..9a037e44fd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -14,6 +14,7 @@ add_subdirectory(libigl) add_subdirectory(hints) add_subdirectory(qoi) add_subdirectory(libnest2d) +add_subdirectory(heatshrink) find_package(Qhull 7.2 REQUIRED) add_library(qhull INTERFACE) diff --git a/src/heatshrink/CMakeLists.txt b/src/heatshrink/CMakeLists.txt new file mode 100644 index 0000000000..94c93315ba --- /dev/null +++ b/src/heatshrink/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 2.8.12) +project(heatshrink) + +add_library(heatshrink STATIC + heatshrink_common.h + heatshrink_config.h + heatshrink_decoder.c + heatshrink_decoder.h + heatshrink_encoder.c + heatshrink_encoder.h +) diff --git a/src/heatshrink/LICENSE b/src/heatshrink/LICENSE new file mode 100644 index 0000000000..9132cb6a0c --- /dev/null +++ b/src/heatshrink/LICENSE @@ -0,0 +1,14 @@ +Copyright (c) 2013-2015, Scott Vokes +All rights reserved. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/src/heatshrink/README.md b/src/heatshrink/README.md new file mode 100644 index 0000000000..4de08bee29 --- /dev/null +++ b/src/heatshrink/README.md @@ -0,0 +1,7 @@ +** heatshrink is a data compression library for embedded/real-time systems.** + +For more information go to https://spin.atomicobject.com/2013/03/14/heatshrink-embedded-data-compression/ + +THIS DIRECTORY CONTAINS THE heatshrink v0.4.1 b9ac05e SOURCE DISTRIBUTION. + + diff --git a/src/heatshrink/heatshrink_common.h b/src/heatshrink/heatshrink_common.h new file mode 100644 index 0000000000..bc89774f5e --- /dev/null +++ b/src/heatshrink/heatshrink_common.h @@ -0,0 +1,20 @@ +#ifndef HEATSHRINK_H +#define HEATSHRINK_H + +#define HEATSHRINK_AUTHOR "Scott Vokes " +#define HEATSHRINK_URL "https://github.com/atomicobject/heatshrink" + +/* Version 0.4.1 */ +#define HEATSHRINK_VERSION_MAJOR 0 +#define HEATSHRINK_VERSION_MINOR 4 +#define HEATSHRINK_VERSION_PATCH 1 + +#define HEATSHRINK_MIN_WINDOW_BITS 4 +#define HEATSHRINK_MAX_WINDOW_BITS 15 + +#define HEATSHRINK_MIN_LOOKAHEAD_BITS 3 + +#define HEATSHRINK_LITERAL_MARKER 0x01 +#define HEATSHRINK_BACKREF_MARKER 0x00 + +#endif diff --git a/src/heatshrink/heatshrink_config.h b/src/heatshrink/heatshrink_config.h new file mode 100644 index 0000000000..13135b9395 --- /dev/null +++ b/src/heatshrink/heatshrink_config.h @@ -0,0 +1,26 @@ +#ifndef HEATSHRINK_CONFIG_H +#define HEATSHRINK_CONFIG_H + +/* Should functionality assuming dynamic allocation be used? */ +#ifndef HEATSHRINK_DYNAMIC_ALLOC +#define HEATSHRINK_DYNAMIC_ALLOC 1 +#endif + +#if HEATSHRINK_DYNAMIC_ALLOC + /* Optional replacement of malloc/free */ + #define HEATSHRINK_MALLOC(SZ) malloc(SZ) + #define HEATSHRINK_FREE(P, SZ) free(P) +#else + /* Required parameters for static configuration */ + #define HEATSHRINK_STATIC_INPUT_BUFFER_SIZE 32 + #define HEATSHRINK_STATIC_WINDOW_BITS 8 + #define HEATSHRINK_STATIC_LOOKAHEAD_BITS 4 +#endif + +/* Turn on logging for debugging. */ +#define HEATSHRINK_DEBUGGING_LOGS 0 + +/* Use indexing for faster compression. (This requires additional space.) */ +#define HEATSHRINK_USE_INDEX 1 + +#endif diff --git a/src/heatshrink/heatshrink_decoder.c b/src/heatshrink/heatshrink_decoder.c new file mode 100644 index 0000000000..0f118cf98a --- /dev/null +++ b/src/heatshrink/heatshrink_decoder.c @@ -0,0 +1,367 @@ +#include +#include +#include "heatshrink_decoder.h" + +/* States for the polling state machine. */ +typedef enum { + HSDS_TAG_BIT, /* tag bit */ + HSDS_YIELD_LITERAL, /* ready to yield literal byte */ + HSDS_BACKREF_INDEX_MSB, /* most significant byte of index */ + HSDS_BACKREF_INDEX_LSB, /* least significant byte of index */ + HSDS_BACKREF_COUNT_MSB, /* most significant byte of count */ + HSDS_BACKREF_COUNT_LSB, /* least significant byte of count */ + HSDS_YIELD_BACKREF, /* ready to yield back-reference */ +} HSD_state; + +#if HEATSHRINK_DEBUGGING_LOGS +#include +#include +#include +#define LOG(...) fprintf(stderr, __VA_ARGS__) +#define ASSERT(X) assert(X) +static const char *state_names[] = { + "tag_bit", + "yield_literal", + "backref_index_msb", + "backref_index_lsb", + "backref_count_msb", + "backref_count_lsb", + "yield_backref", +}; +#else +#define LOG(...) /* no-op */ +#define ASSERT(X) /* no-op */ +#endif + +typedef struct { + uint8_t *buf; /* output buffer */ + size_t buf_size; /* buffer size */ + size_t *output_size; /* bytes pushed to buffer, so far */ +} output_info; + +#define NO_BITS ((uint16_t)-1) + +/* Forward references. */ +static uint16_t get_bits(heatshrink_decoder *hsd, uint8_t count); +static void push_byte(heatshrink_decoder *hsd, output_info *oi, uint8_t byte); + +#if HEATSHRINK_DYNAMIC_ALLOC +heatshrink_decoder *heatshrink_decoder_alloc(uint16_t input_buffer_size, + uint8_t window_sz2, + uint8_t lookahead_sz2) { + if ((window_sz2 < HEATSHRINK_MIN_WINDOW_BITS) || + (window_sz2 > HEATSHRINK_MAX_WINDOW_BITS) || + (input_buffer_size == 0) || + (lookahead_sz2 < HEATSHRINK_MIN_LOOKAHEAD_BITS) || + (lookahead_sz2 >= window_sz2)) { + return NULL; + } + size_t buffers_sz = (1 << window_sz2) + input_buffer_size; + size_t sz = sizeof(heatshrink_decoder) + buffers_sz; + heatshrink_decoder *hsd = HEATSHRINK_MALLOC(sz); + if (hsd == NULL) { return NULL; } + hsd->input_buffer_size = input_buffer_size; + hsd->window_sz2 = window_sz2; + hsd->lookahead_sz2 = lookahead_sz2; + heatshrink_decoder_reset(hsd); + LOG("-- allocated decoder with buffer size of %zu (%zu + %u + %u)\n", + sz, sizeof(heatshrink_decoder), (1 << window_sz2), input_buffer_size); + return hsd; +} + +void heatshrink_decoder_free(heatshrink_decoder *hsd) { + size_t buffers_sz = (1 << hsd->window_sz2) + hsd->input_buffer_size; + size_t sz = sizeof(heatshrink_decoder) + buffers_sz; + HEATSHRINK_FREE(hsd, sz); + (void)sz; /* may not be used by free */ +} +#endif + +void heatshrink_decoder_reset(heatshrink_decoder *hsd) { + size_t buf_sz = 1 << HEATSHRINK_DECODER_WINDOW_BITS(hsd); + size_t input_sz = HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd); + memset(hsd->buffers, 0, buf_sz + input_sz); + hsd->state = HSDS_TAG_BIT; + hsd->input_size = 0; + hsd->input_index = 0; + hsd->bit_index = 0x00; + hsd->current_byte = 0x00; + hsd->output_count = 0; + hsd->output_index = 0; + hsd->head_index = 0; +} + +/* Copy SIZE bytes into the decoder's input buffer, if it will fit. */ +HSD_sink_res heatshrink_decoder_sink(heatshrink_decoder *hsd, + uint8_t *in_buf, size_t size, size_t *input_size) { + if ((hsd == NULL) || (in_buf == NULL) || (input_size == NULL)) { + return HSDR_SINK_ERROR_NULL; + } + + size_t rem = HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd) - hsd->input_size; + if (rem == 0) { + *input_size = 0; + return HSDR_SINK_FULL; + } + + size = rem < size ? rem : size; + LOG("-- sinking %zd bytes\n", size); + /* copy into input buffer (at head of buffers) */ + memcpy(&hsd->buffers[hsd->input_size], in_buf, size); + hsd->input_size += size; + *input_size = size; + return HSDR_SINK_OK; +} + + +/***************** + * Decompression * + *****************/ + +#define BACKREF_COUNT_BITS(HSD) (HEATSHRINK_DECODER_LOOKAHEAD_BITS(HSD)) +#define BACKREF_INDEX_BITS(HSD) (HEATSHRINK_DECODER_WINDOW_BITS(HSD)) + +// States +static HSD_state st_tag_bit(heatshrink_decoder *hsd); +static HSD_state st_yield_literal(heatshrink_decoder *hsd, + output_info *oi); +static HSD_state st_backref_index_msb(heatshrink_decoder *hsd); +static HSD_state st_backref_index_lsb(heatshrink_decoder *hsd); +static HSD_state st_backref_count_msb(heatshrink_decoder *hsd); +static HSD_state st_backref_count_lsb(heatshrink_decoder *hsd); +static HSD_state st_yield_backref(heatshrink_decoder *hsd, + output_info *oi); + +HSD_poll_res heatshrink_decoder_poll(heatshrink_decoder *hsd, + uint8_t *out_buf, size_t out_buf_size, size_t *output_size) { + if ((hsd == NULL) || (out_buf == NULL) || (output_size == NULL)) { + return HSDR_POLL_ERROR_NULL; + } + *output_size = 0; + + output_info oi; + oi.buf = out_buf; + oi.buf_size = out_buf_size; + oi.output_size = output_size; + + while (1) { + LOG("-- poll, state is %d (%s), input_size %d\n", + hsd->state, state_names[hsd->state], hsd->input_size); + uint8_t in_state = hsd->state; + switch (in_state) { + case HSDS_TAG_BIT: + hsd->state = st_tag_bit(hsd); + break; + case HSDS_YIELD_LITERAL: + hsd->state = st_yield_literal(hsd, &oi); + break; + case HSDS_BACKREF_INDEX_MSB: + hsd->state = st_backref_index_msb(hsd); + break; + case HSDS_BACKREF_INDEX_LSB: + hsd->state = st_backref_index_lsb(hsd); + break; + case HSDS_BACKREF_COUNT_MSB: + hsd->state = st_backref_count_msb(hsd); + break; + case HSDS_BACKREF_COUNT_LSB: + hsd->state = st_backref_count_lsb(hsd); + break; + case HSDS_YIELD_BACKREF: + hsd->state = st_yield_backref(hsd, &oi); + break; + default: + return HSDR_POLL_ERROR_UNKNOWN; + } + + /* If the current state cannot advance, check if input or output + * buffer are exhausted. */ + if (hsd->state == in_state) { + if (*output_size == out_buf_size) { return HSDR_POLL_MORE; } + return HSDR_POLL_EMPTY; + } + } +} + +static HSD_state st_tag_bit(heatshrink_decoder *hsd) { + uint32_t bits = get_bits(hsd, 1); // get tag bit + if (bits == NO_BITS) { + return HSDS_TAG_BIT; + } else if (bits) { + return HSDS_YIELD_LITERAL; + } else if (HEATSHRINK_DECODER_WINDOW_BITS(hsd) > 8) { + return HSDS_BACKREF_INDEX_MSB; + } else { + hsd->output_index = 0; + return HSDS_BACKREF_INDEX_LSB; + } +} + +static HSD_state st_yield_literal(heatshrink_decoder *hsd, + output_info *oi) { + /* Emit a repeated section from the window buffer, and add it (again) + * to the window buffer. (Note that the repetition can include + * itself.)*/ + if (*oi->output_size < oi->buf_size) { + uint16_t byte = get_bits(hsd, 8); + if (byte == NO_BITS) { return HSDS_YIELD_LITERAL; } /* out of input */ + uint8_t *buf = &hsd->buffers[HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd)]; + uint16_t mask = (1 << HEATSHRINK_DECODER_WINDOW_BITS(hsd)) - 1; + uint8_t c = byte & 0xFF; + LOG("-- emitting literal byte 0x%02x ('%c')\n", c, isprint(c) ? c : '.'); + buf[hsd->head_index++ & mask] = c; + push_byte(hsd, oi, c); + return HSDS_TAG_BIT; + } else { + return HSDS_YIELD_LITERAL; + } +} + +static HSD_state st_backref_index_msb(heatshrink_decoder *hsd) { + uint8_t bit_ct = BACKREF_INDEX_BITS(hsd); + ASSERT(bit_ct > 8); + uint16_t bits = get_bits(hsd, bit_ct - 8); + LOG("-- backref index (msb), got 0x%04x (+1)\n", bits); + if (bits == NO_BITS) { return HSDS_BACKREF_INDEX_MSB; } + hsd->output_index = bits << 8; + return HSDS_BACKREF_INDEX_LSB; +} + +static HSD_state st_backref_index_lsb(heatshrink_decoder *hsd) { + uint8_t bit_ct = BACKREF_INDEX_BITS(hsd); + uint16_t bits = get_bits(hsd, bit_ct < 8 ? bit_ct : 8); + LOG("-- backref index (lsb), got 0x%04x (+1)\n", bits); + if (bits == NO_BITS) { return HSDS_BACKREF_INDEX_LSB; } + hsd->output_index |= bits; + hsd->output_index++; + uint8_t br_bit_ct = BACKREF_COUNT_BITS(hsd); + hsd->output_count = 0; + return (br_bit_ct > 8) ? HSDS_BACKREF_COUNT_MSB : HSDS_BACKREF_COUNT_LSB; +} + +static HSD_state st_backref_count_msb(heatshrink_decoder *hsd) { + uint8_t br_bit_ct = BACKREF_COUNT_BITS(hsd); + ASSERT(br_bit_ct > 8); + uint16_t bits = get_bits(hsd, br_bit_ct - 8); + LOG("-- backref count (msb), got 0x%04x (+1)\n", bits); + if (bits == NO_BITS) { return HSDS_BACKREF_COUNT_MSB; } + hsd->output_count = bits << 8; + return HSDS_BACKREF_COUNT_LSB; +} + +static HSD_state st_backref_count_lsb(heatshrink_decoder *hsd) { + uint8_t br_bit_ct = BACKREF_COUNT_BITS(hsd); + uint16_t bits = get_bits(hsd, br_bit_ct < 8 ? br_bit_ct : 8); + LOG("-- backref count (lsb), got 0x%04x (+1)\n", bits); + if (bits == NO_BITS) { return HSDS_BACKREF_COUNT_LSB; } + hsd->output_count |= bits; + hsd->output_count++; + return HSDS_YIELD_BACKREF; +} + +static HSD_state st_yield_backref(heatshrink_decoder *hsd, + output_info *oi) { + size_t count = oi->buf_size - *oi->output_size; + if (count > 0) { + size_t i = 0; + if (hsd->output_count < count) count = hsd->output_count; + uint8_t *buf = &hsd->buffers[HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd)]; + uint16_t mask = (1 << HEATSHRINK_DECODER_WINDOW_BITS(hsd)) - 1; + uint16_t neg_offset = hsd->output_index; + LOG("-- emitting %zu bytes from -%u bytes back\n", count, neg_offset); + ASSERT(neg_offset <= mask + 1); + ASSERT(count <= (size_t)(1 << BACKREF_COUNT_BITS(hsd))); + + for (i=0; ihead_index - neg_offset) & mask]; + push_byte(hsd, oi, c); + buf[hsd->head_index & mask] = c; + hsd->head_index++; + LOG(" -- ++ 0x%02x\n", c); + } + hsd->output_count -= count; + if (hsd->output_count == 0) { return HSDS_TAG_BIT; } + } + return HSDS_YIELD_BACKREF; +} + +/* Get the next COUNT bits from the input buffer, saving incremental progress. + * Returns NO_BITS on end of input, or if more than 15 bits are requested. */ +static uint16_t get_bits(heatshrink_decoder *hsd, uint8_t count) { + uint16_t accumulator = 0; + int i = 0; + if (count > 15) { return NO_BITS; } + LOG("-- popping %u bit(s)\n", count); + + /* If we aren't able to get COUNT bits, suspend immediately, because we + * don't track how many bits of COUNT we've accumulated before suspend. */ + if (hsd->input_size == 0) { + if (hsd->bit_index < (1 << (count - 1))) { return NO_BITS; } + } + + for (i = 0; i < count; i++) { + if (hsd->bit_index == 0x00) { + if (hsd->input_size == 0) { + LOG(" -- out of bits, suspending w/ accumulator of %u (0x%02x)\n", + accumulator, accumulator); + return NO_BITS; + } + hsd->current_byte = hsd->buffers[hsd->input_index++]; + LOG(" -- pulled byte 0x%02x\n", hsd->current_byte); + if (hsd->input_index == hsd->input_size) { + hsd->input_index = 0; /* input is exhausted */ + hsd->input_size = 0; + } + hsd->bit_index = 0x80; + } + accumulator <<= 1; + if (hsd->current_byte & hsd->bit_index) { + accumulator |= 0x01; + if (0) { + LOG(" -- got 1, accumulator 0x%04x, bit_index 0x%02x\n", + accumulator, hsd->bit_index); + } + } else { + if (0) { + LOG(" -- got 0, accumulator 0x%04x, bit_index 0x%02x\n", + accumulator, hsd->bit_index); + } + } + hsd->bit_index >>= 1; + } + + if (count > 1) { LOG(" -- accumulated %08x\n", accumulator); } + return accumulator; +} + +HSD_finish_res heatshrink_decoder_finish(heatshrink_decoder *hsd) { + if (hsd == NULL) { return HSDR_FINISH_ERROR_NULL; } + switch (hsd->state) { + case HSDS_TAG_BIT: + return hsd->input_size == 0 ? HSDR_FINISH_DONE : HSDR_FINISH_MORE; + + /* If we want to finish with no input, but are in these states, it's + * because the 0-bit padding to the last byte looks like a backref + * marker bit followed by all 0s for index and count bits. */ + case HSDS_BACKREF_INDEX_LSB: + case HSDS_BACKREF_INDEX_MSB: + case HSDS_BACKREF_COUNT_LSB: + case HSDS_BACKREF_COUNT_MSB: + return hsd->input_size == 0 ? HSDR_FINISH_DONE : HSDR_FINISH_MORE; + + /* If the output stream is padded with 0xFFs (possibly due to being in + * flash memory), also explicitly check the input size rather than + * uselessly returning MORE but yielding 0 bytes when polling. */ + case HSDS_YIELD_LITERAL: + return hsd->input_size == 0 ? HSDR_FINISH_DONE : HSDR_FINISH_MORE; + + default: + return HSDR_FINISH_MORE; + } +} + +static void push_byte(heatshrink_decoder *hsd, output_info *oi, uint8_t byte) { + LOG(" -- pushing byte: 0x%02x ('%c')\n", byte, isprint(byte) ? byte : '.'); + oi->buf[(*oi->output_size)++] = byte; + (void)hsd; +} diff --git a/src/heatshrink/heatshrink_decoder.h b/src/heatshrink/heatshrink_decoder.h new file mode 100644 index 0000000000..bda83991ef --- /dev/null +++ b/src/heatshrink/heatshrink_decoder.h @@ -0,0 +1,100 @@ +#ifndef HEATSHRINK_DECODER_H +#define HEATSHRINK_DECODER_H + +#include +#include +#include "heatshrink_common.h" +#include "heatshrink_config.h" + +typedef enum { + HSDR_SINK_OK, /* data sunk, ready to poll */ + HSDR_SINK_FULL, /* out of space in internal buffer */ + HSDR_SINK_ERROR_NULL=-1, /* NULL argument */ +} HSD_sink_res; + +typedef enum { + HSDR_POLL_EMPTY, /* input exhausted */ + HSDR_POLL_MORE, /* more data remaining, call again w/ fresh output buffer */ + HSDR_POLL_ERROR_NULL=-1, /* NULL arguments */ + HSDR_POLL_ERROR_UNKNOWN=-2, +} HSD_poll_res; + +typedef enum { + HSDR_FINISH_DONE, /* output is done */ + HSDR_FINISH_MORE, /* more output remains */ + HSDR_FINISH_ERROR_NULL=-1, /* NULL arguments */ +} HSD_finish_res; + +#if HEATSHRINK_DYNAMIC_ALLOC +#define HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(BUF) \ + ((BUF)->input_buffer_size) +#define HEATSHRINK_DECODER_WINDOW_BITS(BUF) \ + ((BUF)->window_sz2) +#define HEATSHRINK_DECODER_LOOKAHEAD_BITS(BUF) \ + ((BUF)->lookahead_sz2) +#else +#define HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(_) \ + HEATSHRINK_STATIC_INPUT_BUFFER_SIZE +#define HEATSHRINK_DECODER_WINDOW_BITS(_) \ + (HEATSHRINK_STATIC_WINDOW_BITS) +#define HEATSHRINK_DECODER_LOOKAHEAD_BITS(BUF) \ + (HEATSHRINK_STATIC_LOOKAHEAD_BITS) +#endif + +typedef struct { + uint16_t input_size; /* bytes in input buffer */ + uint16_t input_index; /* offset to next unprocessed input byte */ + uint16_t output_count; /* how many bytes to output */ + uint16_t output_index; /* index for bytes to output */ + uint16_t head_index; /* head of window buffer */ + uint8_t state; /* current state machine node */ + uint8_t current_byte; /* current byte of input */ + uint8_t bit_index; /* current bit index */ + +#if HEATSHRINK_DYNAMIC_ALLOC + /* Fields that are only used if dynamically allocated. */ + uint8_t window_sz2; /* window buffer bits */ + uint8_t lookahead_sz2; /* lookahead bits */ + uint16_t input_buffer_size; /* input buffer size */ + + /* Input buffer, then expansion window buffer */ + uint8_t buffers[]; +#else + /* Input buffer, then expansion window buffer */ + uint8_t buffers[(1 << HEATSHRINK_DECODER_WINDOW_BITS(_)) + + HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(_)]; +#endif +} heatshrink_decoder; + +#if HEATSHRINK_DYNAMIC_ALLOC +/* Allocate a decoder with an input buffer of INPUT_BUFFER_SIZE bytes, + * an expansion buffer size of 2^WINDOW_SZ2, and a lookahead + * size of 2^lookahead_sz2. (The window buffer and lookahead sizes + * must match the settings used when the data was compressed.) + * Returns NULL on error. */ +heatshrink_decoder *heatshrink_decoder_alloc(uint16_t input_buffer_size, + uint8_t expansion_buffer_sz2, uint8_t lookahead_sz2); + +/* Free a decoder. */ +void heatshrink_decoder_free(heatshrink_decoder *hsd); +#endif + +/* Reset a decoder. */ +void heatshrink_decoder_reset(heatshrink_decoder *hsd); + +/* Sink at most SIZE bytes from IN_BUF into the decoder. *INPUT_SIZE is set to + * indicate how many bytes were actually sunk (in case a buffer was filled). */ +HSD_sink_res heatshrink_decoder_sink(heatshrink_decoder *hsd, + uint8_t *in_buf, size_t size, size_t *input_size); + +/* Poll for output from the decoder, copying at most OUT_BUF_SIZE bytes into + * OUT_BUF (setting *OUTPUT_SIZE to the actual amount copied). */ +HSD_poll_res heatshrink_decoder_poll(heatshrink_decoder *hsd, + uint8_t *out_buf, size_t out_buf_size, size_t *output_size); + +/* Notify the dencoder that the input stream is finished. + * If the return value is HSDR_FINISH_MORE, there is still more output, so + * call heatshrink_decoder_poll and repeat. */ +HSD_finish_res heatshrink_decoder_finish(heatshrink_decoder *hsd); + +#endif diff --git a/src/heatshrink/heatshrink_encoder.c b/src/heatshrink/heatshrink_encoder.c new file mode 100644 index 0000000000..edf4abebcc --- /dev/null +++ b/src/heatshrink/heatshrink_encoder.c @@ -0,0 +1,604 @@ +#include +#include +#include +#include "heatshrink_encoder.h" + +typedef enum { + HSES_NOT_FULL, /* input buffer not full enough */ + HSES_FILLED, /* buffer is full */ + HSES_SEARCH, /* searching for patterns */ + HSES_YIELD_TAG_BIT, /* yield tag bit */ + HSES_YIELD_LITERAL, /* emit literal byte */ + HSES_YIELD_BR_INDEX, /* yielding backref index */ + HSES_YIELD_BR_LENGTH, /* yielding backref length */ + HSES_SAVE_BACKLOG, /* copying buffer to backlog */ + HSES_FLUSH_BITS, /* flush bit buffer */ + HSES_DONE, /* done */ +} HSE_state; + +#if HEATSHRINK_DEBUGGING_LOGS +#include +#include +#include +#define LOG(...) fprintf(stderr, __VA_ARGS__) +#define ASSERT(X) assert(X) +static const char *state_names[] = { + "not_full", + "filled", + "search", + "yield_tag_bit", + "yield_literal", + "yield_br_index", + "yield_br_length", + "save_backlog", + "flush_bits", + "done", +}; +#else +#define LOG(...) /* no-op */ +#define ASSERT(X) /* no-op */ +#endif + +// Encoder flags +enum { + FLAG_IS_FINISHING = 0x01, +}; + +typedef struct { + uint8_t *buf; /* output buffer */ + size_t buf_size; /* buffer size */ + size_t *output_size; /* bytes pushed to buffer, so far */ +} output_info; + +#define MATCH_NOT_FOUND ((uint16_t)-1) + +static uint16_t get_input_offset(heatshrink_encoder *hse); +static uint16_t get_input_buffer_size(heatshrink_encoder *hse); +static uint16_t get_lookahead_size(heatshrink_encoder *hse); +static void add_tag_bit(heatshrink_encoder *hse, output_info *oi, uint8_t tag); +static int can_take_byte(output_info *oi); +static int is_finishing(heatshrink_encoder *hse); +static void save_backlog(heatshrink_encoder *hse); + +/* Push COUNT (max 8) bits to the output buffer, which has room. */ +static void push_bits(heatshrink_encoder *hse, uint8_t count, uint8_t bits, + output_info *oi); +static uint8_t push_outgoing_bits(heatshrink_encoder *hse, output_info *oi); +static void push_literal_byte(heatshrink_encoder *hse, output_info *oi); + +#if HEATSHRINK_DYNAMIC_ALLOC +heatshrink_encoder *heatshrink_encoder_alloc(uint8_t window_sz2, + uint8_t lookahead_sz2) { + if ((window_sz2 < HEATSHRINK_MIN_WINDOW_BITS) || + (window_sz2 > HEATSHRINK_MAX_WINDOW_BITS) || + (lookahead_sz2 < HEATSHRINK_MIN_LOOKAHEAD_BITS) || + (lookahead_sz2 >= window_sz2)) { + return NULL; + } + + /* Note: 2 * the window size is used because the buffer needs to fit + * (1 << window_sz2) bytes for the current input, and an additional + * (1 << window_sz2) bytes for the previous buffer of input, which + * will be scanned for useful backreferences. */ + size_t buf_sz = (2 << window_sz2); + + heatshrink_encoder *hse = HEATSHRINK_MALLOC(sizeof(*hse) + buf_sz); + if (hse == NULL) { return NULL; } + hse->window_sz2 = window_sz2; + hse->lookahead_sz2 = lookahead_sz2; + heatshrink_encoder_reset(hse); + +#if HEATSHRINK_USE_INDEX + size_t index_sz = buf_sz*sizeof(uint16_t); + hse->search_index = HEATSHRINK_MALLOC(index_sz + sizeof(struct hs_index)); + if (hse->search_index == NULL) { + HEATSHRINK_FREE(hse, sizeof(*hse) + buf_sz); + return NULL; + } + hse->search_index->size = index_sz; +#endif + + LOG("-- allocated encoder with buffer size of %zu (%u byte input size)\n", + buf_sz, get_input_buffer_size(hse)); + return hse; +} + +void heatshrink_encoder_free(heatshrink_encoder *hse) { + size_t buf_sz = (2 << HEATSHRINK_ENCODER_WINDOW_BITS(hse)); +#if HEATSHRINK_USE_INDEX + size_t index_sz = sizeof(struct hs_index) + hse->search_index->size; + HEATSHRINK_FREE(hse->search_index, index_sz); + (void)index_sz; +#endif + HEATSHRINK_FREE(hse, sizeof(heatshrink_encoder) + buf_sz); + (void)buf_sz; +} +#endif + +void heatshrink_encoder_reset(heatshrink_encoder *hse) { + size_t buf_sz = (2 << HEATSHRINK_ENCODER_WINDOW_BITS(hse)); + memset(hse->buffer, 0, buf_sz); + hse->input_size = 0; + hse->state = HSES_NOT_FULL; + hse->match_scan_index = 0; + hse->flags = 0; + hse->bit_index = 0x80; + hse->current_byte = 0x00; + hse->match_length = 0; + + hse->outgoing_bits = 0x0000; + hse->outgoing_bits_count = 0; + + #ifdef LOOP_DETECT + hse->loop_detect = (uint32_t)-1; + #endif +} + +HSE_sink_res heatshrink_encoder_sink(heatshrink_encoder *hse, + uint8_t *in_buf, size_t size, size_t *input_size) { + if ((hse == NULL) || (in_buf == NULL) || (input_size == NULL)) { + return HSER_SINK_ERROR_NULL; + } + + /* Sinking more content after saying the content is done, tsk tsk */ + if (is_finishing(hse)) { return HSER_SINK_ERROR_MISUSE; } + + /* Sinking more content before processing is done */ + if (hse->state != HSES_NOT_FULL) { return HSER_SINK_ERROR_MISUSE; } + + uint16_t write_offset = get_input_offset(hse) + hse->input_size; + uint16_t ibs = get_input_buffer_size(hse); + uint16_t rem = ibs - hse->input_size; + uint16_t cp_sz = rem < size ? rem : size; + + memcpy(&hse->buffer[write_offset], in_buf, cp_sz); + *input_size = cp_sz; + hse->input_size += cp_sz; + + LOG("-- sunk %u bytes (of %zu) into encoder at %d, input buffer now has %u\n", + cp_sz, size, write_offset, hse->input_size); + if (cp_sz == rem) { + LOG("-- internal buffer is now full\n"); + hse->state = HSES_FILLED; + } + + return HSER_SINK_OK; +} + + +/*************** + * Compression * + ***************/ + +static uint16_t find_longest_match(heatshrink_encoder *hse, uint16_t start, + uint16_t end, const uint16_t maxlen, uint16_t *match_length); +static void do_indexing(heatshrink_encoder *hse); + +static HSE_state st_step_search(heatshrink_encoder *hse); +static HSE_state st_yield_tag_bit(heatshrink_encoder *hse, + output_info *oi); +static HSE_state st_yield_literal(heatshrink_encoder *hse, + output_info *oi); +static HSE_state st_yield_br_index(heatshrink_encoder *hse, + output_info *oi); +static HSE_state st_yield_br_length(heatshrink_encoder *hse, + output_info *oi); +static HSE_state st_save_backlog(heatshrink_encoder *hse); +static HSE_state st_flush_bit_buffer(heatshrink_encoder *hse, + output_info *oi); + +HSE_poll_res heatshrink_encoder_poll(heatshrink_encoder *hse, + uint8_t *out_buf, size_t out_buf_size, size_t *output_size) { + if ((hse == NULL) || (out_buf == NULL) || (output_size == NULL)) { + return HSER_POLL_ERROR_NULL; + } + if (out_buf_size == 0) { + LOG("-- MISUSE: output buffer size is 0\n"); + return HSER_POLL_ERROR_MISUSE; + } + *output_size = 0; + + output_info oi; + oi.buf = out_buf; + oi.buf_size = out_buf_size; + oi.output_size = output_size; + + while (1) { + LOG("-- polling, state %u (%s), flags 0x%02x\n", + hse->state, state_names[hse->state], hse->flags); + + uint8_t in_state = hse->state; + switch (in_state) { + case HSES_NOT_FULL: + return HSER_POLL_EMPTY; + case HSES_FILLED: + do_indexing(hse); + hse->state = HSES_SEARCH; + break; + case HSES_SEARCH: + hse->state = st_step_search(hse); + break; + case HSES_YIELD_TAG_BIT: + hse->state = st_yield_tag_bit(hse, &oi); + break; + case HSES_YIELD_LITERAL: + hse->state = st_yield_literal(hse, &oi); + break; + case HSES_YIELD_BR_INDEX: + hse->state = st_yield_br_index(hse, &oi); + break; + case HSES_YIELD_BR_LENGTH: + hse->state = st_yield_br_length(hse, &oi); + break; + case HSES_SAVE_BACKLOG: + hse->state = st_save_backlog(hse); + break; + case HSES_FLUSH_BITS: + hse->state = st_flush_bit_buffer(hse, &oi); + case HSES_DONE: + return HSER_POLL_EMPTY; + default: + LOG("-- bad state %s\n", state_names[hse->state]); + return HSER_POLL_ERROR_MISUSE; + } + + if (hse->state == in_state) { + /* Check if output buffer is exhausted. */ + if (*output_size == out_buf_size) return HSER_POLL_MORE; + } + } +} + +HSE_finish_res heatshrink_encoder_finish(heatshrink_encoder *hse) { + if (hse == NULL) { return HSER_FINISH_ERROR_NULL; } + LOG("-- setting is_finishing flag\n"); + hse->flags |= FLAG_IS_FINISHING; + if (hse->state == HSES_NOT_FULL) { hse->state = HSES_FILLED; } + return hse->state == HSES_DONE ? HSER_FINISH_DONE : HSER_FINISH_MORE; +} + +static HSE_state st_step_search(heatshrink_encoder *hse) { + uint16_t window_length = get_input_buffer_size(hse); + uint16_t lookahead_sz = get_lookahead_size(hse); + uint16_t msi = hse->match_scan_index; + LOG("## step_search, scan @ +%d (%d/%d), input size %d\n", + msi, hse->input_size + msi, 2*window_length, hse->input_size); + + bool fin = is_finishing(hse); + if (msi > hse->input_size - (fin ? 1 : lookahead_sz)) { + /* Current search buffer is exhausted, copy it into the + * backlog and await more input. */ + LOG("-- end of search @ %d\n", msi); + return fin ? HSES_FLUSH_BITS : HSES_SAVE_BACKLOG; + } + + uint16_t input_offset = get_input_offset(hse); + uint16_t end = input_offset + msi; + uint16_t start = end - window_length; + + uint16_t max_possible = lookahead_sz; + if (hse->input_size - msi < lookahead_sz) { + max_possible = hse->input_size - msi; + } + + uint16_t match_length = 0; + uint16_t match_pos = find_longest_match(hse, + start, end, max_possible, &match_length); + + if (match_pos == MATCH_NOT_FOUND) { + LOG("ss Match not found\n"); + hse->match_scan_index++; + hse->match_length = 0; + return HSES_YIELD_TAG_BIT; + } else { + LOG("ss Found match of %d bytes at %d\n", match_length, match_pos); + hse->match_pos = match_pos; + hse->match_length = match_length; + ASSERT(match_pos <= 1 << HEATSHRINK_ENCODER_WINDOW_BITS(hse) /*window_length*/); + + return HSES_YIELD_TAG_BIT; + } +} + +static HSE_state st_yield_tag_bit(heatshrink_encoder *hse, + output_info *oi) { + if (can_take_byte(oi)) { + if (hse->match_length == 0) { + add_tag_bit(hse, oi, HEATSHRINK_LITERAL_MARKER); + return HSES_YIELD_LITERAL; + } else { + add_tag_bit(hse, oi, HEATSHRINK_BACKREF_MARKER); + hse->outgoing_bits = hse->match_pos - 1; + hse->outgoing_bits_count = HEATSHRINK_ENCODER_WINDOW_BITS(hse); + return HSES_YIELD_BR_INDEX; + } + } else { + return HSES_YIELD_TAG_BIT; /* output is full, continue */ + } +} + +static HSE_state st_yield_literal(heatshrink_encoder *hse, + output_info *oi) { + if (can_take_byte(oi)) { + push_literal_byte(hse, oi); + return HSES_SEARCH; + } else { + return HSES_YIELD_LITERAL; + } +} + +static HSE_state st_yield_br_index(heatshrink_encoder *hse, + output_info *oi) { + if (can_take_byte(oi)) { + LOG("-- yielding backref index %u\n", hse->match_pos); + if (push_outgoing_bits(hse, oi) > 0) { + return HSES_YIELD_BR_INDEX; /* continue */ + } else { + hse->outgoing_bits = hse->match_length - 1; + hse->outgoing_bits_count = HEATSHRINK_ENCODER_LOOKAHEAD_BITS(hse); + return HSES_YIELD_BR_LENGTH; /* done */ + } + } else { + return HSES_YIELD_BR_INDEX; /* continue */ + } +} + +static HSE_state st_yield_br_length(heatshrink_encoder *hse, + output_info *oi) { + if (can_take_byte(oi)) { + LOG("-- yielding backref length %u\n", hse->match_length); + if (push_outgoing_bits(hse, oi) > 0) { + return HSES_YIELD_BR_LENGTH; + } else { + hse->match_scan_index += hse->match_length; + hse->match_length = 0; + return HSES_SEARCH; + } + } else { + return HSES_YIELD_BR_LENGTH; + } +} + +static HSE_state st_save_backlog(heatshrink_encoder *hse) { + LOG("-- saving backlog\n"); + save_backlog(hse); + return HSES_NOT_FULL; +} + +static HSE_state st_flush_bit_buffer(heatshrink_encoder *hse, + output_info *oi) { + if (hse->bit_index == 0x80) { + LOG("-- done!\n"); + return HSES_DONE; + } else if (can_take_byte(oi)) { + LOG("-- flushing remaining byte (bit_index == 0x%02x)\n", hse->bit_index); + oi->buf[(*oi->output_size)++] = hse->current_byte; + LOG("-- done!\n"); + return HSES_DONE; + } else { + return HSES_FLUSH_BITS; + } +} + +static void add_tag_bit(heatshrink_encoder *hse, output_info *oi, uint8_t tag) { + LOG("-- adding tag bit: %d\n", tag); + push_bits(hse, 1, tag, oi); +} + +static uint16_t get_input_offset(heatshrink_encoder *hse) { + return get_input_buffer_size(hse); +} + +static uint16_t get_input_buffer_size(heatshrink_encoder *hse) { + return (1 << HEATSHRINK_ENCODER_WINDOW_BITS(hse)); + (void)hse; +} + +static uint16_t get_lookahead_size(heatshrink_encoder *hse) { + return (1 << HEATSHRINK_ENCODER_LOOKAHEAD_BITS(hse)); + (void)hse; +} + +static void do_indexing(heatshrink_encoder *hse) { +#if HEATSHRINK_USE_INDEX + /* Build an index array I that contains flattened linked lists + * for the previous instances of every byte in the buffer. + * + * For example, if buf[200] == 'x', then index[200] will either + * be an offset i such that buf[i] == 'x', or a negative offset + * to indicate end-of-list. This significantly speeds up matching, + * while only using sizeof(uint16_t)*sizeof(buffer) bytes of RAM. + * + * Future optimization options: + * 1. Since any negative value represents end-of-list, the other + * 15 bits could be used to improve the index dynamically. + * + * 2. Likewise, the last lookahead_sz bytes of the index will + * not be usable, so temporary data could be stored there to + * dynamically improve the index. + * */ + struct hs_index *hsi = HEATSHRINK_ENCODER_INDEX(hse); + int16_t last[256]; + memset(last, 0xFF, sizeof(last)); + + uint8_t * const data = hse->buffer; + int16_t * const index = hsi->index; + + const uint16_t input_offset = get_input_offset(hse); + const uint16_t end = input_offset + hse->input_size; + + for (uint16_t i=0; iflags & FLAG_IS_FINISHING; +} + +static int can_take_byte(output_info *oi) { + return *oi->output_size < oi->buf_size; +} + +/* Return the longest match for the bytes at buf[end:end+maxlen] between + * buf[start] and buf[end-1]. If no match is found, return -1. */ +static uint16_t find_longest_match(heatshrink_encoder *hse, uint16_t start, + uint16_t end, const uint16_t maxlen, uint16_t *match_length) { + LOG("-- scanning for match of buf[%u:%u] between buf[%u:%u] (max %u bytes)\n", + end, end + maxlen, start, end + maxlen - 1, maxlen); + uint8_t *buf = hse->buffer; + + uint16_t match_maxlen = 0; + uint16_t match_index = MATCH_NOT_FOUND; + + uint16_t len = 0; + uint8_t * const needlepoint = &buf[end]; +#if HEATSHRINK_USE_INDEX + struct hs_index *hsi = HEATSHRINK_ENCODER_INDEX(hse); + int16_t pos = hsi->index[end]; + + while (pos - (int16_t)start >= 0) { + uint8_t * const pospoint = &buf[pos]; + len = 0; + + /* Only check matches that will potentially beat the current maxlen. + * This is redundant with the index if match_maxlen is 0, but the + * added branch overhead to check if it == 0 seems to be worse. */ + if (pospoint[match_maxlen] != needlepoint[match_maxlen]) { + pos = hsi->index[pos]; + continue; + } + + for (len = 1; len < maxlen; len++) { + if (pospoint[len] != needlepoint[len]) break; + } + + if (len > match_maxlen) { + match_maxlen = len; + match_index = pos; + if (len == maxlen) { break; } /* won't find better */ + } + pos = hsi->index[pos]; + } +#else + for (int16_t pos=end - 1; pos - (int16_t)start >= 0; pos--) { + uint8_t * const pospoint = &buf[pos]; + if ((pospoint[match_maxlen] == needlepoint[match_maxlen]) + && (*pospoint == *needlepoint)) { + for (len=1; len cmp buf[%d] == 0x%02x against %02x (start %u)\n", + pos + len, pospoint[len], needlepoint[len], start); + } + if (pospoint[len] != needlepoint[len]) { break; } + } + if (len > match_maxlen) { + match_maxlen = len; + match_index = pos; + if (len == maxlen) { break; } /* don't keep searching */ + } + } + } +#endif + + const size_t break_even_point = + (1 + HEATSHRINK_ENCODER_WINDOW_BITS(hse) + + HEATSHRINK_ENCODER_LOOKAHEAD_BITS(hse)); + + /* Instead of comparing break_even_point against 8*match_maxlen, + * compare match_maxlen against break_even_point/8 to avoid + * overflow. Since MIN_WINDOW_BITS and MIN_LOOKAHEAD_BITS are 4 and + * 3, respectively, break_even_point/8 will always be at least 1. */ + if (match_maxlen > (break_even_point / 8)) { + LOG("-- best match: %u bytes at -%u\n", + match_maxlen, end - match_index); + *match_length = match_maxlen; + return end - match_index; + } + LOG("-- none found\n"); + return MATCH_NOT_FOUND; +} + +static uint8_t push_outgoing_bits(heatshrink_encoder *hse, output_info *oi) { + uint8_t count = 0; + uint8_t bits = 0; + if (hse->outgoing_bits_count > 8) { + count = 8; + bits = hse->outgoing_bits >> (hse->outgoing_bits_count - 8); + } else { + count = hse->outgoing_bits_count; + bits = hse->outgoing_bits; + } + + if (count > 0) { + LOG("-- pushing %d outgoing bits: 0x%02x\n", count, bits); + push_bits(hse, count, bits, oi); + hse->outgoing_bits_count -= count; + } + return count; +} + +/* Push COUNT (max 8) bits to the output buffer, which has room. + * Bytes are set from the lowest bits, up. */ +static void push_bits(heatshrink_encoder *hse, uint8_t count, uint8_t bits, + output_info *oi) { + ASSERT(count <= 8); + LOG("++ push_bits: %d bits, input of 0x%02x\n", count, bits); + + /* If adding a whole byte and at the start of a new output byte, + * just push it through whole and skip the bit IO loop. */ + if (count == 8 && hse->bit_index == 0x80) { + oi->buf[(*oi->output_size)++] = bits; + } else { + for (int i=count - 1; i>=0; i--) { + bool bit = bits & (1 << i); + if (bit) { hse->current_byte |= hse->bit_index; } + if (0) { + LOG(" -- setting bit %d at bit index 0x%02x, byte => 0x%02x\n", + bit ? 1 : 0, hse->bit_index, hse->current_byte); + } + hse->bit_index >>= 1; + if (hse->bit_index == 0x00) { + hse->bit_index = 0x80; + LOG(" > pushing byte 0x%02x\n", hse->current_byte); + oi->buf[(*oi->output_size)++] = hse->current_byte; + hse->current_byte = 0x00; + } + } + } +} + +static void push_literal_byte(heatshrink_encoder *hse, output_info *oi) { + uint16_t processed_offset = hse->match_scan_index - 1; + uint16_t input_offset = get_input_offset(hse) + processed_offset; + uint8_t c = hse->buffer[input_offset]; + LOG("-- yielded literal byte 0x%02x ('%c') from +%d\n", + c, isprint(c) ? c : '.', input_offset); + push_bits(hse, 8, c, oi); +} + +static void save_backlog(heatshrink_encoder *hse) { + size_t input_buf_sz = get_input_buffer_size(hse); + + uint16_t msi = hse->match_scan_index; + + /* Copy processed data to beginning of buffer, so it can be + * used for future matches. Don't bother checking whether the + * input is less than the maximum size, because if it isn't, + * we're done anyway. */ + uint16_t rem = input_buf_sz - msi; // unprocessed bytes + uint16_t shift_sz = input_buf_sz + rem; + + memmove(&hse->buffer[0], + &hse->buffer[input_buf_sz - rem], + shift_sz); + + hse->match_scan_index = 0; + hse->input_size -= input_buf_sz - rem; +} diff --git a/src/heatshrink/heatshrink_encoder.h b/src/heatshrink/heatshrink_encoder.h new file mode 100644 index 0000000000..18c17731b1 --- /dev/null +++ b/src/heatshrink/heatshrink_encoder.h @@ -0,0 +1,109 @@ +#ifndef HEATSHRINK_ENCODER_H +#define HEATSHRINK_ENCODER_H + +#include +#include +#include "heatshrink_common.h" +#include "heatshrink_config.h" + +typedef enum { + HSER_SINK_OK, /* data sunk into input buffer */ + HSER_SINK_ERROR_NULL=-1, /* NULL argument */ + HSER_SINK_ERROR_MISUSE=-2, /* API misuse */ +} HSE_sink_res; + +typedef enum { + HSER_POLL_EMPTY, /* input exhausted */ + HSER_POLL_MORE, /* poll again for more output */ + HSER_POLL_ERROR_NULL=-1, /* NULL argument */ + HSER_POLL_ERROR_MISUSE=-2, /* API misuse */ +} HSE_poll_res; + +typedef enum { + HSER_FINISH_DONE, /* encoding is complete */ + HSER_FINISH_MORE, /* more output remaining; use poll */ + HSER_FINISH_ERROR_NULL=-1, /* NULL argument */ +} HSE_finish_res; + +#if HEATSHRINK_DYNAMIC_ALLOC +#define HEATSHRINK_ENCODER_WINDOW_BITS(HSE) \ + ((HSE)->window_sz2) +#define HEATSHRINK_ENCODER_LOOKAHEAD_BITS(HSE) \ + ((HSE)->lookahead_sz2) +#define HEATSHRINK_ENCODER_INDEX(HSE) \ + ((HSE)->search_index) +struct hs_index { + uint16_t size; + int16_t index[]; +}; +#else +#define HEATSHRINK_ENCODER_WINDOW_BITS(_) \ + (HEATSHRINK_STATIC_WINDOW_BITS) +#define HEATSHRINK_ENCODER_LOOKAHEAD_BITS(_) \ + (HEATSHRINK_STATIC_LOOKAHEAD_BITS) +#define HEATSHRINK_ENCODER_INDEX(HSE) \ + (&(HSE)->search_index) +struct hs_index { + uint16_t size; + int16_t index[2 << HEATSHRINK_STATIC_WINDOW_BITS]; +}; +#endif + +typedef struct { + uint16_t input_size; /* bytes in input buffer */ + uint16_t match_scan_index; + uint16_t match_length; + uint16_t match_pos; + uint16_t outgoing_bits; /* enqueued outgoing bits */ + uint8_t outgoing_bits_count; + uint8_t flags; + uint8_t state; /* current state machine node */ + uint8_t current_byte; /* current byte of output */ + uint8_t bit_index; /* current bit index */ +#if HEATSHRINK_DYNAMIC_ALLOC + uint8_t window_sz2; /* 2^n size of window */ + uint8_t lookahead_sz2; /* 2^n size of lookahead */ +#if HEATSHRINK_USE_INDEX + struct hs_index *search_index; +#endif + /* input buffer and / sliding window for expansion */ + uint8_t buffer[]; +#else + #if HEATSHRINK_USE_INDEX + struct hs_index search_index; + #endif + /* input buffer and / sliding window for expansion */ + uint8_t buffer[2 << HEATSHRINK_ENCODER_WINDOW_BITS(_)]; +#endif +} heatshrink_encoder; + +#if HEATSHRINK_DYNAMIC_ALLOC +/* Allocate a new encoder struct and its buffers. + * Returns NULL on error. */ +heatshrink_encoder *heatshrink_encoder_alloc(uint8_t window_sz2, + uint8_t lookahead_sz2); + +/* Free an encoder. */ +void heatshrink_encoder_free(heatshrink_encoder *hse); +#endif + +/* Reset an encoder. */ +void heatshrink_encoder_reset(heatshrink_encoder *hse); + +/* Sink up to SIZE bytes from IN_BUF into the encoder. + * INPUT_SIZE is set to the number of bytes actually sunk (in case a + * buffer was filled.). */ +HSE_sink_res heatshrink_encoder_sink(heatshrink_encoder *hse, + uint8_t *in_buf, size_t size, size_t *input_size); + +/* Poll for output from the encoder, copying at most OUT_BUF_SIZE bytes into + * OUT_BUF (setting *OUTPUT_SIZE to the actual amount copied). */ +HSE_poll_res heatshrink_encoder_poll(heatshrink_encoder *hse, + uint8_t *out_buf, size_t out_buf_size, size_t *output_size); + +/* Notify the encoder that the input stream is finished. + * If the return value is HSER_FINISH_MORE, there is still more output, so + * call heatshrink_encoder_poll and repeat. */ +HSE_finish_res heatshrink_encoder_finish(heatshrink_encoder *hse); + +#endif diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 2c4a22fc5d..9eebd36e8d 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -510,6 +510,7 @@ target_link_libraries(libslic3r ZLIB::ZLIB JPEG::JPEG qoi + heatshrink ) if (APPLE) diff --git a/src/libslic3r/GCode/GCodeBinarizer.cpp b/src/libslic3r/GCode/GCodeBinarizer.cpp index 350905ecda..8afda44169 100644 --- a/src/libslic3r/GCode/GCodeBinarizer.cpp +++ b/src/libslic3r/GCode/GCodeBinarizer.cpp @@ -6,6 +6,11 @@ #include #endif // ENABLE_BINARIZED_GCODE_DEBUG +extern "C" { +#include +#include +} + #include #include @@ -111,10 +116,10 @@ public: } return line; }; - auto is_packable = [this](char c) { + auto is_packable = [](char c) { return (s_lookup_tables.packable[static_cast(c)] != 0); }; - auto pack_chars = [this](char low, char high) { + auto pack_chars = [](char low, char high) { return (((s_lookup_tables.value[static_cast(high)] & 0xF) << 4) | (s_lookup_tables.value[static_cast(low)] & 0xF)); }; @@ -471,7 +476,7 @@ void set_checksum_max_cache_size(size_t size) { g_checksum_max_cache_size = size static uint16_t checksum_types_count() { return 1 + (uint16_t)EChecksumType::CRC32; } static uint16_t block_types_count() { return 1 + (uint16_t)EBlockType::Thumbnail; } -static uint16_t compression_types_count() { return 1 + (uint16_t)ECompressionType::None; } +static uint16_t compression_types_count() { return 1 + (uint16_t)ECompressionType::Heatshrink_12_4; } static uint16_t thumbnail_formats_count() { return 1 + (uint16_t)EThumbnailFormat::QOI; } static uint16_t metadata_encoding_types_count() { return 1 + (uint16_t)EMetadataEncodingType::INI; } static uint16_t gcode_encoding_types_count() { return 1 + (uint16_t)EGCodeEncodingType::MeatPackComments; } @@ -586,13 +591,141 @@ static bool decode_gcode(const std::vector& src, std::string& dst, EGCo return true; } -static bool compress(const std::vector& src, std::vector& data_out, ECompressionType compression_type) +static bool compress(const std::vector& src, std::vector& dst, ECompressionType compression_type) { + switch (compression_type) + { + case ECompressionType::Heatshrink_11_4: + case ECompressionType::Heatshrink_12_4: + { + const uint8_t window_sz = (compression_type == ECompressionType::Heatshrink_11_4) ? 11 : 12; + const uint8_t lookahead_sz = 4; + heatshrink_encoder* encoder = heatshrink_encoder_alloc(window_sz, lookahead_sz); + if (encoder == nullptr) + return false; + + // calculate the maximum compressed size (assuming a conservative estimate) + const size_t src_size = src.size(); + const size_t max_compressed_size = src_size + (src_size >> 2); + dst.resize(max_compressed_size); + + uint8_t* buf = const_cast(src.data()); + uint8_t* outbuf = dst.data(); + + // compress data + size_t tosink = src_size; + size_t output_size = 0; + while (tosink > 0) { + size_t sunk = 0; + const HSE_sink_res sink_res = heatshrink_encoder_sink(encoder, buf, tosink, &sunk); + if (sink_res != HSER_SINK_OK) { + heatshrink_encoder_free(encoder); + return false; + } + if (sunk == 0) + // all input data processed + break; + + tosink -= sunk; + buf += sunk; + + size_t polled = 0; + const HSE_poll_res poll_res = heatshrink_encoder_poll(encoder, outbuf + output_size, max_compressed_size - output_size, &polled); + if (poll_res < 0) { + heatshrink_encoder_free(encoder); + return false; + } + output_size += polled; + } + + // input data finished + const HSE_finish_res finish_res = heatshrink_encoder_finish(encoder); + if (finish_res < 0) { + heatshrink_encoder_free(encoder); + return false; + } + + // poll for final output + size_t polled = 0; + const HSE_poll_res poll_res = heatshrink_encoder_poll(encoder, outbuf + output_size, max_compressed_size - output_size, &polled); + if (poll_res < 0) { + heatshrink_encoder_free(encoder); + return false; + } + dst.resize(output_size + polled); + heatshrink_encoder_free(encoder); + break; + } + case ECompressionType::None: + default: + { + break; + } + } + return true; } -static bool uncompress(const std::vector& src, std::vector& data_out, ECompressionType compression_type) +static bool uncompress(const std::vector& src, std::vector& dst, ECompressionType compression_type, size_t uncompressed_size) { + switch (compression_type) + { + case ECompressionType::Heatshrink_11_4: + case ECompressionType::Heatshrink_12_4: + { + const uint8_t window_sz = (compression_type == ECompressionType::Heatshrink_11_4) ? 11 : 12; + const uint8_t lookahead_sz = 4; + const uint16_t input_buffer_size = 2048; + heatshrink_decoder* decoder = heatshrink_decoder_alloc(input_buffer_size, window_sz, lookahead_sz); + if (decoder == nullptr) + return false; + + dst.resize(uncompressed_size); + + uint8_t* buf = const_cast(src.data()); + uint8_t* outbuf = dst.data(); + + uint32_t sunk = 0; + uint32_t polled = 0; + + const size_t compressed_size = src.size(); + while (sunk < compressed_size) { + size_t count = 0; + const HSD_sink_res sink_res = heatshrink_decoder_sink(decoder, &buf[sunk], compressed_size - sunk, &count); + if (sink_res < 0) { + heatshrink_decoder_free(decoder); + return false; + } + + sunk += (uint32_t)count; + + HSD_poll_res poll_res; + do { + poll_res = heatshrink_decoder_poll(decoder, &outbuf[polled], uncompressed_size - polled, &count); + if (poll_res < 0) { + heatshrink_decoder_free(decoder); + return false; + } + polled += (uint32_t)count; + } while (polled < uncompressed_size && poll_res == HSDR_POLL_MORE); + } + + const HSD_finish_res finish_res = heatshrink_decoder_finish(decoder); + if (finish_res < 0) { + heatshrink_decoder_free(decoder); + return false; + } + + heatshrink_decoder_free(decoder); + break; + } + case ECompressionType::None: + default: + { + break; + } + } + return true; } @@ -847,7 +980,7 @@ EResult BaseMetadataBlock::read_data(FILE& file, const BlockHeader& block_header std::vector uncompressed_data; if (compression_type != ECompressionType::None) { - if (!uncompress(data, uncompressed_data, compression_type)) + if (!uncompress(data, uncompressed_data, compression_type, block_header.uncompressed_size)) return EResult::DataUncompressionError; } @@ -956,6 +1089,7 @@ EResult ThumbnailBlock::read_data(FILE& file, const FileHeader& file_header, con return EResult::InvalidThumbnailHeight; if (block_header.uncompressed_size == 0) return EResult::InvalidThumbnailDataSize; + data.resize(block_header.uncompressed_size); if (!read_from_file(file, (void*)data.data(), block_header.uncompressed_size)) return EResult::ReadError; @@ -1173,7 +1307,7 @@ EResult GCodeBlock::read_data(FILE& file, const FileHeader& file_header, const B std::vector uncompressed_data; if (compression_type != ECompressionType::None) { - if (!uncompress(data, uncompressed_data, compression_type)) + if (!uncompress(data, uncompressed_data, compression_type, block_header.uncompressed_size)) return EResult::DataUncompressionError; } @@ -1246,12 +1380,12 @@ EResult Binarizer::initialize(FILE& file, const BinarizerConfig& config) return res; // save file metadata block - res = m_binary_data.file_metadata.write(*m_file, m_config.compression, m_config.checksum); + res = m_binary_data.file_metadata.write(*m_file, m_config.compression.file_metadata, m_config.checksum); if (res != EResult::Success) return res; // save printer metadata block - res = m_binary_data.printer_metadata.write(*m_file, m_config.compression, m_config.checksum); + res = m_binary_data.printer_metadata.write(*m_file, m_config.compression.printer_metadata, m_config.checksum); if (res != EResult::Success) return res; @@ -1263,12 +1397,12 @@ EResult Binarizer::initialize(FILE& file, const BinarizerConfig& config) } // save print metadata block - res = m_binary_data.print_metadata.write(*m_file, m_config.compression, m_config.checksum); + res = m_binary_data.print_metadata.write(*m_file, m_config.compression.print_metadata, m_config.checksum); if (res != EResult::Success) return res; // save slicer metadata block - res = m_binary_data.slicer_metadata.write(*m_file, m_config.compression, m_config.checksum); + res = m_binary_data.slicer_metadata.write(*m_file, m_config.compression.slicer_metadata, m_config.checksum); if (res != EResult::Success) return res; @@ -1280,7 +1414,7 @@ static EResult write_gcode_block(FILE& file, const std::string& raw_data, const GCodeBlock block; block.encoding_type = (uint16_t)config.gcode_encoding; block.raw_data = raw_data; - return block.write(file, config.compression, config.checksum); + return block.write(file, config.compression.gcode, config.checksum); } EResult Binarizer::append_gcode(const std::string& gcode) diff --git a/src/libslic3r/GCode/GCodeBinarizer.hpp b/src/libslic3r/GCode/GCodeBinarizer.hpp index 595f5da806..6e95d6da89 100644 --- a/src/libslic3r/GCode/GCodeBinarizer.hpp +++ b/src/libslic3r/GCode/GCodeBinarizer.hpp @@ -105,6 +105,8 @@ enum class EBlockType : uint16_t enum class ECompressionType : uint16_t { None, + Heatshrink_11_4, + Heatshrink_12_4, }; struct BlockHeader @@ -255,7 +257,16 @@ struct BinaryData struct BinarizerConfig { - ECompressionType compression{ ECompressionType::None }; + struct Compression + { + ECompressionType file_metadata{ ECompressionType::None }; + ECompressionType printer_metadata{ ECompressionType::None }; + ECompressionType thumbnail{ ECompressionType::None }; + ECompressionType print_metadata{ ECompressionType::None }; + ECompressionType slicer_metadata{ ECompressionType::None }; + ECompressionType gcode{ ECompressionType::None }; + }; + Compression compression; EGCodeEncodingType gcode_encoding{ EGCodeEncodingType::None }; EMetadataEncodingType metadata_encoding{ EMetadataEncodingType::INI }; EChecksumType checksum{ EChecksumType::CRC32 }; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 1ee779e421..5a2aa6eb93 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -7880,41 +7880,84 @@ void GLCanvas3D::show_binary_gcode_debug_window() imgui.begin(std::string("Binary GCode"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); if (ImGui::BeginTable("BinaryGCodeConfig", 2)) { - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Compression"); - ImGui::TableSetColumnIndex(1); - const std::vector gcode_compressions = { "None" }; - int compressions_id = (int)binarizer_config.compression; - if (imgui.combo(std::string("##1"), gcode_compressions, compressions_id, ImGuiComboFlags_HeightLargest, 0.0f, 200.0f)) - binarizer_config.compression = (BinaryGCode::ECompressionType)compressions_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); - imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "GGcode encoding"); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "File metadata compression"); ImGui::TableSetColumnIndex(1); - const std::vector gcode_encodings = { "None", "MeatPack", "MeatPack Comments"}; - int gcode_encodings_id = (int)binarizer_config.gcode_encoding; - if (imgui.combo(std::string("##2"), gcode_encodings, gcode_encodings_id, ImGuiComboFlags_HeightLargest, 0.0f, 200.0f)) - binarizer_config.gcode_encoding = (BinaryGCode::EGCodeEncodingType)gcode_encodings_id; + std::vector options = { "None", "heatshrink 11,4", "heatshrink 12,4" }; + int option_id = (int)binarizer_config.compression.file_metadata; + if (imgui.combo(std::string("##file_metadata_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) + binarizer_config.compression.file_metadata = (BinaryGCode::ECompressionType)option_id; + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Printer metadata compression"); + ImGui::TableSetColumnIndex(1); + option_id = (int)binarizer_config.compression.printer_metadata; + if (imgui.combo(std::string("##printer_metadata_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) + binarizer_config.compression.printer_metadata = (BinaryGCode::ECompressionType)option_id; + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Thumbnail compression"); + ImGui::TableSetColumnIndex(1); + options = { "None" }; + option_id = (int)binarizer_config.compression.thumbnail; + if (imgui.combo(std::string("##thumbnail_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) + binarizer_config.compression.thumbnail = (BinaryGCode::ECompressionType)option_id; + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Print metadata compression"); + ImGui::TableSetColumnIndex(1); + options = { "None", "heatshrink 11,4", "heatshrink 12,4" }; + option_id = (int)binarizer_config.compression.print_metadata; + if (imgui.combo(std::string("##print_metadata_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) + binarizer_config.compression.print_metadata = (BinaryGCode::ECompressionType)option_id; + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Slicer metadata compression"); + ImGui::TableSetColumnIndex(1); + option_id = (int)binarizer_config.compression.slicer_metadata; + if (imgui.combo(std::string("##slicer_metadata_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) + binarizer_config.compression.slicer_metadata = (BinaryGCode::ECompressionType)option_id; + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "GCode compression"); + ImGui::TableSetColumnIndex(1); + option_id = (int)binarizer_config.compression.gcode; + if (imgui.combo(std::string("##gcode_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) + binarizer_config.compression.gcode = (BinaryGCode::ECompressionType)option_id; + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "GCode encoding"); + ImGui::TableSetColumnIndex(1); + options = { "None", "MeatPack", "MeatPack Comments" }; + option_id = (int)binarizer_config.gcode_encoding; + if (imgui.combo(std::string("##gcode_encoding"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) + binarizer_config.gcode_encoding = (BinaryGCode::EGCodeEncodingType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Metadata encoding"); ImGui::TableSetColumnIndex(1); - const std::vector metadata_encodings = { "INI" }; - int metadata_encodings_id = (int)binarizer_config.metadata_encoding; - if (imgui.combo(std::string("##3"), metadata_encodings, metadata_encodings_id, ImGuiComboFlags_HeightLargest, 0.0f, 200.0f)) - binarizer_config.metadata_encoding = (BinaryGCode::EMetadataEncodingType)metadata_encodings_id; + options = { "INI" }; + option_id = (int)binarizer_config.metadata_encoding; + if (imgui.combo(std::string("##metadata_encoding"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) + binarizer_config.metadata_encoding = (BinaryGCode::EMetadataEncodingType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Checksum type"); ImGui::TableSetColumnIndex(1); - const std::vector checksums = { "None", "CRC32" }; - int checksums_id = (int)binarizer_config.checksum; - if (imgui.combo(std::string("##4"), checksums, checksums_id, ImGuiComboFlags_HeightLargest, 0.0f, 200.0f)) - binarizer_config.checksum = (BinaryGCode::EChecksumType)checksums_id; + options = { "None", "CRC32" }; + option_id = (int)binarizer_config.checksum; + if (imgui.combo(std::string("##4"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) + binarizer_config.checksum = (BinaryGCode::EChecksumType)option_id; ImGui::EndTable(); From 004a64a651c66d0b360e15da2f68822e05f434e7 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 25 Jul 2023 14:07:46 +0200 Subject: [PATCH 026/136] Removed code for thumbnails compression --- src/libslic3r/GCode/GCodeBinarizer.hpp | 2 +- src/slic3r/GUI/GLCanvas3D.cpp | 10 ---------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/src/libslic3r/GCode/GCodeBinarizer.hpp b/src/libslic3r/GCode/GCodeBinarizer.hpp index 6e95d6da89..ab03f32563 100644 --- a/src/libslic3r/GCode/GCodeBinarizer.hpp +++ b/src/libslic3r/GCode/GCodeBinarizer.hpp @@ -261,7 +261,6 @@ struct BinarizerConfig { ECompressionType file_metadata{ ECompressionType::None }; ECompressionType printer_metadata{ ECompressionType::None }; - ECompressionType thumbnail{ ECompressionType::None }; ECompressionType print_metadata{ ECompressionType::None }; ECompressionType slicer_metadata{ ECompressionType::None }; ECompressionType gcode{ ECompressionType::None }; @@ -299,6 +298,7 @@ private: //===================================================================================================================================== // +// GCODEVIEWER INTERFACE // FIRMWARE INTERFACE // //===================================================================================================================================== diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 5a2aa6eb93..d075aeda3e 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -7898,20 +7898,10 @@ void GLCanvas3D::show_binary_gcode_debug_window() if (imgui.combo(std::string("##printer_metadata_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) binarizer_config.compression.printer_metadata = (BinaryGCode::ECompressionType)option_id; - ImGui::TableNextRow(); - ImGui::TableSetColumnIndex(0); - imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Thumbnail compression"); - ImGui::TableSetColumnIndex(1); - options = { "None" }; - option_id = (int)binarizer_config.compression.thumbnail; - if (imgui.combo(std::string("##thumbnail_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.compression.thumbnail = (BinaryGCode::ECompressionType)option_id; - ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "Print metadata compression"); ImGui::TableSetColumnIndex(1); - options = { "None", "heatshrink 11,4", "heatshrink 12,4" }; option_id = (int)binarizer_config.compression.print_metadata; if (imgui.combo(std::string("##print_metadata_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) binarizer_config.compression.print_metadata = (BinaryGCode::ECompressionType)option_id; From fba2406ace6b65f80ce84e34bd342a0d996bf8f7 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 26 Jul 2023 11:14:21 +0200 Subject: [PATCH 027/136] SPE-1784: New compressed (binary) gcode format integration Added conversion of gcodes from binary to ascii format --- resources/icons/convert_file.svg | 93 ++++++++++ src/libslic3r/GCode/GCodeBinarizer.cpp | 247 ++++++++++++++++++++++--- src/libslic3r/GCode/GCodeBinarizer.hpp | 19 +- src/slic3r/GUI/MainFrame.cpp | 22 ++- src/slic3r/GUI/Plater.cpp | 71 +++++++ src/slic3r/GUI/Plater.hpp | 4 + 6 files changed, 431 insertions(+), 25 deletions(-) create mode 100644 resources/icons/convert_file.svg diff --git a/resources/icons/convert_file.svg b/resources/icons/convert_file.svg new file mode 100644 index 0000000000..2de2b707f0 --- /dev/null +++ b/resources/icons/convert_file.svg @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/libslic3r/GCode/GCodeBinarizer.cpp b/src/libslic3r/GCode/GCodeBinarizer.cpp index 8afda44169..cd46324174 100644 --- a/src/libslic3r/GCode/GCodeBinarizer.cpp +++ b/src/libslic3r/GCode/GCodeBinarizer.cpp @@ -6,6 +6,10 @@ #include #endif // ENABLE_BINARIZED_GCODE_DEBUG +#if ENABLE_FILE_CONVERSION_INTERFACE +#include +#endif // ENABLE_FILE_CONVERSION_INTERFACE + extern "C" { #include #include @@ -441,32 +445,34 @@ static void unbinarize(const std::vector& src, std::string& dst) { } } // namespace MeatPack -std::string translate_result(BinaryGCode::EResult result) +std::string translate_result(EResult result) { switch (result) { - case BinaryGCode::EResult::Success: { return "Success"; } - case BinaryGCode::EResult::ReadError: { return "Read error"; } - case BinaryGCode::EResult::WriteError: { return "Write error"; } - case BinaryGCode::EResult::InvalidMagicNumber: { return "Invalid magic number"; } - case BinaryGCode::EResult::InvalidVersionNumber: { return "Invalid version number"; } - case BinaryGCode::EResult::InvalidChecksumType: { return "Invalid checksum type"; } - case BinaryGCode::EResult::InvalidBlockType: { return "Invalid block type"; } - case BinaryGCode::EResult::InvalidCompressionType: { return "Invalid compression type"; } - case BinaryGCode::EResult::InvalidMetadataEncodingType: { return "Invalid metadata encoding type"; } - case BinaryGCode::EResult::InvalidGCodeEncodingType: { return "Invalid gcode encoding type"; } - case BinaryGCode::EResult::DataCompressionError: { return "Data compression error"; } - case BinaryGCode::EResult::DataUncompressionError: { return "Data uncompression error"; } - case BinaryGCode::EResult::MetadataEncodingError: { return "Data encoding error"; } - case BinaryGCode::EResult::MetadataDecodingError: { return "Data decoding error"; } - case BinaryGCode::EResult::GCodeEncodingError: { return "GCode encoding error"; } - case BinaryGCode::EResult::GCodeDecodingError: { return "GCode decoding error"; } - case BinaryGCode::EResult::BlockNotFound: { return "Block not found"; } - case BinaryGCode::EResult::InvalidChecksum: { return "Invalid checksum"; } - case BinaryGCode::EResult::InvalidThumbnailFormat: { return "Invalid thumbnail format"; } - case BinaryGCode::EResult::InvalidThumbnailWidth: { return "Invalid thumbnail width"; } - case BinaryGCode::EResult::InvalidThumbnailHeight: { return "Invalid thumbnail height"; } - case BinaryGCode::EResult::InvalidThumbnailDataSize: { return "Invalid thumbnail data size"; } + case EResult::Success: { return "Success"; } + case EResult::ReadError: { return "Read error"; } + case EResult::WriteError: { return "Write error"; } + case EResult::InvalidMagicNumber: { return "Invalid magic number"; } + case EResult::InvalidVersionNumber: { return "Invalid version number"; } + case EResult::InvalidChecksumType: { return "Invalid checksum type"; } + case EResult::InvalidBlockType: { return "Invalid block type"; } + case EResult::InvalidCompressionType: { return "Invalid compression type"; } + case EResult::InvalidMetadataEncodingType: { return "Invalid metadata encoding type"; } + case EResult::InvalidGCodeEncodingType: { return "Invalid gcode encoding type"; } + case EResult::DataCompressionError: { return "Data compression error"; } + case EResult::DataUncompressionError: { return "Data uncompression error"; } + case EResult::MetadataEncodingError: { return "Data encoding error"; } + case EResult::MetadataDecodingError: { return "Data decoding error"; } + case EResult::GCodeEncodingError: { return "GCode encoding error"; } + case EResult::GCodeDecodingError: { return "GCode decoding error"; } + case EResult::BlockNotFound: { return "Block not found"; } + case EResult::InvalidChecksum: { return "Invalid checksum"; } + case EResult::InvalidThumbnailFormat: { return "Invalid thumbnail format"; } + case EResult::InvalidThumbnailWidth: { return "Invalid thumbnail width"; } + case EResult::InvalidThumbnailHeight: { return "Invalid thumbnail height"; } + case EResult::InvalidThumbnailDataSize: { return "Invalid thumbnail data size"; } + case EResult::InvalidBinaryGCodeFile: { return "Invalid binary GCode file"; } + case EResult::InvalidSequenceOfBlocks: { return "Invalid sequence of blocks"; } } return std::string(); } @@ -1658,5 +1664,200 @@ extern size_t block_content_size(const FileHeader& file_header, const BlockHeade #endif // ENABLE_CHECKSUM_BLOCK } +#if ENABLE_FILE_CONVERSION_INTERFACE +EResult from_ascii_to_binary(FILE& src_file, FILE& dst_file) +{ + return EResult::WriteError; +} + +EResult from_binary_to_ascii(FILE& src_file, FILE& dst_file, bool verify_checksum) +{ + auto write_line = [&](const std::string& line) { + fwrite(line.data(), 1, line.length(), &dst_file); + return !ferror(&dst_file); + }; + + auto write_metadata = [&](const std::vector>& data) { + for (const auto& [key, value] : data) { + if (!write_line("; " + key + " = " + value + "\n")) + return false; + } + return !ferror(&dst_file); + }; + + if (!is_valid_binary_gcode(src_file)) + return EResult::InvalidBinaryGCodeFile; + + fseek(&src_file, 0, SEEK_END); + const long file_size = ftell(&src_file); + rewind(&src_file); + + // + // read file header + // + FileHeader file_header; + EResult res = read_header(src_file, file_header, nullptr); + if (res != EResult::Success) + // propagate error + return res; + + // + // convert file metadata block + // + BlockHeader block_header; + res = read_next_block_header(src_file, file_header, block_header, verify_checksum); + if (res != EResult::Success) + // propagate error + return res; + if ((EBlockType)block_header.type != EBlockType::FileMetadata) + return EResult::InvalidSequenceOfBlocks; + FileMetadataBlock file_metadata_block; + res = file_metadata_block.read_data(src_file, file_header, block_header); + if (res != EResult::Success) + // propagate error + return res; + auto producer_it = std::find_if(file_metadata_block.raw_data.begin(), file_metadata_block.raw_data.end(), + [](const std::pair& item) { return item.first == "Producer"; }); + const std::string producer_str = (producer_it != file_metadata_block.raw_data.end()) ? producer_it->second : "Unknown"; + if (!write_line("; generated by " + producer_str + "\n\n\n")) + return EResult::WriteError; + + // + // convert printer metadata block + // + res = read_next_block_header(src_file, file_header, block_header, verify_checksum); + if (res != EResult::Success) + // propagate error + return res; + if ((EBlockType)block_header.type != EBlockType::PrinterMetadata) + return EResult::InvalidSequenceOfBlocks; + PrinterMetadataBlock printer_metadata_block; + res = printer_metadata_block.read_data(src_file, file_header, block_header); + if (res != EResult::Success) + // propagate error + return res; + if (!write_metadata(printer_metadata_block.raw_data)) + return EResult::WriteError; + + // + // convert thumbnail blocks + // + long restore_position = ftell(&src_file); + res = read_next_block_header(src_file, file_header, block_header, verify_checksum); + if (res != EResult::Success) + // propagate error + return res; + while ((EBlockType)block_header.type == EBlockType::Thumbnail) { + ThumbnailBlock thumbnail_block; + res = thumbnail_block.read_data(src_file, file_header, block_header); + if (res != EResult::Success) + // propagate error + return res; + static constexpr const size_t max_row_length = 78; + std::string encoded; + encoded.resize(boost::beast::detail::base64::encoded_size(thumbnail_block.data.size())); + encoded.resize(boost::beast::detail::base64::encode((void*)encoded.data(), (const void*)thumbnail_block.data.data(), thumbnail_block.data.size())); + std::string format; + switch ((EThumbnailFormat)thumbnail_block.format) + { + default: + case EThumbnailFormat::PNG: { format = "thumbnail"; break; } + case EThumbnailFormat::JPG: { format = "thumbnail_JPG"; break; } + case EThumbnailFormat::QOI: { format = "thumbnail_QOI"; break; } + } + if (!write_line(";\n; " + format + " begin " + std::to_string(thumbnail_block.width) + "x" + std::to_string(thumbnail_block.height) + + " " + std::to_string(encoded.length()) + "\n")) + return EResult::WriteError; + while (encoded.size() > max_row_length) { + if (!write_line("; " + encoded.substr(0, max_row_length) + "\n")) + return EResult::WriteError; + encoded = encoded.substr(max_row_length); + } + if (encoded.size() > 0) { + if (!write_line("; " + encoded + "\n")) + return EResult::WriteError; + } + if (!write_line("; " + format + " end\n;\n\n")) + return EResult::WriteError; + + restore_position = ftell(&src_file); + res = read_next_block_header(src_file, file_header, block_header, verify_checksum); + if (res != EResult::Success) + // propagate error + return res; + } + + // + // convert gcode blocks + // + res = skip_block_content(src_file, file_header, block_header); + if (res != EResult::Success) + // propagate error + return res; + res = read_next_block_header(src_file, file_header, block_header, EBlockType::GCode, verify_checksum); + if (res != EResult::Success) + // propagate error + return res; + while ((EBlockType)block_header.type == EBlockType::GCode) { + GCodeBlock block; + res = block.read_data(src_file, file_header, block_header); + if (res != EResult::Success) + // propagate error + return res; + if (!write_line(block.raw_data)) + return EResult::WriteError; + if (ftell(&src_file) == file_size) + break; + res = read_next_block_header(src_file, file_header, block_header, verify_checksum); + if (res != EResult::Success) + // propagate error + return res; + } + + // + // convert print metadata block + // + fseek(&src_file, restore_position, SEEK_SET); + res = read_next_block_header(src_file, file_header, block_header, verify_checksum); + if (res != EResult::Success) + // propagate error + return res; + if ((EBlockType)block_header.type != EBlockType::PrintMetadata) + return EResult::InvalidSequenceOfBlocks; + PrintMetadataBlock print_metadata_block; + res = print_metadata_block.read_data(src_file, file_header, block_header); + if (res != EResult::Success) + // propagate error + return res; + if (!write_line("\n")) + return EResult::WriteError; + if (!write_metadata(print_metadata_block.raw_data)) + return EResult::WriteError; + + // + // convert slicer metadata block + // + res = read_next_block_header(src_file, file_header, block_header, verify_checksum); + if (res != EResult::Success) + // propagate error + return res; + if ((EBlockType)block_header.type != EBlockType::SlicerMetadata) + return EResult::InvalidSequenceOfBlocks; + SlicerMetadataBlock slicer_metadata_block; + res = slicer_metadata_block.read_data(src_file, file_header, block_header); + if (res != EResult::Success) + // propagate error + return res; + if (!write_line("\n; prusaslicer_config = begin\n")) + return EResult::WriteError; + if (!write_metadata(slicer_metadata_block.raw_data)) + return EResult::WriteError; + if (!write_line("; prusaslicer_config = end\n\n")) + return EResult::WriteError; + + return EResult::Success; +} +#endif // ENABLE_FILE_CONVERSION_INTERFACE + } // namespace BinaryGCode diff --git a/src/libslic3r/GCode/GCodeBinarizer.hpp b/src/libslic3r/GCode/GCodeBinarizer.hpp index ab03f32563..236aee1c5f 100644 --- a/src/libslic3r/GCode/GCodeBinarizer.hpp +++ b/src/libslic3r/GCode/GCodeBinarizer.hpp @@ -6,6 +6,7 @@ #endif // _WIN32 #define ENABLE_CHECKSUM_BLOCK 0 +#define ENABLE_FILE_CONVERSION_INTERFACE 1 #include #include @@ -41,7 +42,9 @@ enum class EResult : uint16_t InvalidThumbnailFormat, InvalidThumbnailWidth, InvalidThumbnailHeight, - InvalidThumbnailDataSize + InvalidThumbnailDataSize, + InvalidBinaryGCodeFile, + InvalidSequenceOfBlocks }; // Returns a string description of the given result @@ -359,6 +362,20 @@ extern size_t checksum_size(EChecksumType type); // Returns the size of the content (parameters + data + checksum) of the block with the given header, in bytes. extern size_t block_content_size(const FileHeader& file_header, const BlockHeader& block_header); +#if ENABLE_FILE_CONVERSION_INTERFACE +//===================================================================================================================================== +// +// FILE CONVERSION INTERFACE +// +//===================================================================================================================================== + +// Converts the gcode file contained into src_file from ascii to binary format and save the results into dst_file +extern EResult from_ascii_to_binary(FILE& src_file, FILE& dst_file); + +// Converts the gcode file contained into src_file from binary to ascii format and save the results into dst_file +extern EResult from_binary_to_ascii(FILE& src_file, FILE& dst_file, bool verify_checksum); +#endif // ENABLE_FILE_CONVERSION_INTERFACE + } // namespace BinaryGCode #endif // slic3r_GCode_GCodeBinarizer_hpp_ diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 2a4d8ae180..23ae8a1a24 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1349,6 +1349,17 @@ void MainFrame::init_menubar_as_editor() []() {return true; }, this); append_submenu(fileMenu, export_menu, wxID_ANY, _L("&Export"), ""); +#if ENABLE_BINARIZED_GCODE + wxMenu* convert_menu = new wxMenu(); + append_menu_item(convert_menu, wxID_ANY, _L("Convert ascii G-code to &binary") + dots, _L("Convert a G-code file from ascii to binary format"), + [this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->convert_gcode_to_binary(); }, "convert_file", nullptr, + [this]() { return true; }, this); + append_menu_item(convert_menu, wxID_ANY, _L("Convert binary G-code to &ascii") + dots, _L("Convert a G-code file from binary to ascii format"), + [this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->convert_gcode_to_ascii(); }, "convert_file", nullptr, + [this]() { return true; }, this); + append_submenu(fileMenu, convert_menu, wxID_ANY, _L("&Convert"), ""); +#endif // ENABLE_BINARIZED_GCODE + append_menu_item(fileMenu, wxID_ANY, _L("Ejec&t SD Card / Flash Drive") + dots + "\tCtrl+T", _L("Eject SD card / Flash drive after the G-code was exported to it."), [this](wxCommandEvent&) { if (m_plater) m_plater->eject_drive(); }, "eject_sd", nullptr, [this]() {return can_eject(); }, this); @@ -1621,13 +1632,22 @@ void MainFrame::init_menubar_as_gcodeviewer() _L("Reload the plater from disk"), [this](wxCommandEvent&) { m_plater->reload_gcode_from_disk(); }, "", nullptr, [this]() { return !m_plater->get_last_loaded_gcode().empty(); }, this); #endif // __APPLE__ +#if ENABLE_BINARIZED_GCODE + fileMenu->AppendSeparator(); + append_menu_item(fileMenu, wxID_ANY, _L("Convert ascii G-code to &binary") + dots, _L("Convert a G-code file from ascii to binary format"), + [this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->convert_gcode_to_binary(); }, "convert_file", nullptr, + [this]() { return true; }, this); + append_menu_item(fileMenu, wxID_ANY, _L("Convert binary G-code to &ascii") + dots, _L("Convert a G-code file from binary to ascii format"), + [this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->convert_gcode_to_ascii(); }, "convert_file", nullptr, + [this]() { return true; }, this); +#endif // ENABLE_BINARIZED_GCODE fileMenu->AppendSeparator(); append_menu_item(fileMenu, wxID_ANY, _L("Export &Toolpaths as OBJ") + dots, _L("Export toolpaths as OBJ"), [this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->export_toolpaths_to_obj(); }, "export_plater", nullptr, [this]() {return can_export_toolpaths(); }, this); append_menu_item(fileMenu, wxID_ANY, _L("Open &PrusaSlicer") + dots, _L("Open PrusaSlicer"), [](wxCommandEvent&) { start_new_slicer(); }, "", nullptr, - []() {return true; }, this); + []() { return true; }, this); fileMenu->AppendSeparator(); append_menu_item(fileMenu, wxID_EXIT, _L("&Quit"), wxString::Format(_L("Quit %s"), SLIC3R_APP_NAME), [this](wxCommandEvent&) { Close(false); }); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index fcd97cdc15..2878e28a5a 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5430,6 +5430,77 @@ void Plater::reload_gcode_from_disk() load_gcode(filename); } +#if ENABLE_BINARIZED_GCODE +static bool is_valid_binary_gcode(const wxString& filename) +{ + FILE* file = boost::nowide::fopen(into_u8(filename).c_str(), "rb"); + if (file == nullptr) + return false; + + const bool ret = BinaryGCode::is_valid_binary_gcode(*file); + fclose(file); + return ret; +} + +void Plater::convert_gcode_to_ascii() +{ + // Ask user for a gcode file name. + wxString input_file; + wxGetApp().load_gcode(this, input_file); + + class ScopedFile + { + public: + explicit ScopedFile(FILE* file) : m_file(file) {} + ~ScopedFile() { if (m_file != nullptr) fclose(m_file); } + private: + FILE* m_file{ nullptr }; + }; + + // Open source file + FILE* in_file = boost::nowide::fopen(into_u8(input_file).c_str(), "rb"); + if (in_file == nullptr) { + MessageDialog msg_dlg(this, _L("Unable to open the selected file."), _L("Error"), wxICON_ERROR | wxOK); + msg_dlg.ShowModal(); + return; + } + ScopedFile scoped_in_file(in_file); + + // Set out filename + boost::filesystem::path path(into_u8(input_file)); + const std::string output_file = path.parent_path().string() + "/" + path.stem().string() + "_ascii" + path.extension().string(); + + // Open destination file + FILE* out_file = boost::nowide::fopen(output_file.c_str(), "wb"); + if (out_file == nullptr) { + MessageDialog msg_dlg(this, _L("Unable to open output file."), _L("Error"), wxICON_ERROR | wxOK); + msg_dlg.ShowModal(); + return; + } + ScopedFile scoped_out_file(out_file); + + // Perform conversion + { + wxBusyCursor busy; + BinaryGCode::EResult res = BinaryGCode::from_binary_to_ascii(*in_file, *out_file, true); + if (res != BinaryGCode::EResult::Success) { + MessageDialog msg_dlg(this, _L(BinaryGCode::translate_result(res)), _L("Error converting gcode file"), wxICON_INFORMATION | wxOK); + msg_dlg.ShowModal(); + return; + } + } + + MessageDialog msg_dlg(this, _L("Succesfully created gcode ascii file:\n") + output_file, _L("Convert gcode file to ascii format"), wxICON_ERROR | wxOK); + msg_dlg.ShowModal(); +} + +void Plater::convert_gcode_to_binary() +{ + MessageDialog msg_dlg(this, _L("Not implemented yet."), _L("Error"), wxICON_ERROR | wxOK); + msg_dlg.ShowModal(); +} +#endif // ENABLE_BINARIZED_GCODE + void Plater::refresh_print() { p->preview->refresh_print(); diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 8eeabae428..342555040b 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -172,6 +172,10 @@ public: void load_gcode(); void load_gcode(const wxString& filename); void reload_gcode_from_disk(); +#if ENABLE_BINARIZED_GCODE + void convert_gcode_to_ascii(); + void convert_gcode_to_binary(); +#endif // ENABLE_BINARIZED_GCODE void refresh_print(); std::vector load_files(const std::vector& input_files, bool load_model = true, bool load_config = true, bool imperial_units = false); From 98c011d59bf63415eb0c279a799d4db78263144b Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Wed, 26 Jul 2023 11:24:32 +0200 Subject: [PATCH 028/136] ArcWelder: Improved resolution of arc discretization in G-code preview --- src/libslic3r/GCode.cpp | 9 +++++--- src/libslic3r/GCode/GCodeProcessor.cpp | 24 ++++++++++----------- src/libslic3r/GCode/GCodeProcessor.hpp | 11 +++++++++- src/libslic3r/GCode/GCodeWriter.cpp | 15 +++++++++++++ src/libslic3r/GCode/GCodeWriter.hpp | 6 ++++-- src/libslic3r/GCode/Wipe.cpp | 2 ++ src/libslic3r/Geometry/ArcWelder.cpp | 5 +++-- src/libslic3r/Geometry/ArcWelder.hpp | 30 ++++++++++++++++++++++++-- 8 files changed, 79 insertions(+), 23 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 168dad84ce..eff430fdb0 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2902,9 +2902,10 @@ std::string GCodeGenerator::_extrude( Vec2d p = this->point_to_gcode_quantized(it->point); if (it->radius == 0) { // Extrude line segment. - const double line_length = (p - prev).norm(); - path_length += line_length; - gcode += m_writer.extrude_to_xy(p, e_per_mm * line_length, comment); + if (const double line_length = (p - prev).norm(); line_length > 0) { + path_length += line_length; + gcode += m_writer.extrude_to_xy(p, e_per_mm * line_length, comment); + } } else { // Extrude an arc. assert(m_config.arc_fitting == ArcFittingType::EmitCenter || @@ -2920,9 +2921,11 @@ std::string GCodeGenerator::_extrude( ij = GCodeFormatter::quantize(center_raw); } double angle = Geometry::ArcWelder::arc_angle(prev.cast(), p.cast(), double(radius)); + assert(angle > 0); const double line_length = angle * std::abs(radius); path_length += line_length; const double dE = e_per_mm * line_length; + assert(dE > 0); gcode += emit_radius ? m_writer.extrude_to_xy_G2G3R(p, radius, it->ccw(), dE, comment) : m_writer.extrude_to_xy_G2G3IJ(p, ij, it->ccw(), dE, comment); diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index e654d88f93..28cfc8a515 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -44,8 +44,6 @@ static const Slic3r::Vec3f DEFAULT_EXTRUDER_OFFSET = Slic3r::Vec3f::Zero(); // taken from PrusaResearch.ini - [printer:Original Prusa i3 MK2.5 MMU2] static const std::vector DEFAULT_EXTRUDER_COLORS = { "#FF8000", "#DB5182", "#3EC0FF", "#FF4F4F", "#FBEB7D" }; -static const std::string INTERNAL_G2G3_TAG = "!#!#! internal only - from G2/G3 expansion !#!#!"; - namespace Slic3r { const std::vector GCodeProcessor::Reserved_Tags = { @@ -2364,13 +2362,10 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) if (line.has_e()) g1_axes[E] = (double)line.e(); std::optional g1_feedrate = std::nullopt; if (line.has_f()) g1_feedrate = (double)line.f(); - std::optional g1_cmt = std::nullopt; - if (!line.comment().empty()) g1_cmt = line.comment(); - - process_G1(g1_axes, g1_feedrate, g1_cmt); + process_G1(g1_axes, g1_feedrate); } -void GCodeProcessor::process_G1(const std::array, 4>& axes, std::optional feedrate, std::optional cmt) +void GCodeProcessor::process_G1(const std::array, 4>& axes, std::optional feedrate, G1DiscretizationOrigin origin) { const float filament_diameter = (static_cast(m_extruder_id) < m_result.filament_diameters.size()) ? m_result.filament_diameters[m_extruder_id] : m_result.filament_diameters.back(); const float filament_radius = 0.5f * filament_diameter; @@ -2453,7 +2448,7 @@ void GCodeProcessor::process_G1(const std::array, 4>& axes m_height = m_forced_height; else if (m_layer_id == 0) m_height = m_first_layer_height + m_z_offset; - else if (!cmt.has_value() || *cmt != INTERNAL_G2G3_TAG) { + else if (origin == G1DiscretizationOrigin::G1) { if (m_end_position[Z] > m_extruded_last_z + EPSILON && delta_pos[Z] == 0.0) m_height = m_end_position[Z] - m_extruded_last_z; } @@ -2464,7 +2459,7 @@ void GCodeProcessor::process_G1(const std::array, 4>& axes if (m_end_position[Z] == 0.0f || (m_extrusion_role == GCodeExtrusionRole::Custom && m_layer_id == 0)) m_end_position[Z] = m_height; - if (!cmt.has_value() || *cmt != INTERNAL_G2G3_TAG) + if (origin == G1DiscretizationOrigin::G1) m_extruded_last_z = m_end_position[Z]; m_options_z_corrector.update(m_height); @@ -2694,7 +2689,7 @@ void GCodeProcessor::process_G1(const std::array, 4>& axes } // store move - store_move_vertex(type, cmt.has_value() && *cmt == INTERNAL_G2G3_TAG); + store_move_vertex(type, origin == G1DiscretizationOrigin::G2G3); } void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line, bool clockwise) @@ -2836,9 +2831,7 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line, bool cloc g1_feedrate = (double)*feedrate; if (extrusion.has_value()) g1_axes[E] = target[E]; - std::optional g1_cmt = INTERNAL_G2G3_TAG; - - process_G1(g1_axes, g1_feedrate, g1_cmt); + process_G1(g1_axes, g1_feedrate, G1DiscretizationOrigin::G2G3); }; // calculate arc segments @@ -2847,8 +2840,13 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line, bool cloc // https://github.com/prusa3d/Prusa-Firmware/blob/MK3/Firmware/motion_control.cpp // segments count +#if 0 static const double MM_PER_ARC_SEGMENT = 1.0; const size_t segments = std::max(std::floor(travel_length / MM_PER_ARC_SEGMENT), 1); +#else + static const double gcode_arc_tolerance = 0.0125; + const size_t segments = Geometry::ArcWelder::arc_discretization_steps(arc.start_radius(), std::abs(arc.angle), gcode_arc_tolerance); +#endif const double inv_segment = 1.0 / double(segments); const double theta_per_segment = arc.angle * inv_segment; diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index fcba4321ae..a383f251c2 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -56,9 +56,13 @@ namespace Slic3r { time = 0.0f; travel_time = 0.0f; custom_gcode_times.clear(); + custom_gcode_times.shrink_to_fit(); moves_times.clear(); + moves_times.shrink_to_fit(); roles_times.clear(); + roles_times.shrink_to_fit(); layers_times.clear(); + layers_times.shrink_to_fit(); } }; @@ -76,6 +80,7 @@ namespace Slic3r { m.reset(); } volumes_per_color_change.clear(); + volumes_per_color_change.shrink_to_fit(); volumes_per_extruder.clear(); used_filaments_per_role.clear(); cost_per_extruder.clear(); @@ -680,8 +685,12 @@ namespace Slic3r { // Move void process_G0(const GCodeReader::GCodeLine& line); void process_G1(const GCodeReader::GCodeLine& line); + enum class G1DiscretizationOrigin { + G1, + G2G3, + }; void process_G1(const std::array, 4>& axes = { std::nullopt, std::nullopt, std::nullopt, std::nullopt }, - std::optional feedrate = std::nullopt, std::optional cmt = std::nullopt); + std::optional feedrate = std::nullopt, G1DiscretizationOrigin origin = G1DiscretizationOrigin::G1); // Arc Move void process_G2_G3(const GCodeReader::GCodeLine& line, bool clockwise); diff --git a/src/libslic3r/GCode/GCodeWriter.cpp b/src/libslic3r/GCode/GCodeWriter.cpp index 2dad407caa..241daa3250 100644 --- a/src/libslic3r/GCode/GCodeWriter.cpp +++ b/src/libslic3r/GCode/GCodeWriter.cpp @@ -274,6 +274,12 @@ std::string GCodeWriter::travel_to_xy(const Vec2d &point, const std::string_view std::string GCodeWriter::travel_to_xy_G2G3IJ(const Vec2d &point, const Vec2d &ij, const bool ccw, const std::string_view comment) { + assert(std::abs(point.x()) < 1200.); + assert(std::abs(point.y()) < 1200.); + assert(std::abs(ij.x()) < 1200.); + assert(std::abs(ij.y()) < 1200.); + assert(std::abs(ij.x()) >= 0.001 || std::abs(ij.y()) >= 0.001); + m_pos.head<2>() = point.head<2>(); GCodeG2G3Formatter w(ccw); @@ -285,6 +291,11 @@ std::string GCodeWriter::travel_to_xy_G2G3IJ(const Vec2d &point, const Vec2d &ij std::string GCodeWriter::travel_to_xy_G2G3R(const Vec2d &point, const double radius, const bool ccw, const std::string_view comment) { + assert(std::abs(point.x()) < 1200.); + assert(std::abs(point.y()) < 1200.); + assert(std::abs(radius) >= 0.001); + assert(std::abs(radius) < 1800.); + m_pos.head<2>() = point.head<2>(); GCodeG2G3Formatter w(ccw); @@ -395,6 +406,8 @@ std::string GCodeWriter::extrude_to_xy_G2G3IJ(const Vec2d &point, const Vec2d &i assert(std::abs(point.y()) < 1200.); assert(std::abs(ij.x()) < 1200.); assert(std::abs(ij.y()) < 1200.); + assert(std::abs(ij.x()) >= 0.001 || std::abs(ij.y()) >= 0.001); + m_pos.head<2>() = point.head<2>(); GCodeG2G3Formatter w(ccw); @@ -411,7 +424,9 @@ std::string GCodeWriter::extrude_to_xy_G2G3R(const Vec2d &point, const double ra assert(std::abs(dE) < 1000.0); assert(std::abs(point.x()) < 1200.); assert(std::abs(point.y()) < 1200.); + assert(std::abs(radius) >= 0.001); assert(std::abs(radius) < 1800.); + m_pos.head<2>() = point.head<2>(); GCodeG2G3Formatter w(ccw); diff --git a/src/libslic3r/GCode/GCodeWriter.hpp b/src/libslic3r/GCode/GCodeWriter.hpp index 09a68b92de..127fb61c03 100644 --- a/src/libslic3r/GCode/GCodeWriter.hpp +++ b/src/libslic3r/GCode/GCodeWriter.hpp @@ -181,8 +181,10 @@ public: } void emit_ij(const Vec2d &point) { - this->emit_axis('I', point.x(), XYZF_EXPORT_DIGITS); - this->emit_axis('J', point.y(), XYZF_EXPORT_DIGITS); + if (point.x() != 0) + this->emit_axis('I', point.x(), XYZF_EXPORT_DIGITS); + if (point.y() != 0) + this->emit_axis('J', point.y(), XYZF_EXPORT_DIGITS); } // Positive radius means a smaller arc, diff --git a/src/libslic3r/GCode/Wipe.cpp b/src/libslic3r/GCode/Wipe.cpp index 561e86a835..0fa887810f 100644 --- a/src/libslic3r/GCode/Wipe.cpp +++ b/src/libslic3r/GCode/Wipe.cpp @@ -128,6 +128,7 @@ std::string Wipe::wipe(GCodeGenerator &gcodegen, bool toolchange) double radius = emit_radius ? GCodeFormatter::quantize_xyzf(radius_in) : radius_in; Vec2d center = Geometry::ArcWelder::arc_center(prev_quantized.cast(), p_quantized.cast(), double(radius), ccw); float angle = Geometry::ArcWelder::arc_angle(prev_quantized.cast(), p_quantized.cast(), double(radius)); + assert(angle > 0); double segment_length = angle * std::abs(radius); double dE = GCodeFormatter::quantize_e(xy_to_e * segment_length); bool done = false; @@ -146,6 +147,7 @@ std::string Wipe::wipe(GCodeGenerator &gcodegen, bool toolchange) done = true; } else p = p_quantized; + assert(dE > 0); if (emit_radius) { gcode += gcodegen.writer().extrude_to_xy_G2G3R(p, radius, ccw, -dE, wipe_retract_comment); } else { diff --git a/src/libslic3r/Geometry/ArcWelder.cpp b/src/libslic3r/Geometry/ArcWelder.cpp index bb23381de8..4f789f3c50 100644 --- a/src/libslic3r/Geometry/ArcWelder.cpp +++ b/src/libslic3r/Geometry/ArcWelder.cpp @@ -41,10 +41,11 @@ Points arc_discretize(const Point &p1, const Point &p2, const double radius, con { Vec2d center = arc_center(p1.cast(), p2.cast(), radius, ccw); double angle = arc_angle(p1.cast(), p2.cast(), radius); + assert(angle > 0); double r = std::abs(radius); - double angle_step = 2. * acos((r - deviation) / r); - size_t num_steps = size_t(ceil(angle / angle_step)); + size_t num_steps = arc_discretization_steps(r, angle, deviation); + double angle_step = angle / num_steps; Points out; out.reserve(num_steps + 1); diff --git a/src/libslic3r/Geometry/ArcWelder.hpp b/src/libslic3r/Geometry/ArcWelder.hpp index b328c12786..dfc6d886e6 100644 --- a/src/libslic3r/Geometry/ArcWelder.hpp +++ b/src/libslic3r/Geometry/ArcWelder.hpp @@ -54,8 +54,10 @@ inline typename Derived::Scalar arc_angle( using Float = typename Derived::Scalar; Float a = Float(0.5) * (end_pos - start_pos).norm() / radius; return radius > Float(0) ? - (a > Float( 1.) ? Float( M_PI) : Float(2.) * std::asin(a)) : - (a < Float(-1.) ? Float( - M_PI) : Float(2. * M_PI) + Float(2.) * std::asin(a)); + // acute angle: + (a > Float( 1.) ? Float(M_PI) : Float(2.) * std::asin(a)) : + // obtuse angle: + (a < Float(-1.) ? Float(M_PI) : Float(2. * M_PI) + Float(2.) * std::asin(a)); } // Calculate positive length of an arc given two points and a radius. @@ -164,6 +166,30 @@ inline bool inside_arc_wedge( radius > 0, ccw, query_pt); } +// Return number of linear segments necessary to interpolate arc of a given positive radius and positive angle to satisfy +// maximum deviation of an interpolating polyline from an analytic arc. +template +size_t arc_discretization_steps(const FloatType radius, const FloatType angle, const FloatType deviation) +{ + assert(radius > 0); + assert(angle > 0); + assert(angle <= FloatType(2. * M_PI)); + assert(deviation > 0); + + FloatType d = radius - deviation; + return d < EPSILON ? + // Radius smaller than deviation. + ( // Acute angle: a single segment interpolates the arc with sufficient accuracy. + angle < M_PI || + // Obtuse angle: Test whether the furthest point (center) of an arc is closer than deviation to the center of a line segment. + radius * (FloatType(1.) + cos(M_PI - FloatType(.5) * angle)) < deviation ? + // Single segment is sufficient + 1 : + // Two segments are necessary, the middle point is at the center of the arc. + 2) : + size_t(ceil(angle / (2. * acos(d / radius)))); +} + // Discretize arc given the radius, orientation and maximum deviation from the arc. // Returned polygon starts with p1, ends with p2 and it is discretized to guarantee the maximum deviation. Points arc_discretize(const Point &p1, const Point &p2, const double radius, const bool ccw, const double deviation); From 28cd3ac212f9d2a67538dc5998ce6883d37defb6 Mon Sep 17 00:00:00 2001 From: Pavel Mikus Date: Tue, 18 Jul 2023 17:28:22 +0200 Subject: [PATCH 029/136] new step for overhangs --- src/libslic3r/GCode/ExtrusionProcessor.hpp | 86 ++++++++++++++++++++++ src/libslic3r/Print.hpp | 3 +- src/libslic3r/PrintObject.cpp | 35 +++++++++ 3 files changed, 123 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/GCode/ExtrusionProcessor.hpp b/src/libslic3r/GCode/ExtrusionProcessor.hpp index f6f75bef30..1deb1cd067 100644 --- a/src/libslic3r/GCode/ExtrusionProcessor.hpp +++ b/src/libslic3r/GCode/ExtrusionProcessor.hpp @@ -211,6 +211,92 @@ std::vector estimate_points_properties(const POINTS return points; } +void calculate_and_split_overhanging_extrusions(ExtrusionPath &path, + AABBTreeLines::LinesDistancer prev_layer_polygon, + AABBTreeLines::LinesDistancer prev_layer_curled_lines) +{ + std::vector extended_points = estimate_points_properties(path.polyline.points, + prev_layer_polygon, path.width()); + + + for (size_t i = 0; i < extended_points.size(); i++) { + const ExtendedPoint &curr = extended_points[i]; + const ExtendedPoint &next = extended_points[i + 1 < extended_points.size() ? i + 1 : i]; + + // The following code artifically increases the distance to provide slowdown for extrusions that are over curled lines + float artificial_distance_to_curled_lines = 0.0; + const double dist_limit = 10.0 * path.width(); + { + Vec2d middle = 0.5 * (curr.position + next.position); + auto line_indices = prev_layer_curled_lines.all_lines_in_radius(Point::new_scale(middle), scale_(dist_limit)); + if (!line_indices.empty()) { + double len = (next.position - curr.position).norm(); + // For long lines, there is a problem with the additional slowdown. If by accident, there is small curled line near the middle + // of this long line + // The whole segment gets slower unnecesarily. For these long lines, we do additional check whether it is worth slowing down. + // NOTE that this is still quite rough approximation, e.g. we are still checking lines only near the middle point + // TODO maybe split the lines into smaller segments before running this alg? but can be demanding, and GCode will be huge + if (len > 8) { + Vec2d dir = Vec2d(next.position - curr.position) / len; + Vec2d right = Vec2d(-dir.y(), dir.x()); + + Polygon box_of_influence = { + scaled(Vec2d(curr.position + right * dist_limit)), + scaled(Vec2d(next.position + right * dist_limit)), + scaled(Vec2d(next.position - right * dist_limit)), + scaled(Vec2d(curr.position - right * dist_limit)), + }; + + double projected_lengths_sum = 0; + for (size_t idx : line_indices) { + const CurledLine &line = prev_layer_curled_lines.get_line(idx); + Lines inside = intersection_ln({{line.a, line.b}}, {box_of_influence}); + if (inside.empty()) + continue; + double projected_length = abs(dir.dot(unscaled(Vec2d((inside.back().b - inside.back().a).cast())))); + projected_lengths_sum += projected_length; + } + if (projected_lengths_sum < 0.4 * len) { + line_indices.clear(); + } + } + + for (size_t idx : line_indices) { + const CurledLine &line = prev_layer_curled_lines.get_line(idx); + float distance_from_curled = unscaled(line_alg::distance_to(line, Point::new_scale(middle))); + float dist = path.width() * (1.0 - (distance_from_curled / dist_limit)) * (1.0 - (distance_from_curled / dist_limit)) * + (line.curled_height / (path.height() * 10.0f)); // max_curled_height_factor from SupportSpotGenerator + artificial_distance_to_curled_lines = std::max(artificial_distance_to_curled_lines, dist); + } + } + } + + auto interpolate_speed = [](const std::map &values, float distance) { + auto upper_dist = values.lower_bound(distance); + if (upper_dist == values.end()) { + return values.rbegin()->second; + } + if (upper_dist == values.begin()) { + return upper_dist->second; + } + + auto lower_dist = std::prev(upper_dist); + float t = (distance - lower_dist->first) / (upper_dist->first - lower_dist->first); + return (1.0f - t) * lower_dist->second + t * upper_dist->second; + }; + + float extrusion_speed = std::min(interpolate_speed(speed_sections, curr.distance), interpolate_speed(speed_sections, next.distance)); + float curled_base_speed = interpolate_speed(speed_sections, artificial_distance_to_curled_lines); + float final_speed = std::min(curled_base_speed, extrusion_speed); + float fan_speed = std::min(interpolate_speed(fan_speed_sections, curr.distance), + interpolate_speed(fan_speed_sections, next.distance)); + + processed_points.push_back({scaled(curr.position), final_speed, int(fan_speed)}); + } + return processed_points; +} + + struct ProcessedPoint { Point p; diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 0600b02fff..f16befc964 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -67,7 +67,7 @@ enum PrintStep : unsigned int { enum PrintObjectStep : unsigned int { posSlice, posPerimeters, posPrepareInfill, - posInfill, posIroning, posSupportSpotsSearch, posSupportMaterial, posEstimateCurledExtrusions, posCount, + posInfill, posIroning, posSupportSpotsSearch, posSupportMaterial, posEstimateCurledExtrusions, posCalculateOverhangingPerimeters, posCount, }; // A PrintRegion object represents a group of volumes to print @@ -376,6 +376,7 @@ private: void generate_support_spots(); void generate_support_material(); void estimate_curled_extrusions(); + void calculate_overhanging_perimeters(); void slice_volumes(); // Has any support (not counting the raft). diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index ebf6e8d48c..522a61375e 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -4,6 +4,7 @@ #include "Exception.hpp" #include "Flow.hpp" #include "KDTreeIndirect.hpp" +#include "Line.hpp" #include "Point.hpp" #include "Polygon.hpp" #include "Polyline.hpp" @@ -533,6 +534,40 @@ void PrintObject::estimate_curled_extrusions() } } +void PrintObject::calculate_overhanging_perimeters() +{ + if (this->set_started(posCalculateOverhangingPerimeters)) { + std::unordered_set regions_with_dynamic_overhangs; + for (const PrintRegion *pr : this->print()->m_print_regions) { + if (pr->config().enable_dynamic_overhang_speeds.getBool()) { + regions_with_dynamic_overhangs.insert(pr); + } + } + if (!regions_with_dynamic_overhangs.empty()) { + BOOST_LOG_TRIVIAL(debug) << "Calculating overhanging perimeters - start"; + m_print->set_status(89, _u8L("Calculating overhanging perimeters")); + + std::unordered_map> curled_lines; + for (const Layer *l : this->layers()) { + curled_lines[l->id()] = AABBTreeLines::LinesDistancer{l->curled_lines}; + } + + for (Layer *l : this->layers()) { + for (const LayerRegion *layer_region : l->regions()) { + if (regions_with_dynamic_overhangs.find(layer_region->m_region) == regions_with_dynamic_overhangs.end()) { + continue; + } + + } + } + + m_print->throw_if_canceled(); + BOOST_LOG_TRIVIAL(debug) << "Calculating overhanging perimeters - end"; + } + this->set_done(posCalculateOverhangingPerimeters); + } +} + std::pair PrintObject::prepare_adaptive_infill_data( const std::vector> &surfaces_w_bottom_z) const { From 6ec918c2a2d2b27d8fa73cf34f108745453869b9 Mon Sep 17 00:00:00 2001 From: Pavel Date: Wed, 19 Jul 2023 16:13:23 +0200 Subject: [PATCH 030/136] Compuation of overhang distance of the perimeters is a new step in the slicing pipeline, the result is stored as and ExtrusionPath attribute. Perimeters are split as necessary based on the differing distances. --- src/libslic3r/AABBTreeLines.hpp | 2 +- src/libslic3r/CMakeLists.txt | 2 + src/libslic3r/ExtrusionEntity.hpp | 9 ++ src/libslic3r/GCode/ExtrusionProcessor.cpp | 130 +++++++++++++++++++++ src/libslic3r/GCode/ExtrusionProcessor.hpp | 126 +++++--------------- src/libslic3r/PrintObject.cpp | 12 +- 6 files changed, 178 insertions(+), 103 deletions(-) create mode 100644 src/libslic3r/GCode/ExtrusionProcessor.cpp diff --git a/src/libslic3r/AABBTreeLines.hpp b/src/libslic3r/AABBTreeLines.hpp index 990b3197fd..fd5e7cd967 100644 --- a/src/libslic3r/AABBTreeLines.hpp +++ b/src/libslic3r/AABBTreeLines.hpp @@ -345,7 +345,7 @@ public: return dist; } - std::vector all_lines_in_radius(const Vec<2, Scalar> &point, Floating radius) + std::vector all_lines_in_radius(const Vec<2, Scalar> &point, Floating radius) const { return AABBTreeLines::all_lines_in_radius(this->lines, this->tree, point.template cast(), radius * radius); } diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 10aa7bd38a..696c4b8bb5 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -142,6 +142,8 @@ set(SLIC3R_SOURCES GCode/ConflictChecker.hpp GCode/CoolingBuffer.cpp GCode/CoolingBuffer.hpp + GCode/ExtrusionProcessor.cpp + GCode/ExtrusionProcessor.hpp GCode/FindReplace.cpp GCode/FindReplace.hpp GCode/GCodeWriter.cpp diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp index d7c9367ef5..5893853364 100644 --- a/src/libslic3r/ExtrusionEntity.hpp +++ b/src/libslic3r/ExtrusionEntity.hpp @@ -8,6 +8,7 @@ #include "Polyline.hpp" #include +#include #include #include @@ -101,6 +102,11 @@ inline bool operator==(const ExtrusionFlow &lhs, const ExtrusionFlow &rhs) return lhs.mm3_per_mm == rhs.mm3_per_mm && lhs.width == rhs.width && lhs.height == rhs.height; } +struct OverhangAttributes { + float max_distance_from_prev_layer; + float proximity_to_curled_lines; //value between 0 and 1 +}; + struct ExtrusionAttributes : ExtrusionFlow { ExtrusionAttributes() = default; @@ -110,6 +116,9 @@ struct ExtrusionAttributes : ExtrusionFlow // What is the role / purpose of this extrusion? ExtrusionRole role{ ExtrusionRole::None }; + // OVerhangAttributes are currently computed for perimeters if dynamic overhangs are enabled. + // They are used to control fan and print speed in export. + std::optional overhang_attributes; }; inline bool operator==(const ExtrusionAttributes &lhs, const ExtrusionAttributes &rhs) diff --git a/src/libslic3r/GCode/ExtrusionProcessor.cpp b/src/libslic3r/GCode/ExtrusionProcessor.cpp new file mode 100644 index 0000000000..3f5d8f1d54 --- /dev/null +++ b/src/libslic3r/GCode/ExtrusionProcessor.cpp @@ -0,0 +1,130 @@ +#include "ExtrusionProcessor.hpp" + +namespace Slic3r { + +ExtrusionPaths calculate_and_split_overhanging_extrusions(const ExtrusionPath &path, + const AABBTreeLines::LinesDistancer &unscaled_prev_layer, + const AABBTreeLines::LinesDistancer &prev_layer_curled_lines) +{ + std::vector extended_points = estimate_points_properties(path.polyline.points, + unscaled_prev_layer, path.width()); + std::vector> calculated_distances(extended_points.size()); + + for (size_t i = 0; i < extended_points.size(); i++) { + const ExtendedPoint &curr = extended_points[i]; + const ExtendedPoint &next = extended_points[i + 1 < extended_points.size() ? i + 1 : i]; + + // The following code artifically increases the distance to provide slowdown for extrusions that are over curled lines + float proximity_to_curled_lines = 0.0; + const double dist_limit = 10.0 * path.width(); + { + Vec2d middle = 0.5 * (curr.position + next.position); + auto line_indices = prev_layer_curled_lines.all_lines_in_radius(Point::new_scale(middle), scale_(dist_limit)); + if (!line_indices.empty()) { + double len = (next.position - curr.position).norm(); + // For long lines, there is a problem with the additional slowdown. If by accident, there is small curled line near the middle + // of this long line + // The whole segment gets slower unnecesarily. For these long lines, we do additional check whether it is worth slowing down. + // NOTE that this is still quite rough approximation, e.g. we are still checking lines only near the middle point + // TODO maybe split the lines into smaller segments before running this alg? but can be demanding, and GCode will be huge + if (len > 8) { + Vec2d dir = Vec2d(next.position - curr.position) / len; + Vec2d right = Vec2d(-dir.y(), dir.x()); + + Polygon box_of_influence = { + scaled(Vec2d(curr.position + right * dist_limit)), + scaled(Vec2d(next.position + right * dist_limit)), + scaled(Vec2d(next.position - right * dist_limit)), + scaled(Vec2d(curr.position - right * dist_limit)), + }; + + double projected_lengths_sum = 0; + for (size_t idx : line_indices) { + const CurledLine &line = prev_layer_curled_lines.get_line(idx); + Lines inside = intersection_ln({{line.a, line.b}}, {box_of_influence}); + if (inside.empty()) + continue; + double projected_length = abs(dir.dot(unscaled(Vec2d((inside.back().b - inside.back().a).cast())))); + projected_lengths_sum += projected_length; + } + if (projected_lengths_sum < 0.4 * len) { + line_indices.clear(); + } + } + + for (size_t idx : line_indices) { + const CurledLine &line = prev_layer_curled_lines.get_line(idx); + float distance_from_curled = unscaled(line_alg::distance_to(line, Point::new_scale(middle))); + float proximity = (1.0 - (distance_from_curled / dist_limit)) * (1.0 - (distance_from_curled / dist_limit)) * + (line.curled_height / (path.height() * 10.0f)); // max_curled_height_factor from SupportSpotGenerator + proximity_to_curled_lines = std::max(proximity_to_curled_lines, proximity); + } + } + } + calculated_distances[i].first = std::max(curr.distance, next.distance); + calculated_distances[i].second = proximity_to_curled_lines; + } + + ExtrusionPaths result; + ExtrusionAttributes new_attrs = path.attributes(); + new_attrs.overhang_attributes = std::optional({calculated_distances[0].first, calculated_distances[0].second}); + result.emplace_back(new_attrs); + result.back().polyline.append(Point::new_scale(extended_points[0].position)); + for (size_t i = 1; i < extended_points.size(); i++) { + if (std::abs(calculated_distances[i].first - calculated_distances[i - 1].first) < path.width() * 0.1 && + std::abs(calculated_distances[i].second - calculated_distances[i - 1].second) < 0.1) { + // do not start new path, the attributes are similar enough + } else { + result.back().polyline.append(Point::new_scale(extended_points[i].position)); + new_attrs.overhang_attributes->max_distance_from_prev_layer = calculated_distances[i].first; + new_attrs.overhang_attributes->proximity_to_curled_lines = calculated_distances[i].second; + result.emplace_back(new_attrs); + } + result.back().polyline.append(Point::new_scale(extended_points[i].position)); + } + + return result; +}; + +ExtrusionEntityCollection calculate_and_split_overhanging_extrusions(const ExtrusionEntityCollection &ecc, + const AABBTreeLines::LinesDistancer &unscaled_prev_layer, + const AABBTreeLines::LinesDistancer &prev_layer_curled_lines) +{ + ExtrusionEntityCollection result; + result.no_sort = ecc.no_sort; + for (const auto *e : ecc.entities) { + if (auto *col = static_cast(e)) { + auto new_col = calculate_and_split_overhanging_extrusions(*col, unscaled_prev_layer, prev_layer_curled_lines); + result.append(new_col); + } else if (auto *loop = static_cast(e)) { + ExtrusionLoop new_loop = *loop; + new_loop.paths.clear(); + for (const ExtrusionPath &p : loop->paths) { + auto paths = calculate_and_split_overhanging_extrusions(p, unscaled_prev_layer, prev_layer_curled_lines); + new_loop.paths.insert(new_loop.paths.end(), paths.begin(), paths.end()); + } + result.append(new_loop); + } else if (auto *mp = static_cast(e)) { + ExtrusionMultiPath new_mp = *mp; + new_mp.paths.clear(); + for (const ExtrusionPath &p : mp->paths) { + auto paths = calculate_and_split_overhanging_extrusions(p, unscaled_prev_layer, prev_layer_curled_lines); + new_mp.paths.insert(new_mp.paths.end(), paths.begin(), paths.end()); + } + result.append(new_mp); + } else if (auto *op = static_cast(e)) { + auto paths = calculate_and_split_overhanging_extrusions(*op, unscaled_prev_layer, prev_layer_curled_lines); + for (const ExtrusionPath &p : paths) { + result.append(ExtrusionPathOriented(p.polyline, p.attributes())); + } + } else if (auto *p = static_cast(e)) { + auto paths = calculate_and_split_overhanging_extrusions(*p, unscaled_prev_layer, prev_layer_curled_lines); + result.append(paths); + } else { + throw Slic3r::InvalidArgument("Unknown extrusion entity type"); + } + } + return result; +}; + +} // namespace Slic3r \ No newline at end of file diff --git a/src/libslic3r/GCode/ExtrusionProcessor.hpp b/src/libslic3r/GCode/ExtrusionProcessor.hpp index 1deb1cd067..0d60ad9851 100644 --- a/src/libslic3r/GCode/ExtrusionProcessor.hpp +++ b/src/libslic3r/GCode/ExtrusionProcessor.hpp @@ -14,6 +14,7 @@ #include "../Flow.hpp" #include "../Config.hpp" #include "../Line.hpp" +#include "../Exception.hpp" #include #include @@ -21,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -211,106 +213,29 @@ std::vector estimate_points_properties(const POINTS return points; } -void calculate_and_split_overhanging_extrusions(ExtrusionPath &path, - AABBTreeLines::LinesDistancer prev_layer_polygon, - AABBTreeLines::LinesDistancer prev_layer_curled_lines) -{ - std::vector extended_points = estimate_points_properties(path.polyline.points, - prev_layer_polygon, path.width()); +ExtrusionPaths calculate_and_split_overhanging_extrusions(const ExtrusionPath &path, + const AABBTreeLines::LinesDistancer &unscaled_prev_layer, + const AABBTreeLines::LinesDistancer &prev_layer_curled_lines); - for (size_t i = 0; i < extended_points.size(); i++) { - const ExtendedPoint &curr = extended_points[i]; - const ExtendedPoint &next = extended_points[i + 1 < extended_points.size() ? i + 1 : i]; - - // The following code artifically increases the distance to provide slowdown for extrusions that are over curled lines - float artificial_distance_to_curled_lines = 0.0; - const double dist_limit = 10.0 * path.width(); - { - Vec2d middle = 0.5 * (curr.position + next.position); - auto line_indices = prev_layer_curled_lines.all_lines_in_radius(Point::new_scale(middle), scale_(dist_limit)); - if (!line_indices.empty()) { - double len = (next.position - curr.position).norm(); - // For long lines, there is a problem with the additional slowdown. If by accident, there is small curled line near the middle - // of this long line - // The whole segment gets slower unnecesarily. For these long lines, we do additional check whether it is worth slowing down. - // NOTE that this is still quite rough approximation, e.g. we are still checking lines only near the middle point - // TODO maybe split the lines into smaller segments before running this alg? but can be demanding, and GCode will be huge - if (len > 8) { - Vec2d dir = Vec2d(next.position - curr.position) / len; - Vec2d right = Vec2d(-dir.y(), dir.x()); - - Polygon box_of_influence = { - scaled(Vec2d(curr.position + right * dist_limit)), - scaled(Vec2d(next.position + right * dist_limit)), - scaled(Vec2d(next.position - right * dist_limit)), - scaled(Vec2d(curr.position - right * dist_limit)), - }; - - double projected_lengths_sum = 0; - for (size_t idx : line_indices) { - const CurledLine &line = prev_layer_curled_lines.get_line(idx); - Lines inside = intersection_ln({{line.a, line.b}}, {box_of_influence}); - if (inside.empty()) - continue; - double projected_length = abs(dir.dot(unscaled(Vec2d((inside.back().b - inside.back().a).cast())))); - projected_lengths_sum += projected_length; - } - if (projected_lengths_sum < 0.4 * len) { - line_indices.clear(); - } - } - - for (size_t idx : line_indices) { - const CurledLine &line = prev_layer_curled_lines.get_line(idx); - float distance_from_curled = unscaled(line_alg::distance_to(line, Point::new_scale(middle))); - float dist = path.width() * (1.0 - (distance_from_curled / dist_limit)) * (1.0 - (distance_from_curled / dist_limit)) * - (line.curled_height / (path.height() * 10.0f)); // max_curled_height_factor from SupportSpotGenerator - artificial_distance_to_curled_lines = std::max(artificial_distance_to_curled_lines, dist); - } - } - } - - auto interpolate_speed = [](const std::map &values, float distance) { - auto upper_dist = values.lower_bound(distance); - if (upper_dist == values.end()) { - return values.rbegin()->second; - } - if (upper_dist == values.begin()) { - return upper_dist->second; - } - - auto lower_dist = std::prev(upper_dist); - float t = (distance - lower_dist->first) / (upper_dist->first - lower_dist->first); - return (1.0f - t) * lower_dist->second + t * upper_dist->second; - }; - - float extrusion_speed = std::min(interpolate_speed(speed_sections, curr.distance), interpolate_speed(speed_sections, next.distance)); - float curled_base_speed = interpolate_speed(speed_sections, artificial_distance_to_curled_lines); - float final_speed = std::min(curled_base_speed, extrusion_speed); - float fan_speed = std::min(interpolate_speed(fan_speed_sections, curr.distance), - interpolate_speed(fan_speed_sections, next.distance)); - - processed_points.push_back({scaled(curr.position), final_speed, int(fan_speed)}); - } - return processed_points; -} - +ExtrusionEntityCollection calculate_and_split_overhanging_extrusions(const ExtrusionEntityCollection &ecc, + const AABBTreeLines::LinesDistancer &unscaled_prev_layer, + const AABBTreeLines::LinesDistancer &prev_layer_curled_lines); struct ProcessedPoint { Point p; - float speed = 1.0f; - int fan_speed = 0; + float speed = 1.0f; + int fan_speed = 0; }; class ExtrusionQualityEstimator { - std::unordered_map> prev_layer_boundaries; - std::unordered_map> next_layer_boundaries; + std::unordered_map> prev_layer_boundaries; + std::unordered_map> next_layer_boundaries; std::unordered_map> prev_curled_extrusions; std::unordered_map> next_curled_extrusions; - const PrintObject *current_object; + const PrintObject *current_object; public: void set_current_object(const PrintObject *object) { current_object = object; } @@ -338,17 +263,18 @@ public: float speed_base = ext_perimeter_speed > 0 ? ext_perimeter_speed : original_speed; std::map speed_sections; for (size_t i = 0; i < overhangs_w_speeds.size(); i++) { - float distance = flow.width * (1.0 - (overhangs_w_speeds[i].first / 100.0)); - float speed = overhangs_w_speeds[i].second.percent ? (speed_base * overhangs_w_speeds[i].second.value / 100.0) : - overhangs_w_speeds[i].second.value; - if (speed < EPSILON) speed = speed_base; + float distance = flow.width * (1.0 - (overhangs_w_speeds[i].first / 100.0)); + float speed = overhangs_w_speeds[i].second.percent ? (speed_base * overhangs_w_speeds[i].second.value / 100.0) : + overhangs_w_speeds[i].second.value; + if (speed < EPSILON) + speed = speed_base; speed_sections[distance] = speed; } std::map fan_speed_sections; for (size_t i = 0; i < overhangs_w_fan_speeds.size(); i++) { - float distance = flow.width * (1.0 - (overhangs_w_fan_speeds[i].first / 100.0)); - float fan_speed = overhangs_w_fan_speeds[i].second.get_at(extruder_id); + float distance = flow.width * (1.0 - (overhangs_w_fan_speeds[i].first / 100.0)); + float fan_speed = overhangs_w_fan_speeds[i].second.get_at(extruder_id); fan_speed_sections[distance] = fan_speed; } @@ -362,15 +288,17 @@ public: const ExtendedPoint &next = extended_points[i + 1 < extended_points.size() ? i + 1 : i]; // The following code artifically increases the distance to provide slowdown for extrusions that are over curled lines - float artificial_distance_to_curled_lines = 0.0; - const double dist_limit = 10.0 * flow.width; + float artificial_distance_to_curled_lines = 0.0; + const double dist_limit = 10.0 * flow.width; { Vec2d middle = 0.5 * (curr.position + next.position); auto line_indices = prev_curled_extrusions[current_object].all_lines_in_radius(Point::new_scale(middle), scale_(dist_limit)); if (!line_indices.empty()) { - double len = (next.position - curr.position).norm(); - // For long lines, there is a problem with the additional slowdown. If by accident, there is small curled line near the middle of this long line - // The whole segment gets slower unnecesarily. For these long lines, we do additional check whether it is worth slowing down. + double len = (next.position - curr.position).norm(); + // For long lines, there is a problem with the additional slowdown. If by accident, there is small curled line near + // the middle of this long line + // The whole segment gets slower unnecesarily. For these long lines, we do additional check whether it is worth slowing + // down. // NOTE that this is still quite rough approximation, e.g. we are still checking lines only near the middle point // TODO maybe split the lines into smaller segments before running this alg? but can be demanding, and GCode will be huge if (len > 8) { diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 522a61375e..fd457e1691 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -3,6 +3,7 @@ #include "ExPolygon.hpp" #include "Exception.hpp" #include "Flow.hpp" +#include "GCode/ExtrusionProcessor.hpp" #include "KDTreeIndirect.hpp" #include "Line.hpp" #include "Point.hpp" @@ -548,16 +549,21 @@ void PrintObject::calculate_overhanging_perimeters() m_print->set_status(89, _u8L("Calculating overhanging perimeters")); std::unordered_map> curled_lines; + std::unordered_map> unscaled_polygons_lines; for (const Layer *l : this->layers()) { - curled_lines[l->id()] = AABBTreeLines::LinesDistancer{l->curled_lines}; + curled_lines[l->id()] = AABBTreeLines::LinesDistancer{l->curled_lines}; + unscaled_polygons_lines[l->id()] = AABBTreeLines::LinesDistancer{to_unscaled_linesf(l->lslices)}; } for (Layer *l : this->layers()) { - for (const LayerRegion *layer_region : l->regions()) { + for (LayerRegion *layer_region : l->regions()) { if (regions_with_dynamic_overhangs.find(layer_region->m_region) == regions_with_dynamic_overhangs.end()) { continue; } - + ExPolygons prev_layer_polygon = l->lower_layer == nullptr ? ExPolygons() : l->lower_layer->lslices; + layer_region->m_perimeters = calculate_and_split_overhanging_extrusions(layer_region->m_perimeters, + unscaled_polygons_lines[l->id()], + curled_lines[l->id()]); } } From d0b8323bc81230365bcaca8d4b03a45c6899666c Mon Sep 17 00:00:00 2001 From: Pavel Date: Wed, 19 Jul 2023 17:27:02 +0200 Subject: [PATCH 031/136] Trying to fix the issue with segfault, so far unsucessfully. It seems that one ExtrusionCollection is moved and then accessed... --- src/libslic3r/GCode/ExtrusionProcessor.cpp | 15 ++++++++------- src/libslic3r/GCode/ExtrusionProcessor.hpp | 2 +- src/libslic3r/Print.cpp | 2 ++ src/libslic3r/PrintObject.cpp | 2 +- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/libslic3r/GCode/ExtrusionProcessor.cpp b/src/libslic3r/GCode/ExtrusionProcessor.cpp index 3f5d8f1d54..f5acf4749a 100644 --- a/src/libslic3r/GCode/ExtrusionProcessor.cpp +++ b/src/libslic3r/GCode/ExtrusionProcessor.cpp @@ -86,16 +86,17 @@ ExtrusionPaths calculate_and_split_overhanging_extrusions(const ExtrusionPath return result; }; -ExtrusionEntityCollection calculate_and_split_overhanging_extrusions(const ExtrusionEntityCollection &ecc, +ExtrusionEntityCollection calculate_and_split_overhanging_extrusions(const ExtrusionEntityCollection *ecc, const AABBTreeLines::LinesDistancer &unscaled_prev_layer, const AABBTreeLines::LinesDistancer &prev_layer_curled_lines) { - ExtrusionEntityCollection result; - result.no_sort = ecc.no_sort; - for (const auto *e : ecc.entities) { - if (auto *col = static_cast(e)) { - auto new_col = calculate_and_split_overhanging_extrusions(*col, unscaled_prev_layer, prev_layer_curled_lines); - result.append(new_col); + ExtrusionEntityCollection result{}; + result.no_sort = ecc->no_sort; + for (const auto *e : ecc->entities) { + if (e == nullptr) { + BOOST_LOG_TRIVIAL(debug) << "perimeters collections contain nullptr entities for some reason"; + } else if (auto *col = static_cast(e)) { + result.append(calculate_and_split_overhanging_extrusions(col, unscaled_prev_layer, prev_layer_curled_lines)); } else if (auto *loop = static_cast(e)) { ExtrusionLoop new_loop = *loop; new_loop.paths.clear(); diff --git a/src/libslic3r/GCode/ExtrusionProcessor.hpp b/src/libslic3r/GCode/ExtrusionProcessor.hpp index 0d60ad9851..c303618e46 100644 --- a/src/libslic3r/GCode/ExtrusionProcessor.hpp +++ b/src/libslic3r/GCode/ExtrusionProcessor.hpp @@ -218,7 +218,7 @@ ExtrusionPaths calculate_and_split_overhanging_extrusions(const ExtrusionPath const AABBTreeLines::LinesDistancer &unscaled_prev_layer, const AABBTreeLines::LinesDistancer &prev_layer_curled_lines); -ExtrusionEntityCollection calculate_and_split_overhanging_extrusions(const ExtrusionEntityCollection &ecc, +ExtrusionEntityCollection calculate_and_split_overhanging_extrusions(const ExtrusionEntityCollection *ecc, const AABBTreeLines::LinesDistancer &unscaled_prev_layer, const AABBTreeLines::LinesDistancer &prev_layer_curled_lines); diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 2dac942e9e..f9c74058eb 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -914,6 +914,8 @@ void Print::process() obj->generate_support_material(); for (PrintObject *obj : m_objects) obj->estimate_curled_extrusions(); + for (PrintObject *obj : m_objects) + obj->calculate_overhanging_perimeters(); if (this->set_started(psWipeTower)) { m_wipe_tower_data.clear(); m_tool_ordering.clear(); diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index fd457e1691..43e2937238 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -561,7 +561,7 @@ void PrintObject::calculate_overhanging_perimeters() continue; } ExPolygons prev_layer_polygon = l->lower_layer == nullptr ? ExPolygons() : l->lower_layer->lslices; - layer_region->m_perimeters = calculate_and_split_overhanging_extrusions(layer_region->m_perimeters, + layer_region->m_perimeters = calculate_and_split_overhanging_extrusions(&layer_region->m_perimeters, unscaled_polygons_lines[l->id()], curled_lines[l->id()]); } From 4ad4ae27549837aac2f12dd2926cfd5e54ef20d2 Mon Sep 17 00:00:00 2001 From: Pavel Date: Thu, 20 Jul 2023 10:36:29 +0200 Subject: [PATCH 032/136] fixed issue with segfaulting --- src/libslic3r/GCode/ExtrusionProcessor.cpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/GCode/ExtrusionProcessor.cpp b/src/libslic3r/GCode/ExtrusionProcessor.cpp index f5acf4749a..c7d56c3dbd 100644 --- a/src/libslic3r/GCode/ExtrusionProcessor.cpp +++ b/src/libslic3r/GCode/ExtrusionProcessor.cpp @@ -93,11 +93,9 @@ ExtrusionEntityCollection calculate_and_split_overhanging_extrusions(const Extru ExtrusionEntityCollection result{}; result.no_sort = ecc->no_sort; for (const auto *e : ecc->entities) { - if (e == nullptr) { - BOOST_LOG_TRIVIAL(debug) << "perimeters collections contain nullptr entities for some reason"; - } else if (auto *col = static_cast(e)) { + if (auto *col = dynamic_cast(e)) { result.append(calculate_and_split_overhanging_extrusions(col, unscaled_prev_layer, prev_layer_curled_lines)); - } else if (auto *loop = static_cast(e)) { + } else if (auto *loop = dynamic_cast(e)) { ExtrusionLoop new_loop = *loop; new_loop.paths.clear(); for (const ExtrusionPath &p : loop->paths) { @@ -105,7 +103,7 @@ ExtrusionEntityCollection calculate_and_split_overhanging_extrusions(const Extru new_loop.paths.insert(new_loop.paths.end(), paths.begin(), paths.end()); } result.append(new_loop); - } else if (auto *mp = static_cast(e)) { + } else if (auto *mp = dynamic_cast(e)) { ExtrusionMultiPath new_mp = *mp; new_mp.paths.clear(); for (const ExtrusionPath &p : mp->paths) { @@ -113,12 +111,12 @@ ExtrusionEntityCollection calculate_and_split_overhanging_extrusions(const Extru new_mp.paths.insert(new_mp.paths.end(), paths.begin(), paths.end()); } result.append(new_mp); - } else if (auto *op = static_cast(e)) { + } else if (auto *op = dynamic_cast(e)) { auto paths = calculate_and_split_overhanging_extrusions(*op, unscaled_prev_layer, prev_layer_curled_lines); for (const ExtrusionPath &p : paths) { result.append(ExtrusionPathOriented(p.polyline, p.attributes())); } - } else if (auto *p = static_cast(e)) { + } else if (auto *p = dynamic_cast(e)) { auto paths = calculate_and_split_overhanging_extrusions(*p, unscaled_prev_layer, prev_layer_curled_lines); result.append(paths); } else { From bf6a8dc0b08c679cfd7faeb49170cf60f62474f6 Mon Sep 17 00:00:00 2001 From: Pavel Date: Thu, 20 Jul 2023 10:59:14 +0200 Subject: [PATCH 033/136] updating the gcode extrude to support new overhang attributes --- src/libslic3r/GCode.cpp | 65 +++++++++++++++++----- src/libslic3r/GCode.hpp | 2 - src/libslic3r/GCode/ExtrusionProcessor.cpp | 7 ++- src/libslic3r/GCode/ExtrusionProcessor.hpp | 65 ++++++++++++---------- 4 files changed, 90 insertions(+), 49 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index eff430fdb0..d4a663c60c 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2047,10 +2047,6 @@ LayerResult GCodeGenerator::process_layer( } } - for (const ObjectLayerToPrint &layer_to_print : layers) { - m_extrusion_quality_estimator.prepare_for_new_layer(layer_to_print.object_layer); - } - // Extrude the skirt, brim, support, perimeters, infill ordered by the extruders. for (unsigned int extruder_id : layer_tools.extruders) { @@ -2182,8 +2178,6 @@ void GCodeGenerator::process_layer_single_object( const PrintObject &print_object = print_instance.print_object; const Print &print = *print_object.print(); - m_extrusion_quality_estimator.set_current_object(&print_object); - if (! print_wipe_extrusions && layer_to_print.support_layer != nullptr) if (const SupportLayer &support_layer = *layer_to_print.support_layer; ! support_layer.support_fills.entities.empty()) { ExtrusionRole role = support_layer.support_fills.role(); @@ -2781,10 +2775,7 @@ std::string GCodeGenerator::_extrude( ); } - bool variable_speed_or_fan_speed = false; - std::vector new_points{}; - if ((this->m_config.enable_dynamic_overhang_speeds || this->config().enable_dynamic_fan_speeds.get_at(m_writer.extruder()->id())) && - !this->on_first_layer() && path_attr.role.is_perimeter()) { + if (path_attr.overhang_attributes.has_value()) { std::vector> overhangs_with_speeds = {{100, ConfigOptionFloatOrPercent{speed, false}}}; if (this->m_config.enable_dynamic_overhang_speeds) { overhangs_with_speeds = {{0, m_config.overhang_speed_0}, @@ -2807,22 +2798,66 @@ std::string GCodeGenerator::_extrude( if (external_perim_reference_speed == 0) external_perim_reference_speed = m_volumetric_speed / path_attr.mm3_per_mm; if (m_config.max_volumetric_speed.value > 0) - external_perim_reference_speed = std::min(external_perim_reference_speed, m_config.max_volumetric_speed.value / path_attr.mm3_per_mm); + external_perim_reference_speed = std::min(external_perim_reference_speed, + m_config.max_volumetric_speed.value / path_attr.mm3_per_mm); if (EXTRUDER_CONFIG(filament_max_volumetric_speed) > 0) { external_perim_reference_speed = std::min(external_perim_reference_speed, EXTRUDER_CONFIG(filament_max_volumetric_speed) / path_attr.mm3_per_mm); } + ExtrusionProcessor::calculate_overhang_speed(); + + new_points = m_extrusion_quality_estimator.estimate_speed_from_extrusion_quality( - //FIXME convert estimate_speed_from_extrusion_quality() to smooth paths or move it before smooth path interpolation. - Points{}, - path_attr, overhangs_with_speeds, overhang_w_fan_speeds, - m_writer.extruder()->id(), external_perim_reference_speed, + // FIXME convert estimate_speed_from_extrusion_quality() to smooth paths or move it before smooth path interpolation. + Points{}, path_attr, overhangs_with_speeds, overhang_w_fan_speeds, m_writer.extruder()->id(), external_perim_reference_speed, speed); variable_speed_or_fan_speed = std::any_of(new_points.begin(), new_points.end(), [speed](const ProcessedPoint &p) { return p.speed != speed || p.fan_speed != 0; }); } + // bool variable_speed_or_fan_speed = false; + // std::vector new_points{}; + // if ((this->m_config.enable_dynamic_overhang_speeds || this->config().enable_dynamic_fan_speeds.get_at(m_writer.extruder()->id())) && + // !this->on_first_layer() && path_attr.role.is_perimeter()) { + // std::vector> overhangs_with_speeds = {{100, ConfigOptionFloatOrPercent{speed, false}}}; + // if (this->m_config.enable_dynamic_overhang_speeds) { + // overhangs_with_speeds = {{0, m_config.overhang_speed_0}, + // {25, m_config.overhang_speed_1}, + // {50, m_config.overhang_speed_2}, + // {75, m_config.overhang_speed_3}, + // {100, ConfigOptionFloatOrPercent{speed, false}}}; + // } + + // std::vector> overhang_w_fan_speeds = {{100, ConfigOptionInts{0}}}; + // if (this->m_config.enable_dynamic_fan_speeds.get_at(m_writer.extruder()->id())) { + // overhang_w_fan_speeds = {{0, m_config.overhang_fan_speed_0}, + // {25, m_config.overhang_fan_speed_1}, + // {50, m_config.overhang_fan_speed_2}, + // {75, m_config.overhang_fan_speed_3}, + // {100, ConfigOptionInts{0}}}; + // } + + // double external_perim_reference_speed = m_config.get_abs_value("external_perimeter_speed"); + // if (external_perim_reference_speed == 0) + // external_perim_reference_speed = m_volumetric_speed / path_attr.mm3_per_mm; + // if (m_config.max_volumetric_speed.value > 0) + // external_perim_reference_speed = std::min(external_perim_reference_speed, m_config.max_volumetric_speed.value / path_attr.mm3_per_mm); + // if (EXTRUDER_CONFIG(filament_max_volumetric_speed) > 0) { + // external_perim_reference_speed = std::min(external_perim_reference_speed, + // EXTRUDER_CONFIG(filament_max_volumetric_speed) / path_attr.mm3_per_mm); + // } + + // new_points = m_extrusion_quality_estimator.estimate_speed_from_extrusion_quality( + // //FIXME convert estimate_speed_from_extrusion_quality() to smooth paths or move it before smooth path interpolation. + // Points{}, + // path_attr, overhangs_with_speeds, overhang_w_fan_speeds, + // m_writer.extruder()->id(), external_perim_reference_speed, + // speed); + // variable_speed_or_fan_speed = std::any_of(new_points.begin(), new_points.end(), + // [speed](const ProcessedPoint &p) { return p.speed != speed || p.fan_speed != 0; }); + // } + double F = speed * 60; // convert mm/sec to mm/min // extrude arc or line diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 73e1a793fa..1c92874922 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -293,8 +293,6 @@ private: // Cache for custom seam enforcers/blockers for each layer. SeamPlacer m_seam_placer; - ExtrusionQualityEstimator m_extrusion_quality_estimator; - /* Origin of print coordinates expressed in unscaled G-code coordinates. This affects the input arguments supplied to the extrude*() and travel_to() methods. */ diff --git a/src/libslic3r/GCode/ExtrusionProcessor.cpp b/src/libslic3r/GCode/ExtrusionProcessor.cpp index c7d56c3dbd..2481efb1e2 100644 --- a/src/libslic3r/GCode/ExtrusionProcessor.cpp +++ b/src/libslic3r/GCode/ExtrusionProcessor.cpp @@ -1,6 +1,7 @@ #include "ExtrusionProcessor.hpp" +#include -namespace Slic3r { +namespace Slic3r { namespace ExtrusionProcessor { ExtrusionPaths calculate_and_split_overhanging_extrusions(const ExtrusionPath &path, const AABBTreeLines::LinesDistancer &unscaled_prev_layer, @@ -83,6 +84,8 @@ ExtrusionPaths calculate_and_split_overhanging_extrusions(const ExtrusionPath result.back().polyline.append(Point::new_scale(extended_points[i].position)); } + std::cout << "ExtrusionPath " << std::to_string(size_t(&path)) << " split to " << result.size() << " paths"; + return result; }; @@ -126,4 +129,4 @@ ExtrusionEntityCollection calculate_and_split_overhanging_extrusions(const Extru return result; }; -} // namespace Slic3r \ No newline at end of file +}} // namespace Slic3r::ExtrusionProcessor \ No newline at end of file diff --git a/src/libslic3r/GCode/ExtrusionProcessor.hpp b/src/libslic3r/GCode/ExtrusionProcessor.hpp index c303618e46..a9787f212f 100644 --- a/src/libslic3r/GCode/ExtrusionProcessor.hpp +++ b/src/libslic3r/GCode/ExtrusionProcessor.hpp @@ -28,13 +28,13 @@ #include #include -namespace Slic3r { +namespace Slic3r { namespace ExtrusionProcessor { struct ExtendedPoint { - Vec2d position; - float distance; - float curvature; + Vec2d position; + float distance; + float curvature; }; template @@ -48,27 +48,30 @@ std::vector estimate_points_properties(const POINTS using AABBScalar = typename AABBTreeLines::LinesDistancer::Scalar; if (input_points.empty()) return {}; - float boundary_offset = PREV_LAYER_BOUNDARY_OFFSET ? 0.5 * flow_width : 0.0f; - auto maybe_unscale = [](const P &p) { return SCALED_INPUT ? unscaled(p) : p.template cast(); }; + float boundary_offset = PREV_LAYER_BOUNDARY_OFFSET ? 0.5 * flow_width : 0.0f; + auto maybe_unscale = [](const P &p) { return SCALED_INPUT ? unscaled(p) : p.template cast(); }; std::vector points; points.reserve(input_points.size() * (ADD_INTERSECTIONS ? 1.5 : 1)); { ExtendedPoint start_point{maybe_unscale(input_points.front())}; - auto [distance, nearest_line, x] = unscaled_prev_layer.template distance_from_lines_extra(start_point.position.cast()); - start_point.distance = distance + boundary_offset; + auto [distance, nearest_line, + x] = unscaled_prev_layer.template distance_from_lines_extra(start_point.position.cast()); + start_point.distance = distance + boundary_offset; points.push_back(start_point); } for (size_t i = 1; i < input_points.size(); i++) { ExtendedPoint next_point{maybe_unscale(input_points[i])}; - auto [distance, nearest_line, x] = unscaled_prev_layer.template distance_from_lines_extra(next_point.position.cast()); - next_point.distance = distance + boundary_offset; + auto [distance, nearest_line, + x] = unscaled_prev_layer.template distance_from_lines_extra(next_point.position.cast()); + next_point.distance = distance + boundary_offset; if (ADD_INTERSECTIONS && ((points.back().distance > boundary_offset + EPSILON) != (next_point.distance > boundary_offset + EPSILON))) { - const ExtendedPoint &prev_point = points.back(); - auto intersections = unscaled_prev_layer.template intersections_with_line(L{prev_point.position.cast(), next_point.position.cast()}); + const ExtendedPoint &prev_point = points.back(); + auto intersections = unscaled_prev_layer.template intersections_with_line( + L{prev_point.position.cast(), next_point.position.cast()}); for (const auto &intersection : intersections) { ExtendedPoint p{}; p.position = intersection.first.template cast(); @@ -81,7 +84,7 @@ std::vector estimate_points_properties(const POINTS if (PREV_LAYER_BOUNDARY_OFFSET && ADD_INTERSECTIONS) { std::vector new_points; - new_points.reserve(points.size()*2); + new_points.reserve(points.size() * 2); new_points.push_back(points.front()); for (int point_idx = 0; point_idx < int(points.size()) - 1; ++point_idx) { const ExtendedPoint &curr = points[point_idx]; @@ -97,19 +100,21 @@ std::vector estimate_points_properties(const POINTS double t1 = std::max(a0, a1); if (t0 < 1.0) { - auto p0 = curr.position + t0 * (next.position - curr.position); - auto [p0_dist, p0_near_l, p0_x] = unscaled_prev_layer.template distance_from_lines_extra(p0.cast()); + auto p0 = curr.position + t0 * (next.position - curr.position); + auto [p0_dist, p0_near_l, + p0_x] = unscaled_prev_layer.template distance_from_lines_extra(p0.cast()); ExtendedPoint new_p{}; - new_p.position = p0; - new_p.distance = float(p0_dist + boundary_offset); + new_p.position = p0; + new_p.distance = float(p0_dist + boundary_offset); new_points.push_back(new_p); } if (t1 > 0.0) { - auto p1 = curr.position + t1 * (next.position - curr.position); - auto [p1_dist, p1_near_l, p1_x] = unscaled_prev_layer.template distance_from_lines_extra(p1.cast()); + auto p1 = curr.position + t1 * (next.position - curr.position); + auto [p1_dist, p1_near_l, + p1_x] = unscaled_prev_layer.template distance_from_lines_extra(p1.cast()); ExtendedPoint new_p{}; - new_p.position = p1; - new_p.distance = float(p1_dist + boundary_offset); + new_p.position = p1; + new_p.distance = float(p1_dist + boundary_offset); new_points.push_back(new_p); } } @@ -121,7 +126,7 @@ std::vector estimate_points_properties(const POINTS if (max_line_length > 0) { std::vector new_points; - new_points.reserve(points.size()*2); + new_points.reserve(points.size() * 2); { for (size_t i = 0; i + 1 < points.size(); i++) { const ExtendedPoint &curr = points[i]; @@ -135,8 +140,8 @@ std::vector estimate_points_properties(const POINTS auto [p_dist, p_near_l, p_x] = unscaled_prev_layer.template distance_from_lines_extra(pos.cast()); ExtendedPoint new_p{}; - new_p.position = pos; - new_p.distance = float(p_dist + boundary_offset); + new_p.position = pos; + new_p.distance = float(p_dist + boundary_offset); new_points.push_back(new_p); } } @@ -213,14 +218,14 @@ std::vector estimate_points_properties(const POINTS return points; } - ExtrusionPaths calculate_and_split_overhanging_extrusions(const ExtrusionPath &path, - const AABBTreeLines::LinesDistancer &unscaled_prev_layer, + const AABBTreeLines::LinesDistancer &unscaled_prev_layer, const AABBTreeLines::LinesDistancer &prev_layer_curled_lines); -ExtrusionEntityCollection calculate_and_split_overhanging_extrusions(const ExtrusionEntityCollection *ecc, - const AABBTreeLines::LinesDistancer &unscaled_prev_layer, - const AABBTreeLines::LinesDistancer &prev_layer_curled_lines); +ExtrusionEntityCollection calculate_and_split_overhanging_extrusions( + const ExtrusionEntityCollection *ecc, + const AABBTreeLines::LinesDistancer &unscaled_prev_layer, + const AABBTreeLines::LinesDistancer &prev_layer_curled_lines); struct ProcessedPoint { @@ -364,6 +369,6 @@ public: } }; -} // namespace Slic3r +}} // namespace Slic3r::ExtrusionProcessor #endif // slic3r_ExtrusionProcessor_hpp_ From a8d89140717020a52bff4e16feb4373c7781d042 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 26 Jul 2023 13:21:50 +0200 Subject: [PATCH 034/136] SPE-1784: New compressed (binary) gcode format integration Added Deflate compression using zlib --- src/libslic3r/GCode/GCodeBinarizer.cpp | 100 +++++++++++++++++++++++++ src/libslic3r/GCode/GCodeBinarizer.hpp | 1 + src/slic3r/GUI/GLCanvas3D.cpp | 2 +- src/slic3r/GUI/Plater.cpp | 11 --- 4 files changed, 102 insertions(+), 12 deletions(-) diff --git a/src/libslic3r/GCode/GCodeBinarizer.cpp b/src/libslic3r/GCode/GCodeBinarizer.cpp index cd46324174..e8bd9bffed 100644 --- a/src/libslic3r/GCode/GCodeBinarizer.cpp +++ b/src/libslic3r/GCode/GCodeBinarizer.cpp @@ -14,6 +14,7 @@ extern "C" { #include #include } +#include #include #include @@ -601,6 +602,56 @@ static bool compress(const std::vector& src, std::vector& dst, { switch (compression_type) { + case ECompressionType::Deflate: + { + dst.clear(); + + const size_t BUFSIZE = 2048; + std::vector temp_buffer(BUFSIZE); + + z_stream strm{}; + strm.next_in = const_cast(src.data()); + strm.avail_in = (uInt)src.size(); + strm.next_out = temp_buffer.data(); + strm.avail_out = BUFSIZE; + + const int level = Z_DEFAULT_COMPRESSION; + int res = deflateInit(&strm, level); + if (res != Z_OK) + return false; + + while (strm.avail_in > 0) { + res = deflate(&strm, Z_NO_FLUSH); + if (res != Z_OK) { + deflateEnd(&strm); + return false; + } + if (strm.avail_out == 0) { + dst.insert(dst.end(), temp_buffer.data(), temp_buffer.data() + BUFSIZE); + strm.next_out = temp_buffer.data(); + strm.avail_out = BUFSIZE; + } + } + + int deflate_res = Z_OK; + while (deflate_res == Z_OK) { + if (strm.avail_out == 0) { + dst.insert(dst.end(), temp_buffer.data(), temp_buffer.data() + BUFSIZE); + strm.next_out = temp_buffer.data(); + strm.avail_out = BUFSIZE; + } + deflate_res = deflate(&strm, Z_FINISH); + } + + if (deflate_res != Z_STREAM_END) { + deflateEnd(&strm); + return false; + } + + dst.insert(dst.end(), temp_buffer.data(), temp_buffer.data() + BUFSIZE - strm.avail_out); + deflateEnd(&strm); + break; + } case ECompressionType::Heatshrink_11_4: case ECompressionType::Heatshrink_12_4: { @@ -676,6 +727,55 @@ static bool uncompress(const std::vector& src, std::vector& ds { switch (compression_type) { + case ECompressionType::Deflate: + { + dst.clear(); + dst.reserve(uncompressed_size); + + const size_t BUFSIZE = 2048; + std::vector temp_buffer(BUFSIZE); + + z_stream strm{}; + strm.next_in = const_cast(src.data()); + strm.avail_in = (uInt)src.size(); + strm.next_out = temp_buffer.data(); + strm.avail_out = BUFSIZE; + int res = inflateInit(&strm); + if (res != Z_OK) + return false; + + while (strm.avail_in > 0) { + res = inflate(&strm, Z_NO_FLUSH); + if (res != Z_OK && res != Z_STREAM_END) { + inflateEnd(&strm); + return false; + } + if (strm.avail_out == 0) { + dst.insert(dst.end(), temp_buffer.data(), temp_buffer.data() + BUFSIZE); + strm.next_out = temp_buffer.data(); + strm.avail_out = BUFSIZE; + } + } + + int inflate_res = Z_OK; + while (inflate_res == Z_OK) { + if (strm.avail_out == 0) { + dst.insert(dst.end(), temp_buffer.data(), temp_buffer.data() + BUFSIZE); + strm.next_out = temp_buffer.data(); + strm.avail_out = BUFSIZE; + } + inflate_res = inflate(&strm, Z_FINISH); + } + + if (inflate_res != Z_STREAM_END) { + inflateEnd(&strm); + return false; + } + + dst.insert(dst.end(), temp_buffer.data(), temp_buffer.data() + BUFSIZE - strm.avail_out); + inflateEnd(&strm); + break; + } case ECompressionType::Heatshrink_11_4: case ECompressionType::Heatshrink_12_4: { diff --git a/src/libslic3r/GCode/GCodeBinarizer.hpp b/src/libslic3r/GCode/GCodeBinarizer.hpp index 236aee1c5f..05733cd51a 100644 --- a/src/libslic3r/GCode/GCodeBinarizer.hpp +++ b/src/libslic3r/GCode/GCodeBinarizer.hpp @@ -108,6 +108,7 @@ enum class EBlockType : uint16_t enum class ECompressionType : uint16_t { None, + Deflate, Heatshrink_11_4, Heatshrink_12_4, }; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index d075aeda3e..fccacd2c09 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -7885,7 +7885,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() ImGui::TableSetColumnIndex(0); imgui.text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, "File metadata compression"); ImGui::TableSetColumnIndex(1); - std::vector options = { "None", "heatshrink 11,4", "heatshrink 12,4" }; + std::vector options = { "None", "Deflate", "heatshrink 11,4", "heatshrink 12,4" }; int option_id = (int)binarizer_config.compression.file_metadata; if (imgui.combo(std::string("##file_metadata_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) binarizer_config.compression.file_metadata = (BinaryGCode::ECompressionType)option_id; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 2878e28a5a..355ff79f6a 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5431,17 +5431,6 @@ void Plater::reload_gcode_from_disk() } #if ENABLE_BINARIZED_GCODE -static bool is_valid_binary_gcode(const wxString& filename) -{ - FILE* file = boost::nowide::fopen(into_u8(filename).c_str(), "rb"); - if (file == nullptr) - return false; - - const bool ret = BinaryGCode::is_valid_binary_gcode(*file); - fclose(file); - return ret; -} - void Plater::convert_gcode_to_ascii() { // Ask user for a gcode file name. From f02e8e3438fe3f4b9a1b7b2d34eb21b03e5cd7dd Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 27 Jul 2023 09:04:44 +0200 Subject: [PATCH 035/136] Renamed namespace --- src/libslic3r/GCode.cpp | 10 +- src/libslic3r/GCode/GCodeBinarizer.cpp | 4 +- src/libslic3r/GCode/GCodeBinarizer.hpp | 6 +- src/libslic3r/GCode/GCodeProcessor.cpp | 140 ++++++++++++------------- src/libslic3r/GCode/GCodeProcessor.hpp | 10 +- src/libslic3r/GCode/Thumbnails.hpp | 10 +- src/slic3r/GUI/GLCanvas3D.cpp | 18 ++-- src/slic3r/GUI/Plater.cpp | 14 ++- 8 files changed, 110 insertions(+), 102 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index c9136af7a4..91771220ba 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -984,7 +984,7 @@ namespace DoExport { unsigned int initial_extruder_id, PrintStatistics &print_statistics, bool export_binary_data, - BinaryGCode::BinaryData &binary_data) + bgcode::BinaryData &binary_data) #else static std::string update_print_stats_and_format_filament_stats( const bool has_wipe_tower, @@ -1132,7 +1132,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // 1) generate the thumbnails // 2) collect the config data if (export_to_binary_gcode) { - BinaryGCode::BinaryData& binary_data = m_processor.get_binary_data(); + bgcode::BinaryData& binary_data = m_processor.get_binary_data(); // Unit tests or command line slicing may not define "thumbnails" or "thumbnails_format". // If "thumbnails_format" is not defined, export to PNG. @@ -1146,11 +1146,11 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato } // file data - binary_data.file_metadata.encoding_type = (uint16_t)BinaryGCode::EMetadataEncodingType::INI; + binary_data.file_metadata.encoding_type = (uint16_t)bgcode::EMetadataEncodingType::INI; binary_data.file_metadata.raw_data.emplace_back("Producer", std::string(SLIC3R_APP_NAME) + " " + std::string(SLIC3R_VERSION)); // config data - binary_data.slicer_metadata.encoding_type = (uint16_t)BinaryGCode::EMetadataEncodingType::INI; + binary_data.slicer_metadata.encoding_type = (uint16_t)bgcode::EMetadataEncodingType::INI; encode_full_config(print, binary_data.slicer_metadata.raw_data); // printer data @@ -1613,7 +1613,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato file.write(filament_stats_string_out); if (export_to_binary_gcode) { - BinaryGCode::BinaryData& binary_data = m_processor.get_binary_data(); + bgcode::BinaryData& binary_data = m_processor.get_binary_data(); if (print.m_print_statistics.total_toolchanges > 0) binary_data.print_metadata.raw_data.push_back({ "total toolchanges", std::to_string(print.m_print_statistics.total_toolchanges) }); } diff --git a/src/libslic3r/GCode/GCodeBinarizer.cpp b/src/libslic3r/GCode/GCodeBinarizer.cpp index e8bd9bffed..1a4748bad9 100644 --- a/src/libslic3r/GCode/GCodeBinarizer.cpp +++ b/src/libslic3r/GCode/GCodeBinarizer.cpp @@ -19,7 +19,7 @@ extern "C" { #include #include -namespace BinaryGCode { +namespace bgcode { static size_t g_checksum_max_cache_size = 65536; static constexpr const size_t MAX_GCODE_CACHE_SIZE = 65536; @@ -1959,5 +1959,5 @@ EResult from_binary_to_ascii(FILE& src_file, FILE& dst_file, bool verify_checksu } #endif // ENABLE_FILE_CONVERSION_INTERFACE -} // namespace BinaryGCode +} // namespace bgcode diff --git a/src/libslic3r/GCode/GCodeBinarizer.hpp b/src/libslic3r/GCode/GCodeBinarizer.hpp index 05733cd51a..2961fe548f 100644 --- a/src/libslic3r/GCode/GCodeBinarizer.hpp +++ b/src/libslic3r/GCode/GCodeBinarizer.hpp @@ -14,7 +14,7 @@ #include #include -namespace BinaryGCode { +namespace bgcode { static const std::array MAGIC{ 'G', 'C', 'D', 'E' }; static const uint32_t VERSION = 1; @@ -48,7 +48,7 @@ enum class EResult : uint16_t }; // Returns a string description of the given result -extern std::string translate_result(BinaryGCode::EResult result); +extern std::string translate_result(EResult result); enum class EChecksumType : uint16_t { @@ -377,6 +377,6 @@ extern EResult from_ascii_to_binary(FILE& src_file, FILE& dst_file); extern EResult from_binary_to_ascii(FILE& src_file, FILE& dst_file, bool verify_checksum); #endif // ENABLE_FILE_CONVERSION_INTERFACE -} // namespace BinaryGCode +} // namespace bgcode #endif // slic3r_GCode_GCodeBinarizer_hpp_ diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index e72745fac1..6c0fb8c2cf 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -70,7 +70,7 @@ const float GCodeProcessor::Wipe_Width = 0.05f; const float GCodeProcessor::Wipe_Height = 0.05f; #if ENABLE_BINARIZED_GCODE -BinaryGCode::BinarizerConfig GCodeProcessor::s_binarizer_config{}; +bgcode::BinarizerConfig GCodeProcessor::s_binarizer_config{}; #endif // ENABLE_BINARIZED #if ENABLE_GCODE_VIEWER_DATA_CHECKING @@ -1041,7 +1041,7 @@ void GCodeProcessor::process_file(const std::string& filename, std::function& item) { return item.first == "Producer"; }); if (producer_it != file_metadata_block.raw_data.end() && boost::starts_with(producer_it->second, std::string(SLIC3R_APP_NAME))) @@ -1174,15 +1174,15 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct m_producer = EProducer::Unknown; // read printer metadata block - res = BinaryGCode::read_next_block_header(*file, file_header, block_header, verify_checksum); - if (res != BinaryGCode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); - if ((BinaryGCode::EBlockType)block_header.type != BinaryGCode::EBlockType::PrinterMetadata) + res = bgcode::read_next_block_header(*file, file_header, block_header, verify_checksum); + if (res != bgcode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::translate_result(res) + "\n"); + if ((bgcode::EBlockType)block_header.type != bgcode::EBlockType::PrinterMetadata) throw Slic3r::RuntimeError("Unable to find printer metadata block in file: " + filename + "\n"); - BinaryGCode::PrinterMetadataBlock printer_metadata_block; + bgcode::PrinterMetadataBlock printer_metadata_block; res = printer_metadata_block.read_data(*file, file_header, block_header); - if (res != BinaryGCode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); + if (res != bgcode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::translate_result(res) + "\n"); #if ENABLE_BINARIZED_GCODE_DEBUG OutputDebugStringA("Printer metadata:\n"); for (const auto& [key, value] : printer_metadata_block.raw_data) { @@ -1194,25 +1194,25 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct #endif // ENABLE_BINARIZED_GCODE_DEBUG // read thumbnail blocks - res = BinaryGCode::read_next_block_header(*file, file_header, block_header, verify_checksum); - if (res != BinaryGCode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); + res = bgcode::read_next_block_header(*file, file_header, block_header, verify_checksum); + if (res != bgcode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::translate_result(res) + "\n"); - while ((BinaryGCode::EBlockType)block_header.type == BinaryGCode::EBlockType::Thumbnail) { - BinaryGCode::ThumbnailBlock thumbnail_block; + while ((bgcode::EBlockType)block_header.type == bgcode::EBlockType::Thumbnail) { + bgcode::ThumbnailBlock thumbnail_block; res = thumbnail_block.read_data(*file, file_header, block_header); - if (res != BinaryGCode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); + if (res != bgcode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::translate_result(res) + "\n"); #if ENABLE_BINARIZED_GCODE_DEBUG if (thumbnail_block.data.size() > 0) { - auto format_filename = [](const std::string& stem, const BinaryGCode::ThumbnailBlock& block) { + auto format_filename = [](const std::string& stem, const bgcode::ThumbnailBlock& block) { std::string ret = stem + "_" + std::to_string(block.width) + "x" + std::to_string(block.height); - switch ((BinaryGCode::EThumbnailFormat)block.format) + switch ((bgcode::EThumbnailFormat)block.format) { - case BinaryGCode::EThumbnailFormat::PNG: { ret += ".png"; break; } - case BinaryGCode::EThumbnailFormat::JPG: { ret += ".jpg"; break; } - case BinaryGCode::EThumbnailFormat::QOI: { ret += ".qoi"; break; } + case bgcode::EThumbnailFormat::PNG: { ret += ".png"; break; } + case bgcode::EThumbnailFormat::JPG: { ret += ".jpg"; break; } + case bgcode::EThumbnailFormat::QOI: { ret += ".qoi"; break; } } return ret; }; @@ -1228,18 +1228,18 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct } #endif // ENABLE_BINARIZED_GCODE_DEBUG - res = BinaryGCode::read_next_block_header(*file, file_header, block_header, verify_checksum); - if (res != BinaryGCode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); + res = bgcode::read_next_block_header(*file, file_header, block_header, verify_checksum); + if (res != bgcode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::translate_result(res) + "\n"); } // read print metadata block - if ((BinaryGCode::EBlockType)block_header.type != BinaryGCode::EBlockType::PrintMetadata) + if ((bgcode::EBlockType)block_header.type != bgcode::EBlockType::PrintMetadata) throw Slic3r::RuntimeError("Unable to find print metadata block in file: " + filename + "\n"); - BinaryGCode::PrintMetadataBlock print_metadata_block; + bgcode::PrintMetadataBlock print_metadata_block; res = print_metadata_block.read_data(*file, file_header, block_header); - if (res != BinaryGCode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); + if (res != bgcode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::translate_result(res) + "\n"); #if ENABLE_BINARIZED_GCODE_DEBUG OutputDebugStringA("Print metadata:\n"); for (const auto& [key, value] : print_metadata_block.raw_data) { @@ -1251,15 +1251,15 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct #endif // ENABLE_BINARIZED_GCODE_DEBUG // read slicer metadata block - res = BinaryGCode::read_next_block_header(*file, file_header, block_header, verify_checksum); - if (res != BinaryGCode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); - if ((BinaryGCode::EBlockType)block_header.type != BinaryGCode::EBlockType::SlicerMetadata) + res = bgcode::read_next_block_header(*file, file_header, block_header, verify_checksum); + if (res != bgcode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::translate_result(res) + "\n"); + if ((bgcode::EBlockType)block_header.type != bgcode::EBlockType::SlicerMetadata) throw Slic3r::RuntimeError("Unable to find slicer metadata block in file: " + filename + "\n"); - BinaryGCode::SlicerMetadataBlock slicer_metadata_block; + bgcode::SlicerMetadataBlock slicer_metadata_block; res = slicer_metadata_block.read_data(*file, file_header, block_header); - if (res != BinaryGCode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); + if (res != bgcode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::translate_result(res) + "\n"); #if ENABLE_BINARIZED_GCODE_DEBUG OutputDebugStringA("Slicer metadata:\n"); for (const auto& [key, value] : slicer_metadata_block.raw_data) { @@ -1286,16 +1286,16 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct initialize_result_moves(); // read gcodes block - res = BinaryGCode::read_next_block_header(*file, file_header, block_header, verify_checksum); - if (res != BinaryGCode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); - if ((BinaryGCode::EBlockType)block_header.type != BinaryGCode::EBlockType::GCode) + res = bgcode::read_next_block_header(*file, file_header, block_header, verify_checksum); + if (res != bgcode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::translate_result(res) + "\n"); + if ((bgcode::EBlockType)block_header.type != bgcode::EBlockType::GCode) throw Slic3r::RuntimeError("Unable to find gcode block in file: " + filename + "\n"); - while ((BinaryGCode::EBlockType)block_header.type == BinaryGCode::EBlockType::GCode) { - BinaryGCode::GCodeBlock block; + while ((bgcode::EBlockType)block_header.type == bgcode::EBlockType::GCode) { + bgcode::GCodeBlock block; res = block.read_data(*file, file_header, block_header); - if (res != BinaryGCode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); + if (res != bgcode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::translate_result(res) + "\n"); // TODO: Update m_result.lines_ends @@ -1306,9 +1306,9 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct if (ftell(file) == file_size) break; - res = BinaryGCode::read_next_block_header(*file, file_header, block_header, verify_checksum); - if (res != BinaryGCode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + BinaryGCode::translate_result(res) + "\n"); + res = bgcode::read_next_block_header(*file, file_header, block_header, verify_checksum); + if (res != bgcode::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::translate_result(res) + "\n"); } // Don't post-process the G-code to update time stamps. @@ -3696,7 +3696,7 @@ void GCodeProcessor::post_process() }; // update binary data - BinaryGCode::BinaryData& binary_data = m_binarizer.get_binary_data(); + bgcode::BinaryData& binary_data = m_binarizer.get_binary_data(); binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedMm, stringify(filament_mm) }); binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedCm3, stringify(filament_cm3) }); binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedG, stringify(filament_g) }); @@ -3720,8 +3720,8 @@ void GCodeProcessor::post_process() } } - const BinaryGCode::EResult res = m_binarizer.initialize(*out.f, s_binarizer_config); - if (res != BinaryGCode::EResult::Success) + const bgcode::EResult res = m_binarizer.initialize(*out.f, s_binarizer_config); + if (res != bgcode::EResult::Success) throw Slic3r::RuntimeError(std::string("Unable to initialize the gcode binarizer.\n")); } #endif // ENABLE_BINARIZED_GCODE @@ -3843,12 +3843,12 @@ void GCodeProcessor::post_process() size_t m_out_file_pos{ 0 }; #if ENABLE_BINARIZED_GCODE - BinaryGCode::Binarizer& m_binarizer; + bgcode::Binarizer& m_binarizer; #endif // ENABLE_BINARIZED_GCODE public: #if ENABLE_BINARIZED_GCODE - ExportLines(BinaryGCode::Binarizer& binarizer, EWriteType type, TimeMachine& machine) + ExportLines(bgcode::Binarizer& binarizer, EWriteType type, TimeMachine& machine) #ifndef NDEBUG : m_statistics(*this), m_binarizer(binarizer), m_write_type(type), m_machine(machine) {} #else @@ -3974,8 +3974,8 @@ void GCodeProcessor::post_process() #if ENABLE_BINARIZED_GCODE if (m_binarizer.is_enabled()) { - BinaryGCode::EResult res = m_binarizer.append_gcode(out_string); - if (res != BinaryGCode::EResult::Success) + bgcode::EResult res = m_binarizer.append_gcode(out_string); + if (res != bgcode::EResult::Success) throw Slic3r::RuntimeError(std::string("Error while sending gcode to the binarizer.\n")); } else @@ -4011,8 +4011,8 @@ void GCodeProcessor::post_process() #if ENABLE_BINARIZED_GCODE if (m_binarizer.is_enabled()) { - BinaryGCode::EResult res = m_binarizer.append_gcode(out_string); - if (res != BinaryGCode::EResult::Success) + bgcode::EResult res = m_binarizer.append_gcode(out_string); + if (res != bgcode::EResult::Success) throw Slic3r::RuntimeError(std::string("Error while sending gcode to the binarizer.\n")); } else @@ -4391,8 +4391,8 @@ void GCodeProcessor::post_process() #if ENABLE_BINARIZED_GCODE if (m_binarizer.is_enabled()) { - const BinaryGCode::EResult res = m_binarizer.finalize(); - if (res != BinaryGCode::EResult::Success) + const bgcode::EResult res = m_binarizer.finalize(); + if (res != bgcode::EResult::Success) throw Slic3r::RuntimeError(std::string("Error while finalizing the gcode binarizer.\n")); } #endif // ENABLE_BINARIZED_GCODE diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 333f3295e6..b3390ed1c6 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -528,14 +528,14 @@ namespace Slic3r { #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING #if ENABLE_BINARIZED_GCODE_DEBUG_WINDOW - static BinaryGCode::BinarizerConfig& get_binarizer_config() { return s_binarizer_config; } + static bgcode::BinarizerConfig& get_binarizer_config() { return s_binarizer_config; } #endif // ENABLE_BINARIZED_GCODE_DEBUG_WINDOW private: GCodeReader m_parser; #if ENABLE_BINARIZED_GCODE - BinaryGCode::Binarizer m_binarizer; - static BinaryGCode::BinarizerConfig s_binarizer_config; + bgcode::Binarizer m_binarizer; + static bgcode::BinarizerConfig s_binarizer_config; #endif // ENABLE_BINARIZED_GCODE EUnits m_units; @@ -635,8 +635,8 @@ namespace Slic3r { void apply_config(const PrintConfig& config); void set_print(Print* print) { m_print = print; } #if ENABLE_BINARIZED_GCODE - BinaryGCode::BinaryData& get_binary_data() { return m_binarizer.get_binary_data(); } - const BinaryGCode::BinaryData& get_binary_data() const { return m_binarizer.get_binary_data(); } + bgcode::BinaryData& get_binary_data() { return m_binarizer.get_binary_data(); } + const bgcode::BinaryData& get_binary_data() const { return m_binarizer.get_binary_data(); } #endif // ENABLE_BINARIZED_GCODE void enable_stealth_time_estimator(bool enabled); diff --git a/src/libslic3r/GCode/Thumbnails.hpp b/src/libslic3r/GCode/Thumbnails.hpp index b2b5d60cb1..6fd28af333 100644 --- a/src/libslic3r/GCode/Thumbnails.hpp +++ b/src/libslic3r/GCode/Thumbnails.hpp @@ -60,7 +60,7 @@ inline void export_thumbnails_to_file(ThumbnailsGeneratorCallback &thumbnail_cb, #if ENABLE_BINARIZED_GCODE template -inline void generate_binary_thumbnails(ThumbnailsGeneratorCallback& thumbnail_cb, std::vector& out_thumbnails, +inline void generate_binary_thumbnails(ThumbnailsGeneratorCallback& thumbnail_cb, std::vector& out_thumbnails, const std::vector& sizes, GCodeThumbnailsFormat format, ThrowIfCanceledCallback throw_if_canceled) { out_thumbnails.clear(); @@ -70,13 +70,13 @@ inline void generate_binary_thumbnails(ThumbnailsGeneratorCallback& thumbnail_cb if (data.is_valid()) { auto compressed = compress_thumbnail(data, format); if (compressed->data != nullptr && compressed->size > 0) { - BinaryGCode::ThumbnailBlock& block = out_thumbnails.emplace_back(BinaryGCode::ThumbnailBlock()); + bgcode::ThumbnailBlock& block = out_thumbnails.emplace_back(bgcode::ThumbnailBlock()); block.width = (uint16_t)data.width; block.height = (uint16_t)data.height; switch (format) { - case GCodeThumbnailsFormat::PNG: { block.format = (uint16_t)BinaryGCode::EThumbnailFormat::PNG; break; } - case GCodeThumbnailsFormat::JPG: { block.format = (uint16_t)BinaryGCode::EThumbnailFormat::JPG; break; } - case GCodeThumbnailsFormat::QOI: { block.format = (uint16_t)BinaryGCode::EThumbnailFormat::QOI; break; } + case GCodeThumbnailsFormat::PNG: { block.format = (uint16_t)bgcode::EThumbnailFormat::PNG; break; } + case GCodeThumbnailsFormat::JPG: { block.format = (uint16_t)bgcode::EThumbnailFormat::JPG; break; } + case GCodeThumbnailsFormat::QOI: { block.format = (uint16_t)bgcode::EThumbnailFormat::QOI; break; } } block.data.resize(compressed->size); memcpy(block.data.data(), compressed->data, compressed->size); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index fccacd2c09..be81ea7f1f 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -7874,7 +7874,7 @@ void GLCanvas3D::GizmoHighlighter::blink() #if ENABLE_BINARIZED_GCODE_DEBUG_WINDOW void GLCanvas3D::show_binary_gcode_debug_window() { - BinaryGCode::BinarizerConfig& binarizer_config = GCodeProcessor::get_binarizer_config(); + bgcode::BinarizerConfig& binarizer_config = GCodeProcessor::get_binarizer_config(); ImGuiWrapper& imgui = *wxGetApp().imgui(); imgui.begin(std::string("Binary GCode"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); @@ -7888,7 +7888,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() std::vector options = { "None", "Deflate", "heatshrink 11,4", "heatshrink 12,4" }; int option_id = (int)binarizer_config.compression.file_metadata; if (imgui.combo(std::string("##file_metadata_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.compression.file_metadata = (BinaryGCode::ECompressionType)option_id; + binarizer_config.compression.file_metadata = (bgcode::ECompressionType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -7896,7 +7896,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() ImGui::TableSetColumnIndex(1); option_id = (int)binarizer_config.compression.printer_metadata; if (imgui.combo(std::string("##printer_metadata_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.compression.printer_metadata = (BinaryGCode::ECompressionType)option_id; + binarizer_config.compression.printer_metadata = (bgcode::ECompressionType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -7904,7 +7904,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() ImGui::TableSetColumnIndex(1); option_id = (int)binarizer_config.compression.print_metadata; if (imgui.combo(std::string("##print_metadata_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.compression.print_metadata = (BinaryGCode::ECompressionType)option_id; + binarizer_config.compression.print_metadata = (bgcode::ECompressionType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -7912,7 +7912,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() ImGui::TableSetColumnIndex(1); option_id = (int)binarizer_config.compression.slicer_metadata; if (imgui.combo(std::string("##slicer_metadata_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.compression.slicer_metadata = (BinaryGCode::ECompressionType)option_id; + binarizer_config.compression.slicer_metadata = (bgcode::ECompressionType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -7920,7 +7920,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() ImGui::TableSetColumnIndex(1); option_id = (int)binarizer_config.compression.gcode; if (imgui.combo(std::string("##gcode_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.compression.gcode = (BinaryGCode::ECompressionType)option_id; + binarizer_config.compression.gcode = (bgcode::ECompressionType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -7929,7 +7929,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() options = { "None", "MeatPack", "MeatPack Comments" }; option_id = (int)binarizer_config.gcode_encoding; if (imgui.combo(std::string("##gcode_encoding"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.gcode_encoding = (BinaryGCode::EGCodeEncodingType)option_id; + binarizer_config.gcode_encoding = (bgcode::EGCodeEncodingType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -7938,7 +7938,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() options = { "INI" }; option_id = (int)binarizer_config.metadata_encoding; if (imgui.combo(std::string("##metadata_encoding"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.metadata_encoding = (BinaryGCode::EMetadataEncodingType)option_id; + binarizer_config.metadata_encoding = (bgcode::EMetadataEncodingType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -7947,7 +7947,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() options = { "None", "CRC32" }; option_id = (int)binarizer_config.checksum; if (imgui.combo(std::string("##4"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.checksum = (BinaryGCode::EChecksumType)option_id; + binarizer_config.checksum = (bgcode::EChecksumType)option_id; ImGui::EndTable(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 355ff79f6a..22076aadff 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5442,6 +5442,9 @@ void Plater::convert_gcode_to_ascii() public: explicit ScopedFile(FILE* file) : m_file(file) {} ~ScopedFile() { if (m_file != nullptr) fclose(m_file); } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + void unscope() { m_file = nullptr; } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ private: FILE* m_file{ nullptr }; }; @@ -5471,10 +5474,15 @@ void Plater::convert_gcode_to_ascii() // Perform conversion { wxBusyCursor busy; - BinaryGCode::EResult res = BinaryGCode::from_binary_to_ascii(*in_file, *out_file, true); - if (res != BinaryGCode::EResult::Success) { - MessageDialog msg_dlg(this, _L(BinaryGCode::translate_result(res)), _L("Error converting gcode file"), wxICON_INFORMATION | wxOK); + bgcode::EResult res = bgcode::from_binary_to_ascii(*in_file, *out_file, true); + if (res != bgcode::EResult::Success) { + MessageDialog msg_dlg(this, _L(bgcode::translate_result(res)), _L("Error converting gcode file"), wxICON_INFORMATION | wxOK); msg_dlg.ShowModal(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + scoped_out_file.unscope(); + fclose(out_file); + boost::nowide::remove(output_file.c_str()); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ return; } } From fa0986c0c5548282a955644d40b5f170604dc913 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 27 Jul 2023 09:17:35 +0200 Subject: [PATCH 036/136] Rebased pm_arcwelder_overhangs_port to vb_arc_welder --- src/libslic3r/ExtrusionEntity.hpp | 4 +- src/libslic3r/GCode.cpp | 197 ++++++--------------- src/libslic3r/GCode/ExtrusionProcessor.cpp | 93 ++++++++-- src/libslic3r/GCode/ExtrusionProcessor.hpp | 148 +--------------- src/libslic3r/PrintObject.cpp | 7 +- src/libslic3r/SupportSpotsGenerator.cpp | 48 ++--- 6 files changed, 179 insertions(+), 318 deletions(-) diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp index 5893853364..3930d8f65b 100644 --- a/src/libslic3r/ExtrusionEntity.hpp +++ b/src/libslic3r/ExtrusionEntity.hpp @@ -103,7 +103,8 @@ inline bool operator==(const ExtrusionFlow &lhs, const ExtrusionFlow &rhs) } struct OverhangAttributes { - float max_distance_from_prev_layer; + float start_distance_from_prev_layer; + float end_distance_from_prev_layer; float proximity_to_curled_lines; //value between 0 and 1 }; @@ -169,6 +170,7 @@ public: double mm3_per_mm() const { return m_attributes.mm3_per_mm; } // Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm. double min_mm3_per_mm() const override { return m_attributes.mm3_per_mm; } + std::optional& overhang_attributes_mutable() { return m_attributes.overhang_attributes; } // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width. // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps. diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index d4a663c60c..0504645520 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -2775,25 +2776,8 @@ std::string GCodeGenerator::_extrude( ); } + std::pair dynamic_speed_and_fan_speed{-1, -1}; if (path_attr.overhang_attributes.has_value()) { - std::vector> overhangs_with_speeds = {{100, ConfigOptionFloatOrPercent{speed, false}}}; - if (this->m_config.enable_dynamic_overhang_speeds) { - overhangs_with_speeds = {{0, m_config.overhang_speed_0}, - {25, m_config.overhang_speed_1}, - {50, m_config.overhang_speed_2}, - {75, m_config.overhang_speed_3}, - {100, ConfigOptionFloatOrPercent{speed, false}}}; - } - - std::vector> overhang_w_fan_speeds = {{100, ConfigOptionInts{0}}}; - if (this->m_config.enable_dynamic_fan_speeds.get_at(m_writer.extruder()->id())) { - overhang_w_fan_speeds = {{0, m_config.overhang_fan_speed_0}, - {25, m_config.overhang_fan_speed_1}, - {50, m_config.overhang_fan_speed_2}, - {75, m_config.overhang_fan_speed_3}, - {100, ConfigOptionInts{0}}}; - } - double external_perim_reference_speed = m_config.get_abs_value("external_perimeter_speed"); if (external_perim_reference_speed == 0) external_perim_reference_speed = m_volumetric_speed / path_attr.mm3_per_mm; @@ -2805,58 +2789,13 @@ std::string GCodeGenerator::_extrude( EXTRUDER_CONFIG(filament_max_volumetric_speed) / path_attr.mm3_per_mm); } - ExtrusionProcessor::calculate_overhang_speed(); - - - new_points = m_extrusion_quality_estimator.estimate_speed_from_extrusion_quality( - // FIXME convert estimate_speed_from_extrusion_quality() to smooth paths or move it before smooth path interpolation. - Points{}, path_attr, overhangs_with_speeds, overhang_w_fan_speeds, m_writer.extruder()->id(), external_perim_reference_speed, - speed); - variable_speed_or_fan_speed = std::any_of(new_points.begin(), new_points.end(), - [speed](const ProcessedPoint &p) { return p.speed != speed || p.fan_speed != 0; }); + dynamic_speed_and_fan_speed = ExtrusionProcessor::calculate_overhang_speed(path_attr, this->m_config, m_writer.extruder()->id(), + external_perim_reference_speed, speed); } - // bool variable_speed_or_fan_speed = false; - // std::vector new_points{}; - // if ((this->m_config.enable_dynamic_overhang_speeds || this->config().enable_dynamic_fan_speeds.get_at(m_writer.extruder()->id())) && - // !this->on_first_layer() && path_attr.role.is_perimeter()) { - // std::vector> overhangs_with_speeds = {{100, ConfigOptionFloatOrPercent{speed, false}}}; - // if (this->m_config.enable_dynamic_overhang_speeds) { - // overhangs_with_speeds = {{0, m_config.overhang_speed_0}, - // {25, m_config.overhang_speed_1}, - // {50, m_config.overhang_speed_2}, - // {75, m_config.overhang_speed_3}, - // {100, ConfigOptionFloatOrPercent{speed, false}}}; - // } - - // std::vector> overhang_w_fan_speeds = {{100, ConfigOptionInts{0}}}; - // if (this->m_config.enable_dynamic_fan_speeds.get_at(m_writer.extruder()->id())) { - // overhang_w_fan_speeds = {{0, m_config.overhang_fan_speed_0}, - // {25, m_config.overhang_fan_speed_1}, - // {50, m_config.overhang_fan_speed_2}, - // {75, m_config.overhang_fan_speed_3}, - // {100, ConfigOptionInts{0}}}; - // } - - // double external_perim_reference_speed = m_config.get_abs_value("external_perimeter_speed"); - // if (external_perim_reference_speed == 0) - // external_perim_reference_speed = m_volumetric_speed / path_attr.mm3_per_mm; - // if (m_config.max_volumetric_speed.value > 0) - // external_perim_reference_speed = std::min(external_perim_reference_speed, m_config.max_volumetric_speed.value / path_attr.mm3_per_mm); - // if (EXTRUDER_CONFIG(filament_max_volumetric_speed) > 0) { - // external_perim_reference_speed = std::min(external_perim_reference_speed, - // EXTRUDER_CONFIG(filament_max_volumetric_speed) / path_attr.mm3_per_mm); - // } - - // new_points = m_extrusion_quality_estimator.estimate_speed_from_extrusion_quality( - // //FIXME convert estimate_speed_from_extrusion_quality() to smooth paths or move it before smooth path interpolation. - // Points{}, - // path_attr, overhangs_with_speeds, overhang_w_fan_speeds, - // m_writer.extruder()->id(), external_perim_reference_speed, - // speed); - // variable_speed_or_fan_speed = std::any_of(new_points.begin(), new_points.end(), - // [speed](const ProcessedPoint &p) { return p.speed != speed || p.fan_speed != 0; }); - // } + if (dynamic_speed_and_fan_speed.first > -1) { + speed = dynamic_speed_and_fan_speed.first; + } double F = speed * 60; // convert mm/sec to mm/min @@ -2919,86 +2858,62 @@ std::string GCodeGenerator::_extrude( cooling_marker_setspeed_comments += ";_EXTERNAL_PERIMETER"; } - //FIXME Variable speed on overhangs is inactive until the code is adapted to smooth path. - if (! variable_speed_or_fan_speed) { - // F is mm per minute. - gcode += m_writer.set_speed(F, "", cooling_marker_setspeed_comments); - double path_length = 0.; - std::string comment; - if (m_config.gcode_comments) { - comment = description; - comment += description_bridge; - } - Vec2d prev = this->point_to_gcode_quantized(path.front().point); - auto it = path.begin(); - auto end = path.end(); - const bool emit_radius = m_config.arc_fitting == ArcFittingType::EmitRadius; - for (++ it; it != end; ++ it) { - Vec2d p = this->point_to_gcode_quantized(it->point); - if (it->radius == 0) { - // Extrude line segment. - if (const double line_length = (p - prev).norm(); line_length > 0) { - path_length += line_length; - gcode += m_writer.extrude_to_xy(p, e_per_mm * line_length, comment); - } - } else { - // Extrude an arc. - assert(m_config.arc_fitting == ArcFittingType::EmitCenter || - m_config.arc_fitting == ArcFittingType::EmitRadius); - double radius = unscaled(it->radius); - if (emit_radius) - // Only quantize radius if emitting it directly into G-code. Otherwise use the exact radius for calculating the IJ values. - radius = GCodeFormatter::quantize_xyzf(radius); - Vec2d ij; - if (! emit_radius) { - // Calculate quantized IJ circle center offset. - Vec2d center_raw = Geometry::ArcWelder::arc_center(prev.cast(), p.cast(), double(radius), it->ccw()) - prev; - ij = GCodeFormatter::quantize(center_raw); - } - double angle = Geometry::ArcWelder::arc_angle(prev.cast(), p.cast(), double(radius)); - assert(angle > 0); - const double line_length = angle * std::abs(radius); + // F is mm per minute. + gcode += m_writer.set_speed(F, "", cooling_marker_setspeed_comments); + if (dynamic_speed_and_fan_speed.second >= 0) + gcode += ";_SET_FAN_SPEED" + std::to_string(int(dynamic_speed_and_fan_speed.second)) + "\n"; + double path_length = 0.; + std::string comment; + if (m_config.gcode_comments) { + comment = description; + comment += description_bridge; + } + Vec2d prev = this->point_to_gcode_quantized(path.front().point); + auto it = path.begin(); + auto end = path.end(); + const bool emit_radius = m_config.arc_fitting == ArcFittingType::EmitRadius; + for (++ it; it != end; ++ it) { + Vec2d p = this->point_to_gcode_quantized(it->point); + if (it->radius == 0) { + // Extrude line segment. + if (const double line_length = (p - prev).norm(); line_length > 0) { path_length += line_length; - const double dE = e_per_mm * line_length; - assert(dE > 0); - gcode += emit_radius ? - m_writer.extrude_to_xy_G2G3R(p, radius, it->ccw(), dE, comment) : - m_writer.extrude_to_xy_G2G3IJ(p, ij, it->ccw(), dE, comment); + gcode += m_writer.extrude_to_xy(p, e_per_mm * line_length, comment); } - prev = p; - } - } else { - std::string marked_comment; - if (m_config.gcode_comments) { - marked_comment = description; - marked_comment += description_bridge; - } - double last_set_speed = new_points[0].speed * 60.0; - double last_set_fan_speed = new_points[0].fan_speed; - gcode += m_writer.set_speed(last_set_speed, "", cooling_marker_setspeed_comments); - gcode += "\n;_SET_FAN_SPEED" + std::to_string(int(last_set_fan_speed)) + "\n"; - Vec2d prev = this->point_to_gcode_quantized(new_points[0].p); - for (size_t i = 1; i < new_points.size(); i++) { - const ProcessedPoint &processed_point = new_points[i]; - Vec2d p = this->point_to_gcode_quantized(processed_point.p); - const double line_length = (p - prev).norm(); - gcode += m_writer.extrude_to_xy(p, e_per_mm * line_length, marked_comment); - prev = p; - double new_speed = processed_point.speed * 60.0; - if (last_set_speed != new_speed) { - gcode += m_writer.set_speed(new_speed, "", cooling_marker_setspeed_comments); - last_set_speed = new_speed; - } - if (last_set_fan_speed != processed_point.fan_speed) { - last_set_fan_speed = processed_point.fan_speed; - gcode += "\n;_SET_FAN_SPEED" + std::to_string(int(last_set_fan_speed)) + "\n"; + } else { + // Extrude an arc. + assert(m_config.arc_fitting == ArcFittingType::EmitCenter || + m_config.arc_fitting == ArcFittingType::EmitRadius); + double radius = unscaled(it->radius); + if (emit_radius) + // Only quantize radius if emitting it directly into G-code. Otherwise use the exact radius for calculating the IJ values. + radius = GCodeFormatter::quantize_xyzf(radius); + Vec2d ij; + if (! emit_radius) { + // Calculate quantized IJ circle center offset. + Vec2d center_raw = Geometry::ArcWelder::arc_center(prev.cast(), p.cast(), double(radius), it->ccw()) - prev; + ij = GCodeFormatter::quantize(center_raw); } + double angle = Geometry::ArcWelder::arc_angle(prev.cast(), p.cast(), double(radius)); + assert(angle > 0); + const double line_length = angle * std::abs(radius); + path_length += line_length; + const double dE = e_per_mm * line_length; + assert(dE > 0); + gcode += emit_radius ? + m_writer.extrude_to_xy_G2G3R(p, radius, it->ccw(), dE, comment) : + m_writer.extrude_to_xy_G2G3IJ(p, ij, it->ccw(), dE, comment); } - gcode += "\n;_RESET_FAN_SPEED\n"; + prev = p; } if (m_enable_cooling_markers) - gcode += path_attr.role.is_bridge() ? ";_BRIDGE_FAN_END\n" : ";_EXTRUDE_END\n"; + gcode += path_attr.role.is_bridge() ? ";_BRIDGE_FAN_END" : ";_EXTRUDE_END"; + + if (dynamic_speed_and_fan_speed.second >= 0) + gcode += ";_RESET_FAN_SPEED"; + + gcode += "\n"; this->set_last_pos(path.back().point); return gcode; diff --git a/src/libslic3r/GCode/ExtrusionProcessor.cpp b/src/libslic3r/GCode/ExtrusionProcessor.cpp index 2481efb1e2..3f6aa5f5ed 100644 --- a/src/libslic3r/GCode/ExtrusionProcessor.cpp +++ b/src/libslic3r/GCode/ExtrusionProcessor.cpp @@ -68,24 +68,27 @@ ExtrusionPaths calculate_and_split_overhanging_extrusions(const ExtrusionPath ExtrusionPaths result; ExtrusionAttributes new_attrs = path.attributes(); - new_attrs.overhang_attributes = std::optional({calculated_distances[0].first, calculated_distances[0].second}); + new_attrs.overhang_attributes = std::optional( + {calculated_distances[0].first, calculated_distances[0].first, calculated_distances[0].second}); result.emplace_back(new_attrs); result.back().polyline.append(Point::new_scale(extended_points[0].position)); + size_t sequence_start_index = 0; for (size_t i = 1; i < extended_points.size(); i++) { - if (std::abs(calculated_distances[i].first - calculated_distances[i - 1].first) < path.width() * 0.1 && - std::abs(calculated_distances[i].second - calculated_distances[i - 1].second) < 0.1) { + result.back().polyline.append(Point::new_scale(extended_points[i].position)); + result.back().overhang_attributes_mutable()->end_distance_from_prev_layer = extended_points[i].distance; + if (std::abs(calculated_distances[sequence_start_index].first - calculated_distances[i - 1].first) < path.width() * 0.05 && + std::abs(calculated_distances[sequence_start_index].second - calculated_distances[i - 1].second) < 0.05) { // do not start new path, the attributes are similar enough - } else { - result.back().polyline.append(Point::new_scale(extended_points[i].position)); - new_attrs.overhang_attributes->max_distance_from_prev_layer = calculated_distances[i].first; - new_attrs.overhang_attributes->proximity_to_curled_lines = calculated_distances[i].second; + } else if (i +1 < extended_points.size()) { // do not start new path if this is last point! + // start new path, parameters differ + new_attrs.overhang_attributes->start_distance_from_prev_layer = calculated_distances[i].first; + new_attrs.overhang_attributes->end_distance_from_prev_layer = calculated_distances[i].first; + new_attrs.overhang_attributes->proximity_to_curled_lines = calculated_distances[i].second; + sequence_start_index = i; result.emplace_back(new_attrs); } - result.back().polyline.append(Point::new_scale(extended_points[i].position)); } - std::cout << "ExtrusionPath " << std::to_string(size_t(&path)) << " split to " << result.size() << " paths"; - return result; }; @@ -129,4 +132,74 @@ ExtrusionEntityCollection calculate_and_split_overhanging_extrusions(const Extru return result; }; + +std::pair calculate_overhang_speed(const ExtrusionAttributes &attributes, + const FullPrintConfig &config, + size_t extruder_id, + float external_perim_reference_speed, + float default_speed) +{ + assert(attributes.overhang_attributes.has_value()); + std::vector> overhangs_with_speeds = { + {100, ConfigOptionFloatOrPercent{default_speed, false}}}; + if (config.enable_dynamic_overhang_speeds) { + overhangs_with_speeds = {{0, config.overhang_speed_0}, + {25, config.overhang_speed_1}, + {50, config.overhang_speed_2}, + {75, config.overhang_speed_3}, + {100, ConfigOptionFloatOrPercent{default_speed, false}}}; + } + + std::vector> overhang_with_fan_speeds = {{100, ConfigOptionInts{0}}}; + if (config.enable_dynamic_fan_speeds.get_at(extruder_id)) { + overhang_with_fan_speeds = {{0, config.overhang_fan_speed_0}, + {25, config.overhang_fan_speed_1}, + {50, config.overhang_fan_speed_2}, + {75, config.overhang_fan_speed_3}, + {100, ConfigOptionInts{0}}}; + } + + float speed_base = external_perim_reference_speed > 0 ? external_perim_reference_speed : default_speed; + std::map speed_sections; + for (size_t i = 0; i < overhangs_with_speeds.size(); i++) { + float distance = attributes.width * (1.0 - (overhangs_with_speeds[i].first / 100.0)); + float speed = overhangs_with_speeds[i].second.percent ? (speed_base * overhangs_with_speeds[i].second.value / 100.0) : + overhangs_with_speeds[i].second.value; + if (speed < EPSILON) + speed = speed_base; + speed_sections[distance] = speed; + } + + std::map fan_speed_sections; + for (size_t i = 0; i < overhang_with_fan_speeds.size(); i++) { + float distance = attributes.width * (1.0 - (overhang_with_fan_speeds[i].first / 100.0)); + float fan_speed = overhang_with_fan_speeds[i].second.get_at(extruder_id); + fan_speed_sections[distance] = fan_speed; + } + + auto interpolate_speed = [](const std::map &values, float distance) { + auto upper_dist = values.lower_bound(distance); + if (upper_dist == values.end()) { + return values.rbegin()->second; + } + if (upper_dist == values.begin()) { + return upper_dist->second; + } + + auto lower_dist = std::prev(upper_dist); + float t = (distance - lower_dist->first) / (upper_dist->first - lower_dist->first); + return (1.0f - t) * lower_dist->second + t * upper_dist->second; + }; + + float extrusion_speed = std::min(interpolate_speed(speed_sections, attributes.overhang_attributes->start_distance_from_prev_layer), + interpolate_speed(speed_sections, attributes.overhang_attributes->end_distance_from_prev_layer)); + float curled_base_speed = interpolate_speed(speed_sections, + attributes.width * attributes.overhang_attributes->proximity_to_curled_lines); + float final_speed = std::min(curled_base_speed, extrusion_speed); + float fan_speed = std::min(interpolate_speed(fan_speed_sections, attributes.overhang_attributes->start_distance_from_prev_layer), + interpolate_speed(fan_speed_sections, attributes.overhang_attributes->end_distance_from_prev_layer)); + + return {final_speed, fan_speed}; +} + }} // namespace Slic3r::ExtrusionProcessor \ No newline at end of file diff --git a/src/libslic3r/GCode/ExtrusionProcessor.hpp b/src/libslic3r/GCode/ExtrusionProcessor.hpp index a9787f212f..dacb2d0f73 100644 --- a/src/libslic3r/GCode/ExtrusionProcessor.hpp +++ b/src/libslic3r/GCode/ExtrusionProcessor.hpp @@ -15,8 +15,10 @@ #include "../Config.hpp" #include "../Line.hpp" #include "../Exception.hpp" +#include "../PrintConfig.hpp" #include +#include #include #include #include @@ -227,147 +229,11 @@ ExtrusionEntityCollection calculate_and_split_overhanging_extrusions( const AABBTreeLines::LinesDistancer &unscaled_prev_layer, const AABBTreeLines::LinesDistancer &prev_layer_curled_lines); -struct ProcessedPoint -{ - Point p; - float speed = 1.0f; - int fan_speed = 0; -}; - -class ExtrusionQualityEstimator -{ - std::unordered_map> prev_layer_boundaries; - std::unordered_map> next_layer_boundaries; - std::unordered_map> prev_curled_extrusions; - std::unordered_map> next_curled_extrusions; - const PrintObject *current_object; - -public: - void set_current_object(const PrintObject *object) { current_object = object; } - - void prepare_for_new_layer(const Layer *layer) - { - if (layer == nullptr) - return; - const PrintObject *object = layer->object(); - prev_layer_boundaries[object] = next_layer_boundaries[object]; - next_layer_boundaries[object] = AABBTreeLines::LinesDistancer{to_unscaled_linesf(layer->lslices)}; - prev_curled_extrusions[object] = next_curled_extrusions[object]; - next_curled_extrusions[object] = AABBTreeLines::LinesDistancer{layer->curled_lines}; - } - - std::vector estimate_speed_from_extrusion_quality( - const Points &path, - const ExtrusionFlow &flow, - const std::vector> overhangs_w_speeds, - const std::vector> overhangs_w_fan_speeds, - size_t extruder_id, - float ext_perimeter_speed, - float original_speed) - { - float speed_base = ext_perimeter_speed > 0 ? ext_perimeter_speed : original_speed; - std::map speed_sections; - for (size_t i = 0; i < overhangs_w_speeds.size(); i++) { - float distance = flow.width * (1.0 - (overhangs_w_speeds[i].first / 100.0)); - float speed = overhangs_w_speeds[i].second.percent ? (speed_base * overhangs_w_speeds[i].second.value / 100.0) : - overhangs_w_speeds[i].second.value; - if (speed < EPSILON) - speed = speed_base; - speed_sections[distance] = speed; - } - - std::map fan_speed_sections; - for (size_t i = 0; i < overhangs_w_fan_speeds.size(); i++) { - float distance = flow.width * (1.0 - (overhangs_w_fan_speeds[i].first / 100.0)); - float fan_speed = overhangs_w_fan_speeds[i].second.get_at(extruder_id); - fan_speed_sections[distance] = fan_speed; - } - - std::vector extended_points = - estimate_points_properties(path, prev_layer_boundaries[current_object], flow.width); - - std::vector processed_points; - processed_points.reserve(extended_points.size()); - for (size_t i = 0; i < extended_points.size(); i++) { - const ExtendedPoint &curr = extended_points[i]; - const ExtendedPoint &next = extended_points[i + 1 < extended_points.size() ? i + 1 : i]; - - // The following code artifically increases the distance to provide slowdown for extrusions that are over curled lines - float artificial_distance_to_curled_lines = 0.0; - const double dist_limit = 10.0 * flow.width; - { - Vec2d middle = 0.5 * (curr.position + next.position); - auto line_indices = prev_curled_extrusions[current_object].all_lines_in_radius(Point::new_scale(middle), scale_(dist_limit)); - if (!line_indices.empty()) { - double len = (next.position - curr.position).norm(); - // For long lines, there is a problem with the additional slowdown. If by accident, there is small curled line near - // the middle of this long line - // The whole segment gets slower unnecesarily. For these long lines, we do additional check whether it is worth slowing - // down. - // NOTE that this is still quite rough approximation, e.g. we are still checking lines only near the middle point - // TODO maybe split the lines into smaller segments before running this alg? but can be demanding, and GCode will be huge - if (len > 8) { - Vec2d dir = Vec2d(next.position - curr.position) / len; - Vec2d right = Vec2d(-dir.y(), dir.x()); - - Polygon box_of_influence = { - scaled(Vec2d(curr.position + right * dist_limit)), - scaled(Vec2d(next.position + right * dist_limit)), - scaled(Vec2d(next.position - right * dist_limit)), - scaled(Vec2d(curr.position - right * dist_limit)), - }; - - double projected_lengths_sum = 0; - for (size_t idx : line_indices) { - const CurledLine &line = prev_curled_extrusions[current_object].get_line(idx); - Lines inside = intersection_ln({{line.a, line.b}}, {box_of_influence}); - if (inside.empty()) - continue; - double projected_length = abs(dir.dot(unscaled(Vec2d((inside.back().b - inside.back().a).cast())))); - projected_lengths_sum += projected_length; - } - if (projected_lengths_sum < 0.4 * len) { - line_indices.clear(); - } - } - - for (size_t idx : line_indices) { - const CurledLine &line = prev_curled_extrusions[current_object].get_line(idx); - float distance_from_curled = unscaled(line_alg::distance_to(line, Point::new_scale(middle))); - float dist = flow.width * (1.0 - (distance_from_curled / dist_limit)) * - (1.0 - (distance_from_curled / dist_limit)) * - (line.curled_height / (flow.height * 10.0f)); // max_curled_height_factor from SupportSpotGenerator - artificial_distance_to_curled_lines = std::max(artificial_distance_to_curled_lines, dist); - } - } - } - - auto interpolate_speed = [](const std::map &values, float distance) { - auto upper_dist = values.lower_bound(distance); - if (upper_dist == values.end()) { - return values.rbegin()->second; - } - if (upper_dist == values.begin()) { - return upper_dist->second; - } - - auto lower_dist = std::prev(upper_dist); - float t = (distance - lower_dist->first) / (upper_dist->first - lower_dist->first); - return (1.0f - t) * lower_dist->second + t * upper_dist->second; - }; - - float extrusion_speed = std::min(interpolate_speed(speed_sections, curr.distance), - interpolate_speed(speed_sections, next.distance)); - float curled_base_speed = interpolate_speed(speed_sections, artificial_distance_to_curled_lines); - float final_speed = std::min(curled_base_speed, extrusion_speed); - float fan_speed = std::min(interpolate_speed(fan_speed_sections, curr.distance), - interpolate_speed(fan_speed_sections, next.distance)); - - processed_points.push_back({scaled(curr.position), final_speed, int(fan_speed)}); - } - return processed_points; - } -}; +std::pair calculate_overhang_speed(const ExtrusionAttributes &attributes, + const FullPrintConfig &config, + size_t extruder_id, + float external_perim_reference_speed, + float default_speed); }} // namespace Slic3r::ExtrusionProcessor diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 43e2937238..90e05ae78a 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -561,9 +561,10 @@ void PrintObject::calculate_overhanging_perimeters() continue; } ExPolygons prev_layer_polygon = l->lower_layer == nullptr ? ExPolygons() : l->lower_layer->lslices; - layer_region->m_perimeters = calculate_and_split_overhanging_extrusions(&layer_region->m_perimeters, - unscaled_polygons_lines[l->id()], - curled_lines[l->id()]); + layer_region->m_perimeters = + ExtrusionProcessor::calculate_and_split_overhanging_extrusions(&layer_region->m_perimeters, + unscaled_polygons_lines[l->id()], + curled_lines[l->id()]); } } diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index e1ae58fd21..8e52dcf6fc 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -327,10 +327,10 @@ std::vector check_extrusion_entity_stability(const ExtrusionEntit if (entity->length() < scale_(params.min_distance_to_allow_local_supports)) { return {}; } - const float flow_width = get_flow_width(layer_region, entity->role()); - std::vector annotated_points = estimate_points_properties(entity->as_polyline().points, - prev_layer_boundary, flow_width, - params.bridge_distance); + const float flow_width = get_flow_width(layer_region, entity->role()); + std::vector annotated_points = + ExtrusionProcessor::estimate_points_properties(entity->as_polyline().points, prev_layer_boundary, + flow_width, params.bridge_distance); std::vector lines_out; lines_out.reserve(annotated_points.size()); @@ -339,8 +339,8 @@ std::vector check_extrusion_entity_stability(const ExtrusionEntit std::optional bridging_dir{}; for (size_t i = 0; i < annotated_points.size(); ++i) { - ExtendedPoint &curr_point = annotated_points[i]; - const ExtendedPoint &prev_point = i > 0 ? annotated_points[i - 1] : annotated_points[i]; + ExtrusionProcessor::ExtendedPoint &curr_point = annotated_points[i]; + const ExtrusionProcessor::ExtendedPoint &prev_point = i > 0 ? annotated_points[i - 1] : annotated_points[i]; SupportPointCause potential_cause = std::abs(curr_point.curvature) > 0.1 ? SupportPointCause::FloatingBridgeAnchor : SupportPointCause::LongBridge; @@ -382,19 +382,19 @@ std::vector check_extrusion_entity_stability(const ExtrusionEntit const float flow_width = get_flow_width(layer_region, entity->role()); // Compute only unsigned distance - prev_layer_lines can contain unconnected paths, thus the sign of the distance is unreliable - std::vector annotated_points = estimate_points_properties(entity->as_polyline().points, - prev_layer_lines, flow_width, - params.bridge_distance); + std::vector annotated_points = + ExtrusionProcessor::estimate_points_properties(entity->as_polyline().points, prev_layer_lines, + flow_width, params.bridge_distance); std::vector lines_out; lines_out.reserve(annotated_points.size()); float bridged_distance = annotated_points.front().position != annotated_points.back().position ? (params.bridge_distance + 1.0f) : 0.0f; for (size_t i = 0; i < annotated_points.size(); ++i) { - ExtendedPoint &curr_point = annotated_points[i]; - const ExtendedPoint &prev_point = i > 0 ? annotated_points[i - 1] : annotated_points[i]; - float line_len = (prev_point.position - curr_point.position).norm(); - ExtrusionLine line_out{prev_point.position.cast(), curr_point.position.cast(), line_len, entity}; + ExtrusionProcessor::ExtendedPoint &curr_point = annotated_points[i]; + const ExtrusionProcessor::ExtendedPoint &prev_point = i > 0 ? annotated_points[i - 1] : annotated_points[i]; + float line_len = (prev_point.position - curr_point.position).norm(); + ExtrusionLine line_out{prev_point.position.cast(), curr_point.position.cast(), line_len, entity}; Vec2f middle = 0.5 * (line_out.a + line_out.b); auto [middle_distance, bottom_line_idx, x] = prev_layer_lines.distance_from_lines_extra(middle); @@ -1107,12 +1107,13 @@ void estimate_supports_malformations(SupportLayerPtrs &layers, float flow_width, Polygon pol(pl.points); pol.make_counter_clockwise(); - auto annotated_points = estimate_points_properties(pol.points, prev_layer_lines, flow_width); + auto annotated_points = ExtrusionProcessor::estimate_points_properties(pol.points, prev_layer_lines, + flow_width); for (size_t i = 0; i < annotated_points.size(); ++i) { - const ExtendedPoint &a = i > 0 ? annotated_points[i - 1] : annotated_points[i]; - const ExtendedPoint &b = annotated_points[i]; - ExtrusionLine line_out{a.position.cast(), b.position.cast(), float((a.position - b.position).norm()), + const ExtrusionProcessor::ExtendedPoint &a = i > 0 ? annotated_points[i - 1] : annotated_points[i]; + const ExtrusionProcessor::ExtendedPoint &b = annotated_points[i]; + ExtrusionLine line_out{a.position.cast(), b.position.cast(), float((a.position - b.position).norm()), extrusion}; Vec2f middle = 0.5 * (line_out.a + line_out.b); @@ -1182,11 +1183,13 @@ void estimate_malformations(LayerPtrs &layers, const Params ¶ms) Points extrusion_pts; extrusion->collect_points(extrusion_pts); float flow_width = get_flow_width(layer_region, extrusion->role()); - auto annotated_points = estimate_points_properties(extrusion_pts, prev_layer_lines, flow_width, - params.bridge_distance); + auto annotated_points = ExtrusionProcessor::estimate_points_properties(extrusion_pts, + prev_layer_lines, + flow_width, + params.bridge_distance); for (size_t i = 0; i < annotated_points.size(); ++i) { - const ExtendedPoint &a = i > 0 ? annotated_points[i - 1] : annotated_points[i]; - const ExtendedPoint &b = annotated_points[i]; + const ExtrusionProcessor::ExtendedPoint &a = i > 0 ? annotated_points[i - 1] : annotated_points[i]; + const ExtrusionProcessor::ExtendedPoint &b = annotated_points[i]; ExtrusionLine line_out{a.position.cast(), b.position.cast(), float((a.position - b.position).norm()), extrusion}; @@ -1196,7 +1199,8 @@ void estimate_malformations(LayerPtrs &layers, const Params ¶ms) prev_layer_lines.get_line(bottom_line_idx); // correctify the distance sign using slice polygons - float sign = (prev_layer_boundary.distance_from_lines(middle.cast()) + 0.5f * flow_width) < 0.0f ? -1.0f : 1.0f; + float sign = (prev_layer_boundary.distance_from_lines(middle.cast()) + 0.5f * flow_width) < 0.0f ? -1.0f : + 1.0f; line_out.curled_up_height = estimate_curled_up_height(middle_distance * sign, 0.5 * (a.curvature + b.curvature), l->height, flow_width, bottom_line.curled_up_height, params); From a98e2f893abe505db97a36e522b3d858fac864f5 Mon Sep 17 00:00:00 2001 From: Pavel Mikus Date: Thu, 20 Jul 2023 19:42:31 +0200 Subject: [PATCH 037/136] fixed issues, it now replicates the previous results --- src/libslic3r/GCode/ExtrusionProcessor.cpp | 6 ++++-- src/libslic3r/PrintObject.cpp | 11 ++++++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/GCode/ExtrusionProcessor.cpp b/src/libslic3r/GCode/ExtrusionProcessor.cpp index 3f6aa5f5ed..38c3c3b1f6 100644 --- a/src/libslic3r/GCode/ExtrusionProcessor.cpp +++ b/src/libslic3r/GCode/ExtrusionProcessor.cpp @@ -76,8 +76,9 @@ ExtrusionPaths calculate_and_split_overhanging_extrusions(const ExtrusionPath for (size_t i = 1; i < extended_points.size(); i++) { result.back().polyline.append(Point::new_scale(extended_points[i].position)); result.back().overhang_attributes_mutable()->end_distance_from_prev_layer = extended_points[i].distance; - if (std::abs(calculated_distances[sequence_start_index].first - calculated_distances[i - 1].first) < path.width() * 0.05 && - std::abs(calculated_distances[sequence_start_index].second - calculated_distances[i - 1].second) < 0.05) { + + if (std::abs(calculated_distances[sequence_start_index].first - calculated_distances[i].first) < path.width() * 0.0001 && + std::abs(calculated_distances[sequence_start_index].second - calculated_distances[i].second) < 0.0001) { // do not start new path, the attributes are similar enough } else if (i +1 < extended_points.size()) { // do not start new path if this is last point! // start new path, parameters differ @@ -86,6 +87,7 @@ ExtrusionPaths calculate_and_split_overhanging_extrusions(const ExtrusionPath new_attrs.overhang_attributes->proximity_to_curled_lines = calculated_distances[i].second; sequence_start_index = i; result.emplace_back(new_attrs); + result.back().polyline.append(Point::new_scale(extended_points[i].position)); } } diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 90e05ae78a..336f19d868 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -554,17 +554,22 @@ void PrintObject::calculate_overhanging_perimeters() curled_lines[l->id()] = AABBTreeLines::LinesDistancer{l->curled_lines}; unscaled_polygons_lines[l->id()] = AABBTreeLines::LinesDistancer{to_unscaled_linesf(l->lslices)}; } + curled_lines[size_t(-1)] = {}; + unscaled_polygons_lines[size_t(-1)] = {}; for (Layer *l : this->layers()) { + if (l->id() == 0) { // first layer, do not split + continue; + } for (LayerRegion *layer_region : l->regions()) { if (regions_with_dynamic_overhangs.find(layer_region->m_region) == regions_with_dynamic_overhangs.end()) { continue; } - ExPolygons prev_layer_polygon = l->lower_layer == nullptr ? ExPolygons() : l->lower_layer->lslices; + size_t prev_layer_id = l->lower_layer ? l->lower_layer->id() : size_t(-1); layer_region->m_perimeters = ExtrusionProcessor::calculate_and_split_overhanging_extrusions(&layer_region->m_perimeters, - unscaled_polygons_lines[l->id()], - curled_lines[l->id()]); + unscaled_polygons_lines[prev_layer_id], + curled_lines[prev_layer_id]); } } From ec2caaec8223501d8f69efa8a90388322391ae75 Mon Sep 17 00:00:00 2001 From: Pavel Date: Fri, 21 Jul 2023 11:47:24 +0200 Subject: [PATCH 038/136] Added invalidation of the new step, paralellize the computation of overhangs --- src/libslic3r/GCode/ExtrusionProcessor.cpp | 8 ++-- src/libslic3r/PrintObject.cpp | 54 ++++++++++++++-------- 2 files changed, 39 insertions(+), 23 deletions(-) diff --git a/src/libslic3r/GCode/ExtrusionProcessor.cpp b/src/libslic3r/GCode/ExtrusionProcessor.cpp index 38c3c3b1f6..00e4f8075c 100644 --- a/src/libslic3r/GCode/ExtrusionProcessor.cpp +++ b/src/libslic3r/GCode/ExtrusionProcessor.cpp @@ -77,10 +77,12 @@ ExtrusionPaths calculate_and_split_overhanging_extrusions(const ExtrusionPath result.back().polyline.append(Point::new_scale(extended_points[i].position)); result.back().overhang_attributes_mutable()->end_distance_from_prev_layer = extended_points[i].distance; - if (std::abs(calculated_distances[sequence_start_index].first - calculated_distances[i].first) < path.width() * 0.0001 && - std::abs(calculated_distances[sequence_start_index].second - calculated_distances[i].second) < 0.0001) { + if (std::abs(calculated_distances[sequence_start_index].first - calculated_distances[i].first) < 0.001 * path.attributes().width && + std::abs(calculated_distances[sequence_start_index].second - calculated_distances[i].second) < 0.001) { // do not start new path, the attributes are similar enough - } else if (i +1 < extended_points.size()) { // do not start new path if this is last point! + // NOTE: a larger tolerance may be applied here. However, it makes the gcode preview much less smooth + // (But it has very likely zero impact on the print quality.) + } else if (i + 1 < extended_points.size()) { // do not start new path if this is last point! // start new path, parameters differ new_attrs.overhang_attributes->start_distance_from_prev_layer = calculated_distances[i].first; new_attrs.overhang_attributes->end_distance_from_prev_layer = calculated_distances[i].first; diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 336f19d868..5e3220cab4 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -538,40 +538,54 @@ void PrintObject::estimate_curled_extrusions() void PrintObject::calculate_overhanging_perimeters() { if (this->set_started(posCalculateOverhangingPerimeters)) { - std::unordered_set regions_with_dynamic_overhangs; + BOOST_LOG_TRIVIAL(debug) << "Calculating overhanging perimeters - start"; + m_print->set_status(89, _u8L("Calculating overhanging perimeters")); + std::vector extruders; + std::unordered_set regions_with_dynamic_speeds; for (const PrintRegion *pr : this->print()->m_print_regions) { if (pr->config().enable_dynamic_overhang_speeds.getBool()) { - regions_with_dynamic_overhangs.insert(pr); + regions_with_dynamic_speeds.insert(pr); + } + extruders.clear(); + pr->collect_object_printing_extruders(*this->print(), extruders); + auto cfg = this->print()->config(); + if (std::any_of(extruders.begin(), extruders.end(), + [&cfg](unsigned int extruder_id) { return cfg.enable_dynamic_fan_speeds.get_at(extruder_id); })) { + regions_with_dynamic_speeds.insert(pr); } } - if (!regions_with_dynamic_overhangs.empty()) { - BOOST_LOG_TRIVIAL(debug) << "Calculating overhanging perimeters - start"; - m_print->set_status(89, _u8L("Calculating overhanging perimeters")); + if (!regions_with_dynamic_speeds.empty()) { std::unordered_map> curled_lines; std::unordered_map> unscaled_polygons_lines; for (const Layer *l : this->layers()) { curled_lines[l->id()] = AABBTreeLines::LinesDistancer{l->curled_lines}; unscaled_polygons_lines[l->id()] = AABBTreeLines::LinesDistancer{to_unscaled_linesf(l->lslices)}; } - curled_lines[size_t(-1)] = {}; + curled_lines[size_t(-1)] = {}; unscaled_polygons_lines[size_t(-1)] = {}; - for (Layer *l : this->layers()) { - if (l->id() == 0) { // first layer, do not split - continue; - } - for (LayerRegion *layer_region : l->regions()) { - if (regions_with_dynamic_overhangs.find(layer_region->m_region) == regions_with_dynamic_overhangs.end()) { + tbb::parallel_for(tbb::blocked_range(0, m_layers.size()), [this, &curled_lines, &unscaled_polygons_lines, + ®ions_with_dynamic_speeds]( + const tbb::blocked_range &range) { + PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT); + for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { + auto l = m_layers[layer_idx]; + if (l->id() == 0) { // first layer, do not split continue; } - size_t prev_layer_id = l->lower_layer ? l->lower_layer->id() : size_t(-1); - layer_region->m_perimeters = - ExtrusionProcessor::calculate_and_split_overhanging_extrusions(&layer_region->m_perimeters, - unscaled_polygons_lines[prev_layer_id], - curled_lines[prev_layer_id]); + for (LayerRegion *layer_region : l->regions()) { + if (regions_with_dynamic_speeds.find(layer_region->m_region) == regions_with_dynamic_speeds.end()) { + continue; + } + size_t prev_layer_id = l->lower_layer ? l->lower_layer->id() : size_t(-1); + layer_region->m_perimeters = + ExtrusionProcessor::calculate_and_split_overhanging_extrusions(&layer_region->m_perimeters, + unscaled_polygons_lines[prev_layer_id], + curled_lines[prev_layer_id]); + } } - } + }); m_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Calculating overhanging perimeters - end"; @@ -893,7 +907,7 @@ bool PrintObject::invalidate_step(PrintObjectStep step) // propagate to dependent steps if (step == posPerimeters) { - invalidated |= this->invalidate_steps({ posPrepareInfill, posInfill, posIroning, posSupportSpotsSearch, posEstimateCurledExtrusions }); + invalidated |= this->invalidate_steps({ posPrepareInfill, posInfill, posIroning, posSupportSpotsSearch, posEstimateCurledExtrusions, posCalculateOverhangingPerimeters }); invalidated |= m_print->invalidate_steps({ psSkirtBrim }); } else if (step == posPrepareInfill) { invalidated |= this->invalidate_steps({ posInfill, posIroning, posSupportSpotsSearch}); @@ -902,7 +916,7 @@ bool PrintObject::invalidate_step(PrintObjectStep step) invalidated |= m_print->invalidate_steps({ psSkirtBrim }); } else if (step == posSlice) { invalidated |= this->invalidate_steps({posPerimeters, posPrepareInfill, posInfill, posIroning, posSupportSpotsSearch, - posSupportMaterial, posEstimateCurledExtrusions}); + posSupportMaterial, posEstimateCurledExtrusions, posCalculateOverhangingPerimeters}); invalidated |= m_print->invalidate_steps({ psSkirtBrim }); m_slicing_params.valid = false; } else if (step == posSupportMaterial) { From c74eb1abe0483fa6011a9527f4c2102bcdd84f74 Mon Sep 17 00:00:00 2001 From: Pavel Date: Mon, 24 Jul 2023 16:41:15 +0200 Subject: [PATCH 039/136] SPE-1821 Fix issue with overhangs sometimes wrongly detected when using classic Fix gcode export - remove empty lines --- src/libslic3r/GCode/ExtrusionProcessor.cpp | 7 +++++++ src/libslic3r/GCode/ExtrusionProcessor.hpp | 8 ++++---- src/libslic3r/PrintObject.cpp | 2 +- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/GCode/ExtrusionProcessor.cpp b/src/libslic3r/GCode/ExtrusionProcessor.cpp index 00e4f8075c..8ebc5d3df1 100644 --- a/src/libslic3r/GCode/ExtrusionProcessor.cpp +++ b/src/libslic3r/GCode/ExtrusionProcessor.cpp @@ -203,6 +203,13 @@ std::pair calculate_overhang_speed(const ExtrusionAttributes &attri float fan_speed = std::min(interpolate_speed(fan_speed_sections, attributes.overhang_attributes->start_distance_from_prev_layer), interpolate_speed(fan_speed_sections, attributes.overhang_attributes->end_distance_from_prev_layer)); + if (!config.enable_dynamic_overhang_speeds) { + final_speed = -1; + } + if (!config.enable_dynamic_fan_speeds.get_at(extruder_id)) { + fan_speed = -1; + } + return {final_speed, fan_speed}; } diff --git a/src/libslic3r/GCode/ExtrusionProcessor.hpp b/src/libslic3r/GCode/ExtrusionProcessor.hpp index dacb2d0f73..c636b25c3b 100644 --- a/src/libslic3r/GCode/ExtrusionProcessor.hpp +++ b/src/libslic3r/GCode/ExtrusionProcessor.hpp @@ -92,12 +92,12 @@ std::vector estimate_points_properties(const POINTS const ExtendedPoint &curr = points[point_idx]; const ExtendedPoint &next = points[point_idx + 1]; - if ((curr.distance > 0 && curr.distance < boundary_offset + 2.0f) || - (next.distance > 0 && next.distance < boundary_offset + 2.0f)) { + if ((curr.distance > -boundary_offset && curr.distance < boundary_offset + 2.0f) || + (next.distance > -boundary_offset && next.distance < boundary_offset + 2.0f)) { double line_len = (next.position - curr.position).norm(); if (line_len > 4.0f) { - double a0 = std::clamp((curr.distance + 2 * boundary_offset) / line_len, 0.0, 1.0); - double a1 = std::clamp(1.0f - (next.distance + 2 * boundary_offset) / line_len, 0.0, 1.0); + double a0 = std::clamp((curr.distance + 3 * boundary_offset) / line_len, 0.0, 1.0); + double a1 = std::clamp(1.0f - (next.distance + 3 * boundary_offset) / line_len, 0.0, 1.0); double t0 = std::min(a0, a1); double t1 = std::max(a0, a1); diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 5e3220cab4..49d7afaae2 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -582,7 +582,7 @@ void PrintObject::calculate_overhanging_perimeters() layer_region->m_perimeters = ExtrusionProcessor::calculate_and_split_overhanging_extrusions(&layer_region->m_perimeters, unscaled_polygons_lines[prev_layer_id], - curled_lines[prev_layer_id]); + curled_lines[l->id()]); } } }); From e0baccd654362804587e3af1f3c9c44a43164446 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 27 Jul 2023 10:04:21 +0200 Subject: [PATCH 040/136] ArcWelder: Disabled for pressure equalizer. Fixed some compiler warnings --- src/libslic3r/GCode.cpp | 41 +++++++++++++++----------- src/libslic3r/GCode/GCodeProcessor.cpp | 4 +++ src/libslic3r/Geometry/ArcWelder.cpp | 2 +- 3 files changed, 28 insertions(+), 19 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 0504645520..35d1386ac1 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -799,14 +799,29 @@ std::vector sort_object_instances_by_model_order(const Pri return instances; } +static inline bool arc_welder_enabled(const PrintConfig& print_config) +{ + return + // Enabled + print_config.arc_fitting != ArcFittingType::Disabled && + // Not a spiral vase print + !print_config.spiral_vase && + // Presure equalizer not used + print_config.max_volumetric_extrusion_rate_slope_negative == 0. && + print_config.max_volumetric_extrusion_rate_slope_positive == 0.; +} + +static inline GCode::SmoothPathCache::InterpolationParameters interpolation_parameters(const PrintConfig& print_config) +{ + return { + scaled(print_config.gcode_resolution.value), + arc_welder_enabled(print_config) ? Geometry::ArcWelder::default_arc_length_percent_tolerance : 0 + }; +} + static inline GCode::SmoothPathCache smooth_path_interpolate_global(const Print& print) { - const GCode::SmoothPathCache::InterpolationParameters interpolation_params { - scaled(print.config().gcode_resolution.value), - print.config().arc_fitting != ArcFittingType::Disabled && ! print.config().spiral_vase ? - Geometry::ArcWelder::default_arc_length_percent_tolerance : - 0 - }; + const GCode::SmoothPathCache::InterpolationParameters interpolation_params = interpolation_parameters(print.config()); GCode::SmoothPathCache out; out.interpolate_add(print.skirt(), interpolation_params); out.interpolate_add(print.brim(), interpolation_params); @@ -1282,12 +1297,7 @@ void GCodeGenerator::process_layers( GCodeOutputStream &output_stream) { size_t layer_to_print_idx = 0; - const GCode::SmoothPathCache::InterpolationParameters interpolation_params { - scaled(print.config().gcode_resolution.value), - print.config().arc_fitting != ArcFittingType::Disabled && ! print.config().spiral_vase ? - Geometry::ArcWelder::default_arc_length_percent_tolerance : - 0 - }; + const GCode::SmoothPathCache::InterpolationParameters interpolation_params = interpolation_parameters(print.config()); const auto smooth_path_interpolator = tbb::make_filter>(slic3r_tbb_filtermode::serial_in_order, [this, &print, &layers_to_print, &layer_to_print_idx, &interpolation_params](tbb::flow_control &fc) -> std::pair { if (layer_to_print_idx >= layers_to_print.size()) { @@ -1385,12 +1395,7 @@ void GCodeGenerator::process_layers( GCodeOutputStream &output_stream) { size_t layer_to_print_idx = 0; - const GCode::SmoothPathCache::InterpolationParameters interpolation_params { - scaled(print.config().gcode_resolution.value), - print.config().arc_fitting != ArcFittingType::Disabled && ! print.config().spiral_vase ? - Geometry::ArcWelder::default_arc_length_percent_tolerance : - 0 - }; + const GCode::SmoothPathCache::InterpolationParameters interpolation_params = interpolation_parameters(print.config()); const auto smooth_path_interpolator = tbb::make_filter> (slic3r_tbb_filtermode::serial_in_order, [this, &print, &layers_to_print, &layer_to_print_idx, interpolation_params](tbb::flow_control &fc) -> std::pair { if (layer_to_print_idx >= layers_to_print.size()) { diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 28cfc8a515..40ea2e6903 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -2711,12 +2711,16 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line, bool cloc // relative center Vec3f rel_center = Vec3f::Zero(); +#ifndef _NDEBUG double radius = 0.0; +#endif // _NDEBUG if (fitting == EFitting::R) { float r; if (!line.has_value('R', r) || r == 0.0f) return; +#ifndef _NDEBUG radius = (double)std::abs(r); +#endif // _NDEBUG const Vec2f start_pos((float)m_start_position[X], (float)m_start_position[Y]); const Vec2f end_pos((float)end_position[X], (float)end_position[Y]); const Vec2f c = Geometry::ArcWelder::arc_center(start_pos, end_pos, r, !clockwise); diff --git a/src/libslic3r/Geometry/ArcWelder.cpp b/src/libslic3r/Geometry/ArcWelder.cpp index 4f789f3c50..7a5421699d 100644 --- a/src/libslic3r/Geometry/ArcWelder.cpp +++ b/src/libslic3r/Geometry/ArcWelder.cpp @@ -645,7 +645,7 @@ std::pair split_at(const Path &path, const PathSegmentProjection &pr } } else { assert(split_segment_id >= 0 && split_segment_id < path.size()); - if (split_segment_id + 1 == path.size()) + if (split_segment_id + 1 == int(path.size())) out.first = path; else if (split_segment_id == 0) out.second = path; From 2cde917f11cbd83e3a2820fd6b7b592950c1fcf3 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 27 Jul 2023 10:22:22 +0200 Subject: [PATCH 041/136] Delete output file when gcode conversion fails --- src/slic3r/GUI/Plater.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 22076aadff..13330199db 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5442,9 +5442,7 @@ void Plater::convert_gcode_to_ascii() public: explicit ScopedFile(FILE* file) : m_file(file) {} ~ScopedFile() { if (m_file != nullptr) fclose(m_file); } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void unscope() { m_file = nullptr; } -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ private: FILE* m_file{ nullptr }; }; @@ -5478,11 +5476,9 @@ void Plater::convert_gcode_to_ascii() if (res != bgcode::EResult::Success) { MessageDialog msg_dlg(this, _L(bgcode::translate_result(res)), _L("Error converting gcode file"), wxICON_INFORMATION | wxOK); msg_dlg.ShowModal(); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ scoped_out_file.unscope(); fclose(out_file); boost::nowide::remove(output_file.c_str()); -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ return; } } From cc7f2f3aaefcbdac5839afdb322e8194a38e01de Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 28 Jul 2023 14:48:14 +0200 Subject: [PATCH 042/136] Fixed missing whitespace in GXX lines when unbinarizing using meatpack --- src/libslic3r/GCode/GCodeBinarizer.cpp | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/GCode/GCodeBinarizer.cpp b/src/libslic3r/GCode/GCodeBinarizer.cpp index 1a4748bad9..d1e2368863 100644 --- a/src/libslic3r/GCode/GCodeBinarizer.cpp +++ b/src/libslic3r/GCode/GCodeBinarizer.cpp @@ -407,18 +407,31 @@ static void unbinarize(const std::vector& src, std::string& dst) { } } + auto is_gline_parameter = [](const char c) { + static const std::vector parameters = { + // G0, G1 + 'X', 'Y', 'Z', 'E', 'F', + // G2, G3 + 'I', 'J', 'R', + // G29 + 'P', 'W', 'H', 'C', 'A' + }; + return std::find(parameters.begin(), parameters.end(), c) != parameters.end(); + }; + std::array c_unbin{ 0, 0 }; const size_t char_count = get_result_char(c_unbin); for (size_t i = 0; i < char_count; ++i) { // GCodeReader::parse_line_internal() is unable to parse a G line where the data are not separated by spaces // so we add them where needed - if (c_unbin[i] == 'G' && std::distance(unbin_buffer.begin(), it_unbin_end) > 0 && *std::prev(it_unbin_end, 1) == '\n') + const size_t curr_unbin_buffer_length = std::distance(unbin_buffer.begin(), it_unbin_end); + if (c_unbin[i] == 'G' && (curr_unbin_buffer_length == 0 || *std::prev(it_unbin_end, 1) == '\n')) add_space = true; else if (c_unbin[i] == '\n') add_space = false; - if (add_space && *std::prev(it_unbin_end, 1) != ' ' && - (c_unbin[i] == 'X' || c_unbin[i] == 'Y' || c_unbin[i] == 'Z' || c_unbin[i] == 'E' || c_unbin[i] == 'F')) { + if (add_space && (curr_unbin_buffer_length == 0 || *std::prev(it_unbin_end, 1) != ' ') && + is_gline_parameter(c_unbin[i])) { *it_unbin_end = ' '; ++it_unbin_end; } From 866fc767bb129b042669bc5a50deb98cd0cc5218 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 28 Jul 2023 19:30:49 +0200 Subject: [PATCH 043/136] Integrating LibBGCode into deps with an optional var for source dir One can then co-develop the two projects with relative convenience --- deps/CMakeLists.txt | 9 +++++++-- deps/LibBGCode/LibBGCode.cmake | 24 ++++++++++++++++++++++++ src/libslic3r/CMakeLists.txt | 3 +++ 3 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 deps/LibBGCode/LibBGCode.cmake diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index b00f85ba70..1008707d78 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -20,8 +20,8 @@ # therefore, unfortunatelly, the installation cannot be copied/moved elsewhere without re-installing wxWidgets. # +cmake_minimum_required(VERSION 3.10) project(PrusaSlicer-deps) -cmake_minimum_required(VERSION 3.2) include(ExternalProject) include(ProcessorCount) @@ -62,6 +62,8 @@ if (NOT _is_multi AND NOT CMAKE_BUILD_TYPE) message(STATUS "Forcing CMAKE_BUILD_TYPE to Release as it was not specified.") endif () +cmake_policy(SET CMP0135 NEW) + function(prusaslicer_add_cmake_project projectname) cmake_parse_arguments(P_ARGS "" "INSTALL_DIR;BUILD_COMMAND;INSTALL_COMMAND" "CMAKE_ARGS" ${ARGN}) @@ -195,6 +197,8 @@ include(NanoSVG/NanoSVG.cmake) include(wxWidgets/wxWidgets.cmake) include(OCCT/OCCT.cmake) +include(LibBGCode/LibBGCode.cmake) + set(_dep_list dep_Boost dep_TBB @@ -206,10 +210,11 @@ set(_dep_list dep_OpenCSG dep_CGAL dep_Qhull - dep_OCCT + # dep_OCCT ${PNG_PKG} ${ZLIB_PKG} ${EXPAT_PKG} + dep_LibBGCode ) # if (NOT MSVC) diff --git a/deps/LibBGCode/LibBGCode.cmake b/deps/LibBGCode/LibBGCode.cmake new file mode 100644 index 0000000000..6ecd5ee725 --- /dev/null +++ b/deps/LibBGCode/LibBGCode.cmake @@ -0,0 +1,24 @@ +set(LibBGCode_SOURCE_DIR "" CACHE PATH "Optionally specify local LibBGCode source directory") + +set(_source_dir_line "URL;https://github.com/prusa3d/libbgcode/archive/refs/heads/main.zip") + +if (LibBGCode_SOURCE_DIR) + set(_source_dir_line "SOURCE_DIR;${LibBGCode_SOURCE_DIR};BUILD_ALWAYS;ON") +endif () + +prusaslicer_add_cmake_project(LibBGCode_deps + ${_source_dir_line} + SOURCE_SUBDIR deps + CMAKE_ARGS + -DDEP_DOWNLOAD_DIR:PATH=${DEP_DOWNLOAD_DIR} + -DLibBGCode_Deps_SELECT_ALL:BOOL=OFF + -DLibBGCode_Deps_SELECT_heatshrink:BOOL=ON + -DDESTDIR=${DESTDIR} +) + +prusaslicer_add_cmake_project(LibBGCode + ${_source_dir_line} + DEPENDS dep_LibBGCode_deps + CMAKE_ARGS + -DLibBGCode_BUILD_TESTS:BOOL=OFF +) \ No newline at end of file diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 0de0b4e517..f11986d314 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -14,6 +14,8 @@ if (TARGET OpenVDB::openvdb) set(OpenVDBUtils_SOURCES OpenVDBUtils.cpp OpenVDBUtils.hpp OpenVDBUtilsLegacy.hpp) endif() +find_package(LibBGCode REQUIRED COMPONENTS Convert) + set(SLIC3R_SOURCES pchheader.cpp pchheader.hpp @@ -508,6 +510,7 @@ target_link_libraries(libslic3r ZLIB::ZLIB JPEG::JPEG qoi + LibBGCode::bgcode_convert ) if (APPLE) From d9f0c1a0526343f4b2d3076d386bb8fa52241651 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Sat, 29 Jul 2023 15:41:13 +0200 Subject: [PATCH 044/136] fix unknown policy on older cmakes --- deps/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index 1008707d78..fce6a6e753 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -62,7 +62,9 @@ if (NOT _is_multi AND NOT CMAKE_BUILD_TYPE) message(STATUS "Forcing CMAKE_BUILD_TYPE to Release as it was not specified.") endif () -cmake_policy(SET CMP0135 NEW) +if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.24) + cmake_policy(SET CMP0135 NEW) +endif () function(prusaslicer_add_cmake_project projectname) cmake_parse_arguments(P_ARGS "" "INSTALL_DIR;BUILD_COMMAND;INSTALL_COMMAND" "CMAKE_ARGS" ${ARGN}) From 6d3d3298dafaf74a406c5dedc6f6788c0b8df5f6 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Sat, 29 Jul 2023 15:45:29 +0200 Subject: [PATCH 045/136] use git to download libbgcode by default should work on the build server --- deps/LibBGCode/LibBGCode.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/LibBGCode/LibBGCode.cmake b/deps/LibBGCode/LibBGCode.cmake index 6ecd5ee725..61b7f34504 100644 --- a/deps/LibBGCode/LibBGCode.cmake +++ b/deps/LibBGCode/LibBGCode.cmake @@ -1,6 +1,6 @@ set(LibBGCode_SOURCE_DIR "" CACHE PATH "Optionally specify local LibBGCode source directory") -set(_source_dir_line "URL;https://github.com/prusa3d/libbgcode/archive/refs/heads/main.zip") +set(_source_dir_line "GIT_REPOSITORY;git@github.com:prusa3d/libbgcode.git;GIT_TAG;main") if (LibBGCode_SOURCE_DIR) set(_source_dir_line "SOURCE_DIR;${LibBGCode_SOURCE_DIR};BUILD_ALWAYS;ON") From be468dca797a32260d4daf84a391038853f053ee Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 1 Aug 2023 13:34:39 +0200 Subject: [PATCH 046/136] Revert accidental commenting out of dep_OCCT --- deps/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index fce6a6e753..d5edcb5719 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -212,7 +212,7 @@ set(_dep_list dep_OpenCSG dep_CGAL dep_Qhull - # dep_OCCT + dep_OCCT ${PNG_PKG} ${ZLIB_PKG} ${EXPAT_PKG} From 2c80865ea90dd9a53992c12b672067299415376a Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 2 Aug 2023 15:32:26 +0200 Subject: [PATCH 047/136] Fix missing dependent targets from libbgcode deps --- deps/LibBGCode/LibBGCode.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/deps/LibBGCode/LibBGCode.cmake b/deps/LibBGCode/LibBGCode.cmake index 61b7f34504..074adefffc 100644 --- a/deps/LibBGCode/LibBGCode.cmake +++ b/deps/LibBGCode/LibBGCode.cmake @@ -9,6 +9,7 @@ endif () prusaslicer_add_cmake_project(LibBGCode_deps ${_source_dir_line} SOURCE_SUBDIR deps + DEPENDS dep_Boost ${ZLIB_PKG} CMAKE_ARGS -DDEP_DOWNLOAD_DIR:PATH=${DEP_DOWNLOAD_DIR} -DLibBGCode_Deps_SELECT_ALL:BOOL=OFF From 07065c9e195c2895330fa13a5cbb42a6ae5e4482 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 3 Aug 2023 08:56:21 +0200 Subject: [PATCH 048/136] Partial revert of 7e56d807185348987026786c4f8607fcfdc84d6e - Removal of integrated Heatshrink library --- src/CMakeLists.txt | 1 - src/heatshrink/CMakeLists.txt | 11 - src/heatshrink/LICENSE | 14 - src/heatshrink/README.md | 7 - src/heatshrink/heatshrink_common.h | 20 - src/heatshrink/heatshrink_config.h | 26 -- src/heatshrink/heatshrink_decoder.c | 367 ----------------- src/heatshrink/heatshrink_decoder.h | 100 ----- src/heatshrink/heatshrink_encoder.c | 604 ---------------------------- src/heatshrink/heatshrink_encoder.h | 109 ----- 10 files changed, 1259 deletions(-) delete mode 100644 src/heatshrink/CMakeLists.txt delete mode 100644 src/heatshrink/LICENSE delete mode 100644 src/heatshrink/README.md delete mode 100644 src/heatshrink/heatshrink_common.h delete mode 100644 src/heatshrink/heatshrink_config.h delete mode 100644 src/heatshrink/heatshrink_decoder.c delete mode 100644 src/heatshrink/heatshrink_decoder.h delete mode 100644 src/heatshrink/heatshrink_encoder.c delete mode 100644 src/heatshrink/heatshrink_encoder.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cda4310d9f..fec0d4cf70 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -14,7 +14,6 @@ add_subdirectory(libigl) add_subdirectory(hints) add_subdirectory(qoi) add_subdirectory(libnest2d) -add_subdirectory(heatshrink) find_package(Qhull 7.2 REQUIRED) add_library(qhull INTERFACE) diff --git a/src/heatshrink/CMakeLists.txt b/src/heatshrink/CMakeLists.txt deleted file mode 100644 index 94c93315ba..0000000000 --- a/src/heatshrink/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -cmake_minimum_required(VERSION 2.8.12) -project(heatshrink) - -add_library(heatshrink STATIC - heatshrink_common.h - heatshrink_config.h - heatshrink_decoder.c - heatshrink_decoder.h - heatshrink_encoder.c - heatshrink_encoder.h -) diff --git a/src/heatshrink/LICENSE b/src/heatshrink/LICENSE deleted file mode 100644 index 9132cb6a0c..0000000000 --- a/src/heatshrink/LICENSE +++ /dev/null @@ -1,14 +0,0 @@ -Copyright (c) 2013-2015, Scott Vokes -All rights reserved. - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/src/heatshrink/README.md b/src/heatshrink/README.md deleted file mode 100644 index 4de08bee29..0000000000 --- a/src/heatshrink/README.md +++ /dev/null @@ -1,7 +0,0 @@ -** heatshrink is a data compression library for embedded/real-time systems.** - -For more information go to https://spin.atomicobject.com/2013/03/14/heatshrink-embedded-data-compression/ - -THIS DIRECTORY CONTAINS THE heatshrink v0.4.1 b9ac05e SOURCE DISTRIBUTION. - - diff --git a/src/heatshrink/heatshrink_common.h b/src/heatshrink/heatshrink_common.h deleted file mode 100644 index bc89774f5e..0000000000 --- a/src/heatshrink/heatshrink_common.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef HEATSHRINK_H -#define HEATSHRINK_H - -#define HEATSHRINK_AUTHOR "Scott Vokes " -#define HEATSHRINK_URL "https://github.com/atomicobject/heatshrink" - -/* Version 0.4.1 */ -#define HEATSHRINK_VERSION_MAJOR 0 -#define HEATSHRINK_VERSION_MINOR 4 -#define HEATSHRINK_VERSION_PATCH 1 - -#define HEATSHRINK_MIN_WINDOW_BITS 4 -#define HEATSHRINK_MAX_WINDOW_BITS 15 - -#define HEATSHRINK_MIN_LOOKAHEAD_BITS 3 - -#define HEATSHRINK_LITERAL_MARKER 0x01 -#define HEATSHRINK_BACKREF_MARKER 0x00 - -#endif diff --git a/src/heatshrink/heatshrink_config.h b/src/heatshrink/heatshrink_config.h deleted file mode 100644 index 13135b9395..0000000000 --- a/src/heatshrink/heatshrink_config.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef HEATSHRINK_CONFIG_H -#define HEATSHRINK_CONFIG_H - -/* Should functionality assuming dynamic allocation be used? */ -#ifndef HEATSHRINK_DYNAMIC_ALLOC -#define HEATSHRINK_DYNAMIC_ALLOC 1 -#endif - -#if HEATSHRINK_DYNAMIC_ALLOC - /* Optional replacement of malloc/free */ - #define HEATSHRINK_MALLOC(SZ) malloc(SZ) - #define HEATSHRINK_FREE(P, SZ) free(P) -#else - /* Required parameters for static configuration */ - #define HEATSHRINK_STATIC_INPUT_BUFFER_SIZE 32 - #define HEATSHRINK_STATIC_WINDOW_BITS 8 - #define HEATSHRINK_STATIC_LOOKAHEAD_BITS 4 -#endif - -/* Turn on logging for debugging. */ -#define HEATSHRINK_DEBUGGING_LOGS 0 - -/* Use indexing for faster compression. (This requires additional space.) */ -#define HEATSHRINK_USE_INDEX 1 - -#endif diff --git a/src/heatshrink/heatshrink_decoder.c b/src/heatshrink/heatshrink_decoder.c deleted file mode 100644 index 0f118cf98a..0000000000 --- a/src/heatshrink/heatshrink_decoder.c +++ /dev/null @@ -1,367 +0,0 @@ -#include -#include -#include "heatshrink_decoder.h" - -/* States for the polling state machine. */ -typedef enum { - HSDS_TAG_BIT, /* tag bit */ - HSDS_YIELD_LITERAL, /* ready to yield literal byte */ - HSDS_BACKREF_INDEX_MSB, /* most significant byte of index */ - HSDS_BACKREF_INDEX_LSB, /* least significant byte of index */ - HSDS_BACKREF_COUNT_MSB, /* most significant byte of count */ - HSDS_BACKREF_COUNT_LSB, /* least significant byte of count */ - HSDS_YIELD_BACKREF, /* ready to yield back-reference */ -} HSD_state; - -#if HEATSHRINK_DEBUGGING_LOGS -#include -#include -#include -#define LOG(...) fprintf(stderr, __VA_ARGS__) -#define ASSERT(X) assert(X) -static const char *state_names[] = { - "tag_bit", - "yield_literal", - "backref_index_msb", - "backref_index_lsb", - "backref_count_msb", - "backref_count_lsb", - "yield_backref", -}; -#else -#define LOG(...) /* no-op */ -#define ASSERT(X) /* no-op */ -#endif - -typedef struct { - uint8_t *buf; /* output buffer */ - size_t buf_size; /* buffer size */ - size_t *output_size; /* bytes pushed to buffer, so far */ -} output_info; - -#define NO_BITS ((uint16_t)-1) - -/* Forward references. */ -static uint16_t get_bits(heatshrink_decoder *hsd, uint8_t count); -static void push_byte(heatshrink_decoder *hsd, output_info *oi, uint8_t byte); - -#if HEATSHRINK_DYNAMIC_ALLOC -heatshrink_decoder *heatshrink_decoder_alloc(uint16_t input_buffer_size, - uint8_t window_sz2, - uint8_t lookahead_sz2) { - if ((window_sz2 < HEATSHRINK_MIN_WINDOW_BITS) || - (window_sz2 > HEATSHRINK_MAX_WINDOW_BITS) || - (input_buffer_size == 0) || - (lookahead_sz2 < HEATSHRINK_MIN_LOOKAHEAD_BITS) || - (lookahead_sz2 >= window_sz2)) { - return NULL; - } - size_t buffers_sz = (1 << window_sz2) + input_buffer_size; - size_t sz = sizeof(heatshrink_decoder) + buffers_sz; - heatshrink_decoder *hsd = HEATSHRINK_MALLOC(sz); - if (hsd == NULL) { return NULL; } - hsd->input_buffer_size = input_buffer_size; - hsd->window_sz2 = window_sz2; - hsd->lookahead_sz2 = lookahead_sz2; - heatshrink_decoder_reset(hsd); - LOG("-- allocated decoder with buffer size of %zu (%zu + %u + %u)\n", - sz, sizeof(heatshrink_decoder), (1 << window_sz2), input_buffer_size); - return hsd; -} - -void heatshrink_decoder_free(heatshrink_decoder *hsd) { - size_t buffers_sz = (1 << hsd->window_sz2) + hsd->input_buffer_size; - size_t sz = sizeof(heatshrink_decoder) + buffers_sz; - HEATSHRINK_FREE(hsd, sz); - (void)sz; /* may not be used by free */ -} -#endif - -void heatshrink_decoder_reset(heatshrink_decoder *hsd) { - size_t buf_sz = 1 << HEATSHRINK_DECODER_WINDOW_BITS(hsd); - size_t input_sz = HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd); - memset(hsd->buffers, 0, buf_sz + input_sz); - hsd->state = HSDS_TAG_BIT; - hsd->input_size = 0; - hsd->input_index = 0; - hsd->bit_index = 0x00; - hsd->current_byte = 0x00; - hsd->output_count = 0; - hsd->output_index = 0; - hsd->head_index = 0; -} - -/* Copy SIZE bytes into the decoder's input buffer, if it will fit. */ -HSD_sink_res heatshrink_decoder_sink(heatshrink_decoder *hsd, - uint8_t *in_buf, size_t size, size_t *input_size) { - if ((hsd == NULL) || (in_buf == NULL) || (input_size == NULL)) { - return HSDR_SINK_ERROR_NULL; - } - - size_t rem = HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd) - hsd->input_size; - if (rem == 0) { - *input_size = 0; - return HSDR_SINK_FULL; - } - - size = rem < size ? rem : size; - LOG("-- sinking %zd bytes\n", size); - /* copy into input buffer (at head of buffers) */ - memcpy(&hsd->buffers[hsd->input_size], in_buf, size); - hsd->input_size += size; - *input_size = size; - return HSDR_SINK_OK; -} - - -/***************** - * Decompression * - *****************/ - -#define BACKREF_COUNT_BITS(HSD) (HEATSHRINK_DECODER_LOOKAHEAD_BITS(HSD)) -#define BACKREF_INDEX_BITS(HSD) (HEATSHRINK_DECODER_WINDOW_BITS(HSD)) - -// States -static HSD_state st_tag_bit(heatshrink_decoder *hsd); -static HSD_state st_yield_literal(heatshrink_decoder *hsd, - output_info *oi); -static HSD_state st_backref_index_msb(heatshrink_decoder *hsd); -static HSD_state st_backref_index_lsb(heatshrink_decoder *hsd); -static HSD_state st_backref_count_msb(heatshrink_decoder *hsd); -static HSD_state st_backref_count_lsb(heatshrink_decoder *hsd); -static HSD_state st_yield_backref(heatshrink_decoder *hsd, - output_info *oi); - -HSD_poll_res heatshrink_decoder_poll(heatshrink_decoder *hsd, - uint8_t *out_buf, size_t out_buf_size, size_t *output_size) { - if ((hsd == NULL) || (out_buf == NULL) || (output_size == NULL)) { - return HSDR_POLL_ERROR_NULL; - } - *output_size = 0; - - output_info oi; - oi.buf = out_buf; - oi.buf_size = out_buf_size; - oi.output_size = output_size; - - while (1) { - LOG("-- poll, state is %d (%s), input_size %d\n", - hsd->state, state_names[hsd->state], hsd->input_size); - uint8_t in_state = hsd->state; - switch (in_state) { - case HSDS_TAG_BIT: - hsd->state = st_tag_bit(hsd); - break; - case HSDS_YIELD_LITERAL: - hsd->state = st_yield_literal(hsd, &oi); - break; - case HSDS_BACKREF_INDEX_MSB: - hsd->state = st_backref_index_msb(hsd); - break; - case HSDS_BACKREF_INDEX_LSB: - hsd->state = st_backref_index_lsb(hsd); - break; - case HSDS_BACKREF_COUNT_MSB: - hsd->state = st_backref_count_msb(hsd); - break; - case HSDS_BACKREF_COUNT_LSB: - hsd->state = st_backref_count_lsb(hsd); - break; - case HSDS_YIELD_BACKREF: - hsd->state = st_yield_backref(hsd, &oi); - break; - default: - return HSDR_POLL_ERROR_UNKNOWN; - } - - /* If the current state cannot advance, check if input or output - * buffer are exhausted. */ - if (hsd->state == in_state) { - if (*output_size == out_buf_size) { return HSDR_POLL_MORE; } - return HSDR_POLL_EMPTY; - } - } -} - -static HSD_state st_tag_bit(heatshrink_decoder *hsd) { - uint32_t bits = get_bits(hsd, 1); // get tag bit - if (bits == NO_BITS) { - return HSDS_TAG_BIT; - } else if (bits) { - return HSDS_YIELD_LITERAL; - } else if (HEATSHRINK_DECODER_WINDOW_BITS(hsd) > 8) { - return HSDS_BACKREF_INDEX_MSB; - } else { - hsd->output_index = 0; - return HSDS_BACKREF_INDEX_LSB; - } -} - -static HSD_state st_yield_literal(heatshrink_decoder *hsd, - output_info *oi) { - /* Emit a repeated section from the window buffer, and add it (again) - * to the window buffer. (Note that the repetition can include - * itself.)*/ - if (*oi->output_size < oi->buf_size) { - uint16_t byte = get_bits(hsd, 8); - if (byte == NO_BITS) { return HSDS_YIELD_LITERAL; } /* out of input */ - uint8_t *buf = &hsd->buffers[HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd)]; - uint16_t mask = (1 << HEATSHRINK_DECODER_WINDOW_BITS(hsd)) - 1; - uint8_t c = byte & 0xFF; - LOG("-- emitting literal byte 0x%02x ('%c')\n", c, isprint(c) ? c : '.'); - buf[hsd->head_index++ & mask] = c; - push_byte(hsd, oi, c); - return HSDS_TAG_BIT; - } else { - return HSDS_YIELD_LITERAL; - } -} - -static HSD_state st_backref_index_msb(heatshrink_decoder *hsd) { - uint8_t bit_ct = BACKREF_INDEX_BITS(hsd); - ASSERT(bit_ct > 8); - uint16_t bits = get_bits(hsd, bit_ct - 8); - LOG("-- backref index (msb), got 0x%04x (+1)\n", bits); - if (bits == NO_BITS) { return HSDS_BACKREF_INDEX_MSB; } - hsd->output_index = bits << 8; - return HSDS_BACKREF_INDEX_LSB; -} - -static HSD_state st_backref_index_lsb(heatshrink_decoder *hsd) { - uint8_t bit_ct = BACKREF_INDEX_BITS(hsd); - uint16_t bits = get_bits(hsd, bit_ct < 8 ? bit_ct : 8); - LOG("-- backref index (lsb), got 0x%04x (+1)\n", bits); - if (bits == NO_BITS) { return HSDS_BACKREF_INDEX_LSB; } - hsd->output_index |= bits; - hsd->output_index++; - uint8_t br_bit_ct = BACKREF_COUNT_BITS(hsd); - hsd->output_count = 0; - return (br_bit_ct > 8) ? HSDS_BACKREF_COUNT_MSB : HSDS_BACKREF_COUNT_LSB; -} - -static HSD_state st_backref_count_msb(heatshrink_decoder *hsd) { - uint8_t br_bit_ct = BACKREF_COUNT_BITS(hsd); - ASSERT(br_bit_ct > 8); - uint16_t bits = get_bits(hsd, br_bit_ct - 8); - LOG("-- backref count (msb), got 0x%04x (+1)\n", bits); - if (bits == NO_BITS) { return HSDS_BACKREF_COUNT_MSB; } - hsd->output_count = bits << 8; - return HSDS_BACKREF_COUNT_LSB; -} - -static HSD_state st_backref_count_lsb(heatshrink_decoder *hsd) { - uint8_t br_bit_ct = BACKREF_COUNT_BITS(hsd); - uint16_t bits = get_bits(hsd, br_bit_ct < 8 ? br_bit_ct : 8); - LOG("-- backref count (lsb), got 0x%04x (+1)\n", bits); - if (bits == NO_BITS) { return HSDS_BACKREF_COUNT_LSB; } - hsd->output_count |= bits; - hsd->output_count++; - return HSDS_YIELD_BACKREF; -} - -static HSD_state st_yield_backref(heatshrink_decoder *hsd, - output_info *oi) { - size_t count = oi->buf_size - *oi->output_size; - if (count > 0) { - size_t i = 0; - if (hsd->output_count < count) count = hsd->output_count; - uint8_t *buf = &hsd->buffers[HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(hsd)]; - uint16_t mask = (1 << HEATSHRINK_DECODER_WINDOW_BITS(hsd)) - 1; - uint16_t neg_offset = hsd->output_index; - LOG("-- emitting %zu bytes from -%u bytes back\n", count, neg_offset); - ASSERT(neg_offset <= mask + 1); - ASSERT(count <= (size_t)(1 << BACKREF_COUNT_BITS(hsd))); - - for (i=0; ihead_index - neg_offset) & mask]; - push_byte(hsd, oi, c); - buf[hsd->head_index & mask] = c; - hsd->head_index++; - LOG(" -- ++ 0x%02x\n", c); - } - hsd->output_count -= count; - if (hsd->output_count == 0) { return HSDS_TAG_BIT; } - } - return HSDS_YIELD_BACKREF; -} - -/* Get the next COUNT bits from the input buffer, saving incremental progress. - * Returns NO_BITS on end of input, or if more than 15 bits are requested. */ -static uint16_t get_bits(heatshrink_decoder *hsd, uint8_t count) { - uint16_t accumulator = 0; - int i = 0; - if (count > 15) { return NO_BITS; } - LOG("-- popping %u bit(s)\n", count); - - /* If we aren't able to get COUNT bits, suspend immediately, because we - * don't track how many bits of COUNT we've accumulated before suspend. */ - if (hsd->input_size == 0) { - if (hsd->bit_index < (1 << (count - 1))) { return NO_BITS; } - } - - for (i = 0; i < count; i++) { - if (hsd->bit_index == 0x00) { - if (hsd->input_size == 0) { - LOG(" -- out of bits, suspending w/ accumulator of %u (0x%02x)\n", - accumulator, accumulator); - return NO_BITS; - } - hsd->current_byte = hsd->buffers[hsd->input_index++]; - LOG(" -- pulled byte 0x%02x\n", hsd->current_byte); - if (hsd->input_index == hsd->input_size) { - hsd->input_index = 0; /* input is exhausted */ - hsd->input_size = 0; - } - hsd->bit_index = 0x80; - } - accumulator <<= 1; - if (hsd->current_byte & hsd->bit_index) { - accumulator |= 0x01; - if (0) { - LOG(" -- got 1, accumulator 0x%04x, bit_index 0x%02x\n", - accumulator, hsd->bit_index); - } - } else { - if (0) { - LOG(" -- got 0, accumulator 0x%04x, bit_index 0x%02x\n", - accumulator, hsd->bit_index); - } - } - hsd->bit_index >>= 1; - } - - if (count > 1) { LOG(" -- accumulated %08x\n", accumulator); } - return accumulator; -} - -HSD_finish_res heatshrink_decoder_finish(heatshrink_decoder *hsd) { - if (hsd == NULL) { return HSDR_FINISH_ERROR_NULL; } - switch (hsd->state) { - case HSDS_TAG_BIT: - return hsd->input_size == 0 ? HSDR_FINISH_DONE : HSDR_FINISH_MORE; - - /* If we want to finish with no input, but are in these states, it's - * because the 0-bit padding to the last byte looks like a backref - * marker bit followed by all 0s for index and count bits. */ - case HSDS_BACKREF_INDEX_LSB: - case HSDS_BACKREF_INDEX_MSB: - case HSDS_BACKREF_COUNT_LSB: - case HSDS_BACKREF_COUNT_MSB: - return hsd->input_size == 0 ? HSDR_FINISH_DONE : HSDR_FINISH_MORE; - - /* If the output stream is padded with 0xFFs (possibly due to being in - * flash memory), also explicitly check the input size rather than - * uselessly returning MORE but yielding 0 bytes when polling. */ - case HSDS_YIELD_LITERAL: - return hsd->input_size == 0 ? HSDR_FINISH_DONE : HSDR_FINISH_MORE; - - default: - return HSDR_FINISH_MORE; - } -} - -static void push_byte(heatshrink_decoder *hsd, output_info *oi, uint8_t byte) { - LOG(" -- pushing byte: 0x%02x ('%c')\n", byte, isprint(byte) ? byte : '.'); - oi->buf[(*oi->output_size)++] = byte; - (void)hsd; -} diff --git a/src/heatshrink/heatshrink_decoder.h b/src/heatshrink/heatshrink_decoder.h deleted file mode 100644 index bda83991ef..0000000000 --- a/src/heatshrink/heatshrink_decoder.h +++ /dev/null @@ -1,100 +0,0 @@ -#ifndef HEATSHRINK_DECODER_H -#define HEATSHRINK_DECODER_H - -#include -#include -#include "heatshrink_common.h" -#include "heatshrink_config.h" - -typedef enum { - HSDR_SINK_OK, /* data sunk, ready to poll */ - HSDR_SINK_FULL, /* out of space in internal buffer */ - HSDR_SINK_ERROR_NULL=-1, /* NULL argument */ -} HSD_sink_res; - -typedef enum { - HSDR_POLL_EMPTY, /* input exhausted */ - HSDR_POLL_MORE, /* more data remaining, call again w/ fresh output buffer */ - HSDR_POLL_ERROR_NULL=-1, /* NULL arguments */ - HSDR_POLL_ERROR_UNKNOWN=-2, -} HSD_poll_res; - -typedef enum { - HSDR_FINISH_DONE, /* output is done */ - HSDR_FINISH_MORE, /* more output remains */ - HSDR_FINISH_ERROR_NULL=-1, /* NULL arguments */ -} HSD_finish_res; - -#if HEATSHRINK_DYNAMIC_ALLOC -#define HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(BUF) \ - ((BUF)->input_buffer_size) -#define HEATSHRINK_DECODER_WINDOW_BITS(BUF) \ - ((BUF)->window_sz2) -#define HEATSHRINK_DECODER_LOOKAHEAD_BITS(BUF) \ - ((BUF)->lookahead_sz2) -#else -#define HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(_) \ - HEATSHRINK_STATIC_INPUT_BUFFER_SIZE -#define HEATSHRINK_DECODER_WINDOW_BITS(_) \ - (HEATSHRINK_STATIC_WINDOW_BITS) -#define HEATSHRINK_DECODER_LOOKAHEAD_BITS(BUF) \ - (HEATSHRINK_STATIC_LOOKAHEAD_BITS) -#endif - -typedef struct { - uint16_t input_size; /* bytes in input buffer */ - uint16_t input_index; /* offset to next unprocessed input byte */ - uint16_t output_count; /* how many bytes to output */ - uint16_t output_index; /* index for bytes to output */ - uint16_t head_index; /* head of window buffer */ - uint8_t state; /* current state machine node */ - uint8_t current_byte; /* current byte of input */ - uint8_t bit_index; /* current bit index */ - -#if HEATSHRINK_DYNAMIC_ALLOC - /* Fields that are only used if dynamically allocated. */ - uint8_t window_sz2; /* window buffer bits */ - uint8_t lookahead_sz2; /* lookahead bits */ - uint16_t input_buffer_size; /* input buffer size */ - - /* Input buffer, then expansion window buffer */ - uint8_t buffers[]; -#else - /* Input buffer, then expansion window buffer */ - uint8_t buffers[(1 << HEATSHRINK_DECODER_WINDOW_BITS(_)) - + HEATSHRINK_DECODER_INPUT_BUFFER_SIZE(_)]; -#endif -} heatshrink_decoder; - -#if HEATSHRINK_DYNAMIC_ALLOC -/* Allocate a decoder with an input buffer of INPUT_BUFFER_SIZE bytes, - * an expansion buffer size of 2^WINDOW_SZ2, and a lookahead - * size of 2^lookahead_sz2. (The window buffer and lookahead sizes - * must match the settings used when the data was compressed.) - * Returns NULL on error. */ -heatshrink_decoder *heatshrink_decoder_alloc(uint16_t input_buffer_size, - uint8_t expansion_buffer_sz2, uint8_t lookahead_sz2); - -/* Free a decoder. */ -void heatshrink_decoder_free(heatshrink_decoder *hsd); -#endif - -/* Reset a decoder. */ -void heatshrink_decoder_reset(heatshrink_decoder *hsd); - -/* Sink at most SIZE bytes from IN_BUF into the decoder. *INPUT_SIZE is set to - * indicate how many bytes were actually sunk (in case a buffer was filled). */ -HSD_sink_res heatshrink_decoder_sink(heatshrink_decoder *hsd, - uint8_t *in_buf, size_t size, size_t *input_size); - -/* Poll for output from the decoder, copying at most OUT_BUF_SIZE bytes into - * OUT_BUF (setting *OUTPUT_SIZE to the actual amount copied). */ -HSD_poll_res heatshrink_decoder_poll(heatshrink_decoder *hsd, - uint8_t *out_buf, size_t out_buf_size, size_t *output_size); - -/* Notify the dencoder that the input stream is finished. - * If the return value is HSDR_FINISH_MORE, there is still more output, so - * call heatshrink_decoder_poll and repeat. */ -HSD_finish_res heatshrink_decoder_finish(heatshrink_decoder *hsd); - -#endif diff --git a/src/heatshrink/heatshrink_encoder.c b/src/heatshrink/heatshrink_encoder.c deleted file mode 100644 index edf4abebcc..0000000000 --- a/src/heatshrink/heatshrink_encoder.c +++ /dev/null @@ -1,604 +0,0 @@ -#include -#include -#include -#include "heatshrink_encoder.h" - -typedef enum { - HSES_NOT_FULL, /* input buffer not full enough */ - HSES_FILLED, /* buffer is full */ - HSES_SEARCH, /* searching for patterns */ - HSES_YIELD_TAG_BIT, /* yield tag bit */ - HSES_YIELD_LITERAL, /* emit literal byte */ - HSES_YIELD_BR_INDEX, /* yielding backref index */ - HSES_YIELD_BR_LENGTH, /* yielding backref length */ - HSES_SAVE_BACKLOG, /* copying buffer to backlog */ - HSES_FLUSH_BITS, /* flush bit buffer */ - HSES_DONE, /* done */ -} HSE_state; - -#if HEATSHRINK_DEBUGGING_LOGS -#include -#include -#include -#define LOG(...) fprintf(stderr, __VA_ARGS__) -#define ASSERT(X) assert(X) -static const char *state_names[] = { - "not_full", - "filled", - "search", - "yield_tag_bit", - "yield_literal", - "yield_br_index", - "yield_br_length", - "save_backlog", - "flush_bits", - "done", -}; -#else -#define LOG(...) /* no-op */ -#define ASSERT(X) /* no-op */ -#endif - -// Encoder flags -enum { - FLAG_IS_FINISHING = 0x01, -}; - -typedef struct { - uint8_t *buf; /* output buffer */ - size_t buf_size; /* buffer size */ - size_t *output_size; /* bytes pushed to buffer, so far */ -} output_info; - -#define MATCH_NOT_FOUND ((uint16_t)-1) - -static uint16_t get_input_offset(heatshrink_encoder *hse); -static uint16_t get_input_buffer_size(heatshrink_encoder *hse); -static uint16_t get_lookahead_size(heatshrink_encoder *hse); -static void add_tag_bit(heatshrink_encoder *hse, output_info *oi, uint8_t tag); -static int can_take_byte(output_info *oi); -static int is_finishing(heatshrink_encoder *hse); -static void save_backlog(heatshrink_encoder *hse); - -/* Push COUNT (max 8) bits to the output buffer, which has room. */ -static void push_bits(heatshrink_encoder *hse, uint8_t count, uint8_t bits, - output_info *oi); -static uint8_t push_outgoing_bits(heatshrink_encoder *hse, output_info *oi); -static void push_literal_byte(heatshrink_encoder *hse, output_info *oi); - -#if HEATSHRINK_DYNAMIC_ALLOC -heatshrink_encoder *heatshrink_encoder_alloc(uint8_t window_sz2, - uint8_t lookahead_sz2) { - if ((window_sz2 < HEATSHRINK_MIN_WINDOW_BITS) || - (window_sz2 > HEATSHRINK_MAX_WINDOW_BITS) || - (lookahead_sz2 < HEATSHRINK_MIN_LOOKAHEAD_BITS) || - (lookahead_sz2 >= window_sz2)) { - return NULL; - } - - /* Note: 2 * the window size is used because the buffer needs to fit - * (1 << window_sz2) bytes for the current input, and an additional - * (1 << window_sz2) bytes for the previous buffer of input, which - * will be scanned for useful backreferences. */ - size_t buf_sz = (2 << window_sz2); - - heatshrink_encoder *hse = HEATSHRINK_MALLOC(sizeof(*hse) + buf_sz); - if (hse == NULL) { return NULL; } - hse->window_sz2 = window_sz2; - hse->lookahead_sz2 = lookahead_sz2; - heatshrink_encoder_reset(hse); - -#if HEATSHRINK_USE_INDEX - size_t index_sz = buf_sz*sizeof(uint16_t); - hse->search_index = HEATSHRINK_MALLOC(index_sz + sizeof(struct hs_index)); - if (hse->search_index == NULL) { - HEATSHRINK_FREE(hse, sizeof(*hse) + buf_sz); - return NULL; - } - hse->search_index->size = index_sz; -#endif - - LOG("-- allocated encoder with buffer size of %zu (%u byte input size)\n", - buf_sz, get_input_buffer_size(hse)); - return hse; -} - -void heatshrink_encoder_free(heatshrink_encoder *hse) { - size_t buf_sz = (2 << HEATSHRINK_ENCODER_WINDOW_BITS(hse)); -#if HEATSHRINK_USE_INDEX - size_t index_sz = sizeof(struct hs_index) + hse->search_index->size; - HEATSHRINK_FREE(hse->search_index, index_sz); - (void)index_sz; -#endif - HEATSHRINK_FREE(hse, sizeof(heatshrink_encoder) + buf_sz); - (void)buf_sz; -} -#endif - -void heatshrink_encoder_reset(heatshrink_encoder *hse) { - size_t buf_sz = (2 << HEATSHRINK_ENCODER_WINDOW_BITS(hse)); - memset(hse->buffer, 0, buf_sz); - hse->input_size = 0; - hse->state = HSES_NOT_FULL; - hse->match_scan_index = 0; - hse->flags = 0; - hse->bit_index = 0x80; - hse->current_byte = 0x00; - hse->match_length = 0; - - hse->outgoing_bits = 0x0000; - hse->outgoing_bits_count = 0; - - #ifdef LOOP_DETECT - hse->loop_detect = (uint32_t)-1; - #endif -} - -HSE_sink_res heatshrink_encoder_sink(heatshrink_encoder *hse, - uint8_t *in_buf, size_t size, size_t *input_size) { - if ((hse == NULL) || (in_buf == NULL) || (input_size == NULL)) { - return HSER_SINK_ERROR_NULL; - } - - /* Sinking more content after saying the content is done, tsk tsk */ - if (is_finishing(hse)) { return HSER_SINK_ERROR_MISUSE; } - - /* Sinking more content before processing is done */ - if (hse->state != HSES_NOT_FULL) { return HSER_SINK_ERROR_MISUSE; } - - uint16_t write_offset = get_input_offset(hse) + hse->input_size; - uint16_t ibs = get_input_buffer_size(hse); - uint16_t rem = ibs - hse->input_size; - uint16_t cp_sz = rem < size ? rem : size; - - memcpy(&hse->buffer[write_offset], in_buf, cp_sz); - *input_size = cp_sz; - hse->input_size += cp_sz; - - LOG("-- sunk %u bytes (of %zu) into encoder at %d, input buffer now has %u\n", - cp_sz, size, write_offset, hse->input_size); - if (cp_sz == rem) { - LOG("-- internal buffer is now full\n"); - hse->state = HSES_FILLED; - } - - return HSER_SINK_OK; -} - - -/*************** - * Compression * - ***************/ - -static uint16_t find_longest_match(heatshrink_encoder *hse, uint16_t start, - uint16_t end, const uint16_t maxlen, uint16_t *match_length); -static void do_indexing(heatshrink_encoder *hse); - -static HSE_state st_step_search(heatshrink_encoder *hse); -static HSE_state st_yield_tag_bit(heatshrink_encoder *hse, - output_info *oi); -static HSE_state st_yield_literal(heatshrink_encoder *hse, - output_info *oi); -static HSE_state st_yield_br_index(heatshrink_encoder *hse, - output_info *oi); -static HSE_state st_yield_br_length(heatshrink_encoder *hse, - output_info *oi); -static HSE_state st_save_backlog(heatshrink_encoder *hse); -static HSE_state st_flush_bit_buffer(heatshrink_encoder *hse, - output_info *oi); - -HSE_poll_res heatshrink_encoder_poll(heatshrink_encoder *hse, - uint8_t *out_buf, size_t out_buf_size, size_t *output_size) { - if ((hse == NULL) || (out_buf == NULL) || (output_size == NULL)) { - return HSER_POLL_ERROR_NULL; - } - if (out_buf_size == 0) { - LOG("-- MISUSE: output buffer size is 0\n"); - return HSER_POLL_ERROR_MISUSE; - } - *output_size = 0; - - output_info oi; - oi.buf = out_buf; - oi.buf_size = out_buf_size; - oi.output_size = output_size; - - while (1) { - LOG("-- polling, state %u (%s), flags 0x%02x\n", - hse->state, state_names[hse->state], hse->flags); - - uint8_t in_state = hse->state; - switch (in_state) { - case HSES_NOT_FULL: - return HSER_POLL_EMPTY; - case HSES_FILLED: - do_indexing(hse); - hse->state = HSES_SEARCH; - break; - case HSES_SEARCH: - hse->state = st_step_search(hse); - break; - case HSES_YIELD_TAG_BIT: - hse->state = st_yield_tag_bit(hse, &oi); - break; - case HSES_YIELD_LITERAL: - hse->state = st_yield_literal(hse, &oi); - break; - case HSES_YIELD_BR_INDEX: - hse->state = st_yield_br_index(hse, &oi); - break; - case HSES_YIELD_BR_LENGTH: - hse->state = st_yield_br_length(hse, &oi); - break; - case HSES_SAVE_BACKLOG: - hse->state = st_save_backlog(hse); - break; - case HSES_FLUSH_BITS: - hse->state = st_flush_bit_buffer(hse, &oi); - case HSES_DONE: - return HSER_POLL_EMPTY; - default: - LOG("-- bad state %s\n", state_names[hse->state]); - return HSER_POLL_ERROR_MISUSE; - } - - if (hse->state == in_state) { - /* Check if output buffer is exhausted. */ - if (*output_size == out_buf_size) return HSER_POLL_MORE; - } - } -} - -HSE_finish_res heatshrink_encoder_finish(heatshrink_encoder *hse) { - if (hse == NULL) { return HSER_FINISH_ERROR_NULL; } - LOG("-- setting is_finishing flag\n"); - hse->flags |= FLAG_IS_FINISHING; - if (hse->state == HSES_NOT_FULL) { hse->state = HSES_FILLED; } - return hse->state == HSES_DONE ? HSER_FINISH_DONE : HSER_FINISH_MORE; -} - -static HSE_state st_step_search(heatshrink_encoder *hse) { - uint16_t window_length = get_input_buffer_size(hse); - uint16_t lookahead_sz = get_lookahead_size(hse); - uint16_t msi = hse->match_scan_index; - LOG("## step_search, scan @ +%d (%d/%d), input size %d\n", - msi, hse->input_size + msi, 2*window_length, hse->input_size); - - bool fin = is_finishing(hse); - if (msi > hse->input_size - (fin ? 1 : lookahead_sz)) { - /* Current search buffer is exhausted, copy it into the - * backlog and await more input. */ - LOG("-- end of search @ %d\n", msi); - return fin ? HSES_FLUSH_BITS : HSES_SAVE_BACKLOG; - } - - uint16_t input_offset = get_input_offset(hse); - uint16_t end = input_offset + msi; - uint16_t start = end - window_length; - - uint16_t max_possible = lookahead_sz; - if (hse->input_size - msi < lookahead_sz) { - max_possible = hse->input_size - msi; - } - - uint16_t match_length = 0; - uint16_t match_pos = find_longest_match(hse, - start, end, max_possible, &match_length); - - if (match_pos == MATCH_NOT_FOUND) { - LOG("ss Match not found\n"); - hse->match_scan_index++; - hse->match_length = 0; - return HSES_YIELD_TAG_BIT; - } else { - LOG("ss Found match of %d bytes at %d\n", match_length, match_pos); - hse->match_pos = match_pos; - hse->match_length = match_length; - ASSERT(match_pos <= 1 << HEATSHRINK_ENCODER_WINDOW_BITS(hse) /*window_length*/); - - return HSES_YIELD_TAG_BIT; - } -} - -static HSE_state st_yield_tag_bit(heatshrink_encoder *hse, - output_info *oi) { - if (can_take_byte(oi)) { - if (hse->match_length == 0) { - add_tag_bit(hse, oi, HEATSHRINK_LITERAL_MARKER); - return HSES_YIELD_LITERAL; - } else { - add_tag_bit(hse, oi, HEATSHRINK_BACKREF_MARKER); - hse->outgoing_bits = hse->match_pos - 1; - hse->outgoing_bits_count = HEATSHRINK_ENCODER_WINDOW_BITS(hse); - return HSES_YIELD_BR_INDEX; - } - } else { - return HSES_YIELD_TAG_BIT; /* output is full, continue */ - } -} - -static HSE_state st_yield_literal(heatshrink_encoder *hse, - output_info *oi) { - if (can_take_byte(oi)) { - push_literal_byte(hse, oi); - return HSES_SEARCH; - } else { - return HSES_YIELD_LITERAL; - } -} - -static HSE_state st_yield_br_index(heatshrink_encoder *hse, - output_info *oi) { - if (can_take_byte(oi)) { - LOG("-- yielding backref index %u\n", hse->match_pos); - if (push_outgoing_bits(hse, oi) > 0) { - return HSES_YIELD_BR_INDEX; /* continue */ - } else { - hse->outgoing_bits = hse->match_length - 1; - hse->outgoing_bits_count = HEATSHRINK_ENCODER_LOOKAHEAD_BITS(hse); - return HSES_YIELD_BR_LENGTH; /* done */ - } - } else { - return HSES_YIELD_BR_INDEX; /* continue */ - } -} - -static HSE_state st_yield_br_length(heatshrink_encoder *hse, - output_info *oi) { - if (can_take_byte(oi)) { - LOG("-- yielding backref length %u\n", hse->match_length); - if (push_outgoing_bits(hse, oi) > 0) { - return HSES_YIELD_BR_LENGTH; - } else { - hse->match_scan_index += hse->match_length; - hse->match_length = 0; - return HSES_SEARCH; - } - } else { - return HSES_YIELD_BR_LENGTH; - } -} - -static HSE_state st_save_backlog(heatshrink_encoder *hse) { - LOG("-- saving backlog\n"); - save_backlog(hse); - return HSES_NOT_FULL; -} - -static HSE_state st_flush_bit_buffer(heatshrink_encoder *hse, - output_info *oi) { - if (hse->bit_index == 0x80) { - LOG("-- done!\n"); - return HSES_DONE; - } else if (can_take_byte(oi)) { - LOG("-- flushing remaining byte (bit_index == 0x%02x)\n", hse->bit_index); - oi->buf[(*oi->output_size)++] = hse->current_byte; - LOG("-- done!\n"); - return HSES_DONE; - } else { - return HSES_FLUSH_BITS; - } -} - -static void add_tag_bit(heatshrink_encoder *hse, output_info *oi, uint8_t tag) { - LOG("-- adding tag bit: %d\n", tag); - push_bits(hse, 1, tag, oi); -} - -static uint16_t get_input_offset(heatshrink_encoder *hse) { - return get_input_buffer_size(hse); -} - -static uint16_t get_input_buffer_size(heatshrink_encoder *hse) { - return (1 << HEATSHRINK_ENCODER_WINDOW_BITS(hse)); - (void)hse; -} - -static uint16_t get_lookahead_size(heatshrink_encoder *hse) { - return (1 << HEATSHRINK_ENCODER_LOOKAHEAD_BITS(hse)); - (void)hse; -} - -static void do_indexing(heatshrink_encoder *hse) { -#if HEATSHRINK_USE_INDEX - /* Build an index array I that contains flattened linked lists - * for the previous instances of every byte in the buffer. - * - * For example, if buf[200] == 'x', then index[200] will either - * be an offset i such that buf[i] == 'x', or a negative offset - * to indicate end-of-list. This significantly speeds up matching, - * while only using sizeof(uint16_t)*sizeof(buffer) bytes of RAM. - * - * Future optimization options: - * 1. Since any negative value represents end-of-list, the other - * 15 bits could be used to improve the index dynamically. - * - * 2. Likewise, the last lookahead_sz bytes of the index will - * not be usable, so temporary data could be stored there to - * dynamically improve the index. - * */ - struct hs_index *hsi = HEATSHRINK_ENCODER_INDEX(hse); - int16_t last[256]; - memset(last, 0xFF, sizeof(last)); - - uint8_t * const data = hse->buffer; - int16_t * const index = hsi->index; - - const uint16_t input_offset = get_input_offset(hse); - const uint16_t end = input_offset + hse->input_size; - - for (uint16_t i=0; iflags & FLAG_IS_FINISHING; -} - -static int can_take_byte(output_info *oi) { - return *oi->output_size < oi->buf_size; -} - -/* Return the longest match for the bytes at buf[end:end+maxlen] between - * buf[start] and buf[end-1]. If no match is found, return -1. */ -static uint16_t find_longest_match(heatshrink_encoder *hse, uint16_t start, - uint16_t end, const uint16_t maxlen, uint16_t *match_length) { - LOG("-- scanning for match of buf[%u:%u] between buf[%u:%u] (max %u bytes)\n", - end, end + maxlen, start, end + maxlen - 1, maxlen); - uint8_t *buf = hse->buffer; - - uint16_t match_maxlen = 0; - uint16_t match_index = MATCH_NOT_FOUND; - - uint16_t len = 0; - uint8_t * const needlepoint = &buf[end]; -#if HEATSHRINK_USE_INDEX - struct hs_index *hsi = HEATSHRINK_ENCODER_INDEX(hse); - int16_t pos = hsi->index[end]; - - while (pos - (int16_t)start >= 0) { - uint8_t * const pospoint = &buf[pos]; - len = 0; - - /* Only check matches that will potentially beat the current maxlen. - * This is redundant with the index if match_maxlen is 0, but the - * added branch overhead to check if it == 0 seems to be worse. */ - if (pospoint[match_maxlen] != needlepoint[match_maxlen]) { - pos = hsi->index[pos]; - continue; - } - - for (len = 1; len < maxlen; len++) { - if (pospoint[len] != needlepoint[len]) break; - } - - if (len > match_maxlen) { - match_maxlen = len; - match_index = pos; - if (len == maxlen) { break; } /* won't find better */ - } - pos = hsi->index[pos]; - } -#else - for (int16_t pos=end - 1; pos - (int16_t)start >= 0; pos--) { - uint8_t * const pospoint = &buf[pos]; - if ((pospoint[match_maxlen] == needlepoint[match_maxlen]) - && (*pospoint == *needlepoint)) { - for (len=1; len cmp buf[%d] == 0x%02x against %02x (start %u)\n", - pos + len, pospoint[len], needlepoint[len], start); - } - if (pospoint[len] != needlepoint[len]) { break; } - } - if (len > match_maxlen) { - match_maxlen = len; - match_index = pos; - if (len == maxlen) { break; } /* don't keep searching */ - } - } - } -#endif - - const size_t break_even_point = - (1 + HEATSHRINK_ENCODER_WINDOW_BITS(hse) + - HEATSHRINK_ENCODER_LOOKAHEAD_BITS(hse)); - - /* Instead of comparing break_even_point against 8*match_maxlen, - * compare match_maxlen against break_even_point/8 to avoid - * overflow. Since MIN_WINDOW_BITS and MIN_LOOKAHEAD_BITS are 4 and - * 3, respectively, break_even_point/8 will always be at least 1. */ - if (match_maxlen > (break_even_point / 8)) { - LOG("-- best match: %u bytes at -%u\n", - match_maxlen, end - match_index); - *match_length = match_maxlen; - return end - match_index; - } - LOG("-- none found\n"); - return MATCH_NOT_FOUND; -} - -static uint8_t push_outgoing_bits(heatshrink_encoder *hse, output_info *oi) { - uint8_t count = 0; - uint8_t bits = 0; - if (hse->outgoing_bits_count > 8) { - count = 8; - bits = hse->outgoing_bits >> (hse->outgoing_bits_count - 8); - } else { - count = hse->outgoing_bits_count; - bits = hse->outgoing_bits; - } - - if (count > 0) { - LOG("-- pushing %d outgoing bits: 0x%02x\n", count, bits); - push_bits(hse, count, bits, oi); - hse->outgoing_bits_count -= count; - } - return count; -} - -/* Push COUNT (max 8) bits to the output buffer, which has room. - * Bytes are set from the lowest bits, up. */ -static void push_bits(heatshrink_encoder *hse, uint8_t count, uint8_t bits, - output_info *oi) { - ASSERT(count <= 8); - LOG("++ push_bits: %d bits, input of 0x%02x\n", count, bits); - - /* If adding a whole byte and at the start of a new output byte, - * just push it through whole and skip the bit IO loop. */ - if (count == 8 && hse->bit_index == 0x80) { - oi->buf[(*oi->output_size)++] = bits; - } else { - for (int i=count - 1; i>=0; i--) { - bool bit = bits & (1 << i); - if (bit) { hse->current_byte |= hse->bit_index; } - if (0) { - LOG(" -- setting bit %d at bit index 0x%02x, byte => 0x%02x\n", - bit ? 1 : 0, hse->bit_index, hse->current_byte); - } - hse->bit_index >>= 1; - if (hse->bit_index == 0x00) { - hse->bit_index = 0x80; - LOG(" > pushing byte 0x%02x\n", hse->current_byte); - oi->buf[(*oi->output_size)++] = hse->current_byte; - hse->current_byte = 0x00; - } - } - } -} - -static void push_literal_byte(heatshrink_encoder *hse, output_info *oi) { - uint16_t processed_offset = hse->match_scan_index - 1; - uint16_t input_offset = get_input_offset(hse) + processed_offset; - uint8_t c = hse->buffer[input_offset]; - LOG("-- yielded literal byte 0x%02x ('%c') from +%d\n", - c, isprint(c) ? c : '.', input_offset); - push_bits(hse, 8, c, oi); -} - -static void save_backlog(heatshrink_encoder *hse) { - size_t input_buf_sz = get_input_buffer_size(hse); - - uint16_t msi = hse->match_scan_index; - - /* Copy processed data to beginning of buffer, so it can be - * used for future matches. Don't bother checking whether the - * input is less than the maximum size, because if it isn't, - * we're done anyway. */ - uint16_t rem = input_buf_sz - msi; // unprocessed bytes - uint16_t shift_sz = input_buf_sz + rem; - - memmove(&hse->buffer[0], - &hse->buffer[input_buf_sz - rem], - shift_sz); - - hse->match_scan_index = 0; - hse->input_size -= input_buf_sz - rem; -} diff --git a/src/heatshrink/heatshrink_encoder.h b/src/heatshrink/heatshrink_encoder.h deleted file mode 100644 index 18c17731b1..0000000000 --- a/src/heatshrink/heatshrink_encoder.h +++ /dev/null @@ -1,109 +0,0 @@ -#ifndef HEATSHRINK_ENCODER_H -#define HEATSHRINK_ENCODER_H - -#include -#include -#include "heatshrink_common.h" -#include "heatshrink_config.h" - -typedef enum { - HSER_SINK_OK, /* data sunk into input buffer */ - HSER_SINK_ERROR_NULL=-1, /* NULL argument */ - HSER_SINK_ERROR_MISUSE=-2, /* API misuse */ -} HSE_sink_res; - -typedef enum { - HSER_POLL_EMPTY, /* input exhausted */ - HSER_POLL_MORE, /* poll again for more output */ - HSER_POLL_ERROR_NULL=-1, /* NULL argument */ - HSER_POLL_ERROR_MISUSE=-2, /* API misuse */ -} HSE_poll_res; - -typedef enum { - HSER_FINISH_DONE, /* encoding is complete */ - HSER_FINISH_MORE, /* more output remaining; use poll */ - HSER_FINISH_ERROR_NULL=-1, /* NULL argument */ -} HSE_finish_res; - -#if HEATSHRINK_DYNAMIC_ALLOC -#define HEATSHRINK_ENCODER_WINDOW_BITS(HSE) \ - ((HSE)->window_sz2) -#define HEATSHRINK_ENCODER_LOOKAHEAD_BITS(HSE) \ - ((HSE)->lookahead_sz2) -#define HEATSHRINK_ENCODER_INDEX(HSE) \ - ((HSE)->search_index) -struct hs_index { - uint16_t size; - int16_t index[]; -}; -#else -#define HEATSHRINK_ENCODER_WINDOW_BITS(_) \ - (HEATSHRINK_STATIC_WINDOW_BITS) -#define HEATSHRINK_ENCODER_LOOKAHEAD_BITS(_) \ - (HEATSHRINK_STATIC_LOOKAHEAD_BITS) -#define HEATSHRINK_ENCODER_INDEX(HSE) \ - (&(HSE)->search_index) -struct hs_index { - uint16_t size; - int16_t index[2 << HEATSHRINK_STATIC_WINDOW_BITS]; -}; -#endif - -typedef struct { - uint16_t input_size; /* bytes in input buffer */ - uint16_t match_scan_index; - uint16_t match_length; - uint16_t match_pos; - uint16_t outgoing_bits; /* enqueued outgoing bits */ - uint8_t outgoing_bits_count; - uint8_t flags; - uint8_t state; /* current state machine node */ - uint8_t current_byte; /* current byte of output */ - uint8_t bit_index; /* current bit index */ -#if HEATSHRINK_DYNAMIC_ALLOC - uint8_t window_sz2; /* 2^n size of window */ - uint8_t lookahead_sz2; /* 2^n size of lookahead */ -#if HEATSHRINK_USE_INDEX - struct hs_index *search_index; -#endif - /* input buffer and / sliding window for expansion */ - uint8_t buffer[]; -#else - #if HEATSHRINK_USE_INDEX - struct hs_index search_index; - #endif - /* input buffer and / sliding window for expansion */ - uint8_t buffer[2 << HEATSHRINK_ENCODER_WINDOW_BITS(_)]; -#endif -} heatshrink_encoder; - -#if HEATSHRINK_DYNAMIC_ALLOC -/* Allocate a new encoder struct and its buffers. - * Returns NULL on error. */ -heatshrink_encoder *heatshrink_encoder_alloc(uint8_t window_sz2, - uint8_t lookahead_sz2); - -/* Free an encoder. */ -void heatshrink_encoder_free(heatshrink_encoder *hse); -#endif - -/* Reset an encoder. */ -void heatshrink_encoder_reset(heatshrink_encoder *hse); - -/* Sink up to SIZE bytes from IN_BUF into the encoder. - * INPUT_SIZE is set to the number of bytes actually sunk (in case a - * buffer was filled.). */ -HSE_sink_res heatshrink_encoder_sink(heatshrink_encoder *hse, - uint8_t *in_buf, size_t size, size_t *input_size); - -/* Poll for output from the encoder, copying at most OUT_BUF_SIZE bytes into - * OUT_BUF (setting *OUTPUT_SIZE to the actual amount copied). */ -HSE_poll_res heatshrink_encoder_poll(heatshrink_encoder *hse, - uint8_t *out_buf, size_t out_buf_size, size_t *output_size); - -/* Notify the encoder that the input stream is finished. - * If the return value is HSER_FINISH_MORE, there is still more output, so - * call heatshrink_encoder_poll and repeat. */ -HSE_finish_res heatshrink_encoder_finish(heatshrink_encoder *hse); - -#endif From 793a2710dd9919027950a41a0dda25198f5583b2 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 3 Aug 2023 09:45:49 +0200 Subject: [PATCH 049/136] Change libbgcode git repo link to use https instead of git@ --- deps/LibBGCode/LibBGCode.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/LibBGCode/LibBGCode.cmake b/deps/LibBGCode/LibBGCode.cmake index 074adefffc..cfdcd009bd 100644 --- a/deps/LibBGCode/LibBGCode.cmake +++ b/deps/LibBGCode/LibBGCode.cmake @@ -1,6 +1,6 @@ set(LibBGCode_SOURCE_DIR "" CACHE PATH "Optionally specify local LibBGCode source directory") -set(_source_dir_line "GIT_REPOSITORY;git@github.com:prusa3d/libbgcode.git;GIT_TAG;main") +set(_source_dir_line "GIT_REPOSITORY;https://github.com/prusa3d/libbgcode.git;GIT_TAG;main") if (LibBGCode_SOURCE_DIR) set(_source_dir_line "SOURCE_DIR;${LibBGCode_SOURCE_DIR};BUILD_ALWAYS;ON") From c82f9c76273cdbcb4409cfadf07fb0186d0baf3f Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 3 Aug 2023 12:37:41 +0200 Subject: [PATCH 050/136] Removed code moved to libbgcode library --- src/libslic3r/CMakeLists.txt | 2 - src/libslic3r/GCode/GCodeBinarizer.cpp | 1976 ------------------------ src/libslic3r/GCode/GCodeBinarizer.hpp | 382 ----- 3 files changed, 2360 deletions(-) delete mode 100644 src/libslic3r/GCode/GCodeBinarizer.cpp delete mode 100644 src/libslic3r/GCode/GCodeBinarizer.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index c1dcd961cf..f11986d314 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -164,8 +164,6 @@ set(SLIC3R_SOURCES GCode/WipeTower.hpp GCode/GCodeProcessor.cpp GCode/GCodeProcessor.hpp - GCode/GCodeBinarizer.cpp - GCode/GCodeBinarizer.hpp GCode/AvoidCrossingPerimeters.cpp GCode/AvoidCrossingPerimeters.hpp GCode.cpp diff --git a/src/libslic3r/GCode/GCodeBinarizer.cpp b/src/libslic3r/GCode/GCodeBinarizer.cpp deleted file mode 100644 index d1e2368863..0000000000 --- a/src/libslic3r/GCode/GCodeBinarizer.cpp +++ /dev/null @@ -1,1976 +0,0 @@ -#include "GCodeBinarizer.hpp" - -#if ENABLE_BINARIZED_GCODE_DEBUG -#define NOMINMAX -#include -#include -#endif // ENABLE_BINARIZED_GCODE_DEBUG - -#if ENABLE_FILE_CONVERSION_INTERFACE -#include -#endif // ENABLE_FILE_CONVERSION_INTERFACE - -extern "C" { -#include -#include -} -#include - -#include -#include - -namespace bgcode { - -static size_t g_checksum_max_cache_size = 65536; -static constexpr const size_t MAX_GCODE_CACHE_SIZE = 65536; - -namespace MeatPack { -static constexpr const uint8_t Command_None{ 0 }; -//#Command_TogglePacking = 253 -- Currently unused, byte 253 can be reused later. -static constexpr const uint8_t Command_EnablePacking{ 251 }; -static constexpr const uint8_t Command_DisablePacking{ 250 }; -static constexpr const uint8_t Command_ResetAll{ 249 }; -static constexpr const uint8_t Command_QueryConfig{ 248 }; -static constexpr const uint8_t Command_EnableNoSpaces{ 247 }; -static constexpr const uint8_t Command_DisableNoSpaces{ 246 }; -static constexpr const uint8_t Command_SignalByte{ 0xFF }; - -static constexpr const uint8_t Flag_OmitWhitespaces{ 0x01 }; -static constexpr const uint8_t Flag_RemoveComments{ 0x02 }; - -static constexpr const uint8_t BothUnpackable{ 0b11111111 }; -static constexpr const char SpaceReplacedCharacter{ 'E' }; - -static const std::unordered_map ReverseLookupTbl = { - { '0', 0b00000000 }, - { '1', 0b00000001 }, - { '2', 0b00000010 }, - { '3', 0b00000011 }, - { '4', 0b00000100 }, - { '5', 0b00000101 }, - { '6', 0b00000110 }, - { '7', 0b00000111 }, - { '8', 0b00001000 }, - { '9', 0b00001001 }, - { '.', 0b00001010 }, - { ' ', 0b00001011 }, - { '\n', 0b00001100 }, - { 'G', 0b00001101 }, - { 'X', 0b00001110 }, - { '\0', 0b00001111 } // never used, 0b1111 is used to indicate the next 8-bits is a full character -}; - -class MPBinarizer -{ -public: - explicit MPBinarizer(uint8_t flags = 0) : m_flags(flags) {} - - void initialize(std::vector& dst) { - initialize_lookup_tables(); - append_command(Command_EnablePacking, dst); - m_binarizing = true; - } - - void finalize(std::vector& dst) { - if ((m_flags & Flag_RemoveComments) != 0) { - assert(m_binarizing); - append_command(Command_ResetAll, dst); - m_binarizing = false; - } - } - - void binarize_line(const std::string& line, std::vector& dst) { - auto unified_method = [this](const std::string& line) { - const std::string::size_type g_idx = line.find('G'); - if (g_idx != std::string::npos) { - if (g_idx + 1 < line.size() && line[g_idx + 1] >= '0' && line[g_idx + 1] <= '9') { - if ((m_flags & Flag_OmitWhitespaces) != 0) { - std::string result = line; - std::replace(result.begin(), result.end(), 'e', 'E'); - std::replace(result.begin(), result.end(), 'x', 'X'); - std::replace(result.begin(), result.end(), 'g', 'G'); - result.erase(std::remove(result.begin(), result.end(), ' '), result.end()); - if (result.find('*') != std::string::npos) { - size_t checksum = 0; - result.erase(std::remove(result.begin(), result.end(), '*'), result.end()); - for (size_t i = 0; i < result.size(); ++i) { - checksum ^= static_cast(result[i]); - } - result += "*" + std::to_string(checksum); - } - result += '\n'; - return result; - } - else { - std::string result = line; - std::replace(result.begin(), result.end(), 'x', 'X'); - std::replace(result.begin(), result.end(), 'g', 'G'); - result.erase(std::remove(result.begin(), result.end(), ' '), result.end()); - if (result.find('*') != std::string::npos) { - size_t checksum = 0; - result.erase(std::remove(result.begin(), result.end(), '*'), result.end()); - for (size_t i = 0; i < result.size(); ++i) { - checksum ^= static_cast(result[i]); - } - result += "*" + std::to_string(checksum); - } - result += '\n'; - return result; - } - } - } - return line; - }; - auto is_packable = [](char c) { - return (s_lookup_tables.packable[static_cast(c)] != 0); - }; - auto pack_chars = [](char low, char high) { - return (((s_lookup_tables.value[static_cast(high)] & 0xF) << 4) | - (s_lookup_tables.value[static_cast(low)] & 0xF)); - }; - - if (!line.empty()) { - if ((m_flags & Flag_RemoveComments) == 0) { - if (line[0] == ';') { - if (m_binarizing) { - append_command(Command_DisablePacking, dst); - m_binarizing = false; - } - - dst.insert(dst.end(), line.begin(), line.end()); - return; - } - } - - if (line[0] == ';' || - line[0] == '\n' || - line[0] == '\r' || - line.size() < 2) - return; - - std::string modifiedLine = line.substr(0, line.find(';')); - if (modifiedLine.empty()) - return; - auto trim_right = [](const std::string& str) { - if (str.back() != ' ') - return str; - auto bit = str.rbegin(); - while (bit != str.rend() && *bit == ' ') { - ++bit; - } - return str.substr(0, std::distance(str.begin(), bit.base())); - }; - modifiedLine = trim_right(modifiedLine); - modifiedLine = unified_method(modifiedLine); - if (modifiedLine.back() != '\n') - modifiedLine.push_back('\n'); - const size_t line_len = modifiedLine.size(); - std::vector temp_buffer; - temp_buffer.reserve(line_len); - - for (size_t line_idx = 0; line_idx < line_len; line_idx += 2) { - const bool skip_last = line_idx == (line_len - 1); - const char char_1 = modifiedLine[line_idx]; - const char char_2 = (skip_last ? '\n' : modifiedLine[line_idx + 1]); - const bool c1_p = is_packable(char_1); - const bool c2_p = is_packable(char_2); - - if (c1_p) { - if (c2_p) - temp_buffer.emplace_back(static_cast(pack_chars(char_1, char_2))); - else { - temp_buffer.emplace_back(static_cast(pack_chars(char_1, '\0'))); - temp_buffer.emplace_back(static_cast(char_2)); - } - } - else { - if (c2_p) { - temp_buffer.emplace_back(static_cast(pack_chars('\0', char_2))); - temp_buffer.emplace_back(static_cast(char_1)); - } - else { - temp_buffer.emplace_back(static_cast(BothUnpackable)); - temp_buffer.emplace_back(static_cast(char_1)); - temp_buffer.emplace_back(static_cast(char_2)); - } - } - } - - if (!m_binarizing && !temp_buffer.empty()) { - append_command(Command_EnablePacking, dst); - m_binarizing = true; - } - - dst.insert(dst.end(), temp_buffer.begin(), temp_buffer.end()); - } - } - -private: - unsigned char m_flags{ 0 }; - bool m_binarizing{ false }; - - struct LookupTables - { - std::array packable; - std::array value; - bool initialized; - unsigned char flags; - }; - - static LookupTables s_lookup_tables; - - void append_command(unsigned char cmd, std::vector& dst) { - dst.emplace_back(Command_SignalByte); - dst.emplace_back(Command_SignalByte); - dst.emplace_back(cmd); - } - - void initialize_lookup_tables() { - if (s_lookup_tables.initialized && m_flags == s_lookup_tables.flags) - return; - - for (const auto& [c, value] : ReverseLookupTbl) { - const int index = static_cast(c); - s_lookup_tables.packable[index] = 1; - s_lookup_tables.value[index] = value; - } - - if ((m_flags & Flag_OmitWhitespaces) != 0) { - s_lookup_tables.value[static_cast(SpaceReplacedCharacter)] = ReverseLookupTbl.at(' '); - s_lookup_tables.packable[static_cast(SpaceReplacedCharacter)] = 1; - s_lookup_tables.packable[static_cast(' ')] = 0; - } - else { - s_lookup_tables.packable[static_cast(SpaceReplacedCharacter)] = 0; - s_lookup_tables.packable[static_cast(' ')] = 1; - } - - s_lookup_tables.initialized = true; - s_lookup_tables.flags = m_flags; - } -}; - -MPBinarizer::LookupTables MPBinarizer::s_lookup_tables = { { 0 }, { 0 }, false, 0 }; - -static constexpr const unsigned char SecondNotPacked{ 0b11110000 }; -static constexpr const unsigned char FirstNotPacked{ 0b00001111 }; -static constexpr const unsigned char NextPackedFirst{ 0b00000001 }; -static constexpr const unsigned char NextPackedSecond{ 0b00000010 }; - -// See for reference: https://github.com/scottmudge/Prusa-Firmware-MeatPack/blob/MK3_sm_MeatPack/Firmware/meatpack.cpp -static void unbinarize(const std::vector& src, std::string& dst) { - bool unbinarizing = false; - bool cmd_active = false; // Is a command pending - uint8_t char_buf = 0; // Buffers a character if dealing with out-of-sequence pairs - size_t cmd_count = 0; // Counts how many command bytes are received (need 2) - size_t full_char_queue = 0; // Counts how many full-width characters are to be received - std::array char_out_buf; // Output buffer for caching up to 2 characters - size_t char_out_count = 0; // Stores number of characters to be read out - - auto handle_command = [&](uint8_t c) { - switch (c) - { - case Command_EnablePacking: { unbinarizing = true; break; } - case Command_DisablePacking: { unbinarizing = false; break; } - case Command_ResetAll: { unbinarizing = false; break; } - default: - case Command_QueryConfig: { break; } - } - }; - - auto handle_output_char = [&](uint8_t c) { - char_out_buf[char_out_count++] = c; - }; - - auto get_char = [](uint8_t c) { - switch (c) - { - case 0b0000: { return '0'; } - case 0b0001: { return '1'; } - case 0b0010: { return '2'; } - case 0b0011: { return '3'; } - case 0b0100: { return '4'; } - case 0b0101: { return '5'; } - case 0b0110: { return '6'; } - case 0b0111: { return '7'; } - case 0b1000: { return '8'; } - case 0b1001: { return '9'; } - case 0b1010: { return '.'; } - case 0b1011: { return 'E'; } - case 0b1100: { return '\n'; } - case 0b1101: { return 'G'; } - case 0b1110: { return 'X'; } - } - return '\0'; - }; - - auto unpack_chars = [&](uint8_t pk, std::array& chars_out) { - uint8_t out = 0; - - // If lower 4 bytes is 0b1111, the higher 4 are unused, and next char is full. - if ((pk & FirstNotPacked) == FirstNotPacked) - out |= NextPackedFirst; - else - chars_out[0] = get_char(pk & 0xF); // Assign lower char - - // Check if upper 4 bytes is 0b1111... if so, we don't need the second char. - if ((pk & SecondNotPacked) == SecondNotPacked) - out |= NextPackedSecond; - else - chars_out[1] = get_char((pk >> 4) & 0xF); // Assign upper char - - return out; - }; - - auto handle_rx_char = [&](uint8_t c) { - if (unbinarizing) { - if (full_char_queue > 0) { - handle_output_char(c); - if (char_buf > 0) { - handle_output_char(char_buf); - char_buf = 0; - } - --full_char_queue; - } - else { - std::array buf = { 0, 0 }; - const uint8_t res = unpack_chars(c, buf); - - if ((res & NextPackedFirst) != 0) { - ++full_char_queue; - if ((res & NextPackedSecond) != 0) - ++full_char_queue; - else - char_buf = buf[1]; - } - else { - handle_output_char(buf[0]); - if (buf[0] != '\n') { - if ((res & NextPackedSecond) != 0) - ++full_char_queue; - else - handle_output_char(buf[1]); - } - } - } - } - else // Packing not enabled, just copy character to output - handle_output_char(c); - }; - - auto get_result_char = [&](std::array& chars_out) { - if (char_out_count > 0) { - const size_t res = char_out_count; - for (uint8_t i = 0; i < char_out_count; ++i) { - chars_out[i] = (char)char_out_buf[i]; - } - char_out_count = 0; - return res; - } - return (size_t)0; - }; - - std::vector unbin_buffer(2 * src.size(), 0); - auto it_unbin_end = unbin_buffer.begin(); - -#if ENABLE_BINARIZED_GCODE_DEBUG - size_t line_start = 0; -#endif // ENABLE_BINARIZED_GCODE_DEBUG - bool add_space = false; - - auto begin = src.begin(); - auto end = src.end(); - - auto it_bin = begin; - while (it_bin != end) { - uint8_t c_bin = *it_bin; - if (c_bin == Command_SignalByte) { - if (cmd_count > 0) { - cmd_active = true; - cmd_count = 0; - } - else - ++cmd_count; - } - else { - if (cmd_active) { - handle_command(c_bin); - cmd_active = false; - } - else { - if (cmd_count > 0) { - handle_rx_char(Command_SignalByte); - cmd_count = 0; - } - - handle_rx_char(c_bin); - } - } - - auto is_gline_parameter = [](const char c) { - static const std::vector parameters = { - // G0, G1 - 'X', 'Y', 'Z', 'E', 'F', - // G2, G3 - 'I', 'J', 'R', - // G29 - 'P', 'W', 'H', 'C', 'A' - }; - return std::find(parameters.begin(), parameters.end(), c) != parameters.end(); - }; - - std::array c_unbin{ 0, 0 }; - const size_t char_count = get_result_char(c_unbin); - for (size_t i = 0; i < char_count; ++i) { - // GCodeReader::parse_line_internal() is unable to parse a G line where the data are not separated by spaces - // so we add them where needed - const size_t curr_unbin_buffer_length = std::distance(unbin_buffer.begin(), it_unbin_end); - if (c_unbin[i] == 'G' && (curr_unbin_buffer_length == 0 || *std::prev(it_unbin_end, 1) == '\n')) - add_space = true; - else if (c_unbin[i] == '\n') - add_space = false; - - if (add_space && (curr_unbin_buffer_length == 0 || *std::prev(it_unbin_end, 1) != ' ') && - is_gline_parameter(c_unbin[i])) { - *it_unbin_end = ' '; - ++it_unbin_end; - } - - if (c_unbin[i] != '\n' || std::distance(unbin_buffer.begin(), it_unbin_end) == 0 || *std::prev(it_unbin_end, 1) != '\n') { - *it_unbin_end = c_unbin[i]; - ++it_unbin_end; - } - -#if ENABLE_BINARIZED_GCODE_DEBUG - if (c_unbin[i] == '\n') { - const std::string out(unbin_buffer.begin() + line_start, it_unbin_end); - if (!out.empty()) { - OutputDebugStringA(out.c_str()); - line_start = std::distance(unbin_buffer.begin(), it_unbin_end); - } - } -#endif // ENABLE_BINARIZED_GCODE_DEBUG - } - - ++it_bin; - } - - dst.insert(dst.end(), unbin_buffer.begin(), it_unbin_end); -} -} // namespace MeatPack - -std::string translate_result(EResult result) -{ - switch (result) - { - case EResult::Success: { return "Success"; } - case EResult::ReadError: { return "Read error"; } - case EResult::WriteError: { return "Write error"; } - case EResult::InvalidMagicNumber: { return "Invalid magic number"; } - case EResult::InvalidVersionNumber: { return "Invalid version number"; } - case EResult::InvalidChecksumType: { return "Invalid checksum type"; } - case EResult::InvalidBlockType: { return "Invalid block type"; } - case EResult::InvalidCompressionType: { return "Invalid compression type"; } - case EResult::InvalidMetadataEncodingType: { return "Invalid metadata encoding type"; } - case EResult::InvalidGCodeEncodingType: { return "Invalid gcode encoding type"; } - case EResult::DataCompressionError: { return "Data compression error"; } - case EResult::DataUncompressionError: { return "Data uncompression error"; } - case EResult::MetadataEncodingError: { return "Data encoding error"; } - case EResult::MetadataDecodingError: { return "Data decoding error"; } - case EResult::GCodeEncodingError: { return "GCode encoding error"; } - case EResult::GCodeDecodingError: { return "GCode decoding error"; } - case EResult::BlockNotFound: { return "Block not found"; } - case EResult::InvalidChecksum: { return "Invalid checksum"; } - case EResult::InvalidThumbnailFormat: { return "Invalid thumbnail format"; } - case EResult::InvalidThumbnailWidth: { return "Invalid thumbnail width"; } - case EResult::InvalidThumbnailHeight: { return "Invalid thumbnail height"; } - case EResult::InvalidThumbnailDataSize: { return "Invalid thumbnail data size"; } - case EResult::InvalidBinaryGCodeFile: { return "Invalid binary GCode file"; } - case EResult::InvalidSequenceOfBlocks: { return "Invalid sequence of blocks"; } - } - return std::string(); -} - -size_t get_checksum_max_cache_size() { return g_checksum_max_cache_size; } -void set_checksum_max_cache_size(size_t size) { g_checksum_max_cache_size = size; } - -static uint16_t checksum_types_count() { return 1 + (uint16_t)EChecksumType::CRC32; } -static uint16_t block_types_count() { return 1 + (uint16_t)EBlockType::Thumbnail; } -static uint16_t compression_types_count() { return 1 + (uint16_t)ECompressionType::Heatshrink_12_4; } -static uint16_t thumbnail_formats_count() { return 1 + (uint16_t)EThumbnailFormat::QOI; } -static uint16_t metadata_encoding_types_count() { return 1 + (uint16_t)EMetadataEncodingType::INI; } -static uint16_t gcode_encoding_types_count() { return 1 + (uint16_t)EGCodeEncodingType::MeatPackComments; } - -static bool write_to_file(FILE& file, const void* data, size_t data_size) -{ - fwrite(data, 1, data_size, &file); - return !ferror(&file); -} - -static bool read_from_file(FILE& file, void* data, size_t data_size) -{ - fread(data, 1, data_size, &file); - return !ferror(&file); -} - -static bool encode_metadata(const std::vector>& src, std::vector& dst, - EMetadataEncodingType encoding_type) -{ - for (const auto& [key, value] : src) { - switch (encoding_type) - { - case EMetadataEncodingType::INI: - { - dst.insert(dst.end(), key.begin(), key.end()); - dst.emplace_back('='); - dst.insert(dst.end(), value.begin(), value.end()); - dst.emplace_back('\n'); - break; - } - } - } - return true; -} - -static bool decode_metadata(const std::vector& src, std::vector>& dst, - EMetadataEncodingType encoding_type) -{ - switch (encoding_type) - { - case EMetadataEncodingType::INI: - { - auto begin_it = src.begin(); - auto end_it = src.begin(); - while (end_it != src.end()) { - while (end_it != src.end() && *end_it != '\n') { - ++end_it; - } - const std::string item(begin_it, end_it); - const size_t pos = item.find_first_of('='); - if (pos != std::string::npos) { - dst.emplace_back(std::make_pair(item.substr(0, pos), item.substr(pos + 1))); - begin_it = ++end_it; - } - } - break; - } - } - - return true; -} - -static bool encode_gcode(const std::string& src, std::vector& dst, EGCodeEncodingType encoding_type) -{ - switch (encoding_type) - { - case EGCodeEncodingType::None: - { - dst.insert(dst.end(), src.begin(), src.end()); - break; - } - case EGCodeEncodingType::MeatPack: - case EGCodeEncodingType::MeatPackComments: - { - uint8_t binarizer_flags = (encoding_type == EGCodeEncodingType::MeatPack) ? MeatPack::Flag_RemoveComments : 0; - binarizer_flags |= MeatPack::Flag_OmitWhitespaces; - MeatPack::MPBinarizer binarizer(binarizer_flags); - binarizer.initialize(dst); - auto begin_it = src.begin(); - auto end_it = src.begin(); - while (end_it != src.end()) { - while (end_it != src.end() && *end_it != '\n') { - ++end_it; - } - const std::string line(begin_it, ++end_it); - binarizer.binarize_line(line, dst); - begin_it = end_it; - } - binarizer.finalize(dst); - break; - } - } - return true; -} - -static bool decode_gcode(const std::vector& src, std::string& dst, EGCodeEncodingType encoding_type) -{ - switch (encoding_type) - { - case EGCodeEncodingType::None: - { - dst.insert(dst.end(), src.begin(), src.end()); - break; - } - case EGCodeEncodingType::MeatPack: - case EGCodeEncodingType::MeatPackComments: - { - MeatPack::unbinarize(src, dst); - break; - } - } - return true; -} - -static bool compress(const std::vector& src, std::vector& dst, ECompressionType compression_type) -{ - switch (compression_type) - { - case ECompressionType::Deflate: - { - dst.clear(); - - const size_t BUFSIZE = 2048; - std::vector temp_buffer(BUFSIZE); - - z_stream strm{}; - strm.next_in = const_cast(src.data()); - strm.avail_in = (uInt)src.size(); - strm.next_out = temp_buffer.data(); - strm.avail_out = BUFSIZE; - - const int level = Z_DEFAULT_COMPRESSION; - int res = deflateInit(&strm, level); - if (res != Z_OK) - return false; - - while (strm.avail_in > 0) { - res = deflate(&strm, Z_NO_FLUSH); - if (res != Z_OK) { - deflateEnd(&strm); - return false; - } - if (strm.avail_out == 0) { - dst.insert(dst.end(), temp_buffer.data(), temp_buffer.data() + BUFSIZE); - strm.next_out = temp_buffer.data(); - strm.avail_out = BUFSIZE; - } - } - - int deflate_res = Z_OK; - while (deflate_res == Z_OK) { - if (strm.avail_out == 0) { - dst.insert(dst.end(), temp_buffer.data(), temp_buffer.data() + BUFSIZE); - strm.next_out = temp_buffer.data(); - strm.avail_out = BUFSIZE; - } - deflate_res = deflate(&strm, Z_FINISH); - } - - if (deflate_res != Z_STREAM_END) { - deflateEnd(&strm); - return false; - } - - dst.insert(dst.end(), temp_buffer.data(), temp_buffer.data() + BUFSIZE - strm.avail_out); - deflateEnd(&strm); - break; - } - case ECompressionType::Heatshrink_11_4: - case ECompressionType::Heatshrink_12_4: - { - const uint8_t window_sz = (compression_type == ECompressionType::Heatshrink_11_4) ? 11 : 12; - const uint8_t lookahead_sz = 4; - heatshrink_encoder* encoder = heatshrink_encoder_alloc(window_sz, lookahead_sz); - if (encoder == nullptr) - return false; - - // calculate the maximum compressed size (assuming a conservative estimate) - const size_t src_size = src.size(); - const size_t max_compressed_size = src_size + (src_size >> 2); - dst.resize(max_compressed_size); - - uint8_t* buf = const_cast(src.data()); - uint8_t* outbuf = dst.data(); - - // compress data - size_t tosink = src_size; - size_t output_size = 0; - while (tosink > 0) { - size_t sunk = 0; - const HSE_sink_res sink_res = heatshrink_encoder_sink(encoder, buf, tosink, &sunk); - if (sink_res != HSER_SINK_OK) { - heatshrink_encoder_free(encoder); - return false; - } - if (sunk == 0) - // all input data processed - break; - - tosink -= sunk; - buf += sunk; - - size_t polled = 0; - const HSE_poll_res poll_res = heatshrink_encoder_poll(encoder, outbuf + output_size, max_compressed_size - output_size, &polled); - if (poll_res < 0) { - heatshrink_encoder_free(encoder); - return false; - } - output_size += polled; - } - - // input data finished - const HSE_finish_res finish_res = heatshrink_encoder_finish(encoder); - if (finish_res < 0) { - heatshrink_encoder_free(encoder); - return false; - } - - // poll for final output - size_t polled = 0; - const HSE_poll_res poll_res = heatshrink_encoder_poll(encoder, outbuf + output_size, max_compressed_size - output_size, &polled); - if (poll_res < 0) { - heatshrink_encoder_free(encoder); - return false; - } - dst.resize(output_size + polled); - heatshrink_encoder_free(encoder); - break; - } - case ECompressionType::None: - default: - { - break; - } - } - - return true; -} - -static bool uncompress(const std::vector& src, std::vector& dst, ECompressionType compression_type, size_t uncompressed_size) -{ - switch (compression_type) - { - case ECompressionType::Deflate: - { - dst.clear(); - dst.reserve(uncompressed_size); - - const size_t BUFSIZE = 2048; - std::vector temp_buffer(BUFSIZE); - - z_stream strm{}; - strm.next_in = const_cast(src.data()); - strm.avail_in = (uInt)src.size(); - strm.next_out = temp_buffer.data(); - strm.avail_out = BUFSIZE; - int res = inflateInit(&strm); - if (res != Z_OK) - return false; - - while (strm.avail_in > 0) { - res = inflate(&strm, Z_NO_FLUSH); - if (res != Z_OK && res != Z_STREAM_END) { - inflateEnd(&strm); - return false; - } - if (strm.avail_out == 0) { - dst.insert(dst.end(), temp_buffer.data(), temp_buffer.data() + BUFSIZE); - strm.next_out = temp_buffer.data(); - strm.avail_out = BUFSIZE; - } - } - - int inflate_res = Z_OK; - while (inflate_res == Z_OK) { - if (strm.avail_out == 0) { - dst.insert(dst.end(), temp_buffer.data(), temp_buffer.data() + BUFSIZE); - strm.next_out = temp_buffer.data(); - strm.avail_out = BUFSIZE; - } - inflate_res = inflate(&strm, Z_FINISH); - } - - if (inflate_res != Z_STREAM_END) { - inflateEnd(&strm); - return false; - } - - dst.insert(dst.end(), temp_buffer.data(), temp_buffer.data() + BUFSIZE - strm.avail_out); - inflateEnd(&strm); - break; - } - case ECompressionType::Heatshrink_11_4: - case ECompressionType::Heatshrink_12_4: - { - const uint8_t window_sz = (compression_type == ECompressionType::Heatshrink_11_4) ? 11 : 12; - const uint8_t lookahead_sz = 4; - const uint16_t input_buffer_size = 2048; - heatshrink_decoder* decoder = heatshrink_decoder_alloc(input_buffer_size, window_sz, lookahead_sz); - if (decoder == nullptr) - return false; - - dst.resize(uncompressed_size); - - uint8_t* buf = const_cast(src.data()); - uint8_t* outbuf = dst.data(); - - uint32_t sunk = 0; - uint32_t polled = 0; - - const size_t compressed_size = src.size(); - while (sunk < compressed_size) { - size_t count = 0; - const HSD_sink_res sink_res = heatshrink_decoder_sink(decoder, &buf[sunk], compressed_size - sunk, &count); - if (sink_res < 0) { - heatshrink_decoder_free(decoder); - return false; - } - - sunk += (uint32_t)count; - - HSD_poll_res poll_res; - do { - poll_res = heatshrink_decoder_poll(decoder, &outbuf[polled], uncompressed_size - polled, &count); - if (poll_res < 0) { - heatshrink_decoder_free(decoder); - return false; - } - polled += (uint32_t)count; - } while (polled < uncompressed_size && poll_res == HSDR_POLL_MORE); - } - - const HSD_finish_res finish_res = heatshrink_decoder_finish(decoder); - if (finish_res < 0) { - heatshrink_decoder_free(decoder); - return false; - } - - heatshrink_decoder_free(decoder); - break; - } - case ECompressionType::None: - default: - { - break; - } - } - - return true; -} - -static uint32_t crc32_sw(const uint8_t* buffer, uint32_t length, uint32_t crc) -{ - uint32_t value = crc ^ 0xFFFFFFFF; - while (length--) { - value ^= (uint32_t)*buffer++; - for (int bit = 0; bit < 8; bit++) { - if (value & 1) - value = (value >> 1) ^ 0xEDB88320; - else - value >>= 1; - } - } - value ^= 0xFFFFFFFF; - return value; -} - -std::vector encode(const void* data, size_t data_size) -{ - std::vector ret(data_size); - memcpy(ret.data(), data, data_size); - return ret; -} - -Checksum::Checksum(EChecksumType type) -: m_type(type) -{ - if (m_type != EChecksumType::None) - m_checksum = std::vector(checksum_size(m_type), '\0'); -} - -EChecksumType Checksum::get_type() const -{ - return m_type; -} - -void Checksum::append(const std::vector& data) -{ - size_t remaining_data_size = std::distance(data.begin(), data.end()); - auto it_begin = data.begin(); - while (remaining_data_size + m_cache.size() > g_checksum_max_cache_size) { - update(); - if (remaining_data_size > g_checksum_max_cache_size) { - m_cache.insert(m_cache.end(), it_begin, it_begin + g_checksum_max_cache_size); - it_begin += g_checksum_max_cache_size; - remaining_data_size -= g_checksum_max_cache_size; - } - } - - m_cache.insert(m_cache.end(), it_begin, data.end()); -} - -bool Checksum::matches(Checksum& other) -{ - update(); - other.update(); - return m_checksum == other.m_checksum; -} - -EResult Checksum::write(FILE& file) -{ - if (m_type != EChecksumType::None) { - update(); - if (!write_to_file(file, (const void*)m_checksum.data(), m_checksum.size())) - return EResult::WriteError; - } - return EResult::Success; -} - -EResult Checksum::read(FILE& file) -{ - if (m_type != EChecksumType::None) { - if (!read_from_file(file, (void*)m_checksum.data(), m_checksum.size())) - return EResult::ReadError; - } - return EResult::Success; -} - -void Checksum::update() -{ - if (m_cache.empty()) - return; - - switch (m_type) - { - case EChecksumType::None: - { - break; - } - case EChecksumType::CRC32: - { - const uint32_t old_crc = *(uint32_t*)m_checksum.data(); - const uint32_t new_crc = crc32_sw(m_cache.data(), (uint32_t)m_cache.size(), old_crc); - *(uint32_t*)m_checksum.data() = new_crc; - break; - } - } - - m_cache.clear(); -} - -EResult FileHeader::write(FILE& file) const -{ - if (magic != *(uint32_t*)(MAGIC.data())) - return EResult::InvalidMagicNumber; - if (checksum_type >= checksum_types_count()) - return EResult::InvalidChecksumType; - - if (!write_to_file(file, (const void*)&magic, sizeof(magic))) - return EResult::WriteError; - if (!write_to_file(file, (const void*)&version, sizeof(version))) - return EResult::WriteError; - if (!write_to_file(file, (const void*)&checksum_type, sizeof(checksum_type))) - return EResult::WriteError; - - return EResult::Success; -} - -EResult FileHeader::read(FILE& file, const uint32_t* const max_version) -{ - if (!read_from_file(file, (void*)&magic, sizeof(magic))) - return EResult::ReadError; - if (magic != *(uint32_t*)(MAGIC.data())) - return EResult::InvalidMagicNumber; - - if (!read_from_file(file, (void*)&version, sizeof(version))) - return EResult::ReadError; - if (max_version != nullptr && version > *max_version) - return EResult::InvalidVersionNumber; - - if (!read_from_file(file, (void*)&checksum_type, sizeof(checksum_type))) - return EResult::ReadError; - if (checksum_type >= checksum_types_count()) - return EResult::InvalidChecksumType; - - return EResult::Success; -} - -void BlockHeader::update_checksum(Checksum& checksum) const -{ - checksum.append(encode((const void*)&type, sizeof(type))); - checksum.append(encode((const void*)&compression, sizeof(compression))); - checksum.append(encode((const void*)&uncompressed_size, sizeof(uncompressed_size))); - if (compression != (uint16_t)ECompressionType::None) - checksum.append(encode((const void*)&compressed_size, sizeof(compressed_size))); -} - -EResult BlockHeader::write(FILE& file) const -{ - if (!write_to_file(file, (const void*)&type, sizeof(type))) - return EResult::WriteError; - if (!write_to_file(file, (const void*)&compression, sizeof(compression))) - return EResult::WriteError; - if (!write_to_file(file, (const void*)&uncompressed_size, sizeof(uncompressed_size))) - return EResult::WriteError; - if (compression != (uint16_t)ECompressionType::None) { - if (!write_to_file(file, (const void*)&compressed_size, sizeof(compressed_size))) - return EResult::WriteError; - } - return EResult::Success; -} - -EResult BlockHeader::read(FILE& file) -{ - if (!read_from_file(file, (void*)&type, sizeof(type))) - return EResult::ReadError; - if (type >= block_types_count()) - return EResult::InvalidBlockType; - - if (!read_from_file(file, (void*)&compression, sizeof(compression))) - return EResult::ReadError; - if (compression >= compression_types_count()) - return EResult::InvalidCompressionType; - - if (!read_from_file(file, (void*)&uncompressed_size, sizeof(uncompressed_size))) - return EResult::ReadError; - if (compression != (uint16_t)ECompressionType::None) { - if (!read_from_file(file, (void*)&compressed_size, sizeof(compressed_size))) - return EResult::ReadError; - } - - return EResult::Success; -} - -EResult BaseMetadataBlock::write(FILE& file, EBlockType block_type, ECompressionType compression_type, Checksum& checksum) const -{ - if (encoding_type > metadata_encoding_types_count()) - return EResult::InvalidMetadataEncodingType; - - BlockHeader block_header = { (uint16_t)block_type, (uint16_t)compression_type, (uint32_t)0 }; - std::vector out_data; - if (!raw_data.empty()) { - // process payload encoding - std::vector uncompressed_data; - if (!encode_metadata(raw_data, uncompressed_data, (EMetadataEncodingType)encoding_type)) - return EResult::MetadataEncodingError; - // process payload compression - block_header.uncompressed_size = (uint32_t)uncompressed_data.size(); - std::vector compressed_data; - if (compression_type != ECompressionType::None) { - if (!compress(uncompressed_data, compressed_data, compression_type)) - return EResult::DataCompressionError; - block_header.compressed_size = (uint32_t)compressed_data.size(); - } - out_data.swap((compression_type == ECompressionType::None) ? uncompressed_data : compressed_data); - } - - // write block header - EResult res = block_header.write(file); - if (res != EResult::Success) - // propagate error - return res; - - // write block payload - if (!write_to_file(file, (const void*)&encoding_type, sizeof(encoding_type))) - return EResult::WriteError; - if (!out_data.empty()) { - if (!write_to_file(file, (const void*)out_data.data(), out_data.size())) - return EResult::WriteError; - } - - if (checksum.get_type() != EChecksumType::None) { - // update checksum with block header - block_header.update_checksum(checksum); - // update checksum with block payload - checksum.append(encode((const void*)&encoding_type, sizeof(encoding_type))); - if (!out_data.empty()) - checksum.append(out_data); - } - - return EResult::Success; -} - -EResult BaseMetadataBlock::read_data(FILE& file, const BlockHeader& block_header) -{ - const ECompressionType compression_type = (ECompressionType)block_header.compression; - - if (!read_from_file(file, (void*)&encoding_type, sizeof(encoding_type))) - return EResult::ReadError; - if (encoding_type > metadata_encoding_types_count()) - return EResult::InvalidMetadataEncodingType; - - std::vector data; - const size_t data_size = (compression_type == ECompressionType::None) ? block_header.uncompressed_size : block_header.compressed_size; - if (data_size > 0) { - data.resize(data_size); - if (!read_from_file(file, (void*)data.data(), data_size)) - return EResult::ReadError; - } - - std::vector uncompressed_data; - if (compression_type != ECompressionType::None) { - if (!uncompress(data, uncompressed_data, compression_type, block_header.uncompressed_size)) - return EResult::DataUncompressionError; - } - - if (!decode_metadata((compression_type == ECompressionType::None) ? data : uncompressed_data, raw_data, (EMetadataEncodingType)encoding_type)) - return EResult::MetadataDecodingError; - - return EResult::Success; -} - -EResult FileMetadataBlock::write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const -{ - Checksum cs(checksum_type); - - // write block header, payload - EResult res = BaseMetadataBlock::write(file, EBlockType::FileMetadata, compression_type, cs); - if (res != EResult::Success) - // propagate error - return res; - - // write block checksum - if (checksum_type != EChecksumType::None) - return cs.write(file); - - return EResult::Success; -} - -EResult FileMetadataBlock::read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header) -{ - // read block payload - EResult res = BaseMetadataBlock::read_data(file, block_header); - if (res != EResult::Success) - // propagate error - return res; - - const EChecksumType checksum_type = (EChecksumType)file_header.checksum_type; - if (checksum_type != EChecksumType::None) { - // read block checksum - Checksum cs(checksum_type); - res = cs.read(file); - if (res != EResult::Success) - // propagate error - return res; - } - - return EResult::Success; -} - -EResult ThumbnailBlock::write(FILE& file, EChecksumType checksum_type) const -{ - if (format >= thumbnail_formats_count()) - return EResult::InvalidThumbnailFormat; - if (width == 0) - return EResult::InvalidThumbnailWidth; - if (height == 0) - return EResult::InvalidThumbnailHeight; - if (data.size() == 0) - return EResult::InvalidThumbnailDataSize; - - // write block header - const BlockHeader block_header = { (uint16_t)EBlockType::Thumbnail, (uint16_t)ECompressionType::None, (uint32_t)data.size() }; - EResult res = block_header.write(file); - if (res != EResult::Success) - // propagate error - return res; - - // write block payload - if (!write_to_file(file, (const void*)&format, sizeof(format))) - return EResult::WriteError; - if (!write_to_file(file, (const void*)&width, sizeof(width))) - return EResult::WriteError; - if (!write_to_file(file, (const void*)&height, sizeof(height))) - return EResult::WriteError; - if (!write_to_file(file, (const void*)data.data(), data.size())) - return EResult::WriteError; - - if (checksum_type != EChecksumType::None) { - Checksum cs(checksum_type); - // update checksum with block header - block_header.update_checksum(cs); - // update checksum with block payload - update_checksum(cs); - // write block checksum - res = cs.write(file); - if (res != EResult::Success) - // propagate error - return res; - } - - return EResult::Success; -} - -EResult ThumbnailBlock::read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header) -{ - // read block payload - if (!read_from_file(file, (void*)&format, sizeof(format))) - return EResult::ReadError; - if (format >= thumbnail_formats_count()) - return EResult::InvalidThumbnailFormat; - if (!read_from_file(file, (void*)&width, sizeof(width))) - return EResult::ReadError; - if (width == 0) - return EResult::InvalidThumbnailWidth; - if (!read_from_file(file, (void*)&height, sizeof(height))) - return EResult::ReadError; - if (height == 0) - return EResult::InvalidThumbnailHeight; - if (block_header.uncompressed_size == 0) - return EResult::InvalidThumbnailDataSize; - - data.resize(block_header.uncompressed_size); - if (!read_from_file(file, (void*)data.data(), block_header.uncompressed_size)) - return EResult::ReadError; - - const EChecksumType checksum_type = (EChecksumType)file_header.checksum_type; - if (checksum_type != EChecksumType::None) { - // read block checksum - Checksum cs(checksum_type); - const EResult res = cs.read(file); - if (res != EResult::Success) - // propagate error - return res; - } - - return EResult::Success; -} - -void ThumbnailBlock::update_checksum(Checksum& checksum) const -{ - checksum.append(encode((const void*)&format, sizeof(format))); - checksum.append(encode((const void*)&width, sizeof(width))); - checksum.append(encode((const void*)&height, sizeof(height))); - checksum.append(data); -} - -EResult PrinterMetadataBlock::write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const -{ - Checksum cs(checksum_type); - - // write block header, payload - EResult res = BaseMetadataBlock::write(file, EBlockType::PrinterMetadata, compression_type, cs); - if (res != EResult::Success) - // propagate error - return res; - - // write block checksum - if (checksum_type != EChecksumType::None) - return cs.write(file); - - return EResult::Success; -} - -EResult PrinterMetadataBlock::read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header) -{ - // read block payload - EResult res = BaseMetadataBlock::read_data(file, block_header); - if (res != EResult::Success) - // propagate error - return res; - - const EChecksumType checksum_type = (EChecksumType)file_header.checksum_type; - if (checksum_type != EChecksumType::None) { - // read block checksum - Checksum cs(checksum_type); - res = cs.read(file); - if (res != EResult::Success) - // propagate error - return res; - } - - return EResult::Success; -} - -EResult PrintMetadataBlock::write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const -{ - Checksum cs(checksum_type); - - // write block header, payload - EResult res = BaseMetadataBlock::write(file, EBlockType::PrintMetadata, compression_type, cs); - if (res != EResult::Success) - // propagate error - return res; - - // write block checksum - if (checksum_type != EChecksumType::None) - return cs.write(file); - - return EResult::Success; -} - -EResult PrintMetadataBlock::read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header) -{ - // read block payload - EResult res = BaseMetadataBlock::read_data(file, block_header); - if (res != EResult::Success) - // propagate error - return res; - - const EChecksumType checksum_type = (EChecksumType)file_header.checksum_type; - if (checksum_type != EChecksumType::None) { - // read block checksum - Checksum cs(checksum_type); - res = cs.read(file); - if (res != EResult::Success) - // propagate error - return res; - } - - return EResult::Success; -} - -EResult SlicerMetadataBlock::write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const -{ - Checksum cs(checksum_type); - - // write block header, payload - EResult res = BaseMetadataBlock::write(file, EBlockType::SlicerMetadata, compression_type, cs); - if (res != EResult::Success) - // propagate error - return res; - - // write block checksum - if (checksum_type != EChecksumType::None) - return cs.write(file); - - return EResult::Success; -} - -EResult SlicerMetadataBlock::read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header) -{ - // read block payload - EResult res = BaseMetadataBlock::read_data(file, block_header); - if (res != EResult::Success) - // propagate error - return res; - - const EChecksumType checksum_type = (EChecksumType)file_header.checksum_type; - if (checksum_type != EChecksumType::None) { - // read block checksum - Checksum cs(checksum_type); - res = cs.read(file); - if (res != EResult::Success) - // propagate error - return res; - } - - return EResult::Success; -} - -EResult GCodeBlock::write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const -{ - if (encoding_type > gcode_encoding_types_count()) - return EResult::InvalidGCodeEncodingType; - - BlockHeader block_header = { (uint16_t)EBlockType::GCode, (uint16_t)compression_type, (uint32_t)0 }; - std::vector out_data; - if (!raw_data.empty()) { - // process payload encoding - std::vector uncompressed_data; - if (!encode_gcode(raw_data, uncompressed_data, (EGCodeEncodingType)encoding_type)) - return EResult::GCodeEncodingError; - // process payload compression - block_header.uncompressed_size = (uint32_t)uncompressed_data.size(); - std::vector compressed_data; - if (compression_type != ECompressionType::None) { - if (!compress(uncompressed_data, compressed_data, compression_type)) - return EResult::DataCompressionError; - block_header.compressed_size = (uint32_t)compressed_data.size(); - } - out_data.swap((compression_type == ECompressionType::None) ? uncompressed_data : compressed_data); - } - - // write block header - EResult res = block_header.write(file); - if (res != EResult::Success) - // propagate error - return res; - - // write block payload - if (!write_to_file(file, (const void*)&encoding_type, sizeof(encoding_type))) - return EResult::WriteError; - if (!out_data.empty()) { -#if ENABLE_BINARIZED_GCODE_DEBUG - const std::string out = "GCodeBlock data size:" + std::to_string(out_data.size()) + "\n"; - OutputDebugStringA(out.c_str()); -#endif // ENABLE_BINARIZED_GCODE_DEBUG - if (!write_to_file(file, (const void*)out_data.data(), out_data.size())) - return EResult::WriteError; - } - - // write checksum - if (checksum_type != EChecksumType::None) { - Checksum cs(checksum_type); - // update checksum with block header - block_header.update_checksum(cs); - // update checksum with block payload - cs.append(encode((const void*)&encoding_type, sizeof(encoding_type))); - if (!out_data.empty()) - cs.append(out_data); - res = cs.write(file); - if (res != EResult::Success) - // propagate error - return res; - } - - return EResult::Success; -} - -EResult GCodeBlock::read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header) -{ - const ECompressionType compression_type = (ECompressionType)block_header.compression; - - if (!read_from_file(file, (void*)&encoding_type, sizeof(encoding_type))) - return EResult::ReadError; - if (encoding_type > gcode_encoding_types_count()) - return EResult::InvalidGCodeEncodingType; - - std::vector data; - const size_t data_size = (compression_type == ECompressionType::None) ? block_header.uncompressed_size : block_header.compressed_size; - if (data_size > 0) { - data.resize(data_size); - if (!read_from_file(file, (void*)data.data(), data_size)) - return EResult::ReadError; - } - - std::vector uncompressed_data; - if (compression_type != ECompressionType::None) { - if (!uncompress(data, uncompressed_data, compression_type, block_header.uncompressed_size)) - return EResult::DataUncompressionError; - } - - if (!decode_gcode((compression_type == ECompressionType::None) ? data : uncompressed_data, raw_data, (EGCodeEncodingType)encoding_type)) - return EResult::GCodeDecodingError; - - const EChecksumType checksum_type = (EChecksumType)file_header.checksum_type; - if (checksum_type != EChecksumType::None) { - // read block checksum - Checksum cs(checksum_type); - const EResult res = cs.read(file); - if (res != EResult::Success) - // propagate error - return res; - } - - return EResult::Success; -} - -#if ENABLE_CHECKSUM_BLOCK -EResult ChecksumBlock::write(FILE& file) const -{ - if (!data.empty()) { - const BlockHeader block_header = { (uint16_t)EBlockType::Checksum, (uint16_t)ECompressionType::None, (uint32_t)data.size() }; - // write block header - const EResult res = block_header.write(file); - if (res != EResult::Success) - // propagate error - return res; - // write block payload - if (!write_to_file(file, (const void*)data.data(), data.size())) - return EResult::WriteError; - } - - return EResult::Success; -} - -EResult ChecksumBlock::read_data(FILE& file, const BlockHeader& block_header) -{ - if (block_header.uncompressed_size > 0) { - data.resize(block_header.uncompressed_size); - if (!read_from_file(file, (void*)data.data(), block_header.uncompressed_size)) - return EResult::ReadError; - } - else - data.clear(); - - return EResult::Success; -} -#endif // ENABLE_CHECKSUM_BLOCK - -EResult Binarizer::initialize(FILE& file, const BinarizerConfig& config) -{ - if (!m_enabled) - return EResult::Success; - - m_file = &file; - - m_config = config; -#if ENABLE_CHECKSUM_BLOCK - // initialize checksum - m_checksum = ChecksumBlock(); -#endif // ENABLE_CHECKSUM_BLOCK - - // save header - FileHeader file_header; - file_header.checksum_type = (uint16_t)m_config.checksum; - EResult res = file_header.write(*m_file); - if (res != EResult::Success) - return res; - - // save file metadata block - res = m_binary_data.file_metadata.write(*m_file, m_config.compression.file_metadata, m_config.checksum); - if (res != EResult::Success) - return res; - - // save printer metadata block - res = m_binary_data.printer_metadata.write(*m_file, m_config.compression.printer_metadata, m_config.checksum); - if (res != EResult::Success) - return res; - - // save thumbnail blocks - for (const ThumbnailBlock& block : m_binary_data.thumbnails) { - res = block.write(*m_file, m_config.checksum); - if (res != EResult::Success) - return res; - } - - // save print metadata block - res = m_binary_data.print_metadata.write(*m_file, m_config.compression.print_metadata, m_config.checksum); - if (res != EResult::Success) - return res; - - // save slicer metadata block - res = m_binary_data.slicer_metadata.write(*m_file, m_config.compression.slicer_metadata, m_config.checksum); - if (res != EResult::Success) - return res; - - return EResult::Success; -} - -static EResult write_gcode_block(FILE& file, const std::string& raw_data, const BinarizerConfig& config) -{ - GCodeBlock block; - block.encoding_type = (uint16_t)config.gcode_encoding; - block.raw_data = raw_data; - return block.write(file, config.compression.gcode, config.checksum); -} - -EResult Binarizer::append_gcode(const std::string& gcode) -{ - if (gcode.empty()) - return EResult::Success; - - assert(m_file != nullptr); - if (m_file == nullptr) - return EResult::WriteError; - - auto it_begin = gcode.begin(); - do { - const size_t begin_pos = std::distance(gcode.begin(), it_begin); - const size_t end_line_pos = gcode.find_first_of('\n', begin_pos); - if (end_line_pos == std::string::npos) - return EResult::WriteError; - - const size_t line_size = 1 + end_line_pos - begin_pos; - if (line_size + m_gcode_cache.length() > MAX_GCODE_CACHE_SIZE) { - if (!m_gcode_cache.empty()) { - const EResult res = write_gcode_block(*m_file, m_gcode_cache, m_config); - if (res != EResult::Success) - // propagate error - return res; - m_gcode_cache.clear(); - } - } - - if (line_size > MAX_GCODE_CACHE_SIZE) - return EResult::WriteError; - - m_gcode_cache.insert(m_gcode_cache.end(), it_begin, it_begin + line_size); - it_begin += line_size; - } - while (it_begin != gcode.end()); - - return EResult::Success; -} - -EResult Binarizer::finalize() -{ - if (!m_enabled) - return EResult::Success; - - // save gcode cache, if not empty - if (!m_gcode_cache.empty()) { - const EResult res = write_gcode_block(*m_file, m_gcode_cache, m_config); - if (res != EResult::Success) - // propagate error - return res; - } - -#if ENABLE_CHECKSUM_BLOCK - if (m_checksum_type != EChecksumType::None) { - // save checksum - // dummy checksum until it is not properly implemented - switch (m_checksum_type) - { - case EChecksumType::CRC32: - case EChecksumType::MD5: - { - m_checksum.data.clear(); - break; - } - } - - res = m_checksum.write(file); - if (res != EResult::Success) - return res; - } -#endif // ENABLE_CHECKSUM_BLOCK - - return EResult::Success; -} - -bool is_valid_binary_gcode(FILE& file) -{ - // cache file position - const long curr_pos = ftell(&file); - rewind(&file); - - std::array magic; - fread((void*)magic.data(), 1, magic.size(), &file); - if (ferror(&file)) - return false; - else { - // restore file position - fseek(&file, curr_pos, SEEK_SET); - return magic == MAGIC; - } -} - -EResult read_header(FILE& file, FileHeader& header, const uint32_t* const max_version) -{ - rewind(&file); - return header.read(file, max_version); -} - -static EResult checksums_match(FILE& file, const FileHeader& file_header, const BlockHeader& block_header) -{ - // cache file position - const long curr_pos = ftell(&file); - - Checksum curr_cs((EChecksumType)file_header.checksum_type); - // update block checksum block header - block_header.update_checksum(curr_cs); - - // read block payload - size_t remaining_payload_size = block_payload_size(block_header); - while (remaining_payload_size > 0) { - const size_t size_to_read = std::min(remaining_payload_size, g_checksum_max_cache_size); - std::vector payload(size_to_read); - if (!read_from_file(file, payload.data(), payload.size())) - return EResult::ReadError; - curr_cs.append(payload); - remaining_payload_size -= size_to_read; - } - - // read checksum - Checksum read_cs((EChecksumType)file_header.checksum_type); - EResult res = read_cs.read(file); - if (res != EResult::Success) - // propagate error - return res; - - // Verify checksum - if (!curr_cs.matches(read_cs)) - return EResult::InvalidChecksum; - - // restore file position - fseek(&file, curr_pos, SEEK_SET); - - return EResult::Success; -} - -EResult read_next_block_header(FILE& file, const FileHeader& file_header, BlockHeader& block_header, bool verify_checksum) -{ - if (verify_checksum && (EChecksumType)file_header.checksum_type != EChecksumType::None) { - const EResult res = block_header.read(file); - if (res != EResult::Success) - // propagate error - return res; - - return checksums_match(file, file_header, block_header); - } - else - return block_header.read(file); -} - -EResult read_next_block_header(FILE& file, const FileHeader& file_header, BlockHeader& block_header, EBlockType type, bool verify_checksum) -{ - // cache file position - const long curr_pos = ftell(&file); - - do { - EResult res = read_next_block_header(file, file_header, block_header, false); - if (res != EResult::Success) - // propagate error - return res; - else if (feof(&file)) { - // block not found - // restore file position - fseek(&file, curr_pos, SEEK_SET); - return EResult::BlockNotFound; - } - else if ((EBlockType)block_header.type == type) { - // block found - if (verify_checksum) { - res = checksums_match(file, file_header, block_header); - if (res != EResult::Success) - // propagate error - return res; - else - break; - } - } - - if (!feof(&file)) { - res = skip_block_content(file, file_header, block_header); - if (res != EResult::Success) - // propagate error - return res; - } - } while (true); - - return EResult::Success; -} - -EResult skip_block_payload(FILE& file, const BlockHeader& block_header) -{ - fseek(&file, (long)block_payload_size(block_header), SEEK_CUR); - return ferror(&file) ? EResult::ReadError : EResult::Success; -} - -EResult skip_block_content(FILE& file, const FileHeader& file_header, const BlockHeader& block_header) -{ - fseek(&file, (long)block_content_size(file_header, block_header), SEEK_CUR); - return ferror(&file) ? EResult::ReadError : EResult::Success; -} - -size_t block_parameters_size(EBlockType type) -{ - switch (type) - { - case EBlockType::FileMetadata: { return FileMetadataBlock::get_parameters_size(); } - case EBlockType::GCode: { return GCodeBlock::get_parameters_size(); } - case EBlockType::SlicerMetadata: { return SlicerMetadataBlock::get_parameters_size(); } - case EBlockType::PrinterMetadata: { return PrinterMetadataBlock::get_parameters_size(); } - case EBlockType::PrintMetadata: { return PrintMetadataBlock::get_parameters_size(); } - case EBlockType::Thumbnail: { return ThumbnailBlock::get_parameters_size(); } - } - return 0; -} - -size_t block_payload_size(const BlockHeader& block_header) -{ - size_t ret = block_parameters_size((EBlockType)block_header.type); - ret += ((ECompressionType)block_header.compression == ECompressionType::None) ? - block_header.uncompressed_size : block_header.compressed_size; - return ret; -} - -size_t checksum_size(EChecksumType type) -{ - switch (type) - { - case EChecksumType::None: { return 0; } - case EChecksumType::CRC32: { return 4; } - } - return 0; -} - -extern size_t block_content_size(const FileHeader& file_header, const BlockHeader& block_header) -{ -#if ENABLE_CHECKSUM_BLOCK - return ((EBlockType)block_header.type == EBlockType::Checksum) ? - block_payload_size(block_header) : block_payload_size(block_header) + checksum_size((EChecksumType)file_header.checksum_type); -#else - return block_payload_size(block_header) + checksum_size((EChecksumType)file_header.checksum_type); -#endif // ENABLE_CHECKSUM_BLOCK -} - -#if ENABLE_FILE_CONVERSION_INTERFACE -EResult from_ascii_to_binary(FILE& src_file, FILE& dst_file) -{ - return EResult::WriteError; -} - -EResult from_binary_to_ascii(FILE& src_file, FILE& dst_file, bool verify_checksum) -{ - auto write_line = [&](const std::string& line) { - fwrite(line.data(), 1, line.length(), &dst_file); - return !ferror(&dst_file); - }; - - auto write_metadata = [&](const std::vector>& data) { - for (const auto& [key, value] : data) { - if (!write_line("; " + key + " = " + value + "\n")) - return false; - } - return !ferror(&dst_file); - }; - - if (!is_valid_binary_gcode(src_file)) - return EResult::InvalidBinaryGCodeFile; - - fseek(&src_file, 0, SEEK_END); - const long file_size = ftell(&src_file); - rewind(&src_file); - - // - // read file header - // - FileHeader file_header; - EResult res = read_header(src_file, file_header, nullptr); - if (res != EResult::Success) - // propagate error - return res; - - // - // convert file metadata block - // - BlockHeader block_header; - res = read_next_block_header(src_file, file_header, block_header, verify_checksum); - if (res != EResult::Success) - // propagate error - return res; - if ((EBlockType)block_header.type != EBlockType::FileMetadata) - return EResult::InvalidSequenceOfBlocks; - FileMetadataBlock file_metadata_block; - res = file_metadata_block.read_data(src_file, file_header, block_header); - if (res != EResult::Success) - // propagate error - return res; - auto producer_it = std::find_if(file_metadata_block.raw_data.begin(), file_metadata_block.raw_data.end(), - [](const std::pair& item) { return item.first == "Producer"; }); - const std::string producer_str = (producer_it != file_metadata_block.raw_data.end()) ? producer_it->second : "Unknown"; - if (!write_line("; generated by " + producer_str + "\n\n\n")) - return EResult::WriteError; - - // - // convert printer metadata block - // - res = read_next_block_header(src_file, file_header, block_header, verify_checksum); - if (res != EResult::Success) - // propagate error - return res; - if ((EBlockType)block_header.type != EBlockType::PrinterMetadata) - return EResult::InvalidSequenceOfBlocks; - PrinterMetadataBlock printer_metadata_block; - res = printer_metadata_block.read_data(src_file, file_header, block_header); - if (res != EResult::Success) - // propagate error - return res; - if (!write_metadata(printer_metadata_block.raw_data)) - return EResult::WriteError; - - // - // convert thumbnail blocks - // - long restore_position = ftell(&src_file); - res = read_next_block_header(src_file, file_header, block_header, verify_checksum); - if (res != EResult::Success) - // propagate error - return res; - while ((EBlockType)block_header.type == EBlockType::Thumbnail) { - ThumbnailBlock thumbnail_block; - res = thumbnail_block.read_data(src_file, file_header, block_header); - if (res != EResult::Success) - // propagate error - return res; - static constexpr const size_t max_row_length = 78; - std::string encoded; - encoded.resize(boost::beast::detail::base64::encoded_size(thumbnail_block.data.size())); - encoded.resize(boost::beast::detail::base64::encode((void*)encoded.data(), (const void*)thumbnail_block.data.data(), thumbnail_block.data.size())); - std::string format; - switch ((EThumbnailFormat)thumbnail_block.format) - { - default: - case EThumbnailFormat::PNG: { format = "thumbnail"; break; } - case EThumbnailFormat::JPG: { format = "thumbnail_JPG"; break; } - case EThumbnailFormat::QOI: { format = "thumbnail_QOI"; break; } - } - if (!write_line(";\n; " + format + " begin " + std::to_string(thumbnail_block.width) + "x" + std::to_string(thumbnail_block.height) + - " " + std::to_string(encoded.length()) + "\n")) - return EResult::WriteError; - while (encoded.size() > max_row_length) { - if (!write_line("; " + encoded.substr(0, max_row_length) + "\n")) - return EResult::WriteError; - encoded = encoded.substr(max_row_length); - } - if (encoded.size() > 0) { - if (!write_line("; " + encoded + "\n")) - return EResult::WriteError; - } - if (!write_line("; " + format + " end\n;\n\n")) - return EResult::WriteError; - - restore_position = ftell(&src_file); - res = read_next_block_header(src_file, file_header, block_header, verify_checksum); - if (res != EResult::Success) - // propagate error - return res; - } - - // - // convert gcode blocks - // - res = skip_block_content(src_file, file_header, block_header); - if (res != EResult::Success) - // propagate error - return res; - res = read_next_block_header(src_file, file_header, block_header, EBlockType::GCode, verify_checksum); - if (res != EResult::Success) - // propagate error - return res; - while ((EBlockType)block_header.type == EBlockType::GCode) { - GCodeBlock block; - res = block.read_data(src_file, file_header, block_header); - if (res != EResult::Success) - // propagate error - return res; - if (!write_line(block.raw_data)) - return EResult::WriteError; - if (ftell(&src_file) == file_size) - break; - res = read_next_block_header(src_file, file_header, block_header, verify_checksum); - if (res != EResult::Success) - // propagate error - return res; - } - - // - // convert print metadata block - // - fseek(&src_file, restore_position, SEEK_SET); - res = read_next_block_header(src_file, file_header, block_header, verify_checksum); - if (res != EResult::Success) - // propagate error - return res; - if ((EBlockType)block_header.type != EBlockType::PrintMetadata) - return EResult::InvalidSequenceOfBlocks; - PrintMetadataBlock print_metadata_block; - res = print_metadata_block.read_data(src_file, file_header, block_header); - if (res != EResult::Success) - // propagate error - return res; - if (!write_line("\n")) - return EResult::WriteError; - if (!write_metadata(print_metadata_block.raw_data)) - return EResult::WriteError; - - // - // convert slicer metadata block - // - res = read_next_block_header(src_file, file_header, block_header, verify_checksum); - if (res != EResult::Success) - // propagate error - return res; - if ((EBlockType)block_header.type != EBlockType::SlicerMetadata) - return EResult::InvalidSequenceOfBlocks; - SlicerMetadataBlock slicer_metadata_block; - res = slicer_metadata_block.read_data(src_file, file_header, block_header); - if (res != EResult::Success) - // propagate error - return res; - if (!write_line("\n; prusaslicer_config = begin\n")) - return EResult::WriteError; - if (!write_metadata(slicer_metadata_block.raw_data)) - return EResult::WriteError; - if (!write_line("; prusaslicer_config = end\n\n")) - return EResult::WriteError; - - return EResult::Success; -} -#endif // ENABLE_FILE_CONVERSION_INTERFACE - -} // namespace bgcode - diff --git a/src/libslic3r/GCode/GCodeBinarizer.hpp b/src/libslic3r/GCode/GCodeBinarizer.hpp deleted file mode 100644 index 2961fe548f..0000000000 --- a/src/libslic3r/GCode/GCodeBinarizer.hpp +++ /dev/null @@ -1,382 +0,0 @@ -#ifndef slic3r_GCode_GCodeBinarizer_hpp_ -#define slic3r_GCode_GCodeBinarizer_hpp_ - -#ifdef _WIN32 -#define ENABLE_BINARIZED_GCODE_DEBUG 1 -#endif // _WIN32 - -#define ENABLE_CHECKSUM_BLOCK 0 -#define ENABLE_FILE_CONVERSION_INTERFACE 1 - -#include -#include -#include -#include -#include - -namespace bgcode { - -static const std::array MAGIC{ 'G', 'C', 'D', 'E' }; -static const uint32_t VERSION = 1; - -enum class EResult : uint16_t -{ - Success, - ReadError, - WriteError, - InvalidMagicNumber, - InvalidVersionNumber, - InvalidChecksumType, - InvalidBlockType, - InvalidCompressionType, - InvalidMetadataEncodingType, - InvalidGCodeEncodingType, - DataCompressionError, - DataUncompressionError, - MetadataEncodingError, - MetadataDecodingError, - GCodeEncodingError, - GCodeDecodingError, - BlockNotFound, - InvalidChecksum, - InvalidThumbnailFormat, - InvalidThumbnailWidth, - InvalidThumbnailHeight, - InvalidThumbnailDataSize, - InvalidBinaryGCodeFile, - InvalidSequenceOfBlocks -}; - -// Returns a string description of the given result -extern std::string translate_result(EResult result); - -enum class EChecksumType : uint16_t -{ - None, - CRC32 -}; - -class Checksum -{ -public: - // Constructs a checksum of the given type. - // The checksum data are sized accordingly. - explicit Checksum(EChecksumType type); - - EChecksumType get_type() const; - - // Appends the given data to the cache and performs a checksum update if - // the size of the cache exceeds the max checksum cache size. - void append(const std::vector& data); - // Returns true if the given checksum is equal to this one - bool matches(Checksum& other); - - EResult write(FILE& file); - EResult read(FILE& file); - -private: - EChecksumType m_type; - std::vector m_cache; - std::vector m_checksum; - - void update(); -}; - -struct FileHeader -{ - uint32_t magic{ *(uint32_t*)(MAGIC.data()) }; - uint32_t version{ VERSION }; - uint16_t checksum_type{ (uint16_t)EChecksumType::None }; - - EResult write(FILE& file) const; - EResult read(FILE& file, const uint32_t* const max_version); -}; - -enum class EBlockType : uint16_t -{ -#if ENABLE_CHECKSUM_BLOCK - Checksum, -#endif // ENABLE_CHECKSUM_BLOCK - FileMetadata, - GCode, - SlicerMetadata, - PrinterMetadata, - PrintMetadata, - Thumbnail -}; - -enum class ECompressionType : uint16_t -{ - None, - Deflate, - Heatshrink_11_4, - Heatshrink_12_4, -}; - -struct BlockHeader -{ - uint16_t type{ 0 }; - uint16_t compression{ 0 }; - uint32_t uncompressed_size{ 0 }; - uint32_t compressed_size{ 0 }; - - // Updates the given checksum with the data of this BlockHeader - void update_checksum(Checksum& checksum) const; - - EResult write(FILE& file) const; - EResult read(FILE& file); -}; - -enum class EMetadataEncodingType : uint16_t -{ - INI, -}; - -struct BaseMetadataBlock -{ - // type of data encoding - uint16_t encoding_type{ 0 }; - // data in key/value form - std::vector> raw_data; - - // write block header and data in encoded format - EResult write(FILE& file, EBlockType block_type, ECompressionType compression_type, Checksum& checksum) const; - // read block data in encoded format - EResult read_data(FILE& file, const BlockHeader& block_header); - - static size_t get_parameters_size() { return sizeof(encoding_type); } -}; - -struct FileMetadataBlock : public BaseMetadataBlock -{ - // write block header and data - EResult write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const; - // read block data - EResult read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header); -}; - -enum class EThumbnailFormat : uint16_t -{ - PNG, - JPG, - QOI -}; - -struct ThumbnailBlock -{ - uint16_t format{ 0 }; - uint16_t width{ 0 }; - uint16_t height{ 0 }; - std::vector data; - - // write block header and data - EResult write(FILE& file, EChecksumType checksum_type) const; - // read block data - EResult read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header); - - static size_t get_parameters_size() { return sizeof(format) + sizeof(width) + sizeof(height); } - -private: - void update_checksum(Checksum& checksum) const; -}; - -struct PrinterMetadataBlock : public BaseMetadataBlock -{ - // write block header and data - EResult write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const; - // read block data - EResult read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header); -}; - -struct PrintMetadataBlock : public BaseMetadataBlock -{ - // write block header and data - EResult write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const; - // read block data - EResult read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header); -}; - -struct SlicerMetadataBlock : public BaseMetadataBlock -{ - // write block header and data - EResult write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const; - // read block data - EResult read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header); -}; - -enum class EGCodeEncodingType : uint16_t -{ - None, - MeatPack, - MeatPackComments -}; - -struct GCodeBlock -{ - uint16_t encoding_type{ 0 }; - std::string raw_data; - - // write block header and data - EResult write(FILE& file, ECompressionType compression_type, EChecksumType checksum_type) const; - // read block data - EResult read_data(FILE& file, const FileHeader& file_header, const BlockHeader& block_header); - - static size_t get_parameters_size() { return sizeof(encoding_type); } -}; - -#if ENABLE_CHECKSUM_BLOCK -struct ChecksumBlock -{ - std::vector data; - - // write block header and data - EResult write(FILE& file) const; - // read block data - EResult read_data(FILE& file, const BlockHeader& block_header); -}; -#endif // ENABLE_CHECKSUM_BLOCK - -//===================================================================================================================================== -// -// PRUSASLICER INTERFACE -// -//===================================================================================================================================== - -struct BinaryData -{ - FileMetadataBlock file_metadata; - PrinterMetadataBlock printer_metadata; - std::vector thumbnails; - SlicerMetadataBlock slicer_metadata; - PrintMetadataBlock print_metadata; - - void reset() { - file_metadata.raw_data.clear(); - printer_metadata.raw_data.clear(); - thumbnails.clear(); - slicer_metadata.raw_data.clear(); - print_metadata.raw_data.clear(); - } -}; - -struct BinarizerConfig -{ - struct Compression - { - ECompressionType file_metadata{ ECompressionType::None }; - ECompressionType printer_metadata{ ECompressionType::None }; - ECompressionType print_metadata{ ECompressionType::None }; - ECompressionType slicer_metadata{ ECompressionType::None }; - ECompressionType gcode{ ECompressionType::None }; - }; - Compression compression; - EGCodeEncodingType gcode_encoding{ EGCodeEncodingType::None }; - EMetadataEncodingType metadata_encoding{ EMetadataEncodingType::INI }; - EChecksumType checksum{ EChecksumType::CRC32 }; -}; - -class Binarizer -{ -public: - bool is_enabled() const { return m_enabled; } - void set_enabled(bool enable) { m_enabled = enable; } - - BinaryData& get_binary_data() { return m_binary_data; } - const BinaryData& get_binary_data() const { return m_binary_data; } - - EResult initialize(FILE& file, const BinarizerConfig& config); - EResult append_gcode(const std::string& gcode); - EResult finalize(); - -private: - bool m_enabled{ false }; - - BinarizerConfig m_config; - FILE* m_file{ nullptr }; - BinaryData m_binary_data; - std::string m_gcode_cache; -#if ENABLE_CHECKSUM_BLOCK - ChecksumBlock m_checksum; -#endif // ENABLE_CHECKSUM_BLOCK -}; - -//===================================================================================================================================== -// -// GCODEVIEWER INTERFACE -// FIRMWARE INTERFACE -// -//===================================================================================================================================== - -// Get the max size of the cache used to calculate checksums, in bytes -size_t get_checksum_max_cache_size(); -// Set the max size of the cache used to calculate checksums, in bytes -void set_checksum_max_cache_size(size_t size); - -// Returns true if the given file is a valid binary gcode -// Does not modify the file position -extern bool is_valid_binary_gcode(FILE& file); - -// Reads the file header. -// If max_version is not null, version is checked against the passed value -// If return == EResult::Success: -// - header will contain the file header -// - file position will be set at the start of the 1st block header -extern EResult read_header(FILE& file, FileHeader& header, const uint32_t* const max_version); - -// Reads next block header from the current file position. -// File position must be at the start of a block header. -// If return == EResult::Success: -// - block_header will contain the header of the block -// - file position will be set at the start of the block parameters data -extern EResult read_next_block_header(FILE& file, const FileHeader& file_header, BlockHeader& block_header, bool verify_checksum); - -// Searches and reads next block header with the given type from the current file position. -// File position must be at the start of a block header. -// If return == EResult::Success: -// - block_header will contain the header of the block with the required type -// - file position will be set at the start of the block parameters data -// otherwise: -// - file position will keep the current value -extern EResult read_next_block_header(FILE& file, const FileHeader& file_header, BlockHeader& block_header, EBlockType type, bool verify_checksum); - -// Skips the payload (parameters + data) of the block with the given block header. -// File position must be at the start of the block parameters. -// If return == EResult::Success: -// - file position will be set at the start of the block checksum, if present, or of next block header -extern EResult skip_block_payload(FILE& file, const BlockHeader& block_header); - -// Skips the content (parameters + data + checksum) of the block with the given block header. -// File position must be at the start of the block parameters. -// If return == EResult::Success: -// - file position will be set at the start of the next block header -extern EResult skip_block_content(FILE& file, const FileHeader& file_header, const BlockHeader& block_header); - -// Returns the size of the parameters of the given block type, in bytes. -extern size_t block_parameters_size(EBlockType type); - -// Returns the size of the payload (parameters + data) of the block with the given header, in bytes. -extern size_t block_payload_size(const BlockHeader& block_header); - -// Returns the size of the checksum of the given type, in bytes. -extern size_t checksum_size(EChecksumType type); - -// Returns the size of the content (parameters + data + checksum) of the block with the given header, in bytes. -extern size_t block_content_size(const FileHeader& file_header, const BlockHeader& block_header); - -#if ENABLE_FILE_CONVERSION_INTERFACE -//===================================================================================================================================== -// -// FILE CONVERSION INTERFACE -// -//===================================================================================================================================== - -// Converts the gcode file contained into src_file from ascii to binary format and save the results into dst_file -extern EResult from_ascii_to_binary(FILE& src_file, FILE& dst_file); - -// Converts the gcode file contained into src_file from binary to ascii format and save the results into dst_file -extern EResult from_binary_to_ascii(FILE& src_file, FILE& dst_file, bool verify_checksum); -#endif // ENABLE_FILE_CONVERSION_INTERFACE - -} // namespace bgcode - -#endif // slic3r_GCode_GCodeBinarizer_hpp_ From ec16420f117c5792c212c4f9a4f571a7c9a7fb1c Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 3 Aug 2023 12:57:55 +0200 Subject: [PATCH 051/136] Integrated library libbgcode --- src/libslic3r/GCode.cpp | 12 +-- src/libslic3r/GCode.hpp | 7 +- src/libslic3r/GCode/GCodeProcessor.cpp | 141 ++++++++++++------------- src/libslic3r/GCode/GCodeProcessor.hpp | 12 +-- src/libslic3r/GCode/Thumbnails.hpp | 17 +-- src/libslic3r/Technologies.hpp | 1 + src/slic3r/GUI/GLCanvas3D.cpp | 18 ++-- src/slic3r/GUI/Plater.cpp | 10 +- 8 files changed, 112 insertions(+), 106 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 91771220ba..a037c0c483 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -841,7 +841,7 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu m_processor.initialize(path_tmp); m_processor.set_print(print); #if ENABLE_BINARIZED_GCODE - m_processor.get_binary_data().reset(); + m_processor.get_binary_data() = bgcode::base::BinaryData(); #endif // ENABLE_BINARIZED_GCODE GCodeOutputStream file(boost::nowide::fopen(path_tmp.c_str(), "wb"), m_processor); if (! file.is_open()) @@ -984,7 +984,7 @@ namespace DoExport { unsigned int initial_extruder_id, PrintStatistics &print_statistics, bool export_binary_data, - bgcode::BinaryData &binary_data) + bgcode::base::BinaryData &binary_data) #else static std::string update_print_stats_and_format_filament_stats( const bool has_wipe_tower, @@ -1132,7 +1132,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // 1) generate the thumbnails // 2) collect the config data if (export_to_binary_gcode) { - bgcode::BinaryData& binary_data = m_processor.get_binary_data(); + bgcode::base::BinaryData& binary_data = m_processor.get_binary_data(); // Unit tests or command line slicing may not define "thumbnails" or "thumbnails_format". // If "thumbnails_format" is not defined, export to PNG. @@ -1146,11 +1146,11 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato } // file data - binary_data.file_metadata.encoding_type = (uint16_t)bgcode::EMetadataEncodingType::INI; + binary_data.file_metadata.encoding_type = (uint16_t)bgcode::core::EMetadataEncodingType::INI; binary_data.file_metadata.raw_data.emplace_back("Producer", std::string(SLIC3R_APP_NAME) + " " + std::string(SLIC3R_VERSION)); // config data - binary_data.slicer_metadata.encoding_type = (uint16_t)bgcode::EMetadataEncodingType::INI; + binary_data.slicer_metadata.encoding_type = (uint16_t)bgcode::core::EMetadataEncodingType::INI; encode_full_config(print, binary_data.slicer_metadata.raw_data); // printer data @@ -1613,7 +1613,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato file.write(filament_stats_string_out); if (export_to_binary_gcode) { - bgcode::BinaryData& binary_data = m_processor.get_binary_data(); + bgcode::base::BinaryData& binary_data = m_processor.get_binary_data(); if (print.m_print_statistics.total_toolchanges > 0) binary_data.print_metadata.raw_data.push_back({ "total toolchanges", std::to_string(print.m_print_statistics.total_toolchanges) }); } diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 7358c15058..1821576fd7 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -21,9 +21,6 @@ #include "GCode/GCodeProcessor.hpp" #include "EdgeGrid.hpp" #include "GCode/ThumbnailData.hpp" -#if ENABLE_BINARIZED_GCODE -#include "GCode/GCodeBinarizer.hpp" -#endif // ENABLE_BINARIZED_GCODE #include #include @@ -31,6 +28,10 @@ #include "GCode/PressureEqualizer.hpp" +#if ENABLE_BINARIZED_GCODE +#include +#endif // ENABLE_BINARIZED_GCODE + namespace Slic3r { // Forward declarations. diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 6c0fb8c2cf..6ebddec853 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -70,7 +70,7 @@ const float GCodeProcessor::Wipe_Width = 0.05f; const float GCodeProcessor::Wipe_Height = 0.05f; #if ENABLE_BINARIZED_GCODE -bgcode::BinarizerConfig GCodeProcessor::s_binarizer_config{}; +bgcode::base::BinarizerConfig GCodeProcessor::s_binarizer_config{}; #endif // ENABLE_BINARIZED #if ENABLE_GCODE_VIEWER_DATA_CHECKING @@ -1041,7 +1041,7 @@ void GCodeProcessor::process_file(const std::string& filename, std::function& item) { return item.first == "Producer"; }); if (producer_it != file_metadata_block.raw_data.end() && boost::starts_with(producer_it->second, std::string(SLIC3R_APP_NAME))) @@ -1174,15 +1174,15 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct m_producer = EProducer::Unknown; // read printer metadata block - res = bgcode::read_next_block_header(*file, file_header, block_header, verify_checksum); - if (res != bgcode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::translate_result(res) + "\n"); - if ((bgcode::EBlockType)block_header.type != bgcode::EBlockType::PrinterMetadata) + res = bgcode::core::read_next_block_header(*file, file_header, block_header, verify_checksum); + if (res != bgcode::core::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); + if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::PrinterMetadata) throw Slic3r::RuntimeError("Unable to find printer metadata block in file: " + filename + "\n"); - bgcode::PrinterMetadataBlock printer_metadata_block; + bgcode::base::PrinterMetadataBlock printer_metadata_block; res = printer_metadata_block.read_data(*file, file_header, block_header); - if (res != bgcode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::translate_result(res) + "\n"); + if (res != bgcode::core::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); #if ENABLE_BINARIZED_GCODE_DEBUG OutputDebugStringA("Printer metadata:\n"); for (const auto& [key, value] : printer_metadata_block.raw_data) { @@ -1194,25 +1194,24 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct #endif // ENABLE_BINARIZED_GCODE_DEBUG // read thumbnail blocks - res = bgcode::read_next_block_header(*file, file_header, block_header, verify_checksum); - if (res != bgcode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::translate_result(res) + "\n"); + res = bgcode::core::read_next_block_header(*file, file_header, block_header, verify_checksum); + if (res != bgcode::core::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); - while ((bgcode::EBlockType)block_header.type == bgcode::EBlockType::Thumbnail) { - bgcode::ThumbnailBlock thumbnail_block; + while ((bgcode::core::EBlockType)block_header.type == bgcode::core::EBlockType::Thumbnail) { + bgcode::base::ThumbnailBlock thumbnail_block; res = thumbnail_block.read_data(*file, file_header, block_header); - if (res != bgcode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::translate_result(res) + "\n"); - + if (res != bgcode::core::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); #if ENABLE_BINARIZED_GCODE_DEBUG if (thumbnail_block.data.size() > 0) { - auto format_filename = [](const std::string& stem, const bgcode::ThumbnailBlock& block) { + auto format_filename = [](const std::string& stem, const bgcode::base::ThumbnailBlock& block) { std::string ret = stem + "_" + std::to_string(block.width) + "x" + std::to_string(block.height); - switch ((bgcode::EThumbnailFormat)block.format) + switch ((bgcode::core::EThumbnailFormat)block.format) { - case bgcode::EThumbnailFormat::PNG: { ret += ".png"; break; } - case bgcode::EThumbnailFormat::JPG: { ret += ".jpg"; break; } - case bgcode::EThumbnailFormat::QOI: { ret += ".qoi"; break; } + case bgcode::core::EThumbnailFormat::PNG: { ret += ".png"; break; } + case bgcode::core::EThumbnailFormat::JPG: { ret += ".jpg"; break; } + case bgcode::core::EThumbnailFormat::QOI: { ret += ".qoi"; break; } } return ret; }; @@ -1228,18 +1227,18 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct } #endif // ENABLE_BINARIZED_GCODE_DEBUG - res = bgcode::read_next_block_header(*file, file_header, block_header, verify_checksum); - if (res != bgcode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::translate_result(res) + "\n"); + res = bgcode::core::read_next_block_header(*file, file_header, block_header, verify_checksum); + if (res != bgcode::core::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); } // read print metadata block - if ((bgcode::EBlockType)block_header.type != bgcode::EBlockType::PrintMetadata) + if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::PrintMetadata) throw Slic3r::RuntimeError("Unable to find print metadata block in file: " + filename + "\n"); - bgcode::PrintMetadataBlock print_metadata_block; + bgcode::base::PrintMetadataBlock print_metadata_block; res = print_metadata_block.read_data(*file, file_header, block_header); - if (res != bgcode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::translate_result(res) + "\n"); + if (res != bgcode::core::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); #if ENABLE_BINARIZED_GCODE_DEBUG OutputDebugStringA("Print metadata:\n"); for (const auto& [key, value] : print_metadata_block.raw_data) { @@ -1251,15 +1250,15 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct #endif // ENABLE_BINARIZED_GCODE_DEBUG // read slicer metadata block - res = bgcode::read_next_block_header(*file, file_header, block_header, verify_checksum); - if (res != bgcode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::translate_result(res) + "\n"); - if ((bgcode::EBlockType)block_header.type != bgcode::EBlockType::SlicerMetadata) + res = bgcode::core::read_next_block_header(*file, file_header, block_header, verify_checksum); + if (res != bgcode::core::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); + if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::SlicerMetadata) throw Slic3r::RuntimeError("Unable to find slicer metadata block in file: " + filename + "\n"); - bgcode::SlicerMetadataBlock slicer_metadata_block; + bgcode::base::SlicerMetadataBlock slicer_metadata_block; res = slicer_metadata_block.read_data(*file, file_header, block_header); - if (res != bgcode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::translate_result(res) + "\n"); + if (res != bgcode::core::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); #if ENABLE_BINARIZED_GCODE_DEBUG OutputDebugStringA("Slicer metadata:\n"); for (const auto& [key, value] : slicer_metadata_block.raw_data) { @@ -1286,16 +1285,16 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct initialize_result_moves(); // read gcodes block - res = bgcode::read_next_block_header(*file, file_header, block_header, verify_checksum); - if (res != bgcode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::translate_result(res) + "\n"); - if ((bgcode::EBlockType)block_header.type != bgcode::EBlockType::GCode) + res = bgcode::core::read_next_block_header(*file, file_header, block_header, verify_checksum); + if (res != bgcode::core::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); + if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::GCode) throw Slic3r::RuntimeError("Unable to find gcode block in file: " + filename + "\n"); - while ((bgcode::EBlockType)block_header.type == bgcode::EBlockType::GCode) { - bgcode::GCodeBlock block; + while ((bgcode::core::EBlockType)block_header.type == bgcode::core::EBlockType::GCode) { + bgcode::base::GCodeBlock block; res = block.read_data(*file, file_header, block_header); - if (res != bgcode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::translate_result(res) + "\n"); + if (res != bgcode::core::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); // TODO: Update m_result.lines_ends @@ -1306,9 +1305,9 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct if (ftell(file) == file_size) break; - res = bgcode::read_next_block_header(*file, file_header, block_header, verify_checksum); - if (res != bgcode::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::translate_result(res) + "\n"); + res = bgcode::core::read_next_block_header(*file, file_header, block_header, verify_checksum); + if (res != bgcode::core::EResult::Success) + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); } // Don't post-process the G-code to update time stamps. @@ -3696,7 +3695,7 @@ void GCodeProcessor::post_process() }; // update binary data - bgcode::BinaryData& binary_data = m_binarizer.get_binary_data(); + bgcode::base::BinaryData& binary_data = m_binarizer.get_binary_data(); binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedMm, stringify(filament_mm) }); binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedCm3, stringify(filament_cm3) }); binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedG, stringify(filament_g) }); @@ -3720,8 +3719,8 @@ void GCodeProcessor::post_process() } } - const bgcode::EResult res = m_binarizer.initialize(*out.f, s_binarizer_config); - if (res != bgcode::EResult::Success) + const bgcode::core::EResult res = m_binarizer.initialize(*out.f, s_binarizer_config); + if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError(std::string("Unable to initialize the gcode binarizer.\n")); } #endif // ENABLE_BINARIZED_GCODE @@ -3843,12 +3842,12 @@ void GCodeProcessor::post_process() size_t m_out_file_pos{ 0 }; #if ENABLE_BINARIZED_GCODE - bgcode::Binarizer& m_binarizer; + bgcode::base::Binarizer& m_binarizer; #endif // ENABLE_BINARIZED_GCODE public: #if ENABLE_BINARIZED_GCODE - ExportLines(bgcode::Binarizer& binarizer, EWriteType type, TimeMachine& machine) + ExportLines(bgcode::base::Binarizer& binarizer, EWriteType type, TimeMachine& machine) #ifndef NDEBUG : m_statistics(*this), m_binarizer(binarizer), m_write_type(type), m_machine(machine) {} #else @@ -3974,8 +3973,8 @@ void GCodeProcessor::post_process() #if ENABLE_BINARIZED_GCODE if (m_binarizer.is_enabled()) { - bgcode::EResult res = m_binarizer.append_gcode(out_string); - if (res != bgcode::EResult::Success) + bgcode::core::EResult res = m_binarizer.append_gcode(out_string); + if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError(std::string("Error while sending gcode to the binarizer.\n")); } else @@ -4011,8 +4010,8 @@ void GCodeProcessor::post_process() #if ENABLE_BINARIZED_GCODE if (m_binarizer.is_enabled()) { - bgcode::EResult res = m_binarizer.append_gcode(out_string); - if (res != bgcode::EResult::Success) + bgcode::core::EResult res = m_binarizer.append_gcode(out_string); + if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError(std::string("Error while sending gcode to the binarizer.\n")); } else @@ -4391,8 +4390,8 @@ void GCodeProcessor::post_process() #if ENABLE_BINARIZED_GCODE if (m_binarizer.is_enabled()) { - const bgcode::EResult res = m_binarizer.finalize(); - if (res != bgcode::EResult::Success) + const bgcode::core::EResult res = m_binarizer.finalize(); + if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError(std::string("Error while finalizing the gcode binarizer.\n")); } #endif // ENABLE_BINARIZED_GCODE diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index b3390ed1c6..303a6868d1 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -8,7 +8,7 @@ #include "libslic3r/CustomGCode.hpp" #if ENABLE_BINARIZED_GCODE -#include "GCodeBinarizer.hpp" +#include #endif // ENABLE_BINARIZED_GCODE #include @@ -528,14 +528,14 @@ namespace Slic3r { #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING #if ENABLE_BINARIZED_GCODE_DEBUG_WINDOW - static bgcode::BinarizerConfig& get_binarizer_config() { return s_binarizer_config; } + static bgcode::base::BinarizerConfig& get_binarizer_config() { return s_binarizer_config; } #endif // ENABLE_BINARIZED_GCODE_DEBUG_WINDOW private: GCodeReader m_parser; #if ENABLE_BINARIZED_GCODE - bgcode::Binarizer m_binarizer; - static bgcode::BinarizerConfig s_binarizer_config; + bgcode::base::Binarizer m_binarizer; + static bgcode::base::BinarizerConfig s_binarizer_config; #endif // ENABLE_BINARIZED_GCODE EUnits m_units; @@ -635,8 +635,8 @@ namespace Slic3r { void apply_config(const PrintConfig& config); void set_print(Print* print) { m_print = print; } #if ENABLE_BINARIZED_GCODE - bgcode::BinaryData& get_binary_data() { return m_binarizer.get_binary_data(); } - const bgcode::BinaryData& get_binary_data() const { return m_binarizer.get_binary_data(); } + bgcode::base::BinaryData& get_binary_data() { return m_binarizer.get_binary_data(); } + const bgcode::base::BinaryData& get_binary_data() const { return m_binarizer.get_binary_data(); } #endif // ENABLE_BINARIZED_GCODE void enable_stealth_time_estimator(bool enabled); diff --git a/src/libslic3r/GCode/Thumbnails.hpp b/src/libslic3r/GCode/Thumbnails.hpp index 6fd28af333..7e99013962 100644 --- a/src/libslic3r/GCode/Thumbnails.hpp +++ b/src/libslic3r/GCode/Thumbnails.hpp @@ -4,14 +4,15 @@ #include "../Point.hpp" #include "../PrintConfig.hpp" #include "ThumbnailData.hpp" -#if ENABLE_BINARIZED_GCODE -#include "GCode/GCodeBinarizer.hpp" -#endif // ENABLE_BINARIZED_GCODE #include #include #include +#if ENABLE_BINARIZED_GCODE +#include +#endif // ENABLE_BINARIZED_GCODE + #include namespace Slic3r::GCodeThumbnails { @@ -60,7 +61,7 @@ inline void export_thumbnails_to_file(ThumbnailsGeneratorCallback &thumbnail_cb, #if ENABLE_BINARIZED_GCODE template -inline void generate_binary_thumbnails(ThumbnailsGeneratorCallback& thumbnail_cb, std::vector& out_thumbnails, +inline void generate_binary_thumbnails(ThumbnailsGeneratorCallback& thumbnail_cb, std::vector& out_thumbnails, const std::vector& sizes, GCodeThumbnailsFormat format, ThrowIfCanceledCallback throw_if_canceled) { out_thumbnails.clear(); @@ -70,13 +71,13 @@ inline void generate_binary_thumbnails(ThumbnailsGeneratorCallback& thumbnail_cb if (data.is_valid()) { auto compressed = compress_thumbnail(data, format); if (compressed->data != nullptr && compressed->size > 0) { - bgcode::ThumbnailBlock& block = out_thumbnails.emplace_back(bgcode::ThumbnailBlock()); + bgcode::base::ThumbnailBlock& block = out_thumbnails.emplace_back(bgcode::base::ThumbnailBlock()); block.width = (uint16_t)data.width; block.height = (uint16_t)data.height; switch (format) { - case GCodeThumbnailsFormat::PNG: { block.format = (uint16_t)bgcode::EThumbnailFormat::PNG; break; } - case GCodeThumbnailsFormat::JPG: { block.format = (uint16_t)bgcode::EThumbnailFormat::JPG; break; } - case GCodeThumbnailsFormat::QOI: { block.format = (uint16_t)bgcode::EThumbnailFormat::QOI; break; } + case GCodeThumbnailsFormat::PNG: { block.format = (uint16_t)bgcode::core::EThumbnailFormat::PNG; break; } + case GCodeThumbnailsFormat::JPG: { block.format = (uint16_t)bgcode::core::EThumbnailFormat::JPG; break; } + case GCodeThumbnailsFormat::QOI: { block.format = (uint16_t)bgcode::core::EThumbnailFormat::QOI; break; } } block.data.resize(compressed->size); memcpy(block.data.data(), compressed->data, compressed->size); diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 6af385e71a..db25b587f7 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -69,6 +69,7 @@ // Enable export of binarized gcode #define ENABLE_BINARIZED_GCODE (1 && ENABLE_2_6_1_ALPHA1) +#define ENABLE_BINARIZED_GCODE_DEBUG (1 && ENABLE_BINARIZED_GCODE) #define ENABLE_BINARIZED_GCODE_DEBUG_WINDOW (1 && ENABLE_BINARIZED_GCODE) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index be81ea7f1f..b609795fd2 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -7874,7 +7874,7 @@ void GLCanvas3D::GizmoHighlighter::blink() #if ENABLE_BINARIZED_GCODE_DEBUG_WINDOW void GLCanvas3D::show_binary_gcode_debug_window() { - bgcode::BinarizerConfig& binarizer_config = GCodeProcessor::get_binarizer_config(); + bgcode::base::BinarizerConfig& binarizer_config = GCodeProcessor::get_binarizer_config(); ImGuiWrapper& imgui = *wxGetApp().imgui(); imgui.begin(std::string("Binary GCode"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); @@ -7888,7 +7888,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() std::vector options = { "None", "Deflate", "heatshrink 11,4", "heatshrink 12,4" }; int option_id = (int)binarizer_config.compression.file_metadata; if (imgui.combo(std::string("##file_metadata_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.compression.file_metadata = (bgcode::ECompressionType)option_id; + binarizer_config.compression.file_metadata = (bgcode::core::ECompressionType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -7896,7 +7896,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() ImGui::TableSetColumnIndex(1); option_id = (int)binarizer_config.compression.printer_metadata; if (imgui.combo(std::string("##printer_metadata_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.compression.printer_metadata = (bgcode::ECompressionType)option_id; + binarizer_config.compression.printer_metadata = (bgcode::core::ECompressionType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -7904,7 +7904,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() ImGui::TableSetColumnIndex(1); option_id = (int)binarizer_config.compression.print_metadata; if (imgui.combo(std::string("##print_metadata_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.compression.print_metadata = (bgcode::ECompressionType)option_id; + binarizer_config.compression.print_metadata = (bgcode::core::ECompressionType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -7912,7 +7912,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() ImGui::TableSetColumnIndex(1); option_id = (int)binarizer_config.compression.slicer_metadata; if (imgui.combo(std::string("##slicer_metadata_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.compression.slicer_metadata = (bgcode::ECompressionType)option_id; + binarizer_config.compression.slicer_metadata = (bgcode::core::ECompressionType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -7920,7 +7920,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() ImGui::TableSetColumnIndex(1); option_id = (int)binarizer_config.compression.gcode; if (imgui.combo(std::string("##gcode_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.compression.gcode = (bgcode::ECompressionType)option_id; + binarizer_config.compression.gcode = (bgcode::core::ECompressionType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -7929,7 +7929,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() options = { "None", "MeatPack", "MeatPack Comments" }; option_id = (int)binarizer_config.gcode_encoding; if (imgui.combo(std::string("##gcode_encoding"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.gcode_encoding = (bgcode::EGCodeEncodingType)option_id; + binarizer_config.gcode_encoding = (bgcode::core::EGCodeEncodingType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -7938,7 +7938,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() options = { "INI" }; option_id = (int)binarizer_config.metadata_encoding; if (imgui.combo(std::string("##metadata_encoding"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.metadata_encoding = (bgcode::EMetadataEncodingType)option_id; + binarizer_config.metadata_encoding = (bgcode::core::EMetadataEncodingType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -7947,7 +7947,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() options = { "None", "CRC32" }; option_id = (int)binarizer_config.checksum; if (imgui.combo(std::string("##4"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.checksum = (bgcode::EChecksumType)option_id; + binarizer_config.checksum = (bgcode::core::EChecksumType)option_id; ImGui::EndTable(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 20d6835b16..f23413270c 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -36,6 +36,10 @@ #include #endif +#if ENABLE_BINARIZED_GCODE +#include +#endif // ENABLE_BINARIZED_GCODE + #include "libslic3r/libslic3r.h" #include "libslic3r/Format/STL.hpp" #include "libslic3r/Format/AMF.hpp" @@ -5472,9 +5476,9 @@ void Plater::convert_gcode_to_ascii() // Perform conversion { wxBusyCursor busy; - bgcode::EResult res = bgcode::from_binary_to_ascii(*in_file, *out_file, true); - if (res != bgcode::EResult::Success) { - MessageDialog msg_dlg(this, _L(bgcode::translate_result(res)), _L("Error converting gcode file"), wxICON_INFORMATION | wxOK); + bgcode::core::EResult res = bgcode::convert::from_binary_to_ascii(*in_file, *out_file, true); + if (res != bgcode::core::EResult::Success) { + MessageDialog msg_dlg(this, _L(bgcode::core::translate_result(res)), _L("Error converting gcode file"), wxICON_INFORMATION | wxOK); msg_dlg.ShowModal(); scoped_out_file.unscope(); fclose(out_file); From cd43a8e7448a4dd85452d80df3980d5034eb86c4 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 3 Aug 2023 15:31:13 +0200 Subject: [PATCH 052/136] Fixes required by changes into libbgcode library interface --- src/libslic3r/GCode/GCodeProcessor.cpp | 29 +++++++++++++------------- src/slic3r/GUI/Plater.cpp | 2 +- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 6ebddec853..66f7cc297a 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -1151,7 +1151,8 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct bgcode::core::FileHeader file_header; bgcode::core::EResult res = bgcode::core::read_header(*file, file_header, nullptr); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("File: " + filename + "does not contain a valid binary gcode\n Error: " + bgcode::core::translate_result(res) + "\n"); + throw Slic3r::RuntimeError("File: " + filename + "does not contain a valid binary gcode\n Error: " + + std::string(bgcode::core::translate_result(res)) + "\n"); const bool verify_checksum = true; @@ -1159,13 +1160,13 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct bgcode::core::BlockHeader block_header; res = bgcode::core::read_next_block_header(*file, file_header, block_header, verify_checksum); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::FileMetadata) throw Slic3r::RuntimeError("Unable to find file metadata block in file: " + filename + "\n"); bgcode::base::FileMetadataBlock file_metadata_block; res = file_metadata_block.read_data(*file, file_header, block_header); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); auto producer_it = std::find_if(file_metadata_block.raw_data.begin(), file_metadata_block.raw_data.end(), [](const std::pair& item) { return item.first == "Producer"; }); if (producer_it != file_metadata_block.raw_data.end() && boost::starts_with(producer_it->second, std::string(SLIC3R_APP_NAME))) @@ -1176,13 +1177,13 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct // read printer metadata block res = bgcode::core::read_next_block_header(*file, file_header, block_header, verify_checksum); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::PrinterMetadata) throw Slic3r::RuntimeError("Unable to find printer metadata block in file: " + filename + "\n"); bgcode::base::PrinterMetadataBlock printer_metadata_block; res = printer_metadata_block.read_data(*file, file_header, block_header); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); #if ENABLE_BINARIZED_GCODE_DEBUG OutputDebugStringA("Printer metadata:\n"); for (const auto& [key, value] : printer_metadata_block.raw_data) { @@ -1196,13 +1197,13 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct // read thumbnail blocks res = bgcode::core::read_next_block_header(*file, file_header, block_header, verify_checksum); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); while ((bgcode::core::EBlockType)block_header.type == bgcode::core::EBlockType::Thumbnail) { bgcode::base::ThumbnailBlock thumbnail_block; res = thumbnail_block.read_data(*file, file_header, block_header); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); #if ENABLE_BINARIZED_GCODE_DEBUG if (thumbnail_block.data.size() > 0) { auto format_filename = [](const std::string& stem, const bgcode::base::ThumbnailBlock& block) { @@ -1229,7 +1230,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct res = bgcode::core::read_next_block_header(*file, file_header, block_header, verify_checksum); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); } // read print metadata block @@ -1238,7 +1239,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct bgcode::base::PrintMetadataBlock print_metadata_block; res = print_metadata_block.read_data(*file, file_header, block_header); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); #if ENABLE_BINARIZED_GCODE_DEBUG OutputDebugStringA("Print metadata:\n"); for (const auto& [key, value] : print_metadata_block.raw_data) { @@ -1252,13 +1253,13 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct // read slicer metadata block res = bgcode::core::read_next_block_header(*file, file_header, block_header, verify_checksum); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::SlicerMetadata) throw Slic3r::RuntimeError("Unable to find slicer metadata block in file: " + filename + "\n"); bgcode::base::SlicerMetadataBlock slicer_metadata_block; res = slicer_metadata_block.read_data(*file, file_header, block_header); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); #if ENABLE_BINARIZED_GCODE_DEBUG OutputDebugStringA("Slicer metadata:\n"); for (const auto& [key, value] : slicer_metadata_block.raw_data) { @@ -1287,14 +1288,14 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct // read gcodes block res = bgcode::core::read_next_block_header(*file, file_header, block_header, verify_checksum); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::GCode) throw Slic3r::RuntimeError("Unable to find gcode block in file: " + filename + "\n"); while ((bgcode::core::EBlockType)block_header.type == bgcode::core::EBlockType::GCode) { bgcode::base::GCodeBlock block; res = block.read_data(*file, file_header, block_header); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); // TODO: Update m_result.lines_ends @@ -1307,7 +1308,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct res = bgcode::core::read_next_block_header(*file, file_header, block_header, verify_checksum); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + bgcode::core::translate_result(res) + "\n"); + throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); } // Don't post-process the G-code to update time stamps. diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index f23413270c..c41b20675a 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5478,7 +5478,7 @@ void Plater::convert_gcode_to_ascii() wxBusyCursor busy; bgcode::core::EResult res = bgcode::convert::from_binary_to_ascii(*in_file, *out_file, true); if (res != bgcode::core::EResult::Success) { - MessageDialog msg_dlg(this, _L(bgcode::core::translate_result(res)), _L("Error converting gcode file"), wxICON_INFORMATION | wxOK); + MessageDialog msg_dlg(this, _L(std::string(bgcode::core::translate_result(res))), _L("Error converting gcode file"), wxICON_INFORMATION | wxOK); msg_dlg.ShowModal(); scoped_out_file.unscope(); fclose(out_file); From 1e993a481aa64a2d0e42f6491d3bc78fcb98db0b Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 3 Aug 2023 15:44:04 +0200 Subject: [PATCH 053/136] Implemented command 'Convert ascii G-code to binary' --- src/slic3r/GUI/Plater.cpp | 72 +++++++++++++++++++++++++++++++++------ 1 file changed, 61 insertions(+), 11 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index c41b20675a..691a597362 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5435,22 +5435,22 @@ void Plater::reload_gcode_from_disk() } #if ENABLE_BINARIZED_GCODE +class ScopedFile +{ +public: + explicit ScopedFile(FILE* file) : m_file(file) {} + ~ScopedFile() { if (m_file != nullptr) fclose(m_file); } + void unscope() { m_file = nullptr; } +private: + FILE* m_file{ nullptr }; +}; + void Plater::convert_gcode_to_ascii() { // Ask user for a gcode file name. wxString input_file; wxGetApp().load_gcode(this, input_file); - class ScopedFile - { - public: - explicit ScopedFile(FILE* file) : m_file(file) {} - ~ScopedFile() { if (m_file != nullptr) fclose(m_file); } - void unscope() { m_file = nullptr; } - private: - FILE* m_file{ nullptr }; - }; - // Open source file FILE* in_file = boost::nowide::fopen(into_u8(input_file).c_str(), "rb"); if (in_file == nullptr) { @@ -5493,7 +5493,57 @@ void Plater::convert_gcode_to_ascii() void Plater::convert_gcode_to_binary() { - MessageDialog msg_dlg(this, _L("Not implemented yet."), _L("Error"), wxICON_ERROR | wxOK); + // Ask user for a gcode file name. + wxString input_file; + wxGetApp().load_gcode(this, input_file); + + // Open source file + FILE* in_file = boost::nowide::fopen(into_u8(input_file).c_str(), "rb"); + if (in_file == nullptr) { + MessageDialog msg_dlg(this, _L("Unable to open the selected file."), _L("Error"), wxICON_ERROR | wxOK); + msg_dlg.ShowModal(); + return; + } + ScopedFile scoped_in_file(in_file); + + // Set out filename + boost::filesystem::path path(into_u8(input_file)); + const std::string output_file = path.parent_path().string() + "/" + path.stem().string() + "_binary" + path.extension().string(); + + // Open destination file + FILE* out_file = boost::nowide::fopen(output_file.c_str(), "wb"); + if (out_file == nullptr) { + MessageDialog msg_dlg(this, _L("Unable to open output file."), _L("Error"), wxICON_ERROR | wxOK); + msg_dlg.ShowModal(); + return; + } + ScopedFile scoped_out_file(out_file); + + // Perform conversion + { + wxBusyCursor busy; + // TODO: allow custommization of config + bgcode::base::BinarizerConfig config; + config.checksum = bgcode::core::EChecksumType::CRC32; + config.compression.file_metadata = bgcode::core::ECompressionType::None; + config.compression.print_metadata = bgcode::core::ECompressionType::None; + config.compression.printer_metadata = bgcode::core::ECompressionType::None; + config.compression.slicer_metadata = bgcode::core::ECompressionType::Deflate; + config.compression.gcode = bgcode::core::ECompressionType::Heatshrink_12_4; + config.gcode_encoding = bgcode::core::EGCodeEncodingType::MeatPackComments; + config.metadata_encoding = bgcode::core::EMetadataEncodingType::INI; + bgcode::core::EResult res = bgcode::convert::from_ascii_to_binary(*in_file, *out_file, config); + if (res != bgcode::core::EResult::Success) { + MessageDialog msg_dlg(this, _L(std::string(bgcode::core::translate_result(res))), _L("Error converting gcode file"), wxICON_INFORMATION | wxOK); + msg_dlg.ShowModal(); + scoped_out_file.unscope(); + fclose(out_file); + boost::nowide::remove(output_file.c_str()); + return; + } + } + + MessageDialog msg_dlg(this, _L("Succesfully created gcode binary file:\n") + output_file, _L("Convert gcode file to binary format"), wxICON_ERROR | wxOK); msg_dlg.ShowModal(); } #endif // ENABLE_BINARIZED_GCODE From 28346f78ddfae943cc1c4cd5b2695e702302e62c Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 4 Aug 2023 08:33:28 +0200 Subject: [PATCH 054/136] Fixed naming of metadata exported to gcode file in binary format --- src/libslic3r/GCode.cpp | 8 +++----- src/slic3r/GUI/Plater.cpp | 13 ++----------- 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index a037c0c483..0740ffb415 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1146,29 +1146,27 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato } // file data - binary_data.file_metadata.encoding_type = (uint16_t)bgcode::core::EMetadataEncodingType::INI; binary_data.file_metadata.raw_data.emplace_back("Producer", std::string(SLIC3R_APP_NAME) + " " + std::string(SLIC3R_VERSION)); // config data - binary_data.slicer_metadata.encoding_type = (uint16_t)bgcode::core::EMetadataEncodingType::INI; encode_full_config(print, binary_data.slicer_metadata.raw_data); // printer data - binary_data.printer_metadata.raw_data.emplace_back("printer model" , print.config().printer_model.value); // duplicated into config data + binary_data.printer_metadata.raw_data.emplace_back("printer_model", print.config().printer_model.value); // duplicated into config data std::string filament_types_str; for (size_t i = 0; i < print.config().filament_type.values.size(); ++i) { filament_types_str += print.config().filament_type.values[i]; if (i < print.config().filament_type.values.size() - 1) filament_types_str += ", "; } - binary_data.printer_metadata.raw_data.emplace_back("filament type", filament_types_str); // duplicated into config data + binary_data.printer_metadata.raw_data.emplace_back("filament_type", filament_types_str); // duplicated into config data std::string nozzle_diameters_str; char buf[1024]; for (size_t i = 0; i < print.config().nozzle_diameter.values.size(); ++i) { sprintf(buf, i < print.config().nozzle_diameter.values.size() - 1 ? "%.2lf, " : "%.2lf", print.config().nozzle_diameter.values[i]); nozzle_diameters_str += buf; } - binary_data.printer_metadata.raw_data.emplace_back("nozzle diameter", nozzle_diameters_str); // duplicated into config data + binary_data.printer_metadata.raw_data.emplace_back("nozzle_diameter", nozzle_diameters_str); // duplicated into config data } // modifies m_silent_time_estimator_enabled diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 691a597362..e0c1527e84 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5522,17 +5522,8 @@ void Plater::convert_gcode_to_binary() // Perform conversion { wxBusyCursor busy; - // TODO: allow custommization of config - bgcode::base::BinarizerConfig config; - config.checksum = bgcode::core::EChecksumType::CRC32; - config.compression.file_metadata = bgcode::core::ECompressionType::None; - config.compression.print_metadata = bgcode::core::ECompressionType::None; - config.compression.printer_metadata = bgcode::core::ECompressionType::None; - config.compression.slicer_metadata = bgcode::core::ECompressionType::Deflate; - config.compression.gcode = bgcode::core::ECompressionType::Heatshrink_12_4; - config.gcode_encoding = bgcode::core::EGCodeEncodingType::MeatPackComments; - config.metadata_encoding = bgcode::core::EMetadataEncodingType::INI; - bgcode::core::EResult res = bgcode::convert::from_ascii_to_binary(*in_file, *out_file, config); + const bgcode::base::BinarizerConfig& binarizer_config = GCodeProcessor::get_binarizer_config(); + bgcode::core::EResult res = bgcode::convert::from_ascii_to_binary(*in_file, *out_file, binarizer_config); if (res != bgcode::core::EResult::Success) { MessageDialog msg_dlg(this, _L(std::string(bgcode::core::translate_result(res))), _L("Error converting gcode file"), wxICON_INFORMATION | wxOK); msg_dlg.ShowModal(); From 7ab044fb861ca2961172472e1d6d6e3e50f6c5f1 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 3 Aug 2023 13:00:41 +0200 Subject: [PATCH 055/136] Add debug build for LibBGCode in deps Probably no need for debug build of it's respective heatshrink dependency, as it's a C only lib --- deps/LibBGCode/LibBGCode.cmake | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/deps/LibBGCode/LibBGCode.cmake b/deps/LibBGCode/LibBGCode.cmake index cfdcd009bd..d0c6ac6834 100644 --- a/deps/LibBGCode/LibBGCode.cmake +++ b/deps/LibBGCode/LibBGCode.cmake @@ -22,4 +22,8 @@ prusaslicer_add_cmake_project(LibBGCode DEPENDS dep_LibBGCode_deps CMAKE_ARGS -DLibBGCode_BUILD_TESTS:BOOL=OFF -) \ No newline at end of file +) + +if (MSVC) + add_debug_dep(dep_LibBGCode) +endif () \ No newline at end of file From 87f6fed2746095ca2096ce8b5d46b010956df8c1 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 4 Aug 2023 10:16:37 +0200 Subject: [PATCH 056/136] Fixed link of RelWithDebInfo configuration --- src/libslic3r/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index f11986d314..ce57ba00f2 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -15,6 +15,9 @@ if (TARGET OpenVDB::openvdb) endif() find_package(LibBGCode REQUIRED COMPONENTS Convert) +slic3r_remap_configs(LibBGCode::bgcode_core RelWithDebInfo Release) +slic3r_remap_configs(LibBGCode::bgcode_base RelWithDebInfo Release) +slic3r_remap_configs(LibBGCode::bgcode_convert RelWithDebInfo Release) set(SLIC3R_SOURCES pchheader.cpp From eb8d01888db68da9bce82ebe324f148f43ec35cb Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 4 Aug 2023 12:35:40 +0200 Subject: [PATCH 057/136] Added import of config from binary gcode files --- src/libslic3r/Config.cpp | 100 ++++++++++++++++++++++--- src/libslic3r/Config.hpp | 5 +- src/libslic3r/GCode/GCodeProcessor.cpp | 77 ++++++++----------- src/libslic3r/PresetBundle.cpp | 33 ++++++-- src/slic3r/GUI/Plater.cpp | 40 +++------- 5 files changed, 164 insertions(+), 91 deletions(-) diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index 51870e93d7..af28307faf 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -23,6 +23,10 @@ #include #include +#if ENABLE_BINARIZED_GCODE +#include +#endif // ENABLE_BINARIZED_GCODE + //FIXME for GCodeFlavor and gcfMarlin (for forward-compatibility conversion) // This is not nice, likely it would be better to pass the ConfigSubstitutionContext to handle_legacy(). #include "PrintConfig.hpp" @@ -720,11 +724,41 @@ void ConfigBase::setenv_() const } } -ConfigSubstitutions ConfigBase::load(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule) +ConfigSubstitutions ConfigBase::load(const std::string& filename, ForwardCompatibilitySubstitutionRule compatibility_rule) { - return is_gcode_file(file) ? - this->load_from_gcode_file(file, compatibility_rule) : - this->load_from_ini(file, compatibility_rule); +#if ENABLE_BINARIZED_GCODE + enum class EFileType + { + Ini, + AsciiGCode, + BinaryGCode + }; + + EFileType file_type; + + if (is_gcode_file(filename)) { + FILE* file = boost::nowide::fopen(filename.c_str(), "rb"); + if (file == nullptr) + throw Slic3r::RuntimeError("Error opening the file: " + filename + "\n"); + + file_type = (bgcode::core::is_valid_binary_gcode(*file, true) == bgcode::core::EResult::Success) ? EFileType::BinaryGCode : EFileType::AsciiGCode; + fclose(file); + } + else + file_type = EFileType::Ini; + + switch (file_type) + { + case EFileType::Ini: { return this->load_from_ini(filename, compatibility_rule); } + case EFileType::AsciiGCode: { return this->load_from_gcode_file(filename, compatibility_rule);} + case EFileType::BinaryGCode: { return this->load_from_binary_gcode_file(filename, compatibility_rule);} + default: { throw Slic3r::RuntimeError("Invalid file: " + filename + "\n"); } + } +#else + return is_gcode_file(filename) ? + this->load_from_gcode_file(filename, compatibility_rule) : + this->load_from_ini(filename, compatibility_rule); +#endif // ENABLE_BINARIZED_GCODE } ConfigSubstitutions ConfigBase::load_from_ini(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule) @@ -925,15 +959,15 @@ private: }; // Load the config keys from the tail of a G-code file. -ConfigSubstitutions ConfigBase::load_from_gcode_file(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule) +ConfigSubstitutions ConfigBase::load_from_gcode_file(const std::string &filename, ForwardCompatibilitySubstitutionRule compatibility_rule) { // Read a 64k block from the end of the G-code. - boost::nowide::ifstream ifs(file, std::ifstream::binary); + boost::nowide::ifstream ifs(filename, std::ifstream::binary); // Look for Slic3r or PrusaSlicer header. // Look for the header across the whole file as the G-code may have been extended at the start by a post-processing script or the user. bool has_delimiters = false; { - static constexpr const char slic3r_gcode_header[] = "; generated by Slic3r "; + static constexpr const char slic3r_gcode_header[] = "; generated by Slic3r "; static constexpr const char prusaslicer_gcode_header[] = "; generated by PrusaSlicer "; std::string header; bool header_found = false; @@ -983,7 +1017,7 @@ ConfigSubstitutions ConfigBase::load_from_gcode_file(const std::string &file, Fo break; } if (! end_found) - throw Slic3r::RuntimeError(format("Configuration block closing tag \"; prusaslicer_config = end\" not found when reading %1%", file)); + throw Slic3r::RuntimeError(format("Configuration block closing tag \"; prusaslicer_config = end\" not found when reading %1%", filename)); std::string key, value; while (reader.getline(line)) { if (line == "; prusaslicer_config = begin") { @@ -1006,7 +1040,7 @@ ConfigSubstitutions ConfigBase::load_from_gcode_file(const std::string &file, Fo } } if (! begin_found) - throw Slic3r::RuntimeError(format("Configuration block opening tag \"; prusaslicer_config = begin\" not found when reading %1%", file)); + throw Slic3r::RuntimeError(format("Configuration block opening tag \"; prusaslicer_config = begin\" not found when reading %1%", filename)); } else { @@ -1014,8 +1048,8 @@ ConfigSubstitutions ConfigBase::load_from_gcode_file(const std::string &file, Fo // Try a heuristics reading the G-code from back. ifs.seekg(0, ifs.end); auto file_length = ifs.tellg(); - auto data_length = std::min(65535, file_length - header_end_pos); - ifs.seekg(file_length - data_length, ifs.beg); + auto data_length = std::min(65535, file_length - header_end_pos); + ifs.seekg(file_length - data_length, ifs.beg); std::vector data(size_t(data_length) + 1, 0); ifs.read(data.data(), data_length); ifs.close(); @@ -1023,10 +1057,52 @@ ConfigSubstitutions ConfigBase::load_from_gcode_file(const std::string &file, Fo } if (key_value_pairs < 80) - throw Slic3r::RuntimeError(format("Suspiciously low number of configuration values extracted from %1%: %2%", file, key_value_pairs)); + throw Slic3r::RuntimeError(format("Suspiciously low number of configuration values extracted from %1%: %2%", filename, key_value_pairs)); return std::move(substitutions_ctxt.substitutions); } +#if ENABLE_BINARIZED_GCODE +ConfigSubstitutions ConfigBase::load_from_binary_gcode_file(const std::string& filename, ForwardCompatibilitySubstitutionRule compatibility_rule) +{ + ConfigSubstitutionContext substitutions_ctxt(compatibility_rule); + + FilePtr file{ boost::nowide::fopen(filename.c_str(), "rb") }; + if (file.f == nullptr) + throw Slic3r::RuntimeError(format("Error opening the file: %1%", filename)); + + bgcode::core::EResult res = bgcode::core::is_valid_binary_gcode(*file.f); + if (res != bgcode::core::EResult::Success) + throw Slic3r::RuntimeError(format("The selected file is not a valid binary gcode.\nError: %1%", + std::string(bgcode::core::translate_result(res)))); + + fseek(file.f, 0, SEEK_END); + const long file_size = ftell(file.f); + rewind(file.f); + + bgcode::core::FileHeader file_header; + res = bgcode::core::read_header(*file.f, file_header, nullptr); + if (res != bgcode::core::EResult::Success) + throw Slic3r::RuntimeError(format("Error while reading file '%1%': %2%", filename, std::string(bgcode::core::translate_result(res)))); + + bgcode::core::BlockHeader block_header; + res = read_next_block_header(*file.f, file_header, block_header, bgcode::core::EBlockType::SlicerMetadata, true); + if (res != bgcode::core::EResult::Success) + throw Slic3r::RuntimeError(format("Error while reading file '%1%': %2%", filename, std::string(bgcode::core::translate_result(res)))); + if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::SlicerMetadata) + throw Slic3r::RuntimeError(format("Unable to find slicer metadata block in file: '%1%'", filename)); + bgcode::base::SlicerMetadataBlock slicer_metadata_block; + res = slicer_metadata_block.read_data(*file.f, file_header, block_header); + if (res != bgcode::core::EResult::Success) + throw Slic3r::RuntimeError(format("Error while reading file '%1%': %2%", filename, std::string(bgcode::core::translate_result(res)))); + + for (const auto& [key, value] : slicer_metadata_block.raw_data) { + this->set_deserialize(key, value, substitutions_ctxt); + } + + return std::move(substitutions_ctxt.substitutions); +} +#endif // ENABLE_BINARIZED_GCODE + void ConfigBase::save(const std::string &file) const { boost::nowide::ofstream c; diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index 28410b87dc..4995384203 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -2303,7 +2303,10 @@ public: // Loading a "will be one day a legacy format" of configuration stored into 3MF or AMF. // Accepts the same data as load_from_ini_string(), only with each configuration line possibly prefixed with a semicolon (G-code comment). ConfigSubstitutions load_from_ini_string_commented(std::string &&data, ForwardCompatibilitySubstitutionRule compatibility_rule); - ConfigSubstitutions load_from_gcode_file(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule); + ConfigSubstitutions load_from_gcode_file(const std::string &filename, ForwardCompatibilitySubstitutionRule compatibility_rule); +#if ENABLE_BINARIZED_GCODE + ConfigSubstitutions load_from_binary_gcode_file(const std::string& filename, ForwardCompatibilitySubstitutionRule compatibility_rule); +#endif // ENABLE_BINARIZED_GCODE ConfigSubstitutions load(const boost::property_tree::ptree &tree, ForwardCompatibilitySubstitutionRule compatibility_rule); void save(const std::string &file) const; diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 66f7cc297a..37ac7eb37a 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -1122,34 +1122,23 @@ void GCodeProcessor::process_ascii_file(const std::string& filename, std::functi #if ENABLE_BINARIZED_GCODE void GCodeProcessor::process_binary_file(const std::string& filename, std::function cancel_callback) { - class ScopedFile - { - public: - explicit ScopedFile(FILE* file) : m_file(file) {} - ~ScopedFile() { if (m_file != nullptr) fclose(m_file); } - private: - FILE* m_file{ nullptr }; - }; - #if ENABLE_GCODE_VIEWER_STATISTICS m_start_time = std::chrono::high_resolution_clock::now(); #endif // ENABLE_GCODE_VIEWER_STATISTICS - FILE* file = boost::nowide::fopen(filename.c_str(), "rb"); - if (file == nullptr) + FilePtr file{ boost::nowide::fopen(filename.c_str(), "rb") }; + if (file.f == nullptr) throw Slic3r::RuntimeError("Unable to open file: " + filename + "\n"); - fseek(file, 0, SEEK_END); - const long file_size = ftell(file); - rewind(file); - - ScopedFile scoped_file(file); + fseek(file.f, 0, SEEK_END); + const long file_size = ftell(file.f); + rewind(file.f); bgcode::core::set_checksum_max_cache_size(1024); // read file header bgcode::core::FileHeader file_header; - bgcode::core::EResult res = bgcode::core::read_header(*file, file_header, nullptr); + bgcode::core::EResult res = bgcode::core::read_header(*file.f, file_header, nullptr); if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError("File: " + filename + "does not contain a valid binary gcode\n Error: " + std::string(bgcode::core::translate_result(res)) + "\n"); @@ -1158,15 +1147,15 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct // read file metadata block bgcode::core::BlockHeader block_header; - res = bgcode::core::read_next_block_header(*file, file_header, block_header, verify_checksum); + res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, verify_checksum); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::FileMetadata) throw Slic3r::RuntimeError("Unable to find file metadata block in file: " + filename + "\n"); bgcode::base::FileMetadataBlock file_metadata_block; - res = file_metadata_block.read_data(*file, file_header, block_header); + res = file_metadata_block.read_data(*file.f, file_header, block_header); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); auto producer_it = std::find_if(file_metadata_block.raw_data.begin(), file_metadata_block.raw_data.end(), [](const std::pair& item) { return item.first == "Producer"; }); if (producer_it != file_metadata_block.raw_data.end() && boost::starts_with(producer_it->second, std::string(SLIC3R_APP_NAME))) @@ -1175,15 +1164,15 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct m_producer = EProducer::Unknown; // read printer metadata block - res = bgcode::core::read_next_block_header(*file, file_header, block_header, verify_checksum); + res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, verify_checksum); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::PrinterMetadata) throw Slic3r::RuntimeError("Unable to find printer metadata block in file: " + filename + "\n"); bgcode::base::PrinterMetadataBlock printer_metadata_block; - res = printer_metadata_block.read_data(*file, file_header, block_header); + res = printer_metadata_block.read_data(*file.f, file_header, block_header); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); #if ENABLE_BINARIZED_GCODE_DEBUG OutputDebugStringA("Printer metadata:\n"); for (const auto& [key, value] : printer_metadata_block.raw_data) { @@ -1195,15 +1184,15 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct #endif // ENABLE_BINARIZED_GCODE_DEBUG // read thumbnail blocks - res = bgcode::core::read_next_block_header(*file, file_header, block_header, verify_checksum); + res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, verify_checksum); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); while ((bgcode::core::EBlockType)block_header.type == bgcode::core::EBlockType::Thumbnail) { bgcode::base::ThumbnailBlock thumbnail_block; - res = thumbnail_block.read_data(*file, file_header, block_header); + res = thumbnail_block.read_data(*file.f, file_header, block_header); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); #if ENABLE_BINARIZED_GCODE_DEBUG if (thumbnail_block.data.size() > 0) { auto format_filename = [](const std::string& stem, const bgcode::base::ThumbnailBlock& block) { @@ -1228,18 +1217,18 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct } #endif // ENABLE_BINARIZED_GCODE_DEBUG - res = bgcode::core::read_next_block_header(*file, file_header, block_header, verify_checksum); + res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, verify_checksum); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); } // read print metadata block if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::PrintMetadata) throw Slic3r::RuntimeError("Unable to find print metadata block in file: " + filename + "\n"); bgcode::base::PrintMetadataBlock print_metadata_block; - res = print_metadata_block.read_data(*file, file_header, block_header); + res = print_metadata_block.read_data(*file.f, file_header, block_header); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); #if ENABLE_BINARIZED_GCODE_DEBUG OutputDebugStringA("Print metadata:\n"); for (const auto& [key, value] : print_metadata_block.raw_data) { @@ -1251,15 +1240,15 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct #endif // ENABLE_BINARIZED_GCODE_DEBUG // read slicer metadata block - res = bgcode::core::read_next_block_header(*file, file_header, block_header, verify_checksum); + res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, verify_checksum); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::SlicerMetadata) throw Slic3r::RuntimeError("Unable to find slicer metadata block in file: " + filename + "\n"); bgcode::base::SlicerMetadataBlock slicer_metadata_block; - res = slicer_metadata_block.read_data(*file, file_header, block_header); + res = slicer_metadata_block.read_data(*file.f, file_header, block_header); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); #if ENABLE_BINARIZED_GCODE_DEBUG OutputDebugStringA("Slicer metadata:\n"); for (const auto& [key, value] : slicer_metadata_block.raw_data) { @@ -1286,16 +1275,16 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct initialize_result_moves(); // read gcodes block - res = bgcode::core::read_next_block_header(*file, file_header, block_header, verify_checksum); + res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, verify_checksum); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::GCode) throw Slic3r::RuntimeError("Unable to find gcode block in file: " + filename + "\n"); while ((bgcode::core::EBlockType)block_header.type == bgcode::core::EBlockType::GCode) { bgcode::base::GCodeBlock block; - res = block.read_data(*file, file_header, block_header); + res = block.read_data(*file.f, file_header, block_header); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); // TODO: Update m_result.lines_ends @@ -1303,12 +1292,12 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct this->process_gcode_line(line, true); }); - if (ftell(file) == file_size) + if (ftell(file.f) == file_size) break; - res = bgcode::core::read_next_block_header(*file, file_header, block_header, verify_checksum); + res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, verify_checksum); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file: " + filename + ": " + std::string(bgcode::core::translate_result(res)) + "\n"); + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); } // Don't post-process the G-code to update time stamps. diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index 730574af13..45e4d0535f 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -22,6 +22,9 @@ #include #include +#if ENABLE_BINARIZED_GCODE +#include +#endif // ENABLE_BINARIZED_GCODE // Store the print/filament/printer presets into a "presets" subdirectory of the Slic3rPE config dir. // This breaks compatibility with the upstream Slic3r if the --datadir is used to switch between the two versions. @@ -875,14 +878,32 @@ DynamicPrintConfig PresetBundle::full_sla_config() const // If the file is loaded successfully, its print / filament / printer profiles will be activated. ConfigSubstitutions PresetBundle::load_config_file(const std::string &path, ForwardCompatibilitySubstitutionRule compatibility_rule) { - if (is_gcode_file(path)) { - DynamicPrintConfig config; - config.apply(FullPrintConfig::defaults()); +#if ENABLE_BINARIZED_GCODE + if (is_gcode_file(path)) { + FILE* file = boost::nowide::fopen(path.c_str(), "rb"); + if (file == nullptr) + throw Slic3r::RuntimeError("Error opening the file: " + path + "\n"); + const bool is_binary = bgcode::core::is_valid_binary_gcode(*file, true) == bgcode::core::EResult::Success; + fclose(file); + + DynamicPrintConfig config; + config.apply(FullPrintConfig::defaults()); + ConfigSubstitutions config_substitutions = is_binary ? config.load_from_binary_gcode_file(path, compatibility_rule) : + config.load_from_gcode_file(path, compatibility_rule); + Preset::normalize(config); + load_config_file_config(path, true, std::move(config)); + return config_substitutions; + } +#else + if (is_gcode_file(path)) { + DynamicPrintConfig config; + config.apply(FullPrintConfig::defaults()); ConfigSubstitutions config_substitutions = config.load_from_gcode_file(path, compatibility_rule); Preset::normalize(config); - load_config_file_config(path, true, std::move(config)); - return config_substitutions; - } + load_config_file_config(path, true, std::move(config)); + return config_substitutions; + } +#endif // ENABLE_BINARIZED_GCODE // 1) Try to load the config file into a boost property tree. boost::property_tree::ptree tree; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index e0c1527e84..b9eb5a3c1c 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5435,16 +5435,6 @@ void Plater::reload_gcode_from_disk() } #if ENABLE_BINARIZED_GCODE -class ScopedFile -{ -public: - explicit ScopedFile(FILE* file) : m_file(file) {} - ~ScopedFile() { if (m_file != nullptr) fclose(m_file); } - void unscope() { m_file = nullptr; } -private: - FILE* m_file{ nullptr }; -}; - void Plater::convert_gcode_to_ascii() { // Ask user for a gcode file name. @@ -5452,36 +5442,33 @@ void Plater::convert_gcode_to_ascii() wxGetApp().load_gcode(this, input_file); // Open source file - FILE* in_file = boost::nowide::fopen(into_u8(input_file).c_str(), "rb"); - if (in_file == nullptr) { + FilePtr in_file{ boost::nowide::fopen(into_u8(input_file).c_str(), "rb") }; + if (in_file.f == nullptr) { MessageDialog msg_dlg(this, _L("Unable to open the selected file."), _L("Error"), wxICON_ERROR | wxOK); msg_dlg.ShowModal(); return; } - ScopedFile scoped_in_file(in_file); // Set out filename boost::filesystem::path path(into_u8(input_file)); const std::string output_file = path.parent_path().string() + "/" + path.stem().string() + "_ascii" + path.extension().string(); // Open destination file - FILE* out_file = boost::nowide::fopen(output_file.c_str(), "wb"); - if (out_file == nullptr) { + FilePtr out_file{ boost::nowide::fopen(output_file.c_str(), "wb") }; + if (out_file.f == nullptr) { MessageDialog msg_dlg(this, _L("Unable to open output file."), _L("Error"), wxICON_ERROR | wxOK); msg_dlg.ShowModal(); return; } - ScopedFile scoped_out_file(out_file); // Perform conversion { wxBusyCursor busy; - bgcode::core::EResult res = bgcode::convert::from_binary_to_ascii(*in_file, *out_file, true); + bgcode::core::EResult res = bgcode::convert::from_binary_to_ascii(*in_file.f, *out_file.f, true); if (res != bgcode::core::EResult::Success) { MessageDialog msg_dlg(this, _L(std::string(bgcode::core::translate_result(res))), _L("Error converting gcode file"), wxICON_INFORMATION | wxOK); msg_dlg.ShowModal(); - scoped_out_file.unscope(); - fclose(out_file); + out_file.close(); boost::nowide::remove(output_file.c_str()); return; } @@ -5498,37 +5485,34 @@ void Plater::convert_gcode_to_binary() wxGetApp().load_gcode(this, input_file); // Open source file - FILE* in_file = boost::nowide::fopen(into_u8(input_file).c_str(), "rb"); - if (in_file == nullptr) { + FilePtr in_file{ boost::nowide::fopen(into_u8(input_file).c_str(), "rb") }; + if (in_file.f == nullptr) { MessageDialog msg_dlg(this, _L("Unable to open the selected file."), _L("Error"), wxICON_ERROR | wxOK); msg_dlg.ShowModal(); return; } - ScopedFile scoped_in_file(in_file); // Set out filename boost::filesystem::path path(into_u8(input_file)); const std::string output_file = path.parent_path().string() + "/" + path.stem().string() + "_binary" + path.extension().string(); // Open destination file - FILE* out_file = boost::nowide::fopen(output_file.c_str(), "wb"); - if (out_file == nullptr) { + FilePtr out_file{ boost::nowide::fopen(output_file.c_str(), "wb") }; + if (out_file.f == nullptr) { MessageDialog msg_dlg(this, _L("Unable to open output file."), _L("Error"), wxICON_ERROR | wxOK); msg_dlg.ShowModal(); return; } - ScopedFile scoped_out_file(out_file); // Perform conversion { wxBusyCursor busy; const bgcode::base::BinarizerConfig& binarizer_config = GCodeProcessor::get_binarizer_config(); - bgcode::core::EResult res = bgcode::convert::from_ascii_to_binary(*in_file, *out_file, binarizer_config); + bgcode::core::EResult res = bgcode::convert::from_ascii_to_binary(*in_file.f, *out_file.f, binarizer_config); if (res != bgcode::core::EResult::Success) { MessageDialog msg_dlg(this, _L(std::string(bgcode::core::translate_result(res))), _L("Error converting gcode file"), wxICON_INFORMATION | wxOK); msg_dlg.ShowModal(); - scoped_out_file.unscope(); - fclose(out_file); + out_file.close(); boost::nowide::remove(output_file.c_str()); return; } From 5deb4470c7ed8e0709809f98525082965ba700d8 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 4 Aug 2023 14:44:45 +0200 Subject: [PATCH 058/136] Changes required by updated library libbgcode --- src/libslic3r/CMakeLists.txt | 2 +- src/libslic3r/Config.cpp | 4 ++-- src/libslic3r/GCode.cpp | 8 ++++---- src/libslic3r/GCode.hpp | 2 +- src/libslic3r/GCode/GCodeProcessor.cpp | 22 +++++++++++----------- src/libslic3r/GCode/GCodeProcessor.hpp | 12 ++++++------ src/libslic3r/GCode/Thumbnails.hpp | 6 +++--- src/slic3r/GUI/GLCanvas3D.cpp | 2 +- src/slic3r/GUI/Plater.cpp | 2 +- 9 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index ce57ba00f2..de4ec3a5d7 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -16,7 +16,7 @@ endif() find_package(LibBGCode REQUIRED COMPONENTS Convert) slic3r_remap_configs(LibBGCode::bgcode_core RelWithDebInfo Release) -slic3r_remap_configs(LibBGCode::bgcode_base RelWithDebInfo Release) +slic3r_remap_configs(LibBGCode::bgcode_binarize RelWithDebInfo Release) slic3r_remap_configs(LibBGCode::bgcode_convert RelWithDebInfo Release) set(SLIC3R_SOURCES diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index af28307faf..5d2de1aaa5 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -24,7 +24,7 @@ #include #if ENABLE_BINARIZED_GCODE -#include +#include #endif // ENABLE_BINARIZED_GCODE //FIXME for GCodeFlavor and gcfMarlin (for forward-compatibility conversion) @@ -1090,7 +1090,7 @@ ConfigSubstitutions ConfigBase::load_from_binary_gcode_file(const std::string& f throw Slic3r::RuntimeError(format("Error while reading file '%1%': %2%", filename, std::string(bgcode::core::translate_result(res)))); if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::SlicerMetadata) throw Slic3r::RuntimeError(format("Unable to find slicer metadata block in file: '%1%'", filename)); - bgcode::base::SlicerMetadataBlock slicer_metadata_block; + bgcode::binarize::SlicerMetadataBlock slicer_metadata_block; res = slicer_metadata_block.read_data(*file.f, file_header, block_header); if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError(format("Error while reading file '%1%': %2%", filename, std::string(bgcode::core::translate_result(res)))); diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 0740ffb415..d10cd6bb59 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -841,7 +841,7 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu m_processor.initialize(path_tmp); m_processor.set_print(print); #if ENABLE_BINARIZED_GCODE - m_processor.get_binary_data() = bgcode::base::BinaryData(); + m_processor.get_binary_data() = bgcode::binarize::BinaryData(); #endif // ENABLE_BINARIZED_GCODE GCodeOutputStream file(boost::nowide::fopen(path_tmp.c_str(), "wb"), m_processor); if (! file.is_open()) @@ -984,7 +984,7 @@ namespace DoExport { unsigned int initial_extruder_id, PrintStatistics &print_statistics, bool export_binary_data, - bgcode::base::BinaryData &binary_data) + bgcode::binarize::BinaryData &binary_data) #else static std::string update_print_stats_and_format_filament_stats( const bool has_wipe_tower, @@ -1132,7 +1132,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // 1) generate the thumbnails // 2) collect the config data if (export_to_binary_gcode) { - bgcode::base::BinaryData& binary_data = m_processor.get_binary_data(); + bgcode::binarize::BinaryData& binary_data = m_processor.get_binary_data(); // Unit tests or command line slicing may not define "thumbnails" or "thumbnails_format". // If "thumbnails_format" is not defined, export to PNG. @@ -1611,7 +1611,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato file.write(filament_stats_string_out); if (export_to_binary_gcode) { - bgcode::base::BinaryData& binary_data = m_processor.get_binary_data(); + bgcode::binarize::BinaryData& binary_data = m_processor.get_binary_data(); if (print.m_print_statistics.total_toolchanges > 0) binary_data.print_metadata.raw_data.push_back({ "total toolchanges", std::to_string(print.m_print_statistics.total_toolchanges) }); } diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 1821576fd7..4032aef39e 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -29,7 +29,7 @@ #include "GCode/PressureEqualizer.hpp" #if ENABLE_BINARIZED_GCODE -#include +#include #endif // ENABLE_BINARIZED_GCODE namespace Slic3r { diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 37ac7eb37a..5787b6e17d 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -70,7 +70,7 @@ const float GCodeProcessor::Wipe_Width = 0.05f; const float GCodeProcessor::Wipe_Height = 0.05f; #if ENABLE_BINARIZED_GCODE -bgcode::base::BinarizerConfig GCodeProcessor::s_binarizer_config{}; +bgcode::binarize::BinarizerConfig GCodeProcessor::s_binarizer_config{}; #endif // ENABLE_BINARIZED #if ENABLE_GCODE_VIEWER_DATA_CHECKING @@ -1152,7 +1152,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::FileMetadata) throw Slic3r::RuntimeError("Unable to find file metadata block in file: " + filename + "\n"); - bgcode::base::FileMetadataBlock file_metadata_block; + bgcode::binarize::FileMetadataBlock file_metadata_block; res = file_metadata_block.read_data(*file.f, file_header, block_header); if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); @@ -1169,7 +1169,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::PrinterMetadata) throw Slic3r::RuntimeError("Unable to find printer metadata block in file: " + filename + "\n"); - bgcode::base::PrinterMetadataBlock printer_metadata_block; + bgcode::binarize::PrinterMetadataBlock printer_metadata_block; res = printer_metadata_block.read_data(*file.f, file_header, block_header); if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); @@ -1189,13 +1189,13 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); while ((bgcode::core::EBlockType)block_header.type == bgcode::core::EBlockType::Thumbnail) { - bgcode::base::ThumbnailBlock thumbnail_block; + bgcode::binarize::ThumbnailBlock thumbnail_block; res = thumbnail_block.read_data(*file.f, file_header, block_header); if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); #if ENABLE_BINARIZED_GCODE_DEBUG if (thumbnail_block.data.size() > 0) { - auto format_filename = [](const std::string& stem, const bgcode::base::ThumbnailBlock& block) { + auto format_filename = [](const std::string& stem, const bgcode::binarize::ThumbnailBlock& block) { std::string ret = stem + "_" + std::to_string(block.width) + "x" + std::to_string(block.height); switch ((bgcode::core::EThumbnailFormat)block.format) { @@ -1225,7 +1225,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct // read print metadata block if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::PrintMetadata) throw Slic3r::RuntimeError("Unable to find print metadata block in file: " + filename + "\n"); - bgcode::base::PrintMetadataBlock print_metadata_block; + bgcode::binarize::PrintMetadataBlock print_metadata_block; res = print_metadata_block.read_data(*file.f, file_header, block_header); if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); @@ -1245,7 +1245,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::SlicerMetadata) throw Slic3r::RuntimeError("Unable to find slicer metadata block in file: " + filename + "\n"); - bgcode::base::SlicerMetadataBlock slicer_metadata_block; + bgcode::binarize::SlicerMetadataBlock slicer_metadata_block; res = slicer_metadata_block.read_data(*file.f, file_header, block_header); if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); @@ -1281,7 +1281,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::GCode) throw Slic3r::RuntimeError("Unable to find gcode block in file: " + filename + "\n"); while ((bgcode::core::EBlockType)block_header.type == bgcode::core::EBlockType::GCode) { - bgcode::base::GCodeBlock block; + bgcode::binarize::GCodeBlock block; res = block.read_data(*file.f, file_header, block_header); if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); @@ -3685,7 +3685,7 @@ void GCodeProcessor::post_process() }; // update binary data - bgcode::base::BinaryData& binary_data = m_binarizer.get_binary_data(); + bgcode::binarize::BinaryData& binary_data = m_binarizer.get_binary_data(); binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedMm, stringify(filament_mm) }); binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedCm3, stringify(filament_cm3) }); binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedG, stringify(filament_g) }); @@ -3832,12 +3832,12 @@ void GCodeProcessor::post_process() size_t m_out_file_pos{ 0 }; #if ENABLE_BINARIZED_GCODE - bgcode::base::Binarizer& m_binarizer; + bgcode::binarize::Binarizer& m_binarizer; #endif // ENABLE_BINARIZED_GCODE public: #if ENABLE_BINARIZED_GCODE - ExportLines(bgcode::base::Binarizer& binarizer, EWriteType type, TimeMachine& machine) + ExportLines(bgcode::binarize::Binarizer& binarizer, EWriteType type, TimeMachine& machine) #ifndef NDEBUG : m_statistics(*this), m_binarizer(binarizer), m_write_type(type), m_machine(machine) {} #else diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 303a6868d1..bef1cd68f3 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -8,7 +8,7 @@ #include "libslic3r/CustomGCode.hpp" #if ENABLE_BINARIZED_GCODE -#include +#include #endif // ENABLE_BINARIZED_GCODE #include @@ -528,14 +528,14 @@ namespace Slic3r { #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING #if ENABLE_BINARIZED_GCODE_DEBUG_WINDOW - static bgcode::base::BinarizerConfig& get_binarizer_config() { return s_binarizer_config; } + static bgcode::binarize::BinarizerConfig& get_binarizer_config() { return s_binarizer_config; } #endif // ENABLE_BINARIZED_GCODE_DEBUG_WINDOW private: GCodeReader m_parser; #if ENABLE_BINARIZED_GCODE - bgcode::base::Binarizer m_binarizer; - static bgcode::base::BinarizerConfig s_binarizer_config; + bgcode::binarize::Binarizer m_binarizer; + static bgcode::binarize::BinarizerConfig s_binarizer_config; #endif // ENABLE_BINARIZED_GCODE EUnits m_units; @@ -635,8 +635,8 @@ namespace Slic3r { void apply_config(const PrintConfig& config); void set_print(Print* print) { m_print = print; } #if ENABLE_BINARIZED_GCODE - bgcode::base::BinaryData& get_binary_data() { return m_binarizer.get_binary_data(); } - const bgcode::base::BinaryData& get_binary_data() const { return m_binarizer.get_binary_data(); } + bgcode::binarize::BinaryData& get_binary_data() { return m_binarizer.get_binary_data(); } + const bgcode::binarize::BinaryData& get_binary_data() const { return m_binarizer.get_binary_data(); } #endif // ENABLE_BINARIZED_GCODE void enable_stealth_time_estimator(bool enabled); diff --git a/src/libslic3r/GCode/Thumbnails.hpp b/src/libslic3r/GCode/Thumbnails.hpp index 7e99013962..4bd6637879 100644 --- a/src/libslic3r/GCode/Thumbnails.hpp +++ b/src/libslic3r/GCode/Thumbnails.hpp @@ -10,7 +10,7 @@ #include #if ENABLE_BINARIZED_GCODE -#include +#include #endif // ENABLE_BINARIZED_GCODE #include @@ -61,7 +61,7 @@ inline void export_thumbnails_to_file(ThumbnailsGeneratorCallback &thumbnail_cb, #if ENABLE_BINARIZED_GCODE template -inline void generate_binary_thumbnails(ThumbnailsGeneratorCallback& thumbnail_cb, std::vector& out_thumbnails, +inline void generate_binary_thumbnails(ThumbnailsGeneratorCallback& thumbnail_cb, std::vector& out_thumbnails, const std::vector& sizes, GCodeThumbnailsFormat format, ThrowIfCanceledCallback throw_if_canceled) { out_thumbnails.clear(); @@ -71,7 +71,7 @@ inline void generate_binary_thumbnails(ThumbnailsGeneratorCallback& thumbnail_cb if (data.is_valid()) { auto compressed = compress_thumbnail(data, format); if (compressed->data != nullptr && compressed->size > 0) { - bgcode::base::ThumbnailBlock& block = out_thumbnails.emplace_back(bgcode::base::ThumbnailBlock()); + bgcode::binarize::ThumbnailBlock& block = out_thumbnails.emplace_back(bgcode::binarize::ThumbnailBlock()); block.width = (uint16_t)data.width; block.height = (uint16_t)data.height; switch (format) { diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index b609795fd2..c4a8d093df 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -7874,7 +7874,7 @@ void GLCanvas3D::GizmoHighlighter::blink() #if ENABLE_BINARIZED_GCODE_DEBUG_WINDOW void GLCanvas3D::show_binary_gcode_debug_window() { - bgcode::base::BinarizerConfig& binarizer_config = GCodeProcessor::get_binarizer_config(); + bgcode::binarize::BinarizerConfig& binarizer_config = GCodeProcessor::get_binarizer_config(); ImGuiWrapper& imgui = *wxGetApp().imgui(); imgui.begin(std::string("Binary GCode"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index b9eb5a3c1c..ddab7d6c32 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5507,7 +5507,7 @@ void Plater::convert_gcode_to_binary() // Perform conversion { wxBusyCursor busy; - const bgcode::base::BinarizerConfig& binarizer_config = GCodeProcessor::get_binarizer_config(); + const bgcode::binarize::BinarizerConfig& binarizer_config = GCodeProcessor::get_binarizer_config(); bgcode::core::EResult res = bgcode::convert::from_ascii_to_binary(*in_file.f, *out_file.f, binarizer_config); if (res != bgcode::core::EResult::Success) { MessageDialog msg_dlg(this, _L(std::string(bgcode::core::translate_result(res))), _L("Error converting gcode file"), wxICON_INFORMATION | wxOK); From 7b822184a7a124cba970a17e709d5f1825c3454d Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 7 Aug 2023 09:20:50 +0200 Subject: [PATCH 059/136] Fixes in Plater::convert_gcode_to_ascii() and Plater::convert_gcode_to_binary() --- src/slic3r/GUI/Plater.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index ddab7d6c32..693ba0e5fa 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5440,6 +5440,8 @@ void Plater::convert_gcode_to_ascii() // Ask user for a gcode file name. wxString input_file; wxGetApp().load_gcode(this, input_file); + if (input_file.empty()) + return; // Open source file FilePtr in_file{ boost::nowide::fopen(into_u8(input_file).c_str(), "rb") }; @@ -5483,6 +5485,8 @@ void Plater::convert_gcode_to_binary() // Ask user for a gcode file name. wxString input_file; wxGetApp().load_gcode(this, input_file); + if (input_file.empty()) + return; // Open source file FilePtr in_file{ boost::nowide::fopen(into_u8(input_file).c_str(), "rb") }; From 57df182d6cdd79a6b6a98e73775b4ed52a56a58d Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 7 Aug 2023 12:17:13 +0200 Subject: [PATCH 060/136] GCodeViewer: Fixed crash when selecting Layer time view after importing a binary gcode encoded using MeatPack with no comments --- src/slic3r/GUI/GCodeViewer.cpp | 50 ++++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 11 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index ed4713737f..fd9a44db3d 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -2309,20 +2309,26 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool case EViewType::Temperature: { color = m_extrusions.ranges.temperature.get_color_at(path.temperature); break; } case EViewType::LayerTimeLinear: case EViewType::LayerTimeLogarithmic: { - const Path::Sub_Path& sub_path = path.sub_paths.front(); - double z = static_cast(sub_path.first.position.z()); - const std::vector& zs = m_layers.get_zs(); - const std::vector& ranges = m_layers.get_ranges(); - size_t time_mode_id = static_cast(m_time_estimate_mode); - for (size_t i = 0; i < zs.size(); ++i) { - if (std::abs(zs[i] - z) < EPSILON) { - if (ranges[i].contains(sub_path.first.s_id)) { - color = m_extrusions.ranges.layer_time[time_mode_id].get_color_at(m_layers_times[time_mode_id][i], - (m_view_type == EViewType::LayerTimeLinear) ? Extrusions::Range::EType::Linear : Extrusions::Range::EType::Logarithmic); - break; +#if ENABLE_BINARIZED_GCODE + if (!m_layers_times.empty() && m_layers.size() == m_layers_times.front().size()) { +#endif // ENABLE_BINARIZED_GCODE + const Path::Sub_Path& sub_path = path.sub_paths.front(); + double z = static_cast(sub_path.first.position.z()); + const std::vector& zs = m_layers.get_zs(); + const std::vector& ranges = m_layers.get_ranges(); + size_t time_mode_id = static_cast(m_time_estimate_mode); + for (size_t i = 0; i < zs.size(); ++i) { + if (std::abs(zs[i] - z) < EPSILON) { + if (ranges[i].contains(sub_path.first.s_id)) { + color = m_extrusions.ranges.layer_time[time_mode_id].get_color_at(m_layers_times[time_mode_id][i], + (m_view_type == EViewType::LayerTimeLinear) ? Extrusions::Range::EType::Linear : Extrusions::Range::EType::Logarithmic); + break; + } } } +#if ENABLE_BINARIZED_GCODE } +#endif // ENABLE_BINARIZED_GCODE break; } case EViewType::VolumetricRate: { color = m_extrusions.ranges.volumetric_rate.get_color_at(path.volumetric_rate); break; } @@ -3590,6 +3596,27 @@ void GCodeViewer::render_legend(float& legend_height) ImGui::PushStyleColor(ImGuiCol_FrameBg, { 0.1f, 0.1f, 0.1f, 0.8f }); ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, { 0.2f, 0.2f, 0.2f, 0.8f }); +#if ENABLE_BINARIZED_GCODE + std::vector view_options; + std::vector view_options_id; + if (!m_layers_times.empty() && m_layers.size() == m_layers_times.front().size()) { + view_options = { _u8L("Feature type"), _u8L("Height (mm)"), _u8L("Width (mm)"), _u8L("Speed (mm/s)"), _u8L("Fan speed (%)"), + _u8L("Temperature (°C)"), _u8L("Volumetric flow rate (mm³/s)"), _u8L("Layer time (linear)"), _u8L("Layer time (logarithmic)"), + _u8L("Tool"), _u8L("Color Print") }; + view_options_id = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + } + else { + view_options = { _u8L("Feature type"), _u8L("Height (mm)"), _u8L("Width (mm)"), _u8L("Speed (mm/s)"), _u8L("Fan speed (%)"), + _u8L("Temperature (°C)"), _u8L("Volumetric flow rate (mm³/s)"), _u8L("Tool"), _u8L("Color Print") }; + view_options_id = { 0, 1, 2, 3, 4, 5, 6, 9, 10 }; + if (view_type == 7 || view_type == 8) + view_type = 0; + } + auto view_type_it = std::find(view_options_id.begin(), view_options_id.end(), view_type); + int view_type_id = (view_type_it == view_options_id.end()) ? 0 : std::distance(view_options_id.begin(), view_type_it); + if (imgui.combo(std::string(), view_options, view_type_id, ImGuiComboFlags_HeightLargest, 0.0f, -1.0f)) + view_type = view_options_id[view_type_id]; +#else imgui.combo(std::string(), { _u8L("Feature type"), _u8L("Height (mm)"), _u8L("Width (mm)"), @@ -3601,6 +3628,7 @@ void GCodeViewer::render_legend(float& legend_height) _u8L("Layer time (logarithmic)"), _u8L("Tool"), _u8L("Color Print") }, view_type, ImGuiComboFlags_HeightLargest, 0.0f, -1.0f); +#endif // ENABLE_BINARIZED_GCODE ImGui::PopStyleColor(2); if (old_view_type != view_type) { From e1724e1fa153884c5315b4005263d39eaeeab3dd Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 8 Aug 2023 12:42:26 +0200 Subject: [PATCH 061/136] Fixes required by changes in interface of libbgcode --- src/libslic3r/GCode/GCodeProcessor.cpp | 4 ++-- src/libslic3r/GCode/Thumbnails.hpp | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 5787b6e17d..7fa5d5382c 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -1196,8 +1196,8 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct #if ENABLE_BINARIZED_GCODE_DEBUG if (thumbnail_block.data.size() > 0) { auto format_filename = [](const std::string& stem, const bgcode::binarize::ThumbnailBlock& block) { - std::string ret = stem + "_" + std::to_string(block.width) + "x" + std::to_string(block.height); - switch ((bgcode::core::EThumbnailFormat)block.format) + std::string ret = stem + "_" + std::to_string(block.params.width) + "x" + std::to_string(block.params.height); + switch ((bgcode::core::EThumbnailFormat)block.params.format) { case bgcode::core::EThumbnailFormat::PNG: { ret += ".png"; break; } case bgcode::core::EThumbnailFormat::JPG: { ret += ".jpg"; break; } diff --git a/src/libslic3r/GCode/Thumbnails.hpp b/src/libslic3r/GCode/Thumbnails.hpp index 4bd6637879..c430d695c2 100644 --- a/src/libslic3r/GCode/Thumbnails.hpp +++ b/src/libslic3r/GCode/Thumbnails.hpp @@ -72,12 +72,12 @@ inline void generate_binary_thumbnails(ThumbnailsGeneratorCallback& thumbnail_cb auto compressed = compress_thumbnail(data, format); if (compressed->data != nullptr && compressed->size > 0) { bgcode::binarize::ThumbnailBlock& block = out_thumbnails.emplace_back(bgcode::binarize::ThumbnailBlock()); - block.width = (uint16_t)data.width; - block.height = (uint16_t)data.height; + block.params.width = (uint16_t)data.width; + block.params.height = (uint16_t)data.height; switch (format) { - case GCodeThumbnailsFormat::PNG: { block.format = (uint16_t)bgcode::core::EThumbnailFormat::PNG; break; } - case GCodeThumbnailsFormat::JPG: { block.format = (uint16_t)bgcode::core::EThumbnailFormat::JPG; break; } - case GCodeThumbnailsFormat::QOI: { block.format = (uint16_t)bgcode::core::EThumbnailFormat::QOI; break; } + case GCodeThumbnailsFormat::PNG: { block.params.format = (uint16_t)bgcode::core::EThumbnailFormat::PNG; break; } + case GCodeThumbnailsFormat::JPG: { block.params.format = (uint16_t)bgcode::core::EThumbnailFormat::JPG; break; } + case GCodeThumbnailsFormat::QOI: { block.params.format = (uint16_t)bgcode::core::EThumbnailFormat::QOI; break; } } block.data.resize(compressed->size); memcpy(block.data.data(), compressed->data, compressed->size); From 7b9982bcfebf96e87cd5fdcf4ec5f26365959e06 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 10 Aug 2023 09:15:51 +0200 Subject: [PATCH 062/136] Disable building of the cmd tool for libbgcode Needs nowide, problematic integration, we would need to get rid of boost::nowide inside PS codebase --- deps/LibBGCode/LibBGCode.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/deps/LibBGCode/LibBGCode.cmake b/deps/LibBGCode/LibBGCode.cmake index d0c6ac6834..6a076e9f67 100644 --- a/deps/LibBGCode/LibBGCode.cmake +++ b/deps/LibBGCode/LibBGCode.cmake @@ -22,6 +22,7 @@ prusaslicer_add_cmake_project(LibBGCode DEPENDS dep_LibBGCode_deps CMAKE_ARGS -DLibBGCode_BUILD_TESTS:BOOL=OFF + -DLibBGCode_BUILD_CMD_TOOL:BOOL=OFF ) if (MSVC) From 6b1dc90075adc98c32955aec55bd542a1cee6a33 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Thu, 10 Aug 2023 10:30:28 +0200 Subject: [PATCH 063/136] Added new printer metadata for binary gcode files --- src/libslic3r/GCode.cpp | 57 ++++++++++++++++++++++++-- src/libslic3r/GCode/GCodeProcessor.cpp | 24 ++++++----- 2 files changed, 66 insertions(+), 15 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index d10cd6bb59..b12b0508e7 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1157,16 +1157,62 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato for (size_t i = 0; i < print.config().filament_type.values.size(); ++i) { filament_types_str += print.config().filament_type.values[i]; if (i < print.config().filament_type.values.size() - 1) - filament_types_str += ", "; + filament_types_str += ";"; } binary_data.printer_metadata.raw_data.emplace_back("filament_type", filament_types_str); // duplicated into config data - std::string nozzle_diameters_str; char buf[1024]; + std::string nozzle_diameters_str; for (size_t i = 0; i < print.config().nozzle_diameter.values.size(); ++i) { - sprintf(buf, i < print.config().nozzle_diameter.values.size() - 1 ? "%.2lf, " : "%.2lf", print.config().nozzle_diameter.values[i]); + sprintf(buf, i < print.config().nozzle_diameter.values.size() - 1 ? "%.2g," : "%.2g", print.config().nozzle_diameter.values[i]); nozzle_diameters_str += buf; } binary_data.printer_metadata.raw_data.emplace_back("nozzle_diameter", nozzle_diameters_str); // duplicated into config data + std::string bed_temperatures_str; + for (size_t i = 0; i < print.config().bed_temperature.values.size(); ++i) { + sprintf(buf, i < print.config().bed_temperature.values.size() - 1 ? "%d," : "%d", print.config().bed_temperature.values[i]); + bed_temperatures_str += buf; + } + binary_data.printer_metadata.raw_data.emplace_back("bed_temperature", bed_temperatures_str); // duplicated into config data + + const DynamicPrintConfig& cfg = print.full_print_config(); + if (auto opt = cfg.option("brim_width"); opt != nullptr) { + sprintf(buf, "%.2g", dynamic_cast(opt)->value); + binary_data.printer_metadata.raw_data.emplace_back("brim_width", buf); // duplicated into config data + } + if (auto opt = cfg.option("fill_density"); opt != nullptr) { + sprintf(buf, "%.2g%%", dynamic_cast(opt)->value); + binary_data.printer_metadata.raw_data.emplace_back("fill_density", buf); // duplicated into config data + } + if (auto opt = cfg.option("layer_height"); opt != nullptr) { + sprintf(buf, "%.2g", dynamic_cast(opt)->value); + binary_data.printer_metadata.raw_data.emplace_back("layer_height", buf); // duplicated into config data + } + if (auto opt = cfg.option("temperature"); opt != nullptr) { + auto values = dynamic_cast(opt)->values; + std::string temperatures_str; + for (size_t i = 0; i < values.size(); ++i) { + sprintf(buf, i < values.size() - 1 ? "%d," : "%d", values[i]); + temperatures_str += buf; + } + binary_data.printer_metadata.raw_data.emplace_back("temperature", temperatures_str); // duplicated into config data + } + if (auto opt = cfg.option("ironing"); opt != nullptr) + binary_data.printer_metadata.raw_data.emplace_back("ironing", dynamic_cast(opt)->value ? "1" : "0"); // duplicated into config data + if (auto opt = cfg.option("support_material"); opt != nullptr) + binary_data.printer_metadata.raw_data.emplace_back("support_material", dynamic_cast(opt)->value ? "1" : "0"); // duplicated into config data + if (auto opt = cfg.option("extruder_colour"); opt != nullptr) { + auto values = dynamic_cast(opt)->values; + std::string extruder_colours_str; + if (values.size() == 1 && values.front().empty()) + extruder_colours_str = "\"\""; + else { + for (size_t i = 0; i < values.size(); ++i) { + sprintf(buf, i < values.size() - 1 ? "%s;" : "%s", values[i].c_str()); + extruder_colours_str += buf; + } + } + binary_data.printer_metadata.raw_data.emplace_back("extruder_colour", extruder_colours_str); // duplicated into config data + } } // modifies m_silent_time_estimator_enabled @@ -1613,7 +1659,10 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato if (export_to_binary_gcode) { bgcode::binarize::BinaryData& binary_data = m_processor.get_binary_data(); if (print.m_print_statistics.total_toolchanges > 0) - binary_data.print_metadata.raw_data.push_back({ "total toolchanges", std::to_string(print.m_print_statistics.total_toolchanges) }); + binary_data.print_metadata.raw_data.emplace_back("total toolchanges", std::to_string(print.m_print_statistics.total_toolchanges)); + char buf[1024]; + sprintf(buf, "%.2lf", m_max_layer_z); + binary_data.printer_metadata.raw_data.emplace_back("max_layer_z", buf); } else { #else diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 7fa5d5382c..d0c92af8c7 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -3686,15 +3686,17 @@ void GCodeProcessor::post_process() // update binary data bgcode::binarize::BinaryData& binary_data = m_binarizer.get_binary_data(); - binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedMm, stringify(filament_mm) }); - binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedCm3, stringify(filament_cm3) }); - binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedG, stringify(filament_g) }); - binary_data.print_metadata.raw_data.push_back({ PrintStatistics::FilamentCost, stringify(filament_cost) }); - binary_data.print_metadata.raw_data.push_back({ PrintStatistics::TotalFilamentUsedG, stringify({ filament_total_g }) }); - binary_data.print_metadata.raw_data.push_back({ PrintStatistics::TotalFilamentCost, stringify({ filament_total_cost }) }); + binary_data.print_metadata.raw_data.emplace_back(PrintStatistics::FilamentUsedMm, stringify(filament_mm)); + binary_data.print_metadata.raw_data.emplace_back(PrintStatistics::FilamentUsedCm3, stringify(filament_cm3)); + binary_data.print_metadata.raw_data.emplace_back(PrintStatistics::FilamentUsedG, stringify(filament_g)); + binary_data.print_metadata.raw_data.emplace_back(PrintStatistics::FilamentCost, stringify(filament_cost)); + binary_data.print_metadata.raw_data.emplace_back(PrintStatistics::TotalFilamentUsedG, stringify({ filament_total_g })); + binary_data.print_metadata.raw_data.emplace_back(PrintStatistics::TotalFilamentCost, stringify({ filament_total_cost })); - binary_data.printer_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedMm, stringify(filament_mm) }); // duplicated into print metadata - binary_data.printer_metadata.raw_data.push_back({ PrintStatistics::FilamentUsedG, stringify(filament_g) }); // duplicated into print metadata + binary_data.printer_metadata.raw_data.emplace_back(PrintStatistics::FilamentUsedMm, stringify(filament_mm)); // duplicated into print metadata + binary_data.printer_metadata.raw_data.emplace_back(PrintStatistics::FilamentUsedG, stringify(filament_g)); // duplicated into print metadata + binary_data.printer_metadata.raw_data.emplace_back(PrintStatistics::FilamentCost, stringify(filament_cost)); // duplicated into print metadata + binary_data.printer_metadata.raw_data.emplace_back(PrintStatistics::FilamentUsedCm3, stringify(filament_cm3)); // duplicated into print metadata for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { const TimeMachine& machine = m_time_processor.machines[i]; @@ -3702,10 +3704,10 @@ void GCodeProcessor::post_process() if (mode == PrintEstimatedStatistics::ETimeMode::Normal || machine.enabled) { char buf[128]; sprintf(buf, "(%s mode)", (mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent"); - binary_data.print_metadata.raw_data.push_back({ "estimated printing time " + std::string(buf), get_time_dhms(machine.time) }); - binary_data.print_metadata.raw_data.push_back({ "estimated first layer printing time " + std::string(buf), get_time_dhms(machine.layers_time.empty() ? 0.f : machine.layers_time.front()) }); + binary_data.print_metadata.raw_data.emplace_back("estimated printing time " + std::string(buf), get_time_dhms(machine.time)); + binary_data.print_metadata.raw_data.emplace_back("estimated first layer printing time " + std::string(buf), get_time_dhms(machine.layers_time.empty() ? 0.f : machine.layers_time.front())); - binary_data.printer_metadata.raw_data.push_back({ "estimated printing time " + std::string(buf), get_time_dhms(machine.time) }); + binary_data.printer_metadata.raw_data.emplace_back("estimated printing time " + std::string(buf), get_time_dhms(machine.time)); } } From 0bea17c12870de8c865d6a99521fba7fe8c2e343 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 11 Aug 2023 09:20:27 +0200 Subject: [PATCH 064/136] Preview and GCodeViewer: removed file mapping for gcode window visualization + Fixed build on non Windows OSs --- src/libslic3r/Config.cpp | 2 - src/libslic3r/GCode/GCodeProcessor.cpp | 16 +- src/libslic3r/Technologies.hpp | 9 +- src/slic3r/GUI/GCodeViewer.cpp | 232 ++++++++++++++++++++++--- src/slic3r/GUI/GCodeViewer.hpp | 49 +++++- 5 files changed, 271 insertions(+), 37 deletions(-) diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index 5d2de1aaa5..d07690b73b 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -1075,8 +1075,6 @@ ConfigSubstitutions ConfigBase::load_from_binary_gcode_file(const std::string& f throw Slic3r::RuntimeError(format("The selected file is not a valid binary gcode.\nError: %1%", std::string(bgcode::core::translate_result(res)))); - fseek(file.f, 0, SEEK_END); - const long file_size = ftell(file.f); rewind(file.f); bgcode::core::FileHeader file_header; diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index d0c92af8c7..4953bdbdc4 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -25,10 +25,10 @@ #endif #include -#if ENABLE_BINARIZED_GCODE_DEBUG +#if ENABLE_BINARIZED_GCODE_WIN_DEBUG #include #include -#endif // ENABLE_BINARIZED_GCODE_DEBUG +#endif // ENABLE_BINARIZED_GCODE_WIN_DEBUG static const float DEFAULT_TOOLPATH_WIDTH = 0.4f; static const float DEFAULT_TOOLPATH_HEIGHT = 0.2f; @@ -1173,7 +1173,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct res = printer_metadata_block.read_data(*file.f, file_header, block_header); if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); -#if ENABLE_BINARIZED_GCODE_DEBUG +#if ENABLE_BINARIZED_GCODE_WIN_DEBUG OutputDebugStringA("Printer metadata:\n"); for (const auto& [key, value] : printer_metadata_block.raw_data) { OutputDebugStringA(key.c_str()); @@ -1181,7 +1181,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct OutputDebugStringA(value.c_str()); OutputDebugStringA("\n"); } -#endif // ENABLE_BINARIZED_GCODE_DEBUG +#endif // ENABLE_BINARIZED_GCODE_WIN_DEBUG // read thumbnail blocks res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, verify_checksum); @@ -1229,7 +1229,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct res = print_metadata_block.read_data(*file.f, file_header, block_header); if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); -#if ENABLE_BINARIZED_GCODE_DEBUG +#if ENABLE_BINARIZED_GCODE_WIN_DEBUG OutputDebugStringA("Print metadata:\n"); for (const auto& [key, value] : print_metadata_block.raw_data) { OutputDebugStringA(key.c_str()); @@ -1237,7 +1237,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct OutputDebugStringA(value.c_str()); OutputDebugStringA("\n"); } -#endif // ENABLE_BINARIZED_GCODE_DEBUG +#endif // ENABLE_BINARIZED_GCODE_WIN_DEBUG // read slicer metadata block res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, verify_checksum); @@ -1249,7 +1249,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct res = slicer_metadata_block.read_data(*file.f, file_header, block_header); if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); -#if ENABLE_BINARIZED_GCODE_DEBUG +#if ENABLE_BINARIZED_GCODE_WIN_DEBUG OutputDebugStringA("Slicer metadata:\n"); for (const auto& [key, value] : slicer_metadata_block.raw_data) { OutputDebugStringA(key.c_str()); @@ -1257,7 +1257,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct OutputDebugStringA(value.c_str()); OutputDebugStringA("\n"); } -#endif // ENABLE_BINARIZED_GCODE_DEBUG +#endif // ENABLE_BINARIZED_GCODE_WIN_DEBUG DynamicPrintConfig config; config.apply(FullPrintConfig::defaults()); std::string str; diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index db25b587f7..d7670f5c28 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -63,13 +63,16 @@ //==================== -// 2.6.1.alpha1 techs +// 2.6.2.alpha1 techs //==================== -#define ENABLE_2_6_1_ALPHA1 1 +#define ENABLE_2_6_2_ALPHA1 1 // Enable export of binarized gcode -#define ENABLE_BINARIZED_GCODE (1 && ENABLE_2_6_1_ALPHA1) +#define ENABLE_BINARIZED_GCODE (1 && ENABLE_2_6_2_ALPHA1) #define ENABLE_BINARIZED_GCODE_DEBUG (1 && ENABLE_BINARIZED_GCODE) +#ifdef _WIN32 +#define ENABLE_BINARIZED_GCODE_WIN_DEBUG (1 && ENABLE_BINARIZED_GCODE_DEBUG) +#endif // _WIN32 #define ENABLE_BINARIZED_GCODE_DEBUG_WINDOW (1 && ENABLE_BINARIZED_GCODE) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index fd9a44db3d..bfa6c87946 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -38,6 +38,11 @@ #include #include +#if ENABLE_BINARIZED_GCODE_WIN_DEBUG +#include +#include +#endif // ENABLE_BINARIZED_GCODE_WIN_DEBUG + namespace Slic3r { namespace GUI { @@ -364,13 +369,16 @@ void GCodeViewer::SequentialView::Marker::render() void GCodeViewer::SequentialView::GCodeWindow::load_gcode(const std::string& filename, const std::vector& lines_ends) { +#if !ENABLE_BINARIZED_GCODE assert(! m_file.is_open()); if (m_file.is_open()) return; +#endif // !ENABLE_BINARIZED_GCODE m_filename = filename; m_lines_ends = lines_ends; +#if !ENABLE_BINARIZED_GCODE m_selected_line_id = 0; m_last_lines_size = 0; @@ -383,42 +391,217 @@ void GCodeViewer::SequentialView::GCodeWindow::load_gcode(const std::string& fil BOOST_LOG_TRIVIAL(error) << "Unable to map file " << m_filename << ". Cannot show G-code window."; reset(); } +#endif // !ENABLE_BINARIZED_GCODE } +#if ENABLE_BINARIZED_GCODE +void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, size_t curr_line_id) +{ + auto update_lines = [this]() { + m_lines_cache.clear(); + m_lines_cache.reserve(m_cache_range.size()); + FILE* file = boost::nowide::fopen(m_filename.c_str(), "rb"); + if (file != nullptr) { + for (size_t id = *m_cache_range.start_id; id <= *m_cache_range.end_id; ++id) { + assert(id > 0); + // read line from file + const size_t start = id == 1 ? 0 : m_lines_ends[id - 2]; + const size_t len = m_lines_ends[id - 1] - start; + std::string gline(len, '\0'); + fseek(file, start, SEEK_SET); + const size_t rsize = fread((void*)gline.data(), 1, len, file); + if (ferror(file) || rsize != len) { + m_lines_cache.clear(); + break; + } + + std::string command; + std::string parameters; + std::string comment; + + // extract comment + std::vector tokens; + boost::split(tokens, gline, boost::is_any_of(";"), boost::token_compress_on); + command = tokens.front(); + if (tokens.size() > 1) + comment = ";" + tokens.back(); + + // extract gcode command and parameters + if (!command.empty()) { + boost::split(tokens, command, boost::is_any_of(" "), boost::token_compress_on); + command = tokens.front(); + if (tokens.size() > 1) { + for (size_t i = 1; i < tokens.size(); ++i) { + parameters += " " + tokens[i]; + } + } + } + m_lines_cache.push_back({ command, parameters, comment }); + } + fclose(file); + } + }; + + static const ImVec4 LINE_NUMBER_COLOR = ImGuiWrapper::COL_ORANGE_LIGHT; + static const ImVec4 SELECTION_RECT_COLOR = ImGuiWrapper::COL_ORANGE_DARK; + static const ImVec4 COMMAND_COLOR = { 0.8f, 0.8f, 0.0f, 1.0f }; + static const ImVec4 PARAMETERS_COLOR = { 1.0f, 1.0f, 1.0f, 1.0f }; + static const ImVec4 COMMENT_COLOR = { 0.7f, 0.7f, 0.7f, 1.0f }; + static const ImVec4 ELLIPSIS_COLOR = { 0.0f, 0.7f, 0.0f, 1.0f }; + + if (!m_visible || m_filename.empty() || m_lines_ends.empty() || curr_line_id == 0) + return; + + // window height + const float wnd_height = bottom - top; + + // number of visible lines + const float text_height = ImGui::CalcTextSize("0").y; + const ImGuiStyle& style = ImGui::GetStyle(); + const size_t visible_lines_count = static_cast((wnd_height - 2.0f * style.WindowPadding.y + style.ItemSpacing.y) / (text_height + style.ItemSpacing.y)); + + if (visible_lines_count == 0) + return; + + auto resize_range = [&](Range& range, size_t lines_count) { + const size_t half_lines_count = lines_count / 2; + range.start_id = (curr_line_id >= half_lines_count) ? curr_line_id - half_lines_count : 1; + range.end_id = *range.start_id + lines_count - 1; + if (*range.end_id >= m_lines_ends.size()) { + range.end_id = m_lines_ends.size() - 1; + range.start_id = *range.end_id - lines_count + 1; + } + }; + + // visible range + Range visible_range; + resize_range(visible_range, visible_lines_count); + + // update cache if needed + if (m_cache_range.empty() || !m_cache_range.contains(visible_range)) { + resize_range(m_cache_range, 4 * visible_range.size()); + update_lines(); + } + + // line number's column width + const float id_width = ImGui::CalcTextSize(std::to_string(*visible_range.end_id).c_str()).x; + + ImGuiWrapper& imgui = *wxGetApp().imgui(); + + auto add_item_to_line = [&imgui](const std::string& txt, const ImVec4& color, float spacing, size_t& current_length) { + static const size_t LENGTH_THRESHOLD = 60; + + if (txt.empty()) + return false; + + std::string out_text = txt; + bool reduced = false; + if (current_length + out_text.length() > LENGTH_THRESHOLD) { + out_text = out_text.substr(0, LENGTH_THRESHOLD - current_length); + reduced = true; + } + + current_length += out_text.length(); + + ImGui::SameLine(0.0f, spacing); + imgui.text_colored(color, out_text); + if (reduced) { + ImGui::SameLine(0.0f, 0.0f); + imgui.text_colored(ELLIPSIS_COLOR, "..."); + } + + return reduced; + }; + + imgui.set_next_window_pos(0.0f, top, ImGuiCond_Always, 0.0f, 0.0f); + imgui.set_next_window_size(0.0f, wnd_height, ImGuiCond_Always); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + ImGui::SetNextWindowBgAlpha(0.6f); + imgui.begin(std::string("G-code"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); + + // center the text in the window by pushing down the first line + const float f_lines_count = static_cast(visible_lines_count); + ImGui::SetCursorPosY(0.5f * (wnd_height - f_lines_count * text_height - (f_lines_count - 1.0f) * style.ItemSpacing.y)); + + // render text lines + size_t max_line_length = 0; + for (size_t id = *visible_range.start_id; id <= *visible_range.end_id; ++id) { + const Line& line = m_lines_cache[id - *m_cache_range.start_id]; + + // rect around the current selected line + if (id == curr_line_id) { + const float pos_y = ImGui::GetCursorScreenPos().y; + const float half_ItemSpacing_y = 0.5f * style.ItemSpacing.y; + const float half_padding_x = 0.5f * style.WindowPadding.x; + ImGui::GetWindowDrawList()->AddRect({ half_padding_x, pos_y - half_ItemSpacing_y }, + { ImGui::GetCurrentWindow()->Size.x - half_padding_x, pos_y + text_height + half_ItemSpacing_y }, + ImGui::GetColorU32(SELECTION_RECT_COLOR)); + } + + const std::string id_str = std::to_string(id); + // spacer to right align text + ImGui::Dummy({ id_width - ImGui::CalcTextSize(id_str.c_str()).x, text_height }); + + size_t line_length = 0; + // render line number + bool stop_adding = add_item_to_line(id_str, LINE_NUMBER_COLOR, 0.0f, line_length); + if (!stop_adding && !line.command.empty()) + // render command + stop_adding = add_item_to_line(line.command, COMMAND_COLOR, -1.0f, line_length); + if (!stop_adding && !line.parameters.empty()) + // render parameters + stop_adding = add_item_to_line(line.parameters, PARAMETERS_COLOR, 0.0f, line_length); + if (!stop_adding && !line.comment.empty()) + // render comment + stop_adding = add_item_to_line(line.comment, COMMENT_COLOR, line.command.empty() ? -1.0f : 0.0f, line_length); + + max_line_length = std::max(max_line_length, line_length); + } + + imgui.end(); + ImGui::PopStyleVar(); + + // request an extra frame if window's width changed + if (m_max_line_length != max_line_length) { + m_max_line_length = max_line_length; + imgui.set_requires_extra_frame(); + } +} +#else void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, uint64_t curr_line_id) const { auto update_lines = [this](uint64_t start_id, uint64_t end_id) { std::vector ret; ret.reserve(end_id - start_id + 1); - for (uint64_t id = start_id; id <= end_id; ++id) { - // read line from file - const size_t start = id == 1 ? 0 : m_lines_ends[id - 2]; - const size_t len = m_lines_ends[id - 1] - start; - std::string gline(m_file.data() + start, len); + for (uint64_t id = start_id; id <= end_id; ++id) { + // read line from file + const size_t start = id == 1 ? 0 : m_lines_ends[id - 2]; + const size_t len = m_lines_ends[id - 1] - start; + std::string gline(m_file.data() + start, len); - std::string command; - std::string parameters; - std::string comment; + std::string command; + std::string parameters; + std::string comment; - // extract comment - std::vector tokens; - boost::split(tokens, gline, boost::is_any_of(";"), boost::token_compress_on); - command = tokens.front(); - if (tokens.size() > 1) - comment = ";" + tokens.back(); - - // extract gcode command and parameters - if (!command.empty()) { - boost::split(tokens, command, boost::is_any_of(" "), boost::token_compress_on); + // extract comment + std::vector tokens; + boost::split(tokens, gline, boost::is_any_of(";"), boost::token_compress_on); command = tokens.front(); - if (tokens.size() > 1) { - for (size_t i = 1; i < tokens.size(); ++i) { - parameters += " " + tokens[i]; + if (tokens.size() > 1) + comment = ";" + tokens.back(); + + // extract gcode command and parameters + if (!command.empty()) { + boost::split(tokens, command, boost::is_any_of(" "), boost::token_compress_on); + command = tokens.front(); + if (tokens.size() > 1) { + for (size_t i = 1; i < tokens.size(); ++i) { + parameters += " " + tokens[i]; + } } } + ret.push_back({ command, parameters, comment }); } - ret.push_back({ command, parameters, comment }); - } return ret; }; @@ -546,12 +729,15 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, u imgui.end(); ImGui::PopStyleVar(); } +#endif // ENABLE_BINARIZED_GCODE +#if !ENABLE_BINARIZED_GCODE void GCodeViewer::SequentialView::GCodeWindow::stop_mapping_file() { if (m_file.is_open()) m_file.close(); } +#endif // !ENABLE_BINARIZED_GCODE void GCodeViewer::SequentialView::render(float legend_height) { diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 32d0a91e34..583619e623 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -6,7 +6,9 @@ #include "libslic3r/GCode/GCodeProcessor.hpp" #include "GLModel.hpp" +#if !ENABLE_BINARIZED_GCODE #include +#endif // !ENABLE_BINARIZED_GCODE #include #include @@ -672,6 +674,50 @@ public: void render(); }; +#if ENABLE_BINARIZED_GCODE + class GCodeWindow + { + struct Line + { + std::string command; + std::string parameters; + std::string comment; + }; + + struct Range + { + std::optional start_id; + std::optional end_id; + bool empty() const { + return !start_id.has_value() || !end_id.has_value(); + } + bool contains(const Range& other) const { + return !this->empty() && !other.empty() && *this->start_id <= *other.start_id && *this->end_id >= other.end_id; + } + size_t size() const { + return empty() ? 0 : *this->end_id - *this->start_id + 1; + } + }; + + bool m_visible{ true }; + std::string m_filename; + // map for accessing data in file by line number + std::vector m_lines_ends; + std::vector m_lines_cache; + Range m_cache_range; + size_t m_max_line_length{ 0 }; + + public: + void load_gcode(const std::string& filename, const std::vector& lines_ends); + void reset() { + m_lines_ends.clear(); + m_lines_cache.clear(); + m_filename.clear(); + } + void toggle_visibility() { m_visible = !m_visible; } + void render(float top, float bottom, size_t curr_line_id); + }; +#else class GCodeWindow { struct Line @@ -691,7 +737,7 @@ public: std::vector m_lines; public: - GCodeWindow() = default; + GCodeWindow() = default; ~GCodeWindow() { stop_mapping_file(); } void load_gcode(const std::string& filename, const std::vector& lines_ends); void reset() { @@ -707,6 +753,7 @@ public: void stop_mapping_file(); }; +#endif // ENABLE_BINARIZED_GCODE struct Endpoints { From 8e391d00da90d4943b891111e92b48f69d265622 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 14 Aug 2023 09:38:22 +0200 Subject: [PATCH 065/136] Changes required by update of libbgcode library interface --- src/libslic3r/Config.cpp | 8 +++++--- src/libslic3r/GCode/GCodeProcessor.cpp | 22 ++++++++++------------ src/libslic3r/PresetBundle.cpp | 3 ++- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index d07690b73b..31bd02ad10 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -741,7 +741,8 @@ ConfigSubstitutions ConfigBase::load(const std::string& filename, ForwardCompati if (file == nullptr) throw Slic3r::RuntimeError("Error opening the file: " + filename + "\n"); - file_type = (bgcode::core::is_valid_binary_gcode(*file, true) == bgcode::core::EResult::Success) ? EFileType::BinaryGCode : EFileType::AsciiGCode; + std::vector cs_buffer(65536); + file_type = (bgcode::core::is_valid_binary_gcode(*file, true, cs_buffer.data(), cs_buffer.size()) == bgcode::core::EResult::Success) ? EFileType::BinaryGCode : EFileType::AsciiGCode; fclose(file); } else @@ -1070,7 +1071,8 @@ ConfigSubstitutions ConfigBase::load_from_binary_gcode_file(const std::string& f if (file.f == nullptr) throw Slic3r::RuntimeError(format("Error opening the file: %1%", filename)); - bgcode::core::EResult res = bgcode::core::is_valid_binary_gcode(*file.f); + std::vector cs_buffer(65536); + bgcode::core::EResult res = bgcode::core::is_valid_binary_gcode(*file.f, true, cs_buffer.data(), cs_buffer.size()); if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError(format("The selected file is not a valid binary gcode.\nError: %1%", std::string(bgcode::core::translate_result(res)))); @@ -1083,7 +1085,7 @@ ConfigSubstitutions ConfigBase::load_from_binary_gcode_file(const std::string& f throw Slic3r::RuntimeError(format("Error while reading file '%1%': %2%", filename, std::string(bgcode::core::translate_result(res)))); bgcode::core::BlockHeader block_header; - res = read_next_block_header(*file.f, file_header, block_header, bgcode::core::EBlockType::SlicerMetadata, true); + res = read_next_block_header(*file.f, file_header, block_header, bgcode::core::EBlockType::SlicerMetadata, cs_buffer.data(), cs_buffer.size()); if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError(format("Error while reading file '%1%': %2%", filename, std::string(bgcode::core::translate_result(res)))); if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::SlicerMetadata) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 4953bdbdc4..f16777d384 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -1041,7 +1041,8 @@ void GCodeProcessor::process_file(const std::string& filename, std::function cs_buffer(65536); + const bool is_binary = bgcode::core::is_valid_binary_gcode(*file, true, cs_buffer.data(), cs_buffer.size()) == bgcode::core::EResult::Success; fclose(file); if (is_binary) @@ -1134,8 +1135,6 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct const long file_size = ftell(file.f); rewind(file.f); - bgcode::core::set_checksum_max_cache_size(1024); - // read file header bgcode::core::FileHeader file_header; bgcode::core::EResult res = bgcode::core::read_header(*file.f, file_header, nullptr); @@ -1143,11 +1142,10 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct throw Slic3r::RuntimeError("File: " + filename + "does not contain a valid binary gcode\n Error: " + std::string(bgcode::core::translate_result(res)) + "\n"); - const bool verify_checksum = true; - // read file metadata block bgcode::core::BlockHeader block_header; - res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, verify_checksum); + std::vector cs_buffer(65536); + res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::FileMetadata) @@ -1164,7 +1162,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct m_producer = EProducer::Unknown; // read printer metadata block - res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, verify_checksum); + res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::PrinterMetadata) @@ -1184,7 +1182,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct #endif // ENABLE_BINARIZED_GCODE_WIN_DEBUG // read thumbnail blocks - res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, verify_checksum); + res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); @@ -1217,7 +1215,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct } #endif // ENABLE_BINARIZED_GCODE_DEBUG - res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, verify_checksum); + res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); } @@ -1240,7 +1238,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct #endif // ENABLE_BINARIZED_GCODE_WIN_DEBUG // read slicer metadata block - res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, verify_checksum); + res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::SlicerMetadata) @@ -1275,7 +1273,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct initialize_result_moves(); // read gcodes block - res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, verify_checksum); + res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::GCode) @@ -1295,7 +1293,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct if (ftell(file.f) == file_size) break; - res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, verify_checksum); + res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); } diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index 45e4d0535f..34cae91a7e 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -883,7 +883,8 @@ ConfigSubstitutions PresetBundle::load_config_file(const std::string &path, Forw FILE* file = boost::nowide::fopen(path.c_str(), "rb"); if (file == nullptr) throw Slic3r::RuntimeError("Error opening the file: " + path + "\n"); - const bool is_binary = bgcode::core::is_valid_binary_gcode(*file, true) == bgcode::core::EResult::Success; + std::vector cs_buffer(65536); + const bool is_binary = bgcode::core::is_valid_binary_gcode(*file, true, cs_buffer.data(), cs_buffer.size()) == bgcode::core::EResult::Success; fclose(file); DynamicPrintConfig config; From 98be146fa9947067e63e7f272faf096b6dc57eb3 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 14 Aug 2023 09:55:00 +0200 Subject: [PATCH 066/136] Some code semplification --- src/libslic3r/Config.cpp | 35 +++---- src/libslic3r/GCode/GCodeProcessor.cpp | 128 +++++++++++++------------ src/libslic3r/GCode/Thumbnails.hpp | 10 +- src/slic3r/GUI/GLCanvas3D.cpp | 17 ++-- src/slic3r/GUI/Plater.cpp | 14 +-- 5 files changed, 107 insertions(+), 97 deletions(-) diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index 31bd02ad10..629f39ef43 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -741,8 +741,9 @@ ConfigSubstitutions ConfigBase::load(const std::string& filename, ForwardCompati if (file == nullptr) throw Slic3r::RuntimeError("Error opening the file: " + filename + "\n"); + using namespace bgcode::core; std::vector cs_buffer(65536); - file_type = (bgcode::core::is_valid_binary_gcode(*file, true, cs_buffer.data(), cs_buffer.size()) == bgcode::core::EResult::Success) ? EFileType::BinaryGCode : EFileType::AsciiGCode; + file_type = (is_valid_binary_gcode(*file, true, cs_buffer.data(), cs_buffer.size()) == EResult::Success) ? EFileType::BinaryGCode : EFileType::AsciiGCode; fclose(file); } else @@ -1071,29 +1072,31 @@ ConfigSubstitutions ConfigBase::load_from_binary_gcode_file(const std::string& f if (file.f == nullptr) throw Slic3r::RuntimeError(format("Error opening the file: %1%", filename)); + using namespace bgcode::core; + using namespace bgcode::binarize; std::vector cs_buffer(65536); - bgcode::core::EResult res = bgcode::core::is_valid_binary_gcode(*file.f, true, cs_buffer.data(), cs_buffer.size()); - if (res != bgcode::core::EResult::Success) + EResult res = is_valid_binary_gcode(*file.f, true, cs_buffer.data(), cs_buffer.size()); + if (res != EResult::Success) throw Slic3r::RuntimeError(format("The selected file is not a valid binary gcode.\nError: %1%", - std::string(bgcode::core::translate_result(res)))); + std::string(translate_result(res)))); rewind(file.f); - bgcode::core::FileHeader file_header; - res = bgcode::core::read_header(*file.f, file_header, nullptr); - if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError(format("Error while reading file '%1%': %2%", filename, std::string(bgcode::core::translate_result(res)))); + FileHeader file_header; + res = read_header(*file.f, file_header, nullptr); + if (res != EResult::Success) + throw Slic3r::RuntimeError(format("Error while reading file '%1%': %2%", filename, std::string(translate_result(res)))); - bgcode::core::BlockHeader block_header; - res = read_next_block_header(*file.f, file_header, block_header, bgcode::core::EBlockType::SlicerMetadata, cs_buffer.data(), cs_buffer.size()); - if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError(format("Error while reading file '%1%': %2%", filename, std::string(bgcode::core::translate_result(res)))); - if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::SlicerMetadata) + BlockHeader block_header; + res = read_next_block_header(*file.f, file_header, block_header, EBlockType::SlicerMetadata, cs_buffer.data(), cs_buffer.size()); + if (res != EResult::Success) + throw Slic3r::RuntimeError(format("Error while reading file '%1%': %2%", filename, std::string(translate_result(res)))); + if ((EBlockType)block_header.type != EBlockType::SlicerMetadata) throw Slic3r::RuntimeError(format("Unable to find slicer metadata block in file: '%1%'", filename)); - bgcode::binarize::SlicerMetadataBlock slicer_metadata_block; + SlicerMetadataBlock slicer_metadata_block; res = slicer_metadata_block.read_data(*file.f, file_header, block_header); - if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError(format("Error while reading file '%1%': %2%", filename, std::string(bgcode::core::translate_result(res)))); + if (res != EResult::Success) + throw Slic3r::RuntimeError(format("Error while reading file '%1%': %2%", filename, std::string(translate_result(res)))); for (const auto& [key, value] : slicer_metadata_block.raw_data) { this->set_deserialize(key, value, substitutions_ctxt); diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index f16777d384..1e2596e15a 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -1041,8 +1041,9 @@ void GCodeProcessor::process_file(const std::string& filename, std::function cs_buffer(65536); - const bool is_binary = bgcode::core::is_valid_binary_gcode(*file, true, cs_buffer.data(), cs_buffer.size()) == bgcode::core::EResult::Success; + const bool is_binary = is_valid_binary_gcode(*file, true, cs_buffer.data(), cs_buffer.size()) == EResult::Success; fclose(file); if (is_binary) @@ -1136,24 +1137,26 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct rewind(file.f); // read file header - bgcode::core::FileHeader file_header; - bgcode::core::EResult res = bgcode::core::read_header(*file.f, file_header, nullptr); - if (res != bgcode::core::EResult::Success) + using namespace bgcode::core; + using namespace bgcode::binarize; + FileHeader file_header; + EResult res = read_header(*file.f, file_header, nullptr); + if (res != EResult::Success) throw Slic3r::RuntimeError("File: " + filename + "does not contain a valid binary gcode\n Error: " + - std::string(bgcode::core::translate_result(res)) + "\n"); + std::string(translate_result(res)) + "\n"); // read file metadata block - bgcode::core::BlockHeader block_header; + BlockHeader block_header; std::vector cs_buffer(65536); - res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); - if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); - if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::FileMetadata) + res = read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); + if (res != EResult::Success) + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); + if ((EBlockType)block_header.type != EBlockType::FileMetadata) throw Slic3r::RuntimeError("Unable to find file metadata block in file: " + filename + "\n"); - bgcode::binarize::FileMetadataBlock file_metadata_block; + FileMetadataBlock file_metadata_block; res = file_metadata_block.read_data(*file.f, file_header, block_header); - if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); + if (res != EResult::Success) + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); auto producer_it = std::find_if(file_metadata_block.raw_data.begin(), file_metadata_block.raw_data.end(), [](const std::pair& item) { return item.first == "Producer"; }); if (producer_it != file_metadata_block.raw_data.end() && boost::starts_with(producer_it->second, std::string(SLIC3R_APP_NAME))) @@ -1162,15 +1165,15 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct m_producer = EProducer::Unknown; // read printer metadata block - res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); - if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); - if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::PrinterMetadata) + res = read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); + if (res != EResult::Success) + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); + if ((EBlockType)block_header.type != EBlockType::PrinterMetadata) throw Slic3r::RuntimeError("Unable to find printer metadata block in file: " + filename + "\n"); - bgcode::binarize::PrinterMetadataBlock printer_metadata_block; + PrinterMetadataBlock printer_metadata_block; res = printer_metadata_block.read_data(*file.f, file_header, block_header); - if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); + if (res != EResult::Success) + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); #if ENABLE_BINARIZED_GCODE_WIN_DEBUG OutputDebugStringA("Printer metadata:\n"); for (const auto& [key, value] : printer_metadata_block.raw_data) { @@ -1182,24 +1185,24 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct #endif // ENABLE_BINARIZED_GCODE_WIN_DEBUG // read thumbnail blocks - res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); - if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); + res = read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); + if (res != EResult::Success) + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); - while ((bgcode::core::EBlockType)block_header.type == bgcode::core::EBlockType::Thumbnail) { - bgcode::binarize::ThumbnailBlock thumbnail_block; + while ((EBlockType)block_header.type == EBlockType::Thumbnail) { + ThumbnailBlock thumbnail_block; res = thumbnail_block.read_data(*file.f, file_header, block_header); - if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); + if (res != EResult::Success) + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); #if ENABLE_BINARIZED_GCODE_DEBUG if (thumbnail_block.data.size() > 0) { - auto format_filename = [](const std::string& stem, const bgcode::binarize::ThumbnailBlock& block) { + auto format_filename = [](const std::string& stem, const ThumbnailBlock& block) { std::string ret = stem + "_" + std::to_string(block.params.width) + "x" + std::to_string(block.params.height); - switch ((bgcode::core::EThumbnailFormat)block.params.format) + switch ((EThumbnailFormat)block.params.format) { - case bgcode::core::EThumbnailFormat::PNG: { ret += ".png"; break; } - case bgcode::core::EThumbnailFormat::JPG: { ret += ".jpg"; break; } - case bgcode::core::EThumbnailFormat::QOI: { ret += ".qoi"; break; } + case EThumbnailFormat::PNG: { ret += ".png"; break; } + case EThumbnailFormat::JPG: { ret += ".jpg"; break; } + case EThumbnailFormat::QOI: { ret += ".qoi"; break; } } return ret; }; @@ -1215,18 +1218,18 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct } #endif // ENABLE_BINARIZED_GCODE_DEBUG - res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); - if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); + res = read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); + if (res != EResult::Success) + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); } // read print metadata block - if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::PrintMetadata) + if ((EBlockType)block_header.type != EBlockType::PrintMetadata) throw Slic3r::RuntimeError("Unable to find print metadata block in file: " + filename + "\n"); - bgcode::binarize::PrintMetadataBlock print_metadata_block; + PrintMetadataBlock print_metadata_block; res = print_metadata_block.read_data(*file.f, file_header, block_header); - if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); + if (res != EResult::Success) + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); #if ENABLE_BINARIZED_GCODE_WIN_DEBUG OutputDebugStringA("Print metadata:\n"); for (const auto& [key, value] : print_metadata_block.raw_data) { @@ -1238,15 +1241,15 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct #endif // ENABLE_BINARIZED_GCODE_WIN_DEBUG // read slicer metadata block - res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); - if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); - if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::SlicerMetadata) + res = read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); + if (res != EResult::Success) + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); + if ((EBlockType)block_header.type != EBlockType::SlicerMetadata) throw Slic3r::RuntimeError("Unable to find slicer metadata block in file: " + filename + "\n"); - bgcode::binarize::SlicerMetadataBlock slicer_metadata_block; + SlicerMetadataBlock slicer_metadata_block; res = slicer_metadata_block.read_data(*file.f, file_header, block_header); - if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); + if (res != EResult::Success) + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); #if ENABLE_BINARIZED_GCODE_WIN_DEBUG OutputDebugStringA("Slicer metadata:\n"); for (const auto& [key, value] : slicer_metadata_block.raw_data) { @@ -1273,16 +1276,16 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct initialize_result_moves(); // read gcodes block - res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); - if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); - if ((bgcode::core::EBlockType)block_header.type != bgcode::core::EBlockType::GCode) + res = read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); + if (res != EResult::Success) + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); + if ((EBlockType)block_header.type != EBlockType::GCode) throw Slic3r::RuntimeError("Unable to find gcode block in file: " + filename + "\n"); - while ((bgcode::core::EBlockType)block_header.type == bgcode::core::EBlockType::GCode) { - bgcode::binarize::GCodeBlock block; + while ((EBlockType)block_header.type == EBlockType::GCode) { + GCodeBlock block; res = block.read_data(*file.f, file_header, block_header); - if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); + if (res != EResult::Success) + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); // TODO: Update m_result.lines_ends @@ -1293,9 +1296,9 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct if (ftell(file.f) == file_size) break; - res = bgcode::core::read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); - if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(bgcode::core::translate_result(res)) + "\n"); + res = read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); + if (res != EResult::Success) + throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); } // Don't post-process the G-code to update time stamps. @@ -3709,8 +3712,9 @@ void GCodeProcessor::post_process() } } - const bgcode::core::EResult res = m_binarizer.initialize(*out.f, s_binarizer_config); - if (res != bgcode::core::EResult::Success) + using namespace bgcode::core; + const EResult res = m_binarizer.initialize(*out.f, s_binarizer_config); + if (res != EResult::Success) throw Slic3r::RuntimeError(std::string("Unable to initialize the gcode binarizer.\n")); } #endif // ENABLE_BINARIZED_GCODE @@ -4000,8 +4004,7 @@ void GCodeProcessor::post_process() #if ENABLE_BINARIZED_GCODE if (m_binarizer.is_enabled()) { - bgcode::core::EResult res = m_binarizer.append_gcode(out_string); - if (res != bgcode::core::EResult::Success) + if (m_binarizer.append_gcode(out_string) != bgcode::core::EResult::Success) throw Slic3r::RuntimeError(std::string("Error while sending gcode to the binarizer.\n")); } else @@ -4380,8 +4383,7 @@ void GCodeProcessor::post_process() #if ENABLE_BINARIZED_GCODE if (m_binarizer.is_enabled()) { - const bgcode::core::EResult res = m_binarizer.finalize(); - if (res != bgcode::core::EResult::Success) + if (m_binarizer.finalize() != bgcode::core::EResult::Success) throw Slic3r::RuntimeError(std::string("Error while finalizing the gcode binarizer.\n")); } #endif // ENABLE_BINARIZED_GCODE diff --git a/src/libslic3r/GCode/Thumbnails.hpp b/src/libslic3r/GCode/Thumbnails.hpp index c430d695c2..820ee4a19c 100644 --- a/src/libslic3r/GCode/Thumbnails.hpp +++ b/src/libslic3r/GCode/Thumbnails.hpp @@ -64,6 +64,8 @@ template inline void generate_binary_thumbnails(ThumbnailsGeneratorCallback& thumbnail_cb, std::vector& out_thumbnails, const std::vector& sizes, GCodeThumbnailsFormat format, ThrowIfCanceledCallback throw_if_canceled) { + using namespace bgcode::core; + using namespace bgcode::binarize; out_thumbnails.clear(); if (thumbnail_cb != nullptr) { ThumbnailsList thumbnails = thumbnail_cb(ThumbnailsParams{ sizes, true, true, true, true }); @@ -71,13 +73,13 @@ inline void generate_binary_thumbnails(ThumbnailsGeneratorCallback& thumbnail_cb if (data.is_valid()) { auto compressed = compress_thumbnail(data, format); if (compressed->data != nullptr && compressed->size > 0) { - bgcode::binarize::ThumbnailBlock& block = out_thumbnails.emplace_back(bgcode::binarize::ThumbnailBlock()); + ThumbnailBlock& block = out_thumbnails.emplace_back(ThumbnailBlock()); block.params.width = (uint16_t)data.width; block.params.height = (uint16_t)data.height; switch (format) { - case GCodeThumbnailsFormat::PNG: { block.params.format = (uint16_t)bgcode::core::EThumbnailFormat::PNG; break; } - case GCodeThumbnailsFormat::JPG: { block.params.format = (uint16_t)bgcode::core::EThumbnailFormat::JPG; break; } - case GCodeThumbnailsFormat::QOI: { block.params.format = (uint16_t)bgcode::core::EThumbnailFormat::QOI; break; } + case GCodeThumbnailsFormat::PNG: { block.params.format = (uint16_t)EThumbnailFormat::PNG; break; } + case GCodeThumbnailsFormat::JPG: { block.params.format = (uint16_t)EThumbnailFormat::JPG; break; } + case GCodeThumbnailsFormat::QOI: { block.params.format = (uint16_t)EThumbnailFormat::QOI; break; } } block.data.resize(compressed->size); memcpy(block.data.data(), compressed->data, compressed->size); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index c4a8d093df..2687ddcf4c 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -7879,6 +7879,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() ImGuiWrapper& imgui = *wxGetApp().imgui(); imgui.begin(std::string("Binary GCode"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + using namespace bgcode::core; if (ImGui::BeginTable("BinaryGCodeConfig", 2)) { ImGui::TableNextRow(); @@ -7888,7 +7889,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() std::vector options = { "None", "Deflate", "heatshrink 11,4", "heatshrink 12,4" }; int option_id = (int)binarizer_config.compression.file_metadata; if (imgui.combo(std::string("##file_metadata_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.compression.file_metadata = (bgcode::core::ECompressionType)option_id; + binarizer_config.compression.file_metadata = (ECompressionType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -7896,7 +7897,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() ImGui::TableSetColumnIndex(1); option_id = (int)binarizer_config.compression.printer_metadata; if (imgui.combo(std::string("##printer_metadata_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.compression.printer_metadata = (bgcode::core::ECompressionType)option_id; + binarizer_config.compression.printer_metadata = (ECompressionType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -7904,7 +7905,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() ImGui::TableSetColumnIndex(1); option_id = (int)binarizer_config.compression.print_metadata; if (imgui.combo(std::string("##print_metadata_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.compression.print_metadata = (bgcode::core::ECompressionType)option_id; + binarizer_config.compression.print_metadata = (ECompressionType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -7912,7 +7913,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() ImGui::TableSetColumnIndex(1); option_id = (int)binarizer_config.compression.slicer_metadata; if (imgui.combo(std::string("##slicer_metadata_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.compression.slicer_metadata = (bgcode::core::ECompressionType)option_id; + binarizer_config.compression.slicer_metadata = (ECompressionType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -7920,7 +7921,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() ImGui::TableSetColumnIndex(1); option_id = (int)binarizer_config.compression.gcode; if (imgui.combo(std::string("##gcode_compression"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.compression.gcode = (bgcode::core::ECompressionType)option_id; + binarizer_config.compression.gcode = (ECompressionType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -7929,7 +7930,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() options = { "None", "MeatPack", "MeatPack Comments" }; option_id = (int)binarizer_config.gcode_encoding; if (imgui.combo(std::string("##gcode_encoding"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.gcode_encoding = (bgcode::core::EGCodeEncodingType)option_id; + binarizer_config.gcode_encoding = (EGCodeEncodingType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -7938,7 +7939,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() options = { "INI" }; option_id = (int)binarizer_config.metadata_encoding; if (imgui.combo(std::string("##metadata_encoding"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.metadata_encoding = (bgcode::core::EMetadataEncodingType)option_id; + binarizer_config.metadata_encoding = (EMetadataEncodingType)option_id; ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); @@ -7947,7 +7948,7 @@ void GLCanvas3D::show_binary_gcode_debug_window() options = { "None", "CRC32" }; option_id = (int)binarizer_config.checksum; if (imgui.combo(std::string("##4"), options, option_id, ImGuiComboFlags_HeightLargest, 0.0f, 175.0f)) - binarizer_config.checksum = (bgcode::core::EChecksumType)option_id; + binarizer_config.checksum = (EChecksumType)option_id; ImGui::EndTable(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 693ba0e5fa..018e338335 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5466,9 +5466,10 @@ void Plater::convert_gcode_to_ascii() // Perform conversion { wxBusyCursor busy; - bgcode::core::EResult res = bgcode::convert::from_binary_to_ascii(*in_file.f, *out_file.f, true); - if (res != bgcode::core::EResult::Success) { - MessageDialog msg_dlg(this, _L(std::string(bgcode::core::translate_result(res))), _L("Error converting gcode file"), wxICON_INFORMATION | wxOK); + using namespace bgcode::core; + EResult res = bgcode::convert::from_binary_to_ascii(*in_file.f, *out_file.f, true); + if (res != EResult::Success) { + MessageDialog msg_dlg(this, _L(std::string(translate_result(res))), _L("Error converting gcode file"), wxICON_INFORMATION | wxOK); msg_dlg.ShowModal(); out_file.close(); boost::nowide::remove(output_file.c_str()); @@ -5511,10 +5512,11 @@ void Plater::convert_gcode_to_binary() // Perform conversion { wxBusyCursor busy; + using namespace bgcode::core; const bgcode::binarize::BinarizerConfig& binarizer_config = GCodeProcessor::get_binarizer_config(); - bgcode::core::EResult res = bgcode::convert::from_ascii_to_binary(*in_file.f, *out_file.f, binarizer_config); - if (res != bgcode::core::EResult::Success) { - MessageDialog msg_dlg(this, _L(std::string(bgcode::core::translate_result(res))), _L("Error converting gcode file"), wxICON_INFORMATION | wxOK); + EResult res = bgcode::convert::from_ascii_to_binary(*in_file.f, *out_file.f, binarizer_config); + if (res != EResult::Success) { + MessageDialog msg_dlg(this, _L(std::string(translate_result(res))), _L("Error converting gcode file"), wxICON_INFORMATION | wxOK); msg_dlg.ShowModal(); out_file.close(); boost::nowide::remove(output_file.c_str()); From cfb09222dcd5948d1841ff05736ff3068acd65c6 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 14 Aug 2023 13:15:48 +0200 Subject: [PATCH 067/136] LibBGCode.cmake modified to download library from latest commit --- deps/LibBGCode/LibBGCode.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/LibBGCode/LibBGCode.cmake b/deps/LibBGCode/LibBGCode.cmake index 6a076e9f67..bf3225b28e 100644 --- a/deps/LibBGCode/LibBGCode.cmake +++ b/deps/LibBGCode/LibBGCode.cmake @@ -1,6 +1,6 @@ set(LibBGCode_SOURCE_DIR "" CACHE PATH "Optionally specify local LibBGCode source directory") -set(_source_dir_line "GIT_REPOSITORY;https://github.com/prusa3d/libbgcode.git;GIT_TAG;main") +set(_source_dir_line "GIT_REPOSITORY;https://github.com/prusa3d/libbgcode.git;GIT_TAG;4482d545328ebc1051d3e118ee65952bbcabb751") if (LibBGCode_SOURCE_DIR) set(_source_dir_line "SOURCE_DIR;${LibBGCode_SOURCE_DIR};BUILD_ALWAYS;ON") From 0b6ab8359aabd5fc7c9e5141cca4eccfa9448766 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 14 Aug 2023 13:18:25 +0200 Subject: [PATCH 068/136] Removed unused parameter --- src/libslic3r/GCode/GCodeProcessor.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 1e2596e15a..253da95b13 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -3975,12 +3975,12 @@ void GCodeProcessor::post_process() #endif // ENABLE_BINARIZED_GCODE write_to_file(out, out_string, result, out_path); #if ENABLE_BINARIZED_GCODE - update_out_file_pos(out, out_string, result); + update_out_file_pos(out_string, result); #endif // ENABLE_BINARIZED_GCODE } #if ENABLE_BINARIZED_GCODE - void update_out_file_pos(FilePtr& out, const std::string& out_string, GCodeProcessorResult& result) { + void update_out_file_pos(const std::string& out_string, GCodeProcessorResult& result) { for (size_t i = 0; i < out_string.size(); ++i) { if (out_string[i] == '\n') result.lines_ends.emplace_back(m_out_file_pos + i + 1); @@ -4011,7 +4011,7 @@ void GCodeProcessor::post_process() #endif // ENABLE_BINARIZED_GCODE write_to_file(out, out_string, result, out_path); #if ENABLE_BINARIZED_GCODE - update_out_file_pos(out, out_string, result); + update_out_file_pos(out_string, result); #endif // ENABLE_BINARIZED_GCODE } From 184fad3d8eb2722d3134cae06e8f8d9c2db33f73 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 14 Aug 2023 17:37:08 +0200 Subject: [PATCH 069/136] Follow-up of cfb09222dcd5948d1841ff05736ff3068acd65c6 - Change git url to simple zip url for LibBGCode --- deps/LibBGCode/LibBGCode.cmake | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/deps/LibBGCode/LibBGCode.cmake b/deps/LibBGCode/LibBGCode.cmake index bf3225b28e..10fe70914d 100644 --- a/deps/LibBGCode/LibBGCode.cmake +++ b/deps/LibBGCode/LibBGCode.cmake @@ -1,6 +1,9 @@ set(LibBGCode_SOURCE_DIR "" CACHE PATH "Optionally specify local LibBGCode source directory") -set(_source_dir_line "GIT_REPOSITORY;https://github.com/prusa3d/libbgcode.git;GIT_TAG;4482d545328ebc1051d3e118ee65952bbcabb751") +set(_source_dir_line + URL https://github.com/prusa3d/libbgcode/archive/4482d545328ebc1051d3e118ee65952bbcabb751.zip + URL_HASH SHA256=a9baea92b0defcd460cb620774a3d4aac200f40bce4b2a44af8c69ef090d8fc2 +) if (LibBGCode_SOURCE_DIR) set(_source_dir_line "SOURCE_DIR;${LibBGCode_SOURCE_DIR};BUILD_ALWAYS;ON") From 271d297bb8768da740dfde8c327ccb211c2bf068 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 16 Aug 2023 09:14:08 +0200 Subject: [PATCH 070/136] Updated LibBGCode.cmake to download newer version of libbgcode --- deps/LibBGCode/LibBGCode.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deps/LibBGCode/LibBGCode.cmake b/deps/LibBGCode/LibBGCode.cmake index 10fe70914d..4e8bd2cdfc 100644 --- a/deps/LibBGCode/LibBGCode.cmake +++ b/deps/LibBGCode/LibBGCode.cmake @@ -1,8 +1,8 @@ set(LibBGCode_SOURCE_DIR "" CACHE PATH "Optionally specify local LibBGCode source directory") set(_source_dir_line - URL https://github.com/prusa3d/libbgcode/archive/4482d545328ebc1051d3e118ee65952bbcabb751.zip - URL_HASH SHA256=a9baea92b0defcd460cb620774a3d4aac200f40bce4b2a44af8c69ef090d8fc2 + URL https://github.com/prusa3d/libbgcode/archive/767a0970a0df277c366a08f32b72e39b6e628b54.zip + URL_HASH SHA256=60025397e9ce301ac925b59a12cfced26034b6124866b618c4771842d109847f ) if (LibBGCode_SOURCE_DIR) From e2472fc78cc04fdbd4f4377fd32ce1b6f1c24875 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 16 Aug 2023 12:23:37 +0200 Subject: [PATCH 071/136] Updated LibBGCode.cmake to download newer version of libbgcode --- deps/LibBGCode/LibBGCode.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deps/LibBGCode/LibBGCode.cmake b/deps/LibBGCode/LibBGCode.cmake index 4e8bd2cdfc..cf90266678 100644 --- a/deps/LibBGCode/LibBGCode.cmake +++ b/deps/LibBGCode/LibBGCode.cmake @@ -1,8 +1,8 @@ set(LibBGCode_SOURCE_DIR "" CACHE PATH "Optionally specify local LibBGCode source directory") set(_source_dir_line - URL https://github.com/prusa3d/libbgcode/archive/767a0970a0df277c366a08f32b72e39b6e628b54.zip - URL_HASH SHA256=60025397e9ce301ac925b59a12cfced26034b6124866b618c4771842d109847f + URL https://github.com/prusa3d/libbgcode/archive/49e7b47b7a9a7e9365613d3f90ad49d71a0e93d6.zip + URL_HASH SHA256=b9513ab2bbaf06a79c35c4d69226d211f1226ca625ce07c04f54b89b93e9bc75 ) if (LibBGCode_SOURCE_DIR) From cad97bc9a2843055bf31ba8cb78da4d940f7311d Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 21 Aug 2023 12:26:56 +0200 Subject: [PATCH 072/136] GCodeViewer - Update gcode window for binary gcode files --- src/libslic3r/GCode/GCodeProcessor.cpp | 52 ++++--- src/libslic3r/GCode/GCodeProcessor.hpp | 7 + src/libslic3r/GCodeReader.cpp | 9 ++ src/libslic3r/GCodeReader.hpp | 4 + src/slic3r/GUI/GCodeViewer.cpp | 199 +++++++++++++++++++------ src/slic3r/GUI/GCodeViewer.hpp | 18 ++- 6 files changed, 221 insertions(+), 68 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 253da95b13..ffb35c8e59 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -464,6 +464,9 @@ void GCodeProcessorResult::reset() { #else void GCodeProcessorResult::reset() { +#if ENABLE_BINARIZED_GCODE + is_binary_file = false; +#endif // ENABLE_BINARIZED_GCODE moves.clear(); lines_ends.clear(); bed_shape = Pointfs(); @@ -564,6 +567,7 @@ void GCodeProcessor::apply_config(const PrintConfig& config) #if ENABLE_BINARIZED_GCODE m_binarizer.set_enabled(config.gcode_binary); + m_result.is_binary_file = config.gcode_binary; #endif // ENABLE_BINARIZED_GCODE m_producer = EProducer::PrusaSlicer; @@ -1104,6 +1108,9 @@ void GCodeProcessor::process_ascii_file(const std::string& filename, std::functi // process gcode m_result.filename = filename; +#if ENABLE_BINARIZED_GCODE + m_result.is_binary_file = false; +#endif // ENABLE_BINARIZED_GCODE m_result.id = ++s_result_id; initialize_result_moves(); size_t parse_line_callback_cntr = 10000; @@ -1122,6 +1129,16 @@ void GCodeProcessor::process_ascii_file(const std::string& filename, std::functi } #if ENABLE_BINARIZED_GCODE +static void update_out_file_pos(const std::string& out_string, std::vector& lines_ends, size_t* out_file_pos) +{ + for (size_t i = 0; i < out_string.size(); ++i) { + if (out_string[i] == '\n') + lines_ends.emplace_back((out_file_pos != nullptr) ? *out_file_pos + i + 1 : i + 1); + } + if (out_file_pos != nullptr) + *out_file_pos += out_string.size(); +} + void GCodeProcessor::process_binary_file(const std::string& filename, std::function cancel_callback) { #if ENABLE_GCODE_VIEWER_STATISTICS @@ -1272,6 +1289,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct apply_config(config); m_result.filename = filename; + m_result.is_binary_file = true; m_result.id = ++s_result_id; initialize_result_moves(); @@ -1287,7 +1305,8 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct if (res != EResult::Success) throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); - // TODO: Update m_result.lines_ends + std::vector& lines_ends = m_result.lines_ends.emplace_back(std::vector()); + update_out_file_pos(block.raw_data, lines_ends, nullptr); m_parser.parse_buffer(block.raw_data, [this](GCodeReader& reader, const GCodeReader::GCodeLine& line) { this->process_gcode_line(line, true); @@ -3971,23 +3990,14 @@ void GCodeProcessor::post_process() if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError(std::string("Error while sending gcode to the binarizer.\n")); } - else -#endif // ENABLE_BINARIZED_GCODE + else { write_to_file(out, out_string, result, out_path); -#if ENABLE_BINARIZED_GCODE - update_out_file_pos(out_string, result); -#endif // ENABLE_BINARIZED_GCODE - } - -#if ENABLE_BINARIZED_GCODE - void update_out_file_pos(const std::string& out_string, GCodeProcessorResult& result) { - for (size_t i = 0; i < out_string.size(); ++i) { - if (out_string[i] == '\n') - result.lines_ends.emplace_back(m_out_file_pos + i + 1); + update_out_file_pos(out_string, result.lines_ends.front(), &m_out_file_pos); } - m_out_file_pos += out_string.size(); - } +#else + write_to_file(out, out_string, result, out_path); #endif // ENABLE_BINARIZED_GCODE + } // flush the current content of the cache to file void flush(FilePtr& out, GCodeProcessorResult& result, const std::string& out_path) { @@ -4007,11 +4017,12 @@ void GCodeProcessor::post_process() if (m_binarizer.append_gcode(out_string) != bgcode::core::EResult::Success) throw Slic3r::RuntimeError(std::string("Error while sending gcode to the binarizer.\n")); } - else -#endif // ENABLE_BINARIZED_GCODE + else { write_to_file(out, out_string, result, out_path); -#if ENABLE_BINARIZED_GCODE - update_out_file_pos(out_string, result); + update_out_file_pos(out_string, result.lines_ends.front(), &m_out_file_pos); + } +#else + write_to_file(out, out_string, result, out_path); #endif // ENABLE_BINARIZED_GCODE } @@ -4312,6 +4323,9 @@ void GCodeProcessor::post_process() }; m_result.lines_ends.clear(); +#if ENABLE_BINARIZED_GCODE + m_result.lines_ends.emplace_back(std::vector()); +#endif // ENABLE_BINARIZED_GCODE unsigned int line_id = 0; // Backtrace data for Tx gcode lines diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index bef1cd68f3..1126432518 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -139,10 +139,17 @@ namespace Slic3r { }; std::string filename; +#if ENABLE_BINARIZED_GCODE + bool is_binary_file; +#endif // ENABLE_BINARIZED_GCODE unsigned int id; std::vector moves; // Positions of ends of lines of the final G-code this->filename after TimeProcessor::post_process() finalizes the G-code. +#if ENABLE_BINARIZED_GCODE + std::vector> lines_ends; +#else std::vector lines_ends; +#endif // ENABLE_BINARIZED_GCODE Pointfs bed_shape; float max_print_height; SettingsIds settings_ids; diff --git a/src/libslic3r/GCodeReader.cpp b/src/libslic3r/GCodeReader.cpp index cb0366757a..2b94963e93 100644 --- a/src/libslic3r/GCodeReader.cpp +++ b/src/libslic3r/GCodeReader.cpp @@ -195,11 +195,20 @@ bool GCodeReader::parse_file(const std::string &file, callback_t callback) return this->parse_file_internal(file, callback, [](size_t){}); } +#if ENABLE_BINARIZED_GCODE +bool GCodeReader::parse_file(const std::string& file, callback_t callback, std::vector>& lines_ends) +{ + lines_ends.clear(); + lines_ends.push_back(std::vector()); + return this->parse_file_internal(file, callback, [&lines_ends](size_t file_pos) { lines_ends.front().emplace_back(file_pos); }); +} +#else bool GCodeReader::parse_file(const std::string &file, callback_t callback, std::vector &lines_ends) { lines_ends.clear(); return this->parse_file_internal(file, callback, [&lines_ends](size_t file_pos){ lines_ends.emplace_back(file_pos); }); } +#endif // ENABLE_BINARIZED_GCODE bool GCodeReader::parse_file_raw(const std::string &filename, raw_line_callback_t line_callback) { diff --git a/src/libslic3r/GCodeReader.hpp b/src/libslic3r/GCodeReader.hpp index 58f55fdcf7..7d1dad92a0 100644 --- a/src/libslic3r/GCodeReader.hpp +++ b/src/libslic3r/GCodeReader.hpp @@ -131,7 +131,11 @@ public: bool parse_file(const std::string &file, callback_t callback); // Collect positions of line ends in the binary G-code to be used by the G-code viewer when memory mapping and displaying section of G-code // as an overlay in the 3D scene. +#if ENABLE_BINARIZED_GCODE + bool parse_file(const std::string& file, callback_t callback, std::vector>& lines_ends); +#else bool parse_file(const std::string &file, callback_t callback, std::vector &lines_ends); +#endif // ENABLE_BINARIZED_GCODE // Just read the G-code file line by line, calls callback (const char *begin, const char *end). Returns false if reading the file failed. bool parse_file_raw(const std::string &file, raw_line_callback_t callback); diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index bfa6c87946..6425c359b3 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -367,18 +367,22 @@ void GCodeViewer::SequentialView::Marker::render() ImGui::PopStyleVar(); } +#if ENABLE_BINARIZED_GCODE +void GCodeViewer::SequentialView::GCodeWindow::load_gcode(const GCodeProcessorResult& gcode_result) +{ + m_filename = gcode_result.filename; + m_is_binary_file = gcode_result.is_binary_file; + m_lines_ends = gcode_result.lines_ends; +} +#else void GCodeViewer::SequentialView::GCodeWindow::load_gcode(const std::string& filename, const std::vector& lines_ends) { -#if !ENABLE_BINARIZED_GCODE - assert(! m_file.is_open()); + assert(!m_file.is_open()); if (m_file.is_open()) return; -#endif // !ENABLE_BINARIZED_GCODE - m_filename = filename; + m_filename = filename; m_lines_ends = lines_ends; - -#if !ENABLE_BINARIZED_GCODE m_selected_line_id = 0; m_last_lines_size = 0; @@ -391,54 +395,150 @@ void GCodeViewer::SequentialView::GCodeWindow::load_gcode(const std::string& fil BOOST_LOG_TRIVIAL(error) << "Unable to map file " << m_filename << ". Cannot show G-code window."; reset(); } -#endif // !ENABLE_BINARIZED_GCODE } +#endif // ENABLE_BINARIZED_GCODE #if ENABLE_BINARIZED_GCODE +void GCodeViewer::SequentialView::GCodeWindow::add_gcode_line_to_lines_cache(const std::string& src) +{ + std::string command; + std::string parameters; + std::string comment; + + // extract comment + std::vector tokens; + boost::split(tokens, src, boost::is_any_of(";"), boost::token_compress_on); + command = tokens.front(); + if (tokens.size() > 1) + comment = ";" + tokens.back(); + + // extract gcode command and parameters + if (!command.empty()) { + boost::split(tokens, command, boost::is_any_of(" "), boost::token_compress_on); + command = tokens.front(); + if (tokens.size() > 1) { + for (size_t i = 1; i < tokens.size(); ++i) { + parameters += " " + tokens[i]; + } + } + } + + m_lines_cache.push_back({ command, parameters, comment }); +} + void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, size_t curr_line_id) { - auto update_lines = [this]() { + auto update_lines_ascii = [this]() { m_lines_cache.clear(); m_lines_cache.reserve(m_cache_range.size()); + const std::vector& lines_ends = m_lines_ends.front(); FILE* file = boost::nowide::fopen(m_filename.c_str(), "rb"); if (file != nullptr) { - for (size_t id = *m_cache_range.start_id; id <= *m_cache_range.end_id; ++id) { + for (size_t id = *m_cache_range.min; id <= *m_cache_range.max; ++id) { assert(id > 0); // read line from file - const size_t start = id == 1 ? 0 : m_lines_ends[id - 2]; - const size_t len = m_lines_ends[id - 1] - start; + const size_t begin = id == 1 ? 0 : lines_ends[id - 2]; + const size_t len = lines_ends[id - 1] - begin; std::string gline(len, '\0'); - fseek(file, start, SEEK_SET); + fseek(file, begin, SEEK_SET); const size_t rsize = fread((void*)gline.data(), 1, len, file); if (ferror(file) || rsize != len) { m_lines_cache.clear(); break; } - std::string command; - std::string parameters; - std::string comment; + add_gcode_line_to_lines_cache(gline); + } + fclose(file); + } + }; - // extract comment - std::vector tokens; - boost::split(tokens, gline, boost::is_any_of(";"), boost::token_compress_on); - command = tokens.front(); - if (tokens.size() > 1) - comment = ";" + tokens.back(); + auto update_lines_binary = [this]() { + m_lines_cache.clear(); + m_lines_cache.reserve(m_cache_range.size()); - // extract gcode command and parameters - if (!command.empty()) { - boost::split(tokens, command, boost::is_any_of(" "), boost::token_compress_on); - command = tokens.front(); - if (tokens.size() > 1) { - for (size_t i = 1; i < tokens.size(); ++i) { - parameters += " " + tokens[i]; + size_t cumulative_lines_count = 0; + std::vector cumulative_lines_counts; + cumulative_lines_counts.reserve(m_lines_ends.size()); + for (size_t i = 0; i < m_lines_ends.size(); ++i) { + cumulative_lines_count += m_lines_ends[i].size(); + cumulative_lines_counts.emplace_back(cumulative_lines_count); + } + + size_t first_block_id = 0; + for (size_t i = 0; i < cumulative_lines_counts.size(); ++i) { + if (*m_cache_range.min <= cumulative_lines_counts[i]) { + first_block_id = i; + break; + } + } + size_t last_block_id = 0; + for (size_t i = 0; i < cumulative_lines_counts.size(); ++i) { + if (*m_cache_range.max <= cumulative_lines_counts[i]) { + last_block_id = i; + break; + } + } + assert(last_block_id >= first_block_id); + + FilePtr file(boost::nowide::fopen(m_filename.c_str(), "rb")); + if (file.f != nullptr) { + fseek(file.f, 0, SEEK_END); + const long file_size = ftell(file.f); + rewind(file.f); + + // read file header + using namespace bgcode::core; + using namespace bgcode::binarize; + FileHeader file_header; + EResult res = read_header(*file.f, file_header, nullptr); + if (res == EResult::Success) { + // search first GCode block + BlockHeader block_header; + res = read_next_block_header(*file.f, file_header, block_header, EBlockType::GCode, nullptr, 0); + if (res == EResult::Success) { + for (size_t i = 0; i < first_block_id; ++i) { + skip_block(*file.f, file_header, block_header); + res = read_next_block_header(*file.f, file_header, block_header, nullptr, 0); + if (res != EResult::Success || block_header.type != (uint16_t)EBlockType::GCode) { + m_lines_cache.clear(); + return; + } + } + + for (size_t i = first_block_id; i <= last_block_id; ++i) { + GCodeBlock block; + res = block.read_data(*file.f, file_header, block_header); + if (res != EResult::Success) { + m_lines_cache.clear(); + return; + } + + const size_t ref_id = (i == 0) ? 0 : i - 1; + const size_t first_line_id = (i == 0) ? *m_cache_range.min : + (*m_cache_range.min - 1 >= cumulative_lines_counts[ref_id]) ? *m_cache_range.min - cumulative_lines_counts[ref_id] : 1; + const size_t last_line_id = (*m_cache_range.max - 1 <= cumulative_lines_counts[i]) ? + (i == 0) ? *m_cache_range.max : *m_cache_range.max - cumulative_lines_counts[ref_id] : m_lines_ends[i].size() - 1; + + for (size_t j = first_line_id; j <= last_line_id; ++j) { + const size_t begin = (j == 1) ? 0 : m_lines_ends[i][j - 2]; + const size_t end = m_lines_ends[i][j - 1]; + std::string gline; + gline.insert(gline.end(), block.raw_data.begin() + begin, block.raw_data.begin() + end); + add_gcode_line_to_lines_cache(gline); + } + + if (ftell(file.f) == file_size) + break; + + res = read_next_block_header(*file.f, file_header, block_header, nullptr, 0); + if (res != EResult::Success || block_header.type != (uint16_t)EBlockType::GCode) { + m_lines_cache.clear(); + return; } } } - m_lines_cache.push_back({ command, parameters, comment }); } - fclose(file); } }; @@ -463,13 +563,20 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, s if (visible_lines_count == 0) return; + if (m_lines_ends.empty() || m_lines_ends.front().empty()) + return; + auto resize_range = [&](Range& range, size_t lines_count) { const size_t half_lines_count = lines_count / 2; - range.start_id = (curr_line_id >= half_lines_count) ? curr_line_id - half_lines_count : 1; - range.end_id = *range.start_id + lines_count - 1; - if (*range.end_id >= m_lines_ends.size()) { - range.end_id = m_lines_ends.size() - 1; - range.start_id = *range.end_id - lines_count + 1; + range.min = (curr_line_id >= half_lines_count) ? curr_line_id - half_lines_count : 1; + range.max = *range.min + lines_count - 1; + size_t lines_ends_count = 0; + for (const auto& le : m_lines_ends) { + lines_ends_count += le.size(); + } + if (*range.max >= lines_ends_count) { + range.max = lines_ends_count - 1; + range.min = *range.max - lines_count + 1; } }; @@ -480,11 +587,17 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, s // update cache if needed if (m_cache_range.empty() || !m_cache_range.contains(visible_range)) { resize_range(m_cache_range, 4 * visible_range.size()); - update_lines(); + if (m_is_binary_file) + update_lines_binary(); + else + update_lines_ascii(); } + if (m_lines_cache.empty()) + return; + // line number's column width - const float id_width = ImGui::CalcTextSize(std::to_string(*visible_range.end_id).c_str()).x; + const float id_width = ImGui::CalcTextSize(std::to_string(*visible_range.max).c_str()).x; ImGuiWrapper& imgui = *wxGetApp().imgui(); @@ -525,8 +638,8 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, s // render text lines size_t max_line_length = 0; - for (size_t id = *visible_range.start_id; id <= *visible_range.end_id; ++id) { - const Line& line = m_lines_cache[id - *m_cache_range.start_id]; + for (size_t id = *visible_range.min; id <= *visible_range.max; ++id) { + const Line& line = m_lines_cache[id - *m_cache_range.min]; // rect around the current selected line if (id == curr_line_id) { @@ -729,15 +842,13 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, u imgui.end(); ImGui::PopStyleVar(); } -#endif // ENABLE_BINARIZED_GCODE -#if !ENABLE_BINARIZED_GCODE void GCodeViewer::SequentialView::GCodeWindow::stop_mapping_file() { if (m_file.is_open()) m_file.close(); } -#endif // !ENABLE_BINARIZED_GCODE +#endif // ENABLE_BINARIZED_GCODE void GCodeViewer::SequentialView::render(float legend_height) { @@ -917,7 +1028,11 @@ void GCodeViewer::load(const GCodeProcessorResult& gcode_result, const Print& pr // release gpu memory, if used reset(); +#if ENABLE_BINARIZED_GCODE + m_sequential_view.gcode_window.load_gcode(gcode_result); +#else m_sequential_view.gcode_window.load_gcode(gcode_result.filename, gcode_result.lines_ends); +#endif // ENABLE_BINARIZED_GCODE if (wxGetApp().is_gcode_viewer()) m_custom_gcode_per_print_z = gcode_result.custom_gcode_per_print_z; diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 583619e623..60c350afe2 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -686,29 +686,30 @@ public: struct Range { - std::optional start_id; - std::optional end_id; + std::optional min; + std::optional max; bool empty() const { - return !start_id.has_value() || !end_id.has_value(); + return !min.has_value() || !max.has_value(); } bool contains(const Range& other) const { - return !this->empty() && !other.empty() && *this->start_id <= *other.start_id && *this->end_id >= other.end_id; + return !this->empty() && !other.empty() && *this->min <= *other.min && *this->max >= other.max; } size_t size() const { - return empty() ? 0 : *this->end_id - *this->start_id + 1; + return empty() ? 0 : *this->max - *this->min + 1; } }; bool m_visible{ true }; std::string m_filename; + bool m_is_binary_file{ false }; // map for accessing data in file by line number - std::vector m_lines_ends; + std::vector> m_lines_ends; std::vector m_lines_cache; Range m_cache_range; size_t m_max_line_length{ 0 }; public: - void load_gcode(const std::string& filename, const std::vector& lines_ends); + void load_gcode(const GCodeProcessorResult& gcode_result); void reset() { m_lines_ends.clear(); m_lines_cache.clear(); @@ -716,6 +717,9 @@ public: } void toggle_visibility() { m_visible = !m_visible; } void render(float top, float bottom, size_t curr_line_id); + + private: + void add_gcode_line_to_lines_cache(const std::string& src); }; #else class GCodeWindow From aec962c2950721482af957fbac1bd12108ec2801 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 21 Aug 2023 13:22:38 +0200 Subject: [PATCH 073/136] Preview - Update gcode window when exporting to binary gcode --- src/libslic3r/GCode/GCodeProcessor.cpp | 57 +++++++++++++++++++++++++- src/libslic3r/Technologies.hpp | 2 +- 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index ffb35c8e59..7065f5db14 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -3670,9 +3670,8 @@ void GCodeProcessor::post_process() // temporary file to contain modified gcode std::string out_path = m_result.filename + ".postprocess"; FilePtr out{ boost::nowide::fopen(out_path.c_str(), "wb") }; - if (out.f == nullptr) { + if (out.f == nullptr) throw Slic3r::RuntimeError(std::string("GCode processor post process export failed.\nCannot open file for writing.\n")); - } #if ENABLE_BINARIZED_GCODE std::vector filament_mm(m_result.extruders_count, 0.0); @@ -4405,6 +4404,60 @@ void GCodeProcessor::post_process() out.close(); in.close(); +#if ENABLE_BINARIZED_GCODE + if (m_binarizer.is_enabled()) { + // updates m_result.lines_ends from binarized gcode file + m_result.lines_ends.clear(); + + FilePtr file(boost::nowide::fopen(out_path.c_str(), "rb")); + if (file.f != nullptr) { + fseek(file.f, 0, SEEK_END); + const long file_size = ftell(file.f); + rewind(file.f); + + // read file header + using namespace bgcode::core; + using namespace bgcode::binarize; + FileHeader file_header; + EResult res = read_header(*file.f, file_header, nullptr); + if (res == EResult::Success) { + // search first GCode block + BlockHeader block_header; + res = read_next_block_header(*file.f, file_header, block_header, EBlockType::GCode, nullptr, 0); + while (res == EResult::Success) { + GCodeBlock block; + res = block.read_data(*file.f, file_header, block_header); + if (res != EResult::Success) + break; + + // extract lines ends from block + std::vector& lines_ends = m_result.lines_ends.emplace_back(std::vector()); + for (size_t i = 0; i < block.raw_data.size(); ++i) { + if (block.raw_data[i] == '\n') + lines_ends.emplace_back(i + 1); + } + + if (ftell(file.f) == file_size) + break; + + // read next block header + res = read_next_block_header(*file.f, file_header, block_header, nullptr, 0); + if (res != EResult::Success) + break; + if (block_header.type != (uint16_t)EBlockType::GCode) { + res = EResult::InvalidBlockType; + break; + } + } + } + + if (res != EResult::Success && !m_result.lines_ends.empty() && !m_result.lines_ends.front().empty()) + // some error occourred, clear lines ends + m_result.lines_ends = { std::vector() }; + } + } +#endif // ENABLE_BINARIZED_GCODE + export_lines.synchronize_moves(m_result); if (rename_file(out_path, m_result.filename)) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index d7670f5c28..45db76451b 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -71,7 +71,7 @@ #define ENABLE_BINARIZED_GCODE (1 && ENABLE_2_6_2_ALPHA1) #define ENABLE_BINARIZED_GCODE_DEBUG (1 && ENABLE_BINARIZED_GCODE) #ifdef _WIN32 -#define ENABLE_BINARIZED_GCODE_WIN_DEBUG (1 && ENABLE_BINARIZED_GCODE_DEBUG) +#define ENABLE_BINARIZED_GCODE_WIN_DEBUG (0 && ENABLE_BINARIZED_GCODE_DEBUG) #endif // _WIN32 #define ENABLE_BINARIZED_GCODE_DEBUG_WINDOW (1 && ENABLE_BINARIZED_GCODE) From bfbe4dacaea58e99cc1361507f7e5a016c709dd9 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Mon, 21 Aug 2023 14:26:03 +0200 Subject: [PATCH 074/136] Few small refactorings --- src/libslic3r/Config.cpp | 7 +++---- src/libslic3r/GCode.cpp | 9 +++------ src/libslic3r/GCode/GCodeProcessor.cpp | 18 ++++++++---------- src/libslic3r/Technologies.hpp | 4 ++-- 4 files changed, 16 insertions(+), 22 deletions(-) diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index 629f39ef43..5ed0553a11 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -1077,16 +1077,14 @@ ConfigSubstitutions ConfigBase::load_from_binary_gcode_file(const std::string& f std::vector cs_buffer(65536); EResult res = is_valid_binary_gcode(*file.f, true, cs_buffer.data(), cs_buffer.size()); if (res != EResult::Success) - throw Slic3r::RuntimeError(format("The selected file is not a valid binary gcode.\nError: %1%", - std::string(translate_result(res)))); - - rewind(file.f); + throw Slic3r::RuntimeError(format("The selected file is not a valid binary gcode.\nError: %1%", std::string(translate_result(res)))); FileHeader file_header; res = read_header(*file.f, file_header, nullptr); if (res != EResult::Success) throw Slic3r::RuntimeError(format("Error while reading file '%1%': %2%", filename, std::string(translate_result(res)))); + // searches for config block BlockHeader block_header; res = read_next_block_header(*file.f, file_header, block_header, EBlockType::SlicerMetadata, cs_buffer.data(), cs_buffer.size()); if (res != EResult::Success) @@ -1098,6 +1096,7 @@ ConfigSubstitutions ConfigBase::load_from_binary_gcode_file(const std::string& f if (res != EResult::Success) throw Slic3r::RuntimeError(format("Error while reading file '%1%': %2%", filename, std::string(translate_result(res)))); + // extracts data from block for (const auto& [key, value] : slicer_metadata_block.raw_data) { this->set_deserialize(key, value, substitutions_ctxt); } diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index b12b0508e7..ff49610764 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1214,13 +1214,10 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato binary_data.printer_metadata.raw_data.emplace_back("extruder_colour", extruder_colours_str); // duplicated into config data } } - - // modifies m_silent_time_estimator_enabled - DoExport::init_gcode_processor(print.config(), m_processor, m_silent_time_estimator_enabled); -#else - // modifies m_silent_time_estimator_enabled - DoExport::init_gcode_processor(print.config(), m_processor, m_silent_time_estimator_enabled); #endif // ENABLE_BINARIZED_GCODE + + // modifies m_silent_time_estimator_enabled + DoExport::init_gcode_processor(print.config(), m_processor, m_silent_time_estimator_enabled); if (! print.config().gcode_substitutions.values.empty()) { m_find_replace = make_unique(print.config()); diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 7065f5db14..0e58a318dd 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -71,7 +71,7 @@ const float GCodeProcessor::Wipe_Height = 0.05f; #if ENABLE_BINARIZED_GCODE bgcode::binarize::BinarizerConfig GCodeProcessor::s_binarizer_config{}; -#endif // ENABLE_BINARIZED +#endif // ENABLE_BINARIZED_GCODE #if ENABLE_GCODE_VIEWER_DATA_CHECKING const std::string GCodeProcessor::Mm3_Per_Mm_Tag = "MM3_PER_MM:"; @@ -1129,7 +1129,7 @@ void GCodeProcessor::process_ascii_file(const std::string& filename, std::functi } #if ENABLE_BINARIZED_GCODE -static void update_out_file_pos(const std::string& out_string, std::vector& lines_ends, size_t* out_file_pos) +static void update_lines_ends_and_out_file_pos(const std::string& out_string, std::vector& lines_ends, size_t* out_file_pos) { for (size_t i = 0; i < out_string.size(); ++i) { if (out_string[i] == '\n') @@ -1306,7 +1306,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); std::vector& lines_ends = m_result.lines_ends.emplace_back(std::vector()); - update_out_file_pos(block.raw_data, lines_ends, nullptr); + update_lines_ends_and_out_file_pos(block.raw_data, lines_ends, nullptr); m_parser.parse_buffer(block.raw_data, [this](GCodeReader& reader, const GCodeReader::GCodeLine& line) { this->process_gcode_line(line, true); @@ -3730,9 +3730,8 @@ void GCodeProcessor::post_process() } } - using namespace bgcode::core; - const EResult res = m_binarizer.initialize(*out.f, s_binarizer_config); - if (res != EResult::Success) + const bgcode::core::EResult res = m_binarizer.initialize(*out.f, s_binarizer_config); + if (res != bgcode::core::EResult::Success) throw Slic3r::RuntimeError(std::string("Unable to initialize the gcode binarizer.\n")); } #endif // ENABLE_BINARIZED_GCODE @@ -3985,13 +3984,12 @@ void GCodeProcessor::post_process() #if ENABLE_BINARIZED_GCODE if (m_binarizer.is_enabled()) { - bgcode::core::EResult res = m_binarizer.append_gcode(out_string); - if (res != bgcode::core::EResult::Success) + if (m_binarizer.append_gcode(out_string) != bgcode::core::EResult::Success) throw Slic3r::RuntimeError(std::string("Error while sending gcode to the binarizer.\n")); } else { write_to_file(out, out_string, result, out_path); - update_out_file_pos(out_string, result.lines_ends.front(), &m_out_file_pos); + update_lines_ends_and_out_file_pos(out_string, result.lines_ends.front(), &m_out_file_pos); } #else write_to_file(out, out_string, result, out_path); @@ -4018,7 +4016,7 @@ void GCodeProcessor::post_process() } else { write_to_file(out, out_string, result, out_path); - update_out_file_pos(out_string, result.lines_ends.front(), &m_out_file_pos); + update_lines_ends_and_out_file_pos(out_string, result.lines_ends.front(), &m_out_file_pos); } #else write_to_file(out, out_string, result, out_path); diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 45db76451b..6a1743285e 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -69,9 +69,9 @@ // Enable export of binarized gcode #define ENABLE_BINARIZED_GCODE (1 && ENABLE_2_6_2_ALPHA1) -#define ENABLE_BINARIZED_GCODE_DEBUG (1 && ENABLE_BINARIZED_GCODE) +#define ENABLE_BINARIZED_GCODE_DEBUG (0 && ENABLE_BINARIZED_GCODE) #ifdef _WIN32 -#define ENABLE_BINARIZED_GCODE_WIN_DEBUG (0 && ENABLE_BINARIZED_GCODE_DEBUG) +#define ENABLE_BINARIZED_GCODE_WIN_DEBUG (1 && ENABLE_BINARIZED_GCODE_DEBUG) #endif // _WIN32 #define ENABLE_BINARIZED_GCODE_DEBUG_WINDOW (1 && ENABLE_BINARIZED_GCODE) From 3b79b00a77e8b2624dd01ffd030ab2581e13b024 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 28 Aug 2023 10:58:47 +0200 Subject: [PATCH 075/136] Bumped up version to 2.6.2-alpha --- src/slic3r/GUI/GUI_App.cpp | 4 ++-- version.inc | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index e8c7605473..e06830ec9c 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -960,8 +960,8 @@ void GUI_App::init_app_config() { // Profiles for the alpha are stored into the PrusaSlicer-alpha directory to not mix with the current release. - SetAppName(SLIC3R_APP_KEY); -// SetAppName(SLIC3R_APP_KEY "-alpha"); +// SetAppName(SLIC3R_APP_KEY); + SetAppName(SLIC3R_APP_KEY "-alpha"); // SetAppName(SLIC3R_APP_KEY "-beta"); diff --git a/version.inc b/version.inc index 854685e1a3..54cb68e4a8 100644 --- a/version.inc +++ b/version.inc @@ -3,7 +3,7 @@ set(SLIC3R_APP_NAME "PrusaSlicer") set(SLIC3R_APP_KEY "PrusaSlicer") -set(SLIC3R_VERSION "2.6.1-rc1") +set(SLIC3R_VERSION "2.6.2-alpha0") set(SLIC3R_BUILD_ID "PrusaSlicer-${SLIC3R_VERSION}+UNKNOWN") -set(SLIC3R_RC_VERSION "2,6,1,0") -set(SLIC3R_RC_VERSION_DOTS "2.6.1.0") +set(SLIC3R_RC_VERSION "2,6,2,0") +set(SLIC3R_RC_VERSION_DOTS "2.6.2.0") From 259fc0ff4b24f6e2f66091d11d3183f2d60bf4c4 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 28 Aug 2023 19:21:08 +0200 Subject: [PATCH 076/136] Setting the binary G-code default compression. --- src/libslic3r/GCode/GCodeProcessor.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 232c59b7db..aeefa72e83 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -69,7 +69,18 @@ const float GCodeProcessor::Wipe_Width = 0.05f; const float GCodeProcessor::Wipe_Height = 0.05f; #if ENABLE_BINARIZED_GCODE -bgcode::binarize::BinarizerConfig GCodeProcessor::s_binarizer_config{}; +bgcode::binarize::BinarizerConfig GCodeProcessor::s_binarizer_config{ + { + bgcode::core::ECompressionType::None, // file metadata + bgcode::core::ECompressionType::None, // printer metadata + bgcode::core::ECompressionType::Deflate, // print metadata + bgcode::core::ECompressionType::Deflate, // slicer metadata + bgcode::core::ECompressionType::Heatshrink_12_4, // gcode + }, + bgcode::core::EGCodeEncodingType::MeatPackComments, + bgcode::core::EMetadataEncodingType::INI, + bgcode::core::EChecksumType::CRC32 +}; #endif // ENABLE_BINARIZED_GCODE #if ENABLE_GCODE_VIEWER_DATA_CHECKING From a6dea252432eeb34153d5ab767b1a9d315926ee3 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 14 Aug 2023 16:50:53 +0200 Subject: [PATCH 077/136] WIP: Thumbnails improvements: Merge "thumbnails" and "thumbnails_format" options into just one "thumbnails" option with coString type --- src/libslic3r/GCode.cpp | 39 ++++-- src/libslic3r/GCode/Thumbnails.hpp | 44 +++---- src/libslic3r/PrintConfig.cpp | 6 +- src/libslic3r/PrintConfig.hpp | 2 +- src/slic3r/GUI/Field.cpp | 160 +++++++++++++++--------- src/slic3r/GUI/Field.hpp | 2 +- src/slic3r/GUI/OptionsGroup.cpp | 12 +- src/slic3r/GUI/Tab.cpp | 16 ++- src/slic3r/GUI/UnsavedChangesDialog.cpp | 7 +- 9 files changed, 189 insertions(+), 99 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 35d1386ac1..debe3e63b9 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -886,14 +886,37 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail // Write information on the generator. file.write_format("; %s\n\n", Slic3r::header_slic3r_generated().c_str()); - // Unit tests or command line slicing may not define "thumbnails" or "thumbnails_format". - // If "thumbnails_format" is not defined, export to PNG. - if (const auto [thumbnails, thumbnails_format] = std::make_pair( - print.full_print_config().option("thumbnails"), - print.full_print_config().option>("thumbnails_format")); - thumbnails) - GCodeThumbnails::export_thumbnails_to_file( - thumbnail_cb, thumbnails->values, thumbnails_format ? thumbnails_format->value : GCodeThumbnailsFormat::PNG, + // ??? Unit tests or command line slicing may not define "thumbnails" or "thumbnails_format". + // ??? If "thumbnails_format" is not defined, export to PNG. + + // generate thumbnails data to process it + + std::vector> thumbnails_list; + if (const auto thumbnails_value = print.full_print_config().option("thumbnails")) { + std::string str = thumbnails_value->value; + std::istringstream is(str); + std::string point_str; + while (std::getline(is, point_str, ',')) { + Vec2d point(Vec2d::Zero()); + GCodeThumbnailsFormat format; + std::istringstream iss(point_str); + std::string coord_str; + if (std::getline(iss, coord_str, 'x')) { + std::istringstream(coord_str) >> point(0); + if (std::getline(iss, coord_str, '/')) { + std::istringstream(coord_str) >> point(1); + std::string ext_str; + if (std::getline(iss, ext_str, '/')) + format = ext_str == "JPG" ? GCodeThumbnailsFormat::JPG : + ext_str == "QOI" ? GCodeThumbnailsFormat::QOI :GCodeThumbnailsFormat::PNG; + } + } + thumbnails_list.emplace_back(std::make_pair(format, point)); + } + } + + if (!thumbnails_list.empty()) + GCodeThumbnails::export_thumbnails_to_file(thumbnail_cb, thumbnails_list, [&file](const char* sz) { file.write(sz); }, [&print]() { print.throw_if_canceled(); }); diff --git a/src/libslic3r/GCode/Thumbnails.hpp b/src/libslic3r/GCode/Thumbnails.hpp index 30bb6b653b..68b11d15fb 100644 --- a/src/libslic3r/GCode/Thumbnails.hpp +++ b/src/libslic3r/GCode/Thumbnails.hpp @@ -24,34 +24,36 @@ struct CompressedImageBuffer std::unique_ptr compress_thumbnail(const ThumbnailData &data, GCodeThumbnailsFormat format); template -inline void export_thumbnails_to_file(ThumbnailsGeneratorCallback &thumbnail_cb, const std::vector &sizes, GCodeThumbnailsFormat format, WriteToOutput output, ThrowIfCanceledCallback throw_if_canceled) +inline void export_thumbnails_to_file(ThumbnailsGeneratorCallback &thumbnail_cb, const std::vector>& thumbnails_list, WriteToOutput output, ThrowIfCanceledCallback throw_if_canceled) { // Write thumbnails using base64 encoding if (thumbnail_cb != nullptr) { - static constexpr const size_t max_row_length = 78; - ThumbnailsList thumbnails = thumbnail_cb(ThumbnailsParams{ sizes, true, true, true, true }); - for (const ThumbnailData& data : thumbnails) - if (data.is_valid()) { - auto compressed = compress_thumbnail(data, format); - if (compressed->data && compressed->size) { - std::string encoded; - encoded.resize(boost::beast::detail::base64::encoded_size(compressed->size)); - encoded.resize(boost::beast::detail::base64::encode((void*)encoded.data(), (const void*)compressed->data, compressed->size)); + for (const auto& [format, size] : thumbnails_list) { + static constexpr const size_t max_row_length = 78; + ThumbnailsList thumbnails = thumbnail_cb(ThumbnailsParams{ {size}, true, true, true, true }); + for (const ThumbnailData& data : thumbnails) + if (data.is_valid()) { + auto compressed = compress_thumbnail(data, format); + if (compressed->data && compressed->size) { + std::string encoded; + encoded.resize(boost::beast::detail::base64::encoded_size(compressed->size)); + encoded.resize(boost::beast::detail::base64::encode((void*)encoded.data(), (const void*)compressed->data, compressed->size)); - output((boost::format("\n;\n; %s begin %dx%d %d\n") % compressed->tag() % data.width % data.height % encoded.size()).str().c_str()); + output((boost::format("\n;\n; %s begin %dx%d %d\n") % compressed->tag() % data.width % data.height % encoded.size()).str().c_str()); - while (encoded.size() > max_row_length) { - output((boost::format("; %s\n") % encoded.substr(0, max_row_length)).str().c_str()); - encoded = encoded.substr(max_row_length); + while (encoded.size() > max_row_length) { + output((boost::format("; %s\n") % encoded.substr(0, max_row_length)).str().c_str()); + encoded = encoded.substr(max_row_length); + } + + if (encoded.size() > 0) + output((boost::format("; %s\n") % encoded).str().c_str()); + + output((boost::format("; %s end\n;\n") % compressed->tag()).str().c_str()); } - - if (encoded.size() > 0) - output((boost::format("; %s\n") % encoded).str().c_str()); - - output((boost::format("; %s end\n;\n") % compressed->tag()).str().c_str()); + throw_if_canceled(); } - throw_if_canceled(); - } + } } } diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 8f6057f904..334eb9bb82 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -285,12 +285,12 @@ void PrintConfigDef::init_common_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloat(0.)); - def = this->add("thumbnails", coPoints); + def = this->add("thumbnails", coString); def->label = L("G-code thumbnails"); - def->tooltip = L("Picture sizes to be stored into a .gcode and .sl1 / .sl1s files, in the following format: \"XxY, XxY, ...\""); + def->tooltip = L("Picture sizes to be stored into a .gcode and .sl1 / .sl1s files, in the following format: \"XxYxEXT, XxYxEXT, ...\""); def->mode = comExpert; def->gui_type = ConfigOptionDef::GUIType::one_string; - def->set_default_value(new ConfigOptionPoints()); + def->set_default_value(new ConfigOptionString()); def = this->add("thumbnails_format", coEnum); def->label = L("Format of G-code thumbnails"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 59964c0b8a..a2a4466045 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -835,7 +835,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE( ((ConfigOptionInt, standby_temperature_delta)) ((ConfigOptionInts, temperature)) ((ConfigOptionInt, threads)) - ((ConfigOptionPoints, thumbnails)) + ((ConfigOptionString, thumbnails)) ((ConfigOptionEnum, thumbnails_format)) ((ConfigOptionFloat, top_solid_infill_acceleration)) ((ConfigOptionFloat, travel_acceleration)) diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index 121493b68b..bbc2b04dd2 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -8,6 +8,7 @@ #include "format.hpp" #include "libslic3r/PrintConfig.hpp" +#include "libslic3r/enum_bitmask.hpp" #include #include @@ -26,7 +27,13 @@ #define wxOSX false #endif -namespace Slic3r { namespace GUI { +namespace Slic3r { + +enum class ThumbnailError : int { InvalidVal, OutOfRange, InvalidExt }; +using ThumbnailErrors = enum_bitmask; +ENABLE_ENUM_BITMASK_OPERATORS(ThumbnailError); + +namespace GUI { wxString double_to_string(double const value, const int max_precision /*= 4*/) { @@ -58,14 +65,79 @@ wxString double_to_string(double const value, const int max_precision /*= 4*/) return s; } -wxString get_thumbnails_string(const std::vector& values) +bool is_valid_thumbnails_extention(wxString& ext) { - wxString ret_str; - for (size_t i = 0; i < values.size(); ++ i) { - const Vec2d& el = values[i]; - ret_str += wxString::Format((i == 0) ? "%ix%i" : ", %ix%i", int(el[0]), int(el[1])); - } - return ret_str; + ext.UpperCase(); + static const std::vector extentions = { "PNG", "JPG", "QOI" }; + + return std::find(extentions.begin(), extentions.end(), ext) != extentions.end(); +} + +ThumbnailErrors validate_thumbnails_string(wxString& str, const wxString& def_ext = "PNG") +{ + bool invalid_val, out_of_range_val, invalid_ext; + invalid_val = out_of_range_val = invalid_ext = false; + str.Replace(" ", wxEmptyString, true); + + if (!str.IsEmpty()) { + + std::vector> out_thumbnails; + + wxStringTokenizer thumbnails(str, ","); + while (thumbnails.HasMoreTokens()) { + wxString token = thumbnails.GetNextToken(); + double x, y; + wxStringTokenizer thumbnail(token, "x"); + if (thumbnail.HasMoreTokens()) { + wxString x_str = thumbnail.GetNextToken(); + if (x_str.ToDouble(&x) && thumbnail.HasMoreTokens()) { + wxStringTokenizer y_and_ext(thumbnail.GetNextToken(), "/"); + + wxString y_str = y_and_ext.GetNextToken(); + if (y_str.ToDouble(&y)) { + // thumbnail has no extension + if (0 < x && x < 1000 && 0 < y && y < 1000) { + wxString ext = y_and_ext.HasMoreTokens() ? y_and_ext.GetNextToken() : def_ext; + bool is_valid_ext = is_valid_thumbnails_extention(ext); + invalid_ext |= !is_valid_ext; + out_thumbnails.push_back({ Vec2d(x, y), into_u8(is_valid_ext ? ext : def_ext) }); + continue; + } + out_of_range_val |= true; + continue; + } + } + } + invalid_val |= true; + } + + str.Clear(); + for (const auto& [size, ext] : out_thumbnails) + str += format_wxstr("%1%x%2%/%3%, ", size.x(), size.y(), ext); + str.resize(str.Len()-2); + } + + ThumbnailErrors errors = only_if(invalid_val, ThumbnailError::InvalidVal) | + only_if(invalid_ext, ThumbnailError::InvalidExt) | + only_if(out_of_range_val, ThumbnailError::OutOfRange); + + return errors; +} + +wxString get_valid_thumbnails_string(const DynamicPrintConfig& config) +{ + // >>> ysFIXME - temporary code, till "thumbnails_format" options exists in config + wxString format = "PNG"; + if (const ConfigOptionDef* opt = config.def()->get("thumbnails_format")) + if (auto label = opt->enum_def->enum_to_label(config.option("thumbnails_format")->getInt()); + label.has_value()) + format = from_u8(*label); + // <<< + + wxString str = from_u8(config.opt_string("thumbnails")); + validate_thumbnails_string(str, format); + + return str; } @@ -355,56 +427,35 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true } } - m_value = into_u8(str); - break; } - - case coPoints: { - std::vector out_values; - str.Replace(" ", wxEmptyString, true); - if (!str.IsEmpty()) { - bool invalid_val = false; - bool out_of_range_val = false; - wxStringTokenizer thumbnails(str, ","); - while (thumbnails.HasMoreTokens()) { - wxString token = thumbnails.GetNextToken(); - double x, y; - wxStringTokenizer thumbnail(token, "x"); - if (thumbnail.HasMoreTokens()) { - wxString x_str = thumbnail.GetNextToken(); - if (x_str.ToDouble(&x) && thumbnail.HasMoreTokens()) { - wxString y_str = thumbnail.GetNextToken(); - if (y_str.ToDouble(&y) && !thumbnail.HasMoreTokens()) { - if (0 < x && x < 1000 && 0 < y && y < 1000) { - out_values.push_back(Vec2d(x, y)); - continue; - } - out_of_range_val = true; - break; - } - } + if (m_opt.opt_key == "thumbnails") { + wxString str_out = str; + ThumbnailErrors errors = validate_thumbnails_string(str_out); + if (errors != enum_bitmask()) { + set_value(str_out, true); + wxString error_str; + if (errors.has(ThumbnailError::InvalidVal)) + error_str += format_wxstr(_L("Invalid input format. Expected vector of dimensions in the following format: \"%1%\""), "XxYxEXT, XxYxEXT, ..."); + if (errors.has(ThumbnailError::OutOfRange)) { + if (!error_str.empty()) + error_str += "\n\n"; + error_str += _L("Input value is out of range"); } - invalid_val = true; - break; + if (errors.has(ThumbnailError::InvalidExt)) { + if (!error_str.empty()) + error_str += "\n\n"; + error_str += _L("Some input extention is invalid"); + } + show_error(m_parent, error_str); } - - if (out_of_range_val) { - wxString text_value; - if (!m_value.empty()) - text_value = get_thumbnails_string(boost::any_cast>(m_value)); - set_value(text_value, true); - show_error(m_parent, _L("Input value is out of range")); - } - else if (invalid_val) { - wxString text_value; - if (!m_value.empty()) - text_value = get_thumbnails_string(boost::any_cast>(m_value)); - set_value(text_value, true); - show_error(m_parent, format_wxstr(_L("Invalid input format. Expected vector of dimensions in the following format: \"%1%\""),"XxY, XxY, ..." )); + else if (str_out != str) { + str = str_out; + set_value(str, true); } } - m_value = out_values; - break; } + m_value = into_u8(str); + break; + } default: break; @@ -484,9 +535,6 @@ void TextCtrl::BUILD() { text_value = vec->get_at(m_opt_idx); break; } - case coPoints: - text_value = get_thumbnails_string(m_opt.get_default_value()->values); - break; default: break; } diff --git a/src/slic3r/GUI/Field.hpp b/src/slic3r/GUI/Field.hpp index 73af0ef53d..704e0900b3 100644 --- a/src/slic3r/GUI/Field.hpp +++ b/src/slic3r/GUI/Field.hpp @@ -37,7 +37,7 @@ using t_change = std::function; wxString double_to_string(double const value, const int max_precision = 4); -wxString get_thumbnails_string(const std::vector& values); +wxString get_valid_thumbnails_string(const DynamicPrintConfig& config); class UndoValueUIManager { diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index 614fda9130..3fda23323b 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -904,9 +904,13 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config ret = double_to_string(val); } break; - case coString: - ret = from_u8(config.opt_string(opt_key)); - break; + case coString: { + if (opt_key == "thumbnails") + ret = get_valid_thumbnails_string(config); + else + ret = from_u8(config.opt_string(opt_key)); + break; + } case coStrings: if (opt_key == "compatible_printers" || opt_key == "compatible_prints" || opt_key == "gcode_substitutions") { ret = config.option(opt_key)->values; @@ -946,8 +950,6 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config case coPoints: if (opt_key == "bed_shape") ret = config.option(opt_key)->values; - else if (opt_key == "thumbnails") - ret = get_thumbnails_string(config.option(opt_key)->values); else ret = config.option(opt_key)->get_at(idx); break; diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 5bbc2fa5d8..94a2960679 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -636,6 +636,21 @@ void Tab::update_changed_ui() if (tab->m_sys_extruders_count != tab->m_extruders_count) nonsys_options.emplace_back("extruders_count"); } + + // "thumbnails" can not containe a extentions in old config but are valid and use PNG extention by default + // So, check if "thumbnails" is really changed + // We will compare full strings for thumnails instead of exactly config values + { + auto check_thumbnails_option = [](std::vector& keys, const DynamicPrintConfig& config, const DynamicPrintConfig& config_new) { + if (auto it = std::find(keys.begin(), keys.end(), "thumbnails"); it != keys.end()) + if (get_valid_thumbnails_string(config) == get_valid_thumbnails_string(config_new)) + // if those strings are actually the same, erase them from the list of dirty oprions + keys.erase(it); + }; + check_thumbnails_option(dirty_options, m_presets->get_edited_preset().config, m_presets->get_selected_preset().config); + if (const Preset* parent_preset = m_presets->get_selected_preset_parent()) + check_thumbnails_option(nonsys_options, m_presets->get_edited_preset().config, parent_preset->config); + } } for (auto& it : m_options_list) @@ -2526,7 +2541,6 @@ void TabPrinter::build_fff() option = optgroup->get_option("thumbnails"); option.opt.full_width = true; optgroup->append_single_option_line(option); - optgroup->append_single_option_line("thumbnails_format"); optgroup->append_single_option_line("silent_mode"); optgroup->append_single_option_line("remaining_times"); diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index 5fe036d467..18eacaa153 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -1154,8 +1154,11 @@ static wxString get_string_value(std::string opt_key, const DynamicPrintConfig& } return _L("Undef"); } - case coString: + case coString: { + if (opt_key == "thumbnails") + return get_valid_thumbnails_string(config); return from_u8(config.opt_string(opt_key)); + } case coStrings: { const ConfigOptionStrings* strings = config.opt(opt_key); if (strings) { @@ -1204,8 +1207,6 @@ static wxString get_string_value(std::string opt_key, const DynamicPrintConfig& BedShape shape(*config.option(opt_key)); return shape.get_full_name_with_params(); } - if (opt_key == "thumbnails") - return get_thumbnails_string(config.option(opt_key)->values); Vec2d val = config.opt(opt_key)->get_at(opt_idx); return from_u8((boost::format("[%1%]") % ConfigOptionPoint(val).serialize()).str()); From 2df6f290e769a57afa1cdab3d559a77ee5328b1d Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 29 Aug 2023 10:55:25 +0200 Subject: [PATCH 078/136] Try to fix build of perl bindings on Linux --- deps/LibBGCode/LibBGCode.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/deps/LibBGCode/LibBGCode.cmake b/deps/LibBGCode/LibBGCode.cmake index cf90266678..e51bae4f49 100644 --- a/deps/LibBGCode/LibBGCode.cmake +++ b/deps/LibBGCode/LibBGCode.cmake @@ -15,6 +15,7 @@ prusaslicer_add_cmake_project(LibBGCode_deps DEPENDS dep_Boost ${ZLIB_PKG} CMAKE_ARGS -DDEP_DOWNLOAD_DIR:PATH=${DEP_DOWNLOAD_DIR} + -DDEP_CMAKE_OPTS:STRING="-DCMAKE_POSITION_INDEPENDENT_CODE=ON" -DLibBGCode_Deps_SELECT_ALL:BOOL=OFF -DLibBGCode_Deps_SELECT_heatshrink:BOOL=ON -DDESTDIR=${DESTDIR} From 7bf1189c697fadd3cfdf082c17489a6899fd1ddf Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 29 Aug 2023 11:15:55 +0200 Subject: [PATCH 079/136] Follow up fix, second try --- deps/LibBGCode/LibBGCode.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/LibBGCode/LibBGCode.cmake b/deps/LibBGCode/LibBGCode.cmake index e51bae4f49..076fe290a2 100644 --- a/deps/LibBGCode/LibBGCode.cmake +++ b/deps/LibBGCode/LibBGCode.cmake @@ -15,7 +15,7 @@ prusaslicer_add_cmake_project(LibBGCode_deps DEPENDS dep_Boost ${ZLIB_PKG} CMAKE_ARGS -DDEP_DOWNLOAD_DIR:PATH=${DEP_DOWNLOAD_DIR} - -DDEP_CMAKE_OPTS:STRING="-DCMAKE_POSITION_INDEPENDENT_CODE=ON" + -DDEP_CMAKE_OPTS:STRING=-DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=ON -DLibBGCode_Deps_SELECT_ALL:BOOL=OFF -DLibBGCode_Deps_SELECT_heatshrink:BOOL=ON -DDESTDIR=${DESTDIR} From 7db83d58f0af11efaacdc732641531dee739563b Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 31 May 2023 11:17:06 +0200 Subject: [PATCH 080/136] Editing of Custom G-code: Prepared UI part on SettingsTabs * Added "Edit" button for all custom G-codes --- src/slic3r/GUI/Field.cpp | 6 +++++ src/slic3r/GUI/Field.hpp | 41 +++++++++++++++++++++++++++++ src/slic3r/GUI/OG_CustomCtrl.cpp | 44 ++++++++++++++++++++++++++------ src/slic3r/GUI/OG_CustomCtrl.hpp | 4 ++- src/slic3r/GUI/OptionsGroup.cpp | 8 ++++++ src/slic3r/GUI/OptionsGroup.hpp | 2 ++ src/slic3r/GUI/Tab.cpp | 29 +++++++++++++++++++++ src/slic3r/GUI/Tab.hpp | 3 +++ 8 files changed, 128 insertions(+), 9 deletions(-) diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index bbc2b04dd2..a2f72eaead 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -245,6 +245,12 @@ void Field::on_back_to_sys_value() m_back_to_sys_value(m_opt_id); } +void Field::on_edit_value() +{ + if (m_fn_edit_value) + m_fn_edit_value(m_opt_id); +} + wxString Field::get_tooltip_text(const wxString& default_string) { if (m_opt.tooltip.empty()) diff --git a/src/slic3r/GUI/Field.hpp b/src/slic3r/GUI/Field.hpp index 704e0900b3..6ab464f2e2 100644 --- a/src/slic3r/GUI/Field.hpp +++ b/src/slic3r/GUI/Field.hpp @@ -95,6 +95,30 @@ class UndoValueUIManager UndoValueUI m_undo_ui; + struct EditValueUI { + // Bitmap and Tooltip text for m_Edit_btn. The wxButton will be updated only if the new wxBitmap pointer differs from the currently rendered one. + const ScalableBitmap* bitmap{ nullptr }; + wxString tooltip { wxEmptyString }; + + bool set_bitmap(const ScalableBitmap* bmp) { + if (bitmap != bmp) { + bitmap = bmp; + return true; + } + return false; + } + + bool set_tooltip(const wxString& tip) { + if (tooltip != tip) { + tooltip = tip; + return true; + } + return false; + } + }; + + EditValueUI m_edit_ui; + public: UndoValueUIManager() {} ~UndoValueUIManager() {} @@ -105,6 +129,9 @@ public: bool set_undo_tooltip(const wxString* tip) { return m_undo_ui.set_undo_tooltip(tip); } bool set_undo_to_sys_tooltip(const wxString* tip) { return m_undo_ui.set_undo_to_sys_tooltip(tip); } + bool set_edit_bitmap(const ScalableBitmap* bmp) { return m_edit_ui.set_bitmap(bmp); } + bool set_edit_tooltip(const wxString& tip) { return m_edit_ui.set_tooltip(tip); } + // ui items used for revert line value bool has_undo_ui() const { return m_undo_ui.undo_bitmap != nullptr; } const wxBitmapBundle& undo_bitmap() const { return m_undo_ui.undo_bitmap->bmp(); } @@ -112,8 +139,17 @@ public: const wxBitmapBundle& undo_to_sys_bitmap() const { return m_undo_ui.undo_to_sys_bitmap->bmp(); } const wxString* undo_to_sys_tooltip() const { return m_undo_ui.undo_to_sys_tooltip; } const wxColour* label_color() const { return m_undo_ui.label_color; } + + // Extentions + + // Search blinker const bool blink() const { return m_undo_ui.blink; } bool* get_blink_ptr() { return &m_undo_ui.blink; } + + // Edit field button + bool has_edit_ui() const { return !m_edit_ui.tooltip.IsEmpty(); } + const wxBitmapBundle* edit_bitmap() const { return &m_edit_ui.bitmap->bmp(); } + const wxString* edit_tooltip() const { return &m_edit_ui.tooltip; } }; @@ -147,6 +183,8 @@ public: void on_back_to_initial_value(); /// Call the attached m_back_to_sys_value method. void on_back_to_sys_value(); + /// Call the attached m_fn_edit_value method. + void on_edit_value(); public: /// parent wx item, opportunity to refactor (probably not necessary - data duplication) @@ -162,6 +200,9 @@ public: t_back_to_init m_back_to_initial_value{ nullptr }; t_back_to_init m_back_to_sys_value{ nullptr }; + /// Callback function to edit field value + t_back_to_init m_fn_edit_value{ nullptr }; + // This is used to avoid recursive invocation of the field change/update by wxWidgets. bool m_disable_change_event {false}; bool m_is_modified_value {false}; diff --git a/src/slic3r/GUI/OG_CustomCtrl.cpp b/src/slic3r/GUI/OG_CustomCtrl.cpp index a3cb345b42..ebd9de9464 100644 --- a/src/slic3r/GUI/OG_CustomCtrl.cpp +++ b/src/slic3r/GUI/OG_CustomCtrl.cpp @@ -278,6 +278,11 @@ void OG_CustomCtrl::OnMotion(wxMouseEvent& event) tooltip = *field->undo_to_sys_tooltip(); break; } + if (opt_idx < line.rects_edit_icon.size() && is_point_in_rect(pos, line.rects_edit_icon[opt_idx])) { + if (Field* field = opt_group->get_field(opt_key); field && field->has_edit_ui()) + tooltip = *field->edit_tooltip(); + break; + } } if (!tooltip.IsEmpty()) break; @@ -327,6 +332,13 @@ void OG_CustomCtrl::OnLeftDown(wxMouseEvent& event) event.Skip(); return; } + + if (opt_idx < line.rects_edit_icon.size() && is_point_in_rect(pos, line.rects_edit_icon[opt_idx])) { + if (Field* field = opt_group->get_field(opt_key)) + field->on_edit_value(); + event.Skip(); + return; + } } } @@ -565,17 +577,21 @@ void OG_CustomCtrl::CtrlLine::render(wxDC& dc, wxCoord v_pos) return; } + wxCoord h_pos = draw_mode_bmp(dc, v_pos); + Field* field = ctrl->opt_group->get_field(og_line.get_options().front().opt_id); const bool suppress_hyperlinks = get_app_config()->get_bool("suppress_hyperlinks"); if (draw_just_act_buttons) { - if (field) - draw_act_bmps(dc, wxPoint(0, v_pos), field->undo_to_sys_bitmap(), field->undo_bitmap(), field->blink()); + if (field) { + const wxPoint pos = draw_act_bmps(dc, wxPoint(h_pos, v_pos), field->undo_to_sys_bitmap(), field->undo_bitmap(), field->blink()); + // Add edit button, if it exists + if (field->has_edit_ui()) + draw_edit_bmp(dc, pos, field->edit_bitmap()); + } return; } - wxCoord h_pos = draw_mode_bmp(dc, v_pos); - if (og_line.near_label_widget_win) h_pos += og_line.near_label_widget_win->GetSize().x + ctrl->m_h_gap; @@ -606,7 +622,7 @@ void OG_CustomCtrl::CtrlLine::render(wxDC& dc, wxCoord v_pos) option_set.front().side_widget == nullptr && og_line.get_extra_widgets().size() == 0) { if (field && field->has_undo_ui()) - h_pos = draw_act_bmps(dc, wxPoint(h_pos, v_pos), field->undo_to_sys_bitmap(), field->undo_bitmap(), field->blink()) + ctrl->m_h_gap; + h_pos = draw_act_bmps(dc, wxPoint(h_pos, v_pos), field->undo_to_sys_bitmap(), field->undo_bitmap(), field->blink()).x + ctrl->m_h_gap; else if (field && !field->has_undo_ui() && field->blink()) draw_blinking_bmp(dc, wxPoint(h_pos, v_pos), field->blink()); // update width for full_width fields @@ -635,7 +651,7 @@ void OG_CustomCtrl::CtrlLine::render(wxDC& dc, wxCoord v_pos) } if (field && field->has_undo_ui()) { - h_pos = draw_act_bmps(dc, wxPoint(h_pos, v_pos), field->undo_to_sys_bitmap(), field->undo_bitmap(), field->blink(), bmp_rect_id++); + h_pos = draw_act_bmps(dc, wxPoint(h_pos, v_pos), field->undo_to_sys_bitmap(), field->undo_bitmap(), field->blink(), bmp_rect_id++).x; if (field->getSizer()) { auto children = field->getSizer()->GetChildren(); @@ -746,7 +762,7 @@ wxPoint OG_CustomCtrl::CtrlLine::draw_blinking_bmp(wxDC& dc, wxPoint pos, bool i return wxPoint(h_pos, v_pos); } -wxCoord OG_CustomCtrl::CtrlLine::draw_act_bmps(wxDC& dc, wxPoint pos, const wxBitmapBundle& bmp_undo_to_sys, const wxBitmapBundle& bmp_undo, bool is_blinking, size_t rect_id) +wxPoint OG_CustomCtrl::CtrlLine::draw_act_bmps(wxDC& dc, wxPoint pos, const wxBitmapBundle& bmp_undo_to_sys, const wxBitmapBundle& bmp_undo, bool is_blinking, size_t rect_id) { pos = draw_blinking_bmp(dc, pos, is_blinking); wxCoord h_pos = pos.x; @@ -765,7 +781,19 @@ wxCoord OG_CustomCtrl::CtrlLine::draw_act_bmps(wxDC& dc, wxPoint pos, const wxBi h_pos += bmp_dim + ctrl->m_h_gap; - return h_pos; + return wxPoint(h_pos, v_pos); +} + +wxCoord OG_CustomCtrl::CtrlLine::draw_edit_bmp(wxDC &dc, wxPoint pos, const wxBitmapBundle *bmp_edit) +{ + const wxCoord h_pos = pos.x + ctrl->m_h_gap; + const wxCoord v_pos = pos.y; + const int bmp_w = get_bitmap_size(bmp_edit, ctrl).GetWidth(); + rects_edit_icon.emplace_back(wxRect(h_pos, v_pos, bmp_w, bmp_w)); + + dc.DrawBitmap(bmp_edit->GetBitmapFor(ctrl), h_pos, v_pos); + + return h_pos + bmp_w + ctrl->m_h_gap; } bool OG_CustomCtrl::CtrlLine::launch_browser() const diff --git a/src/slic3r/GUI/OG_CustomCtrl.hpp b/src/slic3r/GUI/OG_CustomCtrl.hpp index 269f847a22..0d7cb62042 100644 --- a/src/slic3r/GUI/OG_CustomCtrl.hpp +++ b/src/slic3r/GUI/OG_CustomCtrl.hpp @@ -62,12 +62,14 @@ class OG_CustomCtrl :public wxPanel wxCoord draw_mode_bmp(wxDC& dc, wxCoord v_pos); wxCoord draw_text (wxDC& dc, wxPoint pos, const wxString& text, const wxColour* color, int width, bool is_url = false); wxPoint draw_blinking_bmp(wxDC& dc, wxPoint pos, bool is_blinking); - wxCoord draw_act_bmps(wxDC& dc, wxPoint pos, const wxBitmapBundle& bmp_undo_to_sys, const wxBitmapBundle& bmp_undo, bool is_blinking, size_t rect_id = 0); + wxPoint draw_act_bmps(wxDC& dc, wxPoint pos, const wxBitmapBundle& bmp_undo_to_sys, const wxBitmapBundle& bmp_undo, bool is_blinking, size_t rect_id = 0); + wxCoord draw_edit_bmp(wxDC& dc, wxPoint pos, const wxBitmapBundle* bmp_edit); bool launch_browser() const; bool is_separator() const { return og_line.is_separator(); } std::vector rects_undo_icon; std::vector rects_undo_to_sys_icon; + std::vector rects_edit_icon; wxRect rect_label; }; diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index 3fda23323b..625cbbbba4 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -96,6 +96,14 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co }; field->m_parent = parent(); + if (edit_custom_gcode && opt.is_code) { + field->m_fn_edit_value = [this](std::string opt_id) { + if (!m_disabled) + this->edit_custom_gcode(opt_id); + }; + field->set_edit_tooltip(_L("Edit CustomG-code")); + } + field->m_back_to_initial_value = [this](std::string opt_id) { if (!m_disabled) this->back_to_initial_value(opt_id); diff --git a/src/slic3r/GUI/OptionsGroup.hpp b/src/slic3r/GUI/OptionsGroup.hpp index f754505dd4..0df7c753fe 100644 --- a/src/slic3r/GUI/OptionsGroup.hpp +++ b/src/slic3r/GUI/OptionsGroup.hpp @@ -121,6 +121,8 @@ public: std::function rescale_extra_column_item { nullptr }; std::function rescale_near_label_widget { nullptr }; + + std::function edit_custom_gcode { nullptr }; wxFont sidetext_font {wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) }; wxFont label_font {wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) }; diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 94a2960679..aef40170b7 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -188,6 +188,8 @@ void Tab::create_preset_tab() // Bitmaps to be shown on the "Undo user changes" button next to each input field. add_scaled_bitmap(this, m_bmp_value_revert, "undo"); add_scaled_bitmap(this, m_bmp_white_bullet, "dot"); + // Bitmap to be shown on the "edit" button before to each editable input field. + add_scaled_bitmap(this, m_bmp_edit_value, "edit"); fill_icon_descriptions(); set_tooltips_text(); @@ -601,6 +603,10 @@ void Tab::decorate() field->set_undo_tooltip(tt); field->set_undo_to_sys_tooltip(sys_tt); field->set_label_colour(color); + + if (field->has_edit_ui()) + field->set_edit_bitmap(&m_bmp_edit_value); + } if (m_active_page) @@ -1852,6 +1858,11 @@ static void validate_custom_gcode_cb(Tab* tab, const wxString& title, const t_co tab->on_value_change(opt_key, value); } +void Tab::edit_custom_gcode(const t_config_option_key& opt_key) +{ + MessageDialog(this, format_wxstr("Edit gcode for %1%", opt_key), this->title()).ShowModal(); +} + void TabFilament::create_line_with_near_label_widget(ConfigOptionsGroupShp optgroup, const std::string& opt_key, int opt_index/* = 0*/) { Line line {"",""}; @@ -2179,6 +2190,7 @@ void TabFilament::build() optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { validate_custom_gcode_cb(this, optgroup_title, opt_key, value); }; + optgroup->edit_custom_gcode = [this](const t_config_option_key& opt_key) { edit_custom_gcode(opt_key); }; option = optgroup->get_option("start_filament_gcode"); option.opt.full_width = true; option.opt.is_code = true; @@ -2189,6 +2201,7 @@ void TabFilament::build() optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { validate_custom_gcode_cb(this, optgroup_title, opt_key, value); }; + optgroup->edit_custom_gcode = [this](const t_config_option_key& opt_key) { edit_custom_gcode(opt_key); }; option = optgroup->get_option("end_filament_gcode"); option.opt.full_width = true; option.opt.is_code = true; @@ -2611,6 +2624,7 @@ void TabPrinter::build_fff() optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { validate_custom_gcode_cb(this, optgroup_title, opt_key, value); }; + optgroup->edit_custom_gcode = [this](const t_config_option_key& opt_key) { edit_custom_gcode(opt_key); }; option = optgroup->get_option("start_gcode"); option.opt.full_width = true; option.opt.is_code = true; @@ -2624,6 +2638,7 @@ void TabPrinter::build_fff() optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { validate_custom_gcode_cb(this, optgroup_title, opt_key, value); }; + optgroup->edit_custom_gcode = [this](const t_config_option_key& opt_key) { edit_custom_gcode(opt_key); }; option = optgroup->get_option("end_gcode"); option.opt.full_width = true; option.opt.is_code = true; @@ -2634,6 +2649,7 @@ void TabPrinter::build_fff() optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { validate_custom_gcode_cb(this, optgroup_title, opt_key, value); }; + optgroup->edit_custom_gcode = [this](const t_config_option_key& opt_key) { edit_custom_gcode(opt_key); }; option = optgroup->get_option("before_layer_gcode"); option.opt.full_width = true; option.opt.is_code = true; @@ -2644,6 +2660,7 @@ void TabPrinter::build_fff() optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { validate_custom_gcode_cb(this, optgroup_title, opt_key, value); }; + optgroup->edit_custom_gcode = [this](const t_config_option_key& opt_key) { edit_custom_gcode(opt_key); }; option = optgroup->get_option("layer_gcode"); option.opt.full_width = true; option.opt.is_code = true; @@ -2654,6 +2671,7 @@ void TabPrinter::build_fff() optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { validate_custom_gcode_cb(this, optgroup_title, opt_key, value); }; + optgroup->edit_custom_gcode = [this](const t_config_option_key& opt_key) { edit_custom_gcode(opt_key); }; option = optgroup->get_option("toolchange_gcode"); option.opt.full_width = true; option.opt.is_code = true; @@ -2664,6 +2682,7 @@ void TabPrinter::build_fff() optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { validate_custom_gcode_cb(this, optgroup_title, opt_key, value); }; + optgroup->edit_custom_gcode = [this](const t_config_option_key& opt_key) { edit_custom_gcode(opt_key); }; option = optgroup->get_option("between_objects_gcode"); option.opt.full_width = true; option.opt.is_code = true; @@ -2674,6 +2693,7 @@ void TabPrinter::build_fff() optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { validate_custom_gcode_cb(this, optgroup_title, opt_key, value); }; + optgroup->edit_custom_gcode = [this](const t_config_option_key& opt_key) { edit_custom_gcode(opt_key); }; option = optgroup->get_option("color_change_gcode"); option.opt.is_code = true; option.opt.height = gcode_field_height;//150; @@ -2683,6 +2703,7 @@ void TabPrinter::build_fff() optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { validate_custom_gcode_cb(this, optgroup_title, opt_key, value); }; + optgroup->edit_custom_gcode = [this](const t_config_option_key& opt_key) { edit_custom_gcode(opt_key); }; option = optgroup->get_option("pause_print_gcode"); option.opt.is_code = true; option.opt.height = gcode_field_height;//150; @@ -2692,6 +2713,7 @@ void TabPrinter::build_fff() optgroup->m_on_change = [this, &optgroup_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) { validate_custom_gcode_cb(this, optgroup_title, opt_key, value); }; + optgroup->edit_custom_gcode = [this](const t_config_option_key& opt_key) { edit_custom_gcode(opt_key); }; option = optgroup->get_option("template_custom_gcode"); option.opt.is_code = true; option.opt.height = gcode_field_height;//150; @@ -4823,6 +4845,13 @@ void Tab::fill_icon_descriptions() "the current option group.\n" "Click the BACK ARROW icon to reset all settings for the current option group to " "the last saved preset.")); + + m_icon_descriptions.emplace_back(&m_bmp_edit_value, L("EDIT VALUE"), + // TRN Description for "EDIT VALUE" + L("indicates that the settings were changed and are not equal to the last saved preset for " + "the current option group.\n" + "Click the BACK ARROW icon to reset all settings for the current option group to " + "the last saved preset.")); } void Tab::set_tooltips_text() diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index f0be456a5a..6ec714fc28 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -213,6 +213,8 @@ protected: ScalableBitmap *m_bmp_non_system; // Bitmaps to be shown on the "Undo user changes" button next to each input field. ScalableBitmap m_bmp_value_revert; + // Bitmaps to be shown on the "Undo user changes" button next to each input field. + ScalableBitmap m_bmp_edit_value; std::vector m_scaled_buttons = {}; std::vector m_scaled_bitmaps = {}; @@ -386,6 +388,7 @@ public: const std::map& get_category_icon_map() { return m_category_icon; } static bool validate_custom_gcode(const wxString& title, const std::string& gcode); + void edit_custom_gcode(const t_config_option_key &opt_key); bool validate_custom_gcodes(); bool validate_custom_gcodes_was_shown{ false }; From a8307bf8b5c4f4c1b0c45a1bf61987399ad155be Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 1 Jun 2023 17:16:16 +0200 Subject: [PATCH 081/136] Editing of Custom G-code: Added EditGCodeDialog : WIP --- src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/EditGCodeDialog.cpp | 155 +++++++++++++++++++++++++++++ src/slic3r/GUI/EditGCodeDialog.hpp | 47 +++++++++ src/slic3r/GUI/Tab.cpp | 8 +- src/slic3r/GUI/Tab.hpp | 5 +- 5 files changed, 215 insertions(+), 2 deletions(-) create mode 100644 src/slic3r/GUI/EditGCodeDialog.cpp create mode 100644 src/slic3r/GUI/EditGCodeDialog.hpp diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 3510fcb70a..5af4a7ed0f 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -109,6 +109,8 @@ set(SLIC3R_GUI_SOURCES GUI/PresetComboBoxes.cpp GUI/BitmapComboBox.hpp GUI/BitmapComboBox.cpp + GUI/EditGCodeDialog.hpp + GUI/EditGCodeDialog.cpp GUI/SavePresetDialog.hpp GUI/SavePresetDialog.cpp GUI/PhysicalPrinterDialog.hpp diff --git a/src/slic3r/GUI/EditGCodeDialog.cpp b/src/slic3r/GUI/EditGCodeDialog.cpp new file mode 100644 index 0000000000..45feb05019 --- /dev/null +++ b/src/slic3r/GUI/EditGCodeDialog.cpp @@ -0,0 +1,155 @@ +#include "EditGCodeDialog.hpp" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "GUI.hpp" +#include "GUI_App.hpp" +#include "MainFrame.hpp" +#include "format.hpp" +#include "Tab.hpp" +#include "wxExtensions.hpp" +#include "BitmapCache.hpp" +#include "MsgDialog.hpp" + +namespace Slic3r { +namespace GUI { + +static wxArrayString get_patterns_list() +{ + wxArrayString patterns; + for (const wxString& item : { + ";comment"//format_wxstr(";%1%",_L("comment")) + , "M862.3 P \"[printer_model]\" ; printer model check" + , "M862.1 P[nozzle_diameter]; nozzle diameter check" + , "M115 U3.12.2; tell printer latest fw version" + , "G90; use absolute coordinates" + , "M83; extruder relative mode" + , "M104 S[first_layer_temperature]; set extruder temp" + , "M140 S[first_layer_bed_temperature]; set bed temp" + , "M190 S[first_layer_bed_temperature]; wait for bed temp" + , "M109 S[first_layer_temperature]; wait for extruder temp" + , "G28 W; home all without mesh bed level" + , "G80; mesh bed leveling" + , "M403 E0 F {\n" + " + ((filament_type[0] == \"FLEX\") ? 1 : ((filament_type[0] == \"PVA\") ? 2 : 0))\n" + "}" + , "{if not OPTION}" + , "G1" + , "T[initial_tool]; select extruder" + , "G92 E0" + , "{endif}" + }) + patterns.Add(item); + return patterns; +} + +//------------------------------------------ +// EditGCodeDialog +//------------------------------------------ + +EditGCodeDialog::EditGCodeDialog(wxWindow* parent, const std::string& key, const std::string& value) : + DPIDialog(parent, wxID_ANY, format_wxstr(_L("Edit Custom G-code (%1%)"), key), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) +{ + SetFont(wxGetApp().normal_font()); + wxGetApp().UpdateDarkUI(this); + + int border = 10; + int em = em_unit(); + + wxStaticText* label_top = new wxStaticText(this, wxID_ANY, _L("Edit your custom G-code using patterns")); + label_top->SetFont(wxGetApp().bold_font()); + + auto* grid_sizer = new wxFlexGridSizer(1, 3, 5, 15); + grid_sizer->SetFlexibleDirection(wxBOTH); + + m_patterns_list = new wxListBox(this, wxID_ANY, wxDefaultPosition, wxSize(em * 15, em * 30), get_patterns_list(), wxLB_SINGLE | wxLB_NEEDED_SB | wxLB_SORT +#ifdef _WIN32 + | wxBORDER_SIMPLE +#endif + ); + m_patterns_list->SetFont(wxGetApp().code_font()); + wxGetApp().UpdateDarkUI(m_patterns_list); + + m_add_btn = new ScalableButton(this, wxID_ANY, "add_copies"); + m_add_btn->SetToolTip(_L("Add selected pettern to the G-code")); + + m_gcode_editor = new wxTextCtrl(this, wxID_ANY, value, wxDefaultPosition, wxSize(em * 45, em * 30), wxTE_MULTILINE +#ifdef _WIN32 + | wxBORDER_SIMPLE +#endif + ); + m_gcode_editor->SetFont(wxGetApp().code_font()); + wxGetApp().UpdateDarkUI(m_gcode_editor); + + grid_sizer->Add(m_patterns_list, 1, wxEXPAND); + grid_sizer->Add(m_add_btn, 0, wxALIGN_CENTER_VERTICAL); + grid_sizer->Add(m_gcode_editor, 2, wxEXPAND); + + grid_sizer->AddGrowableRow(0, 1); + grid_sizer->AddGrowableCol(0, 1); + grid_sizer->AddGrowableCol(2, 1); + + wxStdDialogButtonSizer* btns = this->CreateStdDialogButtonSizer(wxOK | wxCANCEL); + wxButton* btnOK = static_cast(this->FindWindowById(wxID_OK, this)); + wxGetApp().UpdateDarkUI(btnOK); + wxGetApp().UpdateDarkUI(static_cast(this->FindWindowById(wxID_CANCEL, this))); + + wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); + + topSizer->Add(label_top , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); + topSizer->Add(grid_sizer , 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); + topSizer->Add(btns , 0, wxEXPAND | wxALL, border); + + SetSizer(topSizer); + topSizer->SetSizeHints(this); + + this->Fit(); + this->Layout(); + + this->CenterOnScreen(); + + + m_patterns_list->Bind(wxEVT_LISTBOX_DCLICK, [this](wxCommandEvent& evt) { + wxString val = m_patterns_list->GetString(m_patterns_list->GetSelection()); + assert(!val.IsEmpty()); + auto insert_to = m_gcode_editor->GetInsertionPoint(); + m_gcode_editor->WriteText(val); + }); +} + +void EditGCodeDialog::on_dpi_changed(const wxRect&suggested_rect) +{ + const int& em = em_unit(); + + //m_optgroup->msw_rescale(); + + msw_buttons_rescale(this, em, { wxID_OK, wxID_CANCEL }); + + const wxSize& size = wxSize(45 * em, 35 * em); + SetMinSize(size); + + Fit(); + Refresh(); +} + +void EditGCodeDialog::on_sys_color_changed() +{ + m_add_btn->sys_color_changed(); +} + +void EditGCodeDialog::OnOK(wxEvent& event) +{ + + event.Skip(); +} + +}} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/EditGCodeDialog.hpp b/src/slic3r/GUI/EditGCodeDialog.hpp new file mode 100644 index 0000000000..eaf3948c85 --- /dev/null +++ b/src/slic3r/GUI/EditGCodeDialog.hpp @@ -0,0 +1,47 @@ +#ifndef slic3r_EditGCodeDialog_hpp_ +#define slic3r_EditGCodeDialog_hpp_ + +#include + +#include + +#include "GUI_Utils.hpp" + +class wxListBox; +class wxTextCtrl; +//class wxStaticText; +class ScalableButton; +//class wxBoxSizer; + +namespace Slic3r { + +namespace GUI { + +class PresetComboBox; + +//------------------------------------------ +// EditGCodeDialog +//------------------------------------------ + +class EditGCodeDialog : public DPIDialog +{ + wxListBox* m_patterns_list {nullptr}; + ScalableButton* m_add_btn {nullptr}; + wxTextCtrl* m_gcode_editor {nullptr}; + + void OnOK(wxEvent& event); + +public: + EditGCodeDialog(wxWindow* parent, const std::string& key, const std::string& value); + ~EditGCodeDialog() {} + +protected: + void on_dpi_changed(const wxRect& suggested_rect) override; + void on_sys_color_changed() override; +}; + + +} // namespace GUI +} // namespace Slic3r + +#endif diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index aef40170b7..5566921c91 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -45,6 +45,7 @@ #include "format.hpp" #include "UnsavedChangesDialog.hpp" #include "SavePresetDialog.hpp" +#include "EditGCodeDialog.hpp" #include "MsgDialog.hpp" #include "Notebook.hpp" @@ -1860,7 +1861,12 @@ static void validate_custom_gcode_cb(Tab* tab, const wxString& title, const t_co void Tab::edit_custom_gcode(const t_config_option_key& opt_key) { - MessageDialog(this, format_wxstr("Edit gcode for %1%", opt_key), this->title()).ShowModal(); + EditGCodeDialog(this, opt_key, m_config->opt_string(opt_key)).ShowModal(); +} + +void TabFilament::edit_custom_gcode(const t_config_option_key& opt_key) +{ + EditGCodeDialog(this, opt_key, m_config->opt_string(opt_key, m_active_extruder < 0 ? unsigned(0) : unsigned(m_active_extruder))).ShowModal(); } void TabFilament::create_line_with_near_label_widget(ConfigOptionsGroupShp optgroup, const std::string& opt_key, int opt_index/* = 0*/) diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index 6ec714fc28..b95d835000 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -388,10 +388,11 @@ public: const std::map& get_category_icon_map() { return m_category_icon; } static bool validate_custom_gcode(const wxString& title, const std::string& gcode); - void edit_custom_gcode(const t_config_option_key &opt_key); bool validate_custom_gcodes(); bool validate_custom_gcodes_was_shown{ false }; + virtual void edit_custom_gcode(const t_config_option_key &opt_key); + protected: void create_line_with_widget(ConfigOptionsGroup* optgroup, const std::string& opt_key, const std::string& path, widget_t widget); wxSizer* compatible_widget_create(wxWindow* parent, PresetDependencies &deps); @@ -475,6 +476,8 @@ public: void update_extruder_combobox(); int get_active_extruder() const { return m_active_extruder; } + void edit_custom_gcode(const t_config_option_key& opt_key) override; + protected: bool select_preset_by_name(const std::string& name_w_suffix, bool force) override; bool save_current_preset(const std::string& new_name, bool detach) override; From c577b7fb38d6ea2696bccf539230eeb35fda0f18 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 5 Jun 2023 17:23:28 +0200 Subject: [PATCH 082/136] Editing of Custom G-code: Implemented ParamsViewCtrl + GUI_App : Fixed update of the dark mode, when DataViewCtrl doesn't have header --- src/slic3r/GUI/EditGCodeDialog.cpp | 479 ++++++++++++++++++++++++++--- src/slic3r/GUI/EditGCodeDialog.hpp | 211 ++++++++++++- src/slic3r/GUI/GUI_App.cpp | 3 +- src/slic3r/GUI/Tab.cpp | 33 +- src/slic3r/GUI/Tab.hpp | 7 +- 5 files changed, 673 insertions(+), 60 deletions(-) diff --git a/src/slic3r/GUI/EditGCodeDialog.cpp b/src/slic3r/GUI/EditGCodeDialog.cpp index 45feb05019..c29fe7d395 100644 --- a/src/slic3r/GUI/EditGCodeDialog.cpp +++ b/src/slic3r/GUI/EditGCodeDialog.cpp @@ -7,8 +7,6 @@ #include #include #include -#include -#include #include #include "GUI.hpp" @@ -18,7 +16,11 @@ #include "Tab.hpp" #include "wxExtensions.hpp" #include "BitmapCache.hpp" +#include "ExtraRenderers.hpp" #include "MsgDialog.hpp" +#include "Plater.hpp" + +#include "libslic3r/Preset.hpp" namespace Slic3r { namespace GUI { @@ -26,27 +28,14 @@ namespace GUI { static wxArrayString get_patterns_list() { wxArrayString patterns; - for (const wxString& item : { - ";comment"//format_wxstr(";%1%",_L("comment")) - , "M862.3 P \"[printer_model]\" ; printer model check" - , "M862.1 P[nozzle_diameter]; nozzle diameter check" - , "M115 U3.12.2; tell printer latest fw version" - , "G90; use absolute coordinates" - , "M83; extruder relative mode" - , "M104 S[first_layer_temperature]; set extruder temp" - , "M140 S[first_layer_bed_temperature]; set bed temp" - , "M190 S[first_layer_bed_temperature]; wait for bed temp" - , "M109 S[first_layer_temperature]; wait for extruder temp" - , "G28 W; home all without mesh bed level" - , "G80; mesh bed leveling" - , "M403 E0 F {\n" - " + ((filament_type[0] == \"FLEX\") ? 1 : ((filament_type[0] == \"PVA\") ? 2 : 0))\n" - "}" - , "{if not OPTION}" - , "G1" - , "T[initial_tool]; select extruder" - , "G92 E0" - , "{endif}" + for (const wxString& item : { + "printer_model" + , "nozzle_diameter" + , "first_layer_temperature" + , "first_layer_bed_temperature" + , "first_layer_bed_temperature" + , "first_layer_temperature" + , "initial_tool" }) patterns.Add(item); return patterns; @@ -65,22 +54,17 @@ EditGCodeDialog::EditGCodeDialog(wxWindow* parent, const std::string& key, const int border = 10; int em = em_unit(); - wxStaticText* label_top = new wxStaticText(this, wxID_ANY, _L("Edit your custom G-code using patterns")); - label_top->SetFont(wxGetApp().bold_font()); + wxStaticText* label_top = new wxStaticText(this, wxID_ANY, _L("Built-in placeholders (Double click item to add to G-code)") + ":"); auto* grid_sizer = new wxFlexGridSizer(1, 3, 5, 15); grid_sizer->SetFlexibleDirection(wxBOTH); - m_patterns_list = new wxListBox(this, wxID_ANY, wxDefaultPosition, wxSize(em * 15, em * 30), get_patterns_list(), wxLB_SINGLE | wxLB_NEEDED_SB | wxLB_SORT -#ifdef _WIN32 - | wxBORDER_SIMPLE -#endif - ); - m_patterns_list->SetFont(wxGetApp().code_font()); - wxGetApp().UpdateDarkUI(m_patterns_list); + m_params_list = new ParamsViewCtrl(this, wxSize(em * 20, em * 30)); + m_params_list->SetFont(wxGetApp().code_font()); + wxGetApp().UpdateDarkUI(m_params_list); m_add_btn = new ScalableButton(this, wxID_ANY, "add_copies"); - m_add_btn->SetToolTip(_L("Add selected pettern to the G-code")); + m_add_btn->SetToolTip(_L("Add selected placeholder to G-code")); m_gcode_editor = new wxTextCtrl(this, wxID_ANY, value, wxDefaultPosition, wxSize(em * 45, em * 30), wxTE_MULTILINE #ifdef _WIN32 @@ -90,7 +74,7 @@ EditGCodeDialog::EditGCodeDialog(wxWindow* parent, const std::string& key, const m_gcode_editor->SetFont(wxGetApp().code_font()); wxGetApp().UpdateDarkUI(m_gcode_editor); - grid_sizer->Add(m_patterns_list, 1, wxEXPAND); + grid_sizer->Add(m_params_list, 1, wxEXPAND); grid_sizer->Add(m_add_btn, 0, wxALIGN_CENTER_VERTICAL); grid_sizer->Add(m_gcode_editor, 2, wxEXPAND); @@ -98,15 +82,18 @@ EditGCodeDialog::EditGCodeDialog(wxWindow* parent, const std::string& key, const grid_sizer->AddGrowableCol(0, 1); grid_sizer->AddGrowableCol(2, 1); + m_param_label = new wxStaticText(this, wxID_ANY, _L("Select placeholder")); + m_param_label->SetFont(wxGetApp().bold_font()); + wxStdDialogButtonSizer* btns = this->CreateStdDialogButtonSizer(wxOK | wxCANCEL); - wxButton* btnOK = static_cast(this->FindWindowById(wxID_OK, this)); - wxGetApp().UpdateDarkUI(btnOK); - wxGetApp().UpdateDarkUI(static_cast(this->FindWindowById(wxID_CANCEL, this))); + wxGetApp().UpdateDarkUI(this->FindWindowById(wxID_OK, this)); + wxGetApp().UpdateDarkUI(this->FindWindowById(wxID_CANCEL, this)); wxBoxSizer* topSizer = new wxBoxSizer(wxVERTICAL); topSizer->Add(label_top , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); topSizer->Add(grid_sizer , 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); + topSizer->Add(m_param_label , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); topSizer->Add(btns , 0, wxEXPAND | wxALL, border); SetSizer(topSizer); @@ -117,12 +104,106 @@ EditGCodeDialog::EditGCodeDialog(wxWindow* parent, const std::string& key, const this->CenterOnScreen(); + init_params_list(); + bind_list_and_button(); +} - m_patterns_list->Bind(wxEVT_LISTBOX_DCLICK, [this](wxCommandEvent& evt) { - wxString val = m_patterns_list->GetString(m_patterns_list->GetSelection()); - assert(!val.IsEmpty()); - auto insert_to = m_gcode_editor->GetInsertionPoint(); - m_gcode_editor->WriteText(val); +std::string EditGCodeDialog::get_edited_gcode() const +{ + return into_u8(m_gcode_editor->GetValue()); +} + +void EditGCodeDialog::init_params_list() +{ + auto list = get_patterns_list(); + + m_params_list->AppendGroup(GroupParamsType::SlicingState); + for (const auto& sub_gr : { SubSlicingState::ReadOnly, SubSlicingState::ReadWrite }) { + int i = 0; + for (const wxString& name : list) { + const auto param_type = static_cast(1 + std::modulus()(i, 3)); + m_params_list->AppendParam(GroupParamsType::SlicingState, param_type, into_u8(name), sub_gr); + ++i; + } + } + + auto get_set_from_vec = [](const std::vector& vec) { + return std::set(vec.begin(), vec.end()); + }; + + const bool is_fff = wxGetApp().plater()->printer_technology() == ptFFF; + const std::set print_options = get_set_from_vec(is_fff ? Preset::print_options() : Preset::sla_print_options()); + const std::set material_options = get_set_from_vec(is_fff ? Preset::filament_options() : Preset::sla_material_options()); + const std::set printer_options = get_set_from_vec(is_fff ? Preset::printer_options() : Preset::sla_printer_options()); + + const auto& full_config = wxGetApp().preset_bundle->full_config(); + + const auto& def = full_config.def()->get("")->label; + + m_params_list->AppendGroup(GroupParamsType::PrintSettings); + for (const auto& opt : print_options) + if (const ConfigOption *optptr = full_config.optptr(opt)) + m_params_list->AppendParam(GroupParamsType::PrintSettings, optptr->is_scalar() ? ParamType::Scalar : ParamType::Vector, opt); + + m_params_list->AppendGroup(GroupParamsType::MaterialSettings); + for (const auto& opt : material_options) + if (const ConfigOption *optptr = full_config.optptr(opt)) + m_params_list->AppendParam(GroupParamsType::MaterialSettings, optptr->is_scalar() ? ParamType::Scalar : ParamType::FilamentVector, opt); + + m_params_list->AppendGroup(GroupParamsType::PrinterSettings); + for (const auto& opt : printer_options) + if (const ConfigOption *optptr = full_config.optptr(opt)) + m_params_list->AppendParam(GroupParamsType::PrinterSettings, optptr->is_scalar() ? ParamType::Scalar : ParamType::Vector, opt); +} + +void EditGCodeDialog::add_selected_value_to_gcode() +{ + const wxString val = m_params_list->GetSelectedValue(); + if (!val.IsEmpty()) + m_gcode_editor->WriteText(val + "\n"); +} + +void EditGCodeDialog::bind_list_and_button() +{ + m_params_list->Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, [this](wxDataViewEvent& evt) { + wxString label; + + const std::string opt_key = m_params_list->GetSelectedParamKey(); + if (!opt_key.empty()) { + const auto& full_config = wxGetApp().preset_bundle->full_config(); + if (const ConfigDef* def = full_config.def(); + def && def->has(opt_key)) { + const ConfigOptionDef* cod = def->get(opt_key); + const ConfigOption* optptr = full_config.optptr(opt_key); + const ConfigOptionType type = optptr->type(); + + wxString type_str = type == coNone ? "none" : + type == coFloat || type == coFloats ? "float" : + type == coInt || type == coInts ? "integer" : + type == coString || type == coStrings ? "string" : + type == coPercent || type == coPercents ? "percent" : + type == coFloatOrPercent || type == coFloatsOrPercents ? "float ar percent" : + type == coPoint || type == coPoints || type == coPoint3 ? "point" : + type == coBool || type == coBools ? "bool" : + type == coEnum ? "enum" : "undef"; + + label = ( cod->full_label.empty() && cod->label.empty() ) ? format_wxstr("Undef Label\n(%1%)", type_str) : + (!cod->full_label.empty() && !cod->label.empty() ) ? + format_wxstr("%1% > %2%\n(%3%)", _(cod->full_label), _(cod->label), type_str) : + format_wxstr("%1%\n(%2%)", cod->label.empty() ? _(cod->full_label) : _(cod->label), type_str); + } + } + + m_param_label->SetLabel(label); + Layout(); + }); + + m_params_list->Bind(wxEVT_DATAVIEW_ITEM_ACTIVATED, [this](wxDataViewEvent& ) { + add_selected_value_to_gcode(); + }); + + m_add_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { + add_selected_value_to_gcode(); }); } @@ -130,8 +211,6 @@ void EditGCodeDialog::on_dpi_changed(const wxRect&suggested_rect) { const int& em = em_unit(); - //m_optgroup->msw_rescale(); - msw_buttons_rescale(this, em, { wxID_OK, wxID_CANCEL }); const wxSize& size = wxSize(45 * em, 35 * em); @@ -146,10 +225,316 @@ void EditGCodeDialog::on_sys_color_changed() m_add_btn->sys_color_changed(); } -void EditGCodeDialog::OnOK(wxEvent& event) -{ - event.Skip(); +const std::map> GroupParamsInfo { +// Type Name BitmapName + { GroupParamsType::SlicingState, {L("Slicing State"), "re_slice" }, }, + { GroupParamsType::PrintSettings, {L("Print settings"), "cog" }, }, + { GroupParamsType::MaterialSettings,{L("Material Settings"),"spool" }, }, + { GroupParamsType::PrinterSettings, {L("Printer Settings"), "printer" }, }, +}; + +const std::map> SubSlicingStateInfo { +// Type Name BitmapName + { SubSlicingState::ReadOnly, {L("Read Only"), "lock_closed" }, }, + { SubSlicingState::ReadWrite, {L("Read Write"), "lock_open" }, }, +}; + +const std::map ParamsInfo { +// Type BitmapName + { ParamType::Scalar, "scalar_param" }, + { ParamType::Vector, "vector_param" }, + { ParamType::FilamentVector,"vector_filament_param" }, +}; + +static void make_bold(wxString& str) +{ +#if defined(SUPPORTS_MARKUP) && !defined(__APPLE__) + str = format_wxstr("%1%", str); +#endif +} + +// ---------------------------------------------------------------------------- +// ParamsModelNode: a node inside ParamsModel +// ---------------------------------------------------------------------------- + +ParamsNode::ParamsNode(GroupParamsType type) + : m_group_type (type) +{ + const auto& [name, icon_n] = GroupParamsInfo.at(type); + text = _(name); + make_bold(text); + icon_name = icon_n; +} + +ParamsNode::ParamsNode(ParamsNode *parent, SubSlicingState sub_type) + : m_parent(parent) + , m_group_type(parent->m_group_type) +{ + const auto& [name, icon_n] = SubSlicingStateInfo.at(sub_type); + text = _(name); + icon_name = icon_n; +} + +ParamsNode::ParamsNode( ParamsNode* parent, + ParamType param_type, + const std::string& param_key, + SubSlicingState subgroup_type) + : m_parent(parent) + , m_group_type(parent->m_group_type) + , m_sub_type(subgroup_type) + , m_param_type(param_type) + , m_container(false) + , param_key(param_key) +{ + text = from_u8(param_key); + if (param_type == ParamType::Vector) + text += "[]"; + else if (param_type == ParamType::FilamentVector) + text += "[current_extruder]"; + + icon_name = ParamsInfo.at(param_type); +} + + +// ---------------------------------------------------------------------------- +// ParamsModel +// ---------------------------------------------------------------------------- + +ParamsModel::ParamsModel() +{ +} + + +wxDataViewItem ParamsModel::AppendGroup(GroupParamsType type) +{ + m_group_nodes[type] = std::make_unique(type); + + wxDataViewItem parent(nullptr); + wxDataViewItem child((void*)m_group_nodes[type].get()); + + ItemAdded(parent, child); + m_ctrl->Expand(parent); + return child; +} + +wxDataViewItem ParamsModel::AppendSubGroup(GroupParamsType type, SubSlicingState sub_type) +{ + m_sub_slicing_state_nodes[sub_type] = std::make_unique(m_group_nodes[type].get(), sub_type); + + const wxDataViewItem group_item ((void*)m_group_nodes[type].get()); + const wxDataViewItem sub_group_item((void*)m_sub_slicing_state_nodes[sub_type].get()); + + ItemAdded(group_item, sub_group_item); + + m_ctrl->Expand(group_item); + return sub_group_item; +} + +wxDataViewItem ParamsModel::AppendParam(GroupParamsType type, + ParamType param_type, + const std::string& param_key, + SubSlicingState subgroup_type) +{ + ParamsNode* parent_node{ nullptr }; + if (subgroup_type == SubSlicingState::Undef) + parent_node = m_group_nodes[type].get(); + else { + if (m_sub_slicing_state_nodes.find(subgroup_type) == m_sub_slicing_state_nodes.end()) + AppendSubGroup(type, subgroup_type); + parent_node = m_sub_slicing_state_nodes[subgroup_type].get(); + } + + parent_node->Append(std::make_unique(m_group_nodes[type].get(), param_type, param_key, subgroup_type)); + + const wxDataViewItem parent_item((void*)parent_node); + const wxDataViewItem child_item((void*)parent_node->GetChildren().back().get()); + + ItemAdded(parent_item, child_item); + return child_item; +} + +wxString ParamsModel::GetParamName(wxDataViewItem item) +{ + if (item.IsOk()) { + ParamsNode* node = static_cast(item.GetID()); + if (node->IsParamNode()) + return node->text; + } + return wxEmptyString; +} + +std::string ParamsModel::GetParamKey(wxDataViewItem item) +{ + if (item.IsOk()) { + ParamsNode* node = static_cast(item.GetID()); + return node->param_key; + } + return std::string(); +} + +void ParamsModel::Rescale() +{ + +} + +void ParamsModel::Clear() +{ + +} + +void ParamsModel::GetValue(wxVariant& variant, const wxDataViewItem& item, unsigned int col) const +{ + assert(item.IsOk()); + + ParamsNode* node = static_cast(item.GetID()); + if (col == unsigned int(0)) +#ifdef __linux__ + variant << wxDataViewIconText(node->text, get_bmp_bundle(node->icon_name)->GetIconFor(m_ctrl->GetParent())); +#else + variant << DataViewBitmapText(node->text, get_bmp_bundle(node->icon_name)->GetBitmapFor(m_ctrl->GetParent())); +#endif //__linux__ + else + wxLogError("DiffModel::GetValue: wrong column %d", col); +} + +bool ParamsModel::SetValue(const wxVariant& variant, const wxDataViewItem& item, unsigned int col) +{ + assert(item.IsOk()); + + ParamsNode* node = static_cast(item.GetID()); + if (col == unsigned int(0)) { +#ifdef __linux__ + wxDataViewIconText data; + data << variant; + node->m_icon = data.GetIcon(); +#else + DataViewBitmapText data; + data << variant; + node->icon = data.GetBitmap(); +#endif + node->text = data.GetText(); + return true; + } + + wxLogError("DiffModel::SetValue: wrong column"); + return false; +} + +wxDataViewItem ParamsModel::GetParent(const wxDataViewItem&item) const +{ + // the invisible root node has no parent + if (!item.IsOk()) + return wxDataViewItem(nullptr); + + ParamsNode* node = static_cast(item.GetID()); + + if (node->IsGroupNode()) + return wxDataViewItem(nullptr); + + return wxDataViewItem((void*)node->GetParent()); +} + +bool ParamsModel::IsContainer(const wxDataViewItem& item) const +{ + // the invisble root node can have children + if (!item.IsOk()) + return true; + + ParamsNode* node = static_cast(item.GetID()); + return node->IsContainer(); +} + +unsigned int ParamsModel::GetChildren(const wxDataViewItem& parent, wxDataViewItemArray& array) const +{ + ParamsNode* parent_node = (ParamsNode*)parent.GetID(); + + if (parent_node == nullptr) { + for (const auto& [type, group] : m_group_nodes) + array.Add(wxDataViewItem((void*)group.get())); + } + else if (parent_node->IsGroupNode() && parent_node->GetChildren().empty()) { + for (const auto& [type, sub_group] : m_sub_slicing_state_nodes) + array.Add(wxDataViewItem((void*)sub_group.get())); + } + else { + const ParamsNodePtrArray& children = parent_node->GetChildren(); + for (const std::unique_ptr& child : children) + array.Add(wxDataViewItem((void*)child.get())); + } + + return array.Count(); +} + + +// ---------------------------------------------------------------------------- +// ParamsViewCtrl +// ---------------------------------------------------------------------------- + +ParamsViewCtrl::ParamsViewCtrl(wxWindow *parent, wxSize size) + : wxDataViewCtrl(parent, wxID_ANY, wxDefaultPosition, size, wxDV_SINGLE | wxDV_NO_HEADER// | wxDV_ROW_LINES +#ifdef _WIN32 + | wxBORDER_SIMPLE +#endif + ), + m_em_unit(em_unit(parent)) +{ + wxGetApp().UpdateDVCDarkUI(this); + + model = new ParamsModel(); + this->AssociateModel(model); + model->SetAssociatedControl(this); + +#ifdef __linux__ + wxDataViewIconTextRenderer* rd = new wxDataViewIconTextRenderer(); +#ifdef SUPPORTS_MARKUP + rd->EnableMarkup(true); +#endif + wxDataViewColumn* column = new wxDataViewColumn("", rd, 0, width * m_em_unit, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_CELL_INERT); +#else + wxDataViewColumn* column = new wxDataViewColumn("", new BitmapTextRenderer(true, wxDATAVIEW_CELL_INERT), 0, 20 * m_em_unit, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE); +#endif //__linux__ + this->AppendColumn(column); + this->SetExpanderColumn(column); +} + +wxDataViewItem ParamsViewCtrl::AppendGroup(GroupParamsType type) +{ + return model->AppendGroup(type); +} + +wxDataViewItem ParamsViewCtrl::AppendParam( GroupParamsType group_type, + ParamType param_type, + const std::string& param_key, + SubSlicingState subgroup_type /*= SubSlicingState::Undef*/) +{ + return model->AppendParam(group_type, param_type, param_key, subgroup_type); +} + +wxString ParamsViewCtrl::GetValue(wxDataViewItem item) +{ + return model->GetParamName(item); +} + +wxString ParamsViewCtrl::GetSelectedValue() +{ + return model->GetParamName(this->GetSelection()); +} + +std::string ParamsViewCtrl::GetSelectedParamKey() +{ + return model->GetParamKey(this->GetSelection()); +} + +void ParamsViewCtrl::Clear() +{ + model->Clear(); +} + +void ParamsViewCtrl::Rescale(int em/* = 0*/) +{ + model->Rescale(); + Refresh(); } }} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/EditGCodeDialog.hpp b/src/slic3r/GUI/EditGCodeDialog.hpp index eaf3948c85..fddd76a091 100644 --- a/src/slic3r/GUI/EditGCodeDialog.hpp +++ b/src/slic3r/GUI/EditGCodeDialog.hpp @@ -6,18 +6,18 @@ #include #include "GUI_Utils.hpp" +#include "wxExtensions.hpp" +#include "libslic3r/Preset.hpp" class wxListBox; class wxTextCtrl; -//class wxStaticText; class ScalableButton; -//class wxBoxSizer; namespace Slic3r { namespace GUI { -class PresetComboBox; +class ParamsViewCtrl; //------------------------------------------ // EditGCodeDialog @@ -25,22 +25,219 @@ class PresetComboBox; class EditGCodeDialog : public DPIDialog { - wxListBox* m_patterns_list {nullptr}; + ParamsViewCtrl* m_params_list {nullptr}; ScalableButton* m_add_btn {nullptr}; wxTextCtrl* m_gcode_editor {nullptr}; - - void OnOK(wxEvent& event); + wxStaticText* m_param_label {nullptr}; public: - EditGCodeDialog(wxWindow* parent, const std::string& key, const std::string& value); + EditGCodeDialog(wxWindow*parent, const std::string&key, const std::string&value); ~EditGCodeDialog() {} + std::string get_edited_gcode() const; + + void init_params_list(); + void add_selected_value_to_gcode(); + void bind_list_and_button(); + protected: void on_dpi_changed(const wxRect& suggested_rect) override; void on_sys_color_changed() override; }; + + +// ---------------------------------------------------------------------------- +// ParamsModelNode: a node inside ParamsModel +// ---------------------------------------------------------------------------- + +class ParamsNode; +using ParamsNodePtrArray = std::vector>; + +// Discard and Cancel buttons are always but next buttons are optional +enum class GroupParamsType { + SlicingState, + PrintSettings, + MaterialSettings, + PrinterSettings, +}; +using GroupParamsNodePtrMap = std::map>; + +enum class SubSlicingState { + Undef, + ReadOnly, + ReadWrite, +}; +using SubSlicingStateNodePtrMap = std::map>; + +enum class ParamType { + Undef, + Scalar, + Vector, + FilamentVector, +}; + +// On all of 3 different platforms Bitmap+Text icon column looks different +// because of Markup text is missed or not implemented. +// As a temporary workaround, we will use: +// MSW - DataViewBitmapText (our custom renderer wxBitmap + wxString, supported Markup text) +// OSX - -//-, but Markup text is not implemented right now +// GTK - wxDataViewIconText (wxWidgets for GTK renderer wxIcon + wxString, supported Markup text) +class ParamsNode +{ + ParamsNode* m_parent{ nullptr }; + ParamsNodePtrArray m_children; + + GroupParamsType m_group_type{ GroupParamsType::SlicingState }; + SubSlicingState m_sub_type { SubSlicingState::Undef }; + ParamType m_param_type{ ParamType::Undef }; + + // TODO/FIXME: + // the GTK version of wxDVC (in particular wxDataViewCtrlInternal::ItemAdded) + // needs to know in advance if a node is or _will be_ a container. + // Thus implementing: + // bool IsContainer() const + // { return m_children.size()>0; } + // doesn't work with wxGTK when DiffModel::AddToClassical is called + // AND the classical node was removed (a new node temporary without children + // would be added to the control) + bool m_container{ true }; + +public: + +#ifdef __linux__ + wxIcon icon; +#else + wxBitmap icon; +#endif //__linux__ + std::string icon_name; + std::string param_key; + wxString text; + + // Group params(root) node + ParamsNode(GroupParamsType type); + + // sub SlicingState node + ParamsNode(ParamsNode* parent, SubSlicingState sub_type); + + // parametre node + ParamsNode( ParamsNode* parent, + ParamType param_type, + const std::string& param_key, + SubSlicingState subgroup_type); + + bool IsContainer() const { return m_container; } + bool IsGroupNode() const { return m_parent == nullptr; } + bool IsSubGroupNode() const { return m_sub_type != SubSlicingState::Undef; } + bool IsParamNode() const { return m_param_type != ParamType::Undef; } + + ParamsNode* GetParent() { return m_parent; } + ParamsNodePtrArray& GetChildren() { return m_children; } + + void Append(std::unique_ptr child) { m_children.emplace_back(std::move(child)); } +}; + + +// ---------------------------------------------------------------------------- +// ParamsModel +// ---------------------------------------------------------------------------- + +class ParamsModel : public wxDataViewModel +{ + GroupParamsNodePtrMap m_group_nodes; + SubSlicingStateNodePtrMap m_sub_slicing_state_nodes; + + wxDataViewCtrl* m_ctrl{ nullptr }; + +public: + + ParamsModel(); + ~ParamsModel() override = default; + + void SetAssociatedControl(wxDataViewCtrl* ctrl) { m_ctrl = ctrl; } + + wxDataViewItem AppendGroup(GroupParamsType type); + + wxDataViewItem AppendSubGroup( GroupParamsType type, + SubSlicingState sub_type); + + wxDataViewItem AppendParam(GroupParamsType type, + ParamType param_type, + const std::string& param_key, + SubSlicingState subgroup_type = SubSlicingState::Undef); + + wxString GetParamName(wxDataViewItem item); + std::string GetParamKey(wxDataViewItem item); + + void Rescale(); + + void Clear(); + + wxDataViewItem GetParent(const wxDataViewItem& item) const override; + unsigned int GetChildren(const wxDataViewItem& parent, wxDataViewItemArray& array) const override; + + void GetValue(wxVariant& variant, const wxDataViewItem& item, unsigned int col) const override; + bool SetValue(const wxVariant& variant, const wxDataViewItem& item, unsigned int col) override; + + bool IsContainer(const wxDataViewItem& item) const override; + // Is the container just a header or an item with all columns + // In our case it is an item with all columns + bool HasContainerColumns(const wxDataViewItem& WXUNUSED(item)) const override { return true; } +}; + + +// ---------------------------------------------------------------------------- +// ParamsViewCtrl +// ---------------------------------------------------------------------------- + +class ParamsViewCtrl : public wxDataViewCtrl +{ + int m_em_unit; + + //struct ItemData + //{ + // std::string opt_key; + // wxString opt_name; + // wxString old_val; + // wxString mod_val; + // wxString new_val; + // Preset::Type type; + // bool is_long{ false }; + //}; + + //// tree items related to the options + //std::map m_items_map; + +public: + ParamsViewCtrl(wxWindow* parent, wxSize size); + ~ParamsViewCtrl() override { + if (model) { + Clear(); + model->DecRef(); + } + } + + ParamsModel* model{ nullptr }; + + wxDataViewItem AppendGroup( GroupParamsType type); + + wxDataViewItem AppendParam( GroupParamsType group_type, + ParamType param_type, + const std::string& param_key, + SubSlicingState group_subgroup_type = SubSlicingState::Undef); + + wxString GetValue(wxDataViewItem item); + wxString GetSelectedValue(); + std::string GetSelectedParamKey(); + + void Clear(); + void Rescale(int em = 0); + + void set_em_unit(int em) { m_em_unit = em; } +}; + + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index e06830ec9c..b614e5d1be 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -1658,7 +1658,8 @@ void GUI_App::UpdateDVCDarkUI(wxDataViewCtrl* dvc, bool highlited/* = false*/) #ifdef _WIN32 UpdateDarkUI(dvc, highlited ? dark_mode() : false); #ifdef _MSW_DARK_MODE - dvc->RefreshHeaderDarkMode(&m_normal_font); + if (!dvc->HasFlag(wxDV_NO_HEADER)) + dvc->RefreshHeaderDarkMode(&m_normal_font); #endif //_MSW_DARK_MODE if (dvc->HasFlag(wxDV_ROW_LINES)) dvc->SetAlternateRowColour(m_color_highlight_default); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 5566921c91..8b06b8f0a3 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1861,12 +1861,39 @@ static void validate_custom_gcode_cb(Tab* tab, const wxString& title, const t_co void Tab::edit_custom_gcode(const t_config_option_key& opt_key) { - EditGCodeDialog(this, opt_key, m_config->opt_string(opt_key)).ShowModal(); + EditGCodeDialog dlg = EditGCodeDialog(this, opt_key, get_custom_gcode(opt_key)); + if (dlg.ShowModal() == wxID_OK) { + set_custom_gcode(opt_key, dlg.get_edited_gcode()); + update_dirty(); + update(); + } } -void TabFilament::edit_custom_gcode(const t_config_option_key& opt_key) +const std::string& Tab::get_custom_gcode(const t_config_option_key& opt_key) { - EditGCodeDialog(this, opt_key, m_config->opt_string(opt_key, m_active_extruder < 0 ? unsigned(0) : unsigned(m_active_extruder))).ShowModal(); + return m_config->opt_string(opt_key); +} + +void Tab::set_custom_gcode(const t_config_option_key& opt_key, const std::string& value) +{ + DynamicPrintConfig new_conf = *m_config; + new_conf.set_key_value(opt_key, new ConfigOptionString(value)); + load_config(new_conf); +} + +const std::string& TabFilament::get_custom_gcode(const t_config_option_key& opt_key) +{ + return m_config->opt_string(opt_key, unsigned(0)); +} + +void TabFilament::set_custom_gcode(const t_config_option_key& opt_key, const std::string& value) +{ + std::vector gcodes = static_cast(m_config->option(opt_key))->values; + gcodes[0] = value; + + DynamicPrintConfig new_conf = *m_config; + new_conf.set_key_value(opt_key, new ConfigOptionStrings(gcodes)); + load_config(new_conf); } void TabFilament::create_line_with_near_label_widget(ConfigOptionsGroupShp optgroup, const std::string& opt_key, int opt_index/* = 0*/) diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index b95d835000..2fb123964b 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -391,7 +391,9 @@ public: bool validate_custom_gcodes(); bool validate_custom_gcodes_was_shown{ false }; - virtual void edit_custom_gcode(const t_config_option_key &opt_key); + void edit_custom_gcode(const t_config_option_key& opt_key); + virtual const std::string& get_custom_gcode(const t_config_option_key& opt_key); + virtual void set_custom_gcode(const t_config_option_key& opt_key, const std::string& value); protected: void create_line_with_widget(ConfigOptionsGroup* optgroup, const std::string& opt_key, const std::string& path, widget_t widget); @@ -476,7 +478,8 @@ public: void update_extruder_combobox(); int get_active_extruder() const { return m_active_extruder; } - void edit_custom_gcode(const t_config_option_key& opt_key) override; + const std::string& get_custom_gcode(const t_config_option_key& opt_key) override; + void set_custom_gcode(const t_config_option_key& opt_key, const std::string& value) override; protected: bool select_preset_by_name(const std::string& name_w_suffix, bool force) override; From 59af55194109c04becc8f325dde54399babb20ce Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 13 Jun 2023 12:20:05 +0200 Subject: [PATCH 083/136] Editing of Custom G-code: Improved ParamsViewCtrl according to new requirements + Added EditGCodeDialog to localization list --- resources/localization/list.txt | 1 + src/slic3r/GUI/EditGCodeDialog.cpp | 297 ++++++++++++++++++----------- src/slic3r/GUI/EditGCodeDialog.hpp | 80 +++----- 3 files changed, 213 insertions(+), 165 deletions(-) diff --git a/resources/localization/list.txt b/resources/localization/list.txt index de553f2a32..df800c95a6 100644 --- a/resources/localization/list.txt +++ b/resources/localization/list.txt @@ -33,6 +33,7 @@ src/slic3r/GUI/DesktopIntegrationDialog.cpp src/slic3r/GUI/DoubleSlider.cpp src/slic3r/GUI/Downloader.cpp src/slic3r/GUI/DownloaderFileGet.cpp +src/slic3r/GUI/EditGCodeDialog.cpp src/slic3r/GUI/ExtraRenderers.cpp src/slic3r/GUI/ExtruderSequenceDialog.cpp src/slic3r/GUI/Field.cpp diff --git a/src/slic3r/GUI/EditGCodeDialog.cpp b/src/slic3r/GUI/EditGCodeDialog.cpp index c29fe7d395..ea80d95351 100644 --- a/src/slic3r/GUI/EditGCodeDialog.cpp +++ b/src/slic3r/GUI/EditGCodeDialog.cpp @@ -25,20 +25,25 @@ namespace Slic3r { namespace GUI { -static wxArrayString get_patterns_list() +static std::vector get_specific_params(const std::string& custom_gcode) { - wxArrayString patterns; - for (const wxString& item : { - "printer_model" - , "nozzle_diameter" - , "first_layer_temperature" - , "first_layer_bed_temperature" - , "first_layer_bed_temperature" - , "first_layer_temperature" - , "initial_tool" - }) - patterns.Add(item); - return patterns; + if (custom_gcode == "start_filament_gcode" || custom_gcode == "end_filament_gcode") + return{ "max_layer_z", + "layer_num", + "layer_z", + "filament_extruder_id" }; + if (custom_gcode == "end_gcode" || custom_gcode == "before_layer_gcode" || custom_gcode == "layer_gcode") + return{ "max_layer_z", + "layer_num", + "layer_z" }; + if (custom_gcode == "toolchange_gcode") + return{ "next_extruder", + "previous_extruder", + "toolchange_z", + "max_layer_z", + "layer_num", + "layer_z" }; + return {}; } //------------------------------------------ @@ -74,9 +79,9 @@ EditGCodeDialog::EditGCodeDialog(wxWindow* parent, const std::string& key, const m_gcode_editor->SetFont(wxGetApp().code_font()); wxGetApp().UpdateDarkUI(m_gcode_editor); - grid_sizer->Add(m_params_list, 1, wxEXPAND); - grid_sizer->Add(m_add_btn, 0, wxALIGN_CENTER_VERTICAL); - grid_sizer->Add(m_gcode_editor, 2, wxEXPAND); + grid_sizer->Add(m_params_list, 1, wxEXPAND); + grid_sizer->Add(m_add_btn, 0, wxALIGN_CENTER_VERTICAL); + grid_sizer->Add(m_gcode_editor, 2, wxEXPAND); grid_sizer->AddGrowableRow(0, 1); grid_sizer->AddGrowableCol(0, 1); @@ -104,7 +109,7 @@ EditGCodeDialog::EditGCodeDialog(wxWindow* parent, const std::string& key, const this->CenterOnScreen(); - init_params_list(); + init_params_list(key); bind_list_and_button(); } @@ -113,24 +118,71 @@ std::string EditGCodeDialog::get_edited_gcode() const return into_u8(m_gcode_editor->GetValue()); } -void EditGCodeDialog::init_params_list() +void EditGCodeDialog::init_params_list(const std::string& custom_gcode_name) { - auto list = get_patterns_list(); + wxDataViewItem group = m_params_list->AppendGroup(_L("Slicing State"), "re_slice"); + { + wxDataViewItem read_only = m_params_list->AppendSubGroup(group, _L("Read Only"), "lock_closed"); + m_params_list->AppendParam(read_only, ParamType::Scalar, "zhop"); + } + { + wxDataViewItem read_write = m_params_list->AppendSubGroup(group, _L("Read Write"), "lock_open"); + for (const auto& opt_name : { "position", "e_position"}) + m_params_list->AppendParam(read_write, ParamType::Vector, opt_name); + for (const auto& opt_name : { "e_retracted", "e_restart_extra"}) + m_params_list->AppendParam(read_write, ParamType::FilamentVector, opt_name); + } - m_params_list->AppendGroup(GroupParamsType::SlicingState); - for (const auto& sub_gr : { SubSlicingState::ReadOnly, SubSlicingState::ReadWrite }) { - int i = 0; - for (const wxString& name : list) { - const auto param_type = static_cast(1 + std::modulus()(i, 3)); - m_params_list->AppendParam(GroupParamsType::SlicingState, param_type, into_u8(name), sub_gr); - ++i; - } + group = m_params_list->AppendGroup(_L("Universal"), "equal"); + { + wxDataViewItem time_stamp = m_params_list->AppendSubGroup(group, _L("Time Stamp"), "time"); + for (const auto& opt_name : { "day", + "hour", + "minute", + "month", + "second", + "year", + "timestamp" }) + m_params_list->AppendParam(time_stamp, ParamType::Scalar, opt_name); + + for (const auto& opt_name : { "current_extruder", + "current_object_idx", + "filament_preset", + "first_layer_print_convex_hull", + "first_layer_print_max", + "first_layer_print_min", + "first_layer_print_size", + "has_single_extruder_multi_material_priming", + "has_wipe_tower", + "initial_extruder", + "initial_tool", + "input_filename", + "input_filename_base", + "is_extruder_used", + "num_instances", + "num_objects", + "physical_printer_preset", + "print_bed_max", + "print_bed_min", + "print_bed_size", + "print_preset", + "printer_preset", + "scale", + "total_layer_count", + "total_toolchanges" }) + m_params_list->AppendParam(group, ParamType::Scalar, opt_name); + } + + std::vector specific_params = get_specific_params(custom_gcode_name); + if (!specific_params.empty()) { + group = m_params_list->AppendGroup(format_wxstr(_L("Specific for %1%"), custom_gcode_name), "not_equal"); + for (const auto& opt_name : specific_params) + m_params_list->AppendParam(group, ParamType::Scalar, opt_name); } auto get_set_from_vec = [](const std::vector& vec) { return std::set(vec.begin(), vec.end()); }; - const bool is_fff = wxGetApp().plater()->printer_technology() == ptFFF; const std::set print_options = get_set_from_vec(is_fff ? Preset::print_options() : Preset::sla_print_options()); const std::set material_options = get_set_from_vec(is_fff ? Preset::filament_options() : Preset::sla_material_options()); @@ -140,20 +192,25 @@ void EditGCodeDialog::init_params_list() const auto& def = full_config.def()->get("")->label; - m_params_list->AppendGroup(GroupParamsType::PrintSettings); - for (const auto& opt : print_options) - if (const ConfigOption *optptr = full_config.optptr(opt)) - m_params_list->AppendParam(GroupParamsType::PrintSettings, optptr->is_scalar() ? ParamType::Scalar : ParamType::Vector, opt); - m_params_list->AppendGroup(GroupParamsType::MaterialSettings); - for (const auto& opt : material_options) - if (const ConfigOption *optptr = full_config.optptr(opt)) - m_params_list->AppendParam(GroupParamsType::MaterialSettings, optptr->is_scalar() ? ParamType::Scalar : ParamType::FilamentVector, opt); + group = m_params_list->AppendGroup(_L("Presets"), "cog"); + { + wxDataViewItem print = m_params_list->AppendSubGroup(group, _L("Print settings"), "cog"); + for (const auto& opt : print_options) + if (const ConfigOption *optptr = full_config.optptr(opt)) + m_params_list->AppendParam(print, optptr->is_scalar() ? ParamType::Scalar : ParamType::Vector, opt); + + wxDataViewItem material = m_params_list->AppendSubGroup(group, _(is_fff ? L("Filament settings") : L("SLA Materials settings")), is_fff ? "spool" : "resin"); + for (const auto& opt : material_options) + if (const ConfigOption *optptr = full_config.optptr(opt)) + m_params_list->AppendParam(material, optptr->is_scalar() ? ParamType::Scalar : ParamType::FilamentVector, opt); - m_params_list->AppendGroup(GroupParamsType::PrinterSettings); - for (const auto& opt : printer_options) - if (const ConfigOption *optptr = full_config.optptr(opt)) - m_params_list->AppendParam(GroupParamsType::PrinterSettings, optptr->is_scalar() ? ParamType::Scalar : ParamType::Vector, opt); + wxDataViewItem printer = m_params_list->AppendSubGroup(group, _L("Printer settings"), is_fff ? "printer" : "sla_printer"); + for (const auto& opt : printer_options) + if (const ConfigOption *optptr = full_config.optptr(opt)) + m_params_list->AppendParam(printer, optptr->is_scalar() ? ParamType::Scalar : ParamType::Vector, opt); + + } } void EditGCodeDialog::add_selected_value_to_gcode() @@ -226,22 +283,9 @@ void EditGCodeDialog::on_sys_color_changed() } -const std::map> GroupParamsInfo { -// Type Name BitmapName - { GroupParamsType::SlicingState, {L("Slicing State"), "re_slice" }, }, - { GroupParamsType::PrintSettings, {L("Print settings"), "cog" }, }, - { GroupParamsType::MaterialSettings,{L("Material Settings"),"spool" }, }, - { GroupParamsType::PrinterSettings, {L("Printer Settings"), "printer" }, }, -}; - -const std::map> SubSlicingStateInfo { -// Type Name BitmapName - { SubSlicingState::ReadOnly, {L("Read Only"), "lock_closed" }, }, - { SubSlicingState::ReadWrite, {L("Read Write"), "lock_open" }, }, -}; const std::map ParamsInfo { -// Type BitmapName +// Type BitmapName { ParamType::Scalar, "scalar_param" }, { ParamType::Vector, "vector_param" }, { ParamType::FilamentVector,"vector_filament_param" }, @@ -258,31 +302,27 @@ static void make_bold(wxString& str) // ParamsModelNode: a node inside ParamsModel // ---------------------------------------------------------------------------- -ParamsNode::ParamsNode(GroupParamsType type) - : m_group_type (type) +ParamsNode::ParamsNode(const wxString& group_name, const std::string& icon_name) +: icon_name(icon_name) +, text(group_name) { - const auto& [name, icon_n] = GroupParamsInfo.at(type); - text = _(name); make_bold(text); - icon_name = icon_n; } -ParamsNode::ParamsNode(ParamsNode *parent, SubSlicingState sub_type) +ParamsNode::ParamsNode( ParamsNode * parent, + const wxString& sub_group_name, + const std::string& icon_name) : m_parent(parent) - , m_group_type(parent->m_group_type) + , icon_name(icon_name) + , text(sub_group_name) { - const auto& [name, icon_n] = SubSlicingStateInfo.at(sub_type); - text = _(name); - icon_name = icon_n; + make_bold(text); } ParamsNode::ParamsNode( ParamsNode* parent, ParamType param_type, - const std::string& param_key, - SubSlicingState subgroup_type) + const std::string& param_key) : m_parent(parent) - , m_group_type(parent->m_group_type) - , m_sub_type(subgroup_type) , m_param_type(param_type) , m_container(false) , param_key(param_key) @@ -305,56 +345,51 @@ ParamsModel::ParamsModel() { } - -wxDataViewItem ParamsModel::AppendGroup(GroupParamsType type) +wxDataViewItem ParamsModel::AppendGroup(const wxString& group_name, + const std::string& icon_name) { - m_group_nodes[type] = std::make_unique(type); + m_group_nodes.emplace_back(std::make_unique(group_name, icon_name)); wxDataViewItem parent(nullptr); - wxDataViewItem child((void*)m_group_nodes[type].get()); + wxDataViewItem child((void*)m_group_nodes.back().get()); ItemAdded(parent, child); m_ctrl->Expand(parent); return child; } -wxDataViewItem ParamsModel::AppendSubGroup(GroupParamsType type, SubSlicingState sub_type) +wxDataViewItem ParamsModel::AppendSubGroup(wxDataViewItem parent, + const wxString& sub_group_name, + const std::string& icon_name) { - m_sub_slicing_state_nodes[sub_type] = std::make_unique(m_group_nodes[type].get(), sub_type); + ParamsNode* parent_node = static_cast(parent.GetID()); + if (!parent_node) + return wxDataViewItem(0); - const wxDataViewItem group_item ((void*)m_group_nodes[type].get()); - const wxDataViewItem sub_group_item((void*)m_sub_slicing_state_nodes[sub_type].get()); + parent_node->Append(std::make_unique(parent_node, sub_group_name, icon_name)); + const wxDataViewItem sub_group_item((void*)parent_node->GetChildren().back().get()); - ItemAdded(group_item, sub_group_item); - - m_ctrl->Expand(group_item); + ItemAdded(parent, sub_group_item); return sub_group_item; } -wxDataViewItem ParamsModel::AppendParam(GroupParamsType type, +wxDataViewItem ParamsModel::AppendParam(wxDataViewItem parent, ParamType param_type, - const std::string& param_key, - SubSlicingState subgroup_type) + const std::string& param_key) { - ParamsNode* parent_node{ nullptr }; - if (subgroup_type == SubSlicingState::Undef) - parent_node = m_group_nodes[type].get(); - else { - if (m_sub_slicing_state_nodes.find(subgroup_type) == m_sub_slicing_state_nodes.end()) - AppendSubGroup(type, subgroup_type); - parent_node = m_sub_slicing_state_nodes[subgroup_type].get(); - } - - parent_node->Append(std::make_unique(m_group_nodes[type].get(), param_type, param_key, subgroup_type)); + ParamsNode* parent_node = static_cast(parent.GetID()); + if (!parent_node) + return wxDataViewItem(0); + + parent_node->Append(std::make_unique(parent_node, param_type, param_key)); - const wxDataViewItem parent_item((void*)parent_node); const wxDataViewItem child_item((void*)parent_node->GetChildren().back().get()); - ItemAdded(parent_item, child_item); + ItemAdded(parent, child_item); return child_item; } -wxString ParamsModel::GetParamName(wxDataViewItem item) +wxString ParamsModel::GetParamName(wxDataViewItem item) { if (item.IsOk()) { ParamsNode* node = static_cast(item.GetID()); @@ -373,14 +408,52 @@ std::string ParamsModel::GetParamKey(wxDataViewItem item) return std::string(); } -void ParamsModel::Rescale() +wxDataViewItem ParamsModel::Delete(const wxDataViewItem& item) { - + auto ret_item = wxDataViewItem(nullptr); + ParamsNode* node = static_cast(item.GetID()); + if (!node) // happens if item.IsOk()==false + return ret_item; + + // first remove the node from the parent's array of children; + // NOTE: m_group_nodes is only a vector of _pointers_ + // thus removing the node from it doesn't result in freeing it + ParamsNodePtrArray& children = node->GetChildren(); + // Delete all children + while (!children.empty()) + Delete(wxDataViewItem(children.back().get())); + + auto node_parent = node->GetParent(); + + ParamsNodePtrArray& parents_children = node_parent ? node_parent->GetChildren() : m_group_nodes; + auto it = find_if(parents_children.begin(), parents_children.end(), + [node](std::unique_ptr& child) { return child.get() == node; }); + assert(it != parents_children.end()); + it = parents_children.erase(it); + + if (it != parents_children.end()) + ret_item = wxDataViewItem(it->get()); + + wxDataViewItem parent(node_parent); + // set m_container to FALSE if parent has no child + if (node_parent) { +#ifndef __WXGTK__ + if (node_parent->GetChildren().empty()) + node_parent->SetContainer(false); +#endif //__WXGTK__ + ret_item = parent; + } + + // notify control + ItemDeleted(parent, item); + return ret_item; } void ParamsModel::Clear() { - + while (!m_group_nodes.empty()) + Delete(wxDataViewItem(m_group_nodes.back().get())); + } void ParamsModel::GetValue(wxVariant& variant, const wxDataViewItem& item, unsigned int col) const @@ -450,13 +523,9 @@ unsigned int ParamsModel::GetChildren(const wxDataViewItem& parent, wxDataViewIt ParamsNode* parent_node = (ParamsNode*)parent.GetID(); if (parent_node == nullptr) { - for (const auto& [type, group] : m_group_nodes) + for (const auto& group : m_group_nodes) array.Add(wxDataViewItem((void*)group.get())); } - else if (parent_node->IsGroupNode() && parent_node->GetChildren().empty()) { - for (const auto& [type, sub_group] : m_sub_slicing_state_nodes) - array.Add(wxDataViewItem((void*)sub_group.get())); - } else { const ParamsNodePtrArray& children = parent_node->GetChildren(); for (const std::unique_ptr& child : children) @@ -498,17 +567,23 @@ ParamsViewCtrl::ParamsViewCtrl(wxWindow *parent, wxSize size) this->SetExpanderColumn(column); } -wxDataViewItem ParamsViewCtrl::AppendGroup(GroupParamsType type) +wxDataViewItem ParamsViewCtrl::AppendGroup(const wxString& group_name, const std::string& icon_name) { - return model->AppendGroup(type); + return model->AppendGroup(group_name, icon_name); } -wxDataViewItem ParamsViewCtrl::AppendParam( GroupParamsType group_type, - ParamType param_type, - const std::string& param_key, - SubSlicingState subgroup_type /*= SubSlicingState::Undef*/) +wxDataViewItem ParamsViewCtrl::AppendSubGroup( wxDataViewItem parent, + const wxString& sub_group_name, + const std::string& icon_name) { - return model->AppendParam(group_type, param_type, param_key, subgroup_type); + return model->AppendSubGroup(parent, sub_group_name, icon_name); +} + +wxDataViewItem ParamsViewCtrl::AppendParam( wxDataViewItem parent, + ParamType param_type, + const std::string& param_key) +{ + return model->AppendParam(parent, param_type, param_key); } wxString ParamsViewCtrl::GetValue(wxDataViewItem item) @@ -533,7 +608,7 @@ void ParamsViewCtrl::Clear() void ParamsViewCtrl::Rescale(int em/* = 0*/) { - model->Rescale(); +// model->Rescale(); Refresh(); } diff --git a/src/slic3r/GUI/EditGCodeDialog.hpp b/src/slic3r/GUI/EditGCodeDialog.hpp index fddd76a091..be7746b413 100644 --- a/src/slic3r/GUI/EditGCodeDialog.hpp +++ b/src/slic3r/GUI/EditGCodeDialog.hpp @@ -36,7 +36,7 @@ public: std::string get_edited_gcode() const; - void init_params_list(); + void init_params_list(const std::string& custom_gcode_name); void add_selected_value_to_gcode(); void bind_list_and_button(); @@ -55,22 +55,6 @@ protected: class ParamsNode; using ParamsNodePtrArray = std::vector>; -// Discard and Cancel buttons are always but next buttons are optional -enum class GroupParamsType { - SlicingState, - PrintSettings, - MaterialSettings, - PrinterSettings, -}; -using GroupParamsNodePtrMap = std::map>; - -enum class SubSlicingState { - Undef, - ReadOnly, - ReadWrite, -}; -using SubSlicingStateNodePtrMap = std::map>; - enum class ParamType { Undef, Scalar, @@ -89,8 +73,6 @@ class ParamsNode ParamsNode* m_parent{ nullptr }; ParamsNodePtrArray m_children; - GroupParamsType m_group_type{ GroupParamsType::SlicingState }; - SubSlicingState m_sub_type { SubSlicingState::Undef }; ParamType m_param_type{ ParamType::Undef }; // TODO/FIXME: @@ -116,21 +98,22 @@ public: wxString text; // Group params(root) node - ParamsNode(GroupParamsType type); + ParamsNode(const wxString& group_name, const std::string& icon_name); // sub SlicingState node - ParamsNode(ParamsNode* parent, SubSlicingState sub_type); + ParamsNode(ParamsNode* parent, + const wxString& sub_group_name, + const std::string& icon_name); // parametre node ParamsNode( ParamsNode* parent, ParamType param_type, - const std::string& param_key, - SubSlicingState subgroup_type); + const std::string& param_key); bool IsContainer() const { return m_container; } bool IsGroupNode() const { return m_parent == nullptr; } - bool IsSubGroupNode() const { return m_sub_type != SubSlicingState::Undef; } bool IsParamNode() const { return m_param_type != ParamType::Undef; } + void SetContainer(bool is_container) { m_container = is_container; } ParamsNode* GetParent() { return m_parent; } ParamsNodePtrArray& GetChildren() { return m_children; } @@ -145,10 +128,8 @@ public: class ParamsModel : public wxDataViewModel { - GroupParamsNodePtrMap m_group_nodes; - SubSlicingStateNodePtrMap m_sub_slicing_state_nodes; - - wxDataViewCtrl* m_ctrl{ nullptr }; + ParamsNodePtrArray m_group_nodes; + wxDataViewCtrl* m_ctrl{ nullptr }; public: @@ -157,21 +138,22 @@ public: void SetAssociatedControl(wxDataViewCtrl* ctrl) { m_ctrl = ctrl; } - wxDataViewItem AppendGroup(GroupParamsType type); + wxDataViewItem AppendGroup(const wxString& group_name, + const std::string& icon_name); - wxDataViewItem AppendSubGroup( GroupParamsType type, - SubSlicingState sub_type); + wxDataViewItem AppendSubGroup(wxDataViewItem parent, + const wxString& sub_group_name, + const std::string&icon_name); - wxDataViewItem AppendParam(GroupParamsType type, + wxDataViewItem AppendParam( wxDataViewItem parent, ParamType param_type, - const std::string& param_key, - SubSlicingState subgroup_type = SubSlicingState::Undef); + const std::string& param_key); + + wxDataViewItem Delete(const wxDataViewItem& item); wxString GetParamName(wxDataViewItem item); std::string GetParamKey(wxDataViewItem item); - void Rescale(); - void Clear(); wxDataViewItem GetParent(const wxDataViewItem& item) const override; @@ -195,20 +177,6 @@ class ParamsViewCtrl : public wxDataViewCtrl { int m_em_unit; - //struct ItemData - //{ - // std::string opt_key; - // wxString opt_name; - // wxString old_val; - // wxString mod_val; - // wxString new_val; - // Preset::Type type; - // bool is_long{ false }; - //}; - - //// tree items related to the options - //std::map m_items_map; - public: ParamsViewCtrl(wxWindow* parent, wxSize size); ~ParamsViewCtrl() override { @@ -220,12 +188,16 @@ public: ParamsModel* model{ nullptr }; - wxDataViewItem AppendGroup( GroupParamsType type); + wxDataViewItem AppendGroup(const wxString& group_name, + const std::string& icon_name); - wxDataViewItem AppendParam( GroupParamsType group_type, + wxDataViewItem AppendSubGroup(wxDataViewItem parent, + const wxString& sub_group_name, + const std::string&icon_name); + + wxDataViewItem AppendParam( wxDataViewItem parent, ParamType param_type, - const std::string& param_key, - SubSlicingState group_subgroup_type = SubSlicingState::Undef); + const std::string& param_key); wxString GetValue(wxDataViewItem item); wxString GetSelectedValue(); From 55d5921d53fdfdaeb1dad38e2407b598c84a4be7 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 13 Jun 2023 12:21:23 +0200 Subject: [PATCH 084/136] Editing of Custom G-code: Partially automation to get a list of placeholders for custom gcodes + Added missed resources --- resources/custom_gcodes/before_layer_gcode | 4 + resources/custom_gcodes/end_filament_gcode | 5 + resources/custom_gcodes/end_gcode | 5 + resources/custom_gcodes/layer_gcode | 4 + resources/custom_gcodes/rw_slicing_state | 4 + resources/custom_gcodes/start_filament_gcode | 5 + resources/custom_gcodes/tcr_rotated_gcode | 3 + resources/custom_gcodes/toolchange_gcode | 7 + resources/custom_gcodes/universal | 39 +++ resources/icons/scalar_param.svg | 21 ++ resources/icons/vector_filament_param.svg | 28 ++ resources/icons/vector_param.svg | 28 ++ src/PrusaSlicer.cpp | 1 + src/libslic3r/GCode.cpp | 6 + src/libslic3r/GCode.hpp | 9 + src/libslic3r/Print.cpp | 40 +++ src/libslic3r/Utils.hpp | 5 + src/libslic3r/utils.cpp | 12 + src/slic3r/GUI/EditGCodeDialog.cpp | 320 ++++++++++++------- src/slic3r/GUI/EditGCodeDialog.hpp | 7 + 20 files changed, 442 insertions(+), 111 deletions(-) create mode 100644 resources/custom_gcodes/before_layer_gcode create mode 100644 resources/custom_gcodes/end_filament_gcode create mode 100644 resources/custom_gcodes/end_gcode create mode 100644 resources/custom_gcodes/layer_gcode create mode 100644 resources/custom_gcodes/rw_slicing_state create mode 100644 resources/custom_gcodes/start_filament_gcode create mode 100644 resources/custom_gcodes/tcr_rotated_gcode create mode 100644 resources/custom_gcodes/toolchange_gcode create mode 100644 resources/custom_gcodes/universal create mode 100644 resources/icons/scalar_param.svg create mode 100644 resources/icons/vector_filament_param.svg create mode 100644 resources/icons/vector_param.svg diff --git a/resources/custom_gcodes/before_layer_gcode b/resources/custom_gcodes/before_layer_gcode new file mode 100644 index 0000000000..3e00992c4d --- /dev/null +++ b/resources/custom_gcodes/before_layer_gcode @@ -0,0 +1,4 @@ +# generated by PrusaSlicer 2.6.0-rc1 on 2023-06-15 at 13:29:08 UTC +layer_num = 2 +layer_z = 1 +max_layer_z = 1 diff --git a/resources/custom_gcodes/end_filament_gcode b/resources/custom_gcodes/end_filament_gcode new file mode 100644 index 0000000000..6cd107ae83 --- /dev/null +++ b/resources/custom_gcodes/end_filament_gcode @@ -0,0 +1,5 @@ +# generated by PrusaSlicer 2.6.0-rc1 on 2023-06-15 at 13:29:09 UTC +filament_extruder_id = 2 +layer_num = 2 +layer_z = 1 +max_layer_z = 1 diff --git a/resources/custom_gcodes/end_gcode b/resources/custom_gcodes/end_gcode new file mode 100644 index 0000000000..6cd107ae83 --- /dev/null +++ b/resources/custom_gcodes/end_gcode @@ -0,0 +1,5 @@ +# generated by PrusaSlicer 2.6.0-rc1 on 2023-06-15 at 13:29:09 UTC +filament_extruder_id = 2 +layer_num = 2 +layer_z = 1 +max_layer_z = 1 diff --git a/resources/custom_gcodes/layer_gcode b/resources/custom_gcodes/layer_gcode new file mode 100644 index 0000000000..9bebff5d45 --- /dev/null +++ b/resources/custom_gcodes/layer_gcode @@ -0,0 +1,4 @@ +# generated by PrusaSlicer 2.6.0-rc1 on 2023-06-15 at 13:29:09 UTC +layer_num = 2 +layer_z = 1 +max_layer_z = 1 diff --git a/resources/custom_gcodes/rw_slicing_state b/resources/custom_gcodes/rw_slicing_state new file mode 100644 index 0000000000..e269a084ab --- /dev/null +++ b/resources/custom_gcodes/rw_slicing_state @@ -0,0 +1,4 @@ +# generated by PrusaSlicer 2.6.0-rc1 on 2023-06-15 at 13:29:09 UTC +e_restart_extra = 16385 +e_retracted = 16385 +position = 16385 diff --git a/resources/custom_gcodes/start_filament_gcode b/resources/custom_gcodes/start_filament_gcode new file mode 100644 index 0000000000..6cd107ae83 --- /dev/null +++ b/resources/custom_gcodes/start_filament_gcode @@ -0,0 +1,5 @@ +# generated by PrusaSlicer 2.6.0-rc1 on 2023-06-15 at 13:29:09 UTC +filament_extruder_id = 2 +layer_num = 2 +layer_z = 1 +max_layer_z = 1 diff --git a/resources/custom_gcodes/tcr_rotated_gcode b/resources/custom_gcodes/tcr_rotated_gcode new file mode 100644 index 0000000000..b616714f57 --- /dev/null +++ b/resources/custom_gcodes/tcr_rotated_gcode @@ -0,0 +1,3 @@ +# generated by PrusaSlicer 2.6.0-rc1 on 2023-06-14 at 11:40:58 UTC +deretraction_from_wipe_tower_generator = 3 +toolchange_gcode = 3 diff --git a/resources/custom_gcodes/toolchange_gcode b/resources/custom_gcodes/toolchange_gcode new file mode 100644 index 0000000000..a254e530b3 --- /dev/null +++ b/resources/custom_gcodes/toolchange_gcode @@ -0,0 +1,7 @@ +# generated by PrusaSlicer 2.6.0-rc1 on 2023-06-14 at 11:40:58 UTC +layer_num = 2 +layer_z = 1 +max_layer_z = 1 +next_extruder = 2 +previous_extruder = 2 +toolchange_z = 1 diff --git a/resources/custom_gcodes/universal b/resources/custom_gcodes/universal new file mode 100644 index 0000000000..511a9c9b59 --- /dev/null +++ b/resources/custom_gcodes/universal @@ -0,0 +1,39 @@ +# generated by PrusaSlicer 2.6.0-rc1 on 2023-06-15 at 13:29:09 UTC +current_extruder = 2 +current_object_idx = 2 +day = 2 +extruded_volume = 16385 +extruded_volume_total = 1 +extruded_weight = 16385 +extruded_weight_total = 1 +filament_preset = 16387 +first_layer_print_convex_hull = 16390 +first_layer_print_max = 16385 +first_layer_print_min = 16385 +first_layer_print_size = 16385 +has_single_extruder_multi_material_priming = 8 +has_wipe_tower = 8 +hour = 2 +initial_extruder = 2 +initial_tool = 2 +input_filename = 3 +input_filename_base = 3 +is_extruder_used = 16392 +minute = 2 +month = 2 +num_extruders = 2 +num_instances = 2 +num_objects = 2 +physical_printer_preset = 3 +print_bed_max = 16385 +print_bed_min = 16385 +print_bed_size = 16385 +print_preset = 3 +printer_preset = 3 +scale = 16387 +second = 2 +timestamp = 3 +total_layer_count = 2 +total_toolchanges = 2 +year = 2 +zhop = 1 diff --git a/resources/icons/scalar_param.svg b/resources/icons/scalar_param.svg new file mode 100644 index 0000000000..f9386ab70e --- /dev/null +++ b/resources/icons/scalar_param.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + diff --git a/resources/icons/vector_filament_param.svg b/resources/icons/vector_filament_param.svg new file mode 100644 index 0000000000..a3404cfd88 --- /dev/null +++ b/resources/icons/vector_filament_param.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/icons/vector_param.svg b/resources/icons/vector_param.svg new file mode 100644 index 0000000000..a5c8affc7f --- /dev/null +++ b/resources/icons/vector_param.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index 0d627aa837..4b2aaed18d 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -763,6 +763,7 @@ bool CLI::setup(int argc, char **argv) set_var_dir((path_resources / "icons").string()); set_local_dir((path_resources / "localization").string()); set_sys_shapes_dir((path_resources / "shapes").string()); + set_custom_gcodes_dir((path_resources / "custom_gcodes").string()); // Parse all command line options into a DynamicConfig. // If any option is unsupported, print usage and abort immediately. diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index debe3e63b9..5ae7cd0dd0 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1504,6 +1504,12 @@ std::string GCodeGenerator::placeholder_parser_process( unsigned int current_extruder_id, const DynamicConfig *config_override) { +#if GET_CUSTOM_GCODE_PLACEHOLDERS + if (config_override && + g_code_placeholders_map.find(name) == g_code_placeholders_map.end()) + g_code_placeholders_map[name] = *config_override; +#endif + PlaceholderParserIntegration &ppi = m_placeholder_parser_integration; try { ppi.update_from_gcodewriter(m_writer); diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 1c92874922..888055cc9a 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -72,6 +72,8 @@ struct LayerResult { }; class GCodeGenerator { +#define GET_CUSTOM_GCODE_PLACEHOLDERS 1 + public: GCodeGenerator() : m_origin(Vec2d::Zero()), @@ -97,6 +99,13 @@ public: {} ~GCodeGenerator() = default; +#if GET_CUSTOM_GCODE_PLACEHOLDERS + std::map g_code_placeholders_map; + const std::map& get_g_code_placeholders_map() { return g_code_placeholders_map; } + const DynamicConfig& get_placeholder_parser_config() const { return m_placeholder_parser_integration.parser.config(); } + const DynamicConfig& get_placeholder_output_config() const { return m_placeholder_parser_integration.output_config; } +#endif + // throws std::runtime_exception on error, // throws CanceledException through print->throw_if_canceled(). void do_export(Print* print, const char* path, GCodeProcessorResult* result = nullptr, ThumbnailsGeneratorCallback thumbnail_cb = nullptr); diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 1ee545606c..c63b3b1a95 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1014,6 +1014,46 @@ std::string Print::export_gcode(const std::string& path_template, GCodeProcessor std::unique_ptr gcode(new GCodeGenerator); gcode->do_export(this, path.c_str(), result, thumbnail_cb); + +#if GET_CUSTOM_GCODE_PLACEHOLDERS + + const std::string dir = custom_gcodes_dir() + +#ifdef _WIN32 + "\\"; +#else + "/"; +#endif + + auto save_placeholders = [dir](const std::string& file_name, const DynamicConfig& config) { + try { + boost::nowide::ofstream c; + c.open(dir + file_name, std::ios::out | std::ios::trunc); + c << "# " << header_slic3r_generated() << std::endl; + auto keys = config.keys(); + for (const std::string& opt_key : keys) { + const std::string type = std::to_string(int(config.optptr(opt_key)->type())); + c << opt_key << " = " << type << std::endl; + } + c.close(); + } + catch (const std::ofstream::failure& err) { + throw RuntimeError(format("The %1% cannot be loaded:\n\tReason: %2%", file_name, err.what())); + } + }; + + // save specific placeholders + const auto& gcode_placeholders = gcode->get_g_code_placeholders_map(); + for (const auto& [gcode_name, config] : gcode_placeholders) + save_placeholders(gcode_name, config); + + // save universal placeholders + save_placeholders("universal", gcode->get_placeholder_parser_config()); + + // save placeholders for "rw_slicing_state" slicing state + save_placeholders("rw_slicing_state", gcode->get_placeholder_output_config()); + +#endif + if (m_conflict_result.has_value()) result->conflict_result = *m_conflict_result; diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp index 9cc81b241f..201cbcc88d 100644 --- a/src/libslic3r/Utils.hpp +++ b/src/libslic3r/Utils.hpp @@ -53,6 +53,11 @@ const std::string& sys_shapes_dir(); // Return a full path to the custom shapes gallery directory. std::string custom_shapes_dir(); +// Set a path with shapes gallery files. +void set_custom_gcodes_dir(const std::string &path); +// Return a full path to the system shapes gallery directory. +const std::string& custom_gcodes_dir(); + // Set a path with preset files. void set_data_dir(const std::string &path); // Return a full path to the GUI resource files. diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp index d934819bfe..161a24e0a6 100644 --- a/src/libslic3r/utils.cpp +++ b/src/libslic3r/utils.cpp @@ -193,6 +193,18 @@ const std::string& sys_shapes_dir() return g_sys_shapes_dir; } +static std::string g_custom_gcodes_dir; + +void set_custom_gcodes_dir(const std::string &dir) +{ + g_custom_gcodes_dir = dir; +} + +const std::string& custom_gcodes_dir() +{ + return g_custom_gcodes_dir; +} + // Translate function callback, to call wxWidgets translate function to convert non-localized UTF8 string to a localized one. Slic3r::I18N::translate_fn_type Slic3r::I18N::translate_fn = nullptr; diff --git a/src/slic3r/GUI/EditGCodeDialog.cpp b/src/slic3r/GUI/EditGCodeDialog.cpp index ea80d95351..2875ffe7e3 100644 --- a/src/slic3r/GUI/EditGCodeDialog.cpp +++ b/src/slic3r/GUI/EditGCodeDialog.cpp @@ -20,30 +20,104 @@ #include "MsgDialog.hpp" #include "Plater.hpp" +#include "libslic3r/PlaceholderParser.hpp" #include "libslic3r/Preset.hpp" +#include "libslic3r/Print.hpp" namespace Slic3r { namespace GUI { - -static std::vector get_specific_params(const std::string& custom_gcode) + +ConfigOption* get_new_option(const ConfigOptionType type) { - if (custom_gcode == "start_filament_gcode" || custom_gcode == "end_filament_gcode") - return{ "max_layer_z", - "layer_num", - "layer_z", - "filament_extruder_id" }; - if (custom_gcode == "end_gcode" || custom_gcode == "before_layer_gcode" || custom_gcode == "layer_gcode") - return{ "max_layer_z", - "layer_num", - "layer_z" }; - if (custom_gcode == "toolchange_gcode") - return{ "next_extruder", - "previous_extruder", - "toolchange_z", - "max_layer_z", - "layer_num", - "layer_z" }; - return {}; + switch (type) { + case coFloat: + return new ConfigOptionFloat(0.); + case coFloats: + return new ConfigOptionFloats({ 0. }); + case coInt: + return new ConfigOptionInt(0); + case coInts: + return new ConfigOptionInts({ 0 }); + case coString: + return new ConfigOptionString(""); + case coStrings: + return new ConfigOptionStrings({ ""}); + case coPercent: + return new ConfigOptionPercent(0); + case coPercents: + return new ConfigOptionPercents({ 0}); + case coFloatOrPercent: + return new ConfigOptionFloatOrPercent(); + case coFloatsOrPercents: + return new ConfigOptionFloatsOrPercents(); + case coPoint: + return new ConfigOptionPoint(Vec2d(100, 100)); + case coPoints: + return new ConfigOptionPoints({ Vec2d(100,100) }); + case coPoint3: + return new ConfigOptionPoint3(); + case coBool: + return new ConfigOptionBool(true); + case coBools: + return new ConfigOptionBools({ true }); + case coEnum: + return new ConfigOptionEnum(); + default: + return nullptr; + } +} + +namespace fs = boost::filesystem; +namespace pt = boost::property_tree; +static std::vector get_params_from_file(const std::string& file_name, DynamicConfig& out_config) +{ + const fs::path file_path = fs::path(custom_gcodes_dir() + +#ifdef _WIN32 + "\\" +#else + "/" +#endif + + file_name); + + if (!fs::exists(file_path)) + return {}; + + const std::string file = file_path.string(); + + // Load the preset file, apply preset values on top of defaults. + try { + DynamicConfig config; + + try { + pt::ptree tree; + boost::nowide::ifstream ifs(file); + pt::read_ini(ifs, tree); + for (const pt::ptree::value_type& v : tree) { + try { + t_config_option_key opt_key = v.first; + const std::string type_str = v.second.get_value(); + const ConfigOptionType type = ConfigOptionType(std::atoi(type_str.c_str())); + if (ConfigOption* opt = get_new_option(type)) + config.set_key_value(opt_key, std::move(opt)); + } + catch (UnknownOptionException& err) { + throw RuntimeError(format("Some option from %1% cannot be loaded:\n\tReason: %2%", file, err.what())); + } + } + } + catch (const ConfigurationError& e) { + throw ConfigurationError(format("Failed loading configuration file \"%1%\": \n\t%2%", file, e.what())); + } + + out_config += config; + return config.keys(); + } + catch (const std::ifstream::failure& err) { + throw RuntimeError(format("The %1% cannot be loaded:\n\tReason: %2%", file, err.what())); + } + catch (const std::runtime_error& err) { + throw RuntimeError(format("Failed loading the custom_gcode_placeholders file: \"%1%\"\n\tReason: %2%", file , err.what())); + } } //------------------------------------------ @@ -120,97 +194,106 @@ std::string EditGCodeDialog::get_edited_gcode() const void EditGCodeDialog::init_params_list(const std::string& custom_gcode_name) { - wxDataViewItem group = m_params_list->AppendGroup(_L("Slicing State"), "re_slice"); + const std::vector universal_params = get_params_from_file("universal", m_universal_config); + + auto get_type = [](const std::string& opt_key, const DynamicConfig& config) { + return config.optptr(opt_key)->is_scalar() ? ParamType::Scalar : ParamType::Vector; + }; + + // Add slicing states placeholders + + std::vector read_only_opts = { "zhop" }; + wxDataViewItem group = m_params_list->AppendGroup(_L("Slicing State"), "re_slice"); { - wxDataViewItem read_only = m_params_list->AppendSubGroup(group, _L("Read Only"), "lock_closed"); - m_params_list->AppendParam(read_only, ParamType::Scalar, "zhop"); - } - { - wxDataViewItem read_write = m_params_list->AppendSubGroup(group, _L("Read Write"), "lock_open"); - for (const auto& opt_name : { "position", "e_position"}) - m_params_list->AppendParam(read_write, ParamType::Vector, opt_name); - for (const auto& opt_name : { "e_retracted", "e_restart_extra"}) - m_params_list->AppendParam(read_write, ParamType::FilamentVector, opt_name); + wxDataViewItem read_only = m_params_list->AppendSubGroup(group, _L("Read Only"), "lock_closed"); + for (const auto& opt_key : read_only_opts) + m_params_list->AppendParam(read_only, get_type(opt_key, m_universal_config), opt_key); } - group = m_params_list->AppendGroup(_L("Universal"), "equal"); - { - wxDataViewItem time_stamp = m_params_list->AppendSubGroup(group, _L("Time Stamp"), "time"); - for (const auto& opt_name : { "day", - "hour", - "minute", - "month", - "second", - "year", - "timestamp" }) - m_params_list->AppendParam(time_stamp, ParamType::Scalar, opt_name); - - for (const auto& opt_name : { "current_extruder", - "current_object_idx", - "filament_preset", - "first_layer_print_convex_hull", - "first_layer_print_max", - "first_layer_print_min", - "first_layer_print_size", - "has_single_extruder_multi_material_priming", - "has_wipe_tower", - "initial_extruder", - "initial_tool", - "input_filename", - "input_filename_base", - "is_extruder_used", - "num_instances", - "num_objects", - "physical_printer_preset", - "print_bed_max", - "print_bed_min", - "print_bed_size", - "print_preset", - "printer_preset", - "scale", - "total_layer_count", - "total_toolchanges" }) - m_params_list->AppendParam(group, ParamType::Scalar, opt_name); + const std::vector read_write_params = get_params_from_file("rw_slicing_state", m_read_write_config); + if (!read_write_params.empty()) { + wxDataViewItem read_write = m_params_list->AppendSubGroup(group, _L("Read Write"), "lock_open"); + for (const auto& opt_key : read_write_params) + m_params_list->AppendParam(read_write, get_type(opt_key, m_read_write_config), opt_key); } - std::vector specific_params = get_specific_params(custom_gcode_name); + // Add universal placeholders + + if (!universal_params.empty()) { + group = m_params_list->AppendGroup(_L("Universal"), "equal"); + + // Add print statistics subgroup + + m_print_statistics_config = PrintStatistics::placeholders(); + if (!m_print_statistics_config.empty()) { + wxDataViewItem statistics = m_params_list->AppendSubGroup(group, _L("Print Statistics"), "info"); + const std::vector statistics_params = m_print_statistics_config.keys(); + for (const auto& opt_key : statistics_params) + m_params_list->AppendParam(statistics, get_type(opt_key, m_print_statistics_config), opt_key); + } + + // Add timestamp subgroup + + PlaceholderParser parser; + parser.update_timestamp(); + const DynamicConfig& ts_config = parser.config(); + wxDataViewItem time_stamp = ts_config.empty() ? group : m_params_list->AppendSubGroup(group, _L("Timestamps"), "time"); + + // Add un-grouped params + + wxDataViewItem other = m_params_list->AppendSubGroup(group, _L("Other"), "add_gcode"); + for (const auto& opt_key : universal_params) { + if (m_print_statistics_config.has(opt_key) || std::find(read_only_opts.begin(), read_only_opts.end(), opt_key) != read_only_opts.end()) + continue; + m_params_list->AppendParam( ts_config.has(opt_key) ? time_stamp : other, get_type(opt_key, m_universal_config), opt_key); + } + } + + // Add specific placeholders + + const std::vector specific_params = get_params_from_file(custom_gcode_name, m_specific_config); if (!specific_params.empty()) { group = m_params_list->AppendGroup(format_wxstr(_L("Specific for %1%"), custom_gcode_name), "not_equal"); - for (const auto& opt_name : specific_params) - m_params_list->AppendParam(group, ParamType::Scalar, opt_name); + for (const auto& opt_key : specific_params) + m_params_list->AppendParam(group, get_type(opt_key, m_specific_config), opt_key); + + m_params_list->Expand(group); } - auto get_set_from_vec = [](const std::vector& vec) { + // Add placeholders from presets + + add_presets_placeholders(); +} + +void EditGCodeDialog::add_presets_placeholders() +{ + auto get_set_from_vec = [](const std::vector&vec) { return std::set(vec.begin(), vec.end()); }; - const bool is_fff = wxGetApp().plater()->printer_technology() == ptFFF; + + const bool is_fff = wxGetApp().plater()->printer_technology() == ptFFF; const std::set print_options = get_set_from_vec(is_fff ? Preset::print_options() : Preset::sla_print_options()); const std::set material_options = get_set_from_vec(is_fff ? Preset::filament_options() : Preset::sla_material_options()); const std::set printer_options = get_set_from_vec(is_fff ? Preset::printer_options() : Preset::sla_printer_options()); - const auto& full_config = wxGetApp().preset_bundle->full_config(); + const auto&full_config = wxGetApp().preset_bundle->full_config(); - const auto& def = full_config.def()->get("")->label; + wxDataViewItem group = m_params_list->AppendGroup(_L("Presets"), "cog"); + wxDataViewItem print = m_params_list->AppendSubGroup(group, _L("Print settings"), "cog"); + for (const auto&opt : print_options) + if (const ConfigOption *optptr = full_config.optptr(opt)) + m_params_list->AppendParam(print, optptr->is_scalar() ? ParamType::Scalar : ParamType::Vector, opt); + + wxDataViewItem material = m_params_list->AppendSubGroup(group, _(is_fff ? L("Filament settings") : L("SLA Materials settings")), is_fff ? "spool" : "resin"); + for (const auto&opt : material_options) + if (const ConfigOption *optptr = full_config.optptr(opt)) + m_params_list->AppendParam(material, optptr->is_scalar() ? ParamType::Scalar : ParamType::FilamentVector, opt); - group = m_params_list->AppendGroup(_L("Presets"), "cog"); - { - wxDataViewItem print = m_params_list->AppendSubGroup(group, _L("Print settings"), "cog"); - for (const auto& opt : print_options) - if (const ConfigOption *optptr = full_config.optptr(opt)) - m_params_list->AppendParam(print, optptr->is_scalar() ? ParamType::Scalar : ParamType::Vector, opt); - - wxDataViewItem material = m_params_list->AppendSubGroup(group, _(is_fff ? L("Filament settings") : L("SLA Materials settings")), is_fff ? "spool" : "resin"); - for (const auto& opt : material_options) - if (const ConfigOption *optptr = full_config.optptr(opt)) - m_params_list->AppendParam(material, optptr->is_scalar() ? ParamType::Scalar : ParamType::FilamentVector, opt); - - wxDataViewItem printer = m_params_list->AppendSubGroup(group, _L("Printer settings"), is_fff ? "printer" : "sla_printer"); - for (const auto& opt : printer_options) - if (const ConfigOption *optptr = full_config.optptr(opt)) - m_params_list->AppendParam(printer, optptr->is_scalar() ? ParamType::Scalar : ParamType::Vector, opt); - - } + wxDataViewItem printer = m_params_list->AppendSubGroup(group, _L("Printer settings"), is_fff ? "printer" : "sla_printer"); + for (const auto&opt : printer_options) + if (const ConfigOption *optptr = full_config.optptr(opt)) + m_params_list->AppendParam(printer, optptr->is_scalar() ? ParamType::Scalar : ParamType::Vector, opt); } void EditGCodeDialog::add_selected_value_to_gcode() @@ -227,28 +310,43 @@ void EditGCodeDialog::bind_list_and_button() const std::string opt_key = m_params_list->GetSelectedParamKey(); if (!opt_key.empty()) { + const ConfigOptionDef* cod { nullptr }; + const ConfigOption* optptr { nullptr }; + const auto& full_config = wxGetApp().preset_bundle->full_config(); - if (const ConfigDef* def = full_config.def(); - def && def->has(opt_key)) { - const ConfigOptionDef* cod = def->get(opt_key); - const ConfigOption* optptr = full_config.optptr(opt_key); - const ConfigOptionType type = optptr->type(); + if (const ConfigDef* def = full_config.def(); def && def->has(opt_key)) { + cod = def->get(opt_key); + optptr = full_config.optptr(opt_key); + } + else { + for (const DynamicConfig* config: { &m_read_write_config, &m_universal_config, &m_specific_config, &m_print_statistics_config }) { + optptr = config->optptr(opt_key); + if (optptr) + break; + } + } - wxString type_str = type == coNone ? "none" : - type == coFloat || type == coFloats ? "float" : - type == coInt || type == coInts ? "integer" : - type == coString || type == coStrings ? "string" : - type == coPercent || type == coPercents ? "percent" : - type == coFloatOrPercent || type == coFloatsOrPercents ? "float ar percent" : - type == coPoint || type == coPoints || type == coPoint3 ? "point" : - type == coBool || type == coBools ? "bool" : - type == coEnum ? "enum" : "undef"; + if (optptr) { + const ConfigOptionType scalar_type = optptr->is_scalar() ? optptr->type() : static_cast(optptr->type() - coVectorType); + wxString type_str = scalar_type == coNone ? "none" : + scalar_type == coFloat ? "float" : + scalar_type == coInt ? "integer" : + scalar_type == coString ? "string" : + scalar_type == coPercent ? "percent" : + scalar_type == coFloatOrPercent ? "float or percent" : + scalar_type == coPoint ? "point" : + scalar_type == coBool ? "bool" : + scalar_type == coEnum ? "enum" : "undef"; + if (!optptr->is_scalar()) + type_str += "[]"; - label = ( cod->full_label.empty() && cod->label.empty() ) ? format_wxstr("Undef Label\n(%1%)", type_str) : + label = (!cod || (cod->full_label.empty() && cod->label.empty()) ) ? format_wxstr("%1%\n(%2%)", opt_key, type_str) : (!cod->full_label.empty() && !cod->label.empty() ) ? format_wxstr("%1% > %2%\n(%3%)", _(cod->full_label), _(cod->label), type_str) : format_wxstr("%1%\n(%2%)", cod->label.empty() ? _(cod->full_label) : _(cod->label), type_str); } + else + label = "Undef optptr"; } m_param_label->SetLabel(label); @@ -461,7 +559,7 @@ void ParamsModel::GetValue(wxVariant& variant, const wxDataViewItem& item, unsig assert(item.IsOk()); ParamsNode* node = static_cast(item.GetID()); - if (col == unsigned int(0)) + if (col == (unsigned int)0) #ifdef __linux__ variant << wxDataViewIconText(node->text, get_bmp_bundle(node->icon_name)->GetIconFor(m_ctrl->GetParent())); #else @@ -476,11 +574,11 @@ bool ParamsModel::SetValue(const wxVariant& variant, const wxDataViewItem& item, assert(item.IsOk()); ParamsNode* node = static_cast(item.GetID()); - if (col == unsigned int(0)) { + if (col == (unsigned int)0) { #ifdef __linux__ wxDataViewIconText data; data << variant; - node->m_icon = data.GetIcon(); + node->icon = data.GetIcon(); #else DataViewBitmapText data; data << variant; @@ -559,7 +657,7 @@ ParamsViewCtrl::ParamsViewCtrl(wxWindow *parent, wxSize size) #ifdef SUPPORTS_MARKUP rd->EnableMarkup(true); #endif - wxDataViewColumn* column = new wxDataViewColumn("", rd, 0, width * m_em_unit, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_CELL_INERT); + wxDataViewColumn* column = new wxDataViewColumn("", rd, 0, 20 * m_em_unit, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_CELL_INERT); #else wxDataViewColumn* column = new wxDataViewColumn("", new BitmapTextRenderer(true, wxDATAVIEW_CELL_INERT), 0, 20 * m_em_unit, wxALIGN_TOP, wxDATAVIEW_COL_RESIZABLE); #endif //__linux__ diff --git a/src/slic3r/GUI/EditGCodeDialog.hpp b/src/slic3r/GUI/EditGCodeDialog.hpp index be7746b413..f255190f98 100644 --- a/src/slic3r/GUI/EditGCodeDialog.hpp +++ b/src/slic3r/GUI/EditGCodeDialog.hpp @@ -30,6 +30,11 @@ class EditGCodeDialog : public DPIDialog wxTextCtrl* m_gcode_editor {nullptr}; wxStaticText* m_param_label {nullptr}; + DynamicConfig m_read_write_config; + DynamicConfig m_universal_config; + DynamicConfig m_specific_config; + DynamicConfig m_print_statistics_config; + public: EditGCodeDialog(wxWindow*parent, const std::string&key, const std::string&value); ~EditGCodeDialog() {} @@ -37,6 +42,8 @@ public: std::string get_edited_gcode() const; void init_params_list(const std::string& custom_gcode_name); + void add_presets_placeholders(); + void add_selected_value_to_gcode(); void bind_list_and_button(); From b2bd7f5fbd85c98bd81e8a30c596326a4a14b74f Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 27 Jun 2023 11:29:48 +0200 Subject: [PATCH 085/136] Editing of Custom G-code: Implemented new groping for the params --- src/libslic3r/GCode.hpp | 2 +- src/slic3r/GUI/EditGCodeDialog.cpp | 141 ++++++++++++++++++++++++----- src/slic3r/GUI/EditGCodeDialog.hpp | 4 +- 3 files changed, 120 insertions(+), 27 deletions(-) diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 888055cc9a..c3b5bc8c11 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -71,8 +71,8 @@ struct LayerResult { static LayerResult make_nop_layer_result() { return {"", std::numeric_limits::max(), false, false, true}; } }; +#define GET_CUSTOM_GCODE_PLACEHOLDERS 0 class GCodeGenerator { -#define GET_CUSTOM_GCODE_PLACEHOLDERS 1 public: GCodeGenerator() : diff --git a/src/slic3r/GUI/EditGCodeDialog.cpp b/src/slic3r/GUI/EditGCodeDialog.cpp index 2875ffe7e3..b676f3a492 100644 --- a/src/slic3r/GUI/EditGCodeDialog.cpp +++ b/src/slic3r/GUI/EditGCodeDialog.cpp @@ -138,14 +138,14 @@ EditGCodeDialog::EditGCodeDialog(wxWindow* parent, const std::string& key, const auto* grid_sizer = new wxFlexGridSizer(1, 3, 5, 15); grid_sizer->SetFlexibleDirection(wxBOTH); - m_params_list = new ParamsViewCtrl(this, wxSize(em * 20, em * 30)); + m_params_list = new ParamsViewCtrl(this, wxSize(em * 30, em * 70)); m_params_list->SetFont(wxGetApp().code_font()); wxGetApp().UpdateDarkUI(m_params_list); m_add_btn = new ScalableButton(this, wxID_ANY, "add_copies"); m_add_btn->SetToolTip(_L("Add selected placeholder to G-code")); - m_gcode_editor = new wxTextCtrl(this, wxID_ANY, value, wxDefaultPosition, wxSize(em * 45, em * 30), wxTE_MULTILINE + m_gcode_editor = new wxTextCtrl(this, wxID_ANY, value, wxDefaultPosition, wxSize(em * 75, em * 70), wxTE_MULTILINE #ifdef _WIN32 | wxBORDER_SIMPLE #endif @@ -194,7 +194,11 @@ std::string EditGCodeDialog::get_edited_gcode() const void EditGCodeDialog::init_params_list(const std::string& custom_gcode_name) { - const std::vector universal_params = get_params_from_file("universal", m_universal_config); + const std::vector read_write_params = get_params_from_file("rw_slicing_state", m_read_write_config); + const std::vector universal_params = get_params_from_file("universal", m_universal_config); + const std::vector specific_params = get_params_from_file(custom_gcode_name, m_specific_config); + + m_print_statistics_config = PrintStatistics::placeholders(); auto get_type = [](const std::string& opt_key, const DynamicConfig& config) { return config.optptr(opt_key)->is_scalar() ? ParamType::Scalar : ParamType::Vector; @@ -202,34 +206,100 @@ void EditGCodeDialog::init_params_list(const std::string& custom_gcode_name) // Add slicing states placeholders - std::vector read_only_opts = { "zhop" }; - wxDataViewItem group = m_params_list->AppendGroup(_L("Slicing State"), "re_slice"); - { - wxDataViewItem read_only = m_params_list->AppendSubGroup(group, _L("Read Only"), "lock_closed"); - for (const auto& opt_key : read_only_opts) + std::set read_only_slicing_state_opts = { "zhop" }; + + wxDataViewItem slicing_state = m_params_list->AppendGroup(_L("[Global] Slicing State"), "re_slice"); + if (!universal_params.empty()) { + wxDataViewItem read_only = m_params_list->AppendSubGroup(slicing_state, _L("Read Only"), "lock_closed"); + for (const auto& opt_key : read_only_slicing_state_opts) m_params_list->AppendParam(read_only, get_type(opt_key, m_universal_config), opt_key); } - const std::vector read_write_params = get_params_from_file("rw_slicing_state", m_read_write_config); if (!read_write_params.empty()) { - wxDataViewItem read_write = m_params_list->AppendSubGroup(group, _L("Read Write"), "lock_open"); + wxDataViewItem read_write = m_params_list->AppendSubGroup(slicing_state, _L("Read Write"), "lock_open"); for (const auto& opt_key : read_write_params) m_params_list->AppendParam(read_write, get_type(opt_key, m_read_write_config), opt_key); } + // add other universal params, which are related to slicing state + const std::set other_slicing_state_opts = { "initial_extruder" + , "initial_filament_type" + , "initial_tool" + , "current_extruder" + , "is_extruder_used" + , "current_object_idx" + , "has_single_extruder_multi_material_priming" + , "has_wipe_tower" }; + + slicing_state = m_params_list->AppendGroup(_L("Slicing State"), "re_slice"); + for (const auto& opt_key : other_slicing_state_opts) { + if (m_print_statistics_config.has(opt_key)) + m_params_list->AppendParam(slicing_state, get_type(opt_key, m_print_statistics_config), opt_key); + else if(!universal_params.empty()) + m_params_list->AppendParam(slicing_state, get_type(opt_key, m_universal_config), opt_key); + } + + const std::set other_print_statistics_opts = { "extruded_volume_total" + , "extruded_weight" + , "extruded_weight_total" + , "total_layer_count" }; + + const std::set other_presets_opts = { "filament_preset" + , "physical_printer_preset" + , "printer_preset" + , "print_preset" + , "num_extruders" }; + + const std::set objects_info_opts = { "num_instances" + , "num_objects" + , "scale" + , "input_filename" + , "input_filename_base" }; + + const std::set dimensions_opts = { "first_layer_print_convex_hull" + , "first_layer_print_max" + , "first_layer_print_min" + , "first_layer_print_size" + , "print_bed_max" + , "print_bed_min" + , "print_bed_size" }; + // Add universal placeholders if (!universal_params.empty()) { - group = m_params_list->AppendGroup(_L("Universal"), "equal"); +// wxDataViewItem group = m_params_list->AppendGroup(_L("Universal"), "equal"); // Add print statistics subgroup - m_print_statistics_config = PrintStatistics::placeholders(); if (!m_print_statistics_config.empty()) { - wxDataViewItem statistics = m_params_list->AppendSubGroup(group, _L("Print Statistics"), "info"); +// wxDataViewItem statistics = m_params_list->AppendSubGroup(group, _L("Print Statistics"), "info"); + wxDataViewItem statistics = m_params_list->AppendGroup(_L("Print Statistics"), "info"); const std::vector statistics_params = m_print_statistics_config.keys(); for (const auto& opt_key : statistics_params) - m_params_list->AppendParam(statistics, get_type(opt_key, m_print_statistics_config), opt_key); + if (std::find(other_slicing_state_opts.begin(), other_slicing_state_opts.end(), opt_key) == other_slicing_state_opts.end()) + m_params_list->AppendParam(statistics, get_type(opt_key, m_print_statistics_config), opt_key); + // add other universal params, which are related to print statistics + if (!universal_params.empty()) + for (const auto& opt_key : other_print_statistics_opts) + m_params_list->AppendParam(statistics, get_type(opt_key, m_universal_config), opt_key); + } + + // Add objects info subgroup + + if (!universal_params.empty()) { +// wxDataViewItem objects_info = m_params_list->AppendSubGroup(group, _L("Objects Info"), "advanced_plus"); + wxDataViewItem objects_info = m_params_list->AppendGroup(_L("Objects Info"), "advanced_plus"); + for (const auto& opt_key : objects_info_opts) + m_params_list->AppendParam(objects_info, get_type(opt_key, m_universal_config), opt_key); + } + + // Add objects info subgroup + + if (!universal_params.empty()) { +// wxDataViewItem dimensions = m_params_list->AppendSubGroup(group, _L("Dimensions"), "measure"); + wxDataViewItem dimensions = m_params_list->AppendGroup(_L("Dimensions"), "measure"); + for (const auto& opt_key : dimensions_opts) + m_params_list->AppendParam(dimensions, get_type(opt_key, m_universal_config), opt_key); } // Add timestamp subgroup @@ -237,23 +307,30 @@ void EditGCodeDialog::init_params_list(const std::string& custom_gcode_name) PlaceholderParser parser; parser.update_timestamp(); const DynamicConfig& ts_config = parser.config(); - wxDataViewItem time_stamp = ts_config.empty() ? group : m_params_list->AppendSubGroup(group, _L("Timestamps"), "time"); +// wxDataViewItem time_stamp = ts_config.empty() ? group : m_params_list->AppendSubGroup(group, _L("Timestamps"), "time"); + wxDataViewItem time_stamp = m_params_list->AppendGroup(_L("Timestamps"), "time"); // Add un-grouped params - wxDataViewItem other = m_params_list->AppendSubGroup(group, _L("Other"), "add_gcode"); - for (const auto& opt_key : universal_params) { - if (m_print_statistics_config.has(opt_key) || std::find(read_only_opts.begin(), read_only_opts.end(), opt_key) != read_only_opts.end()) - continue; - m_params_list->AppendParam( ts_config.has(opt_key) ? time_stamp : other, get_type(opt_key, m_universal_config), opt_key); - } +// wxDataViewItem other = m_params_list->AppendSubGroup(group, _L("Other"), "add_gcode"); + wxDataViewItem other = m_params_list->AppendGroup(_L("Other"), "add_gcode"); + for (const auto& opt_key : universal_params) + if (std::find(read_only_slicing_state_opts.begin(), read_only_slicing_state_opts.end(), opt_key)== read_only_slicing_state_opts.end() && + std::find(other_slicing_state_opts.begin(), other_slicing_state_opts.end(), opt_key) == other_slicing_state_opts.end() && + std::find(other_print_statistics_opts.begin(), other_print_statistics_opts.end(), opt_key) == other_print_statistics_opts.end() && + std::find(other_presets_opts.begin(), other_presets_opts.end(), opt_key) == other_presets_opts.end() && + std::find(objects_info_opts.begin(), objects_info_opts.end(), opt_key) == objects_info_opts.end() && + std::find(dimensions_opts.begin(), dimensions_opts.end(), opt_key) == dimensions_opts.end() && + !m_print_statistics_config.has(opt_key)) { + m_params_list->AppendParam(ts_config.has(opt_key) ? time_stamp : other, get_type(opt_key, m_universal_config), opt_key); + } + m_params_list->CheckAndDeleteIfEmpty(other); } // Add specific placeholders - const std::vector specific_params = get_params_from_file(custom_gcode_name, m_specific_config); if (!specific_params.empty()) { - group = m_params_list->AppendGroup(format_wxstr(_L("Specific for %1%"), custom_gcode_name), "not_equal"); + wxDataViewItem group = m_params_list->AppendGroup(format_wxstr(_L("Specific for %1%"), custom_gcode_name), /*"not_equal"*/"add_gcode"); for (const auto& opt_key : specific_params) m_params_list->AppendParam(group, get_type(opt_key, m_specific_config), opt_key); @@ -262,10 +339,14 @@ void EditGCodeDialog::init_params_list(const std::string& custom_gcode_name) // Add placeholders from presets - add_presets_placeholders(); + wxDataViewItem presets = add_presets_placeholders(); + // add other params which are related to presets + if (!universal_params.empty()) + for (const auto& opt_key : other_presets_opts) + m_params_list->AppendParam(presets, get_type(opt_key, m_universal_config), opt_key); } -void EditGCodeDialog::add_presets_placeholders() +wxDataViewItem EditGCodeDialog::add_presets_placeholders() { auto get_set_from_vec = [](const std::vector&vec) { return std::set(vec.begin(), vec.end()); @@ -294,6 +375,8 @@ void EditGCodeDialog::add_presets_placeholders() for (const auto&opt : printer_options) if (const ConfigOption *optptr = full_config.optptr(opt)) m_params_list->AppendParam(printer, optptr->is_scalar() ? ParamType::Scalar : ParamType::Vector, opt); + + return group; } void EditGCodeDialog::add_selected_value_to_gcode() @@ -699,6 +782,14 @@ std::string ParamsViewCtrl::GetSelectedParamKey() return model->GetParamKey(this->GetSelection()); } +void ParamsViewCtrl::CheckAndDeleteIfEmpty(wxDataViewItem item) +{ + wxDataViewItemArray children; + model->GetChildren(item, children); + if (children.IsEmpty()) + model->Delete(item); +} + void ParamsViewCtrl::Clear() { model->Clear(); diff --git a/src/slic3r/GUI/EditGCodeDialog.hpp b/src/slic3r/GUI/EditGCodeDialog.hpp index f255190f98..2acad68598 100644 --- a/src/slic3r/GUI/EditGCodeDialog.hpp +++ b/src/slic3r/GUI/EditGCodeDialog.hpp @@ -42,7 +42,7 @@ public: std::string get_edited_gcode() const; void init_params_list(const std::string& custom_gcode_name); - void add_presets_placeholders(); + wxDataViewItem add_presets_placeholders(); void add_selected_value_to_gcode(); void bind_list_and_button(); @@ -210,6 +210,8 @@ public: wxString GetSelectedValue(); std::string GetSelectedParamKey(); + void CheckAndDeleteIfEmpty(wxDataViewItem item); + void Clear(); void Rescale(int em = 0); From d652f15e48b7a172c7df7a8b461ab055bb81942b Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 21 Aug 2023 15:41:57 +0200 Subject: [PATCH 086/136] WIP: EditGCodeDialog: Add parameter description --- src/slic3r/GUI/EditGCodeDialog.cpp | 9 +++++++++ src/slic3r/GUI/EditGCodeDialog.hpp | 1 + 2 files changed, 10 insertions(+) diff --git a/src/slic3r/GUI/EditGCodeDialog.cpp b/src/slic3r/GUI/EditGCodeDialog.cpp index b676f3a492..028cf99d0e 100644 --- a/src/slic3r/GUI/EditGCodeDialog.cpp +++ b/src/slic3r/GUI/EditGCodeDialog.cpp @@ -164,6 +164,8 @@ EditGCodeDialog::EditGCodeDialog(wxWindow* parent, const std::string& key, const m_param_label = new wxStaticText(this, wxID_ANY, _L("Select placeholder")); m_param_label->SetFont(wxGetApp().bold_font()); + m_param_description = new wxStaticText(this, wxID_ANY, wxEmptyString); + wxStdDialogButtonSizer* btns = this->CreateStdDialogButtonSizer(wxOK | wxCANCEL); wxGetApp().UpdateDarkUI(this->FindWindowById(wxID_OK, this)); wxGetApp().UpdateDarkUI(this->FindWindowById(wxID_CANCEL, this)); @@ -173,6 +175,7 @@ EditGCodeDialog::EditGCodeDialog(wxWindow* parent, const std::string& key, const topSizer->Add(label_top , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); topSizer->Add(grid_sizer , 1, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); topSizer->Add(m_param_label , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); + topSizer->Add(m_param_description , 0, wxEXPAND | wxLEFT | wxTOP | wxRIGHT, border); topSizer->Add(btns , 0, wxEXPAND | wxALL, border); SetSizer(topSizer); @@ -390,6 +393,7 @@ void EditGCodeDialog::bind_list_and_button() { m_params_list->Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, [this](wxDataViewEvent& evt) { wxString label; + wxString description; const std::string opt_key = m_params_list->GetSelectedParamKey(); if (!opt_key.empty()) { @@ -427,12 +431,17 @@ void EditGCodeDialog::bind_list_and_button() (!cod->full_label.empty() && !cod->label.empty() ) ? format_wxstr("%1% > %2%\n(%3%)", _(cod->full_label), _(cod->label), type_str) : format_wxstr("%1%\n(%2%)", cod->label.empty() ? _(cod->full_label) : _(cod->label), type_str); + + if (cod) + description = _(cod->tooltip); } else label = "Undef optptr"; } m_param_label->SetLabel(label); + m_param_description->SetLabel(description); + Layout(); }); diff --git a/src/slic3r/GUI/EditGCodeDialog.hpp b/src/slic3r/GUI/EditGCodeDialog.hpp index 2acad68598..48f14acac0 100644 --- a/src/slic3r/GUI/EditGCodeDialog.hpp +++ b/src/slic3r/GUI/EditGCodeDialog.hpp @@ -29,6 +29,7 @@ class EditGCodeDialog : public DPIDialog ScalableButton* m_add_btn {nullptr}; wxTextCtrl* m_gcode_editor {nullptr}; wxStaticText* m_param_label {nullptr}; + wxStaticText* m_param_description {nullptr}; DynamicConfig m_read_write_config; DynamicConfig m_universal_config; From 2f94681dd28689d746080fabd99c7f9eab19ea84 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 29 Aug 2023 16:53:33 +0200 Subject: [PATCH 087/136] ArcWelder: Fixing out-of-vector access. --- src/libslic3r/Geometry/ArcWelder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/Geometry/ArcWelder.cpp b/src/libslic3r/Geometry/ArcWelder.cpp index 7a5421699d..1bb9fea77d 100644 --- a/src/libslic3r/Geometry/ArcWelder.cpp +++ b/src/libslic3r/Geometry/ArcWelder.cpp @@ -457,7 +457,7 @@ double clip_start(Path &path, const double len) double clip_end(Path &path, double distance) { while (distance > 0) { - Segment &last = path.back(); + const Segment last = path.back(); path.pop_back(); if (path.empty()) break; From 695c08e0e70b5c85e26cfa96e57b6185270b93d0 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 29 Aug 2023 17:02:36 +0200 Subject: [PATCH 088/136] Fix for SPE-1822. Added "Apply to all..." checkbox for question dialog, when loading models looks like multiparts objects --- src/slic3r/GUI/Plater.cpp | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 7d710849fc..3c6c2c7e77 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2469,6 +2469,7 @@ std::vector Plater::priv::load_files(const std::vector& input_ int answer_convert_from_meters = wxOK_DEFAULT; int answer_convert_from_imperial_units = wxOK_DEFAULT; + int answer_consider_as_multi_part_objects = wxOK_DEFAULT; bool in_temp = false; const fs::path temp_path = wxStandardPaths::Get().GetTempDir().utf8_str().data(); @@ -2650,14 +2651,21 @@ std::vector Plater::priv::load_files(const std::vector& input_ } if (model.looks_like_multipart_object()) { - MessageDialog msg_dlg(q, _L( - "This file contains several objects positioned at multiple heights.\n" - "Instead of considering them as multiple objects, should \n" - "the file be loaded as a single object having multiple parts?") + "\n", - _L("Multi-part object detected"), wxICON_WARNING | wxYES | wxNO); - if (msg_dlg.ShowModal() == wxID_YES) { - model.convert_multipart_object(nozzle_dmrs->values.size()); + if (answer_consider_as_multi_part_objects == wxOK_DEFAULT) { + RichMessageDialog dlg(q, _L( + "This file contains several objects positioned at multiple heights.\n" + "Instead of considering them as multiple objects, should \n" + "the file be loaded as a single object having multiple parts?") + "\n", + _L("Multi-part object detected"), wxICON_QUESTION | wxYES_NO); + dlg.ShowCheckBox(_L("Apply to all multiple objects being loaded.")); + int answer = dlg.ShowModal(); + if (dlg.IsCheckBoxChecked()) + answer_consider_as_multi_part_objects = answer; + if (answer == wxID_YES) + model.convert_multipart_object(nozzle_dmrs->size()); } + else if (answer_consider_as_multi_part_objects == wxID_YES) + model.convert_multipart_object(nozzle_dmrs->size()); } } if ((wxGetApp().get_mode() == comSimple) && (type_3mf || type_any_amf) && model_has_advanced_features(model)) { @@ -3567,9 +3575,9 @@ bool Plater::priv::replace_volume_with_stl(int object_idx, int volume_idx, const new_volume->convert_from_imperial_units(); else if (old_volume->source.is_converted_from_meters) new_volume->convert_from_meters(); - new_volume->supported_facets.assign(old_volume->supported_facets); - new_volume->seam_facets.assign(old_volume->seam_facets); - new_volume->mmu_segmentation_facets.assign(old_volume->mmu_segmentation_facets); + new_volume->supported_facets.assign(old_volume->supported_facets); + new_volume->seam_facets.assign(old_volume->seam_facets); + new_volume->mmu_segmentation_facets.assign(old_volume->mmu_segmentation_facets); std::swap(old_model_object->volumes[volume_idx], old_model_object->volumes.back()); old_model_object->delete_volume(old_model_object->volumes.size() - 1); if (!sinking) From 015a24133aa19bfa1a5e5ff470cf5ed95a284117 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Wed, 30 Aug 2023 12:51:35 +0200 Subject: [PATCH 089/136] Fixed some compilation warnings. --- src/libslic3r/GCode.cpp | 4 + src/libslic3r/GCode/GCodeProcessor.cpp | 8 +- src/libslic3r/PlaceholderParser.cpp | 2 +- src/libslic3r/Support/TreeSupport.cpp | 2 + src/libslic3r/TriangleMeshSlicer.cpp | 10 +- src/slic3r/GUI/ConfigWizard.cpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 4 +- src/slic3r/GUI/PresetComboBoxes.cpp | 2 +- tests/fff_print/test_data.cpp | 128 +++++++++++++++---------- 9 files changed, 95 insertions(+), 67 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 35d1386ac1..a54e27aeeb 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1025,6 +1025,10 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail this->placeholder_parser().set("total_toolchanges", std::max(0, print.wipe_tower_data().number_of_toolchanges)); // Check for negative toolchanges (single extruder mode) and set to 0 (no tool change). { BoundingBoxf bbox(print.config().bed_shape.values); + assert(bbox.defined); + if (! bbox.defined) + // This should not happen, but let's make the compiler happy. + bbox.min = bbox.max = Vec2d::Zero(); this->placeholder_parser().set("print_bed_min", new ConfigOptionFloats({ bbox.min.x(), bbox.min.y() })); this->placeholder_parser().set("print_bed_max", new ConfigOptionFloats({ bbox.max.x(), bbox.max.y() })); this->placeholder_parser().set("print_bed_size", new ConfigOptionFloats({ bbox.size().x(), bbox.size().y() })); diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 40ea2e6903..ecd59987d8 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -2711,16 +2711,16 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line, bool cloc // relative center Vec3f rel_center = Vec3f::Zero(); -#ifndef _NDEBUG +#ifndef NDEBUG double radius = 0.0; -#endif // _NDEBUG +#endif // NDEBUG if (fitting == EFitting::R) { float r; if (!line.has_value('R', r) || r == 0.0f) return; -#ifndef _NDEBUG +#ifndef NDEBUG radius = (double)std::abs(r); -#endif // _NDEBUG +#endif // NDEBUG const Vec2f start_pos((float)m_start_position[X], (float)m_start_position[Y]); const Vec2f end_pos((float)end_position[X], (float)end_position[Y]); const Vec2f c = Geometry::ArcWelder::arc_center(start_pos, end_pos, r, !clockwise); diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index 40126421c4..a9ecdb98ab 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -2098,7 +2098,7 @@ namespace client multiplicative_expression.name("multiplicative_expression"); assignment_statement = - variable_reference(_r1)[_a = _1] >> '=' > + (variable_reference(_r1)[_a = _1] >> '=') > ( // Consumes also '(' conditional_expression ')', that means enclosing an expression into braces makes it a single value vector initializer. initializer_list(_r1)[px::bind(&MyContext::vector_variable_assign_initializer_list, _r1, _a, _1)] // Process it before conditional_expression, as conditional_expression requires a vector reference to be augmented with an index. diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index af7748e024..4f63cafce2 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -101,6 +101,7 @@ static inline void validate_range(const LineInformations &lines) validate_range(l); } +/* static inline void check_self_intersections(const Polygons &polygons, const std::string_view message) { #ifdef TREE_SUPPORT_SHOW_ERRORS_WIN32 @@ -108,6 +109,7 @@ static inline void check_self_intersections(const Polygons &polygons, const std: ::MessageBoxA(nullptr, (std::string("TreeSupport infill self intersections: ") + std::string(message)).c_str(), "Bug detected!", MB_OK | MB_SYSTEMMODAL | MB_SETFOREGROUND | MB_ICONWARNING); #endif // TREE_SUPPORT_SHOW_ERRORS_WIN32 } +*/ static inline void check_self_intersections(const ExPolygon &expoly, const std::string_view message) { #ifdef TREE_SUPPORT_SHOW_ERRORS_WIN32 diff --git a/src/libslic3r/TriangleMeshSlicer.cpp b/src/libslic3r/TriangleMeshSlicer.cpp index da696e1ecb..79b2f31449 100644 --- a/src/libslic3r/TriangleMeshSlicer.cpp +++ b/src/libslic3r/TriangleMeshSlicer.cpp @@ -1939,7 +1939,7 @@ std::vector slice_mesh_ex( &expolygons); #if 0 -//#ifndef _NDEBUG +//#ifndef NDEBUG // Test whether the expolygons in a single layer overlap. for (size_t i = 0; i < expolygons.size(); ++ i) for (size_t j = i + 1; j < expolygons.size(); ++ j) { @@ -1948,7 +1948,7 @@ std::vector slice_mesh_ex( } #endif #if 0 -//#ifndef _NDEBUG +//#ifndef NDEBUG for (const ExPolygon &ex : expolygons) { assert(! has_duplicate_points(ex.contour)); for (const Polygon &hole : ex.holes) @@ -1956,7 +1956,7 @@ std::vector slice_mesh_ex( assert(! has_duplicate_points(ex)); } assert(!has_duplicate_points(expolygons)); -#endif // _NDEBUG +#endif // NDEBUG //FIXME simplify if (this_mode == MeshSlicingParams::SlicingMode::PositiveLargestContour) keep_largest_contour_only(expolygons); @@ -1968,7 +1968,7 @@ std::vector slice_mesh_ex( expolygons = std::move(simplified); } #if 0 -//#ifndef _NDEBUG +//#ifndef NDEBUG for (const ExPolygon &ex : expolygons) { assert(! has_duplicate_points(ex.contour)); for (const Polygon &hole : ex.holes) @@ -1976,7 +1976,7 @@ std::vector slice_mesh_ex( assert(! has_duplicate_points(ex)); } assert(! has_duplicate_points(expolygons)); -#endif // _NDEBUG +#endif // NDEBUG } }); // BOOST_LOG_TRIVIAL(debug) << "slice_mesh make_expolygons in parallel - end"; diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index 4f211323aa..5d671cea1c 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -2334,7 +2334,7 @@ void ConfigWizard::priv::load_pages() return a.first < b.first; }); - for (const std::pair v : sorted_vendors) { + for (const std::pair &v : sorted_vendors) { const auto& pages = pages_3rdparty.find(v.second); if (pages == pages_3rdparty.end()) continue; // Should not happen diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 885f9d97a2..b38a0835db 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -499,10 +499,10 @@ bool GLGizmoCut3D::render_double_input(const std::string& label, double& value_i bool GLGizmoCut3D::render_slider_double_input(const std::string& label, float& value_in, float& tolerance_in) { - constexpr float UndefMinVal = -0.1f; + constexpr const float UndefMinVal = -0.1f; const float f_mm_to_in = static_cast(ObjectManipulation::mm_to_in); - auto render_slider = [this, UndefMinVal, f_mm_to_in] + auto render_slider = [this, f_mm_to_in] (const std::string& label, float& val, float def_val, float max_val, const wxString& tooltip) { float min_val = val < 0.f ? UndefMinVal : def_val; float value = val; diff --git a/src/slic3r/GUI/PresetComboBoxes.cpp b/src/slic3r/GUI/PresetComboBoxes.cpp index ac189f8268..17850a50f5 100644 --- a/src/slic3r/GUI/PresetComboBoxes.cpp +++ b/src/slic3r/GUI/PresetComboBoxes.cpp @@ -785,7 +785,7 @@ void PlaterPresetComboBox::show_edit_menu() [this](wxCommandEvent&) { this->change_extruder_color(); }, "funnel", menu, []() { return true; }, wxGetApp().plater()); #endif //__linux__ append_menu_item(menu, wxID_ANY, _L("Show/Hide template presets"), "", - [this](wxCommandEvent&) { wxGetApp().open_preferences("no_templates", "General"); }, "spool", menu, []() { return true; }, wxGetApp().plater()); + [](wxCommandEvent&) { wxGetApp().open_preferences("no_templates", "General"); }, "spool", menu, []() { return true; }, wxGetApp().plater()); wxGetApp().plater()->PopupMenu(menu); return; diff --git a/tests/fff_print/test_data.cpp b/tests/fff_print/test_data.cpp index 6d87560579..23dc03ce45 100644 --- a/tests/fff_print/test_data.cpp +++ b/tests/fff_print/test_data.cpp @@ -59,88 +59,110 @@ TriangleMesh mesh(TestMesh m) break; case TestMesh::cube_with_hole: mesh = TriangleMesh( - { {0,0,0}, {0,0,10}, {0,20,0}, {0,20,10}, {20,0,0}, {20,0,10}, {5,5,0}, {15,5,0}, {5,15,0}, {20,20,0}, {15,15,0}, {20,20,10}, {5,5,10}, {5,15,10}, {15,5,10}, {15,15,10} }, - { {0,1,2}, {2,1,3}, {1,0,4}, {5,1,4}, {6,7,4}, {8,2,9}, {0,2,8}, {10,8,9}, {0,8,6}, {0,6,4}, {4,7,9}, {7,10,9}, {2,3,9}, {9,3,11}, {12,1,5}, {13,3,12}, {14,12,5}, {3,1,12}, {11,3,13}, {11,15,5}, {11,13,15}, {15,14,5}, {5,4,9}, {11,5,9}, {8,13,12}, {6,8,12}, {10,15,13}, {8,10,13}, {15,10,14}, {14,10,7}, {14,7,12}, {12,7,6} }); + { {0.f,0.f,0.f}, {0.f,0.f,10.f}, {0.f,20.f,0.f}, {0.f,20.f,10.f}, {20.f,0.f,0.f}, {20.f,0.f,10.f}, {5.f,5.f,0.f}, {15.f,5.f,0.f}, {5.f,15.f,0.f}, {20.f,20.f,0.f}, {15.f,15.f,0.f}, {20.f,20.f,10.f}, {5.f,5.f,10.f}, {5.f,15.f,10.f}, {15.f,5.f,10.f}, {15.f,15.f,10.f} }, + { {0,1,2}, {2,1,3}, {1,0,4}, {5,1,4}, {6,7,4}, {8,2,9}, {0,2,8}, {10,8,9}, {0,8,6}, {0,6,4}, {4,7,9}, {7,10,9}, {2,3,9}, {9,3,11}, {12,1,5}, {13,3,12}, {14,12,5}, {3,1,12}, {11,3,13}, {11,15,5}, {11,13,15}, {15,14,5}, {5,4,9}, {11,5,9}, {8,13,12}, {6,8,12}, {10,15,13}, {8,10,13}, {15,10,14}, {14,10,7}, {14,7,12}, {12,7,6} }); break; case TestMesh::cube_with_concave_hole: mesh = TriangleMesh( - { {-10,-10,-5}, {-10,-10,5}, {-10,10,-5}, {-10,10,5}, {10,-10,-5}, {10,-10,5}, {-5,-5,-5}, {5,-5,-5}, {5,5,-5}, {5,10,-5}, {-5,5,-5}, Vec3f(3.06161699911402e-16f,5,-5), - {5,0,-5}, {0,0,-5}, {10,5,-5}, {5,10,5}, {-5,-5,5}, {5,0,5}, {5,-5,5}, {-5,5,5}, {10,5,5}, {5,5,5}, Vec3f(3.06161699911402e-16f,5,5), {0,0,5} }, - { {0,1,2}, {2,1,3}, {1,0,4}, {5,1,4}, {6,7,4}, {8,2,9}, {10,2,11}, {11,12,13}, {0,2,10}, {0,10,6}, {0,6,4}, {11,2,8}, {4,7,12}, {4,12,8}, {12,11,8}, {14,4,8}, {2,3,9}, {9,3,15}, {16,1,5}, {17,18,5}, {19,3,16}, {20,21,5}, {18,16,5}, {3,1,16}, {22,3,19}, {21,3,22}, {21,17,5}, {21,22,17}, {21,15,3}, {23,17,22}, {5,4,14}, {20,5,14}, {20,14,21}, {21,14,8}, {9,15,21}, {8,9,21}, {10,19,16}, {6,10,16}, {11,22,19}, {10,11,19}, {13,23,11}, {11,23,22}, {23,13,12}, {17,23,12}, {17,12,18}, {18,12,7}, {18,7,16}, {16,7,6} }); + { {-10.f,-10.f,-5.f}, {-10.f,-10.f,5.f}, {-10.f,10.f,-5.f}, {-10.f,10.f,5.f}, {10.f,-10.f,-5.f}, {10.f,-10.f,5.f}, {-5.f,-5.f,-5.f}, {5.f,-5.f,-5.f}, {5.f,5.f,-5.f}, {5.f,10.f,-5.f}, {-5.f,5.f,-5.f}, {float(3.06161699911402e-16),5.f,-5.f}, + {5.f,0.f,-5.f}, {0.f,0.f,-5.f}, {10.f,5.f,-5.f}, {5.f,10.f,5.f}, {-5.f,-5.f,5.f}, {5.f,0.f,5.f}, {5.f,-5.f,5.f}, {-5.f,5.f,5.f}, {10.f,5.f,5.f}, {5.f,5.f,5.f}, {float(3.06161699911402e-16),5.f,5.f}, {0.f,0.f,5.f} }, + { {0,1,2}, {2,1,3}, {1,0,4}, {5,1,4}, {6,7,4}, {8,2,9}, {10,2,11}, {11,12,13}, {0,2,10}, {0,10,6}, {0,6,4}, {11,2,8}, {4,7,12}, {4,12,8}, {12,11,8}, {14,4,8}, {2,3,9}, {9,3,15}, {16,1,5}, {17,18,5}, {19,3,16}, {20,21,5}, {18,16,5}, {3,1,16}, {22,3,19}, {21,3,22}, {21,17,5}, {21,22,17}, {21,15,3}, {23,17,22}, {5,4,14}, {20,5,14}, {20,14,21}, {21,14,8}, {9,15,21}, {8,9,21}, {10,19,16}, {6,10,16}, {11,22,19}, {10,11,19}, {13,23,11}, {11,23,22}, {23,13,12}, {17,23,12}, {17,12,18}, {18,12,7}, {18,7,16}, {16,7,6} }); break; case TestMesh::V: mesh = TriangleMesh( - { {-14,0,20}, {-14,15,20}, {0,0,0}, {0,15,0}, {-4,0,20}, {-4,15,20}, {5,0,7.14286f}, {10,0,0}, {24,0,20}, {14,0,20}, {10,15,0}, {5,15,7.14286f}, {14,15,20}, {24,15,20} }, - { {0,1,2}, {2,1,3}, {1,0,4}, {5,1,4}, {4,0,2}, {6,4,2}, {7,6,2}, {8,9,7}, {9,6,7}, {2,3,7}, {7,3,10}, {1,5,3}, {3,5,11}, {11,12,13}, {11,13,3}, {3,13,10}, {5,4,6}, {11,5,6}, {6,9,11}, {11,9,12}, {12,9,8}, {13,12,8}, {8,7,10}, {13,8,10} }); + { {-14.f,0.f,20.f}, {-14.f,15.f,20.f}, {0.f,0.f,0.f}, {0.f,15.f,0.f}, {-4.f,0.f,20.f}, {-4.f,15.f,20.f}, {5.f,0.f,7.14286f}, {10.f,0.f,0.f}, {24.f,0.f,20.f}, {14.f,0.f,20.f}, {10.f,15.f,0.f}, {5.f,15.f,7.14286f}, {14.f,15.f,20.f}, {24.f,15.f,20.f} }, + { {0,1,2}, {2,1,3}, {1,0,4}, {5,1,4}, {4,0,2}, {6,4,2}, {7,6,2}, {8,9,7}, {9,6,7}, {2,3,7}, {7,3,10}, {1,5,3}, {3,5,11}, {11,12,13}, {11,13,3}, {3,13,10}, {5,4,6}, {11,5,6}, {6,9,11}, {11,9,12}, {12,9,8}, {13,12,8}, {8,7,10}, {13,8,10} }); break; case TestMesh::L: mesh = TriangleMesh( - { {0,10,0}, {0,10,10}, {0,20,0}, {0,20,10}, {10,10,0}, {10,10,10}, {20,20,0}, {20,0,0}, {10,0,0}, {20,20,10}, {10,0,10}, {20,0,10} }, - { {0,1,2}, {2,1,3}, {4,5,1}, {0,4,1}, {0,2,4}, {4,2,6}, {4,6,7}, {4,7,8}, {2,3,6}, {6,3,9}, {3,1,5}, {9,3,5}, {10,11,5}, {11,9,5}, {5,4,10}, {10,4,8}, {10,8,7}, {11,10,7}, {11,7,6}, {9,11,6} }); + { {0.f,10.f,0.f}, {0.f,10.f,10.f}, {0.f,20.f,0.f}, {0.f,20.f,10.f}, {10.f,10.f,0.f}, {10.f,10.f,10.f}, {20.f,20.f,0.f}, {20.f,0.f,0.f}, {10.f,0.f,0.f}, {20.f,20.f,10.f}, {10.f,0.f,10.f}, {20.f,0.f,10.f} }, + { {0,1,2}, {2,1,3}, {4,5,1}, {0,4,1}, {0,2,4}, {4,2,6}, {4,6,7}, {4,7,8}, {2,3,6}, {6,3,9}, {3,1,5}, {9,3,5}, {10,11,5}, {11,9,5}, {5,4,10}, {10,4,8}, {10,8,7}, {11,10,7}, {11,7,6}, {9,11,6} }); break; case TestMesh::overhang: - mesh = TriangleMesh( - { {1364.68505859375,614.398010253906,20.002498626709}, {1389.68505859375,614.398010253906,20.002498626709}, {1377.18505859375,589.398986816406,20.002498626709}, {1389.68505859375,589.398986816406,20.002498626709}, {1389.68505859375,564.398986816406,20.0014991760254}, {1364.68505859375,589.398986816406,20.002498626709}, {1364.68505859375,564.398986816406,20.0014991760254}, {1360.93505859375,589.398986816406,17.0014991760254}, {1360.93505859375,585.64697265625,17.0014991760254}, {1357.18505859375,564.398986816406,17.0014991760254}, {1364.68505859375,589.398986816406,17.0014991760254}, {1364.68505859375,571.899963378906,17.0014991760254}, {1364.68505859375,564.398986816406,17.0014991760254}, {1348.43603515625,564.398986816406,17.0014991760254}, {1352.80908203125,589.398986816406,17.0014991760254}, {1357.18408203125,589.398986816406,17.0014991760254}, {1357.18310546875,614.398010253906,17.0014991760254}, {1364.68505859375,606.89599609375,17.0014991760254}, {1364.68505859375,614.398010253906,17.0014991760254}, {1352.18603515625,564.398986816406,20.0014991760254}, {1363.65405273438,589.398986816406,23.3004989624023}, {1359.46704101562,589.398986816406,23.3004989624023}, {1358.37109375,564.398986816406,23.3004989624023}, {1385.56103515625,564.398986816406,23.3004989624023}, {1373.06311035156,589.398986816406,23.3004989624023}, {1368.80810546875,564.398986816406,23.3004989624023}, {1387.623046875,589.398986816406,23.3004989624023}, {1387.623046875,585.276000976562,23.3004989624023}, {1389.68505859375,589.398986816406,23.3004989624023}, {1389.68505859375,572.64599609375,23.3004989624023}, {1389.68505859375,564.398986816406,23.3004989624023}, {1367.77709960938,589.398986816406,23.3004989624023}, {1366.7470703125,564.398986816406,23.3004989624023}, {1354.31201171875,589.398986816406,23.3004989624023}, {1352.18603515625,564.398986816406,23.3004989624023}, {1389.68505859375,614.398010253906,23.3004989624023}, {1377.31701660156,614.398010253906,23.3004989624023}, {1381.43908691406,589.398986816406,23.3004989624023}, {1368.80700683594,614.398010253906,23.3004989624023}, {1368.80810546875,589.398986816406,23.3004989624023}, {1356.43908691406,614.398010253906,23.3004989624023}, {1357.40502929688,589.398986816406,23.3004989624023}, {1360.56201171875,614.398010253906,23.3004989624023}, {1348.705078125,614.398010253906,23.3004989624023}, {1350.44506835938,589.398986816406,23.3004989624023}, {1389.68505859375,606.153015136719,23.3004989624023}, {1347.35205078125,589.398986816406,23.3004989624023}, {1346.56005859375,589.398986816406,23.3004989624023}, {1346.56005859375,594.159912109375,17.0014991760254}, {1346.56005859375,589.398986816406,17.0014991760254}, {1346.56005859375,605.250427246094,23.3004989624023}, {1346.56005859375,614.398010253906,23.3004989624023}, {1346.56005859375,614.398010253906,20.8258285522461}, {1346.56005859375,614.398010253906,17.0014991760254}, {1346.56005859375,564.398986816406,19.10133934021}, {1346.56005859375,567.548583984375,23.3004989624023}, {1346.56005859375,564.398986816406,17.0020332336426}, {1346.56005859375,564.398986816406,23.0018501281738}, {1346.56005859375,564.398986816406,23.3004989624023}, {1346.56005859375,575.118957519531,17.0014991760254}, {1346.56005859375,574.754028320312,23.3004989624023} }, - { {0,1,2}, {2,3,4}, {2,5,0}, {4,6,2}, {2,6,5}, {2,1,3}, {7,8,9}, {10,9,8}, {11,9,10}, {12,9,11}, {9,13,14}, {7,15,16}, {10,17,0}, {10,0,5}, {12,11,6}, {18,16,0}, {6,19,13}, {6,13,9}, {9,12,6}, {17,18,0}, {11,10,5}, {11,5,6}, {14,16,15}, {17,7,18}, {16,18,7}, {14,15,9}, {7,9,15}, {7,17,8}, {10,8,17}, {20,21,22}, {23,24,25}, {26,23,27}, {28,27,23}, {29,28,23}, {30,29,23}, {25,31,32}, {22,33,34}, {35,36,37}, {24,38,39}, {21,40,41}, {38,42,20}, {33,43,44}, {6,4,23}, {6,23,25}, {36,35,1}, {1,0,38}, {1,38,36}, {29,30,4}, {25,32,6}, {40,42,0}, {35,45,1}, {4,3,28}, {4,28,29}, {3,1,45}, {3,45,28}, {22,34,19}, {19,6,32}, {19,32,22}, {42,38,0}, {30,23,4}, {0,16,43}, {0,43,40}, {24,37,36}, {38,24,36}, {24,23,37}, {37,23,26}, {22,32,20}, {20,32,31}, {33,41,40}, {43,33,40}, {45,35,26}, {37,26,35}, {33,44,34}, {44,43,46}, {20,42,21}, {40,21,42}, {31,39,38}, {20,31,38}, {33,22,41}, {21,41,22}, {31,25,39}, {24,39,25}, {26,27,45}, {28,45,27}, {47,48,49}, {47,50,48}, {51,48,50}, {52,48,51}, {53,48,52}, {54,55,56}, {57,55,54}, {58,55,57}, {49,59,47}, {60,56,55}, {59,56,60}, {60,47,59}, {48,53,16}, {56,13,19}, {54,56,19}, {56,59,13}, {59,49,14}, {59,14,13}, {49,48,16}, {49,16,14}, {44,46,60}, {44,60,55}, {51,50,43}, {19,34,58}, {19,58,57}, {53,52,16}, {43,16,52}, {43,52,51}, {57,54,19}, {47,60,46}, {55,58,34}, {55,34,44}, {50,47,46}, {50,46,43} }); + mesh = TriangleMesh( + { {1364.68505859375f,614.398010253906f,20.002498626709f}, {1389.68505859375f,614.398010253906f,20.002498626709f}, {1377.18505859375f,589.398986816406f,20.002498626709f}, {1389.68505859375f,589.398986816406f,20.002498626709f}, {1389.68505859375f,564.398986816406f,20.0014991760254f}, {1364.68505859375f,589.398986816406f,20.002498626709f}, {1364.68505859375f,564.398986816406f,20.0014991760254f}, {1360.93505859375f,589.398986816406f,17.0014991760254f}, {1360.93505859375f,585.64697265625f,17.0014991760254f}, {1357.18505859375f,564.398986816406f,17.0014991760254f}, {1364.68505859375f,589.398986816406f,17.0014991760254f}, {1364.68505859375f,571.899963378906f,17.0014991760254f}, {1364.68505859375f,564.398986816406f,17.0014991760254f}, {1348.43603515625f,564.398986816406f,17.0014991760254f}, {1352.80908203125f,589.398986816406f,17.0014991760254f}, {1357.18408203125f,589.398986816406f,17.0014991760254f}, {1357.18310546875f,614.398010253906f,17.0014991760254f}, {1364.68505859375f,606.89599609375f,17.0014991760254f}, {1364.68505859375f,614.398010253906f,17.0014991760254f}, {1352.18603515625f,564.398986816406f,20.0014991760254f}, {1363.65405273438f,589.398986816406f,23.3004989624023f}, {1359.46704101562f,589.398986816406f,23.3004989624023f}, {1358.37109375f,564.398986816406f,23.3004989624023f}, {1385.56103515625f,564.398986816406f,23.3004989624023f}, {1373.06311035156f,589.398986816406f,23.3004989624023f}, {1368.80810546875f,564.398986816406f,23.3004989624023f}, {1387.623046875f,589.398986816406f,23.3004989624023f}, {1387.623046875f,585.276000976562f,23.3004989624023f}, {1389.68505859375f,589.398986816406f,23.3004989624023f}, {1389.68505859375f,572.64599609375f,23.3004989624023f}, {1389.68505859375f,564.398986816406f,23.3004989624023f}, {1367.77709960938f,589.398986816406f,23.3004989624023f}, {1366.7470703125f,564.398986816406f,23.3004989624023f}, {1354.31201171875f,589.398986816406f,23.3004989624023f}, {1352.18603515625f,564.398986816406f,23.3004989624023f}, {1389.68505859375f,614.398010253906f,23.3004989624023f}, {1377.31701660156f,614.398010253906f,23.3004989624023f}, {1381.43908691406f,589.398986816406f,23.3004989624023f}, {1368.80700683594f,614.398010253906f,23.3004989624023f}, {1368.80810546875f,589.398986816406f,23.3004989624023f}, {1356.43908691406f,614.398010253906f,23.3004989624023f}, {1357.40502929688f,589.398986816406f,23.3004989624023f}, {1360.56201171875f,614.398010253906f,23.3004989624023f}, {1348.705078125f,614.398010253906f,23.3004989624023f}, {1350.44506835938f,589.398986816406f,23.3004989624023f}, {1389.68505859375f,606.153015136719f,23.3004989624023f}, {1347.35205078125f,589.398986816406f,23.3004989624023f}, {1346.56005859375f,589.398986816406f,23.3004989624023f}, {1346.56005859375f,594.159912109375f,17.0014991760254f}, {1346.56005859375f,589.398986816406f,17.0014991760254f}, {1346.56005859375f,605.250427246094f,23.3004989624023f}, {1346.56005859375f,614.398010253906f,23.3004989624023f}, {1346.56005859375f,614.398010253906f,20.8258285522461f}, {1346.56005859375f,614.398010253906f,17.0014991760254f}, {1346.56005859375f,564.398986816406f,19.10133934021f}, {1346.56005859375f,567.548583984375f,23.3004989624023f}, {1346.56005859375f,564.398986816406f,17.0020332336426f}, {1346.56005859375f,564.398986816406f,23.0018501281738f}, {1346.56005859375f,564.398986816406f,23.3004989624023f}, {1346.56005859375f,575.118957519531f,17.0014991760254f}, {1346.56005859375f,574.754028320312f,23.3004989624023f} }, + { {0,1,2}, {2,3,4}, {2,5,0}, {4,6,2}, {2,6,5}, {2,1,3}, {7,8,9}, {10,9,8}, {11,9,10}, {12,9,11}, {9,13,14}, {7,15,16}, {10,17,0}, {10,0,5}, {12,11,6}, {18,16,0}, {6,19,13}, {6,13,9}, {9,12,6}, {17,18,0}, {11,10,5}, {11,5,6}, {14,16,15}, {17,7,18}, {16,18,7}, {14,15,9}, {7,9,15}, {7,17,8}, {10,8,17}, {20,21,22}, {23,24,25}, {26,23,27}, {28,27,23}, {29,28,23}, {30,29,23}, {25,31,32}, {22,33,34}, {35,36,37}, {24,38,39}, {21,40,41}, {38,42,20}, {33,43,44}, {6,4,23}, {6,23,25}, {36,35,1}, {1,0,38}, {1,38,36}, {29,30,4}, {25,32,6}, {40,42,0}, {35,45,1}, {4,3,28}, {4,28,29}, {3,1,45}, {3,45,28}, {22,34,19}, {19,6,32}, {19,32,22}, {42,38,0}, {30,23,4}, {0,16,43}, {0,43,40}, {24,37,36}, {38,24,36}, {24,23,37}, {37,23,26}, {22,32,20}, {20,32,31}, {33,41,40}, {43,33,40}, {45,35,26}, {37,26,35}, {33,44,34}, {44,43,46}, {20,42,21}, {40,21,42}, {31,39,38}, {20,31,38}, {33,22,41}, {21,41,22}, {31,25,39}, {24,39,25}, {26,27,45}, {28,45,27}, {47,48,49}, {47,50,48}, {51,48,50}, {52,48,51}, {53,48,52}, {54,55,56}, {57,55,54}, {58,55,57}, {49,59,47}, {60,56,55}, {59,56,60}, {60,47,59}, {48,53,16}, {56,13,19}, {54,56,19}, {56,59,13}, {59,49,14}, {59,14,13}, {49,48,16}, {49,16,14}, {44,46,60}, {44,60,55}, {51,50,43}, {19,34,58}, {19,58,57}, {53,52,16}, {43,16,52}, {43,52,51}, {57,54,19}, {47,60,46}, {55,58,34}, {55,34,44}, {50,47,46}, {50,46,43} }); break; case TestMesh::_40x10: mesh = TriangleMesh( - { {12.8680295944214,29.5799007415771,12}, {11.7364797592163,29.8480796813965,12}, {11.1571502685547,29.5300102233887,12}, {10.5814504623413,29.9830799102783,12}, {10,29.6000003814697,12}, {9.41855144500732,29.9830799102783,12}, {8.84284687042236,29.5300102233887,12}, {8.26351833343506,29.8480796813965,12}, {7.70256900787354,29.3210391998291,12}, {7.13196802139282,29.5799007415771,12}, {6.59579277038574,28.9761600494385,12}, {6.03920221328735,29.1821594238281,12}, {5.53865718841553,28.5003795623779,12}, {5,28.6602592468262,12}, {4.54657793045044,27.9006500244141,12}, {4.02841377258301,28.0212306976318,12}, {3.63402199745178,27.1856994628906,12}, {3.13758301734924,27.2737407684326,12}, {2.81429696083069,26.3659801483154,12}, {2.33955597877502,26.4278793334961,12}, {2.0993549823761,25.4534206390381,12}, {1.64512205123901,25.4950904846191,12}, {1.49962198734283,24.4613399505615,12}, {1.0636739730835,24.4879894256592,12}, {1.02384400367737,23.4042091369629,12}, {0.603073298931122,23.4202003479004,12}, {0.678958415985107,22.2974300384521,12}, {0.269550800323486,22.3061599731445,12}, {0.469994693994522,21.1571502685547,12}, {0.067615881562233,21.1609306335449,12}, {0.399999290704727,20,12}, {0,20,12}, {0.399999290704727,5,12}, {0,5,12}, {0.456633001565933,4.2804012298584,12}, {0.0615576282143593,4.21782684326172,12}, {0.625140011310577,3.5785219669342,12}, {0.244717106223106,3.45491504669189,12}, {0.901369392871857,2.91164398193359,12}, {0.544967114925385,2.73004698753357,12}, {1.27852201461792,2.29618692398071,12}, {0.954914808273315,2.06107401847839,12}, {1.74730801582336,1.74730801582336,12}, {1.46446597576141,1.46446597576141,12}, {2.29618692398071,1.27852201461792,12}, {2.06107401847839,0.954914808273315,12}, {2.91164398193359,0.901369392871857,12}, {2.73004698753357,0.544967114925385,12}, {3.5785219669342,0.625140011310577,12}, {3.45491504669189,0.244717106223106,12}, {4.2804012298584,0.456633001565933,12}, {4.21782684326172,0.0615576282143593,12}, {5,0.399999290704727,12}, {5,0,12}, {19.6000003814697,0.399999290704727,12}, {20,0,12}, {19.6000003814697,20,12}, {20,20,12}, {19.5300102233887,21.1571502685547,12}, {19.9323806762695,21.1609306335449,12}, {19.3210391998291,22.2974300384521,12}, {19.7304496765137,22.3061599731445,12}, {18.9761600494385,23.4042091369629,12}, {19.3969306945801,23.4202003479004,12}, {18.5003795623779,24.4613399505615,12}, {18.9363307952881,24.4879894256592,12}, {17.9006500244141,25.4534206390381,12}, {18.3548793792725,25.4950904846191,12}, {17.1856994628906,26.3659801483154,12}, {17.6604404449463,26.4278793334961,12}, {16.3659801483154,27.1856994628906,12}, {16.862419128418,27.2737407684326,12}, {15.4534196853638,27.9006500244141,12}, {15.9715900421143,28.0212306976318,12}, {14.4613399505615,28.5003795623779,12}, {15,28.6602592468262,12}, {13.4042100906372,28.9761600494385,12}, {13.9608001708984,29.1821594238281,12}, {12.2974300384521,29.3210391998291,12}, {7.13196802139282,29.5799007415771,0}, {8.26351833343506,29.8480796813965,0}, {8.84284687042236,29.5300102233887,0}, {9.41855144500732,29.9830799102783,0}, {10,29.6000003814697,0}, {10.5814504623413,29.9830799102783,0}, {11.1571502685547,29.5300102233887,0}, {11.7364797592163,29.8480796813965,0}, {12.2974300384521,29.3210391998291,0}, {12.8680295944214,29.5799007415771,0}, {13.4042100906372,28.9761600494385,0}, {13.9608001708984,29.1821594238281,0}, {14.4613399505615,28.5003795623779,0}, {15,28.6602592468262,0}, {15.4534196853638,27.9006500244141,0}, {15.9715900421143,28.0212306976318,0}, {16.3659801483154,27.1856994628906,0}, {16.862419128418,27.2737407684326,0}, {17.1856994628906,26.3659801483154,0}, {17.6604404449463,26.4278793334961,0}, {17.9006500244141,25.4534206390381,0}, {18.3548793792725,25.4950904846191,0}, {18.5003795623779,24.4613399505615,0}, {18.9363307952881,24.4879894256592,0}, {18.9761600494385,23.4042091369629,0}, {19.3969306945801,23.4202003479004,0}, {19.3210391998291,22.2974300384521,0}, {19.7304496765137,22.3061599731445,0}, {19.5300102233887,21.1571502685547,0}, {19.9323806762695,21.1609306335449,0}, {19.6000003814697,20,0}, {20,20,0}, {19.6000003814697,0.399999290704727,0}, {20,0,0}, {5,0.399999290704727,0}, {5,0,0}, {4.2804012298584,0.456633001565933,0}, {4.21782684326172,0.0615576282143593,0}, {3.5785219669342,0.625140011310577,0}, {3.45491504669189,0.244717106223106,0}, {2.91164398193359,0.901369392871857,0}, {2.73004698753357,0.544967114925385,0}, {2.29618692398071,1.27852201461792,0}, {2.06107401847839,0.954914808273315,0}, {1.74730801582336,1.74730801582336,0}, {1.46446597576141,1.46446597576141,0}, {1.27852201461792,2.29618692398071,0}, {0.954914808273315,2.06107401847839,0}, {0.901369392871857,2.91164398193359,0}, {0.544967114925385,2.73004698753357,0}, {0.625140011310577,3.5785219669342,0}, {0.244717106223106,3.45491504669189,0}, {0.456633001565933,4.2804012298584,0}, {0.0615576282143593,4.21782684326172,0}, {0.399999290704727,5,0}, {0,5,0}, {0.399999290704727,20,0}, {0,20,0}, {0.469994693994522,21.1571502685547,0}, {0.067615881562233,21.1609306335449,0}, {0.678958415985107,22.2974300384521,0}, {0.269550800323486,22.3061599731445,0}, {1.02384400367737,23.4042091369629,0}, {0.603073298931122,23.4202003479004,0}, {1.49962198734283,24.4613399505615,0}, {1.0636739730835,24.4879894256592,0}, {2.0993549823761,25.4534206390381,0}, {1.64512205123901,25.4950904846191,0}, {2.81429696083069,26.3659801483154,0}, {2.33955597877502,26.4278793334961,0}, {3.63402199745178,27.1856994628906,0}, {3.13758301734924,27.2737407684326,0}, {4.54657793045044,27.9006500244141,0}, {4.02841377258301,28.0212306976318,0}, {5.53865718841553,28.5003795623779,0}, {5,28.6602592468262,0}, {6.59579277038574,28.9761600494385,0}, {6.03920221328735,29.1821594238281,0}, {7.70256900787354,29.3210391998291,0} }, - { {0,1,2}, {2,1,3}, {2,3,4}, {4,3,5}, {4,5,6}, {6,5,7}, {6,7,8}, {8,7,9}, {8,9,10}, {10,9,11}, {10,11,12}, {12,11,13}, {12,13,14}, {14,13,15}, {14,15,16}, {16,15,17}, {16,17,18}, {18,17,19}, {18,19,20}, {20,19,21}, {20,21,22}, {22,21,23}, {22,23,24}, {24,23,25}, {24,25,26}, {26,25,27}, {26,27,28}, {28,27,29}, {28,29,30}, {30,29,31}, {30,31,32}, {32,31,33}, {32,33,34}, {34,33,35}, {34,35,36}, {36,35,37}, {36,37,38}, {38,37,39}, {38,39,40}, {40,39,41}, {40,41,42}, {42,41,43}, {42,43,44}, {44,43,45}, {44,45,46}, {46,45,47}, {46,47,48}, {48,47,49}, {48,49,50}, {50,49,51}, {50,51,52}, {52,51,53}, {52,53,54}, {54,53,55}, {54,55,56}, {56,55,57}, {56,57,58}, {58,57,59}, {58,59,60}, {60,59,61}, {60,61,62}, {62,61,63}, {62,63,64}, {64,63,65}, {64,65,66}, {66,65,67}, {66,67,68}, {68,67,69}, {68,69,70}, {70,69,71}, {70,71,72}, {72,71,73}, {72,73,74}, {74,73,75}, {74,75,76}, {76,75,77}, {76,77,78}, {78,77,0}, {78,0,2}, {79,80,81}, {81,80,82}, {81,82,83}, {83,82,84}, {83,84,85}, {85,84,86}, {85,86,87}, {87,86,88}, {87,88,89}, {89,88,90}, {89,90,91}, {91,90,92}, {91,92,93}, {93,92,94}, {93,94,95}, {95,94,96}, {95,96,97}, {97,96,98}, {97,98,99}, {99,98,100}, {99,100,101}, {101,100,102}, {101,102,103}, {103,102,104}, {103,104,105}, {105,104,106}, {105,106,107}, {107,106,108}, {107,108,109}, {109,108,110}, {109,110,111}, {111,110,112}, {111,112,113}, {113,112,114}, {113,114,115}, {115,114,116}, {115,116,117}, {117,116,118}, {117,118,119}, {119,118,120}, {119,120,121}, {121,120,122}, {121,122,123}, {123,122,124}, {123,124,125}, {125,124,126}, {125,126,127}, {127,126,128}, {127,128,129}, {129,128,130}, {129,130,131}, {131,130,132}, {131,132,133}, {133,132,134}, {133,134,135}, {135,134,136}, {135,136,137}, {137,136,138}, {137,138,139}, {139,138,140}, {139,140,141}, {141,140,142}, {141,142,143}, {143,142,144}, {143,144,145}, {145,144,146}, {145,146,147}, {147,146,148}, {147,148,149}, {149,148,150}, {149,150,151}, {151,150,152}, {151,152,153}, {153,152,154}, {153,154,155}, {155,154,156}, {155,156,157}, {157,156,79}, {157,79,81}, {57,110,108}, {57,108,59}, {59,108,106}, {59,106,61}, {61,106,104}, {61,104,63}, {63,104,102}, {63,102,65}, {65,102,100}, {65,100,67}, {67,100,98}, {67,98,69}, {69,98,96}, {69,96,71}, {71,96,94}, {71,94,73}, {73,94,92}, {73,92,75}, {75,92,90}, {75,90,77}, {77,90,88}, {77,88,0}, {0,88,86}, {0,86,1}, {1,86,84}, {1,84,3}, {3,84,82}, {3,82,5}, {5,82,80}, {5,80,7}, {7,80,79}, {7,79,9}, {9,79,156}, {9,156,11}, {11,156,154}, {11,154,13}, {13,154,152}, {13,152,15}, {15,152,150}, {15,150,17}, {17,150,148}, {17,148,19}, {19,148,146}, {19,146,21}, {21,146,144}, {21,144,23}, {23,144,142}, {23,142,25}, {25,142,140}, {25,140,27}, {27,140,138}, {27,138,29}, {29,138,136}, {29,136,31}, {33,31,134}, {134,31,136}, {33,134,132}, {33,132,35}, {35,132,130}, {35,130,37}, {37,130,128}, {37,128,39}, {39,128,126}, {39,126,41}, {41,126,124}, {41,124,43}, {43,124,122}, {43,122,45}, {45,122,120}, {45,120,47}, {47,120,118}, {47,118,49}, {49,118,116}, {49,116,51}, {51,116,114}, {51,114,53}, {55,53,112}, {112,53,114}, {57,55,110}, {110,55,112}, {30,135,137}, {30,137,28}, {28,137,139}, {28,139,26}, {26,139,141}, {26,141,24}, {24,141,143}, {24,143,22}, {22,143,145}, {22,145,20}, {20,145,147}, {20,147,18}, {18,147,149}, {18,149,16}, {16,149,151}, {16,151,14}, {14,151,153}, {14,153,12}, {12,153,155}, {12,155,10}, {10,155,157}, {10,157,8}, {8,157,81}, {8,81,6}, {6,81,83}, {6,83,4}, {4,83,85}, {4,85,2}, {2,85,87}, {2,87,78}, {78,87,89}, {78,89,76}, {76,89,91}, {76,91,74}, {74,91,93}, {74,93,72}, {72,93,95}, {72,95,70}, {70,95,97}, {70,97,68}, {68,97,99}, {68,99,66}, {66,99,101}, {66,101,64}, {64,101,103}, {64,103,62}, {62,103,105}, {62,105,60}, {60,105,107}, {60,107,58}, {58,107,109}, {58,109,56}, {30,32,135}, {135,32,133}, {52,113,115}, {52,115,50}, {50,115,117}, {50,117,48}, {48,117,119}, {48,119,46}, {46,119,121}, {46,121,44}, {44,121,123}, {44,123,42}, {42,123,125}, {42,125,40}, {40,125,127}, {40,127,38}, {38,127,129}, {38,129,36}, {36,129,131}, {36,131,34}, {34,131,133}, {34,133,32}, {52,54,113}, {113,54,111}, {54,56,111}, {111,56,109} }); + { {12.8680295944214f,29.5799007415771f,12.f}, {11.7364797592163f,29.8480796813965f,12.f}, {11.1571502685547f,29.5300102233887f,12.f}, {10.5814504623413f,29.9830799102783f,12.f}, {10.f,29.6000003814697f,12.f}, {9.41855144500732f,29.9830799102783f,12.f}, {8.84284687042236f,29.5300102233887f,12.f}, {8.26351833343506f,29.8480796813965f,12.f}, {7.70256900787354f,29.3210391998291f,12.f}, {7.13196802139282f,29.5799007415771f,12.f}, {6.59579277038574f,28.9761600494385f,12.f}, {6.03920221328735f,29.1821594238281f,12.f}, {5.53865718841553f,28.5003795623779f,12.f}, {5.f,28.6602592468262f,12.f}, {4.54657793045044f,27.9006500244141f,12.f}, {4.02841377258301f,28.0212306976318f,12.f}, {3.63402199745178f,27.1856994628906f,12.f}, {3.13758301734924f,27.2737407684326f,12.f}, {2.81429696083069f,26.3659801483154f,12.f}, {2.33955597877502f,26.4278793334961f,12.f}, {2.0993549823761f,25.4534206390381f,12.f}, {1.64512205123901f,25.4950904846191f,12.f}, {1.49962198734283f,24.4613399505615f,12.f}, {1.0636739730835f,24.4879894256592f,12.f}, {1.02384400367737f,23.4042091369629f,12.f}, {0.603073298931122f,23.4202003479004f,12.f}, {0.678958415985107f,22.2974300384521f,12.f}, {0.269550800323486f,22.3061599731445f,12.f}, {0.469994693994522f,21.1571502685547f,12.f}, {0.067615881562233f,21.1609306335449f,12.f}, {0.399999290704727f,20.f,12.f}, {0.f,20.f,12.f}, {0.399999290704727f,5.f,12.f}, {0.f,5.f,12.f}, {0.456633001565933f,4.2804012298584f,12.f}, {0.0615576282143593f,4.21782684326172f,12.f}, {0.625140011310577f,3.5785219669342f,12.f}, {0.244717106223106f,3.45491504669189f,12.f}, {0.901369392871857f,2.91164398193359f,12.f}, {0.544967114925385f,2.73004698753357f,12.f}, {1.27852201461792f,2.29618692398071f,12.f}, {0.954914808273315f,2.06107401847839f,12.f}, {1.74730801582336f,1.74730801582336f,12.f}, {1.46446597576141f,1.46446597576141f,12.f}, {2.29618692398071f,1.27852201461792f,12.f}, {2.06107401847839f,0.954914808273315f,12.f}, {2.91164398193359f,0.901369392871857f,12.f}, {2.73004698753357f,0.544967114925385f,12.f}, {3.5785219669342f,0.625140011310577f,12.f}, {3.45491504669189f,0.244717106223106f,12.f}, {4.2804012298584f,0.456633001565933f,12.f}, {4.21782684326172f,0.0615576282143593f,12.f}, {5.f,0.399999290704727f,12.f}, {5.f,0.f,12.f}, {19.6000003814697f,0.399999290704727f,12.f}, {20.f,0.f,12.f}, {19.6000003814697f,20.f,12.f}, {20.f,20.f,12.f}, {19.5300102233887f,21.1571502685547f,12.f}, {19.9323806762695f,21.1609306335449f,12.f}, {19.3210391998291f,22.2974300384521f,12.f}, {19.7304496765137f,22.3061599731445f,12.f}, {18.9761600494385f,23.4042091369629f,12.f}, {19.3969306945801f,23.4202003479004f,12.f}, {18.5003795623779f,24.4613399505615f,12.f}, {18.9363307952881f,24.4879894256592f,12.f}, {17.9006500244141f,25.4534206390381f,12.f}, {18.3548793792725f,25.4950904846191f,12.f}, {17.1856994628906f,26.3659801483154f,12.f}, {17.6604404449463f,26.4278793334961f,12.f}, {16.3659801483154f,27.1856994628906f,12.f}, {16.862419128418f,27.2737407684326f,12.f}, {15.4534196853638f,27.9006500244141f,12.f}, {15.9715900421143f,28.0212306976318f,12.f}, {14.4613399505615f,28.5003795623779f,12.f}, {15.f,28.6602592468262f,12.f}, {13.4042100906372f,28.9761600494385f,12.f}, {13.9608001708984f,29.1821594238281f,12.f}, {12.2974300384521f,29.3210391998291f,12.f}, {7.13196802139282f,29.5799007415771f,0.f}, {8.26351833343506f,29.8480796813965f,0.f}, {8.84284687042236f,29.5300102233887f,0.f}, {9.41855144500732f,29.9830799102783f,0.f}, {10.f,29.6000003814697f,0.f}, {10.5814504623413f,29.9830799102783f,0.f}, {11.1571502685547f,29.5300102233887f,0.f}, {11.7364797592163f,29.8480796813965f,0.f}, {12.2974300384521f,29.3210391998291f,0.f}, {12.8680295944214f,29.5799007415771f,0.f}, {13.4042100906372f,28.9761600494385f,0.f}, {13.9608001708984f,29.1821594238281f,0.f}, {14.4613399505615f,28.5003795623779f,0.f}, {15.f,28.6602592468262f,0.f}, {15.4534196853638f,27.9006500244141f,0.f}, {15.9715900421143f,28.0212306976318f,0.f}, {16.3659801483154f,27.1856994628906f,0.f}, {16.862419128418f,27.2737407684326f,0.f}, {17.1856994628906f,26.3659801483154f,0.f}, {17.6604404449463f,26.4278793334961f,0.f}, {17.9006500244141f,25.4534206390381f,0.f}, {18.3548793792725f,25.4950904846191f,0.f}, {18.5003795623779f,24.4613399505615f,0.f}, {18.9363307952881f,24.4879894256592f,0.f}, {18.9761600494385f,23.4042091369629f,0.f}, {19.3969306945801f,23.4202003479004f,0.f}, {19.3210391998291f,22.2974300384521f,0.f}, {19.7304496765137f,22.3061599731445f,0.f}, {19.5300102233887f,21.1571502685547f,0.f}, {19.9323806762695f,21.1609306335449f,0.f}, {19.6000003814697f,20.f,0.f}, {20.f,20.f,0.f}, {19.6000003814697f,0.399999290704727f,0.f}, {20.f,0.f,0.f}, {5.f,0.399999290704727f,0.f}, {5.f,0.f,0.f}, {4.2804012298584f,0.456633001565933f,0.f}, {4.21782684326172f,0.0615576282143593f,0.f}, {3.5785219669342f,0.625140011310577f,0.f}, {3.45491504669189f,0.244717106223106f,0.f}, {2.91164398193359f,0.901369392871857f,0.f}, {2.73004698753357f,0.544967114925385f,0.f}, {2.29618692398071f,1.27852201461792f,0.f}, {2.06107401847839f,0.954914808273315f,0.f}, {1.74730801582336f,1.74730801582336f,0.f}, {1.46446597576141f,1.46446597576141f,0.f}, {1.27852201461792f,2.29618692398071f,0.f}, {0.954914808273315f,2.06107401847839f,0.f}, {0.901369392871857f,2.91164398193359f,0.f}, {0.544967114925385f,2.73004698753357f,0.f}, {0.625140011310577f,3.5785219669342f,0.f}, {0.244717106223106f,3.45491504669189f,0.f}, {0.456633001565933f,4.2804012298584f,0.f}, {0.0615576282143593f,4.21782684326172f,0.f}, {0.399999290704727f,5.f,0.f}, {0.f,5.f,0.f}, {0.399999290704727f,20.f,0.f}, {0.f,20.f,0.f}, {0.469994693994522f,21.1571502685547f,0.f}, {0.067615881562233f,21.1609306335449f,0.f}, {0.678958415985107f,22.2974300384521f,0.f}, {0.269550800323486f,22.3061599731445f,0.f}, {1.02384400367737f,23.4042091369629f,0.f}, {0.603073298931122f,23.4202003479004f,0.f}, {1.49962198734283f,24.4613399505615f,0.f}, {1.0636739730835f,24.4879894256592f,0.f}, {2.0993549823761f,25.4534206390381f,0.f}, {1.64512205123901f,25.4950904846191f,0.f}, {2.81429696083069f,26.3659801483154f,0.f}, {2.33955597877502f,26.4278793334961f,0.f}, {3.63402199745178f,27.1856994628906f,0.f}, {3.13758301734924f,27.2737407684326f,0.f}, {4.54657793045044f,27.9006500244141f,0.f}, {4.02841377258301f,28.0212306976318f,0.f}, {5.53865718841553f,28.5003795623779f,0.f}, {5.f,28.6602592468262f,0.f}, {6.59579277038574f,28.9761600494385f,0.f}, {6.03920221328735f,29.1821594238281f,0.f}, {7.70256900787354f,29.3210391998291f,0.f} }, + { {0,1,2}, {2,1,3}, {2,3,4}, {4,3,5}, {4,5,6}, {6,5,7}, {6,7,8}, {8,7,9}, {8,9,10}, {10,9,11}, {10,11,12}, {12,11,13}, {12,13,14}, {14,13,15}, {14,15,16}, {16,15,17}, {16,17,18}, {18,17,19}, {18,19,20}, {20,19,21}, {20,21,22}, {22,21,23}, {22,23,24}, {24,23,25}, {24,25,26}, {26,25,27}, {26,27,28}, {28,27,29}, {28,29,30}, {30,29,31}, {30,31,32}, {32,31,33}, {32,33,34}, {34,33,35}, {34,35,36}, {36,35,37}, {36,37,38}, {38,37,39}, {38,39,40}, {40,39,41}, {40,41,42}, {42,41,43}, {42,43,44}, {44,43,45}, {44,45,46}, {46,45,47}, {46,47,48}, {48,47,49}, {48,49,50}, {50,49,51}, {50,51,52}, {52,51,53}, {52,53,54}, {54,53,55}, {54,55,56}, {56,55,57}, {56,57,58}, {58,57,59}, {58,59,60}, {60,59,61}, {60,61,62}, {62,61,63}, {62,63,64}, {64,63,65}, {64,65,66}, {66,65,67}, {66,67,68}, {68,67,69}, {68,69,70}, {70,69,71}, {70,71,72}, {72,71,73}, {72,73,74}, {74,73,75}, {74,75,76}, {76,75,77}, {76,77,78}, {78,77,0}, {78,0,2}, {79,80,81}, {81,80,82}, {81,82,83}, {83,82,84}, {83,84,85}, {85,84,86}, {85,86,87}, {87,86,88}, {87,88,89}, {89,88,90}, {89,90,91}, {91,90,92}, {91,92,93}, {93,92,94}, {93,94,95}, {95,94,96}, {95,96,97}, {97,96,98}, {97,98,99}, {99,98,100}, {99,100,101}, {101,100,102}, {101,102,103}, {103,102,104}, {103,104,105}, {105,104,106}, {105,106,107}, {107,106,108}, {107,108,109}, {109,108,110}, {109,110,111}, {111,110,112}, {111,112,113}, {113,112,114}, {113,114,115}, {115,114,116}, {115,116,117}, {117,116,118}, {117,118,119}, {119,118,120}, {119,120,121}, {121,120,122}, {121,122,123}, {123,122,124}, {123,124,125}, {125,124,126}, {125,126,127}, {127,126,128}, {127,128,129}, {129,128,130}, {129,130,131}, {131,130,132}, {131,132,133}, {133,132,134}, {133,134,135}, {135,134,136}, {135,136,137}, {137,136,138}, {137,138,139}, {139,138,140}, {139,140,141}, {141,140,142}, {141,142,143}, {143,142,144}, {143,144,145}, {145,144,146}, {145,146,147}, {147,146,148}, {147,148,149}, {149,148,150}, {149,150,151}, {151,150,152}, {151,152,153}, {153,152,154}, {153,154,155}, {155,154,156}, {155,156,157}, {157,156,79}, {157,79,81}, {57,110,108}, {57,108,59}, {59,108,106}, {59,106,61}, {61,106,104}, {61,104,63}, {63,104,102}, {63,102,65}, {65,102,100}, {65,100,67}, {67,100,98}, {67,98,69}, {69,98,96}, {69,96,71}, {71,96,94}, {71,94,73}, {73,94,92}, {73,92,75}, {75,92,90}, {75,90,77}, {77,90,88}, {77,88,0}, {0,88,86}, {0,86,1}, {1,86,84}, {1,84,3}, {3,84,82}, {3,82,5}, {5,82,80}, {5,80,7}, {7,80,79}, {7,79,9}, {9,79,156}, {9,156,11}, {11,156,154}, {11,154,13}, {13,154,152}, {13,152,15}, {15,152,150}, {15,150,17}, {17,150,148}, {17,148,19}, {19,148,146}, {19,146,21}, {21,146,144}, {21,144,23}, {23,144,142}, {23,142,25}, {25,142,140}, {25,140,27}, {27,140,138}, {27,138,29}, {29,138,136}, {29,136,31}, {33,31,134}, {134,31,136}, {33,134,132}, {33,132,35}, {35,132,130}, {35,130,37}, {37,130,128}, {37,128,39}, {39,128,126}, {39,126,41}, {41,126,124}, {41,124,43}, {43,124,122}, {43,122,45}, {45,122,120}, {45,120,47}, {47,120,118}, {47,118,49}, {49,118,116}, {49,116,51}, {51,116,114}, {51,114,53}, {55,53,112}, {112,53,114}, {57,55,110}, {110,55,112}, {30,135,137}, {30,137,28}, {28,137,139}, {28,139,26}, {26,139,141}, {26,141,24}, {24,141,143}, {24,143,22}, {22,143,145}, {22,145,20}, {20,145,147}, {20,147,18}, {18,147,149}, {18,149,16}, {16,149,151}, {16,151,14}, {14,151,153}, {14,153,12}, {12,153,155}, {12,155,10}, {10,155,157}, {10,157,8}, {8,157,81}, {8,81,6}, {6,81,83}, {6,83,4}, {4,83,85}, {4,85,2}, {2,85,87}, {2,87,78}, {78,87,89}, {78,89,76}, {76,89,91}, {76,91,74}, {74,91,93}, {74,93,72}, {72,93,95}, {72,95,70}, {70,95,97}, {70,97,68}, {68,97,99}, {68,99,66}, {66,99,101}, {66,101,64}, {64,101,103}, {64,103,62}, {62,103,105}, {62,105,60}, {60,105,107}, {60,107,58}, {58,107,109}, {58,109,56}, {30,32,135}, {135,32,133}, {52,113,115}, {52,115,50}, {50,115,117}, {50,117,48}, {48,117,119}, {48,119,46}, {46,119,121}, {46,121,44}, {44,121,123}, {44,123,42}, {42,123,125}, {42,125,40}, {40,125,127}, {40,127,38}, {38,127,129}, {38,129,36}, {36,129,131}, {36,131,34}, {34,131,133}, {34,133,32}, {52,54,113}, {113,54,111}, {54,56,111}, {111,56,109} }); break; case TestMesh::sloping_hole: mesh = TriangleMesh( - { {-20,-20,-5}, {-20,-20,5}, {-20,20,-5}, {-20,20,5}, {20,-20,-5}, {20,-20,5}, {4.46294021606445,7.43144989013672,-5}, {20,20,-5}, {-19.1420993804932,0,-5}, {-18.8330993652344,-2.07911992073059,-5}, {-17.9195003509521,-4.06736993789673,-5}, {-16.4412002563477,-5.87785005569458,-5}, {-14.4629001617432,-7.43144989013672,-5}, {-12.0711002349854,-8.66024971008301,-5}, {-9.37016010284424,-9.51056003570557,-5}, {-3.5217399597168,-9.94521999359131,-5}, {-6.4782600402832,-9.94521999359131,-5}, {-0.629840016365051,-9.51056003570557,-5}, {2.07106995582581,-8.66024971008301,-5}, {6.44122982025146,-5.87785005569458,-5}, {4.46294021606445,-7.43144989013672,-5}, {-12.0711002349854,8.66024971008301,-5}, {-9.37016010284424,9.51056003570557,-5}, {7.91947984695435,-4.06736993789673,-5}, {8.83310031890869,-2.07911992073059,-5}, {-6.4782600402832,9.94521999359131,-5}, {-0.629840016365051,9.51056003570557,-5}, {2.07106995582581,8.66024971008301,-5}, {9.14214038848877,0,-5}, {8.83310031890869,2.07911992073059,-5}, {-3.5217399597168,9.94521999359131,-5}, {7.91947984695435,4.06736993789673,-5}, {6.44122982025146,5.87785005569458,-5}, {-14.4629001617432,7.43144989013672,-5}, {-16.4412002563477,5.87785005569458,-5}, {-17.9195003509521,4.06736993789673,-5}, {-18.8330993652344,2.07911992073059,-5}, {20,20,5}, {3.5217399597168,-9.94521999359131,5}, {-8.83310031890869,-2.07911992073059,5}, {-9.14214038848877,0,5}, {-8.83310031890869,2.07911992073059,5}, {6.4782600402832,-9.94521999359131,5}, {-7.91947984695435,4.06736993789673,5}, {-6.44122982025146,5.87785005569458,5}, {-4.46294021606445,7.43144989013672,5}, {-2.07106995582581,8.66024971008301,5}, {0.629840016365051,9.51056003570557,5}, {12.0711002349854,-8.66024971008301,5}, {9.37016010284424,-9.51056003570557,5}, {3.5217399597168,9.94521999359131,5}, {6.4782600402832,9.94521999359131,5}, {9.37016010284424,9.51056003570557,5}, {12.0711002349854,8.66024971008301,5}, {14.4629001617432,7.43144989013672,5}, {16.4412002563477,-5.87785005569458,5}, {14.4629001617432,-7.43144989013672,5}, {16.4412002563477,5.87785005569458,5}, {17.9195003509521,4.06736993789673,5}, {18.8330993652344,-2.07911992073059,5}, {17.9195003509521,-4.06736993789673,5}, {18.8330993652344,2.07911992073059,5}, {19.1420993804932,0,5}, {0.629840016365051,-9.51056003570557,5}, {-2.07106995582581,-8.66024971008301,5}, {-4.46294021606445,-7.43144989013672,5}, {-6.44122982025146,-5.87785005569458,5}, {-7.91947984695435,-4.06736993789673,5} }, - { {0,1,2}, {2,1,3}, {1,0,4}, {5,1,4}, {6,2,7}, {0,2,8}, {0,8,9}, {0,9,10}, {0,10,11}, {0,11,12}, {0,12,13}, {0,13,4}, {13,14,4}, {15,4,16}, {17,4,15}, {18,4,17}, {19,4,20}, {18,20,4}, {21,2,22}, {4,19,23}, {4,23,7}, {23,24,7}, {22,2,25}, {26,2,27}, {28,29,7}, {25,2,30}, {29,31,7}, {30,2,26}, {31,32,7}, {27,2,6}, {32,6,7}, {28,7,24}, {33,2,21}, {34,2,33}, {35,2,34}, {36,2,35}, {8,2,36}, {16,4,14}, {2,3,7}, {7,3,37}, {38,1,5}, {3,1,39}, {3,39,40}, {3,40,41}, {42,38,5}, {3,41,43}, {3,43,44}, {37,3,45}, {37,45,46}, {37,46,47}, {48,49,5}, {37,47,50}, {49,42,5}, {37,50,51}, {37,51,52}, {37,52,53}, {37,53,54}, {55,56,5}, {37,54,57}, {37,57,58}, {59,60,5}, {37,58,61}, {37,62,5}, {37,61,62}, {62,59,5}, {60,55,5}, {63,1,38}, {64,1,63}, {65,1,64}, {66,1,65}, {67,1,66}, {39,1,67}, {44,45,3}, {56,48,5}, {5,4,7}, {37,5,7}, {41,40,36}, {36,40,8}, {39,9,40}, {40,9,8}, {43,41,35}, {35,41,36}, {44,43,34}, {34,43,35}, {33,45,44}, {34,33,44}, {21,46,45}, {33,21,45}, {22,47,46}, {21,22,46}, {25,50,47}, {22,25,47}, {30,51,50}, {25,30,50}, {26,52,51}, {30,26,51}, {27,53,52}, {26,27,52}, {6,54,53}, {27,6,53}, {32,57,54}, {6,32,54}, {31,58,57}, {32,31,57}, {29,61,58}, {31,29,58}, {28,62,61}, {29,28,61}, {59,62,28}, {24,59,28}, {60,59,24}, {23,60,24}, {55,60,23}, {19,55,23}, {55,19,56}, {56,19,20}, {56,20,48}, {48,20,18}, {48,18,49}, {49,18,17}, {49,17,42}, {42,17,15}, {42,15,38}, {38,15,16}, {38,16,63}, {63,16,14}, {63,14,64}, {64,14,13}, {64,13,65}, {65,13,12}, {65,12,66}, {66,12,11}, {66,11,67}, {67,11,10}, {67,10,39}, {39,10,9} }); + { {-20.f,-20.f,-5.f}, {-20.f,-20.f,5.f}, {-20.f,20.f,-5.f}, {-20.f,20.f,5.f}, {20.f,-20.f,-5.f}, {20.f,-20.f,5.f}, {4.46294021606445f,7.43144989013672f,-5.f}, {20.f,20.f,-5.f}, {-19.1420993804932f,0.f,-5.f}, {-18.8330993652344f,-2.07911992073059f,-5.f}, {-17.9195003509521f,-4.06736993789673f,-5.f}, {-16.4412002563477f,-5.87785005569458f,-5.f}, {-14.4629001617432f,-7.43144989013672f,-5.f}, {-12.0711002349854f,-8.66024971008301f,-5.f}, {-9.37016010284424f,-9.51056003570557f,-5.f}, {-3.5217399597168f,-9.94521999359131f,-5.f}, {-6.4782600402832f,-9.94521999359131f,-5.f}, {-0.629840016365051f,-9.51056003570557f,-5.f}, {2.07106995582581f,-8.66024971008301f,-5.f}, {6.44122982025146f,-5.87785005569458f,-5.f}, {4.46294021606445f,-7.43144989013672f,-5.f}, {-12.0711002349854f,8.66024971008301f,-5.f}, {-9.37016010284424f,9.51056003570557f,-5.f}, {7.91947984695435f,-4.06736993789673f,-5.f}, {8.83310031890869f,-2.07911992073059f,-5.f}, {-6.4782600402832f,9.94521999359131f,-5.f}, {-0.629840016365051f,9.51056003570557f,-5.f}, {2.07106995582581f,8.66024971008301f,-5.f}, {9.14214038848877f,0.f,-5.f}, {8.83310031890869f,2.07911992073059f,-5.f}, {-3.5217399597168f,9.94521999359131f,-5.f}, {7.91947984695435f,4.06736993789673f,-5.f}, {6.44122982025146f,5.87785005569458f,-5.f}, {-14.4629001617432f,7.43144989013672f,-5.f}, {-16.4412002563477f,5.87785005569458f,-5.f}, {-17.9195003509521f,4.06736993789673f,-5.f}, {-18.8330993652344f,2.07911992073059f,-5.f}, {20.f,20.f,5.f}, {3.5217399597168f,-9.94521999359131f,5.f}, {-8.83310031890869f,-2.07911992073059f,5.f}, {-9.14214038848877f,0.f,5.f}, {-8.83310031890869f,2.07911992073059f,5.f}, {6.4782600402832f,-9.94521999359131f,5.f}, {-7.91947984695435f,4.06736993789673f,5.f}, {-6.44122982025146f,5.87785005569458f,5.f}, {-4.46294021606445f,7.43144989013672f,5.f}, {-2.07106995582581f,8.66024971008301f,5.f}, {0.629840016365051f,9.51056003570557f,5.f}, {12.0711002349854f,-8.66024971008301f,5.f}, {9.37016010284424f,-9.51056003570557f,5.f}, {3.5217399597168f,9.94521999359131f,5.f}, {6.4782600402832f,9.94521999359131f,5.f}, {9.37016010284424f,9.51056003570557f,5.f}, {12.0711002349854f,8.66024971008301f,5.f}, {14.4629001617432f,7.43144989013672f,5.f}, {16.4412002563477f,-5.87785005569458f,5.f}, {14.4629001617432f,-7.43144989013672f,5.f}, {16.4412002563477f,5.87785005569458f,5.f}, {17.9195003509521f,4.06736993789673f,5.f}, {18.8330993652344f,-2.07911992073059f,5.f}, {17.9195003509521f,-4.06736993789673f,5.f}, {18.8330993652344f,2.07911992073059f,5.f}, {19.1420993804932f,0.f,5.f}, {0.629840016365051f,-9.51056003570557f,5.f}, {-2.07106995582581f,-8.66024971008301f,5.f}, {-4.46294021606445f,-7.43144989013672f,5.f}, {-6.44122982025146f,-5.87785005569458f,5.f}, {-7.91947984695435f,-4.06736993789673f,5.f} }, + { {0,1,2}, {2,1,3}, {1,0,4}, {5,1,4}, {6,2,7}, {0,2,8}, {0,8,9}, {0,9,10}, {0,10,11}, {0,11,12}, {0,12,13}, {0,13,4}, {13,14,4}, {15,4,16}, {17,4,15}, {18,4,17}, {19,4,20}, {18,20,4}, {21,2,22}, {4,19,23}, {4,23,7}, {23,24,7}, {22,2,25}, {26,2,27}, {28,29,7}, {25,2,30}, {29,31,7}, {30,2,26}, {31,32,7}, {27,2,6}, {32,6,7}, {28,7,24}, {33,2,21}, {34,2,33}, {35,2,34}, {36,2,35}, {8,2,36}, {16,4,14}, {2,3,7}, {7,3,37}, {38,1,5}, {3,1,39}, {3,39,40}, {3,40,41}, {42,38,5}, {3,41,43}, {3,43,44}, {37,3,45}, {37,45,46}, {37,46,47}, {48,49,5}, {37,47,50}, {49,42,5}, {37,50,51}, {37,51,52}, {37,52,53}, {37,53,54}, {55,56,5}, {37,54,57}, {37,57,58}, {59,60,5}, {37,58,61}, {37,62,5}, {37,61,62}, {62,59,5}, {60,55,5}, {63,1,38}, {64,1,63}, {65,1,64}, {66,1,65}, {67,1,66}, {39,1,67}, {44,45,3}, {56,48,5}, {5,4,7}, {37,5,7}, {41,40,36}, {36,40,8}, {39,9,40}, {40,9,8}, {43,41,35}, {35,41,36}, {44,43,34}, {34,43,35}, {33,45,44}, {34,33,44}, {21,46,45}, {33,21,45}, {22,47,46}, {21,22,46}, {25,50,47}, {22,25,47}, {30,51,50}, {25,30,50}, {26,52,51}, {30,26,51}, {27,53,52}, {26,27,52}, {6,54,53}, {27,6,53}, {32,57,54}, {6,32,54}, {31,58,57}, {32,31,57}, {29,61,58}, {31,29,58}, {28,62,61}, {29,28,61}, {59,62,28}, {24,59,28}, {60,59,24}, {23,60,24}, {55,60,23}, {19,55,23}, {55,19,56}, {56,19,20}, {56,20,48}, {48,20,18}, {48,18,49}, {49,18,17}, {49,17,42}, {42,17,15}, {42,15,38}, {38,15,16}, {38,16,63}, {63,16,14}, {63,14,64}, {64,14,13}, {64,13,65}, {65,13,12}, {65,12,66}, {66,12,11}, {66,11,67}, {67,11,10}, {67,10,39}, {39,10,9} }); break; case TestMesh::ipadstand: mesh = TriangleMesh( - { Vec3f(17.4344673156738,-2.69879599481136e-16,9.5), {14.2814798355103,10,9.5}, {0,0,9.5}, {31.7159481048584,10,9.5}, Vec3f(62.2344741821289,2.06667568800577e-16,20), {31.7159481048584,10,20}, Vec3f(17.4344673156738,-2.69879599481136e-16,20), {62.2344741821289,10,20}, {98.2079696655273,10,0}, Vec3f(98.2079696655273,8.56525380796383e-16,10), {98.2079696655273,0,0}, {98.2079696655273,10,20}, {98.2079696655273,0,20}, Vec3f(81.6609649658203,-4.39753856997999e-16,10), {90.0549850463867,10,10}, {78.5079803466797,10,10}, Vec3f(93.2079696655273,8.56525380796383e-16,10), {14.2814798355103,10,20}, {0,0,20}, Vec3f(87.4344711303711,2.81343962782118e-15,20), {84.2814788818359,10,20}, {0,10,20}, {0,0,0}, {0,10,0}, Vec3f(62.2344741821289,2.06667568800577e-16,30), {66.9609756469727,10,30}, {62.2344741821289,10,30}, Vec3f(70.1139602661133,8.5525763717214e-16,30), {67.7053375244141,10,28.7107200622559}, Vec3f(71.6787109375,1.24046736339707e-15,27.2897701263428) }, - { {0,1,2}, {1,0,3}, {4,5,6}, {5,4,7}, {8,9,10}, {9,11,12}, {11,9,8}, {13,14,15}, {14,13,16}, {17,2,1}, {2,17,18}, {19,11,20}, {11,19,12}, {17,21,18}, {21,2,18}, {2,21,22}, {22,21,23}, {8,22,23}, {22,8,10}, {24,25,26}, {25,24,27}, {23,1,8}, {1,23,21}, {1,21,17}, {5,15,3}, {15,5,7}, {15,7,28}, {28,7,26}, {28,26,25}, {8,14,11}, {14,8,3}, {3,8,1}, {14,3,15}, {11,14,20}, {26,4,24}, {4,26,7}, {12,16,9}, {16,12,19}, {29,4,13}, {4,29,24}, {24,29,27}, {9,22,10}, {22,9,0}, {0,9,16}, {0,16,13}, {0,13,6}, {6,13,4}, {2,22,0}, {19,14,16}, {14,19,20}, {15,29,13}, {29,25,27}, {25,29,15}, {25,15,28}, {6,3,0}, {3,6,5} }); + { {17.4344673156738f,float(-2.69879599481136e-16),9.5f}, {14.2814798355103f,10.f,9.5f}, {0.f,0.f,9.5f}, {31.7159481048584f,10.f,9.5f}, {62.2344741821289f,float(2.06667568800577e-16),20.f}, {31.7159481048584f,10.f,20.f}, {17.4344673156738f,float(-2.69879599481136e-16),20.f}, {62.2344741821289f,10.f,20.f}, {98.2079696655273f,10.f,0.f}, {98.2079696655273f,float(8.56525380796383e-16),10.f}, {98.2079696655273f,0.f,0.f}, {98.2079696655273f,10.f,20.f}, {98.2079696655273f,0.f,20.f}, {81.6609649658203f,float(-4.39753856997999e-16),10.f}, {90.0549850463867f,10.f,10.f}, {78.5079803466797f,10.f,10.f}, {93.2079696655273f,float(8.56525380796383e-16),10.f}, {14.2814798355103f,10.f,20.f}, {0.f,0.f,20.f}, {87.4344711303711f,float(2.81343962782118e-15),20.f}, {84.2814788818359f,10.f,20.f}, {0.f,10.f,20.f}, {0.f,0.f,0.f}, {0.f,10.f,0.f}, {62.2344741821289f,float(2.06667568800577e-16),30.f}, {66.9609756469727f,10.f,30.f}, {62.2344741821289f,10.f,30.f}, {70.1139602661133f,float(8.5525763717214e-16),30.f}, {67.7053375244141f,10.f,28.7107200622559f}, {71.6787109375f,float(1.24046736339707e-15),27.2897701263428f} }, + { {0,1,2}, {1,0,3}, {4,5,6}, {5,4,7}, {8,9,10}, {9,11,12}, {11,9,8}, {13,14,15}, {14,13,16}, {17,2,1}, {2,17,18}, {19,11,20}, {11,19,12}, {17,21,18}, {21,2,18}, {2,21,22}, {22,21,23}, {8,22,23}, {22,8,10}, {24,25,26}, {25,24,27}, {23,1,8}, {1,23,21}, {1,21,17}, {5,15,3}, {15,5,7}, {15,7,28}, {28,7,26}, {28,26,25}, {8,14,11}, {14,8,3}, {3,8,1}, {14,3,15}, {11,14,20}, {26,4,24}, {4,26,7}, {12,16,9}, {16,12,19}, {29,4,13}, {4,29,24}, {24,29,27}, {9,22,10}, {22,9,0}, {0,9,16}, {0,16,13}, {0,13,6}, {6,13,4}, {2,22,0}, {19,14,16}, {14,19,20}, {15,29,13}, {29,25,27}, {25,29,15}, {25,15,28}, {6,3,0}, {3,6,5} }); break; case TestMesh::A: mesh = TriangleMesh( - { {513.075988769531,51.6074333190918,36.0009002685547}, {516.648803710938,51.7324333190918,36.0009002685547}, {513.495178222656,51.7324333190918,36.0009002685547}, {489.391204833984,51.4824333190918,24.0011005401611}, {488.928588867188,51.7324333190918,24.0011005401611}, {492.06201171875,51.7324333190918,24.0011005401611}, {496.840393066406,51.2324333190918,24.0011005401611}, {495.195404052734,51.7324333190918,24.0011005401611}, {498.981994628906,51.7324333190918,24.0011005401611}, {506.966613769531,51.6074333190918,24.0011005401611}, {510.342010498047,51.7324333190918,24.0011005401611}, {507.163818359375,51.6074333190918,24.0011005401611}, {512.515380859375,54.7190322875977,36.0009002685547}, {514.161987304688,54.5058326721191,36.0009002685547}, {493.06201171875,54.7190322875977,36.0009002685547}, {495.195404052734,51.7324333190918,36.0009002685547}, {496.195404052734,54.7190322875977,36.0009002685547}, {497.195404052734,57.7058334350586,36.0009002685547}, {500.851989746094,60.2658309936523,36.0009002685547}, {498.915405273438,62.8258323669434,36.0009002685547}, {506.701995849609,62.8258323669434,36.0009002685547}, {503.648590087891,60.2658309936523,36.0009002685547}, {508.381805419922,57.7058334350586,36.0009002685547}, {496.418792724609,60.052433013916,36.0009002685547}, {506.515197753906,72.2124328613281,36.0009002685547}, {502.808807373047,74.5324325561523,36.0009002685547}, {503.781982421875,71.6058349609375,36.0009002685547}, {515.358764648438,55.4658317565918,36.0009002685547}, {499.375183105469,76.9058380126953,36.0009002685547}, {501.168792724609,78.0658340454102,36.0009002685547}, {504.568786621094,78.0658340454102,36.0009002685547}, {506.32861328125,81.599235534668,36.0009002685547}, {502.928588867188,81.599235534668,36.0009002685547}, {499.528594970703,81.599235534668,36.0009002685547}, {498.20361328125,77.8658294677734,36.0009002685547}, {495.195404052734,51.7324333190918,30.0011005401611}, {498.981994628906,51.7324333190918,27.0011005401611}, {506.555206298828,51.7324333190918,33.0009002685547}, {506.555206298828,51.7324333190918,36.0009002685547}, {510.342010498047,51.7324333190918,36.0009002685547}, {512.515380859375,54.7190322875977,24.0011005401611}, {509.361999511719,54.7190322875977,24.0011005401611}, {508.381805419922,57.7058334350586,24.0011005401611}, {506.701995849609,62.8258323669434,24.0011005401611}, {509.188812255859,60.052433013916,24.0011005401611}, {493.06201171875,54.7190322875977,24.0011005401611}, {503.648590087891,60.2658309936523,24.0011005401611}, {500.851989746094,60.2658309936523,24.0011005401611}, {498.915405273438,62.8258323669434,24.0011005401611}, {502.808807373047,62.8258323669434,24.0011005401611}, {491.425201416016,54.5058326721191,24.0011005401611}, {506.421813964844,76.9058380126953,24.0011005401611}, {502.808807373047,74.5324325561523,24.0011005401611}, {504.568786621094,78.0658340454102,24.0011005401611}, {506.32861328125,81.599235534668,24.0011005401611}, {507.618804931641,77.8658294677734,24.0011005401611}, {499.221801757812,72.2124328613281,24.0011005401611}, {501.835388183594,71.6058349609375,24.0011005401611}, {501.168792724609,78.0658340454102,24.0011005401611}, {499.528594970703,81.599235534668,24.0011005401611}, {502.048583984375,79.8324356079102,24.0011005401611}, {490.253601074219,55.4658317565918,24.0011005401611}, {488.928588867188,51.7324333190918,30.0011005401611}, {488.928588867188,51.7324333190918,36.0009002685547}, {490.253601074219,55.4658317565918,31.5009002685547}, {498.20361328125,77.8658294677734,34.5009002685547}, {508.381805419922,57.7058334350586,30.0011005401611}, {505.585388183594,57.7058334350586,27.0011005401611}, {502.788818359375,57.7058334350586,36.0009002685547}, {499.992004394531,57.7058334350586,33.0009002685547}, {509.851989746094,53.2258338928223,33.0009002685547}, {509.361999511719,54.7190322875977,36.0009002685547}, {508.871795654297,56.2124328613281,27.0011005401611}, {496.695404052734,56.2124328613281,33.0009002685547}, {495.695404052734,53.2258338928223,27.0011005401611}, {506.32861328125,81.599235534668,30.0011005401611}, {507.618804931641,77.8658294677734,25.5011005401611}, {515.358764648438,55.4658317565918,34.5009002685547}, {501.228607177734,81.599235534668,33.0009002685547}, {504.628601074219,81.599235534668,27.0011005401611}, {503.781982421875,71.6058349609375,33.0009002685547}, {502.808807373047,74.5324325561523,30.0011005401611}, {498.915405273438,62.8258323669434,30.0011005401611}, {500.861999511719,62.8258323669434,27.0011005401611}, {502.808807373047,62.8258323669434,36.0009002685547}, {504.755187988281,62.8258323669434,33.0009002685547}, {501.835388183594,71.6058349609375,33.0009002685547}, {499.888793945312,65.7524337768555,33.0009002685547}, {499.888793945312,65.7524337768555,36.0009002685547}, {513.128601074219,51.4824333190918,36.0009002685547}, {513.075988769531,51.6074333190918,24.0011005401611}, {516.648803710938,51.7324333190918,24.0011005401611}, {513.128601074219,51.4824333190918,24.0011005401611}, {513.495178222656,51.7324333190918,24.0011005401611}, {506.966613769531,51.6074333190918,36.0009002685547}, {507.163818359375,51.6074333190918,36.0009002685547}, {490.337799072266,51.4824333190918,24.0011005401611}, {489.391204833984,51.4824333190918,36.0009002685547}, {492.06201171875,51.7324333190918,36.0009002685547}, {490.337799072266,51.4824333190918,36.0009002685547}, {513.233764648438,51.2324333190918,24.0011005401611}, {513.233764648438,51.2324333190918,36.0009002685547}, {504.773803710938,51.4824333190918,36.0009002685547}, {504.773803710938,51.4824333190918,24.0011005401611}, {489.266998291016,51.2324333190918,24.0011005401611}, {489.266998291016,51.2324333190918,36.0009002685547}, {490.253601074219,55.4658317565918,25.5011005401611}, {499.528594970703,81.599235534668,30.0011005401611}, {498.20361328125,77.8658294677734,31.5009002685547}, {515.358764648438,55.4658317565918,28.5011005401611}, {515.358764648438,55.4658317565918,25.5011005401611}, {495.246795654297,61.0124320983887,36.0009002685547}, {490.253601074219,55.4658317565918,34.5009002685547}, {490.253601074219,55.4658317565918,36.0009002685547}, {494.228607177734,66.6658325195312,24.0011005401611}, {499.068786621094,67.5192337036133,24.0011005401611}, {498.20361328125,77.8658294677734,25.5011005401611}, {498.20361328125,77.8658294677734,24.0011005401611}, {506.608795166016,67.5192337036133,36.0009002685547}, {509.09521484375,64.7458343505859,36.0009002685547}, {507.618804931641,77.8658294677734,34.5009002685547}, {507.618804931641,77.8658294677734,36.0009002685547}, {510.385406494141,61.0124320983887,24.0011005401611}, {515.358764648438,55.4658317565918,24.0011005401611}, {489.32861328125,47.7324333190918,31.5009002685547}, {492.95361328125,47.7324333190918,33.5634994506836}, {489.32861328125,47.7324333190918,34.5009002685547}, {489.32861328125,47.7324333190918,28.5011005401611}, {489.32861328125,47.7324333190918,25.5011005401611}, {492.95361328125,47.7324333190918,26.4385013580322}, {492.95361328125,47.7324333190918,30.5635013580322}, {492.95361328125,47.7324333190918,32.0634994506836}, {492.95361328125,47.7324333190918,31.3135013580322}, {492.95361328125,47.7324333190918,35.4384994506836}, {489.32861328125,47.7324333190918,36.0009002685547}, {492.95361328125,47.7324333190918,34.3134994506836}, {492.95361328125,47.7324333190918,34.6884994506836}, {492.95361328125,47.7324333190918,27.9385013580322}, {492.95361328125,47.7324333190918,28.6885013580322}, {492.95361328125,47.7324333190918,29.0635013580322}, {489.32861328125,47.7324333190918,24.0011005401611}, {492.95361328125,47.7324333190918,24.5635013580322}, {492.95361328125,47.7324333190918,25.6885013580322}, {492.95361328125,47.7324333190918,25.3135013580322}, {492.95361328125,47.7324333190918,24.1885013580322}, {492.95361328125,47.7324333190918,24.0011005401611}, {513.443786621094,50.7324333190918,24.0011005401611}, {492.95361328125,47.7324333190918,35.8134994506836}, {492.95361328125,47.7324333190918,36.0009002685547}, {513.443786621094,50.7324333190918,36.0009002685547}, {506.350402832031,51.4824333190918,36.0009002685547}, {506.350402832031,51.4824333190918,24.0011005401611}, {492.743804931641,48.2324333190918,24.0011005401611}, {492.638793945312,48.4824333190918,24.0011005401611}, {492.743804931641,48.2324333190918,36.0009002685547}, {492.638793945312,48.4824333190918,36.0009002685547}, {490.089599609375,50.9824333190918,36.0009002685547}, {490.089599609375,50.9824333190918,24.0011005401611}, {510.342010498047,51.7324333190918,30.0011005401611}, {499.068786621094,67.5192337036133,36.0009002685547}, {494.228607177734,66.6658325195312,36.0009002685547}, {499.375183105469,76.9058380126953,24.0011005401611}, {506.421813964844,76.9058380126953,36.0009002685547}, {506.608795166016,67.5192337036133,24.0011005401611}, {505.728607177734,65.7524337768555,24.0011005401611}, {509.09521484375,64.7458343505859,24.0011005401611}, {506.701995849609,62.8258323669434,30.0011005401611}, {505.728607177734,65.7524337768555,27.0011005401611}, {501.835388183594,71.6058349609375,27.0011005401611}, {499.888793945312,65.7524337768555,27.0011005401611}, {494.228607177734,66.6658325195312,30.0011005401611}, {495.553588867188,70.3992309570312,28.5011005401611}, {492.903594970703,62.9324340820312,28.5011005401611}, {495.553588867188,70.3992309570312,31.5009002685547}, {492.903594970703,62.9324340820312,31.5009002685547}, {511.488800048828,66.6658325195312,24.0011005401611}, {511.488800048828,66.6658325195312,30.0011005401611}, {512.778564453125,62.9324340820312,28.5011005401611}, {515.358764648438,55.4658317565918,31.5009002685547}, {507.618804931641,77.8658294677734,31.5009002685547}, {510.198791503906,70.3992309570312,28.5011005401611}, {511.488800048828,66.6658325195312,36.0009002685547}, {512.778564453125,62.9324340820312,31.5009002685547}, {510.198791503906,70.3992309570312,31.5009002685547}, {502.788818359375,57.7058334350586,24.0011005401611}, {497.195404052734,57.7058334350586,30.0011005401611}, {492.903594970703,62.9324340820312,34.5009002685547}, {492.903594970703,62.9324340820312,36.0009002685547}, {495.553588867188,70.3992309570312,24.0011005401611}, {496.725189208984,69.4392318725586,24.0011005401611}, {495.553588867188,70.3992309570312,25.5011005401611}, {495.246795654297,61.0124320983887,24.0011005401611}, {492.903594970703,62.9324340820312,25.5011005401611}, {492.903594970703,62.9324340820312,24.0011005401611}, {495.553588867188,70.3992309570312,36.0009002685547}, {496.725189208984,69.4392318725586,36.0009002685547}, {495.553588867188,70.3992309570312,34.5009002685547}, {510.198791503906,70.3992309570312,36.0009002685547}, {509.002014160156,69.4392318725586,36.0009002685547}, {510.198791503906,70.3992309570312,34.5009002685547}, {512.778564453125,62.9324340820312,25.5011005401611}, {512.778564453125,62.9324340820312,24.0011005401611}, {510.198791503906,70.3992309570312,24.0011005401611}, {509.002014160156,69.4392318725586,24.0011005401611}, {510.198791503906,70.3992309570312,25.5011005401611}, {510.385406494141,61.0124320983887,36.0009002685547}, {512.778564453125,62.9324340820312,34.5009002685547}, {512.778564453125,62.9324340820312,36.0009002685547}, {496.840393066406,51.2324333190918,36.0009002685547}, {498.981994628906,51.7324333190918,36.0009002685547}, {498.981994628906,51.7324333190918,33.0009002685547}, {506.555206298828,51.7324333190918,24.0011005401611}, {506.555206298828,51.7324333190918,27.0011005401611}, {503.82861328125,47.7324333190918,30.7509002685547}, {507.45361328125,47.7324333190918,32.8134994506836}, {503.82861328125,47.7324333190918,33.7509002685547}, {503.82861328125,47.7324333190918,29.2511005401611}, {503.82861328125,47.7324333190918,26.2511005401611}, {507.45361328125,47.7324333190918,27.1885013580322}, {493.921813964844,57.2792320251465,36.0009002685547}, {491.425201416016,54.5058326721191,36.0009002685547}, {497.195404052734,57.7058334350586,24.0011005401611}, {496.418792724609,60.052433013916,24.0011005401611}, {509.188812255859,60.052433013916,36.0009002685547}, {511.675415039062,57.2792320251465,24.0011005401611}, {514.161987304688,54.5058326721191,24.0011005401611}, {507.45361328125,47.7324333190918,34.3134994506836}, {503.82861328125,47.7324333190918,35.2509002685547}, {507.45361328125,47.7324333190918,25.6885013580322}, {503.82861328125,47.7324333190918,24.7511005401611}, {500.20361328125,47.7324333190918,31.6885013580322}, {500.20361328125,47.7324333190918,28.3135013580322}, {500.20361328125,47.7324333190918,30.1885013580322}, {507.45361328125,47.7324333190918,29.8135013580322}, {507.45361328125,47.7324333190918,31.3135013580322}, {507.45361328125,47.7324333190918,30.5635013580322}, {503.82861328125,47.7324333190918,36.0009002685547}, {507.45361328125,47.7324333190918,35.4384994506836}, {507.45361328125,47.7324333190918,35.0634994506836}, {507.45361328125,47.7324333190918,28.6885013580322}, {507.45361328125,47.7324333190918,29.4385013580322}, {503.82861328125,47.7324333190918,24.0011005401611}, {507.45361328125,47.7324333190918,24.5635013580322}, {507.45361328125,47.7324333190918,24.9385013580322}, {500.20361328125,47.7324333190918,34.6884994506836}, {500.20361328125,47.7324333190918,33.1884994506836}, {500.20361328125,47.7324333190918,33.9384994506836}, {500.20361328125,47.7324333190918,25.3135013580322}, {500.20361328125,47.7324333190918,26.8135013580322}, {500.20361328125,47.7324333190918,26.0635013580322}, {500.20361328125,47.7324333190918,30.9385013580322}, {500.20361328125,47.7324333190918,35.0634994506836}, {500.20361328125,47.7324333190918,35.4384994506836}, {500.20361328125,47.7324333190918,29.0635013580322}, {500.20361328125,47.7324333190918,29.4385013580322}, {500.20361328125,47.7324333190918,24.9385013580322}, {500.20361328125,47.7324333190918,24.5635013580322}, {507.45361328125,47.7324333190918,24.1885013580322}, {507.45361328125,47.7324333190918,24.0011005401611}, {513.86376953125,49.7324333190918,24.0011005401611}, {507.45361328125,47.7324333190918,35.8134994506836}, {507.45361328125,47.7324333190918,36.0009002685547}, {513.86376953125,49.7324333190918,36.0009002685547}, {500.20361328125,47.7324333190918,24.1885013580322}, {500.20361328125,47.7324333190918,24.0011005401611}, {502.988800048828,49.7324333190918,24.0011005401611}, {500.20361328125,47.7324333190918,35.8134994506836}, {500.20361328125,47.7324333190918,36.0009002685547}, {502.988800048828,49.7324333190918,36.0009002685547}, {504.755187988281,62.8258323669434,27.0011005401611}, {499.205383300781,51.2324333190918,36.0009002685547}, {498.786193847656,51.1074333190918,36.0009002685547}, {502.358795166016,51.2324333190918,36.0009002685547}, {499.205383300781,51.2324333190918,24.0011005401611}, {502.358795166016,51.2324333190918,24.0011005401611}, {498.786193847656,51.1074333190918,24.0011005401611}, {502.568786621094,50.7324333190918,24.0011005401611}, {505.931213378906,51.3574333190918,24.0011005401611}, {509.503601074219,51.4824333190918,24.0011005401611}, {502.568786621094,50.7324333190918,36.0009002685547}, {505.931213378906,51.3574333190918,36.0009002685547}, {509.503601074219,51.4824333190918,36.0009002685547}, {499.048583984375,50.4824333190918,36.0009002685547}, {492.428588867188,48.9824333190918,36.0009002685547}, {499.048583984375,50.4824333190918,24.0011005401611}, {492.428588867188,48.9824333190918,24.0011005401611}, {506.088806152344,50.9824333190918,24.0011005401611}, {506.036010742188,51.1074333190918,24.0011005401611}, {506.088806152344,50.9824333190918,36.0009002685547}, {506.036010742188,51.1074333190918,36.0009002685547}, {498.891204833984,50.8574333190918,36.0009002685547}, {498.943786621094,50.7324333190918,36.0009002685547}, {498.891204833984,50.8574333190918,24.0011005401611}, {498.943786621094,50.7324333190918,24.0011005401611}, {499.573608398438,49.2324333190918,24.0011005401611}, {499.783813476562,48.7324333190918,24.0011005401611}, {499.573608398438,49.2324333190918,36.0009002685547}, {499.783813476562,48.7324333190918,36.0009002685547}, {506.403594970703,50.2324333190918,24.0011005401611}, {506.298797607422,50.4824333190918,24.0011005401611}, {506.403594970703,50.2324333190918,36.0009002685547}, {506.298797607422,50.4824333190918,36.0009002685547}, {501.228607177734,81.599235534668,27.0011005401611}, {502.928588867188,81.599235534668,24.0011005401611}, {499.2587890625,49.9824333190918,36.0009002685547}, {499.363800048828,49.7324333190918,36.0009002685547}, {499.2587890625,49.9824333190918,24.0011005401611}, {499.363800048828,49.7324333190918,24.0011005401611}, {496.695404052734,56.2124328613281,27.0011005401611}, {496.195404052734,54.7190322875977,24.0011005401611}, {509.851989746094,53.2258338928223,27.0011005401611}, {493.464782714844,51.1074333190918,36.0009002685547}, {493.464782714844,51.1074333190918,24.0011005401611}, {502.768798828125,51.7324333190918,24.0011005401611}, {500.215789794922,51.3574333190918,24.0011005401611}, {497.628601074219,51.2324333190918,24.0011005401611}, {502.768798828125,51.7324333190918,36.0009002685547}, {500.215789794922,51.3574333190918,36.0009002685547}, {497.628601074219,51.2324333190918,36.0009002685547}, {507.033813476562,48.7324333190918,24.0011005401611}, {506.823791503906,49.2324333190918,24.0011005401611}, {507.033813476562,48.7324333190918,36.0009002685547}, {506.823791503906,49.2324333190918,36.0009002685547}, {494.4501953125,51.1074333190918,24.0011005401611}, {494.4501953125,51.1074333190918,36.0009002685547}, {500.807006835938,51.3574333190918,36.0009002685547}, {503.591186523438,51.4824333190918,36.0009002685547}, {503.591186523438,51.4824333190918,24.0011005401611}, {500.807006835938,51.3574333190918,24.0011005401611}, {505.728607177734,65.7524337768555,36.0009002685547}, {505.728607177734,65.7524337768555,33.0009002685547}, {499.221801757812,72.2124328613281,36.0009002685547}, {501.835388183594,71.6058349609375,36.0009002685547}, {506.515197753906,72.2124328613281,24.0011005401611}, {503.781982421875,71.6058349609375,24.0011005401611}, {503.781982421875,71.6058349609375,27.0011005401611}, {499.888793945312,65.7524337768555,24.0011005401611}, {495.695404052734,53.2258338928223,33.0009002685547}, {516.648803710938,51.7324333190918,30.0011005401611}, {498.20361328125,77.8658294677734,28.5011005401611}, {505.585388183594,57.7058334350586,33.0009002685547}, {508.871795654297,56.2124328613281,33.0009002685547}, {499.992004394531,57.7058334350586,27.0011005401611}, {504.628601074219,81.599235534668,33.0009002685547}, {500.861999511719,62.8258323669434,33.0009002685547}, {496.878601074219,74.1324310302734,27.0011005401611}, {496.878601074219,74.1324310302734,33.0009002685547}, {491.57861328125,59.199031829834,27.0011005401611}, {490.253601074219,55.4658317565918,28.5011005401611}, {491.57861328125,59.199031829834,33.0009002685547}, {514.068786621094,59.199031829834,27.0011005401611}, {514.068786621094,59.199031829834,33.0009002685547}, {508.908813476562,74.1324310302734,27.0011005401611}, {507.618804931641,77.8658294677734,28.5011005401611}, {508.908813476562,74.1324310302734,33.0009002685547}, {491.271789550781,50.9824333190918,36.0009002685547}, {490.877807617188,50.9824333190918,36.0009002685547}, {491.271789550781,50.9824333190918,24.0011005401611}, {490.877807617188,50.9824333190918,24.0011005401611}, {495.213806152344,50.9824333190918,36.0009002685547}, {493.636993408203,50.9824333190918,36.0009002685547}, {495.213806152344,50.9824333190918,24.0011005401611}, {493.636993408203,50.9824333190918,24.0011005401611}, {503.985412597656,51.4824333190918,36.0009002685547}, {503.985412597656,51.4824333190918,24.0011005401611}, {511.675415039062,57.2792320251465,36.0009002685547}, {493.921813964844,57.2792320251465,24.0011005401611}, {502.768798828125,51.7324333190918,30.0011005401611}, {506.555206298828,51.7324333190918,30.0011005401611}, {498.981994628906,51.7324333190918,30.0011005401611}, {492.848815917969,50.9824333190918,24.0011005401611}, {492.848815917969,50.9824333190918,36.0009002685547}, {500.861999511719,68.6792297363281,36.0009002685547}, {500.861999511719,68.6792297363281,24.0011005401611}, {496.878601074219,74.1324310302734,24.0011005401611}, {496.878601074219,74.1324310302734,36.0009002685547}, {504.755187988281,68.6792297363281,24.0011005401611}, {504.755187988281,68.6792297363281,36.0009002685547}, {508.908813476562,74.1324310302734,36.0009002685547}, {508.908813476562,74.1324310302734,24.0011005401611}, {505.728607177734,65.7524337768555,30.0011005401611}, {504.755187988281,68.6792297363281,30.0011005401611}, {503.781982421875,71.6058349609375,30.0011005401611}, {500.861999511719,68.6792297363281,30.0011005401611}, {499.888793945312,65.7524337768555,30.0011005401611}, {501.835388183594,71.6058349609375,30.0011005401611}, {491.57861328125,59.199031829834,24.0011005401611}, {491.57861328125,59.199031829834,36.0009002685547}, {514.068786621094,59.199031829834,36.0009002685547}, {514.068786621094,59.199031829834,24.0011005401611}, {511.07861328125,47.7324333190918,34.8759002685547}, {511.07861328125,47.7324333190918,31.8759002685547}, {514.70361328125,47.7324333190918,33.9384994506836}, {511.07861328125,47.7324333190918,25.1261005401611}, {514.70361328125,47.7324333190918,26.0635013580322}, {511.07861328125,47.7324333190918,28.1261005401611}, {502.788818359375,57.7058334350586,30.0011005401611}, {502.048583984375,79.8324356079102,36.0009002685547}, {514.70361328125,47.7324333190918,30.9385013580322}, {511.07861328125,47.7324333190918,30.3759002685547}, {514.70361328125,47.7324333190918,29.0635013580322}, {511.07861328125,47.7324333190918,29.6261005401611}, {496.57861328125,47.7324333190918,31.1259002685547}, {496.57861328125,47.7324333190918,32.6259002685547}, {496.57861328125,47.7324333190918,34.1259002685547}, {496.57861328125,47.7324333190918,28.8761005401611}, {496.57861328125,47.7324333190918,27.3761005401611}, {496.57861328125,47.7324333190918,25.8761005401611}, {496.57861328125,47.7324333190918,29.6261005401611}, {514.70361328125,47.7324333190918,35.4384994506836}, {511.07861328125,47.7324333190918,35.6259002685547}, {514.70361328125,47.7324333190918,24.5635013580322}, {511.07861328125,47.7324333190918,24.3761005401611}, {496.57861328125,47.7324333190918,34.8759002685547}, {496.57861328125,47.7324333190918,25.1261005401611}, {496.57861328125,47.7324333190918,35.6259002685547}, {496.57861328125,47.7324333190918,24.3761005401611}, {511.07861328125,47.7324333190918,36.0009002685547}, {511.07861328125,47.7324333190918,24.0011005401611}, {514.70361328125,47.7324333190918,30.1885013580322}, {514.70361328125,47.7324333190918,35.8134994506836}, {514.70361328125,47.7324333190918,29.8135013580322}, {514.70361328125,47.7324333190918,24.1885013580322}, {496.57861328125,47.7324333190918,36.0009002685547}, {496.57861328125,47.7324333190918,24.0011005401611}, {510.238800048828,49.7324333190918,24.0011005401611}, {510.238800048828,49.7324333190918,36.0009002685547}, {514.70361328125,47.7324333190918,24.0011005401611}, {514.70361328125,47.7324333190918,36.0009002685547}, {496.158813476562,48.7324333190918,36.0009002685547}, {496.158813476562,48.7324333190918,24.0011005401611}, {502.808807373047,62.8258323669434,30.0011005401611}, {509.608795166016,51.2324333190918,24.0011005401611}, {509.608795166016,51.2324333190918,36.0009002685547}, {491.641204833984,50.8574333190918,24.0011005401611}, {495.423797607422,50.4824333190918,36.0009002685547}, {495.423797607422,50.4824333190918,24.0011005401611}, {491.641204833984,50.8574333190918,36.0009002685547}, {495.528594970703,50.2324333190918,24.0011005401611}, {492.0087890625,49.9824333190918,24.0011005401611}, {509.818786621094,50.7324333190918,24.0011005401611}, {495.948608398438,49.2324333190918,36.0009002685547}, {495.528594970703,50.2324333190918,36.0009002685547}, {495.948608398438,49.2324333190918,24.0011005401611}, {509.818786621094,50.7324333190918,36.0009002685547}, {492.0087890625,49.9824333190918,36.0009002685547}, {491.956207275391,50.1074333190918,24.0011005401611}, {491.956207275391,50.1074333190918,36.0009002685547}, {502.928588867188,81.599235534668,30.0011005401611}, {491.851013183594,50.3574333190918,36.0009002685547}, {491.851013183594,50.3574333190918,24.0011005401611}, {496.195404052734,54.7190322875977,30.0011005401611}, {509.361999511719,54.7190322875977,30.0011005401611}, {488.632598876953,51.7256317138672,30.0011005401611}, {488.632598876953,51.7256317138672,29.5091018676758}, {488.632598876953,51.7188339233398,24.0011005401611}, {488.632598876953,51.7256317138672,27.4929008483887}, {488.632598876953,51.7324333190918,30.0011005401611}, {488.632598876953,51.7324333190918,29.0175018310547}, {488.632598876953,51.7324333190918,24.9847011566162}, {488.632598876953,51.7324333190918,24.0011005401611}, {488.632598876953,51.7188339233398,30.0011005401611}, {488.632598876953,51.7176322937012,24.0011005401611}, {488.632598876953,51.7182312011719,30.0011005401611}, {488.632598876953,51.7176322937012,30.0011005401611}, {488.632598876953,51.715030670166,24.0011005401611}, {488.632598876953,51.7162322998047,30.0011005401611}, {488.632598876953,50.761833190918,24.0011005401611}, {488.632598876953,50.7578315734863,24.0011005401611}, {488.632598876953,50.7598342895508,30.0011005401611}, {488.632598876953,50.7522315979004,24.0011005401611}, {488.632598876953,49.7838325500488,24.0011005401611}, {488.632598876953,50.2680320739746,30.0011005401611}, {488.632598876953,51.7046318054199,24.0011005401611}, {488.632598876953,51.709831237793,30.0011005401611}, {488.632598876953,50.9120330810547,24.0011005401611}, {488.632598876953,50.8882331848145,24.0011005401611}, {488.632598876953,50.9002304077148,30.0011005401611}, {488.632598876953,47.7324333190918,24.0370998382568}, {488.632598876953,48.5612335205078,30.0011005401611}, {488.632598876953,47.7324333190918,24.0011005401611}, {488.632598876953,47.7324333190918,24.1091003417969}, {488.632598876953,48.5612335205078,30.0189018249512}, {488.632598876953,47.7324333190918,25.3211002349854}, {488.632598876953,48.5612335205078,30.0551013946533}, {488.632598876953,47.7324333190918,25.4651012420654}, {488.632598876953,48.5612335205078,30.6609001159668}, {488.632598876953,47.7324333190918,25.5371017456055}, {488.632598876953,48.5612335205078,30.7329006195068}, {488.632598876953,47.7324333190918,25.6091003417969}, {488.632598876953,48.5612335205078,30.7689018249512}, {488.632598876953,47.7324333190918,25.8971004486084}, {488.632598876953,48.5612335205078,30.8051013946533}, {488.632598876953,47.7324333190918,28.321102142334}, {488.632598876953,48.5612335205078,30.9491004943848}, {488.632598876953,47.7324333190918,28.4651012420654}, {488.632598876953,48.5612335205078,32.1609001159668}, {488.632598876953,47.7324333190918,28.5371017456055}, {488.632598876953,48.5612335205078,32.2329025268555}, {488.632598876953,47.7324333190918,28.6811008453369}, {488.632598876953,48.5612335205078,32.2689018249512}, {488.632598876953,47.7324333190918,31.1049003601074}, {488.632598876953,48.5612335205078,32.3411026000977}, {488.632598876953,47.7324333190918,31.3929004669189}, {488.632598876953,49.3900299072266,36.0009002685547}, {488.632598876953,47.7324333190918,31.536901473999}, {488.632598876953,47.7324333190918,31.6809005737305}, {488.632598876953,47.7324333190918,34.1049003601074}, {488.632598876953,47.7324333190918,34.3929023742676}, {488.632598876953,47.7324333190918,34.464900970459}, {488.632598876953,47.7324333190918,34.5369033813477}, {488.632598876953,47.7324333190918,34.6809005737305}, {488.632598876953,47.7324333190918,35.8929023742676}, {488.632598876953,47.7324333190918,35.964900970459}, {488.632598876953,47.7324333190918,36.0009002685547}, {488.632598876953,50.8816299438477,24.0011005401611}, {488.632598876953,50.8850326538086,30.0011005401611}, {488.632598876953,49.7480316162109,24.0011005401611}, {488.632598876953,49.7426300048828,24.0011005401611}, {488.632598876953,49.745231628418,30.0011005401611}, {488.632598876953,49.7592315673828,24.0011005401611}, {488.632598876953,49.7536315917969,30.0011005401611}, {488.632598876953,49.3900299072266,24.0011005401611}, {488.632598876953,49.5664329528809,30.0011005401611}, {488.632598876953,50.8786315917969,24.0011005401611}, {488.632598876953,50.7764320373535,24.0011005401611}, {488.632598876953,50.8274307250977,30.0011005401611}, {488.632598876953,50.7550315856934,30.0011005401611}, {488.632598876953,50.7692337036133,30.0011005401611}, {488.632598876953,50.9284324645996,24.0011005401611}, {488.632598876953,50.9202308654785,30.0011005401611}, {488.632598876953,51.1788330078125,24.0011005401611}, {488.632598876953,51.139232635498,24.0011005401611}, {488.632598876953,51.1590309143066,30.0011005401611}, {488.632598876953,51.2324333190918,24.0011005401611}, {488.632598876953,51.2056312561035,30.0011005401611}, {488.632598876953,51.4340324401855,24.0011005401611}, {488.632598876953,51.3946304321289,24.0011005401611}, {488.632598876953,51.4142303466797,30.0011005401611}, {488.632598876953,51.4498329162598,24.0011005401611}, {488.632598876953,51.5772323608398,30.0011005401611}, {488.632598876953,51.4418334960938,30.0011005401611}, {488.632598876953,51.3136329650879,30.0011005401611}, {488.632598876953,49.7714309692383,30.0011005401611}, {488.632598876953,51.0338325500488,30.0011005401611}, {488.632598876953,50.8816299438477,30.0011005401611}, {488.632598876953,50.8800315856934,30.0011005401611}, {488.632598876953,51.7188339233398,36.0009002685547}, {488.632598876953,51.7176322937012,36.0009002685547}, {488.632598876953,49.3900299072266,30.0011005401611}, {488.632598876953,50.7522315979004,30.0011005401611}, {488.632598876953,50.7522315979004,36.0009002685547}, {488.632598876953,49.7426300048828,30.0011005401611}, {488.632598876953,49.7426300048828,36.0009002685547}, {488.632598876953,49.7480316162109,30.0011005401611}, {488.632598876953,49.7480316162109,36.0009002685547}, {488.632598876953,51.715030670166,30.0011005401611}, {488.632598876953,51.715030670166,36.0009002685547}, {488.632598876953,50.7578315734863,30.0011005401611}, {488.632598876953,50.7578315734863,36.0009002685547}, {488.632598876953,50.761833190918,30.0011005401611}, {488.632598876953,50.761833190918,36.0009002685547}, {488.632598876953,50.8882331848145,30.0011005401611}, {488.632598876953,50.8882331848145,36.0009002685547}, {488.632598876953,49.7592315673828,30.0011005401611}, {488.632598876953,49.7592315673828,36.0009002685547}, {488.632598876953,51.1788330078125,30.0011005401611}, {488.632598876953,51.1788330078125,36.0009002685547}, {488.632598876953,50.9120330810547,30.0011005401611}, {488.632598876953,50.9120330810547,36.0009002685547}, {488.632598876953,51.4498329162598,30.0011005401611}, {488.632598876953,51.4498329162598,36.0009002685547}, {488.632598876953,51.7046318054199,30.0011005401611}, {488.632598876953,51.7046318054199,36.0009002685547}, {488.632598876953,51.2324333190918,30.0011005401611}, {488.632598876953,51.2324333190918,36.0009002685547}, {488.632598876953,51.3946304321289,30.0011005401611}, {488.632598876953,51.3946304321289,36.0009002685547}, {488.632598876953,51.4340324401855,30.0011005401611}, {488.632598876953,51.4340324401855,36.0009002685547}, {488.632598876953,49.7838325500488,30.0011005401611}, {488.632598876953,49.7838325500488,36.0009002685547}, {488.632598876953,50.7764320373535,30.0011005401611}, {488.632598876953,50.7764320373535,36.0009002685547}, {488.632598876953,51.139232635498,30.0011005401611}, {488.632598876953,51.139232635498,36.0009002685547}, {488.632598876953,50.9284324645996,30.0011005401611}, {488.632598876953,50.9284324645996,36.0009002685547}, {488.632598876953,50.8816299438477,36.0009002685547}, {488.632598876953,50.8786315917969,30.0011005401611}, {488.632598876953,50.8786315917969,36.0009002685547}, {488.632598876953,51.7324333190918,35.0173034667969}, {488.632598876953,51.7324333190918,36.0009002685547}, {488.632598876953,51.7324333190918,30.9847011566162}, {517.188415527344,51.7140884399414,24.0011005401611}, {517.188415527344,51.7140884399414,36.0009002685547}, {517.188415527344,50.4475173950195,24.0011005401611}, {517.188415527344,51.7324333190918,35.3734130859375}, {517.188415527344,51.7324333190918,36.0009002685547}, {517.188415527344,51.7324333190918,34.1185760498047}, {517.188415527344,51.7324333190918,31.88330078125}, {517.188415527344,51.7324333190918,30.0011005401611}, {517.188415527344,51.7324333190918,28.1187744140625}, {517.188415527344,51.7324333190918,25.8834266662598}, {517.188415527344,51.7324333190918,24.6285915374756}, {517.188415527344,51.7324333190918,24.0011005401611}, {517.188415527344,47.7324333190918,24.0600452423096}, {517.188415527344,47.7324333190918,24.0011005401611}, {517.188415527344,50.4475173950195,36.0009002685547}, {517.188415527344,47.7324333190918,24.1779975891113}, {517.188415527344,47.7324333190918,24.6498031616211}, {517.188415527344,47.7324333190918,28.7625770568848}, {517.188415527344,47.7324333190918,29.7061901092529}, {517.188415527344,47.7324333190918,29.9420928955078}, {517.188415527344,47.7324333190918,30.0600452423096}, {517.188415527344,47.7324333190918,30.2959480285645}, {517.188415527344,47.7324333190918,31.2395629882812}, {517.188415527344,47.7324333190918,35.3521995544434}, {517.188415527344,47.7324333190918,35.8240051269531}, {517.188415527344,47.7324333190918,35.9419555664062}, {517.188415527344,47.7324333190918,36.0009002685547} }, - { {0,1,2}, {3,4,5}, {6,7,8}, {9,10,11}, {12,2,1}, {12,1,13}, {14,15,16}, {17,18,19}, {20,21,22}, {17,19,23}, {24,25,26}, {27,13,1}, {28,25,29}, {30,31,32}, {28,33,34}, {35,36,7}, {37,38,39}, {40,10,41}, {42,43,44}, {45,5,4}, {46,47,48}, {46,48,49}, {45,4,50}, {51,52,53}, {51,54,55}, {56,52,57}, {58,59,60}, {61,50,4}, {62,63,64}, {65,34,33}, {66,67,42}, {68,17,69}, {70,71,22}, {66,42,72}, {73,16,15}, {35,7,74}, {75,76,54}, {77,27,1}, {78,32,31}, {75,54,79}, {80,26,25}, {81,80,25}, {82,83,48}, {84,20,85}, {81,25,86}, {87,88,19}, {0,89,1}, {90,91,92}, {90,10,93}, {38,94,39}, {94,95,39}, {3,7,96}, {97,15,98}, {97,99,15}, {92,91,100}, {89,101,1}, {102,39,95}, {103,11,10}, {104,96,7}, {105,15,99}, {106,61,4}, {107,108,33}, {76,55,54}, {109,91,110}, {111,23,19}, {112,63,113}, {114,115,48}, {116,59,117}, {118,20,119}, {120,31,121}, {122,44,43}, {110,91,123}, {124,125,126}, {127,128,129}, {127,130,124}, {131,124,132}, {126,133,134}, {135,136,126}, {137,138,127}, {139,127,138}, {128,140,141}, {142,128,143}, {144,140,145}, {100,91,146}, {147,148,134}, {101,149,1}, {102,150,39}, {103,10,151}, {145,140,152}, {152,140,153}, {148,154,134}, {154,155,134}, {156,15,105}, {157,104,7}, {36,8,7}, {158,37,39}, {159,19,88}, {160,19,159}, {161,59,58}, {161,117,59}, {162,31,30}, {162,121,31}, {163,43,164}, {163,165,43}, {166,167,43}, {167,164,43}, {168,57,52}, {82,48,169}, {114,170,171}, {108,65,33}, {64,63,112}, {114,172,170}, {160,173,170}, {171,170,173}, {172,174,170}, {160,170,174}, {175,176,177}, {178,77,1}, {179,31,120}, {175,180,176}, {181,182,176}, {177,176,182}, {180,183,176}, {181,176,183}, {184,42,67}, {185,69,17}, {160,111,19}, {186,187,160}, {188,189,114}, {190,188,114}, {114,48,191}, {192,114,193}, {194,160,195}, {196,160,194}, {197,198,181}, {199,197,181}, {122,43,165}, {200,201,175}, {202,175,203}, {204,175,202}, {205,119,20}, {206,181,207}, {208,209,15}, {210,15,209}, {211,10,9}, {212,10,211}, {213,214,215}, {216,217,218}, {219,14,17}, {113,63,220}, {221,222,48}, {191,48,222}, {22,223,20}, {205,20,223}, {224,40,42}, {123,91,225}, {214,226,215}, {227,215,226}, {218,217,228}, {229,228,217}, {215,230,213}, {125,135,126}, {217,216,231}, {129,128,142}, {216,213,232}, {130,132,124}, {213,216,233}, {234,213,235}, {236,227,237}, {238,237,227}, {239,240,216}, {233,216,240}, {241,242,229}, {243,229,242}, {215,227,244}, {245,215,246}, {217,247,229}, {248,249,217}, {232,213,250}, {230,250,213}, {133,147,134}, {244,227,251}, {236,252,227}, {251,227,252}, {231,216,253}, {254,253,216}, {141,140,144}, {247,255,229}, {241,229,256}, {255,256,229}, {257,241,258}, {259,146,91}, {260,261,236}, {262,1,149}, {263,264,241}, {265,241,264}, {266,236,267}, {268,267,236}, {49,48,83}, {166,43,269}, {270,271,272}, {273,274,275}, {276,274,277}, {278,151,10}, {279,280,272}, {281,39,150}, {272,282,279}, {155,283,134}, {274,276,284}, {153,140,285}, {286,276,287}, {265,276,286}, {288,289,279}, {268,288,279}, {290,291,272}, {271,290,272}, {292,274,293}, {275,274,292}, {294,265,295}, {276,265,294}, {296,297,268}, {279,296,268}, {241,265,298}, {298,265,299}, {236,300,268}, {300,301,268}, {107,33,78}, {302,303,59}, {304,305,279}, {282,304,279}, {306,276,307}, {284,276,306}, {185,17,73}, {308,309,221}, {158,39,70}, {310,41,10}, {15,311,208}, {7,6,312}, {313,314,6}, {315,6,314}, {316,208,317}, {318,317,208}, {258,241,319}, {319,241,320}, {261,321,236}, {321,322,236}, {6,315,323}, {208,324,318}, {270,325,318}, {326,318,325}, {327,328,315}, {273,315,328}, {118,329,20}, {330,20,329}, {331,332,25}, {86,25,332}, {333,334,52}, {335,52,334}, {115,336,48}, {169,48,336}, {62,106,4}, {35,15,210}, {35,337,15}, {158,10,212}, {158,310,10}, {338,178,1}, {339,59,116}, {107,302,59}, {66,22,340}, {66,341,22}, {185,221,342}, {185,308,221}, {75,31,179}, {75,343,31}, {166,20,330}, {166,85,20}, {81,52,335}, {81,168,52}, {82,19,344}, {82,87,19}, {108,339,345}, {346,108,345}, {64,347,348}, {349,347,64}, {178,109,350}, {351,178,350}, {179,352,353}, {354,352,179}, {355,208,356}, {356,208,311}, {357,358,6}, {358,312,6}, {68,22,21}, {68,340,22}, {221,48,47}, {184,342,221}, {359,270,360}, {318,360,270}, {361,362,273}, {315,273,362}, {272,102,270}, {363,270,102}, {274,273,103}, {364,103,273}, {21,19,18}, {21,20,84}, {184,46,42}, {43,42,46}, {12,22,71}, {365,22,12}, {14,98,15}, {14,220,63}, {40,93,10}, {40,225,91}, {45,221,309}, {366,221,45}, {313,367,212}, {212,367,368}, {36,369,367}, {313,36,367}, {316,37,367}, {37,368,367}, {210,367,369}, {316,367,210}, {362,370,315}, {370,323,315}, {360,318,371}, {371,318,324}, {372,331,159}, {159,195,160}, {373,115,56}, {115,114,189}, {52,56,161}, {374,161,56}, {25,28,331}, {375,331,28}, {376,333,163}, {163,203,175}, {377,118,24}, {118,181,198}, {25,24,162}, {378,162,24}, {52,51,333}, {379,333,51}, {167,380,381}, {376,167,381}, {377,381,330}, {330,381,380}, {335,381,382}, {376,381,335}, {373,383,169}, {169,383,384}, {168,385,383}, {373,168,383}, {372,87,383}, {87,384,383}, {377,80,381}, {80,382,381}, {86,383,385}, {372,383,86}, {106,348,347}, {386,106,347}, {375,65,346}, {108,346,65}, {64,112,349}, {387,349,112}, {171,190,114}, {346,345,171}, {374,190,345}, {171,345,190}, {349,172,347}, {172,114,192}, {386,347,192}, {172,192,347}, {173,160,196}, {171,173,346}, {375,346,196}, {173,196,346}, {172,349,174}, {174,186,160}, {387,186,349}, {174,349,186}, {64,348,62}, {106,62,348}, {108,107,339}, {59,339,107}, {374,345,116}, {339,116,345}, {76,353,352}, {379,76,352}, {388,77,351}, {178,351,77}, {179,120,354}, {378,354,120}, {177,200,175}, {351,350,177}, {389,200,350}, {177,350,200}, {354,180,352}, {180,175,204}, {379,352,204}, {180,204,352}, {182,181,206}, {177,182,351}, {388,351,206}, {182,206,351}, {180,354,183}, {183,199,181}, {378,199,354}, {183,354,199}, {91,109,338}, {178,338,109}, {76,75,353}, {179,353,75}, {389,350,110}, {109,110,350}, {390,391,392}, {393,394,395}, {224,122,389}, {122,175,201}, {365,388,205}, {205,207,181}, {66,340,396}, {68,396,340}, {184,396,342}, {185,342,396}, {66,396,67}, {184,67,396}, {68,69,396}, {185,396,69}, {219,111,387}, {111,160,187}, {366,386,191}, {191,193,114}, {150,272,280}, {102,272,150}, {151,277,274}, {103,151,274}, {161,374,117}, {116,117,374}, {366,61,386}, {106,386,61}, {111,187,387}, {186,387,187}, {56,188,374}, {190,374,188}, {191,386,193}, {192,193,386}, {331,375,194}, {196,194,375}, {28,34,375}, {65,375,34}, {219,387,113}, {112,113,387}, {224,389,123}, {110,123,389}, {51,55,379}, {76,379,55}, {24,197,378}, {199,378,197}, {122,201,389}, {200,389,201}, {333,379,202}, {204,202,379}, {205,388,207}, {206,207,388}, {365,27,388}, {77,388,27}, {162,378,121}, {120,121,378}, {162,30,25}, {30,29,25}, {51,53,54}, {303,60,59}, {28,29,33}, {29,397,33}, {161,58,52}, {53,52,58}, {21,84,19}, {84,344,19}, {46,49,43}, {49,269,43}, {208,316,209}, {210,209,316}, {327,313,211}, {212,211,313}, {36,35,369}, {210,369,35}, {37,158,368}, {212,368,158}, {6,8,313}, {36,313,8}, {326,38,316}, {37,316,38}, {392,391,398}, {399,398,391}, {394,400,395}, {401,395,400}, {390,214,391}, {214,213,234}, {393,395,218}, {218,239,216}, {402,230,403}, {230,215,245}, {125,124,131}, {404,125,403}, {405,406,231}, {231,248,217}, {129,137,127}, {407,406,129}, {130,127,139}, {402,130,408}, {194,195,331}, {159,331,195}, {115,189,56}, {188,56,189}, {14,219,220}, {113,220,219}, {45,50,366}, {61,366,50}, {221,366,222}, {191,222,366}, {17,23,219}, {111,219,23}, {118,198,24}, {197,24,198}, {202,203,333}, {163,333,203}, {40,224,225}, {123,225,224}, {12,13,365}, {27,365,13}, {22,365,223}, {205,223,365}, {42,44,224}, {122,224,44}, {399,391,234}, {214,234,391}, {401,239,395}, {218,395,239}, {214,390,226}, {226,238,227}, {218,228,393}, {228,229,243}, {401,399,233}, {233,235,213}, {392,409,390}, {410,390,409}, {394,393,411}, {412,411,393}, {402,403,131}, {125,131,403}, {405,137,406}, {129,406,137}, {405,408,139}, {130,139,408}, {230,245,403}, {404,403,245}, {231,406,248}, {407,248,406}, {232,254,216}, {402,408,232}, {413,404,244}, {244,246,215}, {414,247,407}, {247,217,249}, {133,126,136}, {415,133,413}, {141,143,128}, {416,414,141}, {410,238,390}, {226,390,238}, {412,393,243}, {228,243,393}, {233,399,235}, {234,235,399}, {237,260,236}, {238,410,237}, {417,260,410}, {237,410,260}, {239,401,240}, {233,240,401}, {242,241,257}, {243,242,412}, {418,412,257}, {242,257,412}, {401,419,399}, {398,399,419}, {417,410,420}, {409,420,410}, {400,421,401}, {419,401,421}, {418,422,412}, {411,412,422}, {413,135,404}, {125,404,135}, {414,407,142}, {129,142,407}, {130,402,132}, {131,132,402}, {133,136,413}, {135,413,136}, {423,147,415}, {133,415,147}, {137,405,138}, {139,138,405}, {141,414,143}, {142,143,414}, {424,416,144}, {141,144,416}, {405,254,408}, {232,408,254}, {244,404,246}, {245,246,404}, {247,249,407}, {248,407,249}, {232,250,402}, {230,402,250}, {415,413,251}, {244,251,413}, {252,236,266}, {251,252,415}, {423,415,266}, {252,266,415}, {231,253,405}, {254,405,253}, {416,255,414}, {247,414,255}, {256,263,241}, {255,416,256}, {424,263,416}, {256,416,263}, {257,258,418}, {425,418,258}, {260,417,261}, {426,261,417}, {422,418,427}, {427,259,91}, {420,428,417}, {428,1,262}, {147,423,148}, {429,148,423}, {263,424,264}, {264,295,265}, {266,267,423}, {267,268,297}, {144,145,424}, {430,424,145}, {49,431,269}, {166,269,431}, {82,431,83}, {49,83,431}, {84,85,431}, {166,431,85}, {82,344,431}, {84,431,344}, {432,278,90}, {10,90,278}, {433,0,281}, {39,281,0}, {362,361,434}, {435,271,359}, {270,359,271}, {436,361,275}, {273,275,361}, {360,437,359}, {277,287,276}, {151,278,277}, {280,279,289}, {150,280,281}, {436,438,439}, {439,285,140}, {90,92,432}, {440,432,92}, {282,272,291}, {441,282,442}, {284,293,274}, {443,438,284}, {278,432,286}, {286,299,265}, {281,288,433}, {288,268,301}, {0,433,89}, {444,89,433}, {435,445,442}, {445,134,283}, {439,446,436}, {361,436,446}, {442,290,435}, {271,435,290}, {438,436,292}, {275,292,436}, {445,435,447}, {359,447,435}, {286,287,278}, {277,278,287}, {288,281,289}, {280,289,281}, {145,152,430}, {443,430,152}, {148,429,154}, {441,154,429}, {424,430,294}, {294,307,276}, {423,296,429}, {296,279,305}, {425,440,100}, {92,100,440}, {290,442,291}, {282,291,442}, {292,293,438}, {284,438,293}, {298,320,241}, {432,440,298}, {300,236,322}, {433,300,444}, {426,101,444}, {89,444,101}, {107,448,302}, {302,79,54}, {78,31,343}, {107,78,448}, {75,79,448}, {302,448,79}, {78,343,448}, {75,448,343}, {427,418,259}, {425,259,418}, {428,262,417}, {426,417,262}, {437,449,359}, {447,359,449}, {434,361,450}, {446,450,361}, {32,33,397}, {78,33,32}, {53,303,54}, {302,54,303}, {152,153,443}, {438,443,153}, {429,304,441}, {282,441,304}, {430,443,306}, {284,306,443}, {154,441,155}, {442,155,441}, {298,299,432}, {286,432,299}, {300,433,301}, {288,301,433}, {185,451,308}, {308,74,7}, {73,15,337}, {185,73,451}, {35,74,451}, {308,451,74}, {73,337,451}, {35,451,337}, {158,452,310}, {310,72,42}, {70,22,341}, {158,70,452}, {66,72,452}, {310,452,72}, {70,341,452}, {66,452,341}, {313,327,314}, {315,314,327}, {316,317,326}, {318,326,317}, {15,156,311}, {356,311,156}, {7,312,157}, {358,157,312}, {211,9,327}, {364,327,9}, {38,326,94}, {363,94,326}, {294,295,424}, {264,424,295}, {296,423,297}, {267,297,423}, {262,149,426}, {101,426,149}, {258,319,425}, {440,425,319}, {261,426,321}, {444,321,426}, {259,425,146}, {100,146,425}, {306,307,430}, {294,430,307}, {304,429,305}, {296,305,429}, {319,320,440}, {298,440,320}, {321,444,322}, {300,322,444}, {445,283,442}, {155,442,283}, {439,438,285}, {153,285,438}, {17,68,18}, {21,18,68}, {46,184,47}, {221,47,184}, {102,95,363}, {94,363,95}, {9,11,364}, {103,364,11}, {6,323,357}, {370,357,323}, {371,324,355}, {208,355,324}, {270,363,325}, {326,325,363}, {327,364,328}, {273,328,364}, {0,2,39}, {12,39,2}, {90,93,91}, {40,91,93}, {14,16,17}, {73,17,16}, {45,309,7}, {308,7,309}, {12,71,39}, {70,39,71}, {40,41,42}, {310,42,41}, {97,98,63}, {14,63,98}, {3,5,7}, {45,7,5}, {118,377,329}, {330,329,377}, {331,372,332}, {86,332,372}, {333,376,334}, {335,334,376}, {115,373,336}, {169,336,373}, {167,166,380}, {330,380,166}, {80,81,382}, {335,382,81}, {86,385,81}, {168,81,385}, {169,384,82}, {87,82,384}, {159,88,372}, {87,372,88}, {163,164,376}, {167,376,164}, {24,26,377}, {80,377,26}, {56,57,373}, {168,373,57}, {32,397,30}, {29,30,397}, {58,60,53}, {303,53,60}, {205,181,119}, {118,119,181}, {163,175,165}, {122,165,175}, {453,454,455}, {454,456,455}, {457,455,456}, {458,455,457}, {459,455,458}, {460,455,459}, {461,462,463}, {464,465,466}, {467,468,469}, {470,471,472}, {465,473,474}, {475,476,477}, {478,479,480}, {481,482,478}, {483,484,481}, {485,486,483}, {487,488,485}, {489,490,487}, {491,492,489}, {493,494,491}, {495,496,493}, {497,498,495}, {499,500,497}, {501,502,499}, {503,504,501}, {505,504,503}, {506,504,505}, {507,504,506}, {508,504,507}, {509,504,508}, {510,504,509}, {511,504,510}, {512,504,511}, {513,504,512}, {514,504,513}, {476,515,516}, {517,518,519}, {520,517,521}, {518,522,523}, {522,480,479}, {524,525,526}, {468,470,527}, {525,467,528}, {529,475,530}, {531,532,533}, {534,531,535}, {536,537,538}, {473,539,540}, {539,536,541}, {537,534,542}, {471,520,543}, {532,529,544}, {545,524,546}, {453,461,547}, {463,464,548}, {523,549,504}, {527,550,551}, {519,552,553}, {521,554,555}, {466,556,557}, {469,558,559}, {528,560,561}, {477,562,563}, {543,564,565}, {535,566,567}, {530,568,569}, {540,570,571}, {474,572,573}, {542,574,575}, {538,576,577}, {541,578,579}, {472,580,581}, {526,582,583}, {533,584,585}, {544,586,587}, {516,545,588}, {588,589,590}, {455,460,4}, {591,592,63}, {462,455,4}, {592,547,63}, {547,548,63}, {465,462,4}, {548,557,63}, {127,124,501}, {127,501,499}, {505,503,124}, {124,126,507}, {124,507,506}, {509,508,126}, {126,134,512}, {126,512,511}, {510,509,126}, {128,127,493}, {128,493,491}, {497,495,127}, {489,487,128}, {140,128,483}, {140,483,481}, {487,485,128}, {478,480,140}, {480,522,140}, {514,513,134}, {504,514,134}, {551,581,437}, {471,470,434}, {445,447,555}, {445,555,553}, {134,445,553}, {134,553,504}, {446,439,518}, {446,518,517}, {439,140,522}, {439,522,518}, {515,476,358}, {563,588,356}, {557,573,63}, {473,465,4}, {437,360,559}, {437,559,551}, {360,371,561}, {360,561,559}, {362,434,470}, {362,470,468}, {370,362,468}, {370,468,467}, {499,497,127}, {506,505,124}, {495,493,127}, {513,512,134}, {481,478,140}, {447,449,565}, {447,565,555}, {450,446,517}, {450,517,520}, {356,156,569}, {356,569,563}, {157,358,476}, {157,476,475}, {357,370,467}, {357,467,525}, {371,355,583}, {371,583,561}, {460,459,4}, {63,62,593}, {63,593,591}, {62,4,459}, {62,459,458}, {532,531,104}, {531,534,104}, {567,585,105}, {575,567,105}, {4,3,539}, {4,539,473}, {536,539,3}, {97,63,573}, {97,573,571}, {571,579,97}, {99,97,579}, {99,579,577}, {105,99,577}, {105,577,575}, {96,104,534}, {96,534,537}, {3,96,537}, {3,537,536}, {503,501,124}, {508,507,126}, {491,489,128}, {511,510,126}, {485,483,128}, {434,450,520}, {434,520,471}, {449,437,581}, {449,581,565}, {156,105,585}, {156,585,587}, {587,569,156}, {104,157,529}, {104,529,532}, {475,529,157}, {590,583,355}, {355,356,588}, {355,588,590}, {358,357,524}, {358,524,515}, {525,524,357}, {458,457,62}, {457,593,62}, {479,478,482}, {479,504,549}, {479,482,504}, {482,481,484}, {472,551,550}, {581,551,472}, {482,484,504}, {484,483,486}, {523,553,552}, {504,553,523}, {540,573,572}, {571,573,540}, {544,585,584}, {587,585,544}, {542,577,576}, {575,577,542}, {526,590,589}, {583,590,526}, {535,575,574}, {567,575,535}, {533,567,566}, {585,567,533}, {538,579,578}, {577,579,538}, {543,581,580}, {565,581,543}, {477,569,568}, {563,569,477}, {530,587,586}, {569,587,530}, {541,571,570}, {579,571,541}, {528,583,582}, {561,583,528}, {591,453,592}, {547,592,453}, {521,565,564}, {555,565,521}, {474,557,556}, {573,557,474}, {516,563,562}, {588,563,516}, {519,555,554}, {553,555,519}, {527,559,558}, {551,559,527}, {469,561,560}, {559,561,469}, {462,461,455}, {453,455,461}, {461,463,547}, {548,547,463}, {465,464,462}, {463,462,464}, {464,466,548}, {557,548,466}, {469,560,467}, {528,467,560}, {472,550,470}, {527,470,550}, {474,556,465}, {466,465,556}, {477,568,475}, {530,475,568}, {516,562,476}, {477,476,562}, {519,554,517}, {521,517,554}, {521,564,520}, {543,520,564}, {523,552,518}, {519,518,552}, {479,549,522}, {523,522,549}, {526,589,524}, {589,546,524}, {527,558,468}, {469,468,558}, {528,582,525}, {526,525,582}, {530,586,529}, {544,529,586}, {533,566,531}, {535,531,566}, {535,574,534}, {542,534,574}, {538,578,536}, {541,536,578}, {540,572,473}, {474,473,572}, {541,570,539}, {540,539,570}, {542,576,537}, {538,537,576}, {543,580,471}, {472,471,580}, {544,584,532}, {533,532,584}, {524,545,515}, {516,515,545}, {545,546,588}, {589,588,546}, {453,591,454}, {593,454,591}, {484,486,504}, {486,485,488}, {486,488,504}, {488,487,490}, {488,490,504}, {490,489,492}, {490,492,504}, {492,491,494}, {492,494,504}, {494,493,496}, {494,496,504}, {496,495,498}, {496,498,504}, {498,497,500}, {498,500,504}, {500,499,502}, {500,502,504}, {501,504,502}, {454,593,456}, {457,456,593}, {594,595,596}, {597,598,594}, {599,597,594}, {600,599,594}, {601,600,594}, {602,601,594}, {603,602,594}, {604,603,594}, {605,604,594}, {606,607,608}, {609,606,608}, {610,609,608}, {611,610,608}, {612,611,608}, {613,612,608}, {614,613,608}, {615,614,608}, {616,615,608}, {617,616,608}, {618,617,608}, {619,618,608}, {620,619,608}, {596,608,607}, {595,594,598}, {608,596,595}, {605,594,91}, {91,338,602}, {91,602,603}, {598,597,1}, {594,596,91}, {608,595,1}, {595,598,1}, {616,617,392}, {610,611,394}, {419,421,613}, {419,613,614}, {422,427,607}, {422,607,606}, {427,91,596}, {427,596,607}, {428,420,619}, {428,619,620}, {1,428,620}, {1,620,608}, {420,409,618}, {420,618,619}, {411,422,606}, {411,606,609}, {398,419,614}, {398,614,615}, {421,400,612}, {421,612,613}, {409,392,617}, {409,617,618}, {394,411,609}, {394,609,610}, {604,605,91}, {338,1,599}, {338,599,600}, {392,398,615}, {392,615,616}, {400,394,611}, {400,611,612}, {603,604,91}, {601,602,338}, {597,599,1}, {600,601,338} }); + { {513.075988769531f,51.6074333190918f,36.0009002685547f}, {516.648803710938f,51.7324333190918f,36.0009002685547f}, {513.495178222656f,51.7324333190918f,36.0009002685547f}, {489.391204833984f,51.4824333190918f,24.0011005401611f}, {488.928588867188f,51.7324333190918f,24.0011005401611f}, {492.06201171875f,51.7324333190918f,24.0011005401611f}, {496.840393066406f,51.2324333190918f,24.0011005401611f}, {495.195404052734f,51.7324333190918f,24.0011005401611f}, {498.981994628906f,51.7324333190918f,24.0011005401611f}, {506.966613769531f,51.6074333190918f,24.0011005401611f}, {510.342010498047f,51.7324333190918f,24.0011005401611f}, {507.163818359375f,51.6074333190918f,24.0011005401611f}, {512.515380859375f,54.7190322875977f,36.0009002685547f}, {514.161987304688f,54.5058326721191f,36.0009002685547f}, {493.06201171875f,54.7190322875977f,36.0009002685547f}, {495.195404052734f,51.7324333190918f,36.0009002685547f}, {496.195404052734f,54.7190322875977f,36.0009002685547f}, {497.195404052734f,57.7058334350586f,36.0009002685547f}, {500.851989746094f,60.2658309936523f,36.0009002685547f}, {498.915405273438f,62.8258323669434f,36.0009002685547f}, {506.701995849609f,62.8258323669434f,36.0009002685547f}, {503.648590087891f,60.2658309936523f,36.0009002685547f}, {508.381805419922f,57.7058334350586f,36.0009002685547f}, {496.418792724609f,60.052433013916f,36.0009002685547f}, {506.515197753906f,72.2124328613281f,36.0009002685547f}, {502.808807373047f,74.5324325561523f,36.0009002685547f}, {503.781982421875f,71.6058349609375f,36.0009002685547f}, {515.358764648438f,55.4658317565918f,36.0009002685547f}, {499.375183105469f,76.9058380126953f,36.0009002685547f}, {501.168792724609f,78.0658340454102f,36.0009002685547f}, {504.568786621094f,78.0658340454102f,36.0009002685547f}, {506.32861328125f,81.599235534668f,36.0009002685547f}, {502.928588867188f,81.599235534668f,36.0009002685547f}, {499.528594970703f,81.599235534668f,36.0009002685547f}, {498.20361328125f,77.8658294677734f,36.0009002685547f}, {495.195404052734f,51.7324333190918f,30.0011005401611f}, {498.981994628906f,51.7324333190918f,27.0011005401611f}, {506.555206298828f,51.7324333190918f,33.0009002685547f}, {506.555206298828f,51.7324333190918f,36.0009002685547f}, {510.342010498047f,51.7324333190918f,36.0009002685547f}, {512.515380859375f,54.7190322875977f,24.0011005401611f}, {509.361999511719f,54.7190322875977f,24.0011005401611f}, {508.381805419922f,57.7058334350586f,24.0011005401611f}, {506.701995849609f,62.8258323669434f,24.0011005401611f}, {509.188812255859f,60.052433013916f,24.0011005401611f}, {493.06201171875f,54.7190322875977f,24.0011005401611f}, {503.648590087891f,60.2658309936523f,24.0011005401611f}, {500.851989746094f,60.2658309936523f,24.0011005401611f}, {498.915405273438f,62.8258323669434f,24.0011005401611f}, {502.808807373047f,62.8258323669434f,24.0011005401611f}, {491.425201416016f,54.5058326721191f,24.0011005401611f}, {506.421813964844f,76.9058380126953f,24.0011005401611f}, {502.808807373047f,74.5324325561523f,24.0011005401611f}, {504.568786621094f,78.0658340454102f,24.0011005401611f}, {506.32861328125f,81.599235534668f,24.0011005401611f}, {507.618804931641f,77.8658294677734f,24.0011005401611f}, {499.221801757812f,72.2124328613281f,24.0011005401611f}, {501.835388183594f,71.6058349609375f,24.0011005401611f}, {501.168792724609f,78.0658340454102f,24.0011005401611f}, {499.528594970703f,81.599235534668f,24.0011005401611f}, {502.048583984375f,79.8324356079102f,24.0011005401611f}, {490.253601074219f,55.4658317565918f,24.0011005401611f}, {488.928588867188f,51.7324333190918f,30.0011005401611f}, {488.928588867188f,51.7324333190918f,36.0009002685547f}, {490.253601074219f,55.4658317565918f,31.5009002685547f}, {498.20361328125f,77.8658294677734f,34.5009002685547f}, {508.381805419922f,57.7058334350586f,30.0011005401611f}, {505.585388183594f,57.7058334350586f,27.0011005401611f}, {502.788818359375f,57.7058334350586f,36.0009002685547f}, {499.992004394531f,57.7058334350586f,33.0009002685547f}, {509.851989746094f,53.2258338928223f,33.0009002685547f}, {509.361999511719f,54.7190322875977f,36.0009002685547f}, {508.871795654297f,56.2124328613281f,27.0011005401611f}, {496.695404052734f,56.2124328613281f,33.0009002685547f}, {495.695404052734f,53.2258338928223f,27.0011005401611f}, {506.32861328125f,81.599235534668f,30.0011005401611f}, {507.618804931641f,77.8658294677734f,25.5011005401611f}, {515.358764648438f,55.4658317565918f,34.5009002685547f}, {501.228607177734f,81.599235534668f,33.0009002685547f}, {504.628601074219f,81.599235534668f,27.0011005401611f}, {503.781982421875f,71.6058349609375f,33.0009002685547f}, {502.808807373047f,74.5324325561523f,30.0011005401611f}, {498.915405273438f,62.8258323669434f,30.0011005401611f}, {500.861999511719f,62.8258323669434f,27.0011005401611f}, {502.808807373047f,62.8258323669434f,36.0009002685547f}, {504.755187988281f,62.8258323669434f,33.0009002685547f}, {501.835388183594f,71.6058349609375f,33.0009002685547f}, {499.888793945312f,65.7524337768555f,33.0009002685547f}, {499.888793945312f,65.7524337768555f,36.0009002685547f}, {513.128601074219f,51.4824333190918f,36.0009002685547f}, {513.075988769531f,51.6074333190918f,24.0011005401611f}, {516.648803710938f,51.7324333190918f,24.0011005401611f}, {513.128601074219f,51.4824333190918f,24.0011005401611f}, {513.495178222656f,51.7324333190918f,24.0011005401611f}, {506.966613769531f,51.6074333190918f,36.0009002685547f}, {507.163818359375f,51.6074333190918f,36.0009002685547f}, {490.337799072266f,51.4824333190918f,24.0011005401611f}, {489.391204833984f,51.4824333190918f,36.0009002685547f}, {492.06201171875f,51.7324333190918f,36.0009002685547f}, {490.337799072266f,51.4824333190918f,36.0009002685547f}, {513.233764648438f,51.2324333190918f,24.0011005401611f}, {513.233764648438f,51.2324333190918f,36.0009002685547f}, {504.773803710938f,51.4824333190918f,36.0009002685547f}, {504.773803710938f,51.4824333190918f,24.0011005401611f}, {489.266998291016f,51.2324333190918f,24.0011005401611f}, {489.266998291016f,51.2324333190918f,36.0009002685547f}, {490.253601074219f,55.4658317565918f,25.5011005401611f}, {499.528594970703f,81.599235534668f,30.0011005401611f}, {498.20361328125f,77.8658294677734f,31.5009002685547f}, {515.358764648438f,55.4658317565918f,28.5011005401611f}, {515.358764648438f,55.4658317565918f,25.5011005401611f}, {495.246795654297f,61.0124320983887f,36.0009002685547f}, {490.253601074219f,55.4658317565918f,34.5009002685547f}, {490.253601074219f,55.4658317565918f,36.0009002685547f}, {494.228607177734f,66.6658325195312f,24.0011005401611f}, {499.068786621094f,67.5192337036133f,24.0011005401611f}, {498.20361328125f,77.8658294677734f,25.5011005401611f}, {498.20361328125f,77.8658294677734f,24.0011005401611f}, {506.608795166016f,67.5192337036133f,36.0009002685547f}, {509.09521484375f,64.7458343505859f,36.0009002685547f}, {507.618804931641f,77.8658294677734f,34.5009002685547f}, {507.618804931641f,77.8658294677734f,36.0009002685547f}, {510.385406494141f,61.0124320983887f,24.0011005401611f}, {515.358764648438f,55.4658317565918f,24.0011005401611f}, {489.32861328125f,47.7324333190918f,31.5009002685547f}, {492.95361328125f,47.7324333190918f,33.5634994506836f}, {489.32861328125f,47.7324333190918f,34.5009002685547f}, {489.32861328125f,47.7324333190918f,28.5011005401611f}, {489.32861328125f,47.7324333190918f,25.5011005401611f}, {492.95361328125f,47.7324333190918f,26.4385013580322f}, {492.95361328125f,47.7324333190918f,30.5635013580322f}, {492.95361328125f,47.7324333190918f,32.0634994506836f}, {492.95361328125f,47.7324333190918f,31.3135013580322f}, {492.95361328125f,47.7324333190918f,35.4384994506836f}, {489.32861328125f,47.7324333190918f,36.0009002685547f}, {492.95361328125f,47.7324333190918f,34.3134994506836f}, {492.95361328125f,47.7324333190918f,34.6884994506836f}, {492.95361328125f,47.7324333190918f,27.9385013580322f}, {492.95361328125f,47.7324333190918f,28.6885013580322f}, {492.95361328125f,47.7324333190918f,29.0635013580322f}, {489.32861328125f,47.7324333190918f,24.0011005401611f}, {492.95361328125f,47.7324333190918f,24.5635013580322f}, {492.95361328125f,47.7324333190918f,25.6885013580322f}, {492.95361328125f,47.7324333190918f,25.3135013580322f}, {492.95361328125f,47.7324333190918f,24.1885013580322f}, {492.95361328125f,47.7324333190918f,24.0011005401611f}, {513.443786621094f,50.7324333190918f,24.0011005401611f}, {492.95361328125f,47.7324333190918f,35.8134994506836f}, {492.95361328125f,47.7324333190918f,36.0009002685547f}, {513.443786621094f,50.7324333190918f,36.0009002685547f}, {506.350402832031f,51.4824333190918f,36.0009002685547f}, {506.350402832031f,51.4824333190918f,24.0011005401611f}, {492.743804931641f,48.2324333190918f,24.0011005401611f}, {492.638793945312f,48.4824333190918f,24.0011005401611f}, {492.743804931641f,48.2324333190918f,36.0009002685547f}, {492.638793945312f,48.4824333190918f,36.0009002685547f}, {490.089599609375f,50.9824333190918f,36.0009002685547f}, {490.089599609375f,50.9824333190918f,24.0011005401611f}, {510.342010498047f,51.7324333190918f,30.0011005401611f}, {499.068786621094f,67.5192337036133f,36.0009002685547f}, {494.228607177734f,66.6658325195312f,36.0009002685547f}, {499.375183105469f,76.9058380126953f,24.0011005401611f}, {506.421813964844f,76.9058380126953f,36.0009002685547f}, {506.608795166016f,67.5192337036133f,24.0011005401611f}, {505.728607177734f,65.7524337768555f,24.0011005401611f}, {509.09521484375f,64.7458343505859f,24.0011005401611f}, {506.701995849609f,62.8258323669434f,30.0011005401611f}, {505.728607177734f,65.7524337768555f,27.0011005401611f}, {501.835388183594f,71.6058349609375f,27.0011005401611f}, {499.888793945312f,65.7524337768555f,27.0011005401611f}, {494.228607177734f,66.6658325195312f,30.0011005401611f}, {495.553588867188f,70.3992309570312f,28.5011005401611f}, {492.903594970703f,62.9324340820312f,28.5011005401611f}, {495.553588867188f,70.3992309570312f,31.5009002685547f}, {492.903594970703f,62.9324340820312f,31.5009002685547f}, {511.488800048828f,66.6658325195312f,24.0011005401611f}, {511.488800048828f,66.6658325195312f,30.0011005401611f}, {512.778564453125f,62.9324340820312f,28.5011005401611f}, {515.358764648438f,55.4658317565918f,31.5009002685547f}, {507.618804931641f,77.8658294677734f,31.5009002685547f}, {510.198791503906f,70.3992309570312f,28.5011005401611f}, {511.488800048828f,66.6658325195312f,36.0009002685547f}, {512.778564453125f,62.9324340820312f,31.5009002685547f}, {510.198791503906f,70.3992309570312f,31.5009002685547f}, {502.788818359375f,57.7058334350586f,24.0011005401611f}, {497.195404052734f,57.7058334350586f,30.0011005401611f}, {492.903594970703f,62.9324340820312f,34.5009002685547f}, {492.903594970703f,62.9324340820312f,36.0009002685547f}, {495.553588867188f,70.3992309570312f,24.0011005401611f}, {496.725189208984f,69.4392318725586f,24.0011005401611f}, {495.553588867188f,70.3992309570312f,25.5011005401611f}, {495.246795654297f,61.0124320983887f,24.0011005401611f}, {492.903594970703f,62.9324340820312f,25.5011005401611f}, {492.903594970703f,62.9324340820312f,24.0011005401611f}, {495.553588867188f,70.3992309570312f,36.0009002685547f}, {496.725189208984f,69.4392318725586f,36.0009002685547f}, {495.553588867188f,70.3992309570312f,34.5009002685547f}, {510.198791503906f,70.3992309570312f,36.0009002685547f}, {509.002014160156f,69.4392318725586f,36.0009002685547f}, {510.198791503906f,70.3992309570312f,34.5009002685547f}, {512.778564453125f,62.9324340820312f,25.5011005401611f}, {512.778564453125f,62.9324340820312f,24.0011005401611f}, {510.198791503906f,70.3992309570312f,24.0011005401611f}, {509.002014160156f,69.4392318725586f,24.0011005401611f}, {510.198791503906f,70.3992309570312f,25.5011005401611f}, {510.385406494141f,61.0124320983887f,36.0009002685547f}, {512.778564453125f,62.9324340820312f,34.5009002685547f}, {512.778564453125f,62.9324340820312f,36.0009002685547f}, {496.840393066406f,51.2324333190918f,36.0009002685547f}, {498.981994628906f,51.7324333190918f,36.0009002685547f}, {498.981994628906f,51.7324333190918f,33.0009002685547f}, {506.555206298828f,51.7324333190918f,24.0011005401611f}, {506.555206298828f,51.7324333190918f,27.0011005401611f}, {503.82861328125f,47.7324333190918f,30.7509002685547f}, {507.45361328125f,47.7324333190918f,32.8134994506836f}, {503.82861328125f,47.7324333190918f,33.7509002685547f}, {503.82861328125f,47.7324333190918f,29.2511005401611f}, {503.82861328125f,47.7324333190918f,26.2511005401611f}, {507.45361328125f,47.7324333190918f,27.1885013580322f}, {493.921813964844f,57.2792320251465f,36.0009002685547f}, {491.425201416016f,54.5058326721191f,36.0009002685547f}, {497.195404052734f,57.7058334350586f,24.0011005401611f}, {496.418792724609f,60.052433013916f,24.0011005401611f}, {509.188812255859f,60.052433013916f,36.0009002685547f}, {511.675415039062f,57.2792320251465f,24.0011005401611f}, {514.161987304688f,54.5058326721191f,24.0011005401611f}, {507.45361328125f,47.7324333190918f,34.3134994506836f}, {503.82861328125f,47.7324333190918f,35.2509002685547f}, {507.45361328125f,47.7324333190918f,25.6885013580322f}, {503.82861328125f,47.7324333190918f,24.7511005401611f}, {500.20361328125f,47.7324333190918f,31.6885013580322f}, {500.20361328125f,47.7324333190918f,28.3135013580322f}, {500.20361328125f,47.7324333190918f,30.1885013580322f}, {507.45361328125f,47.7324333190918f,29.8135013580322f}, {507.45361328125f,47.7324333190918f,31.3135013580322f}, {507.45361328125f,47.7324333190918f,30.5635013580322f}, {503.82861328125f,47.7324333190918f,36.0009002685547f}, {507.45361328125f,47.7324333190918f,35.4384994506836f}, {507.45361328125f,47.7324333190918f,35.0634994506836f}, {507.45361328125f,47.7324333190918f,28.6885013580322f}, {507.45361328125f,47.7324333190918f,29.4385013580322f}, {503.82861328125f,47.7324333190918f,24.0011005401611f}, {507.45361328125f,47.7324333190918f,24.5635013580322f}, {507.45361328125f,47.7324333190918f,24.9385013580322f}, {500.20361328125f,47.7324333190918f,34.6884994506836f}, {500.20361328125f,47.7324333190918f,33.1884994506836f}, {500.20361328125f,47.7324333190918f,33.9384994506836f}, {500.20361328125f,47.7324333190918f,25.3135013580322f}, {500.20361328125f,47.7324333190918f,26.8135013580322f}, {500.20361328125f,47.7324333190918f,26.0635013580322f}, {500.20361328125f,47.7324333190918f,30.9385013580322f}, {500.20361328125f,47.7324333190918f,35.0634994506836f}, {500.20361328125f,47.7324333190918f,35.4384994506836f}, {500.20361328125f,47.7324333190918f,29.0635013580322f}, {500.20361328125f,47.7324333190918f,29.4385013580322f}, {500.20361328125f,47.7324333190918f,24.9385013580322f}, {500.20361328125f,47.7324333190918f,24.5635013580322f}, {507.45361328125f,47.7324333190918f,24.1885013580322f}, {507.45361328125f,47.7324333190918f,24.0011005401611f}, {513.86376953125f,49.7324333190918f,24.0011005401611f}, {507.45361328125f,47.7324333190918f,35.8134994506836f}, {507.45361328125f,47.7324333190918f,36.0009002685547f}, {513.86376953125f,49.7324333190918f,36.0009002685547f}, {500.20361328125f,47.7324333190918f,24.1885013580322f}, {500.20361328125f,47.7324333190918f,24.0011005401611f}, {502.988800048828f,49.7324333190918f,24.0011005401611f}, {500.20361328125f,47.7324333190918f,35.8134994506836f}, {500.20361328125f,47.7324333190918f,36.0009002685547f}, {502.988800048828f,49.7324333190918f,36.0009002685547f}, {504.755187988281f,62.8258323669434f,27.0011005401611f}, {499.205383300781f,51.2324333190918f,36.0009002685547f}, {498.786193847656f,51.1074333190918f,36.0009002685547f}, {502.358795166016f,51.2324333190918f,36.0009002685547f}, {499.205383300781f,51.2324333190918f,24.0011005401611f}, {502.358795166016f,51.2324333190918f,24.0011005401611f}, {498.786193847656f,51.1074333190918f,24.0011005401611f}, {502.568786621094f,50.7324333190918f,24.0011005401611f}, {505.931213378906f,51.3574333190918f,24.0011005401611f}, {509.503601074219f,51.4824333190918f,24.0011005401611f}, {502.568786621094f,50.7324333190918f,36.0009002685547f}, {505.931213378906f,51.3574333190918f,36.0009002685547f}, {509.503601074219f,51.4824333190918f,36.0009002685547f}, {499.048583984375f,50.4824333190918f,36.0009002685547f}, {492.428588867188f,48.9824333190918f,36.0009002685547f}, {499.048583984375f,50.4824333190918f,24.0011005401611f}, {492.428588867188f,48.9824333190918f,24.0011005401611f}, {506.088806152344f,50.9824333190918f,24.0011005401611f}, {506.036010742188f,51.1074333190918f,24.0011005401611f}, {506.088806152344f,50.9824333190918f,36.0009002685547f}, {506.036010742188f,51.1074333190918f,36.0009002685547f}, {498.891204833984f,50.8574333190918f,36.0009002685547f}, {498.943786621094f,50.7324333190918f,36.0009002685547f}, {498.891204833984f,50.8574333190918f,24.0011005401611f}, {498.943786621094f,50.7324333190918f,24.0011005401611f}, {499.573608398438f,49.2324333190918f,24.0011005401611f}, {499.783813476562f,48.7324333190918f,24.0011005401611f}, {499.573608398438f,49.2324333190918f,36.0009002685547f}, {499.783813476562f,48.7324333190918f,36.0009002685547f}, {506.403594970703f,50.2324333190918f,24.0011005401611f}, {506.298797607422f,50.4824333190918f,24.0011005401611f}, {506.403594970703f,50.2324333190918f,36.0009002685547f}, {506.298797607422f,50.4824333190918f,36.0009002685547f}, {501.228607177734f,81.599235534668f,27.0011005401611f}, {502.928588867188f,81.599235534668f,24.0011005401611f}, {499.2587890625f,49.9824333190918f,36.0009002685547f}, {499.363800048828f,49.7324333190918f,36.0009002685547f}, {499.2587890625f,49.9824333190918f,24.0011005401611f}, {499.363800048828f,49.7324333190918f,24.0011005401611f}, {496.695404052734f,56.2124328613281f,27.0011005401611f}, {496.195404052734f,54.7190322875977f,24.0011005401611f}, {509.851989746094f,53.2258338928223f,27.0011005401611f}, {493.464782714844f,51.1074333190918f,36.0009002685547f}, {493.464782714844f,51.1074333190918f,24.0011005401611f}, {502.768798828125f,51.7324333190918f,24.0011005401611f}, {500.215789794922f,51.3574333190918f,24.0011005401611f}, {497.628601074219f,51.2324333190918f,24.0011005401611f}, {502.768798828125f,51.7324333190918f,36.0009002685547f}, {500.215789794922f,51.3574333190918f,36.0009002685547f}, {497.628601074219f,51.2324333190918f,36.0009002685547f}, {507.033813476562f,48.7324333190918f,24.0011005401611f}, {506.823791503906f,49.2324333190918f,24.0011005401611f}, {507.033813476562f,48.7324333190918f,36.0009002685547f}, {506.823791503906f,49.2324333190918f,36.0009002685547f}, {494.4501953125f,51.1074333190918f,24.0011005401611f}, {494.4501953125f,51.1074333190918f,36.0009002685547f}, {500.807006835938f,51.3574333190918f,36.0009002685547f}, {503.591186523438f,51.4824333190918f,36.0009002685547f}, {503.591186523438f,51.4824333190918f,24.0011005401611f}, {500.807006835938f,51.3574333190918f,24.0011005401611f}, {505.728607177734f,65.7524337768555f,36.0009002685547f}, {505.728607177734f,65.7524337768555f,33.0009002685547f}, {499.221801757812f,72.2124328613281f,36.0009002685547f}, {501.835388183594f,71.6058349609375f,36.0009002685547f}, {506.515197753906f,72.2124328613281f,24.0011005401611f}, {503.781982421875f,71.6058349609375f,24.0011005401611f}, {503.781982421875f,71.6058349609375f,27.0011005401611f}, {499.888793945312f,65.7524337768555f,24.0011005401611f}, {495.695404052734f,53.2258338928223f,33.0009002685547f}, {516.648803710938f,51.7324333190918f,30.0011005401611f}, {498.20361328125f,77.8658294677734f,28.5011005401611f}, {505.585388183594f,57.7058334350586f,33.0009002685547f}, {508.871795654297f,56.2124328613281f,33.0009002685547f}, {499.992004394531f,57.7058334350586f,27.0011005401611f}, {504.628601074219f,81.599235534668f,33.0009002685547f}, {500.861999511719f,62.8258323669434f,33.0009002685547f}, {496.878601074219f,74.1324310302734f,27.0011005401611f}, {496.878601074219f,74.1324310302734f,33.0009002685547f}, {491.57861328125f,59.199031829834f,27.0011005401611f}, {490.253601074219f,55.4658317565918f,28.5011005401611f}, {491.57861328125f,59.199031829834f,33.0009002685547f}, {514.068786621094f,59.199031829834f,27.0011005401611f}, {514.068786621094f,59.199031829834f,33.0009002685547f}, {508.908813476562f,74.1324310302734f,27.0011005401611f}, {507.618804931641f,77.8658294677734f,28.5011005401611f}, {508.908813476562f,74.1324310302734f,33.0009002685547f}, {491.271789550781f,50.9824333190918f,36.0009002685547f}, {490.877807617188f,50.9824333190918f,36.0009002685547f}, {491.271789550781f,50.9824333190918f,24.0011005401611f}, {490.877807617188f,50.9824333190918f,24.0011005401611f}, {495.213806152344f,50.9824333190918f,36.0009002685547f}, {493.636993408203f,50.9824333190918f,36.0009002685547f}, {495.213806152344f,50.9824333190918f,24.0011005401611f}, {493.636993408203f,50.9824333190918f,24.0011005401611f}, {503.985412597656f,51.4824333190918f,36.0009002685547f}, {503.985412597656f,51.4824333190918f,24.0011005401611f}, {511.675415039062f,57.2792320251465f,36.0009002685547f}, {493.921813964844f,57.2792320251465f,24.0011005401611f}, {502.768798828125f,51.7324333190918f,30.0011005401611f}, {506.555206298828f,51.7324333190918f,30.0011005401611f}, {498.981994628906f,51.7324333190918f,30.0011005401611f}, {492.848815917969f,50.9824333190918f,24.0011005401611f}, {492.848815917969f,50.9824333190918f,36.0009002685547f}, {500.861999511719f,68.6792297363281f,36.0009002685547f}, {500.861999511719f,68.6792297363281f,24.0011005401611f}, {496.878601074219f,74.1324310302734f,24.0011005401611f}, {496.878601074219f,74.1324310302734f,36.0009002685547f}, {504.755187988281f,68.6792297363281f,24.0011005401611f}, {504.755187988281f,68.6792297363281f,36.0009002685547f}, {508.908813476562f,74.1324310302734f,36.0009002685547f}, {508.908813476562f,74.1324310302734f,24.0011005401611f}, {505.728607177734f,65.7524337768555f,30.0011005401611f}, {504.755187988281f,68.6792297363281f,30.0011005401611f}, {503.781982421875f,71.6058349609375f,30.0011005401611f}, {500.861999511719f,68.6792297363281f,30.0011005401611f}, {499.888793945312f,65.7524337768555f,30.0011005401611f}, {501.835388183594f,71.6058349609375f,30.0011005401611f}, {491.57861328125f,59.199031829834f,24.0011005401611f}, {491.57861328125f,59.199031829834f,36.0009002685547f}, {514.068786621094f,59.199031829834f,36.0009002685547f}, {514.068786621094f,59.199031829834f,24.0011005401611f}, {511.07861328125f,47.7324333190918f,34.8759002685547f}, {511.07861328125f,47.7324333190918f,31.8759002685547f}, {514.70361328125f,47.7324333190918f,33.9384994506836f}, {511.07861328125f,47.7324333190918f,25.1261005401611f}, {514.70361328125f,47.7324333190918f,26.0635013580322f}, {511.07861328125f,47.7324333190918f,28.1261005401611f}, {502.788818359375f,57.7058334350586f,30.0011005401611f}, {502.048583984375f,79.8324356079102f,36.0009002685547f}, {514.70361328125f,47.7324333190918f,30.9385013580322f}, {511.07861328125f,47.7324333190918f,30.3759002685547f}, {514.70361328125f,47.7324333190918f,29.0635013580322f}, {511.07861328125f,47.7324333190918f,29.6261005401611f}, {496.57861328125f,47.7324333190918f,31.1259002685547f}, {496.57861328125f,47.7324333190918f,32.6259002685547f}, {496.57861328125f,47.7324333190918f,34.1259002685547f}, {496.57861328125f,47.7324333190918f,28.8761005401611f}, {496.57861328125f,47.7324333190918f,27.3761005401611f}, {496.57861328125f,47.7324333190918f,25.8761005401611f}, {496.57861328125f,47.7324333190918f,29.6261005401611f}, {514.70361328125f,47.7324333190918f,35.4384994506836f}, {511.07861328125f,47.7324333190918f,35.6259002685547f}, {514.70361328125f,47.7324333190918f,24.5635013580322f}, {511.07861328125f,47.7324333190918f,24.3761005401611f}, {496.57861328125f,47.7324333190918f,34.8759002685547f}, {496.57861328125f,47.7324333190918f,25.1261005401611f}, {496.57861328125f,47.7324333190918f,35.6259002685547f}, {496.57861328125f,47.7324333190918f,24.3761005401611f}, {511.07861328125f,47.7324333190918f,36.0009002685547f}, {511.07861328125f,47.7324333190918f,24.0011005401611f}, {514.70361328125f,47.7324333190918f,30.1885013580322f}, {514.70361328125f,47.7324333190918f,35.8134994506836f}, {514.70361328125f,47.7324333190918f,29.8135013580322f}, {514.70361328125f,47.7324333190918f,24.1885013580322f}, {496.57861328125f,47.7324333190918f,36.0009002685547f}, {496.57861328125f,47.7324333190918f,24.0011005401611f}, {510.238800048828f,49.7324333190918f,24.0011005401611f}, {510.238800048828f,49.7324333190918f,36.0009002685547f}, {514.70361328125f,47.7324333190918f,24.0011005401611f}, {514.70361328125f,47.7324333190918f,36.0009002685547f}, {496.158813476562f,48.7324333190918f,36.0009002685547f}, {496.158813476562f,48.7324333190918f,24.0011005401611f}, {502.808807373047f,62.8258323669434f,30.0011005401611f}, {509.608795166016f,51.2324333190918f,24.0011005401611f}, {509.608795166016f,51.2324333190918f,36.0009002685547f}, {491.641204833984f,50.8574333190918f,24.0011005401611f}, {495.423797607422f,50.4824333190918f,36.0009002685547f}, {495.423797607422f,50.4824333190918f,24.0011005401611f}, {491.641204833984f,50.8574333190918f,36.0009002685547f}, {495.528594970703f,50.2324333190918f,24.0011005401611f}, {492.0087890625f,49.9824333190918f,24.0011005401611f}, {509.818786621094f,50.7324333190918f,24.0011005401611f}, {495.948608398438f,49.2324333190918f,36.0009002685547f}, {495.528594970703f,50.2324333190918f,36.0009002685547f}, {495.948608398438f,49.2324333190918f,24.0011005401611f}, {509.818786621094f,50.7324333190918f,36.0009002685547f}, {492.0087890625f,49.9824333190918f,36.0009002685547f}, {491.956207275391f,50.1074333190918f,24.0011005401611f}, {491.956207275391f,50.1074333190918f,36.0009002685547f}, {502.928588867188f,81.599235534668f,30.0011005401611f}, {491.851013183594f,50.3574333190918f,36.0009002685547f}, {491.851013183594f,50.3574333190918f,24.0011005401611f}, {496.195404052734f,54.7190322875977f,30.0011005401611f}, {509.361999511719f,54.7190322875977f,30.0011005401611f}, {488.632598876953f,51.7256317138672f,30.0011005401611f}, {488.632598876953f,51.7256317138672f,29.5091018676758f}, {488.632598876953f,51.7188339233398f,24.0011005401611f}, {488.632598876953f,51.7256317138672f,27.4929008483887f}, {488.632598876953f,51.7324333190918f,30.0011005401611f}, {488.632598876953f,51.7324333190918f,29.0175018310547f}, {488.632598876953f,51.7324333190918f,24.9847011566162f}, {488.632598876953f,51.7324333190918f,24.0011005401611f}, {488.632598876953f,51.7188339233398f,30.0011005401611f}, {488.632598876953f,51.7176322937012f,24.0011005401611f}, {488.632598876953f,51.7182312011719f,30.0011005401611f}, {488.632598876953f,51.7176322937012f,30.0011005401611f}, {488.632598876953f,51.715030670166f,24.0011005401611f}, {488.632598876953f,51.7162322998047f,30.0011005401611f}, {488.632598876953f,50.761833190918f,24.0011005401611f}, {488.632598876953f,50.7578315734863f,24.0011005401611f}, {488.632598876953f,50.7598342895508f,30.0011005401611f}, {488.632598876953f,50.7522315979004f,24.0011005401611f}, {488.632598876953f,49.7838325500488f,24.0011005401611f}, {488.632598876953f,50.2680320739746f,30.0011005401611f}, {488.632598876953f,51.7046318054199f,24.0011005401611f}, {488.632598876953f,51.709831237793f,30.0011005401611f}, {488.632598876953f,50.9120330810547f,24.0011005401611f}, {488.632598876953f,50.8882331848145f,24.0011005401611f}, {488.632598876953f,50.9002304077148f,30.0011005401611f}, {488.632598876953f,47.7324333190918f,24.0370998382568f}, {488.632598876953f,48.5612335205078f,30.0011005401611f}, {488.632598876953f,47.7324333190918f,24.0011005401611f}, {488.632598876953f,47.7324333190918f,24.1091003417969f}, {488.632598876953f,48.5612335205078f,30.0189018249512f}, {488.632598876953f,47.7324333190918f,25.3211002349854f}, {488.632598876953f,48.5612335205078f,30.0551013946533f}, {488.632598876953f,47.7324333190918f,25.4651012420654f}, {488.632598876953f,48.5612335205078f,30.6609001159668f}, {488.632598876953f,47.7324333190918f,25.5371017456055f}, {488.632598876953f,48.5612335205078f,30.7329006195068f}, {488.632598876953f,47.7324333190918f,25.6091003417969f}, {488.632598876953f,48.5612335205078f,30.7689018249512f}, {488.632598876953f,47.7324333190918f,25.8971004486084f}, {488.632598876953f,48.5612335205078f,30.8051013946533f}, {488.632598876953f,47.7324333190918f,28.321102142334f}, {488.632598876953f,48.5612335205078f,30.9491004943848f}, {488.632598876953f,47.7324333190918f,28.4651012420654f}, {488.632598876953f,48.5612335205078f,32.1609001159668f}, {488.632598876953f,47.7324333190918f,28.5371017456055f}, {488.632598876953f,48.5612335205078f,32.2329025268555f}, {488.632598876953f,47.7324333190918f,28.6811008453369f}, {488.632598876953f,48.5612335205078f,32.2689018249512f}, {488.632598876953f,47.7324333190918f,31.1049003601074f}, {488.632598876953f,48.5612335205078f,32.3411026000977f}, {488.632598876953f,47.7324333190918f,31.3929004669189f}, {488.632598876953f,49.3900299072266f,36.0009002685547f}, {488.632598876953f,47.7324333190918f,31.536901473999f}, {488.632598876953f,47.7324333190918f,31.6809005737305f}, {488.632598876953f,47.7324333190918f,34.1049003601074f}, {488.632598876953f,47.7324333190918f,34.3929023742676f}, {488.632598876953f,47.7324333190918f,34.464900970459f}, {488.632598876953f,47.7324333190918f,34.5369033813477f}, {488.632598876953f,47.7324333190918f,34.6809005737305f}, {488.632598876953f,47.7324333190918f,35.8929023742676f}, {488.632598876953f,47.7324333190918f,35.964900970459f}, {488.632598876953f,47.7324333190918f,36.0009002685547f}, {488.632598876953f,50.8816299438477f,24.0011005401611f}, {488.632598876953f,50.8850326538086f,30.0011005401611f}, {488.632598876953f,49.7480316162109f,24.0011005401611f}, {488.632598876953f,49.7426300048828f,24.0011005401611f}, {488.632598876953f,49.745231628418f,30.0011005401611f}, {488.632598876953f,49.7592315673828f,24.0011005401611f}, {488.632598876953f,49.7536315917969f,30.0011005401611f}, {488.632598876953f,49.3900299072266f,24.0011005401611f}, {488.632598876953f,49.5664329528809f,30.0011005401611f}, {488.632598876953f,50.8786315917969f,24.0011005401611f}, {488.632598876953f,50.7764320373535f,24.0011005401611f}, {488.632598876953f,50.8274307250977f,30.0011005401611f}, {488.632598876953f,50.7550315856934f,30.0011005401611f}, {488.632598876953f,50.7692337036133f,30.0011005401611f}, {488.632598876953f,50.9284324645996f,24.0011005401611f}, {488.632598876953f,50.9202308654785f,30.0011005401611f}, {488.632598876953f,51.1788330078125f,24.0011005401611f}, {488.632598876953f,51.139232635498f,24.0011005401611f}, {488.632598876953f,51.1590309143066f,30.0011005401611f}, {488.632598876953f,51.2324333190918f,24.0011005401611f}, {488.632598876953f,51.2056312561035f,30.0011005401611f}, {488.632598876953f,51.4340324401855f,24.0011005401611f}, {488.632598876953f,51.3946304321289f,24.0011005401611f}, {488.632598876953f,51.4142303466797f,30.0011005401611f}, {488.632598876953f,51.4498329162598f,24.0011005401611f}, {488.632598876953f,51.5772323608398f,30.0011005401611f}, {488.632598876953f,51.4418334960938f,30.0011005401611f}, {488.632598876953f,51.3136329650879f,30.0011005401611f}, {488.632598876953f,49.7714309692383f,30.0011005401611f}, {488.632598876953f,51.0338325500488f,30.0011005401611f}, {488.632598876953f,50.8816299438477f,30.0011005401611f}, {488.632598876953f,50.8800315856934f,30.0011005401611f}, {488.632598876953f,51.7188339233398f,36.0009002685547f}, {488.632598876953f,51.7176322937012f,36.0009002685547f}, {488.632598876953f,49.3900299072266f,30.0011005401611f}, {488.632598876953f,50.7522315979004f,30.0011005401611f}, {488.632598876953f,50.7522315979004f,36.0009002685547f}, {488.632598876953f,49.7426300048828f,30.0011005401611f}, {488.632598876953f,49.7426300048828f,36.0009002685547f}, {488.632598876953f,49.7480316162109f,30.0011005401611f}, {488.632598876953f,49.7480316162109f,36.0009002685547f}, {488.632598876953f,51.715030670166f,30.0011005401611f}, {488.632598876953f,51.715030670166f,36.0009002685547f}, {488.632598876953f,50.7578315734863f,30.0011005401611f}, {488.632598876953f,50.7578315734863f,36.0009002685547f}, {488.632598876953f,50.761833190918f,30.0011005401611f}, {488.632598876953f,50.761833190918f,36.0009002685547f}, {488.632598876953f,50.8882331848145f,30.0011005401611f}, {488.632598876953f,50.8882331848145f,36.0009002685547f}, {488.632598876953f,49.7592315673828f,30.0011005401611f}, {488.632598876953f,49.7592315673828f,36.0009002685547f}, {488.632598876953f,51.1788330078125f,30.0011005401611f}, {488.632598876953f,51.1788330078125f,36.0009002685547f}, {488.632598876953f,50.9120330810547f,30.0011005401611f}, {488.632598876953f,50.9120330810547f,36.0009002685547f}, {488.632598876953f,51.4498329162598f,30.0011005401611f}, {488.632598876953f,51.4498329162598f,36.0009002685547f}, {488.632598876953f,51.7046318054199f,30.0011005401611f}, {488.632598876953f,51.7046318054199f,36.0009002685547f}, {488.632598876953f,51.2324333190918f,30.0011005401611f}, {488.632598876953f,51.2324333190918f,36.0009002685547f}, {488.632598876953f,51.3946304321289f,30.0011005401611f}, {488.632598876953f,51.3946304321289f,36.0009002685547f}, {488.632598876953f,51.4340324401855f,30.0011005401611f}, {488.632598876953f,51.4340324401855f,36.0009002685547f}, {488.632598876953f,49.7838325500488f,30.0011005401611f}, {488.632598876953f,49.7838325500488f,36.0009002685547f}, {488.632598876953f,50.7764320373535f,30.0011005401611f}, {488.632598876953f,50.7764320373535f,36.0009002685547f}, {488.632598876953f,51.139232635498f,30.0011005401611f}, {488.632598876953f,51.139232635498f,36.0009002685547f}, {488.632598876953f,50.9284324645996f,30.0011005401611f}, {488.632598876953f,50.9284324645996f,36.0009002685547f}, {488.632598876953f,50.8816299438477f,36.0009002685547f}, {488.632598876953f,50.8786315917969f,30.0011005401611f}, {488.632598876953f,50.8786315917969f,36.0009002685547f}, {488.632598876953f,51.7324333190918f,35.0173034667969f}, {488.632598876953f,51.7324333190918f,36.0009002685547f}, {488.632598876953f,51.7324333190918f,30.9847011566162f}, {517.188415527344f,51.7140884399414f,24.0011005401611f}, {517.188415527344f,51.7140884399414f,36.0009002685547f}, {517.188415527344f,50.4475173950195f,24.0011005401611f}, {517.188415527344f,51.7324333190918f,35.3734130859375f}, {517.188415527344f,51.7324333190918f,36.0009002685547f}, {517.188415527344f,51.7324333190918f,34.1185760498047f}, {517.188415527344f,51.7324333190918f,31.88330078125f}, {517.188415527344f,51.7324333190918f,30.0011005401611f}, {517.188415527344f,51.7324333190918f,28.1187744140625f}, {517.188415527344f,51.7324333190918f,25.8834266662598f}, {517.188415527344f,51.7324333190918f,24.6285915374756f}, {517.188415527344f,51.7324333190918f,24.0011005401611f}, {517.188415527344f,47.7324333190918f,24.0600452423096f}, {517.188415527344f,47.7324333190918f,24.0011005401611f}, {517.188415527344f,50.4475173950195f,36.0009002685547f}, {517.188415527344f,47.7324333190918f,24.1779975891113f}, {517.188415527344f,47.7324333190918f,24.6498031616211f}, {517.188415527344f,47.7324333190918f,28.7625770568848f}, {517.188415527344f,47.7324333190918f,29.7061901092529f}, {517.188415527344f,47.7324333190918f,29.9420928955078f}, {517.188415527344f,47.7324333190918f,30.0600452423096f}, {517.188415527344f,47.7324333190918f,30.2959480285645f}, {517.188415527344f,47.7324333190918f,31.2395629882812f}, {517.188415527344f,47.7324333190918f,35.3521995544434f}, {517.188415527344f,47.7324333190918f,35.8240051269531f}, {517.188415527344f,47.7324333190918f,35.9419555664062f}, {517.188415527344f,47.7324333190918f,36.0009002685547f} }, + { {0,1,2}, {3,4,5}, {6,7,8}, {9,10,11}, {12,2,1}, {12,1,13}, {14,15,16}, {17,18,19}, {20,21,22}, {17,19,23}, {24,25,26}, {27,13,1}, {28,25,29}, {30,31,32}, {28,33,34}, {35,36,7}, {37,38,39}, {40,10,41}, {42,43,44}, {45,5,4}, {46,47,48}, {46,48,49}, {45,4,50}, {51,52,53}, {51,54,55}, {56,52,57}, {58,59,60}, {61,50,4}, {62,63,64}, {65,34,33}, {66,67,42}, {68,17,69}, {70,71,22}, {66,42,72}, {73,16,15}, {35,7,74}, {75,76,54}, {77,27,1}, {78,32,31}, {75,54,79}, {80,26,25}, {81,80,25}, {82,83,48}, {84,20,85}, {81,25,86}, {87,88,19}, {0,89,1}, {90,91,92}, {90,10,93}, {38,94,39}, {94,95,39}, {3,7,96}, {97,15,98}, {97,99,15}, {92,91,100}, {89,101,1}, {102,39,95}, {103,11,10}, {104,96,7}, {105,15,99}, {106,61,4}, {107,108,33}, {76,55,54}, {109,91,110}, {111,23,19}, {112,63,113}, {114,115,48}, {116,59,117}, {118,20,119}, {120,31,121}, {122,44,43}, {110,91,123}, {124,125,126}, {127,128,129}, {127,130,124}, {131,124,132}, {126,133,134}, {135,136,126}, {137,138,127}, {139,127,138}, {128,140,141}, {142,128,143}, {144,140,145}, {100,91,146}, {147,148,134}, {101,149,1}, {102,150,39}, {103,10,151}, {145,140,152}, {152,140,153}, {148,154,134}, {154,155,134}, {156,15,105}, {157,104,7}, {36,8,7}, {158,37,39}, {159,19,88}, {160,19,159}, {161,59,58}, {161,117,59}, {162,31,30}, {162,121,31}, {163,43,164}, {163,165,43}, {166,167,43}, {167,164,43}, {168,57,52}, {82,48,169}, {114,170,171}, {108,65,33}, {64,63,112}, {114,172,170}, {160,173,170}, {171,170,173}, {172,174,170}, {160,170,174}, {175,176,177}, {178,77,1}, {179,31,120}, {175,180,176}, {181,182,176}, {177,176,182}, {180,183,176}, {181,176,183}, {184,42,67}, {185,69,17}, {160,111,19}, {186,187,160}, {188,189,114}, {190,188,114}, {114,48,191}, {192,114,193}, {194,160,195}, {196,160,194}, {197,198,181}, {199,197,181}, {122,43,165}, {200,201,175}, {202,175,203}, {204,175,202}, {205,119,20}, {206,181,207}, {208,209,15}, {210,15,209}, {211,10,9}, {212,10,211}, {213,214,215}, {216,217,218}, {219,14,17}, {113,63,220}, {221,222,48}, {191,48,222}, {22,223,20}, {205,20,223}, {224,40,42}, {123,91,225}, {214,226,215}, {227,215,226}, {218,217,228}, {229,228,217}, {215,230,213}, {125,135,126}, {217,216,231}, {129,128,142}, {216,213,232}, {130,132,124}, {213,216,233}, {234,213,235}, {236,227,237}, {238,237,227}, {239,240,216}, {233,216,240}, {241,242,229}, {243,229,242}, {215,227,244}, {245,215,246}, {217,247,229}, {248,249,217}, {232,213,250}, {230,250,213}, {133,147,134}, {244,227,251}, {236,252,227}, {251,227,252}, {231,216,253}, {254,253,216}, {141,140,144}, {247,255,229}, {241,229,256}, {255,256,229}, {257,241,258}, {259,146,91}, {260,261,236}, {262,1,149}, {263,264,241}, {265,241,264}, {266,236,267}, {268,267,236}, {49,48,83}, {166,43,269}, {270,271,272}, {273,274,275}, {276,274,277}, {278,151,10}, {279,280,272}, {281,39,150}, {272,282,279}, {155,283,134}, {274,276,284}, {153,140,285}, {286,276,287}, {265,276,286}, {288,289,279}, {268,288,279}, {290,291,272}, {271,290,272}, {292,274,293}, {275,274,292}, {294,265,295}, {276,265,294}, {296,297,268}, {279,296,268}, {241,265,298}, {298,265,299}, {236,300,268}, {300,301,268}, {107,33,78}, {302,303,59}, {304,305,279}, {282,304,279}, {306,276,307}, {284,276,306}, {185,17,73}, {308,309,221}, {158,39,70}, {310,41,10}, {15,311,208}, {7,6,312}, {313,314,6}, {315,6,314}, {316,208,317}, {318,317,208}, {258,241,319}, {319,241,320}, {261,321,236}, {321,322,236}, {6,315,323}, {208,324,318}, {270,325,318}, {326,318,325}, {327,328,315}, {273,315,328}, {118,329,20}, {330,20,329}, {331,332,25}, {86,25,332}, {333,334,52}, {335,52,334}, {115,336,48}, {169,48,336}, {62,106,4}, {35,15,210}, {35,337,15}, {158,10,212}, {158,310,10}, {338,178,1}, {339,59,116}, {107,302,59}, {66,22,340}, {66,341,22}, {185,221,342}, {185,308,221}, {75,31,179}, {75,343,31}, {166,20,330}, {166,85,20}, {81,52,335}, {81,168,52}, {82,19,344}, {82,87,19}, {108,339,345}, {346,108,345}, {64,347,348}, {349,347,64}, {178,109,350}, {351,178,350}, {179,352,353}, {354,352,179}, {355,208,356}, {356,208,311}, {357,358,6}, {358,312,6}, {68,22,21}, {68,340,22}, {221,48,47}, {184,342,221}, {359,270,360}, {318,360,270}, {361,362,273}, {315,273,362}, {272,102,270}, {363,270,102}, {274,273,103}, {364,103,273}, {21,19,18}, {21,20,84}, {184,46,42}, {43,42,46}, {12,22,71}, {365,22,12}, {14,98,15}, {14,220,63}, {40,93,10}, {40,225,91}, {45,221,309}, {366,221,45}, {313,367,212}, {212,367,368}, {36,369,367}, {313,36,367}, {316,37,367}, {37,368,367}, {210,367,369}, {316,367,210}, {362,370,315}, {370,323,315}, {360,318,371}, {371,318,324}, {372,331,159}, {159,195,160}, {373,115,56}, {115,114,189}, {52,56,161}, {374,161,56}, {25,28,331}, {375,331,28}, {376,333,163}, {163,203,175}, {377,118,24}, {118,181,198}, {25,24,162}, {378,162,24}, {52,51,333}, {379,333,51}, {167,380,381}, {376,167,381}, {377,381,330}, {330,381,380}, {335,381,382}, {376,381,335}, {373,383,169}, {169,383,384}, {168,385,383}, {373,168,383}, {372,87,383}, {87,384,383}, {377,80,381}, {80,382,381}, {86,383,385}, {372,383,86}, {106,348,347}, {386,106,347}, {375,65,346}, {108,346,65}, {64,112,349}, {387,349,112}, {171,190,114}, {346,345,171}, {374,190,345}, {171,345,190}, {349,172,347}, {172,114,192}, {386,347,192}, {172,192,347}, {173,160,196}, {171,173,346}, {375,346,196}, {173,196,346}, {172,349,174}, {174,186,160}, {387,186,349}, {174,349,186}, {64,348,62}, {106,62,348}, {108,107,339}, {59,339,107}, {374,345,116}, {339,116,345}, {76,353,352}, {379,76,352}, {388,77,351}, {178,351,77}, {179,120,354}, {378,354,120}, {177,200,175}, {351,350,177}, {389,200,350}, {177,350,200}, {354,180,352}, {180,175,204}, {379,352,204}, {180,204,352}, {182,181,206}, {177,182,351}, {388,351,206}, {182,206,351}, {180,354,183}, {183,199,181}, {378,199,354}, {183,354,199}, {91,109,338}, {178,338,109}, {76,75,353}, {179,353,75}, {389,350,110}, {109,110,350}, {390,391,392}, {393,394,395}, {224,122,389}, {122,175,201}, {365,388,205}, {205,207,181}, {66,340,396}, {68,396,340}, {184,396,342}, {185,342,396}, {66,396,67}, {184,67,396}, {68,69,396}, {185,396,69}, {219,111,387}, {111,160,187}, {366,386,191}, {191,193,114}, {150,272,280}, {102,272,150}, {151,277,274}, {103,151,274}, {161,374,117}, {116,117,374}, {366,61,386}, {106,386,61}, {111,187,387}, {186,387,187}, {56,188,374}, {190,374,188}, {191,386,193}, {192,193,386}, {331,375,194}, {196,194,375}, {28,34,375}, {65,375,34}, {219,387,113}, {112,113,387}, {224,389,123}, {110,123,389}, {51,55,379}, {76,379,55}, {24,197,378}, {199,378,197}, {122,201,389}, {200,389,201}, {333,379,202}, {204,202,379}, {205,388,207}, {206,207,388}, {365,27,388}, {77,388,27}, {162,378,121}, {120,121,378}, {162,30,25}, {30,29,25}, {51,53,54}, {303,60,59}, {28,29,33}, {29,397,33}, {161,58,52}, {53,52,58}, {21,84,19}, {84,344,19}, {46,49,43}, {49,269,43}, {208,316,209}, {210,209,316}, {327,313,211}, {212,211,313}, {36,35,369}, {210,369,35}, {37,158,368}, {212,368,158}, {6,8,313}, {36,313,8}, {326,38,316}, {37,316,38}, {392,391,398}, {399,398,391}, {394,400,395}, {401,395,400}, {390,214,391}, {214,213,234}, {393,395,218}, {218,239,216}, {402,230,403}, {230,215,245}, {125,124,131}, {404,125,403}, {405,406,231}, {231,248,217}, {129,137,127}, {407,406,129}, {130,127,139}, {402,130,408}, {194,195,331}, {159,331,195}, {115,189,56}, {188,56,189}, {14,219,220}, {113,220,219}, {45,50,366}, {61,366,50}, {221,366,222}, {191,222,366}, {17,23,219}, {111,219,23}, {118,198,24}, {197,24,198}, {202,203,333}, {163,333,203}, {40,224,225}, {123,225,224}, {12,13,365}, {27,365,13}, {22,365,223}, {205,223,365}, {42,44,224}, {122,224,44}, {399,391,234}, {214,234,391}, {401,239,395}, {218,395,239}, {214,390,226}, {226,238,227}, {218,228,393}, {228,229,243}, {401,399,233}, {233,235,213}, {392,409,390}, {410,390,409}, {394,393,411}, {412,411,393}, {402,403,131}, {125,131,403}, {405,137,406}, {129,406,137}, {405,408,139}, {130,139,408}, {230,245,403}, {404,403,245}, {231,406,248}, {407,248,406}, {232,254,216}, {402,408,232}, {413,404,244}, {244,246,215}, {414,247,407}, {247,217,249}, {133,126,136}, {415,133,413}, {141,143,128}, {416,414,141}, {410,238,390}, {226,390,238}, {412,393,243}, {228,243,393}, {233,399,235}, {234,235,399}, {237,260,236}, {238,410,237}, {417,260,410}, {237,410,260}, {239,401,240}, {233,240,401}, {242,241,257}, {243,242,412}, {418,412,257}, {242,257,412}, {401,419,399}, {398,399,419}, {417,410,420}, {409,420,410}, {400,421,401}, {419,401,421}, {418,422,412}, {411,412,422}, {413,135,404}, {125,404,135}, {414,407,142}, {129,142,407}, {130,402,132}, {131,132,402}, {133,136,413}, {135,413,136}, {423,147,415}, {133,415,147}, {137,405,138}, {139,138,405}, {141,414,143}, {142,143,414}, {424,416,144}, {141,144,416}, {405,254,408}, {232,408,254}, {244,404,246}, {245,246,404}, {247,249,407}, {248,407,249}, {232,250,402}, {230,402,250}, {415,413,251}, {244,251,413}, {252,236,266}, {251,252,415}, {423,415,266}, {252,266,415}, {231,253,405}, {254,405,253}, {416,255,414}, {247,414,255}, {256,263,241}, {255,416,256}, {424,263,416}, {256,416,263}, {257,258,418}, {425,418,258}, {260,417,261}, {426,261,417}, {422,418,427}, {427,259,91}, {420,428,417}, {428,1,262}, {147,423,148}, {429,148,423}, {263,424,264}, {264,295,265}, {266,267,423}, {267,268,297}, {144,145,424}, {430,424,145}, {49,431,269}, {166,269,431}, {82,431,83}, {49,83,431}, {84,85,431}, {166,431,85}, {82,344,431}, {84,431,344}, {432,278,90}, {10,90,278}, {433,0,281}, {39,281,0}, {362,361,434}, {435,271,359}, {270,359,271}, {436,361,275}, {273,275,361}, {360,437,359}, {277,287,276}, {151,278,277}, {280,279,289}, {150,280,281}, {436,438,439}, {439,285,140}, {90,92,432}, {440,432,92}, {282,272,291}, {441,282,442}, {284,293,274}, {443,438,284}, {278,432,286}, {286,299,265}, {281,288,433}, {288,268,301}, {0,433,89}, {444,89,433}, {435,445,442}, {445,134,283}, {439,446,436}, {361,436,446}, {442,290,435}, {271,435,290}, {438,436,292}, {275,292,436}, {445,435,447}, {359,447,435}, {286,287,278}, {277,278,287}, {288,281,289}, {280,289,281}, {145,152,430}, {443,430,152}, {148,429,154}, {441,154,429}, {424,430,294}, {294,307,276}, {423,296,429}, {296,279,305}, {425,440,100}, {92,100,440}, {290,442,291}, {282,291,442}, {292,293,438}, {284,438,293}, {298,320,241}, {432,440,298}, {300,236,322}, {433,300,444}, {426,101,444}, {89,444,101}, {107,448,302}, {302,79,54}, {78,31,343}, {107,78,448}, {75,79,448}, {302,448,79}, {78,343,448}, {75,448,343}, {427,418,259}, {425,259,418}, {428,262,417}, {426,417,262}, {437,449,359}, {447,359,449}, {434,361,450}, {446,450,361}, {32,33,397}, {78,33,32}, {53,303,54}, {302,54,303}, {152,153,443}, {438,443,153}, {429,304,441}, {282,441,304}, {430,443,306}, {284,306,443}, {154,441,155}, {442,155,441}, {298,299,432}, {286,432,299}, {300,433,301}, {288,301,433}, {185,451,308}, {308,74,7}, {73,15,337}, {185,73,451}, {35,74,451}, {308,451,74}, {73,337,451}, {35,451,337}, {158,452,310}, {310,72,42}, {70,22,341}, {158,70,452}, {66,72,452}, {310,452,72}, {70,341,452}, {66,452,341}, {313,327,314}, {315,314,327}, {316,317,326}, {318,326,317}, {15,156,311}, {356,311,156}, {7,312,157}, {358,157,312}, {211,9,327}, {364,327,9}, {38,326,94}, {363,94,326}, {294,295,424}, {264,424,295}, {296,423,297}, {267,297,423}, {262,149,426}, {101,426,149}, {258,319,425}, {440,425,319}, {261,426,321}, {444,321,426}, {259,425,146}, {100,146,425}, {306,307,430}, {294,430,307}, {304,429,305}, {296,305,429}, {319,320,440}, {298,440,320}, {321,444,322}, {300,322,444}, {445,283,442}, {155,442,283}, {439,438,285}, {153,285,438}, {17,68,18}, {21,18,68}, {46,184,47}, {221,47,184}, {102,95,363}, {94,363,95}, {9,11,364}, {103,364,11}, {6,323,357}, {370,357,323}, {371,324,355}, {208,355,324}, {270,363,325}, {326,325,363}, {327,364,328}, {273,328,364}, {0,2,39}, {12,39,2}, {90,93,91}, {40,91,93}, {14,16,17}, {73,17,16}, {45,309,7}, {308,7,309}, {12,71,39}, {70,39,71}, {40,41,42}, {310,42,41}, {97,98,63}, {14,63,98}, {3,5,7}, {45,7,5}, {118,377,329}, {330,329,377}, {331,372,332}, {86,332,372}, {333,376,334}, {335,334,376}, {115,373,336}, {169,336,373}, {167,166,380}, {330,380,166}, {80,81,382}, {335,382,81}, {86,385,81}, {168,81,385}, {169,384,82}, {87,82,384}, {159,88,372}, {87,372,88}, {163,164,376}, {167,376,164}, {24,26,377}, {80,377,26}, {56,57,373}, {168,373,57}, {32,397,30}, {29,30,397}, {58,60,53}, {303,53,60}, {205,181,119}, {118,119,181}, {163,175,165}, {122,165,175}, {453,454,455}, {454,456,455}, {457,455,456}, {458,455,457}, {459,455,458}, {460,455,459}, {461,462,463}, {464,465,466}, {467,468,469}, {470,471,472}, {465,473,474}, {475,476,477}, {478,479,480}, {481,482,478}, {483,484,481}, {485,486,483}, {487,488,485}, {489,490,487}, {491,492,489}, {493,494,491}, {495,496,493}, {497,498,495}, {499,500,497}, {501,502,499}, {503,504,501}, {505,504,503}, {506,504,505}, {507,504,506}, {508,504,507}, {509,504,508}, {510,504,509}, {511,504,510}, {512,504,511}, {513,504,512}, {514,504,513}, {476,515,516}, {517,518,519}, {520,517,521}, {518,522,523}, {522,480,479}, {524,525,526}, {468,470,527}, {525,467,528}, {529,475,530}, {531,532,533}, {534,531,535}, {536,537,538}, {473,539,540}, {539,536,541}, {537,534,542}, {471,520,543}, {532,529,544}, {545,524,546}, {453,461,547}, {463,464,548}, {523,549,504}, {527,550,551}, {519,552,553}, {521,554,555}, {466,556,557}, {469,558,559}, {528,560,561}, {477,562,563}, {543,564,565}, {535,566,567}, {530,568,569}, {540,570,571}, {474,572,573}, {542,574,575}, {538,576,577}, {541,578,579}, {472,580,581}, {526,582,583}, {533,584,585}, {544,586,587}, {516,545,588}, {588,589,590}, {455,460,4}, {591,592,63}, {462,455,4}, {592,547,63}, {547,548,63}, {465,462,4}, {548,557,63}, {127,124,501}, {127,501,499}, {505,503,124}, {124,126,507}, {124,507,506}, {509,508,126}, {126,134,512}, {126,512,511}, {510,509,126}, {128,127,493}, {128,493,491}, {497,495,127}, {489,487,128}, {140,128,483}, {140,483,481}, {487,485,128}, {478,480,140}, {480,522,140}, {514,513,134}, {504,514,134}, {551,581,437}, {471,470,434}, {445,447,555}, {445,555,553}, {134,445,553}, {134,553,504}, {446,439,518}, {446,518,517}, {439,140,522}, {439,522,518}, {515,476,358}, {563,588,356}, {557,573,63}, {473,465,4}, {437,360,559}, {437,559,551}, {360,371,561}, {360,561,559}, {362,434,470}, {362,470,468}, {370,362,468}, {370,468,467}, {499,497,127}, {506,505,124}, {495,493,127}, {513,512,134}, {481,478,140}, {447,449,565}, {447,565,555}, {450,446,517}, {450,517,520}, {356,156,569}, {356,569,563}, {157,358,476}, {157,476,475}, {357,370,467}, {357,467,525}, {371,355,583}, {371,583,561}, {460,459,4}, {63,62,593}, {63,593,591}, {62,4,459}, {62,459,458}, {532,531,104}, {531,534,104}, {567,585,105}, {575,567,105}, {4,3,539}, {4,539,473}, {536,539,3}, {97,63,573}, {97,573,571}, {571,579,97}, {99,97,579}, {99,579,577}, {105,99,577}, {105,577,575}, {96,104,534}, {96,534,537}, {3,96,537}, {3,537,536}, {503,501,124}, {508,507,126}, {491,489,128}, {511,510,126}, {485,483,128}, {434,450,520}, {434,520,471}, {449,437,581}, {449,581,565}, {156,105,585}, {156,585,587}, {587,569,156}, {104,157,529}, {104,529,532}, {475,529,157}, {590,583,355}, {355,356,588}, {355,588,590}, {358,357,524}, {358,524,515}, {525,524,357}, {458,457,62}, {457,593,62}, {479,478,482}, {479,504,549}, {479,482,504}, {482,481,484}, {472,551,550}, {581,551,472}, {482,484,504}, {484,483,486}, {523,553,552}, {504,553,523}, {540,573,572}, {571,573,540}, {544,585,584}, {587,585,544}, {542,577,576}, {575,577,542}, {526,590,589}, {583,590,526}, {535,575,574}, {567,575,535}, {533,567,566}, {585,567,533}, {538,579,578}, {577,579,538}, {543,581,580}, {565,581,543}, {477,569,568}, {563,569,477}, {530,587,586}, {569,587,530}, {541,571,570}, {579,571,541}, {528,583,582}, {561,583,528}, {591,453,592}, {547,592,453}, {521,565,564}, {555,565,521}, {474,557,556}, {573,557,474}, {516,563,562}, {588,563,516}, {519,555,554}, {553,555,519}, {527,559,558}, {551,559,527}, {469,561,560}, {559,561,469}, {462,461,455}, {453,455,461}, {461,463,547}, {548,547,463}, {465,464,462}, {463,462,464}, {464,466,548}, {557,548,466}, {469,560,467}, {528,467,560}, {472,550,470}, {527,470,550}, {474,556,465}, {466,465,556}, {477,568,475}, {530,475,568}, {516,562,476}, {477,476,562}, {519,554,517}, {521,517,554}, {521,564,520}, {543,520,564}, {523,552,518}, {519,518,552}, {479,549,522}, {523,522,549}, {526,589,524}, {589,546,524}, {527,558,468}, {469,468,558}, {528,582,525}, {526,525,582}, {530,586,529}, {544,529,586}, {533,566,531}, {535,531,566}, {535,574,534}, {542,534,574}, {538,578,536}, {541,536,578}, {540,572,473}, {474,473,572}, {541,570,539}, {540,539,570}, {542,576,537}, {538,537,576}, {543,580,471}, {472,471,580}, {544,584,532}, {533,532,584}, {524,545,515}, {516,515,545}, {545,546,588}, {589,588,546}, {453,591,454}, {593,454,591}, {484,486,504}, {486,485,488}, {486,488,504}, {488,487,490}, {488,490,504}, {490,489,492}, {490,492,504}, {492,491,494}, {492,494,504}, {494,493,496}, {494,496,504}, {496,495,498}, {496,498,504}, {498,497,500}, {498,500,504}, {500,499,502}, {500,502,504}, {501,504,502}, {454,593,456}, {457,456,593}, {594,595,596}, {597,598,594}, {599,597,594}, {600,599,594}, {601,600,594}, {602,601,594}, {603,602,594}, {604,603,594}, {605,604,594}, {606,607,608}, {609,606,608}, {610,609,608}, {611,610,608}, {612,611,608}, {613,612,608}, {614,613,608}, {615,614,608}, {616,615,608}, {617,616,608}, {618,617,608}, {619,618,608}, {620,619,608}, {596,608,607}, {595,594,598}, {608,596,595}, {605,594,91}, {91,338,602}, {91,602,603}, {598,597,1}, {594,596,91}, {608,595,1}, {595,598,1}, {616,617,392}, {610,611,394}, {419,421,613}, {419,613,614}, {422,427,607}, {422,607,606}, {427,91,596}, {427,596,607}, {428,420,619}, {428,619,620}, {1,428,620}, {1,620,608}, {420,409,618}, {420,618,619}, {411,422,606}, {411,606,609}, {398,419,614}, {398,614,615}, {421,400,612}, {421,612,613}, {409,392,617}, {409,617,618}, {394,411,609}, {394,609,610}, {604,605,91}, {338,1,599}, {338,599,600}, {392,398,615}, {392,615,616}, {400,394,611}, {400,611,612}, {603,604,91}, {601,602,338}, {597,599,1}, {600,601,338} }); break; case TestMesh::gt2_teeth: mesh = TriangleMesh( - { {15.8899993896484,19.444055557251,2.67489433288574}, {15.9129991531372,19.1590557098389,2.67489433288574}, {15.9039993286133,19.1500549316406,2.67489433288574}, {15.9489994049072,19.2490558624268,2.67489433288574}, - {15.9579992294312,19.3570556640625,2.67489433288574}, {15.8819999694824,18.690055847168,2.67489433288574}, {15.8319997787476,17.7460556030273,2.67489433288574}, {15.8489999771118,18.819055557251,2.67489433288574}, - {15.8589992523193,17.7190551757812,2.67489433288574}, {15.8769998550415,19.0490550994873,2.67489433288574}, {15.7529993057251,17.8080558776855,2.67489433288574}, {15.7869997024536,19.5010547637939,2.67489433288574}, - {14.0329990386963,18.7170543670654,2.67489433288574}, {13.9599990844727,18.7460556030273,2.67489433288574}, {13.9869995117188,20.2840557098389,2.67489433288574}, {14.2029991149902,20.149055480957,2.67489433288574}, - {14.1939992904663,19.9560546875,2.67489433288574}, {14.1939992904663,20.1670551300049,2.67489433288574}, {14.2119998931885,20.0590553283691,2.67489433288574}, {12.1899995803833,19.1840553283691,2.67489433288574}, - {12.096999168396,19.1950550079346,2.67489433288574}, {12.1099996566772,20.6690559387207,2.67489433288574}, {11.382999420166,19.9750556945801,2.67489433288574}, {11.2599992752075,19.2490558624268,2.67489433288574}, - {11.2369995117188,19.9320545196533,2.67489433288574}, {11.5349998474121,20.0640544891357,2.67489433288574}, {11.6259994506836,20.1550559997559,2.67489433288574}, {11.6829986572266,20.2390556335449,2.67489433288574}, - {11.7369995117188,20.3570556640625,2.67489433288574}, {11.8449993133545,20.645055770874,2.67489433288574}, {11.7729988098145,20.4640560150146,2.67489433288574}, {11.7799987792969,20.5370559692383,9.41389465332031}, - {11.7639999389648,20.4470558166504,2.67489433288574}, {11.9559993743896,20.6810550689697,2.67489433288574}, {12.3079996109009,20.6020545959473,2.67489433288574}, {12.1959991455078,19.1860542297363,2.67489433288574}, - {12.2059993743896,20.6540546417236,2.67489433288574}, {12.3489990234375,20.3740558624268,2.67489433288574}, {12.3579998016357,20.2750549316406,2.67489433288574}, {12.3669996261597,20.266056060791,2.67489433288574}, - {12.3849992752075,20.1670551300049,2.67489433288574}, {12.4269990921021,20.0680541992188,2.67489433288574}, {12.5029993057251,19.9540557861328,2.67489433288574}, {12.6169996261597,19.8550548553467,2.67489433288574}, - {12.7449989318848,19.7800559997559,2.67489433288574}, {12.7629995346069,19.7800559997559,2.67489433288574}, {12.8799991607666,19.7350559234619,2.67489433288574}, {13.0369997024536,19.7250556945801,2.67489433288574}, - {13.0149993896484,19.0340557098389,2.67489433288574}, {11.1699991226196,19.2580547332764,2.67489433288574}, {11.0959987640381,19.2580547332764,2.67489433288574}, {11.1209993362427,19.9230556488037,2.67489433288574}, - {13.0599994659424,19.024055480957,2.67489433288574}, {14.9049997329712,18.3170547485352,2.67489433288574}, {14.8779993057251,18.3400554656982,2.67489433288574}, {14.8779993057251,19.149055480957,2.67489433288574}, - {13.3039989471436,19.77805519104,2.67489433288574}, {13.1589994430542,18.9890556335449,2.67489433288574}, {13.1559991836548,19.7350559234619,2.67489433288574}, {13.4269990921021,19.8600559234619,2.67489433288574}, - {13.5339994430542,19.9700546264648,2.67389440536499}, {13.6359996795654,20.1220550537109,2.67489433288574}, {13.6359996795654,20.1400547027588,2.67489433288574}, {13.6719989776611,20.2210559844971,2.67489433288574}, - {13.6899995803833,20.2300548553467,2.67489433288574}, {13.7509994506836,20.3010559082031,2.67489433288574}, {13.8539991378784,20.3180541992188,2.67489433288574}, {14.8329992294312,18.3580551147461,2.67489433288574}, - {14.1849994659424,19.8530559539795,2.67489433288574}, {14.0769996643066,18.7000541687012,2.67489433288574}, {14.1099996566772,20.2400550842285,2.67489433288574}, {14.2009992599487,19.6230545043945,2.67489433288574}, - {14.2729997634888,19.4670543670654,2.67489433288574}, {14.3379993438721,19.3790550231934,2.67489433288574}, {14.4549999237061,19.2770557403564,2.67489433288574}, {14.5899991989136,19.2040557861328,2.67489433288574}, - {14.6079998016357,19.2040557861328,2.67489433288574}, {14.7209997177124,19.1600551605225,2.67489433288574}, {15.1379995346069,19.210054397583,2.67489433288574}, {14.9949998855591,18.2680549621582,2.67489433288574}, - {15.0029993057251,19.1580543518066,2.67489433288574}, {15.2369995117188,19.2760543823242,2.67489433288574}, {15.3779993057251,19.4060554504395,2.67489433288574}, {15.4539995193481,19.520055770874,2.67489433288574}, - {15.471999168396,19.52805519104,2.67489433288574}, {15.5449991226196,19.5830554962158,2.67489433288574}, {15.6529998779297,19.573055267334,2.67489433288574}, {15.7059993743896,17.8360557556152,2.67489433288574}, - {15.9449996948242,18.5560550689697,2.67489433288574}, {15.8589992523193,18.9380550384521,2.67489433288574}, {14.9589996337891,18.2950553894043,2.67489433288574}, {15.7779998779297,19.5100555419922,2.67489433288574}, - {14.0049991607666,20.2750549316406,2.67489433288574}, {12.3489990234375,20.5000553131104,2.67489433288574}, {13.0689992904663,19.0150547027588,2.67489433288574}, {13.0999994277954,19.0100555419922,2.67489433288574}, - {15.9489994049072,19.3670558929443,9.41489505767822}, {15.9489994049072,19.2490558624268,9.41489505767822}, {15.75,17.8080558776855,9.41489505767822}, {15.6639995574951,19.5710544586182,9.41489505767822}, - {15.5709991455078,17.9260559082031,9.41489505767822}, {15.8769998550415,18.690055847168,9.41489505767822}, {15.8499994277954,18.8170547485352,9.41489505767822}, {15.9459991455078,18.5520553588867,9.41489505767822}, - {15.914999961853,17.6890544891357,9.41489505767822}, {15.3999996185303,19.4290542602539,9.41489505767822}, {15.3099994659424,19.339054107666,9.41489505767822}, {15.3729991912842,18.0440559387207,9.41489505767822}, - {15.4579992294312,19.5170555114746,9.41489505767822}, {15.5469999313354,19.5820541381836,9.41489505767822}, {13.2309989929199,19.7610549926758,9.41489505767822}, {13.168999671936,19.7360553741455,9.41489505767822}, - {13.096999168396,19.0140552520752,9.41489505767822}, {13.1999988555908,18.9870548248291,9.41489505767822}, {15.1399993896484,19.2080554962158,9.41489505767822}, {15.0159997940063,19.1600551605225,9.41489505767822}, - {14.9859991073608,18.2770557403564,9.41489505767822}, {15.1749992370605,18.1690559387207,9.41489505767822}, {15.9039993286133,19.1320552825928,9.41489505767822}, {15.8949995040894,19.4460544586182,9.41489505767822}, - {15.8769998550415,19.0420551300049,9.41489505767822}, {12.2169990539551,20.6500549316406,9.41489505767822}, {11.9379997253418,20.6810550689697,9.41489505767822}, {11.8629989624023,19.2130546569824,9.41489505767822}, {12.096999168396,19.1950550079346,9.41489505767822}, {14.1669998168945,18.6640548706055,9.41489505767822}, {14.1039991378784,20.2460556030273,9.41489505767822}, {13.9849996566772,18.7360553741455,9.41489505767822}, {14.7349996566772,19.1590557098389,9.41489505767822}, {14.5849990844727,19.2050552368164,9.41489505767822}, {14.5719995498657,18.4850559234619,9.41489505767822}, {14.1939992904663,19.6760559082031,9.41489505767822}, {14.1849994659424,19.9330558776855,9.41489505767822}, {14.1759996414185,18.6640548706055,9.41489505767822}, {14.261999130249,19.4890556335449,9.41489505767822}, {14.3539991378784,19.3610553741455,9.41489505767822}, {14.3559989929199,18.5830554962158,9.41489505767822}, {11.6039991378784,20.1250553131104,9.41489505767822}, {11.5209999084473,20.0520553588867,9.41489505767822}, {11.4209995269775,19.2480545043945,9.41489505767822}, {11.6989994049072,20.2690544128418,9.41389465332031}, {11.7609996795654,20.4310550689697,9.41489505767822}, {11.8359994888306,19.2130546569824,9.41489505767822}, {14.1889991760254,20.1710548400879,9.41489505767822}, {13.9689998626709,20.2840557098389,9.41489505767822}, {13.8739995956421,20.315055847168,9.41489505767822}, {13.7799997329712,18.8080558776855,9.41489505767822}, {13.9869995117188,20.2750549316406,9.41489505767822}, {12.3129997253418,20.5980548858643,9.41489505767822}, {12.3399991989136,20.5090560913086,9.41489505767822}, {12.3489990234375,20.3830547332764,9.41489505767822}, {12.3599996566772,20.2680549621582,9.41489505767822}, {12.3849992752075,20.1850547790527,9.41489505767822}, {12.3849992752075,20.1670551300049,9.41489505767822}, {12.4249992370605,20.065055847168,9.41489505767822}, {12.4729995727539,19.1350555419922,9.41489505767822}, {14.4399995803833,19.2900543212891,9.41489505767822}, {14.3649997711182,18.5740547180176,9.41489505767822}, {13.5729999542236,20.0310554504395,9.41489505767822}, {13.4889993667603,19.9140548706055,9.41489505767822}, {13.5639991760254,18.8710556030273,9.41489505767822}, {13.6389999389648,20.1310558319092,9.41489505767822}, {13.6719989776611,20.2130546569824,9.41489505767822}, {13.75,20.3020553588867,9.41489505767822}, {12.7399997711182,19.7810554504395,9.41489505767822}, {12.6189994812012,19.8520545959473,9.41489505767822}, {12.5799999237061,19.1200542449951,9.41489505767822}, {12.8349990844727,19.069055557251,9.41489505767822}, {11.2669992446899,19.9350547790527,9.41489505767822}, {11.1029987335205,19.9230556488037,9.41489505767822}, {11.0209999084473,19.2600555419922,9.41489505767822}, {11.3819999694824,19.9710559844971,9.41489505767822}, {13.418999671936,19.8530559539795,9.41489505767822}, {13.4329996109009,18.9160556793213,9.41489505767822}, {11.8399991989136,20.6430549621582,9.41489505767822}, {13.3119993209839,19.7800559997559,9.41489505767822}, {15.2189998626709,19.2600555419922,9.41489505767822}, {15.1839990615845,18.1600551605225,9.41489505767822}, {15.3639993667603,18.0520553588867,9.41489505767822}, {13.0189990997314,19.7250556945801,9.41489505767822}, {12.8949995040894,19.7350559234619,9.41489505767822}, {15.9039993286133,19.1500549316406,9.41489505767822}, {15.7699995040894,19.5140552520752,9.41489505767822}, {15.8589992523193,18.9340553283691,9.41489505767822}, {14.1939992904663,19.9510555267334,9.41489505767822}, {14.2119998931885,20.0630550384521,9.41489505767822}, {14.8589992523193,19.149055480957,9.41489505767822}, {14.8159999847412,18.3670558929443,9.41489505767822}, {14.8959999084473,18.3220558166504,9.41489505767822}, {12.5189990997314,19.9360542297363,9.41489505767822}, {11.0209999084473,19.9290542602539,9.41489505767822}, {11.0209999084473,19.2530555725098,2.67489433288574}, {11.0209999084473,19.9300556182861,2.67489433288574}, {15.9799995422363,18.505931854248,5.58724021911621}, {15.9799995422363,18.5044555664062,9.41489505767822}, {15.9799995422363,18.5041732788086,2.67489433288574}, {15.9799995422363,18.1684837341309,2.67489433288574}, {15.9799995422363,18.1288299560547,9.41489505767822}, {15.9799995422363,17.9876575469971,2.67489433288574}, {15.9799995422363,17.6247596740723,3.91620373725891}, {15.9799995422363,17.6247596740723,2.67489433288574}, {15.9799995422363,17.6254329681396,4.32245063781738}, {15.9799995422363,17.8920269012451,9.41489505767822}, {15.9799995422363,17.8795108795166,2.67489433288574}, {15.9799995422363,17.629810333252,4.58585262298584}, {15.9799995422363,17.6336059570312,5.27938556671143}, {15.9799995422363,17.8311748504639,2.67489433288574}, {15.9799995422363,17.638355255127,9.41489505767822}, {15.9799995422363,17.6346111297607,5.98653984069824}, {15.9799995422363,17.8728256225586,2.67489433288574}, {15.9799995422363,18.2221603393555,2.67489433288574} }, - { {0,1,2}, {0,3,1}, {0,4,3}, {5,6,7}, {8,6,5}, {2,9,0}, {6,10,11}, {12,13,14}, {15,16,17}, {18,16,15}, {19,20,21}, {22,23,24}, {25,23,22}, {26,23,25}, {27,23,26}, {28,23,27}, {29,30,31}, {29,32,30}, {29,28,32}, {33,28,29}, {33,23,28}, {21,23,33}, {20,23,21}, {34,35,36}, {37,35,34}, {38,35,37}, {39,35,38}, {40,35,39}, {41,35,40}, {42,35,41}, {43,35,42}, {44,35,43}, {45,35,44}, {46,35,45}, {47,35,46}, {48,35,47}, {49,50,51}, {52,48,47}, {23,49,24}, {53,54,55}, {56,57,58}, {59,57,56}, {60,57,59}, {61,57,60}, {62,57,61}, {63,57,62}, {64,57,63}, {65,57,64}, {66,57,65}, {13,57,66}, {54,67,55}, {68,69,70}, {71,69,68}, {72,69,71}, {73,69,72}, {74,69,73}, {75,69,74}, {76,69,75}, {77,69,76}, {67,69,77}, {70,16,68}, {70,17,16}, {78,79,80}, {81,79,78}, {82,79,81}, {83,79,82}, {84,79,83}, {85,79,84}, {86,79,85}, {87,79,86}, {88,8,5}, {11,7,6}, {11,89,7}, {11,9,89}, {11,0,9}, {55,90,53}, {55,79,90}, {55,80,79}, {91,11,10}, {92,69,12}, {92,70,69}, {34,93,37}, {47,94,52}, {47,95,94}, {47,57,95}, {47,58,57}, {51,24,49}, {21,35,19}, {21,36,35}, {14,92,12}, {86,10,87}, {86,91,10}, {77,55,67}, {66,14,13}, {96,97,4}, {98,99,100}, {101,102,98}, {103,101,98}, {104,103,98}, {105,106,107}, {108,105,107}, {109,108,107}, {100,109,107}, {110,111,112}, {113,110,112}, {114,115,116}, {117,114,116}, {118,119,120}, {121,122,123}, {124,121,123}, {125,126,127}, {128,129,130}, {131,132,133}, {71,131,133}, {134,71,133}, {135,134,133}, {136,135,133}, {137,138,139}, {140,137,139}, {141,140,139}, {142,31,141}, {142,141,139}, {143,126,132}, {144,145,146}, {147,144,146}, {127,147,146}, {148,121,124}, {149,148,124}, {150,149,124}, {151,150,124}, {152,151,124}, {153,152,124}, {154,153,124}, {155,154,124}, {129,156,157}, {130,129,157}, {158,159,160}, {161,158,160}, {162,161,160}, {163,162,160}, {146,163,160}, {164,165,166}, {167,164,166}, {168,169,170}, {171,168,170}, {139,171,170}, {159,172,173}, {123,174,142}, {175,110,113}, {173,175,113}, {106,176,177}, {178,106,177}, {179,180,167}, {112,179,167}, {175,173,172}, {119,118,181}, {119,181,97}, {119,97,96}, {182,98,102}, {182,102,183}, {182,183,120}, {182,120,119}, {143,132,184}, {184,185,143}, {147,127,126}, {174,123,122}, {159,173,160}, {126,125,133}, {126,133,132}, {186,187,188}, {186,188,116}, {186,116,115}, {99,98,182}, {109,100,99}, {106,178,107}, {114,117,177}, {114,177,176}, {128,130,187}, {128,187,186}, {135,136,157}, {135,157,156}, {163,146,145}, {164,167,180}, {179,112,111}, {171,139,138}, {189,155,166}, {189,166,165}, {149,150,93}, {154,155,189}, {31,142,174}, {114,176,78}, {81,78,176}, {7,89,183}, {89,9,120}, {89,120,183}, {78,80,114}, {176,106,81}, {88,5,103}, {183,102,7}, {118,120,9}, {9,2,181}, {9,181,118}, {115,114,80}, {82,81,106}, {101,103,5}, {102,101,5}, {5,7,102}, {97,181,2}, {2,1,97}, {1,3,97}, {80,55,115}, {172,159,59}, {59,56,172}, {3,4,97}, {4,0,96}, {105,108,82}, {186,115,55}, {82,106,105}, {83,82,108}, {60,59,159}, {175,172,56}, {119,96,0}, {0,11,119}, {108,109,84}, {84,83,108}, {55,77,186}, {56,58,110}, {56,110,175}, {60,159,158}, {11,91,182}, {182,119,11}, {91,86,182}, {85,84,109}, {86,85,99}, {128,186,77}, {58,111,110}, {158,161,60}, {26,25,137}, {138,137,25}, {99,182,86}, {109,99,85}, {77,76,128}, {58,47,111}, {61,60,161}, {137,140,26}, {27,26,140}, {25,22,138}, {129,128,76}, {76,75,129}, {75,74,129}, {74,73,156}, {73,72,135}, {68,16,184}, {68,184,132}, {16,18,185}, {161,162,62}, {62,61,161}, {179,111,47}, {171,138,22}, {156,129,74}, {135,156,73}, {134,135,72}, {72,71,134}, {68,132,131}, {185,184,16}, {18,15,185}, {63,62,162}, {28,27,140}, {22,24,171}, {71,68,131}, {15,17,143}, {15,143,185}, {17,70,143}, {70,92,126}, {162,163,64}, {64,63,162}, {180,179,47}, {47,46,180}, {140,141,28}, {168,171,24}, {126,143,70}, {92,14,147}, {147,126,92}, {14,66,144}, {14,144,147}, {65,64,163}, {66,65,145}, {46,45,180}, {32,28,141}, {24,51,168}, {145,144,66}, {163,145,65}, {164,180,45}, {45,44,164}, {44,43,164}, {43,42,165}, {38,37,151}, {150,151,37}, {37,93,150}, {141,31,30}, {30,32,141}, {169,168,51}, {165,164,43}, {189,165,42}, {42,41,189}, {40,39,152}, {40,152,153}, {151,152,39}, {39,38,151}, {93,34,149}, {154,189,41}, {153,154,41}, {41,40,153}, {148,149,34}, {34,36,148}, {36,21,121}, {31,174,29}, {121,148,36}, {21,33,122}, {21,122,121}, {33,29,122}, {174,122,29}, {116,188,53}, {104,98,10}, {87,10,98}, {98,100,87}, {79,87,100}, {79,100,107}, {90,79,107}, {90,107,178}, {178,177,90}, {53,90,177}, {53,177,117}, {117,116,53}, {54,53,188}, {54,188,187}, {67,54,187}, {67,187,130}, {69,67,130}, {69,130,157}, {12,69,157}, {12,157,136}, {136,133,12}, {12,133,125}, {125,127,12}, {13,12,127}, {127,146,13}, {57,13,146}, {57,146,160}, {95,57,160}, {95,160,173}, {173,113,95}, {94,95,113}, {113,112,94}, {52,94,112}, {48,52,112}, {112,167,48}, {35,48,167}, {35,167,166}, {19,35,166}, {139,170,50}, {50,49,139}, {166,155,19}, {20,19,155}, {155,124,20}, {23,20,124}, {23,124,123}, {49,23,123}, {49,123,142}, {142,139,49}, {190,191,170}, {192,191,190}, {191,192,51}, {191,51,50}, {170,169,190}, {169,51,192}, {169,192,190}, {170,191,50}, {193,194,195}, {196,197,198}, {199,200,201}, {198,202,203}, {204,201,200}, {205,204,200}, {206,207,208}, {206,208,205}, {206,205,200}, {207,206,209}, {207,209,203}, {207,203,202}, {202,198,197}, {197,196,210}, {197,210,195}, {197,195,194}, {8,88,195}, {8,195,210}, {210,196,8}, {196,198,8}, {198,203,8}, {203,209,8}, {209,206,8}, {206,200,8}, {202,197,104}, {207,202,104}, {103,104,197}, {103,197,194}, {193,195,88}, {88,103,194}, {88,194,193}, {200,199,8}, {199,201,8}, {204,205,6}, {6,8,201}, {6,201,204}, {10,6,205}, {10,205,208}, {104,10,208}, {104,208,207} }); + { {15.8899993896484f,19.444055557251f,2.67489433288574f}, {15.9129991531372f,19.1590557098389f,2.67489433288574f}, {15.9039993286133f,19.1500549316406f,2.67489433288574f}, {15.9489994049072f,19.2490558624268f,2.67489433288574f}, + {15.9579992294312f,19.3570556640625f,2.67489433288574f}, {15.8819999694824f,18.690055847168f,2.67489433288574f}, {15.8319997787476f,17.7460556030273f,2.67489433288574f}, {15.8489999771118f,18.819055557251f,2.67489433288574f}, + {15.8589992523193f,17.7190551757812f,2.67489433288574f}, {15.8769998550415f,19.0490550994873f,2.67489433288574f}, {15.7529993057251f,17.8080558776855f,2.67489433288574f}, {15.7869997024536f,19.5010547637939f,2.67489433288574f}, + {14.0329990386963f,18.7170543670654f,2.67489433288574f}, {13.9599990844727f,18.7460556030273f,2.67489433288574f}, {13.9869995117188f,20.2840557098389f,2.67489433288574f}, {14.2029991149902f,20.149055480957f,2.67489433288574f}, + {14.1939992904663f,19.9560546875f,2.67489433288574f}, {14.1939992904663f,20.1670551300049f,2.67489433288574f}, {14.2119998931885f,20.0590553283691f,2.67489433288574f}, {12.1899995803833f,19.1840553283691f,2.67489433288574f}, + {12.096999168396f,19.1950550079346f,2.67489433288574f}, {12.1099996566772f,20.6690559387207f,2.67489433288574f}, {11.382999420166f,19.9750556945801f,2.67489433288574f}, {11.2599992752075f,19.2490558624268f,2.67489433288574f}, + {11.2369995117188f,19.9320545196533f,2.67489433288574f}, {11.5349998474121f,20.0640544891357f,2.67489433288574f}, {11.6259994506836f,20.1550559997559f,2.67489433288574f}, {11.6829986572266f,20.2390556335449f,2.67489433288574f}, + {11.7369995117188f,20.3570556640625f,2.67489433288574f}, {11.8449993133545f,20.645055770874f,2.67489433288574f}, {11.7729988098145f,20.4640560150146f,2.67489433288574f}, {11.7799987792969f,20.5370559692383f,9.41389465332031f}, + {11.7639999389648f,20.4470558166504f,2.67489433288574f}, {11.9559993743896f,20.6810550689697f,2.67489433288574f}, {12.3079996109009f,20.6020545959473f,2.67489433288574f}, {12.1959991455078f,19.1860542297363f,2.67489433288574f}, + {12.2059993743896f,20.6540546417236f,2.67489433288574f}, {12.3489990234375f,20.3740558624268f,2.67489433288574f}, {12.3579998016357f,20.2750549316406f,2.67489433288574f}, {12.3669996261597f,20.266056060791f,2.67489433288574f}, + {12.3849992752075f,20.1670551300049f,2.67489433288574f}, {12.4269990921021f,20.0680541992188f,2.67489433288574f}, {12.5029993057251f,19.9540557861328f,2.67489433288574f}, {12.6169996261597f,19.8550548553467f,2.67489433288574f}, + {12.7449989318848f,19.7800559997559f,2.67489433288574f}, {12.7629995346069f,19.7800559997559f,2.67489433288574f}, {12.8799991607666f,19.7350559234619f,2.67489433288574f}, {13.0369997024536f,19.7250556945801f,2.67489433288574f}, + {13.0149993896484f,19.0340557098389f,2.67489433288574f}, {11.1699991226196f,19.2580547332764f,2.67489433288574f}, {11.0959987640381f,19.2580547332764f,2.67489433288574f}, {11.1209993362427f,19.9230556488037f,2.67489433288574f}, + {13.0599994659424f,19.024055480957f,2.67489433288574f}, {14.9049997329712f,18.3170547485352f,2.67489433288574f}, {14.8779993057251f,18.3400554656982f,2.67489433288574f}, {14.8779993057251f,19.149055480957f,2.67489433288574f}, + {13.3039989471436f,19.77805519104f,2.67489433288574f}, {13.1589994430542f,18.9890556335449f,2.67489433288574f}, {13.1559991836548f,19.7350559234619f,2.67489433288574f}, {13.4269990921021f,19.8600559234619f,2.67489433288574f}, + {13.5339994430542f,19.9700546264648f,2.67389440536499f}, {13.6359996795654f,20.1220550537109f,2.67489433288574f}, {13.6359996795654f,20.1400547027588f,2.67489433288574f}, {13.6719989776611f,20.2210559844971f,2.67489433288574f}, + {13.6899995803833f,20.2300548553467f,2.67489433288574f}, {13.7509994506836f,20.3010559082031f,2.67489433288574f}, {13.8539991378784f,20.3180541992188f,2.67489433288574f}, {14.8329992294312f,18.3580551147461f,2.67489433288574f}, + {14.1849994659424f,19.8530559539795f,2.67489433288574f}, {14.0769996643066f,18.7000541687012f,2.67489433288574f}, {14.1099996566772f,20.2400550842285f,2.67489433288574f}, {14.2009992599487f,19.6230545043945f,2.67489433288574f}, + {14.2729997634888f,19.4670543670654f,2.67489433288574f}, {14.3379993438721f,19.3790550231934f,2.67489433288574f}, {14.4549999237061f,19.2770557403564f,2.67489433288574f}, {14.5899991989136f,19.2040557861328f,2.67489433288574f}, + {14.6079998016357f,19.2040557861328f,2.67489433288574f}, {14.7209997177124f,19.1600551605225f,2.67489433288574f}, {15.1379995346069f,19.210054397583f,2.67489433288574f}, {14.9949998855591f,18.2680549621582f,2.67489433288574f}, + {15.0029993057251f,19.1580543518066f,2.67489433288574f}, {15.2369995117188f,19.2760543823242f,2.67489433288574f}, {15.3779993057251f,19.4060554504395f,2.67489433288574f}, {15.4539995193481f,19.520055770874f,2.67489433288574f}, + {15.471999168396f,19.52805519104f,2.67489433288574f}, {15.5449991226196f,19.5830554962158f,2.67489433288574f}, {15.6529998779297f,19.573055267334f,2.67489433288574f}, {15.7059993743896f,17.8360557556152f,2.67489433288574f}, + {15.9449996948242f,18.5560550689697f,2.67489433288574f}, {15.8589992523193f,18.9380550384521f,2.67489433288574f}, {14.9589996337891f,18.2950553894043f,2.67489433288574f}, {15.7779998779297f,19.5100555419922f,2.67489433288574f}, + {14.0049991607666f,20.2750549316406f,2.67489433288574f}, {12.3489990234375f,20.5000553131104f,2.67489433288574f}, {13.0689992904663f,19.0150547027588f,2.67489433288574f}, {13.0999994277954f,19.0100555419922f,2.67489433288574f}, + {15.9489994049072f,19.3670558929443f,9.41489505767822f}, {15.9489994049072f,19.2490558624268f,9.41489505767822f}, {15.75f,17.8080558776855f,9.41489505767822f}, {15.6639995574951f,19.5710544586182f,9.41489505767822f}, + {15.5709991455078f,17.9260559082031f,9.41489505767822f}, {15.8769998550415f,18.690055847168f,9.41489505767822f}, {15.8499994277954f,18.8170547485352f,9.41489505767822f}, {15.9459991455078f,18.5520553588867f,9.41489505767822f}, + {15.914999961853f,17.6890544891357f,9.41489505767822f}, {15.3999996185303f,19.4290542602539f,9.41489505767822f}, {15.3099994659424f,19.339054107666f,9.41489505767822f}, {15.3729991912842f,18.0440559387207f,9.41489505767822f}, + {15.4579992294312f,19.5170555114746f,9.41489505767822f}, {15.5469999313354f,19.5820541381836f,9.41489505767822f}, {13.2309989929199f,19.7610549926758f,9.41489505767822f}, {13.168999671936f,19.7360553741455f,9.41489505767822f}, + {13.096999168396f,19.0140552520752f,9.41489505767822f}, {13.1999988555908f,18.9870548248291f,9.41489505767822f}, {15.1399993896484f,19.2080554962158f,9.41489505767822f}, {15.0159997940063f,19.1600551605225f,9.41489505767822f}, + {14.9859991073608f,18.2770557403564f,9.41489505767822f}, {15.1749992370605f,18.1690559387207f,9.41489505767822f}, {15.9039993286133f,19.1320552825928f,9.41489505767822f}, {15.8949995040894f,19.4460544586182f,9.41489505767822f}, + {15.8769998550415f,19.0420551300049f,9.41489505767822f}, {12.2169990539551f,20.6500549316406f,9.41489505767822f}, {11.9379997253418f,20.6810550689697f,9.41489505767822f}, {11.8629989624023f,19.2130546569824f,9.41489505767822f}, + {12.096999168396f,19.1950550079346f,9.41489505767822f}, {14.1669998168945f,18.6640548706055f,9.41489505767822f}, {14.1039991378784f,20.2460556030273f,9.41489505767822f}, {13.9849996566772f,18.7360553741455f,9.41489505767822f}, + {14.7349996566772f,19.1590557098389f,9.41489505767822f}, {14.5849990844727f,19.2050552368164f,9.41489505767822f}, {14.5719995498657f,18.4850559234619f,9.41489505767822f}, {14.1939992904663f,19.6760559082031f,9.41489505767822f}, + {14.1849994659424f,19.9330558776855f,9.41489505767822f}, {14.1759996414185f,18.6640548706055f,9.41489505767822f}, {14.261999130249f,19.4890556335449f,9.41489505767822f}, {14.3539991378784f,19.3610553741455f,9.41489505767822f}, + {14.3559989929199f,18.5830554962158f,9.41489505767822f}, {11.6039991378784f,20.1250553131104f,9.41489505767822f}, {11.5209999084473f,20.0520553588867f,9.41489505767822f}, {11.4209995269775f,19.2480545043945f,9.41489505767822f}, + {11.6989994049072f,20.2690544128418f,9.41389465332031f}, {11.7609996795654f,20.4310550689697f,9.41489505767822f}, {11.8359994888306f,19.2130546569824f,9.41489505767822f}, {14.1889991760254f,20.1710548400879f,9.41489505767822f}, + {13.9689998626709f,20.2840557098389f,9.41489505767822f}, {13.8739995956421f,20.315055847168f,9.41489505767822f}, {13.7799997329712f,18.8080558776855f,9.41489505767822f}, {13.9869995117188f,20.2750549316406f,9.41489505767822f}, + {12.3129997253418f,20.5980548858643f,9.41489505767822f}, {12.3399991989136f,20.5090560913086f,9.41489505767822f}, {12.3489990234375f,20.3830547332764f,9.41489505767822f}, {12.3599996566772f,20.2680549621582f,9.41489505767822f}, + {12.3849992752075f,20.1850547790527f,9.41489505767822f}, {12.3849992752075f,20.1670551300049f,9.41489505767822f}, {12.4249992370605f,20.065055847168f,9.41489505767822f}, {12.4729995727539f,19.1350555419922f,9.41489505767822f}, + {14.4399995803833f,19.2900543212891f,9.41489505767822f}, {14.3649997711182f,18.5740547180176f,9.41489505767822f}, {13.5729999542236f,20.0310554504395f,9.41489505767822f}, {13.4889993667603f,19.9140548706055f,9.41489505767822f}, + {13.5639991760254f,18.8710556030273f,9.41489505767822f}, {13.6389999389648f,20.1310558319092f,9.41489505767822f}, {13.6719989776611f,20.2130546569824f,9.41489505767822f}, {13.75f,20.3020553588867f,9.41489505767822f}, + {12.7399997711182f,19.7810554504395f,9.41489505767822f}, {12.6189994812012f,19.8520545959473f,9.41489505767822f}, {12.5799999237061f,19.1200542449951f,9.41489505767822f}, {12.8349990844727f,19.069055557251f,9.41489505767822f}, + {11.2669992446899f,19.9350547790527f,9.41489505767822f}, {11.1029987335205f,19.9230556488037f,9.41489505767822f}, {11.0209999084473f,19.2600555419922f,9.41489505767822f}, {11.3819999694824f,19.9710559844971f,9.41489505767822f}, + {13.418999671936f,19.8530559539795f,9.41489505767822f}, {13.4329996109009f,18.9160556793213f,9.41489505767822f}, {11.8399991989136f,20.6430549621582f,9.41489505767822f}, {13.3119993209839f,19.7800559997559f,9.41489505767822f}, + {15.2189998626709f,19.2600555419922f,9.41489505767822f}, {15.1839990615845f,18.1600551605225f,9.41489505767822f}, {15.3639993667603f,18.0520553588867f,9.41489505767822f}, {13.0189990997314f,19.7250556945801f,9.41489505767822f}, + {12.8949995040894f,19.7350559234619f,9.41489505767822f}, {15.9039993286133f,19.1500549316406f,9.41489505767822f}, {15.7699995040894f,19.5140552520752f,9.41489505767822f}, {15.8589992523193f,18.9340553283691f,9.41489505767822f}, + {14.1939992904663f,19.9510555267334f,9.41489505767822f}, {14.2119998931885f,20.0630550384521f,9.41489505767822f}, {14.8589992523193f,19.149055480957f,9.41489505767822f}, {14.8159999847412f,18.3670558929443f,9.41489505767822f}, + {14.8959999084473f,18.3220558166504f,9.41489505767822f}, {12.5189990997314f,19.9360542297363f,9.41489505767822f}, {11.0209999084473f,19.9290542602539f,9.41489505767822f}, {11.0209999084473f,19.2530555725098f,2.67489433288574f}, + {11.0209999084473f,19.9300556182861f,2.67489433288574f}, {15.9799995422363f,18.505931854248f,5.58724021911621f}, {15.9799995422363f,18.5044555664062f,9.41489505767822f}, {15.9799995422363f,18.5041732788086f,2.67489433288574f}, + {15.9799995422363f,18.1684837341309f,2.67489433288574f}, {15.9799995422363f,18.1288299560547f,9.41489505767822f}, {15.9799995422363f,17.9876575469971f,2.67489433288574f}, {15.9799995422363f,17.6247596740723f,3.91620373725891f}, + {15.9799995422363f,17.6247596740723f,2.67489433288574f}, {15.9799995422363f,17.6254329681396f,4.32245063781738f}, {15.9799995422363f,17.8920269012451f,9.41489505767822f}, {15.9799995422363f,17.8795108795166f,2.67489433288574f}, + {15.9799995422363f,17.629810333252f,4.58585262298584f}, {15.9799995422363f,17.6336059570312f,5.27938556671143f}, {15.9799995422363f,17.8311748504639f,2.67489433288574f}, {15.9799995422363f,17.638355255127f,9.41489505767822f}, + {15.9799995422363f,17.6346111297607f,5.98653984069824f}, {15.9799995422363f,17.8728256225586f,2.67489433288574f}, {15.9799995422363f,18.2221603393555f,2.67489433288574f} }, + { {0,1,2}, {0,3,1}, {0,4,3}, {5,6,7}, {8,6,5}, {2,9,0}, {6,10,11}, {12,13,14}, {15,16,17}, {18,16,15}, {19,20,21}, {22,23,24}, {25,23,22}, {26,23,25}, {27,23,26}, {28,23,27}, {29,30,31}, {29,32,30}, {29,28,32}, {33,28,29}, {33,23,28}, {21,23,33}, {20,23,21}, {34,35,36}, {37,35,34}, {38,35,37}, {39,35,38}, {40,35,39}, {41,35,40}, {42,35,41}, {43,35,42}, {44,35,43}, {45,35,44}, {46,35,45}, {47,35,46}, {48,35,47}, {49,50,51}, {52,48,47}, {23,49,24}, {53,54,55}, {56,57,58}, {59,57,56}, {60,57,59}, {61,57,60}, {62,57,61}, {63,57,62}, {64,57,63}, {65,57,64}, {66,57,65}, {13,57,66}, {54,67,55}, {68,69,70}, {71,69,68}, {72,69,71}, {73,69,72}, {74,69,73}, {75,69,74}, {76,69,75}, {77,69,76}, {67,69,77}, {70,16,68}, {70,17,16}, {78,79,80}, {81,79,78}, {82,79,81}, {83,79,82}, {84,79,83}, {85,79,84}, {86,79,85}, {87,79,86}, {88,8,5}, {11,7,6}, {11,89,7}, {11,9,89}, {11,0,9}, {55,90,53}, {55,79,90}, {55,80,79}, {91,11,10}, {92,69,12}, {92,70,69}, {34,93,37}, {47,94,52}, {47,95,94}, {47,57,95}, {47,58,57}, {51,24,49}, {21,35,19}, {21,36,35}, {14,92,12}, {86,10,87}, {86,91,10}, {77,55,67}, {66,14,13}, {96,97,4}, {98,99,100}, {101,102,98}, {103,101,98}, {104,103,98}, {105,106,107}, {108,105,107}, {109,108,107}, {100,109,107}, {110,111,112}, {113,110,112}, {114,115,116}, {117,114,116}, {118,119,120}, {121,122,123}, {124,121,123}, {125,126,127}, {128,129,130}, {131,132,133}, {71,131,133}, {134,71,133}, {135,134,133}, {136,135,133}, {137,138,139}, {140,137,139}, {141,140,139}, {142,31,141}, {142,141,139}, {143,126,132}, {144,145,146}, {147,144,146}, {127,147,146}, {148,121,124}, {149,148,124}, {150,149,124}, {151,150,124}, {152,151,124}, {153,152,124}, {154,153,124}, {155,154,124}, {129,156,157}, {130,129,157}, {158,159,160}, {161,158,160}, {162,161,160}, {163,162,160}, {146,163,160}, {164,165,166}, {167,164,166}, {168,169,170}, {171,168,170}, {139,171,170}, {159,172,173}, {123,174,142}, {175,110,113}, {173,175,113}, {106,176,177}, {178,106,177}, {179,180,167}, {112,179,167}, {175,173,172}, {119,118,181}, {119,181,97}, {119,97,96}, {182,98,102}, {182,102,183}, {182,183,120}, {182,120,119}, {143,132,184}, {184,185,143}, {147,127,126}, {174,123,122}, {159,173,160}, {126,125,133}, {126,133,132}, {186,187,188}, {186,188,116}, {186,116,115}, {99,98,182}, {109,100,99}, {106,178,107}, {114,117,177}, {114,177,176}, {128,130,187}, {128,187,186}, {135,136,157}, {135,157,156}, {163,146,145}, {164,167,180}, {179,112,111}, {171,139,138}, {189,155,166}, {189,166,165}, {149,150,93}, {154,155,189}, {31,142,174}, {114,176,78}, {81,78,176}, {7,89,183}, {89,9,120}, {89,120,183}, {78,80,114}, {176,106,81}, {88,5,103}, {183,102,7}, {118,120,9}, {9,2,181}, {9,181,118}, {115,114,80}, {82,81,106}, {101,103,5}, {102,101,5}, {5,7,102}, {97,181,2}, {2,1,97}, {1,3,97}, {80,55,115}, {172,159,59}, {59,56,172}, {3,4,97}, {4,0,96}, {105,108,82}, {186,115,55}, {82,106,105}, {83,82,108}, {60,59,159}, {175,172,56}, {119,96,0}, {0,11,119}, {108,109,84}, {84,83,108}, {55,77,186}, {56,58,110}, {56,110,175}, {60,159,158}, {11,91,182}, {182,119,11}, {91,86,182}, {85,84,109}, {86,85,99}, {128,186,77}, {58,111,110}, {158,161,60}, {26,25,137}, {138,137,25}, {99,182,86}, {109,99,85}, {77,76,128}, {58,47,111}, {61,60,161}, {137,140,26}, {27,26,140}, {25,22,138}, {129,128,76}, {76,75,129}, {75,74,129}, {74,73,156}, {73,72,135}, {68,16,184}, {68,184,132}, {16,18,185}, {161,162,62}, {62,61,161}, {179,111,47}, {171,138,22}, {156,129,74}, {135,156,73}, {134,135,72}, {72,71,134}, {68,132,131}, {185,184,16}, {18,15,185}, {63,62,162}, {28,27,140}, {22,24,171}, {71,68,131}, {15,17,143}, {15,143,185}, {17,70,143}, {70,92,126}, {162,163,64}, {64,63,162}, {180,179,47}, {47,46,180}, {140,141,28}, {168,171,24}, {126,143,70}, {92,14,147}, {147,126,92}, {14,66,144}, {14,144,147}, {65,64,163}, {66,65,145}, {46,45,180}, {32,28,141}, {24,51,168}, {145,144,66}, {163,145,65}, {164,180,45}, {45,44,164}, {44,43,164}, {43,42,165}, {38,37,151}, {150,151,37}, {37,93,150}, {141,31,30}, {30,32,141}, {169,168,51}, {165,164,43}, {189,165,42}, {42,41,189}, {40,39,152}, {40,152,153}, {151,152,39}, {39,38,151}, {93,34,149}, {154,189,41}, {153,154,41}, {41,40,153}, {148,149,34}, {34,36,148}, {36,21,121}, {31,174,29}, {121,148,36}, {21,33,122}, {21,122,121}, {33,29,122}, {174,122,29}, {116,188,53}, {104,98,10}, {87,10,98}, {98,100,87}, {79,87,100}, {79,100,107}, {90,79,107}, {90,107,178}, {178,177,90}, {53,90,177}, {53,177,117}, {117,116,53}, {54,53,188}, {54,188,187}, {67,54,187}, {67,187,130}, {69,67,130}, {69,130,157}, {12,69,157}, {12,157,136}, {136,133,12}, {12,133,125}, {125,127,12}, {13,12,127}, {127,146,13}, {57,13,146}, {57,146,160}, {95,57,160}, {95,160,173}, {173,113,95}, {94,95,113}, {113,112,94}, {52,94,112}, {48,52,112}, {112,167,48}, {35,48,167}, {35,167,166}, {19,35,166}, {139,170,50}, {50,49,139}, {166,155,19}, {20,19,155}, {155,124,20}, {23,20,124}, {23,124,123}, {49,23,123}, {49,123,142}, {142,139,49}, {190,191,170}, {192,191,190}, {191,192,51}, {191,51,50}, {170,169,190}, {169,51,192}, {169,192,190}, {170,191,50}, {193,194,195}, {196,197,198}, {199,200,201}, {198,202,203}, {204,201,200}, {205,204,200}, {206,207,208}, {206,208,205}, {206,205,200}, {207,206,209}, {207,209,203}, {207,203,202}, {202,198,197}, {197,196,210}, {197,210,195}, {197,195,194}, {8,88,195}, {8,195,210}, {210,196,8}, {196,198,8}, {198,203,8}, {203,209,8}, {209,206,8}, {206,200,8}, {202,197,104}, {207,202,104}, {103,104,197}, {103,197,194}, {193,195,88}, {88,103,194}, {88,194,193}, {200,199,8}, {199,201,8}, {204,205,6}, {6,8,201}, {6,201,204}, {10,6,205}, {10,205,208}, {104,10,208}, {104,208,207} }); break; case TestMesh::pyramid: mesh = TriangleMesh( - { {10,10,40}, {0,0,0}, {20,0,0}, {20,20,0}, {0,20,0} }, + { {10.f,10.f,40.f}, {0.f,0.f,0.f}, {20.f,0.f,0.f}, {20.f,20.f,0.f}, {0.f,20.f,0.f} }, { {0,1,2}, {0,3,4}, {3,1,4}, {1,3,2}, {3,0,2}, {4,1,0} }); break; case TestMesh::two_hollow_squares: From ee83a398bb0313cc1521268ea71301d8a2189fa3 Mon Sep 17 00:00:00 2001 From: rtyr <36745189+rtyr@users.noreply.github.com> Date: Wed, 30 Aug 2023 14:23:49 +0200 Subject: [PATCH 090/136] Binary g-code, arc fitting, QOI/PNG thumbnails, 90degree XL tower, XL specific filament variants. --- resources/profiles/PrusaResearch.idx | 2 + resources/profiles/PrusaResearch.ini | 3139 +++++++++++++++++++------- 2 files changed, 2277 insertions(+), 864 deletions(-) diff --git a/resources/profiles/PrusaResearch.idx b/resources/profiles/PrusaResearch.idx index 09bd30991a..a355d2b38c 100644 --- a/resources/profiles/PrusaResearch.idx +++ b/resources/profiles/PrusaResearch.idx @@ -1,3 +1,5 @@ +min_slic3r_version = 2.6.2-alpha0 +1.11.0-alpha0 Binary g-code, arc fitting, QOI/PNG thumbnails, 90degree XL tower, XL specific filament variants. min_slic3r_version = 2.6.0-beta2 1.9.8 FW version notification (MK2.5/3 family). Minor update of MK4IS profiles. Updated MK4IS thumbnail. 1.9.7 MK4 Input Shaper RC firmware notification. diff --git a/resources/profiles/PrusaResearch.ini b/resources/profiles/PrusaResearch.ini index 93d05a216d..08fbdc7e6b 100644 --- a/resources/profiles/PrusaResearch.ini +++ b/resources/profiles/PrusaResearch.ini @@ -5,7 +5,7 @@ name = Prusa Research # Configuration version of this file. Config file will only be installed, if the config_version differs. # This means, the server may force the PrusaSlicer configuration to be downgraded. -config_version = 1.9.8 +config_version = 1.11.0-alpha0 # Where to get the updates from? config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaResearch/ changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1% @@ -33,6 +33,16 @@ bed_texture = mk4.svg thumbnail = MK4IS_thumbnail_v2.png default_materials = Prusament PLA @PGIS; Prusament PLA Blend @PGIS; Prusament PETG @PGIS; Generic PLA @PGIS; Prusa PLA @PGIS; Prusa PETG @PGIS; Generic ASA @MK4; Generic PETG @PGIS; Prusa PLA @PGIS; Generic PLA Silk @PGIS +; [printer_model:MK3.9] +; name = Original Prusa MK3.9 +; variants = 0.4; 0.25; 0.3; 0.5; 0.6; 0.8 +; technology = FFF +; family = MK3.9 +; bed_model = mk4_bed.stl +; bed_texture = mk4.svg +; thumbnail = MK4IS_thumbnail_v2.png +; default_materials = Prusament PLA @PGIS; Prusament PLA Blend @PGIS; Prusament PETG @PGIS; Generic PLA @PGIS; Prusa PLA @PGIS; Prusa PETG @PGIS; Generic ASA @MK4; Generic PETG @PGIS; Prusa PLA @PGIS; Generic PLA Silk @PGIS + [printer_model:MINI] name = Original Prusa MINI && MINI+ variants = 0.4; 0.25; 0.6; 0.8 @@ -94,7 +104,7 @@ technology = FFF family = XL Single-Tool bed_model = xl_bed.stl bed_texture = xl.svg -default_materials = Generic PLA @PG 0.6; Generic ABS @PG 0.6; Generic PETG @PG 0.6; Prusament PLA @PG 0.6; Prusament PETG @PG 0.6; Prusament ASA @PG 0.6; Prusament PC Blend @PG 0.6; Prusament PC Blend Carbon Fiber @PG 0.6; Prusament PVB @PG 0.6; Prusament PA11 Carbon Fiber @PG 0.6 +default_materials = Generic PLA @XL 0.6; Generic ABS @XL 0.6; Generic PETG @XL 0.6; Prusament PLA @XL 0.6; Prusament PETG @XL 0.6; Prusament ASA @XL 0.6; Prusament PC Blend @XL 0.6; Prusament PC Blend Carbon Fiber @XL 0.6; Prusament PVB @XL 0.6; Prusament PA11 Carbon Fiber @XL 0.6 [printer_model:XL2] name = Original Prusa XL - 2T @@ -103,7 +113,7 @@ technology = FFF family = XL Multi-Tool bed_model = xl_bed.stl bed_texture = xl.svg -default_materials = Generic PLA @PG 0.6; Generic ABS @PG 0.6; Generic PETG @PG 0.6; Prusament PLA @PG 0.6; Prusament PETG @PG 0.6; Prusament ASA @PG 0.6; Prusament PC Blend @PG 0.6; Prusament PC Blend Carbon Fiber @PG 0.6; Prusament PVB @PG 0.6; Prusament PA11 Carbon Fiber @PG 0.6; Verbatim BVOH @PG 0.6 +default_materials = Generic PLA @XL 0.6; Generic ABS @XL 0.6; Generic PETG @XL 0.6; Prusament PLA @XL 0.6; Prusament PETG @XL 0.6; Prusament ASA @XL 0.6; Prusament PC Blend @XL 0.6; Prusament PC Blend Carbon Fiber @XL 0.6; Prusament PVB @XL 0.6; Prusament PA11 Carbon Fiber @XL 0.6; Verbatim BVOH @XL 0.6 [printer_model:XL5] name = Original Prusa XL - 5T @@ -112,7 +122,7 @@ technology = FFF family = XL Multi-Tool bed_model = xl_bed.stl bed_texture = xl.svg -default_materials = Generic PLA @PG 0.6; Generic ABS @PG 0.6; Generic PETG @PG 0.6; Prusament PLA @PG 0.6; Prusament PETG @PG 0.6; Prusament ASA @PG 0.6; Prusament PC Blend @PG 0.6; Prusament PC Blend Carbon Fiber @PG 0.6; Prusament PVB @PG 0.6; Prusament PA11 Carbon Fiber @PG 0.6; Verbatim BVOH @PG 0.6 +default_materials = Generic PLA @XL 0.6; Generic ABS @XL 0.6; Generic PETG @XL 0.6; Prusament PLA @XL 0.6; Prusament PETG @XL 0.6; Prusament ASA @XL 0.6; Prusament PC Blend @XL 0.6; Prusament PC Blend Carbon Fiber @XL 0.6; Prusament PVB @XL 0.6; Prusament PA11 Carbon Fiber @XL 0.6; Verbatim BVOH @XL 0.6 [printer_model:MK2.5S] name = Original Prusa i3 MK2.5S @@ -238,7 +248,7 @@ notes = overhangs = 1 only_retract_when_crossing_perimeters = 0 ooze_prevention = 0 -output_filename_format = {input_filename_base}_{nozzle_diameter[initial_tool]}n_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.gcode +output_filename_format = {input_filename_base}_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.gcode perimeters = 2 perimeter_extruder = 1 perimeter_extrusion_width = 0.45 @@ -332,14 +342,17 @@ first_layer_speed = 25 support_material_threshold = 45 raft_first_layer_density = 80% ## gcode_substitutions = "; stop printing object\\s(.*)\\s+id:(\\d+)\\s+.*";"$0\\nM486 S-1\\n";r;;"; printing object\\s(.*)\\s+id:(\\d+)\\s+.*";"$0\\nM486 S$2\\nM486 N$1\\n";r; -output_filename_format = {input_filename_base}_{nozzle_diameter[initial_tool]}n_{layer_height}mm_{printing_filament_types}_XL_{print_time}.gcode +output_filename_format = {input_filename_base}_{layer_height}mm_{printing_filament_types}_XL_{print_time}.bgcode wipe_tower_cone_angle = 25 +wipe_tower_rotation_angle = 90 wipe_tower = 1 wipe_tower_bridging = 8 wipe_tower_extra_spacing = 100 wipe_tower_brim_width = 3 ooze_prevention = 1 standby_temperature_delta = -110 +gcode_binary = 1 +arc_fitting = emit_radius [print:*MK4*] inherits = *common* @@ -363,6 +376,9 @@ first_layer_speed = 20 support_material_threshold = 45 raft_first_layer_density = 80% ## gcode_substitutions = "; stop printing object\\s(.*)\\s+id:(\\d+)\\s+.*";"$0\\nM486 S-1\\n";r;;"; printing object\\s(.*)\\s+id:(\\d+)\\s+.*";"$0\\nM486 S$2\\nM486 N$1\\n";r; +output_filename_format = {input_filename_base}_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.bgcode +gcode_binary = 1 +arc_fitting = emit_radius [print:*MK306*] inherits = *MK3* @@ -403,6 +419,7 @@ thick_bridges = 0 bridge_flow_ratio = 1 bridge_speed = 20 wipe_tower_bridging = 6 +output_filename_format = {input_filename_base}_0.25n_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.gcode [print:*0.25nozzleMK3*] inherits = *0.25nozzle* @@ -441,9 +458,11 @@ first_layer_acceleration = 500 infill_anchor = 1 perimeters = 3 brim_separation = 0 +output_filename_format = {input_filename_base}_0.25n_{layer_height}mm_{filament_type[0]}_XL_{print_time}.bgcode [print:*0.25nozzleMK4*] inherits = *0.25nozzleXL* +output_filename_format = {input_filename_base}_0.25n_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.bgcode [print:*0.3nozzle*] external_perimeter_extrusion_width = 0.33 @@ -457,6 +476,7 @@ support_material_extrusion_width = 0.3 fill_density = 20% perimeters = 3 infill_anchor = 1.5 +output_filename_format = {input_filename_base}_0.3n_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.bgcode [print:*0.5nozzle*] external_perimeter_extrusion_width = 0.55 @@ -476,6 +496,7 @@ infill_anchor = 2 infill_anchor_max = 15 thick_bridges = 0 bridge_speed = 30 +output_filename_format = {input_filename_base}_0.5n_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.bgcode [print:*0.6nozzle*] external_perimeter_extrusion_width = 0.61 @@ -497,6 +518,7 @@ bridge_flow_ratio = 0.95 bridge_speed = 25 infill_overlap = 15% support_tree_branch_diameter_double_wall = 0 +output_filename_format = {input_filename_base}_0.6n_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.gcode [print:*0.6nozzleMK3*] inherits = *0.6nozzle* @@ -514,9 +536,11 @@ thick_bridges = 0 fill_density = 20% support_material_interface_spacing = 0.25 infill_anchor = 2.5 +output_filename_format = {input_filename_base}_0.6n_{layer_height}mm_{filament_type[0]}_XL_{print_time}.bgcode [print:*0.6nozzleMK4*] inherits = *0.6nozzleXL* +output_filename_format = {input_filename_base}_0.6n_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.bgcode [print:*0.6nozzleMINI*] inherits = *0.6nozzleMK3* @@ -573,6 +597,7 @@ single_extruder_multi_material_priming = 0 thick_bridges = 1 overhangs = 0 support_tree_branch_diameter_double_wall = 0 +output_filename_format = {input_filename_base}_0.8n_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.gcode [print:*0.8nozzleXL*] inherits = *0.8nozzle* @@ -587,12 +612,14 @@ raft_first_layer_expansion = 2 default_acceleration = 1250 infill_anchor = 2.5 first_layer_acceleration = 500 +output_filename_format = {input_filename_base}_0.8n_{layer_height}mm_{filament_type[0]}_XL_{print_time}.bgcode [print:*0.8nozzleMK4*] inherits = *0.8nozzleXL* default_acceleration = 1000 infill_acceleration = 2000 first_layer_acceleration = 600 +output_filename_format = {input_filename_base}_0.8n_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.bgcode [print:*soluble_support*] overhangs = 1 @@ -3730,7 +3757,7 @@ min_skirt_length = 4 mmu_segmented_region_max_width = 0 only_retract_when_crossing_perimeters = 0 ooze_prevention = 0 -output_filename_format = {input_filename_base}_{nozzle_diameter[0]}n_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.gcode +output_filename_format = {input_filename_base}_0.4n_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.bgcode overhang_speed_0 = 15 overhang_speed_1 = 25 overhang_speed_2 = 30 @@ -3800,7 +3827,9 @@ travel_speed = 300 travel_speed_z = 12 thick_bridges = 0 thin_walls = 0 -compatible_printers_condition = printer_model=="MK4IS" and nozzle_diameter[0]==0.4 +compatible_printers_condition = printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.4 +gcode_binary = 1 +arc_fitting = emit_radius [print:*MK4IS_common025*] inherits = *MK4IS_common* @@ -3840,7 +3869,8 @@ raft_first_layer_density = 95% gap_fill_speed = 50 single_extruder_multi_material_priming = 0 wipe_tower = 1 -compatible_printers_condition = printer_model=="MK4IS" and nozzle_diameter[0]==0.25 +output_filename_format = {input_filename_base}_0.25n_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.bgcode +compatible_printers_condition = printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.25 [print:*MK4IS_common03*] inherits = *MK4IS_common* @@ -3875,7 +3905,8 @@ raft_first_layer_density = 90% gap_fill_speed = 50 top_solid_min_thickness = 0.7 bottom_solid_min_thickness = 0.5 -compatible_printers_condition = printer_model=="MK4IS" and nozzle_diameter[0]==0.3 +output_filename_format = {input_filename_base}_0.3n_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.bgcode +compatible_printers_condition = printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.3 [print:*MK4IS_common05*] inherits = *MK4IS_common* @@ -3902,7 +3933,8 @@ infill_acceleration = 4000 default_acceleration = 2500 infill_anchor = 2 infill_anchor_max = 15 -compatible_printers_condition = printer_model=="MK4IS" and nozzle_diameter[0]==0.5 +output_filename_format = {input_filename_base}_0.5n_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.bgcode +compatible_printers_condition = printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.5 [print:*MK4IS_common06*] inherits = *MK4IS_common* @@ -3923,7 +3955,8 @@ support_material_interface_spacing = 0.25 support_material_interface_speed = 75% raft_contact_distance = 0.25 gap_fill_speed = 70 -compatible_printers_condition = printer_model=="MK4IS" and nozzle_diameter[0]==0.6 +output_filename_format = {input_filename_base}_0.6n_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.bgcode +compatible_printers_condition = printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.6 overhang_speed_0 = 15 overhang_speed_1 = 20 overhang_speed_2 = 25 @@ -3956,7 +3989,8 @@ raft_contact_distance = 0.2 gap_fill_speed = 40 top_solid_min_thickness = 1.2 bottom_solid_min_thickness = 0.8 -compatible_printers_condition = printer_model=="MK4IS" and nozzle_diameter[0]==0.8 +output_filename_format = {input_filename_base}_0.8n_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.bgcode +compatible_printers_condition = printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.8 seam_position = nearest infill_anchor = 2.5 infill_anchor_max = 20 @@ -4633,7 +4667,7 @@ compatible_printers_condition = ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ a [filament:*PLAPG*] start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.4}0.05{elsif nozzle_diameter[0]==0.25}0.14{elsif nozzle_diameter[0]==0.3}0.07{elsif nozzle_diameter[0]==0.35}0.06{elsif nozzle_diameter[0]==0.6}0.03{elsif nozzle_diameter[0]==0.5}0.035{elsif nozzle_diameter[0]==0.8}0.015{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*PRINTER_MODEL_MK4IS.*/}\nM572 S{if nozzle_diameter[0]==0.4}0.038{elsif nozzle_diameter[0]==0.5}0.025{elsif nozzle_diameter[0]==0.6}0.02{elsif nozzle_diameter[0]==0.8}0.014{elsif nozzle_diameter[0]==0.25}0.12{elsif nozzle_diameter[0]==0.3}0.08{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S36 ; set heatbreak target temp" -compatible_printers_condition = printer_notes=~/.*PG.*/ and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 slowdown_below_layer_time = 8 filament_cooling_final_speed = 2 filament_cooling_initial_speed = 3 @@ -4651,16 +4685,32 @@ full_fan_speed_layer = 3 [filament:*PLA06PG*] inherits = *PLAPG* -compatible_printers_condition = printer_notes=~/.*PG.*/ and nozzle_diameter[0]==0.6 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and nozzle_diameter[0]==0.6 filament_max_volumetric_speed = 15.5 slowdown_below_layer_time = 14 [filament:*PLA08PG*] inherits = *PLAPG* -compatible_printers_condition = printer_notes=~/.*PG.*/ and nozzle_diameter[0]==0.8 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and nozzle_diameter[0]==0.8 filament_max_volumetric_speed = 19 slowdown_below_layer_time = 18 +[filament:*PLAXL*] +compatible_printers_condition = printer_notes=~/.*XL.*/ and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 +filament_multitool_ramming = 0 +filament_multitool_ramming_volume = 5 +filament_multitool_ramming_flow = 40 +filament_minimal_purge_on_wipe_tower = 15 +filament_retract_length_toolchange = nil + +[filament:*PLA06XL*] +inherits = *PLAXL* +compatible_printers_condition = printer_notes=~/.*XL.*/ and nozzle_diameter[0]==0.6 + +[filament:*PLA08XL*] +inherits = *PLAXL* +compatible_printers_condition = printer_notes=~/.*XL.*/ and nozzle_diameter[0]==0.8 + [filament:*PET*] inherits = *common* bed_temperature = 90 @@ -4688,7 +4738,7 @@ compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_model!="MK2S filament_max_volumetric_speed = 15 [filament:*PETPG*] -compatible_printers_condition = printer_notes=~/.*PG.*/ and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 filament_max_volumetric_speed = 10 start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.4}0.07{elsif nozzle_diameter[0]==0.25}0.12{elsif nozzle_diameter[0]==0.3}0.09{elsif nozzle_diameter[0]==0.35}0.08{elsif nozzle_diameter[0]==0.6}0.04{elsif nozzle_diameter[0]==0.5}0.05{elsif nozzle_diameter[0]==0.8}0.02{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*PRINTER_MODEL_MK4IS.*/}\nM572 S{if nozzle_diameter[0]==0.4}0.055{elsif nozzle_diameter[0]==0.5}0.042{elsif nozzle_diameter[0]==0.6}0.025{elsif nozzle_diameter[0]==0.8}0.018{elsif nozzle_diameter[0]==0.25}0.18{elsif nozzle_diameter[0]==0.3}0.1{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S36 ; set heatbreak target temp" filament_cooling_final_speed = 1 @@ -4710,23 +4760,42 @@ slowdown_below_layer_time = 9 [filament:*PET06PG*] inherits = *PETPG* -compatible_printers_condition = printer_notes=~/.*PG.*/ and nozzle_diameter[0]==0.6 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and nozzle_diameter[0]==0.6 filament_max_volumetric_speed = 17 slowdown_below_layer_time = 14 filament_retract_length = 0.9 [filament:*PET08PG*] inherits = *PETPG* -compatible_printers_condition = printer_notes=~/.*PG.*/ and nozzle_diameter[0]==0.8 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and nozzle_diameter[0]==0.8 filament_max_volumetric_speed = 22 slowdown_below_layer_time = 18 filament_retract_length = 0.8 +[filament:*PETXL*] +compatible_printers_condition = printer_notes=~/.*XL.*/ and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 +filament_multitool_ramming = 1 +filament_multitool_ramming_volume = 5 +filament_multitool_ramming_flow = 40 +filament_minimal_purge_on_wipe_tower = 35 +filament_retract_length_toolchange = 20 + +[filament:*PET06XL*] +inherits = *PETXL* +compatible_printers_condition = printer_notes=~/.*XL.*/ and nozzle_diameter[0]==0.6 + +[filament:*PET08XL*] +inherits = *PETXL* +compatible_printers_condition = printer_notes=~/.*XL.*/ and nozzle_diameter[0]==0.8 + [filament:*04PLUS*] compatible_printers_condition = nozzle_diameter[0]>=0.4 and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) and printer_notes!~/.*PG.*/ [filament:*04PLUSPG*] -compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ +compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*MK4.*/ + +[filament:*04PLUSXL*] +compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*XL.*/ [filament:*PETMMU1*] filament_retract_length = nil @@ -4837,7 +4906,7 @@ start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and no compatible_printers_condition = printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) [filament:*ABSPG*] -compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 +compatible_printers_condition = printer_model=~/.*MK4.*/ and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 filament_max_volumetric_speed = 12 start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.4}0.04{elsif nozzle_diameter[0]==0.25}0.1{elsif nozzle_diameter[0]==0.3}0.06{elsif nozzle_diameter[0]==0.35}0.05{elsif nozzle_diameter[0]==0.5}0.03{elsif nozzle_diameter[0]==0.6}0.02{elsif nozzle_diameter[0]==0.8}0.01{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*PRINTER_MODEL_MK4IS.*/}\nM572 S{if nozzle_diameter[0]==0.4}0.02{elsif nozzle_diameter[0]==0.5}0.018{elsif nozzle_diameter[0]==0.6}0.012{elsif nozzle_diameter[0]==0.8}0.01{elsif nozzle_diameter[0]==0.25}0.09{elsif nozzle_diameter[0]==0.3}0.065{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S40 ; set heatbreak target temp" filament_cooling_final_speed = 50 @@ -4855,97 +4924,105 @@ idle_temperature = 100 [filament:*ABS06PG*] inherits = *ABSPG* filament_max_volumetric_speed = 15 -compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.6 +compatible_printers_condition = printer_model=~/.*MK4.*/ and nozzle_diameter[0]==0.6 [filament:*ABS08PG*] inherits = *ABSPG* filament_max_volumetric_speed = 18 -compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.8 +compatible_printers_condition = printer_model=~/.*MK4.*/ and nozzle_diameter[0]==0.8 slowdown_below_layer_time = 25 -[filament:*ABSMK4*] -inherits = *ABSPG* -compatible_printers_condition = printer_model=~/(MK4|MK4IS)/ and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 +[filament:*ABSXL*] +compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 +filament_multitool_ramming = 1 +filament_multitool_ramming_volume = 5 +filament_multitool_ramming_flow = 40 +filament_minimal_purge_on_wipe_tower = 35 +filament_retract_length_toolchange = 20 -[filament:*ABS06MK4*] -inherits = *ABSMK4* -filament_max_volumetric_speed = 15 -compatible_printers_condition = printer_model=~/(MK4|MK4IS)/ and nozzle_diameter[0]==0.6 +[filament:*ABS06XL*] +inherits = *ABSXL* +compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.6 -[filament:*ABS08MK4*] -inherits = *ABSMK4* +[filament:*ABS08XL*] +inherits = *ABSXL* filament_max_volumetric_speed = 18 -compatible_printers_condition = printer_model=~/(MK4|MK4IS)/ and nozzle_diameter[0]==0.8 +compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.8 slowdown_below_layer_time = 25 [filament:*PCPG*] inherits = *ABSPG* filament_max_volumetric_speed = 8 start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.4}0.07{elsif nozzle_diameter[0]==0.3}0.09{elsif nozzle_diameter[0]==0.35}0.08{elsif nozzle_diameter[0]==0.6}0.04{elsif nozzle_diameter[0]==0.5}0.05{elsif nozzle_diameter[0]==0.8}0.02{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*PRINTER_MODEL_MK4IS.*/}\nM572 S{if nozzle_diameter[0]==0.4}0.05{elsif nozzle_diameter[0]==0.5}0.035{elsif nozzle_diameter[0]==0.6}0.022{elsif nozzle_diameter[0]==0.8}0.016{elsif nozzle_diameter[0]==0.25}0.14{elsif nozzle_diameter[0]==0.3}0.09{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S45 ; set heatbreak target temp" -first_layer_bed_temperature = 100 -bed_temperature = 105 -idle_temperature = 150 filament_minimal_purge_on_wipe_tower = 35 +compatible_printers_condition = printer_model=~/.*MK4.*/ and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 [filament:*PC06PG*] inherits = *PCPG* filament_max_volumetric_speed = 14 -compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.6 +compatible_printers_condition = printer_model=~/.*MK4.*/ and nozzle_diameter[0]==0.6 [filament:*PC08PG*] inherits = *PCPG* filament_max_volumetric_speed = 18 -compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.8 +compatible_printers_condition = printer_model=~/.*MK4.*/ and nozzle_diameter[0]==0.8 -[filament:*PCMK4*] -inherits = *ABSMK4* -filament_max_volumetric_speed = 8 -start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.4}0.07{elsif nozzle_diameter[0]==0.3}0.09{elsif nozzle_diameter[0]==0.35}0.08{elsif nozzle_diameter[0]==0.6}0.04{elsif nozzle_diameter[0]==0.5}0.05{elsif nozzle_diameter[0]==0.8}0.02{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*PRINTER_MODEL_MK4IS.*/}\nM572 S{if nozzle_diameter[0]==0.4}0.05{elsif nozzle_diameter[0]==0.5}0.035{elsif nozzle_diameter[0]==0.6}0.022{elsif nozzle_diameter[0]==0.8}0.016{elsif nozzle_diameter[0]==0.25}0.14{elsif nozzle_diameter[0]==0.3}0.09{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S45 ; set heatbreak target temp" +[filament:*PCXL*] +compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 idle_temperature = 150 +first_layer_bed_temperature = 100 +bed_temperature = 105 +filament_multitool_ramming = 1 +filament_multitool_ramming_volume = 5 +filament_multitool_ramming_flow = 40 +filament_minimal_purge_on_wipe_tower = 35 +filament_retract_length_toolchange = 20 -[filament:*PC06MK4*] -inherits = *PCMK4* -filament_max_volumetric_speed = 14 -compatible_printers_condition = printer_model=~/(MK4|MK4IS)/ and nozzle_diameter[0]==0.6 +[filament:*PC06XL*] +inherits = *PCXL* +compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.6 -[filament:*PC08MK4*] -inherits = *PCMK4* -filament_max_volumetric_speed = 18 -compatible_printers_condition = printer_model=~/(MK4|MK4IS)/ and nozzle_diameter[0]==0.8 +[filament:*PC08XL*] +inherits = *PCXL* +compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.8 [filament:*PAPG*] inherits = *ABSPG* filament_max_volumetric_speed = 5 start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.4}0.07{elsif nozzle_diameter[0]==0.3}0.09{elsif nozzle_diameter[0]==0.35}0.08{elsif nozzle_diameter[0]==0.6}0.04{elsif nozzle_diameter[0]==0.5}0.05{elsif nozzle_diameter[0]==0.8}0.02{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*PRINTER_MODEL_MK4IS.*/}\nM572 S{if nozzle_diameter[0]==0.4}0.05{elsif nozzle_diameter[0]==0.5}0.035{elsif nozzle_diameter[0]==0.6}0.022{elsif nozzle_diameter[0]==0.8}0.016{elsif nozzle_diameter[0]==0.25}0.14{elsif nozzle_diameter[0]==0.3}0.09{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S45 ; set heatbreak target temp" -bed_temperature = 105 idle_temperature = 150 filament_minimal_purge_on_wipe_tower = 35 +compatible_printers_condition = printer_model=~/.*MK4.*/ and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 [filament:*PA06PG*] inherits = *PAPG* filament_max_volumetric_speed = 7 -compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.6 +compatible_printers_condition = printer_model=~/.*MK4.*/ and nozzle_diameter[0]==0.6 [filament:*PA08PG*] inherits = *PAPG* filament_max_volumetric_speed = 10 -compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.8 +compatible_printers_condition = printer_model=~/.*MK4.*/ and nozzle_diameter[0]==0.8 -[filament:*PAMK4*] -inherits = *ABSMK4* -filament_max_volumetric_speed = 5 -start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.4}0.07{elsif nozzle_diameter[0]==0.3}0.09{elsif nozzle_diameter[0]==0.35}0.08{elsif nozzle_diameter[0]==0.6}0.04{elsif nozzle_diameter[0]==0.5}0.05{elsif nozzle_diameter[0]==0.8}0.02{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*PRINTER_MODEL_MK4IS.*/}\nM572 S{if nozzle_diameter[0]==0.4}0.05{elsif nozzle_diameter[0]==0.5}0.035{elsif nozzle_diameter[0]==0.6}0.022{elsif nozzle_diameter[0]==0.8}0.016{elsif nozzle_diameter[0]==0.25}0.14{elsif nozzle_diameter[0]==0.3}0.09{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S45 ; set heatbreak target temp" +[filament:*PAXL*] +compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 idle_temperature = 70 +bed_temperature = 105 +filament_multitool_ramming = 1 +filament_multitool_ramming_volume = 5 +filament_multitool_ramming_flow = 40 +filament_minimal_purge_on_wipe_tower = 35 +filament_retract_length_toolchange = 20 -[filament:*PA06MK4*] -inherits = *PAMK4* +[filament:*PA06XL*] +inherits = *PAXL* filament_max_volumetric_speed = 7 -compatible_printers_condition = printer_model=~/(MK4|MK4IS)/ and nozzle_diameter[0]==0.6 +compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.6 -[filament:*PA08MK4*] -inherits = *PAMK4* +[filament:*PA08XL*] +inherits = *PAXL* filament_max_volumetric_speed = 10 -compatible_printers_condition = printer_model=~/(MK4|MK4IS)/ and nozzle_diameter[0]==0.8 +compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.8 [filament:*FLEX*] inherits = *common* @@ -4973,6 +5050,25 @@ filament_retract_lift = 0 filament_wipe = 0 [filament:*FLEXPG*] +filament_max_volumetric_speed = 4 +filament_retract_speed = 60 +filament_deretract_speed = 20 +filament_retract_before_travel = 2 +compatible_printers_condition = printer_model=~/.*MK4.*/ and nozzle_diameter[0]>=0.3 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 +idle_temperature = 70 +start_filament_gcode = "M900 K0 ; Filament gcode\n\nM142 S36 ; set heatbreak target temp" + +[filament:*FLEX06PG*] +inherits = *FLEXPG* +filament_max_volumetric_speed = 6.5 +compatible_printers_condition = printer_model=~/.*MK4.*/ and nozzle_diameter[0]==0.6 + +[filament:*FLEX08PG*] +inherits = *FLEXPG* +filament_max_volumetric_speed = 9 +compatible_printers_condition = printer_model=~/.*MK4.*/ and nozzle_diameter[0]==0.8 + +[filament:*FLEXXL*] filament_max_volumetric_speed = 3.5 filament_retract_speed = 60 filament_deretract_speed = 20 @@ -4980,37 +5076,24 @@ filament_retract_before_travel = 2 compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]>=0.3 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 idle_temperature = 70 start_filament_gcode = "M900 K0 ; Filament gcode\n\nM142 S36 ; set heatbreak target temp" +filament_multitool_ramming = 0 +filament_multitool_ramming_volume = 5 +filament_multitool_ramming_flow = 3.5 filament_minimal_purge_on_wipe_tower = 35 +filament_retract_length_toolchange = 5 -[filament:*FLEX06PG*] -inherits = *FLEXPG* +[filament:*FLEX06XL*] +inherits = *FLEXXL* filament_max_volumetric_speed = 4.5 +filament_multitool_ramming_flow = 4.5 compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.6 -[filament:*FLEX08PG*] -inherits = *FLEXPG* +[filament:*FLEX08XL*] +inherits = *FLEXXL* filament_max_volumetric_speed = 8 +filament_multitool_ramming_flow = 8 compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.8 -[filament:*FLEXMK4*] -filament_max_volumetric_speed = 4 -filament_retract_speed = 60 -filament_deretract_speed = 20 -filament_retract_before_travel = 2 -compatible_printers_condition = printer_model=~/(MK4|MK4IS)/ and nozzle_diameter[0]>=0.3 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 -idle_temperature = 70 -start_filament_gcode = "M900 K0 ; Filament gcode\n\nM142 S36 ; set heatbreak target temp" - -[filament:*FLEX06MK4*] -inherits = *FLEXMK4* -filament_max_volumetric_speed = 6.5 -compatible_printers_condition = printer_model=~/(MK4|MK4IS)/ and nozzle_diameter[0]==0.6 - -[filament:*FLEX08MK4*] -inherits = *FLEXMK4* -filament_max_volumetric_speed = 9 -compatible_printers_condition = printer_model=~/(MK4|MK4IS)/ and nozzle_diameter[0]==0.8 - [filament:ColorFabb bronzeFill] inherits = *PLA*; *04PLUS* filament_vendor = ColorFabb @@ -5035,6 +5118,15 @@ inherits = ColorFabb bronzeFill; *PLA08PG* filament_max_volumetric_speed = 17 extrusion_multiplier = 1.05 +[filament:ColorFabb bronzeFill @XL] +inherits = ColorFabb bronzeFill @PG; *PLAXL*; *04PLUSXL* + +[filament:ColorFabb bronzeFill @XL 0.6] +inherits = ColorFabb bronzeFill @PG 0.6; *PLA06XL* + +[filament:ColorFabb bronzeFill @XL 0.8] +inherits = ColorFabb bronzeFill @PG 0.8; *PLA08XL* + [filament:ColorFabb steelFill] inherits = ColorFabb bronzeFill extrusion_multiplier = 1.15 @@ -5056,6 +5148,15 @@ inherits = ColorFabb steelFill; *PLA08PG* filament_max_volumetric_speed = 17 extrusion_multiplier = 1.05 +[filament:ColorFabb steelFill @XL] +inherits = ColorFabb steelFill @PG; *PLAXL*; *04PLUSXL* + +[filament:ColorFabb steelFill @XL 0.6] +inherits = ColorFabb steelFill @PG 0.6; *PLA06XL* + +[filament:ColorFabb steelFill @XL 0.8] +inherits = ColorFabb steelFill @PG 0.8; *PLA08XL* + [filament:ColorFabb copperFill] inherits = ColorFabb bronzeFill extrusion_multiplier = 1.15 @@ -5075,6 +5176,15 @@ inherits = ColorFabb copperFill; *PLA08PG* filament_max_volumetric_speed = 17 extrusion_multiplier = 1.05 +[filament:ColorFabb copperFill @XL] +inherits = ColorFabb copperFill @PG; *PLAXL*; *04PLUSXL* + +[filament:ColorFabb copperFill @XL 0.6] +inherits = ColorFabb copperFill @PG 0.6; *PLA06XL* + +[filament:ColorFabb copperFill @XL 0.8] +inherits = ColorFabb copperFill @PG 0.8; *PLA08XL* + [filament:ColorFabb HT] inherits = *PET* filament_vendor = ColorFabb @@ -5097,28 +5207,28 @@ temperature = 270 inherits = ColorFabb HT; *PETPG* first_layer_bed_temperature = 100 bed_temperature = 105 -compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 +compatible_printers_condition = printer_model=~/.*MK4.*/ and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 [filament:ColorFabb HT @PG 0.6] inherits = ColorFabb HT @PG; *PET06PG* -compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.6 +compatible_printers_condition = printer_model=~/.*MK4.*/ and nozzle_diameter[0]==0.6 [filament:ColorFabb HT @PG 0.8] inherits = ColorFabb HT @PG; *PET08PG* +compatible_printers_condition = printer_model=~/.*MK4.*/ and nozzle_diameter[0]==0.8 + +[filament:ColorFabb HT @XL] +inherits = ColorFabb HT @PG; *PETXL* +compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 + +[filament:ColorFabb HT @XL 0.6] +inherits = ColorFabb HT @PG 0.6; *PET06XL* +compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.6 + +[filament:ColorFabb HT @XL 0.8] +inherits = ColorFabb HT @PG 0.8; *PET08XL* compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.8 -[filament:ColorFabb HT @MK4] -inherits = ColorFabb HT; *PETPG* -compatible_printers_condition = printer_model=~/(MK4|MK4IS)/ and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 - -[filament:ColorFabb HT @MK4 0.6] -inherits = ColorFabb HT @MK4; *PET06PG* -compatible_printers_condition = printer_model=~/(MK4|MK4IS)/ and nozzle_diameter[0]==0.6 - -[filament:ColorFabb HT @MK4 0.8] -inherits = ColorFabb HT @MK4; *PET08PG* -compatible_printers_condition = printer_model=~/(MK4|MK4IS)/ and nozzle_diameter[0]==0.8 - [filament:ColorFabb PLA-PHA] inherits = *PLA* filament_vendor = ColorFabb @@ -5135,6 +5245,15 @@ inherits = ColorFabb PLA-PHA; *PLA06PG* [filament:ColorFabb PLA-PHA @PG 0.8] inherits = ColorFabb PLA-PHA; *PLA08PG* +[filament:ColorFabb PLA-PHA @XL] +inherits = ColorFabb PLA-PHA @PG; *PLAXL* + +[filament:ColorFabb PLA-PHA @XL 0.6] +inherits = ColorFabb PLA-PHA @PG 0.6; *PLA06XL* + +[filament:ColorFabb PLA-PHA @XL 0.8] +inherits = ColorFabb PLA-PHA @PG 0.8; *PLA08XL* + [filament:ColorFabb woodFill] inherits = *PLA*; *04PLUS* filament_vendor = ColorFabb @@ -5162,6 +5281,15 @@ inherits = ColorFabb woodFill; *PLA08PG* filament_max_volumetric_speed = 17 extrusion_multiplier = 1 +[filament:ColorFabb woodFill @XL] +inherits = ColorFabb woodFill @PG; *PLAXL*; *04PLUSXL* + +[filament:ColorFabb woodFill @XL 0.6] +inherits = ColorFabb woodFill @PG 0.6; *PLA06XL* + +[filament:ColorFabb woodFill @XL 0.8] +inherits = ColorFabb woodFill @PG 0.8; *PLA08XL* + [filament:ColorFabb corkFill] inherits = *PLA*; *04PLUS* filament_vendor = ColorFabb @@ -5189,6 +5317,15 @@ inherits = ColorFabb corkFill; *PLA08PG* filament_max_volumetric_speed = 17 extrusion_multiplier = 1.05 +[filament:ColorFabb corkFill @XL] +inherits = ColorFabb corkFill @PG; *PLAXL*; *04PLUSXL* + +[filament:ColorFabb corkFill @XL 0.6] +inherits = ColorFabb corkFill @PG 0.6; *PLA06XL* + +[filament:ColorFabb corkFill @XL 0.8] +inherits = ColorFabb corkFill @PG 0.8; *PLA08XL* + [filament:ColorFabb XT] inherits = *PET* filament_vendor = ColorFabb @@ -5208,6 +5345,15 @@ inherits = ColorFabb XT; *PET06PG* [filament:ColorFabb XT @PG 0.8] inherits = ColorFabb XT; *PET08PG* +[filament:ColorFabb XT @XL] +inherits = ColorFabb XT @PG; *PETXL* + +[filament:ColorFabb XT @XL 0.6] +inherits = ColorFabb XT @PG 0.6; *PET06XL* + +[filament:ColorFabb XT @XL 0.8] +inherits = ColorFabb XT @PG 0.8; *PET08XL* + [filament:ColorFabb XT-CF20] inherits = *PET* filament_vendor = ColorFabb @@ -5237,6 +5383,15 @@ filament_max_volumetric_speed = 6 inherits = ColorFabb XT-CF20 @PG; *PET08PG* filament_max_volumetric_speed = 9 +[filament:ColorFabb XT-CF20 @XL] +inherits = ColorFabb XT-CF20 @PG; *PETXL*; *04PLUSXL* + +[filament:ColorFabb XT-CF20 @XL 0.6] +inherits = ColorFabb XT-CF20 @PG 0.6; *PET06XL* + +[filament:ColorFabb XT-CF20 @XL 0.8] +inherits = ColorFabb XT-CF20 @PG 0.8; *PET08XL* + [filament:ColorFabb nGen] inherits = *PET* filament_vendor = ColorFabb @@ -5255,10 +5410,19 @@ min_fan_speed = 20 inherits = ColorFabb nGen; *PETPG* [filament:ColorFabb nGen @PG 0.6] -inherits = ColorFabb nGen; *PET06PG* +inherits = ColorFabb nGen @PG; *PET06PG* [filament:ColorFabb nGen @PG 0.8] -inherits = ColorFabb nGen; *PET08PG* +inherits = ColorFabb nGen @PG; *PET08PG* + +[filament:ColorFabb nGen @XL] +inherits = ColorFabb nGen @PG; *PETPG* + +[filament:ColorFabb nGen @XL 0.6] +inherits = ColorFabb nGen @PG 0.6; *PET06XL* + +[filament:ColorFabb nGen @XL 0.8] +inherits = ColorFabb nGen @PG 0.8; *PET08XL* [filament:ColorFabb nGen flex] inherits = *FLEX* @@ -5282,30 +5446,37 @@ filament_retract_length = nil filament_retract_lift = 0 compatible_printers_condition = nozzle_diameter[0]>0.35 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and num_extruders==1 && ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK3.*/ and single_extruder_multi_material) -[filament:ColorFabb nGen flex @PG] -inherits = ColorFabb nGen flex; *FLEXPG* +[filament:ColorFabb nGen flex @XL] +inherits = ColorFabb nGen flex; *FLEXXL* +renamed_from = "ColorFabb nGen flex @PG" filament_max_volumetric_speed = 6 filament_retract_length = 2.5 +filament_multitool_ramming_flow = 6 +filament_retract_length_toolchange = 2.5 -[filament:ColorFabb nGen flex @PG 0.6] -inherits = ColorFabb nGen flex; *FLEX06PG* +[filament:ColorFabb nGen flex @XL 0.6] +inherits = ColorFabb nGen flex; *FLEX06XL* +renamed_from = "ColorFabb nGen flex @PG 0.6" filament_max_volumetric_speed = 7 +filament_multitool_ramming_flow = 7 -[filament:ColorFabb nGen flex @PG 0.8] -inherits = ColorFabb nGen flex; *FLEX08PG* +[filament:ColorFabb nGen flex @XL 0.8] +inherits = ColorFabb nGen flex; *FLEX08XL* +renamed_from = "ColorFabb nGen flex @PG 0.8" filament_max_volumetric_speed = 10 +filament_multitool_ramming_flow = 10 [filament:ColorFabb nGen flex @MK4] -inherits = ColorFabb nGen flex; *FLEXMK4* +inherits = ColorFabb nGen flex; *FLEXPG* filament_max_volumetric_speed = 7 filament_retract_length = 2.5 [filament:ColorFabb nGen flex @MK4 0.6] -inherits = ColorFabb nGen flex; *FLEX06MK4* +inherits = ColorFabb nGen flex; *FLEX06PG* filament_max_volumetric_speed = 9 [filament:ColorFabb nGen flex @MK4 0.8] -inherits = ColorFabb nGen flex; *FLEX08MK4* +inherits = ColorFabb nGen flex; *FLEX08PG* filament_max_volumetric_speed = 12 [filament:Kimya PETG Carbon] @@ -5335,6 +5506,15 @@ filament_max_volumetric_speed = 9 inherits = Kimya PETG Carbon @PG; *PET08PG* filament_max_volumetric_speed = 14 +[filament:Kimya PETG Carbon @XL] +inherits = Kimya PETG Carbon @PG; *PETXL*; *04PLUSXL* + +[filament:Kimya PETG Carbon @XL 0.6] +inherits = Kimya PETG Carbon @PG 0.6; *PET06XL* + +[filament:Kimya PETG Carbon @XL 0.8] +inherits = Kimya PETG Carbon @PG 0.8; *PET08XL* + [filament:Kimya ABS Carbon] inherits = *ABSC* filament_vendor = Kimya @@ -5346,31 +5526,32 @@ first_layer_temperature = 260 temperature = 260 compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) -[filament:Kimya ABS Carbon @PG] -inherits = Kimya ABS Carbon; *ABSPG*; *04PLUSPG* +[filament:Kimya ABS Carbon @XL] +inherits = Kimya ABS Carbon; *ABSPG*; *ABSXL*; *04PLUSXL* +renamed_from = "Kimya ABS Carbon @PG" bed_temperature = 105 filament_max_volumetric_speed = 6 -compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_model=~/.*XL.*/ -[filament:Kimya ABS Carbon @PG 0.6] -inherits = Kimya ABS Carbon @PG; *ABS06PG* +[filament:Kimya ABS Carbon @XL 0.6] +inherits = Kimya ABS Carbon @XL; *ABS06XL* +renamed_from = "Kimya ABS Carbon @PG 0.6" filament_max_volumetric_speed = 10 -[filament:Kimya ABS Carbon @PG 0.8] -inherits = Kimya ABS Carbon @PG; *ABS08PG* +[filament:Kimya ABS Carbon @XL 0.8] +inherits = Kimya ABS Carbon @XL; *ABS08XL* +renamed_from = "Kimya ABS Carbon @PG 0.8" filament_max_volumetric_speed = 14 [filament:Kimya ABS Carbon @MK4] -inherits = Kimya ABS Carbon; *ABSMK4* +inherits = Kimya ABS Carbon; *ABSPG* filament_max_volumetric_speed = 6 -compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_model=~/(MK4|MK4IS)/ [filament:Kimya ABS Carbon @MK4 0.6] -inherits = Kimya ABS Carbon @MK4; *ABS06MK4* +inherits = Kimya ABS Carbon @MK4; *ABS06PG* filament_max_volumetric_speed = 10 [filament:Kimya ABS Carbon @MK4 0.8] -inherits = Kimya ABS Carbon @MK4; *ABS08MK4* +inherits = Kimya ABS Carbon @MK4; *ABS08PG* filament_max_volumetric_speed = 14 [filament:Kimya ABS Kevlar] @@ -5378,29 +5559,30 @@ inherits = Kimya ABS Carbon filament_vendor = Kimya filament_density = 1.037 -[filament:Kimya ABS Kevlar @PG] -inherits = Kimya ABS Kevlar; *ABSPG*; *04PLUSPG* +[filament:Kimya ABS Kevlar @XL] +inherits = Kimya ABS Kevlar; *ABSPG*; *ABSXL*; *04PLUSXL* +renamed_from = "Kimya ABS Kevlar @PG" bed_temperature = 105 -compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_model=~/.*XL.*/ -[filament:Kimya ABS Kevlar @PG 0.6] -inherits = Kimya ABS Kevlar @PG; *ABS06PG* +[filament:Kimya ABS Kevlar @XL 0.6] +inherits = Kimya ABS Kevlar @XL; *ABS06XL* +renamed_from = "Kimya ABS Kevlar @PG 0.6" filament_max_volumetric_speed = 10 -[filament:Kimya ABS Kevlar @PG 0.8] -inherits = Kimya ABS Kevlar @PG; *ABS08PG* +[filament:Kimya ABS Kevlar @XL 0.8] +inherits = Kimya ABS Kevlar @XL; *ABS08XL* +renamed_from = "Kimya ABS Kevlar @PG 0.8" filament_max_volumetric_speed = 14 [filament:Kimya ABS Kevlar @MK4] -inherits = Kimya ABS Kevlar; *ABSMK4*; *04PLUSPG* -compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_model=~/(MK4|MK4IS)/ +inherits = Kimya ABS Kevlar; *ABSPG*; *04PLUSPG* [filament:Kimya ABS Kevlar @MK4 0.6] -inherits = Kimya ABS Kevlar @MK4; *ABS06MK4* +inherits = Kimya ABS Kevlar @MK4; *ABS06PG* filament_max_volumetric_speed = 10 [filament:Kimya ABS Kevlar @MK4 0.8] -inherits = Kimya ABS Kevlar @MK4; *ABS08MK4* +inherits = Kimya ABS Kevlar @MK4; *ABS08PG* filament_max_volumetric_speed = 14 [filament:Kimya PEBA-S] @@ -5429,6 +5611,15 @@ filament_max_volumetric_speed = 8 inherits = Kimya PEBA-S @PG; *PET08PG* filament_max_volumetric_speed = 10 +[filament:Kimya PEBA-S @XL] +inherits = Kimya PEBA-S @PG; *PETXL* + +[filament:Kimya PEBA-S @XL 0.6] +inherits = Kimya PEBA-S @PG 0.6; *PET06XL* + +[filament:Kimya PEBA-S @XL 0.8] +inherits = Kimya PEBA-S @PG 0.8; *PET08XL* + [filament:E3D Edge] inherits = *PET* filament_vendor = E3D @@ -5445,6 +5636,15 @@ inherits = E3D Edge; *PET06PG* [filament:E3D Edge @PG 0.8] inherits = E3D Edge; *PET08PG* +[filament:E3D Edge @XL] +inherits = E3D Edge @PG; *PETXL* + +[filament:E3D Edge @XL 0.6] +inherits = E3D Edge @PG 0.6; *PET06XL* + +[filament:E3D Edge @XL 0.8] +inherits = E3D Edge @PG 0.8; *PET08XL* + [filament:E3D PC-ABS] ## discontinued inherits = *ABS* @@ -5471,6 +5671,15 @@ inherits = Fillamentum PLA; *PLA06PG* [filament:Fillamentum PLA @PG 0.8] inherits = Fillamentum PLA; *PLA08PG* +[filament:Fillamentum PLA @XL] +inherits = Fillamentum PLA @PG; *PLAXL* + +[filament:Fillamentum PLA @XL 0.6] +inherits = Fillamentum PLA @PG 0.6; *PLA06XL* + +[filament:Fillamentum PLA @XL 0.8] +inherits = Fillamentum PLA @PG 0.8; *PLA08XL* + [filament:Fillamentum ABS] inherits = *ABSC* filament_vendor = Fillamentum @@ -5480,24 +5689,27 @@ filament_spool_weight = 230 first_layer_temperature = 240 temperature = 240 -[filament:Fillamentum ABS @PG] -inherits = Fillamentum ABS; *ABSPG* +[filament:Fillamentum ABS @XL] +inherits = Fillamentum ABS; *ABSPG*; *ABSXL* +renamed_from = "Fillamentum ABS @PG" bed_temperature = 105 -[filament:Fillamentum ABS @PG 0.6] -inherits = Fillamentum ABS @PG; *ABS06PG* +[filament:Fillamentum ABS @XL 0.6] +inherits = Fillamentum ABS @XL; *ABS06XL* +renamed_from = "Fillamentum ABS @PG 0.6" -[filament:Fillamentum ABS @PG 0.8] -inherits = Fillamentum ABS @PG; *ABS08PG* +[filament:Fillamentum ABS @XL 0.8] +inherits = Fillamentum ABS @XL; *ABS08XL* +renamed_from = "Fillamentum ABS @PG 0.8" [filament:Fillamentum ABS @MK4] -inherits = Fillamentum ABS; *ABSMK4* +inherits = Fillamentum ABS; *ABSPG* [filament:Fillamentum ABS @MK4 0.6] -inherits = Fillamentum ABS @MK4; *ABS06MK4* +inherits = Fillamentum ABS @MK4; *ABS06PG* [filament:Fillamentum ABS @MK4 0.8] -inherits = Fillamentum ABS @MK4; *ABS08MK4* +inherits = Fillamentum ABS @MK4; *ABS08PG* [filament:Fillamentum ASA] inherits = *ABS* @@ -5515,28 +5727,31 @@ first_layer_temperature = 260 temperature = 260 filament_type = ASA -[filament:Fillamentum ASA @PG] -inherits = Fillamentum ASA; *ABSPG* +[filament:Fillamentum ASA @XL] +inherits = Fillamentum ASA; *ABSPG*; *ABSXL* +renamed_from = "Fillamentum ASA @PG" bed_temperature = 105 min_fan_speed = 10 max_fan_speed = 10 -[filament:Fillamentum ASA @PG 0.6] -inherits = Fillamentum ASA @PG; *ABS06PG* +[filament:Fillamentum ASA @XL 0.6] +inherits = Fillamentum ASA @XL; *ABS06XL* +renamed_from = "Fillamentum ASA @PG 0.6" -[filament:Fillamentum ASA @PG 0.8] -inherits = Fillamentum ASA @PG; *ABS08PG* +[filament:Fillamentum ASA @XL 0.8] +inherits = Fillamentum ASA @XL; *ABS08XL* +renamed_from = "Fillamentum ASA @PG 0.8" [filament:Fillamentum ASA @MK4] -inherits = Fillamentum ASA; *ABSMK4* +inherits = Fillamentum ASA; *ABSPG* min_fan_speed = 10 max_fan_speed = 10 [filament:Fillamentum ASA @MK4 0.6] -inherits = Fillamentum ASA @MK4; *ABS06MK4* +inherits = Fillamentum ASA @MK4; *ABS06PG* [filament:Fillamentum ASA @MK4 0.8] -inherits = Fillamentum ASA @MK4; *ABS08MK4* +inherits = Fillamentum ASA @MK4; *ABS08PG* [filament:Prusament ASA] inherits = *ABS* @@ -5561,31 +5776,34 @@ filament_colour = #FFF2EC start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.01{elsif nozzle_diameter[0]==0.6}0.02{else}0.04{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K12{elsif nozzle_diameter[0]==0.8};{else}M900 K20{endif} ; Filament gcode LA 1.0" compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) -[filament:Prusament ASA @PG] -inherits = Prusament ASA; *ABSPG* +[filament:Prusament ASA @XL] +inherits = Prusament ASA; *ABSPG*; *ABSXL* +renamed_from = "Prusament ASA @PG" first_layer_bed_temperature = 100 bed_temperature = 105 min_fan_speed = 10 max_fan_speed = 10 -[filament:Prusament ASA @PG 0.6] -inherits = Prusament ASA @PG; *ABS06PG* +[filament:Prusament ASA @XL 0.6] +inherits = Prusament ASA @XL; *ABS06XL* +renamed_from = "Prusament ASA @PG 0.6" -[filament:Prusament ASA @PG 0.8] -inherits = Prusament ASA @PG; *ABS08PG* +[filament:Prusament ASA @XL 0.8] +inherits = Prusament ASA @XL; *ABS08XL* +renamed_from = "Prusament ASA @PG 0.8" first_layer_temperature = 265 temperature = 265 [filament:Prusament ASA @MK4] -inherits = Prusament ASA; *ABSMK4* +inherits = Prusament ASA; *ABSPG* min_fan_speed = 10 max_fan_speed = 10 [filament:Prusament ASA @MK4 0.6] -inherits = Prusament ASA @MK4; *ABS06MK4* +inherits = Prusament ASA @MK4; *ABS06PG* [filament:Prusament ASA @MK4 0.8] -inherits = Prusament ASA @MK4; *ABS08MK4* +inherits = Prusament ASA @MK4; *ABS08PG* first_layer_temperature = 265 temperature = 265 @@ -5615,32 +5833,35 @@ filament_retract_lift = 0.2 compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.02{elsif nozzle_diameter[0]==0.6}0.04{else}0.07{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K24{elsif nozzle_diameter[0]==0.8};{else}M900 K45{endif} ; Filament gcode LA 1.0" -[filament:Prusament PC Blend @PG] +[filament:Prusament PC Blend @XL] +inherits = Prusament PC Blend; *PCPG*; *PCXL* +renamed_from = "Prusament PC Blend @PG" +filament_max_volumetric_speed = 9 +min_fan_speed = 10 +max_fan_speed = 10 + +[filament:Prusament PC Blend @XL 0.6] +inherits = Prusament PC Blend @XL; *PC06XL* +renamed_from = "Prusament PC Blend @PG 0.6" +filament_max_volumetric_speed = 13 + +[filament:Prusament PC Blend @XL 0.8] +inherits = Prusament PC Blend @XL; *PC08XL* +renamed_from = "Prusament PC Blend @PG 0.8" +filament_max_volumetric_speed = 18 + +[filament:Prusament PC Blend @MK4] inherits = Prusament PC Blend; *PCPG* filament_max_volumetric_speed = 9 min_fan_speed = 10 max_fan_speed = 10 -[filament:Prusament PC Blend @PG 0.6] -inherits = Prusament PC Blend @PG; *PC06PG* -filament_max_volumetric_speed = 13 - -[filament:Prusament PC Blend @PG 0.8] -inherits = Prusament PC Blend @PG; *PC08PG* -filament_max_volumetric_speed = 18 - -[filament:Prusament PC Blend @MK4] -inherits = Prusament PC Blend; *PCMK4* -filament_max_volumetric_speed = 9 -min_fan_speed = 10 -max_fan_speed = 10 - [filament:Prusament PC Blend @MK4 0.6] -inherits = Prusament PC Blend @MK4; *PC06MK4* +inherits = Prusament PC Blend @MK4; *PC06PG* filament_max_volumetric_speed = 13 [filament:Prusament PC Blend @MK4 0.8] -inherits = Prusament PC Blend @MK4; *PC08MK4* +inherits = Prusament PC Blend @MK4; *PC08PG* filament_max_volumetric_speed = 18 [filament:Prusament PC Blend @MK2] @@ -5664,30 +5885,33 @@ filament_retract_length = nil filament_retract_lift = nil compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) -[filament:Prusament PC Blend Carbon Fiber @PG] +[filament:Prusament PC Blend Carbon Fiber @XL] +inherits = Prusament PC Blend Carbon Fiber; *PCPG*; *PCXL* +renamed_from = "Prusament PC Blend Carbon Fiber @PG" +min_fan_speed = 10 +max_fan_speed = 10 + +[filament:Prusament PC Blend Carbon Fiber @XL 0.6] +inherits = Prusament PC Blend Carbon Fiber @XL; *PC06XL* +renamed_from = "Prusament PC Blend Carbon Fiber @PG 0.6" +filament_max_volumetric_speed = 13 + +[filament:Prusament PC Blend Carbon Fiber @XL 0.8] +inherits = Prusament PC Blend Carbon Fiber @XL; *PC08XL* +renamed_from = "Prusament PC Blend Carbon Fiber @PG 0.8" +filament_max_volumetric_speed = 18 + +[filament:Prusament PC Blend Carbon Fiber @MK4] inherits = Prusament PC Blend Carbon Fiber; *PCPG* min_fan_speed = 10 max_fan_speed = 10 -[filament:Prusament PC Blend Carbon Fiber @PG 0.6] -inherits = Prusament PC Blend Carbon Fiber @PG; *PC06PG* -filament_max_volumetric_speed = 13 - -[filament:Prusament PC Blend Carbon Fiber @PG 0.8] -inherits = Prusament PC Blend Carbon Fiber @PG; *PC08PG* -filament_max_volumetric_speed = 18 - -[filament:Prusament PC Blend Carbon Fiber @MK4] -inherits = Prusament PC Blend Carbon Fiber; *PCMK4* -min_fan_speed = 10 -max_fan_speed = 10 - [filament:Prusament PC Blend Carbon Fiber @MK4 0.6] -inherits = Prusament PC Blend Carbon Fiber @MK4; *PC06MK4* +inherits = Prusament PC Blend Carbon Fiber @MK4; *PC06PG* filament_max_volumetric_speed = 13 [filament:Prusament PC Blend Carbon Fiber @MK4 0.8] -inherits = Prusament PC Blend Carbon Fiber @MK4; *PC08MK4* +inherits = Prusament PC Blend Carbon Fiber @MK4; *PC08PG* filament_max_volumetric_speed = 18 [filament:Prusament PC Blend Carbon Fiber @MK2] @@ -5712,28 +5936,28 @@ bed_temperature = 115 fan_below_layer_time = 10 compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) -[filament:Prusament PA11 Carbon Fiber @PG] -inherits = Prusament PA11 Carbon Fiber; *PCPG* +[filament:Prusament PA11 Carbon Fiber @XL] +inherits = Prusament PA11 Carbon Fiber; *PCPG*; *PCXL* filament_max_volumetric_speed = 6.5 -[filament:Prusament PA11 Carbon Fiber @PG 0.6] -inherits = Prusament PA11 Carbon Fiber @PG; *PC06PG* +[filament:Prusament PA11 Carbon Fiber @XL 0.6] +inherits = Prusament PA11 Carbon Fiber @XL; *PC06XL* filament_max_volumetric_speed = 8 -[filament:Prusament PA11 Carbon Fiber @PG 0.8] -inherits = Prusament PA11 Carbon Fiber @PG; *PC08PG* +[filament:Prusament PA11 Carbon Fiber @XL 0.8] +inherits = Prusament PA11 Carbon Fiber @XL; *PC08XL* filament_max_volumetric_speed = 10 [filament:Prusament PA11 Carbon Fiber @MK4] -inherits = Prusament PA11 Carbon Fiber; *PCMK4* +inherits = Prusament PA11 Carbon Fiber; *PCPG* filament_max_volumetric_speed = 6.5 [filament:Prusament PA11 Carbon Fiber @MK4 0.6] -inherits = Prusament PA11 Carbon Fiber @MK4; *PC06MK4* +inherits = Prusament PA11 Carbon Fiber @MK4; *PC06PG* filament_max_volumetric_speed = 8 [filament:Prusament PA11 Carbon Fiber @MK4 0.8] -inherits = Prusament PA11 Carbon Fiber @MK4; *PC08MK4* +inherits = Prusament PA11 Carbon Fiber @MK4; *PC08PG* filament_max_volumetric_speed = 10 [filament:Prusament PA11 Carbon Fiber @MK2] @@ -5769,6 +5993,15 @@ inherits = Fillamentum CPE; *PET06PG* [filament:Fillamentum CPE @PG 0.8] inherits = Fillamentum CPE; *PET08PG* +[filament:Fillamentum CPE @XL] +inherits = Fillamentum CPE @PG; *PETXL* + +[filament:Fillamentum CPE @XL 0.6] +inherits = Fillamentum CPE @PG 0.6; *PET06XL* + +[filament:Fillamentum CPE @XL 0.8] +inherits = Fillamentum CPE @PG 0.8; *PET08XL* + [filament:Fillamentum Timberfill] inherits = *PLA*; *04PLUS* filament_vendor = Fillamentum @@ -5797,6 +6030,15 @@ inherits = Fillamentum Timberfill; *PLA08PG* filament_max_volumetric_speed = 17 extrusion_multiplier = 1.05 +[filament:Fillamentum Timberfill @XL] +inherits = Fillamentum Timberfill @PG; *PLAXL*; *04PLUSXL* + +[filament:Fillamentum Timberfill @XL 0.6] +inherits = Fillamentum Timberfill @PG 0.6; *PLA06XL* + +[filament:Fillamentum Timberfill @XL 0.8] +inherits = Fillamentum Timberfill @PG 0.8; *PLA08XL* + [filament:Smartfil Wood] inherits = *PLA*; *04PLUS* filament_vendor = Smart Materials 3D @@ -5824,6 +6066,15 @@ inherits = Smartfil Wood; *PLA08PG* filament_max_volumetric_speed = 17 extrusion_multiplier = 1.05 +[filament:Smartfil Wood @XL] +inherits = Smartfil Wood @PG; *PLAXL* + +[filament:Smartfil Wood @XL 0.6] +inherits = Smartfil Wood @PG 0.6; *PLA06XL* + +[filament:Smartfil Wood @XL 0.8] +inherits = Smartfil Wood @PG 0.8; *PLA08XL* + [filament:Generic ABS] inherits = *ABSC* filament_vendor = Generic @@ -5831,26 +6082,29 @@ filament_cost = 27.82 filament_density = 1.04 compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) -[filament:Generic ABS @PG] -inherits = Generic ABS; *ABSPG* +[filament:Generic ABS @XL] +inherits = Generic ABS; *ABSPG*; *ABSXL* +renamed_from = "Generic ABS @PG" bed_temperature = 105 -[filament:Generic ABS @PG 0.6] -inherits = Generic ABS @PG; *ABS06PG* +[filament:Generic ABS @XL 0.6] +inherits = Generic ABS @XL; *ABS06XL* +renamed_from = "Generic ABS @PG 0.6" -[filament:Generic ABS @PG 0.8] -inherits = Generic ABS @PG; *ABS08PG* +[filament:Generic ABS @XL 0.8] +inherits = Generic ABS @XL; *ABS08XL* +renamed_from = "Generic ABS @PG 0.8" first_layer_temperature = 265 temperature = 265 [filament:Generic ABS @MK4] -inherits = Generic ABS; *ABSMK4* +inherits = Generic ABS; *ABSPG* [filament:Generic ABS @MK4 0.6] -inherits = Generic ABS @MK4; *ABS06MK4* +inherits = Generic ABS @MK4; *ABS06PG* [filament:Generic ABS @MK4 0.8] -inherits = Generic ABS @MK4; *ABS08MK4* +inherits = Generic ABS @MK4; *ABS08PG* first_layer_temperature = 265 temperature = 265 @@ -5861,24 +6115,27 @@ filament_cost = 27.82 filament_density = 1.01 filament_spool_weight = 265 -[filament:Esun ABS @PG] -inherits = Esun ABS; *ABSPG* +[filament:Esun ABS @XL] +inherits = Esun ABS; *ABSPG*; *ABSXL* +renamed_from = "Esun ABS @PG" bed_temperature = 105 -[filament:Esun ABS @PG 0.6] -inherits = Esun ABS @PG; *ABS06PG* +[filament:Esun ABS @XL 0.6] +inherits = Esun ABS @XL; *ABS06XL* +renamed_from = "Esun ABS @PG 0.6" -[filament:Esun ABS @PG 0.8] -inherits = Esun ABS @PG; *ABS08PG* +[filament:Esun ABS @XL 0.8] +inherits = Esun ABS @XL; *ABS08XL* +renamed_from = "Esun ABS @PG 0.8" [filament:Esun ABS @MK4] -inherits = Esun ABS; *ABSMK4* +inherits = Esun ABS; *ABSPG* [filament:Esun ABS @MK4 0.6] -inherits = Esun ABS @MK4; *ABS06MK4* +inherits = Esun ABS @MK4; *ABS06PG* [filament:Esun ABS @MK4 0.8] -inherits = Esun ABS @MK4; *ABS08MK4* +inherits = Esun ABS @MK4; *ABS08PG* [filament:Hatchbox ABS] inherits = *ABSC* @@ -5887,24 +6144,27 @@ filament_cost = 27.82 filament_density = 1.04 filament_spool_weight = 245 -[filament:Hatchbox ABS @PG] -inherits = Hatchbox ABS; *ABSPG* +[filament:Hatchbox ABS @XL] +inherits = Hatchbox ABS; *ABSPG*; *ABSXL* +renamed_from = "Hatchbox ABS @PG" bed_temperature = 105 -[filament:Hatchbox ABS @PG 0.6] -inherits = Hatchbox ABS @PG; *ABS06PG* +[filament:Hatchbox ABS @XL 0.6] +inherits = Hatchbox ABS @XL; *ABS06XL* +renamed_from = "Hatchbox ABS @PG 0.6" -[filament:Hatchbox ABS @PG 0.8] -inherits = Hatchbox ABS @PG; *ABS08PG* +[filament:Hatchbox ABS @XL 0.8] +inherits = Hatchbox ABS @XL; *ABS08XL* +renamed_from = "Hatchbox ABS @PG 0.8" [filament:Hatchbox ABS @MK4] -inherits = Hatchbox ABS; *ABSMK4* +inherits = Hatchbox ABS; *ABSPG* [filament:Hatchbox ABS @MK4 0.6] -inherits = Hatchbox ABS @MK4; *ABS06MK4* +inherits = Hatchbox ABS @MK4; *ABS06PG* [filament:Hatchbox ABS @MK4 0.8] -inherits = Hatchbox ABS @MK4; *ABS08MK4* +inherits = Hatchbox ABS @MK4; *ABS08PG* [filament:Filament PM ABS] inherits = *ABSC* @@ -5914,24 +6174,27 @@ filament_cost = 27.82 filament_density = 1.08 filament_spool_weight = 230 -[filament:Filament PM ABS @PG] -inherits = Filament PM ABS; *ABSPG* +[filament:Filament PM ABS @XL] +inherits = Filament PM ABS; *ABSPG*; *ABSXL* +renamed_from = "Filament PM ABS @PG" bed_temperature = 105 -[filament:Filament PM ABS @PG 0.6] -inherits = Filament PM ABS @PG; *ABS06PG* +[filament:Filament PM ABS @XL 0.6] +inherits = Filament PM ABS @XL; *ABS06XL* +renamed_from = "Filament PM ABS @PG 0.6" -[filament:Filament PM ABS @PG 0.8] -inherits = Filament PM ABS @PG; *ABS08PG* +[filament:Filament PM ABS @XL 0.8] +inherits = Filament PM ABS @XL; *ABS08XL* +renamed_from = "Filament PM ABS @PG 0.8" [filament:Filament PM ABS @MK4] -inherits = Filament PM ABS; *ABSMK4* +inherits = Filament PM ABS; *ABSPG* [filament:Filament PM ABS @MK4 0.6] -inherits = Filament PM ABS @MK4; *ABS06MK4* +inherits = Filament PM ABS @MK4; *ABS06PG* [filament:Filament PM ABS @MK4 0.8] -inherits = Filament PM ABS @MK4; *ABS08MK4* +inherits = Filament PM ABS @MK4; *ABS08PG* [filament:Verbatim ABS] inherits = *ABSC* @@ -5941,24 +6204,27 @@ filament_density = 1.05 filament_spool_weight = 235 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.01{elsif nozzle_diameter[0]==0.6}0.03{else}0.04{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K12{elsif nozzle_diameter[0]==0.8};{else}M900 K20{endif} ; Filament gcode LA 1.0" -[filament:Verbatim ABS @PG] -inherits = Verbatim ABS; *ABSPG* +[filament:Verbatim ABS @XL] +inherits = Verbatim ABS; *ABSPG*; *ABSXL* +renamed_from = "Verbatim ABS @PG" bed_temperature = 105 -[filament:Verbatim ABS @PG 0.6] -inherits = Verbatim ABS @PG; *ABS06PG* +[filament:Verbatim ABS @XL 0.6] +inherits = Verbatim ABS @XL; *ABS06XL* +renamed_from = "Verbatim ABS @PG 0.6" -[filament:Verbatim ABS @PG 0.8] -inherits = Verbatim ABS @PG; *ABS08PG* +[filament:Verbatim ABS @XL 0.8] +inherits = Verbatim ABS @XL; *ABS08XL* +renamed_from = "Verbatim ABS @PG 0.8" [filament:Verbatim ABS @MK4] -inherits = Verbatim ABS; *ABSMK4* +inherits = Verbatim ABS; *ABSPG* [filament:Verbatim ABS @MK4 0.6] -inherits = Verbatim ABS @MK4; *ABS06MK4* +inherits = Verbatim ABS @MK4; *ABS06PG* [filament:Verbatim ABS @MK4 0.8] -inherits = Verbatim ABS @MK4; *ABS08MK4* +inherits = Verbatim ABS @MK4; *ABS08PG* [filament:Generic PETG] inherits = *PET* @@ -5970,18 +6236,27 @@ compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MK2S [filament:Generic PETG @PG] inherits = Generic PETG; *PETPG* -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model!="MK4IS" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and printer_model!="MK4IS" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 [filament:Generic PETG @PG 0.6] inherits = Generic PETG; *PET06PG* filament_max_volumetric_speed = 17 -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model!="MK4IS" and nozzle_diameter[0]==0.6 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and printer_model!="MK4IS" and nozzle_diameter[0]==0.6 [filament:Generic PETG @PG 0.8] inherits = Generic PETG; *PET08PG* first_layer_temperature = 240 temperature = 250 -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model!="MK4IS" and nozzle_diameter[0]==0.8 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and printer_model!="MK4IS" and nozzle_diameter[0]==0.8 + +[filament:Generic PETG @XL] +inherits = Generic PETG @PG; *PETXL* + +[filament:Generic PETG @XL 0.6] +inherits = Generic PETG @PG 0.6; *PET06XL* + +[filament:Generic PETG @XL 0.8] +inherits = Generic PETG @PG 0.8; *PET08XL* [filament:Generic PETG @PGIS] inherits = Generic PETG @PG @@ -5995,7 +6270,7 @@ filament_retract_length = 0.8 filament_wipe = 1 filament_retract_before_wipe = 20 filament_retract_lift = nil -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 [filament:Generic PETG @PGIS 0.6] inherits = Generic PETG @PG 0.6 @@ -6005,7 +6280,7 @@ filament_retract_length = 0.8 filament_wipe = 1 filament_retract_before_wipe = 20 filament_retract_lift = nil -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]==0.6 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.6 [filament:Generic PETG @PGIS 0.8] inherits = Generic PETG @PG 0.8 @@ -6015,7 +6290,7 @@ filament_retract_length = 0.8 filament_wipe = 1 filament_retract_before_wipe = 20 filament_retract_lift = nil -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]==0.8 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.8 [filament:Extrudr DuraPro ASA] inherits = Fillamentum ASA @@ -6033,15 +6308,21 @@ filament_spool_weight = 230 [filament:Extrudr DuraPro ASA @PG] inherits = Extrudr DuraPro ASA; *ABSPG* filament_max_volumetric_speed = 10 -compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Extrudr DuraPro ASA @PG 0.6] inherits = Extrudr DuraPro ASA @PG; *ABS06PG* -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Extrudr DuraPro ASA @PG 0.8] inherits = Extrudr DuraPro ASA @PG; *ABS08PG* -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material + +[filament:Extrudr DuraPro ASA @XL] +inherits = Extrudr DuraPro ASA @PG; *ABSXL* + +[filament:Extrudr DuraPro ASA @XL 0.6] +inherits = Extrudr DuraPro ASA @PG 0.6; *ABS06XL* + +[filament:Extrudr DuraPro ASA @XL 0.8] +inherits = Extrudr DuraPro ASA @PG 0.8; *ABS08XL* [filament:Extrudr PETG] inherits = *PET* @@ -6068,6 +6349,15 @@ inherits = Extrudr PETG; *PET06PG* [filament:Extrudr PETG @PG 0.8] inherits = Extrudr PETG; *PET08PG* +[filament:Extrudr PETG @XL] +inherits = Extrudr PETG @PG; *PETXL* + +[filament:Extrudr PETG @XL 0.6] +inherits = Extrudr PETG @PG 0.6; *PET06XL* + +[filament:Extrudr PETG @XL 0.8] +inherits = Extrudr PETG @PG 0.8; *PET08XL* + [filament:Extrudr PETG @MINI] inherits = Extrudr PETG; *PETMINI* filament_vendor = Extrudr @@ -6091,6 +6381,15 @@ inherits = Extrudr XPETG CF; *PET06PG* [filament:Extrudr XPETG CF @PG 0.8] inherits = Extrudr XPETG CF; *PET08PG* +[filament:Extrudr XPETG CF @XL] +inherits = Extrudr XPETG CF @PG; *PETXL* + +[filament:Extrudr XPETG CF @XL 0.6] +inherits = Extrudr XPETG CF @PG 0.6; *PET06XL* + +[filament:Extrudr XPETG CF @XL 0.8] +inherits = Extrudr XPETG CF @PG 0.8; *PET08XL* + [filament:Extrudr XPETG CF @MINI] inherits = Extrudr XPETG CF; *PETMINI* compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model=="MINI" @@ -6112,6 +6411,15 @@ inherits = Extrudr XPETG Matt; *PET06PG* [filament:Extrudr XPETG Matt @PG 0.8] inherits = Extrudr XPETG Matt; *PET08PG* +[filament:Extrudr XPETG Matt @XL] +inherits = Extrudr XPETG Matt @PG; *PETXL* + +[filament:Extrudr XPETG Matt @XL 0.6] +inherits = Extrudr XPETG Matt @PG 0.6; *PET06XL* + +[filament:Extrudr XPETG Matt @XL 0.8] +inherits = Extrudr XPETG Matt @PG 0.8; *PET08XL* + [filament:Extrudr XPETG Matt @MINI] inherits = Extrudr XPETG Matt; *PETMINI* @@ -6140,6 +6448,15 @@ inherits = Extrudr BioFusion; *PLA06PG* [filament:Extrudr BioFusion @PG 0.8] inherits = Extrudr BioFusion; *PLA08PG* +[filament:Extrudr BioFusion @XL] +inherits = Extrudr BioFusion @PG; *PLAXL* + +[filament:Extrudr BioFusion @XL 0.6] +inherits = Extrudr BioFusion @PG 0.6; *PLA06XL* + +[filament:Extrudr BioFusion @XL 0.8] +inherits = Extrudr BioFusion @PG 0.8; *PLA08XL* + [filament:Extrudr Flax] inherits = *PLA* filament_vendor = Extrudr @@ -6169,6 +6486,15 @@ filament_max_volumetric_speed = 17 first_layer_temperature = 200 temperature = 200 +[filament:Extrudr Flax @XL] +inherits = Extrudr Flax @PG; *PLAXL* + +[filament:Extrudr Flax @XL 0.6] +inherits = Extrudr Flax @PG 0.6; *PLA06XL* + +[filament:Extrudr Flax @XL 0.8] +inherits = Extrudr Flax @PG 0.8; *PLA08XL* + [filament:Extrudr GreenTEC] inherits = *PLA* filament_vendor = Extrudr @@ -6189,6 +6515,15 @@ inherits = Extrudr GreenTEC; *PLA06PG* [filament:Extrudr GreenTEC @PG 0.8] inherits = Extrudr GreenTEC; *PLA08PG* +[filament:Extrudr GreenTEC @XL] +inherits = Extrudr GreenTEC @PG; *PLAXL* + +[filament:Extrudr GreenTEC @XL 0.6] +inherits = Extrudr GreenTEC @PG; *PLA06XL* + +[filament:Extrudr GreenTEC @XL 0.8] +inherits = Extrudr GreenTEC @PG; *PLA08XL* + [filament:Extrudr GreenTEC Pro] inherits = *PLA* filament_vendor = Extrudr @@ -6211,6 +6546,15 @@ inherits = Extrudr GreenTEC Pro; *PLA06PG* [filament:Extrudr GreenTEC Pro @PG 0.8] inherits = Extrudr GreenTEC Pro; *PLA08PG* +[filament:Extrudr GreenTEC Pro @XL] +inherits = Extrudr GreenTEC Pro @PG; *PLAXL* + +[filament:Extrudr GreenTEC Pro @XL 0.6] +inherits = Extrudr GreenTEC Pro @PG 0.6; *PLA06XL* + +[filament:Extrudr GreenTEC Pro @XL 0.8] +inherits = Extrudr GreenTEC Pro @PG 0.8; *PLA08XL* + [filament:Extrudr GreenTEC Pro Carbon] inherits = *PLA*; *04PLUS* filament_vendor = Extrudr @@ -6234,6 +6578,15 @@ inherits = Extrudr GreenTEC Pro Carbon; *PLA06PG* [filament:Extrudr GreenTEC Pro Carbon @PG 0.8] inherits = Extrudr GreenTEC Pro Carbon; *PLA08PG* +[filament:Extrudr GreenTEC Pro Carbon @XL] +inherits = Extrudr GreenTEC Pro Carbon @PG; *PLAXL* + +[filament:Extrudr GreenTEC Pro Carbon @XL 0.6] +inherits = Extrudr GreenTEC Pro Carbon @PG 0.6; *PLA06XL* + +[filament:Extrudr GreenTEC Pro Carbon @XL 0.8] +inherits = Extrudr GreenTEC Pro Carbon @PG 0.8; *PLA08XL* + [filament:Extrudr PLA NX1] inherits = *PLA* filament_vendor = Extrudr @@ -6259,6 +6612,15 @@ inherits = Extrudr PLA NX1; *PLA06PG* [filament:Extrudr PLA NX1 @PG 0.8] inherits = Extrudr PLA NX1; *PLA08PG* +[filament:Extrudr PLA NX1 @XL] +inherits = Extrudr PLA NX1 @PG; *PLAXL* + +[filament:Extrudr PLA NX1 @XL 0.6] +inherits = Extrudr PLA NX1 @PG; *PLA06XL* + +[filament:Extrudr PLA NX1 @XL 0.8] +inherits = Extrudr PLA NX1 @PG; *PLA08XL* + [filament:Extrudr PLA NX2] inherits = Extrudr PLA NX1 filament_cost = 23.63 @@ -6274,6 +6636,15 @@ inherits = Extrudr PLA NX2; *PLA06PG* [filament:Extrudr PLA NX2 @PG 0.8] inherits = Extrudr PLA NX2; *PLA08PG* +[filament:Extrudr PLA NX2 @XL] +inherits = Extrudr PLA NX2 @PG; *PLAXL* + +[filament:Extrudr PLA NX2 @XL 0.6] +inherits = Extrudr PLA NX2 @PG 0.6; *PLA06XL* + +[filament:Extrudr PLA NX2 @XL 0.8] +inherits = Extrudr PLA NX2 @PG 0.8; *PLA08XL* + [filament:Extrudr Flex Hard] inherits = *FLEX* filament_vendor = Extrudr @@ -6289,27 +6660,31 @@ filament_wipe = nil filament_spool_weight = 230 slowdown_below_layer_time = 20 -[filament:Extrudr Flex Hard @PG] +[filament:Extrudr Flex Hard @XL] +inherits = Extrudr Flex Hard; *FLEXXL* +renamed_from = "Extrudr Flex Hard @PG" +extrusion_multiplier = 1.1 +filament_retract_length = 2.5 +filament_retract_length_toolchange = 2.5 + +[filament:Extrudr Flex Hard @XL 0.6] +inherits = Extrudr Flex Hard @XL; *FLEX06XL* +renamed_from = "Extrudr Flex Hard @PG 0.6" + +[filament:Extrudr Flex Hard @XL 0.8] +inherits = Extrudr Flex Hard @XL; *FLEX08XL* +renamed_from = "Extrudr Flex Hard @PG 0.8" + +[filament:Extrudr Flex Hard @MK4] inherits = Extrudr Flex Hard; *FLEXPG* extrusion_multiplier = 1.1 filament_retract_length = 2.5 -[filament:Extrudr Flex Hard @PG 0.6] -inherits = Extrudr Flex Hard @PG; *FLEX06PG* - -[filament:Extrudr Flex Hard @PG 0.8] -inherits = Extrudr Flex Hard @PG; *FLEX08PG* - -[filament:Extrudr Flex Hard @MK4] -inherits = Extrudr Flex Hard; *FLEXMK4* -extrusion_multiplier = 1.1 -filament_retract_length = 2.5 - [filament:Extrudr Flex Hard @MK4 0.6] -inherits = Extrudr Flex Hard @MK4; *FLEX06MK4* +inherits = Extrudr Flex Hard @MK4; *FLEX06PG* [filament:Extrudr Flex Hard @MK4 0.8] -inherits = Extrudr Flex Hard @MK4; *FLEX08MK4* +inherits = Extrudr Flex Hard @MK4; *FLEX08PG* [filament:Extrudr Flex Medium] inherits = *FLEX* @@ -6326,27 +6701,31 @@ filament_wipe = nil filament_spool_weight = 230 slowdown_below_layer_time = 20 -[filament:Extrudr Flex Medium @PG] +[filament:Extrudr Flex Medium @XL] +inherits = Extrudr Flex Medium; *FLEXXL* +renamed_from = "Extrudr Flex Medium @PG" +extrusion_multiplier = 1.1 +filament_retract_length = 2.5 +filament_retract_length_toolchange = 2.5 + +[filament:Extrudr Flex Medium @XL 0.6] +inherits = Extrudr Flex Medium @XL; *FLEX06XL* +renamed_from = "Extrudr Flex Medium @PG 0.6" + +[filament:Extrudr Flex Medium @XL 0.8] +inherits = Extrudr Flex Medium @XL; *FLEX08XL* +renamed_from = "Extrudr Flex Medium @PG 0.8" + +[filament:Extrudr Flex Medium @MK4] inherits = Extrudr Flex Medium; *FLEXPG* extrusion_multiplier = 1.1 filament_retract_length = 2.5 -[filament:Extrudr Flex Medium @PG 0.6] -inherits = Extrudr Flex Medium @PG; *FLEX06PG* - -[filament:Extrudr Flex Medium @PG 0.8] -inherits = Extrudr Flex Medium @PG; *FLEX08PG* - -[filament:Extrudr Flex Medium @MK4] -inherits = Extrudr Flex Medium; *FLEXMK4* -extrusion_multiplier = 1.1 -filament_retract_length = 2.5 - [filament:Extrudr Flex Medium @MK4 0.6] -inherits = Extrudr Flex Medium @MK4; *FLEX06MK4* +inherits = Extrudr Flex Medium @MK4; *FLEX06PG* [filament:Extrudr Flex Medium @MK4 0.8] -inherits = Extrudr Flex Medium @MK4; *FLEX08MK4* +inherits = Extrudr Flex Medium @MK4; *FLEX08PG* [filament:Extrudr Flex SemiSoft] inherits = *FLEX* @@ -6363,32 +6742,37 @@ filament_wipe = nil filament_spool_weight = 230 slowdown_below_layer_time = 20 -[filament:Extrudr Flex SemiSoft @PG] +[filament:Extrudr Flex SemiSoft @XL] +inherits = Extrudr Flex SemiSoft; *FLEXXL* +renamed_from = "Extrudr Flex SemiSoft @PG" +extrusion_multiplier = 1.1 +filament_retract_length = 3 +filament_max_volumetric_speed = 3 +filament_multitool_ramming_flow = 3 +filament_retract_length_toolchange = 3 + +[filament:Extrudr Flex SemiSoft @XL 0.6] +inherits = Extrudr Flex SemiSoft @XL; *FLEX06XL* +renamed_from = "Extrudr Flex SemiSoft @PG 0.6" + +[filament:Extrudr Flex SemiSoft @XL 0.8] +inherits = Extrudr Flex SemiSoft @XL; *FLEX08XL* +renamed_from = "Extrudr Flex SemiSoft @PG 0.8" +filament_max_volumetric_speed = 8 +filament_multitool_ramming_flow = 8 + +[filament:Extrudr Flex SemiSoft @MK4] inherits = Extrudr Flex SemiSoft; *FLEXPG* extrusion_multiplier = 1.1 filament_retract_length = 3 filament_max_volumetric_speed = 3 -[filament:Extrudr Flex SemiSoft @PG 0.6] -inherits = Extrudr Flex SemiSoft @PG; *FLEX06PG* -filament_max_volumetric_speed = 5 - -[filament:Extrudr Flex SemiSoft @PG 0.8] -inherits = Extrudr Flex SemiSoft @PG; *FLEX08PG* -filament_max_volumetric_speed = 8 - -[filament:Extrudr Flex SemiSoft @MK4] -inherits = Extrudr Flex SemiSoft; *FLEXMK4* -extrusion_multiplier = 1.1 -filament_retract_length = 3 -filament_max_volumetric_speed = 3 - [filament:Extrudr Flex SemiSoft @MK4 0.6] -inherits = Extrudr Flex SemiSoft @MK4; *FLEX06MK4* +inherits = Extrudr Flex SemiSoft @MK4; *FLEX06PG* filament_max_volumetric_speed = 5 [filament:Extrudr Flex SemiSoft @MK4 0.8] -inherits = Extrudr Flex SemiSoft @MK4; *FLEX08MK4* +inherits = Extrudr Flex SemiSoft @MK4; *FLEX08PG* filament_max_volumetric_speed = 8 [filament:addnorth Adamant S1] @@ -6418,34 +6802,41 @@ filament_spool_weight = 0 filament_retract_restart_extra = 0.1 filament_wipe = nil -[filament:addnorth Adamant S1 @PG] +[filament:addnorth Adamant S1 @XL] +inherits = addnorth Adamant S1; *FLEXXL* +renamed_from = "addnorth Adamant S1 @PG" +filament_max_volumetric_speed = 3 +filament_retract_length = 1.5 +filament_retract_restart_extra = 0 +filament_retract_lift = 0.2 +filament_multitool_ramming_flow = 3 +filament_retract_length_toolchange = 1.5 + +[filament:addnorth Adamant S1 @XL 0.6] +inherits = addnorth Adamant S1 @XL; *FLEX06XL* +renamed_from = "addnorth Adamant S1 @PG 0.6" +filament_max_volumetric_speed = 4.5 +filament_multitool_ramming_flow = 4.5 + +[filament:addnorth Adamant S1 @XL 0.8] +inherits = addnorth Adamant S1 @XL; *FLEX08XL* +renamed_from = "addnorth Adamant S1 @PG 0.8" +filament_max_volumetric_speed = 9 +filament_multitool_ramming_flow = 9 + +[filament:addnorth Adamant S1 @MK4] inherits = addnorth Adamant S1; *FLEXPG* filament_max_volumetric_speed = 3 filament_retract_length = 1.5 filament_retract_restart_extra = 0 filament_retract_lift = 0.2 -[filament:addnorth Adamant S1 @PG 0.6] -inherits = addnorth Adamant S1 @PG; *FLEX06PG* -filament_max_volumetric_speed = 4.5 - -[filament:addnorth Adamant S1 @PG 0.8] -inherits = addnorth Adamant S1 @PG; *FLEX08PG* -filament_max_volumetric_speed = 9 - -[filament:addnorth Adamant S1 @MK4] -inherits = addnorth Adamant S1; *FLEXMK4* -filament_max_volumetric_speed = 3 -filament_retract_length = 1.5 -filament_retract_restart_extra = 0 -filament_retract_lift = 0.2 - [filament:addnorth Adamant S1 @MK4 0.6] -inherits = addnorth Adamant S1 @MK4; *FLEX06MK4* +inherits = addnorth Adamant S1 @MK4; *FLEX06PG* filament_max_volumetric_speed = 5.5 [filament:addnorth Adamant S1 @MK4 0.8] -inherits = addnorth Adamant S1 @MK4; *FLEX08MK4* +inherits = addnorth Adamant S1 @MK4; *FLEX08PG* filament_max_volumetric_speed = 9 [filament:addnorth Adura X] @@ -6475,37 +6866,34 @@ start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and no filament_spool_weight = 0 compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and nozzle_diameter[0]>=0.4 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and printer_model!="MK2SMM" and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) -[filament:addnorth Adura X @PG] -inherits = addnorth Adura X; *PETPG* +[filament:addnorth Adura X @XL] +inherits = addnorth Adura X; *PETPG*; *PETXL* +renamed_from = "addnorth Adura X @PG" first_layer_bed_temperature = 100 bed_temperature = 105 filament_max_volumetric_speed = 4 -compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_model=~/.*XL.*/ and ! single_extruder_multi_material -[filament:addnorth Adura X @PG 0.6] -inherits = addnorth Adura X @PG; *PET06PG* +[filament:addnorth Adura X @XL 0.6] +inherits = addnorth Adura X @XL; *PET06XL* +renamed_from = "addnorth Adura X @PG 0.6" filament_max_volumetric_speed = 6 -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_model=~/.*XL.*/ and ! single_extruder_multi_material -[filament:addnorth Adura X @PG 0.8] -inherits = addnorth Adura X @PG; *PET08PG* +[filament:addnorth Adura X @XL 0.8] +inherits = addnorth Adura X @XL; *PET08XL* +renamed_from = "addnorth Adura X @PG 0.8" filament_max_volumetric_speed = 8 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model=~/.*XL.*/ and ! single_extruder_multi_material [filament:addnorth Adura X @MK4] inherits = addnorth Adura X; *PETPG* filament_max_volumetric_speed = 4 -compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_model=~/(MK4|MK4IS)/ and ! single_extruder_multi_material [filament:addnorth Adura X @MK4 0.6] inherits = addnorth Adura X @MK4; *PET06PG* filament_max_volumetric_speed = 6 -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_model=~/(MK4|MK4IS)/ and ! single_extruder_multi_material [filament:addnorth Adura X @MK4 0.8] inherits = addnorth Adura X @MK4; *PET08PG* filament_max_volumetric_speed = 8 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_model=~/(MK4|MK4IS)/ and ! single_extruder_multi_material [filament:addnorth Adura X @MINI] inherits = addnorth Adura X @@ -6554,6 +6942,15 @@ inherits = addnorth E-PLA; *PLA06PG* [filament:addnorth E-PLA @PG 0.8] inherits = addnorth E-PLA; *PLA08PG* +[filament:addnorth E-PLA @XL] +inherits = addnorth E-PLA @PG; *PLAXL* + +[filament:addnorth E-PLA @XL 0.6] +inherits = addnorth E-PLA @PG 0.6; *PLA06XL* + +[filament:addnorth E-PLA @XL 0.8] +inherits = addnorth E-PLA @PG 0.8; *PLA08XL* + [filament:addnorth ESD-PETG] inherits = *PET* filament_vendor = addnorth @@ -6590,6 +6987,15 @@ filament_max_volumetric_speed = 3.5 inherits = addnorth ESD-PETG @PG; *PET08PG* filament_max_volumetric_speed = 6 +[filament:addnorth ESD-PETG @XL] +inherits = addnorth ESD-PETG @PG; *PETXL* + +[filament:addnorth ESD-PETG @XL 0.6] +inherits = addnorth ESD-PETG @PG 0.6; *PET06XL* + +[filament:addnorth ESD-PETG @XL 0.8] +inherits = addnorth ESD-PETG @PG 0.8; *PET08XL* + [filament:addnorth ESD-PETG @MINI] inherits = addnorth ESD-PETG filament_retract_length = nil @@ -6631,30 +7037,37 @@ filament_deretract_speed = 25 filament_spool_weight = 0 filament_notes = "Use Magigoo PP bed adhesive or PP packing tape (on a cold printbed)." -[filament:addnorth OBC Polyethylene @PG] +[filament:addnorth OBC Polyethylene @XL] +inherits = addnorth OBC Polyethylene; *FLEXXL* +renamed_from = "addnorth OBC Polyethylene @PG" +filament_max_volumetric_speed = 4 +filament_retract_length = 1.5 +filament_multitool_ramming_flow = 4 +filament_retract_length_toolchange = 1.5 + +[filament:addnorth OBC Polyethylene @XL 0.6] +inherits = addnorth OBC Polyethylene @XL; *FLEX06XL* +renamed_from = "addnorth OBC Polyethylene @PG 0.6" +filament_max_volumetric_speed = 6 +filament_multitool_ramming_flow = 6 + +[filament:addnorth OBC Polyethylene @XL 0.8] +inherits = addnorth OBC Polyethylene @XL; *FLEX08XL* +renamed_from = "addnorth OBC Polyethylene @PG 0.8" +filament_max_volumetric_speed = 10 +filament_multitool_ramming_flow = 10 + +[filament:addnorth OBC Polyethylene @MK4] inherits = addnorth OBC Polyethylene; *FLEXPG* filament_max_volumetric_speed = 4 filament_retract_length = 1.5 -[filament:addnorth OBC Polyethylene @PG 0.6] -inherits = addnorth OBC Polyethylene @PG; *FLEX06PG* -filament_max_volumetric_speed = 6 - -[filament:addnorth OBC Polyethylene @PG 0.8] -inherits = addnorth OBC Polyethylene @PG; *FLEX08PG* -filament_max_volumetric_speed = 10 - -[filament:addnorth OBC Polyethylene @MK4] -inherits = addnorth OBC Polyethylene; *FLEXMK4* -filament_max_volumetric_speed = 4 -filament_retract_length = 1.5 - [filament:addnorth OBC Polyethylene @MK4 0.6] -inherits = addnorth OBC Polyethylene @MK4; *FLEX06MK4* +inherits = addnorth OBC Polyethylene @MK4; *FLEX06PG* filament_max_volumetric_speed = 6 [filament:addnorth OBC Polyethylene @MK4 0.8] -inherits = addnorth OBC Polyethylene @MK4; *FLEX08MK4* +inherits = addnorth OBC Polyethylene @MK4; *FLEX08PG* filament_max_volumetric_speed = 10 [filament:addnorth PETG] @@ -6688,6 +7101,15 @@ inherits = addnorth PETG @PG; *PET06PG* [filament:addnorth PETG @PG 0.8] inherits = addnorth PETG @PG; *PET08PG* +[filament:addnorth PETG @XL] +inherits = addnorth PETG @PG; *PETXL* + +[filament:addnorth PETG @XL 0.6] +inherits = addnorth PETG @PG 0.6; *PET06XL* + +[filament:addnorth PETG @XL 0.8] +inherits = addnorth PETG @PG 0.8; *PET08XL* + [filament:addnorth PETG @MINI] inherits = addnorth PETG filament_retract_length = nil @@ -6741,6 +7163,15 @@ filament_max_volumetric_speed = 7 inherits = addnorth Rigid X @PG; *PET08PG* filament_max_volumetric_speed = 10 +[filament:addnorth Rigid X @XL] +inherits = addnorth Rigid X @PG; *PETXL*; *04PLUSXL* + +[filament:addnorth Rigid X @XL 0.6] +inherits = addnorth Rigid X @PG 0.6; *PET06XL* + +[filament:addnorth Rigid X @XL 0.8] +inherits = addnorth Rigid X @PG 0.8; *PET08XL* + [filament:addnorth Rigid X @MINI] inherits = addnorth Rigid X filament_retract_length = nil @@ -6784,6 +7215,15 @@ inherits = addnorth Textura; *PLA06PG* [filament:addnorth Textura @PG 0.8] inherits = addnorth Textura; *PLA08PG* +[filament:addnorth Textura @XL] +inherits = addnorth Textura @PG; *PLAXL* + +[filament:addnorth Textura @XL 0.6] +inherits = addnorth Textura @PG 0.6; *PLA06XL* + +[filament:addnorth Textura @XL 0.8] +inherits = addnorth Textura @PG 0.8; *PLA08XL* + [filament:addnorth Textura @MINI] inherits = addnorth Textura filament_retract_length = nil @@ -6815,17 +7255,23 @@ compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI [filament:Filamentworld ABS @PG] inherits = Filamentworld ABS; *ABSPG* first_layer_bed_temperature = 100 -compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Filamentworld ABS @PG 0.6] inherits = Filamentworld ABS @PG; *ABS06PG* -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Filamentworld ABS @PG 0.8] inherits = Filamentworld ABS @PG; *ABS08PG* first_layer_temperature = 240 temperature = 240 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material + +[filament:Filamentworld ABS @XL] +inherits = Filamentworld ABS @PG; *ABSXL* + +[filament:Filamentworld ABS @XL 0.6] +inherits = Filamentworld ABS @PG 0.6; *ABS06XL* + +[filament:Filamentworld ABS @XL 0.8] +inherits = Filamentworld ABS @PG 0.8; *ABS08XL* [filament:Filamentworld ABS @MINI] inherits = Filamentworld ABS @@ -6868,6 +7314,15 @@ inherits = Filamentworld PETG @PG; *PET08PG* first_layer_temperature = 240 temperature = 245 +[filament:Filamentworld PETG @XL] +inherits = Filamentworld PETG @PG; *PETXL* + +[filament:Filamentworld PETG @XL 0.6] +inherits = Filamentworld PETG @PG 0.6; *PET06XL* + +[filament:Filamentworld PETG @XL 0.8] +inherits = Filamentworld PETG @PG 0.8; *PET08XL* + [filament:Filamentworld PETG @MINI] inherits = Filamentworld PETG filament_retract_length = nil @@ -6900,6 +7355,15 @@ inherits = Filamentworld PLA; *PLA06PG* [filament:Filamentworld PLA @PG 0.8] inherits = Filamentworld PLA; *PLA08PG* +[filament:Filamentworld PLA @XL] +inherits = Filamentworld PLA @PG; *PLAXL* + +[filament:Filamentworld PLA @XL 0.6] +inherits = Filamentworld PLA @PG 0.6; *PLA06XL* + +[filament:Filamentworld PLA @XL 0.8] +inherits = Filamentworld PLA @PG 0.8; *PLA08XL* + [filament:Filament PM PETG] inherits = *PET* renamed_from = "Plasty Mladec PETG" @@ -6918,6 +7382,15 @@ inherits = Filament PM PETG; *PET06PG* [filament:Filament PM PETG @PG 0.8] inherits = Filament PM PETG; *PET08PG* +[filament:Filament PM PETG @XL] +inherits = Filament PM PETG @PG; *PETXL* + +[filament:Filament PM PETG @XL 0.6] +inherits = Filament PM PETG @PG 0.6; *PET06XL* + +[filament:Filament PM PETG @XL 0.8] +inherits = Filament PM PETG @PG 0.8; *PET08XL* + [filament:Generic PLA] inherits = *PLA* filament_vendor = Generic @@ -6927,18 +7400,27 @@ compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_notes!~/.*PG [filament:Generic PLA @PG] inherits = Generic PLA; *PLAPG* -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model!="MK4IS" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and printer_model!="MK4IS" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 [filament:Generic PLA @PG 0.6] inherits = Generic PLA; *PLA06PG* filament_max_volumetric_speed = 15 -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model!="MK4IS" and nozzle_diameter[0]==0.6 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and printer_model!="MK4IS" and nozzle_diameter[0]==0.6 [filament:Generic PLA @PG 0.8] inherits = Generic PLA; *PLA08PG* first_layer_temperature = 220 temperature = 220 -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model!="MK4IS" and nozzle_diameter[0]==0.8 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and printer_model!="MK4IS" and nozzle_diameter[0]==0.8 + +[filament:Generic PLA @XL] +inherits = Generic PLA @PG; *PLAXL* + +[filament:Generic PLA @XL 0.6] +inherits = Generic PLA @PG 0.6; *PLA06XL* + +[filament:Generic PLA @XL 0.8] +inherits = Generic PLA @PG 0.8; *PLA08XL* [filament:Generic PLA @PGIS] inherits = Generic PLA @PG @@ -6946,19 +7428,19 @@ renamed_from = "Generic PLA @MK4IS" first_layer_temperature = 230 temperature = 220 slowdown_below_layer_time = 6 -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 [filament:Generic PLA @PGIS 0.6] inherits = Generic PLA @PG 0.6 first_layer_temperature = 230 temperature = 220 -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]==0.6 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.6 [filament:Generic PLA @PGIS 0.8] inherits = Generic PLA @PG 0.8 first_layer_temperature = 230 temperature = 225 -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]==0.8 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.8 [filament:Generic PLA Silk @PGIS] inherits = Generic PLA @PG @@ -6968,19 +7450,19 @@ temperature = 225 slowdown_below_layer_time = 8 filament_max_volumetric_speed = 7 start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.4}0.05{elsif nozzle_diameter[0]==0.25}0.14{elsif nozzle_diameter[0]==0.3}0.07{elsif nozzle_diameter[0]==0.35}0.06{elsif nozzle_diameter[0]==0.6}0.03{elsif nozzle_diameter[0]==0.5}0.035{elsif nozzle_diameter[0]==0.8}0.015{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*PRINTER_MODEL_MK4IS.*/}\nM572 S{if nozzle_diameter[0]==0.4}0.035{elsif nozzle_diameter[0]==0.5}0.022{elsif nozzle_diameter[0]==0.6}0.018{elsif nozzle_diameter[0]==0.8}0.012{elsif nozzle_diameter[0]==0.25}0.12{elsif nozzle_diameter[0]==0.3}0.075{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S36 ; set heatbreak target temp" -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 [filament:Generic PLA Silk @PGIS 0.6] inherits = Generic PLA Silk @PGIS slowdown_below_layer_time = 15 filament_max_volumetric_speed = 9 -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]==0.6 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.6 [filament:Generic PLA Silk @PGIS 0.8] inherits = Generic PLA Silk @PGIS slowdown_below_layer_time = 20 filament_max_volumetric_speed = 12 -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]==0.8 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.8 [filament:3D-Fuel Standard PLA] inherits = *PLA* @@ -7005,6 +7487,15 @@ filament_max_volumetric_speed = 16 first_layer_temperature = 210 temperature = 210 +[filament:3D-Fuel Standard PLA @XL] +inherits = 3D-Fuel Standard PLA @PG; *PLAXL* + +[filament:3D-Fuel Standard PLA @XL 0.6] +inherits = 3D-Fuel Standard PLA @PG 0.6; *PLA06XL* + +[filament:3D-Fuel Standard PLA @XL 0.8] +inherits = 3D-Fuel Standard PLA @PG 0.8; *PLA08XL* + [filament:3D-Fuel EasiPrint PLA] inherits = 3D-Fuel Standard PLA filament_cost = 30.44 @@ -7023,6 +7514,15 @@ filament_max_volumetric_speed = 16 first_layer_temperature = 210 temperature = 210 +[filament:3D-Fuel EasiPrint PLA @XL] +inherits = 3D-Fuel EasiPrint PLA @PG; *PLAXL* + +[filament:3D-Fuel EasiPrint PLA @XL 0.6] +inherits = 3D-Fuel EasiPrint PLA @PG 0.6; *PLA06XL* + +[filament:3D-Fuel EasiPrint PLA @XL 0.8] +inherits = 3D-Fuel EasiPrint PLA @PG 0.8; *PLA08XL* + [filament:3D-Fuel Pro PLA] inherits = *PLA* filament_vendor = 3D-Fuel @@ -7047,6 +7547,15 @@ filament_max_volumetric_speed = 17 first_layer_temperature = 225 temperature = 225 +[filament:3D-Fuel Pro PLA @XL] +inherits = 3D-Fuel Pro PLA @PG; *PLAXL* + +[filament:3D-Fuel Pro PLA @XL 0.6] +inherits = 3D-Fuel Pro PLA @PG 0.6; *PLA06XL* + +[filament:3D-Fuel Pro PLA @XL 0.8] +inherits = 3D-Fuel Pro PLA @PG 0.8; *PLA08XL* + [filament:3D-Fuel Buzzed] inherits = 3D-Fuel Standard PLA filament_cost = 44.27 @@ -7069,6 +7578,15 @@ filament_max_volumetric_speed = 12 first_layer_temperature = 210 temperature = 210 +[filament:3D-Fuel Buzzed @XL] +inherits = 3D-Fuel Buzzed @PG; *PLAXL* + +[filament:3D-Fuel Buzzed @XL 0.6] +inherits = 3D-Fuel Buzzed @PG 0.6; *PLA06XL* + +[filament:3D-Fuel Buzzed @XL 0.8] +inherits = 3D-Fuel Buzzed @PG 0.8; *PLA08XL* + [filament:3D-Fuel Wound up] inherits = 3D-Fuel Buzzed filament_cost = 44.27 @@ -7091,30 +7609,42 @@ filament_max_volumetric_speed = 12 first_layer_temperature = 220 temperature = 220 +[filament:3D-Fuel Wound up @XL] +inherits = 3D-Fuel Wound up @PG; *PLAXL* + +[filament:3D-Fuel Wound up @XL 0.6] +inherits = 3D-Fuel Wound up @PG 0.6; *PLA06XL* + +[filament:3D-Fuel Wound up @XL 0.8] +inherits = 3D-Fuel Wound up @PG 0.8; *PLA08XL* + [filament:3D-Fuel Workday ABS] inherits = *ABSC* filament_vendor = 3D-Fuel filament_cost = 23.25 filament_density = 1.04 -[filament:3D-Fuel Workday ABS @PG] -inherits = 3D-Fuel Workday ABS; *ABSPG* +[filament:3D-Fuel Workday ABS @XL] +inherits = 3D-Fuel Workday ABS; *ABSPG*; *ABSXL* +renamed_from = "3D-Fuel Workday ABS @PG" bed_temperature = 105 -[filament:3D-Fuel Workday ABS @PG 0.6] -inherits = 3D-Fuel Workday ABS @PG; *ABS06PG* +[filament:3D-Fuel Workday ABS @XL 0.6] +inherits = 3D-Fuel Workday ABS @XL; *ABS06XL* +renamed_from = "3D-Fuel Workday ABS @PG 0.6" -[filament:3D-Fuel Workday ABS @PG 0.8] -inherits = 3D-Fuel Workday ABS @PG; *ABS08PG* +[filament:3D-Fuel Workday ABS @XL 0.8] +inherits = 3D-Fuel Workday ABS @XL; *ABS08XL* +renamed_from = "3D-Fuel Workday ABS @PG 0.8" [filament:3D-Fuel Workday ABS @MK4] -inherits = 3D-Fuel Workday ABS; *ABSMK4* +inherits = 3D-Fuel Workday ABS; *ABSPG* [filament:3D-Fuel Workday ABS @MK4 0.6] -inherits = 3D-Fuel Workday ABS @MK4; *ABS06MK4* +inherits = 3D-Fuel Workday ABS @MK4; *ABS06PG* [filament:3D-Fuel Workday ABS @MK4 0.8] -inherits = 3D-Fuel Workday ABS @MK4; *ABS08MK4* +inherits = 3D-Fuel Workday ABS @MK4; *ABS08PG* [filament:3D-Fuel Workday ABS @MINI] inherits = 3D-Fuel Workday ABS; *ABSMINI* @@ -7138,6 +7668,15 @@ filament_max_volumetric_speed = 14 inherits = Jessie PLA @PG; *PLA08PG* filament_max_volumetric_speed = 17 +[filament:Jessie PLA @XL] +inherits = Jessie PLA @PG; *PLAXL* + +[filament:Jessie PLA @XL 0.6] +inherits = Jessie PLA @PG 0.6; *PLA06XL* + +[filament:Jessie PLA @XL 0.8] +inherits = Jessie PLA @PG 0.8; *PLA08XL* + [filament:Jessie PETG] inherits = *PET* filament_vendor = Printed Solid @@ -7151,7 +7690,7 @@ filament_max_volumetric_speed = 7 [filament:Jessie PETG @PG] inherits = Jessie PETG; *PETPG* -filament_max_volumetric_speed = 7 +filament_max_volumetric_speed = 8 [filament:Jessie PETG @PG 0.6] inherits = Jessie PETG @PG; *PET06PG* @@ -7163,6 +7702,15 @@ filament_max_volumetric_speed = 20 first_layer_temperature = 245 temperature = 255 +[filament:Jessie PETG @XL] +inherits = Jessie PETG @PG; *PETXL* + +[filament:Jessie PETG @XL 0.6] +inherits = Jessie PETG @PG 0.6; *PET06XL* + +[filament:Jessie PETG @XL 0.8] +inherits = Jessie PETG @PG 0.8; *PET08XL* + [filament:Jessie PETG @MINI] inherits = Jessie PETG; *PETMINI* @@ -7182,6 +7730,15 @@ inherits = Devil Design PLA; *PLA06PG* [filament:Devil Design PLA @PG 0.8] inherits = Devil Design PLA; *PLA08PG* +[filament:Devil Design PLA @XL] +inherits = Devil Design PLA @PG; *PLAXL* + +[filament:Devil Design PLA @XL 0.6] +inherits = Devil Design PLA @PG 0.6; *PLA06XL* + +[filament:Devil Design PLA @XL 0.8] +inherits = Devil Design PLA @PG 0.8; *PLA08XL* + [filament:Devil Design PETG] inherits = *PET* filament_vendor = Devil Design @@ -7202,6 +7759,15 @@ inherits = Devil Design PETG; *PET06PG* [filament:Devil Design PETG @PG 0.8] inherits = Devil Design PETG; *PET08PG* +[filament:Devil Design PETG @XL] +inherits = Devil Design PETG @PG; *PETXL* + +[filament:Devil Design PETG @XL 0.6] +inherits = Devil Design PETG @PG 0.6; *PET06XL* + +[filament:Devil Design PETG @XL 0.8] +inherits = Devil Design PETG @PG 0.8; *PET08XL* + [filament:Spectrum PLA] inherits = *PLA* filament_vendor = Spectrum @@ -7217,6 +7783,15 @@ inherits = Spectrum PLA; *PLA06PG* [filament:Spectrum PLA @PG 0.8] inherits = Spectrum PLA; *PLA08PG* +[filament:Spectrum PLA @XL] +inherits = Spectrum PLA @PG; *PLAXL* + +[filament:Spectrum PLA @XL 0.6] +inherits = Spectrum PLA @PG 0.6; *PLA06XL* + +[filament:Spectrum PLA @XL 0.8] +inherits = Spectrum PLA @PG 0.8; *PLA08XL* + [filament:Spectrum PETG Matt] inherits = *PET* filament_vendor = Spectrum @@ -7246,6 +7821,15 @@ inherits = Spectrum PETG Matt @PG; *PET06PG* [filament:Spectrum PETG Matt @PG 0.8] inherits = Spectrum PETG Matt @PG; *PET08PG* +[filament:Spectrum PETG Matt @XL] +inherits = Spectrum PETG Matt @PG; *PETXL* + +[filament:Spectrum PETG Matt @XL 0.6] +inherits = Spectrum PETG Matt @PG 0.6; *PET06XL* + +[filament:Spectrum PETG Matt @XL 0.8] +inherits = Spectrum PETG Matt @PG 0.8; *PET08XL* + [filament:Spectrum PETG Matt @MINI] inherits = Spectrum PETG Matt; *PETMINI* @@ -7278,6 +7862,15 @@ inherits = Spectrum PETG HT100 @PG; *PET06PG* [filament:Spectrum PETG HT100 @PG 0.8] inherits = Spectrum PETG HT100 @PG; *PET08PG* +[filament:Spectrum PETG HT100 @XL] +inherits = Spectrum PETG HT100 @PG; *PETXL* + +[filament:Spectrum PETG HT100 @XL 0.6] +inherits = Spectrum PETG HT100 @PG 0.6; *PET06XL* + +[filament:Spectrum PETG HT100 @XL 0.8] +inherits = Spectrum PETG HT100 @PG 0.8; *PET08XL* + [filament:Spectrum PETG HT100 @MINI] inherits = Spectrum PETG HT100; *PETMINI* bed_temperature = 100 @@ -7312,6 +7905,15 @@ inherits = Spectrum GreenyHT @PG; *PLA06PG* [filament:Spectrum GreenyHT @PG 0.8] inherits = Spectrum GreenyHT @PG; *PLA08PG* +[filament:Spectrum GreenyHT @XL] +inherits = Spectrum GreenyHT @PG; *PLAXL* + +[filament:Spectrum GreenyHT @XL 0.6] +inherits = Spectrum GreenyHT @PG 0.6; *PLA06XL* + +[filament:Spectrum GreenyHT @XL 0.8] +inherits = Spectrum GreenyHT @PG 0.8; *PLA08XL* + [filament:Spectrum ASA 275] inherits = *ABSC* filament_vendor = Spectrum @@ -7325,15 +7927,21 @@ filament_density = 1.24 [filament:Spectrum ASA 275 @PG] inherits = Spectrum ASA 275; *ABSPG* -compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Spectrum ASA 275 @PG 0.6] inherits = Spectrum ASA 275 @PG; *ABS06PG* -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Spectrum ASA 275 @PG 0.8] inherits = Spectrum ASA 275 @PG; *ABS08PG* -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material + +[filament:Spectrum ASA 275 @XL] +inherits = Spectrum ASA 275; *ABSPG*; *ABSXL* + +[filament:Spectrum ASA 275 @XL 0.6] +inherits = Spectrum ASA 275 @XL; *ABS06XL* + +[filament:Spectrum ASA 275 @XL 0.8] +inherits = Spectrum ASA 275 @XL; *ABS08XL* [filament:Spectrum ASA 275 @MINI] inherits = Spectrum ASA 275; *ABSMINI* @@ -7351,25 +7959,26 @@ filament_type = ASA filament_density = 1.24 compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) -[filament:Spectrum ASA Kevlar @PG] -inherits = Spectrum ASA Kevlar; *ABSPG* -compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_model=~/.*XL.*/ +[filament:Spectrum ASA Kevlar @XL] +inherits = Spectrum ASA Kevlar; *ABSPG*; *ABSXL* +renamed_from = "Spectrum ASA Kevlar @PG" -[filament:Spectrum ASA Kevlar @PG 0.6] -inherits = Spectrum ASA Kevlar @PG; *ABS06PG* +[filament:Spectrum ASA Kevlar @XL 0.6] +inherits = Spectrum ASA Kevlar @XL; *ABS06XL* +renamed_from = "Spectrum ASA Kevlar @PG 0.6" -[filament:Spectrum ASA Kevlar @PG 0.8] -inherits = Spectrum ASA Kevlar @PG; *ABS08PG* +[filament:Spectrum ASA Kevlar @XL 0.8] +inherits = Spectrum ASA Kevlar @XL; *ABS08XL* +renamed_from = "Spectrum ASA Kevlar @PG 0.8" [filament:Spectrum ASA Kevlar @MK4] -inherits = Spectrum ASA Kevlar; *ABSMK4* -compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_model=~/(MK4|MK4IS)/ +inherits = Spectrum ASA Kevlar; *ABSPG* [filament:Spectrum ASA Kevlar @MK4 0.6] -inherits = Spectrum ASA Kevlar @MK4; *ABS06MK4* +inherits = Spectrum ASA Kevlar @MK4; *ABS06PG* [filament:Spectrum ASA Kevlar @MK4 0.8] -inherits = Spectrum ASA Kevlar @MK4; *ABS08MK4* +inherits = Spectrum ASA Kevlar @MK4; *ABS08PG* [filament:Spectrum ASA Kevlar @MINI] inherits = Spectrum ASA Kevlar; *ABSMINI* @@ -7396,6 +8005,15 @@ inherits = Spectrum Tough PLA @PG; *PLA06PG* [filament:Spectrum Tough PLA @PG 0.8] inherits = Spectrum Tough PLA @PG; *PLA08PG* +[filament:Spectrum Tough PLA @XL] +inherits = Spectrum Tough PLA @PG; *PLAXL* + +[filament:Spectrum Tough PLA @XL 0.6] +inherits = Spectrum Tough PLA @PG 0.6; *PLA06XL* + +[filament:Spectrum Tough PLA @XL 0.8] +inherits = Spectrum Tough PLA @PG 0.8; *PLA08XL* + [filament:Spectrum PLA PRO] inherits = *PLA* filament_vendor = Spectrum @@ -7411,6 +8029,15 @@ inherits = Spectrum PLA PRO @PG; *PLA06PG* [filament:Spectrum PLA PRO @PG 0.8] inherits = Spectrum PLA PRO @PG; *PLA08PG* +[filament:Spectrum PLA PRO @XL] +inherits = Spectrum PLA PRO @PG; *PLAXL* + +[filament:Spectrum PLA PRO @XL 0.6] +inherits = Spectrum PLA PRO @PG 0.6; *PLA06XL* + +[filament:Spectrum PLA PRO @XL 0.8] +inherits = Spectrum PLA PRO @PG 0.8; *PLA08XL* + [filament:Spectrum PCTG] inherits = *PET* filament_vendor = Spectrum @@ -7428,6 +8055,15 @@ inherits = Spectrum PCTG @PG; *PET06PG* [filament:Spectrum PCTG @PG 0.8] inherits = Spectrum PCTG @PG; *PET08PG* +[filament:Spectrum PCTG @XL] +inherits = Spectrum PCTG @PG; *PETXL* + +[filament:Spectrum PCTG @XL 0.6] +inherits = Spectrum PCTG @PG 0.6; *PET06XL* + +[filament:Spectrum PCTG @XL 0.8] +inherits = Spectrum PCTG @PG 0.8; *PET08XL* + [filament:Spectrum PCTG @MINI] inherits = Spectrum PCTG; *PETMINI* @@ -7442,7 +8078,33 @@ filament_retract_speed = nil filament_retract_lift = nil compatible_printers_condition = nozzle_diameter[0]>=0.35 and nozzle_diameter[0]!=0.8 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material -[filament:Generic FLEX @PG] +[filament:Generic FLEX @XL] +inherits = Generic FLEX; *FLEXXL* +filament_max_volumetric_speed = 3 +filament_retract_length = 2.5 +fan_always_on = 1 +min_fan_speed = 30 +max_fan_speed = 30 +cooling = 1 +filament_retract_lift = 0 +slowdown_below_layer_time = 10 +first_layer_temperature = 230 +temperature = 230 +extrusion_multiplier = 1.08 +filament_multitool_ramming_flow = 3 +filament_retract_length_toolchange = 2.5 + +[filament:Generic FLEX @XL 0.6] +inherits = Generic FLEX @XL; *FLEX06XL* +filament_max_volumetric_speed = 4 +filament_multitool_ramming_flow = 4 + +[filament:Generic FLEX @XL 0.8] +inherits = Generic FLEX @XL; *FLEX08XL* +filament_max_volumetric_speed = 8 +filament_multitool_ramming_flow = 8 + +[filament:Generic FLEX @MK4] inherits = Generic FLEX; *FLEXPG* filament_max_volumetric_speed = 3 filament_retract_length = 2.5 @@ -7456,34 +8118,12 @@ first_layer_temperature = 230 temperature = 230 extrusion_multiplier = 1.08 -[filament:Generic FLEX @PG 0.6] -inherits = Generic FLEX @PG; *FLEX06PG* -filament_max_volumetric_speed = 4 - -[filament:Generic FLEX @PG 0.8] -inherits = Generic FLEX @PG; *FLEX08PG* -filament_max_volumetric_speed = 8 - -[filament:Generic FLEX @MK4] -inherits = Generic FLEX; *FLEXMK4* -filament_max_volumetric_speed = 3 -filament_retract_length = 2.5 -fan_always_on = 1 -min_fan_speed = 30 -max_fan_speed = 30 -cooling = 1 -filament_retract_lift = 0 -slowdown_below_layer_time = 10 -first_layer_temperature = 230 -temperature = 230 -extrusion_multiplier = 1.08 - [filament:Generic FLEX @MK4 0.6] -inherits = Generic FLEX @MK4; *FLEX06MK4* +inherits = Generic FLEX @MK4; *FLEX06PG* filament_max_volumetric_speed = 6 [filament:Generic FLEX @MK4 0.8] -inherits = Generic FLEX @MK4; *FLEX08MK4* +inherits = Generic FLEX @MK4; *FLEX08PG* filament_max_volumetric_speed = 9 [filament:Fillamentum Flexfill 92A] @@ -7503,32 +8143,36 @@ min_fan_speed = 60 disable_fan_first_layers = 4 full_fan_speed_layer = 6 -[filament:Fillamentum Flexfill 92A @PG] -inherits = Fillamentum Flexfill 92A; *FLEXPG* +[filament:Fillamentum Flexfill 92A @XL] +inherits = Fillamentum Flexfill 92A; *FLEXXL* filament_max_volumetric_speed = 3 extrusion_multiplier = 1.1 filament_retract_length = 3.5 +filament_multitool_ramming_flow = 3 +filament_retract_length_toolchange = 3.5 -[filament:Fillamentum Flexfill 92A @PG 0.6] -inherits = Fillamentum Flexfill 92A @PG; *FLEX06PG* +[filament:Fillamentum Flexfill 92A @XL 0.6] +inherits = Fillamentum Flexfill 92A @XL; *FLEX06XL* filament_max_volumetric_speed = 4.5 +filament_multitool_ramming_flow = 4.5 -[filament:Fillamentum Flexfill 92A @PG 0.8] -inherits = Fillamentum Flexfill 92A @PG; *FLEX08PG* +[filament:Fillamentum Flexfill 92A @XL 0.8] +inherits = Fillamentum Flexfill 92A @XL; *FLEX08XL* filament_max_volumetric_speed = 8 +filament_multitool_ramming_flow = 8 [filament:Fillamentum Flexfill 92A @MK4] -inherits = Fillamentum Flexfill 92A; *FLEXMK4* +inherits = Fillamentum Flexfill 92A; *FLEXPG* filament_max_volumetric_speed = 3.5 extrusion_multiplier = 1.1 filament_retract_length = 3.5 [filament:Fillamentum Flexfill 92A @MK4 0.6] -inherits = Fillamentum Flexfill 92A @MK4; *FLEX06MK4* +inherits = Fillamentum Flexfill 92A @MK4; *FLEX06PG* filament_max_volumetric_speed = 6.5 [filament:Fillamentum Flexfill 92A @MK4 0.8] -inherits = Fillamentum Flexfill 92A @MK4; *FLEX08MK4* +inherits = Fillamentum Flexfill 92A @MK4; *FLEX08PG* filament_max_volumetric_speed = 10 [filament:AmazonBasics TPU] @@ -7558,30 +8202,33 @@ min_print_speed = 15 slowdown_below_layer_time = 10 cooling = 1 -[filament:AmazonBasics TPU @PG] +[filament:AmazonBasics TPU @XL] +inherits = AmazonBasics TPU; *FLEXXL* +filament_retract_length = 2.5 +extrusion_multiplier = 1.1 +filament_retract_length_toolchange = 2.5 + +[filament:AmazonBasics TPU @XL 0.6] +inherits = AmazonBasics TPU @XL; *FLEX06XL* +filament_max_volumetric_speed = 5 +filament_multitool_ramming_flow = 5 + +[filament:AmazonBasics TPU @XL 0.8] +inherits = AmazonBasics TPU @XL; *FLEX08XL* +filament_max_volumetric_speed = 8 +filament_multitool_ramming_flow = 8 + +[filament:AmazonBasics TPU @MK4] inherits = AmazonBasics TPU; *FLEXPG* filament_retract_length = 2.5 extrusion_multiplier = 1.1 -[filament:AmazonBasics TPU @PG 0.6] -inherits = AmazonBasics TPU @PG; *FLEX06PG* -filament_max_volumetric_speed = 5 - -[filament:AmazonBasics TPU @PG 0.8] -inherits = AmazonBasics TPU @PG; *FLEX08PG* -filament_max_volumetric_speed = 8 - -[filament:AmazonBasics TPU @MK4] -inherits = AmazonBasics TPU; *FLEXMK4* -filament_retract_length = 2.5 -extrusion_multiplier = 1.1 - [filament:AmazonBasics TPU @MK4 0.6] -inherits = AmazonBasics TPU @MK4; *FLEX06MK4* +inherits = AmazonBasics TPU @MK4; *FLEX06PG* filament_max_volumetric_speed = 6.5 [filament:AmazonBasics TPU @MK4 0.8] -inherits = AmazonBasics TPU @MK4; *FLEX08MK4* +inherits = AmazonBasics TPU @MK4; *FLEX08PG* filament_max_volumetric_speed = 10 [filament:SainSmart TPU] @@ -7611,34 +8258,38 @@ min_print_speed = 15 slowdown_below_layer_time = 10 cooling = 1 -[filament:SainSmart TPU @PG] +[filament:SainSmart TPU @XL] +inherits = SainSmart TPU; *FLEXXL* +filament_max_volumetric_speed = 5 +first_layer_temperature = 235 +temperature = 235 +filament_retract_length = 1.5 +filament_multitool_ramming_flow = 5 +filament_retract_length_toolchange = 1.5 + +[filament:SainSmart TPU @XL 0.6] +inherits = SainSmart TPU @XL; *FLEX06XL* +filament_max_volumetric_speed = 6 +filament_multitool_ramming_flow = 6 + +[filament:SainSmart TPU @XL 0.8] +inherits = SainSmart TPU @XL; *FLEX08XL* +filament_max_volumetric_speed = 9 +filament_multitool_ramming_flow = 9 + +[filament:SainSmart TPU @MK4] inherits = SainSmart TPU; *FLEXPG* filament_max_volumetric_speed = 5 first_layer_temperature = 235 temperature = 235 filament_retract_length = 1.5 -[filament:SainSmart TPU @PG 0.6] -inherits = SainSmart TPU @PG; *FLEX06PG* -filament_max_volumetric_speed = 6 - -[filament:SainSmart TPU @PG 0.8] -inherits = SainSmart TPU @PG; *FLEX08PG* -filament_max_volumetric_speed = 9 - -[filament:SainSmart TPU @MK4] -inherits = SainSmart TPU; *FLEXMK4* -filament_max_volumetric_speed = 5 -first_layer_temperature = 235 -temperature = 235 -filament_retract_length = 1.5 - [filament:SainSmart TPU @MK4 0.6] -inherits = SainSmart TPU @MK4; *FLEX06MK4* +inherits = SainSmart TPU @MK4; *FLEX06PG* filament_max_volumetric_speed = 7 [filament:SainSmart TPU @MK4 0.8] -inherits = SainSmart TPU @MK4; *FLEX08MK4* +inherits = SainSmart TPU @MK4; *FLEX08PG* filament_max_volumetric_speed = 10 [filament:NinjaTek NinjaFlex TPU] @@ -7668,32 +8319,36 @@ min_print_speed = 10 slowdown_below_layer_time = 10 cooling = 1 -[filament:NinjaTek NinjaFlex TPU @PG] -inherits = NinjaTek NinjaFlex TPU; *FLEXPG* +[filament:NinjaTek NinjaFlex TPU @XL] +inherits = NinjaTek NinjaFlex TPU; *FLEXXL* filament_max_volumetric_speed = 3 filament_retract_length = 3.5 extrusion_multiplier = 1.12 +filament_multitool_ramming_flow = 3 +filament_retract_length_toolchange = 3.5 -[filament:NinjaTek NinjaFlex TPU @PG 0.6] -inherits = NinjaTek NinjaFlex TPU @PG; *FLEX06PG* +[filament:NinjaTek NinjaFlex TPU @XL 0.6] +inherits = NinjaTek NinjaFlex TPU @XL; *FLEX06XL* filament_max_volumetric_speed = 4.5 +filament_multitool_ramming_flow = 4.5 -[filament:NinjaTek NinjaFlex TPU @PG 0.8] -inherits = NinjaTek NinjaFlex TPU @PG; *FLEX08PG* +[filament:NinjaTek NinjaFlex TPU @XL 0.8] +inherits = NinjaTek NinjaFlex TPU @XL; *FLEX08XL* filament_max_volumetric_speed = 8 +filament_multitool_ramming_flow = 8 [filament:NinjaTek NinjaFlex TPU @MK4] -inherits = NinjaTek NinjaFlex TPU; *FLEXMK4* +inherits = NinjaTek NinjaFlex TPU; *FLEXPG* filament_max_volumetric_speed = 3.5 filament_retract_length = 3.5 extrusion_multiplier = 1.12 [filament:NinjaTek NinjaFlex TPU @MK4 0.6] -inherits = NinjaTek NinjaFlex TPU @MK4; *FLEX06MK4* +inherits = NinjaTek NinjaFlex TPU @MK4; *FLEX06PG* filament_max_volumetric_speed = 6.5 [filament:NinjaTek NinjaFlex TPU @MK4 0.8] -inherits = NinjaTek NinjaFlex TPU @MK4; *FLEX08MK4* +inherits = NinjaTek NinjaFlex TPU @MK4; *FLEX08PG* filament_max_volumetric_speed = 10 [filament:NinjaTek Cheetah TPU] @@ -7707,30 +8362,34 @@ filament_deretract_speed = 25 first_layer_temperature = 240 temperature = 240 -[filament:NinjaTek Cheetah TPU @PG] -inherits = NinjaTek Cheetah TPU; *FLEXPG* +[filament:NinjaTek Cheetah TPU @XL] +inherits = NinjaTek Cheetah TPU; *FLEXXL* filament_max_volumetric_speed = 5 filament_retract_length = 2.2 +filament_multitool_ramming_flow = 5 +filament_retract_length_toolchange = 2.2 -[filament:NinjaTek Cheetah TPU @PG 0.6] -inherits = NinjaTek Cheetah TPU @PG; *FLEX06PG* +[filament:NinjaTek Cheetah TPU @XL 0.6] +inherits = NinjaTek Cheetah TPU @XL; *FLEX06XL* filament_max_volumetric_speed = 7 +filament_multitool_ramming_flow = 7 -[filament:NinjaTek Cheetah TPU @PG 0.8] -inherits = NinjaTek Cheetah TPU @PG; *FLEX08PG* +[filament:NinjaTek Cheetah TPU @XL 0.8] +inherits = NinjaTek Cheetah TPU @XL; *FLEX08XL* filament_max_volumetric_speed = 10 +filament_multitool_ramming_flow = 10 [filament:NinjaTek Cheetah TPU @MK4] -inherits = NinjaTek Cheetah TPU; *FLEXMK4* +inherits = NinjaTek Cheetah TPU; *FLEXPG* filament_max_volumetric_speed = 6 filament_retract_length = 2.2 [filament:NinjaTek Cheetah TPU @MK4 0.6] -inherits = NinjaTek Cheetah TPU @MK4; *FLEX06MK4* +inherits = NinjaTek Cheetah TPU @MK4; *FLEX06PG* filament_max_volumetric_speed = 8 [filament:NinjaTek Cheetah TPU @MK4 0.8] -inherits = NinjaTek Cheetah TPU @MK4; *FLEX08MK4* +inherits = NinjaTek Cheetah TPU @MK4; *FLEX08PG* filament_max_volumetric_speed = 12 [filament:NinjaTek Cheetah TPU @MINI] @@ -7771,30 +8430,34 @@ min_print_speed = 15 slowdown_below_layer_time = 10 cooling = 1 -[filament:Filatech FilaFlex40 @PG] +[filament:Filatech FilaFlex40 @XL] +inherits = Filatech FilaFlex40; *FLEXXL* +filament_max_volumetric_speed = 4 +filament_retract_length = 2.5 +filament_multitool_ramming_flow = 4 +filament_retract_length_toolchange = 2.5 + +[filament:Filatech FilaFlex40 @XL 0.6] +inherits = Filatech FilaFlex40 @XL; *FLEX06XL* +filament_max_volumetric_speed = 5 +filament_multitool_ramming_flow = 5 + +[filament:Filatech FilaFlex40 @XL 0.8] +inherits = Filatech FilaFlex40 @XL; *FLEX08XL* +filament_max_volumetric_speed = 10 +filament_multitool_ramming_flow = 10 + +[filament:Filatech FilaFlex40 @MK4] inherits = Filatech FilaFlex40; *FLEXPG* filament_max_volumetric_speed = 4 filament_retract_length = 2.5 -[filament:Filatech FilaFlex40 @PG 0.6] -inherits = Filatech FilaFlex40 @PG; *FLEX06PG* -filament_max_volumetric_speed = 5 - -[filament:Filatech FilaFlex40 @PG 0.8] -inherits = Filatech FilaFlex40 @PG; *FLEX08PG* -filament_max_volumetric_speed = 10 - -[filament:Filatech FilaFlex40 @MK4] -inherits = Filatech FilaFlex40; *FLEXMK4* -filament_max_volumetric_speed = 4 -filament_retract_length = 2.5 - [filament:Filatech FilaFlex40 @MK4 0.6] -inherits = Filatech FilaFlex40 @MK4; *FLEX06MK4* +inherits = Filatech FilaFlex40 @MK4; *FLEX06PG* filament_max_volumetric_speed = 5 [filament:Filatech FilaFlex40 @MK4 0.8] -inherits = Filatech FilaFlex40 @MK4; *FLEX08MK4* +inherits = Filatech FilaFlex40 @MK4; *FLEX08PG* filament_max_volumetric_speed = 10 [filament:Filatech FilaFlex30] @@ -7804,30 +8467,34 @@ filament_density = 1.15 extrusion_multiplier = 1.1 filament_cost = -[filament:Filatech FilaFlex30 @PG] +[filament:Filatech FilaFlex30 @XL] +inherits = Filatech FilaFlex30; *FLEXXL* +filament_max_volumetric_speed = 3.5 +filament_retract_length = 3 +filament_multitool_ramming_flow = 3.5 +filament_retract_length_toolchange = 3 + +[filament:Filatech FilaFlex30 @XL 0.6] +inherits = Filatech FilaFlex30 @XL; *FLEX06XL* +filament_max_volumetric_speed = 5 +filament_multitool_ramming_flow = 5 + +[filament:Filatech FilaFlex30 @XL 0.8] +inherits = Filatech FilaFlex30 @XL; *FLEX08XL* +filament_max_volumetric_speed = 8 +filament_multitool_ramming_flow = 8 + +[filament:Filatech FilaFlex30 @MK4] inherits = Filatech FilaFlex30; *FLEXPG* filament_max_volumetric_speed = 3.5 filament_retract_length = 3 -[filament:Filatech FilaFlex30 @PG 0.6] -inherits = Filatech FilaFlex30 @PG; *FLEX06PG* -filament_max_volumetric_speed = 5 - -[filament:Filatech FilaFlex30 @PG 0.8] -inherits = Filatech FilaFlex30 @PG; *FLEX08PG* -filament_max_volumetric_speed = 8 - -[filament:Filatech FilaFlex30 @MK4] -inherits = Filatech FilaFlex30; *FLEXMK4* -filament_max_volumetric_speed = 3.5 -filament_retract_length = 3 - [filament:Filatech FilaFlex30 @MK4 0.6] -inherits = Filatech FilaFlex30 @MK4; *FLEX06MK4* +inherits = Filatech FilaFlex30 @MK4; *FLEX06PG* filament_max_volumetric_speed = 7 [filament:Filatech FilaFlex30 @MK4 0.8] -inherits = Filatech FilaFlex30 @MK4; *FLEX08MK4* +inherits = Filatech FilaFlex30 @MK4; *FLEX08PG* filament_max_volumetric_speed = 10 [filament:Filatech FilaFlex55] @@ -7870,32 +8537,36 @@ min_fan_speed = 80 fan_always_on = 1 temperature = 235 -[filament:Filatech TPU @PG] -inherits = Filatech TPU; *FLEXPG* +[filament:Filatech TPU @XL] +inherits = Filatech TPU; *FLEXXL* filament_max_volumetric_speed = 4.5 first_layer_temperature = 235 filament_retract_length = 2.2 +filament_multitool_ramming_flow = 4.5 +filament_retract_length_toolchange = 2.2 -[filament:Filatech TPU @PG 0.6] -inherits = Filatech TPU @PG; *FLEX06PG* +[filament:Filatech TPU @XL 0.6] +inherits = Filatech TPU @XL; *FLEX06XL* filament_max_volumetric_speed = 5 +filament_multitool_ramming_flow = 5 -[filament:Filatech TPU @PG 0.8] -inherits = Filatech TPU @PG; *FLEX08PG* +[filament:Filatech TPU @XL 0.8] +inherits = Filatech TPU @XL; *FLEX08XL* filament_max_volumetric_speed = 8 +filament_multitool_ramming_flow = 8 [filament:Filatech TPU @MK4] -inherits = Filatech TPU; *FLEXMK4* +inherits = Filatech TPU; *FLEXPG* filament_max_volumetric_speed = 5.5 first_layer_temperature = 235 filament_retract_length = 2.2 [filament:Filatech TPU @MK4 0.6] -inherits = Filatech TPU @MK4; *FLEX06MK4* +inherits = Filatech TPU @MK4; *FLEX06PG* filament_max_volumetric_speed = 7 [filament:Filatech TPU @MK4 0.8] -inherits = Filatech TPU @MK4; *FLEX08MK4* +inherits = Filatech TPU @MK4; *FLEX08PG* filament_max_volumetric_speed = 10 [filament:Filatech ABS] @@ -7905,24 +8576,27 @@ filament_cost = extrusion_multiplier = 0.95 filament_density = 1.05 -[filament:Filatech ABS @PG] -inherits = Filatech ABS; *ABSPG* +[filament:Filatech ABS @XL] +inherits = Filatech ABS; *ABSPG*; *ABSXL* +renamed_from = "Filatech ABS @PG" bed_temperature = 105 -[filament:Filatech ABS @PG 0.6] -inherits = Filatech ABS @PG; *ABS06PG* +[filament:Filatech ABS @XL 0.6] +inherits = Filatech ABS @XL; *ABS06XL* +renamed_from = "Filatech ABS @PG 0.6" -[filament:Filatech ABS @PG 0.8] -inherits = Filatech ABS @PG; *ABS08PG* +[filament:Filatech ABS @XL 0.8] +inherits = Filatech ABS @XL; *ABS08XL* +renamed_from = "Filatech ABS @PG 0.8" [filament:Filatech ABS @MK4] -inherits = Filatech ABS; *ABSMK4* +inherits = Filatech ABS; *ABSPG* [filament:Filatech ABS @MK4 0.6] -inherits = Filatech ABS @MK4; *ABS06MK4* +inherits = Filatech ABS @MK4; *ABS06PG* [filament:Filatech ABS @MK4 0.8] -inherits = Filatech ABS @MK4; *ABS08MK4* +inherits = Filatech ABS @MK4; *ABS08PG* [filament:Filatech ABS @MINI] inherits = Filatech ABS; *ABSMINI* @@ -7940,15 +8614,21 @@ compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MINI [filament:Filatech FilaCarbon @PG] inherits = Filatech FilaCarbon; *ABSPG*; *04PLUSPG* first_layer_bed_temperature = 100 -compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Filatech FilaCarbon @PG 0.6] inherits = Filatech FilaCarbon @PG; *ABS06PG* -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Filatech FilaCarbon @PG 0.8] inherits = Filatech FilaCarbon @PG; *ABS08PG* -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material + +[filament:Filatech FilaCarbon @XL] +inherits = Filatech FilaCarbon @PG; *ABSXL*; *04PLUSXL* + +[filament:Filatech FilaCarbon @XL 0.6] +inherits = Filatech FilaCarbon @PG 0.6; *ABS06XL* + +[filament:Filatech FilaCarbon @XL 0.8] +inherits = Filatech FilaCarbon @PG 0.8; *ABS08XL* [filament:Filatech FilaCarbon @MINI] inherits = Filatech FilaCarbon; *ABSMINI* @@ -7974,6 +8654,15 @@ inherits = Filatech FilaPLA; *PLA06PG* [filament:Filatech FilaPLA @PG 0.8] inherits = Filatech FilaPLA; *PLA08PG* +[filament:Filatech FilaPLA @XL] +inherits = Filatech FilaPLA @PG; *PLAXL* + +[filament:Filatech FilaPLA @XL 0.6] +inherits = Filatech FilaPLA @PG 0.6; *PLA06XL* + +[filament:Filatech FilaPLA @XL 0.8] +inherits = Filatech FilaPLA @PG 0.8; *PLA08XL* + [filament:Filatech PLA] inherits = *PLA* filament_vendor = Filatech @@ -7991,6 +8680,15 @@ inherits = Filatech PLA; *PLA06PG* [filament:Filatech PLA @PG 0.8] inherits = Filatech PLA; *PLA08PG* +[filament:Filatech PLA @XL] +inherits = Filatech PLA @PG; *PLAXL* + +[filament:Filatech PLA @XL 0.6] +inherits = Filatech PLA @PG 0.6; *PLA06XL* + +[filament:Filatech PLA @XL 0.8] +inherits = Filatech PLA @PG 0.8; *PLA08XL* + [filament:Filatech PLA+] inherits = Filatech PLA filament_density = 1.24 @@ -8004,6 +8702,15 @@ inherits = Filatech PLA+; *PLA06PG* [filament:Filatech PLA+ @PG 0.8] inherits = Filatech PLA+; *PLA08PG* +[filament:Filatech PLA+ @XL] +inherits = Filatech PLA+ @PG; *PLAXL* + +[filament:Filatech PLA+ @XL 0.6] +inherits = Filatech PLA+ @PG; *PLA06XL* + +[filament:Filatech PLA+ @XL 0.8] +inherits = Filatech PLA+ @PG; *PLA08XL* + [filament:Filatech FilaTough] inherits = Filatech ABS filament_cost = @@ -8017,15 +8724,21 @@ cooling = 0 [filament:Filatech FilaTough @PG] inherits = Filatech FilaTough; *ABSPG* -compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Filatech FilaTough @PG 0.6] inherits = Filatech FilaTough; *ABS06PG* -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Filatech FilaTough @PG 0.8] inherits = Filatech FilaTough; *ABS08PG* -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material + +[filament:Filatech FilaTough @XL] +inherits = Filatech FilaTough @PG; *ABSXL* + +[filament:Filatech FilaTough @XL 0.6] +inherits = Filatech FilaTough @PG 0.6; *ABS06XL* + +[filament:Filatech FilaTough @XL 0.8] +inherits = Filatech FilaTough @PG 0.8; *ABS08XL* [filament:Filatech HIPS] inherits = Prusa HIPS @@ -8039,8 +8752,9 @@ temperature = 225 bed_temperature = 110 compatible_printers_condition = printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) -[filament:Filatech HIPS @PG] -inherits = Filatech HIPS; *ABSPG* +[filament:Filatech HIPS @XL] +inherits = Filatech HIPS; *ABSPG*; *ABSXL* +renamed_from = "Filatech HIPS @PG" bridge_fan_speed = 50 cooling = 1 extrusion_multiplier = 1 @@ -8053,14 +8767,16 @@ min_fan_speed = 20 first_layer_bed_temperature = 100 bed_temperature = 105 -[filament:Filatech HIPS @PG 0.6] -inherits = Filatech HIPS @PG; *ABS06PG* +[filament:Filatech HIPS @XL 0.6] +inherits = Filatech HIPS @XL; *ABS06XL* +renamed_from = "Filatech HIPS @PG 0.6" -[filament:Filatech HIPS @PG 0.8] -inherits = Filatech HIPS @PG; *ABS08PG* +[filament:Filatech HIPS @XL 0.8] +inherits = Filatech HIPS @XL; *ABS08XL* +renamed_from = "Filatech HIPS @PG 0.8" [filament:Filatech HIPS @MK4] -inherits = Filatech HIPS; *ABSMK4* +inherits = Filatech HIPS; *ABSPG* bridge_fan_speed = 50 cooling = 1 extrusion_multiplier = 1 @@ -8072,10 +8788,10 @@ max_fan_speed = 20 min_fan_speed = 20 [filament:Filatech HIPS @MK4 0.6] -inherits = Filatech HIPS @MK4; *ABS06MK4* +inherits = Filatech HIPS @MK4; *ABS06PG* [filament:Filatech HIPS @MK4 0.8] -inherits = Filatech HIPS @MK4; *ABS08MK4* +inherits = Filatech HIPS @MK4; *ABS08PG* [filament:Filatech HIPS @MINI] inherits = Filatech HIPS; *ABSMINI* @@ -8096,30 +8812,33 @@ filament_type = PA filament_max_volumetric_speed = 8 compatible_printers_condition = printer_notes!~/.*PRINTER_MODEL_MK(2|2.5).*/ and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) -[filament:Filatech PA @PG] -inherits = Filatech PA; *ABSPG* +[filament:Filatech PA @XL] +inherits = Filatech PA; *ABSPG*; *ABSXL* +renamed_from = "Filatech PA @PG" first_layer_bed_temperature = 100 bed_temperature = 105 filament_max_volumetric_speed = 8 -[filament:Filatech PA @PG 0.6] -inherits = Filatech PA @PG; *ABS06PG* +[filament:Filatech PA @XL 0.6] +inherits = Filatech PA @XL; *ABS06XL* +renamed_from = "Filatech PA @PG 0.6" filament_max_volumetric_speed = 10 -[filament:Filatech PA @PG 0.8] -inherits = Filatech PA @PG; *ABS08PG* +[filament:Filatech PA @XL 0.8] +inherits = Filatech PA @XL; *ABS08XL* +renamed_from = "Filatech PA @PG 0.8" filament_max_volumetric_speed = 12 [filament:Filatech PA @MK4] -inherits = Filatech PA; *ABSMK4* +inherits = Filatech PA; *ABSPG* filament_max_volumetric_speed = 8 [filament:Filatech PA @MK4 0.6] -inherits = Filatech PA @MK4; *ABS06MK4* +inherits = Filatech PA @MK4; *ABS06PG* filament_max_volumetric_speed = 10 [filament:Filatech PA @MK4 0.8] -inherits = Filatech PA @MK4; *ABS08MK4* +inherits = Filatech PA @MK4; *ABS08PG* filament_max_volumetric_speed = 12 [filament:Filatech PA @MK2] @@ -8141,23 +8860,26 @@ bed_temperature = 115 filament_density = 1.2 filament_type = PC -[filament:Filatech PC @PG] -inherits = Filatech PC; *PCPG* +[filament:Filatech PC @XL] +inherits = Filatech PC; *PCPG*; *PCXL* +renamed_from = "Filatech PC @PG" -[filament:Filatech PC @PG 0.6] -inherits = Filatech PC @PG; *PC06PG* +[filament:Filatech PC @XL 0.6] +inherits = Filatech PC @XL; *PC06XL* +renamed_from = "Filatech PC @PG 0.6" -[filament:Filatech PC @PG 0.8] -inherits = Filatech PC @PG; *PC08PG* +[filament:Filatech PC @XL 0.8] +inherits = Filatech PC @XL; *PC08XL* +renamed_from = "Filatech PC @PG 0.8" [filament:Filatech PC @MK4] -inherits = Filatech PC; *PCMK4* +inherits = Filatech PC; *PCPG* [filament:Filatech PC @MK4 0.6] -inherits = Filatech PC @MK4; *PC06MK4* +inherits = Filatech PC @MK4; *PC06PG* [filament:Filatech PC @MK4 0.8] -inherits = Filatech PC @MK4; *PC08MK4* +inherits = Filatech PC @MK4; *PC08PG* [filament:Filatech PC @MK2] inherits = Filatech PC @@ -8178,23 +8900,26 @@ cooling = 1 extrusion_multiplier = 0.95 disable_fan_first_layers = 6 -[filament:Filatech PC-ABS @PG] -inherits = Filatech PC-ABS; *PCPG* +[filament:Filatech PC-ABS @XL] +inherits = Filatech PC-ABS; *PCPG*; *PCXL* +renamed_from = "Filatech PC-ABS @PG" -[filament:Filatech PC-ABS @PG 0.6] -inherits = Filatech PC-ABS; *PC06PG* +[filament:Filatech PC-ABS @XL 0.6] +inherits = Filatech PC-ABS @XL; *PC06XL* +renamed_from = "Filatech PC-ABS @PG 0.6" -[filament:Filatech PC-ABS @PG 0.8] -inherits = Filatech PC-ABS; *PC08PG* +[filament:Filatech PC-ABS @XL 0.8] +inherits = Filatech PC-ABS @XL; *PC08XL* +renamed_from = "Filatech PC-ABS @PG 0.8" [filament:Filatech PC-ABS @MK4] -inherits = Filatech PC-ABS; *PCMK4* +inherits = Filatech PC-ABS; *PCPG* [filament:Filatech PC-ABS @MK4 0.6] -inherits = Filatech PC-ABS @MK4; *PC06MK4* +inherits = Filatech PC-ABS @MK4; *PC06PG* [filament:Filatech PC-ABS @MK4 0.8] -inherits = Filatech PC-ABS @MK4; *PC08MK4* +inherits = Filatech PC-ABS @MK4; *PC08PG* [filament:Filatech PC-ABS @MK2] inherits = Filatech PC-ABS @@ -8223,6 +8948,15 @@ inherits = Filatech PETG; *PET06PG* [filament:Filatech PETG @PG 0.8] inherits = Filatech PETG; *PET08PG* +[filament:Filatech PETG @XL] +inherits = Filatech PETG @PG; *PETXL* + +[filament:Filatech PETG @XL 0.6] +inherits = Filatech PETG @PG 0.6; *PET06XL* + +[filament:Filatech PETG @XL 0.8] +inherits = Filatech PETG @PG 0.8; *PET08XL* + [filament:Filatech PETG @MINI] inherits = Filatech PETG; *PETMINI* @@ -8241,6 +8975,15 @@ inherits = Filatech Wood-PLA; *PLA06PG* [filament:Filatech Wood-PLA @PG 0.8] inherits = Filatech Wood-PLA; *PLA08PG* +[filament:Filatech Wood-PLA @XL] +inherits = Filatech Wood-PLA @PG; *PLAXL* + +[filament:Filatech Wood-PLA @XL 0.6] +inherits = Filatech Wood-PLA @PG 0.6; *PLA06XL* + +[filament:Filatech Wood-PLA @XL 0.8] +inherits = Filatech Wood-PLA @PG 0.8; *PLA08XL* + [filament:Ultrafuse PET] inherits = *PET* filament_vendor = BASF @@ -8275,6 +9018,15 @@ inherits = Ultrafuse PET; *PET06PG* [filament:Ultrafuse PET @PG 0.8] inherits = Ultrafuse PET; *PET08PG* +[filament:Ultrafuse PET @XL] +inherits = Ultrafuse PET @PG; *PETXL* + +[filament:Ultrafuse PET @XL 0.6] +inherits = Ultrafuse PET @PG 0.6; *PET06XL* + +[filament:Ultrafuse PET @XL 0.8] +inherits = Ultrafuse PET @PG 0.8; *PET08XL* + [filament:Ultrafuse PET @MINI] inherits = Ultrafuse PET; *PETMINI* @@ -8302,6 +9054,15 @@ inherits = Ultrafuse PRO1; *PLA06PG* [filament:Ultrafuse PRO1 @PG 0.8] inherits = Ultrafuse PRO1; *PLA08PG* +[filament:Ultrafuse PRO1 @XL] +inherits = Ultrafuse PRO1 @PG; *PLAXL* + +[filament:Ultrafuse PRO1 @XL 0.6] +inherits = Ultrafuse PRO1 @PG 0.6; *PLA06XL* + +[filament:Ultrafuse PRO1 @XL 0.8] +inherits = Ultrafuse PRO1 @PG 0.8; *PLA08XL* + [filament:Ultrafuse PRO1 @MINI] inherits = Ultrafuse PRO1 filament_retract_length = nil @@ -8325,25 +9086,28 @@ filament_retract_before_travel = 2 filament_wipe = 0 filament_retract_layer_change = 0 -[filament:Ultrafuse ABS @PG] -inherits = Ultrafuse ABS; *ABSPG* +[filament:Ultrafuse ABS @XL] +inherits = Ultrafuse ABS; *ABSPG*; *ABSXL* +renamed_from = "Ultrafuse ABS @PG" first_layer_bed_temperature = 100 bed_temperature = 100 -[filament:Ultrafuse ABS @PG 0.6] -inherits = Ultrafuse ABS @PG; *ABS06PG* +[filament:Ultrafuse ABS @XL 0.6] +inherits = Ultrafuse ABS @XL; *ABS06XL* +renamed_from = "Ultrafuse ABS @PG 0.6" -[filament:Ultrafuse ABS @PG 0.8] -inherits = Ultrafuse ABS @PG; *ABS08PG* +[filament:Ultrafuse ABS @XL 0.8] +inherits = Ultrafuse ABS @XL; *ABS08XL* +renamed_from = "Ultrafuse ABS @PG 0.8" [filament:Ultrafuse ABS @MK4] -inherits = Ultrafuse ABS; *ABSMK4* +inherits = Ultrafuse ABS; *ABSPG* [filament:Ultrafuse ABS @MK4 0.6] -inherits = Ultrafuse ABS @MK4; *ABS06MK4* +inherits = Ultrafuse ABS @MK4; *ABS06PG* [filament:Ultrafuse ABS @MK4 0.8] -inherits = Ultrafuse ABS @MK4; *ABS08MK4* +inherits = Ultrafuse ABS @MK4; *ABS08PG* [filament:Ultrafuse ABS @MINI] inherits = Ultrafuse ABS; *ABSMINI* @@ -8362,25 +9126,28 @@ filament_wipe = nil filament_retract_layer_change = 0 filament_retract_lift = 0 -[filament:Ultrafuse ABS Fusion+ @PG] -inherits = Ultrafuse ABS Fusion+; *ABSPG* +[filament:Ultrafuse ABS Fusion+ @XL] +inherits = Ultrafuse ABS Fusion+; *ABSPG*; *ABSXL* +renamed_from = "Ultrafuse ABS Fusion+ @PG" first_layer_bed_temperature = 100 bed_temperature = 100 -[filament:Ultrafuse ABS Fusion+ @PG 0.6] -inherits = Ultrafuse ABS Fusion+ @PG; *ABS06PG* +[filament:Ultrafuse ABS Fusion+ @XL 0.6] +inherits = Ultrafuse ABS Fusion+ @XL; *ABS06XL* +renamed_from = "Ultrafuse ABS Fusion+ @PG 0.6" -[filament:Ultrafuse ABS Fusion+ @PG 0.8] -inherits = Ultrafuse ABS Fusion+ @PG; *ABS08PG* +[filament:Ultrafuse ABS Fusion+ @XL 0.8] +inherits = Ultrafuse ABS Fusion+ @XL; *ABS08XL* +renamed_from = "Ultrafuse ABS Fusion+ @PG 0.8" [filament:Ultrafuse ABS Fusion+ @MK4] -inherits = Ultrafuse ABS Fusion+; *ABSMK4* +inherits = Ultrafuse ABS Fusion+; *ABSPG* [filament:Ultrafuse ABS Fusion+ @MK4 0.6] -inherits = Ultrafuse ABS Fusion+ @MK4; *ABS06MK4* +inherits = Ultrafuse ABS Fusion+ @MK4; *ABS06PG* [filament:Ultrafuse ABS Fusion+ @MK4 0.8] -inherits = Ultrafuse ABS Fusion+ @MK4; *ABS08MK4* +inherits = Ultrafuse ABS Fusion+ @MK4; *ABS08PG* [filament:Ultrafuse ABS Fusion+ @MINI] inherits = Ultrafuse ABS Fusion+; *ABSMINI* @@ -8403,34 +9170,37 @@ disable_fan_first_layers = 4 filament_max_volumetric_speed = 5 filament_notes = "Material Description\nUltrafuse ASA is a high-performance thermoplastic with similar mechanical properties as ABS. ASA offers additional benefits such as high outdoor weather resistance. The UV resistance, toughness, and rigidity make it an ideal material to 3D-print outdoor fixtures and appliances without losing its properties or color. When also taking into account the high heat resistance and high chemical resistance, this filament is a good choice for many types of applications.\n\nPrinting Recommendations:\nApply Magigoo PC, 3D lac or Dimafix to a clean build plate to improve adhesion." -[filament:Ultrafuse ASA @PG] -inherits = Ultrafuse ASA; *ABSPG* +[filament:Ultrafuse ASA @XL] +inherits = Ultrafuse ASA; *ABSPG*; *ABSXL* +renamed_from = "Ultrafuse ASA @PG" first_layer_bed_temperature = 105 bed_temperature = 105 filament_max_volumetric_speed = 5 min_fan_speed = 15 max_fan_speed = 40 -[filament:Ultrafuse ASA @PG 0.6] -inherits = Ultrafuse ASA @PG; *ABS06PG* +[filament:Ultrafuse ASA @XL 0.6] +inherits = Ultrafuse ASA @XL; *ABS06XL* +renamed_from = "Ultrafuse ASA @PG 0.6" filament_max_volumetric_speed = 9 -[filament:Ultrafuse ASA @PG 0.8] -inherits = Ultrafuse ASA @PG; *ABS08PG* +[filament:Ultrafuse ASA @XL 0.8] +inherits = Ultrafuse ASA @XL; *ABS08XL* +renamed_from = "Ultrafuse ASA @PG 0.8" filament_max_volumetric_speed = 12 [filament:Ultrafuse ASA @MK4] -inherits = Ultrafuse ASA; *ABSMK4* +inherits = Ultrafuse ASA; *ABSPG* filament_max_volumetric_speed = 5 min_fan_speed = 15 max_fan_speed = 40 [filament:Ultrafuse ASA @MK4 0.6] -inherits = Ultrafuse ASA @MK4; *ABS06MK4* +inherits = Ultrafuse ASA @MK4; *ABS06PG* filament_max_volumetric_speed = 9 [filament:Ultrafuse ASA @MK4 0.8] -inherits = Ultrafuse ASA @MK4; *ABS08MK4* +inherits = Ultrafuse ASA @MK4; *ABS08PG* filament_max_volumetric_speed = 12 [filament:Ultrafuse ASA @MINI] @@ -8447,25 +9217,28 @@ max_fan_speed = 20 filament_soluble = 1 filament_notes = "Material Description\nUltrafuse HIPS is a high-quality engineering thermoplastic, which is well known in the 3D-printing industry as a support material for ABS. But this material has additional properties to offer like good impact resistance, good dimensional stability, and easy post-processing. HiPS is a great material to use as a support for ABS because there is a good compatibility between the two materials, and HIPS is an easy breakaway support. Now you have the opportunity to create ABS models with complex geometry. HIPS is easy to post process with glue or with sanding paper." -[filament:Ultrafuse HIPS @PG] -inherits = Ultrafuse HIPS; *ABSPG* +[filament:Ultrafuse HIPS @XL] +inherits = Ultrafuse HIPS; *ABSPG*; *ABSXL* +renamed_from = "Ultrafuse HIPS @PG" first_layer_bed_temperature = 100 bed_temperature = 100 -[filament:Ultrafuse HIPS @PG 0.6] -inherits = Ultrafuse HIPS @PG; *ABS06PG* +[filament:Ultrafuse HIPS @XL 0.6] +inherits = Ultrafuse HIPS @XL; *ABS06XL* +renamed_from = "Ultrafuse HIPS @PG 0.6" -[filament:Ultrafuse HIPS @PG 0.8] -inherits = Ultrafuse HIPS @PG; *ABS08PG* +[filament:Ultrafuse HIPS @XL 0.8] +inherits = Ultrafuse HIPS @XL; *ABS08XL* +renamed_from = "Ultrafuse HIPS @PG 0.8" [filament:Ultrafuse HIPS @MK4] -inherits = Ultrafuse HIPS; *ABSMK4* +inherits = Ultrafuse HIPS; *ABSPG* [filament:Ultrafuse HIPS @MK4 0.6] -inherits = Ultrafuse HIPS @MK4; *ABS06MK4* +inherits = Ultrafuse HIPS @MK4; *ABS06PG* [filament:Ultrafuse HIPS @MK4 0.8] -inherits = Ultrafuse HIPS @MK4; *ABS08MK4* +inherits = Ultrafuse HIPS @MK4; *ABS08PG* [filament:Ultrafuse HIPS @MINI] inherits = Ultrafuse HIPS; *ABSMINI* @@ -8502,17 +9275,23 @@ filament_notes = "Material Description\nThe key features of Ultrafuse PA are the [filament:Ultrafuse PA @PG] inherits = Ultrafuse PA; *ABSPG* filament_max_volumetric_speed = 8 -compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Ultrafuse PA @PG 0.6] inherits = Ultrafuse PA @PG; *ABS06PG* filament_max_volumetric_speed = 10 -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Ultrafuse PA @PG 0.8] inherits = Ultrafuse PA @PG; *ABS08PG* filament_max_volumetric_speed = 12 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material + +[filament:Ultrafuse PA @XL] +inherits = Ultrafuse PA @PG; *ABSXL* + +[filament:Ultrafuse PA @XL 0.6] +inherits = Ultrafuse PA @PG 0.6; *ABS06XL* + +[filament:Ultrafuse PA @XL 0.8] +inherits = Ultrafuse PA @PG 0.8; *ABS08XL* [filament:Ultrafuse PA6 GF30] inherits = Ultrafuse PA @@ -8550,6 +9329,12 @@ first_layer_temperature = 275 temperature = 275 compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material +[filament:Ultrafuse PA6 GF30 @XL 0.6] +inherits = Ultrafuse PA6 GF30 @PG 0.6; *ABS06XL* + +[filament:Ultrafuse PA6 GF30 @XL 0.8] +inherits = Ultrafuse PA6 GF30 @PG 0.8; *ABS08XL* + [filament:Ultrafuse PAHT-CF15] inherits = Ultrafuse PA6 GF30 filament_density = 1.23 @@ -8557,11 +9342,15 @@ filament_notes = "Material Description\nPAHT CF15 is a high-performance 3D print [filament:Ultrafuse PAHT-CF15 @PG 0.6] inherits = Ultrafuse PAHT-CF15; *ABS06PG* -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Ultrafuse PAHT-CF15 @PG 0.8] inherits = Ultrafuse PAHT-CF15; *ABS08PG* -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material + +[filament:Ultrafuse PAHT-CF15 @XL 0.6] +inherits = Ultrafuse PAHT-CF15 @PG 0.6; *ABS06XL* + +[filament:Ultrafuse PAHT-CF15 @XL 0.8] +inherits = Ultrafuse PAHT-CF15 @PG 0.8; *ABS08XL* [filament:Ultrafuse PC-ABS-FR] inherits = Ultrafuse ABS @@ -8582,30 +9371,33 @@ compatible_printers_condition = printer_model!="MINI" and printer_notes!~/.*PG.* filament_notes = "Material Description\nUltrafuse® PC/ABS FR Black is a V-0 flame retardant blend of Polycarbonate and ABS – two of the most used thermoplastics for engineering & electrical applications. The combination of these two materials results in a premium material with a mix of the excellent mechanical properties of PC and the comparably low printing temperature of ABS. Combined with a halogen free flame retardant, parts printed with Ultrafuse® PC/ABS FR Black feature great tensile and impact strength, higher thermal resistance than ABS and can fulfill the requirements of the UL94 V-0 standard.\n\nPrinting Recommendations:\nApply Magigoo PC to a clean build plate to improve adhesion." start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.02{elsif nozzle_diameter[0]==0.6}0.04{else}0.07{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K24{elsif nozzle_diameter[0]==0.8};{else}M900 K45{endif} ; Filament gcode LA 1.0" -[filament:Ultrafuse PC-ABS-FR @PG] -inherits = Ultrafuse PC-ABS-FR; *ABSPG* +[filament:Ultrafuse PC-ABS-FR @XL] +inherits = Ultrafuse PC-ABS-FR; *ABSPG*; *ABSXL* +renamed_from = "Ultrafuse PC-ABS-FR @PG" first_layer_bed_temperature = 105 bed_temperature = 105 filament_max_volumetric_speed = 8 -[filament:Ultrafuse PC-ABS-FR @PG 0.6] -inherits = Ultrafuse PC-ABS-FR @PG; *ABS06PG* +[filament:Ultrafuse PC-ABS-FR @XL 0.6] +inherits = Ultrafuse PC-ABS-FR @XL; *ABS06XL* +renamed_from = "Ultrafuse PC-ABS-FR @PG 0.6" filament_max_volumetric_speed = 10 -[filament:Ultrafuse PC-ABS-FR @PG 0.8] -inherits = Ultrafuse PC-ABS-FR @PG; *ABS08PG* +[filament:Ultrafuse PC-ABS-FR @XL 0.8] +inherits = Ultrafuse PC-ABS-FR @XL; *ABS08XL* +renamed_from = "Ultrafuse PC-ABS-FR @PG 0.8" filament_max_volumetric_speed = 12 [filament:Ultrafuse PC-ABS-FR @MK4] -inherits = Ultrafuse PC-ABS-FR; *ABSMK4* +inherits = Ultrafuse PC-ABS-FR; *ABSPG* filament_max_volumetric_speed = 8 [filament:Ultrafuse PC-ABS-FR @MK4 0.6] -inherits = Ultrafuse PC-ABS-FR @MK4; *ABS06MK4* +inherits = Ultrafuse PC-ABS-FR @MK4; *ABS06PG* filament_max_volumetric_speed = 10 [filament:Ultrafuse PC-ABS-FR @MK4 0.8] -inherits = Ultrafuse PC-ABS-FR @MK4; *ABS08MK4* +inherits = Ultrafuse PC-ABS-FR @MK4; *ABS08PG* filament_max_volumetric_speed = 12 [filament:Ultrafuse PET-CF15] @@ -8643,6 +9435,12 @@ filament_max_volumetric_speed = 13 first_layer_temperature = 270 temperature = 275 +[filament:Ultrafuse PET-CF15 @XL 0.6] +inherits = Ultrafuse PET-CF15 @PG 0.6; *PET06XL* + +[filament:Ultrafuse PET-CF15 @XL 0.8] +inherits = Ultrafuse PET-CF15 @PG 0.8; *PET08XL* + [filament:Ultrafuse PLA] inherits = *PLA* filament_vendor = BASF @@ -8659,6 +9457,15 @@ inherits = Ultrafuse PLA; *PLA06PG* [filament:Ultrafuse PLA @PG 0.8] inherits = Ultrafuse PLA; *PLA08PG* +[filament:Ultrafuse PLA @XL] +inherits = Ultrafuse PLA @PG; *PLAXL* + +[filament:Ultrafuse PLA @XL 0.6] +inherits = Ultrafuse PLA @PG 0.6; *PLA06XL* + +[filament:Ultrafuse PLA @XL 0.8] +inherits = Ultrafuse PLA @PG 0.8; *PLA08XL* + [filament:Ultrafuse PP] inherits = Ultrafuse ABS filament_density = 0.91 @@ -8687,17 +9494,23 @@ compatible_printers_condition = printer_model!="MINI" and printer_model!="MK2SMM [filament:Ultrafuse PP @PG] inherits = Ultrafuse PP; *ABSPG* filament_max_volumetric_speed = 2.5 -compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Ultrafuse PP @PG 0.6] inherits = Ultrafuse PP @PG; *ABS06PG* filament_max_volumetric_speed = 4 -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Ultrafuse PP @PG 0.8] inherits = Ultrafuse PP @PG; *ABS08PG* filament_max_volumetric_speed = 6 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material + +[filament:Ultrafuse PP @XL] +inherits = Ultrafuse PP; *ABSPG*; *ABSXL* + +[filament:Ultrafuse PP @XL 0.6] +inherits = Ultrafuse PP @XL; *ABS06XL* + +[filament:Ultrafuse PP @XL 0.8] +inherits = Ultrafuse PP @XL; *ABS08XL* [filament:Ultrafuse PP-GF30] inherits = Ultrafuse PP @@ -8723,11 +9536,15 @@ compatible_printers_condition = nozzle_diameter[0]>=0.6 and printer_model!="MINI [filament:Ultrafuse PP-GF30 @PG 0.6] inherits = Ultrafuse PP; *ABS06PG* -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Ultrafuse PP-GF30 @PG 0.8] inherits = Ultrafuse PP; *ABS08PG* -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material + +[filament:Ultrafuse PP-GF30 @XL 0.6] +inherits = Ultrafuse PP-GF30 @PG 0.6; *ABS06XL* + +[filament:Ultrafuse PP-GF30 @XL 0.8] +inherits = Ultrafuse PP-GF30 @PG 0.8; *ABS08XL* [filament:Ultrafuse TPC-45D] inherits = *FLEX* @@ -8808,7 +9625,30 @@ first_layer_temperature = 225 temperature = 220 filament_notes = "Material Description\nUltrafuse® TPU 85A comes in its natural white color. Chemical properties (e.g. resistance against particular substances) and tolerance for solvents can be made available, if these factors are relevant for a specific application. Generally, these properties correspond to publicly available data on polyether based TPUs. This material is not FDA conform. Good flexibility at low temperature, good wear performance and good damping behavior are the key features of Ultrafuse® TPU 85A.\n\nPrinting Recommendations:\nUltrafuse TPU can be printed directly onto a clean build plate. A small amount of 3Dlac can make removal easier after printing." -[filament:Ultrafuse TPU-85A @PG] +[filament:Ultrafuse TPU-85A @XL] +inherits = Ultrafuse TPU-85A; *FLEXXL* +renamed_from = "Ultrafuse TPU-85A @PG" +filament_max_volumetric_speed = 3 +extrusion_multiplier = 1.1 +first_layer_temperature = 220 +temperature = 215 +filament_retract_length = 3.5 +filament_multitool_ramming_flow = 3 +filament_retract_length_toolchange = 3.5 + +[filament:Ultrafuse TPU-85A @XL 0.6] +inherits = Ultrafuse TPU-85A @XL; *FLEX06XL* +renamed_from = "Ultrafuse TPU-85A @PG 0.6" +filament_max_volumetric_speed = 4 +filament_multitool_ramming_flow = 4 + +[filament:Ultrafuse TPU-85A @XL 0.8] +inherits = Ultrafuse TPU-85A @XL; *FLEX08XL* +renamed_from = "Ultrafuse TPU-85A @PG 0.8" +filament_max_volumetric_speed = 7 +filament_multitool_ramming_flow = 7 + +[filament:Ultrafuse TPU-85A @MK4] inherits = Ultrafuse TPU-85A; *FLEXPG* filament_max_volumetric_speed = 3 extrusion_multiplier = 1.1 @@ -8816,28 +9656,12 @@ first_layer_temperature = 220 temperature = 215 filament_retract_length = 3.5 -[filament:Ultrafuse TPU-85A @PG 0.6] -inherits = Ultrafuse TPU-85A @PG; *FLEX06PG* -filament_max_volumetric_speed = 4 - -[filament:Ultrafuse TPU-85A @PG 0.8] -inherits = Ultrafuse TPU-85A @PG; *FLEX08PG* -filament_max_volumetric_speed = 7 - -[filament:Ultrafuse TPU-85A @MK4] -inherits = Ultrafuse TPU-85A; *FLEXMK4* -filament_max_volumetric_speed = 3 -extrusion_multiplier = 1.1 -first_layer_temperature = 220 -temperature = 215 -filament_retract_length = 3.5 - [filament:Ultrafuse TPU-85A @MK4 0.6] -inherits = Ultrafuse TPU-85A @MK4; *FLEX06MK4* +inherits = Ultrafuse TPU-85A @MK4; *FLEX06PG* filament_max_volumetric_speed = 6 [filament:Ultrafuse TPU-85A @MK4 0.8] -inherits = Ultrafuse TPU-85A @MK4; *FLEX08MK4* +inherits = Ultrafuse TPU-85A @MK4; *FLEX08PG* filament_max_volumetric_speed = 8 [filament:Ultrafuse TPU-95A] @@ -8847,30 +9671,37 @@ first_layer_temperature = 230 temperature = 225 filament_notes = "Material Description\nUltrafuse® TPU 95A comes with a well-balanced profile of flexibility and durability. On top of that, it allows for easier and faster printing then softer TPU grades. Parts printed with Ultrafuse® TPU 95A show a high elongation, good impact resistance, excellent layer adhesion and a good resistance to oils and common industrially used chemicals. Due to its good printing behavior, Ultrafuse® TPU 95A is a good choice for starting printing flexible materials on both direct drive and bowden style printers.\n\nPrinting Recommendations:\nUltrafuse TPU can be printed directly onto a clean build plate. A small amount of 3Dlac can make removal easier after printing." -[filament:Ultrafuse TPU-95A @PG] +[filament:Ultrafuse TPU-95A @XL] +inherits = Ultrafuse TPU-95A; *FLEXXL* +renamed_from = "Ultrafuse TPU-95A @PG" +filament_max_volumetric_speed = 2.5 +filament_retract_length = 3 +filament_multitool_ramming_flow = 2.5 +filament_retract_length_toolchange = 3 + +[filament:Ultrafuse TPU-95A @XL 0.6] +inherits = Ultrafuse TPU-95A @XL; *FLEX06XL* +renamed_from = "Ultrafuse TPU-95A @PG 0.6" +filament_max_volumetric_speed = 3.5 +filament_multitool_ramming_flow = 3.5 + +[filament:Ultrafuse TPU-95A @XL 0.8] +inherits = Ultrafuse TPU-95A @XL; *FLEX08XL* +renamed_from = "Ultrafuse TPU-95A @PG 0.8" +filament_max_volumetric_speed = 6 +filament_multitool_ramming_flow = 6 + +[filament:Ultrafuse TPU-95A @MK4] inherits = Ultrafuse TPU-95A; *FLEXPG* filament_max_volumetric_speed = 2.5 filament_retract_length = 3 -[filament:Ultrafuse TPU-95A @PG 0.6] -inherits = Ultrafuse TPU-95A @PG; *FLEX06PG* -filament_max_volumetric_speed = 3.5 - -[filament:Ultrafuse TPU-95A @PG 0.8] -inherits = Ultrafuse TPU-95A @PG; *FLEX08PG* -filament_max_volumetric_speed = 6 - -[filament:Ultrafuse TPU-95A @MK4] -inherits = Ultrafuse TPU-95A; *FLEXMK4* -filament_max_volumetric_speed = 2.5 -filament_retract_length = 3 - [filament:Ultrafuse TPU-95A @MK4 0.6] -inherits = Ultrafuse TPU-95A @MK4; *FLEX06MK4* +inherits = Ultrafuse TPU-95A @MK4; *FLEX06PG* filament_max_volumetric_speed = 5 [filament:Ultrafuse TPU-95A @MK4 0.8] -inherits = Ultrafuse TPU-95A @MK4; *FLEX08MK4* +inherits = Ultrafuse TPU-95A @MK4; *FLEX08PG* filament_max_volumetric_speed = 7 [filament:Ultrafuse rPET] @@ -8903,6 +9734,15 @@ filament_max_volumetric_speed = 18 first_layer_temperature = 235 temperature = 245 +[filament:Ultrafuse rPET @XL] +inherits = Ultrafuse rPET @PG; *PETXL* + +[filament:Ultrafuse rPET @XL 0.6] +inherits = Ultrafuse rPET @PG 0.6; *PET06XL* + +[filament:Ultrafuse rPET @XL 0.8] +inherits = Ultrafuse rPET @PG 0.8; *PET08XL* + [filament:Ultrafuse Metal] inherits = *ABSC* renamed_from = "Ultrafuse 17-4 PH" @@ -8929,13 +9769,17 @@ filament_colour = #FFFFFF inherits = Ultrafuse Metal; *ABSPG*; *04PLUSPG* filament_max_volumetric_speed = 4 start_filament_gcode = "M900 K0" -compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Ultrafuse Metal @PG 0.6] inherits = Ultrafuse Metal @PG; *ABS06PG* filament_max_volumetric_speed = 4 start_filament_gcode = "M900 K0" -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material + +[filament:Ultrafuse Metal @XL] +inherits = Ultrafuse Metal @PG; *ABSXL* + +[filament:Ultrafuse Metal @XL 0.6] +inherits = Ultrafuse Metal @PG 0.6; *ABS06XL* [filament:Polymaker PC-Max] inherits = *ABS* @@ -8951,30 +9795,33 @@ temperature = 270 bridge_fan_speed = 0 filament_max_volumetric_speed = 8 -[filament:Polymaker PC-Max @PG] -inherits = Polymaker PC-Max; *ABSPG* +[filament:Polymaker PC-Max @XL] +inherits = Polymaker PC-Max; *ABSXL* +renamed_from = "Polymaker PC-Max @PG" first_layer_bed_temperature = 100 bed_temperature = 105 filament_max_volumetric_speed = 8 -[filament:Polymaker PC-Max @PG 0.6] -inherits = Polymaker PC-Max @PG; *ABS06PG* +[filament:Polymaker PC-Max @XL 0.6] +inherits = Polymaker PC-Max @XL; *ABS06XL* +renamed_from = "Polymaker PC-Max @PG 0.6" filament_max_volumetric_speed = 12 -[filament:Polymaker PC-Max @PG 0.8] -inherits = Polymaker PC-Max @PG; *ABS08PG* +[filament:Polymaker PC-Max @XL 0.8] +inherits = Polymaker PC-Max @XL; *ABS08XL* +renamed_from = "Polymaker PC-Max @PG 0.8" filament_max_volumetric_speed = 15 [filament:Polymaker PC-Max @MK4] -inherits = Polymaker PC-Max; *ABSMK4* +inherits = Polymaker PC-Max; *ABSPG* filament_max_volumetric_speed = 8 [filament:Polymaker PC-Max @MK4 0.6] -inherits = Polymaker PC-Max @MK4; *ABS06MK4* +inherits = Polymaker PC-Max @MK4; *ABS06PG* filament_max_volumetric_speed = 12 [filament:Polymaker PC-Max @MK4 0.8] -inherits = Polymaker PC-Max @MK4; *ABS08MK4* +inherits = Polymaker PC-Max @MK4; *ABS08PG* filament_max_volumetric_speed = 15 [filament:PrimaSelect PVA+] @@ -9010,6 +9857,15 @@ first_layer_temperature = 210 temperature = 210 filament_max_volumetric_speed = 8 +[filament:PrimaSelect PVA+ @XL] +inherits = PrimaSelect PVA+ @PG; *PLAXL* + +[filament:PrimaSelect PVA+ @XL 0.6] +inherits = PrimaSelect PVA+ @PG 0.6; *PLA06XL* + +[filament:PrimaSelect PVA+ @XL 0.8] +inherits = PrimaSelect PVA+ @PG 0.8; *PLA08XL* + [filament:Prusa ABS] inherits = *ABSC* filament_vendor = Made for Prusa @@ -9018,26 +9874,29 @@ filament_density = 1.08 filament_spool_weight = 230 compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) -[filament:Prusa ABS @PG] -inherits = Prusa ABS; *ABSPG* +[filament:Prusa ABS @XL] +inherits = Prusa ABS; *ABSPG*; *ABSXL* +renamed_from = "Prusa ABS @PG" bed_temperature = 105 -[filament:Prusa ABS @PG 0.6] -inherits = Prusa ABS @PG; *ABS06PG* +[filament:Prusa ABS @XL 0.6] +inherits = Prusa ABS @XL; *ABS06XL* +renamed_from = "Prusa ABS @PG 0.6" -[filament:Prusa ABS @PG 0.8] -inherits = Prusa ABS @PG; *ABS08PG* +[filament:Prusa ABS @XL 0.8] +inherits = Prusa ABS @XL; *ABS08XL* +renamed_from = "Prusa ABS @PG 0.8" first_layer_temperature = 265 temperature = 265 [filament:Prusa ABS @MK4] -inherits = Prusa ABS; *ABSMK4* +inherits = Prusa ABS; *ABSPG* [filament:Prusa ABS @MK4 0.6] -inherits = Prusa ABS @MK4; *ABS06MK4* +inherits = Prusa ABS @MK4; *ABS06PG* [filament:Prusa ABS @MK4 0.8] -inherits = Prusa ABS @MK4; *ABS08MK4* +inherits = Prusa ABS @MK4; *ABS08PG* first_layer_temperature = 265 temperature = 265 @@ -9312,24 +10171,27 @@ start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and no temperature = 230 compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) -[filament:Generic HIPS @PG] -inherits = Generic HIPS; *ABSPG* +[filament:Generic HIPS @XL] +inherits = Generic HIPS; *ABSPG*; *ABSXL* +renamed_from = "Generic HIPS @PG" bed_temperature = 105 -[filament:Generic HIPS @PG 0.6] -inherits = Generic HIPS @PG; *ABS06PG* +[filament:Generic HIPS @XL 0.6] +inherits = Generic HIPS @XL; *ABS06XL* +renamed_from = "Generic HIPS @PG 0.6" -[filament:Generic HIPS @PG 0.8] -inherits = Generic HIPS @PG; *ABS08PG* +[filament:Generic HIPS @XL 0.8] +inherits = Generic HIPS @XL; *ABS08XL* +renamed_from = "Generic HIPS @PG 0.8" [filament:Generic HIPS @MK4] -inherits = Generic HIPS; *ABSMK4* +inherits = Generic HIPS; *ABSPG* [filament:Generic HIPS @MK4 0.6] -inherits = Generic HIPS @MK4; *ABS06MK4* +inherits = Generic HIPS @MK4; *ABS06PG* [filament:Generic HIPS @MK4 0.8] -inherits = Generic HIPS @MK4; *ABS08MK4* +inherits = Generic HIPS @MK4; *ABS08PG* [filament:Prusa PETG] inherits = *PET* @@ -9342,16 +10204,25 @@ compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!= [filament:Prusa PETG @PG] inherits = Prusa PETG; *PETPG* -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model!="MK4IS" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and printer_model!="MK4IS" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 [filament:Prusa PETG @PG 0.6] inherits = Prusa PETG; *PET06PG* -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model!="MK4IS" and nozzle_diameter[0]==0.6 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and printer_model!="MK4IS" and nozzle_diameter[0]==0.6 [filament:Prusa PETG @PG 0.8] inherits = Prusa PETG; *PET08PG* temperature = 250 -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model!="MK4IS" and nozzle_diameter[0]==0.8 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and printer_model!="MK4IS" and nozzle_diameter[0]==0.8 + +[filament:Prusa PETG @XL] +inherits = Prusa PETG @PG; *PETXL* + +[filament:Prusa PETG @XL 0.6] +inherits = Prusa PETG @PG 0.6; *PET06XL* + +[filament:Prusa PETG @XL 0.8] +inherits = Prusa PETG @PG 0.8; *PET08XL* [filament:Prusa PETG @PGIS] inherits = Generic PETG @PG @@ -9365,7 +10236,7 @@ filament_retract_length = 0.8 filament_wipe = 1 filament_retract_before_wipe = 20 filament_retract_lift = nil -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 [filament:Prusa PETG @PGIS 0.6] inherits = Generic PETG @PG 0.6 @@ -9375,7 +10246,7 @@ filament_retract_length = 0.8 filament_wipe = 1 filament_retract_before_wipe = 20 filament_retract_lift = nil -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]==0.6 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.6 [filament:Prusa PETG @PGIS 0.8] inherits = Generic PETG @PG 0.8 @@ -9385,7 +10256,7 @@ filament_retract_length = 0.8 filament_wipe = 1 filament_retract_before_wipe = 20 filament_retract_lift = nil -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]==0.8 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.8 [filament:Verbatim PETG] inherits = *PET* @@ -9404,6 +10275,15 @@ inherits = Verbatim PETG; *PET06PG* [filament:Verbatim PETG @PG 0.8] inherits = Verbatim PETG; *PET08PG* +[filament:Verbatim PETG @XL] +inherits = Verbatim PETG @PG; *PETXL* + +[filament:Verbatim PETG @XL 0.6] +inherits = Verbatim PETG @PG 0.6; *PET06XL* + +[filament:Verbatim PETG @XL 0.8] +inherits = Verbatim PETG @PG 0.8; *PET08XL* + [filament:Prusament PETG] inherits = *PET* filament_vendor = Prusa Polymers @@ -9417,17 +10297,26 @@ compatible_printers_condition = nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!= [filament:Prusament PETG @PG] inherits = Prusament PETG; *PETPG* -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model!="MK4IS" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and printer_model!="MK4IS" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 [filament:Prusament PETG @PG 0.6] inherits = Prusament PETG; *PET06PG* -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model!="MK4IS" and nozzle_diameter[0]==0.6 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and printer_model!="MK4IS" and nozzle_diameter[0]==0.6 [filament:Prusament PETG @PG 0.8] inherits = Prusament PETG; *PET08PG* first_layer_temperature = 250 temperature = 260 -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model!="MK4IS" and nozzle_diameter[0]==0.8 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and printer_model!="MK4IS" and nozzle_diameter[0]==0.8 + +[filament:Prusament PETG @XL] +inherits = Prusament PETG @PG; *PETXL* + +[filament:Prusament PETG @XL 0.6] +inherits = Prusament PETG @PG 0.6; *PET06XL* + +[filament:Prusament PETG @XL 0.8] +inherits = Prusament PETG @PG 0.8; *PET08XL* [filament:Prusament PETG @PGIS] inherits = Prusament PETG @PG @@ -9439,7 +10328,7 @@ filament_retract_length = 0.8 filament_wipe = 1 filament_retract_before_wipe = 20 filament_retract_lift = nil -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 [filament:Prusament PETG @PGIS 0.6] inherits = Prusament PETG @PG 0.6 @@ -9447,7 +10336,7 @@ filament_retract_length = 0.8 filament_wipe = 1 filament_retract_before_wipe = 20 filament_retract_lift = nil -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]==0.6 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.6 [filament:Prusament PETG @PGIS 0.8] inherits = Prusament PETG @PG 0.8 @@ -9455,7 +10344,7 @@ filament_retract_length = 0.8 filament_wipe = 1 filament_retract_before_wipe = 20 filament_retract_lift = nil -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]==0.8 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.8 [filament:Prusament PETG V0 certified] inherits = Prusament PETG @@ -9483,6 +10372,15 @@ inherits = Prusament PETG V0 @PG; *PET06PG* [filament:Prusament PETG V0 @PG 0.8] inherits = Prusament PETG V0 @PG; *PET08PG* +[filament:Prusament PETG V0 @XL] +inherits = Prusament PETG V0 @PG; *PETXL* + +[filament:Prusament PETG V0 @XL 0.6] +inherits = Prusament PETG V0 @PG 0.6; *PET06XL* + +[filament:Prusament PETG V0 @XL 0.8] +inherits = Prusament PETG V0 @PG 0.8; *PET08XL* + [filament:Prusament PETG V0 @MINI] inherits = Prusament PETG V0 certified; *PETMINI* filament_notes = "" @@ -9507,6 +10405,15 @@ inherits = Prusament PETG Carbon Fiber @PG; *PET06PG* [filament:Prusament PETG Carbon Fiber @PG 0.8] inherits = Prusament PETG Carbon Fiber @PG; *PET08PG* +[filament:Prusament PETG Carbon Fiber @XL] +inherits = Prusament PETG Carbon Fiber @PG; *PETXL*; *04PLUSXL* + +[filament:Prusament PETG Carbon Fiber @XL 0.6] +inherits = Prusament PETG Carbon Fiber @PG 0.6; *PET06XL* + +[filament:Prusament PETG Carbon Fiber @XL 0.8] +inherits = Prusament PETG Carbon Fiber @PG 0.8; *PET08XL* + ## [filament:Prusament PETG Tungsten 75%] ## inherits = *PET* ## filament_vendor = Prusa Polymers @@ -9711,16 +10618,25 @@ compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_notes!~/.*PG [filament:Prusa PLA @PG] inherits = Prusa PLA; *PLAPG* -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model!="MK4IS" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and printer_model!="MK4IS" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 [filament:Prusa PLA @PG 0.6] inherits = Prusa PLA; *PLA06PG* filament_max_volumetric_speed = 15.5 -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model!="MK4IS" and nozzle_diameter[0]==0.6 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and printer_model!="MK4IS" and nozzle_diameter[0]==0.6 [filament:Prusa PLA @PG 0.8] inherits = Prusa PLA; *PLA08PG* -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model!="MK4IS" and nozzle_diameter[0]==0.8 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and printer_model!="MK4IS" and nozzle_diameter[0]==0.8 + +[filament:Prusa PLA @XL] +inherits = Prusa PLA @PG; *PLAXL* + +[filament:Prusa PLA @XL 0.6] +inherits = Prusa PLA @PG 0.6; *PLA06XL* + +[filament:Prusa PLA @XL 0.8] +inherits = Prusa PLA @PG 0.8; *PLA08XL* [filament:Prusa PLA @PGIS] inherits = Prusa PLA @PG @@ -9728,19 +10644,19 @@ renamed_from = "Prusa PLA @MK4IS" first_layer_temperature = 230 temperature = 220 slowdown_below_layer_time = 6 -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 [filament:Prusa PLA @PGIS 0.6] inherits = Prusa PLA @PG 0.6 first_layer_temperature = 230 temperature = 220 -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]==0.6 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.6 [filament:Prusa PLA @PGIS 0.8] inherits = Prusa PLA @PG 0.8 first_layer_temperature = 230 temperature = 230 -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]==0.8 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.8 [filament:Eolas Prints PLA] @@ -9762,6 +10678,15 @@ inherits = Eolas Prints PLA; *PLA06PG* [filament:Eolas Prints PLA @PG 0.8] inherits = Eolas Prints PLA; *PLA08PG* +[filament:Eolas Prints PLA @XL] +inherits = Eolas Prints PLA @PG; *PLAXL* + +[filament:Eolas Prints PLA @XL 0.6] +inherits = Eolas Prints PLA @PG 0.6; *PLA06XL* + +[filament:Eolas Prints PLA @XL 0.8] +inherits = Eolas Prints PLA @PG 0.8; *PLA08XL* + [filament:Eolas Prints PLA Matte] inherits = Eolas Prints PLA filament_cost = 25.50 @@ -9777,6 +10702,15 @@ inherits = Eolas Prints PLA Matte; *PLA06PG* [filament:Eolas Prints PLA Matte @PG 0.8] inherits = Eolas Prints PLA Matte; *PLA08PG* +[filament:Eolas Prints PLA Matte @XL] +inherits = Eolas Prints PLA Matte @PG; *PLAXL* + +[filament:Eolas Prints PLA Matte @XL 0.6] +inherits = Eolas Prints PLA Matte @PG 0.6; *PLA06XL* + +[filament:Eolas Prints PLA Matte @XL 0.8] +inherits = Eolas Prints PLA Matte @PG 0.8; *PLA08XL* + [filament:Eolas Prints INGEO 850] inherits = Eolas Prints PLA filament_cost = 25.90 @@ -9791,6 +10725,15 @@ inherits = Eolas Prints INGEO 850; *PLA06PG* [filament:Eolas Prints INGEO 850 @PG 0.8] inherits = Eolas Prints INGEO 850; *PLA08PG* +[filament:Eolas Prints INGEO 850 @XL] +inherits = Eolas Prints INGEO 850 @PG; *PLAXL* + +[filament:Eolas Prints INGEO 850 @XL 0.6] +inherits = Eolas Prints INGEO 850 @PG 0.6; *PLA06XL* + +[filament:Eolas Prints INGEO 850 @XL 0.8] +inherits = Eolas Prints INGEO 850 @PG 0.8; *PLA08XL* + [filament:Eolas Prints INGEO 870] inherits = Eolas Prints PLA filament_cost = 25.90 @@ -9808,6 +10751,15 @@ inherits = Eolas Prints INGEO 870; *PLA06PG* [filament:Eolas Prints INGEO 870 @PG 0.8] inherits = Eolas Prints INGEO 870; *PLA08PG* +[filament:Eolas Prints INGEO 870 @XL] +inherits = Eolas Prints INGEO 870 @PG; *PLAXL* + +[filament:Eolas Prints INGEO 870 @XL 0.6] +inherits = Eolas Prints INGEO 870 @PG 0.6; *PLA06XL* + +[filament:Eolas Prints INGEO 870 @XL 0.8] +inherits = Eolas Prints INGEO 870 @PG 0.8; *PLA08XL* + [filament:Eolas Prints PETG] inherits = *PET* filament_vendor = Eolas Prints @@ -9832,6 +10784,15 @@ inherits = Eolas Prints PETG; *PET06PG* [filament:Eolas Prints PETG @PG 0.8] inherits = Eolas Prints PETG; *PET08PG* +[filament:Eolas Prints PETG @XL] +inherits = Eolas Prints PETG @PG; *PETXL* + +[filament:Eolas Prints PETG @XL 0.6] +inherits = Eolas Prints PETG @PG 0.6; *PET06XL* + +[filament:Eolas Prints PETG @XL 0.8] +inherits = Eolas Prints PETG @PG 0.8; *PET08XL* + [filament:Eolas Prints PETG @MINI] inherits = Eolas Prints PETG; *PETMINI* @@ -9850,6 +10811,15 @@ inherits = Eolas Prints PETG - UV Resistant; *PET06PG* [filament:Eolas Prints PETG - UV Resistant @PG 0.8] inherits = Eolas Prints PETG - UV Resistant; *PET08PG* +[filament:Eolas Prints PETG - UV Resistant @XL] +inherits = Eolas Prints PETG - UV Resistant @PG; *PETXL* + +[filament:Eolas Prints PETG - UV Resistant @XL 0.6] +inherits = Eolas Prints PETG - UV Resistant @PG 0.6; *PET06XL* + +[filament:Eolas Prints PETG - UV Resistant @XL 0.8] +inherits = Eolas Prints PETG - UV Resistant @PG 0.8; *PET08XL* + [filament:Eolas Prints PETG - UV Resistant @MINI] inherits = Eolas Prints PETG - UV Resistant; *PETMINI* @@ -9866,27 +10836,31 @@ bed_temperature = 30 filament_retract_length = 0 extrusion_multiplier = 1.16 -[filament:Eolas Prints TPU 93A @PG] +[filament:Eolas Prints TPU 93A @XL] +inherits = Eolas Prints TPU 93A; *FLEXXL* +renamed_from = "Eolas Prints TPU 93A @PG" +extrusion_multiplier = 1.1 +filament_retract_length = 2.5 +filament_retract_length_toolchange = 2.5 + +[filament:Eolas Prints TPU 93A @XL 0.6] +inherits = Eolas Prints TPU 93A @XL; *FLEX06XL* +renamed_from = "Eolas Prints TPU 93A @PG 0.6" + +[filament:Eolas Prints TPU 93A @PG 0.8] +inherits = Eolas Prints TPU 93A @XL; *FLEX08XL* +renamed_from = "Eolas Prints TPU 93A @PG 0.8" + +[filament:Eolas Prints TPU 93A @MK4] inherits = Eolas Prints TPU 93A; *FLEXPG* extrusion_multiplier = 1.1 filament_retract_length = 2.5 -[filament:Eolas Prints TPU 93A @PG 0.6] -inherits = Eolas Prints TPU 93A @PG; *FLEX06PG* - -[filament:Eolas Prints TPU 93A @PG 0.8] -inherits = Eolas Prints TPU 93A @PG; *FLEX08PG* - -[filament:Eolas Prints TPU 93A @MK4] -inherits = Eolas Prints TPU 93A; *FLEXMK4* -extrusion_multiplier = 1.1 -filament_retract_length = 2.5 - [filament:Eolas Prints TPU 93A @MK4 0.6] -inherits = Eolas Prints TPU 93A @MK4; *FLEX06MK4* +inherits = Eolas Prints TPU 93A @MK4; *FLEX06PG* [filament:Eolas Prints TPU 93A @MK4 0.8] -inherits = Eolas Prints TPU 93A @MK4; *FLEX08MK4* +inherits = Eolas Prints TPU 93A @MK4; *FLEX08PG* [filament:Print With Smile PLA] inherits = *PLA* @@ -9910,6 +10884,17 @@ inherits = Print With Smile PLA; *PLA08PG* first_layer_temperature = 215 temperature = 215 +[filament:Print With Smile PLA @XL] +inherits = Print With Smile PLA @PG; *PLAXL* + +[filament:Print With Smile PLA @XL 0.6] +inherits = Print With Smile PLA @PG 0.6; *PLA06XL* + +[filament:Print With Smile PLA @XL 0.8] +inherits = Print With Smile PLA @PG 0.8; *PLA08XL* +first_layer_temperature = 215 +temperature = 215 + [filament:Print With Smile PETG] inherits = *PET* filament_vendor = Print With Smile @@ -9935,6 +10920,15 @@ filament_max_volumetric_speed = 16 inherits = Print With Smile PETG; *PET08PG* filament_max_volumetric_speed = 19 +[filament:Print With Smile PETG @XL] +inherits = Print With Smile PETG @PG; *PETXL* + +[filament:Print With Smile PETG @XL 0.6] +inherits = Print With Smile PETG @PG 0.6; *PET06XL* + +[filament:Print With Smile PETG @XL 0.8] +inherits = Print With Smile PETG @PG 0.8; *PET08XL* + [filament:Print With Smile PETG @MINI] inherits = Print With Smile PETG; *PETMINI* @@ -9955,27 +10949,30 @@ filament_max_volumetric_speed = 11 [filament:Print With Smile ASA @MINI] inherits = Print With Smile ASA; *ABSMINI* -[filament:Print With Smile ASA @PG] -inherits = Print With Smile ASA; *ABSPG* +[filament:Print With Smile ASA @XL] +inherits = Print With Smile ASA; *ABSPG*; *ABSXL* +renamed_from = "Print With Smile ASA @PG" first_layer_bed_temperature = 100 bed_temperature = 105 -[filament:Print With Smile ASA @PG 0.6] -inherits = Print With Smile ASA @PG; *ABS06PG* +[filament:Print With Smile ASA @XL 0.6] +inherits = Print With Smile ASA @XL; *ABS06XL* +renamed_from = "Print With Smile ASA @PG 0.6" -[filament:Print With Smile ASA @PG 0.8] -inherits = Print With Smile ASA @PG; *ABS08PG* +[filament:Print With Smile ASA @XL 0.8] +inherits = Print With Smile ASA @XL; *ABS08XL* +renamed_from = "Print With Smile ASA @PG 0.8" first_layer_temperature = 255 temperature = 255 [filament:Print With Smile ASA @MK4] -inherits = Print With Smile ASA; *ABSMK4* +inherits = Print With Smile ASA; *ABSPG* [filament:Print With Smile ASA @MK4 0.6] -inherits = Print With Smile ASA @MK4; *ABS06MK4* +inherits = Print With Smile ASA @MK4; *ABS06PG* [filament:Print With Smile ASA @MK4 0.8] -inherits = Print With Smile ASA @MK4; *ABS08MK4* +inherits = Print With Smile ASA @MK4; *ABS08PG* first_layer_temperature = 255 temperature = 255 @@ -9990,25 +10987,28 @@ temperature = 240 [filament:Print With Smile ABS @MINI] inherits = Print With Smile ABS; *ABSMINI* -[filament:Print With Smile ABS @PG] -inherits = Print With Smile ABS; *ABSPG* +[filament:Print With Smile ABS @XL] +inherits = Print With Smile ABS; *ABSPG*; *ABSXL* +renamed_from = "Print With Smile ABS @PG" first_layer_bed_temperature = 100 bed_temperature = 105 -[filament:Print With Smile ABS @PG 0.6] -inherits = Print With Smile ABS @PG; *ABS06PG* +[filament:Print With Smile ABS @XL 0.6] +inherits = Print With Smile ABS @XL; *ABS06XL* +renamed_from = "Print With Smile ABS @PG 0.6" -[filament:Print With Smile ABS @PG 0.8] -inherits = Print With Smile ABS @PG; *ABS08PG* +[filament:Print With Smile ABS @XL 0.8] +inherits = Print With Smile ABS @XL; *ABS08XL* +renamed_from = "Print With Smile ABS @PG 0.8" [filament:Print With Smile ABS @MK4] -inherits = Print With Smile ABS; *ABSMK4* +inherits = Print With Smile ABS; *ABSPG* [filament:Print With Smile ABS @MK4 0.6] -inherits = Print With Smile ABS @MK4; *ABS06MK4* +inherits = Print With Smile ABS @MK4; *ABS06PG* [filament:Print With Smile ABS @MK4 0.8] -inherits = Print With Smile ABS @MK4; *ABS08MK4* +inherits = Print With Smile ABS @MK4; *ABS08PG* [filament:Print With Smile PETG CF] inherits = Extrudr XPETG CF @@ -10039,6 +11039,15 @@ filament_max_volumetric_speed = 6.5 inherits = Print With Smile PETG CF @PG; *PET08PG* filament_max_volumetric_speed = 8 +[filament:Print With Smile PETG CF @XL] +inherits = Print With Smile PETG CF @PG; *PETXL* + +[filament:Print With Smile PETG CF @XL 0.6] +inherits = Print With Smile PETG CF @PG 0.6; *PET06XL* + +[filament:Print With Smile PETG CF @XL 0.8] +inherits = Print With Smile PETG CF @PG 0.8; *PET08XL* + [filament:Print With Smile TPU96A] inherits = *FLEX* filament_vendor = Print With Smile @@ -10055,30 +11064,37 @@ full_fan_speed_layer = 6 filament_retract_length = 1.2 filament_deretract_speed = 20 -[filament:Print With Smile TPU96A @PG] -inherits = Print With Smile TPU96A; *FLEXPG* +[filament:Print With Smile TPU96A @XL] +inherits = Print With Smile TPU96A; *FLEXXL* +renamed_from = "Print With Smile TPU96A @PG" filament_retract_length = 2 filament_max_volumetric_speed = 2.5 +filament_multitool_ramming_flow = 2.5 +filament_retract_length_toolchange = 2 -[filament:Print With Smile TPU96A @PG 0.6] -inherits = Print With Smile TPU96A @PG; *FLEX06PG* +[filament:Print With Smile TPU96A @XL 0.6] +inherits = Print With Smile TPU96A @XL; *FLEX06XL* +renamed_from = "Print With Smile TPU96A @PG 0.6" filament_max_volumetric_speed = 3.5 +filament_multitool_ramming_flow = 3.5 -[filament:Print With Smile TPU96A @PG 0.8] -inherits = Print With Smile TPU96A @PG; *FLEX08PG* +[filament:Print With Smile TPU96A @XL 0.8] +inherits = Print With Smile TPU96A @XL; *FLEX08XL* +renamed_from = "Print With Smile TPU96A @PG 0.8" filament_max_volumetric_speed = 7 +filament_multitool_ramming_flow = 7 [filament:Print With Smile TPU96A @MK4] -inherits = Print With Smile TPU96A; *FLEXMK4* +inherits = Print With Smile TPU96A; *FLEXPG* filament_retract_length = 2 filament_max_volumetric_speed = 3 [filament:Print With Smile TPU96A @MK4 0.6] -inherits = Print With Smile TPU96A @MK4; *FLEX06MK4* +inherits = Print With Smile TPU96A @MK4; *FLEX06PG* filament_max_volumetric_speed = 5 [filament:Print With Smile TPU96A @MK4 0.8] -inherits = Print With Smile TPU96A @MK4; *FLEX08MK4* +inherits = Print With Smile TPU96A @MK4; *FLEX08PG* filament_max_volumetric_speed = 8 [filament:Fiberlogy Easy PLA] @@ -10100,6 +11116,15 @@ inherits = Fiberlogy Easy PLA; *PLA06PG* [filament:Fiberlogy Easy PLA @PG 0.8] inherits = Fiberlogy Easy PLA; *PLA08PG* +[filament:Fiberlogy Easy PLA @XL] +inherits = Fiberlogy Easy PLA @PG; *PLAXL* + +[filament:Fiberlogy Easy PLA @XL 0.6] +inherits = Fiberlogy Easy PLA @PG 0.6; *PLA06XL* + +[filament:Fiberlogy Easy PLA @XL 0.8] +inherits = Fiberlogy Easy PLA @PG 0.8; *PLA08XL* + [filament:Fiberlogy Easy PET-G] inherits = *PET* renamed_from = Fiberlogy PETG @@ -10128,6 +11153,15 @@ inherits = Fiberlogy Easy PET-G; *PET06PG* [filament:Fiberlogy Easy PET-G @PG 0.8] inherits = Fiberlogy Easy PET-G; *PET08PG* +[filament:Fiberlogy Easy PET-G @XL] +inherits = Fiberlogy Easy PET-G @PG; *PETXL* + +[filament:Fiberlogy Easy PET-G @XL 0.6] +inherits = Fiberlogy Easy PET-G @PG 0.6; *PET06XL* + +[filament:Fiberlogy Easy PET-G @XL 0.8] +inherits = Fiberlogy Easy PET-G @PG 0.8; *PET08XL* + [filament:Fiberlogy ASA] inherits = *ABS* filament_vendor = Fiberlogy @@ -10149,25 +11183,28 @@ filament_type = ASA fan_below_layer_time = 30 disable_fan_first_layers = 5 -[filament:Fiberlogy ASA @PG] -inherits = Fiberlogy ASA; *ABSPG* +[filament:Fiberlogy ASA @XL] +inherits = Fiberlogy ASA; *ABSPG*; *ABSXL* +renamed_from = "Fiberlogy ASA @PG" first_layer_bed_temperature = 100 bed_temperature = 105 -[filament:Fiberlogy ASA @PG 0.6] -inherits = Fiberlogy ASA @PG; *ABS06PG* +[filament:Fiberlogy ASA @XL 0.6] +inherits = Fiberlogy ASA @XL; *ABS06XL* +renamed_from = "Fiberlogy ASA @PG 0.6" -[filament:Fiberlogy ASA @PG 0.8] -inherits = Fiberlogy ASA @PG; *ABS08PG* +[filament:Fiberlogy ASA @XL 0.8] +inherits = Fiberlogy ASA @XL; *ABS08XL* +renamed_from = "Fiberlogy ASA @PG 0.8" [filament:Fiberlogy ASA @MK4] -inherits = Fiberlogy ASA; *ABSMK4* +inherits = Fiberlogy ASA; *ABSPG* [filament:Fiberlogy ASA @MK4 0.6] -inherits = Fiberlogy ASA @MK4; *ABS06MK4* +inherits = Fiberlogy ASA @MK4; *ABS06PG* [filament:Fiberlogy ASA @MK4 0.8] -inherits = Fiberlogy ASA @MK4; *ABS08MK4* +inherits = Fiberlogy ASA @MK4; *ABS08PG* [filament:Fiberlogy ASA @MINI] inherits = Fiberlogy ASA; *ABSMINI* @@ -10192,15 +11229,21 @@ disable_fan_first_layers = 5 [filament:Fiberlogy Easy ABS @PG] inherits = Fiberlogy Easy ABS; *ABSPG* -compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fiberlogy Easy ABS @PG 0.6] inherits = Fiberlogy Easy ABS; *ABS06PG* -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fiberlogy Easy ABS @PG 0.8] inherits = Fiberlogy Easy ABS; *ABS08PG* -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material + +[filament:Fiberlogy Easy ABS @XL] +inherits = Fiberlogy Easy ABS @PG; *ABSXL* + +[filament:Fiberlogy Easy ABS @XL 0.6] +inherits = Fiberlogy Easy ABS @PG 0.6; *ABS06XL* + +[filament:Fiberlogy Easy ABS @XL 0.8] +inherits = Fiberlogy Easy ABS @PG 0.8; *ABS08XL* [filament:Fiberlogy Easy ABS @MINI] inherits = Fiberlogy Easy ABS; *ABSMINI* @@ -10238,6 +11281,15 @@ inherits = Fiberlogy CPE HT @PG; *PET06PG* [filament:Fiberlogy CPE HT @PG 0.8] inherits = Fiberlogy CPE HT @PG; *PET08PG* +[filament:Fiberlogy CPE HT @XL] +inherits = Fiberlogy CPE HT @PG; *PETXL* + +[filament:Fiberlogy CPE HT @XL 0.6] +inherits = Fiberlogy CPE HT @PG 0.6; *PET06XL* + +[filament:Fiberlogy CPE HT @XL 0.8] +inherits = Fiberlogy CPE HT @PG 0.8; *PET08XL* + [filament:Fiberlogy PCTG] inherits = Fiberlogy CPE HT filament_cost = 29.41 @@ -10260,6 +11312,15 @@ inherits = Fiberlogy PCTG; *PET06PG* [filament:Fiberlogy PCTG @PG 0.8] inherits = Fiberlogy PCTG; *PET08PG* +[filament:Fiberlogy PCTG @XL] +inherits = Fiberlogy PCTG @PG; *PETXL* + +[filament:Fiberlogy PCTG @XL 0.6] +inherits = Fiberlogy PCTG @PG 0.6; *PET06XL* + +[filament:Fiberlogy PCTG @XL 0.8] +inherits = Fiberlogy PCTG @PG 0.8; *PET08XL* + [filament:Fiberlogy PCTG @MINI] inherits = Fiberlogy PCTG; *PETMINI* @@ -10290,24 +11351,31 @@ min_print_speed = 15 cooling = 1 filament_spool_weight = 330 -[filament:Fiberlogy FiberFlex 40D @PG] -inherits = Fiberlogy FiberFlex 40D; *FLEXPG* +[filament:Fiberlogy FiberFlex 40D @XL] +inherits = Fiberlogy FiberFlex 40D; *FLEXXL* +renamed_from = "Fiberlogy FiberFlex 40D @PG" filament_max_volumetric_speed = 3 filament_retract_length = 2.2 extrusion_multiplier = 1.1 first_layer_temperature = 220 temperature = 220 +filament_multitool_ramming_flow = 3 +filament_retract_length_toolchange = 2.2 -[filament:Fiberlogy FiberFlex 40D @PG 0.6] -inherits = Fiberlogy FiberFlex 40D @PG; *FLEX06PG* +[filament:Fiberlogy FiberFlex 40D @XL 0.6] +inherits = Fiberlogy FiberFlex 40D @XL; *FLEX06XL* +renamed_from = "Fiberlogy FiberFlex 40D @PG 0.6" filament_max_volumetric_speed = 4.5 +filament_multitool_ramming_flow = 4.5 -[filament:Fiberlogy FiberFlex 40D @PG 0.8] -inherits = Fiberlogy FiberFlex 40D @PG; *FLEX08PG* +[filament:Fiberlogy FiberFlex 40D @XL 0.8] +inherits = Fiberlogy FiberFlex 40D @XL; *FLEX08XL* +renamed_from = "Fiberlogy FiberFlex 40D @PG 0.8" filament_max_volumetric_speed = 9 +filament_multitool_ramming_flow = 9 [filament:Fiberlogy FiberFlex 40D @MK4] -inherits = Fiberlogy FiberFlex 40D; *FLEXMK4* +inherits = Fiberlogy FiberFlex 40D; *FLEXPG* filament_max_volumetric_speed = 4 filament_retract_length = 2.2 extrusion_multiplier = 1.1 @@ -10315,11 +11383,11 @@ first_layer_temperature = 220 temperature = 220 [filament:Fiberlogy FiberFlex 40D @MK4 0.6] -inherits = Fiberlogy FiberFlex 40D @MK4; *FLEX06MK4* +inherits = Fiberlogy FiberFlex 40D @MK4; *FLEX06PG* filament_max_volumetric_speed = 6 [filament:Fiberlogy FiberFlex 40D @MK4 0.8] -inherits = Fiberlogy FiberFlex 40D @MK4; *FLEX08MK4* +inherits = Fiberlogy FiberFlex 40D @MK4; *FLEX08PG* filament_max_volumetric_speed = 10 [filament:Fiberlogy FiberFlex 40D @MINI] @@ -10351,30 +11419,37 @@ filament_retract_before_travel = 2 filament_cost = 49.11 filament_retract_length = 1.2 -[filament:Fiberlogy MattFlex 40D @PG] -inherits = Fiberlogy MattFlex 40D; *FLEXPG* +[filament:Fiberlogy MattFlex 40D @XL] +inherits = Fiberlogy MattFlex 40D; *FLEXXL* +renamed_from = "Fiberlogy MattFlex 40D @PG" filament_max_volumetric_speed = 3 filament_retract_length = 2.2 +filament_multitool_ramming_flow = 3 +filament_retract_length_toolchange = 2.2 -[filament:Fiberlogy MattFlex 40D @PG 0.6] -inherits = Fiberlogy MattFlex 40D @PG; *FLEX06PG* +[filament:Fiberlogy MattFlex 40D @XL 0.6] +inherits = Fiberlogy MattFlex 40D @XL; *FLEX06XL* +renamed_from = "Fiberlogy MattFlex 40D @PG 0.6" filament_max_volumetric_speed = 4.5 +filament_multitool_ramming_flow = 4.5 -[filament:Fiberlogy MattFlex 40D @PG 0.8] -inherits = Fiberlogy MattFlex 40D @PG; *FLEX08PG* +[filament:Fiberlogy MattFlex 40D @XL 0.8] +inherits = Fiberlogy MattFlex 40D @XL; *FLEX08XL* +renamed_from = "Fiberlogy MattFlex 40D @PG 0.8" filament_max_volumetric_speed = 8 +filament_multitool_ramming_flow = 8 [filament:Fiberlogy MattFlex 40D @MK4] -inherits = Fiberlogy MattFlex 40D; *FLEXMK4* +inherits = Fiberlogy MattFlex 40D; *FLEXPG* filament_max_volumetric_speed = 4 filament_retract_length = 2.2 [filament:Fiberlogy MattFlex 40D @MK4 0.6] -inherits = Fiberlogy MattFlex 40D @MK4; *FLEX06MK4* +inherits = Fiberlogy MattFlex 40D @MK4; *FLEX06PG* filament_max_volumetric_speed = 6 [filament:Fiberlogy MattFlex 40D @MK4 0.8] -inherits = Fiberlogy MattFlex 40D @MK4; *FLEX08MK4* +inherits = Fiberlogy MattFlex 40D @MK4; *FLEX08PG* filament_max_volumetric_speed = 10 [filament:Fiberlogy FiberFlex 30D] @@ -10388,8 +11463,9 @@ max_fan_speed = 60 filament_density = 1.07 filament_retract_length = 1.2 -[filament:Fiberlogy FiberFlex 30D @PG] -inherits = Fiberlogy FiberFlex 30D; *FLEXPG* +[filament:Fiberlogy FiberFlex 30D @XL] +inherits = Fiberlogy FiberFlex 30D; *FLEXXL* +renamed_from = "Fiberlogy FiberFlex 30D @PG" filament_max_volumetric_speed = 2.5 filament_retract_length = 3 first_layer_temperature = 220 @@ -10397,17 +11473,22 @@ temperature = 220 first_layer_bed_temperature = 55 bed_temperature = 55 extrusion_multiplier = 1.1 +filament_retract_length_toolchange = 3 -[filament:Fiberlogy FiberFlex 30D @PG 0.6] -inherits = Fiberlogy FiberFlex 30D @PG; *FLEX06PG* +[filament:Fiberlogy FiberFlex 30D @XL 0.6] +inherits = Fiberlogy FiberFlex 30D @XL; *FLEX06XL* +renamed_from = "Fiberlogy FiberFlex 30D @PG 0.6" filament_max_volumetric_speed = 5 +filament_multitool_ramming_flow = 5 -[filament:Fiberlogy FiberFlex 30D @PG 0.8] -inherits = Fiberlogy FiberFlex 30D @PG; *FLEX08PG* +[filament:Fiberlogy FiberFlex 30D @XL 0.8] +inherits = Fiberlogy FiberFlex 30D @XL; *FLEX08XL* +renamed_from = "Fiberlogy FiberFlex 30D @PG 0.8" filament_max_volumetric_speed = 8 +filament_multitool_ramming_flow = 8 [filament:Fiberlogy FiberFlex 30D @MK4] -inherits = Fiberlogy FiberFlex 30D; *FLEXMK4* +inherits = Fiberlogy FiberFlex 30D; *FLEXPG* filament_max_volumetric_speed = 3.5 filament_retract_length = 3 first_layer_temperature = 220 @@ -10417,11 +11498,11 @@ bed_temperature = 55 extrusion_multiplier = 1.1 [filament:Fiberlogy FiberFlex 30D @MK4 0.6] -inherits = Fiberlogy FiberFlex 30D @MK4; *FLEX06MK4* +inherits = Fiberlogy FiberFlex 30D @MK4; *FLEX06PG* filament_max_volumetric_speed = 7 [filament:Fiberlogy FiberFlex 30D @MK4 0.8] -inherits = Fiberlogy FiberFlex 30D @MK4; *FLEX08MK4* +inherits = Fiberlogy FiberFlex 30D @MK4; *FLEX08PG* filament_max_volumetric_speed = 10 [filament:Fiberlogy FiberSatin] @@ -10441,6 +11522,15 @@ inherits = Fiberlogy FiberSatin; *PLA06PG* [filament:Fiberlogy FiberSatin @PG 0.8] inherits = Fiberlogy FiberSatin; *PLA08PG* +[filament:Fiberlogy FiberSatin @XL] +inherits = Fiberlogy FiberSatin @PG; *PLAXL* + +[filament:Fiberlogy FiberSatin @XL 0.6] +inherits = Fiberlogy FiberSatin @PG 0.6; *PLA06XL* + +[filament:Fiberlogy FiberSatin @XL 0.8] +inherits = Fiberlogy FiberSatin @PG 0.8; *PLA08XL* + [filament:Fiberlogy FiberSilk] inherits = Fiberlogy FiberSatin first_layer_temperature = 230 @@ -10458,6 +11548,15 @@ inherits = Fiberlogy FiberSilk; *PLA06PG* [filament:Fiberlogy FiberSilk @PG 0.8] inherits = Fiberlogy FiberSilk; *PLA08PG* +[filament:Fiberlogy FiberSilk @XL] +inherits = Fiberlogy FiberSilk @PG; *PLAXL* + +[filament:Fiberlogy FiberSilk @XL 0.6] +inherits = Fiberlogy FiberSilk @PG 0.6; *PLA06XL* + +[filament:Fiberlogy FiberSilk @XL 0.8] +inherits = Fiberlogy FiberSilk @PG 0.8; *PLA08XL* + [filament:Fiberlogy FiberWood] inherits = Fiberlogy Easy PLA first_layer_temperature = 185 @@ -10481,6 +11580,15 @@ filament_max_volumetric_speed = 15 first_layer_temperature = 195 temperature = 195 +[filament:Fiberlogy FiberWood @XL] +inherits = Fiberlogy FiberWood @PG; *PLAXL* + +[filament:Fiberlogy FiberWood @XL 0.6] +inherits = Fiberlogy FiberWood @PG 0.6; *PLA06XL* + +[filament:Fiberlogy FiberWood @XL 0.8] +inherits = Fiberlogy FiberWood @PG 0.8; *PLA08XL* + [filament:Fiberlogy HD PLA] inherits = Fiberlogy Easy PLA first_layer_temperature = 230 @@ -10498,6 +11606,15 @@ inherits = Fiberlogy HD PLA; *PLA06PG* [filament:Fiberlogy HD PLA @PG 0.8] inherits = Fiberlogy HD PLA; *PLA08PG* +[filament:Fiberlogy HD PLA @XL] +inherits = Fiberlogy HD PLA @PG; *PLAXL* + +[filament:Fiberlogy HD PLA @XL 0.6] +inherits = Fiberlogy HD PLA @PG 0.6; *PLA06XL* + +[filament:Fiberlogy HD PLA @XL 0.8] +inherits = Fiberlogy HD PLA @PG 0.8; *PLA08XL* + [filament:Fiberlogy PLA Mineral] inherits = Fiberlogy Easy PLA first_layer_temperature = 195 @@ -10521,6 +11638,15 @@ filament_max_volumetric_speed = 14 first_layer_temperature = 200 temperature = 200 +[filament:Fiberlogy PLA Mineral @XL] +inherits = Fiberlogy PLA Mineral @PG; *PLAXL* + +[filament:Fiberlogy PLA Mineral @XL 0.6] +inherits = Fiberlogy PLA Mineral @PG 0.6; *PLA06XL* + +[filament:Fiberlogy PLA Mineral @XL 0.8] +inherits = Fiberlogy PLA Mineral @PG 0.8; *PLA08XL* + [filament:Fiberlogy Impact PLA] inherits = Fiberlogy HD PLA filament_density = 1.22 @@ -10535,6 +11661,15 @@ inherits = Fiberlogy Impact PLA; *PLA06PG* [filament:Fiberlogy Impact PLA @PG 0.8] inherits = Fiberlogy Impact PLA; *PLA08PG* +[filament:Fiberlogy Impact PLA @XL] +inherits = Fiberlogy Impact PLA @PG; *PLAXL* + +[filament:Fiberlogy Impact PLA @XL 0.6] +inherits = Fiberlogy Impact PLA @PG 0.6; *PLA06XL* + +[filament:Fiberlogy Impact PLA @XL 0.8] +inherits = Fiberlogy Impact PLA @PG 0.8; *PLA08XL* + [filament:Fiberlogy Nylon PA12] inherits = Fiberlogy ASA filament_type = PA @@ -10553,30 +11688,33 @@ filament_retract_lift = 0.2 filament_max_volumetric_speed = 6 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.02{elsif nozzle_diameter[0]==0.6}0.04{else}0.08{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K26{elsif nozzle_diameter[0]==0.8};{else}M900 K45{endif} ; Filament gcode LA 1.0" -[filament:Fiberlogy Nylon PA12 @PG] -inherits = Fiberlogy Nylon PA12; *ABSPG* +[filament:Fiberlogy Nylon PA12 @XL] +inherits = Fiberlogy Nylon PA12; *ABSPG*; *ABSXL* +renamed_from = "Fiberlogy Nylon PA12 @PG" first_layer_bed_temperature = 100 bed_temperature = 105 filament_max_volumetric_speed = 6 -[filament:Fiberlogy Nylon PA12 @PG 0.6] -inherits = Fiberlogy Nylon PA12 @PG; *ABS06PG* +[filament:Fiberlogy Nylon PA12 @XL 0.6] +inherits = Fiberlogy Nylon PA12 @XL; *ABS06XL* +renamed_from = "Fiberlogy Nylon PA12 @PG 0.6" filament_max_volumetric_speed = 8 -[filament:Fiberlogy Nylon PA12 @PG 0.8] -inherits = Fiberlogy Nylon PA12 @PG; *ABS08PG* +[filament:Fiberlogy Nylon PA12 @XL 0.8] +inherits = Fiberlogy Nylon PA12 @XL; *ABS08XL* +renamed_from = "Fiberlogy Nylon PA12 @PG 0.8" filament_max_volumetric_speed = 11 [filament:Fiberlogy Nylon PA12 @MK4] -inherits = Fiberlogy Nylon PA12; *ABSMK4* +inherits = Fiberlogy Nylon PA12; *ABSPG* filament_max_volumetric_speed = 6 [filament:Fiberlogy Nylon PA12 @MK4 0.6] -inherits = Fiberlogy Nylon PA12 @MK4; *ABS06MK4* +inherits = Fiberlogy Nylon PA12 @MK4; *ABS06PG* filament_max_volumetric_speed = 8 [filament:Fiberlogy Nylon PA12 @MK4 0.8] -inherits = Fiberlogy Nylon PA12 @MK4; *ABS08MK4* +inherits = Fiberlogy Nylon PA12 @MK4; *ABS08PG* filament_max_volumetric_speed = 11 [filament:Fiberlogy Nylon PA12+CF15] @@ -10596,32 +11734,34 @@ fan_always_on = 0 filament_max_volumetric_speed = 8 compatible_printers_condition = nozzle_diameter[0]>=0.4 and printer_model!="MK2SMM" and printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! single_extruder_multi_material -[filament:Fiberlogy Nylon PA12+CF15 @PG] -inherits = Fiberlogy Nylon PA12+CF15; *ABSPG*; *04PLUSPG* +[filament:Fiberlogy Nylon PA12+CF15 @XL] +inherits = Fiberlogy Nylon PA12+CF15; *ABSPG*; *ABSXL*; *04PLUSXL* +renamed_from = "Fiberlogy Nylon PA12+CF15 @PG" first_layer_bed_temperature = 100 bed_temperature = 105 filament_max_volumetric_speed = 8 -compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_model=~/.*XL.*/ and ! single_extruder_multi_material -[filament:Fiberlogy Nylon PA12+CF15 @PG 0.6] -inherits = Fiberlogy Nylon PA12+CF15 @PG; *ABS06PG* +[filament:Fiberlogy Nylon PA12+CF15 @XL 0.6] +inherits = Fiberlogy Nylon PA12+CF15 @XL; *ABS06XL* +renamed_from = "Fiberlogy Nylon PA12+CF15 @PG 0.6" filament_max_volumetric_speed = 10 -[filament:Fiberlogy Nylon PA12+CF15 @PG 0.8] -inherits = Fiberlogy Nylon PA12+CF15 @PG; *ABS08PG* +[filament:Fiberlogy Nylon PA12+CF15 @XL 0.8] +inherits = Fiberlogy Nylon PA12+CF15 @XL; *ABS08XL* +renamed_from = "Fiberlogy Nylon PA12+CF15 @PG 0.8" filament_max_volumetric_speed = 12 [filament:Fiberlogy Nylon PA12+CF15 @MK4] -inherits = Fiberlogy Nylon PA12+CF15; *ABSMK4*; *04PLUSPG* +inherits = Fiberlogy Nylon PA12+CF15; *ABSPG*; *04PLUSPG* filament_max_volumetric_speed = 8 compatible_printers_condition = nozzle_diameter[0]>=0.4 and nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_model=~/(MK4|MK4IS)/ and ! single_extruder_multi_material [filament:Fiberlogy Nylon PA12+CF15 @MK4 0.6] -inherits = Fiberlogy Nylon PA12+CF15 @MK4; *ABS06MK4* +inherits = Fiberlogy Nylon PA12+CF15 @MK4; *ABS06PG* filament_max_volumetric_speed = 10 [filament:Fiberlogy Nylon PA12+CF15 @MK4 0.8] -inherits = Fiberlogy Nylon PA12+CF15 @MK4; *ABS08MK4* +inherits = Fiberlogy Nylon PA12+CF15 @MK4; *ABS08PG* filament_max_volumetric_speed = 12 [filament:Fiberlogy Nylon PA12+GF15] @@ -10629,16 +11769,19 @@ inherits = Fiberlogy Nylon PA12+CF15 filament_density = 1.13 filament_max_volumetric_speed = 8 -[filament:Fiberlogy Nylon PA12+GF15 @PG] -inherits = Fiberlogy Nylon PA12+CF15 @PG +[filament:Fiberlogy Nylon PA12+GF15 @XL] +inherits = Fiberlogy Nylon PA12+CF15 @XL +renamed_from = "Fiberlogy Nylon PA12+GF15 @PG" filament_density = 1.13 -[filament:Fiberlogy Nylon PA12+GF15 @PG 0.6] -inherits = Fiberlogy Nylon PA12+CF15 @PG 0.6 +[filament:Fiberlogy Nylon PA12+GF15 @XL 0.6] +inherits = Fiberlogy Nylon PA12+CF15 @XL 0.6 +renamed_from = "Fiberlogy Nylon PA12+GF15 @PG 0.6" filament_density = 1.13 -[filament:Fiberlogy Nylon PA12+GF15 @PG 0.8] -inherits = Fiberlogy Nylon PA12+CF15 @PG 0.8 +[filament:Fiberlogy Nylon PA12+GF15 @XL 0.8] +inherits = Fiberlogy Nylon PA12+CF15 @XL 0.8 +renamed_from = "Fiberlogy Nylon PA12+GF15 @PG 0.8" filament_density = 1.13 [filament:Fiberlogy Nylon PA12+GF15 @MK4] @@ -10679,19 +11822,25 @@ filament_max_volumetric_speed = 5 [filament:Fiberlogy PP @PG] inherits = Fiberlogy PP; *ABSPG* filament_max_volumetric_speed = 5 -compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fiberlogy PP @PG 0.6] inherits = Fiberlogy PP @PG; *ABS06PG* filament_max_volumetric_speed = 7 -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fiberlogy PP @PG 0.8] inherits = Fiberlogy PP @PG; *ABS08PG* filament_max_volumetric_speed = 10 first_layer_temperature = 250 temperature = 250 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material + +[filament:Fiberlogy PP @XL] +inherits = Fiberlogy PP @PG; *ABSXL* + +[filament:Fiberlogy PP @XL 0.6] +inherits = Fiberlogy PP @PG 0.6; *ABS06XL* + +[filament:Fiberlogy PP @XL 0.8] +inherits = Fiberlogy PP @PG 0.8; *ABS08XL* [filament:Filament PM PLA] inherits = *PLA* @@ -10713,6 +11862,15 @@ inherits = Filament PM PLA; *PLA08PG* first_layer_temperature = 220 temperature = 220 +[filament:Filament PM PLA @XL] +inherits = Filament PM PLA @PG; *PLAXL* + +[filament:Filament PM PLA @XL 0.6] +inherits = Filament PM PLA @PG 0.6; *PLA06XL* + +[filament:Filament PM PLA @XL 0.8] +inherits = Filament PM PLA @PG 0.8; *PLA08XL* + [filament:AmazonBasics PLA] inherits = *PLA* filament_vendor = AmazonBasics @@ -10730,6 +11888,15 @@ inherits = AmazonBasics PLA; *PLA08PG* first_layer_temperature = 220 temperature = 220 +[filament:AmazonBasics PLA @XL] +inherits = AmazonBasics PLA @PG; *PLAXL* + +[filament:AmazonBasics PLA @XL 0.6] +inherits = AmazonBasics PLA @PG 0.6; *PLA06XL* + +[filament:AmazonBasics PLA @XL 0.8] +inherits = AmazonBasics PLA @PG 0.8; *PLA08XL* + [filament:Overture PLA] inherits = *PLA* filament_vendor = Overture @@ -10748,6 +11915,15 @@ inherits = Overture PLA; *PLA08PG* first_layer_temperature = 220 temperature = 220 +[filament:Overture PLA @XL] +inherits = Overture PLA @PG; *PLAXL* + +[filament:Overture PLA @XL 0.6] +inherits = Overture PLA @PG 0.6; *PLA06XL* + +[filament:Overture PLA @XL 0.8] +inherits = Overture PLA @PG 0.8; *PLA08XL* + [filament:Hatchbox PLA] inherits = *PLA* filament_vendor = Hatchbox @@ -10770,6 +11946,15 @@ inherits = Hatchbox PLA; *PLA08PG* first_layer_temperature = 220 temperature = 220 +[filament:Hatchbox PLA @XL] +inherits = Hatchbox PLA @PG; *PLAXL* + +[filament:Hatchbox PLA @XL 0.6] +inherits = Hatchbox PLA @PG 0.6; *PLA06XL* + +[filament:Hatchbox PLA @XL 0.8] +inherits = Hatchbox PLA @PG 0.8; *PLA08XL* + [filament:Esun PLA] inherits = *PLA* filament_vendor = Esun @@ -10790,6 +11975,15 @@ inherits = Esun PLA; *PLA08PG* first_layer_temperature = 220 temperature = 220 +[filament:Esun PLA @XL] +inherits = Esun PLA @PG; *PLAXL* + +[filament:Esun PLA @XL 0.6] +inherits = Esun PLA @PG 0.6; *PLA06XL* + +[filament:Esun PLA @XL 0.8] +inherits = Esun PLA @PG 0.8; *PLA08XL* + [filament:Das Filament PLA] inherits = *PLA* filament_vendor = Das Filament @@ -10807,6 +12001,15 @@ inherits = Das Filament PLA; *PLA08PG* first_layer_temperature = 220 temperature = 220 +[filament:Das Filament PLA @XL] +inherits = Das Filament PLA @PG; *PLAXL* + +[filament:Das Filament PLA @XL 0.6] +inherits = Das Filament PLA @PG 0.6; *PLA06XL* + +[filament:Das Filament PLA @XL 0.8] +inherits = Das Filament PLA @PG 0.8; *PLA08XL* + [filament:EUMAKERS PLA] inherits = *PLA* filament_vendor = EUMAKERS @@ -10825,6 +12028,15 @@ inherits = EUMAKERS PLA; *PLA08PG* first_layer_temperature = 220 temperature = 220 +[filament:EUMAKERS PLA @XL] +inherits = EUMAKERS PLA @PG; *PLAXL* + +[filament:EUMAKERS PLA @XL 0.6] +inherits = EUMAKERS PLA @PG 0.6; *PLA06XL* + +[filament:EUMAKERS PLA @XL 0.8] +inherits = EUMAKERS PLA @PG 0.8; *PLA08XL* + [filament:Floreon3D PLA] inherits = *PLA* filament_vendor = Floreon3D @@ -10842,6 +12054,15 @@ inherits = Floreon3D PLA; *PLA08PG* first_layer_temperature = 220 temperature = 220 +[filament:Floreon3D PLA @XL] +inherits = Floreon3D PLA @PG; *PLAXL* + +[filament:Floreon3D PLA @XL 0.6] +inherits = Floreon3D PLA @PG 0.6; *PLA06XL* + +[filament:Floreon3D PLA @XL 0.8] +inherits = Floreon3D PLA @PG 0.8; *PLA08XL* + [filament:Prusament PLA] inherits = *PLA* filament_vendor = Prusa Polymers @@ -10854,31 +12075,40 @@ compatible_printers_condition = nozzle_diameter[0]!=0.8 and printer_notes!~/.*PG [filament:Prusament PLA @PG] inherits = Prusament PLA; *PLAPG* -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model!="MK4IS" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and printer_model!="MK4IS" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 [filament:Prusament PLA @PG 0.6] inherits = Prusament PLA; *PLA06PG* filament_max_volumetric_speed = 16 -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model!="MK4IS" and nozzle_diameter[0]==0.6 +compatible_printers_condition = printer_notes=~/.*MK4.*/ and printer_model!="MK4IS" and nozzle_diameter[0]==0.6 [filament:Prusament PLA @PG 0.8] inherits = Prusament PLA; *PLA08PG* first_layer_temperature = 230 temperature = 225 +[filament:Prusament PLA @XL] +inherits = Prusament PLA @PG; *PLAXL* + +[filament:Prusament PLA @XL 0.6] +inherits = Prusament PLA @PG 0.6; *PLA06XL* + +[filament:Prusament PLA @XL 0.8] +inherits = Prusament PLA @PG 0.8; *PLA08XL* + [filament:Prusament PLA @PGIS] inherits = Prusament PLA @PG renamed_from = "Prusament PLA @MK4IS" first_layer_temperature = 230 temperature = 225 slowdown_below_layer_time = 6 -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 [filament:Prusament PLA @PGIS 0.6] inherits = Prusament PLA @PG 0.6 first_layer_temperature = 230 temperature = 225 -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]==0.6 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.6 [filament:Prusament PLA Blend @PGIS] inherits = Prusament PLA @PG @@ -10888,20 +12118,20 @@ temperature = 225 filament_max_volumetric_speed = 7 slowdown_below_layer_time = 7 start_filament_gcode = "M900 K{if nozzle_diameter[0]==0.4}0.05{elsif nozzle_diameter[0]==0.25}0.14{elsif nozzle_diameter[0]==0.3}0.07{elsif nozzle_diameter[0]==0.35}0.06{elsif nozzle_diameter[0]==0.6}0.03{elsif nozzle_diameter[0]==0.5}0.035{elsif nozzle_diameter[0]==0.8}0.015{else}0{endif} ; Filament gcode\n\n{if printer_notes=~/.*PRINTER_MODEL_MK4IS.*/}\nM572 S{if nozzle_diameter[0]==0.4}0.035{elsif nozzle_diameter[0]==0.5}0.022{elsif nozzle_diameter[0]==0.6}0.018{elsif nozzle_diameter[0]==0.8}0.012{elsif nozzle_diameter[0]==0.25}0.12{elsif nozzle_diameter[0]==0.3}0.075{else}0{endif} ; Filament gcode\n{endif}\n\nM142 S36 ; set heatbreak target temp" -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.6 [filament:Prusament PLA Blend @PGIS 0.6] inherits = Prusament PLA Blend @PGIS filament_max_volumetric_speed = 9 slowdown_below_layer_time = 14 -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]==0.6 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.6 [filament:Prusament PLA Blend @PGIS 0.8] inherits = Prusament PLA Blend @PGIS filament_max_volumetric_speed = 12 slowdown_below_layer_time = 20 temperature = 230 -compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_model=="MK4IS" and nozzle_diameter[0]==0.8 +compatible_printers_condition = printer_notes=~/.*PG.*/ and printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.8 [filament:Prusament PVB] inherits = *PLA* @@ -10932,6 +12162,15 @@ inherits = Prusament PVB @PG; *PLA08PG* first_layer_temperature = 225 temperature = 225 +[filament:Prusament PVB @XL] +inherits = Prusament PVB @PG; *PLAXL* + +[filament:Prusament PVB @XL 0.6] +inherits = Prusament PVB @PG 0.6; *PLA06XL* + +[filament:Prusament PVB @XL 0.8] +inherits = Prusament PVB @PG 0.8; *PLA08XL* + [filament:*PLA MMU2*] inherits = Prusa PLA compatible_printers_condition = nozzle_diameter[0]!=0.8 and nozzle_diameter[0]!=0.25 and printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material @@ -11035,32 +12274,39 @@ full_fan_speed_layer = 6 filament_retract_length = 1.2 filament_deretract_speed = 20 -[filament:Fillamentum Flexfill 98A @PG] -inherits = Fillamentum Flexfill 98A; *FLEXPG* +[filament:Fillamentum Flexfill 98A @XL] +inherits = Fillamentum Flexfill 98A; *FLEXXL* +renamed_from = "Fillamentum Flexfill 98A @PG" filament_max_volumetric_speed = 2.5 filament_retract_length = 3 extrusion_multiplier = 1.08 +filament_multitool_ramming_flow = 2.5 +filament_retract_length_toolchange = 3 -[filament:Fillamentum Flexfill 98A @PG 0.6] -inherits = Fillamentum Flexfill 98A @PG; *FLEX06PG* +[filament:Fillamentum Flexfill 98A @XL 0.6] +inherits = Fillamentum Flexfill 98A @XL; *FLEX06XL* +renamed_from = "Fillamentum Flexfill 98A @PG 0.6" filament_max_volumetric_speed = 3 +filament_multitool_ramming_flow = 3 -[filament:Fillamentum Flexfill 98A @PG 0.8] -inherits = Fillamentum Flexfill 98A @PG; *FLEX08PG* +[filament:Fillamentum Flexfill 98A @XL 0.8] +inherits = Fillamentum Flexfill 98A @XL; *FLEX08XL* +renamed_from = "Fillamentum Flexfill 98A @PG 0.8" filament_max_volumetric_speed = 8 +filament_multitool_ramming_flow = 8 [filament:Fillamentum Flexfill 98A @MK4] -inherits = Fillamentum Flexfill 98A; *FLEXMK4* +inherits = Fillamentum Flexfill 98A; *FLEXPG* filament_max_volumetric_speed = 3 filament_retract_length = 3 extrusion_multiplier = 1.08 [filament:Fillamentum Flexfill 98A @MK4 0.6] -inherits = Fillamentum Flexfill 98A @MK4; *FLEX06MK4* +inherits = Fillamentum Flexfill 98A @MK4; *FLEX06PG* filament_max_volumetric_speed = 4 [filament:Fillamentum Flexfill 98A @MK4 0.8] -inherits = Fillamentum Flexfill 98A @MK4; *FLEX08MK4* +inherits = Fillamentum Flexfill 98A @MK4; *FLEX08PG* filament_max_volumetric_speed = 10 [filament:ColorFabb VarioShore TPU] @@ -11074,12 +12320,15 @@ extrusion_multiplier = 0.85 first_layer_temperature = 220 temperature = 220 -[filament:ColorFabb VarioShore TPU @PG] -inherits = ColorFabb VarioShore TPU; *FLEXPG* +[filament:ColorFabb VarioShore TPU @XL] +inherits = ColorFabb VarioShore TPU; *FLEXXL* +renamed_from = "ColorFabb VarioShore TPU @PG" filament_max_volumetric_speed = 1.5 +filament_multitool_ramming_flow = 1.5 +filament_retract_length_toolchange = nil [filament:ColorFabb VarioShore TPU @MK4] -inherits = ColorFabb VarioShore TPU; *FLEXMK4* +inherits = ColorFabb VarioShore TPU; *FLEXPG* filament_max_volumetric_speed = 1.5 [filament:Taulman Bridge] @@ -11105,31 +12354,34 @@ min_fan_speed = 0 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.6}0.12{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/ and nozzle_diameter[0]==0.8}0.06{elsif printer_notes=~/.*PRINTER_MODEL_MINI.*/}0.2{elsif nozzle_diameter[0]==0.8}0.02{elsif nozzle_diameter[0]==0.6}0.04{else}0.08{endif} ; Filament gcode LA 1.5\n{if printer_notes=~/.*PRINTER_MODEL_MINI.*/};{elsif printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}M900 K200{elsif nozzle_diameter[0]==0.6}M900 K24{elsif nozzle_diameter[0]==0.8};{else}M900 K45{endif} ; Filament gcode LA 1.0" compatible_printers_condition = printer_model!="MINI" and printer_notes!~/.*PG.*/ and ! (printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK(2.5|3).*/ and single_extruder_multi_material) -[filament:Taulman Bridge @PG] -inherits = Taulman Bridge; *ABSPG* +[filament:Taulman Bridge @XL] +inherits = Taulman Bridge; *ABSPG*; *ABSXL* +renamed_from = "Taulman Bridge @PG" bed_temperature = 105 filament_max_volumetric_speed = 7 -[filament:Taulman Bridge @PG 0.6] -inherits = Taulman Bridge @PG; *ABS06PG* +[filament:Taulman Bridge @XL 0.6] +inherits = Taulman Bridge @XL; *ABS06XL* +renamed_from = "Taulman Bridge @PG 0.6" filament_max_volumetric_speed = 9 -[filament:Taulman Bridge @PG 0.8] -inherits = Taulman Bridge @PG; *ABS08PG* +[filament:Taulman Bridge @XL 0.8] +inherits = Taulman Bridge @XL; *ABS08XL* +renamed_from = "Taulman Bridge @PG 0.8" filament_max_volumetric_speed = 12 first_layer_temperature = 270 temperature = 270 [filament:Taulman Bridge @MK4] -inherits = Taulman Bridge; *ABSMK4* +inherits = Taulman Bridge; *ABSPG* filament_max_volumetric_speed = 7 [filament:Taulman Bridge @MK4 0.6] -inherits = Taulman Bridge @MK4; *ABS06MK4* +inherits = Taulman Bridge @MK4; *ABS06PG* filament_max_volumetric_speed = 9 [filament:Taulman Bridge @MK4 0.8] -inherits = Taulman Bridge @MK4; *ABS08MK4* +inherits = Taulman Bridge @MK4; *ABS08PG* filament_max_volumetric_speed = 12 first_layer_temperature = 270 temperature = 270 @@ -11170,17 +12422,23 @@ temperature = 250 [filament:Fillamentum Nylon FX256 @PG] inherits = Fillamentum Nylon FX256; *PAPG* filament_max_volumetric_speed = 6 -compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fillamentum Nylon FX256 @PG 0.6] inherits = Fillamentum Nylon FX256 @PG; *PA06PG* filament_max_volumetric_speed = 8 -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fillamentum Nylon FX256 @PG 0.8] inherits = Fillamentum Nylon FX256 @PG; *PA08PG* filament_max_volumetric_speed = 11 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material + +[filament:Fillamentum Nylon FX256 @XL] +inherits = Fillamentum Nylon FX256 @PG; *PAXL* + +[filament:Fillamentum Nylon FX256 @XL 0.6] +inherits = Fillamentum Nylon FX256 @PG 0.6; *PA06XL* + +[filament:Fillamentum Nylon FX256 @XL 0.8] +inherits = Fillamentum Nylon FX256 @PG 0.8; *PA08XL* [filament:Fiberthree F3 PA Pure Pro] inherits = *common* @@ -11217,21 +12475,27 @@ inherits = Fiberthree F3 PA Pure Pro; *PAPG* filament_max_volumetric_speed = 5 bed_temperature = 90 first_layer_bed_temperature = 90 -compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fiberthree F3 PA Pure Pro @PG 0.6] inherits = Fiberthree F3 PA Pure Pro @PG; *PA06PG* filament_max_volumetric_speed = 7 bed_temperature = 90 first_layer_bed_temperature = 90 -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fiberthree F3 PA Pure Pro @PG 0.8] inherits = Fiberthree F3 PA Pure Pro @PG; *PA08PG* filament_max_volumetric_speed = 10 bed_temperature = 90 first_layer_bed_temperature = 90 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material + +[filament:Fiberthree F3 PA Pure Pro @XL] +inherits = Fiberthree F3 PA Pure Pro @PG; *PAXL* + +[filament:Fiberthree F3 PA Pure Pro @XL 0.6] +inherits = Fiberthree F3 PA Pure Pro @PG 0.6; *PA06XL* + +[filament:Fiberthree F3 PA Pure Pro @XL 0.8] +inherits = Fiberthree F3 PA Pure Pro @PG 0.8; *PA08XL* [filament:Fiberthree F3 PA-CF Pro] inherits = *common* @@ -11268,21 +12532,27 @@ inherits = Fiberthree F3 PA-CF Pro; *PAPG* filament_max_volumetric_speed = 5 bed_temperature = 90 first_layer_bed_temperature = 90 -compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fiberthree F3 PA-CF Pro @PG 0.6] inherits = Fiberthree F3 PA-CF Pro; *PA06PG* filament_max_volumetric_speed = 7 bed_temperature = 90 first_layer_bed_temperature = 90 -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fiberthree F3 PA-CF Pro @PG 0.8] inherits = Fiberthree F3 PA-CF Pro; *PA08PG* filament_max_volumetric_speed = 10 bed_temperature = 90 first_layer_bed_temperature = 90 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material + +[filament:Fiberthree F3 PA-CF Pro @XL] +inherits = Fiberthree F3 PA-CF Pro @PG; *PAXL* + +[filament:Fiberthree F3 PA-CF Pro @XL 0.6] +inherits = Fiberthree F3 PA-CF Pro @PG 0.6; *PA06XL* + +[filament:Fiberthree F3 PA-CF Pro @XL 0.8] +inherits = Fiberthree F3 PA-CF Pro @PG 0.8; *PA08XL* [filament:Fiberthree F3 PA-GF Pro] inherits = Fiberthree F3 PA-CF Pro @@ -11298,21 +12568,27 @@ inherits = Fiberthree F3 PA-GF Pro; *PAPG* filament_max_volumetric_speed = 5 bed_temperature = 90 first_layer_bed_temperature = 90 -compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fiberthree F3 PA-GF Pro @PG 0.6] inherits = Fiberthree F3 PA-GF Pro @PG; *PA06PG* filament_max_volumetric_speed = 7 bed_temperature = 90 first_layer_bed_temperature = 90 -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fiberthree F3 PA-GF Pro @PG 0.8] inherits = Fiberthree F3 PA-GF Pro @PG; *PA08PG* filament_max_volumetric_speed = 10 bed_temperature = 90 first_layer_bed_temperature = 90 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material + +[filament:Fiberthree F3 PA-GF Pro @XL] +inherits = Fiberthree F3 PA-GF Pro @PG; *PAXL* + +[filament:Fiberthree F3 PA-GF Pro @XL 0.6] +inherits = Fiberthree F3 PA-GF Pro @PG 0.6; *PA06XL* + +[filament:Fiberthree F3 PA-GF Pro @XL 0.8] +inherits = Fiberthree F3 PA-GF Pro @PG 0.8; *PA08XL* [filament:Fiberthree F3 PA-GF30 Pro] inherits = Prusament PC Blend Carbon Fiber @@ -11336,21 +12612,27 @@ inherits = Fiberthree F3 PA-GF30 Pro; *PAPG* filament_max_volumetric_speed = 6 bed_temperature = 90 first_layer_bed_temperature = 90 -compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fiberthree F3 PA-GF30 Pro @PG 0.6] inherits = Fiberthree F3 PA-GF30 Pro @PG; *PA06PG* filament_max_volumetric_speed = 7.5 bed_temperature = 90 first_layer_bed_temperature = 90 -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Fiberthree F3 PA-GF30 Pro @PG 0.8] inherits = Fiberthree F3 PA-GF30 Pro @PG; *PA08PG* filament_max_volumetric_speed = 10 bed_temperature = 90 first_layer_bed_temperature = 90 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material + +[filament:Fiberthree F3 PA-GF30 Pro @XL] +inherits = Fiberthree F3 PA-GF30 Pro @PG; *PAXL* + +[filament:Fiberthree F3 PA-GF30 Pro @XL 0.6] +inherits = Fiberthree F3 PA-GF30 Pro @PG 0.6; *PA06XL* + +[filament:Fiberthree F3 PA-GF30 Pro @XL 0.8] +inherits = Fiberthree F3 PA-GF30 Pro @PG 0.8; *PA08XL* [filament:Taulman T-Glase] inherits = *PET* @@ -11368,15 +12650,21 @@ start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_MODEL_MINI.*/ and no [filament:Taulman T-Glase @PG] inherits = Taulman T-Glase; *PAPG* -compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Taulman T-Glase @PG 0.6] inherits = Taulman T-Glase @PG; *PA06PG* -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Taulman T-Glase @PG 0.8] inherits = Taulman T-Glase @PG; *PA08PG* -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material + +[filament:Taulman T-Glase @XL] +inherits = Taulman T-Glase @PG; *PAXL* + +[filament:Taulman T-Glase @XL 0.6] +inherits = Taulman T-Glase @PG 0.6; *PA06XL* + +[filament:Taulman T-Glase @XL 0.8] +inherits = Taulman T-Glase @PG 0.8; *PA08XL* [filament:Verbatim PLA] inherits = *PLA* @@ -11396,6 +12684,15 @@ inherits = Verbatim PLA; *PLA08PG* first_layer_temperature = 220 temperature = 220 +[filament:Verbatim PLA @XL] +inherits = Verbatim PLA @PG; *PLAXL* + +[filament:Verbatim PLA @XL 0.6] +inherits = Verbatim PLA @PG 0.6; *PLA06XL* + +[filament:Verbatim PLA @XL 0.8] +inherits = Verbatim PLA @PG 0.8; *PLA08XL* + [filament:Verbatim BVOH] inherits = *common* filament_vendor = Verbatim @@ -11426,18 +12723,24 @@ first_layer_temperature = 215 temperature = 210 idle_temperature = 70 filament_max_volumetric_speed = 4 -compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Verbatim BVOH @PG 0.6] inherits = Verbatim BVOH @PG; *ABS06PG* filament_max_volumetric_speed = 5 -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Verbatim BVOH @PG 0.8] inherits = Verbatim BVOH @PG; *ABS08PG* temperature = 215 filament_max_volumetric_speed = 8 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material + +[filament:Verbatim BVOH @XL] +inherits = Verbatim BVOH @PG; *ABSXL* + +[filament:Verbatim BVOH @XL 0.6] +inherits = Verbatim BVOH @PG 0.6; *ABS06XL* + +[filament:Verbatim BVOH @XL 0.8] +inherits = Verbatim BVOH @PG 0.8; *ABS08XL* [filament:Verbatim BVOH @MMU] inherits = Verbatim BVOH @@ -11525,17 +12828,23 @@ temperature = 220 [filament:Verbatim PP @PG] inherits = Verbatim PP; *ABSPG* filament_max_volumetric_speed = 5 -compatible_printers_condition = nozzle_diameter[0]!=0.6 and nozzle_diameter[0]!=0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Verbatim PP @PG 0.6] inherits = Verbatim PP @PG; *ABS06PG* filament_max_volumetric_speed = 7 -compatible_printers_condition = nozzle_diameter[0]==0.6 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material [filament:Verbatim PP @PG 0.8] inherits = Verbatim PP @PG; *ABS08PG* filament_max_volumetric_speed = 10 -compatible_printers_condition = nozzle_diameter[0]==0.8 and printer_notes=~/.*PG.*/ and ! single_extruder_multi_material + +[filament:Verbatim PP @XL] +inherits = Verbatim PP @PG; *ABSXL* + +[filament:Verbatim PP @XL 0.6] +inherits = Verbatim PP @PG 0.6; *ABS06XL* + +[filament:Verbatim PP @XL 0.8] +inherits = Verbatim PP @PG 0.8; *ABS08XL* [filament:FormFutura Centaur PP] inherits = *common* @@ -11577,6 +12886,15 @@ filament_max_volumetric_speed = 8 first_layer_temperature = 240 temperature = 240 +[filament:FormFutura Centaur PP @XL] +inherits = FormFutura Centaur PP @PG; *PETXL* + +[filament:FormFutura Centaur PP @XL 0.6] +inherits = FormFutura Centaur PP @PG 0.6; *PET06XL* + +[filament:FormFutura Centaur PP @XL 0.8] +inherits = FormFutura Centaur PP @PG 0.8; *PET08XL* + [filament:FormFutura Centaur PP @MINI] inherits = FormFutura Centaur PP filament_max_volumetric_speed = 3 @@ -17222,7 +18540,7 @@ printer_model = MINI printer_technology = FFF printer_variant = 0.4 printer_vendor = -thumbnails = 16x16,220x124 +thumbnails = 16x16,220x124,640x480 bed_shape = 0x0,180x0,180x180,0x180 default_filament_profile = "Prusament PLA" default_print_profile = 0.15mm QUALITY @MINI @@ -17344,8 +18662,7 @@ retract_length = 0.8 start_gcode = M17 ; enable steppers\nM862.3 P "[printer_model]" ; printer model check\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\n; set print area\nM555 X{first_layer_print_min[0]} Y{first_layer_print_min[1]} W{(first_layer_print_max[0]) - (first_layer_print_min[0])} H{(first_layer_print_max[1]) - (first_layer_print_min[1])}\n; inform about nozzle diameter\nM862.1 P[nozzle_diameter]\n; set & wait for bed and extruder temp for MBL\nM140 S[first_layer_bed_temperature] ; set bed temp\nM104 T0 S{((filament_type[0] == "PC" or filament_type[0] == "PA") ? (first_layer_temperature[0] - 25) : (filament_type[0] == "FLEX" ? 210 : 170))} ; set extruder temp for bed leveling\nM109 T0 R{((filament_type[0] == "PC" or filament_type[0] == "PA") ? (first_layer_temperature[0] - 25) : (filament_type[0] == "FLEX" ? 210 : 170))} ; wait for temp\n; home carriage, pick tool, home all\nG28 XY\nM84 E ; turn off E motor\nG28 Z\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nG29 G ; absorb heat\n; move to the nozzle cleanup area\nG1 X{(min(((((first_layer_print_min[0] + first_layer_print_max[0]) / 2) < ((print_bed_min[0] + print_bed_max[0]) / 2)) ? (((first_layer_print_min[1] - 7) < -2) ? 70 : (min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)) : (((first_layer_print_min[1] - 7) < -2) ? 260 : (min(print_bed_max[0], first_layer_print_min[0] + 32) - 32))), first_layer_print_min[0])) + 32} Y{(min((first_layer_print_min[1] - 7), first_layer_print_min[1]))} Z{5} F4800\nM302 S160 ; lower cold extrusion limit to 160C\nG1 E{-(filament_type[0] == "FLEX" ? 4 : 2)} F2400 ; retraction for nozzle cleanup\n; nozzle cleanup\nM84 E ; turn off E motor\nG29 P9 X{((((first_layer_print_min[0] + first_layer_print_max[0]) / 2) < ((print_bed_min[0] + print_bed_max[0]) / 2)) ? (((first_layer_print_min[1] - 7) < -2) ? 70 : (min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)) : (((first_layer_print_min[1] - 7) < -2) ? 260 : (min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)))} Y{(first_layer_print_min[1] - 7)} W{32} H{7}\nG0 Z10 F480 ; move away in Z\n{if first_layer_bed_temperature[0] > 60}\nG0 Z70 F480 ; move away (a bit more) in Z\nG0 X30 Y{print_bed_min[1]} F6000 ; move away in X/Y for higher bed temperatures\n{endif}\nM106 S100 ; cool off the nozzle\nM107 ; stop cooling off the nozzle - turn off the fan\n; MBL\nM84 E ; turn off E motor\nG29 P1 ; invalidate mbl & probe print area\nG29 P1 X30 Y0 W50 H20 C ; probe near purge place\nG29 P3.2 ; interpolate mbl probes\nG29 P3.13 ; extrapolate mbl outside probe area\nG29 A ; activate mbl\nM104 S[first_layer_temperature] ; set extruder temp\nG1 Z10 F720 ; move away in Z\nG0 X30 Y-8 F6000 ; move next to the sheet\n; wait for extruder temp\nM109 T0 S{first_layer_temperature[0]}\n;\n; purge\n;\nG92 E0 ; reset extruder position\nG0 X{(0 == 0 ? 30 : (0 == 1 ? 150 : (0 == 2 ? 210 : 330)))} Y{(0 < 4 ? -8 : -5.5)} ; move close to the sheet's edge\nG1 E{(filament_type[0] == "FLEX" ? 4 : 2)} F2400 ; deretraction after the initial one before nozzle cleaning\nG0 E10 X40 Z0.2 F500 ; purge\nG0 X70 E9 F800 ; purge\nG0 X{70 + 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{70 + 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG92 E0 ; reset extruder position\n default_print_profile = 0.20mm QUALITY @XL 0.4 default_filament_profile = "Prusament PLA @PG" -thumbnails = 16x16,313x173,440x240 -thumbnails_format = PNG +thumbnails = 16x16/QOI, 313x173/QOI, 440x240/QOI, 640x480/PNG gcode_flavor = marlin2 high_current_on_filament_swap = 0 retract_lift = 0.3 @@ -17644,8 +18961,7 @@ retract_length = 0.8 start_gcode = M17 ; enable steppers\nM862.3 P "[printer_model]" ; printer model check\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM555 X{(min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)} Y{(max(0, first_layer_print_min[1]) - 4)} W{((min(print_bed_max[0], max(first_layer_print_min[0] + 32, first_layer_print_max[0])))) - ((min(print_bed_max[0], first_layer_print_min[0] + 32) - 32))} H{((first_layer_print_max[1])) - ((max(0, first_layer_print_min[1]) - 4))}\n\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\n\nM140 S[first_layer_bed_temperature] ; set bed temp\n{if filament_type[initial_tool]=="PC" or filament_type[initial_tool]=="PA"}\nM104 S{first_layer_temperature[initial_tool]-25} ; set extruder temp for bed leveling\nM109 R{first_layer_temperature[initial_tool]-25} ; wait for temp\n{elsif filament_type[initial_tool]=="FLEX"}\nM104 S210 ; set extruder temp for bed leveling\nM109 R210 ; wait for temp\n{else}\nM104 S170 ; set extruder temp for bed leveling\nM109 R170 ; wait for temp\n{endif}\n\nM84 E ; turn off E motor\n\nG28 ; home all without mesh bed level\n\nG1 X{10 + 32} Y-4 Z5 F4800\n\nM302 S160 ; lower cold extrusion limit to 160C\n\n{if filament_type[initial_tool]=="FLEX"}\nG1 E-4 F2400 ; retraction\n{else}\nG1 E-2 F2400 ; retraction\n{endif}\n\nM84 E ; turn off E motor\n\nG29 P9 X10 Y-4 W32 H4\n\n{if first_layer_bed_temperature[initial_tool]<=60}M106 S100{endif}\n\nG0 Z40 F10000\n\nM190 S[first_layer_bed_temperature] ; wait for bed temp\n\nM107\n\n;\n; MBL\n;\nM84 E ; turn off E motor\nG29 P1 ; invalidate mbl & probe print area\nG29 P1 X0 Y0 W50 H20 C ; probe near purge place\nG29 P3.2 ; interpolate mbl probes\nG29 P3.13 ; extrapolate mbl outside probe area\nG29 A ; activate mbl\n\n; prepare for purge\nM104 S{first_layer_temperature[0]}\nG0 X0 Y-4 Z15 F4800 ; move away and ready for the purge\nM109 S{first_layer_temperature[0]}\n\nG92 E0\nM569 S0 E ; set spreadcycle mode for extruder\n\n;\n; Extrude purge line\n;\nG92 E0 ; reset extruder position\nG1 E{(filament_type[0] == "FLEX" ? 4 : 2)} F2400 ; deretraction after the initial one before nozzle cleaning\nG0 E7 X15 Z0.2 F500 ; purge\nG0 X25 E4 F500 ; purge\nG0 X35 E4 F650 ; purge\nG0 X45 E4 F800 ; purge\nG0 X{45 + 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{45 + 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\n\nG92 E0\nM221 S100 ; set flow to 100% default_print_profile = 0.20mm QUALITY @MK4 0.4 default_filament_profile = "Prusament PLA @PG" -thumbnails = 16x16,313x173,440x240 -thumbnails_format = PNG +thumbnails = 16x16/QOI, 313x173/QOI, 440x240/QOI, 640x480/PNG gcode_flavor = marlin2 high_current_on_filament_swap = 0 retract_lift = 0.2 @@ -17740,7 +19056,7 @@ retract_speed = 35 deretract_speed = 0 start_gcode = M17 ; enable steppers\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM862.3 P "MK4" ; printer model check\nM862.5 P2 ; g-code level check\nM862.6 P"Input shaper" ; FW feature check\nM115 U5.0.0-RC+11963\n\nM555 X{(min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)} Y{(max(0, first_layer_print_min[1]) - 4)} W{((min(print_bed_max[0], max(first_layer_print_min[0] + 32, first_layer_print_max[0])))) - ((min(print_bed_max[0], first_layer_print_min[0] + 32) - 32))} H{((first_layer_print_max[1])) - ((max(0, first_layer_print_min[1]) - 4))}\n\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\n\nM140 S[first_layer_bed_temperature] ; set bed temp\n{if filament_type[initial_tool]=="PC" or filament_type[initial_tool]=="PA"}\nM104 S{first_layer_temperature[initial_tool]-25} ; set extruder temp for bed leveling\nM109 R{first_layer_temperature[initial_tool]-25} ; wait for temp\n{elsif filament_type[initial_tool]=="FLEX"}\nM104 S210 ; set extruder temp for bed leveling\nM109 R210 ; wait for temp\n{else}\nM104 S170 ; set extruder temp for bed leveling\nM109 R170 ; wait for temp\n{endif}\n\nM84 E ; turn off E motor\n\nG28 ; home all without mesh bed level\n\nG1 X{10 + 32} Y-4 Z5 F4800\n\nM302 S160 ; lower cold extrusion limit to 160C\n\n{if filament_type[initial_tool]=="FLEX"}\nG1 E-4 F2400 ; retraction\n{else}\nG1 E-2 F2400 ; retraction\n{endif}\n\nM84 E ; turn off E motor\n\nG29 P9 X10 Y-4 W32 H4\n\n{if first_layer_bed_temperature[initial_tool]<=60}M106 S100{endif}\n\nG0 Z40 F10000\n\nM190 S[first_layer_bed_temperature] ; wait for bed temp\n\nM107\n\n;\n; MBL\n;\nM84 E ; turn off E motor\nG29 P1 ; invalidate mbl & probe print area\nG29 P1 X0 Y0 W50 H20 C ; probe near purge place\nG29 P3.2 ; interpolate mbl probes\nG29 P3.13 ; extrapolate mbl outside probe area\nG29 A ; activate mbl\n\n; prepare for purge\nM104 S{first_layer_temperature[0]}\nG0 X0 Y-4 Z15 F4800 ; move away and ready for the purge\nM109 S{first_layer_temperature[0]}\n\nG92 E0\nM569 S0 E ; set spreadcycle mode for extruder\n\n;\n; Extrude purge line\n;\nG92 E0 ; reset extruder position\nG1 E{(filament_type[0] == "FLEX" ? 4 : 2)} F2400 ; deretraction after the initial one before nozzle cleaning\nG0 E7 X15 Z0.2 F500 ; purge\nG0 X25 E4 F500 ; purge\nG0 X35 E4 F650 ; purge\nG0 X45 E4 F800 ; purge\nG0 X{45 + 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{45 + 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\n\nG92 E0\nM221 S100 ; set flow to 100% end_gcode = {if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+1, max_print_height)} F720 ; Move print head up{endif}\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nG1 X241 Y170 F3600 ; park\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+23, max_print_height)} F300 ; Move print head up{endif}\nG4 ; wait\nM572 S0 ; reset PA\nM593 X T2 F0 ; disable IS\nM593 Y T2 F0 ; disable IS\nM84 X Y E ; disable motors\n; max_layer_z = [max_layer_z] -before_layer_gcode = ;BEFORE_LAYER_CHANGE\nG92 E0.0\n;[layer_z]\nM201 X{interpolate_table(extruded_weight_total, (0,4000), (1400,2500), (10000,2500))} Y{interpolate_table(extruded_weight_total, (0,4000), (1400,2500), (10000,2500))}\nM74 W[extruded_weight_total] +before_layer_gcode = ;BEFORE_LAYER_CHANGE\nG92 E0.0\n;[layer_z]\nM201 X{interpolate_table(extruded_weight_total, (0,4000), (1400,2500), (10000,2500))} Y{interpolate_table(extruded_weight_total, (0,4000), (1400,2500), (10000,2500))}\n{if ! spiral_vase}M74 W[extruded_weight_total]{endif}\n default_print_profile = 0.20mm SPEED @MK4IS 0.4 default_filament_profile = "Prusament PLA @MK4IS" @@ -17808,6 +19124,101 @@ min_layer_height = 0.2 default_print_profile = 0.40mm QUALITY @MK4IS 0.8 default_filament_profile = "Prusament PLA @PG 0.8" +; [printer:Original Prusa MK3.9 0.4 nozzle] +; inherits = *commonMK4* +; printer_model = MK3.9 +; printer_variant = 0.4 +; max_layer_height = 0.30 +; machine_limits_usage = emit_to_gcode +; machine_max_acceleration_e = 2500,5000 +; machine_max_acceleration_extruding = 4000,2000 +; machine_max_acceleration_retracting = 1200,2000 +; machine_max_acceleration_travel = 4000,1250 +; machine_max_acceleration_x = 4000,2000 +; machine_max_acceleration_y = 4000,2000 +; machine_max_acceleration_z = 200,2000 +; machine_max_feedrate_e = 100,120 +; machine_max_feedrate_x = 300,100 +; machine_max_feedrate_y = 300,100 +; machine_max_feedrate_z = 40,12 +; machine_max_jerk_e = 10,1.5 +; machine_max_jerk_x = 8,8 +; machine_max_jerk_y = 8,8 +; machine_max_jerk_z = 2,0.4 +; machine_min_extruding_rate = 0,0 +; machine_min_travel_rate = 0,0 +; max_print_height = 220 +; printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_MODEL_MK4IS\nPRINTER_MODEL_MK3.9\nPG +; retract_length = 0.7 +; wipe = 0 +; retract_before_wipe = 80 +; retract_speed = 35 +; deretract_speed = 0 +; start_gcode = M17 ; enable steppers\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM862.3 P "[printer_model]" ; printer model check\nM862.5 P2 ; g-code level check\nM862.6 P"Input shaper" ; FW feature check\nM115 U5.0.0-RC+11963\n\nM555 X{(min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)} Y{(max(0, first_layer_print_min[1]) - 4)} W{((min(print_bed_max[0], max(first_layer_print_min[0] + 32, first_layer_print_max[0])))) - ((min(print_bed_max[0], first_layer_print_min[0] + 32) - 32))} H{((first_layer_print_max[1])) - ((max(0, first_layer_print_min[1]) - 4))}\n\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\n\nM140 S[first_layer_bed_temperature] ; set bed temp\n{if filament_type[initial_tool]=="PC" or filament_type[initial_tool]=="PA"}\nM104 S{first_layer_temperature[initial_tool]-25} ; set extruder temp for bed leveling\nM109 R{first_layer_temperature[initial_tool]-25} ; wait for temp\n{elsif filament_type[initial_tool]=="FLEX"}\nM104 S210 ; set extruder temp for bed leveling\nM109 R210 ; wait for temp\n{else}\nM104 S170 ; set extruder temp for bed leveling\nM109 R170 ; wait for temp\n{endif}\n\nM84 E ; turn off E motor\n\nG28 ; home all without mesh bed level\n\nG1 X{10 + 32} Y-4 Z5 F4800\n\nM302 S160 ; lower cold extrusion limit to 160C\n\n{if filament_type[initial_tool]=="FLEX"}\nG1 E-4 F2400 ; retraction\n{else}\nG1 E-2 F2400 ; retraction\n{endif}\n\nM84 E ; turn off E motor\n\nG29 P9 X10 Y-4 W32 H4\n\n{if first_layer_bed_temperature[initial_tool]<=60}M106 S100{endif}\n\nG0 Z40 F10000\n\nM190 S[first_layer_bed_temperature] ; wait for bed temp\n\nM107\n\n;\n; MBL\n;\nM84 E ; turn off E motor\nG29 P1 ; invalidate mbl & probe print area\nG29 P1 X0 Y0 W50 H20 C ; probe near purge place\nG29 P3.2 ; interpolate mbl probes\nG29 P3.13 ; extrapolate mbl outside probe area\nG29 A ; activate mbl\n\n; prepare for purge\nM104 S{first_layer_temperature[0]}\nG0 X0 Y-4 Z15 F4800 ; move away and ready for the purge\nM109 S{first_layer_temperature[0]}\n\nG92 E0\nM569 S0 E ; set spreadcycle mode for extruder\n\n;\n; Extrude purge line\n;\nG92 E0 ; reset extruder position\nG1 E{(filament_type[0] == "FLEX" ? 4 : 2)} F2400 ; deretraction after the initial one before nozzle cleaning\nG0 E7 X15 Z0.2 F500 ; purge\nG0 X25 E4 F500 ; purge\nG0 X35 E4 F650 ; purge\nG0 X45 E4 F800 ; purge\nG0 X{45 + 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{45 + 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\n\nG92 E0\nM221 S100 ; set flow to 100% +; end_gcode = {if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+1, max_print_height)} F720 ; Move print head up{endif}\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nG1 X241 Y170 F3600 ; park\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+23, max_print_height)} F300 ; Move print head up{endif}\nG4 ; wait\nM572 S0 ; reset PA\nM593 X T2 F0 ; disable IS\nM593 Y T2 F0 ; disable IS\nM84 X Y E ; disable motors\n; max_layer_z = [max_layer_z] +; before_layer_gcode = ;BEFORE_LAYER_CHANGE\nG92 E0.0\n;[layer_z]\nM201 X{interpolate_table(extruded_weight_total, (0,4000), (1400,2500), (10000,2500))} Y{interpolate_table(extruded_weight_total, (0,4000), (1400,2500), (10000,2500))}\n{if ! spiral_vase}M74 W[extruded_weight_total]{endif}\n +; default_print_profile = 0.20mm SPEED @MK4IS 0.4 +; default_filament_profile = "Prusament PLA @PGIS" + +; [printer:Original Prusa MK3.9 0.25 nozzle] +; inherits = Original Prusa MK3.9 0.4 nozzle +; printer_model = MK4IS +; printer_variant = 0.25 +; nozzle_diameter = 0.25 +; retract_length = 0.8 +; retract_lift = 0.15 +; max_layer_height = 0.15 +; min_layer_height = 0.05 +; default_print_profile = 0.12mm STRUCTURAL @MK4IS 0.25 + +; [printer:Original Prusa MK3.9 0.3 nozzle] +; inherits = Original Prusa MK3.9 0.4 nozzle +; printer_model = MK4IS +; printer_variant = 0.3 +; nozzle_diameter = 0.3 +; retract_length = 0.7 +; max_layer_height = 0.22 +; min_layer_height = 0.05 +; default_print_profile = 0.16mm STRUCTURAL @MK4IS 0.3 + +; [printer:Original Prusa MK3.9 0.5 nozzle] +; inherits = Original Prusa MK3.9 0.4 nozzle +; printer_variant = 0.5 +; nozzle_diameter = 0.5 +; retract_length = 0.7 +; max_layer_height = 0.32 +; min_layer_height = 0.07 +; deretract_speed = 25 +; wipe = 1 +; default_print_profile = 0.20mm SPEED @MK4IS 0.5 + +; [printer:Original Prusa MK3.9 0.6 nozzle] +; inherits = Original Prusa MK3.9 0.4 nozzle +; printer_variant = 0.6 +; nozzle_diameter = 0.6 +; retract_length = 0.7 +; retract_lift = 0.2 +; max_layer_height = 0.40 +; min_layer_height = 0.15 +; deretract_speed = 25 +; wipe = 1 +; default_print_profile = 0.25mm SPEED @MK4IS 0.6 + +; [printer:Original Prusa MK3.9 0.8 nozzle] +; inherits = Original Prusa MK3.9 0.4 nozzle +; printer_variant = 0.8 +; nozzle_diameter = 0.8 +; retract_length = 0.6 +; wipe = 1 +; retract_before_wipe = 50% +; retract_lift = 0.25 +; retract_speed = 25 +; deretract_speed = 15 +; max_layer_height = 0.6 +; min_layer_height = 0.2 +; default_print_profile = 0.40mm QUALITY @MK4IS 0.8 +; default_filament_profile = "Prusament PLA @PG 0.8" + [printer:Original Prusa SL1] printer_technology = SLA printer_model = SL1 From 7c120b2fa8acf96dbb962a04e3ef739273ce1f82 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Wed, 30 Aug 2023 15:26:53 +0200 Subject: [PATCH 091/136] Removed the obsolete "threads" parameter from the configuration layer. --- src/libslic3r/Preset.cpp | 2 +- src/libslic3r/PrintConfig.cpp | 12 ------------ 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 47aff8647b..7fe93e151f 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -456,7 +456,7 @@ static std::vector s_Preset_print_options { "ooze_prevention", "standby_temperature_delta", "interface_shells", "extrusion_width", "first_layer_extrusion_width", "perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width", "top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "infill_anchor", "infill_anchor_max", "bridge_flow_ratio", - "elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "gcode_resolution", "arc_fitting", "arc_fitting_tolerance", + "elefant_foot_compensation", "xy_size_compensation", "resolution", "gcode_resolution", "arc_fitting", "arc_fitting_tolerance", "wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_cone_angle", "wipe_tower_rotation_angle", "wipe_tower_brim_width", "wipe_tower_bridging", "single_extruder_multi_material_priming", "mmu_segmented_region_max_width", "mmu_segmented_region_interlocking_depth", "wipe_tower_extruder", "wipe_tower_no_sparse_layers", "wipe_tower_extra_spacing", "compatible_printers", "compatible_printers_condition", "inherits", diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 334eb9bb82..eb804ba334 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -3068,18 +3068,6 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionBool(true)); - def = this->add("threads", coInt); - def->label = L("Threads"); - def->tooltip = L("Threads are used to parallelize long-running tasks. Optimal threads number " - "is slightly above the number of available cores/processors."); - def->readonly = true; - def->min = 1; - { - int threads = (unsigned int)boost::thread::hardware_concurrency(); - def->set_default_value(new ConfigOptionInt(threads > 0 ? threads : 2)); - def->cli = ConfigOptionDef::nocli; - } - def = this->add("toolchange_gcode", coString); def->label = L("Tool change G-code"); def->tooltip = L("This custom code is inserted before every toolchange. Placeholder variables for all PrusaSlicer settings " From 90fd88bf12fe1b02ae6093d56f18fbaa1e1049c1 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Wed, 30 Aug 2023 15:50:01 +0200 Subject: [PATCH 092/136] Fixing compiler error --- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index d565e345c7..6f372123b1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -558,7 +558,7 @@ bool GLGizmoCut3D::render_double_input(const std::string& label, double& value_i bool GLGizmoCut3D::render_slider_double_input(const std::string& label, float& value_in, float& tolerance_in, float min_val/* = -0.1f*/, float max_tolerance/* = -0.1f*/) { - constexpr const float UndefMinVal = -0.1f; + static constexpr const float UndefMinVal = -0.1f; const float f_mm_to_in = static_cast(ObjectManipulation::mm_to_in); auto render_slider = [this, f_mm_to_in] From 138a41c8701f2d242e036342ee5a5fc0e7e36af1 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Wed, 30 Aug 2023 16:13:28 +0200 Subject: [PATCH 093/136] Fixed some more compiler warnings --- src/libslic3r/BoundingBox.hpp | 4 +++- src/libslic3r/PrintObject.cpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/BoundingBox.hpp b/src/libslic3r/BoundingBox.hpp index 9a4c68a147..f8da75ac6d 100644 --- a/src/libslic3r/BoundingBox.hpp +++ b/src/libslic3r/BoundingBox.hpp @@ -68,7 +68,9 @@ private: template> static void construct(BoundingBoxType &out, It from, It to) { - if (from != to) { + if (from == to) { + out.defined = false; + } else { auto it = from; out.min = it->template cast(); out.max = out.min; diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 5340599722..cdd5874810 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1611,7 +1611,7 @@ void PrintObject::discover_vertical_shells() to_polygons(m_layers[idx_layer + 1]->lslices) : Polygons{}; object_volume = intersection(shrinked_bottom_slice, shrinked_upper_slice); - internal_volume = closing(polygonsInternal, SCALED_EPSILON); + internal_volume = closing(polygonsInternal, float(SCALED_EPSILON)); } // The regularization operation may cause scattered tiny drops on the smooth parts of the model, filter them out diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 6f372123b1..52d0037819 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -3022,7 +3022,7 @@ void GLGizmoCut3D::toggle_model_objects_visibility() { bool has_active_volume = false; std::vector>* raycasters = m_parent.get_raycasters_for_picking(SceneRaycaster::EType::Volume); - for (const auto raycaster : *raycasters) + for (const std::shared_ptr &raycaster : *raycasters) if (raycaster->is_active()) { has_active_volume = true; break; From 5b1c97439fdfdc89245794582ace63c8267bd7e9 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 31 Aug 2023 09:01:56 +0200 Subject: [PATCH 094/136] Partially revert of 695c08e0e70b5c85e26cfa96e57b6185270b93d0 (Redundant white-spaces was mistakenly added) --- src/slic3r/GUI/Plater.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index cf4eae63f8..377522083c 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3579,9 +3579,9 @@ bool Plater::priv::replace_volume_with_stl(int object_idx, int volume_idx, const new_volume->convert_from_imperial_units(); else if (old_volume->source.is_converted_from_meters) new_volume->convert_from_meters(); - new_volume->supported_facets.assign(old_volume->supported_facets); - new_volume->seam_facets.assign(old_volume->seam_facets); - new_volume->mmu_segmentation_facets.assign(old_volume->mmu_segmentation_facets); + new_volume->supported_facets.assign(old_volume->supported_facets); + new_volume->seam_facets.assign(old_volume->seam_facets); + new_volume->mmu_segmentation_facets.assign(old_volume->mmu_segmentation_facets); std::swap(old_model_object->volumes[volume_idx], old_model_object->volumes.back()); old_model_object->delete_volume(old_model_object->volumes.size() - 1); if (!sinking) From 2970ee39d81810729d49e905acd294e0e517eae3 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 31 Aug 2023 17:17:22 +0200 Subject: [PATCH 095/136] Cherry-pick of 1f510bcc00f935de3657984c329e57b77dcb4c7a --- src/libslic3r/GCode/WipeTowerIntegration.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/GCode/WipeTowerIntegration.cpp b/src/libslic3r/GCode/WipeTowerIntegration.cpp index 77f497faad..d8afefdf95 100644 --- a/src/libslic3r/GCode/WipeTowerIntegration.cpp +++ b/src/libslic3r/GCode/WipeTowerIntegration.cpp @@ -47,11 +47,12 @@ std::string WipeTowerIntegration::append_tcr(GCodeGenerator &gcodegen, const Wip const bool needs_toolchange = gcodegen.writer().need_toolchange(new_extruder_id); const bool will_go_down = ! is_approx(z, current_z); - const bool is_ramming = (gcodegen.config().single_extruder_multi_material && ! tcr.priming) - || (! gcodegen.config().single_extruder_multi_material && gcodegen.config().filament_multitool_ramming.get_at(tcr.initial_tool)); - const bool should_travel_to_tower = tcr.force_travel // wipe tower says so - || ! needs_toolchange // this is just finishing the tower with no toolchange - || is_ramming; + const bool is_ramming = (gcodegen.config().single_extruder_multi_material) + || (! gcodegen.config().single_extruder_multi_material && gcodegen.config().filament_multitool_ramming.get_at(tcr.initial_tool)); + const bool should_travel_to_tower = ! tcr.priming + && (tcr.force_travel // wipe tower says so + || ! needs_toolchange // this is just finishing the tower with no toolchange + || is_ramming); if (should_travel_to_tower) { gcode += gcodegen.retract(); gcodegen.m_avoid_crossing_perimeters.use_external_mp_once(); From 7f57ba6b1288747b8086348896fc962aeb640a59 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 31 Aug 2023 17:21:04 +0200 Subject: [PATCH 096/136] Added ".bgcode", ".bgc" to known GCode file extensions. --- src/slic3r/GUI/GUI_App.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 1b29163f48..6e746f0378 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -480,7 +480,7 @@ static const FileWildcards file_wildcards_by_type[FT_SIZE] = { /* FT_STEP */ { "STEP files"sv, { ".stp"sv, ".step"sv } }, /* FT_AMF */ { "AMF files"sv, { ".amf"sv, ".zip.amf"sv, ".xml"sv } }, /* FT_3MF */ { "3MF files"sv, { ".3mf"sv } }, - /* FT_GCODE */ { "G-code files"sv, { ".gcode"sv, ".gco"sv, ".g"sv, ".ngc"sv } }, + /* FT_GCODE */ { "G-code files"sv, { ".gcode"sv, ".gco"sv, ".bgcode"sv, ".bgc"sv, ".g"sv, ".ngc"sv } }, /* FT_MODEL */ { "Known files"sv, { ".stl"sv, ".obj"sv, ".3mf"sv, ".amf"sv, ".zip.amf"sv, ".xml"sv, ".step"sv, ".stp"sv } }, /* FT_PROJECT */ { "Project files"sv, { ".3mf"sv, ".amf"sv, ".zip.amf"sv } }, /* FT_FONTS */ { "Font files"sv, { ".ttc"sv, ".ttf"sv } }, From b39c33414f377a20d60e9430043a3eb603e0b9c8 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Fri, 1 Sep 2023 09:42:05 +0200 Subject: [PATCH 097/136] Fixed issue in a 32bit clipper, where IntersectPoint() checked for the Y coordinate of the calculated intersection point for validity, but the Y coordinate was already rounded to 32bits, thus an overflow may have in rare cases masked invalidity of the result. --- src/clipper/clipper.cpp | 131 ++++++++++++++++++++-------------------- 1 file changed, 64 insertions(+), 67 deletions(-) diff --git a/src/clipper/clipper.cpp b/src/clipper/clipper.cpp index cd51ccc103..6cbf45b79c 100644 --- a/src/clipper/clipper.cpp +++ b/src/clipper/clipper.cpp @@ -84,15 +84,24 @@ inline IntPoint IntPoint2d(cInt x, cInt y) ); } -inline cInt Round(double val) +// Fast rounding upwards. +inline double FRound(double a) { - double v = val < 0 ? val - 0.5 : val + 0.5; + // Why does Java Math.round(0.49999999999999994) return 1? + // https://stackoverflow.com/questions/9902968/why-does-math-round0-49999999999999994-return-1 + return a == 0.49999999999999994 ? 0 : floor(a + 0.5); +} + +template +inline IType Round(double val) +{ + double v = FRound(val); #if defined(CLIPPERLIB_INT32) && ! defined(NDEBUG) static constexpr const double hi = 65536 * 16383; if (v > hi || -v > hi) throw clipperException("Coordinate outside allowed range"); #endif - return static_cast(v); + return static_cast(v); } // Overriding the Eigen operators because we don't want to compare Z coordinate if IntPoint is 3 dimensional. @@ -340,7 +349,7 @@ inline cInt TopX(TEdge &edge, const cInt currentY) { return (currentY == edge.Top.y()) ? edge.Top.x() : - edge.Bot.x() + Round(edge.Dx *(currentY - edge.Bot.y())); + edge.Bot.x() + Round(edge.Dx *(currentY - edge.Bot.y())); } //------------------------------------------------------------------------------ @@ -350,65 +359,53 @@ void IntersectPoint(TEdge &Edge1, TEdge &Edge2, IntPoint &ip) ip.z() = 0; #endif - double b1, b2; if (Edge1.Dx == Edge2.Dx) { ip.y() = Edge1.Curr.y(); ip.x() = TopX(Edge1, ip.y()); return; } - else if (Edge1.Delta.x() == 0) + + int64_t y; + if (Edge1.Delta.x() == 0) { ip.x() = Edge1.Bot.x(); - if (IsHorizontal(Edge2)) - ip.y() = Edge2.Bot.y(); - else - { - b2 = Edge2.Bot.y() - (Edge2.Bot.x() / Edge2.Dx); - ip.y() = Round(ip.x() / Edge2.Dx + b2); - } + y = IsHorizontal(Edge2) ? + Edge2.Bot.y() : + Round(ip.x() / Edge2.Dx + Edge2.Bot.y() - (Edge2.Bot.x() / Edge2.Dx)); + } else if (Edge2.Delta.x() == 0) { ip.x() = Edge2.Bot.x(); - if (IsHorizontal(Edge1)) - ip.y() = Edge1.Bot.y(); - else - { - b1 = Edge1.Bot.y() - (Edge1.Bot.x() / Edge1.Dx); - ip.y() = Round(ip.x() / Edge1.Dx + b1); - } - } - else + y = IsHorizontal(Edge1) ? + Edge1.Bot.y() : + Round(ip.x() / Edge1.Dx + Edge1.Bot.y() - (Edge1.Bot.x() / Edge1.Dx)); + } + else { - b1 = double(Edge1.Bot.x()) - double(Edge1.Bot.y()) * Edge1.Dx; - b2 = double(Edge2.Bot.x()) - double(Edge2.Bot.y()) * Edge2.Dx; + double b1 = double(Edge1.Bot.x()) - double(Edge1.Bot.y()) * Edge1.Dx; + double b2 = double(Edge2.Bot.x()) - double(Edge2.Bot.y()) * Edge2.Dx; double q = (b2-b1) / (Edge1.Dx - Edge2.Dx); - ip.y() = Round(q); - ip.x() = (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx)) ? - Round(Edge1.Dx * q + b1) : - Round(Edge2.Dx * q + b2); + y = Round(q); + ip.x() = (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx)) ? + Round(Edge1.Dx * q + b1) : + Round(Edge2.Dx * q + b2); } - if (ip.y() < Edge1.Top.y() || ip.y() < Edge2.Top.y()) + ip.y() = cInt(y); + if (y < Edge1.Top.y() || y < Edge2.Top.y()) { - if (Edge1.Top.y() > Edge2.Top.y()) - ip.y() = Edge1.Top.y(); - else - ip.y() = Edge2.Top.y(); - if (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx)) - ip.x() = TopX(Edge1, ip.y()); - else - ip.x() = TopX(Edge2, ip.y()); - } + ip.y() = (Edge1.Top.y() > Edge2.Top.y() ? Edge1 : Edge2).Top.y(); + y = ip.y(); + ip.x() = TopX(std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx) ? Edge1 : Edge2, ip.y()); + } //finally, don't allow 'ip' to be BELOW curr.y() (ie bottom of scanbeam) ... - if (ip.y() > Edge1.Curr.y()) + if (y > Edge1.Curr.y()) { ip.y() = Edge1.Curr.y(); //use the more vertical edge to derive X ... - if (std::fabs(Edge1.Dx) > std::fabs(Edge2.Dx)) - ip.x() = TopX(Edge2, ip.y()); else - ip.x() = TopX(Edge1, ip.y()); + ip.x() = TopX(std::fabs(Edge1.Dx) > std::fabs(Edge2.Dx) ? Edge2 : Edge1, ip.y()); } } //------------------------------------------------------------------------------ @@ -3539,8 +3536,8 @@ void ClipperOffset::DoOffset(double delta) for (cInt j = 1; j <= steps; j++) { m_destPoly.emplace_back(IntPoint2d( - Round(m_srcPoly[0].x() + X * delta), - Round(m_srcPoly[0].y() + Y * delta))); + Round(m_srcPoly[0].x() + X * delta), + Round(m_srcPoly[0].y() + Y * delta))); double X2 = X; X = X * m_cos - m_sin * Y; Y = X2 * m_sin + Y * m_cos; @@ -3552,8 +3549,8 @@ void ClipperOffset::DoOffset(double delta) for (int j = 0; j < 4; ++j) { m_destPoly.emplace_back(IntPoint2d( - Round(m_srcPoly[0].x() + X * delta), - Round(m_srcPoly[0].y() + Y * delta))); + Round(m_srcPoly[0].x() + X * delta), + Round(m_srcPoly[0].y() + Y * delta))); if (X < 0) X = 1; else if (Y < 0) Y = 1; else X = -1; @@ -3606,9 +3603,9 @@ void ClipperOffset::DoOffset(double delta) if (node.m_endtype == etOpenButt) { int j = len - 1; - pt1 = IntPoint2d(Round(m_srcPoly[j].x() + m_normals[j].x() * delta), Round(m_srcPoly[j].y() + m_normals[j].y() * delta)); + pt1 = IntPoint2d(Round(m_srcPoly[j].x() + m_normals[j].x() * delta), Round(m_srcPoly[j].y() + m_normals[j].y() * delta)); m_destPoly.emplace_back(pt1); - pt1 = IntPoint2d(Round(m_srcPoly[j].x() - m_normals[j].x() * delta), Round(m_srcPoly[j].y() - m_normals[j].y() * delta)); + pt1 = IntPoint2d(Round(m_srcPoly[j].x() - m_normals[j].x() * delta), Round(m_srcPoly[j].y() - m_normals[j].y() * delta)); m_destPoly.emplace_back(pt1); } else @@ -3633,9 +3630,9 @@ void ClipperOffset::DoOffset(double delta) if (node.m_endtype == etOpenButt) { - pt1 = IntPoint2d(Round(m_srcPoly[0].x() - m_normals[0].x() * delta), Round(m_srcPoly[0].y() - m_normals[0].y() * delta)); + pt1 = IntPoint2d(Round(m_srcPoly[0].x() - m_normals[0].x() * delta), Round(m_srcPoly[0].y() - m_normals[0].y() * delta)); m_destPoly.emplace_back(pt1); - pt1 = IntPoint2d(Round(m_srcPoly[0].x() + m_normals[0].x() * delta), Round(m_srcPoly[0].y() + m_normals[0].y() * delta)); + pt1 = IntPoint2d(Round(m_srcPoly[0].x() + m_normals[0].x() * delta), Round(m_srcPoly[0].y() + m_normals[0].y() * delta)); m_destPoly.emplace_back(pt1); } else @@ -3663,8 +3660,8 @@ void ClipperOffset::OffsetPoint(int j, int& k, JoinType jointype) double cosA = (m_normals[k].x() * m_normals[j].x() + m_normals[j].y() * m_normals[k].y() ); if (cosA > 0) // angle => 0 degrees { - m_destPoly.emplace_back(IntPoint2d(Round(m_srcPoly[j].x() + m_normals[k].x() * m_delta), - Round(m_srcPoly[j].y() + m_normals[k].y() * m_delta))); + m_destPoly.emplace_back(IntPoint2d(Round(m_srcPoly[j].x() + m_normals[k].x() * m_delta), + Round(m_srcPoly[j].y() + m_normals[k].y() * m_delta))); return; } //else angle => 180 degrees @@ -3674,11 +3671,11 @@ void ClipperOffset::OffsetPoint(int j, int& k, JoinType jointype) if (m_sinA * m_delta < 0) { - m_destPoly.emplace_back(IntPoint2d(Round(m_srcPoly[j].x() + m_normals[k].x() * m_delta), - Round(m_srcPoly[j].y() + m_normals[k].y() * m_delta))); + m_destPoly.emplace_back(IntPoint2d(Round(m_srcPoly[j].x() + m_normals[k].x() * m_delta), + Round(m_srcPoly[j].y() + m_normals[k].y() * m_delta))); m_destPoly.emplace_back(m_srcPoly[j]); - m_destPoly.emplace_back(IntPoint2d(Round(m_srcPoly[j].x() + m_normals[j].x() * m_delta), - Round(m_srcPoly[j].y() + m_normals[j].y() * m_delta))); + m_destPoly.emplace_back(IntPoint2d(Round(m_srcPoly[j].x() + m_normals[j].x() * m_delta), + Round(m_srcPoly[j].y() + m_normals[j].y() * m_delta))); } else switch (jointype) @@ -3702,19 +3699,19 @@ void ClipperOffset::DoSquare(int j, int k) double dx = std::tan(std::atan2(m_sinA, m_normals[k].x() * m_normals[j].x() + m_normals[k].y() * m_normals[j].y()) / 4); m_destPoly.emplace_back(IntPoint2d( - Round(m_srcPoly[j].x() + m_delta * (m_normals[k].x() - m_normals[k].y() * dx)), - Round(m_srcPoly[j].y() + m_delta * (m_normals[k].y() + m_normals[k].x() * dx)))); + Round(m_srcPoly[j].x() + m_delta * (m_normals[k].x() - m_normals[k].y() * dx)), + Round(m_srcPoly[j].y() + m_delta * (m_normals[k].y() + m_normals[k].x() * dx)))); m_destPoly.emplace_back(IntPoint2d( - Round(m_srcPoly[j].x() + m_delta * (m_normals[j].x() + m_normals[j].y() * dx)), - Round(m_srcPoly[j].y() + m_delta * (m_normals[j].y() - m_normals[j].x() * dx)))); + Round(m_srcPoly[j].x() + m_delta * (m_normals[j].x() + m_normals[j].y() * dx)), + Round(m_srcPoly[j].y() + m_delta * (m_normals[j].y() - m_normals[j].x() * dx)))); } //------------------------------------------------------------------------------ void ClipperOffset::DoMiter(int j, int k, double r) { double q = m_delta / r; - m_destPoly.emplace_back(IntPoint2d(Round(m_srcPoly[j].x() + (m_normals[k].x() + m_normals[j].x()) * q), - Round(m_srcPoly[j].y() + (m_normals[k].y() + m_normals[j].y()) * q))); + m_destPoly.emplace_back(IntPoint2d(Round(m_srcPoly[j].x() + (m_normals[k].x() + m_normals[j].x()) * q), + Round(m_srcPoly[j].y() + (m_normals[k].y() + m_normals[j].y()) * q))); } //------------------------------------------------------------------------------ @@ -3722,21 +3719,21 @@ void ClipperOffset::DoRound(int j, int k) { double a = std::atan2(m_sinA, m_normals[k].x() * m_normals[j].x() + m_normals[k].y() * m_normals[j].y()); - auto steps = std::max(Round(m_StepsPerRad * std::fabs(a)), 1); + auto steps = std::max(Round(m_StepsPerRad * std::fabs(a)), 1); double X = m_normals[k].x(), Y = m_normals[k].y(), X2; for (int i = 0; i < steps; ++i) { m_destPoly.emplace_back(IntPoint2d( - Round(m_srcPoly[j].x() + X * m_delta), - Round(m_srcPoly[j].y() + Y * m_delta))); + Round(m_srcPoly[j].x() + X * m_delta), + Round(m_srcPoly[j].y() + Y * m_delta))); X2 = X; X = X * m_cos - m_sin * Y; Y = X2 * m_sin + Y * m_cos; } m_destPoly.emplace_back(IntPoint2d( - Round(m_srcPoly[j].x() + m_normals[j].x() * m_delta), - Round(m_srcPoly[j].y() + m_normals[j].y() * m_delta))); + Round(m_srcPoly[j].x() + m_normals[j].x() * m_delta), + Round(m_srcPoly[j].y() + m_normals[j].y() * m_delta))); } //------------------------------------------------------------------------------ From 4580e69d86440122b5c6922eda2ad98dc0edda0e Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Fri, 1 Sep 2023 13:05:22 +0200 Subject: [PATCH 098/136] ArcWelder: Fixed NaN when exporting arches with nearly zero radii. --- src/libslic3r/Extruder.cpp | 4 ++- src/libslic3r/GCode.cpp | 38 +++++++++++++++++------------ src/libslic3r/GCode/Wipe.cpp | 12 +++++++-- src/libslic3r/PlaceholderParser.cpp | 1 + 4 files changed, 37 insertions(+), 18 deletions(-) diff --git a/src/libslic3r/Extruder.cpp b/src/libslic3r/Extruder.cpp index ea5a3b803b..af5af88cd8 100644 --- a/src/libslic3r/Extruder.cpp +++ b/src/libslic3r/Extruder.cpp @@ -16,6 +16,7 @@ Extruder::Extruder(unsigned int id, GCodeConfig *config) : std::pair Extruder::extrude(double dE) { + assert(! std::isnan(dE)); // in case of relative E distances we always reset to 0 before any output if (m_config->use_relative_e_distances) m_E = 0.; @@ -37,7 +38,8 @@ std::pair Extruder::extrude(double dE) value supplied will overwrite the previous one if any. */ std::pair Extruder::retract(double retract_length, double restart_extra) { - assert(restart_extra >= 0); + assert(! std::isnan(retract_length)); + assert(! std::isnan(restart_extra) && restart_extra >= 0); // in case of relative E distances we always reset to 0 before any output if (m_config->use_relative_e_distances) m_E = 0.; diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 231cba7c1b..ab625a7d90 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -3105,27 +3105,35 @@ std::string GCodeGenerator::_extrude( auto end = path.end(); const bool emit_radius = m_config.arc_fitting == ArcFittingType::EmitRadius; for (++ it; it != end; ++ it) { - Vec2d p = this->point_to_gcode_quantized(it->point); - if (it->radius == 0) { + Vec2d p = this->point_to_gcode_quantized(it->point); + // Center of the radius to be emitted into the G-code: Either by radius or by center offset. + double radius = 0; + Vec2d ij; + if (it->radius != 0) { + // Extrude an arc. + assert(m_config.arc_fitting == ArcFittingType::EmitCenter || + m_config.arc_fitting == ArcFittingType::EmitRadius); + radius = unscaled(it->radius); + if (emit_radius) { + // Only quantize radius if emitting it directly into G-code. Otherwise use the exact radius for calculating the IJ values. + radius = GCodeFormatter::quantize_xyzf(radius); + } else { + // Calculate quantized IJ circle center offset. + ij = GCodeFormatter::quantize( + Geometry::ArcWelder::arc_center(prev.cast(), p.cast(), double(radius), it->ccw()) + - prev); + if (ij == Vec2d::Zero()) + // Don't extrude a degenerated circle. + radius = 0; + } + } + if (radius == 0) { // Extrude line segment. if (const double line_length = (p - prev).norm(); line_length > 0) { path_length += line_length; gcode += m_writer.extrude_to_xy(p, e_per_mm * line_length, comment); } } else { - // Extrude an arc. - assert(m_config.arc_fitting == ArcFittingType::EmitCenter || - m_config.arc_fitting == ArcFittingType::EmitRadius); - double radius = unscaled(it->radius); - if (emit_radius) - // Only quantize radius if emitting it directly into G-code. Otherwise use the exact radius for calculating the IJ values. - radius = GCodeFormatter::quantize_xyzf(radius); - Vec2d ij; - if (! emit_radius) { - // Calculate quantized IJ circle center offset. - Vec2d center_raw = Geometry::ArcWelder::arc_center(prev.cast(), p.cast(), double(radius), it->ccw()) - prev; - ij = GCodeFormatter::quantize(center_raw); - } double angle = Geometry::ArcWelder::arc_angle(prev.cast(), p.cast(), double(radius)); assert(angle > 0); const double line_length = angle * std::abs(radius); diff --git a/src/libslic3r/GCode/Wipe.cpp b/src/libslic3r/GCode/Wipe.cpp index 0fa887810f..fa726424e9 100644 --- a/src/libslic3r/GCode/Wipe.cpp +++ b/src/libslic3r/GCode/Wipe.cpp @@ -117,7 +117,7 @@ std::string Wipe::wipe(GCodeGenerator &gcodegen, bool toolchange) return done; }; const bool emit_radius = gcodegen.config().arc_fitting == ArcFittingType::EmitRadius; - auto wipe_arc = [&gcode, &gcodegen, &retract_length, xy_to_e, emit_radius]( + auto wipe_arc = [&gcode, &gcodegen, &retract_length, xy_to_e, emit_radius, &wipe_linear]( const Vec2d &prev_quantized, Vec2d &p, double radius_in, const bool ccw) { Vec2d p_quantized = GCodeFormatter::quantize(p); if (p_quantized == prev_quantized) { @@ -126,6 +126,9 @@ std::string Wipe::wipe(GCodeGenerator &gcodegen, bool toolchange) } // Only quantize radius if emitting it directly into G-code. Otherwise use the exact radius for calculating the IJ values. double radius = emit_radius ? GCodeFormatter::quantize_xyzf(radius_in) : radius_in; + if (radius == 0) + // Degenerated arc after quantization. Process it as if it was a line segment. + return wipe_linear(prev_quantized, p); Vec2d center = Geometry::ArcWelder::arc_center(prev_quantized.cast(), p_quantized.cast(), double(radius), ccw); float angle = Geometry::ArcWelder::arc_angle(prev_quantized.cast(), p_quantized.cast(), double(radius)); assert(angle > 0); @@ -152,8 +155,13 @@ std::string Wipe::wipe(GCodeGenerator &gcodegen, bool toolchange) gcode += gcodegen.writer().extrude_to_xy_G2G3R(p, radius, ccw, -dE, wipe_retract_comment); } else { // Calculate quantized IJ circle center offset. + Vec2d ij = GCodeFormatter::quantize(Vec2d(center - prev_quantized)); + if (ij == Vec2d::Zero()) + // Degenerated arc after quantization. Process it as if it was a line segment. + return wipe_linear(prev_quantized, p); + // The arc is valid. gcode += gcodegen.writer().extrude_to_xy_G2G3IJ( - p, GCodeFormatter::quantize(Vec2d(center - prev_quantized)), ccw, -dE, wipe_retract_comment); + p, ij, ccw, -dE, wipe_retract_comment); } retract_length -= dE; return done; diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index a9ecdb98ab..6e81aa21ac 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -1643,6 +1643,7 @@ namespace client // Check whether the table X values are sorted. double x = expr_x.as_d(); + assert(! std::isnan(x)); bool evaluated = false; for (size_t i = 1; i < table.table.size(); ++i) { double x0 = table.table[i - 1].x; From d86e4169deaaffd7198e6f924fc4c856e47651e5 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Fri, 1 Sep 2023 13:18:46 +0200 Subject: [PATCH 099/136] Fix of previous commit. --- src/libslic3r/GCode.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index ab625a7d90..fb85a04e6c 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -3119,9 +3119,9 @@ std::string GCodeGenerator::_extrude( radius = GCodeFormatter::quantize_xyzf(radius); } else { // Calculate quantized IJ circle center offset. - ij = GCodeFormatter::quantize( - Geometry::ArcWelder::arc_center(prev.cast(), p.cast(), double(radius), it->ccw()) - - prev); + ij = GCodeFormatter::quantize(Vec2d( + Geometry::ArcWelder::arc_center(prev.cast(), p.cast(), double(radius), it->ccw()) + - prev)); if (ij == Vec2d::Zero()) // Don't extrude a degenerated circle. radius = 0; From 7acccd51d8ae269dbda900c4455b84167a15dd07 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 5 Sep 2023 08:14:12 +0200 Subject: [PATCH 100/136] Updated LibBGCode.cmake to use latest version of libbgcode --- deps/LibBGCode/LibBGCode.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deps/LibBGCode/LibBGCode.cmake b/deps/LibBGCode/LibBGCode.cmake index 076fe290a2..a203d2d72f 100644 --- a/deps/LibBGCode/LibBGCode.cmake +++ b/deps/LibBGCode/LibBGCode.cmake @@ -1,8 +1,8 @@ set(LibBGCode_SOURCE_DIR "" CACHE PATH "Optionally specify local LibBGCode source directory") set(_source_dir_line - URL https://github.com/prusa3d/libbgcode/archive/49e7b47b7a9a7e9365613d3f90ad49d71a0e93d6.zip - URL_HASH SHA256=b9513ab2bbaf06a79c35c4d69226d211f1226ca625ce07c04f54b89b93e9bc75 + URL https://github.com/prusa3d/libbgcode/archive/6b4e4f9fd1270f58f9202a674c7f8e7e00d9cfb0.zip + URL_HASH SHA256=1c613f8340657f87a3179711cb14de346c8ba3b160ac97b7b0d8bfa81d00b98a ) if (LibBGCode_SOURCE_DIR) From d6ca8134ed52967692e29bacfbe0c36d19a19b0c Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 5 Sep 2023 08:34:40 +0200 Subject: [PATCH 101/136] Removal of tech ENABLE_BINARIZED_GCODE_WIN_DEBUG --- src/libslic3r/GCode/GCodeProcessor.cpp | 31 -------------------------- src/libslic3r/Technologies.hpp | 3 --- src/slic3r/GUI/GCodeViewer.cpp | 5 ----- 3 files changed, 39 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 9d8b0345ef..25777ea200 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -31,10 +31,6 @@ #endif #include -#if ENABLE_BINARIZED_GCODE_WIN_DEBUG -#include -#include -#endif // ENABLE_BINARIZED_GCODE_WIN_DEBUG static const float DEFAULT_TOOLPATH_WIDTH = 0.4f; static const float DEFAULT_TOOLPATH_HEIGHT = 0.2f; @@ -1206,15 +1202,6 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct res = printer_metadata_block.read_data(*file.f, file_header, block_header); if (res != EResult::Success) throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); -#if ENABLE_BINARIZED_GCODE_WIN_DEBUG - OutputDebugStringA("Printer metadata:\n"); - for (const auto& [key, value] : printer_metadata_block.raw_data) { - OutputDebugStringA(key.c_str()); - OutputDebugStringA("->"); - OutputDebugStringA(value.c_str()); - OutputDebugStringA("\n"); - } -#endif // ENABLE_BINARIZED_GCODE_WIN_DEBUG // read thumbnail blocks res = read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); @@ -1262,15 +1249,6 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct res = print_metadata_block.read_data(*file.f, file_header, block_header); if (res != EResult::Success) throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); -#if ENABLE_BINARIZED_GCODE_WIN_DEBUG - OutputDebugStringA("Print metadata:\n"); - for (const auto& [key, value] : print_metadata_block.raw_data) { - OutputDebugStringA(key.c_str()); - OutputDebugStringA("->"); - OutputDebugStringA(value.c_str()); - OutputDebugStringA("\n"); - } -#endif // ENABLE_BINARIZED_GCODE_WIN_DEBUG // read slicer metadata block res = read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); @@ -1282,15 +1260,6 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct res = slicer_metadata_block.read_data(*file.f, file_header, block_header); if (res != EResult::Success) throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); -#if ENABLE_BINARIZED_GCODE_WIN_DEBUG - OutputDebugStringA("Slicer metadata:\n"); - for (const auto& [key, value] : slicer_metadata_block.raw_data) { - OutputDebugStringA(key.c_str()); - OutputDebugStringA("->"); - OutputDebugStringA(value.c_str()); - OutputDebugStringA("\n"); - } -#endif // ENABLE_BINARIZED_GCODE_WIN_DEBUG DynamicPrintConfig config; config.apply(FullPrintConfig::defaults()); std::string str; diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index a42a9d4b05..955c7988e1 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -66,9 +66,6 @@ // Enable export of binarized gcode #define ENABLE_BINARIZED_GCODE (1 && ENABLE_2_6_2_ALPHA1) #define ENABLE_BINARIZED_GCODE_DEBUG (0 && ENABLE_BINARIZED_GCODE) -#ifdef _WIN32 -#define ENABLE_BINARIZED_GCODE_WIN_DEBUG (1 && ENABLE_BINARIZED_GCODE_DEBUG) -#endif // _WIN32 #define ENABLE_BINARIZED_GCODE_DEBUG_WINDOW (1 && ENABLE_BINARIZED_GCODE) #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index b069545834..20514bd312 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -44,11 +44,6 @@ #include #include -#if ENABLE_BINARIZED_GCODE_WIN_DEBUG -#include -#include -#endif // ENABLE_BINARIZED_GCODE_WIN_DEBUG - namespace Slic3r { namespace GUI { From 317326bf9cbe80aa4783d0d30cb4cfe09dd6c24a Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 5 Sep 2023 08:46:19 +0200 Subject: [PATCH 102/136] Removal of tech ENABLE_BINARIZED_GCODE_DEBUG --- src/libslic3r/GCode/GCodeProcessor.cpp | 24 ------------------------ src/libslic3r/Technologies.hpp | 1 - 2 files changed, 25 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 25777ea200..8411538522 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -1213,30 +1213,6 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct res = thumbnail_block.read_data(*file.f, file_header, block_header); if (res != EResult::Success) throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); -#if ENABLE_BINARIZED_GCODE_DEBUG - if (thumbnail_block.data.size() > 0) { - auto format_filename = [](const std::string& stem, const ThumbnailBlock& block) { - std::string ret = stem + "_" + std::to_string(block.params.width) + "x" + std::to_string(block.params.height); - switch ((EThumbnailFormat)block.params.format) - { - case EThumbnailFormat::PNG: { ret += ".png"; break; } - case EThumbnailFormat::JPG: { ret += ".jpg"; break; } - case EThumbnailFormat::QOI: { ret += ".qoi"; break; } - } - return ret; - }; - - const boost::filesystem::path path(filename); - const std::string out_path = path.parent_path().string(); - const std::string out_filename = out_path + "\\" + format_filename(path.stem().string(), thumbnail_block); - FILE* outfile = boost::nowide::fopen(out_filename.c_str(), "wb"); - if (outfile != nullptr) { - fwrite((const void*)thumbnail_block.data.data(), 1, thumbnail_block.data.size(), outfile); - fclose(outfile); - } - } -#endif // ENABLE_BINARIZED_GCODE_DEBUG - res = read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); if (res != EResult::Success) throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 955c7988e1..8e52fbadf6 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -65,7 +65,6 @@ // Enable export of binarized gcode #define ENABLE_BINARIZED_GCODE (1 && ENABLE_2_6_2_ALPHA1) -#define ENABLE_BINARIZED_GCODE_DEBUG (0 && ENABLE_BINARIZED_GCODE) #define ENABLE_BINARIZED_GCODE_DEBUG_WINDOW (1 && ENABLE_BINARIZED_GCODE) #endif // _prusaslicer_technologies_h_ From 827fb8356e2f289818a566c94dfa00bc7ed43a07 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 5 Sep 2023 09:42:36 +0200 Subject: [PATCH 103/136] Config layer: Refactored handling of open enums --- src/libslic3r/Config.cpp | 9 +++------ src/libslic3r/Config.hpp | 3 +++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index a7c8cdfbc8..8ac538034e 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -335,12 +335,9 @@ void ConfigDef::finalize() if (def.type == coEnum) { assert(def.enum_def); assert(def.enum_def->is_valid_closed_enum()); - assert(def.gui_type != ConfigOptionDef::GUIType::i_enum_open && - def.gui_type != ConfigOptionDef::GUIType::f_enum_open && - def.gui_type != ConfigOptionDef::GUIType::select_open); + assert(! def.is_gui_type_enum_open()); def.enum_def->finalize_closed_enum(); - } else if (def.gui_type == ConfigOptionDef::GUIType::i_enum_open || def.gui_type == ConfigOptionDef::GUIType::f_enum_open || - def.gui_type == ConfigOptionDef::GUIType::select_open) { + } else if (def.is_gui_type_enum_open()) { assert(def.enum_def); assert(def.enum_def->is_valid_open_enum()); assert(def.gui_type != ConfigOptionDef::GUIType::i_enum_open || def.type == coInt || def.type == coInts); @@ -427,7 +424,7 @@ std::ostream& ConfigDef::print_cli_help(std::ostream& out, bool show_defaults, s descr += " ("; if (!def.sidetext.empty()) { descr += def.sidetext + ", "; - } else if (def.enum_def->has_values()) { + } else if (def.enum_def && def.enum_def->has_values()) { descr += boost::algorithm::join(def.enum_def->values(), ", ") + "; "; } descr += "default: " + def.default_value->serialize() + ")"; diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index b6320b126b..b41ed764c3 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -1836,6 +1836,8 @@ public: // Close parameter, string value could be one of the list values. select_close, }; + static bool is_gui_type_enum_open(const GUIType gui_type) + { return gui_type == ConfigOptionDef::GUIType::i_enum_open || gui_type == ConfigOptionDef::GUIType::f_enum_open || gui_type == ConfigOptionDef::GUIType::select_open; } // Identifier of this option. It is stored here so that it is accessible through the by_serialization_key_ordinal map. t_config_option_key opt_key; @@ -1923,6 +1925,7 @@ public: // Special values - "i_enum_open", "f_enum_open" to provide combo box for int or float selection, // "select_open" - to open a selection dialog (currently only a serial port selection). GUIType gui_type { GUIType::undefined }; + bool is_gui_type_enum_open() const { return is_gui_type_enum_open(this->gui_type); } // Usually empty. Otherwise "serialized" or "show_value" // The flags may be combined. // "serialized" - vector valued option is entered in a single edit field. Values are separated by a semicolon. From e233ced05c38e8177160e047cc64c87f93e0d218 Mon Sep 17 00:00:00 2001 From: rtyr <36745189+rtyr@users.noreply.github.com> Date: Tue, 5 Sep 2023 10:01:24 +0200 Subject: [PATCH 104/136] Updated ramming parameters. Updated start g-code for XL Multi-Tool --- resources/profiles/PrusaResearch.idx | 1 + resources/profiles/PrusaResearch.ini | 127 +++++++++++++++------------ 2 files changed, 72 insertions(+), 56 deletions(-) diff --git a/resources/profiles/PrusaResearch.idx b/resources/profiles/PrusaResearch.idx index a355d2b38c..38535b192f 100644 --- a/resources/profiles/PrusaResearch.idx +++ b/resources/profiles/PrusaResearch.idx @@ -1,4 +1,5 @@ min_slic3r_version = 2.6.2-alpha0 +1.11.0-alpha1 Updated ramming parameters. Updated start-gcode for XL Multi-Tool. Updated output filename format. 1.11.0-alpha0 Binary g-code, arc fitting, QOI/PNG thumbnails, 90degree XL tower, XL specific filament variants. min_slic3r_version = 2.6.0-beta2 1.9.8 FW version notification (MK2.5/3 family). Minor update of MK4IS profiles. Updated MK4IS thumbnail. diff --git a/resources/profiles/PrusaResearch.ini b/resources/profiles/PrusaResearch.ini index 08fbdc7e6b..e81382c039 100644 --- a/resources/profiles/PrusaResearch.ini +++ b/resources/profiles/PrusaResearch.ini @@ -5,7 +5,7 @@ name = Prusa Research # Configuration version of this file. Config file will only be installed, if the config_version differs. # This means, the server may force the PrusaSlicer configuration to be downgraded. -config_version = 1.11.0-alpha0 +config_version = 1.11.0-alpha1 # Where to get the updates from? config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaResearch/ changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1% @@ -347,7 +347,7 @@ wipe_tower_cone_angle = 25 wipe_tower_rotation_angle = 90 wipe_tower = 1 wipe_tower_bridging = 8 -wipe_tower_extra_spacing = 100 +wipe_tower_extra_spacing = 150 wipe_tower_brim_width = 3 ooze_prevention = 1 standby_temperature_delta = -110 @@ -376,7 +376,7 @@ first_layer_speed = 20 support_material_threshold = 45 raft_first_layer_density = 80% ## gcode_substitutions = "; stop printing object\\s(.*)\\s+id:(\\d+)\\s+.*";"$0\\nM486 S-1\\n";r;;"; printing object\\s(.*)\\s+id:(\\d+)\\s+.*";"$0\\nM486 S$2\\nM486 N$1\\n";r; -output_filename_format = {input_filename_base}_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.bgcode +output_filename_format = {input_filename_base}_0.4n_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.bgcode gcode_binary = 1 arc_fitting = emit_radius @@ -419,7 +419,7 @@ thick_bridges = 0 bridge_flow_ratio = 1 bridge_speed = 20 wipe_tower_bridging = 6 -output_filename_format = {input_filename_base}_0.25n_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.gcode +output_filename_format = {input_filename_base}_0.25n_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.gcode [print:*0.25nozzleMK3*] inherits = *0.25nozzle* @@ -450,6 +450,10 @@ infill_acceleration = 800 first_layer_acceleration = 500 [print:*0.25nozzleXL*] +inherits = *0.25nozzleMK4* +output_filename_format = {input_filename_base}_0.25n_{layer_height}mm_{printing_filament_types}_XL_{print_time}.bgcode + +[print:*0.25nozzleMK4*] inherits = *0.25nozzleMK3* infill_speed = 40 solid_infill_speed = 40 @@ -458,11 +462,7 @@ first_layer_acceleration = 500 infill_anchor = 1 perimeters = 3 brim_separation = 0 -output_filename_format = {input_filename_base}_0.25n_{layer_height}mm_{filament_type[0]}_XL_{print_time}.bgcode - -[print:*0.25nozzleMK4*] -inherits = *0.25nozzleXL* -output_filename_format = {input_filename_base}_0.25n_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.bgcode +output_filename_format = {input_filename_base}_0.25n_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.bgcode [print:*0.3nozzle*] external_perimeter_extrusion_width = 0.33 @@ -476,7 +476,14 @@ support_material_extrusion_width = 0.3 fill_density = 20% perimeters = 3 infill_anchor = 1.5 -output_filename_format = {input_filename_base}_0.3n_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.bgcode +output_filename_format = {input_filename_base}_0.3n_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.bgcode + +[print:*0.3nozzleXL*] +inherits = *0.3nozzle* +output_filename_format = {input_filename_base}_0.3n_{layer_height}mm_{printing_filament_types}_XL_{print_time}.bgcode + +[print:*0.4nozzleXL*] +output_filename_format = {input_filename_base}_0.4n_{layer_height}mm_{printing_filament_types}_XL_{print_time}.bgcode [print:*0.5nozzle*] external_perimeter_extrusion_width = 0.55 @@ -496,7 +503,11 @@ infill_anchor = 2 infill_anchor_max = 15 thick_bridges = 0 bridge_speed = 30 -output_filename_format = {input_filename_base}_0.5n_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.bgcode +output_filename_format = {input_filename_base}_0.5n_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.bgcode + +[print:*0.5nozzleXL*] +inherits = *0.5nozzle* +output_filename_format = {input_filename_base}_0.5n_{layer_height}mm_{printing_filament_types}_XL_{print_time}.bgcode [print:*0.6nozzle*] external_perimeter_extrusion_width = 0.61 @@ -518,7 +529,7 @@ bridge_flow_ratio = 0.95 bridge_speed = 25 infill_overlap = 15% support_tree_branch_diameter_double_wall = 0 -output_filename_format = {input_filename_base}_0.6n_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.gcode +output_filename_format = {input_filename_base}_0.6n_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.gcode [print:*0.6nozzleMK3*] inherits = *0.6nozzle* @@ -528,6 +539,10 @@ infill_extrusion_width = 0.65 thick_bridges = 0 [print:*0.6nozzleXL*] +inherits = *0.6nozzleMK4* +output_filename_format = {input_filename_base}_0.6n_{layer_height}mm_{printing_filament_types}_XL_{print_time}.bgcode + +[print:*0.6nozzleMK4*] inherits = *0.6nozzle* external_perimeter_extrusion_width = 0.65 extrusion_width = 0.65 @@ -536,11 +551,7 @@ thick_bridges = 0 fill_density = 20% support_material_interface_spacing = 0.25 infill_anchor = 2.5 -output_filename_format = {input_filename_base}_0.6n_{layer_height}mm_{filament_type[0]}_XL_{print_time}.bgcode - -[print:*0.6nozzleMK4*] -inherits = *0.6nozzleXL* -output_filename_format = {input_filename_base}_0.6n_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.bgcode +output_filename_format = {input_filename_base}_0.6n_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.bgcode [print:*0.6nozzleMINI*] inherits = *0.6nozzleMK3* @@ -597,7 +608,7 @@ single_extruder_multi_material_priming = 0 thick_bridges = 1 overhangs = 0 support_tree_branch_diameter_double_wall = 0 -output_filename_format = {input_filename_base}_0.8n_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.gcode +output_filename_format = {input_filename_base}_0.8n_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.gcode [print:*0.8nozzleXL*] inherits = *0.8nozzle* @@ -612,14 +623,14 @@ raft_first_layer_expansion = 2 default_acceleration = 1250 infill_anchor = 2.5 first_layer_acceleration = 500 -output_filename_format = {input_filename_base}_0.8n_{layer_height}mm_{filament_type[0]}_XL_{print_time}.bgcode +output_filename_format = {input_filename_base}_0.8n_{layer_height}mm_{printing_filament_types}_XL_{print_time}.bgcode [print:*0.8nozzleMK4*] inherits = *0.8nozzleXL* default_acceleration = 1000 infill_acceleration = 2000 first_layer_acceleration = 600 -output_filename_format = {input_filename_base}_0.8n_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.bgcode +output_filename_format = {input_filename_base}_0.8n_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.bgcode [print:*soluble_support*] overhangs = 1 @@ -1976,7 +1987,7 @@ compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]== ## XL - 0.3mm nozzle [print:0.05mm ULTRADETAIL @XL 0.3] -inherits = *0.05mm*; *XL*; *0.3nozzle* +inherits = *0.05mm*; *XL*; *0.3nozzleXL* top_solid_layers = 14 bottom_solid_layers = 9 support_material_contact_distance = 0.07 @@ -2002,7 +2013,7 @@ default_acceleration = 800 compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.3 [print:0.08mm DETAIL @XL 0.3] -inherits = *0.07mm*; *XL*; *0.3nozzle* +inherits = *0.07mm*; *XL*; *0.3nozzleXL* layer_height = 0.08 support_material_contact_distance = 0.08 raft_contact_distance = 0.08 @@ -2030,7 +2041,7 @@ gcode_resolution = 0.006 compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.3 [print:0.12mm QUALITY @XL 0.3] -inherits = *0.12mm*; *XL*; *0.3nozzle* +inherits = *0.12mm*; *XL*; *0.3nozzleXL* support_material_contact_distance = 0.1 raft_contact_distance = 0.1 perimeter_speed = 35 @@ -2056,7 +2067,7 @@ gcode_resolution = 0.008 compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.3 [print:0.16mm SPEED @XL 0.3] -inherits = *0.16mm*; *XL*; *0.3nozzle* +inherits = *0.16mm*; *XL*; *0.3nozzleXL* support_material_contact_distance = 0.15 raft_contact_distance = 0.15 perimeter_speed = 50 @@ -2082,7 +2093,7 @@ gcode_resolution = 0.008 compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.3 [print:0.20mm DRAFT @XL 0.3] -inherits = *0.20mm*; *XL*; *0.3nozzle* +inherits = *0.20mm*; *XL*; *0.3nozzleXL* support_material_contact_distance = 0.18 raft_contact_distance = 0.18 perimeter_speed = 50 @@ -2115,7 +2126,7 @@ compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]== ## XL - 0.4mm nozzle [print:0.07mm ULTRADETAIL @XL 0.4] -inherits = *0.07mm*; *XL* +inherits = *0.07mm*; *XL*; *0.4nozzleXL* thick_bridges = 1 bridge_flow_ratio = 0.6 top_infill_extrusion_width = 0.4 @@ -2150,7 +2161,7 @@ default_acceleration = 800 compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.4 [print:0.10mm DETAIL @XL 0.4] -inherits = *0.10mm*; *XL* +inherits = *0.10mm*; *XL*; *0.4nozzleXL* support_material_contact_distance = 0.17 raft_contact_distance = 0.15 perimeter_speed = 45 @@ -2186,7 +2197,7 @@ gcode_resolution = 0.006 compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.4 [print:0.15mm QUALITY @XL 0.4] -inherits = *0.15mm*; *XL* +inherits = *0.15mm*; *XL*; *0.4nozzleXL* perimeter_speed = 65 external_perimeter_speed = 40 small_perimeter_speed = 35 @@ -2218,7 +2229,7 @@ gcode_resolution = 0.008 compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.4 [print:0.20mm QUALITY @XL 0.4] -inherits = *0.20mm*; *XL* +inherits = *0.20mm*; *XL*; *0.4nozzleXL* perimeter_speed = 65 external_perimeter_speed = 40 small_perimeter_speed = 35 @@ -2250,7 +2261,7 @@ top_infill_extrusion_width = 0.4 compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.4 [print:0.20mm SOLUBLE INTERFACE @XL 0.4] -inherits = 0.20mm QUALITY @XL 0.4; *soluble_support_XL* +inherits = 0.20mm QUALITY @XL 0.4; *soluble_support_XL*; *0.4nozzleXL* support_material_extruder = 0 perimeter_speed = 50 external_perimeter_speed = 35 @@ -2267,7 +2278,7 @@ support_material_extruder = 2 support_material_with_sheath = 1 [print:0.20mm SPEED @XL 0.4] -inherits = *0.20mm*; *XL* +inherits = *0.20mm*; *XL*; *0.4nozzleXL* perimeter_speed = 90 external_perimeter_speed = 70 small_perimeter_speed = 40 @@ -2298,7 +2309,7 @@ top_infill_extrusion_width = 0.42 compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.4 [print:0.30mm DRAFT @XL 0.4] -inherits = *0.30mm*; *XL* +inherits = *0.30mm*; *XL*; *0.4nozzleXL* bottom_solid_layers = 3 perimeter_speed = 80 external_perimeter_speed = 70 @@ -2337,7 +2348,7 @@ compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]== ## XL - 0.5mm nozzle [print:0.10mm DETAIL @XL 0.5] -inherits = *0.10mm*; *XL*; *0.5nozzle* +inherits = *0.10mm*; *XL*; *0.5nozzleXL* perimeter_speed = 40 external_perimeter_speed = 30 small_perimeter_speed = 25 @@ -2368,7 +2379,7 @@ gcode_resolution = 0.008 compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.5 [print:0.15mm QUALITY @XL 0.5] -inherits = *0.15mm*; *XL*; *0.5nozzle* +inherits = *0.15mm*; *XL*; *0.5nozzleXL* perimeter_speed = 65 external_perimeter_speed = 40 small_perimeter_speed = 35 @@ -2392,7 +2403,7 @@ gcode_resolution = 0.008 compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.5 [print:0.20mm QUALITY @XL 0.5] -inherits = 0.15mm QUALITY @XL 0.5; *0.20mm*; *XL*; *0.5nozzle* +inherits = 0.15mm QUALITY @XL 0.5; *0.20mm*; *XL*; *0.5nozzleXL* gcode_resolution = 0.01 support_material_interface_layers = 4 infill_speed = 200 @@ -2423,7 +2434,7 @@ support_material_with_sheath = 1 support_material_extrusion_width = 0.47 [print:0.25mm SPEED @XL 0.5] -inherits = *0.25mm*; *XL*; *0.5nozzle* +inherits = *0.25mm*; *XL*; *0.5nozzleXL* bottom_solid_layers = 3 perimeter_speed = 70 external_perimeter_speed = 70 @@ -2449,7 +2460,7 @@ max_print_speed = 200 compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]==0.5 [print:0.32mm DRAFT @XL 0.5] -inherits = *0.32mm*; *XL*; *0.5nozzle* +inherits = *0.32mm*; *XL*; *0.5nozzleXL* bottom_solid_layers = 3 perimeter_speed = 70 external_perimeter_speed = 70 @@ -3757,7 +3768,7 @@ min_skirt_length = 4 mmu_segmented_region_max_width = 0 only_retract_when_crossing_perimeters = 0 ooze_prevention = 0 -output_filename_format = {input_filename_base}_0.4n_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.bgcode +output_filename_format = {input_filename_base}_0.4n_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.bgcode overhang_speed_0 = 15 overhang_speed_1 = 25 overhang_speed_2 = 30 @@ -3869,7 +3880,7 @@ raft_first_layer_density = 95% gap_fill_speed = 50 single_extruder_multi_material_priming = 0 wipe_tower = 1 -output_filename_format = {input_filename_base}_0.25n_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.bgcode +output_filename_format = {input_filename_base}_0.25n_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.bgcode compatible_printers_condition = printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.25 [print:*MK4IS_common03*] @@ -3905,7 +3916,7 @@ raft_first_layer_density = 90% gap_fill_speed = 50 top_solid_min_thickness = 0.7 bottom_solid_min_thickness = 0.5 -output_filename_format = {input_filename_base}_0.3n_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.bgcode +output_filename_format = {input_filename_base}_0.3n_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.bgcode compatible_printers_condition = printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.3 [print:*MK4IS_common05*] @@ -3933,7 +3944,7 @@ infill_acceleration = 4000 default_acceleration = 2500 infill_anchor = 2 infill_anchor_max = 15 -output_filename_format = {input_filename_base}_0.5n_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.bgcode +output_filename_format = {input_filename_base}_0.5n_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.bgcode compatible_printers_condition = printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.5 [print:*MK4IS_common06*] @@ -3955,7 +3966,7 @@ support_material_interface_spacing = 0.25 support_material_interface_speed = 75% raft_contact_distance = 0.25 gap_fill_speed = 70 -output_filename_format = {input_filename_base}_0.6n_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.bgcode +output_filename_format = {input_filename_base}_0.6n_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.bgcode compatible_printers_condition = printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.6 overhang_speed_0 = 15 overhang_speed_1 = 20 @@ -3989,7 +4000,7 @@ raft_contact_distance = 0.2 gap_fill_speed = 40 top_solid_min_thickness = 1.2 bottom_solid_min_thickness = 0.8 -output_filename_format = {input_filename_base}_0.8n_{layer_height}mm_{filament_type[0]}_{printer_model}_{print_time}.bgcode +output_filename_format = {input_filename_base}_0.8n_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.bgcode compatible_printers_condition = printer_notes=~/.*MK4IS.*/ and nozzle_diameter[0]==0.8 seam_position = nearest infill_anchor = 2.5 @@ -4711,6 +4722,13 @@ compatible_printers_condition = printer_notes=~/.*XL.*/ and nozzle_diameter[0]== inherits = *PLAXL* compatible_printers_condition = printer_notes=~/.*XL.*/ and nozzle_diameter[0]==0.8 +[filament:*PVAXL*] +filament_multitool_ramming = 1 +filament_multitool_ramming_volume = 10 +filament_multitool_ramming_flow = 40 +filament_minimal_purge_on_wipe_tower = 35 +filament_retract_length_toolchange = 20 + [filament:*PET*] inherits = *common* bed_temperature = 90 @@ -5077,10 +5095,9 @@ compatible_printers_condition = printer_model=~/.*XL.*/ and nozzle_diameter[0]>= idle_temperature = 70 start_filament_gcode = "M900 K0 ; Filament gcode\n\nM142 S36 ; set heatbreak target temp" filament_multitool_ramming = 0 -filament_multitool_ramming_volume = 5 +filament_multitool_ramming_volume = 40 filament_multitool_ramming_flow = 3.5 filament_minimal_purge_on_wipe_tower = 35 -filament_retract_length_toolchange = 5 [filament:*FLEX06XL*] inherits = *FLEXXL* @@ -9858,13 +9875,13 @@ temperature = 210 filament_max_volumetric_speed = 8 [filament:PrimaSelect PVA+ @XL] -inherits = PrimaSelect PVA+ @PG; *PLAXL* +inherits = PrimaSelect PVA+ @PG; *PLAXL*; *PVAXL* [filament:PrimaSelect PVA+ @XL 0.6] -inherits = PrimaSelect PVA+ @PG 0.6; *PLA06XL* +inherits = PrimaSelect PVA+ @PG 0.6; *PLA06XL*; *PVAXL* [filament:PrimaSelect PVA+ @XL 0.8] -inherits = PrimaSelect PVA+ @PG 0.8; *PLA08XL* +inherits = PrimaSelect PVA+ @PG 0.8; *PLA08XL*; *PVAXL* [filament:Prusa ABS] inherits = *ABSC* @@ -12734,13 +12751,13 @@ temperature = 215 filament_max_volumetric_speed = 8 [filament:Verbatim BVOH @XL] -inherits = Verbatim BVOH @PG; *ABSXL* +inherits = Verbatim BVOH @PG; *ABSXL*; *PVAXL* [filament:Verbatim BVOH @XL 0.6] -inherits = Verbatim BVOH @PG 0.6; *ABS06XL* +inherits = Verbatim BVOH @PG 0.6; *ABS06XL*; *PVAXL* [filament:Verbatim BVOH @XL 0.8] -inherits = Verbatim BVOH @PG 0.8; *ABS08XL* +inherits = Verbatim BVOH @PG 0.8; *ABS08XL*; *PVAXL* [filament:Verbatim BVOH @MMU] inherits = Verbatim BVOH @@ -18686,7 +18703,7 @@ retract_restart_extra = 0,0,0,0,0 retract_restart_extra_toolchange = 0,0,0,0,0 wipe = 1,1,1,1,1 extruder_colour = #FF8000;#DB5182;#3EC0FF;#FF4F4F;#FBEB7D -start_gcode = M17 ; enable steppers\nM862.3 P "XL" ; printer model check\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\n; set print area\nM555 X{first_layer_print_min[0]} Y{first_layer_print_min[1]} W{(first_layer_print_max[0]) - (first_layer_print_min[0])} H{(first_layer_print_max[1]) - (first_layer_print_min[1])}\n; inform about nozzle diameter\n{if (is_extruder_used[0])}M862.1 T0 P{nozzle_diameter[0]}{endif}\n{if (is_extruder_used[1])}M862.1 T1 P{nozzle_diameter[1]}{endif}\n{if (is_extruder_used[2])}M862.1 T2 P{nozzle_diameter[2]}{endif}\n{if (is_extruder_used[3])}M862.1 T3 P{nozzle_diameter[3]}{endif}\n{if (is_extruder_used[4])}M862.1 T4 P{nozzle_diameter[4]}{endif}\n\n; turn off unused heaters\n{if ! is_extruder_used[0]} M104 T0 S0 {endif}\n{if ! is_extruder_used[1]} M104 T1 S0 {endif}\n{if ! is_extruder_used[2]} M104 T2 S0 {endif}\n{if ! is_extruder_used[3]} M104 T3 S0 {endif}\n{if ! is_extruder_used[4]} M104 T4 S0 {endif}\n\nM217 Z{max(zhop, 2.0)} ; set toolchange z hop to 2mm, or zhop variable from slicer if higher\n; set bed and extruder temp for MBL\nM140 S[first_layer_bed_temperature] ; set bed temp\nM109 T{initial_tool} S{((filament_type[initial_tool] == "PC" or filament_type[initial_tool] == "PA") ? (first_layer_temperature[initial_tool] - 25) : (filament_type[initial_tool] == "FLEX" ? 210 : 170))}\n; Home XY\nG28 XY\n; try picking tools used in print\n{if (is_extruder_used[0]) and (initial_tool != 0)}T0 S1 L0{endif}\n{if (is_extruder_used[1]) and (initial_tool != 1)}T1 S1 L0{endif}\n{if (is_extruder_used[2]) and (initial_tool != 2)}T2 S1 L0{endif}\n{if (is_extruder_used[3]) and (initial_tool != 3)}T3 S1 L0{endif}\n{if (is_extruder_used[4]) and (initial_tool != 4)}T4 S1 L0{endif}\n; select tool that will be used to home & MBL\nT{initial_tool} S1 L0\n; home Z with MBL tool\nM84 E ; turn off E motor\nG28 Z\nG0 Z10 ; add Z clearance\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nG29 G ; absorb heat\n; move to the nozzle cleanup area\nG1 X{(min(((((first_layer_print_min[0] + first_layer_print_max[0]) / 2) < ((print_bed_min[0] + print_bed_max[0]) / 2)) ? (((first_layer_print_min[1] - 7) < -2) ? 70 : (min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)) : (((first_layer_print_min[1] - 7) < -2) ? 260 : (min(print_bed_max[0], first_layer_print_min[0] + 32) - 32))), first_layer_print_min[0])) + 32} Y{(min((first_layer_print_min[1] - 7), first_layer_print_min[1]))} Z{5} F{(travel_speed * 60)}\nM302 S160 ; lower cold extrusion limit to 160C\nG1 E{-(filament_type[0] == "FLEX" ? 4 : 2)} F2400 ; retraction for nozzle cleanup\n; nozzle cleanup\nM84 E ; turn off E motor\nG29 P9 X{((((first_layer_print_min[0] + first_layer_print_max[0]) / 2) < ((print_bed_min[0] + print_bed_max[0]) / 2)) ? (((first_layer_print_min[1] - 7) < -2) ? 70 : (min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)) : (((first_layer_print_min[1] - 7) < -2) ? 260 : (min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)))} Y{(first_layer_print_min[1] - 7)} W{32} H{7}\nG0 Z10 F480 ; move away in Z\nM107 ; turn off the fan\n; MBL\nM84 E ; turn off E motor\nG29 P1 ; invalidate mbl & probe print area\nG29 P1 X30 Y0 W{(((is_extruder_used[4]) or ((is_extruder_used[3]) or (is_extruder_used[2]))) ? "300" : ((is_extruder_used[1]) ? "130" : "50"))} H20 C ; probe near purge place\nG29 P3.2 ; interpolate mbl probes\nG29 P3.13 ; extrapolate mbl outside probe area\nG29 A ; activate mbl\nG1 Z10 F720 ; move away in Z\nP0 S1 L1 D0; park the tool\n; set extruder temp\n{if first_layer_temperature[0] > 0 and (is_extruder_used[0])}M104 T0 S{first_layer_temperature[0]}{endif}\n{if first_layer_temperature[1] > 0 and (is_extruder_used[1])}M104 T1 S{first_layer_temperature[1]}{endif}\n{if first_layer_temperature[2] > 0 and (is_extruder_used[2])}M104 T2 S{first_layer_temperature[2]}{endif}\n{if first_layer_temperature[3] > 0 and (is_extruder_used[3])}M104 T3 S{first_layer_temperature[3]}{endif}\n{if first_layer_temperature[4] > 0 and (is_extruder_used[4])}M104 T4 S{first_layer_temperature[4]}{endif}\n{if (is_extruder_used[0]) and initial_tool != 0}\n;\n; purge first tool\n;\nP0 S1 L1 D0; park the tool\nM109 T0 S{first_layer_temperature[0]}\nT0 S1 L0 D0; pick the tool\nG92 E0 ; reset extruder position\nG1 Z10 F720 ; move to the Z ready for purge\nG0 X{(0 == 0 ? 30 : (0 == 1 ? 150 : (0 == 2 ? 210 : 330)))} Y{(0 < 4 ? -7 : -4.5)} F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E10 X40 Z0.2 F500 ; purge while moving towards the sheet\nG0 X70 E9 F800 ; continue purging and wipe the nozzle\nG0 X{70 + 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{70 + 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[0]} F2400 ; retract\n{e_retracted[0] = 1.5 * retract_length[0]} ; update slicer internal retract variable\nG92 E0 ; reset extruder position\nG1 Z10 F720 ; move away\nM104 S{(is_nil(idle_temperature[0]) ? (first_layer_temperature[0] + standby_temperature_delta) : (idle_temperature[0]))} T0\n{endif}\n{if (is_extruder_used[1]) and initial_tool != 1}\n;\n; purge second tool\n;\nP0 S1 L1 D0; park the tool\nM109 T1 S{first_layer_temperature[1]}\nT1 S1 L0 D0; pick the tool\nG92 E0 ; reset extruder position\nG1 Z10 F720 ; move to the Z ready for purge\nG0 X{(1 == 0 ? 30 : (1 == 1 ? 150 : (1 == 2 ? 210 : 330)))} Y{(1 < 4 ? -7 : -4.5)} F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E10 X140 Z0.2 F500 ; purge while moving towards the sheet\nG0 X110 E9 F800 ; continue purging and wipe the nozzle\nG0 X{110 - 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{110 - 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[1]} F2400 ; retract\n{e_retracted[1] = 1.5 * retract_length[1]} ; update slicer internal retract variable\nG92 E0 ; reset extruder position\nG1 Z10 F720 ; move away\nM104 S{(is_nil(idle_temperature[1]) ? (first_layer_temperature[1] + standby_temperature_delta) : (idle_temperature[1]))} T1\n{endif}\n{if (is_extruder_used[2]) and initial_tool != 2}\n;\n; purge third tool\n;\nP0 S1 L1 D0; park the tool\nM109 T2 S{first_layer_temperature[2]}\nT2 S1 L0 D0; pick the tool\nG92 E0 ; reset extruder position\nG1 Z10 F720 ; move to the Z ready for purge\nG0 X{(2 == 0 ? 30 : (2 == 1 ? 150 : (2 == 2 ? 210 : 330)))} Y{(2 < 4 ? -7 : -4.5)} F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E10 X220 Z0.2 F500 ; purge while moving towards the sheet\nG0 X250 E9 F800 ; continue purging and wipe the nozzle\nG0 X{250 + 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{250 + 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[2]} F2400 ; retract\n{e_retracted[2] = 1.5 * retract_length[2]} ; update slicer internal retract variable\nG92 E0 ; reset extruder position\nG1 Z10 F720 ; move away\nM104 S{(is_nil(idle_temperature[2]) ? (first_layer_temperature[2] + standby_temperature_delta) : (idle_temperature[2]))} T2\n{endif}\n{if (is_extruder_used[3]) and initial_tool != 3}\n;\n; purge fourth tool\n;\nP0 S1 L1 D0; park the tool\nM109 T3 S{first_layer_temperature[3]}\nT3 S1 L0 D0; pick the tool\nG92 E0 ; reset extruder position\nG1 Z10 F720 ; move to the Z ready for purge\nG0 X{(3 == 0 ? 30 : (3 == 1 ? 150 : (3 == 2 ? 210 : 330)))} Y{(3 < 4 ? -7 : -4.5)} F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E10 X320 Z0.2 F500 ; purge while moving towards the sheet\nG0 X290 E9 F800 ; continue purging and wipe the nozzle\nG0 X{290 - 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{290 - 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[3]} F2400 ; retract\n{e_retracted[3] = 1.5 * retract_length[3]} ; update slicer internal retract variable\nG92 E0 ; reset extruder position\nG1 Z10 F720 ; move away\nM104 S{(is_nil(idle_temperature[3]) ? (first_layer_temperature[3] + standby_temperature_delta) : (idle_temperature[3]))} T3\n{endif}\n{if (is_extruder_used[4]) and initial_tool != 4}\n;\n; purge fifth tool\n;\nP0 S1 L1 D0; park the tool\nM109 T4 S{first_layer_temperature[4]}\nT4 S1 L0 D0; pick the tool\nG92 E0 ; reset extruder position\nG1 Z10 F720 ; move to the Z ready for purge\nG0 X{(4 == 0 ? 30 : (4 == 1 ? 150 : (4 == 2 ? 210 : 330)))} Y{(4 < 4 ? -7 : -4.5)} F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E10 X320 Z0.2 F500 ; purge while moving towards the sheet\nG0 X290 E9 F800 ; continue purging and wipe the nozzle\nG0 X{290 - 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{290 - 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[4]} F2400 ; retract\n{e_retracted[4] = 1.5 * retract_length[4]} ; update slicer internal retract variable\nG92 E0 ; reset extruder position\nG1 Z10 F720 ; move away\nM104 S{(is_nil(idle_temperature[4]) ? (first_layer_temperature[4] + standby_temperature_delta) : (idle_temperature[4]))} T4\n{endif}\n;\n; purge initial tool\n;\nP0 S1 L1 D0; park the tool\nM109 T{initial_tool} S{first_layer_temperature[initial_tool]}\nT{initial_tool} S1 L0 D0; pick the tool\nG92 E0 ; reset extruder position\nG1 Z10 F720 ; move to the Z ready for purge\nG0 X{(initial_tool == 0 ? 30 : (initial_tool == 1 ? 150 : (initial_tool == 2 ? 210 : 330)))} Y{(initial_tool < 4 ? -7 : -4.5)} F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E10 X{(initial_tool == 0 ? 30 : (initial_tool == 1 ? 150 : (initial_tool == 2 ? 210 : 330))) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 10)} Z0.2 F500 ; purge while moving towards the sheet\nG0 X{(initial_tool == 0 ? 30 : (initial_tool == 1 ? 150 : (initial_tool == 2 ? 210 : 330))) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 40)} E9 F800 ; continue purging and wipe the nozzle\nG0 X{(initial_tool == 0 ? 30 : (initial_tool == 1 ? 150 : (initial_tool == 2 ? 210 : 330))) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 40) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 3)} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{(initial_tool == 0 ? 30 : (initial_tool == 1 ? 150 : (initial_tool == 2 ? 210 : 330))) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 40) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 3 * 2)} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[initial_tool]} F2400 ; retract\n{e_retracted[initial_tool] = 1.5 * retract_length[initial_tool]}\nG92 E0 ; reset extruder position\n +start_gcode = M17 ; enable steppers\nM862.3 P "XL" ; printer model check\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\n; set print area\nM555 X{first_layer_print_min[0]} Y{first_layer_print_min[1]} W{(first_layer_print_max[0]) - (first_layer_print_min[0])} H{(first_layer_print_max[1]) - (first_layer_print_min[1])}\n; inform about nozzle diameter\n{if (is_extruder_used[0])}M862.1 T0 P{nozzle_diameter[0]}{endif}\n{if (is_extruder_used[1])}M862.1 T1 P{nozzle_diameter[1]}{endif}\n{if (is_extruder_used[2])}M862.1 T2 P{nozzle_diameter[2]}{endif}\n{if (is_extruder_used[3])}M862.1 T3 P{nozzle_diameter[3]}{endif}\n{if (is_extruder_used[4])}M862.1 T4 P{nozzle_diameter[4]}{endif}\n\n; turn off unused heaters\n{if ! is_extruder_used[0]} M104 T0 S0 {endif}\n{if ! is_extruder_used[1]} M104 T1 S0 {endif}\n{if ! is_extruder_used[2]} M104 T2 S0 {endif}\n{if ! is_extruder_used[3]} M104 T3 S0 {endif}\n{if ! is_extruder_used[4]} M104 T4 S0 {endif}\n\nM217 Z{max(zhop, 2.0)} ; set toolchange z hop to 2mm, or zhop variable from slicer if higher\n; set bed and extruder temp for MBL\nM140 S[first_layer_bed_temperature] ; set bed temp\nG0 Z5 ; add Z clearance\nM109 T{initial_tool} S{((filament_type[initial_tool] == "PC" or filament_type[initial_tool] == "PA") ? (first_layer_temperature[initial_tool] - 25) : (filament_type[initial_tool] == "FLEX" ? 210 : 170))}\n; Home XY\nG28 XY\n; try picking tools used in print\nG1 F{travel_speed * 60}\n{if (is_extruder_used[0]) and (initial_tool != 0)}T0 S1 L0 D0{endif}\n{if (is_extruder_used[1]) and (initial_tool != 1)}T1 S1 L0 D0{endif}\n{if (is_extruder_used[2]) and (initial_tool != 2)}T2 S1 L0 D0{endif}\n{if (is_extruder_used[3]) and (initial_tool != 3)}T3 S1 L0 D0{endif}\n{if (is_extruder_used[4]) and (initial_tool != 4)}T4 S1 L0 D0{endif}\n; select tool that will be used to home & MBL\nT{initial_tool} S1 L0 D0\n; home Z with MBL tool\nM84 E ; turn off E motor\nG28 Z\nG0 Z5 ; add Z clearance\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nG29 G ; absorb heat\n; move to the nozzle cleanup area\nG1 X{(min(((((first_layer_print_min[0] + first_layer_print_max[0]) / 2) < ((print_bed_min[0] + print_bed_max[0]) / 2)) ? (((first_layer_print_min[1] - 7) < -2) ? 70 : (min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)) : (((first_layer_print_min[1] - 7) < -2) ? 260 : (min(print_bed_max[0], first_layer_print_min[0] + 32) - 32))), first_layer_print_min[0])) + 32} Y{(min((first_layer_print_min[1] - 7), first_layer_print_min[1]))} Z{5} F{(travel_speed * 60)}\nM302 S160 ; lower cold extrusion limit to 160C\nG1 E{-(filament_type[0] == "FLEX" ? 4 : 2)} F2400 ; retraction for nozzle cleanup\n; nozzle cleanup\nM84 E ; turn off E motor\nG29 P9 X{((((first_layer_print_min[0] + first_layer_print_max[0]) / 2) < ((print_bed_min[0] + print_bed_max[0]) / 2)) ? (((first_layer_print_min[1] - 7) < -2) ? 70 : (min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)) : (((first_layer_print_min[1] - 7) < -2) ? 260 : (min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)))} Y{(first_layer_print_min[1] - 7)} W{32} H{7}\nG0 Z5 F480 ; move away in Z\nM107 ; turn off the fan\n; MBL\nM84 E ; turn off E motor\nG29 P1 ; invalidate mbl & probe print area\nG29 P1 X30 Y0 W{(((is_extruder_used[4]) or ((is_extruder_used[3]) or (is_extruder_used[2]))) ? "300" : ((is_extruder_used[1]) ? "130" : "50"))} H20 C ; probe near purge place\nG29 P3.2 ; interpolate mbl probes\nG29 P3.13 ; extrapolate mbl outside probe area\nG29 A ; activate mbl\nG1 Z10 F720 ; move away in Z\nG1 F{travel_speed * 60}\nP0 S1 L1 D0; park the tool\n; set extruder temp\n{if first_layer_temperature[0] > 0 and (is_extruder_used[0])}M104 T0 S{first_layer_temperature[0]}{endif}\n{if first_layer_temperature[1] > 0 and (is_extruder_used[1])}M104 T1 S{first_layer_temperature[1]}{endif}\n{if first_layer_temperature[2] > 0 and (is_extruder_used[2])}M104 T2 S{first_layer_temperature[2]}{endif}\n{if first_layer_temperature[3] > 0 and (is_extruder_used[3])}M104 T3 S{first_layer_temperature[3]}{endif}\n{if first_layer_temperature[4] > 0 and (is_extruder_used[4])}M104 T4 S{first_layer_temperature[4]}{endif}\n{if (is_extruder_used[0]) and initial_tool != 0}\n;\n; purge first tool\n;\nG1 F{travel_speed * 60}\nP0 S1 L2 D0; park the tool\nM109 T0 S{first_layer_temperature[0]}\nT0 S1 L0 D0; pick the tool\nG92 E0 ; reset extruder position\n\nG0 X{(0 == 0 ? 30 : (0 == 1 ? 150 : (0 == 2 ? 210 : 330)))} Y{(0 < 4 ? -7 : -4.5)} Z10 F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E{if is_nil(filament_multitool_ramming[0])}10{else}30{endif} X40 Z0.2 F{if is_nil(filament_multitool_ramming[0])}500{else}170{endif} ; purge while moving towards the sheet\nG0 X70 E9 F800 ; continue purging and wipe the nozzle\nG0 X{70 + 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{70 + 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[0]} F2400 ; retract\n{e_retracted[0] = 1.5 * retract_length[0]} ; update slicer internal retract variable\nG92 E0 ; reset extruder position\n\nM104 S{(is_nil(idle_temperature[0]) ? (first_layer_temperature[0] + standby_temperature_delta) : (idle_temperature[0]))} T0\n{endif}\n{if (is_extruder_used[1]) and initial_tool != 1}\n;\n; purge second tool\n;\nG1 F{travel_speed * 60}\nP0 S1 L2 D0; park the tool\nM109 T1 S{first_layer_temperature[1]}\nT1 S1 L0 D0; pick the tool\nG92 E0 ; reset extruder position\n\nG0 X{(1 == 0 ? 30 : (1 == 1 ? 150 : (1 == 2 ? 210 : 330)))} Y{(1 < 4 ? -7 : -4.5)} Z10 F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E{if is_nil(filament_multitool_ramming[1])}10{else}30{endif} X140 Z0.2 F{if is_nil(filament_multitool_ramming[1])}500{else}170{endif} ; purge while moving towards the sheet\nG0 X110 E9 F800 ; continue purging and wipe the nozzle\nG0 X{110 - 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{110 - 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[1]} F2400 ; retract\n{e_retracted[1] = 1.5 * retract_length[1]} ; update slicer internal retract variable\nG92 E0 ; reset extruder position\n\nM104 S{(is_nil(idle_temperature[1]) ? (first_layer_temperature[1] + standby_temperature_delta) : (idle_temperature[1]))} T1\n{endif}\n{if (is_extruder_used[2]) and initial_tool != 2}\n;\n; purge third tool\n;\nG1 F{travel_speed * 60}\nP0 S1 L2 D0; park the tool\nM109 T2 S{first_layer_temperature[2]}\nT2 S1 L0 D0; pick the tool\nG92 E0 ; reset extruder position\n\nG0 X{(2 == 0 ? 30 : (2 == 1 ? 150 : (2 == 2 ? 210 : 330)))} Y{(2 < 4 ? -7 : -4.5)} Z10 F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E{if is_nil(filament_multitool_ramming[2])}10{else}30{endif} X220 Z0.2 F{if is_nil(filament_multitool_ramming[2])}500{else}170{endif} ; purge while moving towards the sheet\nG0 X250 E9 F800 ; continue purging and wipe the nozzle\nG0 X{250 + 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{250 + 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[2]} F2400 ; retract\n{e_retracted[2] = 1.5 * retract_length[2]} ; update slicer internal retract variable\nG92 E0 ; reset extruder position\n\nM104 S{(is_nil(idle_temperature[2]) ? (first_layer_temperature[2] + standby_temperature_delta) : (idle_temperature[2]))} T2\n{endif}\n{if (is_extruder_used[3]) and initial_tool != 3}\n;\n; purge fourth tool\n;\nG1 F{travel_speed * 60}\nP0 S1 L2 D0; park the tool\nM109 T3 S{first_layer_temperature[3]}\nT3 S1 L0 D0; pick the tool\nG92 E0 ; reset extruder position\n\nG0 X{(3 == 0 ? 30 : (3 == 1 ? 150 : (3 == 2 ? 210 : 330)))} Y{(3 < 4 ? -7 : -4.5)} Z10 F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E{if is_nil(filament_multitool_ramming[3])}10{else}30{endif} X320 Z0.2 F{if is_nil(filament_multitool_ramming[3])}500{else}170{endif} ; purge while moving towards the sheet\nG0 X290 E9 F800 ; continue purging and wipe the nozzle\nG0 X{290 - 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{290 - 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[3]} F2400 ; retract\n{e_retracted[3] = 1.5 * retract_length[3]} ; update slicer internal retract variable\nG92 E0 ; reset extruder position\n\nM104 S{(is_nil(idle_temperature[3]) ? (first_layer_temperature[3] + standby_temperature_delta) : (idle_temperature[3]))} T3\n{endif}\n{if (is_extruder_used[4]) and initial_tool != 4}\n;\n; purge fifth tool\n;\nG1 F{travel_speed * 60}\nP0 S1 L2 D0; park the tool\nM109 T4 S{first_layer_temperature[4]}\nT4 S1 L0 D0; pick the tool\nG92 E0 ; reset extruder position\n\nG0 X{(4 == 0 ? 30 : (4 == 1 ? 150 : (4 == 2 ? 210 : 330)))} Y{(4 < 4 ? -7 : -4.5)} Z10 F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E{if is_nil(filament_multitool_ramming[4])}10{else}30{endif} X320 Z0.2 F{if is_nil(filament_multitool_ramming[4])}500{else}170{endif} ; purge while moving towards the sheet\nG0 X290 E9 F800 ; continue purging and wipe the nozzle\nG0 X{290 - 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{290 - 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[4]} F2400 ; retract\n{e_retracted[4] = 1.5 * retract_length[4]} ; update slicer internal retract variable\nG92 E0 ; reset extruder position\n\nM104 S{(is_nil(idle_temperature[4]) ? (first_layer_temperature[4] + standby_temperature_delta) : (idle_temperature[4]))} T4\n{endif}\n;\n; purge initial tool\n;\nG1 F{travel_speed * 60}\nP0 S1 L2 D0; park the tool\nM109 T{initial_tool} S{first_layer_temperature[initial_tool]}\nT{initial_tool} S1 L0 D0; pick the tool\nG92 E0 ; reset extruder position\n\nG0 X{(initial_tool == 0 ? 30 : (initial_tool == 1 ? 150 : (initial_tool == 2 ? 210 : 330)))} Y{(initial_tool < 4 ? -7 : -4.5)} Z10 F{(travel_speed * 60)} ; move close to the sheet's edge\nG0 E{if is_nil(filament_multitool_ramming[initial_tool])}10{else}30{endif} X{(initial_tool == 0 ? 30 : (initial_tool == 1 ? 150 : (initial_tool == 2 ? 210 : 330))) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 10)} Z0.2 F{if is_nil(filament_multitool_ramming[initial_tool])}500{else}170{endif} ; purge while moving towards the sheet\nG0 X{(initial_tool == 0 ? 30 : (initial_tool == 1 ? 150 : (initial_tool == 2 ? 210 : 330))) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 40)} E9 F800 ; continue purging and wipe the nozzle\nG0 X{(initial_tool == 0 ? 30 : (initial_tool == 1 ? 150 : (initial_tool == 2 ? 210 : 330))) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 40) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 3)} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{(initial_tool == 0 ? 30 : (initial_tool == 1 ? 150 : (initial_tool == 2 ? 210 : 330))) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 40) + ((initial_tool == 0 or initial_tool == 2 ? 1 : -1) * 3 * 2)} Z0.2 F{8000} ; wipe, move quickly away from the bed\nG1 E{- 1.5 * retract_length[initial_tool]} F2400 ; retract\n{e_retracted[initial_tool] = 1.5 * retract_length[initial_tool]}\nG92 E0 ; reset extruder position end_gcode = G4 ; wait\n\n{if max_layer_z < max_print_height}G1 Z{z_offset+min(max_layer_z+5, max_print_height)}{endif} ; Move print head up\nP0 S1 ; park tool\nM84 ; disable motors\n\n; turn off extruder heaters\n{if is_extruder_used[0]} M104 T0 S0 {endif}\n{if is_extruder_used[1]} M104 T1 S0 {endif}\n{if is_extruder_used[2]} M104 T2 S0 {endif}\n{if is_extruder_used[3]} M104 T3 S0 {endif}\n{if is_extruder_used[4]} M104 T4 S0 {endif}\n\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nM221 S100 ; reset flow percentage\nM84 ; disable motors\nM77 ; stop print timer\n; max_layer_z = [max_layer_z] toolchange_gcode = ; Change Tool[previous_extruder] -> Tool[next_extruder] (layer [layer_num])\n{\nlocal max_speed_toolchange = 350.0;\nlocal wait_for_extruder_temp = true;\nposition[2] = position[2] + 2.0;\n\nlocal speed_toolchange = max_speed_toolchange;\nif travel_speed < max_speed_toolchange then\n speed_toolchange = travel_speed;\nendif\n"G1 F" + (speed_toolchange * 60) + "\n";\nif wait_for_extruder_temp and not((layer_num < 0) and (next_extruder == initial_tool)) then\n "P0 S1 L2 D0\n";\n "; " + layer_num + "\n";\n if layer_num == 0 then\n "M109 S" + first_layer_temperature[next_extruder] + " T" + next_extruder + "\n";\n else\n "M109 S" + temperature[next_extruder] + " T" + next_extruder + "\n";\n endif\nendif\n"T" + next_extruder + " S1 L0 D0\n";\n} color_change_gcode = M600 @@ -19054,7 +19071,7 @@ wipe = 0 retract_before_wipe = 80 retract_speed = 35 deretract_speed = 0 -start_gcode = M17 ; enable steppers\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM862.3 P "MK4" ; printer model check\nM862.5 P2 ; g-code level check\nM862.6 P"Input shaper" ; FW feature check\nM115 U5.0.0-RC+11963\n\nM555 X{(min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)} Y{(max(0, first_layer_print_min[1]) - 4)} W{((min(print_bed_max[0], max(first_layer_print_min[0] + 32, first_layer_print_max[0])))) - ((min(print_bed_max[0], first_layer_print_min[0] + 32) - 32))} H{((first_layer_print_max[1])) - ((max(0, first_layer_print_min[1]) - 4))}\n\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\n\nM140 S[first_layer_bed_temperature] ; set bed temp\n{if filament_type[initial_tool]=="PC" or filament_type[initial_tool]=="PA"}\nM104 S{first_layer_temperature[initial_tool]-25} ; set extruder temp for bed leveling\nM109 R{first_layer_temperature[initial_tool]-25} ; wait for temp\n{elsif filament_type[initial_tool]=="FLEX"}\nM104 S210 ; set extruder temp for bed leveling\nM109 R210 ; wait for temp\n{else}\nM104 S170 ; set extruder temp for bed leveling\nM109 R170 ; wait for temp\n{endif}\n\nM84 E ; turn off E motor\n\nG28 ; home all without mesh bed level\n\nG1 X{10 + 32} Y-4 Z5 F4800\n\nM302 S160 ; lower cold extrusion limit to 160C\n\n{if filament_type[initial_tool]=="FLEX"}\nG1 E-4 F2400 ; retraction\n{else}\nG1 E-2 F2400 ; retraction\n{endif}\n\nM84 E ; turn off E motor\n\nG29 P9 X10 Y-4 W32 H4\n\n{if first_layer_bed_temperature[initial_tool]<=60}M106 S100{endif}\n\nG0 Z40 F10000\n\nM190 S[first_layer_bed_temperature] ; wait for bed temp\n\nM107\n\n;\n; MBL\n;\nM84 E ; turn off E motor\nG29 P1 ; invalidate mbl & probe print area\nG29 P1 X0 Y0 W50 H20 C ; probe near purge place\nG29 P3.2 ; interpolate mbl probes\nG29 P3.13 ; extrapolate mbl outside probe area\nG29 A ; activate mbl\n\n; prepare for purge\nM104 S{first_layer_temperature[0]}\nG0 X0 Y-4 Z15 F4800 ; move away and ready for the purge\nM109 S{first_layer_temperature[0]}\n\nG92 E0\nM569 S0 E ; set spreadcycle mode for extruder\n\n;\n; Extrude purge line\n;\nG92 E0 ; reset extruder position\nG1 E{(filament_type[0] == "FLEX" ? 4 : 2)} F2400 ; deretraction after the initial one before nozzle cleaning\nG0 E7 X15 Z0.2 F500 ; purge\nG0 X25 E4 F500 ; purge\nG0 X35 E4 F650 ; purge\nG0 X45 E4 F800 ; purge\nG0 X{45 + 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{45 + 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\n\nG92 E0\nM221 S100 ; set flow to 100% +start_gcode = M17 ; enable steppers\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM862.3 P "MK4" ; printer model check\nM862.5 P2 ; g-code level check\nM862.6 P"Input shaper" ; FW feature check\nM115 U5.0.0+12056\n\nM555 X{(min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)} Y{(max(0, first_layer_print_min[1]) - 4)} W{((min(print_bed_max[0], max(first_layer_print_min[0] + 32, first_layer_print_max[0])))) - ((min(print_bed_max[0], first_layer_print_min[0] + 32) - 32))} H{((first_layer_print_max[1])) - ((max(0, first_layer_print_min[1]) - 4))}\n\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\n\nM140 S[first_layer_bed_temperature] ; set bed temp\n{if filament_type[initial_tool]=="PC" or filament_type[initial_tool]=="PA"}\nM104 S{first_layer_temperature[initial_tool]-25} ; set extruder temp for bed leveling\nM109 R{first_layer_temperature[initial_tool]-25} ; wait for temp\n{elsif filament_type[initial_tool]=="FLEX"}\nM104 S210 ; set extruder temp for bed leveling\nM109 R210 ; wait for temp\n{else}\nM104 S170 ; set extruder temp for bed leveling\nM109 R170 ; wait for temp\n{endif}\n\nM84 E ; turn off E motor\n\nG28 ; home all without mesh bed level\n\nG1 X{10 + 32} Y-4 Z5 F4800\n\nM302 S160 ; lower cold extrusion limit to 160C\n\n{if filament_type[initial_tool]=="FLEX"}\nG1 E-4 F2400 ; retraction\n{else}\nG1 E-2 F2400 ; retraction\n{endif}\n\nM84 E ; turn off E motor\n\nG29 P9 X10 Y-4 W32 H4\n\n{if first_layer_bed_temperature[initial_tool]<=60}M106 S100{endif}\n\nG0 Z40 F10000\n\nM190 S[first_layer_bed_temperature] ; wait for bed temp\n\nM107\n\n;\n; MBL\n;\nM84 E ; turn off E motor\nG29 P1 ; invalidate mbl & probe print area\nG29 P1 X0 Y0 W50 H20 C ; probe near purge place\nG29 P3.2 ; interpolate mbl probes\nG29 P3.13 ; extrapolate mbl outside probe area\nG29 A ; activate mbl\n\n; prepare for purge\nM104 S{first_layer_temperature[0]}\nG0 X0 Y-4 Z15 F4800 ; move away and ready for the purge\nM109 S{first_layer_temperature[0]}\n\nG92 E0\nM569 S0 E ; set spreadcycle mode for extruder\n\n;\n; Extrude purge line\n;\nG92 E0 ; reset extruder position\nG1 E{(filament_type[0] == "FLEX" ? 4 : 2)} F2400 ; deretraction after the initial one before nozzle cleaning\nG0 E7 X15 Z0.2 F500 ; purge\nG0 X25 E4 F500 ; purge\nG0 X35 E4 F650 ; purge\nG0 X45 E4 F800 ; purge\nG0 X{45 + 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{45 + 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\n\nG92 E0\nM221 S100 ; set flow to 100% end_gcode = {if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+1, max_print_height)} F720 ; Move print head up{endif}\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nG1 X241 Y170 F3600 ; park\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+23, max_print_height)} F300 ; Move print head up{endif}\nG4 ; wait\nM572 S0 ; reset PA\nM593 X T2 F0 ; disable IS\nM593 Y T2 F0 ; disable IS\nM84 X Y E ; disable motors\n; max_layer_z = [max_layer_z] before_layer_gcode = ;BEFORE_LAYER_CHANGE\nG92 E0.0\n;[layer_z]\nM201 X{interpolate_table(extruded_weight_total, (0,4000), (1400,2500), (10000,2500))} Y{interpolate_table(extruded_weight_total, (0,4000), (1400,2500), (10000,2500))}\n{if ! spiral_vase}M74 W[extruded_weight_total]{endif}\n default_print_profile = 0.20mm SPEED @MK4IS 0.4 @@ -19154,7 +19171,7 @@ default_filament_profile = "Prusament PLA @PG 0.8" ; retract_before_wipe = 80 ; retract_speed = 35 ; deretract_speed = 0 -; start_gcode = M17 ; enable steppers\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM862.3 P "[printer_model]" ; printer model check\nM862.5 P2 ; g-code level check\nM862.6 P"Input shaper" ; FW feature check\nM115 U5.0.0-RC+11963\n\nM555 X{(min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)} Y{(max(0, first_layer_print_min[1]) - 4)} W{((min(print_bed_max[0], max(first_layer_print_min[0] + 32, first_layer_print_max[0])))) - ((min(print_bed_max[0], first_layer_print_min[0] + 32) - 32))} H{((first_layer_print_max[1])) - ((max(0, first_layer_print_min[1]) - 4))}\n\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\n\nM140 S[first_layer_bed_temperature] ; set bed temp\n{if filament_type[initial_tool]=="PC" or filament_type[initial_tool]=="PA"}\nM104 S{first_layer_temperature[initial_tool]-25} ; set extruder temp for bed leveling\nM109 R{first_layer_temperature[initial_tool]-25} ; wait for temp\n{elsif filament_type[initial_tool]=="FLEX"}\nM104 S210 ; set extruder temp for bed leveling\nM109 R210 ; wait for temp\n{else}\nM104 S170 ; set extruder temp for bed leveling\nM109 R170 ; wait for temp\n{endif}\n\nM84 E ; turn off E motor\n\nG28 ; home all without mesh bed level\n\nG1 X{10 + 32} Y-4 Z5 F4800\n\nM302 S160 ; lower cold extrusion limit to 160C\n\n{if filament_type[initial_tool]=="FLEX"}\nG1 E-4 F2400 ; retraction\n{else}\nG1 E-2 F2400 ; retraction\n{endif}\n\nM84 E ; turn off E motor\n\nG29 P9 X10 Y-4 W32 H4\n\n{if first_layer_bed_temperature[initial_tool]<=60}M106 S100{endif}\n\nG0 Z40 F10000\n\nM190 S[first_layer_bed_temperature] ; wait for bed temp\n\nM107\n\n;\n; MBL\n;\nM84 E ; turn off E motor\nG29 P1 ; invalidate mbl & probe print area\nG29 P1 X0 Y0 W50 H20 C ; probe near purge place\nG29 P3.2 ; interpolate mbl probes\nG29 P3.13 ; extrapolate mbl outside probe area\nG29 A ; activate mbl\n\n; prepare for purge\nM104 S{first_layer_temperature[0]}\nG0 X0 Y-4 Z15 F4800 ; move away and ready for the purge\nM109 S{first_layer_temperature[0]}\n\nG92 E0\nM569 S0 E ; set spreadcycle mode for extruder\n\n;\n; Extrude purge line\n;\nG92 E0 ; reset extruder position\nG1 E{(filament_type[0] == "FLEX" ? 4 : 2)} F2400 ; deretraction after the initial one before nozzle cleaning\nG0 E7 X15 Z0.2 F500 ; purge\nG0 X25 E4 F500 ; purge\nG0 X35 E4 F650 ; purge\nG0 X45 E4 F800 ; purge\nG0 X{45 + 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{45 + 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\n\nG92 E0\nM221 S100 ; set flow to 100% +; start_gcode = M17 ; enable steppers\nM862.1 P[nozzle_diameter] ; nozzle diameter check\nM862.3 P "[printer_model]" ; printer model check\nM862.5 P2 ; g-code level check\nM862.6 P"Input shaper" ; FW feature check\nM115 U5.0.0+12056\n\nM555 X{(min(print_bed_max[0], first_layer_print_min[0] + 32) - 32)} Y{(max(0, first_layer_print_min[1]) - 4)} W{((min(print_bed_max[0], max(first_layer_print_min[0] + 32, first_layer_print_max[0])))) - ((min(print_bed_max[0], first_layer_print_min[0] + 32) - 32))} H{((first_layer_print_max[1])) - ((max(0, first_layer_print_min[1]) - 4))}\n\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\n\nM140 S[first_layer_bed_temperature] ; set bed temp\n{if filament_type[initial_tool]=="PC" or filament_type[initial_tool]=="PA"}\nM104 S{first_layer_temperature[initial_tool]-25} ; set extruder temp for bed leveling\nM109 R{first_layer_temperature[initial_tool]-25} ; wait for temp\n{elsif filament_type[initial_tool]=="FLEX"}\nM104 S210 ; set extruder temp for bed leveling\nM109 R210 ; wait for temp\n{else}\nM104 S170 ; set extruder temp for bed leveling\nM109 R170 ; wait for temp\n{endif}\n\nM84 E ; turn off E motor\n\nG28 ; home all without mesh bed level\n\nG1 X{10 + 32} Y-4 Z5 F4800\n\nM302 S160 ; lower cold extrusion limit to 160C\n\n{if filament_type[initial_tool]=="FLEX"}\nG1 E-4 F2400 ; retraction\n{else}\nG1 E-2 F2400 ; retraction\n{endif}\n\nM84 E ; turn off E motor\n\nG29 P9 X10 Y-4 W32 H4\n\n{if first_layer_bed_temperature[initial_tool]<=60}M106 S100{endif}\n\nG0 Z40 F10000\n\nM190 S[first_layer_bed_temperature] ; wait for bed temp\n\nM107\n\n;\n; MBL\n;\nM84 E ; turn off E motor\nG29 P1 ; invalidate mbl & probe print area\nG29 P1 X0 Y0 W50 H20 C ; probe near purge place\nG29 P3.2 ; interpolate mbl probes\nG29 P3.13 ; extrapolate mbl outside probe area\nG29 A ; activate mbl\n\n; prepare for purge\nM104 S{first_layer_temperature[0]}\nG0 X0 Y-4 Z15 F4800 ; move away and ready for the purge\nM109 S{first_layer_temperature[0]}\n\nG92 E0\nM569 S0 E ; set spreadcycle mode for extruder\n\n;\n; Extrude purge line\n;\nG92 E0 ; reset extruder position\nG1 E{(filament_type[0] == "FLEX" ? 4 : 2)} F2400 ; deretraction after the initial one before nozzle cleaning\nG0 E7 X15 Z0.2 F500 ; purge\nG0 X25 E4 F500 ; purge\nG0 X35 E4 F650 ; purge\nG0 X45 E4 F800 ; purge\nG0 X{45 + 3} Z{0.05} F{8000} ; wipe, move close to the bed\nG0 X{45 + 3 * 2} Z0.2 F{8000} ; wipe, move quickly away from the bed\n\nG92 E0\nM221 S100 ; set flow to 100% ; end_gcode = {if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+1, max_print_height)} F720 ; Move print head up{endif}\nM104 S0 ; turn off temperature\nM140 S0 ; turn off heatbed\nM107 ; turn off fan\nG1 X241 Y170 F3600 ; park\n{if layer_z < max_print_height}G1 Z{z_offset+min(layer_z+23, max_print_height)} F300 ; Move print head up{endif}\nG4 ; wait\nM572 S0 ; reset PA\nM593 X T2 F0 ; disable IS\nM593 Y T2 F0 ; disable IS\nM84 X Y E ; disable motors\n; max_layer_z = [max_layer_z] ; before_layer_gcode = ;BEFORE_LAYER_CHANGE\nG92 E0.0\n;[layer_z]\nM201 X{interpolate_table(extruded_weight_total, (0,4000), (1400,2500), (10000,2500))} Y{interpolate_table(extruded_weight_total, (0,4000), (1400,2500), (10000,2500))}\n{if ! spiral_vase}M74 W[extruded_weight_total]{endif}\n ; default_print_profile = 0.20mm SPEED @MK4IS 0.4 @@ -19162,7 +19179,6 @@ default_filament_profile = "Prusament PLA @PG 0.8" ; [printer:Original Prusa MK3.9 0.25 nozzle] ; inherits = Original Prusa MK3.9 0.4 nozzle -; printer_model = MK4IS ; printer_variant = 0.25 ; nozzle_diameter = 0.25 ; retract_length = 0.8 @@ -19173,7 +19189,6 @@ default_filament_profile = "Prusament PLA @PG 0.8" ; [printer:Original Prusa MK3.9 0.3 nozzle] ; inherits = Original Prusa MK3.9 0.4 nozzle -; printer_model = MK4IS ; printer_variant = 0.3 ; nozzle_diameter = 0.3 ; retract_length = 0.7 From b5304db75bc622163f6fe904e5a97b9a257285c0 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 5 Sep 2023 11:03:21 +0200 Subject: [PATCH 105/136] Implemented request from #10284 - Rotate connectors in Cut Tool --- src/libslic3r/Model.hpp | 11 +++++---- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 36 ++++++++++++++++++++++------ src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 2 ++ 3 files changed, 37 insertions(+), 12 deletions(-) diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index e089145c57..dc2a57b050 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -293,25 +293,26 @@ struct CutConnector float height; float radius_tolerance;// [0.f : 1.f] float height_tolerance;// [0.f : 1.f] + float z_angle {0.f}; CutConnectorAttributes attribs; CutConnector() - : pos(Vec3d::Zero()), rotation_m(Transform3d::Identity()), radius(5.f), height(10.f), radius_tolerance(0.f), height_tolerance(0.1f) + : pos(Vec3d::Zero()), rotation_m(Transform3d::Identity()), radius(5.f), height(10.f), radius_tolerance(0.f), height_tolerance(0.1f), z_angle(0.f) {} - CutConnector(Vec3d p, Transform3d rot, float r, float h, float rt, float ht, CutConnectorAttributes attributes) - : pos(p), rotation_m(rot), radius(r), height(h), radius_tolerance(rt), height_tolerance(ht), attribs(attributes) + CutConnector(Vec3d p, Transform3d rot, float r, float h, float rt, float ht, float za, CutConnectorAttributes attributes) + : pos(p), rotation_m(rot), radius(r), height(h), radius_tolerance(rt), height_tolerance(ht), z_angle(za), attribs(attributes) {} CutConnector(const CutConnector& rhs) : - CutConnector(rhs.pos, rhs.rotation_m, rhs.radius, rhs.height, rhs.radius_tolerance, rhs.height_tolerance, rhs.attribs) {} + CutConnector(rhs.pos, rhs.rotation_m, rhs.radius, rhs.height, rhs.radius_tolerance, rhs.height_tolerance, rhs.z_angle, rhs.attribs) {} bool operator==(const CutConnector& other) const; bool operator!=(const CutConnector& other) const { return !(other == (*this)); } template inline void serialize(Archive& ar) { - ar(pos, rotation_m, radius, height, radius_tolerance, height_tolerance, attribs); + ar(pos, rotation_m, radius, height, radius_tolerance, height_tolerance, z_angle, attribs); } }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index e52e4c789c..e96e4b37a0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -231,6 +231,7 @@ GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, {"Shape" , _u8L("Shape")}, {"Depth" , _u8L("Depth")}, {"Size" , _u8L("Size")}, + {"Rotation" , _u8L("Rotation")}, {"Groove" , _u8L("Groove")}, {"Width" , _u8L("Width")}, {"Flap Angle" , _u8L("Flap Angle")}, @@ -2329,6 +2330,11 @@ void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors) connectors[idx].radius_tolerance = 0.5f * m_connector_size_tolerance; }); + if (render_angle_input(m_labels_map["Rotation"], m_connector_angle, 0.f, 0.f, 180.f)) + apply_selected_connectors([this, &connectors](size_t idx) { + connectors[idx].z_angle = m_connector_angle; + }); + if (m_connector_type == CutConnectorType::Snap) { render_snap_specific_input(_u8L("Bulge"), _L("Bulge proportion related to radius"), m_snap_bulge_proportion, 0.15f, 5.f, 100.f * m_snap_space_proportion); render_snap_specific_input(_u8L("Space"), _L("Space proportion related to radius"), m_snap_space_proportion, 0.3f, 10.f, 50.f); @@ -2538,7 +2544,7 @@ void GLGizmoCut3D::render_groove_float_input(const std::string& label, float& in } } -void GLGizmoCut3D::render_groove_angle_input(const std::string& label, float& in_val, const float& init_val, float min_val, float max_val) +bool GLGizmoCut3D::render_angle_input(const std::string& label, float& in_val, const float& init_val, float min_val, float max_val) { bool is_changed{ false }; @@ -2551,14 +2557,15 @@ void GLGizmoCut3D::render_groove_angle_input(const std::string& label, float& in const float old_val = val; const std::string format = "%.0f " + _u8L("°"); - m_imgui->slider_float(("##groove_" + label).c_str(), &val, min_val, max_val, format.c_str(), 1.f, true, from_u8(label)); + m_imgui->slider_float(("##angle_" + label).c_str(), &val, min_val, max_val, format.c_str(), 1.f, true, from_u8(label)); m_is_slider_editing_done |= m_imgui->get_last_slider_status().deactivated_after_edit; if (!is_approx(old_val, val)) { if (m_imgui->get_last_slider_status().can_take_snapshot) { - Plater::TakeSnapshot snapshot(wxGetApp().plater(), format_wxstr("%1%: %2%", _L("Groove change"), label), UndoRedo::SnapshotType::GizmoAction); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), format_wxstr("%1%: %2%", _L("Edited"), label), UndoRedo::SnapshotType::GizmoAction); m_imgui->get_last_slider_status().invalidate_snapshot(); - m_groove_editing = true; + if (m_mode == size_t(CutMode::cutTongueAndGroove)) + m_groove_editing = true; } in_val = deg2rad(val); is_changed = true; @@ -2568,14 +2575,19 @@ void GLGizmoCut3D::render_groove_angle_input(const std::string& label, float& in m_imgui->disabled_begin(is_approx(in_val, init_val)); const std::string act_name = _u8L("Reset"); - if (render_reset_button(("##groove_" + label + act_name).c_str(), act_name)) { + if (render_reset_button(("##angle_" + label + act_name).c_str(), act_name)) { Plater::TakeSnapshot snapshot(wxGetApp().plater(), format_wxstr("%1%: %2%", act_name, label), UndoRedo::SnapshotType::GizmoAction); in_val = init_val; is_changed = true; } m_imgui->disabled_end(); - if (is_changed) { + return is_changed; +} + +void GLGizmoCut3D::render_groove_angle_input(const std::string& label, float& in_val, const float& init_val, float min_val, float max_val) +{ + if (render_angle_input(label, in_val, init_val, min_val, max_val)) { update_plane_model(); reset_cut_by_contours(); } @@ -2793,6 +2805,8 @@ void GLGizmoCut3D::validate_connector_settings() m_connector_size = 2.5f; if (m_connector_size_tolerance < 0.f) m_connector_size_tolerance = 0.f; + if (m_connector_angle < 0.f || m_connector_angle > float(PI) ) + m_connector_angle = 0.f; if (m_connector_type == CutConnectorType::Undef) m_connector_type = CutConnectorType::Plug; @@ -2812,6 +2826,7 @@ void GLGizmoCut3D::init_input_window_data(CutConnectors &connectors) float depth_ratio_tolerance { UndefFloat }; float radius { UndefFloat }; float radius_tolerance { UndefFloat }; + float angle { UndefFloat }; CutConnectorType type { CutConnectorType::Undef }; CutConnectorStyle style { CutConnectorStyle::Undef }; CutConnectorShape shape { CutConnectorShape::Undef }; @@ -2825,6 +2840,7 @@ void GLGizmoCut3D::init_input_window_data(CutConnectors &connectors) depth_ratio_tolerance = connector.height_tolerance; radius = connector.radius; radius_tolerance = connector.radius_tolerance; + angle = connector.z_angle; type = connector.attribs.type; style = connector.attribs.style; shape = connector.attribs.shape; @@ -2842,6 +2858,8 @@ void GLGizmoCut3D::init_input_window_data(CutConnectors &connectors) radius = UndefFloat; if (!is_approx(radius_tolerance, connector.radius_tolerance)) radius_tolerance = UndefFloat; + if (!is_approx(angle, connector.z_angle)) + angle = UndefFloat; if (type != connector.attribs.type) type = CutConnectorType::Undef; @@ -2857,6 +2875,7 @@ void GLGizmoCut3D::init_input_window_data(CutConnectors &connectors) m_connector_size = 2.f * radius; m_connector_size_tolerance = 2.f * radius_tolerance; m_connector_type = type; + m_connector_angle = angle; m_connector_style = int(style); m_connector_shape_id = int(shape); } @@ -3108,7 +3127,8 @@ void GLGizmoCut3D::render_connectors() else if (!looking_forward) pos += 0.05 * m_clp_normal; - const Transform3d view_model_matrix = camera.get_view_matrix() * translation_transform(pos) * m_rotation_m * + const Transform3d view_model_matrix = camera.get_view_matrix() * translation_transform(pos) * m_rotation_m * + rotation_transform(-connector.z_angle * Vec3d::UnitZ()) * scale_transform(Vec3f(connector.radius, connector.radius, height).cast()); render_model(m_shapes[connector.attribs].model, render_color, view_model_matrix); @@ -3511,6 +3531,7 @@ bool GLGizmoCut3D::add_connector(CutConnectors& connectors, const Vec2d& mouse_p connectors.emplace_back(pos, m_rotation_m, m_connector_size * 0.5f, m_connector_depth_ratio, m_connector_size_tolerance * 0.5f, m_connector_depth_ratio_tolerance, + m_connector_angle, CutConnectorAttributes( CutConnectorType(m_connector_type), CutConnectorStyle(m_connector_style), CutConnectorShape(m_connector_shape_id))); @@ -3735,6 +3756,7 @@ void GLGizmoCut3D::apply_cut_connectors(ModelObject* mo, const std::string& conn // Transform the new modifier to be aligned inside the instance new_volume->set_transformation(translation_transform(connector.pos) * connector.rotation_m * + rotation_transform(-connector.z_angle * Vec3d::UnitZ()) * scale_transform(Vec3f(connector.radius, connector.radius, connector.height).cast())); new_volume->cut_info = { connector.attribs.type, connector.radius_tolerance, connector.height_tolerance }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index dc4b784b79..89c1b92833 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -138,6 +138,7 @@ class GLGizmoCut3D : public GLGizmoBase float m_connector_depth_ratio{ 3.f }; float m_connector_size{ 2.5f }; + float m_connector_angle{ 0.f }; float m_connector_depth_ratio_tolerance{ 0.1f }; float m_connector_size_tolerance{ 0.f }; @@ -306,6 +307,7 @@ protected: void render_color_marker(float size, const ImU32& color); void render_groove_float_input(const std::string &label, float &in_val, const float &init_val, float &in_tolerance); void render_groove_angle_input(const std::string &label, float &in_val, const float &init_val, float min_val, float max_val); + bool render_angle_input(const std::string& label, float& in_val, const float& init_val, float min_val, float max_val); void render_snap_specific_input(const std::string& label, const wxString& tooltip, float& in_val, const float& init_val, const float min_val, const float max_val); void render_cut_plane_input_window(CutConnectors &connectors); void init_input_window_data(CutConnectors &connectors); From 53adc6871788abdc899c1c274495b75c3316fae4 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 5 Sep 2023 12:17:47 +0200 Subject: [PATCH 106/136] Tech ENABLE_BINARIZED_GCODE set as default --- src/libslic3r/Config.cpp | 29 ++-- src/libslic3r/Config.hpp | 2 - src/libslic3r/GCode.cpp | 92 +---------- src/libslic3r/GCode.hpp | 6 - src/libslic3r/GCode/GCodeProcessor.cpp | 144 ++++------------- src/libslic3r/GCode/GCodeProcessor.hpp | 18 +-- src/libslic3r/GCode/Thumbnails.hpp | 4 - src/libslic3r/GCodeReader.cpp | 8 - src/libslic3r/GCodeReader.hpp | 4 - src/libslic3r/Preset.cpp | 4 - src/libslic3r/PresetBundle.cpp | 15 +- src/libslic3r/Print.cpp | 24 ++- src/libslic3r/Print.hpp | 2 - src/libslic3r/PrintConfig.cpp | 4 +- src/libslic3r/PrintConfig.hpp | 2 - src/libslic3r/Technologies.hpp | 10 +- src/slic3r/GUI/GCodeViewer.cpp | 216 ------------------------- src/slic3r/GUI/GCodeViewer.hpp | 42 ----- src/slic3r/GUI/MainFrame.cpp | 12 +- src/slic3r/GUI/Plater.cpp | 8 +- src/slic3r/GUI/Plater.hpp | 2 - src/slic3r/GUI/Tab.cpp | 2 - 22 files changed, 64 insertions(+), 586 deletions(-) diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index a7c8cdfbc8..4f9844f5ec 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -43,9 +43,7 @@ #include #include -#if ENABLE_BINARIZED_GCODE #include -#endif // ENABLE_BINARIZED_GCODE //FIXME for GCodeFlavor and gcfMarlin (for forward-compatibility conversion) // This is not nice, likely it would be better to pass the ConfigSubstitutionContext to handle_legacy(). @@ -746,7 +744,6 @@ void ConfigBase::setenv_() const ConfigSubstitutions ConfigBase::load(const std::string& filename, ForwardCompatibilitySubstitutionRule compatibility_rule) { -#if ENABLE_BINARIZED_GCODE enum class EFileType { Ini, @@ -759,10 +756,10 @@ ConfigSubstitutions ConfigBase::load(const std::string& filename, ForwardCompati if (is_gcode_file(filename)) { FILE* file = boost::nowide::fopen(filename.c_str(), "rb"); if (file == nullptr) - throw Slic3r::RuntimeError("Error opening the file: " + filename + "\n"); + throw Slic3r::RuntimeError(format("Error opening file %1%", filename)); - using namespace bgcode::core; std::vector cs_buffer(65536); + using namespace bgcode::core; file_type = (is_valid_binary_gcode(*file, true, cs_buffer.data(), cs_buffer.size()) == EResult::Success) ? EFileType::BinaryGCode : EFileType::AsciiGCode; fclose(file); } @@ -774,13 +771,8 @@ ConfigSubstitutions ConfigBase::load(const std::string& filename, ForwardCompati case EFileType::Ini: { return this->load_from_ini(filename, compatibility_rule); } case EFileType::AsciiGCode: { return this->load_from_gcode_file(filename, compatibility_rule);} case EFileType::BinaryGCode: { return this->load_from_binary_gcode_file(filename, compatibility_rule);} - default: { throw Slic3r::RuntimeError("Invalid file: " + filename + "\n"); } + default: { throw Slic3r::RuntimeError(format("Invalid file %1%", filename)); } } -#else - return is_gcode_file(filename) ? - this->load_from_gcode_file(filename, compatibility_rule) : - this->load_from_ini(filename, compatibility_rule); -#endif // ENABLE_BINARIZED_GCODE } ConfigSubstitutions ConfigBase::load_from_ini(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule) @@ -1086,38 +1078,38 @@ ConfigSubstitutions ConfigBase::load_from_gcode_file(const std::string &filename return std::move(substitutions_ctxt.substitutions); } -#if ENABLE_BINARIZED_GCODE ConfigSubstitutions ConfigBase::load_from_binary_gcode_file(const std::string& filename, ForwardCompatibilitySubstitutionRule compatibility_rule) { ConfigSubstitutionContext substitutions_ctxt(compatibility_rule); FilePtr file{ boost::nowide::fopen(filename.c_str(), "rb") }; if (file.f == nullptr) - throw Slic3r::RuntimeError(format("Error opening the file: %1%", filename)); + throw Slic3r::RuntimeError(format("Error opening file %1%", filename)); using namespace bgcode::core; using namespace bgcode::binarize; std::vector cs_buffer(65536); EResult res = is_valid_binary_gcode(*file.f, true, cs_buffer.data(), cs_buffer.size()); if (res != EResult::Success) - throw Slic3r::RuntimeError(format("The selected file is not a valid binary gcode.\nError: %1%", std::string(translate_result(res)))); + throw Slic3r::RuntimeError(format("File %1% does not contain a valid binary gcode\nError: %2%", filename, + std::string(translate_result(res)))); FileHeader file_header; res = read_header(*file.f, file_header, nullptr); if (res != EResult::Success) - throw Slic3r::RuntimeError(format("Error while reading file '%1%': %2%", filename, std::string(translate_result(res)))); + throw Slic3r::RuntimeError(format("Error while reading file %1%: %2%", filename, std::string(translate_result(res)))); // searches for config block BlockHeader block_header; res = read_next_block_header(*file.f, file_header, block_header, EBlockType::SlicerMetadata, cs_buffer.data(), cs_buffer.size()); if (res != EResult::Success) - throw Slic3r::RuntimeError(format("Error while reading file '%1%': %2%", filename, std::string(translate_result(res)))); + throw Slic3r::RuntimeError(format("Error while reading file %1%: %2%", filename, std::string(translate_result(res)))); if ((EBlockType)block_header.type != EBlockType::SlicerMetadata) - throw Slic3r::RuntimeError(format("Unable to find slicer metadata block in file: '%1%'", filename)); + throw Slic3r::RuntimeError(format("Unable to find slicer metadata block in file %1%", filename)); SlicerMetadataBlock slicer_metadata_block; res = slicer_metadata_block.read_data(*file.f, file_header, block_header); if (res != EResult::Success) - throw Slic3r::RuntimeError(format("Error while reading file '%1%': %2%", filename, std::string(translate_result(res)))); + throw Slic3r::RuntimeError(format("Error while reading file %1%: %2%", filename, std::string(translate_result(res)))); // extracts data from block for (const auto& [key, value] : slicer_metadata_block.raw_data) { @@ -1126,7 +1118,6 @@ ConfigSubstitutions ConfigBase::load_from_binary_gcode_file(const std::string& f return std::move(substitutions_ctxt.substitutions); } -#endif // ENABLE_BINARIZED_GCODE void ConfigBase::save(const std::string &file) const { diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index b6320b126b..872e074e28 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -2329,9 +2329,7 @@ public: // Accepts the same data as load_from_ini_string(), only with each configuration line possibly prefixed with a semicolon (G-code comment). ConfigSubstitutions load_from_ini_string_commented(std::string &&data, ForwardCompatibilitySubstitutionRule compatibility_rule); ConfigSubstitutions load_from_gcode_file(const std::string &filename, ForwardCompatibilitySubstitutionRule compatibility_rule); -#if ENABLE_BINARIZED_GCODE ConfigSubstitutions load_from_binary_gcode_file(const std::string& filename, ForwardCompatibilitySubstitutionRule compatibility_rule); -#endif // ENABLE_BINARIZED_GCODE ConfigSubstitutions load(const boost::property_tree::ptree &tree, ForwardCompatibilitySubstitutionRule compatibility_rule); void save(const std::string &file) const; diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 242d8203b4..10e444146a 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -43,9 +43,6 @@ #include "libslic3r.h" #include "LocalesUtils.hpp" #include "format.hpp" -#if ENABLE_BINARIZED_GCODE -#include "libslic3r_version.h" -#endif // ENABLE_BINARIZED_GCODE #include #include @@ -581,9 +578,7 @@ void GCodeGenerator::do_export(Print* print, const char* path, GCodeProcessorRes m_processor.initialize(path_tmp); m_processor.set_print(print); -#if ENABLE_BINARIZED_GCODE m_processor.get_binary_data() = bgcode::binarize::BinaryData(); -#endif // ENABLE_BINARIZED_GCODE GCodeOutputStream file(boost::nowide::fopen(path_tmp.c_str(), "wb"), m_processor); if (! file.is_open()) throw Slic3r::RuntimeError(std::string("G-code export to ") + path + " failed.\nCannot open the file for writing.\n"); @@ -716,7 +711,6 @@ namespace DoExport { } // Fill in print_statistics and return formatted string containing filament statistics to be inserted into G-code comment section. -#if ENABLE_BINARIZED_GCODE static std::string update_print_stats_and_format_filament_stats( const bool has_wipe_tower, const WipeTowerData &wipe_tower_data, @@ -726,15 +720,6 @@ namespace DoExport { PrintStatistics &print_statistics, bool export_binary_data, bgcode::binarize::BinaryData &binary_data) -#else - static std::string update_print_stats_and_format_filament_stats( - const bool has_wipe_tower, - const WipeTowerData &wipe_tower_data, - const FullPrintConfig &config, - const std::vector &extruders, - unsigned int initial_extruder_id, - PrintStatistics &print_statistics) -#endif // ENABLE_BINARIZED_GCODE { std::string filament_stats_string_out; @@ -743,17 +728,10 @@ namespace DoExport { print_statistics.initial_extruder_id = initial_extruder_id; std::vector filament_types; if (! extruders.empty()) { -#if ENABLE_BINARIZED_GCODE std::pair out_filament_used_mm(PrintStatistics::FilamentUsedMmMask + " ", 0); std::pair out_filament_used_cm3(PrintStatistics::FilamentUsedCm3Mask + " ", 0); std::pair out_filament_used_g(PrintStatistics::FilamentUsedGMask + " ", 0); std::pair out_filament_cost(PrintStatistics::FilamentCostMask + " ", 0); -#else - std::pair out_filament_used_mm("; filament used [mm] = ", 0); - std::pair out_filament_used_cm3("; filament used [cm3] = ", 0); - std::pair out_filament_used_g ("; filament used [g] = ", 0); - std::pair out_filament_cost("; filament cost = ", 0); -#endif // ENABLE_BINARIZED_GCODE for (const Extruder &extruder : extruders) { print_statistics.printing_extruders.emplace_back(extruder.id()); filament_types.emplace_back(config.filament_type.get_at(extruder.id())); @@ -776,25 +754,17 @@ namespace DoExport { dst.first += buf; ++ dst.second; }; -#if ENABLE_BINARIZED_GCODE if (!export_binary_data) { -#endif // ENABLE_BINARIZED_GCODE append(out_filament_used_mm, "%.2lf", used_filament); append(out_filament_used_cm3, "%.2lf", extruded_volume * 0.001); -#if ENABLE_BINARIZED_GCODE } -#endif // ENABLE_BINARIZED_GCODE if (filament_weight > 0.) { print_statistics.total_weight = print_statistics.total_weight + filament_weight; -#if ENABLE_BINARIZED_GCODE if (!export_binary_data) -#endif // ENABLE_BINARIZED_GCODE append(out_filament_used_g, "%.2lf", filament_weight); if (filament_cost > 0.) { print_statistics.total_cost = print_statistics.total_cost + filament_cost; -#if ENABLE_BINARIZED_GCODE if (!export_binary_data) -#endif // ENABLE_BINARIZED_GCODE append(out_filament_cost, "%.2lf", filament_cost); } } @@ -803,18 +773,14 @@ namespace DoExport { print_statistics.total_wipe_tower_filament += has_wipe_tower ? used_filament - extruder.used_filament() : 0.; print_statistics.total_wipe_tower_cost += has_wipe_tower ? (extruded_volume - extruder.extruded_volume())* extruder.filament_density() * 0.001 * extruder.filament_cost() * 0.001 : 0.; } -#if ENABLE_BINARIZED_GCODE if (!export_binary_data) { -#endif // ENABLE_BINARIZED_GCODE filament_stats_string_out += out_filament_used_mm.first; filament_stats_string_out += "\n" + out_filament_used_cm3.first; if (out_filament_used_g.second) filament_stats_string_out += "\n" + out_filament_used_g.first; if (out_filament_cost.second) filament_stats_string_out += "\n" + out_filament_cost.first; -#if ENABLE_BINARIZED_GCODE } -#endif // ENABLE_BINARIZED_GCODE print_statistics.initial_filament_type = config.filament_type.get_at(initial_extruder_id); std::sort(filament_types.begin(), filament_types.end()); print_statistics.printing_filament_types = filament_types.front(); @@ -895,7 +861,6 @@ static inline GCode::SmoothPathCache smooth_path_interpolate_global(const Print& void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGeneratorCallback thumbnail_cb) { -#if ENABLE_BINARIZED_GCODE const bool export_to_binary_gcode = print.full_print_config().option("gcode_binary")->value; // if exporting gcode in binary format: // we generate here the data to be passed to the post-processor, who is responsible to export them to file @@ -981,7 +946,6 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail binary_data.printer_metadata.raw_data.emplace_back("extruder_colour", extruder_colours_str); // duplicated into config data } } -#endif // ENABLE_BINARIZED_GCODE // modifies m_silent_time_estimator_enabled DoExport::init_gcode_processor(print.config(), m_processor, m_silent_time_estimator_enabled); @@ -1036,24 +1000,18 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail this->m_avoid_crossing_curled_overhangs.init_bed_shape(get_bed_shape(print.config())); } -#if ENABLE_BINARIZED_GCODE if (!export_to_binary_gcode) -#endif // ENABLE_BINARIZED_GCODE // Write information on the generator. file.write_format("; %s\n\n", Slic3r::header_slic3r_generated().c_str()); -#if ENABLE_BINARIZED_GCODE - // if exporting gcode in ascii format, generate the thumbnails here if (! export_to_binary_gcode) { -#endif // ENABLE_BINARIZED_GCODE + // if exporting gcode in ascii format, generate the thumbnails here if (std::vector> thumbnails = GCodeThumbnails::make_thumbnail_list(print.full_print_config()); ! thumbnails.empty()) GCodeThumbnails::export_thumbnails_to_file(thumbnail_cb, thumbnails, [&file](const char* sz) { file.write(sz); }, [&print]() { print.throw_if_canceled(); }); -#if ENABLE_BINARIZED_GCODE } -#endif // ENABLE_BINARIZED_GCODE // Write notes (content of the Print Settings tab -> Notes) { @@ -1075,9 +1033,7 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail const double layer_height = first_object->config().layer_height.value; assert(! print.config().first_layer_height.percent); const double first_layer_height = print.config().first_layer_height.value; -#if ENABLE_BINARIZED_GCODE if (!export_to_binary_gcode) { -#endif // ENABLE_BINARIZED_GCODE for (size_t region_id = 0; region_id < print.num_print_regions(); ++ region_id) { const PrintRegion ®ion = print.get_print_region(region_id); file.write_format("; external perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frExternalPerimeter, layer_height).width()); @@ -1092,9 +1048,7 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail file.write_format("\n"); } print.throw_if_canceled(); -#if ENABLE_BINARIZED_GCODE } -#endif // ENABLE_BINARIZED_GCODE // adds tags for time estimators if (print.config().remaining_times.value) @@ -1408,7 +1362,6 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail print.throw_if_canceled(); // Get filament stats. -#if ENABLE_BINARIZED_GCODE const std::string filament_stats_string_out = DoExport::update_print_stats_and_format_filament_stats( // Const inputs has_wipe_tower, print.wipe_tower_data(), @@ -1433,34 +1386,15 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail binary_data.printer_metadata.raw_data.emplace_back("max_layer_z", buf); } else { -#else - file.write(DoExport::update_print_stats_and_format_filament_stats( - // Const inputs - has_wipe_tower, print.wipe_tower_data(), - this->config(), - m_writer.extruders(), - initial_extruder_id, - // Modifies - print.m_print_statistics)); -#endif // ENABLE_BINARIZED_GCODE -#if ENABLE_BINARIZED_GCODE // if exporting gcode in ascii format, statistics export is done here -#endif // ENABLE_BINARIZED_GCODE file.write("\n"); -#if ENABLE_BINARIZED_GCODE file.write_format(PrintStatistics::TotalFilamentUsedGValueMask.c_str(), print.m_print_statistics.total_weight); file.write_format(PrintStatistics::TotalFilamentCostValueMask.c_str(), print.m_print_statistics.total_cost); -#else - file.write_format("; total filament used [g] = %.2lf\n", print.m_print_statistics.total_weight); - file.write_format("; total filament cost = %.2lf\n", print.m_print_statistics.total_cost); -#endif // ENABLE_BINARIZED_GCODE if (print.m_print_statistics.total_toolchanges > 0) file.write_format("; total toolchanges = %i\n", print.m_print_statistics.total_toolchanges); file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Estimated_Printing_Time_Placeholder).c_str()); -#if ENABLE_BINARIZED_GCODE // if exporting gcode in ascii format, config export is done here -#endif // ENABLE_BINARIZED_GCODE // Append full config, delimited by two 'phony' configuration keys prusaslicer_config = begin and prusaslicer_config = end. // The delimiters are structured as configuration key / value pairs to be parsable by older versions of PrusaSlicer G-code viewer. { @@ -1471,9 +1405,7 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail file.write(full_config); file.write("; prusaslicer_config = end\n"); } -#if ENABLE_BINARIZED_GCODE } -#endif // ENABLE_BINARIZED_GCODE print.throw_if_canceled(); } @@ -2582,34 +2514,13 @@ void GCodeGenerator::apply_print_config(const PrintConfig &print_config) void GCodeGenerator::append_full_config(const Print &print, std::string &str) { -#if ENABLE_BINARIZED_GCODE std::vector> config; encode_full_config(print, config); for (const auto& [key, value] : config) { str += "; " + key + " = " + value + "\n"; } -#else - const DynamicPrintConfig &cfg = print.full_print_config(); - // Sorted list of config keys, which shall not be stored into the G-code. Initializer list. - static constexpr auto banned_keys = { - "compatible_printers"sv, - "compatible_prints"sv, - //FIXME The print host keys should not be exported to full_print_config anymore. The following keys may likely be removed. - "print_host"sv, - "printhost_apikey"sv, - "printhost_cafile"sv - }; - assert(std::is_sorted(banned_keys.begin(), banned_keys.end())); - auto is_banned = [](const std::string &key) { - return std::binary_search(banned_keys.begin(), banned_keys.end(), key); - }; - for (const std::string &key : cfg.keys()) - if (! is_banned(key) && ! cfg.option(key)->is_nil()) - str += "; " + key + " = " + cfg.opt_serialize(key) + "\n"; -#endif // ENABLE_BINARIZED_GCODE } -#if ENABLE_BINARIZED_GCODE void GCodeGenerator::encode_full_config(const Print& print, std::vector>& config) { const DynamicPrintConfig& cfg = print.full_print_config(); @@ -2633,7 +2544,6 @@ void GCodeGenerator::encode_full_config(const Print& print, std::vector &extruder_ids) { diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index c2f4c9126f..d34f100bf8 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -46,10 +46,6 @@ //#include "GCode/PressureEqualizer.hpp" -#if ENABLE_BINARIZED_GCODE -#include -#endif // ENABLE_BINARIZED_GCODE - namespace Slic3r { // Forward declarations. @@ -168,10 +164,8 @@ public: // append full config to the given string static void append_full_config(const Print& print, std::string& str); -#if ENABLE_BINARIZED_GCODE // translate full config into a list of items static void encode_full_config(const Print& print, std::vector>& config); -#endif // ENABLE_BINARIZED_GCODE // Object and support extrusions of the same PrintObject at the same print_z. // public, so that it could be accessed by free helper functions from GCode.cpp diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 8411538522..ec62f12bc6 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -69,20 +69,18 @@ const std::vector GCodeProcessor::Reserved_Tags = { const float GCodeProcessor::Wipe_Width = 0.05f; const float GCodeProcessor::Wipe_Height = 0.05f; -#if ENABLE_BINARIZED_GCODE bgcode::binarize::BinarizerConfig GCodeProcessor::s_binarizer_config{ { - bgcode::core::ECompressionType::None, // file metadata - bgcode::core::ECompressionType::None, // printer metadata - bgcode::core::ECompressionType::Deflate, // print metadata - bgcode::core::ECompressionType::Deflate, // slicer metadata + bgcode::core::ECompressionType::None, // file metadata + bgcode::core::ECompressionType::None, // printer metadata + bgcode::core::ECompressionType::Deflate, // print metadata + bgcode::core::ECompressionType::Deflate, // slicer metadata bgcode::core::ECompressionType::Heatshrink_12_4, // gcode }, bgcode::core::EGCodeEncodingType::MeatPackComments, bgcode::core::EMetadataEncodingType::INI, bgcode::core::EChecksumType::CRC32 }; -#endif // ENABLE_BINARIZED_GCODE #if ENABLE_GCODE_VIEWER_DATA_CHECKING const std::string GCodeProcessor::Mm3_Per_Mm_Tag = "MM3_PER_MM:"; @@ -474,10 +472,7 @@ void GCodeProcessorResult::reset() { } #else void GCodeProcessorResult::reset() { - -#if ENABLE_BINARIZED_GCODE is_binary_file = false; -#endif // ENABLE_BINARIZED_GCODE moves.clear(); lines_ends.clear(); bed_shape = Pointfs(); @@ -576,10 +571,8 @@ void GCodeProcessor::apply_config(const PrintConfig& config) { m_parser.apply_config(config); -#if ENABLE_BINARIZED_GCODE m_binarizer.set_enabled(config.gcode_binary); m_result.is_binary_file = config.gcode_binary; -#endif // ENABLE_BINARIZED_GCODE m_producer = EProducer::PrusaSlicer; m_flavor = config.gcode_flavor; @@ -1050,11 +1043,10 @@ static inline const char* remove_eols(const char *begin, const char *end) { // Load a G-code into a stand-alone G-code viewer. // throws CanceledException through print->throw_if_canceled() (sent by the caller as callback). void GCodeProcessor::process_file(const std::string& filename, std::function cancel_callback) -#if ENABLE_BINARIZED_GCODE { FILE* file = boost::nowide::fopen(filename.c_str(), "rb"); if (file == nullptr) - throw Slic3r::RuntimeError("Error opening the file: " + filename + "\n"); + throw Slic3r::RuntimeError(format("Error opening file %1%", filename)); using namespace bgcode::core; std::vector cs_buffer(65536); @@ -1068,7 +1060,6 @@ void GCodeProcessor::process_file(const std::string& filename, std::function cancel_callback) -#endif // ENABLE_BINARIZED_GCODE { CNumericLocalesSetter locales_setter; @@ -1119,9 +1110,7 @@ void GCodeProcessor::process_ascii_file(const std::string& filename, std::functi // process gcode m_result.filename = filename; -#if ENABLE_BINARIZED_GCODE m_result.is_binary_file = false; -#endif // ENABLE_BINARIZED_GCODE m_result.id = ++s_result_id; initialize_result_moves(); size_t parse_line_callback_cntr = 10000; @@ -1139,7 +1128,6 @@ void GCodeProcessor::process_ascii_file(const std::string& filename, std::functi this->finalize(false); } -#if ENABLE_BINARIZED_GCODE static void update_lines_ends_and_out_file_pos(const std::string& out_string, std::vector& lines_ends, size_t* out_file_pos) { for (size_t i = 0; i < out_string.size(); ++i) { @@ -1158,7 +1146,7 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct FilePtr file{ boost::nowide::fopen(filename.c_str(), "rb") }; if (file.f == nullptr) - throw Slic3r::RuntimeError("Unable to open file: " + filename + "\n"); + throw Slic3r::RuntimeError(format("Error opening file %1%", filename)); fseek(file.f, 0, SEEK_END); const long file_size = ftell(file.f); @@ -1170,21 +1158,21 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct FileHeader file_header; EResult res = read_header(*file.f, file_header, nullptr); if (res != EResult::Success) - throw Slic3r::RuntimeError("File: " + filename + "does not contain a valid binary gcode\n Error: " + - std::string(translate_result(res)) + "\n"); + throw Slic3r::RuntimeError(format("File %1% does not contain a valid binary gcode\nError: %2%", filename, + std::string(translate_result(res)))); // read file metadata block BlockHeader block_header; std::vector cs_buffer(65536); res = read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); if (res != EResult::Success) - throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); + throw Slic3r::RuntimeError(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); if ((EBlockType)block_header.type != EBlockType::FileMetadata) - throw Slic3r::RuntimeError("Unable to find file metadata block in file: " + filename + "\n"); + throw Slic3r::RuntimeError(format("Unable to find file metadata block in file %1%", filename)); FileMetadataBlock file_metadata_block; res = file_metadata_block.read_data(*file.f, file_header, block_header); if (res != EResult::Success) - throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); + throw Slic3r::RuntimeError(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); auto producer_it = std::find_if(file_metadata_block.raw_data.begin(), file_metadata_block.raw_data.end(), [](const std::pair& item) { return item.first == "Producer"; }); if (producer_it != file_metadata_block.raw_data.end() && boost::starts_with(producer_it->second, std::string(SLIC3R_APP_NAME))) @@ -1195,47 +1183,47 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct // read printer metadata block res = read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); if (res != EResult::Success) - throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); + throw Slic3r::RuntimeError(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); if ((EBlockType)block_header.type != EBlockType::PrinterMetadata) - throw Slic3r::RuntimeError("Unable to find printer metadata block in file: " + filename + "\n"); + throw Slic3r::RuntimeError(format("Unable to find printer metadata block in file %1%", filename)); PrinterMetadataBlock printer_metadata_block; res = printer_metadata_block.read_data(*file.f, file_header, block_header); if (res != EResult::Success) - throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); + throw Slic3r::RuntimeError(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); // read thumbnail blocks res = read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); if (res != EResult::Success) - throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); + throw Slic3r::RuntimeError(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); while ((EBlockType)block_header.type == EBlockType::Thumbnail) { ThumbnailBlock thumbnail_block; res = thumbnail_block.read_data(*file.f, file_header, block_header); if (res != EResult::Success) - throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); + throw Slic3r::RuntimeError(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); res = read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); if (res != EResult::Success) - throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); + throw Slic3r::RuntimeError(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); } // read print metadata block if ((EBlockType)block_header.type != EBlockType::PrintMetadata) - throw Slic3r::RuntimeError("Unable to find print metadata block in file: " + filename + "\n"); + throw Slic3r::RuntimeError(format("Unable to find print metadata block in file %1%", filename)); PrintMetadataBlock print_metadata_block; res = print_metadata_block.read_data(*file.f, file_header, block_header); if (res != EResult::Success) - throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); + throw Slic3r::RuntimeError(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); // read slicer metadata block res = read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); if (res != EResult::Success) - throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); + throw Slic3r::RuntimeError(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); if ((EBlockType)block_header.type != EBlockType::SlicerMetadata) - throw Slic3r::RuntimeError("Unable to find slicer metadata block in file: " + filename + "\n"); + throw Slic3r::RuntimeError(format("Unable to find slicer metadata block in file %1%", filename)); SlicerMetadataBlock slicer_metadata_block; res = slicer_metadata_block.read_data(*file.f, file_header, block_header); if (res != EResult::Success) - throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); + throw Slic3r::RuntimeError(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); DynamicPrintConfig config; config.apply(FullPrintConfig::defaults()); std::string str; @@ -1256,14 +1244,14 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct // read gcodes block res = read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); if (res != EResult::Success) - throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); + throw Slic3r::RuntimeError(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); if ((EBlockType)block_header.type != EBlockType::GCode) - throw Slic3r::RuntimeError("Unable to find gcode block in file: " + filename + "\n"); + throw Slic3r::RuntimeError(format("Unable to find gcode block in file %1%", filename)); while ((EBlockType)block_header.type == EBlockType::GCode) { GCodeBlock block; res = block.read_data(*file.f, file_header, block_header); if (res != EResult::Success) - throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); + throw Slic3r::RuntimeError(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); std::vector& lines_ends = m_result.lines_ends.emplace_back(std::vector()); update_lines_ends_and_out_file_pos(block.raw_data, lines_ends, nullptr); @@ -1277,13 +1265,12 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct res = read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); if (res != EResult::Success) - throw Slic3r::RuntimeError("Error while reading file '" + filename + "': " + std::string(translate_result(res)) + "\n"); + throw Slic3r::RuntimeError(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); } // Don't post-process the G-code to update time stamps. this->finalize(false); } -#endif // ENABLE_BINARIZED_GCODE void GCodeProcessor::initialize(const std::string& filename) { @@ -3656,7 +3643,6 @@ void GCodeProcessor::post_process() if (out.f == nullptr) throw Slic3r::RuntimeError(std::string("GCode processor post process export failed.\nCannot open file for writing.\n")); -#if ENABLE_BINARIZED_GCODE std::vector filament_mm(m_result.extruders_count, 0.0); std::vector filament_cm3(m_result.extruders_count, 0.0); std::vector filament_g(m_result.extruders_count, 0.0); @@ -3715,9 +3701,8 @@ void GCodeProcessor::post_process() const bgcode::core::EResult res = m_binarizer.initialize(*out.f, s_binarizer_config); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError(std::string("Unable to initialize the gcode binarizer.\n")); + throw Slic3r::RuntimeError(std::string("Unable to initialize the gcode binarizer.")); } -#endif // ENABLE_BINARIZED_GCODE auto time_in_minutes = [](float time_in_seconds) { assert(time_in_seconds >= 0.f); @@ -3835,26 +3820,15 @@ void GCodeProcessor::post_process() size_t m_curr_g1_id{ 0 }; size_t m_out_file_pos{ 0 }; -#if ENABLE_BINARIZED_GCODE bgcode::binarize::Binarizer& m_binarizer; -#endif // ENABLE_BINARIZED_GCODE public: -#if ENABLE_BINARIZED_GCODE ExportLines(bgcode::binarize::Binarizer& binarizer, EWriteType type, TimeMachine& machine) #ifndef NDEBUG : m_statistics(*this), m_binarizer(binarizer), m_write_type(type), m_machine(machine) {} #else : m_binarizer(binarizer), m_write_type(type), m_machine(machine) {} #endif // NDEBUG -#else - ExportLines(EWriteType type, TimeMachine& machine) -#ifndef NDEBUG - : m_statistics(*this), m_write_type(type), m_machine(machine) {} -#else - : m_write_type(type), m_machine(machine) {} -#endif // NDEBUG -#endif // ENABLE_BINARIZED_GCODE void update(size_t lines_counter, size_t g1_lines_counter) { m_gcode_lines_map.push_back({ lines_counter, 0 }); @@ -3965,18 +3939,14 @@ void GCodeProcessor::post_process() } } -#if ENABLE_BINARIZED_GCODE if (m_binarizer.is_enabled()) { if (m_binarizer.append_gcode(out_string) != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError(std::string("Error while sending gcode to the binarizer.\n")); + throw Slic3r::RuntimeError("Error while sending gcode to the binarizer."); } else { write_to_file(out, out_string, result, out_path); update_lines_ends_and_out_file_pos(out_string, result.lines_ends.front(), &m_out_file_pos); } -#else - write_to_file(out, out_string, result, out_path); -#endif // ENABLE_BINARIZED_GCODE } // flush the current content of the cache to file @@ -3992,18 +3962,14 @@ void GCodeProcessor::post_process() m_statistics.remove_all_lines(); #endif // NDEBUG -#if ENABLE_BINARIZED_GCODE if (m_binarizer.is_enabled()) { if (m_binarizer.append_gcode(out_string) != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError(std::string("Error while sending gcode to the binarizer.\n")); + throw Slic3r::RuntimeError("Error while sending gcode to the binarizer."); } else { write_to_file(out, out_string, result, out_path); update_lines_ends_and_out_file_pos(out_string, result.lines_ends.front(), &m_out_file_pos); } -#else - write_to_file(out, out_string, result, out_path); -#endif // ENABLE_BINARIZED_GCODE } void synchronize_moves(GCodeProcessorResult& result) const { @@ -4022,33 +3988,19 @@ void GCodeProcessor::post_process() private: void write_to_file(FilePtr& out, const std::string& out_string, GCodeProcessorResult& result, const std::string& out_path) { if (!out_string.empty()) { -#if ENABLE_BINARIZED_GCODE if (!m_binarizer.is_enabled()) { -#endif // ENABLE_BINARIZED_GCODE fwrite((const void*)out_string.c_str(), 1, out_string.length(), out.f); if (ferror(out.f)) { out.close(); boost::nowide::remove(out_path.c_str()); - throw Slic3r::RuntimeError(std::string("GCode processor post process export failed.\nIs the disk full?\n")); + throw Slic3r::RuntimeError("GCode processor post process export failed.\nIs the disk full?"); } -#if ENABLE_BINARIZED_GCODE } -#else - for (size_t i = 0; i < out_string.size(); ++i) { - if (out_string[i] == '\n') - result.lines_ends.emplace_back(m_out_file_pos + i + 1); - } - m_out_file_pos += out_string.size(); -#endif // ENABLE_BINARIZED_GCODE } } }; -#if ENABLE_BINARIZED_GCODE ExportLines export_lines(m_binarizer, m_result.backtrace_enabled ? ExportLines::EWriteType::ByTime : ExportLines::EWriteType::BySize, m_time_processor.machines[0]); -#else - ExportLines export_lines(m_result.backtrace_enabled ? ExportLines::EWriteType::ByTime : ExportLines::EWriteType::BySize, m_time_processor.machines[0]); -#endif // ENABLE_BINARIZED_GCODE // replace placeholder lines with the proper final value // gcode_line is in/out parameter, to reduce expensive memory allocation @@ -4111,25 +4063,6 @@ void GCodeProcessor::post_process() return processed; }; -#if !ENABLE_BINARIZED_GCODE - std::vector filament_mm(m_result.extruders_count, 0.0); - std::vector filament_cm3(m_result.extruders_count, 0.0); - std::vector filament_g(m_result.extruders_count, 0.0); - std::vector filament_cost(m_result.extruders_count, 0.0); - - double filament_total_g = 0.0; - double filament_total_cost = 0.0; - - for (const auto& [id, volume] : m_result.print_statistics.volumes_per_extruder) { - filament_mm[id] = volume / (static_cast(M_PI) * sqr(0.5 * m_result.filament_diameters[id])); - filament_cm3[id] = volume * 0.001; - filament_g[id] = filament_cm3[id] * double(m_result.filament_densities[id]); - filament_cost[id] = filament_g[id] * double(m_result.filament_cost[id]) * 0.001; - filament_total_g += filament_g[id]; - filament_total_cost += filament_cost[id]; - } -#endif // !ENABLE_BINARIZED_GCODE - auto process_used_filament = [&](std::string& gcode_line) { // Prefilter for parsing speed. if (gcode_line.size() < 8 || gcode_line[0] != ';' || gcode_line[1] != ' ') @@ -4150,21 +4083,12 @@ void GCodeProcessor::post_process() }; bool ret = false; -#if ENABLE_BINARIZED_GCODE ret |= process_tag(gcode_line, PrintStatistics::FilamentUsedMmMask, filament_mm); ret |= process_tag(gcode_line, PrintStatistics::FilamentUsedGMask, filament_g); ret |= process_tag(gcode_line, PrintStatistics::TotalFilamentUsedGMask, { filament_total_g }); ret |= process_tag(gcode_line, PrintStatistics::FilamentUsedCm3Mask, filament_cm3); ret |= process_tag(gcode_line, PrintStatistics::FilamentCostMask, filament_cost); ret |= process_tag(gcode_line, PrintStatistics::TotalFilamentCostMask, { filament_total_cost }); -#else - ret |= process_tag(gcode_line, "; filament used [mm] =", filament_mm); - ret |= process_tag(gcode_line, "; filament used [g] =", filament_g); - ret |= process_tag(gcode_line, "; total filament used [g] =", { filament_total_g }); - ret |= process_tag(gcode_line, "; filament used [cm3] =", filament_cm3); - ret |= process_tag(gcode_line, "; filament cost =", filament_cost); - ret |= process_tag(gcode_line, "; total filament cost =", { filament_total_cost }); -#endif // ENABLE_BINARIZED_GCODE return ret; }; @@ -4303,9 +4227,7 @@ void GCodeProcessor::post_process() }; m_result.lines_ends.clear(); -#if ENABLE_BINARIZED_GCODE m_result.lines_ends.emplace_back(std::vector()); -#endif // ENABLE_BINARIZED_GCODE unsigned int line_id = 0; // Backtrace data for Tx gcode lines @@ -4375,17 +4297,14 @@ void GCodeProcessor::post_process() export_lines.flush(out, m_result, out_path); -#if ENABLE_BINARIZED_GCODE if (m_binarizer.is_enabled()) { if (m_binarizer.finalize() != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError(std::string("Error while finalizing the gcode binarizer.\n")); + throw Slic3r::RuntimeError("Error while finalizing the gcode binarizer."); } -#endif // ENABLE_BINARIZED_GCODE out.close(); in.close(); -#if ENABLE_BINARIZED_GCODE if (m_binarizer.is_enabled()) { // updates m_result.lines_ends from binarized gcode file m_result.lines_ends.clear(); @@ -4437,7 +4356,6 @@ void GCodeProcessor::post_process() m_result.lines_ends = { std::vector() }; } } -#endif // ENABLE_BINARIZED_GCODE export_lines.synchronize_moves(m_result); diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 21428f6291..103ac79f0d 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -13,9 +13,7 @@ #include "libslic3r/PrintConfig.hpp" #include "libslic3r/CustomGCode.hpp" -#if ENABLE_BINARIZED_GCODE #include -#endif // ENABLE_BINARIZED_GCODE #include #include @@ -150,17 +148,13 @@ namespace Slic3r { }; std::string filename; -#if ENABLE_BINARIZED_GCODE bool is_binary_file; -#endif // ENABLE_BINARIZED_GCODE unsigned int id; std::vector moves; // Positions of ends of lines of the final G-code this->filename after TimeProcessor::post_process() finalizes the G-code. -#if ENABLE_BINARIZED_GCODE + // Binarized gcodes usually have several gcode blocks. Each block has its own list on ends of lines. + // Ascii gcodes have only one list on ends of lines std::vector> lines_ends; -#else - std::vector lines_ends; -#endif // ENABLE_BINARIZED_GCODE Pointfs bed_shape; float max_print_height; SettingsIds settings_ids; @@ -545,16 +539,12 @@ namespace Slic3r { }; #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING -#if ENABLE_BINARIZED_GCODE_DEBUG_WINDOW static bgcode::binarize::BinarizerConfig& get_binarizer_config() { return s_binarizer_config; } -#endif // ENABLE_BINARIZED_GCODE_DEBUG_WINDOW private: GCodeReader m_parser; -#if ENABLE_BINARIZED_GCODE bgcode::binarize::Binarizer m_binarizer; static bgcode::binarize::BinarizerConfig s_binarizer_config; -#endif // ENABLE_BINARIZED_GCODE EUnits m_units; EPositioningType m_global_positioning_type; @@ -652,10 +642,8 @@ namespace Slic3r { void apply_config(const PrintConfig& config); void set_print(Print* print) { m_print = print; } -#if ENABLE_BINARIZED_GCODE bgcode::binarize::BinaryData& get_binary_data() { return m_binarizer.get_binary_data(); } const bgcode::binarize::BinaryData& get_binary_data() const { return m_binarizer.get_binary_data(); } -#endif // ENABLE_BINARIZED_GCODE void enable_stealth_time_estimator(bool enabled); bool is_stealth_time_estimator_enabled() const { @@ -698,10 +686,8 @@ namespace Slic3r { void apply_config_kissslicer(const std::string& filename); void process_gcode_line(const GCodeReader::GCodeLine& line, bool producers_enabled); -#if ENABLE_BINARIZED_GCODE void process_ascii_file(const std::string& filename, std::function cancel_callback = nullptr); void process_binary_file(const std::string& filename, std::function cancel_callback = nullptr); -#endif // ENABLE_BINARIZED_GCODE // Process tags embedded into comments void process_tags(const std::string_view comment, bool producers_enabled); diff --git a/src/libslic3r/GCode/Thumbnails.hpp b/src/libslic3r/GCode/Thumbnails.hpp index 7635b285e6..4a22e84d13 100644 --- a/src/libslic3r/GCode/Thumbnails.hpp +++ b/src/libslic3r/GCode/Thumbnails.hpp @@ -13,9 +13,7 @@ #include #include -#if ENABLE_BINARIZED_GCODE #include -#endif // ENABLE_BINARIZED_GCODE #include @@ -67,7 +65,6 @@ inline void export_thumbnails_to_file(ThumbnailsGeneratorCallback &thumbnail_cb, } } -#if ENABLE_BINARIZED_GCODE template inline void generate_binary_thumbnails(ThumbnailsGeneratorCallback& thumbnail_cb, std::vector& out_thumbnails, const std::vector> &thumbnails_list, ThrowIfCanceledCallback throw_if_canceled) @@ -98,7 +95,6 @@ inline void generate_binary_thumbnails(ThumbnailsGeneratorCallback& thumbnail_cb } } } -#endif // ENABLE_BINARIZED_GCODE } // namespace Slic3r::GCodeThumbnails diff --git a/src/libslic3r/GCodeReader.cpp b/src/libslic3r/GCodeReader.cpp index 75542a4615..8938d3e22b 100644 --- a/src/libslic3r/GCodeReader.cpp +++ b/src/libslic3r/GCodeReader.cpp @@ -199,20 +199,12 @@ bool GCodeReader::parse_file(const std::string &file, callback_t callback) return this->parse_file_internal(file, callback, [](size_t){}); } -#if ENABLE_BINARIZED_GCODE bool GCodeReader::parse_file(const std::string& file, callback_t callback, std::vector>& lines_ends) { lines_ends.clear(); lines_ends.push_back(std::vector()); return this->parse_file_internal(file, callback, [&lines_ends](size_t file_pos) { lines_ends.front().emplace_back(file_pos); }); } -#else -bool GCodeReader::parse_file(const std::string &file, callback_t callback, std::vector &lines_ends) -{ - lines_ends.clear(); - return this->parse_file_internal(file, callback, [&lines_ends](size_t file_pos){ lines_ends.emplace_back(file_pos); }); -} -#endif // ENABLE_BINARIZED_GCODE bool GCodeReader::parse_file_raw(const std::string &filename, raw_line_callback_t line_callback) { diff --git a/src/libslic3r/GCodeReader.hpp b/src/libslic3r/GCodeReader.hpp index a6df841fa7..1207f03912 100644 --- a/src/libslic3r/GCodeReader.hpp +++ b/src/libslic3r/GCodeReader.hpp @@ -135,11 +135,7 @@ public: bool parse_file(const std::string &file, callback_t callback); // Collect positions of line ends in the binary G-code to be used by the G-code viewer when memory mapping and displaying section of G-code // as an overlay in the 3D scene. -#if ENABLE_BINARIZED_GCODE bool parse_file(const std::string& file, callback_t callback, std::vector>& lines_ends); -#else - bool parse_file(const std::string &file, callback_t callback, std::vector &lines_ends); -#endif // ENABLE_BINARIZED_GCODE // Just read the G-code file line by line, calls callback (const char *begin, const char *end). Returns false if reading the file failed. bool parse_file_raw(const std::string &file, raw_line_callback_t callback); diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 3ee1f424fc..1e9764f815 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -459,11 +459,7 @@ static std::vector s_Preset_print_options { "support_tree_angle", "support_tree_angle_slow", "support_tree_branch_diameter", "support_tree_branch_diameter_angle", "support_tree_branch_diameter_double_wall", "support_tree_top_rate", "support_tree_branch_distance", "support_tree_tip_diameter", "dont_support_bridges", "thick_bridges", "notes", "complete_objects", "extruder_clearance_radius", -#if ENABLE_BINARIZED_GCODE "extruder_clearance_height", "gcode_comments", "gcode_label_objects", "output_filename_format", "post_process", "gcode_substitutions", "gcode_binary", "perimeter_extruder", -#else - "extruder_clearance_height", "gcode_comments", "gcode_label_objects", "output_filename_format", "post_process", "gcode_substitutions", "perimeter_extruder", -#endif // ENABLE_BINARIZED_GCODE "infill_extruder", "solid_infill_extruder", "support_material_extruder", "support_material_interface_extruder", "ooze_prevention", "standby_temperature_delta", "interface_shells", "extrusion_width", "first_layer_extrusion_width", "perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width", diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index 836c2a3413..a9dc23d2c2 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -28,9 +28,7 @@ #include #include -#if ENABLE_BINARIZED_GCODE #include -#endif // ENABLE_BINARIZED_GCODE // Store the print/filament/printer presets into a "presets" subdirectory of the Slic3rPE config dir. // This breaks compatibility with the upstream Slic3r if the --datadir is used to switch between the two versions. @@ -886,11 +884,10 @@ DynamicPrintConfig PresetBundle::full_sla_config() const // If the file is loaded successfully, its print / filament / printer profiles will be activated. ConfigSubstitutions PresetBundle::load_config_file(const std::string &path, ForwardCompatibilitySubstitutionRule compatibility_rule) { -#if ENABLE_BINARIZED_GCODE if (is_gcode_file(path)) { FILE* file = boost::nowide::fopen(path.c_str(), "rb"); if (file == nullptr) - throw Slic3r::RuntimeError("Error opening the file: " + path + "\n"); + throw Slic3r::RuntimeError(format("Error opening file %1%", path)); std::vector cs_buffer(65536); const bool is_binary = bgcode::core::is_valid_binary_gcode(*file, true, cs_buffer.data(), cs_buffer.size()) == bgcode::core::EResult::Success; fclose(file); @@ -903,16 +900,6 @@ ConfigSubstitutions PresetBundle::load_config_file(const std::string &path, Forw load_config_file_config(path, true, std::move(config)); return config_substitutions; } -#else - if (is_gcode_file(path)) { - DynamicPrintConfig config; - config.apply(FullPrintConfig::defaults()); - ConfigSubstitutions config_substitutions = config.load_from_gcode_file(path, compatibility_rule); - Preset::normalize(config); - load_config_file_config(path, true, std::move(config)); - return config_substitutions; - } -#endif // ENABLE_BINARIZED_GCODE // 1) Try to load the config file into a boost property tree. boost::property_tree::ptree tree; diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 4e7f40207e..697caa392a 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -139,9 +139,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n "perimeter_acceleration", "post_process", "gcode_substitutions", -#if ENABLE_BINARIZED_GCODE "gcode_binary", -#endif // ENABLE_BINARIZED_GCODE "printer_notes", "retract_before_travel", "retract_before_wipe", @@ -1637,27 +1635,25 @@ std::string Print::output_filename(const std::string &filename_base) const return this->PrintBase::output_filename(m_config.output_filename_format.value, ".gcode", filename_base, &config); } -#if ENABLE_BINARIZED_GCODE const std::string PrintStatistics::FilamentUsedG = "filament used [g]"; -const std::string PrintStatistics::FilamentUsedGMask = "; " + PrintStatistics::FilamentUsedG + " ="; +const std::string PrintStatistics::FilamentUsedGMask = "; filament used [g] ="; -const std::string PrintStatistics::TotalFilamentUsedG = "total " + PrintStatistics::FilamentUsedG; -const std::string PrintStatistics::TotalFilamentUsedGMask = "; " + PrintStatistics::TotalFilamentUsedG + " ="; -const std::string PrintStatistics::TotalFilamentUsedGValueMask = TotalFilamentUsedGMask + " %.2lf\n"; +const std::string PrintStatistics::TotalFilamentUsedG = "total filament used [g]"; +const std::string PrintStatistics::TotalFilamentUsedGMask = "; total filament used [g] ="; +const std::string PrintStatistics::TotalFilamentUsedGValueMask = "; total filament used [g] = %.2lf\n"; const std::string PrintStatistics::FilamentUsedCm3 = "filament used [cm3]"; -const std::string PrintStatistics::FilamentUsedCm3Mask = "; " + PrintStatistics::FilamentUsedCm3 + " ="; +const std::string PrintStatistics::FilamentUsedCm3Mask = "; filament used [cm3] ="; const std::string PrintStatistics::FilamentUsedMm = "filament used [mm]"; -const std::string PrintStatistics::FilamentUsedMmMask = "; " + PrintStatistics::FilamentUsedMm + " ="; +const std::string PrintStatistics::FilamentUsedMmMask = "; filament used [mm] ="; const std::string PrintStatistics::FilamentCost = "filament cost"; -const std::string PrintStatistics::FilamentCostMask = "; " + PrintStatistics::FilamentCost + " ="; +const std::string PrintStatistics::FilamentCostMask = "; filament cost ="; -const std::string PrintStatistics::TotalFilamentCost = "total " + PrintStatistics::FilamentCost; -const std::string PrintStatistics::TotalFilamentCostMask = "; " + PrintStatistics::TotalFilamentCost + " ="; -const std::string PrintStatistics::TotalFilamentCostValueMask = PrintStatistics::TotalFilamentCostMask + " %.2lf\n"; -#endif // ENABLE_BINARIZED_GCODE +const std::string PrintStatistics::TotalFilamentCost = "total filament cost"; +const std::string PrintStatistics::TotalFilamentCostMask = "; total filament cost ="; +const std::string PrintStatistics::TotalFilamentCostValueMask = "; total filament cost = %.2lf\n"; DynamicConfig PrintStatistics::config() const { diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index e45ff00f48..8e55044531 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -550,7 +550,6 @@ struct PrintStatistics printing_extruders.clear(); } -#if ENABLE_BINARIZED_GCODE static const std::string FilamentUsedG; static const std::string FilamentUsedGMask; static const std::string TotalFilamentUsedG; @@ -565,7 +564,6 @@ struct PrintStatistics static const std::string TotalFilamentCost; static const std::string TotalFilamentCostMask; static const std::string TotalFilamentCostValueMask; -#endif // ENABLE_BINARIZED_GCODE }; using PrintObjectPtrs = std::vector; diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index f9f6d4d8d9..d988070ca9 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -1504,13 +1504,11 @@ void PrintConfigDef::init_fff_params() def->mode = comExpert; def->set_default_value(new ConfigOptionStrings()); -#if ENABLE_BINARIZED_GCODE def = this->add("gcode_binary", coBool); def->label = L("Export as binary G-code"); - def->tooltip = L("Exports the G-code in binary format."); + def->tooltip = L("Exports G-code in binary format."); def->mode = comExpert; def->set_default_value(new ConfigOptionBool(0)); -#endif // ENABLE_BINARIZED_GCODE def = this->add("high_current_on_filament_swap", coBool); def->label = L("High extruder current on filament swap"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 8dcc1b92ad..bc731d5492 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -736,9 +736,7 @@ PRINT_CONFIG_CLASS_DEFINE( // i - case insensitive // w - whole word ((ConfigOptionStrings, gcode_substitutions)) -//#if ENABLE_BINARIZED_GCODE ((ConfigOptionBool, gcode_binary)) -//#endif // ENABLE_BINARIZED_GCODE ((ConfigOptionString, layer_gcode)) ((ConfigOptionFloat, max_print_speed)) ((ConfigOptionFloat, max_volumetric_speed)) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 8e52fbadf6..0196066b33 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -58,13 +58,7 @@ // Enable OpenGL debug messages using debug context #define ENABLE_OPENGL_DEBUG_OPTION (1 && ENABLE_GL_CORE_PROFILE) -//==================== -// 2.6.2.alpha1 techs -//==================== -#define ENABLE_2_6_2_ALPHA1 1 - -// Enable export of binarized gcode -#define ENABLE_BINARIZED_GCODE (1 && ENABLE_2_6_2_ALPHA1) -#define ENABLE_BINARIZED_GCODE_DEBUG_WINDOW (1 && ENABLE_BINARIZED_GCODE) +// Enable imgui dialog which allows to set the parameters used to export binarized gcode +#define ENABLE_BINARIZED_GCODE_DEBUG_WINDOW 1 #endif // _prusaslicer_technologies_h_ diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 20514bd312..a56ae818ec 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -368,38 +368,13 @@ void GCodeViewer::SequentialView::Marker::render() ImGui::PopStyleVar(); } -#if ENABLE_BINARIZED_GCODE void GCodeViewer::SequentialView::GCodeWindow::load_gcode(const GCodeProcessorResult& gcode_result) { m_filename = gcode_result.filename; m_is_binary_file = gcode_result.is_binary_file; m_lines_ends = gcode_result.lines_ends; } -#else -void GCodeViewer::SequentialView::GCodeWindow::load_gcode(const std::string& filename, const std::vector& lines_ends) -{ - assert(!m_file.is_open()); - if (m_file.is_open()) - return; - m_filename = filename; - m_lines_ends = lines_ends; - m_selected_line_id = 0; - m_last_lines_size = 0; - - try - { - m_file.open(boost::filesystem::path(m_filename)); - } - catch (...) - { - BOOST_LOG_TRIVIAL(error) << "Unable to map file " << m_filename << ". Cannot show G-code window."; - reset(); - } -} -#endif // ENABLE_BINARIZED_GCODE - -#if ENABLE_BINARIZED_GCODE void GCodeViewer::SequentialView::GCodeWindow::add_gcode_line_to_lines_cache(const std::string& src) { std::string command; @@ -681,175 +656,6 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, s imgui.set_requires_extra_frame(); } } -#else -void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, uint64_t curr_line_id) const -{ - auto update_lines = [this](uint64_t start_id, uint64_t end_id) { - std::vector ret; - ret.reserve(end_id - start_id + 1); - for (uint64_t id = start_id; id <= end_id; ++id) { - // read line from file - const size_t start = id == 1 ? 0 : m_lines_ends[id - 2]; - const size_t len = m_lines_ends[id - 1] - start; - std::string gline(m_file.data() + start, len); - - std::string command; - std::string parameters; - std::string comment; - - // extract comment - std::vector tokens; - boost::split(tokens, gline, boost::is_any_of(";"), boost::token_compress_on); - command = tokens.front(); - if (tokens.size() > 1) - comment = ";" + tokens.back(); - - // extract gcode command and parameters - if (!command.empty()) { - boost::split(tokens, command, boost::is_any_of(" "), boost::token_compress_on); - command = tokens.front(); - if (tokens.size() > 1) { - for (size_t i = 1; i < tokens.size(); ++i) { - parameters += " " + tokens[i]; - } - } - } - ret.push_back({ command, parameters, comment }); - } - return ret; - }; - - static const ImVec4 LINE_NUMBER_COLOR = ImGuiWrapper::COL_ORANGE_LIGHT; - static const ImVec4 SELECTION_RECT_COLOR = ImGuiWrapper::COL_ORANGE_DARK; - static const ImVec4 COMMAND_COLOR = { 0.8f, 0.8f, 0.0f, 1.0f }; - static const ImVec4 PARAMETERS_COLOR = { 1.0f, 1.0f, 1.0f, 1.0f }; - static const ImVec4 COMMENT_COLOR = { 0.7f, 0.7f, 0.7f, 1.0f }; - static const ImVec4 ELLIPSIS_COLOR = { 0.0f, 0.7f, 0.0f, 1.0f }; - - if (!m_visible || m_filename.empty() || m_lines_ends.empty() || curr_line_id == 0) - return; - - // window height - const float wnd_height = bottom - top; - - // number of visible lines - const float text_height = ImGui::CalcTextSize("0").y; - const ImGuiStyle& style = ImGui::GetStyle(); - const uint64_t lines_count = static_cast((wnd_height - 2.0f * style.WindowPadding.y + style.ItemSpacing.y) / (text_height + style.ItemSpacing.y)); - - if (lines_count == 0) - return; - - // visible range - const uint64_t half_lines_count = lines_count / 2; - uint64_t start_id = (curr_line_id >= half_lines_count) ? curr_line_id - half_lines_count : 0; - uint64_t end_id = start_id + lines_count - 1; - if (end_id >= static_cast(m_lines_ends.size())) { - end_id = static_cast(m_lines_ends.size()) - 1; - start_id = end_id - lines_count + 1; - } - - // updates list of lines to show, if needed - if (m_selected_line_id != curr_line_id || m_last_lines_size != end_id - start_id + 1) { - try - { - *const_cast*>(&m_lines) = update_lines(start_id, end_id); - } - catch (...) - { - BOOST_LOG_TRIVIAL(error) << "Error while loading from file " << m_filename << ". Cannot show G-code window."; - return; - } - *const_cast(&m_selected_line_id) = curr_line_id; - *const_cast(&m_last_lines_size) = m_lines.size(); - } - - // line number's column width - const float id_width = ImGui::CalcTextSize(std::to_string(end_id).c_str()).x; - - ImGuiWrapper& imgui = *wxGetApp().imgui(); - - auto add_item_to_line = [&imgui](const std::string& txt, const ImVec4& color, float spacing, size_t& current_length) { - static const size_t LENGTH_THRESHOLD = 60; - - if (txt.empty()) - return false; - - std::string out_text = txt; - bool reduced = false; - if (current_length + out_text.length() > LENGTH_THRESHOLD) { - out_text = out_text.substr(0, LENGTH_THRESHOLD - current_length); - reduced = true; - } - - current_length += out_text.length(); - - ImGui::SameLine(0.0f, spacing); - ImGui::PushStyleColor(ImGuiCol_Text, color); - imgui.text(out_text); - ImGui::PopStyleColor(); - if (reduced) { - ImGui::SameLine(0.0f, 0.0f); - ImGui::PushStyleColor(ImGuiCol_Text, ELLIPSIS_COLOR); - imgui.text("..."); - ImGui::PopStyleColor(); - } - - return reduced; - }; - - imgui.set_next_window_pos(0.0f, top, ImGuiCond_Always, 0.0f, 0.0f); - imgui.set_next_window_size(0.0f, wnd_height, ImGuiCond_Always); - ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); - ImGui::SetNextWindowBgAlpha(0.6f); - imgui.begin(std::string("G-code"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove); - - // center the text in the window by pushing down the first line - const float f_lines_count = static_cast(lines_count); - ImGui::SetCursorPosY(0.5f * (wnd_height - f_lines_count * text_height - (f_lines_count - 1.0f) * style.ItemSpacing.y)); - - // render text lines - for (uint64_t id = start_id; id <= end_id; ++id) { - const Line& line = m_lines[id - start_id]; - - // rect around the current selected line - if (id == curr_line_id) { - const float pos_y = ImGui::GetCursorScreenPos().y; - const float half_ItemSpacing_y = 0.5f * style.ItemSpacing.y; - const float half_padding_x = 0.5f * style.WindowPadding.x; - ImGui::GetWindowDrawList()->AddRect({ half_padding_x, pos_y - half_ItemSpacing_y }, - { ImGui::GetCurrentWindow()->Size.x - half_padding_x, pos_y + text_height + half_ItemSpacing_y }, - ImGui::GetColorU32(SELECTION_RECT_COLOR)); - } - - const std::string id_str = std::to_string(id); - // spacer to right align text - ImGui::Dummy({ id_width - ImGui::CalcTextSize(id_str.c_str()).x, text_height }); - - size_t line_length = 0; - // render line number - bool stop_adding = add_item_to_line(id_str, LINE_NUMBER_COLOR, 0.0f, line_length); - if (!stop_adding && !line.command.empty()) - // render command - stop_adding = add_item_to_line(line.command, COMMAND_COLOR, -1.0f, line_length); - if (!stop_adding && !line.parameters.empty()) - // render parameters - stop_adding = add_item_to_line(line.parameters, PARAMETERS_COLOR, 0.0f, line_length); - if (!stop_adding && !line.comment.empty()) - // render comment - stop_adding = add_item_to_line(line.comment, COMMENT_COLOR, line.command.empty() ? -1.0f : 0.0f, line_length); - } - - imgui.end(); - ImGui::PopStyleVar(); -} - -void GCodeViewer::SequentialView::GCodeWindow::stop_mapping_file() -{ - if (m_file.is_open()) - m_file.close(); -} -#endif // ENABLE_BINARIZED_GCODE void GCodeViewer::SequentialView::render(float legend_height) { @@ -1029,11 +835,7 @@ void GCodeViewer::load(const GCodeProcessorResult& gcode_result, const Print& pr // release gpu memory, if used reset(); -#if ENABLE_BINARIZED_GCODE m_sequential_view.gcode_window.load_gcode(gcode_result); -#else - m_sequential_view.gcode_window.load_gcode(gcode_result.filename, gcode_result.lines_ends); -#endif // ENABLE_BINARIZED_GCODE if (wxGetApp().is_gcode_viewer()) m_custom_gcode_per_print_z = gcode_result.custom_gcode_per_print_z; @@ -2656,9 +2458,7 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool case EViewType::Temperature: { color = m_extrusions.ranges.temperature.get_color_at(path.temperature); break; } case EViewType::LayerTimeLinear: case EViewType::LayerTimeLogarithmic: { -#if ENABLE_BINARIZED_GCODE if (!m_layers_times.empty() && m_layers.size() == m_layers_times.front().size()) { -#endif // ENABLE_BINARIZED_GCODE const Path::Sub_Path& sub_path = path.sub_paths.front(); double z = static_cast(sub_path.first.position.z()); const std::vector& zs = m_layers.get_zs(); @@ -2673,9 +2473,7 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool } } } -#if ENABLE_BINARIZED_GCODE } -#endif // ENABLE_BINARIZED_GCODE break; } case EViewType::VolumetricRate: { color = m_extrusions.ranges.volumetric_rate.get_color_at(path.volumetric_rate); break; } @@ -3945,7 +3743,6 @@ void GCodeViewer::render_legend(float& legend_height) ImGui::PushStyleColor(ImGuiCol_FrameBg, { 0.1f, 0.1f, 0.1f, 0.8f }); ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, { 0.2f, 0.2f, 0.2f, 0.8f }); -#if ENABLE_BINARIZED_GCODE std::vector view_options; std::vector view_options_id; if (!m_layers_times.empty() && m_layers.size() == m_layers_times.front().size()) { @@ -3965,19 +3762,6 @@ void GCodeViewer::render_legend(float& legend_height) int view_type_id = (view_type_it == view_options_id.end()) ? 0 : std::distance(view_options_id.begin(), view_type_it); if (imgui.combo(std::string(), view_options, view_type_id, ImGuiComboFlags_HeightLargest, 0.0f, -1.0f)) view_type = view_options_id[view_type_id]; -#else - imgui.combo(std::string(), { _u8L("Feature type"), - _u8L("Height (mm)"), - _u8L("Width (mm)"), - _u8L("Speed (mm/s)"), - _u8L("Fan speed (%)"), - _u8L("Temperature (°C)"), - _u8L("Volumetric flow rate (mm³/s)"), - _u8L("Layer time (linear)"), - _u8L("Layer time (logarithmic)"), - _u8L("Tool"), - _u8L("Color Print") }, view_type, ImGuiComboFlags_HeightLargest, 0.0f, -1.0f); -#endif // ENABLE_BINARIZED_GCODE ImGui::PopStyleColor(2); if (old_view_type != view_type) { diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index c531622fe1..754daa8778 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -12,10 +12,6 @@ #include "libslic3r/GCode/GCodeProcessor.hpp" #include "GLModel.hpp" -#if !ENABLE_BINARIZED_GCODE -#include -#endif // !ENABLE_BINARIZED_GCODE - #include #include #include @@ -681,7 +677,6 @@ public: void render(); }; -#if ENABLE_BINARIZED_GCODE class GCodeWindow { struct Line @@ -728,43 +723,6 @@ public: private: void add_gcode_line_to_lines_cache(const std::string& src); }; -#else - class GCodeWindow - { - struct Line - { - std::string command; - std::string parameters; - std::string comment; - }; - bool m_visible{ true }; - uint64_t m_selected_line_id{ 0 }; - size_t m_last_lines_size{ 0 }; - std::string m_filename; - boost::iostreams::mapped_file_source m_file; - // map for accessing data in file by line number - std::vector m_lines_ends; - // current visible lines - std::vector m_lines; - - public: - GCodeWindow() = default; - ~GCodeWindow() { stop_mapping_file(); } - void load_gcode(const std::string& filename, const std::vector& lines_ends); - void reset() { - stop_mapping_file(); - m_lines_ends.clear(); - m_lines.clear(); - m_filename.clear(); - } - - void toggle_visibility() { m_visible = !m_visible; } - - void render(float top, float bottom, uint64_t curr_line_id) const; - - void stop_mapping_file(); - }; -#endif // ENABLE_BINARIZED_GCODE struct Endpoints { diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index e4e0f64305..6759bb4298 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1370,16 +1370,14 @@ void MainFrame::init_menubar_as_editor() []() {return true; }, this); append_submenu(fileMenu, export_menu, wxID_ANY, _L("&Export"), ""); -#if ENABLE_BINARIZED_GCODE wxMenu* convert_menu = new wxMenu(); append_menu_item(convert_menu, wxID_ANY, _L("Convert ascii G-code to &binary") + dots, _L("Convert a G-code file from ascii to binary format"), - [this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->convert_gcode_to_binary(); }, "convert_file", nullptr, - [this]() { return true; }, this); + [this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->convert_gcode_to_binary(); }, "convert_file", nullptr, + [this]() { return true; }, this); append_menu_item(convert_menu, wxID_ANY, _L("Convert binary G-code to &ascii") + dots, _L("Convert a G-code file from binary to ascii format"), - [this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->convert_gcode_to_ascii(); }, "convert_file", nullptr, - [this]() { return true; }, this); + [this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->convert_gcode_to_ascii(); }, "convert_file", nullptr, + [this]() { return true; }, this); append_submenu(fileMenu, convert_menu, wxID_ANY, _L("&Convert"), ""); -#endif // ENABLE_BINARIZED_GCODE append_menu_item(fileMenu, wxID_ANY, _L("Ejec&t SD Card / Flash Drive") + dots + "\tCtrl+T", _L("Eject SD card / Flash drive after the G-code was exported to it."), [this](wxCommandEvent&) { if (m_plater) m_plater->eject_drive(); }, "eject_sd", nullptr, @@ -1653,7 +1651,6 @@ void MainFrame::init_menubar_as_gcodeviewer() _L("Reload the plater from disk"), [this](wxCommandEvent&) { m_plater->reload_gcode_from_disk(); }, "", nullptr, [this]() { return !m_plater->get_last_loaded_gcode().empty(); }, this); #endif // __APPLE__ -#if ENABLE_BINARIZED_GCODE fileMenu->AppendSeparator(); append_menu_item(fileMenu, wxID_ANY, _L("Convert ascii G-code to &binary") + dots, _L("Convert a G-code file from ascii to binary format"), [this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->convert_gcode_to_binary(); }, "convert_file", nullptr, @@ -1661,7 +1658,6 @@ void MainFrame::init_menubar_as_gcodeviewer() append_menu_item(fileMenu, wxID_ANY, _L("Convert binary G-code to &ascii") + dots, _L("Convert a G-code file from binary to ascii format"), [this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->convert_gcode_to_ascii(); }, "convert_file", nullptr, [this]() { return true; }, this); -#endif // ENABLE_BINARIZED_GCODE fileMenu->AppendSeparator(); append_menu_item(fileMenu, wxID_ANY, _L("Export &Toolpaths as OBJ") + dots, _L("Export toolpaths as OBJ"), [this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->export_toolpaths_to_obj(); }, "export_plater", nullptr, diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 91337354f0..d77847115d 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -55,9 +55,7 @@ #include #endif -#if ENABLE_BINARIZED_GCODE #include -#endif // ENABLE_BINARIZED_GCODE #include "libslic3r/libslic3r.h" #include "libslic3r/Format/STL.hpp" @@ -5471,7 +5469,6 @@ void Plater::reload_gcode_from_disk() load_gcode(filename); } -#if ENABLE_BINARIZED_GCODE void Plater::convert_gcode_to_ascii() { // Ask user for a gcode file name. @@ -5514,7 +5511,7 @@ void Plater::convert_gcode_to_ascii() } } - MessageDialog msg_dlg(this, _L("Succesfully created gcode ascii file:\n") + output_file, _L("Convert gcode file to ascii format"), wxICON_ERROR | wxOK); + MessageDialog msg_dlg(this, _L("Succesfully created gcode ascii file \n") + output_file, _L("Convert gcode file to ascii format"), wxICON_ERROR | wxOK); msg_dlg.ShowModal(); } @@ -5561,10 +5558,9 @@ void Plater::convert_gcode_to_binary() } } - MessageDialog msg_dlg(this, _L("Succesfully created gcode binary file:\n") + output_file, _L("Convert gcode file to binary format"), wxICON_ERROR | wxOK); + MessageDialog msg_dlg(this, _L("Succesfully created gcode binary file \n") + output_file, _L("Convert gcode file to binary format"), wxICON_ERROR | wxOK); msg_dlg.ShowModal(); } -#endif // ENABLE_BINARIZED_GCODE void Plater::refresh_print() { diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index e1a9950cdf..2d42c02cb8 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -188,10 +188,8 @@ public: void load_gcode(); void load_gcode(const wxString& filename); void reload_gcode_from_disk(); -#if ENABLE_BINARIZED_GCODE void convert_gcode_to_ascii(); void convert_gcode_to_binary(); -#endif // ENABLE_BINARIZED_GCODE void refresh_print(); std::vector load_files(const std::vector& input_files, bool load_model = true, bool load_config = true, bool imperial_units = false); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index c1fe024ebb..e1ca34f33e 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1717,9 +1717,7 @@ void TabPrint::build() Option option = optgroup->get_option("output_filename_format"); option.opt.full_width = true; optgroup->append_single_option_line(option); -#if ENABLE_BINARIZED_GCODE optgroup->append_single_option_line("gcode_binary"); -#endif // ENABLE_BINARIZED_GCODE optgroup = page->new_optgroup(L("Other")); From a0dfb33b49792744972149ac7bd3150327f07c37 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 5 Sep 2023 14:06:49 +0200 Subject: [PATCH 107/136] Automatically change extension in output_filename_format when switching to/from binary gcode format in Print Settings --- src/slic3r/GUI/Tab.cpp | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index b46e68910f..fd6441c7ff 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1719,6 +1719,33 @@ void TabPrint::build() optgroup->append_single_option_line(option); optgroup->append_single_option_line("gcode_binary"); + optgroup->m_on_change = [this](const t_config_option_key& opt_key, boost::any value) + { + if (opt_key == "gcode_binary") { + const bool is_binary = m_config->opt_bool("gcode_binary"); + std::string output_filename_format = m_config->opt_string("output_filename_format"); + bool modified = false; + if (is_binary && boost::iends_with(output_filename_format, ".gcode")) { + output_filename_format = output_filename_format.substr(0, output_filename_format.length() - 5) + "bgcode"; + modified = true; + } + else if (!is_binary && boost::iends_with(output_filename_format, ".bgcode")) { + output_filename_format = output_filename_format.substr(0, output_filename_format.length() - 6) + "gcode"; + modified = true; + } + if (modified) { + DynamicPrintConfig new_conf = *m_config; + auto off_option = static_cast(m_config->option("output_filename_format")->clone()); + off_option->value = output_filename_format; + new_conf.set_key_value("output_filename_format", off_option); + load_config(new_conf); + } + } + + update_dirty(); + update(); + }; + optgroup = page->new_optgroup(L("Other")); create_line_with_widget(optgroup.get(), "gcode_substitutions", "g-code-substitutions_301694", [this](wxWindow* parent) { From c4c2c5dcf36fde4c9f6bf8357e74d5bbcf91bd47 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 5 Sep 2023 14:09:47 +0200 Subject: [PATCH 108/136] Updated GUI_App::load_gcode() to show also bgcode and bgc extensions in caption --- src/slic3r/GUI/GUI_App.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 3254b1d608..b1102af267 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -2025,7 +2025,7 @@ void GUI_App::load_gcode(wxWindow* parent, wxString& input_file) const { input_file.Clear(); wxFileDialog dialog(parent ? parent : GetTopWindow(), - _L("Choose one file (GCODE/.GCO/.G/.ngc/NGC):"), + _L("Choose one file (GCODE/GCO/G/BGCODE/BGC/NGC):"), app_config->get_last_dir(), "", file_wildcards(FT_GCODE), wxFD_OPEN | wxFD_FILE_MUST_EXIST); From d62ca6646c60c92ffea2168a663e9ed77dc21442 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 5 Sep 2023 14:21:37 +0200 Subject: [PATCH 109/136] Added .bgcode and .bgc extensions to function is_gcode_file() --- src/libslic3r/utils.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp index f61ef3f525..09fa98ca1f 100644 --- a/src/libslic3r/utils.cpp +++ b/src/libslic3r/utils.cpp @@ -798,8 +798,9 @@ bool is_idx_file(const boost::filesystem::directory_entry &dir_entry) bool is_gcode_file(const std::string &path) { - return boost::iends_with(path, ".gcode") || boost::iends_with(path, ".gco") || - boost::iends_with(path, ".g") || boost::iends_with(path, ".ngc"); + return boost::iends_with(path, ".gcode") || boost::iends_with(path, ".gco") || + boost::iends_with(path, ".g") || boost::iends_with(path, ".ngc") || + boost::iends_with(path, ".bgcode") || boost::iends_with(path, ".bgc"); } bool is_img_file(const std::string &path) From 1a06dc0d0535e7f3080bfbd48266d6db3ce249cb Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 5 Sep 2023 14:39:55 +0200 Subject: [PATCH 110/136] Added .bgcode and .bgc extensions to method MainFrame::load_config_file() --- src/slic3r/GUI/MainFrame.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 6759bb4298..f5c644d6de 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1914,8 +1914,8 @@ void MainFrame::load_config_file() return; wxFileDialog dlg(this, _L("Select configuration to load:"), !m_last_config.IsEmpty() ? get_dir_name(m_last_config) : wxGetApp().app_config->get_last_dir(), - "config.ini", "INI files (*.ini, *.gcode)|*.ini;*.INI;*.gcode;*.g", wxFD_OPEN | wxFD_FILE_MUST_EXIST); - wxString file; + "config.ini", "INI files (*.ini, *.gcode, *.bgcode)|*.ini;*.INI;*.gcode;*.g;*.bgcode;*.bgc", wxFD_OPEN | wxFD_FILE_MUST_EXIST); + wxString file; if (dlg.ShowModal() == wxID_OK) file = dlg.GetPath(); if (! file.IsEmpty() && this->load_config_file(file.ToUTF8().data())) { From bc5b24e3adcaa455136dbb2f20bb77d3442c7ef4 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 3 May 2023 12:18:19 +0200 Subject: [PATCH 111/136] ConfigWizard: Fix performance Call update_materials() only when it's reasonable + Improve update_materials(). Code refactoring to fill all variable during one loop trough filaments + TaskTimer: Extended output --- src/slic3r/GUI/ConfigWizard.cpp | 176 +++++++----------------- src/slic3r/GUI/ConfigWizard_private.hpp | 13 +- src/slic3r/GUI/GUI_Utils.cpp | 6 +- 3 files changed, 58 insertions(+), 137 deletions(-) diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index 1f194966c0..741ab7be00 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -786,6 +786,16 @@ PageMaterials::PageMaterials(ConfigWizard *parent, Materials *materials, wxStrin reload_presets(); set_compatible_printers_html_window(std::vector(), false); } + +void PageMaterials::check_and_update_presets(bool force_reload_presets /*= false*/) +{ + if (presets_loaded) + return; + wizard_p()->update_materials(materials->technology); + if (force_reload_presets) + reload_presets(); +} + void PageMaterials::on_paint() { } @@ -1299,10 +1309,7 @@ void PageMaterials::clear() void PageMaterials::on_activate() { - if (! presets_loaded) { - wizard_p()->update_materials(materials->technology); - reload_presets(); - } + check_and_update_presets(true); first_paint = true; } @@ -2507,7 +2514,7 @@ void ConfigWizard::priv::load_vendors() } } appconfig_new.set_section(section_name, section_new); - }; + } } void ConfigWizard::priv::add_page(ConfigWizardPage *page) @@ -2589,78 +2596,37 @@ void ConfigWizard::priv::set_run_reason(RunReason run_reason) void ConfigWizard::priv::update_materials(Technology technology) { + auto add_material = [](Materials& materials, PresetAliases& aliases, const Preset& preset, const Preset* printer = nullptr) { + if (!materials.containts(&preset)) { + materials.push(&preset); + if (!preset.alias.empty()) + aliases[preset.alias].emplace(&preset); + } + if (printer) { + materials.add_printer(printer); + materials.compatibility_counter[preset.alias]++; + } + }; + if ((any_fff_selected || custom_printer_in_bundle || custom_printer_selected) && (technology & T_FFF)) { filaments.clear(); aliases_fff.clear(); - // Iterate filaments in all bundles - for (const auto &pair : bundles) { - for (const auto &filament : pair.second.preset_bundle->filaments) { - // Check if filament is already added - if (filaments.containts(&filament)) - continue; + + for (const auto &[name, bundle] : bundles) { + for (const auto &filament : bundle.preset_bundle->filaments) { // Iterate printers in all bundles - for (const auto &printer : pair.second.preset_bundle->printers) { + for (const auto &printer : bundle.preset_bundle->printers) { if (!printer.is_visible || printer.printer_technology() != ptFFF) continue; // Filter out inapplicable printers - if (is_compatible_with_printer(PresetWithVendorProfile(filament, filament.vendor), PresetWithVendorProfile(printer, printer.vendor))) { - if (!filaments.containts(&filament)) { - filaments.push(&filament); - if (!filament.alias.empty()) - aliases_fff[filament.alias].insert(filament.name); - } - filaments.add_printer(&printer); - } + if (is_compatible_with_printer(PresetWithVendorProfile(filament, filament.vendor), PresetWithVendorProfile(printer, printer.vendor))) + add_material(filaments, aliases_fff, filament, &printer); } // template filament bundle has no printers - filament would be never added - if(pair.second.vendor_profile&& pair.second.vendor_profile->templates_profile && pair.second.preset_bundle->printers.begin() == pair.second.preset_bundle->printers.end()) - { - if (!filaments.containts(&filament)) { - filaments.push(&filament); - if (!filament.alias.empty()) - aliases_fff[filament.alias].insert(filament.name); - } - } + if(bundle.vendor_profile && bundle.vendor_profile->templates_profile && bundle.preset_bundle->printers.begin() == bundle.preset_bundle->printers.end()) + add_material(filaments, aliases_fff, filament); } } - // count compatible printers - for (const auto& preset : filaments.presets) { - // skip template filaments - if (preset->vendor && preset->vendor->templates_profile) - continue; - - const auto filter = [preset](const std::pair element) { - return preset->alias == element.first; - }; - if (std::find_if(filaments.compatibility_counter.begin(), filaments.compatibility_counter.end(), filter) != filaments.compatibility_counter.end()) { - continue; - } - // find all aliases (except templates) - std::vector idx_with_same_alias; - for (size_t i = 0; i < filaments.presets.size(); ++i) { - if (preset->alias == filaments.presets[i]->alias && ((filaments.presets[i]->vendor && !filaments.presets[i]->vendor->templates_profile) || !filaments.presets[i]->vendor)) - idx_with_same_alias.push_back(i); - } - // check compatibility with each printer - size_t counter = 0; - for (const auto& printer : filaments.printers) { - if (!(*printer).is_visible || (*printer).printer_technology() != ptFFF) - continue; - bool compatible = false; - // Test other materials with same alias - for (size_t i = 0; i < idx_with_same_alias.size() && !compatible; ++i) { - const Preset& prst = *(filaments.presets[idx_with_same_alias[i]]); - const Preset& prntr = *printer; - if (is_compatible_with_printer(PresetWithVendorProfile(prst, prst.vendor), PresetWithVendorProfile(prntr, prntr.vendor))) { - compatible = true; - break; - } - } - if (compatible) - counter++; - } - filaments.compatibility_counter.emplace_back(preset->alias, counter); - } } if (any_sla_selected && (technology & T_SLA)) { @@ -2668,62 +2634,20 @@ void ConfigWizard::priv::update_materials(Technology technology) aliases_sla.clear(); // Iterate SLA materials in all bundles - for (const auto &pair : bundles) { - for (const auto &material : pair.second.preset_bundle->sla_materials) { - // Check if material is already added - if (sla_materials.containts(&material)) - continue; + for (const auto& [name, bundle] : bundles) { + for (const auto &material : bundle.preset_bundle->sla_materials) { // Iterate printers in all bundles // For now, we only allow the profiles to be compatible with another profiles inside the same bundle. - for (const auto& printer : pair.second.preset_bundle->printers) { + for (const auto& printer : bundle.preset_bundle->printers) { if(!printer.is_visible || printer.printer_technology() != ptSLA) continue; // Filter out inapplicable printers - if (is_compatible_with_printer(PresetWithVendorProfile(material, nullptr), PresetWithVendorProfile(printer, nullptr))) { + if (is_compatible_with_printer(PresetWithVendorProfile(material, nullptr), PresetWithVendorProfile(printer, nullptr))) // Check if material is already added - if(!sla_materials.containts(&material)) { - sla_materials.push(&material); - if (!material.alias.empty()) - aliases_sla[material.alias].insert(material.name); - } - sla_materials.add_printer(&printer); - } + add_material(sla_materials, aliases_sla, material, &printer); } } } - // count compatible printers - for (const auto& preset : sla_materials.presets) { - - const auto filter = [preset](const std::pair element) { - return preset->alias == element.first; - }; - if (std::find_if(sla_materials.compatibility_counter.begin(), sla_materials.compatibility_counter.end(), filter) != sla_materials.compatibility_counter.end()) { - continue; - } - std::vector idx_with_same_alias; - for (size_t i = 0; i < sla_materials.presets.size(); ++i) { - if(preset->alias == sla_materials.presets[i]->alias) - idx_with_same_alias.push_back(i); - } - size_t counter = 0; - for (const auto& printer : sla_materials.printers) { - if (!(*printer).is_visible || (*printer).printer_technology() != ptSLA) - continue; - bool compatible = false; - // Test otrher materials with same alias - for (size_t i = 0; i < idx_with_same_alias.size() && !compatible; ++i) { - const Preset& prst = *(sla_materials.presets[idx_with_same_alias[i]]); - const Preset& prntr = *printer; - if (is_compatible_with_printer(PresetWithVendorProfile(prst, prst.vendor), PresetWithVendorProfile(prntr, prntr.vendor))) { - compatible = true; - break; - } - } - if (compatible) - counter++; - } - sla_materials.compatibility_counter.emplace_back(preset->alias, counter); - } } } @@ -2857,21 +2781,15 @@ bool ConfigWizard::priv::on_bnt_finish() index->go_to(page_downloader); return false; } - /* When Filaments or Sla Materials pages are activated, - * materials for this pages are automaticaly updated and presets are reloaded. - * - * But, if _Finish_ button was clicked without activation of those pages - * (for example, just some printers were added/deleted), - * than last changes wouldn't be updated for filaments/materials. - * SO, do that before close of Wizard - */ - update_materials(T_ANY); - if (any_fff_selected) - page_filaments->reload_presets(); - if (any_sla_selected) - page_sla_materials->reload_presets(); - // theres no need to check that filament is selected if we have only custom printer + /* If some printers were added/deleted, but related MaterialPage wasn't activated, + * than last changes wouldn't be updated for filaments/materials. + * SO, do that before check_and_install_missing_materials() + */ + page_filaments->check_and_update_presets(); + page_sla_materials->check_and_update_presets(); + + // there's no need to check that filament is selected if we have only custom printer if (custom_printer_selected && !any_fff_selected && !any_sla_selected) return true; // check, that there is selected at least one filament/material return check_and_install_missing_materials(T_ANY); @@ -3357,8 +3275,8 @@ void ConfigWizard::priv::update_presets_in_config(const std::string& section, co // add or delete presets had a same alias auto it = aliases.find(alias_key); if (it != aliases.end()) - for (const std::string& name : it->second) - update(section, name); + for (const Preset* preset : it->second) + update(section, preset->name); } bool ConfigWizard::priv::check_fff_selected() diff --git a/src/slic3r/GUI/ConfigWizard_private.hpp b/src/slic3r/GUI/ConfigWizard_private.hpp index ea4701cd67..ce43fd5e71 100644 --- a/src/slic3r/GUI/ConfigWizard_private.hpp +++ b/src/slic3r/GUI/ConfigWizard_private.hpp @@ -296,6 +296,7 @@ struct PageMaterials: ConfigWizardPage PageMaterials(ConfigWizard *parent, Materials *materials, wxString title, wxString shortname, wxString list1name); + void check_and_update_presets(bool force_reload_presets = false); void reload_presets(); void update_lists(int sel_type, int sel_vendor, int last_selected_printer = -1); void on_material_highlighted(int sel_material); @@ -321,8 +322,8 @@ struct Materials Technology technology; // use vector for the presets to purpose of save of presets sorting in the bundle std::vector presets; - // String is alias of material, size_t number of compatible counters - std::vector> compatibility_counter; + // String is alias of material, size_t number of compatible printers counters + std::map compatibility_counter; std::set types; std::set printers; @@ -579,7 +580,7 @@ wxDEFINE_EVENT(EVT_INDEX_PAGE, wxCommandEvent); // ConfigWizard private data -typedef std::map> PresetAliases; +typedef std::map> PresetAliases; struct ConfigWizard::priv { @@ -592,14 +593,14 @@ struct ConfigWizard::priv // PrinterPickers state. Materials filaments; // Holds available filament presets and their types & vendors Materials sla_materials; // Ditto for SLA materials - PresetAliases aliases_fff; // Map of aliase to preset names - PresetAliases aliases_sla; // Map of aliase to preset names + PresetAliases aliases_fff; // Map of alias to material presets + PresetAliases aliases_sla; // Map of alias to material presets std::unique_ptr custom_config; // Backing for custom printer definition bool any_fff_selected; // Used to decide whether to display Filaments page bool any_sla_selected; // Used to decide whether to display SLA Materials page bool custom_printer_selected { false }; // New custom printer is requested bool custom_printer_in_bundle { false }; // Older custom printer already exists when wizard starts - // Set to true if there are none FFF printers on the main FFF page. If true, only SLA printers are shown (not even custum printers) + // Set to true if there are none FFF printers on the main FFF page. If true, only SLA printers are shown (not even custom printers) bool only_sla_mode { false }; bool template_profile_selected { false }; // This bool has one purpose - to tell that template profile should be installed if its not (because it cannot be added to appconfig) diff --git a/src/slic3r/GUI/GUI_Utils.cpp b/src/slic3r/GUI/GUI_Utils.cpp index f409756f01..e9318f65e4 100644 --- a/src/slic3r/GUI/GUI_Utils.cpp +++ b/src/slic3r/GUI/GUI_Utils.cpp @@ -4,6 +4,7 @@ ///|/ #include "GUI_Utils.hpp" #include "GUI_App.hpp" +#include "format.hpp" #include #include @@ -304,8 +305,9 @@ TaskTimer::~TaskTimer() { std::chrono::milliseconds stop_timer = std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch()); - auto process_duration = std::chrono::milliseconds(stop_timer - start_timer).count(); - std::string out = (boost::format("\n!!! %1% duration = %2% ms \n\n") % task_name % process_duration).str(); + const auto process_duration_ms = std::chrono::milliseconds(stop_timer - start_timer).count(); + const auto process_duration_s = long long(0.001 * process_duration_ms); + std::string out = format("\n! \"%1%\" duration = %2% s (%3% ms) \n", task_name, process_duration_s, process_duration_ms); printf("%s", out.c_str()); #ifdef __WXMSW__ std::wstring stemp = std::wstring(out.begin(), out.end()); From 078af6d5dad116e67be7e1e21d88792f3e9ed42f Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 5 Sep 2023 14:54:04 +0200 Subject: [PATCH 112/136] Added .bgcode and .bgc extensions to method Plater::load_files() --- src/slic3r/GUI/Plater.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index d77847115d..f977833192 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -6069,7 +6069,7 @@ void ProjectDropDialog::on_dpi_changed(const wxRect& suggested_rect) bool Plater::load_files(const wxArrayString& filenames, bool delete_after_load/*=false*/) { const std::regex pattern_drop(".*[.](stl|obj|amf|3mf|prusa|step|stp|zip)", std::regex::icase); - const std::regex pattern_gcode_drop(".*[.](gcode|g)", std::regex::icase); + const std::regex pattern_gcode_drop(".*[.](gcode|g|bgcode|bgc)", std::regex::icase); std::vector paths; From 986e732b806d7a877617bdde38996e4d98f1e54c Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 5 Sep 2023 15:20:31 +0200 Subject: [PATCH 113/136] Methods Plater::convert_gcode_to_binary() and Plater::convert_gcode_to_ascii() modified to change only extension in output file --- src/slic3r/GUI/Plater.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index f977833192..c60e58d47f 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5487,7 +5487,12 @@ void Plater::convert_gcode_to_ascii() // Set out filename boost::filesystem::path path(into_u8(input_file)); - const std::string output_file = path.parent_path().string() + "/" + path.stem().string() + "_ascii" + path.extension().string(); + const std::string output_file = path.replace_extension("gcode").string(); + if (boost::filesystem::exists(output_file)) { + MessageDialog msg_dlg(this, GUI::format_wxstr(_L("File %1% already exists. Do you wish to overwrite it?"), output_file), _L("Notice"), wxYES_NO); + if (msg_dlg.ShowModal() != wxID_YES) + return; + } // Open destination file FilePtr out_file{ boost::nowide::fopen(output_file.c_str(), "wb") }; @@ -5533,7 +5538,12 @@ void Plater::convert_gcode_to_binary() // Set out filename boost::filesystem::path path(into_u8(input_file)); - const std::string output_file = path.parent_path().string() + "/" + path.stem().string() + "_binary" + path.extension().string(); + const std::string output_file = path.replace_extension("bgcode").string(); + if (boost::filesystem::exists(output_file)) { + MessageDialog msg_dlg(this, GUI::format_wxstr(_L("File %1% already exists. Do you wish to overwrite it?"), output_file), _L("Notice"), wxYES_NO); + if (msg_dlg.ShowModal() != wxID_YES) + return; + } // Open destination file FilePtr out_file{ boost::nowide::fopen(output_file.c_str(), "wb") }; From a111e637cd746d3809e08f8134e8d9c413bbed3b Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 5 Sep 2023 16:24:45 +0200 Subject: [PATCH 114/136] Follow-up https://github.com/Prusa-Development/PrusaSlicerPrivate/commit/bc5b24e3adcaa455136dbb2f20bb77d3442c7ef4 - fixed non_MSW builds --- src/slic3r/GUI/GUI_Utils.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/GUI_Utils.cpp b/src/slic3r/GUI/GUI_Utils.cpp index e9318f65e4..6f0079e3e5 100644 --- a/src/slic3r/GUI/GUI_Utils.cpp +++ b/src/slic3r/GUI/GUI_Utils.cpp @@ -305,8 +305,9 @@ TaskTimer::~TaskTimer() { std::chrono::milliseconds stop_timer = std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch()); - const auto process_duration_ms = std::chrono::milliseconds(stop_timer - start_timer).count(); - const auto process_duration_s = long long(0.001 * process_duration_ms); + const auto timer_delta = stop_timer - start_timer; + const auto process_duration_ms = std::chrono::milliseconds(timer_delta).count(); + const auto process_duration_s = std::chrono::duration_cast>(timer_delta).count(); std::string out = format("\n! \"%1%\" duration = %2% s (%3% ms) \n", task_name, process_duration_s, process_duration_ms); printf("%s", out.c_str()); #ifdef __WXMSW__ From c8263d1da18892bfd4cff002a12fb2138074ce30 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 1 Sep 2023 09:22:24 +0200 Subject: [PATCH 115/136] Fixed ooze prevention when 'other layers' temp is set to zero (#11194, SPE-1856) --- src/libslic3r/GCode.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 10e444146a..4262a4d8ec 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -165,7 +165,10 @@ namespace Slic3r { int OozePrevention::_get_temp(const GCodeGenerator &gcodegen) const { - return (gcodegen.layer() == nullptr || gcodegen.layer()->id() == 0) + // First layer temperature should be used when on the first layer (obviously) and when + // "other layers" is set to zero (which means it should not be used). + return (gcodegen.layer() == nullptr || gcodegen.layer()->id() == 0 + || gcodegen.config().temperature.get_at(gcodegen.writer().extruder()->id()) == 0) ? gcodegen.config().first_layer_temperature.get_at(gcodegen.writer().extruder()->id()) : gcodegen.config().temperature.get_at(gcodegen.writer().extruder()->id()); } From 3b57a124bdcc1551bfb8e7339c1e6a0bebe114c9 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 8 Sep 2023 09:10:17 +0200 Subject: [PATCH 116/136] Updated LibBGCode.cmake to use latest version of libbgcode --- deps/LibBGCode/LibBGCode.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deps/LibBGCode/LibBGCode.cmake b/deps/LibBGCode/LibBGCode.cmake index a203d2d72f..92003869a6 100644 --- a/deps/LibBGCode/LibBGCode.cmake +++ b/deps/LibBGCode/LibBGCode.cmake @@ -1,8 +1,8 @@ set(LibBGCode_SOURCE_DIR "" CACHE PATH "Optionally specify local LibBGCode source directory") set(_source_dir_line - URL https://github.com/prusa3d/libbgcode/archive/6b4e4f9fd1270f58f9202a674c7f8e7e00d9cfb0.zip - URL_HASH SHA256=1c613f8340657f87a3179711cb14de346c8ba3b160ac97b7b0d8bfa81d00b98a + URL https://github.com/prusa3d/libbgcode/archive/50bedae2ae0c7fc83dd350a8be99ddc8f1749005.zip + URL_HASH SHA256=3958c93a325d6d7ed1c97aabb37cc09a08f8e981e3a7917312d568071e462162 ) if (LibBGCode_SOURCE_DIR) From bc3a8b037ab42625ea0215e112152575215183c6 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 8 Sep 2023 09:10:40 +0200 Subject: [PATCH 117/136] GCodeProcessor::process_binary_file() modified to process File Metadata Block set as optional --- src/libslic3r/GCode/GCodeProcessor.cpp | 36 +++++++++++++++----------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 4c3e8f988c..6e7e8b8944 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -1161,29 +1161,35 @@ void GCodeProcessor::process_binary_file(const std::string& filename, std::funct throw Slic3r::RuntimeError(format("File %1% does not contain a valid binary gcode\nError: %2%", filename, std::string(translate_result(res)))); - // read file metadata block + // read file metadata block, if present BlockHeader block_header; std::vector cs_buffer(65536); res = read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); if (res != EResult::Success) throw Slic3r::RuntimeError(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); - if ((EBlockType)block_header.type != EBlockType::FileMetadata) + if ((EBlockType)block_header.type != EBlockType::FileMetadata && + (EBlockType)block_header.type != EBlockType::PrinterMetadata) throw Slic3r::RuntimeError(format("Unable to find file metadata block in file %1%", filename)); - FileMetadataBlock file_metadata_block; - res = file_metadata_block.read_data(*file.f, file_header, block_header); - if (res != EResult::Success) - throw Slic3r::RuntimeError(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); - auto producer_it = std::find_if(file_metadata_block.raw_data.begin(), file_metadata_block.raw_data.end(), - [](const std::pair& item) { return item.first == "Producer"; }); - if (producer_it != file_metadata_block.raw_data.end() && boost::starts_with(producer_it->second, std::string(SLIC3R_APP_NAME))) - m_producer = EProducer::PrusaSlicer; - else + if ((EBlockType)block_header.type == EBlockType::FileMetadata) { + FileMetadataBlock file_metadata_block; + res = file_metadata_block.read_data(*file.f, file_header, block_header); + if (res != EResult::Success) + throw Slic3r::RuntimeError(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); + auto producer_it = std::find_if(file_metadata_block.raw_data.begin(), file_metadata_block.raw_data.end(), + [](const std::pair& item) { return item.first == "Producer"; }); + if (producer_it != file_metadata_block.raw_data.end() && boost::starts_with(producer_it->second, std::string(SLIC3R_APP_NAME))) + m_producer = EProducer::PrusaSlicer; + else + m_producer = EProducer::Unknown; + res = read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); + if (res != EResult::Success) + throw Slic3r::RuntimeError(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); + } + else { m_producer = EProducer::Unknown; + } // read printer metadata block - res = read_next_block_header(*file.f, file_header, block_header, cs_buffer.data(), cs_buffer.size()); - if (res != EResult::Success) - throw Slic3r::RuntimeError(format("Error reading file %1%: %2%", filename, std::string(translate_result(res)))); if ((EBlockType)block_header.type != EBlockType::PrinterMetadata) throw Slic3r::RuntimeError(format("Unable to find printer metadata block in file %1%", filename)); PrinterMetadataBlock printer_metadata_block; @@ -3701,7 +3707,7 @@ void GCodeProcessor::post_process() const bgcode::core::EResult res = m_binarizer.initialize(*out.f, s_binarizer_config); if (res != bgcode::core::EResult::Success) - throw Slic3r::RuntimeError(std::string("Unable to initialize the gcode binarizer.")); + throw Slic3r::RuntimeError(format("Unable to initialize the gcode binarizer.\nError: %1%", bgcode::core::translate_result(res))); } auto time_in_minutes = [](float time_in_seconds) { From 3a00a50ee520c42425a8ece88e76318533a5ee44 Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Fri, 8 Sep 2023 09:23:16 +0200 Subject: [PATCH 118/136] Disabled tech ENABLE_BINARIZED_GCODE_DEBUG_WINDOW to hide imgui dialog to set binarized gcode parameters --- src/libslic3r/Technologies.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 0196066b33..6c295279ad 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -59,6 +59,6 @@ #define ENABLE_OPENGL_DEBUG_OPTION (1 && ENABLE_GL_CORE_PROFILE) // Enable imgui dialog which allows to set the parameters used to export binarized gcode -#define ENABLE_BINARIZED_GCODE_DEBUG_WINDOW 1 +#define ENABLE_BINARIZED_GCODE_DEBUG_WINDOW 0 #endif // _prusaslicer_technologies_h_ From 8cdda54d5aef0642ea1499758c736133c2affa85 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 8 Sep 2023 11:49:03 +0200 Subject: [PATCH 119/136] Fix for #11254 - Link from brim to a non-existent page on the Prusa Help. * For unsupported languages help link is redirected to the EN page. + "uk", "zh" and "ru" languages are removed from current_language_code_safe(). Because those languages aren't supported by Prusa Help. --- src/slic3r/GUI/GUI_App.cpp | 6 +++--- src/slic3r/GUI/OptionsGroup.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index b1102af267..c50bd80853 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -3015,9 +3015,9 @@ wxString GUI_App::current_language_code_safe() const { "ja", "ja_JP", }, { "ko", "ko_KR", }, { "pl", "pl_PL", }, - { "uk", "uk_UA", }, - { "zh", "zh_CN", }, - { "ru", "ru_RU", }, + //{ "uk", "uk_UA", }, + //{ "zh", "zh_CN", }, + //{ "ru", "ru_RU", }, }; wxString language_code = this->current_language_code().BeforeFirst('_'); auto it = mapping.find(language_code); diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index 818464bee4..8e80dfcd8b 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -1024,7 +1024,7 @@ wxString OptionsGroup::get_url(const std::string& path_end) if (path_end.empty()) return wxEmptyString; - wxString language = get_app_config()->get("translation_language"); + wxString language = wxGetApp().current_language_code_safe(); wxString lang_marker = language.IsEmpty() ? "en" : language.BeforeFirst('_'); return wxString("https://help.prusa3d.com/") + lang_marker + wxString("/article/" + path_end); From 29fbb1acd13eca9caf2581be4509e710d8b596bf Mon Sep 17 00:00:00 2001 From: Pascal de Bruijn Date: Wed, 14 Jun 2023 19:56:35 +0200 Subject: [PATCH 120/136] wxWidgets: CheckResizerFlags assert fix (PR #10811) ./src/common/sizer.cpp(2258): assert "CheckSizerFlags(!((flags) & (wxALIGN_CENTRE_VERTICAL)))" failed in DoInsert(): wxALIGN_CENTRE_VERTICAL will be ignored in this sizer: wxEXPAND overrides alignment flags in box sizers --- src/slic3r/GUI/Field.cpp | 2 +- src/slic3r/GUI/FirmwareDialog.cpp | 2 +- src/slic3r/GUI/Tab.cpp | 4 ++-- src/slic3r/GUI/WipeTowerDialog.cpp | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index ff2db39a91..d682d2faf3 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -1722,7 +1722,7 @@ void SliderCtrl::BUILD() m_textctrl->SetFont(Slic3r::GUI::wxGetApp().normal_font()); m_textctrl->SetBackgroundStyle(wxBG_STYLE_PAINT); - temp->Add(m_slider, 1, wxEXPAND | wxALIGN_CENTER_VERTICAL, 0); + temp->Add(m_slider, 1, wxEXPAND, 0); temp->Add(m_textctrl, 0, wxALIGN_CENTER_VERTICAL, 0); m_slider->Bind(wxEVT_SLIDER, ([this](wxCommandEvent e) { diff --git a/src/slic3r/GUI/FirmwareDialog.cpp b/src/slic3r/GUI/FirmwareDialog.cpp index c3828fe8f8..fa035abf66 100644 --- a/src/slic3r/GUI/FirmwareDialog.cpp +++ b/src/slic3r/GUI/FirmwareDialog.cpp @@ -845,7 +845,7 @@ FirmwareDialog::FirmwareDialog(wxWindow *parent) : grid->Add(port_sizer, 0, wxEXPAND); grid->Add(label_progress, 0, wxALIGN_CENTER_VERTICAL); - grid->Add(p->progressbar, 1, wxEXPAND | wxALIGN_CENTER_VERTICAL); + grid->Add(p->progressbar, 1, wxEXPAND); grid->Add(label_status, 0, wxALIGN_CENTER_VERTICAL); grid->Add(p->txt_status, 0, wxEXPAND); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index fd6441c7ff..ee7af794db 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -239,7 +239,7 @@ void Tab::create_preset_tab() const float scale_factor = em_unit(this)*0.1;// GetContentScaleFactor(); m_top_hsizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(m_top_hsizer, 0, wxEXPAND | wxBOTTOM | wxALIGN_CENTER_VERTICAL, 3); + sizer->Add(m_top_hsizer, 0, wxEXPAND | wxBOTTOM, 3); m_top_hsizer->Add(m_presets_choice, 0, wxLEFT | wxRIGHT | wxTOP | wxALIGN_CENTER_VERTICAL, 3); m_top_hsizer->AddSpacer(int(4*scale_factor)); @@ -265,7 +265,7 @@ void Tab::create_preset_tab() m_h_buttons_sizer->AddSpacer(int(8*scale_factor)); m_h_buttons_sizer->Add(m_btn_compare_preset, 0, wxALIGN_CENTER_VERTICAL); - m_top_hsizer->Add(m_h_buttons_sizer, 1, wxEXPAND | wxALIGN_CENTRE_VERTICAL); + m_top_hsizer->Add(m_h_buttons_sizer, 1, wxEXPAND); m_top_hsizer->AddSpacer(int(16*scale_factor)); // StretchSpacer has a strange behavior under OSX, so // There is used just additional sizer for m_mode_sizer with right alignment diff --git a/src/slic3r/GUI/WipeTowerDialog.cpp b/src/slic3r/GUI/WipeTowerDialog.cpp index a8324dab26..6671b68d8a 100644 --- a/src/slic3r/GUI/WipeTowerDialog.cpp +++ b/src/slic3r/GUI/WipeTowerDialog.cpp @@ -357,7 +357,7 @@ WipingPanel::WipingPanel(wxWindow* parent, const std::vector& matrix, con hsizer->AddSpacer(10); hsizer->Add(new wxStaticText(m_page_simple, wxID_ANY, wxString(_(L("Tool #"))) << i + 1 << ": "), 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); - gridsizer_simple->Add(hsizer, 1, wxEXPAND | wxALIGN_CENTER_VERTICAL); + gridsizer_simple->Add(hsizer, 1, wxEXPAND); gridsizer_simple->Add(m_old.back(),0); gridsizer_simple->Add(m_new.back(),0); } From bae74457f76a44fbb48cca8ca7f99cb43a07b3fc Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 1 Sep 2023 15:05:01 +0200 Subject: [PATCH 121/136] Follow-up a6dea252432eeb34153d5ab767b1a9d315926ee3 and ae8684a4a46bdaa8fb7dda03b38561ffe170cb47 - Code refactoring --- src/libslic3r/GCode/Thumbnails.cpp | 78 ++++++++++++++++------ src/libslic3r/GCode/Thumbnails.hpp | 9 +++ src/libslic3r/PrintConfig.cpp | 26 ++++++++ src/slic3r/GUI/Field.cpp | 89 ++++--------------------- src/slic3r/GUI/Field.hpp | 1 - src/slic3r/GUI/OptionsGroup.cpp | 5 +- src/slic3r/GUI/Plater.hpp | 2 - src/slic3r/GUI/Tab.cpp | 15 ----- src/slic3r/GUI/UnsavedChangesDialog.cpp | 5 +- 9 files changed, 107 insertions(+), 123 deletions(-) diff --git a/src/libslic3r/GCode/Thumbnails.cpp b/src/libslic3r/GCode/Thumbnails.cpp index 97f06eb059..9095f4e20d 100644 --- a/src/libslic3r/GCode/Thumbnails.cpp +++ b/src/libslic3r/GCode/Thumbnails.cpp @@ -9,6 +9,9 @@ #include #include +#include +#include + namespace Slic3r::GCodeThumbnails { using namespace std::literals; @@ -120,7 +123,57 @@ std::unique_ptr compress_thumbnail(const ThumbnailData &d } } -std::vector> make_thumbnail_list(const DynamicPrintConfig &config) +std::vector> make_and_check_thumbnail_list(const std::string& thumbnails_string, ThumbnailErrors& errors, std::string def_ext /*= "PNG"*/) +{ + if (thumbnails_string.empty()) + return {}; + + const auto& extentions = ConfigOptionEnum::get_enum_names(); + + std::istringstream is(thumbnails_string); + std::string point_str; + + // generate thumbnails data to process it + + std::vector> thumbnails_list; + while (std::getline(is, point_str, ',')) { + Vec2d point(Vec2d::Zero()); + GCodeThumbnailsFormat format; + std::istringstream iss(point_str); + std::string coord_str; + if (std::getline(iss, coord_str, 'x')) { + std::istringstream(coord_str) >> point(0); + if (std::getline(iss, coord_str, '/')) { + std::istringstream(coord_str) >> point(1); + + if (0 < point(0) && point(0) < 1000 && 0 < point(1) && point(1) < 1000) { + std::string ext_str; + std::getline(iss, ext_str, '/'); + + if (ext_str.empty()) + ext_str = def_ext; + else { + // check validity of extention + boost::to_upper(ext_str); + if (std::find(extentions.begin(), extentions.end(), ext_str) == extentions.end()) + errors = enum_bitmask(errors | ThumbnailError::InvalidExt); + } + format = ext_str == "JPG" ? GCodeThumbnailsFormat::JPG : + ext_str == "QOI" ? GCodeThumbnailsFormat::QOI : GCodeThumbnailsFormat::PNG; + thumbnails_list.emplace_back(std::make_pair(format, point)); + } + else + errors = enum_bitmask(errors | ThumbnailError::OutOfRange); + continue; + } + } + errors = enum_bitmask(errors | ThumbnailError::InvalidVal); + } + + return thumbnails_list; +} + +std::vector> make_thumbnail_list(const DynamicPrintConfig& config) { // ??? Unit tests or command line slicing may not define "thumbnails" or "thumbnails_format". // ??? If "thumbnails_format" is not defined, export to PNG. @@ -129,26 +182,9 @@ std::vector> make_thumbnail_list(const D std::vector> thumbnails_list; if (const auto thumbnails_value = config.option("thumbnails")) { - std::string str = thumbnails_value->value; - std::istringstream is(str); - std::string point_str; - while (std::getline(is, point_str, ',')) { - Vec2d point(Vec2d::Zero()); - GCodeThumbnailsFormat format; - std::istringstream iss(point_str); - std::string coord_str; - if (std::getline(iss, coord_str, 'x')) { - std::istringstream(coord_str) >> point(0); - if (std::getline(iss, coord_str, '/')) { - std::istringstream(coord_str) >> point(1); - std::string ext_str; - if (std::getline(iss, ext_str, '/')) - format = ext_str == "JPG" ? GCodeThumbnailsFormat::JPG : - ext_str == "QOI" ? GCodeThumbnailsFormat::QOI :GCodeThumbnailsFormat::PNG; - } - } - thumbnails_list.emplace_back(std::make_pair(format, point)); - } + ThumbnailErrors errors; + thumbnails_list = make_and_check_thumbnail_list(thumbnails_value->value, errors); + assert(errors == enum_bitmask()); } return thumbnails_list; diff --git a/src/libslic3r/GCode/Thumbnails.hpp b/src/libslic3r/GCode/Thumbnails.hpp index 4a22e84d13..b7b45850ff 100644 --- a/src/libslic3r/GCode/Thumbnails.hpp +++ b/src/libslic3r/GCode/Thumbnails.hpp @@ -17,6 +17,14 @@ #include +#include "../libslic3r/enum_bitmask.hpp" + +namespace Slic3r { + enum class ThumbnailError : int { InvalidVal, OutOfRange, InvalidExt }; + using ThumbnailErrors = enum_bitmask; + ENABLE_ENUM_BITMASK_OPERATORS(ThumbnailError); +} + namespace Slic3r::GCodeThumbnails { struct CompressedImageBuffer @@ -29,6 +37,7 @@ struct CompressedImageBuffer std::unique_ptr compress_thumbnail(const ThumbnailData &data, GCodeThumbnailsFormat format); +std::vector> make_and_check_thumbnail_list(const std::string& thumbnails_string, ThumbnailErrors& errors, std::string def_ext = "PNG"); std::vector> make_thumbnail_list(const DynamicPrintConfig &config); template diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index d988070ca9..0af7868222 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -24,6 +24,7 @@ #include "I18N.hpp" #include "SLA/SupportTree.hpp" +#include "GCode/Thumbnails.hpp" #include #include @@ -4371,6 +4372,31 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va // Don't convert single options here, implement such conversion in PrintConfigDef::handle_legacy() instead. void PrintConfigDef::handle_legacy_composite(DynamicPrintConfig &config) { + if (config.has("thumbnails")) { + std::string extention = "PNG"; + if (config.has("thumbnails_format")) { + if (const ConfigOptionDef* opt = config.def()->get("thumbnails_format")) { + auto label = opt->enum_def->enum_to_label(config.option("thumbnails_format")->getInt()); + if (label.has_value()) + extention = *label; + } + } + std::string thumbnails_str = config.opt_string("thumbnails"); + + ThumbnailErrors errors; + auto thumbnails_list = GCodeThumbnails::make_and_check_thumbnail_list(thumbnails_str, errors, extention); + assert(errors == enum_bitmask()); + + if (!thumbnails_list.empty()) { + const auto& extentions = ConfigOptionEnum::get_enum_names(); + thumbnails_str.clear(); + for (const auto& [ext, size] : thumbnails_list) + thumbnails_str += format("%1%x%2%/%3%, ", size.x(), size.y(), extentions[int(ext)]); + thumbnails_str.resize(thumbnails_str.length() - 2); + + config.set_key_value("thumbnails", new ConfigOptionString(thumbnails_str)); + } + } } const PrintConfigDef print_config_def; diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index d682d2faf3..a95b2e7b66 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -15,6 +15,7 @@ #include "libslic3r/PrintConfig.hpp" #include "libslic3r/enum_bitmask.hpp" +#include "libslic3r/GCode/Thumbnails.hpp" #include #include @@ -33,13 +34,7 @@ #define wxOSX false #endif -namespace Slic3r { - -enum class ThumbnailError : int { InvalidVal, OutOfRange, InvalidExt }; -using ThumbnailErrors = enum_bitmask; -ENABLE_ENUM_BITMASK_OPERATORS(ThumbnailError); - -namespace GUI { +namespace Slic3r :: GUI { wxString double_to_string(double const value, const int max_precision /*= 4*/) { @@ -71,82 +66,24 @@ wxString double_to_string(double const value, const int max_precision /*= 4*/) return s; } -bool is_valid_thumbnails_extention(wxString& ext) -{ - ext.UpperCase(); - static const std::vector extentions = { "PNG", "JPG", "QOI" }; - - return std::find(extentions.begin(), extentions.end(), ext) != extentions.end(); -} - ThumbnailErrors validate_thumbnails_string(wxString& str, const wxString& def_ext = "PNG") { - bool invalid_val, out_of_range_val, invalid_ext; - invalid_val = out_of_range_val = invalid_ext = false; - str.Replace(" ", wxEmptyString, true); + std::string input_string = into_u8(str); - if (!str.IsEmpty()) { + str.Clear(); + ThumbnailErrors errors; - std::vector> out_thumbnails; - - wxStringTokenizer thumbnails(str, ","); - while (thumbnails.HasMoreTokens()) { - wxString token = thumbnails.GetNextToken(); - double x, y; - wxStringTokenizer thumbnail(token, "x"); - if (thumbnail.HasMoreTokens()) { - wxString x_str = thumbnail.GetNextToken(); - if (x_str.ToDouble(&x) && thumbnail.HasMoreTokens()) { - wxStringTokenizer y_and_ext(thumbnail.GetNextToken(), "/"); - - wxString y_str = y_and_ext.GetNextToken(); - if (y_str.ToDouble(&y)) { - // thumbnail has no extension - if (0 < x && x < 1000 && 0 < y && y < 1000) { - wxString ext = y_and_ext.HasMoreTokens() ? y_and_ext.GetNextToken() : def_ext; - bool is_valid_ext = is_valid_thumbnails_extention(ext); - invalid_ext |= !is_valid_ext; - out_thumbnails.push_back({ Vec2d(x, y), into_u8(is_valid_ext ? ext : def_ext) }); - continue; - } - out_of_range_val |= true; - continue; - } - } - } - invalid_val |= true; - } - - str.Clear(); - for (const auto& [size, ext] : out_thumbnails) - str += format_wxstr("%1%x%2%/%3%, ", size.x(), size.y(), ext); - str.resize(str.Len()-2); + auto thumbnails_list = Slic3r::GCodeThumbnails::make_and_check_thumbnail_list(input_string, errors); + if (!thumbnails_list.empty()) { + const auto& extentions = ConfigOptionEnum::get_enum_names(); + for (const auto& [format, size] : thumbnails_list) + str += format_wxstr("%1%x%2%/%3%, ", size.x(), size.y(), extentions[int(format)]); + str.resize(str.Len() - 2); } - ThumbnailErrors errors = only_if(invalid_val, ThumbnailError::InvalidVal) | - only_if(invalid_ext, ThumbnailError::InvalidExt) | - only_if(out_of_range_val, ThumbnailError::OutOfRange); - return errors; } -wxString get_valid_thumbnails_string(const DynamicPrintConfig& config) -{ - // >>> ysFIXME - temporary code, till "thumbnails_format" options exists in config - wxString format = "PNG"; - if (const ConfigOptionDef* opt = config.def()->get("thumbnails_format")) - if (auto label = opt->enum_def->enum_to_label(config.option("thumbnails_format")->getInt()); - label.has_value()) - format = from_u8(*label); - // <<< - - wxString str = from_u8(config.opt_string("thumbnails")); - validate_thumbnails_string(str, format); - - return str; -} - - Field::~Field() { if (m_on_kill_focus) @@ -1765,5 +1702,5 @@ boost::any& SliderCtrl::get_value() } -} // GUI -} // Slic3r +} // Slic3r :: GUI + diff --git a/src/slic3r/GUI/Field.hpp b/src/slic3r/GUI/Field.hpp index 7e6437742e..bdc8199136 100644 --- a/src/slic3r/GUI/Field.hpp +++ b/src/slic3r/GUI/Field.hpp @@ -41,7 +41,6 @@ using t_change = std::function; wxString double_to_string(double const value, const int max_precision = 4); -wxString get_valid_thumbnails_string(const DynamicPrintConfig& config); class UndoValueUIManager { diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index 8e80dfcd8b..1e1d3c08bd 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -925,10 +925,7 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config } break; case coString: { - if (opt_key == "thumbnails") - ret = get_valid_thumbnails_string(config); - else - ret = from_u8(config.opt_string(opt_key)); + ret = from_u8(config.opt_string(opt_key)); break; } case coStrings: diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 2d42c02cb8..6ded1c4a2f 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -43,8 +43,6 @@ namespace Slic3r { class BuildVolume; class Model; class ModelObject; -enum class ModelObjectCutAttribute : int; -using ModelObjectCutAttributes = enum_bitmask; class ModelInstance; class Print; class SLAPrint; diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index ee7af794db..0aee803397 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -662,21 +662,6 @@ void Tab::update_changed_ui() if (tab->m_sys_extruders_count != tab->m_extruders_count) nonsys_options.emplace_back("extruders_count"); } - - // "thumbnails" can not containe a extentions in old config but are valid and use PNG extention by default - // So, check if "thumbnails" is really changed - // We will compare full strings for thumnails instead of exactly config values - { - auto check_thumbnails_option = [](std::vector& keys, const DynamicPrintConfig& config, const DynamicPrintConfig& config_new) { - if (auto it = std::find(keys.begin(), keys.end(), "thumbnails"); it != keys.end()) - if (get_valid_thumbnails_string(config) == get_valid_thumbnails_string(config_new)) - // if those strings are actually the same, erase them from the list of dirty oprions - keys.erase(it); - }; - check_thumbnails_option(dirty_options, m_presets->get_edited_preset().config, m_presets->get_selected_preset().config); - if (const Preset* parent_preset = m_presets->get_selected_preset_parent()) - check_thumbnails_option(nonsys_options, m_presets->get_edited_preset().config, parent_preset->config); - } } for (auto& it : m_options_list) diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index 956e13e4be..5ae26ea9e8 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -1160,11 +1160,8 @@ static wxString get_string_value(std::string opt_key, const DynamicPrintConfig& } return _L("Undef"); } - case coString: { - if (opt_key == "thumbnails") - return get_valid_thumbnails_string(config); + case coString: return from_u8(config.opt_string(opt_key)); - } case coStrings: { const ConfigOptionStrings* strings = config.opt(opt_key); if (strings) { From 649c1083576b6bf3e0038016e16fa9ecd691cfb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Mon, 11 Sep 2023 10:26:26 +0200 Subject: [PATCH 122/136] SPE-1865: Fixed crash caused by inserting empty ExtrusionEntityCollection. --- src/libslic3r/Fill/Fill.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index dae22234f7..4f070140e8 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -543,19 +543,19 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: flow_mm3_per_mm = new_flow.mm3_per_mm(); flow_width = new_flow.width(); } - // Save into layer. - ExtrusionEntityCollection* eec = nullptr; - auto fill_begin = uint32_t(layerm.fills().size()); - layerm.m_fills.entities.push_back(eec = new ExtrusionEntityCollection()); - // Only concentric fills are not sorted. - eec->no_sort = f->no_sort(); - if (params.use_arachne) { + auto fill_begin = uint32_t(layerm.fills().size()); + // Save into layer. + if (ExtrusionEntityCollection *eec = nullptr; params.use_arachne) { for (const ThickPolyline &thick_polyline : thick_polylines) { Flow new_flow = surface_fill.params.flow.with_spacing(float(f->spacing)); ExtrusionMultiPath multi_path = PerimeterGenerator::thick_polyline_to_multi_path(thick_polyline, surface_fill.params.extrusion_role, new_flow, scaled(0.05), float(SCALED_EPSILON)); // Append paths to collection. if (!multi_path.empty()) { + layerm.m_fills.entities.push_back(eec = new ExtrusionEntityCollection()); + // Only concentric fills are not sorted. + eec->no_sort = f->no_sort(); + if (multi_path.paths.front().first_point() == multi_path.paths.back().last_point()) eec->entities.emplace_back(new ExtrusionLoop(std::move(multi_path.paths))); else @@ -565,6 +565,10 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: thick_polylines.clear(); } else { + layerm.m_fills.entities.push_back(eec = new ExtrusionEntityCollection()); + // Only concentric fills are not sorted. + eec->no_sort = f->no_sort(); + extrusion_entities_append_paths( eec->entities, std::move(polylines), ExtrusionAttributes{ surface_fill.params.extrusion_role, From dd88e0c5df0adf41d2ba4be65d81b703aee16508 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 11 Sep 2023 11:10:58 +0200 Subject: [PATCH 123/136] Fixed some duplicate macro definition warnings --- src/PrusaSlicer.cpp | 8 ++++++-- src/PrusaSlicer_app_msvc.cpp | 10 ++++++---- src/libslic3r/Config.hpp | 2 +- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index 4b2aaed18d..f3441a8d62 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -2,8 +2,12 @@ // Why? #define _WIN32_WINNT 0x0502 // The standard Windows includes. - #define WIN32_LEAN_AND_MEAN - #define NOMINMAX + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif // WIN32_LEAN_AND_MEAN + #ifndef NOMINMAX + #define NOMINMAX + #endif // NOMINMAX #include #include #ifdef SLIC3R_GUI diff --git a/src/PrusaSlicer_app_msvc.cpp b/src/PrusaSlicer_app_msvc.cpp index 90bd2d89f2..06f15aa5ad 100644 --- a/src/PrusaSlicer_app_msvc.cpp +++ b/src/PrusaSlicer_app_msvc.cpp @@ -1,14 +1,16 @@ // Why? #define _WIN32_WINNT 0x0502 // The standard Windows includes. -#define WIN32_LEAN_AND_MEAN -#define NOMINMAX +#ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN +#endif // WIN32_LEAN_AND_MEAN +#ifndef NOMINMAX + #define NOMINMAX +#endif // NOMINMAX #include #include #include - - #ifdef SLIC3R_GUI extern "C" { diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index bc8f87ac5e..d3d9fe93e3 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -1989,7 +1989,7 @@ public: void set_enum_values(GUIType gui_type, const std::initializer_list il) { this->enum_def_new(); - assert(gui_type == GUIType::i_enum_open || gui_type == GUIType::f_enum_open || gui_type == GUIType::select_open); + assert(is_gui_type_enum_open(gui_type)); this->gui_type = gui_type; enum_def->set_values(il); } From ca5f6da08d38c75e0e00ea2604de9709b9caa8aa Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 11 Sep 2023 11:11:20 +0200 Subject: [PATCH 124/136] Fix of Extruder selection ignored when using support material without interface #11281 --- src/libslic3r/Support/SupportCommon.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/Support/SupportCommon.cpp b/src/libslic3r/Support/SupportCommon.cpp index f475204ade..64a6924a3c 100644 --- a/src/libslic3r/Support/SupportCommon.cpp +++ b/src/libslic3r/Support/SupportCommon.cpp @@ -1729,7 +1729,8 @@ void generate_support_toolpaths( // Filler and its parameters filler, float(density), // Extrusion parameters - ExtrusionRole::SupportMaterialInterface, interface_flow); + interface_as_base ? ExtrusionRole::SupportMaterial : ExtrusionRole::SupportMaterialInterface, + interface_flow); } }; const bool top_interfaces = config.support_material_interface_layers.value != 0; From b33b28c076bbc8ec25a271ed1120e82cb20d1ebc Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Tue, 12 Sep 2023 10:30:14 +0200 Subject: [PATCH 125/136] SPE-1868: Fixed GCodeViewer max bounding box calculation leading to wrong camera near z plane --- src/slic3r/GUI/GCodeViewer.hpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 754daa8778..c1e9ae874e 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -830,12 +830,16 @@ public: const BoundingBoxf3& get_paths_bounding_box() const { return m_paths_bounding_box; } const BoundingBoxf3& get_shells_bounding_box() const { return m_shells_bounding_box; } + const BoundingBoxf3& get_max_bounding_box() const { BoundingBoxf3& max_bounding_box = const_cast(m_max_bounding_box); if (!max_bounding_box.defined) { - max_bounding_box = m_shells_bounding_box; - max_bounding_box.merge(m_paths_bounding_box); - max_bounding_box.merge(m_paths_bounding_box.max + m_sequential_view.marker.get_bounding_box().size().z() * Vec3d::UnitZ()); + if (m_shells_bounding_box.defined) + max_bounding_box = m_shells_bounding_box; + if (m_paths_bounding_box.defined) { + max_bounding_box.merge(m_paths_bounding_box); + max_bounding_box.merge(m_paths_bounding_box.max + m_sequential_view.marker.get_bounding_box().size().z() * Vec3d::UnitZ()); + } } return m_max_bounding_box; } From 0cbbe96d08d45cfcb571636088120176b9612582 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 12 Sep 2023 13:44:43 +0200 Subject: [PATCH 126/136] Removed "input_filename" placeholder, it was not generated correctly since PrusaSlicer 2.0.0 and nobody complained. --- src/libslic3r/PrintBase.cpp | 6 +++--- src/libslic3r/PrintBase.hpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/PrintBase.cpp b/src/libslic3r/PrintBase.cpp index 63d1a27067..18d9833974 100644 --- a/src/libslic3r/PrintBase.cpp +++ b/src/libslic3r/PrintBase.cpp @@ -21,7 +21,7 @@ void PrintTryCancel::operator()() const size_t PrintStateBase::g_last_timestamp = 0; // Update "scale", "input_filename", "input_filename_base" placeholders from the current m_objects. -void PrintBase::update_object_placeholders(DynamicConfig &config, const std::string &default_ext) const +void PrintBase::update_object_placeholders(DynamicConfig &config, const std::string & /* default_output_ext */) const { // get the first input file name std::string input_file; @@ -54,7 +54,7 @@ void PrintBase::update_object_placeholders(DynamicConfig &config, const std::str // get basename with and without suffix const std::string input_filename = boost::filesystem::path(input_file).filename().string(); const std::string input_filename_base = input_filename.substr(0, input_filename.find_last_of(".")); - config.set_key_value("input_filename", new ConfigOptionString(input_filename_base + default_ext)); +// config.set_key_value("input_filename", new ConfigOptionString(input_filename_base + default_output_ext)); config.set_key_value("input_filename_base", new ConfigOptionString(input_filename_base)); } } @@ -70,7 +70,7 @@ std::string PrintBase::output_filename(const std::string &format, const std::str PlaceholderParser::update_timestamp(cfg); this->update_object_placeholders(cfg, default_ext); if (! filename_base.empty()) { - cfg.set_key_value("input_filename", new ConfigOptionString(filename_base + default_ext)); +// cfg.set_key_value("input_filename", new ConfigOptionString(filename_base + default_ext)); cfg.set_key_value("input_filename_base", new ConfigOptionString(filename_base)); } try { diff --git a/src/libslic3r/PrintBase.hpp b/src/libslic3r/PrintBase.hpp index 779c7674ab..4b4976ea43 100644 --- a/src/libslic3r/PrintBase.hpp +++ b/src/libslic3r/PrintBase.hpp @@ -552,7 +552,7 @@ protected: // To be called by this->output_filename() with the format string pulled from the configuration layer. std::string output_filename(const std::string &format, const std::string &default_ext, const std::string &filename_base, const DynamicConfig *config_override = nullptr) const; // Update "scale", "input_filename", "input_filename_base" placeholders from the current printable ModelObjects. - void update_object_placeholders(DynamicConfig &config, const std::string &default_ext) const; + void update_object_placeholders(DynamicConfig &config, const std::string &default_output_ext) const; Model m_model; DynamicPrintConfig m_full_print_config; From 8d9d349af9bd100e290bff72d0cc0bd4864ca17f Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 12 Sep 2023 16:03:44 +0200 Subject: [PATCH 127/136] Substitutions info: Fixed a preset name, containing non-ASCII characters, presented in info dialog --- src/slic3r/GUI/GUI.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index 2b5c12ba3f..a74fd7f3dd 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -344,7 +344,7 @@ void show_substitutions_info(const PresetsConfigSubstitutions& presets_config_su }; for (const PresetConfigSubstitutions& substitution : presets_config_substitutions) { - changes += "\n\n" + format_wxstr("%1% : %2%", preset_type_name(substitution.preset_type), bold_string(substitution.preset_name)); + changes += "\n\n" + format_wxstr("%1% : %2%", preset_type_name(substitution.preset_type), bold_string(from_u8(substitution.preset_name))); if (!substitution.preset_file.empty()) changes += format_wxstr(" (%1%)", substitution.preset_file); From 7dcad1a035e7362aa5006fb863ce7ad4c4166da5 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 13 Sep 2023 08:18:07 +0200 Subject: [PATCH 128/136] Follow-up to 0cbbe96: remove input_filename from unit tests --- t/gcode.t | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/t/gcode.t b/t/gcode.t index 902c40b834..7bf6b06535 100644 --- a/t/gcode.t +++ b/t/gcode.t @@ -1,4 +1,4 @@ -use Test::More tests => 23; +use Test::More tests => 22; use strict; use warnings; @@ -185,13 +185,14 @@ use Slic3r::Test; } } -{ - my $config = Slic3r::Config::new_from_defaults; - $config->set('start_gcode', 'START:[input_filename]'); - my $print = Slic3r::Test::init_print('20mm_cube', config => $config); - my $gcode = Slic3r::Test::gcode($print); - like $gcode, qr/START:20mm_cube/, '[input_filename] is also available in custom G-code'; -} +#{ +# [input_filename] placeholder was removed in 0cbbe96. +# my $config = Slic3r::Config::new_from_defaults; +# $config->set('start_gcode', 'START:[input_filename]'); +# my $print = Slic3r::Test::init_print('20mm_cube', config => $config); +# my $gcode = Slic3r::Test::gcode($print); +# like $gcode, qr/START:20mm_cube/, '[input_filename] is also available in custom G-code'; +#} # The current Spiral Vase slicing code removes the holes and all but the largest contours from each slice, # therefore the following test is no more valid. From b8bb7f271616df67dbfd969f4c61b6888a58a9c6 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 7 Sep 2023 16:58:37 +0200 Subject: [PATCH 129/136] Edit custom G-codes: Improvements * Prepared ConfigDefs for placeholders used in EditGCodeDialog. * Removed unused code and files * DEBUG mode only: Added check of placeholder's existence in custom_gcode_specific_placeholders and custom_gcode_specific_config_def during the custom G-code parsing. --- resources/custom_gcodes/before_layer_gcode | 4 - resources/custom_gcodes/end_filament_gcode | 5 - resources/custom_gcodes/end_gcode | 5 - resources/custom_gcodes/layer_gcode | 4 - resources/custom_gcodes/rw_slicing_state | 4 - resources/custom_gcodes/start_filament_gcode | 5 - resources/custom_gcodes/tcr_rotated_gcode | 3 - resources/custom_gcodes/toolchange_gcode | 7 - resources/custom_gcodes/universal | 39 --- src/libslic3r/Config.hpp | 3 + src/libslic3r/GCode.cpp | 28 +- src/libslic3r/GCode.hpp | 8 - src/libslic3r/Print.cpp | 40 --- src/libslic3r/PrintConfig.cpp | 316 +++++++++++++++++++ src/libslic3r/PrintConfig.hpp | 62 ++++ src/slic3r/GUI/EditGCodeDialog.cpp | 285 ++++------------- src/slic3r/GUI/EditGCodeDialog.hpp | 13 +- 17 files changed, 483 insertions(+), 348 deletions(-) delete mode 100644 resources/custom_gcodes/before_layer_gcode delete mode 100644 resources/custom_gcodes/end_filament_gcode delete mode 100644 resources/custom_gcodes/end_gcode delete mode 100644 resources/custom_gcodes/layer_gcode delete mode 100644 resources/custom_gcodes/rw_slicing_state delete mode 100644 resources/custom_gcodes/start_filament_gcode delete mode 100644 resources/custom_gcodes/tcr_rotated_gcode delete mode 100644 resources/custom_gcodes/toolchange_gcode delete mode 100644 resources/custom_gcodes/universal diff --git a/resources/custom_gcodes/before_layer_gcode b/resources/custom_gcodes/before_layer_gcode deleted file mode 100644 index 3e00992c4d..0000000000 --- a/resources/custom_gcodes/before_layer_gcode +++ /dev/null @@ -1,4 +0,0 @@ -# generated by PrusaSlicer 2.6.0-rc1 on 2023-06-15 at 13:29:08 UTC -layer_num = 2 -layer_z = 1 -max_layer_z = 1 diff --git a/resources/custom_gcodes/end_filament_gcode b/resources/custom_gcodes/end_filament_gcode deleted file mode 100644 index 6cd107ae83..0000000000 --- a/resources/custom_gcodes/end_filament_gcode +++ /dev/null @@ -1,5 +0,0 @@ -# generated by PrusaSlicer 2.6.0-rc1 on 2023-06-15 at 13:29:09 UTC -filament_extruder_id = 2 -layer_num = 2 -layer_z = 1 -max_layer_z = 1 diff --git a/resources/custom_gcodes/end_gcode b/resources/custom_gcodes/end_gcode deleted file mode 100644 index 6cd107ae83..0000000000 --- a/resources/custom_gcodes/end_gcode +++ /dev/null @@ -1,5 +0,0 @@ -# generated by PrusaSlicer 2.6.0-rc1 on 2023-06-15 at 13:29:09 UTC -filament_extruder_id = 2 -layer_num = 2 -layer_z = 1 -max_layer_z = 1 diff --git a/resources/custom_gcodes/layer_gcode b/resources/custom_gcodes/layer_gcode deleted file mode 100644 index 9bebff5d45..0000000000 --- a/resources/custom_gcodes/layer_gcode +++ /dev/null @@ -1,4 +0,0 @@ -# generated by PrusaSlicer 2.6.0-rc1 on 2023-06-15 at 13:29:09 UTC -layer_num = 2 -layer_z = 1 -max_layer_z = 1 diff --git a/resources/custom_gcodes/rw_slicing_state b/resources/custom_gcodes/rw_slicing_state deleted file mode 100644 index e269a084ab..0000000000 --- a/resources/custom_gcodes/rw_slicing_state +++ /dev/null @@ -1,4 +0,0 @@ -# generated by PrusaSlicer 2.6.0-rc1 on 2023-06-15 at 13:29:09 UTC -e_restart_extra = 16385 -e_retracted = 16385 -position = 16385 diff --git a/resources/custom_gcodes/start_filament_gcode b/resources/custom_gcodes/start_filament_gcode deleted file mode 100644 index 6cd107ae83..0000000000 --- a/resources/custom_gcodes/start_filament_gcode +++ /dev/null @@ -1,5 +0,0 @@ -# generated by PrusaSlicer 2.6.0-rc1 on 2023-06-15 at 13:29:09 UTC -filament_extruder_id = 2 -layer_num = 2 -layer_z = 1 -max_layer_z = 1 diff --git a/resources/custom_gcodes/tcr_rotated_gcode b/resources/custom_gcodes/tcr_rotated_gcode deleted file mode 100644 index b616714f57..0000000000 --- a/resources/custom_gcodes/tcr_rotated_gcode +++ /dev/null @@ -1,3 +0,0 @@ -# generated by PrusaSlicer 2.6.0-rc1 on 2023-06-14 at 11:40:58 UTC -deretraction_from_wipe_tower_generator = 3 -toolchange_gcode = 3 diff --git a/resources/custom_gcodes/toolchange_gcode b/resources/custom_gcodes/toolchange_gcode deleted file mode 100644 index a254e530b3..0000000000 --- a/resources/custom_gcodes/toolchange_gcode +++ /dev/null @@ -1,7 +0,0 @@ -# generated by PrusaSlicer 2.6.0-rc1 on 2023-06-14 at 11:40:58 UTC -layer_num = 2 -layer_z = 1 -max_layer_z = 1 -next_extruder = 2 -previous_extruder = 2 -toolchange_z = 1 diff --git a/resources/custom_gcodes/universal b/resources/custom_gcodes/universal deleted file mode 100644 index 511a9c9b59..0000000000 --- a/resources/custom_gcodes/universal +++ /dev/null @@ -1,39 +0,0 @@ -# generated by PrusaSlicer 2.6.0-rc1 on 2023-06-15 at 13:29:09 UTC -current_extruder = 2 -current_object_idx = 2 -day = 2 -extruded_volume = 16385 -extruded_volume_total = 1 -extruded_weight = 16385 -extruded_weight_total = 1 -filament_preset = 16387 -first_layer_print_convex_hull = 16390 -first_layer_print_max = 16385 -first_layer_print_min = 16385 -first_layer_print_size = 16385 -has_single_extruder_multi_material_priming = 8 -has_wipe_tower = 8 -hour = 2 -initial_extruder = 2 -initial_tool = 2 -input_filename = 3 -input_filename_base = 3 -is_extruder_used = 16392 -minute = 2 -month = 2 -num_extruders = 2 -num_instances = 2 -num_objects = 2 -physical_printer_preset = 3 -print_bed_max = 16385 -print_bed_min = 16385 -print_bed_size = 16385 -print_preset = 3 -printer_preset = 3 -scale = 16387 -second = 2 -timestamp = 3 -total_layer_count = 2 -total_toolchanges = 2 -year = 2 -zhop = 1 diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index d3d9fe93e3..b75ec61db3 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -1855,6 +1855,8 @@ public: // Create a default option to be inserted into a DynamicConfig. ConfigOption* create_default_option() const; + bool is_scalar() const { return (int(this->type) & int(coVectorType)) == 0; } + template ConfigOption* load_option_from_archive(Archive &archive) const { if (this->nullable) { switch (this->type) { @@ -2101,6 +2103,7 @@ public: out.push_back(kvp.first); return out; } + bool empty() { return options.empty(); } // Iterate through all of the CLI options and write them to a stream. std::ostream& print_cli_help( diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 4262a4d8ec..192d42bf43 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1625,10 +1625,30 @@ std::string GCodeGenerator::placeholder_parser_process( unsigned int current_extruder_id, const DynamicConfig *config_override) { -#if GET_CUSTOM_GCODE_PLACEHOLDERS - if (config_override && - g_code_placeholders_map.find(name) == g_code_placeholders_map.end()) - g_code_placeholders_map[name] = *config_override; +#ifndef NDEBUG // CHECK_CUSTOM_GCODE_PLACEHOLDERS + if (config_override) { + const auto& custom_gcode_placeholders = custom_gcode_specific_placeholders(); + + // 1-st check: custom G-code "name" have to be present in s_CustomGcodeSpecificOptions; + //if (custom_gcode_placeholders.count(name) > 0) { + // const auto& placeholders = custom_gcode_placeholders.at(name); + if (auto it = custom_gcode_placeholders.find(name); it != custom_gcode_placeholders.end()) { + const auto& placeholders = it->second; + + for (const std::string& key : config_override->keys()) { + // 2-nd check: "key" have to be present in s_CustomGcodeSpecificOptions for "name" custom G-code ; + if (std::find(placeholders.begin(), placeholders.end(), key) == placeholders.end()) + throw Slic3r::PlaceholderParserError(format("\"%s\" placeholder for \"%s\" custom G-code \n" + "needs to be added to s_CustomGcodeSpecificOptions", key.c_str(), name.c_str())); + // 3-rd check: "key" have to be present in CustomGcodeSpecificConfigDef for "key" placeholder; + if (!custom_gcode_specific_config_def.has(key)) + throw Slic3r::PlaceholderParserError(format("Definition of \"%s\" placeholder \n" + "needs to be added to CustomGcodeSpecificConfigDef", key.c_str())); + } + } + else + throw Slic3r::PlaceholderParserError(format("\"%s\" custom G-code needs to be added to s_CustomGcodeSpecificOptions", name.c_str())); + } #endif PlaceholderParserIntegration &ppi = m_placeholder_parser_integration; diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index d34f100bf8..ae1cc333b1 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -87,7 +87,6 @@ struct LayerResult { static LayerResult make_nop_layer_result() { return {"", std::numeric_limits::max(), false, false, true}; } }; -#define GET_CUSTOM_GCODE_PLACEHOLDERS 0 class GCodeGenerator { public: @@ -115,13 +114,6 @@ public: {} ~GCodeGenerator() = default; -#if GET_CUSTOM_GCODE_PLACEHOLDERS - std::map g_code_placeholders_map; - const std::map& get_g_code_placeholders_map() { return g_code_placeholders_map; } - const DynamicConfig& get_placeholder_parser_config() const { return m_placeholder_parser_integration.parser.config(); } - const DynamicConfig& get_placeholder_output_config() const { return m_placeholder_parser_integration.output_config; } -#endif - // throws std::runtime_exception on error, // throws CanceledException through print->throw_if_canceled(). void do_export(Print* print, const char* path, GCodeProcessorResult* result = nullptr, ThumbnailsGeneratorCallback thumbnail_cb = nullptr); diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 697caa392a..cbc9404e84 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1048,46 +1048,6 @@ std::string Print::export_gcode(const std::string& path_template, GCodeProcessor std::unique_ptr gcode(new GCodeGenerator); gcode->do_export(this, path.c_str(), result, thumbnail_cb); - -#if GET_CUSTOM_GCODE_PLACEHOLDERS - - const std::string dir = custom_gcodes_dir() + -#ifdef _WIN32 - "\\"; -#else - "/"; -#endif - - auto save_placeholders = [dir](const std::string& file_name, const DynamicConfig& config) { - try { - boost::nowide::ofstream c; - c.open(dir + file_name, std::ios::out | std::ios::trunc); - c << "# " << header_slic3r_generated() << std::endl; - auto keys = config.keys(); - for (const std::string& opt_key : keys) { - const std::string type = std::to_string(int(config.optptr(opt_key)->type())); - c << opt_key << " = " << type << std::endl; - } - c.close(); - } - catch (const std::ofstream::failure& err) { - throw RuntimeError(format("The %1% cannot be loaded:\n\tReason: %2%", file_name, err.what())); - } - }; - - // save specific placeholders - const auto& gcode_placeholders = gcode->get_g_code_placeholders_map(); - for (const auto& [gcode_name, config] : gcode_placeholders) - save_placeholders(gcode_name, config); - - // save universal placeholders - save_placeholders("universal", gcode->get_placeholder_parser_config()); - - // save placeholders for "rw_slicing_state" slicing state - save_placeholders("rw_slicing_state", gcode->get_placeholder_output_config()); - -#endif - if (m_conflict_result.has_value()) result->conflict_result = *m_conflict_result; diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index d988070ca9..806e66b19a 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -5013,6 +5013,322 @@ void DynamicPrintAndCLIConfig::handle_legacy(t_config_option_key &opt_key, std:: } } +// SlicingStatesConfigDefs + +ReadOnlySlicingStatesConfigDef::ReadOnlySlicingStatesConfigDef() +{ + ConfigOptionDef* def; + + def = this->add("zhop", coFloat); + def->label = L(""); + def->tooltip = L(""); +} + +ReadWriteSlicingStatesConfigDef::ReadWriteSlicingStatesConfigDef() +{ + ConfigOptionDef* def; + + def = this->add("position", coFloats); + def->label = L("Position"); + def->tooltip = L(""); + + def = this->add("e_retracted", coFloats); + def->label = L(""); + def->tooltip = L(""); + + def = this->add("e_restart_extra", coFloats); + def->label = L(""); + def->tooltip = L(""); + + def = this->add("e_position", coFloats); + def->label = L(""); + def->tooltip = L(""); +} + +OtherSlicingStatesConfigDef::OtherSlicingStatesConfigDef() +{ + ConfigOptionDef* def; + + def = this->add("current_extruder", coInt); + def->label = L("Current extruder"); + def->tooltip = L(""); + + def = this->add("current_object_idx", coInt); + def->label = L("Current object index"); + def->tooltip = L(""); + + def = this->add("has_single_extruder_multi_material_priming", coBool); + def->label = L("Has single extruder MM priming"); + def->tooltip = L(""); + + def = this->add("has_wipe_tower", coBool); + def->label = L("Has wipe tower"); + def->tooltip = L(""); + + def = this->add("initial_extruder", coInt); + def->label = L("Initial extruder"); + def->tooltip = L(""); + + def = this->add("initial_filament_type", coStrings); + def->label = L(""); + def->tooltip = L(""); + + def = this->add("initial_tool", coInt); + def->label = L("Initial tool"); + def->tooltip = L(""); + + def = this->add("is_extruder_used", coBools); + def->label = L(""); + def->tooltip = L(""); +} + +PrintStatisticsConfigDef::PrintStatisticsConfigDef() +{ + ConfigOptionDef* def; + + def = this->add("extruded_volume", coFloats); + def->label = L(""); + def->tooltip = L(""); + + def = this->add("normal_print_time", coString); + def->label = L(""); + def->tooltip = L(""); + + def = this->add("num_printing_extruders", coInt); + def->label = L(""); + def->tooltip = L(""); + + def = this->add("print_time", coString); + def->label = L(""); + def->tooltip = L(""); + + def = this->add("printing_filament_types", coString); + def->label = L(""); + def->tooltip = L(""); + + def = this->add("silent_print_time", coString); + def->label = L(""); + def->tooltip = L(""); + + def = this->add("total_cost", coFloat); + def->label = L(""); + def->tooltip = L(""); + + def = this->add("total_weight", coFloat); + def->label = L(""); + def->tooltip = L(""); + + def = this->add("total_wipe_tower_cost", coFloat); + def->label = L(""); + def->tooltip = L(""); + + def = this->add("total_wipe_tower_filament", coFloat); + def->label = L(""); + def->tooltip = L(""); + + def = this->add("used_filament", coFloat); + def->label = L(""); + def->tooltip = L(""); + + def = this->add("total_toolchanges", coInt); + def->label = L("Total toolchanges"); + def->tooltip = L(""); + + def = this->add("extruded_volume_total", coFloat); + def->label = L(""); + def->tooltip = L(""); + + def = this->add("extruded_weight", coFloats); + def->label = L(""); + def->tooltip = L(""); + + def = this->add("extruded_weight_total", coFloat); + def->label = L(""); + def->tooltip = L(""); + + def = this->add("total_layer_count", coInt); + def->label = L("Total layer count"); + def->tooltip = L(""); +} + +ObjectsInfoConfigDef::ObjectsInfoConfigDef() +{ + ConfigOptionDef* def; + + def = this->add("num_objects", coInt); + def->label = L(""); + def->tooltip = L(""); + + def = this->add("num_instances", coInt); + def->label = L(""); + def->tooltip = L(""); + + def = this->add("scale", coStrings); + def->label = L(""); + def->tooltip = L(""); + + def = this->add("input_filename", coString); + def->label = L(""); + def->tooltip = L(""); + + def = this->add("input_filename_base", coString); + def->label = L(""); + def->tooltip = L(""); +} + +DimensionsConfigDef::DimensionsConfigDef() +{ + ConfigOptionDef* def; + + def = this->add("first_layer_print_convex_hull", coPoints); + def->label = L(""); + def->tooltip = L(""); + + def = this->add("first_layer_print_min", coFloats); + def->label = L(""); + def->tooltip = L(""); + + def = this->add("first_layer_print_max", coFloats); + def->label = L(""); + def->tooltip = L(""); + + def = this->add("first_layer_print_size", coFloats); + def->label = L(""); + def->tooltip = L(""); + + def = this->add("print_bed_min", coFloats); + def->label = L(""); + def->tooltip = L(""); + + def = this->add("print_bed_max", coFloats); + def->label = L(""); + def->tooltip = L(""); + + def = this->add("print_bed_size", coFloats); + def->label = L(""); + def->tooltip = L(""); +} + +TimestampsConfigDef::TimestampsConfigDef() +{ + ConfigOptionDef* def; + + def = this->add("timestamp", coString); + def->label = L(""); + def->tooltip = L(""); + + def = this->add("year", coInt); + def->label = L("Year"); + def->tooltip = L(""); + + def = this->add("month", coInt); + def->label = L("Month"); + def->tooltip = L(""); + + def = this->add("day", coInt); + def->label = L("Day"); + def->tooltip = L(""); + + def = this->add("hour", coInt); + def->label = L("Hour"); + def->tooltip = L(""); + + def = this->add("minute", coInt); + def->label = L("Minute"); + def->tooltip = L(""); + + def = this->add("second", coInt); + def->label = L("Second"); + def->tooltip = L(""); +} + +OtherPresetsConfigDef::OtherPresetsConfigDef() +{ + ConfigOptionDef* def; + + def = this->add("num_extruders", coInt); + def->label = L(""); + def->tooltip = L(""); + + def = this->add("print_preset", coString); + def->label = L(""); + def->tooltip = L(""); + + def = this->add("filament_preset", coString); + def->label = L(""); + def->tooltip = L(""); + + def = this->add("printer_preset", coString); + def->label = L(""); + def->tooltip = L(""); + + def = this->add("physical_printer_preset", coString); + def->label = L(""); + def->tooltip = L(""); +} + + +static std::map s_CustomGcodeSpecificPlaceholders{ + {"start_filament_gcode", {"layer_num", "layer_z", "max_layer_z", "filament_extruder_id"}}, + {"end_filament_gcode", {"layer_num", "layer_z", "max_layer_z", "filament_extruder_id"}}, + {"end_gcode", {"layer_num", "layer_z", "max_layer_z", "filament_extruder_id"}}, + {"before_layer_gcode", {"layer_num", "layer_z", "max_layer_z"}}, + {"layer_gcode", {"layer_num", "layer_z", "max_layer_z"}}, + {"toolchange_gcode", {"layer_num", "layer_z", "max_layer_z", "previous_extruder", "next_extruder", "toolchange_z"}}, + // some internal g_code ? + {"tcr_rotated_gcode", {"toolchange_gcode", "deretraction_from_wipe_tower_generator"}}, +}; + +const std::map& custom_gcode_specific_placeholders() +{ + return s_CustomGcodeSpecificPlaceholders; +} + +CustomGcodeSpecificConfigDef::CustomGcodeSpecificConfigDef() +{ + ConfigOptionDef* def; + + def = this->add("layer_num", coInt); + def->label = L(""); + def->tooltip = L(""); + + def = this->add("layer_z", coFloat); + def->label = L(""); + def->tooltip = L(""); + + def = this->add("max_layer_z", coFloat); + def->label = L(""); + def->tooltip = L(""); + + def = this->add("filament_extruder_id", coInt); + def->label = L(""); + def->tooltip = L(""); + + def = this->add("previous_extruder", coInt); + def->label = L(""); + def->tooltip = L(""); + + def = this->add("next_extruder", coInt); + def->label = L(""); + def->tooltip = L(""); + + def = this->add("toolchange_z", coFloat); + def->label = L(""); + def->tooltip = L(""); + + // I'm not sure if next options are really needed + + def = this->add("toolchange_gcode", coString); + def->label = L(""); + def->tooltip = L(""); + + def = this->add("deretraction_from_wipe_tower_generator", coString); + def->label = L(""); + def->tooltip = L(""); +} + +const CustomGcodeSpecificConfigDef custom_gcode_specific_config_def; + uint64_t ModelConfig::s_last_timestamp = 1; static Points to_points(const std::vector &dpts) diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index bc731d5492..390f91daf6 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -1190,6 +1190,68 @@ public: CLIMiscConfigDef(); }; +typedef std::string t_custom_gcode_key; +// This map containes list of specific placeholders for each custom G-code, if any exist +const std::map& custom_gcode_specific_placeholders(); + +// Next classes define placeholders used by GUI::EditGCodeDialog. + +class ReadOnlySlicingStatesConfigDef : public ConfigDef +{ +public: + ReadOnlySlicingStatesConfigDef(); +}; + +class ReadWriteSlicingStatesConfigDef : public ConfigDef +{ +public: + ReadWriteSlicingStatesConfigDef(); +}; + +class OtherSlicingStatesConfigDef : public ConfigDef +{ +public: + OtherSlicingStatesConfigDef(); +}; + +class PrintStatisticsConfigDef : public ConfigDef +{ +public: + PrintStatisticsConfigDef(); +}; + +class ObjectsInfoConfigDef : public ConfigDef +{ +public: + ObjectsInfoConfigDef(); +}; + +class DimensionsConfigDef : public ConfigDef +{ +public: + DimensionsConfigDef(); +}; + +class TimestampsConfigDef : public ConfigDef +{ +public: + TimestampsConfigDef(); +}; + +class OtherPresetsConfigDef : public ConfigDef +{ +public: + OtherPresetsConfigDef(); +}; + +// This classes defines all custom G-code specific placeholders. +class CustomGcodeSpecificConfigDef : public ConfigDef +{ +public: + CustomGcodeSpecificConfigDef(); +}; +extern const CustomGcodeSpecificConfigDef custom_gcode_specific_config_def; + // This class defines the command line options representing actions. extern const CLIActionsConfigDef cli_actions_config_def; diff --git a/src/slic3r/GUI/EditGCodeDialog.cpp b/src/slic3r/GUI/EditGCodeDialog.cpp index 028cf99d0e..413c524a49 100644 --- a/src/slic3r/GUI/EditGCodeDialog.cpp +++ b/src/slic3r/GUI/EditGCodeDialog.cpp @@ -27,99 +27,6 @@ namespace Slic3r { namespace GUI { -ConfigOption* get_new_option(const ConfigOptionType type) -{ - switch (type) { - case coFloat: - return new ConfigOptionFloat(0.); - case coFloats: - return new ConfigOptionFloats({ 0. }); - case coInt: - return new ConfigOptionInt(0); - case coInts: - return new ConfigOptionInts({ 0 }); - case coString: - return new ConfigOptionString(""); - case coStrings: - return new ConfigOptionStrings({ ""}); - case coPercent: - return new ConfigOptionPercent(0); - case coPercents: - return new ConfigOptionPercents({ 0}); - case coFloatOrPercent: - return new ConfigOptionFloatOrPercent(); - case coFloatsOrPercents: - return new ConfigOptionFloatsOrPercents(); - case coPoint: - return new ConfigOptionPoint(Vec2d(100, 100)); - case coPoints: - return new ConfigOptionPoints({ Vec2d(100,100) }); - case coPoint3: - return new ConfigOptionPoint3(); - case coBool: - return new ConfigOptionBool(true); - case coBools: - return new ConfigOptionBools({ true }); - case coEnum: - return new ConfigOptionEnum(); - default: - return nullptr; - } -} - -namespace fs = boost::filesystem; -namespace pt = boost::property_tree; -static std::vector get_params_from_file(const std::string& file_name, DynamicConfig& out_config) -{ - const fs::path file_path = fs::path(custom_gcodes_dir() + -#ifdef _WIN32 - "\\" -#else - "/" -#endif - + file_name); - - if (!fs::exists(file_path)) - return {}; - - const std::string file = file_path.string(); - - // Load the preset file, apply preset values on top of defaults. - try { - DynamicConfig config; - - try { - pt::ptree tree; - boost::nowide::ifstream ifs(file); - pt::read_ini(ifs, tree); - for (const pt::ptree::value_type& v : tree) { - try { - t_config_option_key opt_key = v.first; - const std::string type_str = v.second.get_value(); - const ConfigOptionType type = ConfigOptionType(std::atoi(type_str.c_str())); - if (ConfigOption* opt = get_new_option(type)) - config.set_key_value(opt_key, std::move(opt)); - } - catch (UnknownOptionException& err) { - throw RuntimeError(format("Some option from %1% cannot be loaded:\n\tReason: %2%", file, err.what())); - } - } - } - catch (const ConfigurationError& e) { - throw ConfigurationError(format("Failed loading configuration file \"%1%\": \n\t%2%", file, e.what())); - } - - out_config += config; - return config.keys(); - } - catch (const std::ifstream::failure& err) { - throw RuntimeError(format("The %1% cannot be loaded:\n\tReason: %2%", file, err.what())); - } - catch (const std::runtime_error& err) { - throw RuntimeError(format("Failed loading the custom_gcode_placeholders file: \"%1%\"\n\tReason: %2%", file , err.what())); - } -} - //------------------------------------------ // EditGCodeDialog //------------------------------------------ @@ -195,148 +102,85 @@ std::string EditGCodeDialog::get_edited_gcode() const return into_u8(m_gcode_editor->GetValue()); } +static ParamType get_type(const std::string& opt_key, const ConfigOptionDef& opt_def) +{ + return opt_def.is_scalar() ? ParamType::Scalar : ParamType::Vector; +} + void EditGCodeDialog::init_params_list(const std::string& custom_gcode_name) { - const std::vector read_write_params = get_params_from_file("rw_slicing_state", m_read_write_config); - const std::vector universal_params = get_params_from_file("universal", m_universal_config); - const std::vector specific_params = get_params_from_file(custom_gcode_name, m_specific_config); - - m_print_statistics_config = PrintStatistics::placeholders(); - - auto get_type = [](const std::string& opt_key, const DynamicConfig& config) { - return config.optptr(opt_key)->is_scalar() ? ParamType::Scalar : ParamType::Vector; - }; + const auto& custom_gcode_placeholders = custom_gcode_specific_placeholders(); + const auto& specific_params = custom_gcode_placeholders.count(custom_gcode_name) > 0 ? + custom_gcode_placeholders.at(custom_gcode_name) : t_config_option_keys({}); // Add slicing states placeholders - std::set read_only_slicing_state_opts = { "zhop" }; - wxDataViewItem slicing_state = m_params_list->AppendGroup(_L("[Global] Slicing State"), "re_slice"); - if (!universal_params.empty()) { - wxDataViewItem read_only = m_params_list->AppendSubGroup(slicing_state, _L("Read Only"), "lock_closed"); - for (const auto& opt_key : read_only_slicing_state_opts) - m_params_list->AppendParam(read_only, get_type(opt_key, m_universal_config), opt_key); + if (!cgp_ro_slicing_states_config_def.empty()) { + wxDataViewItem read_only = m_params_list->AppendSubGroup(slicing_state, _L("Read Only"), "lock_closed"); + for (const auto& [opt_key, def]: cgp_ro_slicing_states_config_def.options) + m_params_list->AppendParam(read_only, get_type(opt_key, def), opt_key); } - if (!read_write_params.empty()) { + if (!cgp_rw_slicing_states_config_def.empty()) { wxDataViewItem read_write = m_params_list->AppendSubGroup(slicing_state, _L("Read Write"), "lock_open"); - for (const auto& opt_key : read_write_params) - m_params_list->AppendParam(read_write, get_type(opt_key, m_read_write_config), opt_key); + for (const auto& [opt_key, def] : cgp_rw_slicing_states_config_def.options) + m_params_list->AppendParam(read_write, get_type(opt_key, def), opt_key); } // add other universal params, which are related to slicing state - const std::set other_slicing_state_opts = { "initial_extruder" - , "initial_filament_type" - , "initial_tool" - , "current_extruder" - , "is_extruder_used" - , "current_object_idx" - , "has_single_extruder_multi_material_priming" - , "has_wipe_tower" }; - slicing_state = m_params_list->AppendGroup(_L("Slicing State"), "re_slice"); - for (const auto& opt_key : other_slicing_state_opts) { - if (m_print_statistics_config.has(opt_key)) - m_params_list->AppendParam(slicing_state, get_type(opt_key, m_print_statistics_config), opt_key); - else if(!universal_params.empty()) - m_params_list->AppendParam(slicing_state, get_type(opt_key, m_universal_config), opt_key); + if (!cgp_other_slicing_states_config_def.empty()) { + slicing_state = m_params_list->AppendGroup(_L("Slicing State"), "re_slice"); + for (const auto& [opt_key, def] : cgp_other_slicing_states_config_def.options) + m_params_list->AppendParam(slicing_state, get_type(opt_key, def), opt_key); } - const std::set other_print_statistics_opts = { "extruded_volume_total" - , "extruded_weight" - , "extruded_weight_total" - , "total_layer_count" }; - - const std::set other_presets_opts = { "filament_preset" - , "physical_printer_preset" - , "printer_preset" - , "print_preset" - , "num_extruders" }; - - const std::set objects_info_opts = { "num_instances" - , "num_objects" - , "scale" - , "input_filename" - , "input_filename_base" }; - - const std::set dimensions_opts = { "first_layer_print_convex_hull" - , "first_layer_print_max" - , "first_layer_print_min" - , "first_layer_print_size" - , "print_bed_max" - , "print_bed_min" - , "print_bed_size" }; - // Add universal placeholders - if (!universal_params.empty()) { -// wxDataViewItem group = m_params_list->AppendGroup(_L("Universal"), "equal"); - + { // Add print statistics subgroup - if (!m_print_statistics_config.empty()) { -// wxDataViewItem statistics = m_params_list->AppendSubGroup(group, _L("Print Statistics"), "info"); + if (!cgp_print_statistics_config_def.empty()) { wxDataViewItem statistics = m_params_list->AppendGroup(_L("Print Statistics"), "info"); - const std::vector statistics_params = m_print_statistics_config.keys(); - for (const auto& opt_key : statistics_params) - if (std::find(other_slicing_state_opts.begin(), other_slicing_state_opts.end(), opt_key) == other_slicing_state_opts.end()) - m_params_list->AppendParam(statistics, get_type(opt_key, m_print_statistics_config), opt_key); - // add other universal params, which are related to print statistics - if (!universal_params.empty()) - for (const auto& opt_key : other_print_statistics_opts) - m_params_list->AppendParam(statistics, get_type(opt_key, m_universal_config), opt_key); + for (const auto& [opt_key, def] : cgp_print_statistics_config_def.options) + m_params_list->AppendParam(statistics, get_type(opt_key, def), opt_key); } // Add objects info subgroup - if (!universal_params.empty()) { -// wxDataViewItem objects_info = m_params_list->AppendSubGroup(group, _L("Objects Info"), "advanced_plus"); + if (!cgp_objects_info_config_def.empty()) { wxDataViewItem objects_info = m_params_list->AppendGroup(_L("Objects Info"), "advanced_plus"); - for (const auto& opt_key : objects_info_opts) - m_params_list->AppendParam(objects_info, get_type(opt_key, m_universal_config), opt_key); + for (const auto& [opt_key, def] : cgp_objects_info_config_def.options) + m_params_list->AppendParam(objects_info, get_type(opt_key, def), opt_key); } - // Add objects info subgroup + // Add dimensions subgroup - if (!universal_params.empty()) { -// wxDataViewItem dimensions = m_params_list->AppendSubGroup(group, _L("Dimensions"), "measure"); + if (!cgp_dimensions_config_def.empty()) { wxDataViewItem dimensions = m_params_list->AppendGroup(_L("Dimensions"), "measure"); - for (const auto& opt_key : dimensions_opts) - m_params_list->AppendParam(dimensions, get_type(opt_key, m_universal_config), opt_key); + for (const auto& [opt_key, def] : cgp_dimensions_config_def.options) + m_params_list->AppendParam(dimensions, get_type(opt_key, def), opt_key); } // Add timestamp subgroup - PlaceholderParser parser; - parser.update_timestamp(); - const DynamicConfig& ts_config = parser.config(); -// wxDataViewItem time_stamp = ts_config.empty() ? group : m_params_list->AppendSubGroup(group, _L("Timestamps"), "time"); - wxDataViewItem time_stamp = m_params_list->AppendGroup(_L("Timestamps"), "time"); - - // Add un-grouped params - -// wxDataViewItem other = m_params_list->AppendSubGroup(group, _L("Other"), "add_gcode"); - wxDataViewItem other = m_params_list->AppendGroup(_L("Other"), "add_gcode"); - for (const auto& opt_key : universal_params) - if (std::find(read_only_slicing_state_opts.begin(), read_only_slicing_state_opts.end(), opt_key)== read_only_slicing_state_opts.end() && - std::find(other_slicing_state_opts.begin(), other_slicing_state_opts.end(), opt_key) == other_slicing_state_opts.end() && - std::find(other_print_statistics_opts.begin(), other_print_statistics_opts.end(), opt_key) == other_print_statistics_opts.end() && - std::find(other_presets_opts.begin(), other_presets_opts.end(), opt_key) == other_presets_opts.end() && - std::find(objects_info_opts.begin(), objects_info_opts.end(), opt_key) == objects_info_opts.end() && - std::find(dimensions_opts.begin(), dimensions_opts.end(), opt_key) == dimensions_opts.end() && - !m_print_statistics_config.has(opt_key)) { - m_params_list->AppendParam(ts_config.has(opt_key) ? time_stamp : other, get_type(opt_key, m_universal_config), opt_key); - } - m_params_list->CheckAndDeleteIfEmpty(other); + if (!cgp_timestamps_config_def.empty()) { + wxDataViewItem dimensions = m_params_list->AppendGroup(_L("Timestamps"), "time"); + for (const auto& [opt_key, def] : cgp_timestamps_config_def.options) + m_params_list->AppendParam(dimensions, get_type(opt_key, def), opt_key); + } } // Add specific placeholders if (!specific_params.empty()) { - wxDataViewItem group = m_params_list->AppendGroup(format_wxstr(_L("Specific for %1%"), custom_gcode_name), /*"not_equal"*/"add_gcode"); + wxDataViewItem group = m_params_list->AppendGroup(format_wxstr(_L("Specific for %1%"), custom_gcode_name), "add_gcode"); for (const auto& opt_key : specific_params) - m_params_list->AppendParam(group, get_type(opt_key, m_specific_config), opt_key); - + if (custom_gcode_specific_config_def.has(opt_key)) { + auto def = custom_gcode_specific_config_def.get(opt_key); + m_params_list->AppendParam(group, get_type(opt_key, *def), opt_key); + } m_params_list->Expand(group); } @@ -344,9 +188,9 @@ void EditGCodeDialog::init_params_list(const std::string& custom_gcode_name) wxDataViewItem presets = add_presets_placeholders(); // add other params which are related to presets - if (!universal_params.empty()) - for (const auto& opt_key : other_presets_opts) - m_params_list->AppendParam(presets, get_type(opt_key, m_universal_config), opt_key); + if (!cgp_other_presets_config_def.empty()) + for (const auto& [opt_key, def] : cgp_other_presets_config_def.options) + m_params_list->AppendParam(presets, get_type(opt_key, def), opt_key); } wxDataViewItem EditGCodeDialog::add_presets_placeholders() @@ -397,24 +241,33 @@ void EditGCodeDialog::bind_list_and_button() const std::string opt_key = m_params_list->GetSelectedParamKey(); if (!opt_key.empty()) { - const ConfigOptionDef* cod { nullptr }; - const ConfigOption* optptr { nullptr }; + const ConfigOptionDef* def { nullptr }; const auto& full_config = wxGetApp().preset_bundle->full_config(); - if (const ConfigDef* def = full_config.def(); def && def->has(opt_key)) { - cod = def->get(opt_key); - optptr = full_config.optptr(opt_key); + if (const ConfigDef* config_def = full_config.def(); config_def && config_def->has(opt_key)) { + def = config_def->get(opt_key); } else { - for (const DynamicConfig* config: { &m_read_write_config, &m_universal_config, &m_specific_config, &m_print_statistics_config }) { - optptr = config->optptr(opt_key); - if (optptr) + for (const ConfigDef* config: std::initializer_list { + &custom_gcode_specific_config_def, + &cgp_ro_slicing_states_config_def, + &cgp_rw_slicing_states_config_def, + &cgp_other_slicing_states_config_def, + &cgp_print_statistics_config_def, + &cgp_objects_info_config_def, + &cgp_dimensions_config_def, + &cgp_timestamps_config_def, + &cgp_other_presets_config_def + }) { + if (config->has(opt_key)) { + def = config->get(opt_key); break; + } } } - if (optptr) { - const ConfigOptionType scalar_type = optptr->is_scalar() ? optptr->type() : static_cast(optptr->type() - coVectorType); + if (def) { + const ConfigOptionType scalar_type = def->is_scalar() ? def->type : static_cast(def->type - coVectorType); wxString type_str = scalar_type == coNone ? "none" : scalar_type == coFloat ? "float" : scalar_type == coInt ? "integer" : @@ -424,16 +277,16 @@ void EditGCodeDialog::bind_list_and_button() scalar_type == coPoint ? "point" : scalar_type == coBool ? "bool" : scalar_type == coEnum ? "enum" : "undef"; - if (!optptr->is_scalar()) + if (!def->is_scalar()) type_str += "[]"; - label = (!cod || (cod->full_label.empty() && cod->label.empty()) ) ? format_wxstr("%1%\n(%2%)", opt_key, type_str) : - (!cod->full_label.empty() && !cod->label.empty() ) ? - format_wxstr("%1% > %2%\n(%3%)", _(cod->full_label), _(cod->label), type_str) : - format_wxstr("%1%\n(%2%)", cod->label.empty() ? _(cod->full_label) : _(cod->label), type_str); + label = (!def || (def->full_label.empty() && def->label.empty()) ) ? format_wxstr("%1%\n(%2%)", opt_key, type_str) : + (!def->full_label.empty() && !def->label.empty() ) ? + format_wxstr("%1% > %2%\n(%3%)", _(def->full_label), _(def->label), type_str) : + format_wxstr("%1%\n(%2%)", def->label.empty() ? _(def->full_label) : _(def->label), type_str); - if (cod) - description = _(cod->tooltip); + if (def) + description = _(def->tooltip); } else label = "Undef optptr"; diff --git a/src/slic3r/GUI/EditGCodeDialog.hpp b/src/slic3r/GUI/EditGCodeDialog.hpp index 48f14acac0..39757ab376 100644 --- a/src/slic3r/GUI/EditGCodeDialog.hpp +++ b/src/slic3r/GUI/EditGCodeDialog.hpp @@ -8,6 +8,7 @@ #include "GUI_Utils.hpp" #include "wxExtensions.hpp" #include "libslic3r/Preset.hpp" +#include "libslic3r/PrintConfig.hpp" class wxListBox; class wxTextCtrl; @@ -31,10 +32,14 @@ class EditGCodeDialog : public DPIDialog wxStaticText* m_param_label {nullptr}; wxStaticText* m_param_description {nullptr}; - DynamicConfig m_read_write_config; - DynamicConfig m_universal_config; - DynamicConfig m_specific_config; - DynamicConfig m_print_statistics_config; + ReadOnlySlicingStatesConfigDef cgp_ro_slicing_states_config_def; + ReadWriteSlicingStatesConfigDef cgp_rw_slicing_states_config_def; + OtherSlicingStatesConfigDef cgp_other_slicing_states_config_def; + PrintStatisticsConfigDef cgp_print_statistics_config_def; + ObjectsInfoConfigDef cgp_objects_info_config_def; + DimensionsConfigDef cgp_dimensions_config_def; + TimestampsConfigDef cgp_timestamps_config_def; + OtherPresetsConfigDef cgp_other_presets_config_def; public: EditGCodeDialog(wxWindow*parent, const std::string&key, const std::string&value); From c66929387e2d056faf0bcf820af82258f0ba53e8 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 12 Sep 2023 10:41:59 +0200 Subject: [PATCH 130/136] Custom G-code editor: removed internal parsing of wipe tower gcode --- src/libslic3r/Config.hpp | 2 +- src/libslic3r/GCode/WipeTower.cpp | 2 +- src/libslic3r/GCode/WipeTowerIntegration.cpp | 18 ++++++++---------- src/libslic3r/PrintConfig.cpp | 12 ------------ 4 files changed, 10 insertions(+), 24 deletions(-) diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index b75ec61db3..09d283a327 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -2103,7 +2103,7 @@ public: out.push_back(kvp.first); return out; } - bool empty() { return options.empty(); } + bool empty() const { return options.empty(); } // Iterate through all of the CLI options and write them to a stream. std::ostream& print_cli_help( diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index ca80dd3dc2..464d0fe755 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -1008,7 +1008,7 @@ void WipeTower::toolchange_Change( // This is where we want to place the custom gcodes. We will use placeholders for this. // These will be substituted by the actual gcodes when the gcode is generated. //writer.append("[end_filament_gcode]\n"); - writer.append("[toolchange_gcode]\n"); + writer.append("[toolchange_gcode_from_wipe_tower_generator]\n"); // Travel to where we assume we are. Custom toolchange or some special T code handling (parking extruder etc) // gcode could have left the extruder somewhere, we cannot just start extruding. We should also inform the diff --git a/src/libslic3r/GCode/WipeTowerIntegration.cpp b/src/libslic3r/GCode/WipeTowerIntegration.cpp index d8afefdf95..eb06ddd165 100644 --- a/src/libslic3r/GCode/WipeTowerIntegration.cpp +++ b/src/libslic3r/GCode/WipeTowerIntegration.cpp @@ -3,6 +3,8 @@ #include "../GCode.hpp" #include "../libslic3r.h" +#include "boost/algorithm/string/replace.hpp" + namespace Slic3r::GCode { static inline Point wipe_tower_point_to_object_point(GCodeGenerator &gcodegen, const Vec2f& wipe_tower_pt) @@ -81,19 +83,15 @@ std::string WipeTowerIntegration::append_tcr(GCodeGenerator &gcodegen, const Wip if (gcodegen.config().wipe_tower) deretraction_str = gcodegen.unretract(); } - - - + assert(toolchange_gcode_str.empty() || toolchange_gcode_str.back() == '\n'); + assert(deretraction_str.empty() || deretraction_str.back() == '\n'); // Insert the toolchange and deretraction gcode into the generated gcode. - DynamicConfig config; - config.set_key_value("toolchange_gcode", new ConfigOptionString(toolchange_gcode_str)); - config.set_key_value("deretraction_from_wipe_tower_generator", new ConfigOptionString(deretraction_str)); - std::string tcr_gcode, tcr_escaped_gcode = gcodegen.placeholder_parser_process("tcr_rotated_gcode", tcr_rotated_gcode, new_extruder_id, &config); - unescape_string_cstyle(tcr_escaped_gcode, tcr_gcode); + boost::replace_first(tcr_rotated_gcode, "[toolchange_gcode_from_wipe_tower_generator]", toolchange_gcode_str); + boost::replace_first(tcr_rotated_gcode, "[deretraction_from_wipe_tower_generator]", deretraction_str); + std::string tcr_gcode; + unescape_string_cstyle(tcr_rotated_gcode, tcr_gcode); gcode += tcr_gcode; - if (! toolchange_gcode_str.empty() && toolchange_gcode_str.back() != '\n') - toolchange_gcode_str += '\n'; // A phony move to the end position at the wipe tower. gcodegen.writer().travel_to_xy(end_pos.cast()); diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 806e66b19a..0246280e35 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -5275,8 +5275,6 @@ static std::map s_CustomGcodeSpecificP {"before_layer_gcode", {"layer_num", "layer_z", "max_layer_z"}}, {"layer_gcode", {"layer_num", "layer_z", "max_layer_z"}}, {"toolchange_gcode", {"layer_num", "layer_z", "max_layer_z", "previous_extruder", "next_extruder", "toolchange_z"}}, - // some internal g_code ? - {"tcr_rotated_gcode", {"toolchange_gcode", "deretraction_from_wipe_tower_generator"}}, }; const std::map& custom_gcode_specific_placeholders() @@ -5315,16 +5313,6 @@ CustomGcodeSpecificConfigDef::CustomGcodeSpecificConfigDef() def = this->add("toolchange_z", coFloat); def->label = L(""); def->tooltip = L(""); - - // I'm not sure if next options are really needed - - def = this->add("toolchange_gcode", coString); - def->label = L(""); - def->tooltip = L(""); - - def = this->add("deretraction_from_wipe_tower_generator", coString); - def->label = L(""); - def->tooltip = L(""); } const CustomGcodeSpecificConfigDef custom_gcode_specific_config_def; From 7efdbecaf43bba0dad939fe99fcae23077e7fe04 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 12 Sep 2023 13:55:04 +0200 Subject: [PATCH 131/136] Custom GCode editor: updated labels and tooltips --- src/libslic3r/PrintConfig.cpp | 199 +++++++++++++++++----------------- 1 file changed, 102 insertions(+), 97 deletions(-) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 0246280e35..832db5ecf0 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -5020,8 +5020,8 @@ ReadOnlySlicingStatesConfigDef::ReadOnlySlicingStatesConfigDef() ConfigOptionDef* def; def = this->add("zhop", coFloat); - def->label = L(""); - def->tooltip = L(""); + def->label = L("Current z-hop"); + def->tooltip = L("Contains z-hop present at the beginning of the custom G-code block."); } ReadWriteSlicingStatesConfigDef::ReadWriteSlicingStatesConfigDef() @@ -5030,19 +5030,21 @@ ReadWriteSlicingStatesConfigDef::ReadWriteSlicingStatesConfigDef() def = this->add("position", coFloats); def->label = L("Position"); - def->tooltip = L(""); + def->tooltip = L("Position of the extruder at the beginning of the custom G-code block. If the custom G-code travels somewhere else, " + "it should write to this variable so PrusaSlicer knows where it travels from when it gets control back."); def = this->add("e_retracted", coFloats); - def->label = L(""); - def->tooltip = L(""); + def->label = L("Retraction"); + def->tooltip = L("Retraction state at the beginning of the custom G-code block. If the custom G-code moves the extruder axis, " + "it should write to this variable so PrusaSlicer deretracts correctly when it gets control back."); def = this->add("e_restart_extra", coFloats); - def->label = L(""); - def->tooltip = L(""); + def->label = L("Extra deretraction"); + def->tooltip = L("Currently planned extra extruder priming after deretraction."); def = this->add("e_position", coFloats); - def->label = L(""); - def->tooltip = L(""); + def->label = L("Absolute E position"); + def->tooltip = L("Current position of the extruder axis. Only used with absolute extruder addressing."); } OtherSlicingStatesConfigDef::OtherSlicingStatesConfigDef() @@ -5051,35 +5053,35 @@ OtherSlicingStatesConfigDef::OtherSlicingStatesConfigDef() def = this->add("current_extruder", coInt); def->label = L("Current extruder"); - def->tooltip = L(""); + def->tooltip = L("Zero-based index of currently used extruder."); def = this->add("current_object_idx", coInt); def->label = L("Current object index"); - def->tooltip = L(""); + def->tooltip = L("Specific for sequential printing. Zero-based index of currently printed object."); def = this->add("has_single_extruder_multi_material_priming", coBool); def->label = L("Has single extruder MM priming"); - def->tooltip = L(""); + def->tooltip = L("Are the extra multi-material priming regions used in this print?"); def = this->add("has_wipe_tower", coBool); def->label = L("Has wipe tower"); - def->tooltip = L(""); + def->tooltip = L("Whether or not wipe tower is being generated in the print."); def = this->add("initial_extruder", coInt); def->label = L("Initial extruder"); - def->tooltip = L(""); + def->tooltip = L("Zero-based index of the first extruder used in the print. Same as initial_tool."); - def = this->add("initial_filament_type", coStrings); - def->label = L(""); - def->tooltip = L(""); + def = this->add("initial_filament_type", coString); + def->label = L("Initial filament type"); + def->tooltip = L("String containing filament type of the first used extruder."); def = this->add("initial_tool", coInt); def->label = L("Initial tool"); - def->tooltip = L(""); + def->tooltip = L("Zero-based index of the first extruder used in the print. Same as initial_extruder."); def = this->add("is_extruder_used", coBools); - def->label = L(""); - def->tooltip = L(""); + def->label = L("Is extruder used?"); + def->tooltip = L("Vector of bools stating whether a given extruder is used in the print."); } PrintStatisticsConfigDef::PrintStatisticsConfigDef() @@ -5087,68 +5089,68 @@ PrintStatisticsConfigDef::PrintStatisticsConfigDef() ConfigOptionDef* def; def = this->add("extruded_volume", coFloats); - def->label = L(""); - def->tooltip = L(""); + def->label = L("Volume per extruder"); + def->tooltip = L("Total filament volume extruded per extruder during the entire print."); def = this->add("normal_print_time", coString); - def->label = L(""); - def->tooltip = L(""); + def->label = L("Print time (normal mode)"); + def->tooltip = L("Estimated print time when printed in normal mode (i.e. not in silent mode). Same as print_time."); def = this->add("num_printing_extruders", coInt); - def->label = L(""); - def->tooltip = L(""); + def->label = L("Number of printing extruders"); + def->tooltip = L("Number of extruders used during the print."); def = this->add("print_time", coString); - def->label = L(""); - def->tooltip = L(""); + def->label = L("Print time (normal mode)"); + def->tooltip = L("Estimated print time when printed in normal mode (i.e. not in silent mode). Same as normal_print_time."); def = this->add("printing_filament_types", coString); - def->label = L(""); - def->tooltip = L(""); + def->label = L("Used filament types"); + def->tooltip = L("Comma-separated list of all filament types used during the print."); def = this->add("silent_print_time", coString); - def->label = L(""); - def->tooltip = L(""); + def->label = L("Print time (silent mode)"); + def->tooltip = L("Estimated print time when printed in silent mode."); def = this->add("total_cost", coFloat); - def->label = L(""); - def->tooltip = L(""); + def->label = L("Total cost"); + def->tooltip = L("Total cost of all material used in the print. Calculated from filament_cost value in Filament Settings."); def = this->add("total_weight", coFloat); - def->label = L(""); - def->tooltip = L(""); + def->label = L("Total weight"); + def->tooltip = L("Total weight of the print. Calculated from filament_density value in Filament Settings."); def = this->add("total_wipe_tower_cost", coFloat); - def->label = L(""); - def->tooltip = L(""); + def->label = L("Total wipe tower cost"); + def->tooltip = L("Total cost of the material wasted on the wipe tower. Calculated from filament_cost value in Filament Settings."); def = this->add("total_wipe_tower_filament", coFloat); - def->label = L(""); - def->tooltip = L(""); + def->label = L("Wipe tower volume"); + def->tooltip = L("Total filament volume extruded on the wipe tower."); def = this->add("used_filament", coFloat); - def->label = L(""); - def->tooltip = L(""); + def->label = L("Used filament"); + def->tooltip = L("Total length of filament used in the print."); def = this->add("total_toolchanges", coInt); def->label = L("Total toolchanges"); - def->tooltip = L(""); + def->tooltip = L("Number of toolchanges during the print."); def = this->add("extruded_volume_total", coFloat); - def->label = L(""); - def->tooltip = L(""); + def->label = L("Total volume"); + def->tooltip = L("Total volume of filament used during the entire print."); def = this->add("extruded_weight", coFloats); - def->label = L(""); - def->tooltip = L(""); + def->label = L("Weight per extruder"); + def->tooltip = L("Weight per extruder extruded during the entire print. Calculated from filament_density value in Filament Settings."); def = this->add("extruded_weight_total", coFloat); - def->label = L(""); - def->tooltip = L(""); + def->label = L("Total weight"); + def->tooltip = L("Total weight of the print. Calculated from filament_density value in Filament Settings."); def = this->add("total_layer_count", coInt); def->label = L("Total layer count"); - def->tooltip = L(""); + def->tooltip = L("Number of layers in the entire print."); } ObjectsInfoConfigDef::ObjectsInfoConfigDef() @@ -5156,57 +5158,59 @@ ObjectsInfoConfigDef::ObjectsInfoConfigDef() ConfigOptionDef* def; def = this->add("num_objects", coInt); - def->label = L(""); - def->tooltip = L(""); + def->label = L("Number of objects"); + def->tooltip = L("Total number of objects in the print."); def = this->add("num_instances", coInt); - def->label = L(""); - def->tooltip = L(""); + def->label = L("Number of instances"); + def->tooltip = L("Total number of object instances in the print, summed over all objects."); def = this->add("scale", coStrings); - def->label = L(""); - def->tooltip = L(""); - - def = this->add("input_filename", coString); - def->label = L(""); - def->tooltip = L(""); + def->label = L("Scale per object"); + def->tooltip = L("Contains a string with the information about what scaling was applied to the individual objects. " + "Indexing of the objects is zero-based (first object has index 0).\n" + "Example: 'x:100% y:50% z:100'."); def = this->add("input_filename_base", coString); - def->label = L(""); - def->tooltip = L(""); + def->label = L("Input filename without extension"); + def->tooltip = L("Source filename of the first object, without extension."); } DimensionsConfigDef::DimensionsConfigDef() { ConfigOptionDef* def; + const std::string point_tooltip = L("The vector has two elements: x and y coordinate of the point. Values in mm."); + const std::string bb_size_tooltip = L("The vector has two elements: x and y dimension of the bounding box. Values in mm."); + def = this->add("first_layer_print_convex_hull", coPoints); - def->label = L(""); - def->tooltip = L(""); + def->label = L("First layer convex hull"); + def->tooltip = L("Vector of points of the first layer convex hull. Each element has the following format:" + "'[x, y]' (x and y are floating-point numbers in mm)."); def = this->add("first_layer_print_min", coFloats); - def->label = L(""); - def->tooltip = L(""); + def->label = L("Bottom-left corner of first layer bounding box"); + def->tooltip = point_tooltip; def = this->add("first_layer_print_max", coFloats); - def->label = L(""); - def->tooltip = L(""); + def->label = L("Top-right corner of first layer bounding box"); + def->tooltip = point_tooltip; def = this->add("first_layer_print_size", coFloats); - def->label = L(""); - def->tooltip = L(""); + def->label = L("Size of the first layer bounding box"); + def->tooltip = bb_size_tooltip; def = this->add("print_bed_min", coFloats); - def->label = L(""); - def->tooltip = L(""); + def->label = L("Bottom-left corner of print bed bounding box"); + def->tooltip = point_tooltip; def = this->add("print_bed_max", coFloats); - def->label = L(""); - def->tooltip = L(""); + def->label = L("Top-right corner of print bed bounding box"); + def->tooltip = point_tooltip; def = this->add("print_bed_size", coFloats); - def->label = L(""); - def->tooltip = L(""); + def->label = L("Size of the print bed bounding box"); + def->tooltip = bb_size_tooltip; } TimestampsConfigDef::TimestampsConfigDef() @@ -5214,8 +5218,8 @@ TimestampsConfigDef::TimestampsConfigDef() ConfigOptionDef* def; def = this->add("timestamp", coString); - def->label = L(""); - def->tooltip = L(""); + def->label = L("Timestamp"); + def->tooltip = L("String containing current time in yyyyMMdd-hhmmss format."); def = this->add("year", coInt); def->label = L("Year"); @@ -5247,24 +5251,25 @@ OtherPresetsConfigDef::OtherPresetsConfigDef() ConfigOptionDef* def; def = this->add("num_extruders", coInt); - def->label = L(""); - def->tooltip = L(""); + def->label = L("Number of extruders"); + def->tooltip = L("Total number of extruders, regardless of whether they are used in the current print."); def = this->add("print_preset", coString); - def->label = L(""); - def->tooltip = L(""); + def->label = L("Print preset name"); + def->tooltip = L("Name of the print preset used for slicing."); def = this->add("filament_preset", coString); def->label = L(""); - def->tooltip = L(""); + def->tooltip = L("Names of the filament presets used for slicing. The variable is a vector " + "containing one name for each extruder."); def = this->add("printer_preset", coString); def->label = L(""); - def->tooltip = L(""); + def->tooltip = L("Name of the printer preset used for slicing."); def = this->add("physical_printer_preset", coString); def->label = L(""); - def->tooltip = L(""); + def->tooltip = L("Name of the physical print preset used for slicing."); } @@ -5287,32 +5292,32 @@ CustomGcodeSpecificConfigDef::CustomGcodeSpecificConfigDef() ConfigOptionDef* def; def = this->add("layer_num", coInt); - def->label = L(""); - def->tooltip = L(""); + def->label = L("Layer number"); + def->tooltip = L("Index of the current layer. One-based (i.e. first layer is number 1)."); def = this->add("layer_z", coFloat); - def->label = L(""); - def->tooltip = L(""); + def->label = L("Layer z"); + def->tooltip = L("Height of the current layer above the print bed, measured to the top of the layer."); def = this->add("max_layer_z", coFloat); - def->label = L(""); - def->tooltip = L(""); + def->label = L("Maximal layer z"); + def->tooltip = L("Height of the last layer above the print bed."); def = this->add("filament_extruder_id", coInt); def->label = L(""); def->tooltip = L(""); def = this->add("previous_extruder", coInt); - def->label = L(""); - def->tooltip = L(""); + def->label = L("Previous extruder"); + def->tooltip = L("Index of the extruder that is being unloaded. The index is zero based (first extruder has index 0)."); def = this->add("next_extruder", coInt); - def->label = L(""); - def->tooltip = L(""); + def->label = L("Next extruder"); + def->tooltip = L("Index of the extruder that is being loaded. The index is zero based (first extruder has index 0)."); def = this->add("toolchange_z", coFloat); - def->label = L(""); - def->tooltip = L(""); + def->label = L("Toolchange z"); + def->tooltip = L("Height above the print bed when the toolchange takes place. Usually the same as layer_z, but can be different."); } const CustomGcodeSpecificConfigDef custom_gcode_specific_config_def; From 83b8988524ada62bf5c742df88627575e7545ba9 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 12 Sep 2023 15:48:03 +0200 Subject: [PATCH 132/136] Custom GCode editor: Added wrapping for descriptions lines in dialog + Some fixes for tooltips and labels in *Configs --- src/libslic3r/PrintConfig.cpp | 14 ++++---------- src/slic3r/GUI/EditGCodeDialog.cpp | 2 +- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 832db5ecf0..e521b2bc08 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -5223,27 +5223,21 @@ TimestampsConfigDef::TimestampsConfigDef() def = this->add("year", coInt); def->label = L("Year"); - def->tooltip = L(""); def = this->add("month", coInt); def->label = L("Month"); - def->tooltip = L(""); def = this->add("day", coInt); def->label = L("Day"); - def->tooltip = L(""); def = this->add("hour", coInt); def->label = L("Hour"); - def->tooltip = L(""); def = this->add("minute", coInt); def->label = L("Minute"); - def->tooltip = L(""); def = this->add("second", coInt); def->label = L("Second"); - def->tooltip = L(""); } OtherPresetsConfigDef::OtherPresetsConfigDef() @@ -5259,17 +5253,17 @@ OtherPresetsConfigDef::OtherPresetsConfigDef() def->tooltip = L("Name of the print preset used for slicing."); def = this->add("filament_preset", coString); - def->label = L(""); + def->label = L("Filament preset name"); def->tooltip = L("Names of the filament presets used for slicing. The variable is a vector " "containing one name for each extruder."); def = this->add("printer_preset", coString); - def->label = L(""); + def->label = L("Printer preset name"); def->tooltip = L("Name of the printer preset used for slicing."); def = this->add("physical_printer_preset", coString); - def->label = L(""); - def->tooltip = L("Name of the physical print preset used for slicing."); + def->label = L("Physical printer name"); + def->tooltip = L("Name of the physical printer used for slicing."); } diff --git a/src/slic3r/GUI/EditGCodeDialog.cpp b/src/slic3r/GUI/EditGCodeDialog.cpp index 413c524a49..26ad5e827f 100644 --- a/src/slic3r/GUI/EditGCodeDialog.cpp +++ b/src/slic3r/GUI/EditGCodeDialog.cpp @@ -286,7 +286,7 @@ void EditGCodeDialog::bind_list_and_button() format_wxstr("%1%\n(%2%)", def->label.empty() ? _(def->full_label) : _(def->label), type_str); if (def) - description = _(def->tooltip); + description = get_wraped_wxString(_(def->tooltip), 120); } else label = "Undef optptr"; From d389ce91f67cb37644d59c98795557deca467806 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 13 Sep 2023 10:03:44 +0200 Subject: [PATCH 133/136] Custom GCode editor: Updated tooltips, fixed end_filament_gcode, fixed type of filament_preset --- src/libslic3r/GCode.cpp | 7 ++++++- src/libslic3r/PrintConfig.cpp | 10 +++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 192d42bf43..8e7f43d9f8 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -3299,7 +3299,12 @@ std::string GCodeGenerator::set_extruder(unsigned int extruder_id, double print_ unsigned int old_extruder_id = m_writer.extruder()->id(); const std::string &end_filament_gcode = m_config.end_filament_gcode.get_at(old_extruder_id); if (! end_filament_gcode.empty()) { - gcode += placeholder_parser_process("end_filament_gcode", end_filament_gcode, old_extruder_id); + DynamicConfig config; + config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); + config.set_key_value("layer_z", new ConfigOptionFloat(m_writer.get_position().z() - m_config.z_offset.value)); + config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z)); + config.set_key_value("filament_extruder_id", new ConfigOptionInt(int(old_extruder_id))); + gcode += placeholder_parser_process("end_filament_gcode", end_filament_gcode, old_extruder_id, &config); check_add_eol(gcode); } } diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index e521b2bc08..15b7639227 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -5185,7 +5185,7 @@ DimensionsConfigDef::DimensionsConfigDef() def = this->add("first_layer_print_convex_hull", coPoints); def->label = L("First layer convex hull"); - def->tooltip = L("Vector of points of the first layer convex hull. Each element has the following format:" + def->tooltip = L("Vector of points of the first layer convex hull. Each element has the following format: " "'[x, y]' (x and y are floating-point numbers in mm)."); def = this->add("first_layer_print_min", coFloats); @@ -5252,7 +5252,7 @@ OtherPresetsConfigDef::OtherPresetsConfigDef() def->label = L("Print preset name"); def->tooltip = L("Name of the print preset used for slicing."); - def = this->add("filament_preset", coString); + def = this->add("filament_preset", coStrings); def->label = L("Filament preset name"); def->tooltip = L("Names of the filament presets used for slicing. The variable is a vector " "containing one name for each extruder."); @@ -5287,7 +5287,7 @@ CustomGcodeSpecificConfigDef::CustomGcodeSpecificConfigDef() def = this->add("layer_num", coInt); def->label = L("Layer number"); - def->tooltip = L("Index of the current layer. One-based (i.e. first layer is number 1)."); + def->tooltip = L("Zero-based index of the current layer (i.e. first layer is number 0)."); def = this->add("layer_z", coFloat); def->label = L("Layer z"); @@ -5298,8 +5298,8 @@ CustomGcodeSpecificConfigDef::CustomGcodeSpecificConfigDef() def->tooltip = L("Height of the last layer above the print bed."); def = this->add("filament_extruder_id", coInt); - def->label = L(""); - def->tooltip = L(""); + def->label = L("Current extruder index"); + def->tooltip = L("Zero-based index of currently used extruder (i.e. first extruder has index 0)."); def = this->add("previous_extruder", coInt); def->label = L("Previous extruder"); From 82a0c09a57c4366f11969280a9de0cdab25599e9 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 13 Sep 2023 09:04:50 +0200 Subject: [PATCH 134/136] Fixed compilation on Linux (gcc 9.4.0) --- src/libslic3r/Config.cpp | 1 + src/slic3r/GUI/EditGCodeDialog.hpp | 1 + src/slic3r/GUI/Plater.cpp | 1 + src/slic3r/GUI/Tab.cpp | 2 +- 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index 75a71acb0e..63e2b3821e 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include diff --git a/src/slic3r/GUI/EditGCodeDialog.hpp b/src/slic3r/GUI/EditGCodeDialog.hpp index 39757ab376..b54b9cc045 100644 --- a/src/slic3r/GUI/EditGCodeDialog.hpp +++ b/src/slic3r/GUI/EditGCodeDialog.hpp @@ -13,6 +13,7 @@ class wxListBox; class wxTextCtrl; class ScalableButton; +class wxStaticText; namespace Slic3r { diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index c60e58d47f..282f0d0185 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index ee7af794db..f318f84959 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2456,7 +2456,7 @@ void TabFilament::load_current_preset() m_extruders_cb->Select(m_active_extruder); } - assert(m_active_extruder >= 0 && m_active_extruder < m_preset_bundle->extruders_filaments.size()); + assert(m_active_extruder >= 0 && size_t(m_active_extruder) < m_preset_bundle->extruders_filaments.size()); const std::string& selected_extr_filament_name = m_preset_bundle->extruders_filaments[m_active_extruder].get_selected_preset_name(); if (selected_extr_filament_name != selected_filament_name) { m_presets->select_preset_by_name(selected_extr_filament_name, false); From cfc520c97ab9aa692f843a72f0dad194901b489f Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 8 Sep 2023 18:32:39 +0200 Subject: [PATCH 135/136] Thumbnails refactoring: Next round * Check errors state on all places of its using (throw exceptions or show a warnings message) + To backward compatibility, save "thumbnails_format" value to the config + deep_diff() function is extended for the case of "thumbnails" comparison + Added unit tests to: * check a load of configuration for "thumbnails" and "thumbnails_format" options * check return values for make_and_check_thumbnail_list() function --- src/libslic3r/GCode.cpp | 22 +- src/libslic3r/GCode/Thumbnails.cpp | 56 +++-- src/libslic3r/GCode/Thumbnails.hpp | 9 +- src/libslic3r/Preset.cpp | 20 +- src/libslic3r/PrintConfig.cpp | 14 +- src/slic3r/GUI/Field.cpp | 3 +- src/slic3r/GUI/Tab.cpp | 27 ++ tests/CMakeLists.txt | 1 + tests/thumbnails/CMakeLists.txt | 13 + .../thumbnails/test_thumbnails_ini_string.cpp | 235 ++++++++++++++++++ .../test_thumbnails_input_string.cpp | 152 +++++++++++ tests/thumbnails/thumbnails_tests_main.cpp | 1 + 12 files changed, 515 insertions(+), 38 deletions(-) create mode 100644 tests/thumbnails/CMakeLists.txt create mode 100644 tests/thumbnails/test_thumbnails_ini_string.cpp create mode 100644 tests/thumbnails/test_thumbnails_input_string.cpp create mode 100644 tests/thumbnails/thumbnails_tests_main.cpp diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 4262a4d8ec..0a2c1066c4 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -874,8 +874,15 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail // Unit tests or command line slicing may not define "thumbnails" or "thumbnails_format". // If "thumbnails_format" is not defined, export to PNG. - if (std::vector> thumbnails = GCodeThumbnails::make_thumbnail_list(print.full_print_config()); - ! thumbnails.empty()) + auto [thumbnails, errors] = GCodeThumbnails::make_and_check_thumbnail_list(print.full_print_config()); + + if (errors != enum_bitmask()) { + std::string error_str = format("Invalid thumbnails value:"); + error_str += GCodeThumbnails::get_error_string(errors); + throw Slic3r::ExportError(error_str); + } + + if (!thumbnails.empty()) GCodeThumbnails::generate_binary_thumbnails( thumbnail_cb, binary_data.thumbnails, thumbnails, [&print]() { print.throw_if_canceled(); }); @@ -1009,8 +1016,15 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail if (! export_to_binary_gcode) { // if exporting gcode in ascii format, generate the thumbnails here - if (std::vector> thumbnails = GCodeThumbnails::make_thumbnail_list(print.full_print_config()); - ! thumbnails.empty()) + auto [thumbnails, errors] = GCodeThumbnails::make_and_check_thumbnail_list(print.full_print_config()); + + if (errors != enum_bitmask()) { + std::string error_str = format("Invalid thumbnails value:"); + error_str += GCodeThumbnails::get_error_string(errors); + throw Slic3r::ExportError(error_str); + } + + if (!thumbnails.empty()) GCodeThumbnails::export_thumbnails_to_file(thumbnail_cb, thumbnails, [&file](const char* sz) { file.write(sz); }, [&print]() { print.throw_if_canceled(); }); diff --git a/src/libslic3r/GCode/Thumbnails.cpp b/src/libslic3r/GCode/Thumbnails.cpp index 9095f4e20d..edafccbea8 100644 --- a/src/libslic3r/GCode/Thumbnails.cpp +++ b/src/libslic3r/GCode/Thumbnails.cpp @@ -123,27 +123,27 @@ std::unique_ptr compress_thumbnail(const ThumbnailData &d } } -std::vector> make_and_check_thumbnail_list(const std::string& thumbnails_string, ThumbnailErrors& errors, std::string def_ext /*= "PNG"*/) +std::pair make_and_check_thumbnail_list(const std::string& thumbnails_string, const std::string_view def_ext /*= "PNG"sv*/) { if (thumbnails_string.empty()) return {}; - const auto& extentions = ConfigOptionEnum::get_enum_names(); - std::istringstream is(thumbnails_string); std::string point_str; + ThumbnailErrors errors; + // generate thumbnails data to process it - std::vector> thumbnails_list; + GCodeThumbnailDefinitionsList thumbnails_list; while (std::getline(is, point_str, ',')) { Vec2d point(Vec2d::Zero()); GCodeThumbnailsFormat format; std::istringstream iss(point_str); std::string coord_str; - if (std::getline(iss, coord_str, 'x')) { + if (std::getline(iss, coord_str, 'x') && !coord_str.empty()) { std::istringstream(coord_str) >> point(0); - if (std::getline(iss, coord_str, '/')) { + if (std::getline(iss, coord_str, '/') && !coord_str.empty()) { std::istringstream(coord_str) >> point(1); if (0 < point(0) && point(0) < 1000 && 0 < point(1) && point(1) < 1000) { @@ -151,15 +151,15 @@ std::vector> make_and_check_thumbnail_li std::getline(iss, ext_str, '/'); if (ext_str.empty()) - ext_str = def_ext; - else { - // check validity of extention - boost::to_upper(ext_str); - if (std::find(extentions.begin(), extentions.end(), ext_str) == extentions.end()) - errors = enum_bitmask(errors | ThumbnailError::InvalidExt); + ext_str = def_ext.empty() ? "PNG"sv : def_ext; + + // check validity of extention + boost::to_upper(ext_str); + if (!ConfigOptionEnum::from_string(ext_str, format)) { + format = GCodeThumbnailsFormat::PNG; + errors = enum_bitmask(errors | ThumbnailError::InvalidExt); } - format = ext_str == "JPG" ? GCodeThumbnailsFormat::JPG : - ext_str == "QOI" ? GCodeThumbnailsFormat::QOI : GCodeThumbnailsFormat::PNG; + thumbnails_list.emplace_back(std::make_pair(format, point)); } else @@ -170,24 +170,34 @@ std::vector> make_and_check_thumbnail_li errors = enum_bitmask(errors | ThumbnailError::InvalidVal); } - return thumbnails_list; + return std::make_pair(std::move(thumbnails_list), errors); } -std::vector> make_thumbnail_list(const DynamicPrintConfig& config) +std::pair make_and_check_thumbnail_list(const ConfigBase& config) { // ??? Unit tests or command line slicing may not define "thumbnails" or "thumbnails_format". // ??? If "thumbnails_format" is not defined, export to PNG. // generate thumbnails data to process it - std::vector> thumbnails_list; - if (const auto thumbnails_value = config.option("thumbnails")) { - ThumbnailErrors errors; - thumbnails_list = make_and_check_thumbnail_list(thumbnails_value->value, errors); - assert(errors == enum_bitmask()); - } + if (const auto thumbnails_value = config.option("thumbnails")) + return make_and_check_thumbnail_list(thumbnails_value->value); - return thumbnails_list; + return {}; +} + +std::string get_error_string(const ThumbnailErrors& errors) +{ + std::string error_str; + + if (errors.has(ThumbnailError::InvalidVal)) + error_str += "\n - " + format("Invalid input format. Expected vector of dimensions in the following format: \"%1%\"", "XxYxEXT, XxYxEXT, ..."); + if (errors.has(ThumbnailError::OutOfRange)) + error_str += "\n - Input value is out of range"; + if (errors.has(ThumbnailError::InvalidExt)) + error_str += "\n - Some input extention is invalid"; + + return error_str; } } // namespace Slic3r::GCodeThumbnails diff --git a/src/libslic3r/GCode/Thumbnails.hpp b/src/libslic3r/GCode/Thumbnails.hpp index b7b45850ff..a5f9803363 100644 --- a/src/libslic3r/GCode/Thumbnails.hpp +++ b/src/libslic3r/GCode/Thumbnails.hpp @@ -37,8 +37,13 @@ struct CompressedImageBuffer std::unique_ptr compress_thumbnail(const ThumbnailData &data, GCodeThumbnailsFormat format); -std::vector> make_and_check_thumbnail_list(const std::string& thumbnails_string, ThumbnailErrors& errors, std::string def_ext = "PNG"); -std::vector> make_thumbnail_list(const DynamicPrintConfig &config); +typedef std::vector> GCodeThumbnailDefinitionsList; + +using namespace std::literals; +std::pair make_and_check_thumbnail_list(const std::string& thumbnails_string, const std::string_view def_ext = "PNG"sv); +std::pair make_and_check_thumbnail_list(const ConfigBase &config); + +std::string get_error_string(const ThumbnailErrors& errors); template inline void export_thumbnails_to_file(ThumbnailsGeneratorCallback &thumbnail_cb, const std::vector>& thumbnails_list, WriteToOutput output, ThrowIfCanceledCallback throw_if_canceled) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 1e9764f815..0ff2f103c3 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -44,6 +44,7 @@ #include "libslic3r.h" #include "Utils.hpp" #include "PlaceholderParser.hpp" +#include "GCode/Thumbnails.hpp" using boost::property_tree::ptree; @@ -1299,7 +1300,6 @@ static const std::set independent_from_extruder_number_options = { "filament_ramming_parameters", "gcode_substitutions", "post_process", - "thumbnails", }; bool PresetCollection::is_independent_from_extruder_number_option(const std::string& opt_key) @@ -1323,6 +1323,15 @@ inline t_config_option_keys deep_diff(const ConfigBase &config_this, const Confi } else if (opt_key == "default_filament_profile") { // Ignore this field, it is not presented to the user, therefore showing a "modified" flag for this parameter does not help. // Also the length of this field may differ, which may lead to a crash if the block below is used. + } else if (opt_key == "thumbnails") { + // "thumbnails" can not containes a extentions in old config but are valid and use PNG extention by default + // So, check if "thumbnails" is really changed + // We will compare full thumnails instead of exactly config values + auto [thumbnails, er] = GCodeThumbnails::make_and_check_thumbnail_list(config_this); + auto [thumbnails_new, er_new] = GCodeThumbnails::make_and_check_thumbnail_list(config_other); + if (thumbnails != thumbnails_new || er != er_new) + // if those strings are actually the same, erase them from the list of dirty oprions + diff.emplace_back(opt_key); } else { switch (other_opt->type()) { case coInts: add_correct_opts_to_diff(opt_key, diff, config_other, config_this); break; @@ -1346,7 +1355,14 @@ bool PresetCollection::is_dirty(const Preset *edited, const Preset *reference) { if (edited != nullptr && reference != nullptr) { // Only compares options existing in both configs. - if (! reference->config.equals(edited->config)) + bool is_dirty = !reference->config.equals(edited->config); + if (is_dirty && edited->type != Preset::TYPE_FILAMENT) { + // for non-filaments preset check deep difference for compared configs + // there can be cases (as for thumbnails), when configs can logically equal + // even when their values are not equal. + is_dirty = !deep_diff(edited->config, reference->config).empty(); + } + if (is_dirty) return true; // The "compatible_printers" option key is handled differently from the others: // It is not mandatory. If the key is missing, it means it is compatible with any printer. diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 0af7868222..c712639660 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -4373,7 +4373,7 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va void PrintConfigDef::handle_legacy_composite(DynamicPrintConfig &config) { if (config.has("thumbnails")) { - std::string extention = "PNG"; + std::string extention; if (config.has("thumbnails_format")) { if (const ConfigOptionDef* opt = config.def()->get("thumbnails_format")) { auto label = opt->enum_def->enum_to_label(config.option("thumbnails_format")->getInt()); @@ -4381,11 +4381,15 @@ void PrintConfigDef::handle_legacy_composite(DynamicPrintConfig &config) extention = *label; } } - std::string thumbnails_str = config.opt_string("thumbnails"); - ThumbnailErrors errors; - auto thumbnails_list = GCodeThumbnails::make_and_check_thumbnail_list(thumbnails_str, errors, extention); - assert(errors == enum_bitmask()); + std::string thumbnails_str = config.opt_string("thumbnails"); + auto [thumbnails_list, errors] = GCodeThumbnails::make_and_check_thumbnail_list(thumbnails_str, extention); + + if (errors != enum_bitmask()) { + std::string error_str = "\n" + format("Invalid value provided for parameter %1%: %2%", "thumbnails", thumbnails_str); + error_str += GCodeThumbnails::get_error_string(errors); + throw BadOptionValueException(error_str); + } if (!thumbnails_list.empty()) { const auto& extentions = ConfigOptionEnum::get_enum_names(); diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index a95b2e7b66..e82f835587 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -71,9 +71,8 @@ ThumbnailErrors validate_thumbnails_string(wxString& str, const wxString& def_ex std::string input_string = into_u8(str); str.Clear(); - ThumbnailErrors errors; - auto thumbnails_list = Slic3r::GCodeThumbnails::make_and_check_thumbnail_list(input_string, errors); + auto [thumbnails_list, errors] = GCodeThumbnails::make_and_check_thumbnail_list(input_string); if (!thumbnails_list.empty()) { const auto& extentions = ConfigOptionEnum::get_enum_names(); for (const auto& [format, size] : thumbnails_list) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 0aee803397..305346b0b6 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -27,6 +27,7 @@ #include "libslic3r/Model.hpp" #include "libslic3r/GCode/GCodeProcessor.hpp" #include "libslic3r/GCode/GCodeWriter.hpp" +#include "libslic3r/GCode/Thumbnails.hpp" #include "slic3r/Utils/Http.hpp" #include "slic3r/Utils/PrintHost.hpp" @@ -2658,6 +2659,32 @@ void TabPrinter::build_fff() optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) { wxTheApp->CallAfter([this, opt_key, value]() { + if (opt_key == "thumbnails" && m_config->has("thumbnails_format")) { + // to backward compatibility we need to update "thumbnails_format" from new "thumbnails" + if (const std::string val = boost::any_cast(value); !value.empty()) { + auto [thumbnails_list, errors] = GCodeThumbnails::make_and_check_thumbnail_list(val); + + if (errors != enum_bitmask()) { + std::string error_str = format(_u8L("Invalid value provided for parameter %1%: %2%"), "thumbnails", val); + error_str += GCodeThumbnails::get_error_string(errors); + InfoDialog(parent(), _L("G-code flavor is switched"), from_u8(error_str)).ShowModal(); + } + + if (!thumbnails_list.empty()) { + GCodeThumbnailsFormat old_format = GCodeThumbnailsFormat(m_config->option("thumbnails_format")->getInt()); + GCodeThumbnailsFormat new_format = thumbnails_list.begin()->first; + if (old_format != new_format) { + DynamicPrintConfig new_conf = *m_config; + + auto* opt = m_config->option("thumbnails_format")->clone(); + opt->setInt(int(new_format)); + new_conf.set_key_value("thumbnails_format", opt); + + load_config(new_conf); + } + } + } + } if (opt_key == "silent_mode") { bool val = boost::any_cast(value); if (m_use_silent_mode != val) { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d6d16db1d2..af2a4e033d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -28,6 +28,7 @@ endif() set_property(GLOBAL PROPERTY USE_FOLDERS ON) add_subdirectory(arrange) +add_subdirectory(thumbnails) add_subdirectory(libslic3r) add_subdirectory(slic3rutils) add_subdirectory(fff_print) diff --git a/tests/thumbnails/CMakeLists.txt b/tests/thumbnails/CMakeLists.txt new file mode 100644 index 0000000000..e07ef0421f --- /dev/null +++ b/tests/thumbnails/CMakeLists.txt @@ -0,0 +1,13 @@ +get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME) + +add_executable(${_TEST_NAME}_tests + ${_TEST_NAME}_tests_main.cpp + test_thumbnails_input_string.cpp + test_thumbnails_ini_string.cpp +) + +target_link_libraries(${_TEST_NAME}_tests test_common libslic3r) +set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests") + +# catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ") +add_test(${_TEST_NAME}_tests ${_TEST_NAME}_tests ${CATCH_EXTRA_ARGS}) \ No newline at end of file diff --git a/tests/thumbnails/test_thumbnails_ini_string.cpp b/tests/thumbnails/test_thumbnails_ini_string.cpp new file mode 100644 index 0000000000..32ec5cbd13 --- /dev/null +++ b/tests/thumbnails/test_thumbnails_ini_string.cpp @@ -0,0 +1,235 @@ +#include + +#include "libslic3r/Config.hpp" +#include "libslic3r/PrintConfig.hpp" + +#include + +using namespace Slic3r; +using namespace GCodeThumbnails; + + +static std::string empty_thumbnails() +{ + return "thumbnails = \n" + "thumbnails_format = "; +} + +static std::string valid_thumbnails() +{ + return "thumbnails = 160x120/JPG, 23x78/QOI, 230x780/JPG\n" + "thumbnails_format = JPG"; +} + +static std::string valid_thumbnails2() +{ + return "thumbnails = 160x120/PNG, 23x78/QOi, 320x240/PNg, 230x780/JPG\n" + "thumbnails_format = pnG"; +} + +static std::string valid_thumbnails3() +{ + return "thumbnails = 160x120/JPG, 23x78/QOI, 230x780/JPG"; +} + +static std::string old_valid_thumbnails() +{ + return "thumbnails = 160x120\n" + "thumbnails_format = JPG"; +} + +static std::string old_valid_thumbnails2() +{ + return "thumbnails = 160x120, 23x78, 320x240\n" + "thumbnails_format = PNG"; +} + +static std::string old_invalid_thumbnails() +{ + return "thumbnails = 160x\n" + "thumbnails_format = JPG"; +} + +static std::string old_invalid_thumbnails2() +{ + return "thumbnails = 160x120, 23*78, 320x240\n" + "thumbnails_format = PNG"; +} + +static std::string out_of_range_thumbnails() +{ + return "thumbnails = 1160x1200/PNG, 23x78/QOI, 320x240/PNG, 230x780/JPG\n" + "thumbnails_format = PNG"; +} + +static std::string out_of_range_thumbnails2() +{ + return "thumbnails = 1160x120/PNG, 23x78/QOI, -320x240/PNG, 230x780/JPG\n" + "thumbnails_format = PNG"; +} + +static std::string invalid_ext_thumbnails() +{ + return "thumbnails = 1160x120/PNk, 23x78/QOI, 320x240/PNG, 230x780/JPG\n" + "thumbnails_format = QOI"; +} + +static std::string invalid_ext_thumbnails2() +{ + return "thumbnails = 1160x120/PNG, 23x78/QO, 320x240/PNG, 230x780/JPG\n" + "thumbnails_format = PNG"; +} + +static std::string invalid_val_thumbnails() +{ + return "thumbnails = 1160x/PNg, 23x78/QOI, 320x240/PNG, 230x780/JPG\n" + "thumbnails_format = JPG"; +} + +static std::string invalid_val_thumbnails2() +{ + return "thumbnails = x120/PNg, 23x78/QOI, 320x240/PNG, 230x780/JPG\n" + "thumbnails_format = PNG"; +} + +static std::string invalid_val_thumbnails3() +{ + return "thumbnails = 1x/PNg, 23x78/QOI, 320x240/PNG, 230x780/JPG\n" + "thumbnails_format = qoi"; +} + +static std::string invalid_val_thumbnails4() +{ + return "thumbnails = 123*78/QOI, 320x240/PNG, 230x780/JPG\n" + "thumbnails_format = jpG"; +} + +static DynamicPrintConfig thumbnails_config() +{ + DynamicPrintConfig config; + config.apply_only(FullPrintConfig::defaults() , { "thumbnails", "thumbnails_format" }); + + return config; +} + +TEST_CASE("Validate Empty Thumbnails", "[Thumbnails in Config]") { + DynamicPrintConfig config = thumbnails_config(); + + auto test_loaded_config = [](DynamicPrintConfig& config) { + REQUIRE(config.opt("thumbnails")->empty()); + REQUIRE(config.option("thumbnails_format")->getInt() == (int)GCodeThumbnailsFormat::PNG); + }; + + SECTION("Load empty init_data") { + REQUIRE_NOTHROW(config.load_from_ini_string("", Enable)); + test_loaded_config(config); + } + + SECTION("Load empty format and empty thumbnails") { + REQUIRE_THROWS_AS(config.load_from_ini_string(empty_thumbnails(), Enable), BadOptionValueException); + test_loaded_config(config); + } +} + +TEST_CASE("Validate New Thumbnails", "[Thumbnails in Config]") { + + DynamicPrintConfig config = thumbnails_config(); + + auto test_loaded_config = [](DynamicPrintConfig& config, GCodeThumbnailsFormat format) { + REQUIRE(!config.opt("thumbnails")->empty()); + REQUIRE(config.option("thumbnails_format")->getInt() == (int)format); + }; + + SECTION("Test 1 (valid)") { + REQUIRE_NOTHROW(config.load_from_ini_string(valid_thumbnails(), Enable)); + test_loaded_config(config, GCodeThumbnailsFormat::JPG); + } + + SECTION("Test 2 (valid)") { + REQUIRE_NOTHROW(config.load_from_ini_string(valid_thumbnails2(), Enable)); + test_loaded_config(config, GCodeThumbnailsFormat::PNG); + } + + SECTION("Test 3 (valid)") { + REQUIRE_NOTHROW(config.load_from_ini_string(valid_thumbnails3(), Enable)); + test_loaded_config(config, GCodeThumbnailsFormat::PNG); + } + + + SECTION("Test 1 (out_of_range)") { + REQUIRE_THROWS_AS(config.load_from_ini_string(out_of_range_thumbnails(), Enable), BadOptionValueException); + test_loaded_config(config, GCodeThumbnailsFormat::PNG); + } + + SECTION("Test 2 (out_of_range)") { + REQUIRE_THROWS_AS(config.load_from_ini_string(out_of_range_thumbnails2(), Enable), BadOptionValueException); + test_loaded_config(config, GCodeThumbnailsFormat::PNG); + } + + + SECTION("Test 1 (invalid_ext)") { + REQUIRE_THROWS_AS(config.load_from_ini_string(invalid_ext_thumbnails(), Enable), BadOptionValueException); + test_loaded_config(config, GCodeThumbnailsFormat::QOI); + } + + SECTION("Test 2 (invalid_ext)") { + REQUIRE_THROWS_AS(config.load_from_ini_string(invalid_ext_thumbnails2(), Enable), BadOptionValueException); + test_loaded_config(config, GCodeThumbnailsFormat::PNG); + } + + + SECTION("Test 1 (invalid_val)") { + REQUIRE_THROWS_AS(config.load_from_ini_string(invalid_val_thumbnails(), Enable), BadOptionValueException); + test_loaded_config(config, GCodeThumbnailsFormat::JPG); + } + + SECTION("Test 2 (invalid_val)") { + REQUIRE_THROWS_AS(config.load_from_ini_string(invalid_val_thumbnails2(), Enable), BadOptionValueException); + test_loaded_config(config, GCodeThumbnailsFormat::PNG); + } + + SECTION("Test 3 (invalid_val)") { + REQUIRE_THROWS_AS(config.load_from_ini_string(invalid_val_thumbnails3(), Enable), BadOptionValueException); + test_loaded_config(config, GCodeThumbnailsFormat::PNG); + } + + SECTION("Test 4 (invalid_val)") { + REQUIRE_THROWS_AS(config.load_from_ini_string(invalid_val_thumbnails4(), Enable), BadOptionValueException); + test_loaded_config(config, GCodeThumbnailsFormat::PNG); + } +} + +TEST_CASE("Validate Old Thumbnails", "[Thumbnails in Config]") { + + DynamicPrintConfig config = thumbnails_config(); + + auto test_loaded_config = [](DynamicPrintConfig& config, GCodeThumbnailsFormat format) { + REQUIRE(!config.opt("thumbnails")->empty()); + REQUIRE(config.option("thumbnails_format")->getInt() == (int)format); + }; + + SECTION("Test 1 (valid)") { + REQUIRE_NOTHROW(config.load_from_ini_string(old_valid_thumbnails(), Enable)); + test_loaded_config(config, GCodeThumbnailsFormat::JPG); + } + + SECTION("Test 2 (valid)") { + REQUIRE_NOTHROW(config.load_from_ini_string(old_valid_thumbnails2(), Enable)); + test_loaded_config(config, GCodeThumbnailsFormat::PNG); + } + + SECTION("Test 1 (invalid)") { + REQUIRE_THROWS_AS(config.load_from_ini_string(old_invalid_thumbnails(), Enable), BadOptionValueException); + test_loaded_config(config, GCodeThumbnailsFormat::JPG); + } + + SECTION("Test 2 (invalid)") { + REQUIRE_THROWS_AS(config.load_from_ini_string(old_invalid_thumbnails2(), Enable), BadOptionValueException); + test_loaded_config(config, GCodeThumbnailsFormat::PNG); + } +} + + + + + diff --git a/tests/thumbnails/test_thumbnails_input_string.cpp b/tests/thumbnails/test_thumbnails_input_string.cpp new file mode 100644 index 0000000000..4c623b2736 --- /dev/null +++ b/tests/thumbnails/test_thumbnails_input_string.cpp @@ -0,0 +1,152 @@ +#include +#include + +#include + +using namespace Slic3r; +using namespace GCodeThumbnails; + + +// Test Thumbnails lines + +static std::string empty_thumbnails() +{ + return ""; +} + +static std::string valid_thumbnails() +{ + return "160x120/PNG, 23x78/QOI, 230x780/JPG"; +} + +static std::string valid_thumbnails2() +{ + return "160x120/PNG, 23x78/QOi, 320x240/PNg, 230x780/JPG"; +} + +static std::string out_of_range_thumbnail() +{ + return "160x1200/PNG, 23x78/QOI, 320x240/PNG, 230x780/JPG"; +} + +static std::string out_of_range_thumbnail2() +{ + return "160x120/PNG, 23x78/QOI, -320x240/PNG, 230x780/JPG"; +} + +static std::string invalid_ext_thumbnail() +{ + return "160x120/PNk, 23x78/QOI, 320x240/PNG, 230x780/JPG"; +} + +static std::string invalid_ext_thumbnail2() +{ + return "160x120/PNG, 23x78/QO, 320x240/PNG, 230x780/JPG"; +} + +static std::string invalid_val_thumbnail() +{ + return "160x/PNg, 23x78/QOI, 320x240/PNG, 230x780/JPG"; +} + +static std::string invalid_val_thumbnail2() +{ + return "x120/PNg, 23x78/QOI, 320x240/PNG, 230x780/JPG"; +} + +static std::string invalid_val_thumbnail3() +{ + return "x/PNg, 23x78/QOI, 320x240/PNG, 230x780/JPG"; +} + +static std::string invalid_val_thumbnail4() +{ + return "23*78/QOI, 320x240/PNG, 230x780/JPG"; +} + + +TEST_CASE("Empty Thumbnails", "[Thumbnails]") { + auto [thumbnails, errors] = make_and_check_thumbnail_list(empty_thumbnails()); + REQUIRE(errors == enum_bitmask()); + REQUIRE(thumbnails.empty()); +} + +TEST_CASE("Valid Thumbnails", "[Thumbnails]") { + + SECTION("Test 1") { + auto [thumbnails, errors] = make_and_check_thumbnail_list(valid_thumbnails()); + REQUIRE(errors == enum_bitmask()); + REQUIRE(thumbnails.size() == 3); + } + + SECTION("Test 2") { + auto [thumbnails, errors] = make_and_check_thumbnail_list(valid_thumbnails2()); + REQUIRE(errors == enum_bitmask()); + REQUIRE(thumbnails.size() == 4); + } +} + +TEST_CASE("Out of range Thumbnails", "[Thumbnails]") { + + SECTION("Test 1") { + auto [thumbnails, errors] = make_and_check_thumbnail_list(out_of_range_thumbnail()); + REQUIRE(errors != enum_bitmask()); + REQUIRE(errors.has(ThumbnailError::OutOfRange)); + REQUIRE(thumbnails.size() == 3); + } + + SECTION("Test 2") { + auto [thumbnails, errors] = make_and_check_thumbnail_list(out_of_range_thumbnail2()); + REQUIRE(errors != enum_bitmask()); + REQUIRE(errors.has(ThumbnailError::OutOfRange)); + REQUIRE(thumbnails.size() == 3); + } +} + +TEST_CASE("Invalid extention Thumbnails", "[Thumbnails]") { + + SECTION("Test 1") { + auto [thumbnails, errors] = make_and_check_thumbnail_list(invalid_ext_thumbnail()); + REQUIRE(errors != enum_bitmask()); + REQUIRE(errors.has(ThumbnailError::InvalidExt)); + REQUIRE(thumbnails.size() == 4); + } + + SECTION("Test 2") { + auto [thumbnails, errors] = make_and_check_thumbnail_list(invalid_ext_thumbnail2()); + REQUIRE(errors != enum_bitmask()); + REQUIRE(errors.has(ThumbnailError::InvalidExt)); + REQUIRE(thumbnails.size() == 4); + } +} + +TEST_CASE("Invalid value Thumbnails", "[Thumbnails]") { + + SECTION("Test 1") { + auto [thumbnails, errors] = make_and_check_thumbnail_list(invalid_val_thumbnail()); + REQUIRE(errors != enum_bitmask()); + REQUIRE(errors.has(ThumbnailError::InvalidVal)); + REQUIRE(thumbnails.size() == 3); + } + + SECTION("Test 2") { + auto [thumbnails, errors] = make_and_check_thumbnail_list(invalid_val_thumbnail2()); + REQUIRE(errors != enum_bitmask()); + REQUIRE(errors.has(ThumbnailError::InvalidVal)); + REQUIRE(thumbnails.size() == 3); + } + + SECTION("Test 3") { + auto [thumbnails, errors] = make_and_check_thumbnail_list(invalid_val_thumbnail3()); + REQUIRE(errors != enum_bitmask()); + REQUIRE(errors.has(ThumbnailError::InvalidVal)); + REQUIRE(thumbnails.size() == 3); + } + + SECTION("Test 4") { + auto [thumbnails, errors] = make_and_check_thumbnail_list(invalid_val_thumbnail4()); + REQUIRE(errors != enum_bitmask()); + REQUIRE(errors.has(ThumbnailError::InvalidVal)); + REQUIRE(thumbnails.size() == 2); + } +} \ No newline at end of file diff --git a/tests/thumbnails/thumbnails_tests_main.cpp b/tests/thumbnails/thumbnails_tests_main.cpp new file mode 100644 index 0000000000..b2aa80259d --- /dev/null +++ b/tests/thumbnails/thumbnails_tests_main.cpp @@ -0,0 +1 @@ +#include From bf3938f506080e9c30e26e633a6c20dc792a12d1 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 13 Sep 2023 16:27:37 +0200 Subject: [PATCH 136/136] Fixed some compiler warnings --- src/libslic3r/Measure.cpp | 4 ++-- src/slic3r/GUI/MainFrame.cpp | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index 97962a5a0e..2e6156a88e 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -189,7 +189,7 @@ void MeasuringImpl::update_planes() int neighbor_idx = face_neighbors[facets[face_id]][edge_id]; if (neighbor_idx == -1) goto PLANE_FAILURE; - if (visited[face_id][edge_id] || (int)face_to_plane[neighbor_idx] == plane_id) { + if (visited[face_id][edge_id] || face_to_plane[neighbor_idx] == plane_id) { visited[face_id][edge_id] = true; continue; } @@ -223,7 +223,7 @@ void MeasuringImpl::update_planes() // Remember all halfedges we saw to break out of such infinite loops. boost::container::small_vector he_seen; - while ( (int)face_to_plane[sm.face(he)] == plane_id && he != he_orig) { + while ( face_to_plane[sm.face(he)] == plane_id && he != he_orig) { he_seen.emplace_back(he); he = sm.next_around_target(he); if (he.is_invalid() || std::find(he_seen.begin(), he_seen.end(), he) != he_seen.end()) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index f5c644d6de..22a1461812 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1373,10 +1373,10 @@ void MainFrame::init_menubar_as_editor() wxMenu* convert_menu = new wxMenu(); append_menu_item(convert_menu, wxID_ANY, _L("Convert ascii G-code to &binary") + dots, _L("Convert a G-code file from ascii to binary format"), [this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->convert_gcode_to_binary(); }, "convert_file", nullptr, - [this]() { return true; }, this); + []() { return true; }, this); append_menu_item(convert_menu, wxID_ANY, _L("Convert binary G-code to &ascii") + dots, _L("Convert a G-code file from binary to ascii format"), [this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->convert_gcode_to_ascii(); }, "convert_file", nullptr, - [this]() { return true; }, this); + []() { return true; }, this); append_submenu(fileMenu, convert_menu, wxID_ANY, _L("&Convert"), ""); append_menu_item(fileMenu, wxID_ANY, _L("Ejec&t SD Card / Flash Drive") + dots + "\tCtrl+T", _L("Eject SD card / Flash drive after the G-code was exported to it."), @@ -1654,10 +1654,10 @@ void MainFrame::init_menubar_as_gcodeviewer() fileMenu->AppendSeparator(); append_menu_item(fileMenu, wxID_ANY, _L("Convert ascii G-code to &binary") + dots, _L("Convert a G-code file from ascii to binary format"), [this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->convert_gcode_to_binary(); }, "convert_file", nullptr, - [this]() { return true; }, this); + []() { return true; }, this); append_menu_item(fileMenu, wxID_ANY, _L("Convert binary G-code to &ascii") + dots, _L("Convert a G-code file from binary to ascii format"), [this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->convert_gcode_to_ascii(); }, "convert_file", nullptr, - [this]() { return true; }, this); + []() { return true; }, this); fileMenu->AppendSeparator(); append_menu_item(fileMenu, wxID_ANY, _L("Export &Toolpaths as OBJ") + dots, _L("Export toolpaths as OBJ"), [this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->export_toolpaths_to_obj(); }, "export_plater", nullptr,