From 3cb950a2600b382fcc288736bc2731df90dff4b5 Mon Sep 17 00:00:00 2001 From: Leonid Raiz Date: Sun, 25 Dec 2022 12:54:07 -0500 Subject: [PATCH 1/9] zdithering - inititial changes --- src/libslic3r/CMakeLists.txt | 2 + src/libslic3r/ExPolygon.hpp | 35 ++++++ src/libslic3r/PrintObjectSlice.cpp | 36 ++++-- src/libslic3r/TriangleMeshSlicer.hpp | 2 + src/libslic3r/ZDither.cpp | 167 +++++++++++++++++++++++++++ src/libslic3r/ZDither.hpp | 29 +++++ 6 files changed, 260 insertions(+), 11 deletions(-) create mode 100644 src/libslic3r/ZDither.cpp create mode 100644 src/libslic3r/ZDither.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 396eb0764e6..737115bbba2 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -260,6 +260,8 @@ set(SLIC3R_SOURCES TriangleSelector.cpp TriangleSelector.hpp MTUtils.hpp + ZDither.hpp + ZDither.cpp Zipper.hpp Zipper.cpp MinAreaBoundingBox.hpp diff --git a/src/libslic3r/ExPolygon.hpp b/src/libslic3r/ExPolygon.hpp index 7eccf2ec823..099b1a57a14 100644 --- a/src/libslic3r/ExPolygon.hpp +++ b/src/libslic3r/ExPolygon.hpp @@ -11,6 +11,41 @@ namespace Slic3r { class ExPolygon; using ExPolygons = std::vector; +struct ExPolygons5 +{ + ExPolygons5() = default; + ExPolygons5(const ExPolygons5 &other) = default; + ExPolygons5(ExPolygons5 &&other) = default; + explicit ExPolygons5(ExPolygons main) : whole(main) {}; + ExPolygons5 &operator=(const ExPolygons5 &other) = default; + ExPolygons5 &operator=(ExPolygons5 &&other) = default; + // z-dithering reduces stair-step appearance of layers of low-slope surfaces by splitting + // each layer into up to 5 regions of different thinkness deposited from different heights. + ExPolygons whole; // Most of the layer made without z-dithering potentialy reduced near + // low sloped surfaces + ExPolygons ditherUp[2]; // Upto two regions near upward facing surface. May be absent. + // ditherUp[0] - deposited on top of previous layer, 0.25 layer height + // ditherUp[1] - deposited on top of ditherUp[0], 0.5 layer height + ExPolygons ditherDn[2]; // Upto two regions near downward facing surface. May be absent. + // ditherDn[0] - deposited from the height of next layer bottom, 0.25 layer height + // ditherDn[1] - deposited from the height of ditherDn[0] bottom, 0.5 layer height + // In actual priting ditherDn[1] should be printed before ditherDn[0] + bool empty() const + { + return whole.empty() && ditherUp[0].empty() && ditherUp[1].empty() && + ditherDn[0].empty() && ditherDn[1].empty(); + } + void clear() + { + whole.clear(); + ditherUp[0].clear(); + ditherUp[1].clear(); + ditherDn[0].clear(); + ditherDn[1].clear(); + } +}; + + class ExPolygon { public: diff --git a/src/libslic3r/PrintObjectSlice.cpp b/src/libslic3r/PrintObjectSlice.cpp index 4b91714e586..4368d79e705 100644 --- a/src/libslic3r/PrintObjectSlice.cpp +++ b/src/libslic3r/PrintObjectSlice.cpp @@ -4,6 +4,7 @@ #include "MultiMaterialSegmentation.hpp" #include "Print.hpp" #include "ClipperUtils.hpp" +#include "ZDither.hpp" #include @@ -46,18 +47,29 @@ static std::vector slice_volume( const MeshSlicingParamsEx ¶ms, const std::function &throw_on_cancel_callback) { - std::vector layers; - if (! zs.empty()) { - indexed_triangle_set its = volume.mesh().its; - if (its.indices.size() > 0) { - MeshSlicingParamsEx params2 { params }; - params2.trafo = params2.trafo * volume.get_matrix(); - if (params2.trafo.rotation().determinant() < 0.) - its_flip_triangles(its); - layers = slice_mesh_ex(its, zs, params2, throw_on_cancel_callback); - throw_on_cancel_callback(); - } + indexed_triangle_set its = volume.mesh().its; + if (zs.empty() || its.indices.size() == 0) + return std::vector(); + + MeshSlicingParamsEx params2{params}; + params2.trafo = params2.trafo * volume.get_matrix(); + if (params2.trafo.rotation().determinant() < 0.) + its_flip_triangles(its); + std::vector layers = slice_mesh_ex(its, zs, params2, throw_on_cancel_callback); + /* + std::vector layers5 = z_dither(its, zs, params2, layers, throw_on_cancel_callback); + // It is not quite clear what is the best way to incorporate z-dithering into the rest of PrusaSlicer. + // I would need some developer assistance. + // The code below is an aid in debugging + // Make new output vector and let vector destructors release layers and layers3 + std::vector out; + out.reserve(layers5.size()); + for (int i = 0; i < layers5.size(); i++) { + // To visualize ditherUp[0] while in development + out.push_back(ExPolygons(layers5[i].ditherUp[0].empty() ? layers5[i].whole : layers5[i].ditherUp[0])); } + */ + throw_on_cancel_callback(); return layers; } @@ -141,6 +153,8 @@ static std::vector slice_volumes_inner( params_base.extra_offset = 0; params_base.trafo = object_trafo; params_base.resolution = print_config.resolution.value; + const std::vector &diameters = print_config.nozzle_diameter.values; + params_base.nozzle_diameter = float(*std::min_element(diameters.begin(), diameters.end())); switch (print_object_config.slicing_mode.value) { case SlicingMode::Regular: params_base.mode = MeshSlicingParams::SlicingMode::Regular; break; diff --git a/src/libslic3r/TriangleMeshSlicer.hpp b/src/libslic3r/TriangleMeshSlicer.hpp index fbe72443321..5a3ba31a071 100644 --- a/src/libslic3r/TriangleMeshSlicer.hpp +++ b/src/libslic3r/TriangleMeshSlicer.hpp @@ -44,6 +44,8 @@ struct MeshSlicingParamsEx : public MeshSlicingParams // Resolution for contour simplification, unscaled. // 0 = don't simplify. double resolution { 0 }; + // nozzle diameter (needed for z_dithering optimization) + float nozzle_diameter{0}; }; // All the following slicing functions shall produce consistent results with the same mesh, same transformation matrix and slicing parameters. diff --git a/src/libslic3r/ZDither.cpp b/src/libslic3r/ZDither.cpp new file mode 100644 index 00000000000..c48cd573484 --- /dev/null +++ b/src/libslic3r/ZDither.cpp @@ -0,0 +1,167 @@ +#include "ZDither.hpp" +#include "ExPolygon.hpp" +#include "TriangleMeshSlicer.hpp" + + +namespace Slic3r { + +// Find z where additional midlevel slices need to be made for z-dithering +// It is assumed that original layers corresponding to zs[i] will be filled +// from zs[i] - layerHight/2 till zs[i] + layerHeight/2 +// Required midlevel slices are to be computed halfway between layers, +// starting with top of layer 0 and ending with top of last layer ; + +void midslice_zs(const indexed_triangle_set &mesh, + const std::vector & zs, + const Transform3d & trafo, + float nozzle_diameter, + std::vector * mid_zs, + std::vector * upwrd_mididx, + std::vector * dnwrd_mididx) +{ + mid_zs->clear(); + upwrd_mididx->clear(); + dnwrd_mididx->clear(); + + auto n_zs = zs.size(); + if (n_zs < 2) return; + + std::vector transformed_vertices(mesh.vertices); + auto trafo_float = trafo.cast(); + for (stl_vertex &v : transformed_vertices) { v = trafo_float * v; } + + std::vector candidate_zs(n_zs); + std::vector layerHeight(n_zs); + float layerHeightMax = 0; + + for (auto i = 0; i < n_zs - 1; i++) { + candidate_zs[i] = (zs[i + 1] + zs[i]) / 2; + layerHeight[i] = zs[i + 1] - zs[i]; + layerHeightMax = std::max(layerHeight[i], layerHeightMax); + } + layerHeight[n_zs - 1] = layerHeight[n_zs - 2]; + candidate_zs[n_zs - 1] = zs[n_zs - 1] + layerHeight[n_zs - 1] / 2; + + upwrd_mididx->assign(n_zs, -1); + dnwrd_mididx->assign(n_zs, -1); + + float nDia = 1.0; // Cooficient to avoid cutting layers with too vertical triangles (see below) + // With nozzle_diameter = 0.4 and layer_height = 0.25 + // nDia = 1.5 results in slopes below 22.5 degrees; nDia = 1 results in slopes under 32 degrees + // nDia = 0.7 - 42 degrees; nDia = 0.5 51.4 degrees + + float eps = layerHeightMax / 100; + + for (auto i = 0; i < mesh.indices.size(); ++i) { + const stl_triangle_vertex_indices &facetVertices = mesh.indices[i]; + stl_vertex vertex[3] = {transformed_vertices[facetVertices[0]], transformed_vertices[facetVertices[1]], + transformed_vertices[facetVertices[2]]}; + + Vec3f norm((vertex[1] - vertex[0]).cross(vertex[2] - vertex[0]).normalized()); + // Skip triangle if over a thickest layer it would not fit nDia * nozzle_diameter + if (nozzle_diameter * sqrt(1 - norm[2] * norm[2]) * nDia > fabs(norm[2]) * layerHeightMax ) continue; + + float z_min = std::min(std::min(vertex[0][2], vertex[1][2]), vertex[2][2]); + float z_max = std::max(std::max(vertex[0][2], vertex[1][2]), vertex[2][2]); + + int start = std::lower_bound(candidate_zs.begin(), candidate_zs.end(), z_min - eps) - candidate_zs.begin(); + + for (auto i = start; i < candidate_zs.size() && candidate_zs[i] < z_max + eps; i++) { + // Ignore facets that are too vertical to fit nDia nozzles at layer height + if (nozzle_diameter * sqrt(1 - norm[2] * norm[2]) * nDia < fabs(norm[2]) * layerHeight[i]) { + if (norm[2] > 0) + upwrd_mididx->at(i) = i; + else + dnwrd_mididx->at(i) = i; + } + } + } + + if (std::all_of(upwrd_mididx->begin(), upwrd_mididx->end(), [](int idx) { return idx == -1; }) && + std::all_of(dnwrd_mididx->begin(), dnwrd_mididx->end(), [](int idx) { return idx = -1; })) + return; + + // Retrn mid_zs which will contribute to z-dithering + for (int i = 0; i < n_zs; i++) { + if (upwrd_mididx->at(i) + dnwrd_mididx->at(i) != -2) { + int count = mid_zs->size(); + mid_zs->push_back(candidate_zs[i]); + if (upwrd_mididx->at(i) != -1) upwrd_mididx->at(i) = count; + if (dnwrd_mididx->at(i) != -1) dnwrd_mididx->at(i) = count; + } + } + return; +} + +std::vector apply_z_dither(const std::vector &layers, + const std::vector &layers_mid, + const std::vector & upwrd_mididx, + const std::vector & dnwrd_mididx) +{ + std::vector layers5; + layers5.resize(layers.size()); + + for (auto ll = 0; ll < layers.size(); ll++) { + // idx0 - bottom of layer, idx1 - top of layer + int upwrd_idx0 = ll > 0 ? upwrd_mididx[ll - 1] : -1; + int dnwrd_idx0 = ll > 0 ? dnwrd_mididx[ll - 1] : -1; + int upwrd_idx1 = upwrd_mididx[ll]; + int dnwrd_idx1 = dnwrd_mididx[ll]; + + auto useMidCut = [](int idx) { return idx != -1; }; + + if (!useMidCut(upwrd_idx0) && !useMidCut(dnwrd_idx0) && !useMidCut(upwrd_idx1) && !useMidCut(dnwrd_idx1)) { + layers5[ll] = ExPolygons5(ExPolygons(layers[ll])); + } else { + layers5[ll] = ExPolygons5(); + if (useMidCut(upwrd_idx0) || useMidCut(upwrd_idx1)) { + layers5[ll].ditherUp[0] = std::move( + diff_ex(useMidCut(upwrd_idx0) ? layers_mid[upwrd_idx0] : layers[ll], + useMidCut(upwrd_idx1) ? layers_mid[upwrd_idx1] : layers[ll])); + } + if (useMidCut(upwrd_idx1)) { + layers5[ll].ditherUp[1] = std::move(diff_ex(layers[ll], layers_mid[upwrd_idx1])); + } + + if (useMidCut(dnwrd_idx0) || useMidCut(dnwrd_idx1)) { + layers5[ll].ditherDn[0] = std::move( + diff_ex(useMidCut(dnwrd_idx1) ? layers_mid[dnwrd_idx1] : layers[ll], + useMidCut(dnwrd_idx0) ? layers_mid[dnwrd_idx0] : layers[ll])); + } + if (useMidCut(dnwrd_idx0)) { + layers5[ll].ditherDn[1] = std::move(diff_ex(layers[ll], layers_mid[dnwrd_idx0])); + } + + if (useMidCut(upwrd_idx1) && useMidCut(dnwrd_idx0)) + layers5[ll].whole = std::move(intersection_ex(layers_mid[dnwrd_idx0], layers_mid[upwrd_idx1])); + else if (useMidCut(upwrd_idx1)) + layers5[ll].whole = std::move(intersection_ex(layers[ll], layers_mid[upwrd_idx1])); + else if (useMidCut(dnwrd_idx0)) + layers5[ll].whole = std::move(intersection_ex(layers[ll], layers_mid[dnwrd_idx0])); + else + layers5[ll].whole = ExPolygons(layers[ll]); + } + } + return layers5; +} +std::vector z_dither(const indexed_triangle_set & mesh, + const std::vector & zs, + const MeshSlicingParamsEx & params, + const std::vector &layers, + const std::function & throw_on_cancel_callback) +{ + std::vector mid_zs; + std::vector upwrd_mididx; + std::vector dnwrd_mididx; + midslice_zs(mesh, zs, params.trafo, params.nozzle_diameter, &mid_zs, &upwrd_mididx, &dnwrd_mididx); + if (!mid_zs.empty()) { + std::vector layers_mid = slice_mesh_ex(mesh, mid_zs, params, throw_on_cancel_callback); + return apply_z_dither(layers, layers_mid, upwrd_mididx, dnwrd_mididx); + } else { + std::vector layers5; + for (auto i = 0; i < layers.size(); i++) { layers5.push_back(ExPolygons5((layers[i]))); }; + return layers5; + } +} + +} // namespace Slic3r \ No newline at end of file diff --git a/src/libslic3r/ZDither.hpp b/src/libslic3r/ZDither.hpp new file mode 100644 index 00000000000..77c6cea1027 --- /dev/null +++ b/src/libslic3r/ZDither.hpp @@ -0,0 +1,29 @@ +#ifndef slic3r_ZDither_hpp +#define slic3r_Zdither_hpp + +#include "Point.hpp" +#include "TriangleMeshSlicer.hpp" + +namespace Slic3r { + +void midslice_zs(const indexed_triangle_set &mesh, + const std::vector & zs, + const Transform3d & trafo, + float nozzle_diameter, + std::vector * mid_zs, + std::vector * upwrd_mididx, + std::vector * dnwrd_mididx); + +std::vector apply_z_dither(const std::vector &layers, + const std::vector &mid_layers, + const std::vector &do_low, + const std::vector &do_high); + +std::vector z_dither(const indexed_triangle_set & mesh, + const std::vector & zs, + const MeshSlicingParamsEx & params, + const std::vector &layers, + const std::function & throw_on_cancel_callback); + +} // namespace Slic3r +#endif From 88a6f72a6a9925034acbb157941f9130385f527d Mon Sep 17 00:00:00 2001 From: Leonid Raiz Date: Tue, 11 Apr 2023 13:17:20 -0400 Subject: [PATCH 2/9] Backported z-dither from SuperSlicer --- src/libslic3r/GCode.cpp | 18 +- src/libslic3r/Layer.hpp | 21 +- src/libslic3r/Preset.cpp | 10 +- src/libslic3r/Print.cpp | 83 +- src/libslic3r/Print.hpp | 22 +- src/libslic3r/PrintConfig.cpp | 50 +- src/libslic3r/PrintConfig.hpp | 1673 +++++++++++++------------- src/libslic3r/PrintObject.cpp | 258 ++-- src/libslic3r/PrintObjectSlice.cpp | 278 ++++- src/libslic3r/SupportMaterial.cpp | 280 ++--- src/libslic3r/TriangleMeshSlicer.hpp | 7 +- src/libslic3r/ZDither.cpp | 98 +- src/libslic3r/ZDither.hpp | 32 +- src/slic3r/GUI/Tab.cpp | 1 + 14 files changed, 1539 insertions(+), 1292 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 58951ffa5a4..14d7475a02a 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -586,6 +586,9 @@ std::vector>> GCode::collec coordf_t print_z; size_t object_idx; size_t layer_idx; + coordf_t layer_height; + + }; std::vector> per_object(print.objects().size(), std::vector()); @@ -599,11 +602,14 @@ std::vector>> GCode::collec for (const LayerToPrint& ltp : per_object[i]) { ordering_item.print_z = ltp.print_z(); ordering_item.layer_idx = <p - &front; + ordering_item.layer_height = ltp.layer()->height; ordering.emplace_back(ordering_item); } } - std::sort(ordering.begin(), ordering.end(), [](const OrderingItem& oi1, const OrderingItem& oi2) { return oi1.print_z < oi2.print_z; }); + std::sort(ordering.begin(), ordering.end(), [](const OrderingItem& oi1, const OrderingItem& oi2) { + return fabs(oi1.print_z - oi2.print_z) > EPSILON ? oi1.print_z < oi2.print_z : oi1.layer_height < oi2.layer_height; + }); std::vector>> layers_to_print; @@ -612,16 +618,16 @@ std::vector>> GCode::collec // Find the last layer with roughly the same print_z. size_t j = i + 1; coordf_t zmax = ordering[i].print_z + EPSILON; - for (; j < ordering.size() && ordering[j].print_z <= zmax; ++j); + coordf_t hmax = ordering[i].layer_height + EPSILON; // z-dithering may make layers of different height but same print_z + for (; j < ordering.size() && ordering[j].print_z <= zmax && ordering[j].layer_height < hmax; ++j); // Merge into layers_to_print. std::pair> merged; // Assign an average print_z to the set of layers with nearly equal print_z. merged.first = 0.5 * (ordering[i].print_z + ordering[j - 1].print_z); - merged.second.assign(print.objects().size(), LayerToPrint()); - for (; i < j; ++i) { + // z-dithering may result in 2 layers from the same object in merged + for (; i < j; ++ i) { const OrderingItem& oi = ordering[i]; - assert(merged.second[oi.object_idx].layer() == nullptr); - merged.second[oi.object_idx] = std::move(per_object[oi.object_idx][oi.layer_idx]); + merged.second.emplace_back(std::move(per_object[oi.object_idx][oi.layer_idx])); } layers_to_print.emplace_back(std::move(merged)); } diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index 2e3affec7ba..8484ed56f13 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -65,7 +65,7 @@ class LayerRegion // ordered collection of extrusion paths to fill surfaces // (this collection contains only ExtrusionEntityCollection objects) ExtrusionEntityCollection fills; - + Flow flow(FlowRole role) const; Flow flow(FlowRole role, double layer_height) const; Flow bridging_flow(FlowRole role) const; @@ -102,7 +102,7 @@ class LayerRegion const PrintRegion *m_region; }; -class Layer +class Layer { public: // Sequential index of this layer in PrintObject::m_layers, offsetted by the number of raft layers. @@ -118,8 +118,9 @@ class Layer coordf_t print_z; // Z used for printing in unscaled coordinates coordf_t height; // layer height in unscaled coordinates coordf_t bottom_z() const { return this->print_z - this->height; } + bool dithered = false; // is this is a layer produced by z-dithering. - // Collection of expolygons generated by slicing the possibly multiple meshes of the source geometry + // Collection of expolygons generated by slicing the possibly multiple meshes of the source geometry // (with possibly differing extruder ID and slicing parameters) and merged. // For the first layer, if the Elephant foot compensation is applied, this lslice is uncompensated, therefore // it includes the Elephant foot effect, thus it corresponds to the shape of the printed 1st layer. @@ -136,7 +137,7 @@ class Layer LayerRegion* add_region(const PrintRegion *print_region); const LayerRegionPtrs& regions() const { return m_regions; } // Test whether whether there are any slices assigned to this layer. - bool empty() const; + bool empty() const; void make_slices(); // Backup and restore raw sliced regions if needed. //FIXME Review whether not to simplify the code by keeping the raw_slices all the time. @@ -173,6 +174,8 @@ class Layer friend class PrintObject; friend std::vector new_layers(PrintObject*, const std::vector&); friend std::string fix_slicing_errors(LayerPtrs&, const std::function&); + // Create dithering layer. bottom & top >= 0 and <= 1 + friend Layer *make_dither_layer(Layer *refLayer, double bottom, double top); Layer(size_t id, PrintObject *object, coordf_t height, coordf_t print_z, coordf_t slice_z) : upper_layer(nullptr), lower_layer(nullptr), slicing_errors(false), @@ -187,7 +190,7 @@ class Layer LayerRegionPtrs m_regions; }; -class SupportLayer : public Layer +class SupportLayer : public Layer { public: // Polygons covered by the supports: base, interface and contact areas. @@ -216,12 +219,14 @@ class SupportLayer : public Layer }; template -inline std::vector zs_from_layers(const LayerContainer &layers) +inline std::vector zs_from_layers(const LayerContainer &layers, bool exclude_dithered = true) { std::vector zs; zs.reserve(layers.size()); - for (const Layer *l : layers) - zs.emplace_back((float)l->slice_z); + for (const Layer *l : layers) { + if (!(l->dithered && exclude_dithered)) + zs.emplace_back((float) l->slice_z); + } return zs; } diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index f171cb14dd1..756260d5398 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -412,7 +412,7 @@ void Preset::set_visible_from_appconfig(const AppConfig &app_config) for (auto it = this->renamed_from.begin(); ! is_visible && it != this->renamed_from.end(); ++ it) is_visible = has(*it); } - else + else is_visible = false; } } @@ -438,7 +438,7 @@ static std::vector s_Preset_print_options { "raft_layers", "raft_first_layer_density", "raft_first_layer_expansion", "raft_contact_distance", "raft_expansion", "support_material_pattern", "support_material_with_sheath", "support_material_spacing", "support_material_closing_radius", "support_material_style", "support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers", "support_material_bottom_interface_layers", - "support_material_interface_pattern", "support_material_interface_spacing", "support_material_interface_contact_loops", + "support_material_interface_pattern", "support_material_interface_spacing", "support_material_interface_contact_loops", "support_material_contact_distance", "support_material_bottom_contact_distance", "support_material_buildplate_only", "dont_support_bridges", "thick_bridges", "notes", "complete_objects", "extruder_clearance_radius", "extruder_clearance_height", "gcode_comments", "gcode_label_objects", "output_filename_format", "post_process", "gcode_substitutions", "perimeter_extruder", @@ -448,7 +448,7 @@ static std::vector s_Preset_print_options { "top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "infill_anchor", "infill_anchor_max", "bridge_flow_ratio", "clip_multipart_objects", "elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "gcode_resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim_width", "wipe_tower_bridging", "single_extruder_multi_material_priming", "mmu_segmented_region_max_width", - "wipe_tower_no_sparse_layers", "compatible_printers", "compatible_printers_condition", "inherits" + "wipe_tower_no_sparse_layers", "compatible_printers", "compatible_printers_condition", "inherits", "z_dither" }; static std::vector s_Preset_filament_options { @@ -644,7 +644,7 @@ void PresetCollection::add_default_preset(const std::vector &keys, // Load all presets found in dir_path. // Throws an exception on error. void PresetCollection::load_presets( - const std::string &dir_path, const std::string &subdir, + const std::string &dir_path, const std::string &subdir, PresetsConfigSubstitutions& substitutions, ForwardCompatibilitySubstitutionRule substitution_rule) { // Don't use boost::filesystem::canonical() on Windows, it is broken in regard to reparse points, @@ -1610,7 +1610,7 @@ PhysicalPrinterCollection::PhysicalPrinterCollection( const std::vector; PrintRegion::PrintRegion(const PrintRegionConfig &config) : PrintRegion(config, config.hash()) {} PrintRegion::PrintRegion(PrintRegionConfig &&config) : PrintRegion(std::move(config), config.hash()) {} -void Print::clear() +void Print::clear() { std::scoped_lock lock(this->state_mutex()); // The following call should stop background processing if it is running. @@ -216,7 +216,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n //FIXME Killing supports on any change of "filament_soluble" is rough. We should check for each object whether that is necessary. osteps.emplace_back(posSupportMaterial); } else if ( - opt_key == "first_layer_extrusion_width" + opt_key == "first_layer_extrusion_width" || opt_key == "min_layer_height" || opt_key == "max_layer_height" || opt_key == "gcode_resolution") { @@ -305,7 +305,7 @@ std::vector Print::support_material_extruders() const if (support_uses_current_extruder) // Add all object extruders to the support extruders as it is not know which one will be used to print supports. append(extruders, this->object_extruders()); - + sort_remove_duplicates(extruders); return extruders; } @@ -335,9 +335,9 @@ double Print::max_allowed_layer_height() const return nozzle_diameter_max; } -std::vector Print::print_object_ids() const -{ - std::vector out; +std::vector Print::print_object_ids() const +{ + std::vector out; // Reserve one more for the caller to append the ID of the Print itself. out.reserve(m_objects.size() + 1); for (const PrintObject *print_object : m_objects) @@ -378,7 +378,7 @@ bool Print::sequential_print_horizontal_clearance_valid(const Print& print, Poly // Get convex hull of all printable volumes assigned to this print object. ModelInstance *model_instance0 = print_object->model_object()->instances.front(); if (it_convex_hull == map_model_object_to_convex_hull.end()) { - // Calculate the convex hull of a printable object. + // Calculate the convex hull of a printable object. // Grow convex hull with the clearance margin. // FIXME: Arrangement has different parameters for offsetting (jtMiter, limit 2) // which causes that the warning will be showed after arrangement with the @@ -475,6 +475,13 @@ std::string Print::validate(std::string* warning) const return L("The Spiral Vase option can only be used when printing single material objects."); } + if (m_default_object_config.z_dither) { + if (m_default_region_config.infill_every_layers > 1) + return L("Z-dither slicing option is not compatible with option to combine infills of multiple layers."); + if (extruders.size() > 1) // Is there a better way to check for a possibility of multimaterial printing? + return L("Z-dither slicing option is currently not supported for printers with multiple extruders."); + } + if (this->has_wipe_tower() && ! m_objects.empty()) { // Make sure all extruders use same diameter filament and have the same nozzle diameter // EPSILON comparison is used for nozzles and 10 % tolerance is used for filaments @@ -500,7 +507,7 @@ std::string Print::validate(std::string* warning) const return L("The Wipe Tower currently does not support volumetric E (use_volumetric_e=0)."); if (m_config.complete_objects && extruders.size() > 1) return L("The Wipe Tower is currently not supported for multimaterial sequential prints."); - + if (m_objects.size() > 1) { bool has_custom_layering = false; std::vector> layer_height_profiles; @@ -558,7 +565,7 @@ std::string Print::validate(std::string* warning) const } } } - + { // Find the smallest used nozzle diameter and the number of unique nozzle diameters. double min_nozzle_diameter = std::numeric_limits::max(); @@ -624,7 +631,7 @@ std::string Print::validate(std::string* warning) const // Notify the user in that case. if (! object->has_support() && warning) { for (const ModelVolume* mv : object->model_object()->volumes) { - bool has_enforcers = mv->is_support_enforcer() || + bool has_enforcers = mv->is_support_enforcer() || (mv->is_model_part() && mv->supported_facets.has_facets(*mv, EnforcerBlockerType::ENFORCER)); if (has_enforcers) { *warning = "_SUPPORTS_OFF"; @@ -642,8 +649,8 @@ std::string Print::validate(std::string* warning) const size_t first_layer_extruder = object->config().raft_layers == 1 ? object->config().support_material_interface_extruder-1 : object->config().support_material_extruder-1; - first_layer_min_nozzle_diameter = (first_layer_extruder == size_t(-1)) ? - min_nozzle_diameter : + first_layer_min_nozzle_diameter = (first_layer_extruder == size_t(-1)) ? + min_nozzle_diameter : m_config.nozzle_diameter.get_at(first_layer_extruder); } else { // if we don't have raft layers, any nozzle diameter is potentially used in first layer @@ -651,7 +658,7 @@ std::string Print::validate(std::string* warning) const } if (first_layer_height > first_layer_min_nozzle_diameter) return L("First layer height can't be greater than nozzle diameter"); - + // validate layer_height double layer_height = object->config().layer_height.value; if (layer_height > min_nozzle_diameter) @@ -708,16 +715,16 @@ BoundingBox Print::total_bounding_box() const { // get objects bounding box BoundingBox bb = this->bounding_box(); - + // we need to offset the objects bounding box by at least half the perimeters extrusion width Flow perimeter_flow = m_objects.front()->get_layer(0)->get_region(0)->flow(frPerimeter); double extra = perimeter_flow.width/2; - + // consider support material if (this->has_support_material()) { extra = std::max(extra, SUPPORT_MATERIAL_MARGIN); } - + // consider brim and skirt if (m_config.brim_width.value > 0) { Flow brim_flow = this->brim_flow(); @@ -735,10 +742,10 @@ BoundingBox Print::total_bounding_box() const + skirt_flow.width/2 ); } - + if (extra > 0) bb.offset(scale_(extra)); - + return bb; } #endif @@ -752,11 +759,11 @@ double Print::skirt_first_layer_height() const Flow Print::brim_flow() const { ConfigOptionFloatOrPercent width = m_config.first_layer_extrusion_width; - if (width.value == 0) + if (width.value == 0) width = m_print_regions.front()->config().perimeter_extrusion_width; - if (width.value == 0) + if (width.value == 0) width = m_objects.front()->config().extrusion_width; - + /* We currently use a random region's perimeter extruder. While this works for most cases, we should probably consider all of the perimeter extruders and take the one with, say, the smallest index. @@ -772,11 +779,11 @@ Flow Print::brim_flow() const Flow Print::skirt_flow() const { ConfigOptionFloatOrPercent width = m_config.first_layer_extrusion_width; - if (width.value == 0) + if (width.value == 0) width = m_print_regions.front()->config().perimeter_extrusion_width; if (width.value == 0) width = m_objects.front()->config().extrusion_width; - + /* We currently use a random object's support material extruder. While this works for most cases, we should probably consider all of the support material extruders and take the one with, say, the smallest index; @@ -792,7 +799,7 @@ Flow Print::skirt_flow() const bool Print::has_support_material() const { for (const PrintObject *object : m_objects) - if (object->has_support_material()) + if (object->has_support_material()) return true; return false; } @@ -804,7 +811,7 @@ void Print::auto_assign_extruders(ModelObject* model_object) const // only assign extruders if object has more than one volume if (model_object->volumes.size() < 2) return; - + // size_t extruders = m_config.nozzle_diameter.values.size(); for (size_t volume_id = 0; volume_id < model_object->volumes.size(); ++ volume_id) { ModelVolume *volume = model_object->volumes[volume_id]; @@ -920,11 +927,11 @@ void Print::_make_skirt() coordf_t skirt_height_z = 0.; for (const PrintObject *object : m_objects) { size_t skirt_layers = this->has_infinite_skirt() ? - object->layer_count() : + object->layer_count() : std::min(size_t(m_config.skirt_height.value), object->layer_count()); skirt_height_z = std::max(skirt_height_z, object->m_layers[skirt_layers-1]->print_z); } - + // Collect points from all layers contained in skirt height. Points points; for (const PrintObject *object : m_objects) { @@ -962,10 +969,10 @@ void Print::_make_skirt() if (points.size() < 3) // At least three points required for a convex hull. return; - + this->throw_if_canceled(); Polygon convex_hull = Slic3r::Geometry::convex_hull(points); - + // Skirt may be printed on several layers, having distinct layer heights, // but loops must be aligned so can't vary width/spacing // TODO: use each extruder's own flow @@ -973,7 +980,7 @@ void Print::_make_skirt() Flow flow = this->skirt_flow(); float spacing = flow.spacing(); double mm3_per_mm = flow.mm3_per_mm(); - + std::vector extruders; std::vector extruders_e_per_mm; { @@ -1102,9 +1109,9 @@ void Print::finalize_first_layer_convex_hull() // Wipe tower support. bool Print::has_wipe_tower() const { - return + return ! m_config.spiral_vase.value && - m_config.wipe_tower.value && + m_config.wipe_tower.value && m_config.nozzle_diameter.values.size() > 1; } @@ -1257,8 +1264,8 @@ void Print::_make_wipe_tower() // Generate a recommended G-code output file name based on the format template, default extension, and template parameters // (timestamps, object placeholders derived from the model, current placeholder prameters and print statistics. // Use the final print statistics if available, or just keep the print statistics placeholders if not available yet (before G-code is finalized). -std::string Print::output_filename(const std::string &filename_base) const -{ +std::string Print::output_filename(const std::string &filename_base) const +{ // Set the placeholders for the data know first after the G-code export is finished. // These values will be just propagated into the output file name. DynamicConfig config = this->finished() ? this->print_statistics().config() : this->print_statistics().placeholders(); @@ -1287,16 +1294,16 @@ DynamicConfig PrintStatistics::config() const config.set_key_value("printing_filament_types", new ConfigOptionString(this->printing_filament_types)); config.set_key_value("num_printing_extruders", new ConfigOptionInt(int(this->printing_extruders.size()))); // config.set_key_value("printing_extruders", new ConfigOptionInts(std::vector(this->printing_extruders.begin(), this->printing_extruders.end()))); - + return config; } DynamicConfig PrintStatistics::placeholders() { DynamicConfig config; - for (const std::string &key : { - "print_time", "normal_print_time", "silent_print_time", - "used_filament", "extruded_volume", "total_cost", "total_weight", + for (const std::string &key : { + "print_time", "normal_print_time", "silent_print_time", + "used_filament", "extruded_volume", "total_cost", "total_weight", "total_toolchanges", "total_wipe_tower_cost", "total_wipe_tower_filament", "initial_tool", "initial_extruder", "initial_filament_type", "printing_filament_types", "num_printing_extruders" }) config.set_key_value(key, new ConfigOptionString(std::string("{") + key + "}")); diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 1cd983fb91e..ec184c59b66 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -99,7 +99,7 @@ class PrintRegion public: void set_config(const PrintRegionConfig &config) { m_config = config; m_config_hash = m_config.hash(); } void set_config(PrintRegionConfig &&config) { m_config = std::move(config); m_config_hash = m_config.hash(); } - void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) + void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) { m_config.apply_only(other, keys, ignore_nonexistent); m_config_hash = m_config.hash(); } private: friend Print; @@ -255,12 +255,12 @@ class PrintObject : public PrintObjectBaseWithStatetrafo(); t.pretranslate(Vec3d(- unscale(m_center_offset.x()), - unscale(m_center_offset.y()), 0)); return t; } const PrintInstances& instances() const { return m_instances; } @@ -309,7 +309,7 @@ class PrintObject : public PrintObjectBaseWithState &layer_height_profile); @@ -385,6 +385,8 @@ class PrintObject : public PrintObjectBaseWithState // methods for handling state bool is_step_done(PrintStep step) const { return Inherited::is_step_done(step); } - // Returns true if an object step is done on all objects and there's at least one object. + // Returns true if an object step is done on all objects and there's at least one object. bool is_step_done(PrintObjectStep step) const; // Returns true if the last step was finished with success. bool finished() const override { return this->is_step_done(psGCodeExport); } @@ -562,7 +564,7 @@ class Print : public PrintBaseWithState double skirt_first_layer_height() const; Flow brim_flow() const; Flow skirt_flow() const; - + std::vector object_extruders() const; std::vector support_material_extruders() const; std::vector extruders() const; @@ -579,8 +581,8 @@ class Print : public PrintBaseWithState const PrintObject* get_object(size_t idx) const { return m_objects[idx]; } // PrintObject by its ObjectID, to be used to uniquely bind slicing warnings to their source PrintObjects // in the notification center. - const PrintObject* get_object(ObjectID object_id) const { - auto it = std::find_if(m_objects.begin(), m_objects.end(), + const PrintObject* get_object(ObjectID object_id) const { + auto it = std::find_if(m_objects.begin(), m_objects.end(), [object_id](const PrintObject *obj) { return obj->id() == object_id; }); return (it == m_objects.end()) ? nullptr : *it; } @@ -588,7 +590,7 @@ class Print : public PrintBaseWithState // If zero, then the print is empty and the print shall not be executed. unsigned int num_object_instances() const; - // For Perl bindings. + // For Perl bindings. PrintObjectPtrs& objects_mutable() { return m_objects; } PrintRegionPtrs& print_regions_mutable() { return m_print_regions; } diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index b21ed563180..3970f0fa1d2 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -309,7 +309,7 @@ void PrintConfigDef::init_common_params() def->mode = comAdvanced; def->cli = ConfigOptionDef::nocli; def->set_default_value(new ConfigOptionString("")); - + def = this->add("printhost_port", coString); def->label = L("Printer"); def->tooltip = L("Name of the printer"); @@ -317,7 +317,7 @@ void PrintConfigDef::init_common_params() def->mode = comAdvanced; def->cli = ConfigOptionDef::nocli; def->set_default_value(new ConfigOptionString("")); - + def = this->add("printhost_cafile", coString); def->label = L("HTTPS CA File"); def->tooltip = L("Custom CA certificate file can be specified for HTTPS OctoPrint connections, in crt/pem format. " @@ -325,16 +325,16 @@ void PrintConfigDef::init_common_params() def->mode = comAdvanced; def->cli = ConfigOptionDef::nocli; def->set_default_value(new ConfigOptionString("")); - + // Options used by physical printers - + def = this->add("printhost_user", coString); def->label = L("User"); // def->tooltip = L(""); def->mode = comAdvanced; def->cli = ConfigOptionDef::nocli; def->set_default_value(new ConfigOptionString("")); - + def = this->add("printhost_password", coString); def->label = L("Password"); // def->tooltip = L(""); @@ -350,7 +350,7 @@ void PrintConfigDef::init_common_params() def->mode = comAdvanced; def->cli = ConfigOptionDef::nocli; def->set_default_value(new ConfigOptionBool(false)); - + def = this->add("preset_names", coStrings); def->label = L("Printer preset names"); def->tooltip = L("Names of presets related to the physical printer"); @@ -2298,6 +2298,14 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionInt(1)); + def = this->add("z_dither", coBool); + def->label = L("Z-dither"); + def->category = L("Layers and Perimeters"); + def->tooltip = L("This experimental setting is used to halve stairstep effect on low slope surfaces. " + "Near slice periphery it introduces additional layers that are 25% and 50% of nominal layer height."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + def = this->add("slowdown_below_layer_time", coInts); def->label = L("Slow down if layer print time is below"); def->tooltip = L("If layer print time is estimated below this number of seconds, print moves " @@ -3230,7 +3238,7 @@ void PrintConfigDef::init_sla_params() "to the sign of the correction."); def->mode = comExpert; def->set_default_value(new ConfigOptionFloat(0.0)); - + def = this->add("elefant_foot_min_width", coFloat); def->label = L("Elephant foot minimum width"); def->category = L("Advanced"); @@ -3480,7 +3488,7 @@ void PrintConfigDef::init_sla_params() def->max = 100; def->mode = comExpert; def->set_default_value(new ConfigOptionPercent(50)); - + def = this->add("support_max_bridges_on_pillar", coInt); def->label = L("Max bridges on a pillar"); def->tooltip = L( @@ -3643,7 +3651,7 @@ void PrintConfigDef::init_sla_params() def->max = 30; def->mode = comExpert; def->set_default_value(new ConfigOptionFloat(0.)); - + def = this->add("pad_brim_size", coFloat); def->label = L("Pad brim size"); def->tooltip = L("How far should the pad extend around the contained geometry"); @@ -3694,7 +3702,7 @@ void PrintConfigDef::init_sla_params() def->tooltip = L("Create pad around object and ignore the support elevation"); def->mode = comSimple; def->set_default_value(new ConfigOptionBool(false)); - + def = this->add("pad_around_object_everywhere", coBool); def->label = L("Pad around object everywhere"); def->category = L("Pad"); @@ -3740,14 +3748,14 @@ void PrintConfigDef::init_sla_params() def->min = 0; def->mode = comExpert; def->set_default_value(new ConfigOptionFloat(0.3)); - + def = this->add("hollowing_enable", coBool); def->label = L("Enable hollowing"); def->category = L("Hollowing"); def->tooltip = L("Hollow out a model to have an empty interior"); def->mode = comSimple; def->set_default_value(new ConfigOptionBool(false)); - + def = this->add("hollowing_min_thickness", coFloat); def->label = L("Wall thickness"); def->category = L("Hollowing"); @@ -3757,7 +3765,7 @@ void PrintConfigDef::init_sla_params() def->max = 10; def->mode = comSimple; def->set_default_value(new ConfigOptionFloat(3.)); - + def = this->add("hollowing_quality", coFloat); def->label = L("Accuracy"); def->category = L("Hollowing"); @@ -3766,7 +3774,7 @@ void PrintConfigDef::init_sla_params() def->max = 1; def->mode = comExpert; def->set_default_value(new ConfigOptionFloat(0.5)); - + def = this->add("hollowing_closing_distance", coFloat); def->label = L("Closing distance"); def->category = L("Hollowing"); @@ -3932,7 +3940,7 @@ DynamicPrintConfig* DynamicPrintConfig::new_from_defaults_keys(const std::vector } double min_object_distance(const ConfigBase &cfg) -{ +{ const ConfigOptionEnum *opt_printer_technology = cfg.option>("printer_technology"); auto printer_technology = opt_printer_technology ? opt_printer_technology->value : ptUnknown; @@ -3945,7 +3953,7 @@ double min_object_distance(const ConfigBase &cfg) auto dd_opt = cfg.option("duplicate_distance"); auto co_opt = cfg.option("complete_objects"); - if (!ecr_opt || !dd_opt || !co_opt) + if (!ecr_opt || !dd_opt || !co_opt) ret = 0.; else { // min object distance is max(duplicate_distance, clearance_radius) @@ -4250,7 +4258,7 @@ std::string validate(const FullPrintConfig &cfg) return 1; \ } PRINT_CONFIG_CACHE_INITIALIZE(( - PrintObjectConfig, PrintRegionConfig, MachineEnvelopeConfig, GCodeConfig, PrintConfig, FullPrintConfig, + PrintObjectConfig, PrintRegionConfig, MachineEnvelopeConfig, GCodeConfig, PrintConfig, FullPrintConfig, SLAMaterialConfig, SLAPrintConfig, SLAPrintObjectConfig, SLAPrinterConfig, SLAFullPrintConfig)) static int print_config_static_initialized = print_config_static_initializer(); @@ -4514,22 +4522,22 @@ static Points to_points(const std::vector &dpts) Points pts; pts.reserve(dpts.size()); for (auto &v : dpts) pts.emplace_back( coord_t(scale_(v.x())), coord_t(scale_(v.y())) ); - return pts; + return pts; } Points get_bed_shape(const DynamicPrintConfig &config) { const auto *bed_shape_opt = config.opt("bed_shape"); if (!bed_shape_opt) { - + // Here, it is certain that the bed shape is missing, so an infinite one // has to be used, but still, the center of bed can be queried if (auto center_opt = config.opt("center")) return { scaled(center_opt->value) }; - + return {}; } - + return to_points(bed_shape_opt->values); } diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 0c1060b7de6..cd958595d38 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -32,108 +32,108 @@ namespace Slic3r { enum GCodeFlavor : unsigned char { - gcfRepRapSprinter, gcfRepRapFirmware, gcfRepetier, gcfTeacup, gcfMakerWare, gcfMarlinLegacy, gcfMarlinFirmware, gcfSailfish, gcfMach3, gcfMachinekit, - gcfSmoothie, gcfNoExtrusion, + gcfRepRapSprinter, gcfRepRapFirmware, gcfRepetier, gcfTeacup, gcfMakerWare, gcfMarlinLegacy, gcfMarlinFirmware, gcfSailfish, gcfMach3, gcfMachinekit, + gcfSmoothie, gcfNoExtrusion, }; enum class MachineLimitsUsage { - EmitToGCode, - TimeEstimateOnly, - Ignore, - Count, + EmitToGCode, + TimeEstimateOnly, + Ignore, + Count, }; enum PrintHostType { - htPrusaLink, htOctoPrint, htDuet, htFlashAir, htAstroBox, htRepetier, htMKS + htPrusaLink, htOctoPrint, htDuet, htFlashAir, htAstroBox, htRepetier, htMKS }; enum AuthorizationType { - atKeyPassword, atUserPassword + atKeyPassword, atUserPassword }; enum class FuzzySkinType { - None, - External, - All, + None, + External, + All, }; enum InfillPattern : int { - ipRectilinear, ipMonotonic, ipAlignedRectilinear, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb, - ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipAdaptiveCubic, ipSupportCubic, ipSupportBase, - ipLightning, + ipRectilinear, ipMonotonic, ipAlignedRectilinear, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb, + ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipAdaptiveCubic, ipSupportCubic, ipSupportBase, + ipLightning, ipCount, }; enum class IroningType { - TopSurfaces, - TopmostOnly, - AllSolid, - Count, + TopSurfaces, + TopmostOnly, + AllSolid, + Count, }; enum class SlicingMode { - // Regular, applying ClipperLib::pftNonZero rule when creating ExPolygons. - Regular, - // Compatible with 3DLabPrint models, applying ClipperLib::pftEvenOdd rule when creating ExPolygons. - EvenOdd, - // Orienting all contours CCW, thus closing all holes. - CloseHoles, + // Regular, applying ClipperLib::pftNonZero rule when creating ExPolygons. + Regular, + // Compatible with 3DLabPrint models, applying ClipperLib::pftEvenOdd rule when creating ExPolygons. + EvenOdd, + // Orienting all contours CCW, thus closing all holes. + CloseHoles, }; enum SupportMaterialPattern { - smpRectilinear, smpRectilinearGrid, smpHoneycomb, + smpRectilinear, smpRectilinearGrid, smpHoneycomb, }; enum SupportMaterialStyle { - smsGrid, smsSnug, + smsGrid, smsSnug, }; enum SupportMaterialInterfacePattern { - smipAuto, smipRectilinear, smipConcentric, + smipAuto, smipRectilinear, smipConcentric, }; enum SeamPosition { - spRandom, spNearest, spAligned, spRear + spRandom, spNearest, spAligned, spRear }; enum SLAMaterial { - slamTough, - slamFlex, - slamCasting, - slamDental, - slamHeatResistant, + slamTough, + slamFlex, + slamCasting, + slamDental, + slamHeatResistant, }; enum SLADisplayOrientation { - sladoLandscape, - sladoPortrait + sladoLandscape, + sladoPortrait }; enum SLAPillarConnectionMode { - slapcmZigZag, - slapcmCross, - slapcmDynamic + slapcmZigZag, + slapcmCross, + slapcmDynamic }; enum BrimType { - btNoBrim, - btOuterOnly, - btInnerOnly, - btOuterAndInner, + btNoBrim, + btOuterOnly, + btInnerOnly, + btOuterAndInner, }; enum DraftShield { - dsDisabled, dsLimited, dsEnabled + dsDisabled, dsLimited, dsEnabled }; enum class GCodeThumbnailsFormat { - PNG, JPG, QOI + PNG, JPG, QOI }; #define CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(NAME) \ - template<> const t_config_enum_names& ConfigOptionEnum::get_enum_names(); \ - template<> const t_config_enum_values& ConfigOptionEnum::get_enum_values(); + 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(PrinterTechnology) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(GCodeFlavor) @@ -162,25 +162,25 @@ CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(ForwardCompatibilitySubstitutionRule) class PrintConfigDef : public ConfigDef { public: - PrintConfigDef(); + PrintConfigDef(); - static void handle_legacy(t_config_option_key &opt_key, std::string &value); + static void handle_legacy(t_config_option_key &opt_key, std::string &value); - // Array options growing with the number of extruders - const std::vector& extruder_option_keys() const { return m_extruder_option_keys; } - // Options defining the extruder retract properties. These keys are sorted lexicographically. - // The extruder retract keys could be overidden by the same values defined at the Filament level - // (then the key is further prefixed with the "filament_" prefix). - const std::vector& extruder_retract_keys() const { return m_extruder_retract_keys; } + // Array options growing with the number of extruders + const std::vector& extruder_option_keys() const { return m_extruder_option_keys; } + // Options defining the extruder retract properties. These keys are sorted lexicographically. + // The extruder retract keys could be overidden by the same values defined at the Filament level + // (then the key is further prefixed with the "filament_" prefix). + const std::vector& extruder_retract_keys() const { return m_extruder_retract_keys; } private: - void init_common_params(); - void init_fff_params(); - void init_extruder_option_keys(); - void init_sla_params(); + void init_common_params(); + void init_fff_params(); + void init_extruder_option_keys(); + void init_sla_params(); - std::vector m_extruder_option_keys; - std::vector m_extruder_retract_keys; + std::vector m_extruder_option_keys; + std::vector m_extruder_retract_keys; }; // The one and only global definition of SLic3r configuration options. @@ -200,54 +200,54 @@ double min_object_distance(const ConfigBase &cfg); class DynamicPrintConfig : public DynamicConfig { public: - DynamicPrintConfig() {} - DynamicPrintConfig(const DynamicPrintConfig &rhs) : DynamicConfig(rhs) {} - DynamicPrintConfig(DynamicPrintConfig &&rhs) noexcept : DynamicConfig(std::move(rhs)) {} - explicit DynamicPrintConfig(const StaticPrintConfig &rhs); - explicit DynamicPrintConfig(const ConfigBase &rhs) : DynamicConfig(rhs) {} - - DynamicPrintConfig& operator=(const DynamicPrintConfig &rhs) { DynamicConfig::operator=(rhs); return *this; } - DynamicPrintConfig& operator=(DynamicPrintConfig &&rhs) noexcept { DynamicConfig::operator=(std::move(rhs)); return *this; } - - static DynamicPrintConfig full_print_config(); - static DynamicPrintConfig full_print_config_with(const t_config_option_key &opt_key, const std::string &str, bool append = false) { - auto config = DynamicPrintConfig::full_print_config(); - config.set_deserialize_strict(opt_key, str, append); - return config; - } - static DynamicPrintConfig full_print_config_with(std::initializer_list items) { - auto config = DynamicPrintConfig::full_print_config(); - config.set_deserialize_strict(items); - return config; - } - static DynamicPrintConfig new_with(const t_config_option_key &opt_key, const std::string &str, bool append = false) { - DynamicPrintConfig config; - config.set_deserialize_strict(opt_key, str, append); - return config; - } - static DynamicPrintConfig new_with(std::initializer_list items) { - DynamicPrintConfig config; - config.set_deserialize_strict(items); - return config; - } - static DynamicPrintConfig* new_from_defaults_keys(const std::vector &keys); - - // Overrides ConfigBase::def(). Static configuration definition. Any value stored into this ConfigBase shall have its definition here. - const ConfigDef* def() const override { return &print_config_def; } - - void normalize_fdm(); - - void set_num_extruders(unsigned int num_extruders); - - // Validate the PrintConfig. Returns an empty string on success, otherwise an error message is returned. - std::string validate(); - - // Verify whether the opt_key has not been obsoleted or renamed. - // Both opt_key and value may be modified by handle_legacy(). - // If the opt_key is no more valid in this version of Slic3r, opt_key is cleared by handle_legacy(). - // handle_legacy() is called internally by set_deserialize(). - void handle_legacy(t_config_option_key &opt_key, std::string &value) const override - { PrintConfigDef::handle_legacy(opt_key, value); } + DynamicPrintConfig() {} + DynamicPrintConfig(const DynamicPrintConfig &rhs) : DynamicConfig(rhs) {} + DynamicPrintConfig(DynamicPrintConfig &&rhs) noexcept : DynamicConfig(std::move(rhs)) {} + explicit DynamicPrintConfig(const StaticPrintConfig &rhs); + explicit DynamicPrintConfig(const ConfigBase &rhs) : DynamicConfig(rhs) {} + + DynamicPrintConfig& operator=(const DynamicPrintConfig &rhs) { DynamicConfig::operator=(rhs); return *this; } + DynamicPrintConfig& operator=(DynamicPrintConfig &&rhs) noexcept { DynamicConfig::operator=(std::move(rhs)); return *this; } + + static DynamicPrintConfig full_print_config(); + static DynamicPrintConfig full_print_config_with(const t_config_option_key &opt_key, const std::string &str, bool append = false) { + auto config = DynamicPrintConfig::full_print_config(); + config.set_deserialize_strict(opt_key, str, append); + return config; + } + static DynamicPrintConfig full_print_config_with(std::initializer_list items) { + auto config = DynamicPrintConfig::full_print_config(); + config.set_deserialize_strict(items); + return config; + } + static DynamicPrintConfig new_with(const t_config_option_key &opt_key, const std::string &str, bool append = false) { + DynamicPrintConfig config; + config.set_deserialize_strict(opt_key, str, append); + return config; + } + static DynamicPrintConfig new_with(std::initializer_list items) { + DynamicPrintConfig config; + config.set_deserialize_strict(items); + return config; + } + static DynamicPrintConfig* new_from_defaults_keys(const std::vector &keys); + + // Overrides ConfigBase::def(). Static configuration definition. Any value stored into this ConfigBase shall have its definition here. + const ConfigDef* def() const override { return &print_config_def; } + + void normalize_fdm(); + + void set_num_extruders(unsigned int num_extruders); + + // Validate the PrintConfig. Returns an empty string on success, otherwise an error message is returned. + std::string validate(); + + // Verify whether the opt_key has not been obsoleted or renamed. + // Both opt_key and value may be modified by handle_legacy(). + // If the opt_key is no more valid in this version of Slic3r, opt_key is cleared by handle_legacy(). + // handle_legacy() is called internally by set_deserialize(). + void handle_legacy(t_config_option_key &opt_key, std::string &value) const override + { PrintConfigDef::handle_legacy(opt_key, value); } }; void handle_legacy_sla(DynamicPrintConfig &config); @@ -255,136 +255,136 @@ void handle_legacy_sla(DynamicPrintConfig &config); class StaticPrintConfig : public StaticConfig { public: - StaticPrintConfig() {} + StaticPrintConfig() {} - // Overrides ConfigBase::def(). Static configuration definition. Any value stored into this ConfigBase shall have its definition here. - const ConfigDef* def() const override { return &print_config_def; } - // Reference to the cached list of keys. - virtual const t_config_option_keys& keys_ref() const = 0; + // Overrides ConfigBase::def(). Static configuration definition. Any value stored into this ConfigBase shall have its definition here. + const ConfigDef* def() const override { return &print_config_def; } + // Reference to the cached list of keys. + virtual const t_config_option_keys& keys_ref() const = 0; protected: - // Verify whether the opt_key has not been obsoleted or renamed. - // Both opt_key and value may be modified by handle_legacy(). - // If the opt_key is no more valid in this version of Slic3r, opt_key is cleared by handle_legacy(). - // handle_legacy() is called internally by set_deserialize(). - void handle_legacy(t_config_option_key &opt_key, std::string &value) const override - { PrintConfigDef::handle_legacy(opt_key, value); } - - // Internal class for keeping a dynamic map to static options. - class StaticCacheBase - { - public: - // To be called during the StaticCache setup. - // Add one ConfigOption into m_map_name_to_offset. - template - void opt_add(const std::string &name, const char *base_ptr, const T &opt) - { - assert(m_map_name_to_offset.find(name) == m_map_name_to_offset.end()); - m_map_name_to_offset[name] = (const char*)&opt - base_ptr; - } - - protected: - std::map m_map_name_to_offset; - }; - - // Parametrized by the type of the topmost class owning the options. - template - class StaticCache : public StaticCacheBase - { - public: - // Calling the constructor of m_defaults with 0 forces m_defaults to not run the initialization. - StaticCache() : m_defaults(nullptr) {} - ~StaticCache() { delete m_defaults; m_defaults = nullptr; } - - bool initialized() const { return ! m_keys.empty(); } - - ConfigOption* optptr(const std::string &name, T *owner) const - { - const auto it = m_map_name_to_offset.find(name); - return (it == m_map_name_to_offset.end()) ? nullptr : reinterpret_cast((char*)owner + it->second); - } - - const ConfigOption* optptr(const std::string &name, const T *owner) const - { - const auto it = m_map_name_to_offset.find(name); - return (it == m_map_name_to_offset.end()) ? nullptr : reinterpret_cast((const char*)owner + it->second); - } - - const std::vector& keys() const { return m_keys; } - const T& defaults() const { return *m_defaults; } - - // To be called during the StaticCache setup. - // Collect option keys from m_map_name_to_offset, - // assign default values to m_defaults. - void finalize(T *defaults, const ConfigDef *defs) - { - assert(defs != nullptr); - m_defaults = defaults; - m_keys.clear(); - m_keys.reserve(m_map_name_to_offset.size()); - for (const auto &kvp : defs->options) { - // Find the option given the option name kvp.first by an offset from (char*)m_defaults. - ConfigOption *opt = this->optptr(kvp.first, m_defaults); - if (opt == nullptr) - // This option is not defined by the ConfigBase of type T. - continue; - m_keys.emplace_back(kvp.first); - const ConfigOptionDef *def = defs->get(kvp.first); - assert(def != nullptr); - if (def->default_value) - opt->set(def->default_value.get()); - } - } - - private: - T *m_defaults; - std::vector m_keys; - }; + // Verify whether the opt_key has not been obsoleted or renamed. + // Both opt_key and value may be modified by handle_legacy(). + // If the opt_key is no more valid in this version of Slic3r, opt_key is cleared by handle_legacy(). + // handle_legacy() is called internally by set_deserialize(). + void handle_legacy(t_config_option_key &opt_key, std::string &value) const override + { PrintConfigDef::handle_legacy(opt_key, value); } + + // Internal class for keeping a dynamic map to static options. + class StaticCacheBase + { + public: + // To be called during the StaticCache setup. + // Add one ConfigOption into m_map_name_to_offset. + template + void opt_add(const std::string &name, const char *base_ptr, const T &opt) + { + assert(m_map_name_to_offset.find(name) == m_map_name_to_offset.end()); + m_map_name_to_offset[name] = (const char*)&opt - base_ptr; + } + + protected: + std::map m_map_name_to_offset; + }; + + // Parametrized by the type of the topmost class owning the options. + template + class StaticCache : public StaticCacheBase + { + public: + // Calling the constructor of m_defaults with 0 forces m_defaults to not run the initialization. + StaticCache() : m_defaults(nullptr) {} + ~StaticCache() { delete m_defaults; m_defaults = nullptr; } + + bool initialized() const { return ! m_keys.empty(); } + + ConfigOption* optptr(const std::string &name, T *owner) const + { + const auto it = m_map_name_to_offset.find(name); + return (it == m_map_name_to_offset.end()) ? nullptr : reinterpret_cast((char*)owner + it->second); + } + + const ConfigOption* optptr(const std::string &name, const T *owner) const + { + const auto it = m_map_name_to_offset.find(name); + return (it == m_map_name_to_offset.end()) ? nullptr : reinterpret_cast((const char*)owner + it->second); + } + + const std::vector& keys() const { return m_keys; } + const T& defaults() const { return *m_defaults; } + + // To be called during the StaticCache setup. + // Collect option keys from m_map_name_to_offset, + // assign default values to m_defaults. + void finalize(T *defaults, const ConfigDef *defs) + { + assert(defs != nullptr); + m_defaults = defaults; + m_keys.clear(); + m_keys.reserve(m_map_name_to_offset.size()); + for (const auto &kvp : defs->options) { + // Find the option given the option name kvp.first by an offset from (char*)m_defaults. + ConfigOption *opt = this->optptr(kvp.first, m_defaults); + if (opt == nullptr) + // This option is not defined by the ConfigBase of type T. + continue; + m_keys.emplace_back(kvp.first); + const ConfigOptionDef *def = defs->get(kvp.first); + assert(def != nullptr); + if (def->default_value) + opt->set(def->default_value.get()); + } + } + + private: + T *m_defaults; + std::vector m_keys; + }; }; #define STATIC_PRINT_CONFIG_CACHE_BASE(CLASS_NAME) \ public: \ - /* Overrides ConfigBase::optptr(). Find ando/or create a ConfigOption instance for a given name. */ \ - const ConfigOption* optptr(const t_config_option_key &opt_key) const override \ - { return s_cache_##CLASS_NAME.optptr(opt_key, this); } \ - /* Overrides ConfigBase::optptr(). Find ando/or create a ConfigOption instance for a given name. */ \ - ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) override \ - { return s_cache_##CLASS_NAME.optptr(opt_key, this); } \ - /* Overrides ConfigBase::keys(). Collect names of all configuration values maintained by this configuration store. */ \ - t_config_option_keys keys() const override { return s_cache_##CLASS_NAME.keys(); } \ - const t_config_option_keys& keys_ref() const override { return s_cache_##CLASS_NAME.keys(); } \ - static const CLASS_NAME& defaults() { assert(s_cache_##CLASS_NAME.initialized()); return s_cache_##CLASS_NAME.defaults(); } \ + /* Overrides ConfigBase::optptr(). Find ando/or create a ConfigOption instance for a given name. */ \ + const ConfigOption* optptr(const t_config_option_key &opt_key) const override \ + { return s_cache_##CLASS_NAME.optptr(opt_key, this); } \ + /* Overrides ConfigBase::optptr(). Find ando/or create a ConfigOption instance for a given name. */ \ + ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) override \ + { return s_cache_##CLASS_NAME.optptr(opt_key, this); } \ + /* Overrides ConfigBase::keys(). Collect names of all configuration values maintained by this configuration store. */ \ + t_config_option_keys keys() const override { return s_cache_##CLASS_NAME.keys(); } \ + const t_config_option_keys& keys_ref() const override { return s_cache_##CLASS_NAME.keys(); } \ + static const CLASS_NAME& defaults() { assert(s_cache_##CLASS_NAME.initialized()); return s_cache_##CLASS_NAME.defaults(); } \ private: \ - friend int print_config_static_initializer(); \ - static void initialize_cache() \ - { \ - assert(! s_cache_##CLASS_NAME.initialized()); \ - if (! s_cache_##CLASS_NAME.initialized()) { \ - CLASS_NAME *inst = new CLASS_NAME(1); \ - inst->initialize(s_cache_##CLASS_NAME, (const char*)inst); \ - s_cache_##CLASS_NAME.finalize(inst, inst->def()); \ - } \ - } \ - /* Cache object holding a key/option map, a list of option keys and a copy of this static config initialized with the defaults. */ \ - static StaticPrintConfig::StaticCache s_cache_##CLASS_NAME; + friend int print_config_static_initializer(); \ + static void initialize_cache() \ + { \ + assert(! s_cache_##CLASS_NAME.initialized()); \ + if (! s_cache_##CLASS_NAME.initialized()) { \ + CLASS_NAME *inst = new CLASS_NAME(1); \ + inst->initialize(s_cache_##CLASS_NAME, (const char*)inst); \ + s_cache_##CLASS_NAME.finalize(inst, inst->def()); \ + } \ + } \ + /* Cache object holding a key/option map, a list of option keys and a copy of this static config initialized with the defaults. */ \ + static StaticPrintConfig::StaticCache s_cache_##CLASS_NAME; #define STATIC_PRINT_CONFIG_CACHE(CLASS_NAME) \ - STATIC_PRINT_CONFIG_CACHE_BASE(CLASS_NAME) \ + STATIC_PRINT_CONFIG_CACHE_BASE(CLASS_NAME) \ public: \ - /* Public default constructor will initialize the key/option cache and the default object copy if needed. */ \ - CLASS_NAME() { assert(s_cache_##CLASS_NAME.initialized()); *this = s_cache_##CLASS_NAME.defaults(); } \ + /* Public default constructor will initialize the key/option cache and the default object copy if needed. */ \ + CLASS_NAME() { assert(s_cache_##CLASS_NAME.initialized()); *this = s_cache_##CLASS_NAME.defaults(); } \ protected: \ - /* Protected constructor to be called when compounded. */ \ - CLASS_NAME(int) {} + /* Protected constructor to be called when compounded. */ \ + CLASS_NAME(int) {} #define STATIC_PRINT_CONFIG_CACHE_DERIVED(CLASS_NAME) \ - STATIC_PRINT_CONFIG_CACHE_BASE(CLASS_NAME) \ + STATIC_PRINT_CONFIG_CACHE_BASE(CLASS_NAME) \ public: \ - /* Overrides ConfigBase::def(). Static configuration definition. Any value stored into this ConfigBase shall have its definition here. */ \ - const ConfigDef* def() const override { return &print_config_def; } \ - /* Handle legacy and obsoleted config keys */ \ - void handle_legacy(t_config_option_key &opt_key, std::string &value) const override \ - { PrintConfigDef::handle_legacy(opt_key, value); } + /* Overrides ConfigBase::def(). Static configuration definition. Any value stored into this ConfigBase shall have its definition here. */ \ + const ConfigDef* def() const override { return &print_config_def; } \ + /* Handle legacy and obsoleted config keys */ \ + void handle_legacy(t_config_option_key &opt_key, std::string &value) const override \ + { PrintConfigDef::handle_legacy(opt_key, value); } #define PRINT_CONFIG_CLASS_ELEMENT_DEFINITION(r, data, elem) BOOST_PP_TUPLE_ELEM(0, elem) BOOST_PP_TUPLE_ELEM(1, elem); #define PRINT_CONFIG_CLASS_ELEMENT_INITIALIZATION2(KEY) cache.opt_add(BOOST_PP_STRINGIZE(KEY), base_ptr, this->KEY); @@ -392,36 +392,36 @@ public: \ #define PRINT_CONFIG_CLASS_ELEMENT_HASH(r, data, elem) boost::hash_combine(seed, BOOST_PP_TUPLE_ELEM(1, elem).hash()); #define PRINT_CONFIG_CLASS_ELEMENT_EQUAL(r, data, elem) if (! (BOOST_PP_TUPLE_ELEM(1, elem) == rhs.BOOST_PP_TUPLE_ELEM(1, elem))) return false; #define PRINT_CONFIG_CLASS_ELEMENT_LOWER(r, data, elem) \ - if (BOOST_PP_TUPLE_ELEM(1, elem) < rhs.BOOST_PP_TUPLE_ELEM(1, elem)) return true; \ - if (! (BOOST_PP_TUPLE_ELEM(1, elem) == rhs.BOOST_PP_TUPLE_ELEM(1, elem))) return false; + if (BOOST_PP_TUPLE_ELEM(1, elem) < rhs.BOOST_PP_TUPLE_ELEM(1, elem)) return true; \ + if (! (BOOST_PP_TUPLE_ELEM(1, elem) == rhs.BOOST_PP_TUPLE_ELEM(1, elem))) return false; #define PRINT_CONFIG_CLASS_DEFINE(CLASS_NAME, PARAMETER_DEFINITION_SEQ) \ class CLASS_NAME : public StaticPrintConfig { \ - STATIC_PRINT_CONFIG_CACHE(CLASS_NAME) \ + STATIC_PRINT_CONFIG_CACHE(CLASS_NAME) \ public: \ - BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_ELEMENT_DEFINITION, _, PARAMETER_DEFINITION_SEQ) \ - size_t hash() const throw() \ - { \ - size_t seed = 0; \ - BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_ELEMENT_HASH, _, PARAMETER_DEFINITION_SEQ) \ - return seed; \ - } \ - bool operator==(const CLASS_NAME &rhs) const throw() \ - { \ - BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_ELEMENT_EQUAL, _, PARAMETER_DEFINITION_SEQ) \ - return true; \ - } \ - bool operator!=(const CLASS_NAME &rhs) const throw() { return ! (*this == rhs); } \ - bool operator<(const CLASS_NAME &rhs) const throw() \ - { \ - BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_ELEMENT_LOWER, _, PARAMETER_DEFINITION_SEQ) \ - return false; \ - } \ + BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_ELEMENT_DEFINITION, _, PARAMETER_DEFINITION_SEQ) \ + size_t hash() const throw() \ + { \ + size_t seed = 0; \ + BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_ELEMENT_HASH, _, PARAMETER_DEFINITION_SEQ) \ + return seed; \ + } \ + bool operator==(const CLASS_NAME &rhs) const throw() \ + { \ + BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_ELEMENT_EQUAL, _, PARAMETER_DEFINITION_SEQ) \ + return true; \ + } \ + bool operator!=(const CLASS_NAME &rhs) const throw() { return ! (*this == rhs); } \ + bool operator<(const CLASS_NAME &rhs) const throw() \ + { \ + BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_ELEMENT_LOWER, _, PARAMETER_DEFINITION_SEQ) \ + return false; \ + } \ protected: \ - void initialize(StaticCacheBase &cache, const char *base_ptr) \ - { \ - BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_ELEMENT_INITIALIZATION, _, PARAMETER_DEFINITION_SEQ) \ - } \ + void initialize(StaticCacheBase &cache, const char *base_ptr) \ + { \ + BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_ELEMENT_INITIALIZATION, _, PARAMETER_DEFINITION_SEQ) \ + } \ }; #define PRINT_CONFIG_CLASS_DERIVED_CLASS_LIST_ITEM(r, data, i, elem) BOOST_PP_COMMA_IF(i) public elem @@ -432,573 +432,574 @@ protected: \ #define PRINT_CONFIG_CLASS_DERIVED_INITCACHE(CLASSES_PARENTS_TUPLE) BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_DERIVED_INITCACHE_ITEM, _, BOOST_PP_TUPLE_TO_SEQ(CLASSES_PARENTS_TUPLE)) #define PRINT_CONFIG_CLASS_DERIVED_HASH(r, data, elem) boost::hash_combine(seed, static_cast(this)->hash()); #define PRINT_CONFIG_CLASS_DERIVED_EQUAL(r, data, elem) \ - if (! (*static_cast(this) == static_cast(rhs))) return false; + if (! (*static_cast(this) == static_cast(rhs))) return false; // Generic version, with or without new parameters. Don't use this directly. #define PRINT_CONFIG_CLASS_DERIVED_DEFINE1(CLASS_NAME, CLASSES_PARENTS_TUPLE, PARAMETER_DEFINITION, PARAMETER_REGISTRATION, PARAMETER_HASHES, PARAMETER_EQUALS) \ class CLASS_NAME : PRINT_CONFIG_CLASS_DERIVED_CLASS_LIST(CLASSES_PARENTS_TUPLE) { \ - STATIC_PRINT_CONFIG_CACHE_DERIVED(CLASS_NAME) \ - CLASS_NAME() : PRINT_CONFIG_CLASS_DERIVED_INITIALIZER(CLASSES_PARENTS_TUPLE, 0) { assert(s_cache_##CLASS_NAME.initialized()); *this = s_cache_##CLASS_NAME.defaults(); } \ + STATIC_PRINT_CONFIG_CACHE_DERIVED(CLASS_NAME) \ + CLASS_NAME() : PRINT_CONFIG_CLASS_DERIVED_INITIALIZER(CLASSES_PARENTS_TUPLE, 0) { assert(s_cache_##CLASS_NAME.initialized()); *this = s_cache_##CLASS_NAME.defaults(); } \ public: \ - PARAMETER_DEFINITION \ - size_t hash() const throw() \ - { \ - size_t seed = 0; \ - BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_DERIVED_HASH, _, BOOST_PP_TUPLE_TO_SEQ(CLASSES_PARENTS_TUPLE)) \ - PARAMETER_HASHES \ - return seed; \ - } \ - bool operator==(const CLASS_NAME &rhs) const throw() \ - { \ - BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_DERIVED_EQUAL, _, BOOST_PP_TUPLE_TO_SEQ(CLASSES_PARENTS_TUPLE)) \ - PARAMETER_EQUALS \ - return true; \ - } \ - bool operator!=(const CLASS_NAME &rhs) const throw() { return ! (*this == rhs); } \ + PARAMETER_DEFINITION \ + size_t hash() const throw() \ + { \ + size_t seed = 0; \ + BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_DERIVED_HASH, _, BOOST_PP_TUPLE_TO_SEQ(CLASSES_PARENTS_TUPLE)) \ + PARAMETER_HASHES \ + return seed; \ + } \ + bool operator==(const CLASS_NAME &rhs) const throw() \ + { \ + BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_DERIVED_EQUAL, _, BOOST_PP_TUPLE_TO_SEQ(CLASSES_PARENTS_TUPLE)) \ + PARAMETER_EQUALS \ + return true; \ + } \ + bool operator!=(const CLASS_NAME &rhs) const throw() { return ! (*this == rhs); } \ protected: \ - CLASS_NAME(int) : PRINT_CONFIG_CLASS_DERIVED_INITIALIZER(CLASSES_PARENTS_TUPLE, 1) {} \ - void initialize(StaticCacheBase &cache, const char* base_ptr) { \ - PRINT_CONFIG_CLASS_DERIVED_INITCACHE(CLASSES_PARENTS_TUPLE) \ - PARAMETER_REGISTRATION \ - } \ + CLASS_NAME(int) : PRINT_CONFIG_CLASS_DERIVED_INITIALIZER(CLASSES_PARENTS_TUPLE, 1) {} \ + void initialize(StaticCacheBase &cache, const char* base_ptr) { \ + PRINT_CONFIG_CLASS_DERIVED_INITCACHE(CLASSES_PARENTS_TUPLE) \ + PARAMETER_REGISTRATION \ + } \ }; // Variant without adding new parameters. #define PRINT_CONFIG_CLASS_DERIVED_DEFINE0(CLASS_NAME, CLASSES_PARENTS_TUPLE) \ - PRINT_CONFIG_CLASS_DERIVED_DEFINE1(CLASS_NAME, CLASSES_PARENTS_TUPLE, BOOST_PP_EMPTY(), BOOST_PP_EMPTY(), BOOST_PP_EMPTY(), BOOST_PP_EMPTY()) + PRINT_CONFIG_CLASS_DERIVED_DEFINE1(CLASS_NAME, CLASSES_PARENTS_TUPLE, BOOST_PP_EMPTY(), BOOST_PP_EMPTY(), BOOST_PP_EMPTY(), BOOST_PP_EMPTY()) // Variant with adding new parameters. #define PRINT_CONFIG_CLASS_DERIVED_DEFINE(CLASS_NAME, CLASSES_PARENTS_TUPLE, PARAMETER_DEFINITION_SEQ) \ - PRINT_CONFIG_CLASS_DERIVED_DEFINE1(CLASS_NAME, CLASSES_PARENTS_TUPLE, \ - BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_ELEMENT_DEFINITION, _, PARAMETER_DEFINITION_SEQ), \ - BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_ELEMENT_INITIALIZATION, _, PARAMETER_DEFINITION_SEQ), \ - BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_ELEMENT_HASH, _, PARAMETER_DEFINITION_SEQ), \ - BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_ELEMENT_EQUAL, _, PARAMETER_DEFINITION_SEQ)) + PRINT_CONFIG_CLASS_DERIVED_DEFINE1(CLASS_NAME, CLASSES_PARENTS_TUPLE, \ + BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_ELEMENT_DEFINITION, _, PARAMETER_DEFINITION_SEQ), \ + BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_ELEMENT_INITIALIZATION, _, PARAMETER_DEFINITION_SEQ), \ + BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_ELEMENT_HASH, _, PARAMETER_DEFINITION_SEQ), \ + BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_ELEMENT_EQUAL, _, PARAMETER_DEFINITION_SEQ)) PRINT_CONFIG_CLASS_DEFINE( - PrintObjectConfig, - - ((ConfigOptionFloat, brim_separation)) - ((ConfigOptionEnum, brim_type)) - ((ConfigOptionFloat, brim_width)) - ((ConfigOptionBool, clip_multipart_objects)) - ((ConfigOptionBool, dont_support_bridges)) - ((ConfigOptionFloat, elefant_foot_compensation)) - ((ConfigOptionFloatOrPercent, extrusion_width)) - ((ConfigOptionFloat, first_layer_acceleration_over_raft)) - ((ConfigOptionFloatOrPercent, first_layer_speed_over_raft)) - ((ConfigOptionBool, infill_only_where_needed)) - // Force the generation of solid shells between adjacent materials/volumes. - ((ConfigOptionBool, interface_shells)) - ((ConfigOptionFloat, layer_height)) - ((ConfigOptionFloat, mmu_segmented_region_max_width)) - ((ConfigOptionFloat, raft_contact_distance)) - ((ConfigOptionFloat, raft_expansion)) - ((ConfigOptionPercent, raft_first_layer_density)) - ((ConfigOptionFloat, raft_first_layer_expansion)) - ((ConfigOptionInt, raft_layers)) - ((ConfigOptionEnum, seam_position)) + PrintObjectConfig, + + ((ConfigOptionFloat, brim_separation)) + ((ConfigOptionEnum, brim_type)) + ((ConfigOptionFloat, brim_width)) + ((ConfigOptionBool, clip_multipart_objects)) + ((ConfigOptionBool, dont_support_bridges)) + ((ConfigOptionFloat, elefant_foot_compensation)) + ((ConfigOptionFloatOrPercent, extrusion_width)) + ((ConfigOptionFloat, first_layer_acceleration_over_raft)) + ((ConfigOptionFloatOrPercent, first_layer_speed_over_raft)) + ((ConfigOptionBool, infill_only_where_needed)) + // Force the generation of solid shells between adjacent materials/volumes. + ((ConfigOptionBool, interface_shells)) + ((ConfigOptionFloat, layer_height)) + ((ConfigOptionFloat, mmu_segmented_region_max_width)) + ((ConfigOptionFloat, raft_contact_distance)) + ((ConfigOptionFloat, raft_expansion)) + ((ConfigOptionPercent, raft_first_layer_density)) + ((ConfigOptionFloat, raft_first_layer_expansion)) + ((ConfigOptionInt, raft_layers)) + ((ConfigOptionEnum, seam_position)) // ((ConfigOptionFloat, seam_preferred_direction)) // ((ConfigOptionFloat, seam_preferred_direction_jitter)) - ((ConfigOptionFloat, slice_closing_radius)) - ((ConfigOptionEnum, slicing_mode)) - ((ConfigOptionBool, support_material)) - // Automatic supports (generated based on support_material_threshold). - ((ConfigOptionBool, support_material_auto)) - // Direction of the support pattern (in XY plane).` - ((ConfigOptionFloat, support_material_angle)) - ((ConfigOptionBool, support_material_buildplate_only)) - ((ConfigOptionFloat, support_material_contact_distance)) - ((ConfigOptionFloat, support_material_bottom_contact_distance)) - ((ConfigOptionInt, support_material_enforce_layers)) - ((ConfigOptionInt, support_material_extruder)) - ((ConfigOptionFloatOrPercent, support_material_extrusion_width)) - ((ConfigOptionBool, support_material_interface_contact_loops)) - ((ConfigOptionInt, support_material_interface_extruder)) - ((ConfigOptionInt, support_material_interface_layers)) - ((ConfigOptionInt, support_material_bottom_interface_layers)) - // Spacing between interface lines (the hatching distance). Set zero to get a solid interface. - ((ConfigOptionFloat, support_material_interface_spacing)) - ((ConfigOptionFloatOrPercent, support_material_interface_speed)) - ((ConfigOptionEnum, support_material_pattern)) - ((ConfigOptionEnum, support_material_interface_pattern)) - // Morphological closing of support areas. Only used for "sung" supports. - ((ConfigOptionFloat, support_material_closing_radius)) - // Spacing between support material lines (the hatching distance). - ((ConfigOptionFloat, support_material_spacing)) - ((ConfigOptionFloat, support_material_speed)) - ((ConfigOptionEnum, support_material_style)) - ((ConfigOptionBool, support_material_synchronize_layers)) - // Overhang angle threshold. - ((ConfigOptionInt, support_material_threshold)) - ((ConfigOptionBool, support_material_with_sheath)) - ((ConfigOptionFloatOrPercent, support_material_xy_spacing)) - ((ConfigOptionBool, thick_bridges)) - ((ConfigOptionFloat, xy_size_compensation)) + ((ConfigOptionFloat, slice_closing_radius)) + ((ConfigOptionEnum, slicing_mode)) + ((ConfigOptionBool, support_material)) + // Automatic supports (generated based on support_material_threshold). + ((ConfigOptionBool, support_material_auto)) + // Direction of the support pattern (in XY plane).` + ((ConfigOptionFloat, support_material_angle)) + ((ConfigOptionBool, support_material_buildplate_only)) + ((ConfigOptionFloat, support_material_contact_distance)) + ((ConfigOptionFloat, support_material_bottom_contact_distance)) + ((ConfigOptionInt, support_material_enforce_layers)) + ((ConfigOptionInt, support_material_extruder)) + ((ConfigOptionFloatOrPercent, support_material_extrusion_width)) + ((ConfigOptionBool, support_material_interface_contact_loops)) + ((ConfigOptionInt, support_material_interface_extruder)) + ((ConfigOptionInt, support_material_interface_layers)) + ((ConfigOptionInt, support_material_bottom_interface_layers)) + // Spacing between interface lines (the hatching distance). Set zero to get a solid interface. + ((ConfigOptionFloat, support_material_interface_spacing)) + ((ConfigOptionFloatOrPercent, support_material_interface_speed)) + ((ConfigOptionEnum, support_material_pattern)) + ((ConfigOptionEnum, support_material_interface_pattern)) + // Morphological closing of support areas. Only used for "sung" supports. + ((ConfigOptionFloat, support_material_closing_radius)) + // Spacing between support material lines (the hatching distance). + ((ConfigOptionFloat, support_material_spacing)) + ((ConfigOptionFloat, support_material_speed)) + ((ConfigOptionEnum, support_material_style)) + ((ConfigOptionBool, support_material_synchronize_layers)) + // Overhang angle threshold. + ((ConfigOptionInt, support_material_threshold)) + ((ConfigOptionBool, support_material_with_sheath)) + ((ConfigOptionFloatOrPercent, support_material_xy_spacing)) + ((ConfigOptionBool, thick_bridges)) + ((ConfigOptionFloat, xy_size_compensation)) + ((ConfigOptionBool, z_dither)) ((ConfigOptionBool, wipe_into_objects)) ) PRINT_CONFIG_CLASS_DEFINE( - PrintRegionConfig, - - ((ConfigOptionFloat, bridge_angle)) - ((ConfigOptionInt, bottom_solid_layers)) - ((ConfigOptionFloat, bottom_solid_min_thickness)) - ((ConfigOptionFloat, bridge_flow_ratio)) - ((ConfigOptionFloat, bridge_speed)) - ((ConfigOptionBool, ensure_vertical_shell_thickness)) - ((ConfigOptionEnum, top_fill_pattern)) - ((ConfigOptionEnum, bottom_fill_pattern)) - ((ConfigOptionFloatOrPercent, external_perimeter_extrusion_width)) - ((ConfigOptionFloatOrPercent, external_perimeter_speed)) - ((ConfigOptionBool, external_perimeters_first)) - ((ConfigOptionBool, extra_perimeters)) - ((ConfigOptionFloat, fill_angle)) - ((ConfigOptionPercent, fill_density)) - ((ConfigOptionEnum, fill_pattern)) - ((ConfigOptionEnum, fuzzy_skin)) - ((ConfigOptionFloat, fuzzy_skin_thickness)) - ((ConfigOptionFloat, fuzzy_skin_point_dist)) - ((ConfigOptionBool, gap_fill_enabled)) - ((ConfigOptionFloat, gap_fill_speed)) - ((ConfigOptionFloatOrPercent, infill_anchor)) - ((ConfigOptionFloatOrPercent, infill_anchor_max)) - ((ConfigOptionInt, infill_extruder)) - ((ConfigOptionFloatOrPercent, infill_extrusion_width)) - ((ConfigOptionInt, infill_every_layers)) - ((ConfigOptionFloatOrPercent, infill_overlap)) - ((ConfigOptionFloat, infill_speed)) - // Ironing options - ((ConfigOptionBool, ironing)) - ((ConfigOptionEnum, ironing_type)) - ((ConfigOptionPercent, ironing_flowrate)) - ((ConfigOptionFloat, ironing_spacing)) - ((ConfigOptionFloat, ironing_speed)) - // Detect bridging perimeters - ((ConfigOptionBool, overhangs)) - ((ConfigOptionInt, perimeter_extruder)) - ((ConfigOptionFloatOrPercent, perimeter_extrusion_width)) - ((ConfigOptionFloat, perimeter_speed)) - // Total number of perimeters. - ((ConfigOptionInt, perimeters)) - ((ConfigOptionFloatOrPercent, small_perimeter_speed)) - ((ConfigOptionFloat, solid_infill_below_area)) - ((ConfigOptionInt, solid_infill_extruder)) - ((ConfigOptionFloatOrPercent, solid_infill_extrusion_width)) - ((ConfigOptionInt, solid_infill_every_layers)) - ((ConfigOptionFloatOrPercent, solid_infill_speed)) - // Detect thin walls. - ((ConfigOptionBool, thin_walls)) - ((ConfigOptionFloatOrPercent, top_infill_extrusion_width)) - ((ConfigOptionInt, top_solid_layers)) - ((ConfigOptionFloat, top_solid_min_thickness)) - ((ConfigOptionFloatOrPercent, top_solid_infill_speed)) - ((ConfigOptionBool, wipe_into_infill)) + PrintRegionConfig, + + ((ConfigOptionFloat, bridge_angle)) + ((ConfigOptionInt, bottom_solid_layers)) + ((ConfigOptionFloat, bottom_solid_min_thickness)) + ((ConfigOptionFloat, bridge_flow_ratio)) + ((ConfigOptionFloat, bridge_speed)) + ((ConfigOptionBool, ensure_vertical_shell_thickness)) + ((ConfigOptionEnum, top_fill_pattern)) + ((ConfigOptionEnum, bottom_fill_pattern)) + ((ConfigOptionFloatOrPercent, external_perimeter_extrusion_width)) + ((ConfigOptionFloatOrPercent, external_perimeter_speed)) + ((ConfigOptionBool, external_perimeters_first)) + ((ConfigOptionBool, extra_perimeters)) + ((ConfigOptionFloat, fill_angle)) + ((ConfigOptionPercent, fill_density)) + ((ConfigOptionEnum, fill_pattern)) + ((ConfigOptionEnum, fuzzy_skin)) + ((ConfigOptionFloat, fuzzy_skin_thickness)) + ((ConfigOptionFloat, fuzzy_skin_point_dist)) + ((ConfigOptionBool, gap_fill_enabled)) + ((ConfigOptionFloat, gap_fill_speed)) + ((ConfigOptionFloatOrPercent, infill_anchor)) + ((ConfigOptionFloatOrPercent, infill_anchor_max)) + ((ConfigOptionInt, infill_extruder)) + ((ConfigOptionFloatOrPercent, infill_extrusion_width)) + ((ConfigOptionInt, infill_every_layers)) + ((ConfigOptionFloatOrPercent, infill_overlap)) + ((ConfigOptionFloat, infill_speed)) + // Ironing options + ((ConfigOptionBool, ironing)) + ((ConfigOptionEnum, ironing_type)) + ((ConfigOptionPercent, ironing_flowrate)) + ((ConfigOptionFloat, ironing_spacing)) + ((ConfigOptionFloat, ironing_speed)) + // Detect bridging perimeters + ((ConfigOptionBool, overhangs)) + ((ConfigOptionInt, perimeter_extruder)) + ((ConfigOptionFloatOrPercent, perimeter_extrusion_width)) + ((ConfigOptionFloat, perimeter_speed)) + // Total number of perimeters. + ((ConfigOptionInt, perimeters)) + ((ConfigOptionFloatOrPercent, small_perimeter_speed)) + ((ConfigOptionFloat, solid_infill_below_area)) + ((ConfigOptionInt, solid_infill_extruder)) + ((ConfigOptionFloatOrPercent, solid_infill_extrusion_width)) + ((ConfigOptionInt, solid_infill_every_layers)) + ((ConfigOptionFloatOrPercent, solid_infill_speed)) + // Detect thin walls. + ((ConfigOptionBool, thin_walls)) + ((ConfigOptionFloatOrPercent, top_infill_extrusion_width)) + ((ConfigOptionInt, top_solid_layers)) + ((ConfigOptionFloat, top_solid_min_thickness)) + ((ConfigOptionFloatOrPercent, top_solid_infill_speed)) + ((ConfigOptionBool, wipe_into_infill)) ) PRINT_CONFIG_CLASS_DEFINE( - MachineEnvelopeConfig, - - // Allowing the machine limits to be completely ignored or used just for time estimator. - ((ConfigOptionEnum, machine_limits_usage)) - // M201 X... Y... Z... E... [mm/sec^2] - ((ConfigOptionFloats, machine_max_acceleration_x)) - ((ConfigOptionFloats, machine_max_acceleration_y)) - ((ConfigOptionFloats, machine_max_acceleration_z)) - ((ConfigOptionFloats, machine_max_acceleration_e)) - // M203 X... Y... Z... E... [mm/sec] - ((ConfigOptionFloats, machine_max_feedrate_x)) - ((ConfigOptionFloats, machine_max_feedrate_y)) - ((ConfigOptionFloats, machine_max_feedrate_z)) - ((ConfigOptionFloats, machine_max_feedrate_e)) - - // M204 P... R... T...[mm/sec^2] - ((ConfigOptionFloats, machine_max_acceleration_extruding)) - ((ConfigOptionFloats, machine_max_acceleration_retracting)) - ((ConfigOptionFloats, machine_max_acceleration_travel)) - - // M205 X... Y... Z... E... [mm/sec] - ((ConfigOptionFloats, machine_max_jerk_x)) - ((ConfigOptionFloats, machine_max_jerk_y)) - ((ConfigOptionFloats, machine_max_jerk_z)) - ((ConfigOptionFloats, machine_max_jerk_e)) - // M205 T... [mm/sec] - ((ConfigOptionFloats, machine_min_travel_rate)) - // M205 S... [mm/sec] - ((ConfigOptionFloats, machine_min_extruding_rate)) + MachineEnvelopeConfig, + + // Allowing the machine limits to be completely ignored or used just for time estimator. + ((ConfigOptionEnum, machine_limits_usage)) + // M201 X... Y... Z... E... [mm/sec^2] + ((ConfigOptionFloats, machine_max_acceleration_x)) + ((ConfigOptionFloats, machine_max_acceleration_y)) + ((ConfigOptionFloats, machine_max_acceleration_z)) + ((ConfigOptionFloats, machine_max_acceleration_e)) + // M203 X... Y... Z... E... [mm/sec] + ((ConfigOptionFloats, machine_max_feedrate_x)) + ((ConfigOptionFloats, machine_max_feedrate_y)) + ((ConfigOptionFloats, machine_max_feedrate_z)) + ((ConfigOptionFloats, machine_max_feedrate_e)) + + // M204 P... R... T...[mm/sec^2] + ((ConfigOptionFloats, machine_max_acceleration_extruding)) + ((ConfigOptionFloats, machine_max_acceleration_retracting)) + ((ConfigOptionFloats, machine_max_acceleration_travel)) + + // M205 X... Y... Z... E... [mm/sec] + ((ConfigOptionFloats, machine_max_jerk_x)) + ((ConfigOptionFloats, machine_max_jerk_y)) + ((ConfigOptionFloats, machine_max_jerk_z)) + ((ConfigOptionFloats, machine_max_jerk_e)) + // M205 T... [mm/sec] + ((ConfigOptionFloats, machine_min_travel_rate)) + // M205 S... [mm/sec] + ((ConfigOptionFloats, machine_min_extruding_rate)) ) PRINT_CONFIG_CLASS_DEFINE( - GCodeConfig, - - ((ConfigOptionString, before_layer_gcode)) - ((ConfigOptionString, between_objects_gcode)) - ((ConfigOptionFloats, deretract_speed)) - ((ConfigOptionString, end_gcode)) - ((ConfigOptionStrings, end_filament_gcode)) - ((ConfigOptionString, extrusion_axis)) - ((ConfigOptionFloats, extrusion_multiplier)) - ((ConfigOptionFloats, filament_diameter)) - ((ConfigOptionFloats, filament_density)) - ((ConfigOptionStrings, filament_type)) - ((ConfigOptionBools, filament_soluble)) - ((ConfigOptionFloats, filament_cost)) - ((ConfigOptionFloats, filament_spool_weight)) - ((ConfigOptionFloats, filament_max_volumetric_speed)) - ((ConfigOptionFloats, filament_loading_speed)) - ((ConfigOptionFloats, filament_loading_speed_start)) - ((ConfigOptionFloats, filament_load_time)) - ((ConfigOptionFloats, filament_unloading_speed)) - ((ConfigOptionFloats, filament_unloading_speed_start)) - ((ConfigOptionFloats, filament_toolchange_delay)) - ((ConfigOptionFloats, filament_unload_time)) - ((ConfigOptionInts, filament_cooling_moves)) - ((ConfigOptionFloats, filament_cooling_initial_speed)) - ((ConfigOptionFloats, filament_minimal_purge_on_wipe_tower)) - ((ConfigOptionFloats, filament_cooling_final_speed)) - ((ConfigOptionStrings, filament_ramming_parameters)) - ((ConfigOptionBool, gcode_comments)) - ((ConfigOptionEnum, gcode_flavor)) - ((ConfigOptionBool, gcode_label_objects)) - // Triples of strings: "search pattern", "replace with pattern", "attribs" - // where "attribs" are one of: - // r - regular expression - // i - case insensitive - // w - whole word - ((ConfigOptionStrings, gcode_substitutions)) - ((ConfigOptionString, layer_gcode)) - ((ConfigOptionFloat, max_print_speed)) - ((ConfigOptionFloat, max_volumetric_speed)) + GCodeConfig, + + ((ConfigOptionString, before_layer_gcode)) + ((ConfigOptionString, between_objects_gcode)) + ((ConfigOptionFloats, deretract_speed)) + ((ConfigOptionString, end_gcode)) + ((ConfigOptionStrings, end_filament_gcode)) + ((ConfigOptionString, extrusion_axis)) + ((ConfigOptionFloats, extrusion_multiplier)) + ((ConfigOptionFloats, filament_diameter)) + ((ConfigOptionFloats, filament_density)) + ((ConfigOptionStrings, filament_type)) + ((ConfigOptionBools, filament_soluble)) + ((ConfigOptionFloats, filament_cost)) + ((ConfigOptionFloats, filament_spool_weight)) + ((ConfigOptionFloats, filament_max_volumetric_speed)) + ((ConfigOptionFloats, filament_loading_speed)) + ((ConfigOptionFloats, filament_loading_speed_start)) + ((ConfigOptionFloats, filament_load_time)) + ((ConfigOptionFloats, filament_unloading_speed)) + ((ConfigOptionFloats, filament_unloading_speed_start)) + ((ConfigOptionFloats, filament_toolchange_delay)) + ((ConfigOptionFloats, filament_unload_time)) + ((ConfigOptionInts, filament_cooling_moves)) + ((ConfigOptionFloats, filament_cooling_initial_speed)) + ((ConfigOptionFloats, filament_minimal_purge_on_wipe_tower)) + ((ConfigOptionFloats, filament_cooling_final_speed)) + ((ConfigOptionStrings, filament_ramming_parameters)) + ((ConfigOptionBool, gcode_comments)) + ((ConfigOptionEnum, gcode_flavor)) + ((ConfigOptionBool, gcode_label_objects)) + // Triples of strings: "search pattern", "replace with pattern", "attribs" + // where "attribs" are one of: + // r - regular expression + // i - case insensitive + // w - whole word + ((ConfigOptionStrings, gcode_substitutions)) + ((ConfigOptionString, layer_gcode)) + ((ConfigOptionFloat, max_print_speed)) + ((ConfigOptionFloat, max_volumetric_speed)) //#ifdef HAS_PRESSURE_EQUALIZER // ((ConfigOptionFloat, max_volumetric_extrusion_rate_slope_positive)) // ((ConfigOptionFloat, max_volumetric_extrusion_rate_slope_negative)) //#endif - ((ConfigOptionPercents, retract_before_wipe)) - ((ConfigOptionFloats, retract_length)) - ((ConfigOptionFloats, retract_length_toolchange)) - ((ConfigOptionFloats, retract_lift)) - ((ConfigOptionFloats, retract_lift_above)) - ((ConfigOptionFloats, retract_lift_below)) - ((ConfigOptionFloats, retract_restart_extra)) - ((ConfigOptionFloats, retract_restart_extra_toolchange)) - ((ConfigOptionFloats, retract_speed)) - ((ConfigOptionString, start_gcode)) - ((ConfigOptionStrings, start_filament_gcode)) - ((ConfigOptionBool, single_extruder_multi_material)) - ((ConfigOptionBool, single_extruder_multi_material_priming)) - ((ConfigOptionBool, wipe_tower_no_sparse_layers)) - ((ConfigOptionString, toolchange_gcode)) - ((ConfigOptionFloat, travel_speed)) - ((ConfigOptionFloat, travel_speed_z)) - ((ConfigOptionBool, use_firmware_retraction)) - ((ConfigOptionBool, use_relative_e_distances)) - ((ConfigOptionBool, use_volumetric_e)) - ((ConfigOptionBool, variable_layer_height)) - ((ConfigOptionFloat, cooling_tube_retraction)) - ((ConfigOptionFloat, cooling_tube_length)) - ((ConfigOptionBool, high_current_on_filament_swap)) - ((ConfigOptionFloat, parking_pos_retraction)) - ((ConfigOptionBool, remaining_times)) - ((ConfigOptionBool, silent_mode)) - ((ConfigOptionFloat, extra_loading_move)) - ((ConfigOptionString, color_change_gcode)) - ((ConfigOptionString, pause_print_gcode)) - ((ConfigOptionString, template_custom_gcode)) + ((ConfigOptionPercents, retract_before_wipe)) + ((ConfigOptionFloats, retract_length)) + ((ConfigOptionFloats, retract_length_toolchange)) + ((ConfigOptionFloats, retract_lift)) + ((ConfigOptionFloats, retract_lift_above)) + ((ConfigOptionFloats, retract_lift_below)) + ((ConfigOptionFloats, retract_restart_extra)) + ((ConfigOptionFloats, retract_restart_extra_toolchange)) + ((ConfigOptionFloats, retract_speed)) + ((ConfigOptionString, start_gcode)) + ((ConfigOptionStrings, start_filament_gcode)) + ((ConfigOptionBool, single_extruder_multi_material)) + ((ConfigOptionBool, single_extruder_multi_material_priming)) + ((ConfigOptionBool, wipe_tower_no_sparse_layers)) + ((ConfigOptionString, toolchange_gcode)) + ((ConfigOptionFloat, travel_speed)) + ((ConfigOptionFloat, travel_speed_z)) + ((ConfigOptionBool, use_firmware_retraction)) + ((ConfigOptionBool, use_relative_e_distances)) + ((ConfigOptionBool, use_volumetric_e)) + ((ConfigOptionBool, variable_layer_height)) + ((ConfigOptionFloat, cooling_tube_retraction)) + ((ConfigOptionFloat, cooling_tube_length)) + ((ConfigOptionBool, high_current_on_filament_swap)) + ((ConfigOptionFloat, parking_pos_retraction)) + ((ConfigOptionBool, remaining_times)) + ((ConfigOptionBool, silent_mode)) + ((ConfigOptionFloat, extra_loading_move)) + ((ConfigOptionString, color_change_gcode)) + ((ConfigOptionString, pause_print_gcode)) + ((ConfigOptionString, template_custom_gcode)) ) static inline std::string get_extrusion_axis(const GCodeConfig &cfg) { - return - ((cfg.gcode_flavor.value == gcfMach3) || (cfg.gcode_flavor.value == gcfMachinekit)) ? "A" : - (cfg.gcode_flavor.value == gcfNoExtrusion) ? "" : cfg.extrusion_axis.value; + return + ((cfg.gcode_flavor.value == gcfMach3) || (cfg.gcode_flavor.value == gcfMachinekit)) ? "A" : + (cfg.gcode_flavor.value == gcfNoExtrusion) ? "" : cfg.extrusion_axis.value; } PRINT_CONFIG_CLASS_DERIVED_DEFINE( - PrintConfig, - (MachineEnvelopeConfig, GCodeConfig), - - ((ConfigOptionBool, avoid_crossing_perimeters)) - ((ConfigOptionFloatOrPercent, avoid_crossing_perimeters_max_detour)) - ((ConfigOptionPoints, bed_shape)) - ((ConfigOptionInts, bed_temperature)) - ((ConfigOptionFloat, bridge_acceleration)) - ((ConfigOptionInts, bridge_fan_speed)) - ((ConfigOptionBool, complete_objects)) - ((ConfigOptionFloats, colorprint_heights)) - ((ConfigOptionBools, cooling)) - ((ConfigOptionFloat, default_acceleration)) - ((ConfigOptionInts, disable_fan_first_layers)) - ((ConfigOptionEnum, draft_shield)) - ((ConfigOptionFloat, duplicate_distance)) - ((ConfigOptionFloat, extruder_clearance_height)) - ((ConfigOptionFloat, extruder_clearance_radius)) - ((ConfigOptionStrings, extruder_colour)) - ((ConfigOptionPoints, extruder_offset)) - ((ConfigOptionBools, fan_always_on)) - ((ConfigOptionInts, fan_below_layer_time)) - ((ConfigOptionStrings, filament_colour)) - ((ConfigOptionStrings, filament_notes)) - ((ConfigOptionFloat, first_layer_acceleration)) - ((ConfigOptionInts, first_layer_bed_temperature)) - ((ConfigOptionFloatOrPercent, first_layer_extrusion_width)) - ((ConfigOptionFloatOrPercent, first_layer_height)) - ((ConfigOptionFloatOrPercent, first_layer_speed)) - ((ConfigOptionInts, first_layer_temperature)) - ((ConfigOptionInts, full_fan_speed_layer)) - ((ConfigOptionFloat, infill_acceleration)) - ((ConfigOptionBool, infill_first)) - ((ConfigOptionInts, max_fan_speed)) - ((ConfigOptionFloats, max_layer_height)) - ((ConfigOptionInts, min_fan_speed)) - ((ConfigOptionFloats, min_layer_height)) - ((ConfigOptionFloat, max_print_height)) - ((ConfigOptionFloats, min_print_speed)) - ((ConfigOptionFloat, min_skirt_length)) - ((ConfigOptionString, notes)) - ((ConfigOptionFloats, nozzle_diameter)) - ((ConfigOptionBool, only_retract_when_crossing_perimeters)) - ((ConfigOptionBool, ooze_prevention)) - ((ConfigOptionString, output_filename_format)) - ((ConfigOptionFloat, perimeter_acceleration)) - ((ConfigOptionStrings, post_process)) - ((ConfigOptionString, printer_model)) - ((ConfigOptionString, printer_notes)) - ((ConfigOptionFloat, resolution)) - ((ConfigOptionFloat, gcode_resolution)) - ((ConfigOptionFloats, retract_before_travel)) - ((ConfigOptionBools, retract_layer_change)) - ((ConfigOptionFloat, skirt_distance)) - ((ConfigOptionInt, skirt_height)) - ((ConfigOptionInt, skirts)) - ((ConfigOptionInts, slowdown_below_layer_time)) - ((ConfigOptionBool, spiral_vase)) - ((ConfigOptionInt, standby_temperature_delta)) - ((ConfigOptionInts, temperature)) - ((ConfigOptionInt, threads)) - ((ConfigOptionPoints, thumbnails)) - ((ConfigOptionEnum, thumbnails_format)) - ((ConfigOptionBools, wipe)) - ((ConfigOptionBool, wipe_tower)) - ((ConfigOptionFloat, wipe_tower_x)) - ((ConfigOptionFloat, wipe_tower_y)) - ((ConfigOptionFloat, wipe_tower_width)) - ((ConfigOptionFloat, wipe_tower_per_color_wipe)) - ((ConfigOptionFloat, wipe_tower_rotation_angle)) - ((ConfigOptionFloat, wipe_tower_brim_width)) - ((ConfigOptionFloat, wipe_tower_bridging)) - ((ConfigOptionFloats, wiping_volumes_matrix)) - ((ConfigOptionFloats, wiping_volumes_extruders)) - ((ConfigOptionFloat, z_offset)) + PrintConfig, + (MachineEnvelopeConfig, GCodeConfig), + + ((ConfigOptionBool, avoid_crossing_perimeters)) + ((ConfigOptionFloatOrPercent, avoid_crossing_perimeters_max_detour)) + ((ConfigOptionPoints, bed_shape)) + ((ConfigOptionInts, bed_temperature)) + ((ConfigOptionFloat, bridge_acceleration)) + ((ConfigOptionInts, bridge_fan_speed)) + ((ConfigOptionBool, complete_objects)) + ((ConfigOptionFloats, colorprint_heights)) + ((ConfigOptionBools, cooling)) + ((ConfigOptionFloat, default_acceleration)) + ((ConfigOptionInts, disable_fan_first_layers)) + ((ConfigOptionEnum, draft_shield)) + ((ConfigOptionFloat, duplicate_distance)) + ((ConfigOptionFloat, extruder_clearance_height)) + ((ConfigOptionFloat, extruder_clearance_radius)) + ((ConfigOptionStrings, extruder_colour)) + ((ConfigOptionPoints, extruder_offset)) + ((ConfigOptionBools, fan_always_on)) + ((ConfigOptionInts, fan_below_layer_time)) + ((ConfigOptionStrings, filament_colour)) + ((ConfigOptionStrings, filament_notes)) + ((ConfigOptionFloat, first_layer_acceleration)) + ((ConfigOptionInts, first_layer_bed_temperature)) + ((ConfigOptionFloatOrPercent, first_layer_extrusion_width)) + ((ConfigOptionFloatOrPercent, first_layer_height)) + ((ConfigOptionFloatOrPercent, first_layer_speed)) + ((ConfigOptionInts, first_layer_temperature)) + ((ConfigOptionInts, full_fan_speed_layer)) + ((ConfigOptionFloat, infill_acceleration)) + ((ConfigOptionBool, infill_first)) + ((ConfigOptionInts, max_fan_speed)) + ((ConfigOptionFloats, max_layer_height)) + ((ConfigOptionInts, min_fan_speed)) + ((ConfigOptionFloats, min_layer_height)) + ((ConfigOptionFloat, max_print_height)) + ((ConfigOptionFloats, min_print_speed)) + ((ConfigOptionFloat, min_skirt_length)) + ((ConfigOptionString, notes)) + ((ConfigOptionFloats, nozzle_diameter)) + ((ConfigOptionBool, only_retract_when_crossing_perimeters)) + ((ConfigOptionBool, ooze_prevention)) + ((ConfigOptionString, output_filename_format)) + ((ConfigOptionFloat, perimeter_acceleration)) + ((ConfigOptionStrings, post_process)) + ((ConfigOptionString, printer_model)) + ((ConfigOptionString, printer_notes)) + ((ConfigOptionFloat, resolution)) + ((ConfigOptionFloat, gcode_resolution)) + ((ConfigOptionFloats, retract_before_travel)) + ((ConfigOptionBools, retract_layer_change)) + ((ConfigOptionFloat, skirt_distance)) + ((ConfigOptionInt, skirt_height)) + ((ConfigOptionInt, skirts)) + ((ConfigOptionInts, slowdown_below_layer_time)) + ((ConfigOptionBool, spiral_vase)) + ((ConfigOptionInt, standby_temperature_delta)) + ((ConfigOptionInts, temperature)) + ((ConfigOptionInt, threads)) + ((ConfigOptionPoints, thumbnails)) + ((ConfigOptionEnum, thumbnails_format)) + ((ConfigOptionBools, wipe)) + ((ConfigOptionBool, wipe_tower)) + ((ConfigOptionFloat, wipe_tower_x)) + ((ConfigOptionFloat, wipe_tower_y)) + ((ConfigOptionFloat, wipe_tower_width)) + ((ConfigOptionFloat, wipe_tower_per_color_wipe)) + ((ConfigOptionFloat, wipe_tower_rotation_angle)) + ((ConfigOptionFloat, wipe_tower_brim_width)) + ((ConfigOptionFloat, wipe_tower_bridging)) + ((ConfigOptionFloats, wiping_volumes_matrix)) + ((ConfigOptionFloats, wiping_volumes_extruders)) + ((ConfigOptionFloat, z_offset)) ) PRINT_CONFIG_CLASS_DERIVED_DEFINE0( - FullPrintConfig, - (PrintObjectConfig, PrintRegionConfig, PrintConfig) + FullPrintConfig, + (PrintObjectConfig, PrintRegionConfig, PrintConfig) ) // Validate the FullPrintConfig. Returns an empty string on success, otherwise an error message is returned. std::string validate(const FullPrintConfig &config); PRINT_CONFIG_CLASS_DEFINE( - SLAPrintConfig, - ((ConfigOptionString, output_filename_format)) + SLAPrintConfig, + ((ConfigOptionString, output_filename_format)) ) PRINT_CONFIG_CLASS_DEFINE( - SLAPrintObjectConfig, + SLAPrintObjectConfig, - ((ConfigOptionFloat, layer_height)) + ((ConfigOptionFloat, layer_height)) - //Number of the layers needed for the exposure time fade [3;20] - ((ConfigOptionInt, faded_layers))/*= 10*/ + //Number of the layers needed for the exposure time fade [3;20] + ((ConfigOptionInt, faded_layers))/*= 10*/ - ((ConfigOptionFloat, slice_closing_radius)) - ((ConfigOptionEnum, slicing_mode)) + ((ConfigOptionFloat, slice_closing_radius)) + ((ConfigOptionEnum, slicing_mode)) - // Enabling or disabling support creation - ((ConfigOptionBool, supports_enable)) + // Enabling or disabling support creation + ((ConfigOptionBool, supports_enable)) - // Diameter in mm of the pointing side of the head. - ((ConfigOptionFloat, support_head_front_diameter))/*= 0.2*/ + // Diameter in mm of the pointing side of the head. + ((ConfigOptionFloat, support_head_front_diameter))/*= 0.2*/ - // How much the pinhead has to penetrate the model surface - ((ConfigOptionFloat, support_head_penetration))/*= 0.2*/ + // How much the pinhead has to penetrate the model surface + ((ConfigOptionFloat, support_head_penetration))/*= 0.2*/ - // Width in mm from the back sphere center to the front sphere center. - ((ConfigOptionFloat, support_head_width))/*= 1.0*/ + // Width in mm from the back sphere center to the front sphere center. + ((ConfigOptionFloat, support_head_width))/*= 1.0*/ - // Radius in mm of the support pillars. - ((ConfigOptionFloat, support_pillar_diameter))/*= 0.8*/ + // Radius in mm of the support pillars. + ((ConfigOptionFloat, support_pillar_diameter))/*= 0.8*/ - // The percentage of smaller pillars compared to the normal pillar diameter - // which are used in problematic areas where a normal pilla cannot fit. - ((ConfigOptionPercent, support_small_pillar_diameter_percent)) + // The percentage of smaller pillars compared to the normal pillar diameter + // which are used in problematic areas where a normal pilla cannot fit. + ((ConfigOptionPercent, support_small_pillar_diameter_percent)) - // How much bridge (supporting another pinhead) can be placed on a pillar. - ((ConfigOptionInt, support_max_bridges_on_pillar)) + // How much bridge (supporting another pinhead) can be placed on a pillar. + ((ConfigOptionInt, support_max_bridges_on_pillar)) - // How the pillars are bridged together - ((ConfigOptionEnum, support_pillar_connection_mode)) + // How the pillars are bridged together + ((ConfigOptionEnum, support_pillar_connection_mode)) - // Generate only ground facing supports - ((ConfigOptionBool, support_buildplate_only)) + // Generate only ground facing supports + ((ConfigOptionBool, support_buildplate_only)) - // TODO: unimplemented at the moment. This coefficient will have an impact - // when bridges and pillars are merged. The resulting pillar should be a bit - // thicker than the ones merging into it. How much thicker? I don't know - // but it will be derived from this value. - ((ConfigOptionFloat, support_pillar_widening_factor)) + // TODO: unimplemented at the moment. This coefficient will have an impact + // when bridges and pillars are merged. The resulting pillar should be a bit + // thicker than the ones merging into it. How much thicker? I don't know + // but it will be derived from this value. + ((ConfigOptionFloat, support_pillar_widening_factor)) - // Radius in mm of the pillar base. - ((ConfigOptionFloat, support_base_diameter))/*= 2.0*/ + // Radius in mm of the pillar base. + ((ConfigOptionFloat, support_base_diameter))/*= 2.0*/ - // The height of the pillar base cone in mm. - ((ConfigOptionFloat, support_base_height))/*= 1.0*/ + // The height of the pillar base cone in mm. + ((ConfigOptionFloat, support_base_height))/*= 1.0*/ - // The minimum distance of the pillar base from the model in mm. - ((ConfigOptionFloat, support_base_safety_distance)) /*= 1.0*/ + // The minimum distance of the pillar base from the model in mm. + ((ConfigOptionFloat, support_base_safety_distance)) /*= 1.0*/ - // The default angle for connecting support sticks and junctions. - ((ConfigOptionFloat, support_critical_angle))/*= 45*/ + // The default angle for connecting support sticks and junctions. + ((ConfigOptionFloat, support_critical_angle))/*= 45*/ - // The max length of a bridge in mm - ((ConfigOptionFloat, support_max_bridge_length))/*= 15.0*/ + // The max length of a bridge in mm + ((ConfigOptionFloat, support_max_bridge_length))/*= 15.0*/ - // The max distance of two pillars to get cross linked. - ((ConfigOptionFloat, support_max_pillar_link_distance)) + // The max distance of two pillars to get cross linked. + ((ConfigOptionFloat, support_max_pillar_link_distance)) - // The elevation in Z direction upwards. This is the space between the pad - // and the model object's bounding box bottom. Units in mm. - ((ConfigOptionFloat, support_object_elevation))/*= 5.0*/ + // The elevation in Z direction upwards. This is the space between the pad + // and the model object's bounding box bottom. Units in mm. + ((ConfigOptionFloat, support_object_elevation))/*= 5.0*/ - /////// Following options influence automatic support points placement: - ((ConfigOptionInt, support_points_density_relative)) - ((ConfigOptionFloat, support_points_minimal_distance)) + /////// Following options influence automatic support points placement: + ((ConfigOptionInt, support_points_density_relative)) + ((ConfigOptionFloat, support_points_minimal_distance)) - // Now for the base pool (pad) ///////////////////////////////////////////// + // Now for the base pool (pad) ///////////////////////////////////////////// - // Enabling or disabling support creation - ((ConfigOptionBool, pad_enable)) + // Enabling or disabling support creation + ((ConfigOptionBool, pad_enable)) - // The thickness of the pad walls - ((ConfigOptionFloat, pad_wall_thickness))/*= 2*/ + // The thickness of the pad walls + ((ConfigOptionFloat, pad_wall_thickness))/*= 2*/ - // The height of the pad from the bottom to the top not considering the pit - ((ConfigOptionFloat, pad_wall_height))/*= 5*/ + // The height of the pad from the bottom to the top not considering the pit + ((ConfigOptionFloat, pad_wall_height))/*= 5*/ - // How far should the pad extend around the contained geometry - ((ConfigOptionFloat, pad_brim_size)) + // How far should the pad extend around the contained geometry + ((ConfigOptionFloat, pad_brim_size)) - // The greatest distance where two individual pads are merged into one. The - // distance is measured roughly from the centroids of the pads. - ((ConfigOptionFloat, pad_max_merge_distance))/*= 50*/ + // The greatest distance where two individual pads are merged into one. The + // distance is measured roughly from the centroids of the pads. + ((ConfigOptionFloat, pad_max_merge_distance))/*= 50*/ - // The smoothing radius of the pad edges - // ((ConfigOptionFloat, pad_edge_radius))/*= 1*/; + // The smoothing radius of the pad edges + // ((ConfigOptionFloat, pad_edge_radius))/*= 1*/; - // The slope of the pad wall... - ((ConfigOptionFloat, pad_wall_slope)) + // The slope of the pad wall... + ((ConfigOptionFloat, pad_wall_slope)) - // ///////////////////////////////////////////////////////////////////////// - // Zero elevation mode parameters: - // - The object pad will be derived from the model geometry. - // - There will be a gap between the object pad and the generated pad - // according to the support_base_safety_distance parameter. - // - The two pads will be connected with tiny connector sticks - // ///////////////////////////////////////////////////////////////////////// + // ///////////////////////////////////////////////////////////////////////// + // Zero elevation mode parameters: + // - The object pad will be derived from the model geometry. + // - There will be a gap between the object pad and the generated pad + // according to the support_base_safety_distance parameter. + // - The two pads will be connected with tiny connector sticks + // ///////////////////////////////////////////////////////////////////////// - // Disable the elevation (ignore its value) and use the zero elevation mode - ((ConfigOptionBool, pad_around_object)) + // Disable the elevation (ignore its value) and use the zero elevation mode + ((ConfigOptionBool, pad_around_object)) - ((ConfigOptionBool, pad_around_object_everywhere)) + ((ConfigOptionBool, pad_around_object_everywhere)) - // This is the gap between the object bottom and the generated pad - ((ConfigOptionFloat, pad_object_gap)) + // This is the gap between the object bottom and the generated pad + ((ConfigOptionFloat, pad_object_gap)) - // How far to place the connector sticks on the object pad perimeter - ((ConfigOptionFloat, pad_object_connector_stride)) + // How far to place the connector sticks on the object pad perimeter + ((ConfigOptionFloat, pad_object_connector_stride)) - // The width of the connectors sticks - ((ConfigOptionFloat, pad_object_connector_width)) + // The width of the connectors sticks + ((ConfigOptionFloat, pad_object_connector_width)) - // How much should the tiny connectors penetrate into the model body - ((ConfigOptionFloat, pad_object_connector_penetration)) + // How much should the tiny connectors penetrate into the model body + ((ConfigOptionFloat, pad_object_connector_penetration)) - // ///////////////////////////////////////////////////////////////////////// - // Model hollowing parameters: - // - Models can be hollowed out as part of the SLA print process - // - Thickness of the hollowed model walls can be adjusted - // - - // - Additional holes will be drilled into the hollow model to allow for - // - resin removal. - // ///////////////////////////////////////////////////////////////////////// + // ///////////////////////////////////////////////////////////////////////// + // Model hollowing parameters: + // - Models can be hollowed out as part of the SLA print process + // - Thickness of the hollowed model walls can be adjusted + // - + // - Additional holes will be drilled into the hollow model to allow for + // - resin removal. + // ///////////////////////////////////////////////////////////////////////// - ((ConfigOptionBool, hollowing_enable)) + ((ConfigOptionBool, hollowing_enable)) - // The minimum thickness of the model walls to maintain. Note that the - // resulting walls may be thicker due to smoothing out fine cavities where - // resin could stuck. - ((ConfigOptionFloat, hollowing_min_thickness)) + // The minimum thickness of the model walls to maintain. Note that the + // resulting walls may be thicker due to smoothing out fine cavities where + // resin could stuck. + ((ConfigOptionFloat, hollowing_min_thickness)) - // Indirectly controls the voxel size (resolution) used by openvdb - ((ConfigOptionFloat, hollowing_quality)) + // Indirectly controls the voxel size (resolution) used by openvdb + ((ConfigOptionFloat, hollowing_quality)) - // Indirectly controls the minimum size of created cavities. - ((ConfigOptionFloat, hollowing_closing_distance)) + // Indirectly controls the minimum size of created cavities. + ((ConfigOptionFloat, hollowing_closing_distance)) ) enum SLAMaterialSpeed { slamsSlow, slamsFast }; PRINT_CONFIG_CLASS_DEFINE( - SLAMaterialConfig, - - ((ConfigOptionFloat, initial_layer_height)) - ((ConfigOptionFloat, bottle_cost)) - ((ConfigOptionFloat, bottle_volume)) - ((ConfigOptionFloat, bottle_weight)) - ((ConfigOptionFloat, material_density)) - ((ConfigOptionFloat, exposure_time)) - ((ConfigOptionFloat, initial_exposure_time)) - ((ConfigOptionFloats, material_correction)) - ((ConfigOptionFloat, material_correction_x)) - ((ConfigOptionFloat, material_correction_y)) - ((ConfigOptionFloat, material_correction_z)) - ((ConfigOptionEnum, material_print_speed)) + SLAMaterialConfig, + + ((ConfigOptionFloat, initial_layer_height)) + ((ConfigOptionFloat, bottle_cost)) + ((ConfigOptionFloat, bottle_volume)) + ((ConfigOptionFloat, bottle_weight)) + ((ConfigOptionFloat, material_density)) + ((ConfigOptionFloat, exposure_time)) + ((ConfigOptionFloat, initial_exposure_time)) + ((ConfigOptionFloats, material_correction)) + ((ConfigOptionFloat, material_correction_x)) + ((ConfigOptionFloat, material_correction_y)) + ((ConfigOptionFloat, material_correction_z)) + ((ConfigOptionEnum, material_print_speed)) ) PRINT_CONFIG_CLASS_DEFINE( - SLAPrinterConfig, - - ((ConfigOptionEnum, printer_technology)) - ((ConfigOptionPoints, bed_shape)) - ((ConfigOptionFloat, max_print_height)) - ((ConfigOptionFloat, display_width)) - ((ConfigOptionFloat, display_height)) - ((ConfigOptionInt, display_pixels_x)) - ((ConfigOptionInt, display_pixels_y)) - ((ConfigOptionEnum,display_orientation)) - ((ConfigOptionBool, display_mirror_x)) - ((ConfigOptionBool, display_mirror_y)) - ((ConfigOptionFloats, relative_correction)) - ((ConfigOptionFloat, relative_correction_x)) - ((ConfigOptionFloat, relative_correction_y)) - ((ConfigOptionFloat, relative_correction_z)) - ((ConfigOptionFloat, absolute_correction)) - ((ConfigOptionFloat, elefant_foot_compensation)) - ((ConfigOptionFloat, elefant_foot_min_width)) - ((ConfigOptionFloat, gamma_correction)) - ((ConfigOptionFloat, fast_tilt_time)) - ((ConfigOptionFloat, slow_tilt_time)) - ((ConfigOptionFloat, area_fill)) - ((ConfigOptionFloat, min_exposure_time)) - ((ConfigOptionFloat, max_exposure_time)) - ((ConfigOptionFloat, min_initial_exposure_time)) - ((ConfigOptionFloat, max_initial_exposure_time)) - ((ConfigOptionString, sla_archive_format)) - ((ConfigOptionFloat, sla_output_precision)) + SLAPrinterConfig, + + ((ConfigOptionEnum, printer_technology)) + ((ConfigOptionPoints, bed_shape)) + ((ConfigOptionFloat, max_print_height)) + ((ConfigOptionFloat, display_width)) + ((ConfigOptionFloat, display_height)) + ((ConfigOptionInt, display_pixels_x)) + ((ConfigOptionInt, display_pixels_y)) + ((ConfigOptionEnum,display_orientation)) + ((ConfigOptionBool, display_mirror_x)) + ((ConfigOptionBool, display_mirror_y)) + ((ConfigOptionFloats, relative_correction)) + ((ConfigOptionFloat, relative_correction_x)) + ((ConfigOptionFloat, relative_correction_y)) + ((ConfigOptionFloat, relative_correction_z)) + ((ConfigOptionFloat, absolute_correction)) + ((ConfigOptionFloat, elefant_foot_compensation)) + ((ConfigOptionFloat, elefant_foot_min_width)) + ((ConfigOptionFloat, gamma_correction)) + ((ConfigOptionFloat, fast_tilt_time)) + ((ConfigOptionFloat, slow_tilt_time)) + ((ConfigOptionFloat, area_fill)) + ((ConfigOptionFloat, min_exposure_time)) + ((ConfigOptionFloat, max_exposure_time)) + ((ConfigOptionFloat, min_initial_exposure_time)) + ((ConfigOptionFloat, max_initial_exposure_time)) + ((ConfigOptionString, sla_archive_format)) + ((ConfigOptionFloat, sla_output_precision)) ) PRINT_CONFIG_CLASS_DERIVED_DEFINE0( - SLAFullPrintConfig, - (SLAPrinterConfig, SLAPrintConfig, SLAPrintObjectConfig, SLAMaterialConfig) + SLAFullPrintConfig, + (SLAPrinterConfig, SLAPrintConfig, SLAPrintObjectConfig, SLAMaterialConfig) ) #undef STATIC_PRINT_CONFIG_CACHE @@ -1026,19 +1027,19 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE0( class CLIActionsConfigDef : public ConfigDef { public: - CLIActionsConfigDef(); + CLIActionsConfigDef(); }; class CLITransformConfigDef : public ConfigDef { public: - CLITransformConfigDef(); + CLITransformConfigDef(); }; class CLIMiscConfigDef : public ConfigDef { public: - CLIMiscConfigDef(); + CLIMiscConfigDef(); }; // This class defines the command line options representing actions. @@ -1053,34 +1054,34 @@ extern const CLIMiscConfigDef cli_misc_config_def; class DynamicPrintAndCLIConfig : public DynamicPrintConfig { public: - DynamicPrintAndCLIConfig() {} - DynamicPrintAndCLIConfig(const DynamicPrintAndCLIConfig &other) : DynamicPrintConfig(other) {} + DynamicPrintAndCLIConfig() {} + DynamicPrintAndCLIConfig(const DynamicPrintAndCLIConfig &other) : DynamicPrintConfig(other) {} - // Overrides ConfigBase::def(). Static configuration definition. Any value stored into this ConfigBase shall have its definition here. - const ConfigDef* def() const override { return &s_def; } + // Overrides ConfigBase::def(). Static configuration definition. Any value stored into this ConfigBase shall have its definition here. + const ConfigDef* def() const override { return &s_def; } - // Verify whether the opt_key has not been obsoleted or renamed. - // Both opt_key and value may be modified by handle_legacy(). - // If the opt_key is no more valid in this version of Slic3r, opt_key is cleared by handle_legacy(). - // handle_legacy() is called internally by set_deserialize(). - void handle_legacy(t_config_option_key &opt_key, std::string &value) const override; + // Verify whether the opt_key has not been obsoleted or renamed. + // Both opt_key and value may be modified by handle_legacy(). + // If the opt_key is no more valid in this version of Slic3r, opt_key is cleared by handle_legacy(). + // handle_legacy() is called internally by set_deserialize(). + void handle_legacy(t_config_option_key &opt_key, std::string &value) const override; private: - class PrintAndCLIConfigDef : public ConfigDef - { - public: - PrintAndCLIConfigDef() { - this->options.insert(print_config_def.options.begin(), print_config_def.options.end()); - this->options.insert(cli_actions_config_def.options.begin(), cli_actions_config_def.options.end()); - this->options.insert(cli_transform_config_def.options.begin(), cli_transform_config_def.options.end()); - this->options.insert(cli_misc_config_def.options.begin(), cli_misc_config_def.options.end()); - for (const auto &kvp : this->options) - this->by_serialization_key_ordinal[kvp.second.serialization_key_ordinal] = &kvp.second; - } - // Do not release the default values, they are handled by print_config_def & cli_actions_config_def / cli_transform_config_def / cli_misc_config_def. - ~PrintAndCLIConfigDef() { this->options.clear(); } - }; - static PrintAndCLIConfigDef s_def; + class PrintAndCLIConfigDef : public ConfigDef + { + public: + PrintAndCLIConfigDef() { + this->options.insert(print_config_def.options.begin(), print_config_def.options.end()); + this->options.insert(cli_actions_config_def.options.begin(), cli_actions_config_def.options.end()); + this->options.insert(cli_transform_config_def.options.begin(), cli_transform_config_def.options.end()); + this->options.insert(cli_misc_config_def.options.begin(), cli_misc_config_def.options.end()); + for (const auto &kvp : this->options) + this->by_serialization_key_ordinal[kvp.second.serialization_key_ordinal] = &kvp.second; + } + // Do not release the default values, they are handled by print_config_def & cli_actions_config_def / cli_transform_config_def / cli_misc_config_def. + ~PrintAndCLIConfigDef() { this->options.clear(); } + }; + static PrintAndCLIConfigDef s_def; }; Points get_bed_shape(const DynamicPrintConfig &cfg); @@ -1109,111 +1110,111 @@ Points get_bed_shape(const SLAPrinterConfig &cfg); class ModelConfig { public: - // Following method clears the config and increases its timestamp, so the deleted - // state is considered changed from perspective of the undo/redo stack. - void reset() { m_data.clear(); touch(); } - - void assign_config(const ModelConfig &rhs) { - if (m_timestamp != rhs.m_timestamp) { - m_data = rhs.m_data; - m_timestamp = rhs.m_timestamp; - } - } - void assign_config(ModelConfig &&rhs) { - if (m_timestamp != rhs.m_timestamp) { - m_data = std::move(rhs.m_data); - m_timestamp = rhs.m_timestamp; - rhs.reset(); - } - } - - // Modification of the ModelConfig is not thread safe due to the global timestamp counter! - // Don't call modification methods from the back-end! - // Assign methods don't assign if src==dst to not having to bump the timestamp in case they are equal. - void assign_config(const DynamicPrintConfig &rhs) { if (m_data != rhs) { m_data = rhs; this->touch(); } } - void assign_config(DynamicPrintConfig &&rhs) { if (m_data != rhs) { m_data = std::move(rhs); this->touch(); } } - void apply(const ModelConfig &other, bool ignore_nonexistent = false) { this->apply(other.get(), ignore_nonexistent); } - void apply(const ConfigBase &other, bool ignore_nonexistent = false) { m_data.apply_only(other, other.keys(), ignore_nonexistent); this->touch(); } - void apply_only(const ModelConfig &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) { this->apply_only(other.get(), keys, ignore_nonexistent); } - void apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) { m_data.apply_only(other, keys, ignore_nonexistent); this->touch(); } - bool set_key_value(const std::string &opt_key, ConfigOption *opt) { bool out = m_data.set_key_value(opt_key, opt); this->touch(); return out; } - template - void set(const std::string &opt_key, T value) { m_data.set(opt_key, value, true); this->touch(); } - void set_deserialize(const t_config_option_key &opt_key, const std::string &str, ConfigSubstitutionContext &substitution_context, bool append = false) - { m_data.set_deserialize(opt_key, str, substitution_context, append); this->touch(); } - void set_deserialize_strict(const t_config_option_key &opt_key, const std::string &str, bool append = false) - { m_data.set_deserialize_strict(opt_key, str, append); this->touch(); } - bool erase(const t_config_option_key &opt_key) { bool out = m_data.erase(opt_key); if (out) this->touch(); return out; } - - // Getters are thread safe. - // The following implicit conversion breaks the Cereal serialization. + // Following method clears the config and increases its timestamp, so the deleted + // state is considered changed from perspective of the undo/redo stack. + void reset() { m_data.clear(); touch(); } + + void assign_config(const ModelConfig &rhs) { + if (m_timestamp != rhs.m_timestamp) { + m_data = rhs.m_data; + m_timestamp = rhs.m_timestamp; + } + } + void assign_config(ModelConfig &&rhs) { + if (m_timestamp != rhs.m_timestamp) { + m_data = std::move(rhs.m_data); + m_timestamp = rhs.m_timestamp; + rhs.reset(); + } + } + + // Modification of the ModelConfig is not thread safe due to the global timestamp counter! + // Don't call modification methods from the back-end! + // Assign methods don't assign if src==dst to not having to bump the timestamp in case they are equal. + void assign_config(const DynamicPrintConfig &rhs) { if (m_data != rhs) { m_data = rhs; this->touch(); } } + void assign_config(DynamicPrintConfig &&rhs) { if (m_data != rhs) { m_data = std::move(rhs); this->touch(); } } + void apply(const ModelConfig &other, bool ignore_nonexistent = false) { this->apply(other.get(), ignore_nonexistent); } + void apply(const ConfigBase &other, bool ignore_nonexistent = false) { m_data.apply_only(other, other.keys(), ignore_nonexistent); this->touch(); } + void apply_only(const ModelConfig &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) { this->apply_only(other.get(), keys, ignore_nonexistent); } + void apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) { m_data.apply_only(other, keys, ignore_nonexistent); this->touch(); } + bool set_key_value(const std::string &opt_key, ConfigOption *opt) { bool out = m_data.set_key_value(opt_key, opt); this->touch(); return out; } + template + void set(const std::string &opt_key, T value) { m_data.set(opt_key, value, true); this->touch(); } + void set_deserialize(const t_config_option_key &opt_key, const std::string &str, ConfigSubstitutionContext &substitution_context, bool append = false) + { m_data.set_deserialize(opt_key, str, substitution_context, append); this->touch(); } + void set_deserialize_strict(const t_config_option_key &opt_key, const std::string &str, bool append = false) + { m_data.set_deserialize_strict(opt_key, str, append); this->touch(); } + bool erase(const t_config_option_key &opt_key) { bool out = m_data.erase(opt_key); if (out) this->touch(); return out; } + + // Getters are thread safe. + // The following implicit conversion breaks the Cereal serialization. // operator const DynamicPrintConfig&() const throw() { return this->get(); } - const DynamicPrintConfig& get() const throw() { return m_data; } - bool empty() const throw() { return m_data.empty(); } - size_t size() const throw() { return m_data.size(); } - auto cbegin() const { return m_data.cbegin(); } - auto cend() const { return m_data.cend(); } - t_config_option_keys keys() const { return m_data.keys(); } - bool has(const t_config_option_key &opt_key) const { return m_data.has(opt_key); } - const ConfigOption* option(const t_config_option_key &opt_key) const { return m_data.option(opt_key); } - int opt_int(const t_config_option_key &opt_key) const { return m_data.opt_int(opt_key); } - int extruder() const { return opt_int("extruder"); } - double opt_float(const t_config_option_key &opt_key) const { return m_data.opt_float(opt_key); } - std::string opt_serialize(const t_config_option_key &opt_key) const { return m_data.opt_serialize(opt_key); } - - // Return an optional timestamp of this object. - // If the timestamp returned is non-zero, then the serialization framework will - // only save this object on the Undo/Redo stack if the timestamp is different - // from the timestmap of the object at the top of the Undo / Redo stack. - virtual uint64_t timestamp() const throw() { return m_timestamp; } - bool timestamp_matches(const ModelConfig &rhs) const throw() { return m_timestamp == rhs.m_timestamp; } - // Not thread safe! Should not be called from other than the main thread! - void touch() { m_timestamp = ++ s_last_timestamp; } + const DynamicPrintConfig& get() const throw() { return m_data; } + bool empty() const throw() { return m_data.empty(); } + size_t size() const throw() { return m_data.size(); } + auto cbegin() const { return m_data.cbegin(); } + auto cend() const { return m_data.cend(); } + t_config_option_keys keys() const { return m_data.keys(); } + bool has(const t_config_option_key &opt_key) const { return m_data.has(opt_key); } + const ConfigOption* option(const t_config_option_key &opt_key) const { return m_data.option(opt_key); } + int opt_int(const t_config_option_key &opt_key) const { return m_data.opt_int(opt_key); } + int extruder() const { return opt_int("extruder"); } + double opt_float(const t_config_option_key &opt_key) const { return m_data.opt_float(opt_key); } + std::string opt_serialize(const t_config_option_key &opt_key) const { return m_data.opt_serialize(opt_key); } + + // Return an optional timestamp of this object. + // If the timestamp returned is non-zero, then the serialization framework will + // only save this object on the Undo/Redo stack if the timestamp is different + // from the timestmap of the object at the top of the Undo / Redo stack. + virtual uint64_t timestamp() const throw() { return m_timestamp; } + bool timestamp_matches(const ModelConfig &rhs) const throw() { return m_timestamp == rhs.m_timestamp; } + // Not thread safe! Should not be called from other than the main thread! + void touch() { m_timestamp = ++ s_last_timestamp; } private: - friend class cereal::access; - template void serialize(Archive& ar) { ar(m_timestamp); ar(m_data); } + friend class cereal::access; + template void serialize(Archive& ar) { ar(m_timestamp); ar(m_data); } - uint64_t m_timestamp { 1 }; - DynamicPrintConfig m_data; + uint64_t m_timestamp { 1 }; + DynamicPrintConfig m_data; - static uint64_t s_last_timestamp; + static uint64_t s_last_timestamp; }; } // namespace Slic3r // Serialization through the Cereal library namespace cereal { - // Let cereal know that there are load / save non-member functions declared for DynamicPrintConfig, ignore serialize / load / save from parent class DynamicConfig. - template struct specialize {}; - - template void load(Archive& archive, Slic3r::DynamicPrintConfig &config) - { - size_t cnt; - archive(cnt); - config.clear(); - for (size_t i = 0; i < cnt; ++ i) { - size_t serialization_key_ordinal; - archive(serialization_key_ordinal); - assert(serialization_key_ordinal > 0); - auto it = Slic3r::print_config_def.by_serialization_key_ordinal.find(serialization_key_ordinal); - assert(it != Slic3r::print_config_def.by_serialization_key_ordinal.end()); - config.set_key_value(it->second->opt_key, it->second->load_option_from_archive(archive)); - } - } - - template void save(Archive& archive, const Slic3r::DynamicPrintConfig &config) - { - size_t cnt = config.size(); - archive(cnt); - for (auto it = config.cbegin(); it != config.cend(); ++it) { - const Slic3r::ConfigOptionDef* optdef = Slic3r::print_config_def.get(it->first); - assert(optdef != nullptr); - assert(optdef->serialization_key_ordinal > 0); - archive(optdef->serialization_key_ordinal); - optdef->save_option_to_archive(archive, it->second.get()); - } - } + // Let cereal know that there are load / save non-member functions declared for DynamicPrintConfig, ignore serialize / load / save from parent class DynamicConfig. + template struct specialize {}; + + template void load(Archive& archive, Slic3r::DynamicPrintConfig &config) + { + size_t cnt; + archive(cnt); + config.clear(); + for (size_t i = 0; i < cnt; ++ i) { + size_t serialization_key_ordinal; + archive(serialization_key_ordinal); + assert(serialization_key_ordinal > 0); + auto it = Slic3r::print_config_def.by_serialization_key_ordinal.find(serialization_key_ordinal); + assert(it != Slic3r::print_config_def.by_serialization_key_ordinal.end()); + config.set_key_value(it->second->opt_key, it->second->load_option_from_archive(archive)); + } + } + + template void save(Archive& archive, const Slic3r::DynamicPrintConfig &config) + { + size_t cnt = config.size(); + archive(cnt); + for (auto it = config.cbegin(); it != config.cend(); ++it) { + const Slic3r::ConfigOptionDef* optdef = Slic3r::print_config_def.get(it->first); + assert(optdef != nullptr); + assert(optdef->serialization_key_ordinal > 0); + archive(optdef->serialization_key_ordinal); + optdef->save_option_to_archive(archive, it->second.get()); + } + } } #endif diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index eeaf1b13cf6..0e361245f2e 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -45,7 +45,7 @@ using namespace std::literals; #define DEBUG #define _DEBUG #include "SVG.hpp" - #undef assert + #undef assert #include #endif @@ -89,7 +89,7 @@ PrintBase::ApplyStatus PrintObject::set_instances(PrintInstances &&instances) // Invalidate and set copies. PrintBase::ApplyStatus status = PrintBase::APPLY_STATUS_UNCHANGED; bool equal_length = instances.size() == m_instances.size(); - bool equal = equal_length && std::equal(instances.begin(), instances.end(), m_instances.begin(), + bool equal = equal_length && std::equal(instances.begin(), instances.end(), m_instances.begin(), [](const PrintInstance& lhs, const PrintInstance& rhs) { return lhs.model_instance == rhs.model_instance && lhs.shift == rhs.shift; }); if (! equal) { status = PrintBase::APPLY_STATUS_CHANGED; @@ -125,7 +125,7 @@ void PrintObject::make_perimeters() m_print->set_status(20, L("Generating perimeters")); BOOST_LOG_TRIVIAL(info) << "Generating perimeters..." << log_memory_info(); - + // Revert the typed slices into untyped slices. if (m_typed_slices) { for (Layer *layer : m_layers) { @@ -134,10 +134,10 @@ void PrintObject::make_perimeters() } m_typed_slices = false; } - + // compare each layer to the one below, and mark those slices needing // one additional inner perimeter, like the top of domed objects- - + // this algorithm makes sure that at least one perimeter is overlapping // but we don't generate any extra perimeter if fill density is zero, as they would be floating // inside the object - infill_only_where_needed should be the method of choice for printing @@ -153,8 +153,10 @@ void PrintObject::make_perimeters() [this, ®ion, region_id](const tbb::blocked_range& range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { m_print->throw_if_canceled(); - LayerRegion &layerm = *m_layers[layer_idx]->get_region(region_id); - const LayerRegion &upper_layerm = *m_layers[layer_idx+1]->get_region(region_id); + LayerRegion & layerm = *m_layers[layer_idx]->get_region(region_id); + int upper_layer_idx = this->next_layer_index(layer_idx, false); + if (upper_layer_idx == m_layers.size()) continue; + const LayerRegion &upper_layerm = *m_layers[upper_layer_idx]->get_region(region_id); const Polygons upper_layerm_polygons = to_polygons(upper_layerm.slices.surfaces); // Filter upper layer polygons in intersection_ppl by their bounding boxes? // my $upper_layerm_poly_bboxes= [ map $_->bounding_box, @{$upper_layerm_polygons} ]; @@ -226,6 +228,9 @@ void PrintObject::prepare_infill() if (! this->set_started(posPrepareInfill)) return; + for (size_t idx = 0; idx < m_layers.size(); ++idx) { + assert(m_layers[idx]->id() == idx + m_config.raft_layers); // subsequent calls will depend on such numbering + } m_print->set_status(30, L("Preparing infill")); if (m_typed_slices) { @@ -240,21 +245,22 @@ void PrintObject::prepare_infill() } // This will assign a type (top/bottom/internal) to $layerm->slices. - // Then the classifcation of $layerm->slices is transfered onto + // Then the classifcation of $layerm->slices is transfered onto // the $layerm->fill_surfaces by clipping $layerm->fill_surfaces // by the cummulative area of the previous $layerm->fill_surfaces. this->detect_surfaces_type(); m_print->throw_if_canceled(); - + // Decide what surfaces are to be filled. // Here the stTop / stBottomBridge / stBottom infill is turned to just stInternal if zero top / bottom infill layers are configured. // Also tiny stInternal surfaces are turned to stInternalSolid. BOOST_LOG_TRIVIAL(info) << "Preparing fill surfaces..." << log_memory_info(); - for (auto *layer : m_layers) + for (auto *layer : m_layers) { for (auto *region : layer->m_regions) { region->prepare_fill_surfaces(); m_print->throw_if_canceled(); } + } // this will detect bridges and reverse bridges // and rearrange top/bottom/internal surfaces @@ -320,7 +326,7 @@ void PrintObject::prepare_infill() } // for each layer } // for each region #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ - + // the following step needs to be done before combination because it may need // to remove only half of the combined infill this->bridge_over_infill(); @@ -400,7 +406,7 @@ void PrintObject::generate_support_material() if (this->set_started(posSupportMaterial)) { this->clear_support_layers(); if ((this->has_support() && m_layers.size() > 1) || (this->has_raft() && ! m_layers.empty())) { - m_print->set_status(85, L("Generating support material")); + m_print->set_status(85, L("Generating support material")); this->_generate_support_material(); m_print->throw_if_canceled(); } else { @@ -480,6 +486,18 @@ Layer* PrintObject::add_layer(int id, coordf_t height, coordf_t print_z, coordf_ return m_layers.back(); } +// Returns index of a layer below or above the idx. +// With introduction of z-dithering we can no longer rely of just incrementing or decrementing indecies. +int PrintObject::next_layer_index(size_t idx, bool lower) const +{ + const Layer *current_layer = m_layers[idx]; + const Layer *next_layer = lower ? current_layer->lower_layer : current_layer->upper_layer; + if (next_layer != nullptr) + return int(next_layer->id() - m_config.raft_layers); + else + return lower ? -1 : int(m_layers.size()); +} + void PrintObject::clear_support_layers() { for (Layer *l : m_support_layers) @@ -554,7 +572,7 @@ bool PrintObject::invalidate_state_by_config_options( } else if ( opt_key == "clip_multipart_objects" || opt_key == "elefant_foot_compensation" - || opt_key == "support_material_contact_distance" + || opt_key == "support_material_contact_distance" || opt_key == "xy_size_compensation") { steps.emplace_back(posSlice); } else if (opt_key == "support_material") { @@ -696,7 +714,7 @@ bool PrintObject::invalidate_state_by_config_options( bool PrintObject::invalidate_step(PrintObjectStep step) { bool invalidated = Inherited::invalidate_step(step); - + // propagate to dependent steps if (step == posPerimeters) { invalidated |= this->invalidate_steps({ posPrepareInfill, posInfill, posIroning }); @@ -769,7 +787,7 @@ void PrintObject::detect_surfaces_type() surfaces_new.assign(num_layers, Surfaces()); tbb::parallel_for( - tbb::blocked_range(0, + tbb::blocked_range(0, spiral_vase ? // In spiral vase mode, reserve the last layer for the top surface if more than 1 layer is planned for the vase bottom. ((num_layers > 1) ? num_layers - 1 : num_layers) : @@ -788,8 +806,8 @@ void PrintObject::detect_surfaces_type() LayerRegion *layerm = layer->m_regions[region_id]; // comparison happens against the *full* slices (considering all regions) // unless internal shells are requested - Layer *upper_layer = (idx_layer + 1 < this->layer_count()) ? m_layers[idx_layer + 1] : nullptr; - Layer *lower_layer = (idx_layer > 0) ? m_layers[idx_layer - 1] : nullptr; + Layer *upper_layer = layer->upper_layer; + Layer *lower_layer = layer->lower_layer; // collapse very narrow parts (using the safety offset in the diff is not enough) float offset = layerm->flow(frExternalPerimeter).scaled_width() / 10.f; @@ -797,7 +815,7 @@ void PrintObject::detect_surfaces_type() // of current layer and upper one) Surfaces top; if (upper_layer) { - ExPolygons upper_slices = interface_shells ? + ExPolygons upper_slices = interface_shells ? diff_ex(layerm->slices.surfaces, upper_layer->m_regions[region_id]->slices.surfaces, ApplySafetyOffset::Yes) : diff_ex(layerm->slices.surfaces, upper_layer->lslices, ApplySafetyOffset::Yes); surfaces_append(top, opening_ex(upper_slices, offset), stTop); @@ -808,14 +826,14 @@ void PrintObject::detect_surfaces_type() for (Surface &surface : top) surface.surface_type = stTop; } - + // Find bottom surfaces (difference between current surfaces of current layer and lower one). Surfaces bottom; if (lower_layer) { #if 0 //FIXME Why is this branch failing t\multi.t ? - Polygons lower_slices = interface_shells ? - to_polygons(lower_layer->get_region(region_id)->slices.surfaces) : + Polygons lower_slices = interface_shells ? + to_polygons(lower_layer->get_region(region_id)->slices.surfaces) : to_polygons(lower_layer->slices); surfaces_append(bottom, opening_ex(diff(layerm->slices.surfaces, lower_slices, true), offset), @@ -831,7 +849,7 @@ void PrintObject::detect_surfaces_type() // if user requested internal shells, we need to identify surfaces // lying on other slices not belonging to this region if (interface_shells) { - // non-bridging bottom surfaces: any part of this layer lying + // non-bridging bottom surfaces: any part of this layer lying // on something else, excluding those lying on our own region surfaces_append( bottom, @@ -851,7 +869,7 @@ void PrintObject::detect_surfaces_type() for (Surface &surface : bottom) surface.surface_type = stBottom; } - + // now, if the object contained a thin membrane, we could have overlapping bottom // and top surfaces; let's do an intersection to discover them and consider them // as bottom surfaces (to allow for bridge detection) @@ -874,7 +892,7 @@ void PrintObject::detect_surfaces_type() SVG::export_expolygons(debug_out_path("1_detect_surfaces_type_%d_region%d-layer_%f.svg", iRun ++, region_id, layer->print_z).c_str(), expolygons_with_attributes); } #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ - + // save surfaces to layer Surfaces &surfaces_out = interface_shells ? surfaces_new[idx_layer] : layerm->slices.surfaces; Surfaces surfaces_backup; @@ -893,7 +911,7 @@ void PrintObject::detect_surfaces_type() surfaces_append(surfaces_out, std::move(top)); surfaces_append(surfaces_out, std::move(bottom)); - + // Slic3r::debugf " layer %d has %d bottom, %d top and %d internal surfaces\n", // $layerm->layer->id, scalar(@bottom), scalar(@top), scalar(@internal) if $Slic3r::debug; @@ -1008,9 +1026,10 @@ void PrintObject::process_external_surfaces() for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { m_print->throw_if_canceled(); // BOOST_LOG_TRIVIAL(trace) << "Processing external surface, layer" << m_layers[layer_idx]->print_z; - m_layers[layer_idx]->get_region(int(region_id))->process_external_surfaces( - (layer_idx == 0) ? nullptr : m_layers[layer_idx - 1], - (layer_idx == 0 || surfaces_covered.empty() || surfaces_covered[layer_idx - 1].empty()) ? nullptr : &surfaces_covered[layer_idx - 1]); + Layer *lower_layer = this->m_layers[layer_idx]->lower_layer; + Polygons *covered = (lower_layer == nullptr || surfaces_covered.empty() || surfaces_covered[lower_layer->id()].empty()) + ? nullptr : &surfaces_covered[lower_layer->id()]; + m_layers[layer_idx]->get_region(int(region_id))->process_external_surfaces(lower_layer, covered); } } ); @@ -1121,7 +1140,7 @@ void PrintObject::discover_vertical_shells() svg.draw(layer.lslices, "blue"); svg.draw(union_ex(cache.holes), "red"); svg.draw_outline(union_ex(cache.holes), "black", "blue", scale_(0.05)); - svg.Close(); + svg.Close(); } #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ } @@ -1201,7 +1220,7 @@ void PrintObject::discover_vertical_shells() #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ Flow solid_infill_flow = layerm->flow(frSolidInfill); - coord_t infill_line_spacing = solid_infill_flow.scaled_spacing(); + coord_t infill_line_spacing = solid_infill_flow.scaled_spacing(); // Find a union of perimeters below / above this surface to guarantee a minimum shell thickness. Polygons shell; Polygons holes; @@ -1237,17 +1256,17 @@ void PrintObject::discover_vertical_shells() if (int n_top_layers = region_config.top_solid_layers.value; n_top_layers > 0) { // Gather top regions projected to this layer. coordf_t print_z = layer->print_z; - for (int i = int(idx_layer) + 1; - i < int(cache_top_botom_regions.size()) && - (i < int(idx_layer) + n_top_layers || - m_layers[i]->print_z - print_z < region_config.top_solid_min_thickness - EPSILON); - ++ i) { - const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[i]; - if (! holes.empty()) + for (int i = next_layer_index(idx_layer, false), count = 1; + i < int(cache_top_botom_regions.size()) && + (count < n_top_layers || + m_layers[i]->print_z - print_z < region_config.top_solid_min_thickness - EPSILON); + i = next_layer_index(i, false), ++count) { + const DiscoverVerticalShellsCacheEntry& cache = cache_top_botom_regions[i]; + if (!holes.empty()) holes = intersection(holes, cache.holes); if (! cache.top_surfaces.empty()) { polygons_append(shell, cache.top_surfaces); - // Running the union_ using the Clipper library piece by piece is cheaper + // Running the union_ using the Clipper library piece by piece is cheaper // than running the union_ all at once. shell = union_(shell); } @@ -1256,17 +1275,16 @@ void PrintObject::discover_vertical_shells() if (int n_bottom_layers = region_config.bottom_solid_layers.value; n_bottom_layers > 0) { // Gather bottom regions projected to this layer. coordf_t bottom_z = layer->bottom_z(); - for (int i = int(idx_layer) - 1; - i >= 0 && - (i > int(idx_layer) - n_bottom_layers || - bottom_z - m_layers[i]->bottom_z() < region_config.bottom_solid_min_thickness - EPSILON); - -- i) { + for (int i = next_layer_index(idx_layer, true), count = 1; + i >= 0 && (count < n_bottom_layers || + bottom_z - m_layers[i]->bottom_z() < region_config.bottom_solid_min_thickness - EPSILON); + i = next_layer_index(i, true), ++count) { const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[i]; if (! holes.empty()) holes = intersection(holes, cache.holes); if (! cache.bottom_surfaces.empty()) { polygons_append(shell, cache.bottom_surfaces); - // Running the union_ using the Clipper library piece by piece is cheaper + // Running the union_ using the Clipper library piece by piece is cheaper // than running the union_ all at once. shell = union_(shell); } @@ -1277,14 +1295,14 @@ void PrintObject::discover_vertical_shells() Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-before-union-%d.svg", debug_idx), get_extents(shell)); svg.draw(shell); svg.draw_outline(shell, "black", scale_(0.05)); - svg.Close(); + svg.Close(); } #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ #if 0 { PROFILE_BLOCK(discover_vertical_shells_region_layer_shell_); // shell = union_(shell, true); - shell = union_(shell, false); + shell = union_(shell, false); } #endif #ifdef SLIC3R_DEBUG_SLICE_PROCESSING @@ -1300,7 +1318,7 @@ void PrintObject::discover_vertical_shells() Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-after-union-%d.svg", debug_idx), get_extents(shell)); svg.draw(shell_ex); svg.draw_outline(shell_ex, "black", "blue", scale_(0.05)); - svg.Close(); + svg.Close(); } #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ @@ -1312,7 +1330,7 @@ void PrintObject::discover_vertical_shells() svg.draw(shell_ex, "blue", 0.5); svg.draw_outline(shell_ex, "black", "blue", scale_(0.05)); svg.Close(); - } + } { Slic3r::SVG svg(debug_out_path("discover_vertical_shells-internalvoid-wshell-%d.svg", debug_idx), get_extents(shell)); svg.draw(layerm->fill_surfaces.filter_by_type(stInternalVoid), "yellow", 0.5); @@ -1320,15 +1338,15 @@ void PrintObject::discover_vertical_shells() svg.draw(shell_ex, "blue", 0.5); svg.draw_outline(shell_ex, "black", "blue", scale_(0.05)); svg.Close(); - } + } { Slic3r::SVG svg(debug_out_path("discover_vertical_shells-internalvoid-wshell-%d.svg", debug_idx), get_extents(shell)); svg.draw(layerm->fill_surfaces.filter_by_type(stInternalVoid), "yellow", 0.5); svg.draw_outline(layerm->fill_surfaces.filter_by_type(stInternalVoid), "black", "blue", scale_(0.05)); svg.draw(shell_ex, "blue", 0.5); - svg.draw_outline(shell_ex, "black", "blue", scale_(0.05)); + svg.draw_outline(shell_ex, "black", "blue", scale_(0.05)); svg.Close(); - } + } #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ // Trim the shells region by the internal & internal void surfaces. @@ -1349,7 +1367,7 @@ void PrintObject::discover_vertical_shells() Polygons shell_before = shell; #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ #if 1 - // Intentionally inflate a bit more than how much the region has been shrunk, + // Intentionally inflate a bit more than how much the region has been shrunk, // so there will be some overlap between this solid infill and the other infill regions (mainly the sparse infill). shell = opening(union_(shell), 0.5f * min_perimeter_infill_spacing, 0.8f * min_perimeter_infill_spacing, ClipperLib::jtSquare); if (shell.empty()) @@ -1365,8 +1383,8 @@ void PrintObject::discover_vertical_shells() // have the same angle, so the next shell would be grown even more and so on. Polygons too_narrow = diff(shell, opening(shell, margin, ClipperLib::jtMiter, 5.), true); if (! too_narrow.empty()) { - // grow the collapsing parts and add the extra area to the neighbor layer - // as well as to our original surfaces so that we support this + // grow the collapsing parts and add the extra area to the neighbor layer + // as well as to our original surfaces so that we support this // additional area in the next shell too // make sure our grown surfaces don't exceed the fill area polygons_append(shell, intersection(offset(too_narrow, margin), polygonsInternal)); @@ -1382,7 +1400,7 @@ void PrintObject::discover_vertical_shells() svg.draw_outline(union_safety_offset_ex(shell), "black", "blue", scale_(0.05)); // Regularized infill region. svg.draw_outline(new_internal_solid, "red", "magenta", scale_(0.05)); - svg.Close(); + svg.Close(); } #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ @@ -1431,7 +1449,7 @@ void PrintObject::bridge_over_infill() for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) { const PrintRegion ®ion = this->printing_region(region_id); - + // skip bridging in case there are no voids if (region.config().fill_density.value == 100) continue; @@ -1440,7 +1458,7 @@ void PrintObject::bridge_over_infill() // skip first layer if (layer_it == m_layers.begin()) continue; - + Layer *layer = *layer_it; LayerRegion *layerm = layer->m_regions[region_id]; Flow bridge_flow = layerm->bridging_flow(frSolidInfill); @@ -1448,50 +1466,49 @@ void PrintObject::bridge_over_infill() // extract the stInternalSolid surfaces that might be transformed into bridges Polygons internal_solid; layerm->fill_surfaces.filter_by_type(stInternalSolid, &internal_solid); - + // check whether the lower area is deep enough for absorbing the extra flow // (for obvious physical reasons but also for preventing the bridge extrudates // from overflowing in 3D preview) ExPolygons to_bridge; { Polygons to_bridge_pp = internal_solid; - + // iterate through lower layers spanned by bridge_flow double bottom_z = layer->print_z - bridge_flow.height() - EPSILON; - for (int i = int(layer_it - m_layers.begin()) - 1; i >= 0; --i) { - const Layer* lower_layer = m_layers[i]; - + for (Layer *lower_layer = (*layer_it)->lower_layer; lower_layer != nullptr; lower_layer = lower_layer->lower_layer) { + // stop iterating if layer is lower than bottom_z if (lower_layer->print_z < bottom_z) break; - + // iterate through regions and collect internal surfaces Polygons lower_internal; for (LayerRegion *lower_layerm : lower_layer->m_regions) lower_layerm->fill_surfaces.filter_by_type(stInternal, &lower_internal); - + // intersect such lower internal surfaces with the candidate solid surfaces to_bridge_pp = intersection(to_bridge_pp, lower_internal); } - + // there's no point in bridging too thin/short regions - //FIXME Vojtech: The offset2 function is not a geometric offset, + //FIXME Vojtech: The offset2 function is not a geometric offset, // therefore it may create 1) gaps, and 2) sharp corners, which are outside the original contour. // The gaps will be filled by a separate region, which makes the infill less stable and it takes longer. { float min_width = float(bridge_flow.scaled_width()) * 3.f; to_bridge_pp = opening(to_bridge_pp, min_width); } - + if (to_bridge_pp.empty()) continue; - + // convert into ExPolygons to_bridge = union_ex(to_bridge_pp); } - + #ifdef SLIC3R_DEBUG printf("Bridging %zu internal areas at layer %zu\n", to_bridge.size(), layer->id()); #endif - + // compute the remaning internal solid surfaces as difference ExPolygons not_to_bridge = diff_ex(internal_solid, to_bridge, ApplySafetyOffset::Yes); to_bridge = intersection_ex(to_bridge, internal_solid, ApplySafetyOffset::Yes); @@ -1500,7 +1517,7 @@ void PrintObject::bridge_over_infill() for (ExPolygon &ex : to_bridge) layerm->fill_surfaces.surfaces.push_back(Surface(stInternalBridge, ex)); for (ExPolygon &ex : not_to_bridge) - layerm->fill_surfaces.surfaces.push_back(Surface(stInternalSolid, ex)); + layerm->fill_surfaces.surfaces.push_back(Surface(stInternalSolid, ex)); /* # exclude infill from the layers below if needed # see discussion at https://github.com/alexrj/Slic3r/issues/240 @@ -1529,7 +1546,7 @@ void PrintObject::bridge_over_infill() $lower_layerm->fill_surfaces->clear; $lower_layerm->fill_surfaces->append($_) for @new_surfaces; } - + $excess -= $self->get_layer($i)->height; } } @@ -1619,7 +1636,7 @@ PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &defau // Switch of infill for very low infill rates, also avoid division by zero in infill generator for these very low rates. // See GH issue #5910. config.fill_density.value = 0; - else + else config.fill_density.value = std::min(config.fill_density.value, 100.); if (config.fuzzy_skin.value != FuzzySkinType::None && (config.fuzzy_skin_point_dist.value < 0.01 || config.fuzzy_skin_thickness.value < 0.001)) config.fuzzy_skin.value = FuzzySkinType::None; @@ -1735,7 +1752,7 @@ void PrintObject::clip_fill_surfaces() Polygons upper_internal; for (int layer_id = int(m_layers.size()) - 1; layer_id > 0; -- layer_id) { Layer *layer = m_layers[layer_id]; - Layer *lower_layer = m_layers[layer_id - 1]; + Layer* lower_layer = layer->lower_layer; // Detect things that we need to support. // Cummulative fill surfaces. Polygons fill_surfaces; @@ -1778,7 +1795,7 @@ void PrintObject::clip_fill_surfaces() // Regularize the overhang regions, so that the infill areas will not become excessively jagged. smooth_outward( closing(upper_internal, closing_radius, ClipperLib::jtSquare, 0.), - scaled(0.1)), + scaled(0.1)), lower_layer_internal_surfaces); // Apply new internal infill to regions. for (LayerRegion *layerm : lower_layer->m_regions) { @@ -1806,7 +1823,7 @@ void PrintObject::clip_fill_surfaces() void PrintObject::discover_horizontal_shells() { BOOST_LOG_TRIVIAL(trace) << "discover_horizontal_shells()"; - + for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) { for (size_t i = 0; i < m_layers.size(); ++ i) { m_print->throw_if_canceled(); @@ -1825,7 +1842,7 @@ void PrintObject::discover_horizontal_shells() // If ensure_vertical_shell_thickness, then the rest has already been performed by discover_vertical_shells(). if (region_config.ensure_vertical_shell_thickness.value) continue; - + coordf_t print_z = layer->print_z; coordf_t bottom_z = layer->bottom_z(); for (size_t idx_surface_type = 0; idx_surface_type < 3; ++ idx_surface_type) { @@ -1838,7 +1855,7 @@ void PrintObject::discover_horizontal_shells() // Use slices instead of fill_surfaces, because they also include the perimeter area, // which needs to be propagated in shells; we need to grow slices like we did for // fill_surfaces though. Using both ungrown slices and grown fill_surfaces will - // not work in some situations, as there won't be any grown region in the perimeter + // not work in some situations, as there won't be any grown region in the perimeter // area (this was seen in a model where the top layer had one extra perimeter, thus // its fill_surfaces were thinner than the lower layer's infill), however it's the best // solution so far. Growing the external slices by EXTERNAL_INFILL_MARGIN will put @@ -1858,20 +1875,21 @@ void PrintObject::discover_horizontal_shells() if (solid.empty()) continue; // Slic3r::debugf "Layer %d has %s surfaces\n", $i, ($type == stTop) ? 'top' : 'bottom'; - + // Scatter top / bottom regions to other layers. Scattering process is inherently serial, it is difficult to parallelize without locking. - for (int n = (type == stTop) ? int(i) - 1 : int(i) + 1; - (type == stTop) ? - (n >= 0 && (int(i) - n < num_solid_layers || - print_z - m_layers[n]->print_z < region_config.top_solid_min_thickness.value - EPSILON)) : - (n < int(m_layers.size()) && (n - int(i) < num_solid_layers || - m_layers[n]->bottom_z() - bottom_z < region_config.bottom_solid_min_thickness.value - EPSILON)); - (type == stTop) ? -- n : ++ n) + bool lower = (type == stTop); + for (int n = next_layer_index(i, lower), num_done = 1; + lower ? + (n >= 0 && (num_done < num_solid_layers || + print_z - m_layers[n]->print_z < region_config.top_solid_min_thickness.value - EPSILON)) : + (n < int(m_layers.size()) && (num_done < num_solid_layers || + m_layers[n]->bottom_z() - bottom_z < region_config.bottom_solid_min_thickness.value - EPSILON)); + n = next_layer_index(n, lower), ++num_done) { -// Slic3r::debugf " looking for neighbors on layer %d...\n", $n; +// Slic3r::debugf " looking for neighbors on layer %d...\n", $n; // Reference to the lower layer of a TOP surface, or an upper layer of a BOTTOM surface. LayerRegion *neighbor_layerm = m_layers[n]->regions()[region_id]; - + // find intersection between neighbor and current layer's surfaces // intersections have contours and holes // we update $solid so that we limit the next neighbor layer to the areas that were @@ -1879,7 +1897,7 @@ void PrintObject::discover_horizontal_shells() // are always a subset of the shells found on the previous shell layer // this approach allows for DWIM in hollow sloping vases, where we want bottom // shells to be generated in the base but not in the walls (where there are many - // narrow bottom surfaces): reassigning $solid will consider the 'shadow' of the + // narrow bottom surfaces): reassigning $solid will consider the 'shadow' of the // upper perimeter as an obstacle and shell will not be propagated to more upper layers //FIXME How does it work for stInternalBRIDGE? This is set for sparse infill. Likely this does not work. Polygons new_internal_solid; @@ -1897,8 +1915,8 @@ void PrintObject::discover_horizontal_shells() if (region_config.fill_density.value == 0) { // If user expects the object to be void (for example a hollow sloping vase), // don't continue the search. In this case, we only generate the external solid - // shell if the object would otherwise show a hole (gap between perimeters of - // the two layers), and internal solid shells are a subset of the shells found + // shell if the object would otherwise show a hole (gap between perimeters of + // the two layers), and internal solid shells are a subset of the shells found // on each previous layer. goto EXTERNAL; } else { @@ -1906,7 +1924,7 @@ void PrintObject::discover_horizontal_shells() continue; } } - + if (region_config.fill_density.value == 0) { // if we're printing a hollow object we discard any solid shell thinner // than a perimeter width, since it's probably just crossing a sloping wall @@ -1914,7 +1932,7 @@ void PrintObject::discover_horizontal_shells() // obeying the solid shell count option strictly (DWIM!) float margin = float(neighbor_layerm->flow(frExternalPerimeter).scaled_width()); Polygons too_narrow = diff( - new_internal_solid, + new_internal_solid, opening(new_internal_solid, margin, margin + ClipperSafetyOffset, jtMiter, 5)); // Trim the regularized region by the original region. if (! too_narrow.empty()) @@ -1936,28 +1954,28 @@ void PrintObject::discover_horizontal_shells() new_internal_solid, opening(new_internal_solid, margin, margin + ClipperSafetyOffset, ClipperLib::jtMiter, 5)); if (! too_narrow.empty()) { - // grow the collapsing parts and add the extra area to the neighbor layer - // as well as to our original surfaces so that we support this + // grow the collapsing parts and add the extra area to the neighbor layer + // as well as to our original surfaces so that we support this // additional area in the next shell too // make sure our grown surfaces don't exceed the fill area Polygons internal; for (const Surface &surface : neighbor_layerm->fill_surfaces.surfaces) if (surface.is_internal() && !surface.is_bridge()) polygons_append(internal, to_polygons(surface.expolygon)); - polygons_append(new_internal_solid, + polygons_append(new_internal_solid, intersection( expand(too_narrow, +margin), // Discard bridges as they are grown for anchoring and we can't - // remove such anchors. (This may happen when a bridge is being + // remove such anchors. (This may happen when a bridge is being // anchored onto a wall where little space remains after the bridge - // is grown, and that little space is an internal solid shell so + // is grown, and that little space is an internal solid shell so // it triggers this too_narrow logic.) internal)); // see https://github.com/prusa3d/PrusaSlicer/pull/3426 // solid = new_internal_solid; } } - + // internal-solid are the union of the existing internal-solid surfaces // and new ones SurfaceCollection backup = std::move(neighbor_layerm->fill_surfaces); @@ -2036,11 +2054,11 @@ void PrintObject::combine_infill() current_height += layer->height; ++ num_layers; } - + // Append lower layers (if any) to uppermost layer. combine[m_layers.size() - 1] = num_layers; } - + // loop through layers to which we have assigned layers to combine for (size_t layer_idx = 0; layer_idx < m_layers.size(); ++ layer_idx) { m_print->throw_if_canceled(); @@ -2060,8 +2078,8 @@ void PrintObject::combine_infill() intersection = intersection_ex(layerms[i]->fill_surfaces.filter_by_type(stInternal), intersection); double area_threshold = layerms.front()->infill_area_threshold(); if (! intersection.empty() && area_threshold > 0.) - intersection.erase(std::remove_if(intersection.begin(), intersection.end(), - [area_threshold](const ExPolygon &expoly) { return expoly.area() <= area_threshold; }), + intersection.erase(std::remove_if(intersection.begin(), intersection.end(), + [area_threshold](const ExPolygon &expoly) { return expoly.area() <= area_threshold; }), intersection.end()); if (intersection.empty()) continue; @@ -2073,15 +2091,15 @@ void PrintObject::combine_infill() // so let's remove those areas from all layers. Polygons intersection_with_clearance; intersection_with_clearance.reserve(intersection.size()); - float clearance_offset = + float clearance_offset = 0.5f * layerms.back()->flow(frPerimeter).scaled_width() + - // Because fill areas for rectilinear and honeycomb are grown + // Because fill areas for rectilinear and honeycomb are grown // later to overlap perimeters, we need to counteract that too. ((region.config().fill_pattern == ipRectilinear || region.config().fill_pattern == ipMonotonic || region.config().fill_pattern == ipGrid || region.config().fill_pattern == ipLine || - region.config().fill_pattern == ipHoneycomb) ? 1.5f : 0.5f) * + region.config().fill_pattern == ipHoneycomb) ? 1.5f : 0.5f) * layerms.back()->flow(frSolidInfill).scaled_width(); for (ExPolygon &expoly : intersection) polygons_append(intersection_with_clearance, offset(expoly, clearance_offset)); @@ -2293,7 +2311,7 @@ static void project_triangles_to_slabs(ConstLayerPtrsAdaptor layers, const index // The resulting triangles are fed to the Clipper library, which seem to handle flipped triangles well. // if (cross2(Vec2d((poly.pts[1] - poly.pts[0]).cast()), Vec2d((poly.pts[2] - poly.pts[1]).cast())) < 0) // std::swap(poly.pts.front(), poly.pts.back()); - + out[layer_id].emplace_back(std::move(poly.pts)); ++layer_id; } @@ -2315,16 +2333,28 @@ void PrintObject::project_and_append_custom_facets( seam, out); else { std::vector projected; + ConstLayerPtrsAdaptor &layers = this->layers(); // Support blockers or enforcers. Project downward facing painted areas upwards to their respective slicing plane. - slice_mesh_slabs(custom_facets, zs_from_layers(this->layers()), this->trafo_centered() * mv->get_matrix(), nullptr, &projected, [](){}); + slice_mesh_slabs(custom_facets, zs_from_layers(layers, true), this->trafo_centered() * mv->get_matrix(), nullptr, &projected, [](){}); // Merge these projections with the output, layer by layer. assert(! projected.empty()); - assert(out.empty() || out.size() == projected.size()); - if (out.empty()) - out = std::move(projected); - else - for (size_t i = 0; i < out.size(); ++ i) - append(out[i], std::move(projected[i])); + bool out_empty = out.empty(); + assert(out_empty || out.size() == layers.size()); + size_t proj_idx = 0; + + for (size_t i = 0; i < layers.size(); ++i) { + if (!layers[i]->dithered) { + if (out_empty) + out.emplace_back(std::move(projected[proj_idx])); + else + append(out[i], std::move(projected[proj_idx])); + + proj_idx++; + } else if (out_empty) { + out.emplace_back(Polygons()); + } + } + assert(proj_idx == projected.size()); } } } diff --git a/src/libslic3r/PrintObjectSlice.cpp b/src/libslic3r/PrintObjectSlice.cpp index 4368d79e705..27ee478af6f 100644 --- a/src/libslic3r/PrintObjectSlice.cpp +++ b/src/libslic3r/PrintObjectSlice.cpp @@ -40,37 +40,30 @@ LayerPtrs new_layers( return out; } -// Slice single triangle mesh. -static std::vector slice_volume( - const ModelVolume &volume, - const std::vector &zs, - const MeshSlicingParamsEx ¶ms, - const std::function &throw_on_cancel_callback) + // Slice single triangle mesh. +static std::vector slice_volume(const ModelVolume & volume, + const std::vector & zs, + const MeshSlicingParamsEx & params, + std::vector * sublayers, + const std::function &throw_on_cancel_callback) { indexed_triangle_set its = volume.mesh().its; - if (zs.empty() || its.indices.size() == 0) + if (zs.empty() || its.indices.size() == 0) { return std::vector(); - + } + MeshSlicingParamsEx params2{params}; params2.trafo = params2.trafo * volume.get_matrix(); - if (params2.trafo.rotation().determinant() < 0.) + if (params2.trafo.rotation().determinant() < 0.) its_flip_triangles(its); - std::vector layers = slice_mesh_ex(its, zs, params2, throw_on_cancel_callback); - /* - std::vector layers5 = z_dither(its, zs, params2, layers, throw_on_cancel_callback); - // It is not quite clear what is the best way to incorporate z-dithering into the rest of PrusaSlicer. - // I would need some developer assistance. - // The code below is an aid in debugging - // Make new output vector and let vector destructors release layers and layers3 - std::vector out; - out.reserve(layers5.size()); - for (int i = 0; i < layers5.size(); i++) { - // To visualize ditherUp[0] while in development - out.push_back(ExPolygons(layers5[i].ditherUp[0].empty() ? layers5[i].whole : layers5[i].ditherUp[0])); + std::vector expolys = slice_mesh_ex(its, zs, params2, throw_on_cancel_callback); + if (params2.z_dither) { + expolys = z_dither(its, zs, params2, expolys, sublayers, throw_on_cancel_callback); + } else { + *sublayers = std::vector(expolys.size()); } - */ throw_on_cancel_callback(); - return layers; + return expolys; } // Slice single triangle mesh. @@ -80,13 +73,14 @@ static std::vector slice_volume( const std::vector &z, const std::vector &ranges, const MeshSlicingParamsEx ¶ms, + std::vector * outSublayers, const std::function &throw_on_cancel_callback) { std::vector out; if (! z.empty() && ! ranges.empty()) { if (ranges.size() == 1 && z.front() >= ranges.front().first && z.back() < ranges.front().second) { // All layers fit into a single range. - out = slice_volume(volume, z, params, throw_on_cancel_callback); + out = slice_volume(volume, z, params, outSublayers, throw_on_cancel_callback); } else { std::vector z_filtered; std::vector> n_filtered; @@ -102,18 +96,22 @@ static std::vector slice_volume( n_filtered.emplace_back(std::make_pair(first, i)); } if (! n_filtered.empty()) { - std::vector layers = slice_volume(volume, z_filtered, params, throw_on_cancel_callback); + std::vector sublayers; + std::vector layers = slice_volume(volume, z_filtered, params, &sublayers, throw_on_cancel_callback); out.assign(z.size(), ExPolygons()); i = 0; for (const std::pair &span : n_filtered) - for (size_t j = span.first; j < span.second; ++ j) - out[j] = std::move(layers[i ++]); + for (size_t j = span.first; j < span.second; ++j) { + out[j] = std::move(layers[i++]); + outSublayers->emplace_back(std::move(sublayers[j])); + } } } } return out; } + struct VolumeSlices { ObjectID volume_id; @@ -126,6 +124,12 @@ static inline bool model_volume_needs_slicing(const ModelVolume &mv) return type == ModelVolumeType::MODEL_PART || type == ModelVolumeType::NEGATIVE_VOLUME || type == ModelVolumeType::PARAMETER_MODIFIER; } +struct VolumeSublayers +{ + ObjectID volume_id; + std::vector sublayers; +}; + // Slice printable volumes, negative volumes and modifier volumes, sorted by ModelVolume::id(). // Apply closing radius. // Apply positive XY compensation to ModelVolumeType::MODEL_PART and ModelVolumeType::PARAMETER_MODIFIER, not to ModelVolumeType::NEGATIVE_VOLUME. @@ -137,12 +141,15 @@ static std::vector slice_volumes_inner( ModelVolumePtrs model_volumes, const std::vector &layer_ranges, const std::vector &zs, + std::vector *outSublayers, const std::function &throw_on_cancel_callback) { model_volumes_sort_by_id(model_volumes); std::vector out; out.reserve(model_volumes.size()); + outSublayers->clear(); + outSublayers->reserve(model_volumes.size()); std::vector slicing_ranges; if (layer_ranges.size() > 1) @@ -155,6 +162,7 @@ static std::vector slice_volumes_inner( params_base.resolution = print_config.resolution.value; const std::vector &diameters = print_config.nozzle_diameter.values; params_base.nozzle_diameter = float(*std::min_element(diameters.begin(), diameters.end())); + params_base.z_dither = print_object_config.z_dither; switch (print_object_config.slicing_mode.value) { case SlicingMode::Regular: params_base.mode = MeshSlicingParams::SlicingMode::Regular; break; @@ -176,7 +184,7 @@ static std::vector slice_volumes_inner( if (layer_ranges.size() == 1) { if (const PrintObjectRegions::LayerRangeRegions &layer_range = layer_ranges.front(); layer_range.has_volume(model_volume->id())) { if (model_volume->is_model_part() && print_config.spiral_vase) { - auto it = std::find_if(layer_range.volume_regions.begin(), layer_range.volume_regions.end(), + auto it = std::find_if(layer_range.volume_regions.begin(), layer_range.volume_regions.end(), [model_volume](const auto &slice){ return model_volume == slice.model_volume; }); params.mode = MeshSlicingParams::SlicingMode::PositiveLargestContour; // Slice the bottom layers with SlicingMode::Regular. @@ -186,10 +194,12 @@ static std::vector slice_volumes_inner( for (; params.slicing_mode_normal_below_layer < zs.size() && zs[params.slicing_mode_normal_below_layer] < region_config.bottom_solid_min_thickness - EPSILON; ++ params.slicing_mode_normal_below_layer); } - out.push_back({ - model_volume->id(), - slice_volume(*model_volume, zs, params, throw_on_cancel_callback) - }); + std::vector sublayers; + std::vector expoly = slice_volume(*model_volume, zs, params, + &sublayers, + throw_on_cancel_callback); + out.push_back({model_volume->id(), std::move(expoly)}); + outSublayers->push_back({model_volume->id(), std::move(sublayers)}); } } else { assert(! print_config.spiral_vase); @@ -197,16 +207,20 @@ static std::vector slice_volumes_inner( for (const PrintObjectRegions::LayerRangeRegions &layer_range : layer_ranges) if (layer_range.has_volume(model_volume->id())) slicing_ranges.emplace_back(layer_range.layer_height_range); - if (! slicing_ranges.empty()) - out.push_back({ - model_volume->id(), - slice_volume(*model_volume, zs, slicing_ranges, params, throw_on_cancel_callback) - }); + if (!slicing_ranges.empty()) { + std::vector sublayers; + std::vector expoly = slice_volume(*model_volume, zs, slicing_ranges, + params, &sublayers, + throw_on_cancel_callback); + out.push_back({model_volume->id(), std::move(expoly)}); + outSublayers->push_back({model_volume->id(), std::move(sublayers)}); + } } - if (! out.empty() && out.back().slices.empty()) + if (!out.empty() && out.back().slices.empty()) { out.pop_back(); + outSublayers->pop_back(); + } } - return out; } @@ -236,7 +250,7 @@ static std::vector::const_iterator layer_ } static std::vector::const_iterator layer_range_next( - const std::vector &layer_ranges, + const std::vector &layer_ranges, std::vector::const_iterator it, double z) { @@ -247,6 +261,8 @@ static std::vector::const_iterator layer_ } static std::vector> slices_to_regions( + const PrintConfig &print_config, + const PrintObject &print_object, ModelVolumePtrs model_volumes, const PrintObjectRegions &print_object_regions, const std::vector &zs, @@ -327,7 +343,7 @@ static std::vector> slices_to_regions( float z = zs_complex[range.begin()].second; auto it_layer_range = layer_range_first(print_object_regions.layer_ranges, z); // Per volume_regions slices at this Z height. - struct RegionSlice { + struct RegionSlice { ExPolygons expolygons; // Identifier of this region in PrintObjectRegions::all_regions int region_id; @@ -403,7 +419,7 @@ static std::vector> slices_to_regions( } } // Don't unite the regions if ! clip_multipart_objects. In that case it is user's responsibility - // to handle region overlaps. Indeed, one may intentionally let the regions overlap to produce crossing perimeters + // to handle region overlaps. Indeed, one may intentionally let the regions overlap to produce crossing perimeters // for example. if (merged && clip_multipart_objects) expolygons = closing_ex(expolygons, float(scale_(EPSILON))); @@ -457,7 +473,7 @@ std::string fix_slicing_errors(LayerPtrs &layers, const std::function &t // Collect outer contours and holes from the valid layers above & below. Polygons outer; outer.reserve( - ((upper_surfaces == nullptr) ? 0 : upper_surfaces->size()) + + ((upper_surfaces == nullptr) ? 0 : upper_surfaces->size()) + ((lower_surfaces == nullptr) ? 0 : lower_surfaces->size())); size_t num_holes = 0; if (upper_surfaces) @@ -543,7 +559,7 @@ void PrintObject::slice() } }); if (m_layers.empty()) - throw Slic3r::SlicingError("No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry.\n"); + throw Slic3r::SlicingError("No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry.\n"); this->set_done(posSlice); } @@ -673,6 +689,97 @@ static inline void apply_mm_segmentation(PrintObject &print_object, ThrowOnCance }); } +Layer *make_dither_layer(Layer *refLayer, double bottom, double top) +{ + coordf_t height = refLayer->height; + coordf_t hi = refLayer->slice_z + height / 2; + coordf_t lo = refLayer->slice_z - height / 2; + coordf_t h_new = height * (top - bottom); + Layer * layer = new Layer(refLayer->id(), refLayer->object(), h_new, + refLayer->bottom_z() + height * coordf_t(top), + refLayer->bottom_z() + height * coordf_t(bottom) + h_new / 2); + layer->dithered = true; + return layer; +} + +void merge_sublayers_to_slices(std::vector & volume_slices, + std::vector &volume_sublayers, + int which, // 0 - bottom, 1 - halfUp, 2 - halfDn, 3 - top + int from, + int to) +{ + for (VolumeSublayers &sublayers : volume_sublayers) { + VolumeSlices &v_slices = volume_slices_find_by_id(volume_slices, sublayers.volume_id); + auto & slices = v_slices.slices; + if (which == 0) + slices.insert(slices.begin() + to, std::move(sublayers.sublayers[from].bottom_)); + else if (which == 1) + slices.insert(slices.begin() + to, std::move(sublayers.sublayers[from].halfUp_)); + else if (which == 2) + slices.insert(slices.begin() + to, std::move(sublayers.sublayers[from].halfDn_)); + else if (which == 3) + slices.insert(slices.begin() + to, std::move(sublayers.sublayers[from].top_)); + else + BOOST_LOG_TRIVIAL(error) << "merge_sublayers_to_slices illegal call"; + } +} + +LayerPtrs add_dithering_layers(const LayerPtrs & layers, + std::vector & volume_slices, + std::vector &volume_sublayers) +{ + LayerPtrs original(layers); + LayerPtrs resulting; + Layer * newLayer[4]; + + for (int ll = 0; ll < original.size(); ll++) { + if (std::any_of(volume_sublayers.begin(), volume_sublayers.end(), + [&ll](VolumeSublayers &v_sub) { + return !v_sub.sublayers[ll].bottom_.empty(); + })) { + newLayer[0] = make_dither_layer(original[ll], 0., 0.25); + newLayer[0]->lower_layer = original[ll]->lower_layer; + merge_sublayers_to_slices(volume_slices, volume_sublayers, 0, ll, resulting.size()); + resulting.push_back(newLayer[0]); + } + if (std::any_of(volume_sublayers.begin(), volume_sublayers.end(), + [&ll](VolumeSublayers &v_sub) { + return !v_sub.sublayers[ll].halfUp_.empty(); + })) { + newLayer[1] = make_dither_layer(original[ll], 0.25, 0.75); + newLayer[1]->lower_layer = newLayer[0]; // must be != nullptr + newLayer[0]->upper_layer = newLayer[1]; + merge_sublayers_to_slices(volume_slices, volume_sublayers, 1, ll, resulting.size()); + resulting.push_back(newLayer[1]); + } + if (std::any_of(volume_sublayers.begin(), volume_sublayers.end(), + [&ll](VolumeSublayers &v_sub) { + return !v_sub.sublayers[ll].halfDn_.empty(); + })) { + newLayer[2] = make_dither_layer(original[ll], 0.25, 0.75); + merge_sublayers_to_slices(volume_slices, volume_sublayers, 2, ll, resulting.size()); + resulting.push_back(newLayer[2]); + } + if (std::any_of(volume_sublayers.begin(), volume_sublayers.end(), + [&ll](VolumeSublayers &v_sub) { + return !v_sub.sublayers[ll].top_.empty(); + })) { + newLayer[3] = make_dither_layer(original[ll], 0.75, 1.); + newLayer[3]->upper_layer = original[ll]->upper_layer; + newLayer[3]->lower_layer = newLayer[2]; // must be != nullptr + newLayer[2]->upper_layer = newLayer[3]; + merge_sublayers_to_slices(volume_slices, volume_sublayers, 3, ll, resulting.size()); + resulting.push_back(newLayer[3]); + } + resulting.push_back(original[ll]); + } + // Renumber + size_t start = original[0]->id(); + for (size_t ll = 0; ll < resulting.size(); ll++) + resulting[ll]->set_id(start + ll); + return resulting; +} + // 1) Decides Z positions of the layers, // 2) Initializes layers and their regions // 3) Slices the object meshes @@ -696,11 +803,37 @@ void PrintObject::slice_volumes() layer->m_regions.emplace_back(new LayerRegion(layer, pr.get())); } - std::vector slice_zs = zs_from_layers(m_layers); - std::vector> region_slices = slices_to_regions(this->model_object()->volumes, *m_shared_regions, slice_zs, - slice_volumes_inner( - print->config(), this->config(), this->trafo_centered(), - this->model_object()->volumes, m_shared_regions->layer_ranges, slice_zs, throw_on_cancel_callback), + std::vector slice_zs = zs_from_layers(m_layers, true); + std::vector volume_sublayers; + std::vector volume_slices = slice_volumes_inner( + print->config(), + this->config(), + this->trafo_centered(), + this->model_object()->volumes, + m_shared_regions->layer_ranges, + slice_zs, + &volume_sublayers, + throw_on_cancel_callback); + + if (this->config().z_dither) { + m_layers = add_dithering_layers(m_layers, volume_slices, volume_sublayers); + for (Layer *layer : m_layers) { + if (layer->dithered) { + layer->m_regions.reserve(m_shared_regions->all_regions.size()); + for (const std::unique_ptr &pr : m_shared_regions->all_regions) + layer->m_regions.emplace_back(new LayerRegion(layer, pr.get())); + } + } + slice_zs = zs_from_layers(m_layers, false); + } + + std::vector> region_slices = slices_to_regions( + print->config(), + *this, + this->model_object()->volumes, + *m_shared_regions, + slice_zs, + std::move(volume_slices), m_config.clip_multipart_objects, throw_on_cancel_callback); @@ -710,7 +843,7 @@ void PrintObject::slice_volumes() m_layers[layer_id]->regions()[region_id]->slices.append(std::move(by_layer[layer_id]), stInternal); } region_slices.clear(); - + BOOST_LOG_TRIVIAL(debug) << "Slicing volumes - removing top empty layers"; while (! m_layers.empty()) { const Layer *layer = m_layers.back(); @@ -778,7 +911,7 @@ void PrintObject::slice_volumes() layerm->slices.set( union_ex( Slic3r::elephant_foot_compensation( - (delta == 0.f) ? lslices_1st_layer : offset_ex(lslices_1st_layer, delta), + (delta == 0.f) ? lslices_1st_layer : offset_ex(lslices_1st_layer, delta), layerm->flow(frExternalPerimeter), unscale(elfoot))), stInternal); if (xy_compensation_scaled < 0.f) @@ -828,7 +961,8 @@ std::vector PrintObject::slice_support_volumes(const ModelVolumeType m std::vector slices; if (it_volume != it_volume_end) { // Found at least a single support volume of model_volume_type. - std::vector zs = zs_from_layers(this->layers()); + std::vector zs = zs_from_layers(this->layers(), true); // exclude ditthered layers + size_t num_layers = this->layers().size(); std::vector merge_layers; bool merge = false; const Print *print = this->print(); @@ -837,29 +971,41 @@ std::vector PrintObject::slice_support_volumes(const ModelVolumeType m params.trafo = this->trafo_centered(); for (; it_volume != it_volume_end; ++ it_volume) if ((*it_volume)->type() == model_volume_type) { - std::vector slices2 = slice_volume(*(*it_volume), zs, params, throw_on_cancel_callback); + std::vector sublayers2; + std::vector slices2 = slice_volume(*(*it_volume), zs, params, &sublayers2, throw_on_cancel_callback); if (slices.empty()) { - slices.reserve(slices2.size()); - for (ExPolygons &src : slices2) - slices.emplace_back(to_polygons(std::move(src))); + slices.reserve(num_layers); + size_t slice_idx = 0; + for (size_t i = 0; i < num_layers; i++) { + if (this->layers()[i]->dithered) + slices.emplace_back(Polygons()); + else { + slices.emplace_back(to_polygons(std::move(slices2[slice_idx]))); + slice_idx++; + } + } } else if (!slices2.empty()) { if (merge_layers.empty()) - merge_layers.assign(zs.size(), false); - for (size_t i = 0; i < zs.size(); ++ i) { - if (slices[i].empty()) - slices[i] = to_polygons(std::move(slices2[i])); - else if (! slices2[i].empty()) { - append(slices[i], to_polygons(std::move(slices2[i]))); - merge_layers[i] = true; - merge = true; + merge_layers.assign(num_layers, false); + size_t slice_idx = 0; + for (size_t i = 0; i < num_layers; ++ i) { + if (!this->layers()[i]->dithered) { + if (slices[i].empty()) + slices[i] = to_polygons(std::move(slices2[slice_idx])); + else if (!slices2[slice_idx].empty()) { + append(slices[i], to_polygons(std::move(slices2[slice_idx]))); + merge_layers[i] = true; + merge = true; + } + slice_idx++; } } } } if (merge) { std::vector to_merge; - to_merge.reserve(zs.size()); - for (size_t i = 0; i < zs.size(); ++ i) + to_merge.reserve(num_layers); + for (size_t i = 0; i < num_layers; ++ i) if (merge_layers[i]) to_merge.emplace_back(&slices[i]); tbb::parallel_for( diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index 52cb177bb8a..7d350234fd6 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -48,7 +48,7 @@ namespace Slic3r { // how much we extend support around the actual contact area //FIXME this should be dependent on the nozzle diameter! -#define SUPPORT_MATERIAL_MARGIN 1.5 +#define SUPPORT_MATERIAL_MARGIN 1.5 // Increment used to reach MARGIN in steps to avoid trespassing thin objects #define NUM_MARGIN_STEPS 3 @@ -68,7 +68,7 @@ const char* support_surface_type_to_color_name(const PrintObjectSupportMaterial: case PrintObjectSupportMaterial::sltTopContact: return "rgb(255,0,0)"; // "red"; case PrintObjectSupportMaterial::sltTopInterface: return "rgb(0,255,0)"; // "green"; case PrintObjectSupportMaterial::sltBase: return "rgb(0,0,255)"; // "blue"; - case PrintObjectSupportMaterial::sltBottomInterface:return "rgb(255,255,128)"; // yellow + case PrintObjectSupportMaterial::sltBottomInterface:return "rgb(255,255,128)"; // yellow case PrintObjectSupportMaterial::sltBottomContact: return "rgb(255,0,255)"; // magenta case PrintObjectSupportMaterial::sltRaftInterface: return "rgb(0,255,255)"; case PrintObjectSupportMaterial::sltRaftBase: return "rgb(128,128,128)"; @@ -79,7 +79,7 @@ const char* support_surface_type_to_color_name(const PrintObjectSupportMaterial: Point export_support_surface_type_legend_to_svg_box_size() { - return Point(scale_(1.+10.*8.), scale_(3.)); + return Point(scale_(1.+10.*8.), scale_(3.)); } void export_support_surface_type_legend_to_svg(SVG &svg, const Point &pos) @@ -129,8 +129,8 @@ void export_print_z_polygons_to_svg(const char *path, PrintObjectSupportMaterial } void export_print_z_polygons_and_extrusions_to_svg( - const char *path, - PrintObjectSupportMaterial::MyLayer ** const layers, + const char *path, + PrintObjectSupportMaterial::MyLayer ** const layers, size_t n_layers, SupportLayer &support_layer) { @@ -166,7 +166,7 @@ static std::vector rasterize_polygons(const Vec2i &grid_size, con agg::pixfmt_gray8 pixel_renderer(rendering_buffer); agg::renderer_base raw_renderer(pixel_renderer); agg::renderer_scanline_aa_solid> renderer(raw_renderer); - + renderer.color(agg::pixfmt_gray8::color_type(255)); raw_renderer.clear(agg::pixfmt_gray8::color_type(0)); @@ -224,16 +224,16 @@ static Polygons contours_simplified(const Vec2i &grid_size, const double pixel_s bool current = cell_inside[addr] != 0; if (left != current) { lines.push_back( - left ? - Line(Point(c, r+1), Point(c, r )) : + left ? + Line(Point(c, r+1), Point(c, r )) : Line(Point(c, r ), Point(c, r+1))); start_point_to_line_idx.emplace_back(lines.back().a, int(lines.size()) - 1); } if (top != current) { lines.push_back( - top ? + top ? Line(Point(c , r), Point(c+1, r)) : - Line(Point(c+1, r), Point(c , r))); + Line(Point(c+1, r), Point(c , r))); start_point_to_line_idx.emplace_back(lines.back().a, int(lines.size()) - 1); } } @@ -251,7 +251,7 @@ static Polygons contours_simplified(const Vec2i &grid_size, const double pixel_s poly.points.push_back(lines[i_candidate].b); int i_line_current = i_candidate; for (;;) { - auto line_range = std::equal_range(std::begin(start_point_to_line_idx), std::end(start_point_to_line_idx), + auto line_range = std::equal_range(std::begin(start_point_to_line_idx), std::end(start_point_to_line_idx), std::make_pair(lines[i_line_current].b, 0), [](const auto& l, const auto& r) { return l.first < r.first; }); // The interval has to be non empty, there shall be at least one line continuing the current one. assert(line_range.first != line_range.second); @@ -313,7 +313,7 @@ static Polygons contours_simplified(const Vec2i &grid_size, const double pixel_s p(1) += (v(0) < 0) ? - offset : offset; p(0) += (v(1) > 0) ? - offset : offset; pts.push_back(p); - } + } } poly.points = std::move(pts); } @@ -385,7 +385,7 @@ PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object SupportMaterialPattern support_pattern = m_object_config->support_material_pattern; m_support_params.with_sheath = m_object_config->support_material_with_sheath; - m_support_params.base_fill_pattern = + m_support_params.base_fill_pattern = support_pattern == smpHoneycomb ? ipHoneycomb : m_support_params.support_density > 0.95 || m_support_params.with_sheath ? ipRectilinear : ipSupportBase; m_support_params.interface_fill_pattern = (m_support_params.interface_density > 0.95 ? ipRectilinear : ipSupportBase); @@ -398,9 +398,9 @@ PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object // Using the std::deque as an allocator. inline PrintObjectSupportMaterial::MyLayer& layer_allocate( - std::deque &layer_storage, + std::deque &layer_storage, PrintObjectSupportMaterial::SupporLayerType layer_type) -{ +{ layer_storage.push_back(PrintObjectSupportMaterial::MyLayer()); layer_storage.back().layer_type = layer_type; return layer_storage.back(); @@ -410,7 +410,7 @@ inline PrintObjectSupportMaterial::MyLayer& layer_allocate( std::deque &layer_storage, tbb::spin_mutex &layer_storage_mutex, PrintObjectSupportMaterial::SupporLayerType layer_type) -{ +{ layer_storage_mutex.lock(); layer_storage.push_back(PrintObjectSupportMaterial::MyLayer()); PrintObjectSupportMaterial::MyLayer *layer_new = &layer_storage.back(); @@ -425,7 +425,7 @@ inline void layers_append(PrintObjectSupportMaterial::MyLayersPtr &dst, const Pr } // Support layer that is covered by some form of dense interface. -static constexpr const std::initializer_list support_types_interface { +static constexpr const std::initializer_list support_types_interface { PrintObjectSupportMaterial::sltRaftInterface, PrintObjectSupportMaterial::sltBottomContact, PrintObjectSupportMaterial::sltBottomInterface, PrintObjectSupportMaterial::sltTopContact, PrintObjectSupportMaterial::sltTopInterface }; @@ -462,7 +462,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) iRun ++; for (const MyLayer *layer : top_contacts) Slic3r::SVG::export_expolygons( - debug_out_path("support-top-contacts-%d-%lf.svg", iRun, layer->print_z), + debug_out_path("support-top-contacts-%d-%lf.svg", iRun, layer->print_z), union_ex(layer->polygons)); #endif /* SLIC3R_DEBUG */ @@ -480,7 +480,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) #ifdef SLIC3R_DEBUG for (size_t layer_id = 0; layer_id < object.layers().size(); ++ layer_id) Slic3r::SVG::export_expolygons( - debug_out_path("support-areas-%d-%lf.svg", iRun, object.layers()[layer_id]->print_z), + debug_out_path("support-areas-%d-%lf.svg", iRun, object.layers()[layer_id]->print_z), union_ex(layer_support_areas[layer_id])); #endif /* SLIC3R_DEBUG */ @@ -499,7 +499,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) #ifdef SLIC3R_DEBUG for (const MyLayer *layer : top_contacts) Slic3r::SVG::export_expolygons( - debug_out_path("support-top-contacts-trimmed-by-object-%d-%lf.svg", iRun, layer->print_z), + debug_out_path("support-top-contacts-trimmed-by-object-%d-%lf.svg", iRun, layer->print_z), union_ex(layer->polygons)); #endif @@ -511,13 +511,13 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) #ifdef SLIC3R_DEBUG for (MyLayersPtr::const_iterator it = intermediate_layers.begin(); it != intermediate_layers.end(); ++ it) Slic3r::SVG::export_expolygons( - debug_out_path("support-base-layers-%d-%lf.svg", iRun, (*it)->print_z), + debug_out_path("support-base-layers-%d-%lf.svg", iRun, (*it)->print_z), union_ex((*it)->polygons)); #endif /* SLIC3R_DEBUG */ BOOST_LOG_TRIVIAL(info) << "Support generator - Trimming top contacts by bottom contacts"; - // Because the top and bottom contacts are thick slabs, they may overlap causing over extrusion + // Because the top and bottom contacts are thick slabs, they may overlap causing over extrusion // and unwanted strong bonds to the object. // Rather trim the top contacts by their overlapping bottom contacts to leave a gap instead of over extruding // top contacts over the bottom contacts. @@ -526,7 +526,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) BOOST_LOG_TRIVIAL(info) << "Support generator - Creating interfaces"; - // Propagate top / bottom contact layers to generate interface layers + // Propagate top / bottom contact layers to generate interface layers // and base interface layers (for soluble interface / non souble base only) auto [interface_layers, base_interface_layers] = this->generate_interface_layers(bottom_contacts, top_contacts, intermediate_layers, layer_storage); @@ -540,11 +540,11 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) #ifdef SLIC3R_DEBUG for (const MyLayer *l : interface_layers) Slic3r::SVG::export_expolygons( - debug_out_path("support-interface-layers-%d-%lf.svg", iRun, l->print_z), + debug_out_path("support-interface-layers-%d-%lf.svg", iRun, l->print_z), union_ex(l->polygons)); for (const MyLayer *l : base_interface_layers) Slic3r::SVG::export_expolygons( - debug_out_path("support-base-interface-layers-%d-%lf.svg", iRun, l->print_z), + debug_out_path("support-base-interface-layers-%d-%lf.svg", iRun, l->print_z), union_ex(l->polygons)); #endif // SLIC3R_DEBUG @@ -611,7 +611,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) height_min = std::min(height_min, layer.height); } if (! empty) { - // Here the upper_layer and lower_layer pointers are left to null at the support layers, + // Here the upper_layer and lower_layer pointers are left to null at the support layers, // as they are never used. These pointers are candidates for removal. bool this_layer_contacts_only = num_top_contacts > 0 && num_top_contacts == num_interfaces; size_t this_layer_id_interface = layer_id_interface; @@ -747,7 +747,7 @@ class SupportGridPattern public: SupportGridPattern( // Support islands, to be stretched into a grid. Already trimmed with min(lower_layer_offset, m_gap_xy) - const Polygons *support_polygons, + const Polygons *support_polygons, // Trimming polygons, to trim the stretched support islands. support_polygons were already trimmed with trimming_polygons. const Polygons *trimming_polygons, const SupportGridParams ¶ms) : @@ -790,7 +790,7 @@ class SupportGridPattern Vec2i grid_size_raw(int(ceil((m_bbox.max.x() - m_bbox.min.x()) / m_pixel_size)), int(ceil((m_bbox.max.y() - m_bbox.min.y()) / m_pixel_size))); // Overlay macro blocks of (oversampling x oversampling) over the grid. - Vec2i grid_blocks((grid_size_raw.x() + oversampling - 1 - 2) / oversampling, + Vec2i grid_blocks((grid_size_raw.x() + oversampling - 1 - 2) / oversampling, (grid_size_raw.y() + oversampling - 1 - 2) / oversampling); // and resize the grid to fit the macro blocks + one pixel boundary. m_grid_size = grid_blocks * oversampling + Vec2i(2, 2); @@ -868,7 +868,7 @@ class SupportGridPattern // As offset_in_grid may be negative, m_support_polygons may stick slightly outside of islands. // Trim ti with islands. Points samples = island_samples( - offset_in_grid > 0 ? + offset_in_grid > 0 ? // Expanding, thus m_support_polygons are all inside islands. union_ex(*m_support_polygons) : // Shrinking, thus m_support_polygons may be trimmed a tiny bit by islands. @@ -1171,7 +1171,7 @@ class SupportGridPattern if (expoly.contour.points.size() > 2) { #if 0 pts.push_back(island_sample(expoly)); - #else + #else Polygons polygons = offset(expoly, - 20.f); for (const Polygon &poly : polygons) if (! poly.points.empty()) { @@ -1188,7 +1188,7 @@ class SupportGridPattern // Sort the points lexicographically, so a binary search could be used to locate points inside a bounding box. std::sort(pts.begin(), pts.end()); return pts; - } + } SupportMaterialStyle m_style; const Polygons *m_support_polygons; @@ -1255,7 +1255,7 @@ namespace SupportMaterialInternal { } return false; } - static bool has_bridging_extrusions(const Layer &layer) + static bool has_bridging_extrusions(const Layer &layer) { for (const LayerRegion *region : layer.regions()) { if (SupportMaterialInternal::has_bridging_perimeters(region->perimeters)) @@ -1307,11 +1307,11 @@ namespace SupportMaterialInternal { } static void remove_bridges_from_contacts( - const PrintConfig &print_config, + const PrintConfig &print_config, const Layer &lower_layer, const Polygons &lower_layer_polygons, const LayerRegion &layerm, - float fw, + float fw, Polygons &contact_polygons) { // compute the area of bridging perimeters @@ -1334,7 +1334,7 @@ namespace SupportMaterialInternal { #else Polylines overhang_perimeters = diff_pl(layerm.perimeters.as_polylines(), lower_grown_slices); #endif - + // only consider straight overhangs // only consider overhangs having endpoints inside layer's slices // convert bridging polylines into polygons by inflating them with their thickness @@ -1348,7 +1348,7 @@ namespace SupportMaterialInternal { const float w = float(0.5 * std::max(perimeter_bridge_flow.scaled_width(), perimeter_bridge_flow.scaled_spacing())) + scaled(0.001); for (Polyline &polyline : overhang_perimeters) if (polyline.is_straight()) { - // This is a bridge + // This is a bridge polyline.extend_start(fw); polyline.extend_end(fw); // Is the straight perimeter segment supported at both sides? @@ -1447,10 +1447,10 @@ static inline std::tuple detect_overhangs( const Layer &layer, const size_t layer_id, const Polygons &lower_layer_polygons, - const PrintConfig &print_config, + const PrintConfig &print_config, const PrintObjectConfig &object_config, - SupportAnnotations &annotations, - SlicesMarginCache &slices_margin, + SupportAnnotations &annotations, + SlicesMarginCache &slices_margin, const double gap_xy #ifdef SLIC3R_DEBUG , size_t iRun @@ -1473,7 +1473,7 @@ static inline std::tuple detect_overhangs( 0.; float no_interface_offset = 0.f; - if (layer_id == 0) + if (layer_id == 0) { // This is the first object layer, so the object is being printed on a raft and // we're here just to get the object footprint for the raft. @@ -1494,7 +1494,7 @@ static inline std::tuple detect_overhangs( const bool has_enforcer = ! annotations.enforcers_layers.empty() && ! annotations.enforcers_layers[layer_id].empty(); // Cache support trimming polygons derived from lower layer polygons, possible merged with "on build plate only" trimming polygons. - auto slices_margin_update = + auto slices_margin_update = [&slices_margin, &lower_layer, &lower_layer_polygons, buildplate_only, has_enforcer, &annotations, layer_id] (float slices_margin_offset, float no_interface_offset) { if (slices_margin.offset != slices_margin_offset) { @@ -1513,7 +1513,7 @@ static inline std::tuple detect_overhangs( } }; - no_interface_offset = std::accumulate(layer.regions().begin(), layer.regions().end(), FLT_MAX, + no_interface_offset = std::accumulate(layer.regions().begin(), layer.regions().end(), FLT_MAX, [](float acc, const LayerRegion *layerm) { return std::min(acc, float(layerm->flow(frExternalPerimeter).scaled_width())); }); float lower_layer_offset = 0; @@ -1521,11 +1521,11 @@ static inline std::tuple detect_overhangs( // Extrusion width accounts for the roundings of the extrudates. // It is the maximum widh of the extrudate. float fw = float(layerm->flow(frExternalPerimeter).scaled_width()); - lower_layer_offset = - (layer_id < (size_t)object_config.support_material_enforce_layers.value) ? + lower_layer_offset = + (layer_id < (size_t)object_config.support_material_enforce_layers.value) ? // Enforce a full possible support, ignore the overhang angle. 0.f : - (threshold_rad > 0. ? + (threshold_rad > 0. ? // Overhang defined by an angle. float(scale_(lower_layer.height / tan(threshold_rad))) : // Overhang defined by half the extrusion width. @@ -1552,12 +1552,12 @@ static inline std::tuple detect_overhangs( // are not supporting this layer. // However this may lead to a situation where regions at the current layer that are narrow thus not extrudable will generate unnecessary supports. // For example, see GH issue #3094 - opening(lower_layer_polygons, 0.5f * fw, lower_layer_offset + 0.5f * fw, SUPPORT_SURFACES_OFFSET_PARAMETERS)), + opening(lower_layer_polygons, 0.5f * fw, lower_layer_offset + 0.5f * fw, SUPPORT_SURFACES_OFFSET_PARAMETERS)), //FIXME This opening is targeted to reduce very thin regions to support, but it may lead to // no support at all for not so steep overhangs. 0.1f * fw); #else - diff_polygons = + diff_polygons = diff(layerm_polygons, expand(lower_layer_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS)); #endif @@ -1568,7 +1568,7 @@ static inline std::tuple detect_overhangs( } if (! diff_polygons.empty()) { // Offset the support regions back to a full overhang, restrict them to the full overhang. - // This is done to increase size of the supporting columns below, as they are calculated by + // This is done to increase size of the supporting columns below, as they are calculated by // propagating these contact surfaces downwards. diff_polygons = diff( intersection(expand(diff_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS), layerm_polygons), @@ -1592,8 +1592,8 @@ static inline std::tuple detect_overhangs( #ifdef SLIC3R_DEBUG { - ::Slic3r::SVG svg(debug_out_path("support-top-contacts-raw-run%d-layer%d-region%d.svg", - iRun, layer_id, + ::Slic3r::SVG svg(debug_out_path("support-top-contacts-raw-run%d-layer%d-region%d.svg", + iRun, layer_id, std::find_if(layer.regions().begin(), layer.regions().end(), [layerm](const LayerRegion* other){return other == layerm;}) - layer.regions().begin()), get_extents(diff_polygons)); Slic3r::ExPolygons expolys = union_ex(diff_polygons); @@ -1611,8 +1611,8 @@ static inline std::tuple detect_overhangs( #ifdef SLIC3R_DEBUG Slic3r::SVG::export_expolygons( - debug_out_path("support-top-contacts-filtered-run%d-layer%d-region%d-z%f.svg", - iRun, layer_id, + debug_out_path("support-top-contacts-filtered-run%d-layer%d-region%d-z%f.svg", + iRun, layer_id, std::find_if(layer.regions().begin(), layer.regions().end(), [layerm](const LayerRegion* other){return other == layerm;}) - layer.regions().begin(), layer.print_z), union_ex(diff_polygons)); @@ -1623,7 +1623,7 @@ static inline std::tuple detect_overhangs( // Store the exact contour of the overhang for the contact loops. polygons_append(overhang_polygons, diff_polygons); - // Let's define the required contact area by using a max gap of half the upper + // Let's define the required contact area by using a max gap of half the upper // extrusion width and extending the area according to the configured margin. // We increment the area in steps because we don't want our support to overflow // on the other side of the object (if it's very thin). @@ -1682,11 +1682,11 @@ static inline std::tuple detect_overhangs( // Allocate one, possibly two support contact layers. // For "thick" overhangs, one support layer will be generated to support normal extrusions, the other to support the "thick" extrusions. static inline std::pair new_contact_layer( - const PrintConfig &print_config, + const PrintConfig &print_config, const PrintObjectConfig &object_config, const SlicingParameters &slicing_params, const coordf_t support_layer_height_min, - const Layer &layer, + const Layer &layer, std::deque &layer_storage, tbb::spin_mutex &layer_storage_mutex) { @@ -1728,7 +1728,7 @@ static inline std::pair const Polygons& { if (lower_layer_polygons_for_dense_interface_cache.empty()) - lower_layer_polygons_for_dense_interface_cache = + lower_layer_polygons_for_dense_interface_cache = //FIXME no_interface_offset * 0.6f offset is not quite correct, one shall derive it based on an angle thus depending on layer height. opening(lower_layer_polygons, no_interface_offset * 0.5f, no_interface_offset * (0.6f + 0.5f), SUPPORT_SURFACES_OFFSET_PARAMETERS); return lower_layer_polygons_for_dense_interface_cache; }; - // Stretch support islands into a grid, trim them. + // Stretch support islands into a grid, trim them. SupportGridPattern support_grid_pattern(&contact_polygons, &slices_margin.polygons, grid_params); // 1) Contact polygons will be projected down. To keep the interface and base layers from growing, return a contour a tiny bit smaller than the grid cells. new_layer.contact_polygons = std::make_unique(support_grid_pattern.extract_support(grid_params.expansion_to_propagate, true @@ -1825,7 +1825,7 @@ static inline void fill_contact_layer( // thus some dense interface areas may not get supported. Trim the excess with contact_polygons at the following line. // See for example GH #4874. Polygons dense_interface_polygons_trimmed = intersection(dense_interface_polygons, *new_layer.contact_polygons); - // Stretch support islands into a grid, trim them. + // Stretch support islands into a grid, trim them. SupportGridPattern support_grid_pattern(&dense_interface_polygons_trimmed, &slices_margin.polygons, grid_params); new_layer.polygons = support_grid_pattern.extract_support(grid_params.expansion_to_slice, false #ifdef SLIC3R_DEBUG @@ -1858,7 +1858,7 @@ static inline void fill_contact_layer( if (! enforcer_polygons.empty() && ! slices_margin.all_polygons.empty() && layer_id > 0) { // Support enforcers used together with support enforcers. The support enforcers need to be handled separately from the rest of the support. - + SupportGridPattern support_grid_pattern(&enforcer_polygons, &slices_margin.all_polygons, grid_params); // 1) Contact polygons will be projected down. To keep the interface and base layers from growing, return a contour a tiny bit smaller than the grid cells. new_layer.enforcer_polygons = std::make_unique(support_grid_pattern.extract_support(grid_params.expansion_to_propagate, true @@ -1914,7 +1914,7 @@ static inline void fill_contact_layer( // Even after the contact layer was expanded into a grid, some of the contact islands may be too tiny to be extruded. // Remove those tiny islands from new_layer.polygons and new_layer.contact_polygons. - + // Store the overhang polygons. // The overhang polygons are used in the path generator for planning of the contact loops. // if (this->has_contact_loops()). Compared to "polygons", "overhang_polygons" are snug. @@ -1976,7 +1976,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ { #ifdef SLIC3R_DEBUG static int iRun = 0; - ++ iRun; + ++ iRun; #define SLIC3R_IRUN , iRun #endif /* SLIC3R_DEBUG */ @@ -1993,17 +1993,23 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ // Note that layer_id < layer->id when raft_layers > 0 as the layer->id incorporates the raft layers. // So layer_id == 0 means first object layer and layer->id == 0 means first print layer if there are no explicit raft layers. size_t num_layers = this->has_support() ? object.layer_count() : 1; - // For each overhang layer, two supporting layers may be generated: One for the overhangs extruded with a bridging flow, + // For each overhang layer, two supporting layers may be generated: One for the overhangs extruded with a bridging flow, // and the other for the overhangs extruded with a normal flow. contact_out.assign(num_layers * 2, nullptr); tbb::spin_mutex layer_storage_mutex; tbb::parallel_for(tbb::blocked_range(this->has_raft() ? 0 : 1, num_layers), [this, &object, &annotations, &layer_storage, &layer_storage_mutex, &contact_out] (const tbb::blocked_range& range) { - for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) + for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) { - const Layer &layer = *object.layers()[layer_id]; - Polygons lower_layer_polygons = (layer_id == 0) ? Polygons() : to_polygons(object.layers()[layer_id - 1]->lslices); + const Layer &layer = *object.layers()[layer_id]; + const Layer *lower_layer = layer.lower_layer; + if (lower_layer == nullptr && layer_id != 0) { + assert(layer.dithered); + continue; + } + Polygons lower_layer_polygons = (layer_id == 0) ? Polygons() : + to_polygons(object.layers()[layer_id - 1]->lslices); SlicesMarginCache slices_margin; auto [overhang_polygons, contact_polygons, enforcer_polygons, no_interface_offset] = @@ -2086,8 +2092,8 @@ static inline PrintObjectSupportMaterial::MyLayer* detect_bottom_contacts( { { union_safety_offset_ex(polygons_new) }, { "polygons_new", "red", "black", "", scaled(0.1f), 0.5f } } }); #endif /* SLIC3R_DEBUG */ - // Now find whether any projection of the contact surfaces above layer.print_z not yet supported by any - // top surfaces above layer.print_z falls onto this top surface. + // Now find whether any projection of the contact surfaces above layer.print_z not yet supported by any + // top surfaces above layer.print_z falls onto this top surface. // Touching are the contact surfaces supported exclusively by this top surfaces. // Don't use a safety offset as it has been applied during insertion of polygons. if (top.empty()) @@ -2183,7 +2189,7 @@ static inline PrintObjectSupportMaterial::MyLayer* detect_bottom_contacts( // Returns polygons to print + polygons to propagate downwards. // Called twice: First for normal supports, possibly trimmed by "on build plate only", second for support enforcers not trimmed by "on build plate only". static inline std::pair project_support_to_grid(const Layer &layer, const SupportGridParams &grid_params, const Polygons &overhangs, Polygons *layer_buildplate_covered -#ifdef SLIC3R_DEBUG +#ifdef SLIC3R_DEBUG , size_t iRun, size_t layer_id, const char *debug_name #endif /* SLIC3R_DEBUG */ ) @@ -2216,7 +2222,7 @@ static inline std::pair project_support_to_grid(const Layer // 1) Cache the slice of a support volume. The support volume is expanded by 1/2 of support material flow spacing // to allow a placement of suppot zig-zag snake along the grid lines. task_group_inner.run([&grid_params, &support_grid_pattern, &out -#ifdef SLIC3R_DEBUG +#ifdef SLIC3R_DEBUG , &layer, layer_id, iRun, debug_name #endif /* SLIC3R_DEBUG */ ] { @@ -2234,7 +2240,7 @@ static inline std::pair project_support_to_grid(const Layer // 2) Support polygons will be projected down. To keep the interface and base layers from growing, return a contour a tiny bit smaller than the grid cells. task_group_inner.run([&grid_params, &support_grid_pattern, &out -#ifdef SLIC3R_DEBUG +#ifdef SLIC3R_DEBUG , &layer, layer_id, &overhangs_projection, &trimming, iRun, debug_name #endif /* SLIC3R_DEBUG */ ] { @@ -2261,10 +2267,10 @@ static inline std::pair project_support_to_grid(const Layer } // Generate bottom contact layers supporting the top contact layers. -// For a soluble interface material synchronize the layer heights with the object, +// For a soluble interface material synchronize the layer heights with the object, // otherwise set the layer height to a bridging flow of a support interface nozzle. PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_contact_layers_and_layer_support_areas( - const PrintObject &object, const MyLayersPtr &top_contacts, std::vector &buildplate_covered, + const PrintObject &object, const MyLayersPtr &top_contacts, std::vector &buildplate_covered, MyLayerStorage &layer_storage, std::vector &layer_support_areas) const { if (top_contacts.empty()) @@ -2361,13 +2367,13 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta // Filtering the propagated support columns to two extrusions, overlapping by maximum 20%. // float column_propagation_filtering_radius = scaled(0.8 * 0.5 * (m_support_params.support_material_flow.spacing() + m_support_params.support_material_flow.width())); task_group.run([&grid_params, &overhangs_projection, &overhangs_projection_raw, &layer, &layer_support_area, layer_buildplate_covered /* , column_propagation_filtering_radius */ -#ifdef SLIC3R_DEBUG +#ifdef SLIC3R_DEBUG , iRun, layer_id #endif /* SLIC3R_DEBUG */ ] { // buildplate_covered[layer_id] will be consumed here. std::tie(layer_support_area, overhangs_projection) = project_support_to_grid(layer, grid_params, overhangs_projection_raw, layer_buildplate_covered -#ifdef SLIC3R_DEBUG +#ifdef SLIC3R_DEBUG , iRun, layer_id, "general" #endif /* SLIC3R_DEBUG */ ); @@ -2379,12 +2385,12 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta if (! enforcers_projection.empty()) // Project the enforcers polygons downwards, don't trim them with the "buildplate only" polygons. task_group.run([&grid_params, &enforcers_projection, &enforcers_projection_raw, &layer, &layer_support_area_enforcers -#ifdef SLIC3R_DEBUG +#ifdef SLIC3R_DEBUG , iRun, layer_id #endif /* SLIC3R_DEBUG */ ]{ std::tie(layer_support_area_enforcers, enforcers_projection) = project_support_to_grid(layer, grid_params, enforcers_projection_raw, nullptr -#ifdef SLIC3R_DEBUG +#ifdef SLIC3R_DEBUG , iRun, layer_id, "enforcers" #endif /* SLIC3R_DEBUG */ ); @@ -2534,8 +2540,8 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int }; std::sort(extremes.begin(), extremes.end(), layer_extreme_lower); - assert(extremes.empty() || - (extremes.front()->extreme_z() > m_slicing_params.raft_interface_top_z - EPSILON && + assert(extremes.empty() || + (extremes.front()->extreme_z() > m_slicing_params.raft_interface_top_z - EPSILON && (m_slicing_params.raft_layers() == 1 || // only raft contact layer extremes.front()->layer_type == sltTopContact || // first extreme is a top contact layer extremes.front()->extreme_z() > m_slicing_params.first_print_layer_height - EPSILON))); @@ -2633,12 +2639,12 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int } } else { // Insert intermediate layers. - size_t n_layers_extra = size_t(ceil(dist / m_slicing_params.max_suport_layer_height)); + size_t n_layers_extra = size_t(ceil(dist / m_slicing_params.max_suport_layer_height)); assert(n_layers_extra > 0); coordf_t step = dist / coordf_t(n_layers_extra); if (extr1 != nullptr && extr1->layer_type == sltTopContact && extr1->print_z + m_support_params.support_layer_height_min > extr1->bottom_z + step) { - // The bottom extreme is a bottom of a top surface. Ensure that the gap + // The bottom extreme is a bottom of a top surface. Ensure that the gap // between the 1st intermediate layer print_z and extr1->print_z is not too small. assert(extr1->bottom_z + m_support_params.support_layer_height_min < extr1->print_z + EPSILON); // Generate the first intermediate layer. @@ -2721,10 +2727,10 @@ void PrintObjectSupportMaterial::generate_base_layers( // Counting down due to the way idx_lower_or_equal caches indices to avoid repeated binary search over the complete sequence. for (int idx_intermediate = int(range.end()) - 1; idx_intermediate >= int(range.begin()); -- idx_intermediate) { - BOOST_LOG_TRIVIAL(trace) << "Support generator - generate_base_layers - creating layer " << + BOOST_LOG_TRIVIAL(trace) << "Support generator - generate_base_layers - creating layer " << idx_intermediate << " of " << intermediate_layers.size(); MyLayer &layer_intermediate = *intermediate_layers[idx_intermediate]; - // Layers must be sorted by print_z. + // Layers must be sorted by print_z. assert(idx_intermediate == 0 || layer_intermediate.print_z >= intermediate_layers[idx_intermediate - 1]->print_z); // Find a top_contact layer touching the layer_intermediate from above, if any, and collect its polygons into polygons_new. @@ -2736,7 +2742,7 @@ void PrintObjectSupportMaterial::generate_base_layers( [&layer_intermediate](const Layer* layer) { return layer->print_z <= layer_intermediate.print_z + EPSILON; }); // Polygons to trim polygons_new. - Polygons polygons_trimming; + Polygons polygons_trimming; // Trimming the base layer with any overlapping top layer. // Following cases are recognized: @@ -2745,7 +2751,7 @@ void PrintObjectSupportMaterial::generate_base_layers( // 3) base.print_z > top.print_z && base.bottom_z >= top.bottom_z -> Overlap, which will be solved inside generate_toolpaths() by reducing the base layer height where it overlaps the top layer. No trimming needed here. // 4) base.print_z > top.bottom_z && base.bottom_z < top.bottom_z -> Base overlaps with top.bottom_z. This must not happen. // 5) base.print_z <= top.print_z && base.bottom_z >= top.bottom_z -> Base is fully inside top. Trim base by top. - idx_top_contact_above = idx_lower_or_equal(top_contacts, idx_top_contact_above, + idx_top_contact_above = idx_lower_or_equal(top_contacts, idx_top_contact_above, [&layer_intermediate](const MyLayer *layer){ return layer->bottom_z <= layer_intermediate.print_z - EPSILON; }); // Collect all the top_contact layer intersecting with this layer. for (int idx_top_contact_overlapping = idx_top_contact_above; idx_top_contact_overlapping >= 0; -- idx_top_contact_overlapping) { @@ -2782,13 +2788,13 @@ void PrintObjectSupportMaterial::generate_base_layers( // 3) base.print_z > bottom.bottom_z && base.bottom_z < bottom.bottom_z -> Overlap, which will be solved inside generate_toolpaths() by reducing the bottom layer height where it overlaps the base layer. No trimming needed here. // 4) base.print_z > bottom.print_z && base.bottom_z >= bottom.print_z -> Base overlaps with bottom.print_z. This must not happen. // 5) base.print_z <= bottom.print_z && base.bottom_z >= bottom.bottom_z -> Base is fully inside top. Trim base by top. - idx_bottom_contact_overlapping = idx_lower_or_equal(bottom_contacts, idx_bottom_contact_overlapping, + idx_bottom_contact_overlapping = idx_lower_or_equal(bottom_contacts, idx_bottom_contact_overlapping, [&layer_intermediate](const MyLayer *layer){ return layer->bottom_print_z() <= layer_intermediate.print_z - EPSILON; }); // Collect all the bottom_contacts layer intersecting with this layer. for (int i = idx_bottom_contact_overlapping; i >= 0; -- i) { MyLayer &layer_bottom_overlapping = *bottom_contacts[i]; if (layer_bottom_overlapping.print_z < layer_intermediate.bottom_print_z() + EPSILON) - break; + break; // Base must not overlap with bottom.top_z. assert(! (layer_intermediate.print_z > layer_bottom_overlapping.print_z + EPSILON && layer_intermediate.bottom_z < layer_bottom_overlapping.print_z - EPSILON)); if (layer_intermediate.print_z <= layer_bottom_overlapping.print_z + EPSILON && layer_intermediate.bottom_z >= layer_bottom_overlapping.bottom_print_z() - EPSILON) @@ -2823,8 +2829,8 @@ void PrintObjectSupportMaterial::generate_base_layers( // Fillet the base polygons and trim them again with the top, interface and contact layers. $base->{$i} = diff( offset2( - $base->{$i}, - $fillet_radius_scaled, + $base->{$i}, + $fillet_radius_scaled, -$fillet_radius_scaled, # Use a geometric offsetting for filleting. JT_ROUND, @@ -2902,7 +2908,7 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object( if (object_layer.print_z - bridging_height > support_layer.print_z + gap_extra_above - EPSILON) break; some_region_overlaps = true; - polygons_append(polygons_trimming, + polygons_append(polygons_trimming, offset(region->fill_surfaces.filter_by_type(stBottomBridge), gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS)); if (region->region().config().overhangs.value) // Add bridging perimeters. @@ -2986,7 +2992,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_raf polygons_append(interface_polygons, expand(interfaces->polygons, inflate_factor_fine, SUPPORT_SURFACES_OFFSET_PARAMETERS)); if (base_interfaces != nullptr && ! base_interfaces->polygons.empty()) polygons_append(interface_polygons, expand(base_interfaces->polygons, inflate_factor_fine, SUPPORT_SURFACES_OFFSET_PARAMETERS)); - + // Output vector. MyLayersPtr raft_layers; @@ -3002,7 +3008,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_raf } if (! interface_polygons.empty()) { // Merge the untrimmed columns base with the expanded raft interface, to be used for the support base and interface. - base = union_(base, interface_polygons); + base = union_(base, interface_polygons); } // Do not add the raft contact layer, only add the raft layers below the contact layer. // Insert the 1st layer. @@ -3085,9 +3091,9 @@ std::pair 0, has soluble support and extruders are different. bool soluble_interface_non_soluble_base = // Zero z-gap between the overhangs and the support interface. - m_slicing_params.soluble_interface && + m_slicing_params.soluble_interface && // Interface extruder soluble. - m_object_config->support_material_interface_extruder.value > 0 && m_print_config->filament_soluble.get_at(m_object_config->support_material_interface_extruder.value - 1) && + m_object_config->support_material_interface_extruder.value > 0 && m_print_config->filament_soluble.get_at(m_object_config->support_material_interface_extruder.value - 1) && // Base extruder: Either "print with active extruder" not soluble. (m_object_config->support_material_extruder.value == 0 || ! m_print_config->filament_soluble.get_at(m_object_config->support_material_extruder.value - 1)); bool snug_supports = m_object_config->support_material_style.value == smsSnug; @@ -3148,9 +3154,9 @@ std::pair(0, int(intermediate_layers.size())), - [&bottom_contacts, &top_contacts, &intermediate_layers, &insert_layer, + [&bottom_contacts, &top_contacts, &intermediate_layers, &insert_layer, num_interface_layers_top, num_interface_layers_bottom, num_base_interface_layers_top, num_base_interface_layers_bottom, num_interface_layers_only_top, num_interface_layers_only_bottom, - snug_supports, &interface_layers, &base_interface_layers](const tbb::blocked_range& range) { + snug_supports, &interface_layers, &base_interface_layers](const tbb::blocked_range& range) { // Gather the top / bottom contact layers intersecting with num_interface_layers resp. num_interface_layers_only intermediate layers above / below // this intermediate layer. // Index of the first top contact layer intersecting the current intermediate layer. @@ -3182,7 +3188,7 @@ std::pair top_z) break; - polygons_append(top_contact_layer.bottom_z - EPSILON > top_inteface_z ? polygons_top_contact_projected_base : polygons_top_contact_projected_interface, + polygons_append(top_contact_layer.bottom_z - EPSILON > top_inteface_z ? polygons_top_contact_projected_base : polygons_top_contact_projected_interface, // For snug supports, project the overhang polygons covering the whole overhang, so that they will merge without a gap with support polygons of the other layers. // For grid supports, merging of support regions will be performed by the projection into grid. snug_supports ? *top_contact_layer.overhang_polygons : top_contact_layer.polygons); @@ -3194,7 +3200,7 @@ std::pair::max(); if (num_base_interface_layers_bottom > 0) // Some bottom base interface layers will be generated. - bottom_interface_z = num_interface_layers_only_bottom == 0 ? + bottom_interface_z = num_interface_layers_only_bottom == 0 ? // Only base interface layers to generate. std::numeric_limits::max() : intermediate_layers[std::max(0, idx_intermediate_layer - num_interface_layers_only_bottom)]->bottom_z; @@ -3217,7 +3223,7 @@ std::pairpolygons : nullptr, sltBase); } }); @@ -3227,7 +3233,7 @@ std::pairpolygons.empty(); } - void set_polygons_to_extrude(Polygons &&polygons) { - if (m_polygons_to_extrude == nullptr) + void set_polygons_to_extrude(Polygons &&polygons) { + if (m_polygons_to_extrude == nullptr) m_polygons_to_extrude = std::make_unique(std::move(polygons)); else *m_polygons_to_extrude = std::move(polygons); @@ -3356,9 +3362,9 @@ struct MyLayerExtruded const Polygons& polygons_to_extrude() const { return (m_polygons_to_extrude == nullptr) ? layer->polygons : *m_polygons_to_extrude; } bool could_merge(const MyLayerExtruded &other) const { - return ! this->empty() && ! other.empty() && + return ! this->empty() && ! other.empty() && std::abs(this->layer->height - other.layer->height) < EPSILON && - this->layer->bridging == other.layer->bridging; + this->layer->bridging == other.layer->bridging; } // Merge regions, perform boolean union over the merged polygons. @@ -3464,7 +3470,7 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const const Point* operator()(const Point &pt) const { return &pt; } }; typedef ClosestPointInRadiusLookup ClosestPointLookupType; - + Polygons loops0; { // find centerline of the external loop of the contours @@ -3561,7 +3567,7 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const for (int i = 1; i < n_contact_loops; ++ i) polygons_append(loop_polygons, opening( - loops0, + loops0, i * flow.scaled_spacing() + 0.5f * flow.scaled_spacing(), 0.5f * flow.scaled_spacing())); // Clip such loops to the side oriented towards the object. @@ -3621,7 +3627,7 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const // Remove empty lines. remove_degenerate(loop_lines); } - + // add the contact infill area to the interface area // note that growing loops by $circle_radius ensures no tiny // extrusions are left inside the circles; however it creates @@ -3693,7 +3699,7 @@ void modulate_extrusion_by_overlapping_layers( // 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, + n_overlapping_layers + 1, ExtrusionPathFragment(extrusion_path_template->mm3_per_mm, extrusion_path_template->width, extrusion_path_template->height)); // Don't use it, it will be released. extrusion_path_template = nullptr; @@ -3745,7 +3751,7 @@ void modulate_extrusion_by_overlapping_layers( #endif /* SLIC3R_DEBUG */ // End points of the original paths. - std::vector> path_ends; + std::vector> path_ends; // Collect the paths of this_layer. { Polylines &polylines = path_fragments.back().polylines; @@ -3954,7 +3960,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( // Insert the raft base layers. size_t n_raft_layers = size_t(std::max(0, int(m_slicing_params.raft_layers()) - 1)); tbb::parallel_for(tbb::blocked_range(0, n_raft_layers), - [this, &support_layers, &raft_layers, + [this, &support_layers, &raft_layers, &bbox_object, raft_angle_1st_layer, raft_angle_base, raft_angle_interface, link_max_length_factor] (const tbb::blocked_range& range) { for (size_t support_layer_id = range.begin(); support_layer_id < range.end(); ++ support_layer_id) @@ -3971,7 +3977,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( // Print the support base below the support columns, or the support base for the support columns plus the contacts. if (support_layer_id > 0) { - const Polygons &to_infill_polygons = (support_layer_id < m_slicing_params.base_raft_layers) ? + const Polygons &to_infill_polygons = (support_layer_id < m_slicing_params.base_raft_layers) ? raft_layer.polygons : //FIXME misusing contact_polygons for support columns. ((raft_layer.contact_polygons == nullptr) ? Polygons() : *raft_layer.contact_polygons); @@ -4016,13 +4022,13 @@ void PrintObjectSupportMaterial::generate_toolpaths( filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / density)); fill_expolygons_with_sheath_generate_paths( // Destination - support_layer.support_fills.entities, + support_layer.support_fills.entities, // Regions to fill raft_layer.polygons, // Filler and its parameters filler, density, // Extrusion parameters - (support_layer_id < m_slicing_params.base_raft_layers) ? erSupportMaterial : erSupportMaterialInterface, flow, + (support_layer_id < m_slicing_params.base_raft_layers) ? erSupportMaterial : erSupportMaterialInterface, flow, // sheath at first layer support_layer_id == 0, support_layer_id == 0); } @@ -4052,7 +4058,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( std::vector layer_caches(support_layers.size()); tbb::parallel_for(tbb::blocked_range(n_raft_layers, support_layers.size()), - [this, &support_layers, &bottom_contacts, &top_contacts, &intermediate_layers, &interface_layers, &base_interface_layers, &layer_caches, &loop_interface_processor, + [this, &support_layers, &bottom_contacts, &top_contacts, &intermediate_layers, &interface_layers, &base_interface_layers, &layer_caches, &loop_interface_processor, &bbox_object, &angles, link_max_length_factor] (const tbb::blocked_range& range) { // Indices of the 1st layer in their respective container at the support layer height. @@ -4068,7 +4074,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( // Pointer to the 1st layer interface filler. auto filler_first_layer = filler_first_layer_ptr ? filler_first_layer_ptr.get() : filler_interface.get(); // Filler for the base interface (to be used for soluble interface / non soluble base, to produce non soluble interface layer below soluble interface layer). - auto filler_base_interface = std::unique_ptr(base_interface_layers.empty() ? nullptr : + auto filler_base_interface = std::unique_ptr(base_interface_layers.empty() ? nullptr : Fill::new_from_type(m_support_params.interface_density > 0.95 || m_support_params.with_sheath ? ipRectilinear : ipSupportBase)); auto filler_support = std::unique_ptr(Fill::new_from_type(m_support_params.base_fill_pattern)); filler_interface->set_bounding_box(bbox_object); @@ -4081,7 +4087,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( { SupportLayer &support_layer = *support_layers[support_layer_id]; LayerCache &layer_cache = layer_caches[support_layer_id]; - float interface_angle_delta = m_object_config->support_material_style.value == smsSnug ? + float interface_angle_delta = m_object_config->support_material_style.value == smsSnug ? (support_layer.interface_id() & 1) ? float(- M_PI / 4.) : float(+ M_PI / 4.) : 0; @@ -4115,7 +4121,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( if (m_object_config->support_material_interface_layers == 0) { // If no top interface layers were requested, we treat the contact layer exactly as a generic base layer. if (m_support_params.can_merge_support_regions) { - if (base_layer.could_merge(top_contact_layer)) + if (base_layer.could_merge(top_contact_layer)) base_layer.merge(std::move(top_contact_layer)); else if (base_layer.empty()) base_layer = std::move(top_contact_layer); @@ -4127,7 +4133,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( // to trim other layers. if (top_contact_layer.could_merge(interface_layer)) top_contact_layer.merge(std::move(interface_layer)); - } + } if ((m_object_config->support_material_interface_layers == 0 || m_object_config->support_material_bottom_interface_layers == 0) && m_support_params.can_merge_support_regions) { if (base_layer.could_merge(bottom_contact_layer)) base_layer.merge(std::move(bottom_contact_layer)); @@ -4155,7 +4161,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( MyLayerExtruded &layer_ex = (i == 0) ? top_contact_layer : (i == 1 ? bottom_contact_layer : interface_layer); if (layer_ex.empty() || layer_ex.polygons_to_extrude().empty()) continue; - bool interface_as_base = m_object_config->support_material_interface_layers.value == 0 || + bool interface_as_base = m_object_config->support_material_interface_layers.value == 0 || (m_object_config->support_material_bottom_interface_layers == 0 && &layer_ex == &bottom_contact_layer); //FIXME Bottom interfaces are extruded with the briding flow. Some bridging layers have its height slightly reduced, therefore // the bridging flow does not quite apply. Reduce the flow to area of an ellipse? (A = pi * a * b) @@ -4172,7 +4178,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( filler_interface->link_max_length = coord_t(scale_(filler_interface->spacing * link_max_length_factor / density)); fill_expolygons_generate_paths( // Destination - layer_ex.extrusions, + layer_ex.extrusions, // Regions to fill union_safety_offset_ex(layer_ex.polygons_to_extrude()), // Filler and its parameters @@ -4193,7 +4199,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / m_support_params.interface_density)); fill_expolygons_generate_paths( // Destination - base_interface_layer.extrusions, + base_interface_layer.extrusions, //base_layer_interface.extrusions, // Regions to fill union_safety_offset_ex(base_interface_layer.polygons_to_extrude()), @@ -4339,7 +4345,7 @@ void PrintObjectSupportMaterial::clip_by_pillars( coord_t pillar_size = scale_(PILLAR_SIZE); coord_t pillar_spacing = scale_(PILLAR_SPACING); - + // A regular grid of pillars, filling the 2D bounding box. Polygons grid; { @@ -4349,7 +4355,7 @@ void PrintObjectSupportMaterial::clip_by_pillars( pillar.points.push_back(Point(pillar_size, 0)); pillar.points.push_back(Point(pillar_size, pillar_size)); pillar.points.push_back(Point(0, pillar_size)); - + // 2D bounding box of the projection of all contact polygons. BoundingBox bbox; for (LayersPtr::const_iterator it = top_contacts.begin(); it != top_contacts.end(); ++ it) @@ -4363,30 +4369,30 @@ void PrintObjectSupportMaterial::clip_by_pillars( } } } - + // add pillars to every layer for my $i (0..n_support_z) { $shape->[$i] = [ @$grid ]; } - + // build capitals for my $i (0..n_support_z) { my $z = $support_z->[$i]; - + my $capitals = intersection( $grid, $contact->{$z} // [], ); - + // work on one pillar at time (if any) to prevent the capitals from being merged - // but store the contact area supported by the capital because we need to make + // but store the contact area supported by the capital because we need to make // sure nothing is left my $contact_supported_by_capitals = []; foreach my $capital (@$capitals) { // enlarge capital tops $capital = offset([$capital], +($pillar_spacing - $pillar_size)/2); push @$contact_supported_by_capitals, @$capital; - + for (my $j = $i-1; $j >= 0; $j--) { my $jz = $support_z->[$j]; $capital = offset($capital, -$self->interface_flow->scaled_width/2); @@ -4394,7 +4400,7 @@ void PrintObjectSupportMaterial::clip_by_pillars( push @{ $shape->[$j] }, @$capital; } } - + // Capitals will not generally cover the whole contact area because there will be // remainders. For now we handle this situation by projecting such unsupported // areas to the ground, just like we would do with a normal support. @@ -4412,10 +4418,10 @@ void PrintObjectSupportMaterial::clip_by_pillars( sub clip_with_shape { my ($self, $support, $shape) = @_; - + foreach my $i (keys %$support) { - // don't clip bottom layer with shape so that we - // can generate a continuous base flange + // don't clip bottom layer with shape so that we + // can generate a continuous base flange // also don't clip raft layers next if $i == 0; next if $i < $self->object_config->raft_layers; diff --git a/src/libslic3r/TriangleMeshSlicer.hpp b/src/libslic3r/TriangleMeshSlicer.hpp index 5a3ba31a071..8e86237e179 100644 --- a/src/libslic3r/TriangleMeshSlicer.hpp +++ b/src/libslic3r/TriangleMeshSlicer.hpp @@ -46,16 +46,17 @@ struct MeshSlicingParamsEx : public MeshSlicingParams double resolution { 0 }; // nozzle diameter (needed for z_dithering optimization) float nozzle_diameter{0}; + bool z_dither{false}; // indicates if z-dithering is needed }; // All the following slicing functions shall produce consistent results with the same mesh, same transformation matrix and slicing parameters. -// Namely, slice_mesh_slabs() shall produce consistent results with slice_mesh() and slice_mesh_ex() in the sense, that projections made by +// Namely, slice_mesh_slabs() shall produce consistent results with slice_mesh() and slice_mesh_ex() in the sense, that projections made by // slice_mesh_slabs() shall fall onto slicing planes produced by slice_mesh(). // // If a slicing plane slices a horizontal face of a mesh exactly, // an upward facing horizontal face is is considered on slicing plane, // while a downward facing horizontal face is considered not on slicing plane. -// +// // slice_mesh_slabs() thus projects an upward facing horizontal slice to the slicing plane, // while slice_mesh_slabs() projects a downward facing horizontal slice to the slicing plane above if it exists. @@ -97,7 +98,7 @@ inline std::vector slice_mesh_ex( } // Slice a triangle set with a set of Z slabs (thick layers). -// The effect is similar to producing the usual top / bottom layers from a sliced mesh by +// The effect is similar to producing the usual top / bottom layers from a sliced mesh by // subtracting layer[i] from layer[i - 1] for the top surfaces resp. // subtracting layer[i] from layer[i + 1] for the bottom surfaces, // with the exception that the triangle set this function processes may not cover the whole top resp. bottom surface. diff --git a/src/libslic3r/ZDither.cpp b/src/libslic3r/ZDither.cpp index c48cd573484..2b930e8608e 100644 --- a/src/libslic3r/ZDither.cpp +++ b/src/libslic3r/ZDither.cpp @@ -35,6 +35,7 @@ void midslice_zs(const indexed_triangle_set &mesh, float layerHeightMax = 0; for (auto i = 0; i < n_zs - 1; i++) { + // This may be a bit inaccurate if layer heights vary, but will still be an improvement. candidate_zs[i] = (zs[i + 1] + zs[i]) / 2; layerHeight[i] = zs[i + 1] - zs[i]; layerHeightMax = std::max(layerHeight[i], layerHeightMax); @@ -45,7 +46,7 @@ void midslice_zs(const indexed_triangle_set &mesh, upwrd_mididx->assign(n_zs, -1); dnwrd_mididx->assign(n_zs, -1); - float nDia = 1.0; // Cooficient to avoid cutting layers with too vertical triangles (see below) + float nDia = 1.0; // Cooficient to avoid cutting expolys with too vertical triangles (see below) // With nozzle_diameter = 0.4 and layer_height = 0.25 // nDia = 1.5 results in slopes below 22.5 degrees; nDia = 1 results in slopes under 32 degrees // nDia = 0.7 - 42 degrees; nDia = 0.5 51.4 degrees @@ -66,19 +67,19 @@ void midslice_zs(const indexed_triangle_set &mesh, int start = std::lower_bound(candidate_zs.begin(), candidate_zs.end(), z_min - eps) - candidate_zs.begin(); - for (auto i = start; i < candidate_zs.size() && candidate_zs[i] < z_max + eps; i++) { + for (auto cc = start; cc < candidate_zs.size() && candidate_zs[cc] < z_max + eps; cc++) { // Ignore facets that are too vertical to fit nDia nozzles at layer height - if (nozzle_diameter * sqrt(1 - norm[2] * norm[2]) * nDia < fabs(norm[2]) * layerHeight[i]) { + if (nozzle_diameter * sqrt(1 - norm[2] * norm[2]) * nDia < fabs(norm[2]) * layerHeight[cc]) { if (norm[2] > 0) - upwrd_mididx->at(i) = i; + upwrd_mididx->at(cc) = cc; else - dnwrd_mididx->at(i) = i; + dnwrd_mididx->at(cc) = cc; } } } if (std::all_of(upwrd_mididx->begin(), upwrd_mididx->end(), [](int idx) { return idx == -1; }) && - std::all_of(dnwrd_mididx->begin(), dnwrd_mididx->end(), [](int idx) { return idx = -1; })) + std::all_of(dnwrd_mididx->begin(), dnwrd_mididx->end(), [](int idx) { return idx == -1; })) return; // Retrn mid_zs which will contribute to z-dithering @@ -93,15 +94,17 @@ void midslice_zs(const indexed_triangle_set &mesh, return; } -std::vector apply_z_dither(const std::vector &layers, - const std::vector &layers_mid, - const std::vector & upwrd_mididx, - const std::vector & dnwrd_mididx) +std::vector apply_z_dither(std::vector &expolys, + std::vector &expolys_mid, + const std::vector & upwrd_mididx, + const std::vector & dnwrd_mididx, + std::vector * sublayers) { - std::vector layers5; - layers5.resize(layers.size()); + sublayers->clear(); + sublayers->resize(expolys.size()); + std::vector out(expolys.size()); - for (auto ll = 0; ll < layers.size(); ll++) { + for (auto ll = 0; ll < expolys.size(); ll++) { // idx0 - bottom of layer, idx1 - top of layer int upwrd_idx0 = ll > 0 ? upwrd_mididx[ll - 1] : -1; int dnwrd_idx0 = ll > 0 ? dnwrd_mididx[ll - 1] : -1; @@ -111,56 +114,73 @@ std::vector apply_z_dither(const std::vector &layers, auto useMidCut = [](int idx) { return idx != -1; }; if (!useMidCut(upwrd_idx0) && !useMidCut(dnwrd_idx0) && !useMidCut(upwrd_idx1) && !useMidCut(dnwrd_idx1)) { - layers5[ll] = ExPolygons5(ExPolygons(layers[ll])); + out[ll] = std::move(expolys[ll]); + continue; } else { - layers5[ll] = ExPolygons5(); + ExPolygons bottom, middleUp, middleDn, top; + if (useMidCut(upwrd_idx0) || useMidCut(upwrd_idx1)) { - layers5[ll].ditherUp[0] = std::move( - diff_ex(useMidCut(upwrd_idx0) ? layers_mid[upwrd_idx0] : layers[ll], - useMidCut(upwrd_idx1) ? layers_mid[upwrd_idx1] : layers[ll])); + bottom = std::move( + diff_ex(useMidCut(upwrd_idx0) ? expolys_mid[upwrd_idx0] : expolys[ll], + useMidCut(upwrd_idx1) ? expolys_mid[upwrd_idx1] : expolys[ll])); } if (useMidCut(upwrd_idx1)) { - layers5[ll].ditherUp[1] = std::move(diff_ex(layers[ll], layers_mid[upwrd_idx1])); + middleUp = std::move(diff_ex(expolys[ll], expolys_mid[upwrd_idx1])); } if (useMidCut(dnwrd_idx0) || useMidCut(dnwrd_idx1)) { - layers5[ll].ditherDn[0] = std::move( - diff_ex(useMidCut(dnwrd_idx1) ? layers_mid[dnwrd_idx1] : layers[ll], - useMidCut(dnwrd_idx0) ? layers_mid[dnwrd_idx0] : layers[ll])); + top = std::move( + diff_ex(useMidCut(dnwrd_idx1) ? expolys_mid[dnwrd_idx1] : expolys[ll], + useMidCut(dnwrd_idx0) ? expolys_mid[dnwrd_idx0] : expolys[ll])); } if (useMidCut(dnwrd_idx0)) { - layers5[ll].ditherDn[1] = std::move(diff_ex(layers[ll], layers_mid[dnwrd_idx0])); + middleDn = std::move(diff_ex(expolys[ll], expolys_mid[dnwrd_idx0])); } + ExPolygons whole; if (useMidCut(upwrd_idx1) && useMidCut(dnwrd_idx0)) - layers5[ll].whole = std::move(intersection_ex(layers_mid[dnwrd_idx0], layers_mid[upwrd_idx1])); + whole = std::move(intersection_ex(expolys_mid[dnwrd_idx0], expolys_mid[upwrd_idx1])); else if (useMidCut(upwrd_idx1)) - layers5[ll].whole = std::move(intersection_ex(layers[ll], layers_mid[upwrd_idx1])); + whole = std::move(intersection_ex(expolys[ll], expolys_mid[upwrd_idx1])); else if (useMidCut(dnwrd_idx0)) - layers5[ll].whole = std::move(intersection_ex(layers[ll], layers_mid[dnwrd_idx0])); - else - layers5[ll].whole = ExPolygons(layers[ll]); + whole = std::move(intersection_ex(expolys[ll], expolys_mid[dnwrd_idx0])); + else { + out[ll] = std::move(expolys[ll]); + continue; + } + out[ll] = std::move(whole); + if (bottom.empty() != middleUp.empty() || middleDn.empty() != top.empty()) { + BOOST_LOG_TRIVIAL(error) + << "z-dithering: internal error"; + } else { + sublayers->at(ll).bottom_ = std::move(bottom); + sublayers->at(ll).halfUp_ = std::move(middleUp); + sublayers->at(ll).halfDn_ = std::move(middleDn); + sublayers->at(ll).top_ = std::move(top); + } } } - return layers5; + return out; } -std::vector z_dither(const indexed_triangle_set & mesh, - const std::vector & zs, - const MeshSlicingParamsEx & params, - const std::vector &layers, - const std::function & throw_on_cancel_callback) + + +std::vector z_dither(const indexed_triangle_set &mesh, + const std::vector &zs, + const MeshSlicingParamsEx ¶ms, + std::vector & expolys, + std::vector * sublayers, + const std::function &throw_on_cancel_callback) { std::vector mid_zs; std::vector upwrd_mididx; std::vector dnwrd_mididx; midslice_zs(mesh, zs, params.trafo, params.nozzle_diameter, &mid_zs, &upwrd_mididx, &dnwrd_mididx); if (!mid_zs.empty()) { - std::vector layers_mid = slice_mesh_ex(mesh, mid_zs, params, throw_on_cancel_callback); - return apply_z_dither(layers, layers_mid, upwrd_mididx, dnwrd_mididx); + std::vector expolys_mid = slice_mesh_ex(mesh, mid_zs, params, throw_on_cancel_callback); + return apply_z_dither(expolys, expolys_mid, upwrd_mididx, dnwrd_mididx, sublayers); } else { - std::vector layers5; - for (auto i = 0; i < layers.size(); i++) { layers5.push_back(ExPolygons5((layers[i]))); }; - return layers5; + *sublayers = std::vector(expolys.size()); + return expolys; } } diff --git a/src/libslic3r/ZDither.hpp b/src/libslic3r/ZDither.hpp index 77c6cea1027..f547bd29ad6 100644 --- a/src/libslic3r/ZDither.hpp +++ b/src/libslic3r/ZDither.hpp @@ -6,6 +6,18 @@ namespace Slic3r { + struct SubLayers +{ + ExPolygons bottom_; // polygons filling the botthom 0.25% of layer thickness + ExPolygons halfUp_; // polygons filling only half a layer leaving 0.25% at top and botton not filled, located above bottom_ + ExPolygons halfDn_; // similar to halfUp_ but located under top_ + ExPolygons top_; // polygons filling the top 0.25% of layer thickness + SubLayers() = default; + SubLayers(const SubLayers &other) = default; + SubLayers(SubLayers &&other) = default; + bool empty() const { return bottom_.empty() && halfUp_.empty() && halfDn_.empty() && top_.empty(); }; +}; + void midslice_zs(const indexed_triangle_set &mesh, const std::vector & zs, const Transform3d & trafo, @@ -14,16 +26,18 @@ void midslice_zs(const indexed_triangle_set &mesh, std::vector * upwrd_mididx, std::vector * dnwrd_mididx); -std::vector apply_z_dither(const std::vector &layers, - const std::vector &mid_layers, - const std::vector &do_low, - const std::vector &do_high); +std::vector apply_z_dither(std::vector &layers, + std::vector &mid_layers, + const std::vector &do_low, + const std::vector &do_high, + std::vector * sublayers); -std::vector z_dither(const indexed_triangle_set & mesh, - const std::vector & zs, - const MeshSlicingParamsEx & params, - const std::vector &layers, - const std::function & throw_on_cancel_callback); +std::vector z_dither(const indexed_triangle_set &mesh, + const std::vector & zs, + const MeshSlicingParamsEx ¶ms, + std::vector & layers, + std::vector * sublayers, + const std::function &throw_on_cancel_callback); } // namespace Slic3r #endif diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 96151d8c7ff..50cfcd7e469 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1450,6 +1450,7 @@ void TabPrint::build() optgroup->append_single_option_line("seam_position", category_path + "seam-position"); optgroup->append_single_option_line("external_perimeters_first", category_path + "external-perimeters-first"); optgroup->append_single_option_line("gap_fill_enabled", category_path + "fill-gaps"); + optgroup->append_single_option_line("z_dither", category_path + "z-dither"); optgroup = page->new_optgroup(L("Fuzzy skin (experimental)")); category_path = "fuzzy-skin_246186/#"; From cef05e41cb1cb679c0d7b672063b382c6e2e623f Mon Sep 17 00:00:00 2001 From: Leonid Raiz Date: Fri, 14 Apr 2023 14:16:25 -0400 Subject: [PATCH 3/9] Corrected merge errors --- src/libslic3r/ExPolygon.hpp | 35 ----------- src/libslic3r/GCode.cpp | 3 +- src/libslic3r/Layer.hpp | 2 +- src/libslic3r/Preset.cpp | 2 +- src/libslic3r/Print.cpp | 4 +- src/libslic3r/PrintObject.cpp | 99 +++++++++++++----------------- src/libslic3r/PrintObjectSlice.cpp | 42 +++++-------- 7 files changed, 63 insertions(+), 124 deletions(-) diff --git a/src/libslic3r/ExPolygon.hpp b/src/libslic3r/ExPolygon.hpp index cbfe7e628c7..83b264803c7 100644 --- a/src/libslic3r/ExPolygon.hpp +++ b/src/libslic3r/ExPolygon.hpp @@ -12,41 +12,6 @@ namespace Slic3r { class ExPolygon; using ExPolygons = std::vector; -struct ExPolygons5 -{ - ExPolygons5() = default; - ExPolygons5(const ExPolygons5 &other) = default; - ExPolygons5(ExPolygons5 &&other) = default; - explicit ExPolygons5(ExPolygons main) : whole(main) {}; - ExPolygons5 &operator=(const ExPolygons5 &other) = default; - ExPolygons5 &operator=(ExPolygons5 &&other) = default; - // z-dithering reduces stair-step appearance of layers of low-slope surfaces by splitting - // each layer into up to 5 regions of different thinkness deposited from different heights. - ExPolygons whole; // Most of the layer made without z-dithering potentialy reduced near - // low sloped surfaces - ExPolygons ditherUp[2]; // Upto two regions near upward facing surface. May be absent. - // ditherUp[0] - deposited on top of previous layer, 0.25 layer height - // ditherUp[1] - deposited on top of ditherUp[0], 0.5 layer height - ExPolygons ditherDn[2]; // Upto two regions near downward facing surface. May be absent. - // ditherDn[0] - deposited from the height of next layer bottom, 0.25 layer height - // ditherDn[1] - deposited from the height of ditherDn[0] bottom, 0.5 layer height - // In actual priting ditherDn[1] should be printed before ditherDn[0] - bool empty() const - { - return whole.empty() && ditherUp[0].empty() && ditherUp[1].empty() && - ditherDn[0].empty() && ditherDn[1].empty(); - } - void clear() - { - whole.clear(); - ditherUp[0].clear(); - ditherUp[1].clear(); - ditherDn[0].clear(); - ditherDn[1].clear(); - } -}; - - class ExPolygon { public: diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index fcb6962c9a7..e1d12c8d363 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -690,10 +690,9 @@ std::vector> GCode::collect_laye // Assign an average print_z to the set of layers with nearly equal print_z. merged.first = 0.5 * (ordering[i].print_z + ordering[j - 1].print_z); // z-dithering may result in 2 layers from the same object in merged - merged.second.assign(print.objects().size(), ObjectLayerToPrint()); for (; i < j; ++ i) { const OrderingItem& oi = ordering[i]; - merged.second.emplace_back(std::move(per_object[oi.object_idx][oi.layer_idx]); + merged.second.emplace_back(std::move(per_object[oi.object_idx][oi.layer_idx])); } layers_to_print.emplace_back(std::move(merged)); } diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index 21dc400808c..981e4355aa4 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -392,7 +392,7 @@ class Layer friend std::vector new_layers(PrintObject*, const std::vector&); friend std::string fix_slicing_errors(LayerPtrs&, const std::function&); // Create dithering layer. bottom & top >= 0 and <= 1 - friend Layer *make_dither_layer(Layer *refLayer, double bottom, double top); + friend Layer *make_dithered_layer(Layer *refLayer, double bottom, double top); Layer(size_t id, PrintObject *object, coordf_t height, coordf_t print_z, coordf_t slice_z) : upper_layer(nullptr), lower_layer(nullptr), diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index f2b87df3da9..4311d732160 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -454,7 +454,7 @@ static std::vector s_Preset_print_options { "elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "gcode_resolution", "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_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", + "perimeter_generator", "z_dither", "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle", "wall_distribution_count", "min_feature_size", "min_bead_width" }; diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 4370cc66ff3..279d4c9db4b 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -540,9 +540,9 @@ std::string Print::validate(std::string* warning) const if (m_default_object_config.z_dither) { if (m_default_region_config.infill_every_layers > 1) - return L("Z-dither slicing option is not compatible with option to combine infills of multiple layers."); + return _u8L("Z-dither slicing option is not compatible with option to combine infills of multiple layers."); if (extruders.size() > 1) // Is there a better way to check for a possibility of multimaterial printing? - return L("Z-dither slicing option is currently not supported for printers with multiple extruders."); + return _u8L("Z-dither slicing option is currently not supported for printers with multiple extruders."); } if (this->has_wipe_tower() && ! m_objects.empty()) { diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 04a169d513b..a7f460211a7 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -256,9 +256,10 @@ void PrintObject::prepare_infill() if (! this->set_started(posPrepareInfill)) return; - for (size_t idx = 0; idx < m_layers.size(); ++idx) { - assert(m_layers[idx]->id() == idx + m_config.raft_layers); // subsequent calls will depend on such numbering - } + for (size_t idx = 0; idx < m_layers.size(); ++idx) { + assert(m_layers[idx]->id() == idx + m_config.raft_layers); // subsequent calls will depend on such numbering + } + m_print->set_status(30, _u8L("Preparing infill")); if (m_typed_slices) { @@ -405,7 +406,7 @@ void PrintObject::infill() this->prepare_infill(); if (this->set_started(posInfill)) { - // TRN Status for the Print calculation + // TRN Status for the Print calculation m_print->set_status(45, _u8L("Making infill")); const auto& adaptive_fill_octree = this->m_adaptive_fill_octrees.first; const auto& support_fill_octree = this->m_adaptive_fill_octrees.second; @@ -478,7 +479,7 @@ void PrintObject::generate_support_material() if (this->set_started(posSupportMaterial)) { this->clear_support_layers(); if ((this->has_support() && m_layers.size() > 1) || (this->has_raft() && ! m_layers.empty())) { - m_print->set_status(70, _u8L("Generating support material")); + m_print->set_status(70, _u8L("Generating support material")); this->_generate_support_material(); m_print->throw_if_canceled(); } else { @@ -1134,7 +1135,7 @@ void PrintObject::process_external_surfaces() tbb::blocked_range(0, m_layers.size() - 1), [this, &surfaces_covered, &layer_expansions_and_voids, unsupported_width](const tbb::blocked_range& range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) - if (layer_expansions_and_voids[layer_idx + 1]) { + if (layer_expansions_and_voids[next_layer_index(layer_idx, false)]) { // Layer above is partially filled with solid infill (top, bottom, bridging...), // while some sparse inill regions are empty (0% infill). m_print->throw_if_canceled(); @@ -1403,7 +1404,7 @@ void PrintObject::discover_vertical_shells() shell = std::move(shells2); else if (! shells2.empty()) { polygons_append(shell, shells2); - // Running the union_ using the Clipper library piece by piece is cheaper + // Running the union_ using the Clipper library piece by piece is cheaper // than running the union_ all at once. shell = union_(shell); } @@ -1412,38 +1413,38 @@ void PrintObject::discover_vertical_shells() if (int n_top_layers = region_config.top_solid_layers.value; n_top_layers > 0) { // Gather top regions projected to this layer. coordf_t print_z = layer->print_z; - for (int i = int(idx_layer) + 1; - int itop = int(idx_layer) + n_top_layers; + int i = next_layer_index(idx_layer, false); + int count = 1; for (; i < int(cache_top_botom_regions.size()) && - (i < int(idx_layer) + n_top_layers || - (i < itop || m_layers[i]->print_z - print_z < region_config.top_solid_min_thickness - EPSILON); + (count < n_top_layers || + m_layers[i]->print_z - print_z < region_config.top_solid_min_thickness - EPSILON); i = next_layer_index(i, false), ++count) { const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[i]; combine_holes(cache.holes); combine_shells(cache.top_surfaces); - // Running the union_ using the Clipper library piece by piece is cheaper + // Running the union_ using the Clipper library piece by piece is cheaper } if (one_more_layer_below_top_bottom_surfaces) if (i < int(cache_top_botom_regions.size()) && - (i <= itop || m_layers[i]->bottom_z() - print_z < region_config.top_solid_min_thickness - EPSILON)) + (count <= n_top_layers || m_layers[i]->bottom_z() - print_z < region_config.top_solid_min_thickness - EPSILON)) combine_holes(cache_top_botom_regions[i].holes); } if (int n_bottom_layers = region_config.bottom_solid_layers.value; n_bottom_layers > 0) { // Gather bottom regions projected to this layer. coordf_t bottom_z = layer->bottom_z(); - for (int i = int(idx_layer) - 1; - (i > int(idx_layer) - n_bottom_layers || - for (; i >= 0 && - (i > ibottom || bottom_z - m_layers[i]->bottom_z() < region_config.bottom_solid_min_thickness - EPSILON); + int i = next_layer_index(idx_layer, true); + int count = 1; + for (; i >= 0 && (count < n_bottom_layers || + bottom_z - m_layers[i]->bottom_z() < region_config.bottom_solid_min_thickness - EPSILON); i = next_layer_index(i, true), ++count) { const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[i]; combine_holes(cache.holes); combine_shells(cache.bottom_surfaces); - // Running the union_ using the Clipper library piece by piece is cheaper + // Running the union_ using the Clipper library piece by piece is cheaper } if (one_more_layer_below_top_bottom_surfaces) if (i >= 0 && - (i > ibottom || bottom_z - m_layers[i]->print_z < region_config.bottom_solid_min_thickness - EPSILON)) + (count < n_bottom_layers || bottom_z - m_layers[i]->print_z < region_config.bottom_solid_min_thickness - EPSILON)) combine_holes(cache_top_botom_regions[i].holes); } #ifdef SLIC3R_DEBUG_SLICE_PROCESSING @@ -1451,12 +1452,12 @@ void PrintObject::discover_vertical_shells() Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-before-union-%d.svg", debug_idx), get_extents(shell)); svg.draw(shell); svg.draw_outline(shell, "black", scale_(0.05)); - svg.Close(); + svg.Close(); } #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ #if 0 // shell = union_(shell, true); - shell = union_(shell, false); + shell = union_(shell, false); #endif #ifdef SLIC3R_DEBUG_SLICE_PROCESSING shell_ex = union_safety_offset_ex(shell); @@ -1519,7 +1520,7 @@ void PrintObject::discover_vertical_shells() #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ ExPolygons regularized_shell; { - // Intentionally inflate a bit more than how much the region has been shrunk, + // Intentionally inflate a bit more than how much the region has been shrunk, // Such narrow regions are difficult to fill in with a gap fill algorithm (or Arachne), however they are most likely // not needed for print stability / quality. const float narrow_ensure_vertical_wall_thickness_region_radius = 0.5f * 0.65f * min_perimeter_infill_spacing; @@ -1536,7 +1537,7 @@ void PrintObject::discover_vertical_shells() // Finally expand the infill a bit to remove tiny gaps between solid infill and the other regions. narrow_sparse_infill_region_radius - tiny_overlap_radius, ClipperLib::jtSquare); - // grow the collapsing parts and add the extra area to the neighbor layer + // grow the collapsing parts and add the extra area to the neighbor layer regularized_shell.erase(std::remove_if(regularized_shell.begin(), regularized_shell.end(), [&min_perimeter_infill_spacing](const ExPolygon &p) { return p.area() < min_perimeter_infill_spacing * scaled(8.0); @@ -1602,7 +1603,7 @@ template void debug_draw(std::string name, const T& a, const T& b, c bbox.merge(get_extents(c)); bbox.merge(get_extents(d)); bbox.offset(scale_(1.)); - ::Slic3r::SVG svg(debug_out_path(name.c_str()).c_str(), bbox); + ::Slic3r::SVG svg(debug_out_path(name.c_str()).c_str(), bbox); svg.draw(a, colors[0], scale_(0.3)); svg.draw(b, colors[1], scale_(0.23)); svg.draw(c, colors[2], scale_(0.16)); @@ -1683,8 +1684,8 @@ void PrintObject::bridge_over_infill() SurfacesPtr region_internal_solids = region->fill_surfaces().filter_by_type(stInternalSolid); for (const Surface *s : region_internal_solids) { Polygons unsupported = intersection(to_polygons(s->expolygon), unsupported_area); - // The following flag marks those surfaces, which overlap with unuspported area, but at least part of them is supported. - // These regions can be filtered by area, because they for sure are touching solids on lower layers, and it does not make sense to bridge their tiny overhangs + // The following flag marks those surfaces, which overlap with unuspported area, but at least part of them is supported. + // These regions can be filtered by area, because they for sure are touching solids on lower layers, and it does not make sense to bridge their tiny overhangs bool partially_supported = area(unsupported) < area(to_polygons(s->expolygon)) - EPSILON; if (!unsupported.empty() && (!partially_supported || area(unsupported) > 3 * 3 * spacing * spacing)) { Polygons worth_bridging = intersection(to_polygons(s->expolygon), expand(unsupported, 4 * spacing)); @@ -1705,7 +1706,7 @@ void PrintObject::bridge_over_infill() #endif #ifdef DEBUG_BRIDGE_OVER_INFILL debug_draw(std::to_string(lidx) + "_candidate_processing_" + std::to_string(area(unsupported)), - to_lines(unsupported), to_lines(intersection(to_polygons(s->expolygon), expand(unsupported, 5 * spacing))), + to_lines(unsupported), to_lines(intersection(to_polygons(s->expolygon), expand(unsupported, 5 * spacing))), to_lines(diff(to_polygons(s->expolygon), expand(worth_bridging, spacing))), to_lines(unsupported_area)); #endif @@ -1731,10 +1732,10 @@ void PrintObject::bridge_over_infill() } this->m_adaptive_fill_octrees = this->prepare_adaptive_infill_data(surfaces_w_bottom_z); - for (int i = int(layer_it - m_layers.begin()) - 1; i >= 0; --i) { + this->m_lightning_generator = this->prepare_lightning_infill_data(); std::vector layers_to_generate_infill; - const Layer* lower_layer = m_layers[i]; + for (const auto &pair : surfaces_by_layer) { assert(pair.first > 0); infill_lines[pair.first - 1] = {}; layers_to_generate_infill.push_back(pair.first - 1); @@ -1747,7 +1748,7 @@ void PrintObject::bridge_over_infill() size_t lidx = layers_to_generate_infill[job_idx]; infill_lines.at( lidx) = po->get_layer(lidx)->generate_sparse_infill_polylines_for_anchoring(po->m_adaptive_fill_octrees.first.get(), - + po->m_adaptive_fill_octrees.second.get(), po->m_lightning_generator.get()); } @@ -1771,7 +1772,7 @@ void PrintObject::bridge_over_infill() } // prepare inflated filter for each candidate on each layer. layers will be put into single thread cluster if they are close to each other (z-axis-wise) - + // and if the inflated AABB polygons overlap somewhere tbb::parallel_for(tbb::blocked_range(0, layers_with_candidates.size()), [&layers_with_candidates, &surfaces_by_layer, &layer_area_covered_by_candidates]( @@ -1815,12 +1816,12 @@ void PrintObject::bridge_over_infill() } // LAMBDA to gather areas with sparse infill deep enough that we can fit thick bridges there. - auto gather_areas_w_depth = [target_flow_height_factor](const PrintObject *po, int lidx, float target_flow_height) { + auto gather_areas_w_depth = [target_flow_height_factor, this](const PrintObject *po, int lidx, float target_flow_height) { // Gather layers sparse infill areas, to depth defined by used bridge flow ExPolygons layers_sparse_infill{}; ExPolygons not_sparse_infill{}; double bottom_z = po->get_layer(lidx)->print_z - target_flow_height * target_flow_height_factor - EPSILON; - for (int i = int(lidx) - 1; i >= 0; --i) { + for (int i = next_layer_index(lidx, true); i >= 0; i= next_layer_index(i, true)) { // Stop iterating if layer is lower than bottom_z. const Layer *layer = po->get_layer(i); if (layer->print_z < bottom_z) @@ -2502,7 +2503,7 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c // Polygons upper_internal; // for (int layer_id = int(m_layers.size()) - 1; layer_id > 0; -- layer_id) { // Layer *layer = m_layers[layer_id]; - Layer *lower_layer = m_layers[layer_id - 1]; +// Layer* lower_layer = layer->lower_layer; // // Detect things that we need to support. // // Cummulative fill surfaces. // Polygons fill_surfaces; @@ -2545,7 +2546,7 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c // // Regularize the overhang regions, so that the infill areas will not become excessively jagged. // smooth_outward( // closing(upper_internal, closing_radius, ClipperLib::jtSquare, 0.), -// scaled(0.1)), +// scaled(0.1)), // lower_layer_internal_surfaces); // // Apply new internal infill to regions. // for (LayerRegion *layerm : lower_layer->m_regions) { @@ -2589,26 +2590,6 @@ void PrintObject::discover_horizontal_shells() surface.surface_type = type; } // The rest has already been performed by discover_vertical_shells(). - - // not work in some situations, as there won't be any grown region in the perimeter - - (type == stTop) ? - for (int n = next_layer_index(i, lower), num_done = 1; - lower ? - (n >= 0 && (int(i) - n < num_solid_layers || - (n < int(m_layers.size()) && (n - int(i) < num_solid_layers || - (type == stTop) ? -- n : ++ n) -// Slic3r::debugf " looking for neighbors on layer %d...\n", $n; - - // narrow bottom surfaces): reassigning $solid will consider the 'shadow' of the - // shell if the object would otherwise show a hole (gap between perimeters of - // the two layers), and internal solid shells are a subset of the shells found - - // grow the collapsing parts and add the extra area to the neighbor layer - // as well as to our original surfaces so that we support this - // remove such anchors. (This may happen when a bridge is being - // is grown, and that little space is an internal solid shell so - } // for each layer } // for each region @@ -2628,6 +2609,9 @@ void PrintObject::discover_horizontal_shells() // fill_surfaces but we only turn them into VOID surfaces, thus preserving the boundaries. void PrintObject::combine_infill() { + // z-dithering is not currently compatible with combining infills + if (m_config.z_dither) return; + // Work on each region separately. for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) { const PrintRegion ®ion = this->printing_region(region_id); @@ -2654,7 +2638,7 @@ void PrintObject::combine_infill() // would exceed max layer height or max combined layer count. if (current_height + layer->height >= nozzle_diameter + EPSILON || num_layers >= every) { // Append combination to lower layer. - combine[layer_idx - 1] = num_layers; + combine[next_layer_index(layer_idx, true)] = num_layers; current_height = 0.; num_layers = 0; } @@ -2747,6 +2731,7 @@ void PrintObject::_generate_support_material() static void project_triangles_to_slabs(SpanOfConstPtrs layers, const indexed_triangle_set &custom_facets, const Transform3f &tr, bool seam, std::vector &out) { + // this function relies on absence of dithered layers in "layers" and therefore used only by slicing of supports if (custom_facets.indices.empty()) return; @@ -2946,7 +2931,7 @@ void PrintObject::project_and_append_custom_facets( seam, out); else { std::vector projected; - ConstLayerPtrsAdaptor &layers = this->layers(); + SpanOfConstPtrs layers = this->layers(); // Support blockers or enforcers. Project downward facing painted areas upwards to their respective slicing plane. slice_mesh_slabs(custom_facets, zs_from_layers(layers, true), this->trafo_centered() * mv->get_matrix(), nullptr, &projected, [](){}); // Merge these projections with the output, layer by layer. diff --git a/src/libslic3r/PrintObjectSlice.cpp b/src/libslic3r/PrintObjectSlice.cpp index 108d4db69b9..371afcb309c 100644 --- a/src/libslic3r/PrintObjectSlice.cpp +++ b/src/libslic3r/PrintObjectSlice.cpp @@ -260,8 +260,6 @@ static std::vector::const_iterator layer_ } static std::vector> slices_to_regions( - const PrintConfig &print_config, - const PrintObject &print_object, ModelVolumePtrs model_volumes, const PrintObjectRegions &print_object_regions, const std::vector &zs, @@ -569,7 +567,11 @@ void PrintObject::slice() [this](const tbb::blocked_range &range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { m_print->throw_if_canceled(); - Layer::build_up_down_graph(*m_layers[layer_idx - 1], *m_layers[layer_idx]); + Layer &above = *m_layers[layer_idx]; + Layer &below = *m_layers[next_layer_index(layer_idx, true)]; + if (above.dithered == below.dithered) { + Layer::build_up_down_graph(below, above); + } } }); if (m_layers.empty()) @@ -703,7 +705,7 @@ void apply_mm_segmentation(PrintObject &print_object, ThrowOnCancel throw_on_can }); } -Layer *make_dither_layer(Layer *refLayer, double bottom, double top) +Layer *make_dithered_layer(Layer *refLayer, double bottom, double top) { coordf_t height = refLayer->height; coordf_t hi = refLayer->slice_z + height / 2; @@ -751,7 +753,7 @@ LayerPtrs add_dithering_layers(const LayerPtrs & layers, [&ll](VolumeSublayers &v_sub) { return !v_sub.sublayers[ll].bottom_.empty(); })) { - newLayer[0] = make_dither_layer(original[ll], 0., 0.25); + newLayer[0] = make_dithered_layer(original[ll], 0., 0.25); newLayer[0]->lower_layer = original[ll]->lower_layer; merge_sublayers_to_slices(volume_slices, volume_sublayers, 0, ll, resulting.size()); resulting.push_back(newLayer[0]); @@ -760,7 +762,7 @@ LayerPtrs add_dithering_layers(const LayerPtrs & layers, [&ll](VolumeSublayers &v_sub) { return !v_sub.sublayers[ll].halfUp_.empty(); })) { - newLayer[1] = make_dither_layer(original[ll], 0.25, 0.75); + newLayer[1] = make_dithered_layer(original[ll], 0.25, 0.75); newLayer[1]->lower_layer = newLayer[0]; // must be != nullptr newLayer[0]->upper_layer = newLayer[1]; merge_sublayers_to_slices(volume_slices, volume_sublayers, 1, ll, resulting.size()); @@ -770,7 +772,7 @@ LayerPtrs add_dithering_layers(const LayerPtrs & layers, [&ll](VolumeSublayers &v_sub) { return !v_sub.sublayers[ll].halfDn_.empty(); })) { - newLayer[2] = make_dither_layer(original[ll], 0.25, 0.75); + newLayer[2] = make_dithered_layer(original[ll], 0.25, 0.75); merge_sublayers_to_slices(volume_slices, volume_sublayers, 2, ll, resulting.size()); resulting.push_back(newLayer[2]); } @@ -778,7 +780,7 @@ LayerPtrs add_dithering_layers(const LayerPtrs & layers, [&ll](VolumeSublayers &v_sub) { return !v_sub.sublayers[ll].top_.empty(); })) { - newLayer[3] = make_dither_layer(original[ll], 0.75, 1.); + newLayer[3] = make_dithered_layer(original[ll], 0.75, 1.); newLayer[3]->upper_layer = original[ll]->upper_layer; newLayer[3]->lower_layer = newLayer[2]; // must be != nullptr newLayer[2]->upper_layer = newLayer[3]; @@ -819,15 +821,8 @@ void PrintObject::slice_volumes() std::vector slice_zs = zs_from_layers(m_layers, true); std::vector volume_sublayers; - std::vector volume_slices = slice_volumes_inner( - print->config(), - this->config(), - this->trafo_centered(), - this->model_object()->volumes, - m_shared_regions->layer_ranges, - slice_zs, - &volume_sublayers, - throw_on_cancel_callback); + std::vector volume_slices = slice_volumes_inner(print->config(), this->config(), this->trafo_centered(), + this->model_object()->volumes, m_shared_regions->layer_ranges, slice_zs, &volume_sublayers, throw_on_cancel_callback); if (this->config().z_dither) { m_layers = add_dithering_layers(m_layers, volume_slices, volume_sublayers); @@ -841,14 +836,8 @@ void PrintObject::slice_volumes() slice_zs = zs_from_layers(m_layers, false); } - std::vector> region_slices = slices_to_regions( - print->config(), - *this, - this->model_object()->volumes, - *m_shared_regions, - slice_zs, - std::move(volume_slices), - throw_on_cancel_callback); + std::vector> region_slices = slices_to_regions(this->model_object()->volumes, *m_shared_regions, slice_zs, + std::move(volume_slices), throw_on_cancel_callback); for (size_t region_id = 0; region_id < region_slices.size(); ++ region_id) { std::vector &by_layer = region_slices[region_id]; @@ -978,7 +967,8 @@ std::vector PrintObject::slice_support_volumes(const ModelVolumeType m std::vector slices; if (it_volume != it_volume_end) { // Found at least a single support volume of model_volume_type. - std::vector zs = zs_from_layers(this->layers(), true); // exclude ditthered layers + // Exclude z of ditthered layers. Do we need to improve this? + std::vector zs = zs_from_layers(this->layers(), true); size_t num_layers = this->layers().size(); std::vector merge_layers; bool merge = false; From 639a3e4847f653371ddabf6bd565c082b5da14c8 Mon Sep 17 00:00:00 2001 From: Leonid Raiz Date: Mon, 17 Apr 2023 14:52:55 -0400 Subject: [PATCH 4/9] Debug merged code --- src/libslic3r/PrintObject.cpp | 7 ++-- src/libslic3r/PrintObjectSlice.cpp | 26 ++++++++------ src/libslic3r/SupportMaterial.cpp | 48 ++++++++++++------------- src/libslic3r/SupportSpotsGenerator.cpp | 17 ++++++--- src/libslic3r/ZDither.cpp | 7 ++-- 5 files changed, 59 insertions(+), 46 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index a7f460211a7..771f9cf666b 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -283,6 +283,7 @@ void PrintObject::prepare_infill() // Decide what surfaces are to be filled. // Here the stTop / stBottomBridge / stBottom infill is turned to just stInternal if zero top / bottom infill layers are configured. // Also tiny stInternal surfaces are turned to stInternalSolid. + BOOST_LOG_TRIVIAL(info) << "Preparing fill surfaces..." << log_memory_info(); for (auto *layer : m_layers) { for (auto *region : layer->m_regions) { @@ -1737,8 +1738,8 @@ void PrintObject::bridge_over_infill() std::vector layers_to_generate_infill; for (const auto &pair : surfaces_by_layer) { assert(pair.first > 0); - infill_lines[pair.first - 1] = {}; - layers_to_generate_infill.push_back(pair.first - 1); + infill_lines[next_layer_index(pair.first, true)] = {}; + layers_to_generate_infill.push_back(next_layer_index(pair.first, true)); } tbb::parallel_for(tbb::blocked_range(0, layers_to_generate_infill.size()), [po = static_cast(this), @@ -2167,7 +2168,7 @@ void PrintObject::bridge_over_infill() total_fill_area = closing(total_fill_area, SCALED_EPSILON); expansion_area = closing(expansion_area, SCALED_EPSILON); expansion_area = intersection(expansion_area, deep_infill_area); - Polylines anchors = intersection_pl(infill_lines[lidx - 1], shrink(expansion_area, spacing)); + Polylines anchors = intersection_pl(infill_lines[po->next_layer_index(lidx,true)], shrink(expansion_area, spacing)); #ifdef DEBUG_BRIDGE_OVER_INFILL debug_draw(std::to_string(lidx) + "_" + std::to_string(cluster_idx) + "_" + std::to_string(job_idx) + "_" + "_total_area", diff --git a/src/libslic3r/PrintObjectSlice.cpp b/src/libslic3r/PrintObjectSlice.cpp index 371afcb309c..cce405aaa27 100644 --- a/src/libslic3r/PrintObjectSlice.cpp +++ b/src/libslic3r/PrintObjectSlice.cpp @@ -413,7 +413,7 @@ static std::vector> slices_to_regions( } } if (merged) - // to handle region overlaps. Indeed, one may intentionally let the regions overlap to produce crossing perimeters + // to handle region overlaps. Indeed, one may intentionally let the regions overlap to produce crossing perimeters expolygons = closing_ex(expolygons, float(scale_(EPSILON))); slices_by_region[temp_slices[i].region_id][z_idx] = std::move(expolygons); i = j; @@ -469,7 +469,7 @@ std::string fix_slicing_errors(LayerPtrs &layers, const std::function &t // Collect outer contours and holes from the valid layers above & below. Polygons outer; outer.reserve( - ((upper_surfaces == nullptr) ? 0 : upper_surfaces->size()) + + ((upper_surfaces == nullptr) ? 0 : upper_surfaces->size()) + ((lower_surfaces == nullptr) ? 0 : lower_surfaces->size())); size_t num_holes = 0; if (upper_surfaces) @@ -565,15 +565,21 @@ void PrintObject::slice() tbb::parallel_for( tbb::blocked_range(1, m_layers.size()), [this](const tbb::blocked_range &range) { - for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { - m_print->throw_if_canceled(); - Layer &above = *m_layers[layer_idx]; - Layer &below = *m_layers[next_layer_index(layer_idx, true)]; - if (above.dithered == below.dithered) { - Layer::build_up_down_graph(below, above); - } + for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { + m_print->throw_if_canceled(); + // Layer::build_up_down_graph and subsequent support stability checks + // appear to get in trouble when multiple layers point to the same + // above/below layer as happens in case of z-dithering. For now I just avoid + // mixing dithered and non-dithered layers in the same graph. + Layer *above = m_layers[layer_idx]; + Layer *below = above->lower_layer; + if (below != nullptr && above->dithered == below->dithered) { + Layer::build_up_down_graph(*below, *above); } + // Layer::build_up_down_graph(*m_layers[layer_idx - 1], *m_layers[layer_idx]); + } }); + // }); if (m_layers.empty()) throw Slic3r::SlicingError("No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry.\n"); this->set_done(posSlice); @@ -968,7 +974,7 @@ std::vector PrintObject::slice_support_volumes(const ModelVolumeType m if (it_volume != it_volume_end) { // Found at least a single support volume of model_volume_type. // Exclude z of ditthered layers. Do we need to improve this? - std::vector zs = zs_from_layers(this->layers(), true); + std::vector zs = zs_from_layers(this->layers(), true); size_t num_layers = this->layers().size(); std::vector merge_layers; bool merge = false; diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index 9f13821dbfa..836bc7f622b 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -73,7 +73,7 @@ const char* support_surface_type_to_color_name(const SupporLayerType surface_typ case SupporLayerType::TopContact: return "rgb(255,0,0)"; // "red"; case SupporLayerType::TopInterface: return "rgb(0,255,0)"; // "green"; case SupporLayerType::Base: return "rgb(0,0,255)"; // "blue"; - case SupporLayerType::BottomInterface:return "rgb(255,255,128)"; // yellow + case SupporLayerType::BottomInterface:return "rgb(255,255,128)"; // yellow case SupporLayerType::BottomContact: return "rgb(255,0,255)"; // magenta case SupporLayerType::RaftInterface: return "rgb(0,255,255)"; case SupporLayerType::RaftBase: return "rgb(128,128,128)"; @@ -134,8 +134,8 @@ void export_print_z_polygons_to_svg(const char *path, SupportGeneratorLayer ** c } void export_print_z_polygons_and_extrusions_to_svg( - const char *path, - SupportGeneratorLayer ** const layers, + const char *path, + SupportGeneratorLayer ** const layers, int n_layers, SupportLayer &support_layer) { @@ -389,7 +389,7 @@ SupportParameters::SupportParameters(const PrintObject &object) SupportMaterialPattern support_pattern = object_config.support_material_pattern; this->with_sheath = object_config.support_material_with_sheath; - this->base_fill_pattern = + this->base_fill_pattern = support_pattern == smpHoneycomb ? ipHoneycomb : this->support_density > 0.95 || this->with_sheath ? ipRectilinear : ipSupportBase; this->interface_fill_pattern = (this->interface_density > 0.95 ? ipRectilinear : ipSupportBase); @@ -444,7 +444,7 @@ PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object // Using the std::deque as an allocator. inline SupportGeneratorLayer& layer_allocate( - std::deque &layer_storage, + std::deque &layer_storage, SupporLayerType layer_type) { layer_storage.push_back(SupportGeneratorLayer()); @@ -471,7 +471,7 @@ inline void layers_append(SupportGeneratorLayersPtr &dst, const SupportGenerator } // Support layer that is covered by some form of dense interface. -static constexpr const std::initializer_list support_types_interface { +static constexpr const std::initializer_list support_types_interface { SupporLayerType::RaftInterface, SupporLayerType::BottomContact, SupporLayerType::BottomInterface, SupporLayerType::TopContact, SupporLayerType::TopInterface }; @@ -615,7 +615,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) SupportGeneratorLayersPtr layers_sorted = #endif // SLIC3R_DEBUG generate_support_layers(object, raft_layers, bottom_contacts, top_contacts, intermediate_layers, interface_layers, base_interface_layers); - // Here the upper_layer and lower_layer pointers are left to null at the support layers, + // Here the upper_layer and lower_layer pointers are left to null at the support layers, BOOST_LOG_TRIVIAL(info) << "Support generator - Generating tool paths"; @@ -1298,10 +1298,10 @@ namespace SupportMaterialInternal { } void remove_bridges_from_contacts( - const PrintConfig &print_config, + const PrintConfig &print_config, const Layer &lower_layer, const LayerRegion &layerm, - float fw, + float fw, Polygons &contact_polygons) { // compute the area of bridging perimeters @@ -1324,7 +1324,7 @@ void remove_bridges_from_contacts( #else Polylines overhang_perimeters = diff_pl(layerm.perimeters().as_polylines(), lower_grown_slices); #endif - + // only consider straight overhangs // only consider overhangs having endpoints inside layer's slices // convert bridging polylines into polygons by inflating them with their thickness @@ -1338,7 +1338,7 @@ void remove_bridges_from_contacts( const float w = float(0.5 * std::max(perimeter_bridge_flow.scaled_width(), perimeter_bridge_flow.scaled_spacing())) + scaled(0.001); for (Polyline &polyline : overhang_perimeters) if (polyline.is_straight()) { - // This is a bridge + // This is a bridge polyline.extend_start(fw); polyline.extend_end(fw); // Is the straight perimeter segment supported at both sides? @@ -1992,12 +1992,10 @@ SupportGeneratorLayersPtr PrintObjectSupportMaterial::top_contact_layers( { const Layer &layer = *object.layers()[layer_id]; const Layer *lower_layer = layer.lower_layer; - if (lower_layer == nullptr && layer_id != 0) { - assert(layer.dithered); + if (lower_layer == nullptr && layer_id != 0 && layer.dithered) { continue; } - Polygons lower_layer_polygons = (layer_id == 0) ? Polygons() : - to_polygons(object.layers()[layer_id - 1]->lslices); + Polygons lower_layer_polygons = (layer_id == 0) ? Polygons() : to_polygons(lower_layer->lslices); SlicesMarginCache slices_margin; auto [overhang_polygons, contact_polygons, enforcer_polygons, no_interface_offset] = @@ -2258,7 +2256,7 @@ static inline std::pair project_support_to_grid(const Layer // For a soluble interface material synchronize the layer heights with the object, // otherwise set the layer height to a bridging flow of a support interface nozzle. SupportGeneratorLayersPtr PrintObjectSupportMaterial::bottom_contact_layers_and_layer_support_areas( - const PrintObject &object, const SupportGeneratorLayersPtr &top_contacts, std::vector &buildplate_covered, + const PrintObject &object, const SupportGeneratorLayersPtr &top_contacts, std::vector &buildplate_covered, SupportGeneratorLayerStorage &layer_storage, std::vector &layer_support_areas) const { if (top_contacts.empty()) @@ -3374,7 +3372,7 @@ static inline void tree_supports_generate_paths( // No hole remaining after an offset. Just copy the outer contour. append(out, std::move(contours)); } else { - // Negative offset. There is a chance, that the offsetted hole intersects the outer contour. + // Negative offset. There is a chance, that the offsetted hole intersects the outer contour. // Subtract the offsetted holes from the offsetted contours. ClipperLib_Z::Clipper clipper; clipper.ZFillFunction([](const ClipperLib_Z::IntPoint &e1bot, const ClipperLib_Z::IntPoint &e1top, const ClipperLib_Z::IntPoint &e2bot, const ClipperLib_Z::IntPoint &e2top, ClipperLib_Z::IntPoint &pt) { @@ -3520,7 +3518,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.mm3_per_mm(), flow.width(), flow.height(), // Disable reversal of the path, always start with the anchor, always print CCW. false); if (eec) { @@ -4198,7 +4196,7 @@ SupportGeneratorLayersPtr generate_support_layers( height_min = std::min(height_min, layer.height); } if (! empty) { - // Here the upper_layer and lower_layer pointers are left to null at the support layers, + // Here the upper_layer and lower_layer pointers are left to null at the support layers, // as they are never used. These pointers are candidates for removal. bool this_layer_contacts_only = num_top_contacts > 0 && num_top_contacts == num_interfaces; size_t this_layer_id_interface = layer_id_interface; @@ -4273,7 +4271,7 @@ void generate_support_toolpaths( // Print the support base below the support columns, or the support base for the support columns plus the contacts. if (support_layer_id > 0) { - const Polygons &to_infill_polygons = (support_layer_id < slicing_params.base_raft_layers) ? + const Polygons &to_infill_polygons = (support_layer_id < slicing_params.base_raft_layers) ? raft_layer.polygons : //FIXME misusing contact_polygons for support columns. ((raft_layer.contact_polygons == nullptr) ? Polygons() : *raft_layer.contact_polygons); @@ -4327,7 +4325,7 @@ void generate_support_toolpaths( // Filler and its parameters filler, density, // Extrusion parameters - (support_layer_id < slicing_params.base_raft_layers) ? ExtrusionRole::SupportMaterial : ExtrusionRole::SupportMaterialInterface, flow, + (support_layer_id < slicing_params.base_raft_layers) ? ExtrusionRole::SupportMaterial : ExtrusionRole::SupportMaterialInterface, flow, // sheath at first layer support_layer_id == 0, support_layer_id == 0); } @@ -4373,7 +4371,7 @@ void generate_support_toolpaths( // Pointer to the 1st layer interface filler. auto filler_first_layer = filler_first_layer_ptr ? filler_first_layer_ptr.get() : filler_interface.get(); // Filler for the 1st layer interface, if different from filler_interface. - auto filler_raft_contact_ptr = std::unique_ptr(range.begin() == n_raft_layers && config.support_material_interface_layers.value == 0 ? + auto filler_raft_contact_ptr = std::unique_ptr(range.begin() == n_raft_layers && config.support_material_interface_layers.value == 0 ? Fill::new_from_type(support_params.raft_interface_fill_pattern) : nullptr); // Pointer to the 1st layer interface filler. auto filler_raft_contact = filler_raft_contact_ptr ? filler_raft_contact_ptr.get() : filler_interface.get(); @@ -4475,14 +4473,14 @@ void generate_support_toolpaths( auto *filler = raft_contact ? filler_raft_contact : filler_interface.get(); auto interface_flow = layer_ex.layer->bridging ? Flow::bridging_flow(layer_ex.layer->height, support_params.support_material_bottom_interface_flow.nozzle_diameter()) : - (raft_contact ? &support_params.raft_interface_flow : + (raft_contact ? &support_params.raft_interface_flow : interface_as_base ? &support_params.support_material_flow : &support_params.support_material_interface_flow) ->with_height(float(layer_ex.layer->height)); filler->angle = interface_as_base ? // If zero interface layers are configured, use the same angle as for the base layers. angles[support_layer_id % angles.size()] : // Use interface angle for the interface layers. - raft_contact ? + raft_contact ? support_params.raft_interface_angle(support_layer.interface_id()) : support_interface_angle; double density = raft_contact ? support_params.raft_interface_density : interface_as_base ? support_params.support_density : support_params.interface_density; @@ -4491,7 +4489,7 @@ void generate_support_toolpaths( filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / density)); fill_expolygons_generate_paths( // Destination - layer_ex.extrusions, + layer_ex.extrusions, // Regions to fill union_safety_offset_ex(layer_ex.polygons_to_extrude()), // Filler and its parameters diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index 5062fe18abf..a1e1b6712d1 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -758,7 +758,14 @@ std::tuple check_stability(const PrintObject *po, } }; - for (size_t layer_idx = 0; layer_idx < po->layer_count(); ++layer_idx) { + // Skip over dithered layers + assert(!po->get_layer(0)->dithered); + auto next_layer_index = [&po](size_t layer_idx) { + const Layer *upper_layer = po->get_layer(layer_idx)->upper_layer; + return upper_layer != nullptr ? upper_layer->id() : po->layer_count(); + }; + + for (size_t layer_idx = 0; layer_idx < po->layer_count(); layer_idx = next_layer_index(layer_idx)) { cancel_func(); const Layer *layer = po->get_layer(layer_idx); float bottom_z = layer->bottom_z(); @@ -844,7 +851,7 @@ std::tuple check_stability(const PrintObject *po, SliceConnection &weakest_conn = prev_slice_idx_to_weakest_connection[slice_idx]; std::vector boundary_lines; - for (const auto &link : slice.overlaps_below) { + for (const auto &link : slice.overlaps_below) { auto ls = to_unscaled_linesf({layer->lower_layer->lslices[link.slice_idx]}); boundary_lines.insert(boundary_lines.end(), ls.begin(), ls.end()); } @@ -944,8 +951,8 @@ std::tuple check_stability(const PrintObject *po, // get_extents(scaledl)); // svg.draw(scaledl, "red", scale_(0.4)); // svg.draw(perimsl, "blue", scale_(0.25)); - - + + // svg.Close(); // } } @@ -1121,7 +1128,7 @@ void estimate_malformations(LayerPtrs &layers, const Params ¶ms) std::vector current_layer_lines; for (const LayerRegion *layer_region : l->regions()) { for (const ExtrusionEntity *extrusion : layer_region->perimeters().flatten().entities) { - + if (!extrusion->role().is_external_perimeter()) continue; Points extrusion_pts; diff --git a/src/libslic3r/ZDither.cpp b/src/libslic3r/ZDither.cpp index 2b930e8608e..ec17e7ecdbe 100644 --- a/src/libslic3r/ZDither.cpp +++ b/src/libslic3r/ZDither.cpp @@ -104,10 +104,11 @@ std::vector apply_z_dither(std::vector &expolys, sublayers->resize(expolys.size()); std::vector out(expolys.size()); - for (auto ll = 0; ll < expolys.size(); ll++) { + out[0] = std::move(expolys[0]); // Do not make sublayers of first layer + for (auto ll = 1; ll < expolys.size(); ll++) { // idx0 - bottom of layer, idx1 - top of layer - int upwrd_idx0 = ll > 0 ? upwrd_mididx[ll - 1] : -1; - int dnwrd_idx0 = ll > 0 ? dnwrd_mididx[ll - 1] : -1; + int upwrd_idx0 = upwrd_mididx[ll - 1]; + int dnwrd_idx0 = dnwrd_mididx[ll - 1]; int upwrd_idx1 = upwrd_mididx[ll]; int dnwrd_idx1 = dnwrd_mididx[ll]; From f6a6ef7c5b3a761c0d87db097d5bf160d05c0f61 Mon Sep 17 00:00:00 2001 From: Leonid Raiz Date: Mon, 17 Apr 2023 14:52:55 -0400 Subject: [PATCH 5/9] Debug merged code --- src/libslic3r/PrintConfig.cpp | 1 - src/libslic3r/PrintObject.cpp | 11 +++--- src/libslic3r/PrintObjectSlice.cpp | 28 +++++++++------ src/libslic3r/SupportMaterial.cpp | 48 ++++++++++++------------- src/libslic3r/SupportSpotsGenerator.cpp | 17 ++++++--- src/libslic3r/ZDither.cpp | 7 ++-- 6 files changed, 62 insertions(+), 50 deletions(-) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 8b67ae4bed0..de184114498 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -3926,7 +3926,6 @@ void PrintConfigDef::init_sla_params() def->mode = comSimple; def->set_default_value(new ConfigOptionEnum(sla::SupportTreeType::Default)); - init_sla_support_params(""); init_sla_support_params("branching"); diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index a7f460211a7..4d561e64759 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -283,6 +283,7 @@ void PrintObject::prepare_infill() // Decide what surfaces are to be filled. // Here the stTop / stBottomBridge / stBottom infill is turned to just stInternal if zero top / bottom infill layers are configured. // Also tiny stInternal surfaces are turned to stInternalSolid. + BOOST_LOG_TRIVIAL(info) << "Preparing fill surfaces..." << log_memory_info(); for (auto *layer : m_layers) { for (auto *region : layer->m_regions) { @@ -1135,7 +1136,7 @@ void PrintObject::process_external_surfaces() tbb::blocked_range(0, m_layers.size() - 1), [this, &surfaces_covered, &layer_expansions_and_voids, unsupported_width](const tbb::blocked_range& range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) - if (layer_expansions_and_voids[next_layer_index(layer_idx, false)]) { + if (layer_expansions_and_voids[next_layer_index(layer_idx, false)]) { // Layer above is partially filled with solid infill (top, bottom, bridging...), // while some sparse inill regions are empty (0% infill). m_print->throw_if_canceled(); @@ -1737,8 +1738,8 @@ void PrintObject::bridge_over_infill() std::vector layers_to_generate_infill; for (const auto &pair : surfaces_by_layer) { assert(pair.first > 0); - infill_lines[pair.first - 1] = {}; - layers_to_generate_infill.push_back(pair.first - 1); + infill_lines[next_layer_index(pair.first, true)] = {}; + layers_to_generate_infill.push_back(next_layer_index(pair.first, true)); } tbb::parallel_for(tbb::blocked_range(0, layers_to_generate_infill.size()), [po = static_cast(this), @@ -1821,7 +1822,7 @@ void PrintObject::bridge_over_infill() ExPolygons layers_sparse_infill{}; ExPolygons not_sparse_infill{}; double bottom_z = po->get_layer(lidx)->print_z - target_flow_height * target_flow_height_factor - EPSILON; - for (int i = next_layer_index(lidx, true); i >= 0; i= next_layer_index(i, true)) { + for (int i = next_layer_index(lidx, true); i >= 0; i = next_layer_index(i, true)) { // Stop iterating if layer is lower than bottom_z. const Layer *layer = po->get_layer(i); if (layer->print_z < bottom_z) @@ -2167,7 +2168,7 @@ void PrintObject::bridge_over_infill() total_fill_area = closing(total_fill_area, SCALED_EPSILON); expansion_area = closing(expansion_area, SCALED_EPSILON); expansion_area = intersection(expansion_area, deep_infill_area); - Polylines anchors = intersection_pl(infill_lines[lidx - 1], shrink(expansion_area, spacing)); + Polylines anchors = intersection_pl(infill_lines[po->next_layer_index(lidx,true)], shrink(expansion_area, spacing)); #ifdef DEBUG_BRIDGE_OVER_INFILL debug_draw(std::to_string(lidx) + "_" + std::to_string(cluster_idx) + "_" + std::to_string(job_idx) + "_" + "_total_area", diff --git a/src/libslic3r/PrintObjectSlice.cpp b/src/libslic3r/PrintObjectSlice.cpp index 371afcb309c..1e763e15ce6 100644 --- a/src/libslic3r/PrintObjectSlice.cpp +++ b/src/libslic3r/PrintObjectSlice.cpp @@ -413,7 +413,7 @@ static std::vector> slices_to_regions( } } if (merged) - // to handle region overlaps. Indeed, one may intentionally let the regions overlap to produce crossing perimeters + // to handle region overlaps. Indeed, one may intentionally let the regions overlap to produce crossing perimeters expolygons = closing_ex(expolygons, float(scale_(EPSILON))); slices_by_region[temp_slices[i].region_id][z_idx] = std::move(expolygons); i = j; @@ -469,7 +469,7 @@ std::string fix_slicing_errors(LayerPtrs &layers, const std::function &t // Collect outer contours and holes from the valid layers above & below. Polygons outer; outer.reserve( - ((upper_surfaces == nullptr) ? 0 : upper_surfaces->size()) + + ((upper_surfaces == nullptr) ? 0 : upper_surfaces->size()) + ((lower_surfaces == nullptr) ? 0 : lower_surfaces->size())); size_t num_holes = 0; if (upper_surfaces) @@ -565,15 +565,21 @@ void PrintObject::slice() tbb::parallel_for( tbb::blocked_range(1, m_layers.size()), [this](const tbb::blocked_range &range) { - for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { - m_print->throw_if_canceled(); - Layer &above = *m_layers[layer_idx]; - Layer &below = *m_layers[next_layer_index(layer_idx, true)]; - if (above.dithered == below.dithered) { - Layer::build_up_down_graph(below, above); - } + for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { + m_print->throw_if_canceled(); + // Layer::build_up_down_graph and subsequent support stability checks + // appear to get in trouble when multiple layers point to the same + // above/below layer as happens in case of z-dithering. For now I just avoid + // mixing dithered and non-dithered layers in the same graph. + Layer *above = m_layers[layer_idx]; + Layer *below = above->lower_layer; + if (below != nullptr && above->dithered == below->dithered) { + Layer::build_up_down_graph(*below, *above); } + // Layer::build_up_down_graph(*m_layers[layer_idx - 1], *m_layers[layer_idx]); + } }); + // }); if (m_layers.empty()) throw Slic3r::SlicingError("No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry.\n"); this->set_done(posSlice); @@ -860,7 +866,7 @@ void PrintObject::slice_volumes() // Is any ModelVolume MMU painted? if (const auto& volumes = this->model_object()->volumes; - m_print->config().nozzle_diameter.size() > 1 && + m_print->config().nozzle_diameter.size() > 1 && !this->config().z_dither && std::find_if(volumes.begin(), volumes.end(), [](const ModelVolume* v) { return !v->mmu_segmentation_facets.empty(); }) != volumes.end()) { // If XY Size compensation is also enabled, notify the user that XY Size compensation @@ -968,7 +974,7 @@ std::vector PrintObject::slice_support_volumes(const ModelVolumeType m if (it_volume != it_volume_end) { // Found at least a single support volume of model_volume_type. // Exclude z of ditthered layers. Do we need to improve this? - std::vector zs = zs_from_layers(this->layers(), true); + std::vector zs = zs_from_layers(this->layers(), true); size_t num_layers = this->layers().size(); std::vector merge_layers; bool merge = false; diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index 9f13821dbfa..836bc7f622b 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -73,7 +73,7 @@ const char* support_surface_type_to_color_name(const SupporLayerType surface_typ case SupporLayerType::TopContact: return "rgb(255,0,0)"; // "red"; case SupporLayerType::TopInterface: return "rgb(0,255,0)"; // "green"; case SupporLayerType::Base: return "rgb(0,0,255)"; // "blue"; - case SupporLayerType::BottomInterface:return "rgb(255,255,128)"; // yellow + case SupporLayerType::BottomInterface:return "rgb(255,255,128)"; // yellow case SupporLayerType::BottomContact: return "rgb(255,0,255)"; // magenta case SupporLayerType::RaftInterface: return "rgb(0,255,255)"; case SupporLayerType::RaftBase: return "rgb(128,128,128)"; @@ -134,8 +134,8 @@ void export_print_z_polygons_to_svg(const char *path, SupportGeneratorLayer ** c } void export_print_z_polygons_and_extrusions_to_svg( - const char *path, - SupportGeneratorLayer ** const layers, + const char *path, + SupportGeneratorLayer ** const layers, int n_layers, SupportLayer &support_layer) { @@ -389,7 +389,7 @@ SupportParameters::SupportParameters(const PrintObject &object) SupportMaterialPattern support_pattern = object_config.support_material_pattern; this->with_sheath = object_config.support_material_with_sheath; - this->base_fill_pattern = + this->base_fill_pattern = support_pattern == smpHoneycomb ? ipHoneycomb : this->support_density > 0.95 || this->with_sheath ? ipRectilinear : ipSupportBase; this->interface_fill_pattern = (this->interface_density > 0.95 ? ipRectilinear : ipSupportBase); @@ -444,7 +444,7 @@ PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object // Using the std::deque as an allocator. inline SupportGeneratorLayer& layer_allocate( - std::deque &layer_storage, + std::deque &layer_storage, SupporLayerType layer_type) { layer_storage.push_back(SupportGeneratorLayer()); @@ -471,7 +471,7 @@ inline void layers_append(SupportGeneratorLayersPtr &dst, const SupportGenerator } // Support layer that is covered by some form of dense interface. -static constexpr const std::initializer_list support_types_interface { +static constexpr const std::initializer_list support_types_interface { SupporLayerType::RaftInterface, SupporLayerType::BottomContact, SupporLayerType::BottomInterface, SupporLayerType::TopContact, SupporLayerType::TopInterface }; @@ -615,7 +615,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) SupportGeneratorLayersPtr layers_sorted = #endif // SLIC3R_DEBUG generate_support_layers(object, raft_layers, bottom_contacts, top_contacts, intermediate_layers, interface_layers, base_interface_layers); - // Here the upper_layer and lower_layer pointers are left to null at the support layers, + // Here the upper_layer and lower_layer pointers are left to null at the support layers, BOOST_LOG_TRIVIAL(info) << "Support generator - Generating tool paths"; @@ -1298,10 +1298,10 @@ namespace SupportMaterialInternal { } void remove_bridges_from_contacts( - const PrintConfig &print_config, + const PrintConfig &print_config, const Layer &lower_layer, const LayerRegion &layerm, - float fw, + float fw, Polygons &contact_polygons) { // compute the area of bridging perimeters @@ -1324,7 +1324,7 @@ void remove_bridges_from_contacts( #else Polylines overhang_perimeters = diff_pl(layerm.perimeters().as_polylines(), lower_grown_slices); #endif - + // only consider straight overhangs // only consider overhangs having endpoints inside layer's slices // convert bridging polylines into polygons by inflating them with their thickness @@ -1338,7 +1338,7 @@ void remove_bridges_from_contacts( const float w = float(0.5 * std::max(perimeter_bridge_flow.scaled_width(), perimeter_bridge_flow.scaled_spacing())) + scaled(0.001); for (Polyline &polyline : overhang_perimeters) if (polyline.is_straight()) { - // This is a bridge + // This is a bridge polyline.extend_start(fw); polyline.extend_end(fw); // Is the straight perimeter segment supported at both sides? @@ -1992,12 +1992,10 @@ SupportGeneratorLayersPtr PrintObjectSupportMaterial::top_contact_layers( { const Layer &layer = *object.layers()[layer_id]; const Layer *lower_layer = layer.lower_layer; - if (lower_layer == nullptr && layer_id != 0) { - assert(layer.dithered); + if (lower_layer == nullptr && layer_id != 0 && layer.dithered) { continue; } - Polygons lower_layer_polygons = (layer_id == 0) ? Polygons() : - to_polygons(object.layers()[layer_id - 1]->lslices); + Polygons lower_layer_polygons = (layer_id == 0) ? Polygons() : to_polygons(lower_layer->lslices); SlicesMarginCache slices_margin; auto [overhang_polygons, contact_polygons, enforcer_polygons, no_interface_offset] = @@ -2258,7 +2256,7 @@ static inline std::pair project_support_to_grid(const Layer // For a soluble interface material synchronize the layer heights with the object, // otherwise set the layer height to a bridging flow of a support interface nozzle. SupportGeneratorLayersPtr PrintObjectSupportMaterial::bottom_contact_layers_and_layer_support_areas( - const PrintObject &object, const SupportGeneratorLayersPtr &top_contacts, std::vector &buildplate_covered, + const PrintObject &object, const SupportGeneratorLayersPtr &top_contacts, std::vector &buildplate_covered, SupportGeneratorLayerStorage &layer_storage, std::vector &layer_support_areas) const { if (top_contacts.empty()) @@ -3374,7 +3372,7 @@ static inline void tree_supports_generate_paths( // No hole remaining after an offset. Just copy the outer contour. append(out, std::move(contours)); } else { - // Negative offset. There is a chance, that the offsetted hole intersects the outer contour. + // Negative offset. There is a chance, that the offsetted hole intersects the outer contour. // Subtract the offsetted holes from the offsetted contours. ClipperLib_Z::Clipper clipper; clipper.ZFillFunction([](const ClipperLib_Z::IntPoint &e1bot, const ClipperLib_Z::IntPoint &e1top, const ClipperLib_Z::IntPoint &e2bot, const ClipperLib_Z::IntPoint &e2top, ClipperLib_Z::IntPoint &pt) { @@ -3520,7 +3518,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.mm3_per_mm(), flow.width(), flow.height(), // Disable reversal of the path, always start with the anchor, always print CCW. false); if (eec) { @@ -4198,7 +4196,7 @@ SupportGeneratorLayersPtr generate_support_layers( height_min = std::min(height_min, layer.height); } if (! empty) { - // Here the upper_layer and lower_layer pointers are left to null at the support layers, + // Here the upper_layer and lower_layer pointers are left to null at the support layers, // as they are never used. These pointers are candidates for removal. bool this_layer_contacts_only = num_top_contacts > 0 && num_top_contacts == num_interfaces; size_t this_layer_id_interface = layer_id_interface; @@ -4273,7 +4271,7 @@ void generate_support_toolpaths( // Print the support base below the support columns, or the support base for the support columns plus the contacts. if (support_layer_id > 0) { - const Polygons &to_infill_polygons = (support_layer_id < slicing_params.base_raft_layers) ? + const Polygons &to_infill_polygons = (support_layer_id < slicing_params.base_raft_layers) ? raft_layer.polygons : //FIXME misusing contact_polygons for support columns. ((raft_layer.contact_polygons == nullptr) ? Polygons() : *raft_layer.contact_polygons); @@ -4327,7 +4325,7 @@ void generate_support_toolpaths( // Filler and its parameters filler, density, // Extrusion parameters - (support_layer_id < slicing_params.base_raft_layers) ? ExtrusionRole::SupportMaterial : ExtrusionRole::SupportMaterialInterface, flow, + (support_layer_id < slicing_params.base_raft_layers) ? ExtrusionRole::SupportMaterial : ExtrusionRole::SupportMaterialInterface, flow, // sheath at first layer support_layer_id == 0, support_layer_id == 0); } @@ -4373,7 +4371,7 @@ void generate_support_toolpaths( // Pointer to the 1st layer interface filler. auto filler_first_layer = filler_first_layer_ptr ? filler_first_layer_ptr.get() : filler_interface.get(); // Filler for the 1st layer interface, if different from filler_interface. - auto filler_raft_contact_ptr = std::unique_ptr(range.begin() == n_raft_layers && config.support_material_interface_layers.value == 0 ? + auto filler_raft_contact_ptr = std::unique_ptr(range.begin() == n_raft_layers && config.support_material_interface_layers.value == 0 ? Fill::new_from_type(support_params.raft_interface_fill_pattern) : nullptr); // Pointer to the 1st layer interface filler. auto filler_raft_contact = filler_raft_contact_ptr ? filler_raft_contact_ptr.get() : filler_interface.get(); @@ -4475,14 +4473,14 @@ void generate_support_toolpaths( auto *filler = raft_contact ? filler_raft_contact : filler_interface.get(); auto interface_flow = layer_ex.layer->bridging ? Flow::bridging_flow(layer_ex.layer->height, support_params.support_material_bottom_interface_flow.nozzle_diameter()) : - (raft_contact ? &support_params.raft_interface_flow : + (raft_contact ? &support_params.raft_interface_flow : interface_as_base ? &support_params.support_material_flow : &support_params.support_material_interface_flow) ->with_height(float(layer_ex.layer->height)); filler->angle = interface_as_base ? // If zero interface layers are configured, use the same angle as for the base layers. angles[support_layer_id % angles.size()] : // Use interface angle for the interface layers. - raft_contact ? + raft_contact ? support_params.raft_interface_angle(support_layer.interface_id()) : support_interface_angle; double density = raft_contact ? support_params.raft_interface_density : interface_as_base ? support_params.support_density : support_params.interface_density; @@ -4491,7 +4489,7 @@ void generate_support_toolpaths( filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / density)); fill_expolygons_generate_paths( // Destination - layer_ex.extrusions, + layer_ex.extrusions, // Regions to fill union_safety_offset_ex(layer_ex.polygons_to_extrude()), // Filler and its parameters diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index 5062fe18abf..a1e1b6712d1 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -758,7 +758,14 @@ std::tuple check_stability(const PrintObject *po, } }; - for (size_t layer_idx = 0; layer_idx < po->layer_count(); ++layer_idx) { + // Skip over dithered layers + assert(!po->get_layer(0)->dithered); + auto next_layer_index = [&po](size_t layer_idx) { + const Layer *upper_layer = po->get_layer(layer_idx)->upper_layer; + return upper_layer != nullptr ? upper_layer->id() : po->layer_count(); + }; + + for (size_t layer_idx = 0; layer_idx < po->layer_count(); layer_idx = next_layer_index(layer_idx)) { cancel_func(); const Layer *layer = po->get_layer(layer_idx); float bottom_z = layer->bottom_z(); @@ -844,7 +851,7 @@ std::tuple check_stability(const PrintObject *po, SliceConnection &weakest_conn = prev_slice_idx_to_weakest_connection[slice_idx]; std::vector boundary_lines; - for (const auto &link : slice.overlaps_below) { + for (const auto &link : slice.overlaps_below) { auto ls = to_unscaled_linesf({layer->lower_layer->lslices[link.slice_idx]}); boundary_lines.insert(boundary_lines.end(), ls.begin(), ls.end()); } @@ -944,8 +951,8 @@ std::tuple check_stability(const PrintObject *po, // get_extents(scaledl)); // svg.draw(scaledl, "red", scale_(0.4)); // svg.draw(perimsl, "blue", scale_(0.25)); - - + + // svg.Close(); // } } @@ -1121,7 +1128,7 @@ void estimate_malformations(LayerPtrs &layers, const Params ¶ms) std::vector current_layer_lines; for (const LayerRegion *layer_region : l->regions()) { for (const ExtrusionEntity *extrusion : layer_region->perimeters().flatten().entities) { - + if (!extrusion->role().is_external_perimeter()) continue; Points extrusion_pts; diff --git a/src/libslic3r/ZDither.cpp b/src/libslic3r/ZDither.cpp index 2b930e8608e..ec17e7ecdbe 100644 --- a/src/libslic3r/ZDither.cpp +++ b/src/libslic3r/ZDither.cpp @@ -104,10 +104,11 @@ std::vector apply_z_dither(std::vector &expolys, sublayers->resize(expolys.size()); std::vector out(expolys.size()); - for (auto ll = 0; ll < expolys.size(); ll++) { + out[0] = std::move(expolys[0]); // Do not make sublayers of first layer + for (auto ll = 1; ll < expolys.size(); ll++) { // idx0 - bottom of layer, idx1 - top of layer - int upwrd_idx0 = ll > 0 ? upwrd_mididx[ll - 1] : -1; - int dnwrd_idx0 = ll > 0 ? dnwrd_mididx[ll - 1] : -1; + int upwrd_idx0 = upwrd_mididx[ll - 1]; + int dnwrd_idx0 = dnwrd_mididx[ll - 1]; int upwrd_idx1 = upwrd_mididx[ll]; int dnwrd_idx1 = dnwrd_mididx[ll]; From 950b94b59403d9e556d2ccd61d0efc01c8299207 Mon Sep 17 00:00:00 2001 From: Leonid Raiz Date: Sun, 21 May 2023 14:18:50 -0400 Subject: [PATCH 6/9] Styling changes --- src/libslic3r/GCode.cpp | 10 +- src/libslic3r/Layer.hpp | 9 +- src/libslic3r/Preset.cpp | 6 +- src/libslic3r/Print.cpp | 76 +- src/libslic3r/Print.hpp | 22 +- src/libslic3r/PrintConfig.cpp | 42 +- src/libslic3r/PrintConfig.hpp | 1648 ++++++++++----------- src/libslic3r/PrintObject.cpp | 132 +- src/libslic3r/PrintObjectSlice.cpp | 69 +- src/libslic3r/Support/SupportMaterial.cpp | 197 ++- src/libslic3r/SupportSpotsGenerator.cpp | 3 - src/libslic3r/TriangleMeshSlicer.hpp | 6 +- src/slic3r/GUI/Tab.cpp | 2 +- 13 files changed, 1098 insertions(+), 1124 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index cf54e94f272..34eb8f0a5c2 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -652,8 +652,6 @@ std::vector> GCode::collect_laye size_t object_idx; size_t layer_idx; coordf_t layer_height; - - }; std::vector per_object(print.objects().size(), ObjectsLayerToPrint()); @@ -672,9 +670,9 @@ std::vector> GCode::collect_laye } } - std::sort(ordering.begin(), ordering.end(), [](const OrderingItem& oi1, const OrderingItem& oi2) { + std::sort(ordering.begin(), ordering.end(), [](const OrderingItem &oi1, const OrderingItem &oi2) { return fabs(oi1.print_z - oi2.print_z) > EPSILON ? oi1.print_z < oi2.print_z : oi1.layer_height < oi2.layer_height; - }); + }); std::vector> layers_to_print; @@ -689,8 +687,8 @@ std::vector> GCode::collect_laye std::pair merged; // Assign an average print_z to the set of layers with nearly equal print_z. merged.first = 0.5 * (ordering[i].print_z + ordering[j - 1].print_z); - // z-dithering may result in 2 layers from the same object in merged - for (; i < j; ++ i) { + // z-dithering may result in 2 layers from the same object in merged + for (; i < j; ++i) { const OrderingItem& oi = ordering[i]; merged.second.emplace_back(std::move(per_object[oi.object_idx][oi.layer_idx])); } diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index 3528430c02f..8bdfd2c69c6 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -329,7 +329,7 @@ class Layer //Extrusions estimated to be seriously malformed, estimated during "Estimating curled extrusions" step. These lines should be avoided during fast travels. CurledLines curled_lines; - // Collection of expolygons generated by slicing the possibly multiple meshes of the source geometry + // Collection of expolygons generated by slicing the possibly multiple meshes of the source geometry // (with possibly differing extruder ID and slicing parameters) and merged. // For the first layer, if the Elephant foot compensation is applied, this lslice is uncompensated, therefore // it includes the Elephant foot effect, thus it corresponds to the shape of the printed 1st layer. @@ -347,7 +347,7 @@ class Layer LayerRegion* add_region(const PrintRegion *print_region); const LayerRegionPtrs& regions() const { return m_regions; } // Test whether whether there are any slices assigned to this layer. - bool empty() const; + bool empty() const; void make_slices(); // After creating the slices on all layers, chain the islands overlapping in Z. static void build_up_down_graph(Layer &below, Layer &above); @@ -427,7 +427,7 @@ class Layer LayerRegionPtrs m_regions; }; -class SupportLayer : public Layer +class SupportLayer : public Layer { public: // Polygons covered by the supports: base, interface and contact areas. @@ -462,10 +462,9 @@ inline std::vector zs_from_layers(const LayerContainer &layers, bool excl { std::vector zs; zs.reserve(layers.size()); - for (const Layer *l : layers) { + for (const Layer *l : layers) if (!(l->dithered && exclude_dithered)) zs.emplace_back((float) l->slice_z); - } return zs; } diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 7e5a13228d8..0529cfca7a1 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -417,7 +417,7 @@ void Preset::set_visible_from_appconfig(const AppConfig &app_config) for (auto it = this->renamed_from.begin(); ! is_visible && it != this->renamed_from.end(); ++ it) is_visible = has(*it); } - else + else is_visible = false; } } @@ -678,7 +678,7 @@ void PresetCollection::add_default_preset(const std::vector &keys, // Load all presets found in dir_path. // Throws an exception on error. void PresetCollection::load_presets( - const std::string &dir_path, const std::string &subdir, + const std::string &dir_path, const std::string &subdir, PresetsConfigSubstitutions& substitutions, ForwardCompatibilitySubstitutionRule substitution_rule) { // Don't use boost::filesystem::canonical() on Windows, it is broken in regard to reparse points, @@ -1755,7 +1755,7 @@ PhysicalPrinterCollection::PhysicalPrinterCollection( const std::vector; PrintRegion::PrintRegion(const PrintRegionConfig &config) : PrintRegion(config, config.hash()) {} PrintRegion::PrintRegion(PrintRegionConfig &&config) : PrintRegion(std::move(config), config.hash()) {} -void Print::clear() +void Print::clear() { std::scoped_lock lock(this->state_mutex()); // The following call should stop background processing if it is running. @@ -226,7 +226,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n //FIXME Killing supports on any change of "filament_soluble" is rough. We should check for each object whether that is necessary. osteps.emplace_back(posSupportMaterial); } else if ( - opt_key == "first_layer_extrusion_width" + opt_key == "first_layer_extrusion_width" || opt_key == "min_layer_height" || opt_key == "max_layer_height" || opt_key == "gcode_resolution") { @@ -317,7 +317,7 @@ std::vector Print::support_material_extruders() const if (support_uses_current_extruder) // Add all object extruders to the support extruders as it is not know which one will be used to print supports. append(extruders, this->object_extruders()); - + sort_remove_duplicates(extruders); return extruders; } @@ -347,9 +347,9 @@ double Print::max_allowed_layer_height() const return nozzle_diameter_max; } -std::vector Print::print_object_ids() const -{ - std::vector out; +std::vector Print::print_object_ids() const +{ + std::vector out; // Reserve one more for the caller to append the ID of the Print itself. out.reserve(m_objects.size() + 1); for (const PrintObject *print_object : m_objects) @@ -390,7 +390,7 @@ bool Print::sequential_print_horizontal_clearance_valid(const Print& print, Poly // Get convex hull of all printable volumes assigned to this print object. ModelInstance *model_instance0 = print_object->model_object()->instances.front(); if (it_convex_hull == map_model_object_to_convex_hull.end()) { - // Calculate the convex hull of a printable object. + // Calculate the convex hull of a printable object. // Grow convex hull with the clearance margin. // FIXME: Arrangement has different parameters for offsetting (jtMiter, limit 2) // which causes that the warning will be showed after arrangement with the @@ -574,7 +574,7 @@ std::string Print::validate(std::string* warning) const return _u8L("The Wipe Tower currently does not support volumetric E (use_volumetric_e=0)."); if (m_config.complete_objects && extruders.size() > 1) return _u8L("The Wipe Tower is currently not supported for multimaterial sequential prints."); - + if (m_objects.size() > 1) { const SlicingParameters &slicing_params0 = m_objects.front()->slicing_parameters(); size_t tallest_object_idx = 0; @@ -622,7 +622,7 @@ std::string Print::validate(std::string* warning) const } } } - + { // Find the smallest used nozzle diameter and the number of unique nozzle diameters. double min_nozzle_diameter = std::numeric_limits::max(); @@ -688,7 +688,7 @@ std::string Print::validate(std::string* warning) const // Notify the user in that case. if (! object->has_support() && warning) { for (const ModelVolume* mv : object->model_object()->volumes) { - bool has_enforcers = mv->is_support_enforcer() || + bool has_enforcers = mv->is_support_enforcer() || (mv->is_model_part() && mv->supported_facets.has_facets(*mv, EnforcerBlockerType::ENFORCER)); if (has_enforcers) { *warning = "_SUPPORTS_OFF"; @@ -706,8 +706,8 @@ std::string Print::validate(std::string* warning) const size_t first_layer_extruder = object->config().raft_layers == 1 ? object->config().support_material_interface_extruder-1 : object->config().support_material_extruder-1; - first_layer_min_nozzle_diameter = (first_layer_extruder == size_t(-1)) ? - min_nozzle_diameter : + first_layer_min_nozzle_diameter = (first_layer_extruder == size_t(-1)) ? + min_nozzle_diameter : m_config.nozzle_diameter.get_at(first_layer_extruder); } else { // if we don't have raft layers, any nozzle diameter is potentially used in first layer @@ -715,7 +715,7 @@ std::string Print::validate(std::string* warning) const } if (first_layer_height > first_layer_min_nozzle_diameter) return _u8L("First layer height can't be greater than nozzle diameter"); - + // validate layer_height double layer_height = object->config().layer_height.value; if (layer_height > min_nozzle_diameter) @@ -772,16 +772,16 @@ BoundingBox Print::total_bounding_box() const { // get objects bounding box BoundingBox bb = this->bounding_box(); - + // we need to offset the objects bounding box by at least half the perimeters extrusion width Flow perimeter_flow = m_objects.front()->get_layer(0)->get_region(0)->flow(frPerimeter); double extra = perimeter_flow.width/2; - + // consider support material if (this->has_support_material()) { extra = std::max(extra, SUPPORT_MATERIAL_MARGIN); } - + // consider brim and skirt if (m_config.brim_width.value > 0) { Flow brim_flow = this->brim_flow(); @@ -799,10 +799,10 @@ BoundingBox Print::total_bounding_box() const + skirt_flow.width/2 ); } - + if (extra > 0) bb.offset(scale_(extra)); - + return bb; } #endif @@ -816,11 +816,11 @@ double Print::skirt_first_layer_height() const Flow Print::brim_flow() const { ConfigOptionFloatOrPercent width = m_config.first_layer_extrusion_width; - if (width.value == 0) + if (width.value == 0) width = m_print_regions.front()->config().perimeter_extrusion_width; - if (width.value == 0) + if (width.value == 0) width = m_objects.front()->config().extrusion_width; - + /* We currently use a random region's perimeter extruder. While this works for most cases, we should probably consider all of the perimeter extruders and take the one with, say, the smallest index. @@ -836,11 +836,11 @@ Flow Print::brim_flow() const Flow Print::skirt_flow() const { ConfigOptionFloatOrPercent width = m_config.first_layer_extrusion_width; - if (width.value == 0) + if (width.value == 0) width = m_print_regions.front()->config().perimeter_extrusion_width; if (width.value == 0) width = m_objects.front()->config().extrusion_width; - + /* We currently use a random object's support material extruder. While this works for most cases, we should probably consider all of the support material extruders and take the one with, say, the smallest index; @@ -856,7 +856,7 @@ Flow Print::skirt_flow() const bool Print::has_support_material() const { for (const PrintObject *object : m_objects) - if (object->has_support_material()) + if (object->has_support_material()) return true; return false; } @@ -868,7 +868,7 @@ void Print::auto_assign_extruders(ModelObject* model_object) const // only assign extruders if object has more than one volume if (model_object->volumes.size() < 2) return; - + // size_t extruders = m_config.nozzle_diameter.values.size(); for (size_t volume_id = 0; volume_id < model_object->volumes.size(); ++ volume_id) { ModelVolume *volume = model_object->volumes[volume_id]; @@ -989,11 +989,11 @@ void Print::_make_skirt() coordf_t skirt_height_z = 0.; for (const PrintObject *object : m_objects) { size_t skirt_layers = this->has_infinite_skirt() ? - object->layer_count() : + object->layer_count() : std::min(size_t(m_config.skirt_height.value), object->layer_count()); skirt_height_z = std::max(skirt_height_z, object->m_layers[skirt_layers-1]->print_z); } - + // Collect points from all layers contained in skirt height. Points points; for (const PrintObject *object : m_objects) { @@ -1031,10 +1031,10 @@ void Print::_make_skirt() if (points.size() < 3) // At least three points required for a convex hull. return; - + this->throw_if_canceled(); Polygon convex_hull = Slic3r::Geometry::convex_hull(points); - + // Skirt may be printed on several layers, having distinct layer heights, // but loops must be aligned so can't vary width/spacing // TODO: use each extruder's own flow @@ -1042,7 +1042,7 @@ void Print::_make_skirt() Flow flow = this->skirt_flow(); float spacing = flow.spacing(); double mm3_per_mm = flow.mm3_per_mm(); - + std::vector extruders; std::vector extruders_e_per_mm; { @@ -1336,9 +1336,9 @@ void Print::alert_when_supports_needed() // Wipe tower support. bool Print::has_wipe_tower() const { - return + return ! m_config.spiral_vase.value && - m_config.wipe_tower.value && + m_config.wipe_tower.value && m_config.nozzle_diameter.values.size() > 1; } @@ -1498,8 +1498,8 @@ void Print::_make_wipe_tower() // Generate a recommended G-code output file name based on the format template, default extension, and template parameters // (timestamps, object placeholders derived from the model, current placeholder prameters and print statistics. // Use the final print statistics if available, or just keep the print statistics placeholders if not available yet (before G-code is finalized). -std::string Print::output_filename(const std::string &filename_base) const -{ +std::string Print::output_filename(const std::string &filename_base) const +{ // Set the placeholders for the data know first after the G-code export is finished. // These values will be just propagated into the output file name. DynamicConfig config = this->finished() ? this->print_statistics().config() : this->print_statistics().placeholders(); @@ -1528,16 +1528,16 @@ DynamicConfig PrintStatistics::config() const config.set_key_value("printing_filament_types", new ConfigOptionString(this->printing_filament_types)); config.set_key_value("num_printing_extruders", new ConfigOptionInt(int(this->printing_extruders.size()))); // config.set_key_value("printing_extruders", new ConfigOptionInts(std::vector(this->printing_extruders.begin(), this->printing_extruders.end()))); - + return config; } DynamicConfig PrintStatistics::placeholders() { DynamicConfig config; - for (const std::string &key : { - "print_time", "normal_print_time", "silent_print_time", - "used_filament", "extruded_volume", "total_cost", "total_weight", + for (const std::string &key : { + "print_time", "normal_print_time", "silent_print_time", + "used_filament", "extruded_volume", "total_cost", "total_weight", "total_toolchanges", "total_wipe_tower_cost", "total_wipe_tower_filament", "initial_tool", "initial_extruder", "initial_filament_type", "printing_filament_types", "num_printing_extruders" }) config.set_key_value(key, new ConfigOptionString(std::string("{") + key + "}")); diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index b5fb78d8a3a..b0efcc5750b 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -105,7 +105,7 @@ class PrintRegion public: void set_config(const PrintRegionConfig &config) { m_config = config; m_config_hash = m_config.hash(); } void set_config(PrintRegionConfig &&config) { m_config = std::move(config); m_config_hash = m_config.hash(); } - void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) + void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) { m_config.apply_only(other, keys, ignore_nonexistent); m_config_hash = m_config.hash(); } private: friend Print; @@ -243,12 +243,12 @@ class PrintObject : public PrintObjectBaseWithState(const_cast(m_layers.data()), m_layers.size()); } auto support_layers() const { return SpanOfConstPtrs(const_cast(m_support_layers.data()), m_support_layers.size()); } const Transform3d& trafo() const { return m_trafo; } // Trafo with the center_offset() applied after the transformation, to center the object in XY before slicing. - Transform3d trafo_centered() const + Transform3d trafo_centered() const { Transform3d t = this->trafo(); t.pretranslate(Vec3d(- unscale(m_center_offset.x()), - unscale(m_center_offset.y()), 0)); return t; } const PrintInstances& instances() const { return m_instances; } @@ -297,7 +297,7 @@ class PrintObject : public PrintObjectBaseWithState &layer_height_profile); @@ -380,7 +380,7 @@ class PrintObject : public PrintObjectBaseWithState // methods for handling state bool is_step_done(PrintStep step) const { return Inherited::is_step_done(step); } - // Returns true if an object step is done on all objects and there's at least one object. + // Returns true if an object step is done on all objects and there's at least one object. bool is_step_done(PrintObjectStep step) const; // Returns true if the last step was finished with success. bool finished() const override { return this->is_step_done(psGCodeExport); } @@ -553,7 +553,7 @@ class Print : public PrintBaseWithState double skirt_first_layer_height() const; Flow brim_flow() const; Flow skirt_flow() const; - + std::vector object_extruders() const; std::vector support_material_extruders() const; std::vector extruders() const; @@ -575,8 +575,8 @@ class Print : public PrintBaseWithState } // PrintObject by its ObjectID, to be used to uniquely bind slicing warnings to their source PrintObjects // in the notification center. - const PrintObject* get_object(ObjectID object_id) const { - auto it = std::find_if(m_objects.begin(), m_objects.end(), + const PrintObject* get_object(ObjectID object_id) const { + auto it = std::find_if(m_objects.begin(), m_objects.end(), [object_id](const PrintObject *obj) { return obj->id() == object_id; }); return (it == m_objects.end()) ? nullptr : *it; } @@ -584,7 +584,7 @@ class Print : public PrintBaseWithState // If zero, then the print is empty and the print shall not be executed. unsigned int num_object_instances() const; - // For Perl bindings. + // For Perl bindings. PrintObjectPtrs& objects_mutable() { return m_objects; } PrintRegionPtrs& print_regions_mutable() { return m_print_regions; } diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 4058c4d7cfd..8e22bfb484e 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -327,7 +327,7 @@ void PrintConfigDef::init_common_params() def->mode = comAdvanced; def->cli = ConfigOptionDef::nocli; def->set_default_value(new ConfigOptionString("")); - + def = this->add("printhost_port", coString); def->label = L("Printer"); def->tooltip = L("Name of the printer"); @@ -335,7 +335,7 @@ void PrintConfigDef::init_common_params() def->mode = comAdvanced; def->cli = ConfigOptionDef::nocli; def->set_default_value(new ConfigOptionString("")); - + def = this->add("printhost_cafile", coString); def->label = L("HTTPS CA File"); def->tooltip = L("Custom CA certificate file can be specified for HTTPS OctoPrint connections, in crt/pem format. " @@ -343,16 +343,16 @@ void PrintConfigDef::init_common_params() def->mode = comAdvanced; def->cli = ConfigOptionDef::nocli; def->set_default_value(new ConfigOptionString("")); - + // Options used by physical printers - + def = this->add("printhost_user", coString); def->label = L("User"); // def->tooltip = L(""); def->mode = comAdvanced; def->cli = ConfigOptionDef::nocli; def->set_default_value(new ConfigOptionString("")); - + def = this->add("printhost_password", coString); def->label = L("Password"); // def->tooltip = L(""); @@ -368,7 +368,7 @@ void PrintConfigDef::init_common_params() def->mode = comAdvanced; def->cli = ConfigOptionDef::nocli; def->set_default_value(new ConfigOptionBool(false)); - + def = this->add("preset_names", coStrings); def->label = L("Printer preset names"); def->tooltip = L("Names of presets related to the physical printer"); @@ -3718,7 +3718,7 @@ void PrintConfigDef::init_sla_params() "to the sign of the correction."); def->mode = comExpert; def->set_default_value(new ConfigOptionFloat(0.0)); - + def = this->add("elefant_foot_min_width", coFloat); def->label = L("Elephant foot minimum width"); def->category = L("Advanced"); @@ -3889,8 +3889,6 @@ void PrintConfigDef::init_sla_params() def->multiline = true; def->full_width = true; def->height = 13; - // TODO currently notes are the only way to pass data - // for non-PrusaResearch printers. We therefore need to always show them def->mode = comSimple; def->set_default_value(new ConfigOptionString("")); @@ -3995,7 +3993,7 @@ void PrintConfigDef::init_sla_params() def->max = 30; def->mode = comExpert; def->set_default_value(new ConfigOptionFloat(0.)); - + def = this->add("pad_brim_size", coFloat); def->label = L("Pad brim size"); def->tooltip = L("How far should the pad extend around the contained geometry"); @@ -4046,7 +4044,7 @@ void PrintConfigDef::init_sla_params() def->tooltip = L("Create pad around object and ignore the support elevation"); def->mode = comSimple; def->set_default_value(new ConfigOptionBool(false)); - + def = this->add("pad_around_object_everywhere", coBool); def->label = L("Pad around object everywhere"); def->category = L("Pad"); @@ -4092,14 +4090,14 @@ void PrintConfigDef::init_sla_params() def->min = 0; def->mode = comExpert; def->set_default_value(new ConfigOptionFloat(0.3)); - + def = this->add("hollowing_enable", coBool); def->label = L("Enable hollowing"); def->category = L("Hollowing"); def->tooltip = L("Hollow out a model to have an empty interior"); def->mode = comSimple; def->set_default_value(new ConfigOptionBool(false)); - + def = this->add("hollowing_min_thickness", coFloat); def->label = L("Wall thickness"); def->category = L("Hollowing"); @@ -4109,7 +4107,7 @@ void PrintConfigDef::init_sla_params() def->max = 10; def->mode = comSimple; def->set_default_value(new ConfigOptionFloat(3.)); - + def = this->add("hollowing_quality", coFloat); def->label = L("Accuracy"); def->category = L("Hollowing"); @@ -4118,7 +4116,7 @@ void PrintConfigDef::init_sla_params() def->max = 1; def->mode = comExpert; def->set_default_value(new ConfigOptionFloat(0.5)); - + def = this->add("hollowing_closing_distance", coFloat); def->label = L("Closing distance"); def->category = L("Hollowing"); @@ -4288,7 +4286,7 @@ DynamicPrintConfig* DynamicPrintConfig::new_from_defaults_keys(const std::vector } double min_object_distance(const ConfigBase &cfg) -{ +{ const ConfigOptionEnum *opt_printer_technology = cfg.option>("printer_technology"); auto printer_technology = opt_printer_technology ? opt_printer_technology->value : ptUnknown; @@ -4301,7 +4299,7 @@ double min_object_distance(const ConfigBase &cfg) auto dd_opt = cfg.option("duplicate_distance"); auto co_opt = cfg.option("complete_objects"); - if (!ecr_opt || !dd_opt || !co_opt) + if (!ecr_opt || !dd_opt || !co_opt) ret = 0.; else { // min object distance is max(duplicate_distance, clearance_radius) @@ -4632,7 +4630,7 @@ std::string validate(const FullPrintConfig &cfg) return 1; \ } PRINT_CONFIG_CACHE_INITIALIZE(( - PrintObjectConfig, PrintRegionConfig, MachineEnvelopeConfig, GCodeConfig, PrintConfig, FullPrintConfig, + PrintObjectConfig, PrintRegionConfig, MachineEnvelopeConfig, GCodeConfig, PrintConfig, FullPrintConfig, SLAMaterialConfig, SLAPrintConfig, SLAPrintObjectConfig, SLAPrinterConfig, SLAFullPrintConfig)) static int print_config_static_initialized = print_config_static_initializer(); @@ -4906,22 +4904,22 @@ static Points to_points(const std::vector &dpts) Points pts; pts.reserve(dpts.size()); for (auto &v : dpts) pts.emplace_back( coord_t(scale_(v.x())), coord_t(scale_(v.y())) ); - return pts; + return pts; } Points get_bed_shape(const DynamicPrintConfig &config) { const auto *bed_shape_opt = config.opt("bed_shape"); if (!bed_shape_opt) { - + // Here, it is certain that the bed shape is missing, so an infinite one // has to be used, but still, the center of bed can be queried if (auto center_opt = config.opt("center")) return { scaled(center_opt->value) }; - + return {}; } - + return to_points(bed_shape_opt->values); } diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 994907594d7..dc8a73d4463 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -33,14 +33,14 @@ namespace Slic3r { enum GCodeFlavor : unsigned char { gcfRepRapSprinter, gcfRepRapFirmware, gcfRepetier, gcfTeacup, gcfMakerWare, gcfMarlinLegacy, gcfMarlinFirmware, gcfKlipper, gcfSailfish, gcfMach3, gcfMachinekit, - gcfSmoothie, gcfNoExtrusion, + gcfSmoothie, gcfNoExtrusion, }; enum class MachineLimitsUsage { - EmitToGCode, - TimeEstimateOnly, - Ignore, - Count, + EmitToGCode, + TimeEstimateOnly, + Ignore, + Count, }; enum PrintHostType { @@ -48,42 +48,42 @@ enum PrintHostType { }; enum AuthorizationType { - atKeyPassword, atUserPassword + atKeyPassword, atUserPassword }; enum class FuzzySkinType { - None, - External, - All, + None, + External, + All, }; enum InfillPattern : int { ipRectilinear, ipMonotonic, ipMonotonicLines, ipAlignedRectilinear, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb, - ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipAdaptiveCubic, ipSupportCubic, ipSupportBase, - ipLightning, + ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipAdaptiveCubic, ipSupportCubic, ipSupportBase, + ipLightning, ipEnsuring, ipCount, }; enum class IroningType { - TopSurfaces, - TopmostOnly, - AllSolid, - Count, + TopSurfaces, + TopmostOnly, + AllSolid, + Count, }; enum class SlicingMode { - // Regular, applying ClipperLib::pftNonZero rule when creating ExPolygons. - Regular, - // Compatible with 3DLabPrint models, applying ClipperLib::pftEvenOdd rule when creating ExPolygons. - EvenOdd, - // Orienting all contours CCW, thus closing all holes. - CloseHoles, + // Regular, applying ClipperLib::pftNonZero rule when creating ExPolygons. + Regular, + // Compatible with 3DLabPrint models, applying ClipperLib::pftEvenOdd rule when creating ExPolygons. + EvenOdd, + // Orienting all contours CCW, thus closing all holes. + CloseHoles, }; enum SupportMaterialPattern { - smpRectilinear, smpRectilinearGrid, smpHoneycomb, + smpRectilinear, smpRectilinearGrid, smpHoneycomb, }; enum SupportMaterialStyle { @@ -91,38 +91,38 @@ enum SupportMaterialStyle { }; enum SupportMaterialInterfacePattern { - smipAuto, smipRectilinear, smipConcentric, + smipAuto, smipRectilinear, smipConcentric, }; enum SeamPosition { - spRandom, spNearest, spAligned, spRear + spRandom, spNearest, spAligned, spRear }; enum SLAMaterial { - slamTough, - slamFlex, - slamCasting, - slamDental, - slamHeatResistant, + slamTough, + slamFlex, + slamCasting, + slamDental, + slamHeatResistant, }; enum SLADisplayOrientation { - sladoLandscape, - sladoPortrait + sladoLandscape, + sladoPortrait }; using SLASupportTreeType = sla::SupportTreeType; using SLAPillarConnectionMode = sla::PillarConnectionMode; enum BrimType { - btNoBrim, - btOuterOnly, - btInnerOnly, - btOuterAndInner, + btNoBrim, + btOuterOnly, + btInnerOnly, + btOuterAndInner, }; enum DraftShield { - dsDisabled, dsLimited, dsEnabled + dsDisabled, dsLimited, dsEnabled }; enum class PerimeterGeneratorType @@ -135,12 +135,12 @@ enum class PerimeterGeneratorType }; enum class GCodeThumbnailsFormat { - PNG, JPG, QOI + PNG, JPG, QOI }; #define CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(NAME) \ - template<> const t_config_enum_names& ConfigOptionEnum::get_enum_names(); \ - template<> const t_config_enum_values& ConfigOptionEnum::get_enum_values(); + 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(PrinterTechnology) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(GCodeFlavor) @@ -172,26 +172,26 @@ CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(PerimeterGeneratorType) class PrintConfigDef : public ConfigDef { public: - PrintConfigDef(); + PrintConfigDef(); - static void handle_legacy(t_config_option_key &opt_key, std::string &value); + static void handle_legacy(t_config_option_key &opt_key, std::string &value); - // Array options growing with the number of extruders - const std::vector& extruder_option_keys() const { return m_extruder_option_keys; } - // Options defining the extruder retract properties. These keys are sorted lexicographically. - // The extruder retract keys could be overidden by the same values defined at the Filament level - // (then the key is further prefixed with the "filament_" prefix). - const std::vector& extruder_retract_keys() const { return m_extruder_retract_keys; } + // Array options growing with the number of extruders + const std::vector& extruder_option_keys() const { return m_extruder_option_keys; } + // Options defining the extruder retract properties. These keys are sorted lexicographically. + // The extruder retract keys could be overidden by the same values defined at the Filament level + // (then the key is further prefixed with the "filament_" prefix). + const std::vector& extruder_retract_keys() const { return m_extruder_retract_keys; } private: - void init_common_params(); - void init_fff_params(); - void init_extruder_option_keys(); - void init_sla_params(); + void init_common_params(); + void init_fff_params(); + void init_extruder_option_keys(); + void init_sla_params(); void init_sla_support_params(const std::string &method_prefix); - std::vector m_extruder_option_keys; - std::vector m_extruder_retract_keys; + std::vector m_extruder_option_keys; + std::vector m_extruder_retract_keys; }; // The one and only global definition of SLic3r configuration options. @@ -211,54 +211,54 @@ double min_object_distance(const ConfigBase &cfg); class DynamicPrintConfig : public DynamicConfig { public: - DynamicPrintConfig() {} - DynamicPrintConfig(const DynamicPrintConfig &rhs) : DynamicConfig(rhs) {} - DynamicPrintConfig(DynamicPrintConfig &&rhs) noexcept : DynamicConfig(std::move(rhs)) {} - explicit DynamicPrintConfig(const StaticPrintConfig &rhs); - explicit DynamicPrintConfig(const ConfigBase &rhs) : DynamicConfig(rhs) {} - - DynamicPrintConfig& operator=(const DynamicPrintConfig &rhs) { DynamicConfig::operator=(rhs); return *this; } - DynamicPrintConfig& operator=(DynamicPrintConfig &&rhs) noexcept { DynamicConfig::operator=(std::move(rhs)); return *this; } - - static DynamicPrintConfig full_print_config(); - static DynamicPrintConfig full_print_config_with(const t_config_option_key &opt_key, const std::string &str, bool append = false) { - auto config = DynamicPrintConfig::full_print_config(); - config.set_deserialize_strict(opt_key, str, append); - return config; - } - static DynamicPrintConfig full_print_config_with(std::initializer_list items) { - auto config = DynamicPrintConfig::full_print_config(); - config.set_deserialize_strict(items); - return config; - } - static DynamicPrintConfig new_with(const t_config_option_key &opt_key, const std::string &str, bool append = false) { - DynamicPrintConfig config; - config.set_deserialize_strict(opt_key, str, append); - return config; - } - static DynamicPrintConfig new_with(std::initializer_list items) { - DynamicPrintConfig config; - config.set_deserialize_strict(items); - return config; - } - static DynamicPrintConfig* new_from_defaults_keys(const std::vector &keys); - - // Overrides ConfigBase::def(). Static configuration definition. Any value stored into this ConfigBase shall have its definition here. - const ConfigDef* def() const override { return &print_config_def; } - - void normalize_fdm(); - - void set_num_extruders(unsigned int num_extruders); - - // Validate the PrintConfig. Returns an empty string on success, otherwise an error message is returned. - std::string validate(); - - // Verify whether the opt_key has not been obsoleted or renamed. - // Both opt_key and value may be modified by handle_legacy(). - // If the opt_key is no more valid in this version of Slic3r, opt_key is cleared by handle_legacy(). - // handle_legacy() is called internally by set_deserialize(). - void handle_legacy(t_config_option_key &opt_key, std::string &value) const override - { PrintConfigDef::handle_legacy(opt_key, value); } + DynamicPrintConfig() {} + DynamicPrintConfig(const DynamicPrintConfig &rhs) : DynamicConfig(rhs) {} + DynamicPrintConfig(DynamicPrintConfig &&rhs) noexcept : DynamicConfig(std::move(rhs)) {} + explicit DynamicPrintConfig(const StaticPrintConfig &rhs); + explicit DynamicPrintConfig(const ConfigBase &rhs) : DynamicConfig(rhs) {} + + DynamicPrintConfig& operator=(const DynamicPrintConfig &rhs) { DynamicConfig::operator=(rhs); return *this; } + DynamicPrintConfig& operator=(DynamicPrintConfig &&rhs) noexcept { DynamicConfig::operator=(std::move(rhs)); return *this; } + + static DynamicPrintConfig full_print_config(); + static DynamicPrintConfig full_print_config_with(const t_config_option_key &opt_key, const std::string &str, bool append = false) { + auto config = DynamicPrintConfig::full_print_config(); + config.set_deserialize_strict(opt_key, str, append); + return config; + } + static DynamicPrintConfig full_print_config_with(std::initializer_list items) { + auto config = DynamicPrintConfig::full_print_config(); + config.set_deserialize_strict(items); + return config; + } + static DynamicPrintConfig new_with(const t_config_option_key &opt_key, const std::string &str, bool append = false) { + DynamicPrintConfig config; + config.set_deserialize_strict(opt_key, str, append); + return config; + } + static DynamicPrintConfig new_with(std::initializer_list items) { + DynamicPrintConfig config; + config.set_deserialize_strict(items); + return config; + } + static DynamicPrintConfig* new_from_defaults_keys(const std::vector &keys); + + // Overrides ConfigBase::def(). Static configuration definition. Any value stored into this ConfigBase shall have its definition here. + const ConfigDef* def() const override { return &print_config_def; } + + void normalize_fdm(); + + void set_num_extruders(unsigned int num_extruders); + + // Validate the PrintConfig. Returns an empty string on success, otherwise an error message is returned. + std::string validate(); + + // Verify whether the opt_key has not been obsoleted or renamed. + // Both opt_key and value may be modified by handle_legacy(). + // If the opt_key is no more valid in this version of Slic3r, opt_key is cleared by handle_legacy(). + // handle_legacy() is called internally by set_deserialize(). + void handle_legacy(t_config_option_key &opt_key, std::string &value) const override + { PrintConfigDef::handle_legacy(opt_key, value); } }; void handle_legacy_sla(DynamicPrintConfig &config); @@ -266,136 +266,136 @@ void handle_legacy_sla(DynamicPrintConfig &config); class StaticPrintConfig : public StaticConfig { public: - StaticPrintConfig() {} + StaticPrintConfig() {} - // Overrides ConfigBase::def(). Static configuration definition. Any value stored into this ConfigBase shall have its definition here. - const ConfigDef* def() const override { return &print_config_def; } - // Reference to the cached list of keys. - virtual const t_config_option_keys& keys_ref() const = 0; + // Overrides ConfigBase::def(). Static configuration definition. Any value stored into this ConfigBase shall have its definition here. + const ConfigDef* def() const override { return &print_config_def; } + // Reference to the cached list of keys. + virtual const t_config_option_keys& keys_ref() const = 0; protected: - // Verify whether the opt_key has not been obsoleted or renamed. - // Both opt_key and value may be modified by handle_legacy(). - // If the opt_key is no more valid in this version of Slic3r, opt_key is cleared by handle_legacy(). - // handle_legacy() is called internally by set_deserialize(). - void handle_legacy(t_config_option_key &opt_key, std::string &value) const override - { PrintConfigDef::handle_legacy(opt_key, value); } - - // Internal class for keeping a dynamic map to static options. - class StaticCacheBase - { - public: - // To be called during the StaticCache setup. - // Add one ConfigOption into m_map_name_to_offset. - template - void opt_add(const std::string &name, const char *base_ptr, const T &opt) - { - assert(m_map_name_to_offset.find(name) == m_map_name_to_offset.end()); - m_map_name_to_offset[name] = (const char*)&opt - base_ptr; - } - - protected: - std::map m_map_name_to_offset; - }; - - // Parametrized by the type of the topmost class owning the options. - template - class StaticCache : public StaticCacheBase - { - public: - // Calling the constructor of m_defaults with 0 forces m_defaults to not run the initialization. - StaticCache() : m_defaults(nullptr) {} - ~StaticCache() { delete m_defaults; m_defaults = nullptr; } - - bool initialized() const { return ! m_keys.empty(); } - - ConfigOption* optptr(const std::string &name, T *owner) const - { - const auto it = m_map_name_to_offset.find(name); - return (it == m_map_name_to_offset.end()) ? nullptr : reinterpret_cast((char*)owner + it->second); - } - - const ConfigOption* optptr(const std::string &name, const T *owner) const - { - const auto it = m_map_name_to_offset.find(name); - return (it == m_map_name_to_offset.end()) ? nullptr : reinterpret_cast((const char*)owner + it->second); - } - - const std::vector& keys() const { return m_keys; } - const T& defaults() const { return *m_defaults; } - - // To be called during the StaticCache setup. - // Collect option keys from m_map_name_to_offset, - // assign default values to m_defaults. - void finalize(T *defaults, const ConfigDef *defs) - { - assert(defs != nullptr); - m_defaults = defaults; - m_keys.clear(); - m_keys.reserve(m_map_name_to_offset.size()); - for (const auto &kvp : defs->options) { - // Find the option given the option name kvp.first by an offset from (char*)m_defaults. - ConfigOption *opt = this->optptr(kvp.first, m_defaults); - if (opt == nullptr) - // This option is not defined by the ConfigBase of type T. - continue; - m_keys.emplace_back(kvp.first); - const ConfigOptionDef *def = defs->get(kvp.first); - assert(def != nullptr); - if (def->default_value) - opt->set(def->default_value.get()); - } - } - - private: - T *m_defaults; - std::vector m_keys; - }; + // Verify whether the opt_key has not been obsoleted or renamed. + // Both opt_key and value may be modified by handle_legacy(). + // If the opt_key is no more valid in this version of Slic3r, opt_key is cleared by handle_legacy(). + // handle_legacy() is called internally by set_deserialize(). + void handle_legacy(t_config_option_key &opt_key, std::string &value) const override + { PrintConfigDef::handle_legacy(opt_key, value); } + + // Internal class for keeping a dynamic map to static options. + class StaticCacheBase + { + public: + // To be called during the StaticCache setup. + // Add one ConfigOption into m_map_name_to_offset. + template + void opt_add(const std::string &name, const char *base_ptr, const T &opt) + { + assert(m_map_name_to_offset.find(name) == m_map_name_to_offset.end()); + m_map_name_to_offset[name] = (const char*)&opt - base_ptr; + } + + protected: + std::map m_map_name_to_offset; + }; + + // Parametrized by the type of the topmost class owning the options. + template + class StaticCache : public StaticCacheBase + { + public: + // Calling the constructor of m_defaults with 0 forces m_defaults to not run the initialization. + StaticCache() : m_defaults(nullptr) {} + ~StaticCache() { delete m_defaults; m_defaults = nullptr; } + + bool initialized() const { return ! m_keys.empty(); } + + ConfigOption* optptr(const std::string &name, T *owner) const + { + const auto it = m_map_name_to_offset.find(name); + return (it == m_map_name_to_offset.end()) ? nullptr : reinterpret_cast((char*)owner + it->second); + } + + const ConfigOption* optptr(const std::string &name, const T *owner) const + { + const auto it = m_map_name_to_offset.find(name); + return (it == m_map_name_to_offset.end()) ? nullptr : reinterpret_cast((const char*)owner + it->second); + } + + const std::vector& keys() const { return m_keys; } + const T& defaults() const { return *m_defaults; } + + // To be called during the StaticCache setup. + // Collect option keys from m_map_name_to_offset, + // assign default values to m_defaults. + void finalize(T *defaults, const ConfigDef *defs) + { + assert(defs != nullptr); + m_defaults = defaults; + m_keys.clear(); + m_keys.reserve(m_map_name_to_offset.size()); + for (const auto &kvp : defs->options) { + // Find the option given the option name kvp.first by an offset from (char*)m_defaults. + ConfigOption *opt = this->optptr(kvp.first, m_defaults); + if (opt == nullptr) + // This option is not defined by the ConfigBase of type T. + continue; + m_keys.emplace_back(kvp.first); + const ConfigOptionDef *def = defs->get(kvp.first); + assert(def != nullptr); + if (def->default_value) + opt->set(def->default_value.get()); + } + } + + private: + T *m_defaults; + std::vector m_keys; + }; }; #define STATIC_PRINT_CONFIG_CACHE_BASE(CLASS_NAME) \ public: \ - /* Overrides ConfigBase::optptr(). Find ando/or create a ConfigOption instance for a given name. */ \ - const ConfigOption* optptr(const t_config_option_key &opt_key) const override \ - { return s_cache_##CLASS_NAME.optptr(opt_key, this); } \ - /* Overrides ConfigBase::optptr(). Find ando/or create a ConfigOption instance for a given name. */ \ - ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) override \ - { return s_cache_##CLASS_NAME.optptr(opt_key, this); } \ - /* Overrides ConfigBase::keys(). Collect names of all configuration values maintained by this configuration store. */ \ - t_config_option_keys keys() const override { return s_cache_##CLASS_NAME.keys(); } \ - const t_config_option_keys& keys_ref() const override { return s_cache_##CLASS_NAME.keys(); } \ - static const CLASS_NAME& defaults() { assert(s_cache_##CLASS_NAME.initialized()); return s_cache_##CLASS_NAME.defaults(); } \ + /* Overrides ConfigBase::optptr(). Find ando/or create a ConfigOption instance for a given name. */ \ + const ConfigOption* optptr(const t_config_option_key &opt_key) const override \ + { return s_cache_##CLASS_NAME.optptr(opt_key, this); } \ + /* Overrides ConfigBase::optptr(). Find ando/or create a ConfigOption instance for a given name. */ \ + ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) override \ + { return s_cache_##CLASS_NAME.optptr(opt_key, this); } \ + /* Overrides ConfigBase::keys(). Collect names of all configuration values maintained by this configuration store. */ \ + t_config_option_keys keys() const override { return s_cache_##CLASS_NAME.keys(); } \ + const t_config_option_keys& keys_ref() const override { return s_cache_##CLASS_NAME.keys(); } \ + static const CLASS_NAME& defaults() { assert(s_cache_##CLASS_NAME.initialized()); return s_cache_##CLASS_NAME.defaults(); } \ private: \ - friend int print_config_static_initializer(); \ - static void initialize_cache() \ - { \ - assert(! s_cache_##CLASS_NAME.initialized()); \ - if (! s_cache_##CLASS_NAME.initialized()) { \ - CLASS_NAME *inst = new CLASS_NAME(1); \ - inst->initialize(s_cache_##CLASS_NAME, (const char*)inst); \ - s_cache_##CLASS_NAME.finalize(inst, inst->def()); \ - } \ - } \ - /* Cache object holding a key/option map, a list of option keys and a copy of this static config initialized with the defaults. */ \ - static StaticPrintConfig::StaticCache s_cache_##CLASS_NAME; + friend int print_config_static_initializer(); \ + static void initialize_cache() \ + { \ + assert(! s_cache_##CLASS_NAME.initialized()); \ + if (! s_cache_##CLASS_NAME.initialized()) { \ + CLASS_NAME *inst = new CLASS_NAME(1); \ + inst->initialize(s_cache_##CLASS_NAME, (const char*)inst); \ + s_cache_##CLASS_NAME.finalize(inst, inst->def()); \ + } \ + } \ + /* Cache object holding a key/option map, a list of option keys and a copy of this static config initialized with the defaults. */ \ + static StaticPrintConfig::StaticCache s_cache_##CLASS_NAME; #define STATIC_PRINT_CONFIG_CACHE(CLASS_NAME) \ - STATIC_PRINT_CONFIG_CACHE_BASE(CLASS_NAME) \ + STATIC_PRINT_CONFIG_CACHE_BASE(CLASS_NAME) \ public: \ - /* Public default constructor will initialize the key/option cache and the default object copy if needed. */ \ - CLASS_NAME() { assert(s_cache_##CLASS_NAME.initialized()); *this = s_cache_##CLASS_NAME.defaults(); } \ + /* Public default constructor will initialize the key/option cache and the default object copy if needed. */ \ + CLASS_NAME() { assert(s_cache_##CLASS_NAME.initialized()); *this = s_cache_##CLASS_NAME.defaults(); } \ protected: \ - /* Protected constructor to be called when compounded. */ \ - CLASS_NAME(int) {} + /* Protected constructor to be called when compounded. */ \ + CLASS_NAME(int) {} #define STATIC_PRINT_CONFIG_CACHE_DERIVED(CLASS_NAME) \ - STATIC_PRINT_CONFIG_CACHE_BASE(CLASS_NAME) \ + STATIC_PRINT_CONFIG_CACHE_BASE(CLASS_NAME) \ public: \ - /* Overrides ConfigBase::def(). Static configuration definition. Any value stored into this ConfigBase shall have its definition here. */ \ - const ConfigDef* def() const override { return &print_config_def; } \ - /* Handle legacy and obsoleted config keys */ \ - void handle_legacy(t_config_option_key &opt_key, std::string &value) const override \ - { PrintConfigDef::handle_legacy(opt_key, value); } + /* Overrides ConfigBase::def(). Static configuration definition. Any value stored into this ConfigBase shall have its definition here. */ \ + const ConfigDef* def() const override { return &print_config_def; } \ + /* Handle legacy and obsoleted config keys */ \ + void handle_legacy(t_config_option_key &opt_key, std::string &value) const override \ + { PrintConfigDef::handle_legacy(opt_key, value); } #define PRINT_CONFIG_CLASS_ELEMENT_DEFINITION(r, data, elem) BOOST_PP_TUPLE_ELEM(0, elem) BOOST_PP_TUPLE_ELEM(1, elem); #define PRINT_CONFIG_CLASS_ELEMENT_INITIALIZATION2(KEY) cache.opt_add(BOOST_PP_STRINGIZE(KEY), base_ptr, this->KEY); @@ -403,36 +403,36 @@ public: \ #define PRINT_CONFIG_CLASS_ELEMENT_HASH(r, data, elem) boost::hash_combine(seed, BOOST_PP_TUPLE_ELEM(1, elem).hash()); #define PRINT_CONFIG_CLASS_ELEMENT_EQUAL(r, data, elem) if (! (BOOST_PP_TUPLE_ELEM(1, elem) == rhs.BOOST_PP_TUPLE_ELEM(1, elem))) return false; #define PRINT_CONFIG_CLASS_ELEMENT_LOWER(r, data, elem) \ - if (BOOST_PP_TUPLE_ELEM(1, elem) < rhs.BOOST_PP_TUPLE_ELEM(1, elem)) return true; \ - if (! (BOOST_PP_TUPLE_ELEM(1, elem) == rhs.BOOST_PP_TUPLE_ELEM(1, elem))) return false; + if (BOOST_PP_TUPLE_ELEM(1, elem) < rhs.BOOST_PP_TUPLE_ELEM(1, elem)) return true; \ + if (! (BOOST_PP_TUPLE_ELEM(1, elem) == rhs.BOOST_PP_TUPLE_ELEM(1, elem))) return false; #define PRINT_CONFIG_CLASS_DEFINE(CLASS_NAME, PARAMETER_DEFINITION_SEQ) \ class CLASS_NAME : public StaticPrintConfig { \ - STATIC_PRINT_CONFIG_CACHE(CLASS_NAME) \ + STATIC_PRINT_CONFIG_CACHE(CLASS_NAME) \ public: \ - BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_ELEMENT_DEFINITION, _, PARAMETER_DEFINITION_SEQ) \ - size_t hash() const throw() \ - { \ - size_t seed = 0; \ - BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_ELEMENT_HASH, _, PARAMETER_DEFINITION_SEQ) \ - return seed; \ - } \ - bool operator==(const CLASS_NAME &rhs) const throw() \ - { \ - BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_ELEMENT_EQUAL, _, PARAMETER_DEFINITION_SEQ) \ - return true; \ - } \ - bool operator!=(const CLASS_NAME &rhs) const throw() { return ! (*this == rhs); } \ - bool operator<(const CLASS_NAME &rhs) const throw() \ - { \ - BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_ELEMENT_LOWER, _, PARAMETER_DEFINITION_SEQ) \ - return false; \ - } \ + BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_ELEMENT_DEFINITION, _, PARAMETER_DEFINITION_SEQ) \ + size_t hash() const throw() \ + { \ + size_t seed = 0; \ + BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_ELEMENT_HASH, _, PARAMETER_DEFINITION_SEQ) \ + return seed; \ + } \ + bool operator==(const CLASS_NAME &rhs) const throw() \ + { \ + BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_ELEMENT_EQUAL, _, PARAMETER_DEFINITION_SEQ) \ + return true; \ + } \ + bool operator!=(const CLASS_NAME &rhs) const throw() { return ! (*this == rhs); } \ + bool operator<(const CLASS_NAME &rhs) const throw() \ + { \ + BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_ELEMENT_LOWER, _, PARAMETER_DEFINITION_SEQ) \ + return false; \ + } \ protected: \ - void initialize(StaticCacheBase &cache, const char *base_ptr) \ - { \ - BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_ELEMENT_INITIALIZATION, _, PARAMETER_DEFINITION_SEQ) \ - } \ + void initialize(StaticCacheBase &cache, const char *base_ptr) \ + { \ + BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_ELEMENT_INITIALIZATION, _, PARAMETER_DEFINITION_SEQ) \ + } \ }; #define PRINT_CONFIG_CLASS_DERIVED_CLASS_LIST_ITEM(r, data, i, elem) BOOST_PP_COMMA_IF(i) public elem @@ -443,74 +443,74 @@ protected: \ #define PRINT_CONFIG_CLASS_DERIVED_INITCACHE(CLASSES_PARENTS_TUPLE) BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_DERIVED_INITCACHE_ITEM, _, BOOST_PP_TUPLE_TO_SEQ(CLASSES_PARENTS_TUPLE)) #define PRINT_CONFIG_CLASS_DERIVED_HASH(r, data, elem) boost::hash_combine(seed, static_cast(this)->hash()); #define PRINT_CONFIG_CLASS_DERIVED_EQUAL(r, data, elem) \ - if (! (*static_cast(this) == static_cast(rhs))) return false; + if (! (*static_cast(this) == static_cast(rhs))) return false; // Generic version, with or without new parameters. Don't use this directly. #define PRINT_CONFIG_CLASS_DERIVED_DEFINE1(CLASS_NAME, CLASSES_PARENTS_TUPLE, PARAMETER_DEFINITION, PARAMETER_REGISTRATION, PARAMETER_HASHES, PARAMETER_EQUALS) \ class CLASS_NAME : PRINT_CONFIG_CLASS_DERIVED_CLASS_LIST(CLASSES_PARENTS_TUPLE) { \ - STATIC_PRINT_CONFIG_CACHE_DERIVED(CLASS_NAME) \ - CLASS_NAME() : PRINT_CONFIG_CLASS_DERIVED_INITIALIZER(CLASSES_PARENTS_TUPLE, 0) { assert(s_cache_##CLASS_NAME.initialized()); *this = s_cache_##CLASS_NAME.defaults(); } \ + STATIC_PRINT_CONFIG_CACHE_DERIVED(CLASS_NAME) \ + CLASS_NAME() : PRINT_CONFIG_CLASS_DERIVED_INITIALIZER(CLASSES_PARENTS_TUPLE, 0) { assert(s_cache_##CLASS_NAME.initialized()); *this = s_cache_##CLASS_NAME.defaults(); } \ public: \ - PARAMETER_DEFINITION \ - size_t hash() const throw() \ - { \ - size_t seed = 0; \ - BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_DERIVED_HASH, _, BOOST_PP_TUPLE_TO_SEQ(CLASSES_PARENTS_TUPLE)) \ - PARAMETER_HASHES \ - return seed; \ - } \ - bool operator==(const CLASS_NAME &rhs) const throw() \ - { \ - BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_DERIVED_EQUAL, _, BOOST_PP_TUPLE_TO_SEQ(CLASSES_PARENTS_TUPLE)) \ - PARAMETER_EQUALS \ - return true; \ - } \ - bool operator!=(const CLASS_NAME &rhs) const throw() { return ! (*this == rhs); } \ + PARAMETER_DEFINITION \ + size_t hash() const throw() \ + { \ + size_t seed = 0; \ + BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_DERIVED_HASH, _, BOOST_PP_TUPLE_TO_SEQ(CLASSES_PARENTS_TUPLE)) \ + PARAMETER_HASHES \ + return seed; \ + } \ + bool operator==(const CLASS_NAME &rhs) const throw() \ + { \ + BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_DERIVED_EQUAL, _, BOOST_PP_TUPLE_TO_SEQ(CLASSES_PARENTS_TUPLE)) \ + PARAMETER_EQUALS \ + return true; \ + } \ + bool operator!=(const CLASS_NAME &rhs) const throw() { return ! (*this == rhs); } \ protected: \ - CLASS_NAME(int) : PRINT_CONFIG_CLASS_DERIVED_INITIALIZER(CLASSES_PARENTS_TUPLE, 1) {} \ - void initialize(StaticCacheBase &cache, const char* base_ptr) { \ - PRINT_CONFIG_CLASS_DERIVED_INITCACHE(CLASSES_PARENTS_TUPLE) \ - PARAMETER_REGISTRATION \ - } \ + CLASS_NAME(int) : PRINT_CONFIG_CLASS_DERIVED_INITIALIZER(CLASSES_PARENTS_TUPLE, 1) {} \ + void initialize(StaticCacheBase &cache, const char* base_ptr) { \ + PRINT_CONFIG_CLASS_DERIVED_INITCACHE(CLASSES_PARENTS_TUPLE) \ + PARAMETER_REGISTRATION \ + } \ }; // Variant without adding new parameters. #define PRINT_CONFIG_CLASS_DERIVED_DEFINE0(CLASS_NAME, CLASSES_PARENTS_TUPLE) \ - PRINT_CONFIG_CLASS_DERIVED_DEFINE1(CLASS_NAME, CLASSES_PARENTS_TUPLE, BOOST_PP_EMPTY(), BOOST_PP_EMPTY(), BOOST_PP_EMPTY(), BOOST_PP_EMPTY()) + PRINT_CONFIG_CLASS_DERIVED_DEFINE1(CLASS_NAME, CLASSES_PARENTS_TUPLE, BOOST_PP_EMPTY(), BOOST_PP_EMPTY(), BOOST_PP_EMPTY(), BOOST_PP_EMPTY()) // Variant with adding new parameters. #define PRINT_CONFIG_CLASS_DERIVED_DEFINE(CLASS_NAME, CLASSES_PARENTS_TUPLE, PARAMETER_DEFINITION_SEQ) \ - PRINT_CONFIG_CLASS_DERIVED_DEFINE1(CLASS_NAME, CLASSES_PARENTS_TUPLE, \ - BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_ELEMENT_DEFINITION, _, PARAMETER_DEFINITION_SEQ), \ - BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_ELEMENT_INITIALIZATION, _, PARAMETER_DEFINITION_SEQ), \ - BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_ELEMENT_HASH, _, PARAMETER_DEFINITION_SEQ), \ - BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_ELEMENT_EQUAL, _, PARAMETER_DEFINITION_SEQ)) + PRINT_CONFIG_CLASS_DERIVED_DEFINE1(CLASS_NAME, CLASSES_PARENTS_TUPLE, \ + BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_ELEMENT_DEFINITION, _, PARAMETER_DEFINITION_SEQ), \ + BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_ELEMENT_INITIALIZATION, _, PARAMETER_DEFINITION_SEQ), \ + BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_ELEMENT_HASH, _, PARAMETER_DEFINITION_SEQ), \ + BOOST_PP_SEQ_FOR_EACH(PRINT_CONFIG_CLASS_ELEMENT_EQUAL, _, PARAMETER_DEFINITION_SEQ)) PRINT_CONFIG_CLASS_DEFINE( - PrintObjectConfig, - - ((ConfigOptionFloat, brim_separation)) - ((ConfigOptionEnum, brim_type)) - ((ConfigOptionFloat, brim_width)) - ((ConfigOptionBool, dont_support_bridges)) - ((ConfigOptionFloat, elefant_foot_compensation)) - ((ConfigOptionFloatOrPercent, extrusion_width)) - ((ConfigOptionFloat, first_layer_acceleration_over_raft)) - ((ConfigOptionFloatOrPercent, first_layer_speed_over_raft)) + PrintObjectConfig, + + ((ConfigOptionFloat, brim_separation)) + ((ConfigOptionEnum, brim_type)) + ((ConfigOptionFloat, brim_width)) + ((ConfigOptionBool, dont_support_bridges)) + ((ConfigOptionFloat, elefant_foot_compensation)) + ((ConfigOptionFloatOrPercent, extrusion_width)) + ((ConfigOptionFloat, first_layer_acceleration_over_raft)) + ((ConfigOptionFloatOrPercent, first_layer_speed_over_raft)) // ((ConfigOptionBool, infill_only_where_needed)) - // Force the generation of solid shells between adjacent materials/volumes. - ((ConfigOptionBool, interface_shells)) - ((ConfigOptionFloat, layer_height)) - ((ConfigOptionFloat, mmu_segmented_region_max_width)) - ((ConfigOptionFloat, raft_contact_distance)) - ((ConfigOptionFloat, raft_expansion)) - ((ConfigOptionPercent, raft_first_layer_density)) - ((ConfigOptionFloat, raft_first_layer_expansion)) - ((ConfigOptionInt, raft_layers)) - ((ConfigOptionEnum, seam_position)) + // Force the generation of solid shells between adjacent materials/volumes. + ((ConfigOptionBool, interface_shells)) + ((ConfigOptionFloat, layer_height)) + ((ConfigOptionFloat, mmu_segmented_region_max_width)) + ((ConfigOptionFloat, raft_contact_distance)) + ((ConfigOptionFloat, raft_expansion)) + ((ConfigOptionPercent, raft_first_layer_density)) + ((ConfigOptionFloat, raft_first_layer_expansion)) + ((ConfigOptionInt, raft_layers)) + ((ConfigOptionEnum, seam_position)) ((ConfigOptionBool, staggered_inner_seams)) // ((ConfigOptionFloat, seam_preferred_direction)) // ((ConfigOptionFloat, seam_preferred_direction_jitter)) - ((ConfigOptionFloat, slice_closing_radius)) - ((ConfigOptionEnum, slicing_mode)) + ((ConfigOptionFloat, slice_closing_radius)) + ((ConfigOptionEnum, slicing_mode)) ((ConfigOptionEnum, perimeter_generator)) ((ConfigOptionFloatOrPercent, wall_transition_length)) ((ConfigOptionFloatOrPercent, wall_transition_filter_deviation)) @@ -518,37 +518,37 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionInt, wall_distribution_count)) ((ConfigOptionFloatOrPercent, min_feature_size)) ((ConfigOptionFloatOrPercent, min_bead_width)) - ((ConfigOptionBool, support_material)) - // Automatic supports (generated based on support_material_threshold). - ((ConfigOptionBool, support_material_auto)) - // Direction of the support pattern (in XY plane).` - ((ConfigOptionFloat, support_material_angle)) - ((ConfigOptionBool, support_material_buildplate_only)) - ((ConfigOptionFloat, support_material_contact_distance)) - ((ConfigOptionFloat, support_material_bottom_contact_distance)) - ((ConfigOptionInt, support_material_enforce_layers)) - ((ConfigOptionInt, support_material_extruder)) - ((ConfigOptionFloatOrPercent, support_material_extrusion_width)) - ((ConfigOptionBool, support_material_interface_contact_loops)) - ((ConfigOptionInt, support_material_interface_extruder)) - ((ConfigOptionInt, support_material_interface_layers)) - ((ConfigOptionInt, support_material_bottom_interface_layers)) - // Spacing between interface lines (the hatching distance). Set zero to get a solid interface. - ((ConfigOptionFloat, support_material_interface_spacing)) - ((ConfigOptionFloatOrPercent, support_material_interface_speed)) - ((ConfigOptionEnum, support_material_pattern)) - ((ConfigOptionEnum, support_material_interface_pattern)) - // Morphological closing of support areas. Only used for "sung" supports. - ((ConfigOptionFloat, support_material_closing_radius)) - // Spacing between support material lines (the hatching distance). - ((ConfigOptionFloat, support_material_spacing)) - ((ConfigOptionFloat, support_material_speed)) - ((ConfigOptionEnum, support_material_style)) - ((ConfigOptionBool, support_material_synchronize_layers)) - // Overhang angle threshold. - ((ConfigOptionInt, support_material_threshold)) - ((ConfigOptionBool, support_material_with_sheath)) - ((ConfigOptionFloatOrPercent, support_material_xy_spacing)) + ((ConfigOptionBool, support_material)) + // Automatic supports (generated based fdm support point generator). + ((ConfigOptionBool, support_material_auto)) + // Direction of the support pattern (in XY plane).` + ((ConfigOptionFloat, support_material_angle)) + ((ConfigOptionBool, support_material_buildplate_only)) + ((ConfigOptionFloat, support_material_contact_distance)) + ((ConfigOptionFloat, support_material_bottom_contact_distance)) + ((ConfigOptionInt, support_material_enforce_layers)) + ((ConfigOptionInt, support_material_extruder)) + ((ConfigOptionFloatOrPercent, support_material_extrusion_width)) + ((ConfigOptionBool, support_material_interface_contact_loops)) + ((ConfigOptionInt, support_material_interface_extruder)) + ((ConfigOptionInt, support_material_interface_layers)) + ((ConfigOptionInt, support_material_bottom_interface_layers)) + // Spacing between interface lines (the hatching distance). Set zero to get a solid interface. + ((ConfigOptionFloat, support_material_interface_spacing)) + ((ConfigOptionFloatOrPercent, support_material_interface_speed)) + ((ConfigOptionEnum, support_material_pattern)) + ((ConfigOptionEnum, support_material_interface_pattern)) + // Morphological closing of support areas. Only used for "sung" supports. + ((ConfigOptionFloat, support_material_closing_radius)) + // Spacing between support material lines (the hatching distance). + ((ConfigOptionFloat, support_material_spacing)) + ((ConfigOptionFloat, support_material_speed)) + ((ConfigOptionEnum, support_material_style)) + ((ConfigOptionBool, support_material_synchronize_layers)) + // Overhang angle threshold. + ((ConfigOptionInt, support_material_threshold)) + ((ConfigOptionBool, support_material_with_sheath)) + ((ConfigOptionFloatOrPercent, support_material_xy_spacing)) // Tree supports ((ConfigOptionFloat, support_tree_angle)) ((ConfigOptionFloat, support_tree_angle_slow)) @@ -559,368 +559,368 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloat, support_tree_branch_distance)) ((ConfigOptionFloat, support_tree_tip_diameter)) // The rest - ((ConfigOptionBool, thick_bridges)) - ((ConfigOptionFloat, xy_size_compensation)) + ((ConfigOptionBool, thick_bridges)) + ((ConfigOptionFloat, xy_size_compensation)) ((ConfigOptionBool, z_dither)) ((ConfigOptionBool, wipe_into_objects)) ) PRINT_CONFIG_CLASS_DEFINE( - PrintRegionConfig, - - ((ConfigOptionFloat, bridge_angle)) - ((ConfigOptionInt, bottom_solid_layers)) - ((ConfigOptionFloat, bottom_solid_min_thickness)) - ((ConfigOptionFloat, bridge_flow_ratio)) - ((ConfigOptionFloat, bridge_speed)) - ((ConfigOptionEnum, top_fill_pattern)) - ((ConfigOptionEnum, bottom_fill_pattern)) - ((ConfigOptionFloatOrPercent, external_perimeter_extrusion_width)) - ((ConfigOptionFloatOrPercent, external_perimeter_speed)) + PrintRegionConfig, + + ((ConfigOptionFloat, bridge_angle)) + ((ConfigOptionInt, bottom_solid_layers)) + ((ConfigOptionFloat, bottom_solid_min_thickness)) + ((ConfigOptionFloat, bridge_flow_ratio)) + ((ConfigOptionFloat, bridge_speed)) + ((ConfigOptionEnum, top_fill_pattern)) + ((ConfigOptionEnum, bottom_fill_pattern)) + ((ConfigOptionFloatOrPercent, external_perimeter_extrusion_width)) + ((ConfigOptionFloatOrPercent, external_perimeter_speed)) ((ConfigOptionBool, enable_dynamic_overhang_speeds)) ((ConfigOptionFloatOrPercent, overhang_speed_0)) ((ConfigOptionFloatOrPercent, overhang_speed_1)) ((ConfigOptionFloatOrPercent, overhang_speed_2)) ((ConfigOptionFloatOrPercent, overhang_speed_3)) - ((ConfigOptionBool, external_perimeters_first)) - ((ConfigOptionBool, extra_perimeters)) + ((ConfigOptionBool, external_perimeters_first)) + ((ConfigOptionBool, extra_perimeters)) ((ConfigOptionBool, extra_perimeters_on_overhangs)) - ((ConfigOptionFloat, fill_angle)) - ((ConfigOptionPercent, fill_density)) - ((ConfigOptionEnum, fill_pattern)) - ((ConfigOptionEnum, fuzzy_skin)) - ((ConfigOptionFloat, fuzzy_skin_thickness)) - ((ConfigOptionFloat, fuzzy_skin_point_dist)) - ((ConfigOptionBool, gap_fill_enabled)) - ((ConfigOptionFloat, gap_fill_speed)) - ((ConfigOptionFloatOrPercent, infill_anchor)) - ((ConfigOptionFloatOrPercent, infill_anchor_max)) - ((ConfigOptionInt, infill_extruder)) - ((ConfigOptionFloatOrPercent, infill_extrusion_width)) - ((ConfigOptionInt, infill_every_layers)) - ((ConfigOptionFloatOrPercent, infill_overlap)) - ((ConfigOptionFloat, infill_speed)) - // Ironing options - ((ConfigOptionBool, ironing)) - ((ConfigOptionEnum, ironing_type)) - ((ConfigOptionPercent, ironing_flowrate)) - ((ConfigOptionFloat, ironing_spacing)) - ((ConfigOptionFloat, ironing_speed)) - // Detect bridging perimeters - ((ConfigOptionBool, overhangs)) - ((ConfigOptionInt, perimeter_extruder)) - ((ConfigOptionFloatOrPercent, perimeter_extrusion_width)) - ((ConfigOptionFloat, perimeter_speed)) - // Total number of perimeters. - ((ConfigOptionInt, perimeters)) - ((ConfigOptionFloatOrPercent, small_perimeter_speed)) - ((ConfigOptionFloat, solid_infill_below_area)) - ((ConfigOptionInt, solid_infill_extruder)) - ((ConfigOptionFloatOrPercent, solid_infill_extrusion_width)) - ((ConfigOptionInt, solid_infill_every_layers)) - ((ConfigOptionFloatOrPercent, solid_infill_speed)) - // Detect thin walls. - ((ConfigOptionBool, thin_walls)) - ((ConfigOptionFloatOrPercent, top_infill_extrusion_width)) - ((ConfigOptionInt, top_solid_layers)) - ((ConfigOptionFloat, top_solid_min_thickness)) - ((ConfigOptionFloatOrPercent, top_solid_infill_speed)) - ((ConfigOptionBool, wipe_into_infill)) + ((ConfigOptionFloat, fill_angle)) + ((ConfigOptionPercent, fill_density)) + ((ConfigOptionEnum, fill_pattern)) + ((ConfigOptionEnum, fuzzy_skin)) + ((ConfigOptionFloat, fuzzy_skin_thickness)) + ((ConfigOptionFloat, fuzzy_skin_point_dist)) + ((ConfigOptionBool, gap_fill_enabled)) + ((ConfigOptionFloat, gap_fill_speed)) + ((ConfigOptionFloatOrPercent, infill_anchor)) + ((ConfigOptionFloatOrPercent, infill_anchor_max)) + ((ConfigOptionInt, infill_extruder)) + ((ConfigOptionFloatOrPercent, infill_extrusion_width)) + ((ConfigOptionInt, infill_every_layers)) + ((ConfigOptionFloatOrPercent, infill_overlap)) + ((ConfigOptionFloat, infill_speed)) + // Ironing options + ((ConfigOptionBool, ironing)) + ((ConfigOptionEnum, ironing_type)) + ((ConfigOptionPercent, ironing_flowrate)) + ((ConfigOptionFloat, ironing_spacing)) + ((ConfigOptionFloat, ironing_speed)) + // Detect bridging perimeters + ((ConfigOptionBool, overhangs)) + ((ConfigOptionInt, perimeter_extruder)) + ((ConfigOptionFloatOrPercent, perimeter_extrusion_width)) + ((ConfigOptionFloat, perimeter_speed)) + // Total number of perimeters. + ((ConfigOptionInt, perimeters)) + ((ConfigOptionFloatOrPercent, small_perimeter_speed)) + ((ConfigOptionFloat, solid_infill_below_area)) + ((ConfigOptionInt, solid_infill_extruder)) + ((ConfigOptionFloatOrPercent, solid_infill_extrusion_width)) + ((ConfigOptionInt, solid_infill_every_layers)) + ((ConfigOptionFloatOrPercent, solid_infill_speed)) + // Detect thin walls. + ((ConfigOptionBool, thin_walls)) + ((ConfigOptionFloatOrPercent, top_infill_extrusion_width)) + ((ConfigOptionInt, top_solid_layers)) + ((ConfigOptionFloat, top_solid_min_thickness)) + ((ConfigOptionFloatOrPercent, top_solid_infill_speed)) + ((ConfigOptionBool, wipe_into_infill)) ) PRINT_CONFIG_CLASS_DEFINE( - MachineEnvelopeConfig, - - // Allowing the machine limits to be completely ignored or used just for time estimator. - ((ConfigOptionEnum, machine_limits_usage)) - // M201 X... Y... Z... E... [mm/sec^2] - ((ConfigOptionFloats, machine_max_acceleration_x)) - ((ConfigOptionFloats, machine_max_acceleration_y)) - ((ConfigOptionFloats, machine_max_acceleration_z)) - ((ConfigOptionFloats, machine_max_acceleration_e)) - // M203 X... Y... Z... E... [mm/sec] - ((ConfigOptionFloats, machine_max_feedrate_x)) - ((ConfigOptionFloats, machine_max_feedrate_y)) - ((ConfigOptionFloats, machine_max_feedrate_z)) - ((ConfigOptionFloats, machine_max_feedrate_e)) - - // M204 P... R... T...[mm/sec^2] - ((ConfigOptionFloats, machine_max_acceleration_extruding)) - ((ConfigOptionFloats, machine_max_acceleration_retracting)) - ((ConfigOptionFloats, machine_max_acceleration_travel)) - - // M205 X... Y... Z... E... [mm/sec] - ((ConfigOptionFloats, machine_max_jerk_x)) - ((ConfigOptionFloats, machine_max_jerk_y)) - ((ConfigOptionFloats, machine_max_jerk_z)) - ((ConfigOptionFloats, machine_max_jerk_e)) - // M205 T... [mm/sec] - ((ConfigOptionFloats, machine_min_travel_rate)) - // M205 S... [mm/sec] - ((ConfigOptionFloats, machine_min_extruding_rate)) + MachineEnvelopeConfig, + + // Allowing the machine limits to be completely ignored or used just for time estimator. + ((ConfigOptionEnum, machine_limits_usage)) + // M201 X... Y... Z... E... [mm/sec^2] + ((ConfigOptionFloats, machine_max_acceleration_x)) + ((ConfigOptionFloats, machine_max_acceleration_y)) + ((ConfigOptionFloats, machine_max_acceleration_z)) + ((ConfigOptionFloats, machine_max_acceleration_e)) + // M203 X... Y... Z... E... [mm/sec] + ((ConfigOptionFloats, machine_max_feedrate_x)) + ((ConfigOptionFloats, machine_max_feedrate_y)) + ((ConfigOptionFloats, machine_max_feedrate_z)) + ((ConfigOptionFloats, machine_max_feedrate_e)) + + // M204 P... R... T...[mm/sec^2] + ((ConfigOptionFloats, machine_max_acceleration_extruding)) + ((ConfigOptionFloats, machine_max_acceleration_retracting)) + ((ConfigOptionFloats, machine_max_acceleration_travel)) + + // M205 X... Y... Z... E... [mm/sec] + ((ConfigOptionFloats, machine_max_jerk_x)) + ((ConfigOptionFloats, machine_max_jerk_y)) + ((ConfigOptionFloats, machine_max_jerk_z)) + ((ConfigOptionFloats, machine_max_jerk_e)) + // M205 T... [mm/sec] + ((ConfigOptionFloats, machine_min_travel_rate)) + // M205 S... [mm/sec] + ((ConfigOptionFloats, machine_min_extruding_rate)) ) PRINT_CONFIG_CLASS_DEFINE( - GCodeConfig, + GCodeConfig, ((ConfigOptionBool, autoemit_temperature_commands)) - ((ConfigOptionString, before_layer_gcode)) - ((ConfigOptionString, between_objects_gcode)) - ((ConfigOptionFloats, deretract_speed)) - ((ConfigOptionString, end_gcode)) - ((ConfigOptionStrings, end_filament_gcode)) - ((ConfigOptionString, extrusion_axis)) - ((ConfigOptionFloats, extrusion_multiplier)) - ((ConfigOptionFloats, filament_diameter)) - ((ConfigOptionFloats, filament_density)) - ((ConfigOptionStrings, filament_type)) - ((ConfigOptionBools, filament_soluble)) - ((ConfigOptionFloats, filament_cost)) - ((ConfigOptionFloats, filament_spool_weight)) - ((ConfigOptionFloats, filament_max_volumetric_speed)) - ((ConfigOptionFloats, filament_loading_speed)) - ((ConfigOptionFloats, filament_loading_speed_start)) - ((ConfigOptionFloats, filament_load_time)) - ((ConfigOptionFloats, filament_unloading_speed)) - ((ConfigOptionFloats, filament_unloading_speed_start)) - ((ConfigOptionFloats, filament_toolchange_delay)) - ((ConfigOptionFloats, filament_unload_time)) - ((ConfigOptionInts, filament_cooling_moves)) - ((ConfigOptionFloats, filament_cooling_initial_speed)) - ((ConfigOptionFloats, filament_minimal_purge_on_wipe_tower)) - ((ConfigOptionFloats, filament_cooling_final_speed)) - ((ConfigOptionStrings, filament_ramming_parameters)) - ((ConfigOptionBool, gcode_comments)) - ((ConfigOptionEnum, gcode_flavor)) - ((ConfigOptionBool, gcode_label_objects)) - // Triples of strings: "search pattern", "replace with pattern", "attribs" - // where "attribs" are one of: - // r - regular expression - // i - case insensitive - // w - whole word - ((ConfigOptionStrings, gcode_substitutions)) - ((ConfigOptionString, layer_gcode)) - ((ConfigOptionFloat, max_print_speed)) - ((ConfigOptionFloat, max_volumetric_speed)) + ((ConfigOptionString, before_layer_gcode)) + ((ConfigOptionString, between_objects_gcode)) + ((ConfigOptionFloats, deretract_speed)) + ((ConfigOptionString, end_gcode)) + ((ConfigOptionStrings, end_filament_gcode)) + ((ConfigOptionString, extrusion_axis)) + ((ConfigOptionFloats, extrusion_multiplier)) + ((ConfigOptionFloats, filament_diameter)) + ((ConfigOptionFloats, filament_density)) + ((ConfigOptionStrings, filament_type)) + ((ConfigOptionBools, filament_soluble)) + ((ConfigOptionFloats, filament_cost)) + ((ConfigOptionFloats, filament_spool_weight)) + ((ConfigOptionFloats, filament_max_volumetric_speed)) + ((ConfigOptionFloats, filament_loading_speed)) + ((ConfigOptionFloats, filament_loading_speed_start)) + ((ConfigOptionFloats, filament_load_time)) + ((ConfigOptionFloats, filament_unloading_speed)) + ((ConfigOptionFloats, filament_unloading_speed_start)) + ((ConfigOptionFloats, filament_toolchange_delay)) + ((ConfigOptionFloats, filament_unload_time)) + ((ConfigOptionInts, filament_cooling_moves)) + ((ConfigOptionFloats, filament_cooling_initial_speed)) + ((ConfigOptionFloats, filament_minimal_purge_on_wipe_tower)) + ((ConfigOptionFloats, filament_cooling_final_speed)) + ((ConfigOptionStrings, filament_ramming_parameters)) + ((ConfigOptionBool, gcode_comments)) + ((ConfigOptionEnum, gcode_flavor)) + ((ConfigOptionBool, gcode_label_objects)) + // Triples of strings: "search pattern", "replace with pattern", "attribs" + // where "attribs" are one of: + // r - regular expression + // i - case insensitive + // w - whole word + ((ConfigOptionStrings, gcode_substitutions)) + ((ConfigOptionString, layer_gcode)) + ((ConfigOptionFloat, max_print_speed)) + ((ConfigOptionFloat, max_volumetric_speed)) ((ConfigOptionFloat, max_volumetric_extrusion_rate_slope_positive)) ((ConfigOptionFloat, max_volumetric_extrusion_rate_slope_negative)) - ((ConfigOptionPercents, retract_before_wipe)) - ((ConfigOptionFloats, retract_length)) - ((ConfigOptionFloats, retract_length_toolchange)) - ((ConfigOptionFloats, retract_lift)) - ((ConfigOptionFloats, retract_lift_above)) - ((ConfigOptionFloats, retract_lift_below)) - ((ConfigOptionFloats, retract_restart_extra)) - ((ConfigOptionFloats, retract_restart_extra_toolchange)) - ((ConfigOptionFloats, retract_speed)) - ((ConfigOptionString, start_gcode)) - ((ConfigOptionStrings, start_filament_gcode)) - ((ConfigOptionBool, single_extruder_multi_material)) - ((ConfigOptionBool, single_extruder_multi_material_priming)) - ((ConfigOptionBool, wipe_tower_no_sparse_layers)) - ((ConfigOptionString, toolchange_gcode)) - ((ConfigOptionFloat, travel_speed)) - ((ConfigOptionFloat, travel_speed_z)) - ((ConfigOptionBool, use_firmware_retraction)) - ((ConfigOptionBool, use_relative_e_distances)) - ((ConfigOptionBool, use_volumetric_e)) - ((ConfigOptionBool, variable_layer_height)) - ((ConfigOptionFloat, cooling_tube_retraction)) - ((ConfigOptionFloat, cooling_tube_length)) - ((ConfigOptionBool, high_current_on_filament_swap)) - ((ConfigOptionFloat, parking_pos_retraction)) - ((ConfigOptionBool, remaining_times)) - ((ConfigOptionBool, silent_mode)) - ((ConfigOptionFloat, extra_loading_move)) - ((ConfigOptionString, color_change_gcode)) - ((ConfigOptionString, pause_print_gcode)) - ((ConfigOptionString, template_custom_gcode)) + ((ConfigOptionPercents, retract_before_wipe)) + ((ConfigOptionFloats, retract_length)) + ((ConfigOptionFloats, retract_length_toolchange)) + ((ConfigOptionFloats, retract_lift)) + ((ConfigOptionFloats, retract_lift_above)) + ((ConfigOptionFloats, retract_lift_below)) + ((ConfigOptionFloats, retract_restart_extra)) + ((ConfigOptionFloats, retract_restart_extra_toolchange)) + ((ConfigOptionFloats, retract_speed)) + ((ConfigOptionString, start_gcode)) + ((ConfigOptionStrings, start_filament_gcode)) + ((ConfigOptionBool, single_extruder_multi_material)) + ((ConfigOptionBool, single_extruder_multi_material_priming)) + ((ConfigOptionBool, wipe_tower_no_sparse_layers)) + ((ConfigOptionString, toolchange_gcode)) + ((ConfigOptionFloat, travel_speed)) + ((ConfigOptionFloat, travel_speed_z)) + ((ConfigOptionBool, use_firmware_retraction)) + ((ConfigOptionBool, use_relative_e_distances)) + ((ConfigOptionBool, use_volumetric_e)) + ((ConfigOptionBool, variable_layer_height)) + ((ConfigOptionFloat, cooling_tube_retraction)) + ((ConfigOptionFloat, cooling_tube_length)) + ((ConfigOptionBool, high_current_on_filament_swap)) + ((ConfigOptionFloat, parking_pos_retraction)) + ((ConfigOptionBool, remaining_times)) + ((ConfigOptionBool, silent_mode)) + ((ConfigOptionFloat, extra_loading_move)) + ((ConfigOptionString, color_change_gcode)) + ((ConfigOptionString, pause_print_gcode)) + ((ConfigOptionString, template_custom_gcode)) ) static inline std::string get_extrusion_axis(const GCodeConfig &cfg) { - return - ((cfg.gcode_flavor.value == gcfMach3) || (cfg.gcode_flavor.value == gcfMachinekit)) ? "A" : - (cfg.gcode_flavor.value == gcfNoExtrusion) ? "" : cfg.extrusion_axis.value; + return + ((cfg.gcode_flavor.value == gcfMach3) || (cfg.gcode_flavor.value == gcfMachinekit)) ? "A" : + (cfg.gcode_flavor.value == gcfNoExtrusion) ? "" : cfg.extrusion_axis.value; } PRINT_CONFIG_CLASS_DERIVED_DEFINE( - PrintConfig, - (MachineEnvelopeConfig, GCodeConfig), + PrintConfig, + (MachineEnvelopeConfig, GCodeConfig), ((ConfigOptionBool, avoid_crossing_curled_overhangs)) - ((ConfigOptionBool, avoid_crossing_perimeters)) - ((ConfigOptionFloatOrPercent, avoid_crossing_perimeters_max_detour)) - ((ConfigOptionPoints, bed_shape)) - ((ConfigOptionInts, bed_temperature)) - ((ConfigOptionFloat, bridge_acceleration)) - ((ConfigOptionInts, bridge_fan_speed)) + ((ConfigOptionBool, avoid_crossing_perimeters)) + ((ConfigOptionFloatOrPercent, avoid_crossing_perimeters_max_detour)) + ((ConfigOptionPoints, bed_shape)) + ((ConfigOptionInts, bed_temperature)) + ((ConfigOptionFloat, bridge_acceleration)) + ((ConfigOptionInts, bridge_fan_speed)) ((ConfigOptionBools, enable_dynamic_fan_speeds)) ((ConfigOptionInts, overhang_fan_speed_0)) ((ConfigOptionInts, overhang_fan_speed_1)) ((ConfigOptionInts, overhang_fan_speed_2)) ((ConfigOptionInts, overhang_fan_speed_3)) - ((ConfigOptionBool, complete_objects)) - ((ConfigOptionFloats, colorprint_heights)) - ((ConfigOptionBools, cooling)) - ((ConfigOptionFloat, default_acceleration)) - ((ConfigOptionInts, disable_fan_first_layers)) - ((ConfigOptionEnum, draft_shield)) - ((ConfigOptionFloat, duplicate_distance)) + ((ConfigOptionBool, complete_objects)) + ((ConfigOptionFloats, colorprint_heights)) + ((ConfigOptionBools, cooling)) + ((ConfigOptionFloat, default_acceleration)) + ((ConfigOptionInts, disable_fan_first_layers)) + ((ConfigOptionEnum, draft_shield)) + ((ConfigOptionFloat, duplicate_distance)) ((ConfigOptionFloat, external_perimeter_acceleration)) - ((ConfigOptionFloat, extruder_clearance_height)) - ((ConfigOptionFloat, extruder_clearance_radius)) - ((ConfigOptionStrings, extruder_colour)) - ((ConfigOptionPoints, extruder_offset)) - ((ConfigOptionBools, fan_always_on)) - ((ConfigOptionInts, fan_below_layer_time)) - ((ConfigOptionStrings, filament_colour)) - ((ConfigOptionStrings, filament_notes)) - ((ConfigOptionFloat, first_layer_acceleration)) - ((ConfigOptionInts, first_layer_bed_temperature)) - ((ConfigOptionFloatOrPercent, first_layer_extrusion_width)) - ((ConfigOptionFloatOrPercent, first_layer_height)) - ((ConfigOptionFloatOrPercent, first_layer_speed)) - ((ConfigOptionInts, first_layer_temperature)) + ((ConfigOptionFloat, extruder_clearance_height)) + ((ConfigOptionFloat, extruder_clearance_radius)) + ((ConfigOptionStrings, extruder_colour)) + ((ConfigOptionPoints, extruder_offset)) + ((ConfigOptionBools, fan_always_on)) + ((ConfigOptionInts, fan_below_layer_time)) + ((ConfigOptionStrings, filament_colour)) + ((ConfigOptionStrings, filament_notes)) + ((ConfigOptionFloat, first_layer_acceleration)) + ((ConfigOptionInts, first_layer_bed_temperature)) + ((ConfigOptionFloatOrPercent, first_layer_extrusion_width)) + ((ConfigOptionFloatOrPercent, first_layer_height)) + ((ConfigOptionFloatOrPercent, first_layer_speed)) + ((ConfigOptionInts, first_layer_temperature)) ((ConfigOptionIntsNullable, idle_temperature)) - ((ConfigOptionInts, full_fan_speed_layer)) - ((ConfigOptionFloat, infill_acceleration)) - ((ConfigOptionBool, infill_first)) - ((ConfigOptionInts, max_fan_speed)) - ((ConfigOptionFloats, max_layer_height)) - ((ConfigOptionInts, min_fan_speed)) - ((ConfigOptionFloats, min_layer_height)) - ((ConfigOptionFloat, max_print_height)) - ((ConfigOptionFloats, min_print_speed)) - ((ConfigOptionFloat, min_skirt_length)) - ((ConfigOptionString, notes)) - ((ConfigOptionFloats, nozzle_diameter)) - ((ConfigOptionBool, only_retract_when_crossing_perimeters)) - ((ConfigOptionBool, ooze_prevention)) - ((ConfigOptionString, output_filename_format)) - ((ConfigOptionFloat, perimeter_acceleration)) - ((ConfigOptionStrings, post_process)) - ((ConfigOptionString, printer_model)) - ((ConfigOptionString, printer_notes)) - ((ConfigOptionFloat, resolution)) - ((ConfigOptionFloat, gcode_resolution)) - ((ConfigOptionFloats, retract_before_travel)) - ((ConfigOptionBools, retract_layer_change)) - ((ConfigOptionFloat, skirt_distance)) - ((ConfigOptionInt, skirt_height)) - ((ConfigOptionInt, skirts)) - ((ConfigOptionInts, slowdown_below_layer_time)) + ((ConfigOptionInts, full_fan_speed_layer)) + ((ConfigOptionFloat, infill_acceleration)) + ((ConfigOptionBool, infill_first)) + ((ConfigOptionInts, max_fan_speed)) + ((ConfigOptionFloats, max_layer_height)) + ((ConfigOptionInts, min_fan_speed)) + ((ConfigOptionFloats, min_layer_height)) + ((ConfigOptionFloat, max_print_height)) + ((ConfigOptionFloats, min_print_speed)) + ((ConfigOptionFloat, min_skirt_length)) + ((ConfigOptionString, notes)) + ((ConfigOptionFloats, nozzle_diameter)) + ((ConfigOptionBool, only_retract_when_crossing_perimeters)) + ((ConfigOptionBool, ooze_prevention)) + ((ConfigOptionString, output_filename_format)) + ((ConfigOptionFloat, perimeter_acceleration)) + ((ConfigOptionStrings, post_process)) + ((ConfigOptionString, printer_model)) + ((ConfigOptionString, printer_notes)) + ((ConfigOptionFloat, resolution)) + ((ConfigOptionFloat, gcode_resolution)) + ((ConfigOptionFloats, retract_before_travel)) + ((ConfigOptionBools, retract_layer_change)) + ((ConfigOptionFloat, skirt_distance)) + ((ConfigOptionInt, skirt_height)) + ((ConfigOptionInt, skirts)) + ((ConfigOptionInts, slowdown_below_layer_time)) ((ConfigOptionFloat, solid_infill_acceleration)) - ((ConfigOptionBool, spiral_vase)) - ((ConfigOptionInt, standby_temperature_delta)) - ((ConfigOptionInts, temperature)) - ((ConfigOptionInt, threads)) - ((ConfigOptionPoints, thumbnails)) - ((ConfigOptionEnum, thumbnails_format)) + ((ConfigOptionBool, spiral_vase)) + ((ConfigOptionInt, standby_temperature_delta)) + ((ConfigOptionInts, temperature)) + ((ConfigOptionInt, threads)) + ((ConfigOptionPoints, thumbnails)) + ((ConfigOptionEnum, thumbnails_format)) ((ConfigOptionFloat, top_solid_infill_acceleration)) ((ConfigOptionFloat, travel_acceleration)) - ((ConfigOptionBools, wipe)) - ((ConfigOptionBool, wipe_tower)) - ((ConfigOptionFloat, wipe_tower_x)) - ((ConfigOptionFloat, wipe_tower_y)) - ((ConfigOptionFloat, wipe_tower_width)) - ((ConfigOptionFloat, wipe_tower_per_color_wipe)) - ((ConfigOptionFloat, wipe_tower_rotation_angle)) - ((ConfigOptionFloat, wipe_tower_brim_width)) + ((ConfigOptionBools, wipe)) + ((ConfigOptionBool, wipe_tower)) + ((ConfigOptionFloat, wipe_tower_x)) + ((ConfigOptionFloat, wipe_tower_y)) + ((ConfigOptionFloat, wipe_tower_width)) + ((ConfigOptionFloat, wipe_tower_per_color_wipe)) + ((ConfigOptionFloat, wipe_tower_rotation_angle)) + ((ConfigOptionFloat, wipe_tower_brim_width)) ((ConfigOptionFloat, wipe_tower_cone_angle)) ((ConfigOptionPercent, wipe_tower_extra_spacing)) - ((ConfigOptionFloat, wipe_tower_bridging)) - ((ConfigOptionFloats, wiping_volumes_matrix)) - ((ConfigOptionFloats, wiping_volumes_extruders)) - ((ConfigOptionFloat, z_offset)) + ((ConfigOptionFloat, wipe_tower_bridging)) + ((ConfigOptionFloats, wiping_volumes_matrix)) + ((ConfigOptionFloats, wiping_volumes_extruders)) + ((ConfigOptionFloat, z_offset)) ) PRINT_CONFIG_CLASS_DERIVED_DEFINE0( - FullPrintConfig, - (PrintObjectConfig, PrintRegionConfig, PrintConfig) + FullPrintConfig, + (PrintObjectConfig, PrintRegionConfig, PrintConfig) ) // Validate the FullPrintConfig. Returns an empty string on success, otherwise an error message is returned. std::string validate(const FullPrintConfig &config); PRINT_CONFIG_CLASS_DEFINE( - SLAPrintConfig, - ((ConfigOptionString, output_filename_format)) + SLAPrintConfig, + ((ConfigOptionString, output_filename_format)) ) PRINT_CONFIG_CLASS_DEFINE( - SLAPrintObjectConfig, + SLAPrintObjectConfig, - ((ConfigOptionFloat, layer_height)) + ((ConfigOptionFloat, layer_height)) - //Number of the layers needed for the exposure time fade [3;20] - ((ConfigOptionInt, faded_layers))/*= 10*/ + //Number of the layers needed for the exposure time fade [3;20] + ((ConfigOptionInt, faded_layers))/*= 10*/ - ((ConfigOptionFloat, slice_closing_radius)) - ((ConfigOptionEnum, slicing_mode)) + ((ConfigOptionFloat, slice_closing_radius)) + ((ConfigOptionEnum, slicing_mode)) - // Enabling or disabling support creation - ((ConfigOptionBool, supports_enable)) + // Enabling or disabling support creation + ((ConfigOptionBool, supports_enable)) ((ConfigOptionEnum, support_tree_type)) - // Diameter in mm of the pointing side of the head. - ((ConfigOptionFloat, support_head_front_diameter))/*= 0.2*/ + // Diameter in mm of the pointing side of the head. + ((ConfigOptionFloat, support_head_front_diameter))/*= 0.2*/ - // How much the pinhead has to penetrate the model surface - ((ConfigOptionFloat, support_head_penetration))/*= 0.2*/ + // How much the pinhead has to penetrate the model surface + ((ConfigOptionFloat, support_head_penetration))/*= 0.2*/ - // Width in mm from the back sphere center to the front sphere center. - ((ConfigOptionFloat, support_head_width))/*= 1.0*/ + // Width in mm from the back sphere center to the front sphere center. + ((ConfigOptionFloat, support_head_width))/*= 1.0*/ - // Radius in mm of the support pillars. - ((ConfigOptionFloat, support_pillar_diameter))/*= 0.8*/ + // Radius in mm of the support pillars. + ((ConfigOptionFloat, support_pillar_diameter))/*= 0.8*/ - // The percentage of smaller pillars compared to the normal pillar diameter - // which are used in problematic areas where a normal pilla cannot fit. - ((ConfigOptionPercent, support_small_pillar_diameter_percent)) + // The percentage of smaller pillars compared to the normal pillar diameter + // which are used in problematic areas where a normal pilla cannot fit. + ((ConfigOptionPercent, support_small_pillar_diameter_percent)) - // How much bridge (supporting another pinhead) can be placed on a pillar. - ((ConfigOptionInt, support_max_bridges_on_pillar)) + // How much bridge (supporting another pinhead) can be placed on a pillar. + ((ConfigOptionInt, support_max_bridges_on_pillar)) - // How the pillars are bridged together - ((ConfigOptionEnum, support_pillar_connection_mode)) + // How the pillars are bridged together + ((ConfigOptionEnum, support_pillar_connection_mode)) - // Generate only ground facing supports - ((ConfigOptionBool, support_buildplate_only)) + // Generate only ground facing supports + ((ConfigOptionBool, support_buildplate_only)) ((ConfigOptionFloat, support_max_weight_on_model)) // Generate only ground facing supports ((ConfigOptionBool, support_enforcers_only)) - // TODO: unimplemented at the moment. This coefficient will have an impact - // when bridges and pillars are merged. The resulting pillar should be a bit - // thicker than the ones merging into it. How much thicker? I don't know - // but it will be derived from this value. - ((ConfigOptionFloat, support_pillar_widening_factor)) + // TODO: unimplemented at the moment. This coefficient will have an impact + // when bridges and pillars are merged. The resulting pillar should be a bit + // thicker than the ones merging into it. How much thicker? I don't know + // but it will be derived from this value. + ((ConfigOptionFloat, support_pillar_widening_factor)) - // Radius in mm of the pillar base. - ((ConfigOptionFloat, support_base_diameter))/*= 2.0*/ + // Radius in mm of the pillar base. + ((ConfigOptionFloat, support_base_diameter))/*= 2.0*/ - // The height of the pillar base cone in mm. - ((ConfigOptionFloat, support_base_height))/*= 1.0*/ + // The height of the pillar base cone in mm. + ((ConfigOptionFloat, support_base_height))/*= 1.0*/ - // The minimum distance of the pillar base from the model in mm. - ((ConfigOptionFloat, support_base_safety_distance)) /*= 1.0*/ + // The minimum distance of the pillar base from the model in mm. + ((ConfigOptionFloat, support_base_safety_distance)) /*= 1.0*/ - // The default angle for connecting support sticks and junctions. - ((ConfigOptionFloat, support_critical_angle))/*= 45*/ + // The default angle for connecting support sticks and junctions. + ((ConfigOptionFloat, support_critical_angle))/*= 45*/ - // The max length of a bridge in mm - ((ConfigOptionFloat, support_max_bridge_length))/*= 15.0*/ + // The max length of a bridge in mm + ((ConfigOptionFloat, support_max_bridge_length))/*= 15.0*/ - // The max distance of two pillars to get cross linked. - ((ConfigOptionFloat, support_max_pillar_link_distance)) + // The max distance of two pillars to get cross linked. + ((ConfigOptionFloat, support_max_pillar_link_distance)) - // The elevation in Z direction upwards. This is the space between the pad - // and the model object's bounding box bottom. Units in mm. - ((ConfigOptionFloat, support_object_elevation))/*= 5.0*/ + // The elevation in Z direction upwards. This is the space between the pad + // and the model object's bounding box bottom. Units in mm. + ((ConfigOptionFloat, support_object_elevation))/*= 5.0*/ // Branching tree @@ -978,137 +978,137 @@ PRINT_CONFIG_CLASS_DEFINE( - /////// Following options influence automatic support points placement: - ((ConfigOptionInt, support_points_density_relative)) - ((ConfigOptionFloat, support_points_minimal_distance)) + /////// Following options influence automatic support points placement: + ((ConfigOptionInt, support_points_density_relative)) + ((ConfigOptionFloat, support_points_minimal_distance)) - // Now for the base pool (pad) ///////////////////////////////////////////// + // Now for the base pool (pad) ///////////////////////////////////////////// - // Enabling or disabling support creation - ((ConfigOptionBool, pad_enable)) + // Enabling or disabling support creation + ((ConfigOptionBool, pad_enable)) - // The thickness of the pad walls - ((ConfigOptionFloat, pad_wall_thickness))/*= 2*/ + // The thickness of the pad walls + ((ConfigOptionFloat, pad_wall_thickness))/*= 2*/ - // The height of the pad from the bottom to the top not considering the pit - ((ConfigOptionFloat, pad_wall_height))/*= 5*/ + // The height of the pad from the bottom to the top not considering the pit + ((ConfigOptionFloat, pad_wall_height))/*= 5*/ - // How far should the pad extend around the contained geometry - ((ConfigOptionFloat, pad_brim_size)) + // How far should the pad extend around the contained geometry + ((ConfigOptionFloat, pad_brim_size)) - // The greatest distance where two individual pads are merged into one. The - // distance is measured roughly from the centroids of the pads. - ((ConfigOptionFloat, pad_max_merge_distance))/*= 50*/ + // The greatest distance where two individual pads are merged into one. The + // distance is measured roughly from the centroids of the pads. + ((ConfigOptionFloat, pad_max_merge_distance))/*= 50*/ - // The smoothing radius of the pad edges - // ((ConfigOptionFloat, pad_edge_radius))/*= 1*/; + // The smoothing radius of the pad edges + // ((ConfigOptionFloat, pad_edge_radius))/*= 1*/; - // The slope of the pad wall... - ((ConfigOptionFloat, pad_wall_slope)) + // The slope of the pad wall... + ((ConfigOptionFloat, pad_wall_slope)) - // ///////////////////////////////////////////////////////////////////////// - // Zero elevation mode parameters: - // - The object pad will be derived from the model geometry. - // - There will be a gap between the object pad and the generated pad - // according to the support_base_safety_distance parameter. - // - The two pads will be connected with tiny connector sticks - // ///////////////////////////////////////////////////////////////////////// + // ///////////////////////////////////////////////////////////////////////// + // Zero elevation mode parameters: + // - The object pad will be derived from the model geometry. + // - There will be a gap between the object pad and the generated pad + // according to the support_base_safety_distance parameter. + // - The two pads will be connected with tiny connector sticks + // ///////////////////////////////////////////////////////////////////////// - // Disable the elevation (ignore its value) and use the zero elevation mode - ((ConfigOptionBool, pad_around_object)) + // Disable the elevation (ignore its value) and use the zero elevation mode + ((ConfigOptionBool, pad_around_object)) - ((ConfigOptionBool, pad_around_object_everywhere)) + ((ConfigOptionBool, pad_around_object_everywhere)) - // This is the gap between the object bottom and the generated pad - ((ConfigOptionFloat, pad_object_gap)) + // This is the gap between the object bottom and the generated pad + ((ConfigOptionFloat, pad_object_gap)) - // How far to place the connector sticks on the object pad perimeter - ((ConfigOptionFloat, pad_object_connector_stride)) + // How far to place the connector sticks on the object pad perimeter + ((ConfigOptionFloat, pad_object_connector_stride)) - // The width of the connectors sticks - ((ConfigOptionFloat, pad_object_connector_width)) + // The width of the connectors sticks + ((ConfigOptionFloat, pad_object_connector_width)) - // How much should the tiny connectors penetrate into the model body - ((ConfigOptionFloat, pad_object_connector_penetration)) + // How much should the tiny connectors penetrate into the model body + ((ConfigOptionFloat, pad_object_connector_penetration)) - // ///////////////////////////////////////////////////////////////////////// - // Model hollowing parameters: - // - Models can be hollowed out as part of the SLA print process - // - Thickness of the hollowed model walls can be adjusted - // - - // - Additional holes will be drilled into the hollow model to allow for - // - resin removal. - // ///////////////////////////////////////////////////////////////////////// + // ///////////////////////////////////////////////////////////////////////// + // Model hollowing parameters: + // - Models can be hollowed out as part of the SLA print process + // - Thickness of the hollowed model walls can be adjusted + // - + // - Additional holes will be drilled into the hollow model to allow for + // - resin removal. + // ///////////////////////////////////////////////////////////////////////// - ((ConfigOptionBool, hollowing_enable)) + ((ConfigOptionBool, hollowing_enable)) - // The minimum thickness of the model walls to maintain. Note that the - // resulting walls may be thicker due to smoothing out fine cavities where - // resin could stuck. - ((ConfigOptionFloat, hollowing_min_thickness)) + // The minimum thickness of the model walls to maintain. Note that the + // resulting walls may be thicker due to smoothing out fine cavities where + // resin could stuck. + ((ConfigOptionFloat, hollowing_min_thickness)) - // Indirectly controls the voxel size (resolution) used by openvdb - ((ConfigOptionFloat, hollowing_quality)) + // Indirectly controls the voxel size (resolution) used by openvdb + ((ConfigOptionFloat, hollowing_quality)) - // Indirectly controls the minimum size of created cavities. - ((ConfigOptionFloat, hollowing_closing_distance)) + // Indirectly controls the minimum size of created cavities. + ((ConfigOptionFloat, hollowing_closing_distance)) ) enum SLAMaterialSpeed { slamsSlow, slamsFast, slamsHighViscosity }; PRINT_CONFIG_CLASS_DEFINE( - SLAMaterialConfig, - - ((ConfigOptionFloat, initial_layer_height)) - ((ConfigOptionFloat, bottle_cost)) - ((ConfigOptionFloat, bottle_volume)) - ((ConfigOptionFloat, bottle_weight)) - ((ConfigOptionFloat, material_density)) - ((ConfigOptionFloat, exposure_time)) - ((ConfigOptionFloat, initial_exposure_time)) - ((ConfigOptionFloats, material_correction)) - ((ConfigOptionFloat, material_correction_x)) - ((ConfigOptionFloat, material_correction_y)) - ((ConfigOptionFloat, material_correction_z)) - ((ConfigOptionEnum, material_print_speed)) + SLAMaterialConfig, + + ((ConfigOptionFloat, initial_layer_height)) + ((ConfigOptionFloat, bottle_cost)) + ((ConfigOptionFloat, bottle_volume)) + ((ConfigOptionFloat, bottle_weight)) + ((ConfigOptionFloat, material_density)) + ((ConfigOptionFloat, exposure_time)) + ((ConfigOptionFloat, initial_exposure_time)) + ((ConfigOptionFloats, material_correction)) + ((ConfigOptionFloat, material_correction_x)) + ((ConfigOptionFloat, material_correction_y)) + ((ConfigOptionFloat, material_correction_z)) + ((ConfigOptionEnum, material_print_speed)) ) PRINT_CONFIG_CLASS_DEFINE( - SLAPrinterConfig, - - ((ConfigOptionEnum, printer_technology)) - ((ConfigOptionPoints, bed_shape)) - ((ConfigOptionFloat, max_print_height)) - ((ConfigOptionFloat, display_width)) - ((ConfigOptionFloat, display_height)) - ((ConfigOptionInt, display_pixels_x)) - ((ConfigOptionInt, display_pixels_y)) - ((ConfigOptionEnum,display_orientation)) - ((ConfigOptionBool, display_mirror_x)) - ((ConfigOptionBool, display_mirror_y)) - ((ConfigOptionFloats, relative_correction)) - ((ConfigOptionFloat, relative_correction_x)) - ((ConfigOptionFloat, relative_correction_y)) - ((ConfigOptionFloat, relative_correction_z)) - ((ConfigOptionFloat, absolute_correction)) - ((ConfigOptionFloat, elefant_foot_compensation)) - ((ConfigOptionFloat, elefant_foot_min_width)) - ((ConfigOptionFloat, gamma_correction)) - ((ConfigOptionFloat, fast_tilt_time)) - ((ConfigOptionFloat, slow_tilt_time)) + SLAPrinterConfig, + + ((ConfigOptionEnum, printer_technology)) + ((ConfigOptionPoints, bed_shape)) + ((ConfigOptionFloat, max_print_height)) + ((ConfigOptionFloat, display_width)) + ((ConfigOptionFloat, display_height)) + ((ConfigOptionInt, display_pixels_x)) + ((ConfigOptionInt, display_pixels_y)) + ((ConfigOptionEnum,display_orientation)) + ((ConfigOptionBool, display_mirror_x)) + ((ConfigOptionBool, display_mirror_y)) + ((ConfigOptionFloats, relative_correction)) + ((ConfigOptionFloat, relative_correction_x)) + ((ConfigOptionFloat, relative_correction_y)) + ((ConfigOptionFloat, relative_correction_z)) + ((ConfigOptionFloat, absolute_correction)) + ((ConfigOptionFloat, elefant_foot_compensation)) + ((ConfigOptionFloat, elefant_foot_min_width)) + ((ConfigOptionFloat, gamma_correction)) + ((ConfigOptionFloat, fast_tilt_time)) + ((ConfigOptionFloat, slow_tilt_time)) ((ConfigOptionFloat, high_viscosity_tilt_time)) - ((ConfigOptionFloat, area_fill)) - ((ConfigOptionFloat, min_exposure_time)) - ((ConfigOptionFloat, max_exposure_time)) - ((ConfigOptionFloat, min_initial_exposure_time)) - ((ConfigOptionFloat, max_initial_exposure_time)) - ((ConfigOptionString, sla_archive_format)) - ((ConfigOptionFloat, sla_output_precision)) + ((ConfigOptionFloat, area_fill)) + ((ConfigOptionFloat, min_exposure_time)) + ((ConfigOptionFloat, max_exposure_time)) + ((ConfigOptionFloat, min_initial_exposure_time)) + ((ConfigOptionFloat, max_initial_exposure_time)) + ((ConfigOptionString, sla_archive_format)) + ((ConfigOptionFloat, sla_output_precision)) ) PRINT_CONFIG_CLASS_DERIVED_DEFINE0( - SLAFullPrintConfig, - (SLAPrinterConfig, SLAPrintConfig, SLAPrintObjectConfig, SLAMaterialConfig) + SLAFullPrintConfig, + (SLAPrinterConfig, SLAPrintConfig, SLAPrintObjectConfig, SLAMaterialConfig) ) #undef STATIC_PRINT_CONFIG_CACHE @@ -1136,19 +1136,19 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE0( class CLIActionsConfigDef : public ConfigDef { public: - CLIActionsConfigDef(); + CLIActionsConfigDef(); }; class CLITransformConfigDef : public ConfigDef { public: - CLITransformConfigDef(); + CLITransformConfigDef(); }; class CLIMiscConfigDef : public ConfigDef { public: - CLIMiscConfigDef(); + CLIMiscConfigDef(); }; // This class defines the command line options representing actions. @@ -1163,34 +1163,34 @@ extern const CLIMiscConfigDef cli_misc_config_def; class DynamicPrintAndCLIConfig : public DynamicPrintConfig { public: - DynamicPrintAndCLIConfig() {} - DynamicPrintAndCLIConfig(const DynamicPrintAndCLIConfig &other) : DynamicPrintConfig(other) {} + DynamicPrintAndCLIConfig() {} + DynamicPrintAndCLIConfig(const DynamicPrintAndCLIConfig &other) : DynamicPrintConfig(other) {} - // Overrides ConfigBase::def(). Static configuration definition. Any value stored into this ConfigBase shall have its definition here. - const ConfigDef* def() const override { return &s_def; } + // Overrides ConfigBase::def(). Static configuration definition. Any value stored into this ConfigBase shall have its definition here. + const ConfigDef* def() const override { return &s_def; } - // Verify whether the opt_key has not been obsoleted or renamed. - // Both opt_key and value may be modified by handle_legacy(). - // If the opt_key is no more valid in this version of Slic3r, opt_key is cleared by handle_legacy(). - // handle_legacy() is called internally by set_deserialize(). - void handle_legacy(t_config_option_key &opt_key, std::string &value) const override; + // Verify whether the opt_key has not been obsoleted or renamed. + // Both opt_key and value may be modified by handle_legacy(). + // If the opt_key is no more valid in this version of Slic3r, opt_key is cleared by handle_legacy(). + // handle_legacy() is called internally by set_deserialize(). + void handle_legacy(t_config_option_key &opt_key, std::string &value) const override; private: - class PrintAndCLIConfigDef : public ConfigDef - { - public: - PrintAndCLIConfigDef() { - this->options.insert(print_config_def.options.begin(), print_config_def.options.end()); - this->options.insert(cli_actions_config_def.options.begin(), cli_actions_config_def.options.end()); - this->options.insert(cli_transform_config_def.options.begin(), cli_transform_config_def.options.end()); - this->options.insert(cli_misc_config_def.options.begin(), cli_misc_config_def.options.end()); - for (const auto &kvp : this->options) - this->by_serialization_key_ordinal[kvp.second.serialization_key_ordinal] = &kvp.second; - } - // Do not release the default values, they are handled by print_config_def & cli_actions_config_def / cli_transform_config_def / cli_misc_config_def. - ~PrintAndCLIConfigDef() { this->options.clear(); } - }; - static PrintAndCLIConfigDef s_def; + class PrintAndCLIConfigDef : public ConfigDef + { + public: + PrintAndCLIConfigDef() { + this->options.insert(print_config_def.options.begin(), print_config_def.options.end()); + this->options.insert(cli_actions_config_def.options.begin(), cli_actions_config_def.options.end()); + this->options.insert(cli_transform_config_def.options.begin(), cli_transform_config_def.options.end()); + this->options.insert(cli_misc_config_def.options.begin(), cli_misc_config_def.options.end()); + for (const auto &kvp : this->options) + this->by_serialization_key_ordinal[kvp.second.serialization_key_ordinal] = &kvp.second; + } + // Do not release the default values, they are handled by print_config_def & cli_actions_config_def / cli_transform_config_def / cli_misc_config_def. + ~PrintAndCLIConfigDef() { this->options.clear(); } + }; + static PrintAndCLIConfigDef s_def; }; bool is_XL_printer(const DynamicPrintConfig &cfg); @@ -1226,111 +1226,111 @@ std::string get_sla_suptree_prefix(const DynamicPrintConfig &config); class ModelConfig { public: - // Following method clears the config and increases its timestamp, so the deleted - // state is considered changed from perspective of the undo/redo stack. - void reset() { m_data.clear(); touch(); } - - void assign_config(const ModelConfig &rhs) { - if (m_timestamp != rhs.m_timestamp) { - m_data = rhs.m_data; - m_timestamp = rhs.m_timestamp; - } - } - void assign_config(ModelConfig &&rhs) { - if (m_timestamp != rhs.m_timestamp) { - m_data = std::move(rhs.m_data); - m_timestamp = rhs.m_timestamp; - rhs.reset(); - } - } - - // Modification of the ModelConfig is not thread safe due to the global timestamp counter! - // Don't call modification methods from the back-end! - // Assign methods don't assign if src==dst to not having to bump the timestamp in case they are equal. - void assign_config(const DynamicPrintConfig &rhs) { if (m_data != rhs) { m_data = rhs; this->touch(); } } - void assign_config(DynamicPrintConfig &&rhs) { if (m_data != rhs) { m_data = std::move(rhs); this->touch(); } } - void apply(const ModelConfig &other, bool ignore_nonexistent = false) { this->apply(other.get(), ignore_nonexistent); } - void apply(const ConfigBase &other, bool ignore_nonexistent = false) { m_data.apply_only(other, other.keys(), ignore_nonexistent); this->touch(); } - void apply_only(const ModelConfig &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) { this->apply_only(other.get(), keys, ignore_nonexistent); } - void apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) { m_data.apply_only(other, keys, ignore_nonexistent); this->touch(); } - bool set_key_value(const std::string &opt_key, ConfigOption *opt) { bool out = m_data.set_key_value(opt_key, opt); this->touch(); return out; } - template - void set(const std::string &opt_key, T value) { m_data.set(opt_key, value, true); this->touch(); } - void set_deserialize(const t_config_option_key &opt_key, const std::string &str, ConfigSubstitutionContext &substitution_context, bool append = false) - { m_data.set_deserialize(opt_key, str, substitution_context, append); this->touch(); } - void set_deserialize_strict(const t_config_option_key &opt_key, const std::string &str, bool append = false) - { m_data.set_deserialize_strict(opt_key, str, append); this->touch(); } - bool erase(const t_config_option_key &opt_key) { bool out = m_data.erase(opt_key); if (out) this->touch(); return out; } - - // Getters are thread safe. - // The following implicit conversion breaks the Cereal serialization. + // Following method clears the config and increases its timestamp, so the deleted + // state is considered changed from perspective of the undo/redo stack. + void reset() { m_data.clear(); touch(); } + + void assign_config(const ModelConfig &rhs) { + if (m_timestamp != rhs.m_timestamp) { + m_data = rhs.m_data; + m_timestamp = rhs.m_timestamp; + } + } + void assign_config(ModelConfig &&rhs) { + if (m_timestamp != rhs.m_timestamp) { + m_data = std::move(rhs.m_data); + m_timestamp = rhs.m_timestamp; + rhs.reset(); + } + } + + // Modification of the ModelConfig is not thread safe due to the global timestamp counter! + // Don't call modification methods from the back-end! + // Assign methods don't assign if src==dst to not having to bump the timestamp in case they are equal. + void assign_config(const DynamicPrintConfig &rhs) { if (m_data != rhs) { m_data = rhs; this->touch(); } } + void assign_config(DynamicPrintConfig &&rhs) { if (m_data != rhs) { m_data = std::move(rhs); this->touch(); } } + void apply(const ModelConfig &other, bool ignore_nonexistent = false) { this->apply(other.get(), ignore_nonexistent); } + void apply(const ConfigBase &other, bool ignore_nonexistent = false) { m_data.apply_only(other, other.keys(), ignore_nonexistent); this->touch(); } + void apply_only(const ModelConfig &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) { this->apply_only(other.get(), keys, ignore_nonexistent); } + void apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) { m_data.apply_only(other, keys, ignore_nonexistent); this->touch(); } + bool set_key_value(const std::string &opt_key, ConfigOption *opt) { bool out = m_data.set_key_value(opt_key, opt); this->touch(); return out; } + template + void set(const std::string &opt_key, T value) { m_data.set(opt_key, value, true); this->touch(); } + void set_deserialize(const t_config_option_key &opt_key, const std::string &str, ConfigSubstitutionContext &substitution_context, bool append = false) + { m_data.set_deserialize(opt_key, str, substitution_context, append); this->touch(); } + void set_deserialize_strict(const t_config_option_key &opt_key, const std::string &str, bool append = false) + { m_data.set_deserialize_strict(opt_key, str, append); this->touch(); } + bool erase(const t_config_option_key &opt_key) { bool out = m_data.erase(opt_key); if (out) this->touch(); return out; } + + // Getters are thread safe. + // The following implicit conversion breaks the Cereal serialization. // operator const DynamicPrintConfig&() const throw() { return this->get(); } - const DynamicPrintConfig& get() const throw() { return m_data; } - bool empty() const throw() { return m_data.empty(); } - size_t size() const throw() { return m_data.size(); } - auto cbegin() const { return m_data.cbegin(); } - auto cend() const { return m_data.cend(); } - t_config_option_keys keys() const { return m_data.keys(); } - bool has(const t_config_option_key &opt_key) const { return m_data.has(opt_key); } - const ConfigOption* option(const t_config_option_key &opt_key) const { return m_data.option(opt_key); } - int opt_int(const t_config_option_key &opt_key) const { return m_data.opt_int(opt_key); } - int extruder() const { return opt_int("extruder"); } - double opt_float(const t_config_option_key &opt_key) const { return m_data.opt_float(opt_key); } - std::string opt_serialize(const t_config_option_key &opt_key) const { return m_data.opt_serialize(opt_key); } - - // Return an optional timestamp of this object. - // If the timestamp returned is non-zero, then the serialization framework will - // only save this object on the Undo/Redo stack if the timestamp is different - // from the timestmap of the object at the top of the Undo / Redo stack. - virtual uint64_t timestamp() const throw() { return m_timestamp; } - bool timestamp_matches(const ModelConfig &rhs) const throw() { return m_timestamp == rhs.m_timestamp; } - // Not thread safe! Should not be called from other than the main thread! - void touch() { m_timestamp = ++ s_last_timestamp; } + const DynamicPrintConfig& get() const throw() { return m_data; } + bool empty() const throw() { return m_data.empty(); } + size_t size() const throw() { return m_data.size(); } + auto cbegin() const { return m_data.cbegin(); } + auto cend() const { return m_data.cend(); } + t_config_option_keys keys() const { return m_data.keys(); } + bool has(const t_config_option_key &opt_key) const { return m_data.has(opt_key); } + const ConfigOption* option(const t_config_option_key &opt_key) const { return m_data.option(opt_key); } + int opt_int(const t_config_option_key &opt_key) const { return m_data.opt_int(opt_key); } + int extruder() const { return opt_int("extruder"); } + double opt_float(const t_config_option_key &opt_key) const { return m_data.opt_float(opt_key); } + std::string opt_serialize(const t_config_option_key &opt_key) const { return m_data.opt_serialize(opt_key); } + + // Return an optional timestamp of this object. + // If the timestamp returned is non-zero, then the serialization framework will + // only save this object on the Undo/Redo stack if the timestamp is different + // from the timestmap of the object at the top of the Undo / Redo stack. + virtual uint64_t timestamp() const throw() { return m_timestamp; } + bool timestamp_matches(const ModelConfig &rhs) const throw() { return m_timestamp == rhs.m_timestamp; } + // Not thread safe! Should not be called from other than the main thread! + void touch() { m_timestamp = ++ s_last_timestamp; } private: - friend class cereal::access; - template void serialize(Archive& ar) { ar(m_timestamp); ar(m_data); } + friend class cereal::access; + template void serialize(Archive& ar) { ar(m_timestamp); ar(m_data); } - uint64_t m_timestamp { 1 }; - DynamicPrintConfig m_data; + uint64_t m_timestamp { 1 }; + DynamicPrintConfig m_data; - static uint64_t s_last_timestamp; + static uint64_t s_last_timestamp; }; } // namespace Slic3r // Serialization through the Cereal library namespace cereal { - // Let cereal know that there are load / save non-member functions declared for DynamicPrintConfig, ignore serialize / load / save from parent class DynamicConfig. - template struct specialize {}; - - template void load(Archive& archive, Slic3r::DynamicPrintConfig &config) - { - size_t cnt; - archive(cnt); - config.clear(); - for (size_t i = 0; i < cnt; ++ i) { - size_t serialization_key_ordinal; - archive(serialization_key_ordinal); - assert(serialization_key_ordinal > 0); - auto it = Slic3r::print_config_def.by_serialization_key_ordinal.find(serialization_key_ordinal); - assert(it != Slic3r::print_config_def.by_serialization_key_ordinal.end()); - config.set_key_value(it->second->opt_key, it->second->load_option_from_archive(archive)); - } - } - - template void save(Archive& archive, const Slic3r::DynamicPrintConfig &config) - { - size_t cnt = config.size(); - archive(cnt); - for (auto it = config.cbegin(); it != config.cend(); ++it) { - const Slic3r::ConfigOptionDef* optdef = Slic3r::print_config_def.get(it->first); - assert(optdef != nullptr); - assert(optdef->serialization_key_ordinal > 0); - archive(optdef->serialization_key_ordinal); - optdef->save_option_to_archive(archive, it->second.get()); - } - } + // Let cereal know that there are load / save non-member functions declared for DynamicPrintConfig, ignore serialize / load / save from parent class DynamicConfig. + template struct specialize {}; + + template void load(Archive& archive, Slic3r::DynamicPrintConfig &config) + { + size_t cnt; + archive(cnt); + config.clear(); + for (size_t i = 0; i < cnt; ++ i) { + size_t serialization_key_ordinal; + archive(serialization_key_ordinal); + assert(serialization_key_ordinal > 0); + auto it = Slic3r::print_config_def.by_serialization_key_ordinal.find(serialization_key_ordinal); + assert(it != Slic3r::print_config_def.by_serialization_key_ordinal.end()); + config.set_key_value(it->second->opt_key, it->second->load_option_from_archive(archive)); + } + } + + template void save(Archive& archive, const Slic3r::DynamicPrintConfig &config) + { + size_t cnt = config.size(); + archive(cnt); + for (auto it = config.cbegin(); it != config.cend(); ++it) { + const Slic3r::ConfigOptionDef* optdef = Slic3r::print_config_def.get(it->first); + assert(optdef != nullptr); + assert(optdef->serialization_key_ordinal > 0); + archive(optdef->serialization_key_ordinal); + optdef->save_option_to_archive(archive, it->second.get()); + } + } } #endif diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 1a597b269c2..92a269130c9 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -85,7 +85,7 @@ using namespace std::literals; #define DEBUG #define _DEBUG #include "SVG.hpp" - #undef assert + #undef assert #include #endif @@ -132,7 +132,7 @@ PrintBase::ApplyStatus PrintObject::set_instances(PrintInstances &&instances) // Invalidate and set copies. PrintBase::ApplyStatus status = PrintBase::APPLY_STATUS_UNCHANGED; bool equal_length = instances.size() == m_instances.size(); - bool equal = equal_length && std::equal(instances.begin(), instances.end(), m_instances.begin(), + bool equal = equal_length && std::equal(instances.begin(), instances.end(), m_instances.begin(), [](const PrintInstance& lhs, const PrintInstance& rhs) { return lhs.model_instance == rhs.model_instance && lhs.shift == rhs.shift; }); if (! equal) { status = PrintBase::APPLY_STATUS_CHANGED; @@ -168,7 +168,7 @@ void PrintObject::make_perimeters() m_print->set_status(20, _u8L("Generating perimeters")); BOOST_LOG_TRIVIAL(info) << "Generating perimeters..." << log_memory_info(); - + // Revert the typed slices into untyped slices. if (m_typed_slices) { for (Layer *layer : m_layers) { @@ -178,10 +178,10 @@ void PrintObject::make_perimeters() } m_typed_slices = false; } - + // compare each layer to the one below, and mark those slices needing // one additional inner perimeter, like the top of domed objects- - + // this algorithm makes sure that at least one perimeter is overlapping // but we don't generate any extra perimeter if fill density is zero, as they would be floating // inside the object - infill_only_where_needed should be the method of choice for printing @@ -198,9 +198,10 @@ void PrintObject::make_perimeters() PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT); for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { m_print->throw_if_canceled(); - LayerRegion & layerm = *m_layers[layer_idx]->get_region(region_id); - int upper_layer_idx = this->next_layer_index(layer_idx, false); - if (upper_layer_idx == m_layers.size()) continue; + LayerRegion &layerm = *m_layers[layer_idx]->get_region(region_id); + int upper_layer_idx = this->next_layer_index(layer_idx, false); + if (upper_layer_idx == m_layers.size()) + continue; const LayerRegion &upper_layerm = *m_layers[upper_layer_idx]->get_region(region_id); const Polygons upper_layerm_polygons = to_polygons(upper_layerm.slices().surfaces); // Filter upper layer polygons in intersection_ppl by their bounding boxes? @@ -275,9 +276,8 @@ void PrintObject::prepare_infill() if (! this->set_started(posPrepareInfill)) return; - for (size_t idx = 0; idx < m_layers.size(); ++idx) { + for (size_t idx = 0; idx < m_layers.size(); ++idx) assert(m_layers[idx]->id() == idx + m_config.raft_layers); // subsequent calls will depend on such numbering - } m_print->set_status(30, _u8L("Preparing infill")); @@ -293,23 +293,22 @@ void PrintObject::prepare_infill() } // This will assign a type (top/bottom/internal) to $layerm->slices. - // Then the classifcation of $layerm->slices is transfered onto + // Then the classifcation of $layerm->slices is transfered onto // the $layerm->fill_surfaces by clipping $layerm->fill_surfaces // by the cummulative area of the previous $layerm->fill_surfaces. this->detect_surfaces_type(); m_print->throw_if_canceled(); - + // Decide what surfaces are to be filled. // Here the stTop / stBottomBridge / stBottom infill is turned to just stInternal if zero top / bottom infill layers are configured. // Also tiny stInternal surfaces are turned to stInternalSolid. BOOST_LOG_TRIVIAL(info) << "Preparing fill surfaces..." << log_memory_info(); - for (auto *layer : m_layers) { + for (auto *layer : m_layers) for (auto *region : layer->m_regions) { region->prepare_fill_surfaces(); m_print->throw_if_canceled(); } - } // Add solid fills to ensure the shell vertical thickness. @@ -387,7 +386,7 @@ void PrintObject::prepare_infill() } // for each layer } // for each region #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ - + // the following step needs to be done before combination because it may need // to remove only half of the combined infill this->bridge_over_infill(); @@ -426,7 +425,7 @@ void PrintObject::infill() this->prepare_infill(); if (this->set_started(posInfill)) { - // TRN Status for the Print calculation + // TRN Status for the Print calculation m_print->set_status(45, _u8L("Making infill")); const auto& adaptive_fill_octree = this->m_adaptive_fill_octrees.first; const auto& support_fill_octree = this->m_adaptive_fill_octrees.second; @@ -501,7 +500,7 @@ void PrintObject::generate_support_material() if (this->set_started(posSupportMaterial)) { this->clear_support_layers(); if ((this->has_support() && m_layers.size() > 1) || (this->has_raft() && ! m_layers.empty())) { - m_print->set_status(70, _u8L("Generating support material")); + m_print->set_status(70, _u8L("Generating support material")); this->_generate_support_material(); m_print->throw_if_canceled(); } else { @@ -697,7 +696,7 @@ bool PrintObject::invalidate_state_by_config_options( steps.emplace_back(posSlice); } else if ( opt_key == "elefant_foot_compensation" - || opt_key == "support_material_contact_distance" + || opt_key == "support_material_contact_distance" || opt_key == "xy_size_compensation") { steps.emplace_back(posSlice); } else if (opt_key == "support_material") { @@ -861,7 +860,7 @@ bool PrintObject::invalidate_state_by_config_options( bool PrintObject::invalidate_step(PrintObjectStep step) { bool invalidated = Inherited::invalidate_step(step); - + // propagate to dependent steps if (step == posPerimeters) { invalidated |= this->invalidate_steps({ posPrepareInfill, posInfill, posIroning, posSupportSpotsSearch, posEstimateCurledExtrusions }); @@ -947,7 +946,7 @@ void PrintObject::detect_surfaces_type() surfaces_new.assign(num_layers, Surfaces()); tbb::parallel_for( - tbb::blocked_range(0, + tbb::blocked_range(0, spiral_vase ? // In spiral vase mode, reserve the last layer for the top surface if more than 1 layer is planned for the vase bottom. ((num_layers > 1) ? num_layers - 1 : num_layers) : @@ -976,7 +975,7 @@ void PrintObject::detect_surfaces_type() // of current layer and upper one) Surfaces top; if (upper_layer) { - ExPolygons upper_slices = interface_shells ? + ExPolygons upper_slices = interface_shells ? diff_ex(layerm->slices().surfaces, upper_layer->m_regions[region_id]->slices().surfaces, ApplySafetyOffset::Yes) : diff_ex(layerm->slices().surfaces, upper_layer->lslices, ApplySafetyOffset::Yes); surfaces_append(top, opening_ex(upper_slices, offset), stTop); @@ -987,14 +986,14 @@ void PrintObject::detect_surfaces_type() for (Surface &surface : top) surface.surface_type = stTop; } - + // Find bottom surfaces (difference between current surfaces of current layer and lower one). Surfaces bottom; if (lower_layer) { #if 0 //FIXME Why is this branch failing t\multi.t ? - Polygons lower_slices = interface_shells ? - to_polygons(lower_layer->get_region(region_id)->slices.surfaces) : + Polygons lower_slices = interface_shells ? + to_polygons(lower_layer->get_region(region_id)->slices.surfaces) : to_polygons(lower_layer->slices); surfaces_append(bottom, opening_ex(diff(layerm->slices.surfaces, lower_slices, true), offset), @@ -1010,7 +1009,7 @@ void PrintObject::detect_surfaces_type() // if user requested internal shells, we need to identify surfaces // lying on other slices not belonging to this region if (interface_shells) { - // non-bridging bottom surfaces: any part of this layer lying + // non-bridging bottom surfaces: any part of this layer lying // on something else, excluding those lying on our own region surfaces_append( bottom, @@ -1030,7 +1029,7 @@ void PrintObject::detect_surfaces_type() for (Surface &surface : bottom) surface.surface_type = stBottom; } - + // now, if the object contained a thin membrane, we could have overlapping bottom // and top surfaces; let's do an intersection to discover them and consider them // as bottom surfaces (to allow for bridge detection) @@ -1053,7 +1052,7 @@ void PrintObject::detect_surfaces_type() SVG::export_expolygons(debug_out_path("1_detect_surfaces_type_%d_region%d-layer_%f.svg", iRun ++, region_id, layer->print_z).c_str(), expolygons_with_attributes); } #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ - + // save surfaces to layer Surfaces &surfaces_out = interface_shells ? surfaces_new[idx_layer] : layerm->m_slices.surfaces; Surfaces surfaces_backup; @@ -1072,7 +1071,7 @@ void PrintObject::detect_surfaces_type() surfaces_append(surfaces_out, std::move(top)); surfaces_append(surfaces_out, std::move(bottom)); - + // Slic3r::debugf " layer %d has %d bottom, %d top and %d internal surfaces\n", // $layerm->layer->id, scalar(@bottom), scalar(@top), scalar(@internal) if $Slic3r::debug; @@ -1197,7 +1196,7 @@ void PrintObject::process_external_surfaces() // lower layer ? nullptr : &surfaces_covered[lower_layer->id()]; // lower layer polygons with density > 0% - m_layers[layer_idx]->get_region(int(region_id))->process_external_surfaces(lower_layer, covered); + m_layers[layer_idx]->get_region(int(region_id))->process_external_surfaces(lower_layer, covered); } } ); @@ -1294,7 +1293,7 @@ void PrintObject::discover_vertical_shells() svg.draw(layer.lslices, "blue"); svg.draw(union_ex(cache.holes), "red"); svg.draw_outline(union_ex(cache.holes), "black", "blue", scale_(0.05)); - svg.Close(); + svg.Close(); } #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ } @@ -1366,7 +1365,7 @@ void PrintObject::discover_vertical_shells() #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ Flow solid_infill_flow = layerm->flow(frSolidInfill); - coord_t infill_line_spacing = solid_infill_flow.scaled_spacing(); + coord_t infill_line_spacing = solid_infill_flow.scaled_spacing(); // Find a union of perimeters below / above this surface to guarantee a minimum shell thickness. Polygons shell; Polygons holes; @@ -1408,7 +1407,7 @@ void PrintObject::discover_vertical_shells() shell = std::move(shells2); else if (! shells2.empty()) { polygons_append(shell, shells2); - // Running the union_ using the Clipper library piece by piece is cheaper + // Running the union_ using the Clipper library piece by piece is cheaper // than running the union_ all at once. shell = union_(shell); } @@ -1417,9 +1416,9 @@ void PrintObject::discover_vertical_shells() if (int n_top_layers = region_config.top_solid_layers.value; n_top_layers > 0) { // Gather top regions projected to this layer. coordf_t print_z = layer->print_z; - int i = next_layer_index(idx_layer, false); + int i = next_layer_index(idx_layer, false); int count = 1; - bool at_least_one_top_projected = false; + bool at_least_one_top_projected = false; for (; i < int(cache_top_botom_regions.size()) && (count < n_top_layers || m_layers[i]->print_z - print_z < region_config.top_solid_min_thickness - EPSILON); @@ -1428,7 +1427,6 @@ void PrintObject::discover_vertical_shells() const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[i]; combine_holes(cache.holes); combine_shells(cache.top_surfaces); - // Running the union_ using the Clipper library piece by piece is cheaper } if (!at_least_one_top_projected && i < int(cache_top_botom_regions.size())) { // Lets consider this a special case - with only 1 top solid and minimal shell thickness settings, the @@ -1448,13 +1446,13 @@ void PrintObject::discover_vertical_shells() if (int n_bottom_layers = region_config.bottom_solid_layers.value; n_bottom_layers > 0) { // Gather bottom regions projected to this layer. coordf_t bottom_z = layer->bottom_z(); - int i = next_layer_index(idx_layer, true); - int count = 1; + int i = next_layer_index(idx_layer, true); + int count = 1; bool at_least_one_bottom_projected = false; - for (; i >= 0 && (count < n_bottom_layers || + for (; i >= 0 && (count < n_bottom_layers || bottom_z - m_layers[i]->bottom_z() < region_config.bottom_solid_min_thickness - EPSILON); i = next_layer_index(i, true), ++count) { - at_least_one_bottom_projected = true; + at_least_one_bottom_projected = true; const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[i]; combine_holes(cache.holes); combine_shells(cache.bottom_surfaces); @@ -1477,12 +1475,12 @@ void PrintObject::discover_vertical_shells() Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-before-union-%d.svg", debug_idx), get_extents(shell)); svg.draw(shell); svg.draw_outline(shell, "black", scale_(0.05)); - svg.Close(); + svg.Close(); } #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ #if 0 // shell = union_(shell, true); - shell = union_(shell, false); + shell = union_(shell, false); #endif #ifdef SLIC3R_DEBUG_SLICE_PROCESSING shell_ex = union_safety_offset_ex(shell); @@ -1496,7 +1494,7 @@ void PrintObject::discover_vertical_shells() Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-after-union-%d.svg", debug_idx), get_extents(shell)); svg.draw(shell_ex); svg.draw_outline(shell_ex, "black", "blue", scale_(0.05)); - svg.Close(); + svg.Close(); } #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ @@ -1508,7 +1506,7 @@ void PrintObject::discover_vertical_shells() svg.draw(shell_ex, "blue", 0.5); svg.draw_outline(shell_ex, "black", "blue", scale_(0.05)); svg.Close(); - } + } { Slic3r::SVG svg(debug_out_path("discover_vertical_shells-internalvoid-wshell-%d.svg", debug_idx), get_extents(shell)); svg.draw(layerm->fill_surfaces().filter_by_type(stInternalVoid), "yellow", 0.5); @@ -1516,7 +1514,7 @@ void PrintObject::discover_vertical_shells() svg.draw(shell_ex, "blue", 0.5); svg.draw_outline(shell_ex, "black", "blue", scale_(0.05)); svg.Close(); - } + } { Slic3r::SVG svg(debug_out_path("discover_vertical_shells-internalsolid-wshell-%d.svg", debug_idx), get_extents(shell)); svg.draw(layerm->fill_surfaces().filter_by_type(stInternalSolid), "yellow", 0.5); @@ -1524,7 +1522,7 @@ void PrintObject::discover_vertical_shells() svg.draw(shell_ex, "blue", 0.5); svg.draw_outline(shell_ex, "black", "blue", scale_(0.05)); svg.Close(); - } + } #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ // Trim the shells region by the internal & internal void surfaces. @@ -1545,7 +1543,7 @@ void PrintObject::discover_vertical_shells() #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ ExPolygons regularized_shell; { - // Intentionally inflate a bit more than how much the region has been shrunk, + // Open to remove (filter out) regions narrower than a bit less than an infill extrusion line width. // Such narrow regions are difficult to fill in with a gap fill algorithm (or Arachne), however they are most likely // not needed for print stability / quality. const float narrow_ensure_vertical_wall_thickness_region_radius = 0.5f * 0.65f * min_perimeter_infill_spacing; @@ -1564,14 +1562,14 @@ void PrintObject::discover_vertical_shells() Polygons internal_volume; { - int lower_layer_index = next_layer_index(idx_layer, true); + int lower_layer_index = next_layer_index(idx_layer, true); Polygons shrinked_bottom_slice = lower_layer_index >= 0 ? to_polygons(m_layers[lower_layer_index]->lslices) : Polygons{}; - int upper_layer_index = next_layer_index(idx_layer, false); - Polygons shrinked_upper_slice = upper_layer_index >= 0 ? to_polygons(m_layers[upper_layer_index]->lslices) : Polygons{}; + int upper_layer_index = next_layer_index(idx_layer, false); + Polygons shrinked_upper_slice = upper_layer_index < m_layers.size() ? to_polygons(m_layers[upper_layer_index]->lslices) : Polygons{}; internal_volume = intersection(shrinked_bottom_slice, shrinked_upper_slice); } - // grow the collapsing parts and add the extra area to the neighbor layer + // The opening operation may cause scattered tiny drops on the smooth parts of the model, filter them out regularized_shell.erase(std::remove_if(regularized_shell.begin(), regularized_shell.end(), [&min_perimeter_infill_spacing, &internal_volume](const ExPolygon &p) { return p.area() < min_perimeter_infill_spacing * scaled(1.5) || @@ -1593,7 +1591,7 @@ void PrintObject::discover_vertical_shells() svg.draw_outline(union_safety_offset_ex(shell), "black", "blue", scale_(0.05)); // Regularized infill region. svg.draw_outline(new_internal_solid, "red", "magenta", scale_(0.05)); - svg.Close(); + svg.Close(); } #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ @@ -1639,7 +1637,7 @@ template void debug_draw(std::string name, const T& a, const T& b, c bbox.merge(get_extents(c)); bbox.merge(get_extents(d)); bbox.offset(scale_(1.)); - ::Slic3r::SVG svg(debug_out_path(name.c_str()).c_str(), bbox); + ::Slic3r::SVG svg(debug_out_path(name.c_str()).c_str(), bbox); svg.draw(a, colors[0], scale_(0.3)); svg.draw(b, colors[1], scale_(0.23)); svg.draw(c, colors[2], scale_(0.16)); @@ -1714,8 +1712,8 @@ void PrintObject::bridge_over_infill() SurfacesPtr region_internal_solids = region->fill_surfaces().filter_by_type(stInternalSolid); for (const Surface *s : region_internal_solids) { Polygons unsupported = intersection(to_polygons(s->expolygon), unsupported_area); - // The following flag marks those surfaces, which overlap with unuspported area, but at least part of them is supported. - // These regions can be filtered by area, because they for sure are touching solids on lower layers, and it does not make sense to bridge their tiny overhangs + // The following flag marks those surfaces, which overlap with unuspported area, but at least part of them is supported. + // These regions can be filtered by area, because they for sure are touching solids on lower layers, and it does not make sense to bridge their tiny overhangs bool partially_supported = area(unsupported) < area(to_polygons(s->expolygon)) - EPSILON; if (!unsupported.empty() && (!partially_supported || area(unsupported) > 3 * 3 * spacing * spacing)) { Polygons worth_bridging = intersection(to_polygons(s->expolygon), expand(unsupported, 4 * spacing)); @@ -1736,7 +1734,7 @@ void PrintObject::bridge_over_infill() #endif #ifdef DEBUG_BRIDGE_OVER_INFILL debug_draw(std::to_string(lidx) + "_candidate_processing_" + std::to_string(area(unsupported)), - to_lines(unsupported), to_lines(intersection(to_polygons(s->expolygon), expand(unsupported, 5 * spacing))), + to_lines(unsupported), to_lines(intersection(to_polygons(s->expolygon), expand(unsupported, 5 * spacing))), to_lines(diff(to_polygons(s->expolygon), expand(worth_bridging, spacing))), to_lines(unsupported_area)); #endif @@ -1856,7 +1854,7 @@ void PrintObject::bridge_over_infill() this->m_adaptive_fill_octrees = this->prepare_adaptive_infill_data(surfaces_w_bottom_z); std::vector layers_to_generate_infill; - for (const auto &pair : surfaces_by_layer) { + for (const auto &pair : surfaces_by_layer) { assert(pair.first > 0); infill_lines[next_layer_index(pair.first, true)] = {}; layers_to_generate_infill.push_back(next_layer_index(pair.first, true)); @@ -1870,7 +1868,6 @@ void PrintObject::bridge_over_infill() size_t lidx = layers_to_generate_infill[job_idx]; infill_lines.at( lidx) = po->get_layer(lidx)->generate_sparse_infill_polylines_for_anchoring(po->m_adaptive_fill_octrees.first.get(), - po->m_adaptive_fill_octrees.second.get(), po->m_lightning_generator.get()); } @@ -1894,7 +1891,6 @@ void PrintObject::bridge_over_infill() } // prepare inflated filter for each candidate on each layer. layers will be put into single thread cluster if they are close to each other (z-axis-wise) - // and if the inflated AABB polygons overlap somewhere tbb::parallel_for(tbb::blocked_range(0, layers_with_candidates.size()), [&layers_with_candidates, &surfaces_by_layer, &layer_area_covered_by_candidates]( @@ -2552,7 +2548,7 @@ PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &defau // Switch of infill for very low infill rates, also avoid division by zero in infill generator for these very low rates. // See GH issue #5910. config.fill_density.value = 0; - else + else config.fill_density.value = std::min(config.fill_density.value, 100.); if (config.fuzzy_skin.value != FuzzySkinType::None && (config.fuzzy_skin_point_dist.value < 0.01 || config.fuzzy_skin_thickness.value < 0.001)) config.fuzzy_skin.value = FuzzySkinType::None; @@ -2721,7 +2717,7 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c // // Regularize the overhang regions, so that the infill areas will not become excessively jagged. // smooth_outward( // closing(upper_internal, closing_radius, ClipperLib::jtSquare, 0.), -// scaled(0.1)), +// scaled(0.1)), // lower_layer_internal_surfaces); // // Apply new internal infill to regions. // for (LayerRegion *layerm : lower_layer->m_regions) { @@ -2820,11 +2816,11 @@ void PrintObject::combine_infill() current_height += layer->height; ++ num_layers; } - + // Append lower layers (if any) to uppermost layer. combine[m_layers.size() - 1] = num_layers; } - + // loop through layers to which we have assigned layers to combine for (size_t layer_idx = 0; layer_idx < m_layers.size(); ++ layer_idx) { m_print->throw_if_canceled(); @@ -2844,8 +2840,8 @@ void PrintObject::combine_infill() intersection = intersection_ex(layerms[i]->fill_surfaces().filter_by_type(stInternal), intersection); double area_threshold = layerms.front()->infill_area_threshold(); if (! intersection.empty() && area_threshold > 0.) - intersection.erase(std::remove_if(intersection.begin(), intersection.end(), - [area_threshold](const ExPolygon &expoly) { return expoly.area() <= area_threshold; }), + intersection.erase(std::remove_if(intersection.begin(), intersection.end(), + [area_threshold](const ExPolygon &expoly) { return expoly.area() <= area_threshold; }), intersection.end()); if (intersection.empty()) continue; @@ -2857,15 +2853,15 @@ void PrintObject::combine_infill() // so let's remove those areas from all layers. Polygons intersection_with_clearance; intersection_with_clearance.reserve(intersection.size()); - float clearance_offset = + float clearance_offset = 0.5f * layerms.back()->flow(frPerimeter).scaled_width() + - // Because fill areas for rectilinear and honeycomb are grown + // Because fill areas for rectilinear and honeycomb are grown // later to overlap perimeters, we need to counteract that too. ((region.config().fill_pattern == ipRectilinear || region.config().fill_pattern == ipMonotonic || region.config().fill_pattern == ipGrid || region.config().fill_pattern == ipLine || - region.config().fill_pattern == ipHoneycomb) ? 1.5f : 0.5f) * + region.config().fill_pattern == ipHoneycomb) ? 1.5f : 0.5f) * layerms.back()->flow(frSolidInfill).scaled_width(); for (ExPolygon &expoly : intersection) polygons_append(intersection_with_clearance, offset(expoly, clearance_offset)); @@ -3085,7 +3081,7 @@ static void project_triangles_to_slabs(SpanOfConstPtrs layers, const inde // The resulting triangles are fed to the Clipper library, which seem to handle flipped triangles well. // if (cross2(Vec2d((poly.pts[1] - poly.pts[0]).cast()), Vec2d((poly.pts[2] - poly.pts[1]).cast())) < 0) // std::swap(poly.pts.front(), poly.pts.back()); - + out[layer_id].emplace_back(std::move(poly.pts)); ++layer_id; } diff --git a/src/libslic3r/PrintObjectSlice.cpp b/src/libslic3r/PrintObjectSlice.cpp index 1e763e15ce6..56f4b1207c5 100644 --- a/src/libslic3r/PrintObjectSlice.cpp +++ b/src/libslic3r/PrintObjectSlice.cpp @@ -39,28 +39,27 @@ LayerPtrs new_layers( return out; } - // Slice single triangle mesh. -static std::vector slice_volume(const ModelVolume & volume, - const std::vector & zs, - const MeshSlicingParamsEx & params, - std::vector * sublayers, - const std::function &throw_on_cancel_callback) +// Slice single triangle mesh. +static std::vector slice_volume( + const ModelVolume &volume, + const std::vector &zs, + const MeshSlicingParamsEx ¶ms, + std::vector *sublayers, + const std::function &throw_on_cancel_callback) { indexed_triangle_set its = volume.mesh().its; - if (zs.empty() || its.indices.size() == 0) { + if (zs.empty() || its.indices.size() == 0) return std::vector(); - } MeshSlicingParamsEx params2{params}; params2.trafo = params2.trafo * volume.get_matrix(); if (params2.trafo.rotation().determinant() < 0.) its_flip_triangles(its); std::vector expolys = slice_mesh_ex(its, zs, params2, throw_on_cancel_callback); - if (params2.z_dither) { + if (params2.z_dither) expolys = z_dither(its, zs, params2, expolys, sublayers, throw_on_cancel_callback); - } else { + else *sublayers = std::vector(expolys.size()); - } throw_on_cancel_callback(); return expolys; } @@ -101,7 +100,7 @@ static std::vector slice_volume( i = 0; for (const std::pair &span : n_filtered) for (size_t j = span.first; j < span.second; ++j) { - out[j] = std::move(layers[i++]); + out[j] = std::move(layers[i ++]); outSublayers->emplace_back(std::move(sublayers[j])); } } @@ -110,7 +109,6 @@ static std::vector slice_volume( return out; } - struct VolumeSlices { ObjectID volume_id; @@ -183,7 +181,7 @@ static std::vector slice_volumes_inner( if (layer_ranges.size() == 1) { if (const PrintObjectRegions::LayerRangeRegions &layer_range = layer_ranges.front(); layer_range.has_volume(model_volume->id())) { if (model_volume->is_model_part() && print_config.spiral_vase) { - auto it = std::find_if(layer_range.volume_regions.begin(), layer_range.volume_regions.end(), + auto it = std::find_if(layer_range.volume_regions.begin(), layer_range.volume_regions.end(), [model_volume](const auto &slice){ return model_volume == slice.model_volume; }); params.mode = MeshSlicingParams::SlicingMode::PositiveLargestContour; // Slice the bottom layers with SlicingMode::Regular. @@ -249,7 +247,7 @@ static std::vector::const_iterator layer_ } static std::vector::const_iterator layer_range_next( - const std::vector &layer_ranges, + const std::vector &layer_ranges, std::vector::const_iterator it, double z) { @@ -337,7 +335,7 @@ static std::vector> slices_to_regions( float z = zs_complex[range.begin()].second; auto it_layer_range = layer_range_first(print_object_regions.layer_ranges, z); // Per volume_regions slices at this Z height. - struct RegionSlice { + struct RegionSlice { ExPolygons expolygons; // Identifier of this region in PrintObjectRegions::all_regions int region_id; @@ -413,7 +411,7 @@ static std::vector> slices_to_regions( } } if (merged) - // to handle region overlaps. Indeed, one may intentionally let the regions overlap to produce crossing perimeters + // to handle region overlaps. Indeed, one may intentionally let the regions overlap to produce crossing perimeters expolygons = closing_ex(expolygons, float(scale_(EPSILON))); slices_by_region[temp_slices[i].region_id][z_idx] = std::move(expolygons); i = j; @@ -469,7 +467,7 @@ std::string fix_slicing_errors(LayerPtrs &layers, const std::function &t // Collect outer contours and holes from the valid layers above & below. Polygons outer; outer.reserve( - ((upper_surfaces == nullptr) ? 0 : upper_surfaces->size()) + + ((upper_surfaces == nullptr) ? 0 : upper_surfaces->size()) + ((lower_surfaces == nullptr) ? 0 : lower_surfaces->size())); size_t num_holes = 0; if (upper_surfaces) @@ -565,23 +563,21 @@ void PrintObject::slice() tbb::parallel_for( tbb::blocked_range(1, m_layers.size()), [this](const tbb::blocked_range &range) { - for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { - m_print->throw_if_canceled(); - // Layer::build_up_down_graph and subsequent support stability checks - // appear to get in trouble when multiple layers point to the same - // above/below layer as happens in case of z-dithering. For now I just avoid - // mixing dithered and non-dithered layers in the same graph. - Layer *above = m_layers[layer_idx]; - Layer *below = above->lower_layer; - if (below != nullptr && above->dithered == below->dithered) { - Layer::build_up_down_graph(*below, *above); - } - // Layer::build_up_down_graph(*m_layers[layer_idx - 1], *m_layers[layer_idx]); - } + for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { + m_print->throw_if_canceled(); + // Layer::build_up_down_graph and subsequent support stability checks + // appear to get in trouble when multiple layers point to the same + // above/below layer as happens in case of z-dithering. For now I just avoid + // mixing dithered and non-dithered layers in the same graph. + Layer *above = m_layers[layer_idx]; + Layer *below = above->lower_layer; + if (below != nullptr && above->dithered == below->dithered) + Layer::build_up_down_graph(*below, *above); + // Layer::build_up_down_graph(*m_layers[layer_idx - 1], *m_layers[layer_idx]); + } }); - // }); if (m_layers.empty()) - throw Slic3r::SlicingError("No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry.\n"); + throw Slic3r::SlicingError("No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry.\n"); this->set_done(posSlice); } @@ -832,13 +828,12 @@ void PrintObject::slice_volumes() if (this->config().z_dither) { m_layers = add_dithering_layers(m_layers, volume_slices, volume_sublayers); - for (Layer *layer : m_layers) { + for (Layer *layer : m_layers) if (layer->dithered) { layer->m_regions.reserve(m_shared_regions->all_regions.size()); for (const std::unique_ptr &pr : m_shared_regions->all_regions) layer->m_regions.emplace_back(new LayerRegion(layer, pr.get())); } - } slice_zs = zs_from_layers(m_layers, false); } @@ -851,7 +846,7 @@ void PrintObject::slice_volumes() m_layers[layer_id]->regions()[region_id]->m_slices.append(std::move(by_layer[layer_id]), stInternal); } region_slices.clear(); - + BOOST_LOG_TRIVIAL(debug) << "Slicing volumes - removing top empty layers"; while (! m_layers.empty()) { const Layer *layer = m_layers.back(); @@ -919,7 +914,7 @@ void PrintObject::slice_volumes() layerm->m_slices.set( union_ex( Slic3r::elephant_foot_compensation( - (delta == 0.f) ? lslices_1st_layer : offset_ex(lslices_1st_layer, delta), + (delta == 0.f) ? lslices_1st_layer : offset_ex(lslices_1st_layer, delta), layerm->flow(frExternalPerimeter), unscale(elfoot))), stInternal); if (xy_compensation_scaled < 0.f) diff --git a/src/libslic3r/Support/SupportMaterial.cpp b/src/libslic3r/Support/SupportMaterial.cpp index e8e52585d48..e70bcb651b9 100644 --- a/src/libslic3r/Support/SupportMaterial.cpp +++ b/src/libslic3r/Support/SupportMaterial.cpp @@ -52,7 +52,7 @@ namespace Slic3r { // how much we extend support around the actual contact area //FIXME this should be dependent on the nozzle diameter! -#define SUPPORT_MATERIAL_MARGIN 1.5 +#define SUPPORT_MATERIAL_MARGIN 1.5 // Increment used to reach MARGIN in steps to avoid trespassing thin objects #define NUM_MARGIN_STEPS 3 @@ -73,7 +73,7 @@ static std::vector rasterize_polygons(const Vec2i &grid_size, con agg::pixfmt_gray8 pixel_renderer(rendering_buffer); agg::renderer_base raw_renderer(pixel_renderer); agg::renderer_scanline_aa_solid> renderer(raw_renderer); - + renderer.color(agg::pixfmt_gray8::color_type(255)); raw_renderer.clear(agg::pixfmt_gray8::color_type(0)); @@ -131,16 +131,16 @@ static Polygons contours_simplified(const Vec2i &grid_size, const double pixel_s bool current = cell_inside[addr] != 0; if (left != current) { lines.push_back( - left ? - Line(Point(c, r+1), Point(c, r )) : + left ? + Line(Point(c, r+1), Point(c, r )) : Line(Point(c, r ), Point(c, r+1))); start_point_to_line_idx.emplace_back(lines.back().a, int(lines.size()) - 1); } if (top != current) { lines.push_back( - top ? + top ? Line(Point(c , r), Point(c+1, r)) : - Line(Point(c+1, r), Point(c , r))); + Line(Point(c+1, r), Point(c , r))); start_point_to_line_idx.emplace_back(lines.back().a, int(lines.size()) - 1); } } @@ -158,7 +158,7 @@ static Polygons contours_simplified(const Vec2i &grid_size, const double pixel_s poly.points.push_back(lines[i_candidate].b); int i_line_current = i_candidate; for (;;) { - auto line_range = std::equal_range(std::begin(start_point_to_line_idx), std::end(start_point_to_line_idx), + auto line_range = std::equal_range(std::begin(start_point_to_line_idx), std::end(start_point_to_line_idx), std::make_pair(lines[i_line_current].b, 0), [](const auto& l, const auto& r) { return l.first < r.first; }); // The interval has to be non empty, there shall be at least one line continuing the current one. assert(line_range.first != line_range.second); @@ -220,7 +220,7 @@ static Polygons contours_simplified(const Vec2i &grid_size, const double pixel_s p(1) += (v(0) < 0) ? - offset : offset; p(0) += (v(1) > 0) ? - offset : offset; pts.push_back(p); - } + } } poly.points = std::move(pts); } @@ -269,7 +269,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) iRun ++; for (const SupportGeneratorLayer *layer : top_contacts) Slic3r::SVG::export_expolygons( - debug_out_path("support-top-contacts-%d-%lf.svg", iRun, layer->print_z), + debug_out_path("support-top-contacts-%d-%lf.svg", iRun, layer->print_z), union_ex(layer->polygons)); #endif /* SLIC3R_DEBUG */ @@ -287,7 +287,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) #ifdef SLIC3R_DEBUG for (size_t layer_id = 0; layer_id < object.layers().size(); ++ layer_id) Slic3r::SVG::export_expolygons( - debug_out_path("support-areas-%d-%lf.svg", iRun, object.layers()[layer_id]->print_z), + debug_out_path("support-areas-%d-%lf.svg", iRun, object.layers()[layer_id]->print_z), union_ex(layer_support_areas[layer_id])); #endif /* SLIC3R_DEBUG */ @@ -306,7 +306,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) #ifdef SLIC3R_DEBUG for (const SupportGeneratorLayer *layer : top_contacts) Slic3r::SVG::export_expolygons( - debug_out_path("support-top-contacts-trimmed-by-object-%d-%lf.svg", iRun, layer->print_z), + debug_out_path("support-top-contacts-trimmed-by-object-%d-%lf.svg", iRun, layer->print_z), union_ex(layer->polygons)); #endif @@ -318,13 +318,13 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) #ifdef SLIC3R_DEBUG for (SupportGeneratorLayersPtr::const_iterator it = intermediate_layers.begin(); it != intermediate_layers.end(); ++ it) Slic3r::SVG::export_expolygons( - debug_out_path("support-base-layers-%d-%lf.svg", iRun, (*it)->print_z), + debug_out_path("support-base-layers-%d-%lf.svg", iRun, (*it)->print_z), union_ex((*it)->polygons)); #endif /* SLIC3R_DEBUG */ BOOST_LOG_TRIVIAL(info) << "Support generator - Trimming top contacts by bottom contacts"; - // Because the top and bottom contacts are thick slabs, they may overlap causing over extrusion + // Because the top and bottom contacts are thick slabs, they may overlap causing over extrusion // and unwanted strong bonds to the object. // Rather trim the top contacts by their overlapping bottom contacts to leave a gap instead of over extruding // top contacts over the bottom contacts. @@ -333,7 +333,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) BOOST_LOG_TRIVIAL(info) << "Support generator - Creating interfaces"; - // Propagate top / bottom contact layers to generate interface layers + // Propagate top / bottom contact layers to generate interface layers // and base interface layers (for soluble interface / non souble base only) SupportGeneratorLayersPtr empty_layers; auto [interface_layers, base_interface_layers] = FFFSupport::generate_interface_layers( @@ -349,11 +349,11 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) #ifdef SLIC3R_DEBUG for (const SupportGeneratorLayer *l : interface_layers) Slic3r::SVG::export_expolygons( - debug_out_path("support-interface-layers-%d-%lf.svg", iRun, l->print_z), + debug_out_path("support-interface-layers-%d-%lf.svg", iRun, l->print_z), union_ex(l->polygons)); for (const SupportGeneratorLayer *l : base_interface_layers) Slic3r::SVG::export_expolygons( - debug_out_path("support-base-interface-layers-%d-%lf.svg", iRun, l->print_z), + debug_out_path("support-base-interface-layers-%d-%lf.svg", iRun, l->print_z), union_ex(l->polygons)); #endif // SLIC3R_DEBUG @@ -378,7 +378,6 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) SupportGeneratorLayersPtr layers_sorted = #endif // SLIC3R_DEBUG generate_support_layers(object, raft_layers, bottom_contacts, top_contacts, intermediate_layers, interface_layers, base_interface_layers); - // Here the upper_layer and lower_layer pointers are left to null at the support layers, BOOST_LOG_TRIVIAL(info) << "Support generator - Generating tool paths"; @@ -496,7 +495,7 @@ class SupportGridPattern public: SupportGridPattern( // Support islands, to be stretched into a grid. Already trimmed with min(lower_layer_offset, m_gap_xy) - const Polygons *support_polygons, + const Polygons *support_polygons, // Trimming polygons, to trim the stretched support islands. support_polygons were already trimmed with trimming_polygons. const Polygons *trimming_polygons, const SupportGridParams ¶ms) : @@ -539,7 +538,7 @@ class SupportGridPattern Vec2i grid_size_raw(int(ceil((m_bbox.max.x() - m_bbox.min.x()) / m_pixel_size)), int(ceil((m_bbox.max.y() - m_bbox.min.y()) / m_pixel_size))); // Overlay macro blocks of (oversampling x oversampling) over the grid. - Vec2i grid_blocks((grid_size_raw.x() + oversampling - 1 - 2) / oversampling, + Vec2i grid_blocks((grid_size_raw.x() + oversampling - 1 - 2) / oversampling, (grid_size_raw.y() + oversampling - 1 - 2) / oversampling); // and resize the grid to fit the macro blocks + one pixel boundary. m_grid_size = grid_blocks * oversampling + Vec2i(2, 2); @@ -617,7 +616,7 @@ class SupportGridPattern // As offset_in_grid may be negative, m_support_polygons may stick slightly outside of islands. // Trim ti with islands. Points samples = island_samples( - offset_in_grid > 0 ? + offset_in_grid > 0 ? // Expanding, thus m_support_polygons are all inside islands. union_ex(*m_support_polygons) : // Shrinking, thus m_support_polygons may be trimmed a tiny bit by islands. @@ -924,7 +923,7 @@ class SupportGridPattern if (expoly.contour.points.size() > 2) { #if 0 pts.push_back(island_sample(expoly)); - #else + #else Polygons polygons = offset(expoly, - 20.f); for (const Polygon &poly : polygons) if (! poly.points.empty()) { @@ -941,7 +940,7 @@ class SupportGridPattern // Sort the points lexicographically, so a binary search could be used to locate points inside a bounding box. std::sort(pts.begin(), pts.end()); return pts; - } + } SupportMaterialStyle m_style; const Polygons *m_support_polygons; @@ -1008,7 +1007,7 @@ namespace SupportMaterialInternal { } return false; } - static bool has_bridging_extrusions(const Layer &layer) + static bool has_bridging_extrusions(const Layer &layer) { for (const LayerRegion *region : layer.regions()) { if (SupportMaterialInternal::has_bridging_perimeters(region->perimeters())) @@ -1060,8 +1059,6 @@ namespace SupportMaterialInternal { } } - - // This is a bridge std::vector PrintObjectSupportMaterial::buildplate_covered(const PrintObject &object) const { // Build support on a build plate only? If so, then collect and union all the surfaces below the current layer. @@ -1121,10 +1118,10 @@ static inline std::tuple detect_overhangs( const Layer &layer, const size_t layer_id, const Polygons &lower_layer_polygons, - const PrintConfig &print_config, + const PrintConfig &print_config, const PrintObjectConfig &object_config, - SupportAnnotations &annotations, - SlicesMarginCache &slices_margin, + SupportAnnotations &annotations, + SlicesMarginCache &slices_margin, const double gap_xy #ifdef SLIC3R_DEBUG , size_t iRun @@ -1147,7 +1144,7 @@ static inline std::tuple detect_overhangs( 0.; float no_interface_offset = 0.f; - if (layer_id == 0) + if (layer_id == 0) { // This is the first object layer, so the object is being printed on a raft and // we're here just to get the object footprint for the raft. @@ -1168,7 +1165,7 @@ static inline std::tuple detect_overhangs( const bool has_enforcer = ! annotations.enforcers_layers.empty() && ! annotations.enforcers_layers[layer_id].empty(); // Cache support trimming polygons derived from lower layer polygons, possible merged with "on build plate only" trimming polygons. - auto slices_margin_update = + auto slices_margin_update = [&slices_margin, &lower_layer, &lower_layer_polygons, buildplate_only, has_enforcer, &annotations, layer_id] (float slices_margin_offset, float no_interface_offset) { if (slices_margin.offset != slices_margin_offset) { @@ -1187,7 +1184,7 @@ static inline std::tuple detect_overhangs( } }; - no_interface_offset = std::accumulate(layer.regions().begin(), layer.regions().end(), FLT_MAX, + no_interface_offset = std::accumulate(layer.regions().begin(), layer.regions().end(), FLT_MAX, [](float acc, const LayerRegion *layerm) { return std::min(acc, float(layerm->flow(frExternalPerimeter).scaled_width())); }); float lower_layer_offset = 0; @@ -1195,11 +1192,11 @@ static inline std::tuple detect_overhangs( // Extrusion width accounts for the roundings of the extrudates. // It is the maximum widh of the extrudate. float fw = float(layerm->flow(frExternalPerimeter).scaled_width()); - lower_layer_offset = - (layer_id < (size_t)object_config.support_material_enforce_layers.value) ? + lower_layer_offset = + (layer_id < (size_t)object_config.support_material_enforce_layers.value) ? // Enforce a full possible support, ignore the overhang angle. 0.f : - (threshold_rad > 0. ? + (threshold_rad > 0. ? // Overhang defined by an angle. float(scale_(lower_layer.height / tan(threshold_rad))) : // Overhang defined by half the extrusion width. @@ -1226,12 +1223,12 @@ static inline std::tuple detect_overhangs( // are not supporting this layer. // However this may lead to a situation where regions at the current layer that are narrow thus not extrudable will generate unnecessary supports. // For example, see GH issue #3094 - opening(lower_layer_polygons, 0.5f * fw, lower_layer_offset + 0.5f * fw, SUPPORT_SURFACES_OFFSET_PARAMETERS)), + opening(lower_layer_polygons, 0.5f * fw, lower_layer_offset + 0.5f * fw, SUPPORT_SURFACES_OFFSET_PARAMETERS)), //FIXME This opening is targeted to reduce very thin regions to support, but it may lead to // no support at all for not so steep overhangs. 0.1f * fw); #else - diff_polygons = + diff_polygons = diff(layerm_polygons, expand(lower_layer_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS)); #endif @@ -1242,7 +1239,7 @@ static inline std::tuple detect_overhangs( } if (! diff_polygons.empty()) { // Offset the support regions back to a full overhang, restrict them to the full overhang. - // This is done to increase size of the supporting columns below, as they are calculated by + // This is done to increase size of the supporting columns below, as they are calculated by // propagating these contact surfaces downwards. diff_polygons = diff( intersection(expand(diff_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS), layerm_polygons), @@ -1266,8 +1263,8 @@ static inline std::tuple detect_overhangs( #ifdef SLIC3R_DEBUG { - ::Slic3r::SVG svg(debug_out_path("support-top-contacts-raw-run%d-layer%d-region%d.svg", - iRun, layer_id, + ::Slic3r::SVG svg(debug_out_path("support-top-contacts-raw-run%d-layer%d-region%d.svg", + iRun, layer_id, std::find_if(layer.regions().begin(), layer.regions().end(), [layerm](const LayerRegion* other){return other == layerm;}) - layer.regions().begin()), get_extents(diff_polygons)); Slic3r::ExPolygons expolys = union_ex(diff_polygons); @@ -1284,8 +1281,8 @@ static inline std::tuple detect_overhangs( #ifdef SLIC3R_DEBUG Slic3r::SVG::export_expolygons( - debug_out_path("support-top-contacts-filtered-run%d-layer%d-region%d-z%f.svg", - iRun, layer_id, + debug_out_path("support-top-contacts-filtered-run%d-layer%d-region%d-z%f.svg", + iRun, layer_id, std::find_if(layer.regions().begin(), layer.regions().end(), [layerm](const LayerRegion* other){return other == layerm;}) - layer.regions().begin(), layer.print_z), union_ex(diff_polygons)); @@ -1296,7 +1293,7 @@ static inline std::tuple detect_overhangs( // Store the exact contour of the overhang for the contact loops. polygons_append(overhang_polygons, diff_polygons); - // Let's define the required contact area by using a max gap of half the upper + // Let's define the required contact area by using a max gap of half the upper // extrusion width and extending the area according to the configured margin. // We increment the area in steps because we don't want our support to overflow // on the other side of the object (if it's very thin). @@ -1355,11 +1352,11 @@ static inline std::tuple detect_overhangs( // Allocate one, possibly two support contact layers. // For "thick" overhangs, one support layer will be generated to support normal extrusions, the other to support the "thick" extrusions. static inline std::pair new_contact_layer( - const PrintConfig &print_config, + const PrintConfig &print_config, const PrintObjectConfig &object_config, const SlicingParameters &slicing_params, const coordf_t support_layer_height_min, - const Layer &layer, + const Layer &layer, SupportGeneratorLayerStorage &layer_storage) { double print_z, bottom_z, height; @@ -1400,7 +1397,7 @@ static inline std::pair new_cont bottom_z = has_raft ? slicing_params.raft_interface_top_z : 0; height = has_raft ? slicing_params.contact_raft_layer_height : min_print_z; } else { - // Don't know the height of the top contact layer yet. The top contact layer is printed with a normal flow and + // Don't know the height of the top contact layer yet. The top contact layer is printed with a normal flow and // its height will be set adaptively later on. } @@ -1449,10 +1446,10 @@ static inline void fill_contact_layer( size_t layer_id, const SlicingParameters &slicing_params, const PrintObjectConfig &object_config, - const SlicesMarginCache &slices_margin, - const Polygons &overhang_polygons, - const Polygons &contact_polygons, - const Polygons &enforcer_polygons, + const SlicesMarginCache &slices_margin, + const Polygons &overhang_polygons, + const Polygons &contact_polygons, + const Polygons &enforcer_polygons, const Polygons &lower_layer_polygons, const Flow &support_material_flow, float no_interface_offset @@ -1467,13 +1464,13 @@ static inline void fill_contact_layer( Polygons lower_layer_polygons_for_dense_interface_cache; auto lower_layer_polygons_for_dense_interface = [&lower_layer_polygons_for_dense_interface_cache, &lower_layer_polygons, no_interface_offset]() -> const Polygons& { if (lower_layer_polygons_for_dense_interface_cache.empty()) - lower_layer_polygons_for_dense_interface_cache = + lower_layer_polygons_for_dense_interface_cache = //FIXME no_interface_offset * 0.6f offset is not quite correct, one shall derive it based on an angle thus depending on layer height. opening(lower_layer_polygons, no_interface_offset * 0.5f, no_interface_offset * (0.6f + 0.5f), SUPPORT_SURFACES_OFFSET_PARAMETERS); return lower_layer_polygons_for_dense_interface_cache; }; - // Stretch support islands into a grid, trim them. + // Stretch support islands into a grid, trim them. SupportGridPattern support_grid_pattern(&contact_polygons, &slices_margin.polygons, grid_params); // 1) Contact polygons will be projected down. To keep the interface and base layers from growing, return a contour a tiny bit smaller than the grid cells. new_layer.contact_polygons = std::make_unique(support_grid_pattern.extract_support(grid_params.expansion_to_propagate, true @@ -1497,7 +1494,7 @@ static inline void fill_contact_layer( // thus some dense interface areas may not get supported. Trim the excess with contact_polygons at the following line. // See for example GH #4874. Polygons dense_interface_polygons_trimmed = intersection(dense_interface_polygons, *new_layer.contact_polygons); - // Stretch support islands into a grid, trim them. + // Stretch support islands into a grid, trim them. SupportGridPattern support_grid_pattern(&dense_interface_polygons_trimmed, &slices_margin.polygons, grid_params); new_layer.polygons = support_grid_pattern.extract_support(grid_params.expansion_to_slice, false #ifdef SLIC3R_DEBUG @@ -1530,7 +1527,7 @@ static inline void fill_contact_layer( if (! enforcer_polygons.empty() && ! slices_margin.all_polygons.empty() && layer_id > 0) { // Support enforcers used together with support enforcers. The support enforcers need to be handled separately from the rest of the support. - + SupportGridPattern support_grid_pattern(&enforcer_polygons, &slices_margin.all_polygons, grid_params); // 1) Contact polygons will be projected down. To keep the interface and base layers from growing, return a contour a tiny bit smaller than the grid cells. new_layer.enforcer_polygons = std::make_unique(support_grid_pattern.extract_support(grid_params.expansion_to_propagate, true @@ -1586,7 +1583,7 @@ static inline void fill_contact_layer( // Even after the contact layer was expanded into a grid, some of the contact islands may be too tiny to be extruded. // Remove those tiny islands from new_layer.polygons and new_layer.contact_polygons. - + // Store the overhang polygons. // The overhang polygons are used in the path generator for planning of the contact loops. // if (this->has_contact_loops()). Compared to "polygons", "overhang_polygons" are snug. @@ -1648,7 +1645,7 @@ SupportGeneratorLayersPtr PrintObjectSupportMaterial::top_contact_layers( { #ifdef SLIC3R_DEBUG static int iRun = 0; - ++ iRun; + ++ iRun; #define SLIC3R_IRUN , iRun #endif /* SLIC3R_DEBUG */ @@ -1665,20 +1662,20 @@ SupportGeneratorLayersPtr PrintObjectSupportMaterial::top_contact_layers( // Note that layer_id < layer->id when raft_layers > 0 as the layer->id incorporates the raft layers. // So layer_id == 0 means first object layer and layer->id == 0 means first print layer if there are no explicit raft layers. size_t num_layers = this->has_support() ? object.layer_count() : 1; - // For each overhang layer, two supporting layers may be generated: One for the overhangs extruded with a bridging flow, + // For each overhang layer, two supporting layers may be generated: One for the overhangs extruded with a bridging flow, // and the other for the overhangs extruded with a normal flow. contact_out.assign(num_layers * 2, nullptr); tbb::parallel_for(tbb::blocked_range(this->has_raft() ? 0 : 1, num_layers), [this, &object, &annotations, &layer_storage, &contact_out] (const tbb::blocked_range& range) { - for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) + for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) { - const Layer &layer = *object.layers()[layer_id]; + const Layer &layer = *object.layers()[layer_id]; const Layer *lower_layer = layer.lower_layer; - if (lower_layer == nullptr && layer_id != 0 && layer.dithered) { + if (lower_layer == nullptr && layer_id != 0 && layer.dithered) continue; - } - Polygons lower_layer_polygons = (layer_id == 0) ? Polygons() : to_polygons(lower_layer->lslices); + + Polygons lower_layer_polygons = (layer_id == 0) ? Polygons() : to_polygons(lower_layer->lslices); SlicesMarginCache slices_margin; auto [overhang_polygons, contact_polygons, enforcer_polygons, no_interface_offset] = @@ -1761,8 +1758,8 @@ static inline SupportGeneratorLayer* detect_bottom_contacts( { { union_safety_offset_ex(polygons_new) }, { "polygons_new", "red", "black", "", scaled(0.1f), 0.5f } } }); #endif /* SLIC3R_DEBUG */ - // Now find whether any projection of the contact surfaces above layer.print_z not yet supported by any - // top surfaces above layer.print_z falls onto this top surface. + // Now find whether any projection of the contact surfaces above layer.print_z not yet supported by any + // top surfaces above layer.print_z falls onto this top surface. // Touching are the contact surfaces supported exclusively by this top surfaces. // Don't use a safety offset as it has been applied during insertion of polygons. if (top.empty()) @@ -1858,7 +1855,7 @@ static inline SupportGeneratorLayer* detect_bottom_contacts( // Returns polygons to print + polygons to propagate downwards. // Called twice: First for normal supports, possibly trimmed by "on build plate only", second for support enforcers not trimmed by "on build plate only". static inline std::pair project_support_to_grid(const Layer &layer, const SupportGridParams &grid_params, const Polygons &overhangs, Polygons *layer_buildplate_covered -#ifdef SLIC3R_DEBUG +#ifdef SLIC3R_DEBUG , size_t iRun, size_t layer_id, const char *debug_name #endif /* SLIC3R_DEBUG */ ) @@ -1891,7 +1888,7 @@ static inline std::pair project_support_to_grid(const Layer // 1) Cache the slice of a support volume. The support volume is expanded by 1/2 of support material flow spacing // to allow a placement of suppot zig-zag snake along the grid lines. task_group_inner.run([&grid_params, &support_grid_pattern, &out -#ifdef SLIC3R_DEBUG +#ifdef SLIC3R_DEBUG , &layer, layer_id, iRun, debug_name #endif /* SLIC3R_DEBUG */ ] { @@ -1909,7 +1906,7 @@ static inline std::pair project_support_to_grid(const Layer // 2) Support polygons will be projected down. To keep the interface and base layers from growing, return a contour a tiny bit smaller than the grid cells. task_group_inner.run([&grid_params, &support_grid_pattern, &out -#ifdef SLIC3R_DEBUG +#ifdef SLIC3R_DEBUG , &layer, layer_id, &overhangs_projection, &trimming, iRun, debug_name #endif /* SLIC3R_DEBUG */ ] { @@ -1936,10 +1933,10 @@ static inline std::pair project_support_to_grid(const Layer } // Generate bottom contact layers supporting the top contact layers. -// For a soluble interface material synchronize the layer heights with the object, +// For a soluble interface material synchronize the layer heights with the object, // otherwise set the layer height to a bridging flow of a support interface nozzle. SupportGeneratorLayersPtr PrintObjectSupportMaterial::bottom_contact_layers_and_layer_support_areas( - const PrintObject &object, const SupportGeneratorLayersPtr &top_contacts, std::vector &buildplate_covered, + const PrintObject &object, const SupportGeneratorLayersPtr &top_contacts, std::vector &buildplate_covered, SupportGeneratorLayerStorage &layer_storage, std::vector &layer_support_areas) const { if (top_contacts.empty()) @@ -2036,13 +2033,13 @@ SupportGeneratorLayersPtr PrintObjectSupportMaterial::bottom_contact_layers_and_ // Filtering the propagated support columns to two extrusions, overlapping by maximum 20%. // float column_propagation_filtering_radius = scaled(0.8 * 0.5 * (m_support_params.support_material_flow.spacing() + m_support_params.support_material_flow.width())); task_group.run([&grid_params, &overhangs_projection, &overhangs_projection_raw, &layer, &layer_support_area, layer_buildplate_covered /* , column_propagation_filtering_radius */ -#ifdef SLIC3R_DEBUG +#ifdef SLIC3R_DEBUG , iRun, layer_id #endif /* SLIC3R_DEBUG */ ] { // buildplate_covered[layer_id] will be consumed here. std::tie(layer_support_area, overhangs_projection) = project_support_to_grid(layer, grid_params, overhangs_projection_raw, layer_buildplate_covered -#ifdef SLIC3R_DEBUG +#ifdef SLIC3R_DEBUG , iRun, layer_id, "general" #endif /* SLIC3R_DEBUG */ ); @@ -2054,12 +2051,12 @@ SupportGeneratorLayersPtr PrintObjectSupportMaterial::bottom_contact_layers_and_ if (! enforcers_projection.empty()) // Project the enforcers polygons downwards, don't trim them with the "buildplate only" polygons. task_group.run([&grid_params, &enforcers_projection, &enforcers_projection_raw, &layer, &layer_support_area_enforcers -#ifdef SLIC3R_DEBUG +#ifdef SLIC3R_DEBUG , iRun, layer_id #endif /* SLIC3R_DEBUG */ ]{ std::tie(layer_support_area_enforcers, enforcers_projection) = project_support_to_grid(layer, grid_params, enforcers_projection_raw, nullptr -#ifdef SLIC3R_DEBUG +#ifdef SLIC3R_DEBUG , iRun, layer_id, "enforcers" #endif /* SLIC3R_DEBUG */ ); @@ -2135,8 +2132,8 @@ SupportGeneratorLayersPtr PrintObjectSupportMaterial::raft_and_intermediate_supp }; std::sort(extremes.begin(), extremes.end(), layer_extreme_lower); - assert(extremes.empty() || - (extremes.front()->extreme_z() > m_slicing_params.raft_interface_top_z - EPSILON && + assert(extremes.empty() || + (extremes.front()->extreme_z() > m_slicing_params.raft_interface_top_z - EPSILON && (m_slicing_params.raft_layers() == 1 || // only raft contact layer extremes.front()->layer_type == SupporLayerType::TopContact || // first extreme is a top contact layer extremes.front()->extreme_z() > m_slicing_params.first_print_layer_height - EPSILON))); @@ -2234,12 +2231,12 @@ SupportGeneratorLayersPtr PrintObjectSupportMaterial::raft_and_intermediate_supp } } else { // Insert intermediate layers. - size_t n_layers_extra = size_t(ceil(dist / m_slicing_params.max_suport_layer_height)); + size_t n_layers_extra = size_t(ceil(dist / m_slicing_params.max_suport_layer_height)); assert(n_layers_extra > 0); coordf_t step = dist / coordf_t(n_layers_extra); if (extr1 != nullptr && extr1->layer_type == SupporLayerType::TopContact && extr1->print_z + m_support_params.support_layer_height_min > extr1->bottom_z + step) { - // The bottom extreme is a bottom of a top surface. Ensure that the gap + // The bottom extreme is a bottom of a top surface. Ensure that the gap // between the 1st intermediate layer print_z and extr1->print_z is not too small. assert(extr1->bottom_z + m_support_params.support_layer_height_min < extr1->print_z + EPSILON); // Generate the first intermediate layer. @@ -2322,10 +2319,10 @@ void PrintObjectSupportMaterial::generate_base_layers( // Counting down due to the way idx_lower_or_equal caches indices to avoid repeated binary search over the complete sequence. for (int idx_intermediate = int(range.end()) - 1; idx_intermediate >= int(range.begin()); -- idx_intermediate) { - BOOST_LOG_TRIVIAL(trace) << "Support generator - generate_base_layers - creating layer " << + BOOST_LOG_TRIVIAL(trace) << "Support generator - generate_base_layers - creating layer " << idx_intermediate << " of " << intermediate_layers.size(); SupportGeneratorLayer &layer_intermediate = *intermediate_layers[idx_intermediate]; - // Layers must be sorted by print_z. + // Layers must be sorted by print_z. assert(idx_intermediate == 0 || layer_intermediate.print_z >= intermediate_layers[idx_intermediate - 1]->print_z); // Find a top_contact layer touching the layer_intermediate from above, if any, and collect its polygons into polygons_new. @@ -2337,7 +2334,7 @@ void PrintObjectSupportMaterial::generate_base_layers( [&layer_intermediate](const Layer* layer) { return layer->print_z <= layer_intermediate.print_z + EPSILON; }); // Polygons to trim polygons_new. - Polygons polygons_trimming; + Polygons polygons_trimming; // Trimming the base layer with any overlapping top layer. // Following cases are recognized: @@ -2346,7 +2343,7 @@ void PrintObjectSupportMaterial::generate_base_layers( // 3) base.print_z > top.print_z && base.bottom_z >= top.bottom_z -> Overlap, which will be solved inside generate_toolpaths() by reducing the base layer height where it overlaps the top layer. No trimming needed here. // 4) base.print_z > top.bottom_z && base.bottom_z < top.bottom_z -> Base overlaps with top.bottom_z. This must not happen. // 5) base.print_z <= top.print_z && base.bottom_z >= top.bottom_z -> Base is fully inside top. Trim base by top. - idx_top_contact_above = idx_lower_or_equal(top_contacts, idx_top_contact_above, + idx_top_contact_above = idx_lower_or_equal(top_contacts, idx_top_contact_above, [&layer_intermediate](const SupportGeneratorLayer *layer){ return layer->bottom_z <= layer_intermediate.print_z - EPSILON; }); // Collect all the top_contact layer intersecting with this layer. for (int idx_top_contact_overlapping = idx_top_contact_above; idx_top_contact_overlapping >= 0; -- idx_top_contact_overlapping) { @@ -2383,13 +2380,13 @@ void PrintObjectSupportMaterial::generate_base_layers( // 3) base.print_z > bottom.bottom_z && base.bottom_z < bottom.bottom_z -> Overlap, which will be solved inside generate_toolpaths() by reducing the bottom layer height where it overlaps the base layer. No trimming needed here. // 4) base.print_z > bottom.print_z && base.bottom_z >= bottom.print_z -> Base overlaps with bottom.print_z. This must not happen. // 5) base.print_z <= bottom.print_z && base.bottom_z >= bottom.bottom_z -> Base is fully inside top. Trim base by top. - idx_bottom_contact_overlapping = idx_lower_or_equal(bottom_contacts, idx_bottom_contact_overlapping, + idx_bottom_contact_overlapping = idx_lower_or_equal(bottom_contacts, idx_bottom_contact_overlapping, [&layer_intermediate](const SupportGeneratorLayer *layer){ return layer->bottom_print_z() <= layer_intermediate.print_z - EPSILON; }); // Collect all the bottom_contacts layer intersecting with this layer. for (int i = idx_bottom_contact_overlapping; i >= 0; -- i) { SupportGeneratorLayer &layer_bottom_overlapping = *bottom_contacts[i]; if (layer_bottom_overlapping.print_z < layer_intermediate.bottom_print_z() + EPSILON) - break; + break; // Base must not overlap with bottom.top_z. assert(! (layer_intermediate.print_z > layer_bottom_overlapping.print_z + EPSILON && layer_intermediate.bottom_z < layer_bottom_overlapping.print_z - EPSILON)); if (layer_intermediate.print_z <= layer_bottom_overlapping.print_z + EPSILON && layer_intermediate.bottom_z >= layer_bottom_overlapping.bottom_print_z() - EPSILON) @@ -2424,8 +2421,8 @@ void PrintObjectSupportMaterial::generate_base_layers( // Fillet the base polygons and trim them again with the top, interface and contact layers. $base->{$i} = diff( offset2( - $base->{$i}, - $fillet_radius_scaled, + $base->{$i}, + $fillet_radius_scaled, -$fillet_radius_scaled, # Use a geometric offsetting for filleting. JT_ROUND, @@ -2503,7 +2500,7 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object( if (object_layer.print_z - bridging_height > support_layer.print_z + gap_extra_above - EPSILON) break; some_region_overlaps = true; - polygons_append(polygons_trimming, + polygons_append(polygons_trimming, offset(region->fill_surfaces().filter_by_type(stBottomBridge), gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS)); if (region->region().config().overhangs.value) // Add bridging perimeters. @@ -2523,12 +2520,6 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object( BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::trim_support_layers_by_object() in parallel - end"; } - - - // Negative offset. There is a chance, that the offsetted hole intersects the outer contour. - - - // Here the upper_layer and lower_layer pointers are left to null at the support layers, /* void PrintObjectSupportMaterial::clip_by_pillars( const PrintObject &object, @@ -2543,7 +2534,7 @@ void PrintObjectSupportMaterial::clip_by_pillars( coord_t pillar_size = scale_(PILLAR_SIZE); coord_t pillar_spacing = scale_(PILLAR_SPACING); - + // A regular grid of pillars, filling the 2D bounding box. Polygons grid; { @@ -2553,7 +2544,7 @@ void PrintObjectSupportMaterial::clip_by_pillars( pillar.points.push_back(Point(pillar_size, 0)); pillar.points.push_back(Point(pillar_size, pillar_size)); pillar.points.push_back(Point(0, pillar_size)); - + // 2D bounding box of the projection of all contact polygons. BoundingBox bbox; for (LayersPtr::const_iterator it = top_contacts.begin(); it != top_contacts.end(); ++ it) @@ -2567,30 +2558,30 @@ void PrintObjectSupportMaterial::clip_by_pillars( } } } - + // add pillars to every layer for my $i (0..n_support_z) { $shape->[$i] = [ @$grid ]; } - + // build capitals for my $i (0..n_support_z) { my $z = $support_z->[$i]; - + my $capitals = intersection( $grid, $contact->{$z} // [], ); - + // work on one pillar at time (if any) to prevent the capitals from being merged - // but store the contact area supported by the capital because we need to make + // but store the contact area supported by the capital because we need to make // sure nothing is left my $contact_supported_by_capitals = []; foreach my $capital (@$capitals) { // enlarge capital tops $capital = offset([$capital], +($pillar_spacing - $pillar_size)/2); push @$contact_supported_by_capitals, @$capital; - + for (my $j = $i-1; $j >= 0; $j--) { my $jz = $support_z->[$j]; $capital = offset($capital, -$self->interface_flow->scaled_width/2); @@ -2598,7 +2589,7 @@ void PrintObjectSupportMaterial::clip_by_pillars( push @{ $shape->[$j] }, @$capital; } } - + // Capitals will not generally cover the whole contact area because there will be // remainders. For now we handle this situation by projecting such unsupported // areas to the ground, just like we would do with a normal support. @@ -2616,10 +2607,10 @@ void PrintObjectSupportMaterial::clip_by_pillars( sub clip_with_shape { my ($self, $support, $shape) = @_; - + foreach my $i (keys %$support) { - // don't clip bottom layer with shape so that we - // can generate a continuous base flange + // don't clip bottom layer with shape so that we + // can generate a continuous base flange // also don't clip raft layers next if $i == 0; next if $i < $self->object_config->raft_layers; diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index fb00f5aeee6..9a50259961e 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -1007,8 +1007,6 @@ std::tuple check_stability(const PrintObject for (const auto &l : unstable_lines_per_slice[slice_idx]) { assert(l.support_point_generated.has_value()); reckon_new_support_point(*l.support_point_generated, create_support_point_position(l.b), float(-EPSILON), Vec2f::Zero()); - - } LD current_slice_lines_distancer({ext_perim_lines_per_slice[slice_idx].begin(), ext_perim_lines_per_slice[slice_idx].end()}); @@ -1182,7 +1180,6 @@ void estimate_malformations(LayerPtrs &layers, const Params ¶ms) std::vector current_layer_lines; for (const LayerRegion *layer_region : l->regions()) { for (const ExtrusionEntity *extrusion : layer_region->perimeters().flatten().entities) { - if (!extrusion->role().is_external_perimeter()) continue; diff --git a/src/libslic3r/TriangleMeshSlicer.hpp b/src/libslic3r/TriangleMeshSlicer.hpp index f17919ac868..5619d247ef8 100644 --- a/src/libslic3r/TriangleMeshSlicer.hpp +++ b/src/libslic3r/TriangleMeshSlicer.hpp @@ -50,13 +50,13 @@ struct MeshSlicingParamsEx : public MeshSlicingParams }; // All the following slicing functions shall produce consistent results with the same mesh, same transformation matrix and slicing parameters. -// Namely, slice_mesh_slabs() shall produce consistent results with slice_mesh() and slice_mesh_ex() in the sense, that projections made by +// Namely, slice_mesh_slabs() shall produce consistent results with slice_mesh() and slice_mesh_ex() in the sense, that projections made by // slice_mesh_slabs() shall fall onto slicing planes produced by slice_mesh(). // // If a slicing plane slices a horizontal face of a mesh exactly, // an upward facing horizontal face is is considered on slicing plane, // while a downward facing horizontal face is considered not on slicing plane. -// +// // slice_mesh_slabs() thus projects an upward facing horizontal slice to the slicing plane, // while slice_mesh_slabs() projects a downward facing horizontal slice to the slicing plane above if it exists. @@ -98,7 +98,7 @@ inline std::vector slice_mesh_ex( } // Slice a triangle set with a set of Z slabs (thick layers). -// The effect is similar to producing the usual top / bottom layers from a sliced mesh by +// The effect is similar to producing the usual top / bottom layers from a sliced mesh by // subtracting layer[i] from layer[i - 1] for the top surfaces resp. // subtracting layer[i] from layer[i + 1] for the bottom surfaces, // with the exception that the triangle set this function processes may not cover the whole top resp. bottom surface. diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 00566d29b18..b45ff1cb22f 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1446,7 +1446,7 @@ void TabPrint::build() optgroup->append_single_option_line("staggered_inner_seams", category_path + "staggered-inner-seams"); optgroup->append_single_option_line("external_perimeters_first", category_path + "external-perimeters-first"); optgroup->append_single_option_line("gap_fill_enabled", category_path + "fill-gaps"); - optgroup->append_single_option_line("perimeter_generator"); + optgroup->append_single_option_line("perimeter_generator"); optgroup->append_single_option_line("z_dither", category_path + "z-dither"); optgroup = page->new_optgroup(L("Fuzzy skin (experimental)")); From 281f9ffe3227f1d1c6104d8ea521f9c53c89483c Mon Sep 17 00:00:00 2001 From: Leonid Raiz Date: Tue, 23 May 2023 15:49:41 -0400 Subject: [PATCH 7/9] Review comments --- src/libslic3r/PrintObject.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 92a269130c9..3cc03678f71 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -302,7 +302,6 @@ void PrintObject::prepare_infill() // Decide what surfaces are to be filled. // Here the stTop / stBottomBridge / stBottom infill is turned to just stInternal if zero top / bottom infill layers are configured. // Also tiny stInternal surfaces are turned to stInternalSolid. - BOOST_LOG_TRIVIAL(info) << "Preparing fill surfaces..." << log_memory_info(); for (auto *layer : m_layers) for (auto *region : layer->m_regions) { From 7f1f6bde0f44e64d10ff548e6d897c0596388402 Mon Sep 17 00:00:00 2001 From: Leonid Raiz Date: Sat, 24 Jun 2023 10:06:54 -0400 Subject: [PATCH 8/9] Bug fixing, enhancements 1. Check and filter out tiny areas made during z-dithering 2. Prefer to use better bahaving classic perimeters for dithered layers 3. Computed dithered overhangs only if support is enabled 4. Turning support on/off in the presence of z-dithering requires reslicing in order to account for potential change in number of layers. 5. Accounted for dithered layers effect on skirt-height handling by GCode. --- src/libslic3r/GCode.cpp | 9 +- src/libslic3r/LayerRegion.cpp | 32 +++---- src/libslic3r/Print.cpp | 25 +++--- src/libslic3r/Print.hpp | 2 + src/libslic3r/PrintObject.cpp | 8 +- src/libslic3r/PrintObjectSlice.cpp | 18 ++-- src/libslic3r/TriangleMeshSlicer.hpp | 8 +- src/libslic3r/ZDither.cpp | 124 +++++++++++++++++++++------ src/libslic3r/ZDither.hpp | 1 + 9 files changed, 155 insertions(+), 72 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index e43261167a7..4ebbc81e6d4 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2082,14 +2082,13 @@ namespace Skirt { // not at the print_z of the interlaced support material layers. std::map> skirt_loops_per_extruder_out; if (print.has_skirt() && ! print.skirt().entities.empty() && layer_tools.has_skirt && + // Bevare of several layers with same print_z but different heights which could happen + // in case of dithered overhanging layers with support + fabs(skirt_done.back() - layer_tools.print_z) > EPSILON && // Not enough skirt layers printed yet. - //FIXME infinite or high skirt does not make sense for sequential print! - (skirt_done.size() < (size_t)print.config().skirt_height.value || print.has_infinite_skirt())) { + (layer_tools.print_z < print.skirt_print_z() + EPSILON || print.has_infinite_skirt())) { bool valid = ! skirt_done.empty() && skirt_done.back() < layer_tools.print_z - EPSILON; assert(valid); - // This print_z has not been extruded yet (sequential print) - // FIXME: The skirt_done should not be empty at this point. The check is a workaround - // of https://github.com/prusa3d/PrusaSlicer/issues/5652, but it deserves a real fix. if (valid) { #if 0 // Prime just the first printing extruder. This is original Slic3r's implementation. diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index 73f225c577c..8d58bf4745d 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -113,28 +113,24 @@ void LayerRegion::make_perimeters( auto perimeters_begin = uint32_t(m_perimeters.size()); auto gap_fills_begin = uint32_t(m_thin_fills.size()); auto fill_expolygons_begin = uint32_t(fill_expolygons.size()); - if (this->layer()->object()->config().perimeter_generator.value == PerimeterGeneratorType::Arachne && !spiral_vase) - PerimeterGenerator::process_arachne( + bool use_arachne = this->layer()->object()->config().perimeter_generator.value == PerimeterGeneratorType::Arachne && !spiral_vase; + bool dithered_layer = this->layer()->dithered; + // arachne width averaging is not so good for thin areas of dithered layers becuase it results in routh attachment/seams of + // dithered layers to non-dithered (regular) layers. On the other hand for very narrow dithered layers classic algothim + // sometimes makes no perimeters at all and in this case we better employ arachne algorithm (even if user selected classic). + if (!use_arachne || dithered_layer) + PerimeterGenerator::process_classic( // input: - params, - surface, - lower_slices, - lower_layer_polygons_cache, + params, surface, lower_slices, lower_layer_polygons_cache, // output: - m_perimeters, - m_thin_fills, - fill_expolygons); - else - PerimeterGenerator::process_classic( + m_perimeters, m_thin_fills, fill_expolygons); + if (dithered_layer && m_perimeters.empty() || !dithered_layer && use_arachne) + PerimeterGenerator::process_arachne( // input: - params, - surface, - lower_slices, - lower_layer_polygons_cache, + params, surface, lower_slices, lower_layer_polygons_cache, // output: - m_perimeters, - m_thin_fills, - fill_expolygons); + m_perimeters, m_thin_fills, fill_expolygons); + perimeter_and_gapfill_ranges.emplace_back( ExtrusionRange{ perimeters_begin, uint32_t(m_perimeters.size()) }, ExtrusionRange{ gap_fills_begin, uint32_t(m_thin_fills.size()) }); diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 4290c38c777..59646ce5666 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1019,18 +1019,17 @@ void Print::_make_skirt() // The skirt_height option from config is expressed in layers, but our // object might have different layer heights, so we need to find the print_z // of the highest layer involved. - // Note that unless has_infinite_skirt() == true - // the actual skirt might not reach this $skirt_height_z value since the print - // order of objects on each layer is not guaranteed and will not generally - // include the thickest object first. It is just guaranteed that a skirt is - // prepended to the first 'n' layers (with 'n' = skirt_height). - // $skirt_height_z in this case is the highest possible skirt height for safety. - coordf_t skirt_height_z = 0.; + m_skirt_height_z = 0.; for (const PrintObject *object : m_objects) { - size_t skirt_layers = this->has_infinite_skirt() ? - object->layer_count() : - std::min(size_t(m_config.skirt_height.value), object->layer_count()); - skirt_height_z = std::max(skirt_height_z, object->m_layers[skirt_layers-1]->print_z); + size_t skirt_layer = this->has_infinite_skirt() ? object->layer_count() - 1: 0; + if (!this->has_infinite_skirt()) { + for (size_t i = 0, non_dithered = 0; i < object->layer_count() && non_dithered < size_t(m_config.skirt_height.value); i++) + if (!object->m_layers[i]->dithered) { // Skip dithered layers + skirt_layer = i; + non_dithered += 1; + } + } + m_skirt_height_z = std::max(m_skirt_height_z, object->m_layers[skirt_layer]->print_z); } // Collect points from all layers contained in skirt height. @@ -1039,7 +1038,7 @@ void Print::_make_skirt() Points object_points; // Get object layers up to skirt_height_z. for (const Layer *layer : object->m_layers) { - if (layer->print_z > skirt_height_z) + if (layer->print_z > m_skirt_height_z) break; for (const ExPolygon &expoly : layer->lslices) // Collect the outer contour points only, ignore holes for the calculation of the convex hull. @@ -1047,7 +1046,7 @@ void Print::_make_skirt() } // Get support layers up to skirt_height_z. for (const SupportLayer *layer : object->support_layers()) { - if (layer->print_z > skirt_height_z) + if (layer->print_z > m_skirt_height_z) break; layer->support_fills.collect_points(object_points); } diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index a7b88f2fd42..e69e2d516d1 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -587,6 +587,7 @@ class Print : public PrintBaseWithState double skirt_first_layer_height() const; Flow brim_flow() const; Flow skirt_flow() const; + coordf_t skirt_print_z() const { return m_skirt_height_z; }; std::vector object_extruders() const; std::vector support_material_extruders() const; @@ -687,6 +688,7 @@ class Print : public PrintBaseWithState // It does NOT encompass MMU/MMU2 starting (wipe) areas. Polygon m_first_layer_convex_hull; Points m_skirt_convex_hull; + coordf_t m_skirt_height_z; // Following section will be consumed by the GCodeGenerator. ToolOrdering m_tool_ordering; diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index db3a4c64952..4582c7799df 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -700,11 +700,12 @@ bool PrintObject::invalidate_state_by_config_options( steps.emplace_back(posSlice); } else if (opt_key == "support_material") { steps.emplace_back(posSupportMaterial); - if (m_config.support_material_contact_distance == 0.) { + if (m_config.support_material_contact_distance == 0. || m_config.z_dither) { // Enabling / disabling supports while soluble support interface is enabled. // This changes the bridging logic (bridging enabled without supports, disabled with supports). - // Reset everything. // See GH #1482 for details. + // Similarly enabling / disabling supports affects the logic of dithered layer calculations + // Reset everything. steps.emplace_back(posSlice); } } else if ( @@ -817,7 +818,8 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "wall_transition_angle" || opt_key == "wall_distribution_count" || opt_key == "min_feature_size" - || opt_key == "min_bead_width") { + || opt_key == "min_bead_width" + || opt_key == "z_dither") { steps.emplace_back(posSlice); } else if ( opt_key == "seam_position" diff --git a/src/libslic3r/PrintObjectSlice.cpp b/src/libslic3r/PrintObjectSlice.cpp index 56f4b1207c5..3fb7f3792a5 100644 --- a/src/libslic3r/PrintObjectSlice.cpp +++ b/src/libslic3r/PrintObjectSlice.cpp @@ -56,7 +56,7 @@ static std::vector slice_volume( if (params2.trafo.rotation().determinant() < 0.) its_flip_triangles(its); std::vector expolys = slice_mesh_ex(its, zs, params2, throw_on_cancel_callback); - if (params2.z_dither) + if (params2.z_dither_mode != Z_dither_mode::None) expolys = z_dither(its, zs, params2, expolys, sublayers, throw_on_cancel_callback); else *sublayers = std::vector(expolys.size()); @@ -159,7 +159,9 @@ static std::vector slice_volumes_inner( params_base.resolution = print_config.resolution.value; const std::vector &diameters = print_config.nozzle_diameter.values; params_base.nozzle_diameter = float(*std::min_element(diameters.begin(), diameters.end())); - params_base.z_dither = print_object_config.z_dither; + params_base.z_dither_mode = print_object_config.z_dither + ? (print_object_config.support_material ? Z_dither_mode::Both : Z_dither_mode::Upward) + : Z_dither_mode::None; switch (print_object_config.slicing_mode.value) { case SlicingMode::Regular: params_base.mode = MeshSlicingParams::SlicingMode::Regular; break; @@ -748,9 +750,9 @@ LayerPtrs add_dithering_layers(const LayerPtrs & layers, { LayerPtrs original(layers); LayerPtrs resulting; - Layer * newLayer[4]; for (int ll = 0; ll < original.size(); ll++) { + Layer *newLayer[4] = {nullptr, nullptr, nullptr, nullptr}; if (std::any_of(volume_sublayers.begin(), volume_sublayers.end(), [&ll](VolumeSublayers &v_sub) { return !v_sub.sublayers[ll].bottom_.empty(); @@ -765,8 +767,9 @@ LayerPtrs add_dithering_layers(const LayerPtrs & layers, return !v_sub.sublayers[ll].halfUp_.empty(); })) { newLayer[1] = make_dithered_layer(original[ll], 0.25, 0.75); - newLayer[1]->lower_layer = newLayer[0]; // must be != nullptr - newLayer[0]->upper_layer = newLayer[1]; + newLayer[1]->lower_layer = newLayer[0]; + if (newLayer[0]) + newLayer[0]->upper_layer = newLayer[1]; merge_sublayers_to_slices(volume_slices, volume_sublayers, 1, ll, resulting.size()); resulting.push_back(newLayer[1]); } @@ -784,8 +787,9 @@ LayerPtrs add_dithering_layers(const LayerPtrs & layers, })) { newLayer[3] = make_dithered_layer(original[ll], 0.75, 1.); newLayer[3]->upper_layer = original[ll]->upper_layer; - newLayer[3]->lower_layer = newLayer[2]; // must be != nullptr - newLayer[2]->upper_layer = newLayer[3]; + newLayer[3]->lower_layer = newLayer[2]; + if (newLayer[2]) + newLayer[2]->upper_layer = newLayer[3]; merge_sublayers_to_slices(volume_slices, volume_sublayers, 3, ll, resulting.size()); resulting.push_back(newLayer[3]); } diff --git a/src/libslic3r/TriangleMeshSlicer.hpp b/src/libslic3r/TriangleMeshSlicer.hpp index 5619d247ef8..1cb0063634e 100644 --- a/src/libslic3r/TriangleMeshSlicer.hpp +++ b/src/libslic3r/TriangleMeshSlicer.hpp @@ -35,6 +35,12 @@ struct MeshSlicingParams Transform3d trafo { Transform3d::Identity() }; }; +enum class Z_dither_mode { + None, // No z-dithering + Upward, // Dither just upward facing surfaces, no overhangs + Both // Dither Both upward andf downward surfacefaces. Requires presence of support. +}; + struct MeshSlicingParamsEx : public MeshSlicingParams { // Morphological closing operation when creating output expolygons, unscaled. @@ -46,7 +52,7 @@ struct MeshSlicingParamsEx : public MeshSlicingParams double resolution { 0 }; // nozzle diameter (needed for z_dithering optimization) float nozzle_diameter{0}; - bool z_dither{false}; // indicates if z-dithering is needed + Z_dither_mode z_dither_mode = Z_dither_mode::None; }; // All the following slicing functions shall produce consistent results with the same mesh, same transformation matrix and slicing parameters. diff --git a/src/libslic3r/ZDither.cpp b/src/libslic3r/ZDither.cpp index ec17e7ecdbe..c285e00e857 100644 --- a/src/libslic3r/ZDither.cpp +++ b/src/libslic3r/ZDither.cpp @@ -1,6 +1,7 @@ #include "ZDither.hpp" #include "ExPolygon.hpp" #include "TriangleMeshSlicer.hpp" +#include "Utils.hpp" namespace Slic3r { @@ -15,6 +16,7 @@ void midslice_zs(const indexed_triangle_set &mesh, const std::vector & zs, const Transform3d & trafo, float nozzle_diameter, + bool dither_both_up_n_down, std::vector * mid_zs, std::vector * upwrd_mididx, std::vector * dnwrd_mididx) @@ -72,7 +74,7 @@ void midslice_zs(const indexed_triangle_set &mesh, if (nozzle_diameter * sqrt(1 - norm[2] * norm[2]) * nDia < fabs(norm[2]) * layerHeight[cc]) { if (norm[2] > 0) upwrd_mididx->at(cc) = cc; - else + else if (dither_both_up_n_down) dnwrd_mididx->at(cc) = cc; } } @@ -94,7 +96,68 @@ void midslice_zs(const indexed_triangle_set &mesh, return; } -std::vector apply_z_dither(std::vector &expolys, +namespace { +void export_sublayers_to_svg(size_t layer_id, + const ExPolygons &whole, + const ExPolygons &bottom, + const ExPolygons &middleUp, + const ExPolygons &middleDn, + const ExPolygons &top) +{ + BoundingBox bbox = get_extents(whole); + bbox.merge(get_extents(bottom)); + bbox.merge(get_extents(middleUp)); + bbox.merge(get_extents(middleDn)); + bbox.merge(get_extents(top)); + SVG svg(debug_out_path("z-dither_%d_sublayers.svg", layer_id).c_str(), bbox); + svg.draw(whole, "green"); + svg.draw(bottom, "lightcoral"); + svg.draw_outline(middleUp, "red", "red", scale_(0.05)); + svg.draw(top, "cyan"); + svg.draw_outline(middleDn, "blue", "blue", scale_(0.05)); + svg.Close(); +} + +void export_cuts_to_svg(size_t layer_id, const ExPolygons &expoly, const ExPolygons &below, const ExPolygons &above) +{ + BoundingBox bbox = get_extents(expoly); + bbox.merge(get_extents(below)); + bbox.merge(get_extents(above)); + SVG svg(debug_out_path("z-dither_%d_cuts.svg", layer_id).c_str(), bbox); + svg.draw_outline(below, "lightcoral", "lightcoral", scale_(0.05)); + svg.draw_outline(expoly, "green", "green", scale_(0.05)); + svg.draw_outline(above, "cyan", "cyan", scale_(0.05)); + svg.Close(); +} + +// Subtraction of ExPolygons that are very close to each other along some portion of their boundary +// may result in ExPolygons covering tiny, very narrow areas. We need to filter them out. +ExPolygons &filter_tiny_areas(ExPolygons &expolys, double min) +{ + expolys.erase(std::remove_if(expolys.begin(), expolys.end(), [&min](ExPolygon &poly) { + // Use area and perimeter to estimate width of a closed polygon + double area = poly.contour.area(); + double perimeter = poly.contour.length(); + // For cirle area/perimeter = width / 4; For thin rectangle area/perimeter = width / 2. + // Arbitrary shape will have average width less than width of a circle + double width = area / perimeter * 4; + return width < scale_(min); + }), expolys.end()); + return expolys; +} + +int total_num_contours(const ExPolygons &expolys) +{ + int total = 0; + for (const ExPolygon &poly : expolys) + total += poly.num_contours(); + return total; +} + +} // namespace + +std::vector apply_z_dither(std::vector &expolys, + double min_contour_width, std::vector &expolys_mid, const std::vector & upwrd_mididx, const std::vector & dnwrd_mididx, @@ -107,18 +170,22 @@ std::vector apply_z_dither(std::vector &expolys, out[0] = std::move(expolys[0]); // Do not make sublayers of first layer for (auto ll = 1; ll < expolys.size(); ll++) { // idx0 - bottom of layer, idx1 - top of layer - int upwrd_idx0 = upwrd_mididx[ll - 1]; - int dnwrd_idx0 = dnwrd_mididx[ll - 1]; - int upwrd_idx1 = upwrd_mididx[ll]; - int dnwrd_idx1 = dnwrd_mididx[ll]; + int upwrd_idx0 = upwrd_mididx[ll - 1]; + int dnwrd_idx0 = dnwrd_mididx[ll - 1]; + int upwrd_idx1 = upwrd_mididx[ll]; + int dnwrd_idx1 = dnwrd_mididx[ll]; auto useMidCut = [](int idx) { return idx != -1; }; + if (useMidCut(dnwrd_idx0) && useMidCut(dnwrd_idx1) && + total_num_contours(expolys_mid[dnwrd_idx0]) != total_num_contours(expolys_mid[dnwrd_idx1])) { + dnwrd_idx0 = dnwrd_idx1 = -1; // Don't mess up with bridging even in the presence of support + } if (!useMidCut(upwrd_idx0) && !useMidCut(dnwrd_idx0) && !useMidCut(upwrd_idx1) && !useMidCut(dnwrd_idx1)) { out[ll] = std::move(expolys[ll]); continue; } else { - ExPolygons bottom, middleUp, middleDn, top; + ExPolygons bottom, middleUp, middleDn, top, whole; if (useMidCut(upwrd_idx0) || useMidCut(upwrd_idx1)) { bottom = std::move( @@ -138,27 +205,33 @@ std::vector apply_z_dither(std::vector &expolys, middleDn = std::move(diff_ex(expolys[ll], expolys_mid[dnwrd_idx0])); } - ExPolygons whole; - if (useMidCut(upwrd_idx1) && useMidCut(dnwrd_idx0)) - whole = std::move(intersection_ex(expolys_mid[dnwrd_idx0], expolys_mid[upwrd_idx1])); - else if (useMidCut(upwrd_idx1)) - whole = std::move(intersection_ex(expolys[ll], expolys_mid[upwrd_idx1])); - else if (useMidCut(dnwrd_idx0)) - whole = std::move(intersection_ex(expolys[ll], expolys_mid[dnwrd_idx0])); + filter_tiny_areas(bottom, min_contour_width); + filter_tiny_areas(middleUp, min_contour_width); + filter_tiny_areas(middleDn, min_contour_width); + filter_tiny_areas(top, min_contour_width); + + if (!bottom.empty() || !top.empty()) { + whole = std::move(diff_ex(expolys[ll], top)); + whole = std::move(diff_ex(filter_tiny_areas(whole, min_contour_width), bottom)); + filter_tiny_areas(whole, min_contour_width); + } else { out[ll] = std::move(expolys[ll]); continue; } + + #if 0 + export_sublayers_to_svg(ll, whole, bottom, middleUp, middleDn, top); + export_cuts_to_svg(ll, expolys[ll], + useMidCut(dnwrd_idx0) ? expolys_mid[dnwrd_idx0] : (useMidCut(upwrd_idx0) ? expolys_mid[upwrd_idx0] : ExPolygons()), + useMidCut(dnwrd_idx1) ? expolys_mid[dnwrd_idx1] : (useMidCut(upwrd_idx1) ? expolys_mid[upwrd_idx1] : ExPolygons())); + #endif + out[ll] = std::move(whole); - if (bottom.empty() != middleUp.empty() || middleDn.empty() != top.empty()) { - BOOST_LOG_TRIVIAL(error) - << "z-dithering: internal error"; - } else { - sublayers->at(ll).bottom_ = std::move(bottom); - sublayers->at(ll).halfUp_ = std::move(middleUp); - sublayers->at(ll).halfDn_ = std::move(middleDn); - sublayers->at(ll).top_ = std::move(top); - } + sublayers->at(ll).bottom_ = std::move(bottom); + sublayers->at(ll).halfUp_ = std::move(middleUp); + sublayers->at(ll).halfDn_ = std::move(middleDn); + sublayers->at(ll).top_ = std::move(top); } } return out; @@ -175,10 +248,11 @@ std::vector z_dither(const indexed_triangle_set &mesh, std::vector mid_zs; std::vector upwrd_mididx; std::vector dnwrd_mididx; - midslice_zs(mesh, zs, params.trafo, params.nozzle_diameter, &mid_zs, &upwrd_mididx, &dnwrd_mididx); + midslice_zs(mesh, zs, params.trafo, params.nozzle_diameter, params.z_dither_mode == Z_dither_mode::Both, + &mid_zs, &upwrd_mididx, &dnwrd_mididx); if (!mid_zs.empty()) { std::vector expolys_mid = slice_mesh_ex(mesh, mid_zs, params, throw_on_cancel_callback); - return apply_z_dither(expolys, expolys_mid, upwrd_mididx, dnwrd_mididx, sublayers); + return apply_z_dither(expolys, params.nozzle_diameter / 10, expolys_mid, upwrd_mididx, dnwrd_mididx, sublayers); } else { *sublayers = std::vector(expolys.size()); return expolys; diff --git a/src/libslic3r/ZDither.hpp b/src/libslic3r/ZDither.hpp index f547bd29ad6..6d4e19c80c7 100644 --- a/src/libslic3r/ZDither.hpp +++ b/src/libslic3r/ZDither.hpp @@ -27,6 +27,7 @@ void midslice_zs(const indexed_triangle_set &mesh, std::vector * dnwrd_mididx); std::vector apply_z_dither(std::vector &layers, + double min_contour_width, std::vector &mid_layers, const std::vector &do_low, const std::vector &do_high, From 84318be65c53d597415493d21e996e769c20ae07 Mon Sep 17 00:00:00 2001 From: Leonid Raiz Date: Sat, 24 Jun 2023 10:06:54 -0400 Subject: [PATCH 9/9] Bug fixing, enhancements 1. Check and filter out tiny areas made during z-dithering 2. Prefer to use better bahaving classic perimeters for dithered layers 3. Computed dithered overhangs only if support is enabled 4. Turning support on/off in the presence of z-dithering requires reslicing in order to account for potential change in number of layers. 5. Accounted for dithered layers effect on skirt-height handling by GCode. --- src/libslic3r/GCode.cpp | 9 +- src/libslic3r/LayerRegion.cpp | 15 +++- src/libslic3r/Print.cpp | 25 +++--- src/libslic3r/Print.hpp | 2 + src/libslic3r/PrintObject.cpp | 8 +- src/libslic3r/PrintObjectSlice.cpp | 18 ++-- src/libslic3r/TriangleMeshSlicer.hpp | 8 +- src/libslic3r/ZDither.cpp | 124 +++++++++++++++++++++------ src/libslic3r/ZDither.hpp | 1 + 9 files changed, 152 insertions(+), 58 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index e43261167a7..4ebbc81e6d4 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2082,14 +2082,13 @@ namespace Skirt { // not at the print_z of the interlaced support material layers. std::map> skirt_loops_per_extruder_out; if (print.has_skirt() && ! print.skirt().entities.empty() && layer_tools.has_skirt && + // Bevare of several layers with same print_z but different heights which could happen + // in case of dithered overhanging layers with support + fabs(skirt_done.back() - layer_tools.print_z) > EPSILON && // Not enough skirt layers printed yet. - //FIXME infinite or high skirt does not make sense for sequential print! - (skirt_done.size() < (size_t)print.config().skirt_height.value || print.has_infinite_skirt())) { + (layer_tools.print_z < print.skirt_print_z() + EPSILON || print.has_infinite_skirt())) { bool valid = ! skirt_done.empty() && skirt_done.back() < layer_tools.print_z - EPSILON; assert(valid); - // This print_z has not been extruded yet (sequential print) - // FIXME: The skirt_done should not be empty at this point. The check is a workaround - // of https://github.com/prusa3d/PrusaSlicer/issues/5652, but it deserves a real fix. if (valid) { #if 0 // Prime just the first printing extruder. This is original Slic3r's implementation. diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index 73f225c577c..0f11b9244b2 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -113,8 +113,15 @@ void LayerRegion::make_perimeters( auto perimeters_begin = uint32_t(m_perimeters.size()); auto gap_fills_begin = uint32_t(m_thin_fills.size()); auto fill_expolygons_begin = uint32_t(fill_expolygons.size()); - if (this->layer()->object()->config().perimeter_generator.value == PerimeterGeneratorType::Arachne && !spiral_vase) - PerimeterGenerator::process_arachne( + bool use_arachne = this->layer()->object()->config().perimeter_generator.value == PerimeterGeneratorType::Arachne && !spiral_vase; + bool dithered_layer = this->layer()->dithered; + // arachne width averaging is not so good for thin areas of dithered layers becuase it may generate open contours + // which make routh attachment/seams between dithered layers and adjacent non-dithered (regular) layers. + // On the other hand, for very narrow regions, classic algothim sometimes makes no perimeters at all. + // In this case we better fall back on arachne algorithm (even if user selected classic). + // Thus the logic of trying classic first for dithered layers and if it makes no perimeters then trying arachne. + if (!use_arachne || dithered_layer) + PerimeterGenerator::process_classic( // input: params, surface, @@ -124,8 +131,8 @@ void LayerRegion::make_perimeters( m_perimeters, m_thin_fills, fill_expolygons); - else - PerimeterGenerator::process_classic( + if (dithered_layer && m_perimeters.empty() || !dithered_layer && use_arachne) + PerimeterGenerator::process_arachne( // input: params, surface, diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 4290c38c777..59646ce5666 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1019,18 +1019,17 @@ void Print::_make_skirt() // The skirt_height option from config is expressed in layers, but our // object might have different layer heights, so we need to find the print_z // of the highest layer involved. - // Note that unless has_infinite_skirt() == true - // the actual skirt might not reach this $skirt_height_z value since the print - // order of objects on each layer is not guaranteed and will not generally - // include the thickest object first. It is just guaranteed that a skirt is - // prepended to the first 'n' layers (with 'n' = skirt_height). - // $skirt_height_z in this case is the highest possible skirt height for safety. - coordf_t skirt_height_z = 0.; + m_skirt_height_z = 0.; for (const PrintObject *object : m_objects) { - size_t skirt_layers = this->has_infinite_skirt() ? - object->layer_count() : - std::min(size_t(m_config.skirt_height.value), object->layer_count()); - skirt_height_z = std::max(skirt_height_z, object->m_layers[skirt_layers-1]->print_z); + size_t skirt_layer = this->has_infinite_skirt() ? object->layer_count() - 1: 0; + if (!this->has_infinite_skirt()) { + for (size_t i = 0, non_dithered = 0; i < object->layer_count() && non_dithered < size_t(m_config.skirt_height.value); i++) + if (!object->m_layers[i]->dithered) { // Skip dithered layers + skirt_layer = i; + non_dithered += 1; + } + } + m_skirt_height_z = std::max(m_skirt_height_z, object->m_layers[skirt_layer]->print_z); } // Collect points from all layers contained in skirt height. @@ -1039,7 +1038,7 @@ void Print::_make_skirt() Points object_points; // Get object layers up to skirt_height_z. for (const Layer *layer : object->m_layers) { - if (layer->print_z > skirt_height_z) + if (layer->print_z > m_skirt_height_z) break; for (const ExPolygon &expoly : layer->lslices) // Collect the outer contour points only, ignore holes for the calculation of the convex hull. @@ -1047,7 +1046,7 @@ void Print::_make_skirt() } // Get support layers up to skirt_height_z. for (const SupportLayer *layer : object->support_layers()) { - if (layer->print_z > skirt_height_z) + if (layer->print_z > m_skirt_height_z) break; layer->support_fills.collect_points(object_points); } diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index a7b88f2fd42..e69e2d516d1 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -587,6 +587,7 @@ class Print : public PrintBaseWithState double skirt_first_layer_height() const; Flow brim_flow() const; Flow skirt_flow() const; + coordf_t skirt_print_z() const { return m_skirt_height_z; }; std::vector object_extruders() const; std::vector support_material_extruders() const; @@ -687,6 +688,7 @@ class Print : public PrintBaseWithState // It does NOT encompass MMU/MMU2 starting (wipe) areas. Polygon m_first_layer_convex_hull; Points m_skirt_convex_hull; + coordf_t m_skirt_height_z; // Following section will be consumed by the GCodeGenerator. ToolOrdering m_tool_ordering; diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index db3a4c64952..4582c7799df 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -700,11 +700,12 @@ bool PrintObject::invalidate_state_by_config_options( steps.emplace_back(posSlice); } else if (opt_key == "support_material") { steps.emplace_back(posSupportMaterial); - if (m_config.support_material_contact_distance == 0.) { + if (m_config.support_material_contact_distance == 0. || m_config.z_dither) { // Enabling / disabling supports while soluble support interface is enabled. // This changes the bridging logic (bridging enabled without supports, disabled with supports). - // Reset everything. // See GH #1482 for details. + // Similarly enabling / disabling supports affects the logic of dithered layer calculations + // Reset everything. steps.emplace_back(posSlice); } } else if ( @@ -817,7 +818,8 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "wall_transition_angle" || opt_key == "wall_distribution_count" || opt_key == "min_feature_size" - || opt_key == "min_bead_width") { + || opt_key == "min_bead_width" + || opt_key == "z_dither") { steps.emplace_back(posSlice); } else if ( opt_key == "seam_position" diff --git a/src/libslic3r/PrintObjectSlice.cpp b/src/libslic3r/PrintObjectSlice.cpp index 56f4b1207c5..3fb7f3792a5 100644 --- a/src/libslic3r/PrintObjectSlice.cpp +++ b/src/libslic3r/PrintObjectSlice.cpp @@ -56,7 +56,7 @@ static std::vector slice_volume( if (params2.trafo.rotation().determinant() < 0.) its_flip_triangles(its); std::vector expolys = slice_mesh_ex(its, zs, params2, throw_on_cancel_callback); - if (params2.z_dither) + if (params2.z_dither_mode != Z_dither_mode::None) expolys = z_dither(its, zs, params2, expolys, sublayers, throw_on_cancel_callback); else *sublayers = std::vector(expolys.size()); @@ -159,7 +159,9 @@ static std::vector slice_volumes_inner( params_base.resolution = print_config.resolution.value; const std::vector &diameters = print_config.nozzle_diameter.values; params_base.nozzle_diameter = float(*std::min_element(diameters.begin(), diameters.end())); - params_base.z_dither = print_object_config.z_dither; + params_base.z_dither_mode = print_object_config.z_dither + ? (print_object_config.support_material ? Z_dither_mode::Both : Z_dither_mode::Upward) + : Z_dither_mode::None; switch (print_object_config.slicing_mode.value) { case SlicingMode::Regular: params_base.mode = MeshSlicingParams::SlicingMode::Regular; break; @@ -748,9 +750,9 @@ LayerPtrs add_dithering_layers(const LayerPtrs & layers, { LayerPtrs original(layers); LayerPtrs resulting; - Layer * newLayer[4]; for (int ll = 0; ll < original.size(); ll++) { + Layer *newLayer[4] = {nullptr, nullptr, nullptr, nullptr}; if (std::any_of(volume_sublayers.begin(), volume_sublayers.end(), [&ll](VolumeSublayers &v_sub) { return !v_sub.sublayers[ll].bottom_.empty(); @@ -765,8 +767,9 @@ LayerPtrs add_dithering_layers(const LayerPtrs & layers, return !v_sub.sublayers[ll].halfUp_.empty(); })) { newLayer[1] = make_dithered_layer(original[ll], 0.25, 0.75); - newLayer[1]->lower_layer = newLayer[0]; // must be != nullptr - newLayer[0]->upper_layer = newLayer[1]; + newLayer[1]->lower_layer = newLayer[0]; + if (newLayer[0]) + newLayer[0]->upper_layer = newLayer[1]; merge_sublayers_to_slices(volume_slices, volume_sublayers, 1, ll, resulting.size()); resulting.push_back(newLayer[1]); } @@ -784,8 +787,9 @@ LayerPtrs add_dithering_layers(const LayerPtrs & layers, })) { newLayer[3] = make_dithered_layer(original[ll], 0.75, 1.); newLayer[3]->upper_layer = original[ll]->upper_layer; - newLayer[3]->lower_layer = newLayer[2]; // must be != nullptr - newLayer[2]->upper_layer = newLayer[3]; + newLayer[3]->lower_layer = newLayer[2]; + if (newLayer[2]) + newLayer[2]->upper_layer = newLayer[3]; merge_sublayers_to_slices(volume_slices, volume_sublayers, 3, ll, resulting.size()); resulting.push_back(newLayer[3]); } diff --git a/src/libslic3r/TriangleMeshSlicer.hpp b/src/libslic3r/TriangleMeshSlicer.hpp index 5619d247ef8..1cb0063634e 100644 --- a/src/libslic3r/TriangleMeshSlicer.hpp +++ b/src/libslic3r/TriangleMeshSlicer.hpp @@ -35,6 +35,12 @@ struct MeshSlicingParams Transform3d trafo { Transform3d::Identity() }; }; +enum class Z_dither_mode { + None, // No z-dithering + Upward, // Dither just upward facing surfaces, no overhangs + Both // Dither Both upward andf downward surfacefaces. Requires presence of support. +}; + struct MeshSlicingParamsEx : public MeshSlicingParams { // Morphological closing operation when creating output expolygons, unscaled. @@ -46,7 +52,7 @@ struct MeshSlicingParamsEx : public MeshSlicingParams double resolution { 0 }; // nozzle diameter (needed for z_dithering optimization) float nozzle_diameter{0}; - bool z_dither{false}; // indicates if z-dithering is needed + Z_dither_mode z_dither_mode = Z_dither_mode::None; }; // All the following slicing functions shall produce consistent results with the same mesh, same transformation matrix and slicing parameters. diff --git a/src/libslic3r/ZDither.cpp b/src/libslic3r/ZDither.cpp index ec17e7ecdbe..c285e00e857 100644 --- a/src/libslic3r/ZDither.cpp +++ b/src/libslic3r/ZDither.cpp @@ -1,6 +1,7 @@ #include "ZDither.hpp" #include "ExPolygon.hpp" #include "TriangleMeshSlicer.hpp" +#include "Utils.hpp" namespace Slic3r { @@ -15,6 +16,7 @@ void midslice_zs(const indexed_triangle_set &mesh, const std::vector & zs, const Transform3d & trafo, float nozzle_diameter, + bool dither_both_up_n_down, std::vector * mid_zs, std::vector * upwrd_mididx, std::vector * dnwrd_mididx) @@ -72,7 +74,7 @@ void midslice_zs(const indexed_triangle_set &mesh, if (nozzle_diameter * sqrt(1 - norm[2] * norm[2]) * nDia < fabs(norm[2]) * layerHeight[cc]) { if (norm[2] > 0) upwrd_mididx->at(cc) = cc; - else + else if (dither_both_up_n_down) dnwrd_mididx->at(cc) = cc; } } @@ -94,7 +96,68 @@ void midslice_zs(const indexed_triangle_set &mesh, return; } -std::vector apply_z_dither(std::vector &expolys, +namespace { +void export_sublayers_to_svg(size_t layer_id, + const ExPolygons &whole, + const ExPolygons &bottom, + const ExPolygons &middleUp, + const ExPolygons &middleDn, + const ExPolygons &top) +{ + BoundingBox bbox = get_extents(whole); + bbox.merge(get_extents(bottom)); + bbox.merge(get_extents(middleUp)); + bbox.merge(get_extents(middleDn)); + bbox.merge(get_extents(top)); + SVG svg(debug_out_path("z-dither_%d_sublayers.svg", layer_id).c_str(), bbox); + svg.draw(whole, "green"); + svg.draw(bottom, "lightcoral"); + svg.draw_outline(middleUp, "red", "red", scale_(0.05)); + svg.draw(top, "cyan"); + svg.draw_outline(middleDn, "blue", "blue", scale_(0.05)); + svg.Close(); +} + +void export_cuts_to_svg(size_t layer_id, const ExPolygons &expoly, const ExPolygons &below, const ExPolygons &above) +{ + BoundingBox bbox = get_extents(expoly); + bbox.merge(get_extents(below)); + bbox.merge(get_extents(above)); + SVG svg(debug_out_path("z-dither_%d_cuts.svg", layer_id).c_str(), bbox); + svg.draw_outline(below, "lightcoral", "lightcoral", scale_(0.05)); + svg.draw_outline(expoly, "green", "green", scale_(0.05)); + svg.draw_outline(above, "cyan", "cyan", scale_(0.05)); + svg.Close(); +} + +// Subtraction of ExPolygons that are very close to each other along some portion of their boundary +// may result in ExPolygons covering tiny, very narrow areas. We need to filter them out. +ExPolygons &filter_tiny_areas(ExPolygons &expolys, double min) +{ + expolys.erase(std::remove_if(expolys.begin(), expolys.end(), [&min](ExPolygon &poly) { + // Use area and perimeter to estimate width of a closed polygon + double area = poly.contour.area(); + double perimeter = poly.contour.length(); + // For cirle area/perimeter = width / 4; For thin rectangle area/perimeter = width / 2. + // Arbitrary shape will have average width less than width of a circle + double width = area / perimeter * 4; + return width < scale_(min); + }), expolys.end()); + return expolys; +} + +int total_num_contours(const ExPolygons &expolys) +{ + int total = 0; + for (const ExPolygon &poly : expolys) + total += poly.num_contours(); + return total; +} + +} // namespace + +std::vector apply_z_dither(std::vector &expolys, + double min_contour_width, std::vector &expolys_mid, const std::vector & upwrd_mididx, const std::vector & dnwrd_mididx, @@ -107,18 +170,22 @@ std::vector apply_z_dither(std::vector &expolys, out[0] = std::move(expolys[0]); // Do not make sublayers of first layer for (auto ll = 1; ll < expolys.size(); ll++) { // idx0 - bottom of layer, idx1 - top of layer - int upwrd_idx0 = upwrd_mididx[ll - 1]; - int dnwrd_idx0 = dnwrd_mididx[ll - 1]; - int upwrd_idx1 = upwrd_mididx[ll]; - int dnwrd_idx1 = dnwrd_mididx[ll]; + int upwrd_idx0 = upwrd_mididx[ll - 1]; + int dnwrd_idx0 = dnwrd_mididx[ll - 1]; + int upwrd_idx1 = upwrd_mididx[ll]; + int dnwrd_idx1 = dnwrd_mididx[ll]; auto useMidCut = [](int idx) { return idx != -1; }; + if (useMidCut(dnwrd_idx0) && useMidCut(dnwrd_idx1) && + total_num_contours(expolys_mid[dnwrd_idx0]) != total_num_contours(expolys_mid[dnwrd_idx1])) { + dnwrd_idx0 = dnwrd_idx1 = -1; // Don't mess up with bridging even in the presence of support + } if (!useMidCut(upwrd_idx0) && !useMidCut(dnwrd_idx0) && !useMidCut(upwrd_idx1) && !useMidCut(dnwrd_idx1)) { out[ll] = std::move(expolys[ll]); continue; } else { - ExPolygons bottom, middleUp, middleDn, top; + ExPolygons bottom, middleUp, middleDn, top, whole; if (useMidCut(upwrd_idx0) || useMidCut(upwrd_idx1)) { bottom = std::move( @@ -138,27 +205,33 @@ std::vector apply_z_dither(std::vector &expolys, middleDn = std::move(diff_ex(expolys[ll], expolys_mid[dnwrd_idx0])); } - ExPolygons whole; - if (useMidCut(upwrd_idx1) && useMidCut(dnwrd_idx0)) - whole = std::move(intersection_ex(expolys_mid[dnwrd_idx0], expolys_mid[upwrd_idx1])); - else if (useMidCut(upwrd_idx1)) - whole = std::move(intersection_ex(expolys[ll], expolys_mid[upwrd_idx1])); - else if (useMidCut(dnwrd_idx0)) - whole = std::move(intersection_ex(expolys[ll], expolys_mid[dnwrd_idx0])); + filter_tiny_areas(bottom, min_contour_width); + filter_tiny_areas(middleUp, min_contour_width); + filter_tiny_areas(middleDn, min_contour_width); + filter_tiny_areas(top, min_contour_width); + + if (!bottom.empty() || !top.empty()) { + whole = std::move(diff_ex(expolys[ll], top)); + whole = std::move(diff_ex(filter_tiny_areas(whole, min_contour_width), bottom)); + filter_tiny_areas(whole, min_contour_width); + } else { out[ll] = std::move(expolys[ll]); continue; } + + #if 0 + export_sublayers_to_svg(ll, whole, bottom, middleUp, middleDn, top); + export_cuts_to_svg(ll, expolys[ll], + useMidCut(dnwrd_idx0) ? expolys_mid[dnwrd_idx0] : (useMidCut(upwrd_idx0) ? expolys_mid[upwrd_idx0] : ExPolygons()), + useMidCut(dnwrd_idx1) ? expolys_mid[dnwrd_idx1] : (useMidCut(upwrd_idx1) ? expolys_mid[upwrd_idx1] : ExPolygons())); + #endif + out[ll] = std::move(whole); - if (bottom.empty() != middleUp.empty() || middleDn.empty() != top.empty()) { - BOOST_LOG_TRIVIAL(error) - << "z-dithering: internal error"; - } else { - sublayers->at(ll).bottom_ = std::move(bottom); - sublayers->at(ll).halfUp_ = std::move(middleUp); - sublayers->at(ll).halfDn_ = std::move(middleDn); - sublayers->at(ll).top_ = std::move(top); - } + sublayers->at(ll).bottom_ = std::move(bottom); + sublayers->at(ll).halfUp_ = std::move(middleUp); + sublayers->at(ll).halfDn_ = std::move(middleDn); + sublayers->at(ll).top_ = std::move(top); } } return out; @@ -175,10 +248,11 @@ std::vector z_dither(const indexed_triangle_set &mesh, std::vector mid_zs; std::vector upwrd_mididx; std::vector dnwrd_mididx; - midslice_zs(mesh, zs, params.trafo, params.nozzle_diameter, &mid_zs, &upwrd_mididx, &dnwrd_mididx); + midslice_zs(mesh, zs, params.trafo, params.nozzle_diameter, params.z_dither_mode == Z_dither_mode::Both, + &mid_zs, &upwrd_mididx, &dnwrd_mididx); if (!mid_zs.empty()) { std::vector expolys_mid = slice_mesh_ex(mesh, mid_zs, params, throw_on_cancel_callback); - return apply_z_dither(expolys, expolys_mid, upwrd_mididx, dnwrd_mididx, sublayers); + return apply_z_dither(expolys, params.nozzle_diameter / 10, expolys_mid, upwrd_mididx, dnwrd_mididx, sublayers); } else { *sublayers = std::vector(expolys.size()); return expolys; diff --git a/src/libslic3r/ZDither.hpp b/src/libslic3r/ZDither.hpp index f547bd29ad6..6d4e19c80c7 100644 --- a/src/libslic3r/ZDither.hpp +++ b/src/libslic3r/ZDither.hpp @@ -27,6 +27,7 @@ void midslice_zs(const indexed_triangle_set &mesh, std::vector * dnwrd_mididx); std::vector apply_z_dither(std::vector &layers, + double min_contour_width, std::vector &mid_layers, const std::vector &do_low, const std::vector &do_high,