From a7ea283f923979c62e9e8a03aaa8a81af9b9fa2d Mon Sep 17 00:00:00 2001 From: Ahmed Samir Date: Sat, 14 Oct 2017 19:16:09 +0200 Subject: [PATCH 01/30] Add 3MF format to ReadMe.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index aa5983a9e..5517ca174 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ The MacOS X build server is kindly sponsored by: Date: Tue, 10 Jul 2018 04:45:25 +0200 Subject: [PATCH 02/30] Add Initial supports class. --- xs/src/libslic3r/SupportMaterial.hpp | 219 +++++++++++++++++++++++++++ 1 file changed, 219 insertions(+) diff --git a/xs/src/libslic3r/SupportMaterial.hpp b/xs/src/libslic3r/SupportMaterial.hpp index 03703aa40..18c8a9b19 100644 --- a/xs/src/libslic3r/SupportMaterial.hpp +++ b/xs/src/libslic3r/SupportMaterial.hpp @@ -1,11 +1,230 @@ #ifndef slic3r_SupportMaterial_hpp_ #define slic3r_SupportMaterial_hpp_ +#include +#include "libslic3r.h" +#include "PrintConfig.hpp" +#include "Flow.hpp" +#include "Layer.hpp" +#include "Geometry.hpp" +#include "Print.hpp" +#include "ClipperUtils.hpp" +#include "SupportMaterial.hpp" +#include "ExPolygon.hpp" + +using namespace std; + +#define MARGIN_STEP MARGIN/3 +#define PILLAR_SIZE 2.5 +#define PILLAR_SPACING 10 + namespace Slic3r { // how much we extend support around the actual contact area constexpr coordf_t SUPPORT_MATERIAL_MARGIN = 1.5; +class Supports +{ +public: + PrintConfig *print_config; ///< + PrintObjectConfig *print_object_config; ///< + Flow flow; ///< + Flow first_layer_flow; ///< + Flow interface_flow; ///< + + Supports(PrintConfig *print_config, + PrintObjectConfig *print_object_config, + const Flow &flow, + const Flow &first_layer_flow, + const Flow &interface_flow) + : print_config(print_config), + print_object_config(print_object_config), + flow(flow), + first_layer_flow(first_layer_flow), + interface_flow(interface_flow) + {} + + void generate() + {} + + void contact_area(PrintObject *object) + { + PrintObjectConfig conf = this->print_object_config; + + // If user specified a custom angle threshold, convert it to radians. + float threshold_rad; + cout << conf.support_material_threshold << endl; + if (conf.support_material_threshold > 0) { + threshold_rad = deg2rad(conf.support_material_threshold + 1); + cout << "Threshold angle = %d°\n", rad2deg(threshold_rad) << endl; + } + + // Build support on a build plate only? If so, then collect top surfaces into $buildplate_only_top_surfaces + // and subtract $buildplate_only_top_surfaces from the contact surfaces, so + // there is no contact surface supported by a top surface. + bool buildplate_only = (conf.support_material || conf.support_material_enforce_layers) + && conf.support_material_buildplate_only; + SurfacesPtr buildplate_only_top_surfaces = SurfacesPtr(); + + auto contact; + Polygons overhangs; // This stores the actual overhang supported by each contact layer + + for (int layer_id = 0; layer_id < object->layers.size(); layer_id++) { + // Note $layer_id might != $layer->id when raft_layers > 0 + // so $layer_id == 0 means first object layer + // and $layer->id == 0 means first print layer (including raft). + + // If no raft, and we're at layer 0, skip to layer 1 + if (conf.raft_layers == 0 && layer_id == 0) + continue; + + // With or without raft, if we're above layer 1, we need to quit + // support generation if supports are disabled, or if we're at a high + // enough layer that enforce-supports no longer applies. + if (layer_id > 0 && !conf.support_material && (layer_id >= conf.support_material_enforce_layers)) + // If we are only going to generate raft just check + // the 'overhangs' of the first object layer. + break; + + auto layer = object->get_layer(layer_id); + + if (buildplate_only) { + // Collect the top surfaces up to this layer and merge them. + SurfacesPtr projection_new; + for (auto const ®ion : layer->regions) { + SurfacesPtr top_surfaces = region->slices.filter_by_type(stTop); + for (auto top_surface : top_surfaces) { + projection_new.push_back(top_surface); + } + } + if (!projection_new.empty()) { + // Merge the new top surfaces with the preceding top surfaces. + // Apply the safety offset to the newly added polygons, so they will connect + // with the polygons collected before, + // but don't apply the safety offset during the union operation as it would + // inflate the polygons over and over. + for (auto surface : projection_new) { + buildplate_only_top_surfaces.push_back(offset(surface, scale(0.01))); + } + } + } + + // Detect overhangs and contact areas needed to support them. + if (layer_id == 0) { + // this is the first object layer, so we're here just to get the object + // footprint for the raft. + // we only consider contours and discard holes to get a more continuous raft. + for (auto const &contour : layer->slices.contours()) { + auto contour_clone = contour; + overhangs.push_back(contour_clone); + contact.push_back(offset(overhangs.back(), static_cast(SUPPORT_MATERIAL_MARGIN))); + } + } + else { + Layer *lower_layer = object->get_layer(layer_id - 1); + for (auto layerm : layer->regions) { + float fw = layerm->flow(frExternalPerimeter).scaled_width(); + auto difference; + + // If a threshold angle was specified, use a different logic for detecting overhangs. + if ((conf.support_material && threshold_rad > 0) + || layer_id <= conf.support_material_enforce_layers + || (conf.raft_layers > 0 && layer_id == 0)) { + float d = 0; + float layer_threshold_rad = threshold_rad; + if (layer_id <= conf.support_material_enforce_layers) { + // Use ~45 deg number for enforced supports if we are in auto. + layer_threshold_rad = deg2rad(89); + } + if (layer_threshold_rad > 0) { + d = scale(lower_layer->height) * + ((cos(layer_threshold_rad)) / (sin(layer_threshold_rad))); + } + + difference = diff(offset(layerm->slices, -d), lower_layer->slices); + + // only enforce spacing from the object ($fw/2) if the threshold angle + // is not too high: in that case, $d will be very small (as we need to catch + // very short overhangs), and such contact area would be eaten by the + // enforced spacing, resulting in high threshold angles to be almost ignored + if (d > fw/2) { + difference = diff(offset(difference, d - fw/2), lower_layer->slices); + } + } else { + // $diff now contains the ring or stripe comprised between the boundary of + // lower slices and the centerline of the last perimeter in this overhanging layer. + // Void $diff means that there's no upper perimeter whose centerline is + // outside the lower slice boundary, thus no overhang + } + } + } + } + + } + + void object_top() + {} + + void support_layers_z() + {} + + void generate_interface_layers() + {} + + void generate_bottom_interface_layers() + {} + + void generate_base_layers() + {} + + void clip_with_object() + {} + + void generate_toolpaths() + {} + + void generate_pillars_shape() + {} + + void clip_with_shape() + { + //TODO + } + + /// This method returns the indices of the layers overlapping with the given one. + vector overlapping_layers(int layer_idx, vector support_z) + { + vector ret; + + float z_max = support_z[layer_idx]; + float z_min = layer_idx == 0 ? 0 : support_z[layer_idx - 1]; + + for (int i = 0; i < support_z.size(); i++) { + if (i == layer_idx) continue; + + float z_max2 = support_z[i]; + float z_min2 = i == 0 ? 0 : support_z[i - 1]; + + if (z_max > z_min2 && z_min < z_max2) + ret.push_back(i); + } + + return ret; + } + + float contact_distance(float layer_height, float nozzle_diameter) + { + float extra = static_cast(print_object_config->support_material_contact_distance.value); + if (extra == 0) { + return layer_height; + } + else { + return nozzle_diameter + extra; + } + } + +}; + } #endif From 5546d28150f7eff8d20b7e41b0dfa1926aa1e9b9 Mon Sep 17 00:00:00 2001 From: Samir55 Date: Tue, 10 Jul 2018 16:45:03 +0200 Subject: [PATCH 03/30] Porting supports_z funtion. --- lib/Slic3r/Print/SupportMaterial.pm | 4 +- src/CMakeLists.txt | 2 +- xs/src/libslic3r/SupportMaterial.cpp | 1 + xs/src/libslic3r/SupportMaterial.hpp | 147 +++++++++++++++++++++------ 4 files changed, 119 insertions(+), 35 deletions(-) create mode 100644 xs/src/libslic3r/SupportMaterial.cpp diff --git a/lib/Slic3r/Print/SupportMaterial.pm b/lib/Slic3r/Print/SupportMaterial.pm index f20a79bc9..87140e8a9 100644 --- a/lib/Slic3r/Print/SupportMaterial.pm +++ b/lib/Slic3r/Print/SupportMaterial.pm @@ -422,11 +422,12 @@ sub support_layers_z { # add raft layers by dividing the space between first layer and # first contact layer evenly if ($self->object_config->raft_layers > 1 && @z >= 2) { + print @z; # $z[1] is last raft layer (contact layer for the first layer object) my $height = ($z[1] - $z[0]) / ($self->object_config->raft_layers - 1); # since we already have two raft layers ($z[0] and $z[1]) we need to insert # raft_layers-2 more - splice @z, 1, 0, + splice @z, 1, 0, #splice ARRAY,OFFSET,LENGTH,LIST map { sprintf "%.2f", $_ } map { $z[0] + $height * $_ } 1..($self->object_config->raft_layers - 2); @@ -450,6 +451,7 @@ sub support_layers_z { # remove duplicates and make sure all 0.x values have the leading 0 { my %sl = map { 1 * $_ => 1 } @z; + print "HERE"; @z = sort { $a <=> $b } keys %sl; } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ee67ee8e0..2155c9a79 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -91,7 +91,7 @@ add_library(libslic3r STATIC ${LIBDIR}/libslic3r/SVG.cpp ${LIBDIR}/libslic3r/TriangleMesh.cpp ${LIBDIR}/libslic3r/Zip/ZipArchive.cpp -) + ../xs/src/libslic3r/SupportMaterial.cpp) add_library(BSpline STATIC ${LIBDIR}/BSpline/BSpline.cpp diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp new file mode 100644 index 000000000..ea4f5bd98 --- /dev/null +++ b/xs/src/libslic3r/SupportMaterial.cpp @@ -0,0 +1 @@ +#include "SupportMaterial.hpp" diff --git a/xs/src/libslic3r/SupportMaterial.hpp b/xs/src/libslic3r/SupportMaterial.hpp index 18c8a9b19..488580792 100644 --- a/xs/src/libslic3r/SupportMaterial.hpp +++ b/xs/src/libslic3r/SupportMaterial.hpp @@ -2,6 +2,8 @@ #define slic3r_SupportMaterial_hpp_ #include +#include +#include #include "libslic3r.h" #include "PrintConfig.hpp" #include "Flow.hpp" @@ -14,31 +16,32 @@ using namespace std; -#define MARGIN_STEP MARGIN/3 -#define PILLAR_SIZE 2.5 -#define PILLAR_SPACING 10 - -namespace Slic3r { +namespace Slic3r +{ // how much we extend support around the actual contact area constexpr coordf_t SUPPORT_MATERIAL_MARGIN = 1.5; -class Supports +constexpr coordf_t PILLAR_SIZE = 2.5; + +constexpr coordf_t PILLAR_SPACING = 10; + +class SupportMaterial { public: - PrintConfig *print_config; ///< - PrintObjectConfig *print_object_config; ///< + PrintConfig *config; ///< + PrintObjectConfig *object_config; ///< Flow flow; ///< Flow first_layer_flow; ///< Flow interface_flow; ///< - Supports(PrintConfig *print_config, - PrintObjectConfig *print_object_config, - const Flow &flow, - const Flow &first_layer_flow, - const Flow &interface_flow) - : print_config(print_config), - print_object_config(print_object_config), + SupportMaterial(PrintConfig *print_config, + PrintObjectConfig *print_object_config, + const Flow &flow, + const Flow &first_layer_flow, + const Flow &interface_flow) + : config(print_config), + object_config(print_object_config), flow(flow), first_layer_flow(first_layer_flow), interface_flow(interface_flow) @@ -49,14 +52,15 @@ public: void contact_area(PrintObject *object) { - PrintObjectConfig conf = this->print_object_config; + PrintObjectConfig conf = this->object_config; // If user specified a custom angle threshold, convert it to radians. float threshold_rad; cout << conf.support_material_threshold << endl; if (conf.support_material_threshold > 0) { - threshold_rad = deg2rad(conf.support_material_threshold + 1); - cout << "Threshold angle = %d°\n", rad2deg(threshold_rad) << endl; + threshold_rad = Geometry::deg2rad(conf.support_material_threshold.value + 1.0); + + // TODO @Samir55 add debug statetments. } // Build support on a build plate only? If so, then collect top surfaces into $buildplate_only_top_surfaces @@ -66,7 +70,7 @@ public: && conf.support_material_buildplate_only; SurfacesPtr buildplate_only_top_surfaces = SurfacesPtr(); - auto contact; + Polygons contact; Polygons overhangs; // This stores the actual overhang supported by each contact layer for (int layer_id = 0; layer_id < object->layers.size(); layer_id++) { @@ -104,7 +108,7 @@ public: // but don't apply the safety offset during the union operation as it would // inflate the polygons over and over. for (auto surface : projection_new) { - buildplate_only_top_surfaces.push_back(offset(surface, scale(0.01))); +// buildplate_only_top_surfaces.push_back(offset(surface, scale_(0.01))); } } } @@ -117,14 +121,14 @@ public: for (auto const &contour : layer->slices.contours()) { auto contour_clone = contour; overhangs.push_back(contour_clone); - contact.push_back(offset(overhangs.back(), static_cast(SUPPORT_MATERIAL_MARGIN))); +// contact.push_back(offset(overhangs.back(), static_cast(SUPPORT_MATERIAL_MARGIN))); } } else { Layer *lower_layer = object->get_layer(layer_id - 1); for (auto layerm : layer->regions) { float fw = layerm->flow(frExternalPerimeter).scaled_width(); - auto difference; + Polygons difference; // If a threshold angle was specified, use a different logic for detecting overhangs. if ((conf.support_material && threshold_rad > 0) @@ -134,10 +138,10 @@ public: float layer_threshold_rad = threshold_rad; if (layer_id <= conf.support_material_enforce_layers) { // Use ~45 deg number for enforced supports if we are in auto. - layer_threshold_rad = deg2rad(89); + layer_threshold_rad = Geometry::deg2rad(89); } if (layer_threshold_rad > 0) { - d = scale(lower_layer->height) * + d = scale_(lower_layer->height) * ((cos(layer_threshold_rad)) / (sin(layer_threshold_rad))); } @@ -147,10 +151,11 @@ public: // is not too high: in that case, $d will be very small (as we need to catch // very short overhangs), and such contact area would be eaten by the // enforced spacing, resulting in high threshold angles to be almost ignored - if (d > fw/2) { - difference = diff(offset(difference, d - fw/2), lower_layer->slices); + if (d > fw / 2) { + difference = diff(offset(difference, d - fw / 2), lower_layer->slices); } - } else { + } + else { // $diff now contains the ring or stripe comprised between the boundary of // lower slices and the centerline of the last perimeter in this overhanging layer. // Void $diff means that there's no upper perimeter whose centerline is @@ -162,11 +167,87 @@ public: } - void object_top() - {} + ExPolygons *object_top(PrintObject *object, SurfacesPtr contact) + { + // find object top surfaces + // we'll use them to clip our support and detect where does it stick. + ExPolygons *top = new ExPolygons(); + if (object_config->support_material_buildplate_only.value) + return top; - void support_layers_z() - {} + } + + vector support_layers_z(vector contact_z, vector top_z, coordf_t max_object_layer_height) + { + // Quick table to check whether a given Z is a top surface. + map is_top; + for (auto z : top_z) is_top[z] = true; + + // determine layer height for any non-contact layer + // we use max() to prevent many ultra-thin layers to be inserted in case + // layer_height > nozzle_diameter * 0.75. + auto nozzle_diameter = config->nozzle_diameter.get_at(object_config->support_material_extruder - 1); + auto support_material_height = (max_object_layer_height, (nozzle_diameter * 0.75)); + coordf_t _contact_distance = this->contact_distance(support_material_height, nozzle_diameter); + + // Initialize known, fixed, support layers. + vector z; + for (auto c_z : contact_z) z.push_back(c_z); + for (auto t_z : top_z) { + z.push_back(t_z); + z.push_back(t_z + _contact_distance); + } + sort(z.begin(), z.end()); + + // Enforce first layer height. + coordf_t first_layer_height = object_config->first_layer_height; + while (!z.empty() && z.front() <= first_layer_height) z.erase(z.begin()); + z.insert(z.begin(), first_layer_height); + + // Add raft layers by dividing the space between first layer and + // first contact layer evenly. + if (object_config->raft_layers > 1 && z.size() >= 2) { + // z[1] is last raft layer (contact layer for the first layer object) TODO @Samir55 How so? + coordf_t height = (z[1] - z[0]) / (object_config->raft_layers - 1); + + // since we already have two raft layers ($z[0] and $z[1]) we need to insert + // raft_layers-2 more + int idx = 1; + for (int j = 0; j < object_config->raft_layers - 2; j++) { + float z_new = + roundf(static_cast((z[0] + height * idx) * 100)) / 100; // round it to 2 decimal places. + z.insert(z.begin() + idx, z_new); + idx++; + } + } + + // Create other layers (skip raft layers as they're already done and use thicker layers). + for (size_t i = z.size(); i >= object_config->raft_layers; i--) { + coordf_t target_height = support_material_height; + if (i > 0 && is_top[z[i - 1]]) { + target_height = nozzle_diameter; + } + + // Enforce first layer height. + if ((i == 0 && z[i] > target_height + first_layer_height) + || (z[i] - z[i - 1] > target_height + EPSILON)) { + z.insert(z.begin() + i, (z[i] - target_height)); + i++; + } + } + + // Remove duplicates and make sure all 0.x values have the leading 0. + { + set s; + for (auto el : z) + s.insert(roundf(static_cast((el * 100)) / 100)); // round it to 2 decimal places. + z = vector(); + for (auto el : s) + z.push_back(el); + } + + return z; + } void generate_interface_layers() {} @@ -212,9 +293,9 @@ public: return ret; } - float contact_distance(float layer_height, float nozzle_diameter) + coordf_t contact_distance(coordf_t layer_height, coordf_t nozzle_diameter) { - float extra = static_cast(print_object_config->support_material_contact_distance.value); + coordf_t extra = static_cast(object_config->support_material_contact_distance.value); if (extra == 0) { return layer_height; } From 716946a9f58c6b4ff999b41054a3ac536cc225e5 Mon Sep 17 00:00:00 2001 From: Samir55 Date: Tue, 10 Jul 2018 17:22:56 +0200 Subject: [PATCH 04/30] some porting. --- xs/src/libslic3r/Print.hpp | 23 ++++- xs/src/libslic3r/SupportMaterial.hpp | 124 +++++++++++++++++---------- 2 files changed, 102 insertions(+), 45 deletions(-) diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index 7f83c684f..dd5bec251 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -228,7 +228,28 @@ class Print void auto_assign_extruders(ModelObject* model_object) const; std::string output_filename(); std::string output_filepath(const std::string &path); - + +// Flow support_material_flow (FlowRole role){ +// +// int extruder = (role == FlowRole::frSupportMaterial) +// ? default_object_config.support_material_extruder +// : default_object_config.support_material_interface_extruder; +// +// ConfigOptionFloatOrPercent width = default_object_config.support_material_extrusion_width || config.extrusion_width; +// +// if (role == FlowRole::frSupportMaterialInterface) { +// width = default_object_config.support_material_interface_extrusion_width || width; +// } +// +// // We use a bogus layer_height because we use the same flow for all +// // support material layers. +// return Flow::new_from_config_width( +// role, +// width, +// config.nozzle_diameter.get_at(extruder-1), +// default_object_config.layer_height, +// 0); +// } private: void clear_regions(); void delete_region(size_t idx); diff --git a/xs/src/libslic3r/SupportMaterial.hpp b/xs/src/libslic3r/SupportMaterial.hpp index 488580792..f3db6fe73 100644 --- a/xs/src/libslic3r/SupportMaterial.hpp +++ b/xs/src/libslic3r/SupportMaterial.hpp @@ -14,6 +14,9 @@ #include "SupportMaterial.hpp" #include "ExPolygon.hpp" +// Supports Material tests. + + using namespace std; namespace Slic3r @@ -177,6 +180,50 @@ public: } + void generate_interface_layers() + {} + + void generate_bottom_interface_layers() + {} + + void generate_base_layers() + {} + + void clip_with_object() + {} + + void generate_toolpaths() + {} + + void generate_pillars_shape() + {} + + void clip_with_shape() + { + //TODO + } + + /// This method returns the indices of the layers overlapping with the given one. + vector overlapping_layers(int layer_idx, vector support_z) + { + vector ret; + + float z_max = support_z[layer_idx]; + float z_min = layer_idx == 0 ? 0 : support_z[layer_idx - 1]; + + for (int i = 0; i < support_z.size(); i++) { + if (i == layer_idx) continue; + + float z_max2 = support_z[i]; + float z_min2 = i == 0 ? 0 : support_z[i - 1]; + + if (z_max > z_min2 && z_min < z_max2) + ret.push_back(i); + } + + return ret; + } + vector support_layers_z(vector contact_z, vector top_z, coordf_t max_object_layer_height) { // Quick table to check whether a given Z is a top surface. @@ -249,50 +296,6 @@ public: return z; } - void generate_interface_layers() - {} - - void generate_bottom_interface_layers() - {} - - void generate_base_layers() - {} - - void clip_with_object() - {} - - void generate_toolpaths() - {} - - void generate_pillars_shape() - {} - - void clip_with_shape() - { - //TODO - } - - /// This method returns the indices of the layers overlapping with the given one. - vector overlapping_layers(int layer_idx, vector support_z) - { - vector ret; - - float z_max = support_z[layer_idx]; - float z_min = layer_idx == 0 ? 0 : support_z[layer_idx - 1]; - - for (int i = 0; i < support_z.size(); i++) { - if (i == layer_idx) continue; - - float z_max2 = support_z[i]; - float z_min2 = i == 0 ? 0 : support_z[i - 1]; - - if (z_max > z_min2 && z_min < z_max2) - ret.push_back(i); - } - - return ret; - } - coordf_t contact_distance(coordf_t layer_height, coordf_t nozzle_diameter) { coordf_t extra = static_cast(object_config->support_material_contact_distance.value); @@ -306,6 +309,39 @@ public: }; +class SupportMaterialTests +{ +public: + bool test_1() + { + // Create a mesh & modelObject. + TriangleMesh mesh = TriangleMesh::make_cube(20, 20, 20); + + // Create modelObject. + Model model = Model(); + ModelObject *object = model.add_object(); + object->add_volume(mesh); + model.add_default_instances(); + + // Align to origin. + model.align_instances_to_origin(); + + // Create Print. + Print print = Print(); + + // Configure the printObjectConfig. + print.default_object_config.set_deserialize("support_material", "1"); + + // Add the modelObject. + print.add_model_object(model.objects[0]); + + // Create new supports. + SupportMaterial support = SupportMaterial(&print, &print.objects.front()->config, + print.objects.front().suppo) + + } + +}; } #endif From 861452e4287a02664fd33b873df316a2409708f1 Mon Sep 17 00:00:00 2001 From: Samir55 Date: Tue, 10 Jul 2018 23:01:37 +0200 Subject: [PATCH 05/30] Porting clip_with_shape function. --- xs/src/libslic3r/Print.hpp | 22 ---------------- xs/src/libslic3r/SupportMaterial.hpp | 38 ++++++++++++++++++++-------- 2 files changed, 28 insertions(+), 32 deletions(-) diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index dd5bec251..502d3c64d 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -228,28 +228,6 @@ class Print void auto_assign_extruders(ModelObject* model_object) const; std::string output_filename(); std::string output_filepath(const std::string &path); - -// Flow support_material_flow (FlowRole role){ -// -// int extruder = (role == FlowRole::frSupportMaterial) -// ? default_object_config.support_material_extruder -// : default_object_config.support_material_interface_extruder; -// -// ConfigOptionFloatOrPercent width = default_object_config.support_material_extrusion_width || config.extrusion_width; -// -// if (role == FlowRole::frSupportMaterialInterface) { -// width = default_object_config.support_material_interface_extrusion_width || width; -// } -// -// // We use a bogus layer_height because we use the same flow for all -// // support material layers. -// return Flow::new_from_config_width( -// role, -// width, -// config.nozzle_diameter.get_at(extruder-1), -// default_object_config.layer_height, -// 0); -// } private: void clear_regions(); void delete_region(size_t idx); diff --git a/xs/src/libslic3r/SupportMaterial.hpp b/xs/src/libslic3r/SupportMaterial.hpp index f3db6fe73..e712b7b2c 100644 --- a/xs/src/libslic3r/SupportMaterial.hpp +++ b/xs/src/libslic3r/SupportMaterial.hpp @@ -34,15 +34,15 @@ class SupportMaterial public: PrintConfig *config; ///< PrintObjectConfig *object_config; ///< - Flow flow; ///< - Flow first_layer_flow; ///< - Flow interface_flow; ///< + Flow *flow; ///< + Flow *first_layer_flow; ///< + Flow *interface_flow; ///< SupportMaterial(PrintConfig *print_config, PrintObjectConfig *print_object_config, - const Flow &flow, - const Flow &first_layer_flow, - const Flow &interface_flow) + Flow *flow = nullptr, + Flow *first_layer_flow = nullptr, + Flow *interface_flow = nullptr) : config(print_config), object_config(print_object_config), flow(flow), @@ -198,9 +198,17 @@ public: void generate_pillars_shape() {} - void clip_with_shape() + void clip_with_shape(map &support, map &shape) { - //TODO + for (auto layer : support) { + // Don't clip bottom layer with shape so that we + // can generate a continuous base flange + // also don't clip raft layers + if (layer.first == 0) continue; + else if (layer.first < object_config->raft_layers) continue; + + layer.second = intersection(layer.second, shape[layer.first]); + } } /// This method returns the indices of the layers overlapping with the given one. @@ -331,13 +339,23 @@ public: // Configure the printObjectConfig. print.default_object_config.set_deserialize("support_material", "1"); + print.default_object_config.set_deserialize("layer_height", "0.2"); + print.config.set_deserialize("first_layer_height", "0.3"); + + vector contact_z; + vector top_z; + contact_z.push_back(1.9); + top_z.push_back(1.9); // Add the modelObject. print.add_model_object(model.objects[0]); // Create new supports. - SupportMaterial support = SupportMaterial(&print, &print.objects.front()->config, - print.objects.front().suppo) + SupportMaterial support = SupportMaterial(&print.config, &print.objects.front()->config); + + vector + support_z = support.support_layers_z(contact_z, top_z, print.default_object_config.layer_height); + assert(support_z[0] == print.default_object_config.first_layer_height); } From 6b0e6de9948c3812246ed514bfc633029d992aa5 Mon Sep 17 00:00:00 2001 From: Samir55 Date: Tue, 10 Jul 2018 23:51:41 +0200 Subject: [PATCH 06/30] Porting clip with object function. --- xs/src/libslic3r/SupportMaterial.hpp | 44 ++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/xs/src/libslic3r/SupportMaterial.hpp b/xs/src/libslic3r/SupportMaterial.hpp index e712b7b2c..54fcab2cb 100644 --- a/xs/src/libslic3r/SupportMaterial.hpp +++ b/xs/src/libslic3r/SupportMaterial.hpp @@ -189,15 +189,53 @@ public: void generate_base_layers() {} - void clip_with_object() - {} - void generate_toolpaths() {} void generate_pillars_shape() {} + // This method removes object silhouette from support material + // (it's used with interface and base only). It removes a bit more, + // leaving a thin gap between object and support in the XY plane. + void clip_with_object(map &support, vector support_z, PrintObject &object) + { + int i = 0; + for (auto support_layer: support) { + if (support_layer.second.empty()) { + i++; + continue; + } + coordf_t z_max = support_z[i]; + coordf_t z_min = (i == 0) ? 0 : support_z[i-1]; + + LayerPtrs layers; + for (auto layer : object.layers) { + if (layer->print_z > z_min && (layer->print_z - layer->height) < z_max) { + layers.push_back(layer); + } + } + + // $layer->slices contains the full shape of layer, thus including + // perimeter's width. $support contains the full shape of support + // material, thus including the width of its foremost extrusion. + // We leave a gap equal to a full extrusion width. TODO ask about this line @samir + Polygons slices; + for (Layer *l : layers) { + for (auto s : l->slices.contours()) { + slices.push_back(s); + } + } + support_layer.second = diff(support_layer.second, offset(slices, flow->scaled_width())); + } + /* + $support->{$i} = diff( + $support->{$i}, + offset([ map @$_, map @{$_->slices}, @layers ], +$self->flow->scaled_width), + ); + */ + } + void clip_with_shape(map &support, map &shape) { for (auto layer : support) { From 7fa60d2c594179d8d913271d026477e56a89a149 Mon Sep 17 00:00:00 2001 From: Samir55 Date: Wed, 11 Jul 2018 00:08:25 +0200 Subject: [PATCH 07/30] Add Test 1. --- xs/src/libslic3r/SupportMaterial.hpp | 45 ++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/xs/src/libslic3r/SupportMaterial.hpp b/xs/src/libslic3r/SupportMaterial.hpp index 54fcab2cb..061e8c840 100644 --- a/xs/src/libslic3r/SupportMaterial.hpp +++ b/xs/src/libslic3r/SupportMaterial.hpp @@ -207,11 +207,11 @@ public: continue; } coordf_t z_max = support_z[i]; - coordf_t z_min = (i == 0) ? 0 : support_z[i-1]; + coordf_t z_min = (i == 0) ? 0 : support_z[i - 1]; LayerPtrs layers; for (auto layer : object.layers) { - if (layer->print_z > z_min && (layer->print_z - layer->height) < z_max) { + if (layer->print_z > z_min && (layer->print_z - layer->height) < z_max) { layers.push_back(layer); } } @@ -393,8 +393,47 @@ public: vector support_z = support.support_layers_z(contact_z, top_z, print.default_object_config.layer_height); - assert(support_z[0] == print.default_object_config.first_layer_height); + bool + is_1 = (support_z[0] == print.default_object_config.first_layer_height); // 'first layer height is honored'. + + bool is_2 = false; // 'no null or negative support layers'. + for (int i = 1; i < support_z.size(); ++i) { + if (support_z[i] - support_z[i - 1] <= 0) is_2 = true; + } + + bool is_3 = false; // 'no layers thicker than nozzle diameter'. + for (int i = 1; i < support_z.size(); ++i) { + if (support_z[i] - support_z[i - 1] > print.config.nozzle_diameter.get_at(0) + EPSILON) is_2 = true; + } + + coordf_t expected_top_spacing = + support.contact_distance(print.default_object_config.layer_height, print.config.nozzle_diameter.get_at(0)); + coordf_t wrong_top_spacing = 0; + for (auto top_z_el : top_z) { + // find layer index of this top surface. + int layer_id = -1; + for (int i = 0; i < support_z.size(); i++) { + if (abs(support_z[i] - top_z_el) < EPSILON) { + layer_id = i; + i = static_cast(support_z.size()); + } + } + + // check that first support layer above this top surface (or the next one) is spaced with nozzle diameter + if ((support_z[layer_id + 1] - support_z[layer_id]) != expected_top_spacing + && (support_z[layer_id + 2] - support_z[layer_id]) != expected_top_spacing) + wrong_top_spacing = 1; + } + bool is_4 = !wrong_top_spacing; // 'layers above top surfaces are spaced correctly' + + /* Test Also with this + $config->set('first_layer_height', 0.4); + $test->(); + + $config->set('layer_height', $config->nozzle_diameter->[0]); + $test->(); + */ } }; From 9c8e48595d01f185b5d4723d660e9a9a8b6a6287 Mon Sep 17 00:00:00 2001 From: Samir55 Date: Wed, 11 Jul 2018 02:36:50 +0200 Subject: [PATCH 08/30] porting contact_area function. --- lib/Slic3r/Print/SupportMaterial.pm | 3 +- t/support.t | 1 + xs/src/libslic3r/SupportMaterial.hpp | 281 +++++++++++++++++++++++++-- 3 files changed, 264 insertions(+), 21 deletions(-) diff --git a/lib/Slic3r/Print/SupportMaterial.pm b/lib/Slic3r/Print/SupportMaterial.pm index 87140e8a9..2c13cc2cd 100644 --- a/lib/Slic3r/Print/SupportMaterial.pm +++ b/lib/Slic3r/Print/SupportMaterial.pm @@ -427,7 +427,7 @@ sub support_layers_z { my $height = ($z[1] - $z[0]) / ($self->object_config->raft_layers - 1); # since we already have two raft layers ($z[0] and $z[1]) we need to insert # raft_layers-2 more - splice @z, 1, 0, #splice ARRAY,OFFSET,LENGTH,LIST + splice @z, 1, 0, # map { sprintf "%.2f", $_ } map { $z[0] + $height * $_ } 1..($self->object_config->raft_layers - 2); @@ -451,7 +451,6 @@ sub support_layers_z { # remove duplicates and make sure all 0.x values have the leading 0 { my %sl = map { 1 * $_ => 1 } @z; - print "HERE"; @z = sort { $a <=> $b } keys %sl; } diff --git a/t/support.t b/t/support.t index 0a5a75337..50f4d53fa 100644 --- a/t/support.t +++ b/t/support.t @@ -312,6 +312,7 @@ use Slic3r::Test; @{ $layer_heights_by_tool{$config->support_material_extruder-1} }), 'no support material layer is as thin as object layers'; } + { my $config = Slic3r::Config->new_from_defaults; $config->set('support_material_enforce_layers', 100); diff --git a/xs/src/libslic3r/SupportMaterial.hpp b/xs/src/libslic3r/SupportMaterial.hpp index 061e8c840..d899bc94c 100644 --- a/xs/src/libslic3r/SupportMaterial.hpp +++ b/xs/src/libslic3r/SupportMaterial.hpp @@ -1,6 +1,7 @@ #ifndef slic3r_SupportMaterial_hpp_ #define slic3r_SupportMaterial_hpp_ +#include #include #include #include @@ -13,6 +14,7 @@ #include "ClipperUtils.hpp" #include "SupportMaterial.hpp" #include "ExPolygon.hpp" +#include "SVG.hpp" // Supports Material tests. @@ -53,7 +55,7 @@ public: void generate() {} - void contact_area(PrintObject *object) + pair contact_area(PrintObject *object) { PrintObjectConfig conf = this->object_config; @@ -61,20 +63,22 @@ public: float threshold_rad; cout << conf.support_material_threshold << endl; if (conf.support_material_threshold > 0) { - threshold_rad = Geometry::deg2rad(conf.support_material_threshold.value + 1.0); - + threshold_rad = static_cast(Geometry::deg2rad( + conf.support_material_threshold.value + 1)); // +1 makes the threshold inclusive // TODO @Samir55 add debug statetments. } // Build support on a build plate only? If so, then collect top surfaces into $buildplate_only_top_surfaces - // and subtract $buildplate_only_top_surfaces from the contact surfaces, so + // and subtract buildplate_only_top_surfaces from the contact surfaces, so // there is no contact surface supported by a top surface. - bool buildplate_only = (conf.support_material || conf.support_material_enforce_layers) - && conf.support_material_buildplate_only; - SurfacesPtr buildplate_only_top_surfaces = SurfacesPtr(); + bool buildplate_only = + (conf.support_material || conf.support_material_enforce_layers) + && conf.support_material_buildplate_only; + Surfaces buildplate_only_top_surfaces; + // Determine contact areas. Polygons contact; - Polygons overhangs; // This stores the actual overhang supported by each contact layer + Polygons overhang; // This stores the actual overhang supported by each contact layer for (int layer_id = 0; layer_id < object->layers.size(); layer_id++) { // Note $layer_id might != $layer->id when raft_layers > 0 @@ -88,15 +92,22 @@ public: // With or without raft, if we're above layer 1, we need to quit // support generation if supports are disabled, or if we're at a high // enough layer that enforce-supports no longer applies. - if (layer_id > 0 && !conf.support_material && (layer_id >= conf.support_material_enforce_layers)) + if (layer_id > 0 + && !conf.support_material + && (layer_id >= conf.support_material_enforce_layers)) // If we are only going to generate raft just check // the 'overhangs' of the first object layer. break; auto layer = object->get_layer(layer_id); + if (conf.support_material_max_layers + && layer_id > conf.support_material_max_layers) + break; + + ExPolygons buildplate_only_top_surfaces_polygons; if (buildplate_only) { - // Collect the top surfaces up to this layer and merge them. + // Collect the top surfaces up to this layer and merge them. TODO @Ask about this line. SurfacesPtr projection_new; for (auto const ®ion : layer->regions) { SurfacesPtr top_surfaces = region->slices.filter_by_type(stTop); @@ -110,9 +121,13 @@ public: // with the polygons collected before, // but don't apply the safety offset during the union operation as it would // inflate the polygons over and over. - for (auto surface : projection_new) { -// buildplate_only_top_surfaces.push_back(offset(surface, scale_(0.01))); + for (Surface *surface : projection_new) { + Surfaces s = offset(*surface, scale_(0.01)); + for (auto el : s) + buildplate_only_top_surfaces.push_back(el); } + // TODO @Ask about this + buildplate_only_top_surfaces_polygons = union_ex(buildplate_only_top_surfaces, 0); } } @@ -123,9 +138,12 @@ public: // we only consider contours and discard holes to get a more continuous raft. for (auto const &contour : layer->slices.contours()) { auto contour_clone = contour; - overhangs.push_back(contour_clone); -// contact.push_back(offset(overhangs.back(), static_cast(SUPPORT_MATERIAL_MARGIN))); + overhang.push_back(contour_clone); } + + Polygons ps = offset(overhang, scale_(+SUPPORT_MATERIAL_MARGIN)); + for (auto p : ps) + contact.push_back(p); } else { Layer *lower_layer = object->get_layer(layer_id - 1); @@ -144,30 +162,250 @@ public: layer_threshold_rad = Geometry::deg2rad(89); } if (layer_threshold_rad > 0) { - d = scale_(lower_layer->height) * - ((cos(layer_threshold_rad)) / (sin(layer_threshold_rad))); + d = scale_(lower_layer->height + * ((cos(layer_threshold_rad)) / (sin(layer_threshold_rad)))); } - difference = diff(offset(layerm->slices, -d), lower_layer->slices); + // TODO Ask about this. + difference = diff( + layerm->slices, + offset(lower_layer->slices, +d) + ); // only enforce spacing from the object ($fw/2) if the threshold angle // is not too high: in that case, $d will be very small (as we need to catch // very short overhangs), and such contact area would be eaten by the // enforced spacing, resulting in high threshold angles to be almost ignored if (d > fw / 2) { - difference = diff(offset(difference, d - fw / 2), lower_layer->slices); + difference = diff( + offset(difference, d - fw / 2), + lower_layer->slices); } } else { + difference = diff( + layerm->slices, + offset(lower_layer->slices, + static_cast(+conf.get_abs_value("support_material_threshols", fw))) + ); + + // Collapse very tiny spots. + difference = offset2(difference, -fw / 10, +fw / 10); // $diff now contains the ring or stripe comprised between the boundary of // lower slices and the centerline of the last perimeter in this overhanging layer. // Void $diff means that there's no upper perimeter whose centerline is // outside the lower slice boundary, thus no overhang } + + if (conf.dont_support_bridges) { + // Compute the area of bridging perimeters. + Polygons bridged_perimeters; + { + auto bridge_flow = layerm->flow(FlowRole::frPerimeter, 1); + + // Get the lower layer's slices and grow them by half the nozzle diameter + // because we will consider the upper perimeters supported even if half nozzle + // falls outside the lower slices. + Polygons lower_grown_slices; + { + coordf_t nozzle_diameter = this->config->nozzle_diameter + .get_at(layerm->region()->config.perimeter_extruder - 1); + + lower_grown_slices = offset( + lower_layer->slices, + scale_(nozzle_diameter / 2) + ); + } + + // Get all perimeters as polylines. + // TODO: split_at_first_point() (called by as_polyline() for ExtrusionLoops) + // could split a bridge mid-way. + Polyline overhang_perimeters_polyline = layerm->perimeters.flatten().as_polyline(); + Polylines overhang_perimeters_polylines; + + // Only consider the overhang parts of such perimeters, + // overhangs being those parts not supported by + // workaround for Clipper bug, see Slic3r::Polygon::clip_as_polyline() + // ASk About htis. + overhang_perimeters_polyline.translate(1, 0); + Polylines ps = diff_pl(overhang_perimeters_polyline, lower_grown_slices); + for (const auto &p : ps) { + overhang_perimeters_polylines.push_back(p); + } + + // Only consider straight overhangs. + Polylines new_overhangs_perimeters_polylines; + for (auto p : overhang_perimeters_polylines) { + if (p.is_straight()) + new_overhangs_perimeters_polylines.push_back(p); + } + overhang_perimeters_polylines = new_overhangs_perimeters_polylines; + new_overhangs_perimeters_polylines = Polylines(); + + // Only consider overhangs having endpoints inside layer's slices + for (auto &p : overhang_perimeters_polylines) { + p.extend_start(fw); + p.extend_end(fw); + } + + for (auto p : overhang_perimeters_polylines) { + if (layer->slices.contains_b(p.first_point()) + && layer->slices.contains_b(p.last_point())) { + new_overhangs_perimeters_polylines.push_back(p); + } + } + + overhang_perimeters_polylines = new_overhangs_perimeters_polylines; + new_overhangs_perimeters_polylines = Polylines(); + + // Convert bridging polylines into polygons by inflating them with their thickness. + { + // For bridges we can't assume width is larger than spacing because they + // are positioned according to non-bridging perimeters spacing. + coordf_t widths[] = {bridge_flow.scaled_width(), + bridge_flow.scaled_spacing(), + fw, + layerm->flow(FlowRole::frPerimeter).scaled_width()}; + + coordf_t w = *max_element(widths, widths + 4); + + // Also apply safety offset to ensure no gaps are left in between. + for (auto &p : overhang_perimeters_polylines) { + Polygons ps = union_(offset(p, w / 2 + 10)); + for (auto ps_el : ps) + bridged_perimeters.push_back(ps_el); + } + } + } + + if (1) { + // Remove the entire bridges and only support the unsupported edges. + ExPolygons bridges; + for (auto surface : layerm->fill_surfaces.filter_by_type(stBottomBridge)) { + if (surface->bridge_angle != -1) { + bridges.push_back(surface->expolygon); + } + } + + ExPolygons ps; + for (auto p : bridged_perimeters) + ps.push_back(ExPolygon(p)); + for (auto p : bridges) + ps.push_back(p); + + // TODO ASK about this. + difference = diff( + difference, + to_polygons(ps), + true + ); + + // TODO Ask about this. + auto p_intersections = intersection(offset(layerm->unsupported_bridge_edges.polylines, + +scale_(SUPPORT_MATERIAL_MARGIN)), + to_polygons(bridges)); + for (auto p: p_intersections) { + difference.push_back(p); + } + } + else { + // just remove bridged areas. + difference = diff( + difference, + layerm->bridged, + 1 + ); + } + } // if ($conf->dont_support_bridges) + + if (buildplate_only) { + // Don't support overhangs above the top surfaces. + // This step is done before the contact surface is calcuated by growing the overhang region. + // TODO Ask about this. + difference = diff(difference, to_polygons(buildplate_only_top_surfaces_polygons)); + } + + if (difference.empty()) continue; + + // NOTE: this is not the full overhang as it misses the outermost half of the perimeter width! + for (auto p : difference) { + overhang.push_back(p); + } + + // 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). + { + auto slices_margin = offset(lower_layer->slices, +fw / 2); + + if (buildplate_only) { + // TODO Ask about this. + // Trim the inflated contact surfaces by the top surfaces as well. + for (auto s_p : to_polygons(buildplate_only_top_surfaces)) { + slices_margin.push_back(s_p); + } + slices_margin = union_(slices_margin); + } + + // TODO Ask how to port this. + /* + for ($fw/2, map {scale MARGIN_STEP} 1..(MARGIN / MARGIN_STEP)) { + $diff = diff( + offset($diff, $_), + $slices_margin, + ); + } + */ + } + + for (auto p : difference) + contact.push_back(p); } } - } + if (contact.empty()) + continue; + // Now apply the contact areas to the layer were they need to be made. + { + // Get the average nozzle diameter used on this layer. + vector nozzle_diameters; + for (auto region : layer->regions) { + nozzle_diameters.push_back(config->nozzle_diameter.get_at(static_cast( + region->region()->config + .perimeter_extruder - 1))); + nozzle_diameters.push_back(config->nozzle_diameter.get_at(static_cast( + region->region()->config + .infill_extruder - 1))); + nozzle_diameters.push_back(config->nozzle_diameter.get_at(static_cast( + region->region()->config + .solid_infill_extruder - 1))); + } + + auto nozzle_diameter = + accumulate(nozzle_diameters.begin(), nozzle_diameters.end(), 0.0) / nozzle_diameters.size(); + + auto contact_z = layer->print_z - contact_distance(layer->height, nozzle_diameter); + + // Ignore this contact area if it's too low. + if (contact_z < conf.first_layer_height - EPSILON) + continue; + + // TODO ASK how to port this. + /* + $contact{$contact_z} = [ @contact ]; + $overhang{$contact_z} = [ @overhang ]; + + require "Slic3r/SVG.pm"; + Slic3r::SVG::output("out\\contact_" . $contact_z . ".svg", + green_expolygons => union_ex($buildplate_only_top_surfaces), + blue_expolygons => union_ex(\@contact), + red_expolygons => union_ex(\@overhang), + ); + */ + } + } + return make_pair(contact, overhang); } ExPolygons *object_top(PrintObject *object, SurfacesPtr contact) @@ -436,6 +674,11 @@ public: */ } + bool test_2() + { + + } + }; } From 5a2e71283dccb9b54e6f06e40081adb84d3fb506 Mon Sep 17 00:00:00 2001 From: Samir55 Date: Wed, 11 Jul 2018 05:24:30 +0200 Subject: [PATCH 09/30] Porting object_top function. --- lib/Slic3r/Print/SupportMaterial.pm | 3 +- xs/src/libslic3r/SupportMaterial.cpp | 233 +++++++++++++++++ xs/src/libslic3r/SupportMaterial.hpp | 370 ++++++++------------------- 3 files changed, 345 insertions(+), 261 deletions(-) diff --git a/lib/Slic3r/Print/SupportMaterial.pm b/lib/Slic3r/Print/SupportMaterial.pm index 2c13cc2cd..8cb66721a 100644 --- a/lib/Slic3r/Print/SupportMaterial.pm +++ b/lib/Slic3r/Print/SupportMaterial.pm @@ -2,7 +2,7 @@ # only generate() and contact_distance() are called from the outside of this module. package Slic3r::Print::SupportMaterial; use Moo; - +use Data::Dumper; use List::Util qw(sum min max); use Slic3r::ExtrusionPath ':roles'; use Slic3r::Flow ':roles'; @@ -391,7 +391,6 @@ sub object_top { $projection = diff($projection, $touching); } } - return \%top; } diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp index ea4f5bd98..9dcc33165 100644 --- a/xs/src/libslic3r/SupportMaterial.cpp +++ b/xs/src/libslic3r/SupportMaterial.cpp @@ -1 +1,234 @@ #include "SupportMaterial.hpp" + +namespace Slic3r +{ + +Polygons +SupportMaterial::p(SurfacesPtr &surfaces) +{ + Polygons ret; + for (auto surface : surfaces) { + ret.push_back(surface->expolygon.contour); + for (const auto &hole_polygon : surface->expolygon.holes) { + ret.push_back(hole_polygon); + } + } + return ret; +} + +coordf_t +SupportMaterial::contact_distance(coordf_t layer_height, coordf_t nozzle_diameter) +{ + coordf_t extra = static_cast(object_config->support_material_contact_distance.value); + if (extra == 0) { + return layer_height; + } + else { + return nozzle_diameter + extra; + } +} + +vector +SupportMaterial::support_layers_z(vector contact_z, + vector top_z, + coordf_t max_object_layer_height) +{ + // Quick table to check whether a given Z is a top surface. + map is_top; + for (auto z : top_z) is_top[z] = true; + + // determine layer height for any non-contact layer + // we use max() to prevent many ultra-thin layers to be inserted in case + // layer_height > nozzle_diameter * 0.75. + auto nozzle_diameter = config->nozzle_diameter.get_at(object_config->support_material_extruder - 1); + auto support_material_height = (max_object_layer_height, (nozzle_diameter * 0.75)); + coordf_t _contact_distance = this->contact_distance(support_material_height, nozzle_diameter); + + // Initialize known, fixed, support layers. + vector z; + for (auto c_z : contact_z) z.push_back(c_z); + for (auto t_z : top_z) { + z.push_back(t_z); + z.push_back(t_z + _contact_distance); + } + sort(z.begin(), z.end()); + + // Enforce first layer height. + coordf_t first_layer_height = object_config->first_layer_height; + while (!z.empty() && z.front() <= first_layer_height) z.erase(z.begin()); + z.insert(z.begin(), first_layer_height); + + // Add raft layers by dividing the space between first layer and + // first contact layer evenly. + if (object_config->raft_layers > 1 && z.size() >= 2) { + // z[1] is last raft layer (contact layer for the first layer object) TODO @Samir55 How so? + coordf_t height = (z[1] - z[0]) / (object_config->raft_layers - 1); + + // since we already have two raft layers ($z[0] and $z[1]) we need to insert + // raft_layers-2 more + int idx = 1; + for (int j = 0; j < object_config->raft_layers - 2; j++) { + float z_new = + roundf(static_cast((z[0] + height * idx) * 100)) / 100; // round it to 2 decimal places. + z.insert(z.begin() + idx, z_new); + idx++; + } + } + + // Create other layers (skip raft layers as they're already done and use thicker layers). + for (size_t i = z.size(); i >= object_config->raft_layers; i--) { + coordf_t target_height = support_material_height; + if (i > 0 && is_top[z[i - 1]]) { + target_height = nozzle_diameter; + } + + // Enforce first layer height. + if ((i == 0 && z[i] > target_height + first_layer_height) + || (z[i] - z[i - 1] > target_height + EPSILON)) { + z.insert(z.begin() + i, (z[i] - target_height)); + i++; + } + } + + // Remove duplicates and make sure all 0.x values have the leading 0. + { + set s; + for (auto el : z) + s.insert(roundf(static_cast((el * 100)) / 100)); // round it to 2 decimal places. + z = vector(); + for (auto el : s) + z.push_back(el); + } + + return z; +} + +vector +SupportMaterial::overlapping_layers(int layer_idx, vector support_z) +{ + vector ret; + + float z_max = support_z[layer_idx]; + float z_min = layer_idx == 0 ? 0 : support_z[layer_idx - 1]; + + for (int i = 0; i < support_z.size(); i++) { + if (i == layer_idx) continue; + + float z_max2 = support_z[i]; + float z_min2 = i == 0 ? 0 : support_z[i - 1]; + + if (z_max > z_min2 && z_min < z_max2) + ret.push_back(i); + } + + return ret; +} + +void +SupportMaterial::clip_with_shape(map &support, map &shape) +{ + for (auto layer : support) { + // Don't clip bottom layer with shape so that we + // can generate a continuous base flange + // also don't clip raft layers + if (layer.first == 0) continue; + else if (layer.first < object_config->raft_layers) continue; + + layer.second = intersection(layer.second, shape[layer.first]); + } +} + +void +SupportMaterial::clip_with_object(map &support, vector support_z, PrintObject &object) +{ + int i = 0; + for (auto support_layer: support) { + if (support_layer.second.empty()) { + i++; + continue; + } + coordf_t z_max = support_z[i]; + coordf_t z_min = (i == 0) ? 0 : support_z[i - 1]; + + LayerPtrs layers; + for (auto layer : object.layers) { + if (layer->print_z > z_min && (layer->print_z - layer->height) < z_max) { + layers.push_back(layer); + } + } + + // $layer->slices contains the full shape of layer, thus including + // perimeter's width. $support contains the full shape of support + // material, thus including the width of its foremost extrusion. + // We leave a gap equal to a full extrusion width. TODO ask about this line @samir + Polygons slices; + for (Layer *l : layers) { + for (auto s : l->slices.contours()) { + slices.push_back(s); + } + } + support_layer.second = diff(support_layer.second, offset(slices, flow->scaled_width())); + } + /* + $support->{$i} = diff( + $support->{$i}, + offset([ map @$_, map @{$_->slices}, @layers ], +$self->flow->scaled_width), + ); + */ +} + +map +SupportMaterial::object_top(PrintObject *object, map *contact) +{ + // find object top surfaces + // we'll use them to clip our support and detect where does it stick. + map top; + if (object_config->support_material_buildplate_only.value) + return top; + + Polygons projection; + for (auto i = static_cast(object->layers.size() - 1); i >= 0; i--) { + + Layer *layer = object->layers[i]; + SurfacesPtr m_top; + + for (auto r : layer->regions) + for (auto s : r->slices.filter_by_type(stTop)) + m_top.push_back(s); + + if (!m_top.empty()) { + // compute projection of the contact areas above this top layer + // first add all the 'new' contact areas to the current projection + // ('new' means all the areas that are lower than the last top layer + // we considered). + // TODO Ask about this line + /* + my $min_top = min(keys %top) // max(keys %$contact); + */ + double min_top = top.begin()->first; + + // Use <= instead of just < because otherwise we'd ignore any contact regions + // having the same Z of top layers. + for (auto el : *contact) + if (el.first > layer->print_z && el.first <= min_top) + for (const auto &p : el.second) + projection.push_back(p); + + // Now find whether any projection falls onto this top surface. + Polygons touching = intersection(projection, p(m_top)); + if (!touching.empty()) { + // Grow top surfaces so that interface and support generation are generated + // with some spacing from object - it looks we don't need the actual + // top shapes so this can be done here. + top[layer->print_z] = offset(touching, flow->scaled_width()); + } + + // Remove the areas that touched from the projection that will continue on + // next, lower, top surfaces. + projection = diff(projection, touching); + } + } + return top; +} + +} diff --git a/xs/src/libslic3r/SupportMaterial.hpp b/xs/src/libslic3r/SupportMaterial.hpp index d899bc94c..67f141614 100644 --- a/xs/src/libslic3r/SupportMaterial.hpp +++ b/xs/src/libslic3r/SupportMaterial.hpp @@ -55,6 +55,21 @@ public: void generate() {} + void generate_interface_layers() + {} + + void generate_bottom_interface_layers() + {} + + void generate_base_layers() + {} + + void generate_toolpaths() + {} + + void generate_pillars_shape() + {} + pair contact_area(PrintObject *object) { PrintObjectConfig conf = this->object_config; @@ -408,278 +423,115 @@ public: return make_pair(contact, overhang); } - ExPolygons *object_top(PrintObject *object, SurfacesPtr contact) - { - // find object top surfaces - // we'll use them to clip our support and detect where does it stick. - ExPolygons *top = new ExPolygons(); - if (object_config->support_material_buildplate_only.value) - return top; - - } - - void generate_interface_layers() - {} - - void generate_bottom_interface_layers() - {} - - void generate_base_layers() - {} - - void generate_toolpaths() - {} - - void generate_pillars_shape() - {} + // Is this expolygons or polygons? + map object_top(PrintObject *object, map *contact); // This method removes object silhouette from support material // (it's used with interface and base only). It removes a bit more, // leaving a thin gap between object and support in the XY plane. - void clip_with_object(map &support, vector support_z, PrintObject &object) - { - int i = 0; - for (auto support_layer: support) { - if (support_layer.second.empty()) { - i++; - continue; - } - coordf_t z_max = support_z[i]; - coordf_t z_min = (i == 0) ? 0 : support_z[i - 1]; + void clip_with_object(map &support, vector support_z, PrintObject &object); - LayerPtrs layers; - for (auto layer : object.layers) { - if (layer->print_z > z_min && (layer->print_z - layer->height) < z_max) { - layers.push_back(layer); - } - } - - // $layer->slices contains the full shape of layer, thus including - // perimeter's width. $support contains the full shape of support - // material, thus including the width of its foremost extrusion. - // We leave a gap equal to a full extrusion width. TODO ask about this line @samir - Polygons slices; - for (Layer *l : layers) { - for (auto s : l->slices.contours()) { - slices.push_back(s); - } - } - support_layer.second = diff(support_layer.second, offset(slices, flow->scaled_width())); - } - /* - $support->{$i} = diff( - $support->{$i}, - offset([ map @$_, map @{$_->slices}, @layers ], +$self->flow->scaled_width), - ); - */ - } - - void clip_with_shape(map &support, map &shape) - { - for (auto layer : support) { - // Don't clip bottom layer with shape so that we - // can generate a continuous base flange - // also don't clip raft layers - if (layer.first == 0) continue; - else if (layer.first < object_config->raft_layers) continue; - - layer.second = intersection(layer.second, shape[layer.first]); - } - } + void clip_with_shape(map &support, map &shape); /// This method returns the indices of the layers overlapping with the given one. - vector overlapping_layers(int layer_idx, vector support_z) - { - vector ret; + vector overlapping_layers(int layer_idx, vector support_z); - float z_max = support_z[layer_idx]; - float z_min = layer_idx == 0 ? 0 : support_z[layer_idx - 1]; + vector support_layers_z(vector contact_z, vector top_z, coordf_t max_object_layer_height); - for (int i = 0; i < support_z.size(); i++) { - if (i == layer_idx) continue; - - float z_max2 = support_z[i]; - float z_min2 = i == 0 ? 0 : support_z[i - 1]; - - if (z_max > z_min2 && z_min < z_max2) - ret.push_back(i); - } - - return ret; - } - - vector support_layers_z(vector contact_z, vector top_z, coordf_t max_object_layer_height) - { - // Quick table to check whether a given Z is a top surface. - map is_top; - for (auto z : top_z) is_top[z] = true; - - // determine layer height for any non-contact layer - // we use max() to prevent many ultra-thin layers to be inserted in case - // layer_height > nozzle_diameter * 0.75. - auto nozzle_diameter = config->nozzle_diameter.get_at(object_config->support_material_extruder - 1); - auto support_material_height = (max_object_layer_height, (nozzle_diameter * 0.75)); - coordf_t _contact_distance = this->contact_distance(support_material_height, nozzle_diameter); - - // Initialize known, fixed, support layers. - vector z; - for (auto c_z : contact_z) z.push_back(c_z); - for (auto t_z : top_z) { - z.push_back(t_z); - z.push_back(t_z + _contact_distance); - } - sort(z.begin(), z.end()); - - // Enforce first layer height. - coordf_t first_layer_height = object_config->first_layer_height; - while (!z.empty() && z.front() <= first_layer_height) z.erase(z.begin()); - z.insert(z.begin(), first_layer_height); - - // Add raft layers by dividing the space between first layer and - // first contact layer evenly. - if (object_config->raft_layers > 1 && z.size() >= 2) { - // z[1] is last raft layer (contact layer for the first layer object) TODO @Samir55 How so? - coordf_t height = (z[1] - z[0]) / (object_config->raft_layers - 1); - - // since we already have two raft layers ($z[0] and $z[1]) we need to insert - // raft_layers-2 more - int idx = 1; - for (int j = 0; j < object_config->raft_layers - 2; j++) { - float z_new = - roundf(static_cast((z[0] + height * idx) * 100)) / 100; // round it to 2 decimal places. - z.insert(z.begin() + idx, z_new); - idx++; - } - } - - // Create other layers (skip raft layers as they're already done and use thicker layers). - for (size_t i = z.size(); i >= object_config->raft_layers; i--) { - coordf_t target_height = support_material_height; - if (i > 0 && is_top[z[i - 1]]) { - target_height = nozzle_diameter; - } - - // Enforce first layer height. - if ((i == 0 && z[i] > target_height + first_layer_height) - || (z[i] - z[i - 1] > target_height + EPSILON)) { - z.insert(z.begin() + i, (z[i] - target_height)); - i++; - } - } - - // Remove duplicates and make sure all 0.x values have the leading 0. - { - set s; - for (auto el : z) - s.insert(roundf(static_cast((el * 100)) / 100)); // round it to 2 decimal places. - z = vector(); - for (auto el : s) - z.push_back(el); - } - - return z; - } - - coordf_t contact_distance(coordf_t layer_height, coordf_t nozzle_diameter) - { - coordf_t extra = static_cast(object_config->support_material_contact_distance.value); - if (extra == 0) { - return layer_height; - } - else { - return nozzle_diameter + extra; - } - } + coordf_t contact_distance(coordf_t layer_height, coordf_t nozzle_diameter); + Polygons p(SurfacesPtr &surfaces); }; -class SupportMaterialTests -{ -public: - bool test_1() - { - // Create a mesh & modelObject. - TriangleMesh mesh = TriangleMesh::make_cube(20, 20, 20); +//// TO Be converted to catch. +//class SupportMaterialTests +//{ +//public: +// bool test_1() +// { +// // Create a mesh & modelObject. +// TriangleMesh mesh = TriangleMesh::make_cube(20, 20, 20); +// +// // Create modelObject. +// Model model = Model(); +// ModelObject *object = model.add_object(); +// object->add_volume(mesh); +// model.add_default_instances(); +// +// // Align to origin. +// model.align_instances_to_origin(); +// +// // Create Print. +// Print print = Print(); +// +// // Configure the printObjectConfig. +// print.default_object_config.set_deserialize("support_material", "1"); +// print.default_object_config.set_deserialize("layer_height", "0.2"); +// print.config.set_deserialize("first_layer_height", "0.3"); +// +// vector contact_z; +// vector top_z; +// contact_z.push_back(1.9); +// top_z.push_back(1.9); +// +// // Add the modelObject. +// print.add_model_object(model.objects[0]); +// +// // Create new supports. +// SupportMaterial support = SupportMaterial(&print.config, &print.objects.front()->config); +// +// vector +// support_z = support.support_layers_z(contact_z, top_z, print.default_object_config.layer_height); +// +// bool +// is_1 = (support_z[0] == print.default_object_config.first_layer_height); // 'first layer height is honored'. +// +// bool is_2 = false; // 'no null or negative support layers'. +// for (int i = 1; i < support_z.size(); ++i) { +// if (support_z[i] - support_z[i - 1] <= 0) is_2 = true; +// } +// +// bool is_3 = false; // 'no layers thicker than nozzle diameter'. +// for (int i = 1; i < support_z.size(); ++i) { +// if (support_z[i] - support_z[i - 1] > print.config.nozzle_diameter.get_at(0) + EPSILON) is_2 = true; +// } +// +// coordf_t expected_top_spacing = +// support.contact_distance(print.default_object_config.layer_height, print.config.nozzle_diameter.get_at(0)); +// coordf_t wrong_top_spacing = 0; +// for (auto top_z_el : top_z) { +// // find layer index of this top surface. +// int layer_id = -1; +// for (int i = 0; i < support_z.size(); i++) { +// if (abs(support_z[i] - top_z_el) < EPSILON) { +// layer_id = i; +// i = static_cast(support_z.size()); +// } +// } +// +// // check that first support layer above this top surface (or the next one) is spaced with nozzle diameter +// if ((support_z[layer_id + 1] - support_z[layer_id]) != expected_top_spacing +// && (support_z[layer_id + 2] - support_z[layer_id]) != expected_top_spacing) +// wrong_top_spacing = 1; +// } +// bool is_4 = !wrong_top_spacing; // 'layers above top surfaces are spaced correctly' +// +// /* Test Also with this +// $config->set('first_layer_height', 0.4); +// $test->(); +// +// $config->set('layer_height', $config->nozzle_diameter->[0]); +// $test->(); +// */ +// } +// +// bool test_2() +// { +// +// } +// +//}; - // Create modelObject. - Model model = Model(); - ModelObject *object = model.add_object(); - object->add_volume(mesh); - model.add_default_instances(); - - // Align to origin. - model.align_instances_to_origin(); - - // Create Print. - Print print = Print(); - - // Configure the printObjectConfig. - print.default_object_config.set_deserialize("support_material", "1"); - print.default_object_config.set_deserialize("layer_height", "0.2"); - print.config.set_deserialize("first_layer_height", "0.3"); - - vector contact_z; - vector top_z; - contact_z.push_back(1.9); - top_z.push_back(1.9); - - // Add the modelObject. - print.add_model_object(model.objects[0]); - - // Create new supports. - SupportMaterial support = SupportMaterial(&print.config, &print.objects.front()->config); - - vector - support_z = support.support_layers_z(contact_z, top_z, print.default_object_config.layer_height); - - bool - is_1 = (support_z[0] == print.default_object_config.first_layer_height); // 'first layer height is honored'. - - bool is_2 = false; // 'no null or negative support layers'. - for (int i = 1; i < support_z.size(); ++i) { - if (support_z[i] - support_z[i - 1] <= 0) is_2 = true; - } - - bool is_3 = false; // 'no layers thicker than nozzle diameter'. - for (int i = 1; i < support_z.size(); ++i) { - if (support_z[i] - support_z[i - 1] > print.config.nozzle_diameter.get_at(0) + EPSILON) is_2 = true; - } - - coordf_t expected_top_spacing = - support.contact_distance(print.default_object_config.layer_height, print.config.nozzle_diameter.get_at(0)); - coordf_t wrong_top_spacing = 0; - for (auto top_z_el : top_z) { - // find layer index of this top surface. - int layer_id = -1; - for (int i = 0; i < support_z.size(); i++) { - if (abs(support_z[i] - top_z_el) < EPSILON) { - layer_id = i; - i = static_cast(support_z.size()); - } - } - - // check that first support layer above this top surface (or the next one) is spaced with nozzle diameter - if ((support_z[layer_id + 1] - support_z[layer_id]) != expected_top_spacing - && (support_z[layer_id + 2] - support_z[layer_id]) != expected_top_spacing) - wrong_top_spacing = 1; - } - bool is_4 = !wrong_top_spacing; // 'layers above top surfaces are spaced correctly' - - /* Test Also with this - $config->set('first_layer_height', 0.4); - $test->(); - - $config->set('layer_height', $config->nozzle_diameter->[0]); - $test->(); - */ - } - - bool test_2() - { - - } - -}; } #endif From 50acce9f54556a9ad089e6ac0f64eb3a00056e66 Mon Sep 17 00:00:00 2001 From: Samir55 Date: Wed, 11 Jul 2018 07:27:45 +0200 Subject: [PATCH 10/30] Some Refactoring. --- xs/src/libslic3r/SupportMaterial.cpp | 402 +++++++++++++++++++++ xs/src/libslic3r/SupportMaterial.hpp | 511 ++++++--------------------- 2 files changed, 502 insertions(+), 411 deletions(-) diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp index 9dcc33165..4c4735b42 100644 --- a/xs/src/libslic3r/SupportMaterial.cpp +++ b/xs/src/libslic3r/SupportMaterial.cpp @@ -2,6 +2,22 @@ namespace Slic3r { +Polygon +SupportMaterial::create_circle(coordf_t radius) +{ + Points points; + coordf_t positions[] = {5 * PI / 3, + 4 * PI / 3, + PI, + 2 * PI / 3, + PI / 3, + 0}; + for (auto pos : positions) { + points.push_back(Point(radius * cos(pos), (radius * sin(pos)))); + } + + return Polygon(points); +} Polygons SupportMaterial::p(SurfacesPtr &surfaces) @@ -16,6 +32,14 @@ SupportMaterial::p(SurfacesPtr &surfaces) return ret; } +void +SupportMaterial::append_polygons(Polygons &dst, Polygons &src) +{ + for (const auto polygon : src) { + dst.push_back(polygon); + } +} + coordf_t SupportMaterial::contact_distance(coordf_t layer_height, coordf_t nozzle_diameter) { @@ -231,4 +255,382 @@ SupportMaterial::object_top(PrintObject *object, map *contac return top; } +void +SupportMaterial::generate_toolpaths(PrintObject *object, + map overhang, + map contact, + map interface, + map base) +{ + // Assig the object to the supports class. + this->object = object; + + // Shape of contact area. + int contact_loops = 1; + coordf_t circle_radius = 1.5 * interface_flow->scaled_width(); + coordf_t circle_distance = 3 * circle_radius; + Polygon circle = create_circle(circle_radius); + + // TODO Add Slic3r debug. "Generating patterns\n" + // Prepare fillers. + auto pattern = object_config->support_material_pattern; + vector angles; + angles.push_back(object_config->support_material_angle.value); + + if (pattern == smpRectilinearGrid) { + pattern = smpRectilinear; + angles.push_back(angles[0] + 90); + } + else if (pattern == smpPillars) { + pattern = smpHoneycomb; + } + + auto interface_angle = object_config->support_material_angle.value + 90; + auto interface_spacing = object_config->support_material_interface_spacing.value + interface_flow->spacing(); + auto interface_density = interface_spacing == 0 ? 1 : interface_flow->spacing() / interface_spacing; + auto support_spacing = object_config->support_material_spacing + flow->spacing(); + auto support_density = support_spacing == 0 ? 1 : flow->spacing() / support_spacing; + + parallelize( + 0, + object->support_layers.size() - 1, + boost::bind(&SupportMaterial::process_layer, this, _1), + this->config->threads.value + ); +} + +pair, map> +SupportMaterial::contact_area(PrintObject *object) +{ + PrintObjectConfig conf = this->object_config; + + // If user specified a custom angle threshold, convert it to radians. + float threshold_rad; + cout << conf.support_material_threshold << endl; + if (conf.support_material_threshold > 0) { + threshold_rad = static_cast(Geometry::deg2rad( + conf.support_material_threshold.value + 1)); // +1 makes the threshold inclusive + // TODO @Samir55 add debug statetments. + } + + // Build support on a build plate only? If so, then collect top surfaces into $buildplate_only_top_surfaces + // and subtract buildplate_only_top_surfaces from the contact surfaces, so + // there is no contact surface supported by a top surface. + bool buildplate_only = + (conf.support_material || conf.support_material_enforce_layers) + && conf.support_material_buildplate_only; + Polygons buildplate_only_top_surfaces; + + // Determine contact areas. + map contact; + map overhang; // This stores the actual overhang supported by each contact layer + + for (int layer_id = 0; layer_id < object->layers.size(); layer_id++) { + // Note $layer_id might != $layer->id when raft_layers > 0 + // so $layer_id == 0 means first object layer + // and $layer->id == 0 means first print layer (including raft). + + // If no raft, and we're at layer 0, skip to layer 1 + if (conf.raft_layers == 0 && layer_id == 0) + continue; + + // With or without raft, if we're above layer 1, we need to quit + // support generation if supports are disabled, or if we're at a high + // enough layer that enforce-supports no longer applies. + if (layer_id > 0 + && !conf.support_material + && (layer_id >= conf.support_material_enforce_layers)) + // If we are only going to generate raft just check + // the 'overhangs' of the first object layer. + break; + + Layer *layer = object->get_layer(layer_id); + + if (conf.support_material_max_layers + && layer_id > conf.support_material_max_layers) + break; + + if (buildplate_only) { + // Collect the top surfaces up to this layer and merge them. TODO @Ask about this line. + Polygons projection_new; + for (auto const ®ion : layer->regions) { + SurfacesPtr top_surfaces = region->slices.filter_by_type(stTop); + for (auto polygon : p(top_surfaces)) { + projection_new.push_back(polygon); + } + } + if (!projection_new.empty()) { + // Merge the new top surfaces with the preceding top surfaces. + // Apply the safety offset to the newly added polygons, so they will connect + // with the polygons collected before, + // but don't apply the safety offset during the union operation as it would + // inflate the polygons over and over. + Polygons polygons = offset(projection_new, scale_(0.01)); + append_polygons(buildplate_only_top_surfaces, polygons); + + buildplate_only_top_surfaces = union_(buildplate_only_top_surfaces, 0); + } + } + + // Detect overhangs and contact areas needed to support them. + Polygons m_overhang, m_contact; + if (layer_id == 0) { + // this is the first object layer, so we're here just to get the object + // footprint for the raft. + // we only consider contours and discard holes to get a more continuous raft. + for (auto const &contour : layer->slices.contours()) { + auto contour_clone = contour; + m_overhang.push_back(contour_clone); + } + + Polygons polygons = offset(m_overhang, scale_(+SUPPORT_MATERIAL_MARGIN)); + append_polygons(m_contact, polygons); + } + else { + Layer *lower_layer = object->get_layer(layer_id - 1); + for (auto layerm : layer->regions) { + float fw = layerm->flow(frExternalPerimeter).scaled_width(); + Polygons difference; + + // If a threshold angle was specified, use a different logic for detecting overhangs. + if ((conf.support_material && threshold_rad > 0) + || layer_id <= conf.support_material_enforce_layers + || (conf.raft_layers > 0 && layer_id + == 0)) { // TODO ASK @Samir why layer_id ==0 check , layer_id will never equal to zero + float d = 0; + float layer_threshold_rad = threshold_rad; + if (layer_id <= conf.support_material_enforce_layers) { + // Use ~45 deg number for enforced supports if we are in auto. + layer_threshold_rad = static_cast(Geometry::deg2rad(89)); + } + if (layer_threshold_rad > 0) { + d = scale_(lower_layer->height + * ((cos(layer_threshold_rad)) / (sin(layer_threshold_rad)))); + } + + // TODO Check. + difference = diff( + Polygons(layerm->slices), + offset(lower_layer->slices, +d) + ); + + // only enforce spacing from the object ($fw/2) if the threshold angle + // is not too high: in that case, $d will be very small (as we need to catch + // very short overhangs), and such contact area would be eaten by the + // enforced spacing, resulting in high threshold angles to be almost ignored + if (d > fw / 2) { + difference = diff( + offset(difference, d - fw / 2), + lower_layer->slices); + } + } + else { + difference = diff( + Polygons(layerm->slices), + offset(lower_layer->slices, + static_cast(+conf.get_abs_value("support_material_threshold", fw))) + ); + + // Collapse very tiny spots. + difference = offset2(difference, -fw / 10, +fw / 10); + // $diff now contains the ring or stripe comprised between the boundary of + // lower slices and the centerline of the last perimeter in this overhanging layer. + // Void $diff means that there's no upper perimeter whose centerline is + // outside the lower slice boundary, thus no overhang + } + + if (conf.dont_support_bridges) { + // Compute the area of bridging perimeters. + Polygons bridged_perimeters; + { + auto bridge_flow = layerm->flow(FlowRole::frPerimeter, 1); + + // Get the lower layer's slices and grow them by half the nozzle diameter + // because we will consider the upper perimeters supported even if half nozzle + // falls outside the lower slices. + Polygons lower_grown_slices; + { + coordf_t nozzle_diameter = this->config->nozzle_diameter + .get_at(static_cast(layerm->region()->config.perimeter_extruder - 1)); + + lower_grown_slices = offset( + lower_layer->slices, + scale_(nozzle_diameter / 2) + ); + } + + // Get all perimeters as polylines. + // TODO: split_at_first_point() (called by as_polyline() for ExtrusionLoops) + // could split a bridge mid-way. + Polylines overhang_perimeters; + overhang_perimeters.push_back(layerm->perimeters.flatten().as_polyline()); + + // Only consider the overhang parts of such perimeters, + // overhangs being those parts not supported by + // workaround for Clipper bug, see Slic3r::Polygon::clip_as_polyline() + overhang_perimeters[0].translate(1, 0); + overhang_perimeters = diff_pl(overhang_perimeters, lower_grown_slices); + + // Only consider straight overhangs. + Polylines new_overhangs_perimeters_polylines; + for (auto p : overhang_perimeters) + if (p.is_straight()) + new_overhangs_perimeters_polylines.push_back(p); + + overhang_perimeters = new_overhangs_perimeters_polylines; + new_overhangs_perimeters_polylines = Polylines(); + + // Only consider overhangs having endpoints inside layer's slices + for (auto &p : overhang_perimeters) { + p.extend_start(fw); + p.extend_end(fw); + } + + for (auto p : overhang_perimeters) { + if (layer->slices.contains_b(p.first_point()) + && layer->slices.contains_b(p.last_point())) { + new_overhangs_perimeters_polylines.push_back(p); + } + } + + overhang_perimeters = new_overhangs_perimeters_polylines; + new_overhangs_perimeters_polylines = Polylines(); + + // Convert bridging polylines into polygons by inflating them with their thickness. + { + // For bridges we can't assume width is larger than spacing because they + // are positioned according to non-bridging perimeters spacing. + coordf_t widths[] = {bridge_flow.scaled_width(), + bridge_flow.scaled_spacing(), + fw, + layerm->flow(FlowRole::frPerimeter).scaled_width()}; + + coordf_t w = *max_element(widths, widths + 4); + + // Also apply safety offset to ensure no gaps are left in between. + // TODO CHeck this. + for (auto &p : overhang_perimeters) { + Polygons ps = union_(offset(p, w / 2 + 10)); + for (auto ps_el : ps) + bridged_perimeters.push_back(ps_el); + } + } + } + + if (1) { + // Remove the entire bridges and only support the unsupported edges. + ExPolygons bridges; + for (auto surface : layerm->fill_surfaces.filter_by_type(stBottomBridge)) { + if (surface->bridge_angle != -1) { + bridges.push_back(surface->expolygon); + } + } + + ExPolygons ps; + for (auto p : bridged_perimeters) + ps.push_back(ExPolygon(p)); + for (auto p : bridges) + ps.push_back(p); + + // TODO ASK about this. + difference = diff( + difference, + to_polygons(ps), + true + ); + + // TODO Ask about this. + auto p_intersections = intersection(offset(layerm->unsupported_bridge_edges.polylines, + +scale_(SUPPORT_MATERIAL_MARGIN)), + to_polygons(bridges)); + for (auto p: p_intersections) { + difference.push_back(p); + } + } + else { + // just remove bridged areas. + difference = diff( + difference, + layerm->bridged, + 1 + ); + } + } // if ($conf->dont_support_bridges) + + if (buildplate_only) { + // Don't support overhangs above the top surfaces. + // This step is done before the contact surface is calcuated by growing the overhang region. + difference = diff(difference, buildplate_only_top_surfaces); + } + + if (difference.empty()) continue; + + // NOTE: this is not the full overhang as it misses the outermost half of the perimeter width! + append_polygons(m_overhang, difference); + + // 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). + { + Polygons slices_margin = offset(lower_layer->slices, +fw / 2); + + if (buildplate_only) { + // Trim the inflated contact surfaces by the top surfaces as well. + append_polygons(slices_margin, buildplate_only_top_surfaces); + slices_margin = union_(slices_margin); + } + + // TODO Ask how to port this. + /* + for ($fw/2, map {scale MARGIN_STEP} 1..(MARGIN / MARGIN_STEP)) { + $diff = diff( + offset($diff, $_), + $slices_margin, + ); + } + */ + } + + append_polygons(m_contact, difference); + } + } + if (contact.empty()) + continue; + + // Now apply the contact areas to the layer were they need to be made. + { + // Get the average nozzle diameter used on this layer. + vector nozzle_diameters; + for (auto region : layer->regions) { + nozzle_diameters.push_back(config->nozzle_diameter.get_at(static_cast( + region->region()->config + .perimeter_extruder - 1))); + nozzle_diameters.push_back(config->nozzle_diameter.get_at(static_cast( + region->region()->config + .infill_extruder - 1))); + nozzle_diameters.push_back(config->nozzle_diameter.get_at(static_cast( + region->region()->config + .solid_infill_extruder - 1))); + } + + int nozzle_diameters_count = static_cast(nozzle_diameters.size() > 0 ? nozzle_diameters.size() : 1); + auto nozzle_diameter = + accumulate(nozzle_diameters.begin(), nozzle_diameters.end(), 0.0) / nozzle_diameters_count; + + coordf_t contact_z = layer->print_z - contact_distance(layer->height, nozzle_diameter); + + // Ignore this contact area if it's too low. + if (contact_z < conf.first_layer_height - EPSILON) + continue; + + + contact[contact_z] = m_contact; + overhang[contact_z] = m_overhang; + } + } + + return make_pair(contact, overhang); +} + } diff --git a/xs/src/libslic3r/SupportMaterial.hpp b/xs/src/libslic3r/SupportMaterial.hpp index 67f141614..73f6ba513 100644 --- a/xs/src/libslic3r/SupportMaterial.hpp +++ b/xs/src/libslic3r/SupportMaterial.hpp @@ -49,7 +49,8 @@ public: object_config(print_object_config), flow(flow), first_layer_flow(first_layer_flow), - interface_flow(interface_flow) + interface_flow(interface_flow), + object(nullptr) {} void generate() @@ -64,364 +65,16 @@ public: void generate_base_layers() {} - void generate_toolpaths() - {} - void generate_pillars_shape() {} - pair contact_area(PrintObject *object) - { - PrintObjectConfig conf = this->object_config; + pair, map> contact_area(PrintObject *object); - // If user specified a custom angle threshold, convert it to radians. - float threshold_rad; - cout << conf.support_material_threshold << endl; - if (conf.support_material_threshold > 0) { - threshold_rad = static_cast(Geometry::deg2rad( - conf.support_material_threshold.value + 1)); // +1 makes the threshold inclusive - // TODO @Samir55 add debug statetments. - } - - // Build support on a build plate only? If so, then collect top surfaces into $buildplate_only_top_surfaces - // and subtract buildplate_only_top_surfaces from the contact surfaces, so - // there is no contact surface supported by a top surface. - bool buildplate_only = - (conf.support_material || conf.support_material_enforce_layers) - && conf.support_material_buildplate_only; - Surfaces buildplate_only_top_surfaces; - - // Determine contact areas. - Polygons contact; - Polygons overhang; // This stores the actual overhang supported by each contact layer - - for (int layer_id = 0; layer_id < object->layers.size(); layer_id++) { - // Note $layer_id might != $layer->id when raft_layers > 0 - // so $layer_id == 0 means first object layer - // and $layer->id == 0 means first print layer (including raft). - - // If no raft, and we're at layer 0, skip to layer 1 - if (conf.raft_layers == 0 && layer_id == 0) - continue; - - // With or without raft, if we're above layer 1, we need to quit - // support generation if supports are disabled, or if we're at a high - // enough layer that enforce-supports no longer applies. - if (layer_id > 0 - && !conf.support_material - && (layer_id >= conf.support_material_enforce_layers)) - // If we are only going to generate raft just check - // the 'overhangs' of the first object layer. - break; - - auto layer = object->get_layer(layer_id); - - if (conf.support_material_max_layers - && layer_id > conf.support_material_max_layers) - break; - - ExPolygons buildplate_only_top_surfaces_polygons; - if (buildplate_only) { - // Collect the top surfaces up to this layer and merge them. TODO @Ask about this line. - SurfacesPtr projection_new; - for (auto const ®ion : layer->regions) { - SurfacesPtr top_surfaces = region->slices.filter_by_type(stTop); - for (auto top_surface : top_surfaces) { - projection_new.push_back(top_surface); - } - } - if (!projection_new.empty()) { - // Merge the new top surfaces with the preceding top surfaces. - // Apply the safety offset to the newly added polygons, so they will connect - // with the polygons collected before, - // but don't apply the safety offset during the union operation as it would - // inflate the polygons over and over. - for (Surface *surface : projection_new) { - Surfaces s = offset(*surface, scale_(0.01)); - for (auto el : s) - buildplate_only_top_surfaces.push_back(el); - } - // TODO @Ask about this - buildplate_only_top_surfaces_polygons = union_ex(buildplate_only_top_surfaces, 0); - } - } - - // Detect overhangs and contact areas needed to support them. - if (layer_id == 0) { - // this is the first object layer, so we're here just to get the object - // footprint for the raft. - // we only consider contours and discard holes to get a more continuous raft. - for (auto const &contour : layer->slices.contours()) { - auto contour_clone = contour; - overhang.push_back(contour_clone); - } - - Polygons ps = offset(overhang, scale_(+SUPPORT_MATERIAL_MARGIN)); - for (auto p : ps) - contact.push_back(p); - } - else { - Layer *lower_layer = object->get_layer(layer_id - 1); - for (auto layerm : layer->regions) { - float fw = layerm->flow(frExternalPerimeter).scaled_width(); - Polygons difference; - - // If a threshold angle was specified, use a different logic for detecting overhangs. - if ((conf.support_material && threshold_rad > 0) - || layer_id <= conf.support_material_enforce_layers - || (conf.raft_layers > 0 && layer_id == 0)) { - float d = 0; - float layer_threshold_rad = threshold_rad; - if (layer_id <= conf.support_material_enforce_layers) { - // Use ~45 deg number for enforced supports if we are in auto. - layer_threshold_rad = Geometry::deg2rad(89); - } - if (layer_threshold_rad > 0) { - d = scale_(lower_layer->height - * ((cos(layer_threshold_rad)) / (sin(layer_threshold_rad)))); - } - - // TODO Ask about this. - difference = diff( - layerm->slices, - offset(lower_layer->slices, +d) - ); - - // only enforce spacing from the object ($fw/2) if the threshold angle - // is not too high: in that case, $d will be very small (as we need to catch - // very short overhangs), and such contact area would be eaten by the - // enforced spacing, resulting in high threshold angles to be almost ignored - if (d > fw / 2) { - difference = diff( - offset(difference, d - fw / 2), - lower_layer->slices); - } - } - else { - difference = diff( - layerm->slices, - offset(lower_layer->slices, - static_cast(+conf.get_abs_value("support_material_threshols", fw))) - ); - - // Collapse very tiny spots. - difference = offset2(difference, -fw / 10, +fw / 10); - // $diff now contains the ring or stripe comprised between the boundary of - // lower slices and the centerline of the last perimeter in this overhanging layer. - // Void $diff means that there's no upper perimeter whose centerline is - // outside the lower slice boundary, thus no overhang - } - - if (conf.dont_support_bridges) { - // Compute the area of bridging perimeters. - Polygons bridged_perimeters; - { - auto bridge_flow = layerm->flow(FlowRole::frPerimeter, 1); - - // Get the lower layer's slices and grow them by half the nozzle diameter - // because we will consider the upper perimeters supported even if half nozzle - // falls outside the lower slices. - Polygons lower_grown_slices; - { - coordf_t nozzle_diameter = this->config->nozzle_diameter - .get_at(layerm->region()->config.perimeter_extruder - 1); - - lower_grown_slices = offset( - lower_layer->slices, - scale_(nozzle_diameter / 2) - ); - } - - // Get all perimeters as polylines. - // TODO: split_at_first_point() (called by as_polyline() for ExtrusionLoops) - // could split a bridge mid-way. - Polyline overhang_perimeters_polyline = layerm->perimeters.flatten().as_polyline(); - Polylines overhang_perimeters_polylines; - - // Only consider the overhang parts of such perimeters, - // overhangs being those parts not supported by - // workaround for Clipper bug, see Slic3r::Polygon::clip_as_polyline() - // ASk About htis. - overhang_perimeters_polyline.translate(1, 0); - Polylines ps = diff_pl(overhang_perimeters_polyline, lower_grown_slices); - for (const auto &p : ps) { - overhang_perimeters_polylines.push_back(p); - } - - // Only consider straight overhangs. - Polylines new_overhangs_perimeters_polylines; - for (auto p : overhang_perimeters_polylines) { - if (p.is_straight()) - new_overhangs_perimeters_polylines.push_back(p); - } - overhang_perimeters_polylines = new_overhangs_perimeters_polylines; - new_overhangs_perimeters_polylines = Polylines(); - - // Only consider overhangs having endpoints inside layer's slices - for (auto &p : overhang_perimeters_polylines) { - p.extend_start(fw); - p.extend_end(fw); - } - - for (auto p : overhang_perimeters_polylines) { - if (layer->slices.contains_b(p.first_point()) - && layer->slices.contains_b(p.last_point())) { - new_overhangs_perimeters_polylines.push_back(p); - } - } - - overhang_perimeters_polylines = new_overhangs_perimeters_polylines; - new_overhangs_perimeters_polylines = Polylines(); - - // Convert bridging polylines into polygons by inflating them with their thickness. - { - // For bridges we can't assume width is larger than spacing because they - // are positioned according to non-bridging perimeters spacing. - coordf_t widths[] = {bridge_flow.scaled_width(), - bridge_flow.scaled_spacing(), - fw, - layerm->flow(FlowRole::frPerimeter).scaled_width()}; - - coordf_t w = *max_element(widths, widths + 4); - - // Also apply safety offset to ensure no gaps are left in between. - for (auto &p : overhang_perimeters_polylines) { - Polygons ps = union_(offset(p, w / 2 + 10)); - for (auto ps_el : ps) - bridged_perimeters.push_back(ps_el); - } - } - } - - if (1) { - // Remove the entire bridges and only support the unsupported edges. - ExPolygons bridges; - for (auto surface : layerm->fill_surfaces.filter_by_type(stBottomBridge)) { - if (surface->bridge_angle != -1) { - bridges.push_back(surface->expolygon); - } - } - - ExPolygons ps; - for (auto p : bridged_perimeters) - ps.push_back(ExPolygon(p)); - for (auto p : bridges) - ps.push_back(p); - - // TODO ASK about this. - difference = diff( - difference, - to_polygons(ps), - true - ); - - // TODO Ask about this. - auto p_intersections = intersection(offset(layerm->unsupported_bridge_edges.polylines, - +scale_(SUPPORT_MATERIAL_MARGIN)), - to_polygons(bridges)); - for (auto p: p_intersections) { - difference.push_back(p); - } - } - else { - // just remove bridged areas. - difference = diff( - difference, - layerm->bridged, - 1 - ); - } - } // if ($conf->dont_support_bridges) - - if (buildplate_only) { - // Don't support overhangs above the top surfaces. - // This step is done before the contact surface is calcuated by growing the overhang region. - // TODO Ask about this. - difference = diff(difference, to_polygons(buildplate_only_top_surfaces_polygons)); - } - - if (difference.empty()) continue; - - // NOTE: this is not the full overhang as it misses the outermost half of the perimeter width! - for (auto p : difference) { - overhang.push_back(p); - } - - // 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). - { - auto slices_margin = offset(lower_layer->slices, +fw / 2); - - if (buildplate_only) { - // TODO Ask about this. - // Trim the inflated contact surfaces by the top surfaces as well. - for (auto s_p : to_polygons(buildplate_only_top_surfaces)) { - slices_margin.push_back(s_p); - } - slices_margin = union_(slices_margin); - } - - // TODO Ask how to port this. - /* - for ($fw/2, map {scale MARGIN_STEP} 1..(MARGIN / MARGIN_STEP)) { - $diff = diff( - offset($diff, $_), - $slices_margin, - ); - } - */ - } - - for (auto p : difference) - contact.push_back(p); - } - } - if (contact.empty()) - continue; - - // Now apply the contact areas to the layer were they need to be made. - { - // Get the average nozzle diameter used on this layer. - vector nozzle_diameters; - for (auto region : layer->regions) { - nozzle_diameters.push_back(config->nozzle_diameter.get_at(static_cast( - region->region()->config - .perimeter_extruder - 1))); - nozzle_diameters.push_back(config->nozzle_diameter.get_at(static_cast( - region->region()->config - .infill_extruder - 1))); - nozzle_diameters.push_back(config->nozzle_diameter.get_at(static_cast( - region->region()->config - .solid_infill_extruder - 1))); - } - - auto nozzle_diameter = - accumulate(nozzle_diameters.begin(), nozzle_diameters.end(), 0.0) / nozzle_diameters.size(); - - auto contact_z = layer->print_z - contact_distance(layer->height, nozzle_diameter); - - // Ignore this contact area if it's too low. - if (contact_z < conf.first_layer_height - EPSILON) - continue; - - // TODO ASK how to port this. - /* - $contact{$contact_z} = [ @contact ]; - $overhang{$contact_z} = [ @overhang ]; - - require "Slic3r/SVG.pm"; - Slic3r::SVG::output("out\\contact_" . $contact_z . ".svg", - green_expolygons => union_ex($buildplate_only_top_surfaces), - blue_expolygons => union_ex(\@contact), - red_expolygons => union_ex(\@overhang), - ); - */ - } - } - return make_pair(contact, overhang); - } + void generate_toolpaths(PrintObject *object, + map overhang, + map contact, + map interface, + map base); // Is this expolygons or polygons? map object_top(PrintObject *object, map *contact); @@ -441,48 +94,84 @@ public: coordf_t contact_distance(coordf_t layer_height, coordf_t nozzle_diameter); Polygons p(SurfacesPtr &surfaces); + + Polygon create_circle(coordf_t radius); + + void append_polygons(Polygons &dst, Polygons &src); + + void process_layer(int layer_id) + { + SupportLayer *layer = this->object->support_layers[layer_id]; + coordf_t z = layer->print_z; + + // We redefine flows locally by applyinh this layer's height. + auto _flow = *flow; + auto _interface_flow = *interface_flow; + _flow.height = static_cast(layer->height); + _interface_flow.height = static_cast(layer->height); + + Polygons overhang = this->overhang.count(z) > 0 ? this->overhang[z] : Polygons(); + Polygons contact = this->contact.count(z) > 0 ? this->contact[z] : Polygons(); + Polygons interface = interface[layer_id]; + Polygons base = base[layer_id]; + + // TODO add the equivalent debug code. + + // Islands. +// layer->support_islands.append() + + } + +private: + // Used during generate_toolpaths function. + PrintObject *object; + map overhang; + map contact; + map interface; + map base; + }; -//// TO Be converted to catch. -//class SupportMaterialTests -//{ -//public: -// bool test_1() -// { -// // Create a mesh & modelObject. -// TriangleMesh mesh = TriangleMesh::make_cube(20, 20, 20); -// -// // Create modelObject. -// Model model = Model(); -// ModelObject *object = model.add_object(); -// object->add_volume(mesh); -// model.add_default_instances(); -// -// // Align to origin. -// model.align_instances_to_origin(); -// -// // Create Print. -// Print print = Print(); -// -// // Configure the printObjectConfig. -// print.default_object_config.set_deserialize("support_material", "1"); -// print.default_object_config.set_deserialize("layer_height", "0.2"); -// print.config.set_deserialize("first_layer_height", "0.3"); -// -// vector contact_z; -// vector top_z; -// contact_z.push_back(1.9); -// top_z.push_back(1.9); -// -// // Add the modelObject. -// print.add_model_object(model.objects[0]); -// -// // Create new supports. -// SupportMaterial support = SupportMaterial(&print.config, &print.objects.front()->config); -// -// vector -// support_z = support.support_layers_z(contact_z, top_z, print.default_object_config.layer_height); -// +// To Be converted to catch. +class SupportMaterialTests +{ +public: + bool test_1() + { + // Create a mesh & modelObject. + TriangleMesh mesh = TriangleMesh::make_cube(20, 20, 20); + + // Create modelObject. + Model model = Model(); + ModelObject *object = model.add_object(); + object->add_volume(mesh); + model.add_default_instances(); + + // Align to origin. + model.align_instances_to_origin(); + + // Create Print. + Print print = Print(); + + // Configure the printObjectConfig. + print.default_object_config.set_deserialize("support_material", "1"); + print.default_object_config.set_deserialize("layer_height", "0.2"); + print.config.set_deserialize("first_layer_height", "0.3"); + + vector contact_z; + vector top_z; + contact_z.push_back(1.9); + top_z.push_back(1.9); + + // Add the modelObject. + print.add_model_object(model.objects[0]); + + // Create new supports. + SupportMaterial support = SupportMaterial(&print.config, &print.objects.front()->config); + + vector + support_z = support.support_layers_z(contact_z, top_z, print.default_object_config.layer_height); + // bool // is_1 = (support_z[0] == print.default_object_config.first_layer_height); // 'first layer height is honored'. // @@ -515,22 +204,22 @@ public: // wrong_top_spacing = 1; // } // bool is_4 = !wrong_top_spacing; // 'layers above top surfaces are spaced correctly' -// -// /* Test Also with this -// $config->set('first_layer_height', 0.4); -// $test->(); -// -// $config->set('layer_height', $config->nozzle_diameter->[0]); -// $test->(); -// */ -// } -// -// bool test_2() -// { -// -// } -// -//}; + + /* Test Also with this + $config->set('first_layer_height', 0.4); + $test->(); + + $config->set('layer_height', $config->nozzle_diameter->[0]); + $test->(); + */ + } + + bool test_2() + { + + } + +}; } From 4f77048fb5d275bc326cac5617dca925fd82cc30 Mon Sep 17 00:00:00 2001 From: Samir55 Date: Wed, 11 Jul 2018 08:00:30 +0200 Subject: [PATCH 11/30] Porting generate() function. --- lib/Slic3r/Print/SupportMaterial.pm | 6 +-- xs/src/libslic3r/SupportMaterial.cpp | 23 +++++++-- xs/src/libslic3r/SupportMaterial.hpp | 73 +++++++++++++++++++++++++--- 3 files changed, 88 insertions(+), 14 deletions(-) diff --git a/lib/Slic3r/Print/SupportMaterial.pm b/lib/Slic3r/Print/SupportMaterial.pm index 8cb66721a..05d5cf7b7 100644 --- a/lib/Slic3r/Print/SupportMaterial.pm +++ b/lib/Slic3r/Print/SupportMaterial.pm @@ -33,9 +33,9 @@ sub generate { # that it will be effective, regardless of how it's built below. my ($contact, $overhang) = $self->contact_area($object); - # Determine the top surfaces of the object. We need these to determine - # the layer heights of support material and to clip support to the object - # silhouette. + # // Determine the top surfaces of the object. We need these to determine + # // the layer heights of support material and to clip support to the object + # // silhouette. my ($top) = $self->object_top($object, $contact); # We now know the upper and lower boundaries for our support material object diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp index 4c4735b42..8e541b2a2 100644 --- a/xs/src/libslic3r/SupportMaterial.cpp +++ b/xs/src/libslic3r/SupportMaterial.cpp @@ -53,8 +53,25 @@ SupportMaterial::contact_distance(coordf_t layer_height, coordf_t nozzle_diamete } vector -SupportMaterial::support_layers_z(vector contact_z, - vector top_z, +SupportMaterial::get_keys_sorted(map _map) { + vector ret; + for (auto el : _map) + ret.push_back(el.first); + sort(ret.begin(), ret.end()); + return ret; +} + +coordf_t +SupportMaterial::get_max_layer_height(PrintObject* object) { + coordf_t ret = -1; + for (auto layer : object->layers) + ret = max(ret, layer->height); + return ret; +} + +vector +SupportMaterial::support_layers_z(vector contact_z, + vector top_z, coordf_t max_object_layer_height) { // Quick table to check whether a given Z is a top surface. @@ -629,7 +646,7 @@ SupportMaterial::contact_area(PrintObject *object) overhang[contact_z] = m_overhang; } } - + return make_pair(contact, overhang); } diff --git a/xs/src/libslic3r/SupportMaterial.hpp b/xs/src/libslic3r/SupportMaterial.hpp index 73f6ba513..6b9099e58 100644 --- a/xs/src/libslic3r/SupportMaterial.hpp +++ b/xs/src/libslic3r/SupportMaterial.hpp @@ -16,9 +16,6 @@ #include "ExPolygon.hpp" #include "SVG.hpp" -// Supports Material tests. - - using namespace std; namespace Slic3r @@ -53,8 +50,61 @@ public: object(nullptr) {} - void generate() - {} + void generate(PrintObject *object) + { + // Determine the top surfaces of the support, defined as: + // contact = overhangs - clearance + margin + // This method is responsible for identifying what contact surfaces + // should the support material expose to the object in order to guarantee + // that it will be effective, regardless of how it's built below. + pair, map> contact_overhang = contact_area(object); + map &contact = contact_overhang.first; + map &overhang = contact_overhang.second; + + + // Determine the top surfaces of the object. We need these to determine + // the layer heights of support material and to clip support to the object + // silhouette. + map top = object_top(object, &contact); + + // We now know the upper and lower boundaries for our support material object + // (@$contact_z and @$top_z), so we can generate intermediate layers. + vector support_z = support_layers_z(get_keys_sorted(contact), + get_keys_sorted(top), + get_max_layer_height(object)); + + // If we wanted to apply some special logic to the first support layers lying on + // object's top surfaces this is the place to detect them. TODO + + + // Propagate contact layers downwards to generate interface layers. TODO + + // Propagate contact layers and interface layers downwards to generate + // the main support layers. TODO + + + // Detect what part of base support layers are "reverse interfaces" because they + // lie above object's top surfaces. TODO + + + // Install support layers into object. + for (int i = 0; i < support_z.size(); i++) { + object->add_support_layer( + i, // id. + (i == 0) ? support_z[0] - 0 : (support_z[i] - support_z[i-1]), // height. + support_z[i] // print_z + ); + + if (i >= 1) { + object->support_layers.end()[-2]->upper_layer = object->support_layers.end()[-1]; + object->support_layers.end()[-1]->lower_layer = object->support_layers.end()[-2]; + } + } + + // Generate the actual toolpaths and save them into each layer. + generate_toolpaths(object, overhang, contact, interface, base); + + } void generate_interface_layers() {} @@ -89,7 +139,9 @@ public: /// This method returns the indices of the layers overlapping with the given one. vector overlapping_layers(int layer_idx, vector support_z); - vector support_layers_z(vector contact_z, vector top_z, coordf_t max_object_layer_height); + vector support_layers_z(vector contact_z, + vector top_z, + coordf_t max_object_layer_height); coordf_t contact_distance(coordf_t layer_height, coordf_t nozzle_diameter); @@ -99,6 +151,10 @@ public: void append_polygons(Polygons &dst, Polygons &src); + vector get_keys_sorted(map _map); + + coordf_t get_max_layer_height(PrintObject *object); + void process_layer(int layer_id) { SupportLayer *layer = this->object->support_layers[layer_id]; @@ -133,6 +189,7 @@ private: }; // To Be converted to catch. +// Supports Material tests. class SupportMaterialTests { public: @@ -158,8 +215,8 @@ public: print.default_object_config.set_deserialize("layer_height", "0.2"); print.config.set_deserialize("first_layer_height", "0.3"); - vector contact_z; - vector top_z; + vector contact_z; + vector top_z; contact_z.push_back(1.9); top_z.push_back(1.9); From da873635aa5aba0d115405008b8ec35903cb5c8f Mon Sep 17 00:00:00 2001 From: Samir55 Date: Wed, 11 Jul 2018 08:01:30 +0200 Subject: [PATCH 12/30] Some Refactoring. --- xs/src/libslic3r/SupportMaterial.cpp | 69 ++++++++++++++++++++++++++-- xs/src/libslic3r/SupportMaterial.hpp | 56 +--------------------- 2 files changed, 65 insertions(+), 60 deletions(-) diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp index 8e541b2a2..dc5ddc3d5 100644 --- a/xs/src/libslic3r/SupportMaterial.cpp +++ b/xs/src/libslic3r/SupportMaterial.cpp @@ -53,7 +53,8 @@ SupportMaterial::contact_distance(coordf_t layer_height, coordf_t nozzle_diamete } vector -SupportMaterial::get_keys_sorted(map _map) { +SupportMaterial::get_keys_sorted(map _map) +{ vector ret; for (auto el : _map) ret.push_back(el.first); @@ -62,16 +63,17 @@ SupportMaterial::get_keys_sorted(map _map) { } coordf_t -SupportMaterial::get_max_layer_height(PrintObject* object) { - coordf_t ret = -1; +SupportMaterial::get_max_layer_height(PrintObject *object) +{ + coordf_t ret = -1; for (auto layer : object->layers) ret = max(ret, layer->height); return ret; } vector -SupportMaterial::support_layers_z(vector contact_z, - vector top_z, +SupportMaterial::support_layers_z(vector contact_z, + vector top_z, coordf_t max_object_layer_height) { // Quick table to check whether a given Z is a top surface. @@ -650,4 +652,61 @@ SupportMaterial::contact_area(PrintObject *object) return make_pair(contact, overhang); } +void +SupportMaterial::generate(PrintObject *object) +{ + // Determine the top surfaces of the support, defined as: + // contact = overhangs - clearance + margin + // This method is responsible for identifying what contact surfaces + // should the support material expose to the object in order to guarantee + // that it will be effective, regardless of how it's built below. + pair, map> contact_overhang = contact_area(object); + map &contact = contact_overhang.first; + map &overhang = contact_overhang.second; + + + // Determine the top surfaces of the object. We need these to determine + // the layer heights of support material and to clip support to the object + // silhouette. + map top = object_top(object, &contact); + + // We now know the upper and lower boundaries for our support material object + // (@$contact_z and @$top_z), so we can generate intermediate layers. + vector support_z = support_layers_z(get_keys_sorted(contact), + get_keys_sorted(top), + get_max_layer_height(object)); + + // If we wanted to apply some special logic to the first support layers lying on + // object's top surfaces this is the place to detect them. TODO + + + // Propagate contact layers downwards to generate interface layers. TODO + + // Propagate contact layers and interface layers downwards to generate + // the main support layers. TODO + + + // Detect what part of base support layers are "reverse interfaces" because they + // lie above object's top surfaces. TODO + + + // Install support layers into object. + for (int i = 0; i < support_z.size(); i++) { + object->add_support_layer( + i, // id. + (i == 0) ? support_z[0] - 0 : (support_z[i] - support_z[i - 1]), // height. + support_z[i] // print_z + ); + + if (i >= 1) { + object->support_layers.end()[-2]->upper_layer = object->support_layers.end()[-1]; + object->support_layers.end()[-1]->lower_layer = object->support_layers.end()[-2]; + } + } + + // Generate the actual toolpaths and save them into each layer. + generate_toolpaths(object, overhang, contact, interface, base); + +} + } diff --git a/xs/src/libslic3r/SupportMaterial.hpp b/xs/src/libslic3r/SupportMaterial.hpp index 6b9099e58..a5fdea036 100644 --- a/xs/src/libslic3r/SupportMaterial.hpp +++ b/xs/src/libslic3r/SupportMaterial.hpp @@ -50,61 +50,7 @@ public: object(nullptr) {} - void generate(PrintObject *object) - { - // Determine the top surfaces of the support, defined as: - // contact = overhangs - clearance + margin - // This method is responsible for identifying what contact surfaces - // should the support material expose to the object in order to guarantee - // that it will be effective, regardless of how it's built below. - pair, map> contact_overhang = contact_area(object); - map &contact = contact_overhang.first; - map &overhang = contact_overhang.second; - - - // Determine the top surfaces of the object. We need these to determine - // the layer heights of support material and to clip support to the object - // silhouette. - map top = object_top(object, &contact); - - // We now know the upper and lower boundaries for our support material object - // (@$contact_z and @$top_z), so we can generate intermediate layers. - vector support_z = support_layers_z(get_keys_sorted(contact), - get_keys_sorted(top), - get_max_layer_height(object)); - - // If we wanted to apply some special logic to the first support layers lying on - // object's top surfaces this is the place to detect them. TODO - - - // Propagate contact layers downwards to generate interface layers. TODO - - // Propagate contact layers and interface layers downwards to generate - // the main support layers. TODO - - - // Detect what part of base support layers are "reverse interfaces" because they - // lie above object's top surfaces. TODO - - - // Install support layers into object. - for (int i = 0; i < support_z.size(); i++) { - object->add_support_layer( - i, // id. - (i == 0) ? support_z[0] - 0 : (support_z[i] - support_z[i-1]), // height. - support_z[i] // print_z - ); - - if (i >= 1) { - object->support_layers.end()[-2]->upper_layer = object->support_layers.end()[-1]; - object->support_layers.end()[-1]->lower_layer = object->support_layers.end()[-2]; - } - } - - // Generate the actual toolpaths and save them into each layer. - generate_toolpaths(object, overhang, contact, interface, base); - - } + void generate(PrintObject *object); void generate_interface_layers() {} From 1ed01913458e8815d5e7643b6f781f3dbc7b7f9a Mon Sep 17 00:00:00 2001 From: Samir55 Date: Wed, 11 Jul 2018 08:06:23 +0200 Subject: [PATCH 13/30] Reverting back SupportMaterial.pm --- lib/Slic3r/Print/SupportMaterial.pm | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/lib/Slic3r/Print/SupportMaterial.pm b/lib/Slic3r/Print/SupportMaterial.pm index 05d5cf7b7..cc297a415 100644 --- a/lib/Slic3r/Print/SupportMaterial.pm +++ b/lib/Slic3r/Print/SupportMaterial.pm @@ -2,7 +2,7 @@ # only generate() and contact_distance() are called from the outside of this module. package Slic3r::Print::SupportMaterial; use Moo; -use Data::Dumper; + use List::Util qw(sum min max); use Slic3r::ExtrusionPath ':roles'; use Slic3r::Flow ':roles'; @@ -33,9 +33,9 @@ sub generate { # that it will be effective, regardless of how it's built below. my ($contact, $overhang) = $self->contact_area($object); - # // Determine the top surfaces of the object. We need these to determine - # // the layer heights of support material and to clip support to the object - # // silhouette. + # Determine the top surfaces of the object. We need these to determine + # the layer heights of support material and to clip support to the object + # silhouette. my ($top) = $self->object_top($object, $contact); # We now know the upper and lower boundaries for our support material object @@ -60,7 +60,7 @@ sub generate { $self->clip_with_shape($interface, $shape) if @$shape; # Propagate contact layers and interface layers downwards to generate - # the main support layers. + # the main support layers. my ($base) = $self->generate_base_layers($support_z, $contact, $interface, $top); $self->clip_with_object($base, $support_z, $object); $self->clip_with_shape($base, $shape) if @$shape; @@ -108,7 +108,7 @@ sub contact_area { # determine contact areas my %contact = (); # contact_z => [ polygons ] - my %overhang = (); # contact_z => [ polygons ] - this stores the actual overhang supported by each contact layer + my %overhang = (); # contact_z => [ polygons ] - this stores the actual overhang supported by each contact layer for my $layer_id (0 .. $#{$object->layers}) { # note $layer_id might != $layer->id when raft_layers > 0 # so $layer_id == 0 means first object layer @@ -224,7 +224,7 @@ sub contact_area { # Get all perimeters as polylines. # TODO: split_at_first_point() (called by as_polyline() for ExtrusionLoops) - # could split a bridge mid-way + # could split a bridge mid-way my @overhang_perimeters = map $_->as_polyline, @{$layerm->perimeters->flatten}; # Only consider the overhang parts of such perimeters, @@ -374,7 +374,7 @@ sub object_top { # we considered) my $min_top = min(keys %top) // max(keys %$contact); # use <= instead of just < because otherwise we'd ignore any contact regions - # having the same Z of top layers + # having the same Z of top layers push @$projection, map @{$contact->{$_}}, grep { $_ > $layer->print_z && $_ <= $min_top } keys %$contact; # now find whether any projection falls onto this top surface @@ -391,6 +391,7 @@ sub object_top { $projection = diff($projection, $touching); } } + return \%top; } @@ -421,12 +422,11 @@ sub support_layers_z { # add raft layers by dividing the space between first layer and # first contact layer evenly if ($self->object_config->raft_layers > 1 && @z >= 2) { - print @z; # $z[1] is last raft layer (contact layer for the first layer object) my $height = ($z[1] - $z[0]) / ($self->object_config->raft_layers - 1); # since we already have two raft layers ($z[0] and $z[1]) we need to insert # raft_layers-2 more - splice @z, 1, 0, # + splice @z, 1, 0, map { sprintf "%.2f", $_ } map { $z[0] + $height * $_ } 1..($self->object_config->raft_layers - 2); @@ -511,7 +511,7 @@ sub generate_bottom_interface_layers { my $interface_layers = 0; # loop through support layers until we find the one(s) right above the top - # surface + # surface foreach my $layer_id (0 .. $#$support_z) { my $z = $support_z->[$layer_id]; next unless $z > $top_z; @@ -581,7 +581,7 @@ sub generate_base_layers { # This method removes object silhouette from support material # (it's used with interface and base only). It removes a bit more, -# leaving a thin gap between object and support in the XY plane. +# leaving a thin gap between object and support in the XY plane. sub clip_with_object { my ($self, $support, $support_z, $object) = @_; @@ -595,7 +595,7 @@ sub clip_with_object { # $layer->slices contains the full shape of layer, thus including # perimeter's width. $support contains the full shape of support - # material, thus including the width of its foremost extrusion. + # material, thus including the width of its foremost extrusion. # We leave a gap equal to a full extrusion width. $support->{$i} = diff( $support->{$i}, @@ -825,7 +825,7 @@ sub generate_toolpaths { $base_flow = $self->first_layer_flow; # use the proper spacing for first layer as we don't need to align - # its pattern to the other layers + # its pattern to the other layers $filler->set_min_spacing($base_flow->spacing); # subtract brim so that it goes around the object fully (and support gets its own brim) @@ -1018,3 +1018,4 @@ sub contact_distance { } 1; + From b84413fd0f9867c3d7621629b7cb95ffe1460108 Mon Sep 17 00:00:00 2001 From: Samir55 Date: Thu, 12 Jul 2018 01:14:24 +0200 Subject: [PATCH 14/30] Porting generate_interface_layers() function. --- xs/src/libslic3r/SupportMaterial.cpp | 10 ++--- xs/src/libslic3r/SupportMaterial.hpp | 55 ++++++++++++++++++++++++++-- 2 files changed, 57 insertions(+), 8 deletions(-) diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp index dc5ddc3d5..bb4d6091a 100644 --- a/xs/src/libslic3r/SupportMaterial.cpp +++ b/xs/src/libslic3r/SupportMaterial.cpp @@ -147,18 +147,18 @@ SupportMaterial::support_layers_z(vector contact_z, } vector -SupportMaterial::overlapping_layers(int layer_idx, vector support_z) +SupportMaterial::overlapping_layers(int layer_idx, vector support_z) { vector ret; - float z_max = support_z[layer_idx]; - float z_min = layer_idx == 0 ? 0 : support_z[layer_idx - 1]; + coordf_t z_max = support_z[layer_idx]; + coordf_t z_min = layer_idx == 0 ? 0 : support_z[layer_idx - 1]; for (int i = 0; i < support_z.size(); i++) { if (i == layer_idx) continue; - float z_max2 = support_z[i]; - float z_min2 = i == 0 ? 0 : support_z[i - 1]; + coordf_t z_max2 = support_z[i]; + coordf_t z_min2 = i == 0 ? 0 : support_z[i - 1]; if (z_max > z_min2 && z_min < z_max2) ret.push_back(i); diff --git a/xs/src/libslic3r/SupportMaterial.hpp b/xs/src/libslic3r/SupportMaterial.hpp index a5fdea036..e90b6a042 100644 --- a/xs/src/libslic3r/SupportMaterial.hpp +++ b/xs/src/libslic3r/SupportMaterial.hpp @@ -52,8 +52,57 @@ public: void generate(PrintObject *object); - void generate_interface_layers() - {} + map generate_interface_layers(vector support_z, map contact, map top) + { + // let's now generate interface layers below contact areas. + map interface; + auto interface_layers_num = object_config->support_material_interface_layers.value; + + for (int layer_id = 0; layer_id < support_z.size(); layer_id++) + { + // Todo Ask about how to port this line. "my $this = $contact->{$z} // next;" + auto z = support_z[layer_id]; + if (contact.count(z) <= 0) + continue; + Polygons &_contact = contact[z]; + + // Count contact layer as interface layer. + for (int i = layer_id - 1; i >= 0 && i > layer_id - interface_layers_num; i--) + { + auto _z = support_z[i]; + auto overlapping_layers = this->overlapping_layers(i, support_z); + vector overlapping_z; + for (auto z_el : overlapping_layers) + overlapping_z.push_back(support_z[z_el]); + + // Compute interface area on this layer as diff of upper contact area + // (or upper interface area) and layer slices. + // This diff is responsible of the contact between support material and + // the top surfaces of the object. We should probably offset the top + // surfaces vertically before performing the diff, but this needs + // investigation. + Polygons ps_1; + append_polygons(ps_1, _contact); // clipped projection of the current contact regions. + append_polygons(ps_1, interface[i]); // interface regions already applied to this layer. + + Polygons ps_2; + for (auto el : overlapping_z) + { + if (top.count(el) > 0) + append_polygons(ps_2, top[el]); // top slices on this layer. + if (contact.count(el)) + append_polygons(ps_2, contact[el]); // contact regions on this layer. + } + + _contact = interface[i] = diff( + ps_1, + ps_2, + true + ); + } + } + return interface; + } void generate_bottom_interface_layers() {} @@ -83,7 +132,7 @@ public: void clip_with_shape(map &support, map &shape); /// This method returns the indices of the layers overlapping with the given one. - vector overlapping_layers(int layer_idx, vector support_z); + vector overlapping_layers(int layer_idx, vector support_z); vector support_layers_z(vector contact_z, vector top_z, From 4c26cd221769c906ff0c2ed2345c727044240a07 Mon Sep 17 00:00:00 2001 From: Samir55 Date: Thu, 12 Jul 2018 01:20:27 +0200 Subject: [PATCH 15/30] Fix in compiling on macos. --- xs/src/libslic3r/SupportMaterial.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp index bb4d6091a..90e76fa0e 100644 --- a/xs/src/libslic3r/SupportMaterial.cpp +++ b/xs/src/libslic3r/SupportMaterial.cpp @@ -408,7 +408,7 @@ SupportMaterial::contact_area(PrintObject *object) else { Layer *lower_layer = object->get_layer(layer_id - 1); for (auto layerm : layer->regions) { - float fw = layerm->flow(frExternalPerimeter).scaled_width(); + auto fw = layerm->flow(frExternalPerimeter).scaled_width(); Polygons difference; // If a threshold angle was specified, use a different logic for detecting overhangs. @@ -519,12 +519,12 @@ SupportMaterial::contact_area(PrintObject *object) { // For bridges we can't assume width is larger than spacing because they // are positioned according to non-bridging perimeters spacing. - coordf_t widths[] = {bridge_flow.scaled_width(), + coord_t widths[] = {bridge_flow.scaled_width(), bridge_flow.scaled_spacing(), fw, layerm->flow(FlowRole::frPerimeter).scaled_width()}; - coordf_t w = *max_element(widths, widths + 4); + auto w = *max_element(widths, widths + 4); // Also apply safety offset to ensure no gaps are left in between. // TODO CHeck this. From abea497a789ae755f2bbaa879ac8786378a164d3 Mon Sep 17 00:00:00 2001 From: Samir55 Date: Thu, 12 Jul 2018 01:23:32 +0200 Subject: [PATCH 16/30] Refactoring in SupportMaterial class. --- xs/src/libslic3r/SupportMaterial.cpp | 54 ++++++++++++++++++++++++++++ xs/src/libslic3r/SupportMaterial.hpp | 52 +-------------------------- 2 files changed, 55 insertions(+), 51 deletions(-) diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp index 90e76fa0e..b5418cb3b 100644 --- a/xs/src/libslic3r/SupportMaterial.cpp +++ b/xs/src/libslic3r/SupportMaterial.cpp @@ -709,4 +709,58 @@ SupportMaterial::generate(PrintObject *object) } + +map +SupportMaterial::generate_interface_layers(vector support_z, map contact, map top) +{ + // let's now generate interface layers below contact areas. + map interface; + auto interface_layers_num = object_config->support_material_interface_layers.value; + + for (int layer_id = 0; layer_id < support_z.size(); layer_id++) + { + // Todo Ask about how to port this line. "my $this = $contact->{$z} // next;" + auto z = support_z[layer_id]; + if (contact.count(z) <= 0) + continue; + Polygons &_contact = contact[z]; + + // Count contact layer as interface layer. + for (int i = layer_id - 1; i >= 0 && i > layer_id - interface_layers_num; i--) + { + auto _z = support_z[i]; + auto overlapping_layers = this->overlapping_layers(i, support_z); + vector overlapping_z; + for (auto z_el : overlapping_layers) + overlapping_z.push_back(support_z[z_el]); + + // Compute interface area on this layer as diff of upper contact area + // (or upper interface area) and layer slices. + // This diff is responsible of the contact between support material and + // the top surfaces of the object. We should probably offset the top + // surfaces vertically before performing the diff, but this needs + // investigation. + Polygons ps_1; + append_polygons(ps_1, _contact); // clipped projection of the current contact regions. + append_polygons(ps_1, interface[i]); // interface regions already applied to this layer. + + Polygons ps_2; + for (auto el : overlapping_z) + { + if (top.count(el) > 0) + append_polygons(ps_2, top[el]); // top slices on this layer. + if (contact.count(el)) + append_polygons(ps_2, contact[el]); // contact regions on this layer. + } + + _contact = interface[i] = diff( + ps_1, + ps_2, + true + ); + } + } + return interface; +} + } diff --git a/xs/src/libslic3r/SupportMaterial.hpp b/xs/src/libslic3r/SupportMaterial.hpp index e90b6a042..417805ba3 100644 --- a/xs/src/libslic3r/SupportMaterial.hpp +++ b/xs/src/libslic3r/SupportMaterial.hpp @@ -52,57 +52,7 @@ public: void generate(PrintObject *object); - map generate_interface_layers(vector support_z, map contact, map top) - { - // let's now generate interface layers below contact areas. - map interface; - auto interface_layers_num = object_config->support_material_interface_layers.value; - - for (int layer_id = 0; layer_id < support_z.size(); layer_id++) - { - // Todo Ask about how to port this line. "my $this = $contact->{$z} // next;" - auto z = support_z[layer_id]; - if (contact.count(z) <= 0) - continue; - Polygons &_contact = contact[z]; - - // Count contact layer as interface layer. - for (int i = layer_id - 1; i >= 0 && i > layer_id - interface_layers_num; i--) - { - auto _z = support_z[i]; - auto overlapping_layers = this->overlapping_layers(i, support_z); - vector overlapping_z; - for (auto z_el : overlapping_layers) - overlapping_z.push_back(support_z[z_el]); - - // Compute interface area on this layer as diff of upper contact area - // (or upper interface area) and layer slices. - // This diff is responsible of the contact between support material and - // the top surfaces of the object. We should probably offset the top - // surfaces vertically before performing the diff, but this needs - // investigation. - Polygons ps_1; - append_polygons(ps_1, _contact); // clipped projection of the current contact regions. - append_polygons(ps_1, interface[i]); // interface regions already applied to this layer. - - Polygons ps_2; - for (auto el : overlapping_z) - { - if (top.count(el) > 0) - append_polygons(ps_2, top[el]); // top slices on this layer. - if (contact.count(el)) - append_polygons(ps_2, contact[el]); // contact regions on this layer. - } - - _contact = interface[i] = diff( - ps_1, - ps_2, - true - ); - } - } - return interface; - } + map generate_interface_layers(vector support_z, map contact, map top); void generate_bottom_interface_layers() {} From 93d81062afeedc15087d503a03cc37d3fce38ba6 Mon Sep 17 00:00:00 2001 From: Samir55 Date: Thu, 12 Jul 2018 02:14:22 +0200 Subject: [PATCH 17/30] Porting generate_base_layers() function. --- xs/src/libslic3r/SupportMaterial.cpp | 31 +++++++------- xs/src/libslic3r/SupportMaterial.hpp | 64 ++++++++++++++++++++++++---- 2 files changed, 71 insertions(+), 24 deletions(-) diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp index b5418cb3b..6414c444d 100644 --- a/xs/src/libslic3r/SupportMaterial.cpp +++ b/xs/src/libslic3r/SupportMaterial.cpp @@ -147,7 +147,7 @@ SupportMaterial::support_layers_z(vector contact_z, } vector -SupportMaterial::overlapping_layers(int layer_idx, vector support_z) +SupportMaterial::overlapping_layers(int layer_idx, const vector &support_z) { vector ret; @@ -520,9 +520,9 @@ SupportMaterial::contact_area(PrintObject *object) // For bridges we can't assume width is larger than spacing because they // are positioned according to non-bridging perimeters spacing. coord_t widths[] = {bridge_flow.scaled_width(), - bridge_flow.scaled_spacing(), - fw, - layerm->flow(FlowRole::frPerimeter).scaled_width()}; + bridge_flow.scaled_spacing(), + fw, + layerm->flow(FlowRole::frPerimeter).scaled_width()}; auto w = *max_element(widths, widths + 4); @@ -680,7 +680,10 @@ SupportMaterial::generate(PrintObject *object) // object's top surfaces this is the place to detect them. TODO - // Propagate contact layers downwards to generate interface layers. TODO + // Propagate contact layers downwards to generate interface layers. + map interface = generate_interface_layers(support_z, contact, top); + clip_with_object(interface, support_z, *object); +// clip_with_shape(base, shape); // TODO // Propagate contact layers and interface layers downwards to generate // the main support layers. TODO @@ -709,16 +712,16 @@ SupportMaterial::generate(PrintObject *object) } - -map -SupportMaterial::generate_interface_layers(vector support_z, map contact, map top) +map +SupportMaterial::generate_interface_layers(vector support_z, + map contact, + map top) { // let's now generate interface layers below contact areas. - map interface; + map interface; auto interface_layers_num = object_config->support_material_interface_layers.value; - for (int layer_id = 0; layer_id < support_z.size(); layer_id++) - { + for (int layer_id = 0; layer_id < support_z.size(); layer_id++) { // Todo Ask about how to port this line. "my $this = $contact->{$z} // next;" auto z = support_z[layer_id]; if (contact.count(z) <= 0) @@ -726,8 +729,7 @@ SupportMaterial::generate_interface_layers(vector support_z, map= 0 && i > layer_id - interface_layers_num; i--) - { + for (int i = layer_id - 1; i >= 0 && i > layer_id - interface_layers_num; i--) { auto _z = support_z[i]; auto overlapping_layers = this->overlapping_layers(i, support_z); vector overlapping_z; @@ -745,8 +747,7 @@ SupportMaterial::generate_interface_layers(vector support_z, map 0) append_polygons(ps_2, top[el]); // top slices on this layer. if (contact.count(el)) diff --git a/xs/src/libslic3r/SupportMaterial.hpp b/xs/src/libslic3r/SupportMaterial.hpp index 417805ba3..723e9569e 100644 --- a/xs/src/libslic3r/SupportMaterial.hpp +++ b/xs/src/libslic3r/SupportMaterial.hpp @@ -50,21 +50,67 @@ public: object(nullptr) {} - void generate(PrintObject *object); - - map generate_interface_layers(vector support_z, map contact, map top); - void generate_bottom_interface_layers() {} - void generate_base_layers() - {} + map generate_base_layers(vector support_z, + map contact, + map interface, + map top) + { + // Let's now generate support layers under interface layers. + map base; + { + for (int i = static_cast(support_z.size() - 1); i >= 0; i--) { + auto z = support_z[i]; + auto overlapping_layers = this->overlapping_layers(i, support_z); + vector overlapping_z; + for (auto el : overlapping_layers) + overlapping_z.push_back(support_z[el]); + + // In case we have no interface layers, look at upper contact + // (1 interface layer means we only have contact layer, so $interface->{$i+1} is empty). + Polygons upper_contact; + if (object_config->support_material_interface_layers.value <= 1) { + append_polygons(upper_contact, contact[support_z[i + 1]]); + } + + Polygons ps_1; + append_polygons(ps_1, base[i + 1]); // support regions on upper layer. + append_polygons(ps_1, interface[i + 1]); // interface regions on upper layer + append_polygons(ps_1, upper_contact); // contact regions on upper layer + + Polygons ps_2; + for (auto el : overlapping_z) { + if (top.count(el) > 0) + append_polygons(ps_2, top[el]); // top slices on this layer. + if (interface.count(el)) + append_polygons(ps_2, interface[el]); // interface regions on this layer. + if (contact.count(el)) + append_polygons(ps_2, contact[el]); // contact regions on this layer. + } + + base[i] = diff( + ps_1, + ps_2, + 1 + ); + } + } + return base; + } void generate_pillars_shape() {} + map generate_interface_layers(vector support_z, + map contact, + map top); + pair, map> contact_area(PrintObject *object); + void generate(PrintObject *object); + void generate_toolpaths(PrintObject *object, map overhang, map contact, @@ -82,7 +128,7 @@ public: void clip_with_shape(map &support, map &shape); /// This method returns the indices of the layers overlapping with the given one. - vector overlapping_layers(int layer_idx, vector support_z); + vector overlapping_layers(int layer_idx, const vector &support_z); vector support_layers_z(vector contact_z, vector top_z, @@ -160,8 +206,8 @@ public: print.default_object_config.set_deserialize("layer_height", "0.2"); print.config.set_deserialize("first_layer_height", "0.3"); - vector contact_z; - vector top_z; + vector contact_z; + vector top_z; contact_z.push_back(1.9); top_z.push_back(1.9); From d52dbf2bbd75eb6440943c68e096dfe8f66a56c3 Mon Sep 17 00:00:00 2001 From: Samir55 Date: Thu, 12 Jul 2018 02:16:11 +0200 Subject: [PATCH 18/30] Refactoring. --- xs/src/libslic3r/SupportMaterial.cpp | 48 ++++++++++++++++++++++++++ xs/src/libslic3r/SupportMaterial.hpp | 50 +++------------------------- 2 files changed, 52 insertions(+), 46 deletions(-) diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp index 6414c444d..8ee04840a 100644 --- a/xs/src/libslic3r/SupportMaterial.cpp +++ b/xs/src/libslic3r/SupportMaterial.cpp @@ -764,4 +764,52 @@ SupportMaterial::generate_interface_layers(vector support_z, return interface; } +map +SupportMaterial::generate_base_layers(vector support_z, + map contact, + map interface, + map top) +{ + // Let's now generate support layers under interface layers. + map base; + { + for (int i = static_cast(support_z.size() - 1); i >= 0; i--) { + auto z = support_z[i]; + auto overlapping_layers = this->overlapping_layers(i, support_z); + vector overlapping_z; + for (auto el : overlapping_layers) + overlapping_z.push_back(support_z[el]); + + // In case we have no interface layers, look at upper contact + // (1 interface layer means we only have contact layer, so $interface->{$i+1} is empty). + Polygons upper_contact; + if (object_config->support_material_interface_layers.value <= 1) { + append_polygons(upper_contact, contact[support_z[i + 1]]); + } + + Polygons ps_1; + append_polygons(ps_1, base[i + 1]); // support regions on upper layer. + append_polygons(ps_1, interface[i + 1]); // interface regions on upper layer + append_polygons(ps_1, upper_contact); // contact regions on upper layer + + Polygons ps_2; + for (auto el : overlapping_z) { + if (top.count(el) > 0) + append_polygons(ps_2, top[el]); // top slices on this layer. + if (interface.count(el)) + append_polygons(ps_2, interface[el]); // interface regions on this layer. + if (contact.count(el)) + append_polygons(ps_2, contact[el]); // contact regions on this layer. + } + + base[i] = diff( + ps_1, + ps_2, + 1 + ); + } + } + return base; +} + } diff --git a/xs/src/libslic3r/SupportMaterial.hpp b/xs/src/libslic3r/SupportMaterial.hpp index 723e9569e..7012e77b8 100644 --- a/xs/src/libslic3r/SupportMaterial.hpp +++ b/xs/src/libslic3r/SupportMaterial.hpp @@ -53,55 +53,13 @@ public: void generate_bottom_interface_layers() {} + void generate_pillars_shape() + {} + map generate_base_layers(vector support_z, map contact, map interface, - map top) - { - // Let's now generate support layers under interface layers. - map base; - { - for (int i = static_cast(support_z.size() - 1); i >= 0; i--) { - auto z = support_z[i]; - auto overlapping_layers = this->overlapping_layers(i, support_z); - vector overlapping_z; - for (auto el : overlapping_layers) - overlapping_z.push_back(support_z[el]); - - // In case we have no interface layers, look at upper contact - // (1 interface layer means we only have contact layer, so $interface->{$i+1} is empty). - Polygons upper_contact; - if (object_config->support_material_interface_layers.value <= 1) { - append_polygons(upper_contact, contact[support_z[i + 1]]); - } - - Polygons ps_1; - append_polygons(ps_1, base[i + 1]); // support regions on upper layer. - append_polygons(ps_1, interface[i + 1]); // interface regions on upper layer - append_polygons(ps_1, upper_contact); // contact regions on upper layer - - Polygons ps_2; - for (auto el : overlapping_z) { - if (top.count(el) > 0) - append_polygons(ps_2, top[el]); // top slices on this layer. - if (interface.count(el)) - append_polygons(ps_2, interface[el]); // interface regions on this layer. - if (contact.count(el)) - append_polygons(ps_2, contact[el]); // contact regions on this layer. - } - - base[i] = diff( - ps_1, - ps_2, - 1 - ); - } - } - return base; - } - - void generate_pillars_shape() - {} + map top); map generate_interface_layers(vector support_z, map contact, From 054a52b559a8c214b2a0aae38451c3083dbef9b2 Mon Sep 17 00:00:00 2001 From: Samir55 Date: Thu, 12 Jul 2018 02:41:42 +0200 Subject: [PATCH 19/30] Porting generate_bottom_interface() function. --- xs/src/libslic3r/SupportMaterial.cpp | 6 +-- xs/src/libslic3r/SupportMaterial.hpp | 56 +++++++++++++++++++++++++++- 2 files changed, 57 insertions(+), 5 deletions(-) diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp index 8ee04840a..a08f2be2f 100644 --- a/xs/src/libslic3r/SupportMaterial.cpp +++ b/xs/src/libslic3r/SupportMaterial.cpp @@ -750,7 +750,7 @@ SupportMaterial::generate_interface_layers(vector support_z, for (auto el : overlapping_z) { if (top.count(el) > 0) append_polygons(ps_2, top[el]); // top slices on this layer. - if (contact.count(el)) + if (contact.count(el) > 0) append_polygons(ps_2, contact[el]); // contact regions on this layer. } @@ -796,9 +796,9 @@ SupportMaterial::generate_base_layers(vector support_z, for (auto el : overlapping_z) { if (top.count(el) > 0) append_polygons(ps_2, top[el]); // top slices on this layer. - if (interface.count(el)) + if (interface.count(el) > 0) append_polygons(ps_2, interface[el]); // interface regions on this layer. - if (contact.count(el)) + if (contact.count(el) > 0) append_polygons(ps_2, contact[el]); // contact regions on this layer. } diff --git a/xs/src/libslic3r/SupportMaterial.hpp b/xs/src/libslic3r/SupportMaterial.hpp index 7012e77b8..f33fa654e 100644 --- a/xs/src/libslic3r/SupportMaterial.hpp +++ b/xs/src/libslic3r/SupportMaterial.hpp @@ -50,8 +50,60 @@ public: object(nullptr) {} - void generate_bottom_interface_layers() - {} + void generate_bottom_interface_layers(const vector &support_z, + map &base, + map &top, + map &interface) + { + // If no interface layers are allowed, don't generate bottom interface layers. + if (object_config->support_material_interface_layers.value == 0) + return; + + auto area_threshold = interface_flow->scaled_spacing() * interface_flow->scaled_spacing(); + + // Loop through object's top surfaces. TODO CHeck if the keys are sorted. + for (auto &top_el : top) { + // Keep a count of the interface layers we generated for this top surface. + int interface_layers = 0; + + // Loop through support layers until we find the one(s) right above the top + // surface. + for (int layer_id = 0; layer_id < support_z.size(); layer_id++) { + auto z = support_z[layer_id]; + if (!z > top_el.first) // next unless $z > $top_z; + continue; + + if (base.count(layer_id) > 0) { + // Get the support material area that should be considered interface. + auto interface_area = intersection( + base[layer_id], + top_el.second + ); + + // Discard too small areas. + Polygons new_interface_area; + for (auto p : interface_area) { + if (abs(p.area()) >= area_threshold) + new_interface_area.push_back(p); + } + interface_area = new_interface_area; + + // Subtract new interface area from base. + base[layer_id] = diff( + base[layer_id], + interface_area + ); + + // Add the new interface area to interface. + append_polygons(interface[layer_id], interface_area); + } + + interface_layers++; + if (interface_layers == object_config->support_material_interface_layers.value) + layer_id = support_z.size(); + } + } + } void generate_pillars_shape() {} From 873558fec69ee412ce7085a2210b9128f39a81dd Mon Sep 17 00:00:00 2001 From: Samir55 Date: Thu, 12 Jul 2018 02:48:13 +0200 Subject: [PATCH 20/30] Refactoring. --- xs/src/libslic3r/SupportMaterial.cpp | 574 +++++++++++++++------------ xs/src/libslic3r/SupportMaterial.hpp | 58 +-- 2 files changed, 319 insertions(+), 313 deletions(-) diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp index a08f2be2f..c859f5eb1 100644 --- a/xs/src/libslic3r/SupportMaterial.cpp +++ b/xs/src/libslic3r/SupportMaterial.cpp @@ -13,7 +13,7 @@ SupportMaterial::create_circle(coordf_t radius) PI / 3, 0}; for (auto pos : positions) { - points.push_back(Point(radius * cos(pos), (radius * sin(pos)))); + points.emplace_back(radius * cos(pos), (radius * sin(pos))); } return Polygon(points); @@ -71,6 +71,110 @@ SupportMaterial::get_max_layer_height(PrintObject *object) return ret; } +void +SupportMaterial::generate_toolpaths(PrintObject *object, + map overhang, + map contact, + map interface, + map base) +{ + // Assig the object to the supports class. + this->object = object; + + // Shape of contact area. + int contact_loops = 1; + coordf_t circle_radius = 1.5 * interface_flow->scaled_width(); + coordf_t circle_distance = 3 * circle_radius; + Polygon circle = create_circle(circle_radius); + + // TODO Add Slic3r debug. "Generating patterns\n" + // Prepare fillers. + auto pattern = object_config->support_material_pattern; + vector angles; + angles.push_back(object_config->support_material_angle.value); + + if (pattern == smpRectilinearGrid) { + pattern = smpRectilinear; + angles.push_back(angles[0] + 90); + } + else if (pattern == smpPillars) { + pattern = smpHoneycomb; + } + + auto interface_angle = object_config->support_material_angle.value + 90; + auto interface_spacing = object_config->support_material_interface_spacing.value + interface_flow->spacing(); + auto interface_density = interface_spacing == 0 ? 1 : interface_flow->spacing() / interface_spacing; + auto support_spacing = object_config->support_material_spacing + flow->spacing(); + auto support_density = support_spacing == 0 ? 1 : flow->spacing() / support_spacing; + + parallelize( + 0, + object->support_layers.size() - 1, + boost::bind(&SupportMaterial::process_layer, this, _1), + this->config->threads.value + ); +} + +void +SupportMaterial::generate(PrintObject *object) +{ + // Determine the top surfaces of the support, defined as: + // contact = overhangs - clearance + margin + // This method is responsible for identifying what contact surfaces + // should the support material expose to the object in order to guarantee + // that it will be effective, regardless of how it's built below. + pair, map> contact_overhang = contact_area(object); + map &contact = contact_overhang.first; + map &overhang = contact_overhang.second; + + + // Determine the top surfaces of the object. We need these to determine + // the layer heights of support material and to clip support to the object + // silhouette. + map top = object_top(object, &contact); + + // We now know the upper and lower boundaries for our support material object + // (@$contact_z and @$top_z), so we can generate intermediate layers. + vector support_z = support_layers_z(get_keys_sorted(contact), + get_keys_sorted(top), + get_max_layer_height(object)); + + // If we wanted to apply some special logic to the first support layers lying on + // object's top surfaces this is the place to detect them. TODO + + + // Propagate contact layers downwards to generate interface layers. + map interface = generate_interface_layers(support_z, contact, top); + clip_with_object(interface, support_z, *object); +// clip_with_shape(base, shape); // TODO + + // Propagate contact layers and interface layers downwards to generate + // the main support layers. TODO + + + // Detect what part of base support layers are "reverse interfaces" because they + // lie above object's top surfaces. TODO + + + // Install support layers into object. + for (int i = 0; i < support_z.size(); i++) { + object->add_support_layer( + i, // id. + (i == 0) ? support_z[0] - 0 : (support_z[i] - support_z[i - 1]), // height. + support_z[i] // print_z + ); + + if (i >= 1) { + object->support_layers.end()[-2]->upper_layer = object->support_layers.end()[-1]; + object->support_layers.end()[-1]->lower_layer = object->support_layers.end()[-2]; + } + } + + // Generate the actual toolpaths and save them into each layer. + generate_toolpaths(object, overhang, contact, interface, base); + +} + vector SupportMaterial::support_layers_z(vector contact_z, vector top_z, @@ -146,178 +250,6 @@ SupportMaterial::support_layers_z(vector contact_z, return z; } -vector -SupportMaterial::overlapping_layers(int layer_idx, const vector &support_z) -{ - vector ret; - - coordf_t z_max = support_z[layer_idx]; - coordf_t z_min = layer_idx == 0 ? 0 : support_z[layer_idx - 1]; - - for (int i = 0; i < support_z.size(); i++) { - if (i == layer_idx) continue; - - coordf_t z_max2 = support_z[i]; - coordf_t z_min2 = i == 0 ? 0 : support_z[i - 1]; - - if (z_max > z_min2 && z_min < z_max2) - ret.push_back(i); - } - - return ret; -} - -void -SupportMaterial::clip_with_shape(map &support, map &shape) -{ - for (auto layer : support) { - // Don't clip bottom layer with shape so that we - // can generate a continuous base flange - // also don't clip raft layers - if (layer.first == 0) continue; - else if (layer.first < object_config->raft_layers) continue; - - layer.second = intersection(layer.second, shape[layer.first]); - } -} - -void -SupportMaterial::clip_with_object(map &support, vector support_z, PrintObject &object) -{ - int i = 0; - for (auto support_layer: support) { - if (support_layer.second.empty()) { - i++; - continue; - } - coordf_t z_max = support_z[i]; - coordf_t z_min = (i == 0) ? 0 : support_z[i - 1]; - - LayerPtrs layers; - for (auto layer : object.layers) { - if (layer->print_z > z_min && (layer->print_z - layer->height) < z_max) { - layers.push_back(layer); - } - } - - // $layer->slices contains the full shape of layer, thus including - // perimeter's width. $support contains the full shape of support - // material, thus including the width of its foremost extrusion. - // We leave a gap equal to a full extrusion width. TODO ask about this line @samir - Polygons slices; - for (Layer *l : layers) { - for (auto s : l->slices.contours()) { - slices.push_back(s); - } - } - support_layer.second = diff(support_layer.second, offset(slices, flow->scaled_width())); - } - /* - $support->{$i} = diff( - $support->{$i}, - offset([ map @$_, map @{$_->slices}, @layers ], +$self->flow->scaled_width), - ); - */ -} - -map -SupportMaterial::object_top(PrintObject *object, map *contact) -{ - // find object top surfaces - // we'll use them to clip our support and detect where does it stick. - map top; - if (object_config->support_material_buildplate_only.value) - return top; - - Polygons projection; - for (auto i = static_cast(object->layers.size() - 1); i >= 0; i--) { - - Layer *layer = object->layers[i]; - SurfacesPtr m_top; - - for (auto r : layer->regions) - for (auto s : r->slices.filter_by_type(stTop)) - m_top.push_back(s); - - if (!m_top.empty()) { - // compute projection of the contact areas above this top layer - // first add all the 'new' contact areas to the current projection - // ('new' means all the areas that are lower than the last top layer - // we considered). - // TODO Ask about this line - /* - my $min_top = min(keys %top) // max(keys %$contact); - */ - double min_top = top.begin()->first; - - // Use <= instead of just < because otherwise we'd ignore any contact regions - // having the same Z of top layers. - for (auto el : *contact) - if (el.first > layer->print_z && el.first <= min_top) - for (const auto &p : el.second) - projection.push_back(p); - - // Now find whether any projection falls onto this top surface. - Polygons touching = intersection(projection, p(m_top)); - if (!touching.empty()) { - // Grow top surfaces so that interface and support generation are generated - // with some spacing from object - it looks we don't need the actual - // top shapes so this can be done here. - top[layer->print_z] = offset(touching, flow->scaled_width()); - } - - // Remove the areas that touched from the projection that will continue on - // next, lower, top surfaces. - projection = diff(projection, touching); - } - } - return top; -} - -void -SupportMaterial::generate_toolpaths(PrintObject *object, - map overhang, - map contact, - map interface, - map base) -{ - // Assig the object to the supports class. - this->object = object; - - // Shape of contact area. - int contact_loops = 1; - coordf_t circle_radius = 1.5 * interface_flow->scaled_width(); - coordf_t circle_distance = 3 * circle_radius; - Polygon circle = create_circle(circle_radius); - - // TODO Add Slic3r debug. "Generating patterns\n" - // Prepare fillers. - auto pattern = object_config->support_material_pattern; - vector angles; - angles.push_back(object_config->support_material_angle.value); - - if (pattern == smpRectilinearGrid) { - pattern = smpRectilinear; - angles.push_back(angles[0] + 90); - } - else if (pattern == smpPillars) { - pattern = smpHoneycomb; - } - - auto interface_angle = object_config->support_material_angle.value + 90; - auto interface_spacing = object_config->support_material_interface_spacing.value + interface_flow->spacing(); - auto interface_density = interface_spacing == 0 ? 1 : interface_flow->spacing() / interface_spacing; - auto support_spacing = object_config->support_material_spacing + flow->spacing(); - auto support_density = support_spacing == 0 ? 1 : flow->spacing() / support_spacing; - - parallelize( - 0, - object->support_layers.size() - 1, - boost::bind(&SupportMaterial::process_layer, this, _1), - this->config->threads.value - ); -} - pair, map> SupportMaterial::contact_area(PrintObject *object) { @@ -652,64 +584,106 @@ SupportMaterial::contact_area(PrintObject *object) return make_pair(contact, overhang); } -void -SupportMaterial::generate(PrintObject *object) +map +SupportMaterial::object_top(PrintObject *object, map *contact) { - // Determine the top surfaces of the support, defined as: - // contact = overhangs - clearance + margin - // This method is responsible for identifying what contact surfaces - // should the support material expose to the object in order to guarantee - // that it will be effective, regardless of how it's built below. - pair, map> contact_overhang = contact_area(object); - map &contact = contact_overhang.first; - map &overhang = contact_overhang.second; + // find object top surfaces + // we'll use them to clip our support and detect where does it stick. + map top; + if (object_config->support_material_buildplate_only.value) + return top; + Polygons projection; + for (auto i = static_cast(object->layers.size() - 1); i >= 0; i--) { - // Determine the top surfaces of the object. We need these to determine - // the layer heights of support material and to clip support to the object - // silhouette. - map top = object_top(object, &contact); + Layer *layer = object->layers[i]; + SurfacesPtr m_top; - // We now know the upper and lower boundaries for our support material object - // (@$contact_z and @$top_z), so we can generate intermediate layers. - vector support_z = support_layers_z(get_keys_sorted(contact), - get_keys_sorted(top), - get_max_layer_height(object)); + for (auto r : layer->regions) + for (auto s : r->slices.filter_by_type(stTop)) + m_top.push_back(s); - // If we wanted to apply some special logic to the first support layers lying on - // object's top surfaces this is the place to detect them. TODO + if (!m_top.empty()) { + // compute projection of the contact areas above this top layer + // first add all the 'new' contact areas to the current projection + // ('new' means all the areas that are lower than the last top layer + // we considered). + // TODO Ask about this line + /* + my $min_top = min(keys %top) // max(keys %$contact); + */ + double min_top = top.begin()->first; + // Use <= instead of just < because otherwise we'd ignore any contact regions + // having the same Z of top layers. + for (auto el : *contact) + if (el.first > layer->print_z && el.first <= min_top) + for (const auto &p : el.second) + projection.push_back(p); - // Propagate contact layers downwards to generate interface layers. - map interface = generate_interface_layers(support_z, contact, top); - clip_with_object(interface, support_z, *object); -// clip_with_shape(base, shape); // TODO + // Now find whether any projection falls onto this top surface. + Polygons touching = intersection(projection, p(m_top)); + if (!touching.empty()) { + // Grow top surfaces so that interface and support generation are generated + // with some spacing from object - it looks we don't need the actual + // top shapes so this can be done here. + top[layer->print_z] = offset(touching, flow->scaled_width()); + } - // Propagate contact layers and interface layers downwards to generate - // the main support layers. TODO - - - // Detect what part of base support layers are "reverse interfaces" because they - // lie above object's top surfaces. TODO - - - // Install support layers into object. - for (int i = 0; i < support_z.size(); i++) { - object->add_support_layer( - i, // id. - (i == 0) ? support_z[0] - 0 : (support_z[i] - support_z[i - 1]), // height. - support_z[i] // print_z - ); - - if (i >= 1) { - object->support_layers.end()[-2]->upper_layer = object->support_layers.end()[-1]; - object->support_layers.end()[-1]->lower_layer = object->support_layers.end()[-2]; + // Remove the areas that touched from the projection that will continue on + // next, lower, top surfaces. + projection = diff(projection, touching); } } + return top; +} - // Generate the actual toolpaths and save them into each layer. - generate_toolpaths(object, overhang, contact, interface, base); +map +SupportMaterial::generate_base_layers(vector support_z, + map contact, + map interface, + map top) +{ + // Let's now generate support layers under interface layers. + map base; + { + for (int i = static_cast(support_z.size() - 1); i >= 0; i--) { + auto z = support_z[i]; + auto overlapping_layers = this->overlapping_layers(i, support_z); + vector overlapping_z; + for (auto el : overlapping_layers) + overlapping_z.push_back(support_z[el]); + // In case we have no interface layers, look at upper contact + // (1 interface layer means we only have contact layer, so $interface->{$i+1} is empty). + Polygons upper_contact; + if (object_config->support_material_interface_layers.value <= 1) { + append_polygons(upper_contact, contact[support_z[i + 1]]); + } + + Polygons ps_1; + append_polygons(ps_1, base[i + 1]); // support regions on upper layer. + append_polygons(ps_1, interface[i + 1]); // interface regions on upper layer + append_polygons(ps_1, upper_contact); // contact regions on upper layer + + Polygons ps_2; + for (auto el : overlapping_z) { + if (top.count(el) > 0) + append_polygons(ps_2, top[el]); // top slices on this layer. + if (interface.count(el) > 0) + append_polygons(ps_2, interface[el]); // interface regions on this layer. + if (contact.count(el) > 0) + append_polygons(ps_2, contact[el]); // contact regions on this layer. + } + + base[i] = diff( + ps_1, + ps_2, + 1 + ); + } + } + return base; } map @@ -764,52 +738,134 @@ SupportMaterial::generate_interface_layers(vector support_z, return interface; } -map -SupportMaterial::generate_base_layers(vector support_z, - map contact, - map interface, - map top) +void +SupportMaterial::generate_bottom_interface_layers(const vector &support_z, + map &base, + map &top, + map &interface) { - // Let's now generate support layers under interface layers. - map base; - { - for (int i = static_cast(support_z.size() - 1); i >= 0; i--) { - auto z = support_z[i]; - auto overlapping_layers = this->overlapping_layers(i, support_z); - vector overlapping_z; - for (auto el : overlapping_layers) - overlapping_z.push_back(support_z[el]); + // If no interface layers are allowed, don't generate bottom interface layers. + if (object_config->support_material_interface_layers.value == 0) + return; - // In case we have no interface layers, look at upper contact - // (1 interface layer means we only have contact layer, so $interface->{$i+1} is empty). - Polygons upper_contact; - if (object_config->support_material_interface_layers.value <= 1) { - append_polygons(upper_contact, contact[support_z[i + 1]]); + auto area_threshold = interface_flow->scaled_spacing() * interface_flow->scaled_spacing(); + + // Loop through object's top surfaces. TODO CHeck if the keys are sorted. + for (auto &top_el : top) { + // Keep a count of the interface layers we generated for this top surface. + int interface_layers = 0; + + // Loop through support layers until we find the one(s) right above the top + // surface. + for (int layer_id = 0; layer_id < support_z.size(); layer_id++) { + auto z = support_z[layer_id]; + if (!z > top_el.first) // next unless $z > $top_z; + continue; + + if (base.count(layer_id) > 0) { + // Get the support material area that should be considered interface. + auto interface_area = intersection( + base[layer_id], + top_el.second + ); + + // Discard too small areas. + Polygons new_interface_area; + for (auto p : interface_area) { + if (abs(p.area()) >= area_threshold) + new_interface_area.push_back(p); + } + interface_area = new_interface_area; + + // Subtract new interface area from base. + base[layer_id] = diff( + base[layer_id], + interface_area + ); + + // Add the new interface area to interface. + append_polygons(interface[layer_id], interface_area); } - Polygons ps_1; - append_polygons(ps_1, base[i + 1]); // support regions on upper layer. - append_polygons(ps_1, interface[i + 1]); // interface regions on upper layer - append_polygons(ps_1, upper_contact); // contact regions on upper layer - - Polygons ps_2; - for (auto el : overlapping_z) { - if (top.count(el) > 0) - append_polygons(ps_2, top[el]); // top slices on this layer. - if (interface.count(el) > 0) - append_polygons(ps_2, interface[el]); // interface regions on this layer. - if (contact.count(el) > 0) - append_polygons(ps_2, contact[el]); // contact regions on this layer. - } - - base[i] = diff( - ps_1, - ps_2, - 1 - ); + interface_layers++; + if (interface_layers == object_config->support_material_interface_layers.value) + layer_id = static_cast(support_z.size()); } } - return base; +} + +vector +SupportMaterial::overlapping_layers(int layer_idx, const vector &support_z) +{ + vector ret; + + coordf_t z_max = support_z[layer_idx]; + coordf_t z_min = layer_idx == 0 ? 0 : support_z[layer_idx - 1]; + + for (int i = 0; i < support_z.size(); i++) { + if (i == layer_idx) continue; + + coordf_t z_max2 = support_z[i]; + coordf_t z_min2 = i == 0 ? 0 : support_z[i - 1]; + + if (z_max > z_min2 && z_min < z_max2) + ret.push_back(i); + } + + return ret; +} + +void +SupportMaterial::clip_with_shape(map &support, map &shape) +{ + for (auto layer : support) { + // Don't clip bottom layer with shape so that we + // can generate a continuous base flange + // also don't clip raft layers + if (layer.first == 0) continue; + else if (layer.first < object_config->raft_layers) continue; + + layer.second = intersection(layer.second, shape[layer.first]); + } +} + +void +SupportMaterial::clip_with_object(map &support, vector support_z, PrintObject &object) +{ + int i = 0; + for (auto support_layer: support) { + if (support_layer.second.empty()) { + i++; + continue; + } + coordf_t z_max = support_z[i]; + coordf_t z_min = (i == 0) ? 0 : support_z[i - 1]; + + LayerPtrs layers; + for (auto layer : object.layers) { + if (layer->print_z > z_min && (layer->print_z - layer->height) < z_max) { + layers.push_back(layer); + } + } + + // $layer->slices contains the full shape of layer, thus including + // perimeter's width. $support contains the full shape of support + // material, thus including the width of its foremost extrusion. + // We leave a gap equal to a full extrusion width. TODO ask about this line @samir + Polygons slices; + for (Layer *l : layers) { + for (auto s : l->slices.contours()) { + slices.push_back(s); + } + } + support_layer.second = diff(support_layer.second, offset(slices, flow->scaled_width())); + } + /* + $support->{$i} = diff( + $support->{$i}, + offset([ map @$_, map @{$_->slices}, @layers ], +$self->flow->scaled_width), + ); + */ } } diff --git a/xs/src/libslic3r/SupportMaterial.hpp b/xs/src/libslic3r/SupportMaterial.hpp index f33fa654e..8341ca9db 100644 --- a/xs/src/libslic3r/SupportMaterial.hpp +++ b/xs/src/libslic3r/SupportMaterial.hpp @@ -50,63 +50,13 @@ public: object(nullptr) {} + void generate_pillars_shape(vector contact, vector support_z) + {} + void generate_bottom_interface_layers(const vector &support_z, map &base, map &top, - map &interface) - { - // If no interface layers are allowed, don't generate bottom interface layers. - if (object_config->support_material_interface_layers.value == 0) - return; - - auto area_threshold = interface_flow->scaled_spacing() * interface_flow->scaled_spacing(); - - // Loop through object's top surfaces. TODO CHeck if the keys are sorted. - for (auto &top_el : top) { - // Keep a count of the interface layers we generated for this top surface. - int interface_layers = 0; - - // Loop through support layers until we find the one(s) right above the top - // surface. - for (int layer_id = 0; layer_id < support_z.size(); layer_id++) { - auto z = support_z[layer_id]; - if (!z > top_el.first) // next unless $z > $top_z; - continue; - - if (base.count(layer_id) > 0) { - // Get the support material area that should be considered interface. - auto interface_area = intersection( - base[layer_id], - top_el.second - ); - - // Discard too small areas. - Polygons new_interface_area; - for (auto p : interface_area) { - if (abs(p.area()) >= area_threshold) - new_interface_area.push_back(p); - } - interface_area = new_interface_area; - - // Subtract new interface area from base. - base[layer_id] = diff( - base[layer_id], - interface_area - ); - - // Add the new interface area to interface. - append_polygons(interface[layer_id], interface_area); - } - - interface_layers++; - if (interface_layers == object_config->support_material_interface_layers.value) - layer_id = support_z.size(); - } - } - } - - void generate_pillars_shape() - {} + map &interface); map generate_base_layers(vector support_z, map contact, From b555fff86f2d881fdbecd4c6aad275badc2149cc Mon Sep 17 00:00:00 2001 From: Samir55 Date: Thu, 12 Jul 2018 04:11:39 +0200 Subject: [PATCH 21/30] Porting generate_support_pillars() function. --- xs/src/libslic3r/SupportMaterial.hpp | 87 +++++++++++++++++++++++++++- 1 file changed, 85 insertions(+), 2 deletions(-) diff --git a/xs/src/libslic3r/SupportMaterial.hpp b/xs/src/libslic3r/SupportMaterial.hpp index 8341ca9db..9d855bedd 100644 --- a/xs/src/libslic3r/SupportMaterial.hpp +++ b/xs/src/libslic3r/SupportMaterial.hpp @@ -50,8 +50,91 @@ public: object(nullptr) {} - void generate_pillars_shape(vector contact, vector support_z) - {} + void generate_pillars_shape(const map &contact, + const vector &support_z, + map &shape) + { + // This prevents supplying an empty point set to BoundingBox constructor. + if (contact.empty()) return; + + coord_t pillar_size = scale_(object_config->support_material_pillar_size.value); + coord_t pillar_spacing = scale_(object_config->support_material_pillar_spacing.value); + + Polygons grid; + { + auto pillar = Polygon({ + Point(0, 0), + Point(pillar_size, coord_t(0)), + Point(pillar_size, pillar_size), + Point(coord_t(0), pillar_size) + }); + + Polygons pillars; + BoundingBox bb; + { + Points bb_points; + for (auto contact_el : contact) { + append_to(bb_points, to_points(contact_el.second)); + } + bb = BoundingBox(bb_points); + } + + for (auto x = bb.min.x; x <= bb.max.x - pillar_size; x += pillar_spacing) { + for (auto y = bb.min.y; y <= bb.max.y - pillar_size; y += pillar_spacing) { + pillars.push_back(pillar); + pillar.translate(x, y); + } + } + + grid = union_(pillars); + } + + // Add pillars to every layer. + for (auto i = 0; i < support_z.size(); i++) { + shape[i] = grid; + } + + // Build capitals. + for (auto i = 0; i < support_z.size(); i++) { + coordf_t z = support_z[i]; + + auto capitals = intersection( + grid, + contact.at(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 + // sure nothing is left. + Polygons contact_supported_by_capitals; + for (auto capital : capitals) { + // Enlarge capital tops. + auto capital_polygons = offset(Polygons({capital}), +(pillar_spacing - pillar_size) / 2); + append_to(contact_supported_by_capitals, capital_polygons); + + for (int j = i - 1; j >= 0; j--) { + auto jz = support_z[j]; + capital_polygons = offset(Polygons{capital}, -interface_flow->scaled_width() / 2); + if (capitals.empty()) break; + append_to(shape[i], capital_polygons); + } + } + + // 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 + // sure nothing is left. + auto contact_not_supported_by_capitals = diff( + contact.at(z), + contact_supported_by_capitals + ); + + if (!contact_not_supported_by_capitals.empty()) { + for (int j = i - 1; j >= 0; j--) { + append_to(shape[j], contact_not_supported_by_capitals); + } + } + } + } void generate_bottom_interface_layers(const vector &support_z, map &base, From 4d1e4eb252c59d4d454e9c66b2478c0651483380 Mon Sep 17 00:00:00 2001 From: Samir55 Date: Thu, 12 Jul 2018 04:18:47 +0200 Subject: [PATCH 22/30] Refactoring. --- xs/src/libslic3r/SupportMaterial.cpp | 225 +++++++++++++++++++-------- xs/src/libslic3r/SupportMaterial.hpp | 196 +++++++---------------- 2 files changed, 213 insertions(+), 208 deletions(-) diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp index c859f5eb1..b32eaea12 100644 --- a/xs/src/libslic3r/SupportMaterial.cpp +++ b/xs/src/libslic3r/SupportMaterial.cpp @@ -2,75 +2,6 @@ namespace Slic3r { -Polygon -SupportMaterial::create_circle(coordf_t radius) -{ - Points points; - coordf_t positions[] = {5 * PI / 3, - 4 * PI / 3, - PI, - 2 * PI / 3, - PI / 3, - 0}; - for (auto pos : positions) { - points.emplace_back(radius * cos(pos), (radius * sin(pos))); - } - - return Polygon(points); -} - -Polygons -SupportMaterial::p(SurfacesPtr &surfaces) -{ - Polygons ret; - for (auto surface : surfaces) { - ret.push_back(surface->expolygon.contour); - for (const auto &hole_polygon : surface->expolygon.holes) { - ret.push_back(hole_polygon); - } - } - return ret; -} - -void -SupportMaterial::append_polygons(Polygons &dst, Polygons &src) -{ - for (const auto polygon : src) { - dst.push_back(polygon); - } -} - -coordf_t -SupportMaterial::contact_distance(coordf_t layer_height, coordf_t nozzle_diameter) -{ - coordf_t extra = static_cast(object_config->support_material_contact_distance.value); - if (extra == 0) { - return layer_height; - } - else { - return nozzle_diameter + extra; - } -} - -vector -SupportMaterial::get_keys_sorted(map _map) -{ - vector ret; - for (auto el : _map) - ret.push_back(el.first); - sort(ret.begin(), ret.end()); - return ret; -} - -coordf_t -SupportMaterial::get_max_layer_height(PrintObject *object) -{ - coordf_t ret = -1; - for (auto layer : object->layers) - ret = max(ret, layer->height); - return ret; -} - void SupportMaterial::generate_toolpaths(PrintObject *object, map overhang, @@ -638,6 +569,93 @@ SupportMaterial::object_top(PrintObject *object, map *contac return top; } +void +SupportMaterial::generate_pillars_shape(const map &contact, + const vector &support_z, + map &shape) +{ + // This prevents supplying an empty point set to BoundingBox constructor. + if (contact.empty()) return; + + coord_t pillar_size = scale_(object_config->support_material_pillar_size.value); + coord_t pillar_spacing = scale_(object_config->support_material_pillar_spacing.value); + + Polygons grid; + { + auto pillar = Polygon({ + Point(0, 0), + Point(pillar_size, coord_t(0)), + Point(pillar_size, pillar_size), + Point(coord_t(0), pillar_size) + }); + + Polygons pillars; + BoundingBox bb; + { + Points bb_points; + for (auto contact_el : contact) { + append_to(bb_points, to_points(contact_el.second)); + } + bb = BoundingBox(bb_points); + } + + for (auto x = bb.min.x; x <= bb.max.x - pillar_size; x += pillar_spacing) { + for (auto y = bb.min.y; y <= bb.max.y - pillar_size; y += pillar_spacing) { + pillars.push_back(pillar); + pillar.translate(x, y); + } + } + + grid = union_(pillars); + } + + // Add pillars to every layer. + for (auto i = 0; i < support_z.size(); i++) { + shape[i] = grid; + } + + // Build capitals. + for (auto i = 0; i < support_z.size(); i++) { + coordf_t z = support_z[i]; + + auto capitals = intersection( + grid, + contact.at(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 + // sure nothing is left. + Polygons contact_supported_by_capitals; + for (auto capital : capitals) { + // Enlarge capital tops. + auto capital_polygons = offset(Polygons({capital}), +(pillar_spacing - pillar_size) / 2); + append_to(contact_supported_by_capitals, capital_polygons); + + for (int j = i - 1; j >= 0; j--) { + auto jz = support_z[j]; + capital_polygons = offset(Polygons{capital}, -interface_flow->scaled_width() / 2); + if (capitals.empty()) break; + append_to(shape[i], capital_polygons); + } + } + + // 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 + // sure nothing is left. + auto contact_not_supported_by_capitals = diff( + contact.at(z), + contact_supported_by_capitals + ); + + if (!contact_not_supported_by_capitals.empty()) { + for (int j = i - 1; j >= 0; j--) { + append_to(shape[j], contact_not_supported_by_capitals); + } + } + } +} + map SupportMaterial::generate_base_layers(vector support_z, map contact, @@ -794,6 +812,18 @@ SupportMaterial::generate_bottom_interface_layers(const vector &suppor } } +coordf_t +SupportMaterial::contact_distance(coordf_t layer_height, coordf_t nozzle_diameter) +{ + coordf_t extra = static_cast(object_config->support_material_contact_distance.value); + if (extra == 0) { + return layer_height; + } + else { + return nozzle_diameter + extra; + } +} + vector SupportMaterial::overlapping_layers(int layer_idx, const vector &support_z) { @@ -868,4 +898,61 @@ SupportMaterial::clip_with_object(map &support, vector */ } +Polygons +SupportMaterial::p(SurfacesPtr &surfaces) +{ + Polygons ret; + for (auto surface : surfaces) { + ret.push_back(surface->expolygon.contour); + for (const auto &hole_polygon : surface->expolygon.holes) { + ret.push_back(hole_polygon); + } + } + return ret; +} + +void +SupportMaterial::append_polygons(Polygons &dst, Polygons &src) +{ + for (const auto polygon : src) { + dst.push_back(polygon); + } +} + +vector +SupportMaterial::get_keys_sorted(map _map) +{ + vector ret; + for (auto el : _map) + ret.push_back(el.first); + sort(ret.begin(), ret.end()); + return ret; +} + +coordf_t +SupportMaterial::get_max_layer_height(PrintObject *object) +{ + coordf_t ret = -1; + for (auto layer : object->layers) + ret = max(ret, layer->height); + return ret; +} + +Polygon +SupportMaterial::create_circle(coordf_t radius) +{ + Points points; + coordf_t positions[] = {5 * PI / 3, + 4 * PI / 3, + PI, + 2 * PI / 3, + PI / 3, + 0}; + for (auto pos : positions) { + points.emplace_back(radius * cos(pos), (radius * sin(pos))); + } + + return Polygon(points); +} + } diff --git a/xs/src/libslic3r/SupportMaterial.hpp b/xs/src/libslic3r/SupportMaterial.hpp index 9d855bedd..2a7c52ed6 100644 --- a/xs/src/libslic3r/SupportMaterial.hpp +++ b/xs/src/libslic3r/SupportMaterial.hpp @@ -50,145 +50,6 @@ public: object(nullptr) {} - void generate_pillars_shape(const map &contact, - const vector &support_z, - map &shape) - { - // This prevents supplying an empty point set to BoundingBox constructor. - if (contact.empty()) return; - - coord_t pillar_size = scale_(object_config->support_material_pillar_size.value); - coord_t pillar_spacing = scale_(object_config->support_material_pillar_spacing.value); - - Polygons grid; - { - auto pillar = Polygon({ - Point(0, 0), - Point(pillar_size, coord_t(0)), - Point(pillar_size, pillar_size), - Point(coord_t(0), pillar_size) - }); - - Polygons pillars; - BoundingBox bb; - { - Points bb_points; - for (auto contact_el : contact) { - append_to(bb_points, to_points(contact_el.second)); - } - bb = BoundingBox(bb_points); - } - - for (auto x = bb.min.x; x <= bb.max.x - pillar_size; x += pillar_spacing) { - for (auto y = bb.min.y; y <= bb.max.y - pillar_size; y += pillar_spacing) { - pillars.push_back(pillar); - pillar.translate(x, y); - } - } - - grid = union_(pillars); - } - - // Add pillars to every layer. - for (auto i = 0; i < support_z.size(); i++) { - shape[i] = grid; - } - - // Build capitals. - for (auto i = 0; i < support_z.size(); i++) { - coordf_t z = support_z[i]; - - auto capitals = intersection( - grid, - contact.at(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 - // sure nothing is left. - Polygons contact_supported_by_capitals; - for (auto capital : capitals) { - // Enlarge capital tops. - auto capital_polygons = offset(Polygons({capital}), +(pillar_spacing - pillar_size) / 2); - append_to(contact_supported_by_capitals, capital_polygons); - - for (int j = i - 1; j >= 0; j--) { - auto jz = support_z[j]; - capital_polygons = offset(Polygons{capital}, -interface_flow->scaled_width() / 2); - if (capitals.empty()) break; - append_to(shape[i], capital_polygons); - } - } - - // 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 - // sure nothing is left. - auto contact_not_supported_by_capitals = diff( - contact.at(z), - contact_supported_by_capitals - ); - - if (!contact_not_supported_by_capitals.empty()) { - for (int j = i - 1; j >= 0; j--) { - append_to(shape[j], contact_not_supported_by_capitals); - } - } - } - } - - void generate_bottom_interface_layers(const vector &support_z, - map &base, - map &top, - map &interface); - - map generate_base_layers(vector support_z, - map contact, - map interface, - map top); - - map generate_interface_layers(vector support_z, - map contact, - map top); - - pair, map> contact_area(PrintObject *object); - - void generate(PrintObject *object); - - void generate_toolpaths(PrintObject *object, - map overhang, - map contact, - map interface, - map base); - - // Is this expolygons or polygons? - map object_top(PrintObject *object, map *contact); - - // This method removes object silhouette from support material - // (it's used with interface and base only). It removes a bit more, - // leaving a thin gap between object and support in the XY plane. - void clip_with_object(map &support, vector support_z, PrintObject &object); - - void clip_with_shape(map &support, map &shape); - - /// This method returns the indices of the layers overlapping with the given one. - vector overlapping_layers(int layer_idx, const vector &support_z); - - vector support_layers_z(vector contact_z, - vector top_z, - coordf_t max_object_layer_height); - - coordf_t contact_distance(coordf_t layer_height, coordf_t nozzle_diameter); - - Polygons p(SurfacesPtr &surfaces); - - Polygon create_circle(coordf_t radius); - - void append_polygons(Polygons &dst, Polygons &src); - - vector get_keys_sorted(map _map); - - coordf_t get_max_layer_height(PrintObject *object); - void process_layer(int layer_id) { SupportLayer *layer = this->object->support_layers[layer_id]; @@ -212,7 +73,64 @@ public: } + void generate_toolpaths(PrintObject *object, + map overhang, + map contact, + map interface, + map base); + + void generate(PrintObject *object); + + vector support_layers_z(vector contact_z, + vector top_z, + coordf_t max_object_layer_height); + + pair, map> contact_area(PrintObject *object); + + // Is this expolygons or polygons? + map object_top(PrintObject *object, map *contact); + + void generate_pillars_shape(const map &contact, + const vector &support_z, + map &shape); + + map generate_base_layers(vector support_z, + map contact, + map interface, + map top); + + map generate_interface_layers(vector support_z, + map contact, + map top); + + void generate_bottom_interface_layers(const vector &support_z, + map &base, + map &top, + map &interface); + + coordf_t contact_distance(coordf_t layer_height, coordf_t nozzle_diameter); + + /// This method returns the indices of the layers overlapping with the given one. + vector overlapping_layers(int layer_idx, const vector &support_z); + + void clip_with_shape(map &support, map &shape); + + // This method removes object silhouette from support material + // (it's used with interface and base only). It removes a bit more, + // leaving a thin gap between object and support in the XY plane. + void clip_with_object(map &support, vector support_z, PrintObject &object); + private: + coordf_t get_max_layer_height(PrintObject *object); + + void append_polygons(Polygons &dst, Polygons &src); + + Polygons p(SurfacesPtr &surfaces); + + vector get_keys_sorted(map _map); + + Polygon create_circle(coordf_t radius); + // Used during generate_toolpaths function. PrintObject *object; map overhang; From 76c686b2ca27606db8d9067f5000bfc6fc1e5caf Mon Sep 17 00:00:00 2001 From: Samir55 Date: Thu, 12 Jul 2018 07:11:34 +0200 Subject: [PATCH 23/30] Porting process_layer function and start testing. --- xs/src/libslic3r/SupportMaterial.cpp | 331 +++++++++++++++++++++++++-- xs/src/libslic3r/SupportMaterial.hpp | 56 +++-- 2 files changed, 340 insertions(+), 47 deletions(-) diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp index b32eaea12..5d76a951b 100644 --- a/xs/src/libslic3r/SupportMaterial.cpp +++ b/xs/src/libslic3r/SupportMaterial.cpp @@ -2,6 +2,14 @@ namespace Slic3r { + +PolylineCollection _fill_surface(Fill *fill, Surface *surface) +{ + PolylineCollection ps; + ps.polylines = fill->fill_surface(*surface); + return ps; +} + void SupportMaterial::generate_toolpaths(PrintObject *object, map overhang, @@ -13,35 +21,35 @@ SupportMaterial::generate_toolpaths(PrintObject *object, this->object = object; // Shape of contact area. - int contact_loops = 1; - coordf_t circle_radius = 1.5 * interface_flow->scaled_width(); - coordf_t circle_distance = 3 * circle_radius; - Polygon circle = create_circle(circle_radius); + toolpaths_params params; + params.contact_loops = 1; + params.circle_radius = 1.5 * interface_flow->scaled_width(); + params.circle_distance = 3 * params.circle_radius; + params.circle = create_circle(params.circle_radius); // TODO Add Slic3r debug. "Generating patterns\n" // Prepare fillers. - auto pattern = object_config->support_material_pattern; - vector angles; - angles.push_back(object_config->support_material_angle.value); + params.pattern = object_config->support_material_pattern.value; + params.angles.push_back(object_config->support_material_angle.value); - if (pattern == smpRectilinearGrid) { - pattern = smpRectilinear; - angles.push_back(angles[0] + 90); + if (params.pattern == smpRectilinearGrid) { + params.pattern = smpRectilinear; + params.angles.push_back(params.angles[0] + 90); } - else if (pattern == smpPillars) { - pattern = smpHoneycomb; + else if (params.pattern == smpPillars) { + params.pattern = smpHoneycomb; } - auto interface_angle = object_config->support_material_angle.value + 90; - auto interface_spacing = object_config->support_material_interface_spacing.value + interface_flow->spacing(); - auto interface_density = interface_spacing == 0 ? 1 : interface_flow->spacing() / interface_spacing; - auto support_spacing = object_config->support_material_spacing + flow->spacing(); - auto support_density = support_spacing == 0 ? 1 : flow->spacing() / support_spacing; + params.interface_angle = object_config->support_material_angle.value + 90; + params.interface_spacing = object_config->support_material_interface_spacing.value + interface_flow->spacing(); + params.interface_density = params.interface_spacing == 0 ? 1 : interface_flow->spacing() / params.interface_spacing; + params.support_spacing = object_config->support_material_spacing.value + flow->spacing(); + params.support_density = params.support_spacing == 0 ? 1 : flow->spacing() / params.support_spacing; parallelize( 0, object->support_layers.size() - 1, - boost::bind(&SupportMaterial::process_layer, this, _1), + boost::bind(&SupportMaterial::process_layer, this, _1, params), this->config->threads.value ); } @@ -571,8 +579,8 @@ SupportMaterial::object_top(PrintObject *object, map *contac void SupportMaterial::generate_pillars_shape(const map &contact, - const vector &support_z, - map &shape) + const vector &support_z, + map &shape) { // This prevents supplying an empty point set to BoundingBox constructor. if (contact.empty()) return; @@ -758,9 +766,9 @@ SupportMaterial::generate_interface_layers(vector support_z, void SupportMaterial::generate_bottom_interface_layers(const vector &support_z, - map &base, - map &top, - map &interface) + map &base, + map &top, + map &interface) { // If no interface layers are allowed, don't generate bottom interface layers. if (object_config->support_material_interface_layers.value == 0) @@ -898,6 +906,283 @@ SupportMaterial::clip_with_object(map &support, vector */ } +void +SupportMaterial::process_layer(int layer_id, toolpaths_params params) +{ + SupportLayer *layer = this->object->support_layers[layer_id]; + coordf_t z = layer->print_z; + + // We redefine flows locally by applyinh this layer's height. + auto _flow = *flow; + auto _interface_flow = *interface_flow; + _flow.height = static_cast(layer->height); + _interface_flow.height = static_cast(layer->height); + + Polygons overhang = this->overhang.count(z) > 0 ? this->overhang[z] : Polygons(); + Polygons contact = this->contact.count(z) > 0 ? this->contact[z] : Polygons(); + Polygons interface = this->interface.count(layer_id) > 0 ? this->interface[layer_id] : Polygons(); + Polygons base = this->base.count(layer_id) > 0 ? this->base[layer_id] : Polygons(); + + // Islands. + { + Polygons ps; + append_to(ps, base); + append_to(ps, contact); + append_to(ps, interface); + layer->support_islands.append(union_ex(ps)); + } + + // Contact. + Polygons contact_infill; + if (object_config->support_material_interface_layers.value == 0) { + // If no interface layers were requested we treat the contact layer + // exactly as a generic base layer. + append_to(base, contact); + } + else if (!contact.empty() && params.contact_loops > 0) { // Generate the outermost loop. + // Find the centerline of the external loop (or any other kind of extrusions should the loop be skipped). + contact = offset(contact, -_interface_flow.scaled_width() / 2); + + Polygons loops0; + { + // Find centerline of the external loop of the contours. + auto external_loops = contact; + + // Only consider the loops facing the overhang. + { + auto overhang_with_margin = offset(overhang, +_interface_flow.scaled_width() / 2); + { + Polygons ps; + for (auto p : external_loops) { + if (!intersection_pl( + p.split_at_first_point(), + overhang_with_margin).empty() + ) + ps.push_back(p); + } + external_loops = ps; + } + } + + // Apply a pattern to the loop. + Points positions; + for (auto p : external_loops) + append_to(positions, Polygon(p).equally_spaced_points(params.circle_distance)); + Polygons circles; + for (auto pos : positions) { + circles.push_back(params.circle); + circles.back().translate(pos.x, pos.y); + } + loops0 = diff( + external_loops, + circles + ); + } + + // TODO Revise the loop range. + // Make more loops. + auto loops = loops0; + for (int i = 2; i <= params.contact_loops; i++) { + auto d = (i - 1) * _interface_flow.scaled_spacing(); + append_to(loops, + offset2(loops0, + -d - 0.5 * _interface_flow.scaled_spacing(), + +0.5 * _interface_flow.scaled_spacing())); + } + + // Clip such loops to the side oriented towards the object. + Polylines ps; + for (auto p : loops) + ps.push_back(p.split_at_first_point()); + + auto new_loops = intersection_pl( + ps, + offset(overhang, scale_(SUPPORT_MATERIAL_MARGIN)) + ); + + // 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 + // a very large gap between loops and contact_infill, so maybe another + // solution should be found to achieve both goals. + { + Polygons ps; + for (auto pl : new_loops) + append_to(ps, offset(pl, params.circle_radius * 1.1)); + + contact_infill = diff( + contact, + ps + ); + } + + // Transform loops into ExtrusionPath objects. + auto mm3_per_mm = _interface_flow.mm3_per_mm(); + ExtrusionPaths loops_extrusion_paths; + for (auto l : new_loops) { + ExtrusionPath extrusion_path(erSupportMaterialInterface); + extrusion_path.polyline = l; + extrusion_path.mm3_per_mm = mm3_per_mm; + extrusion_path.width = _interface_flow.width; + extrusion_path.height = layer->height; + loops_extrusion_paths.push_back(extrusion_path); + } + + layer->support_interface_fills.append(loops_extrusion_paths); + } + + // Allocate the fillers exclusively in the worker threads! Don't allocate them at the main thread, + // as Perl copies the C++ pointers by default, so then the C++ objects are shared between threads! + map fillers; + fillers["interface"] = Fill::new_from_type("rectilinear"); + fillers["support"] = Fill::new_from_type(InfillPattern(params.pattern)); // FIXME @Samir55 + + BoundingBox bounding_box = object->bounding_box(); + fillers["interface"]->bounding_box = bounding_box; + fillers["support"]->bounding_box = bounding_box; + + // Interface and contact infill. + if (!interface.empty() || !contact_infill.empty()) { + // Make interface layers alternate angles by 90 degrees. + double alternate_angle = params.interface_angle + (90 * ((layer_id + 1) % 2)); + fillers["interface"]->angle = static_cast(Geometry::deg2rad(alternate_angle)); + fillers["interface"]->min_spacing = _interface_flow.spacing(); + + // Find centerline of the external loop. + interface = offset2(interface, +SCALED_EPSILON, -(SCALED_EPSILON + _interface_flow.scaled_width() / 2)); + + // Join regions by offsetting them to ensure they're merged. + { + Polygons ps; + append_to(ps, interface); + append_to(ps, contact_infill); + interface = offset(ps, SCALED_EPSILON); + } + + // turn base support into interface when it's contained in our holes + // (this way we get wider interface anchoring). + { + Polygons ps = interface; + interface = Polygons(); + for (auto p: ps) { + if (p.is_clockwise()) { + Polygon p2 = p; + p2.make_counter_clockwise(); + if (diff(Polygons({p2}), base, 1).empty()) + continue; + interface.push_back(p); + } + } + } + base = diff(base, interface); + + ExtrusionPaths paths; + ExPolygons expolygons = union_ex(interface); + for (auto expolygon : expolygons) { + Surface surface(stInternal, expolygon); + fillers["interface"]->density = params.interface_density; + fillers["interface"]->complete = true; + // TODO What layer height come from FIXME. or Polyline collection + Polylines ps = fillers["interface"]->fill_surface(surface); + + auto mm3_per_mm = _interface_flow.mm3_per_mm(); + + for (auto p : ps) { + ExtrusionPath extrusion_path(erSupportMaterialInterface); + extrusion_path.polyline = p; + extrusion_path.mm3_per_mm = mm3_per_mm; + extrusion_path.width = _interface_flow.width; + extrusion_path.height = layer->height; + paths.push_back(extrusion_path); + } + } + + layer->support_interface_fills.append(paths); + } + + // Support or flange + if (!base.empty()) { + Fill *filler = fillers["support"]; + filler->angle = static_cast(Geometry::deg2rad(params.angles[layer_id % int(params.angles.size())])); + + // We don't use $base_flow->spacing because we need a constant spacing + // value that guarantees that all layers are correctly aligned. + filler->min_spacing = flow->spacing(); + + auto density = params.support_density; + Flow *base_flow = &_flow; + + // Find centerline of the external loop/extrusions. + Polygons to_infill = offset2(base, +SCALED_EPSILON, -(SCALED_EPSILON + _flow.scaled_width() / 2)); + ExPolygons new_to_infill; + + ExtrusionPaths paths; + + // Base flange. + if (layer_id == 0) { + filler = fillers["interface"]; + filler->angle = static_cast(Geometry::deg2rad(object_config->support_material_angle.value + 90)); + density = 0.5; + base_flow = first_layer_flow; + + // Use the proper spacing for first layer as we don't need to align + // its pattern to the other layers. + filler->min_spacing = base_flow->spacing(); + + // Subtract brim so that it goes around the object fully (and support gets its own brim). + if (config->brim_width.value > 0) { + float d = +scale_(config->brim_width.value * 2); + + new_to_infill = diff_ex( + to_infill, + offset(to_polygons(object->get_layer(0)->slices), d) + ); + } + else { + new_to_infill = union_ex(to_infill); + } + } + else { + // Draw a perimeter all around support infill. + // TODO: use brim ordering algorithm. + auto mm3_per_mm = _flow.mm3_per_mm(); + for (auto p : to_infill) { + ExtrusionPath extrusionPath(erSupportMaterial); + extrusionPath.polyline = p.split_at_first_point(); + extrusionPath.mm3_per_mm = mm3_per_mm; + extrusionPath.width = _flow.width; + extrusionPath.height = layer->height; + + paths.push_back(extrusionPath); + } + + // TODO: use offset2_ex() + new_to_infill = offset_ex(to_infill, -_flow.scaled_spacing()); + } + + auto mm3_per_mm = base_flow->mm3_per_mm(); + for (const auto &expolygon : new_to_infill) { + Surface surface(stInternal, expolygon); + filler->density = static_cast(density); + filler->complete = true; + // TODO What layer height come from FIXME. or Polyline collection + Polylines ps = filler->fill_surface(surface); + + for (auto pl : ps) { + ExtrusionPath extrusionPath(erSupportMaterial); + extrusionPath.polyline = pl; + extrusionPath.mm3_per_mm = mm3_per_mm; + extrusionPath.width = base_flow->width; + extrusionPath.height = static_cast(layer->height); + + paths.push_back(extrusionPath); + } + } + + layer->support_fills.append(paths); + } +} + Polygons SupportMaterial::p(SurfacesPtr &surfaces) { diff --git a/xs/src/libslic3r/SupportMaterial.hpp b/xs/src/libslic3r/SupportMaterial.hpp index 2a7c52ed6..7f375892b 100644 --- a/xs/src/libslic3r/SupportMaterial.hpp +++ b/xs/src/libslic3r/SupportMaterial.hpp @@ -12,9 +12,9 @@ #include "Geometry.hpp" #include "Print.hpp" #include "ClipperUtils.hpp" -#include "SupportMaterial.hpp" #include "ExPolygon.hpp" #include "SVG.hpp" +#include using namespace std; @@ -28,6 +28,35 @@ constexpr coordf_t PILLAR_SIZE = 2.5; constexpr coordf_t PILLAR_SPACING = 10; +struct toolpaths_params +{ + int contact_loops; + coordf_t circle_radius; + coordf_t circle_distance; + Polygon circle; + SupportMaterialPattern pattern; + vector angles; + double interface_angle; + double interface_spacing; + float interface_density; + double support_spacing; + double support_density; + + toolpaths_params(int contact_loops = 0, + coordf_t circle_radius = 0, + coordf_t circle_distance = 0, + const Polygon &circle = Polygon(), + const SupportMaterialPattern &pattern = SupportMaterialPattern(), + const vector &angles = vector()) + : contact_loops(contact_loops), + circle_radius(circle_radius), + circle_distance(circle_distance), + circle(circle), + pattern(pattern), + angles(angles) + {} +}; + class SupportMaterial { public: @@ -50,29 +79,6 @@ public: object(nullptr) {} - void process_layer(int layer_id) - { - SupportLayer *layer = this->object->support_layers[layer_id]; - coordf_t z = layer->print_z; - - // We redefine flows locally by applyinh this layer's height. - auto _flow = *flow; - auto _interface_flow = *interface_flow; - _flow.height = static_cast(layer->height); - _interface_flow.height = static_cast(layer->height); - - Polygons overhang = this->overhang.count(z) > 0 ? this->overhang[z] : Polygons(); - Polygons contact = this->contact.count(z) > 0 ? this->contact[z] : Polygons(); - Polygons interface = interface[layer_id]; - Polygons base = base[layer_id]; - - // TODO add the equivalent debug code. - - // Islands. -// layer->support_islands.append() - - } - void generate_toolpaths(PrintObject *object, map overhang, map contact, @@ -120,6 +126,8 @@ public: // leaving a thin gap between object and support in the XY plane. void clip_with_object(map &support, vector support_z, PrintObject &object); + void process_layer(int layer_id, toolpaths_params params); + private: coordf_t get_max_layer_height(PrintObject *object); From e80c4e467ba34f3a315522c4152c053cb2068c3d Mon Sep 17 00:00:00 2001 From: Ahmed Samir Date: Thu, 12 Jul 2018 07:14:28 +0200 Subject: [PATCH 24/30] Update CMakeLists.txt readd removed file in cmake --- src/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4fc3ad07b..5ddc12ef5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -140,6 +140,7 @@ add_library(libslic3r STATIC ${LIBDIR}/libslic3r/SurfaceCollection.cpp ${LIBDIR}/libslic3r/SVG.cpp ${LIBDIR}/libslic3r/TriangleMesh.cpp + ${LIBDIR}/libslic3r/Zip/ZipArchive.cpp ${LIBDIR}/libslic3r/SupportMaterial.cpp ${LIBDIR}/libslic3r/utils.cpp ) From 02e489a11f7b52f636896b26f3a00f3dcbba9903 Mon Sep 17 00:00:00 2001 From: Ahmed Samir Date: Thu, 12 Jul 2018 07:15:22 +0200 Subject: [PATCH 25/30] Update CMakeLists.txt revoke last commit --- src/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5ddc12ef5..4fc3ad07b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -140,7 +140,6 @@ add_library(libslic3r STATIC ${LIBDIR}/libslic3r/SurfaceCollection.cpp ${LIBDIR}/libslic3r/SVG.cpp ${LIBDIR}/libslic3r/TriangleMesh.cpp - ${LIBDIR}/libslic3r/Zip/ZipArchive.cpp ${LIBDIR}/libslic3r/SupportMaterial.cpp ${LIBDIR}/libslic3r/utils.cpp ) From 93b0cd436a8ca8c48ac8b39a59e49d951fa28930 Mon Sep 17 00:00:00 2001 From: Samir55 Date: Thu, 12 Jul 2018 09:34:47 +0200 Subject: [PATCH 26/30] Porting test 1. --- src/CMakeLists.txt | 6 +- src/test/libslic3r/test_support_material.cpp | 98 ++++++++++++++++ xs/src/libslic3r/SupportMaterial.cpp | 9 +- xs/src/libslic3r/SupportMaterial.hpp | 117 +++---------------- 4 files changed, 123 insertions(+), 107 deletions(-) create mode 100644 src/test/libslic3r/test_support_material.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4fc3ad07b..0f2dbe99e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -84,7 +84,7 @@ include_directories(${LIBDIR}/poly2tri/common) add_library(ZipArchive STATIC ${LIBDIR}/Zip/ZipArchive.cpp ) - +target_compile_features(ZipArchive PUBLIC cxx_std_11) add_library(libslic3r STATIC ${LIBDIR}/libslic3r/BoundingBox.cpp @@ -165,6 +165,8 @@ add_library(expat STATIC ${LIBDIR}/expat/xmlrole.c ${LIBDIR}/expat/xmltok.c ) +target_compile_features(clipper PUBLIC cxx_std_11) + add_library(polypartition STATIC ${LIBDIR}/polypartition.cpp) add_library(poly2tri STATIC ${LIBDIR}/poly2tri/common/shapes.cc @@ -191,7 +193,7 @@ set(UI_TEST_SOURCES set(SLIC3R_TEST_SOURCES ${TESTDIR}/test_harness.cpp ${TESTDIR}/libslic3r/test_trianglemesh.cpp -) + test/libslic3r/test_support_material.cpp) add_executable(slic3r slic3r.cpp) #set_target_properties(slic3r PROPERTIES LINK_SEARCH_START_STATIC 1) #set_target_properties(slic3r PROPERTIES LINK_SEARCH_END_STATIC 1) diff --git a/src/test/libslic3r/test_support_material.cpp b/src/test/libslic3r/test_support_material.cpp new file mode 100644 index 000000000..29d888e0b --- /dev/null +++ b/src/test/libslic3r/test_support_material.cpp @@ -0,0 +1,98 @@ +#include +//#include "/home/ahmedsamir/Work/SamirSlic3r/Slic3r/build/external/Catch/include/catch.hpp" + +#include "libslic3r.h" +#include "TriangleMesh.hpp" +#include "Model.hpp" +#include "SupportMaterial.hpp" + +using namespace std; +using namespace Slic3r; + +SCENARIO("SupportMaterial: support_layers_z and contact_distance") +{ + GIVEN("A print object having one modelObject") { + // Create a mesh & modelObject. + TriangleMesh mesh = TriangleMesh::make_cube(20, 20, 20); + + // Create modelObject. + Model model = Model(); + ModelObject *object = model.add_object(); + object->add_volume(mesh); + model.add_default_instances(); + + // Align to origin. + model.align_instances_to_origin(); + + // Create Print. + Print print = Print(); + print.default_object_config.set_deserialize("support_material", "1"); + + vector contact_z = {1.9}; + vector top_z = {1.1}; + + WHEN("Layer height = 0.2 and, first layer height = 0.3") { + + print.default_object_config.set_deserialize("layer_height", "0.2"); + print.default_object_config.set_deserialize("first_layer_height", "0.3"); + + print.add_model_object(model.objects[0]); + print.objects.front()->_slice(); + + SupportMaterial support = SupportMaterial(&print.config, &print.objects.front()->config); + + vector + support_z = support.support_layers_z(contact_z, top_z, print.default_object_config.layer_height.value); + + THEN("First layer height is honored") { + REQUIRE((support_z[0] == print.default_object_config.first_layer_height.value)); + } + + THEN("No null or negative support layers") { + bool check = true; + for (size_t i = 1; i < support_z.size(); ++i) + if (support_z[i] - support_z[i - 1] <= 0) check = false; + REQUIRE(check); + } + + THEN("No layers thicker than nozzle diameter") { + bool check = true; + for (size_t i = 1; i < support_z.size(); ++i) + if (support_z[i] - support_z[i - 1] > print.config.nozzle_diameter.get_at(0) + EPSILON) + check = false; + REQUIRE(check); + } + + THEN("Layers above top surfaces are spaced correctly") { + coordf_t expected_top_spacing = support + .contact_distance(print.default_object_config.layer_height, print.config.nozzle_diameter.get_at(0)); + + bool wrong_top_spacing = 0; + for (coordf_t top_z_el : top_z) { + // find layer index of this top surface. + int layer_id = -1; + for (int i = 0; i < support_z.size(); i++) { + if (abs(support_z[i] - top_z_el) < EPSILON) { + layer_id = i; + i = static_cast(support_z.size()); + } + } + + // check that first support layer above this top surface (or the next one) is spaced with nozzle diameter + if (abs(support_z[layer_id + 1] - support_z[layer_id] - expected_top_spacing) > EPSILON + && abs(support_z[layer_id + 2] - support_z[layer_id] - expected_top_spacing) > EPSILON) { + wrong_top_spacing = 1; + } + REQUIRE(!wrong_top_spacing); + } + } + } +// /* Test Also with this +// $config->set('first_layer_height', 0.4); +// $test->(); +// +// $config->set('layer_height', $config->nozzle_diameter->[0]); +// $test->(); +// */ + } +} diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp index 5d76a951b..e23e0c71b 100644 --- a/xs/src/libslic3r/SupportMaterial.cpp +++ b/xs/src/libslic3r/SupportMaterial.cpp @@ -162,12 +162,11 @@ SupportMaterial::support_layers_z(vector contact_z, } // Create other layers (skip raft layers as they're already done and use thicker layers). - for (size_t i = z.size(); i >= object_config->raft_layers; i--) { + for (int i = static_cast(z.size()); i >= object_config->raft_layers.value; i--) { coordf_t target_height = support_material_height; if (i > 0 && is_top[z[i - 1]]) { target_height = nozzle_diameter; } - // Enforce first layer height. if ((i == 0 && z[i] > target_height + first_layer_height) || (z[i] - z[i - 1] > target_height + EPSILON)) { @@ -179,10 +178,10 @@ SupportMaterial::support_layers_z(vector contact_z, // Remove duplicates and make sure all 0.x values have the leading 0. { set s; - for (auto el : z) - s.insert(roundf(static_cast((el * 100)) / 100)); // round it to 2 decimal places. + for (coordf_t el : z) + s.insert(int(el * 100) / 100.0); // round it to 2 decimal places. z = vector(); - for (auto el : s) + for (coordf_t el : s) z.push_back(el); } diff --git a/xs/src/libslic3r/SupportMaterial.hpp b/xs/src/libslic3r/SupportMaterial.hpp index 7f375892b..923dadc26 100644 --- a/xs/src/libslic3r/SupportMaterial.hpp +++ b/xs/src/libslic3r/SupportMaterial.hpp @@ -28,6 +28,7 @@ constexpr coordf_t PILLAR_SIZE = 2.5; constexpr coordf_t PILLAR_SPACING = 10; +/// Struct for carrying the toolpaths parameters needed for each thread. struct toolpaths_params { int contact_loops; @@ -36,11 +37,11 @@ struct toolpaths_params Polygon circle; SupportMaterialPattern pattern; vector angles; - double interface_angle; - double interface_spacing; - float interface_density; - double support_spacing; - double support_density; + double interface_angle{}; + double interface_spacing{}; + float interface_density{}; + double support_spacing{}; + double support_density{}; toolpaths_params(int contact_loops = 0, coordf_t circle_radius = 0, @@ -60,11 +61,11 @@ struct toolpaths_params class SupportMaterial { public: - PrintConfig *config; ///< - PrintObjectConfig *object_config; ///< - Flow *flow; ///< - Flow *first_layer_flow; ///< - Flow *interface_flow; ///< + PrintConfig *config; ///< The print config + PrintObjectConfig *object_config; ///< The object print config. + Flow *flow; ///< The intermediate layers print flow. + Flow *first_layer_flow; ///< The first (base) layers print flow. + Flow *interface_flow; ///< The interface layers print flow. SupportMaterial(PrintConfig *print_config, PrintObjectConfig *print_object_config, @@ -79,14 +80,17 @@ public: object(nullptr) {} + /// Generate the extrusions paths for the support matterial generated for the given print object. void generate_toolpaths(PrintObject *object, map overhang, map contact, map interface, map base); + /// Generate support material for the given print object. void generate(PrintObject *object); + /// Generate the support layers z coordinates. (TODO Dicuss more). vector support_layers_z(vector contact_z, vector top_z, coordf_t max_object_layer_height); @@ -129,10 +133,13 @@ public: void process_layer(int layer_id, toolpaths_params params); private: + // Get the maximum layer height given a print object. coordf_t get_max_layer_height(PrintObject *object); + // (Deprecated) use append_to instead TODO @Samir55. void append_polygons(Polygons &dst, Polygons &src); + // Return polygon vector given a vector of surfaces. Polygons p(SurfacesPtr &surfaces); vector get_keys_sorted(map _map); @@ -148,96 +155,6 @@ private: }; -// To Be converted to catch. -// Supports Material tests. -class SupportMaterialTests -{ -public: - bool test_1() - { - // Create a mesh & modelObject. - TriangleMesh mesh = TriangleMesh::make_cube(20, 20, 20); - - // Create modelObject. - Model model = Model(); - ModelObject *object = model.add_object(); - object->add_volume(mesh); - model.add_default_instances(); - - // Align to origin. - model.align_instances_to_origin(); - - // Create Print. - Print print = Print(); - - // Configure the printObjectConfig. - print.default_object_config.set_deserialize("support_material", "1"); - print.default_object_config.set_deserialize("layer_height", "0.2"); - print.config.set_deserialize("first_layer_height", "0.3"); - - vector contact_z; - vector top_z; - contact_z.push_back(1.9); - top_z.push_back(1.9); - - // Add the modelObject. - print.add_model_object(model.objects[0]); - - // Create new supports. - SupportMaterial support = SupportMaterial(&print.config, &print.objects.front()->config); - - vector - support_z = support.support_layers_z(contact_z, top_z, print.default_object_config.layer_height); - -// bool -// is_1 = (support_z[0] == print.default_object_config.first_layer_height); // 'first layer height is honored'. -// -// bool is_2 = false; // 'no null or negative support layers'. -// for (int i = 1; i < support_z.size(); ++i) { -// if (support_z[i] - support_z[i - 1] <= 0) is_2 = true; -// } -// -// bool is_3 = false; // 'no layers thicker than nozzle diameter'. -// for (int i = 1; i < support_z.size(); ++i) { -// if (support_z[i] - support_z[i - 1] > print.config.nozzle_diameter.get_at(0) + EPSILON) is_2 = true; -// } -// -// coordf_t expected_top_spacing = -// support.contact_distance(print.default_object_config.layer_height, print.config.nozzle_diameter.get_at(0)); -// coordf_t wrong_top_spacing = 0; -// for (auto top_z_el : top_z) { -// // find layer index of this top surface. -// int layer_id = -1; -// for (int i = 0; i < support_z.size(); i++) { -// if (abs(support_z[i] - top_z_el) < EPSILON) { -// layer_id = i; -// i = static_cast(support_z.size()); -// } -// } -// -// // check that first support layer above this top surface (or the next one) is spaced with nozzle diameter -// if ((support_z[layer_id + 1] - support_z[layer_id]) != expected_top_spacing -// && (support_z[layer_id + 2] - support_z[layer_id]) != expected_top_spacing) -// wrong_top_spacing = 1; -// } -// bool is_4 = !wrong_top_spacing; // 'layers above top surfaces are spaced correctly' - - /* Test Also with this - $config->set('first_layer_height', 0.4); - $test->(); - - $config->set('layer_height', $config->nozzle_diameter->[0]); - $test->(); - */ - } - - bool test_2() - { - - } - -}; - } #endif From 4931e4025e8d5773bbd6da79c3f295e5eadbd512 Mon Sep 17 00:00:00 2001 From: Samir55 Date: Sat, 14 Jul 2018 23:09:22 +0200 Subject: [PATCH 27/30] Some fixes to SupportMaterial memver functions. --- src/test/libslic3r/test_support_material.cpp | 52 ++- xs/src/libslic3r/Print.hpp | 4 + xs/src/libslic3r/PrintObject.cpp | 51 +++ xs/src/libslic3r/SupportMaterial.cpp | 327 ++++++++++--------- xs/src/libslic3r/SupportMaterial.hpp | 39 ++- 5 files changed, 298 insertions(+), 175 deletions(-) diff --git a/src/test/libslic3r/test_support_material.cpp b/src/test/libslic3r/test_support_material.cpp index 29d888e0b..13fbd6d3e 100644 --- a/src/test/libslic3r/test_support_material.cpp +++ b/src/test/libslic3r/test_support_material.cpp @@ -1,5 +1,6 @@ -#include -//#include "/home/ahmedsamir/Work/SamirSlic3r/Slic3r/build/external/Catch/include/catch.hpp" +//#include +#include +#include "/home/ahmedsamir/Work/SamirSlic3r/Slic3r/build/external/Catch/include/catch.hpp" #include "libslic3r.h" #include "TriangleMesh.hpp" @@ -9,6 +10,38 @@ using namespace std; using namespace Slic3r; +// Testing supports material member functions. +TEST_CASE("", "") +{ + // Create a mesh & modelObject. + TriangleMesh mesh = TriangleMesh::make_cube(20, 20, 20); + + // Create modelObject. + Model model = Model(); + ModelObject *object = model.add_object(); + object->add_volume(mesh); + model.add_default_instances(); + + // Align to origin. + model.align_instances_to_origin(); + + // Create Print. + Print print = Print(); + vector contact_z = {1.9}; + vector top_z = {1.1}; + print.default_object_config.support_material = 1; + print.default_object_config.set_deserialize("raft_layers", "3"); + print.add_model_object(model.objects[0]); + print.objects.front()->_slice(); + + + SupportMaterial *support = print.objects.front()->_support_material(); + + support->generate(print.objects.front()); + REQUIRE(print.objects.front()->support_layer_count() == 3); + +} + SCENARIO("SupportMaterial: support_layers_z and contact_distance") { GIVEN("A print object having one modelObject") { @@ -39,10 +72,10 @@ SCENARIO("SupportMaterial: support_layers_z and contact_distance") print.add_model_object(model.objects[0]); print.objects.front()->_slice(); - SupportMaterial support = SupportMaterial(&print.config, &print.objects.front()->config); + SupportMaterial *support = print.objects.front()->_support_material(); vector - support_z = support.support_layers_z(contact_z, top_z, print.default_object_config.layer_height.value); + support_z = support->support_layers_z(contact_z, top_z, print.default_object_config.layer_height.value); THEN("First layer height is honored") { REQUIRE((support_z[0] == print.default_object_config.first_layer_height.value)); @@ -52,7 +85,7 @@ SCENARIO("SupportMaterial: support_layers_z and contact_distance") bool check = true; for (size_t i = 1; i < support_z.size(); ++i) if (support_z[i] - support_z[i - 1] <= 0) check = false; - REQUIRE(check); + REQUIRE(check == true); } THEN("No layers thicker than nozzle diameter") { @@ -60,18 +93,19 @@ SCENARIO("SupportMaterial: support_layers_z and contact_distance") for (size_t i = 1; i < support_z.size(); ++i) if (support_z[i] - support_z[i - 1] > print.config.nozzle_diameter.get_at(0) + EPSILON) check = false; - REQUIRE(check); + REQUIRE(check == true); } THEN("Layers above top surfaces are spaced correctly") { coordf_t expected_top_spacing = support - .contact_distance(print.default_object_config.layer_height, print.config.nozzle_diameter.get_at(0)); + ->contact_distance(print.default_object_config.layer_height, + print.config.nozzle_diameter.get_at(0)); bool wrong_top_spacing = 0; for (coordf_t top_z_el : top_z) { // find layer index of this top surface. - int layer_id = -1; - for (int i = 0; i < support_z.size(); i++) { + size_t layer_id = -1; + for (size_t i = 0; i < support_z.size(); i++) { if (abs(support_z[i] - top_z_el) < EPSILON) { layer_id = i; i = static_cast(support_z.size()); diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index 28b40f1af..1dda0974a 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -15,6 +15,7 @@ #include "PlaceholderParser.hpp" #include "SlicingAdaptive.hpp" #include "LayerHeightSpline.hpp" +#include "SupportMaterial.hpp" #include @@ -25,6 +26,7 @@ class InvalidObjectException : public std::exception {}; class Print; class PrintObject; class ModelObject; +class SupportMaterial; // Print step IDs for keeping track of the print state. enum PrintStep { @@ -132,6 +134,8 @@ class PrintObject Layer* add_layer(int id, coordf_t height, coordf_t print_z, coordf_t slice_z); void delete_layer(int idx); + SupportMaterial* _support_material(); + Flow _support_material_flow(FlowRole role = frSupportMaterial); size_t support_layer_count() const; void clear_support_layers(); SupportLayer* get_support_layer(int idx) { return this->support_layers.at(idx); }; diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index de1bd57ad..7af318b45 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -1095,4 +1095,55 @@ PrintObject::_infill() this->state.set_done(posInfill); } +SupportMaterial * +PrintObject::_support_material() +{ + // TODO what does this line do //= FLOW_ROLE_SUPPORT_MATERIAL; + Flow first_layer_flow = Flow::new_from_config_width( + frSupportMaterial, + print()->config + .first_layer_extrusion_width, // check why this line is put || config.support_material_extrusion_width, + static_cast(print()->config.nozzle_diameter.get_at(static_cast( + config.support_material_extruder + - 1))), // Check why this is put in perl "// $self->print->config->nozzle_diameter->[0]" + static_cast(config.get_abs_value("first_layer_height")), + 0 // No bridge flow ratio. + ); + + return new SupportMaterial( + &print()->config, + &config, + first_layer_flow, + _support_material_flow(), + _support_material_flow(frSupportMaterialInterface) + ); +} + +Flow +PrintObject::_support_material_flow(FlowRole role) +{ + // Create support flow. + int extruder = + (role == frSupportMaterial) ? + config.support_material_extruder.value : config + .support_material_interface_extruder.value; + + auto width = config.support_material_extrusion_width; // || config.extrusion_width; + + if (role == frSupportMaterialInterface) + width = config.support_material_interface_extrusion_width; // || width; + + // We use a bogus layer_height because we use the same flow for all + // support material layers. + Flow support_flow = Flow::new_from_config_width( + role, + width, + static_cast(print()->config.nozzle_diameter + .get_at(static_cast(extruder - 1))), // Check this line $self->print->config->nozzle_diameter->[0]. + static_cast(config.layer_height.value), + 0 + ); + + return support_flow; +} } diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp index e23e0c71b..b29c9030e 100644 --- a/xs/src/libslic3r/SupportMaterial.cpp +++ b/xs/src/libslic3r/SupportMaterial.cpp @@ -17,16 +17,24 @@ SupportMaterial::generate_toolpaths(PrintObject *object, map interface, map base) { + int ll = 0; + //cout << "Generate toolpaths " << ll++ << endl; // Assig the object to the supports class. this->object = object; // Shape of contact area. toolpaths_params params; params.contact_loops = 1; - params.circle_radius = 1.5 * interface_flow->scaled_width(); - params.circle_distance = 3 * params.circle_radius; - params.circle = create_circle(params.circle_radius); + //cout << "Generate toolpaths " << ll++ << endl; + params.circle_radius = 1.5 * interface_flow.scaled_width(); + //cout << "Generate toolpaths " << ll++ << endl; + + params.circle_distance = 3 * params.circle_radius; + //cout << "Generate toolpaths " << ll++ << endl; + + params.circle = create_circle(params.circle_radius); + //cout << "Generate toolpaths " << ll++ << endl; // TODO Add Slic3r debug. "Generating patterns\n" // Prepare fillers. params.pattern = object_config->support_material_pattern.value; @@ -39,12 +47,13 @@ SupportMaterial::generate_toolpaths(PrintObject *object, else if (params.pattern == smpPillars) { params.pattern = smpHoneycomb; } - + //cout << "Generate toolpaths " << ll++ << endl; params.interface_angle = object_config->support_material_angle.value + 90; - params.interface_spacing = object_config->support_material_interface_spacing.value + interface_flow->spacing(); - params.interface_density = params.interface_spacing == 0 ? 1 : interface_flow->spacing() / params.interface_spacing; - params.support_spacing = object_config->support_material_spacing.value + flow->spacing(); - params.support_density = params.support_spacing == 0 ? 1 : flow->spacing() / params.support_spacing; + params.interface_spacing = object_config->support_material_interface_spacing.value + interface_flow.spacing(); + params.interface_density = + static_cast(params.interface_spacing == 0 ? 1 : interface_flow.spacing() / params.interface_spacing); + params.support_spacing = object_config->support_material_spacing.value + flow.spacing(); + params.support_density = params.support_spacing == 0 ? 1 : flow.spacing() / params.support_spacing; parallelize( 0, @@ -52,6 +61,7 @@ SupportMaterial::generate_toolpaths(PrintObject *object, boost::bind(&SupportMaterial::process_layer, this, _1, params), this->config->threads.value ); + //cout << "Generate toolpaths finished" << ll++ << endl; } void @@ -64,39 +74,50 @@ SupportMaterial::generate(PrintObject *object) // that it will be effective, regardless of how it's built below. pair, map> contact_overhang = contact_area(object); map &contact = contact_overhang.first; + cout << "Contact size " << contact.size() << endl; map &overhang = contact_overhang.second; + int ppp = 0; + cout << "Samir " << ppp++ << endl; // Determine the top surfaces of the object. We need these to determine // the layer heights of support material and to clip support to the object // silhouette. map top = object_top(object, &contact); - + cout << "Samir " << ppp++ << endl; // We now know the upper and lower boundaries for our support material object // (@$contact_z and @$top_z), so we can generate intermediate layers. vector support_z = support_layers_z(get_keys_sorted(contact), get_keys_sorted(top), get_max_layer_height(object)); - + cout << "Samir " << ppp++ << endl; // If we wanted to apply some special logic to the first support layers lying on - // object's top surfaces this is the place to detect them. TODO - + // object's top surfaces this is the place to detect them. + map shape; + if (object_config->support_material_pattern.value == smpPillars) + this->generate_pillars_shape(contact, support_z, shape); + cout << "Samir " << ppp++ << endl; // Propagate contact layers downwards to generate interface layers. map interface = generate_interface_layers(support_z, contact, top); clip_with_object(interface, support_z, *object); -// clip_with_shape(base, shape); // TODO - + if (!shape.empty()) + clip_with_shape(interface, shape); + cout << "Samir " << ppp++ << endl; // Propagate contact layers and interface layers downwards to generate - // the main support layers. TODO - + // the main support layers. + map base = generate_base_layers(support_z, contact, interface, top); + clip_with_object(base, support_z, *object); + if (!shape.empty()) + clip_with_shape(base, shape); + cout << "Samir " << ppp++ << endl; // Detect what part of base support layers are "reverse interfaces" because they - // lie above object's top surfaces. TODO - - + // lie above object's top surfaces. + generate_bottom_interface_layers(support_z, base, top, interface); + cout << "Samir " << ppp++ << endl; // Install support layers into object. - for (int i = 0; i < support_z.size(); i++) { + for (int i = 0; i < int(support_z.size()); i++) { object->add_support_layer( i, // id. (i == 0) ? support_z[0] - 0 : (support_z[i] - support_z[i - 1]), // height. @@ -108,10 +129,11 @@ SupportMaterial::generate(PrintObject *object) object->support_layers.end()[-1]->lower_layer = object->support_layers.end()[-2]; } } - + cout << "Supports z count is " << support_z.size() << endl; + cout << "Samir " << ppp++ << endl; // Generate the actual toolpaths and save them into each layer. generate_toolpaths(object, overhang, contact, interface, base); - + cout << "Samir " << ppp++ << endl; } vector @@ -126,7 +148,8 @@ SupportMaterial::support_layers_z(vector contact_z, // determine layer height for any non-contact layer // we use max() to prevent many ultra-thin layers to be inserted in case // layer_height > nozzle_diameter * 0.75. - auto nozzle_diameter = config->nozzle_diameter.get_at(object_config->support_material_extruder - 1); + auto nozzle_diameter = config->nozzle_diameter.get_at(static_cast( + object_config->support_material_extruder - 1)); auto support_material_height = (max_object_layer_height, (nozzle_diameter * 0.75)); coordf_t _contact_distance = this->contact_distance(support_material_height, nozzle_diameter); @@ -191,15 +214,21 @@ SupportMaterial::support_layers_z(vector contact_z, pair, map> SupportMaterial::contact_area(PrintObject *object) { - PrintObjectConfig conf = this->object_config; + //cout << "After " << this->object_config->support_material.value << " ," << (this->object_config->support_material ? " Support Material is enabled" : "Support Material is disabled") << endl; + + PrintObjectConfig &conf = *this->object_config; + //cout << conf.support_material.value << " " << (conf.support_material.value ? " Support Material is enabled" : "Support Material is disabled") << endl; // If user specified a custom angle threshold, convert it to radians. - float threshold_rad; - cout << conf.support_material_threshold << endl; - if (conf.support_material_threshold > 0) { + float threshold_rad = 0.0; + + if (!conf.support_material_threshold.percent) { threshold_rad = static_cast(Geometry::deg2rad( - conf.support_material_threshold.value + 1)); // +1 makes the threshold inclusive - // TODO @Samir55 add debug statetments. + conf.support_material_threshold + 1)); // +1 makes the threshold inclusive + +#ifdef SLIC3R_DEBUG + printf("Threshold angle = %d°\n", static_cast(Geometry::rad2deg(threshold_rad))); +#endif } // Build support on a build plate only? If so, then collect top surfaces into $buildplate_only_top_surfaces @@ -211,18 +240,19 @@ SupportMaterial::contact_area(PrintObject *object) Polygons buildplate_only_top_surfaces; // Determine contact areas. - map contact; + map contact; // contact_z => [ polygons ]. map overhang; // This stores the actual overhang supported by each contact layer - for (int layer_id = 0; layer_id < object->layers.size(); layer_id++) { // Note $layer_id might != $layer->id when raft_layers > 0 // so $layer_id == 0 means first object layer // and $layer->id == 0 means first print layer (including raft). // If no raft, and we're at layer 0, skip to layer 1 - if (conf.raft_layers == 0 && layer_id == 0) + cout << "LAYER ID " << layer_id << endl; + if (conf.raft_layers == 0 && layer_id == 0) { continue; - + } + //cout << conf.support_material.value << " " << (conf.support_material.value ? " Support Material is enabled" : "Support Material is disabled") << endl; // With or without raft, if we're above layer 1, we need to quit // support generation if supports are disabled, or if we're at a high // enough layer that enforce-supports no longer applies. @@ -244,45 +274,44 @@ SupportMaterial::contact_area(PrintObject *object) Polygons projection_new; for (auto const ®ion : layer->regions) { SurfacesPtr top_surfaces = region->slices.filter_by_type(stTop); - for (auto polygon : p(top_surfaces)) { + for (const auto &polygon : p(top_surfaces)) { projection_new.push_back(polygon); } } if (!projection_new.empty()) { - // Merge the new top surfaces with the preceding top surfaces. + // Merge the new top surfaces with the preceding top surfaces. TODO @Ask about this line. // Apply the safety offset to the newly added polygons, so they will connect // with the polygons collected before, // but don't apply the safety offset during the union operation as it would // inflate the polygons over and over. - Polygons polygons = offset(projection_new, scale_(0.01)); - append_polygons(buildplate_only_top_surfaces, polygons); + //cout << "Old Build plate only surfaces " << buildplate_only_top_surfaces.size() << endl; + append_to(buildplate_only_top_surfaces, offset(projection_new, scale_(0.01))); + //cout << "New Build plate only surfaces " << buildplate_only_top_surfaces.size() << endl; buildplate_only_top_surfaces = union_(buildplate_only_top_surfaces, 0); } } // Detect overhangs and contact areas needed to support them. - Polygons m_overhang, m_contact; + Polygons tmp_overhang, tmp_contact; if (layer_id == 0) { // this is the first object layer, so we're here just to get the object // footprint for the raft. // we only consider contours and discard holes to get a more continuous raft. - for (auto const &contour : layer->slices.contours()) { - auto contour_clone = contour; - m_overhang.push_back(contour_clone); - } + for (auto const &contour : layer->slices.contours()) + tmp_overhang.push_back(contour); - Polygons polygons = offset(m_overhang, scale_(+SUPPORT_MATERIAL_MARGIN)); - append_polygons(m_contact, polygons); + Polygons polygons = offset(tmp_overhang, scale_(+SUPPORT_MATERIAL_MARGIN)); + append_to(tmp_contact, polygons); } else { Layer *lower_layer = object->get_layer(layer_id - 1); - for (auto layerm : layer->regions) { - auto fw = layerm->flow(frExternalPerimeter).scaled_width(); + for (auto layer_m : layer->regions) { + auto fw = layer_m->flow(frExternalPerimeter).scaled_width(); Polygons difference; // If a threshold angle was specified, use a different logic for detecting overhangs. - if ((conf.support_material && threshold_rad > 0) + if ((conf.support_material && threshold_rad != 0.0) || layer_id <= conf.support_material_enforce_layers || (conf.raft_layers > 0 && layer_id == 0)) { // TODO ASK @Samir why layer_id ==0 check , layer_id will never equal to zero @@ -294,12 +323,11 @@ SupportMaterial::contact_area(PrintObject *object) } if (layer_threshold_rad > 0) { d = scale_(lower_layer->height - * ((cos(layer_threshold_rad)) / (sin(layer_threshold_rad)))); + * (cos(layer_threshold_rad) / sin(layer_threshold_rad))); } - // TODO Check. difference = diff( - Polygons(layerm->slices), + Polygons(layer_m->slices), offset(lower_layer->slices, +d) ); @@ -307,15 +335,15 @@ SupportMaterial::contact_area(PrintObject *object) // is not too high: in that case, $d will be very small (as we need to catch // very short overhangs), and such contact area would be eaten by the // enforced spacing, resulting in high threshold angles to be almost ignored - if (d > fw / 2) { + if (d > fw / 2) difference = diff( offset(difference, d - fw / 2), lower_layer->slices); - } + } else { difference = diff( - Polygons(layerm->slices), + Polygons(layer_m->slices), offset(lower_layer->slices, static_cast(+conf.get_abs_value("support_material_threshold", fw))) ); @@ -332,7 +360,7 @@ SupportMaterial::contact_area(PrintObject *object) // Compute the area of bridging perimeters. Polygons bridged_perimeters; { - auto bridge_flow = layerm->flow(FlowRole::frPerimeter, 1); + auto bridge_flow = layer_m->flow(FlowRole::frPerimeter, 1); // Get the lower layer's slices and grow them by half the nozzle diameter // because we will consider the upper perimeters supported even if half nozzle @@ -340,7 +368,7 @@ SupportMaterial::contact_area(PrintObject *object) Polygons lower_grown_slices; { coordf_t nozzle_diameter = this->config->nozzle_diameter - .get_at(static_cast(layerm->region()->config.perimeter_extruder - 1)); + .get_at(static_cast(layer_m->region()->config.perimeter_extruder - 1)); lower_grown_slices = offset( lower_layer->slices, @@ -348,26 +376,29 @@ SupportMaterial::contact_area(PrintObject *object) ); } + // TODO Revise Here. // Get all perimeters as polylines. // TODO: split_at_first_point() (called by as_polyline() for ExtrusionLoops) // could split a bridge mid-way. Polylines overhang_perimeters; - overhang_perimeters.push_back(layerm->perimeters.flatten().as_polyline()); + for (auto extr_path : ExtrusionPaths(layer_m->perimeters.flatten())) { + overhang_perimeters.push_back(extr_path.as_polyline()); + } // Only consider the overhang parts of such perimeters, // overhangs being those parts not supported by // workaround for Clipper bug, see Slic3r::Polygon::clip_as_polyline() - overhang_perimeters[0].translate(1, 0); + for (auto &overhang_perimeter : overhang_perimeters) + overhang_perimeter.translate(1, 0); overhang_perimeters = diff_pl(overhang_perimeters, lower_grown_slices); // Only consider straight overhangs. Polylines new_overhangs_perimeters_polylines; - for (auto p : overhang_perimeters) + for (const auto &p : overhang_perimeters) if (p.is_straight()) new_overhangs_perimeters_polylines.push_back(p); overhang_perimeters = new_overhangs_perimeters_polylines; - new_overhangs_perimeters_polylines = Polylines(); // Only consider overhangs having endpoints inside layer's slices for (auto &p : overhang_perimeters) { @@ -375,7 +406,8 @@ SupportMaterial::contact_area(PrintObject *object) p.extend_end(fw); } - for (auto p : overhang_perimeters) { + new_overhangs_perimeters_polylines = Polylines(); + for (const auto &p : overhang_perimeters) { if (layer->slices.contains_b(p.first_point()) && layer->slices.contains_b(p.last_point())) { new_overhangs_perimeters_polylines.push_back(p); @@ -392,56 +424,45 @@ SupportMaterial::contact_area(PrintObject *object) coord_t widths[] = {bridge_flow.scaled_width(), bridge_flow.scaled_spacing(), fw, - layerm->flow(FlowRole::frPerimeter).scaled_width()}; + layer_m->flow(FlowRole::frPerimeter).scaled_width()}; auto w = *max_element(widths, widths + 4); // Also apply safety offset to ensure no gaps are left in between. - // TODO CHeck this. - for (auto &p : overhang_perimeters) { - Polygons ps = union_(offset(p, w / 2 + 10)); - for (auto ps_el : ps) - bridged_perimeters.push_back(ps_el); - } + Polygons ps = offset(overhang_perimeters, w / 2 + 10); + bridged_perimeters = union_(ps); } } if (1) { // Remove the entire bridges and only support the unsupported edges. ExPolygons bridges; - for (auto surface : layerm->fill_surfaces.filter_by_type(stBottomBridge)) { + for (auto surface : layer_m->fill_surfaces.filter_by_type(stBottomBridge)) { if (surface->bridge_angle != -1) { bridges.push_back(surface->expolygon); } } - ExPolygons ps; - for (auto p : bridged_perimeters) - ps.push_back(ExPolygon(p)); - for (auto p : bridges) - ps.push_back(p); + Polygons ps = to_polygons(bridges); + append_to(ps, bridged_perimeters); - // TODO ASK about this. - difference = diff( + difference = diff( // TODO ASK about expolygons and polygons miss match. difference, - to_polygons(ps), + ps, true ); - // TODO Ask about this. - auto p_intersections = intersection(offset(layerm->unsupported_bridge_edges.polylines, - +scale_(SUPPORT_MATERIAL_MARGIN)), - to_polygons(bridges)); - for (auto p: p_intersections) { - difference.push_back(p); - } + append_to(difference, + intersection( + offset(layer_m->unsupported_bridge_edges.polylines, + +scale_(SUPPORT_MATERIAL_MARGIN)), to_polygons(bridges))); } else { // just remove bridged areas. difference = diff( difference, - layerm->bridged, - 1 + layer_m->bridged, + true ); } } // if ($conf->dont_support_bridges) @@ -455,7 +476,7 @@ SupportMaterial::contact_area(PrintObject *object) if (difference.empty()) continue; // NOTE: this is not the full overhang as it misses the outermost half of the perimeter width! - append_polygons(m_overhang, difference); + append_polygons(tmp_overhang, difference); // 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. @@ -466,25 +487,24 @@ SupportMaterial::contact_area(PrintObject *object) if (buildplate_only) { // Trim the inflated contact surfaces by the top surfaces as well. - append_polygons(slices_margin, buildplate_only_top_surfaces); + append_to(slices_margin, buildplate_only_top_surfaces); slices_margin = union_(slices_margin); } - // TODO Ask how to port this. - /* - for ($fw/2, map {scale MARGIN_STEP} 1..(MARGIN / MARGIN_STEP)) { - $diff = diff( - offset($diff, $_), - $slices_margin, + vector scale_vector + (static_cast(SUPPORT_MATERIAL_MARGIN / MARGIN_STEP), scale_(MARGIN_STEP)); + scale_vector.push_back(fw / 2); + for (int i = static_cast(scale_vector.size()) - 1; i >= 0; i--) { + difference = diff( + offset(difference, i), + slices_margin ); - } - */ + } } - - append_polygons(m_contact, difference); + append_polygons(tmp_contact, difference); } } - if (contact.empty()) + if (tmp_contact.empty()) continue; // Now apply the contact areas to the layer were they need to be made. @@ -503,7 +523,7 @@ SupportMaterial::contact_area(PrintObject *object) .solid_infill_extruder - 1))); } - int nozzle_diameters_count = static_cast(nozzle_diameters.size() > 0 ? nozzle_diameters.size() : 1); + int nozzle_diameters_count = static_cast(!nozzle_diameters.empty() ? nozzle_diameters.size() : 1); auto nozzle_diameter = accumulate(nozzle_diameters.begin(), nozzle_diameters.end(), 0.0) / nozzle_diameters_count; @@ -512,10 +532,10 @@ SupportMaterial::contact_area(PrintObject *object) // Ignore this contact area if it's too low. if (contact_z < conf.first_layer_height - EPSILON) continue; +//cout << contact_z << " " << tmp_contact.size() << endl; - - contact[contact_z] = m_contact; - overhang[contact_z] = m_overhang; + contact[contact_z] = tmp_contact; + overhang[contact_z] = tmp_overhang; } } @@ -532,46 +552,42 @@ SupportMaterial::object_top(PrintObject *object, map *contac return top; Polygons projection; - for (auto i = static_cast(object->layers.size() - 1); i >= 0; i--) { + for (auto i = static_cast(object->layers.size()) - 1; i >= 0; i--) { Layer *layer = object->layers[i]; SurfacesPtr m_top; for (auto r : layer->regions) - for (auto s : r->slices.filter_by_type(stTop)) - m_top.push_back(s); + append_to(m_top, r->slices.filter_by_type(stTop)); - if (!m_top.empty()) { - // compute projection of the contact areas above this top layer - // first add all the 'new' contact areas to the current projection - // ('new' means all the areas that are lower than the last top layer - // we considered). - // TODO Ask about this line - /* - my $min_top = min(keys %top) // max(keys %$contact); - */ - double min_top = top.begin()->first; + if (m_top.empty()) continue; - // Use <= instead of just < because otherwise we'd ignore any contact regions - // having the same Z of top layers. - for (auto el : *contact) - if (el.first > layer->print_z && el.first <= min_top) - for (const auto &p : el.second) - projection.push_back(p); + // compute projection of the contact areas above this top layer + // first add all the 'new' contact areas to the current projection + // ('new' means all the areas that are lower than the last top layer + // we considered). + double min_top = (!top.empty() ? top.begin()->first : contact->rbegin()->first); - // Now find whether any projection falls onto this top surface. - Polygons touching = intersection(projection, p(m_top)); - if (!touching.empty()) { - // Grow top surfaces so that interface and support generation are generated - // with some spacing from object - it looks we don't need the actual - // top shapes so this can be done here. - top[layer->print_z] = offset(touching, flow->scaled_width()); - } + // Use <= instead of just < because otherwise we'd ignore any contact regions + // having the same Z of top layers. + for (auto el : *contact) + if (el.first > layer->print_z && el.first <= min_top) + for (const auto &p : el.second) + projection.push_back(p); - // Remove the areas that touched from the projection that will continue on - // next, lower, top surfaces. - projection = diff(projection, touching); + // Now find whether any projection falls onto this top surface. + Polygons touching = intersection(projection, p(m_top)); + if (!touching.empty()) { + // Grow top surfaces so that interface and support generation are generated + // with some spacing from object - it looks we don't need the actual + // top shapes so this can be done here. + top[layer->print_z] = offset(touching, flow.scaled_width()); } + + // Remove the areas that touched from the projection that will continue on + // next, lower, top surfaces. + projection = diff(projection, touching); + } return top; } @@ -584,9 +600,13 @@ SupportMaterial::generate_pillars_shape(const map &contact, // This prevents supplying an empty point set to BoundingBox constructor. if (contact.empty()) return; + int u = 0; + cout << "Pillars generation " << contact.size() << endl; + coord_t pillar_size = scale_(object_config->support_material_pillar_size.value); coord_t pillar_spacing = scale_(object_config->support_material_pillar_spacing.value); + cout << "Samir U " << u++ << endl; Polygons grid; { auto pillar = Polygon({ @@ -615,21 +635,28 @@ SupportMaterial::generate_pillars_shape(const map &contact, grid = union_(pillars); } - + cout << "Samir U " << u++ << endl; //1 + cout << "Support z size " << support_z.size() << endl; // Add pillars to every layer. for (auto i = 0; i < support_z.size(); i++) { shape[i] = grid; } - + cout << "Samir U " << u++ << endl; // Build capitals. + cout << "contacts START " << endl; + for (auto el : contact) { + cout << el.first << endl; + } + cout << "contacts END" << endl; for (auto i = 0; i < support_z.size(); i++) { coordf_t z = support_z[i]; + cout << z << endl; auto capitals = intersection( grid, - contact.at(z) + contact.count(z) > 0 ? contact.at(z) : Polygons() ); - + cout << "Samir U " << u++ << endl; // 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 // sure nothing is left. @@ -641,17 +668,17 @@ SupportMaterial::generate_pillars_shape(const map &contact, for (int j = i - 1; j >= 0; j--) { auto jz = support_z[j]; - capital_polygons = offset(Polygons{capital}, -interface_flow->scaled_width() / 2); + capital_polygons = offset(Polygons{capital}, -interface_flow.scaled_width() / 2); if (capitals.empty()) break; append_to(shape[i], capital_polygons); } } - + cout << "Samir U " << u++ << endl; // 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 // sure nothing is left. auto contact_not_supported_by_capitals = diff( - contact.at(z), + contact.count(z) > 0 ? contact.at(z) : Polygons(), contact_supported_by_capitals ); @@ -661,6 +688,7 @@ SupportMaterial::generate_pillars_shape(const map &contact, } } } + cout << "Samir U " << u++ << endl; } map @@ -672,7 +700,7 @@ SupportMaterial::generate_base_layers(vector support_z, // Let's now generate support layers under interface layers. map base; { - for (int i = static_cast(support_z.size() - 1); i >= 0; i--) { + for (auto i = static_cast(support_z.size()) - 1; i >= 0; i--) { auto z = support_z[i]; auto overlapping_layers = this->overlapping_layers(i, support_z); vector overlapping_z; @@ -683,7 +711,7 @@ SupportMaterial::generate_base_layers(vector support_z, // (1 interface layer means we only have contact layer, so $interface->{$i+1} is empty). Polygons upper_contact; if (object_config->support_material_interface_layers.value <= 1) { - append_polygons(upper_contact, contact[support_z[i + 1]]); + append_polygons(upper_contact, (i + 1 < support_z.size() ? contact[support_z[i + 1]] : contact[-1])); } Polygons ps_1; @@ -723,6 +751,7 @@ SupportMaterial::generate_interface_layers(vector support_z, for (int layer_id = 0; layer_id < support_z.size(); layer_id++) { // Todo Ask about how to port this line. "my $this = $contact->{$z} // next;" auto z = support_z[layer_id]; + if (contact.count(z) <= 0) continue; Polygons &_contact = contact[z]; @@ -773,7 +802,7 @@ SupportMaterial::generate_bottom_interface_layers(const vector &suppor if (object_config->support_material_interface_layers.value == 0) return; - auto area_threshold = interface_flow->scaled_spacing() * interface_flow->scaled_spacing(); + auto area_threshold = interface_flow.scaled_spacing() * interface_flow.scaled_spacing(); // Loop through object's top surfaces. TODO CHeck if the keys are sorted. for (auto &top_el : top) { @@ -895,12 +924,12 @@ SupportMaterial::clip_with_object(map &support, vector slices.push_back(s); } } - support_layer.second = diff(support_layer.second, offset(slices, flow->scaled_width())); + support_layer.second = diff(support_layer.second, offset(slices, flow.scaled_width())); } /* $support->{$i} = diff( $support->{$i}, - offset([ map @$_, map @{$_->slices}, @layers ], +$self->flow->scaled_width), + offset([ map @$_, map @{$_->slices}, @layers ], +$self->flow.scaled_width), ); */ } @@ -912,8 +941,8 @@ SupportMaterial::process_layer(int layer_id, toolpaths_params params) coordf_t z = layer->print_z; // We redefine flows locally by applyinh this layer's height. - auto _flow = *flow; - auto _interface_flow = *interface_flow; + Flow _flow = flow; + Flow _interface_flow = interface_flow; _flow.height = static_cast(layer->height); _interface_flow.height = static_cast(layer->height); @@ -1104,9 +1133,9 @@ SupportMaterial::process_layer(int layer_id, toolpaths_params params) Fill *filler = fillers["support"]; filler->angle = static_cast(Geometry::deg2rad(params.angles[layer_id % int(params.angles.size())])); - // We don't use $base_flow->spacing because we need a constant spacing + // We don't use $base_flow.spacing because we need a constant spacing // value that guarantees that all layers are correctly aligned. - filler->min_spacing = flow->spacing(); + filler->min_spacing = flow.spacing(); auto density = params.support_density; Flow *base_flow = &_flow; @@ -1122,7 +1151,7 @@ SupportMaterial::process_layer(int layer_id, toolpaths_params params) filler = fillers["interface"]; filler->angle = static_cast(Geometry::deg2rad(object_config->support_material_angle.value + 90)); density = 0.5; - base_flow = first_layer_flow; + base_flow = &first_layer_flow; // Use the proper spacing for first layer as we don't need to align // its pattern to the other layers. diff --git a/xs/src/libslic3r/SupportMaterial.hpp b/xs/src/libslic3r/SupportMaterial.hpp index 923dadc26..15c2fc1e6 100644 --- a/xs/src/libslic3r/SupportMaterial.hpp +++ b/xs/src/libslic3r/SupportMaterial.hpp @@ -24,6 +24,8 @@ namespace Slic3r // how much we extend support around the actual contact area constexpr coordf_t SUPPORT_MATERIAL_MARGIN = 1.5; +constexpr coordf_t MARGIN_STEP = SUPPORT_MATERIAL_MARGIN / 3; + constexpr coordf_t PILLAR_SIZE = 2.5; constexpr coordf_t PILLAR_SPACING = 10; @@ -61,24 +63,13 @@ struct toolpaths_params class SupportMaterial { public: + friend PrintObject; + PrintConfig *config; ///< The print config PrintObjectConfig *object_config; ///< The object print config. - Flow *flow; ///< The intermediate layers print flow. - Flow *first_layer_flow; ///< The first (base) layers print flow. - Flow *interface_flow; ///< The interface layers print flow. - - SupportMaterial(PrintConfig *print_config, - PrintObjectConfig *print_object_config, - Flow *flow = nullptr, - Flow *first_layer_flow = nullptr, - Flow *interface_flow = nullptr) - : config(print_config), - object_config(print_object_config), - flow(flow), - first_layer_flow(first_layer_flow), - interface_flow(interface_flow), - object(nullptr) - {} + Flow flow; ///< The intermediate layers print flow. + Flow first_layer_flow; ///< The first (base) layers print flow. + Flow interface_flow; ///< The interface layers print flow. /// Generate the extrusions paths for the support matterial generated for the given print object. void generate_toolpaths(PrintObject *object, @@ -97,7 +88,7 @@ public: pair, map> contact_area(PrintObject *object); - // Is this expolygons or polygons? + // TODO Is this expolygons or polygons? map object_top(PrintObject *object, map *contact); void generate_pillars_shape(const map &contact, @@ -133,6 +124,20 @@ public: void process_layer(int layer_id, toolpaths_params params); private: + /// SupportMaterial is generated by PrintObject. + SupportMaterial(PrintConfig *print_config, + PrintObjectConfig *print_object_config, + Flow flow, + Flow first_layer_flow, + Flow interface_flow) + : config(print_config), + object_config(print_object_config), + flow(Flow(0, 0, 0)), + first_layer_flow(Flow(0, 0, 0)), + interface_flow(Flow(0, 0, 0)), + object(nullptr) + {} + // Get the maximum layer height given a print object. coordf_t get_max_layer_height(PrintObject *object); From 49fbe989109843ad3a2cbc25bcd32a65ec4dfcd3 Mon Sep 17 00:00:00 2001 From: Samir55 Date: Sun, 15 Jul 2018 03:42:50 +0200 Subject: [PATCH 28/30] Fixing bugs. --- src/test/libslic3r/test_support_material.cpp | 209 +++++-- t/support.t | 560 +++++++++---------- xs/src/libslic3r/SupportMaterial.cpp | 62 +- xs/src/libslic3r/SupportMaterial.hpp | 1 - 4 files changed, 463 insertions(+), 369 deletions(-) diff --git a/src/test/libslic3r/test_support_material.cpp b/src/test/libslic3r/test_support_material.cpp index 13fbd6d3e..0222ffa07 100644 --- a/src/test/libslic3r/test_support_material.cpp +++ b/src/test/libslic3r/test_support_material.cpp @@ -10,6 +10,8 @@ using namespace std; using namespace Slic3r; +void test_1_check(Print &print, bool &a, bool &b, bool &c, bool &d); + // Testing supports material member functions. TEST_CASE("", "") { @@ -42,6 +44,41 @@ TEST_CASE("", "") } +TEST_CASE("SupportMaterial: forced support is generated", "") +{ + // Create a mesh & modelObject. + TriangleMesh mesh = TriangleMesh::make_cube(20, 20, 20); + + Model model = Model(); + ModelObject *object = model.add_object(); + object->add_volume(mesh); + model.add_default_instances(); + model.align_instances_to_origin(); + + Print print = Print(); + + vector contact_z = {1.9}; + vector top_z = {1.1}; + print.default_object_config.support_material_enforce_layers = 100; + print.default_object_config.support_material = 0; + print.default_object_config.layer_height = 0.2; + print.default_object_config.set_deserialize("first_layer_height", "0.3"); + + print.add_model_object(model.objects[0]); + print.objects.front()->_slice(); + + SupportMaterial *support = print.objects.front()->_support_material(); + auto support_z = support->support_layers_z(contact_z, top_z, print.default_object_config.layer_height); + + bool check = true; + for (int i = 1; i < support_z.size(); i++) { + if (support_z[i] - support_z[i - 1] <= 0) + check = false; + } + + REQUIRE(check == true); +} + SCENARIO("SupportMaterial: support_layers_z and contact_distance") { GIVEN("A print object having one modelObject") { @@ -57,76 +94,136 @@ SCENARIO("SupportMaterial: support_layers_z and contact_distance") // Align to origin. model.align_instances_to_origin(); - // Create Print. - Print print = Print(); - print.default_object_config.set_deserialize("support_material", "1"); - - vector contact_z = {1.9}; - vector top_z = {1.1}; - - WHEN("Layer height = 0.2 and, first layer height = 0.3") { - + WHEN("First layer height = 0.4") { + // Create Print. + Print print = Print(); + print.default_object_config.set_deserialize("support_material", "1"); print.default_object_config.set_deserialize("layer_height", "0.2"); - print.default_object_config.set_deserialize("first_layer_height", "0.3"); + print.default_object_config.set_deserialize("first_layer_height", "0.4"); print.add_model_object(model.objects[0]); print.objects.front()->_slice(); + bool a, b, c, d; - SupportMaterial *support = print.objects.front()->_support_material(); - - vector - support_z = support->support_layers_z(contact_z, top_z, print.default_object_config.layer_height.value); - + test_1_check(print, a, b, c, d); THEN("First layer height is honored") { - REQUIRE((support_z[0] == print.default_object_config.first_layer_height.value)); + REQUIRE(a == true); } - THEN("No null or negative support layers") { - bool check = true; - for (size_t i = 1; i < support_z.size(); ++i) - if (support_z[i] - support_z[i - 1] <= 0) check = false; - REQUIRE(check == true); + REQUIRE(b == true); } - THEN("No layers thicker than nozzle diameter") { - bool check = true; - for (size_t i = 1; i < support_z.size(); ++i) - if (support_z[i] - support_z[i - 1] > print.config.nozzle_diameter.get_at(0) + EPSILON) - check = false; - REQUIRE(check == true); + REQUIRE(c == true); } - THEN("Layers above top surfaces are spaced correctly") { - coordf_t expected_top_spacing = support - ->contact_distance(print.default_object_config.layer_height, - print.config.nozzle_diameter.get_at(0)); - - bool wrong_top_spacing = 0; - for (coordf_t top_z_el : top_z) { - // find layer index of this top surface. - size_t layer_id = -1; - for (size_t i = 0; i < support_z.size(); i++) { - if (abs(support_z[i] - top_z_el) < EPSILON) { - layer_id = i; - i = static_cast(support_z.size()); - } - } - - // check that first support layer above this top surface (or the next one) is spaced with nozzle diameter - if (abs(support_z[layer_id + 1] - support_z[layer_id] - expected_top_spacing) > EPSILON - && abs(support_z[layer_id + 2] - support_z[layer_id] - expected_top_spacing) > EPSILON) { - wrong_top_spacing = 1; - } - REQUIRE(!wrong_top_spacing); - } + REQUIRE(d == true); } } -// /* Test Also with this -// $config->set('first_layer_height', 0.4); -// $test->(); +// WHEN("Layer height = 0.2 and, first layer height = 0.3") { +// print.default_object_config.set_deserialize("layer_height", "0.2"); +// print.default_object_config.set_deserialize("first_layer_height", "0.3"); +// print.add_model_object(model.objects[0]); +// print.objects.front()->_slice(); +// bool a, b, c, d; // -// $config->set('layer_height', $config->nozzle_diameter->[0]); -// $test->(); -// */ +// test_1_check(print, a, b, c, d); +// THEN("First layer height is honored") { +// REQUIRE(a == true); +// } +// THEN("No null or negative support layers") { +// REQUIRE(b == true); +// } +// THEN("No layers thicker than nozzle diameter") { +// REQUIRE(c == true); +// } +// THEN("Layers above top surfaces are spaced correctly") { +// REQUIRE(d == true); +// } +// } +// +// +// WHEN("Layer height = nozzle_diameter[0]") { +// print.default_object_config.set_deserialize("layer_height", "0.2"); +// print.default_object_config.set_deserialize("first_layer_height", "0.3"); +// print.add_model_object(model.objects[0]); +// print.objects.front()->_slice(); +// bool a, b, c, d; +// +// test_1_check(print, a, b, c, d); +// THEN("First layer height is honored") { +// REQUIRE(a == true); +// } +// THEN("No null or negative support layers") { +// REQUIRE(b == true); +// } +// THEN("No layers thicker than nozzle diameter") { +// REQUIRE(c == true); +// } +// THEN("Layers above top surfaces are spaced correctly") { +// REQUIRE(d == true); +// } +// } } } + +SCENARIO("SupportMaterial: Checking bridge speed") { + // Create a mesh & modelObject. + TriangleMesh mesh = TriangleMesh::make_cube(20, 20, 20); + + Model model = Model(); + ModelObject *object = model.add_object(); + object->add_volume(mesh); + model.add_default_instances(); + model.align_instances_to_origin(); + + Print print = Print(); +} + +void test_1_check(Print &print, bool &a, bool &b, bool &c, bool &d) +{ + vector contact_z = {1.9}; + vector top_z = {1.1}; + + SupportMaterial *support = print.objects.front()->_support_material(); + + vector + support_z = support->support_layers_z(contact_z, top_z, print.default_object_config.layer_height); +for(auto el: support_z) + cout << el << endl; + cout << endl; + + a = (support_z[0] == print.default_object_config.first_layer_height.value); + + b = true; + for (size_t i = 1; i < support_z.size(); ++i) + if (support_z[i] - support_z[i - 1] <= 0) b = false; + + + c = true; + for (size_t i = 1; i < support_z.size(); ++i) + if (support_z[i] - support_z[i - 1] > print.config.nozzle_diameter.get_at(0) + EPSILON) + c = false; + + coordf_t expected_top_spacing = support + ->contact_distance(print.default_object_config.layer_height, + print.config.nozzle_diameter.get_at(0)); + + bool wrong_top_spacing = 0; + for (coordf_t top_z_el : top_z) { + // find layer index of this top surface. + size_t layer_id = -1; + for (size_t i = 0; i < support_z.size(); i++) { + if (abs(support_z[i] - top_z_el) < EPSILON) { + layer_id = i; + i = static_cast(support_z.size()); + } + } + + // check that first support layer above this top surface (or the next one) is spaced with nozzle diameter + if (abs(support_z[layer_id + 1] - support_z[layer_id] - expected_top_spacing) > EPSILON + && abs(support_z[layer_id + 2] - support_z[layer_id] - expected_top_spacing) > EPSILON) { + wrong_top_spacing = 1; + } + } + d = !wrong_top_spacing; +} diff --git a/t/support.t b/t/support.t index 50f4d53fa..61d1416fa 100644 --- a/t/support.t +++ b/t/support.t @@ -1,4 +1,4 @@ -use Test::More tests => 29; +use Test::More tests => 12; use strict; use warnings; @@ -7,7 +7,7 @@ BEGIN { use lib "$FindBin::Bin/../lib"; use local::lib "$FindBin::Bin/../local-lib"; } - +use Data::Dumper; use List::Util qw(first); use Slic3r; use Slic3r::Geometry qw(epsilon scale PI); @@ -31,7 +31,7 @@ use Slic3r::Test; ); my $support_z = $support->support_layers_z(\@contact_z, \@top_z, $config->layer_height); my $expected_top_spacing = $support->contact_distance($config->layer_height, $config->nozzle_diameter->[0]); - + diag(Dumper($support_z)); is $support_z->[0], $config->first_layer_height, 'first layer height is honored'; is scalar(grep { $support_z->[$_]-$support_z->[$_-1] <= 0 } 1..$#$support_z), 0, @@ -64,281 +64,281 @@ use Slic3r::Test; $config->set('layer_height', $config->nozzle_diameter->[0]); $test->(); } - -{ - my $config = Slic3r::Config->new_from_defaults; - $config->set('raft_layers', 3); - $config->set('brim_width', 0); - $config->set('skirts', 0); - $config->set('support_material_extruder', 2); - $config->set('support_material_interface_extruder', 2); - $config->set('layer_height', 0.4); - $config->set('first_layer_height', 0.4); - my $print = Slic3r::Test::init_print('overhang', config => $config); - ok my $gcode = Slic3r::Test::gcode($print), 'no conflict between raft/support and brim'; - - my $tool = 0; - Slic3r::GCode::Reader->new->parse($gcode, sub { - my ($self, $cmd, $args, $info) = @_; - - if ($cmd =~ /^T(\d+)/) { - $tool = $1; - } elsif ($info->{extruding}) { - if ($self->Z <= ($config->raft_layers * $config->layer_height)) { - fail 'not extruding raft with support material extruder' - if $tool != ($config->support_material_extruder-1); - } else { - fail 'support material exceeds raft layers' - if $tool == $config->support_material_extruder-1; - # TODO: we should test that full support is generated when we use raft too - } - } - }); -} - -{ - my $config = Slic3r::Config->new_from_defaults; - $config->set('skirts', 0); - $config->set('raft_layers', 3); - $config->set('support_material_pattern', 'honeycomb'); - $config->set('support_material_extrusion_width', 0.6); - $config->set('first_layer_extrusion_width', '100%'); - $config->set('bridge_speed', 99); - $config->set('cooling', 0); # prevent speed alteration - $config->set('first_layer_speed', '100%'); # prevent speed alteration - $config->set('start_gcode', ''); # prevent any unexpected Z move - my $print = Slic3r::Test::init_print('20mm_cube', config => $config); - - my $layer_id = -1; # so that first Z move sets this to 0 - my @raft = my @first_object_layer = (); - my %first_object_layer_speeds = (); # F => 1 - Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { - my ($self, $cmd, $args, $info) = @_; - if ($info->{extruding} && $info->{dist_XY} > 0) { - if ($layer_id <= $config->raft_layers) { - # this is a raft layer or the first object layer - my $line = Slic3r::Line->new_scale([ $self->X, $self->Y ], [ $info->{new_X}, $info->{new_Y} ]); - my @path = @{$line->grow(scale($config->support_material_extrusion_width/2))}; - if ($layer_id < $config->raft_layers) { - # this is a raft layer - push @raft, @path; - } else { - push @first_object_layer, @path; - $first_object_layer_speeds{ $args->{F} // $self->F } = 1; - } - } - } elsif ($cmd eq 'G1' && $info->{dist_Z} > 0) { - $layer_id++; - } - }); - - ok !@{diff(\@first_object_layer, \@raft)}, - 'first object layer is completely supported by raft'; - is scalar(keys %first_object_layer_speeds), 1, - 'only one speed used in first object layer'; - ok +(keys %first_object_layer_speeds)[0] == $config->bridge_speed*60, - 'bridge speed used in first object layer'; -} - -{ - my $config = Slic3r::Config->new_from_defaults; - $config->set('layer_height', 0.2); - $config->set('skirts', 0); - $config->set('raft_layers', 5); - $config->set('support_material_pattern', 'rectilinear'); - $config->set('support_material_extrusion_width', 0.4); - $config->set('support_material_interface_extrusion_width', 0.6); - $config->set('support_material_interface_layers', 2); - $config->set('first_layer_extrusion_width', '100%'); - $config->set('bridge_speed', 99); - $config->set('cooling', 0); # prevent speed alteration - $config->set('first_layer_speed', '100%'); # prevent speed alteration - $config->set('start_gcode', ''); # prevent any unexpected Z move - my $print = Slic3r::Test::init_print('20mm_cube', config => $config); - - my $layer_id = -1; - my $success = 1; - Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { - my ($self, $cmd, $args, $info) = @_; - if ($info->{extruding} && $info->{dist_XY} > 0) { - # this is a raft layer - if (($layer_id < $config->raft_layers) && ($layer_id > 0)) { - my $width; - my $support_layer_height = $config->nozzle_diameter->[0] * 0.75; - - # support layer - if ($config->raft_layers - $config->support_material_interface_layers > $layer_id) { - $width = $config->support_material_extrusion_width; - } - # interface layer - else { - $width = $config->support_material_interface_extrusion_width; - } - my $expected_E_per_mm3 = 4 / (($config->filament_diameter->[0]**2) * PI); - my $expected_mm3_per_mm = $width * $support_layer_height + ($support_layer_height**2) / 4.0 * (PI-4.0); - my $expected_e_per_mm = $expected_E_per_mm3 * $expected_mm3_per_mm; - - my $e_per_mm = ($info->{dist_E} / $info->{dist_XY});; - - my $diff = abs($e_per_mm - $expected_e_per_mm); - - if ($diff > 0.001) { - $success = 0; - } - } - } elsif ($cmd eq 'G1' && $info->{dist_Z} > 0) { - $layer_id++; - } - }); - - ok $success, - 'support material interface extrusion width is used for interfaces'; -} - -{ - my $config = Slic3r::Config->new_from_defaults; - $config->set('skirts', 0); - $config->set('layer_height', 0.35); - $config->set('first_layer_height', 0.3); - $config->set('nozzle_diameter', [0.5]); - $config->set('support_material_extruder', 2); - $config->set('support_material_interface_extruder', 2); - - my $test = sub { - my ($raft_layers) = @_; - $config->set('raft_layers', $raft_layers); - my $print = Slic3r::Test::init_print('20mm_cube', config => $config); - my %raft_z = (); # z => 1 - my $tool = undef; - Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { - my ($self, $cmd, $args, $info) = @_; - - if ($cmd =~ /^T(\d+)/) { - $tool = $1; - } elsif ($info->{extruding} && $info->{dist_XY} > 0) { - if ($tool == $config->support_material_extruder-1) { - $raft_z{$self->Z} = 1; - } - } - }); - - is scalar(keys %raft_z), $config->raft_layers, 'correct number of raft layers is generated'; - }; - - $test->(2); - $test->(70); - - $config->set('layer_height', 0.4); - $config->set('first_layer_height', 0.35); - $test->(3); - $test->(70); -} - -{ - my $config = Slic3r::Config->new_from_defaults; - $config->set('brim_width', 0); - $config->set('skirts', 0); - $config->set('support_material', 1); - $config->set('top_solid_layers', 0); # so that we don't have the internal bridge over infill - $config->set('bridge_speed', 99); - $config->set('cooling', 0); - $config->set('first_layer_speed', '100%'); - - my $test = sub { - my $print = Slic3r::Test::init_print('overhang', config => $config); - - my $has_bridge_speed = 0; - Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { - my ($self, $cmd, $args, $info) = @_; - - if ($info->{extruding}) { - if (($args->{F} // $self->F) == $config->bridge_speed*60) { - $has_bridge_speed = 1; - } - } - }); - return $has_bridge_speed; - }; - - $config->set('support_material_contact_distance', 0.2); - ok $test->(), 'bridge speed is used when support_material_contact_distance > 0'; - - $config->set('support_material_contact_distance', 0); - ok !$test->(), 'bridge speed is not used when support_material_contact_distance == 0'; - - $config->set('raft_layers', 5); - $config->set('support_material_contact_distance', 0.2); - ok $test->(), 'bridge speed is used when raft_layers > 0 and support_material_contact_distance > 0'; - - $config->set('support_material_contact_distance', 0); - ok !$test->(), 'bridge speed is not used when raft_layers > 0 and support_material_contact_distance == 0'; -} - -{ - my $config = Slic3r::Config->new_from_defaults; - $config->set('skirts', 0); - $config->set('start_gcode', ''); - $config->set('raft_layers', 8); - $config->set('nozzle_diameter', [0.4, 1]); - $config->set('layer_height', 0.1); - $config->set('first_layer_height', 0.8); - $config->set('support_material_extruder', 2); - $config->set('support_material_interface_extruder', 2); - $config->set('support_material_contact_distance', 0); - my $print = Slic3r::Test::init_print('20mm_cube', config => $config); - ok my $gcode = Slic3r::Test::gcode($print), 'first_layer_height is validated with support material extruder nozzle diameter when using raft layers'; - - my $tool = undef; - my @z = (0); - my %layer_heights_by_tool = (); # tool => [ lh, lh... ] - Slic3r::GCode::Reader->new->parse($gcode, sub { - my ($self, $cmd, $args, $info) = @_; - - if ($cmd =~ /^T(\d+)/) { - $tool = $1; - } elsif ($cmd eq 'G1' && exists $args->{Z} && $args->{Z} != $self->Z) { - push @z, $args->{Z}; - } elsif ($info->{extruding} && $info->{dist_XY} > 0) { - $layer_heights_by_tool{$tool} ||= []; - push @{ $layer_heights_by_tool{$tool} }, $z[-1] - $z[-2]; - } - }); - - ok !defined(first { $_ > $config->nozzle_diameter->[0] + epsilon } - @{ $layer_heights_by_tool{$config->perimeter_extruder-1} }), - 'no object layer is thicker than nozzle diameter'; - - ok !defined(first { abs($_ - $config->layer_height) < epsilon } - @{ $layer_heights_by_tool{$config->support_material_extruder-1} }), - 'no support material layer is as thin as object layers'; -} - -{ - my $config = Slic3r::Config->new_from_defaults; - $config->set('support_material_enforce_layers', 100); - $config->set('support_material', 0); - my @contact_z = my @top_z = (); - - my $test = sub { - my $print = Slic3r::Test::init_print('20mm_cube', config => $config); - my $flow = $print->print->objects->[0]->support_material_flow; - my $support = Slic3r::Print::SupportMaterial->new( - object_config => $print->print->objects->[0]->config, - print_config => $print->print->config, - flow => $flow, - interface_flow => $flow, - first_layer_flow => $flow, - ); - my $support_z = $support->support_layers_z(\@contact_z, \@top_z, $config->layer_height); - - is scalar(grep { $support_z->[$_]-$support_z->[$_-1] <= 0 } 1..$#$support_z), 0, - 'forced support is generated'; - - }; - $config->set('layer_height', 0.2); - $config->set('first_layer_height', 0.3); - @contact_z = (1.9); - @top_z = (1.1); - $test->(); -} +# +#{ +# my $config = Slic3r::Config->new_from_defaults; +# $config->set('raft_layers', 3); +# $config->set('brim_width', 0); +# $config->set('skirts', 0); +# $config->set('support_material_extruder', 2); +# $config->set('support_material_interface_extruder', 2); +# $config->set('layer_height', 0.4); +# $config->set('first_layer_height', 0.4); +# my $print = Slic3r::Test::init_print('overhang', config => $config); +# ok my $gcode = Slic3r::Test::gcode($print), 'no conflict between raft/support and brim'; +# +# my $tool = 0; +# Slic3r::GCode::Reader->new->parse($gcode, sub { +# my ($self, $cmd, $args, $info) = @_; +# +# if ($cmd =~ /^T(\d+)/) { +# $tool = $1; +# } elsif ($info->{extruding}) { +# if ($self->Z <= ($config->raft_layers * $config->layer_height)) { +# fail 'not extruding raft with support material extruder' +# if $tool != ($config->support_material_extruder-1); +# } else { +# fail 'support material exceeds raft layers' +# if $tool == $config->support_material_extruder-1; +# # TODO: we should test that full support is generated when we use raft too +# } +# } +# }); +#} +# +#{ +# my $config = Slic3r::Config->new_from_defaults; +# $config->set('skirts', 0); +# $config->set('raft_layers', 3); +# $config->set('support_material_pattern', 'honeycomb'); +# $config->set('support_material_extrusion_width', 0.6); +# $config->set('first_layer_extrusion_width', '100%'); +# $config->set('bridge_speed', 99); +# $config->set('cooling', 0); # prevent speed alteration +# $config->set('first_layer_speed', '100%'); # prevent speed alteration +# $config->set('start_gcode', ''); # prevent any unexpected Z move +# my $print = Slic3r::Test::init_print('20mm_cube', config => $config); +# +# my $layer_id = -1; # so that first Z move sets this to 0 +# my @raft = my @first_object_layer = (); +# my %first_object_layer_speeds = (); # F => 1 +# Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { +# my ($self, $cmd, $args, $info) = @_; +# if ($info->{extruding} && $info->{dist_XY} > 0) { +# if ($layer_id <= $config->raft_layers) { +# # this is a raft layer or the first object layer +# my $line = Slic3r::Line->new_scale([ $self->X, $self->Y ], [ $info->{new_X}, $info->{new_Y} ]); +# my @path = @{$line->grow(scale($config->support_material_extrusion_width/2))}; +# if ($layer_id < $config->raft_layers) { +# # this is a raft layer +# push @raft, @path; +# } else { +# push @first_object_layer, @path; +# $first_object_layer_speeds{ $args->{F} // $self->F } = 1; +# } +# } +# } elsif ($cmd eq 'G1' && $info->{dist_Z} > 0) { +# $layer_id++; +# } +# }); +# +# ok !@{diff(\@first_object_layer, \@raft)}, +# 'first object layer is completely supported by raft'; +# is scalar(keys %first_object_layer_speeds), 1, +# 'only one speed used in first object layer'; +# ok +(keys %first_object_layer_speeds)[0] == $config->bridge_speed*60, +# 'bridge speed used in first object layer'; +#} +# +#{ +# my $config = Slic3r::Config->new_from_defaults; +# $config->set('layer_height', 0.2); +# $config->set('skirts', 0); +# $config->set('raft_layers', 5); +# $config->set('support_material_pattern', 'rectilinear'); +# $config->set('support_material_extrusion_width', 0.4); +# $config->set('support_material_interface_extrusion_width', 0.6); +# $config->set('support_material_interface_layers', 2); +# $config->set('first_layer_extrusion_width', '100%'); +# $config->set('bridge_speed', 99); +# $config->set('cooling', 0); # prevent speed alteration +# $config->set('first_layer_speed', '100%'); # prevent speed alteration +# $config->set('start_gcode', ''); # prevent any unexpected Z move +# my $print = Slic3r::Test::init_print('20mm_cube', config => $config); +# +# my $layer_id = -1; +# my $success = 1; +# Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { +# my ($self, $cmd, $args, $info) = @_; +# if ($info->{extruding} && $info->{dist_XY} > 0) { +# # this is a raft layer +# if (($layer_id < $config->raft_layers) && ($layer_id > 0)) { +# my $width; +# my $support_layer_height = $config->nozzle_diameter->[0] * 0.75; +# +# # support layer +# if ($config->raft_layers - $config->support_material_interface_layers > $layer_id) { +# $width = $config->support_material_extrusion_width; +# } +# # interface layer +# else { +# $width = $config->support_material_interface_extrusion_width; +# } +# my $expected_E_per_mm3 = 4 / (($config->filament_diameter->[0]**2) * PI); +# my $expected_mm3_per_mm = $width * $support_layer_height + ($support_layer_height**2) / 4.0 * (PI-4.0); +# my $expected_e_per_mm = $expected_E_per_mm3 * $expected_mm3_per_mm; +# +# my $e_per_mm = ($info->{dist_E} / $info->{dist_XY});; +# +# my $diff = abs($e_per_mm - $expected_e_per_mm); +# +# if ($diff > 0.001) { +# $success = 0; +# } +# } +# } elsif ($cmd eq 'G1' && $info->{dist_Z} > 0) { +# $layer_id++; +# } +# }); +# +# ok $success, +# 'support material interface extrusion width is used for interfaces'; +#} +# +#{ +# my $config = Slic3r::Config->new_from_defaults; +# $config->set('skirts', 0); +# $config->set('layer_height', 0.35); +# $config->set('first_layer_height', 0.3); +# $config->set('nozzle_diameter', [0.5]); +# $config->set('support_material_extruder', 2); +# $config->set('support_material_interface_extruder', 2); +# +# my $test = sub { +# my ($raft_layers) = @_; +# $config->set('raft_layers', $raft_layers); +# my $print = Slic3r::Test::init_print('20mm_cube', config => $config); +# my %raft_z = (); # z => 1 +# my $tool = undef; +# Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { +# my ($self, $cmd, $args, $info) = @_; +# +# if ($cmd =~ /^T(\d+)/) { +# $tool = $1; +# } elsif ($info->{extruding} && $info->{dist_XY} > 0) { +# if ($tool == $config->support_material_extruder-1) { +# $raft_z{$self->Z} = 1; +# } +# } +# }); +# +# is scalar(keys %raft_z), $config->raft_layers, 'correct number of raft layers is generated'; +# }; +# +# $test->(2); +# $test->(70); +# +# $config->set('layer_height', 0.4); +# $config->set('first_layer_height', 0.35); +# $test->(3); +# $test->(70); +#} +# +#{ +# my $config = Slic3r::Config->new_from_defaults; +# $config->set('brim_width', 0); +# $config->set('skirts', 0); +# $config->set('support_material', 1); +# $config->set('top_solid_layers', 0); # so that we don't have the internal bridge over infill +# $config->set('bridge_speed', 99); +# $config->set('cooling', 0); +# $config->set('first_layer_speed', '100%'); +# +# my $test = sub { +# my $print = Slic3r::Test::init_print('overhang', config => $config); +# +# my $has_bridge_speed = 0; +# Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { +# my ($self, $cmd, $args, $info) = @_; +# +# if ($info->{extruding}) { +# if (($args->{F} // $self->F) == $config->bridge_speed*60) { +# $has_bridge_speed = 1; +# } +# } +# }); +# return $has_bridge_speed; +# }; +# +# $config->set('support_material_contact_distance', 0.2); +# ok $test->(), 'bridge speed is used when support_material_contact_distance > 0'; +# +# $config->set('support_material_contact_distance', 0); +# ok !$test->(), 'bridge speed is not used when support_material_contact_distance == 0'; +# +# $config->set('raft_layers', 5); +# $config->set('support_material_contact_distance', 0.2); +# ok $test->(), 'bridge speed is used when raft_layers > 0 and support_material_contact_distance > 0'; +# +# $config->set('support_material_contact_distance', 0); +# ok !$test->(), 'bridge speed is not used when raft_layers > 0 and support_material_contact_distance == 0'; +#} +# +#{ +# my $config = Slic3r::Config->new_from_defaults; +# $config->set('skirts', 0); +# $config->set('start_gcode', ''); +# $config->set('raft_layers', 8); +# $config->set('nozzle_diameter', [0.4, 1]); +# $config->set('layer_height', 0.1); +# $config->set('first_layer_height', 0.8); +# $config->set('support_material_extruder', 2); +# $config->set('support_material_interface_extruder', 2); +# $config->set('support_material_contact_distance', 0); +# my $print = Slic3r::Test::init_print('20mm_cube', config => $config); +# ok my $gcode = Slic3r::Test::gcode($print), 'first_layer_height is validated with support material extruder nozzle diameter when using raft layers'; +# +# my $tool = undef; +# my @z = (0); +# my %layer_heights_by_tool = (); # tool => [ lh, lh... ] +# Slic3r::GCode::Reader->new->parse($gcode, sub { +# my ($self, $cmd, $args, $info) = @_; +# +# if ($cmd =~ /^T(\d+)/) { +# $tool = $1; +# } elsif ($cmd eq 'G1' && exists $args->{Z} && $args->{Z} != $self->Z) { +# push @z, $args->{Z}; +# } elsif ($info->{extruding} && $info->{dist_XY} > 0) { +# $layer_heights_by_tool{$tool} ||= []; +# push @{ $layer_heights_by_tool{$tool} }, $z[-1] - $z[-2]; +# } +# }); +# +# ok !defined(first { $_ > $config->nozzle_diameter->[0] + epsilon } +# @{ $layer_heights_by_tool{$config->perimeter_extruder-1} }), +# 'no object layer is thicker than nozzle diameter'; +# +# ok !defined(first { abs($_ - $config->layer_height) < epsilon } +# @{ $layer_heights_by_tool{$config->support_material_extruder-1} }), +# 'no support material layer is as thin as object layers'; +#} +# +#{ +# my $config = Slic3r::Config->new_from_defaults; +# $config->set('support_material_enforce_layers', 100); +# $config->set('support_material', 0); +# my @contact_z = my @top_z = (); +# +# my $test = sub { +# my $print = Slic3r::Test::init_print('20mm_cube', config => $config); +# my $flow = $print->print->objects->[0]->support_material_flow; +# my $support = Slic3r::Print::SupportMaterial->new( +# object_config => $print->print->objects->[0]->config, +# print_config => $print->print->config, +# flow => $flow, +# interface_flow => $flow, +# first_layer_flow => $flow, +# ); +# my $support_z = $support->support_layers_z(\@contact_z, \@top_z, $config->layer_height); +# +# is scalar(grep { $support_z->[$_]-$support_z->[$_-1] <= 0 } 1..$#$support_z), 0, +# 'forced support is generated'; +# +# }; +# $config->set('layer_height', 0.2); +# $config->set('first_layer_height', 0.3); +# @contact_z = (1.9); +# @top_z = (1.1); +# $test->(); +#} __END__ diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp index b29c9030e..11aef4124 100644 --- a/xs/src/libslic3r/SupportMaterial.cpp +++ b/xs/src/libslic3r/SupportMaterial.cpp @@ -74,36 +74,36 @@ SupportMaterial::generate(PrintObject *object) // that it will be effective, regardless of how it's built below. pair, map> contact_overhang = contact_area(object); map &contact = contact_overhang.first; - cout << "Contact size " << contact.size() << endl; + //cout << "Contact size " << contact.size() << endl; map &overhang = contact_overhang.second; int ppp = 0; - cout << "Samir " << ppp++ << endl; + //cout << "Samir " << ppp++ << endl; // Determine the top surfaces of the object. We need these to determine // the layer heights of support material and to clip support to the object // silhouette. map top = object_top(object, &contact); - cout << "Samir " << ppp++ << endl; + //cout << "Samir " << ppp++ << endl; // We now know the upper and lower boundaries for our support material object // (@$contact_z and @$top_z), so we can generate intermediate layers. vector support_z = support_layers_z(get_keys_sorted(contact), get_keys_sorted(top), get_max_layer_height(object)); - cout << "Samir " << ppp++ << endl; + //cout << "Samir " << ppp++ << endl; // If we wanted to apply some special logic to the first support layers lying on // object's top surfaces this is the place to detect them. map shape; if (object_config->support_material_pattern.value == smpPillars) this->generate_pillars_shape(contact, support_z, shape); - cout << "Samir " << ppp++ << endl; + //cout << "Samir " << ppp++ << endl; // Propagate contact layers downwards to generate interface layers. map interface = generate_interface_layers(support_z, contact, top); clip_with_object(interface, support_z, *object); if (!shape.empty()) clip_with_shape(interface, shape); - cout << "Samir " << ppp++ << endl; + //cout << "Samir " << ppp++ << endl; // Propagate contact layers and interface layers downwards to generate // the main support layers. map base = generate_base_layers(support_z, contact, interface, top); @@ -111,11 +111,11 @@ SupportMaterial::generate(PrintObject *object) if (!shape.empty()) clip_with_shape(base, shape); - cout << "Samir " << ppp++ << endl; + //cout << "Samir " << ppp++ << endl; // Detect what part of base support layers are "reverse interfaces" because they // lie above object's top surfaces. generate_bottom_interface_layers(support_z, base, top, interface); - cout << "Samir " << ppp++ << endl; + //cout << "Samir " << ppp++ << endl; // Install support layers into object. for (int i = 0; i < int(support_z.size()); i++) { object->add_support_layer( @@ -129,11 +129,11 @@ SupportMaterial::generate(PrintObject *object) object->support_layers.end()[-1]->lower_layer = object->support_layers.end()[-2]; } } - cout << "Supports z count is " << support_z.size() << endl; - cout << "Samir " << ppp++ << endl; + //cout << "Supports z count is " << support_z.size() << endl; + //cout << "Samir " << ppp++ << endl; // Generate the actual toolpaths and save them into each layer. generate_toolpaths(object, overhang, contact, interface, base); - cout << "Samir " << ppp++ << endl; + //cout << "Samir " << ppp++ << endl; } vector @@ -142,7 +142,7 @@ SupportMaterial::support_layers_z(vector contact_z, coordf_t max_object_layer_height) { // Quick table to check whether a given Z is a top surface. - map is_top; + map is_top; for (auto z : top_z) is_top[z] = true; // determine layer height for any non-contact layer @@ -150,9 +150,8 @@ SupportMaterial::support_layers_z(vector contact_z, // layer_height > nozzle_diameter * 0.75. auto nozzle_diameter = config->nozzle_diameter.get_at(static_cast( object_config->support_material_extruder - 1)); - auto support_material_height = (max_object_layer_height, (nozzle_diameter * 0.75)); + auto support_material_height = max(max_object_layer_height, (nozzle_diameter * 0.75)); coordf_t _contact_distance = this->contact_distance(support_material_height, nozzle_diameter); - // Initialize known, fixed, support layers. vector z; for (auto c_z : contact_z) z.push_back(c_z); @@ -176,7 +175,7 @@ SupportMaterial::support_layers_z(vector contact_z, // since we already have two raft layers ($z[0] and $z[1]) we need to insert // raft_layers-2 more int idx = 1; - for (int j = 0; j < object_config->raft_layers - 2; j++) { + for (int j = 1; j <= object_config->raft_layers - 2; j++) { float z_new = roundf(static_cast((z[0] + height * idx) * 100)) / 100; // round it to 2 decimal places. z.insert(z.begin() + idx, z_new); @@ -185,14 +184,14 @@ SupportMaterial::support_layers_z(vector contact_z, } // Create other layers (skip raft layers as they're already done and use thicker layers). - for (int i = static_cast(z.size()); i >= object_config->raft_layers.value; i--) { + for (auto i = static_cast(z.size()) - 1; i >= object_config->raft_layers; i--) { coordf_t target_height = support_material_height; - if (i > 0 && is_top[z[i - 1]]) { + if (i > 0 && is_top.count(z[i - 1]) > 0 && is_top[z[i - 1]]) { target_height = nozzle_diameter; } // Enforce first layer height. if ((i == 0 && z[i] > target_height + first_layer_height) - || (z[i] - z[i - 1] > target_height + EPSILON)) { + || (i > 0 && z[i] - z[i - 1] > target_height + EPSILON)) { z.insert(z.begin() + i, (z[i] - target_height)); i++; } @@ -248,7 +247,7 @@ SupportMaterial::contact_area(PrintObject *object) // and $layer->id == 0 means first print layer (including raft). // If no raft, and we're at layer 0, skip to layer 1 - cout << "LAYER ID " << layer_id << endl; + //cout << "LAYER ID " << layer_id << endl; if (conf.raft_layers == 0 && layer_id == 0) { continue; } @@ -601,12 +600,12 @@ SupportMaterial::generate_pillars_shape(const map &contact, if (contact.empty()) return; int u = 0; - cout << "Pillars generation " << contact.size() << endl; + //cout << "Pillars generation " << contact.size() << endl; coord_t pillar_size = scale_(object_config->support_material_pillar_size.value); coord_t pillar_spacing = scale_(object_config->support_material_pillar_spacing.value); - cout << "Samir U " << u++ << endl; + //cout << "Samir U " << u++ << endl; Polygons grid; { auto pillar = Polygon({ @@ -635,28 +634,28 @@ SupportMaterial::generate_pillars_shape(const map &contact, grid = union_(pillars); } - cout << "Samir U " << u++ << endl; //1 - cout << "Support z size " << support_z.size() << endl; + //cout << "Samir U " << u++ << endl; //1 + //cout << "Support z size " << support_z.size() << endl; // Add pillars to every layer. for (auto i = 0; i < support_z.size(); i++) { shape[i] = grid; } - cout << "Samir U " << u++ << endl; + //cout << "Samir U " << u++ << endl; // Build capitals. - cout << "contacts START " << endl; + //cout << "contacts START " << endl; for (auto el : contact) { - cout << el.first << endl; + //cout << el.first << endl; } - cout << "contacts END" << endl; + //cout << "contacts END" << endl; for (auto i = 0; i < support_z.size(); i++) { coordf_t z = support_z[i]; - cout << z << endl; + //cout << z << endl; auto capitals = intersection( grid, contact.count(z) > 0 ? contact.at(z) : Polygons() ); - cout << "Samir U " << u++ << endl; + //cout << "Samir U " << u++ << endl; // 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 // sure nothing is left. @@ -673,7 +672,7 @@ SupportMaterial::generate_pillars_shape(const map &contact, append_to(shape[i], capital_polygons); } } - cout << "Samir U " << u++ << endl; + //cout << "Samir U " << u++ << endl; // 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 // sure nothing is left. @@ -688,7 +687,7 @@ SupportMaterial::generate_pillars_shape(const map &contact, } } } - cout << "Samir U " << u++ << endl; + //cout << "Samir U " << u++ << endl; } map @@ -749,7 +748,6 @@ SupportMaterial::generate_interface_layers(vector support_z, auto interface_layers_num = object_config->support_material_interface_layers.value; for (int layer_id = 0; layer_id < support_z.size(); layer_id++) { - // Todo Ask about how to port this line. "my $this = $contact->{$z} // next;" auto z = support_z[layer_id]; if (contact.count(z) <= 0) diff --git a/xs/src/libslic3r/SupportMaterial.hpp b/xs/src/libslic3r/SupportMaterial.hpp index 15c2fc1e6..4b1eda2f0 100644 --- a/xs/src/libslic3r/SupportMaterial.hpp +++ b/xs/src/libslic3r/SupportMaterial.hpp @@ -88,7 +88,6 @@ public: pair, map> contact_area(PrintObject *object); - // TODO Is this expolygons or polygons? map object_top(PrintObject *object, map *contact); void generate_pillars_shape(const map &contact, From 99c7c2c8bf8b2ce15d0ec600805ad4ed6b668acd Mon Sep 17 00:00:00 2001 From: Samir55 Date: Sun, 15 Jul 2018 03:46:51 +0200 Subject: [PATCH 29/30] Refactoring. --- xs/src/libslic3r/SupportMaterial.cpp | 58 ++++------------------------ 1 file changed, 7 insertions(+), 51 deletions(-) diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp index 11aef4124..598423265 100644 --- a/xs/src/libslic3r/SupportMaterial.cpp +++ b/xs/src/libslic3r/SupportMaterial.cpp @@ -17,25 +17,20 @@ SupportMaterial::generate_toolpaths(PrintObject *object, map interface, map base) { - int ll = 0; - //cout << "Generate toolpaths " << ll++ << endl; - // Assig the object to the supports class. + // Assign the object to the supports class. this->object = object; // Shape of contact area. toolpaths_params params; params.contact_loops = 1; - //cout << "Generate toolpaths " << ll++ << endl; - params.circle_radius = 1.5 * interface_flow.scaled_width(); - //cout << "Generate toolpaths " << ll++ << endl; - params.circle_distance = 3 * params.circle_radius; - //cout << "Generate toolpaths " << ll++ << endl; - params.circle = create_circle(params.circle_radius); - //cout << "Generate toolpaths " << ll++ << endl; - // TODO Add Slic3r debug. "Generating patterns\n" + +#ifdef SLIC3R_DEBUG + printf("Generating patterns\n"); +#endif + // Prepare fillers. params.pattern = object_config->support_material_pattern.value; params.angles.push_back(object_config->support_material_angle.value); @@ -47,7 +42,6 @@ SupportMaterial::generate_toolpaths(PrintObject *object, else if (params.pattern == smpPillars) { params.pattern = smpHoneycomb; } - //cout << "Generate toolpaths " << ll++ << endl; params.interface_angle = object_config->support_material_angle.value + 90; params.interface_spacing = object_config->support_material_interface_spacing.value + interface_flow.spacing(); params.interface_density = @@ -61,7 +55,6 @@ SupportMaterial::generate_toolpaths(PrintObject *object, boost::bind(&SupportMaterial::process_layer, this, _1, params), this->config->threads.value ); - //cout << "Generate toolpaths finished" << ll++ << endl; } void @@ -74,36 +67,28 @@ SupportMaterial::generate(PrintObject *object) // that it will be effective, regardless of how it's built below. pair, map> contact_overhang = contact_area(object); map &contact = contact_overhang.first; - //cout << "Contact size " << contact.size() << endl; map &overhang = contact_overhang.second; - int ppp = 0; - //cout << "Samir " << ppp++ << endl; - // Determine the top surfaces of the object. We need these to determine // the layer heights of support material and to clip support to the object // silhouette. map top = object_top(object, &contact); - //cout << "Samir " << ppp++ << endl; // We now know the upper and lower boundaries for our support material object // (@$contact_z and @$top_z), so we can generate intermediate layers. vector support_z = support_layers_z(get_keys_sorted(contact), get_keys_sorted(top), get_max_layer_height(object)); - //cout << "Samir " << ppp++ << endl; // If we wanted to apply some special logic to the first support layers lying on // object's top surfaces this is the place to detect them. map shape; if (object_config->support_material_pattern.value == smpPillars) this->generate_pillars_shape(contact, support_z, shape); - //cout << "Samir " << ppp++ << endl; // Propagate contact layers downwards to generate interface layers. map interface = generate_interface_layers(support_z, contact, top); clip_with_object(interface, support_z, *object); if (!shape.empty()) clip_with_shape(interface, shape); - //cout << "Samir " << ppp++ << endl; // Propagate contact layers and interface layers downwards to generate // the main support layers. map base = generate_base_layers(support_z, contact, interface, top); @@ -111,11 +96,9 @@ SupportMaterial::generate(PrintObject *object) if (!shape.empty()) clip_with_shape(base, shape); - //cout << "Samir " << ppp++ << endl; // Detect what part of base support layers are "reverse interfaces" because they // lie above object's top surfaces. generate_bottom_interface_layers(support_z, base, top, interface); - //cout << "Samir " << ppp++ << endl; // Install support layers into object. for (int i = 0; i < int(support_z.size()); i++) { object->add_support_layer( @@ -129,11 +112,8 @@ SupportMaterial::generate(PrintObject *object) object->support_layers.end()[-1]->lower_layer = object->support_layers.end()[-2]; } } - //cout << "Supports z count is " << support_z.size() << endl; - //cout << "Samir " << ppp++ << endl; // Generate the actual toolpaths and save them into each layer. generate_toolpaths(object, overhang, contact, interface, base); - //cout << "Samir " << ppp++ << endl; } vector @@ -201,7 +181,7 @@ SupportMaterial::support_layers_z(vector contact_z, { set s; for (coordf_t el : z) - s.insert(int(el * 100) / 100.0); // round it to 2 decimal places. + s.insert(int(el * 1000) / 1000.0); // round it to 2 decimal places. z = vector(); for (coordf_t el : s) z.push_back(el); @@ -213,10 +193,7 @@ SupportMaterial::support_layers_z(vector contact_z, pair, map> SupportMaterial::contact_area(PrintObject *object) { - //cout << "After " << this->object_config->support_material.value << " ," << (this->object_config->support_material ? " Support Material is enabled" : "Support Material is disabled") << endl; - PrintObjectConfig &conf = *this->object_config; - //cout << conf.support_material.value << " " << (conf.support_material.value ? " Support Material is enabled" : "Support Material is disabled") << endl; // If user specified a custom angle threshold, convert it to radians. float threshold_rad = 0.0; @@ -247,11 +224,9 @@ SupportMaterial::contact_area(PrintObject *object) // and $layer->id == 0 means first print layer (including raft). // If no raft, and we're at layer 0, skip to layer 1 - //cout << "LAYER ID " << layer_id << endl; if (conf.raft_layers == 0 && layer_id == 0) { continue; } - //cout << conf.support_material.value << " " << (conf.support_material.value ? " Support Material is enabled" : "Support Material is disabled") << endl; // With or without raft, if we're above layer 1, we need to quit // support generation if supports are disabled, or if we're at a high // enough layer that enforce-supports no longer applies. @@ -283,9 +258,7 @@ SupportMaterial::contact_area(PrintObject *object) // with the polygons collected before, // but don't apply the safety offset during the union operation as it would // inflate the polygons over and over. - //cout << "Old Build plate only surfaces " << buildplate_only_top_surfaces.size() << endl; append_to(buildplate_only_top_surfaces, offset(projection_new, scale_(0.01))); - //cout << "New Build plate only surfaces " << buildplate_only_top_surfaces.size() << endl; buildplate_only_top_surfaces = union_(buildplate_only_top_surfaces, 0); } @@ -531,7 +504,6 @@ SupportMaterial::contact_area(PrintObject *object) // Ignore this contact area if it's too low. if (contact_z < conf.first_layer_height - EPSILON) continue; -//cout << contact_z << " " << tmp_contact.size() << endl; contact[contact_z] = tmp_contact; overhang[contact_z] = tmp_overhang; @@ -599,13 +571,9 @@ SupportMaterial::generate_pillars_shape(const map &contact, // This prevents supplying an empty point set to BoundingBox constructor. if (contact.empty()) return; - int u = 0; - //cout << "Pillars generation " << contact.size() << endl; - coord_t pillar_size = scale_(object_config->support_material_pillar_size.value); coord_t pillar_spacing = scale_(object_config->support_material_pillar_spacing.value); - //cout << "Samir U " << u++ << endl; Polygons grid; { auto pillar = Polygon({ @@ -634,28 +602,18 @@ SupportMaterial::generate_pillars_shape(const map &contact, grid = union_(pillars); } - //cout << "Samir U " << u++ << endl; //1 - //cout << "Support z size " << support_z.size() << endl; // Add pillars to every layer. for (auto i = 0; i < support_z.size(); i++) { shape[i] = grid; } - //cout << "Samir U " << u++ << endl; // Build capitals. - //cout << "contacts START " << endl; - for (auto el : contact) { - //cout << el.first << endl; - } - //cout << "contacts END" << endl; for (auto i = 0; i < support_z.size(); i++) { coordf_t z = support_z[i]; - //cout << z << endl; auto capitals = intersection( grid, contact.count(z) > 0 ? contact.at(z) : Polygons() ); - //cout << "Samir U " << u++ << endl; // 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 // sure nothing is left. @@ -672,7 +630,6 @@ SupportMaterial::generate_pillars_shape(const map &contact, append_to(shape[i], capital_polygons); } } - //cout << "Samir U " << u++ << endl; // 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 // sure nothing is left. @@ -687,7 +644,6 @@ SupportMaterial::generate_pillars_shape(const map &contact, } } } - //cout << "Samir U " << u++ << endl; } map From 737353a85bda4f0c87e2b456d9f0c57aad91b1e7 Mon Sep 17 00:00:00 2001 From: Samir55 Date: Sun, 15 Jul 2018 04:34:49 +0200 Subject: [PATCH 30/30] Refactoring. --- lib/Slic3r/Print/SupportMaterial.pm | 1 - src/test/libslic3r/test_support_material.cpp | 247 +++++--- t/support.t | 559 +++++++++---------- xs/src/libslic3r/SupportMaterial.cpp | 30 +- xs/src/libslic3r/SupportMaterial.hpp | 4 +- 5 files changed, 453 insertions(+), 388 deletions(-) diff --git a/lib/Slic3r/Print/SupportMaterial.pm b/lib/Slic3r/Print/SupportMaterial.pm index cc297a415..5c7324d65 100644 --- a/lib/Slic3r/Print/SupportMaterial.pm +++ b/lib/Slic3r/Print/SupportMaterial.pm @@ -1018,4 +1018,3 @@ sub contact_distance { } 1; - diff --git a/src/test/libslic3r/test_support_material.cpp b/src/test/libslic3r/test_support_material.cpp index 0222ffa07..0ecdb9f3b 100644 --- a/src/test/libslic3r/test_support_material.cpp +++ b/src/test/libslic3r/test_support_material.cpp @@ -1,5 +1,6 @@ //#include #include +#include #include "/home/ahmedsamir/Work/SamirSlic3r/Slic3r/build/external/Catch/include/catch.hpp" #include "libslic3r.h" @@ -10,9 +11,10 @@ using namespace std; using namespace Slic3r; -void test_1_check(Print &print, bool &a, bool &b, bool &c, bool &d); +void test_1_checks(Print &print, bool &a, bool &b, bool &c, bool &d); +bool test_6_checks(Print &print); -// Testing supports material member functions. +// Testing 0.1: supports material member functions. TEST_CASE("", "") { // Create a mesh & modelObject. @@ -44,6 +46,95 @@ TEST_CASE("", "") } +// Test 1. +SCENARIO("SupportMaterial: support_layers_z and contact_distance") +{ + GIVEN("A print object having one modelObject") { + // Create a mesh & modelObject. + TriangleMesh mesh = TriangleMesh::make_cube(20, 20, 20); + + // Create modelObject. + Model model = Model(); + ModelObject *object = model.add_object(); + object->add_volume(mesh); + model.add_default_instances(); + + // Align to origin. + model.align_instances_to_origin(); + // Create Print. + Print print = Print(); + print.default_object_config.set_deserialize("support_material", "1"); + + WHEN("First layer height = 0.4") { + print.default_object_config.set_deserialize("layer_height", "0.2"); + print.default_object_config.set_deserialize("first_layer_height", "0.4"); + + print.add_model_object(model.objects[0]); + print.objects.front()->_slice(); + bool a, b, c, d; + + test_1_checks(print, a, b, c, d); + THEN("First layer height is honored") { + REQUIRE(a == true); + } + THEN("No null or negative support layers") { + REQUIRE(b == true); + } + THEN("No layers thicker than nozzle diameter") { + REQUIRE(c == true); + } + THEN("Layers above top surfaces are spaced correctly") { + REQUIRE(d == true); + } + } + WHEN("Layer height = 0.2 and, first layer height = 0.3") { + print.default_object_config.set_deserialize("layer_height", "0.2"); + print.default_object_config.set_deserialize("first_layer_height", "0.3"); + print.add_model_object(model.objects[0]); + print.objects.front()->_slice(); + bool a, b, c, d; + + test_1_checks(print, a, b, c, d); + THEN("First layer height is honored") { + REQUIRE(a == true); + } + THEN("No null or negative support layers") { + REQUIRE(b == true); + } + THEN("No layers thicker than nozzle diameter") { + REQUIRE(c == true); + } + THEN("Layers above top surfaces are spaced correctly") { + REQUIRE(d == true); + } + } + + + WHEN("Layer height = nozzle_diameter[0]") { + print.default_object_config.set_deserialize("layer_height", "0.2"); + print.default_object_config.set_deserialize("first_layer_height", "0.3"); + print.add_model_object(model.objects[0]); + print.objects.front()->_slice(); + bool a, b, c, d; + + test_1_checks(print, a, b, c, d); + THEN("First layer height is honored") { + REQUIRE(a == true); + } + THEN("No null or negative support layers") { + REQUIRE(b == true); + } + THEN("No layers thicker than nozzle diameter") { + REQUIRE(c == true); + } + THEN("Layers above top surfaces are spaced correctly") { + REQUIRE(d == true); + } + } + } +} + +// Test 8. TEST_CASE("SupportMaterial: forced support is generated", "") { // Create a mesh & modelObject. @@ -71,7 +162,7 @@ TEST_CASE("SupportMaterial: forced support is generated", "") auto support_z = support->support_layers_z(contact_z, top_z, print.default_object_config.layer_height); bool check = true; - for (int i = 1; i < support_z.size(); i++) { + for (size_t i = 1; i < support_z.size(); i++) { if (support_z[i] - support_z[i - 1] <= 0) check = false; } @@ -79,107 +170,67 @@ TEST_CASE("SupportMaterial: forced support is generated", "") REQUIRE(check == true); } -SCENARIO("SupportMaterial: support_layers_z and contact_distance") +// Test 6. +SCENARIO("SupportMaterial: Checking bridge speed") { - GIVEN("A print object having one modelObject") { + GIVEN("Print object") { // Create a mesh & modelObject. TriangleMesh mesh = TriangleMesh::make_cube(20, 20, 20); - // Create modelObject. Model model = Model(); ModelObject *object = model.add_object(); object->add_volume(mesh); model.add_default_instances(); - - // Align to origin. model.align_instances_to_origin(); - WHEN("First layer height = 0.4") { - // Create Print. - Print print = Print(); - print.default_object_config.set_deserialize("support_material", "1"); - print.default_object_config.set_deserialize("layer_height", "0.2"); - print.default_object_config.set_deserialize("first_layer_height", "0.4"); + Print print = Print(); + print.config.brim_width = 0; + print.config.skirts = 0; + print.config.skirts = 0; + print.default_object_config.support_material = 1; + print.default_region_config.top_solid_layers = 0; // so that we don't have the internal bridge over infill. + print.default_region_config.bridge_speed = 99; + print.config.cooling = 0; + print.config.set_deserialize("first_layer_speed", "100%"); + WHEN("support_material_contact_distance = 0.2") { + print.default_object_config.support_material_contact_distance = 0.2; print.add_model_object(model.objects[0]); - print.objects.front()->_slice(); - bool a, b, c, d; - test_1_check(print, a, b, c, d); - THEN("First layer height is honored") { - REQUIRE(a == true); - } - THEN("No null or negative support layers") { - REQUIRE(b == true); - } - THEN("No layers thicker than nozzle diameter") { - REQUIRE(c == true); - } - THEN("Layers above top surfaces are spaced correctly") { - REQUIRE(d == true); - } + bool check = test_6_checks(print); + REQUIRE(check == true); // bridge speed is used. + } + + WHEN("support_material_contact_distance = 0") { + print.default_object_config.support_material_contact_distance = 0; + print.add_model_object(model.objects[0]); + + bool check = test_6_checks(print); + REQUIRE(check == true); // bridge speed is not used. + } + + WHEN("support_material_contact_distance = 0.2 & raft_layers = 5") { + print.default_object_config.support_material_contact_distance = 0.2; + print.default_object_config.raft_layers = 5; + print.add_model_object(model.objects[0]); + + bool check = test_6_checks(print); + REQUIRE(check == true); // bridge speed is used. + } + + WHEN("support_material_contact_distance = 0 & raft_layers = 5") { + print.default_object_config.support_material_contact_distance = 0; + print.default_object_config.raft_layers = 5; + print.add_model_object(model.objects[0]); + + bool check = test_6_checks(print); + + REQUIRE(check == true); // bridge speed is not used. } -// WHEN("Layer height = 0.2 and, first layer height = 0.3") { -// print.default_object_config.set_deserialize("layer_height", "0.2"); -// print.default_object_config.set_deserialize("first_layer_height", "0.3"); -// print.add_model_object(model.objects[0]); -// print.objects.front()->_slice(); -// bool a, b, c, d; -// -// test_1_check(print, a, b, c, d); -// THEN("First layer height is honored") { -// REQUIRE(a == true); -// } -// THEN("No null or negative support layers") { -// REQUIRE(b == true); -// } -// THEN("No layers thicker than nozzle diameter") { -// REQUIRE(c == true); -// } -// THEN("Layers above top surfaces are spaced correctly") { -// REQUIRE(d == true); -// } -// } -// -// -// WHEN("Layer height = nozzle_diameter[0]") { -// print.default_object_config.set_deserialize("layer_height", "0.2"); -// print.default_object_config.set_deserialize("first_layer_height", "0.3"); -// print.add_model_object(model.objects[0]); -// print.objects.front()->_slice(); -// bool a, b, c, d; -// -// test_1_check(print, a, b, c, d); -// THEN("First layer height is honored") { -// REQUIRE(a == true); -// } -// THEN("No null or negative support layers") { -// REQUIRE(b == true); -// } -// THEN("No layers thicker than nozzle diameter") { -// REQUIRE(c == true); -// } -// THEN("Layers above top surfaces are spaced correctly") { -// REQUIRE(d == true); -// } -// } } } -SCENARIO("SupportMaterial: Checking bridge speed") { - // Create a mesh & modelObject. - TriangleMesh mesh = TriangleMesh::make_cube(20, 20, 20); - - Model model = Model(); - ModelObject *object = model.add_object(); - object->add_volume(mesh); - model.add_default_instances(); - model.align_instances_to_origin(); - - Print print = Print(); -} - -void test_1_check(Print &print, bool &a, bool &b, bool &c, bool &d) +void test_1_checks(Print &print, bool &a, bool &b, bool &c, bool &d) { vector contact_z = {1.9}; vector top_z = {1.1}; @@ -188,9 +239,6 @@ void test_1_check(Print &print, bool &a, bool &b, bool &c, bool &d) vector support_z = support->support_layers_z(contact_z, top_z, print.default_object_config.layer_height); -for(auto el: support_z) - cout << el << endl; - cout << endl; a = (support_z[0] == print.default_object_config.first_layer_height.value); @@ -227,3 +275,22 @@ for(auto el: support_z) } d = !wrong_top_spacing; } + +// TODO +bool test_6_checks(Print &print) +{ + bool has_bridge_speed = true; + + // Pre-Processing. + PrintObject *print_object = print.objects.front(); + print_object->_infill(); + SupportMaterial *support_material = print.objects.front()->_support_material(); + support_material->generate(print_object); + // TODO but not needed in test 6 (make brims and make skirts). + + // Exporting gcode. + // TODO validation found in Simple.pm + + + return has_bridge_speed; +} \ No newline at end of file diff --git a/t/support.t b/t/support.t index 61d1416fa..0a5a75337 100644 --- a/t/support.t +++ b/t/support.t @@ -1,4 +1,4 @@ -use Test::More tests => 12; +use Test::More tests => 29; use strict; use warnings; @@ -7,7 +7,7 @@ BEGIN { use lib "$FindBin::Bin/../lib"; use local::lib "$FindBin::Bin/../local-lib"; } -use Data::Dumper; + use List::Util qw(first); use Slic3r; use Slic3r::Geometry qw(epsilon scale PI); @@ -31,7 +31,7 @@ use Slic3r::Test; ); my $support_z = $support->support_layers_z(\@contact_z, \@top_z, $config->layer_height); my $expected_top_spacing = $support->contact_distance($config->layer_height, $config->nozzle_diameter->[0]); - diag(Dumper($support_z)); + is $support_z->[0], $config->first_layer_height, 'first layer height is honored'; is scalar(grep { $support_z->[$_]-$support_z->[$_-1] <= 0 } 1..$#$support_z), 0, @@ -64,281 +64,280 @@ use Slic3r::Test; $config->set('layer_height', $config->nozzle_diameter->[0]); $test->(); } -# -#{ -# my $config = Slic3r::Config->new_from_defaults; -# $config->set('raft_layers', 3); -# $config->set('brim_width', 0); -# $config->set('skirts', 0); -# $config->set('support_material_extruder', 2); -# $config->set('support_material_interface_extruder', 2); -# $config->set('layer_height', 0.4); -# $config->set('first_layer_height', 0.4); -# my $print = Slic3r::Test::init_print('overhang', config => $config); -# ok my $gcode = Slic3r::Test::gcode($print), 'no conflict between raft/support and brim'; -# -# my $tool = 0; -# Slic3r::GCode::Reader->new->parse($gcode, sub { -# my ($self, $cmd, $args, $info) = @_; -# -# if ($cmd =~ /^T(\d+)/) { -# $tool = $1; -# } elsif ($info->{extruding}) { -# if ($self->Z <= ($config->raft_layers * $config->layer_height)) { -# fail 'not extruding raft with support material extruder' -# if $tool != ($config->support_material_extruder-1); -# } else { -# fail 'support material exceeds raft layers' -# if $tool == $config->support_material_extruder-1; -# # TODO: we should test that full support is generated when we use raft too -# } -# } -# }); -#} -# -#{ -# my $config = Slic3r::Config->new_from_defaults; -# $config->set('skirts', 0); -# $config->set('raft_layers', 3); -# $config->set('support_material_pattern', 'honeycomb'); -# $config->set('support_material_extrusion_width', 0.6); -# $config->set('first_layer_extrusion_width', '100%'); -# $config->set('bridge_speed', 99); -# $config->set('cooling', 0); # prevent speed alteration -# $config->set('first_layer_speed', '100%'); # prevent speed alteration -# $config->set('start_gcode', ''); # prevent any unexpected Z move -# my $print = Slic3r::Test::init_print('20mm_cube', config => $config); -# -# my $layer_id = -1; # so that first Z move sets this to 0 -# my @raft = my @first_object_layer = (); -# my %first_object_layer_speeds = (); # F => 1 -# Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { -# my ($self, $cmd, $args, $info) = @_; -# if ($info->{extruding} && $info->{dist_XY} > 0) { -# if ($layer_id <= $config->raft_layers) { -# # this is a raft layer or the first object layer -# my $line = Slic3r::Line->new_scale([ $self->X, $self->Y ], [ $info->{new_X}, $info->{new_Y} ]); -# my @path = @{$line->grow(scale($config->support_material_extrusion_width/2))}; -# if ($layer_id < $config->raft_layers) { -# # this is a raft layer -# push @raft, @path; -# } else { -# push @first_object_layer, @path; -# $first_object_layer_speeds{ $args->{F} // $self->F } = 1; -# } -# } -# } elsif ($cmd eq 'G1' && $info->{dist_Z} > 0) { -# $layer_id++; -# } -# }); -# -# ok !@{diff(\@first_object_layer, \@raft)}, -# 'first object layer is completely supported by raft'; -# is scalar(keys %first_object_layer_speeds), 1, -# 'only one speed used in first object layer'; -# ok +(keys %first_object_layer_speeds)[0] == $config->bridge_speed*60, -# 'bridge speed used in first object layer'; -#} -# -#{ -# my $config = Slic3r::Config->new_from_defaults; -# $config->set('layer_height', 0.2); -# $config->set('skirts', 0); -# $config->set('raft_layers', 5); -# $config->set('support_material_pattern', 'rectilinear'); -# $config->set('support_material_extrusion_width', 0.4); -# $config->set('support_material_interface_extrusion_width', 0.6); -# $config->set('support_material_interface_layers', 2); -# $config->set('first_layer_extrusion_width', '100%'); -# $config->set('bridge_speed', 99); -# $config->set('cooling', 0); # prevent speed alteration -# $config->set('first_layer_speed', '100%'); # prevent speed alteration -# $config->set('start_gcode', ''); # prevent any unexpected Z move -# my $print = Slic3r::Test::init_print('20mm_cube', config => $config); -# -# my $layer_id = -1; -# my $success = 1; -# Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { -# my ($self, $cmd, $args, $info) = @_; -# if ($info->{extruding} && $info->{dist_XY} > 0) { -# # this is a raft layer -# if (($layer_id < $config->raft_layers) && ($layer_id > 0)) { -# my $width; -# my $support_layer_height = $config->nozzle_diameter->[0] * 0.75; -# -# # support layer -# if ($config->raft_layers - $config->support_material_interface_layers > $layer_id) { -# $width = $config->support_material_extrusion_width; -# } -# # interface layer -# else { -# $width = $config->support_material_interface_extrusion_width; -# } -# my $expected_E_per_mm3 = 4 / (($config->filament_diameter->[0]**2) * PI); -# my $expected_mm3_per_mm = $width * $support_layer_height + ($support_layer_height**2) / 4.0 * (PI-4.0); -# my $expected_e_per_mm = $expected_E_per_mm3 * $expected_mm3_per_mm; -# -# my $e_per_mm = ($info->{dist_E} / $info->{dist_XY});; -# -# my $diff = abs($e_per_mm - $expected_e_per_mm); -# -# if ($diff > 0.001) { -# $success = 0; -# } -# } -# } elsif ($cmd eq 'G1' && $info->{dist_Z} > 0) { -# $layer_id++; -# } -# }); -# -# ok $success, -# 'support material interface extrusion width is used for interfaces'; -#} -# -#{ -# my $config = Slic3r::Config->new_from_defaults; -# $config->set('skirts', 0); -# $config->set('layer_height', 0.35); -# $config->set('first_layer_height', 0.3); -# $config->set('nozzle_diameter', [0.5]); -# $config->set('support_material_extruder', 2); -# $config->set('support_material_interface_extruder', 2); -# -# my $test = sub { -# my ($raft_layers) = @_; -# $config->set('raft_layers', $raft_layers); -# my $print = Slic3r::Test::init_print('20mm_cube', config => $config); -# my %raft_z = (); # z => 1 -# my $tool = undef; -# Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { -# my ($self, $cmd, $args, $info) = @_; -# -# if ($cmd =~ /^T(\d+)/) { -# $tool = $1; -# } elsif ($info->{extruding} && $info->{dist_XY} > 0) { -# if ($tool == $config->support_material_extruder-1) { -# $raft_z{$self->Z} = 1; -# } -# } -# }); -# -# is scalar(keys %raft_z), $config->raft_layers, 'correct number of raft layers is generated'; -# }; -# -# $test->(2); -# $test->(70); -# -# $config->set('layer_height', 0.4); -# $config->set('first_layer_height', 0.35); -# $test->(3); -# $test->(70); -#} -# -#{ -# my $config = Slic3r::Config->new_from_defaults; -# $config->set('brim_width', 0); -# $config->set('skirts', 0); -# $config->set('support_material', 1); -# $config->set('top_solid_layers', 0); # so that we don't have the internal bridge over infill -# $config->set('bridge_speed', 99); -# $config->set('cooling', 0); -# $config->set('first_layer_speed', '100%'); -# -# my $test = sub { -# my $print = Slic3r::Test::init_print('overhang', config => $config); -# -# my $has_bridge_speed = 0; -# Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { -# my ($self, $cmd, $args, $info) = @_; -# -# if ($info->{extruding}) { -# if (($args->{F} // $self->F) == $config->bridge_speed*60) { -# $has_bridge_speed = 1; -# } -# } -# }); -# return $has_bridge_speed; -# }; -# -# $config->set('support_material_contact_distance', 0.2); -# ok $test->(), 'bridge speed is used when support_material_contact_distance > 0'; -# -# $config->set('support_material_contact_distance', 0); -# ok !$test->(), 'bridge speed is not used when support_material_contact_distance == 0'; -# -# $config->set('raft_layers', 5); -# $config->set('support_material_contact_distance', 0.2); -# ok $test->(), 'bridge speed is used when raft_layers > 0 and support_material_contact_distance > 0'; -# -# $config->set('support_material_contact_distance', 0); -# ok !$test->(), 'bridge speed is not used when raft_layers > 0 and support_material_contact_distance == 0'; -#} -# -#{ -# my $config = Slic3r::Config->new_from_defaults; -# $config->set('skirts', 0); -# $config->set('start_gcode', ''); -# $config->set('raft_layers', 8); -# $config->set('nozzle_diameter', [0.4, 1]); -# $config->set('layer_height', 0.1); -# $config->set('first_layer_height', 0.8); -# $config->set('support_material_extruder', 2); -# $config->set('support_material_interface_extruder', 2); -# $config->set('support_material_contact_distance', 0); -# my $print = Slic3r::Test::init_print('20mm_cube', config => $config); -# ok my $gcode = Slic3r::Test::gcode($print), 'first_layer_height is validated with support material extruder nozzle diameter when using raft layers'; -# -# my $tool = undef; -# my @z = (0); -# my %layer_heights_by_tool = (); # tool => [ lh, lh... ] -# Slic3r::GCode::Reader->new->parse($gcode, sub { -# my ($self, $cmd, $args, $info) = @_; -# -# if ($cmd =~ /^T(\d+)/) { -# $tool = $1; -# } elsif ($cmd eq 'G1' && exists $args->{Z} && $args->{Z} != $self->Z) { -# push @z, $args->{Z}; -# } elsif ($info->{extruding} && $info->{dist_XY} > 0) { -# $layer_heights_by_tool{$tool} ||= []; -# push @{ $layer_heights_by_tool{$tool} }, $z[-1] - $z[-2]; -# } -# }); -# -# ok !defined(first { $_ > $config->nozzle_diameter->[0] + epsilon } -# @{ $layer_heights_by_tool{$config->perimeter_extruder-1} }), -# 'no object layer is thicker than nozzle diameter'; -# -# ok !defined(first { abs($_ - $config->layer_height) < epsilon } -# @{ $layer_heights_by_tool{$config->support_material_extruder-1} }), -# 'no support material layer is as thin as object layers'; -#} -# -#{ -# my $config = Slic3r::Config->new_from_defaults; -# $config->set('support_material_enforce_layers', 100); -# $config->set('support_material', 0); -# my @contact_z = my @top_z = (); -# -# my $test = sub { -# my $print = Slic3r::Test::init_print('20mm_cube', config => $config); -# my $flow = $print->print->objects->[0]->support_material_flow; -# my $support = Slic3r::Print::SupportMaterial->new( -# object_config => $print->print->objects->[0]->config, -# print_config => $print->print->config, -# flow => $flow, -# interface_flow => $flow, -# first_layer_flow => $flow, -# ); -# my $support_z = $support->support_layers_z(\@contact_z, \@top_z, $config->layer_height); -# -# is scalar(grep { $support_z->[$_]-$support_z->[$_-1] <= 0 } 1..$#$support_z), 0, -# 'forced support is generated'; -# -# }; -# $config->set('layer_height', 0.2); -# $config->set('first_layer_height', 0.3); -# @contact_z = (1.9); -# @top_z = (1.1); -# $test->(); -#} + +{ + my $config = Slic3r::Config->new_from_defaults; + $config->set('raft_layers', 3); + $config->set('brim_width', 0); + $config->set('skirts', 0); + $config->set('support_material_extruder', 2); + $config->set('support_material_interface_extruder', 2); + $config->set('layer_height', 0.4); + $config->set('first_layer_height', 0.4); + my $print = Slic3r::Test::init_print('overhang', config => $config); + ok my $gcode = Slic3r::Test::gcode($print), 'no conflict between raft/support and brim'; + + my $tool = 0; + Slic3r::GCode::Reader->new->parse($gcode, sub { + my ($self, $cmd, $args, $info) = @_; + + if ($cmd =~ /^T(\d+)/) { + $tool = $1; + } elsif ($info->{extruding}) { + if ($self->Z <= ($config->raft_layers * $config->layer_height)) { + fail 'not extruding raft with support material extruder' + if $tool != ($config->support_material_extruder-1); + } else { + fail 'support material exceeds raft layers' + if $tool == $config->support_material_extruder-1; + # TODO: we should test that full support is generated when we use raft too + } + } + }); +} + +{ + my $config = Slic3r::Config->new_from_defaults; + $config->set('skirts', 0); + $config->set('raft_layers', 3); + $config->set('support_material_pattern', 'honeycomb'); + $config->set('support_material_extrusion_width', 0.6); + $config->set('first_layer_extrusion_width', '100%'); + $config->set('bridge_speed', 99); + $config->set('cooling', 0); # prevent speed alteration + $config->set('first_layer_speed', '100%'); # prevent speed alteration + $config->set('start_gcode', ''); # prevent any unexpected Z move + my $print = Slic3r::Test::init_print('20mm_cube', config => $config); + + my $layer_id = -1; # so that first Z move sets this to 0 + my @raft = my @first_object_layer = (); + my %first_object_layer_speeds = (); # F => 1 + Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { + my ($self, $cmd, $args, $info) = @_; + if ($info->{extruding} && $info->{dist_XY} > 0) { + if ($layer_id <= $config->raft_layers) { + # this is a raft layer or the first object layer + my $line = Slic3r::Line->new_scale([ $self->X, $self->Y ], [ $info->{new_X}, $info->{new_Y} ]); + my @path = @{$line->grow(scale($config->support_material_extrusion_width/2))}; + if ($layer_id < $config->raft_layers) { + # this is a raft layer + push @raft, @path; + } else { + push @first_object_layer, @path; + $first_object_layer_speeds{ $args->{F} // $self->F } = 1; + } + } + } elsif ($cmd eq 'G1' && $info->{dist_Z} > 0) { + $layer_id++; + } + }); + + ok !@{diff(\@first_object_layer, \@raft)}, + 'first object layer is completely supported by raft'; + is scalar(keys %first_object_layer_speeds), 1, + 'only one speed used in first object layer'; + ok +(keys %first_object_layer_speeds)[0] == $config->bridge_speed*60, + 'bridge speed used in first object layer'; +} + +{ + my $config = Slic3r::Config->new_from_defaults; + $config->set('layer_height', 0.2); + $config->set('skirts', 0); + $config->set('raft_layers', 5); + $config->set('support_material_pattern', 'rectilinear'); + $config->set('support_material_extrusion_width', 0.4); + $config->set('support_material_interface_extrusion_width', 0.6); + $config->set('support_material_interface_layers', 2); + $config->set('first_layer_extrusion_width', '100%'); + $config->set('bridge_speed', 99); + $config->set('cooling', 0); # prevent speed alteration + $config->set('first_layer_speed', '100%'); # prevent speed alteration + $config->set('start_gcode', ''); # prevent any unexpected Z move + my $print = Slic3r::Test::init_print('20mm_cube', config => $config); + + my $layer_id = -1; + my $success = 1; + Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { + my ($self, $cmd, $args, $info) = @_; + if ($info->{extruding} && $info->{dist_XY} > 0) { + # this is a raft layer + if (($layer_id < $config->raft_layers) && ($layer_id > 0)) { + my $width; + my $support_layer_height = $config->nozzle_diameter->[0] * 0.75; + + # support layer + if ($config->raft_layers - $config->support_material_interface_layers > $layer_id) { + $width = $config->support_material_extrusion_width; + } + # interface layer + else { + $width = $config->support_material_interface_extrusion_width; + } + my $expected_E_per_mm3 = 4 / (($config->filament_diameter->[0]**2) * PI); + my $expected_mm3_per_mm = $width * $support_layer_height + ($support_layer_height**2) / 4.0 * (PI-4.0); + my $expected_e_per_mm = $expected_E_per_mm3 * $expected_mm3_per_mm; + + my $e_per_mm = ($info->{dist_E} / $info->{dist_XY});; + + my $diff = abs($e_per_mm - $expected_e_per_mm); + + if ($diff > 0.001) { + $success = 0; + } + } + } elsif ($cmd eq 'G1' && $info->{dist_Z} > 0) { + $layer_id++; + } + }); + + ok $success, + 'support material interface extrusion width is used for interfaces'; +} + +{ + my $config = Slic3r::Config->new_from_defaults; + $config->set('skirts', 0); + $config->set('layer_height', 0.35); + $config->set('first_layer_height', 0.3); + $config->set('nozzle_diameter', [0.5]); + $config->set('support_material_extruder', 2); + $config->set('support_material_interface_extruder', 2); + + my $test = sub { + my ($raft_layers) = @_; + $config->set('raft_layers', $raft_layers); + my $print = Slic3r::Test::init_print('20mm_cube', config => $config); + my %raft_z = (); # z => 1 + my $tool = undef; + Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { + my ($self, $cmd, $args, $info) = @_; + + if ($cmd =~ /^T(\d+)/) { + $tool = $1; + } elsif ($info->{extruding} && $info->{dist_XY} > 0) { + if ($tool == $config->support_material_extruder-1) { + $raft_z{$self->Z} = 1; + } + } + }); + + is scalar(keys %raft_z), $config->raft_layers, 'correct number of raft layers is generated'; + }; + + $test->(2); + $test->(70); + + $config->set('layer_height', 0.4); + $config->set('first_layer_height', 0.35); + $test->(3); + $test->(70); +} + +{ + my $config = Slic3r::Config->new_from_defaults; + $config->set('brim_width', 0); + $config->set('skirts', 0); + $config->set('support_material', 1); + $config->set('top_solid_layers', 0); # so that we don't have the internal bridge over infill + $config->set('bridge_speed', 99); + $config->set('cooling', 0); + $config->set('first_layer_speed', '100%'); + + my $test = sub { + my $print = Slic3r::Test::init_print('overhang', config => $config); + + my $has_bridge_speed = 0; + Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { + my ($self, $cmd, $args, $info) = @_; + + if ($info->{extruding}) { + if (($args->{F} // $self->F) == $config->bridge_speed*60) { + $has_bridge_speed = 1; + } + } + }); + return $has_bridge_speed; + }; + + $config->set('support_material_contact_distance', 0.2); + ok $test->(), 'bridge speed is used when support_material_contact_distance > 0'; + + $config->set('support_material_contact_distance', 0); + ok !$test->(), 'bridge speed is not used when support_material_contact_distance == 0'; + + $config->set('raft_layers', 5); + $config->set('support_material_contact_distance', 0.2); + ok $test->(), 'bridge speed is used when raft_layers > 0 and support_material_contact_distance > 0'; + + $config->set('support_material_contact_distance', 0); + ok !$test->(), 'bridge speed is not used when raft_layers > 0 and support_material_contact_distance == 0'; +} + +{ + my $config = Slic3r::Config->new_from_defaults; + $config->set('skirts', 0); + $config->set('start_gcode', ''); + $config->set('raft_layers', 8); + $config->set('nozzle_diameter', [0.4, 1]); + $config->set('layer_height', 0.1); + $config->set('first_layer_height', 0.8); + $config->set('support_material_extruder', 2); + $config->set('support_material_interface_extruder', 2); + $config->set('support_material_contact_distance', 0); + my $print = Slic3r::Test::init_print('20mm_cube', config => $config); + ok my $gcode = Slic3r::Test::gcode($print), 'first_layer_height is validated with support material extruder nozzle diameter when using raft layers'; + + my $tool = undef; + my @z = (0); + my %layer_heights_by_tool = (); # tool => [ lh, lh... ] + Slic3r::GCode::Reader->new->parse($gcode, sub { + my ($self, $cmd, $args, $info) = @_; + + if ($cmd =~ /^T(\d+)/) { + $tool = $1; + } elsif ($cmd eq 'G1' && exists $args->{Z} && $args->{Z} != $self->Z) { + push @z, $args->{Z}; + } elsif ($info->{extruding} && $info->{dist_XY} > 0) { + $layer_heights_by_tool{$tool} ||= []; + push @{ $layer_heights_by_tool{$tool} }, $z[-1] - $z[-2]; + } + }); + + ok !defined(first { $_ > $config->nozzle_diameter->[0] + epsilon } + @{ $layer_heights_by_tool{$config->perimeter_extruder-1} }), + 'no object layer is thicker than nozzle diameter'; + + ok !defined(first { abs($_ - $config->layer_height) < epsilon } + @{ $layer_heights_by_tool{$config->support_material_extruder-1} }), + 'no support material layer is as thin as object layers'; +} +{ + my $config = Slic3r::Config->new_from_defaults; + $config->set('support_material_enforce_layers', 100); + $config->set('support_material', 0); + my @contact_z = my @top_z = (); + + my $test = sub { + my $print = Slic3r::Test::init_print('20mm_cube', config => $config); + my $flow = $print->print->objects->[0]->support_material_flow; + my $support = Slic3r::Print::SupportMaterial->new( + object_config => $print->print->objects->[0]->config, + print_config => $print->print->config, + flow => $flow, + interface_flow => $flow, + first_layer_flow => $flow, + ); + my $support_z = $support->support_layers_z(\@contact_z, \@top_z, $config->layer_height); + + is scalar(grep { $support_z->[$_]-$support_z->[$_-1] <= 0 } 1..$#$support_z), 0, + 'forced support is generated'; + + }; + $config->set('layer_height', 0.2); + $config->set('first_layer_height', 0.3); + @contact_z = (1.9); + @top_z = (1.1); + $test->(); +} __END__ diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp index 598423265..62beb24f0 100644 --- a/xs/src/libslic3r/SupportMaterial.cpp +++ b/xs/src/libslic3r/SupportMaterial.cpp @@ -28,7 +28,7 @@ SupportMaterial::generate_toolpaths(PrintObject *object, params.circle = create_circle(params.circle_radius); #ifdef SLIC3R_DEBUG - printf("Generating patterns\n"); + printf("Generating patterns.\n"); #endif // Prepare fillers. @@ -448,7 +448,7 @@ SupportMaterial::contact_area(PrintObject *object) if (difference.empty()) continue; // NOTE: this is not the full overhang as it misses the outermost half of the perimeter width! - append_polygons(tmp_overhang, difference); + append_to(tmp_overhang, difference); // 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. @@ -473,7 +473,7 @@ SupportMaterial::contact_area(PrintObject *object) ); } } - append_polygons(tmp_contact, difference); + append_to(tmp_contact, difference); } } if (tmp_contact.empty()) @@ -666,22 +666,22 @@ SupportMaterial::generate_base_layers(vector support_z, // (1 interface layer means we only have contact layer, so $interface->{$i+1} is empty). Polygons upper_contact; if (object_config->support_material_interface_layers.value <= 1) { - append_polygons(upper_contact, (i + 1 < support_z.size() ? contact[support_z[i + 1]] : contact[-1])); + append_to(upper_contact, (i + 1 < support_z.size() ? contact[support_z[i + 1]] : contact[-1])); } Polygons ps_1; - append_polygons(ps_1, base[i + 1]); // support regions on upper layer. - append_polygons(ps_1, interface[i + 1]); // interface regions on upper layer - append_polygons(ps_1, upper_contact); // contact regions on upper layer + append_to(ps_1, base[i + 1]); // support regions on upper layer. + append_to(ps_1, interface[i + 1]); // interface regions on upper layer + append_to(ps_1, upper_contact); // contact regions on upper layer Polygons ps_2; for (auto el : overlapping_z) { if (top.count(el) > 0) - append_polygons(ps_2, top[el]); // top slices on this layer. + append_to(ps_2, top[el]); // top slices on this layer. if (interface.count(el) > 0) - append_polygons(ps_2, interface[el]); // interface regions on this layer. + append_to(ps_2, interface[el]); // interface regions on this layer. if (contact.count(el) > 0) - append_polygons(ps_2, contact[el]); // contact regions on this layer. + append_to(ps_2, contact[el]); // contact regions on this layer. } base[i] = diff( @@ -725,15 +725,15 @@ SupportMaterial::generate_interface_layers(vector support_z, // surfaces vertically before performing the diff, but this needs // investigation. Polygons ps_1; - append_polygons(ps_1, _contact); // clipped projection of the current contact regions. - append_polygons(ps_1, interface[i]); // interface regions already applied to this layer. + append_to(ps_1, _contact); // clipped projection of the current contact regions. + append_to(ps_1, interface[i]); // interface regions already applied to this layer. Polygons ps_2; for (auto el : overlapping_z) { if (top.count(el) > 0) - append_polygons(ps_2, top[el]); // top slices on this layer. + append_to(ps_2, top[el]); // top slices on this layer. if (contact.count(el) > 0) - append_polygons(ps_2, contact[el]); // contact regions on this layer. + append_to(ps_2, contact[el]); // contact regions on this layer. } _contact = interface[i] = diff( @@ -792,7 +792,7 @@ SupportMaterial::generate_bottom_interface_layers(const vector &suppor ); // Add the new interface area to interface. - append_polygons(interface[layer_id], interface_area); + append_to(interface[layer_id], interface_area); } interface_layers++; diff --git a/xs/src/libslic3r/SupportMaterial.hpp b/xs/src/libslic3r/SupportMaterial.hpp index 4b1eda2f0..75668fa82 100644 --- a/xs/src/libslic3r/SupportMaterial.hpp +++ b/xs/src/libslic3r/SupportMaterial.hpp @@ -81,7 +81,7 @@ public: /// Generate support material for the given print object. void generate(PrintObject *object); - /// Generate the support layers z coordinates. (TODO Dicuss more). + /// Generate the support layers slicing z coordinates. vector support_layers_z(vector contact_z, vector top_z, coordf_t max_object_layer_height); @@ -140,7 +140,7 @@ private: // Get the maximum layer height given a print object. coordf_t get_max_layer_height(PrintObject *object); - // (Deprecated) use append_to instead TODO @Samir55. + // (Deprecated) use append_to instead void append_polygons(Polygons &dst, Polygons &src); // Return polygon vector given a vector of surfaces.