From 1ea958158a9e0b7aee9a5ed006e81a7fa009d9ab Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 12 Dec 2016 17:53:38 +0100 Subject: Support for user definable variable layer thickness, the C++ backend. --- xs/src/libslic3r/Print.hpp | 26 +- xs/src/libslic3r/PrintObject.cpp | 213 ++++++++++++- xs/src/libslic3r/Slicing.cpp | 585 +++++++++++++++++++++++++++++++++++ xs/src/libslic3r/Slicing.hpp | 74 ++++- xs/src/libslic3r/SlicingAdaptive.cpp | 39 +-- xs/src/libslic3r/SlicingAdaptive.hpp | 6 +- 6 files changed, 900 insertions(+), 43 deletions(-) create mode 100644 xs/src/libslic3r/Slicing.cpp (limited to 'xs/src/libslic3r') diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index 3a5d38ec3..f97f888cd 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -3,9 +3,8 @@ #include "libslic3r.h" #include -#include #include -#include +#include #include "BoundingBox.hpp" #include "Flow.hpp" #include "PrintConfig.hpp" @@ -13,7 +12,7 @@ #include "Layer.hpp" #include "Model.hpp" #include "PlaceholderParser.hpp" - +#include "Slicing.hpp" namespace Slic3r { @@ -79,6 +78,10 @@ public: std::map< size_t,std::vector > region_volumes; PrintObjectConfig config; t_layer_height_ranges layer_height_ranges; + + // Profile of increasing z to a layer height, to be linearly interpolated when calculating the layers. + // The pairs of are packed into a 1D array to simplify handling by the Perl XS. + std::vector layer_height_profile; // this is set to true when LayerRegion->slices is split in top/internal/bottom // so that next call to make_perimeters() performs a union() before computing loops @@ -137,7 +140,18 @@ public: bool invalidate_state_by_config_options(const std::vector &opt_keys); bool invalidate_step(PrintObjectStep step); bool invalidate_all_steps(); - + + // Process layer_height_ranges, the raft layers and first layer thickness into layer_height_profile. + // The layer_height_profile may be later modified interactively by the user to refine layers at sloping surfaces. + void update_layer_height_profile(); + + // Collect the slicing parameters, to be used by variable layer thickness algorithm, + // by the interactive layer height editor and by the printing process itself. + // The slicing parameters are dependent on various configuration values + // (layer height, first layer height, raft settings, print nozzle diameter etc). + SlicingParameters slicing_parameters() const; + + void _slice(); bool has_support_material() const; void detect_surfaces_type(); void process_external_surfaces(); @@ -145,7 +159,7 @@ public: void bridge_over_infill(); void _make_perimeters(); void _infill(); - + private: Print* _print; ModelObject* _model_object; @@ -155,6 +169,8 @@ private: // parameter PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox); ~PrintObject() {} + + std::vector _slice_region(size_t region_id, const std::vector &z, bool modifier); }; typedef std::vector PrintObjectPtrs; diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index 7b5014414..90449d64f 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -2,12 +2,23 @@ #include "BoundingBox.hpp" #include "ClipperUtils.hpp" #include "Geometry.hpp" -#include "SVG.hpp" #include #include +// #define SLIC3R_DEBUG + +// Make assert active if SLIC3R_DEBUG +#ifdef SLIC3R_DEBUG + #undef NDEBUG + #define DEBUG + #define _DEBUG + #include "SVG.hpp" + #undef assert + #include +#endif + namespace Slic3r { PrintObject::PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox) @@ -760,9 +771,9 @@ PrintObject::discover_vertical_shells() // Assign resulting internal surfaces to layer. const SurfaceType surfaceTypesKeep[] = { stTop, stBottom, stBottomBridge }; layerm->fill_surfaces.keep_types(surfaceTypesKeep, sizeof(surfaceTypesKeep)/sizeof(SurfaceType)); - layerm->fill_surfaces.append(stInternal , new_internal); - layerm->fill_surfaces.append(stInternalVoid , new_internal_void); - layerm->fill_surfaces.append(stInternalSolid, new_internal_solid); + layerm->fill_surfaces.append(new_internal, stInternal); + layerm->fill_surfaces.append(new_internal_void, stInternalVoid); + layerm->fill_surfaces.append(new_internal_solid, stInternalSolid); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING layerm->export_region_slices_to_svg_debug("4_discover_vertical_shells"); @@ -910,6 +921,194 @@ PrintObject::bridge_over_infill() } } +SlicingParameters PrintObject::slicing_parameters() const +{ + return SlicingParameters::create_from_config( + this->print()->config, this->config, + unscale(this->size.z), this->print()->object_extruders()); +} + +void PrintObject::update_layer_height_profile() +{ + if (this->layer_height_profile.empty()) { + if (0) +// if (this->layer_height_profile.empty()) + this->layer_height_profile = layer_height_profile_adaptive(this->slicing_parameters(), this->layer_height_ranges, + this->model_object()->volumes); + else + this->layer_height_profile = layer_height_profile_from_ranges(this->slicing_parameters(), this->layer_height_ranges); + } +} + +// 1) Decides Z positions of the layers, +// 2) Initializes layers and their regions +// 3) Slices the object meshes +// 4) Slices the modifier meshes and reclassifies the slices of the object meshes by the slices of the modifier meshes +// 5) Applies size compensation (offsets the slices in XY plane) +// 6) Replaces bad slices by the slices reconstructed from the upper/lower layer +// Resulting expolygons of layer regions are marked as Internal. +// +// this should be idempotent +void PrintObject::_slice() +{ + SlicingParameters slicing_params = this->slicing_parameters(); + + // 1) Initialize layers and their slice heights. + std::vector slice_zs; + { + this->clear_layers(); + // Object layers (pairs of bottom/top Z coordinate), without the raft. + this->update_layer_height_profile(); + std::vector object_layers = generate_object_layers(slicing_params, this->layer_height_profile); + // Reserve object layers for the raft. Last layer of the raft is the contact layer. + int id = int(slicing_params.raft_layers()); + slice_zs.reserve(object_layers.size()); + Layer *prev = nullptr; + for (size_t i_layer = 0; i_layer < object_layers.size(); i_layer += 2) { + coordf_t lo = object_layers[i_layer]; + coordf_t hi = object_layers[i_layer + 1]; + coordf_t slice_z = 0.5 * (lo + hi); + Layer *layer = this->add_layer(id ++, hi - lo, hi + slicing_params.object_print_z_min, slice_z); + slice_zs.push_back(float(slice_z)); + if (prev != nullptr) { + prev->upper_layer = layer; + layer->lower_layer = prev; + } + // Make sure all layers contain layer region objects for all regions. + for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) + layer->add_region(this->print()->regions[region_id]); + prev = layer; + } + } + + if (this->print()->regions.size() == 1) { + // Optimized for a single region. Slice the single non-modifier mesh. + std::vector expolygons_by_layer = this->_slice_region(0, slice_zs, false); + for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id) + this->layers[layer_id]->regions.front()->slices.append(std::move(expolygons_by_layer[layer_id]), stInternal); + } else { + // Slice all non-modifier volumes. + for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { + std::vector expolygons_by_layer = this->_slice_region(region_id, slice_zs, false); + for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id) + this->layers[layer_id]->regions[region_id]->slices.append(std::move(expolygons_by_layer[layer_id]), stInternal); + } + // Slice all modifier volumes. + for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) { + std::vector expolygons_by_layer = this->_slice_region(region_id, slice_zs, true); + // loop through the other regions and 'steal' the slices belonging to this one + for (size_t other_region_id = 0; other_region_id < this->print()->regions.size(); ++ other_region_id) { + if (region_id == other_region_id) + continue; + for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id) { + Layer *layer = layers[layer_id]; + LayerRegion *layerm = layer->regions[region_id]; + LayerRegion *other_layerm = layer->regions[other_region_id]; + if (layerm == nullptr || other_layerm == nullptr) + continue; + Polygons other_slices = to_polygons(other_layerm->slices); + ExPolygons my_parts = intersection_ex(other_slices, to_polygons(expolygons_by_layer[layer_id])); + if (my_parts.empty()) + continue; + // Remove such parts from original region. + other_layerm->slices.set(diff_ex(other_slices, my_parts), stInternal); + // Append new parts to our region. + layerm->slices.append(std::move(my_parts), stInternal); + } + } + } + } + + // remove last layer(s) if empty + while (! this->layers.empty()) { + const Layer *layer = this->layers.back(); + for (size_t region_id = 0; region_id < this->print()->regions.size(); ++ region_id) + if (layer->regions[region_id] != nullptr && ! layer->regions[region_id]->slices.empty()) + // Non empty layer. + goto end; + this->delete_layer(int(this->layers.size()) - 1); + } +end: + ; + + for (size_t layer_id = 0; layer_id < layers.size(); ++ layer_id) { + Layer *layer = this->layers[layer_id]; + // apply size compensation + if (this->config.xy_size_compensation.value != 0.) { + float delta = float(scale_(this->config.xy_size_compensation.value)); + if (layer->regions.size() == 1) { + // single region + LayerRegion *layerm = layer->regions.front(); + layerm->slices.set(offset_ex(to_polygons(std::move(layerm->slices.surfaces)), delta), stInternal); + } else { + if (delta < 0) { + // multiple regions, shrinking + // we apply the offset to the combined shape, then intersect it + // with the original slices for each region + Polygons region_slices; + for (size_t region_id = 0; region_id < layer->regions.size(); ++ region_id) + polygons_append(region_slices, layer->regions[region_id]->slices.surfaces); + Polygons slices = offset(union_(region_slices), delta); + for (size_t region_id = 0; region_id < layer->regions.size(); ++ region_id) { + LayerRegion *layerm = layer->regions[region_id]; + layerm->slices.set(std::move(intersection_ex(slices, to_polygons(std::move(layerm->slices.surfaces)))), stInternal); + } + } else { + // multiple regions, growing + // this is an ambiguous case, since it's not clear how to grow regions where they are going to overlap + // so we give priority to the first one and so on + Polygons processed; + for (size_t region_id = 0;; ++ region_id) { + LayerRegion *layerm = layer->regions[region_id]; + ExPolygons slices = offset_ex(to_polygons(layerm->slices.surfaces), delta); + if (region_id > 0) + // Trim by the slices of already processed regions. + slices = diff_ex(to_polygons(std::move(slices)), processed); + if (region_id + 1 == layer->regions.size()) { + layerm->slices.set(std::move(slices), stInternal); + break; + } + polygons_append(processed, slices); + layerm->slices.set(std::move(slices), stInternal); + } + } + } + } + + // Merge all regions' slices to get islands, chain them by a shortest path. + layer->make_slices(); + } +} + +std::vector PrintObject::_slice_region(size_t region_id, const std::vector &z, bool modifier) +{ + std::vector layers; + assert(region_id < this->region_volumes.size()); + std::vector &volumes = this->region_volumes[region_id]; + if (! volumes.empty()) { + // Compose mesh. + //FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them. + TriangleMesh mesh; + for (std::vector::const_iterator it_volume = volumes.begin(); it_volume != volumes.end(); ++ it_volume) { + ModelVolume *volume = this->model_object()->volumes[*it_volume]; + if (volume->modifier == modifier) + mesh.merge(volume->mesh); + } + if (mesh.stl.stats.number_of_facets > 0) { + // transform mesh + // we ignore the per-instance transformations currently and only + // consider the first one + this->model_object()->instances.front()->transform_mesh(&mesh, true); + // align mesh to Z = 0 (it should be already aligned actually) and apply XY shift + mesh.translate(- unscale(this->_copies_shift.x), - unscale(this->_copies_shift.y), -this->model_object()->bounding_box().min.z); + // perform actual slicing + TriangleMeshSlicer mslicer(&mesh); + mslicer.slice(z, &layers); + } + } + return layers; +} + void PrintObject::_make_perimeters() { @@ -930,7 +1129,7 @@ PrintObject::_make_perimeters() // this algorithm makes sure that at least one perimeter is overlapping // but we don't generate any extra perimeter if fill density is zero, as they would be floating // inside the object - infill_only_where_needed should be the method of choice for printing - // hollow objects + // hollow objects FOREACH_REGION(this->_print, region_it) { size_t region_id = region_it - this->_print->regions.begin(); const PrintRegion ®ion = **region_it; @@ -941,7 +1140,7 @@ PrintObject::_make_perimeters() || region.config.fill_density == 0 || this->layer_count() < 2) continue; - for (size_t i = 0; i <= (this->layer_count()-2); ++i) { + for (int i = 0; i < int(this->layer_count()) - 1; ++i) { LayerRegion &layerm = *this->get_layer(i)->get_region(region_id); const LayerRegion &upper_layerm = *this->get_layer(i+1)->get_region(region_id); const Polygons upper_layerm_polygons = upper_layerm.slices; @@ -1044,4 +1243,4 @@ PrintObject::_infill() this->state.set_done(posInfill); } -} +} // namespace Slic3r diff --git a/xs/src/libslic3r/Slicing.cpp b/xs/src/libslic3r/Slicing.cpp new file mode 100644 index 000000000..82813770f --- /dev/null +++ b/xs/src/libslic3r/Slicing.cpp @@ -0,0 +1,585 @@ +#include "Slicing.hpp" +#include "SlicingAdaptive.hpp" +#include "PrintConfig.hpp" +#include "Model.hpp" + +// #define SLIC3R_DEBUG + +// Make assert active if SLIC3R_DEBUG +#ifdef SLIC3R_DEBUG + #undef NDEBUG + #define DEBUG + #define _DEBUG + #include "SVG.hpp" + #undef assert + #include +#endif + +namespace Slic3r +{ + +SlicingParameters create_from_config( + const PrintConfig &print_config, + const PrintObjectConfig &object_config, + coordf_t object_height, + const std::set &object_extruders) +{ + coordf_t first_layer_height = (object_config.first_layer_height.value <= 0) ? + object_config.layer_height.value : + object_config.first_layer_height.get_abs_value(object_config.layer_height.value); + coordf_t support_material_extruder_dmr = print_config.nozzle_diameter.get_at(object_config.support_material_extruder.value - 1); + coordf_t support_material_interface_extruder_dmr = print_config.nozzle_diameter.get_at(object_config.support_material_interface_extruder.value - 1); + bool soluble_interface = object_config.support_material_contact_distance.value == 0.; + + SlicingParameters params; + params.layer_height = object_config.layer_height.value; + params.first_object_layer_height = first_layer_height; + params.object_print_z_min = 0.; + params.object_print_z_max = object_height; + params.base_raft_layers = object_config.raft_layers.value; + + if (params.base_raft_layers > 0) { + params.interface_raft_layers = (params.base_raft_layers + 1) / 2; + params.base_raft_layers -= params.interface_raft_layers; + // Use as large as possible layer height for the intermediate raft layers. + params.base_raft_layer_height = std::max(params.layer_height, 0.75 * support_material_extruder_dmr); + params.interface_raft_layer_height = std::max(params.layer_height, 0.75 * support_material_interface_extruder_dmr); + params.contact_raft_layer_height_bridging = false; + params.first_object_layer_bridging = false; + #if 1 + params.contact_raft_layer_height = std::max(params.layer_height, 0.75 * support_material_interface_extruder_dmr); + if (! soluble_interface) { + // Compute the average of all nozzles used for printing the object over a raft. + //FIXME It is expected, that the 1st layer of the object is printed with a bridging flow over a full raft. Shall it not be vice versa? + coordf_t average_object_extruder_dmr = 0.; + if (! object_extruders.empty()) { + for (std::set::const_iterator it_extruder = object_extruders.begin(); it_extruder != object_extruders.end(); ++ it_extruder) + average_object_extruder_dmr += print_config.nozzle_diameter.get_at(*it_extruder); + average_object_extruder_dmr /= coordf_t(object_extruders.size()); + } + params.first_object_layer_height = average_object_extruder_dmr; + params.first_object_layer_bridging = true; + } + #else + params.contact_raft_layer_height = soluble_interface ? support_material_interface_extruder_dmr : 0.75 * support_material_interface_extruder_dmr; + params.contact_raft_layer_height_bridging = ! soluble_interface; + ... + #endif + } + + if (params.has_raft()) { + // Raise first object layer Z by the thickness of the raft itself plus the extra distance required by the support material logic. + //FIXME The last raft layer is the contact layer, which shall be printed with a bridging flow for ease of separation. Currently it is not the case. + coordf_t print_z = first_layer_height + object_config.support_material_contact_distance.value; + if (params.raft_layers() == 1) { + params.contact_raft_layer_height = first_layer_height; + } else { + print_z += + // Number of the base raft layers is decreased by the first layer, which has already been added to print_z. + coordf_t(params.base_raft_layers - 1) * params.base_raft_layer_height + + // Number of the interface raft layers is decreased by the contact layer. + coordf_t(params.interface_raft_layers - 1) * params.interface_raft_layer_height + + params.contact_raft_layer_height; + } + params.object_print_z_min = print_z; + params.object_print_z_max += print_z; + } + + params.min_layer_height = std::min(params.layer_height, first_layer_height); + params.max_layer_height = std::max(params.layer_height, first_layer_height); + + //FIXME add it to the print configuration + params.min_layer_height = 0.05; + + // Calculate the maximum layer height as 0.75 from the minimum nozzle diameter. + if (! object_extruders.empty()) { + coordf_t min_object_extruder_dmr = 1000000.; + for (std::set::const_iterator it_extruder = object_extruders.begin(); it_extruder != object_extruders.end(); ++ it_extruder) + min_object_extruder_dmr = std::min(min_object_extruder_dmr, print_config.nozzle_diameter.get_at(*it_extruder)); + // Allow excessive maximum layer height higher than 0.75 * min_object_extruder_dmr + params.max_layer_height = std::max(std::max(params.layer_height, first_layer_height), 0.75 * min_object_extruder_dmr); + } + + return params; +} + +// Convert layer_height_ranges to layer_height_profile. Both are referenced to z=0, meaning the raft layers are not accounted for +// in the height profile and the printed object may be lifted by the raft thickness at the time of the G-code generation. +std::vector layer_height_profile_from_ranges( + const SlicingParameters &slicing_params, + const t_layer_height_ranges &layer_height_ranges) +{ + // 1) If there are any height ranges, trim one by the other to make them non-overlapping. Insert the 1st layer if fixed. + std::vector> ranges_non_overlapping; + ranges_non_overlapping.reserve(layer_height_ranges.size() * 4); + if (slicing_params.first_object_layer_height_fixed()) + ranges_non_overlapping.push_back(std::pair( + t_layer_height_range(0., slicing_params.first_object_layer_height), + slicing_params.first_object_layer_height)); + // The height ranges are sorted lexicographically by low / high layer boundaries. + for (t_layer_height_ranges::const_iterator it_range = layer_height_ranges.begin(); it_range != layer_height_ranges.end(); ++ it_range) { + coordf_t lo = it_range->first.first; + coordf_t hi = std::min(it_range->first.second, slicing_params.object_print_z_height()); + coordf_t height = it_range->second; + if (! ranges_non_overlapping.empty()) + // Trim current low with the last high. + lo = std::max(lo, ranges_non_overlapping.back().first.second); + if (lo + EPSILON < hi) + // Ignore too narrow ranges. + ranges_non_overlapping.push_back(std::pair(t_layer_height_range(lo, hi), height)); + } + + // 2) Convert the trimmed ranges to a height profile, fill in the undefined intervals between z=0 and z=slicing_params.object_print_z_max() + // with slicing_params.layer_height + std::vector layer_height_profile; + for (std::vector>::const_iterator it_range = ranges_non_overlapping.begin(); it_range != ranges_non_overlapping.end(); ++ it_range) { + coordf_t lo = it_range->first.first; + coordf_t hi = it_range->first.second; + coordf_t height = it_range->second; + coordf_t last_z = layer_height_profile.empty() ? 0. : layer_height_profile[layer_height_profile.size() - 2]; + coordf_t last_height = layer_height_profile.empty() ? 0. : layer_height_profile[layer_height_profile.size() - 1]; + if (lo > last_z + EPSILON) { + // Insert a step of normal layer height. + layer_height_profile.push_back(last_z); + layer_height_profile.push_back(slicing_params.layer_height); + layer_height_profile.push_back(lo); + layer_height_profile.push_back(slicing_params.layer_height); + } + // Insert a step of the overriden layer height. + layer_height_profile.push_back(lo); + layer_height_profile.push_back(height); + layer_height_profile.push_back(hi); + layer_height_profile.push_back(height); + } + + coordf_t last_z = layer_height_profile.empty() ? 0. : layer_height_profile[layer_height_profile.size() - 2]; + coordf_t last_height = layer_height_profile.empty() ? 0. : layer_height_profile[layer_height_profile.size() - 1]; + if (last_z < slicing_params.object_print_z_height()) { + // Insert a step of normal layer height up to the object top. + layer_height_profile.push_back(last_z); + layer_height_profile.push_back(slicing_params.layer_height); + layer_height_profile.push_back(slicing_params.object_print_z_height()); + layer_height_profile.push_back(slicing_params.layer_height); + } + + return layer_height_profile; +} + +// Based on the work of @platsch +// Fill layer_height_profile by heights ensuring a prescribed maximum cusp height. +std::vector layer_height_profile_adaptive( + const SlicingParameters &slicing_params, + const t_layer_height_ranges &layer_height_ranges, + const ModelVolumePtrs &volumes) +{ + // 1) Initialize the SlicingAdaptive class with the object meshes. + SlicingAdaptive as; + as.set_slicing_parameters(slicing_params); + for (ModelVolumePtrs::const_iterator it = volumes.begin(); it != volumes.end(); ++ it) + if (! (*it)->modifier) + as.add_mesh(&(*it)->mesh); + as.prepare(); + + // 2) Generate layers using the algorithm of @platsch + // loop until we have at least one layer and the max slice_z reaches the object height + //FIXME make it configurable + // Cusp value: A maximum allowed distance from a corner of a rectangular extrusion to a chrodal line, in mm. + const coordf_t cusp_value = 0.2; // $self->config->get_value('cusp_value'); + + std::vector layer_height_profile; + layer_height_profile.push_back(0.); + layer_height_profile.push_back(slicing_params.first_object_layer_height); + if (slicing_params.first_object_layer_height_fixed()) { + layer_height_profile.push_back(slicing_params.first_object_layer_height); + layer_height_profile.push_back(slicing_params.first_object_layer_height); + } + coordf_t slice_z = slicing_params.first_object_layer_height; + coordf_t height = slicing_params.first_object_layer_height; + coordf_t cusp_height = 0.; + int current_facet = 0; + while ((slice_z - height) <= slicing_params.object_print_z_height()) { + height = 999; + // Slic3r::debugf "\n Slice layer: %d\n", $id; + // determine next layer height + coordf_t cusp_height = as.cusp_height(slice_z, cusp_value, current_facet); + // check for horizontal features and object size + /* + if($self->config->get_value('match_horizontal_surfaces')) { + my $horizontal_dist = $adaptive_slicing[$region_id]->horizontal_facet_distance(scale $slice_z+$cusp_height, $min_height); + if(($horizontal_dist < $min_height) && ($horizontal_dist > 0)) { + Slic3r::debugf "Horizontal feature ahead, distance: %f\n", $horizontal_dist; + # can we shrink the current layer a bit? + if($cusp_height-($min_height-$horizontal_dist) > $min_height) { + # yes we can + $cusp_height = $cusp_height-($min_height-$horizontal_dist); + Slic3r::debugf "Shrink layer height to %f\n", $cusp_height; + }else{ + # no, current layer would become too thin + $cusp_height = $cusp_height+$horizontal_dist; + Slic3r::debugf "Widen layer height to %f\n", $cusp_height; + } + } + } + */ + height = std::min(cusp_height, height); + + // apply z-gradation + /* + my $gradation = $self->config->get_value('adaptive_slicing_z_gradation'); + if($gradation > 0) { + $height = $height - unscale((scale($height)) % (scale($gradation))); + } + */ + + // look for an applicable custom range + /* + if (my $range = first { $_->[0] <= $slice_z && $_->[1] > $slice_z } @{$self->layer_height_ranges}) { + $height = $range->[2]; + + # if user set custom height to zero we should just skip the range and resume slicing over it + if ($height == 0) { + $slice_z += $range->[1] - $range->[0]; + next; + } + } + */ + + layer_height_profile.push_back(slice_z); + layer_height_profile.push_back(height); + slice_z += height; + layer_height_profile.push_back(slice_z); + layer_height_profile.push_back(height); + } + + coordf_t last = std::max(slicing_params.first_object_layer_height, layer_height_profile[layer_height_profile.size() - 2]); + layer_height_profile.push_back(last); + layer_height_profile.push_back(slicing_params.first_object_layer_height); + layer_height_profile.push_back(slicing_params.object_print_z_height()); + layer_height_profile.push_back(slicing_params.first_object_layer_height); + + return layer_height_profile; +} + +template +static inline T clamp(const T low, const T high, const T value) +{ + return std::max(low, std::min(high, value)); +} + +template +static inline T lerp(const T a, const T b, const T t) +{ + assert(t >= T(-EPSILON) && t <= T(1.+EPSILON)); + return (1. - t) * a + t * b; +} + +void adjust_layer_height_profile( + const SlicingParameters &slicing_params, + std::vector &layer_height_profile, + coordf_t z, + coordf_t layer_thickness_delta, + coordf_t band_width, + int action) +{ + // Constrain the profile variability by the 1st layer height. + std::pair z_span_variable = + std::pair( + slicing_params.first_object_layer_height_fixed() ? slicing_params.first_object_layer_height : 0., + slicing_params.object_print_z_height()); + if (z < z_span_variable.first || z > z_span_variable.second) + return; + + assert(layer_height_profile.size() >= 2); + + // 1) Get the current layer thickness at z. + coordf_t current_layer_height = slicing_params.layer_height; + for (size_t i = 0; i < layer_height_profile.size(); i += 2) { + if (i + 2 == layer_height_profile.size()) { + current_layer_height = layer_height_profile[i + 1]; + break; + } else if (layer_height_profile[i + 2] > z) { + coordf_t z1 = layer_height_profile[i]; + coordf_t h1 = layer_height_profile[i + 1]; + coordf_t z2 = layer_height_profile[i + 2]; + coordf_t h2 = layer_height_profile[i + 3]; + current_layer_height = lerp(h1, h2, (z - z1) / (z2 - z1)); + break; + } + } + + // 2) Is it possible to apply the delta? + switch (action) { + case 0: + default: + if (layer_thickness_delta > 0) { + if (current_layer_height >= slicing_params.max_layer_height - EPSILON) + return; + layer_thickness_delta = std::min(layer_thickness_delta, slicing_params.max_layer_height - current_layer_height); + } else { + if (current_layer_height <= slicing_params.min_layer_height + EPSILON) + return; + layer_thickness_delta = std::max(layer_thickness_delta, slicing_params.min_layer_height - current_layer_height); + } + break; + case 1: + layer_thickness_delta = std::abs(layer_thickness_delta); + layer_thickness_delta = std::min(layer_thickness_delta, std::abs(slicing_params.layer_height - current_layer_height)); + if (layer_thickness_delta < EPSILON) + return; + break; + } + + // 3) Densify the profile inside z +- band_width/2, remove duplicate Zs from the height profile inside the band. + coordf_t lo = std::max(z_span_variable.first, z - 0.5 * band_width); + coordf_t hi = std::min(z_span_variable.second, z + 0.5 * band_width); + coordf_t z_step = 0.1; + size_t i = 0; + while (i < layer_height_profile.size() && layer_height_profile[i] < lo) + i += 2; + i -= 2; + + std::vector profile_new; + profile_new.reserve(layer_height_profile.size()); + assert(i >= 0 && i + 1 < layer_height_profile.size()); + profile_new.insert(profile_new.end(), layer_height_profile.begin(), layer_height_profile.begin() + i + 2); + coordf_t zz = lo; + while (zz < hi) { + size_t next = i + 2; + coordf_t z1 = layer_height_profile[i]; + coordf_t h1 = layer_height_profile[i + 1]; + coordf_t height = h1; + if (next < layer_height_profile.size()) { + coordf_t z2 = layer_height_profile[next]; + coordf_t h2 = layer_height_profile[next + 1]; + height = lerp(h1, h2, (zz - z1) / (z2 - z1)); + } + // Adjust height by layer_thickness_delta. + coordf_t weight = std::abs(zz - z) < 0.5 * band_width ? (0.5 + 0.5 * cos(2. * M_PI * (zz - z) / band_width)) : 0.; + coordf_t height_new = height; + switch (action) { + case 0: + default: + height += weight * layer_thickness_delta; + break; + case 1: + { + coordf_t delta = height - slicing_params.layer_height; + coordf_t step = weight * layer_thickness_delta; + step = (std::abs(delta) > step) ? + (delta > 0) ? -step : step : + -delta; + height += step; + break; + } + } + // Avoid entering a too short segment. + if (profile_new[profile_new.size() - 2] + EPSILON < zz) { + profile_new.push_back(zz); + profile_new.push_back(clamp(slicing_params.min_layer_height, slicing_params.max_layer_height, height)); + } + zz += z_step; + i = next; + while (i < layer_height_profile.size() && layer_height_profile[i] < zz) + i += 2; + i -= 2; + } + + i += 2; + if (i < layer_height_profile.size()) { + if (profile_new[profile_new.size() - 2] + z_step < layer_height_profile[i]) { + profile_new.push_back(profile_new[profile_new.size() - 2] + z_step); + profile_new.push_back(layer_height_profile[i + 1]); + } + profile_new.insert(profile_new.end(), layer_height_profile.begin() + i, layer_height_profile.end()); + } + layer_height_profile = std::move(profile_new); + + assert(layer_height_profile.size() > 2); + assert(layer_height_profile.size() % 2 == 0); + assert(layer_height_profile[0] == 0.); +#ifdef _DEBUG + for (size_t i = 2; i < layer_height_profile.size(); i += 2) + assert(layer_height_profile[i - 2] <= layer_height_profile[i]); + for (size_t i = 1; i < layer_height_profile.size(); i += 2) { + assert(layer_height_profile[i] > slicing_params.min_layer_height - EPSILON); + assert(layer_height_profile[i] < slicing_params.max_layer_height + EPSILON); + } +#endif /* _DEBUG */ +} + +// Produce object layers as pairs of low / high layer boundaries, stored into a linear vector. +std::vector generate_object_layers( + const SlicingParameters &slicing_params, + const std::vector &layer_height_profile) +{ + coordf_t print_z = 0; + coordf_t height = 0; + + std::vector out; + + if (slicing_params.first_object_layer_height_fixed()) { + out.push_back(0); + print_z = slicing_params.first_object_layer_height; + out.push_back(print_z); + } + + size_t idx_layer_height_profile = 0; + // loop until we have at least one layer and the max slice_z reaches the object height + coordf_t slice_z = print_z + 0.5 * slicing_params.min_layer_height; + while (slice_z < slicing_params.object_print_z_height()) { + height = slicing_params.min_layer_height; + if (idx_layer_height_profile < layer_height_profile.size()) { + size_t next = idx_layer_height_profile + 2; + for (;;) { + if (next >= layer_height_profile.size() || slice_z < layer_height_profile[next]) + break; + idx_layer_height_profile = next; + next += 2; + } + coordf_t z1 = layer_height_profile[idx_layer_height_profile]; + coordf_t h1 = layer_height_profile[idx_layer_height_profile + 1]; + height = h1; + if (next < layer_height_profile.size()) { + coordf_t z2 = layer_height_profile[next]; + coordf_t h2 = layer_height_profile[next + 1]; + height = lerp(h1, h2, (slice_z - z1) / (z2 - z1)); + assert(height >= slicing_params.min_layer_height - EPSILON && height <= slicing_params.max_layer_height + EPSILON); + } + } + slice_z = print_z + 0.5 * height; + if (slice_z >= slicing_params.object_print_z_height()) + break; + assert(height > slicing_params.min_layer_height - EPSILON); + assert(height < slicing_params.max_layer_height + EPSILON); + out.push_back(print_z); + print_z += height; + slice_z = print_z + 0.5 * slicing_params.min_layer_height; + out.push_back(print_z); + } + + //FIXME Adjust the last layer to align with the top object layer exactly? + return out; +} + +int generate_layer_height_texture( + const SlicingParameters &slicing_params, + const std::vector &layers, + void *data, int rows, int cols, bool level_of_detail_2nd_level) +{ +// https://github.com/aschn/gnuplot-colorbrewer + std::vector palette_raw; + palette_raw.push_back(Point3(0x0B2, 0x018, 0x02B)); + palette_raw.push_back(Point3(0x0D6, 0x060, 0x04D)); + palette_raw.push_back(Point3(0x0F4, 0x0A5, 0x082)); + palette_raw.push_back(Point3(0x0FD, 0x0DB, 0x0C7)); + palette_raw.push_back(Point3(0x0D1, 0x0E5, 0x0F0)); + palette_raw.push_back(Point3(0x092, 0x0C5, 0x0DE)); + palette_raw.push_back(Point3(0x043, 0x093, 0x0C3)); + palette_raw.push_back(Point3(0x021, 0x066, 0x0AC)); + + // Clear the main texture and the 2nd LOD level. + memset(data, 0, rows * cols * 5); + // 2nd LOD level data start + unsigned char *data1 = reinterpret_cast(data) + rows * cols * 4; + int ncells = std::min((cols-1) * rows, int(ceil(16. * (slicing_params.object_print_z_height() / slicing_params.min_layer_height)))); + int ncells1 = ncells / 2; + int cols1 = cols / 2; + coordf_t z_to_cell = coordf_t(ncells-1) / slicing_params.object_print_z_height(); + coordf_t cell_to_z = slicing_params.object_print_z_height() / coordf_t(ncells-1); + coordf_t z_to_cell1 = coordf_t(ncells1-1) / slicing_params.object_print_z_height(); + coordf_t cell_to_z1 = slicing_params.object_print_z_height() / coordf_t(ncells1-1); + // for color scaling + coordf_t hscale = 2.f * std::max(slicing_params.max_layer_height - slicing_params.layer_height, slicing_params.layer_height - slicing_params.min_layer_height); + if (hscale == 0) + // All layers have the same height. Provide some height scale to avoid division by zero. + hscale = slicing_params.layer_height; + for (size_t idx_layer = 0; idx_layer < layers.size(); idx_layer += 2) { + coordf_t lo = layers[idx_layer]; + coordf_t hi = layers[idx_layer + 1]; + coordf_t mid = 0.5f * (lo + hi); + assert(mid <= slicing_params.object_print_z_height()); + coordf_t h = hi - lo; + hi = std::min(hi, slicing_params.object_print_z_height()); + int cell_first = clamp(0, ncells-1, int(ceil(lo * z_to_cell))); + int cell_last = clamp(0, ncells-1, int(floor(hi * z_to_cell))); + for (int cell = cell_first; cell <= cell_last; ++ cell) { + coordf_t idxf = (0.5 * hscale + (h - slicing_params.layer_height)) * coordf_t(palette_raw.size()) / hscale; + int idx1 = clamp(0, int(palette_raw.size() - 1), int(floor(idxf))); + int idx2 = std::min(int(palette_raw.size() - 1), idx1 + 1); + coordf_t t = idxf - coordf_t(idx1); + const Point3 &color1 = palette_raw[idx1]; + const Point3 &color2 = palette_raw[idx2]; + + coordf_t z = cell_to_z * coordf_t(cell); + assert(z >= lo && z <= hi); + // Intensity profile to visualize the layers. + coordf_t intensity = cos(M_PI * 0.7 * (mid - z) / h); + + // Color mapping from layer height to RGB. + Pointf3 color( + intensity * lerp(coordf_t(color1.x), coordf_t(color2.x), t), + intensity * lerp(coordf_t(color1.y), coordf_t(color2.y), t), + intensity * lerp(coordf_t(color1.z), coordf_t(color2.z), t)); + + int row = cell / (cols - 1); + int col = cell - row * (cols - 1); + assert(row >= 0 && row < rows); + assert(col >= 0 && col < cols); + unsigned char *ptr = (unsigned char*)data + (row * cols + col) * 4; + ptr[0] = clamp(0, 255, int(floor(color.x + 0.5))); + ptr[1] = clamp(0, 255, int(floor(color.y + 0.5))); + ptr[2] = clamp(0, 255, int(floor(color.z + 0.5))); + ptr[3] = 255; + if (col == 0 && row > 0) { + // Duplicate the first value in a row as a last value of the preceding row. + ptr[-4] = ptr[0]; + ptr[-3] = ptr[1]; + ptr[-2] = ptr[2]; + ptr[-1] = ptr[3]; + } + } + if (level_of_detail_2nd_level) { + cell_first = clamp(0, ncells1-1, int(ceil(lo * z_to_cell1))); + cell_last = clamp(0, ncells1-1, int(floor(hi * z_to_cell1))); + for (int cell = cell_first; cell <= cell_last; ++ cell) { + coordf_t idxf = (0.5 * hscale + (h - slicing_params.layer_height)) * coordf_t(palette_raw.size()) / hscale; + int idx1 = clamp(0, int(palette_raw.size() - 1), int(floor(idxf))); + int idx2 = std::min(int(palette_raw.size() - 1), idx1 + 1); + coordf_t t = idxf - coordf_t(idx1); + const Point3 &color1 = palette_raw[idx1]; + const Point3 &color2 = palette_raw[idx2]; + + coordf_t z = cell_to_z1 * coordf_t(cell); + assert(z >= lo && z <= hi); + + // Color mapping from layer height to RGB. + Pointf3 color( + lerp(coordf_t(color1.x), coordf_t(color2.x), t), + lerp(coordf_t(color1.y), coordf_t(color2.y), t), + lerp(coordf_t(color1.z), coordf_t(color2.z), t)); + + int row = cell / (cols1 - 1); + int col = cell - row * (cols1 - 1); + assert(row >= 0 && row < rows/2); + assert(col >= 0 && col < cols/2); + unsigned char *ptr = data1 + (row * cols1 + col) * 4; + ptr[0] = clamp(0, 255, int(floor(color.x + 0.5))); + ptr[1] = clamp(0, 255, int(floor(color.y + 0.5))); + ptr[2] = clamp(0, 255, int(floor(color.z + 0.5))); + ptr[3] = 255; + if (col == 0 && row > 0) { + // Duplicate the first value in a row as a last value of the preceding row. + ptr[-4] = ptr[0]; + ptr[-3] = ptr[1]; + ptr[-2] = ptr[2]; + ptr[-1] = ptr[3]; + } + } + } + } + + // Returns number of cells of the 0th LOD level. + return ncells; +} + +}; // namespace Slic3r diff --git a/xs/src/libslic3r/Slicing.hpp b/xs/src/libslic3r/Slicing.hpp index 5e910c672..349be2e4a 100644 --- a/xs/src/libslic3r/Slicing.hpp +++ b/xs/src/libslic3r/Slicing.hpp @@ -3,15 +3,52 @@ #ifndef slic3r_Slicing_hpp_ #define slic3r_Slicing_hpp_ -#include "libslic3r.h" +#include +#include +#include "libslic3r.h" namespace Slic3r { +class PrintConfig; +class PrintObjectConfig; +class ModelVolume; +typedef std::vector ModelVolumePtrs; + +// Parameters to guide object slicing and support generation. +// The slicing parameters account for a raft and whether the 1st object layer is printed with a normal or a bridging flow +// (using a normal flow over a soluble support, using a bridging flow over a non-soluble support). struct SlicingParameters { SlicingParameters() { memset(this, 0, sizeof(SlicingParameters)); } + static SlicingParameters create_from_config( + const PrintConfig &print_config, + const PrintObjectConfig &object_config, + coordf_t object_height, + const std::set &object_extruders); + + // Has any raft layers? + bool has_raft() const { return raft_layers() > 0; } + size_t raft_layers() const { return base_raft_layers + interface_raft_layers; } + + // Is the 1st object layer height fixed, or could it be varied? + bool first_object_layer_height_fixed() const { return ! has_raft() || first_object_layer_bridging; } + + // Height of the object to be printed. This value does not contain the raft height. + coordf_t object_print_z_height() const { return object_print_z_max - object_print_z_min; } + + // Number of raft layers. + size_t base_raft_layers; + // Number of interface layers including the contact layer. + size_t interface_raft_layers; + + // Layer heights of the raft (base, interface and a contact layer). + coordf_t base_raft_layer_height; + coordf_t interface_raft_layer_height; + coordf_t contact_raft_layer_height; + bool contact_raft_layer_height_bridging; + // The regular layer height, applied for all but the first layer, if not overridden by layer ranges // or by the variable layer thickness table. coordf_t layer_height; @@ -19,10 +56,10 @@ struct SlicingParameters // Thickness of the first layer. This is either the first print layer thickness if printed without a raft, // or a bridging flow thickness if printed over a non-soluble raft, // or a normal layer height if printed over a soluble raft. - coordf_t first_layer_height; + coordf_t first_object_layer_height; // If the object is printed over a non-soluble raft, the first layer may be printed with a briding flow. - bool first_layer_bridging; + bool first_object_layer_bridging; // Minimum / maximum layer height, to be used for the automatic adaptive layer height algorithm, // or by an interactive layer height editor. @@ -39,6 +76,37 @@ struct SlicingParameters typedef std::pair t_layer_height_range; typedef std::map t_layer_height_ranges; +extern std::vector layer_height_profile_from_ranges( + const SlicingParameters &slicing_params, + const t_layer_height_ranges &layer_height_ranges); + +extern std::vector layer_height_profile_adaptive( + const SlicingParameters &slicing_params, + const t_layer_height_ranges &layer_height_ranges, + const ModelVolumePtrs &volumes); + +extern void adjust_layer_height_profile( + const SlicingParameters &slicing_params, + std::vector &layer_height_profile, + coordf_t z, + coordf_t layer_thickness_delta, + coordf_t band_width, + int action); + +// Produce object layers as pairs of low / high layer boundaries, stored into a linear vector. +// The object layers are based at z=0, ignoring the raft layers. +extern std::vector generate_object_layers( + const SlicingParameters &slicing_params, + const std::vector &layer_height_profile); + +// Produce a 1D texture packed into a 2D texture describing in the RGBA format +// the planned object layers. +// Returns number of cells used by the texture of the 0th LOD level. +extern int generate_layer_height_texture( + const SlicingParameters &slicing_params, + const std::vector &layers, + void *data, int rows, int cols, bool level_of_detail_2nd_level); + }; // namespace Slic3r #endif /* slic3r_Slicing_hpp_ */ diff --git a/xs/src/libslic3r/SlicingAdaptive.cpp b/xs/src/libslic3r/SlicingAdaptive.cpp index b7f116770..d501d6f62 100644 --- a/xs/src/libslic3r/SlicingAdaptive.cpp +++ b/xs/src/libslic3r/SlicingAdaptive.cpp @@ -22,22 +22,13 @@ std::pair face_z_span(const stl_facet *f) void SlicingAdaptive::prepare() { // 1) Collect faces of all meshes. - { - int nfaces_total = 0; - for (std::vector::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh) - nfaces_total += (*it_mesh)->stl.stats.number_of_facets; - m_faces.reserve(nfaces_total); - } - m_max_z = 0; - for (std::vector::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh) { - const stl_facet *faces = (*it_mesh)->stl.facet_start; - int nfaces = (*it_mesh)->stl.stats.number_of_facets; - for (int i = 0; i < nfaces; ++ i) { - const stl_facet *facet = faces + i; - m_faces.push_back(facet); - m_max_z = std::max(std::max(m_max_z, facet->vertex[0].z), std::max(facet->vertex[1].z, facet->vertex[2].z)); - } - } + int nfaces_total = 0; + for (std::vector::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh) + nfaces_total += (*it_mesh)->stl.stats.number_of_facets; + m_faces.reserve(nfaces_total); + for (std::vector::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh) + for (int i = 0; i < (*it_mesh)->stl.stats.number_of_facets; ++ i) + m_faces.push_back((*it_mesh)->stl.facet_start + i); // 2) Sort faces lexicographically by their Z span. std::sort(m_faces.begin(), m_faces.end(), [](const stl_facet *f1, const stl_facet *f2) { @@ -54,7 +45,7 @@ void SlicingAdaptive::prepare() float SlicingAdaptive::cusp_height(float z, float cusp_value, int ¤t_facet) { - float height = m_layer_height_max; + float height = m_slicing_params.max_layer_height; bool first_hit = false; // find all facets intersecting the slice-layer @@ -81,10 +72,10 @@ float SlicingAdaptive::cusp_height(float z, float cusp_value, int ¤t_facet } // lower height limit due to printer capabilities - height = std::max(height, m_layer_height_min); + height = std::max(height, float(m_slicing_params.min_layer_height)); // check for sloped facets inside the determined layer and correct height if necessary - if (height > m_layer_height_min) { + if (height > m_slicing_params.min_layer_height) { for (; ordered_id < int(m_faces.size()); ++ ordered_id) { std::pair zspan = face_z_span(m_faces[ordered_id]); // facet's minimum is higher than slice_z + height -> end loop @@ -119,7 +110,7 @@ float SlicingAdaptive::cusp_height(float z, float cusp_value, int ¤t_facet } } // lower height limit due to printer capabilities again - height = std::max(height, m_layer_height_min); + height = std::max(height, float(m_slicing_params.min_layer_height)); } // Slic3r::debugf "cusp computation, layer-bottom at z:%f, cusp_value:%f, resulting layer height:%f\n", unscale $z, $cusp_value, $height; @@ -133,7 +124,7 @@ float SlicingAdaptive::horizontal_facet_distance(float z) for (size_t i = 0; i < m_faces.size(); ++ i) { std::pair zspan = face_z_span(m_faces[i]); // facet's minimum is higher than max forward distance -> end loop - if (zspan.first > z + m_layer_height_max) + if (zspan.first > z + m_slicing_params.max_layer_height) break; // min_z == max_z -> horizontal facet if (zspan.first > z && zspan.first == zspan.second) @@ -141,9 +132,9 @@ float SlicingAdaptive::horizontal_facet_distance(float z) } // objects maximum? - return (z + m_layer_height_max > m_max_z) ? - std::max(m_max_z - z, 0.f) : - m_layer_height_max; + return (z + m_slicing_params.max_layer_height > m_slicing_params.object_print_z_height()) ? + std::max(m_slicing_params.object_print_z_height() - z, 0.f) : + m_slicing_params.max_layer_height; } }; // namespace Slic3r diff --git a/xs/src/libslic3r/SlicingAdaptive.hpp b/xs/src/libslic3r/SlicingAdaptive.hpp index 23e8f4b62..bfd081d81 100644 --- a/xs/src/libslic3r/SlicingAdaptive.hpp +++ b/xs/src/libslic3r/SlicingAdaptive.hpp @@ -15,16 +15,14 @@ class SlicingAdaptive { public: void clear(); - void set_layer_height_range(float min, float max) { m_layer_height_min = min; m_layer_height_max = max; } + void set_slicing_parameters(SlicingParameters params) { m_slicing_params = params; } void add_mesh(const TriangleMesh *mesh) { m_meshes.push_back(mesh); } void prepare(); float cusp_height(float z, float cusp_value, int ¤t_facet); float horizontal_facet_distance(float z); protected: - float m_layer_height_min; - float m_layer_height_max; - float m_max_z; + SlicingParameters m_slicing_params; std::vector m_meshes; // Collected faces of all meshes, sorted by raising Z of the bottom most face. -- cgit v1.2.3