diff options
Diffstat (limited to 'src/libslic3r/PrintObject.cpp')
-rw-r--r-- | src/libslic3r/PrintObject.cpp | 395 |
1 files changed, 353 insertions, 42 deletions
diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 5573f4ac3..99b4130f4 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1,13 +1,18 @@ +#include "Exception.hpp" #include "Print.hpp" #include "BoundingBox.hpp" #include "ClipperUtils.hpp" #include "ElephantFootCompensation.hpp" #include "Geometry.hpp" #include "I18N.hpp" +#include "Layer.hpp" #include "SupportMaterial.hpp" #include "Surface.hpp" #include "Slicing.hpp" +#include "Tesselate.hpp" #include "Utils.hpp" +#include "Fill/FillAdaptive.hpp" +#include "Format/STL.hpp" #include <utility> #include <boost/log/trivial.hpp> @@ -92,6 +97,7 @@ PrintBase::ApplyStatus PrintObject::set_instances(PrintInstances &&instances) return status; } +// Called by make_perimeters() // 1) Decides Z positions of the layers, // 2) Initializes layers and their regions // 3) Slices the object meshes @@ -99,8 +105,6 @@ PrintBase::ApplyStatus PrintObject::set_instances(PrintInstances &&instances) // 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() { if (! this->set_started(posSlice)) @@ -120,7 +124,7 @@ void PrintObject::slice() // Simplify slices if required. if (m_print->config().resolution) this->simplify_slices(scale_(this->print()->config().resolution)); - // Update bounding boxes + // Update bounding boxes, back up raw slices of complex models. tbb::parallel_for( tbb::blocked_range<size_t>(0, m_layers.size()), [this](const tbb::blocked_range<size_t>& range) { @@ -131,10 +135,11 @@ void PrintObject::slice() layer.lslices_bboxes.reserve(layer.lslices.size()); for (const ExPolygon &expoly : layer.lslices) layer.lslices_bboxes.emplace_back(get_extents(expoly)); + layer.backup_untyped_slices(); } }); if (m_layers.empty()) - throw std::runtime_error("No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry.\n"); + throw Slic3r::SlicingError("No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry.\n"); this->set_done(posSlice); } @@ -152,10 +157,10 @@ void PrintObject::make_perimeters() m_print->set_status(20, L("Generating perimeters")); BOOST_LOG_TRIVIAL(info) << "Generating perimeters..." << log_memory_info(); - // merge slices if they were split into types + // Revert the typed slices into untyped slices. if (m_typed_slices) { for (Layer *layer : m_layers) { - layer->merge_slices(); + layer->restore_untyped_slices(); m_print->throw_if_canceled(); } m_typed_slices = false; @@ -368,13 +373,15 @@ void PrintObject::infill() this->prepare_infill(); if (this->set_started(posInfill)) { + auto [adaptive_fill_octree, support_fill_octree] = this->prepare_adaptive_infill_data(); + BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - start"; tbb::parallel_for( tbb::blocked_range<size_t>(0, m_layers.size()), - [this](const tbb::blocked_range<size_t>& range) { + [this, &adaptive_fill_octree = adaptive_fill_octree, &support_fill_octree = support_fill_octree](const tbb::blocked_range<size_t>& range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { m_print->throw_if_canceled(); - m_layers[layer_idx]->make_fills(); + m_layers[layer_idx]->make_fills(adaptive_fill_octree.get(), support_fill_octree.get()); } } ); @@ -387,6 +394,25 @@ void PrintObject::infill() } } +void PrintObject::ironing() +{ + if (this->set_started(posIroning)) { + BOOST_LOG_TRIVIAL(debug) << "Ironing in parallel - start"; + tbb::parallel_for( + tbb::blocked_range<size_t>(1, m_layers.size()), + [this](const tbb::blocked_range<size_t>& range) { + for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { + m_print->throw_if_canceled(); + m_layers[layer_idx]->make_ironing(); + } + } + ); + m_print->throw_if_canceled(); + BOOST_LOG_TRIVIAL(debug) << "Ironing in parallel - end"; + this->set_done(posIroning); + } +} + void PrintObject::generate_support_material() { if (this->set_started(posSupportMaterial)) { @@ -401,13 +427,54 @@ void PrintObject::generate_support_material() // therefore they cannot be printed without supports. for (const Layer *layer : m_layers) if (layer->empty()) - throw std::runtime_error("Levitating objects cannot be printed without supports."); + throw Slic3r::SlicingError("Levitating objects cannot be printed without supports."); #endif } this->set_done(posSupportMaterial); } } +std::pair<FillAdaptive::OctreePtr, FillAdaptive::OctreePtr> PrintObject::prepare_adaptive_infill_data() +{ + using namespace FillAdaptive; + + auto [adaptive_line_spacing, support_line_spacing] = adaptive_fill_line_spacing(*this); + if ((adaptive_line_spacing == 0. && support_line_spacing == 0.) || this->layers().empty()) + return std::make_pair(OctreePtr(), OctreePtr()); + + indexed_triangle_set mesh = this->model_object()->raw_indexed_triangle_set(); + // Rotate mesh and build octree on it with axis-aligned (standart base) cubes. + Transform3d m = m_trafo; + m.pretranslate(Vec3d(- unscale<float>(m_center_offset.x()), - unscale<float>(m_center_offset.y()), 0)); + auto to_octree = transform_to_octree().toRotationMatrix(); + its_transform(mesh, to_octree * m, true); + + // Triangulate internal bridging surfaces. + std::vector<std::vector<Vec3d>> overhangs(this->layers().size()); + tbb::parallel_for( + tbb::blocked_range<int>(0, int(m_layers.size()) - 1), + [this, &to_octree, &overhangs](const tbb::blocked_range<int> &range) { + std::vector<Vec3d> &out = overhangs[range.begin()]; + for (int idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { + m_print->throw_if_canceled(); + const Layer *layer = this->layers()[idx_layer]; + for (const LayerRegion *layerm : layer->regions()) + for (const Surface &surface : layerm->fill_surfaces.surfaces) + if (surface.surface_type == stInternalBridge) + append(out, triangulate_expolygon_3d(surface.expolygon, layer->bottom_z())); + } + for (Vec3d &p : out) + p = (to_octree * p).eval(); + }); + // and gather them. + for (size_t i = 1; i < overhangs.size(); ++ i) + append(overhangs.front(), std::move(overhangs[i])); + + return std::make_pair( + adaptive_line_spacing ? build_octree(mesh, overhangs.front(), adaptive_line_spacing, false) : OctreePtr(), + support_line_spacing ? build_octree(mesh, overhangs.front(), support_line_spacing, true) : OctreePtr()); +} + void PrintObject::clear_layers() { for (Layer *l : m_layers) @@ -500,12 +567,18 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_ || opt_key == "dont_support_bridges" || opt_key == "first_layer_extrusion_width") { steps.emplace_back(posSupportMaterial); + } else if (opt_key == "bottom_solid_layers") { + steps.emplace_back(posPrepareInfill); + if(m_print->config().spiral_vase) { + // Changing the number of bottom layers when a spiral vase is enabled requires re-slicing the object again. + // Otherwise, holes in the bottom layers could be filled, as is reported in GH #5528. + steps.emplace_back(posSlice); + } } else if ( opt_key == "interface_shells" || opt_key == "infill_only_where_needed" || opt_key == "infill_every_layers" || opt_key == "solid_infill_every_layers" - || opt_key == "bottom_solid_layers" || opt_key == "bottom_solid_min_thickness" || opt_key == "top_solid_layers" || opt_key == "top_solid_min_thickness" @@ -522,7 +595,8 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_ || opt_key == "external_fill_link_max_length" || opt_key == "fill_angle" || opt_key == "fill_pattern" - || opt_key == "fill_link_max_length" + || opt_key == "infill_anchor" + || opt_key == "infill_anchor_max" || opt_key == "top_infill_extrusion_width" || opt_key == "first_layer_extrusion_width") { steps.emplace_back(posInfill); @@ -582,14 +656,15 @@ bool PrintObject::invalidate_step(PrintObjectStep step) // propagate to dependent steps if (step == posPerimeters) { - invalidated |= this->invalidate_steps({ posPrepareInfill, posInfill }); + invalidated |= this->invalidate_steps({ posPrepareInfill, posInfill, posIroning }); invalidated |= m_print->invalidate_steps({ psSkirt, psBrim }); } else if (step == posPrepareInfill) { - invalidated |= this->invalidate_step(posInfill); + invalidated |= this->invalidate_steps({ posInfill, posIroning }); } else if (step == posInfill) { + invalidated |= this->invalidate_steps({ posIroning }); invalidated |= m_print->invalidate_steps({ psSkirt, psBrim }); } else if (step == posSlice) { - invalidated |= this->invalidate_steps({ posPerimeters, posPrepareInfill, posInfill, posSupportMaterial }); + invalidated |= this->invalidate_steps({ posPerimeters, posPrepareInfill, posInfill, posIroning, posSupportMaterial }); invalidated |= m_print->invalidate_steps({ psSkirt, psBrim }); this->m_slicing_params.valid = false; } else if (step == posSupportMaterial) { @@ -651,7 +726,7 @@ void PrintObject::detect_surfaces_type() // should be visible. bool spiral_vase = this->print()->config().spiral_vase.value; bool interface_shells = ! spiral_vase && m_config.interface_shells.value; - size_t num_layers = spiral_vase ? first_printing_region(*this)->config().bottom_solid_layers : m_layers.size(); + size_t num_layers = spiral_vase ? std::min(size_t(first_printing_region(*this)->config().bottom_solid_layers), m_layers.size()) : m_layers.size(); for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++ idx_region) { BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << idx_region << " in parallel - start"; @@ -939,7 +1014,7 @@ void PrintObject::discover_vertical_shells() Polygons holes; }; bool spiral_vase = this->print()->config().spiral_vase.value; - size_t num_layers = spiral_vase ? first_printing_region(*this)->config().bottom_solid_layers : m_layers.size(); + size_t num_layers = spiral_vase ? std::min(size_t(first_printing_region(*this)->config().bottom_solid_layers), m_layers.size()) : m_layers.size(); coordf_t min_layer_height = this->slicing_parameters().min_layer_height; // Does this region possibly produce more than 1 top or bottom layer? auto has_extra_layers_fn = [min_layer_height](const PrintRegionConfig &config) { @@ -1411,7 +1486,7 @@ void PrintObject::bridge_over_infill() } #ifdef SLIC3R_DEBUG - printf("Bridging " PRINTF_ZU " internal areas at layer " PRINTF_ZU "\n", to_bridge.size(), layer->id()); + printf("Bridging %zu internal areas at layer %zu\n", to_bridge.size(), layer->id()); #endif // compute the remaning internal solid surfaces as difference @@ -1476,22 +1551,48 @@ static void clamp_exturder_to_default(ConfigOptionInt &opt, size_t num_extruders PrintObjectConfig PrintObject::object_config_from_model_object(const PrintObjectConfig &default_object_config, const ModelObject &object, size_t num_extruders) { PrintObjectConfig config = default_object_config; - normalize_and_apply_config(config, object.config); + { + DynamicPrintConfig src_normalized(object.config.get()); + src_normalized.normalize_fdm(); + config.apply(src_normalized, true); + } // Clamp invalid extruders to the default extruder (with index 1). clamp_exturder_to_default(config.support_material_extruder, num_extruders); clamp_exturder_to_default(config.support_material_interface_extruder, num_extruders); return config; } +static void apply_to_print_region_config(PrintRegionConfig &out, const DynamicPrintConfig &in) +{ + // 1) Copy the "extruder key to infill_extruder and perimeter_extruder. + std::string sextruder = "extruder"; + auto *opt_extruder = in.opt<ConfigOptionInt>(sextruder); + if (opt_extruder) { + int extruder = opt_extruder->value; + if (extruder != 0) { + out.infill_extruder .value = extruder; + out.solid_infill_extruder.value = extruder; + out.perimeter_extruder .value = extruder; + } + } + // 2) Copy the rest of the values. + for (auto it = in.cbegin(); it != in.cend(); ++ it) + if (it->first != sextruder) { + ConfigOption *my_opt = out.option(it->first, false); + if (my_opt) + my_opt->set(it->second.get()); + } +} + PrintRegionConfig PrintObject::region_config_from_model_volume(const PrintRegionConfig &default_region_config, const DynamicPrintConfig *layer_range_config, const ModelVolume &volume, size_t num_extruders) { PrintRegionConfig config = default_region_config; - normalize_and_apply_config(config, volume.get_object()->config); + apply_to_print_region_config(config, volume.get_object()->config.get()); if (layer_range_config != nullptr) - normalize_and_apply_config(config, *layer_range_config); - normalize_and_apply_config(config, volume.config); + apply_to_print_region_config(config, *layer_range_config); + apply_to_print_region_config(config, volume.config.get()); if (! volume.material_id().empty()) - normalize_and_apply_config(config, volume.material()->config); + apply_to_print_region_config(config, volume.material()->config.get()); // Clamp invalid extruders to the default extruder (with index 1). clamp_exturder_to_default(config.infill_extruder, num_extruders); clamp_exturder_to_default(config.perimeter_extruder, num_extruders); @@ -1524,13 +1625,13 @@ SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full print_config, region_config_from_model_volume(default_region_config, nullptr, *model_volume, num_extruders), object_extruders); - for (const std::pair<const t_layer_height_range, DynamicPrintConfig> &range_and_config : model_object.layer_config_ranges) + for (const std::pair<const t_layer_height_range, ModelConfig> &range_and_config : model_object.layer_config_ranges) if (range_and_config.second.has("perimeter_extruder") || range_and_config.second.has("infill_extruder") || range_and_config.second.has("solid_infill_extruder")) PrintRegion::collect_object_printing_extruders( print_config, - region_config_from_model_volume(default_region_config, &range_and_config.second, *model_volume, num_extruders), + region_config_from_model_volume(default_region_config, &range_and_config.second.get(), *model_volume, num_extruders), object_extruders); } sort_remove_duplicates(object_extruders); @@ -1557,7 +1658,9 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c bool updated = false; if (layer_height_profile.empty()) { - layer_height_profile = model_object.layer_height_profile; + // use the constructor because the assignement is crashing on ASAN OsX + layer_height_profile = std::vector<coordf_t>(model_object.layer_height_profile.get()); +// layer_height_profile = model_object.layer_height_profile; updated = true; } @@ -1592,12 +1695,6 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile) m_typed_slices = false; -#ifdef SLIC3R_PROFILE - // Disable parallelization so the Shiny profiler works - static tbb::task_scheduler_init *tbb_init = nullptr; - tbb_init = new tbb::task_scheduler_init(1); -#endif - // 1) Initialize layers and their slice heights. std::vector<float> slice_zs; { @@ -1657,14 +1754,24 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile) // Slice all non-modifier volumes. bool clipped = false; bool upscaled = false; - auto slicing_mode = this->print()->config().spiral_vase ? SlicingMode::PositiveLargestContour : SlicingMode::Regular; + bool spiral_vase = this->print()->config().spiral_vase; + auto slicing_mode = spiral_vase ? SlicingMode::PositiveLargestContour : SlicingMode::Regular; if (! has_z_ranges && (! m_config.clip_multipart_objects.value || all_volumes_single_region >= 0)) { // Cheap path: Slice regions without mutual clipping. // The cheap path is possible if no clipping is allowed or if slicing volumes of just a single region. for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { BOOST_LOG_TRIVIAL(debug) << "Slicing objects - region " << region_id; // slicing in parallel - std::vector<ExPolygons> expolygons_by_layer = this->slice_region(region_id, slice_zs, slicing_mode); + size_t slicing_mode_normal_below_layer = 0; + if (spiral_vase) { + // Slice the bottom layers with SlicingMode::Regular. + // This needs to be in sync with LayerRegion::make_perimeters() spiral_vase! + const PrintRegionConfig &config = this->print()->regions()[region_id]->config(); + slicing_mode_normal_below_layer = size_t(config.bottom_solid_layers.value); + for (; slicing_mode_normal_below_layer < slice_zs.size() && slice_zs[slicing_mode_normal_below_layer] < config.bottom_solid_min_thickness - EPSILON; + ++ slicing_mode_normal_below_layer); + } + std::vector<ExPolygons> expolygons_by_layer = this->slice_region(region_id, slice_zs, slicing_mode, slicing_mode_normal_below_layer, SlicingMode::Regular); m_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Slicing objects - append slices " << region_id << " start"; for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id) @@ -1897,10 +2004,9 @@ end: layer->make_slices(); } }); - if (elephant_foot_compensation_scaled > 0.f) { + if (elephant_foot_compensation_scaled > 0.f && ! m_layers.empty()) { // The Elephant foot has been compensated, therefore the 1st layer's lslices are shrank with the Elephant foot compensation value. // Store the uncompensated value there. - assert(! m_layers.empty()); assert(m_layers.front()->id() == 0); m_layers.front()->lslices = std::move(lslices_1st_layer); } @@ -1911,7 +2017,7 @@ end: } // To be used only if there are no layer span specific configurations applied, which would lead to z ranges being generated for this region. -std::vector<ExPolygons> PrintObject::slice_region(size_t region_id, const std::vector<float> &z, SlicingMode mode) const +std::vector<ExPolygons> PrintObject::slice_region(size_t region_id, const std::vector<float> &z, SlicingMode mode, size_t slicing_mode_normal_below_layer, SlicingMode mode_below) const { std::vector<const ModelVolume*> volumes; if (region_id < this->region_volumes.size()) { @@ -1921,10 +2027,10 @@ std::vector<ExPolygons> PrintObject::slice_region(size_t region_id, const std::v volumes.emplace_back(volume); } } - return this->slice_volumes(z, mode, volumes); + return this->slice_volumes(z, mode, slicing_mode_normal_below_layer, mode_below, volumes); } -// Z ranges are not applicable to modifier meshes, therefore a sinle volume will be found in volume_and_range at most once. +// Z ranges are not applicable to modifier meshes, therefore a single volume will be found in volume_and_range at most once. std::vector<ExPolygons> PrintObject::slice_modifiers(size_t region_id, const std::vector<float> &slice_zs) const { std::vector<ExPolygons> out; @@ -1990,10 +2096,12 @@ std::vector<ExPolygons> PrintObject::slice_modifiers(size_t region_id, const std ranges.emplace_back(volumes_and_ranges[j].first); // slicing in parallel std::vector<ExPolygons> this_slices = this->slice_volume(slice_zs, ranges, SlicingMode::Regular, *model_volume); + // Variable this_slices could be empty if no value of slice_zs is within any of the ranges of this volume. if (out.empty()) { out = std::move(this_slices); merge.assign(out.size(), false); - } else { + } else if (!this_slices.empty()) { + assert(out.size() == this_slices.size()); for (size_t i = 0; i < out.size(); ++ i) if (! this_slices[i].empty()) { if (! out[i].empty()) { @@ -2031,7 +2139,10 @@ std::vector<ExPolygons> PrintObject::slice_support_volumes(const ModelVolumeType return this->slice_volumes(zs, SlicingMode::Regular, volumes); } -std::vector<ExPolygons> PrintObject::slice_volumes(const std::vector<float> &z, SlicingMode mode, const std::vector<const ModelVolume*> &volumes) const +std::vector<ExPolygons> PrintObject::slice_volumes( + const std::vector<float> &z, + SlicingMode mode, size_t slicing_mode_normal_below_layer, SlicingMode mode_below, + const std::vector<const ModelVolume*> &volumes) const { std::vector<ExPolygons> layers; if (! volumes.empty()) { @@ -2061,7 +2172,7 @@ std::vector<ExPolygons> PrintObject::slice_volumes(const std::vector<float> &z, mesh.require_shared_vertices(); TriangleMeshSlicer mslicer; mslicer.init(&mesh, callback); - mslicer.slice(z, mode, float(m_config.slice_closing_radius.value), &layers, callback); + mslicer.slice(z, mode, slicing_mode_normal_below_layer, mode_below, float(m_config.slice_closing_radius.value), &layers, callback); m_print->throw_if_canceled(); } } @@ -2098,7 +2209,7 @@ std::vector<ExPolygons> PrintObject::slice_volume(const std::vector<float> &z, S return layers; } -// Filter the zs not inside the ranges. The ranges are closed at the botton and open at the top, they are sorted lexicographically and non overlapping. +// Filter the zs not inside the ranges. The ranges are closed at the bottom and open at the top, they are sorted lexicographically and non overlapping. std::vector<ExPolygons> PrintObject::slice_volume(const std::vector<float> &z, const std::vector<t_layer_height_range> &ranges, SlicingMode mode, const ModelVolume &volume) const { std::vector<ExPolygons> out; @@ -2610,6 +2721,7 @@ void PrintObject::combine_infill() // Because fill areas for rectilinear and honeycomb are grown // later to overlap perimeters, we need to counteract that too. ((region->config().fill_pattern == ipRectilinear || + region->config().fill_pattern == ipMonotonic || region->config().fill_pattern == ipGrid || region->config().fill_pattern == ipLine || region->config().fill_pattern == ipHoneycomb) ? 1.5f : 0.5f) * @@ -2645,4 +2757,203 @@ void PrintObject::_generate_support_material() support_material.generate(*this); } + +void PrintObject::project_and_append_custom_facets( + bool seam, EnforcerBlockerType type, std::vector<ExPolygons>& expolys) const +{ + for (const ModelVolume* mv : this->model_object()->volumes) { + const indexed_triangle_set custom_facets = seam + ? mv->seam_facets.get_facets(*mv, type) + : mv->supported_facets.get_facets(*mv, type); + if (! mv->is_model_part() || custom_facets.indices.empty()) + continue; + + const Transform3f& tr1 = mv->get_matrix().cast<float>(); + const Transform3f& tr2 = this->trafo().cast<float>(); + const Transform3f tr = tr2 * tr1; + const float tr_det_sign = (tr.matrix().determinant() > 0. ? 1.f : -1.f); + + + // The projection will be at most a pentagon. Let's minimize heap + // reallocations by saving in in the following struct. + // Points are used so that scaling can be done in parallel + // and they can be moved from to create an ExPolygon later. + struct LightPolygon { + LightPolygon() { pts.reserve(5); } + Points pts; + + void add(const Vec2f& pt) { + pts.emplace_back(scale_(pt.x()), scale_(pt.y())); + assert(pts.size() <= 5); + } + }; + + // Structure to collect projected polygons. One element for each triangle. + // Saves vector of polygons and layer_id of the first one. + struct TriangleProjections { + size_t first_layer_id; + std::vector<LightPolygon> polygons; + }; + + // Vector to collect resulting projections from each triangle. + std::vector<TriangleProjections> projections_of_triangles(custom_facets.indices.size()); + + // Iterate over all triangles. + tbb::parallel_for( + tbb::blocked_range<size_t>(0, custom_facets.indices.size()), + [&](const tbb::blocked_range<size_t>& range) { + for (size_t idx = range.begin(); idx < range.end(); ++ idx) { + + std::array<Vec3f, 3> facet; + + // Transform the triangle into worlds coords. + for (int i=0; i<3; ++i) + facet[i] = tr * custom_facets.vertices[custom_facets.indices[idx](i)]; + + // Ignore triangles with upward-pointing normal. Don't forget about mirroring. + float z_comp = (facet[1]-facet[0]).cross(facet[2]-facet[0]).z(); + if (! seam && tr_det_sign * z_comp > 0.) + continue; + + // Sort the three vertices according to z-coordinate. + std::sort(facet.begin(), facet.end(), + [](const Vec3f& pt1, const Vec3f&pt2) { + return pt1.z() < pt2.z(); + }); + + std::array<Vec2f, 3> trianglef; + for (int i=0; i<3; ++i) { + trianglef[i] = Vec2f(facet[i].x(), facet[i].y()); + trianglef[i] -= Vec2f(unscale<float>(this->center_offset().x()), + unscale<float>(this->center_offset().y())); + } + + // Find lowest slice not below the triangle. + auto it = std::lower_bound(layers().begin(), layers().end(), facet[0].z()+EPSILON, + [](const Layer* l1, float z) { + return l1->slice_z < z; + }); + + // Count how many projections will be generated for this triangle + // and allocate respective amount in projections_of_triangles. + projections_of_triangles[idx].first_layer_id = it-layers().begin(); + size_t last_layer_id = projections_of_triangles[idx].first_layer_id; + // The cast in the condition below is important. The comparison must + // be an exact opposite of the one lower in the code where + // the polygons are appended. And that one is on floats. + while (last_layer_id + 1 < layers().size() + && float(layers()[last_layer_id]->slice_z) <= facet[2].z()) + ++last_layer_id; + projections_of_triangles[idx].polygons.resize( + last_layer_id - projections_of_triangles[idx].first_layer_id + 1); + + // Calculate how to move points on triangle sides per unit z increment. + Vec2f ta(trianglef[1] - trianglef[0]); + Vec2f tb(trianglef[2] - trianglef[0]); + ta *= 1.f/(facet[1].z() - facet[0].z()); + tb *= 1.f/(facet[2].z() - facet[0].z()); + + // Projection on current slice will be build directly in place. + LightPolygon* proj = &projections_of_triangles[idx].polygons[0]; + proj->add(trianglef[0]); + + bool passed_first = false; + bool stop = false; + + // Project a sub-polygon on all slices intersecting the triangle. + while (it != layers().end()) { + const float z = float((*it)->slice_z); + + // Projections of triangle sides intersections with slices. + // a moves along one side, b tracks the other. + Vec2f a; + Vec2f b; + + // If the middle vertex was already passed, append the vertex + // and use ta for tracking the remaining side. + if (z > facet[1].z() && ! passed_first) { + proj->add(trianglef[1]); + ta = trianglef[2]-trianglef[1]; + ta *= 1.f/(facet[2].z() - facet[1].z()); + passed_first = true; + } + + // This slice is above the triangle already. + if (z > facet[2].z() || it+1 == layers().end()) { + proj->add(trianglef[2]); + stop = true; + } + else { + // Move a, b along the side it currently tracks to get + // projected intersection with current slice. + a = passed_first ? (trianglef[1]+ta*(z-facet[1].z())) + : (trianglef[0]+ta*(z-facet[0].z())); + b = trianglef[0]+tb*(z-facet[0].z()); + proj->add(a); + proj->add(b); + } + + if (stop) + break; + + // Advance to the next layer. + ++it; + ++proj; + assert(proj <= &projections_of_triangles[idx].polygons.back() ); + + // a, b are first two points of the polygon for the next layer. + proj->add(b); + proj->add(a); + } + } + }); // end of parallel_for + + // Make sure that the output vector can be used. + expolys.resize(layers().size()); + + // Now append the collected polygons to respective layers. + for (auto& trg : projections_of_triangles) { + int layer_id = int(trg.first_layer_id); + for (const LightPolygon& poly : trg.polygons) { + if (layer_id >= int(expolys.size())) + break; // part of triangle could be projected above top layer + expolys[layer_id].emplace_back(std::move(poly.pts)); + ++layer_id; + } + } + + } // loop over ModelVolumes +} + + + +const Layer* PrintObject::get_layer_at_printz(coordf_t print_z) const { + auto it = Slic3r::lower_bound_by_predicate(m_layers.begin(), m_layers.end(), [print_z](const Layer *layer) { return layer->print_z < print_z; }); + return (it == m_layers.end() || (*it)->print_z != print_z) ? nullptr : *it; +} + + + +Layer* PrintObject::get_layer_at_printz(coordf_t print_z) { return const_cast<Layer*>(std::as_const(*this).get_layer_at_printz(print_z)); } + + + +// Get a layer approximately at print_z. +const Layer* PrintObject::get_layer_at_printz(coordf_t print_z, coordf_t epsilon) const { + coordf_t limit = print_z - epsilon; + auto it = Slic3r::lower_bound_by_predicate(m_layers.begin(), m_layers.end(), [limit](const Layer *layer) { return layer->print_z < limit; }); + return (it == m_layers.end() || (*it)->print_z > print_z + epsilon) ? nullptr : *it; +} + + + +Layer* PrintObject::get_layer_at_printz(coordf_t print_z, coordf_t epsilon) { return const_cast<Layer*>(std::as_const(*this).get_layer_at_printz(print_z, epsilon)); } + +const Layer *PrintObject::get_first_layer_bellow_printz(coordf_t print_z, coordf_t epsilon) const +{ + coordf_t limit = print_z + epsilon; + auto it = Slic3r::lower_bound_by_predicate(m_layers.begin(), m_layers.end(), [limit](const Layer *layer) { return layer->print_z < limit; }); + return (it == m_layers.begin()) ? nullptr : *(--it); +} + } // namespace Slic3r |