From adf39805bce53ca896b24690703879229f946ee7 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Mon, 4 Apr 2022 12:35:29 +0200 Subject: [PATCH 01/78] work in progress: hooked in new step: posSupportableIssuesSearch created layout of the processing --- src/libslic3r/CMakeLists.txt | 2 + src/libslic3r/Print.cpp | 2 + src/libslic3r/Print.hpp | 3 +- src/libslic3r/PrintObject.cpp | 150 +++++++++++++--------- src/libslic3r/SupportableIssuesSearch.cpp | 149 +++++++++++++++++++++ src/libslic3r/SupportableIssuesSearch.hpp | 16 +++ 6 files changed, 257 insertions(+), 65 deletions(-) create mode 100644 src/libslic3r/SupportableIssuesSearch.cpp create mode 100644 src/libslic3r/SupportableIssuesSearch.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 01a6a3aa2a..a342da8319 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -245,6 +245,8 @@ set(SLIC3R_SOURCES SlicingAdaptive.hpp Subdivide.cpp Subdivide.hpp + SupportableIssuesSearch.cpp + SupportableIssuesSearch.hpp SupportMaterial.cpp SupportMaterial.hpp Surface.cpp diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 9792a6968b..a50974cafd 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -825,6 +825,8 @@ void Print::process() obj->infill(); for (PrintObject *obj : m_objects) obj->ironing(); + for (PrintObject *obj : m_objects) + obj->find_supportable_issues(); for (PrintObject *obj : m_objects) obj->generate_support_material(); if (this->set_started(psWipeTower)) { diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index cb01600fa5..c89b463a8f 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -61,7 +61,7 @@ enum PrintStep : unsigned int { enum PrintObjectStep : unsigned int { posSlice, posPerimeters, posPrepareInfill, - posInfill, posIroning, posSupportMaterial, posCount, + posInfill, posIroning, posSupportableIssuesSearch, posSupportMaterial, posCount, }; // A PrintRegion object represents a group of volumes to print @@ -381,6 +381,7 @@ private: void prepare_infill(); void infill(); void ironing(); + void find_supportable_issues(); void generate_support_material(); void slice_volumes(); diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 6ec27ea95b..4bcf74fe5e 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -16,6 +16,7 @@ #include "Fill/FillAdaptive.hpp" #include "Fill/FillLightning.hpp" #include "Format/STL.hpp" +#include "SupportableIssuesSearch.hpp" #include #include @@ -89,7 +90,7 @@ PrintBase::ApplyStatus PrintObject::set_instances(PrintInstances &&instances) // Invalidate and set copies. PrintBase::ApplyStatus status = PrintBase::APPLY_STATUS_UNCHANGED; bool equal_length = instances.size() == m_instances.size(); - bool equal = equal_length && std::equal(instances.begin(), instances.end(), m_instances.begin(), + bool equal = equal_length && std::equal(instances.begin(), instances.end(), m_instances.begin(), [](const PrintInstance& lhs, const PrintInstance& rhs) { return lhs.model_instance == rhs.model_instance && lhs.shift == rhs.shift; }); if (! equal) { status = PrintBase::APPLY_STATUS_CHANGED; @@ -125,7 +126,7 @@ void PrintObject::make_perimeters() m_print->set_status(20, L("Generating perimeters")); BOOST_LOG_TRIVIAL(info) << "Generating perimeters..." << log_memory_info(); - + // Revert the typed slices into untyped slices. if (m_typed_slices) { for (Layer *layer : m_layers) { @@ -134,10 +135,10 @@ void PrintObject::make_perimeters() } m_typed_slices = false; } - + // compare each layer to the one below, and mark those slices needing // one additional inner perimeter, like the top of domed objects- - + // this algorithm makes sure that at least one perimeter is overlapping // but we don't generate any extra perimeter if fill density is zero, as they would be floating // inside the object - infill_only_where_needed should be the method of choice for printing @@ -245,7 +246,7 @@ void PrintObject::prepare_infill() // by the cummulative area of the previous $layerm->fill_surfaces. this->detect_surfaces_type(); m_print->throw_if_canceled(); - + // Decide what surfaces are to be filled. // Here the stTop / stBottomBridge / stBottom infill is turned to just stInternal if zero top / bottom infill layers are configured. // Also tiny stInternal surfaces are turned to stInternalSolid. @@ -320,7 +321,7 @@ void PrintObject::prepare_infill() } // for each layer } // for each region #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ - + // the following step needs to be done before combination because it may need // to remove only half of the combined infill this->bridge_over_infill(); @@ -395,12 +396,33 @@ void PrintObject::ironing() } } +void PrintObject::find_supportable_issues() +{ + if (this->set_started(posSupportableIssuesSearch)) { + BOOST_LOG_TRIVIAL(debug) + << "Searching supportable issues - start"; + //TODO status number? + m_print->set_status(70, L("Searching supportable issues")); + + if (this->has_support()) { + + } else { + SupportableIssues::quick_search(this); + } + + m_print->throw_if_canceled(); + BOOST_LOG_TRIVIAL(debug) + << "Searching supportable issues - end"; + this->set_done(posSupportableIssuesSearch); + } +} + void PrintObject::generate_support_material() { if (this->set_started(posSupportMaterial)) { this->clear_support_layers(); if ((this->has_support() && m_layers.size() > 1) || (this->has_raft() && ! m_layers.empty())) { - m_print->set_status(85, L("Generating support material")); + m_print->set_status(85, L("Generating support material")); this->_generate_support_material(); m_print->throw_if_canceled(); } else { @@ -560,7 +582,7 @@ bool PrintObject::invalidate_state_by_config_options( } else if ( opt_key == "clip_multipart_objects" || opt_key == "elefant_foot_compensation" - || opt_key == "support_material_contact_distance" + || opt_key == "support_material_contact_distance" || opt_key == "xy_size_compensation") { steps.emplace_back(posSlice); } else if (opt_key == "support_material") { @@ -711,7 +733,7 @@ bool PrintObject::invalidate_state_by_config_options( bool PrintObject::invalidate_step(PrintObjectStep step) { bool invalidated = Inherited::invalidate_step(step); - + // propagate to dependent steps if (step == posPerimeters) { invalidated |= this->invalidate_steps({ posPrepareInfill, posInfill, posIroning }); @@ -784,7 +806,7 @@ void PrintObject::detect_surfaces_type() surfaces_new.assign(num_layers, Surfaces()); tbb::parallel_for( - tbb::blocked_range(0, + tbb::blocked_range(0, spiral_vase ? // In spiral vase mode, reserve the last layer for the top surface if more than 1 layer is planned for the vase bottom. ((num_layers > 1) ? num_layers - 1 : num_layers) : @@ -812,7 +834,7 @@ void PrintObject::detect_surfaces_type() // of current layer and upper one) Surfaces top; if (upper_layer) { - ExPolygons upper_slices = interface_shells ? + ExPolygons upper_slices = interface_shells ? diff_ex(layerm->slices.surfaces, upper_layer->m_regions[region_id]->slices.surfaces, ApplySafetyOffset::Yes) : diff_ex(layerm->slices.surfaces, upper_layer->lslices, ApplySafetyOffset::Yes); surfaces_append(top, opening_ex(upper_slices, offset), stTop); @@ -823,7 +845,7 @@ void PrintObject::detect_surfaces_type() for (Surface &surface : top) surface.surface_type = stTop; } - + // Find bottom surfaces (difference between current surfaces of current layer and lower one). Surfaces bottom; if (lower_layer) { @@ -846,7 +868,7 @@ void PrintObject::detect_surfaces_type() // if user requested internal shells, we need to identify surfaces // lying on other slices not belonging to this region if (interface_shells) { - // non-bridging bottom surfaces: any part of this layer lying + // non-bridging bottom surfaces: any part of this layer lying // on something else, excluding those lying on our own region surfaces_append( bottom, @@ -866,7 +888,7 @@ void PrintObject::detect_surfaces_type() for (Surface &surface : bottom) surface.surface_type = stBottom; } - + // now, if the object contained a thin membrane, we could have overlapping bottom // and top surfaces; let's do an intersection to discover them and consider them // as bottom surfaces (to allow for bridge detection) @@ -889,7 +911,7 @@ void PrintObject::detect_surfaces_type() SVG::export_expolygons(debug_out_path("1_detect_surfaces_type_%d_region%d-layer_%f.svg", iRun ++, region_id, layer->print_z).c_str(), expolygons_with_attributes); } #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ - + // save surfaces to layer Surfaces &surfaces_out = interface_shells ? surfaces_new[idx_layer] : layerm->slices.surfaces; Surfaces surfaces_backup; @@ -908,7 +930,7 @@ void PrintObject::detect_surfaces_type() surfaces_append(surfaces_out, std::move(top)); surfaces_append(surfaces_out, std::move(bottom)); - + // Slic3r::debugf " layer %d has %d bottom, %d top and %d internal surfaces\n", // $layerm->layer->id, scalar(@bottom), scalar(@top), scalar(@internal) if $Slic3r::debug; @@ -1216,7 +1238,7 @@ void PrintObject::discover_vertical_shells() #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ Flow solid_infill_flow = layerm->flow(frSolidInfill); - coord_t infill_line_spacing = solid_infill_flow.scaled_spacing(); + coord_t infill_line_spacing = solid_infill_flow.scaled_spacing(); // Find a union of perimeters below / above this surface to guarantee a minimum shell thickness. Polygons shell; Polygons holes; @@ -1252,8 +1274,8 @@ void PrintObject::discover_vertical_shells() if (int n_top_layers = region_config.top_solid_layers.value; n_top_layers > 0) { // Gather top regions projected to this layer. coordf_t print_z = layer->print_z; - for (int i = int(idx_layer) + 1; - i < int(cache_top_botom_regions.size()) && + for (int i = int(idx_layer) + 1; + i < int(cache_top_botom_regions.size()) && (i < int(idx_layer) + n_top_layers || m_layers[i]->print_z - print_z < region_config.top_solid_min_thickness - EPSILON); ++ i) { @@ -1262,7 +1284,7 @@ void PrintObject::discover_vertical_shells() holes = intersection(holes, cache.holes); if (! cache.top_surfaces.empty()) { polygons_append(shell, cache.top_surfaces); - // Running the union_ using the Clipper library piece by piece is cheaper + // Running the union_ using the Clipper library piece by piece is cheaper // than running the union_ all at once. shell = union_(shell); } @@ -1281,7 +1303,7 @@ void PrintObject::discover_vertical_shells() holes = intersection(holes, cache.holes); if (! cache.bottom_surfaces.empty()) { polygons_append(shell, cache.bottom_surfaces); - // Running the union_ using the Clipper library piece by piece is cheaper + // Running the union_ using the Clipper library piece by piece is cheaper // than running the union_ all at once. shell = union_(shell); } @@ -1292,14 +1314,14 @@ void PrintObject::discover_vertical_shells() Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-before-union-%d.svg", debug_idx), get_extents(shell)); svg.draw(shell); svg.draw_outline(shell, "black", scale_(0.05)); - svg.Close(); + svg.Close(); } #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ #if 0 { PROFILE_BLOCK(discover_vertical_shells_region_layer_shell_); // shell = union_(shell, true); - shell = union_(shell, false); + shell = union_(shell, false); } #endif #ifdef SLIC3R_DEBUG_SLICE_PROCESSING @@ -1315,7 +1337,7 @@ void PrintObject::discover_vertical_shells() Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-after-union-%d.svg", debug_idx), get_extents(shell)); svg.draw(shell_ex); svg.draw_outline(shell_ex, "black", "blue", scale_(0.05)); - svg.Close(); + svg.Close(); } #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ @@ -1327,7 +1349,7 @@ void PrintObject::discover_vertical_shells() svg.draw(shell_ex, "blue", 0.5); svg.draw_outline(shell_ex, "black", "blue", scale_(0.05)); svg.Close(); - } + } { Slic3r::SVG svg(debug_out_path("discover_vertical_shells-internalvoid-wshell-%d.svg", debug_idx), get_extents(shell)); svg.draw(layerm->fill_surfaces.filter_by_type(stInternalVoid), "yellow", 0.5); @@ -1335,15 +1357,15 @@ void PrintObject::discover_vertical_shells() svg.draw(shell_ex, "blue", 0.5); svg.draw_outline(shell_ex, "black", "blue", scale_(0.05)); svg.Close(); - } + } { Slic3r::SVG svg(debug_out_path("discover_vertical_shells-internalvoid-wshell-%d.svg", debug_idx), get_extents(shell)); svg.draw(layerm->fill_surfaces.filter_by_type(stInternalVoid), "yellow", 0.5); svg.draw_outline(layerm->fill_surfaces.filter_by_type(stInternalVoid), "black", "blue", scale_(0.05)); svg.draw(shell_ex, "blue", 0.5); - svg.draw_outline(shell_ex, "black", "blue", scale_(0.05)); + svg.draw_outline(shell_ex, "black", "blue", scale_(0.05)); svg.Close(); - } + } #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ // Trim the shells region by the internal & internal void surfaces. @@ -1364,7 +1386,7 @@ void PrintObject::discover_vertical_shells() Polygons shell_before = shell; #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ #if 1 - // Intentionally inflate a bit more than how much the region has been shrunk, + // Intentionally inflate a bit more than how much the region has been shrunk, // so there will be some overlap between this solid infill and the other infill regions (mainly the sparse infill). shell = opening(union_(shell), 0.5f * min_perimeter_infill_spacing, 0.8f * min_perimeter_infill_spacing, ClipperLib::jtSquare); if (shell.empty()) @@ -1446,7 +1468,7 @@ void PrintObject::bridge_over_infill() for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) { const PrintRegion ®ion = this->printing_region(region_id); - + // skip bridging in case there are no voids if (region.config().fill_density.value == 100) continue; @@ -1455,7 +1477,7 @@ void PrintObject::bridge_over_infill() // skip first layer if (layer_it == m_layers.begin()) continue; - + Layer *layer = *layer_it; LayerRegion *layerm = layer->m_regions[region_id]; Flow bridge_flow = layerm->bridging_flow(frSolidInfill); @@ -1463,31 +1485,31 @@ void PrintObject::bridge_over_infill() // extract the stInternalSolid surfaces that might be transformed into bridges Polygons internal_solid; layerm->fill_surfaces.filter_by_type(stInternalSolid, &internal_solid); - + // check whether the lower area is deep enough for absorbing the extra flow // (for obvious physical reasons but also for preventing the bridge extrudates // from overflowing in 3D preview) ExPolygons to_bridge; { Polygons to_bridge_pp = internal_solid; - + // iterate through lower layers spanned by bridge_flow double bottom_z = layer->print_z - bridge_flow.height() - EPSILON; for (int i = int(layer_it - m_layers.begin()) - 1; i >= 0; --i) { const Layer* lower_layer = m_layers[i]; - + // stop iterating if layer is lower than bottom_z if (lower_layer->print_z < bottom_z) break; - + // iterate through regions and collect internal surfaces Polygons lower_internal; for (LayerRegion *lower_layerm : lower_layer->m_regions) lower_layerm->fill_surfaces.filter_by_type(stInternal, &lower_internal); - + // intersect such lower internal surfaces with the candidate solid surfaces to_bridge_pp = intersection(to_bridge_pp, lower_internal); } - + // there's no point in bridging too thin/short regions //FIXME Vojtech: The offset2 function is not a geometric offset, // therefore it may create 1) gaps, and 2) sharp corners, which are outside the original contour. @@ -1496,17 +1518,17 @@ void PrintObject::bridge_over_infill() float min_width = float(bridge_flow.scaled_width()) * 3.f; to_bridge_pp = opening(to_bridge_pp, min_width); } - + if (to_bridge_pp.empty()) continue; - + // convert into ExPolygons to_bridge = union_ex(to_bridge_pp); } - + #ifdef SLIC3R_DEBUG printf("Bridging %zu internal areas at layer %zu\n", to_bridge.size(), layer->id()); #endif - + // compute the remaning internal solid surfaces as difference ExPolygons not_to_bridge = diff_ex(internal_solid, to_bridge, ApplySafetyOffset::Yes); to_bridge = intersection_ex(to_bridge, internal_solid, ApplySafetyOffset::Yes); @@ -1515,7 +1537,7 @@ void PrintObject::bridge_over_infill() for (ExPolygon &ex : to_bridge) layerm->fill_surfaces.surfaces.push_back(Surface(stInternalBridge, ex)); for (ExPolygon &ex : not_to_bridge) - layerm->fill_surfaces.surfaces.push_back(Surface(stInternalSolid, ex)); + layerm->fill_surfaces.surfaces.push_back(Surface(stInternalSolid, ex)); /* # exclude infill from the layers below if needed # see discussion at https://github.com/alexrj/Slic3r/issues/240 @@ -1544,7 +1566,7 @@ void PrintObject::bridge_over_infill() $lower_layerm->fill_surfaces->clear; $lower_layerm->fill_surfaces->append($_) for @new_surfaces; } - + $excess -= $self->get_layer($i)->height; } } @@ -1634,7 +1656,7 @@ PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &defau // Switch of infill for very low infill rates, also avoid division by zero in infill generator for these very low rates. // See GH issue #5910. config.fill_density.value = 0; - else + else config.fill_density.value = std::min(config.fill_density.value, 100.); if (config.fuzzy_skin.value != FuzzySkinType::None && (config.fuzzy_skin_point_dist.value < 0.01 || config.fuzzy_skin_thickness.value < 0.001)) config.fuzzy_skin.value = FuzzySkinType::None; @@ -1793,7 +1815,7 @@ void PrintObject::clip_fill_surfaces() // Regularize the overhang regions, so that the infill areas will not become excessively jagged. smooth_outward( closing(upper_internal, closing_radius, ClipperLib::jtSquare, 0.), - scaled(0.1)), + scaled(0.1)), lower_layer_internal_surfaces); // Apply new internal infill to regions. for (LayerRegion *layerm : lower_layer->m_regions) { @@ -1821,7 +1843,7 @@ void PrintObject::clip_fill_surfaces() void PrintObject::discover_horizontal_shells() { BOOST_LOG_TRIVIAL(trace) << "discover_horizontal_shells()"; - + for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) { for (size_t i = 0; i < m_layers.size(); ++ i) { m_print->throw_if_canceled(); @@ -1840,7 +1862,7 @@ void PrintObject::discover_horizontal_shells() // If ensure_vertical_shell_thickness, then the rest has already been performed by discover_vertical_shells(). if (region_config.ensure_vertical_shell_thickness.value) continue; - + coordf_t print_z = layer->print_z; coordf_t bottom_z = layer->bottom_z(); for (size_t idx_surface_type = 0; idx_surface_type < 3; ++ idx_surface_type) { @@ -1873,11 +1895,11 @@ void PrintObject::discover_horizontal_shells() if (solid.empty()) continue; // Slic3r::debugf "Layer %d has %s surfaces\n", $i, ($type == stTop) ? 'top' : 'bottom'; - + // Scatter top / bottom regions to other layers. Scattering process is inherently serial, it is difficult to parallelize without locking. for (int n = (type == stTop) ? int(i) - 1 : int(i) + 1; (type == stTop) ? - (n >= 0 && (int(i) - n < num_solid_layers || + (n >= 0 && (int(i) - n < num_solid_layers || print_z - m_layers[n]->print_z < region_config.top_solid_min_thickness.value - EPSILON)) : (n < int(m_layers.size()) && (n - int(i) < num_solid_layers || m_layers[n]->bottom_z() - bottom_z < region_config.bottom_solid_min_thickness.value - EPSILON)); @@ -1886,7 +1908,7 @@ void PrintObject::discover_horizontal_shells() // Slic3r::debugf " looking for neighbors on layer %d...\n", $n; // Reference to the lower layer of a TOP surface, or an upper layer of a BOTTOM surface. LayerRegion *neighbor_layerm = m_layers[n]->regions()[region_id]; - + // find intersection between neighbor and current layer's surfaces // intersections have contours and holes // we update $solid so that we limit the next neighbor layer to the areas that were @@ -1921,7 +1943,7 @@ void PrintObject::discover_horizontal_shells() continue; } } - + if (region_config.fill_density.value == 0) { // if we're printing a hollow object we discard any solid shell thinner // than a perimeter width, since it's probably just crossing a sloping wall @@ -1929,7 +1951,7 @@ void PrintObject::discover_horizontal_shells() // obeying the solid shell count option strictly (DWIM!) float margin = float(neighbor_layerm->flow(frExternalPerimeter).scaled_width()); Polygons too_narrow = diff( - new_internal_solid, + new_internal_solid, opening(new_internal_solid, margin, margin + ClipperSafetyOffset, jtMiter, 5)); // Trim the regularized region by the original region. if (! too_narrow.empty()) @@ -1959,20 +1981,20 @@ void PrintObject::discover_horizontal_shells() for (const Surface &surface : neighbor_layerm->fill_surfaces.surfaces) if (surface.is_internal() && !surface.is_bridge()) polygons_append(internal, to_polygons(surface.expolygon)); - polygons_append(new_internal_solid, + polygons_append(new_internal_solid, intersection( expand(too_narrow, +margin), // Discard bridges as they are grown for anchoring and we can't - // remove such anchors. (This may happen when a bridge is being + // remove such anchors. (This may happen when a bridge is being // anchored onto a wall where little space remains after the bridge - // is grown, and that little space is an internal solid shell so + // is grown, and that little space is an internal solid shell so // it triggers this too_narrow logic.) internal)); // see https://github.com/prusa3d/PrusaSlicer/pull/3426 // solid = new_internal_solid; } } - + // internal-solid are the union of the existing internal-solid surfaces // and new ones SurfaceCollection backup = std::move(neighbor_layerm->fill_surfaces); @@ -2051,11 +2073,11 @@ void PrintObject::combine_infill() current_height += layer->height; ++ num_layers; } - + // Append lower layers (if any) to uppermost layer. combine[m_layers.size() - 1] = num_layers; } - + // loop through layers to which we have assigned layers to combine for (size_t layer_idx = 0; layer_idx < m_layers.size(); ++ layer_idx) { m_print->throw_if_canceled(); @@ -2075,8 +2097,8 @@ void PrintObject::combine_infill() intersection = intersection_ex(layerms[i]->fill_surfaces.filter_by_type(stInternal), intersection); double area_threshold = layerms.front()->infill_area_threshold(); if (! intersection.empty() && area_threshold > 0.) - intersection.erase(std::remove_if(intersection.begin(), intersection.end(), - [area_threshold](const ExPolygon &expoly) { return expoly.area() <= area_threshold; }), + intersection.erase(std::remove_if(intersection.begin(), intersection.end(), + [area_threshold](const ExPolygon &expoly) { return expoly.area() <= area_threshold; }), intersection.end()); if (intersection.empty()) continue; @@ -2088,15 +2110,15 @@ void PrintObject::combine_infill() // so let's remove those areas from all layers. Polygons intersection_with_clearance; intersection_with_clearance.reserve(intersection.size()); - float clearance_offset = + float clearance_offset = 0.5f * layerms.back()->flow(frPerimeter).scaled_width() + - // Because fill areas for rectilinear and honeycomb are grown + // Because fill areas for rectilinear and honeycomb are grown // later to overlap perimeters, we need to counteract that too. ((region.config().fill_pattern == ipRectilinear || region.config().fill_pattern == ipMonotonic || region.config().fill_pattern == ipGrid || region.config().fill_pattern == ipLine || - region.config().fill_pattern == ipHoneycomb) ? 1.5f : 0.5f) * + region.config().fill_pattern == ipHoneycomb) ? 1.5f : 0.5f) * layerms.back()->flow(frSolidInfill).scaled_width(); for (ExPolygon &expoly : intersection) polygons_append(intersection_with_clearance, offset(expoly, clearance_offset)); @@ -2308,7 +2330,7 @@ static void project_triangles_to_slabs(ConstLayerPtrsAdaptor layers, const index // The resulting triangles are fed to the Clipper library, which seem to handle flipped triangles well. // if (cross2(Vec2d((poly.pts[1] - poly.pts[0]).cast()), Vec2d((poly.pts[2] - poly.pts[1]).cast())) < 0) // std::swap(poly.pts.front(), poly.pts.back()); - + out[layer_id].emplace_back(std::move(poly.pts)); ++layer_id; } diff --git a/src/libslic3r/SupportableIssuesSearch.cpp b/src/libslic3r/SupportableIssuesSearch.cpp new file mode 100644 index 0000000000..bace1aea00 --- /dev/null +++ b/src/libslic3r/SupportableIssuesSearch.cpp @@ -0,0 +1,149 @@ +#include "SupportableIssuesSearch.hpp" + +#include "tbb/parallel_for.h" +#include "tbb/blocked_range.h" +#include "tbb/parallel_reduce.h" +#include + +#include "libslic3r/Layer.hpp" +#include "libslic3r/EdgeGrid.hpp" +#include "libslic3r/ClipperUtils.hpp" + +namespace Slic3r { +namespace SupportableIssues { + +struct Params { + float bridge_distance = 5.0f; + float printable_protrusion_distance = 1.0f; +}; + +namespace Impl { + +struct LayerDescriptor { + Vec2f centroid { 0.0f, 0.0f }; + size_t segments_count { 0 }; + float perimeter_length { 0.0f }; +}; + +struct EdgeGridWrapper { + EdgeGridWrapper(coord_t resolution, ExPolygons ex_polys) : + ex_polys(ex_polys) { + + grid.create(this->ex_polys, resolution); + grid.calculate_sdf(); + } + EdgeGrid::Grid grid; + ExPolygons ex_polys; +} +; + +EdgeGridWrapper compute_layer_merged_edge_grid(const Layer *layer) { + static const float eps = float(scale_(layer->object()->config().slice_closing_radius.value)); + // merge with offset + ExPolygons merged = layer->merged(eps); + // ofsset back + ExPolygons layer_outline = offset_ex(merged, -eps); + + float min_region_flow_width { }; + for (const auto *region : layer->regions()) { + min_region_flow_width = std::max(min_region_flow_width, region->flow(FlowRole::frExternalPerimeter).width()); + } + std::cout << "min_region_flow_width: " << min_region_flow_width << std::endl; + return EdgeGridWrapper(scale_(min_region_flow_width), layer_outline); +} + +void check_extrusion_entity_stability(const ExtrusionEntity *entity, const EdgeGridWrapper &supported_grid, + const Params ¶ms) { + if (entity->is_collection()){ + for (const auto* e: static_cast(entity).entities){ + check_extrusion_entity_stability(e, supported_grid, params); + } + } else { //single extrusion path, with possible varying parameters + entity->as_polyline().points; + } + + +} + +void check_layer_stability(const PrintObject *po, size_t layer_idx, const Params ¶ms) { + if (layer_idx == 0) { + // first layer is usually ok + return; + } + const Layer *layer = po->get_layer(layer_idx); + const Layer *prev_layer = layer->lower_layer; + EdgeGridWrapper supported_grid = compute_layer_merged_edge_grid(prev_layer); + + for (const LayerRegion *layer_region : layer->regions()) { + coordf_t flow_width = coordf_t( + scale_(layer_region->flow(FlowRole::frExternalPerimeter).width())); + for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { + for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { + if (perimeter->role() == ExtrusionRole::erExternalPerimeter) { + check_extrusion_entity_stability(perimeter, supported_grid, params); + } // ex_perimeter + } // perimeter + } // ex_entity + } +} + +} //Impl End + +void quick_search(const PrintObject *po, const Params ¶ms = Params { }) { + using namespace Impl; + std::vector descriptors(po->layer_count()); + + tbb::parallel_for(tbb::blocked_range(0, po->layer_count()), + [&](tbb::blocked_range r) { + for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) { + const Layer *layer = po->get_layer(layer_idx); + + LayerDescriptor &descriptor = descriptors[layer_idx]; + size_t point_count { 0 }; + + for (const LayerRegion *layer_region : layer->regions()) { + for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { + for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { + if (perimeter->role() == ExtrusionRole::erExternalPerimeter) { + assert(perimeter->is_loop()); + descriptor.segments_count++; + const ExtrusionLoop *loop = static_cast(perimeter); + for (const ExtrusionPath& path : loop->paths) { + Vec2f prev_pos = unscale(path.polyline.last_point()).cast(); + for (size_t p_idx = 0; p_idx < path.polyline.points.size(); ++p_idx) { + point_count++; + Vec2f point_pos = unscale(path.polyline.points[p_idx]).cast(); + descriptor.centroid += point_pos; + descriptor.perimeter_length += (point_pos - prev_pos).norm(); + prev_pos = point_pos; + } //point + } //path + } // ex_perimeter + } // perimeter +} // ex_entity +} // region + + descriptor.centroid /= float(point_count); + + } // layer + } // thread + ); + + for (size_t desc_idx = 0; desc_idx < descriptors.size(); ++desc_idx) { + const LayerDescriptor &descriptor = descriptors[desc_idx]; + std::cout << "SIS layer idx: " << desc_idx << " reg count: " << descriptor.segments_count << " len: " + << descriptor.perimeter_length << + " centroid: " << descriptor.centroid.x() << " | " << descriptor.centroid.y() << std::endl; + if (desc_idx > 0) { + const LayerDescriptor &prev = descriptors[desc_idx - 1]; + std::cout << "SIS diff: " << desc_idx << " reg count: " + << (int(descriptor.segments_count) - int(prev.segments_count)) << + " len: " << (descriptor.perimeter_length - prev.perimeter_length) << + " centroid: " << (descriptor.centroid - prev.centroid).norm() << std::endl; + } + } + +} + +} +} diff --git a/src/libslic3r/SupportableIssuesSearch.hpp b/src/libslic3r/SupportableIssuesSearch.hpp new file mode 100644 index 0000000000..dbec52aff0 --- /dev/null +++ b/src/libslic3r/SupportableIssuesSearch.hpp @@ -0,0 +1,16 @@ +#ifndef SRC_LIBSLIC3R_SUPPORTABLEISSUESSEARCH_HPP_ +#define SRC_LIBSLIC3R_SUPPORTABLEISSUESSEARCH_HPP_ + +#include "libslic3r/Print.hpp" + +namespace Slic3r { + +namespace SupportableIssues { + +void quick_search(const PrintObject *po); + +} + +} + +#endif /* SRC_LIBSLIC3R_SUPPORTABLEISSUESSEARCH_HPP_ */ From 706cd63e610949f383937c4f71cf44c9aefe194d Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Thu, 7 Apr 2022 12:44:50 +0200 Subject: [PATCH 02/78] Beta version of the algorithm Implemented long unsupported segments detection, which considers also curvature Implemented detection of curved segments at the edge of the previous layer - danger of warping/curling --- src/libslic3r/CMakeLists.txt | 2 + src/libslic3r/PrintObject.cpp | 35 ++- src/libslic3r/SupportableIssuesSearch.cpp | 318 ++++++++++++++++++---- src/libslic3r/SupportableIssuesSearch.hpp | 20 +- src/libslic3r/TriangleSelectorWrapper.cpp | 1 + src/libslic3r/TriangleSelectorWrapper.hpp | 42 +++ 6 files changed, 366 insertions(+), 52 deletions(-) create mode 100644 src/libslic3r/TriangleSelectorWrapper.cpp create mode 100644 src/libslic3r/TriangleSelectorWrapper.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index a342da8319..0a0062a754 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -275,6 +275,8 @@ set(SLIC3R_SOURCES TriangleSelector.hpp TriangleSetSampling.cpp TriangleSetSampling.hpp + TriangleSelectorWrapper.cpp + TriangleSelectorWrapper.hpp MTUtils.hpp Zipper.hpp Zipper.cpp diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 4bcf74fe5e..cf6f168dff 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -402,12 +402,38 @@ void PrintObject::find_supportable_issues() BOOST_LOG_TRIVIAL(debug) << "Searching supportable issues - start"; //TODO status number? - m_print->set_status(70, L("Searching supportable issues")); - - if (this->has_support()) { + m_print->set_status(75, L("Searching supportable issues")); + if (!this->m_config.support_material) { + std::vector problematic_layers = SupportableIssues::quick_search(this); + if (!problematic_layers.empty()){ + //TODO report problems + } } else { - SupportableIssues::quick_search(this); + SupportableIssues::Issues issues = SupportableIssues::full_search(this); + if (!issues.supports_nedded.empty()) { + auto obj_transform = this->trafo_centered(); + for (ModelVolume *model_volume : this->model_object()->volumes) { + if (model_volume->type() == ModelVolumeType::MODEL_PART) { + Transform3d model_transformation = model_volume->get_matrix(); + Transform3d inv_transform = (obj_transform * model_transformation).inverse(); + TriangleSelectorWrapper selector { model_volume->mesh() }; + + for (const Vec3f &support_point : issues.supports_nedded) { + selector.enforce_spot(Vec3f(inv_transform.cast() * support_point), 1.0f); + } + + model_volume->supported_facets.set(selector.selector); + +#if 1 + indexed_triangle_set copy = model_volume->mesh().its; + its_transform(copy, obj_transform * model_transformation); + its_write_obj(copy, + debug_out_path(("model" + std::to_string(model_volume->id().id) + ".obj").c_str()).c_str()); +#endif + } + } + } } m_print->throw_if_canceled(); @@ -417,6 +443,7 @@ void PrintObject::find_supportable_issues() } } + void PrintObject::generate_support_material() { if (this->set_started(posSupportMaterial)) { diff --git a/src/libslic3r/SupportableIssuesSearch.cpp b/src/libslic3r/SupportableIssuesSearch.cpp index bace1aea00..cdd3130a0a 100644 --- a/src/libslic3r/SupportableIssuesSearch.cpp +++ b/src/libslic3r/SupportableIssuesSearch.cpp @@ -4,18 +4,31 @@ #include "tbb/blocked_range.h" #include "tbb/parallel_reduce.h" #include +#include #include "libslic3r/Layer.hpp" #include "libslic3r/EdgeGrid.hpp" #include "libslic3r/ClipperUtils.hpp" +#define DEBUG_FILES + +#ifdef DEBUG_FILES +#include +#endif + namespace Slic3r { namespace SupportableIssues { -struct Params { - float bridge_distance = 5.0f; - float printable_protrusion_distance = 1.0f; -}; +void Issues::add(const Issues &layer_issues) { + supports_nedded.insert(supports_nedded.end(), + layer_issues.supports_nedded.begin(), layer_issues.supports_nedded.end()); + curling_up.insert(curling_up.end(), layer_issues.curling_up.begin(), + layer_issues.curling_up.end()); +} + +bool Issues::empty() const { + return supports_nedded.empty() && curling_up.empty(); +} namespace Impl { @@ -34,62 +47,218 @@ struct EdgeGridWrapper { } EdgeGrid::Grid grid; ExPolygons ex_polys; -} -; +}; -EdgeGridWrapper compute_layer_merged_edge_grid(const Layer *layer) { - static const float eps = float(scale_(layer->object()->config().slice_closing_radius.value)); - // merge with offset - ExPolygons merged = layer->merged(eps); - // ofsset back - ExPolygons layer_outline = offset_ex(merged, -eps); +#ifdef DEBUG_FILES +void debug_export(Issues issues, std::string file_name) { + Slic3r::CNumericLocalesSetter locales_setter; - float min_region_flow_width { }; - for (const auto *region : layer->regions()) { - min_region_flow_width = std::max(min_region_flow_width, region->flow(FlowRole::frExternalPerimeter).width()); - } - std::cout << "min_region_flow_width: " << min_region_flow_width << std::endl; - return EdgeGridWrapper(scale_(min_region_flow_width), layer_outline); -} - -void check_extrusion_entity_stability(const ExtrusionEntity *entity, const EdgeGridWrapper &supported_grid, - const Params ¶ms) { - if (entity->is_collection()){ - for (const auto* e: static_cast(entity).entities){ - check_extrusion_entity_stability(e, supported_grid, params); + { + FILE *fp = boost::nowide::fopen(debug_out_path((file_name + "_supports.obj").c_str()).c_str(), "w"); + if (fp == nullptr) { + BOOST_LOG_TRIVIAL(error) + << "Debug files: Couldn't open " << file_name << " for writing"; + return; } - } else { //single extrusion path, with possible varying parameters - entity->as_polyline().points; - } + for (size_t i = 0; i < issues.supports_nedded.size(); ++i) { + fprintf(fp, "v %f %f %f %f %f %f\n", + issues.supports_nedded[i](0), issues.supports_nedded[i](1), issues.supports_nedded[i](2), + 1.0, 0.0, 0.0 + ); + } + + fclose(fp); + } + { + FILE *fp = boost::nowide::fopen(debug_out_path((file_name + "_curling.obj").c_str()).c_str(), "w"); + if (fp == nullptr) { + BOOST_LOG_TRIVIAL(error) + << "Debug files: Couldn't open " << file_name << " for writing"; + return; + } + + for (size_t i = 0; i < issues.curling_up.size(); ++i) { + fprintf(fp, "v %f %f %f %f %f %f\n", + issues.curling_up[i](0), issues.curling_up[i](1), issues.curling_up[i](2), + 0.0, 1.0, 0.0 + ); + } + fclose(fp); + } } +#endif -void check_layer_stability(const PrintObject *po, size_t layer_idx, const Params ¶ms) { - if (layer_idx == 0) { - // first layer is usually ok - return; +EdgeGridWrapper compute_layer_edge_grid(const Layer *layer) { + float min_region_flow_width { 1.0f }; + for (const auto *region : layer->regions()) { + min_region_flow_width = std::min(min_region_flow_width, region->flow(FlowRole::frExternalPerimeter).width()); } - const Layer *layer = po->get_layer(layer_idx); - const Layer *prev_layer = layer->lower_layer; - EdgeGridWrapper supported_grid = compute_layer_merged_edge_grid(prev_layer); - + ExPolygons ex_polygons; for (const LayerRegion *layer_region : layer->regions()) { - coordf_t flow_width = coordf_t( - scale_(layer_region->flow(FlowRole::frExternalPerimeter).width())); for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { if (perimeter->role() == ExtrusionRole::erExternalPerimeter) { - check_extrusion_entity_stability(perimeter, supported_grid, params); + ex_polygons.push_back(ExPolygon { perimeter->as_polyline().points }); } // ex_perimeter } // perimeter } // ex_entity } + + return EdgeGridWrapper(scale_(min_region_flow_width), ex_polygons); +} + +Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, size_t layer_idx, + float slice_z, + coordf_t flow_width, + const EdgeGridWrapper &supported_grid, + const Params ¶ms) { + + Issues issues { }; + if (entity->is_collection()) { + for (const auto *e : static_cast(entity)->entities) { + issues.add(check_extrusion_entity_stability(e, layer_idx, slice_z, flow_width, supported_grid, params)); + } + } else { //single extrusion path, with possible varying parameters + Points points = entity->as_polyline().points; + float unsupported_distance = params.bridge_distance + 1.0f; + float curvature = 0; + float max_curvature = 0; + Vec2f tmp = unscale(points[0]).cast(); + Vec3f prev_point = Vec3f(tmp.x(), tmp.y(), slice_z); + + for (size_t point_index = 0; point_index < points.size(); ++point_index) { + std::cout << "index: " << point_index << " dist: " << unsupported_distance << " curvature: " + << curvature << " max curvature: " << max_curvature << std::endl; + + Vec2f tmp = unscale(points[point_index]).cast(); + Vec3f u_point = Vec3f(tmp.x(), tmp.y(), slice_z); + + coordf_t dist_from_prev_layer { 0 }; + if (!supported_grid.grid.signed_distance(points[point_index], flow_width, dist_from_prev_layer)) { + issues.supports_nedded.push_back(u_point); + continue; + } + + constexpr float limit_overlap_factor = 0.5; + + if (dist_from_prev_layer > flow_width) { //unsupported + std::cout << "index: " << point_index << " unsupported " << std::endl; + unsupported_distance += (u_point - prev_point).norm(); + } else { + std::cout << "index: " << point_index << " grounded " << std::endl; + unsupported_distance = 0; + } + + std::cout << "index: " << point_index << " dfromprev: " << dist_from_prev_layer << std::endl; + + if (dist_from_prev_layer > flow_width * limit_overlap_factor && point_index < points.size() - 1) { + const Vec2f v1 = (u_point - prev_point).head<2>(); + const Vec2f v2 = unscale(points[point_index + 1]).cast() - u_point.head<2>(); + float dot = v1(0) * v2(0) + v1(1) * v2(1); + float cross = v1(0) * v2(1) - v1(1) * v2(0); + float angle = float(atan2(float(cross), float(dot))); + + std::cout << "index: " << point_index << " angle: " << angle << std::endl; + + curvature += angle; + max_curvature = std::max(abs(curvature), max_curvature); + } + + if (!(dist_from_prev_layer > flow_width * limit_overlap_factor)) { + std::cout << "index: " << point_index << " reset curvature" << std::endl; + max_curvature = 0; + curvature = 0; + } + + if (unsupported_distance > params.bridge_distance / (1 + int(max_curvature * 9 / PI))) { + issues.supports_nedded.push_back(u_point); + unsupported_distance = 0; + curvature = 0; + max_curvature = 0; + } + + if (max_curvature / (PI * unsupported_distance) > params.limit_curvature) { + issues.curling_up.push_back(u_point); + curvature = 0; + max_curvature = 0; + } + + prev_point = u_point; + } + } + return issues; +} + +//TODO needs revision +coordf_t get_flow_width(const LayerRegion *region, ExtrusionRole role) { + switch (role) { + case ExtrusionRole::erBridgeInfill: + return region->flow(FlowRole::frExternalPerimeter).scaled_width(); + case ExtrusionRole::erExternalPerimeter: + return region->flow(FlowRole::frExternalPerimeter).scaled_width(); + case ExtrusionRole::erGapFill: + return region->flow(FlowRole::frInfill).scaled_width(); + case ExtrusionRole::erPerimeter: + return region->flow(FlowRole::frPerimeter).scaled_width(); + case ExtrusionRole::erSolidInfill: + return region->flow(FlowRole::frSolidInfill).scaled_width(); + default: + return region->flow(FlowRole::frExternalPerimeter).scaled_width(); + } +} + +Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_check, const Params ¶ms) { + std::cout << "Checking: " << layer_idx << std::endl; + if (layer_idx == 0) { + // first layer is usually ok + return {}; + } + const Layer *layer = po->get_layer(layer_idx); + EdgeGridWrapper supported_grid = compute_layer_edge_grid(layer->lower_layer); + + Issues issues { }; + if (full_check) { + for (const LayerRegion *layer_region : layer->regions()) { + for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { + for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { + issues.add(check_extrusion_entity_stability(perimeter, layer_idx, + layer->slice_z, get_flow_width(layer_region, perimeter->role()), + supported_grid, params)); + } // perimeter + } // ex_entity + for (const ExtrusionEntity *ex_entity : layer_region->fills.entities) { + for (const ExtrusionEntity *fill : static_cast(ex_entity)->entities) { + if (fill->role() == ExtrusionRole::erGapFill || fill->role() == ExtrusionRole::erBridgeInfill) { + issues.add(check_extrusion_entity_stability(fill, layer_idx, + layer->slice_z, get_flow_width(layer_region, fill->role()), + supported_grid, params)); + } + } // fill + } // ex_entity + } // region + } else { //check only external perimeters + for (const LayerRegion *layer_region : layer->regions()) { + for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { + for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { + if (perimeter->role() == ExtrusionRole::erExternalPerimeter) { + std::cout << "checking ex perimeter " << std::endl; + issues.add(check_extrusion_entity_stability(perimeter, layer_idx, + layer->slice_z, get_flow_width(layer_region, perimeter->role()), + supported_grid, params)); + }; // ex_perimeter + } // perimeter + } // ex_entity + } //region + } + + return issues; } } //Impl End -void quick_search(const PrintObject *po, const Params ¶ms = Params { }) { +std::vector quick_search(const PrintObject *po, const Params ¶ms) { using namespace Impl; std::vector descriptors(po->layer_count()); @@ -129,20 +298,75 @@ void quick_search(const PrintObject *po, const Params ¶ms = Params { }) { } // thread ); - for (size_t desc_idx = 0; desc_idx < descriptors.size(); ++desc_idx) { + std::vector suspicious_layers_indices { }; + + for (size_t desc_idx = 1; desc_idx < descriptors.size(); ++desc_idx) { + const LayerDescriptor &prev = descriptors[desc_idx - 1]; const LayerDescriptor &descriptor = descriptors[desc_idx]; + if (descriptor.segments_count - prev.segments_count != 0 + || + std::abs(descriptor.perimeter_length - prev.perimeter_length) + > params.perimeter_length_diff_tolerance || + (descriptor.centroid - prev.centroid).norm() > params.centroid_offset_tolerance + ) { + suspicious_layers_indices.push_back(desc_idx); + } +#ifdef DEBUG_FILES std::cout << "SIS layer idx: " << desc_idx << " reg count: " << descriptor.segments_count << " len: " << descriptor.perimeter_length << " centroid: " << descriptor.centroid.x() << " | " << descriptor.centroid.y() << std::endl; - if (desc_idx > 0) { - const LayerDescriptor &prev = descriptors[desc_idx - 1]; - std::cout << "SIS diff: " << desc_idx << " reg count: " - << (int(descriptor.segments_count) - int(prev.segments_count)) << - " len: " << (descriptor.perimeter_length - prev.perimeter_length) << - " centroid: " << (descriptor.centroid - prev.centroid).norm() << std::endl; + std::cout << "SIS diff: " << desc_idx << " reg count: " + << (int(descriptor.segments_count) - int(prev.segments_count)) << + " len: " << (descriptor.perimeter_length - prev.perimeter_length) << + " centroid: " << (descriptor.centroid - prev.centroid).norm() << std::endl; +#endif + } + + std::vector layer_needs_supports(suspicious_layers_indices.size(), false); + tbb::parallel_for(tbb::blocked_range(0, suspicious_layers_indices.size()), + [&](tbb::blocked_range r) { + for (size_t suspicious_index = r.begin(); suspicious_index < r.end(); ++suspicious_index) { + auto layer_issues = check_layer_stability(po, suspicious_layers_indices[suspicious_index], + false, + params); + if (!layer_issues.supports_nedded.empty()) { + layer_needs_supports[suspicious_index] = true; + } + } + }); + + std::vector problematic_layers; + + for (size_t index = suspicious_layers_indices.size() - 1; index <= 0; ++index) { + if (!layer_needs_supports[index]) { + problematic_layers.push_back(suspicious_layers_indices[index]); + } + } + return problematic_layers; +} + +Issues full_search(const PrintObject *po, const Params ¶ms) { + using namespace Impl; + Issues issues { }; + for (size_t layer_idx = 1; layer_idx < po->layer_count(); ++layer_idx) { + auto layer_issues = check_layer_stability(po, layer_idx, true, params); + if (!layer_issues.empty()) { + issues.add(layer_issues); } } +#ifdef DEBUG_FILES + Impl::debug_export(issues, "issues"); +#endif + +// tbb::parallel_for(tbb::blocked_range(0, suspicious_layers_indices.size()), +// [&](tbb::blocked_range r) { +// for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) { +// check_layer_stability(po, suspicious_layers_indices[layer_idx], params); +// } +// }); + + return issues; } } diff --git a/src/libslic3r/SupportableIssuesSearch.hpp b/src/libslic3r/SupportableIssuesSearch.hpp index dbec52aff0..22289c923c 100644 --- a/src/libslic3r/SupportableIssuesSearch.hpp +++ b/src/libslic3r/SupportableIssuesSearch.hpp @@ -7,7 +7,25 @@ namespace Slic3r { namespace SupportableIssues { -void quick_search(const PrintObject *po); +struct Params { + float bridge_distance = 5.0f; + float limit_curvature = 0.25f; + + float perimeter_length_diff_tolerance = 8.0f; + float centroid_offset_tolerance = 1.0f; +}; + + +struct Issues { + std::vector supports_nedded; + std::vector curling_up; + + void add(const Issues &layer_issues); + bool empty() const; +}; + +std::vector quick_search(const PrintObject *po, const Params ¶ms = Params { }); +Issues full_search(const PrintObject *po, const Params ¶ms = Params { }); } diff --git a/src/libslic3r/TriangleSelectorWrapper.cpp b/src/libslic3r/TriangleSelectorWrapper.cpp new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/src/libslic3r/TriangleSelectorWrapper.cpp @@ -0,0 +1 @@ + diff --git a/src/libslic3r/TriangleSelectorWrapper.hpp b/src/libslic3r/TriangleSelectorWrapper.hpp new file mode 100644 index 0000000000..f818d8be2b --- /dev/null +++ b/src/libslic3r/TriangleSelectorWrapper.hpp @@ -0,0 +1,42 @@ +#ifndef SRC_LIBSLIC3R_TRIANGLESELECTORWRAPPER_HPP_ +#define SRC_LIBSLIC3R_TRIANGLESELECTORWRAPPER_HPP_ + +#include + +#include "TriangleSelector.hpp" +#include "AABBTreeIndirect.hpp" + +namespace Slic3r { + +class TriangleSelectorWrapper { +public: + const TriangleMesh &mesh; + TriangleSelector selector; + AABBTreeIndirect::Tree<3, float> triangles_tree; + + TriangleSelectorWrapper(const TriangleMesh &mesh) : + mesh(mesh), selector(mesh), triangles_tree(AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(mesh.its.vertices, mesh.its.indices)) { + + } + + void enforce_spot(const Vec3f &point, float radius) { + size_t hit_face_index; + Vec3f hit_point; + auto dist = AABBTreeIndirect::squared_distance_to_indexed_triangle_set(mesh.its.vertices, mesh.its.indices, + triangles_tree, + point, hit_face_index, hit_point); + if (dist < 0 || dist > radius) + return; + + std::unique_ptr cursor = std::make_unique(point, point, + radius, Transform3d::Identity(), TriangleSelector::ClippingPlane { }); + + selector.select_patch(hit_face_index, std::move(cursor), EnforcerBlockerType::ENFORCER, Transform3d::Identity(), true, + 0.0f); + } + +}; + +} + +#endif /* SRC_LIBSLIC3R_TRIANGLESELECTORWRAPPER_HPP_ */ From e516ba0dd0ea0c598d8ee8b912b2e4c3782bf6da Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Thu, 7 Apr 2022 13:52:59 +0200 Subject: [PATCH 03/78] Moved TriangleSelectorWrapper methods to cpp file, added comment describing problems with FacetsAnnotations/TriangleSelector structure --- src/libslic3r/PrintObject.cpp | 1 + src/libslic3r/SupportableIssuesSearch.cpp | 2 +- src/libslic3r/TriangleSelectorWrapper.cpp | 29 +++++++++++++++++++++++ src/libslic3r/TriangleSelectorWrapper.hpp | 29 +++++++---------------- 4 files changed, 39 insertions(+), 22 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index cf6f168dff..5c1d918aea 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -17,6 +17,7 @@ #include "Fill/FillLightning.hpp" #include "Format/STL.hpp" #include "SupportableIssuesSearch.hpp" +#include "TriangleSelectorWrapper.hpp" #include #include diff --git a/src/libslic3r/SupportableIssuesSearch.cpp b/src/libslic3r/SupportableIssuesSearch.cpp index cdd3130a0a..51a425a7ed 100644 --- a/src/libslic3r/SupportableIssuesSearch.cpp +++ b/src/libslic3r/SupportableIssuesSearch.cpp @@ -172,7 +172,7 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, size_t la curvature = 0; } - if (unsupported_distance > params.bridge_distance / (1 + int(max_curvature * 9 / PI))) { + if (unsupported_distance > params.bridge_distance / (1 + int(max_curvature * 7 / PI))) { issues.supports_nedded.push_back(u_point); unsupported_distance = 0; curvature = 0; diff --git a/src/libslic3r/TriangleSelectorWrapper.cpp b/src/libslic3r/TriangleSelectorWrapper.cpp index 8b13789179..98d00bc985 100644 --- a/src/libslic3r/TriangleSelectorWrapper.cpp +++ b/src/libslic3r/TriangleSelectorWrapper.cpp @@ -1 +1,30 @@ +#include "Model.hpp" +#include "TriangleSelectorWrapper.hpp" +#include +namespace Slic3r { + +TriangleSelectorWrapper::TriangleSelectorWrapper(const TriangleMesh &mesh) : + mesh(mesh), selector(mesh), triangles_tree( + AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(mesh.its.vertices, mesh.its.indices)) { + +} + +void TriangleSelectorWrapper::enforce_spot(const Vec3f &point, float radius) { + size_t hit_face_index; + Vec3f hit_point; + auto dist = AABBTreeIndirect::squared_distance_to_indexed_triangle_set(mesh.its.vertices, mesh.its.indices, + triangles_tree, + point, hit_face_index, hit_point); + if (dist < 0 || dist > radius) + return; + + std::unique_ptr cursor = std::make_unique(point, point, + radius, Transform3d::Identity(), TriangleSelector::ClippingPlane { }); + + selector.select_patch(hit_face_index, std::move(cursor), EnforcerBlockerType::ENFORCER, Transform3d::Identity(), + true, + 0.0f); +} + +} diff --git a/src/libslic3r/TriangleSelectorWrapper.hpp b/src/libslic3r/TriangleSelectorWrapper.hpp index f818d8be2b..f3b56205fa 100644 --- a/src/libslic3r/TriangleSelectorWrapper.hpp +++ b/src/libslic3r/TriangleSelectorWrapper.hpp @@ -1,39 +1,26 @@ #ifndef SRC_LIBSLIC3R_TRIANGLESELECTORWRAPPER_HPP_ #define SRC_LIBSLIC3R_TRIANGLESELECTORWRAPPER_HPP_ -#include - #include "TriangleSelector.hpp" #include "AABBTreeIndirect.hpp" namespace Slic3r { +//NOTE: We need to replace the FacetsAnnotation struct for support storage (or extend/add another) +// Problems: Does not support negative volumes, strange usage for supports computed from extrusion - +// expensively converted back to triangles and then sliced again. +// Another problem is weird and very limited interface when painting supports via algorithms + + class TriangleSelectorWrapper { public: const TriangleMesh &mesh; TriangleSelector selector; AABBTreeIndirect::Tree<3, float> triangles_tree; - TriangleSelectorWrapper(const TriangleMesh &mesh) : - mesh(mesh), selector(mesh), triangles_tree(AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(mesh.its.vertices, mesh.its.indices)) { + TriangleSelectorWrapper(const TriangleMesh &mesh); - } - - void enforce_spot(const Vec3f &point, float radius) { - size_t hit_face_index; - Vec3f hit_point; - auto dist = AABBTreeIndirect::squared_distance_to_indexed_triangle_set(mesh.its.vertices, mesh.its.indices, - triangles_tree, - point, hit_face_index, hit_point); - if (dist < 0 || dist > radius) - return; - - std::unique_ptr cursor = std::make_unique(point, point, - radius, Transform3d::Identity(), TriangleSelector::ClippingPlane { }); - - selector.select_patch(hit_face_index, std::move(cursor), EnforcerBlockerType::ENFORCER, Transform3d::Identity(), true, - 0.0f); - } + void enforce_spot(const Vec3f &point, float radius); }; From d41b20547decc38e03e2e19eeb0ebcf41db909a6 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Mon, 11 Apr 2022 17:20:29 +0200 Subject: [PATCH 04/78] greatly upgraded the algorithm for support placement - added dynamic splitting of long paths, included flow width of current and previous layer, refactored and renamed parameters --- src/libslic3r/PrintObject.cpp | 19 +- src/libslic3r/SupportableIssuesSearch.cpp | 198 +++++++++++-------- src/libslic3r/SupportableIssuesSearch.hpp | 9 +- src/libslic3r/TriangleSelectorWrapper.cpp | 5 +- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp | 4 + 5 files changed, 147 insertions(+), 88 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 5c1d918aea..c9f64cfb3c 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -18,6 +18,7 @@ #include "Format/STL.hpp" #include "SupportableIssuesSearch.hpp" #include "TriangleSelectorWrapper.hpp" +#include "format.hpp" #include #include @@ -397,18 +398,26 @@ void PrintObject::ironing() } } + void PrintObject::find_supportable_issues() { if (this->set_started(posSupportableIssuesSearch)) { BOOST_LOG_TRIVIAL(debug) << "Searching supportable issues - start"; - //TODO status number? m_print->set_status(75, L("Searching supportable issues")); if (!this->m_config.support_material) { std::vector problematic_layers = SupportableIssues::quick_search(this); - if (!problematic_layers.empty()){ - //TODO report problems + if (!problematic_layers.empty()) { + std::cout << "Object needs supports" << std::endl; + this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, + L("Supportable issues found. Consider enabling supports for this object")); + this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, + L("Supportable issues found. Consider enabling supports for this object")); + for (size_t index = 0; index < std::min(problematic_layers.size(), size_t(4)); ++index) { + this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, + format(L("Layer with issues: %1%"), problematic_layers[index] + 1)); + } } } else { SupportableIssues::Issues issues = SupportableIssues::full_search(this); @@ -421,7 +430,7 @@ void PrintObject::find_supportable_issues() TriangleSelectorWrapper selector { model_volume->mesh() }; for (const Vec3f &support_point : issues.supports_nedded) { - selector.enforce_spot(Vec3f(inv_transform.cast() * support_point), 1.0f); + selector.enforce_spot(Vec3f(inv_transform.cast() * support_point), 0.3f); } model_volume->supported_facets.set(selector.selector); @@ -430,7 +439,7 @@ void PrintObject::find_supportable_issues() indexed_triangle_set copy = model_volume->mesh().its; its_transform(copy, obj_transform * model_transformation); its_write_obj(copy, - debug_out_path(("model" + std::to_string(model_volume->id().id) + ".obj").c_str()).c_str()); + debug_out_path("model.obj").c_str()); #endif } } diff --git a/src/libslic3r/SupportableIssuesSearch.cpp b/src/libslic3r/SupportableIssuesSearch.cpp index 51a425a7ed..ea2ba41063 100644 --- a/src/libslic3r/SupportableIssuesSearch.cpp +++ b/src/libslic3r/SupportableIssuesSearch.cpp @@ -5,6 +5,7 @@ #include "tbb/parallel_reduce.h" #include #include +#include #include "libslic3r/Layer.hpp" #include "libslic3r/EdgeGrid.hpp" @@ -39,14 +40,24 @@ struct LayerDescriptor { }; struct EdgeGridWrapper { - EdgeGridWrapper(coord_t resolution, ExPolygons ex_polys) : - ex_polys(ex_polys) { + EdgeGridWrapper(coord_t edge_width, ExPolygons ex_polys) : + ex_polys(ex_polys), edge_width(edge_width) { - grid.create(this->ex_polys, resolution); + grid.create(this->ex_polys, edge_width); grid.calculate_sdf(); } + + bool signed_distance(const Point &point, coordf_t point_width, coordf_t &dist_out) const { + coordf_t tmp_dist_out; + bool found = grid.signed_distance(point, edge_width, tmp_dist_out); + dist_out = tmp_dist_out - edge_width / 2 - point_width / 2; + return found; + + } + EdgeGrid::Grid grid; ExPolygons ex_polys; + coord_t edge_width; }; #ifdef DEBUG_FILES @@ -99,8 +110,13 @@ EdgeGridWrapper compute_layer_edge_grid(const Layer *layer) { for (const LayerRegion *layer_region : layer->regions()) { for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { - if (perimeter->role() == ExtrusionRole::erExternalPerimeter) { - ex_polygons.push_back(ExPolygon { perimeter->as_polyline().points }); + if (perimeter->role() == ExtrusionRole::erExternalPerimeter + || perimeter->role() == ExtrusionRole::erOverhangPerimeter) { + Points perimeter_points { }; + perimeter->collect_points(perimeter_points); + assert(perimeter->is_loop()); + perimeter_points.pop_back(); + ex_polygons.push_back(ExPolygon { perimeter_points }); } // ex_perimeter } // perimeter } // ex_entity @@ -109,7 +125,16 @@ EdgeGridWrapper compute_layer_edge_grid(const Layer *layer) { return EdgeGridWrapper(scale_(min_region_flow_width), ex_polygons); } -Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, size_t layer_idx, +coordf_t get_max_allowed_distance(ExtrusionRole role, coord_t flow_width, const Params ¶ms) { // <= distance / flow_width (can be larger for perimeter, if not external perimeter first) + if (!params.external_perimeter_first + && (role == ExtrusionRole::erExternalPerimeter || role == ExtrusionRole::erOverhangPerimeter)) { + return params.max_ex_perim_unsupported_distance_factor * flow_width; + } else { + return params.max_unsupported_distance_factor * flow_width; + } +} + +Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, float slice_z, coordf_t flow_width, const EdgeGridWrapper &supported_grid, @@ -118,74 +143,87 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, size_t la Issues issues { }; if (entity->is_collection()) { for (const auto *e : static_cast(entity)->entities) { - issues.add(check_extrusion_entity_stability(e, layer_idx, slice_z, flow_width, supported_grid, params)); + issues.add(check_extrusion_entity_stability(e, slice_z, flow_width, supported_grid, params)); } } else { //single extrusion path, with possible varying parameters - Points points = entity->as_polyline().points; + std::stack points { }; + for (const auto &p : entity->as_polyline().points) { + points.push(p); + } + float unsupported_distance = params.bridge_distance + 1.0f; float curvature = 0; float max_curvature = 0; - Vec2f tmp = unscale(points[0]).cast(); - Vec3f prev_point = Vec3f(tmp.x(), tmp.y(), slice_z); + Vec2f tmp = unscale(points.top()).cast(); + Vec3f prev_fpoint = Vec3f(tmp.x(), tmp.y(), slice_z); - for (size_t point_index = 0; point_index < points.size(); ++point_index) { - std::cout << "index: " << point_index << " dist: " << unsupported_distance << " curvature: " - << curvature << " max curvature: " << max_curvature << std::endl; + const coordf_t max_allowed_dist_from_prev_layer = get_max_allowed_distance(entity->role(), flow_width, + params); - Vec2f tmp = unscale(points[point_index]).cast(); - Vec3f u_point = Vec3f(tmp.x(), tmp.y(), slice_z); + while (!points.empty()) { + Point point = points.top(); + points.pop(); + Vec2f tmp = unscale(point).cast(); + Vec3f fpoint = Vec3f(tmp.x(), tmp.y(), slice_z); coordf_t dist_from_prev_layer { 0 }; - if (!supported_grid.grid.signed_distance(points[point_index], flow_width, dist_from_prev_layer)) { - issues.supports_nedded.push_back(u_point); + if (!supported_grid.signed_distance(point, flow_width, dist_from_prev_layer)) { + issues.supports_nedded.push_back(fpoint); continue; } - constexpr float limit_overlap_factor = 0.5; + if (dist_from_prev_layer > max_allowed_dist_from_prev_layer) { //unsupported + unsupported_distance += (fpoint - prev_fpoint).norm(); - if (dist_from_prev_layer > flow_width) { //unsupported - std::cout << "index: " << point_index << " unsupported " << std::endl; - unsupported_distance += (u_point - prev_point).norm(); + if (!points.empty()) { + const Vec2f v1 = (fpoint - prev_fpoint).head<2>(); + const Vec2f v2 = unscale(points.top()).cast() - fpoint.head<2>(); + float dot = v1(0) * v2(0) + v1(1) * v2(1); + float cross = v1(0) * v2(1) - v1(1) * v2(0); + float angle = float(atan2(float(cross), float(dot))); + + curvature += angle; + max_curvature = std::max(abs(curvature), max_curvature); + } + + if (unsupported_distance + > params.bridge_distance + / (1.0f + (max_curvature * params.bridge_distance_decrease_by_curvature_factor / PI))) { + issues.supports_nedded.push_back(fpoint); + + std::cout << "SUPP: " << "udis: " << unsupported_distance << " curv: " << curvature << " max curv: " + << max_curvature << std::endl; + std::cout << "max dist from layer: " << max_allowed_dist_from_prev_layer << " measured dist: " + << dist_from_prev_layer << " FW: " << flow_width << std::endl; + + unsupported_distance = 0; + curvature = 0; + max_curvature = 0; + } } else { - std::cout << "index: " << point_index << " grounded " << std::endl; - unsupported_distance = 0; - } - - std::cout << "index: " << point_index << " dfromprev: " << dist_from_prev_layer << std::endl; - - if (dist_from_prev_layer > flow_width * limit_overlap_factor && point_index < points.size() - 1) { - const Vec2f v1 = (u_point - prev_point).head<2>(); - const Vec2f v2 = unscale(points[point_index + 1]).cast() - u_point.head<2>(); - float dot = v1(0) * v2(0) + v1(1) * v2(1); - float cross = v1(0) * v2(1) - v1(1) * v2(0); - float angle = float(atan2(float(cross), float(dot))); - - std::cout << "index: " << point_index << " angle: " << angle << std::endl; - - curvature += angle; - max_curvature = std::max(abs(curvature), max_curvature); - } - - if (!(dist_from_prev_layer > flow_width * limit_overlap_factor)) { - std::cout << "index: " << point_index << " reset curvature" << std::endl; - max_curvature = 0; - curvature = 0; - } - - if (unsupported_distance > params.bridge_distance / (1 + int(max_curvature * 7 / PI))) { - issues.supports_nedded.push_back(u_point); unsupported_distance = 0; curvature = 0; max_curvature = 0; } if (max_curvature / (PI * unsupported_distance) > params.limit_curvature) { - issues.curling_up.push_back(u_point); - curvature = 0; - max_curvature = 0; + issues.curling_up.push_back(fpoint); + } + + prev_fpoint = fpoint; + + if (!points.empty()) { + Vec2f next = unscale(points.top()).cast(); + Vec2f reverse_v = fpoint.head<2>() - next; + float dist_to_next = reverse_v.norm(); + reverse_v.normalize(); + int new_points_count = dist_to_next / params.bridge_distance; + float step_size = dist_to_next / (new_points_count + 1); + for (int i = 1; i <= new_points_count; ++i) { + points.push(Point::new_scale(Vec2f(next + reverse_v * (i * step_size)))); + } } - prev_point = u_point; } } return issues; @@ -205,7 +243,7 @@ coordf_t get_flow_width(const LayerRegion *region, ExtrusionRole role) { case ExtrusionRole::erSolidInfill: return region->flow(FlowRole::frSolidInfill).scaled_width(); default: - return region->flow(FlowRole::frExternalPerimeter).scaled_width(); + return region->flow(FlowRole::frPerimeter).scaled_width(); } } @@ -223,7 +261,7 @@ Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_ for (const LayerRegion *layer_region : layer->regions()) { for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { - issues.add(check_extrusion_entity_stability(perimeter, layer_idx, + issues.add(check_extrusion_entity_stability(perimeter, layer->slice_z, get_flow_width(layer_region, perimeter->role()), supported_grid, params)); } // perimeter @@ -231,7 +269,7 @@ Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_ for (const ExtrusionEntity *ex_entity : layer_region->fills.entities) { for (const ExtrusionEntity *fill : static_cast(ex_entity)->entities) { if (fill->role() == ExtrusionRole::erGapFill || fill->role() == ExtrusionRole::erBridgeInfill) { - issues.add(check_extrusion_entity_stability(fill, layer_idx, + issues.add(check_extrusion_entity_stability(fill, layer->slice_z, get_flow_width(layer_region, fill->role()), supported_grid, params)); } @@ -242,9 +280,9 @@ Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_ for (const LayerRegion *layer_region : layer->regions()) { for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { - if (perimeter->role() == ExtrusionRole::erExternalPerimeter) { - std::cout << "checking ex perimeter " << std::endl; - issues.add(check_extrusion_entity_stability(perimeter, layer_idx, + if (perimeter->role() == ExtrusionRole::erExternalPerimeter + || perimeter->role() == ExtrusionRole::erOverhangPerimeter) { + issues.add(check_extrusion_entity_stability(perimeter, layer->slice_z, get_flow_width(layer_region, perimeter->role()), supported_grid, params)); }; // ex_perimeter @@ -273,7 +311,7 @@ std::vector quick_search(const PrintObject *po, const Params ¶ms) { for (const LayerRegion *layer_region : layer->regions()) { for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { - if (perimeter->role() == ExtrusionRole::erExternalPerimeter) { + if (perimeter->role() == ExtrusionRole::erExternalPerimeter || perimeter->role() == ExtrusionRole::erOverhangPerimeter) { assert(perimeter->is_loop()); descriptor.segments_count++; const ExtrusionLoop *loop = static_cast(perimeter); @@ -327,8 +365,7 @@ std::vector quick_search(const PrintObject *po, const Params ¶ms) { [&](tbb::blocked_range r) { for (size_t suspicious_index = r.begin(); suspicious_index < r.end(); ++suspicious_index) { auto layer_issues = check_layer_stability(po, suspicious_layers_indices[suspicious_index], - false, - params); + false, params); if (!layer_issues.supports_nedded.empty()) { layer_needs_supports[suspicious_index] = true; } @@ -337,8 +374,8 @@ std::vector quick_search(const PrintObject *po, const Params ¶ms) { std::vector problematic_layers; - for (size_t index = suspicious_layers_indices.size() - 1; index <= 0; ++index) { - if (!layer_needs_supports[index]) { + for (size_t index = 0; index < suspicious_layers_indices.size(); ++index) { + if (layer_needs_supports[index]) { problematic_layers.push_back(suspicious_layers_indices[index]); } } @@ -347,26 +384,29 @@ std::vector quick_search(const PrintObject *po, const Params ¶ms) { Issues full_search(const PrintObject *po, const Params ¶ms) { using namespace Impl; - Issues issues { }; - for (size_t layer_idx = 1; layer_idx < po->layer_count(); ++layer_idx) { - auto layer_issues = check_layer_stability(po, layer_idx, true, params); - if (!layer_issues.empty()) { - issues.add(layer_issues); - } - } + size_t layer_count = po->layer_count(); + Issues found_issues = tbb::parallel_reduce(tbb::blocked_range(1, layer_count), Issues { }, + [&](tbb::blocked_range r, const Issues &init) { + Issues issues = init; + for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) { + auto layer_issues = check_layer_stability(po, layer_idx, true, params); + if (!layer_issues.empty()) { + issues.add(layer_issues); + } + } + return issues; + }, + [](Issues left, const Issues &right) { + left.add(right); + return left; + } + ); #ifdef DEBUG_FILES - Impl::debug_export(issues, "issues"); + Impl::debug_export(found_issues, "issues"); #endif -// tbb::parallel_for(tbb::blocked_range(0, suspicious_layers_indices.size()), -// [&](tbb::blocked_range r) { -// for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) { -// check_layer_stability(po, suspicious_layers_indices[layer_idx], params); -// } -// }); - - return issues; + return found_issues; } } diff --git a/src/libslic3r/SupportableIssuesSearch.hpp b/src/libslic3r/SupportableIssuesSearch.hpp index 22289c923c..4ff0e32faa 100644 --- a/src/libslic3r/SupportableIssuesSearch.hpp +++ b/src/libslic3r/SupportableIssuesSearch.hpp @@ -8,8 +8,13 @@ namespace Slic3r { namespace SupportableIssues { struct Params { - float bridge_distance = 5.0f; - float limit_curvature = 0.25f; + float bridge_distance = 10.0f; + float limit_curvature = 0.3f; + + bool external_perimeter_first = false; + float max_unsupported_distance_factor = 0.0f; + float max_ex_perim_unsupported_distance_factor = 1.0f; + float bridge_distance_decrease_by_curvature_factor = 5.0f; float perimeter_length_diff_tolerance = 8.0f; float centroid_offset_tolerance = 1.0f; diff --git a/src/libslic3r/TriangleSelectorWrapper.cpp b/src/libslic3r/TriangleSelectorWrapper.cpp index 98d00bc985..02a3b8a1ab 100644 --- a/src/libslic3r/TriangleSelectorWrapper.cpp +++ b/src/libslic3r/TriangleSelectorWrapper.cpp @@ -16,10 +16,11 @@ void TriangleSelectorWrapper::enforce_spot(const Vec3f &point, float radius) { auto dist = AABBTreeIndirect::squared_distance_to_indexed_triangle_set(mesh.its.vertices, mesh.its.indices, triangles_tree, point, hit_face_index, hit_point); - if (dist < 0 || dist > radius) + if (dist < 0 || dist > radius * radius) { return; + } - std::unique_ptr cursor = std::make_unique(point, point, + std::unique_ptr cursor = std::make_unique(hit_point, point, radius, Transform3d::Identity(), TriangleSelector::ClippingPlane { }); selector.select_patch(hit_face_index, std::move(cursor), EnforcerBlockerType::ENFORCER, Transform3d::Identity(), diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp index df9cdce56f..f4c21a174b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp @@ -39,6 +39,10 @@ private: // This map holds all translated description texts, so they can be easily referenced during layout calculations // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect. std::map m_desc; + + + bool has_backend_supports() const; + void reslice_FDM_supports(bool postpone_error_messages = false) const; }; From 1955cd066e14865755e4273064701158e0904d12 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Tue, 12 Apr 2022 09:54:44 +0200 Subject: [PATCH 05/78] include external_perimeters_first option, change paramters accordingly --- src/libslic3r/SupportableIssuesSearch.cpp | 60 +++++++++++++---------- src/libslic3r/SupportableIssuesSearch.hpp | 1 - 2 files changed, 33 insertions(+), 28 deletions(-) diff --git a/src/libslic3r/SupportableIssuesSearch.cpp b/src/libslic3r/SupportableIssuesSearch.cpp index ea2ba41063..e60f859302 100644 --- a/src/libslic3r/SupportableIssuesSearch.cpp +++ b/src/libslic3r/SupportableIssuesSearch.cpp @@ -125,9 +125,29 @@ EdgeGridWrapper compute_layer_edge_grid(const Layer *layer) { return EdgeGridWrapper(scale_(min_region_flow_width), ex_polygons); } -coordf_t get_max_allowed_distance(ExtrusionRole role, coord_t flow_width, const Params ¶ms) { // <= distance / flow_width (can be larger for perimeter, if not external perimeter first) - if (!params.external_perimeter_first - && (role == ExtrusionRole::erExternalPerimeter || role == ExtrusionRole::erOverhangPerimeter)) { +//TODO needs revision +coordf_t get_flow_width(const LayerRegion *region, ExtrusionRole role) { + switch (role) { + case ExtrusionRole::erBridgeInfill: + return region->flow(FlowRole::frExternalPerimeter).scaled_width(); + case ExtrusionRole::erExternalPerimeter: + return region->flow(FlowRole::frExternalPerimeter).scaled_width(); + case ExtrusionRole::erGapFill: + return region->flow(FlowRole::frInfill).scaled_width(); + case ExtrusionRole::erPerimeter: + return region->flow(FlowRole::frPerimeter).scaled_width(); + case ExtrusionRole::erSolidInfill: + return region->flow(FlowRole::frSolidInfill).scaled_width(); + default: + return region->flow(FlowRole::frPerimeter).scaled_width(); + } +} + +coordf_t get_max_allowed_distance(ExtrusionRole role, coord_t flow_width, bool external_perimeters_first, + const Params ¶ms) { // <= distance / flow_width (can be larger for perimeter, if not external perimeter first) + if ((role == ExtrusionRole::erExternalPerimeter || role == ExtrusionRole::erOverhangPerimeter) + && !(external_perimeters_first) + ) { return params.max_ex_perim_unsupported_distance_factor * flow_width; } else { return params.max_unsupported_distance_factor * flow_width; @@ -136,16 +156,17 @@ coordf_t get_max_allowed_distance(ExtrusionRole role, coord_t flow_width, const Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, float slice_z, - coordf_t flow_width, + const LayerRegion *layer_region, const EdgeGridWrapper &supported_grid, const Params ¶ms) { Issues issues { }; if (entity->is_collection()) { for (const auto *e : static_cast(entity)->entities) { - issues.add(check_extrusion_entity_stability(e, slice_z, flow_width, supported_grid, params)); + issues.add(check_extrusion_entity_stability(e, slice_z, layer_region, supported_grid, params)); } } else { //single extrusion path, with possible varying parameters + std::stack points { }; for (const auto &p : entity->as_polyline().points) { points.push(p); @@ -157,8 +178,10 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, Vec2f tmp = unscale(points.top()).cast(); Vec3f prev_fpoint = Vec3f(tmp.x(), tmp.y(), slice_z); + coordf_t flow_width = get_flow_width(layer_region, entity->role()); + bool external_perimters_first = layer_region->region().config().external_perimeters_first; const coordf_t max_allowed_dist_from_prev_layer = get_max_allowed_distance(entity->role(), flow_width, - params); + external_perimters_first, params); while (!points.empty()) { Point point = points.top(); @@ -229,24 +252,6 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, return issues; } -//TODO needs revision -coordf_t get_flow_width(const LayerRegion *region, ExtrusionRole role) { - switch (role) { - case ExtrusionRole::erBridgeInfill: - return region->flow(FlowRole::frExternalPerimeter).scaled_width(); - case ExtrusionRole::erExternalPerimeter: - return region->flow(FlowRole::frExternalPerimeter).scaled_width(); - case ExtrusionRole::erGapFill: - return region->flow(FlowRole::frInfill).scaled_width(); - case ExtrusionRole::erPerimeter: - return region->flow(FlowRole::frPerimeter).scaled_width(); - case ExtrusionRole::erSolidInfill: - return region->flow(FlowRole::frSolidInfill).scaled_width(); - default: - return region->flow(FlowRole::frPerimeter).scaled_width(); - } -} - Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_check, const Params ¶ms) { std::cout << "Checking: " << layer_idx << std::endl; if (layer_idx == 0) { @@ -262,7 +267,7 @@ Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_ for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { issues.add(check_extrusion_entity_stability(perimeter, - layer->slice_z, get_flow_width(layer_region, perimeter->role()), + layer->slice_z, layer_region, supported_grid, params)); } // perimeter } // ex_entity @@ -270,12 +275,13 @@ Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_ for (const ExtrusionEntity *fill : static_cast(ex_entity)->entities) { if (fill->role() == ExtrusionRole::erGapFill || fill->role() == ExtrusionRole::erBridgeInfill) { issues.add(check_extrusion_entity_stability(fill, - layer->slice_z, get_flow_width(layer_region, fill->role()), + layer->slice_z, layer_region, supported_grid, params)); } } // fill } // ex_entity } // region + } else { //check only external perimeters for (const LayerRegion *layer_region : layer->regions()) { for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { @@ -283,7 +289,7 @@ Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_ if (perimeter->role() == ExtrusionRole::erExternalPerimeter || perimeter->role() == ExtrusionRole::erOverhangPerimeter) { issues.add(check_extrusion_entity_stability(perimeter, - layer->slice_z, get_flow_width(layer_region, perimeter->role()), + layer->slice_z, layer_region, supported_grid, params)); }; // ex_perimeter } // perimeter diff --git a/src/libslic3r/SupportableIssuesSearch.hpp b/src/libslic3r/SupportableIssuesSearch.hpp index 4ff0e32faa..80127520af 100644 --- a/src/libslic3r/SupportableIssuesSearch.hpp +++ b/src/libslic3r/SupportableIssuesSearch.hpp @@ -11,7 +11,6 @@ struct Params { float bridge_distance = 10.0f; float limit_curvature = 0.3f; - bool external_perimeter_first = false; float max_unsupported_distance_factor = 0.0f; float max_ex_perim_unsupported_distance_factor = 1.0f; float bridge_distance_decrease_by_curvature_factor = 5.0f; From c14b4a5d2e44e53c4c5e4e797b9eafa2f7337318 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Tue, 12 Apr 2022 13:58:01 +0200 Subject: [PATCH 06/78] quick search simplified, removed expensive layer estimators added explanations and comments --- src/libslic3r/SupportableIssuesSearch.cpp | 125 ++++++---------------- src/libslic3r/SupportableIssuesSearch.hpp | 8 +- 2 files changed, 36 insertions(+), 97 deletions(-) diff --git a/src/libslic3r/SupportableIssuesSearch.cpp b/src/libslic3r/SupportableIssuesSearch.cpp index e60f859302..6510c35468 100644 --- a/src/libslic3r/SupportableIssuesSearch.cpp +++ b/src/libslic3r/SupportableIssuesSearch.cpp @@ -33,12 +33,6 @@ bool Issues::empty() const { namespace Impl { -struct LayerDescriptor { - Vec2f centroid { 0.0f, 0.0f }; - size_t segments_count { 0 }; - float perimeter_length { 0.0f }; -}; - struct EdgeGridWrapper { EdgeGridWrapper(coord_t edge_width, ExPolygons ex_polys) : ex_polys(ex_polys), edge_width(edge_width) { @@ -50,6 +44,7 @@ struct EdgeGridWrapper { bool signed_distance(const Point &point, coordf_t point_width, coordf_t &dist_out) const { coordf_t tmp_dist_out; bool found = grid.signed_distance(point, edge_width, tmp_dist_out); + // decrease the distance by half of edge width of previous layer and half of flow width of current layer dist_out = tmp_dist_out - edge_width / 2 - point_width / 2; return found; @@ -115,7 +110,7 @@ EdgeGridWrapper compute_layer_edge_grid(const Layer *layer) { Points perimeter_points { }; perimeter->collect_points(perimeter_points); assert(perimeter->is_loop()); - perimeter_points.pop_back(); + perimeter_points.pop_back(); // EdgeGrid structure does not like repetition of the first/last point ex_polygons.push_back(ExPolygon { perimeter_points }); } // ex_perimeter } // perimeter @@ -166,17 +161,18 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, issues.add(check_extrusion_entity_stability(e, slice_z, layer_region, supported_grid, params)); } } else { //single extrusion path, with possible varying parameters - + //prepare stack of points on the extrusion path. If there are long segments, additional points might be pushed onto the stack during the algorithm. std::stack points { }; for (const auto &p : entity->as_polyline().points) { points.push(p); } - float unsupported_distance = params.bridge_distance + 1.0f; - float curvature = 0; - float max_curvature = 0; + float unsupported_distance = params.bridge_distance + 1.0f; // initialize unsupported distance with larger than tolerable distance -> + // -> it prevents extruding perimeter start and short loops into air. + float curvature = 0; // current curvature of the unsupported part of the extrusion - it is accumulated value of signed ccw angles of continuously unsupported points. + float max_curvature = 0; // max curvature (in abs value) for the current unsupported segment. Vec2f tmp = unscale(points.top()).cast(); - Vec3f prev_fpoint = Vec3f(tmp.x(), tmp.y(), slice_z); + Vec3f prev_fpoint = Vec3f(tmp.x(), tmp.y(), slice_z); // prev point of the path. Initialize with first point. coordf_t flow_width = get_flow_width(layer_region, entity->role()); bool external_perimters_first = layer_region->region().config().external_perimeters_first; @@ -190,30 +186,33 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, Vec3f fpoint = Vec3f(tmp.x(), tmp.y(), slice_z); coordf_t dist_from_prev_layer { 0 }; - if (!supported_grid.signed_distance(point, flow_width, dist_from_prev_layer)) { + if (!supported_grid.signed_distance(point, flow_width, dist_from_prev_layer)) { // dist from prev layer not found, assume empty layer issues.supports_nedded.push_back(fpoint); - continue; + unsupported_distance = 0; + curvature = 0; + max_curvature = 0; } - if (dist_from_prev_layer > max_allowed_dist_from_prev_layer) { //unsupported - unsupported_distance += (fpoint - prev_fpoint).norm(); + if (dist_from_prev_layer > max_allowed_dist_from_prev_layer) { //extrusion point is unsupported + unsupported_distance += (fpoint - prev_fpoint).norm(); // for algortihm simplicity, expect that the whole line between prev and current point is unsupported if (!points.empty()) { const Vec2f v1 = (fpoint - prev_fpoint).head<2>(); const Vec2f v2 = unscale(points.top()).cast() - fpoint.head<2>(); float dot = v1(0) * v2(0) + v1(1) * v2(1); float cross = v1(0) * v2(1) - v1(1) * v2(0); - float angle = float(atan2(float(cross), float(dot))); + float angle = float(atan2(float(cross), float(dot))); // ccw angle, TODO replace with angle func, once it gets into master curvature += angle; max_curvature = std::max(abs(curvature), max_curvature); } - if (unsupported_distance - > params.bridge_distance - / (1.0f + (max_curvature * params.bridge_distance_decrease_by_curvature_factor / PI))) { + if (unsupported_distance // if unsupported distance is larger than bridge distance linearly decreased by curvature, enforce supports. + > params.bridge_distance + / (1.0f + (max_curvature * params.bridge_distance_decrease_by_curvature_factor / PI))) { issues.supports_nedded.push_back(fpoint); + //DEBUG stuff TODO remove std::cout << "SUPP: " << "udis: " << unsupported_distance << " curv: " << curvature << " max curv: " << max_curvature << std::endl; std::cout << "max dist from layer: " << max_allowed_dist_from_prev_layer << " measured dist: " @@ -229,15 +228,17 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, max_curvature = 0; } + // Estimation of short curvy segments which are not supported -> problems with curling + // Currently the curling issues are ignored if (max_curvature / (PI * unsupported_distance) > params.limit_curvature) { issues.curling_up.push_back(fpoint); } prev_fpoint = fpoint; - if (!points.empty()) { + if (!points.empty()) { //oversampling if necessary Vec2f next = unscale(points.top()).cast(); - Vec2f reverse_v = fpoint.head<2>() - next; + Vec2f reverse_v = fpoint.head<2>() - next; // vector from next to current float dist_to_next = reverse_v.norm(); reverse_v.normalize(); int new_points_count = dist_to_next / params.bridge_distance; @@ -259,10 +260,11 @@ Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_ return {}; } const Layer *layer = po->get_layer(layer_idx); + //Prepare edge grid of previous layer, will be used to check if the extrusion path is supported EdgeGridWrapper supported_grid = compute_layer_edge_grid(layer->lower_layer); Issues issues { }; - if (full_check) { + if (full_check) { // If full checkm check stability of perimeters, gap fills, and bridges. for (const LayerRegion *layer_region : layer->regions()) { for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { @@ -282,7 +284,7 @@ Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_ } // ex_entity } // region - } else { //check only external perimeters + } else { // If NOT full check, check only external perimeters for (const LayerRegion *layer_region : layer->regions()) { for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { @@ -304,85 +306,24 @@ Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_ std::vector quick_search(const PrintObject *po, const Params ¶ms) { using namespace Impl; - std::vector descriptors(po->layer_count()); - tbb::parallel_for(tbb::blocked_range(0, po->layer_count()), + size_t layer_count = po->layer_count(); + std::vector layer_needs_supports(layer_count, false); + tbb::parallel_for(tbb::blocked_range(1, layer_count), [&](tbb::blocked_range r) { for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) { - const Layer *layer = po->get_layer(layer_idx); - - LayerDescriptor &descriptor = descriptors[layer_idx]; - size_t point_count { 0 }; - - for (const LayerRegion *layer_region : layer->regions()) { - for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { - for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { - if (perimeter->role() == ExtrusionRole::erExternalPerimeter || perimeter->role() == ExtrusionRole::erOverhangPerimeter) { - assert(perimeter->is_loop()); - descriptor.segments_count++; - const ExtrusionLoop *loop = static_cast(perimeter); - for (const ExtrusionPath& path : loop->paths) { - Vec2f prev_pos = unscale(path.polyline.last_point()).cast(); - for (size_t p_idx = 0; p_idx < path.polyline.points.size(); ++p_idx) { - point_count++; - Vec2f point_pos = unscale(path.polyline.points[p_idx]).cast(); - descriptor.centroid += point_pos; - descriptor.perimeter_length += (point_pos - prev_pos).norm(); - prev_pos = point_pos; - } //point - } //path - } // ex_perimeter - } // perimeter -} // ex_entity -} // region - - descriptor.centroid /= float(point_count); - - } // layer - } // thread - ); - - std::vector suspicious_layers_indices { }; - - for (size_t desc_idx = 1; desc_idx < descriptors.size(); ++desc_idx) { - const LayerDescriptor &prev = descriptors[desc_idx - 1]; - const LayerDescriptor &descriptor = descriptors[desc_idx]; - if (descriptor.segments_count - prev.segments_count != 0 - || - std::abs(descriptor.perimeter_length - prev.perimeter_length) - > params.perimeter_length_diff_tolerance || - (descriptor.centroid - prev.centroid).norm() > params.centroid_offset_tolerance - ) { - suspicious_layers_indices.push_back(desc_idx); - } -#ifdef DEBUG_FILES - std::cout << "SIS layer idx: " << desc_idx << " reg count: " << descriptor.segments_count << " len: " - << descriptor.perimeter_length << - " centroid: " << descriptor.centroid.x() << " | " << descriptor.centroid.y() << std::endl; - std::cout << "SIS diff: " << desc_idx << " reg count: " - << (int(descriptor.segments_count) - int(prev.segments_count)) << - " len: " << (descriptor.perimeter_length - prev.perimeter_length) << - " centroid: " << (descriptor.centroid - prev.centroid).norm() << std::endl; -#endif - } - - std::vector layer_needs_supports(suspicious_layers_indices.size(), false); - tbb::parallel_for(tbb::blocked_range(0, suspicious_layers_indices.size()), - [&](tbb::blocked_range r) { - for (size_t suspicious_index = r.begin(); suspicious_index < r.end(); ++suspicious_index) { - auto layer_issues = check_layer_stability(po, suspicious_layers_indices[suspicious_index], + auto layer_issues = check_layer_stability(po, layer_idx, false, params); if (!layer_issues.supports_nedded.empty()) { - layer_needs_supports[suspicious_index] = true; + layer_needs_supports[layer_idx] = true; } } }); std::vector problematic_layers; - - for (size_t index = 0; index < suspicious_layers_indices.size(); ++index) { + for (size_t index = 0; index < layer_needs_supports.size(); ++index) { if (layer_needs_supports[index]) { - problematic_layers.push_back(suspicious_layers_indices[index]); + problematic_layers.push_back(index); } } return problematic_layers; diff --git a/src/libslic3r/SupportableIssuesSearch.hpp b/src/libslic3r/SupportableIssuesSearch.hpp index 80127520af..4e8a541f94 100644 --- a/src/libslic3r/SupportableIssuesSearch.hpp +++ b/src/libslic3r/SupportableIssuesSearch.hpp @@ -9,14 +9,12 @@ namespace SupportableIssues { struct Params { float bridge_distance = 10.0f; - float limit_curvature = 0.3f; + float limit_curvature = 0.3f; // used to detect curling issues, but they are currently not considered anyway float max_unsupported_distance_factor = 0.0f; + // allow printing external perimeter in the air to some extent. it hopefully attaches to the internal perimeter. float max_ex_perim_unsupported_distance_factor = 1.0f; - float bridge_distance_decrease_by_curvature_factor = 5.0f; - - float perimeter_length_diff_tolerance = 8.0f; - float centroid_offset_tolerance = 1.0f; + float bridge_distance_decrease_by_curvature_factor = 5.0f; // allowed bridge distance = bridge_distance / ( 1 + this factor * (curvature / PI) ) }; From a46e1dc79c023897e781c3158e6754ea4bd37aaf Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Thu, 14 Apr 2022 17:21:14 +0200 Subject: [PATCH 07/78] initial works on EdgeGrid alternative --- src/libslic3r/CMakeLists.txt | 2 + src/libslic3r/PolygonPointTest.cpp | 1 + src/libslic3r/PolygonPointTest.hpp | 73 +++++++++++++++++++++++ src/libslic3r/SupportableIssuesSearch.cpp | 26 +------- 4 files changed, 78 insertions(+), 24 deletions(-) create mode 100644 src/libslic3r/PolygonPointTest.cpp create mode 100644 src/libslic3r/PolygonPointTest.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 0a0062a754..443c81b486 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -201,6 +201,8 @@ set(SLIC3R_SOURCES Point.hpp Polygon.cpp Polygon.hpp + PolygonPointTest.cpp + PolygonPointTest.hpp MutablePolygon.cpp MutablePolygon.hpp PolygonTrimmer.cpp diff --git a/src/libslic3r/PolygonPointTest.cpp b/src/libslic3r/PolygonPointTest.cpp new file mode 100644 index 0000000000..dcab9e87eb --- /dev/null +++ b/src/libslic3r/PolygonPointTest.cpp @@ -0,0 +1 @@ +#include "PolygonPointTest.hpp" diff --git a/src/libslic3r/PolygonPointTest.hpp b/src/libslic3r/PolygonPointTest.hpp new file mode 100644 index 0000000000..79e6b4ab5c --- /dev/null +++ b/src/libslic3r/PolygonPointTest.hpp @@ -0,0 +1,73 @@ +#ifndef SRC_LIBSLIC3R_POLYGONPOINTTEST_HPP_ +#define SRC_LIBSLIC3R_POLYGONPOINTTEST_HPP_ + +#include "libslic3r/Point.hpp" +#include "libslic3r/EdgeGrid.hpp" + +namespace Slic3r { + +struct EdgeGridWrapper { + EdgeGridWrapper(coord_t resolution, ExPolygons ex_polys) : + ex_polys(ex_polys) { + + grid.create(this->ex_polys, resolution); + grid.calculate_sdf(); + } + + bool signed_distance(const Point &point, coordf_t point_width, coordf_t &dist_out) const { + coordf_t tmp_dist_out; + bool found = grid.signed_distance(point, point_width, tmp_dist_out); + // decrease the distance by half of edge width of previous layer and half of flow width of current layer + dist_out = tmp_dist_out - point_width / 2; + return found; + + } + + EdgeGrid::Grid grid; + ExPolygons ex_polys; +}; + +class PolygonPointTest { +public: + PolygonPointTest(const ExPolygons &ex_polygons) { + std::vector lines; + for (const auto &exp : ex_polygons) { + Lines contour = exp.contour.lines(); + lines.insert(lines.end(), contour.begin(), contour.end()); + for (const auto &hole : exp.holes) { + Lines hole_lines = hole.lines(); + for (Line &line : hole_lines) { + line.reverse(); // reverse hole lines, so that we can use normal to deduce where the object is + } + lines.insert(lines.end(), hole_lines.begin(), hole_lines.end()); + } + } + + std::vector> sweeping_data(lines.size()); + sweeping_data.reserve(lines.size() * 2); + for (int line_index = 0; line_index < lines.size(); ++line_index) { + sweeping_data[line_index].first = line_index; + sweeping_data[line_index].second = true; + } + + const auto data_comparator = [&lines](const std::pair &left, + const std::pair &right) { + return std::min(lines[left.first].a.x(), lines[left.first].b.x()) + < std::min(lines[right.first].a.x(), lines[right.first].b.x()); + }; + + std::make_heap(sweeping_data.begin(), sweeping_data.end(), data_comparator); + std::set active_lines; + + //TODO continue + + + + + } + +}; + +} + +#endif /* SRC_LIBSLIC3R_POLYGONPOINTTEST_HPP_ */ diff --git a/src/libslic3r/SupportableIssuesSearch.cpp b/src/libslic3r/SupportableIssuesSearch.cpp index 6510c35468..39ded7648b 100644 --- a/src/libslic3r/SupportableIssuesSearch.cpp +++ b/src/libslic3r/SupportableIssuesSearch.cpp @@ -8,8 +8,8 @@ #include #include "libslic3r/Layer.hpp" -#include "libslic3r/EdgeGrid.hpp" #include "libslic3r/ClipperUtils.hpp" +#include "PolygonPointTest.hpp" #define DEBUG_FILES @@ -33,28 +33,6 @@ bool Issues::empty() const { namespace Impl { -struct EdgeGridWrapper { - EdgeGridWrapper(coord_t edge_width, ExPolygons ex_polys) : - ex_polys(ex_polys), edge_width(edge_width) { - - grid.create(this->ex_polys, edge_width); - grid.calculate_sdf(); - } - - bool signed_distance(const Point &point, coordf_t point_width, coordf_t &dist_out) const { - coordf_t tmp_dist_out; - bool found = grid.signed_distance(point, edge_width, tmp_dist_out); - // decrease the distance by half of edge width of previous layer and half of flow width of current layer - dist_out = tmp_dist_out - edge_width / 2 - point_width / 2; - return found; - - } - - EdgeGrid::Grid grid; - ExPolygons ex_polys; - coord_t edge_width; -}; - #ifdef DEBUG_FILES void debug_export(Issues issues, std::string file_name) { Slic3r::CNumericLocalesSetter locales_setter; @@ -194,7 +172,7 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, } if (dist_from_prev_layer > max_allowed_dist_from_prev_layer) { //extrusion point is unsupported - unsupported_distance += (fpoint - prev_fpoint).norm(); // for algortihm simplicity, expect that the whole line between prev and current point is unsupported + unsupported_distance += (fpoint - prev_fpoint).norm(); // for algorithm simplicity, expect that the whole line between prev and current point is unsupported if (!points.empty()) { const Vec2f v1 = (fpoint - prev_fpoint).head<2>(); From cfe9b27a6d1f9889196ae901d7751c20548b5c17 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Fri, 22 Apr 2022 09:47:30 +0200 Subject: [PATCH 08/78] refactoring, initial work on weight distribution matrix --- src/libslic3r/PolygonPointTest.hpp | 49 +++-- src/libslic3r/SupportableIssuesSearch.cpp | 240 +++++++++++++++++----- src/libslic3r/SupportableIssuesSearch.hpp | 8 +- 3 files changed, 224 insertions(+), 73 deletions(-) diff --git a/src/libslic3r/PolygonPointTest.hpp b/src/libslic3r/PolygonPointTest.hpp index 79e6b4ab5c..d95b191bf6 100644 --- a/src/libslic3r/PolygonPointTest.hpp +++ b/src/libslic3r/PolygonPointTest.hpp @@ -7,27 +7,37 @@ namespace Slic3r { struct EdgeGridWrapper { - EdgeGridWrapper(coord_t resolution, ExPolygons ex_polys) : - ex_polys(ex_polys) { + EdgeGridWrapper(coord_t edge_width, std::vector lines) : + lines(lines), edge_width(edge_width) { - grid.create(this->ex_polys, resolution); + grid.create(this->lines, edge_width, true); grid.calculate_sdf(); } bool signed_distance(const Point &point, coordf_t point_width, coordf_t &dist_out) const { coordf_t tmp_dist_out; - bool found = grid.signed_distance(point, point_width, tmp_dist_out); - // decrease the distance by half of edge width of previous layer and half of flow width of current layer - dist_out = tmp_dist_out - point_width / 2; + bool found = grid.signed_distance(point, edge_width, tmp_dist_out); + dist_out = tmp_dist_out - edge_width / 2 - point_width / 2; return found; } EdgeGrid::Grid grid; - ExPolygons ex_polys; + std::vector lines; + coord_t edge_width; }; +namespace TODO { + class PolygonPointTest { + + struct Segment { + coord_t start; + std::vector lines; + }; + + std::vector x_coord_segments; + public: PolygonPointTest(const ExPolygons &ex_polygons) { std::vector lines; @@ -43,30 +53,37 @@ public: } } - std::vector> sweeping_data(lines.size()); - sweeping_data.reserve(lines.size() * 2); - for (int line_index = 0; line_index < lines.size(); ++line_index) { + std::vector> sweeping_data(lines.size() * 2); + for (size_t line_index = 0; line_index < lines.size(); ++line_index) { sweeping_data[line_index].first = line_index; sweeping_data[line_index].second = true; + sweeping_data[line_index * 2 + 1].first = line_index; + sweeping_data[line_index * 2 + 1].second = false; } const auto data_comparator = [&lines](const std::pair &left, const std::pair &right) { - return std::min(lines[left.first].a.x(), lines[left.first].b.x()) - < std::min(lines[right.first].a.x(), lines[right.first].b.x()); + const auto left_x = + left.second ? + std::min(lines[left.first].a.x(), lines[left.first].b.x()) : + std::max(lines[left.first].a.x(), lines[left.first].b.x()); + const auto right_x = + right.second ? + std::min(lines[right.first].a.x(), lines[right.first].b.x()) : + std::max(lines[right.first].a.x(), lines[right.first].b.x()); + + return left_x < right_x; }; - std::make_heap(sweeping_data.begin(), sweeping_data.end(), data_comparator); + std::sort(sweeping_data.begin(), sweeping_data.end(), data_comparator); std::set active_lines; //TODO continue - - - } }; +} } diff --git a/src/libslic3r/SupportableIssuesSearch.cpp b/src/libslic3r/SupportableIssuesSearch.cpp index 39ded7648b..ee1f440608 100644 --- a/src/libslic3r/SupportableIssuesSearch.cpp +++ b/src/libslic3r/SupportableIssuesSearch.cpp @@ -31,6 +31,119 @@ bool Issues::empty() const { return supports_nedded.empty() && curling_up.empty(); } +struct Cell { + float weight; + char last_extrusion_id; +}; + +struct WeightDistributionMatrix { + // Lets make Z coord half the size of X (and Y). + // This corresponds to angle of ~26 degrees between center of one cell and other one up and sideways + // which is approximately a limiting printable angle. + + WeightDistributionMatrix(const PrintObject *po, size_t layer_idx_begin, size_t layer_idx_end) { + Vec3crd object_origin = scaled(po->trafo_centered() * Vec3d::Zero()); + Vec3crd min = object_origin - po->size() / 2 - Vec3crd::Ones(); + Vec3crd max = object_origin + po->size() / 2 + Vec3crd::Ones(); + + cell_size = Vec3crd { int(cell_height * 2), int(cell_height * 2), int(cell_height) }; + + global_origin = min; + global_size = max - min; + global_cell_count = global_size.cwiseQuotient(cell_size); + + coord_t local_min_z = scale_(po->layers()[layer_idx_begin]->slice_z); + coord_t local_max_z = scale_(po->layers()[layer_idx_end]->slice_z); + coord_t local_min_z_index = local_min_z / cell_size.z(); + coord_t local_max_z_index = local_max_z / cell_size.z(); + + local_z_index_offset = local_min_z_index; + local_z_cell_count = local_max_z_index - local_min_z_index + 1; + + cells.resize(local_z_cell_count * global_cell_count.y() * global_cell_count.x()); + } + + Vec3i to_global_cell_coords(const Point &p, float slice_z) const { + Vec3crd position = Vec3crd { p.x(), p.y(), coord_t(scale_(slice_z)) }; + Vec3i cell_coords = position.cwiseQuotient(cell_size); + return cell_coords; + } + + Vec3i to_local_cell_coords(const Point &p, float slice_z) const { + Vec3i cell_coords = to_global_cell_coords(p, slice_z); + Vec3i local_cell_coords = cell_coords - local_z_index_offset * Vec3i::UnitZ(); + return local_cell_coords; + } + + size_t to_cell_index(const Vec3i &local_cell_coords) { + assert(local_cell_coords.x() >= 0); + assert(local_cell_coords.x() < global_cell_count.x()); + assert(local_cell_coords.y() >= 0); + assert(local_cell_coords.y() < global_cell_count.y()); + assert(local_cell_coords.z() >= 0); + assert(local_cell_coords.z() < local_z_cell_count); + return local_cell_coords.z() * global_cell_count.x() * global_cell_count.y() + + local_cell_coords.y() * global_cell_count.x() + + local_cell_coords.x(); + } + + Vec3crd cell_center(const Vec3i &global_cell_coords) { + return global_origin + global_cell_coords.cwiseProduct(cell_size); + } + + Cell& access_cell(const Point &p, float slice_z) { + return cells[to_cell_index(to_local_cell_coords(p, slice_z))]; + } + + Cell& access_cell(const Vec3i& local_cell_coords) { + return cells[to_cell_index(local_cell_coords)]; + } + + +#ifdef DEBUG_FILES + void debug_export(std::string file_name) { + Slic3r::CNumericLocalesSetter locales_setter; + { + FILE *fp = boost::nowide::fopen(debug_out_path((file_name + "_matrix.obj").c_str()).c_str(), "w"); + if (fp == nullptr) { + BOOST_LOG_TRIVIAL(error) + << "Debug files: Couldn't open " << file_name << " for writing"; + return; + } + + for (int x = 0; x < global_cell_count.x(); ++x) { + for (int y = 0; y < global_cell_count.y(); ++y) { + for (int z = 0; z < local_z_cell_count; ++z) { + Vec3f center = unscale(cell_center(Vec3i(x, y, z + local_z_index_offset))).cast(); + Cell &cell = access_cell(Vec3i(x, y, z)); + fprintf(fp, "v %f %f %f %f %f %f\n", + center(0), center(1), + center(2), + cell.weight, 0.0, 0.0 + ); + } + } + } + + fclose(fp); + } + } +#endif + + static constexpr float cell_height = scale_(0.15f); + + Vec3crd cell_size; + + Vec3crd global_origin; + Vec3crd global_size; + Vec3i global_cell_count; + + int local_z_index_offset; + int local_z_cell_count; + std::vector cells; + +}; + namespace Impl { #ifdef DEBUG_FILES @@ -79,23 +192,20 @@ EdgeGridWrapper compute_layer_edge_grid(const Layer *layer) { for (const auto *region : layer->regions()) { min_region_flow_width = std::min(min_region_flow_width, region->flow(FlowRole::frExternalPerimeter).width()); } - ExPolygons ex_polygons; + std::vector lines; for (const LayerRegion *layer_region : layer->regions()) { for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { - for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { - if (perimeter->role() == ExtrusionRole::erExternalPerimeter - || perimeter->role() == ExtrusionRole::erOverhangPerimeter) { - Points perimeter_points { }; - perimeter->collect_points(perimeter_points); - assert(perimeter->is_loop()); - perimeter_points.pop_back(); // EdgeGrid structure does not like repetition of the first/last point - ex_polygons.push_back(ExPolygon { perimeter_points }); - } // ex_perimeter - } // perimeter + lines.push_back(Points { }); + ex_entity->collect_points(lines.back()); + } // ex_entity + + for (const ExtrusionEntity *ex_entity : layer_region->fills.entities) { + lines.push_back(Points { }); + ex_entity->collect_points(lines.back()); } // ex_entity } - return EdgeGridWrapper(scale_(min_region_flow_width), ex_polygons); + return EdgeGridWrapper(scale_(min_region_flow_width), lines); } //TODO needs revision @@ -119,14 +229,36 @@ coordf_t get_flow_width(const LayerRegion *region, ExtrusionRole role) { coordf_t get_max_allowed_distance(ExtrusionRole role, coord_t flow_width, bool external_perimeters_first, const Params ¶ms) { // <= distance / flow_width (can be larger for perimeter, if not external perimeter first) if ((role == ExtrusionRole::erExternalPerimeter || role == ExtrusionRole::erOverhangPerimeter) - && !(external_perimeters_first) + && (external_perimeters_first) ) { - return params.max_ex_perim_unsupported_distance_factor * flow_width; + return params.max_first_ex_perim_unsupported_distance_factor * flow_width; } else { return params.max_unsupported_distance_factor * flow_width; } } +struct SegmentAccumulator { + float distance = 0; //accumulated distance + float curvature = 0; //accumulated signed ccw angles + float max_curvature = 0; //max absolute accumulated value + + void add_distance(float dist) { + distance += dist; + } + + void add_angle(float ccw_angle) { + curvature += ccw_angle; + max_curvature = std::max(max_curvature, std::abs(curvature)); + } + + void reset() { + distance = 0; + curvature = 0; + max_curvature = 0; + } + +}; + Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, float slice_z, const LayerRegion *layer_region, @@ -145,13 +277,17 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, points.push(p); } - float unsupported_distance = params.bridge_distance + 1.0f; // initialize unsupported distance with larger than tolerable distance -> + SegmentAccumulator supports_acc { }; + supports_acc.add_distance(params.bridge_distance + 1.0f); // initialize unsupported distance with larger than tolerable distance -> // -> it prevents extruding perimeter start and short loops into air. - float curvature = 0; // current curvature of the unsupported part of the extrusion - it is accumulated value of signed ccw angles of continuously unsupported points. - float max_curvature = 0; // max curvature (in abs value) for the current unsupported segment. - Vec2f tmp = unscale(points.top()).cast(); - Vec3f prev_fpoint = Vec3f(tmp.x(), tmp.y(), slice_z); // prev point of the path. Initialize with first point. + SegmentAccumulator curling_acc { }; + const auto to_vec3f = [slice_z](const Point &point) { + Vec2f tmp = unscale(point).cast(); + return Vec3f(tmp.x(), tmp.y(), slice_z); + }; + + Vec3f prev_fpoint = to_vec3f(points.top()); // prev point of the path. Initialize with first point. coordf_t flow_width = get_flow_width(layer_region, entity->role()); bool external_perimters_first = layer_region->region().config().external_perimeters_first; const coordf_t max_allowed_dist_from_prev_layer = get_max_allowed_distance(entity->role(), flow_width, @@ -162,54 +298,50 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, points.pop(); Vec2f tmp = unscale(point).cast(); Vec3f fpoint = Vec3f(tmp.x(), tmp.y(), slice_z); + float edge_len = (fpoint - prev_fpoint).norm(); coordf_t dist_from_prev_layer { 0 }; if (!supported_grid.signed_distance(point, flow_width, dist_from_prev_layer)) { // dist from prev layer not found, assume empty layer issues.supports_nedded.push_back(fpoint); - unsupported_distance = 0; - curvature = 0; - max_curvature = 0; + supports_acc.reset(); } + float angle = 0; + if (!points.empty()) { + const Vec2f v1 = (fpoint - prev_fpoint).head<2>(); + const Vec2f v2 = unscale(points.top()).cast() - fpoint.head<2>(); + float dot = v1(0) * v2(0) + v1(1) * v2(1); + float cross = v1(0) * v2(1) - v1(1) * v2(0); + angle = float(atan2(float(cross), float(dot))); // ccw angle, TODO replace with angle func, once it gets into master + } + + supports_acc.add_angle(angle); + curling_acc.add_angle(angle); + if (dist_from_prev_layer > max_allowed_dist_from_prev_layer) { //extrusion point is unsupported - unsupported_distance += (fpoint - prev_fpoint).norm(); // for algorithm simplicity, expect that the whole line between prev and current point is unsupported + supports_acc.add_distance(edge_len); // for algorithm simplicity, expect that the whole line between prev and current point is unsupported - if (!points.empty()) { - const Vec2f v1 = (fpoint - prev_fpoint).head<2>(); - const Vec2f v2 = unscale(points.top()).cast() - fpoint.head<2>(); - float dot = v1(0) * v2(0) + v1(1) * v2(1); - float cross = v1(0) * v2(1) - v1(1) * v2(0); - float angle = float(atan2(float(cross), float(dot))); // ccw angle, TODO replace with angle func, once it gets into master - - curvature += angle; - max_curvature = std::max(abs(curvature), max_curvature); - } - - if (unsupported_distance // if unsupported distance is larger than bridge distance linearly decreased by curvature, enforce supports. + if (supports_acc.distance // if unsupported distance is larger than bridge distance linearly decreased by curvature, enforce supports. > params.bridge_distance - / (1.0f + (max_curvature * params.bridge_distance_decrease_by_curvature_factor / PI))) { + / (1.0f + + (supports_acc.max_curvature + * params.bridge_distance_decrease_by_curvature_factor / PI))) { issues.supports_nedded.push_back(fpoint); - - //DEBUG stuff TODO remove - std::cout << "SUPP: " << "udis: " << unsupported_distance << " curv: " << curvature << " max curv: " - << max_curvature << std::endl; - std::cout << "max dist from layer: " << max_allowed_dist_from_prev_layer << " measured dist: " - << dist_from_prev_layer << " FW: " << flow_width << std::endl; - - unsupported_distance = 0; - curvature = 0; - max_curvature = 0; + supports_acc.reset(); } } else { - unsupported_distance = 0; - curvature = 0; - max_curvature = 0; + supports_acc.reset(); } // Estimation of short curvy segments which are not supported -> problems with curling - // Currently the curling issues are ignored - if (max_curvature / (PI * unsupported_distance) > params.limit_curvature) { - issues.curling_up.push_back(fpoint); + if (dist_from_prev_layer > 0.0f) { //extrusion point is unsupported or poorly supported + curling_acc.add_distance(edge_len); + if (curling_acc.max_curvature / (PI * curling_acc.distance) > params.limit_curvature) { + issues.curling_up.push_back(fpoint); + curling_acc.reset(); + } + } else { + curling_acc.reset(); } prev_fpoint = fpoint; @@ -309,6 +441,10 @@ std::vector quick_search(const PrintObject *po, const Params ¶ms) { Issues full_search(const PrintObject *po, const Params ¶ms) { using namespace Impl; + + WeightDistributionMatrix matrix { po, 0, po->layers().size() }; + matrix.debug_export("matrix"); + size_t layer_count = po->layer_count(); Issues found_issues = tbb::parallel_reduce(tbb::blocked_range(1, layer_count), Issues { }, [&](tbb::blocked_range r, const Issues &init) { diff --git a/src/libslic3r/SupportableIssuesSearch.hpp b/src/libslic3r/SupportableIssuesSearch.hpp index 4e8a541f94..08b09d143f 100644 --- a/src/libslic3r/SupportableIssuesSearch.hpp +++ b/src/libslic3r/SupportableIssuesSearch.hpp @@ -9,15 +9,13 @@ namespace SupportableIssues { struct Params { float bridge_distance = 10.0f; - float limit_curvature = 0.3f; // used to detect curling issues, but they are currently not considered anyway + float limit_curvature = 0.15f; // used to detect curling issues - float max_unsupported_distance_factor = 0.0f; - // allow printing external perimeter in the air to some extent. it hopefully attaches to the internal perimeter. - float max_ex_perim_unsupported_distance_factor = 1.0f; + float max_first_ex_perim_unsupported_distance_factor = 0.0f; // if external perim first, return tighter max allowed distance from previous layer extrusion + float max_unsupported_distance_factor = 1.0f; // For internal perimeters, infill, bridges etc, allow gap of [extrusion width] size, these extrusions have usually something to stick to. float bridge_distance_decrease_by_curvature_factor = 5.0f; // allowed bridge distance = bridge_distance / ( 1 + this factor * (curvature / PI) ) }; - struct Issues { std::vector supports_nedded; std::vector curling_up; From f0bdf2760c3262629f7fbdf782fa67475fde7c0e Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Mon, 25 Apr 2022 17:28:13 +0200 Subject: [PATCH 09/78] improved voxelization - fixed bugs with sinking objects. testing version of flooding the weight matrix --- src/libslic3r/SupportableIssuesSearch.cpp | 268 +++++++++++++++++----- 1 file changed, 209 insertions(+), 59 deletions(-) diff --git a/src/libslic3r/SupportableIssuesSearch.cpp b/src/libslic3r/SupportableIssuesSearch.cpp index ee1f440608..4082f17ca9 100644 --- a/src/libslic3r/SupportableIssuesSearch.cpp +++ b/src/libslic3r/SupportableIssuesSearch.cpp @@ -33,7 +33,7 @@ bool Issues::empty() const { struct Cell { float weight; - char last_extrusion_id; + int island_id; }; struct WeightDistributionMatrix { @@ -41,67 +41,173 @@ struct WeightDistributionMatrix { // This corresponds to angle of ~26 degrees between center of one cell and other one up and sideways // which is approximately a limiting printable angle. - WeightDistributionMatrix(const PrintObject *po, size_t layer_idx_begin, size_t layer_idx_end) { - Vec3crd object_origin = scaled(po->trafo_centered() * Vec3d::Zero()); - Vec3crd min = object_origin - po->size() / 2 - Vec3crd::Ones(); - Vec3crd max = object_origin + po->size() / 2 + Vec3crd::Ones(); + WeightDistributionMatrix() = default; + + void init(const PrintObject *po, size_t layer_idx_begin, size_t layer_idx_end) { + Vec2crd size_half = po->size().head<2>().cwiseQuotient(Vec2crd(2, 2)) + Vec2crd::Ones(); + Vec3crd min = Vec3crd(-size_half.x(), -size_half.y(), 0); + Vec3crd max = Vec3crd(size_half.x(), size_half.y(), po->height()); cell_size = Vec3crd { int(cell_height * 2), int(cell_height * 2), int(cell_height) }; + assert(cell_size.x() == cell_size.y()); global_origin = min; global_size = max - min; - global_cell_count = global_size.cwiseQuotient(cell_size); + global_cell_count = global_size.cwiseQuotient(cell_size) + Vec3i::Ones(); - coord_t local_min_z = scale_(po->layers()[layer_idx_begin]->slice_z); - coord_t local_max_z = scale_(po->layers()[layer_idx_end]->slice_z); - coord_t local_min_z_index = local_min_z / cell_size.z(); - coord_t local_max_z_index = local_max_z / cell_size.z(); + coord_t local_min_z = scale_(po->layers()[layer_idx_begin]->print_z); + coord_t local_max_z = scale_(po->layers()[layer_idx_end > 0 ? layer_idx_end - 1 : 0]->print_z); + int local_min_z_index = local_min_z / cell_size.z(); + int local_max_z_index = local_max_z / cell_size.z() + 1; local_z_index_offset = local_min_z_index; - local_z_cell_count = local_max_z_index - local_min_z_index + 1; + local_z_cell_count = local_max_z_index + 1 - local_min_z_index; cells.resize(local_z_cell_count * global_cell_count.y() * global_cell_count.x()); } - Vec3i to_global_cell_coords(const Point &p, float slice_z) const { - Vec3crd position = Vec3crd { p.x(), p.y(), coord_t(scale_(slice_z)) }; - Vec3i cell_coords = position.cwiseQuotient(cell_size); + Vec3i to_global_cell_coords(const Vec3i &local_cell_coords) const { + return local_cell_coords + local_z_index_offset * Vec3i::UnitZ(); + } + + Vec3i to_local_cell_coords(const Vec3i &global_cell_coords) const { + return global_cell_coords - local_z_index_offset * Vec3i::UnitZ(); + } + + Vec3i to_global_cell_coords(const Point &p, float print_z) const { + Vec3i position = Vec3crd { p.x(), p.y(), int(scale_(print_z)) }; + Vec3i cell_coords = (position - this->global_origin).cwiseQuotient(this->cell_size); return cell_coords; } - Vec3i to_local_cell_coords(const Point &p, float slice_z) const { - Vec3i cell_coords = to_global_cell_coords(p, slice_z); - Vec3i local_cell_coords = cell_coords - local_z_index_offset * Vec3i::UnitZ(); - return local_cell_coords; + Vec3i to_local_cell_coords(const Point &p, float print_z) const { + Vec3i cell_coords = this->to_global_cell_coords(p, print_z); + return this->to_local_cell_coords(cell_coords); } - size_t to_cell_index(const Vec3i &local_cell_coords) { + size_t to_cell_index(const Vec3i &local_cell_coords) const { assert(local_cell_coords.x() >= 0); assert(local_cell_coords.x() < global_cell_count.x()); assert(local_cell_coords.y() >= 0); assert(local_cell_coords.y() < global_cell_count.y()); assert(local_cell_coords.z() >= 0); assert(local_cell_coords.z() < local_z_cell_count); + return local_cell_coords.z() * global_cell_count.x() * global_cell_count.y() + local_cell_coords.y() * global_cell_count.x() + local_cell_coords.x(); } - Vec3crd cell_center(const Vec3i &global_cell_coords) { - return global_origin + global_cell_coords.cwiseProduct(cell_size); + Vec3crd get_cell_center(const Vec3i &global_cell_coords) const { + return global_origin + global_cell_coords.cwiseProduct(this->cell_size) + + this->cell_size.cwiseQuotient(Vec3crd(2, 2, 2)); } - Cell& access_cell(const Point &p, float slice_z) { - return cells[to_cell_index(to_local_cell_coords(p, slice_z))]; + Cell& access_cell(const Point &p, float print_z) { + return cells[this->to_cell_index(to_local_cell_coords(p, print_z))]; } - Cell& access_cell(const Vec3i& local_cell_coords) { - return cells[to_cell_index(local_cell_coords)]; + Cell& access_cell(const Vec3i &local_cell_coords) { + return cells[this->to_cell_index(local_cell_coords)]; } + const Cell& access_cell(const Vec3i &local_cell_coords) const { + return cells[this->to_cell_index(local_cell_coords)]; + } + + void ditribute_edge_weight(const Point &p1, const Point &p2, float print_z, coordf_t width) { + Vec2d dir = (p2 - p1).cast(); + double length = dir.norm(); + if (length < 0.01) { + return; + } + dir /= length; + double step_size = this->cell_size.x() / 2.0; + + double distributed_length = 0; + while (distributed_length < length) { + double next_len = std::min(length, distributed_length + step_size); + double current_dist_payload = next_len - distributed_length; + + Point location = p1 + ((next_len / length) * dir).cast(); + double payload = current_dist_payload * width; + + Vec3i local_index = this->to_local_cell_coords(location, print_z); + + if (this->to_cell_index(local_index) >= this->cells.size() || this->to_cell_index(local_index) < 0) { + std::cout << "loc: " << local_index.x() << " " << local_index.y() << " " << local_index.z() + << " globals: " << this->global_cell_count.x() << " " + << this->global_cell_count.y() << " " << this->local_z_cell_count << + "+" << this->local_z_cell_count << std::endl; + return; + } + this->access_cell(location, print_z).weight += payload; + + distributed_length = next_len; + } + } + + void merge(const WeightDistributionMatrix &other) { + int z_start = std::max(local_z_index_offset, other.local_z_index_offset); + int z_end = std::min(local_z_index_offset + local_z_cell_count, + other.local_z_index_offset + other.local_z_cell_count); + + for (int x = 0; x < global_cell_count.x(); ++x) { + for (int y = 0; y < global_cell_count.y(); ++y) { + for (int z = z_start; z < z_end; ++z) { + Vec3i global_coords { x, y, z }; + Vec3i local_coords = this->to_local_cell_coords(global_coords); + Vec3i other_local_coords = other.to_local_cell_coords(global_coords); + this->access_cell(local_coords).weight += other.access_cell(other_local_coords).weight; + } + } + } + } + + void distribute_top_down() { + const auto validate_xy_coords = [&](const Vec2i &local_coords) { + return local_coords.x() >= 0 && local_coords.y() >= 0 && + local_coords.x() < this->global_cell_count.x() && local_coords.y() < this->global_cell_count.y(); + }; + + Vec2i valid_coords[9]; + + for (int x = 0; x < global_cell_count.x(); ++x) { + for (int y = 0; y < global_cell_count.y(); ++y) { + for (int z = local_z_cell_count - 1; z > local_z_index_offset; --z) { + Cell ¤t = this->access_cell(Vec3i(x, y, z)); + size_t valid_coords_count = 0; + if (current.weight > 0) { + for (int y_offset = -1; y_offset <= 1; ++y_offset) { + for (int x_offset = -1; x_offset <= 1; ++x_offset) { + Vec2i xy_coords { x + x_offset, y + y_offset }; + if (validate_xy_coords(xy_coords) + && + this->access_cell(Vec3i(xy_coords.x(), xy_coords.y(), z - 1)).weight != 0) { + valid_coords[valid_coords_count] = xy_coords; + valid_coords_count++; + } + } + } + + float distribution = current.weight / valid_coords_count; + for (size_t index = 0; index < valid_coords_count; ++index) { + this->access_cell(Vec3i(valid_coords[index].x(), valid_coords[index].y(), z - 1)).weight += + distribution; + } + + if (valid_coords_count > 0) { + current.weight = 0; + } + } + } + + } + } + } #ifdef DEBUG_FILES - void debug_export(std::string file_name) { + void debug_export(std::string file_name) const { Slic3r::CNumericLocalesSetter locales_setter; { FILE *fp = boost::nowide::fopen(debug_out_path((file_name + "_matrix.obj").c_str()).c_str(), "w"); @@ -111,16 +217,30 @@ struct WeightDistributionMatrix { return; } + float max_weight = 0; for (int x = 0; x < global_cell_count.x(); ++x) { for (int y = 0; y < global_cell_count.y(); ++y) { for (int z = 0; z < local_z_cell_count; ++z) { - Vec3f center = unscale(cell_center(Vec3i(x, y, z + local_z_index_offset))).cast(); - Cell &cell = access_cell(Vec3i(x, y, z)); - fprintf(fp, "v %f %f %f %f %f %f\n", - center(0), center(1), - center(2), - cell.weight, 0.0, 0.0 - ); + const Cell &cell = access_cell(Vec3i(x, y, z)); + max_weight = std::max(max_weight, cell.weight); + } + } + } + + max_weight *= 0.8; + + for (int x = 0; x < global_cell_count.x(); ++x) { + for (int y = 0; y < global_cell_count.y(); ++y) { + for (int z = 0; z < local_z_cell_count; ++z) { + Vec3f center = unscale(get_cell_center(to_global_cell_coords(Vec3i { x, y, z }))).cast(); + const Cell &cell = access_cell(Vec3i(x, y, z)); + if (cell.weight != 0) { + fprintf(fp, "v %f %f %f %f %f %f\n", + center(0), center(1), + center(2), + cell.weight / max_weight, 0.0, 0.0 + ); + } } } } @@ -130,17 +250,17 @@ struct WeightDistributionMatrix { } #endif - static constexpr float cell_height = scale_(0.15f); + static constexpr float cell_height = scale_(0.3f); - Vec3crd cell_size; + Vec3crd cell_size { }; - Vec3crd global_origin; - Vec3crd global_size; - Vec3i global_cell_count; + Vec3crd global_origin { }; + Vec3crd global_size { }; + Vec3i global_cell_count { }; - int local_z_index_offset; - int local_z_cell_count; - std::vector cells; + int local_z_index_offset { }; + int local_z_cell_count { }; + std::vector cells { }; }; @@ -226,7 +346,7 @@ coordf_t get_flow_width(const LayerRegion *region, ExtrusionRole role) { } } -coordf_t get_max_allowed_distance(ExtrusionRole role, coord_t flow_width, bool external_perimeters_first, +coordf_t get_max_allowed_distance(ExtrusionRole role, coordf_t flow_width, bool external_perimeters_first, const Params ¶ms) { // <= distance / flow_width (can be larger for perimeter, if not external perimeter first) if ((role == ExtrusionRole::erExternalPerimeter || role == ExtrusionRole::erOverhangPerimeter) && (external_perimeters_first) @@ -260,15 +380,17 @@ struct SegmentAccumulator { }; Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, - float slice_z, + float print_z, const LayerRegion *layer_region, const EdgeGridWrapper &supported_grid, + WeightDistributionMatrix &weight_matrix, const Params ¶ms) { Issues issues { }; if (entity->is_collection()) { for (const auto *e : static_cast(entity)->entities) { - issues.add(check_extrusion_entity_stability(e, slice_z, layer_region, supported_grid, params)); + issues.add( + check_extrusion_entity_stability(e, print_z, layer_region, supported_grid, weight_matrix, params)); } } else { //single extrusion path, with possible varying parameters //prepare stack of points on the extrusion path. If there are long segments, additional points might be pushed onto the stack during the algorithm. @@ -282,12 +404,13 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, // -> it prevents extruding perimeter start and short loops into air. SegmentAccumulator curling_acc { }; - const auto to_vec3f = [slice_z](const Point &point) { + const auto to_vec3f = [print_z](const Point &point) { Vec2f tmp = unscale(point).cast(); - return Vec3f(tmp.x(), tmp.y(), slice_z); + return Vec3f(tmp.x(), tmp.y(), print_z); }; - Vec3f prev_fpoint = to_vec3f(points.top()); // prev point of the path. Initialize with first point. + Point prev_point = points.top(); // prev point of the path. Initialize with first point. + Vec3f prev_fpoint = to_vec3f(prev_point); coordf_t flow_width = get_flow_width(layer_region, entity->role()); bool external_perimters_first = layer_region->region().config().external_perimeters_first; const coordf_t max_allowed_dist_from_prev_layer = get_max_allowed_distance(entity->role(), flow_width, @@ -297,9 +420,11 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, Point point = points.top(); points.pop(); Vec2f tmp = unscale(point).cast(); - Vec3f fpoint = Vec3f(tmp.x(), tmp.y(), slice_z); + Vec3f fpoint = Vec3f(tmp.x(), tmp.y(), print_z); float edge_len = (fpoint - prev_fpoint).norm(); + weight_matrix.ditribute_edge_weight(prev_point, point, print_z, flow_width); + coordf_t dist_from_prev_layer { 0 }; if (!supported_grid.signed_distance(point, flow_width, dist_from_prev_layer)) { // dist from prev layer not found, assume empty layer issues.supports_nedded.push_back(fpoint); @@ -344,6 +469,7 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, curling_acc.reset(); } + prev_point = point; prev_fpoint = fpoint; if (!points.empty()) { //oversampling if necessary @@ -363,7 +489,8 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, return issues; } -Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_check, const Params ¶ms) { +Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_check, + WeightDistributionMatrix &weight_matrix, const Params ¶ms) { std::cout << "Checking: " << layer_idx << std::endl; if (layer_idx == 0) { // first layer is usually ok @@ -379,16 +506,16 @@ Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_ for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { issues.add(check_extrusion_entity_stability(perimeter, - layer->slice_z, layer_region, - supported_grid, params)); + layer->print_z, layer_region, + supported_grid, weight_matrix, params)); } // perimeter } // ex_entity for (const ExtrusionEntity *ex_entity : layer_region->fills.entities) { for (const ExtrusionEntity *fill : static_cast(ex_entity)->entities) { if (fill->role() == ExtrusionRole::erGapFill || fill->role() == ExtrusionRole::erBridgeInfill) { issues.add(check_extrusion_entity_stability(fill, - layer->slice_z, layer_region, - supported_grid, params)); + layer->print_z, layer_region, + supported_grid, weight_matrix, params)); } } // fill } // ex_entity @@ -401,8 +528,8 @@ Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_ if (perimeter->role() == ExtrusionRole::erExternalPerimeter || perimeter->role() == ExtrusionRole::erOverhangPerimeter) { issues.add(check_extrusion_entity_stability(perimeter, - layer->slice_z, layer_region, - supported_grid, params)); + layer->print_z, layer_region, + supported_grid, weight_matrix, params)); }; // ex_perimeter } // perimeter } // ex_entity @@ -417,17 +544,28 @@ Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_ std::vector quick_search(const PrintObject *po, const Params ¶ms) { using namespace Impl; + WeightDistributionMatrix matrix { }; + matrix.init(po, 0, po->layers().size()); + std::mutex matrix_mutex; + size_t layer_count = po->layer_count(); std::vector layer_needs_supports(layer_count, false); tbb::parallel_for(tbb::blocked_range(1, layer_count), [&](tbb::blocked_range r) { + WeightDistributionMatrix weight_matrix { }; + weight_matrix.init(po, r.begin(), r.end()); + for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) { auto layer_issues = check_layer_stability(po, layer_idx, - false, params); + false, weight_matrix, params); if (!layer_issues.supports_nedded.empty()) { layer_needs_supports[layer_idx] = true; } } + + matrix_mutex.lock(); + matrix.merge(weight_matrix); + matrix_mutex.unlock(); }); std::vector problematic_layers; @@ -442,19 +580,27 @@ std::vector quick_search(const PrintObject *po, const Params ¶ms) { Issues full_search(const PrintObject *po, const Params ¶ms) { using namespace Impl; - WeightDistributionMatrix matrix { po, 0, po->layers().size() }; - matrix.debug_export("matrix"); + WeightDistributionMatrix matrix { }; + matrix.init(po, 0, po->layers().size()); + std::mutex matrix_mutex; size_t layer_count = po->layer_count(); Issues found_issues = tbb::parallel_reduce(tbb::blocked_range(1, layer_count), Issues { }, [&](tbb::blocked_range r, const Issues &init) { + WeightDistributionMatrix weight_matrix { }; + weight_matrix.init(po, r.begin(), r.end()); Issues issues = init; for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) { - auto layer_issues = check_layer_stability(po, layer_idx, true, params); + auto layer_issues = check_layer_stability(po, layer_idx, true, weight_matrix, params); if (!layer_issues.empty()) { issues.add(layer_issues); } } + + matrix_mutex.lock(); + matrix.merge(weight_matrix); + matrix_mutex.unlock(); + return issues; }, [](Issues left, const Issues &right) { @@ -463,6 +609,10 @@ Issues full_search(const PrintObject *po, const Params ¶ms) { } ); + matrix.distribute_top_down(); + + matrix.debug_export("weight"); + #ifdef DEBUG_FILES Impl::debug_export(found_issues, "issues"); #endif From d9bd1080da644dd90eef613a1d678cdf7abfe51d Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Tue, 26 Apr 2022 17:13:46 +0200 Subject: [PATCH 10/78] UNFINISHED! refactoring of algorithm to bottom up propagation of support islands Added CentroidAccumulators for balance issues checking --- src/libslic3r/PrintObject.cpp | 4 +- src/libslic3r/SupportableIssuesSearch.cpp | 225 +++++++++++++++++----- src/libslic3r/SupportableIssuesSearch.hpp | 18 +- 3 files changed, 192 insertions(+), 55 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index c9f64cfb3c..5dbd119124 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -429,8 +429,8 @@ void PrintObject::find_supportable_issues() Transform3d inv_transform = (obj_transform * model_transformation).inverse(); TriangleSelectorWrapper selector { model_volume->mesh() }; - for (const Vec3f &support_point : issues.supports_nedded) { - selector.enforce_spot(Vec3f(inv_transform.cast() * support_point), 0.3f); + for (const SupportableIssues::SupportPoint &support_point : issues.supports_nedded) { + selector.enforce_spot(Vec3f(inv_transform.cast() * support_point.position), 0.3f); } model_volume->supported_facets.set(selector.selector); diff --git a/src/libslic3r/SupportableIssuesSearch.cpp b/src/libslic3r/SupportableIssuesSearch.cpp index 4082f17ca9..87301278cd 100644 --- a/src/libslic3r/SupportableIssuesSearch.cpp +++ b/src/libslic3r/SupportableIssuesSearch.cpp @@ -5,10 +5,12 @@ #include "tbb/parallel_reduce.h" #include #include +#include #include #include "libslic3r/Layer.hpp" #include "libslic3r/ClipperUtils.hpp" +#include "Geometry/ConvexHull.hpp" #include "PolygonPointTest.hpp" #define DEBUG_FILES @@ -31,9 +33,68 @@ bool Issues::empty() const { return supports_nedded.empty() && curling_up.empty(); } +SupportPoint::SupportPoint(const Vec3f &position, float weight) : + position(position), weight(weight) { +} + +SupportPoint::SupportPoint(const Vec3f &position) : + position(position), weight(0.0f) { +} + +CurledFilament::CurledFilament(const Vec3f &position, float estimated_height) : + position(position), estimated_height(estimated_height) { +} + +CurledFilament::CurledFilament(const Vec3f &position) : + position(position), estimated_height(0.0f) { +} + struct Cell { float weight; - int island_id; + float curled_height; + int island_id = std::numeric_limits::max(); +}; + +struct CentroidAccumulator { + //TODO add base height + + Polygon convex_hull; + Points points; + Vec3d accumulated_value; + float accumulated_weight; + + void calculate_base_hull() { + convex_hull = Geometry::convex_hull(points); + } +}; + +struct CentroidAccumulators { + std::unordered_map mapping; + std::vector acccumulators; + + explicit CentroidAccumulators(int count) { + acccumulators.resize(count); + for (int index = 0; index < count; ++index) { + mapping[index - 1] = index; + } + } + + CentroidAccumulator& access(int id) { + return acccumulators[mapping[id]]; + } + + void merge_to(int from_id, int to_id) { + if (from_id == to_id) { + return; + } + CentroidAccumulator &from_acc = this->access(from_id); + CentroidAccumulator &to_acc = this->access(to_id); + to_acc.accumulated_value += from_acc.accumulated_value; + to_acc.accumulated_weight += from_acc.accumulated_weight; + to_acc.points.insert(to_acc.points.end(), from_acc.points.begin(), from_acc.points.end()); + to_acc.calculate_base_hull(); + mapping[from_id] = mapping[to_id]; + } }; struct WeightDistributionMatrix { @@ -75,11 +136,17 @@ struct WeightDistributionMatrix { } Vec3i to_global_cell_coords(const Point &p, float print_z) const { - Vec3i position = Vec3crd { p.x(), p.y(), int(scale_(print_z)) }; + Vec3crd position = Vec3crd { p.x(), p.y(), int(scale_(print_z)) }; Vec3i cell_coords = (position - this->global_origin).cwiseQuotient(this->cell_size); return cell_coords; } + Vec3i to_global_cell_coords(const Vec3f &position) const { + Vec3crd scaled_position = scaled(position); + Vec3i cell_coords = (scaled_position - this->global_origin).cwiseQuotient(this->cell_size); + return cell_coords; + } + Vec3i to_local_cell_coords(const Point &p, float print_z) const { Vec3i cell_coords = this->to_global_cell_coords(p, print_z); return this->to_local_cell_coords(cell_coords); @@ -107,6 +174,10 @@ struct WeightDistributionMatrix { return cells[this->to_cell_index(to_local_cell_coords(p, print_z))]; } + Cell& access_cell(const Vec3f &unscaled_position) { + return cells[this->to_cell_index(this->to_local_cell_coords(this->to_global_cell_coords(unscaled_position)))]; + } + Cell& access_cell(const Vec3i &local_cell_coords) { return cells[this->to_cell_index(local_cell_coords)]; } @@ -115,7 +186,7 @@ struct WeightDistributionMatrix { return cells[this->to_cell_index(local_cell_coords)]; } - void ditribute_edge_weight(const Point &p1, const Point &p2, float print_z, coordf_t width) { + void distribute_edge_weight(const Point &p1, const Point &p2, float print_z, float unscaled_width) { Vec2d dir = (p2 - p1).cast(); double length = dir.norm(); if (length < 0.01) { @@ -130,17 +201,7 @@ struct WeightDistributionMatrix { double current_dist_payload = next_len - distributed_length; Point location = p1 + ((next_len / length) * dir).cast(); - double payload = current_dist_payload * width; - - Vec3i local_index = this->to_local_cell_coords(location, print_z); - - if (this->to_cell_index(local_index) >= this->cells.size() || this->to_cell_index(local_index) < 0) { - std::cout << "loc: " << local_index.x() << " " << local_index.y() << " " << local_index.z() - << " globals: " << this->global_cell_count.x() << " " - << this->global_cell_count.y() << " " << this->local_z_cell_count << - "+" << this->local_z_cell_count << std::endl; - return; - } + float payload = unscale(current_dist_payload) * unscaled_width; this->access_cell(location, print_z).weight += payload; distributed_length = next_len; @@ -164,42 +225,97 @@ struct WeightDistributionMatrix { } } - void distribute_top_down() { + void analyze(Issues &issues) { + CentroidAccumulators accumulators(issues.supports_nedded.size() + 1); + + + //TODO split base support points by connectivity!! + for (int x = 0; x < global_cell_count.x(); ++x) { + for (int y = 0; y < global_cell_count.y(); ++y) { + Cell &cell = this->access_cell(Vec3i(x, y, 0)); + if (cell.weight > 0) { + cell.island_id = -1; + Vec3crd cell_center = this->get_cell_center(Vec3i(x, y, local_z_index_offset)); + CentroidAccumulator &acc = accumulators.access(-1); + acc.points.push_back(Point(cell_center.head<2>())); + acc.accumulated_value += cell_center.cast() * cell.weight; + acc.accumulated_weight += cell.weight; + } + } + } + + std::sort(issues.supports_nedded.begin(), issues.supports_nedded.end(), + [](const SupportPoint &left, const SupportPoint &right) { + return left.position.z() < right.position.z(); + }); + for (int index = 0; index < int(issues.supports_nedded.size()); ++index) { + Vec3i local_coords = this->to_local_cell_coords( + this->to_global_cell_coords(issues.supports_nedded[index].position)); + this->access_cell(local_coords).island_id = index; + accumulators.access(index).points.push_back( + Point(scaled(Vec2f(issues.supports_nedded[index].position.head<2>())))); + } + + for (const CurledFilament &curling : issues.curling_up) { + this->access_cell(curling.position).curled_height += curling.estimated_height; + } + const auto validate_xy_coords = [&](const Vec2i &local_coords) { return local_coords.x() >= 0 && local_coords.y() >= 0 && local_coords.x() < this->global_cell_count.x() && local_coords.y() < this->global_cell_count.y(); }; - Vec2i valid_coords[9]; + std::unordered_set modified_acc_ids; + modified_acc_ids.reserve(issues.supports_nedded.size() + 1); + for (int z = 1; z < local_z_cell_count; ++z) { + modified_acc_ids.clear(); + + for (int x = 0; x < global_cell_count.x(); ++x) { + for (int y = 0; y < global_cell_count.y(); ++y) { - for (int x = 0; x < global_cell_count.x(); ++x) { - for (int y = 0; y < global_cell_count.y(); ++y) { - for (int z = local_z_cell_count - 1; z > local_z_index_offset; --z) { Cell ¤t = this->access_cell(Vec3i(x, y, z)); - size_t valid_coords_count = 0; - if (current.weight > 0) { + //first determine island id + if (current.island_id == std::numeric_limits::max()) { for (int y_offset = -1; y_offset <= 1; ++y_offset) { for (int x_offset = -1; x_offset <= 1; ++x_offset) { Vec2i xy_coords { x + x_offset, y + y_offset }; - if (validate_xy_coords(xy_coords) - && - this->access_cell(Vec3i(xy_coords.x(), xy_coords.y(), z - 1)).weight != 0) { - valid_coords[valid_coords_count] = xy_coords; - valid_coords_count++; + if (validate_xy_coords(xy_coords)) { + Cell &under = this->access_cell(Vec3i(x, y, z - 1)); + int island_id = std::min(under.island_id, current.island_id); + int merging_id = std::max(under.island_id, current.island_id); + if (merging_id != std::numeric_limits::max() + && island_id != merging_id) { + accumulators.merge_to(merging_id, island_id); + } + if (island_id != std::numeric_limits::max()) { + current.island_id = island_id; + modified_acc_ids.insert(current.island_id); + } + + current.curled_height += under.curled_height + / (2 + std::abs(x_offset) + std::abs(y_offset)); } } } - - float distribution = current.weight / valid_coords_count; - for (size_t index = 0; index < valid_coords_count; ++index) { - this->access_cell(Vec3i(valid_coords[index].x(), valid_coords[index].y(), z - 1)).weight += - distribution; - } - - if (valid_coords_count > 0) { - current.weight = 0; - } } + + //Propagate to accumulators. TODO what to do if no supporter is found? + if (current.island_id != std::numeric_limits::max()) { + CentroidAccumulator &acc = accumulators.access(current.island_id); + acc.accumulated_value += current.weight * this->get_cell_center( + this->to_global_cell_coords(Vec3i(x, y, z))).cast(); + acc.accumulated_weight += current.weight; + } + } + } + + // check stability of modified centroid accumulators + for (int acc_index : modified_acc_ids) { + CentroidAccumulator &acc = accumulators.access(acc_index); + Vec3d centroid = acc.accumulated_value / acc.accumulated_weight; + + if (!acc.convex_hull.contains(Point(centroid.head<2>().cast()))) { + //TODO :] } } @@ -232,7 +348,8 @@ struct WeightDistributionMatrix { for (int x = 0; x < global_cell_count.x(); ++x) { for (int y = 0; y < global_cell_count.y(); ++y) { for (int z = 0; z < local_z_cell_count; ++z) { - Vec3f center = unscale(get_cell_center(to_global_cell_coords(Vec3i { x, y, z }))).cast(); + Vec3f center = unscale(get_cell_center(to_global_cell_coords(Vec3i { x, y, z }))).cast< + float>(); const Cell &cell = access_cell(Vec3i(x, y, z)); if (cell.weight != 0) { fprintf(fp, "v %f %f %f %f %f %f\n", @@ -262,7 +379,8 @@ struct WeightDistributionMatrix { int local_z_cell_count { }; std::vector cells { }; -}; +} +; namespace Impl { @@ -280,7 +398,8 @@ void debug_export(Issues issues, std::string file_name) { for (size_t i = 0; i < issues.supports_nedded.size(); ++i) { fprintf(fp, "v %f %f %f %f %f %f\n", - issues.supports_nedded[i](0), issues.supports_nedded[i](1), issues.supports_nedded[i](2), + issues.supports_nedded[i].position(0), issues.supports_nedded[i].position(1), + issues.supports_nedded[i].position(2), 1.0, 0.0, 0.0 ); } @@ -297,7 +416,8 @@ void debug_export(Issues issues, std::string file_name) { for (size_t i = 0; i < issues.curling_up.size(); ++i) { fprintf(fp, "v %f %f %f %f %f %f\n", - issues.curling_up[i](0), issues.curling_up[i](1), issues.curling_up[i](2), + issues.curling_up[i].position(0), issues.curling_up[i].position(1), + issues.curling_up[i].position(2), 0.0, 1.0, 0.0 ); } @@ -310,7 +430,8 @@ void debug_export(Issues issues, std::string file_name) { EdgeGridWrapper compute_layer_edge_grid(const Layer *layer) { float min_region_flow_width { 1.0f }; for (const auto *region : layer->regions()) { - min_region_flow_width = std::min(min_region_flow_width, region->flow(FlowRole::frExternalPerimeter).width()); + min_region_flow_width = std::min(min_region_flow_width, + region->flow(FlowRole::frExternalPerimeter).width()); } std::vector lines; for (const LayerRegion *layer_region : layer->regions()) { @@ -390,7 +511,8 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, if (entity->is_collection()) { for (const auto *e : static_cast(entity)->entities) { issues.add( - check_extrusion_entity_stability(e, print_z, layer_region, supported_grid, weight_matrix, params)); + check_extrusion_entity_stability(e, print_z, layer_region, supported_grid, weight_matrix, + params)); } } else { //single extrusion path, with possible varying parameters //prepare stack of points on the extrusion path. If there are long segments, additional points might be pushed onto the stack during the algorithm. @@ -419,15 +541,14 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, while (!points.empty()) { Point point = points.top(); points.pop(); - Vec2f tmp = unscale(point).cast(); - Vec3f fpoint = Vec3f(tmp.x(), tmp.y(), print_z); + Vec3f fpoint = to_vec3f(point); float edge_len = (fpoint - prev_fpoint).norm(); - weight_matrix.ditribute_edge_weight(prev_point, point, print_z, flow_width); + weight_matrix.distribute_edge_weight(prev_point, point, print_z, unscale(flow_width)); coordf_t dist_from_prev_layer { 0 }; if (!supported_grid.signed_distance(point, flow_width, dist_from_prev_layer)) { // dist from prev layer not found, assume empty layer - issues.supports_nedded.push_back(fpoint); + issues.supports_nedded.push_back(SupportPoint(fpoint)); supports_acc.reset(); } @@ -451,7 +572,7 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, / (1.0f + (supports_acc.max_curvature * params.bridge_distance_decrease_by_curvature_factor / PI))) { - issues.supports_nedded.push_back(fpoint); + issues.supports_nedded.push_back(SupportPoint(fpoint)); supports_acc.reset(); } } else { @@ -462,7 +583,7 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, if (dist_from_prev_layer > 0.0f) { //extrusion point is unsupported or poorly supported curling_acc.add_distance(edge_len); if (curling_acc.max_curvature / (PI * curling_acc.distance) > params.limit_curvature) { - issues.curling_up.push_back(fpoint); + issues.curling_up.push_back(CurledFilament(fpoint, layer_region->layer()->height)); curling_acc.reset(); } } else { @@ -512,7 +633,8 @@ Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_ } // ex_entity for (const ExtrusionEntity *ex_entity : layer_region->fills.entities) { for (const ExtrusionEntity *fill : static_cast(ex_entity)->entities) { - if (fill->role() == ExtrusionRole::erGapFill || fill->role() == ExtrusionRole::erBridgeInfill) { + if (fill->role() == ExtrusionRole::erGapFill + || fill->role() == ExtrusionRole::erBridgeInfill) { issues.add(check_extrusion_entity_stability(fill, layer->print_z, layer_region, supported_grid, weight_matrix, params)); @@ -577,7 +699,8 @@ std::vector quick_search(const PrintObject *po, const Params ¶ms) { return problematic_layers; } -Issues full_search(const PrintObject *po, const Params ¶ms) { +Issues +full_search(const PrintObject *po, const Params ¶ms) { using namespace Impl; WeightDistributionMatrix matrix { }; @@ -609,7 +732,7 @@ Issues full_search(const PrintObject *po, const Params ¶ms) { } ); - matrix.distribute_top_down(); + matrix.analyze(found_issues); matrix.debug_export("weight"); diff --git a/src/libslic3r/SupportableIssuesSearch.hpp b/src/libslic3r/SupportableIssuesSearch.hpp index 08b09d143f..fef4eecd5e 100644 --- a/src/libslic3r/SupportableIssuesSearch.hpp +++ b/src/libslic3r/SupportableIssuesSearch.hpp @@ -16,9 +16,23 @@ struct Params { float bridge_distance_decrease_by_curvature_factor = 5.0f; // allowed bridge distance = bridge_distance / ( 1 + this factor * (curvature / PI) ) }; +struct SupportPoint { + SupportPoint(const Vec3f &position, float weight); + explicit SupportPoint(const Vec3f &position); + Vec3f position; + float weight; +}; + +struct CurledFilament { + CurledFilament(const Vec3f &position, float estimated_height); + explicit CurledFilament(const Vec3f &position); + Vec3f position; + float estimated_height; +}; + struct Issues { - std::vector supports_nedded; - std::vector curling_up; + std::vector supports_nedded; + std::vector curling_up; void add(const Issues &layer_issues); bool empty() const; From 148b24bd93081bd34f1660ff04704d445066d87c Mon Sep 17 00:00:00 2001 From: Godrak Date: Wed, 27 Apr 2022 14:15:21 +0200 Subject: [PATCH 11/78] accumulators given base height; object base split to separate islands by connectivity --- src/libslic3r/SupportableIssuesSearch.cpp | 264 +++++++++++----------- 1 file changed, 129 insertions(+), 135 deletions(-) diff --git a/src/libslic3r/SupportableIssuesSearch.cpp b/src/libslic3r/SupportableIssuesSearch.cpp index 87301278cd..7309c462f7 100644 --- a/src/libslic3r/SupportableIssuesSearch.cpp +++ b/src/libslic3r/SupportableIssuesSearch.cpp @@ -23,10 +23,8 @@ namespace Slic3r { namespace SupportableIssues { void Issues::add(const Issues &layer_issues) { - supports_nedded.insert(supports_nedded.end(), - layer_issues.supports_nedded.begin(), layer_issues.supports_nedded.end()); - curling_up.insert(curling_up.end(), layer_issues.curling_up.begin(), - layer_issues.curling_up.end()); + supports_nedded.insert(supports_nedded.end(), layer_issues.supports_nedded.begin(), layer_issues.supports_nedded.end()); + curling_up.insert(curling_up.end(), layer_issues.curling_up.begin(), layer_issues.curling_up.end()); } bool Issues::empty() const { @@ -56,12 +54,15 @@ struct Cell { }; struct CentroidAccumulator { - //TODO add base height + Polygon convex_hull { }; + Points points { }; + Vec3d accumulated_value { }; + float accumulated_weight { }; + const double base_height { }; - Polygon convex_hull; - Points points; - Vec3d accumulated_value; - float accumulated_weight; + explicit CentroidAccumulator(double base_height) : + base_height(base_height) { + } void calculate_base_hull() { convex_hull = Geometry::convex_hull(points); @@ -72,11 +73,14 @@ struct CentroidAccumulators { std::unordered_map mapping; std::vector acccumulators; - explicit CentroidAccumulators(int count) { - acccumulators.resize(count); - for (int index = 0; index < count; ++index) { - mapping[index - 1] = index; - } + explicit CentroidAccumulators(size_t reserve_count) { + acccumulators.reserve(reserve_count); + } + + CentroidAccumulator& create_accumulator(int id, double base_height) { + mapping[id] = acccumulators.size(); + acccumulators.push_back(CentroidAccumulator { base_height }); + return this->access(id); } CentroidAccumulator& access(int id) { @@ -160,14 +164,12 @@ struct WeightDistributionMatrix { assert(local_cell_coords.z() >= 0); assert(local_cell_coords.z() < local_z_cell_count); - return local_cell_coords.z() * global_cell_count.x() * global_cell_count.y() - + local_cell_coords.y() * global_cell_count.x() + - local_cell_coords.x(); + return local_cell_coords.z() * global_cell_count.x() * global_cell_count.y() + local_cell_coords.y() * global_cell_count.x() + + local_cell_coords.x(); } Vec3crd get_cell_center(const Vec3i &global_cell_coords) const { - return global_origin + global_cell_coords.cwiseProduct(this->cell_size) - + this->cell_size.cwiseQuotient(Vec3crd(2, 2, 2)); + return global_origin + global_cell_coords.cwiseProduct(this->cell_size) + this->cell_size.cwiseQuotient(Vec3crd(2, 2, 2)); } Cell& access_cell(const Point &p, float print_z) { @@ -210,8 +212,7 @@ struct WeightDistributionMatrix { void merge(const WeightDistributionMatrix &other) { int z_start = std::max(local_z_index_offset, other.local_z_index_offset); - int z_end = std::min(local_z_index_offset + local_z_cell_count, - other.local_z_index_offset + other.local_z_cell_count); + int z_end = std::min(local_z_index_offset + local_z_cell_count, other.local_z_index_offset + other.local_z_cell_count); for (int x = 0; x < global_cell_count.x(); ++x) { for (int y = 0; y < global_cell_count.y(); ++y) { @@ -226,34 +227,50 @@ struct WeightDistributionMatrix { } void analyze(Issues &issues) { - CentroidAccumulators accumulators(issues.supports_nedded.size() + 1); + CentroidAccumulators accumulators(issues.supports_nedded.size() + 4); - - //TODO split base support points by connectivity!! - for (int x = 0; x < global_cell_count.x(); ++x) { - for (int y = 0; y < global_cell_count.y(); ++y) { + int next_island_id = -1; + for (int y = 0; y < global_cell_count.y(); ++y) { + for (int x = 0; x < global_cell_count.x(); ++x) { Cell &cell = this->access_cell(Vec3i(x, y, 0)); - if (cell.weight > 0) { - cell.island_id = -1; - Vec3crd cell_center = this->get_cell_center(Vec3i(x, y, local_z_index_offset)); - CentroidAccumulator &acc = accumulators.access(-1); - acc.points.push_back(Point(cell_center.head<2>())); - acc.accumulated_value += cell_center.cast() * cell.weight; - acc.accumulated_weight += cell.weight; + if (cell.weight > 0 && cell.island_id == std::numeric_limits::max()) { + CentroidAccumulator &acc = accumulators.create_accumulator(next_island_id, 0); + std::set coords_to_check { Vec2i(x, y) }; + while (!coords_to_check.empty()) { + Vec2i current_coords = *coords_to_check.begin(); + coords_to_check.erase(coords_to_check.begin()); + cell = this->access_cell(Vec3i(current_coords.x(), current_coords.y(), 0)); + if (cell.weight > 0 && cell.island_id == std::numeric_limits::max()) { + cell.island_id = next_island_id; + Vec3crd cell_center = this->get_cell_center( + Vec3i(current_coords.x(), current_coords.y(), local_z_index_offset)); + acc.points.push_back(Point(cell_center.head<2>())); + acc.accumulated_value += cell_center.cast() * cell.weight; + acc.accumulated_weight += cell.weight; + for (int y_offset = -1; y_offset <= 1; ++y_offset) { + for (int x_offset = -1; x_offset <= 1; ++x_offset) { + if (y_offset != 0 || x_offset != 0) { + coords_to_check.insert(Vec2i(current_coords.x() + x_offset, current_coords.y() + y_offset)); + } + } + } + } + } + next_island_id--; + acc.calculate_base_hull(); } } } - std::sort(issues.supports_nedded.begin(), issues.supports_nedded.end(), - [](const SupportPoint &left, const SupportPoint &right) { - return left.position.z() < right.position.z(); - }); + std::sort(issues.supports_nedded.begin(), issues.supports_nedded.end(), [](const SupportPoint &left, const SupportPoint &right) { + return left.position.z() < right.position.z(); + }); for (int index = 0; index < int(issues.supports_nedded.size()); ++index) { - Vec3i local_coords = this->to_local_cell_coords( - this->to_global_cell_coords(issues.supports_nedded[index].position)); + Vec3i local_coords = this->to_local_cell_coords(this->to_global_cell_coords(issues.supports_nedded[index].position)); this->access_cell(local_coords).island_id = index; - accumulators.access(index).points.push_back( - Point(scaled(Vec2f(issues.supports_nedded[index].position.head<2>())))); + CentroidAccumulator &acc = accumulators.create_accumulator(index, issues.supports_nedded[index].position.z()); + acc.points.push_back(Point(scaled(Vec2f(issues.supports_nedded[index].position.head<2>())))); + acc.calculate_base_hull(); } for (const CurledFilament &curling : issues.curling_up) { @@ -261,8 +278,8 @@ struct WeightDistributionMatrix { } const auto validate_xy_coords = [&](const Vec2i &local_coords) { - return local_coords.x() >= 0 && local_coords.y() >= 0 && - local_coords.x() < this->global_cell_count.x() && local_coords.y() < this->global_cell_count.y(); + return local_coords.x() >= 0 && local_coords.y() >= 0 && local_coords.x() < this->global_cell_count.x() + && local_coords.y() < this->global_cell_count.y(); }; std::unordered_set modified_acc_ids; @@ -283,8 +300,7 @@ struct WeightDistributionMatrix { Cell &under = this->access_cell(Vec3i(x, y, z - 1)); int island_id = std::min(under.island_id, current.island_id); int merging_id = std::max(under.island_id, current.island_id); - if (merging_id != std::numeric_limits::max() - && island_id != merging_id) { + if (merging_id != std::numeric_limits::max() && island_id != merging_id) { accumulators.merge_to(merging_id, island_id); } if (island_id != std::numeric_limits::max()) { @@ -292,8 +308,7 @@ struct WeightDistributionMatrix { modified_acc_ids.insert(current.island_id); } - current.curled_height += under.curled_height - / (2 + std::abs(x_offset) + std::abs(y_offset)); + current.curled_height += under.curled_height / (2 + std::abs(x_offset) + std::abs(y_offset)); } } } @@ -302,22 +317,28 @@ struct WeightDistributionMatrix { //Propagate to accumulators. TODO what to do if no supporter is found? if (current.island_id != std::numeric_limits::max()) { CentroidAccumulator &acc = accumulators.access(current.island_id); - acc.accumulated_value += current.weight * this->get_cell_center( - this->to_global_cell_coords(Vec3i(x, y, z))).cast(); + acc.accumulated_value += current.weight + * this->get_cell_center(this->to_global_cell_coords(Vec3i(x, y, z))).cast(); acc.accumulated_weight += current.weight; } } } - // check stability of modified centroid accumulators + // check stability of modified centroid accumulators. + // Stability is the amount of work needed to push the object from stable position into unstable. + // This amount of work is proportional to the increase of height of the centroid during toppling. + // image here: https://hgphysics.com/gph/c-forces/2-force-effects/1-moment/stability/ + // better image in Czech here in the first question: https://www.priklady.eu/cs/fyzika/mechanika-tuheho-telesa/stabilita-teles.alej for (int acc_index : modified_acc_ids) { CentroidAccumulator &acc = accumulators.access(acc_index); Vec3d centroid = acc.accumulated_value / acc.accumulated_weight; - - if (!acc.convex_hull.contains(Point(centroid.head<2>().cast()))) { - //TODO :] + //determine signed shortest distance to the convex hull + Point centroid_base_projection = Point(centroid.head<2>().cast()); + double distance_sq = std::numeric_limits::max(); + bool inside = true; + for (Line line : acc.convex_hull.lines()) { + distance_sq = std::min(line.distance_to_squared(centroid_base_projection), distance_sq); } - } } } @@ -348,15 +369,10 @@ struct WeightDistributionMatrix { for (int x = 0; x < global_cell_count.x(); ++x) { for (int y = 0; y < global_cell_count.y(); ++y) { for (int z = 0; z < local_z_cell_count; ++z) { - Vec3f center = unscale(get_cell_center(to_global_cell_coords(Vec3i { x, y, z }))).cast< - float>(); + Vec3f center = unscale(get_cell_center(to_global_cell_coords(Vec3i { x, y, z }))).cast(); const Cell &cell = access_cell(Vec3i(x, y, z)); if (cell.weight != 0) { - fprintf(fp, "v %f %f %f %f %f %f\n", - center(0), center(1), - center(2), - cell.weight / max_weight, 0.0, 0.0 - ); + fprintf(fp, "v %f %f %f %f %f %f\n", center(0), center(1), center(2), cell.weight / max_weight, 0.0, 0.0); } } } @@ -397,11 +413,8 @@ void debug_export(Issues issues, std::string file_name) { } for (size_t i = 0; i < issues.supports_nedded.size(); ++i) { - fprintf(fp, "v %f %f %f %f %f %f\n", - issues.supports_nedded[i].position(0), issues.supports_nedded[i].position(1), - issues.supports_nedded[i].position(2), - 1.0, 0.0, 0.0 - ); + fprintf(fp, "v %f %f %f %f %f %f\n", issues.supports_nedded[i].position(0), issues.supports_nedded[i].position(1), + issues.supports_nedded[i].position(2), 1.0, 0.0, 0.0); } fclose(fp); @@ -415,11 +428,8 @@ void debug_export(Issues issues, std::string file_name) { } for (size_t i = 0; i < issues.curling_up.size(); ++i) { - fprintf(fp, "v %f %f %f %f %f %f\n", - issues.curling_up[i].position(0), issues.curling_up[i].position(1), - issues.curling_up[i].position(2), - 0.0, 1.0, 0.0 - ); + fprintf(fp, "v %f %f %f %f %f %f\n", issues.curling_up[i].position(0), issues.curling_up[i].position(1), + issues.curling_up[i].position(2), 0.0, 1.0, 0.0); } fclose(fp); } @@ -430,8 +440,7 @@ void debug_export(Issues issues, std::string file_name) { EdgeGridWrapper compute_layer_edge_grid(const Layer *layer) { float min_region_flow_width { 1.0f }; for (const auto *region : layer->regions()) { - min_region_flow_width = std::min(min_region_flow_width, - region->flow(FlowRole::frExternalPerimeter).width()); + min_region_flow_width = std::min(min_region_flow_width, region->flow(FlowRole::frExternalPerimeter).width()); } std::vector lines; for (const LayerRegion *layer_region : layer->regions()) { @@ -452,26 +461,23 @@ EdgeGridWrapper compute_layer_edge_grid(const Layer *layer) { //TODO needs revision coordf_t get_flow_width(const LayerRegion *region, ExtrusionRole role) { switch (role) { - case ExtrusionRole::erBridgeInfill: - return region->flow(FlowRole::frExternalPerimeter).scaled_width(); - case ExtrusionRole::erExternalPerimeter: - return region->flow(FlowRole::frExternalPerimeter).scaled_width(); - case ExtrusionRole::erGapFill: - return region->flow(FlowRole::frInfill).scaled_width(); - case ExtrusionRole::erPerimeter: - return region->flow(FlowRole::frPerimeter).scaled_width(); - case ExtrusionRole::erSolidInfill: - return region->flow(FlowRole::frSolidInfill).scaled_width(); - default: - return region->flow(FlowRole::frPerimeter).scaled_width(); + case ExtrusionRole::erBridgeInfill: + return region->flow(FlowRole::frExternalPerimeter).scaled_width(); + case ExtrusionRole::erExternalPerimeter: + return region->flow(FlowRole::frExternalPerimeter).scaled_width(); + case ExtrusionRole::erGapFill: + return region->flow(FlowRole::frInfill).scaled_width(); + case ExtrusionRole::erPerimeter: + return region->flow(FlowRole::frPerimeter).scaled_width(); + case ExtrusionRole::erSolidInfill: + return region->flow(FlowRole::frSolidInfill).scaled_width(); + default: + return region->flow(FlowRole::frPerimeter).scaled_width(); } } -coordf_t get_max_allowed_distance(ExtrusionRole role, coordf_t flow_width, bool external_perimeters_first, - const Params ¶ms) { // <= distance / flow_width (can be larger for perimeter, if not external perimeter first) - if ((role == ExtrusionRole::erExternalPerimeter || role == ExtrusionRole::erOverhangPerimeter) - && (external_perimeters_first) - ) { +coordf_t get_max_allowed_distance(ExtrusionRole role, coordf_t flow_width, bool external_perimeters_first, const Params ¶ms) { // <= distance / flow_width (can be larger for perimeter, if not external perimeter first) + if ((role == ExtrusionRole::erExternalPerimeter || role == ExtrusionRole::erOverhangPerimeter) && (external_perimeters_first)) { return params.max_first_ex_perim_unsupported_distance_factor * flow_width; } else { return params.max_unsupported_distance_factor * flow_width; @@ -500,19 +506,13 @@ struct SegmentAccumulator { }; -Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, - float print_z, - const LayerRegion *layer_region, - const EdgeGridWrapper &supported_grid, - WeightDistributionMatrix &weight_matrix, - const Params ¶ms) { +Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, float print_z, const LayerRegion *layer_region, + const EdgeGridWrapper &supported_grid, WeightDistributionMatrix &weight_matrix, const Params ¶ms) { Issues issues { }; if (entity->is_collection()) { for (const auto *e : static_cast(entity)->entities) { - issues.add( - check_extrusion_entity_stability(e, print_z, layer_region, supported_grid, weight_matrix, - params)); + issues.add(check_extrusion_entity_stability(e, print_z, layer_region, supported_grid, weight_matrix, params)); } } else { //single extrusion path, with possible varying parameters //prepare stack of points on the extrusion path. If there are long segments, additional points might be pushed onto the stack during the algorithm. @@ -535,8 +535,8 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, Vec3f prev_fpoint = to_vec3f(prev_point); coordf_t flow_width = get_flow_width(layer_region, entity->role()); bool external_perimters_first = layer_region->region().config().external_perimeters_first; - const coordf_t max_allowed_dist_from_prev_layer = get_max_allowed_distance(entity->role(), flow_width, - external_perimters_first, params); + const coordf_t max_allowed_dist_from_prev_layer = get_max_allowed_distance(entity->role(), flow_width, external_perimters_first, + params); while (!points.empty()) { Point point = points.top(); @@ -568,10 +568,8 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, supports_acc.add_distance(edge_len); // for algorithm simplicity, expect that the whole line between prev and current point is unsupported if (supports_acc.distance // if unsupported distance is larger than bridge distance linearly decreased by curvature, enforce supports. - > params.bridge_distance - / (1.0f - + (supports_acc.max_curvature - * params.bridge_distance_decrease_by_curvature_factor / PI))) { + > params.bridge_distance + / (1.0f + (supports_acc.max_curvature * params.bridge_distance_decrease_by_curvature_factor / PI))) { issues.supports_nedded.push_back(SupportPoint(fpoint)); supports_acc.reset(); } @@ -610,8 +608,8 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, return issues; } -Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_check, - WeightDistributionMatrix &weight_matrix, const Params ¶ms) { +Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_check, WeightDistributionMatrix &weight_matrix, + const Params ¶ms) { std::cout << "Checking: " << layer_idx << std::endl; if (layer_idx == 0) { // first layer is usually ok @@ -626,18 +624,17 @@ Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_ for (const LayerRegion *layer_region : layer->regions()) { for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { - issues.add(check_extrusion_entity_stability(perimeter, - layer->print_z, layer_region, - supported_grid, weight_matrix, params)); + issues.add( + check_extrusion_entity_stability(perimeter, layer->print_z, layer_region, supported_grid, weight_matrix, + params)); } // perimeter } // ex_entity for (const ExtrusionEntity *ex_entity : layer_region->fills.entities) { for (const ExtrusionEntity *fill : static_cast(ex_entity)->entities) { - if (fill->role() == ExtrusionRole::erGapFill - || fill->role() == ExtrusionRole::erBridgeInfill) { - issues.add(check_extrusion_entity_stability(fill, - layer->print_z, layer_region, - supported_grid, weight_matrix, params)); + if (fill->role() == ExtrusionRole::erGapFill || fill->role() == ExtrusionRole::erBridgeInfill) { + issues.add( + check_extrusion_entity_stability(fill, layer->print_z, layer_region, supported_grid, weight_matrix, + params)); } } // fill } // ex_entity @@ -649,9 +646,9 @@ Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_ for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { if (perimeter->role() == ExtrusionRole::erExternalPerimeter || perimeter->role() == ExtrusionRole::erOverhangPerimeter) { - issues.add(check_extrusion_entity_stability(perimeter, - layer->print_z, layer_region, - supported_grid, weight_matrix, params)); + issues.add( + check_extrusion_entity_stability(perimeter, layer->print_z, layer_region, supported_grid, weight_matrix, + params)); }; // ex_perimeter } // perimeter } // ex_entity @@ -672,23 +669,21 @@ std::vector quick_search(const PrintObject *po, const Params ¶ms) { size_t layer_count = po->layer_count(); std::vector layer_needs_supports(layer_count, false); - tbb::parallel_for(tbb::blocked_range(1, layer_count), - [&](tbb::blocked_range r) { - WeightDistributionMatrix weight_matrix { }; - weight_matrix.init(po, r.begin(), r.end()); + tbb::parallel_for(tbb::blocked_range(1, layer_count), [&](tbb::blocked_range r) { + WeightDistributionMatrix weight_matrix { }; + weight_matrix.init(po, r.begin(), r.end()); - for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) { - auto layer_issues = check_layer_stability(po, layer_idx, - false, weight_matrix, params); - if (!layer_issues.supports_nedded.empty()) { - layer_needs_supports[layer_idx] = true; - } - } + for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) { + auto layer_issues = check_layer_stability(po, layer_idx, false, weight_matrix, params); + if (!layer_issues.supports_nedded.empty()) { + layer_needs_supports[layer_idx] = true; + } + } - matrix_mutex.lock(); - matrix.merge(weight_matrix); - matrix_mutex.unlock(); - }); + matrix_mutex.lock(); + matrix.merge(weight_matrix); + matrix_mutex.unlock(); + }); std::vector problematic_layers; for (size_t index = 0; index < layer_needs_supports.size(); ++index) { @@ -699,8 +694,7 @@ std::vector quick_search(const PrintObject *po, const Params ¶ms) { return problematic_layers; } -Issues -full_search(const PrintObject *po, const Params ¶ms) { +Issues full_search(const PrintObject *po, const Params ¶ms) { using namespace Impl; WeightDistributionMatrix matrix { }; From 5cc9bd380b3c625eaacaa485a9d7d3cbcb79cb7e Mon Sep 17 00:00:00 2001 From: Godrak Date: Wed, 27 Apr 2022 15:18:46 +0200 Subject: [PATCH 12/78] Compilation fixes after rebase --- src/libslic3r/PrintObject.cpp | 128 +++++++++++----------- src/libslic3r/SupportableIssuesSearch.cpp | 11 +- 2 files changed, 74 insertions(+), 65 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 5dbd119124..fce5d00570 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -92,7 +92,7 @@ PrintBase::ApplyStatus PrintObject::set_instances(PrintInstances &&instances) // Invalidate and set copies. PrintBase::ApplyStatus status = PrintBase::APPLY_STATUS_UNCHANGED; bool equal_length = instances.size() == m_instances.size(); - bool equal = equal_length && std::equal(instances.begin(), instances.end(), m_instances.begin(), + bool equal = equal_length && std::equal(instances.begin(), instances.end(), m_instances.begin(), [](const PrintInstance& lhs, const PrintInstance& rhs) { return lhs.model_instance == rhs.model_instance && lhs.shift == rhs.shift; }); if (! equal) { status = PrintBase::APPLY_STATUS_CHANGED; @@ -128,7 +128,7 @@ void PrintObject::make_perimeters() m_print->set_status(20, L("Generating perimeters")); BOOST_LOG_TRIVIAL(info) << "Generating perimeters..." << log_memory_info(); - + // Revert the typed slices into untyped slices. if (m_typed_slices) { for (Layer *layer : m_layers) { @@ -137,10 +137,10 @@ void PrintObject::make_perimeters() } m_typed_slices = false; } - + // compare each layer to the one below, and mark those slices needing // one additional inner perimeter, like the top of domed objects- - + // this algorithm makes sure that at least one perimeter is overlapping // but we don't generate any extra perimeter if fill density is zero, as they would be floating // inside the object - infill_only_where_needed should be the method of choice for printing @@ -248,7 +248,7 @@ void PrintObject::prepare_infill() // by the cummulative area of the previous $layerm->fill_surfaces. this->detect_surfaces_type(); m_print->throw_if_canceled(); - + // Decide what surfaces are to be filled. // Here the stTop / stBottomBridge / stBottom infill is turned to just stInternal if zero top / bottom infill layers are configured. // Also tiny stInternal surfaces are turned to stInternalSolid. @@ -323,7 +323,7 @@ void PrintObject::prepare_infill() } // for each layer } // for each region #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ - + // the following step needs to be done before combination because it may need // to remove only half of the combined infill this->bridge_over_infill(); @@ -459,7 +459,7 @@ void PrintObject::generate_support_material() if (this->set_started(posSupportMaterial)) { this->clear_support_layers(); if ((this->has_support() && m_layers.size() > 1) || (this->has_raft() && ! m_layers.empty())) { - m_print->set_status(85, L("Generating support material")); + m_print->set_status(85, L("Generating support material")); this->_generate_support_material(); m_print->throw_if_canceled(); } else { @@ -619,7 +619,7 @@ bool PrintObject::invalidate_state_by_config_options( } else if ( opt_key == "clip_multipart_objects" || opt_key == "elefant_foot_compensation" - || opt_key == "support_material_contact_distance" + || opt_key == "support_material_contact_distance" || opt_key == "xy_size_compensation") { steps.emplace_back(posSlice); } else if (opt_key == "support_material") { @@ -770,7 +770,7 @@ bool PrintObject::invalidate_state_by_config_options( bool PrintObject::invalidate_step(PrintObjectStep step) { bool invalidated = Inherited::invalidate_step(step); - + // propagate to dependent steps if (step == posPerimeters) { invalidated |= this->invalidate_steps({ posPrepareInfill, posInfill, posIroning }); @@ -843,7 +843,7 @@ void PrintObject::detect_surfaces_type() surfaces_new.assign(num_layers, Surfaces()); tbb::parallel_for( - tbb::blocked_range(0, + tbb::blocked_range(0, spiral_vase ? // In spiral vase mode, reserve the last layer for the top surface if more than 1 layer is planned for the vase bottom. ((num_layers > 1) ? num_layers - 1 : num_layers) : @@ -871,7 +871,7 @@ void PrintObject::detect_surfaces_type() // of current layer and upper one) Surfaces top; if (upper_layer) { - ExPolygons upper_slices = interface_shells ? + ExPolygons upper_slices = interface_shells ? diff_ex(layerm->slices.surfaces, upper_layer->m_regions[region_id]->slices.surfaces, ApplySafetyOffset::Yes) : diff_ex(layerm->slices.surfaces, upper_layer->lslices, ApplySafetyOffset::Yes); surfaces_append(top, opening_ex(upper_slices, offset), stTop); @@ -882,7 +882,7 @@ void PrintObject::detect_surfaces_type() for (Surface &surface : top) surface.surface_type = stTop; } - + // Find bottom surfaces (difference between current surfaces of current layer and lower one). Surfaces bottom; if (lower_layer) { @@ -905,7 +905,7 @@ void PrintObject::detect_surfaces_type() // if user requested internal shells, we need to identify surfaces // lying on other slices not belonging to this region if (interface_shells) { - // non-bridging bottom surfaces: any part of this layer lying + // non-bridging bottom surfaces: any part of this layer lying // on something else, excluding those lying on our own region surfaces_append( bottom, @@ -925,7 +925,7 @@ void PrintObject::detect_surfaces_type() for (Surface &surface : bottom) surface.surface_type = stBottom; } - + // now, if the object contained a thin membrane, we could have overlapping bottom // and top surfaces; let's do an intersection to discover them and consider them // as bottom surfaces (to allow for bridge detection) @@ -948,7 +948,7 @@ void PrintObject::detect_surfaces_type() SVG::export_expolygons(debug_out_path("1_detect_surfaces_type_%d_region%d-layer_%f.svg", iRun ++, region_id, layer->print_z).c_str(), expolygons_with_attributes); } #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ - + // save surfaces to layer Surfaces &surfaces_out = interface_shells ? surfaces_new[idx_layer] : layerm->slices.surfaces; Surfaces surfaces_backup; @@ -967,7 +967,7 @@ void PrintObject::detect_surfaces_type() surfaces_append(surfaces_out, std::move(top)); surfaces_append(surfaces_out, std::move(bottom)); - + // Slic3r::debugf " layer %d has %d bottom, %d top and %d internal surfaces\n", // $layerm->layer->id, scalar(@bottom), scalar(@top), scalar(@internal) if $Slic3r::debug; @@ -1275,7 +1275,7 @@ void PrintObject::discover_vertical_shells() #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ Flow solid_infill_flow = layerm->flow(frSolidInfill); - coord_t infill_line_spacing = solid_infill_flow.scaled_spacing(); + coord_t infill_line_spacing = solid_infill_flow.scaled_spacing(); // Find a union of perimeters below / above this surface to guarantee a minimum shell thickness. Polygons shell; Polygons holes; @@ -1311,8 +1311,8 @@ void PrintObject::discover_vertical_shells() if (int n_top_layers = region_config.top_solid_layers.value; n_top_layers > 0) { // Gather top regions projected to this layer. coordf_t print_z = layer->print_z; - for (int i = int(idx_layer) + 1; - i < int(cache_top_botom_regions.size()) && + for (int i = int(idx_layer) + 1; + i < int(cache_top_botom_regions.size()) && (i < int(idx_layer) + n_top_layers || m_layers[i]->print_z - print_z < region_config.top_solid_min_thickness - EPSILON); ++ i) { @@ -1321,7 +1321,7 @@ void PrintObject::discover_vertical_shells() holes = intersection(holes, cache.holes); if (! cache.top_surfaces.empty()) { polygons_append(shell, cache.top_surfaces); - // Running the union_ using the Clipper library piece by piece is cheaper + // Running the union_ using the Clipper library piece by piece is cheaper // than running the union_ all at once. shell = union_(shell); } @@ -1340,7 +1340,7 @@ void PrintObject::discover_vertical_shells() holes = intersection(holes, cache.holes); if (! cache.bottom_surfaces.empty()) { polygons_append(shell, cache.bottom_surfaces); - // Running the union_ using the Clipper library piece by piece is cheaper + // Running the union_ using the Clipper library piece by piece is cheaper // than running the union_ all at once. shell = union_(shell); } @@ -1351,14 +1351,14 @@ void PrintObject::discover_vertical_shells() Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-before-union-%d.svg", debug_idx), get_extents(shell)); svg.draw(shell); svg.draw_outline(shell, "black", scale_(0.05)); - svg.Close(); + svg.Close(); } #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ #if 0 { PROFILE_BLOCK(discover_vertical_shells_region_layer_shell_); // shell = union_(shell, true); - shell = union_(shell, false); + shell = union_(shell, false); } #endif #ifdef SLIC3R_DEBUG_SLICE_PROCESSING @@ -1374,7 +1374,7 @@ void PrintObject::discover_vertical_shells() Slic3r::SVG svg(debug_out_path("discover_vertical_shells-perimeters-after-union-%d.svg", debug_idx), get_extents(shell)); svg.draw(shell_ex); svg.draw_outline(shell_ex, "black", "blue", scale_(0.05)); - svg.Close(); + svg.Close(); } #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ @@ -1386,7 +1386,7 @@ void PrintObject::discover_vertical_shells() svg.draw(shell_ex, "blue", 0.5); svg.draw_outline(shell_ex, "black", "blue", scale_(0.05)); svg.Close(); - } + } { Slic3r::SVG svg(debug_out_path("discover_vertical_shells-internalvoid-wshell-%d.svg", debug_idx), get_extents(shell)); svg.draw(layerm->fill_surfaces.filter_by_type(stInternalVoid), "yellow", 0.5); @@ -1394,15 +1394,15 @@ void PrintObject::discover_vertical_shells() svg.draw(shell_ex, "blue", 0.5); svg.draw_outline(shell_ex, "black", "blue", scale_(0.05)); svg.Close(); - } + } { Slic3r::SVG svg(debug_out_path("discover_vertical_shells-internalvoid-wshell-%d.svg", debug_idx), get_extents(shell)); svg.draw(layerm->fill_surfaces.filter_by_type(stInternalVoid), "yellow", 0.5); svg.draw_outline(layerm->fill_surfaces.filter_by_type(stInternalVoid), "black", "blue", scale_(0.05)); svg.draw(shell_ex, "blue", 0.5); - svg.draw_outline(shell_ex, "black", "blue", scale_(0.05)); + svg.draw_outline(shell_ex, "black", "blue", scale_(0.05)); svg.Close(); - } + } #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ // Trim the shells region by the internal & internal void surfaces. @@ -1423,7 +1423,7 @@ void PrintObject::discover_vertical_shells() Polygons shell_before = shell; #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ #if 1 - // Intentionally inflate a bit more than how much the region has been shrunk, + // Intentionally inflate a bit more than how much the region has been shrunk, // so there will be some overlap between this solid infill and the other infill regions (mainly the sparse infill). shell = opening(union_(shell), 0.5f * min_perimeter_infill_spacing, 0.8f * min_perimeter_infill_spacing, ClipperLib::jtSquare); if (shell.empty()) @@ -1505,7 +1505,7 @@ void PrintObject::bridge_over_infill() for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) { const PrintRegion ®ion = this->printing_region(region_id); - + // skip bridging in case there are no voids if (region.config().fill_density.value == 100) continue; @@ -1514,7 +1514,7 @@ void PrintObject::bridge_over_infill() // skip first layer if (layer_it == m_layers.begin()) continue; - + Layer *layer = *layer_it; LayerRegion *layerm = layer->m_regions[region_id]; Flow bridge_flow = layerm->bridging_flow(frSolidInfill); @@ -1522,31 +1522,31 @@ void PrintObject::bridge_over_infill() // extract the stInternalSolid surfaces that might be transformed into bridges Polygons internal_solid; layerm->fill_surfaces.filter_by_type(stInternalSolid, &internal_solid); - + // check whether the lower area is deep enough for absorbing the extra flow // (for obvious physical reasons but also for preventing the bridge extrudates // from overflowing in 3D preview) ExPolygons to_bridge; { Polygons to_bridge_pp = internal_solid; - + // iterate through lower layers spanned by bridge_flow double bottom_z = layer->print_z - bridge_flow.height() - EPSILON; for (int i = int(layer_it - m_layers.begin()) - 1; i >= 0; --i) { const Layer* lower_layer = m_layers[i]; - + // stop iterating if layer is lower than bottom_z if (lower_layer->print_z < bottom_z) break; - + // iterate through regions and collect internal surfaces Polygons lower_internal; for (LayerRegion *lower_layerm : lower_layer->m_regions) lower_layerm->fill_surfaces.filter_by_type(stInternal, &lower_internal); - + // intersect such lower internal surfaces with the candidate solid surfaces to_bridge_pp = intersection(to_bridge_pp, lower_internal); } - + // there's no point in bridging too thin/short regions //FIXME Vojtech: The offset2 function is not a geometric offset, // therefore it may create 1) gaps, and 2) sharp corners, which are outside the original contour. @@ -1555,17 +1555,17 @@ void PrintObject::bridge_over_infill() float min_width = float(bridge_flow.scaled_width()) * 3.f; to_bridge_pp = opening(to_bridge_pp, min_width); } - + if (to_bridge_pp.empty()) continue; - + // convert into ExPolygons to_bridge = union_ex(to_bridge_pp); } - + #ifdef SLIC3R_DEBUG printf("Bridging %zu internal areas at layer %zu\n", to_bridge.size(), layer->id()); #endif - + // compute the remaning internal solid surfaces as difference ExPolygons not_to_bridge = diff_ex(internal_solid, to_bridge, ApplySafetyOffset::Yes); to_bridge = intersection_ex(to_bridge, internal_solid, ApplySafetyOffset::Yes); @@ -1574,7 +1574,7 @@ void PrintObject::bridge_over_infill() for (ExPolygon &ex : to_bridge) layerm->fill_surfaces.surfaces.push_back(Surface(stInternalBridge, ex)); for (ExPolygon &ex : not_to_bridge) - layerm->fill_surfaces.surfaces.push_back(Surface(stInternalSolid, ex)); + layerm->fill_surfaces.surfaces.push_back(Surface(stInternalSolid, ex)); /* # exclude infill from the layers below if needed # see discussion at https://github.com/alexrj/Slic3r/issues/240 @@ -1603,7 +1603,7 @@ void PrintObject::bridge_over_infill() $lower_layerm->fill_surfaces->clear; $lower_layerm->fill_surfaces->append($_) for @new_surfaces; } - + $excess -= $self->get_layer($i)->height; } } @@ -1693,7 +1693,7 @@ PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &defau // Switch of infill for very low infill rates, also avoid division by zero in infill generator for these very low rates. // See GH issue #5910. config.fill_density.value = 0; - else + else config.fill_density.value = std::min(config.fill_density.value, 100.); if (config.fuzzy_skin.value != FuzzySkinType::None && (config.fuzzy_skin_point_dist.value < 0.01 || config.fuzzy_skin_thickness.value < 0.001)) config.fuzzy_skin.value = FuzzySkinType::None; @@ -1852,7 +1852,7 @@ void PrintObject::clip_fill_surfaces() // Regularize the overhang regions, so that the infill areas will not become excessively jagged. smooth_outward( closing(upper_internal, closing_radius, ClipperLib::jtSquare, 0.), - scaled(0.1)), + scaled(0.1)), lower_layer_internal_surfaces); // Apply new internal infill to regions. for (LayerRegion *layerm : lower_layer->m_regions) { @@ -1880,7 +1880,7 @@ void PrintObject::clip_fill_surfaces() void PrintObject::discover_horizontal_shells() { BOOST_LOG_TRIVIAL(trace) << "discover_horizontal_shells()"; - + for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) { for (size_t i = 0; i < m_layers.size(); ++ i) { m_print->throw_if_canceled(); @@ -1899,7 +1899,7 @@ void PrintObject::discover_horizontal_shells() // If ensure_vertical_shell_thickness, then the rest has already been performed by discover_vertical_shells(). if (region_config.ensure_vertical_shell_thickness.value) continue; - + coordf_t print_z = layer->print_z; coordf_t bottom_z = layer->bottom_z(); for (size_t idx_surface_type = 0; idx_surface_type < 3; ++ idx_surface_type) { @@ -1932,11 +1932,11 @@ void PrintObject::discover_horizontal_shells() if (solid.empty()) continue; // Slic3r::debugf "Layer %d has %s surfaces\n", $i, ($type == stTop) ? 'top' : 'bottom'; - + // Scatter top / bottom regions to other layers. Scattering process is inherently serial, it is difficult to parallelize without locking. for (int n = (type == stTop) ? int(i) - 1 : int(i) + 1; (type == stTop) ? - (n >= 0 && (int(i) - n < num_solid_layers || + (n >= 0 && (int(i) - n < num_solid_layers || print_z - m_layers[n]->print_z < region_config.top_solid_min_thickness.value - EPSILON)) : (n < int(m_layers.size()) && (n - int(i) < num_solid_layers || m_layers[n]->bottom_z() - bottom_z < region_config.bottom_solid_min_thickness.value - EPSILON)); @@ -1945,7 +1945,7 @@ void PrintObject::discover_horizontal_shells() // Slic3r::debugf " looking for neighbors on layer %d...\n", $n; // Reference to the lower layer of a TOP surface, or an upper layer of a BOTTOM surface. LayerRegion *neighbor_layerm = m_layers[n]->regions()[region_id]; - + // find intersection between neighbor and current layer's surfaces // intersections have contours and holes // we update $solid so that we limit the next neighbor layer to the areas that were @@ -1980,7 +1980,7 @@ void PrintObject::discover_horizontal_shells() continue; } } - + if (region_config.fill_density.value == 0) { // if we're printing a hollow object we discard any solid shell thinner // than a perimeter width, since it's probably just crossing a sloping wall @@ -1988,7 +1988,7 @@ void PrintObject::discover_horizontal_shells() // obeying the solid shell count option strictly (DWIM!) float margin = float(neighbor_layerm->flow(frExternalPerimeter).scaled_width()); Polygons too_narrow = diff( - new_internal_solid, + new_internal_solid, opening(new_internal_solid, margin, margin + ClipperSafetyOffset, jtMiter, 5)); // Trim the regularized region by the original region. if (! too_narrow.empty()) @@ -2018,20 +2018,20 @@ void PrintObject::discover_horizontal_shells() for (const Surface &surface : neighbor_layerm->fill_surfaces.surfaces) if (surface.is_internal() && !surface.is_bridge()) polygons_append(internal, to_polygons(surface.expolygon)); - polygons_append(new_internal_solid, + polygons_append(new_internal_solid, intersection( expand(too_narrow, +margin), // Discard bridges as they are grown for anchoring and we can't - // remove such anchors. (This may happen when a bridge is being + // remove such anchors. (This may happen when a bridge is being // anchored onto a wall where little space remains after the bridge - // is grown, and that little space is an internal solid shell so + // is grown, and that little space is an internal solid shell so // it triggers this too_narrow logic.) internal)); // see https://github.com/prusa3d/PrusaSlicer/pull/3426 // solid = new_internal_solid; } } - + // internal-solid are the union of the existing internal-solid surfaces // and new ones SurfaceCollection backup = std::move(neighbor_layerm->fill_surfaces); @@ -2110,11 +2110,11 @@ void PrintObject::combine_infill() current_height += layer->height; ++ num_layers; } - + // Append lower layers (if any) to uppermost layer. combine[m_layers.size() - 1] = num_layers; } - + // loop through layers to which we have assigned layers to combine for (size_t layer_idx = 0; layer_idx < m_layers.size(); ++ layer_idx) { m_print->throw_if_canceled(); @@ -2134,8 +2134,8 @@ void PrintObject::combine_infill() intersection = intersection_ex(layerms[i]->fill_surfaces.filter_by_type(stInternal), intersection); double area_threshold = layerms.front()->infill_area_threshold(); if (! intersection.empty() && area_threshold > 0.) - intersection.erase(std::remove_if(intersection.begin(), intersection.end(), - [area_threshold](const ExPolygon &expoly) { return expoly.area() <= area_threshold; }), + intersection.erase(std::remove_if(intersection.begin(), intersection.end(), + [area_threshold](const ExPolygon &expoly) { return expoly.area() <= area_threshold; }), intersection.end()); if (intersection.empty()) continue; @@ -2147,15 +2147,15 @@ void PrintObject::combine_infill() // so let's remove those areas from all layers. Polygons intersection_with_clearance; intersection_with_clearance.reserve(intersection.size()); - float clearance_offset = + float clearance_offset = 0.5f * layerms.back()->flow(frPerimeter).scaled_width() + - // Because fill areas for rectilinear and honeycomb are grown + // Because fill areas for rectilinear and honeycomb are grown // later to overlap perimeters, we need to counteract that too. ((region.config().fill_pattern == ipRectilinear || region.config().fill_pattern == ipMonotonic || region.config().fill_pattern == ipGrid || region.config().fill_pattern == ipLine || - region.config().fill_pattern == ipHoneycomb) ? 1.5f : 0.5f) * + region.config().fill_pattern == ipHoneycomb) ? 1.5f : 0.5f) * layerms.back()->flow(frSolidInfill).scaled_width(); for (ExPolygon &expoly : intersection) polygons_append(intersection_with_clearance, offset(expoly, clearance_offset)); @@ -2367,7 +2367,7 @@ static void project_triangles_to_slabs(ConstLayerPtrsAdaptor layers, const index // The resulting triangles are fed to the Clipper library, which seem to handle flipped triangles well. // if (cross2(Vec2d((poly.pts[1] - poly.pts[0]).cast()), Vec2d((poly.pts[2] - poly.pts[1]).cast())) < 0) // std::swap(poly.pts.front(), poly.pts.back()); - + out[layer_id].emplace_back(std::move(poly.pts)); ++layer_id; } diff --git a/src/libslic3r/SupportableIssuesSearch.cpp b/src/libslic3r/SupportableIssuesSearch.cpp index 7309c462f7..93c10e7bb1 100644 --- a/src/libslic3r/SupportableIssuesSearch.cpp +++ b/src/libslic3r/SupportableIssuesSearch.cpp @@ -230,12 +230,21 @@ struct WeightDistributionMatrix { CentroidAccumulators accumulators(issues.supports_nedded.size() + 4); int next_island_id = -1; + auto custom_comparator = [](const Vec2i& left,const Vec2i& right){ + if (left.x() == right.x()) { + return left.y() < right.y(); + } + return left.x() < right.x(); + }; + std::set coords_to_check(custom_comparator); + for (int y = 0; y < global_cell_count.y(); ++y) { for (int x = 0; x < global_cell_count.x(); ++x) { Cell &cell = this->access_cell(Vec3i(x, y, 0)); if (cell.weight > 0 && cell.island_id == std::numeric_limits::max()) { CentroidAccumulator &acc = accumulators.create_accumulator(next_island_id, 0); - std::set coords_to_check { Vec2i(x, y) }; + coords_to_check.clear(); + coords_to_check.insert(Vec2i(x,y)); while (!coords_to_check.empty()) { Vec2i current_coords = *coords_to_check.begin(); coords_to_check.erase(coords_to_check.begin()); From 824e3f111e537ef8c84333674febc328ae3ff547 Mon Sep 17 00:00:00 2001 From: Godrak Date: Wed, 27 Apr 2022 18:37:16 +0200 Subject: [PATCH 13/78] extended model with balance checking - centroids of segments, bed adhesion, supports adhesion, model stability --- src/libslic3r/SupportableIssuesSearch.cpp | 179 +++++++++++++++------- src/libslic3r/SupportableIssuesSearch.hpp | 4 + 2 files changed, 125 insertions(+), 58 deletions(-) diff --git a/src/libslic3r/SupportableIssuesSearch.cpp b/src/libslic3r/SupportableIssuesSearch.cpp index 93c10e7bb1..5ae1547457 100644 --- a/src/libslic3r/SupportableIssuesSearch.cpp +++ b/src/libslic3r/SupportableIssuesSearch.cpp @@ -48,7 +48,7 @@ CurledFilament::CurledFilament(const Vec3f &position) : } struct Cell { - float weight; + float volume; float curled_height; int island_id = std::numeric_limits::max(); }; @@ -57,7 +57,7 @@ struct CentroidAccumulator { Polygon convex_hull { }; Points points { }; Vec3d accumulated_value { }; - float accumulated_weight { }; + float accumulated_volume { }; const double base_height { }; explicit CentroidAccumulator(double base_height) : @@ -66,6 +66,7 @@ struct CentroidAccumulator { void calculate_base_hull() { convex_hull = Geometry::convex_hull(points); + assert(convex_hull.is_counter_clockwise()); } }; @@ -94,19 +95,19 @@ struct CentroidAccumulators { CentroidAccumulator &from_acc = this->access(from_id); CentroidAccumulator &to_acc = this->access(to_id); to_acc.accumulated_value += from_acc.accumulated_value; - to_acc.accumulated_weight += from_acc.accumulated_weight; + to_acc.accumulated_volume += from_acc.accumulated_volume; to_acc.points.insert(to_acc.points.end(), from_acc.points.begin(), from_acc.points.end()); to_acc.calculate_base_hull(); mapping[from_id] = mapping[to_id]; } }; -struct WeightDistributionMatrix { +struct BalanceDistributionGrid { // Lets make Z coord half the size of X (and Y). // This corresponds to angle of ~26 degrees between center of one cell and other one up and sideways // which is approximately a limiting printable angle. - WeightDistributionMatrix() = default; + BalanceDistributionGrid() = default; void init(const PrintObject *po, size_t layer_idx_begin, size_t layer_idx_end) { Vec2crd size_half = po->size().head<2>().cwiseQuotient(Vec2crd(2, 2)) + Vec2crd::Ones(); @@ -188,7 +189,7 @@ struct WeightDistributionMatrix { return cells[this->to_cell_index(local_cell_coords)]; } - void distribute_edge_weight(const Point &p1, const Point &p2, float print_z, float unscaled_width) { + void distribute_edge(const Point &p1, const Point &p2, float print_z, float unscaled_width, float unscaled_height) { Vec2d dir = (p2 - p1).cast(); double length = dir.norm(); if (length < 0.01) { @@ -197,20 +198,22 @@ struct WeightDistributionMatrix { dir /= length; double step_size = this->cell_size.x() / 2.0; + float diameter = unscaled_height * unscaled_width * 0.7071f; // approximate constant to consider eliptical shape (1/sqrt(2)) + double distributed_length = 0; while (distributed_length < length) { double next_len = std::min(length, distributed_length + step_size); double current_dist_payload = next_len - distributed_length; Point location = p1 + ((next_len / length) * dir).cast(); - float payload = unscale(current_dist_payload) * unscaled_width; - this->access_cell(location, print_z).weight += payload; + float payload = unscale(current_dist_payload) * diameter; + this->access_cell(location, print_z).volume += payload; distributed_length = next_len; } } - void merge(const WeightDistributionMatrix &other) { + void merge(const BalanceDistributionGrid &other) { int z_start = std::max(local_z_index_offset, other.local_z_index_offset); int z_end = std::min(local_z_index_offset + local_z_cell_count, other.local_z_index_offset + other.local_z_cell_count); @@ -220,17 +223,17 @@ struct WeightDistributionMatrix { Vec3i global_coords { x, y, z }; Vec3i local_coords = this->to_local_cell_coords(global_coords); Vec3i other_local_coords = other.to_local_cell_coords(global_coords); - this->access_cell(local_coords).weight += other.access_cell(other_local_coords).weight; + this->access_cell(local_coords).volume += other.access_cell(other_local_coords).volume; } } } } - void analyze(Issues &issues) { + void analyze(Issues &issues, const Params ¶ms) { CentroidAccumulators accumulators(issues.supports_nedded.size() + 4); int next_island_id = -1; - auto custom_comparator = [](const Vec2i& left,const Vec2i& right){ + auto custom_comparator = [](const Vec2i &left, const Vec2i &right) { if (left.x() == right.x()) { return left.y() < right.y(); } @@ -241,21 +244,21 @@ struct WeightDistributionMatrix { for (int y = 0; y < global_cell_count.y(); ++y) { for (int x = 0; x < global_cell_count.x(); ++x) { Cell &cell = this->access_cell(Vec3i(x, y, 0)); - if (cell.weight > 0 && cell.island_id == std::numeric_limits::max()) { + if (cell.volume > 0 && cell.island_id == std::numeric_limits::max()) { CentroidAccumulator &acc = accumulators.create_accumulator(next_island_id, 0); coords_to_check.clear(); - coords_to_check.insert(Vec2i(x,y)); + coords_to_check.insert(Vec2i(x, y)); while (!coords_to_check.empty()) { Vec2i current_coords = *coords_to_check.begin(); coords_to_check.erase(coords_to_check.begin()); cell = this->access_cell(Vec3i(current_coords.x(), current_coords.y(), 0)); - if (cell.weight > 0 && cell.island_id == std::numeric_limits::max()) { + if (cell.volume > 0 && cell.island_id == std::numeric_limits::max()) { cell.island_id = next_island_id; Vec3crd cell_center = this->get_cell_center( Vec3i(current_coords.x(), current_coords.y(), local_z_index_offset)); acc.points.push_back(Point(cell_center.head<2>())); - acc.accumulated_value += cell_center.cast() * cell.weight; - acc.accumulated_weight += cell.weight; + acc.accumulated_value += cell_center.cast() * cell.volume; + acc.accumulated_volume += cell.volume; for (int y_offset = -1; y_offset <= 1; ++y_offset) { for (int x_offset = -1; x_offset <= 1; ++x_offset) { if (y_offset != 0 || x_offset != 0) { @@ -326,9 +329,9 @@ struct WeightDistributionMatrix { //Propagate to accumulators. TODO what to do if no supporter is found? if (current.island_id != std::numeric_limits::max()) { CentroidAccumulator &acc = accumulators.access(current.island_id); - acc.accumulated_value += current.weight + acc.accumulated_value += current.volume * this->get_cell_center(this->to_global_cell_coords(Vec3i(x, y, z))).cast(); - acc.accumulated_weight += current.weight; + acc.accumulated_volume += current.volume; } } } @@ -338,16 +341,76 @@ struct WeightDistributionMatrix { // This amount of work is proportional to the increase of height of the centroid during toppling. // image here: https://hgphysics.com/gph/c-forces/2-force-effects/1-moment/stability/ // better image in Czech here in the first question: https://www.priklady.eu/cs/fyzika/mechanika-tuheho-telesa/stabilita-teles.alej - for (int acc_index : modified_acc_ids) { - CentroidAccumulator &acc = accumulators.access(acc_index); - Vec3d centroid = acc.accumulated_value / acc.accumulated_weight; + for (int acc_id : modified_acc_ids) { + CentroidAccumulator &acc = accumulators.access(acc_id); + Vec3d centroid = acc.accumulated_value / acc.accumulated_volume; //determine signed shortest distance to the convex hull Point centroid_base_projection = Point(centroid.head<2>().cast()); + Point pivot; double distance_sq = std::numeric_limits::max(); bool inside = true; - for (Line line : acc.convex_hull.lines()) { - distance_sq = std::min(line.distance_to_squared(centroid_base_projection), distance_sq); + if (acc.convex_hull.points.size() == 1) { + pivot = acc.convex_hull.points[0]; + distance_sq = (pivot - centroid_base_projection).squaredNorm(); + inside = true; + } else { + for (Line line : acc.convex_hull.lines()) { + Point closest_point; + double dist_sq = line.distance_to_squared(centroid_base_projection, &closest_point); + if (dist_sq < distance_sq) { + pivot = closest_point; + distance_sq = dist_sq; + } + if (float(angle(line.b - line.a, centroid_base_projection - line.b)) < 0) { + inside = false; + } + } } + + bool additional_supports_needed = false; + double base_area = std::max(acc.convex_hull.area(), scale_(5.0)); // assume 5 mm area for support points and other degenerate bases + double sticking_force = base_area * (acc.base_height == 0 ? params.base_adhesion : params.support_adhesion); + if (inside) { + double toppling_force = (Vec2d(sqrt(distance_sq), acc.base_height).norm() - acc.base_height) * acc.accumulated_volume; + sticking_force += toppling_force; + } + double y_movement_force = 0.5f * acc.accumulated_volume * params.top_object_movement_speed + * params.top_object_movement_speed; + if (sticking_force < y_movement_force) { + additional_supports_needed = true; + } + + if (!inside) { + double torque = sqrt(distance_sq) * acc.accumulated_volume; + if (torque > sticking_force) { //comparing apples and oranges; but we are far beyond physical simulation + additional_supports_needed = true; + } + } + + if (additional_supports_needed) { + Vec2crd attractor_dir = inside ? pivot - centroid_base_projection : centroid_base_projection - pivot; + Vec2d attractor = centroid_base_projection.cast() + (1e9 * attractor_dir.cast().normalized()); + double min_dist = std::numeric_limits::max(); + Vec3d support_point = centroid; + for (int y = 0; y < global_cell_count.y(); ++y) { + for (int x = 0; x < global_cell_count.x(); ++x) { + Cell &cell = this->access_cell(Vec3i(x, y, 0)); + if (cell.island_id == acc_id) { + Vec3d cell_center = this->get_cell_center(this->to_global_cell_coords(Vec3i(x, y, z))).cast(); + double dist_sq = (cell_center.head<2>() - attractor).squaredNorm(); + if (dist_sq < min_dist) { + min_dist = dist_sq; + support_point = cell_center; + } + } + } + } + + issues.supports_nedded.emplace_back(support_point.cast()); + acc.points.push_back(Point(support_point.head<2>().cast())); + acc.calculate_base_hull(); + } + } } } @@ -356,32 +419,32 @@ struct WeightDistributionMatrix { void debug_export(std::string file_name) const { Slic3r::CNumericLocalesSetter locales_setter; { - FILE *fp = boost::nowide::fopen(debug_out_path((file_name + "_matrix.obj").c_str()).c_str(), "w"); + FILE *fp = boost::nowide::fopen(debug_out_path((file_name + "_grid.obj").c_str()).c_str(), "w"); if (fp == nullptr) { BOOST_LOG_TRIVIAL(error) << "Debug files: Couldn't open " << file_name << " for writing"; return; } - float max_weight = 0; + float max_volume = 0; for (int x = 0; x < global_cell_count.x(); ++x) { for (int y = 0; y < global_cell_count.y(); ++y) { for (int z = 0; z < local_z_cell_count; ++z) { const Cell &cell = access_cell(Vec3i(x, y, z)); - max_weight = std::max(max_weight, cell.weight); + max_volume = std::max(max_volume, cell.volume); } } } - max_weight *= 0.8; + max_volume *= 0.8; for (int x = 0; x < global_cell_count.x(); ++x) { for (int y = 0; y < global_cell_count.y(); ++y) { for (int z = 0; z < local_z_cell_count; ++z) { Vec3f center = unscale(get_cell_center(to_global_cell_coords(Vec3i { x, y, z }))).cast(); const Cell &cell = access_cell(Vec3i(x, y, z)); - if (cell.weight != 0) { - fprintf(fp, "v %f %f %f %f %f %f\n", center(0), center(1), center(2), cell.weight / max_weight, 0.0, 0.0); + if (cell.volume != 0) { + fprintf(fp, "v %f %f %f %f %f %f\n", center(0), center(1), center(2), cell.volume / max_volume, 0.0, 0.0); } } } @@ -516,12 +579,12 @@ struct SegmentAccumulator { }; Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, float print_z, const LayerRegion *layer_region, - const EdgeGridWrapper &supported_grid, WeightDistributionMatrix &weight_matrix, const Params ¶ms) { + const EdgeGridWrapper &supported_grid, BalanceDistributionGrid &balance_grid, const Params ¶ms) { Issues issues { }; if (entity->is_collection()) { for (const auto *e : static_cast(entity)->entities) { - issues.add(check_extrusion_entity_stability(e, print_z, layer_region, supported_grid, weight_matrix, params)); + issues.add(check_extrusion_entity_stability(e, print_z, layer_region, supported_grid, balance_grid, params)); } } else { //single extrusion path, with possible varying parameters //prepare stack of points on the extrusion path. If there are long segments, additional points might be pushed onto the stack during the algorithm. @@ -543,6 +606,7 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, float pri Point prev_point = points.top(); // prev point of the path. Initialize with first point. Vec3f prev_fpoint = to_vec3f(prev_point); coordf_t flow_width = get_flow_width(layer_region, entity->role()); + coordf_t layer_height = layer_region->layer()->height; bool external_perimters_first = layer_region->region().config().external_perimeters_first; const coordf_t max_allowed_dist_from_prev_layer = get_max_allowed_distance(entity->role(), flow_width, external_perimters_first, params); @@ -553,7 +617,7 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, float pri Vec3f fpoint = to_vec3f(point); float edge_len = (fpoint - prev_fpoint).norm(); - weight_matrix.distribute_edge_weight(prev_point, point, print_z, unscale(flow_width)); + balance_grid.distribute_edge(prev_point, point, print_z, unscale(flow_width), unscale(layer_height)); coordf_t dist_from_prev_layer { 0 }; if (!supported_grid.signed_distance(point, flow_width, dist_from_prev_layer)) { // dist from prev layer not found, assume empty layer @@ -617,7 +681,7 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, float pri return issues; } -Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_check, WeightDistributionMatrix &weight_matrix, +Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_check, BalanceDistributionGrid &balance_grid, const Params ¶ms) { std::cout << "Checking: " << layer_idx << std::endl; if (layer_idx == 0) { @@ -634,7 +698,7 @@ Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_ for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { issues.add( - check_extrusion_entity_stability(perimeter, layer->print_z, layer_region, supported_grid, weight_matrix, + check_extrusion_entity_stability(perimeter, layer->print_z, layer_region, supported_grid, balance_grid, params)); } // perimeter } // ex_entity @@ -642,8 +706,7 @@ Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_ for (const ExtrusionEntity *fill : static_cast(ex_entity)->entities) { if (fill->role() == ExtrusionRole::erGapFill || fill->role() == ExtrusionRole::erBridgeInfill) { issues.add( - check_extrusion_entity_stability(fill, layer->print_z, layer_region, supported_grid, weight_matrix, - params)); + check_extrusion_entity_stability(fill, layer->print_z, layer_region, supported_grid, balance_grid, params)); } } // fill } // ex_entity @@ -656,7 +719,7 @@ Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_ if (perimeter->role() == ExtrusionRole::erExternalPerimeter || perimeter->role() == ExtrusionRole::erOverhangPerimeter) { issues.add( - check_extrusion_entity_stability(perimeter, layer->print_z, layer_region, supported_grid, weight_matrix, + check_extrusion_entity_stability(perimeter, layer->print_z, layer_region, supported_grid, balance_grid, params)); }; // ex_perimeter } // perimeter @@ -672,26 +735,26 @@ Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_ std::vector quick_search(const PrintObject *po, const Params ¶ms) { using namespace Impl; - WeightDistributionMatrix matrix { }; - matrix.init(po, 0, po->layers().size()); - std::mutex matrix_mutex; + BalanceDistributionGrid grid { }; + grid.init(po, 0, po->layers().size()); + std::mutex grid_mutex; size_t layer_count = po->layer_count(); std::vector layer_needs_supports(layer_count, false); tbb::parallel_for(tbb::blocked_range(1, layer_count), [&](tbb::blocked_range r) { - WeightDistributionMatrix weight_matrix { }; - weight_matrix.init(po, r.begin(), r.end()); + BalanceDistributionGrid balance_grid { }; + balance_grid.init(po, r.begin(), r.end()); for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) { - auto layer_issues = check_layer_stability(po, layer_idx, false, weight_matrix, params); + auto layer_issues = check_layer_stability(po, layer_idx, false, balance_grid, params); if (!layer_issues.supports_nedded.empty()) { layer_needs_supports[layer_idx] = true; } } - matrix_mutex.lock(); - matrix.merge(weight_matrix); - matrix_mutex.unlock(); + grid_mutex.lock(); + grid.merge(balance_grid); + grid_mutex.unlock(); }); std::vector problematic_layers; @@ -706,26 +769,26 @@ std::vector quick_search(const PrintObject *po, const Params ¶ms) { Issues full_search(const PrintObject *po, const Params ¶ms) { using namespace Impl; - WeightDistributionMatrix matrix { }; - matrix.init(po, 0, po->layers().size()); - std::mutex matrix_mutex; + BalanceDistributionGrid grid { }; + grid.init(po, 0, po->layers().size()); + std::mutex grid_mutex; size_t layer_count = po->layer_count(); Issues found_issues = tbb::parallel_reduce(tbb::blocked_range(1, layer_count), Issues { }, [&](tbb::blocked_range r, const Issues &init) { - WeightDistributionMatrix weight_matrix { }; - weight_matrix.init(po, r.begin(), r.end()); + BalanceDistributionGrid balance_grid { }; + balance_grid.init(po, r.begin(), r.end()); Issues issues = init; for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) { - auto layer_issues = check_layer_stability(po, layer_idx, true, weight_matrix, params); + auto layer_issues = check_layer_stability(po, layer_idx, true, balance_grid, params); if (!layer_issues.empty()) { issues.add(layer_issues); } } - matrix_mutex.lock(); - matrix.merge(weight_matrix); - matrix_mutex.unlock(); + grid_mutex.lock(); + grid.merge(balance_grid); + grid_mutex.unlock(); return issues; }, @@ -735,9 +798,9 @@ Issues full_search(const PrintObject *po, const Params ¶ms) { } ); - matrix.analyze(found_issues); + grid.analyze(found_issues, params); - matrix.debug_export("weight"); + grid.debug_export("volume"); #ifdef DEBUG_FILES Impl::debug_export(found_issues, "issues"); diff --git a/src/libslic3r/SupportableIssuesSearch.hpp b/src/libslic3r/SupportableIssuesSearch.hpp index fef4eecd5e..867ccd21b5 100644 --- a/src/libslic3r/SupportableIssuesSearch.hpp +++ b/src/libslic3r/SupportableIssuesSearch.hpp @@ -14,6 +14,10 @@ struct Params { float max_first_ex_perim_unsupported_distance_factor = 0.0f; // if external perim first, return tighter max allowed distance from previous layer extrusion float max_unsupported_distance_factor = 1.0f; // For internal perimeters, infill, bridges etc, allow gap of [extrusion width] size, these extrusions have usually something to stick to. float bridge_distance_decrease_by_curvature_factor = 5.0f; // allowed bridge distance = bridge_distance / ( 1 + this factor * (curvature / PI) ) + + float base_adhesion = 60.0f; // adhesion per mm^2 of first layer; the value should say how much *volume* it can hold per one square millimiter + float support_adhesion = 300.0f; // adhesion per mm^2 of support interface layer + float top_object_movement_speed = 200.0f; // movement speed of 200 mm/s in Y }; struct SupportPoint { From 6caec6926cd837a2c70e07ae3a7df001acc13c33 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Thu, 28 Apr 2022 17:16:58 +0200 Subject: [PATCH 14/78] TON of bugfixes, balancing still does not work --- src/libslic3r/PrintObject.cpp | 5 +- src/libslic3r/SupportableIssuesSearch.cpp | 353 ++++++++++++++-------- 2 files changed, 230 insertions(+), 128 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index fce5d00570..f8fcc22039 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -421,7 +421,8 @@ void PrintObject::find_supportable_issues() } } else { SupportableIssues::Issues issues = SupportableIssues::full_search(this); - if (!issues.supports_nedded.empty()) { + //TODO fix +// if (!issues.supports_nedded.empty()) { auto obj_transform = this->trafo_centered(); for (ModelVolume *model_volume : this->model_object()->volumes) { if (model_volume->type() == ModelVolumeType::MODEL_PART) { @@ -443,7 +444,7 @@ void PrintObject::find_supportable_issues() #endif } } - } +// } } m_print->throw_if_canceled(); diff --git a/src/libslic3r/SupportableIssuesSearch.cpp b/src/libslic3r/SupportableIssuesSearch.cpp index 5ae1547457..f2612035d9 100644 --- a/src/libslic3r/SupportableIssuesSearch.cpp +++ b/src/libslic3r/SupportableIssuesSearch.cpp @@ -17,13 +17,15 @@ #ifdef DEBUG_FILES #include +#include "libslic3r/Color.hpp" #endif namespace Slic3r { namespace SupportableIssues { void Issues::add(const Issues &layer_issues) { - supports_nedded.insert(supports_nedded.end(), layer_issues.supports_nedded.begin(), layer_issues.supports_nedded.end()); + supports_nedded.insert(supports_nedded.end(), layer_issues.supports_nedded.begin(), + layer_issues.supports_nedded.end()); curling_up.insert(curling_up.end(), layer_issues.curling_up.begin(), layer_issues.curling_up.end()); } @@ -56,7 +58,7 @@ struct Cell { struct CentroidAccumulator { Polygon convex_hull { }; Points points { }; - Vec3d accumulated_value { }; + Vec3d accumulated_value = Vec3d::Zero(); float accumulated_volume { }; const double base_height { }; @@ -89,11 +91,11 @@ struct CentroidAccumulators { } void merge_to(int from_id, int to_id) { - if (from_id == to_id) { - return; - } CentroidAccumulator &from_acc = this->access(from_id); CentroidAccumulator &to_acc = this->access(to_id); + if (&from_acc == &to_acc) { + return; + } to_acc.accumulated_value += from_acc.accumulated_value; to_acc.accumulated_volume += from_acc.accumulated_volume; to_acc.points.insert(to_acc.points.end(), from_acc.points.begin(), from_acc.points.end()); @@ -103,10 +105,6 @@ struct CentroidAccumulators { }; struct BalanceDistributionGrid { - // Lets make Z coord half the size of X (and Y). - // This corresponds to angle of ~26 degrees between center of one cell and other one up and sideways - // which is approximately a limiting printable angle. - BalanceDistributionGrid() = default; void init(const PrintObject *po, size_t layer_idx_begin, size_t layer_idx_end) { @@ -165,12 +163,14 @@ struct BalanceDistributionGrid { assert(local_cell_coords.z() >= 0); assert(local_cell_coords.z() < local_z_cell_count); - return local_cell_coords.z() * global_cell_count.x() * global_cell_count.y() + local_cell_coords.y() * global_cell_count.x() + return local_cell_coords.z() * global_cell_count.x() * global_cell_count.y() + + local_cell_coords.y() * global_cell_count.x() + local_cell_coords.x(); } Vec3crd get_cell_center(const Vec3i &global_cell_coords) const { - return global_origin + global_cell_coords.cwiseProduct(this->cell_size) + this->cell_size.cwiseQuotient(Vec3crd(2, 2, 2)); + return global_origin + global_cell_coords.cwiseProduct(this->cell_size) + + this->cell_size.cwiseQuotient(Vec3crd(2, 2, 2)); } Cell& access_cell(const Point &p, float print_z) { @@ -192,13 +192,12 @@ struct BalanceDistributionGrid { void distribute_edge(const Point &p1, const Point &p2, float print_z, float unscaled_width, float unscaled_height) { Vec2d dir = (p2 - p1).cast(); double length = dir.norm(); - if (length < 0.01) { + if (length < 0.1) { return; } - dir /= length; double step_size = this->cell_size.x() / 2.0; - float diameter = unscaled_height * unscaled_width * 0.7071f; // approximate constant to consider eliptical shape (1/sqrt(2)) + float diameter = unscaled_height * unscaled_width * 0.7071f; // constant to simulate somewhat elliptical shape (1/sqrt(2)) double distributed_length = 0; while (distributed_length < length) { @@ -215,7 +214,8 @@ struct BalanceDistributionGrid { void merge(const BalanceDistributionGrid &other) { int z_start = std::max(local_z_index_offset, other.local_z_index_offset); - int z_end = std::min(local_z_index_offset + local_z_cell_count, other.local_z_index_offset + other.local_z_cell_count); + int z_end = std::min(local_z_index_offset + local_z_cell_count, + other.local_z_index_offset + other.local_z_cell_count); for (int x = 0; x < global_cell_count.x(); ++x) { for (int y = 0; y < global_cell_count.y(); ++y) { @@ -230,40 +230,49 @@ struct BalanceDistributionGrid { } void analyze(Issues &issues, const Params ¶ms) { + const auto validate_xy_coords = [&](const Vec2i &local_coords) { + return local_coords.x() >= 0 && local_coords.y() >= 0 && local_coords.x() < this->global_cell_count.x() + && local_coords.y() < this->global_cell_count.y(); + }; CentroidAccumulators accumulators(issues.supports_nedded.size() + 4); - - int next_island_id = -1; auto custom_comparator = [](const Vec2i &left, const Vec2i &right) { if (left.x() == right.x()) { return left.y() < right.y(); } return left.x() < right.x(); }; - std::set coords_to_check(custom_comparator); + int next_island_id = -1; + std::set coords_to_check(custom_comparator); for (int y = 0; y < global_cell_count.y(); ++y) { for (int x = 0; x < global_cell_count.x(); ++x) { - Cell &cell = this->access_cell(Vec3i(x, y, 0)); - if (cell.volume > 0 && cell.island_id == std::numeric_limits::max()) { + Cell &origin_cell = this->access_cell(Vec3i(x, y, 0)); + if (origin_cell.volume > 0 && origin_cell.island_id == std::numeric_limits::max()) { CentroidAccumulator &acc = accumulators.create_accumulator(next_island_id, 0); coords_to_check.clear(); coords_to_check.insert(Vec2i(x, y)); while (!coords_to_check.empty()) { Vec2i current_coords = *coords_to_check.begin(); coords_to_check.erase(coords_to_check.begin()); - cell = this->access_cell(Vec3i(current_coords.x(), current_coords.y(), 0)); - if (cell.volume > 0 && cell.island_id == std::numeric_limits::max()) { - cell.island_id = next_island_id; - Vec3crd cell_center = this->get_cell_center( - Vec3i(current_coords.x(), current_coords.y(), local_z_index_offset)); - acc.points.push_back(Point(cell_center.head<2>())); - acc.accumulated_value += cell_center.cast() * cell.volume; - acc.accumulated_volume += cell.volume; - for (int y_offset = -1; y_offset <= 1; ++y_offset) { - for (int x_offset = -1; x_offset <= 1; ++x_offset) { - if (y_offset != 0 || x_offset != 0) { - coords_to_check.insert(Vec2i(current_coords.x() + x_offset, current_coords.y() + y_offset)); - } + if (!validate_xy_coords(current_coords)) { + continue; + } + Cell &cell = this->access_cell(Vec3i(current_coords.x(), current_coords.y(), 0)); + if (cell.volume <= 0 || cell.island_id != std::numeric_limits::max()) { + continue; + } + cell.island_id = next_island_id; + Vec3crd cell_center = this->get_cell_center( + Vec3i(current_coords.x(), current_coords.y(), local_z_index_offset)); + acc.points.push_back(Point(cell_center.head<2>())); + acc.accumulated_value += cell_center.cast() * cell.volume; + acc.accumulated_volume += cell.volume; + + for (int y_offset = -1; y_offset <= 1; ++y_offset) { + for (int x_offset = -1; x_offset <= 1; ++x_offset) { + if (y_offset != 0 || x_offset != 0) { + coords_to_check.insert( + Vec2i(current_coords.x() + x_offset, current_coords.y() + y_offset)); } } } @@ -274,13 +283,16 @@ struct BalanceDistributionGrid { } } - std::sort(issues.supports_nedded.begin(), issues.supports_nedded.end(), [](const SupportPoint &left, const SupportPoint &right) { - return left.position.z() < right.position.z(); - }); + std::sort(issues.supports_nedded.begin(), issues.supports_nedded.end(), + [](const SupportPoint &left, const SupportPoint &right) { + return left.position.z() < right.position.z(); + }); for (int index = 0; index < int(issues.supports_nedded.size()); ++index) { - Vec3i local_coords = this->to_local_cell_coords(this->to_global_cell_coords(issues.supports_nedded[index].position)); + Vec3i local_coords = this->to_local_cell_coords( + this->to_global_cell_coords(issues.supports_nedded[index].position)); this->access_cell(local_coords).island_id = index; - CentroidAccumulator &acc = accumulators.create_accumulator(index, issues.supports_nedded[index].position.z()); + CentroidAccumulator &acc = accumulators.create_accumulator(index, + issues.supports_nedded[index].position.z()); acc.points.push_back(Point(scaled(Vec2f(issues.supports_nedded[index].position.head<2>())))); acc.calculate_base_hull(); } @@ -289,11 +301,6 @@ struct BalanceDistributionGrid { this->access_cell(curling.position).curled_height += curling.estimated_height; } - const auto validate_xy_coords = [&](const Vec2i &local_coords) { - return local_coords.x() >= 0 && local_coords.y() >= 0 && local_coords.x() < this->global_cell_count.x() - && local_coords.y() < this->global_cell_count.y(); - }; - std::unordered_set modified_acc_ids; modified_acc_ids.reserve(issues.supports_nedded.size() + 1); for (int z = 1; z < local_z_cell_count; ++z) { @@ -301,37 +308,47 @@ struct BalanceDistributionGrid { for (int x = 0; x < global_cell_count.x(); ++x) { for (int y = 0; y < global_cell_count.y(); ++y) { - Cell ¤t = this->access_cell(Vec3i(x, y, z)); - //first determine island id - if (current.island_id == std::numeric_limits::max()) { + if (current.volume > 0) { for (int y_offset = -1; y_offset <= 1; ++y_offset) { for (int x_offset = -1; x_offset <= 1; ++x_offset) { - Vec2i xy_coords { x + x_offset, y + y_offset }; - if (validate_xy_coords(xy_coords)) { - Cell &under = this->access_cell(Vec3i(x, y, z - 1)); - int island_id = std::min(under.island_id, current.island_id); - int merging_id = std::max(under.island_id, current.island_id); - if (merging_id != std::numeric_limits::max() && island_id != merging_id) { - accumulators.merge_to(merging_id, island_id); - } - if (island_id != std::numeric_limits::max()) { - current.island_id = island_id; - modified_acc_ids.insert(current.island_id); - } - - current.curled_height += under.curled_height / (2 + std::abs(x_offset) + std::abs(y_offset)); + if (validate_xy_coords(Vec2i(x_offset, y_offset))) { + Cell &under = this->access_cell(Vec3i(x + x_offset, y + y_offset, z - 1)); + current.curled_height += under.curled_height + / (2 + std::abs(x_offset) + std::abs(y_offset)); } } } } - //Propagate to accumulators. TODO what to do if no supporter is found? - if (current.island_id != std::numeric_limits::max()) { - CentroidAccumulator &acc = accumulators.access(current.island_id); - acc.accumulated_value += current.volume - * this->get_cell_center(this->to_global_cell_coords(Vec3i(x, y, z))).cast(); - acc.accumulated_volume += current.volume; + if (current.volume > 0 && current.island_id == std::numeric_limits::max()) { + int min_island_id_found = std::numeric_limits::max(); + std::unordered_set ids_to_merge { }; + for (int y_offset = -2; y_offset <= 2; ++y_offset) { + for (int x_offset = -2; x_offset <= 2; ++x_offset) { + if (validate_xy_coords(Vec2i(x + x_offset, y + y_offset))) { + Cell &under = this->access_cell(Vec3i(x + x_offset, y + y_offset, z - 1)); + if (under.island_id < min_island_id_found) { + min_island_id_found = under.island_id; + } + ids_to_merge.insert(under.island_id); + } + } + } + if (min_island_id_found < std::numeric_limits::max()) { + ids_to_merge.erase(std::numeric_limits::max()); + ids_to_merge.erase(min_island_id_found); + current.island_id = min_island_id_found; + for (auto id : ids_to_merge) { + accumulators.merge_to(id, min_island_id_found); + } + + CentroidAccumulator &acc = accumulators.access(current.island_id); + acc.accumulated_value += current.volume + * this->get_cell_center(this->to_global_cell_coords(Vec3i(x, y, z))).cast(); + acc.accumulated_volume += current.volume; + modified_acc_ids.insert(min_island_id_found); + } } } } @@ -342,8 +359,17 @@ struct BalanceDistributionGrid { // image here: https://hgphysics.com/gph/c-forces/2-force-effects/1-moment/stability/ // better image in Czech here in the first question: https://www.priklady.eu/cs/fyzika/mechanika-tuheho-telesa/stabilita-teles.alej for (int acc_id : modified_acc_ids) { + + std::cout << "controlling acc id: " << acc_id << std::endl; + CentroidAccumulator &acc = accumulators.access(acc_id); Vec3d centroid = acc.accumulated_value / acc.accumulated_volume; + + std::cout << "acc.accumulated_value : " << acc.accumulated_value.x() << " " + << acc.accumulated_value.y() << " " << acc.accumulated_value.z() << std::endl; + std::cout << "acc.accumulated_volume : " << acc.accumulated_volume << std::endl; + std::cout << "centorid: " << centroid.x() << " " << centroid.y() << " " << centroid.z() << std::endl; + //determine signed shortest distance to the convex hull Point centroid_base_projection = Point(centroid.head<2>().cast()); Point pivot; @@ -367,36 +393,58 @@ struct BalanceDistributionGrid { } } + std::cout << "centoroid inside ? " << inside << " and distance is: " << distance_sq << std::endl; + bool additional_supports_needed = false; - double base_area = std::max(acc.convex_hull.area(), scale_(5.0)); // assume 5 mm area for support points and other degenerate bases - double sticking_force = base_area * (acc.base_height == 0 ? params.base_adhesion : params.support_adhesion); + double base_area = std::max(acc.convex_hull.area(), scale_(5.0)); // assume 5 mm^2 area for support points and other degenerate bases + double sticking_force = base_area + * (acc.base_height == 0 ? scale_(params.base_adhesion) : scale_(params.support_adhesion)); + + std::cout << "stcking force: " << sticking_force << std::endl; + if (inside) { - double toppling_force = (Vec2d(sqrt(distance_sq), acc.base_height).norm() - acc.base_height) * acc.accumulated_volume; + double toppling_force = (Vec2d(sqrt(distance_sq), centroid.z() - acc.base_height).norm() + - centroid.z()) + * acc.accumulated_volume; sticking_force += toppling_force; + std::cout << "toppling force: " << toppling_force << std::endl; } - double y_movement_force = 0.5f * acc.accumulated_volume * params.top_object_movement_speed - * params.top_object_movement_speed; + + double y_movement_force = 0.5f * acc.accumulated_volume * scale_(params.top_object_movement_speed) + * scale_(params.top_object_movement_speed); + + std::cout << "y_movement_force: " << y_movement_force << std::endl; + if (sticking_force < y_movement_force) { additional_supports_needed = true; } if (!inside) { - double torque = sqrt(distance_sq) * acc.accumulated_volume; + double torque = sqrt(distance_sq) * scale_(scale_(scale_(acc.accumulated_volume))); // if sticking force is computed with scaled adhesion, than torque needs scaled volume as well + + std::cout << "torque: " << torque << std::endl; + if (torque > sticking_force) { //comparing apples and oranges; but we are far beyond physical simulation additional_supports_needed = true; } } + std::cout << "additional supports needed: " << additional_supports_needed << std::endl; + if (additional_supports_needed) { - Vec2crd attractor_dir = inside ? pivot - centroid_base_projection : centroid_base_projection - pivot; - Vec2d attractor = centroid_base_projection.cast() + (1e9 * attractor_dir.cast().normalized()); + Vec2crd attractor_dir = + inside ? pivot - centroid_base_projection : centroid_base_projection - pivot; + Vec2d attractor = centroid_base_projection.cast() + + (1e9 * attractor_dir.cast().normalized()); double min_dist = std::numeric_limits::max(); Vec3d support_point = centroid; for (int y = 0; y < global_cell_count.y(); ++y) { for (int x = 0; x < global_cell_count.x(); ++x) { Cell &cell = this->access_cell(Vec3i(x, y, 0)); if (cell.island_id == acc_id) { - Vec3d cell_center = this->get_cell_center(this->to_global_cell_coords(Vec3i(x, y, z))).cast(); + Vec3d cell_center = + this->get_cell_center(this->to_global_cell_coords(Vec3i(x, y, z))).cast< + double>(); double dist_sq = (cell_center.head<2>() - attractor).squaredNorm(); if (dist_sq < min_dist) { min_dist = dist_sq; @@ -406,7 +454,7 @@ struct BalanceDistributionGrid { } } - issues.supports_nedded.emplace_back(support_point.cast()); + issues.supports_nedded.emplace_back(unscale(support_point).cast()); acc.points.push_back(Point(support_point.head<2>().cast())); acc.calculate_base_hull(); } @@ -416,41 +464,65 @@ struct BalanceDistributionGrid { } #ifdef DEBUG_FILES - void debug_export(std::string file_name) const { + void debug_export() const { Slic3r::CNumericLocalesSetter locales_setter; { - FILE *fp = boost::nowide::fopen(debug_out_path((file_name + "_grid.obj").c_str()).c_str(), "w"); - if (fp == nullptr) { + FILE *volume_grid_file = boost::nowide::fopen(debug_out_path("volume_grid.obj").c_str(), "w"); + FILE *islands_grid_file = boost::nowide::fopen(debug_out_path("islands_grid.obj").c_str(), "w"); + FILE *curling_grid_file = boost::nowide::fopen(debug_out_path("curling_grid.obj").c_str(), "w"); + + if (volume_grid_file == nullptr || islands_grid_file == nullptr || curling_grid_file == nullptr) { BOOST_LOG_TRIVIAL(error) - << "Debug files: Couldn't open " << file_name << " for writing"; + << "Debug files: Couldn't open debug file for writing, destination: " << debug_out_path(""); return; } float max_volume = 0; + int min_island_id = 0; + int max_island_id = 0; + float max_curling_height = 0; + for (int x = 0; x < global_cell_count.x(); ++x) { for (int y = 0; y < global_cell_count.y(); ++y) { for (int z = 0; z < local_z_cell_count; ++z) { const Cell &cell = access_cell(Vec3i(x, y, z)); max_volume = std::max(max_volume, cell.volume); + if (cell.island_id != std::numeric_limits::max()) { + min_island_id = std::min(min_island_id, cell.island_id); + max_island_id = std::max(max_island_id, cell.island_id); + } + max_curling_height = std::max(max_curling_height, cell.curled_height); } } } - max_volume *= 0.8; - for (int x = 0; x < global_cell_count.x(); ++x) { for (int y = 0; y < global_cell_count.y(); ++y) { for (int z = 0; z < local_z_cell_count; ++z) { Vec3f center = unscale(get_cell_center(to_global_cell_coords(Vec3i { x, y, z }))).cast(); const Cell &cell = access_cell(Vec3i(x, y, z)); if (cell.volume != 0) { - fprintf(fp, "v %f %f %f %f %f %f\n", center(0), center(1), center(2), cell.volume / max_volume, 0.0, 0.0); + auto volume_color = value_to_rgbf(0, cell.volume, cell.volume); + fprintf(volume_grid_file, "v %f %f %f %f %f %f\n", center(0), center(1), center(2), + volume_color.x(), volume_color.y(), volume_color.z()); + } + if (cell.island_id != std::numeric_limits::max()) { + auto island_color = value_to_rgbf(min_island_id, max_island_id + 1, cell.island_id); + fprintf(islands_grid_file, "v %f %f %f %f %f %f\n", center(0), center(1), center(2), + island_color.x(), island_color.y(), island_color.z()); + } + if (cell.curled_height > 0) { + auto curling_color = value_to_rgbf(0, max_curling_height, cell.curled_height); + fprintf(curling_grid_file, "v %f %f %f %f %f %f\n", center(0), center(1), center(2), + curling_color.x(), curling_color.y(), curling_color.z()); } } } } - fclose(fp); + fclose(volume_grid_file); + fclose(islands_grid_file); + fclose(curling_grid_file); } } #endif @@ -485,7 +557,8 @@ void debug_export(Issues issues, std::string file_name) { } for (size_t i = 0; i < issues.supports_nedded.size(); ++i) { - fprintf(fp, "v %f %f %f %f %f %f\n", issues.supports_nedded[i].position(0), issues.supports_nedded[i].position(1), + fprintf(fp, "v %f %f %f %f %f %f\n", issues.supports_nedded[i].position(0), + issues.supports_nedded[i].position(1), issues.supports_nedded[i].position(2), 1.0, 0.0, 0.0); } @@ -500,7 +573,8 @@ void debug_export(Issues issues, std::string file_name) { } for (size_t i = 0; i < issues.curling_up.size(); ++i) { - fprintf(fp, "v %f %f %f %f %f %f\n", issues.curling_up[i].position(0), issues.curling_up[i].position(1), + fprintf(fp, "v %f %f %f %f %f %f\n", issues.curling_up[i].position(0), + issues.curling_up[i].position(1), issues.curling_up[i].position(2), 0.0, 1.0, 0.0); } fclose(fp); @@ -512,7 +586,8 @@ void debug_export(Issues issues, std::string file_name) { EdgeGridWrapper compute_layer_edge_grid(const Layer *layer) { float min_region_flow_width { 1.0f }; for (const auto *region : layer->regions()) { - min_region_flow_width = std::min(min_region_flow_width, region->flow(FlowRole::frExternalPerimeter).width()); + min_region_flow_width = std::min(min_region_flow_width, + region->flow(FlowRole::frExternalPerimeter).width()); } std::vector lines; for (const LayerRegion *layer_region : layer->regions()) { @@ -533,23 +608,29 @@ EdgeGridWrapper compute_layer_edge_grid(const Layer *layer) { //TODO needs revision coordf_t get_flow_width(const LayerRegion *region, ExtrusionRole role) { switch (role) { - case ExtrusionRole::erBridgeInfill: - return region->flow(FlowRole::frExternalPerimeter).scaled_width(); - case ExtrusionRole::erExternalPerimeter: - return region->flow(FlowRole::frExternalPerimeter).scaled_width(); - case ExtrusionRole::erGapFill: - return region->flow(FlowRole::frInfill).scaled_width(); - case ExtrusionRole::erPerimeter: - return region->flow(FlowRole::frPerimeter).scaled_width(); - case ExtrusionRole::erSolidInfill: - return region->flow(FlowRole::frSolidInfill).scaled_width(); - default: - return region->flow(FlowRole::frPerimeter).scaled_width(); + case ExtrusionRole::erBridgeInfill: + return region->flow(FlowRole::frExternalPerimeter).scaled_width(); + case ExtrusionRole::erExternalPerimeter: + return region->flow(FlowRole::frExternalPerimeter).scaled_width(); + case ExtrusionRole::erGapFill: + return region->flow(FlowRole::frInfill).scaled_width(); + case ExtrusionRole::erPerimeter: + return region->flow(FlowRole::frPerimeter).scaled_width(); + case ExtrusionRole::erSolidInfill: + return region->flow(FlowRole::frSolidInfill).scaled_width(); + case ExtrusionRole::erInternalInfill: + return region->flow(FlowRole::frInfill).scaled_width(); + case ExtrusionRole::erTopSolidInfill: + return region->flow(FlowRole::frTopSolidInfill).scaled_width(); + default: + return region->flow(FlowRole::frPerimeter).scaled_width(); } } -coordf_t get_max_allowed_distance(ExtrusionRole role, coordf_t flow_width, bool external_perimeters_first, const Params ¶ms) { // <= distance / flow_width (can be larger for perimeter, if not external perimeter first) - if ((role == ExtrusionRole::erExternalPerimeter || role == ExtrusionRole::erOverhangPerimeter) && (external_perimeters_first)) { +coordf_t get_max_allowed_distance(ExtrusionRole role, coordf_t flow_width, bool external_perimeters_first, + const Params ¶ms) { // <= distance / flow_width (can be larger for perimeter, if not external perimeter first) + if ((role == ExtrusionRole::erExternalPerimeter || role == ExtrusionRole::erOverhangPerimeter) + && (external_perimeters_first)) { return params.max_first_ex_perim_unsupported_distance_factor * flow_width; } else { return params.max_unsupported_distance_factor * flow_width; @@ -579,12 +660,13 @@ struct SegmentAccumulator { }; Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, float print_z, const LayerRegion *layer_region, - const EdgeGridWrapper &supported_grid, BalanceDistributionGrid &balance_grid, const Params ¶ms) { + const EdgeGridWrapper &supported_grid, const Params ¶ms) { Issues issues { }; if (entity->is_collection()) { for (const auto *e : static_cast(entity)->entities) { - issues.add(check_extrusion_entity_stability(e, print_z, layer_region, supported_grid, balance_grid, params)); + issues.add( + check_extrusion_entity_stability(e, print_z, layer_region, supported_grid, params)); } } else { //single extrusion path, with possible varying parameters //prepare stack of points on the extrusion path. If there are long segments, additional points might be pushed onto the stack during the algorithm. @@ -606,10 +688,9 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, float pri Point prev_point = points.top(); // prev point of the path. Initialize with first point. Vec3f prev_fpoint = to_vec3f(prev_point); coordf_t flow_width = get_flow_width(layer_region, entity->role()); - coordf_t layer_height = layer_region->layer()->height; bool external_perimters_first = layer_region->region().config().external_perimeters_first; - const coordf_t max_allowed_dist_from_prev_layer = get_max_allowed_distance(entity->role(), flow_width, external_perimters_first, - params); + const coordf_t max_allowed_dist_from_prev_layer = get_max_allowed_distance(entity->role(), flow_width, + external_perimters_first, params); while (!points.empty()) { Point point = points.top(); @@ -617,8 +698,6 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, float pri Vec3f fpoint = to_vec3f(point); float edge_len = (fpoint - prev_fpoint).norm(); - balance_grid.distribute_edge(prev_point, point, print_z, unscale(flow_width), unscale(layer_height)); - coordf_t dist_from_prev_layer { 0 }; if (!supported_grid.signed_distance(point, flow_width, dist_from_prev_layer)) { // dist from prev layer not found, assume empty layer issues.supports_nedded.push_back(SupportPoint(fpoint)); @@ -641,8 +720,10 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, float pri supports_acc.add_distance(edge_len); // for algorithm simplicity, expect that the whole line between prev and current point is unsupported if (supports_acc.distance // if unsupported distance is larger than bridge distance linearly decreased by curvature, enforce supports. - > params.bridge_distance - / (1.0f + (supports_acc.max_curvature * params.bridge_distance_decrease_by_curvature_factor / PI))) { + > params.bridge_distance + / (1.0f + + (supports_acc.max_curvature + * params.bridge_distance_decrease_by_curvature_factor / PI))) { issues.supports_nedded.push_back(SupportPoint(fpoint)); supports_acc.reset(); } @@ -681,32 +762,49 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, float pri return issues; } -Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_check, BalanceDistributionGrid &balance_grid, - const Params ¶ms) { - std::cout << "Checking: " << layer_idx << std::endl; - if (layer_idx == 0) { - // first layer is usually ok - return {}; +void distribute_layer_volume(const PrintObject *po, size_t layer_idx, BalanceDistributionGrid &balance_grid) { + const Layer *layer = po->get_layer(layer_idx); + for (const LayerRegion *region : layer->regions()) { + for (const ExtrusionEntity *collections : region->fills.entities) { + for (const ExtrusionEntity *entity : static_cast(collections)->entities) { + for (const Line &line : entity->as_polyline().lines()) { + balance_grid.distribute_edge(line.a, line.b, layer->print_z, + unscale(get_flow_width(region, entity->role())), layer->height); + } + } + } + for (const ExtrusionEntity *collections : region->perimeters.entities) { + for (const ExtrusionEntity *entity : static_cast(collections)->entities) { + for (const Line &line : entity->as_polyline().lines()) { + balance_grid.distribute_edge(line.a, line.b, layer->print_z, + unscale(get_flow_width(region, entity->role())), layer->height); + } + } + } } +} + +Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_check, const Params ¶ms) { const Layer *layer = po->get_layer(layer_idx); //Prepare edge grid of previous layer, will be used to check if the extrusion path is supported EdgeGridWrapper supported_grid = compute_layer_edge_grid(layer->lower_layer); Issues issues { }; - if (full_check) { // If full checkm check stability of perimeters, gap fills, and bridges. + if (full_check) { // If full check; check stability of perimeters, gap fills, and bridges. for (const LayerRegion *layer_region : layer->regions()) { for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { issues.add( - check_extrusion_entity_stability(perimeter, layer->print_z, layer_region, supported_grid, balance_grid, - params)); + check_extrusion_entity_stability(perimeter, layer->print_z, layer_region, + supported_grid, params)); } // perimeter } // ex_entity for (const ExtrusionEntity *ex_entity : layer_region->fills.entities) { for (const ExtrusionEntity *fill : static_cast(ex_entity)->entities) { if (fill->role() == ExtrusionRole::erGapFill || fill->role() == ExtrusionRole::erBridgeInfill) { issues.add( - check_extrusion_entity_stability(fill, layer->print_z, layer_region, supported_grid, balance_grid, params)); + check_extrusion_entity_stability(fill, layer->print_z, layer_region, supported_grid, + params)); } } // fill } // ex_entity @@ -719,8 +817,8 @@ Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_ if (perimeter->role() == ExtrusionRole::erExternalPerimeter || perimeter->role() == ExtrusionRole::erOverhangPerimeter) { issues.add( - check_extrusion_entity_stability(perimeter, layer->print_z, layer_region, supported_grid, balance_grid, - params)); + check_extrusion_entity_stability(perimeter, layer->print_z, layer_region, + supported_grid, params)); }; // ex_perimeter } // perimeter } // ex_entity @@ -737,6 +835,7 @@ std::vector quick_search(const PrintObject *po, const Params ¶ms) { BalanceDistributionGrid grid { }; grid.init(po, 0, po->layers().size()); + distribute_layer_volume(po, 0, grid); std::mutex grid_mutex; size_t layer_count = po->layer_count(); @@ -746,7 +845,8 @@ std::vector quick_search(const PrintObject *po, const Params ¶ms) { balance_grid.init(po, r.begin(), r.end()); for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) { - auto layer_issues = check_layer_stability(po, layer_idx, false, balance_grid, params); + distribute_layer_volume(po, layer_idx, balance_grid); + auto layer_issues = check_layer_stability(po, layer_idx, false, params); if (!layer_issues.supports_nedded.empty()) { layer_needs_supports[layer_idx] = true; } @@ -771,6 +871,7 @@ Issues full_search(const PrintObject *po, const Params ¶ms) { BalanceDistributionGrid grid { }; grid.init(po, 0, po->layers().size()); + distribute_layer_volume(po, 0, grid); std::mutex grid_mutex; size_t layer_count = po->layer_count(); @@ -780,7 +881,8 @@ Issues full_search(const PrintObject *po, const Params ¶ms) { balance_grid.init(po, r.begin(), r.end()); Issues issues = init; for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) { - auto layer_issues = check_layer_stability(po, layer_idx, true, balance_grid, params); + distribute_layer_volume(po, layer_idx, balance_grid); + auto layer_issues = check_layer_stability(po, layer_idx, true, params); if (!layer_issues.empty()) { issues.add(layer_issues); } @@ -800,9 +902,8 @@ Issues full_search(const PrintObject *po, const Params ¶ms) { grid.analyze(found_issues, params); - grid.debug_export("volume"); - #ifdef DEBUG_FILES + grid.debug_export(); Impl::debug_export(found_issues, "issues"); #endif From 6f6a0e7efdebba4beb9323ac300f50d19d07f599 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Fri, 29 Apr 2022 17:19:26 +0200 Subject: [PATCH 15/78] Another bulk of bug fixes. Some problems however persist, support points are still placed on weird spots --- src/libslic3r/SupportableIssuesSearch.cpp | 133 +++++++++++----------- src/libslic3r/SupportableIssuesSearch.hpp | 7 +- 2 files changed, 74 insertions(+), 66 deletions(-) diff --git a/src/libslic3r/SupportableIssuesSearch.cpp b/src/libslic3r/SupportableIssuesSearch.cpp index f2612035d9..72ee7a1176 100644 --- a/src/libslic3r/SupportableIssuesSearch.cpp +++ b/src/libslic3r/SupportableIssuesSearch.cpp @@ -58,11 +58,12 @@ struct Cell { struct CentroidAccumulator { Polygon convex_hull { }; Points points { }; - Vec3d accumulated_value = Vec3d::Zero(); + Vec3f accumulated_value = Vec3f::Zero(); float accumulated_volume { }; - const double base_height { }; + float base_area { }; + const float base_height { }; - explicit CentroidAccumulator(double base_height) : + explicit CentroidAccumulator(float base_height) : base_height(base_height) { } @@ -80,7 +81,7 @@ struct CentroidAccumulators { acccumulators.reserve(reserve_count); } - CentroidAccumulator& create_accumulator(int id, double base_height) { + CentroidAccumulator& create_accumulator(int id, float base_height) { mapping[id] = acccumulators.size(); acccumulators.push_back(CentroidAccumulator { base_height }); return this->access(id); @@ -100,6 +101,7 @@ struct CentroidAccumulators { to_acc.accumulated_volume += from_acc.accumulated_volume; to_acc.points.insert(to_acc.points.end(), from_acc.points.begin(), from_acc.points.end()); to_acc.calculate_base_hull(); + to_acc.base_area += from_acc.base_area; mapping[from_id] = mapping[to_id]; } }; @@ -265,7 +267,7 @@ struct BalanceDistributionGrid { Vec3crd cell_center = this->get_cell_center( Vec3i(current_coords.x(), current_coords.y(), local_z_index_offset)); acc.points.push_back(Point(cell_center.head<2>())); - acc.accumulated_value += cell_center.cast() * cell.volume; + acc.accumulated_value += unscale(cell_center).cast() * cell.volume; acc.accumulated_volume += cell.volume; for (int y_offset = -1; y_offset <= 1; ++y_offset) { @@ -279,6 +281,7 @@ struct BalanceDistributionGrid { } next_island_id--; acc.calculate_base_hull(); + acc.base_area = unscale(unscale(acc.convex_hull.area())); //apply unscale 2x, it has units of area } } } @@ -295,6 +298,7 @@ struct BalanceDistributionGrid { issues.supports_nedded[index].position.z()); acc.points.push_back(Point(scaled(Vec2f(issues.supports_nedded[index].position.head<2>())))); acc.calculate_base_hull(); + acc.base_area = params.support_points_inteface_area; } for (const CurledFilament &curling : issues.curling_up) { @@ -343,9 +347,10 @@ struct BalanceDistributionGrid { accumulators.merge_to(id, min_island_id_found); } - CentroidAccumulator &acc = accumulators.access(current.island_id); + CentroidAccumulator &acc = accumulators.access(min_island_id_found); acc.accumulated_value += current.volume - * this->get_cell_center(this->to_global_cell_coords(Vec3i(x, y, z))).cast(); + * unscale(this->get_cell_center(this->to_global_cell_coords(Vec3i(x, y, z)))).cast< + float>(); acc.accumulated_volume += current.volume; modified_acc_ids.insert(min_island_id_found); } @@ -353,109 +358,107 @@ struct BalanceDistributionGrid { } } - // check stability of modified centroid accumulators. - // Stability is the amount of work needed to push the object from stable position into unstable. - // This amount of work is proportional to the increase of height of the centroid during toppling. - // image here: https://hgphysics.com/gph/c-forces/2-force-effects/1-moment/stability/ - // better image in Czech here in the first question: https://www.priklady.eu/cs/fyzika/mechanika-tuheho-telesa/stabilita-teles.alej for (int acc_id : modified_acc_ids) { - std::cout << "controlling acc id: " << acc_id << std::endl; + std::cout << "Z: " << z << " controlling acc id: " << acc_id << std::endl; CentroidAccumulator &acc = accumulators.access(acc_id); - Vec3d centroid = acc.accumulated_value / acc.accumulated_volume; + Vec3f centroid = acc.accumulated_value / acc.accumulated_volume; std::cout << "acc.accumulated_value : " << acc.accumulated_value.x() << " " << acc.accumulated_value.y() << " " << acc.accumulated_value.z() << std::endl; std::cout << "acc.accumulated_volume : " << acc.accumulated_volume << std::endl; - std::cout << "centorid: " << centroid.x() << " " << centroid.y() << " " << centroid.z() << std::endl; + std::cout << "centroid: " << centroid.x() << " " << centroid.y() << " " << centroid.z() << std::endl; //determine signed shortest distance to the convex hull - Point centroid_base_projection = Point(centroid.head<2>().cast()); + Point centroid_base_projection = Point(scaled(Vec2f(centroid.head<2>()))); + Point pivot; - double distance_sq = std::numeric_limits::max(); + double distance_scaled_sq = std::numeric_limits::max(); bool inside = true; if (acc.convex_hull.points.size() == 1) { pivot = acc.convex_hull.points[0]; - distance_sq = (pivot - centroid_base_projection).squaredNorm(); + distance_scaled_sq = (pivot - centroid_base_projection).squaredNorm(); inside = true; } else { for (Line line : acc.convex_hull.lines()) { Point closest_point; double dist_sq = line.distance_to_squared(centroid_base_projection, &closest_point); - if (dist_sq < distance_sq) { + if (dist_sq < distance_scaled_sq) { pivot = closest_point; - distance_sq = dist_sq; + distance_scaled_sq = dist_sq; } - if (float(angle(line.b - line.a, centroid_base_projection - line.b)) < 0) { + if ((centroid_base_projection - closest_point).cast().dot(line.normal().cast()) + > 0) { inside = false; } } } - std::cout << "centoroid inside ? " << inside << " and distance is: " << distance_sq << std::endl; + float centroid_pivot_distance = unscaled(sqrt(distance_scaled_sq)); + float base_center_pivot_distance = float(unscale(Vec2crd(acc.convex_hull.centroid() - pivot)).norm()); + + std::cout << "centroid inside ? " << inside << " and distance is: " << centroid_pivot_distance + << std::endl; bool additional_supports_needed = false; - double base_area = std::max(acc.convex_hull.area(), scale_(5.0)); // assume 5 mm^2 area for support points and other degenerate bases - double sticking_force = base_area - * (acc.base_height == 0 ? scale_(params.base_adhesion) : scale_(params.support_adhesion)); + float sticking_force = acc.base_area + * (acc.base_height == 0 ? params.base_adhesion : params.support_adhesion); + float sticking_torque = base_center_pivot_distance * sticking_force; - std::cout << "stcking force: " << sticking_force << std::endl; + std::cout << "sticking force: " << sticking_force << " sticking torque: " << sticking_torque + << std::endl; - if (inside) { - double toppling_force = (Vec2d(sqrt(distance_sq), centroid.z() - acc.base_height).norm() - - centroid.z()) - * acc.accumulated_volume; - sticking_force += toppling_force; - std::cout << "toppling force: " << toppling_force << std::endl; - } + float xy_movement_force = acc.accumulated_volume * params.filament_density * params.max_acceleration; + float xy_movement_torque = xy_movement_force * centroid_pivot_distance; - double y_movement_force = 0.5f * acc.accumulated_volume * scale_(params.top_object_movement_speed) - * scale_(params.top_object_movement_speed); - - std::cout << "y_movement_force: " << y_movement_force << std::endl; - - if (sticking_force < y_movement_force) { - additional_supports_needed = true; - } + std::cout << "xy_movement_force: " << xy_movement_force << " xy_movement_torque: " + << xy_movement_torque << std::endl; + float weight_torque = 0; if (!inside) { - double torque = sqrt(distance_sq) * scale_(scale_(scale_(acc.accumulated_volume))); // if sticking force is computed with scaled adhesion, than torque needs scaled volume as well + float weight = acc.accumulated_volume * params.filament_density * 10000.0f; + weight_torque = centroid_pivot_distance * weight; - std::cout << "torque: " << torque << std::endl; - - if (torque > sticking_force) { //comparing apples and oranges; but we are far beyond physical simulation - additional_supports_needed = true; - } + std::cout << "weight: " << weight << " weight_torque: " << weight_torque << std::endl; } + float total_momentum = sticking_torque - xy_movement_torque - weight_torque; + additional_supports_needed = total_momentum < 0; + + std::cout << "total_momentum: " << total_momentum << std::endl; std::cout << "additional supports needed: " << additional_supports_needed << std::endl; if (additional_supports_needed) { - Vec2crd attractor_dir = - inside ? pivot - centroid_base_projection : centroid_base_projection - pivot; - Vec2d attractor = centroid_base_projection.cast() - + (1e9 * attractor_dir.cast().normalized()); + Vec2f attractor_dir = + unscale(Vec2crd(inside ? + pivot - centroid_base_projection : + centroid_base_projection - pivot)).cast().normalized(); + Vec2f attractor = unscale(centroid_base_projection).cast() + 10000 * attractor_dir; + + std::cout << " attractor: " << attractor.x() << " | " << attractor.y() << std::endl; + double min_dist = std::numeric_limits::max(); - Vec3d support_point = centroid; + Vec3f support_point = centroid; for (int y = 0; y < global_cell_count.y(); ++y) { for (int x = 0; x < global_cell_count.x(); ++x) { - Cell &cell = this->access_cell(Vec3i(x, y, 0)); - if (cell.island_id == acc_id) { - Vec3d cell_center = - this->get_cell_center(this->to_global_cell_coords(Vec3i(x, y, z))).cast< - double>(); - double dist_sq = (cell_center.head<2>() - attractor).squaredNorm(); + Cell &cell = this->access_cell(Vec3i(x, y, z)); + if (&accumulators.access(cell.island_id) == &acc) { + Vec3f cell_center = + unscale(this->get_cell_center(this->to_global_cell_coords(Vec3i(x, y, z)))).cast< + float>(); + float dist_sq = (cell_center.head<2>() - attractor).squaredNorm(); if (dist_sq < min_dist) { min_dist = dist_sq; support_point = cell_center; + std::cout << " new support point: " << support_point.x() << " | " << support_point.y() << " | " << support_point.z() << std::endl; } } } } - issues.supports_nedded.emplace_back(unscale(support_point).cast()); - acc.points.push_back(Point(support_point.head<2>().cast())); + issues.supports_nedded.emplace_back(support_point); + acc.points.push_back(Point::new_scale(Vec2f(support_point.head<2>()))); acc.calculate_base_hull(); } @@ -559,7 +562,7 @@ void debug_export(Issues issues, std::string file_name) { for (size_t i = 0; i < issues.supports_nedded.size(); ++i) { fprintf(fp, "v %f %f %f %f %f %f\n", issues.supports_nedded[i].position(0), issues.supports_nedded[i].position(1), - issues.supports_nedded[i].position(2), 1.0, 0.0, 0.0); + issues.supports_nedded[i].position(2), 1.0, 0.0, 1.0); } fclose(fp); @@ -721,9 +724,8 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, float pri if (supports_acc.distance // if unsupported distance is larger than bridge distance linearly decreased by curvature, enforce supports. > params.bridge_distance - / (1.0f - + (supports_acc.max_curvature - * params.bridge_distance_decrease_by_curvature_factor / PI))) { + / (1.0f + (supports_acc.max_curvature + * params.bridge_distance_decrease_by_curvature_factor / PI))) { issues.supports_nedded.push_back(SupportPoint(fpoint)); supports_acc.reset(); } @@ -899,6 +901,9 @@ Issues full_search(const PrintObject *po, const Params ¶ms) { return left; } ); +#ifdef DEBUG_FILES + Impl::debug_export(found_issues, "pre_issues"); +#endif grid.analyze(found_issues, params); diff --git a/src/libslic3r/SupportableIssuesSearch.hpp b/src/libslic3r/SupportableIssuesSearch.hpp index 867ccd21b5..f16d454e0f 100644 --- a/src/libslic3r/SupportableIssuesSearch.hpp +++ b/src/libslic3r/SupportableIssuesSearch.hpp @@ -15,9 +15,12 @@ struct Params { float max_unsupported_distance_factor = 1.0f; // For internal perimeters, infill, bridges etc, allow gap of [extrusion width] size, these extrusions have usually something to stick to. float bridge_distance_decrease_by_curvature_factor = 5.0f; // allowed bridge distance = bridge_distance / ( 1 + this factor * (curvature / PI) ) - float base_adhesion = 60.0f; // adhesion per mm^2 of first layer; the value should say how much *volume* it can hold per one square millimiter + float base_adhesion = 100.0f; // adhesion per mm^2 of first layer; Force needed to remove the object from the bed, divided by the adhesion area, so in Pascal units N/mm^2 float support_adhesion = 300.0f; // adhesion per mm^2 of support interface layer - float top_object_movement_speed = 200.0f; // movement speed of 200 mm/s in Y + float support_points_inteface_area = 5.0f; // mm^2 + float max_acceleration = 1000.0f; // mm/s^2 ; max acceleration in XY + float filament_density = 1.25f * 0.001f; // g/mm^3 + }; struct SupportPoint { From 91a4047586f0eef30c59c388f2c5b4d86e9b0273 Mon Sep 17 00:00:00 2001 From: Godrak Date: Mon, 2 May 2022 14:48:07 +0200 Subject: [PATCH 16/78] Fixed various problems with support placement. --- src/libslic3r/PrintObject.cpp | 2 +- src/libslic3r/SupportableIssuesSearch.cpp | 53 ++++++++++++++--------- src/libslic3r/SupportableIssuesSearch.hpp | 10 +++-- 3 files changed, 40 insertions(+), 25 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index f8fcc22039..ee311b5610 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -431,7 +431,7 @@ void PrintObject::find_supportable_issues() TriangleSelectorWrapper selector { model_volume->mesh() }; for (const SupportableIssues::SupportPoint &support_point : issues.supports_nedded) { - selector.enforce_spot(Vec3f(inv_transform.cast() * support_point.position), 0.3f); + selector.enforce_spot(Vec3f(inv_transform.cast() * support_point.position), 2.0f); } model_volume->supported_facets.set(selector.selector); diff --git a/src/libslic3r/SupportableIssuesSearch.cpp b/src/libslic3r/SupportableIssuesSearch.cpp index 72ee7a1176..6adf615e80 100644 --- a/src/libslic3r/SupportableIssuesSearch.cpp +++ b/src/libslic3r/SupportableIssuesSearch.cpp @@ -37,10 +37,6 @@ SupportPoint::SupportPoint(const Vec3f &position, float weight) : position(position), weight(weight) { } -SupportPoint::SupportPoint(const Vec3f &position) : - position(position), weight(0.0f) { -} - CurledFilament::CurledFilament(const Vec3f &position, float estimated_height) : position(position), estimated_height(estimated_height) { } @@ -298,7 +294,7 @@ struct BalanceDistributionGrid { issues.supports_nedded[index].position.z()); acc.points.push_back(Point(scaled(Vec2f(issues.supports_nedded[index].position.head<2>())))); acc.calculate_base_hull(); - acc.base_area = params.support_points_inteface_area; + acc.base_area = params.support_points_interface_area; } for (const CurledFilament &curling : issues.curling_up) { @@ -372,7 +368,6 @@ struct BalanceDistributionGrid { //determine signed shortest distance to the convex hull Point centroid_base_projection = Point(scaled(Vec2f(centroid.head<2>()))); - Point pivot; double distance_scaled_sq = std::numeric_limits::max(); bool inside = true; @@ -395,10 +390,13 @@ struct BalanceDistributionGrid { } } - float centroid_pivot_distance = unscaled(sqrt(distance_scaled_sq)); + Vec3f pivot_3d; + pivot_3d << unscale(pivot).cast(), acc.base_height; + float embedded_distance = unscaled(sqrt(distance_scaled_sq)); + float centroid_pivot_distance = (centroid - pivot_3d).norm(); float base_center_pivot_distance = float(unscale(Vec2crd(acc.convex_hull.centroid() - pivot)).norm()); - std::cout << "centroid inside ? " << inside << " and distance is: " << centroid_pivot_distance + std::cout << "centroid inside ? " << inside << " and embedded distance is: " << embedded_distance << std::endl; bool additional_supports_needed = false; @@ -415,15 +413,17 @@ struct BalanceDistributionGrid { std::cout << "xy_movement_force: " << xy_movement_force << " xy_movement_torque: " << xy_movement_torque << std::endl; - float weight_torque = 0; - if (!inside) { - float weight = acc.accumulated_volume * params.filament_density * 10000.0f; - weight_torque = centroid_pivot_distance * weight; - - std::cout << "weight: " << weight << " weight_torque: " << weight_torque << std::endl; + float weight = acc.accumulated_volume * params.filament_density * params.gravity_constant; + float weight_torque = embedded_distance * weight; + if (!inside){ + weight_torque*=-1; } + std::cout << "weight: " << weight << " weight_torque: " << weight_torque << std::endl; - float total_momentum = sticking_torque - xy_movement_torque - weight_torque; + float extruder_conflict_torque = params.tolerable_extruder_conflict_force * 2.0f * centroid_pivot_distance; + std::cout << "extruder_conflict_torque: " << extruder_conflict_torque << std::endl; + + float total_momentum = sticking_torque + weight_torque - xy_movement_torque - extruder_conflict_torque; additional_supports_needed = total_momentum < 0; std::cout << "total_momentum: " << total_momentum << std::endl; @@ -440,10 +440,12 @@ struct BalanceDistributionGrid { double min_dist = std::numeric_limits::max(); Vec3f support_point = centroid; + Vec2i coords = Vec2i(0,0); for (int y = 0; y < global_cell_count.y(); ++y) { for (int x = 0; x < global_cell_count.x(); ++x) { Cell &cell = this->access_cell(Vec3i(x, y, z)); - if (&accumulators.access(cell.island_id) == &acc) { + if (cell.island_id != std::numeric_limits::max() && + &accumulators.access(cell.island_id) == &acc) { Vec3f cell_center = unscale(this->get_cell_center(this->to_global_cell_coords(Vec3i(x, y, z)))).cast< float>(); @@ -451,14 +453,25 @@ struct BalanceDistributionGrid { if (dist_sq < min_dist) { min_dist = dist_sq; support_point = cell_center; - std::cout << " new support point: " << support_point.x() << " | " << support_point.y() << " | " << support_point.z() << std::endl; + coords = Vec2i(x,y); } } } } - issues.supports_nedded.emplace_back(support_point); + int final_height_coords = z; + while (final_height_coords > 0 && this->access_cell(Vec3i(coords.x(), coords.y(), final_height_coords)).volume > 0){ + final_height_coords --; + } + support_point.z() = unscaled((final_height_coords + this->local_z_index_offset) * this->cell_size.z()); + float expected_force = total_momentum / (support_point - pivot_3d).norm(); + + std::cout << " new support point: " << support_point.x() << " | " << support_point.y() << " | " << support_point.z() << std::endl; + std::cout << " expected_force: " << expected_force << std::endl; + + issues.supports_nedded.emplace_back(support_point, expected_force); acc.points.push_back(Point::new_scale(Vec2f(support_point.head<2>()))); + acc.base_area += params.support_points_interface_area; acc.calculate_base_hull(); } @@ -703,7 +716,7 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, float pri coordf_t dist_from_prev_layer { 0 }; if (!supported_grid.signed_distance(point, flow_width, dist_from_prev_layer)) { // dist from prev layer not found, assume empty layer - issues.supports_nedded.push_back(SupportPoint(fpoint)); + issues.supports_nedded.push_back(SupportPoint(fpoint, 1.0f)); supports_acc.reset(); } @@ -726,7 +739,7 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, float pri > params.bridge_distance / (1.0f + (supports_acc.max_curvature * params.bridge_distance_decrease_by_curvature_factor / PI))) { - issues.supports_nedded.push_back(SupportPoint(fpoint)); + issues.supports_nedded.push_back(SupportPoint(fpoint, 1.0f)); supports_acc.reset(); } } else { diff --git a/src/libslic3r/SupportableIssuesSearch.hpp b/src/libslic3r/SupportableIssuesSearch.hpp index f16d454e0f..a34d1577f3 100644 --- a/src/libslic3r/SupportableIssuesSearch.hpp +++ b/src/libslic3r/SupportableIssuesSearch.hpp @@ -8,6 +8,8 @@ namespace Slic3r { namespace SupportableIssues { struct Params { + const float gravity_constant = 9806.65f; // mm/s^2 ; gravity acceleration on Earth's surface, and assuming printer is in upwards position. + float bridge_distance = 10.0f; float limit_curvature = 0.15f; // used to detect curling issues @@ -15,17 +17,17 @@ struct Params { float max_unsupported_distance_factor = 1.0f; // For internal perimeters, infill, bridges etc, allow gap of [extrusion width] size, these extrusions have usually something to stick to. float bridge_distance_decrease_by_curvature_factor = 5.0f; // allowed bridge distance = bridge_distance / ( 1 + this factor * (curvature / PI) ) - float base_adhesion = 100.0f; // adhesion per mm^2 of first layer; Force needed to remove the object from the bed, divided by the adhesion area, so in Pascal units N/mm^2 - float support_adhesion = 300.0f; // adhesion per mm^2 of support interface layer - float support_points_inteface_area = 5.0f; // mm^2 + float base_adhesion = 2000.0f; // adhesion per mm^2 of first layer; Force needed to remove the object from the bed, divided by the adhesion area (g/mm*s^2) + float support_adhesion = 1000.0f; // adhesion per mm^2 of support interface layer + float support_points_interface_area = 5.0f; // mm^2 float max_acceleration = 1000.0f; // mm/s^2 ; max acceleration in XY float filament_density = 1.25f * 0.001f; // g/mm^3 + float tolerable_extruder_conflict_force = 50.0f * gravity_constant; // force that can occasionally push the model due to various factors (filament leaks, small curling, ... ); current value corresponds to weight of 200g }; struct SupportPoint { SupportPoint(const Vec3f &position, float weight); - explicit SupportPoint(const Vec3f &position); Vec3f position; float weight; }; From 9b290bd21184391f09ddefdaaad05ee06fc6ac68 Mon Sep 17 00:00:00 2001 From: Godrak Date: Mon, 2 May 2022 16:47:19 +0200 Subject: [PATCH 17/78] debug info, problem with random freezing, also support point downward projection still has issues --- src/libslic3r/SupportableIssuesSearch.cpp | 13 ++++++++++++- src/libslic3r/SupportableIssuesSearch.hpp | 9 +++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/SupportableIssuesSearch.cpp b/src/libslic3r/SupportableIssuesSearch.cpp index 6adf615e80..21dbb0e1fb 100644 --- a/src/libslic3r/SupportableIssuesSearch.cpp +++ b/src/libslic3r/SupportableIssuesSearch.cpp @@ -304,15 +304,20 @@ struct BalanceDistributionGrid { std::unordered_set modified_acc_ids; modified_acc_ids.reserve(issues.supports_nedded.size() + 1); for (int z = 1; z < local_z_cell_count; ++z) { + std::cout << "current z: " << z << std::endl; + modified_acc_ids.clear(); for (int x = 0; x < global_cell_count.x(); ++x) { for (int y = 0; y < global_cell_count.y(); ++y) { Cell ¤t = this->access_cell(Vec3i(x, y, z)); + + // distribute curling + std::cout << "distribute curling " << std::endl; if (current.volume > 0) { for (int y_offset = -1; y_offset <= 1; ++y_offset) { for (int x_offset = -1; x_offset <= 1; ++x_offset) { - if (validate_xy_coords(Vec2i(x_offset, y_offset))) { + if (validate_xy_coords(Vec2i(x+x_offset,y+ y_offset))) { Cell &under = this->access_cell(Vec3i(x + x_offset, y + y_offset, z - 1)); current.curled_height += under.curled_height / (2 + std::abs(x_offset) + std::abs(y_offset)); @@ -321,6 +326,8 @@ struct BalanceDistributionGrid { } } + // distribute islands info + std::cout << "distribute islands info " << std::endl; if (current.volume > 0 && current.island_id == std::numeric_limits::max()) { int min_island_id_found = std::numeric_limits::max(); std::unordered_set ids_to_merge { }; @@ -335,6 +342,8 @@ struct BalanceDistributionGrid { } } } + // assign island and update its info + std::cout << "assign island and update its info " << std::endl; if (min_island_id_found < std::numeric_limits::max()) { ids_to_merge.erase(std::numeric_limits::max()); ids_to_merge.erase(min_island_id_found); @@ -354,6 +363,8 @@ struct BalanceDistributionGrid { } } + std::cout << " check all active accumulators " << std::endl; + for (int acc_id : modified_acc_ids) { std::cout << "Z: " << z << " controlling acc id: " << acc_id << std::endl; diff --git a/src/libslic3r/SupportableIssuesSearch.hpp b/src/libslic3r/SupportableIssuesSearch.hpp index a34d1577f3..cdaa55ed5f 100644 --- a/src/libslic3r/SupportableIssuesSearch.hpp +++ b/src/libslic3r/SupportableIssuesSearch.hpp @@ -8,9 +8,9 @@ namespace Slic3r { namespace SupportableIssues { struct Params { - const float gravity_constant = 9806.65f; // mm/s^2 ; gravity acceleration on Earth's surface, and assuming printer is in upwards position. + const float gravity_constant = 9806.65f; // mm/s^2; gravity acceleration on Earth's surface, algorithm assumes that printer is in upwards position. - float bridge_distance = 10.0f; + float bridge_distance = 10.0f; //mm float limit_curvature = 0.15f; // used to detect curling issues float max_first_ex_perim_unsupported_distance_factor = 0.0f; // if external perim first, return tighter max allowed distance from previous layer extrusion @@ -20,10 +20,11 @@ struct Params { float base_adhesion = 2000.0f; // adhesion per mm^2 of first layer; Force needed to remove the object from the bed, divided by the adhesion area (g/mm*s^2) float support_adhesion = 1000.0f; // adhesion per mm^2 of support interface layer float support_points_interface_area = 5.0f; // mm^2 - float max_acceleration = 1000.0f; // mm/s^2 ; max acceleration in XY - float filament_density = 1.25f * 0.001f; // g/mm^3 + float max_acceleration = 1000.0f; // mm/s^2 ; max acceleration of object (bed) in XY + float filament_density = 1.25f * 0.001f; // g/mm^3 ; Common filaments are very lightweight, so precise number is not that important float tolerable_extruder_conflict_force = 50.0f * gravity_constant; // force that can occasionally push the model due to various factors (filament leaks, small curling, ... ); current value corresponds to weight of 200g + }; struct SupportPoint { From 49e6d15a67459cb089fc2209b27a6ad67881564c Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Tue, 3 May 2022 17:26:41 +0200 Subject: [PATCH 18/78] vastly improved curling detection, 3d histogram of curled height now corresponds with real prints --- src/libslic3r/SupportableIssuesSearch.cpp | 71 ++++++++++++----------- src/libslic3r/SupportableIssuesSearch.hpp | 3 +- 2 files changed, 39 insertions(+), 35 deletions(-) diff --git a/src/libslic3r/SupportableIssuesSearch.cpp b/src/libslic3r/SupportableIssuesSearch.cpp index 21dbb0e1fb..4496e9a25f 100644 --- a/src/libslic3r/SupportableIssuesSearch.cpp +++ b/src/libslic3r/SupportableIssuesSearch.cpp @@ -57,6 +57,7 @@ struct CentroidAccumulator { Vec3f accumulated_value = Vec3f::Zero(); float accumulated_volume { }; float base_area { }; + float additional_supports_adhesion { }; const float base_height { }; explicit CentroidAccumulator(float base_height) : @@ -304,7 +305,7 @@ struct BalanceDistributionGrid { std::unordered_set modified_acc_ids; modified_acc_ids.reserve(issues.supports_nedded.size() + 1); for (int z = 1; z < local_z_cell_count; ++z) { - std::cout << "current z: " << z << std::endl; + std::cout << "current z: " << z << std::endl; modified_acc_ids.clear(); @@ -313,21 +314,24 @@ struct BalanceDistributionGrid { Cell ¤t = this->access_cell(Vec3i(x, y, z)); // distribute curling - std::cout << "distribute curling " << std::endl; if (current.volume > 0) { - for (int y_offset = -1; y_offset <= 1; ++y_offset) { - for (int x_offset = -1; x_offset <= 1; ++x_offset) { - if (validate_xy_coords(Vec2i(x+x_offset,y+ y_offset))) { + float curled_height = 0; + for (int y_offset = -2; y_offset <= 2; ++y_offset) { + for (int x_offset = -2; x_offset <= 2; ++x_offset) { + if (validate_xy_coords(Vec2i(x + x_offset, y + y_offset))) { Cell &under = this->access_cell(Vec3i(x + x_offset, y + y_offset, z - 1)); - current.curled_height += under.curled_height - / (2 + std::abs(x_offset) + std::abs(y_offset)); + curled_height = std::max(curled_height, under.curled_height); } } } + bool curled = current.curled_height > 0; + current.curled_height += std::max(0.0f, float(curled_height - unscaled(this->cell_size.z()))); + if (!curled) { + current.curled_height /= 4.0f; + } } // distribute islands info - std::cout << "distribute islands info " << std::endl; if (current.volume > 0 && current.island_id == std::numeric_limits::max()) { int min_island_id_found = std::numeric_limits::max(); std::unordered_set ids_to_merge { }; @@ -343,7 +347,6 @@ struct BalanceDistributionGrid { } } // assign island and update its info - std::cout << "assign island and update its info " << std::endl; if (min_island_id_found < std::numeric_limits::max()) { ids_to_merge.erase(std::numeric_limits::max()); ids_to_merge.erase(min_island_id_found); @@ -426,12 +429,13 @@ struct BalanceDistributionGrid { float weight = acc.accumulated_volume * params.filament_density * params.gravity_constant; float weight_torque = embedded_distance * weight; - if (!inside){ - weight_torque*=-1; + if (!inside) { + weight_torque *= -1; } std::cout << "weight: " << weight << " weight_torque: " << weight_torque << std::endl; - float extruder_conflict_torque = params.tolerable_extruder_conflict_force * 2.0f * centroid_pivot_distance; + float extruder_conflict_torque = params.tolerable_extruder_conflict_force * 2.0f + * centroid_pivot_distance; std::cout << "extruder_conflict_torque: " << extruder_conflict_torque << std::endl; float total_momentum = sticking_torque + weight_torque - xy_movement_torque - extruder_conflict_torque; @@ -451,7 +455,7 @@ struct BalanceDistributionGrid { double min_dist = std::numeric_limits::max(); Vec3f support_point = centroid; - Vec2i coords = Vec2i(0,0); + Vec2i coords = Vec2i(0, 0); for (int y = 0; y < global_cell_count.y(); ++y) { for (int x = 0; x < global_cell_count.x(); ++x) { Cell &cell = this->access_cell(Vec3i(x, y, z)); @@ -464,20 +468,23 @@ struct BalanceDistributionGrid { if (dist_sq < min_dist) { min_dist = dist_sq; support_point = cell_center; - coords = Vec2i(x,y); + coords = Vec2i(x, y); } } } } int final_height_coords = z; - while (final_height_coords > 0 && this->access_cell(Vec3i(coords.x(), coords.y(), final_height_coords)).volume > 0){ - final_height_coords --; + while (final_height_coords > 0 + && this->access_cell(Vec3i(coords.x(), coords.y(), final_height_coords)).volume > 0) { + final_height_coords--; } - support_point.z() = unscaled((final_height_coords + this->local_z_index_offset) * this->cell_size.z()); - float expected_force = total_momentum / (support_point - pivot_3d).norm(); + support_point.z() = unscaled( + (final_height_coords + this->local_z_index_offset) * this->cell_size.z()); + float expected_force = total_momentum / (support_point - pivot_3d).norm(); - std::cout << " new support point: " << support_point.x() << " | " << support_point.y() << " | " << support_point.z() << std::endl; + std::cout << " new support point: " << support_point.x() << " | " << support_point.y() << " | " + << support_point.z() << std::endl; std::cout << " expected_force: " << expected_force << std::endl; issues.supports_nedded.emplace_back(support_point, expected_force); @@ -705,12 +712,12 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, float pri SegmentAccumulator supports_acc { }; supports_acc.add_distance(params.bridge_distance + 1.0f); // initialize unsupported distance with larger than tolerable distance -> // -> it prevents extruding perimeter start and short loops into air. - SegmentAccumulator curling_acc { }; const auto to_vec3f = [print_z](const Point &point) { Vec2f tmp = unscale(point).cast(); return Vec3f(tmp.x(), tmp.y(), print_z); }; + float region_height = layer_region->layer()->height; Point prev_point = points.top(); // prev point of the path. Initialize with first point. Vec3f prev_fpoint = to_vec3f(prev_point); @@ -741,7 +748,6 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, float pri } supports_acc.add_angle(angle); - curling_acc.add_angle(angle); if (dist_from_prev_layer > max_allowed_dist_from_prev_layer) { //extrusion point is unsupported supports_acc.add_distance(edge_len); // for algorithm simplicity, expect that the whole line between prev and current point is unsupported @@ -758,14 +764,9 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, float pri } // Estimation of short curvy segments which are not supported -> problems with curling - if (dist_from_prev_layer > 0.0f) { //extrusion point is unsupported or poorly supported - curling_acc.add_distance(edge_len); - if (curling_acc.max_curvature / (PI * curling_acc.distance) > params.limit_curvature) { - issues.curling_up.push_back(CurledFilament(fpoint, layer_region->layer()->height)); - curling_acc.reset(); - } - } else { - curling_acc.reset(); + if (dist_from_prev_layer > -max_allowed_dist_from_prev_layer * 0.7071) { //extrusion point is unsupported or poorly supported + issues.curling_up.push_back( + CurledFilament(fpoint, 2.0f * region_height + region_height * 6.0f * std::abs(angle) / PI)); } prev_point = point; @@ -788,7 +789,8 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, float pri return issues; } -void distribute_layer_volume(const PrintObject *po, size_t layer_idx, BalanceDistributionGrid &balance_grid) { +void distribute_layer_volume(const PrintObject *po, size_t layer_idx, + BalanceDistributionGrid &balance_grid) { const Layer *layer = po->get_layer(layer_idx); for (const LayerRegion *region : layer->regions()) { for (const ExtrusionEntity *collections : region->fills.entities) { @@ -810,7 +812,8 @@ void distribute_layer_volume(const PrintObject *po, size_t layer_idx, BalanceDis } } -Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_check, const Params ¶ms) { +Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_check, + const Params ¶ms) { const Layer *layer = po->get_layer(layer_idx); //Prepare edge grid of previous layer, will be used to check if the extrusion path is supported EdgeGridWrapper supported_grid = compute_layer_edge_grid(layer->lower_layer); @@ -827,9 +830,11 @@ Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_ } // ex_entity for (const ExtrusionEntity *ex_entity : layer_region->fills.entities) { for (const ExtrusionEntity *fill : static_cast(ex_entity)->entities) { - if (fill->role() == ExtrusionRole::erGapFill || fill->role() == ExtrusionRole::erBridgeInfill) { + if (fill->role() == ExtrusionRole::erGapFill + || fill->role() == ExtrusionRole::erBridgeInfill) { issues.add( - check_extrusion_entity_stability(fill, layer->print_z, layer_region, supported_grid, + check_extrusion_entity_stability(fill, layer->print_z, layer_region, + supported_grid, params)); } } // fill diff --git a/src/libslic3r/SupportableIssuesSearch.hpp b/src/libslic3r/SupportableIssuesSearch.hpp index cdaa55ed5f..6586a97d5b 100644 --- a/src/libslic3r/SupportableIssuesSearch.hpp +++ b/src/libslic3r/SupportableIssuesSearch.hpp @@ -10,9 +10,8 @@ namespace SupportableIssues { struct Params { const float gravity_constant = 9806.65f; // mm/s^2; gravity acceleration on Earth's surface, algorithm assumes that printer is in upwards position. - float bridge_distance = 10.0f; //mm - float limit_curvature = 0.15f; // used to detect curling issues + float bridge_distance = 10.0f; //mm float max_first_ex_perim_unsupported_distance_factor = 0.0f; // if external perim first, return tighter max allowed distance from previous layer extrusion float max_unsupported_distance_factor = 1.0f; // For internal perimeters, infill, bridges etc, allow gap of [extrusion width] size, these extrusions have usually something to stick to. float bridge_distance_decrease_by_curvature_factor = 5.0f; // allowed bridge distance = bridge_distance / ( 1 + this factor * (curvature / PI) ) From 68243edc65f21fd0ea7990d48cf33771221a8daa Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Thu, 5 May 2022 17:27:07 +0200 Subject: [PATCH 19/78] vastly improved computational time by optimizing the convex hull computations --- src/libslic3r/SupportableIssuesSearch.cpp | 54 ++++++++++++++--------- src/libslic3r/SupportableIssuesSearch.hpp | 3 +- 2 files changed, 35 insertions(+), 22 deletions(-) diff --git a/src/libslic3r/SupportableIssuesSearch.cpp b/src/libslic3r/SupportableIssuesSearch.cpp index 4496e9a25f..d9e78b79bb 100644 --- a/src/libslic3r/SupportableIssuesSearch.cpp +++ b/src/libslic3r/SupportableIssuesSearch.cpp @@ -51,23 +51,37 @@ struct Cell { int island_id = std::numeric_limits::max(); }; -struct CentroidAccumulator { +class CentroidAccumulator { +private: Polygon convex_hull { }; Points points { }; +public: Vec3f accumulated_value = Vec3f::Zero(); float accumulated_volume { }; float base_area { }; float additional_supports_adhesion { }; - const float base_height { }; + float base_height { }; explicit CentroidAccumulator(float base_height) : base_height(base_height) { } - void calculate_base_hull() { - convex_hull = Geometry::convex_hull(points); - assert(convex_hull.is_counter_clockwise()); + const Polygon& base_hull(){ + if (this->convex_hull.empty()) { + this->convex_hull = Geometry::convex_hull(this->points); + } + return this->convex_hull; } + + void add_base_points(const Points& other) { + this->points.insert(this->points.end(), other.begin(), other.end()); + convex_hull.clear(); + } + + const Points& get_base_points(){ + return this->points; + } + }; struct CentroidAccumulators { @@ -96,10 +110,11 @@ struct CentroidAccumulators { } to_acc.accumulated_value += from_acc.accumulated_value; to_acc.accumulated_volume += from_acc.accumulated_volume; - to_acc.points.insert(to_acc.points.end(), from_acc.points.begin(), from_acc.points.end()); - to_acc.calculate_base_hull(); + to_acc.add_base_points(from_acc.get_base_points()); to_acc.base_area += from_acc.base_area; mapping[from_id] = mapping[to_id]; + from_acc = CentroidAccumulator{0.0f}; + } }; @@ -263,7 +278,7 @@ struct BalanceDistributionGrid { cell.island_id = next_island_id; Vec3crd cell_center = this->get_cell_center( Vec3i(current_coords.x(), current_coords.y(), local_z_index_offset)); - acc.points.push_back(Point(cell_center.head<2>())); + acc.add_base_points({Point(cell_center.head<2>())}); acc.accumulated_value += unscale(cell_center).cast() * cell.volume; acc.accumulated_volume += cell.volume; @@ -277,8 +292,7 @@ struct BalanceDistributionGrid { } } next_island_id--; - acc.calculate_base_hull(); - acc.base_area = unscale(unscale(acc.convex_hull.area())); //apply unscale 2x, it has units of area + acc.base_area = unscale(unscale(acc.base_hull().area())); //apply unscale 2x, it has units of area } } } @@ -293,8 +307,7 @@ struct BalanceDistributionGrid { this->access_cell(local_coords).island_id = index; CentroidAccumulator &acc = accumulators.create_accumulator(index, issues.supports_nedded[index].position.z()); - acc.points.push_back(Point(scaled(Vec2f(issues.supports_nedded[index].position.head<2>())))); - acc.calculate_base_hull(); + acc.add_base_points({Point(scaled(Vec2f(issues.supports_nedded[index].position.head<2>())))}); acc.base_area = params.support_points_interface_area; } @@ -329,8 +342,9 @@ struct BalanceDistributionGrid { if (!curled) { current.curled_height /= 4.0f; } - } + std::cout << "Curling: " << current.curled_height << std::endl; + } // distribute islands info if (current.volume > 0 && current.island_id == std::numeric_limits::max()) { int min_island_id_found = std::numeric_limits::max(); @@ -354,7 +368,6 @@ struct BalanceDistributionGrid { for (auto id : ids_to_merge) { accumulators.merge_to(id, min_island_id_found); } - CentroidAccumulator &acc = accumulators.access(min_island_id_found); acc.accumulated_value += current.volume * unscale(this->get_cell_center(this->to_global_cell_coords(Vec3i(x, y, z)))).cast< @@ -385,12 +398,12 @@ struct BalanceDistributionGrid { Point pivot; double distance_scaled_sq = std::numeric_limits::max(); bool inside = true; - if (acc.convex_hull.points.size() == 1) { - pivot = acc.convex_hull.points[0]; + if (acc.base_hull().points.size() == 1) { + pivot = acc.base_hull().points[0]; distance_scaled_sq = (pivot - centroid_base_projection).squaredNorm(); inside = true; } else { - for (Line line : acc.convex_hull.lines()) { + for (Line line : acc.base_hull().lines()) { Point closest_point; double dist_sq = line.distance_to_squared(centroid_base_projection, &closest_point); if (dist_sq < distance_scaled_sq) { @@ -408,7 +421,7 @@ struct BalanceDistributionGrid { pivot_3d << unscale(pivot).cast(), acc.base_height; float embedded_distance = unscaled(sqrt(distance_scaled_sq)); float centroid_pivot_distance = (centroid - pivot_3d).norm(); - float base_center_pivot_distance = float(unscale(Vec2crd(acc.convex_hull.centroid() - pivot)).norm()); + float base_center_pivot_distance = float(unscale(Vec2crd(acc.base_hull().centroid() - pivot)).norm()); std::cout << "centroid inside ? " << inside << " and embedded distance is: " << embedded_distance << std::endl; @@ -488,9 +501,8 @@ struct BalanceDistributionGrid { std::cout << " expected_force: " << expected_force << std::endl; issues.supports_nedded.emplace_back(support_point, expected_force); - acc.points.push_back(Point::new_scale(Vec2f(support_point.head<2>()))); + acc.add_base_points({Point::new_scale(Vec2f(support_point.head<2>()))}); acc.base_area += params.support_points_interface_area; - acc.calculate_base_hull(); } } @@ -766,7 +778,7 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, float pri // Estimation of short curvy segments which are not supported -> problems with curling if (dist_from_prev_layer > -max_allowed_dist_from_prev_layer * 0.7071) { //extrusion point is unsupported or poorly supported issues.curling_up.push_back( - CurledFilament(fpoint, 2.0f * region_height + region_height * 6.0f * std::abs(angle) / PI)); + CurledFilament(fpoint, 0.2f * region_height + region_height * 0.6f * std::abs(angle) / PI)); } prev_point = point; diff --git a/src/libslic3r/SupportableIssuesSearch.hpp b/src/libslic3r/SupportableIssuesSearch.hpp index 6586a97d5b..1e90e8cde0 100644 --- a/src/libslic3r/SupportableIssuesSearch.hpp +++ b/src/libslic3r/SupportableIssuesSearch.hpp @@ -22,7 +22,8 @@ struct Params { float max_acceleration = 1000.0f; // mm/s^2 ; max acceleration of object (bed) in XY float filament_density = 1.25f * 0.001f; // g/mm^3 ; Common filaments are very lightweight, so precise number is not that important - float tolerable_extruder_conflict_force = 50.0f * gravity_constant; // force that can occasionally push the model due to various factors (filament leaks, small curling, ... ); current value corresponds to weight of 200g + float tolerable_extruder_conflict_force = 50.0f * gravity_constant; // force that can occasionally push the model due to various factors (filament leaks, small curling, ... ); current value corresponds to weight of 50g + float max_curled_conflict_extruder_force = 200.0f * gravity_constant; // for areas with possible high layered curled filaments, max force to account fo ; current value corresponds to weight of 200g }; From 609f42fb18a59e56ad0454cf9a9d3320caffcc04 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Fri, 6 May 2022 13:30:40 +0200 Subject: [PATCH 20/78] refactoring, pressure points extracted but not accounted for --- src/libslic3r/SupportableIssuesSearch.cpp | 313 ++++++++++++---------- 1 file changed, 166 insertions(+), 147 deletions(-) diff --git a/src/libslic3r/SupportableIssuesSearch.cpp b/src/libslic3r/SupportableIssuesSearch.cpp index d9e78b79bb..a097d220f5 100644 --- a/src/libslic3r/SupportableIssuesSearch.cpp +++ b/src/libslic3r/SupportableIssuesSearch.cpp @@ -55,7 +55,7 @@ class CentroidAccumulator { private: Polygon convex_hull { }; Points points { }; -public: + public: Vec3f accumulated_value = Vec3f::Zero(); float accumulated_volume { }; float base_area { }; @@ -66,19 +66,19 @@ public: base_height(base_height) { } - const Polygon& base_hull(){ + const Polygon& base_hull() { if (this->convex_hull.empty()) { this->convex_hull = Geometry::convex_hull(this->points); } return this->convex_hull; } - void add_base_points(const Points& other) { + void add_base_points(const Points &other) { this->points.insert(this->points.end(), other.begin(), other.end()); convex_hull.clear(); } - const Points& get_base_points(){ + const Points& get_base_points() { return this->points; } @@ -113,12 +113,27 @@ struct CentroidAccumulators { to_acc.add_base_points(from_acc.get_base_points()); to_acc.base_area += from_acc.base_area; mapping[from_id] = mapping[to_id]; - from_acc = CentroidAccumulator{0.0f}; + from_acc = CentroidAccumulator { 0.0f }; } }; -struct BalanceDistributionGrid { +class BalanceDistributionGrid { + + static constexpr float cell_height = scale_(0.3f); + + Vec3crd cell_size { }; + + Vec3crd global_origin { }; + Vec3crd global_size { }; + Vec3i global_cell_count { }; + + int local_z_index_offset { }; + int local_z_cell_count { }; + std::vector cells { }; + +public: + BalanceDistributionGrid() = default; void init(const PrintObject *po, size_t layer_idx_begin, size_t layer_idx_end) { @@ -278,7 +293,7 @@ struct BalanceDistributionGrid { cell.island_id = next_island_id; Vec3crd cell_center = this->get_cell_center( Vec3i(current_coords.x(), current_coords.y(), local_z_index_offset)); - acc.add_base_points({Point(cell_center.head<2>())}); + acc.add_base_points( { Point(cell_center.head<2>()) }); acc.accumulated_value += unscale(cell_center).cast() * cell.volume; acc.accumulated_volume += cell.volume; @@ -307,7 +322,7 @@ struct BalanceDistributionGrid { this->access_cell(local_coords).island_id = index; CentroidAccumulator &acc = accumulators.create_accumulator(index, issues.supports_nedded[index].position.z()); - acc.add_base_points({Point(scaled(Vec2f(issues.supports_nedded[index].position.head<2>())))}); + acc.add_base_points( { Point(scaled(Vec2f(issues.supports_nedded[index].position.head<2>()))) }); acc.base_area = params.support_points_interface_area; } @@ -315,8 +330,10 @@ struct BalanceDistributionGrid { this->access_cell(curling.position).curled_height += curling.estimated_height; } - std::unordered_set modified_acc_ids; + std::unordered_map> modified_acc_ids; + std::unordered_map> filtered_active_accumulators; modified_acc_ids.reserve(issues.supports_nedded.size() + 1); + for (int z = 1; z < local_z_cell_count; ++z) { std::cout << "current z: " << z << std::endl; @@ -325,26 +342,6 @@ struct BalanceDistributionGrid { for (int x = 0; x < global_cell_count.x(); ++x) { for (int y = 0; y < global_cell_count.y(); ++y) { Cell ¤t = this->access_cell(Vec3i(x, y, z)); - - // distribute curling - if (current.volume > 0) { - float curled_height = 0; - for (int y_offset = -2; y_offset <= 2; ++y_offset) { - for (int x_offset = -2; x_offset <= 2; ++x_offset) { - if (validate_xy_coords(Vec2i(x + x_offset, y + y_offset))) { - Cell &under = this->access_cell(Vec3i(x + x_offset, y + y_offset, z - 1)); - curled_height = std::max(curled_height, under.curled_height); - } - } - } - bool curled = current.curled_height > 0; - current.curled_height += std::max(0.0f, float(curled_height - unscaled(this->cell_size.z()))); - if (!curled) { - current.curled_height /= 4.0f; - } - - std::cout << "Curling: " << current.curled_height << std::endl; - } // distribute islands info if (current.volume > 0 && current.island_id == std::numeric_limits::max()) { int min_island_id_found = std::numeric_limits::max(); @@ -373,143 +370,176 @@ struct BalanceDistributionGrid { * unscale(this->get_cell_center(this->to_global_cell_coords(Vec3i(x, y, z)))).cast< float>(); acc.accumulated_volume += current.volume; - modified_acc_ids.insert(min_island_id_found); + modified_acc_ids.emplace(min_island_id_found, std::vector { }); + } + } + + // distribute curling + if (current.volume > 0) { + float curled_height = 0; + for (int y_offset = -2; y_offset <= 2; ++y_offset) { + for (int x_offset = -2; x_offset <= 2; ++x_offset) { + if (validate_xy_coords(Vec2i(x + x_offset, y + y_offset))) { + Cell &under = this->access_cell(Vec3i(x + x_offset, y + y_offset, z - 1)); + curled_height = std::max(curled_height, under.curled_height); + } + } + } + bool curled = current.curled_height > 0; + current.curled_height += curled_height; + if (!curled) { + current.curled_height -= unscaled(this->cell_size.z()); + } + std::cout << "Curling: " << current.curled_height << std::endl; + + if (current.curled_height / unscaled(this->cell_size.z()) > 0.3f) { + modified_acc_ids[current.island_id].push_back( { x, y }); } } } } - std::cout << " check all active accumulators " << std::endl; + filtered_active_accumulators.clear(); + for (const auto &pair : modified_acc_ids) { + CentroidAccumulator *acc = &accumulators.access(pair.first); + filtered_active_accumulators[acc].insert(filtered_active_accumulators[acc].end(), pair.second.begin(), + pair.second.end()); + } - for (int acc_id : modified_acc_ids) { + check_accumulators_stability(z, accumulators, filtered_active_accumulators, issues, params); + } + } +private: + void check_accumulators_stability(int z, CentroidAccumulators &accumulators, + std::unordered_map> filtered_active_accumulators, + Issues &issues, const Params ¶ms) { - std::cout << "Z: " << z << " controlling acc id: " << acc_id << std::endl; + for (const auto &pair : filtered_active_accumulators) { + std::cout << "Z: " << z << std::endl; + CentroidAccumulator &acc = *pair.first; + Vec3f centroid = acc.accumulated_value / acc.accumulated_volume; - CentroidAccumulator &acc = accumulators.access(acc_id); - Vec3f centroid = acc.accumulated_value / acc.accumulated_volume; + std::cout << "acc.accumulated_value : " << acc.accumulated_value.x() << " " + << acc.accumulated_value.y() << " " << acc.accumulated_value.z() << std::endl; + std::cout << "acc.accumulated_volume : " << acc.accumulated_volume << std::endl; + std::cout << "centroid: " << centroid.x() << " " << centroid.y() << " " << centroid.z() << std::endl; - std::cout << "acc.accumulated_value : " << acc.accumulated_value.x() << " " - << acc.accumulated_value.y() << " " << acc.accumulated_value.z() << std::endl; - std::cout << "acc.accumulated_volume : " << acc.accumulated_volume << std::endl; - std::cout << "centroid: " << centroid.x() << " " << centroid.y() << " " << centroid.z() << std::endl; - - //determine signed shortest distance to the convex hull - Point centroid_base_projection = Point(scaled(Vec2f(centroid.head<2>()))); - Point pivot; - double distance_scaled_sq = std::numeric_limits::max(); - bool inside = true; - if (acc.base_hull().points.size() == 1) { - pivot = acc.base_hull().points[0]; - distance_scaled_sq = (pivot - centroid_base_projection).squaredNorm(); - inside = true; - } else { - for (Line line : acc.base_hull().lines()) { - Point closest_point; - double dist_sq = line.distance_to_squared(centroid_base_projection, &closest_point); - if (dist_sq < distance_scaled_sq) { - pivot = closest_point; - distance_scaled_sq = dist_sq; - } - if ((centroid_base_projection - closest_point).cast().dot(line.normal().cast()) - > 0) { - inside = false; - } + //determine signed shortest distance to the convex hull + Point centroid_base_projection = Point(scaled(Vec2f(centroid.head<2>()))); + Point pivot; + double distance_scaled_sq = std::numeric_limits::max(); + bool inside = true; + if (acc.base_hull().points.size() == 1) { + pivot = acc.base_hull().points[0]; + distance_scaled_sq = (pivot - centroid_base_projection).squaredNorm(); + inside = distance_scaled_sq < params.support_points_interface_area; + } else { + for (Line line : acc.base_hull().lines()) { + Point closest_point; + double dist_sq = line.distance_to_squared(centroid_base_projection, &closest_point); + if (dist_sq < distance_scaled_sq) { + pivot = closest_point; + distance_scaled_sq = dist_sq; + } + if ((centroid_base_projection - closest_point).cast().dot(line.normal().cast()) + > 0) { + inside = false; } } + } - Vec3f pivot_3d; - pivot_3d << unscale(pivot).cast(), acc.base_height; - float embedded_distance = unscaled(sqrt(distance_scaled_sq)); - float centroid_pivot_distance = (centroid - pivot_3d).norm(); - float base_center_pivot_distance = float(unscale(Vec2crd(acc.base_hull().centroid() - pivot)).norm()); + Vec3f pivot_3d; + pivot_3d << unscale(pivot).cast(), acc.base_height; + float embedded_distance = unscaled(sqrt(distance_scaled_sq)); + float centroid_pivot_distance = (centroid - pivot_3d).norm(); + float base_center_pivot_distance = float(unscale(Vec2crd(acc.base_hull().centroid() - pivot)).norm()); - std::cout << "centroid inside ? " << inside << " and embedded distance is: " << embedded_distance - << std::endl; + std::cout << "centroid inside ? " << inside << " and embedded distance is: " << embedded_distance + << std::endl; - bool additional_supports_needed = false; - float sticking_force = acc.base_area - * (acc.base_height == 0 ? params.base_adhesion : params.support_adhesion); - float sticking_torque = base_center_pivot_distance * sticking_force; + bool additional_supports_needed = false; + float sticking_force = acc.base_area + * (acc.base_height == 0 ? params.base_adhesion : params.support_adhesion); + float sticking_torque = base_center_pivot_distance * sticking_force; - std::cout << "sticking force: " << sticking_force << " sticking torque: " << sticking_torque - << std::endl; + std::cout << "sticking force: " << sticking_force << " sticking torque: " << sticking_torque + << std::endl; - float xy_movement_force = acc.accumulated_volume * params.filament_density * params.max_acceleration; - float xy_movement_torque = xy_movement_force * centroid_pivot_distance; + float xy_movement_force = acc.accumulated_volume * params.filament_density * params.max_acceleration; + float xy_movement_torque = xy_movement_force * centroid_pivot_distance; - std::cout << "xy_movement_force: " << xy_movement_force << " xy_movement_torque: " - << xy_movement_torque << std::endl; + std::cout << "xy_movement_force: " << xy_movement_force << " xy_movement_torque: " + << xy_movement_torque << std::endl; - float weight = acc.accumulated_volume * params.filament_density * params.gravity_constant; - float weight_torque = embedded_distance * weight; - if (!inside) { - weight_torque *= -1; - } - std::cout << "weight: " << weight << " weight_torque: " << weight_torque << std::endl; + float weight = acc.accumulated_volume * params.filament_density * params.gravity_constant; + float weight_torque = embedded_distance * weight; + if (!inside) { + weight_torque *= -1; + } + std::cout << "weight: " << weight << " weight_torque: " << weight_torque << std::endl; - float extruder_conflict_torque = params.tolerable_extruder_conflict_force * 2.0f - * centroid_pivot_distance; - std::cout << "extruder_conflict_torque: " << extruder_conflict_torque << std::endl; + float extruder_conflict_torque = params.tolerable_extruder_conflict_force * 2.0f + * centroid_pivot_distance; + std::cout << "extruder_conflict_torque: " << extruder_conflict_torque << std::endl; - float total_momentum = sticking_torque + weight_torque - xy_movement_torque - extruder_conflict_torque; - additional_supports_needed = total_momentum < 0; + float total_momentum = sticking_torque + weight_torque - xy_movement_torque - extruder_conflict_torque; + additional_supports_needed = total_momentum < 0; - std::cout << "total_momentum: " << total_momentum << std::endl; - std::cout << "additional supports needed: " << additional_supports_needed << std::endl; + std::cout << "total_momentum: " << total_momentum << std::endl; + std::cout << "additional supports needed: " << additional_supports_needed << std::endl; - if (additional_supports_needed) { - Vec2f attractor_dir = - unscale(Vec2crd(inside ? - pivot - centroid_base_projection : - centroid_base_projection - pivot)).cast().normalized(); - Vec2f attractor = unscale(centroid_base_projection).cast() + 10000 * attractor_dir; + if (additional_supports_needed) { + Vec2f attractor_dir = + unscale(Vec2crd(inside ? + pivot - centroid_base_projection : + centroid_base_projection - pivot)).cast().normalized(); + Vec2f attractor = unscale(centroid_base_projection).cast() + 10000 * attractor_dir; - std::cout << " attractor: " << attractor.x() << " | " << attractor.y() << std::endl; + std::cout << " attractor: " << attractor.x() << " | " << attractor.y() << std::endl; - double min_dist = std::numeric_limits::max(); - Vec3f support_point = centroid; - Vec2i coords = Vec2i(0, 0); - for (int y = 0; y < global_cell_count.y(); ++y) { - for (int x = 0; x < global_cell_count.x(); ++x) { - Cell &cell = this->access_cell(Vec3i(x, y, z)); - if (cell.island_id != std::numeric_limits::max() && - &accumulators.access(cell.island_id) == &acc) { - Vec3f cell_center = - unscale(this->get_cell_center(this->to_global_cell_coords(Vec3i(x, y, z)))).cast< - float>(); - float dist_sq = (cell_center.head<2>() - attractor).squaredNorm(); - if (dist_sq < min_dist) { - min_dist = dist_sq; - support_point = cell_center; - coords = Vec2i(x, y); - } + double min_dist = std::numeric_limits::max(); + Vec3f support_point = centroid; + Vec2i coords = Vec2i(0, 0); + for (int y = 0; y < global_cell_count.y(); ++y) { + for (int x = 0; x < global_cell_count.x(); ++x) { + Cell &cell = this->access_cell(Vec3i(x, y, z)); + if (cell.island_id != std::numeric_limits::max() && + &accumulators.access(cell.island_id) == &acc) { + Vec3f cell_center = + unscale(this->get_cell_center(this->to_global_cell_coords(Vec3i(x, y, z)))).cast< + float>(); + float dist_sq = (cell_center.head<2>() - attractor).squaredNorm(); + if (dist_sq < min_dist) { + min_dist = dist_sq; + support_point = cell_center; + coords = Vec2i(x, y); } } } - - int final_height_coords = z; - while (final_height_coords > 0 - && this->access_cell(Vec3i(coords.x(), coords.y(), final_height_coords)).volume > 0) { - final_height_coords--; - } - support_point.z() = unscaled( - (final_height_coords + this->local_z_index_offset) * this->cell_size.z()); - float expected_force = total_momentum / (support_point - pivot_3d).norm(); - - std::cout << " new support point: " << support_point.x() << " | " << support_point.y() << " | " - << support_point.z() << std::endl; - std::cout << " expected_force: " << expected_force << std::endl; - - issues.supports_nedded.emplace_back(support_point, expected_force); - acc.add_base_points({Point::new_scale(Vec2f(support_point.head<2>()))}); - acc.base_area += params.support_points_interface_area; } + int final_height_coords = z; + while (final_height_coords > 0 + && this->access_cell(Vec3i(coords.x(), coords.y(), final_height_coords)).volume > 0) { + final_height_coords--; + } + support_point.z() = unscaled( + (final_height_coords + this->local_z_index_offset) * this->cell_size.z()); + float expected_force = total_momentum / (support_point - pivot_3d).norm(); + + std::cout << " new support point: " << support_point.x() << " | " << support_point.y() << " | " + << support_point.z() << std::endl; + std::cout << " expected_force: " << expected_force << std::endl; + + issues.supports_nedded.emplace_back(support_point, expected_force); + acc.add_base_points( { Point::new_scale(Vec2f(support_point.head<2>())) }); + acc.base_area += params.support_points_interface_area; } } } #ifdef DEBUG_FILES +public: void debug_export() const { Slic3r::CNumericLocalesSetter locales_setter; { @@ -572,19 +602,6 @@ struct BalanceDistributionGrid { } } #endif - - static constexpr float cell_height = scale_(0.3f); - - Vec3crd cell_size { }; - - Vec3crd global_origin { }; - Vec3crd global_size { }; - Vec3i global_cell_count { }; - - int local_z_index_offset { }; - int local_z_cell_count { }; - std::vector cells { }; - } ; @@ -777,8 +794,11 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, float pri // Estimation of short curvy segments which are not supported -> problems with curling if (dist_from_prev_layer > -max_allowed_dist_from_prev_layer * 0.7071) { //extrusion point is unsupported or poorly supported + float dist_factor = (dist_from_prev_layer + max_allowed_dist_from_prev_layer * 0.7071) + / max_allowed_dist_from_prev_layer; issues.curling_up.push_back( - CurledFilament(fpoint, 0.2f * region_height + region_height * 0.6f * std::abs(angle) / PI)); + CurledFilament(fpoint, + dist_factor * (0.25f * region_height + region_height * 0.75f * std::abs(angle) / PI))); } prev_point = point; @@ -795,7 +815,6 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, float pri points.push(Point::new_scale(Vec2f(next + reverse_v * (i * step_size)))); } } - } } return issues; From 4144b73ccdbc836e20b1dc6c762466809cf8523d Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Fri, 6 May 2022 17:01:26 +0200 Subject: [PATCH 21/78] curling estimation improvements --- src/libslic3r/SupportableIssuesSearch.cpp | 90 ++++++++++++++++++----- 1 file changed, 71 insertions(+), 19 deletions(-) diff --git a/src/libslic3r/SupportableIssuesSearch.cpp b/src/libslic3r/SupportableIssuesSearch.cpp index a097d220f5..1f28a5f074 100644 --- a/src/libslic3r/SupportableIssuesSearch.cpp +++ b/src/libslic3r/SupportableIssuesSearch.cpp @@ -81,7 +81,6 @@ private: const Points& get_base_points() { return this->points; } - }; struct CentroidAccumulators { @@ -385,11 +384,10 @@ public: } } } - bool curled = current.curled_height > 0; - current.curled_height += curled_height; - if (!curled) { - current.curled_height -= unscaled(this->cell_size.z()); - } + current.curled_height += std::max(0.0f, + float(curled_height - unscaled(this->cell_size.z()) / 2.0f)); + current.curled_height = std::min(current.curled_height, + float(unscaled(this->cell_size.z()) * 2.0f)); std::cout << "Curling: " << current.curled_height << std::endl; if (current.curled_height / unscaled(this->cell_size.z()) > 0.3f) { @@ -402,7 +400,8 @@ public: filtered_active_accumulators.clear(); for (const auto &pair : modified_acc_ids) { CentroidAccumulator *acc = &accumulators.access(pair.first); - filtered_active_accumulators[acc].insert(filtered_active_accumulators[acc].end(), pair.second.begin(), + filtered_active_accumulators[acc].insert(filtered_active_accumulators[acc].end(), + pair.second.begin(), pair.second.end()); } @@ -422,9 +421,9 @@ private: std::cout << "acc.accumulated_value : " << acc.accumulated_value.x() << " " << acc.accumulated_value.y() << " " << acc.accumulated_value.z() << std::endl; std::cout << "acc.accumulated_volume : " << acc.accumulated_volume << std::endl; - std::cout << "centroid: " << centroid.x() << " " << centroid.y() << " " << centroid.z() << std::endl; + std::cout << "centroid: " << centroid.x() << " " << centroid.y() << " " << centroid.z() + << std::endl; - //determine signed shortest distance to the convex hull Point centroid_base_projection = Point(scaled(Vec2f(centroid.head<2>()))); Point pivot; double distance_scaled_sq = std::numeric_limits::max(); @@ -441,7 +440,8 @@ private: pivot = closest_point; distance_scaled_sq = dist_sq; } - if ((centroid_base_projection - closest_point).cast().dot(line.normal().cast()) + if ((centroid_base_projection - closest_point).cast().dot( + line.normal().cast()) > 0) { inside = false; } @@ -452,7 +452,8 @@ private: pivot_3d << unscale(pivot).cast(), acc.base_height; float embedded_distance = unscaled(sqrt(distance_scaled_sq)); float centroid_pivot_distance = (centroid - pivot_3d).norm(); - float base_center_pivot_distance = float(unscale(Vec2crd(acc.base_hull().centroid() - pivot)).norm()); + float base_center_pivot_distance = float( + unscale(Vec2crd(acc.base_hull().centroid() - pivot)).norm()); std::cout << "centroid inside ? " << inside << " and embedded distance is: " << embedded_distance << std::endl; @@ -465,7 +466,8 @@ private: std::cout << "sticking force: " << sticking_force << " sticking torque: " << sticking_torque << std::endl; - float xy_movement_force = acc.accumulated_volume * params.filament_density * params.max_acceleration; + float xy_movement_force = acc.accumulated_volume * params.filament_density + * params.max_acceleration; float xy_movement_torque = xy_movement_force * centroid_pivot_distance; std::cout << "xy_movement_force: " << xy_movement_force << " xy_movement_torque: " @@ -482,7 +484,8 @@ private: * centroid_pivot_distance; std::cout << "extruder_conflict_torque: " << extruder_conflict_torque << std::endl; - float total_momentum = sticking_torque + weight_torque - xy_movement_torque - extruder_conflict_torque; + float total_momentum = sticking_torque + weight_torque - xy_movement_torque + - extruder_conflict_torque; additional_supports_needed = total_momentum < 0; std::cout << "total_momentum: " << total_momentum << std::endl; @@ -538,6 +541,50 @@ private: } } + float check_point_stability_under_pressure(CentroidAccumulator &acc, const Point &base_centroid, + const Vec3f &pressure_point, const Params ¶ms) { + Point pressure_base_projection = Point(scaled(Vec2f(pressure_point.head<2>()))); + Point pivot; + double distance_scaled_sq = std::numeric_limits::max(); + bool inside = true; + if (acc.base_hull().points.size() == 1) { + pivot = acc.base_hull().points[0]; + distance_scaled_sq = (pivot - pressure_base_projection).squaredNorm(); + inside = distance_scaled_sq < params.support_points_interface_area; + } else { + for (Line line : acc.base_hull().lines()) { + Point closest_point; + double dist_sq = line.distance_to_squared(pressure_base_projection, &closest_point); + if (dist_sq < distance_scaled_sq) { + pivot = closest_point; + distance_scaled_sq = dist_sq; + } + if ((pressure_base_projection - closest_point).cast().dot(line.normal().cast()) + > 0) { + inside = false; + } + } + } + float embedded_distance = unscaled(sqrt(distance_scaled_sq)); + float base_center_pivot_distance = float(unscale(Vec2crd(base_centroid - pivot)).norm()); + Vec3f pivot_3d; + pivot_3d << unscale(pivot).cast(), acc.base_height; + + float sticking_force = acc.base_area + * (acc.base_height == 0 ? params.base_adhesion : params.support_adhesion); + float sticking_torque = sticking_force * base_center_pivot_distance; + + float pressure_arm = inside ? pressure_point.z() - pivot_3d.z() : (pressure_point - pivot_3d).norm(); + float pressure_force = 1.0f; + float pressure_torque = pressure_arm * pressure_force; + + if (sticking_torque < pressure_torque) { + return pressure_force; + } else { + return 0.0f; + } + } + #ifdef DEBUG_FILES public: void debug_export() const { @@ -575,7 +622,8 @@ public: for (int x = 0; x < global_cell_count.x(); ++x) { for (int y = 0; y < global_cell_count.y(); ++y) { for (int z = 0; z < local_z_cell_count; ++z) { - Vec3f center = unscale(get_cell_center(to_global_cell_coords(Vec3i { x, y, z }))).cast(); + Vec3f center = unscale(get_cell_center(to_global_cell_coords(Vec3i { x, y, z }))).cast< + float>(); const Cell &cell = access_cell(Vec3i(x, y, z)); if (cell.volume != 0) { auto volume_color = value_to_rgbf(0, cell.volume, cell.volume); @@ -584,12 +632,14 @@ public: } if (cell.island_id != std::numeric_limits::max()) { auto island_color = value_to_rgbf(min_island_id, max_island_id + 1, cell.island_id); - fprintf(islands_grid_file, "v %f %f %f %f %f %f\n", center(0), center(1), center(2), + fprintf(islands_grid_file, "v %f %f %f %f %f %f\n", center(0), center(1), + center(2), island_color.x(), island_color.y(), island_color.z()); } if (cell.curled_height > 0) { auto curling_color = value_to_rgbf(0, max_curling_height, cell.curled_height); - fprintf(curling_grid_file, "v %f %f %f %f %f %f\n", center(0), center(1), center(2), + fprintf(curling_grid_file, "v %f %f %f %f %f %f\n", center(0), center(1), + center(2), curling_color.x(), curling_color.y(), curling_color.z()); } } @@ -722,7 +772,8 @@ struct SegmentAccumulator { }; -Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, float print_z, const LayerRegion *layer_region, +Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, float print_z, + const LayerRegion *layer_region, const EdgeGridWrapper &supported_grid, const Params ¶ms) { Issues issues { }; @@ -794,11 +845,12 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, float pri // Estimation of short curvy segments which are not supported -> problems with curling if (dist_from_prev_layer > -max_allowed_dist_from_prev_layer * 0.7071) { //extrusion point is unsupported or poorly supported - float dist_factor = (dist_from_prev_layer + max_allowed_dist_from_prev_layer * 0.7071) + float dist_factor = 0.5f + 0.5f * (dist_from_prev_layer + max_allowed_dist_from_prev_layer * 0.7071) / max_allowed_dist_from_prev_layer; issues.curling_up.push_back( CurledFilament(fpoint, - dist_factor * (0.25f * region_height + region_height * 0.75f * std::abs(angle) / PI))); + dist_factor + * (0.25f * region_height + region_height * 0.75f * std::abs(angle) / PI))); } prev_point = point; From e39d14bf989862950287b64a5d0394e96587c4ea Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Wed, 11 May 2022 13:17:01 +0200 Subject: [PATCH 22/78] finished base for curling stability tests added comments --- src/libslic3r/SupportableIssuesSearch.cpp | 287 +++++++++++----------- 1 file changed, 137 insertions(+), 150 deletions(-) diff --git a/src/libslic3r/SupportableIssuesSearch.cpp b/src/libslic3r/SupportableIssuesSearch.cpp index 1f28a5f074..5e3e1822de 100644 --- a/src/libslic3r/SupportableIssuesSearch.cpp +++ b/src/libslic3r/SupportableIssuesSearch.cpp @@ -48,7 +48,7 @@ CurledFilament::CurledFilament(const Vec3f &position) : struct Cell { float volume; float curled_height; - int island_id = std::numeric_limits::max(); + int accumulator_id = std::numeric_limits::max(); }; class CentroidAccumulator { @@ -262,7 +262,7 @@ public: return local_coords.x() >= 0 && local_coords.y() >= 0 && local_coords.x() < this->global_cell_count.x() && local_coords.y() < this->global_cell_count.y(); }; - CentroidAccumulators accumulators(issues.supports_nedded.size() + 4); + CentroidAccumulators accumulators(issues.supports_nedded.size() + 4); // just an estimation; one for each support point from prev step, and 4 for the base auto custom_comparator = [](const Vec2i &left, const Vec2i &right) { if (left.x() == right.x()) { return left.y() < right.y(); @@ -270,26 +270,28 @@ public: return left.x() < right.x(); }; - int next_island_id = -1; - std::set coords_to_check(custom_comparator); + //initialization of the bed accumulators from the bed cells (first layer of grid) + int next_accumulator_id = -1; // accumulators from the bed have negative ids, starting with -1. Accumulators generated by support points have nonegative ids, starting with 0, and sorted by height + // The reason is, that during merging of accumulators (when they meet at the upper cells), algorithm always keeps the one with the lower id (so bed is preffered), and discards the other + std::set coords_to_check(custom_comparator); // set of coords to check for the current accumulator (search based on connectivity) for (int y = 0; y < global_cell_count.y(); ++y) { for (int x = 0; x < global_cell_count.x(); ++x) { Cell &origin_cell = this->access_cell(Vec3i(x, y, 0)); - if (origin_cell.volume > 0 && origin_cell.island_id == std::numeric_limits::max()) { - CentroidAccumulator &acc = accumulators.create_accumulator(next_island_id, 0); + if (origin_cell.volume > 0 && origin_cell.accumulator_id == std::numeric_limits::max()) { // cell has volume and no accumulator assigned yet + CentroidAccumulator &acc = accumulators.create_accumulator(next_accumulator_id, 0); // create new accumulator, height is 0, because we are on the bed coords_to_check.clear(); coords_to_check.insert(Vec2i(x, y)); - while (!coords_to_check.empty()) { + while (!coords_to_check.empty()) { // insert the origin cell coords in to the set, and search all connected cells with volume and without assigned accumulator, assign them to acc Vec2i current_coords = *coords_to_check.begin(); coords_to_check.erase(coords_to_check.begin()); - if (!validate_xy_coords(current_coords)) { + if (!validate_xy_coords(current_coords)) { // invalid coords, drop continue; } Cell &cell = this->access_cell(Vec3i(current_coords.x(), current_coords.y(), 0)); - if (cell.volume <= 0 || cell.island_id != std::numeric_limits::max()) { + if (cell.volume <= 0 || cell.accumulator_id != std::numeric_limits::max()) { // empty cell or already assigned, drop continue; } - cell.island_id = next_island_id; + cell.accumulator_id = next_accumulator_id; // update cell accumulator id, update the accumulator with the new cell data, add neighbours to queue Vec3crd cell_center = this->get_cell_center( Vec3i(current_coords.x(), current_coords.y(), local_z_index_offset)); acc.add_base_points( { Point(cell_center.head<2>()) }); @@ -305,12 +307,15 @@ public: } } } - next_island_id--; + next_accumulator_id--; + //base area is separated from the base convex hull - bed accumulators are initialized with convex hull area (TODO compute from number of covered cells instead ) + // but support points are initialized with constant, and during merging, the base_areas are added. acc.base_area = unscale(unscale(acc.base_hull().area())); //apply unscale 2x, it has units of area } } } + // sort support points by height, so that their accumulators ids are also sorted by height std::sort(issues.supports_nedded.begin(), issues.supports_nedded.end(), [](const SupportPoint &left, const SupportPoint &right) { return left.position.z() < right.position.z(); @@ -318,21 +323,27 @@ public: for (int index = 0; index < int(issues.supports_nedded.size()); ++index) { Vec3i local_coords = this->to_local_cell_coords( this->to_global_cell_coords(issues.supports_nedded[index].position)); - this->access_cell(local_coords).island_id = index; + this->access_cell(local_coords).accumulator_id = index; // assign accumulator id (in case that multiple support points fall into the same cell, they are just overriden) CentroidAccumulator &acc = accumulators.create_accumulator(index, issues.supports_nedded[index].position.z()); acc.add_base_points( { Point(scaled(Vec2f(issues.supports_nedded[index].position.head<2>()))) }); acc.base_area = params.support_points_interface_area; } + // add curling data to each cell for (const CurledFilament &curling : issues.curling_up) { this->access_cell(curling.position).curled_height += curling.estimated_height; } + // keep map of modified accumulator for each layer, so that discarded accumulators are not further checked for stability + // the value of the map is list of cells with curling, that should be further checked for pressure stability with repsect to the accumulator std::unordered_map> modified_acc_ids; + // At the end of the layer check, accumulators are further filtered, since merging causes that single accumulator can have mutliple ids. + // But each accumulator should be checked only once. std::unordered_map> filtered_active_accumulators; modified_acc_ids.reserve(issues.supports_nedded.size() + 1); + // For each grid layer, cells are added to the accumulators and all active accumulators are checked of stability. for (int z = 1; z < local_z_cell_count; ++z) { std::cout << "current z: " << z << std::endl; @@ -341,39 +352,41 @@ public: for (int x = 0; x < global_cell_count.x(); ++x) { for (int y = 0; y < global_cell_count.y(); ++y) { Cell ¤t = this->access_cell(Vec3i(x, y, z)); - // distribute islands info - if (current.volume > 0 && current.island_id == std::numeric_limits::max()) { - int min_island_id_found = std::numeric_limits::max(); + // distribute islands info - look for neighbours under the cell, and pick the smallest accumulator id + // also gather all ids, they will be merged to the smallest id accumualtor + if (current.volume > 0 && current.accumulator_id == std::numeric_limits::max()) { + int min_accumulator_id_found = std::numeric_limits::max(); std::unordered_set ids_to_merge { }; for (int y_offset = -2; y_offset <= 2; ++y_offset) { for (int x_offset = -2; x_offset <= 2; ++x_offset) { if (validate_xy_coords(Vec2i(x + x_offset, y + y_offset))) { Cell &under = this->access_cell(Vec3i(x + x_offset, y + y_offset, z - 1)); - if (under.island_id < min_island_id_found) { - min_island_id_found = under.island_id; + if (under.accumulator_id < min_accumulator_id_found) { + min_accumulator_id_found = under.accumulator_id; } - ids_to_merge.insert(under.island_id); + ids_to_merge.insert(under.accumulator_id); } } } - // assign island and update its info - if (min_island_id_found < std::numeric_limits::max()) { + // assign accumulator and update its info + if (min_accumulator_id_found < std::numeric_limits::max()) { // accumulator id found ids_to_merge.erase(std::numeric_limits::max()); - ids_to_merge.erase(min_island_id_found); - current.island_id = min_island_id_found; + ids_to_merge.erase(min_accumulator_id_found); + current.accumulator_id = min_accumulator_id_found; //assign accumualtor id to the cell for (auto id : ids_to_merge) { - accumulators.merge_to(id, min_island_id_found); + accumulators.merge_to(id, min_accumulator_id_found); //merge other ids to the smallest id } - CentroidAccumulator &acc = accumulators.access(min_island_id_found); + //update the acc with new point, and store in the modified accumulators map + CentroidAccumulator &acc = accumulators.access(min_accumulator_id_found); acc.accumulated_value += current.volume * unscale(this->get_cell_center(this->to_global_cell_coords(Vec3i(x, y, z)))).cast< float>(); acc.accumulated_volume += current.volume; - modified_acc_ids.emplace(min_island_id_found, std::vector { }); + modified_acc_ids.emplace(min_accumulator_id_found, std::vector { }); } } - // distribute curling + // distribute curling (add curling from neighbours under, but also decrease but some factor) if (current.volume > 0) { float curled_height = 0; for (int y_offset = -2; y_offset <= 2; ++y_offset) { @@ -385,18 +398,18 @@ public: } } current.curled_height += std::max(0.0f, - float(curled_height - unscaled(this->cell_size.z()) / 2.0f)); + float(curled_height - unscaled(this->cell_size.z()) / 1.5f)); current.curled_height = std::min(current.curled_height, float(unscaled(this->cell_size.z()) * 2.0f)); - std::cout << "Curling: " << current.curled_height << std::endl; - if (current.curled_height / unscaled(this->cell_size.z()) > 0.3f) { - modified_acc_ids[current.island_id].push_back( { x, y }); + if (current.curled_height / unscaled(this->cell_size.z()) > 1.5f) { // just a magic threshold number. + modified_acc_ids[current.accumulator_id].push_back( { x, y }); } } } } + //all cells of the grid layer checked, now further filter the modified accumulators, because multiple ids can point to the same acc filtered_active_accumulators.clear(); for (const auto &pair : modified_acc_ids) { CentroidAccumulator *acc = &accumulators.access(pair.first); @@ -411,142 +424,89 @@ public: private: void check_accumulators_stability(int z, CentroidAccumulators &accumulators, std::unordered_map> filtered_active_accumulators, - Issues &issues, const Params ¶ms) { + Issues &issues, const Params ¶ms) const { for (const auto &pair : filtered_active_accumulators) { std::cout << "Z: " << z << std::endl; CentroidAccumulator &acc = *pair.first; Vec3f centroid = acc.accumulated_value / acc.accumulated_volume; + Point base_centroid = acc.base_hull().centroid(); + Vec3f base_centroid_3d { }; + base_centroid_3d << unscale(base_centroid).cast(), acc.base_height; std::cout << "acc.accumulated_value : " << acc.accumulated_value.x() << " " << acc.accumulated_value.y() << " " << acc.accumulated_value.z() << std::endl; std::cout << "acc.accumulated_volume : " << acc.accumulated_volume << std::endl; std::cout << "centroid: " << centroid.x() << " " << centroid.y() << " " << centroid.z() << std::endl; - - Point centroid_base_projection = Point(scaled(Vec2f(centroid.head<2>()))); - Point pivot; - double distance_scaled_sq = std::numeric_limits::max(); - bool inside = true; - if (acc.base_hull().points.size() == 1) { - pivot = acc.base_hull().points[0]; - distance_scaled_sq = (pivot - centroid_base_projection).squaredNorm(); - inside = distance_scaled_sq < params.support_points_interface_area; - } else { - for (Line line : acc.base_hull().lines()) { - Point closest_point; - double dist_sq = line.distance_to_squared(centroid_base_projection, &closest_point); - if (dist_sq < distance_scaled_sq) { - pivot = closest_point; - distance_scaled_sq = dist_sq; - } - if ((centroid_base_projection - closest_point).cast().dot( - line.normal().cast()) - > 0) { - inside = false; - } - } - } - - Vec3f pivot_3d; - pivot_3d << unscale(pivot).cast(), acc.base_height; - float embedded_distance = unscaled(sqrt(distance_scaled_sq)); - float centroid_pivot_distance = (centroid - pivot_3d).norm(); - float base_center_pivot_distance = float( - unscale(Vec2crd(acc.base_hull().centroid() - pivot)).norm()); - - std::cout << "centroid inside ? " << inside << " and embedded distance is: " << embedded_distance + std::cout << "base_centroid_3d: " << base_centroid_3d.x() << " " << base_centroid_3d.y() << " " + << base_centroid_3d.z() << std::endl; - bool additional_supports_needed = false; - float sticking_force = acc.base_area - * (acc.base_height == 0 ? params.base_adhesion : params.support_adhesion); - float sticking_torque = base_center_pivot_distance * sticking_force; - - std::cout << "sticking force: " << sticking_force << " sticking torque: " << sticking_torque - << std::endl; - - float xy_movement_force = acc.accumulated_volume * params.filament_density - * params.max_acceleration; - float xy_movement_torque = xy_movement_force * centroid_pivot_distance; - - std::cout << "xy_movement_force: " << xy_movement_force << " xy_movement_torque: " - << xy_movement_torque << std::endl; - - float weight = acc.accumulated_volume * params.filament_density * params.gravity_constant; - float weight_torque = embedded_distance * weight; - if (!inside) { - weight_torque *= -1; - } - std::cout << "weight: " << weight << " weight_torque: " << weight_torque << std::endl; - - float extruder_conflict_torque = params.tolerable_extruder_conflict_force * 2.0f - * centroid_pivot_distance; - std::cout << "extruder_conflict_torque: " << extruder_conflict_torque << std::endl; - - float total_momentum = sticking_torque + weight_torque - xy_movement_torque - - extruder_conflict_torque; - additional_supports_needed = total_momentum < 0; - - std::cout << "total_momentum: " << total_momentum << std::endl; - std::cout << "additional supports needed: " << additional_supports_needed << std::endl; - - if (additional_supports_needed) { - Vec2f attractor_dir = - unscale(Vec2crd(inside ? - pivot - centroid_base_projection : - centroid_base_projection - pivot)).cast().normalized(); - Vec2f attractor = unscale(centroid_base_projection).cast() + 10000 * attractor_dir; - - std::cout << " attractor: " << attractor.x() << " | " << attractor.y() << std::endl; - - double min_dist = std::numeric_limits::max(); - Vec3f support_point = centroid; - Vec2i coords = Vec2i(0, 0); - for (int y = 0; y < global_cell_count.y(); ++y) { - for (int x = 0; x < global_cell_count.x(); ++x) { - Cell &cell = this->access_cell(Vec3i(x, y, z)); - if (cell.island_id != std::numeric_limits::max() && - &accumulators.access(cell.island_id) == &acc) { - Vec3f cell_center = - unscale(this->get_cell_center(this->to_global_cell_coords(Vec3i(x, y, z)))).cast< - float>(); - float dist_sq = (cell_center.head<2>() - attractor).squaredNorm(); - if (dist_sq < min_dist) { - min_dist = dist_sq; - support_point = cell_center; - coords = Vec2i(x, y); - } + // find the cell that is furthest from the base centroid ( its a heurstic to find a possible problems with balance without checking all layer cells) + //TODO better result are if first pivot is chosen as the closest point of the convex hull to the base centroid, and then cell furthest in the direction defined by + // the vector from base centroid to this pivot is taken. + double max_dist_sqr = 0; + Vec3f suspicious_point = centroid; + Vec2i coords = Vec2i(0, 0); + for (int y = 0; y < global_cell_count.y(); ++y) { + for (int x = 0; x < global_cell_count.x(); ++x) { + const Cell &cell = this->access_cell(Vec3i(x, y, z)); + if (cell.accumulator_id != std::numeric_limits::max() && + &accumulators.access(cell.accumulator_id) == &acc) { + Vec3f cell_center = + unscale(this->get_cell_center(this->to_global_cell_coords(Vec3i(x, y, z)))).cast< + float>(); + float dist_sq = (cell_center - base_centroid_3d).squaredNorm(); + if (dist_sq > max_dist_sqr) { + max_dist_sqr = dist_sq; + suspicious_point = cell_center; + coords = Vec2i(x, y); } } } + } - int final_height_coords = z; - while (final_height_coords > 0 - && this->access_cell(Vec3i(coords.x(), coords.y(), final_height_coords)).volume > 0) { - final_height_coords--; + // for the suspicious point, add movement force in xy (bed sliding, it is assumed that the worst direction is taken, for simplicity) + float xy_movement_force = acc.accumulated_volume * params.filament_density + * params.max_acceleration; + + std::cout << "xy_movement_force: " << xy_movement_force << std::endl; + + // also add weight (weight is the small factor, because the materials are very light. The weight torque will be computed much higher then what is real, + //since it does not push in the suspicoius point, but in centroid. Its approximation) + float weight = acc.accumulated_volume * params.filament_density * params.gravity_constant; + + std::cout << "weight: " << weight << std::endl; + + float force = this->check_point_stability_under_pressure(acc, base_centroid, suspicious_point, + xy_movement_force + weight + params.tolerable_extruder_conflict_force, + params); + if (force > 0) { + this->add_suppport_point(Vec3i(coords.x(), coords.y(), z), force, acc, issues, params); + } + + for (const Vec2i &cell : pair.second) { + Vec3f pressure_point = unscale( + this->get_cell_center( + this->to_global_cell_coords(Vec3i(cell.x(), cell.y(), z)))).cast(); + float force = this->check_point_stability_under_pressure(acc, base_centroid, pressure_point, + params.max_curled_conflict_extruder_force, //TODO add linear scaling of the extruder force based on the curled height (but current data about curled height are kind of unreliable in scale) + params); + if (force > 0) { + this->add_suppport_point(Vec3i(cell.x(), cell.y(), z), force, acc, issues, params); } - support_point.z() = unscaled( - (final_height_coords + this->local_z_index_offset) * this->cell_size.z()); - float expected_force = total_momentum / (support_point - pivot_3d).norm(); - - std::cout << " new support point: " << support_point.x() << " | " << support_point.y() << " | " - << support_point.z() << std::endl; - std::cout << " expected_force: " << expected_force << std::endl; - - issues.supports_nedded.emplace_back(support_point, expected_force); - acc.add_base_points( { Point::new_scale(Vec2f(support_point.head<2>())) }); - acc.base_area += params.support_points_interface_area; } } } float check_point_stability_under_pressure(CentroidAccumulator &acc, const Point &base_centroid, - const Vec3f &pressure_point, const Params ¶ms) { + const Vec3f &pressure_point, float pressure_force, const Params ¶ms) const { Point pressure_base_projection = Point(scaled(Vec2f(pressure_point.head<2>()))); Point pivot; double distance_scaled_sq = std::numeric_limits::max(); bool inside = true; + // find pivot, which is the closest point of the accumulator base hull to pressure point (if the object should fall, it would be over this point) if (acc.base_hull().points.size() == 1) { pivot = acc.base_hull().points[0]; distance_scaled_sq = (pivot - pressure_base_projection).squaredNorm(); @@ -565,19 +525,25 @@ private: } } } - float embedded_distance = unscaled(sqrt(distance_scaled_sq)); +// float embedded_distance = unscaled(sqrt(distance_scaled_sq)); float base_center_pivot_distance = float(unscale(Vec2crd(base_centroid - pivot)).norm()); Vec3f pivot_3d; pivot_3d << unscale(pivot).cast(), acc.base_height; + //sticking force estimated from the base area and support points float sticking_force = acc.base_area * (acc.base_height == 0 ? params.base_adhesion : params.support_adhesion); - float sticking_torque = sticking_force * base_center_pivot_distance; - float pressure_arm = inside ? pressure_point.z() - pivot_3d.z() : (pressure_point - pivot_3d).norm(); - float pressure_force = 1.0f; + std::cout << "sticking force: " << sticking_force << std::endl; + std::cout << "pressure force: " << pressure_force << std::endl; + float sticking_torque = sticking_force * base_center_pivot_distance; // sticking torque is computed from the distance to the centroid + + float pressure_arm = inside ? pressure_point.z() - pivot_3d.z() : (pressure_point - pivot_3d).norm(); // pressure arm is again higher then in reality, + // since it assumes the worst direction of the pressure force (perpendicular to the vector between pivot and pressure point) float pressure_torque = pressure_arm * pressure_force; + std::cout << "sticking_torque: " << sticking_torque << std::endl; + std::cout << "pressure_torque: " << pressure_torque << std::endl; if (sticking_torque < pressure_torque) { return pressure_force; } else { @@ -585,6 +551,27 @@ private: } } + void add_suppport_point(const Vec3i &local_coords, float expected_force, CentroidAccumulator &acc, Issues &issues, + const Params ¶ms) const { + //add support point - but first finding the lowest full cell is needed, since putting support point to the current cell may end up inside the model, + // essentially doing nothing. + int final_height_coords = local_coords.z(); + while (final_height_coords > 0 + && this->access_cell(this->to_global_cell_coords(Vec3i(local_coords.x(), local_coords.y(), final_height_coords))).volume > 0) { + final_height_coords--; + } + Vec3f support_point = unscale(this->get_cell_center(this->to_global_cell_coords(Vec3i(local_coords.x(), local_coords.y(), final_height_coords)))).cast< + float>(); + + std::cout << " new support point: " << support_point.x() << " | " << support_point.y() << " | " + << support_point.z() << std::endl; + std::cout << " expected_force: " << expected_force << std::endl; + + issues.supports_nedded.emplace_back(support_point, expected_force); + acc.add_base_points( { Point::new_scale(Vec2f(support_point.head<2>())) }); + acc.base_area += params.support_points_interface_area; + } + #ifdef DEBUG_FILES public: void debug_export() const { @@ -603,18 +590,18 @@ public: float max_volume = 0; int min_island_id = 0; int max_island_id = 0; - float max_curling_height = 0; + float max_curling_height = 0.5f; for (int x = 0; x < global_cell_count.x(); ++x) { for (int y = 0; y < global_cell_count.y(); ++y) { for (int z = 0; z < local_z_cell_count; ++z) { const Cell &cell = access_cell(Vec3i(x, y, z)); max_volume = std::max(max_volume, cell.volume); - if (cell.island_id != std::numeric_limits::max()) { - min_island_id = std::min(min_island_id, cell.island_id); - max_island_id = std::max(max_island_id, cell.island_id); + if (cell.accumulator_id != std::numeric_limits::max()) { + min_island_id = std::min(min_island_id, cell.accumulator_id); + max_island_id = std::max(max_island_id, cell.accumulator_id); } - max_curling_height = std::max(max_curling_height, cell.curled_height); +// max_curling_height = std::max(max_curling_height, cell.curled_height); } } } @@ -630,8 +617,8 @@ public: fprintf(volume_grid_file, "v %f %f %f %f %f %f\n", center(0), center(1), center(2), volume_color.x(), volume_color.y(), volume_color.z()); } - if (cell.island_id != std::numeric_limits::max()) { - auto island_color = value_to_rgbf(min_island_id, max_island_id + 1, cell.island_id); + if (cell.accumulator_id != std::numeric_limits::max()) { + auto island_color = value_to_rgbf(min_island_id, max_island_id + 1, cell.accumulator_id); fprintf(islands_grid_file, "v %f %f %f %f %f %f\n", center(0), center(1), center(2), island_color.x(), island_color.y(), island_color.z()); From ad4502d96ed8be4ebe8f9b2e90688c3d44fc59c1 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Wed, 11 May 2022 15:32:46 +0200 Subject: [PATCH 23/78] implemented AABBTree version for lines --- src/libslic3r/AABBTreeLines.hpp | 22 ++++++ src/libslic3r/CMakeLists.txt | 2 - src/libslic3r/PolygonPointTest.cpp | 1 - src/libslic3r/PolygonPointTest.hpp | 90 ----------------------- src/libslic3r/SupportableIssuesSearch.cpp | 2 +- 5 files changed, 23 insertions(+), 94 deletions(-) delete mode 100644 src/libslic3r/PolygonPointTest.cpp delete mode 100644 src/libslic3r/PolygonPointTest.hpp diff --git a/src/libslic3r/AABBTreeLines.hpp b/src/libslic3r/AABBTreeLines.hpp index 7b9595419a..7eb7ab56a4 100644 --- a/src/libslic3r/AABBTreeLines.hpp +++ b/src/libslic3r/AABBTreeLines.hpp @@ -8,6 +8,28 @@ namespace Slic3r { + +struct EdgeGridWrapper { + EdgeGridWrapper(coord_t edge_width, std::vector lines) : + lines(lines), edge_width(edge_width) { + + grid.create(this->lines, edge_width, true); + grid.calculate_sdf(); + } + + bool signed_distance(const Point &point, coordf_t point_width, coordf_t &dist_out) const { + coordf_t tmp_dist_out; + bool found = grid.signed_distance(point, edge_width, tmp_dist_out); + dist_out = tmp_dist_out - edge_width / 2 - point_width / 2; + return found; + } + + EdgeGrid::Grid grid; + std::vector lines; + coord_t edge_width; +}; + + namespace AABBTreeLines { namespace detail { diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 443c81b486..0a0062a754 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -201,8 +201,6 @@ set(SLIC3R_SOURCES Point.hpp Polygon.cpp Polygon.hpp - PolygonPointTest.cpp - PolygonPointTest.hpp MutablePolygon.cpp MutablePolygon.hpp PolygonTrimmer.cpp diff --git a/src/libslic3r/PolygonPointTest.cpp b/src/libslic3r/PolygonPointTest.cpp deleted file mode 100644 index dcab9e87eb..0000000000 --- a/src/libslic3r/PolygonPointTest.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "PolygonPointTest.hpp" diff --git a/src/libslic3r/PolygonPointTest.hpp b/src/libslic3r/PolygonPointTest.hpp deleted file mode 100644 index d95b191bf6..0000000000 --- a/src/libslic3r/PolygonPointTest.hpp +++ /dev/null @@ -1,90 +0,0 @@ -#ifndef SRC_LIBSLIC3R_POLYGONPOINTTEST_HPP_ -#define SRC_LIBSLIC3R_POLYGONPOINTTEST_HPP_ - -#include "libslic3r/Point.hpp" -#include "libslic3r/EdgeGrid.hpp" - -namespace Slic3r { - -struct EdgeGridWrapper { - EdgeGridWrapper(coord_t edge_width, std::vector lines) : - lines(lines), edge_width(edge_width) { - - grid.create(this->lines, edge_width, true); - grid.calculate_sdf(); - } - - bool signed_distance(const Point &point, coordf_t point_width, coordf_t &dist_out) const { - coordf_t tmp_dist_out; - bool found = grid.signed_distance(point, edge_width, tmp_dist_out); - dist_out = tmp_dist_out - edge_width / 2 - point_width / 2; - return found; - - } - - EdgeGrid::Grid grid; - std::vector lines; - coord_t edge_width; -}; - -namespace TODO { - -class PolygonPointTest { - - struct Segment { - coord_t start; - std::vector lines; - }; - - std::vector x_coord_segments; - -public: - PolygonPointTest(const ExPolygons &ex_polygons) { - std::vector lines; - for (const auto &exp : ex_polygons) { - Lines contour = exp.contour.lines(); - lines.insert(lines.end(), contour.begin(), contour.end()); - for (const auto &hole : exp.holes) { - Lines hole_lines = hole.lines(); - for (Line &line : hole_lines) { - line.reverse(); // reverse hole lines, so that we can use normal to deduce where the object is - } - lines.insert(lines.end(), hole_lines.begin(), hole_lines.end()); - } - } - - std::vector> sweeping_data(lines.size() * 2); - for (size_t line_index = 0; line_index < lines.size(); ++line_index) { - sweeping_data[line_index].first = line_index; - sweeping_data[line_index].second = true; - sweeping_data[line_index * 2 + 1].first = line_index; - sweeping_data[line_index * 2 + 1].second = false; - } - - const auto data_comparator = [&lines](const std::pair &left, - const std::pair &right) { - const auto left_x = - left.second ? - std::min(lines[left.first].a.x(), lines[left.first].b.x()) : - std::max(lines[left.first].a.x(), lines[left.first].b.x()); - const auto right_x = - right.second ? - std::min(lines[right.first].a.x(), lines[right.first].b.x()) : - std::max(lines[right.first].a.x(), lines[right.first].b.x()); - - return left_x < right_x; - }; - - std::sort(sweeping_data.begin(), sweeping_data.end(), data_comparator); - std::set active_lines; - - //TODO continue - - } - -}; -} - -} - -#endif /* SRC_LIBSLIC3R_POLYGONPOINTTEST_HPP_ */ diff --git a/src/libslic3r/SupportableIssuesSearch.cpp b/src/libslic3r/SupportableIssuesSearch.cpp index 5e3e1822de..d8ff5b7172 100644 --- a/src/libslic3r/SupportableIssuesSearch.cpp +++ b/src/libslic3r/SupportableIssuesSearch.cpp @@ -8,10 +8,10 @@ #include #include +#include "AABBTreeLines.hpp" #include "libslic3r/Layer.hpp" #include "libslic3r/ClipperUtils.hpp" #include "Geometry/ConvexHull.hpp" -#include "PolygonPointTest.hpp" #define DEBUG_FILES From 51d738c5642ff78c5d0dcb74bc8e37bd493f4d56 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Thu, 12 May 2022 14:13:23 +0200 Subject: [PATCH 24/78] refactored algorithm to use AABB tree instead of edge grid --- src/libslic3r/AABBTreeLines.hpp | 24 ----- src/libslic3r/SupportableIssuesSearch.cpp | 117 ++++++++++++++-------- 2 files changed, 74 insertions(+), 67 deletions(-) diff --git a/src/libslic3r/AABBTreeLines.hpp b/src/libslic3r/AABBTreeLines.hpp index 7eb7ab56a4..39f828b558 100644 --- a/src/libslic3r/AABBTreeLines.hpp +++ b/src/libslic3r/AABBTreeLines.hpp @@ -1,35 +1,11 @@ #ifndef SRC_LIBSLIC3R_AABBTREELINES_HPP_ #define SRC_LIBSLIC3R_AABBTREELINES_HPP_ -#include "libslic3r/Point.hpp" -#include "libslic3r/EdgeGrid.hpp" #include "libslic3r/AABBTreeIndirect.hpp" #include "libslic3r/Line.hpp" namespace Slic3r { - -struct EdgeGridWrapper { - EdgeGridWrapper(coord_t edge_width, std::vector lines) : - lines(lines), edge_width(edge_width) { - - grid.create(this->lines, edge_width, true); - grid.calculate_sdf(); - } - - bool signed_distance(const Point &point, coordf_t point_width, coordf_t &dist_out) const { - coordf_t tmp_dist_out; - bool found = grid.signed_distance(point, edge_width, tmp_dist_out); - dist_out = tmp_dist_out - edge_width / 2 - point_width / 2; - return found; - } - - EdgeGrid::Grid grid; - std::vector lines; - coord_t edge_width; -}; - - namespace AABBTreeLines { namespace detail { diff --git a/src/libslic3r/SupportableIssuesSearch.cpp b/src/libslic3r/SupportableIssuesSearch.cpp index d8ff5b7172..55d883f0a9 100644 --- a/src/libslic3r/SupportableIssuesSearch.cpp +++ b/src/libslic3r/SupportableIssuesSearch.cpp @@ -557,11 +557,17 @@ private: // essentially doing nothing. int final_height_coords = local_coords.z(); while (final_height_coords > 0 - && this->access_cell(this->to_global_cell_coords(Vec3i(local_coords.x(), local_coords.y(), final_height_coords))).volume > 0) { + && this->access_cell( + this->to_global_cell_coords(Vec3i(local_coords.x(), local_coords.y(), final_height_coords))).volume + > 0) { final_height_coords--; } - Vec3f support_point = unscale(this->get_cell_center(this->to_global_cell_coords(Vec3i(local_coords.x(), local_coords.y(), final_height_coords)))).cast< - float>(); + Vec3f support_point = + unscale( + this->get_cell_center( + this->to_global_cell_coords( + Vec3i(local_coords.x(), local_coords.y(), final_height_coords)))).cast< + float>(); std::cout << " new support point: " << support_point.x() << " | " << support_point.y() << " | " << support_point.z() << std::endl; @@ -683,51 +689,76 @@ void debug_export(Issues issues, std::string file_name) { } #endif -EdgeGridWrapper compute_layer_edge_grid(const Layer *layer) { - float min_region_flow_width { 1.0f }; - for (const auto *region : layer->regions()) { - min_region_flow_width = std::min(min_region_flow_width, - region->flow(FlowRole::frExternalPerimeter).width()); - } - std::vector lines; - for (const LayerRegion *layer_region : layer->regions()) { - for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { - lines.push_back(Points { }); - ex_entity->collect_points(lines.back()); - } // ex_entity +struct LayerLinesDistancer { + std::vector lines; + AABBTreeIndirect::Tree<2, double> tree; + float line_width; - for (const ExtrusionEntity *ex_entity : layer_region->fills.entities) { - lines.push_back(Points { }); - ex_entity->collect_points(lines.back()); - } // ex_entity + LayerLinesDistancer(const Layer *layer) { + float min_region_flow_width { 1.0f }; + for (const auto *region : layer->regions()) { + min_region_flow_width = std::min(min_region_flow_width, + region->flow(FlowRole::frExternalPerimeter).width()); + } + for (const LayerRegion *layer_region : layer->regions()) { + for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { + Points points { }; + ex_entity->collect_points(points); + Vec2d prev = points.size() > 0 ? unscaled(points.front()) : Vec2d::Zero(); + for (size_t idx = 1; idx < points.size(); ++idx) { + lines.emplace_back(prev, unscaled(points[idx])); + } + } // ex_entity + + for (const ExtrusionEntity *ex_entity : layer_region->fills.entities) { + Points points { }; + ex_entity->collect_points(points); + Vec2d prev = points.size() > 0 ? unscaled(points.front()) : Vec2d::Zero(); + for (size_t idx = 1; idx < points.size(); ++idx) { + lines.emplace_back(prev, unscaled(points[idx])); + } + } // ex_entity + } + tree = AABBTreeLines::build_aabb_tree_over_indexed_lines(lines); + line_width = min_region_flow_width; } - return EdgeGridWrapper(scale_(min_region_flow_width), lines); -} + float distance_from_lines(const Point &point, float point_width) const { + Vec2d p = unscaled(point); + size_t hit_idx_out; + Vec2d hit_point_out; + auto distance = AABBTreeLines::squared_distance_to_indexed_lines(lines, tree, p, hit_idx_out, hit_point_out); + if (distance < 0) + return distance; + + distance = sqrt(distance); + return std::max(float(distance - point_width / 2 - line_width / 20), 0.0f); + } +}; //TODO needs revision -coordf_t get_flow_width(const LayerRegion *region, ExtrusionRole role) { +float get_flow_width(const LayerRegion *region, ExtrusionRole role) { switch (role) { case ExtrusionRole::erBridgeInfill: - return region->flow(FlowRole::frExternalPerimeter).scaled_width(); + return region->flow(FlowRole::frExternalPerimeter).width(); case ExtrusionRole::erExternalPerimeter: - return region->flow(FlowRole::frExternalPerimeter).scaled_width(); + return region->flow(FlowRole::frExternalPerimeter).width(); case ExtrusionRole::erGapFill: - return region->flow(FlowRole::frInfill).scaled_width(); + return region->flow(FlowRole::frInfill).width(); case ExtrusionRole::erPerimeter: - return region->flow(FlowRole::frPerimeter).scaled_width(); + return region->flow(FlowRole::frPerimeter).width(); case ExtrusionRole::erSolidInfill: - return region->flow(FlowRole::frSolidInfill).scaled_width(); + return region->flow(FlowRole::frSolidInfill).width(); case ExtrusionRole::erInternalInfill: - return region->flow(FlowRole::frInfill).scaled_width(); + return region->flow(FlowRole::frInfill).width(); case ExtrusionRole::erTopSolidInfill: - return region->flow(FlowRole::frTopSolidInfill).scaled_width(); + return region->flow(FlowRole::frTopSolidInfill).width(); default: - return region->flow(FlowRole::frPerimeter).scaled_width(); + return region->flow(FlowRole::frPerimeter).width(); } } -coordf_t get_max_allowed_distance(ExtrusionRole role, coordf_t flow_width, bool external_perimeters_first, +float get_max_allowed_distance(ExtrusionRole role, float flow_width, bool external_perimeters_first, const Params ¶ms) { // <= distance / flow_width (can be larger for perimeter, if not external perimeter first) if ((role == ExtrusionRole::erExternalPerimeter || role == ExtrusionRole::erOverhangPerimeter) && (external_perimeters_first)) { @@ -761,13 +792,13 @@ struct SegmentAccumulator { Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, float print_z, const LayerRegion *layer_region, - const EdgeGridWrapper &supported_grid, const Params ¶ms) { + const LayerLinesDistancer &support_layer, const Params ¶ms) { Issues issues { }; if (entity->is_collection()) { for (const auto *e : static_cast(entity)->entities) { issues.add( - check_extrusion_entity_stability(e, print_z, layer_region, supported_grid, params)); + check_extrusion_entity_stability(e, print_z, layer_region, support_layer, params)); } } else { //single extrusion path, with possible varying parameters //prepare stack of points on the extrusion path. If there are long segments, additional points might be pushed onto the stack during the algorithm. @@ -788,9 +819,9 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, float pri Point prev_point = points.top(); // prev point of the path. Initialize with first point. Vec3f prev_fpoint = to_vec3f(prev_point); - coordf_t flow_width = get_flow_width(layer_region, entity->role()); + float flow_width = get_flow_width(layer_region, entity->role()); bool external_perimters_first = layer_region->region().config().external_perimeters_first; - const coordf_t max_allowed_dist_from_prev_layer = get_max_allowed_distance(entity->role(), flow_width, + const float max_allowed_dist_from_prev_layer = get_max_allowed_distance(entity->role(), flow_width, external_perimters_first, params); while (!points.empty()) { @@ -799,8 +830,8 @@ Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, float pri Vec3f fpoint = to_vec3f(point); float edge_len = (fpoint - prev_fpoint).norm(); - coordf_t dist_from_prev_layer { 0 }; - if (!supported_grid.signed_distance(point, flow_width, dist_from_prev_layer)) { // dist from prev layer not found, assume empty layer + float dist_from_prev_layer = support_layer.distance_from_lines(point, flow_width); + if (dist_from_prev_layer < 0) { // dist from prev layer not found, assume empty layer issues.supports_nedded.push_back(SupportPoint(fpoint, 1.0f)); supports_acc.reset(); } @@ -867,7 +898,7 @@ void distribute_layer_volume(const PrintObject *po, size_t layer_idx, for (const ExtrusionEntity *entity : static_cast(collections)->entities) { for (const Line &line : entity->as_polyline().lines()) { balance_grid.distribute_edge(line.a, line.b, layer->print_z, - unscale(get_flow_width(region, entity->role())), layer->height); + get_flow_width(region, entity->role()), layer->height); } } } @@ -875,7 +906,7 @@ void distribute_layer_volume(const PrintObject *po, size_t layer_idx, for (const ExtrusionEntity *entity : static_cast(collections)->entities) { for (const Line &line : entity->as_polyline().lines()) { balance_grid.distribute_edge(line.a, line.b, layer->print_z, - unscale(get_flow_width(region, entity->role())), layer->height); + get_flow_width(region, entity->role()), layer->height); } } } @@ -886,7 +917,7 @@ Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_ const Params ¶ms) { const Layer *layer = po->get_layer(layer_idx); //Prepare edge grid of previous layer, will be used to check if the extrusion path is supported - EdgeGridWrapper supported_grid = compute_layer_edge_grid(layer->lower_layer); + LayerLinesDistancer support_layer(layer->lower_layer); Issues issues { }; if (full_check) { // If full check; check stability of perimeters, gap fills, and bridges. @@ -895,7 +926,7 @@ Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_ for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { issues.add( check_extrusion_entity_stability(perimeter, layer->print_z, layer_region, - supported_grid, params)); + support_layer, params)); } // perimeter } // ex_entity for (const ExtrusionEntity *ex_entity : layer_region->fills.entities) { @@ -904,7 +935,7 @@ Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_ || fill->role() == ExtrusionRole::erBridgeInfill) { issues.add( check_extrusion_entity_stability(fill, layer->print_z, layer_region, - supported_grid, + support_layer, params)); } } // fill @@ -919,7 +950,7 @@ Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_ || perimeter->role() == ExtrusionRole::erOverhangPerimeter) { issues.add( check_extrusion_entity_stability(perimeter, layer->print_z, layer_region, - supported_grid, params)); + support_layer, params)); }; // ex_perimeter } // perimeter } // ex_entity From 30f072457f8325c05235ea8bb4b1bb3b324aedbc Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Fri, 17 Jun 2022 12:11:12 +0200 Subject: [PATCH 25/78] Refactored version without voxel grid, init commit --- src/libslic3r/CMakeLists.txt | 3 +- src/libslic3r/PrintObject.cpp | 2 +- .../SupportableIssuesSearchRefactoring.cpp | 514 ++++++++++++++++++ 3 files changed, 517 insertions(+), 2 deletions(-) create mode 100644 src/libslic3r/SupportableIssuesSearchRefactoring.cpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 0a0062a754..e866ac34e5 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -245,7 +245,8 @@ set(SLIC3R_SOURCES SlicingAdaptive.hpp Subdivide.cpp Subdivide.hpp - SupportableIssuesSearch.cpp +# SupportableIssuesSearch.cpp + SupportableIssuesSearchRefactoring.cpp SupportableIssuesSearch.hpp SupportMaterial.cpp SupportMaterial.hpp diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index ee311b5610..f0e435dea8 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -431,7 +431,7 @@ void PrintObject::find_supportable_issues() TriangleSelectorWrapper selector { model_volume->mesh() }; for (const SupportableIssues::SupportPoint &support_point : issues.supports_nedded) { - selector.enforce_spot(Vec3f(inv_transform.cast() * support_point.position), 2.0f); + selector.enforce_spot(Vec3f(inv_transform.cast() * support_point.position), 0.5f); } model_volume->supported_facets.set(selector.selector); diff --git a/src/libslic3r/SupportableIssuesSearchRefactoring.cpp b/src/libslic3r/SupportableIssuesSearchRefactoring.cpp new file mode 100644 index 0000000000..b988976bb5 --- /dev/null +++ b/src/libslic3r/SupportableIssuesSearchRefactoring.cpp @@ -0,0 +1,514 @@ +#include "SupportableIssuesSearch.hpp" + +#include "tbb/parallel_for.h" +#include "tbb/blocked_range.h" +#include "tbb/parallel_reduce.h" +#include +#include +#include +#include + +#include "AABBTreeLines.hpp" +#include "libslic3r/Layer.hpp" +#include "libslic3r/ClipperUtils.hpp" +#include "Geometry/ConvexHull.hpp" + +#define DEBUG_FILES + +#ifdef DEBUG_FILES +#include +#include "libslic3r/Color.hpp" +#endif + +namespace Slic3r { + +static const size_t NULL_ACC_ID = std::numeric_limits::max(); + +class ExtrusionLine +{ +public: + ExtrusionLine() : + a(Vec2f::Zero()), b(Vec2f::Zero()), len(0.0f) { + } + ExtrusionLine(const Vec2f &_a, const Vec2f &_b) : + a(_a), b(_b), len((_a - _b).norm()) { + } + + float length() { + return (a - b).norm(); + } + + Vec2f a; + Vec2f b; + float len; + + size_t supported_segment_accumulator_id = NULL_ACC_ID; + + static const constexpr int Dim = 2; + using Scalar = Vec2f::Scalar; +}; + +auto get_a(ExtrusionLine &&l) { + return l.a; +} +auto get_b(ExtrusionLine &&l) { + return l.b; +} + +namespace SupportableIssues { + +void Issues::add(const Issues &layer_issues) { + supports_nedded.insert(supports_nedded.end(), layer_issues.supports_nedded.begin(), + layer_issues.supports_nedded.end()); + curling_up.insert(curling_up.end(), layer_issues.curling_up.begin(), layer_issues.curling_up.end()); +} + +bool Issues::empty() const { + return supports_nedded.empty() && curling_up.empty(); +} + +SupportPoint::SupportPoint(const Vec3f &position, float weight) : + position(position), weight(weight) { +} + +CurledFilament::CurledFilament(const Vec3f &position, float estimated_height) : + position(position), estimated_height(estimated_height) { +} + +CurledFilament::CurledFilament(const Vec3f &position) : + position(position), estimated_height(0.0f) { +} + +class LayerLinesDistancer { +private: + std::vector lines; + AABBTreeIndirect::Tree<2, float> tree; + +public: + explicit LayerLinesDistancer(std::vector &&lines) : + lines(lines) { + tree = AABBTreeLines::build_aabb_tree_over_indexed_lines(lines); + } + + // negative sign means inside + float signed_distance_from_lines(const Point &point, size_t &nearest_line_index_out, + Vec2f &nearest_point_out) const { + Vec2f p = unscaled(point).cast(); + auto distance = AABBTreeLines::squared_distance_to_indexed_lines(lines, tree, p, nearest_line_index_out, + nearest_point_out); + if (distance < 0) + return std::numeric_limits::infinity(); + + distance = sqrt(distance); + const ExtrusionLine &line = lines[nearest_line_index_out]; + Vec2f v1 = line.b - line.a; + Vec2f v2 = p - line.a; + if ((v1.x() * v2.y()) - (v1.y() * v2.x()) > 0.0) { + distance *= -1; + } + return distance; + } + + const ExtrusionLine& get_line(size_t line_idx) const { + return lines[line_idx]; + } +}; + +class SupportedSegmentAccumulator { +private: + Polygon base_convex_hull { }; + Points supported_points { }; + Vec3f centroid_accumulator = Vec3f::Zero(); + float accumulated_volume { }; + float base_area { }; + float base_height { }; + +public: + explicit SupportedSegmentAccumulator(float base_height) : + base_height(base_height) { + } + + void add_base_extrusion(const ExtrusionLine &line, float width, float print_z, float cross_section) { + base_area += line.len * width; + supported_points.push_back(Point::new_scale(line.a)); + supported_points.push_back(Point::new_scale(line.b)); + base_convex_hull.clear(); + add_extrusion(line, print_z, cross_section); + } + + void add_support_point(const Point &position, float area) { + supported_points.push_back(position); + base_convex_hull.clear(); + base_area += area; + } + + void add_extrusion(const ExtrusionLine &line, float print_z, float cross_section) { + float volume = line.len * cross_section; + accumulated_volume += volume; + Vec2f center = (line.a + line.b) / 2.0f; + centroid_accumulator += volume * Vec3f(center.x(), center.y(), print_z); + } + + const Polygon& segment_base_hull() { + if (this->base_convex_hull.empty()) { + this->base_convex_hull = Geometry::convex_hull(this->supported_points); + } + return this->base_convex_hull; + } + + void add_from(const SupportedSegmentAccumulator &acc) { + this->supported_points.insert(this->supported_points.end(), acc.supported_points.begin(), + acc.supported_points.end()); + base_convex_hull.clear(); + this->centroid_accumulator += acc.centroid_accumulator; + this->accumulated_volume += acc.accumulated_volume; + this->base_area += acc.base_area; + } + + bool check_stability() { + return true; + } +}; + +struct SupportedSegmentAccumulators { +private: + size_t next_id = 0; + std::unordered_map mapping; + std::vector acccumulators; + + void merge_to(size_t from_id, size_t to_id) { + SupportedSegmentAccumulator &from_acc = this->access(from_id); + SupportedSegmentAccumulator &to_acc = this->access(to_id); + if (&from_acc == &to_acc) { + return; + } + to_acc.add_from(from_acc); + mapping[from_id] = mapping[to_id]; + from_acc = SupportedSegmentAccumulator { 0.0f }; + + } + +public: + SupportedSegmentAccumulators() = default; + + int create_accumulator(float base_height) { + size_t id = next_id; + next_id++; + mapping[id] = acccumulators.size(); + acccumulators.push_back(SupportedSegmentAccumulator { base_height }); + return id; + } + + SupportedSegmentAccumulator& access(size_t id) { + return acccumulators[mapping[id]]; + } + + void merge_accumulators(size_t from_id, size_t to_id) { + if (from_id == NULL_ACC_ID || to_id == NULL_ACC_ID) { + return; + } + SupportedSegmentAccumulator &from_acc = this->access(from_id); + SupportedSegmentAccumulator &to_acc = this->access(to_id); + if (&from_acc == &to_acc) { + return; + } + to_acc.add_from(from_acc); + mapping[from_id] = mapping[to_id]; + from_acc = SupportedSegmentAccumulator { 0.0f }; + } + + std::unordered_set get_active_acc_indices() const { + std::unordered_set result; + for (const auto &pair : mapping) { + result.insert(pair.second); + } + return result; + } +}; + +float get_flow_width(const LayerRegion *region, ExtrusionRole role) { + switch (role) { + case ExtrusionRole::erBridgeInfill: + return region->flow(FlowRole::frExternalPerimeter).width(); + case ExtrusionRole::erExternalPerimeter: + return region->flow(FlowRole::frExternalPerimeter).width(); + case ExtrusionRole::erGapFill: + return region->flow(FlowRole::frInfill).width(); + case ExtrusionRole::erPerimeter: + return region->flow(FlowRole::frPerimeter).width(); + case ExtrusionRole::erSolidInfill: + return region->flow(FlowRole::frSolidInfill).width(); + case ExtrusionRole::erInternalInfill: + return region->flow(FlowRole::frInfill).width(); + case ExtrusionRole::erTopSolidInfill: + return region->flow(FlowRole::frTopSolidInfill).width(); + default: + return region->flow(FlowRole::frPerimeter).width(); + } +} + +float get_max_allowed_distance(ExtrusionRole role, float flow_width, bool external_perimeters_first, + const Params ¶ms) { // <= distance / flow_width (can be larger for perimeter, if not external perimeter first) + if ((role == ExtrusionRole::erExternalPerimeter || role == ExtrusionRole::erOverhangPerimeter) + && (external_perimeters_first)) { + return params.max_first_ex_perim_unsupported_distance_factor * flow_width; + } else { + return params.max_unsupported_distance_factor * flow_width; + } +} + +struct ExtrusionPropertiesAccumulator { + float distance = 0; //accumulated distance + float curvature = 0; //accumulated signed ccw angles + float max_curvature = 0; //max absolute accumulated value + + void add_distance(float dist) { + distance += dist; + } + + void add_angle(float ccw_angle) { + curvature += ccw_angle; + max_curvature = std::max(max_curvature, std::abs(curvature)); + } + + void reset() { + distance = 0; + curvature = 0; + max_curvature = 0; + } +}; + +void check_extrusion_entity_stability(const ExtrusionEntity *entity, + SupportedSegmentAccumulators &stability_accs, + Issues &issues, + std::vector &checked_lines, + float print_z, + const LayerRegion *layer_region, + const LayerLinesDistancer &prev_layer_lines, + const Params ¶ms) { + + if (entity->is_collection()) { + for (const auto *e : static_cast(entity)->entities) { + check_extrusion_entity_stability(e, stability_accs, issues, checked_lines, print_z, layer_region, + prev_layer_lines, + params); + } + } else { //single extrusion path, with possible varying parameters + const auto to_vec3f = [print_z](const Point &point) { + Vec2f tmp = unscale(point).cast(); + return Vec3f(tmp.x(), tmp.y(), print_z); + }; + Points points { }; + entity->collect_points(points); + std::vector lines; + lines.reserve(points.size() * 1.5); + lines.emplace_back(unscaled(points[0]).cast(), unscaled(points[0]).cast()); + for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { + Vec2f start = unscaled(points[point_idx]).cast(); + Vec2f next = unscaled(points[point_idx]).cast(); + Vec2f v = next - start; // vector from next to current + float dist_to_next = v.norm(); + v.normalize(); + int lines_count = int(std::ceil(dist_to_next / params.bridge_distance)); + float step_size = dist_to_next / lines_count; + for (int i = 0; i < lines_count; ++i) { + Vec2f a(start + v * (i * step_size)); + Vec2f b(start + v * ((i + 1) * step_size)); + lines.emplace_back(a, b); + } + } + + checked_lines.insert(checked_lines.end(), lines.begin(), lines.end()); + + size_t current_stability_acc = NULL_ACC_ID; + ExtrusionPropertiesAccumulator bridging_acc { }; + bridging_acc.add_distance(params.bridge_distance + 1.0f); // Initialise unsupported distance with larger than tolerable distance -> + // -> it prevents extruding perimeter start and short loops into air. + const float flow_width = get_flow_width(layer_region, entity->role()); + const float region_height = layer_region->layer()->height; + const float max_allowed_dist_from_prev_layer = get_max_allowed_distance(entity->role(), flow_width, + layer_region->region().config().external_perimeters_first, params); + + for (size_t line_idx = 0; line_idx < lines.size(); ++line_idx) { + ExtrusionLine current_line = lines[line_idx]; + Point current = Point::new_scale(current_line.b); + float cross_section = region_height * flow_width * 0.7071f; + + float angle = 0; + if (line_idx + 1 < lines.size()) { + const Vec2f v1 = current_line.b - current_line.a; + const Vec2f v2 = lines[line_idx + 1].b - lines[line_idx + 1].a; + float dot = v1(0) * v2(0) + v1(1) * v2(1); + float cross = v1(0) * v2(1) - v1(1) * v2(0); + angle = float(atan2(float(cross), float(dot))); // ccw angle, TODO replace with angle func, once it gets into master + } + bridging_acc.add_angle(angle); + + size_t nearest_line_idx; + Vec2f nearest_point; + float dist_from_prev_layer = prev_layer_lines.signed_distance_from_lines(current, nearest_line_idx, + nearest_point); + if (dist_from_prev_layer - flow_width < max_allowed_dist_from_prev_layer) { + const ExtrusionLine &nearest_line = prev_layer_lines.get_line(nearest_line_idx); + size_t acc_id = nearest_line.supported_segment_accumulator_id; + stability_accs.merge_accumulators(std::max(acc_id, current_stability_acc), + std::min(acc_id, current_stability_acc)); + current_stability_acc = std::min(acc_id, current_stability_acc); + current_line.supported_segment_accumulator_id = current_stability_acc; + stability_accs.access(current_stability_acc).add_extrusion(current_line, print_z, cross_section); + bridging_acc.reset(); + // TODO curving here + } else { + bridging_acc.add_distance(current_line.len); + if (current_stability_acc < 0) { + size_t acc_id = stability_accs.create_accumulator(print_z); + stability_accs.merge_accumulators(std::max(acc_id, current_stability_acc), + std::min(acc_id, current_stability_acc)); + current_stability_acc = std::min(acc_id, current_stability_acc); + } + SupportedSegmentAccumulator ¤t_segment = stability_accs.access(current_stability_acc); + current_segment.add_extrusion(current_line, print_z, cross_section); + if (bridging_acc.distance // if unsupported distance is larger than bridge distance linearly decreased by curvature, enforce supports. + > params.bridge_distance + / (1.0f + (bridging_acc.max_curvature + * params.bridge_distance_decrease_by_curvature_factor / PI))) { + current_segment.add_support_point(current, 5.0f); + issues.supports_nedded.emplace_back(to_vec3f(current), 1.0); + bridging_acc.reset(); + } + } + } + } +} + +Issues check_object_stability(const PrintObject *po, const Params ¶ms) { + SupportedSegmentAccumulators stability_accs; + LayerLinesDistancer prev_layer_lines { { } }; + Issues issues { }; + std::vector checked_lines; + + const Layer *layer = po->layers()[0]; + float base_print_z = layer->print_z; + for (const LayerRegion *layer_region : layer->regions()) { + for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { + for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { + const float flow_width = get_flow_width(layer_region, perimeter->role()); + const float region_height = layer_region->layer()->height; + const float cross_section = region_height * flow_width * 0.7071f; + int id = stability_accs.create_accumulator(base_print_z); + SupportedSegmentAccumulator &acc = stability_accs.access(id); + Points points { }; + perimeter->collect_points(points); + for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { + Vec2f start = unscaled(points[point_idx]).cast(); + Vec2f next = unscaled(points[point_idx]).cast(); + ExtrusionLine line{start, next}; + line.supported_segment_accumulator_id = id; + acc.add_base_extrusion( line, flow_width, base_print_z, cross_section); + checked_lines.push_back(line); + } + } // perimeter + } // ex_entity + for (const ExtrusionEntity *ex_entity : layer_region->fills.entities) { + for (const ExtrusionEntity *fill : static_cast(ex_entity)->entities) { + const float flow_width = get_flow_width(layer_region, fill->role()); + const float region_height = layer_region->layer()->height; + const float cross_section = region_height * flow_width * 0.7071f; + int id = stability_accs.create_accumulator(base_print_z); + SupportedSegmentAccumulator &acc = stability_accs.access(id); + Points points { }; + fill->collect_points(points); + for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { + Vec2f start = unscaled(points[point_idx]).cast(); + Vec2f next = unscaled(points[point_idx]).cast(); + acc.add_base_extrusion( { start, next }, flow_width, base_print_z, cross_section); + } + } // fill + } // ex_entity + } // region + + for (size_t layer_idx = 1; layer_idx < po->layer_count(); ++layer_idx) { + const Layer *layer = po->layers()[layer_idx]; + prev_layer_lines = LayerLinesDistancer{std::move(checked_lines)}; + checked_lines = std::vector{}; + + float print_z = layer->print_z; + for (const LayerRegion *layer_region : layer->regions()) { + for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { + for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { + check_extrusion_entity_stability(perimeter, stability_accs, issues, checked_lines, print_z, + layer_region, + prev_layer_lines, params); + } // perimeter + } // ex_entity + for (const ExtrusionEntity *ex_entity : layer_region->fills.entities) { + for (const ExtrusionEntity *fill : static_cast(ex_entity)->entities) { + if (fill->role() == ExtrusionRole::erGapFill + || fill->role() == ExtrusionRole::erBridgeInfill) { + check_extrusion_entity_stability(fill, stability_accs, issues, checked_lines, print_z, + layer_region, + prev_layer_lines, params); + } + } // fill + } // ex_entity + } // region + } + + std::cout << " SUPP: " << issues.supports_nedded.size() << std::endl; + return issues; +} + + +#ifdef DEBUG_FILES +void debug_export(Issues issues, std::string file_name) { + Slic3r::CNumericLocalesSetter locales_setter; + + { + FILE *fp = boost::nowide::fopen(debug_out_path((file_name + "_supports.obj").c_str()).c_str(), "w"); + if (fp == nullptr) { + BOOST_LOG_TRIVIAL(error) + << "Debug files: Couldn't open " << file_name << " for writing"; + return; + } + + for (size_t i = 0; i < issues.supports_nedded.size(); ++i) { + fprintf(fp, "v %f %f %f %f %f %f\n", issues.supports_nedded[i].position(0), + issues.supports_nedded[i].position(1), + issues.supports_nedded[i].position(2), 1.0, 0.0, 1.0); + } + + fclose(fp); + } + { + FILE *fp = boost::nowide::fopen(debug_out_path((file_name + "_curling.obj").c_str()).c_str(), "w"); + if (fp == nullptr) { + BOOST_LOG_TRIVIAL(error) + << "Debug files: Couldn't open " << file_name << " for writing"; + return; + } + + for (size_t i = 0; i < issues.curling_up.size(); ++i) { + fprintf(fp, "v %f %f %f %f %f %f\n", issues.curling_up[i].position(0), + issues.curling_up[i].position(1), + issues.curling_up[i].position(2), 0.0, 1.0, 0.0); + } + fclose(fp); + } + +} +#endif + +std::vector quick_search(const PrintObject *po, const Params ¶ms) { + check_object_stability(po, params); + return {}; +} + +Issues full_search(const PrintObject *po, const Params ¶ms) { + auto issues = check_object_stability(po, params); + debug_export(issues, "issues"); + return issues; + +} +} //SupportableIssues End +} + From 08071d85ee889071cd4060a17bd88b97c660e88b Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Fri, 17 Jun 2022 17:41:48 +0200 Subject: [PATCH 26/78] integration of the simple physical model into the refactored version --- src/libslic3r/SupportableIssuesSearch.hpp | 2 - .../SupportableIssuesSearchRefactoring.cpp | 277 +++++++++++++----- 2 files changed, 199 insertions(+), 80 deletions(-) diff --git a/src/libslic3r/SupportableIssuesSearch.hpp b/src/libslic3r/SupportableIssuesSearch.hpp index 1e90e8cde0..d5736a3bde 100644 --- a/src/libslic3r/SupportableIssuesSearch.hpp +++ b/src/libslic3r/SupportableIssuesSearch.hpp @@ -12,8 +12,6 @@ struct Params { float bridge_distance = 10.0f; //mm - float max_first_ex_perim_unsupported_distance_factor = 0.0f; // if external perim first, return tighter max allowed distance from previous layer extrusion - float max_unsupported_distance_factor = 1.0f; // For internal perimeters, infill, bridges etc, allow gap of [extrusion width] size, these extrusions have usually something to stick to. float bridge_distance_decrease_by_curvature_factor = 5.0f; // allowed bridge distance = bridge_distance / ( 1 + this factor * (curvature / PI) ) float base_adhesion = 2000.0f; // adhesion per mm^2 of first layer; Force needed to remove the object from the bed, divided by the adhesion area (g/mm*s^2) diff --git a/src/libslic3r/SupportableIssuesSearchRefactoring.cpp b/src/libslic3r/SupportableIssuesSearchRefactoring.cpp index b988976bb5..35b567c55e 100644 --- a/src/libslic3r/SupportableIssuesSearchRefactoring.cpp +++ b/src/libslic3r/SupportableIssuesSearchRefactoring.cpp @@ -91,10 +91,9 @@ public: } // negative sign means inside - float signed_distance_from_lines(const Point &point, size_t &nearest_line_index_out, + float signed_distance_from_lines(const Vec2f &point, size_t &nearest_line_index_out, Vec2f &nearest_point_out) const { - Vec2f p = unscaled(point).cast(); - auto distance = AABBTreeLines::squared_distance_to_indexed_lines(lines, tree, p, nearest_line_index_out, + auto distance = AABBTreeLines::squared_distance_to_indexed_lines(lines, tree, point, nearest_line_index_out, nearest_point_out); if (distance < 0) return std::numeric_limits::infinity(); @@ -102,7 +101,7 @@ public: distance = sqrt(distance); const ExtrusionLine &line = lines[nearest_line_index_out]; Vec2f v1 = line.b - line.a; - Vec2f v2 = p - line.a; + Vec2f v2 = point - line.a; if ((v1.x() * v2.y()) - (v1.y() * v2.x()) > 0.0) { distance *= -1; } @@ -114,30 +113,30 @@ public: } }; -class SupportedSegmentAccumulator { +class StabilityAccumulator { private: Polygon base_convex_hull { }; - Points supported_points { }; + Points support_points { }; Vec3f centroid_accumulator = Vec3f::Zero(); float accumulated_volume { }; float base_area { }; float base_height { }; public: - explicit SupportedSegmentAccumulator(float base_height) : + explicit StabilityAccumulator(float base_height) : base_height(base_height) { } void add_base_extrusion(const ExtrusionLine &line, float width, float print_z, float cross_section) { base_area += line.len * width; - supported_points.push_back(Point::new_scale(line.a)); - supported_points.push_back(Point::new_scale(line.b)); + support_points.push_back(Point::new_scale(line.a)); + support_points.push_back(Point::new_scale(line.b)); base_convex_hull.clear(); add_extrusion(line, print_z, cross_section); } void add_support_point(const Point &position, float area) { - supported_points.push_back(position); + support_points.push_back(position); base_convex_hull.clear(); base_area += area; } @@ -149,57 +148,68 @@ public: centroid_accumulator += volume * Vec3f(center.x(), center.y(), print_z); } + Vec3f get_centroid() const { + return centroid_accumulator / accumulated_volume; + } + + float get_base_area() const { + return base_area; + } + float get_base_height() const { + return base_height; + } + const Polygon& segment_base_hull() { if (this->base_convex_hull.empty()) { - this->base_convex_hull = Geometry::convex_hull(this->supported_points); + this->base_convex_hull = Geometry::convex_hull(this->support_points); } return this->base_convex_hull; } - void add_from(const SupportedSegmentAccumulator &acc) { - this->supported_points.insert(this->supported_points.end(), acc.supported_points.begin(), - acc.supported_points.end()); + const Points& get_support_points() { + return support_points; + } + + void add_from(const StabilityAccumulator &acc) { + this->support_points.insert(this->support_points.end(), acc.support_points.begin(), + acc.support_points.end()); base_convex_hull.clear(); this->centroid_accumulator += acc.centroid_accumulator; this->accumulated_volume += acc.accumulated_volume; this->base_area += acc.base_area; } - - bool check_stability() { - return true; - } }; -struct SupportedSegmentAccumulators { +struct StabilityAccumulators { private: size_t next_id = 0; std::unordered_map mapping; - std::vector acccumulators; + std::vector acccumulators; void merge_to(size_t from_id, size_t to_id) { - SupportedSegmentAccumulator &from_acc = this->access(from_id); - SupportedSegmentAccumulator &to_acc = this->access(to_id); + StabilityAccumulator &from_acc = this->access(from_id); + StabilityAccumulator &to_acc = this->access(to_id); if (&from_acc == &to_acc) { return; } to_acc.add_from(from_acc); mapping[from_id] = mapping[to_id]; - from_acc = SupportedSegmentAccumulator { 0.0f }; + from_acc = StabilityAccumulator { 0.0f }; } public: - SupportedSegmentAccumulators() = default; + StabilityAccumulators() = default; int create_accumulator(float base_height) { size_t id = next_id; next_id++; mapping[id] = acccumulators.size(); - acccumulators.push_back(SupportedSegmentAccumulator { base_height }); + acccumulators.push_back(StabilityAccumulator { base_height }); return id; } - SupportedSegmentAccumulator& access(size_t id) { + StabilityAccumulator& access(size_t id) { return acccumulators[mapping[id]]; } @@ -207,23 +217,37 @@ public: if (from_id == NULL_ACC_ID || to_id == NULL_ACC_ID) { return; } - SupportedSegmentAccumulator &from_acc = this->access(from_id); - SupportedSegmentAccumulator &to_acc = this->access(to_id); + StabilityAccumulator &from_acc = this->access(from_id); + StabilityAccumulator &to_acc = this->access(to_id); if (&from_acc == &to_acc) { return; } to_acc.add_from(from_acc); mapping[from_id] = mapping[to_id]; - from_acc = SupportedSegmentAccumulator { 0.0f }; + from_acc = StabilityAccumulator { 0.0f }; } - std::unordered_set get_active_acc_indices() const { - std::unordered_set result; - for (const auto &pair : mapping) { - result.insert(pair.second); +#ifdef DEBUG_FILES + Vec3f get_emerging_color(size_t id) { + if (mapping.find(id) == mapping.end()) { + std::cerr << " ERROR: uknown accumulator ID: " << id << std::endl; + return Vec3f(1.0f, 1.0f, 1.0f); } - return result; + + size_t pseudornd = ((id + 127) * 33331 + 6907) % 13; + return value_to_rgbf(0.0f, 13.0f, float(pseudornd)); } + + Vec3f get_final_color(size_t id) { + if (mapping.find(id) == mapping.end()) { + std::cerr << " ERROR: uknown accumulator ID: " << id << std::endl; + return Vec3f(1.0f, 1.0f, 1.0f); + } + + size_t pseudornd = ((mapping[id] + 127) * 33331 + 6907) % 13; + return value_to_rgbf(0.0f, 13.0f, float(pseudornd)); + } +#endif DEBUG_FILES }; float get_flow_width(const LayerRegion *region, ExtrusionRole role) { @@ -247,16 +271,6 @@ float get_flow_width(const LayerRegion *region, ExtrusionRole role) { } } -float get_max_allowed_distance(ExtrusionRole role, float flow_width, bool external_perimeters_first, - const Params ¶ms) { // <= distance / flow_width (can be larger for perimeter, if not external perimeter first) - if ((role == ExtrusionRole::erExternalPerimeter || role == ExtrusionRole::erOverhangPerimeter) - && (external_perimeters_first)) { - return params.max_first_ex_perim_unsupported_distance_factor * flow_width; - } else { - return params.max_unsupported_distance_factor * flow_width; - } -} - struct ExtrusionPropertiesAccumulator { float distance = 0; //accumulated distance float curvature = 0; //accumulated signed ccw angles @@ -279,7 +293,7 @@ struct ExtrusionPropertiesAccumulator { }; void check_extrusion_entity_stability(const ExtrusionEntity *entity, - SupportedSegmentAccumulators &stability_accs, + StabilityAccumulators &stability_accs, Issues &issues, std::vector &checked_lines, float print_z, @@ -305,7 +319,7 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, lines.emplace_back(unscaled(points[0]).cast(), unscaled(points[0]).cast()); for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { Vec2f start = unscaled(points[point_idx]).cast(); - Vec2f next = unscaled(points[point_idx]).cast(); + Vec2f next = unscaled(points[point_idx + 1]).cast(); Vec2f v = next - start; // vector from next to current float dist_to_next = v.norm(); v.normalize(); @@ -318,37 +332,32 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, } } - checked_lines.insert(checked_lines.end(), lines.begin(), lines.end()); - size_t current_stability_acc = NULL_ACC_ID; ExtrusionPropertiesAccumulator bridging_acc { }; bridging_acc.add_distance(params.bridge_distance + 1.0f); // Initialise unsupported distance with larger than tolerable distance -> // -> it prevents extruding perimeter start and short loops into air. const float flow_width = get_flow_width(layer_region, entity->role()); const float region_height = layer_region->layer()->height; - const float max_allowed_dist_from_prev_layer = get_max_allowed_distance(entity->role(), flow_width, - layer_region->region().config().external_perimeters_first, params); + const float max_allowed_dist_from_prev_layer = flow_width; for (size_t line_idx = 0; line_idx < lines.size(); ++line_idx) { - ExtrusionLine current_line = lines[line_idx]; + ExtrusionLine ¤t_line = lines[line_idx]; Point current = Point::new_scale(current_line.b); float cross_section = region_height * flow_width * 0.7071f; - float angle = 0; + float curr_angle = 0; if (line_idx + 1 < lines.size()) { const Vec2f v1 = current_line.b - current_line.a; const Vec2f v2 = lines[line_idx + 1].b - lines[line_idx + 1].a; - float dot = v1(0) * v2(0) + v1(1) * v2(1); - float cross = v1(0) * v2(1) - v1(1) * v2(0); - angle = float(atan2(float(cross), float(dot))); // ccw angle, TODO replace with angle func, once it gets into master + curr_angle = angle(v1, v2); } - bridging_acc.add_angle(angle); + bridging_acc.add_angle(curr_angle); size_t nearest_line_idx; Vec2f nearest_point; - float dist_from_prev_layer = prev_layer_lines.signed_distance_from_lines(current, nearest_line_idx, + float dist_from_prev_layer = prev_layer_lines.signed_distance_from_lines(current_line.b, nearest_line_idx, nearest_point); - if (dist_from_prev_layer - flow_width < max_allowed_dist_from_prev_layer) { + if (dist_from_prev_layer < max_allowed_dist_from_prev_layer) { const ExtrusionLine &nearest_line = prev_layer_lines.get_line(nearest_line_idx); size_t acc_id = nearest_line.supported_segment_accumulator_id; stability_accs.merge_accumulators(std::max(acc_id, current_stability_acc), @@ -360,29 +369,110 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, // TODO curving here } else { bridging_acc.add_distance(current_line.len); - if (current_stability_acc < 0) { - size_t acc_id = stability_accs.create_accumulator(print_z); - stability_accs.merge_accumulators(std::max(acc_id, current_stability_acc), - std::min(acc_id, current_stability_acc)); - current_stability_acc = std::min(acc_id, current_stability_acc); + if (current_stability_acc == NULL_ACC_ID) { + current_stability_acc = stability_accs.create_accumulator(print_z); } - SupportedSegmentAccumulator ¤t_segment = stability_accs.access(current_stability_acc); + StabilityAccumulator ¤t_segment = stability_accs.access(current_stability_acc); + current_line.supported_segment_accumulator_id = current_stability_acc; current_segment.add_extrusion(current_line, print_z, cross_section); if (bridging_acc.distance // if unsupported distance is larger than bridge distance linearly decreased by curvature, enforce supports. > params.bridge_distance / (1.0f + (bridging_acc.max_curvature * params.bridge_distance_decrease_by_curvature_factor / PI))) { - current_segment.add_support_point(current, 5.0f); + current_segment.add_support_point(current, params.support_points_interface_area); issues.supports_nedded.emplace_back(to_vec3f(current), 1.0); bridging_acc.reset(); } } } + checked_lines.insert(checked_lines.end(), lines.begin(), lines.end()); + } +} + +void check_layer_global_stability(StabilityAccumulators &stability_accs, + Issues &issues, + const std::vector &checked_lines, + float print_z, + const Params ¶ms) { + std::unordered_map> layer_accs_lines; + for (size_t i = 0; i < checked_lines.size(); ++i) { + layer_accs_lines[&stability_accs.access(checked_lines[i].supported_segment_accumulator_id)].push_back(i); + } + + for (auto &acc_lines : layer_accs_lines) { + StabilityAccumulator *acc = acc_lines.first; + Vec3f centroid = acc->get_centroid(); + Vec2f hull_centroid = unscaled(acc->segment_base_hull().centroid()).cast(); + std::vector hull_lines; + for (const Line &line : acc->segment_base_hull().lines()) { + Vec2f start = unscaled(line.a).cast(); + Vec2f next = unscaled(line.b).cast(); + hull_lines.push_back( { start, next }); + } + if (hull_lines.empty()) { + if (acc->get_support_points().empty()) { + acc->add_support_point(Point::new_scale(checked_lines[acc_lines.second[0]].a), + params.support_points_interface_area); + issues.supports_nedded.emplace_back(to_3d(checked_lines[acc_lines.second[0]].a, print_z), 1.0); + } + hull_lines.push_back( { unscaled(acc->get_support_points()[0]).cast(), + unscaled(acc->get_support_points()[0]).cast() }); + hull_centroid = unscaled(acc->get_support_points()[0]).cast(); + } + + LayerLinesDistancer hull_distancer(std::move(hull_lines)); + + size_t _li; + Vec2f _p; + bool centroid_inside_hull = hull_distancer.signed_distance_from_lines(centroid.head<2>(), _li, _p) < 0; + + float sticking_force = acc->get_base_area() + * (acc->get_base_height() == 0 ? params.base_adhesion : params.support_adhesion); +// float weight = acc-> * params.filament_density * params.gravity_constant; +// float weight_torque = embedded_distance * weight; +// if (!inside) { +// weight_torque *= -1; +// } + + for (size_t line_idx : acc_lines.second){ + const ExtrusionLine &line = checked_lines[line_idx]; + + size_t nearest_line_idx; + Vec2f nearest_hull_point; + float hull_distance = hull_distancer.signed_distance_from_lines(line.b, nearest_line_idx, + nearest_hull_point); + + float sticking_torque = (nearest_hull_point - hull_centroid).norm() * sticking_force; + + std::cout << "sticking_torque: " << sticking_torque << std::endl; + + + Vec3f extruder_pressure_direction = to_3d(Vec2f(line.b - line.a), 0.0f).normalized(); + if (hull_distance > 0) { + extruder_pressure_direction.z() = -0.333f; + extruder_pressure_direction.normalize(); + } + float pressure_torque_arm = (to_3d(Vec2f(nearest_hull_point - line.b), print_z).cross(extruder_pressure_direction)).norm(); + + float extruder_conflict_torque = params.tolerable_extruder_conflict_force * pressure_torque_arm; + + std::cout << "extruder_conflict_torque: " << extruder_conflict_torque << std::endl; + + if (extruder_conflict_torque > sticking_torque) { + acc->add_support_point(Point::new_scale(line.b), params.support_points_interface_area); + issues.supports_nedded.emplace_back(to_3d(line.b, print_z), extruder_conflict_torque - sticking_torque); + } + + } } } Issues check_object_stability(const PrintObject *po, const Params ¶ms) { - SupportedSegmentAccumulators stability_accs; +#ifdef DEBUG_FILES + FILE *eacc = boost::nowide::fopen(debug_out_path("emerging_accumulators.obj").c_str(), "w"); + FILE *facc = boost::nowide::fopen(debug_out_path("final_accumulators.obj").c_str(), "w"); +#endif DEBUG_FILES + StabilityAccumulators stability_accs; LayerLinesDistancer prev_layer_lines { { } }; Issues issues { }; std::vector checked_lines; @@ -396,15 +486,15 @@ Issues check_object_stability(const PrintObject *po, const Params ¶ms) { const float region_height = layer_region->layer()->height; const float cross_section = region_height * flow_width * 0.7071f; int id = stability_accs.create_accumulator(base_print_z); - SupportedSegmentAccumulator &acc = stability_accs.access(id); + StabilityAccumulator &acc = stability_accs.access(id); Points points { }; perimeter->collect_points(points); for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { Vec2f start = unscaled(points[point_idx]).cast(); - Vec2f next = unscaled(points[point_idx]).cast(); - ExtrusionLine line{start, next}; + Vec2f next = unscaled(points[point_idx + 1]).cast(); + ExtrusionLine line { start, next }; line.supported_segment_accumulator_id = id; - acc.add_base_extrusion( line, flow_width, base_print_z, cross_section); + acc.add_base_extrusion(line, flow_width, base_print_z, cross_section); checked_lines.push_back(line); } } // perimeter @@ -415,22 +505,37 @@ Issues check_object_stability(const PrintObject *po, const Params ¶ms) { const float region_height = layer_region->layer()->height; const float cross_section = region_height * flow_width * 0.7071f; int id = stability_accs.create_accumulator(base_print_z); - SupportedSegmentAccumulator &acc = stability_accs.access(id); + StabilityAccumulator &acc = stability_accs.access(id); Points points { }; fill->collect_points(points); for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { Vec2f start = unscaled(points[point_idx]).cast(); - Vec2f next = unscaled(points[point_idx]).cast(); - acc.add_base_extrusion( { start, next }, flow_width, base_print_z, cross_section); + Vec2f next = unscaled(points[point_idx + 1]).cast(); + ExtrusionLine line { start, next }; + line.supported_segment_accumulator_id = id; + acc.add_base_extrusion(line, flow_width, base_print_z, cross_section); + checked_lines.push_back(line); } } // fill } // ex_entity } // region +#ifdef DEBUG_FILES + for (const auto &line : checked_lines) { + Vec3f ecolor = stability_accs.get_emerging_color(line.supported_segment_accumulator_id); + fprintf(eacc, "v %f %f %f %f %f %f\n", line.b[0], + line.b[1], base_print_z, ecolor[0], ecolor[1], ecolor[2]); + + Vec3f fcolor = stability_accs.get_final_color(line.supported_segment_accumulator_id); + fprintf(facc, "v %f %f %f %f %f %f\n", line.b[0], + line.b[1], base_print_z, fcolor[0], fcolor[1], fcolor[2]); + } +#endif DEBUG_FILES + for (size_t layer_idx = 1; layer_idx < po->layer_count(); ++layer_idx) { const Layer *layer = po->layers()[layer_idx]; - prev_layer_lines = LayerLinesDistancer{std::move(checked_lines)}; - checked_lines = std::vector{}; + prev_layer_lines = LayerLinesDistancer { std::move(checked_lines) }; + checked_lines = std::vector { }; float print_z = layer->print_z; for (const LayerRegion *layer_region : layer->regions()) { @@ -452,17 +557,34 @@ Issues check_object_stability(const PrintObject *po, const Params ¶ms) { } // fill } // ex_entity } // region + + check_layer_global_stability(stability_accs, issues, checked_lines, print_z, params); + +#ifdef DEBUG_FILES + for (const auto &line : checked_lines) { + Vec3f ecolor = stability_accs.get_emerging_color(line.supported_segment_accumulator_id); + fprintf(eacc, "v %f %f %f %f %f %f\n", line.b[0], + line.b[1], print_z, ecolor[0], ecolor[1], ecolor[2]); + + Vec3f fcolor = stability_accs.get_final_color(line.supported_segment_accumulator_id); + fprintf(facc, "v %f %f %f %f %f %f\n", line.b[0], + line.b[1], print_z, fcolor[0], fcolor[1], fcolor[2]); + } +#endif DEBUG_FILES } +#ifdef DEBUG_FILES + fclose(eacc); + fclose(facc); +#endif DEBUG_FILES + std::cout << " SUPP: " << issues.supports_nedded.size() << std::endl; return issues; } - #ifdef DEBUG_FILES void debug_export(Issues issues, std::string file_name) { Slic3r::CNumericLocalesSetter locales_setter; - { FILE *fp = boost::nowide::fopen(debug_out_path((file_name + "_supports.obj").c_str()).c_str(), "w"); if (fp == nullptr) { @@ -494,7 +616,6 @@ void debug_export(Issues issues, std::string file_name) { } fclose(fp); } - } #endif From bef26fee2bf593efa694f8756029a5c459acf6f4 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Mon, 20 Jun 2022 17:43:04 +0200 Subject: [PATCH 27/78] Bugfixing and refactoring --- src/libslic3r/CMakeLists.txt | 3 +- src/libslic3r/SupportableIssuesSearch.cpp | 1500 +++++++---------- src/libslic3r/SupportableIssuesSearch.hpp | 10 +- .../SupportableIssuesSearchRefactoring.cpp | 635 ------- 4 files changed, 591 insertions(+), 1557 deletions(-) delete mode 100644 src/libslic3r/SupportableIssuesSearchRefactoring.cpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index e866ac34e5..0a0062a754 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -245,8 +245,7 @@ set(SLIC3R_SOURCES SlicingAdaptive.hpp Subdivide.cpp Subdivide.hpp -# SupportableIssuesSearch.cpp - SupportableIssuesSearchRefactoring.cpp + SupportableIssuesSearch.cpp SupportableIssuesSearch.hpp SupportMaterial.cpp SupportMaterial.hpp diff --git a/src/libslic3r/SupportableIssuesSearch.cpp b/src/libslic3r/SupportableIssuesSearch.cpp index 55d883f0a9..73f89e0d63 100644 --- a/src/libslic3r/SupportableIssuesSearch.cpp +++ b/src/libslic3r/SupportableIssuesSearch.cpp @@ -21,6 +21,40 @@ #endif namespace Slic3r { + +static const size_t NULL_ACC_ID = std::numeric_limits::max(); + +class ExtrusionLine +{ +public: + ExtrusionLine() : + a(Vec2f::Zero()), b(Vec2f::Zero()), len(0.0f) { + } + ExtrusionLine(const Vec2f &_a, const Vec2f &_b) : + a(_a), b(_b), len((_a - _b).norm()) { + } + + float length() { + return (a - b).norm(); + } + + Vec2f a; + Vec2f b; + float len; + + size_t stability_accumulator_id = NULL_ACC_ID; + + static const constexpr int Dim = 2; + using Scalar = Vec2f::Scalar; +}; + +auto get_a(ExtrusionLine &&l) { + return l.a; +} +auto get_b(ExtrusionLine &&l) { + return l.b; +} + namespace SupportableIssues { void Issues::add(const Issues &layer_issues) { @@ -45,615 +79,596 @@ CurledFilament::CurledFilament(const Vec3f &position) : position(position), estimated_height(0.0f) { } -struct Cell { - float volume; - float curled_height; - int accumulator_id = std::numeric_limits::max(); +class LayerLinesDistancer { +private: + std::vector lines; + AABBTreeIndirect::Tree<2, float> tree; + +public: + explicit LayerLinesDistancer(std::vector &&lines) : + lines(lines) { + tree = AABBTreeLines::build_aabb_tree_over_indexed_lines(lines); + } + + // negative sign means inside + float signed_distance_from_lines(const Vec2f &point, size_t &nearest_line_index_out, + Vec2f &nearest_point_out) const { + auto distance = AABBTreeLines::squared_distance_to_indexed_lines(lines, tree, point, nearest_line_index_out, + nearest_point_out); + if (distance < 0) + return std::numeric_limits::infinity(); + + distance = sqrt(distance); + const ExtrusionLine &line = lines[nearest_line_index_out]; + Vec2f v1 = line.b - line.a; + Vec2f v2 = point - line.a; + if ((v1.x() * v2.y()) - (v1.y() * v2.x()) > 0.0) { + distance *= -1; + } + return distance; + } + + const ExtrusionLine& get_line(size_t line_idx) const { + return lines[line_idx]; + } + + const std::vector& get_lines() const { + return lines; + } }; -class CentroidAccumulator { +class StabilityAccumulator { private: - Polygon convex_hull { }; - Points points { }; - public: - Vec3f accumulated_value = Vec3f::Zero(); + Polygon base_convex_hull { }; + Points support_points { }; + Vec3f centroid_accumulator = Vec3f::Zero(); float accumulated_volume { }; float base_area { }; - float additional_supports_adhesion { }; float base_height { }; - explicit CentroidAccumulator(float base_height) : +public: + explicit StabilityAccumulator(float base_height) : base_height(base_height) { } - const Polygon& base_hull() { - if (this->convex_hull.empty()) { - this->convex_hull = Geometry::convex_hull(this->points); + void add_base_extrusion(const ExtrusionLine &line, float width, float print_z, float mm3_per_mm) { + base_area += line.len * width; + support_points.push_back(Point::new_scale(line.a)); + support_points.push_back(Point::new_scale(line.b)); + base_convex_hull.clear(); + add_extrusion(line, print_z, mm3_per_mm); + } + + void add_support_point(const Point &position, float area) { + support_points.push_back(position); + base_convex_hull.clear(); + base_area += area; + } + + void add_extrusion(const ExtrusionLine &line, float print_z, float mm3_per_mm) { + float volume = line.len * mm3_per_mm; + accumulated_volume += volume; + Vec2f center = (line.a + line.b) / 2.0f; + centroid_accumulator += volume * Vec3f(center.x(), center.y(), print_z); + } + + Vec3f get_centroid() const { + return centroid_accumulator / accumulated_volume; + } + + float get_base_area() const { + return base_area; + } + float get_base_height() const { + return base_height; + } + float get_accumulated_volume() const { + return accumulated_volume; + } + + const Polygon& segment_base_hull() { + if (this->base_convex_hull.empty()) { + this->base_convex_hull = Geometry::convex_hull(this->support_points); } - return this->convex_hull; + return this->base_convex_hull; } - void add_base_points(const Points &other) { - this->points.insert(this->points.end(), other.begin(), other.end()); - convex_hull.clear(); + const Points& get_support_points() { + return support_points; } - const Points& get_base_points() { - return this->points; + void add_from(const StabilityAccumulator &acc) { + this->support_points.insert(this->support_points.end(), acc.support_points.begin(), + acc.support_points.end()); + base_convex_hull.clear(); + this->centroid_accumulator += acc.centroid_accumulator; + this->accumulated_volume += acc.accumulated_volume; + this->base_area += acc.base_area; } }; -struct CentroidAccumulators { - std::unordered_map mapping; - std::vector acccumulators; +struct StabilityAccumulators { +private: + size_t next_id = 0; + std::unordered_map mapping; + std::vector acccumulators; - explicit CentroidAccumulators(size_t reserve_count) { - acccumulators.reserve(reserve_count); - } - - CentroidAccumulator& create_accumulator(int id, float base_height) { - mapping[id] = acccumulators.size(); - acccumulators.push_back(CentroidAccumulator { base_height }); - return this->access(id); - } - - CentroidAccumulator& access(int id) { - return acccumulators[mapping[id]]; - } - - void merge_to(int from_id, int to_id) { - CentroidAccumulator &from_acc = this->access(from_id); - CentroidAccumulator &to_acc = this->access(to_id); + void merge_to(size_t from_id, size_t to_id) { + StabilityAccumulator &from_acc = this->access(from_id); + StabilityAccumulator &to_acc = this->access(to_id); if (&from_acc == &to_acc) { return; } - to_acc.accumulated_value += from_acc.accumulated_value; - to_acc.accumulated_volume += from_acc.accumulated_volume; - to_acc.add_base_points(from_acc.get_base_points()); - to_acc.base_area += from_acc.base_area; + to_acc.add_from(from_acc); mapping[from_id] = mapping[to_id]; - from_acc = CentroidAccumulator { 0.0f }; + from_acc = StabilityAccumulator { 0.0f }; } -}; - -class BalanceDistributionGrid { - - static constexpr float cell_height = scale_(0.3f); - - Vec3crd cell_size { }; - - Vec3crd global_origin { }; - Vec3crd global_size { }; - Vec3i global_cell_count { }; - - int local_z_index_offset { }; - int local_z_cell_count { }; - std::vector cells { }; public: + StabilityAccumulators() = default; - BalanceDistributionGrid() = default; - - void init(const PrintObject *po, size_t layer_idx_begin, size_t layer_idx_end) { - Vec2crd size_half = po->size().head<2>().cwiseQuotient(Vec2crd(2, 2)) + Vec2crd::Ones(); - Vec3crd min = Vec3crd(-size_half.x(), -size_half.y(), 0); - Vec3crd max = Vec3crd(size_half.x(), size_half.y(), po->height()); - - cell_size = Vec3crd { int(cell_height * 2), int(cell_height * 2), int(cell_height) }; - assert(cell_size.x() == cell_size.y()); - - global_origin = min; - global_size = max - min; - global_cell_count = global_size.cwiseQuotient(cell_size) + Vec3i::Ones(); - - coord_t local_min_z = scale_(po->layers()[layer_idx_begin]->print_z); - coord_t local_max_z = scale_(po->layers()[layer_idx_end > 0 ? layer_idx_end - 1 : 0]->print_z); - int local_min_z_index = local_min_z / cell_size.z(); - int local_max_z_index = local_max_z / cell_size.z() + 1; - - local_z_index_offset = local_min_z_index; - local_z_cell_count = local_max_z_index + 1 - local_min_z_index; - - cells.resize(local_z_cell_count * global_cell_count.y() * global_cell_count.x()); + int create_accumulator(float base_height) { + size_t id = next_id; + next_id++; + mapping[id] = acccumulators.size(); + acccumulators.push_back(StabilityAccumulator { base_height }); + return id; } - Vec3i to_global_cell_coords(const Vec3i &local_cell_coords) const { - return local_cell_coords + local_z_index_offset * Vec3i::UnitZ(); + StabilityAccumulator& access(size_t id) { + return acccumulators[mapping[id]]; } - Vec3i to_local_cell_coords(const Vec3i &global_cell_coords) const { - return global_cell_coords - local_z_index_offset * Vec3i::UnitZ(); - } - - Vec3i to_global_cell_coords(const Point &p, float print_z) const { - Vec3crd position = Vec3crd { p.x(), p.y(), int(scale_(print_z)) }; - Vec3i cell_coords = (position - this->global_origin).cwiseQuotient(this->cell_size); - return cell_coords; - } - - Vec3i to_global_cell_coords(const Vec3f &position) const { - Vec3crd scaled_position = scaled(position); - Vec3i cell_coords = (scaled_position - this->global_origin).cwiseQuotient(this->cell_size); - return cell_coords; - } - - Vec3i to_local_cell_coords(const Point &p, float print_z) const { - Vec3i cell_coords = this->to_global_cell_coords(p, print_z); - return this->to_local_cell_coords(cell_coords); - } - - size_t to_cell_index(const Vec3i &local_cell_coords) const { - assert(local_cell_coords.x() >= 0); - assert(local_cell_coords.x() < global_cell_count.x()); - assert(local_cell_coords.y() >= 0); - assert(local_cell_coords.y() < global_cell_count.y()); - assert(local_cell_coords.z() >= 0); - assert(local_cell_coords.z() < local_z_cell_count); - - return local_cell_coords.z() * global_cell_count.x() * global_cell_count.y() - + local_cell_coords.y() * global_cell_count.x() - + local_cell_coords.x(); - } - - Vec3crd get_cell_center(const Vec3i &global_cell_coords) const { - return global_origin + global_cell_coords.cwiseProduct(this->cell_size) - + this->cell_size.cwiseQuotient(Vec3crd(2, 2, 2)); - } - - Cell& access_cell(const Point &p, float print_z) { - return cells[this->to_cell_index(to_local_cell_coords(p, print_z))]; - } - - Cell& access_cell(const Vec3f &unscaled_position) { - return cells[this->to_cell_index(this->to_local_cell_coords(this->to_global_cell_coords(unscaled_position)))]; - } - - Cell& access_cell(const Vec3i &local_cell_coords) { - return cells[this->to_cell_index(local_cell_coords)]; - } - - const Cell& access_cell(const Vec3i &local_cell_coords) const { - return cells[this->to_cell_index(local_cell_coords)]; - } - - void distribute_edge(const Point &p1, const Point &p2, float print_z, float unscaled_width, float unscaled_height) { - Vec2d dir = (p2 - p1).cast(); - double length = dir.norm(); - if (length < 0.1) { + void merge_accumulators(size_t from_id, size_t to_id) { + if (from_id == NULL_ACC_ID || to_id == NULL_ACC_ID) { return; } - double step_size = this->cell_size.x() / 2.0; - - float diameter = unscaled_height * unscaled_width * 0.7071f; // constant to simulate somewhat elliptical shape (1/sqrt(2)) - - double distributed_length = 0; - while (distributed_length < length) { - double next_len = std::min(length, distributed_length + step_size); - double current_dist_payload = next_len - distributed_length; - - Point location = p1 + ((next_len / length) * dir).cast(); - float payload = unscale(current_dist_payload) * diameter; - this->access_cell(location, print_z).volume += payload; - - distributed_length = next_len; + StabilityAccumulator &from_acc = this->access(from_id); + StabilityAccumulator &to_acc = this->access(to_id); + if (&from_acc == &to_acc) { + return; } - } - - void merge(const BalanceDistributionGrid &other) { - int z_start = std::max(local_z_index_offset, other.local_z_index_offset); - int z_end = std::min(local_z_index_offset + local_z_cell_count, - other.local_z_index_offset + other.local_z_cell_count); - - for (int x = 0; x < global_cell_count.x(); ++x) { - for (int y = 0; y < global_cell_count.y(); ++y) { - for (int z = z_start; z < z_end; ++z) { - Vec3i global_coords { x, y, z }; - Vec3i local_coords = this->to_local_cell_coords(global_coords); - Vec3i other_local_coords = other.to_local_cell_coords(global_coords); - this->access_cell(local_coords).volume += other.access_cell(other_local_coords).volume; - } - } - } - } - - void analyze(Issues &issues, const Params ¶ms) { - const auto validate_xy_coords = [&](const Vec2i &local_coords) { - return local_coords.x() >= 0 && local_coords.y() >= 0 && local_coords.x() < this->global_cell_count.x() - && local_coords.y() < this->global_cell_count.y(); - }; - CentroidAccumulators accumulators(issues.supports_nedded.size() + 4); // just an estimation; one for each support point from prev step, and 4 for the base - auto custom_comparator = [](const Vec2i &left, const Vec2i &right) { - if (left.x() == right.x()) { - return left.y() < right.y(); - } - return left.x() < right.x(); - }; - - //initialization of the bed accumulators from the bed cells (first layer of grid) - int next_accumulator_id = -1; // accumulators from the bed have negative ids, starting with -1. Accumulators generated by support points have nonegative ids, starting with 0, and sorted by height - // The reason is, that during merging of accumulators (when they meet at the upper cells), algorithm always keeps the one with the lower id (so bed is preffered), and discards the other - std::set coords_to_check(custom_comparator); // set of coords to check for the current accumulator (search based on connectivity) - for (int y = 0; y < global_cell_count.y(); ++y) { - for (int x = 0; x < global_cell_count.x(); ++x) { - Cell &origin_cell = this->access_cell(Vec3i(x, y, 0)); - if (origin_cell.volume > 0 && origin_cell.accumulator_id == std::numeric_limits::max()) { // cell has volume and no accumulator assigned yet - CentroidAccumulator &acc = accumulators.create_accumulator(next_accumulator_id, 0); // create new accumulator, height is 0, because we are on the bed - coords_to_check.clear(); - coords_to_check.insert(Vec2i(x, y)); - while (!coords_to_check.empty()) { // insert the origin cell coords in to the set, and search all connected cells with volume and without assigned accumulator, assign them to acc - Vec2i current_coords = *coords_to_check.begin(); - coords_to_check.erase(coords_to_check.begin()); - if (!validate_xy_coords(current_coords)) { // invalid coords, drop - continue; - } - Cell &cell = this->access_cell(Vec3i(current_coords.x(), current_coords.y(), 0)); - if (cell.volume <= 0 || cell.accumulator_id != std::numeric_limits::max()) { // empty cell or already assigned, drop - continue; - } - cell.accumulator_id = next_accumulator_id; // update cell accumulator id, update the accumulator with the new cell data, add neighbours to queue - Vec3crd cell_center = this->get_cell_center( - Vec3i(current_coords.x(), current_coords.y(), local_z_index_offset)); - acc.add_base_points( { Point(cell_center.head<2>()) }); - acc.accumulated_value += unscale(cell_center).cast() * cell.volume; - acc.accumulated_volume += cell.volume; - - for (int y_offset = -1; y_offset <= 1; ++y_offset) { - for (int x_offset = -1; x_offset <= 1; ++x_offset) { - if (y_offset != 0 || x_offset != 0) { - coords_to_check.insert( - Vec2i(current_coords.x() + x_offset, current_coords.y() + y_offset)); - } - } - } - } - next_accumulator_id--; - //base area is separated from the base convex hull - bed accumulators are initialized with convex hull area (TODO compute from number of covered cells instead ) - // but support points are initialized with constant, and during merging, the base_areas are added. - acc.base_area = unscale(unscale(acc.base_hull().area())); //apply unscale 2x, it has units of area - } - } - } - - // sort support points by height, so that their accumulators ids are also sorted by height - std::sort(issues.supports_nedded.begin(), issues.supports_nedded.end(), - [](const SupportPoint &left, const SupportPoint &right) { - return left.position.z() < right.position.z(); - }); - for (int index = 0; index < int(issues.supports_nedded.size()); ++index) { - Vec3i local_coords = this->to_local_cell_coords( - this->to_global_cell_coords(issues.supports_nedded[index].position)); - this->access_cell(local_coords).accumulator_id = index; // assign accumulator id (in case that multiple support points fall into the same cell, they are just overriden) - CentroidAccumulator &acc = accumulators.create_accumulator(index, - issues.supports_nedded[index].position.z()); - acc.add_base_points( { Point(scaled(Vec2f(issues.supports_nedded[index].position.head<2>()))) }); - acc.base_area = params.support_points_interface_area; - } - - // add curling data to each cell - for (const CurledFilament &curling : issues.curling_up) { - this->access_cell(curling.position).curled_height += curling.estimated_height; - } - - // keep map of modified accumulator for each layer, so that discarded accumulators are not further checked for stability - // the value of the map is list of cells with curling, that should be further checked for pressure stability with repsect to the accumulator - std::unordered_map> modified_acc_ids; - // At the end of the layer check, accumulators are further filtered, since merging causes that single accumulator can have mutliple ids. - // But each accumulator should be checked only once. - std::unordered_map> filtered_active_accumulators; - modified_acc_ids.reserve(issues.supports_nedded.size() + 1); - - // For each grid layer, cells are added to the accumulators and all active accumulators are checked of stability. - for (int z = 1; z < local_z_cell_count; ++z) { - std::cout << "current z: " << z << std::endl; - - modified_acc_ids.clear(); - - for (int x = 0; x < global_cell_count.x(); ++x) { - for (int y = 0; y < global_cell_count.y(); ++y) { - Cell ¤t = this->access_cell(Vec3i(x, y, z)); - // distribute islands info - look for neighbours under the cell, and pick the smallest accumulator id - // also gather all ids, they will be merged to the smallest id accumualtor - if (current.volume > 0 && current.accumulator_id == std::numeric_limits::max()) { - int min_accumulator_id_found = std::numeric_limits::max(); - std::unordered_set ids_to_merge { }; - for (int y_offset = -2; y_offset <= 2; ++y_offset) { - for (int x_offset = -2; x_offset <= 2; ++x_offset) { - if (validate_xy_coords(Vec2i(x + x_offset, y + y_offset))) { - Cell &under = this->access_cell(Vec3i(x + x_offset, y + y_offset, z - 1)); - if (under.accumulator_id < min_accumulator_id_found) { - min_accumulator_id_found = under.accumulator_id; - } - ids_to_merge.insert(under.accumulator_id); - } - } - } - // assign accumulator and update its info - if (min_accumulator_id_found < std::numeric_limits::max()) { // accumulator id found - ids_to_merge.erase(std::numeric_limits::max()); - ids_to_merge.erase(min_accumulator_id_found); - current.accumulator_id = min_accumulator_id_found; //assign accumualtor id to the cell - for (auto id : ids_to_merge) { - accumulators.merge_to(id, min_accumulator_id_found); //merge other ids to the smallest id - } - //update the acc with new point, and store in the modified accumulators map - CentroidAccumulator &acc = accumulators.access(min_accumulator_id_found); - acc.accumulated_value += current.volume - * unscale(this->get_cell_center(this->to_global_cell_coords(Vec3i(x, y, z)))).cast< - float>(); - acc.accumulated_volume += current.volume; - modified_acc_ids.emplace(min_accumulator_id_found, std::vector { }); - } - } - - // distribute curling (add curling from neighbours under, but also decrease but some factor) - if (current.volume > 0) { - float curled_height = 0; - for (int y_offset = -2; y_offset <= 2; ++y_offset) { - for (int x_offset = -2; x_offset <= 2; ++x_offset) { - if (validate_xy_coords(Vec2i(x + x_offset, y + y_offset))) { - Cell &under = this->access_cell(Vec3i(x + x_offset, y + y_offset, z - 1)); - curled_height = std::max(curled_height, under.curled_height); - } - } - } - current.curled_height += std::max(0.0f, - float(curled_height - unscaled(this->cell_size.z()) / 1.5f)); - current.curled_height = std::min(current.curled_height, - float(unscaled(this->cell_size.z()) * 2.0f)); - - if (current.curled_height / unscaled(this->cell_size.z()) > 1.5f) { // just a magic threshold number. - modified_acc_ids[current.accumulator_id].push_back( { x, y }); - } - } - } - } - - //all cells of the grid layer checked, now further filter the modified accumulators, because multiple ids can point to the same acc - filtered_active_accumulators.clear(); - for (const auto &pair : modified_acc_ids) { - CentroidAccumulator *acc = &accumulators.access(pair.first); - filtered_active_accumulators[acc].insert(filtered_active_accumulators[acc].end(), - pair.second.begin(), - pair.second.end()); - } - - check_accumulators_stability(z, accumulators, filtered_active_accumulators, issues, params); - } - } -private: - void check_accumulators_stability(int z, CentroidAccumulators &accumulators, - std::unordered_map> filtered_active_accumulators, - Issues &issues, const Params ¶ms) const { - - for (const auto &pair : filtered_active_accumulators) { - std::cout << "Z: " << z << std::endl; - CentroidAccumulator &acc = *pair.first; - Vec3f centroid = acc.accumulated_value / acc.accumulated_volume; - Point base_centroid = acc.base_hull().centroid(); - Vec3f base_centroid_3d { }; - base_centroid_3d << unscale(base_centroid).cast(), acc.base_height; - - std::cout << "acc.accumulated_value : " << acc.accumulated_value.x() << " " - << acc.accumulated_value.y() << " " << acc.accumulated_value.z() << std::endl; - std::cout << "acc.accumulated_volume : " << acc.accumulated_volume << std::endl; - std::cout << "centroid: " << centroid.x() << " " << centroid.y() << " " << centroid.z() - << std::endl; - std::cout << "base_centroid_3d: " << base_centroid_3d.x() << " " << base_centroid_3d.y() << " " - << base_centroid_3d.z() - << std::endl; - - // find the cell that is furthest from the base centroid ( its a heurstic to find a possible problems with balance without checking all layer cells) - //TODO better result are if first pivot is chosen as the closest point of the convex hull to the base centroid, and then cell furthest in the direction defined by - // the vector from base centroid to this pivot is taken. - double max_dist_sqr = 0; - Vec3f suspicious_point = centroid; - Vec2i coords = Vec2i(0, 0); - for (int y = 0; y < global_cell_count.y(); ++y) { - for (int x = 0; x < global_cell_count.x(); ++x) { - const Cell &cell = this->access_cell(Vec3i(x, y, z)); - if (cell.accumulator_id != std::numeric_limits::max() && - &accumulators.access(cell.accumulator_id) == &acc) { - Vec3f cell_center = - unscale(this->get_cell_center(this->to_global_cell_coords(Vec3i(x, y, z)))).cast< - float>(); - float dist_sq = (cell_center - base_centroid_3d).squaredNorm(); - if (dist_sq > max_dist_sqr) { - max_dist_sqr = dist_sq; - suspicious_point = cell_center; - coords = Vec2i(x, y); - } - } - } - } - - // for the suspicious point, add movement force in xy (bed sliding, it is assumed that the worst direction is taken, for simplicity) - float xy_movement_force = acc.accumulated_volume * params.filament_density - * params.max_acceleration; - - std::cout << "xy_movement_force: " << xy_movement_force << std::endl; - - // also add weight (weight is the small factor, because the materials are very light. The weight torque will be computed much higher then what is real, - //since it does not push in the suspicoius point, but in centroid. Its approximation) - float weight = acc.accumulated_volume * params.filament_density * params.gravity_constant; - - std::cout << "weight: " << weight << std::endl; - - float force = this->check_point_stability_under_pressure(acc, base_centroid, suspicious_point, - xy_movement_force + weight + params.tolerable_extruder_conflict_force, - params); - if (force > 0) { - this->add_suppport_point(Vec3i(coords.x(), coords.y(), z), force, acc, issues, params); - } - - for (const Vec2i &cell : pair.second) { - Vec3f pressure_point = unscale( - this->get_cell_center( - this->to_global_cell_coords(Vec3i(cell.x(), cell.y(), z)))).cast(); - float force = this->check_point_stability_under_pressure(acc, base_centroid, pressure_point, - params.max_curled_conflict_extruder_force, //TODO add linear scaling of the extruder force based on the curled height (but current data about curled height are kind of unreliable in scale) - params); - if (force > 0) { - this->add_suppport_point(Vec3i(cell.x(), cell.y(), z), force, acc, issues, params); - } - } - } - } - - float check_point_stability_under_pressure(CentroidAccumulator &acc, const Point &base_centroid, - const Vec3f &pressure_point, float pressure_force, const Params ¶ms) const { - Point pressure_base_projection = Point(scaled(Vec2f(pressure_point.head<2>()))); - Point pivot; - double distance_scaled_sq = std::numeric_limits::max(); - bool inside = true; - // find pivot, which is the closest point of the accumulator base hull to pressure point (if the object should fall, it would be over this point) - if (acc.base_hull().points.size() == 1) { - pivot = acc.base_hull().points[0]; - distance_scaled_sq = (pivot - pressure_base_projection).squaredNorm(); - inside = distance_scaled_sq < params.support_points_interface_area; - } else { - for (Line line : acc.base_hull().lines()) { - Point closest_point; - double dist_sq = line.distance_to_squared(pressure_base_projection, &closest_point); - if (dist_sq < distance_scaled_sq) { - pivot = closest_point; - distance_scaled_sq = dist_sq; - } - if ((pressure_base_projection - closest_point).cast().dot(line.normal().cast()) - > 0) { - inside = false; - } - } - } -// float embedded_distance = unscaled(sqrt(distance_scaled_sq)); - float base_center_pivot_distance = float(unscale(Vec2crd(base_centroid - pivot)).norm()); - Vec3f pivot_3d; - pivot_3d << unscale(pivot).cast(), acc.base_height; - - //sticking force estimated from the base area and support points - float sticking_force = acc.base_area - * (acc.base_height == 0 ? params.base_adhesion : params.support_adhesion); - - std::cout << "sticking force: " << sticking_force << std::endl; - std::cout << "pressure force: " << pressure_force << std::endl; - float sticking_torque = sticking_force * base_center_pivot_distance; // sticking torque is computed from the distance to the centroid - - float pressure_arm = inside ? pressure_point.z() - pivot_3d.z() : (pressure_point - pivot_3d).norm(); // pressure arm is again higher then in reality, - // since it assumes the worst direction of the pressure force (perpendicular to the vector between pivot and pressure point) - float pressure_torque = pressure_arm * pressure_force; - - std::cout << "sticking_torque: " << sticking_torque << std::endl; - std::cout << "pressure_torque: " << pressure_torque << std::endl; - if (sticking_torque < pressure_torque) { - return pressure_force; - } else { - return 0.0f; - } - } - - void add_suppport_point(const Vec3i &local_coords, float expected_force, CentroidAccumulator &acc, Issues &issues, - const Params ¶ms) const { - //add support point - but first finding the lowest full cell is needed, since putting support point to the current cell may end up inside the model, - // essentially doing nothing. - int final_height_coords = local_coords.z(); - while (final_height_coords > 0 - && this->access_cell( - this->to_global_cell_coords(Vec3i(local_coords.x(), local_coords.y(), final_height_coords))).volume - > 0) { - final_height_coords--; - } - Vec3f support_point = - unscale( - this->get_cell_center( - this->to_global_cell_coords( - Vec3i(local_coords.x(), local_coords.y(), final_height_coords)))).cast< - float>(); - - std::cout << " new support point: " << support_point.x() << " | " << support_point.y() << " | " - << support_point.z() << std::endl; - std::cout << " expected_force: " << expected_force << std::endl; - - issues.supports_nedded.emplace_back(support_point, expected_force); - acc.add_base_points( { Point::new_scale(Vec2f(support_point.head<2>())) }); - acc.base_area += params.support_points_interface_area; + to_acc.add_from(from_acc); + mapping[from_id] = mapping[to_id]; + from_acc = StabilityAccumulator { 0.0f }; } #ifdef DEBUG_FILES -public: - void debug_export() const { - Slic3r::CNumericLocalesSetter locales_setter; - { - FILE *volume_grid_file = boost::nowide::fopen(debug_out_path("volume_grid.obj").c_str(), "w"); - FILE *islands_grid_file = boost::nowide::fopen(debug_out_path("islands_grid.obj").c_str(), "w"); - FILE *curling_grid_file = boost::nowide::fopen(debug_out_path("curling_grid.obj").c_str(), "w"); - - if (volume_grid_file == nullptr || islands_grid_file == nullptr || curling_grid_file == nullptr) { - BOOST_LOG_TRIVIAL(error) - << "Debug files: Couldn't open debug file for writing, destination: " << debug_out_path(""); - return; - } - - float max_volume = 0; - int min_island_id = 0; - int max_island_id = 0; - float max_curling_height = 0.5f; - - for (int x = 0; x < global_cell_count.x(); ++x) { - for (int y = 0; y < global_cell_count.y(); ++y) { - for (int z = 0; z < local_z_cell_count; ++z) { - const Cell &cell = access_cell(Vec3i(x, y, z)); - max_volume = std::max(max_volume, cell.volume); - if (cell.accumulator_id != std::numeric_limits::max()) { - min_island_id = std::min(min_island_id, cell.accumulator_id); - max_island_id = std::max(max_island_id, cell.accumulator_id); - } -// max_curling_height = std::max(max_curling_height, cell.curled_height); - } - } - } - - for (int x = 0; x < global_cell_count.x(); ++x) { - for (int y = 0; y < global_cell_count.y(); ++y) { - for (int z = 0; z < local_z_cell_count; ++z) { - Vec3f center = unscale(get_cell_center(to_global_cell_coords(Vec3i { x, y, z }))).cast< - float>(); - const Cell &cell = access_cell(Vec3i(x, y, z)); - if (cell.volume != 0) { - auto volume_color = value_to_rgbf(0, cell.volume, cell.volume); - fprintf(volume_grid_file, "v %f %f %f %f %f %f\n", center(0), center(1), center(2), - volume_color.x(), volume_color.y(), volume_color.z()); - } - if (cell.accumulator_id != std::numeric_limits::max()) { - auto island_color = value_to_rgbf(min_island_id, max_island_id + 1, cell.accumulator_id); - fprintf(islands_grid_file, "v %f %f %f %f %f %f\n", center(0), center(1), - center(2), - island_color.x(), island_color.y(), island_color.z()); - } - if (cell.curled_height > 0) { - auto curling_color = value_to_rgbf(0, max_curling_height, cell.curled_height); - fprintf(curling_grid_file, "v %f %f %f %f %f %f\n", center(0), center(1), - center(2), - curling_color.x(), curling_color.y(), curling_color.z()); - } - } - } - } - - fclose(volume_grid_file); - fclose(islands_grid_file); - fclose(curling_grid_file); + Vec3f get_emerging_color(size_t id) { + if (mapping.find(id) == mapping.end()) { + std::cerr << " ERROR: uknown accumulator ID: " << id << std::endl; + return Vec3f(1.0f, 1.0f, 1.0f); } + + size_t pseudornd = ((id + 127) * 33331 + 6907) % 13; + return value_to_rgbf(0.0f, 13.0f, float(pseudornd)); + } + + Vec3f get_final_color(size_t id) { + if (mapping.find(id) == mapping.end()) { + std::cerr << " ERROR: uknown accumulator ID: " << id << std::endl; + return Vec3f(1.0f, 1.0f, 1.0f); + } + + size_t pseudornd = ((mapping[id] + 127) * 33331 + 6907) % 13; + return value_to_rgbf(0.0f, 13.0f, float(pseudornd)); } #endif -} -; +}; -namespace Impl { +float get_flow_width(const LayerRegion *region, ExtrusionRole role) { + switch (role) { + case ExtrusionRole::erBridgeInfill: + return region->flow(FlowRole::frExternalPerimeter).width(); + case ExtrusionRole::erExternalPerimeter: + return region->flow(FlowRole::frExternalPerimeter).width(); + case ExtrusionRole::erGapFill: + return region->flow(FlowRole::frInfill).width(); + case ExtrusionRole::erPerimeter: + return region->flow(FlowRole::frPerimeter).width(); + case ExtrusionRole::erSolidInfill: + return region->flow(FlowRole::frSolidInfill).width(); + case ExtrusionRole::erInternalInfill: + return region->flow(FlowRole::frInfill).width(); + case ExtrusionRole::erTopSolidInfill: + return region->flow(FlowRole::frTopSolidInfill).width(); + default: + return region->flow(FlowRole::frPerimeter).width(); + } +} + +struct ExtrusionPropertiesAccumulator { + float distance = 0; //accumulated distance + float curvature = 0; //accumulated signed ccw angles + float max_curvature = 0; //max absolute accumulated value + + void add_distance(float dist) { + distance += dist; + } + + void add_angle(float ccw_angle) { + curvature += ccw_angle; + max_curvature = std::max(max_curvature, std::abs(curvature)); + } + + void reset() { + distance = 0; + curvature = 0; + max_curvature = 0; + } +}; + +void check_extrusion_entity_stability(const ExtrusionEntity *entity, + StabilityAccumulators &stability_accs, + Issues &issues, + std::vector &checked_lines, + float print_z, + const LayerRegion *layer_region, + const LayerLinesDistancer &prev_layer_lines, + const Params ¶ms) { + + if (entity->is_collection()) { + for (const auto *e : static_cast(entity)->entities) { + check_extrusion_entity_stability(e, stability_accs, issues, checked_lines, print_z, layer_region, + prev_layer_lines, + params); + } + } else { //single extrusion path, with possible varying parameters + const auto to_vec3f = [print_z](const Point &point) { + Vec2f tmp = unscale(point).cast(); + return Vec3f(tmp.x(), tmp.y(), print_z); + }; + Points points { }; + entity->collect_points(points); + std::vector lines; + lines.reserve(points.size() * 1.5); + lines.emplace_back(unscaled(points[0]).cast(), unscaled(points[0]).cast()); + for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { + Vec2f start = unscaled(points[point_idx]).cast(); + Vec2f next = unscaled(points[point_idx + 1]).cast(); + Vec2f v = next - start; // vector from next to current + float dist_to_next = v.norm(); + v.normalize(); + int lines_count = int(std::ceil(dist_to_next / params.bridge_distance)); + float step_size = dist_to_next / lines_count; + for (int i = 0; i < lines_count; ++i) { + Vec2f a(start + v * (i * step_size)); + Vec2f b(start + v * ((i + 1) * step_size)); + lines.emplace_back(a, b); + } + } + + size_t current_stability_acc = NULL_ACC_ID; + ExtrusionPropertiesAccumulator bridging_acc { }; + bridging_acc.add_distance(params.bridge_distance + 1.0f); // Initialise unsupported distance with larger than tolerable distance -> + // -> it prevents extruding perimeter start and short loops into air. + const float flow_width = get_flow_width(layer_region, entity->role()); + const float max_allowed_dist_from_prev_layer = flow_width; + + for (size_t line_idx = 0; line_idx < lines.size(); ++line_idx) { + ExtrusionLine ¤t_line = lines[line_idx]; + Point current = Point::new_scale(current_line.b); + float mm3_per_mm = float(entity->min_mm3_per_mm()); + + float curr_angle = 0; + if (line_idx + 1 < lines.size()) { + const Vec2f v1 = current_line.b - current_line.a; + const Vec2f v2 = lines[line_idx + 1].b - lines[line_idx + 1].a; + curr_angle = angle(v1, v2); + } + bridging_acc.add_angle(curr_angle); + + size_t nearest_line_idx; + Vec2f nearest_point; + float dist_from_prev_layer = prev_layer_lines.signed_distance_from_lines(current_line.b, nearest_line_idx, + nearest_point); + if (dist_from_prev_layer < max_allowed_dist_from_prev_layer) { + const ExtrusionLine &nearest_line = prev_layer_lines.get_line(nearest_line_idx); + size_t acc_id = nearest_line.stability_accumulator_id; + stability_accs.merge_accumulators(std::max(acc_id, current_stability_acc), + std::min(acc_id, current_stability_acc)); + current_stability_acc = std::min(acc_id, current_stability_acc); + current_line.stability_accumulator_id = current_stability_acc; + stability_accs.access(current_stability_acc).add_extrusion(current_line, print_z, mm3_per_mm); + bridging_acc.reset(); + // TODO curving here + } else { + bridging_acc.add_distance(current_line.len); + if (current_stability_acc == NULL_ACC_ID) { + current_stability_acc = stability_accs.create_accumulator(print_z); + } + StabilityAccumulator ¤t_segment = stability_accs.access(current_stability_acc); + current_line.stability_accumulator_id = current_stability_acc; + current_segment.add_extrusion(current_line, print_z, mm3_per_mm); + if (bridging_acc.distance // if unsupported distance is larger than bridge distance linearly decreased by curvature, enforce supports. + > params.bridge_distance + / (1.0f + (bridging_acc.max_curvature + * params.bridge_distance_decrease_by_curvature_factor / PI))) { + current_segment.add_support_point(current, params.support_points_interface_area); + issues.supports_nedded.emplace_back(to_vec3f(current), 1.0); + bridging_acc.reset(); + } + } + } + checked_lines.insert(checked_lines.end(), lines.begin(), lines.end()); + } +} + +void check_layer_global_stability(StabilityAccumulators &stability_accs, + Issues &issues, + const std::vector &checked_lines, + float print_z, + const Params ¶ms) { + std::unordered_map> layer_accs_lines; + for (size_t i = 0; i < checked_lines.size(); ++i) { + layer_accs_lines[&stability_accs.access(checked_lines[i].stability_accumulator_id)].push_back(i); + } + + for (auto &acc_lines : layer_accs_lines) { + StabilityAccumulator *acc = acc_lines.first; + Vec3f centroid = acc->get_centroid(); + Vec2f hull_centroid = unscaled(acc->segment_base_hull().centroid()).cast(); + std::vector hull_lines; + for (const Line &line : acc->segment_base_hull().lines()) { + Vec2f start = unscaled(line.a).cast(); + Vec2f next = unscaled(line.b).cast(); + hull_lines.push_back( { start, next }); + } + if (hull_lines.empty()) { + if (acc->get_support_points().empty()) { + acc->add_support_point(Point::new_scale(checked_lines[acc_lines.second[0]].a), + params.support_points_interface_area); + issues.supports_nedded.emplace_back(to_3d(checked_lines[acc_lines.second[0]].a, print_z), 1.0); + } + hull_lines.push_back( { unscaled(acc->get_support_points()[0]).cast(), + unscaled(acc->get_support_points()[0]).cast() }); + hull_centroid = unscaled(acc->get_support_points()[0]).cast(); + } + + LayerLinesDistancer hull_distancer(std::move(hull_lines)); + + float sticking_force = acc->get_base_area() + * (acc->get_base_height() == 0 ? params.base_adhesion : params.support_adhesion); + float weight = acc->get_accumulated_volume() * params.filament_density * params.gravity_constant; + + for (size_t line_idx : acc_lines.second) { + const ExtrusionLine &line = checked_lines[line_idx]; + + Vec3f extruder_pressure_direction = to_3d(Vec2f(line.b - line.a), 0.0f).normalized(); + Vec2f pivot_site_search = line.b + extruder_pressure_direction.head<2>() * 1000.0f; + extruder_pressure_direction.z() = -0.1f; + extruder_pressure_direction.normalize(); + + size_t nearest_line_idx; + Vec2f pivot; + hull_distancer.signed_distance_from_lines(pivot_site_search, nearest_line_idx, pivot); + + float sticking_arm = (pivot - hull_centroid).norm(); + float sticking_torque = sticking_arm * sticking_force; + + std::cout << "sticking_arm: " << sticking_arm << std::endl; + std::cout << "sticking_torque: " << sticking_torque << std::endl; + + float weight_arm = (pivot - centroid.head<2>()).norm(); + float weight_torque = weight_arm * weight; + + std::cout << "weight_arm: " << sticking_arm << std::endl; + std::cout << "weight_torque: " << weight_torque << std::endl; + + float bed_movement_arm = centroid.z() - acc->get_base_height(); + float bed_movement_force = params.max_acceleration * weight; + float bed_movement_torque = bed_movement_force * bed_movement_arm; + + std::cout << "bed_movement_arm: " << bed_movement_arm << std::endl; + std::cout << "bed_movement_torque: " << bed_movement_torque << std::endl; + + float conflict_torque_arm = (to_3d(Vec2f(pivot - line.b), print_z).cross( + extruder_pressure_direction)).norm(); + float extruder_conflict_torque = params.tolerable_extruder_conflict_force * conflict_torque_arm; + + std::cout << "conflict_torque_arm: " << conflict_torque_arm << std::endl; + std::cout << "extruder_conflict_torque: " << extruder_conflict_torque << std::endl; + + float total_torque = bed_movement_torque + extruder_conflict_torque - weight_torque - sticking_torque; + + std::cout << "total_torque: " << total_torque << " printz: " << print_z << std::endl; + + if (total_torque > 0) { + acc->add_support_point(Point::new_scale(line.b), params.support_points_interface_area); + issues.supports_nedded.emplace_back(to_3d(line.b, print_z), extruder_conflict_torque - sticking_torque); + } + + } + } +} + +Issues check_object_stability(const PrintObject *po, const Params ¶ms) { +#ifdef DEBUG_FILES + FILE *eacc = boost::nowide::fopen(debug_out_path("emerging_accumulators.obj").c_str(), "w"); + FILE *facc = boost::nowide::fopen(debug_out_path("final_accumulators.obj").c_str(), "w"); +#endif + StabilityAccumulators stability_accs; + LayerLinesDistancer prev_layer_lines { { } }; + Issues issues { }; + std::vector checked_lines; + + // PREPARE BASE LAYER + float max_flow_width = 0.0f; + const Layer *layer = po->layers()[0]; + float base_print_z = layer->print_z; + for (const LayerRegion *layer_region : layer->regions()) { + for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { + for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { + const float flow_width = get_flow_width(layer_region, perimeter->role()); + max_flow_width = std::max(flow_width, max_flow_width); + const float mm3_per_mm = float(perimeter->min_mm3_per_mm()); + int id = stability_accs.create_accumulator(base_print_z); + StabilityAccumulator &acc = stability_accs.access(id); + Points points { }; + perimeter->collect_points(points); + for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { + Vec2f start = unscaled(points[point_idx]).cast(); + Vec2f next = unscaled(points[point_idx + 1]).cast(); + ExtrusionLine line { start, next }; + line.stability_accumulator_id = id; + acc.add_base_extrusion(line, flow_width, base_print_z, mm3_per_mm); + checked_lines.push_back(line); + } + if (perimeter->is_loop()) { + Vec2f start = unscaled(points[points.size() - 1]).cast(); + Vec2f next = unscaled(points[0]).cast(); + ExtrusionLine line { start, next }; + line.stability_accumulator_id = id; + acc.add_base_extrusion(line, flow_width, base_print_z, mm3_per_mm); + checked_lines.push_back(line); + } + } // perimeter + } // ex_entity + for (const ExtrusionEntity *ex_entity : layer_region->fills.entities) { + for (const ExtrusionEntity *fill : static_cast(ex_entity)->entities) { + const float flow_width = get_flow_width(layer_region, fill->role()); + max_flow_width = std::max(flow_width, max_flow_width); + const float mm3_per_mm = float(fill->min_mm3_per_mm()); + int id = stability_accs.create_accumulator(base_print_z); + StabilityAccumulator &acc = stability_accs.access(id); + Points points { }; + fill->collect_points(points); + for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { + Vec2f start = unscaled(points[point_idx]).cast(); + Vec2f next = unscaled(points[point_idx + 1]).cast(); + ExtrusionLine line { start, next }; + line.stability_accumulator_id = id; + acc.add_base_extrusion(line, flow_width, base_print_z, mm3_per_mm); + checked_lines.push_back(line); + } + } // fill + } // ex_entity + } // region + +#ifdef DEBUG_FILES + for (const auto &line : checked_lines) { + Vec3f ecolor = stability_accs.get_emerging_color(line.stability_accumulator_id); + fprintf(eacc, "v %f %f %f %f %f %f\n", line.b[0], + line.b[1], base_print_z, ecolor[0], ecolor[1], ecolor[2]); + + Vec3f fcolor = stability_accs.get_final_color(line.stability_accumulator_id); + fprintf(facc, "v %f %f %f %f %f %f\n", line.b[0], + line.b[1], base_print_z, fcolor[0], fcolor[1], fcolor[2]); + } +#endif DEBUG_FILES + + //MERGE BASE LAYER STABILITY ACCS + prev_layer_lines = LayerLinesDistancer { std::move(checked_lines) }; + for (const ExtrusionLine &l : prev_layer_lines.get_lines()) { + size_t nearest_line_idx; + Vec2f nearest_pt; + float dist = prev_layer_lines.signed_distance_from_lines(l.a, nearest_line_idx, nearest_pt); + if (std::abs(dist) < max_flow_width) { + size_t other_line_acc_id = prev_layer_lines.get_line(nearest_line_idx).stability_accumulator_id; + size_t from_id = std::max(other_line_acc_id, l.stability_accumulator_id); + size_t to_id = std::min(other_line_acc_id, l.stability_accumulator_id); + stability_accs.merge_accumulators(from_id, to_id); + } + } + + //CHECK STABILITY OF ALL LAYERS + for (size_t layer_idx = 1; layer_idx < po->layer_count(); ++layer_idx) { + const Layer *layer = po->layers()[layer_idx]; + checked_lines = std::vector { }; + std::vector> fill_points; + float max_fill_flow_width = 0.0f; + + float print_z = layer->print_z; + for (const LayerRegion *layer_region : layer->regions()) { + for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { + for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { + check_extrusion_entity_stability(perimeter, stability_accs, issues, checked_lines, print_z, + layer_region, + prev_layer_lines, params); + } // perimeter + } // ex_entity + for (const ExtrusionEntity *ex_entity : layer_region->fills.entities) { + for (const ExtrusionEntity *fill : static_cast(ex_entity)->entities) { + if (fill->role() == ExtrusionRole::erGapFill + || fill->role() == ExtrusionRole::erBridgeInfill) { + check_extrusion_entity_stability(fill, stability_accs, issues, checked_lines, print_z, + layer_region, + prev_layer_lines, params); + } else { + const float flow_width = get_flow_width(layer_region, fill->role()); + max_fill_flow_width = std::max(max_fill_flow_width, flow_width); + Vec2f start = unscaled(fill->first_point()).cast(); + size_t nearest_line_idx; + Vec2f nearest_pt; + float dist = prev_layer_lines.signed_distance_from_lines(start, nearest_line_idx, nearest_pt); + if (dist < flow_width) { + size_t acc_id = prev_layer_lines.get_line(nearest_line_idx).stability_accumulator_id; + StabilityAccumulator &acc = stability_accs.access(acc_id); + Points points { }; + const float mm3_per_mm = float(fill->min_mm3_per_mm()); + fill->collect_points(points); + for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { + Vec2f start = unscaled(points[point_idx]).cast(); + Vec2f next = unscaled(points[point_idx + 1]).cast(); + ExtrusionLine line { start, next }; + line.stability_accumulator_id = acc_id; + acc.add_extrusion(line, print_z, mm3_per_mm); + } + fill_points.emplace_back(start, acc_id); + } else { + std::cout << " SUPPORTS POINT GEN, start infill in the air? on printz: " << print_z + << std::endl; + } + } + } // fill + } // ex_entity + } // region + + prev_layer_lines = LayerLinesDistancer { std::move(checked_lines) }; + + for (const std::pair &fill_point : fill_points) { + size_t nearest_line_idx; + Vec2f nearest_pt; + float dist = prev_layer_lines.signed_distance_from_lines(fill_point.first, nearest_line_idx, nearest_pt); + if (dist < max_fill_flow_width) { + size_t other_line_acc_id = prev_layer_lines.get_line(nearest_line_idx).stability_accumulator_id; + size_t from_id = std::max(other_line_acc_id, fill_point.second); + size_t to_id = std::min(other_line_acc_id, fill_point.second); + stability_accs.merge_accumulators(from_id, to_id); + } else { + std::cout << " SUPPORTS POINT GEN, no connection on current layer for infill? on printz: " << print_z + << std::endl; + } + } + + check_layer_global_stability(stability_accs, issues, prev_layer_lines.get_lines(), print_z, params); + +#ifdef DEBUG_FILES + for (const auto &line : prev_layer_lines.get_lines()) { + Vec3f ecolor = stability_accs.get_emerging_color(line.stability_accumulator_id); + fprintf(eacc, "v %f %f %f %f %f %f\n", line.b[0], + line.b[1], print_z, ecolor[0], ecolor[1], ecolor[2]); + + Vec3f fcolor = stability_accs.get_final_color(line.stability_accumulator_id); + fprintf(facc, "v %f %f %f %f %f %f\n", line.b[0], + line.b[1], print_z, fcolor[0], fcolor[1], fcolor[2]); + } +#endif + } + +#ifdef DEBUG_FILES + fclose(eacc); + fclose(facc); +#endif + + std::cout << " SUPP: " << issues.supports_nedded.size() << std::endl; + return issues; +} #ifdef DEBUG_FILES void debug_export(Issues issues, std::string file_name) { Slic3r::CNumericLocalesSetter locales_setter; - { FILE *fp = boost::nowide::fopen(debug_out_path((file_name + "_supports.obj").c_str()).c_str(), "w"); if (fp == nullptr) { @@ -685,365 +700,20 @@ void debug_export(Issues issues, std::string file_name) { } fclose(fp); } - } #endif -struct LayerLinesDistancer { - std::vector lines; - AABBTreeIndirect::Tree<2, double> tree; - float line_width; - - LayerLinesDistancer(const Layer *layer) { - float min_region_flow_width { 1.0f }; - for (const auto *region : layer->regions()) { - min_region_flow_width = std::min(min_region_flow_width, - region->flow(FlowRole::frExternalPerimeter).width()); - } - for (const LayerRegion *layer_region : layer->regions()) { - for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { - Points points { }; - ex_entity->collect_points(points); - Vec2d prev = points.size() > 0 ? unscaled(points.front()) : Vec2d::Zero(); - for (size_t idx = 1; idx < points.size(); ++idx) { - lines.emplace_back(prev, unscaled(points[idx])); - } - } // ex_entity - - for (const ExtrusionEntity *ex_entity : layer_region->fills.entities) { - Points points { }; - ex_entity->collect_points(points); - Vec2d prev = points.size() > 0 ? unscaled(points.front()) : Vec2d::Zero(); - for (size_t idx = 1; idx < points.size(); ++idx) { - lines.emplace_back(prev, unscaled(points[idx])); - } - } // ex_entity - } - tree = AABBTreeLines::build_aabb_tree_over_indexed_lines(lines); - line_width = min_region_flow_width; - } - - float distance_from_lines(const Point &point, float point_width) const { - Vec2d p = unscaled(point); - size_t hit_idx_out; - Vec2d hit_point_out; - auto distance = AABBTreeLines::squared_distance_to_indexed_lines(lines, tree, p, hit_idx_out, hit_point_out); - if (distance < 0) - return distance; - - distance = sqrt(distance); - return std::max(float(distance - point_width / 2 - line_width / 20), 0.0f); - } -}; - -//TODO needs revision -float get_flow_width(const LayerRegion *region, ExtrusionRole role) { - switch (role) { - case ExtrusionRole::erBridgeInfill: - return region->flow(FlowRole::frExternalPerimeter).width(); - case ExtrusionRole::erExternalPerimeter: - return region->flow(FlowRole::frExternalPerimeter).width(); - case ExtrusionRole::erGapFill: - return region->flow(FlowRole::frInfill).width(); - case ExtrusionRole::erPerimeter: - return region->flow(FlowRole::frPerimeter).width(); - case ExtrusionRole::erSolidInfill: - return region->flow(FlowRole::frSolidInfill).width(); - case ExtrusionRole::erInternalInfill: - return region->flow(FlowRole::frInfill).width(); - case ExtrusionRole::erTopSolidInfill: - return region->flow(FlowRole::frTopSolidInfill).width(); - default: - return region->flow(FlowRole::frPerimeter).width(); - } -} - -float get_max_allowed_distance(ExtrusionRole role, float flow_width, bool external_perimeters_first, - const Params ¶ms) { // <= distance / flow_width (can be larger for perimeter, if not external perimeter first) - if ((role == ExtrusionRole::erExternalPerimeter || role == ExtrusionRole::erOverhangPerimeter) - && (external_perimeters_first)) { - return params.max_first_ex_perim_unsupported_distance_factor * flow_width; - } else { - return params.max_unsupported_distance_factor * flow_width; - } -} - -struct SegmentAccumulator { - float distance = 0; //accumulated distance - float curvature = 0; //accumulated signed ccw angles - float max_curvature = 0; //max absolute accumulated value - - void add_distance(float dist) { - distance += dist; - } - - void add_angle(float ccw_angle) { - curvature += ccw_angle; - max_curvature = std::max(max_curvature, std::abs(curvature)); - } - - void reset() { - distance = 0; - curvature = 0; - max_curvature = 0; - } - -}; - -Issues check_extrusion_entity_stability(const ExtrusionEntity *entity, float print_z, - const LayerRegion *layer_region, - const LayerLinesDistancer &support_layer, const Params ¶ms) { - - Issues issues { }; - if (entity->is_collection()) { - for (const auto *e : static_cast(entity)->entities) { - issues.add( - check_extrusion_entity_stability(e, print_z, layer_region, support_layer, params)); - } - } else { //single extrusion path, with possible varying parameters - //prepare stack of points on the extrusion path. If there are long segments, additional points might be pushed onto the stack during the algorithm. - std::stack points { }; - for (const auto &p : entity->as_polyline().points) { - points.push(p); - } - - SegmentAccumulator supports_acc { }; - supports_acc.add_distance(params.bridge_distance + 1.0f); // initialize unsupported distance with larger than tolerable distance -> - // -> it prevents extruding perimeter start and short loops into air. - - const auto to_vec3f = [print_z](const Point &point) { - Vec2f tmp = unscale(point).cast(); - return Vec3f(tmp.x(), tmp.y(), print_z); - }; - float region_height = layer_region->layer()->height; - - Point prev_point = points.top(); // prev point of the path. Initialize with first point. - Vec3f prev_fpoint = to_vec3f(prev_point); - float flow_width = get_flow_width(layer_region, entity->role()); - bool external_perimters_first = layer_region->region().config().external_perimeters_first; - const float max_allowed_dist_from_prev_layer = get_max_allowed_distance(entity->role(), flow_width, - external_perimters_first, params); - - while (!points.empty()) { - Point point = points.top(); - points.pop(); - Vec3f fpoint = to_vec3f(point); - float edge_len = (fpoint - prev_fpoint).norm(); - - float dist_from_prev_layer = support_layer.distance_from_lines(point, flow_width); - if (dist_from_prev_layer < 0) { // dist from prev layer not found, assume empty layer - issues.supports_nedded.push_back(SupportPoint(fpoint, 1.0f)); - supports_acc.reset(); - } - - float angle = 0; - if (!points.empty()) { - const Vec2f v1 = (fpoint - prev_fpoint).head<2>(); - const Vec2f v2 = unscale(points.top()).cast() - fpoint.head<2>(); - float dot = v1(0) * v2(0) + v1(1) * v2(1); - float cross = v1(0) * v2(1) - v1(1) * v2(0); - angle = float(atan2(float(cross), float(dot))); // ccw angle, TODO replace with angle func, once it gets into master - } - - supports_acc.add_angle(angle); - - if (dist_from_prev_layer > max_allowed_dist_from_prev_layer) { //extrusion point is unsupported - supports_acc.add_distance(edge_len); // for algorithm simplicity, expect that the whole line between prev and current point is unsupported - - if (supports_acc.distance // if unsupported distance is larger than bridge distance linearly decreased by curvature, enforce supports. - > params.bridge_distance - / (1.0f + (supports_acc.max_curvature - * params.bridge_distance_decrease_by_curvature_factor / PI))) { - issues.supports_nedded.push_back(SupportPoint(fpoint, 1.0f)); - supports_acc.reset(); - } - } else { - supports_acc.reset(); - } - - // Estimation of short curvy segments which are not supported -> problems with curling - if (dist_from_prev_layer > -max_allowed_dist_from_prev_layer * 0.7071) { //extrusion point is unsupported or poorly supported - float dist_factor = 0.5f + 0.5f * (dist_from_prev_layer + max_allowed_dist_from_prev_layer * 0.7071) - / max_allowed_dist_from_prev_layer; - issues.curling_up.push_back( - CurledFilament(fpoint, - dist_factor - * (0.25f * region_height + region_height * 0.75f * std::abs(angle) / PI))); - } - - prev_point = point; - prev_fpoint = fpoint; - - if (!points.empty()) { //oversampling if necessary - Vec2f next = unscale(points.top()).cast(); - Vec2f reverse_v = fpoint.head<2>() - next; // vector from next to current - float dist_to_next = reverse_v.norm(); - reverse_v.normalize(); - int new_points_count = dist_to_next / params.bridge_distance; - float step_size = dist_to_next / (new_points_count + 1); - for (int i = 1; i <= new_points_count; ++i) { - points.push(Point::new_scale(Vec2f(next + reverse_v * (i * step_size)))); - } - } - } - } - return issues; -} - -void distribute_layer_volume(const PrintObject *po, size_t layer_idx, - BalanceDistributionGrid &balance_grid) { - const Layer *layer = po->get_layer(layer_idx); - for (const LayerRegion *region : layer->regions()) { - for (const ExtrusionEntity *collections : region->fills.entities) { - for (const ExtrusionEntity *entity : static_cast(collections)->entities) { - for (const Line &line : entity->as_polyline().lines()) { - balance_grid.distribute_edge(line.a, line.b, layer->print_z, - get_flow_width(region, entity->role()), layer->height); - } - } - } - for (const ExtrusionEntity *collections : region->perimeters.entities) { - for (const ExtrusionEntity *entity : static_cast(collections)->entities) { - for (const Line &line : entity->as_polyline().lines()) { - balance_grid.distribute_edge(line.a, line.b, layer->print_z, - get_flow_width(region, entity->role()), layer->height); - } - } - } - } -} - -Issues check_layer_stability(const PrintObject *po, size_t layer_idx, bool full_check, - const Params ¶ms) { - const Layer *layer = po->get_layer(layer_idx); - //Prepare edge grid of previous layer, will be used to check if the extrusion path is supported - LayerLinesDistancer support_layer(layer->lower_layer); - - Issues issues { }; - if (full_check) { // If full check; check stability of perimeters, gap fills, and bridges. - for (const LayerRegion *layer_region : layer->regions()) { - for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { - for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { - issues.add( - check_extrusion_entity_stability(perimeter, layer->print_z, layer_region, - support_layer, params)); - } // perimeter - } // ex_entity - for (const ExtrusionEntity *ex_entity : layer_region->fills.entities) { - for (const ExtrusionEntity *fill : static_cast(ex_entity)->entities) { - if (fill->role() == ExtrusionRole::erGapFill - || fill->role() == ExtrusionRole::erBridgeInfill) { - issues.add( - check_extrusion_entity_stability(fill, layer->print_z, layer_region, - support_layer, - params)); - } - } // fill - } // ex_entity - } // region - - } else { // If NOT full check, check only external perimeters - for (const LayerRegion *layer_region : layer->regions()) { - for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { - for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { - if (perimeter->role() == ExtrusionRole::erExternalPerimeter - || perimeter->role() == ExtrusionRole::erOverhangPerimeter) { - issues.add( - check_extrusion_entity_stability(perimeter, layer->print_z, layer_region, - support_layer, params)); - }; // ex_perimeter - } // perimeter - } // ex_entity - } //region - } - - return issues; -} - -} //Impl End - std::vector quick_search(const PrintObject *po, const Params ¶ms) { - using namespace Impl; - - BalanceDistributionGrid grid { }; - grid.init(po, 0, po->layers().size()); - distribute_layer_volume(po, 0, grid); - std::mutex grid_mutex; - - size_t layer_count = po->layer_count(); - std::vector layer_needs_supports(layer_count, false); - tbb::parallel_for(tbb::blocked_range(1, layer_count), [&](tbb::blocked_range r) { - BalanceDistributionGrid balance_grid { }; - balance_grid.init(po, r.begin(), r.end()); - - for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) { - distribute_layer_volume(po, layer_idx, balance_grid); - auto layer_issues = check_layer_stability(po, layer_idx, false, params); - if (!layer_issues.supports_nedded.empty()) { - layer_needs_supports[layer_idx] = true; - } - } - - grid_mutex.lock(); - grid.merge(balance_grid); - grid_mutex.unlock(); - }); - - std::vector problematic_layers; - for (size_t index = 0; index < layer_needs_supports.size(); ++index) { - if (layer_needs_supports[index]) { - problematic_layers.push_back(index); - } - } - return problematic_layers; + check_object_stability(po, params); + return {}; } Issues full_search(const PrintObject *po, const Params ¶ms) { - using namespace Impl; - - BalanceDistributionGrid grid { }; - grid.init(po, 0, po->layers().size()); - distribute_layer_volume(po, 0, grid); - std::mutex grid_mutex; - - size_t layer_count = po->layer_count(); - Issues found_issues = tbb::parallel_reduce(tbb::blocked_range(1, layer_count), Issues { }, - [&](tbb::blocked_range r, const Issues &init) { - BalanceDistributionGrid balance_grid { }; - balance_grid.init(po, r.begin(), r.end()); - Issues issues = init; - for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) { - distribute_layer_volume(po, layer_idx, balance_grid); - auto layer_issues = check_layer_stability(po, layer_idx, true, params); - if (!layer_issues.empty()) { - issues.add(layer_issues); - } - } - - grid_mutex.lock(); - grid.merge(balance_grid); - grid_mutex.unlock(); - - return issues; - }, - [](Issues left, const Issues &right) { - left.add(right); - return left; - } - ); -#ifdef DEBUG_FILES - Impl::debug_export(found_issues, "pre_issues"); -#endif - - grid.analyze(found_issues, params); - -#ifdef DEBUG_FILES - grid.debug_export(); - Impl::debug_export(found_issues, "issues"); -#endif - - return found_issues; -} + auto issues = check_object_stability(po, params); + debug_export(issues, "issues"); + return issues; } +} //SupportableIssues End } + diff --git a/src/libslic3r/SupportableIssuesSearch.hpp b/src/libslic3r/SupportableIssuesSearch.hpp index d5736a3bde..e8a9f0e0cd 100644 --- a/src/libslic3r/SupportableIssuesSearch.hpp +++ b/src/libslic3r/SupportableIssuesSearch.hpp @@ -14,15 +14,15 @@ struct Params { float bridge_distance = 10.0f; //mm float bridge_distance_decrease_by_curvature_factor = 5.0f; // allowed bridge distance = bridge_distance / ( 1 + this factor * (curvature / PI) ) - float base_adhesion = 2000.0f; // adhesion per mm^2 of first layer; Force needed to remove the object from the bed, divided by the adhesion area (g/mm*s^2) - float support_adhesion = 1000.0f; // adhesion per mm^2 of support interface layer - float support_points_interface_area = 5.0f; // mm^2 + // Adhesion computation : from experiment, PLA holds about 3g per mm^2 of base area (with reserve); So it can withstand about 3*gravity_constant force per mm^2 + float base_adhesion = 3.0f * gravity_constant; // adhesion per mm^2 of first layer + float support_adhesion = 1.0f * gravity_constant; // adhesion per mm^2 of support interface layer + float support_points_interface_area = 2.0f; // mm^2 float max_acceleration = 1000.0f; // mm/s^2 ; max acceleration of object (bed) in XY float filament_density = 1.25f * 0.001f; // g/mm^3 ; Common filaments are very lightweight, so precise number is not that important float tolerable_extruder_conflict_force = 50.0f * gravity_constant; // force that can occasionally push the model due to various factors (filament leaks, small curling, ... ); current value corresponds to weight of 50g - float max_curled_conflict_extruder_force = 200.0f * gravity_constant; // for areas with possible high layered curled filaments, max force to account fo ; current value corresponds to weight of 200g - + float max_curled_conflict_extruder_force = 200.0f * gravity_constant; // for areas with possible high layered curled filaments, max force to account for; current value corresponds to weight of 200g }; struct SupportPoint { diff --git a/src/libslic3r/SupportableIssuesSearchRefactoring.cpp b/src/libslic3r/SupportableIssuesSearchRefactoring.cpp deleted file mode 100644 index 35b567c55e..0000000000 --- a/src/libslic3r/SupportableIssuesSearchRefactoring.cpp +++ /dev/null @@ -1,635 +0,0 @@ -#include "SupportableIssuesSearch.hpp" - -#include "tbb/parallel_for.h" -#include "tbb/blocked_range.h" -#include "tbb/parallel_reduce.h" -#include -#include -#include -#include - -#include "AABBTreeLines.hpp" -#include "libslic3r/Layer.hpp" -#include "libslic3r/ClipperUtils.hpp" -#include "Geometry/ConvexHull.hpp" - -#define DEBUG_FILES - -#ifdef DEBUG_FILES -#include -#include "libslic3r/Color.hpp" -#endif - -namespace Slic3r { - -static const size_t NULL_ACC_ID = std::numeric_limits::max(); - -class ExtrusionLine -{ -public: - ExtrusionLine() : - a(Vec2f::Zero()), b(Vec2f::Zero()), len(0.0f) { - } - ExtrusionLine(const Vec2f &_a, const Vec2f &_b) : - a(_a), b(_b), len((_a - _b).norm()) { - } - - float length() { - return (a - b).norm(); - } - - Vec2f a; - Vec2f b; - float len; - - size_t supported_segment_accumulator_id = NULL_ACC_ID; - - static const constexpr int Dim = 2; - using Scalar = Vec2f::Scalar; -}; - -auto get_a(ExtrusionLine &&l) { - return l.a; -} -auto get_b(ExtrusionLine &&l) { - return l.b; -} - -namespace SupportableIssues { - -void Issues::add(const Issues &layer_issues) { - supports_nedded.insert(supports_nedded.end(), layer_issues.supports_nedded.begin(), - layer_issues.supports_nedded.end()); - curling_up.insert(curling_up.end(), layer_issues.curling_up.begin(), layer_issues.curling_up.end()); -} - -bool Issues::empty() const { - return supports_nedded.empty() && curling_up.empty(); -} - -SupportPoint::SupportPoint(const Vec3f &position, float weight) : - position(position), weight(weight) { -} - -CurledFilament::CurledFilament(const Vec3f &position, float estimated_height) : - position(position), estimated_height(estimated_height) { -} - -CurledFilament::CurledFilament(const Vec3f &position) : - position(position), estimated_height(0.0f) { -} - -class LayerLinesDistancer { -private: - std::vector lines; - AABBTreeIndirect::Tree<2, float> tree; - -public: - explicit LayerLinesDistancer(std::vector &&lines) : - lines(lines) { - tree = AABBTreeLines::build_aabb_tree_over_indexed_lines(lines); - } - - // negative sign means inside - float signed_distance_from_lines(const Vec2f &point, size_t &nearest_line_index_out, - Vec2f &nearest_point_out) const { - auto distance = AABBTreeLines::squared_distance_to_indexed_lines(lines, tree, point, nearest_line_index_out, - nearest_point_out); - if (distance < 0) - return std::numeric_limits::infinity(); - - distance = sqrt(distance); - const ExtrusionLine &line = lines[nearest_line_index_out]; - Vec2f v1 = line.b - line.a; - Vec2f v2 = point - line.a; - if ((v1.x() * v2.y()) - (v1.y() * v2.x()) > 0.0) { - distance *= -1; - } - return distance; - } - - const ExtrusionLine& get_line(size_t line_idx) const { - return lines[line_idx]; - } -}; - -class StabilityAccumulator { -private: - Polygon base_convex_hull { }; - Points support_points { }; - Vec3f centroid_accumulator = Vec3f::Zero(); - float accumulated_volume { }; - float base_area { }; - float base_height { }; - -public: - explicit StabilityAccumulator(float base_height) : - base_height(base_height) { - } - - void add_base_extrusion(const ExtrusionLine &line, float width, float print_z, float cross_section) { - base_area += line.len * width; - support_points.push_back(Point::new_scale(line.a)); - support_points.push_back(Point::new_scale(line.b)); - base_convex_hull.clear(); - add_extrusion(line, print_z, cross_section); - } - - void add_support_point(const Point &position, float area) { - support_points.push_back(position); - base_convex_hull.clear(); - base_area += area; - } - - void add_extrusion(const ExtrusionLine &line, float print_z, float cross_section) { - float volume = line.len * cross_section; - accumulated_volume += volume; - Vec2f center = (line.a + line.b) / 2.0f; - centroid_accumulator += volume * Vec3f(center.x(), center.y(), print_z); - } - - Vec3f get_centroid() const { - return centroid_accumulator / accumulated_volume; - } - - float get_base_area() const { - return base_area; - } - float get_base_height() const { - return base_height; - } - - const Polygon& segment_base_hull() { - if (this->base_convex_hull.empty()) { - this->base_convex_hull = Geometry::convex_hull(this->support_points); - } - return this->base_convex_hull; - } - - const Points& get_support_points() { - return support_points; - } - - void add_from(const StabilityAccumulator &acc) { - this->support_points.insert(this->support_points.end(), acc.support_points.begin(), - acc.support_points.end()); - base_convex_hull.clear(); - this->centroid_accumulator += acc.centroid_accumulator; - this->accumulated_volume += acc.accumulated_volume; - this->base_area += acc.base_area; - } -}; - -struct StabilityAccumulators { -private: - size_t next_id = 0; - std::unordered_map mapping; - std::vector acccumulators; - - void merge_to(size_t from_id, size_t to_id) { - StabilityAccumulator &from_acc = this->access(from_id); - StabilityAccumulator &to_acc = this->access(to_id); - if (&from_acc == &to_acc) { - return; - } - to_acc.add_from(from_acc); - mapping[from_id] = mapping[to_id]; - from_acc = StabilityAccumulator { 0.0f }; - - } - -public: - StabilityAccumulators() = default; - - int create_accumulator(float base_height) { - size_t id = next_id; - next_id++; - mapping[id] = acccumulators.size(); - acccumulators.push_back(StabilityAccumulator { base_height }); - return id; - } - - StabilityAccumulator& access(size_t id) { - return acccumulators[mapping[id]]; - } - - void merge_accumulators(size_t from_id, size_t to_id) { - if (from_id == NULL_ACC_ID || to_id == NULL_ACC_ID) { - return; - } - StabilityAccumulator &from_acc = this->access(from_id); - StabilityAccumulator &to_acc = this->access(to_id); - if (&from_acc == &to_acc) { - return; - } - to_acc.add_from(from_acc); - mapping[from_id] = mapping[to_id]; - from_acc = StabilityAccumulator { 0.0f }; - } - -#ifdef DEBUG_FILES - Vec3f get_emerging_color(size_t id) { - if (mapping.find(id) == mapping.end()) { - std::cerr << " ERROR: uknown accumulator ID: " << id << std::endl; - return Vec3f(1.0f, 1.0f, 1.0f); - } - - size_t pseudornd = ((id + 127) * 33331 + 6907) % 13; - return value_to_rgbf(0.0f, 13.0f, float(pseudornd)); - } - - Vec3f get_final_color(size_t id) { - if (mapping.find(id) == mapping.end()) { - std::cerr << " ERROR: uknown accumulator ID: " << id << std::endl; - return Vec3f(1.0f, 1.0f, 1.0f); - } - - size_t pseudornd = ((mapping[id] + 127) * 33331 + 6907) % 13; - return value_to_rgbf(0.0f, 13.0f, float(pseudornd)); - } -#endif DEBUG_FILES -}; - -float get_flow_width(const LayerRegion *region, ExtrusionRole role) { - switch (role) { - case ExtrusionRole::erBridgeInfill: - return region->flow(FlowRole::frExternalPerimeter).width(); - case ExtrusionRole::erExternalPerimeter: - return region->flow(FlowRole::frExternalPerimeter).width(); - case ExtrusionRole::erGapFill: - return region->flow(FlowRole::frInfill).width(); - case ExtrusionRole::erPerimeter: - return region->flow(FlowRole::frPerimeter).width(); - case ExtrusionRole::erSolidInfill: - return region->flow(FlowRole::frSolidInfill).width(); - case ExtrusionRole::erInternalInfill: - return region->flow(FlowRole::frInfill).width(); - case ExtrusionRole::erTopSolidInfill: - return region->flow(FlowRole::frTopSolidInfill).width(); - default: - return region->flow(FlowRole::frPerimeter).width(); - } -} - -struct ExtrusionPropertiesAccumulator { - float distance = 0; //accumulated distance - float curvature = 0; //accumulated signed ccw angles - float max_curvature = 0; //max absolute accumulated value - - void add_distance(float dist) { - distance += dist; - } - - void add_angle(float ccw_angle) { - curvature += ccw_angle; - max_curvature = std::max(max_curvature, std::abs(curvature)); - } - - void reset() { - distance = 0; - curvature = 0; - max_curvature = 0; - } -}; - -void check_extrusion_entity_stability(const ExtrusionEntity *entity, - StabilityAccumulators &stability_accs, - Issues &issues, - std::vector &checked_lines, - float print_z, - const LayerRegion *layer_region, - const LayerLinesDistancer &prev_layer_lines, - const Params ¶ms) { - - if (entity->is_collection()) { - for (const auto *e : static_cast(entity)->entities) { - check_extrusion_entity_stability(e, stability_accs, issues, checked_lines, print_z, layer_region, - prev_layer_lines, - params); - } - } else { //single extrusion path, with possible varying parameters - const auto to_vec3f = [print_z](const Point &point) { - Vec2f tmp = unscale(point).cast(); - return Vec3f(tmp.x(), tmp.y(), print_z); - }; - Points points { }; - entity->collect_points(points); - std::vector lines; - lines.reserve(points.size() * 1.5); - lines.emplace_back(unscaled(points[0]).cast(), unscaled(points[0]).cast()); - for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { - Vec2f start = unscaled(points[point_idx]).cast(); - Vec2f next = unscaled(points[point_idx + 1]).cast(); - Vec2f v = next - start; // vector from next to current - float dist_to_next = v.norm(); - v.normalize(); - int lines_count = int(std::ceil(dist_to_next / params.bridge_distance)); - float step_size = dist_to_next / lines_count; - for (int i = 0; i < lines_count; ++i) { - Vec2f a(start + v * (i * step_size)); - Vec2f b(start + v * ((i + 1) * step_size)); - lines.emplace_back(a, b); - } - } - - size_t current_stability_acc = NULL_ACC_ID; - ExtrusionPropertiesAccumulator bridging_acc { }; - bridging_acc.add_distance(params.bridge_distance + 1.0f); // Initialise unsupported distance with larger than tolerable distance -> - // -> it prevents extruding perimeter start and short loops into air. - const float flow_width = get_flow_width(layer_region, entity->role()); - const float region_height = layer_region->layer()->height; - const float max_allowed_dist_from_prev_layer = flow_width; - - for (size_t line_idx = 0; line_idx < lines.size(); ++line_idx) { - ExtrusionLine ¤t_line = lines[line_idx]; - Point current = Point::new_scale(current_line.b); - float cross_section = region_height * flow_width * 0.7071f; - - float curr_angle = 0; - if (line_idx + 1 < lines.size()) { - const Vec2f v1 = current_line.b - current_line.a; - const Vec2f v2 = lines[line_idx + 1].b - lines[line_idx + 1].a; - curr_angle = angle(v1, v2); - } - bridging_acc.add_angle(curr_angle); - - size_t nearest_line_idx; - Vec2f nearest_point; - float dist_from_prev_layer = prev_layer_lines.signed_distance_from_lines(current_line.b, nearest_line_idx, - nearest_point); - if (dist_from_prev_layer < max_allowed_dist_from_prev_layer) { - const ExtrusionLine &nearest_line = prev_layer_lines.get_line(nearest_line_idx); - size_t acc_id = nearest_line.supported_segment_accumulator_id; - stability_accs.merge_accumulators(std::max(acc_id, current_stability_acc), - std::min(acc_id, current_stability_acc)); - current_stability_acc = std::min(acc_id, current_stability_acc); - current_line.supported_segment_accumulator_id = current_stability_acc; - stability_accs.access(current_stability_acc).add_extrusion(current_line, print_z, cross_section); - bridging_acc.reset(); - // TODO curving here - } else { - bridging_acc.add_distance(current_line.len); - if (current_stability_acc == NULL_ACC_ID) { - current_stability_acc = stability_accs.create_accumulator(print_z); - } - StabilityAccumulator ¤t_segment = stability_accs.access(current_stability_acc); - current_line.supported_segment_accumulator_id = current_stability_acc; - current_segment.add_extrusion(current_line, print_z, cross_section); - if (bridging_acc.distance // if unsupported distance is larger than bridge distance linearly decreased by curvature, enforce supports. - > params.bridge_distance - / (1.0f + (bridging_acc.max_curvature - * params.bridge_distance_decrease_by_curvature_factor / PI))) { - current_segment.add_support_point(current, params.support_points_interface_area); - issues.supports_nedded.emplace_back(to_vec3f(current), 1.0); - bridging_acc.reset(); - } - } - } - checked_lines.insert(checked_lines.end(), lines.begin(), lines.end()); - } -} - -void check_layer_global_stability(StabilityAccumulators &stability_accs, - Issues &issues, - const std::vector &checked_lines, - float print_z, - const Params ¶ms) { - std::unordered_map> layer_accs_lines; - for (size_t i = 0; i < checked_lines.size(); ++i) { - layer_accs_lines[&stability_accs.access(checked_lines[i].supported_segment_accumulator_id)].push_back(i); - } - - for (auto &acc_lines : layer_accs_lines) { - StabilityAccumulator *acc = acc_lines.first; - Vec3f centroid = acc->get_centroid(); - Vec2f hull_centroid = unscaled(acc->segment_base_hull().centroid()).cast(); - std::vector hull_lines; - for (const Line &line : acc->segment_base_hull().lines()) { - Vec2f start = unscaled(line.a).cast(); - Vec2f next = unscaled(line.b).cast(); - hull_lines.push_back( { start, next }); - } - if (hull_lines.empty()) { - if (acc->get_support_points().empty()) { - acc->add_support_point(Point::new_scale(checked_lines[acc_lines.second[0]].a), - params.support_points_interface_area); - issues.supports_nedded.emplace_back(to_3d(checked_lines[acc_lines.second[0]].a, print_z), 1.0); - } - hull_lines.push_back( { unscaled(acc->get_support_points()[0]).cast(), - unscaled(acc->get_support_points()[0]).cast() }); - hull_centroid = unscaled(acc->get_support_points()[0]).cast(); - } - - LayerLinesDistancer hull_distancer(std::move(hull_lines)); - - size_t _li; - Vec2f _p; - bool centroid_inside_hull = hull_distancer.signed_distance_from_lines(centroid.head<2>(), _li, _p) < 0; - - float sticking_force = acc->get_base_area() - * (acc->get_base_height() == 0 ? params.base_adhesion : params.support_adhesion); -// float weight = acc-> * params.filament_density * params.gravity_constant; -// float weight_torque = embedded_distance * weight; -// if (!inside) { -// weight_torque *= -1; -// } - - for (size_t line_idx : acc_lines.second){ - const ExtrusionLine &line = checked_lines[line_idx]; - - size_t nearest_line_idx; - Vec2f nearest_hull_point; - float hull_distance = hull_distancer.signed_distance_from_lines(line.b, nearest_line_idx, - nearest_hull_point); - - float sticking_torque = (nearest_hull_point - hull_centroid).norm() * sticking_force; - - std::cout << "sticking_torque: " << sticking_torque << std::endl; - - - Vec3f extruder_pressure_direction = to_3d(Vec2f(line.b - line.a), 0.0f).normalized(); - if (hull_distance > 0) { - extruder_pressure_direction.z() = -0.333f; - extruder_pressure_direction.normalize(); - } - float pressure_torque_arm = (to_3d(Vec2f(nearest_hull_point - line.b), print_z).cross(extruder_pressure_direction)).norm(); - - float extruder_conflict_torque = params.tolerable_extruder_conflict_force * pressure_torque_arm; - - std::cout << "extruder_conflict_torque: " << extruder_conflict_torque << std::endl; - - if (extruder_conflict_torque > sticking_torque) { - acc->add_support_point(Point::new_scale(line.b), params.support_points_interface_area); - issues.supports_nedded.emplace_back(to_3d(line.b, print_z), extruder_conflict_torque - sticking_torque); - } - - } - } -} - -Issues check_object_stability(const PrintObject *po, const Params ¶ms) { -#ifdef DEBUG_FILES - FILE *eacc = boost::nowide::fopen(debug_out_path("emerging_accumulators.obj").c_str(), "w"); - FILE *facc = boost::nowide::fopen(debug_out_path("final_accumulators.obj").c_str(), "w"); -#endif DEBUG_FILES - StabilityAccumulators stability_accs; - LayerLinesDistancer prev_layer_lines { { } }; - Issues issues { }; - std::vector checked_lines; - - const Layer *layer = po->layers()[0]; - float base_print_z = layer->print_z; - for (const LayerRegion *layer_region : layer->regions()) { - for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { - for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { - const float flow_width = get_flow_width(layer_region, perimeter->role()); - const float region_height = layer_region->layer()->height; - const float cross_section = region_height * flow_width * 0.7071f; - int id = stability_accs.create_accumulator(base_print_z); - StabilityAccumulator &acc = stability_accs.access(id); - Points points { }; - perimeter->collect_points(points); - for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { - Vec2f start = unscaled(points[point_idx]).cast(); - Vec2f next = unscaled(points[point_idx + 1]).cast(); - ExtrusionLine line { start, next }; - line.supported_segment_accumulator_id = id; - acc.add_base_extrusion(line, flow_width, base_print_z, cross_section); - checked_lines.push_back(line); - } - } // perimeter - } // ex_entity - for (const ExtrusionEntity *ex_entity : layer_region->fills.entities) { - for (const ExtrusionEntity *fill : static_cast(ex_entity)->entities) { - const float flow_width = get_flow_width(layer_region, fill->role()); - const float region_height = layer_region->layer()->height; - const float cross_section = region_height * flow_width * 0.7071f; - int id = stability_accs.create_accumulator(base_print_z); - StabilityAccumulator &acc = stability_accs.access(id); - Points points { }; - fill->collect_points(points); - for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { - Vec2f start = unscaled(points[point_idx]).cast(); - Vec2f next = unscaled(points[point_idx + 1]).cast(); - ExtrusionLine line { start, next }; - line.supported_segment_accumulator_id = id; - acc.add_base_extrusion(line, flow_width, base_print_z, cross_section); - checked_lines.push_back(line); - } - } // fill - } // ex_entity - } // region - -#ifdef DEBUG_FILES - for (const auto &line : checked_lines) { - Vec3f ecolor = stability_accs.get_emerging_color(line.supported_segment_accumulator_id); - fprintf(eacc, "v %f %f %f %f %f %f\n", line.b[0], - line.b[1], base_print_z, ecolor[0], ecolor[1], ecolor[2]); - - Vec3f fcolor = stability_accs.get_final_color(line.supported_segment_accumulator_id); - fprintf(facc, "v %f %f %f %f %f %f\n", line.b[0], - line.b[1], base_print_z, fcolor[0], fcolor[1], fcolor[2]); - } -#endif DEBUG_FILES - - for (size_t layer_idx = 1; layer_idx < po->layer_count(); ++layer_idx) { - const Layer *layer = po->layers()[layer_idx]; - prev_layer_lines = LayerLinesDistancer { std::move(checked_lines) }; - checked_lines = std::vector { }; - - float print_z = layer->print_z; - for (const LayerRegion *layer_region : layer->regions()) { - for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { - for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { - check_extrusion_entity_stability(perimeter, stability_accs, issues, checked_lines, print_z, - layer_region, - prev_layer_lines, params); - } // perimeter - } // ex_entity - for (const ExtrusionEntity *ex_entity : layer_region->fills.entities) { - for (const ExtrusionEntity *fill : static_cast(ex_entity)->entities) { - if (fill->role() == ExtrusionRole::erGapFill - || fill->role() == ExtrusionRole::erBridgeInfill) { - check_extrusion_entity_stability(fill, stability_accs, issues, checked_lines, print_z, - layer_region, - prev_layer_lines, params); - } - } // fill - } // ex_entity - } // region - - check_layer_global_stability(stability_accs, issues, checked_lines, print_z, params); - -#ifdef DEBUG_FILES - for (const auto &line : checked_lines) { - Vec3f ecolor = stability_accs.get_emerging_color(line.supported_segment_accumulator_id); - fprintf(eacc, "v %f %f %f %f %f %f\n", line.b[0], - line.b[1], print_z, ecolor[0], ecolor[1], ecolor[2]); - - Vec3f fcolor = stability_accs.get_final_color(line.supported_segment_accumulator_id); - fprintf(facc, "v %f %f %f %f %f %f\n", line.b[0], - line.b[1], print_z, fcolor[0], fcolor[1], fcolor[2]); - } -#endif DEBUG_FILES - } - -#ifdef DEBUG_FILES - fclose(eacc); - fclose(facc); -#endif DEBUG_FILES - - std::cout << " SUPP: " << issues.supports_nedded.size() << std::endl; - return issues; -} - -#ifdef DEBUG_FILES -void debug_export(Issues issues, std::string file_name) { - Slic3r::CNumericLocalesSetter locales_setter; - { - FILE *fp = boost::nowide::fopen(debug_out_path((file_name + "_supports.obj").c_str()).c_str(), "w"); - if (fp == nullptr) { - BOOST_LOG_TRIVIAL(error) - << "Debug files: Couldn't open " << file_name << " for writing"; - return; - } - - for (size_t i = 0; i < issues.supports_nedded.size(); ++i) { - fprintf(fp, "v %f %f %f %f %f %f\n", issues.supports_nedded[i].position(0), - issues.supports_nedded[i].position(1), - issues.supports_nedded[i].position(2), 1.0, 0.0, 1.0); - } - - fclose(fp); - } - { - FILE *fp = boost::nowide::fopen(debug_out_path((file_name + "_curling.obj").c_str()).c_str(), "w"); - if (fp == nullptr) { - BOOST_LOG_TRIVIAL(error) - << "Debug files: Couldn't open " << file_name << " for writing"; - return; - } - - for (size_t i = 0; i < issues.curling_up.size(); ++i) { - fprintf(fp, "v %f %f %f %f %f %f\n", issues.curling_up[i].position(0), - issues.curling_up[i].position(1), - issues.curling_up[i].position(2), 0.0, 1.0, 0.0); - } - fclose(fp); - } -} -#endif - -std::vector quick_search(const PrintObject *po, const Params ¶ms) { - check_object_stability(po, params); - return {}; -} - -Issues full_search(const PrintObject *po, const Params ¶ms) { - auto issues = check_object_stability(po, params); - debug_export(issues, "issues"); - return issues; - -} -} //SupportableIssues End -} - From 8dc3956b64770eed64ef263d03a8eb716d2a2314 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Tue, 21 Jun 2022 16:04:29 +0200 Subject: [PATCH 28/78] bug fixes, raycasting to find good support spot --- src/libslic3r/PrintObject.cpp | 38 +++++------ src/libslic3r/SupportableIssuesSearch.cpp | 78 ++++++++++------------- src/libslic3r/SupportableIssuesSearch.hpp | 10 ++- src/libslic3r/TriangleSelectorWrapper.cpp | 34 +++++----- src/libslic3r/TriangleSelectorWrapper.hpp | 2 +- 5 files changed, 80 insertions(+), 82 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index f0e435dea8..40f740b91c 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -423,27 +423,29 @@ void PrintObject::find_supportable_issues() SupportableIssues::Issues issues = SupportableIssues::full_search(this); //TODO fix // if (!issues.supports_nedded.empty()) { - auto obj_transform = this->trafo_centered(); - for (ModelVolume *model_volume : this->model_object()->volumes) { - if (model_volume->type() == ModelVolumeType::MODEL_PART) { - Transform3d model_transformation = model_volume->get_matrix(); - Transform3d inv_transform = (obj_transform * model_transformation).inverse(); - TriangleSelectorWrapper selector { model_volume->mesh() }; + auto obj_transform = this->trafo_centered(); + for (ModelVolume *model_volume : this->model_object()->volumes) { + if (model_volume->type() == ModelVolumeType::MODEL_PART) { + Transform3d model_transformation = model_volume->get_matrix(); + Transform3f inv_transform = (obj_transform * model_transformation).inverse().cast(); + TriangleSelectorWrapper selector { model_volume->mesh() }; - for (const SupportableIssues::SupportPoint &support_point : issues.supports_nedded) { - selector.enforce_spot(Vec3f(inv_transform.cast() * support_point.position), 0.5f); - } - - model_volume->supported_facets.set(selector.selector); - -#if 1 - indexed_triangle_set copy = model_volume->mesh().its; - its_transform(copy, obj_transform * model_transformation); - its_write_obj(copy, - debug_out_path("model.obj").c_str()); -#endif + for (const SupportableIssues::SupportPoint &support_point : issues.supports_nedded) { + Vec3f point = Vec3f(inv_transform * support_point.position); + Vec3f origin = Vec3f( + inv_transform * Vec3f(support_point.position.x(), support_point.position.y(), 0.0f)); + selector.enforce_spot(point, origin, 0.5f); } + + model_volume->supported_facets.set(selector.selector); +#if 1 + indexed_triangle_set copy = model_volume->mesh().its; + its_transform(copy, obj_transform * model_transformation); + its_write_obj(copy, + debug_out_path("model.obj").c_str()); +#endif } + } // } } diff --git a/src/libslic3r/SupportableIssuesSearch.cpp b/src/libslic3r/SupportableIssuesSearch.cpp index 73f89e0d63..ce17020ed7 100644 --- a/src/libslic3r/SupportableIssuesSearch.cpp +++ b/src/libslic3r/SupportableIssuesSearch.cpp @@ -191,7 +191,7 @@ struct StabilityAccumulators { private: size_t next_id = 0; std::unordered_map mapping; - std::vector acccumulators; + std::vector accumulators; void merge_to(size_t from_id, size_t to_id) { StabilityAccumulator &from_acc = this->access(from_id); @@ -211,13 +211,13 @@ public: int create_accumulator(float base_height) { size_t id = next_id; next_id++; - mapping[id] = acccumulators.size(); - acccumulators.push_back(StabilityAccumulator { base_height }); + mapping[id] = accumulators.size(); + accumulators.push_back(StabilityAccumulator { base_height }); return id; } StabilityAccumulator& access(size_t id) { - return acccumulators[mapping[id]]; + return accumulators[mapping[id]]; } void merge_accumulators(size_t from_id, size_t to_id) { @@ -235,24 +235,14 @@ public: } #ifdef DEBUG_FILES - Vec3f get_emerging_color(size_t id) { + Vec3f get_accumulator_color(size_t id) { if (mapping.find(id) == mapping.end()) { std::cerr << " ERROR: uknown accumulator ID: " << id << std::endl; return Vec3f(1.0f, 1.0f, 1.0f); } - size_t pseudornd = ((id + 127) * 33331 + 6907) % 13; - return value_to_rgbf(0.0f, 13.0f, float(pseudornd)); - } - - Vec3f get_final_color(size_t id) { - if (mapping.find(id) == mapping.end()) { - std::cerr << " ERROR: uknown accumulator ID: " << id << std::endl; - return Vec3f(1.0f, 1.0f, 1.0f); - } - - size_t pseudornd = ((mapping[id] + 127) * 33331 + 6907) % 13; - return value_to_rgbf(0.0f, 13.0f, float(pseudornd)); + size_t pseudornd = ((mapping[id] + 127) * 33331 + 6907) % 987; + return value_to_rgbf(0.0f, float(987), float(pseudornd)); } #endif }; @@ -345,10 +335,12 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, // -> it prevents extruding perimeter start and short loops into air. const float flow_width = get_flow_width(layer_region, entity->role()); const float max_allowed_dist_from_prev_layer = flow_width; + float distance_from_last_support_point = params.min_distance_between_support_points * 2.0f; for (size_t line_idx = 0; line_idx < lines.size(); ++line_idx) { ExtrusionLine ¤t_line = lines[line_idx]; Point current = Point::new_scale(current_line.b); + distance_from_last_support_point += current_line.len; float mm3_per_mm = float(entity->min_mm3_per_mm()); float curr_angle = 0; @@ -381,13 +373,15 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, StabilityAccumulator ¤t_segment = stability_accs.access(current_stability_acc); current_line.stability_accumulator_id = current_stability_acc; current_segment.add_extrusion(current_line, print_z, mm3_per_mm); - if (bridging_acc.distance // if unsupported distance is larger than bridge distance linearly decreased by curvature, enforce supports. - > params.bridge_distance - / (1.0f + (bridging_acc.max_curvature - * params.bridge_distance_decrease_by_curvature_factor / PI))) { - current_segment.add_support_point(current, params.support_points_interface_area); + if (distance_from_last_support_point > params.min_distance_between_support_points && + bridging_acc.distance // if unsupported distance is larger than bridge distance linearly decreased by curvature, enforce supports. + > params.bridge_distance + / (1.0f + (bridging_acc.max_curvature + * params.bridge_distance_decrease_by_curvature_factor / PI))) { + current_segment.add_support_point(current, params.extrusion_support_points_area); issues.supports_nedded.emplace_back(to_vec3f(current), 1.0); bridging_acc.reset(); + distance_from_last_support_point = 0.0f; } } } @@ -430,10 +424,13 @@ void check_layer_global_stability(StabilityAccumulators &stability_accs, float sticking_force = acc->get_base_area() * (acc->get_base_height() == 0 ? params.base_adhesion : params.support_adhesion); - float weight = acc->get_accumulated_volume() * params.filament_density * params.gravity_constant; + float mass = acc->get_accumulated_volume() * params.filament_density; + float weight = mass * params.gravity_constant; + float distance_from_last_support_point = params.min_distance_between_support_points * 2.0f; for (size_t line_idx : acc_lines.second) { const ExtrusionLine &line = checked_lines[line_idx]; + distance_from_last_support_point += line.len; Vec3f extruder_pressure_direction = to_3d(Vec2f(line.b - line.a), 0.0f).normalized(); Vec2f pivot_site_search = line.b + extruder_pressure_direction.head<2>() * 1000.0f; @@ -457,7 +454,7 @@ void check_layer_global_stability(StabilityAccumulators &stability_accs, std::cout << "weight_torque: " << weight_torque << std::endl; float bed_movement_arm = centroid.z() - acc->get_base_height(); - float bed_movement_force = params.max_acceleration * weight; + float bed_movement_force = params.max_acceleration * mass; float bed_movement_torque = bed_movement_force * bed_movement_arm; std::cout << "bed_movement_arm: " << bed_movement_arm << std::endl; @@ -475,8 +472,9 @@ void check_layer_global_stability(StabilityAccumulators &stability_accs, std::cout << "total_torque: " << total_torque << " printz: " << print_z << std::endl; if (total_torque > 0) { - acc->add_support_point(Point::new_scale(line.b), params.support_points_interface_area); + acc->add_support_point(Point::new_scale(line.b), std::min(params.support_points_interface_area, (pivot - line.b).squaredNorm())); issues.supports_nedded.emplace_back(to_3d(line.b, print_z), extruder_conflict_torque - sticking_torque); + distance_from_last_support_point = 0.0f; } } @@ -485,8 +483,7 @@ void check_layer_global_stability(StabilityAccumulators &stability_accs, Issues check_object_stability(const PrintObject *po, const Params ¶ms) { #ifdef DEBUG_FILES - FILE *eacc = boost::nowide::fopen(debug_out_path("emerging_accumulators.obj").c_str(), "w"); - FILE *facc = boost::nowide::fopen(debug_out_path("final_accumulators.obj").c_str(), "w"); + FILE *debug_acc = boost::nowide::fopen(debug_out_path("accumulators.obj").c_str(), "w"); #endif StabilityAccumulators stability_accs; LayerLinesDistancer prev_layer_lines { { } }; @@ -548,15 +545,11 @@ Issues check_object_stability(const PrintObject *po, const Params ¶ms) { #ifdef DEBUG_FILES for (const auto &line : checked_lines) { - Vec3f ecolor = stability_accs.get_emerging_color(line.stability_accumulator_id); - fprintf(eacc, "v %f %f %f %f %f %f\n", line.b[0], - line.b[1], base_print_z, ecolor[0], ecolor[1], ecolor[2]); - - Vec3f fcolor = stability_accs.get_final_color(line.stability_accumulator_id); - fprintf(facc, "v %f %f %f %f %f %f\n", line.b[0], - line.b[1], base_print_z, fcolor[0], fcolor[1], fcolor[2]); + Vec3f color = stability_accs.get_accumulator_color(line.stability_accumulator_id); + fprintf(debug_acc, "v %f %f %f %f %f %f\n", line.b[0], + line.b[1], base_print_z, color[0], color[1], color[2]); } -#endif DEBUG_FILES +#endif //MERGE BASE LAYER STABILITY ACCS prev_layer_lines = LayerLinesDistancer { std::move(checked_lines) }; @@ -564,7 +557,7 @@ Issues check_object_stability(const PrintObject *po, const Params ¶ms) { size_t nearest_line_idx; Vec2f nearest_pt; float dist = prev_layer_lines.signed_distance_from_lines(l.a, nearest_line_idx, nearest_pt); - if (std::abs(dist) < max_flow_width) { + if (std::abs(dist) < max_flow_width*1.1f) { size_t other_line_acc_id = prev_layer_lines.get_line(nearest_line_idx).stability_accumulator_id; size_t from_id = std::max(other_line_acc_id, l.stability_accumulator_id); size_t to_id = std::min(other_line_acc_id, l.stability_accumulator_id); @@ -646,20 +639,15 @@ Issues check_object_stability(const PrintObject *po, const Params ¶ms) { #ifdef DEBUG_FILES for (const auto &line : prev_layer_lines.get_lines()) { - Vec3f ecolor = stability_accs.get_emerging_color(line.stability_accumulator_id); - fprintf(eacc, "v %f %f %f %f %f %f\n", line.b[0], - line.b[1], print_z, ecolor[0], ecolor[1], ecolor[2]); - - Vec3f fcolor = stability_accs.get_final_color(line.stability_accumulator_id); - fprintf(facc, "v %f %f %f %f %f %f\n", line.b[0], - line.b[1], print_z, fcolor[0], fcolor[1], fcolor[2]); + Vec3f color = stability_accs.get_accumulator_color(line.stability_accumulator_id); + fprintf(debug_acc, "v %f %f %f %f %f %f\n", line.b[0], + line.b[1], print_z, color[0], color[1], color[2]); } #endif } #ifdef DEBUG_FILES - fclose(eacc); - fclose(facc); + fclose(debug_acc); #endif std::cout << " SUPP: " << issues.supports_nedded.size() << std::endl; diff --git a/src/libslic3r/SupportableIssuesSearch.hpp b/src/libslic3r/SupportableIssuesSearch.hpp index e8a9f0e0cd..ccda2d77af 100644 --- a/src/libslic3r/SupportableIssuesSearch.hpp +++ b/src/libslic3r/SupportableIssuesSearch.hpp @@ -10,14 +10,18 @@ namespace SupportableIssues { struct Params { const float gravity_constant = 9806.65f; // mm/s^2; gravity acceleration on Earth's surface, algorithm assumes that printer is in upwards position. - float bridge_distance = 10.0f; //mm float bridge_distance_decrease_by_curvature_factor = 5.0f; // allowed bridge distance = bridge_distance / ( 1 + this factor * (curvature / PI) ) + float min_distance_between_support_points = 0.5f; + // Adhesion computation : from experiment, PLA holds about 3g per mm^2 of base area (with reserve); So it can withstand about 3*gravity_constant force per mm^2 float base_adhesion = 3.0f * gravity_constant; // adhesion per mm^2 of first layer - float support_adhesion = 1.0f * gravity_constant; // adhesion per mm^2 of support interface layer - float support_points_interface_area = 2.0f; // mm^2 + float support_adhesion = 3.0f * gravity_constant; // adhesion per mm^2 of support interface layer + float support_points_interface_area = 5.0f; // mm^2 + float extrusion_support_points_area = 0.5f; // much lower value, because these support points appear due to unsupported extrusion, + // not stability - they can be very densely placed, making the sticking estimation incorrect + float max_acceleration = 1000.0f; // mm/s^2 ; max acceleration of object (bed) in XY float filament_density = 1.25f * 0.001f; // g/mm^3 ; Common filaments are very lightweight, so precise number is not that important diff --git a/src/libslic3r/TriangleSelectorWrapper.cpp b/src/libslic3r/TriangleSelectorWrapper.cpp index 02a3b8a1ab..ec716ebca3 100644 --- a/src/libslic3r/TriangleSelectorWrapper.cpp +++ b/src/libslic3r/TriangleSelectorWrapper.cpp @@ -10,22 +10,26 @@ TriangleSelectorWrapper::TriangleSelectorWrapper(const TriangleMesh &mesh) : } -void TriangleSelectorWrapper::enforce_spot(const Vec3f &point, float radius) { - size_t hit_face_index; - Vec3f hit_point; - auto dist = AABBTreeIndirect::squared_distance_to_indexed_triangle_set(mesh.its.vertices, mesh.its.indices, - triangles_tree, - point, hit_face_index, hit_point); - if (dist < 0 || dist > radius * radius) { - return; +void TriangleSelectorWrapper::enforce_spot(const Vec3f &point, const Vec3f &origin, float radius) { + std::vector hits; + Vec3f dir = (point - origin).normalized(); + if (AABBTreeIndirect::intersect_ray_all_hits(mesh.its.vertices, mesh.its.indices, triangles_tree, + Vec3d(origin.cast()), + Vec3d(dir.cast()), + hits)) { + for (int hit_idx = hits.size() - 1; hit_idx >= 0; --hit_idx) { + const igl::Hit &hit = hits[hit_idx]; + Vec3f pos = origin + dir * hit.t; + Vec3f face_normal = its_face_normal(mesh.its, hit.id); + if (point.z() + radius > pos.z() && face_normal.dot(dir) < 0) { + std::unique_ptr cursor = std::make_unique( + pos, origin, radius, Transform3d::Identity(), TriangleSelector::ClippingPlane { }); + selector.select_patch(hit.id, std::move(cursor), EnforcerBlockerType::ENFORCER, Transform3d::Identity(), + true, 0.0f); + break; + } + } } - - std::unique_ptr cursor = std::make_unique(hit_point, point, - radius, Transform3d::Identity(), TriangleSelector::ClippingPlane { }); - - selector.select_patch(hit_face_index, std::move(cursor), EnforcerBlockerType::ENFORCER, Transform3d::Identity(), - true, - 0.0f); } } diff --git a/src/libslic3r/TriangleSelectorWrapper.hpp b/src/libslic3r/TriangleSelectorWrapper.hpp index f3b56205fa..10707cc257 100644 --- a/src/libslic3r/TriangleSelectorWrapper.hpp +++ b/src/libslic3r/TriangleSelectorWrapper.hpp @@ -20,7 +20,7 @@ public: TriangleSelectorWrapper(const TriangleMesh &mesh); - void enforce_spot(const Vec3f &point, float radius); + void enforce_spot(const Vec3f &point, const Vec3f& origin, float radius); }; From eaffb1492154edb31b99de591098f91e03628344 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Wed, 22 Jun 2022 11:37:52 +0200 Subject: [PATCH 29/78] Improved stability supports - now accounts for base convex hull, decreases area of points if too close. --- src/libslic3r/CMakeLists.txt | 4 +- src/libslic3r/Print.cpp | 2 +- src/libslic3r/Print.hpp | 4 +- src/libslic3r/PrintObject.cpp | 20 ++--- ...esSearch.cpp => SupportSpotsGenerator.cpp} | 89 +++++++++++-------- ...esSearch.hpp => SupportSpotsGenerator.hpp} | 13 ++- 6 files changed, 73 insertions(+), 59 deletions(-) rename src/libslic3r/{SupportableIssuesSearch.cpp => SupportSpotsGenerator.cpp} (90%) rename src/libslic3r/{SupportableIssuesSearch.hpp => SupportSpotsGenerator.hpp} (80%) diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 0a0062a754..e478188356 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -245,8 +245,8 @@ set(SLIC3R_SOURCES SlicingAdaptive.hpp Subdivide.cpp Subdivide.hpp - SupportableIssuesSearch.cpp - SupportableIssuesSearch.hpp + SupportSpotsGenerator.cpp + SupportSpotsGenerator.hpp SupportMaterial.cpp SupportMaterial.hpp Surface.cpp diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index a50974cafd..2979b3557b 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -826,7 +826,7 @@ void Print::process() for (PrintObject *obj : m_objects) obj->ironing(); for (PrintObject *obj : m_objects) - obj->find_supportable_issues(); + obj->generate_support_spots(); for (PrintObject *obj : m_objects) obj->generate_support_material(); if (this->set_started(psWipeTower)) { diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index c89b463a8f..c2777083d8 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -61,7 +61,7 @@ enum PrintStep : unsigned int { enum PrintObjectStep : unsigned int { posSlice, posPerimeters, posPrepareInfill, - posInfill, posIroning, posSupportableIssuesSearch, posSupportMaterial, posCount, + posInfill, posIroning, posSupportSpotsSearch, posSupportMaterial, posCount, }; // A PrintRegion object represents a group of volumes to print @@ -381,7 +381,7 @@ private: void prepare_infill(); void infill(); void ironing(); - void find_supportable_issues(); + void generate_support_spots(); void generate_support_material(); void slice_volumes(); diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 40f740b91c..322b6844ad 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -16,7 +16,7 @@ #include "Fill/FillAdaptive.hpp" #include "Fill/FillLightning.hpp" #include "Format/STL.hpp" -#include "SupportableIssuesSearch.hpp" +#include "SupportSpotsGenerator.hpp" #include "TriangleSelectorWrapper.hpp" #include "format.hpp" @@ -399,15 +399,15 @@ void PrintObject::ironing() } -void PrintObject::find_supportable_issues() +void PrintObject::generate_support_spots() { - if (this->set_started(posSupportableIssuesSearch)) { + if (this->set_started(posSupportSpotsSearch)) { BOOST_LOG_TRIVIAL(debug) - << "Searching supportable issues - start"; - m_print->set_status(75, L("Searching supportable issues")); + << "Searching support spots - start"; + m_print->set_status(75, L("Searching support spots")); if (!this->m_config.support_material) { - std::vector problematic_layers = SupportableIssues::quick_search(this); + std::vector problematic_layers = SupportSpotsGenerator::quick_search(this); if (!problematic_layers.empty()) { std::cout << "Object needs supports" << std::endl; this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, @@ -420,7 +420,7 @@ void PrintObject::find_supportable_issues() } } } else { - SupportableIssues::Issues issues = SupportableIssues::full_search(this); + SupportSpotsGenerator::Issues issues = SupportSpotsGenerator::full_search(this); //TODO fix // if (!issues.supports_nedded.empty()) { auto obj_transform = this->trafo_centered(); @@ -430,7 +430,7 @@ void PrintObject::find_supportable_issues() Transform3f inv_transform = (obj_transform * model_transformation).inverse().cast(); TriangleSelectorWrapper selector { model_volume->mesh() }; - for (const SupportableIssues::SupportPoint &support_point : issues.supports_nedded) { + for (const SupportSpotsGenerator::SupportPoint &support_point : issues.supports_nedded) { Vec3f point = Vec3f(inv_transform * support_point.position); Vec3f origin = Vec3f( inv_transform * Vec3f(support_point.position.x(), support_point.position.y(), 0.0f)); @@ -451,8 +451,8 @@ void PrintObject::find_supportable_issues() m_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) - << "Searching supportable issues - end"; - this->set_done(posSupportableIssuesSearch); + << "Searching support spots - end"; + this->set_done(posSupportSpotsSearch); } } diff --git a/src/libslic3r/SupportableIssuesSearch.cpp b/src/libslic3r/SupportSpotsGenerator.cpp similarity index 90% rename from src/libslic3r/SupportableIssuesSearch.cpp rename to src/libslic3r/SupportSpotsGenerator.cpp index ce17020ed7..1726b4cacb 100644 --- a/src/libslic3r/SupportableIssuesSearch.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -1,4 +1,4 @@ -#include "SupportableIssuesSearch.hpp" +#include "SupportSpotsGenerator.hpp" #include "tbb/parallel_for.h" #include "tbb/blocked_range.h" @@ -55,7 +55,7 @@ auto get_b(ExtrusionLine &&l) { return l.b; } -namespace SupportableIssues { +namespace SupportSpotsGenerator { void Issues::add(const Issues &layer_issues) { supports_nedded.insert(supports_nedded.end(), layer_issues.supports_nedded.begin(), @@ -237,7 +237,8 @@ public: #ifdef DEBUG_FILES Vec3f get_accumulator_color(size_t id) { if (mapping.find(id) == mapping.end()) { - std::cerr << " ERROR: uknown accumulator ID: " << id << std::endl; + BOOST_LOG_TRIVIAL(debug) + << "SSG: ERROR: uknown accumulator ID: " << id; return Vec3f(1.0f, 1.0f, 1.0f); } @@ -332,7 +333,7 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, size_t current_stability_acc = NULL_ACC_ID; ExtrusionPropertiesAccumulator bridging_acc { }; bridging_acc.add_distance(params.bridge_distance + 1.0f); // Initialise unsupported distance with larger than tolerable distance -> - // -> it prevents extruding perimeter start and short loops into air. + // -> it prevents extruding perimeter starts and short loops into air. const float flow_width = get_flow_width(layer_region, entity->role()); const float max_allowed_dist_from_prev_layer = flow_width; float distance_from_last_support_point = params.min_distance_between_support_points * 2.0f; @@ -376,9 +377,9 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, if (distance_from_last_support_point > params.min_distance_between_support_points && bridging_acc.distance // if unsupported distance is larger than bridge distance linearly decreased by curvature, enforce supports. > params.bridge_distance - / (1.0f + (bridging_acc.max_curvature - * params.bridge_distance_decrease_by_curvature_factor / PI))) { - current_segment.add_support_point(current, params.extrusion_support_points_area); + / (bridging_acc.max_curvature + * params.bridge_distance_decrease_by_curvature_factor / PI)) { + current_segment.add_support_point(current, 0.0f); // Do not count extrusion supports into the support area. They can be very densely placed, causing algorithm to overestimate stickiness. issues.supports_nedded.emplace_back(to_vec3f(current), 1.0); bridging_acc.reset(); distance_from_last_support_point = 0.0f; @@ -394,13 +395,13 @@ void check_layer_global_stability(StabilityAccumulators &stability_accs, const std::vector &checked_lines, float print_z, const Params ¶ms) { - std::unordered_map> layer_accs_lines; + std::unordered_map> layer_accs_w_lines; for (size_t i = 0; i < checked_lines.size(); ++i) { - layer_accs_lines[&stability_accs.access(checked_lines[i].stability_accumulator_id)].push_back(i); + layer_accs_w_lines[&stability_accs.access(checked_lines[i].stability_accumulator_id)].push_back(i); } - for (auto &acc_lines : layer_accs_lines) { - StabilityAccumulator *acc = acc_lines.first; + for (auto &accumulator : layer_accs_w_lines) { + StabilityAccumulator *acc = accumulator.first; Vec3f centroid = acc->get_centroid(); Vec2f hull_centroid = unscaled(acc->segment_base_hull().centroid()).cast(); std::vector hull_lines; @@ -411,9 +412,9 @@ void check_layer_global_stability(StabilityAccumulators &stability_accs, } if (hull_lines.empty()) { if (acc->get_support_points().empty()) { - acc->add_support_point(Point::new_scale(checked_lines[acc_lines.second[0]].a), - params.support_points_interface_area); - issues.supports_nedded.emplace_back(to_3d(checked_lines[acc_lines.second[0]].a, print_z), 1.0); + acc->add_support_point(Point::new_scale(checked_lines[accumulator.second[0]].a), + params.support_points_interface_radius*params.support_points_interface_radius* float(PI) ); + issues.supports_nedded.emplace_back(to_3d(checked_lines[accumulator.second[0]].a, print_z), 1.0); } hull_lines.push_back( { unscaled(acc->get_support_points()[0]).cast(), unscaled(acc->get_support_points()[0]).cast() }); @@ -428,7 +429,7 @@ void check_layer_global_stability(StabilityAccumulators &stability_accs, float weight = mass * params.gravity_constant; float distance_from_last_support_point = params.min_distance_between_support_points * 2.0f; - for (size_t line_idx : acc_lines.second) { + for (size_t line_idx : accumulator.second) { const ExtrusionLine &line = checked_lines[line_idx]; distance_from_last_support_point += line.len; @@ -444,39 +445,51 @@ void check_layer_global_stability(StabilityAccumulators &stability_accs, float sticking_arm = (pivot - hull_centroid).norm(); float sticking_torque = sticking_arm * sticking_force; - std::cout << "sticking_arm: " << sticking_arm << std::endl; - std::cout << "sticking_torque: " << sticking_torque << std::endl; - float weight_arm = (pivot - centroid.head<2>()).norm(); float weight_torque = weight_arm * weight; - std::cout << "weight_arm: " << sticking_arm << std::endl; - std::cout << "weight_torque: " << weight_torque << std::endl; - float bed_movement_arm = centroid.z() - acc->get_base_height(); float bed_movement_force = params.max_acceleration * mass; float bed_movement_torque = bed_movement_force * bed_movement_arm; - std::cout << "bed_movement_arm: " << bed_movement_arm << std::endl; - std::cout << "bed_movement_torque: " << bed_movement_torque << std::endl; - float conflict_torque_arm = (to_3d(Vec2f(pivot - line.b), print_z).cross( extruder_pressure_direction)).norm(); float extruder_conflict_torque = params.tolerable_extruder_conflict_force * conflict_torque_arm; - - std::cout << "conflict_torque_arm: " << conflict_torque_arm << std::endl; - std::cout << "extruder_conflict_torque: " << extruder_conflict_torque << std::endl; - float total_torque = bed_movement_torque + extruder_conflict_torque - weight_torque - sticking_torque; - std::cout << "total_torque: " << total_torque << " printz: " << print_z << std::endl; - if (total_torque > 0) { - acc->add_support_point(Point::new_scale(line.b), std::min(params.support_points_interface_area, (pivot - line.b).squaredNorm())); + size_t _nearest_idx; + Vec2f _nearest_pt; + float area = params.support_points_interface_radius* params.support_points_interface_radius * float(PI); + float dist_from_hull = hull_distancer.signed_distance_from_lines(line.b, _nearest_idx, _nearest_pt); + if (dist_from_hull < params.support_points_interface_radius) { + area = std::max(0.0f, dist_from_hull*params.support_points_interface_radius * float(PI)); + } + + acc->add_support_point(Point::new_scale(line.b), area); issues.supports_nedded.emplace_back(to_3d(line.b, print_z), extruder_conflict_torque - sticking_torque); distance_from_last_support_point = 0.0f; } - +#if 0 + BOOST_LOG_TRIVIAL(debug) + << "SSG: sticking_arm: " << sticking_arm; + BOOST_LOG_TRIVIAL(debug) + << "SSG: sticking_torque: " << sticking_torque; + BOOST_LOG_TRIVIAL(debug) + << "SSG: weight_arm: " << sticking_arm; + BOOST_LOG_TRIVIAL(debug) + << "SSG: weight_torque: " << weight_torque; + BOOST_LOG_TRIVIAL(debug) + << "SSG: bed_movement_arm: " << bed_movement_arm; + BOOST_LOG_TRIVIAL(debug) + << "SSG: bed_movement_torque: " << bed_movement_torque; + BOOST_LOG_TRIVIAL(debug) + << "SSG: conflict_torque_arm: " << conflict_torque_arm; + BOOST_LOG_TRIVIAL(debug) + << "SSG: extruder_conflict_torque: " << extruder_conflict_torque; + BOOST_LOG_TRIVIAL(debug) + << "SSG: total_torque: " << total_torque << " printz: " << print_z; +#endif } } } @@ -557,7 +570,7 @@ Issues check_object_stability(const PrintObject *po, const Params ¶ms) { size_t nearest_line_idx; Vec2f nearest_pt; float dist = prev_layer_lines.signed_distance_from_lines(l.a, nearest_line_idx, nearest_pt); - if (std::abs(dist) < max_flow_width*1.1f) { + if (std::abs(dist) < max_flow_width) { size_t other_line_acc_id = prev_layer_lines.get_line(nearest_line_idx).stability_accumulator_id; size_t from_id = std::max(other_line_acc_id, l.stability_accumulator_id); size_t to_id = std::min(other_line_acc_id, l.stability_accumulator_id); @@ -610,8 +623,8 @@ Issues check_object_stability(const PrintObject *po, const Params ¶ms) { } fill_points.emplace_back(start, acc_id); } else { - std::cout << " SUPPORTS POINT GEN, start infill in the air? on printz: " << print_z - << std::endl; + BOOST_LOG_TRIVIAL(debug) + << "SSG: ERROR: seem that infill starts in the air? on printz: " << print_z; } } } // fill @@ -630,8 +643,8 @@ Issues check_object_stability(const PrintObject *po, const Params ¶ms) { size_t to_id = std::min(other_line_acc_id, fill_point.second); stability_accs.merge_accumulators(from_id, to_id); } else { - std::cout << " SUPPORTS POINT GEN, no connection on current layer for infill? on printz: " << print_z - << std::endl; + BOOST_LOG_TRIVIAL(debug) + << "SSG: ERROR: seem that infill starts in the air? on printz: " << print_z; } } @@ -698,7 +711,9 @@ std::vector quick_search(const PrintObject *po, const Params ¶ms) { Issues full_search(const PrintObject *po, const Params ¶ms) { auto issues = check_object_stability(po, params); +#ifdef DEBUG_FILES debug_export(issues, "issues"); +#endif return issues; } diff --git a/src/libslic3r/SupportableIssuesSearch.hpp b/src/libslic3r/SupportSpotsGenerator.hpp similarity index 80% rename from src/libslic3r/SupportableIssuesSearch.hpp rename to src/libslic3r/SupportSpotsGenerator.hpp index ccda2d77af..b7c987c928 100644 --- a/src/libslic3r/SupportableIssuesSearch.hpp +++ b/src/libslic3r/SupportSpotsGenerator.hpp @@ -5,22 +5,21 @@ namespace Slic3r { -namespace SupportableIssues { +namespace SupportSpotsGenerator { struct Params { const float gravity_constant = 9806.65f; // mm/s^2; gravity acceleration on Earth's surface, algorithm assumes that printer is in upwards position. - float bridge_distance = 10.0f; //mm - float bridge_distance_decrease_by_curvature_factor = 5.0f; // allowed bridge distance = bridge_distance / ( 1 + this factor * (curvature / PI) ) + float bridge_distance = 15.0f; //mm + float bridge_distance_decrease_by_curvature_factor = 5.0f; // >0 REQUIRED; allowed bridge distance = bridge_distance / (this factor * (curvature / PI) ) float min_distance_between_support_points = 0.5f; // Adhesion computation : from experiment, PLA holds about 3g per mm^2 of base area (with reserve); So it can withstand about 3*gravity_constant force per mm^2 float base_adhesion = 3.0f * gravity_constant; // adhesion per mm^2 of first layer - float support_adhesion = 3.0f * gravity_constant; // adhesion per mm^2 of support interface layer - float support_points_interface_area = 5.0f; // mm^2 - float extrusion_support_points_area = 0.5f; // much lower value, because these support points appear due to unsupported extrusion, - // not stability - they can be very densely placed, making the sticking estimation incorrect + float support_adhesion = 1.0f * gravity_constant; // adhesion per mm^2 of support interface layer + + float support_points_interface_radius = 0.5f; // mm float max_acceleration = 1000.0f; // mm/s^2 ; max acceleration of object (bed) in XY float filament_density = 1.25f * 0.001f; // g/mm^3 ; Common filaments are very lightweight, so precise number is not that important From d5a584a2c2bce03133395efbc7d1105fbc24a47b Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Wed, 22 Jun 2022 15:27:18 +0200 Subject: [PATCH 30/78] fixed bug with base layers merging to single accumulator --- src/libslic3r/SupportSpotsGenerator.cpp | 106 ++++++++++++++---------- src/libslic3r/SupportSpotsGenerator.hpp | 6 +- 2 files changed, 64 insertions(+), 48 deletions(-) diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index 1726b4cacb..6072b613d4 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -123,26 +123,23 @@ private: Points support_points { }; Vec3f centroid_accumulator = Vec3f::Zero(); float accumulated_volume { }; - float base_area { }; - float base_height { }; + float accumulated_sticking_force { }; public: - explicit StabilityAccumulator(float base_height) : - base_height(base_height) { - } + StabilityAccumulator() = default; - void add_base_extrusion(const ExtrusionLine &line, float width, float print_z, float mm3_per_mm) { - base_area += line.len * width; + void add_base_extrusion(const ExtrusionLine &line, float sticking_force, float print_z, float mm3_per_mm) { + accumulated_sticking_force += sticking_force; support_points.push_back(Point::new_scale(line.a)); support_points.push_back(Point::new_scale(line.b)); base_convex_hull.clear(); add_extrusion(line, print_z, mm3_per_mm); } - void add_support_point(const Point &position, float area) { + void add_support_point(const Point &position, float sticking_force) { support_points.push_back(position); base_convex_hull.clear(); - base_area += area; + accumulated_sticking_force += sticking_force; } void add_extrusion(const ExtrusionLine &line, float print_z, float mm3_per_mm) { @@ -156,12 +153,10 @@ public: return centroid_accumulator / accumulated_volume; } - float get_base_area() const { - return base_area; - } - float get_base_height() const { - return base_height; + float get_sticking_force() const { + return accumulated_sticking_force; } + float get_accumulated_volume() const { return accumulated_volume; } @@ -173,7 +168,7 @@ public: return this->base_convex_hull; } - const Points& get_support_points() { + const Points& get_support_points() const { return support_points; } @@ -183,7 +178,7 @@ public: base_convex_hull.clear(); this->centroid_accumulator += acc.centroid_accumulator; this->accumulated_volume += acc.accumulated_volume; - this->base_area += acc.base_area; + this->accumulated_sticking_force += acc.accumulated_sticking_force; } }; @@ -201,18 +196,18 @@ private: } to_acc.add_from(from_acc); mapping[from_id] = mapping[to_id]; - from_acc = StabilityAccumulator { 0.0f }; + from_acc = StabilityAccumulator { }; } public: StabilityAccumulators() = default; - int create_accumulator(float base_height) { + int create_accumulator() { size_t id = next_id; next_id++; mapping[id] = accumulators.size(); - accumulators.push_back(StabilityAccumulator { base_height }); + accumulators.push_back(StabilityAccumulator { }); return id; } @@ -231,7 +226,7 @@ public: } to_acc.add_from(from_acc); mapping[from_id] = mapping[to_id]; - from_acc = StabilityAccumulator { 0.0f }; + from_acc = StabilityAccumulator { }; } #ifdef DEBUG_FILES @@ -245,6 +240,18 @@ public: size_t pseudornd = ((mapping[id] + 127) * 33331 + 6907) % 987; return value_to_rgbf(0.0f, float(987), float(pseudornd)); } + + void log_accumulators(){ + for (size_t i = 0; i < accumulators.size(); ++i) { + const auto& acc = accumulators[i]; + BOOST_LOG_TRIVIAL(debug) + << "SSG: accumulator POS: " << i << "\n" + << "SSG: get_accumulated_volume: " << acc.get_accumulated_volume() << "\n" + << "SSG: get_sticking_force: " << acc.get_sticking_force() << "\n" + << "SSG: support points count: " << acc.get_support_points().size() << "\n"; + + } + } #endif }; @@ -369,7 +376,7 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, } else { bridging_acc.add_distance(current_line.len); if (current_stability_acc == NULL_ACC_ID) { - current_stability_acc = stability_accs.create_accumulator(print_z); + current_stability_acc = stability_accs.create_accumulator(); } StabilityAccumulator ¤t_segment = stability_accs.access(current_stability_acc); current_line.stability_accumulator_id = current_stability_acc; @@ -379,7 +386,7 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, > params.bridge_distance / (bridging_acc.max_curvature * params.bridge_distance_decrease_by_curvature_factor / PI)) { - current_segment.add_support_point(current, 0.0f); // Do not count extrusion supports into the support area. They can be very densely placed, causing algorithm to overestimate stickiness. + current_segment.add_support_point(current, 0.0f); // Do not count extrusion supports into the sticking force. They can be very densely placed, causing algorithm to overestimate stickiness. issues.supports_nedded.emplace_back(to_vec3f(current), 1.0); bridging_acc.reset(); distance_from_last_support_point = 0.0f; @@ -413,7 +420,8 @@ void check_layer_global_stability(StabilityAccumulators &stability_accs, if (hull_lines.empty()) { if (acc->get_support_points().empty()) { acc->add_support_point(Point::new_scale(checked_lines[accumulator.second[0]].a), - params.support_points_interface_radius*params.support_points_interface_radius* float(PI) ); + params.support_points_interface_radius * params.support_points_interface_radius * float(PI) + * params.support_adhesion); issues.supports_nedded.emplace_back(to_3d(checked_lines[accumulator.second[0]].a, print_z), 1.0); } hull_lines.push_back( { unscaled(acc->get_support_points()[0]).cast(), @@ -423,8 +431,7 @@ void check_layer_global_stability(StabilityAccumulators &stability_accs, LayerLinesDistancer hull_distancer(std::move(hull_lines)); - float sticking_force = acc->get_base_area() - * (acc->get_base_height() == 0 ? params.base_adhesion : params.support_adhesion); + float sticking_force = acc->get_sticking_force(); float mass = acc->get_accumulated_volume() * params.filament_density; float weight = mass * params.gravity_constant; @@ -435,7 +442,7 @@ void check_layer_global_stability(StabilityAccumulators &stability_accs, Vec3f extruder_pressure_direction = to_3d(Vec2f(line.b - line.a), 0.0f).normalized(); Vec2f pivot_site_search = line.b + extruder_pressure_direction.head<2>() * 1000.0f; - extruder_pressure_direction.z() = -0.1f; + extruder_pressure_direction.z() = -0.3f; extruder_pressure_direction.normalize(); size_t nearest_line_idx; @@ -448,7 +455,7 @@ void check_layer_global_stability(StabilityAccumulators &stability_accs, float weight_arm = (pivot - centroid.head<2>()).norm(); float weight_torque = weight_arm * weight; - float bed_movement_arm = centroid.z() - acc->get_base_height(); + float bed_movement_arm = centroid.z(); float bed_movement_force = params.max_acceleration * mass; float bed_movement_torque = bed_movement_force * bed_movement_arm; @@ -460,13 +467,14 @@ void check_layer_global_stability(StabilityAccumulators &stability_accs, if (total_torque > 0) { size_t _nearest_idx; Vec2f _nearest_pt; - float area = params.support_points_interface_radius* params.support_points_interface_radius * float(PI); + float area = params.support_points_interface_radius * params.support_points_interface_radius + * float(PI); float dist_from_hull = hull_distancer.signed_distance_from_lines(line.b, _nearest_idx, _nearest_pt); if (dist_from_hull < params.support_points_interface_radius) { - area = std::max(0.0f, dist_from_hull*params.support_points_interface_radius * float(PI)); + area = std::max(0.0f, dist_from_hull * params.support_points_interface_radius * float(PI)); } - - acc->add_support_point(Point::new_scale(line.b), area); + float sticking_force = area * params.support_adhesion; + acc->add_support_point(Point::new_scale(line.b), sticking_force); issues.supports_nedded.emplace_back(to_3d(line.b, print_z), extruder_conflict_torque - sticking_torque); distance_from_last_support_point = 0.0f; } @@ -513,7 +521,7 @@ Issues check_object_stability(const PrintObject *po, const Params ¶ms) { const float flow_width = get_flow_width(layer_region, perimeter->role()); max_flow_width = std::max(flow_width, max_flow_width); const float mm3_per_mm = float(perimeter->min_mm3_per_mm()); - int id = stability_accs.create_accumulator(base_print_z); + int id = stability_accs.create_accumulator(); StabilityAccumulator &acc = stability_accs.access(id); Points points { }; perimeter->collect_points(points); @@ -522,7 +530,8 @@ Issues check_object_stability(const PrintObject *po, const Params ¶ms) { Vec2f next = unscaled(points[point_idx + 1]).cast(); ExtrusionLine line { start, next }; line.stability_accumulator_id = id; - acc.add_base_extrusion(line, flow_width, base_print_z, mm3_per_mm); + float line_sticking_force = line.len * flow_width * params.base_adhesion; + acc.add_base_extrusion(line, line_sticking_force, base_print_z, mm3_per_mm); checked_lines.push_back(line); } if (perimeter->is_loop()) { @@ -530,7 +539,8 @@ Issues check_object_stability(const PrintObject *po, const Params ¶ms) { Vec2f next = unscaled(points[0]).cast(); ExtrusionLine line { start, next }; line.stability_accumulator_id = id; - acc.add_base_extrusion(line, flow_width, base_print_z, mm3_per_mm); + float line_sticking_force = line.len * flow_width * params.base_adhesion; + acc.add_base_extrusion(line, line_sticking_force, base_print_z, mm3_per_mm); checked_lines.push_back(line); } } // perimeter @@ -540,7 +550,7 @@ Issues check_object_stability(const PrintObject *po, const Params ¶ms) { const float flow_width = get_flow_width(layer_region, fill->role()); max_flow_width = std::max(flow_width, max_flow_width); const float mm3_per_mm = float(fill->min_mm3_per_mm()); - int id = stability_accs.create_accumulator(base_print_z); + int id = stability_accs.create_accumulator(); StabilityAccumulator &acc = stability_accs.access(id); Points points { }; fill->collect_points(points); @@ -549,27 +559,22 @@ Issues check_object_stability(const PrintObject *po, const Params ¶ms) { Vec2f next = unscaled(points[point_idx + 1]).cast(); ExtrusionLine line { start, next }; line.stability_accumulator_id = id; - acc.add_base_extrusion(line, flow_width, base_print_z, mm3_per_mm); + float line_sticking_force = line.len * flow_width * params.base_adhesion; + acc.add_base_extrusion(line, line_sticking_force, base_print_z, mm3_per_mm); checked_lines.push_back(line); } } // fill } // ex_entity } // region -#ifdef DEBUG_FILES - for (const auto &line : checked_lines) { - Vec3f color = stability_accs.get_accumulator_color(line.stability_accumulator_id); - fprintf(debug_acc, "v %f %f %f %f %f %f\n", line.b[0], - line.b[1], base_print_z, color[0], color[1], color[2]); - } -#endif - //MERGE BASE LAYER STABILITY ACCS prev_layer_lines = LayerLinesDistancer { std::move(checked_lines) }; for (const ExtrusionLine &l : prev_layer_lines.get_lines()) { size_t nearest_line_idx; Vec2f nearest_pt; - float dist = prev_layer_lines.signed_distance_from_lines(l.a, nearest_line_idx, nearest_pt); + Vec2f line_dir = (l.b - l.a).normalized(); + Vec2f site_search_location = l.a + Vec2f(line_dir.y(), -line_dir.x()) * max_flow_width; + float dist = prev_layer_lines.signed_distance_from_lines(site_search_location, nearest_line_idx, nearest_pt); if (std::abs(dist) < max_flow_width) { size_t other_line_acc_id = prev_layer_lines.get_line(nearest_line_idx).stability_accumulator_id; size_t from_id = std::max(other_line_acc_id, l.stability_accumulator_id); @@ -578,6 +583,16 @@ Issues check_object_stability(const PrintObject *po, const Params ¶ms) { } } +#ifdef DEBUG_FILES + for (const auto &line : prev_layer_lines.get_lines()) { + Vec3f color = stability_accs.get_accumulator_color(line.stability_accumulator_id); + fprintf(debug_acc, "v %f %f %f %f %f %f\n", line.b[0], + line.b[1], base_print_z, color[0], color[1], color[2]); + } + + stability_accs.log_accumulators(); +#endif + //CHECK STABILITY OF ALL LAYERS for (size_t layer_idx = 1; layer_idx < po->layer_count(); ++layer_idx) { const Layer *layer = po->layers()[layer_idx]; @@ -656,6 +671,7 @@ Issues check_object_stability(const PrintObject *po, const Params ¶ms) { fprintf(debug_acc, "v %f %f %f %f %f %f\n", line.b[0], line.b[1], print_z, color[0], color[1], color[2]); } + stability_accs.log_accumulators(); #endif } diff --git a/src/libslic3r/SupportSpotsGenerator.hpp b/src/libslic3r/SupportSpotsGenerator.hpp index b7c987c928..603076b291 100644 --- a/src/libslic3r/SupportSpotsGenerator.hpp +++ b/src/libslic3r/SupportSpotsGenerator.hpp @@ -10,7 +10,7 @@ namespace SupportSpotsGenerator { struct Params { const float gravity_constant = 9806.65f; // mm/s^2; gravity acceleration on Earth's surface, algorithm assumes that printer is in upwards position. - float bridge_distance = 15.0f; //mm + float bridge_distance = 10.0f; //mm float bridge_distance_decrease_by_curvature_factor = 5.0f; // >0 REQUIRED; allowed bridge distance = bridge_distance / (this factor * (curvature / PI) ) float min_distance_between_support_points = 0.5f; @@ -24,8 +24,8 @@ struct Params { float max_acceleration = 1000.0f; // mm/s^2 ; max acceleration of object (bed) in XY float filament_density = 1.25f * 0.001f; // g/mm^3 ; Common filaments are very lightweight, so precise number is not that important - float tolerable_extruder_conflict_force = 50.0f * gravity_constant; // force that can occasionally push the model due to various factors (filament leaks, small curling, ... ); current value corresponds to weight of 50g - float max_curled_conflict_extruder_force = 200.0f * gravity_constant; // for areas with possible high layered curled filaments, max force to account for; current value corresponds to weight of 200g + float tolerable_extruder_conflict_force = 50.0f * gravity_constant; // force that can occasionally push the model due to various factors (filament leaks, small curling, ... ); current value corresponds to weight of X grams + float max_curled_conflict_extruder_force = 300.0f * gravity_constant; // for areas with possible high layered curled filaments, max force to account for; }; struct SupportPoint { From 9294d5e604b81fc636ad08e50c4f8e22274b9c55 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Thu, 23 Jun 2022 10:47:35 +0200 Subject: [PATCH 31/78] improved triangle tracking in triangle selector - if not hit registered, nearest triangle is taken instead --- src/libslic3r/SupportSpotsGenerator.cpp | 3 +-- src/libslic3r/SupportSpotsGenerator.hpp | 4 ++-- src/libslic3r/TriangleSelectorWrapper.cpp | 8 ++++++++ 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index 6072b613d4..d6dff123b2 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -119,7 +119,6 @@ public: class StabilityAccumulator { private: - Polygon base_convex_hull { }; Points support_points { }; Vec3f centroid_accumulator = Vec3f::Zero(); float accumulated_volume { }; @@ -384,7 +383,7 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, if (distance_from_last_support_point > params.min_distance_between_support_points && bridging_acc.distance // if unsupported distance is larger than bridge distance linearly decreased by curvature, enforce supports. > params.bridge_distance - / (bridging_acc.max_curvature + / (1.0f + bridging_acc.max_curvature * params.bridge_distance_decrease_by_curvature_factor / PI)) { current_segment.add_support_point(current, 0.0f); // Do not count extrusion supports into the sticking force. They can be very densely placed, causing algorithm to overestimate stickiness. issues.supports_nedded.emplace_back(to_vec3f(current), 1.0); diff --git a/src/libslic3r/SupportSpotsGenerator.hpp b/src/libslic3r/SupportSpotsGenerator.hpp index 603076b291..0390b32afd 100644 --- a/src/libslic3r/SupportSpotsGenerator.hpp +++ b/src/libslic3r/SupportSpotsGenerator.hpp @@ -11,7 +11,7 @@ struct Params { const float gravity_constant = 9806.65f; // mm/s^2; gravity acceleration on Earth's surface, algorithm assumes that printer is in upwards position. float bridge_distance = 10.0f; //mm - float bridge_distance_decrease_by_curvature_factor = 5.0f; // >0 REQUIRED; allowed bridge distance = bridge_distance / (this factor * (curvature / PI) ) + float bridge_distance_decrease_by_curvature_factor = 5.0f; // allowed bridge distance = bridge_distance / (this factor * (curvature / PI) ) float min_distance_between_support_points = 0.5f; @@ -21,7 +21,7 @@ struct Params { float support_points_interface_radius = 0.5f; // mm - float max_acceleration = 1000.0f; // mm/s^2 ; max acceleration of object (bed) in XY + float max_acceleration = 9*1000.0f; // mm/s^2 ; max acceleration of object (bed) in XY (NOTE: The max hit is received by the object in the jerk phase, so the usual machine limits are too low) float filament_density = 1.25f * 0.001f; // g/mm^3 ; Common filaments are very lightweight, so precise number is not that important float tolerable_extruder_conflict_force = 50.0f * gravity_constant; // force that can occasionally push the model due to various factors (filament leaks, small curling, ... ); current value corresponds to weight of X grams diff --git a/src/libslic3r/TriangleSelectorWrapper.cpp b/src/libslic3r/TriangleSelectorWrapper.cpp index ec716ebca3..c6040c721c 100644 --- a/src/libslic3r/TriangleSelectorWrapper.cpp +++ b/src/libslic3r/TriangleSelectorWrapper.cpp @@ -29,6 +29,14 @@ void TriangleSelectorWrapper::enforce_spot(const Vec3f &point, const Vec3f &orig break; } } + } else { + size_t hit_idx_out; + Vec3f hit_point_out; + AABBTreeIndirect::squared_distance_to_indexed_triangle_set(mesh.its.vertices, mesh.its.indices, triangles_tree, point, hit_idx_out, hit_point_out); + std::unique_ptr cursor = std::make_unique( + point, origin, radius, Transform3d::Identity(), TriangleSelector::ClippingPlane { }); + selector.select_patch(hit_idx_out, std::move(cursor), EnforcerBlockerType::ENFORCER, Transform3d::Identity(), + true, 0.0f); } } From 864c85d47e6099d9e0cd4fd3024f0a2b299e8f4b Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Fri, 24 Jun 2022 12:30:18 +0200 Subject: [PATCH 32/78] replace convex hull computation with KDTree, improve sticking centroid estimation --- src/libslic3r/PrintObject.cpp | 2 +- src/libslic3r/SupportSpotsGenerator.cpp | 131 +++++++++++------------- src/libslic3r/SupportSpotsGenerator.hpp | 6 +- 3 files changed, 65 insertions(+), 74 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 322b6844ad..92c002c8de 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -434,7 +434,7 @@ void PrintObject::generate_support_spots() Vec3f point = Vec3f(inv_transform * support_point.position); Vec3f origin = Vec3f( inv_transform * Vec3f(support_point.position.x(), support_point.position.y(), 0.0f)); - selector.enforce_spot(point, origin, 0.5f); + selector.enforce_spot(point, origin, 1.0f); } model_volume->supported_facets.set(selector.selector); diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index d6dff123b2..174b5bc041 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -9,6 +9,7 @@ #include #include "AABBTreeLines.hpp" +#include "KDTreeIndirect.hpp" #include "libslic3r/Layer.hpp" #include "libslic3r/ClipperUtils.hpp" #include "Geometry/ConvexHull.hpp" @@ -119,9 +120,10 @@ public: class StabilityAccumulator { private: - Points support_points { }; + std::vector support_points { }; Vec3f centroid_accumulator = Vec3f::Zero(); float accumulated_volume { }; + Vec2f sticking_centroid_accumulator = Vec2f::Zero(); float accumulated_sticking_force { }; public: @@ -129,16 +131,16 @@ public: void add_base_extrusion(const ExtrusionLine &line, float sticking_force, float print_z, float mm3_per_mm) { accumulated_sticking_force += sticking_force; - support_points.push_back(Point::new_scale(line.a)); - support_points.push_back(Point::new_scale(line.b)); - base_convex_hull.clear(); + sticking_centroid_accumulator += sticking_force * ((line.a + line.b) / 2.0f); + support_points.push_back(line.a); + support_points.push_back(line.b); add_extrusion(line, print_z, mm3_per_mm); } - void add_support_point(const Point &position, float sticking_force) { + void add_support_point(const Vec2f &position, float sticking_force) { support_points.push_back(position); - base_convex_hull.clear(); accumulated_sticking_force += sticking_force; + sticking_centroid_accumulator += sticking_force * position; } void add_extrusion(const ExtrusionLine &line, float print_z, float mm3_per_mm) { @@ -149,6 +151,9 @@ public: } Vec3f get_centroid() const { + if (accumulated_volume <= 0.0f) { + return Vec3f::Zero(); + } return centroid_accumulator / accumulated_volume; } @@ -160,24 +165,24 @@ public: return accumulated_volume; } - const Polygon& segment_base_hull() { - if (this->base_convex_hull.empty()) { - this->base_convex_hull = Geometry::convex_hull(this->support_points); - } - return this->base_convex_hull; + const std::vector& get_support_points() const { + return support_points; } - const Points& get_support_points() const { - return support_points; + Vec2f get_sticking_centroid() const { + if (accumulated_sticking_force <= 0.0f) { + return Vec2f::Zero(); + } + return sticking_centroid_accumulator / accumulated_sticking_force; } void add_from(const StabilityAccumulator &acc) { this->support_points.insert(this->support_points.end(), acc.support_points.begin(), acc.support_points.end()); - base_convex_hull.clear(); this->centroid_accumulator += acc.centroid_accumulator; this->accumulated_volume += acc.accumulated_volume; this->accumulated_sticking_force += acc.accumulated_sticking_force; + this->sticking_centroid_accumulator += acc.sticking_centroid_accumulator; } }; @@ -240,14 +245,14 @@ public: return value_to_rgbf(0.0f, float(987), float(pseudornd)); } - void log_accumulators(){ - for (size_t i = 0; i < accumulators.size(); ++i) { - const auto& acc = accumulators[i]; + void log_accumulators() { + for (size_t i = 0; i < accumulators.size(); ++i) { + const auto &acc = accumulators[i]; BOOST_LOG_TRIVIAL(debug) << "SSG: accumulator POS: " << i << "\n" - << "SSG: get_accumulated_volume: " << acc.get_accumulated_volume() << "\n" - << "SSG: get_sticking_force: " << acc.get_sticking_force() << "\n" - << "SSG: support points count: " << acc.get_support_points().size() << "\n"; + << "SSG: get_accumulated_volume: " << acc.get_accumulated_volume() << "\n" + << "SSG: get_sticking_force: " << acc.get_sticking_force() << "\n" + << "SSG: support points count: " << acc.get_support_points().size() << "\n"; } } @@ -312,9 +317,8 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, params); } } else { //single extrusion path, with possible varying parameters - const auto to_vec3f = [print_z](const Point &point) { - Vec2f tmp = unscale(point).cast(); - return Vec3f(tmp.x(), tmp.y(), print_z); + const auto to_vec3f = [print_z](const Vec2f &point) { + return Vec3f(point.x(), point.y(), print_z); }; Points points { }; entity->collect_points(points); @@ -342,12 +346,9 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, // -> it prevents extruding perimeter starts and short loops into air. const float flow_width = get_flow_width(layer_region, entity->role()); const float max_allowed_dist_from_prev_layer = flow_width; - float distance_from_last_support_point = params.min_distance_between_support_points * 2.0f; for (size_t line_idx = 0; line_idx < lines.size(); ++line_idx) { ExtrusionLine ¤t_line = lines[line_idx]; - Point current = Point::new_scale(current_line.b); - distance_from_last_support_point += current_line.len; float mm3_per_mm = float(entity->min_mm3_per_mm()); float curr_angle = 0; @@ -380,15 +381,13 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, StabilityAccumulator ¤t_segment = stability_accs.access(current_stability_acc); current_line.stability_accumulator_id = current_stability_acc; current_segment.add_extrusion(current_line, print_z, mm3_per_mm); - if (distance_from_last_support_point > params.min_distance_between_support_points && - bridging_acc.distance // if unsupported distance is larger than bridge distance linearly decreased by curvature, enforce supports. + if (bridging_acc.distance // if unsupported distance is larger than bridge distance linearly decreased by curvature, enforce supports. > params.bridge_distance / (1.0f + bridging_acc.max_curvature * params.bridge_distance_decrease_by_curvature_factor / PI)) { - current_segment.add_support_point(current, 0.0f); // Do not count extrusion supports into the sticking force. They can be very densely placed, causing algorithm to overestimate stickiness. - issues.supports_nedded.emplace_back(to_vec3f(current), 1.0); + current_segment.add_support_point(current_line.b, 0.0f); // Do not count extrusion supports into the sticking force. They can be very densely placed, causing algorithm to overestimate stickiness. + issues.supports_nedded.emplace_back(to_vec3f(current_line.b), 1.0); bridging_acc.reset(); - distance_from_last_support_point = 0.0f; } } } @@ -398,6 +397,7 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, void check_layer_global_stability(StabilityAccumulators &stability_accs, Issues &issues, + float flow_width, const std::vector &checked_lines, float print_z, const Params ¶ms) { @@ -408,53 +408,50 @@ void check_layer_global_stability(StabilityAccumulators &stability_accs, for (auto &accumulator : layer_accs_w_lines) { StabilityAccumulator *acc = accumulator.first; - Vec3f centroid = acc->get_centroid(); - Vec2f hull_centroid = unscaled(acc->segment_base_hull().centroid()).cast(); - std::vector hull_lines; - for (const Line &line : acc->segment_base_hull().lines()) { - Vec2f start = unscaled(line.a).cast(); - Vec2f next = unscaled(line.b).cast(); - hull_lines.push_back( { start, next }); - } - if (hull_lines.empty()) { - if (acc->get_support_points().empty()) { - acc->add_support_point(Point::new_scale(checked_lines[accumulator.second[0]].a), - params.support_points_interface_radius * params.support_points_interface_radius * float(PI) - * params.support_adhesion); - issues.supports_nedded.emplace_back(to_3d(checked_lines[accumulator.second[0]].a, print_z), 1.0); - } - hull_lines.push_back( { unscaled(acc->get_support_points()[0]).cast(), - unscaled(acc->get_support_points()[0]).cast() }); - hull_centroid = unscaled(acc->get_support_points()[0]).cast(); - } - LayerLinesDistancer hull_distancer(std::move(hull_lines)); + if (acc->get_support_points().empty()) { + acc->add_support_point(checked_lines[accumulator.second[0]].a, 0.0f); + issues.supports_nedded.emplace_back(to_3d(checked_lines[accumulator.second[0]].a, print_z), 0.0); + } + const std::vector &support_points = acc->get_support_points(); - float sticking_force = acc->get_sticking_force(); - float mass = acc->get_accumulated_volume() * params.filament_density; - float weight = mass * params.gravity_constant; + auto coord_fn = [&support_points](size_t idx, size_t dim) { + return support_points[idx][dim]; + }; + KDTreeIndirect<2, float, decltype(coord_fn)> tree(coord_fn, support_points.size()); float distance_from_last_support_point = params.min_distance_between_support_points * 2.0f; for (size_t line_idx : accumulator.second) { const ExtrusionLine &line = checked_lines[line_idx]; distance_from_last_support_point += line.len; + if (distance_from_last_support_point < params.min_distance_between_support_points) { + continue; + } + size_t nearest_supp_point_idx = find_closest_point(tree, line.b); + if ((line.b - support_points[nearest_supp_point_idx]).norm() < params.min_distance_between_support_points) { + continue; + } + Vec3f extruder_pressure_direction = to_3d(Vec2f(line.b - line.a), 0.0f).normalized(); Vec2f pivot_site_search = line.b + extruder_pressure_direction.head<2>() * 1000.0f; extruder_pressure_direction.z() = -0.3f; extruder_pressure_direction.normalize(); - size_t nearest_line_idx; - Vec2f pivot; - hull_distancer.signed_distance_from_lines(pivot_site_search, nearest_line_idx, pivot); + size_t pivot_idx = find_closest_point(tree, pivot_site_search); + const Vec2f &pivot = support_points[pivot_idx]; - float sticking_arm = (pivot - hull_centroid).norm(); - float sticking_torque = sticking_arm * sticking_force; + const Vec2f &sticking_centroid = acc->get_sticking_centroid(); + float sticking_arm = (pivot - sticking_centroid).norm(); + float sticking_torque = sticking_arm * acc->get_sticking_force(); - float weight_arm = (pivot - centroid.head<2>()).norm(); + float mass = acc->get_accumulated_volume() * params.filament_density; + const Vec3f &mass_centorid = acc->get_centroid(); + float weight = mass * params.gravity_constant; + float weight_arm = (pivot - mass_centorid.head<2>()).norm(); float weight_torque = weight_arm * weight; - float bed_movement_arm = centroid.z(); + float bed_movement_arm = mass_centorid.z(); float bed_movement_force = params.max_acceleration * mass; float bed_movement_torque = bed_movement_force * bed_movement_arm; @@ -464,20 +461,14 @@ void check_layer_global_stability(StabilityAccumulators &stability_accs, float total_torque = bed_movement_torque + extruder_conflict_torque - weight_torque - sticking_torque; if (total_torque > 0) { - size_t _nearest_idx; - Vec2f _nearest_pt; float area = params.support_points_interface_radius * params.support_points_interface_radius * float(PI); - float dist_from_hull = hull_distancer.signed_distance_from_lines(line.b, _nearest_idx, _nearest_pt); - if (dist_from_hull < params.support_points_interface_radius) { - area = std::max(0.0f, dist_from_hull * params.support_points_interface_radius * float(PI)); - } float sticking_force = area * params.support_adhesion; - acc->add_support_point(Point::new_scale(line.b), sticking_force); + acc->add_support_point(line.b, sticking_force); issues.supports_nedded.emplace_back(to_3d(line.b, print_z), extruder_conflict_torque - sticking_torque); distance_from_last_support_point = 0.0f; } -#if 0 +#if 1 BOOST_LOG_TRIVIAL(debug) << "SSG: sticking_arm: " << sticking_arm; BOOST_LOG_TRIVIAL(debug) @@ -662,7 +653,7 @@ Issues check_object_stability(const PrintObject *po, const Params ¶ms) { } } - check_layer_global_stability(stability_accs, issues, prev_layer_lines.get_lines(), print_z, params); + check_layer_global_stability(stability_accs, issues, max_flow_width, prev_layer_lines.get_lines(), print_z, params); #ifdef DEBUG_FILES for (const auto &line : prev_layer_lines.get_lines()) { diff --git a/src/libslic3r/SupportSpotsGenerator.hpp b/src/libslic3r/SupportSpotsGenerator.hpp index 0390b32afd..52b8b6078e 100644 --- a/src/libslic3r/SupportSpotsGenerator.hpp +++ b/src/libslic3r/SupportSpotsGenerator.hpp @@ -13,17 +13,17 @@ struct Params { float bridge_distance = 10.0f; //mm float bridge_distance_decrease_by_curvature_factor = 5.0f; // allowed bridge distance = bridge_distance / (this factor * (curvature / PI) ) - float min_distance_between_support_points = 0.5f; + float min_distance_between_support_points = 3.0f; // Adhesion computation : from experiment, PLA holds about 3g per mm^2 of base area (with reserve); So it can withstand about 3*gravity_constant force per mm^2 float base_adhesion = 3.0f * gravity_constant; // adhesion per mm^2 of first layer float support_adhesion = 1.0f * gravity_constant; // adhesion per mm^2 of support interface layer - float support_points_interface_radius = 0.5f; // mm + float support_points_interface_radius = 1.0f; // mm float max_acceleration = 9*1000.0f; // mm/s^2 ; max acceleration of object (bed) in XY (NOTE: The max hit is received by the object in the jerk phase, so the usual machine limits are too low) float filament_density = 1.25f * 0.001f; // g/mm^3 ; Common filaments are very lightweight, so precise number is not that important - + float tensile_strength = 33000.0f; // mN/mm^2; 33 MPa is tensile strength of ABS, which has the lowest tensile strength from common materials. float tolerable_extruder_conflict_force = 50.0f * gravity_constant; // force that can occasionally push the model due to various factors (filament leaks, small curling, ... ); current value corresponds to weight of X grams float max_curled_conflict_extruder_force = 300.0f * gravity_constant; // for areas with possible high layered curled filaments, max force to account for; }; From 8e2e4154bd647042eb07ee2f6911f7a041e5d2cc Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Fri, 24 Jun 2022 13:20:03 +0200 Subject: [PATCH 33/78] description of the functions --- src/libslic3r/SupportSpotsGenerator.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index 174b5bc041..165467819d 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -118,6 +118,11 @@ public: } }; +// StabilityAccumulator accumulates extrusions for each connected model part from bed to current printed layer. +// If the originaly disconected parts meet in the layer, their stability accumulators get merged and continue as one. +// (think legs of table, which get connected when the top desk is being printed). +// The class gathers mass, centroid mass, sticking force (bed extrusions, support points) and sticking centroid for the +// connected part. These values are then used to check global part stability. class StabilityAccumulator { private: std::vector support_points { }; @@ -186,6 +191,9 @@ public: } }; +// StabilityAccumulators class is wrapper over the vector of stability accumualtors. It provides a level of indirection +// between accumulator ID and the accumulator instance itself. While each extrusion line has one id, which is asigned +// when algorithm reaches the line's layer, the accumulator this id points to can change due to merging. struct StabilityAccumulators { private: size_t next_id = 0; @@ -280,6 +288,9 @@ float get_flow_width(const LayerRegion *region, ExtrusionRole role) { } } +// Accumulator of current extruion path properties +// It remembers unsuported distance and maximum accumulated curvature over that distance. +// Used to determine local stability issues (too long bridges, extrusion curves into air) struct ExtrusionPropertiesAccumulator { float distance = 0; //accumulated distance float curvature = 0; //accumulated signed ccw angles @@ -301,6 +312,14 @@ struct ExtrusionPropertiesAccumulator { } }; +// check_extrusion_entity_stability checks each extrusion for local issues, appends the extrusion +// into checked lines, and gives it a stability accumulator id. If support is needed it pushes it +// into issues as well. +// Rules for stability accumulator id assigment: + // If there is close extrusion under, use min extrusion id between the id of the previous line, + // and id of line under. Also merge the accumulators of those two ids! + // If there is no close extrusion under, use id of the previous extrusion line. + // If there is no previous line, create new stability accumulator. void check_extrusion_entity_stability(const ExtrusionEntity *entity, StabilityAccumulators &stability_accs, Issues &issues, @@ -395,6 +414,11 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, } } +//check_layer_global_stability checks stability of the accumulators that are still present on the current line +// ( this is determined from the gathered checked_lines vector) +// For each accumulator and each its extrusion, forces and torques (weight, bed movement, extruder pressure, stickness to bed) +// are computed and if stability is not sufficient, support points are added +// accumualtors are filtered by their pointer address, since one accumulator can have multiple IDs due to merging void check_layer_global_stability(StabilityAccumulators &stability_accs, Issues &issues, float flow_width, From 6a971b462dcbe44b7369509fd47296cc5ad66177 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Mon, 27 Jun 2022 16:20:52 +0200 Subject: [PATCH 34/78] estimation of malformed and curled segments, increase extruder conflict power accordingly --- src/libslic3r/SupportSpotsGenerator.cpp | 62 ++++++++++++++++++------- src/libslic3r/SupportSpotsGenerator.hpp | 5 +- 2 files changed, 48 insertions(+), 19 deletions(-) diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index 165467819d..d89f7222a7 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -43,6 +43,7 @@ public: Vec2f b; float len; + float malformation = 0.0f; size_t stability_accumulator_id = NULL_ACC_ID; static const constexpr int Dim = 2; @@ -88,7 +89,7 @@ private: public: explicit LayerLinesDistancer(std::vector &&lines) : lines(lines) { - tree = AABBTreeLines::build_aabb_tree_over_indexed_lines(lines); + tree = AABBTreeLines::build_aabb_tree_over_indexed_lines(this->lines); } // negative sign means inside @@ -361,10 +362,10 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, size_t current_stability_acc = NULL_ACC_ID; ExtrusionPropertiesAccumulator bridging_acc { }; + ExtrusionPropertiesAccumulator malformation_acc { }; bridging_acc.add_distance(params.bridge_distance + 1.0f); // Initialise unsupported distance with larger than tolerable distance -> // -> it prevents extruding perimeter starts and short loops into air. const float flow_width = get_flow_width(layer_region, entity->role()); - const float max_allowed_dist_from_prev_layer = flow_width; for (size_t line_idx = 0; line_idx < lines.size(); ++line_idx) { ExtrusionLine ¤t_line = lines[line_idx]; @@ -377,12 +378,14 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, curr_angle = angle(v1, v2); } bridging_acc.add_angle(curr_angle); + malformation_acc.add_angle(curr_angle); size_t nearest_line_idx; Vec2f nearest_point; float dist_from_prev_layer = prev_layer_lines.signed_distance_from_lines(current_line.b, nearest_line_idx, nearest_point); - if (dist_from_prev_layer < max_allowed_dist_from_prev_layer) { + + if (dist_from_prev_layer < flow_width) { const ExtrusionLine &nearest_line = prev_layer_lines.get_line(nearest_line_idx); size_t acc_id = nearest_line.stability_accumulator_id; stability_accs.merge_accumulators(std::max(acc_id, current_stability_acc), @@ -409,6 +412,17 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, bridging_acc.reset(); } } + + //malformation + if (fabs(dist_from_prev_layer) < flow_width*2.0f) { + const ExtrusionLine &nearest_line = prev_layer_lines.get_line(nearest_line_idx); + current_line.malformation += 0.7 * nearest_line.malformation; + } + if (dist_from_prev_layer > flow_width * 0.3) { + current_line.malformation += 0.6 + 0.4 * malformation_acc.max_curvature / PI; + } else { + malformation_acc.reset(); + } } checked_lines.insert(checked_lines.end(), lines.begin(), lines.end()); } @@ -425,17 +439,19 @@ void check_layer_global_stability(StabilityAccumulators &stability_accs, const std::vector &checked_lines, float print_z, const Params ¶ms) { - std::unordered_map> layer_accs_w_lines; + std::unordered_map> layer_accs_w_lines; for (size_t i = 0; i < checked_lines.size(); ++i) { - layer_accs_w_lines[&stability_accs.access(checked_lines[i].stability_accumulator_id)].push_back(i); + layer_accs_w_lines[&stability_accs.access(checked_lines[i].stability_accumulator_id)].push_back(checked_lines[i]); } for (auto &accumulator : layer_accs_w_lines) { StabilityAccumulator *acc = accumulator.first; + LayerLinesDistancer acc_lines(std::move(accumulator.second)); if (acc->get_support_points().empty()) { - acc->add_support_point(checked_lines[accumulator.second[0]].a, 0.0f); - issues.supports_nedded.emplace_back(to_3d(checked_lines[accumulator.second[0]].a, print_z), 0.0); + // acc_lines cannot be empty - if the accumulator has no extrusion in the current layer, it is not considered in stability computation + acc->add_support_point(acc_lines.get_line(0).a, 0.0f); + issues.supports_nedded.emplace_back(to_3d(acc_lines.get_line(0).a, print_z), 0.0); } const std::vector &support_points = acc->get_support_points(); @@ -445,8 +461,7 @@ void check_layer_global_stability(StabilityAccumulators &stability_accs, KDTreeIndirect<2, float, decltype(coord_fn)> tree(coord_fn, support_points.size()); float distance_from_last_support_point = params.min_distance_between_support_points * 2.0f; - for (size_t line_idx : accumulator.second) { - const ExtrusionLine &line = checked_lines[line_idx]; + for (const ExtrusionLine& line : acc_lines.get_lines()) { distance_from_last_support_point += line.len; if (distance_from_last_support_point < params.min_distance_between_support_points) { @@ -457,12 +472,9 @@ void check_layer_global_stability(StabilityAccumulators &stability_accs, continue; } - Vec3f extruder_pressure_direction = to_3d(Vec2f(line.b - line.a), 0.0f).normalized(); - Vec2f pivot_site_search = line.b + extruder_pressure_direction.head<2>() * 1000.0f; - extruder_pressure_direction.z() = -0.3f; - extruder_pressure_direction.normalize(); - - size_t pivot_idx = find_closest_point(tree, pivot_site_search); + Vec2f line_dir = (line.b - line.a).normalized(); + Vec2f pivot_site_search_point = line.b + line_dir * 300.0f; + size_t pivot_idx = find_closest_point(tree, pivot_site_search_point); const Vec2f &pivot = support_points[pivot_idx]; const Vec2f &sticking_centroid = acc->get_sticking_centroid(); @@ -479,16 +491,25 @@ void check_layer_global_stability(StabilityAccumulators &stability_accs, float bed_movement_force = params.max_acceleration * mass; float bed_movement_torque = bed_movement_force * bed_movement_arm; + Vec3f extruder_pressure_direction = to_3d(line_dir, 0.0f); + extruder_pressure_direction.z() = -0.2 - line.malformation * 0.5; + extruder_pressure_direction.normalize(); float conflict_torque_arm = (to_3d(Vec2f(pivot - line.b), print_z).cross( extruder_pressure_direction)).norm(); - float extruder_conflict_torque = params.tolerable_extruder_conflict_force * conflict_torque_arm; + float extruder_conflict_force = params.tolerable_extruder_conflict_force + + line.malformation * params.malformations_additive_conflict_extruder_force; + float extruder_conflict_torque = extruder_conflict_force * conflict_torque_arm; + float total_torque = bed_movement_torque + extruder_conflict_torque - weight_torque - sticking_torque; if (total_torque > 0) { + Vec2f target_point; + size_t _idx; + acc_lines.signed_distance_from_lines(pivot_site_search_point, _idx, target_point); float area = params.support_points_interface_radius * params.support_points_interface_radius * float(PI); float sticking_force = area * params.support_adhesion; - acc->add_support_point(line.b, sticking_force); + acc->add_support_point(target_point, sticking_force); issues.supports_nedded.emplace_back(to_3d(line.b, print_z), extruder_conflict_torque - sticking_torque); distance_from_last_support_point = 0.0f; } @@ -519,6 +540,7 @@ void check_layer_global_stability(StabilityAccumulators &stability_accs, Issues check_object_stability(const PrintObject *po, const Params ¶ms) { #ifdef DEBUG_FILES FILE *debug_acc = boost::nowide::fopen(debug_out_path("accumulators.obj").c_str(), "w"); + FILE *malform_f = boost::nowide::fopen(debug_out_path("malformations.obj").c_str(), "w"); #endif StabilityAccumulators stability_accs; LayerLinesDistancer prev_layer_lines { { } }; @@ -680,6 +702,11 @@ Issues check_object_stability(const PrintObject *po, const Params ¶ms) { check_layer_global_stability(stability_accs, issues, max_flow_width, prev_layer_lines.get_lines(), print_z, params); #ifdef DEBUG_FILES + for (const auto &line : prev_layer_lines.get_lines()) { + Vec3f color = value_to_rgbf(0, 5.0f, line.malformation); + fprintf(malform_f, "v %f %f %f %f %f %f\n", line.b[0], + line.b[1], print_z, color[0], color[1], color[2]); + } for (const auto &line : prev_layer_lines.get_lines()) { Vec3f color = stability_accs.get_accumulator_color(line.stability_accumulator_id); fprintf(debug_acc, "v %f %f %f %f %f %f\n", line.b[0], @@ -691,6 +718,7 @@ Issues check_object_stability(const PrintObject *po, const Params ¶ms) { #ifdef DEBUG_FILES fclose(debug_acc); + fclose(malform_f); #endif std::cout << " SUPP: " << issues.supports_nedded.size() << std::endl; diff --git a/src/libslic3r/SupportSpotsGenerator.hpp b/src/libslic3r/SupportSpotsGenerator.hpp index 52b8b6078e..b1aab8156d 100644 --- a/src/libslic3r/SupportSpotsGenerator.hpp +++ b/src/libslic3r/SupportSpotsGenerator.hpp @@ -13,7 +13,7 @@ struct Params { float bridge_distance = 10.0f; //mm float bridge_distance_decrease_by_curvature_factor = 5.0f; // allowed bridge distance = bridge_distance / (this factor * (curvature / PI) ) - float min_distance_between_support_points = 3.0f; + float min_distance_between_support_points = 1.5f; // Adhesion computation : from experiment, PLA holds about 3g per mm^2 of base area (with reserve); So it can withstand about 3*gravity_constant force per mm^2 float base_adhesion = 3.0f * gravity_constant; // adhesion per mm^2 of first layer @@ -25,7 +25,8 @@ struct Params { float filament_density = 1.25f * 0.001f; // g/mm^3 ; Common filaments are very lightweight, so precise number is not that important float tensile_strength = 33000.0f; // mN/mm^2; 33 MPa is tensile strength of ABS, which has the lowest tensile strength from common materials. float tolerable_extruder_conflict_force = 50.0f * gravity_constant; // force that can occasionally push the model due to various factors (filament leaks, small curling, ... ); current value corresponds to weight of X grams - float max_curled_conflict_extruder_force = 300.0f * gravity_constant; // for areas with possible high layered curled filaments, max force to account for; + float malformations_additive_conflict_extruder_force = 100.0f * gravity_constant; // for areas with possible high layered curled filaments + }; struct SupportPoint { From cf94c44fd56dd3d108ad58c21c48075c841780ee Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Mon, 27 Jun 2022 17:15:51 +0200 Subject: [PATCH 35/78] add voxel grid cache to suppress accumulation of stability support points --- src/libslic3r/SupportSpotsGenerator.cpp | 132 +++++++++++++++++------- src/libslic3r/SupportSpotsGenerator.hpp | 2 +- 2 files changed, 97 insertions(+), 37 deletions(-) diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index d89f7222a7..1af7b96aaf 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -81,6 +81,61 @@ CurledFilament::CurledFilament(const Vec3f &position) : position(position), estimated_height(0.0f) { } +struct VoxelGrid { +private: + Vec3f cell_size; + Vec3f origin; + Vec3f size; + Vec3i cell_count; + + std::unordered_set taken_cells { }; + +public: + VoxelGrid(const PrintObject *po, float voxel_size) { + cell_size = Vec3f(voxel_size, voxel_size, voxel_size); + + Vec2crd size_half = po->size().head<2>().cwiseQuotient(Vec2crd(2, 2)) + Vec2crd::Ones(); + Vec3f min = unscale(Vec3crd(-size_half.x(), -size_half.y(), 0)).cast() - cell_size; + Vec3f max = unscale(Vec3crd(size_half.x(), size_half.y(), po->height())).cast() + cell_size; + + origin = min; + size = max - min; + cell_count = size.cwiseQuotient(cell_size).cast() + Vec3i::Ones(); + } + + Vec3i to_cell_coords(const Vec3f &position) const { + Vec3i cell_coords = (position - this->origin).cwiseQuotient(this->cell_size).cast(); + return cell_coords; + } + + size_t to_cell_index(const Vec3i &cell_coords) const { + assert(cell_coords.x() >= 0); + assert(cell_coords.x() < cell_count.x()); + assert(cell_coords.y() >= 0); + assert(cell_coords.y() < cell_count.y()); + assert(cell_coords.z() >= 0); + assert(cell_coords.z() < cell_count.z()); + + return cell_coords.z() * cell_count.x() * cell_count.y() + + cell_coords.y() * cell_count.x() + + cell_coords.x(); + } + + Vec3f get_cell_center(const Vec3i &cell_coords) const { + return origin + cell_coords.cast().cwiseProduct(this->cell_size) + + this->cell_size.cwiseQuotient(Vec3f(2.0f, 2.0f, 2.0)); + } + + void take_position(const Vec3f &position) { + taken_cells.insert(to_cell_index(to_cell_coords(position))); + } + + bool position_taken(const Vec3f &position) const { + return taken_cells.find(to_cell_index(to_cell_coords(position))) != taken_cells.end(); + } + +}; + class LayerLinesDistancer { private: std::vector lines; @@ -317,10 +372,10 @@ struct ExtrusionPropertiesAccumulator { // into checked lines, and gives it a stability accumulator id. If support is needed it pushes it // into issues as well. // Rules for stability accumulator id assigment: - // If there is close extrusion under, use min extrusion id between the id of the previous line, - // and id of line under. Also merge the accumulators of those two ids! - // If there is no close extrusion under, use id of the previous extrusion line. - // If there is no previous line, create new stability accumulator. +// If there is close extrusion under, use min extrusion id between the id of the previous line, +// and id of line under. Also merge the accumulators of those two ids! +// If there is no close extrusion under, use id of the previous extrusion line. +// If there is no previous line, create new stability accumulator. void check_extrusion_entity_stability(const ExtrusionEntity *entity, StabilityAccumulators &stability_accs, Issues &issues, @@ -404,9 +459,9 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, current_line.stability_accumulator_id = current_stability_acc; current_segment.add_extrusion(current_line, print_z, mm3_per_mm); if (bridging_acc.distance // if unsupported distance is larger than bridge distance linearly decreased by curvature, enforce supports. - > params.bridge_distance - / (1.0f + bridging_acc.max_curvature - * params.bridge_distance_decrease_by_curvature_factor / PI)) { + > params.bridge_distance + / (1.0f + bridging_acc.max_curvature + * params.bridge_distance_decrease_by_curvature_factor / PI)) { current_segment.add_support_point(current_line.b, 0.0f); // Do not count extrusion supports into the sticking force. They can be very densely placed, causing algorithm to overestimate stickiness. issues.supports_nedded.emplace_back(to_vec3f(current_line.b), 1.0); bridging_acc.reset(); @@ -414,7 +469,7 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, } //malformation - if (fabs(dist_from_prev_layer) < flow_width*2.0f) { + if (fabs(dist_from_prev_layer) < flow_width * 2.0f) { const ExtrusionLine &nearest_line = prev_layer_lines.get_line(nearest_line_idx); current_line.malformation += 0.7 * nearest_line.malformation; } @@ -434,18 +489,22 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, // are computed and if stability is not sufficient, support points are added // accumualtors are filtered by their pointer address, since one accumulator can have multiple IDs due to merging void check_layer_global_stability(StabilityAccumulators &stability_accs, + VoxelGrid &supports_presence_grid, Issues &issues, float flow_width, const std::vector &checked_lines, float print_z, - const Params ¶ms) { + const Params ¶ms, + std::mt19937_64& generator) { std::unordered_map> layer_accs_w_lines; for (size_t i = 0; i < checked_lines.size(); ++i) { - layer_accs_w_lines[&stability_accs.access(checked_lines[i].stability_accumulator_id)].push_back(checked_lines[i]); + layer_accs_w_lines[&stability_accs.access(checked_lines[i].stability_accumulator_id)].push_back( + checked_lines[i]); } for (auto &accumulator : layer_accs_w_lines) { StabilityAccumulator *acc = accumulator.first; + std::shuffle(accumulator.second.begin(), accumulator.second.end(), generator); LayerLinesDistancer acc_lines(std::move(accumulator.second)); if (acc->get_support_points().empty()) { @@ -458,23 +517,12 @@ void check_layer_global_stability(StabilityAccumulators &stability_accs, auto coord_fn = [&support_points](size_t idx, size_t dim) { return support_points[idx][dim]; }; - KDTreeIndirect<2, float, decltype(coord_fn)> tree(coord_fn, support_points.size()); - - float distance_from_last_support_point = params.min_distance_between_support_points * 2.0f; - for (const ExtrusionLine& line : acc_lines.get_lines()) { - distance_from_last_support_point += line.len; - - if (distance_from_last_support_point < params.min_distance_between_support_points) { - continue; - } - size_t nearest_supp_point_idx = find_closest_point(tree, line.b); - if ((line.b - support_points[nearest_supp_point_idx]).norm() < params.min_distance_between_support_points) { - continue; - } + KDTreeIndirect<2, float, decltype(coord_fn)> supports_tree(coord_fn, support_points.size()); + for (const ExtrusionLine &line : acc_lines.get_lines()) { Vec2f line_dir = (line.b - line.a).normalized(); Vec2f pivot_site_search_point = line.b + line_dir * 300.0f; - size_t pivot_idx = find_closest_point(tree, pivot_site_search_point); + size_t pivot_idx = find_closest_point(supports_tree, pivot_site_search_point); const Vec2f &pivot = support_points[pivot_idx]; const Vec2f &sticking_centroid = acc->get_sticking_centroid(); @@ -496,7 +544,7 @@ void check_layer_global_stability(StabilityAccumulators &stability_accs, extruder_pressure_direction.normalize(); float conflict_torque_arm = (to_3d(Vec2f(pivot - line.b), print_z).cross( extruder_pressure_direction)).norm(); - float extruder_conflict_force = params.tolerable_extruder_conflict_force + + float extruder_conflict_force = params.tolerable_extruder_conflict_force + line.malformation * params.malformations_additive_conflict_extruder_force; float extruder_conflict_torque = extruder_conflict_force * conflict_torque_arm; @@ -506,12 +554,15 @@ void check_layer_global_stability(StabilityAccumulators &stability_accs, Vec2f target_point; size_t _idx; acc_lines.signed_distance_from_lines(pivot_site_search_point, _idx, target_point); - float area = params.support_points_interface_radius * params.support_points_interface_radius - * float(PI); - float sticking_force = area * params.support_adhesion; - acc->add_support_point(target_point, sticking_force); - issues.supports_nedded.emplace_back(to_3d(line.b, print_z), extruder_conflict_torque - sticking_torque); - distance_from_last_support_point = 0.0f; + if (!supports_presence_grid.position_taken(to_3d(target_point, print_z))) { + float area = params.support_points_interface_radius * params.support_points_interface_radius + * float(PI); + float sticking_force = area * params.support_adhesion; + acc->add_support_point(target_point, sticking_force); + issues.supports_nedded.emplace_back(to_3d(target_point, print_z), + extruder_conflict_torque - sticking_torque); + supports_presence_grid.take_position(to_3d(target_point, print_z)); + } } #if 1 BOOST_LOG_TRIVIAL(debug) @@ -546,6 +597,8 @@ Issues check_object_stability(const PrintObject *po, const Params ¶ms) { LayerLinesDistancer prev_layer_lines { { } }; Issues issues { }; std::vector checked_lines; + VoxelGrid supports_presence_grid { po, params.min_distance_between_support_points }; + std::mt19937_64 generator { 27644437 }; // PREPARE BASE LAYER float max_flow_width = 0.0f; @@ -699,14 +752,21 @@ Issues check_object_stability(const PrintObject *po, const Params ¶ms) { } } - check_layer_global_stability(stability_accs, issues, max_flow_width, prev_layer_lines.get_lines(), print_z, params); + check_layer_global_stability(stability_accs, + supports_presence_grid, + issues, + max_flow_width, + prev_layer_lines.get_lines(), + print_z, + params, + generator); #ifdef DEBUG_FILES for (const auto &line : prev_layer_lines.get_lines()) { - Vec3f color = value_to_rgbf(0, 5.0f, line.malformation); - fprintf(malform_f, "v %f %f %f %f %f %f\n", line.b[0], - line.b[1], print_z, color[0], color[1], color[2]); - } + Vec3f color = value_to_rgbf(0, 5.0f, line.malformation); + fprintf(malform_f, "v %f %f %f %f %f %f\n", line.b[0], + line.b[1], print_z, color[0], color[1], color[2]); + } for (const auto &line : prev_layer_lines.get_lines()) { Vec3f color = stability_accs.get_accumulator_color(line.stability_accumulator_id); fprintf(debug_acc, "v %f %f %f %f %f %f\n", line.b[0], diff --git a/src/libslic3r/SupportSpotsGenerator.hpp b/src/libslic3r/SupportSpotsGenerator.hpp index b1aab8156d..63b4e55724 100644 --- a/src/libslic3r/SupportSpotsGenerator.hpp +++ b/src/libslic3r/SupportSpotsGenerator.hpp @@ -13,7 +13,7 @@ struct Params { float bridge_distance = 10.0f; //mm float bridge_distance_decrease_by_curvature_factor = 5.0f; // allowed bridge distance = bridge_distance / (this factor * (curvature / PI) ) - float min_distance_between_support_points = 1.5f; + float min_distance_between_support_points = 3.0f; // Adhesion computation : from experiment, PLA holds about 3g per mm^2 of base area (with reserve); So it can withstand about 3*gravity_constant force per mm^2 float base_adhesion = 3.0f * gravity_constant; // adhesion per mm^2 of first layer From 8e5cdf29baa8a9e01887876609788c6a896b8b27 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Tue, 28 Jun 2022 13:40:22 +0200 Subject: [PATCH 36/78] improve curling model parameters, other small improvements --- src/libslic3r/PrintObject.cpp | 2 +- src/libslic3r/SupportSpotsGenerator.cpp | 15 +++++++-------- src/libslic3r/SupportSpotsGenerator.hpp | 22 +++++++++++----------- 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 92c002c8de..d923c810a7 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -434,7 +434,7 @@ void PrintObject::generate_support_spots() Vec3f point = Vec3f(inv_transform * support_point.position); Vec3f origin = Vec3f( inv_transform * Vec3f(support_point.position.x(), support_point.position.y(), 0.0f)); - selector.enforce_spot(point, origin, 1.0f); + selector.enforce_spot(point, origin, 0.3f); } model_volume->supported_facets.set(selector.selector); diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index 1af7b96aaf..b9b98e952b 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -433,7 +433,7 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, curr_angle = angle(v1, v2); } bridging_acc.add_angle(curr_angle); - malformation_acc.add_angle(curr_angle); + malformation_acc.add_angle(std::max(0.0f,curr_angle)); size_t nearest_line_idx; Vec2f nearest_point; @@ -449,7 +449,6 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, current_line.stability_accumulator_id = current_stability_acc; stability_accs.access(current_stability_acc).add_extrusion(current_line, print_z, mm3_per_mm); bridging_acc.reset(); - // TODO curving here } else { bridging_acc.add_distance(current_line.len); if (current_stability_acc == NULL_ACC_ID) { @@ -460,8 +459,8 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, current_segment.add_extrusion(current_line, print_z, mm3_per_mm); if (bridging_acc.distance // if unsupported distance is larger than bridge distance linearly decreased by curvature, enforce supports. > params.bridge_distance - / (1.0f + bridging_acc.max_curvature - * params.bridge_distance_decrease_by_curvature_factor / PI)) { + / (1.0f + (bridging_acc.max_curvature + * params.bridge_distance_decrease_by_curvature_factor / PI))) { current_segment.add_support_point(current_line.b, 0.0f); // Do not count extrusion supports into the sticking force. They can be very densely placed, causing algorithm to overestimate stickiness. issues.supports_nedded.emplace_back(to_vec3f(current_line.b), 1.0); bridging_acc.reset(); @@ -471,10 +470,10 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, //malformation if (fabs(dist_from_prev_layer) < flow_width * 2.0f) { const ExtrusionLine &nearest_line = prev_layer_lines.get_line(nearest_line_idx); - current_line.malformation += 0.7 * nearest_line.malformation; + current_line.malformation += 0.9 * nearest_line.malformation; } if (dist_from_prev_layer > flow_width * 0.3) { - current_line.malformation += 0.6 + 0.4 * malformation_acc.max_curvature / PI; + current_line.malformation += 0.15 * (0.6 + 0.4 * malformation_acc.max_curvature / PI); } else { malformation_acc.reset(); } @@ -545,7 +544,7 @@ void check_layer_global_stability(StabilityAccumulators &stability_accs, float conflict_torque_arm = (to_3d(Vec2f(pivot - line.b), print_z).cross( extruder_pressure_direction)).norm(); float extruder_conflict_force = params.tolerable_extruder_conflict_force + - line.malformation * params.malformations_additive_conflict_extruder_force; + std::min(line.malformation, 1.0f) * params.malformations_additive_conflict_extruder_force; float extruder_conflict_torque = extruder_conflict_force * conflict_torque_arm; float total_torque = bed_movement_torque + extruder_conflict_torque - weight_torque - sticking_torque; @@ -763,7 +762,7 @@ Issues check_object_stability(const PrintObject *po, const Params ¶ms) { #ifdef DEBUG_FILES for (const auto &line : prev_layer_lines.get_lines()) { - Vec3f color = value_to_rgbf(0, 5.0f, line.malformation); + Vec3f color = value_to_rgbf(0, 1.0f, line.malformation); fprintf(malform_f, "v %f %f %f %f %f %f\n", line.b[0], line.b[1], print_z, color[0], color[1], color[2]); } diff --git a/src/libslic3r/SupportSpotsGenerator.hpp b/src/libslic3r/SupportSpotsGenerator.hpp index 63b4e55724..ac5f0176d8 100644 --- a/src/libslic3r/SupportSpotsGenerator.hpp +++ b/src/libslic3r/SupportSpotsGenerator.hpp @@ -10,22 +10,22 @@ namespace SupportSpotsGenerator { struct Params { const float gravity_constant = 9806.65f; // mm/s^2; gravity acceleration on Earth's surface, algorithm assumes that printer is in upwards position. - float bridge_distance = 10.0f; //mm - float bridge_distance_decrease_by_curvature_factor = 5.0f; // allowed bridge distance = bridge_distance / (this factor * (curvature / PI) ) + const float bridge_distance = 12.0f; //mm + const float bridge_distance_decrease_by_curvature_factor = 5.0f; // allowed bridge distance = bridge_distance / (this factor * (curvature / PI) ) - float min_distance_between_support_points = 3.0f; + const float min_distance_between_support_points = 3.0f; // Adhesion computation : from experiment, PLA holds about 3g per mm^2 of base area (with reserve); So it can withstand about 3*gravity_constant force per mm^2 - float base_adhesion = 3.0f * gravity_constant; // adhesion per mm^2 of first layer - float support_adhesion = 1.0f * gravity_constant; // adhesion per mm^2 of support interface layer + const float base_adhesion = 3.0f * gravity_constant; // adhesion per mm^2 of first layer + const float support_adhesion = 1.0f * gravity_constant; // adhesion per mm^2 of support interface layer - float support_points_interface_radius = 1.0f; // mm + const float support_points_interface_radius = 0.3f; // mm - float max_acceleration = 9*1000.0f; // mm/s^2 ; max acceleration of object (bed) in XY (NOTE: The max hit is received by the object in the jerk phase, so the usual machine limits are too low) - float filament_density = 1.25f * 0.001f; // g/mm^3 ; Common filaments are very lightweight, so precise number is not that important - float tensile_strength = 33000.0f; // mN/mm^2; 33 MPa is tensile strength of ABS, which has the lowest tensile strength from common materials. - float tolerable_extruder_conflict_force = 50.0f * gravity_constant; // force that can occasionally push the model due to various factors (filament leaks, small curling, ... ); current value corresponds to weight of X grams - float malformations_additive_conflict_extruder_force = 100.0f * gravity_constant; // for areas with possible high layered curled filaments + const float max_acceleration = 9*1000.0f; // mm/s^2 ; max acceleration of object (bed) in XY (NOTE: The max hit is received by the object in the jerk phase, so the usual machine limits are too low) + const float filament_density = 1.25f * 0.001f; // g/mm^3 ; Common filaments are very lightweight, so precise number is not that important + const float tensile_strength = 33000.0f; // mN/mm^2; 33 MPa is tensile strength of ABS, which has the lowest tensile strength from common materials. + const float tolerable_extruder_conflict_force = 50.0f * gravity_constant; // force that can occasionally push the model due to various factors (filament leaks, small curling, ... ); current value corresponds to weight of X grams + const float malformations_additive_conflict_extruder_force = 100.0f * gravity_constant; // for areas with possible high layered curled filaments }; From 0187ed855e3f8332d7693499a29e908f3e8d0f24 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Tue, 28 Jun 2022 14:49:08 +0200 Subject: [PATCH 37/78] do not consider concave angles for curling, they actually improve the issue --- src/libslic3r/SupportSpotsGenerator.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index b9b98e952b..7c84f7c847 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -473,7 +473,8 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, current_line.malformation += 0.9 * nearest_line.malformation; } if (dist_from_prev_layer > flow_width * 0.3) { - current_line.malformation += 0.15 * (0.6 + 0.4 * malformation_acc.max_curvature / PI); + malformation_acc.add_distance(current_line.len); + current_line.malformation += 0.15 * (0.8 + 0.2 * malformation_acc.max_curvature / (1.0f + 0.5f*malformation_acc.distance)); } else { malformation_acc.reset(); } From 91ec455fa33ffec7eaf60c466d2b4965e74803cf Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Tue, 28 Jun 2022 15:03:39 +0200 Subject: [PATCH 38/78] remove unnecesary randomization --- src/libslic3r/SupportSpotsGenerator.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index 7c84f7c847..ad475b9087 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -494,8 +494,7 @@ void check_layer_global_stability(StabilityAccumulators &stability_accs, float flow_width, const std::vector &checked_lines, float print_z, - const Params ¶ms, - std::mt19937_64& generator) { + const Params ¶ms) { std::unordered_map> layer_accs_w_lines; for (size_t i = 0; i < checked_lines.size(); ++i) { layer_accs_w_lines[&stability_accs.access(checked_lines[i].stability_accumulator_id)].push_back( @@ -504,7 +503,6 @@ void check_layer_global_stability(StabilityAccumulators &stability_accs, for (auto &accumulator : layer_accs_w_lines) { StabilityAccumulator *acc = accumulator.first; - std::shuffle(accumulator.second.begin(), accumulator.second.end(), generator); LayerLinesDistancer acc_lines(std::move(accumulator.second)); if (acc->get_support_points().empty()) { @@ -598,7 +596,6 @@ Issues check_object_stability(const PrintObject *po, const Params ¶ms) { Issues issues { }; std::vector checked_lines; VoxelGrid supports_presence_grid { po, params.min_distance_between_support_points }; - std::mt19937_64 generator { 27644437 }; // PREPARE BASE LAYER float max_flow_width = 0.0f; @@ -758,8 +755,7 @@ Issues check_object_stability(const PrintObject *po, const Params ¶ms) { max_flow_width, prev_layer_lines.get_lines(), print_z, - params, - generator); + params); #ifdef DEBUG_FILES for (const auto &line : prev_layer_lines.get_lines()) { From 263e16ca925f264a726f18579fe50fc67181c87a Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Wed, 29 Jun 2022 17:34:13 +0200 Subject: [PATCH 39/78] draft mode of recon_thin_islands, but not working properly --- src/libslic3r/SupportSpotsGenerator.cpp | 166 ++++++++++++++++++++---- 1 file changed, 141 insertions(+), 25 deletions(-) diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index ad475b9087..61829243be 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -29,10 +29,10 @@ class ExtrusionLine { public: ExtrusionLine() : - a(Vec2f::Zero()), b(Vec2f::Zero()), len(0.0f) { + a(Vec2f::Zero()), b(Vec2f::Zero()), len(0.0f), external_perimeter(false) { } - ExtrusionLine(const Vec2f &_a, const Vec2f &_b) : - a(_a), b(_b), len((_a - _b).norm()) { + ExtrusionLine(const Vec2f &_a, const Vec2f &_b, bool external_perimeter) : + a(_a), b(_b), len((_a - _b).norm()), external_perimeter(external_perimeter) { } float length() { @@ -42,6 +42,7 @@ public: Vec2f a; Vec2f b; float len; + bool external_perimeter; float malformation = 0.0f; size_t stability_accumulator_id = NULL_ACC_ID; @@ -172,6 +173,10 @@ public: const std::vector& get_lines() const { return lines; } + + void set_new_acc_id(size_t line_idx, size_t acc_id) { + lines[line_idx].stability_accumulator_id = acc_id; + } }; // StabilityAccumulator accumulates extrusions for each connected model part from bed to current printed layer. @@ -271,7 +276,7 @@ private: public: StabilityAccumulators() = default; - int create_accumulator() { + size_t create_accumulator() { size_t id = next_id; next_id++; mapping[id] = accumulators.size(); @@ -283,7 +288,9 @@ public: return accumulators[mapping[id]]; } - void merge_accumulators(size_t from_id, size_t to_id) { + void merge_accumulators(size_t id_a, size_t id_b) { + size_t from_id = std::max(id_a, id_b); + size_t to_id = std::min(id_a, id_b); if (from_id == NULL_ACC_ID || to_id == NULL_ACC_ID) { return; } @@ -399,7 +406,8 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, entity->collect_points(points); std::vector lines; lines.reserve(points.size() * 1.5); - lines.emplace_back(unscaled(points[0]).cast(), unscaled(points[0]).cast()); + bool is_ex_perimeter = entity->role() == erExternalPerimeter; + lines.emplace_back(unscaled(points[0]).cast(), unscaled(points[0]).cast(), is_ex_perimeter); for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { Vec2f start = unscaled(points[point_idx]).cast(); Vec2f next = unscaled(points[point_idx + 1]).cast(); @@ -411,7 +419,7 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, for (int i = 0; i < lines_count; ++i) { Vec2f a(start + v * (i * step_size)); Vec2f b(start + v * ((i + 1) * step_size)); - lines.emplace_back(a, b); + lines.emplace_back(a, b, is_ex_perimeter); } } @@ -433,7 +441,7 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, curr_angle = angle(v1, v2); } bridging_acc.add_angle(curr_angle); - malformation_acc.add_angle(std::max(0.0f,curr_angle)); + malformation_acc.add_angle(std::max(0.0f, curr_angle)); size_t nearest_line_idx; Vec2f nearest_point; @@ -443,8 +451,7 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, if (dist_from_prev_layer < flow_width) { const ExtrusionLine &nearest_line = prev_layer_lines.get_line(nearest_line_idx); size_t acc_id = nearest_line.stability_accumulator_id; - stability_accs.merge_accumulators(std::max(acc_id, current_stability_acc), - std::min(acc_id, current_stability_acc)); + stability_accs.merge_accumulators(acc_id, current_stability_acc); current_stability_acc = std::min(acc_id, current_stability_acc); current_line.stability_accumulator_id = current_stability_acc; stability_accs.access(current_stability_acc).add_extrusion(current_line, print_z, mm3_per_mm); @@ -474,7 +481,8 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, } if (dist_from_prev_layer > flow_width * 0.3) { malformation_acc.add_distance(current_line.len); - current_line.malformation += 0.15 * (0.8 + 0.2 * malformation_acc.max_curvature / (1.0f + 0.5f*malformation_acc.distance)); + current_line.malformation += 0.15 + * (0.8 + 0.2 * malformation_acc.max_curvature / (1.0f + 0.5f * malformation_acc.distance)); } else { malformation_acc.reset(); } @@ -562,7 +570,7 @@ void check_layer_global_stability(StabilityAccumulators &stability_accs, supports_presence_grid.take_position(to_3d(target_point, print_z)); } } -#if 1 +#if 0 BOOST_LOG_TRIVIAL(debug) << "SSG: sticking_arm: " << sticking_arm; BOOST_LOG_TRIVIAL(debug) @@ -586,7 +594,114 @@ void check_layer_global_stability(StabilityAccumulators &stability_accs, } } -Issues check_object_stability(const PrintObject *po, const Params ¶ms) { +void reckon_thin_islands(StabilityAccumulators &stability_accs, + float flow_width, + float mm3_per_mm, + float print_z, + LayerLinesDistancer &layer_lines, + const Params& params) { + const std::vector &lines = layer_lines.get_lines(); + std::vector> extrusions; //start and end idx (one beyond last extrusion) [start,end) + Vec2f current_pt = lines[0].a; + std::pair current_ext(0,1); + for (size_t lidx = 0; lidx < lines.size(); ++lidx) { + const ExtrusionLine& line = lines[lidx]; + if (line.a == current_pt) { + current_ext.second = lidx + 1; + } else { + extrusions.push_back(current_ext); + current_ext.first = lidx; + current_ext.second = lidx + 1; + } + current_pt = line.b; + } + + std::vector islands; + std::vector> island_extrusions; + for (size_t e = 0; e < extrusions.size(); ++e) { + if (lines[extrusions[e].first].external_perimeter) { + std::vector copy(extrusions[e].second - extrusions[e].first); + for (size_t ex_line_idx = extrusions[e].first; ex_line_idx < extrusions[e].second; ++ex_line_idx) { + copy[ex_line_idx-extrusions[e].first] = lines[ex_line_idx]; + } + islands.emplace_back(std::move(copy)); + island_extrusions.push_back({e}); + } + } + + for (size_t i = 0; i < islands.size(); ++i) { + for (size_t e = 0; e < extrusions.size(); ++e) { + if (!lines[extrusions[e].first].external_perimeter){ + size_t _idx; + Vec2f _pt; + if (islands[i].signed_distance_from_lines(lines[extrusions[e].first].a, _idx, _pt) < 0) { + island_extrusions[i].push_back(e); + } + } + } + } + + for (size_t i = 0; i < islands.size(); ++i) { + if (islands[i].get_lines().empty()) { + continue; + } + for (size_t j = 0; j < islands.size(); ++j) { + if (islands[j].get_lines().empty() || i == j) { + continue; + } + size_t _idx; + Vec2f _pt; + if (islands[i].signed_distance_from_lines(islands[j].get_line(0).a, _idx, _pt) < 0) { + island_extrusions[i].insert(island_extrusions[i].end(), island_extrusions[j].begin(), + island_extrusions[j].end()); + island_extrusions[j].clear(); + } + } + } + + size_t islands_count = 0; + for (const std::vector& island_ex : island_extrusions) { + if (island_ex.empty()) { + continue; + } + islands_count++; + float cross_section = 0.0f; + size_t acc_id = NULL_ACC_ID; + for (size_t extrusion_idx : island_ex) { + for (size_t lidx = extrusions[extrusion_idx].first; lidx < extrusions[extrusion_idx].second; ++lidx) { + const ExtrusionLine& line = lines[lidx]; + cross_section += line.len * flow_width; + stability_accs.merge_accumulators(acc_id, line.stability_accumulator_id); + acc_id = std::min(acc_id, line.stability_accumulator_id); + } + } + + float max_force = cross_section * params.tensile_strength; + + if (stability_accs.access(acc_id).get_sticking_force() > max_force) { + BOOST_LOG_TRIVIAL(debug) << "SSG: Forking new accumulator for island because tensile strenth is too low: " << max_force; + BOOST_LOG_TRIVIAL(debug) << "SSG: sticking force: " << stability_accs.access(acc_id).get_sticking_force(); + + size_t new_acc_id = stability_accs.create_accumulator(); + StabilityAccumulator& acc = stability_accs.access(new_acc_id); + + for (size_t extrusion_idx : island_ex) { + for (size_t lidx = extrusions[extrusion_idx].first; lidx < extrusions[extrusion_idx].second; ++lidx) { + const ExtrusionLine& line = lines[lidx]; + float tensile_strength = params.tensile_strength * line.len * flow_width; + acc.add_base_extrusion(line, tensile_strength, print_z, mm3_per_mm); + layer_lines.set_new_acc_id(lidx, new_acc_id); + } + } + } + } + + BOOST_LOG_TRIVIAL(debug) << "SSG: There are " << islands_count << " islands on printz: " << print_z; + +} + +Issues +check_object_stability(const PrintObject *po, const Params ¶ms) { #ifdef DEBUG_FILES FILE *debug_acc = boost::nowide::fopen(debug_out_path("accumulators.obj").c_str(), "w"); FILE *malform_f = boost::nowide::fopen(debug_out_path("malformations.obj").c_str(), "w"); @@ -614,7 +729,7 @@ Issues check_object_stability(const PrintObject *po, const Params ¶ms) { for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { Vec2f start = unscaled(points[point_idx]).cast(); Vec2f next = unscaled(points[point_idx + 1]).cast(); - ExtrusionLine line { start, next }; + ExtrusionLine line { start, next, perimeter->role() == erExternalPerimeter }; line.stability_accumulator_id = id; float line_sticking_force = line.len * flow_width * params.base_adhesion; acc.add_base_extrusion(line, line_sticking_force, base_print_z, mm3_per_mm); @@ -623,7 +738,7 @@ Issues check_object_stability(const PrintObject *po, const Params ¶ms) { if (perimeter->is_loop()) { Vec2f start = unscaled(points[points.size() - 1]).cast(); Vec2f next = unscaled(points[0]).cast(); - ExtrusionLine line { start, next }; + ExtrusionLine line { start, next, perimeter->role() == erExternalPerimeter }; line.stability_accumulator_id = id; float line_sticking_force = line.len * flow_width * params.base_adhesion; acc.add_base_extrusion(line, line_sticking_force, base_print_z, mm3_per_mm); @@ -643,7 +758,7 @@ Issues check_object_stability(const PrintObject *po, const Params ¶ms) { for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { Vec2f start = unscaled(points[point_idx]).cast(); Vec2f next = unscaled(points[point_idx + 1]).cast(); - ExtrusionLine line { start, next }; + ExtrusionLine line { start, next, false }; line.stability_accumulator_id = id; float line_sticking_force = line.len * flow_width * params.base_adhesion; acc.add_base_extrusion(line, line_sticking_force, base_print_z, mm3_per_mm); @@ -663,9 +778,7 @@ Issues check_object_stability(const PrintObject *po, const Params ¶ms) { float dist = prev_layer_lines.signed_distance_from_lines(site_search_location, nearest_line_idx, nearest_pt); if (std::abs(dist) < max_flow_width) { size_t other_line_acc_id = prev_layer_lines.get_line(nearest_line_idx).stability_accumulator_id; - size_t from_id = std::max(other_line_acc_id, l.stability_accumulator_id); - size_t to_id = std::min(other_line_acc_id, l.stability_accumulator_id); - stability_accs.merge_accumulators(from_id, to_id); + stability_accs.merge_accumulators(other_line_acc_id, l.stability_accumulator_id); } } @@ -718,7 +831,7 @@ Issues check_object_stability(const PrintObject *po, const Params ¶ms) { for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { Vec2f start = unscaled(points[point_idx]).cast(); Vec2f next = unscaled(points[point_idx + 1]).cast(); - ExtrusionLine line { start, next }; + ExtrusionLine line { start, next, false }; line.stability_accumulator_id = acc_id; acc.add_extrusion(line, print_z, mm3_per_mm); } @@ -740,23 +853,25 @@ Issues check_object_stability(const PrintObject *po, const Params ¶ms) { float dist = prev_layer_lines.signed_distance_from_lines(fill_point.first, nearest_line_idx, nearest_pt); if (dist < max_fill_flow_width) { size_t other_line_acc_id = prev_layer_lines.get_line(nearest_line_idx).stability_accumulator_id; - size_t from_id = std::max(other_line_acc_id, fill_point.second); - size_t to_id = std::min(other_line_acc_id, fill_point.second); - stability_accs.merge_accumulators(from_id, to_id); + stability_accs.merge_accumulators(other_line_acc_id, fill_point.second); } else { BOOST_LOG_TRIVIAL(debug) << "SSG: ERROR: seem that infill starts in the air? on printz: " << print_z; } } + float flow_width = get_flow_width(layer->regions()[0], erExternalPerimeter); + check_layer_global_stability(stability_accs, supports_presence_grid, issues, - max_flow_width, + flow_width, prev_layer_lines.get_lines(), print_z, params); +// reckon_thin_islands(stability_accs, flow_width, flow_width*layer->height, print_z, prev_layer_lines, params); + #ifdef DEBUG_FILES for (const auto &line : prev_layer_lines.get_lines()) { Vec3f color = value_to_rgbf(0, 1.0f, line.malformation); @@ -823,7 +938,8 @@ std::vector quick_search(const PrintObject *po, const Params ¶ms) { return {}; } -Issues full_search(const PrintObject *po, const Params ¶ms) { +Issues +full_search(const PrintObject *po, const Params ¶ms) { auto issues = check_object_stability(po, params); #ifdef DEBUG_FILES debug_export(issues, "issues"); From 0a8f70c1bab07bcfb5b904f3173ce9fe21f88f6b Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Thu, 30 Jun 2022 17:10:51 +0200 Subject: [PATCH 40/78] inital phase of refactoring, segmentation should now build graph of connected sections --- src/libslic3r/CMakeLists.txt | 3 +- src/libslic3r/PrintObject.cpp | 2 +- src/libslic3r/SupportSpotsGenerator.cpp | 2 +- src/libslic3r/SupportSpotsGenerator.hpp | 18 +- .../SupportSpotsGeneratorRefactoring.cpp | 612 ++++++++++++++++++ 5 files changed, 620 insertions(+), 17 deletions(-) create mode 100644 src/libslic3r/SupportSpotsGeneratorRefactoring.cpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index e478188356..0c4fd20795 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -245,7 +245,8 @@ set(SLIC3R_SOURCES SlicingAdaptive.hpp Subdivide.cpp Subdivide.hpp - SupportSpotsGenerator.cpp +# SupportSpotsGenerator.cpp + SupportSpotsGeneratorRefactoring.cpp SupportSpotsGenerator.hpp SupportMaterial.cpp SupportMaterial.hpp diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index d923c810a7..674aaec26a 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -430,7 +430,7 @@ void PrintObject::generate_support_spots() Transform3f inv_transform = (obj_transform * model_transformation).inverse().cast(); TriangleSelectorWrapper selector { model_volume->mesh() }; - for (const SupportSpotsGenerator::SupportPoint &support_point : issues.supports_nedded) { + for (const SupportSpotsGenerator::SupportPoint &support_point : issues.support_points) { Vec3f point = Vec3f(inv_transform * support_point.position); Vec3f origin = Vec3f( inv_transform * Vec3f(support_point.position.x(), support_point.position.y(), 0.0f)); diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index 61829243be..08d734a38e 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -870,7 +870,7 @@ check_object_stability(const PrintObject *po, const Params ¶ms) { print_z, params); -// reckon_thin_islands(stability_accs, flow_width, flow_width*layer->height, print_z, prev_layer_lines, params); + reckon_thin_islands(stability_accs, flow_width, flow_width*layer->height, print_z, prev_layer_lines, params); #ifdef DEBUG_FILES for (const auto &line : prev_layer_lines.get_lines()) { diff --git a/src/libslic3r/SupportSpotsGenerator.hpp b/src/libslic3r/SupportSpotsGenerator.hpp index ac5f0176d8..2a9b48fddc 100644 --- a/src/libslic3r/SupportSpotsGenerator.hpp +++ b/src/libslic3r/SupportSpotsGenerator.hpp @@ -30,24 +30,14 @@ struct Params { }; struct SupportPoint { - SupportPoint(const Vec3f &position, float weight); + SupportPoint(const Vec3f &position, float force,const Vec3f& direction); Vec3f position; - float weight; -}; - -struct CurledFilament { - CurledFilament(const Vec3f &position, float estimated_height); - explicit CurledFilament(const Vec3f &position); - Vec3f position; - float estimated_height; + float force; + Vec3f direction; }; struct Issues { - std::vector supports_nedded; - std::vector curling_up; - - void add(const Issues &layer_issues); - bool empty() const; + std::vector support_points; }; std::vector quick_search(const PrintObject *po, const Params ¶ms = Params { }); diff --git a/src/libslic3r/SupportSpotsGeneratorRefactoring.cpp b/src/libslic3r/SupportSpotsGeneratorRefactoring.cpp new file mode 100644 index 0000000000..1fe3b6b2ab --- /dev/null +++ b/src/libslic3r/SupportSpotsGeneratorRefactoring.cpp @@ -0,0 +1,612 @@ +#include "SupportSpotsGenerator.hpp" + +#include "tbb/parallel_for.h" +#include "tbb/blocked_range.h" +#include "tbb/blocked_range2d.h" +#include "tbb/parallel_reduce.h" +#include +#include +#include +#include + +#include "AABBTreeLines.hpp" +#include "KDTreeIndirect.hpp" +#include "libslic3r/Layer.hpp" +#include "libslic3r/ClipperUtils.hpp" +#include "Geometry/ConvexHull.hpp" + +#define DEBUG_FILES + +#ifdef DEBUG_FILES +#include +#include "libslic3r/Color.hpp" +#endif + +namespace Slic3r { + +class ExtrusionLine +{ +public: + ExtrusionLine() : + a(Vec2f::Zero()), b(Vec2f::Zero()), len(0.0f), external_perimeter(false) { + } + ExtrusionLine(const Vec2f &_a, const Vec2f &_b, bool external_perimeter) : + a(_a), b(_b), len((_a - _b).norm()), external_perimeter(external_perimeter) { + } + + float length() { + return (a - b).norm(); + } + + Vec2f a; + Vec2f b; + float len; + bool external_perimeter; + + bool support_point_generated = false; + float malformation = 0.0f; + + static const constexpr int Dim = 2; + using Scalar = Vec2f::Scalar; +}; + +auto get_a(ExtrusionLine &&l) { + return l.a; +} +auto get_b(ExtrusionLine &&l) { + return l.b; +} + +namespace SupportSpotsGenerator { + +SupportPoint::SupportPoint(const Vec3f &position, float force, const Vec3f &direction) : + position(position), force(force), direction(direction) { +} + +class LinesDistancer { +private: + std::vector lines; + AABBTreeIndirect::Tree<2, float> tree; + +public: + explicit LinesDistancer(std::vector &lines) : + lines(lines) { + tree = AABBTreeLines::build_aabb_tree_over_indexed_lines(this->lines); + } + + // negative sign means inside + float signed_distance_from_lines(const Vec2f &point, size_t &nearest_line_index_out, + Vec2f &nearest_point_out) const { + auto distance = AABBTreeLines::squared_distance_to_indexed_lines(lines, tree, point, nearest_line_index_out, + nearest_point_out); + if (distance < 0) + return std::numeric_limits::infinity(); + + distance = sqrt(distance); + const ExtrusionLine &line = lines[nearest_line_index_out]; + Vec2f v1 = line.b - line.a; + Vec2f v2 = point - line.a; + if ((v1.x() * v2.y()) - (v1.y() * v2.x()) > 0.0) { + distance *= -1; + } + return distance; + } + + const ExtrusionLine& get_line(size_t line_idx) const { + return lines[line_idx]; + } + + const std::vector& get_lines() const { + return lines; + } +}; + +static const size_t NULL_ISLAND = std::numeric_limits::max(); + +class PixelGrid { + Vec2f pixel_size; + Vec2f origin; + Vec2f size; + Vec2i pixel_count; + + std::vector pixels { }; + +public: + PixelGrid(const PrintObject *po, float resolution) { + pixel_size = Vec2f(resolution, resolution); + + Vec2crd size_half = po->size().head<2>().cwiseQuotient(Vec2crd(2, 2)) + Vec2crd::Ones(); + Vec2f min = unscale(Vec2crd(-size_half.x(), -size_half.y())).cast(); + Vec2f max = unscale(Vec2crd(size_half.x(), size_half.y())).cast(); + + origin = min; + size = max - min; + pixel_count = size.cwiseQuotient(pixel_size).cast() + Vec2i::Ones(); + + pixels.resize(pixel_count.y() * pixel_count.x()); + clear(); + } + + void distribute_edge(const Vec2f &p1, const Vec2f &p2, size_t value) { + Vec2f dir = (p2 - p1); + float length = dir.norm(); + if (length < 0.1) { + return; + } + float step_size = this->pixel_size.x() / 2.0; + + float distributed_length = 0; + while (distributed_length < length) { + float next_len = std::min(length, distributed_length + step_size); + Vec2f location = p1 + ((next_len / length) * dir); + this->access_pixel(location) = value; + + distributed_length = next_len; + } + } + + void clear() { + for (size_t &val : pixels) { + val = NULL_ISLAND; + } + } + + float pixel_area() const { + return this->pixel_size.x() * this->pixel_size.y(); + } + + size_t get_pixel(const Vec2i &coords) const { + return pixels[this->to_pixel_index(coords)]; + } + + Vec2i get_pixel_count() { + return pixel_count; + } + +private: + Vec2i to_pixel_coords(const Vec2f &position) const { + Vec2i pixel_coords = (position - this->origin).cwiseQuotient(this->pixel_size).cast(); + return pixel_coords; + } + + size_t to_pixel_index(const Vec2i &pixel_coords) const { + assert(pixel_coords.x() >= 0); + assert(pixel_coords.x() < pixel_count.x()); + assert(pixel_coords.y() >= 0); + assert(pixel_coords.y() < pixel_count.y()); + + return pixel_coords.y() * pixel_count.x() + pixel_coords.x(); + } + + size_t& access_pixel(const Vec2f &position) { + return pixels[this->to_pixel_index(this->to_pixel_coords(position))]; + } +}; + +struct Island { + std::unordered_map islands_under_with_connection_area; + std::vector pivot_points; + float volume; + Vec3f volume_centroid; + float sticking_force; // for support points present on this layer (or bed extrusions) + Vec3f sticking_centroid; +}; + +struct LayerIslands { + std::vector islands; +}; + +float get_flow_width(const LayerRegion *region, ExtrusionRole role) { + switch (role) { + case ExtrusionRole::erBridgeInfill: + return region->flow(FlowRole::frExternalPerimeter).width(); + case ExtrusionRole::erExternalPerimeter: + return region->flow(FlowRole::frExternalPerimeter).width(); + case ExtrusionRole::erGapFill: + return region->flow(FlowRole::frInfill).width(); + case ExtrusionRole::erPerimeter: + return region->flow(FlowRole::frPerimeter).width(); + case ExtrusionRole::erSolidInfill: + return region->flow(FlowRole::frSolidInfill).width(); + case ExtrusionRole::erInternalInfill: + return region->flow(FlowRole::frInfill).width(); + case ExtrusionRole::erTopSolidInfill: + return region->flow(FlowRole::frTopSolidInfill).width(); + default: + return region->flow(FlowRole::frPerimeter).width(); + } +} + +// Accumulator of current extruion path properties +// It remembers unsuported distance and maximum accumulated curvature over that distance. +// Used to determine local stability issues (too long bridges, extrusion curves into air) +struct ExtrusionPropertiesAccumulator { + float distance = 0; //accumulated distance + float curvature = 0; //accumulated signed ccw angles + float max_curvature = 0; //max absolute accumulated value + + void add_distance(float dist) { + distance += dist; + } + + void add_angle(float ccw_angle) { + curvature += ccw_angle; + max_curvature = std::max(max_curvature, std::abs(curvature)); + } + + void reset() { + distance = 0; + curvature = 0; + max_curvature = 0; + } +}; + +void check_extrusion_entity_stability(const ExtrusionEntity *entity, + std::vector &checked_lines_out, + float print_z, + const LayerRegion *layer_region, + const LinesDistancer &prev_layer_lines, + Issues &issues, + const Params ¶ms) { + + if (entity->is_collection()) { + for (const auto *e : static_cast(entity)->entities) { + check_extrusion_entity_stability(e, checked_lines_out, print_z, layer_region, prev_layer_lines, + issues, params); + } + } else { //single extrusion path, with possible varying parameters + const auto to_vec3f = [print_z](const Vec2f &point) { + return Vec3f(point.x(), point.y(), print_z); + }; + Points points { }; + entity->collect_points(points); + std::vector lines; + lines.reserve(points.size() * 1.5); + bool is_ex_perimeter = entity->role() == erExternalPerimeter; + lines.emplace_back(unscaled(points[0]).cast(), unscaled(points[0]).cast(), is_ex_perimeter); + for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { + Vec2f start = unscaled(points[point_idx]).cast(); + Vec2f next = unscaled(points[point_idx + 1]).cast(); + Vec2f v = next - start; // vector from next to current + float dist_to_next = v.norm(); + v.normalize(); + int lines_count = int(std::ceil(dist_to_next / params.bridge_distance)); + float step_size = dist_to_next / lines_count; + for (int i = 0; i < lines_count; ++i) { + Vec2f a(start + v * (i * step_size)); + Vec2f b(start + v * ((i + 1) * step_size)); + lines.emplace_back(a, b, is_ex_perimeter); + } + } + + ExtrusionPropertiesAccumulator bridging_acc { }; + ExtrusionPropertiesAccumulator malformation_acc { }; + bridging_acc.add_distance(params.bridge_distance + 1.0f); // Initialise unsupported distance with larger than tolerable distance -> + // -> it prevents extruding perimeter starts and short loops into air. + const float flow_width = get_flow_width(layer_region, entity->role()); + + for (size_t line_idx = 0; line_idx < lines.size(); ++line_idx) { + ExtrusionLine ¤t_line = lines[line_idx]; + float curr_angle = 0; + if (line_idx + 1 < lines.size()) { + const Vec2f v1 = current_line.b - current_line.a; + const Vec2f v2 = lines[line_idx + 1].b - lines[line_idx + 1].a; + curr_angle = angle(v1, v2); + } + bridging_acc.add_angle(curr_angle); + malformation_acc.add_angle(std::max(0.0f, curr_angle)); + + size_t nearest_line_idx; + Vec2f nearest_point; + float dist_from_prev_layer = prev_layer_lines.signed_distance_from_lines(current_line.b, nearest_line_idx, + nearest_point); + + if (dist_from_prev_layer < flow_width) { + bridging_acc.reset(); + } else { + bridging_acc.add_distance(current_line.len); + if (bridging_acc.distance // if unsupported distance is larger than bridge distance linearly decreased by curvature, enforce supports. + > params.bridge_distance + / (1.0f + (bridging_acc.max_curvature + * params.bridge_distance_decrease_by_curvature_factor / PI))) { + issues.support_points.emplace_back(to_vec3f(current_line.b), 0.0f, Vec3f(0.f, 0.0f, -1.0f)); + current_line.support_point_generated = true; + bridging_acc.reset(); + } + } + + //malformation + if (fabs(dist_from_prev_layer) < flow_width * 2.0f) { + const ExtrusionLine &nearest_line = prev_layer_lines.get_line(nearest_line_idx); + current_line.malformation += 0.9 * nearest_line.malformation; + } + if (dist_from_prev_layer > flow_width * 0.3) { + malformation_acc.add_distance(current_line.len); + current_line.malformation += 0.15 + * (0.8 + 0.2 * malformation_acc.max_curvature / (1.0f + 0.5f * malformation_acc.distance)); + } else { + malformation_acc.reset(); + } + } + checked_lines_out.insert(checked_lines_out.end(), lines.begin(), lines.end()); + } +} + +std::tuple reckon_islands( + const Layer *layer, bool first_layer, + size_t prev_layer_islands_count, + const PixelGrid &prev_layer_grid, + const std::vector &layer_lines, + const Params ¶ms) { + + BOOST_LOG_TRIVIAL(debug) << "SSG: reckon islands on printz: " << layer->print_z; + + std::vector> extrusions; //start and end idx (one beyond last extrusion) [start,end) + Vec2f current_pt = layer_lines[0].a; + std::pair current_ext(0, 1); + for (size_t lidx = 0; lidx < layer_lines.size(); ++lidx) { + const ExtrusionLine &line = layer_lines[lidx]; + if (line.a == current_pt) { + current_ext.second = lidx + 1; + } else { + extrusions.push_back(current_ext); + current_ext.first = lidx; + current_ext.second = lidx + 1; + } + current_pt = line.b; + } + + BOOST_LOG_TRIVIAL(debug) << "SSG: layer_lines size: " << layer_lines.size(); + + std::vector islands; + std::vector> island_extrusions; + for (size_t e = 0; e < extrusions.size(); ++e) { + if (layer_lines[extrusions[e].first].external_perimeter) { + std::vector copy(extrusions[e].second - extrusions[e].first); + for (size_t ex_line_idx = extrusions[e].first; ex_line_idx < extrusions[e].second; ++ex_line_idx) { + copy[ex_line_idx - extrusions[e].first] = layer_lines[ex_line_idx]; + } + islands.emplace_back(copy); + island_extrusions.push_back( { e }); + } + } + + BOOST_LOG_TRIVIAL(debug) << "SSG: external perims: " << islands.size(); + + for (size_t i = 0; i < islands.size(); ++i) { + for (size_t e = 0; e < extrusions.size(); ++e) { + if (!layer_lines[extrusions[e].first].external_perimeter) { + size_t _idx; + Vec2f _pt; + if (islands[i].signed_distance_from_lines(layer_lines[extrusions[e].first].a, _idx, _pt) < 0) { + island_extrusions[i].push_back(e); + } + } + } + } + + for (size_t i = 0; i < islands.size(); ++i) { + if (islands[i].get_lines().empty()) { + continue; + } + for (size_t j = 0; j < islands.size(); ++j) { + if (islands[j].get_lines().empty() || i == j) { + continue; + } + size_t _idx; + Vec2f _pt; + if (islands[i].signed_distance_from_lines(islands[j].get_line(0).a, _idx, _pt) < 0) { + island_extrusions[i].insert(island_extrusions[i].end(), island_extrusions[j].begin(), + island_extrusions[j].end()); + island_extrusions[j].clear(); + } + } + } + + BOOST_LOG_TRIVIAL(debug) << "SSG: filter islands"; + + float flow_width = get_flow_width(layer->regions()[0], erExternalPerimeter); + + LayerIslands result { }; + std::vector line_to_island_mapping(layer_lines.size(), NULL_ISLAND); + for (const std::vector &island_ex : island_extrusions) { + if (island_ex.empty()) { + continue; + } + + Island island { }; + for (size_t extrusion_idx : island_ex) { + for (size_t lidx = extrusions[extrusion_idx].first; lidx < extrusions[extrusion_idx].second; ++lidx) { + line_to_island_mapping[lidx] = result.islands.size(); + const ExtrusionLine &line = layer_lines[lidx]; + float volume = line.len * flow_width * layer->height * 0.7; // 1/sqrt(2) compensation for cylindrical shape + island.volume += volume; + island.volume_centroid += to_3d(Vec2f((line.a + line.b) / 2.0f), float(layer->print_z)) * volume; + + if (first_layer) { + float sticking_force = line.len * flow_width * params.base_adhesion; + island.sticking_force += sticking_force; + island.sticking_centroid += sticking_force + * to_3d(Vec2f((line.a + line.b) / 2.0f), float(layer->print_z)); + if (line.external_perimeter) { + island.pivot_points.push_back(to_3d(Vec2f(line.b), float(layer->print_z))); + } + } else if (layer_lines[lidx].support_point_generated) { + float support_interface_area = params.support_points_interface_radius + * params.support_points_interface_radius + * float(PI); + float sticking_force = support_interface_area * params.support_adhesion; + island.sticking_force += sticking_force; + island.sticking_centroid += sticking_force * to_3d(Vec2f(line.b), float(layer->print_z)); + island.pivot_points.push_back(to_3d(Vec2f(line.b), float(layer->print_z))); + } + } + } + result.islands.push_back(island); + } + + BOOST_LOG_TRIVIAL(debug) + << "SSG: There are " << result.islands.size() << " islands on printz: " << layer->print_z; + + PixelGrid current_layer_grid = prev_layer_grid; + current_layer_grid.clear(); + + tbb::parallel_for(tbb::blocked_range(0, layer_lines.size()), + [&layer_lines, ¤t_layer_grid, &line_to_island_mapping]( + tbb::blocked_range r) { + for (size_t i = r.begin(); i < r.end(); ++i) { + size_t island = line_to_island_mapping[i]; + const ExtrusionLine &line = layer_lines[i]; + current_layer_grid.distribute_edge(line.a, line.b, island); + } + }); + + BOOST_LOG_TRIVIAL(debug) << "SSG: rasterized"; + + for (size_t x = 0; x < size_t(current_layer_grid.get_pixel_count().x()); ++x) { + for (size_t y = 0; y < size_t(current_layer_grid.get_pixel_count().y()); ++y) { + Vec2i coords = Vec2i(x, y); + if (current_layer_grid.get_pixel(coords) != NULL_ISLAND + && prev_layer_grid.get_pixel(coords) != NULL_ISLAND) { + result.islands[current_layer_grid.get_pixel(coords)].islands_under_with_connection_area[prev_layer_grid.get_pixel(coords)] += + current_layer_grid.pixel_area(); + } + } + } + + BOOST_LOG_TRIVIAL(debug) << "SSG: connection area computed"; + + return {result, current_layer_grid}; +} + +Issues check_object_stability(const PrintObject *po, const Params ¶ms) { + Issues issues { }; + std::vector layer_lines; + float flow_width = get_flow_width(po->layers()[po->layer_count()-1]->regions()[0], erExternalPerimeter); + PixelGrid prev_layer_grid(po, flow_width); + BOOST_LOG_TRIVIAL(debug) << "SSG: flow width: " << flow_width; + + // PREPARE BASE LAYER + const Layer *layer = po->layers()[0]; + for (const LayerRegion *layer_region : layer->regions()) { + for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { + for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { + Points points { }; + perimeter->collect_points(points); + for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { + Vec2f start = unscaled(points[point_idx]).cast(); + Vec2f next = unscaled(points[point_idx + 1]).cast(); + ExtrusionLine line { start, next, perimeter->role() == erExternalPerimeter }; + layer_lines.push_back(line); + } + if (perimeter->is_loop()) { + Vec2f start = unscaled(points[points.size() - 1]).cast(); + Vec2f next = unscaled(points[0]).cast(); + ExtrusionLine line { start, next, perimeter->role() == erExternalPerimeter }; + layer_lines.push_back(line); + } + } // perimeter + } // ex_entity + for (const ExtrusionEntity *ex_entity : layer_region->fills.entities) { + for (const ExtrusionEntity *fill : static_cast(ex_entity)->entities) { + Points points { }; + fill->collect_points(points); + for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { + Vec2f start = unscaled(points[point_idx]).cast(); + Vec2f next = unscaled(points[point_idx + 1]).cast(); + ExtrusionLine line { start, next, false }; + layer_lines.push_back(line); + } + } // fill + } // ex_entity + } // region + + auto [layer_islands, layer_grid] = reckon_islands(layer, true, 0, prev_layer_grid, layer_lines, params); + std::remove_if(layer_lines.begin(), layer_lines.end(), [](const ExtrusionLine &line) { + return !line.external_perimeter; + }); + LinesDistancer external_lines(layer_lines); + layer_lines.clear(); + prev_layer_grid = layer_grid; + + for (size_t layer_idx = 1; layer_idx < po->layer_count(); ++layer_idx) { + const Layer *layer = po->layers()[layer_idx]; + for (const LayerRegion *layer_region : layer->regions()) { + for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { + for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { + check_extrusion_entity_stability(perimeter, layer_lines, layer->print_z, layer_region, + external_lines, issues, params); + } // perimeter + } // ex_entity + for (const ExtrusionEntity *ex_entity : layer_region->fills.entities) { + for (const ExtrusionEntity *fill : static_cast(ex_entity)->entities) { + if (fill->role() == ExtrusionRole::erGapFill + || fill->role() == ExtrusionRole::erBridgeInfill) { + check_extrusion_entity_stability(fill, layer_lines, layer->print_z, layer_region, + external_lines, issues, params); + } else { + Points points { }; + fill->collect_points(points); + for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { + Vec2f start = unscaled(points[point_idx]).cast(); + Vec2f next = unscaled(points[point_idx + 1]).cast(); + ExtrusionLine line { start, next, false }; + layer_lines.push_back(line); + } + } + } // fill + } // ex_entity + } // region + + auto [layer_islands, layer_grid] = reckon_islands(layer, true, 0, prev_layer_grid, layer_lines, params); + std::remove_if(layer_lines.begin(), layer_lines.end(), [](const ExtrusionLine &line) { + return !line.external_perimeter; + }); + layer_lines = std::vector(); + LinesDistancer external_lines(layer_lines); + layer_lines.clear(); + prev_layer_grid = layer_grid; + } + + return issues; +} + +#ifdef DEBUG_FILES +void debug_export(Issues issues, std::string file_name) { + Slic3r::CNumericLocalesSetter locales_setter; + { + FILE *fp = boost::nowide::fopen(debug_out_path((file_name + "_supports.obj").c_str()).c_str(), "w"); + if (fp == nullptr) { + BOOST_LOG_TRIVIAL(error) + << "Debug files: Couldn't open " << file_name << " for writing"; + return; + } + + for (size_t i = 0; i < issues.support_points.size(); ++i) { + fprintf(fp, "v %f %f %f %f %f %f\n", issues.support_points[i].position(0), + issues.support_points[i].position(1), + issues.support_points[i].position(2), 1.0, 0.0, 1.0); + } + + fclose(fp); + } +} +#endif + +std::vector quick_search(const PrintObject *po, const Params ¶ms) { + check_object_stability(po, params); + return {}; +} + +Issues +full_search(const PrintObject *po, const Params ¶ms) { + auto issues = check_object_stability(po, params); +#ifdef DEBUG_FILES + debug_export(issues, "issues"); +#endif + return issues; + +} +} //SupportableIssues End +} + From 619309a1a4228255b5c2aed069a61193af506f8b Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Fri, 1 Jul 2022 09:26:42 +0200 Subject: [PATCH 41/78] bug fix - external extrusions were cleaned out before use --- src/libslic3r/SupportSpotsGeneratorRefactoring.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/SupportSpotsGeneratorRefactoring.cpp b/src/libslic3r/SupportSpotsGeneratorRefactoring.cpp index 1fe3b6b2ab..bbc4efb18e 100644 --- a/src/libslic3r/SupportSpotsGeneratorRefactoring.cpp +++ b/src/libslic3r/SupportSpotsGeneratorRefactoring.cpp @@ -562,8 +562,7 @@ Issues check_object_stability(const PrintObject *po, const Params ¶ms) { std::remove_if(layer_lines.begin(), layer_lines.end(), [](const ExtrusionLine &line) { return !line.external_perimeter; }); - layer_lines = std::vector(); - LinesDistancer external_lines(layer_lines); + external_lines = LinesDistancer(layer_lines); layer_lines.clear(); prev_layer_grid = layer_grid; } @@ -605,8 +604,8 @@ full_search(const PrintObject *po, const Params ¶ms) { debug_export(issues, "issues"); #endif return issues; - } + } //SupportableIssues End } From 7743bf250216287c1234975d8f448a578349fd1e Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Fri, 1 Jul 2022 11:51:04 +0200 Subject: [PATCH 42/78] store pointer to original extrusion in each line --- .../SupportSpotsGeneratorRefactoring.cpp | 56 ++++++++++--------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/src/libslic3r/SupportSpotsGeneratorRefactoring.cpp b/src/libslic3r/SupportSpotsGeneratorRefactoring.cpp index bbc4efb18e..86b8eacd4d 100644 --- a/src/libslic3r/SupportSpotsGeneratorRefactoring.cpp +++ b/src/libslic3r/SupportSpotsGeneratorRefactoring.cpp @@ -28,20 +28,25 @@ class ExtrusionLine { public: ExtrusionLine() : - a(Vec2f::Zero()), b(Vec2f::Zero()), len(0.0f), external_perimeter(false) { + a(Vec2f::Zero()), b(Vec2f::Zero()), len(0.0f), origin_entity(nullptr) { } - ExtrusionLine(const Vec2f &_a, const Vec2f &_b, bool external_perimeter) : - a(_a), b(_b), len((_a - _b).norm()), external_perimeter(external_perimeter) { + ExtrusionLine(const Vec2f &_a, const Vec2f &_b, const ExtrusionEntity* origin_entity) : + a(_a), b(_b), len((_a - _b).norm()), origin_entity(origin_entity) { } float length() { return (a - b).norm(); } + bool is_external_perimeter() const { + assert(origin_entity != nullptr); + return origin_entity->role() == erExternalPerimeter; + } + Vec2f a; Vec2f b; float len; - bool external_perimeter; + const ExtrusionEntity* origin_entity; bool support_point_generated = false; float malformation = 0.0f; @@ -262,8 +267,7 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, entity->collect_points(points); std::vector lines; lines.reserve(points.size() * 1.5); - bool is_ex_perimeter = entity->role() == erExternalPerimeter; - lines.emplace_back(unscaled(points[0]).cast(), unscaled(points[0]).cast(), is_ex_perimeter); + lines.emplace_back(unscaled(points[0]).cast(), unscaled(points[0]).cast(), entity); for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { Vec2f start = unscaled(points[point_idx]).cast(); Vec2f next = unscaled(points[point_idx + 1]).cast(); @@ -275,7 +279,7 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, for (int i = 0; i < lines_count; ++i) { Vec2f a(start + v * (i * step_size)); Vec2f b(start + v * ((i + 1) * step_size)); - lines.emplace_back(a, b, is_ex_perimeter); + lines.emplace_back(a, b, entity); } } @@ -342,26 +346,28 @@ std::tuple reckon_islands( BOOST_LOG_TRIVIAL(debug) << "SSG: reckon islands on printz: " << layer->print_z; std::vector> extrusions; //start and end idx (one beyond last extrusion) [start,end) - Vec2f current_pt = layer_lines[0].a; - std::pair current_ext(0, 1); - for (size_t lidx = 0; lidx < layer_lines.size(); ++lidx) { + const ExtrusionEntity* current_ex = nullptr; + for (size_t lidx = 1; lidx < layer_lines.size(); ++lidx) { const ExtrusionLine &line = layer_lines[lidx]; - if (line.a == current_pt) { - current_ext.second = lidx + 1; + if (line.origin_entity == current_ex) { + extrusions.back().second = lidx + 1; } else { - extrusions.push_back(current_ext); - current_ext.first = lidx; - current_ext.second = lidx + 1; + extrusions.emplace_back(lidx, lidx + 1); + current_ex = line.origin_entity; } - current_pt = line.b; } BOOST_LOG_TRIVIAL(debug) << "SSG: layer_lines size: " << layer_lines.size(); + BOOST_LOG_TRIVIAL(debug) << "SSG: extrusions count: " << extrusions.size(); + BOOST_LOG_TRIVIAL(debug) << "SSG: extrusions sizes: "; + for (const auto& ext: extrusions) { + BOOST_LOG_TRIVIAL(debug) << "SSG: " << ext.second - ext.first; + } std::vector islands; std::vector> island_extrusions; for (size_t e = 0; e < extrusions.size(); ++e) { - if (layer_lines[extrusions[e].first].external_perimeter) { + if (layer_lines[extrusions[e].first].is_external_perimeter()) { std::vector copy(extrusions[e].second - extrusions[e].first); for (size_t ex_line_idx = extrusions[e].first; ex_line_idx < extrusions[e].second; ++ex_line_idx) { copy[ex_line_idx - extrusions[e].first] = layer_lines[ex_line_idx]; @@ -375,7 +381,7 @@ std::tuple reckon_islands( for (size_t i = 0; i < islands.size(); ++i) { for (size_t e = 0; e < extrusions.size(); ++e) { - if (!layer_lines[extrusions[e].first].external_perimeter) { + if (!layer_lines[extrusions[e].first].is_external_perimeter()) { size_t _idx; Vec2f _pt; if (islands[i].signed_distance_from_lines(layer_lines[extrusions[e].first].a, _idx, _pt) < 0) { @@ -428,7 +434,7 @@ std::tuple reckon_islands( island.sticking_force += sticking_force; island.sticking_centroid += sticking_force * to_3d(Vec2f((line.a + line.b) / 2.0f), float(layer->print_z)); - if (line.external_perimeter) { + if (line.is_external_perimeter()) { island.pivot_points.push_back(to_3d(Vec2f(line.b), float(layer->print_z))); } } else if (layer_lines[lidx].support_point_generated) { @@ -496,13 +502,13 @@ Issues check_object_stability(const PrintObject *po, const Params ¶ms) { for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { Vec2f start = unscaled(points[point_idx]).cast(); Vec2f next = unscaled(points[point_idx + 1]).cast(); - ExtrusionLine line { start, next, perimeter->role() == erExternalPerimeter }; + ExtrusionLine line { start, next, perimeter }; layer_lines.push_back(line); } if (perimeter->is_loop()) { Vec2f start = unscaled(points[points.size() - 1]).cast(); Vec2f next = unscaled(points[0]).cast(); - ExtrusionLine line { start, next, perimeter->role() == erExternalPerimeter }; + ExtrusionLine line { start, next, perimeter }; layer_lines.push_back(line); } } // perimeter @@ -514,7 +520,7 @@ Issues check_object_stability(const PrintObject *po, const Params ¶ms) { for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { Vec2f start = unscaled(points[point_idx]).cast(); Vec2f next = unscaled(points[point_idx + 1]).cast(); - ExtrusionLine line { start, next, false }; + ExtrusionLine line { start, next, fill }; layer_lines.push_back(line); } } // fill @@ -523,7 +529,7 @@ Issues check_object_stability(const PrintObject *po, const Params ¶ms) { auto [layer_islands, layer_grid] = reckon_islands(layer, true, 0, prev_layer_grid, layer_lines, params); std::remove_if(layer_lines.begin(), layer_lines.end(), [](const ExtrusionLine &line) { - return !line.external_perimeter; + return !line.is_external_perimeter(); }); LinesDistancer external_lines(layer_lines); layer_lines.clear(); @@ -550,7 +556,7 @@ Issues check_object_stability(const PrintObject *po, const Params ¶ms) { for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { Vec2f start = unscaled(points[point_idx]).cast(); Vec2f next = unscaled(points[point_idx + 1]).cast(); - ExtrusionLine line { start, next, false }; + ExtrusionLine line { start, next, fill }; layer_lines.push_back(line); } } @@ -560,7 +566,7 @@ Issues check_object_stability(const PrintObject *po, const Params ¶ms) { auto [layer_islands, layer_grid] = reckon_islands(layer, true, 0, prev_layer_grid, layer_lines, params); std::remove_if(layer_lines.begin(), layer_lines.end(), [](const ExtrusionLine &line) { - return !line.external_perimeter; + return !line.is_external_perimeter(); }); external_lines = LinesDistancer(layer_lines); layer_lines.clear(); From 3e47b19b8614d68f9232c1f80c8dbbca6aece1fc Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Fri, 1 Jul 2022 16:13:00 +0200 Subject: [PATCH 43/78] added computation of stability accors the object graph, but not finished yet --- .../SupportSpotsGeneratorRefactoring.cpp | 258 +++++++++++++++--- src/libslic3r/TriangleSelectorWrapper.cpp | 16 +- 2 files changed, 236 insertions(+), 38 deletions(-) diff --git a/src/libslic3r/SupportSpotsGeneratorRefactoring.cpp b/src/libslic3r/SupportSpotsGeneratorRefactoring.cpp index 86b8eacd4d..23b249219a 100644 --- a/src/libslic3r/SupportSpotsGeneratorRefactoring.cpp +++ b/src/libslic3r/SupportSpotsGeneratorRefactoring.cpp @@ -30,7 +30,7 @@ public: ExtrusionLine() : a(Vec2f::Zero()), b(Vec2f::Zero()), len(0.0f), origin_entity(nullptr) { } - ExtrusionLine(const Vec2f &_a, const Vec2f &_b, const ExtrusionEntity* origin_entity) : + ExtrusionLine(const Vec2f &_a, const Vec2f &_b, const ExtrusionEntity *origin_entity) : a(_a), b(_b), len((_a - _b).norm()), origin_entity(origin_entity) { } @@ -46,7 +46,7 @@ public: Vec2f a; Vec2f b; float len; - const ExtrusionEntity* origin_entity; + const ExtrusionEntity *origin_entity; bool support_point_generated = false; float malformation = 0.0f; @@ -168,6 +168,11 @@ public: return pixel_count; } + Vec2f get_pixel_center(const Vec2i &coords) const { + return origin + coords.cast().cwiseProduct(this->pixel_size) + + this->pixel_size.cwiseQuotient(Vec2f(2.0f, 2.0f)); + } + private: Vec2i to_pixel_coords(const Vec2f &position) const { Vec2i pixel_coords = (position - this->origin).cwiseQuotient(this->pixel_size).cast(); @@ -192,9 +197,9 @@ struct Island { std::unordered_map islands_under_with_connection_area; std::vector pivot_points; float volume; - Vec3f volume_centroid; + Vec3f volume_centroid_accumulator; float sticking_force; // for support points present on this layer (or bed extrusions) - Vec3f sticking_centroid; + Vec3f sticking_centroid_accumulator; }; struct LayerIslands { @@ -305,7 +310,7 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, float dist_from_prev_layer = prev_layer_lines.signed_distance_from_lines(current_line.b, nearest_line_idx, nearest_point); - if (dist_from_prev_layer < flow_width) { + if (fabs(dist_from_prev_layer) < flow_width) { bridging_acc.reset(); } else { bridging_acc.add_distance(current_line.len); @@ -336,17 +341,18 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, } } -std::tuple reckon_islands( +std::tuple> reckon_islands( const Layer *layer, bool first_layer, size_t prev_layer_islands_count, const PixelGrid &prev_layer_grid, const std::vector &layer_lines, const Params ¶ms) { - BOOST_LOG_TRIVIAL(debug) << "SSG: reckon islands on printz: " << layer->print_z; + BOOST_LOG_TRIVIAL(debug) + << "SSG: reckon islands on printz: " << layer->print_z; std::vector> extrusions; //start and end idx (one beyond last extrusion) [start,end) - const ExtrusionEntity* current_ex = nullptr; + const ExtrusionEntity *current_ex = nullptr; for (size_t lidx = 1; lidx < layer_lines.size(); ++lidx) { const ExtrusionLine &line = layer_lines[lidx]; if (line.origin_entity == current_ex) { @@ -357,11 +363,15 @@ std::tuple reckon_islands( } } - BOOST_LOG_TRIVIAL(debug) << "SSG: layer_lines size: " << layer_lines.size(); - BOOST_LOG_TRIVIAL(debug) << "SSG: extrusions count: " << extrusions.size(); - BOOST_LOG_TRIVIAL(debug) << "SSG: extrusions sizes: "; - for (const auto& ext: extrusions) { - BOOST_LOG_TRIVIAL(debug) << "SSG: " << ext.second - ext.first; + BOOST_LOG_TRIVIAL(debug) + << "SSG: layer_lines size: " << layer_lines.size(); + BOOST_LOG_TRIVIAL(debug) + << "SSG: extrusions count: " << extrusions.size(); + BOOST_LOG_TRIVIAL(debug) + << "SSG: extrusions sizes: "; + for (const auto &ext : extrusions) { + BOOST_LOG_TRIVIAL(debug) + << "SSG: " << ext.second - ext.first; } std::vector islands; @@ -377,7 +387,8 @@ std::tuple reckon_islands( } } - BOOST_LOG_TRIVIAL(debug) << "SSG: external perims: " << islands.size(); + BOOST_LOG_TRIVIAL(debug) + << "SSG: external perims: " << islands.size(); for (size_t i = 0; i < islands.size(); ++i) { for (size_t e = 0; e < extrusions.size(); ++e) { @@ -409,7 +420,8 @@ std::tuple reckon_islands( } } - BOOST_LOG_TRIVIAL(debug) << "SSG: filter islands"; + BOOST_LOG_TRIVIAL(debug) + << "SSG: filter islands"; float flow_width = get_flow_width(layer->regions()[0], erExternalPerimeter); @@ -427,12 +439,12 @@ std::tuple reckon_islands( const ExtrusionLine &line = layer_lines[lidx]; float volume = line.len * flow_width * layer->height * 0.7; // 1/sqrt(2) compensation for cylindrical shape island.volume += volume; - island.volume_centroid += to_3d(Vec2f((line.a + line.b) / 2.0f), float(layer->print_z)) * volume; + island.volume_centroid_accumulator += to_3d(Vec2f((line.a + line.b) / 2.0f), float(layer->print_z)) * volume; if (first_layer) { float sticking_force = line.len * flow_width * params.base_adhesion; island.sticking_force += sticking_force; - island.sticking_centroid += sticking_force + island.sticking_centroid_accumulator += sticking_force * to_3d(Vec2f((line.a + line.b) / 2.0f), float(layer->print_z)); if (line.is_external_perimeter()) { island.pivot_points.push_back(to_3d(Vec2f(line.b), float(layer->print_z))); @@ -443,7 +455,7 @@ std::tuple reckon_islands( * float(PI); float sticking_force = support_interface_area * params.support_adhesion; island.sticking_force += sticking_force; - island.sticking_centroid += sticking_force * to_3d(Vec2f(line.b), float(layer->print_z)); + island.sticking_centroid_accumulator += sticking_force * to_3d(Vec2f(line.b), float(layer->print_z)); island.pivot_points.push_back(to_3d(Vec2f(line.b), float(layer->print_z))); } } @@ -467,30 +479,165 @@ std::tuple reckon_islands( } }); - BOOST_LOG_TRIVIAL(debug) << "SSG: rasterized"; + BOOST_LOG_TRIVIAL(debug) + << "SSG: rasterized"; for (size_t x = 0; x < size_t(current_layer_grid.get_pixel_count().x()); ++x) { for (size_t y = 0; y < size_t(current_layer_grid.get_pixel_count().y()); ++y) { Vec2i coords = Vec2i(x, y); if (current_layer_grid.get_pixel(coords) != NULL_ISLAND && prev_layer_grid.get_pixel(coords) != NULL_ISLAND) { - result.islands[current_layer_grid.get_pixel(coords)].islands_under_with_connection_area[prev_layer_grid.get_pixel(coords)] += + result.islands[current_layer_grid.get_pixel(coords)].islands_under_with_connection_area[prev_layer_grid.get_pixel( + coords)] += current_layer_grid.pixel_area(); } } } - BOOST_LOG_TRIVIAL(debug) << "SSG: connection area computed"; + BOOST_LOG_TRIVIAL(debug) + << "SSG: connection area computed"; - return {result, current_layer_grid}; + return {result, current_layer_grid, line_to_island_mapping}; +} + +void check_global_stability( + float print_z, + std::vector& islands_graph, + const std::vector &layer_lines, + const std::vector &line_to_island_mapping, + Issues& issues, + const Params& params + ) { + std::vector> islands_lines(islands_graph.back().islands.size()); + for (int lidx = 0; lidx < layer_lines.size(); ++lidx) { + if (layer_lines[lidx].origin_entity->role() == erExternalPerimeter) { + islands_lines[line_to_island_mapping[lidx]].push_back(lidx); + } + } + + using Accumulator = Island; + + for (size_t island_idx = 0; island_idx < islands_graph.back().islands.size(); ++island_idx) { + Island& island = islands_graph.back().islands[island_idx]; + + std::vector island_external_lines; + for (size_t lidx : islands_lines[island_idx]) { + island_external_lines.push_back(layer_lines[lidx]); + } + LinesDistancer island_lines_dist(island_external_lines); + + Accumulator acc = island; + int layer_idx = islands_graph.size() -1; + while (acc.islands_under_with_connection_area.size() > 0) { + //TEST for break between layer_idx and layer_idx -1; + LayerIslands below = islands_graph[layer_idx-1]; + std::vector pivot_points; + Vec2f sticking_centroid; + float connection_area = 0; + for (const auto& pair : acc.islands_under_with_connection_area) { + const Island& below_i = below.islands[pair.first]; + Vec2f centroid = (below_i.volume_centroid_accumulator / below_i.volume).head<2>(); + pivot_points.push_back(centroid); + sticking_centroid += centroid * pair.second; + connection_area += pair.second; + } + + sticking_centroid /= connection_area; + + auto coord_fn = [&pivot_points](size_t idx, size_t dim) { + return pivot_points[idx][dim]; + }; + KDTreeIndirect<2, float, decltype(coord_fn)> supports_tree(coord_fn, pivot_points.size()); + + for (const ExtrusionLine& line : island_external_lines){ + Vec2f line_dir = (line.b - line.a).normalized(); + Vec2f pivot_site_search_point = line.b + line_dir * 300.0f; + size_t pivot_idx = find_closest_point(supports_tree, pivot_site_search_point); + const Vec2f &pivot = pivot_points[pivot_idx]; + + float sticking_arm = (pivot - sticking_centroid).norm(); + float sticking_torque = sticking_arm * connection_area * params.tensile_strength; + + float mass = acc.volume * params.filament_density; + const Vec3f &mass_centorid = acc.volume_centroid_accumulator / acc.volume; + float weight = mass * params.gravity_constant; + float weight_arm = (pivot - mass_centorid.head<2>()).norm(); + float weight_torque = weight_arm * weight; + + float bed_movement_arm = mass_centorid.z(); + float bed_movement_force = params.max_acceleration * mass; + float bed_movement_torque = bed_movement_force * bed_movement_arm; + + Vec3f extruder_pressure_direction = to_3d(line_dir, 0.0f); + extruder_pressure_direction.z() = -0.2 - line.malformation * 0.5; + extruder_pressure_direction.normalize(); + float conflict_torque_arm = (to_3d(Vec2f(pivot - line.b), print_z).cross( + extruder_pressure_direction)).norm(); + float extruder_conflict_force = params.tolerable_extruder_conflict_force + + std::min(line.malformation, 1.0f) * params.malformations_additive_conflict_extruder_force; + float extruder_conflict_torque = extruder_conflict_force * conflict_torque_arm; + + float total_torque = bed_movement_torque + extruder_conflict_torque - weight_torque - sticking_torque; + + if (total_torque > 0) { + Vec2f target_point; + size_t _idx; + island_lines_dist.signed_distance_from_lines(pivot_site_search_point, _idx, target_point); +// if (!supports_presence_grid.position_taken(to_3d(target_point, print_z))) { + float area = params.support_points_interface_radius * params.support_points_interface_radius + * float(PI); + float sticking_force = area * params.support_adhesion; + Vec3f support_point = to_3d(target_point, print_z); + island.pivot_points.push_back(support_point); + island.sticking_force += sticking_force; + island.sticking_centroid_accumulator += sticking_force*support_point; + issues.support_points.emplace_back(support_point, + extruder_conflict_torque - sticking_torque, extruder_pressure_direction); +// supports_presence_grid.take_position(to_3d(target_point, print_z)); +// } + } + #if 0 + BOOST_LOG_TRIVIAL(debug) + << "SSG: sticking_arm: " << sticking_arm; + BOOST_LOG_TRIVIAL(debug) + << "SSG: sticking_torque: " << sticking_torque; + BOOST_LOG_TRIVIAL(debug) + << "SSG: weight_arm: " << sticking_arm; + BOOST_LOG_TRIVIAL(debug) + << "SSG: weight_torque: " << weight_torque; + BOOST_LOG_TRIVIAL(debug) + << "SSG: bed_movement_arm: " << bed_movement_arm; + BOOST_LOG_TRIVIAL(debug) + << "SSG: bed_movement_torque: " << bed_movement_torque; + BOOST_LOG_TRIVIAL(debug) + << "SSG: conflict_torque_arm: " << conflict_torque_arm; + BOOST_LOG_TRIVIAL(debug) + << "SSG: extruder_conflict_torque: " << extruder_conflict_torque; + BOOST_LOG_TRIVIAL(debug) + << "SSG: total_torque: " << total_torque << " printz: " << print_z; + #endif + } + + //TODO add stuf to accumulator + + + } + } } Issues check_object_stability(const PrintObject *po, const Params ¶ms) { +#ifdef DEBUG_FILES + FILE *segmentation_f = boost::nowide::fopen(debug_out_path("segmentation.obj").c_str(), "w"); + FILE *malform_f = boost::nowide::fopen(debug_out_path("malformations.obj").c_str(), "w"); +#endif + Issues issues { }; + std::vector islands_graph; std::vector layer_lines; - float flow_width = get_flow_width(po->layers()[po->layer_count()-1]->regions()[0], erExternalPerimeter); + float flow_width = get_flow_width(po->layers()[po->layer_count() - 1]->regions()[0], erExternalPerimeter); PixelGrid prev_layer_grid(po, flow_width); - BOOST_LOG_TRIVIAL(debug) << "SSG: flow width: " << flow_width; + BOOST_LOG_TRIVIAL(debug) + << "SSG: flow width: " << flow_width; // PREPARE BASE LAYER const Layer *layer = po->layers()[0]; @@ -527,10 +674,31 @@ Issues check_object_stability(const PrintObject *po, const Params ¶ms) { } // ex_entity } // region - auto [layer_islands, layer_grid] = reckon_islands(layer, true, 0, prev_layer_grid, layer_lines, params); - std::remove_if(layer_lines.begin(), layer_lines.end(), [](const ExtrusionLine &line) { - return !line.is_external_perimeter(); - }); + auto [layer_islands, layer_grid, line_to_island_mapping] = reckon_islands(layer, true, 0, prev_layer_grid, + layer_lines, params); + islands_graph.push_back(std::move(layer_islands)); +#ifdef DEBUG_FILES + for (size_t x = 0; x < size_t(layer_grid.get_pixel_count().x()); ++x) { + for (size_t y = 0; y < size_t(layer_grid.get_pixel_count().y()); ++y) { + Vec2i coords = Vec2i(x, y); + size_t island_idx = layer_grid.get_pixel(coords); + if (layer_grid.get_pixel(coords) != NULL_ISLAND) { + Vec2f pos = layer_grid.get_pixel_center(coords); + size_t pseudornd = ((island_idx + 127) * 33331 + 6907) % 23; + Vec3f color = value_to_rgbf(0.0f, float(23), float(pseudornd)); + fprintf(segmentation_f, "v %f %f %f %f %f %f\n", pos[0], + pos[1], layer->print_z, color[0], color[1], color[2]); + } + } + } + for (const auto &line : layer_lines) { + if (line.malformation > 0.0f) { + Vec3f color = value_to_rgbf(0, 1.0f, line.malformation); + fprintf(malform_f, "v %f %f %f %f %f %f\n", line.b[0], + line.b[1], layer->print_z, color[0], color[1], color[2]); + } + } +#endif LinesDistancer external_lines(layer_lines); layer_lines.clear(); prev_layer_grid = layer_grid; @@ -564,15 +732,41 @@ Issues check_object_stability(const PrintObject *po, const Params ¶ms) { } // ex_entity } // region - auto [layer_islands, layer_grid] = reckon_islands(layer, true, 0, prev_layer_grid, layer_lines, params); - std::remove_if(layer_lines.begin(), layer_lines.end(), [](const ExtrusionLine &line) { - return !line.is_external_perimeter(); - }); + auto [layer_islands, layer_grid, line_to_island_mapping] = reckon_islands(layer, true, 0, prev_layer_grid, + layer_lines, params); + islands_graph.push_back(std::move(layer_islands)); +#ifdef DEBUG_FILES + for (size_t x = 0; x < size_t(layer_grid.get_pixel_count().x()); ++x) { + for (size_t y = 0; y < size_t(layer_grid.get_pixel_count().y()); ++y) { + Vec2i coords = Vec2i(x, y); + size_t island_idx = layer_grid.get_pixel(coords); + if (layer_grid.get_pixel(coords) != NULL_ISLAND) { + Vec2f pos = layer_grid.get_pixel_center(coords); + size_t pseudornd = ((island_idx + 127) * 33331 + 6907) % 23; + Vec3f color = value_to_rgbf(0.0f, float(23), float(pseudornd)); + fprintf(segmentation_f, "v %f %f %f %f %f %f\n", pos[0], + pos[1], layer->print_z, color[0], color[1], color[2]); + } + } + } + for (const auto &line : layer_lines) { + if (line.malformation > 0.0f) { + Vec3f color = value_to_rgbf(0, 1.0f, line.malformation); + fprintf(malform_f, "v %f %f %f %f %f %f\n", line.b[0], + line.b[1], layer->print_z, color[0], color[1], color[2]); + } + } +#endif external_lines = LinesDistancer(layer_lines); layer_lines.clear(); prev_layer_grid = layer_grid; } +#ifdef DEBUG_FILES + fclose(segmentation_f); + fclose(malform_f); +#endif + return issues; } diff --git a/src/libslic3r/TriangleSelectorWrapper.cpp b/src/libslic3r/TriangleSelectorWrapper.cpp index c6040c721c..ec22ed5dd1 100644 --- a/src/libslic3r/TriangleSelectorWrapper.cpp +++ b/src/libslic3r/TriangleSelectorWrapper.cpp @@ -21,7 +21,7 @@ void TriangleSelectorWrapper::enforce_spot(const Vec3f &point, const Vec3f &orig const igl::Hit &hit = hits[hit_idx]; Vec3f pos = origin + dir * hit.t; Vec3f face_normal = its_face_normal(mesh.its, hit.id); - if (point.z() + radius > pos.z() && face_normal.dot(dir) < 0) { + if ((point - pos).norm() < radius && face_normal.dot(dir) < 0) { std::unique_ptr cursor = std::make_unique( pos, origin, radius, Transform3d::Identity(), TriangleSelector::ClippingPlane { }); selector.select_patch(hit.id, std::move(cursor), EnforcerBlockerType::ENFORCER, Transform3d::Identity(), @@ -32,11 +32,15 @@ void TriangleSelectorWrapper::enforce_spot(const Vec3f &point, const Vec3f &orig } else { size_t hit_idx_out; Vec3f hit_point_out; - AABBTreeIndirect::squared_distance_to_indexed_triangle_set(mesh.its.vertices, mesh.its.indices, triangles_tree, point, hit_idx_out, hit_point_out); - std::unique_ptr cursor = std::make_unique( - point, origin, radius, Transform3d::Identity(), TriangleSelector::ClippingPlane { }); - selector.select_patch(hit_idx_out, std::move(cursor), EnforcerBlockerType::ENFORCER, Transform3d::Identity(), - true, 0.0f); + float dist = AABBTreeIndirect::squared_distance_to_indexed_triangle_set(mesh.its.vertices, mesh.its.indices, + triangles_tree, point, hit_idx_out, hit_point_out); + if (dist < radius) { + std::unique_ptr cursor = std::make_unique( + point, origin, radius, Transform3d::Identity(), TriangleSelector::ClippingPlane { }); + selector.select_patch(hit_idx_out, std::move(cursor), EnforcerBlockerType::ENFORCER, + Transform3d::Identity(), + true, 0.0f); + } } } From f311ccbc4c90509762ecd178f80318a04cd224d8 Mon Sep 17 00:00:00 2001 From: Pavel Mikus Date: Sun, 17 Jul 2022 19:45:39 +0200 Subject: [PATCH 44/78] basic implementation should be complete, bugs not fixed, last iteration copied --- .../SupportSpotsGeneratorRefactoring.cpp | 144 +++++++++++++++--- 1 file changed, 126 insertions(+), 18 deletions(-) diff --git a/src/libslic3r/SupportSpotsGeneratorRefactoring.cpp b/src/libslic3r/SupportSpotsGeneratorRefactoring.cpp index 23b249219a..9b44594268 100644 --- a/src/libslic3r/SupportSpotsGeneratorRefactoring.cpp +++ b/src/libslic3r/SupportSpotsGeneratorRefactoring.cpp @@ -15,7 +15,7 @@ #include "libslic3r/ClipperUtils.hpp" #include "Geometry/ConvexHull.hpp" -#define DEBUG_FILES +//#define DEBUG_FILES #ifdef DEBUG_FILES #include @@ -508,8 +508,10 @@ void check_global_stability( Issues& issues, const Params& params ) { + // vector of islands, where each contains vector of line indices (to layer_lines vector) + // basically reverse of line_to_island_mapping std::vector> islands_lines(islands_graph.back().islands.size()); - for (int lidx = 0; lidx < layer_lines.size(); ++lidx) { + for (size_t lidx = 0; lidx < layer_lines.size(); ++lidx) { if (layer_lines[lidx].origin_entity->role() == erExternalPerimeter) { islands_lines[line_to_island_mapping[lidx]].push_back(lidx); } @@ -517,46 +519,56 @@ void check_global_stability( using Accumulator = Island; + // islands_graph.back() refers to the top most (currently) layer for (size_t island_idx = 0; island_idx < islands_graph.back().islands.size(); ++island_idx) { Island& island = islands_graph.back().islands[island_idx]; - std::vector island_external_lines; + std::vector island_external_lines; //TODO currently not external but all for (size_t lidx : islands_lines[island_idx]) { island_external_lines.push_back(layer_lines[lidx]); } LinesDistancer island_lines_dist(island_external_lines); - - Accumulator acc = island; + Accumulator acc = island; // in acc, we accumulate the mass and other properties of the object part as we traverse the islands down to bed + // There is one object part for each island at the top most layer, and each one is computed individually - + // Some of the calculations will be done mutliple times int layer_idx = islands_graph.size() -1; + // traverse the islands graph down, and for each connection area, calculate if it holds or breaks while (acc.islands_under_with_connection_area.size() > 0) { - //TEST for break between layer_idx and layer_idx -1; - LayerIslands below = islands_graph[layer_idx-1]; + //test for break between layer_idx and layer_idx -1; + LayerIslands below = islands_graph[layer_idx-1]; // must exist, see while condition + layer_idx--; + // initialize variables that we will accumulate over all islands, which are connected to the current object part std::vector pivot_points; Vec2f sticking_centroid; float connection_area = 0; for (const auto& pair : acc.islands_under_with_connection_area) { const Island& below_i = below.islands[pair.first]; - Vec2f centroid = (below_i.volume_centroid_accumulator / below_i.volume).head<2>(); - pivot_points.push_back(centroid); - sticking_centroid += centroid * pair.second; + Vec2f centroid = (below_i.volume_centroid_accumulator / below_i.volume).head<2>(); // centroid of the island 'below_i'; TODO it should be centroid of the connection area + pivot_points.push_back(centroid); // for object parts, we also consider breaking pivots in the centroids of the islands + sticking_centroid += centroid * pair.second; // pair.second is connection area in mm^2 connection_area += pair.second; } + sticking_centroid /= connection_area; //normalize to get final sticking centroid + for (const Vec3f& p_point: acc.pivot_points){ + pivot_points.push_back(p_point.head<2>()); + } + // Now we have accumulated pivot points, connection area and sticking centroid of the whole layer to the current object part - sticking_centroid /= connection_area; - + // create KD tree over current pivot points auto coord_fn = [&pivot_points](size_t idx, size_t dim) { return pivot_points[idx][dim]; }; - KDTreeIndirect<2, float, decltype(coord_fn)> supports_tree(coord_fn, pivot_points.size()); + KDTreeIndirect<2, float, decltype(coord_fn)> pivot_points_tree(coord_fn, pivot_points.size()); + // iterate over extrusions at top layer island, check each for stability for (const ExtrusionLine& line : island_external_lines){ Vec2f line_dir = (line.b - line.a).normalized(); Vec2f pivot_site_search_point = line.b + line_dir * 300.0f; - size_t pivot_idx = find_closest_point(supports_tree, pivot_site_search_point); + size_t pivot_idx = find_closest_point(pivot_points_tree, pivot_site_search_point); const Vec2f &pivot = pivot_points[pivot_idx]; float sticking_arm = (pivot - sticking_centroid).norm(); - float sticking_torque = sticking_arm * connection_area * params.tensile_strength; + float sticking_torque = sticking_arm * connection_area * params.tensile_strength; // For breakage in between layers, we compute with tensile strength, not bed adhesion float mass = acc.volume * params.filament_density; const Vec3f &mass_centorid = acc.volume_centroid_accumulator / acc.volume; @@ -618,10 +630,103 @@ void check_global_stability( #endif } - //TODO add stuf to accumulator - - + std::unordered_map tmp = acc.islands_under_with_connection_area; + acc.islands_under_with_connection_area.clear(); + // finally, add gathered islands to accumulator, and continue down to next layer + for (const auto& pair : tmp) { + const Island& below_i = below.islands[pair.first]; + for (const auto& below_islands : below_i.islands_under_with_connection_area) { + acc.islands_under_with_connection_area[below_islands.first] += below_islands.second; + } + for (const Vec3f& pivot_p : below_i.pivot_points) { + acc.pivot_points.push_back(pivot_p); + } + acc.sticking_centroid_accumulator += below_i.sticking_centroid_accumulator; + acc.sticking_force += below_i.sticking_force; + acc.volume += below_i.volume; + acc.volume_centroid_accumulator += below_i.volume_centroid_accumulator; + } } + + // We have arrived to the bed level, now check for stability of the object part on the bed + std::vector pivot_points; + for (const Vec3f& p_point: acc.pivot_points){ + pivot_points.push_back(p_point.head<2>()); + } + auto coord_fn = [&pivot_points](size_t idx, size_t dim) { + return pivot_points[idx][dim]; + }; + KDTreeIndirect<2, float, decltype(coord_fn)> pivot_points_tree(coord_fn, pivot_points.size()); + + for (const ExtrusionLine &line : island_external_lines) { + Vec2f line_dir = (line.b - line.a).normalized(); + Vec2f pivot_site_search_point = line.b + line_dir * 300.0f; + size_t pivot_idx = find_closest_point(pivot_points_tree, pivot_site_search_point); + const Vec2f &pivot = pivot_points[pivot_idx]; + + const Vec2f &sticking_centroid = acc.sticking_centroid_accumulator.head<2>() / acc.sticking_force; + float sticking_arm = (pivot - sticking_centroid).norm(); + float sticking_torque = sticking_arm * acc.sticking_force; + + float mass = acc.volume * params.filament_density; + const Vec3f &mass_centorid = acc.volume_centroid_accumulator / acc.volume; + float weight = mass * params.gravity_constant; + float weight_arm = (pivot - mass_centorid.head<2>()).norm(); + float weight_torque = weight_arm * weight; + + float bed_movement_arm = mass_centorid.z(); + float bed_movement_force = params.max_acceleration * mass; + float bed_movement_torque = bed_movement_force * bed_movement_arm; + + Vec3f extruder_pressure_direction = to_3d(line_dir, 0.0f); + extruder_pressure_direction.z() = -0.2 - line.malformation * 0.5; + extruder_pressure_direction.normalize(); + float conflict_torque_arm = (to_3d(Vec2f(pivot - line.b), print_z).cross( + extruder_pressure_direction)).norm(); + float extruder_conflict_force = params.tolerable_extruder_conflict_force + + std::min(line.malformation, 1.0f) * params.malformations_additive_conflict_extruder_force; + float extruder_conflict_torque = extruder_conflict_force * conflict_torque_arm; + + float total_torque = bed_movement_torque + extruder_conflict_torque - weight_torque - sticking_torque; + + if (total_torque > 0) { + Vec2f target_point; + size_t _idx; + island_lines_dist.signed_distance_from_lines(pivot_site_search_point, _idx, target_point); + // if (!supports_presence_grid.position_taken(to_3d(target_point, print_z))) { + float area = params.support_points_interface_radius * params.support_points_interface_radius + * float(PI); + float sticking_force = area * params.support_adhesion; + Vec3f support_point = to_3d(target_point, print_z); + island.pivot_points.push_back(support_point); + island.sticking_force += sticking_force; + island.sticking_centroid_accumulator += sticking_force*support_point; + issues.support_points.emplace_back(support_point, + extruder_conflict_torque - sticking_torque, extruder_pressure_direction); + // supports_presence_grid.take_position(to_3d(target_point, print_z)); + // } + } + #if 0 + BOOST_LOG_TRIVIAL(debug) + << "SSG: sticking_arm: " << sticking_arm; + BOOST_LOG_TRIVIAL(debug) + << "SSG: sticking_torque: " << sticking_torque; + BOOST_LOG_TRIVIAL(debug) + << "SSG: weight_arm: " << sticking_arm; + BOOST_LOG_TRIVIAL(debug) + << "SSG: weight_torque: " << weight_torque; + BOOST_LOG_TRIVIAL(debug) + << "SSG: bed_movement_arm: " << bed_movement_arm; + BOOST_LOG_TRIVIAL(debug) + << "SSG: bed_movement_torque: " << bed_movement_torque; + BOOST_LOG_TRIVIAL(debug) + << "SSG: conflict_torque_arm: " << conflict_torque_arm; + BOOST_LOG_TRIVIAL(debug) + << "SSG: extruder_conflict_torque: " << extruder_conflict_torque; + BOOST_LOG_TRIVIAL(debug) + << "SSG: total_torque: " << total_torque << " printz: " << print_z; + #endif + } } } @@ -735,6 +840,9 @@ Issues check_object_stability(const PrintObject *po, const Params ¶ms) { auto [layer_islands, layer_grid, line_to_island_mapping] = reckon_islands(layer, true, 0, prev_layer_grid, layer_lines, params); islands_graph.push_back(std::move(layer_islands)); + + check_global_stability(layer->print_z, islands_graph, layer_lines, line_to_island_mapping, issues, params); + #ifdef DEBUG_FILES for (size_t x = 0; x < size_t(layer_grid.get_pixel_count().x()); ++x) { for (size_t y = 0; y < size_t(layer_grid.get_pixel_count().y()); ++y) { From 1e4b56cc852eb42033b6b619ca9ea9b266c6702a Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Mon, 18 Jul 2022 16:46:10 +0200 Subject: [PATCH 45/78] fix crashing when extrusion is not assigned island Add voxel filter grid for supports padding --- .../SupportSpotsGeneratorRefactoring.cpp | 298 ++++++++++++------ 1 file changed, 196 insertions(+), 102 deletions(-) diff --git a/src/libslic3r/SupportSpotsGeneratorRefactoring.cpp b/src/libslic3r/SupportSpotsGeneratorRefactoring.cpp index 9b44594268..23d5698567 100644 --- a/src/libslic3r/SupportSpotsGeneratorRefactoring.cpp +++ b/src/libslic3r/SupportSpotsGeneratorRefactoring.cpp @@ -15,7 +15,7 @@ #include "libslic3r/ClipperUtils.hpp" #include "Geometry/ConvexHull.hpp" -//#define DEBUG_FILES +#define DEBUG_FILES #ifdef DEBUG_FILES #include @@ -193,6 +193,61 @@ private: } }; +struct SupportGridFilter { +private: + Vec3f cell_size; + Vec3f origin; + Vec3f size; + Vec3i cell_count; + + std::unordered_set taken_cells { }; + +public: + SupportGridFilter(const PrintObject *po, float voxel_size) { + cell_size = Vec3f(voxel_size, voxel_size, voxel_size); + + Vec2crd size_half = po->size().head<2>().cwiseQuotient(Vec2crd(2, 2)) + Vec2crd::Ones(); + Vec3f min = unscale(Vec3crd(-size_half.x(), -size_half.y(), 0)).cast() - cell_size; + Vec3f max = unscale(Vec3crd(size_half.x(), size_half.y(), po->height())).cast() + cell_size; + + origin = min; + size = max - min; + cell_count = size.cwiseQuotient(cell_size).cast() + Vec3i::Ones(); + } + + Vec3i to_cell_coords(const Vec3f &position) const { + Vec3i cell_coords = (position - this->origin).cwiseQuotient(this->cell_size).cast(); + return cell_coords; + } + + size_t to_cell_index(const Vec3i &cell_coords) const { + assert(cell_coords.x() >= 0); + assert(cell_coords.x() < cell_count.x()); + assert(cell_coords.y() >= 0); + assert(cell_coords.y() < cell_count.y()); + assert(cell_coords.z() >= 0); + assert(cell_coords.z() < cell_count.z()); + + return cell_coords.z() * cell_count.x() * cell_count.y() + + cell_coords.y() * cell_count.x() + + cell_coords.x(); + } + + Vec3f get_cell_center(const Vec3i &cell_coords) const { + return origin + cell_coords.cast().cwiseProduct(this->cell_size) + + this->cell_size.cwiseQuotient(Vec3f(2.0f, 2.0f, 2.0)); + } + + void take_position(const Vec3f &position) { + taken_cells.insert(to_cell_index(to_cell_coords(position))); + } + + bool position_taken(const Vec3f &position) const { + return taken_cells.find(to_cell_index(to_cell_coords(position))) != taken_cells.end(); + } + +}; + struct Island { std::unordered_map islands_under_with_connection_area; std::vector pivot_points; @@ -351,9 +406,11 @@ std::tuple> reckon_islands( BOOST_LOG_TRIVIAL(debug) << "SSG: reckon islands on printz: " << layer->print_z; + //extract extrusions (connected paths from multiple lines) from the layer_lines. belonging to single polyline is determined by origin_entity ptr. + // result is a vector of [start, end) index pairs into the layer_lines vector std::vector> extrusions; //start and end idx (one beyond last extrusion) [start,end) const ExtrusionEntity *current_ex = nullptr; - for (size_t lidx = 1; lidx < layer_lines.size(); ++lidx) { + for (size_t lidx = 0; lidx < layer_lines.size(); ++lidx) { const ExtrusionLine &line = layer_lines[lidx]; if (line.origin_entity == current_ex) { extrusions.back().second = lidx + 1; @@ -374,8 +431,10 @@ std::tuple> reckon_islands( << "SSG: " << ext.second - ext.first; } - std::vector islands; - std::vector> island_extrusions; + std::vector islands; // these search trees will be used to determine to which island does the extrusion begin + std::vector> island_extrusions; //final assigment of each extrusion to an island + // initliaze the search from external perimeters - at the beginning, there is island candidate for each external perimeter. + // some of them will disappear (e.g. holes) for (size_t e = 0; e < extrusions.size(); ++e) { if (layer_lines[extrusions[e].first].is_external_perimeter()) { std::vector copy(extrusions[e].second - extrusions[e].first); @@ -386,22 +445,40 @@ std::tuple> reckon_islands( island_extrusions.push_back( { e }); } } + // backup code if islands not found - this can currently happen, as external perimeters may be also pure overhang perimeters, and there is no + // way to distinguish external extrusions with total certainty. + // If that happens, just make the first extrusion into island - it may be wrong, but it won't crash. + if (islands.empty() && !extrusions.empty()) { + std::vector copy(extrusions[0].second - extrusions[0].first); + for (size_t ex_line_idx = extrusions[0].first; ex_line_idx < extrusions[0].second; ++ex_line_idx) { + copy[ex_line_idx - extrusions[0].first] = layer_lines[ex_line_idx]; + } + islands.emplace_back(copy); + island_extrusions.push_back( { 0 }); + } BOOST_LOG_TRIVIAL(debug) << "SSG: external perims: " << islands.size(); - - for (size_t i = 0; i < islands.size(); ++i) { - for (size_t e = 0; e < extrusions.size(); ++e) { - if (!layer_lines[extrusions[e].first].is_external_perimeter()) { + // assign non external extrusions to islands + for (size_t e = 0; e < extrusions.size(); ++e) { + if (!layer_lines[extrusions[e].first].is_external_perimeter()) { + bool island_assigned = false; + for (size_t i = 0; i < islands.size(); ++i) { size_t _idx; Vec2f _pt; if (islands[i].signed_distance_from_lines(layer_lines[extrusions[e].first].a, _idx, _pt) < 0) { island_extrusions[i].push_back(e); + island_assigned = true; + break; } } + if (!island_assigned) { // If extrusion is not assigned for some reason, push it into the first island. As with the previous backup code, + // it may be wrong, but it won't crash + island_extrusions[0].push_back(e); + } } } - + // merge islands which are embedded within each other (mainly holes) for (size_t i = 0; i < islands.size(); ++i) { if (islands[i].get_lines().empty()) { continue; @@ -424,7 +501,7 @@ std::tuple> reckon_islands( << "SSG: filter islands"; float flow_width = get_flow_width(layer->regions()[0], erExternalPerimeter); - + // after filtering the layer lines into islands, build the result LayerIslands structure. LayerIslands result { }; std::vector line_to_island_mapping(layer_lines.size(), NULL_ISLAND); for (const std::vector &island_ex : island_extrusions) { @@ -437,9 +514,10 @@ std::tuple> reckon_islands( for (size_t lidx = extrusions[extrusion_idx].first; lidx < extrusions[extrusion_idx].second; ++lidx) { line_to_island_mapping[lidx] = result.islands.size(); const ExtrusionLine &line = layer_lines[lidx]; - float volume = line.len * flow_width * layer->height * 0.7; // 1/sqrt(2) compensation for cylindrical shape + float volume = line.origin_entity->min_mm3_per_mm() * line.len; island.volume += volume; - island.volume_centroid_accumulator += to_3d(Vec2f((line.a + line.b) / 2.0f), float(layer->print_z)) * volume; + island.volume_centroid_accumulator += to_3d(Vec2f((line.a + line.b) / 2.0f), float(layer->print_z)) + * volume; if (first_layer) { float sticking_force = line.len * flow_width * params.base_adhesion; @@ -455,7 +533,8 @@ std::tuple> reckon_islands( * float(PI); float sticking_force = support_interface_area * params.support_adhesion; island.sticking_force += sticking_force; - island.sticking_centroid_accumulator += sticking_force * to_3d(Vec2f(line.b), float(layer->print_z)); + island.sticking_centroid_accumulator += sticking_force + * to_3d(Vec2f(line.b), float(layer->print_z)); island.pivot_points.push_back(to_3d(Vec2f(line.b), float(layer->print_z))); } } @@ -466,9 +545,10 @@ std::tuple> reckon_islands( BOOST_LOG_TRIVIAL(debug) << "SSG: There are " << result.islands.size() << " islands on printz: " << layer->print_z; + //LayerIslands structure built. Now determine connections and their areas to the previous layer using raterization. PixelGrid current_layer_grid = prev_layer_grid; current_layer_grid.clear(); - + // build index image of current layer tbb::parallel_for(tbb::blocked_range(0, layer_lines.size()), [&layer_lines, ¤t_layer_grid, &line_to_island_mapping]( tbb::blocked_range r) { @@ -482,6 +562,7 @@ std::tuple> reckon_islands( BOOST_LOG_TRIVIAL(debug) << "SSG: rasterized"; + //compare the image of previous layer with the current layer. For each pair of overlapping valid pixels, add pixel area to the respecitve island connection for (size_t x = 0; x < size_t(current_layer_grid.get_pixel_count().x()); ++x) { for (size_t y = 0; y < size_t(current_layer_grid.get_pixel_count().y()); ++y) { Vec2i coords = Vec2i(x, y); @@ -502,14 +583,20 @@ std::tuple> reckon_islands( void check_global_stability( float print_z, - std::vector& islands_graph, + std::vector &islands_graph, + SupportGridFilter &supports_presence_grid, const std::vector &layer_lines, const std::vector &line_to_island_mapping, - Issues& issues, - const Params& params + Issues &issues, + const Params ¶ms ) { - // vector of islands, where each contains vector of line indices (to layer_lines vector) - // basically reverse of line_to_island_mapping + + std::cout << "there are " << islands_graph.back().islands.size() << " islands, " << layer_lines.size() << " lines" << std::endl; + for (int i = 0; i < line_to_island_mapping.size(); ++i) { + std::cout << "line " << i << " belongs to island " << line_to_island_mapping[i] << std::endl; + } + // vector of islands, where each contains vector of line indices (to layer_lines vector) + // basically reverse of line_to_island_mapping std::vector> islands_lines(islands_graph.back().islands.size()); for (size_t lidx = 0; lidx < layer_lines.size(); ++lidx) { if (layer_lines[lidx].origin_entity->role() == erExternalPerimeter) { @@ -519,38 +606,41 @@ void check_global_stability( using Accumulator = Island; - // islands_graph.back() refers to the top most (currently) layer + // islands_graph.back() refers to the top most (current) layer for (size_t island_idx = 0; island_idx < islands_graph.back().islands.size(); ++island_idx) { - Island& island = islands_graph.back().islands[island_idx]; + Island &island = islands_graph.back().islands[island_idx]; - std::vector island_external_lines; //TODO currently not external but all + std::cout << "TOP LEVEL ITERATION FOR ISLAND: " << island_idx << std::endl; + + std::vector island_external_lines; for (size_t lidx : islands_lines[island_idx]) { island_external_lines.push_back(layer_lines[lidx]); } LinesDistancer island_lines_dist(island_external_lines); Accumulator acc = island; // in acc, we accumulate the mass and other properties of the object part as we traverse the islands down to bed // There is one object part for each island at the top most layer, and each one is computed individually - - // Some of the calculations will be done mutliple times - int layer_idx = islands_graph.size() -1; + // Some of the calculations will be done multiple times + int layer_idx = islands_graph.size() - 1; // traverse the islands graph down, and for each connection area, calculate if it holds or breaks while (acc.islands_under_with_connection_area.size() > 0) { + std::cout << "PARTIAL ITERATION FOR LAYER: " << layer_idx << std::endl; //test for break between layer_idx and layer_idx -1; - LayerIslands below = islands_graph[layer_idx-1]; // must exist, see while condition + LayerIslands below = islands_graph[layer_idx - 1]; // must exist, see while condition layer_idx--; // initialize variables that we will accumulate over all islands, which are connected to the current object part std::vector pivot_points; Vec2f sticking_centroid; float connection_area = 0; - for (const auto& pair : acc.islands_under_with_connection_area) { - const Island& below_i = below.islands[pair.first]; + for (const auto &pair : acc.islands_under_with_connection_area) { + const Island &below_i = below.islands[pair.first]; Vec2f centroid = (below_i.volume_centroid_accumulator / below_i.volume).head<2>(); // centroid of the island 'below_i'; TODO it should be centroid of the connection area pivot_points.push_back(centroid); // for object parts, we also consider breaking pivots in the centroids of the islands sticking_centroid += centroid * pair.second; // pair.second is connection area in mm^2 connection_area += pair.second; } sticking_centroid /= connection_area; //normalize to get final sticking centroid - for (const Vec3f& p_point: acc.pivot_points){ - pivot_points.push_back(p_point.head<2>()); + for (const Vec3f &p_point : acc.pivot_points) { + pivot_points.push_back(p_point.head<2>()); } // Now we have accumulated pivot points, connection area and sticking centroid of the whole layer to the current object part @@ -561,7 +651,7 @@ void check_global_stability( KDTreeIndirect<2, float, decltype(coord_fn)> pivot_points_tree(coord_fn, pivot_points.size()); // iterate over extrusions at top layer island, check each for stability - for (const ExtrusionLine& line : island_external_lines){ + for (const ExtrusionLine &line : island_external_lines) { Vec2f line_dir = (line.b - line.a).normalized(); Vec2f pivot_site_search_point = line.b + line_dir * 300.0f; size_t pivot_idx = find_closest_point(pivot_points_tree, pivot_site_search_point); @@ -592,23 +682,23 @@ void check_global_stability( float total_torque = bed_movement_torque + extruder_conflict_torque - weight_torque - sticking_torque; if (total_torque > 0) { - Vec2f target_point; - size_t _idx; + Vec2f target_point { }; + size_t _idx { }; island_lines_dist.signed_distance_from_lines(pivot_site_search_point, _idx, target_point); -// if (!supports_presence_grid.position_taken(to_3d(target_point, print_z))) { + if (!supports_presence_grid.position_taken(to_3d(target_point, print_z))) { float area = params.support_points_interface_radius * params.support_points_interface_radius * float(PI); float sticking_force = area * params.support_adhesion; Vec3f support_point = to_3d(target_point, print_z); - island.pivot_points.push_back(support_point); - island.sticking_force += sticking_force; - island.sticking_centroid_accumulator += sticking_force*support_point; + island.pivot_points.push_back(support_point); + island.sticking_force += sticking_force; + island.sticking_centroid_accumulator += sticking_force * support_point; issues.support_points.emplace_back(support_point, extruder_conflict_torque - sticking_torque, extruder_pressure_direction); -// supports_presence_grid.take_position(to_3d(target_point, print_z)); -// } + supports_presence_grid.take_position(to_3d(target_point, print_z)); + } } - #if 0 +#if 0 BOOST_LOG_TRIVIAL(debug) << "SSG: sticking_arm: " << sticking_arm; BOOST_LOG_TRIVIAL(debug) @@ -633,80 +723,81 @@ void check_global_stability( std::unordered_map tmp = acc.islands_under_with_connection_area; acc.islands_under_with_connection_area.clear(); // finally, add gathered islands to accumulator, and continue down to next layer - for (const auto& pair : tmp) { - const Island& below_i = below.islands[pair.first]; - for (const auto& below_islands : below_i.islands_under_with_connection_area) { - acc.islands_under_with_connection_area[below_islands.first] += below_islands.second; - } - for (const Vec3f& pivot_p : below_i.pivot_points) { - acc.pivot_points.push_back(pivot_p); - } - acc.sticking_centroid_accumulator += below_i.sticking_centroid_accumulator; - acc.sticking_force += below_i.sticking_force; - acc.volume += below_i.volume; - acc.volume_centroid_accumulator += below_i.volume_centroid_accumulator; - } + for (const auto &pair : tmp) { + const Island &below_i = below.islands[pair.first]; + for (const auto &below_islands : below_i.islands_under_with_connection_area) { + acc.islands_under_with_connection_area[below_islands.first] += below_islands.second; + } + for (const Vec3f &pivot_p : below_i.pivot_points) { + acc.pivot_points.push_back(pivot_p); + } + acc.sticking_centroid_accumulator += below_i.sticking_centroid_accumulator; + acc.sticking_force += below_i.sticking_force; + acc.volume += below_i.volume; + acc.volume_centroid_accumulator += below_i.volume_centroid_accumulator; + } } + std::cout << "FINAL ITERATION FOR THE BED LEVEL: " << acc.volume << std::endl; // We have arrived to the bed level, now check for stability of the object part on the bed std::vector pivot_points; - for (const Vec3f& p_point: acc.pivot_points){ - pivot_points.push_back(p_point.head<2>()); + for (const Vec3f &p_point : acc.pivot_points) { + pivot_points.push_back(p_point.head<2>()); } auto coord_fn = [&pivot_points](size_t idx, size_t dim) { - return pivot_points[idx][dim]; - }; - KDTreeIndirect<2, float, decltype(coord_fn)> pivot_points_tree(coord_fn, pivot_points.size()); + return pivot_points[idx][dim]; + }; + KDTreeIndirect<2, float, decltype(coord_fn)> pivot_points_tree(coord_fn, pivot_points.size()); - for (const ExtrusionLine &line : island_external_lines) { - Vec2f line_dir = (line.b - line.a).normalized(); - Vec2f pivot_site_search_point = line.b + line_dir * 300.0f; - size_t pivot_idx = find_closest_point(pivot_points_tree, pivot_site_search_point); - const Vec2f &pivot = pivot_points[pivot_idx]; + for (const ExtrusionLine &line : island_external_lines) { + Vec2f line_dir = (line.b - line.a).normalized(); + Vec2f pivot_site_search_point = line.b + line_dir * 300.0f; + size_t pivot_idx = find_closest_point(pivot_points_tree, pivot_site_search_point); + const Vec2f &pivot = pivot_points[pivot_idx]; - const Vec2f &sticking_centroid = acc.sticking_centroid_accumulator.head<2>() / acc.sticking_force; - float sticking_arm = (pivot - sticking_centroid).norm(); - float sticking_torque = sticking_arm * acc.sticking_force; + const Vec2f &sticking_centroid = acc.sticking_centroid_accumulator.head<2>() / acc.sticking_force; + float sticking_arm = (pivot - sticking_centroid).norm(); + float sticking_torque = sticking_arm * acc.sticking_force; - float mass = acc.volume * params.filament_density; - const Vec3f &mass_centorid = acc.volume_centroid_accumulator / acc.volume; - float weight = mass * params.gravity_constant; - float weight_arm = (pivot - mass_centorid.head<2>()).norm(); - float weight_torque = weight_arm * weight; + float mass = acc.volume * params.filament_density; + const Vec3f &mass_centorid = acc.volume_centroid_accumulator / acc.volume; + float weight = mass * params.gravity_constant; + float weight_arm = (pivot - mass_centorid.head<2>()).norm(); + float weight_torque = weight_arm * weight; - float bed_movement_arm = mass_centorid.z(); - float bed_movement_force = params.max_acceleration * mass; - float bed_movement_torque = bed_movement_force * bed_movement_arm; + float bed_movement_arm = mass_centorid.z(); + float bed_movement_force = params.max_acceleration * mass; + float bed_movement_torque = bed_movement_force * bed_movement_arm; - Vec3f extruder_pressure_direction = to_3d(line_dir, 0.0f); - extruder_pressure_direction.z() = -0.2 - line.malformation * 0.5; - extruder_pressure_direction.normalize(); - float conflict_torque_arm = (to_3d(Vec2f(pivot - line.b), print_z).cross( - extruder_pressure_direction)).norm(); - float extruder_conflict_force = params.tolerable_extruder_conflict_force + - std::min(line.malformation, 1.0f) * params.malformations_additive_conflict_extruder_force; - float extruder_conflict_torque = extruder_conflict_force * conflict_torque_arm; + Vec3f extruder_pressure_direction = to_3d(line_dir, 0.0f); + extruder_pressure_direction.z() = -0.2 - line.malformation * 0.5; + extruder_pressure_direction.normalize(); + float conflict_torque_arm = (to_3d(Vec2f(pivot - line.b), print_z).cross( + extruder_pressure_direction)).norm(); + float extruder_conflict_force = params.tolerable_extruder_conflict_force + + std::min(line.malformation, 1.0f) * params.malformations_additive_conflict_extruder_force; + float extruder_conflict_torque = extruder_conflict_force * conflict_torque_arm; - float total_torque = bed_movement_torque + extruder_conflict_torque - weight_torque - sticking_torque; + float total_torque = bed_movement_torque + extruder_conflict_torque - weight_torque - sticking_torque; - if (total_torque > 0) { - Vec2f target_point; - size_t _idx; - island_lines_dist.signed_distance_from_lines(pivot_site_search_point, _idx, target_point); - // if (!supports_presence_grid.position_taken(to_3d(target_point, print_z))) { - float area = params.support_points_interface_radius * params.support_points_interface_radius - * float(PI); - float sticking_force = area * params.support_adhesion; - Vec3f support_point = to_3d(target_point, print_z); - island.pivot_points.push_back(support_point); - island.sticking_force += sticking_force; - island.sticking_centroid_accumulator += sticking_force*support_point; - issues.support_points.emplace_back(support_point, - extruder_conflict_torque - sticking_torque, extruder_pressure_direction); - // supports_presence_grid.take_position(to_3d(target_point, print_z)); - // } - } - #if 0 + if (total_torque > 0) { + Vec2f target_point; + size_t _idx; + island_lines_dist.signed_distance_from_lines(pivot_site_search_point, _idx, target_point); + if (!supports_presence_grid.position_taken(to_3d(target_point, print_z))) { + float area = params.support_points_interface_radius * params.support_points_interface_radius + * float(PI); + float sticking_force = area * params.support_adhesion; + Vec3f support_point = to_3d(target_point, print_z); + island.pivot_points.push_back(support_point); + island.sticking_force += sticking_force; + island.sticking_centroid_accumulator += sticking_force * support_point; + issues.support_points.emplace_back(support_point, + extruder_conflict_torque - sticking_torque, extruder_pressure_direction); + supports_presence_grid.take_position(to_3d(target_point, print_z)); + } + } +#if 0 BOOST_LOG_TRIVIAL(debug) << "SSG: sticking_arm: " << sticking_arm; BOOST_LOG_TRIVIAL(debug) @@ -726,7 +817,7 @@ void check_global_stability( BOOST_LOG_TRIVIAL(debug) << "SSG: total_torque: " << total_torque << " printz: " << print_z; #endif - } + } } } @@ -741,6 +832,8 @@ Issues check_object_stability(const PrintObject *po, const Params ¶ms) { std::vector layer_lines; float flow_width = get_flow_width(po->layers()[po->layer_count() - 1]->regions()[0], erExternalPerimeter); PixelGrid prev_layer_grid(po, flow_width); + SupportGridFilter supports_presence_grid { po, params.min_distance_between_support_points }; + BOOST_LOG_TRIVIAL(debug) << "SSG: flow width: " << flow_width; @@ -841,7 +934,8 @@ Issues check_object_stability(const PrintObject *po, const Params ¶ms) { layer_lines, params); islands_graph.push_back(std::move(layer_islands)); - check_global_stability(layer->print_z, islands_graph, layer_lines, line_to_island_mapping, issues, params); + check_global_stability(layer->print_z, islands_graph, supports_presence_grid, layer_lines, + line_to_island_mapping, issues, params); #ifdef DEBUG_FILES for (size_t x = 0; x < size_t(layer_grid.get_pixel_count().x()); ++x) { From 9afb350cddba918faa7dacdcc0a039dbfff1181b Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Tue, 19 Jul 2022 10:09:21 +0200 Subject: [PATCH 46/78] remove noisy debug info --- .../SupportSpotsGeneratorRefactoring.cpp | 41 ------------------- 1 file changed, 41 deletions(-) diff --git a/src/libslic3r/SupportSpotsGeneratorRefactoring.cpp b/src/libslic3r/SupportSpotsGeneratorRefactoring.cpp index 23d5698567..a2275c6372 100644 --- a/src/libslic3r/SupportSpotsGeneratorRefactoring.cpp +++ b/src/libslic3r/SupportSpotsGeneratorRefactoring.cpp @@ -403,9 +403,6 @@ std::tuple> reckon_islands( const std::vector &layer_lines, const Params ¶ms) { - BOOST_LOG_TRIVIAL(debug) - << "SSG: reckon islands on printz: " << layer->print_z; - //extract extrusions (connected paths from multiple lines) from the layer_lines. belonging to single polyline is determined by origin_entity ptr. // result is a vector of [start, end) index pairs into the layer_lines vector std::vector> extrusions; //start and end idx (one beyond last extrusion) [start,end) @@ -420,17 +417,6 @@ std::tuple> reckon_islands( } } - BOOST_LOG_TRIVIAL(debug) - << "SSG: layer_lines size: " << layer_lines.size(); - BOOST_LOG_TRIVIAL(debug) - << "SSG: extrusions count: " << extrusions.size(); - BOOST_LOG_TRIVIAL(debug) - << "SSG: extrusions sizes: "; - for (const auto &ext : extrusions) { - BOOST_LOG_TRIVIAL(debug) - << "SSG: " << ext.second - ext.first; - } - std::vector islands; // these search trees will be used to determine to which island does the extrusion begin std::vector> island_extrusions; //final assigment of each extrusion to an island // initliaze the search from external perimeters - at the beginning, there is island candidate for each external perimeter. @@ -457,8 +443,6 @@ std::tuple> reckon_islands( island_extrusions.push_back( { 0 }); } - BOOST_LOG_TRIVIAL(debug) - << "SSG: external perims: " << islands.size(); // assign non external extrusions to islands for (size_t e = 0; e < extrusions.size(); ++e) { if (!layer_lines[extrusions[e].first].is_external_perimeter()) { @@ -497,9 +481,6 @@ std::tuple> reckon_islands( } } - BOOST_LOG_TRIVIAL(debug) - << "SSG: filter islands"; - float flow_width = get_flow_width(layer->regions()[0], erExternalPerimeter); // after filtering the layer lines into islands, build the result LayerIslands structure. LayerIslands result { }; @@ -542,9 +523,6 @@ std::tuple> reckon_islands( result.islands.push_back(island); } - BOOST_LOG_TRIVIAL(debug) - << "SSG: There are " << result.islands.size() << " islands on printz: " << layer->print_z; - //LayerIslands structure built. Now determine connections and their areas to the previous layer using raterization. PixelGrid current_layer_grid = prev_layer_grid; current_layer_grid.clear(); @@ -559,9 +537,6 @@ std::tuple> reckon_islands( } }); - BOOST_LOG_TRIVIAL(debug) - << "SSG: rasterized"; - //compare the image of previous layer with the current layer. For each pair of overlapping valid pixels, add pixel area to the respecitve island connection for (size_t x = 0; x < size_t(current_layer_grid.get_pixel_count().x()); ++x) { for (size_t y = 0; y < size_t(current_layer_grid.get_pixel_count().y()); ++y) { @@ -574,10 +549,6 @@ std::tuple> reckon_islands( } } } - - BOOST_LOG_TRIVIAL(debug) - << "SSG: connection area computed"; - return {result, current_layer_grid, line_to_island_mapping}; } @@ -590,11 +561,6 @@ void check_global_stability( Issues &issues, const Params ¶ms ) { - - std::cout << "there are " << islands_graph.back().islands.size() << " islands, " << layer_lines.size() << " lines" << std::endl; - for (int i = 0; i < line_to_island_mapping.size(); ++i) { - std::cout << "line " << i << " belongs to island " << line_to_island_mapping[i] << std::endl; - } // vector of islands, where each contains vector of line indices (to layer_lines vector) // basically reverse of line_to_island_mapping std::vector> islands_lines(islands_graph.back().islands.size()); @@ -610,8 +576,6 @@ void check_global_stability( for (size_t island_idx = 0; island_idx < islands_graph.back().islands.size(); ++island_idx) { Island &island = islands_graph.back().islands[island_idx]; - std::cout << "TOP LEVEL ITERATION FOR ISLAND: " << island_idx << std::endl; - std::vector island_external_lines; for (size_t lidx : islands_lines[island_idx]) { island_external_lines.push_back(layer_lines[lidx]); @@ -623,7 +587,6 @@ void check_global_stability( int layer_idx = islands_graph.size() - 1; // traverse the islands graph down, and for each connection area, calculate if it holds or breaks while (acc.islands_under_with_connection_area.size() > 0) { - std::cout << "PARTIAL ITERATION FOR LAYER: " << layer_idx << std::endl; //test for break between layer_idx and layer_idx -1; LayerIslands below = islands_graph[layer_idx - 1]; // must exist, see while condition layer_idx--; @@ -738,7 +701,6 @@ void check_global_stability( } } - std::cout << "FINAL ITERATION FOR THE BED LEVEL: " << acc.volume << std::endl; // We have arrived to the bed level, now check for stability of the object part on the bed std::vector pivot_points; for (const Vec3f &p_point : acc.pivot_points) { @@ -834,9 +796,6 @@ Issues check_object_stability(const PrintObject *po, const Params ¶ms) { PixelGrid prev_layer_grid(po, flow_width); SupportGridFilter supports_presence_grid { po, params.min_distance_between_support_points }; - BOOST_LOG_TRIVIAL(debug) - << "SSG: flow width: " << flow_width; - // PREPARE BASE LAYER const Layer *layer = po->layers()[0]; for (const LayerRegion *layer_region : layer->regions()) { From 3d1f2f0cb66e77f70c6d8c71e1f0b9424a3538c9 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Wed, 20 Jul 2022 16:23:03 +0200 Subject: [PATCH 47/78] implemented graph traversal, keeping the segments and the location of the weakest point for each island --- src/libslic3r/CMakeLists.txt | 3 +- src/libslic3r/SupportSpotsGenerator.cpp | 1262 +++++++++-------- .../SupportSpotsGeneratorRefactoring.cpp | 972 ------------- src/slic3r/GUI/BonjourDialog.hpp | 2 + 4 files changed, 708 insertions(+), 1531 deletions(-) delete mode 100644 src/libslic3r/SupportSpotsGeneratorRefactoring.cpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 0c4fd20795..e478188356 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -245,8 +245,7 @@ set(SLIC3R_SOURCES SlicingAdaptive.hpp Subdivide.cpp Subdivide.hpp -# SupportSpotsGenerator.cpp - SupportSpotsGeneratorRefactoring.cpp + SupportSpotsGenerator.cpp SupportSpotsGenerator.hpp SupportMaterial.cpp SupportMaterial.hpp diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index 08d734a38e..4512c795b4 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -2,6 +2,7 @@ #include "tbb/parallel_for.h" #include "tbb/blocked_range.h" +#include "tbb/blocked_range2d.h" #include "tbb/parallel_reduce.h" #include #include @@ -23,29 +24,32 @@ namespace Slic3r { -static const size_t NULL_ACC_ID = std::numeric_limits::max(); - class ExtrusionLine { public: ExtrusionLine() : - a(Vec2f::Zero()), b(Vec2f::Zero()), len(0.0f), external_perimeter(false) { + a(Vec2f::Zero()), b(Vec2f::Zero()), len(0.0f), origin_entity(nullptr) { } - ExtrusionLine(const Vec2f &_a, const Vec2f &_b, bool external_perimeter) : - a(_a), b(_b), len((_a - _b).norm()), external_perimeter(external_perimeter) { + ExtrusionLine(const Vec2f &_a, const Vec2f &_b, const ExtrusionEntity *origin_entity) : + a(_a), b(_b), len((_a - _b).norm()), origin_entity(origin_entity) { } float length() { return (a - b).norm(); } + bool is_external_perimeter() const { + assert(origin_entity != nullptr); + return origin_entity->role() == erExternalPerimeter; + } + Vec2f a; Vec2f b; float len; - bool external_perimeter; + const ExtrusionEntity *origin_entity; + bool support_point_generated = false; float malformation = 0.0f; - size_t stability_accumulator_id = NULL_ACC_ID; static const constexpr int Dim = 2; using Scalar = Vec2f::Scalar; @@ -60,29 +64,136 @@ auto get_b(ExtrusionLine &&l) { namespace SupportSpotsGenerator { -void Issues::add(const Issues &layer_issues) { - supports_nedded.insert(supports_nedded.end(), layer_issues.supports_nedded.begin(), - layer_issues.supports_nedded.end()); - curling_up.insert(curling_up.end(), layer_issues.curling_up.begin(), layer_issues.curling_up.end()); +SupportPoint::SupportPoint(const Vec3f &position, float force, const Vec3f &direction) : + position(position), force(force), direction(direction) { } -bool Issues::empty() const { - return supports_nedded.empty() && curling_up.empty(); -} +class LinesDistancer { +private: + std::vector lines; + AABBTreeIndirect::Tree<2, float> tree; -SupportPoint::SupportPoint(const Vec3f &position, float weight) : - position(position), weight(weight) { -} +public: + explicit LinesDistancer(std::vector &lines) : + lines(lines) { + tree = AABBTreeLines::build_aabb_tree_over_indexed_lines(this->lines); + } -CurledFilament::CurledFilament(const Vec3f &position, float estimated_height) : - position(position), estimated_height(estimated_height) { -} + // negative sign means inside + float signed_distance_from_lines(const Vec2f &point, size_t &nearest_line_index_out, + Vec2f &nearest_point_out) const { + auto distance = AABBTreeLines::squared_distance_to_indexed_lines(lines, tree, point, nearest_line_index_out, + nearest_point_out); + if (distance < 0) + return std::numeric_limits::infinity(); -CurledFilament::CurledFilament(const Vec3f &position) : - position(position), estimated_height(0.0f) { -} + distance = sqrt(distance); + const ExtrusionLine &line = lines[nearest_line_index_out]; + Vec2f v1 = line.b - line.a; + Vec2f v2 = point - line.a; + if ((v1.x() * v2.y()) - (v1.y() * v2.x()) > 0.0) { + distance *= -1; + } + return distance; + } -struct VoxelGrid { + const ExtrusionLine& get_line(size_t line_idx) const { + return lines[line_idx]; + } + + const std::vector& get_lines() const { + return lines; + } +}; + +static const size_t NULL_ISLAND = std::numeric_limits::max(); + +class PixelGrid { + Vec2f pixel_size; + Vec2f origin; + Vec2f size; + Vec2i pixel_count; + + std::vector pixels { }; + +public: + PixelGrid(const PrintObject *po, float resolution) { + pixel_size = Vec2f(resolution, resolution); + + Vec2crd size_half = po->size().head<2>().cwiseQuotient(Vec2crd(2, 2)) + Vec2crd::Ones(); + Vec2f min = unscale(Vec2crd(-size_half.x(), -size_half.y())).cast(); + Vec2f max = unscale(Vec2crd(size_half.x(), size_half.y())).cast(); + + origin = min; + size = max - min; + pixel_count = size.cwiseQuotient(pixel_size).cast() + Vec2i::Ones(); + + pixels.resize(pixel_count.y() * pixel_count.x()); + clear(); + } + + void distribute_edge(const Vec2f &p1, const Vec2f &p2, size_t value) { + Vec2f dir = (p2 - p1); + float length = dir.norm(); + if (length < 0.1) { + return; + } + float step_size = this->pixel_size.x() / 2.0; + + float distributed_length = 0; + while (distributed_length < length) { + float next_len = std::min(length, distributed_length + step_size); + Vec2f location = p1 + ((next_len / length) * dir); + this->access_pixel(location) = value; + + distributed_length = next_len; + } + } + + void clear() { + for (size_t &val : pixels) { + val = NULL_ISLAND; + } + } + + float pixel_area() const { + return this->pixel_size.x() * this->pixel_size.y(); + } + + size_t get_pixel(const Vec2i &coords) const { + return pixels[this->to_pixel_index(coords)]; + } + + Vec2i get_pixel_count() { + return pixel_count; + } + + Vec2f get_pixel_center(const Vec2i &coords) const { + return origin + coords.cast().cwiseProduct(this->pixel_size) + + this->pixel_size.cwiseQuotient(Vec2f(2.0f, 2.0f)); + } + +private: + Vec2i to_pixel_coords(const Vec2f &position) const { + Vec2i pixel_coords = (position - this->origin).cwiseQuotient(this->pixel_size).cast(); + return pixel_coords; + } + + size_t to_pixel_index(const Vec2i &pixel_coords) const { + assert(pixel_coords.x() >= 0); + assert(pixel_coords.x() < pixel_count.x()); + assert(pixel_coords.y() >= 0); + assert(pixel_coords.y() < pixel_count.y()); + + return pixel_coords.y() * pixel_count.x() + pixel_coords.x(); + } + + size_t& access_pixel(const Vec2f &position) { + return pixels[this->to_pixel_index(this->to_pixel_coords(position))]; + } +}; + +struct SupportGridFilter { private: Vec3f cell_size; Vec3f origin; @@ -92,7 +203,7 @@ private: std::unordered_set taken_cells { }; public: - VoxelGrid(const PrintObject *po, float voxel_size) { + SupportGridFilter(const PrintObject *po, float voxel_size) { cell_size = Vec3f(voxel_size, voxel_size, voxel_size); Vec2crd size_half = po->size().head<2>().cwiseQuotient(Vec2crd(2, 2)) + Vec2crd::Ones(); @@ -137,197 +248,25 @@ public: }; -class LayerLinesDistancer { -private: - std::vector lines; - AABBTreeIndirect::Tree<2, float> tree; - -public: - explicit LayerLinesDistancer(std::vector &&lines) : - lines(lines) { - tree = AABBTreeLines::build_aabb_tree_over_indexed_lines(this->lines); - } - - // negative sign means inside - float signed_distance_from_lines(const Vec2f &point, size_t &nearest_line_index_out, - Vec2f &nearest_point_out) const { - auto distance = AABBTreeLines::squared_distance_to_indexed_lines(lines, tree, point, nearest_line_index_out, - nearest_point_out); - if (distance < 0) - return std::numeric_limits::infinity(); - - distance = sqrt(distance); - const ExtrusionLine &line = lines[nearest_line_index_out]; - Vec2f v1 = line.b - line.a; - Vec2f v2 = point - line.a; - if ((v1.x() * v2.y()) - (v1.y() * v2.x()) > 0.0) { - distance *= -1; - } - return distance; - } - - const ExtrusionLine& get_line(size_t line_idx) const { - return lines[line_idx]; - } - - const std::vector& get_lines() const { - return lines; - } - - void set_new_acc_id(size_t line_idx, size_t acc_id) { - lines[line_idx].stability_accumulator_id = acc_id; - } +struct IslandConnection { + float area; + Vec3f centroid_accumulator; }; -// StabilityAccumulator accumulates extrusions for each connected model part from bed to current printed layer. -// If the originaly disconected parts meet in the layer, their stability accumulators get merged and continue as one. -// (think legs of table, which get connected when the top desk is being printed). -// The class gathers mass, centroid mass, sticking force (bed extrusions, support points) and sticking centroid for the -// connected part. These values are then used to check global part stability. -class StabilityAccumulator { -private: - std::vector support_points { }; - Vec3f centroid_accumulator = Vec3f::Zero(); - float accumulated_volume { }; - Vec2f sticking_centroid_accumulator = Vec2f::Zero(); - float accumulated_sticking_force { }; +struct Island { + std::unordered_map connected_islands; + std::vector pivot_points; // for support points present on this layer (or bed extrusions) + float volume; + Vec3f volume_centroid_accumulator; + float sticking_force; // for support points present on this layer (or bed extrusions) + Vec3f sticking_centroid_accumulator; -public: - StabilityAccumulator() = default; - - void add_base_extrusion(const ExtrusionLine &line, float sticking_force, float print_z, float mm3_per_mm) { - accumulated_sticking_force += sticking_force; - sticking_centroid_accumulator += sticking_force * ((line.a + line.b) / 2.0f); - support_points.push_back(line.a); - support_points.push_back(line.b); - add_extrusion(line, print_z, mm3_per_mm); - } - - void add_support_point(const Vec2f &position, float sticking_force) { - support_points.push_back(position); - accumulated_sticking_force += sticking_force; - sticking_centroid_accumulator += sticking_force * position; - } - - void add_extrusion(const ExtrusionLine &line, float print_z, float mm3_per_mm) { - float volume = line.len * mm3_per_mm; - accumulated_volume += volume; - Vec2f center = (line.a + line.b) / 2.0f; - centroid_accumulator += volume * Vec3f(center.x(), center.y(), print_z); - } - - Vec3f get_centroid() const { - if (accumulated_volume <= 0.0f) { - return Vec3f::Zero(); - } - return centroid_accumulator / accumulated_volume; - } - - float get_sticking_force() const { - return accumulated_sticking_force; - } - - float get_accumulated_volume() const { - return accumulated_volume; - } - - const std::vector& get_support_points() const { - return support_points; - } - - Vec2f get_sticking_centroid() const { - if (accumulated_sticking_force <= 0.0f) { - return Vec2f::Zero(); - } - return sticking_centroid_accumulator / accumulated_sticking_force; - } - - void add_from(const StabilityAccumulator &acc) { - this->support_points.insert(this->support_points.end(), acc.support_points.begin(), - acc.support_points.end()); - this->centroid_accumulator += acc.centroid_accumulator; - this->accumulated_volume += acc.accumulated_volume; - this->accumulated_sticking_force += acc.accumulated_sticking_force; - this->sticking_centroid_accumulator += acc.sticking_centroid_accumulator; - } + std::vector external_lines; }; -// StabilityAccumulators class is wrapper over the vector of stability accumualtors. It provides a level of indirection -// between accumulator ID and the accumulator instance itself. While each extrusion line has one id, which is asigned -// when algorithm reaches the line's layer, the accumulator this id points to can change due to merging. -struct StabilityAccumulators { -private: - size_t next_id = 0; - std::unordered_map mapping; - std::vector accumulators; - - void merge_to(size_t from_id, size_t to_id) { - StabilityAccumulator &from_acc = this->access(from_id); - StabilityAccumulator &to_acc = this->access(to_id); - if (&from_acc == &to_acc) { - return; - } - to_acc.add_from(from_acc); - mapping[from_id] = mapping[to_id]; - from_acc = StabilityAccumulator { }; - - } - -public: - StabilityAccumulators() = default; - - size_t create_accumulator() { - size_t id = next_id; - next_id++; - mapping[id] = accumulators.size(); - accumulators.push_back(StabilityAccumulator { }); - return id; - } - - StabilityAccumulator& access(size_t id) { - return accumulators[mapping[id]]; - } - - void merge_accumulators(size_t id_a, size_t id_b) { - size_t from_id = std::max(id_a, id_b); - size_t to_id = std::min(id_a, id_b); - if (from_id == NULL_ACC_ID || to_id == NULL_ACC_ID) { - return; - } - StabilityAccumulator &from_acc = this->access(from_id); - StabilityAccumulator &to_acc = this->access(to_id); - if (&from_acc == &to_acc) { - return; - } - to_acc.add_from(from_acc); - mapping[from_id] = mapping[to_id]; - from_acc = StabilityAccumulator { }; - } - -#ifdef DEBUG_FILES - Vec3f get_accumulator_color(size_t id) { - if (mapping.find(id) == mapping.end()) { - BOOST_LOG_TRIVIAL(debug) - << "SSG: ERROR: uknown accumulator ID: " << id; - return Vec3f(1.0f, 1.0f, 1.0f); - } - - size_t pseudornd = ((mapping[id] + 127) * 33331 + 6907) % 987; - return value_to_rgbf(0.0f, float(987), float(pseudornd)); - } - - void log_accumulators() { - for (size_t i = 0; i < accumulators.size(); ++i) { - const auto &acc = accumulators[i]; - BOOST_LOG_TRIVIAL(debug) - << "SSG: accumulator POS: " << i << "\n" - << "SSG: get_accumulated_volume: " << acc.get_accumulated_volume() << "\n" - << "SSG: get_sticking_force: " << acc.get_sticking_force() << "\n" - << "SSG: support points count: " << acc.get_support_points().size() << "\n"; - - } - } -#endif +struct LayerIslands { + std::vector islands; + float layer_z; }; float get_flow_width(const LayerRegion *region, ExtrusionRole role) { @@ -375,28 +314,18 @@ struct ExtrusionPropertiesAccumulator { } }; -// check_extrusion_entity_stability checks each extrusion for local issues, appends the extrusion -// into checked lines, and gives it a stability accumulator id. If support is needed it pushes it -// into issues as well. -// Rules for stability accumulator id assigment: -// If there is close extrusion under, use min extrusion id between the id of the previous line, -// and id of line under. Also merge the accumulators of those two ids! -// If there is no close extrusion under, use id of the previous extrusion line. -// If there is no previous line, create new stability accumulator. void check_extrusion_entity_stability(const ExtrusionEntity *entity, - StabilityAccumulators &stability_accs, - Issues &issues, - std::vector &checked_lines, + std::vector &checked_lines_out, float print_z, const LayerRegion *layer_region, - const LayerLinesDistancer &prev_layer_lines, + const LinesDistancer &prev_layer_lines, + Issues &issues, const Params ¶ms) { if (entity->is_collection()) { for (const auto *e : static_cast(entity)->entities) { - check_extrusion_entity_stability(e, stability_accs, issues, checked_lines, print_z, layer_region, - prev_layer_lines, - params); + check_extrusion_entity_stability(e, checked_lines_out, print_z, layer_region, prev_layer_lines, + issues, params); } } else { //single extrusion path, with possible varying parameters const auto to_vec3f = [print_z](const Vec2f &point) { @@ -406,8 +335,7 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, entity->collect_points(points); std::vector lines; lines.reserve(points.size() * 1.5); - bool is_ex_perimeter = entity->role() == erExternalPerimeter; - lines.emplace_back(unscaled(points[0]).cast(), unscaled(points[0]).cast(), is_ex_perimeter); + lines.emplace_back(unscaled(points[0]).cast(), unscaled(points[0]).cast(), entity); for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { Vec2f start = unscaled(points[point_idx]).cast(); Vec2f next = unscaled(points[point_idx + 1]).cast(); @@ -419,11 +347,10 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, for (int i = 0; i < lines_count; ++i) { Vec2f a(start + v * (i * step_size)); Vec2f b(start + v * ((i + 1) * step_size)); - lines.emplace_back(a, b, is_ex_perimeter); + lines.emplace_back(a, b, entity); } } - size_t current_stability_acc = NULL_ACC_ID; ExtrusionPropertiesAccumulator bridging_acc { }; ExtrusionPropertiesAccumulator malformation_acc { }; bridging_acc.add_distance(params.bridge_distance + 1.0f); // Initialise unsupported distance with larger than tolerable distance -> @@ -432,8 +359,6 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, for (size_t line_idx = 0; line_idx < lines.size(); ++line_idx) { ExtrusionLine ¤t_line = lines[line_idx]; - float mm3_per_mm = float(entity->min_mm3_per_mm()); - float curr_angle = 0; if (line_idx + 1 < lines.size()) { const Vec2f v1 = current_line.b - current_line.a; @@ -448,28 +373,16 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, float dist_from_prev_layer = prev_layer_lines.signed_distance_from_lines(current_line.b, nearest_line_idx, nearest_point); - if (dist_from_prev_layer < flow_width) { - const ExtrusionLine &nearest_line = prev_layer_lines.get_line(nearest_line_idx); - size_t acc_id = nearest_line.stability_accumulator_id; - stability_accs.merge_accumulators(acc_id, current_stability_acc); - current_stability_acc = std::min(acc_id, current_stability_acc); - current_line.stability_accumulator_id = current_stability_acc; - stability_accs.access(current_stability_acc).add_extrusion(current_line, print_z, mm3_per_mm); + if (fabs(dist_from_prev_layer) < flow_width) { bridging_acc.reset(); } else { bridging_acc.add_distance(current_line.len); - if (current_stability_acc == NULL_ACC_ID) { - current_stability_acc = stability_accs.create_accumulator(); - } - StabilityAccumulator ¤t_segment = stability_accs.access(current_stability_acc); - current_line.stability_accumulator_id = current_stability_acc; - current_segment.add_extrusion(current_line, print_z, mm3_per_mm); if (bridging_acc.distance // if unsupported distance is larger than bridge distance linearly decreased by curvature, enforce supports. > params.bridge_distance / (1.0f + (bridging_acc.max_curvature * params.bridge_distance_decrease_by_curvature_factor / PI))) { - current_segment.add_support_point(current_line.b, 0.0f); // Do not count extrusion supports into the sticking force. They can be very densely placed, causing algorithm to overestimate stickiness. - issues.supports_nedded.emplace_back(to_vec3f(current_line.b), 1.0); + issues.support_points.emplace_back(to_vec3f(current_line.b), 0.0f, Vec3f(0.f, 0.0f, -1.0f)); + current_line.support_point_generated = true; bridging_acc.reset(); } } @@ -481,166 +394,83 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, } if (dist_from_prev_layer > flow_width * 0.3) { malformation_acc.add_distance(current_line.len); - current_line.malformation += 0.15 - * (0.8 + 0.2 * malformation_acc.max_curvature / (1.0f + 0.5f * malformation_acc.distance)); + current_line.malformation += 0.15f + * (0.8f + 0.2f * malformation_acc.max_curvature / (1.0f + 0.5f * malformation_acc.distance)); } else { malformation_acc.reset(); } } - checked_lines.insert(checked_lines.end(), lines.begin(), lines.end()); + checked_lines_out.insert(checked_lines_out.end(), lines.begin(), lines.end()); } } -//check_layer_global_stability checks stability of the accumulators that are still present on the current line -// ( this is determined from the gathered checked_lines vector) -// For each accumulator and each its extrusion, forces and torques (weight, bed movement, extruder pressure, stickness to bed) -// are computed and if stability is not sufficient, support points are added -// accumualtors are filtered by their pointer address, since one accumulator can have multiple IDs due to merging -void check_layer_global_stability(StabilityAccumulators &stability_accs, - VoxelGrid &supports_presence_grid, - Issues &issues, - float flow_width, - const std::vector &checked_lines, - float print_z, +std::tuple reckon_islands( + const Layer *layer, bool first_layer, + size_t prev_layer_islands_count, + const PixelGrid &prev_layer_grid, + const std::vector &layer_lines, const Params ¶ms) { - std::unordered_map> layer_accs_w_lines; - for (size_t i = 0; i < checked_lines.size(); ++i) { - layer_accs_w_lines[&stability_accs.access(checked_lines[i].stability_accumulator_id)].push_back( - checked_lines[i]); - } - for (auto &accumulator : layer_accs_w_lines) { - StabilityAccumulator *acc = accumulator.first; - LayerLinesDistancer acc_lines(std::move(accumulator.second)); - - if (acc->get_support_points().empty()) { - // acc_lines cannot be empty - if the accumulator has no extrusion in the current layer, it is not considered in stability computation - acc->add_support_point(acc_lines.get_line(0).a, 0.0f); - issues.supports_nedded.emplace_back(to_3d(acc_lines.get_line(0).a, print_z), 0.0); - } - const std::vector &support_points = acc->get_support_points(); - - auto coord_fn = [&support_points](size_t idx, size_t dim) { - return support_points[idx][dim]; - }; - KDTreeIndirect<2, float, decltype(coord_fn)> supports_tree(coord_fn, support_points.size()); - - for (const ExtrusionLine &line : acc_lines.get_lines()) { - Vec2f line_dir = (line.b - line.a).normalized(); - Vec2f pivot_site_search_point = line.b + line_dir * 300.0f; - size_t pivot_idx = find_closest_point(supports_tree, pivot_site_search_point); - const Vec2f &pivot = support_points[pivot_idx]; - - const Vec2f &sticking_centroid = acc->get_sticking_centroid(); - float sticking_arm = (pivot - sticking_centroid).norm(); - float sticking_torque = sticking_arm * acc->get_sticking_force(); - - float mass = acc->get_accumulated_volume() * params.filament_density; - const Vec3f &mass_centorid = acc->get_centroid(); - float weight = mass * params.gravity_constant; - float weight_arm = (pivot - mass_centorid.head<2>()).norm(); - float weight_torque = weight_arm * weight; - - float bed_movement_arm = mass_centorid.z(); - float bed_movement_force = params.max_acceleration * mass; - float bed_movement_torque = bed_movement_force * bed_movement_arm; - - Vec3f extruder_pressure_direction = to_3d(line_dir, 0.0f); - extruder_pressure_direction.z() = -0.2 - line.malformation * 0.5; - extruder_pressure_direction.normalize(); - float conflict_torque_arm = (to_3d(Vec2f(pivot - line.b), print_z).cross( - extruder_pressure_direction)).norm(); - float extruder_conflict_force = params.tolerable_extruder_conflict_force + - std::min(line.malformation, 1.0f) * params.malformations_additive_conflict_extruder_force; - float extruder_conflict_torque = extruder_conflict_force * conflict_torque_arm; - - float total_torque = bed_movement_torque + extruder_conflict_torque - weight_torque - sticking_torque; - - if (total_torque > 0) { - Vec2f target_point; - size_t _idx; - acc_lines.signed_distance_from_lines(pivot_site_search_point, _idx, target_point); - if (!supports_presence_grid.position_taken(to_3d(target_point, print_z))) { - float area = params.support_points_interface_radius * params.support_points_interface_radius - * float(PI); - float sticking_force = area * params.support_adhesion; - acc->add_support_point(target_point, sticking_force); - issues.supports_nedded.emplace_back(to_3d(target_point, print_z), - extruder_conflict_torque - sticking_torque); - supports_presence_grid.take_position(to_3d(target_point, print_z)); - } - } -#if 0 - BOOST_LOG_TRIVIAL(debug) - << "SSG: sticking_arm: " << sticking_arm; - BOOST_LOG_TRIVIAL(debug) - << "SSG: sticking_torque: " << sticking_torque; - BOOST_LOG_TRIVIAL(debug) - << "SSG: weight_arm: " << sticking_arm; - BOOST_LOG_TRIVIAL(debug) - << "SSG: weight_torque: " << weight_torque; - BOOST_LOG_TRIVIAL(debug) - << "SSG: bed_movement_arm: " << bed_movement_arm; - BOOST_LOG_TRIVIAL(debug) - << "SSG: bed_movement_torque: " << bed_movement_torque; - BOOST_LOG_TRIVIAL(debug) - << "SSG: conflict_torque_arm: " << conflict_torque_arm; - BOOST_LOG_TRIVIAL(debug) - << "SSG: extruder_conflict_torque: " << extruder_conflict_torque; - BOOST_LOG_TRIVIAL(debug) - << "SSG: total_torque: " << total_torque << " printz: " << print_z; -#endif - } - } -} - -void reckon_thin_islands(StabilityAccumulators &stability_accs, - float flow_width, - float mm3_per_mm, - float print_z, - LayerLinesDistancer &layer_lines, - const Params& params) { - const std::vector &lines = layer_lines.get_lines(); - std::vector> extrusions; //start and end idx (one beyond last extrusion) [start,end) - Vec2f current_pt = lines[0].a; - std::pair current_ext(0,1); - for (size_t lidx = 0; lidx < lines.size(); ++lidx) { - const ExtrusionLine& line = lines[lidx]; - if (line.a == current_pt) { - current_ext.second = lidx + 1; + //extract extrusions (connected paths from multiple lines) from the layer_lines. belonging to single polyline is determined by origin_entity ptr. + // result is a vector of [start, end) index pairs into the layer_lines vector + std::vector> extrusions; //start and end idx (one beyond last extrusion) [start,end) + const ExtrusionEntity *current_ex = nullptr; + for (size_t lidx = 0; lidx < layer_lines.size(); ++lidx) { + const ExtrusionLine &line = layer_lines[lidx]; + if (line.origin_entity == current_ex) { + extrusions.back().second = lidx + 1; } else { - extrusions.push_back(current_ext); - current_ext.first = lidx; - current_ext.second = lidx + 1; + extrusions.emplace_back(lidx, lidx + 1); + current_ex = line.origin_entity; } - current_pt = line.b; } - std::vector islands; - std::vector> island_extrusions; + std::vector islands; // these search trees will be used to determine to which island does the extrusion begin + std::vector> island_extrusions; //final assigment of each extrusion to an island + // initliaze the search from external perimeters - at the beginning, there is island candidate for each external perimeter. + // some of them will disappear (e.g. holes) for (size_t e = 0; e < extrusions.size(); ++e) { - if (lines[extrusions[e].first].external_perimeter) { + if (layer_lines[extrusions[e].first].is_external_perimeter()) { std::vector copy(extrusions[e].second - extrusions[e].first); for (size_t ex_line_idx = extrusions[e].first; ex_line_idx < extrusions[e].second; ++ex_line_idx) { - copy[ex_line_idx-extrusions[e].first] = lines[ex_line_idx]; + copy[ex_line_idx - extrusions[e].first] = layer_lines[ex_line_idx]; } - islands.emplace_back(std::move(copy)); - island_extrusions.push_back({e}); + islands.emplace_back(copy); + island_extrusions.push_back( { e }); } } + // backup code if islands not found - this can currently happen, as external perimeters may be also pure overhang perimeters, and there is no + // way to distinguish external extrusions with total certainty. + // If that happens, just make the first extrusion into island - it may be wrong, but it won't crash. + if (islands.empty() && !extrusions.empty()) { + std::vector copy(extrusions[0].second - extrusions[0].first); + for (size_t ex_line_idx = extrusions[0].first; ex_line_idx < extrusions[0].second; ++ex_line_idx) { + copy[ex_line_idx - extrusions[0].first] = layer_lines[ex_line_idx]; + } + islands.emplace_back(copy); + island_extrusions.push_back( { 0 }); + } - for (size_t i = 0; i < islands.size(); ++i) { - for (size_t e = 0; e < extrusions.size(); ++e) { - if (!lines[extrusions[e].first].external_perimeter){ + // assign non external extrusions to islands + for (size_t e = 0; e < extrusions.size(); ++e) { + if (!layer_lines[extrusions[e].first].is_external_perimeter()) { + bool island_assigned = false; + for (size_t i = 0; i < islands.size(); ++i) { size_t _idx; Vec2f _pt; - if (islands[i].signed_distance_from_lines(lines[extrusions[e].first].a, _idx, _pt) < 0) { + if (islands[i].signed_distance_from_lines(layer_lines[extrusions[e].first].a, _idx, _pt) < 0) { island_extrusions[i].push_back(e); + island_assigned = true; + break; } } + if (!island_assigned) { // If extrusion is not assigned for some reason, push it into the first island. As with the previous backup code, + // it may be wrong, but it won't crash + island_extrusions[0].push_back(e); + } } } - + // merge islands which are embedded within each other (mainly holes) for (size_t i = 0; i < islands.size(); ++i) { if (islands[i].get_lines().empty()) { continue; @@ -659,241 +489,570 @@ void reckon_thin_islands(StabilityAccumulators &stability_accs, } } - size_t islands_count = 0; - for (const std::vector& island_ex : island_extrusions) { + float flow_width = get_flow_width(layer->regions()[0], erExternalPerimeter); + // after filtering the layer lines into islands, build the result LayerIslands structure. + LayerIslands result { }; + result.layer_z = layer->slice_z; + std::vector line_to_island_mapping(layer_lines.size(), NULL_ISLAND); + for (const std::vector &island_ex : island_extrusions) { if (island_ex.empty()) { continue; } - islands_count++; - float cross_section = 0.0f; - size_t acc_id = NULL_ACC_ID; + + Island island { }; + island.external_lines.insert(island.external_lines.end(), + layer_lines.begin() + extrusions[island_ex[0]].first, + layer_lines.begin() + extrusions[island_ex[0]].second); for (size_t extrusion_idx : island_ex) { for (size_t lidx = extrusions[extrusion_idx].first; lidx < extrusions[extrusion_idx].second; ++lidx) { - const ExtrusionLine& line = lines[lidx]; - cross_section += line.len * flow_width; - stability_accs.merge_accumulators(acc_id, line.stability_accumulator_id); - acc_id = std::min(acc_id, line.stability_accumulator_id); + line_to_island_mapping[lidx] = result.islands.size(); + const ExtrusionLine &line = layer_lines[lidx]; + float volume = line.origin_entity->min_mm3_per_mm() * line.len; + island.volume += volume; + island.volume_centroid_accumulator += to_3d(Vec2f((line.a + line.b) / 2.0f), float(layer->print_z)) + * volume; + + if (first_layer) { + float sticking_force = line.len * flow_width * params.base_adhesion; + island.sticking_force += sticking_force; + island.sticking_centroid_accumulator += sticking_force + * to_3d(Vec2f((line.a + line.b) / 2.0f), float(layer->print_z)); + if (line.is_external_perimeter()) { + island.pivot_points.push_back(to_3d(Vec2f(line.b), float(layer->print_z))); + } + } else if (layer_lines[lidx].support_point_generated) { + float support_interface_area = params.support_points_interface_radius + * params.support_points_interface_radius + * float(PI); + float sticking_force = support_interface_area * params.support_adhesion; + island.sticking_force += sticking_force; + island.sticking_centroid_accumulator += sticking_force + * to_3d(Vec2f(line.b), float(layer->print_z)); + island.pivot_points.push_back(to_3d(Vec2f(line.b), float(layer->print_z))); + } } } + result.islands.push_back(island); + } - float max_force = cross_section * params.tensile_strength; - - if (stability_accs.access(acc_id).get_sticking_force() > max_force) { - BOOST_LOG_TRIVIAL(debug) << "SSG: Forking new accumulator for island because tensile strenth is too low: " << max_force; - BOOST_LOG_TRIVIAL(debug) << "SSG: sticking force: " << stability_accs.access(acc_id).get_sticking_force(); - - size_t new_acc_id = stability_accs.create_accumulator(); - StabilityAccumulator& acc = stability_accs.access(new_acc_id); - - for (size_t extrusion_idx : island_ex) { - for (size_t lidx = extrusions[extrusion_idx].first; lidx < extrusions[extrusion_idx].second; ++lidx) { - const ExtrusionLine& line = lines[lidx]; - float tensile_strength = params.tensile_strength * line.len * flow_width; - acc.add_base_extrusion(line, tensile_strength, print_z, mm3_per_mm); - layer_lines.set_new_acc_id(lidx, new_acc_id); + //LayerIslands structure built. Now determine connections and their areas to the previous layer using raterization. + PixelGrid current_layer_grid = prev_layer_grid; + current_layer_grid.clear(); + // build index image of current layer + tbb::parallel_for(tbb::blocked_range(0, layer_lines.size()), + [&layer_lines, ¤t_layer_grid, &line_to_island_mapping]( + tbb::blocked_range r) { + for (size_t i = r.begin(); i < r.end(); ++i) { + size_t island = line_to_island_mapping[i]; + const ExtrusionLine &line = layer_lines[i]; + current_layer_grid.distribute_edge(line.a, line.b, island); } + }); + + //compare the image of previous layer with the current layer. For each pair of overlapping valid pixels, add pixel area to the respective island connection + for (size_t x = 0; x < size_t(current_layer_grid.get_pixel_count().x()); ++x) { + for (size_t y = 0; y < size_t(current_layer_grid.get_pixel_count().y()); ++y) { + Vec2i coords = Vec2i(x, y); + if (current_layer_grid.get_pixel(coords) != NULL_ISLAND + && prev_layer_grid.get_pixel(coords) != NULL_ISLAND) { + IslandConnection& connection = result.islands[current_layer_grid.get_pixel(coords)] + .connected_islands[prev_layer_grid.get_pixel(coords)]; + connection.area += current_layer_grid.pixel_area(); + connection.centroid_accumulator += to_3d(current_layer_grid.get_pixel_center(coords), result.layer_z) * current_layer_grid.pixel_area(); } } } - BOOST_LOG_TRIVIAL(debug) << "SSG: There are " << islands_count << " islands on printz: " << print_z; - + return {result, current_layer_grid}; } -Issues -check_object_stability(const PrintObject *po, const Params ¶ms) { +struct ObjectPart { + float volume { }; + Vec3f volume_centroid_accumulator = Vec3f::Zero(); + float sticking_force { }; + Vec3f sticking_centroid_accumulator = Vec3f::Zero(); + std::vector pivot_points { }; + + void add(const ObjectPart &other) { + this->volume_centroid_accumulator += other.volume_centroid_accumulator; + this->volume += other.volume; + this->sticking_force += other.sticking_force; + this->sticking_centroid_accumulator += other.sticking_centroid_accumulator; + this->pivot_points.insert(this->pivot_points.end(), other.pivot_points.begin(), other.pivot_points.end()); + } + + ObjectPart(const Island &island) { + this->volume = island.volume; + this->volume_centroid_accumulator = island.volume_centroid_accumulator; + this->sticking_force = island.sticking_force; + this->sticking_centroid_accumulator = island.sticking_centroid_accumulator; + this->pivot_points = island.pivot_points; + } + + ObjectPart() = default; +}; + +struct WeakestConnection { + float area = 0.0f; + Vec3f centroid_accumulator = Vec3f::Zero(); + + void add(const WeakestConnection& other) { + this->area += other.area; + this->centroid_accumulator += other.centroid_accumulator; + } +}; + +Issues check_global_stability(const std::vector &islands_graph, const Params ¶ms) { + Issues issues { }; + size_t next_part_idx = 0; + std::unordered_map active_object_parts; + std::unordered_map prev_island_to_object_part_mapping; + std::unordered_map next_island_to_object_part_mapping; + + std::unordered_map prev_island_to_weakest_connection; + std::unordered_map next_island_to_weakest_connection; + + for (size_t layer_idx = 0; layer_idx < islands_graph.size(); ++layer_idx) { + float layer_z = islands_graph[layer_idx].layer_z; + std::unordered_set layer_active_parts; + std::cout << "at layer: " << layer_idx << " the following island to object mapping is used:" << std::endl; + for (const auto &m : prev_island_to_object_part_mapping) { + std::cout << "island " << m.first << " maps to part " << m.second << std::endl; + Vec3f connection_center = prev_island_to_weakest_connection[m.first].centroid_accumulator / prev_island_to_weakest_connection[m.first].area; + std::cout << " island has weak point with connection area: " << + prev_island_to_weakest_connection[m.first].area << " and center: " << + connection_center.x() << " " << connection_center.y() << " " << connection_center.z() << std::endl; + } + + for (size_t island_idx = 0; island_idx < islands_graph[layer_idx].islands.size(); ++island_idx) { + const Island &island = islands_graph[layer_idx].islands[island_idx]; + if (island.connected_islands.empty()) { //new object part emerging + size_t part_idx = next_part_idx; + next_part_idx++; + active_object_parts.emplace(part_idx, ObjectPart(island)); + next_island_to_object_part_mapping.emplace(island_idx, part_idx); + next_island_to_weakest_connection.emplace(island_idx, + WeakestConnection{INFINITY, Vec3f::Zero()}); + layer_active_parts.insert(part_idx); + } else { + size_t final_part_idx{}; + WeakestConnection transfered_weakest_connection{}; + WeakestConnection new_weakest_connection{}; + // MERGE parts + { + std::unordered_set part_indices; + for (const auto &connection : island.connected_islands) { + part_indices.insert(prev_island_to_object_part_mapping.at(connection.first)); + transfered_weakest_connection.add(prev_island_to_weakest_connection.at(connection.first)); + new_weakest_connection.area += connection.second.area; + new_weakest_connection.centroid_accumulator += connection.second.centroid_accumulator; + } + final_part_idx = *part_indices.begin(); + for (size_t part_idx : part_indices) { + if (final_part_idx != part_idx) { + std::cout << "at layer: " << layer_idx << " merging object part: " << part_idx + << " into final part: " << final_part_idx << std::endl; + active_object_parts.at(final_part_idx).add(active_object_parts.at(part_idx)); + active_object_parts.erase(part_idx); + } + } + } + auto estimate_strength = [layer_z](const WeakestConnection& conn){ + float radius = fsqrt(conn.area / PI); + float arm_len_estimate = std::max(0.001f, layer_z - (conn.centroid_accumulator.z() / conn.area)); + return radius * conn.area / arm_len_estimate; + }; + + if (estimate_strength(transfered_weakest_connection) < estimate_strength(new_weakest_connection)) { + new_weakest_connection = transfered_weakest_connection; + } + next_island_to_weakest_connection.emplace(island_idx, new_weakest_connection); + next_island_to_object_part_mapping.emplace(island_idx, final_part_idx); + ObjectPart &part = active_object_parts[final_part_idx]; + part.add(ObjectPart(island)); + layer_active_parts.insert(final_part_idx); + } + } + + std::unordered_set parts_to_delete; + for (const auto &part : active_object_parts) { + if (layer_active_parts.find(part.first) == layer_active_parts.end()) { + parts_to_delete.insert(part.first); + } else { + std::cout << "at layer " << layer_idx << " part is still active: " << part.first << std::endl; + } + } + for (size_t part_id : parts_to_delete) { + active_object_parts.erase(part_id); + std::cout << " at layer: " << layer_idx << " removing object part " << part_id << std::endl; + } + prev_island_to_object_part_mapping = next_island_to_object_part_mapping; + next_island_to_object_part_mapping.clear(); + prev_island_to_weakest_connection = next_island_to_weakest_connection; + next_island_to_weakest_connection.clear(); + + } + return issues; +} + +/* + + // islands_graph.back() refers to the top most (current) layer + for (size_t island_idx = 0; island_idx < islands_graph.back().islands.size(); ++island_idx) { + Island &island = islands_graph.back().islands[island_idx]; + + std::vector island_external_lines; + for (size_t lidx : islands_lines[island_idx]) { + island_external_lines.push_back(layer_lines[lidx]); + } + LinesDistancer island_lines_dist(island_external_lines); + Accumulator acc = island; // in acc, we accumulate the mass and other properties of the object part as we traverse the islands down to bed + // There is one object part for each island at the top most layer, and each one is computed individually - + // Some of the calculations will be done multiple times + int layer_idx = islands_graph.size() - 1; + // traverse the islands graph down, and for each connection area, calculate if it holds or breaks + while (acc.connected_islands.size() > 0) { + //test for break between layer_idx and layer_idx -1; + LayerIslands below = islands_graph[layer_idx - 1]; // must exist, see while condition + layer_idx--; + // initialize variables that we will accumulate over all islands, which are connected to the current object part + std::vector pivot_points; + Vec2f sticking_centroid; + float connection_area = 0; + for (const auto &pair : acc.connected_islands) { + const Island &below_i = below.islands[pair.first]; + Vec2f centroid = (below_i.volume_centroid_accumulator / below_i.volume).head<2>(); // centroid of the island 'below_i'; TODO it should be centroid of the connection area + pivot_points.push_back(centroid); // for object parts, we also consider breaking pivots in the centroids of the islands + sticking_centroid += centroid * pair.second; // pair.second is connection area in mm^2 + connection_area += pair.second; + } + sticking_centroid /= connection_area; //normalize to get final sticking centroid + for (const Vec3f &p_point : acc.pivot_points) { + pivot_points.push_back(p_point.head<2>()); + } + // Now we have accumulated pivot points, connection area and sticking centroid of the whole layer to the current object part + + // create KD tree over current pivot points + auto coord_fn = [&pivot_points](size_t idx, size_t dim) { + return pivot_points[idx][dim]; + }; + KDTreeIndirect<2, float, decltype(coord_fn)> pivot_points_tree(coord_fn, pivot_points.size()); + + // iterate over extrusions at top layer island, check each for stability + for (const ExtrusionLine &line : island_external_lines) { + Vec2f line_dir = (line.b - line.a).normalized(); + Vec2f pivot_site_search_point = line.b + line_dir * 300.0f; + size_t pivot_idx = find_closest_point(pivot_points_tree, pivot_site_search_point); + const Vec2f &pivot = pivot_points[pivot_idx]; + + float sticking_arm = (pivot - sticking_centroid).norm(); + float sticking_torque = sticking_arm * connection_area * params.tensile_strength; // For breakage in between layers, we compute with tensile strength, not bed adhesion + + float mass = acc.volume * params.filament_density; + const Vec3f &mass_centorid = acc.volume_centroid_accumulator / acc.volume; + float weight = mass * params.gravity_constant; + float weight_arm = (pivot - mass_centorid.head<2>()).norm(); + float weight_torque = weight_arm * weight; + + float bed_movement_arm = mass_centorid.z(); + float bed_movement_force = params.max_acceleration * mass; + float bed_movement_torque = bed_movement_force * bed_movement_arm; + + Vec3f extruder_pressure_direction = to_3d(line_dir, 0.0f); + extruder_pressure_direction.z() = -0.2 - line.malformation * 0.5; + extruder_pressure_direction.normalize(); + float conflict_torque_arm = (to_3d(Vec2f(pivot - line.b), print_z).cross( + extruder_pressure_direction)).norm(); + float extruder_conflict_force = params.tolerable_extruder_conflict_force + + std::min(line.malformation, 1.0f) * params.malformations_additive_conflict_extruder_force; + float extruder_conflict_torque = extruder_conflict_force * conflict_torque_arm; + + float total_torque = bed_movement_torque + extruder_conflict_torque - weight_torque - sticking_torque; + + if (total_torque > 0) { + Vec2f target_point { }; + size_t _idx { }; + island_lines_dist.signed_distance_from_lines(pivot_site_search_point, _idx, target_point); + if (!supports_presence_grid.position_taken(to_3d(target_point, print_z))) { + float area = params.support_points_interface_radius * params.support_points_interface_radius + * float(PI); + float sticking_force = area * params.support_adhesion; + Vec3f support_point = to_3d(target_point, print_z); + island.pivot_points.push_back(support_point); + island.sticking_force += sticking_force; + island.sticking_centroid_accumulator += sticking_force * support_point; + issues.support_points.emplace_back(support_point, + extruder_conflict_torque - sticking_torque, extruder_pressure_direction); + supports_presence_grid.take_position(to_3d(target_point, print_z)); + } + } + #if 0 + BOOST_LOG_TRIVIAL(debug) + << "SSG: sticking_arm: " << sticking_arm; + BOOST_LOG_TRIVIAL(debug) + << "SSG: sticking_torque: " << sticking_torque; + BOOST_LOG_TRIVIAL(debug) + << "SSG: weight_arm: " << sticking_arm; + BOOST_LOG_TRIVIAL(debug) + << "SSG: weight_torque: " << weight_torque; + BOOST_LOG_TRIVIAL(debug) + << "SSG: bed_movement_arm: " << bed_movement_arm; + BOOST_LOG_TRIVIAL(debug) + << "SSG: bed_movement_torque: " << bed_movement_torque; + BOOST_LOG_TRIVIAL(debug) + << "SSG: conflict_torque_arm: " << conflict_torque_arm; + BOOST_LOG_TRIVIAL(debug) + << "SSG: extruder_conflict_torque: " << extruder_conflict_torque; + BOOST_LOG_TRIVIAL(debug) + << "SSG: total_torque: " << total_torque << " printz: " << print_z; + #endif + } + + std::unordered_map tmp = acc.connected_islands; + acc.connected_islands.clear(); + // finally, add gathered islands to accumulator, and continue down to next layer + for (const auto &pair : tmp) { + const Island &below_i = below.islands[pair.first]; + for (const auto &below_islands : below_i.connected_islands) { + acc.connected_islands[below_islands.first] += below_islands.second; + } + for (const Vec3f &pivot_p : below_i.pivot_points) { + acc.pivot_points.push_back(pivot_p); + } + acc.sticking_centroid_accumulator += below_i.sticking_centroid_accumulator; + acc.sticking_force += below_i.sticking_force; + acc.volume += below_i.volume; + acc.volume_centroid_accumulator += below_i.volume_centroid_accumulator; + } + } + + // We have arrived to the bed level, now check for stability of the object part on the bed + std::vector pivot_points; + for (const Vec3f &p_point : acc.pivot_points) { + pivot_points.push_back(p_point.head<2>()); + } + auto coord_fn = [&pivot_points](size_t idx, size_t dim) { + return pivot_points[idx][dim]; + }; + KDTreeIndirect<2, float, decltype(coord_fn)> pivot_points_tree(coord_fn, pivot_points.size()); + + for (const ExtrusionLine &line : island_external_lines) { + Vec2f line_dir = (line.b - line.a).normalized(); + Vec2f pivot_site_search_point = line.b + line_dir * 300.0f; + size_t pivot_idx = find_closest_point(pivot_points_tree, pivot_site_search_point); + const Vec2f &pivot = pivot_points[pivot_idx]; + + const Vec2f &sticking_centroid = acc.sticking_centroid_accumulator.head<2>() / acc.sticking_force; + float sticking_arm = (pivot - sticking_centroid).norm(); + float sticking_torque = sticking_arm * acc.sticking_force; + + float mass = acc.volume * params.filament_density; + const Vec3f &mass_centorid = acc.volume_centroid_accumulator / acc.volume; + float weight = mass * params.gravity_constant; + float weight_arm = (pivot - mass_centorid.head<2>()).norm(); + float weight_torque = weight_arm * weight; + + float bed_movement_arm = mass_centorid.z(); + float bed_movement_force = params.max_acceleration * mass; + float bed_movement_torque = bed_movement_force * bed_movement_arm; + + Vec3f extruder_pressure_direction = to_3d(line_dir, 0.0f); + extruder_pressure_direction.z() = -0.2 - line.malformation * 0.5; + extruder_pressure_direction.normalize(); + float conflict_torque_arm = (to_3d(Vec2f(pivot - line.b), print_z).cross( + extruder_pressure_direction)).norm(); + float extruder_conflict_force = params.tolerable_extruder_conflict_force + + std::min(line.malformation, 1.0f) * params.malformations_additive_conflict_extruder_force; + float extruder_conflict_torque = extruder_conflict_force * conflict_torque_arm; + + float total_torque = bed_movement_torque + extruder_conflict_torque - weight_torque - sticking_torque; + + if (total_torque > 0) { + Vec2f target_point; + size_t _idx; + island_lines_dist.signed_distance_from_lines(pivot_site_search_point, _idx, target_point); + if (!supports_presence_grid.position_taken(to_3d(target_point, print_z))) { + float area = params.support_points_interface_radius * params.support_points_interface_radius + * float(PI); + float sticking_force = area * params.support_adhesion; + Vec3f support_point = to_3d(target_point, print_z); + island.pivot_points.push_back(support_point); + island.sticking_force += sticking_force; + island.sticking_centroid_accumulator += sticking_force * support_point; + issues.support_points.emplace_back(support_point, + extruder_conflict_torque - sticking_torque, extruder_pressure_direction); + supports_presence_grid.take_position(to_3d(target_point, print_z)); + } + } + #if 0 + BOOST_LOG_TRIVIAL(debug) + << "SSG: sticking_arm: " << sticking_arm; + BOOST_LOG_TRIVIAL(debug) + << "SSG: sticking_torque: " << sticking_torque; + BOOST_LOG_TRIVIAL(debug) + << "SSG: weight_arm: " << sticking_arm; + BOOST_LOG_TRIVIAL(debug) + << "SSG: weight_torque: " << weight_torque; + BOOST_LOG_TRIVIAL(debug) + << "SSG: bed_movement_arm: " << bed_movement_arm; + BOOST_LOG_TRIVIAL(debug) + << "SSG: bed_movement_torque: " << bed_movement_torque; + BOOST_LOG_TRIVIAL(debug) + << "SSG: conflict_torque_arm: " << conflict_torque_arm; + BOOST_LOG_TRIVIAL(debug) + << "SSG: extruder_conflict_torque: " << extruder_conflict_torque; + BOOST_LOG_TRIVIAL(debug) + << "SSG: total_torque: " << total_torque << " printz: " << print_z; + #endif + } + } + + return issues; + */ + +std::tuple> check_extrusions_and_build_graph(const PrintObject *po, + const Params ¶ms) { #ifdef DEBUG_FILES - FILE *debug_acc = boost::nowide::fopen(debug_out_path("accumulators.obj").c_str(), "w"); + FILE *segmentation_f = boost::nowide::fopen(debug_out_path("segmentation.obj").c_str(), "w"); FILE *malform_f = boost::nowide::fopen(debug_out_path("malformations.obj").c_str(), "w"); #endif - StabilityAccumulators stability_accs; - LayerLinesDistancer prev_layer_lines { { } }; + Issues issues { }; - std::vector checked_lines; - VoxelGrid supports_presence_grid { po, params.min_distance_between_support_points }; + std::vector islands_graph; + std::vector layer_lines; + float flow_width = get_flow_width(po->layers()[po->layer_count() - 1]->regions()[0], erExternalPerimeter); + PixelGrid prev_layer_grid(po, flow_width); // PREPARE BASE LAYER - float max_flow_width = 0.0f; const Layer *layer = po->layers()[0]; - float base_print_z = layer->print_z; for (const LayerRegion *layer_region : layer->regions()) { for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { - const float flow_width = get_flow_width(layer_region, perimeter->role()); - max_flow_width = std::max(flow_width, max_flow_width); - const float mm3_per_mm = float(perimeter->min_mm3_per_mm()); - int id = stability_accs.create_accumulator(); - StabilityAccumulator &acc = stability_accs.access(id); Points points { }; perimeter->collect_points(points); for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { Vec2f start = unscaled(points[point_idx]).cast(); Vec2f next = unscaled(points[point_idx + 1]).cast(); - ExtrusionLine line { start, next, perimeter->role() == erExternalPerimeter }; - line.stability_accumulator_id = id; - float line_sticking_force = line.len * flow_width * params.base_adhesion; - acc.add_base_extrusion(line, line_sticking_force, base_print_z, mm3_per_mm); - checked_lines.push_back(line); + ExtrusionLine line { start, next, perimeter }; + layer_lines.push_back(line); } if (perimeter->is_loop()) { Vec2f start = unscaled(points[points.size() - 1]).cast(); Vec2f next = unscaled(points[0]).cast(); - ExtrusionLine line { start, next, perimeter->role() == erExternalPerimeter }; - line.stability_accumulator_id = id; - float line_sticking_force = line.len * flow_width * params.base_adhesion; - acc.add_base_extrusion(line, line_sticking_force, base_print_z, mm3_per_mm); - checked_lines.push_back(line); + ExtrusionLine line { start, next, perimeter }; + layer_lines.push_back(line); } } // perimeter } // ex_entity for (const ExtrusionEntity *ex_entity : layer_region->fills.entities) { for (const ExtrusionEntity *fill : static_cast(ex_entity)->entities) { - const float flow_width = get_flow_width(layer_region, fill->role()); - max_flow_width = std::max(flow_width, max_flow_width); - const float mm3_per_mm = float(fill->min_mm3_per_mm()); - int id = stability_accs.create_accumulator(); - StabilityAccumulator &acc = stability_accs.access(id); Points points { }; fill->collect_points(points); for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { Vec2f start = unscaled(points[point_idx]).cast(); Vec2f next = unscaled(points[point_idx + 1]).cast(); - ExtrusionLine line { start, next, false }; - line.stability_accumulator_id = id; - float line_sticking_force = line.len * flow_width * params.base_adhesion; - acc.add_base_extrusion(line, line_sticking_force, base_print_z, mm3_per_mm); - checked_lines.push_back(line); + ExtrusionLine line { start, next, fill }; + layer_lines.push_back(line); } } // fill } // ex_entity } // region - //MERGE BASE LAYER STABILITY ACCS - prev_layer_lines = LayerLinesDistancer { std::move(checked_lines) }; - for (const ExtrusionLine &l : prev_layer_lines.get_lines()) { - size_t nearest_line_idx; - Vec2f nearest_pt; - Vec2f line_dir = (l.b - l.a).normalized(); - Vec2f site_search_location = l.a + Vec2f(line_dir.y(), -line_dir.x()) * max_flow_width; - float dist = prev_layer_lines.signed_distance_from_lines(site_search_location, nearest_line_idx, nearest_pt); - if (std::abs(dist) < max_flow_width) { - size_t other_line_acc_id = prev_layer_lines.get_line(nearest_line_idx).stability_accumulator_id; - stability_accs.merge_accumulators(other_line_acc_id, l.stability_accumulator_id); + auto [layer_islands, layer_grid] = reckon_islands(layer, true, 0, prev_layer_grid, + layer_lines, params); + islands_graph.push_back(std::move(layer_islands)); +#ifdef DEBUG_FILES + for (size_t x = 0; x < size_t(layer_grid.get_pixel_count().x()); ++x) { + for (size_t y = 0; y < size_t(layer_grid.get_pixel_count().y()); ++y) { + Vec2i coords = Vec2i(x, y); + size_t island_idx = layer_grid.get_pixel(coords); + if (layer_grid.get_pixel(coords) != NULL_ISLAND) { + Vec2f pos = layer_grid.get_pixel_center(coords); + size_t pseudornd = ((island_idx + 127) * 33331 + 6907) % 23; + Vec3f color = value_to_rgbf(0.0f, float(23), float(pseudornd)); + fprintf(segmentation_f, "v %f %f %f %f %f %f\n", pos[0], + pos[1], layer->print_z, color[0], color[1], color[2]); + } } } - -#ifdef DEBUG_FILES - for (const auto &line : prev_layer_lines.get_lines()) { - Vec3f color = stability_accs.get_accumulator_color(line.stability_accumulator_id); - fprintf(debug_acc, "v %f %f %f %f %f %f\n", line.b[0], - line.b[1], base_print_z, color[0], color[1], color[2]); + for (const auto &line : layer_lines) { + if (line.malformation > 0.0f) { + Vec3f color = value_to_rgbf(0, 1.0f, line.malformation); + fprintf(malform_f, "v %f %f %f %f %f %f\n", line.b[0], + line.b[1], layer->print_z, color[0], color[1], color[2]); + } } - - stability_accs.log_accumulators(); #endif + LinesDistancer external_lines(layer_lines); + layer_lines.clear(); + prev_layer_grid = layer_grid; - //CHECK STABILITY OF ALL LAYERS for (size_t layer_idx = 1; layer_idx < po->layer_count(); ++layer_idx) { const Layer *layer = po->layers()[layer_idx]; - checked_lines = std::vector { }; - std::vector> fill_points; - float max_fill_flow_width = 0.0f; - - float print_z = layer->print_z; for (const LayerRegion *layer_region : layer->regions()) { for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { - check_extrusion_entity_stability(perimeter, stability_accs, issues, checked_lines, print_z, - layer_region, - prev_layer_lines, params); + check_extrusion_entity_stability(perimeter, layer_lines, layer->print_z, layer_region, + external_lines, issues, params); } // perimeter } // ex_entity for (const ExtrusionEntity *ex_entity : layer_region->fills.entities) { for (const ExtrusionEntity *fill : static_cast(ex_entity)->entities) { if (fill->role() == ExtrusionRole::erGapFill || fill->role() == ExtrusionRole::erBridgeInfill) { - check_extrusion_entity_stability(fill, stability_accs, issues, checked_lines, print_z, - layer_region, - prev_layer_lines, params); + check_extrusion_entity_stability(fill, layer_lines, layer->print_z, layer_region, + external_lines, issues, params); } else { - const float flow_width = get_flow_width(layer_region, fill->role()); - max_fill_flow_width = std::max(max_fill_flow_width, flow_width); - Vec2f start = unscaled(fill->first_point()).cast(); - size_t nearest_line_idx; - Vec2f nearest_pt; - float dist = prev_layer_lines.signed_distance_from_lines(start, nearest_line_idx, nearest_pt); - if (dist < flow_width) { - size_t acc_id = prev_layer_lines.get_line(nearest_line_idx).stability_accumulator_id; - StabilityAccumulator &acc = stability_accs.access(acc_id); - Points points { }; - const float mm3_per_mm = float(fill->min_mm3_per_mm()); - fill->collect_points(points); - for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { - Vec2f start = unscaled(points[point_idx]).cast(); - Vec2f next = unscaled(points[point_idx + 1]).cast(); - ExtrusionLine line { start, next, false }; - line.stability_accumulator_id = acc_id; - acc.add_extrusion(line, print_z, mm3_per_mm); - } - fill_points.emplace_back(start, acc_id); - } else { - BOOST_LOG_TRIVIAL(debug) - << "SSG: ERROR: seem that infill starts in the air? on printz: " << print_z; + Points points { }; + fill->collect_points(points); + for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { + Vec2f start = unscaled(points[point_idx]).cast(); + Vec2f next = unscaled(points[point_idx + 1]).cast(); + ExtrusionLine line { start, next, fill }; + layer_lines.push_back(line); } } } // fill } // ex_entity } // region - prev_layer_lines = LayerLinesDistancer { std::move(checked_lines) }; - - for (const std::pair &fill_point : fill_points) { - size_t nearest_line_idx; - Vec2f nearest_pt; - float dist = prev_layer_lines.signed_distance_from_lines(fill_point.first, nearest_line_idx, nearest_pt); - if (dist < max_fill_flow_width) { - size_t other_line_acc_id = prev_layer_lines.get_line(nearest_line_idx).stability_accumulator_id; - stability_accs.merge_accumulators(other_line_acc_id, fill_point.second); - } else { - BOOST_LOG_TRIVIAL(debug) - << "SSG: ERROR: seem that infill starts in the air? on printz: " << print_z; - } - } - - float flow_width = get_flow_width(layer->regions()[0], erExternalPerimeter); - - check_layer_global_stability(stability_accs, - supports_presence_grid, - issues, - flow_width, - prev_layer_lines.get_lines(), - print_z, - params); - - reckon_thin_islands(stability_accs, flow_width, flow_width*layer->height, print_z, prev_layer_lines, params); + auto [layer_islands, layer_grid] = reckon_islands(layer, true, 0, prev_layer_grid, + layer_lines, params); + islands_graph.push_back(std::move(layer_islands)); #ifdef DEBUG_FILES - for (const auto &line : prev_layer_lines.get_lines()) { - Vec3f color = value_to_rgbf(0, 1.0f, line.malformation); - fprintf(malform_f, "v %f %f %f %f %f %f\n", line.b[0], - line.b[1], print_z, color[0], color[1], color[2]); + for (size_t x = 0; x < size_t(layer_grid.get_pixel_count().x()); ++x) { + for (size_t y = 0; y < size_t(layer_grid.get_pixel_count().y()); ++y) { + Vec2i coords = Vec2i(x, y); + size_t island_idx = layer_grid.get_pixel(coords); + if (layer_grid.get_pixel(coords) != NULL_ISLAND) { + Vec2f pos = layer_grid.get_pixel_center(coords); + size_t pseudornd = ((island_idx + 127) * 33331 + 6907) % 23; + Vec3f color = value_to_rgbf(0.0f, float(23), float(pseudornd)); + fprintf(segmentation_f, "v %f %f %f %f %f %f\n", pos[0], + pos[1], layer->print_z, color[0], color[1], color[2]); + } + } } - for (const auto &line : prev_layer_lines.get_lines()) { - Vec3f color = stability_accs.get_accumulator_color(line.stability_accumulator_id); - fprintf(debug_acc, "v %f %f %f %f %f %f\n", line.b[0], - line.b[1], print_z, color[0], color[1], color[2]); + for (const auto &line : layer_lines) { + if (line.malformation > 0.0f) { + Vec3f color = value_to_rgbf(0, 1.0f, line.malformation); + fprintf(malform_f, "v %f %f %f %f %f %f\n", line.b[0], + line.b[1], layer->print_z, color[0], color[1], color[2]); + } } - stability_accs.log_accumulators(); #endif + external_lines = LinesDistancer(layer_lines); + layer_lines.clear(); + prev_layer_grid = layer_grid; } #ifdef DEBUG_FILES - fclose(debug_acc); + fclose(segmentation_f); fclose(malform_f); #endif - std::cout << " SUPP: " << issues.supports_nedded.size() << std::endl; - return issues; + return {issues, islands_graph}; } #ifdef DEBUG_FILES @@ -907,46 +1066,35 @@ void debug_export(Issues issues, std::string file_name) { return; } - for (size_t i = 0; i < issues.supports_nedded.size(); ++i) { - fprintf(fp, "v %f %f %f %f %f %f\n", issues.supports_nedded[i].position(0), - issues.supports_nedded[i].position(1), - issues.supports_nedded[i].position(2), 1.0, 0.0, 1.0); + for (size_t i = 0; i < issues.support_points.size(); ++i) { + fprintf(fp, "v %f %f %f %f %f %f\n", issues.support_points[i].position(0), + issues.support_points[i].position(1), + issues.support_points[i].position(2), 1.0, 0.0, 1.0); } fclose(fp); } - { - FILE *fp = boost::nowide::fopen(debug_out_path((file_name + "_curling.obj").c_str()).c_str(), "w"); - if (fp == nullptr) { - BOOST_LOG_TRIVIAL(error) - << "Debug files: Couldn't open " << file_name << " for writing"; - return; - } - - for (size_t i = 0; i < issues.curling_up.size(); ++i) { - fprintf(fp, "v %f %f %f %f %f %f\n", issues.curling_up[i].position(0), - issues.curling_up[i].position(1), - issues.curling_up[i].position(2), 0.0, 1.0, 0.0); - } - fclose(fp); - } } #endif std::vector quick_search(const PrintObject *po, const Params ¶ms) { - check_object_stability(po, params); return {}; } -Issues -full_search(const PrintObject *po, const Params ¶ms) { - auto issues = check_object_stability(po, params); +Issues full_search(const PrintObject *po, const Params ¶ms) { + auto [local_issues, graph] = check_extrusions_and_build_graph(po, params); + Issues global_issues = check_global_stability(graph, params); #ifdef DEBUG_FILES - debug_export(issues, "issues"); + debug_export(local_issues, "local_issues"); + debug_export(global_issues, "global_issues"); #endif - return issues; + global_issues.support_points.insert(global_issues.support_points.end(), + local_issues.support_points.begin(), local_issues.support_points.end()); + + return global_issues; } + } //SupportableIssues End } diff --git a/src/libslic3r/SupportSpotsGeneratorRefactoring.cpp b/src/libslic3r/SupportSpotsGeneratorRefactoring.cpp deleted file mode 100644 index a2275c6372..0000000000 --- a/src/libslic3r/SupportSpotsGeneratorRefactoring.cpp +++ /dev/null @@ -1,972 +0,0 @@ -#include "SupportSpotsGenerator.hpp" - -#include "tbb/parallel_for.h" -#include "tbb/blocked_range.h" -#include "tbb/blocked_range2d.h" -#include "tbb/parallel_reduce.h" -#include -#include -#include -#include - -#include "AABBTreeLines.hpp" -#include "KDTreeIndirect.hpp" -#include "libslic3r/Layer.hpp" -#include "libslic3r/ClipperUtils.hpp" -#include "Geometry/ConvexHull.hpp" - -#define DEBUG_FILES - -#ifdef DEBUG_FILES -#include -#include "libslic3r/Color.hpp" -#endif - -namespace Slic3r { - -class ExtrusionLine -{ -public: - ExtrusionLine() : - a(Vec2f::Zero()), b(Vec2f::Zero()), len(0.0f), origin_entity(nullptr) { - } - ExtrusionLine(const Vec2f &_a, const Vec2f &_b, const ExtrusionEntity *origin_entity) : - a(_a), b(_b), len((_a - _b).norm()), origin_entity(origin_entity) { - } - - float length() { - return (a - b).norm(); - } - - bool is_external_perimeter() const { - assert(origin_entity != nullptr); - return origin_entity->role() == erExternalPerimeter; - } - - Vec2f a; - Vec2f b; - float len; - const ExtrusionEntity *origin_entity; - - bool support_point_generated = false; - float malformation = 0.0f; - - static const constexpr int Dim = 2; - using Scalar = Vec2f::Scalar; -}; - -auto get_a(ExtrusionLine &&l) { - return l.a; -} -auto get_b(ExtrusionLine &&l) { - return l.b; -} - -namespace SupportSpotsGenerator { - -SupportPoint::SupportPoint(const Vec3f &position, float force, const Vec3f &direction) : - position(position), force(force), direction(direction) { -} - -class LinesDistancer { -private: - std::vector lines; - AABBTreeIndirect::Tree<2, float> tree; - -public: - explicit LinesDistancer(std::vector &lines) : - lines(lines) { - tree = AABBTreeLines::build_aabb_tree_over_indexed_lines(this->lines); - } - - // negative sign means inside - float signed_distance_from_lines(const Vec2f &point, size_t &nearest_line_index_out, - Vec2f &nearest_point_out) const { - auto distance = AABBTreeLines::squared_distance_to_indexed_lines(lines, tree, point, nearest_line_index_out, - nearest_point_out); - if (distance < 0) - return std::numeric_limits::infinity(); - - distance = sqrt(distance); - const ExtrusionLine &line = lines[nearest_line_index_out]; - Vec2f v1 = line.b - line.a; - Vec2f v2 = point - line.a; - if ((v1.x() * v2.y()) - (v1.y() * v2.x()) > 0.0) { - distance *= -1; - } - return distance; - } - - const ExtrusionLine& get_line(size_t line_idx) const { - return lines[line_idx]; - } - - const std::vector& get_lines() const { - return lines; - } -}; - -static const size_t NULL_ISLAND = std::numeric_limits::max(); - -class PixelGrid { - Vec2f pixel_size; - Vec2f origin; - Vec2f size; - Vec2i pixel_count; - - std::vector pixels { }; - -public: - PixelGrid(const PrintObject *po, float resolution) { - pixel_size = Vec2f(resolution, resolution); - - Vec2crd size_half = po->size().head<2>().cwiseQuotient(Vec2crd(2, 2)) + Vec2crd::Ones(); - Vec2f min = unscale(Vec2crd(-size_half.x(), -size_half.y())).cast(); - Vec2f max = unscale(Vec2crd(size_half.x(), size_half.y())).cast(); - - origin = min; - size = max - min; - pixel_count = size.cwiseQuotient(pixel_size).cast() + Vec2i::Ones(); - - pixels.resize(pixel_count.y() * pixel_count.x()); - clear(); - } - - void distribute_edge(const Vec2f &p1, const Vec2f &p2, size_t value) { - Vec2f dir = (p2 - p1); - float length = dir.norm(); - if (length < 0.1) { - return; - } - float step_size = this->pixel_size.x() / 2.0; - - float distributed_length = 0; - while (distributed_length < length) { - float next_len = std::min(length, distributed_length + step_size); - Vec2f location = p1 + ((next_len / length) * dir); - this->access_pixel(location) = value; - - distributed_length = next_len; - } - } - - void clear() { - for (size_t &val : pixels) { - val = NULL_ISLAND; - } - } - - float pixel_area() const { - return this->pixel_size.x() * this->pixel_size.y(); - } - - size_t get_pixel(const Vec2i &coords) const { - return pixels[this->to_pixel_index(coords)]; - } - - Vec2i get_pixel_count() { - return pixel_count; - } - - Vec2f get_pixel_center(const Vec2i &coords) const { - return origin + coords.cast().cwiseProduct(this->pixel_size) - + this->pixel_size.cwiseQuotient(Vec2f(2.0f, 2.0f)); - } - -private: - Vec2i to_pixel_coords(const Vec2f &position) const { - Vec2i pixel_coords = (position - this->origin).cwiseQuotient(this->pixel_size).cast(); - return pixel_coords; - } - - size_t to_pixel_index(const Vec2i &pixel_coords) const { - assert(pixel_coords.x() >= 0); - assert(pixel_coords.x() < pixel_count.x()); - assert(pixel_coords.y() >= 0); - assert(pixel_coords.y() < pixel_count.y()); - - return pixel_coords.y() * pixel_count.x() + pixel_coords.x(); - } - - size_t& access_pixel(const Vec2f &position) { - return pixels[this->to_pixel_index(this->to_pixel_coords(position))]; - } -}; - -struct SupportGridFilter { -private: - Vec3f cell_size; - Vec3f origin; - Vec3f size; - Vec3i cell_count; - - std::unordered_set taken_cells { }; - -public: - SupportGridFilter(const PrintObject *po, float voxel_size) { - cell_size = Vec3f(voxel_size, voxel_size, voxel_size); - - Vec2crd size_half = po->size().head<2>().cwiseQuotient(Vec2crd(2, 2)) + Vec2crd::Ones(); - Vec3f min = unscale(Vec3crd(-size_half.x(), -size_half.y(), 0)).cast() - cell_size; - Vec3f max = unscale(Vec3crd(size_half.x(), size_half.y(), po->height())).cast() + cell_size; - - origin = min; - size = max - min; - cell_count = size.cwiseQuotient(cell_size).cast() + Vec3i::Ones(); - } - - Vec3i to_cell_coords(const Vec3f &position) const { - Vec3i cell_coords = (position - this->origin).cwiseQuotient(this->cell_size).cast(); - return cell_coords; - } - - size_t to_cell_index(const Vec3i &cell_coords) const { - assert(cell_coords.x() >= 0); - assert(cell_coords.x() < cell_count.x()); - assert(cell_coords.y() >= 0); - assert(cell_coords.y() < cell_count.y()); - assert(cell_coords.z() >= 0); - assert(cell_coords.z() < cell_count.z()); - - return cell_coords.z() * cell_count.x() * cell_count.y() - + cell_coords.y() * cell_count.x() - + cell_coords.x(); - } - - Vec3f get_cell_center(const Vec3i &cell_coords) const { - return origin + cell_coords.cast().cwiseProduct(this->cell_size) - + this->cell_size.cwiseQuotient(Vec3f(2.0f, 2.0f, 2.0)); - } - - void take_position(const Vec3f &position) { - taken_cells.insert(to_cell_index(to_cell_coords(position))); - } - - bool position_taken(const Vec3f &position) const { - return taken_cells.find(to_cell_index(to_cell_coords(position))) != taken_cells.end(); - } - -}; - -struct Island { - std::unordered_map islands_under_with_connection_area; - std::vector pivot_points; - float volume; - Vec3f volume_centroid_accumulator; - float sticking_force; // for support points present on this layer (or bed extrusions) - Vec3f sticking_centroid_accumulator; -}; - -struct LayerIslands { - std::vector islands; -}; - -float get_flow_width(const LayerRegion *region, ExtrusionRole role) { - switch (role) { - case ExtrusionRole::erBridgeInfill: - return region->flow(FlowRole::frExternalPerimeter).width(); - case ExtrusionRole::erExternalPerimeter: - return region->flow(FlowRole::frExternalPerimeter).width(); - case ExtrusionRole::erGapFill: - return region->flow(FlowRole::frInfill).width(); - case ExtrusionRole::erPerimeter: - return region->flow(FlowRole::frPerimeter).width(); - case ExtrusionRole::erSolidInfill: - return region->flow(FlowRole::frSolidInfill).width(); - case ExtrusionRole::erInternalInfill: - return region->flow(FlowRole::frInfill).width(); - case ExtrusionRole::erTopSolidInfill: - return region->flow(FlowRole::frTopSolidInfill).width(); - default: - return region->flow(FlowRole::frPerimeter).width(); - } -} - -// Accumulator of current extruion path properties -// It remembers unsuported distance and maximum accumulated curvature over that distance. -// Used to determine local stability issues (too long bridges, extrusion curves into air) -struct ExtrusionPropertiesAccumulator { - float distance = 0; //accumulated distance - float curvature = 0; //accumulated signed ccw angles - float max_curvature = 0; //max absolute accumulated value - - void add_distance(float dist) { - distance += dist; - } - - void add_angle(float ccw_angle) { - curvature += ccw_angle; - max_curvature = std::max(max_curvature, std::abs(curvature)); - } - - void reset() { - distance = 0; - curvature = 0; - max_curvature = 0; - } -}; - -void check_extrusion_entity_stability(const ExtrusionEntity *entity, - std::vector &checked_lines_out, - float print_z, - const LayerRegion *layer_region, - const LinesDistancer &prev_layer_lines, - Issues &issues, - const Params ¶ms) { - - if (entity->is_collection()) { - for (const auto *e : static_cast(entity)->entities) { - check_extrusion_entity_stability(e, checked_lines_out, print_z, layer_region, prev_layer_lines, - issues, params); - } - } else { //single extrusion path, with possible varying parameters - const auto to_vec3f = [print_z](const Vec2f &point) { - return Vec3f(point.x(), point.y(), print_z); - }; - Points points { }; - entity->collect_points(points); - std::vector lines; - lines.reserve(points.size() * 1.5); - lines.emplace_back(unscaled(points[0]).cast(), unscaled(points[0]).cast(), entity); - for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { - Vec2f start = unscaled(points[point_idx]).cast(); - Vec2f next = unscaled(points[point_idx + 1]).cast(); - Vec2f v = next - start; // vector from next to current - float dist_to_next = v.norm(); - v.normalize(); - int lines_count = int(std::ceil(dist_to_next / params.bridge_distance)); - float step_size = dist_to_next / lines_count; - for (int i = 0; i < lines_count; ++i) { - Vec2f a(start + v * (i * step_size)); - Vec2f b(start + v * ((i + 1) * step_size)); - lines.emplace_back(a, b, entity); - } - } - - ExtrusionPropertiesAccumulator bridging_acc { }; - ExtrusionPropertiesAccumulator malformation_acc { }; - bridging_acc.add_distance(params.bridge_distance + 1.0f); // Initialise unsupported distance with larger than tolerable distance -> - // -> it prevents extruding perimeter starts and short loops into air. - const float flow_width = get_flow_width(layer_region, entity->role()); - - for (size_t line_idx = 0; line_idx < lines.size(); ++line_idx) { - ExtrusionLine ¤t_line = lines[line_idx]; - float curr_angle = 0; - if (line_idx + 1 < lines.size()) { - const Vec2f v1 = current_line.b - current_line.a; - const Vec2f v2 = lines[line_idx + 1].b - lines[line_idx + 1].a; - curr_angle = angle(v1, v2); - } - bridging_acc.add_angle(curr_angle); - malformation_acc.add_angle(std::max(0.0f, curr_angle)); - - size_t nearest_line_idx; - Vec2f nearest_point; - float dist_from_prev_layer = prev_layer_lines.signed_distance_from_lines(current_line.b, nearest_line_idx, - nearest_point); - - if (fabs(dist_from_prev_layer) < flow_width) { - bridging_acc.reset(); - } else { - bridging_acc.add_distance(current_line.len); - if (bridging_acc.distance // if unsupported distance is larger than bridge distance linearly decreased by curvature, enforce supports. - > params.bridge_distance - / (1.0f + (bridging_acc.max_curvature - * params.bridge_distance_decrease_by_curvature_factor / PI))) { - issues.support_points.emplace_back(to_vec3f(current_line.b), 0.0f, Vec3f(0.f, 0.0f, -1.0f)); - current_line.support_point_generated = true; - bridging_acc.reset(); - } - } - - //malformation - if (fabs(dist_from_prev_layer) < flow_width * 2.0f) { - const ExtrusionLine &nearest_line = prev_layer_lines.get_line(nearest_line_idx); - current_line.malformation += 0.9 * nearest_line.malformation; - } - if (dist_from_prev_layer > flow_width * 0.3) { - malformation_acc.add_distance(current_line.len); - current_line.malformation += 0.15 - * (0.8 + 0.2 * malformation_acc.max_curvature / (1.0f + 0.5f * malformation_acc.distance)); - } else { - malformation_acc.reset(); - } - } - checked_lines_out.insert(checked_lines_out.end(), lines.begin(), lines.end()); - } -} - -std::tuple> reckon_islands( - const Layer *layer, bool first_layer, - size_t prev_layer_islands_count, - const PixelGrid &prev_layer_grid, - const std::vector &layer_lines, - const Params ¶ms) { - - //extract extrusions (connected paths from multiple lines) from the layer_lines. belonging to single polyline is determined by origin_entity ptr. - // result is a vector of [start, end) index pairs into the layer_lines vector - std::vector> extrusions; //start and end idx (one beyond last extrusion) [start,end) - const ExtrusionEntity *current_ex = nullptr; - for (size_t lidx = 0; lidx < layer_lines.size(); ++lidx) { - const ExtrusionLine &line = layer_lines[lidx]; - if (line.origin_entity == current_ex) { - extrusions.back().second = lidx + 1; - } else { - extrusions.emplace_back(lidx, lidx + 1); - current_ex = line.origin_entity; - } - } - - std::vector islands; // these search trees will be used to determine to which island does the extrusion begin - std::vector> island_extrusions; //final assigment of each extrusion to an island - // initliaze the search from external perimeters - at the beginning, there is island candidate for each external perimeter. - // some of them will disappear (e.g. holes) - for (size_t e = 0; e < extrusions.size(); ++e) { - if (layer_lines[extrusions[e].first].is_external_perimeter()) { - std::vector copy(extrusions[e].second - extrusions[e].first); - for (size_t ex_line_idx = extrusions[e].first; ex_line_idx < extrusions[e].second; ++ex_line_idx) { - copy[ex_line_idx - extrusions[e].first] = layer_lines[ex_line_idx]; - } - islands.emplace_back(copy); - island_extrusions.push_back( { e }); - } - } - // backup code if islands not found - this can currently happen, as external perimeters may be also pure overhang perimeters, and there is no - // way to distinguish external extrusions with total certainty. - // If that happens, just make the first extrusion into island - it may be wrong, but it won't crash. - if (islands.empty() && !extrusions.empty()) { - std::vector copy(extrusions[0].second - extrusions[0].first); - for (size_t ex_line_idx = extrusions[0].first; ex_line_idx < extrusions[0].second; ++ex_line_idx) { - copy[ex_line_idx - extrusions[0].first] = layer_lines[ex_line_idx]; - } - islands.emplace_back(copy); - island_extrusions.push_back( { 0 }); - } - - // assign non external extrusions to islands - for (size_t e = 0; e < extrusions.size(); ++e) { - if (!layer_lines[extrusions[e].first].is_external_perimeter()) { - bool island_assigned = false; - for (size_t i = 0; i < islands.size(); ++i) { - size_t _idx; - Vec2f _pt; - if (islands[i].signed_distance_from_lines(layer_lines[extrusions[e].first].a, _idx, _pt) < 0) { - island_extrusions[i].push_back(e); - island_assigned = true; - break; - } - } - if (!island_assigned) { // If extrusion is not assigned for some reason, push it into the first island. As with the previous backup code, - // it may be wrong, but it won't crash - island_extrusions[0].push_back(e); - } - } - } - // merge islands which are embedded within each other (mainly holes) - for (size_t i = 0; i < islands.size(); ++i) { - if (islands[i].get_lines().empty()) { - continue; - } - for (size_t j = 0; j < islands.size(); ++j) { - if (islands[j].get_lines().empty() || i == j) { - continue; - } - size_t _idx; - Vec2f _pt; - if (islands[i].signed_distance_from_lines(islands[j].get_line(0).a, _idx, _pt) < 0) { - island_extrusions[i].insert(island_extrusions[i].end(), island_extrusions[j].begin(), - island_extrusions[j].end()); - island_extrusions[j].clear(); - } - } - } - - float flow_width = get_flow_width(layer->regions()[0], erExternalPerimeter); - // after filtering the layer lines into islands, build the result LayerIslands structure. - LayerIslands result { }; - std::vector line_to_island_mapping(layer_lines.size(), NULL_ISLAND); - for (const std::vector &island_ex : island_extrusions) { - if (island_ex.empty()) { - continue; - } - - Island island { }; - for (size_t extrusion_idx : island_ex) { - for (size_t lidx = extrusions[extrusion_idx].first; lidx < extrusions[extrusion_idx].second; ++lidx) { - line_to_island_mapping[lidx] = result.islands.size(); - const ExtrusionLine &line = layer_lines[lidx]; - float volume = line.origin_entity->min_mm3_per_mm() * line.len; - island.volume += volume; - island.volume_centroid_accumulator += to_3d(Vec2f((line.a + line.b) / 2.0f), float(layer->print_z)) - * volume; - - if (first_layer) { - float sticking_force = line.len * flow_width * params.base_adhesion; - island.sticking_force += sticking_force; - island.sticking_centroid_accumulator += sticking_force - * to_3d(Vec2f((line.a + line.b) / 2.0f), float(layer->print_z)); - if (line.is_external_perimeter()) { - island.pivot_points.push_back(to_3d(Vec2f(line.b), float(layer->print_z))); - } - } else if (layer_lines[lidx].support_point_generated) { - float support_interface_area = params.support_points_interface_radius - * params.support_points_interface_radius - * float(PI); - float sticking_force = support_interface_area * params.support_adhesion; - island.sticking_force += sticking_force; - island.sticking_centroid_accumulator += sticking_force - * to_3d(Vec2f(line.b), float(layer->print_z)); - island.pivot_points.push_back(to_3d(Vec2f(line.b), float(layer->print_z))); - } - } - } - result.islands.push_back(island); - } - - //LayerIslands structure built. Now determine connections and their areas to the previous layer using raterization. - PixelGrid current_layer_grid = prev_layer_grid; - current_layer_grid.clear(); - // build index image of current layer - tbb::parallel_for(tbb::blocked_range(0, layer_lines.size()), - [&layer_lines, ¤t_layer_grid, &line_to_island_mapping]( - tbb::blocked_range r) { - for (size_t i = r.begin(); i < r.end(); ++i) { - size_t island = line_to_island_mapping[i]; - const ExtrusionLine &line = layer_lines[i]; - current_layer_grid.distribute_edge(line.a, line.b, island); - } - }); - - //compare the image of previous layer with the current layer. For each pair of overlapping valid pixels, add pixel area to the respecitve island connection - for (size_t x = 0; x < size_t(current_layer_grid.get_pixel_count().x()); ++x) { - for (size_t y = 0; y < size_t(current_layer_grid.get_pixel_count().y()); ++y) { - Vec2i coords = Vec2i(x, y); - if (current_layer_grid.get_pixel(coords) != NULL_ISLAND - && prev_layer_grid.get_pixel(coords) != NULL_ISLAND) { - result.islands[current_layer_grid.get_pixel(coords)].islands_under_with_connection_area[prev_layer_grid.get_pixel( - coords)] += - current_layer_grid.pixel_area(); - } - } - } - return {result, current_layer_grid, line_to_island_mapping}; -} - -void check_global_stability( - float print_z, - std::vector &islands_graph, - SupportGridFilter &supports_presence_grid, - const std::vector &layer_lines, - const std::vector &line_to_island_mapping, - Issues &issues, - const Params ¶ms - ) { - // vector of islands, where each contains vector of line indices (to layer_lines vector) - // basically reverse of line_to_island_mapping - std::vector> islands_lines(islands_graph.back().islands.size()); - for (size_t lidx = 0; lidx < layer_lines.size(); ++lidx) { - if (layer_lines[lidx].origin_entity->role() == erExternalPerimeter) { - islands_lines[line_to_island_mapping[lidx]].push_back(lidx); - } - } - - using Accumulator = Island; - - // islands_graph.back() refers to the top most (current) layer - for (size_t island_idx = 0; island_idx < islands_graph.back().islands.size(); ++island_idx) { - Island &island = islands_graph.back().islands[island_idx]; - - std::vector island_external_lines; - for (size_t lidx : islands_lines[island_idx]) { - island_external_lines.push_back(layer_lines[lidx]); - } - LinesDistancer island_lines_dist(island_external_lines); - Accumulator acc = island; // in acc, we accumulate the mass and other properties of the object part as we traverse the islands down to bed - // There is one object part for each island at the top most layer, and each one is computed individually - - // Some of the calculations will be done multiple times - int layer_idx = islands_graph.size() - 1; - // traverse the islands graph down, and for each connection area, calculate if it holds or breaks - while (acc.islands_under_with_connection_area.size() > 0) { - //test for break between layer_idx and layer_idx -1; - LayerIslands below = islands_graph[layer_idx - 1]; // must exist, see while condition - layer_idx--; - // initialize variables that we will accumulate over all islands, which are connected to the current object part - std::vector pivot_points; - Vec2f sticking_centroid; - float connection_area = 0; - for (const auto &pair : acc.islands_under_with_connection_area) { - const Island &below_i = below.islands[pair.first]; - Vec2f centroid = (below_i.volume_centroid_accumulator / below_i.volume).head<2>(); // centroid of the island 'below_i'; TODO it should be centroid of the connection area - pivot_points.push_back(centroid); // for object parts, we also consider breaking pivots in the centroids of the islands - sticking_centroid += centroid * pair.second; // pair.second is connection area in mm^2 - connection_area += pair.second; - } - sticking_centroid /= connection_area; //normalize to get final sticking centroid - for (const Vec3f &p_point : acc.pivot_points) { - pivot_points.push_back(p_point.head<2>()); - } - // Now we have accumulated pivot points, connection area and sticking centroid of the whole layer to the current object part - - // create KD tree over current pivot points - auto coord_fn = [&pivot_points](size_t idx, size_t dim) { - return pivot_points[idx][dim]; - }; - KDTreeIndirect<2, float, decltype(coord_fn)> pivot_points_tree(coord_fn, pivot_points.size()); - - // iterate over extrusions at top layer island, check each for stability - for (const ExtrusionLine &line : island_external_lines) { - Vec2f line_dir = (line.b - line.a).normalized(); - Vec2f pivot_site_search_point = line.b + line_dir * 300.0f; - size_t pivot_idx = find_closest_point(pivot_points_tree, pivot_site_search_point); - const Vec2f &pivot = pivot_points[pivot_idx]; - - float sticking_arm = (pivot - sticking_centroid).norm(); - float sticking_torque = sticking_arm * connection_area * params.tensile_strength; // For breakage in between layers, we compute with tensile strength, not bed adhesion - - float mass = acc.volume * params.filament_density; - const Vec3f &mass_centorid = acc.volume_centroid_accumulator / acc.volume; - float weight = mass * params.gravity_constant; - float weight_arm = (pivot - mass_centorid.head<2>()).norm(); - float weight_torque = weight_arm * weight; - - float bed_movement_arm = mass_centorid.z(); - float bed_movement_force = params.max_acceleration * mass; - float bed_movement_torque = bed_movement_force * bed_movement_arm; - - Vec3f extruder_pressure_direction = to_3d(line_dir, 0.0f); - extruder_pressure_direction.z() = -0.2 - line.malformation * 0.5; - extruder_pressure_direction.normalize(); - float conflict_torque_arm = (to_3d(Vec2f(pivot - line.b), print_z).cross( - extruder_pressure_direction)).norm(); - float extruder_conflict_force = params.tolerable_extruder_conflict_force + - std::min(line.malformation, 1.0f) * params.malformations_additive_conflict_extruder_force; - float extruder_conflict_torque = extruder_conflict_force * conflict_torque_arm; - - float total_torque = bed_movement_torque + extruder_conflict_torque - weight_torque - sticking_torque; - - if (total_torque > 0) { - Vec2f target_point { }; - size_t _idx { }; - island_lines_dist.signed_distance_from_lines(pivot_site_search_point, _idx, target_point); - if (!supports_presence_grid.position_taken(to_3d(target_point, print_z))) { - float area = params.support_points_interface_radius * params.support_points_interface_radius - * float(PI); - float sticking_force = area * params.support_adhesion; - Vec3f support_point = to_3d(target_point, print_z); - island.pivot_points.push_back(support_point); - island.sticking_force += sticking_force; - island.sticking_centroid_accumulator += sticking_force * support_point; - issues.support_points.emplace_back(support_point, - extruder_conflict_torque - sticking_torque, extruder_pressure_direction); - supports_presence_grid.take_position(to_3d(target_point, print_z)); - } - } -#if 0 - BOOST_LOG_TRIVIAL(debug) - << "SSG: sticking_arm: " << sticking_arm; - BOOST_LOG_TRIVIAL(debug) - << "SSG: sticking_torque: " << sticking_torque; - BOOST_LOG_TRIVIAL(debug) - << "SSG: weight_arm: " << sticking_arm; - BOOST_LOG_TRIVIAL(debug) - << "SSG: weight_torque: " << weight_torque; - BOOST_LOG_TRIVIAL(debug) - << "SSG: bed_movement_arm: " << bed_movement_arm; - BOOST_LOG_TRIVIAL(debug) - << "SSG: bed_movement_torque: " << bed_movement_torque; - BOOST_LOG_TRIVIAL(debug) - << "SSG: conflict_torque_arm: " << conflict_torque_arm; - BOOST_LOG_TRIVIAL(debug) - << "SSG: extruder_conflict_torque: " << extruder_conflict_torque; - BOOST_LOG_TRIVIAL(debug) - << "SSG: total_torque: " << total_torque << " printz: " << print_z; - #endif - } - - std::unordered_map tmp = acc.islands_under_with_connection_area; - acc.islands_under_with_connection_area.clear(); - // finally, add gathered islands to accumulator, and continue down to next layer - for (const auto &pair : tmp) { - const Island &below_i = below.islands[pair.first]; - for (const auto &below_islands : below_i.islands_under_with_connection_area) { - acc.islands_under_with_connection_area[below_islands.first] += below_islands.second; - } - for (const Vec3f &pivot_p : below_i.pivot_points) { - acc.pivot_points.push_back(pivot_p); - } - acc.sticking_centroid_accumulator += below_i.sticking_centroid_accumulator; - acc.sticking_force += below_i.sticking_force; - acc.volume += below_i.volume; - acc.volume_centroid_accumulator += below_i.volume_centroid_accumulator; - } - } - - // We have arrived to the bed level, now check for stability of the object part on the bed - std::vector pivot_points; - for (const Vec3f &p_point : acc.pivot_points) { - pivot_points.push_back(p_point.head<2>()); - } - auto coord_fn = [&pivot_points](size_t idx, size_t dim) { - return pivot_points[idx][dim]; - }; - KDTreeIndirect<2, float, decltype(coord_fn)> pivot_points_tree(coord_fn, pivot_points.size()); - - for (const ExtrusionLine &line : island_external_lines) { - Vec2f line_dir = (line.b - line.a).normalized(); - Vec2f pivot_site_search_point = line.b + line_dir * 300.0f; - size_t pivot_idx = find_closest_point(pivot_points_tree, pivot_site_search_point); - const Vec2f &pivot = pivot_points[pivot_idx]; - - const Vec2f &sticking_centroid = acc.sticking_centroid_accumulator.head<2>() / acc.sticking_force; - float sticking_arm = (pivot - sticking_centroid).norm(); - float sticking_torque = sticking_arm * acc.sticking_force; - - float mass = acc.volume * params.filament_density; - const Vec3f &mass_centorid = acc.volume_centroid_accumulator / acc.volume; - float weight = mass * params.gravity_constant; - float weight_arm = (pivot - mass_centorid.head<2>()).norm(); - float weight_torque = weight_arm * weight; - - float bed_movement_arm = mass_centorid.z(); - float bed_movement_force = params.max_acceleration * mass; - float bed_movement_torque = bed_movement_force * bed_movement_arm; - - Vec3f extruder_pressure_direction = to_3d(line_dir, 0.0f); - extruder_pressure_direction.z() = -0.2 - line.malformation * 0.5; - extruder_pressure_direction.normalize(); - float conflict_torque_arm = (to_3d(Vec2f(pivot - line.b), print_z).cross( - extruder_pressure_direction)).norm(); - float extruder_conflict_force = params.tolerable_extruder_conflict_force + - std::min(line.malformation, 1.0f) * params.malformations_additive_conflict_extruder_force; - float extruder_conflict_torque = extruder_conflict_force * conflict_torque_arm; - - float total_torque = bed_movement_torque + extruder_conflict_torque - weight_torque - sticking_torque; - - if (total_torque > 0) { - Vec2f target_point; - size_t _idx; - island_lines_dist.signed_distance_from_lines(pivot_site_search_point, _idx, target_point); - if (!supports_presence_grid.position_taken(to_3d(target_point, print_z))) { - float area = params.support_points_interface_radius * params.support_points_interface_radius - * float(PI); - float sticking_force = area * params.support_adhesion; - Vec3f support_point = to_3d(target_point, print_z); - island.pivot_points.push_back(support_point); - island.sticking_force += sticking_force; - island.sticking_centroid_accumulator += sticking_force * support_point; - issues.support_points.emplace_back(support_point, - extruder_conflict_torque - sticking_torque, extruder_pressure_direction); - supports_presence_grid.take_position(to_3d(target_point, print_z)); - } - } -#if 0 - BOOST_LOG_TRIVIAL(debug) - << "SSG: sticking_arm: " << sticking_arm; - BOOST_LOG_TRIVIAL(debug) - << "SSG: sticking_torque: " << sticking_torque; - BOOST_LOG_TRIVIAL(debug) - << "SSG: weight_arm: " << sticking_arm; - BOOST_LOG_TRIVIAL(debug) - << "SSG: weight_torque: " << weight_torque; - BOOST_LOG_TRIVIAL(debug) - << "SSG: bed_movement_arm: " << bed_movement_arm; - BOOST_LOG_TRIVIAL(debug) - << "SSG: bed_movement_torque: " << bed_movement_torque; - BOOST_LOG_TRIVIAL(debug) - << "SSG: conflict_torque_arm: " << conflict_torque_arm; - BOOST_LOG_TRIVIAL(debug) - << "SSG: extruder_conflict_torque: " << extruder_conflict_torque; - BOOST_LOG_TRIVIAL(debug) - << "SSG: total_torque: " << total_torque << " printz: " << print_z; - #endif - } - } -} - -Issues check_object_stability(const PrintObject *po, const Params ¶ms) { -#ifdef DEBUG_FILES - FILE *segmentation_f = boost::nowide::fopen(debug_out_path("segmentation.obj").c_str(), "w"); - FILE *malform_f = boost::nowide::fopen(debug_out_path("malformations.obj").c_str(), "w"); -#endif - - Issues issues { }; - std::vector islands_graph; - std::vector layer_lines; - float flow_width = get_flow_width(po->layers()[po->layer_count() - 1]->regions()[0], erExternalPerimeter); - PixelGrid prev_layer_grid(po, flow_width); - SupportGridFilter supports_presence_grid { po, params.min_distance_between_support_points }; - - // PREPARE BASE LAYER - const Layer *layer = po->layers()[0]; - for (const LayerRegion *layer_region : layer->regions()) { - for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { - for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { - Points points { }; - perimeter->collect_points(points); - for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { - Vec2f start = unscaled(points[point_idx]).cast(); - Vec2f next = unscaled(points[point_idx + 1]).cast(); - ExtrusionLine line { start, next, perimeter }; - layer_lines.push_back(line); - } - if (perimeter->is_loop()) { - Vec2f start = unscaled(points[points.size() - 1]).cast(); - Vec2f next = unscaled(points[0]).cast(); - ExtrusionLine line { start, next, perimeter }; - layer_lines.push_back(line); - } - } // perimeter - } // ex_entity - for (const ExtrusionEntity *ex_entity : layer_region->fills.entities) { - for (const ExtrusionEntity *fill : static_cast(ex_entity)->entities) { - Points points { }; - fill->collect_points(points); - for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { - Vec2f start = unscaled(points[point_idx]).cast(); - Vec2f next = unscaled(points[point_idx + 1]).cast(); - ExtrusionLine line { start, next, fill }; - layer_lines.push_back(line); - } - } // fill - } // ex_entity - } // region - - auto [layer_islands, layer_grid, line_to_island_mapping] = reckon_islands(layer, true, 0, prev_layer_grid, - layer_lines, params); - islands_graph.push_back(std::move(layer_islands)); -#ifdef DEBUG_FILES - for (size_t x = 0; x < size_t(layer_grid.get_pixel_count().x()); ++x) { - for (size_t y = 0; y < size_t(layer_grid.get_pixel_count().y()); ++y) { - Vec2i coords = Vec2i(x, y); - size_t island_idx = layer_grid.get_pixel(coords); - if (layer_grid.get_pixel(coords) != NULL_ISLAND) { - Vec2f pos = layer_grid.get_pixel_center(coords); - size_t pseudornd = ((island_idx + 127) * 33331 + 6907) % 23; - Vec3f color = value_to_rgbf(0.0f, float(23), float(pseudornd)); - fprintf(segmentation_f, "v %f %f %f %f %f %f\n", pos[0], - pos[1], layer->print_z, color[0], color[1], color[2]); - } - } - } - for (const auto &line : layer_lines) { - if (line.malformation > 0.0f) { - Vec3f color = value_to_rgbf(0, 1.0f, line.malformation); - fprintf(malform_f, "v %f %f %f %f %f %f\n", line.b[0], - line.b[1], layer->print_z, color[0], color[1], color[2]); - } - } -#endif - LinesDistancer external_lines(layer_lines); - layer_lines.clear(); - prev_layer_grid = layer_grid; - - for (size_t layer_idx = 1; layer_idx < po->layer_count(); ++layer_idx) { - const Layer *layer = po->layers()[layer_idx]; - for (const LayerRegion *layer_region : layer->regions()) { - for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { - for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { - check_extrusion_entity_stability(perimeter, layer_lines, layer->print_z, layer_region, - external_lines, issues, params); - } // perimeter - } // ex_entity - for (const ExtrusionEntity *ex_entity : layer_region->fills.entities) { - for (const ExtrusionEntity *fill : static_cast(ex_entity)->entities) { - if (fill->role() == ExtrusionRole::erGapFill - || fill->role() == ExtrusionRole::erBridgeInfill) { - check_extrusion_entity_stability(fill, layer_lines, layer->print_z, layer_region, - external_lines, issues, params); - } else { - Points points { }; - fill->collect_points(points); - for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { - Vec2f start = unscaled(points[point_idx]).cast(); - Vec2f next = unscaled(points[point_idx + 1]).cast(); - ExtrusionLine line { start, next, fill }; - layer_lines.push_back(line); - } - } - } // fill - } // ex_entity - } // region - - auto [layer_islands, layer_grid, line_to_island_mapping] = reckon_islands(layer, true, 0, prev_layer_grid, - layer_lines, params); - islands_graph.push_back(std::move(layer_islands)); - - check_global_stability(layer->print_z, islands_graph, supports_presence_grid, layer_lines, - line_to_island_mapping, issues, params); - -#ifdef DEBUG_FILES - for (size_t x = 0; x < size_t(layer_grid.get_pixel_count().x()); ++x) { - for (size_t y = 0; y < size_t(layer_grid.get_pixel_count().y()); ++y) { - Vec2i coords = Vec2i(x, y); - size_t island_idx = layer_grid.get_pixel(coords); - if (layer_grid.get_pixel(coords) != NULL_ISLAND) { - Vec2f pos = layer_grid.get_pixel_center(coords); - size_t pseudornd = ((island_idx + 127) * 33331 + 6907) % 23; - Vec3f color = value_to_rgbf(0.0f, float(23), float(pseudornd)); - fprintf(segmentation_f, "v %f %f %f %f %f %f\n", pos[0], - pos[1], layer->print_z, color[0], color[1], color[2]); - } - } - } - for (const auto &line : layer_lines) { - if (line.malformation > 0.0f) { - Vec3f color = value_to_rgbf(0, 1.0f, line.malformation); - fprintf(malform_f, "v %f %f %f %f %f %f\n", line.b[0], - line.b[1], layer->print_z, color[0], color[1], color[2]); - } - } -#endif - external_lines = LinesDistancer(layer_lines); - layer_lines.clear(); - prev_layer_grid = layer_grid; - } - -#ifdef DEBUG_FILES - fclose(segmentation_f); - fclose(malform_f); -#endif - - return issues; -} - -#ifdef DEBUG_FILES -void debug_export(Issues issues, std::string file_name) { - Slic3r::CNumericLocalesSetter locales_setter; - { - FILE *fp = boost::nowide::fopen(debug_out_path((file_name + "_supports.obj").c_str()).c_str(), "w"); - if (fp == nullptr) { - BOOST_LOG_TRIVIAL(error) - << "Debug files: Couldn't open " << file_name << " for writing"; - return; - } - - for (size_t i = 0; i < issues.support_points.size(); ++i) { - fprintf(fp, "v %f %f %f %f %f %f\n", issues.support_points[i].position(0), - issues.support_points[i].position(1), - issues.support_points[i].position(2), 1.0, 0.0, 1.0); - } - - fclose(fp); - } -} -#endif - -std::vector quick_search(const PrintObject *po, const Params ¶ms) { - check_object_stability(po, params); - return {}; -} - -Issues -full_search(const PrintObject *po, const Params ¶ms) { - auto issues = check_object_stability(po, params); -#ifdef DEBUG_FILES - debug_export(issues, "issues"); -#endif - return issues; -} - -} //SupportableIssues End -} - diff --git a/src/slic3r/GUI/BonjourDialog.hpp b/src/slic3r/GUI/BonjourDialog.hpp index 8bfc076c44..5bb61131d0 100644 --- a/src/slic3r/GUI/BonjourDialog.hpp +++ b/src/slic3r/GUI/BonjourDialog.hpp @@ -8,6 +8,8 @@ #include #include +#include +#include #include "libslic3r/PrintConfig.hpp" From 8723fb22bb47f26d24d320be30939b0fc53d7774 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Wed, 20 Jul 2022 17:21:36 +0200 Subject: [PATCH 48/78] add pivot tree into ObjectPart struct --- src/libslic3r/SupportSpotsGenerator.cpp | 31 +++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index 4512c795b4..61e078d8b6 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -566,13 +566,33 @@ std::tuple reckon_islands( return {result, current_layer_grid}; } -struct ObjectPart { +struct CoordinateFunctor { + const std::vector *coordinates; + CoordinateFunctor(const std::vector *coords) : + coordinates(coords) { + } + CoordinateFunctor() : + coordinates(nullptr) { + } + + const float& operator()(size_t idx, size_t dim) const { + return coordinates->operator [](idx)[dim]; + } +}; + +//TODO make pivot tree part of this structure, with cached invalidation, then recompute manually when needed +class ObjectPart { float volume { }; Vec3f volume_centroid_accumulator = Vec3f::Zero(); float sticking_force { }; Vec3f sticking_centroid_accumulator = Vec3f::Zero(); std::vector pivot_points { }; + CoordinateFunctor pivots_coordinate_functor; + bool is_pivot_tree_valid = false; + KDTreeIndirect<3, float, CoordinateFunctor> pivot_tree { CoordinateFunctor { } }; + +public: void add(const ObjectPart &other) { this->volume_centroid_accumulator += other.volume_centroid_accumulator; this->volume += other.volume; @@ -587,9 +607,12 @@ struct ObjectPart { this->sticking_force = island.sticking_force; this->sticking_centroid_accumulator = island.sticking_centroid_accumulator; this->pivot_points = island.pivot_points; + this->pivots_coordinate_functor = CoordinateFunctor(&this->pivot_points); } - ObjectPart() = default; + ObjectPart() { + this->pivots_coordinate_functor = CoordinateFunctor(&this->pivot_points); + }; }; struct WeakestConnection { @@ -691,6 +714,10 @@ Issues check_global_stability(const std::vector &islands_graph, co prev_island_to_weakest_connection = next_island_to_weakest_connection; next_island_to_weakest_connection.clear(); + // All object parts updated, inactive parts removed and weakest point of each island updated as well. + // Now compute the stability of each active object part, adding supports where necessary, and also + // check each island whether the weakest point is strong enough. If not, add supports as well. + } return issues; } From 07049b849e44bd0ee4c712f619aeb574d9e6ad8d Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Thu, 21 Jul 2022 14:41:53 +0200 Subject: [PATCH 49/78] fixed various bugs --- src/libslic3r/SupportSpotsGenerator.cpp | 254 +++++++++++++++++++----- 1 file changed, 201 insertions(+), 53 deletions(-) diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index 61e078d8b6..b4ddc97c2e 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -74,7 +74,7 @@ private: AABBTreeIndirect::Tree<2, float> tree; public: - explicit LinesDistancer(std::vector &lines) : + explicit LinesDistancer(const std::vector &lines) : lines(lines) { tree = AABBTreeLines::build_aabb_tree_over_indexed_lines(this->lines); } @@ -249,17 +249,17 @@ public: }; struct IslandConnection { - float area; - Vec3f centroid_accumulator; + float area{}; + Vec3f centroid_accumulator = Vec3f::Zero(); }; struct Island { - std::unordered_map connected_islands; - std::vector pivot_points; // for support points present on this layer (or bed extrusions) - float volume; - Vec3f volume_centroid_accumulator; - float sticking_force; // for support points present on this layer (or bed extrusions) - Vec3f sticking_centroid_accumulator; + std::unordered_map connected_islands{}; + std::vector pivot_points{}; // for support points present on this layer (or bed extrusions) + float volume{}; + Vec3f volume_centroid_accumulator = Vec3f::Zero(); + float sticking_force{}; // for support points present on this layer (or bed extrusions) + Vec3f sticking_centroid_accumulator = Vec3f::Zero(); std::vector external_lines; }; @@ -316,7 +316,7 @@ struct ExtrusionPropertiesAccumulator { void check_extrusion_entity_stability(const ExtrusionEntity *entity, std::vector &checked_lines_out, - float print_z, + float layer_z, const LayerRegion *layer_region, const LinesDistancer &prev_layer_lines, Issues &issues, @@ -324,12 +324,12 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, if (entity->is_collection()) { for (const auto *e : static_cast(entity)->entities) { - check_extrusion_entity_stability(e, checked_lines_out, print_z, layer_region, prev_layer_lines, + check_extrusion_entity_stability(e, checked_lines_out, layer_z, layer_region, prev_layer_lines, issues, params); } } else { //single extrusion path, with possible varying parameters - const auto to_vec3f = [print_z](const Vec2f &point) { - return Vec3f(point.x(), point.y(), print_z); + const auto to_vec3f = [layer_z](const Vec2f &point) { + return Vec3f(point.x(), point.y(), layer_z); }; Points points { }; entity->collect_points(points); @@ -509,16 +509,16 @@ std::tuple reckon_islands( const ExtrusionLine &line = layer_lines[lidx]; float volume = line.origin_entity->min_mm3_per_mm() * line.len; island.volume += volume; - island.volume_centroid_accumulator += to_3d(Vec2f((line.a + line.b) / 2.0f), float(layer->print_z)) + island.volume_centroid_accumulator += to_3d(Vec2f((line.a + line.b) / 2.0f), float(layer->slice_z)) * volume; if (first_layer) { float sticking_force = line.len * flow_width * params.base_adhesion; island.sticking_force += sticking_force; island.sticking_centroid_accumulator += sticking_force - * to_3d(Vec2f((line.a + line.b) / 2.0f), float(layer->print_z)); + * to_3d(Vec2f((line.a + line.b) / 2.0f), float(layer->slice_z)); if (line.is_external_perimeter()) { - island.pivot_points.push_back(to_3d(Vec2f(line.b), float(layer->print_z))); + island.pivot_points.push_back(to_3d(Vec2f(line.b), float(layer->slice_z))); } } else if (layer_lines[lidx].support_point_generated) { float support_interface_area = params.support_points_interface_radius @@ -527,8 +527,8 @@ std::tuple reckon_islands( float sticking_force = support_interface_area * params.support_adhesion; island.sticking_force += sticking_force; island.sticking_centroid_accumulator += sticking_force - * to_3d(Vec2f(line.b), float(layer->print_z)); - island.pivot_points.push_back(to_3d(Vec2f(line.b), float(layer->print_z))); + * to_3d(Vec2f(line.b), float(layer->slice_z)); + island.pivot_points.push_back(to_3d(Vec2f(line.b), float(layer->slice_z))); } } } @@ -555,10 +555,11 @@ std::tuple reckon_islands( Vec2i coords = Vec2i(x, y); if (current_layer_grid.get_pixel(coords) != NULL_ISLAND && prev_layer_grid.get_pixel(coords) != NULL_ISLAND) { - IslandConnection& connection = result.islands[current_layer_grid.get_pixel(coords)] - .connected_islands[prev_layer_grid.get_pixel(coords)]; + IslandConnection &connection = result.islands[current_layer_grid.get_pixel(coords)] + .connected_islands[prev_layer_grid.get_pixel(coords)]; connection.area += current_layer_grid.pixel_area(); - connection.centroid_accumulator += to_3d(current_layer_grid.get_pixel_center(coords), result.layer_z) * current_layer_grid.pixel_area(); + connection.centroid_accumulator += to_3d(current_layer_grid.get_pixel_center(coords), result.layer_z) + * current_layer_grid.pixel_area(); } } } @@ -580,7 +581,6 @@ struct CoordinateFunctor { } }; -//TODO make pivot tree part of this structure, with cached invalidation, then recompute manually when needed class ObjectPart { float volume { }; Vec3f volume_centroid_accumulator = Vec3f::Zero(); @@ -592,6 +592,15 @@ class ObjectPart { bool is_pivot_tree_valid = false; KDTreeIndirect<3, float, CoordinateFunctor> pivot_tree { CoordinateFunctor { } }; + void check_pivot_tree() { + if (!is_pivot_tree_valid) { + this->pivots_coordinate_functor = CoordinateFunctor(&this->pivot_points); + this->pivot_tree = { this->pivots_coordinate_functor }; + pivot_tree.build(pivot_points.size()); + is_pivot_tree_valid = true; + } + } + public: void add(const ObjectPart &other) { this->volume_centroid_accumulator += other.volume_centroid_accumulator; @@ -599,6 +608,7 @@ public: this->sticking_force += other.sticking_force; this->sticking_centroid_accumulator += other.sticking_centroid_accumulator; this->pivot_points.insert(this->pivot_points.end(), other.pivot_points.begin(), other.pivot_points.end()); + this->is_pivot_tree_valid = this->is_pivot_tree_valid && other.pivot_points.empty(); } ObjectPart(const Island &island) { @@ -607,25 +617,121 @@ public: this->sticking_force = island.sticking_force; this->sticking_centroid_accumulator = island.sticking_centroid_accumulator; this->pivot_points = island.pivot_points; - this->pivots_coordinate_functor = CoordinateFunctor(&this->pivot_points); } - ObjectPart() { - this->pivots_coordinate_functor = CoordinateFunctor(&this->pivot_points); - }; + ObjectPart() = default; + + std::tuple is_stable_while_extruding(const ExtrusionLine &extruded_line, float layer_z, + const Params ¶ms) { + check_pivot_tree(); + + Vec2f line_dir = (extruded_line.b - extruded_line.a).normalized(); + Vec3f pivot_site_search_point = to_3d(Vec2f(extruded_line.b + line_dir * 300.0f), layer_z); + size_t pivot_idx = find_closest_point(this->pivot_tree, pivot_site_search_point); + const Vec3f &pivot = pivot_points[pivot_idx]; + + const Vec3f &sticking_centroid = this->sticking_centroid_accumulator / this->sticking_force; + float sticking_arm = (pivot - sticking_centroid).norm(); + float sticking_torque = sticking_arm * this->sticking_force; + + float mass = this->volume * params.filament_density; + const Vec3f &mass_centorid = this->volume_centroid_accumulator / this->volume; + float weight = mass * params.gravity_constant; + float weight_arm = (pivot.head<2>() - mass_centorid.head<2>()).norm(); + float weight_torque = weight_arm * weight; + + float bed_movement_arm = mass_centorid.z(); + float bed_movement_force = params.max_acceleration * mass; + float bed_movement_torque = bed_movement_force * bed_movement_arm; + + Vec3f extruder_pressure_direction = to_3d(line_dir, 0.0f); + extruder_pressure_direction.z() = -0.1f - extruded_line.malformation * 0.5f; + extruder_pressure_direction.normalize(); + Vec3d endpoint = (to_3d(extruded_line.b, layer_z)).cast(); + float conflict_torque_arm = line_alg::distance_to(Linef3(endpoint, endpoint + extruder_pressure_direction.cast()), pivot.cast()); +// float conflict_torque_arm = (to_3d(Vec2f(pivot.head<2>() - extruded_line.b), layer_z).cross( +// extruder_pressure_direction)).norm(); + float extruder_conflict_force = params.tolerable_extruder_conflict_force + + std::min(extruded_line.malformation, 1.0f) * params.malformations_additive_conflict_extruder_force; + float extruder_conflict_torque = extruder_conflict_force * conflict_torque_arm; + + float total_torque = bed_movement_torque + extruder_conflict_torque - weight_torque - sticking_torque; + +#if 1 + BOOST_LOG_TRIVIAL(debug) + << "pivot: " << pivot.x() << " " << pivot.y() << " " << pivot.z(); + BOOST_LOG_TRIVIAL(debug) + << "sticking_centroid: " << sticking_centroid.x() << " " << sticking_centroid.y() << " " << sticking_centroid.z(); + BOOST_LOG_TRIVIAL(debug) + << "SSG: sticking_force: " << sticking_force; + BOOST_LOG_TRIVIAL(debug) + << "SSG: sticking_arm: " << sticking_arm; + BOOST_LOG_TRIVIAL(debug) + << "SSG: sticking_torque: " << sticking_torque; + BOOST_LOG_TRIVIAL(debug) + << "SSG: weight_arm: " << sticking_arm; + BOOST_LOG_TRIVIAL(debug) + << "SSG: weight_torque: " << weight_torque; + BOOST_LOG_TRIVIAL(debug) + << "SSG: bed_movement_arm: " << bed_movement_arm; + BOOST_LOG_TRIVIAL(debug) + << "SSG: bed_movement_torque: " << bed_movement_torque; + BOOST_LOG_TRIVIAL(debug) + << "SSG: conflict_torque_arm: " << conflict_torque_arm; + BOOST_LOG_TRIVIAL(debug) + << "SSG: extruder_conflict_torque: " << extruder_conflict_torque; + BOOST_LOG_TRIVIAL(debug) + << "SSG: total_torque: " << total_torque << " layer_z: " << layer_z; +#endif + + return {total_torque / conflict_torque_arm, pivot_site_search_point}; + } + + void add_pivot_point(const Vec3f pivot_point, float sticking_force) { + this->pivot_points.push_back(pivot_point); + this->sticking_force += sticking_force; + this->sticking_centroid_accumulator += sticking_force * pivot_point; + this->is_pivot_tree_valid = false; + } + + void print() const { + std::cout << "sticking_force: " << sticking_force << std::endl; + std::cout << "volume: " << volume << std::endl; + } + }; struct WeakestConnection { float area = 0.0f; Vec3f centroid_accumulator = Vec3f::Zero(); - void add(const WeakestConnection& other) { + void add(const WeakestConnection &other) { this->area += other.area; this->centroid_accumulator += other.centroid_accumulator; } }; -Issues check_global_stability(const std::vector &islands_graph, const Params ¶ms) { +void debug_print_graph(const std::vector& islands_graph) { + std::cout << "BUILT ISLANDS GRAPH:" << std::endl; + for (size_t layer_idx = 0; layer_idx < islands_graph.size(); ++layer_idx) { + std::cout << "ISLANDS AT LAYER: " << layer_idx << " AT HEIGHT: " << islands_graph[layer_idx].layer_z << std::endl; + for (size_t island_idx = 0; island_idx < islands_graph[layer_idx].islands.size(); ++island_idx) { + const Island &island = islands_graph[layer_idx].islands[island_idx]; + std::cout << " ISLAND " << island_idx << std::endl; + std::cout << " volume: " << island.volume << std::endl; + std::cout << " sticking_force: " << island.sticking_force << std::endl; + std::cout << " pivot_points count: " << island.pivot_points.size() << std::endl; + std::cout << " connected_islands count: " << island.connected_islands.size() << std::endl; + } + } + std::cout << "END OF GRAPH" << std::endl; + + +} + +Issues check_global_stability(SupportGridFilter supports_presence_grid, + const std::vector &islands_graph, const Params ¶ms) { + debug_print_graph(islands_graph); Issues issues { }; size_t next_part_idx = 0; std::unordered_map active_object_parts; @@ -641,7 +747,8 @@ Issues check_global_stability(const std::vector &islands_graph, co std::cout << "at layer: " << layer_idx << " the following island to object mapping is used:" << std::endl; for (const auto &m : prev_island_to_object_part_mapping) { std::cout << "island " << m.first << " maps to part " << m.second << std::endl; - Vec3f connection_center = prev_island_to_weakest_connection[m.first].centroid_accumulator / prev_island_to_weakest_connection[m.first].area; + Vec3f connection_center = prev_island_to_weakest_connection[m.first].centroid_accumulator + / prev_island_to_weakest_connection[m.first].area; std::cout << " island has weak point with connection area: " << prev_island_to_weakest_connection[m.first].area << " and center: " << connection_center.x() << " " << connection_center.y() << " " << connection_center.z() << std::endl; @@ -655,12 +762,12 @@ Issues check_global_stability(const std::vector &islands_graph, co active_object_parts.emplace(part_idx, ObjectPart(island)); next_island_to_object_part_mapping.emplace(island_idx, part_idx); next_island_to_weakest_connection.emplace(island_idx, - WeakestConnection{INFINITY, Vec3f::Zero()}); + WeakestConnection { INFINITY, Vec3f::Zero() }); layer_active_parts.insert(part_idx); } else { - size_t final_part_idx{}; - WeakestConnection transfered_weakest_connection{}; - WeakestConnection new_weakest_connection{}; + size_t final_part_idx { }; + WeakestConnection transfered_weakest_connection { }; + WeakestConnection new_weakest_connection { }; // MERGE parts { std::unordered_set part_indices; @@ -680,7 +787,7 @@ Issues check_global_stability(const std::vector &islands_graph, co } } } - auto estimate_strength = [layer_z](const WeakestConnection& conn){ + auto estimate_strength = [layer_z](const WeakestConnection &conn) { float radius = fsqrt(conn.area / PI); float arm_len_estimate = std::max(0.001f, layer_z - (conn.centroid_accumulator.z() / conn.area)); return radius * conn.area / arm_len_estimate; @@ -703,6 +810,7 @@ Issues check_global_stability(const std::vector &islands_graph, co parts_to_delete.insert(part.first); } else { std::cout << "at layer " << layer_idx << " part is still active: " << part.first << std::endl; + part.second.print(); } } for (size_t part_id : parts_to_delete) { @@ -718,11 +826,50 @@ Issues check_global_stability(const std::vector &islands_graph, co // Now compute the stability of each active object part, adding supports where necessary, and also // check each island whether the weakest point is strong enough. If not, add supports as well. + for (size_t island_idx = 0; island_idx < islands_graph[layer_idx].islands.size(); ++island_idx) { + const Island &island = islands_graph[layer_idx].islands[island_idx]; + ObjectPart &part = active_object_parts.at(prev_island_to_object_part_mapping[island_idx]); + + std::vector dummy { }; + LinesDistancer island_lines_dist(dummy); + float unchecked_dist = params.min_distance_between_support_points + 1.0f; + + for (const ExtrusionLine &line : island.external_lines) { + if (unchecked_dist + line.len < params.min_distance_between_support_points + && line.malformation < 0.3f) { + unchecked_dist += line.len; + } else { + unchecked_dist = line.len; + auto [force, pivot_site_search_point] = part.is_stable_while_extruding(line, layer_z, params); + if (force > 0) { + if (island_lines_dist.get_lines().empty()) { + island_lines_dist = LinesDistancer(island.external_lines); + } + Vec2f target_point; + size_t _idx; + island_lines_dist.signed_distance_from_lines(pivot_site_search_point.head<2>(), _idx, + target_point); + Vec3f support_point = to_3d(target_point, layer_z); + if (!supports_presence_grid.position_taken(support_point)) { + float area = params.support_points_interface_radius * params.support_points_interface_radius + * float(PI); + float sticking_force = area * params.support_adhesion; + part.add_pivot_point(support_point, sticking_force); + issues.support_points.emplace_back(support_point, force, + to_3d(Vec2f(line.b - line.a).normalized(), 0.0f)); + supports_presence_grid.take_position(support_point); + } + } + } + } + } + //end of iteration over layer } return issues; } /* + void a() { // islands_graph.back() refers to the top most (current) layer for (size_t island_idx = 0; island_idx < islands_graph.back().islands.size(); ++island_idx) { @@ -788,7 +935,7 @@ Issues check_global_stability(const std::vector &islands_graph, co Vec3f extruder_pressure_direction = to_3d(line_dir, 0.0f); extruder_pressure_direction.z() = -0.2 - line.malformation * 0.5; extruder_pressure_direction.normalize(); - float conflict_torque_arm = (to_3d(Vec2f(pivot - line.b), print_z).cross( + float conflict_torque_arm = (to_3d(Vec2f(pivot - line.b), layer_z).cross( extruder_pressure_direction)).norm(); float extruder_conflict_force = params.tolerable_extruder_conflict_force + std::min(line.malformation, 1.0f) * params.malformations_additive_conflict_extruder_force; @@ -800,17 +947,17 @@ Issues check_global_stability(const std::vector &islands_graph, co Vec2f target_point { }; size_t _idx { }; island_lines_dist.signed_distance_from_lines(pivot_site_search_point, _idx, target_point); - if (!supports_presence_grid.position_taken(to_3d(target_point, print_z))) { + if (!supports_presence_grid.position_taken(to_3d(target_point, layer_z))) { float area = params.support_points_interface_radius * params.support_points_interface_radius * float(PI); float sticking_force = area * params.support_adhesion; - Vec3f support_point = to_3d(target_point, print_z); + Vec3f support_point = to_3d(target_point, layer_z); island.pivot_points.push_back(support_point); island.sticking_force += sticking_force; island.sticking_centroid_accumulator += sticking_force * support_point; issues.support_points.emplace_back(support_point, extruder_conflict_torque - sticking_torque, extruder_pressure_direction); - supports_presence_grid.take_position(to_3d(target_point, print_z)); + supports_presence_grid.take_position(to_3d(target_point, layer_z)); } } #if 0 @@ -831,7 +978,7 @@ Issues check_global_stability(const std::vector &islands_graph, co BOOST_LOG_TRIVIAL(debug) << "SSG: extruder_conflict_torque: " << extruder_conflict_torque; BOOST_LOG_TRIVIAL(debug) - << "SSG: total_torque: " << total_torque << " printz: " << print_z; + << "SSG: total_torque: " << total_torque << " layer_z: " << layer_z; #endif } @@ -886,7 +1033,7 @@ Issues check_global_stability(const std::vector &islands_graph, co Vec3f extruder_pressure_direction = to_3d(line_dir, 0.0f); extruder_pressure_direction.z() = -0.2 - line.malformation * 0.5; extruder_pressure_direction.normalize(); - float conflict_torque_arm = (to_3d(Vec2f(pivot - line.b), print_z).cross( + float conflict_torque_arm = (to_3d(Vec2f(pivot - line.b), layer_z).cross( extruder_pressure_direction)).norm(); float extruder_conflict_force = params.tolerable_extruder_conflict_force + std::min(line.malformation, 1.0f) * params.malformations_additive_conflict_extruder_force; @@ -898,17 +1045,17 @@ Issues check_global_stability(const std::vector &islands_graph, co Vec2f target_point; size_t _idx; island_lines_dist.signed_distance_from_lines(pivot_site_search_point, _idx, target_point); - if (!supports_presence_grid.position_taken(to_3d(target_point, print_z))) { + if (!supports_presence_grid.position_taken(to_3d(target_point, layer_z))) { float area = params.support_points_interface_radius * params.support_points_interface_radius * float(PI); float sticking_force = area * params.support_adhesion; - Vec3f support_point = to_3d(target_point, print_z); + Vec3f support_point = to_3d(target_point, layer_z); island.pivot_points.push_back(support_point); island.sticking_force += sticking_force; island.sticking_centroid_accumulator += sticking_force * support_point; issues.support_points.emplace_back(support_point, extruder_conflict_torque - sticking_torque, extruder_pressure_direction); - supports_presence_grid.take_position(to_3d(target_point, print_z)); + supports_presence_grid.take_position(to_3d(target_point, layer_z)); } } #if 0 @@ -929,12 +1076,13 @@ Issues check_global_stability(const std::vector &islands_graph, co BOOST_LOG_TRIVIAL(debug) << "SSG: extruder_conflict_torque: " << extruder_conflict_torque; BOOST_LOG_TRIVIAL(debug) - << "SSG: total_torque: " << total_torque << " printz: " << print_z; + << "SSG: total_torque: " << total_torque << " layer_z: " << layer_z; #endif } } return issues; + } */ std::tuple> check_extrusions_and_build_graph(const PrintObject *po, @@ -950,7 +1098,7 @@ std::tuple> check_extrusions_and_build_graph(c float flow_width = get_flow_width(po->layers()[po->layer_count() - 1]->regions()[0], erExternalPerimeter); PixelGrid prev_layer_grid(po, flow_width); - // PREPARE BASE LAYER +// PREPARE BASE LAYER const Layer *layer = po->layers()[0]; for (const LayerRegion *layer_region : layer->regions()) { for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { @@ -998,7 +1146,7 @@ std::tuple> check_extrusions_and_build_graph(c size_t pseudornd = ((island_idx + 127) * 33331 + 6907) % 23; Vec3f color = value_to_rgbf(0.0f, float(23), float(pseudornd)); fprintf(segmentation_f, "v %f %f %f %f %f %f\n", pos[0], - pos[1], layer->print_z, color[0], color[1], color[2]); + pos[1], layer->slice_z, color[0], color[1], color[2]); } } } @@ -1006,7 +1154,7 @@ std::tuple> check_extrusions_and_build_graph(c if (line.malformation > 0.0f) { Vec3f color = value_to_rgbf(0, 1.0f, line.malformation); fprintf(malform_f, "v %f %f %f %f %f %f\n", line.b[0], - line.b[1], layer->print_z, color[0], color[1], color[2]); + line.b[1], layer->slice_z, color[0], color[1], color[2]); } } #endif @@ -1019,7 +1167,7 @@ std::tuple> check_extrusions_and_build_graph(c for (const LayerRegion *layer_region : layer->regions()) { for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { - check_extrusion_entity_stability(perimeter, layer_lines, layer->print_z, layer_region, + check_extrusion_entity_stability(perimeter, layer_lines, layer->slice_z, layer_region, external_lines, issues, params); } // perimeter } // ex_entity @@ -1027,7 +1175,7 @@ std::tuple> check_extrusions_and_build_graph(c for (const ExtrusionEntity *fill : static_cast(ex_entity)->entities) { if (fill->role() == ExtrusionRole::erGapFill || fill->role() == ExtrusionRole::erBridgeInfill) { - check_extrusion_entity_stability(fill, layer_lines, layer->print_z, layer_region, + check_extrusion_entity_stability(fill, layer_lines, layer->slice_z, layer_region, external_lines, issues, params); } else { Points points { }; @@ -1043,7 +1191,7 @@ std::tuple> check_extrusions_and_build_graph(c } // ex_entity } // region - auto [layer_islands, layer_grid] = reckon_islands(layer, true, 0, prev_layer_grid, + auto [layer_islands, layer_grid] = reckon_islands(layer, false, 0, prev_layer_grid, layer_lines, params); islands_graph.push_back(std::move(layer_islands)); @@ -1057,7 +1205,7 @@ std::tuple> check_extrusions_and_build_graph(c size_t pseudornd = ((island_idx + 127) * 33331 + 6907) % 23; Vec3f color = value_to_rgbf(0.0f, float(23), float(pseudornd)); fprintf(segmentation_f, "v %f %f %f %f %f %f\n", pos[0], - pos[1], layer->print_z, color[0], color[1], color[2]); + pos[1], layer->slice_z, color[0], color[1], color[2]); } } } @@ -1065,7 +1213,7 @@ std::tuple> check_extrusions_and_build_graph(c if (line.malformation > 0.0f) { Vec3f color = value_to_rgbf(0, 1.0f, line.malformation); fprintf(malform_f, "v %f %f %f %f %f %f\n", line.b[0], - line.b[1], layer->print_z, color[0], color[1], color[2]); + line.b[1], layer->slice_z, color[0], color[1], color[2]); } } #endif @@ -1110,7 +1258,7 @@ std::vector quick_search(const PrintObject *po, const Params ¶ms) { Issues full_search(const PrintObject *po, const Params ¶ms) { auto [local_issues, graph] = check_extrusions_and_build_graph(po, params); - Issues global_issues = check_global_stability(graph, params); + Issues global_issues = check_global_stability( { po, params.min_distance_between_support_points }, graph, params); #ifdef DEBUG_FILES debug_export(local_issues, "local_issues"); debug_export(global_issues, "global_issues"); From 3b029cef05da0cdc2655144c9f94e12877b6597a Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Thu, 21 Jul 2022 14:58:47 +0200 Subject: [PATCH 50/78] another bulk of fixes GLOBAL STABILITY check works --- src/libslic3r/SupportSpotsGenerator.cpp | 28 ++++++++++++++----------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index b4ddc97c2e..4abf4dd952 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -249,16 +249,16 @@ public: }; struct IslandConnection { - float area{}; + float area { }; Vec3f centroid_accumulator = Vec3f::Zero(); }; struct Island { - std::unordered_map connected_islands{}; - std::vector pivot_points{}; // for support points present on this layer (or bed extrusions) - float volume{}; + std::unordered_map connected_islands { }; + std::vector pivot_points { }; // for support points present on this layer (or bed extrusions) + float volume { }; Vec3f volume_centroid_accumulator = Vec3f::Zero(); - float sticking_force{}; // for support points present on this layer (or bed extrusions) + float sticking_force { }; // for support points present on this layer (or bed extrusions) Vec3f sticking_centroid_accumulator = Vec3f::Zero(); std::vector external_lines; @@ -623,6 +623,10 @@ public: std::tuple is_stable_while_extruding(const ExtrusionLine &extruded_line, float layer_z, const Params ¶ms) { + if (pivot_points.empty()) { + return {this->volume * params.filament_density*params.gravity_constant,Vec3f {0.0f,0.0f,-1.0f}}; + } + check_pivot_tree(); Vec2f line_dir = (extruded_line.b - extruded_line.a).normalized(); @@ -648,9 +652,8 @@ public: extruder_pressure_direction.z() = -0.1f - extruded_line.malformation * 0.5f; extruder_pressure_direction.normalize(); Vec3d endpoint = (to_3d(extruded_line.b, layer_z)).cast(); - float conflict_torque_arm = line_alg::distance_to(Linef3(endpoint, endpoint + extruder_pressure_direction.cast()), pivot.cast()); -// float conflict_torque_arm = (to_3d(Vec2f(pivot.head<2>() - extruded_line.b), layer_z).cross( -// extruder_pressure_direction)).norm(); + float conflict_torque_arm = line_alg::distance_to( + Linef3(endpoint, endpoint + extruder_pressure_direction.cast()), pivot.cast()); float extruder_conflict_force = params.tolerable_extruder_conflict_force + std::min(extruded_line.malformation, 1.0f) * params.malformations_additive_conflict_extruder_force; float extruder_conflict_torque = extruder_conflict_force * conflict_torque_arm; @@ -661,7 +664,8 @@ public: BOOST_LOG_TRIVIAL(debug) << "pivot: " << pivot.x() << " " << pivot.y() << " " << pivot.z(); BOOST_LOG_TRIVIAL(debug) - << "sticking_centroid: " << sticking_centroid.x() << " " << sticking_centroid.y() << " " << sticking_centroid.z(); + << "sticking_centroid: " << sticking_centroid.x() << " " << sticking_centroid.y() << " " + << sticking_centroid.z(); BOOST_LOG_TRIVIAL(debug) << "SSG: sticking_force: " << sticking_force; BOOST_LOG_TRIVIAL(debug) @@ -711,10 +715,11 @@ struct WeakestConnection { } }; -void debug_print_graph(const std::vector& islands_graph) { +void debug_print_graph(const std::vector &islands_graph) { std::cout << "BUILT ISLANDS GRAPH:" << std::endl; for (size_t layer_idx = 0; layer_idx < islands_graph.size(); ++layer_idx) { - std::cout << "ISLANDS AT LAYER: " << layer_idx << " AT HEIGHT: " << islands_graph[layer_idx].layer_z << std::endl; + std::cout << "ISLANDS AT LAYER: " << layer_idx << " AT HEIGHT: " << islands_graph[layer_idx].layer_z + << std::endl; for (size_t island_idx = 0; island_idx < islands_graph[layer_idx].islands.size(); ++island_idx) { const Island &island = islands_graph[layer_idx].islands[island_idx]; std::cout << " ISLAND " << island_idx << std::endl; @@ -726,7 +731,6 @@ void debug_print_graph(const std::vector& islands_graph) { } std::cout << "END OF GRAPH" << std::endl; - } Issues check_global_stability(SupportGridFilter supports_presence_grid, From ed1c4d99a7285db6008b43ba12a9483771dd8355 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Thu, 21 Jul 2022 17:40:18 +0200 Subject: [PATCH 51/78] Weakest connection break check also implemented. Tensile force however might be too low approximation. --- src/libslic3r/SupportSpotsGenerator.cpp | 338 ++++++++---------------- 1 file changed, 106 insertions(+), 232 deletions(-) diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index 4abf4dd952..31ac358a0b 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -567,6 +567,16 @@ std::tuple reckon_islands( return {result, current_layer_grid}; } +struct WeakestConnection { + float area = 0.0f; + Vec3f centroid_accumulator = Vec3f::Zero(); + + void add(const WeakestConnection &other) { + this->area += other.area; + this->centroid_accumulator += other.centroid_accumulator; + } +}; + struct CoordinateFunctor { const std::vector *coordinates; CoordinateFunctor(const std::vector *coords) : @@ -639,12 +649,12 @@ public: float sticking_torque = sticking_arm * this->sticking_force; float mass = this->volume * params.filament_density; - const Vec3f &mass_centorid = this->volume_centroid_accumulator / this->volume; + const Vec3f &mass_centroid = this->volume_centroid_accumulator / this->volume; float weight = mass * params.gravity_constant; - float weight_arm = (pivot.head<2>() - mass_centorid.head<2>()).norm(); + float weight_arm = (pivot.head<2>() - mass_centroid.head<2>()).norm(); float weight_torque = weight_arm * weight; - float bed_movement_arm = mass_centorid.z(); + float bed_movement_arm = mass_centroid.z(); float bed_movement_force = params.max_acceleration * mass; float bed_movement_torque = bed_movement_force * bed_movement_arm; @@ -673,7 +683,7 @@ public: BOOST_LOG_TRIVIAL(debug) << "SSG: sticking_torque: " << sticking_torque; BOOST_LOG_TRIVIAL(debug) - << "SSG: weight_arm: " << sticking_arm; + << "SSG: weight_arm: " << weight_arm; BOOST_LOG_TRIVIAL(debug) << "SSG: weight_torque: " << weight_torque; BOOST_LOG_TRIVIAL(debug) @@ -691,6 +701,73 @@ public: return {total_torque / conflict_torque_arm, pivot_site_search_point}; } + float is_strong_enough_while_extruding( + const WeakestConnection &connection, + const ExtrusionLine &extruded_line, + float layer_z, + const Params ¶ms) const { + + Vec2f line_dir = (extruded_line.b - extruded_line.a).normalized(); + Vec3f pivot = connection.centroid_accumulator / connection.area; + + float tensile_force = connection.area * params.tensile_strength; + float tensile_arm = fsqrt(connection.area / float(PI)); + float tensile_torque = tensile_force * tensile_arm; + + const Vec3f &mass_centroid = this->volume_centroid_accumulator / this->volume; + float mass = this->volume * params.filament_density + * ((2.0f * layer_z - pivot.z() - mass_centroid.z()) / (2.0f * layer_z)); + + float weight = mass * params.gravity_constant; + float weight_arm = (pivot.head<2>() - mass_centroid.head<2>()).norm(); + float weight_torque = weight_arm * weight; + + float bed_movement_arm = mass_centroid.z(); + float bed_movement_force = params.max_acceleration * mass; + float bed_movement_torque = bed_movement_force * bed_movement_arm; + + Vec3f extruder_pressure_direction = to_3d(line_dir, 0.0f); + extruder_pressure_direction.z() = -0.1f - extruded_line.malformation * 0.5f; + extruder_pressure_direction.normalize(); + Vec3d endpoint = (to_3d(extruded_line.b, layer_z)).cast(); + float conflict_torque_arm = line_alg::distance_to( + Linef3(endpoint, endpoint + extruder_pressure_direction.cast()), pivot.cast()); + float extruder_conflict_force = params.tolerable_extruder_conflict_force + + std::min(extruded_line.malformation, 1.0f) * params.malformations_additive_conflict_extruder_force; + float extruder_conflict_torque = extruder_conflict_force * conflict_torque_arm; + + float total_torque = bed_movement_torque + extruder_conflict_torque + weight_torque - tensile_torque; +#if 1 + BOOST_LOG_TRIVIAL(debug) + << "pivot: " << pivot.x() << " " << pivot.y() << " " << pivot.z(); + BOOST_LOG_TRIVIAL(debug) + << "mass_centroid: " << mass_centroid.x() << " " << mass_centroid.y() << " " + << mass_centroid.z(); + BOOST_LOG_TRIVIAL(debug) + << "SSG: tensile_force: " << tensile_force; + BOOST_LOG_TRIVIAL(debug) + << "SSG: tensile_arm: " << tensile_arm; + BOOST_LOG_TRIVIAL(debug) + << "SSG: tensile_torque: " << tensile_torque; + BOOST_LOG_TRIVIAL(debug) + << "SSG: weight_arm: " << weight_arm; + BOOST_LOG_TRIVIAL(debug) + << "SSG: weight_torque: " << weight_torque; + BOOST_LOG_TRIVIAL(debug) + << "SSG: bed_movement_arm: " << bed_movement_arm; + BOOST_LOG_TRIVIAL(debug) + << "SSG: bed_movement_torque: " << bed_movement_torque; + BOOST_LOG_TRIVIAL(debug) + << "SSG: conflict_torque_arm: " << conflict_torque_arm; + BOOST_LOG_TRIVIAL(debug) + << "SSG: extruder_conflict_torque: " << extruder_conflict_torque; + BOOST_LOG_TRIVIAL(debug) + << "SSG: total_torque: " << total_torque << " layer_z: " << layer_z; +#endif + + return total_torque / conflict_torque_arm; + } + void add_pivot_point(const Vec3f pivot_point, float sticking_force) { this->pivot_points.push_back(pivot_point); this->sticking_force += sticking_force; @@ -705,16 +782,6 @@ public: }; -struct WeakestConnection { - float area = 0.0f; - Vec3f centroid_accumulator = Vec3f::Zero(); - - void add(const WeakestConnection &other) { - this->area += other.area; - this->centroid_accumulator += other.centroid_accumulator; - } -}; - void debug_print_graph(const std::vector &islands_graph) { std::cout << "BUILT ISLANDS GRAPH:" << std::endl; for (size_t layer_idx = 0; layer_idx < islands_graph.size(); ++layer_idx) { @@ -793,10 +860,21 @@ Issues check_global_stability(SupportGridFilter supports_presence_grid, } auto estimate_strength = [layer_z](const WeakestConnection &conn) { float radius = fsqrt(conn.area / PI); - float arm_len_estimate = std::max(0.001f, layer_z - (conn.centroid_accumulator.z() / conn.area)); + float arm_len_estimate = std::max(1.3f, layer_z - (conn.centroid_accumulator.z() / conn.area)); return radius * conn.area / arm_len_estimate; }; + std::cout << "new_weakest_connection info: " << std::endl; + std::cout << "area: " << new_weakest_connection.area << std::endl; + std::cout << "centroid acc: " << new_weakest_connection.centroid_accumulator.x() << " " + << new_weakest_connection.centroid_accumulator.y() << " " + << new_weakest_connection.centroid_accumulator.z() << std::endl; + std::cout << "transfered_weakest_connection info: " << std::endl; + std::cout << "area: " << transfered_weakest_connection.area << std::endl; + std::cout << "centroid acc: " << transfered_weakest_connection.centroid_accumulator.x() << " " + << transfered_weakest_connection.centroid_accumulator.y() << " " + << transfered_weakest_connection.centroid_accumulator.z() << std::endl; + if (estimate_strength(transfered_weakest_connection) < estimate_strength(new_weakest_connection)) { new_weakest_connection = transfered_weakest_connection; } @@ -833,6 +911,12 @@ Issues check_global_stability(SupportGridFilter supports_presence_grid, for (size_t island_idx = 0; island_idx < islands_graph[layer_idx].islands.size(); ++island_idx) { const Island &island = islands_graph[layer_idx].islands[island_idx]; ObjectPart &part = active_object_parts.at(prev_island_to_object_part_mapping[island_idx]); + WeakestConnection &weakest_conn = prev_island_to_weakest_connection[island_idx]; + std::cout << "Weakest connection info: " << std::endl; + std::cout << "area: " << weakest_conn.area << std::endl; + std::cout << "centroid acc: " << weakest_conn.centroid_accumulator.x() << " " + << weakest_conn.centroid_accumulator.y() << " " + << weakest_conn.centroid_accumulator.z() << std::endl; std::vector dummy { }; LinesDistancer island_lines_dist(dummy); @@ -845,6 +929,10 @@ Issues check_global_stability(SupportGridFilter supports_presence_grid, } else { unchecked_dist = line.len; auto [force, pivot_site_search_point] = part.is_stable_while_extruding(line, layer_z, params); + if (force <= 0) { + force = part.is_strong_enough_while_extruding(weakest_conn, line, layer_z, params); + } + if (force > 0) { if (island_lines_dist.get_lines().empty()) { island_lines_dist = LinesDistancer(island.external_lines); @@ -862,6 +950,9 @@ Issues check_global_stability(SupportGridFilter supports_presence_grid, issues.support_points.emplace_back(support_point, force, to_3d(Vec2f(line.b - line.a).normalized(), 0.0f)); supports_presence_grid.take_position(support_point); + + weakest_conn.area += area; + weakest_conn.centroid_accumulator += support_point * area; } } } @@ -872,223 +963,6 @@ Issues check_global_stability(SupportGridFilter supports_presence_grid, return issues; } -/* - void a() { - - // islands_graph.back() refers to the top most (current) layer - for (size_t island_idx = 0; island_idx < islands_graph.back().islands.size(); ++island_idx) { - Island &island = islands_graph.back().islands[island_idx]; - - std::vector island_external_lines; - for (size_t lidx : islands_lines[island_idx]) { - island_external_lines.push_back(layer_lines[lidx]); - } - LinesDistancer island_lines_dist(island_external_lines); - Accumulator acc = island; // in acc, we accumulate the mass and other properties of the object part as we traverse the islands down to bed - // There is one object part for each island at the top most layer, and each one is computed individually - - // Some of the calculations will be done multiple times - int layer_idx = islands_graph.size() - 1; - // traverse the islands graph down, and for each connection area, calculate if it holds or breaks - while (acc.connected_islands.size() > 0) { - //test for break between layer_idx and layer_idx -1; - LayerIslands below = islands_graph[layer_idx - 1]; // must exist, see while condition - layer_idx--; - // initialize variables that we will accumulate over all islands, which are connected to the current object part - std::vector pivot_points; - Vec2f sticking_centroid; - float connection_area = 0; - for (const auto &pair : acc.connected_islands) { - const Island &below_i = below.islands[pair.first]; - Vec2f centroid = (below_i.volume_centroid_accumulator / below_i.volume).head<2>(); // centroid of the island 'below_i'; TODO it should be centroid of the connection area - pivot_points.push_back(centroid); // for object parts, we also consider breaking pivots in the centroids of the islands - sticking_centroid += centroid * pair.second; // pair.second is connection area in mm^2 - connection_area += pair.second; - } - sticking_centroid /= connection_area; //normalize to get final sticking centroid - for (const Vec3f &p_point : acc.pivot_points) { - pivot_points.push_back(p_point.head<2>()); - } - // Now we have accumulated pivot points, connection area and sticking centroid of the whole layer to the current object part - - // create KD tree over current pivot points - auto coord_fn = [&pivot_points](size_t idx, size_t dim) { - return pivot_points[idx][dim]; - }; - KDTreeIndirect<2, float, decltype(coord_fn)> pivot_points_tree(coord_fn, pivot_points.size()); - - // iterate over extrusions at top layer island, check each for stability - for (const ExtrusionLine &line : island_external_lines) { - Vec2f line_dir = (line.b - line.a).normalized(); - Vec2f pivot_site_search_point = line.b + line_dir * 300.0f; - size_t pivot_idx = find_closest_point(pivot_points_tree, pivot_site_search_point); - const Vec2f &pivot = pivot_points[pivot_idx]; - - float sticking_arm = (pivot - sticking_centroid).norm(); - float sticking_torque = sticking_arm * connection_area * params.tensile_strength; // For breakage in between layers, we compute with tensile strength, not bed adhesion - - float mass = acc.volume * params.filament_density; - const Vec3f &mass_centorid = acc.volume_centroid_accumulator / acc.volume; - float weight = mass * params.gravity_constant; - float weight_arm = (pivot - mass_centorid.head<2>()).norm(); - float weight_torque = weight_arm * weight; - - float bed_movement_arm = mass_centorid.z(); - float bed_movement_force = params.max_acceleration * mass; - float bed_movement_torque = bed_movement_force * bed_movement_arm; - - Vec3f extruder_pressure_direction = to_3d(line_dir, 0.0f); - extruder_pressure_direction.z() = -0.2 - line.malformation * 0.5; - extruder_pressure_direction.normalize(); - float conflict_torque_arm = (to_3d(Vec2f(pivot - line.b), layer_z).cross( - extruder_pressure_direction)).norm(); - float extruder_conflict_force = params.tolerable_extruder_conflict_force + - std::min(line.malformation, 1.0f) * params.malformations_additive_conflict_extruder_force; - float extruder_conflict_torque = extruder_conflict_force * conflict_torque_arm; - - float total_torque = bed_movement_torque + extruder_conflict_torque - weight_torque - sticking_torque; - - if (total_torque > 0) { - Vec2f target_point { }; - size_t _idx { }; - island_lines_dist.signed_distance_from_lines(pivot_site_search_point, _idx, target_point); - if (!supports_presence_grid.position_taken(to_3d(target_point, layer_z))) { - float area = params.support_points_interface_radius * params.support_points_interface_radius - * float(PI); - float sticking_force = area * params.support_adhesion; - Vec3f support_point = to_3d(target_point, layer_z); - island.pivot_points.push_back(support_point); - island.sticking_force += sticking_force; - island.sticking_centroid_accumulator += sticking_force * support_point; - issues.support_points.emplace_back(support_point, - extruder_conflict_torque - sticking_torque, extruder_pressure_direction); - supports_presence_grid.take_position(to_3d(target_point, layer_z)); - } - } - #if 0 - BOOST_LOG_TRIVIAL(debug) - << "SSG: sticking_arm: " << sticking_arm; - BOOST_LOG_TRIVIAL(debug) - << "SSG: sticking_torque: " << sticking_torque; - BOOST_LOG_TRIVIAL(debug) - << "SSG: weight_arm: " << sticking_arm; - BOOST_LOG_TRIVIAL(debug) - << "SSG: weight_torque: " << weight_torque; - BOOST_LOG_TRIVIAL(debug) - << "SSG: bed_movement_arm: " << bed_movement_arm; - BOOST_LOG_TRIVIAL(debug) - << "SSG: bed_movement_torque: " << bed_movement_torque; - BOOST_LOG_TRIVIAL(debug) - << "SSG: conflict_torque_arm: " << conflict_torque_arm; - BOOST_LOG_TRIVIAL(debug) - << "SSG: extruder_conflict_torque: " << extruder_conflict_torque; - BOOST_LOG_TRIVIAL(debug) - << "SSG: total_torque: " << total_torque << " layer_z: " << layer_z; - #endif - } - - std::unordered_map tmp = acc.connected_islands; - acc.connected_islands.clear(); - // finally, add gathered islands to accumulator, and continue down to next layer - for (const auto &pair : tmp) { - const Island &below_i = below.islands[pair.first]; - for (const auto &below_islands : below_i.connected_islands) { - acc.connected_islands[below_islands.first] += below_islands.second; - } - for (const Vec3f &pivot_p : below_i.pivot_points) { - acc.pivot_points.push_back(pivot_p); - } - acc.sticking_centroid_accumulator += below_i.sticking_centroid_accumulator; - acc.sticking_force += below_i.sticking_force; - acc.volume += below_i.volume; - acc.volume_centroid_accumulator += below_i.volume_centroid_accumulator; - } - } - - // We have arrived to the bed level, now check for stability of the object part on the bed - std::vector pivot_points; - for (const Vec3f &p_point : acc.pivot_points) { - pivot_points.push_back(p_point.head<2>()); - } - auto coord_fn = [&pivot_points](size_t idx, size_t dim) { - return pivot_points[idx][dim]; - }; - KDTreeIndirect<2, float, decltype(coord_fn)> pivot_points_tree(coord_fn, pivot_points.size()); - - for (const ExtrusionLine &line : island_external_lines) { - Vec2f line_dir = (line.b - line.a).normalized(); - Vec2f pivot_site_search_point = line.b + line_dir * 300.0f; - size_t pivot_idx = find_closest_point(pivot_points_tree, pivot_site_search_point); - const Vec2f &pivot = pivot_points[pivot_idx]; - - const Vec2f &sticking_centroid = acc.sticking_centroid_accumulator.head<2>() / acc.sticking_force; - float sticking_arm = (pivot - sticking_centroid).norm(); - float sticking_torque = sticking_arm * acc.sticking_force; - - float mass = acc.volume * params.filament_density; - const Vec3f &mass_centorid = acc.volume_centroid_accumulator / acc.volume; - float weight = mass * params.gravity_constant; - float weight_arm = (pivot - mass_centorid.head<2>()).norm(); - float weight_torque = weight_arm * weight; - - float bed_movement_arm = mass_centorid.z(); - float bed_movement_force = params.max_acceleration * mass; - float bed_movement_torque = bed_movement_force * bed_movement_arm; - - Vec3f extruder_pressure_direction = to_3d(line_dir, 0.0f); - extruder_pressure_direction.z() = -0.2 - line.malformation * 0.5; - extruder_pressure_direction.normalize(); - float conflict_torque_arm = (to_3d(Vec2f(pivot - line.b), layer_z).cross( - extruder_pressure_direction)).norm(); - float extruder_conflict_force = params.tolerable_extruder_conflict_force + - std::min(line.malformation, 1.0f) * params.malformations_additive_conflict_extruder_force; - float extruder_conflict_torque = extruder_conflict_force * conflict_torque_arm; - - float total_torque = bed_movement_torque + extruder_conflict_torque - weight_torque - sticking_torque; - - if (total_torque > 0) { - Vec2f target_point; - size_t _idx; - island_lines_dist.signed_distance_from_lines(pivot_site_search_point, _idx, target_point); - if (!supports_presence_grid.position_taken(to_3d(target_point, layer_z))) { - float area = params.support_points_interface_radius * params.support_points_interface_radius - * float(PI); - float sticking_force = area * params.support_adhesion; - Vec3f support_point = to_3d(target_point, layer_z); - island.pivot_points.push_back(support_point); - island.sticking_force += sticking_force; - island.sticking_centroid_accumulator += sticking_force * support_point; - issues.support_points.emplace_back(support_point, - extruder_conflict_torque - sticking_torque, extruder_pressure_direction); - supports_presence_grid.take_position(to_3d(target_point, layer_z)); - } - } - #if 0 - BOOST_LOG_TRIVIAL(debug) - << "SSG: sticking_arm: " << sticking_arm; - BOOST_LOG_TRIVIAL(debug) - << "SSG: sticking_torque: " << sticking_torque; - BOOST_LOG_TRIVIAL(debug) - << "SSG: weight_arm: " << sticking_arm; - BOOST_LOG_TRIVIAL(debug) - << "SSG: weight_torque: " << weight_torque; - BOOST_LOG_TRIVIAL(debug) - << "SSG: bed_movement_arm: " << bed_movement_arm; - BOOST_LOG_TRIVIAL(debug) - << "SSG: bed_movement_torque: " << bed_movement_torque; - BOOST_LOG_TRIVIAL(debug) - << "SSG: conflict_torque_arm: " << conflict_torque_arm; - BOOST_LOG_TRIVIAL(debug) - << "SSG: extruder_conflict_torque: " << extruder_conflict_torque; - BOOST_LOG_TRIVIAL(debug) - << "SSG: total_torque: " << total_torque << " layer_z: " << layer_z; - #endif - } - } - - return issues; - } - */ - std::tuple> check_extrusions_and_build_graph(const PrintObject *po, const Params ¶ms) { #ifdef DEBUG_FILES From 9ff0d49fae61323a9c77f0be84afa2175292f6bf Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Fri, 22 Jul 2022 16:51:44 +0200 Subject: [PATCH 52/78] Implemented calculation of elastic section modulus --- src/libslic3r/PrintObject.cpp | 2 +- src/libslic3r/SupportSpotsGenerator.cpp | 116 ++++++++++++------------ src/libslic3r/SupportSpotsGenerator.hpp | 4 +- 3 files changed, 60 insertions(+), 62 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 674aaec26a..b5848cabac 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -434,7 +434,7 @@ void PrintObject::generate_support_spots() Vec3f point = Vec3f(inv_transform * support_point.position); Vec3f origin = Vec3f( inv_transform * Vec3f(support_point.position.x(), support_point.position.y(), 0.0f)); - selector.enforce_spot(point, origin, 0.3f); + selector.enforce_spot(point, origin, 0.6f); } model_volume->supported_facets.set(selector.selector); diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index 31ac358a0b..63f8f21000 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -251,6 +251,23 @@ public: struct IslandConnection { float area { }; Vec3f centroid_accumulator = Vec3f::Zero(); + Vec2f second_moment_of_area_accumulator = Vec2f::Zero(); + + void add(const IslandConnection &other) { + this->area += other.area; + this->centroid_accumulator += other.centroid_accumulator; + this->second_moment_of_area_accumulator += other.second_moment_of_area_accumulator; + } + + void print_info(const std::string &tag) { + Vec3f centroid = centroid_accumulator / area; + Vec2f variance = + (second_moment_of_area_accumulator / area - centroid.head<2>().cwiseProduct(centroid.head<2>())); + std::cout << tag << std::endl; + std::cout << "area: " << area << std::endl; + std::cout << "centroid: " << centroid.x() << " " << centroid.y() << " " << centroid.z() << std::endl; + std::cout << "variance: " << variance.x() << " " << variance.y() << std::endl; + } }; struct Island { @@ -557,8 +574,11 @@ std::tuple reckon_islands( && prev_layer_grid.get_pixel(coords) != NULL_ISLAND) { IslandConnection &connection = result.islands[current_layer_grid.get_pixel(coords)] .connected_islands[prev_layer_grid.get_pixel(coords)]; + Vec2f current_coords = current_layer_grid.get_pixel_center(coords); connection.area += current_layer_grid.pixel_area(); - connection.centroid_accumulator += to_3d(current_layer_grid.get_pixel_center(coords), result.layer_z) + connection.centroid_accumulator += to_3d(current_coords, result.layer_z) + * current_layer_grid.pixel_area(); + connection.second_moment_of_area_accumulator += current_coords.cwiseProduct(current_coords) * current_layer_grid.pixel_area(); } } @@ -567,16 +587,6 @@ std::tuple reckon_islands( return {result, current_layer_grid}; } -struct WeakestConnection { - float area = 0.0f; - Vec3f centroid_accumulator = Vec3f::Zero(); - - void add(const WeakestConnection &other) { - this->area += other.area; - this->centroid_accumulator += other.centroid_accumulator; - } -}; - struct CoordinateFunctor { const std::vector *coordinates; CoordinateFunctor(const std::vector *coords) : @@ -702,24 +712,25 @@ public: } float is_strong_enough_while_extruding( - const WeakestConnection &connection, + const IslandConnection &connection, const ExtrusionLine &extruded_line, float layer_z, const Params ¶ms) const { Vec2f line_dir = (extruded_line.b - extruded_line.a).normalized(); - Vec3f pivot = connection.centroid_accumulator / connection.area; - - float tensile_force = connection.area * params.tensile_strength; - float tensile_arm = fsqrt(connection.area / float(PI)); - float tensile_torque = tensile_force * tensile_arm; + Vec3f centroid = connection.centroid_accumulator / connection.area; + Vec2f variance = (connection.second_moment_of_area_accumulator / connection.area + - centroid.head<2>().cwiseProduct(centroid.head<2>())); + float extreme_fiber_dist = variance.cwiseSqrt().norm(); + float elastic_section_modulus = connection.area * (variance.x() + variance.y()) / extreme_fiber_dist; + float yield_torque = elastic_section_modulus * params.yield_strength; const Vec3f &mass_centroid = this->volume_centroid_accumulator / this->volume; float mass = this->volume * params.filament_density - * ((2.0f * layer_z - pivot.z() - mass_centroid.z()) / (2.0f * layer_z)); + * ((2.0f * layer_z - centroid.z() - mass_centroid.z()) / (2.0f * layer_z)); float weight = mass * params.gravity_constant; - float weight_arm = (pivot.head<2>() - mass_centroid.head<2>()).norm(); + float weight_arm = (centroid.head<2>() - mass_centroid.head<2>()).norm(); float weight_torque = weight_arm * weight; float bed_movement_arm = mass_centroid.z(); @@ -731,24 +742,24 @@ public: extruder_pressure_direction.normalize(); Vec3d endpoint = (to_3d(extruded_line.b, layer_z)).cast(); float conflict_torque_arm = line_alg::distance_to( - Linef3(endpoint, endpoint + extruder_pressure_direction.cast()), pivot.cast()); + Linef3(endpoint, endpoint + extruder_pressure_direction.cast()), centroid.cast()); float extruder_conflict_force = params.tolerable_extruder_conflict_force + std::min(extruded_line.malformation, 1.0f) * params.malformations_additive_conflict_extruder_force; float extruder_conflict_torque = extruder_conflict_force * conflict_torque_arm; - float total_torque = bed_movement_torque + extruder_conflict_torque + weight_torque - tensile_torque; + float total_torque = bed_movement_torque + extruder_conflict_torque + weight_torque - yield_torque; #if 1 BOOST_LOG_TRIVIAL(debug) - << "pivot: " << pivot.x() << " " << pivot.y() << " " << pivot.z(); + << "centroid: " << centroid.x() << " " << centroid.y() << " " << centroid.z(); BOOST_LOG_TRIVIAL(debug) << "mass_centroid: " << mass_centroid.x() << " " << mass_centroid.y() << " " << mass_centroid.z(); BOOST_LOG_TRIVIAL(debug) - << "SSG: tensile_force: " << tensile_force; + << "variance: " << variance.x() << " " << variance.y(); BOOST_LOG_TRIVIAL(debug) - << "SSG: tensile_arm: " << tensile_arm; + << "SSG: elastic_section_modulus: " << elastic_section_modulus; BOOST_LOG_TRIVIAL(debug) - << "SSG: tensile_torque: " << tensile_torque; + << "SSG: yield_torque: " << yield_torque; BOOST_LOG_TRIVIAL(debug) << "SSG: weight_arm: " << weight_arm; BOOST_LOG_TRIVIAL(debug) @@ -809,8 +820,8 @@ Issues check_global_stability(SupportGridFilter supports_presence_grid, std::unordered_map prev_island_to_object_part_mapping; std::unordered_map next_island_to_object_part_mapping; - std::unordered_map prev_island_to_weakest_connection; - std::unordered_map next_island_to_weakest_connection; + std::unordered_map prev_island_to_weakest_connection; + std::unordered_map next_island_to_weakest_connection; for (size_t layer_idx = 0; layer_idx < islands_graph.size(); ++layer_idx) { float layer_z = islands_graph[layer_idx].layer_z; @@ -818,11 +829,7 @@ Issues check_global_stability(SupportGridFilter supports_presence_grid, std::cout << "at layer: " << layer_idx << " the following island to object mapping is used:" << std::endl; for (const auto &m : prev_island_to_object_part_mapping) { std::cout << "island " << m.first << " maps to part " << m.second << std::endl; - Vec3f connection_center = prev_island_to_weakest_connection[m.first].centroid_accumulator - / prev_island_to_weakest_connection[m.first].area; - std::cout << " island has weak point with connection area: " << - prev_island_to_weakest_connection[m.first].area << " and center: " << - connection_center.x() << " " << connection_center.y() << " " << connection_center.z() << std::endl; + prev_island_to_weakest_connection[m.first].print_info("connection info:"); } for (size_t island_idx = 0; island_idx < islands_graph[layer_idx].islands.size(); ++island_idx) { @@ -833,20 +840,19 @@ Issues check_global_stability(SupportGridFilter supports_presence_grid, active_object_parts.emplace(part_idx, ObjectPart(island)); next_island_to_object_part_mapping.emplace(island_idx, part_idx); next_island_to_weakest_connection.emplace(island_idx, - WeakestConnection { INFINITY, Vec3f::Zero() }); + IslandConnection { 1.0f, Vec3f::Zero(), Vec2f { INFINITY, INFINITY } }); layer_active_parts.insert(part_idx); } else { size_t final_part_idx { }; - WeakestConnection transfered_weakest_connection { }; - WeakestConnection new_weakest_connection { }; + IslandConnection transfered_weakest_connection { }; + IslandConnection new_weakest_connection { }; // MERGE parts { std::unordered_set part_indices; for (const auto &connection : island.connected_islands) { part_indices.insert(prev_island_to_object_part_mapping.at(connection.first)); transfered_weakest_connection.add(prev_island_to_weakest_connection.at(connection.first)); - new_weakest_connection.area += connection.second.area; - new_weakest_connection.centroid_accumulator += connection.second.centroid_accumulator; + new_weakest_connection.add(connection.second); } final_part_idx = *part_indices.begin(); for (size_t part_idx : part_indices) { @@ -858,22 +864,16 @@ Issues check_global_stability(SupportGridFilter supports_presence_grid, } } } - auto estimate_strength = [layer_z](const WeakestConnection &conn) { - float radius = fsqrt(conn.area / PI); - float arm_len_estimate = std::max(1.3f, layer_z - (conn.centroid_accumulator.z() / conn.area)); - return radius * conn.area / arm_len_estimate; + auto estimate_strength = [layer_z](const IslandConnection &conn) { + Vec3f centroid = conn.centroid_accumulator / conn.area; + float min_variance = (conn.second_moment_of_area_accumulator / conn.area + - centroid.head<2>().cwiseProduct(centroid.head<2>())).minCoeff(); + float arm_len_estimate = std::max(1.1f, layer_z - (conn.centroid_accumulator.z() / conn.area)); + return min_variance / arm_len_estimate; }; - std::cout << "new_weakest_connection info: " << std::endl; - std::cout << "area: " << new_weakest_connection.area << std::endl; - std::cout << "centroid acc: " << new_weakest_connection.centroid_accumulator.x() << " " - << new_weakest_connection.centroid_accumulator.y() << " " - << new_weakest_connection.centroid_accumulator.z() << std::endl; - std::cout << "transfered_weakest_connection info: " << std::endl; - std::cout << "area: " << transfered_weakest_connection.area << std::endl; - std::cout << "centroid acc: " << transfered_weakest_connection.centroid_accumulator.x() << " " - << transfered_weakest_connection.centroid_accumulator.y() << " " - << transfered_weakest_connection.centroid_accumulator.z() << std::endl; + new_weakest_connection.print_info("new_weakest_connection"); + transfered_weakest_connection.print_info("transfered_weakest_connection"); if (estimate_strength(transfered_weakest_connection) < estimate_strength(new_weakest_connection)) { new_weakest_connection = transfered_weakest_connection; @@ -911,20 +911,16 @@ Issues check_global_stability(SupportGridFilter supports_presence_grid, for (size_t island_idx = 0; island_idx < islands_graph[layer_idx].islands.size(); ++island_idx) { const Island &island = islands_graph[layer_idx].islands[island_idx]; ObjectPart &part = active_object_parts.at(prev_island_to_object_part_mapping[island_idx]); - WeakestConnection &weakest_conn = prev_island_to_weakest_connection[island_idx]; - std::cout << "Weakest connection info: " << std::endl; - std::cout << "area: " << weakest_conn.area << std::endl; - std::cout << "centroid acc: " << weakest_conn.centroid_accumulator.x() << " " - << weakest_conn.centroid_accumulator.y() << " " - << weakest_conn.centroid_accumulator.z() << std::endl; + IslandConnection &weakest_conn = prev_island_to_weakest_connection[island_idx]; + weakest_conn.print_info("weakest connection info: "); std::vector dummy { }; LinesDistancer island_lines_dist(dummy); float unchecked_dist = params.min_distance_between_support_points + 1.0f; for (const ExtrusionLine &line : island.external_lines) { - if (unchecked_dist + line.len < params.min_distance_between_support_points - && line.malformation < 0.3f) { + if ((unchecked_dist + line.len < params.min_distance_between_support_points + && line.malformation < 0.3f) || line.len == 0) { unchecked_dist += line.len; } else { unchecked_dist = line.len; @@ -953,6 +949,8 @@ Issues check_global_stability(SupportGridFilter supports_presence_grid, weakest_conn.area += area; weakest_conn.centroid_accumulator += support_point * area; + weakest_conn.second_moment_of_area_accumulator += area + * support_point.head<2>().cwiseProduct(support_point.head<2>()); } } } diff --git a/src/libslic3r/SupportSpotsGenerator.hpp b/src/libslic3r/SupportSpotsGenerator.hpp index 2a9b48fddc..15b5802895 100644 --- a/src/libslic3r/SupportSpotsGenerator.hpp +++ b/src/libslic3r/SupportSpotsGenerator.hpp @@ -19,11 +19,11 @@ struct Params { const float base_adhesion = 3.0f * gravity_constant; // adhesion per mm^2 of first layer const float support_adhesion = 1.0f * gravity_constant; // adhesion per mm^2 of support interface layer - const float support_points_interface_radius = 0.3f; // mm + const float support_points_interface_radius = 0.6f; // mm const float max_acceleration = 9*1000.0f; // mm/s^2 ; max acceleration of object (bed) in XY (NOTE: The max hit is received by the object in the jerk phase, so the usual machine limits are too low) const float filament_density = 1.25f * 0.001f; // g/mm^3 ; Common filaments are very lightweight, so precise number is not that important - const float tensile_strength = 33000.0f; // mN/mm^2; 33 MPa is tensile strength of ABS, which has the lowest tensile strength from common materials. + const float yield_strength = 33000.0f; // mN/mm^2; 33 MPa is yield strength of ABS, which has the lowest yield strength from common materials. const float tolerable_extruder_conflict_force = 50.0f * gravity_constant; // force that can occasionally push the model due to various factors (filament leaks, small curling, ... ); current value corresponds to weight of X grams const float malformations_additive_conflict_extruder_force = 100.0f * gravity_constant; // for areas with possible high layered curled filaments From 50e7cc9d4c95523341adefcb1a0f47eb182aaa70 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Mon, 25 Jul 2022 12:32:48 +0200 Subject: [PATCH 53/78] fix bug with removed object parts which were still referenced by other islands --- src/libslic3r/SupportSpotsGenerator.cpp | 109 ++++++++++++++---------- 1 file changed, 65 insertions(+), 44 deletions(-) diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index 63f8f21000..b7067fd9c3 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -811,56 +811,90 @@ void debug_print_graph(const std::vector &islands_graph) { } +class ActiveObjectParts { + size_t next_part_idx = 0; + std::unordered_map active_object_parts; + std::unordered_map active_object_parts_id_mapping; + +public: + size_t get_flat_id(size_t id) { + size_t index = active_object_parts_id_mapping.at(id); + while (index != active_object_parts_id_mapping.at(index)) { + index = active_object_parts_id_mapping.at(index); + } + size_t i = id; + while (index != active_object_parts_id_mapping.at(i)) { + size_t next = active_object_parts_id_mapping[i]; + active_object_parts_id_mapping[i] = index; + i = next; + } + return index; + } + + ObjectPart& access(size_t id) { + return this->active_object_parts.at(this->get_flat_id(id)); + } + + size_t insert(const Island &island) { + this->active_object_parts.emplace(next_part_idx, ObjectPart(island)); + this->active_object_parts_id_mapping.emplace(next_part_idx, next_part_idx); + return next_part_idx++; + } + + void merge(size_t from, size_t to) { + size_t to_flat = this->get_flat_id(to); + size_t from_flat = this->get_flat_id(from); + active_object_parts.at(to_flat).add(active_object_parts.at(from_flat)); + active_object_parts.erase(from_flat); + active_object_parts_id_mapping[from] = to_flat; + } +}; + Issues check_global_stability(SupportGridFilter supports_presence_grid, const std::vector &islands_graph, const Params ¶ms) { debug_print_graph(islands_graph); Issues issues { }; - size_t next_part_idx = 0; - std::unordered_map active_object_parts; + ActiveObjectParts active_object_parts { }; std::unordered_map prev_island_to_object_part_mapping; std::unordered_map next_island_to_object_part_mapping; - std::unordered_map prev_island_to_weakest_connection; - std::unordered_map next_island_to_weakest_connection; + std::unordered_map prev_island_weakest_connection; + std::unordered_map next_island_weakest_connection; for (size_t layer_idx = 0; layer_idx < islands_graph.size(); ++layer_idx) { float layer_z = islands_graph[layer_idx].layer_z; - std::unordered_set layer_active_parts; std::cout << "at layer: " << layer_idx << " the following island to object mapping is used:" << std::endl; for (const auto &m : prev_island_to_object_part_mapping) { std::cout << "island " << m.first << " maps to part " << m.second << std::endl; - prev_island_to_weakest_connection[m.first].print_info("connection info:"); + prev_island_weakest_connection[m.first].print_info("connection info:"); } for (size_t island_idx = 0; island_idx < islands_graph[layer_idx].islands.size(); ++island_idx) { const Island &island = islands_graph[layer_idx].islands[island_idx]; if (island.connected_islands.empty()) { //new object part emerging - size_t part_idx = next_part_idx; - next_part_idx++; - active_object_parts.emplace(part_idx, ObjectPart(island)); - next_island_to_object_part_mapping.emplace(island_idx, part_idx); - next_island_to_weakest_connection.emplace(island_idx, + size_t part_id = active_object_parts.insert(island); + next_island_to_object_part_mapping.emplace(island_idx, part_id); + next_island_weakest_connection.emplace(island_idx, IslandConnection { 1.0f, Vec3f::Zero(), Vec2f { INFINITY, INFINITY } }); - layer_active_parts.insert(part_idx); } else { - size_t final_part_idx { }; + size_t final_part_id { }; IslandConnection transfered_weakest_connection { }; IslandConnection new_weakest_connection { }; // MERGE parts { - std::unordered_set part_indices; + std::unordered_set parts_ids; for (const auto &connection : island.connected_islands) { - part_indices.insert(prev_island_to_object_part_mapping.at(connection.first)); - transfered_weakest_connection.add(prev_island_to_weakest_connection.at(connection.first)); + size_t part_id = active_object_parts.get_flat_id(prev_island_to_object_part_mapping.at(connection.first)); + parts_ids.insert(part_id); + transfered_weakest_connection.add(prev_island_weakest_connection.at(connection.first)); new_weakest_connection.add(connection.second); } - final_part_idx = *part_indices.begin(); - for (size_t part_idx : part_indices) { - if (final_part_idx != part_idx) { - std::cout << "at layer: " << layer_idx << " merging object part: " << part_idx - << " into final part: " << final_part_idx << std::endl; - active_object_parts.at(final_part_idx).add(active_object_parts.at(part_idx)); - active_object_parts.erase(part_idx); + final_part_id = *parts_ids.begin(); + for (size_t part_id : parts_ids) { + if (final_part_id != part_id) { + std::cout << "at layer: " << layer_idx << " merging object part: " << part_id + << " into final part: " << final_part_id << std::endl; + active_object_parts.merge(part_id, final_part_id); } } } @@ -878,31 +912,17 @@ Issues check_global_stability(SupportGridFilter supports_presence_grid, if (estimate_strength(transfered_weakest_connection) < estimate_strength(new_weakest_connection)) { new_weakest_connection = transfered_weakest_connection; } - next_island_to_weakest_connection.emplace(island_idx, new_weakest_connection); - next_island_to_object_part_mapping.emplace(island_idx, final_part_idx); - ObjectPart &part = active_object_parts[final_part_idx]; + next_island_weakest_connection.emplace(island_idx, new_weakest_connection); + next_island_to_object_part_mapping.emplace(island_idx, final_part_id); + ObjectPart &part = active_object_parts.access(final_part_id); part.add(ObjectPart(island)); - layer_active_parts.insert(final_part_idx); } } - std::unordered_set parts_to_delete; - for (const auto &part : active_object_parts) { - if (layer_active_parts.find(part.first) == layer_active_parts.end()) { - parts_to_delete.insert(part.first); - } else { - std::cout << "at layer " << layer_idx << " part is still active: " << part.first << std::endl; - part.second.print(); - } - } - for (size_t part_id : parts_to_delete) { - active_object_parts.erase(part_id); - std::cout << " at layer: " << layer_idx << " removing object part " << part_id << std::endl; - } prev_island_to_object_part_mapping = next_island_to_object_part_mapping; next_island_to_object_part_mapping.clear(); - prev_island_to_weakest_connection = next_island_to_weakest_connection; - next_island_to_weakest_connection.clear(); + prev_island_weakest_connection = next_island_weakest_connection; + next_island_weakest_connection.clear(); // All object parts updated, inactive parts removed and weakest point of each island updated as well. // Now compute the stability of each active object part, adding supports where necessary, and also @@ -910,8 +930,9 @@ Issues check_global_stability(SupportGridFilter supports_presence_grid, for (size_t island_idx = 0; island_idx < islands_graph[layer_idx].islands.size(); ++island_idx) { const Island &island = islands_graph[layer_idx].islands[island_idx]; - ObjectPart &part = active_object_parts.at(prev_island_to_object_part_mapping[island_idx]); - IslandConnection &weakest_conn = prev_island_to_weakest_connection[island_idx]; + ObjectPart &part = active_object_parts.access(prev_island_to_object_part_mapping[island_idx]); + part.print(); + IslandConnection &weakest_conn = prev_island_weakest_connection[island_idx]; weakest_conn.print_info("weakest connection info: "); std::vector dummy { }; From 3f7f5ec0ede76a72fceb4f798fc4f4fd24017238 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Mon, 25 Jul 2022 13:36:50 +0200 Subject: [PATCH 54/78] Lowered default extrusion conflict force - it probably needs more adjusting, after the bed adhesion is reworked with elastic section modulus --- src/libslic3r/SupportSpotsGenerator.cpp | 9 +++++---- src/libslic3r/SupportSpotsGenerator.hpp | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index b7067fd9c3..28ea8d17c1 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -674,7 +674,7 @@ public: Vec3d endpoint = (to_3d(extruded_line.b, layer_z)).cast(); float conflict_torque_arm = line_alg::distance_to( Linef3(endpoint, endpoint + extruder_pressure_direction.cast()), pivot.cast()); - float extruder_conflict_force = params.tolerable_extruder_conflict_force + + float extruder_conflict_force = params.standard_extruder_conflict_force + std::min(extruded_line.malformation, 1.0f) * params.malformations_additive_conflict_extruder_force; float extruder_conflict_torque = extruder_conflict_force * conflict_torque_arm; @@ -721,6 +721,7 @@ public: Vec3f centroid = connection.centroid_accumulator / connection.area; Vec2f variance = (connection.second_moment_of_area_accumulator / connection.area - centroid.head<2>().cwiseProduct(centroid.head<2>())); + variance = variance.cwiseProduct(line_dir.cwiseAbs()); float extreme_fiber_dist = variance.cwiseSqrt().norm(); float elastic_section_modulus = connection.area * (variance.x() + variance.y()) / extreme_fiber_dist; float yield_torque = elastic_section_modulus * params.yield_strength; @@ -733,17 +734,17 @@ public: float weight_arm = (centroid.head<2>() - mass_centroid.head<2>()).norm(); float weight_torque = weight_arm * weight; - float bed_movement_arm = mass_centroid.z(); + float bed_movement_arm = std::max(0.0f, mass_centroid.z() - centroid.z()); float bed_movement_force = params.max_acceleration * mass; float bed_movement_torque = bed_movement_force * bed_movement_arm; Vec3f extruder_pressure_direction = to_3d(line_dir, 0.0f); - extruder_pressure_direction.z() = -0.1f - extruded_line.malformation * 0.5f; + extruder_pressure_direction.z() = -extruded_line.malformation * 0.5f; extruder_pressure_direction.normalize(); Vec3d endpoint = (to_3d(extruded_line.b, layer_z)).cast(); float conflict_torque_arm = line_alg::distance_to( Linef3(endpoint, endpoint + extruder_pressure_direction.cast()), centroid.cast()); - float extruder_conflict_force = params.tolerable_extruder_conflict_force + + float extruder_conflict_force = params.standard_extruder_conflict_force + std::min(extruded_line.malformation, 1.0f) * params.malformations_additive_conflict_extruder_force; float extruder_conflict_torque = extruder_conflict_force * conflict_torque_arm; diff --git a/src/libslic3r/SupportSpotsGenerator.hpp b/src/libslic3r/SupportSpotsGenerator.hpp index 15b5802895..0813663696 100644 --- a/src/libslic3r/SupportSpotsGenerator.hpp +++ b/src/libslic3r/SupportSpotsGenerator.hpp @@ -24,7 +24,7 @@ struct Params { const float max_acceleration = 9*1000.0f; // mm/s^2 ; max acceleration of object (bed) in XY (NOTE: The max hit is received by the object in the jerk phase, so the usual machine limits are too low) const float filament_density = 1.25f * 0.001f; // g/mm^3 ; Common filaments are very lightweight, so precise number is not that important const float yield_strength = 33000.0f; // mN/mm^2; 33 MPa is yield strength of ABS, which has the lowest yield strength from common materials. - const float tolerable_extruder_conflict_force = 50.0f * gravity_constant; // force that can occasionally push the model due to various factors (filament leaks, small curling, ... ); current value corresponds to weight of X grams + const float standard_extruder_conflict_force = 1.0f * gravity_constant; // force that can occasionally push the model due to various factors (filament leaks, small curling, ... ); current value corresponds to weight of X grams const float malformations_additive_conflict_extruder_force = 100.0f * gravity_constant; // for areas with possible high layered curled filaments }; From 2808e41238dded9c09841a252db3b56df0fd558d Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Mon, 25 Jul 2022 17:48:42 +0200 Subject: [PATCH 55/78] reworked bed adhesion model to use elastic section modulus fixed units updated bed adhesion value --- src/libslic3r/SupportSpotsGenerator.cpp | 317 ++++++++++-------------- src/libslic3r/SupportSpotsGenerator.hpp | 23 +- 2 files changed, 144 insertions(+), 196 deletions(-) diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index 28ea8d17c1..b313030ac2 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -272,11 +272,11 @@ struct IslandConnection { struct Island { std::unordered_map connected_islands { }; - std::vector pivot_points { }; // for support points present on this layer (or bed extrusions) float volume { }; Vec3f volume_centroid_accumulator = Vec3f::Zero(); - float sticking_force { }; // for support points present on this layer (or bed extrusions) + float sticking_area { }; // for support points present on this layer (or bed extrusions) Vec3f sticking_centroid_accumulator = Vec3f::Zero(); + Vec2f sticking_second_moment_of_area_accumulator = Vec2f::Zero(); std::vector external_lines; }; @@ -473,8 +473,8 @@ std::tuple reckon_islands( if (!layer_lines[extrusions[e].first].is_external_perimeter()) { bool island_assigned = false; for (size_t i = 0; i < islands.size(); ++i) { - size_t _idx; - Vec2f _pt; + size_t _idx = 0; + Vec2f _pt = Vec2f::Zero(); if (islands[i].signed_distance_from_lines(layer_lines[extrusions[e].first].a, _idx, _pt) < 0) { island_extrusions[i].push_back(e); island_assigned = true; @@ -530,22 +530,16 @@ std::tuple reckon_islands( * volume; if (first_layer) { - float sticking_force = line.len * flow_width * params.base_adhesion; - island.sticking_force += sticking_force; - island.sticking_centroid_accumulator += sticking_force - * to_3d(Vec2f((line.a + line.b) / 2.0f), float(layer->slice_z)); - if (line.is_external_perimeter()) { - island.pivot_points.push_back(to_3d(Vec2f(line.b), float(layer->slice_z))); - } + float sticking_area = line.len * flow_width; + island.sticking_area += sticking_area; + Vec2f middle = Vec2f((line.a + line.b) / 2.0f); + island.sticking_centroid_accumulator += sticking_area * to_3d(middle, float(layer->slice_z)); + island.sticking_second_moment_of_area_accumulator += sticking_area * middle.cwiseProduct(middle); } else if (layer_lines[lidx].support_point_generated) { - float support_interface_area = params.support_points_interface_radius - * params.support_points_interface_radius - * float(PI); - float sticking_force = support_interface_area * params.support_adhesion; - island.sticking_force += sticking_force; - island.sticking_centroid_accumulator += sticking_force - * to_3d(Vec2f(line.b), float(layer->slice_z)); - island.pivot_points.push_back(to_3d(Vec2f(line.b), float(layer->slice_z))); + float sticking_area = line.len * flow_width; + island.sticking_area += sticking_area; + island.sticking_centroid_accumulator += sticking_area * to_3d(line.b, float(layer->slice_z)); + island.sticking_second_moment_of_area_accumulator += sticking_area * line.b.cwiseProduct(line.b); } } } @@ -604,194 +598,157 @@ struct CoordinateFunctor { class ObjectPart { float volume { }; Vec3f volume_centroid_accumulator = Vec3f::Zero(); - float sticking_force { }; + float sticking_area { }; Vec3f sticking_centroid_accumulator = Vec3f::Zero(); - std::vector pivot_points { }; - - CoordinateFunctor pivots_coordinate_functor; - bool is_pivot_tree_valid = false; - KDTreeIndirect<3, float, CoordinateFunctor> pivot_tree { CoordinateFunctor { } }; - - void check_pivot_tree() { - if (!is_pivot_tree_valid) { - this->pivots_coordinate_functor = CoordinateFunctor(&this->pivot_points); - this->pivot_tree = { this->pivots_coordinate_functor }; - pivot_tree.build(pivot_points.size()); - is_pivot_tree_valid = true; - } - } + Vec2f sticking_second_moment_of_area_accumulator = Vec2f::Zero(); public: - void add(const ObjectPart &other) { - this->volume_centroid_accumulator += other.volume_centroid_accumulator; - this->volume += other.volume; - this->sticking_force += other.sticking_force; - this->sticking_centroid_accumulator += other.sticking_centroid_accumulator; - this->pivot_points.insert(this->pivot_points.end(), other.pivot_points.begin(), other.pivot_points.end()); - this->is_pivot_tree_valid = this->is_pivot_tree_valid && other.pivot_points.empty(); - } + ObjectPart() = default; ObjectPart(const Island &island) { this->volume = island.volume; this->volume_centroid_accumulator = island.volume_centroid_accumulator; - this->sticking_force = island.sticking_force; + this->sticking_area = island.sticking_area; this->sticking_centroid_accumulator = island.sticking_centroid_accumulator; - this->pivot_points = island.pivot_points; + this->sticking_second_moment_of_area_accumulator = island.sticking_second_moment_of_area_accumulator; } - ObjectPart() = default; - - std::tuple is_stable_while_extruding(const ExtrusionLine &extruded_line, float layer_z, - const Params ¶ms) { - if (pivot_points.empty()) { - return {this->volume * params.filament_density*params.gravity_constant,Vec3f {0.0f,0.0f,-1.0f}}; - } - - check_pivot_tree(); - - Vec2f line_dir = (extruded_line.b - extruded_line.a).normalized(); - Vec3f pivot_site_search_point = to_3d(Vec2f(extruded_line.b + line_dir * 300.0f), layer_z); - size_t pivot_idx = find_closest_point(this->pivot_tree, pivot_site_search_point); - const Vec3f &pivot = pivot_points[pivot_idx]; - - const Vec3f &sticking_centroid = this->sticking_centroid_accumulator / this->sticking_force; - float sticking_arm = (pivot - sticking_centroid).norm(); - float sticking_torque = sticking_arm * this->sticking_force; - - float mass = this->volume * params.filament_density; - const Vec3f &mass_centroid = this->volume_centroid_accumulator / this->volume; - float weight = mass * params.gravity_constant; - float weight_arm = (pivot.head<2>() - mass_centroid.head<2>()).norm(); - float weight_torque = weight_arm * weight; - - float bed_movement_arm = mass_centroid.z(); - float bed_movement_force = params.max_acceleration * mass; - float bed_movement_torque = bed_movement_force * bed_movement_arm; - - Vec3f extruder_pressure_direction = to_3d(line_dir, 0.0f); - extruder_pressure_direction.z() = -0.1f - extruded_line.malformation * 0.5f; - extruder_pressure_direction.normalize(); - Vec3d endpoint = (to_3d(extruded_line.b, layer_z)).cast(); - float conflict_torque_arm = line_alg::distance_to( - Linef3(endpoint, endpoint + extruder_pressure_direction.cast()), pivot.cast()); - float extruder_conflict_force = params.standard_extruder_conflict_force + - std::min(extruded_line.malformation, 1.0f) * params.malformations_additive_conflict_extruder_force; - float extruder_conflict_torque = extruder_conflict_force * conflict_torque_arm; - - float total_torque = bed_movement_torque + extruder_conflict_torque - weight_torque - sticking_torque; - -#if 1 - BOOST_LOG_TRIVIAL(debug) - << "pivot: " << pivot.x() << " " << pivot.y() << " " << pivot.z(); - BOOST_LOG_TRIVIAL(debug) - << "sticking_centroid: " << sticking_centroid.x() << " " << sticking_centroid.y() << " " - << sticking_centroid.z(); - BOOST_LOG_TRIVIAL(debug) - << "SSG: sticking_force: " << sticking_force; - BOOST_LOG_TRIVIAL(debug) - << "SSG: sticking_arm: " << sticking_arm; - BOOST_LOG_TRIVIAL(debug) - << "SSG: sticking_torque: " << sticking_torque; - BOOST_LOG_TRIVIAL(debug) - << "SSG: weight_arm: " << weight_arm; - BOOST_LOG_TRIVIAL(debug) - << "SSG: weight_torque: " << weight_torque; - BOOST_LOG_TRIVIAL(debug) - << "SSG: bed_movement_arm: " << bed_movement_arm; - BOOST_LOG_TRIVIAL(debug) - << "SSG: bed_movement_torque: " << bed_movement_torque; - BOOST_LOG_TRIVIAL(debug) - << "SSG: conflict_torque_arm: " << conflict_torque_arm; - BOOST_LOG_TRIVIAL(debug) - << "SSG: extruder_conflict_torque: " << extruder_conflict_torque; - BOOST_LOG_TRIVIAL(debug) - << "SSG: total_torque: " << total_torque << " layer_z: " << layer_z; -#endif - - return {total_torque / conflict_torque_arm, pivot_site_search_point}; + void add(const ObjectPart &other) { + this->volume_centroid_accumulator += other.volume_centroid_accumulator; + this->volume += other.volume; + this->sticking_area += other.sticking_area; + this->sticking_centroid_accumulator += other.sticking_centroid_accumulator; + this->sticking_second_moment_of_area_accumulator += other.sticking_second_moment_of_area_accumulator; } - float is_strong_enough_while_extruding( + void add_support_point(const Vec3f &position, float sticking_area) { + this->sticking_area += sticking_area; + this->sticking_centroid_accumulator += sticking_area * position; + this->sticking_second_moment_of_area_accumulator += sticking_area + * position.head<2>().cwiseProduct(position.head<2>()); + } + + float is_stable_while_extruding( const IslandConnection &connection, const ExtrusionLine &extruded_line, float layer_z, const Params ¶ms) const { Vec2f line_dir = (extruded_line.b - extruded_line.a).normalized(); - Vec3f centroid = connection.centroid_accumulator / connection.area; - Vec2f variance = (connection.second_moment_of_area_accumulator / connection.area - - centroid.head<2>().cwiseProduct(centroid.head<2>())); - variance = variance.cwiseProduct(line_dir.cwiseAbs()); - float extreme_fiber_dist = variance.cwiseSqrt().norm(); - float elastic_section_modulus = connection.area * (variance.x() + variance.y()) / extreme_fiber_dist; - float yield_torque = elastic_section_modulus * params.yield_strength; + + auto compute_elastic_section_modulus = [&line_dir]( + const Vec3f ¢roid_accumulator, const Vec2f &second_moment_of_area_accumulator, const float &area) { + Vec3f centroid = centroid_accumulator / area; + Vec2f variance = (second_moment_of_area_accumulator / area + - centroid.head<2>().cwiseProduct(centroid.head<2>())); + variance = variance.cwiseProduct(line_dir.cwiseAbs()); + float extreme_fiber_dist = variance.cwiseSqrt().norm(); + float elastic_section_modulus = area * (variance.x() + variance.y()) / extreme_fiber_dist; + return elastic_section_modulus; + }; const Vec3f &mass_centroid = this->volume_centroid_accumulator / this->volume; - float mass = this->volume * params.filament_density - * ((2.0f * layer_z - centroid.z() - mass_centroid.z()) / (2.0f * layer_z)); - + float mass = this->volume * params.filament_density; float weight = mass * params.gravity_constant; - float weight_arm = (centroid.head<2>() - mass_centroid.head<2>()).norm(); - float weight_torque = weight_arm * weight; - float bed_movement_arm = std::max(0.0f, mass_centroid.z() - centroid.z()); - float bed_movement_force = params.max_acceleration * mass; - float bed_movement_torque = bed_movement_force * bed_movement_arm; + float movement_force = params.max_acceleration * mass; Vec3f extruder_pressure_direction = to_3d(line_dir, 0.0f); extruder_pressure_direction.z() = -extruded_line.malformation * 0.5f; extruder_pressure_direction.normalize(); Vec3d endpoint = (to_3d(extruded_line.b, layer_z)).cast(); - float conflict_torque_arm = line_alg::distance_to( - Linef3(endpoint, endpoint + extruder_pressure_direction.cast()), centroid.cast()); float extruder_conflict_force = params.standard_extruder_conflict_force + std::min(extruded_line.malformation, 1.0f) * params.malformations_additive_conflict_extruder_force; - float extruder_conflict_torque = extruder_conflict_force * conflict_torque_arm; - float total_torque = bed_movement_torque + extruder_conflict_torque + weight_torque - yield_torque; + // section for bed calculations + { + Vec3f bed_centroid = this->sticking_centroid_accumulator / this->sticking_area; + float bed_yield_torque = compute_elastic_section_modulus(this->sticking_centroid_accumulator, + this->sticking_second_moment_of_area_accumulator, this->sticking_area) + * params.bed_adhesion_yield_strength; + + float bed_weight_arm = (bed_centroid.head<2>() - mass_centroid.head<2>()).norm(); + float bed_weight_torque = bed_weight_arm * weight; + + float bed_movement_arm = std::max(0.0f, mass_centroid.z() - bed_centroid.z()); + float bed_movement_torque = movement_force * bed_movement_arm; + + float bed_conflict_torque_arm = line_alg::distance_to( + Linef3(endpoint, endpoint + extruder_pressure_direction.cast()), + bed_centroid.cast()); + float bed_extruder_conflict_torque = extruder_conflict_force * bed_conflict_torque_arm; + + float bed_total_torque = bed_movement_torque + bed_extruder_conflict_torque + bed_weight_torque + - bed_yield_torque; #if 1 - BOOST_LOG_TRIVIAL(debug) - << "centroid: " << centroid.x() << " " << centroid.y() << " " << centroid.z(); - BOOST_LOG_TRIVIAL(debug) - << "mass_centroid: " << mass_centroid.x() << " " << mass_centroid.y() << " " - << mass_centroid.z(); - BOOST_LOG_TRIVIAL(debug) - << "variance: " << variance.x() << " " << variance.y(); - BOOST_LOG_TRIVIAL(debug) - << "SSG: elastic_section_modulus: " << elastic_section_modulus; - BOOST_LOG_TRIVIAL(debug) - << "SSG: yield_torque: " << yield_torque; - BOOST_LOG_TRIVIAL(debug) - << "SSG: weight_arm: " << weight_arm; - BOOST_LOG_TRIVIAL(debug) - << "SSG: weight_torque: " << weight_torque; - BOOST_LOG_TRIVIAL(debug) - << "SSG: bed_movement_arm: " << bed_movement_arm; - BOOST_LOG_TRIVIAL(debug) - << "SSG: bed_movement_torque: " << bed_movement_torque; - BOOST_LOG_TRIVIAL(debug) - << "SSG: conflict_torque_arm: " << conflict_torque_arm; - BOOST_LOG_TRIVIAL(debug) - << "SSG: extruder_conflict_torque: " << extruder_conflict_torque; - BOOST_LOG_TRIVIAL(debug) - << "SSG: total_torque: " << total_torque << " layer_z: " << layer_z; + BOOST_LOG_TRIVIAL(debug) + << "bed_centroid: " << bed_centroid.x() << " " << bed_centroid.y() << " " << bed_centroid.z(); + BOOST_LOG_TRIVIAL(debug) + << "SSG: bed_yield_torque: " << bed_yield_torque; + BOOST_LOG_TRIVIAL(debug) + << "SSG: bed_weight_arm: " << bed_weight_arm; + BOOST_LOG_TRIVIAL(debug) + << "SSG: bed_weight_torque: " << bed_weight_torque; + BOOST_LOG_TRIVIAL(debug) + << "SSG: bed_movement_arm: " << bed_movement_arm; + BOOST_LOG_TRIVIAL(debug) + << "SSG: bed_movement_torque: " << bed_movement_torque; + BOOST_LOG_TRIVIAL(debug) + << "SSG: bed_conflict_torque_arm: " << bed_conflict_torque_arm; + BOOST_LOG_TRIVIAL(debug) + << "SSG: bed_extruder_conflict_torque: " << bed_extruder_conflict_torque; + BOOST_LOG_TRIVIAL(debug) + << "SSG: total_torque: " << bed_total_torque << " layer_z: " << layer_z; #endif - return total_torque / conflict_torque_arm; - } + if (bed_total_torque > 0) + return bed_total_torque / bed_conflict_torque_arm; + } - void add_pivot_point(const Vec3f pivot_point, float sticking_force) { - this->pivot_points.push_back(pivot_point); - this->sticking_force += sticking_force; - this->sticking_centroid_accumulator += sticking_force * pivot_point; - this->is_pivot_tree_valid = false; - } + //section for weak connection calculations + { + Vec3f conn_centroid = connection.centroid_accumulator / connection.area; + float conn_yield_torque = compute_elastic_section_modulus(connection.centroid_accumulator, + connection.second_moment_of_area_accumulator, connection.area) * params.material_yield_strength; - void print() const { - std::cout << "sticking_force: " << sticking_force << std::endl; - std::cout << "volume: " << volume << std::endl; - } + float conn_weight_arm = (conn_centroid.head<2>() - mass_centroid.head<2>()).norm(); + float conn_weight_torque = conn_weight_arm * weight * (conn_centroid.z() / layer_z); + float conn_movement_arm = std::max(0.0f, mass_centroid.z() - conn_centroid.z()); + float conn_movement_torque = movement_force * conn_movement_arm; + + float conn_conflict_torque_arm = line_alg::distance_to( + Linef3(endpoint, endpoint + extruder_pressure_direction.cast()), + conn_centroid.cast()); + float conn_extruder_conflict_torque = extruder_conflict_force * conn_conflict_torque_arm; + + float conn_total_torque = conn_movement_torque + conn_extruder_conflict_torque + conn_weight_torque + - conn_yield_torque; + +#if 1 + BOOST_LOG_TRIVIAL(debug) + << "bed_centroid: " << conn_centroid.x() << " " << conn_centroid.y() << " " << conn_centroid.z(); + BOOST_LOG_TRIVIAL(debug) + << "SSG: conn_yield_torque: " << conn_yield_torque; + BOOST_LOG_TRIVIAL(debug) + << "SSG: conn_weight_arm: " << conn_weight_arm; + BOOST_LOG_TRIVIAL(debug) + << "SSG: conn_weight_torque: " << conn_weight_torque; + BOOST_LOG_TRIVIAL(debug) + << "SSG: conn_movement_arm: " << conn_movement_arm; + BOOST_LOG_TRIVIAL(debug) + << "SSG: conn_movement_torque: " << conn_movement_torque; + BOOST_LOG_TRIVIAL(debug) + << "SSG: conn_conflict_torque_arm: " << conn_conflict_torque_arm; + BOOST_LOG_TRIVIAL(debug) + << "SSG: conn_extruder_conflict_torque: " << conn_extruder_conflict_torque; + BOOST_LOG_TRIVIAL(debug) + << "SSG: total_torque: " << conn_total_torque << " layer_z: " << layer_z; +#endif + + return conn_total_torque / conn_conflict_torque_arm; + } + } }; void debug_print_graph(const std::vector &islands_graph) { @@ -803,8 +760,7 @@ void debug_print_graph(const std::vector &islands_graph) { const Island &island = islands_graph[layer_idx].islands[island_idx]; std::cout << " ISLAND " << island_idx << std::endl; std::cout << " volume: " << island.volume << std::endl; - std::cout << " sticking_force: " << island.sticking_force << std::endl; - std::cout << " pivot_points count: " << island.pivot_points.size() << std::endl; + std::cout << " sticking_area: " << island.sticking_area << std::endl; std::cout << " connected_islands count: " << island.connected_islands.size() << std::endl; } } @@ -885,7 +841,8 @@ Issues check_global_stability(SupportGridFilter supports_presence_grid, { std::unordered_set parts_ids; for (const auto &connection : island.connected_islands) { - size_t part_id = active_object_parts.get_flat_id(prev_island_to_object_part_mapping.at(connection.first)); + size_t part_id = active_object_parts.get_flat_id( + prev_island_to_object_part_mapping.at(connection.first)); parts_ids.insert(part_id); transfered_weakest_connection.add(prev_island_weakest_connection.at(connection.first)); new_weakest_connection.add(connection.second); @@ -932,7 +889,6 @@ Issues check_global_stability(SupportGridFilter supports_presence_grid, for (size_t island_idx = 0; island_idx < islands_graph[layer_idx].islands.size(); ++island_idx) { const Island &island = islands_graph[layer_idx].islands[island_idx]; ObjectPart &part = active_object_parts.access(prev_island_to_object_part_mapping[island_idx]); - part.print(); IslandConnection &weakest_conn = prev_island_weakest_connection[island_idx]; weakest_conn.print_info("weakest connection info: "); @@ -946,25 +902,22 @@ Issues check_global_stability(SupportGridFilter supports_presence_grid, unchecked_dist += line.len; } else { unchecked_dist = line.len; - auto [force, pivot_site_search_point] = part.is_stable_while_extruding(line, layer_z, params); - if (force <= 0) { - force = part.is_strong_enough_while_extruding(weakest_conn, line, layer_z, params); - } - + auto force = part.is_stable_while_extruding(weakest_conn, line, layer_z, params); if (force > 0) { if (island_lines_dist.get_lines().empty()) { island_lines_dist = LinesDistancer(island.external_lines); } Vec2f target_point; size_t _idx; + Vec3f pivot_site_search_point = to_3d(Vec2f(line.b + (line.b - line.a).normalized() * 300.0f), + layer_z); island_lines_dist.signed_distance_from_lines(pivot_site_search_point.head<2>(), _idx, target_point); Vec3f support_point = to_3d(target_point, layer_z); if (!supports_presence_grid.position_taken(support_point)) { float area = params.support_points_interface_radius * params.support_points_interface_radius * float(PI); - float sticking_force = area * params.support_adhesion; - part.add_pivot_point(support_point, sticking_force); + part.add_support_point(support_point, area); issues.support_points.emplace_back(support_point, force, to_3d(Vec2f(line.b - line.a).normalized(), 0.0f)); supports_presence_grid.take_position(support_point); diff --git a/src/libslic3r/SupportSpotsGenerator.hpp b/src/libslic3r/SupportSpotsGenerator.hpp index 0813663696..531f2768bf 100644 --- a/src/libslic3r/SupportSpotsGenerator.hpp +++ b/src/libslic3r/SupportSpotsGenerator.hpp @@ -8,29 +8,24 @@ namespace Slic3r { namespace SupportSpotsGenerator { struct Params { - const float gravity_constant = 9806.65f; // mm/s^2; gravity acceleration on Earth's surface, algorithm assumes that printer is in upwards position. - + // the algorithm should use the following units for all computations: distance [mm], mass [g], time [s], force [N] const float bridge_distance = 12.0f; //mm const float bridge_distance_decrease_by_curvature_factor = 5.0f; // allowed bridge distance = bridge_distance / (this factor * (curvature / PI) ) - const float min_distance_between_support_points = 3.0f; - - // Adhesion computation : from experiment, PLA holds about 3g per mm^2 of base area (with reserve); So it can withstand about 3*gravity_constant force per mm^2 - const float base_adhesion = 3.0f * gravity_constant; // adhesion per mm^2 of first layer - const float support_adhesion = 1.0f * gravity_constant; // adhesion per mm^2 of support interface layer - + const float min_distance_between_support_points = 3.0f; //mm const float support_points_interface_radius = 0.6f; // mm + const float gravity_constant = 9806.65f; // mm/s^2; gravity acceleration on Earth's surface, algorithm assumes that printer is in upwards position. const float max_acceleration = 9*1000.0f; // mm/s^2 ; max acceleration of object (bed) in XY (NOTE: The max hit is received by the object in the jerk phase, so the usual machine limits are too low) - const float filament_density = 1.25f * 0.001f; // g/mm^3 ; Common filaments are very lightweight, so precise number is not that important - const float yield_strength = 33000.0f; // mN/mm^2; 33 MPa is yield strength of ABS, which has the lowest yield strength from common materials. - const float standard_extruder_conflict_force = 1.0f * gravity_constant; // force that can occasionally push the model due to various factors (filament leaks, small curling, ... ); current value corresponds to weight of X grams - const float malformations_additive_conflict_extruder_force = 100.0f * gravity_constant; // for areas with possible high layered curled filaments - + const float filament_density = 1.25e-3f ; // g/mm^3 ; Common filaments are very lightweight, so precise number is not that important + const float bed_adhesion_yield_strength = 0.128f * 1e6f; //MPa * 1e^6 = (g*mm/s^2)/mm^2 = g/(mm*s^2); yield strength of the bed surface + const float material_yield_strength = 33.0f * 1e6f; // (g*mm/s^2)/mm^2; 33 MPa is yield strength of ABS, which has the lowest yield strength from common materials. + const float standard_extruder_conflict_force = 20.0f * gravity_constant; // force that can occasionally push the model due to various factors (filament leaks, small curling, ... ); + const float malformations_additive_conflict_extruder_force = 300.0f * gravity_constant; // for areas with possible high layered curled filaments }; struct SupportPoint { - SupportPoint(const Vec3f &position, float force,const Vec3f& direction); + SupportPoint(const Vec3f &position, float force, const Vec3f& direction); Vec3f position; float force; Vec3f direction; From cdf68039f7281207ecc4c1257b1afda8b66108e6 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Tue, 26 Jul 2022 10:30:13 +0200 Subject: [PATCH 56/78] fixed bug with zero area section modulus returning nans --- src/libslic3r/SupportSpotsGenerator.cpp | 1 + src/libslic3r/SupportSpotsGenerator.hpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index b313030ac2..a0b84b285c 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -638,6 +638,7 @@ public: auto compute_elastic_section_modulus = [&line_dir]( const Vec3f ¢roid_accumulator, const Vec2f &second_moment_of_area_accumulator, const float &area) { + if (area < EPSILON) return 0.0f; Vec3f centroid = centroid_accumulator / area; Vec2f variance = (second_moment_of_area_accumulator / area - centroid.head<2>().cwiseProduct(centroid.head<2>())); diff --git a/src/libslic3r/SupportSpotsGenerator.hpp b/src/libslic3r/SupportSpotsGenerator.hpp index 531f2768bf..7be312bcb4 100644 --- a/src/libslic3r/SupportSpotsGenerator.hpp +++ b/src/libslic3r/SupportSpotsGenerator.hpp @@ -10,7 +10,7 @@ namespace SupportSpotsGenerator { struct Params { // the algorithm should use the following units for all computations: distance [mm], mass [g], time [s], force [N] const float bridge_distance = 12.0f; //mm - const float bridge_distance_decrease_by_curvature_factor = 5.0f; // allowed bridge distance = bridge_distance / (this factor * (curvature / PI) ) + const float bridge_distance_decrease_by_curvature_factor = 3.0f; // allowed bridge distance = bridge_distance / (this factor * (curvature / PI) ) const float min_distance_between_support_points = 3.0f; //mm const float support_points_interface_radius = 0.6f; // mm From 90e77f9135f55e57cd12a6d9b25cdeb1598bcdfc Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Tue, 26 Jul 2022 14:45:36 +0200 Subject: [PATCH 57/78] integration into FDM supports painter gizmo --- src/libslic3r/PrintObject.cpp | 7 +- src/libslic3r/SupportSpotsGenerator.hpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 97 +++++++++++++++++++- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp | 4 + 4 files changed, 100 insertions(+), 10 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index b5848cabac..0f85891ee2 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -398,7 +398,6 @@ void PrintObject::ironing() } } - void PrintObject::generate_support_spots() { if (this->set_started(posSupportSpotsSearch)) { @@ -421,8 +420,6 @@ void PrintObject::generate_support_spots() } } else { SupportSpotsGenerator::Issues issues = SupportSpotsGenerator::full_search(this); - //TODO fix -// if (!issues.supports_nedded.empty()) { auto obj_transform = this->trafo_centered(); for (ModelVolume *model_volume : this->model_object()->volumes) { if (model_volume->type() == ModelVolumeType::MODEL_PART) { @@ -438,7 +435,7 @@ void PrintObject::generate_support_spots() } model_volume->supported_facets.set(selector.selector); -#if 1 +#if 0 //DEBUG export indexed_triangle_set copy = model_volume->mesh().its; its_transform(copy, obj_transform * model_transformation); its_write_obj(copy, @@ -446,7 +443,6 @@ void PrintObject::generate_support_spots() #endif } } -// } } m_print->throw_if_canceled(); @@ -456,7 +452,6 @@ void PrintObject::generate_support_spots() } } - void PrintObject::generate_support_material() { if (this->set_started(posSupportMaterial)) { diff --git a/src/libslic3r/SupportSpotsGenerator.hpp b/src/libslic3r/SupportSpotsGenerator.hpp index 7be312bcb4..cc11c27c7e 100644 --- a/src/libslic3r/SupportSpotsGenerator.hpp +++ b/src/libslic3r/SupportSpotsGenerator.hpp @@ -8,7 +8,7 @@ namespace Slic3r { namespace SupportSpotsGenerator { struct Params { - // the algorithm should use the following units for all computations: distance [mm], mass [g], time [s], force [N] + // the algorithm should use the following units for all computations: distance [mm], mass [g], time [s], force [g*mm/s^2] const float bridge_distance = 12.0f; //mm const float bridge_distance_decrease_by_curvature_factor = 3.0f; // allowed bridge distance = bridge_distance / (this factor * (curvature / PI) ) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 49e97ee1f1..36f48d0087 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -10,6 +10,8 @@ #include "slic3r/GUI/GUI_ObjectList.hpp" #include "slic3r/GUI/format.hpp" #include "slic3r/Utils/UndoRedo.hpp" +#include "libslic3r/Print.hpp" +#include "slic3r/GUI/MsgDialog.hpp" #include @@ -39,6 +41,7 @@ bool GLGizmoFdmSupports::on_init() { m_shortcut_key = WXK_CONTROL_L; + m_desc["auto_generate"] = _L("Auto-generate supports"); m_desc["clipping_of_view"] = _L("Clipping of view") + ": "; m_desc["reset_direction"] = _L("Reset direction"); m_desc["cursor_size"] = _L("Brush size") + ": "; @@ -91,7 +94,7 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l if (! m_c->selection_info()->model_object()) return; - const float approx_height = m_imgui->scaled(23.f); + const float approx_height = m_imgui->scaled(25.f); y = std::min(y, bottom_limit - approx_height); m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); @@ -153,6 +156,12 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l ImGui::Separator(); + bool generate = m_imgui->button(m_desc.at("auto_generate")); + if (generate) + auto_generate(); + + ImGui::Separator(); + float position_before_text_y = ImGui::GetCursorPos().y; ImGui::AlignTextToFramePadding(); m_imgui->text_wrapped(m_desc["highlight_by_angle"] + ":", autoset_slider_label_max_width); @@ -369,6 +378,50 @@ void GLGizmoFdmSupports::select_facets_by_angle(float threshold_deg, bool block) m_parent.set_as_dirty(); } +void GLGizmoFdmSupports::data_changed() +{ + GLGizmoPainterBase::data_changed(); + if (! m_c->selection_info()) + return; + + ModelObject* mo = m_c->selection_info()->model_object(); + if (mo && this->waiting_for_autogenerated_supports) { + get_data_from_backend(); + } +} + +void GLGizmoFdmSupports::get_data_from_backend() +{ + if (! has_backend_supports()) + return; + ModelObject* mo = m_c->selection_info()->model_object(); + + // find the respective PrintObject, we need a pointer to it + for (const PrintObject* po : m_parent.fff_print()->objects()) { + if (po->model_object()->id() == mo->id()) { + std::unordered_map mvs; + for (const ModelVolume* mv : po->model_object()->volumes) { + if (mv->is_model_part()) { + mvs.emplace(mv->id().id, mv); + } + } + // NOTE: Copying the data into ModelVolumes stops the background processing. + int mesh_id = -1.0f; + for (ModelVolume* mv : mo->volumes){ + if (mv->is_model_part()){ + mesh_id++; + mv->supported_facets.assign(mvs[mv->id().id]->supported_facets); + m_triangle_selectors[mesh_id]->deserialize(mv->supported_facets.get_data(), true); + m_triangle_selectors[mesh_id]->request_update_render_data(); + } + } + this->waiting_for_autogenerated_supports = false; + m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); + m_parent.set_as_dirty(); + + } + } +} void GLGizmoFdmSupports::update_model_object() const @@ -391,8 +444,6 @@ void GLGizmoFdmSupports::update_model_object() const } } - - void GLGizmoFdmSupports::update_from_model_object() { wxBusyCursor wait; @@ -417,6 +468,46 @@ void GLGizmoFdmSupports::update_from_model_object() } } +bool GLGizmoFdmSupports::has_backend_supports() const +{ + const ModelObject* mo = m_c->selection_info()->model_object(); + if (! mo) + return false; + + // find SlaPrintObject with this ID + for (const PrintObject* po : m_parent.fff_print()->objects()) { + if (po->model_object()->id() == mo->id()) + return po->is_step_done(posSupportSpotsSearch); + } + return false; +} + +void GLGizmoFdmSupports::reslice_FDM_supports(bool postpone_error_messages) const { + wxGetApp().CallAfter( + [this, postpone_error_messages]() { + wxGetApp().plater()->reslice_FFF_until_step(posSupportSpotsSearch, + *m_c->selection_info()->model_object(), postpone_error_messages); + }); +} + +void GLGizmoFdmSupports::auto_generate() +{ + ModelObject *mo = m_c->selection_info()->model_object(); + bool not_painted = std::all_of(mo->volumes.begin(), mo->volumes.end(), [](const ModelVolume* vol){ + return vol->type() != ModelVolumeType::MODEL_PART || vol->supported_facets.empty(); + }); + + MessageDialog dlg(GUI::wxGetApp().plater(), + _L("Autogeneration will erase all currently painted areas.") + "\n\n" + + _L("Are you sure you want to do it?") + "\n", + _L("Warning"), wxICON_WARNING | wxYES | wxNO); + + if (not_painted || dlg.ShowModal() == wxID_YES) { + Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Autogenerate support points")); + this->waiting_for_autogenerated_supports = true; + wxGetApp().CallAfter([this]() { reslice_FDM_supports(); }); + } +} PainterGizmoType GLGizmoFdmSupports::get_painter_type() const diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp index f4c21a174b..2820298b6f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp @@ -26,6 +26,7 @@ protected: private: bool on_init() override; + void data_changed() override; void update_model_object() const override; void update_from_model_object() override; @@ -41,8 +42,11 @@ private: std::map m_desc; + bool waiting_for_autogenerated_supports = false; bool has_backend_supports() const; void reslice_FDM_supports(bool postpone_error_messages = false) const; + void auto_generate(); + void get_data_from_backend(); }; From 9cfde724f100a01543b6e2421c9021bdcf794019 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Tue, 26 Jul 2022 16:50:08 +0200 Subject: [PATCH 58/78] fix numerical issues in stability calculations --- src/libslic3r/SupportSpotsGenerator.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index a0b84b285c..6a1aada0a1 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -638,12 +638,14 @@ public: auto compute_elastic_section_modulus = [&line_dir]( const Vec3f ¢roid_accumulator, const Vec2f &second_moment_of_area_accumulator, const float &area) { - if (area < EPSILON) return 0.0f; Vec3f centroid = centroid_accumulator / area; Vec2f variance = (second_moment_of_area_accumulator / area - centroid.head<2>().cwiseProduct(centroid.head<2>())); variance = variance.cwiseProduct(line_dir.cwiseAbs()); float extreme_fiber_dist = variance.cwiseSqrt().norm(); + if (extreme_fiber_dist < EPSILON) { + return 0.0f; + } float elastic_section_modulus = area * (variance.x() + variance.y()) / extreme_fiber_dist; return elastic_section_modulus; }; @@ -663,6 +665,9 @@ public: // section for bed calculations { + if (this->sticking_area < EPSILON) + return 1.0f; + Vec3f bed_centroid = this->sticking_centroid_accumulator / this->sticking_area; float bed_yield_torque = compute_elastic_section_modulus(this->sticking_centroid_accumulator, this->sticking_second_moment_of_area_accumulator, this->sticking_area) @@ -708,6 +713,9 @@ public: //section for weak connection calculations { + if (connection.area < EPSILON) + return 1.0f; + Vec3f conn_centroid = connection.centroid_accumulator / connection.area; float conn_yield_torque = compute_elastic_section_modulus(connection.centroid_accumulator, connection.second_moment_of_area_accumulator, connection.area) * params.material_yield_strength; @@ -763,6 +771,7 @@ void debug_print_graph(const std::vector &islands_graph) { std::cout << " volume: " << island.volume << std::endl; std::cout << " sticking_area: " << island.sticking_area << std::endl; std::cout << " connected_islands count: " << island.connected_islands.size() << std::endl; + std::cout << " lines count: " << island.external_lines.size() << std::endl; } } std::cout << "END OF GRAPH" << std::endl; From dbe864ea8a0c838026c030b15b0a3e2968f96a97 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Wed, 27 Jul 2022 11:29:31 +0200 Subject: [PATCH 59/78] refactor to use covariance to best estimate XY variance of the connection and thus second moment of area --- src/libslic3r/SupportSpotsGenerator.cpp | 99 ++++++++++++++++++++----- 1 file changed, 79 insertions(+), 20 deletions(-) diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index 6a1aada0a1..def0217e52 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -15,6 +15,7 @@ #include "libslic3r/ClipperUtils.hpp" #include "Geometry/ConvexHull.hpp" +#define DETAILED_DEBUG_LOGS #define DEBUG_FILES #ifdef DEBUG_FILES @@ -252,21 +253,25 @@ struct IslandConnection { float area { }; Vec3f centroid_accumulator = Vec3f::Zero(); Vec2f second_moment_of_area_accumulator = Vec2f::Zero(); + float second_moment_of_area_covariance_accumulator { }; void add(const IslandConnection &other) { this->area += other.area; this->centroid_accumulator += other.centroid_accumulator; this->second_moment_of_area_accumulator += other.second_moment_of_area_accumulator; + this->second_moment_of_area_covariance_accumulator += other.second_moment_of_area_covariance_accumulator; } void print_info(const std::string &tag) { Vec3f centroid = centroid_accumulator / area; Vec2f variance = (second_moment_of_area_accumulator / area - centroid.head<2>().cwiseProduct(centroid.head<2>())); + float covariance = second_moment_of_area_covariance_accumulator / area - centroid.x() * centroid.y(); std::cout << tag << std::endl; std::cout << "area: " << area << std::endl; std::cout << "centroid: " << centroid.x() << " " << centroid.y() << " " << centroid.z() << std::endl; std::cout << "variance: " << variance.x() << " " << variance.y() << std::endl; + std::cout << "covariance: " << covariance << std::endl; } }; @@ -277,6 +282,7 @@ struct Island { float sticking_area { }; // for support points present on this layer (or bed extrusions) Vec3f sticking_centroid_accumulator = Vec3f::Zero(); Vec2f sticking_second_moment_of_area_accumulator = Vec2f::Zero(); + float sticking_second_moment_of_area_covariance_accumulator { }; std::vector external_lines; }; @@ -535,11 +541,15 @@ std::tuple reckon_islands( Vec2f middle = Vec2f((line.a + line.b) / 2.0f); island.sticking_centroid_accumulator += sticking_area * to_3d(middle, float(layer->slice_z)); island.sticking_second_moment_of_area_accumulator += sticking_area * middle.cwiseProduct(middle); + island.sticking_second_moment_of_area_covariance_accumulator += sticking_area * middle.x() + * middle.y(); } else if (layer_lines[lidx].support_point_generated) { float sticking_area = line.len * flow_width; island.sticking_area += sticking_area; island.sticking_centroid_accumulator += sticking_area * to_3d(line.b, float(layer->slice_z)); island.sticking_second_moment_of_area_accumulator += sticking_area * line.b.cwiseProduct(line.b); + island.sticking_second_moment_of_area_covariance_accumulator += sticking_area * line.b.x() + * line.b.y(); } } } @@ -574,6 +584,8 @@ std::tuple reckon_islands( * current_layer_grid.pixel_area(); connection.second_moment_of_area_accumulator += current_coords.cwiseProduct(current_coords) * current_layer_grid.pixel_area(); + connection.second_moment_of_area_covariance_accumulator += current_coords.x() * current_coords.y() + * current_layer_grid.pixel_area(); } } } @@ -601,6 +613,7 @@ class ObjectPart { float sticking_area { }; Vec3f sticking_centroid_accumulator = Vec3f::Zero(); Vec2f sticking_second_moment_of_area_accumulator = Vec2f::Zero(); + float sticking_second_moment_of_area_covariance_accumulator { }; public: ObjectPart() = default; @@ -611,6 +624,8 @@ public: this->sticking_area = island.sticking_area; this->sticking_centroid_accumulator = island.sticking_centroid_accumulator; this->sticking_second_moment_of_area_accumulator = island.sticking_second_moment_of_area_accumulator; + this->sticking_second_moment_of_area_covariance_accumulator = + island.sticking_second_moment_of_area_covariance_accumulator; } void add(const ObjectPart &other) { @@ -619,6 +634,8 @@ public: this->sticking_area += other.sticking_area; this->sticking_centroid_accumulator += other.sticking_centroid_accumulator; this->sticking_second_moment_of_area_accumulator += other.sticking_second_moment_of_area_accumulator; + this->sticking_second_moment_of_area_covariance_accumulator += + other.sticking_second_moment_of_area_covariance_accumulator; } void add_support_point(const Vec3f &position, float sticking_area) { @@ -626,6 +643,51 @@ public: this->sticking_centroid_accumulator += sticking_area * position; this->sticking_second_moment_of_area_accumulator += sticking_area * position.head<2>().cwiseProduct(position.head<2>()); + this->sticking_second_moment_of_area_covariance_accumulator += sticking_area + * position.x() * position.y(); + } + + float compute_elastic_section_modulus( + const Vec2f &line_dir, + const Vec3f ¢roid_accumulator, + const Vec2f &second_moment_of_area_accumulator, + const float &second_moment_of_area_covariance_accumulator, + const float &area) const { + assert(area > 0); + Vec3f centroid = centroid_accumulator / area; + Vec2f variance = (second_moment_of_area_accumulator / area + - centroid.head<2>().cwiseProduct(centroid.head<2>())); + float covariance = second_moment_of_area_covariance_accumulator / area - centroid.x() * centroid.y(); + // Var(aX+bY)=a^2*Var(X)+b^2*Var(Y)+2*a*b*Cov(X,Y) + float directional_xy_variance = line_dir.x() * line_dir.x() * variance.x() + + line_dir.y() * line_dir.y() * variance.y() + + 2.0f * line_dir.x() * line_dir.y() * covariance; + +#ifdef DETAILED_DEBUG_LOGS + BOOST_LOG_TRIVIAL(debug) + << "centroid: " << centroid.x() << " " << centroid.y() << " " << centroid.z(); + BOOST_LOG_TRIVIAL(debug) + << "variance: " << variance.x() << " " << variance.y(); + BOOST_LOG_TRIVIAL(debug) + << "covariance: " << covariance; + BOOST_LOG_TRIVIAL(debug) + << "directional_xy_variance: " << directional_xy_variance; +#endif + + if (directional_xy_variance < EPSILON) { + return 0.0f; + } + float extreme_fiber_dist = sqrt(area * directional_xy_variance); + float elastic_section_modulus = area * directional_xy_variance / extreme_fiber_dist; + +#ifdef DETAILED_DEBUG_LOGS + BOOST_LOG_TRIVIAL(debug) + << "extreme_fiber_dist: " << extreme_fiber_dist; + BOOST_LOG_TRIVIAL(debug) + << "elastic_section_modulus: " << elastic_section_modulus; +#endif + + return elastic_section_modulus; } float is_stable_while_extruding( @@ -636,20 +698,6 @@ public: Vec2f line_dir = (extruded_line.b - extruded_line.a).normalized(); - auto compute_elastic_section_modulus = [&line_dir]( - const Vec3f ¢roid_accumulator, const Vec2f &second_moment_of_area_accumulator, const float &area) { - Vec3f centroid = centroid_accumulator / area; - Vec2f variance = (second_moment_of_area_accumulator / area - - centroid.head<2>().cwiseProduct(centroid.head<2>())); - variance = variance.cwiseProduct(line_dir.cwiseAbs()); - float extreme_fiber_dist = variance.cwiseSqrt().norm(); - if (extreme_fiber_dist < EPSILON) { - return 0.0f; - } - float elastic_section_modulus = area * (variance.x() + variance.y()) / extreme_fiber_dist; - return elastic_section_modulus; - }; - const Vec3f &mass_centroid = this->volume_centroid_accumulator / this->volume; float mass = this->volume * params.filament_density; float weight = mass * params.gravity_constant; @@ -669,8 +717,12 @@ public: return 1.0f; Vec3f bed_centroid = this->sticking_centroid_accumulator / this->sticking_area; - float bed_yield_torque = compute_elastic_section_modulus(this->sticking_centroid_accumulator, - this->sticking_second_moment_of_area_accumulator, this->sticking_area) + float bed_yield_torque = compute_elastic_section_modulus( + line_dir, + this->sticking_centroid_accumulator, + this->sticking_second_moment_of_area_accumulator, + this->sticking_second_moment_of_area_covariance_accumulator, + this->sticking_area) * params.bed_adhesion_yield_strength; float bed_weight_arm = (bed_centroid.head<2>() - mass_centroid.head<2>()).norm(); @@ -686,7 +738,8 @@ public: float bed_total_torque = bed_movement_torque + bed_extruder_conflict_torque + bed_weight_torque - bed_yield_torque; -#if 1 + +#ifdef DETAILED_DEBUG_LOGS BOOST_LOG_TRIVIAL(debug) << "bed_centroid: " << bed_centroid.x() << " " << bed_centroid.y() << " " << bed_centroid.z(); BOOST_LOG_TRIVIAL(debug) @@ -717,8 +770,12 @@ public: return 1.0f; Vec3f conn_centroid = connection.centroid_accumulator / connection.area; - float conn_yield_torque = compute_elastic_section_modulus(connection.centroid_accumulator, - connection.second_moment_of_area_accumulator, connection.area) * params.material_yield_strength; + float conn_yield_torque = compute_elastic_section_modulus( + line_dir, + connection.centroid_accumulator, + connection.second_moment_of_area_accumulator, + connection.second_moment_of_area_covariance_accumulator, + connection.area) * params.material_yield_strength; float conn_weight_arm = (conn_centroid.head<2>() - mass_centroid.head<2>()).norm(); float conn_weight_torque = conn_weight_arm * weight * (conn_centroid.z() / layer_z); @@ -734,7 +791,7 @@ public: float conn_total_torque = conn_movement_torque + conn_extruder_conflict_torque + conn_weight_torque - conn_yield_torque; -#if 1 +#ifdef DETAILED_DEBUG_LOGS BOOST_LOG_TRIVIAL(debug) << "bed_centroid: " << conn_centroid.x() << " " << conn_centroid.y() << " " << conn_centroid.z(); BOOST_LOG_TRIVIAL(debug) @@ -936,6 +993,8 @@ Issues check_global_stability(SupportGridFilter supports_presence_grid, weakest_conn.centroid_accumulator += support_point * area; weakest_conn.second_moment_of_area_accumulator += area * support_point.head<2>().cwiseProduct(support_point.head<2>()); + weakest_conn.second_moment_of_area_covariance_accumulator += area * support_point.x() + * support_point.y(); } } } From a6cf309020acc571b50468f3c58d50979a624e1f Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Wed, 27 Jul 2022 14:29:30 +0200 Subject: [PATCH 60/78] updated weakest connection strength estimation, fixed various issues --- src/libslic3r/PrintObject.cpp | 4 +- src/libslic3r/SupportSpotsGenerator.cpp | 44 ++++++++++---------- src/libslic3r/SupportSpotsGenerator.hpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 2 - 4 files changed, 25 insertions(+), 27 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 0f85891ee2..b085c7a07a 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -422,7 +422,7 @@ void PrintObject::generate_support_spots() SupportSpotsGenerator::Issues issues = SupportSpotsGenerator::full_search(this); auto obj_transform = this->trafo_centered(); for (ModelVolume *model_volume : this->model_object()->volumes) { - if (model_volume->type() == ModelVolumeType::MODEL_PART) { + if (model_volume->is_model_part()) { Transform3d model_transformation = model_volume->get_matrix(); Transform3f inv_transform = (obj_transform * model_transformation).inverse().cast(); TriangleSelectorWrapper selector { model_volume->mesh() }; @@ -439,7 +439,7 @@ void PrintObject::generate_support_spots() indexed_triangle_set copy = model_volume->mesh().its; its_transform(copy, obj_transform * model_transformation); its_write_obj(copy, - debug_out_path("model.obj").c_str()); + debug_out_path(("model"+std::to_string(model_volume->id().id)+".obj").c_str()).c_str()); #endif } } diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index def0217e52..2669ac5434 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -313,7 +313,7 @@ float get_flow_width(const LayerRegion *region, ExtrusionRole role) { } } -// Accumulator of current extruion path properties +// Accumulator of current extrusion path properties // It remembers unsuported distance and maximum accumulated curvature over that distance. // Used to determine local stability issues (too long bridges, extrusion curves into air) struct ExtrusionPropertiesAccumulator { @@ -417,7 +417,7 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, } if (dist_from_prev_layer > flow_width * 0.3) { malformation_acc.add_distance(current_line.len); - current_line.malformation += 0.15f + current_line.malformation += 0.3f * layer_region->layer()->height * (0.8f + 0.2f * malformation_acc.max_curvature / (1.0f + 0.5f * malformation_acc.distance)); } else { malformation_acc.reset(); @@ -704,10 +704,6 @@ public: float movement_force = params.max_acceleration * mass; - Vec3f extruder_pressure_direction = to_3d(line_dir, 0.0f); - extruder_pressure_direction.z() = -extruded_line.malformation * 0.5f; - extruder_pressure_direction.normalize(); - Vec3d endpoint = (to_3d(extruded_line.b, layer_z)).cast(); float extruder_conflict_force = params.standard_extruder_conflict_force + std::min(extruded_line.malformation, 1.0f) * params.malformations_additive_conflict_extruder_force; @@ -731,9 +727,7 @@ public: float bed_movement_arm = std::max(0.0f, mass_centroid.z() - bed_centroid.z()); float bed_movement_torque = movement_force * bed_movement_arm; - float bed_conflict_torque_arm = line_alg::distance_to( - Linef3(endpoint, endpoint + extruder_pressure_direction.cast()), - bed_centroid.cast()); + float bed_conflict_torque_arm = layer_z - bed_centroid.z(); float bed_extruder_conflict_torque = extruder_conflict_force * bed_conflict_torque_arm; float bed_total_torque = bed_movement_torque + bed_extruder_conflict_torque + bed_weight_torque @@ -783,9 +777,7 @@ public: float conn_movement_arm = std::max(0.0f, mass_centroid.z() - conn_centroid.z()); float conn_movement_torque = movement_force * conn_movement_arm; - float conn_conflict_torque_arm = line_alg::distance_to( - Linef3(endpoint, endpoint + extruder_pressure_direction.cast()), - conn_centroid.cast()); + float conn_conflict_torque_arm = layer_z - conn_centroid.z(); float conn_extruder_conflict_torque = extruder_conflict_force * conn_conflict_torque_arm; float conn_total_torque = conn_movement_torque + conn_extruder_conflict_torque + conn_weight_torque @@ -817,6 +809,7 @@ public: } }; +#ifdef DETAILED_DEBUG_LOGS void debug_print_graph(const std::vector &islands_graph) { std::cout << "BUILT ISLANDS GRAPH:" << std::endl; for (size_t layer_idx = 0; layer_idx < islands_graph.size(); ++layer_idx) { @@ -832,8 +825,8 @@ void debug_print_graph(const std::vector &islands_graph) { } } std::cout << "END OF GRAPH" << std::endl; - } +#endif class ActiveObjectParts { size_t next_part_idx = 0; @@ -876,7 +869,10 @@ public: Issues check_global_stability(SupportGridFilter supports_presence_grid, const std::vector &islands_graph, const Params ¶ms) { +#ifdef DETAILED_DEBUG_LOGS debug_print_graph(islands_graph); +#endif + Issues issues { }; ActiveObjectParts active_object_parts { }; std::unordered_map prev_island_to_object_part_mapping; @@ -887,11 +883,13 @@ Issues check_global_stability(SupportGridFilter supports_presence_grid, for (size_t layer_idx = 0; layer_idx < islands_graph.size(); ++layer_idx) { float layer_z = islands_graph[layer_idx].layer_z; - std::cout << "at layer: " << layer_idx << " the following island to object mapping is used:" << std::endl; + +#ifdef DETAILED_DEBUG_LOGS for (const auto &m : prev_island_to_object_part_mapping) { std::cout << "island " << m.first << " maps to part " << m.second << std::endl; prev_island_weakest_connection[m.first].print_info("connection info:"); } +#endif for (size_t island_idx = 0; island_idx < islands_graph[layer_idx].islands.size(); ++island_idx) { const Island &island = islands_graph[layer_idx].islands[island_idx]; @@ -917,24 +915,25 @@ Issues check_global_stability(SupportGridFilter supports_presence_grid, final_part_id = *parts_ids.begin(); for (size_t part_id : parts_ids) { if (final_part_id != part_id) { - std::cout << "at layer: " << layer_idx << " merging object part: " << part_id - << " into final part: " << final_part_id << std::endl; active_object_parts.merge(part_id, final_part_id); } } } - auto estimate_strength = [layer_z](const IslandConnection &conn) { + auto estimate_conn_strength = [layer_z](const IslandConnection &conn) { Vec3f centroid = conn.centroid_accumulator / conn.area; - float min_variance = (conn.second_moment_of_area_accumulator / conn.area - - centroid.head<2>().cwiseProduct(centroid.head<2>())).minCoeff(); + Vec2f variance = (conn.second_moment_of_area_accumulator / conn.area + - centroid.head<2>().cwiseProduct(centroid.head<2>())); + float xy_variance = variance.x() + variance.y(); float arm_len_estimate = std::max(1.1f, layer_z - (conn.centroid_accumulator.z() / conn.area)); - return min_variance / arm_len_estimate; + return conn.area * sqrt(xy_variance) / arm_len_estimate; }; +#ifdef DETAILED_DEBUG_LOGS new_weakest_connection.print_info("new_weakest_connection"); transfered_weakest_connection.print_info("transfered_weakest_connection"); +#endif - if (estimate_strength(transfered_weakest_connection) < estimate_strength(new_weakest_connection)) { + if (estimate_conn_strength(transfered_weakest_connection) < estimate_conn_strength(new_weakest_connection)) { new_weakest_connection = transfered_weakest_connection; } next_island_weakest_connection.emplace(island_idx, new_weakest_connection); @@ -957,8 +956,9 @@ Issues check_global_stability(SupportGridFilter supports_presence_grid, const Island &island = islands_graph[layer_idx].islands[island_idx]; ObjectPart &part = active_object_parts.access(prev_island_to_object_part_mapping[island_idx]); IslandConnection &weakest_conn = prev_island_weakest_connection[island_idx]; +#ifdef DETAILED_DEBUG_LOGS weakest_conn.print_info("weakest connection info: "); - +#endif std::vector dummy { }; LinesDistancer island_lines_dist(dummy); float unchecked_dist = params.min_distance_between_support_points + 1.0f; diff --git a/src/libslic3r/SupportSpotsGenerator.hpp b/src/libslic3r/SupportSpotsGenerator.hpp index cc11c27c7e..6b94ceeaa2 100644 --- a/src/libslic3r/SupportSpotsGenerator.hpp +++ b/src/libslic3r/SupportSpotsGenerator.hpp @@ -21,7 +21,7 @@ struct Params { const float bed_adhesion_yield_strength = 0.128f * 1e6f; //MPa * 1e^6 = (g*mm/s^2)/mm^2 = g/(mm*s^2); yield strength of the bed surface const float material_yield_strength = 33.0f * 1e6f; // (g*mm/s^2)/mm^2; 33 MPa is yield strength of ABS, which has the lowest yield strength from common materials. const float standard_extruder_conflict_force = 20.0f * gravity_constant; // force that can occasionally push the model due to various factors (filament leaks, small curling, ... ); - const float malformations_additive_conflict_extruder_force = 300.0f * gravity_constant; // for areas with possible high layered curled filaments + const float malformations_additive_conflict_extruder_force = 150.0f * gravity_constant; // for areas with possible high layered curled filaments }; struct SupportPoint { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 36f48d0087..7e8f0954e0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -405,7 +405,6 @@ void GLGizmoFdmSupports::get_data_from_backend() mvs.emplace(mv->id().id, mv); } } - // NOTE: Copying the data into ModelVolumes stops the background processing. int mesh_id = -1.0f; for (ModelVolume* mv : mo->volumes){ if (mv->is_model_part()){ @@ -418,7 +417,6 @@ void GLGizmoFdmSupports::get_data_from_backend() this->waiting_for_autogenerated_supports = false; m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); m_parent.set_as_dirty(); - } } } From ff73cd253edc168957ddfbc9131ed18f87c9d1fd Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Wed, 27 Jul 2022 15:33:10 +0200 Subject: [PATCH 61/78] fix extreme fibre distance calculation --- src/libslic3r/SupportSpotsGenerator.cpp | 10 +++++----- src/libslic3r/SupportSpotsGenerator.hpp | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index 2669ac5434..e90693f6d2 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -677,7 +677,7 @@ public: if (directional_xy_variance < EPSILON) { return 0.0f; } - float extreme_fiber_dist = sqrt(area * directional_xy_variance); + float extreme_fiber_dist = sqrt(area/PI); float elastic_section_modulus = area * directional_xy_variance / extreme_fiber_dist; #ifdef DETAILED_DEBUG_LOGS @@ -924,7 +924,7 @@ Issues check_global_stability(SupportGridFilter supports_presence_grid, Vec2f variance = (conn.second_moment_of_area_accumulator / conn.area - centroid.head<2>().cwiseProduct(centroid.head<2>())); float xy_variance = variance.x() + variance.y(); - float arm_len_estimate = std::max(1.1f, layer_z - (conn.centroid_accumulator.z() / conn.area)); + float arm_len_estimate = std::max(1.0f, layer_z - (conn.centroid_accumulator.z() / conn.area)); return conn.area * sqrt(xy_variance) / arm_len_estimate; }; @@ -933,10 +933,10 @@ Issues check_global_stability(SupportGridFilter supports_presence_grid, transfered_weakest_connection.print_info("transfered_weakest_connection"); #endif - if (estimate_conn_strength(transfered_weakest_connection) < estimate_conn_strength(new_weakest_connection)) { - new_weakest_connection = transfered_weakest_connection; + if (estimate_conn_strength(transfered_weakest_connection) > estimate_conn_strength(new_weakest_connection)) { + transfered_weakest_connection = new_weakest_connection; } - next_island_weakest_connection.emplace(island_idx, new_weakest_connection); + next_island_weakest_connection.emplace(island_idx, transfered_weakest_connection); next_island_to_object_part_mapping.emplace(island_idx, final_part_id); ObjectPart &part = active_object_parts.access(final_part_id); part.add(ObjectPart(island)); diff --git a/src/libslic3r/SupportSpotsGenerator.hpp b/src/libslic3r/SupportSpotsGenerator.hpp index 6b94ceeaa2..10c7abe70f 100644 --- a/src/libslic3r/SupportSpotsGenerator.hpp +++ b/src/libslic3r/SupportSpotsGenerator.hpp @@ -19,9 +19,9 @@ struct Params { const float max_acceleration = 9*1000.0f; // mm/s^2 ; max acceleration of object (bed) in XY (NOTE: The max hit is received by the object in the jerk phase, so the usual machine limits are too low) const float filament_density = 1.25e-3f ; // g/mm^3 ; Common filaments are very lightweight, so precise number is not that important const float bed_adhesion_yield_strength = 0.128f * 1e6f; //MPa * 1e^6 = (g*mm/s^2)/mm^2 = g/(mm*s^2); yield strength of the bed surface - const float material_yield_strength = 33.0f * 1e6f; // (g*mm/s^2)/mm^2; 33 MPa is yield strength of ABS, which has the lowest yield strength from common materials. + const float material_yield_strength = 15.0f * 1e6f; // (g*mm/s^2)/mm^2; 33 MPa is yield strength of ABS, which has the lowest yield strength from common materials. const float standard_extruder_conflict_force = 20.0f * gravity_constant; // force that can occasionally push the model due to various factors (filament leaks, small curling, ... ); - const float malformations_additive_conflict_extruder_force = 150.0f * gravity_constant; // for areas with possible high layered curled filaments + const float malformations_additive_conflict_extruder_force = 300.0f * gravity_constant; // for areas with possible high layered curled filaments }; struct SupportPoint { From 2401556193208955eee1aa852e1bd33df8eae2e7 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Wed, 27 Jul 2022 17:15:23 +0200 Subject: [PATCH 62/78] most extreme fiber is now taken from the current island.. this is not correct, but from all aproximations it gives best results --- src/libslic3r/SupportSpotsGenerator.cpp | 40 ++++++++++++++----------- src/libslic3r/SupportSpotsGenerator.hpp | 2 +- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index e90693f6d2..59ed818b37 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -649,6 +649,7 @@ public: float compute_elastic_section_modulus( const Vec2f &line_dir, + const Vec3f &extreme_point, const Vec3f ¢roid_accumulator, const Vec2f &second_moment_of_area_accumulator, const float &second_moment_of_area_covariance_accumulator, @@ -673,11 +674,13 @@ public: BOOST_LOG_TRIVIAL(debug) << "directional_xy_variance: " << directional_xy_variance; #endif - if (directional_xy_variance < EPSILON) { return 0.0f; } - float extreme_fiber_dist = sqrt(area/PI); + + float extreme_fiber_dist = line_alg::distance_to( + Linef(centroid.head<2>().cast(), (centroid.head<2>() + Vec2f(line_dir.y(), -line_dir.x())).cast()), + extreme_point.head<2>().cast()); float elastic_section_modulus = area * directional_xy_variance / extreme_fiber_dist; #ifdef DETAILED_DEBUG_LOGS @@ -693,11 +696,11 @@ public: float is_stable_while_extruding( const IslandConnection &connection, const ExtrusionLine &extruded_line, + const Vec3f &extreme_point, float layer_z, const Params ¶ms) const { Vec2f line_dir = (extruded_line.b - extruded_line.a).normalized(); - const Vec3f &mass_centroid = this->volume_centroid_accumulator / this->volume; float mass = this->volume * params.filament_density; float weight = mass * params.gravity_constant; @@ -715,6 +718,7 @@ public: Vec3f bed_centroid = this->sticking_centroid_accumulator / this->sticking_area; float bed_yield_torque = compute_elastic_section_modulus( line_dir, + extreme_point, this->sticking_centroid_accumulator, this->sticking_second_moment_of_area_accumulator, this->sticking_second_moment_of_area_covariance_accumulator, @@ -764,8 +768,13 @@ public: return 1.0f; Vec3f conn_centroid = connection.centroid_accumulator / connection.area; + + if (layer_z - conn_centroid.z() < 3.0f) { + return -1.0f; + } float conn_yield_torque = compute_elastic_section_modulus( line_dir, + extreme_point, connection.centroid_accumulator, connection.second_moment_of_area_accumulator, connection.second_moment_of_area_covariance_accumulator, @@ -933,7 +942,8 @@ Issues check_global_stability(SupportGridFilter supports_presence_grid, transfered_weakest_connection.print_info("transfered_weakest_connection"); #endif - if (estimate_conn_strength(transfered_weakest_connection) > estimate_conn_strength(new_weakest_connection)) { + if (estimate_conn_strength(transfered_weakest_connection) + > estimate_conn_strength(new_weakest_connection)) { transfered_weakest_connection = new_weakest_connection; } next_island_weakest_connection.emplace(island_idx, transfered_weakest_connection); @@ -959,8 +969,7 @@ Issues check_global_stability(SupportGridFilter supports_presence_grid, #ifdef DETAILED_DEBUG_LOGS weakest_conn.print_info("weakest connection info: "); #endif - std::vector dummy { }; - LinesDistancer island_lines_dist(dummy); + LinesDistancer island_lines_dist(island.external_lines); float unchecked_dist = params.min_distance_between_support_points + 1.0f; for (const ExtrusionLine &line : island.external_lines) { @@ -969,18 +978,15 @@ Issues check_global_stability(SupportGridFilter supports_presence_grid, unchecked_dist += line.len; } else { unchecked_dist = line.len; - auto force = part.is_stable_while_extruding(weakest_conn, line, layer_z, params); + Vec2f target_point; + size_t _idx; + Vec3f pivot_site_search_point = to_3d(Vec2f(line.b + (line.b - line.a).normalized() * 300.0f), + layer_z); + island_lines_dist.signed_distance_from_lines(pivot_site_search_point.head<2>(), _idx, + target_point); + Vec3f support_point = to_3d(target_point, layer_z); + auto force = part.is_stable_while_extruding(weakest_conn, line, support_point, layer_z, params); if (force > 0) { - if (island_lines_dist.get_lines().empty()) { - island_lines_dist = LinesDistancer(island.external_lines); - } - Vec2f target_point; - size_t _idx; - Vec3f pivot_site_search_point = to_3d(Vec2f(line.b + (line.b - line.a).normalized() * 300.0f), - layer_z); - island_lines_dist.signed_distance_from_lines(pivot_site_search_point.head<2>(), _idx, - target_point); - Vec3f support_point = to_3d(target_point, layer_z); if (!supports_presence_grid.position_taken(support_point)) { float area = params.support_points_interface_radius * params.support_points_interface_radius * float(PI); diff --git a/src/libslic3r/SupportSpotsGenerator.hpp b/src/libslic3r/SupportSpotsGenerator.hpp index 10c7abe70f..7052105612 100644 --- a/src/libslic3r/SupportSpotsGenerator.hpp +++ b/src/libslic3r/SupportSpotsGenerator.hpp @@ -21,7 +21,7 @@ struct Params { const float bed_adhesion_yield_strength = 0.128f * 1e6f; //MPa * 1e^6 = (g*mm/s^2)/mm^2 = g/(mm*s^2); yield strength of the bed surface const float material_yield_strength = 15.0f * 1e6f; // (g*mm/s^2)/mm^2; 33 MPa is yield strength of ABS, which has the lowest yield strength from common materials. const float standard_extruder_conflict_force = 20.0f * gravity_constant; // force that can occasionally push the model due to various factors (filament leaks, small curling, ... ); - const float malformations_additive_conflict_extruder_force = 300.0f * gravity_constant; // for areas with possible high layered curled filaments + const float malformations_additive_conflict_extruder_force = 150.0f * gravity_constant; // for areas with possible high layered curled filaments }; struct SupportPoint { From 1d4f41a2fd91823c03f724611066faf35d721cfb Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Thu, 28 Jul 2022 14:46:16 +0200 Subject: [PATCH 63/78] improved option logic, custom setting for object soe that it uses the painted supports --- src/libslic3r/PrintObject.cpp | 33 +++++++++++--------- src/libslic3r/SupportSpotsGenerator.cpp | 2 +- src/libslic3r/SupportSpotsGenerator.hpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 18 ++++++++--- 4 files changed, 34 insertions(+), 21 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index b085c7a07a..2d473415c5 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -398,6 +398,21 @@ void PrintObject::ironing() } } + +/* +std::vector problematic_layers = SupportSpotsGenerator::quick_search(this); + if (!problematic_layers.empty()) { + std::cout << "Object needs supports" << std::endl; + this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, + L("Supportable issues found. Consider enabling supports for this object")); + this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, + L("Supportable issues found. Consider enabling supports for this object")); + for (size_t index = 0; index < std::min(problematic_layers.size(), size_t(4)); ++index) { + this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, + format(L("Layer with issues: %1%"), problematic_layers[index] + 1)); + } + } + */ void PrintObject::generate_support_spots() { if (this->set_started(posSupportSpotsSearch)) { @@ -405,20 +420,10 @@ void PrintObject::generate_support_spots() << "Searching support spots - start"; m_print->set_status(75, L("Searching support spots")); - if (!this->m_config.support_material) { - std::vector problematic_layers = SupportSpotsGenerator::quick_search(this); - if (!problematic_layers.empty()) { - std::cout << "Object needs supports" << std::endl; - this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, - L("Supportable issues found. Consider enabling supports for this object")); - this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, - L("Supportable issues found. Consider enabling supports for this object")); - for (size_t index = 0; index < std::min(problematic_layers.size(), size_t(4)); ++index) { - this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, - format(L("Layer with issues: %1%"), problematic_layers[index] + 1)); - } - } - } else { + if (this->m_config.support_material && !this->m_config.support_material_auto && + std::all_of(this->model_object()->volumes.begin(), this->model_object()->volumes.end(), + [](const ModelVolume* mv){return mv->supported_facets.empty();}) + ) { SupportSpotsGenerator::Issues issues = SupportSpotsGenerator::full_search(this); auto obj_transform = this->trafo_centered(); for (ModelVolume *model_volume : this->model_object()->volumes) { diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index 59ed818b37..b75efd6ca0 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -434,7 +434,7 @@ std::tuple reckon_islands( const std::vector &layer_lines, const Params ¶ms) { - //extract extrusions (connected paths from multiple lines) from the layer_lines. belonging to single polyline is determined by origin_entity ptr. + //extract extrusions (connected paths from multiple lines) from the layer_lines. Grouping by the same polyline is determined by common origin_entity ptr. // result is a vector of [start, end) index pairs into the layer_lines vector std::vector> extrusions; //start and end idx (one beyond last extrusion) [start,end) const ExtrusionEntity *current_ex = nullptr; diff --git a/src/libslic3r/SupportSpotsGenerator.hpp b/src/libslic3r/SupportSpotsGenerator.hpp index 7052105612..6b94ceeaa2 100644 --- a/src/libslic3r/SupportSpotsGenerator.hpp +++ b/src/libslic3r/SupportSpotsGenerator.hpp @@ -19,7 +19,7 @@ struct Params { const float max_acceleration = 9*1000.0f; // mm/s^2 ; max acceleration of object (bed) in XY (NOTE: The max hit is received by the object in the jerk phase, so the usual machine limits are too low) const float filament_density = 1.25e-3f ; // g/mm^3 ; Common filaments are very lightweight, so precise number is not that important const float bed_adhesion_yield_strength = 0.128f * 1e6f; //MPa * 1e^6 = (g*mm/s^2)/mm^2 = g/(mm*s^2); yield strength of the bed surface - const float material_yield_strength = 15.0f * 1e6f; // (g*mm/s^2)/mm^2; 33 MPa is yield strength of ABS, which has the lowest yield strength from common materials. + const float material_yield_strength = 33.0f * 1e6f; // (g*mm/s^2)/mm^2; 33 MPa is yield strength of ABS, which has the lowest yield strength from common materials. const float standard_extruder_conflict_force = 20.0f * gravity_constant; // force that can occasionally push the model due to various factors (filament leaks, small curling, ... ); const float malformations_additive_conflict_extruder_force = 150.0f * gravity_constant; // for areas with possible high layered curled filaments }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 7e8f0954e0..494adb55ad 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -42,6 +42,7 @@ bool GLGizmoFdmSupports::on_init() m_shortcut_key = WXK_CONTROL_L; m_desc["auto_generate"] = _L("Auto-generate supports"); + m_desc["generating"] = _L("Generating supports..."); m_desc["clipping_of_view"] = _L("Clipping of view") + ": "; m_desc["reset_direction"] = _L("Reset direction"); m_desc["cursor_size"] = _L("Brush size") + ": "; @@ -156,10 +157,13 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l ImGui::Separator(); - bool generate = m_imgui->button(m_desc.at("auto_generate")); - if (generate) - auto_generate(); - + if (waiting_for_autogenerated_supports) { + m_imgui->text(m_desc.at("generating")); + } else { + bool generate = m_imgui->button(m_desc.at("auto_generate")); + if (generate) + auto_generate(); + } ImGui::Separator(); float position_before_text_y = ImGui::GetCursorPos().y; @@ -410,6 +414,7 @@ void GLGizmoFdmSupports::get_data_from_backend() if (mv->is_model_part()){ mesh_id++; mv->supported_facets.assign(mvs[mv->id().id]->supported_facets); + mv->supported_facets.touch(); m_triangle_selectors[mesh_id]->deserialize(mv->supported_facets.get_data(), true); m_triangle_selectors[mesh_id]->request_update_render_data(); } @@ -472,7 +477,7 @@ bool GLGizmoFdmSupports::has_backend_supports() const if (! mo) return false; - // find SlaPrintObject with this ID + // find PrintObject with this ID for (const PrintObject* po : m_parent.fff_print()->objects()) { if (po->model_object()->id() == mo->id()) return po->is_step_done(posSupportSpotsSearch); @@ -495,6 +500,9 @@ void GLGizmoFdmSupports::auto_generate() return vol->type() != ModelVolumeType::MODEL_PART || vol->supported_facets.empty(); }); + mo->config.set("support_material", true); + mo->config.set("support_material_auto", false); + MessageDialog dlg(GUI::wxGetApp().plater(), _L("Autogeneration will erase all currently painted areas.") + "\n\n" + _L("Are you sure you want to do it?") + "\n", From ea769776027ef6233af5963bfa699e4c62f4e473 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Thu, 28 Jul 2022 15:47:34 +0200 Subject: [PATCH 64/78] Quick fix for invalidation of support spots search --- src/libslic3r/PrintApply.cpp | 4 +++- src/libslic3r/PrintObject.cpp | 2 +- src/libslic3r/SupportSpotsGenerator.hpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 11 ++++++++++- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/PrintApply.cpp b/src/libslic3r/PrintApply.cpp index 4c085728c7..2a5b14c7e3 100644 --- a/src/libslic3r/PrintApply.cpp +++ b/src/libslic3r/PrintApply.cpp @@ -1194,8 +1194,10 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ update_apply_status(false); } // Invalidate just the supports step. - for (const PrintObjectStatus &print_object_status : print_objects_range) + for (const PrintObjectStatus &print_object_status : print_objects_range) { + update_apply_status(print_object_status.print_object->invalidate_step(posSupportSpotsSearch)); update_apply_status(print_object_status.print_object->invalidate_step(posSupportMaterial)); + } if (supports_differ) { // Copy just the support volumes. model_volume_list_update_supports(model_object, model_object_new); diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 2d473415c5..59fbcc99d9 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -436,7 +436,7 @@ void PrintObject::generate_support_spots() Vec3f point = Vec3f(inv_transform * support_point.position); Vec3f origin = Vec3f( inv_transform * Vec3f(support_point.position.x(), support_point.position.y(), 0.0f)); - selector.enforce_spot(point, origin, 0.6f); + selector.enforce_spot(point, origin, 1.0f); } model_volume->supported_facets.set(selector.selector); diff --git a/src/libslic3r/SupportSpotsGenerator.hpp b/src/libslic3r/SupportSpotsGenerator.hpp index 6b94ceeaa2..6e435faa31 100644 --- a/src/libslic3r/SupportSpotsGenerator.hpp +++ b/src/libslic3r/SupportSpotsGenerator.hpp @@ -13,7 +13,7 @@ struct Params { const float bridge_distance_decrease_by_curvature_factor = 3.0f; // allowed bridge distance = bridge_distance / (this factor * (curvature / PI) ) const float min_distance_between_support_points = 3.0f; //mm - const float support_points_interface_radius = 0.6f; // mm + const float support_points_interface_radius = 1.0f; // mm const float gravity_constant = 9806.65f; // mm/s^2; gravity acceleration on Earth's surface, algorithm assumes that printer is in upwards position. const float max_acceleration = 9*1000.0f; // mm/s^2 ; max acceleration of object (bed) in XY (NOTE: The max hit is received by the object in the jerk phase, so the usual machine limits are too low) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 494adb55ad..c193dbf908 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -414,7 +414,6 @@ void GLGizmoFdmSupports::get_data_from_backend() if (mv->is_model_part()){ mesh_id++; mv->supported_facets.assign(mvs[mv->id().id]->supported_facets); - mv->supported_facets.touch(); m_triangle_selectors[mesh_id]->deserialize(mv->supported_facets.get_data(), true); m_triangle_selectors[mesh_id]->request_update_render_data(); } @@ -510,6 +509,16 @@ void GLGizmoFdmSupports::auto_generate() if (not_painted || dlg.ShowModal() == wxID_YES) { Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Autogenerate support points")); + int mesh_id = -1.0f; + for (ModelVolume *mv : mo->volumes) { + if (mv->is_model_part()) { + mesh_id++; + mv->supported_facets.reset(); + m_triangle_selectors[mesh_id]->reset(); + m_triangle_selectors[mesh_id]->request_update_render_data(); + } + } + this->waiting_for_autogenerated_supports = true; wxGetApp().CallAfter([this]() { reslice_FDM_supports(); }); } From 14f109e703bb7afe9317654b4d64fa26bfe1b0be Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Thu, 28 Jul 2022 17:46:03 +0200 Subject: [PATCH 65/78] refactored local issues to use overhang distance --- src/libslic3r/PrintObject.cpp | 2 +- src/libslic3r/SupportSpotsGenerator.cpp | 24 +++++++++++++++--------- src/libslic3r/SupportSpotsGenerator.hpp | 5 +++-- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 59fbcc99d9..2d473415c5 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -436,7 +436,7 @@ void PrintObject::generate_support_spots() Vec3f point = Vec3f(inv_transform * support_point.position); Vec3f origin = Vec3f( inv_transform * Vec3f(support_point.position.x(), support_point.position.y(), 0.0f)); - selector.enforce_spot(point, origin, 1.0f); + selector.enforce_spot(point, origin, 0.6f); } model_volume->supported_facets.set(selector.selector); diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index b75efd6ca0..bb60605bc9 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -354,10 +354,12 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, const auto to_vec3f = [layer_z](const Vec2f &point) { return Vec3f(point.x(), point.y(), layer_z); }; + float overhang_dist = tan(params.overhang_angle_deg * PI / 180.0f)*layer_region->layer()->height; + Points points { }; entity->collect_points(points); std::vector lines; - lines.reserve(points.size() * 1.5); + lines.reserve(points.size() * 1.5f); lines.emplace_back(unscaled(points[0]).cast(), unscaled(points[0]).cast(), entity); for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { Vec2f start = unscaled(points[point_idx]).cast(); @@ -396,14 +398,18 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, float dist_from_prev_layer = prev_layer_lines.signed_distance_from_lines(current_line.b, nearest_line_idx, nearest_point); - if (fabs(dist_from_prev_layer) < flow_width) { + if (fabs(dist_from_prev_layer) < overhang_dist) { bridging_acc.reset(); } else { bridging_acc.add_distance(current_line.len); - if (bridging_acc.distance // if unsupported distance is larger than bridge distance linearly decreased by curvature, enforce supports. - > params.bridge_distance - / (1.0f + (bridging_acc.max_curvature - * params.bridge_distance_decrease_by_curvature_factor / PI))) { + // if unsupported distance is larger than bridge distance linearly decreased by curvature, enforce supports. + bool in_layer_dist_condition = bridging_acc.distance + > params.bridge_distance / (1.0f + (bridging_acc.max_curvature + * params.bridge_distance_decrease_by_curvature_factor / PI)); + bool between_layers_condition = fabs(dist_from_prev_layer) > 5.0f*overhang_dist || + prev_layer_lines.get_line(nearest_line_idx).malformation > 0.3f; + + if (in_layer_dist_condition && between_layers_condition) { issues.support_points.emplace_back(to_vec3f(current_line.b), 0.0f, Vec3f(0.f, 0.0f, -1.0f)); current_line.support_point_generated = true; bridging_acc.reset(); @@ -411,13 +417,13 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, } //malformation - if (fabs(dist_from_prev_layer) < flow_width * 2.0f) { + if (fabs(dist_from_prev_layer) < overhang_dist * 5.0f) { const ExtrusionLine &nearest_line = prev_layer_lines.get_line(nearest_line_idx); current_line.malformation += 0.9 * nearest_line.malformation; } - if (dist_from_prev_layer > flow_width * 0.3) { + if (dist_from_prev_layer > overhang_dist) { malformation_acc.add_distance(current_line.len); - current_line.malformation += 0.3f * layer_region->layer()->height + current_line.malformation += 0.1f * (0.8f + 0.2f * malformation_acc.max_curvature / (1.0f + 0.5f * malformation_acc.distance)); } else { malformation_acc.reset(); diff --git a/src/libslic3r/SupportSpotsGenerator.hpp b/src/libslic3r/SupportSpotsGenerator.hpp index 6e435faa31..e8ad8c6249 100644 --- a/src/libslic3r/SupportSpotsGenerator.hpp +++ b/src/libslic3r/SupportSpotsGenerator.hpp @@ -10,10 +10,11 @@ namespace SupportSpotsGenerator { struct Params { // the algorithm should use the following units for all computations: distance [mm], mass [g], time [s], force [g*mm/s^2] const float bridge_distance = 12.0f; //mm - const float bridge_distance_decrease_by_curvature_factor = 3.0f; // allowed bridge distance = bridge_distance / (this factor * (curvature / PI) ) + const float bridge_distance_decrease_by_curvature_factor = 5.0f; // allowed bridge distance = bridge_distance / (this factor * (curvature / PI) ) + const float overhang_angle_deg = 55.0f; const float min_distance_between_support_points = 3.0f; //mm - const float support_points_interface_radius = 1.0f; // mm + const float support_points_interface_radius = 0.6f; // mm const float gravity_constant = 9806.65f; // mm/s^2; gravity acceleration on Earth's surface, algorithm assumes that printer is in upwards position. const float max_acceleration = 9*1000.0f; // mm/s^2 ; max acceleration of object (bed) in XY (NOTE: The max hit is received by the object in the jerk phase, so the usual machine limits are too low) From 62c3ca5e991576e54d84bd355ceb692148a5b5d1 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Fri, 29 Jul 2022 13:08:27 +0200 Subject: [PATCH 66/78] gui integration, overhang angle hack --- src/libslic3r/PrintObject.cpp | 4 +++- src/libslic3r/SupportSpotsGenerator.cpp | 12 ++++++------ src/libslic3r/SupportSpotsGenerator.hpp | 6 +++--- src/slic3r/GUI/ConfigManipulation.cpp | 2 +- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 2d473415c5..631ad7c3c2 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -424,7 +424,9 @@ void PrintObject::generate_support_spots() std::all_of(this->model_object()->volumes.begin(), this->model_object()->volumes.end(), [](const ModelVolume* mv){return mv->supported_facets.empty();}) ) { - SupportSpotsGenerator::Issues issues = SupportSpotsGenerator::full_search(this); + SupportSpotsGenerator::Params params{}; + params.overhang_angle_deg = 90.001f - this->m_config.support_material_threshold; + SupportSpotsGenerator::Issues issues = SupportSpotsGenerator::full_search(this, params); auto obj_transform = this->trafo_centered(); for (ModelVolume *model_volume : this->model_object()->volumes) { if (model_volume->is_model_part()) { diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index bb60605bc9..2e5c8dbd42 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -15,8 +15,8 @@ #include "libslic3r/ClipperUtils.hpp" #include "Geometry/ConvexHull.hpp" -#define DETAILED_DEBUG_LOGS -#define DEBUG_FILES +//#define DETAILED_DEBUG_LOGS +//#define DEBUG_FILES #ifdef DEBUG_FILES #include @@ -406,8 +406,8 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, bool in_layer_dist_condition = bridging_acc.distance > params.bridge_distance / (1.0f + (bridging_acc.max_curvature * params.bridge_distance_decrease_by_curvature_factor / PI)); - bool between_layers_condition = fabs(dist_from_prev_layer) > 5.0f*overhang_dist || - prev_layer_lines.get_line(nearest_line_idx).malformation > 0.3f; + bool between_layers_condition = fabs(dist_from_prev_layer) > 3.0f*flow_width || + prev_layer_lines.get_line(nearest_line_idx).malformation > 0.6f; if (in_layer_dist_condition && between_layers_condition) { issues.support_points.emplace_back(to_vec3f(current_line.b), 0.0f, Vec3f(0.f, 0.0f, -1.0f)); @@ -417,13 +417,13 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, } //malformation - if (fabs(dist_from_prev_layer) < overhang_dist * 5.0f) { + if (fabs(dist_from_prev_layer) < 3.0f*flow_width) { const ExtrusionLine &nearest_line = prev_layer_lines.get_line(nearest_line_idx); current_line.malformation += 0.9 * nearest_line.malformation; } if (dist_from_prev_layer > overhang_dist) { malformation_acc.add_distance(current_line.len); - current_line.malformation += 0.1f + current_line.malformation += 0.3f * (0.8f + 0.2f * malformation_acc.max_curvature / (1.0f + 0.5f * malformation_acc.distance)); } else { malformation_acc.reset(); diff --git a/src/libslic3r/SupportSpotsGenerator.hpp b/src/libslic3r/SupportSpotsGenerator.hpp index e8ad8c6249..87f1f48c39 100644 --- a/src/libslic3r/SupportSpotsGenerator.hpp +++ b/src/libslic3r/SupportSpotsGenerator.hpp @@ -11,7 +11,7 @@ struct Params { // the algorithm should use the following units for all computations: distance [mm], mass [g], time [s], force [g*mm/s^2] const float bridge_distance = 12.0f; //mm const float bridge_distance_decrease_by_curvature_factor = 5.0f; // allowed bridge distance = bridge_distance / (this factor * (curvature / PI) ) - const float overhang_angle_deg = 55.0f; + float overhang_angle_deg = 50.0f; const float min_distance_between_support_points = 3.0f; //mm const float support_points_interface_radius = 0.6f; // mm @@ -21,8 +21,8 @@ struct Params { const float filament_density = 1.25e-3f ; // g/mm^3 ; Common filaments are very lightweight, so precise number is not that important const float bed_adhesion_yield_strength = 0.128f * 1e6f; //MPa * 1e^6 = (g*mm/s^2)/mm^2 = g/(mm*s^2); yield strength of the bed surface const float material_yield_strength = 33.0f * 1e6f; // (g*mm/s^2)/mm^2; 33 MPa is yield strength of ABS, which has the lowest yield strength from common materials. - const float standard_extruder_conflict_force = 20.0f * gravity_constant; // force that can occasionally push the model due to various factors (filament leaks, small curling, ... ); - const float malformations_additive_conflict_extruder_force = 150.0f * gravity_constant; // for areas with possible high layered curled filaments + const float standard_extruder_conflict_force = 50.0f * gravity_constant; // force that can occasionally push the model due to various factors (filament leaks, small curling, ... ); + const float malformations_additive_conflict_extruder_force = 200.0f * gravity_constant; // for areas with possible high layered curled filaments }; struct SupportPoint { diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 96bf2fe020..d591297dfb 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -282,7 +282,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) "dont_support_bridges", "support_material_extrusion_width", "support_material_contact_distance", "support_material_xy_spacing" }) toggle_field(el, have_support_material); - toggle_field("support_material_threshold", have_support_material_auto); +// toggle_field("support_material_threshold", have_support_material_auto); toggle_field("support_material_bottom_contact_distance", have_support_material && ! have_support_soluble); toggle_field("support_material_closing_radius", have_support_material && support_material_style == smsSnug); From 4eaa863ba4c892dfa59523a8b58343a1ad2943c8 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Fri, 12 Aug 2022 15:27:00 +0200 Subject: [PATCH 67/78] make supports bigger, improve malformations, TODO: do not support small extrusions, check part size --- src/libslic3r/PrintObject.cpp | 6 ++-- src/libslic3r/SupportSpotsGenerator.cpp | 27 ++++++++++++---- src/libslic3r/SupportSpotsGenerator.hpp | 41 ++++++++++++++++++++----- 3 files changed, 56 insertions(+), 18 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 631ad7c3c2..918c4a3259 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -419,13 +419,11 @@ void PrintObject::generate_support_spots() BOOST_LOG_TRIVIAL(debug) << "Searching support spots - start"; m_print->set_status(75, L("Searching support spots")); - if (this->m_config.support_material && !this->m_config.support_material_auto && std::all_of(this->model_object()->volumes.begin(), this->model_object()->volumes.end(), [](const ModelVolume* mv){return mv->supported_facets.empty();}) ) { - SupportSpotsGenerator::Params params{}; - params.overhang_angle_deg = 90.001f - this->m_config.support_material_threshold; + SupportSpotsGenerator::Params params{90.001f - this->m_config.support_material_threshold, this->print()->m_config.filament_type.values}; SupportSpotsGenerator::Issues issues = SupportSpotsGenerator::full_search(this, params); auto obj_transform = this->trafo_centered(); for (ModelVolume *model_volume : this->model_object()->volumes) { @@ -438,7 +436,7 @@ void PrintObject::generate_support_spots() Vec3f point = Vec3f(inv_transform * support_point.position); Vec3f origin = Vec3f( inv_transform * Vec3f(support_point.position.x(), support_point.position.y(), 0.0f)); - selector.enforce_spot(point, origin, 0.6f); + selector.enforce_spot(point, origin, 1.5f); } model_volume->supported_facets.set(selector.selector); diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index 2e5c8dbd42..584eaf58c2 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -15,8 +15,8 @@ #include "libslic3r/ClipperUtils.hpp" #include "Geometry/ConvexHull.hpp" -//#define DETAILED_DEBUG_LOGS -//#define DEBUG_FILES +#define DETAILED_DEBUG_LOGS +#define DEBUG_FILES #ifdef DEBUG_FILES #include @@ -337,6 +337,15 @@ struct ExtrusionPropertiesAccumulator { } }; +// base function: ((e^(((1)/(x^(2)+1)))-1)/(e-1)) +// checkout e.g. here: https://www.geogebra.org/calculator +float gauss(float value, float mean_x_coord, float mean_value, float falloff_speed) { + float shifted = value - mean_x_coord; + float denominator = falloff_speed * shifted * shifted + 1.0f; + float exponent = 1.0f / denominator; + return mean_value * (std::exp(exponent) - 1.0f) / (std::exp(1.0f) - 1.0f); +} + void check_extrusion_entity_stability(const ExtrusionEntity *entity, std::vector &checked_lines_out, float layer_z, @@ -407,7 +416,7 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, > params.bridge_distance / (1.0f + (bridging_acc.max_curvature * params.bridge_distance_decrease_by_curvature_factor / PI)); bool between_layers_condition = fabs(dist_from_prev_layer) > 3.0f*flow_width || - prev_layer_lines.get_line(nearest_line_idx).malformation > 0.6f; + prev_layer_lines.get_line(nearest_line_idx).malformation > 3.0f * layer_region->layer()->height; if (in_layer_dist_condition && between_layers_condition) { issues.support_points.emplace_back(to_vec3f(current_line.b), 0.0f, Vec3f(0.f, 0.0f, -1.0f)); @@ -423,8 +432,8 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, } if (dist_from_prev_layer > overhang_dist) { malformation_acc.add_distance(current_line.len); - current_line.malformation += 0.3f - * (0.8f + 0.2f * malformation_acc.max_curvature / (1.0f + 0.5f * malformation_acc.distance)); + current_line.malformation += layer_region->layer()->height * (0.5f + + 1.5f * (malformation_acc.max_curvature / PI) * gauss(malformation_acc.distance, 5.0f, 1.0f, 0.2f)); } else { malformation_acc.reset(); } @@ -729,7 +738,7 @@ public: this->sticking_second_moment_of_area_accumulator, this->sticking_second_moment_of_area_covariance_accumulator, this->sticking_area) - * params.bed_adhesion_yield_strength; + * params.get_bed_adhesion_yield_strength(); float bed_weight_arm = (bed_centroid.head<2>() - mass_centroid.head<2>()).norm(); float bed_weight_torque = bed_weight_arm * weight; @@ -759,6 +768,10 @@ public: BOOST_LOG_TRIVIAL(debug) << "SSG: bed_conflict_torque_arm: " << bed_conflict_torque_arm; BOOST_LOG_TRIVIAL(debug) + << "SSG: extruded_line.malformation: " << extruded_line.malformation; + BOOST_LOG_TRIVIAL(debug) + << "SSG: extruder_conflict_force: " << extruder_conflict_force; + BOOST_LOG_TRIVIAL(debug) << "SSG: bed_extruder_conflict_torque: " << bed_extruder_conflict_torque; BOOST_LOG_TRIVIAL(debug) << "SSG: total_torque: " << bed_total_torque << " layer_z: " << layer_z; @@ -971,6 +984,8 @@ Issues check_global_stability(SupportGridFilter supports_presence_grid, for (size_t island_idx = 0; island_idx < islands_graph[layer_idx].islands.size(); ++island_idx) { const Island &island = islands_graph[layer_idx].islands[island_idx]; ObjectPart &part = active_object_parts.access(prev_island_to_object_part_mapping[island_idx]); + + IslandConnection &weakest_conn = prev_island_weakest_connection[island_idx]; #ifdef DETAILED_DEBUG_LOGS weakest_conn.print_info("weakest connection info: "); diff --git a/src/libslic3r/SupportSpotsGenerator.hpp b/src/libslic3r/SupportSpotsGenerator.hpp index 87f1f48c39..b139ae0980 100644 --- a/src/libslic3r/SupportSpotsGenerator.hpp +++ b/src/libslic3r/SupportSpotsGenerator.hpp @@ -2,27 +2,52 @@ #define SRC_LIBSLIC3R_SUPPORTABLEISSUESSEARCH_HPP_ #include "libslic3r/Print.hpp" +#include namespace Slic3r { namespace SupportSpotsGenerator { struct Params { + Params(float overhang_angle_deg, const std::vector& filament_types) : overhang_angle_deg(overhang_angle_deg) + { + if (filament_types.size() > 1) { + BOOST_LOG_TRIVIAL(warning) << "SupportSpotsGenerator does not currently handle different materials properly, only first will be used"; + } + if (filament_types.empty() || filament_types[0].empty()) { + BOOST_LOG_TRIVIAL(error) << "SupportSpotsGenerator error: empty filament_type"; + filament_type = std::string("PLA"); + }else { + filament_type = filament_types[0]; + } + } + // the algorithm should use the following units for all computations: distance [mm], mass [g], time [s], force [g*mm/s^2] - const float bridge_distance = 12.0f; //mm + const float bridge_distance = 15.0f; //mm const float bridge_distance_decrease_by_curvature_factor = 5.0f; // allowed bridge distance = bridge_distance / (this factor * (curvature / PI) ) - float overhang_angle_deg = 50.0f; + const float overhang_angle_deg = 50.0f; const float min_distance_between_support_points = 3.0f; //mm - const float support_points_interface_radius = 0.6f; // mm + const float support_points_interface_radius = 1.5f; // mm + std::string filament_type; const float gravity_constant = 9806.65f; // mm/s^2; gravity acceleration on Earth's surface, algorithm assumes that printer is in upwards position. const float max_acceleration = 9*1000.0f; // mm/s^2 ; max acceleration of object (bed) in XY (NOTE: The max hit is received by the object in the jerk phase, so the usual machine limits are too low) const float filament_density = 1.25e-3f ; // g/mm^3 ; Common filaments are very lightweight, so precise number is not that important - const float bed_adhesion_yield_strength = 0.128f * 1e6f; //MPa * 1e^6 = (g*mm/s^2)/mm^2 = g/(mm*s^2); yield strength of the bed surface const float material_yield_strength = 33.0f * 1e6f; // (g*mm/s^2)/mm^2; 33 MPa is yield strength of ABS, which has the lowest yield strength from common materials. - const float standard_extruder_conflict_force = 50.0f * gravity_constant; // force that can occasionally push the model due to various factors (filament leaks, small curling, ... ); - const float malformations_additive_conflict_extruder_force = 200.0f * gravity_constant; // for areas with possible high layered curled filaments + const float standard_extruder_conflict_force = 20.0f * gravity_constant; // force that can occasionally push the model due to various factors (filament leaks, small curling, ... ); + const float malformations_additive_conflict_extruder_force = 300.0f * gravity_constant; // for areas with possible high layered curled filaments + + // MPa * 1e^6 = (g*mm/s^2)/mm^2 = g/(mm*s^2); yield strength of the bed surface + float get_bed_adhesion_yield_strength() const { + if (filament_type == "PLA"){ + return 0.018f * 1e6f; + } else if (filament_type == "PET" || filament_type == "PETG") { + return 0.3f * 1e6f; + } else { //PLA default value - defensive approach, PLA has quite low adhesion + return 0.018f * 1e6f; + } + } }; struct SupportPoint { @@ -36,8 +61,8 @@ struct Issues { std::vector support_points; }; -std::vector quick_search(const PrintObject *po, const Params ¶ms = Params { }); -Issues full_search(const PrintObject *po, const Params ¶ms = Params { }); +std::vector quick_search(const PrintObject *po, const Params ¶ms); +Issues full_search(const PrintObject *po, const Params ¶ms); } From 970c9e033d9594244d6dfc8735d29f5fd85caddc Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Mon, 15 Aug 2022 13:03:37 +0200 Subject: [PATCH 68/78] fix triangle selector painting on models with transformation --- src/libslic3r/PrintObject.cpp | 10 +++++----- src/libslic3r/SupportSpotsGenerator.cpp | 2 -- src/libslic3r/TriangleSelectorWrapper.cpp | 9 ++++----- src/libslic3r/TriangleSelectorWrapper.hpp | 4 +++- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 918c4a3259..256cf70270 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -428,14 +428,14 @@ void PrintObject::generate_support_spots() auto obj_transform = this->trafo_centered(); for (ModelVolume *model_volume : this->model_object()->volumes) { if (model_volume->is_model_part()) { - Transform3d model_transformation = model_volume->get_matrix(); - Transform3f inv_transform = (obj_transform * model_transformation).inverse().cast(); - TriangleSelectorWrapper selector { model_volume->mesh() }; + Transform3d mesh_transformation = obj_transform * model_volume->get_matrix(); + Transform3d inv_transform = mesh_transformation.inverse(); + TriangleSelectorWrapper selector { model_volume->mesh(), mesh_transformation}; for (const SupportSpotsGenerator::SupportPoint &support_point : issues.support_points) { - Vec3f point = Vec3f(inv_transform * support_point.position); + Vec3f point = Vec3f(inv_transform.cast() * support_point.position); Vec3f origin = Vec3f( - inv_transform * Vec3f(support_point.position.x(), support_point.position.y(), 0.0f)); + inv_transform.cast() * Vec3f(support_point.position.x(), support_point.position.y(), 0.0f)); selector.enforce_spot(point, origin, 1.5f); } diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index 584eaf58c2..a1ecd9652a 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -984,8 +984,6 @@ Issues check_global_stability(SupportGridFilter supports_presence_grid, for (size_t island_idx = 0; island_idx < islands_graph[layer_idx].islands.size(); ++island_idx) { const Island &island = islands_graph[layer_idx].islands[island_idx]; ObjectPart &part = active_object_parts.access(prev_island_to_object_part_mapping[island_idx]); - - IslandConnection &weakest_conn = prev_island_weakest_connection[island_idx]; #ifdef DETAILED_DEBUG_LOGS weakest_conn.print_info("weakest connection info: "); diff --git a/src/libslic3r/TriangleSelectorWrapper.cpp b/src/libslic3r/TriangleSelectorWrapper.cpp index ec22ed5dd1..3a5f44d843 100644 --- a/src/libslic3r/TriangleSelectorWrapper.cpp +++ b/src/libslic3r/TriangleSelectorWrapper.cpp @@ -1,11 +1,10 @@ -#include "Model.hpp" #include "TriangleSelectorWrapper.hpp" #include namespace Slic3r { -TriangleSelectorWrapper::TriangleSelectorWrapper(const TriangleMesh &mesh) : - mesh(mesh), selector(mesh), triangles_tree( +TriangleSelectorWrapper::TriangleSelectorWrapper(const TriangleMesh &mesh, const Transform3d& mesh_transform) : + mesh(mesh), mesh_transform(mesh_transform), selector(mesh), triangles_tree( AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(mesh.its.vertices, mesh.its.indices)) { } @@ -23,7 +22,7 @@ void TriangleSelectorWrapper::enforce_spot(const Vec3f &point, const Vec3f &orig Vec3f face_normal = its_face_normal(mesh.its, hit.id); if ((point - pos).norm() < radius && face_normal.dot(dir) < 0) { std::unique_ptr cursor = std::make_unique( - pos, origin, radius, Transform3d::Identity(), TriangleSelector::ClippingPlane { }); + pos, origin, radius, this->mesh_transform, TriangleSelector::ClippingPlane { }); selector.select_patch(hit.id, std::move(cursor), EnforcerBlockerType::ENFORCER, Transform3d::Identity(), true, 0.0f); break; @@ -36,7 +35,7 @@ void TriangleSelectorWrapper::enforce_spot(const Vec3f &point, const Vec3f &orig triangles_tree, point, hit_idx_out, hit_point_out); if (dist < radius) { std::unique_ptr cursor = std::make_unique( - point, origin, radius, Transform3d::Identity(), TriangleSelector::ClippingPlane { }); + point, origin, radius, this->mesh_transform, TriangleSelector::ClippingPlane { }); selector.select_patch(hit_idx_out, std::move(cursor), EnforcerBlockerType::ENFORCER, Transform3d::Identity(), true, 0.0f); diff --git a/src/libslic3r/TriangleSelectorWrapper.hpp b/src/libslic3r/TriangleSelectorWrapper.hpp index 10707cc257..22c61d6279 100644 --- a/src/libslic3r/TriangleSelectorWrapper.hpp +++ b/src/libslic3r/TriangleSelectorWrapper.hpp @@ -2,6 +2,7 @@ #define SRC_LIBSLIC3R_TRIANGLESELECTORWRAPPER_HPP_ #include "TriangleSelector.hpp" +#include "Model.hpp" #include "AABBTreeIndirect.hpp" namespace Slic3r { @@ -15,10 +16,11 @@ namespace Slic3r { class TriangleSelectorWrapper { public: const TriangleMesh &mesh; + const Transform3d& mesh_transform; TriangleSelector selector; AABBTreeIndirect::Tree<3, float> triangles_tree; - TriangleSelectorWrapper(const TriangleMesh &mesh); + TriangleSelectorWrapper(const TriangleMesh &mesh, const Transform3d& mesh_transform); void enforce_spot(const Vec3f &point, const Vec3f& origin, float radius); From 6114b04594ac2071181951a94e0e038ff6cc2571 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Mon, 15 Aug 2022 17:27:06 +0200 Subject: [PATCH 69/78] improve bed adhesion estimation, comupute weight factor sign --- src/libslic3r/SupportSpotsGenerator.cpp | 52 +++++++++++++++++++------ 1 file changed, 41 insertions(+), 11 deletions(-) diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index a1ecd9652a..518c8cacc3 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -555,9 +555,18 @@ std::tuple reckon_islands( island.sticking_area += sticking_area; Vec2f middle = Vec2f((line.a + line.b) / 2.0f); island.sticking_centroid_accumulator += sticking_area * to_3d(middle, float(layer->slice_z)); - island.sticking_second_moment_of_area_accumulator += sticking_area * middle.cwiseProduct(middle); - island.sticking_second_moment_of_area_covariance_accumulator += sticking_area * middle.x() - * middle.y(); + // Bottom infill lines can be quite long, and algined, so the middle approximaton used above does not work + Vec2f dir = (line.b - line.a).normalized(); + float segment_length = flow_width; // segments of size flow_width + for (float segment_middle_dist = std::min(line.len, segment_length * 0.5f); segment_middle_dist < line.len; + segment_middle_dist += segment_length) { + Vec2f segment_middle = line.a + segment_middle_dist * dir; + island.sticking_second_moment_of_area_accumulator += segment_length * flow_width + * segment_middle.cwiseProduct(segment_middle); + island.sticking_second_moment_of_area_covariance_accumulator += segment_length * flow_width + * segment_middle.x() + * segment_middle.y(); + } } else if (layer_lines[lidx].support_point_generated) { float sticking_area = line.len * flow_width; island.sticking_area += sticking_area; @@ -662,9 +671,8 @@ public: * position.x() * position.y(); } - float compute_elastic_section_modulus( + float compute_directional_xy_variance( const Vec2f &line_dir, - const Vec3f &extreme_point, const Vec3f ¢roid_accumulator, const Vec2f &second_moment_of_area_accumulator, const float &second_moment_of_area_covariance_accumulator, @@ -678,7 +686,6 @@ public: float directional_xy_variance = line_dir.x() * line_dir.x() * variance.x() + line_dir.y() * line_dir.y() * variance.y() + 2.0f * line_dir.x() * line_dir.y() * covariance; - #ifdef DETAILED_DEBUG_LOGS BOOST_LOG_TRIVIAL(debug) << "centroid: " << centroid.x() << " " << centroid.y() << " " << centroid.z(); @@ -689,10 +696,27 @@ public: BOOST_LOG_TRIVIAL(debug) << "directional_xy_variance: " << directional_xy_variance; #endif + return directional_xy_variance; + } + + float compute_elastic_section_modulus( + const Vec2f &line_dir, + const Vec3f &extreme_point, + const Vec3f ¢roid_accumulator, + const Vec2f &second_moment_of_area_accumulator, + const float &second_moment_of_area_covariance_accumulator, + const float &area) const { + + float directional_xy_variance = compute_directional_xy_variance( + line_dir, + centroid_accumulator, + second_moment_of_area_accumulator, + second_moment_of_area_covariance_accumulator, + area); if (directional_xy_variance < EPSILON) { return 0.0f; } - + Vec3f centroid = centroid_accumulator / area; float extreme_fiber_dist = line_alg::distance_to( Linef(centroid.head<2>().cast(), (centroid.head<2>() + Vec2f(line_dir.y(), -line_dir.x())).cast()), extreme_point.head<2>().cast()); @@ -731,7 +755,7 @@ public: return 1.0f; Vec3f bed_centroid = this->sticking_centroid_accumulator / this->sticking_area; - float bed_yield_torque = compute_elastic_section_modulus( + float bed_yield_torque = - compute_elastic_section_modulus( line_dir, extreme_point, this->sticking_centroid_accumulator, @@ -740,8 +764,14 @@ public: this->sticking_area) * params.get_bed_adhesion_yield_strength(); - float bed_weight_arm = (bed_centroid.head<2>() - mass_centroid.head<2>()).norm(); - float bed_weight_torque = bed_weight_arm * weight; + Vec2f bed_weight_arm = (mass_centroid.head<2>() - bed_centroid.head<2>()); + float bed_weight_arm_len = bed_weight_arm.norm(); + float bed_weight_dir_xy_variance = compute_directional_xy_variance(bed_weight_arm, this->sticking_centroid_accumulator, + this->sticking_second_moment_of_area_accumulator, + this->sticking_second_moment_of_area_covariance_accumulator, + this->sticking_area); + float bed_weight_sign = bed_weight_arm_len < 2.0f * sqrt(bed_weight_dir_xy_variance) ? -1.0f : 1.0f; + float bed_weight_torque = bed_weight_sign * bed_weight_arm_len * weight; float bed_movement_arm = std::max(0.0f, mass_centroid.z() - bed_centroid.z()); float bed_movement_torque = movement_force * bed_movement_arm; @@ -750,7 +780,7 @@ public: float bed_extruder_conflict_torque = extruder_conflict_force * bed_conflict_torque_arm; float bed_total_torque = bed_movement_torque + bed_extruder_conflict_torque + bed_weight_torque - - bed_yield_torque; + + bed_yield_torque; #ifdef DETAILED_DEBUG_LOGS BOOST_LOG_TRIVIAL(debug) From 3773de29575acb5880cbc9d7a1d6364c2ec01052 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Tue, 16 Aug 2022 16:14:22 +0200 Subject: [PATCH 70/78] hardcode overhang angles, remove volumetric filtering (does not work correctly) --- src/libslic3r/PrintObject.cpp | 2 +- src/libslic3r/SupportSpotsGenerator.cpp | 11 +++++++--- src/libslic3r/SupportSpotsGenerator.hpp | 22 +++++++++++--------- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 2 ++ 4 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 256cf70270..c96e23fe06 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -423,7 +423,7 @@ void PrintObject::generate_support_spots() std::all_of(this->model_object()->volumes.begin(), this->model_object()->volumes.end(), [](const ModelVolume* mv){return mv->supported_facets.empty();}) ) { - SupportSpotsGenerator::Params params{90.001f - this->m_config.support_material_threshold, this->print()->m_config.filament_type.values}; + SupportSpotsGenerator::Params params{this->print()->m_config.filament_type.values}; SupportSpotsGenerator::Issues issues = SupportSpotsGenerator::full_search(this, params); auto obj_transform = this->trafo_centered(); for (ModelVolume *model_volume : this->model_object()->volumes) { diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index 518c8cacc3..c7fd7e882c 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -364,6 +364,8 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, return Vec3f(point.x(), point.y(), layer_z); }; float overhang_dist = tan(params.overhang_angle_deg * PI / 180.0f)*layer_region->layer()->height; + float min_malformation_dist = tan(params.malformation_angle_span_deg.first * PI / 180.0f)*layer_region->layer()->height; + float max_malformation_dist = tan(params.malformation_angle_span_deg.second * PI / 180.0f)*layer_region->layer()->height; Points points { }; entity->collect_points(points); @@ -387,8 +389,7 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, ExtrusionPropertiesAccumulator bridging_acc { }; ExtrusionPropertiesAccumulator malformation_acc { }; - bridging_acc.add_distance(params.bridge_distance + 1.0f); // Initialise unsupported distance with larger than tolerable distance -> - // -> it prevents extruding perimeter starts and short loops into air. + bridging_acc.add_distance(params.bridge_distance); const float flow_width = get_flow_width(layer_region, entity->role()); for (size_t line_idx = 0; line_idx < lines.size(); ++line_idx) { @@ -430,7 +431,7 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, const ExtrusionLine &nearest_line = prev_layer_lines.get_line(nearest_line_idx); current_line.malformation += 0.9 * nearest_line.malformation; } - if (dist_from_prev_layer > overhang_dist) { + if (dist_from_prev_layer > min_malformation_dist && dist_from_prev_layer < max_malformation_dist) { malformation_acc.add_distance(current_line.len); current_line.malformation += layer_region->layer()->height * (0.5f + 1.5f * (malformation_acc.max_curvature / PI) * gauss(malformation_acc.distance, 5.0f, 1.0f, 0.2f)); @@ -652,6 +653,10 @@ public: island.sticking_second_moment_of_area_covariance_accumulator; } + float get_volume() const { + return volume; + } + void add(const ObjectPart &other) { this->volume_centroid_accumulator += other.volume_centroid_accumulator; this->volume += other.volume; diff --git a/src/libslic3r/SupportSpotsGenerator.hpp b/src/libslic3r/SupportSpotsGenerator.hpp index b139ae0980..a88b7a7be0 100644 --- a/src/libslic3r/SupportSpotsGenerator.hpp +++ b/src/libslic3r/SupportSpotsGenerator.hpp @@ -9,15 +9,16 @@ namespace Slic3r { namespace SupportSpotsGenerator { struct Params { - Params(float overhang_angle_deg, const std::vector& filament_types) : overhang_angle_deg(overhang_angle_deg) - { + Params(const std::vector &filament_types) { if (filament_types.size() > 1) { - BOOST_LOG_TRIVIAL(warning) << "SupportSpotsGenerator does not currently handle different materials properly, only first will be used"; + BOOST_LOG_TRIVIAL(warning) + << "SupportSpotsGenerator does not currently handle different materials properly, only first will be used"; } if (filament_types.empty() || filament_types[0].empty()) { - BOOST_LOG_TRIVIAL(error) << "SupportSpotsGenerator error: empty filament_type"; + BOOST_LOG_TRIVIAL(error) + << "SupportSpotsGenerator error: empty filament_type"; filament_type = std::string("PLA"); - }else { + } else { filament_type = filament_types[0]; } } @@ -25,22 +26,23 @@ struct Params { // the algorithm should use the following units for all computations: distance [mm], mass [g], time [s], force [g*mm/s^2] const float bridge_distance = 15.0f; //mm const float bridge_distance_decrease_by_curvature_factor = 5.0f; // allowed bridge distance = bridge_distance / (this factor * (curvature / PI) ) - const float overhang_angle_deg = 50.0f; + const float overhang_angle_deg = 80.0f; + const std::pair malformation_angle_span_deg = std::pair { 45.0f, 80.0f }; const float min_distance_between_support_points = 3.0f; //mm const float support_points_interface_radius = 1.5f; // mm std::string filament_type; const float gravity_constant = 9806.65f; // mm/s^2; gravity acceleration on Earth's surface, algorithm assumes that printer is in upwards position. - const float max_acceleration = 9*1000.0f; // mm/s^2 ; max acceleration of object (bed) in XY (NOTE: The max hit is received by the object in the jerk phase, so the usual machine limits are too low) - const float filament_density = 1.25e-3f ; // g/mm^3 ; Common filaments are very lightweight, so precise number is not that important + const float max_acceleration = 9 * 1000.0f; // mm/s^2 ; max acceleration of object (bed) in XY (NOTE: The max hit is received by the object in the jerk phase, so the usual machine limits are too low) + const float filament_density = 1.25e-3f; // g/mm^3 ; Common filaments are very lightweight, so precise number is not that important const float material_yield_strength = 33.0f * 1e6f; // (g*mm/s^2)/mm^2; 33 MPa is yield strength of ABS, which has the lowest yield strength from common materials. const float standard_extruder_conflict_force = 20.0f * gravity_constant; // force that can occasionally push the model due to various factors (filament leaks, small curling, ... ); const float malformations_additive_conflict_extruder_force = 300.0f * gravity_constant; // for areas with possible high layered curled filaments // MPa * 1e^6 = (g*mm/s^2)/mm^2 = g/(mm*s^2); yield strength of the bed surface float get_bed_adhesion_yield_strength() const { - if (filament_type == "PLA"){ + if (filament_type == "PLA") { return 0.018f * 1e6f; } else if (filament_type == "PET" || filament_type == "PETG") { return 0.3f * 1e6f; @@ -51,7 +53,7 @@ struct Params { }; struct SupportPoint { - SupportPoint(const Vec3f &position, float force, const Vec3f& direction); + SupportPoint(const Vec3f &position, float force, const Vec3f &direction); Vec3f position; float force; Vec3f direction; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index c193dbf908..2ed7ae6792 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -332,6 +332,7 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l } update_model_object(); + this->waiting_for_autogenerated_supports = false; m_parent.set_as_dirty(); } @@ -379,6 +380,7 @@ void GLGizmoFdmSupports::select_facets_by_angle(float threshold_deg, bool block) Plater::TakeSnapshot snapshot(wxGetApp().plater(), block ? _L("Block supports by angle") : _L("Add supports by angle")); update_model_object(); + this->waiting_for_autogenerated_supports = false; m_parent.set_as_dirty(); } From 15d0c55d54121d02409ff861025ca7af8e4760e7 Mon Sep 17 00:00:00 2001 From: Pavel Mikus Date: Fri, 19 Aug 2022 14:21:36 +0200 Subject: [PATCH 71/78] improve GUI responsivenes, turn off debug info --- src/libslic3r/SupportSpotsGenerator.cpp | 4 ++-- src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index c7fd7e882c..eced07b2c2 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -15,8 +15,8 @@ #include "libslic3r/ClipperUtils.hpp" #include "Geometry/ConvexHull.hpp" -#define DETAILED_DEBUG_LOGS -#define DEBUG_FILES +//#define DETAILED_DEBUG_LOGS +//#define DEBUG_FILES #ifdef DEBUG_FILES #include diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 2ed7ae6792..513a655ec0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -393,6 +393,8 @@ void GLGizmoFdmSupports::data_changed() ModelObject* mo = m_c->selection_info()->model_object(); if (mo && this->waiting_for_autogenerated_supports) { get_data_from_backend(); + } else { + this->waiting_for_autogenerated_supports = false; } } From f17e3f2c8b723a6b329f6dd8dbdfe4442a466b54 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Tue, 23 Aug 2022 14:46:08 +0200 Subject: [PATCH 72/78] Added support for ignoring of tiny extrusion drops which are usually not worth the supports. However, it is disabled, as it can currently result in unsupported large columns --- src/libslic3r/SupportSpotsGenerator.cpp | 83 ++++++++++++++----------- src/libslic3r/SupportSpotsGenerator.hpp | 7 ++- 2 files changed, 50 insertions(+), 40 deletions(-) diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index eced07b2c2..a405031681 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -41,7 +41,7 @@ public: bool is_external_perimeter() const { assert(origin_entity != nullptr); - return origin_entity->role() == erExternalPerimeter; + return origin_entity->role() == erExternalPerimeter || origin_entity->role() == erOverhangPerimeter; } Vec2f a; @@ -363,9 +363,11 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, const auto to_vec3f = [layer_z](const Vec2f &point) { return Vec3f(point.x(), point.y(), layer_z); }; - float overhang_dist = tan(params.overhang_angle_deg * PI / 180.0f)*layer_region->layer()->height; - float min_malformation_dist = tan(params.malformation_angle_span_deg.first * PI / 180.0f)*layer_region->layer()->height; - float max_malformation_dist = tan(params.malformation_angle_span_deg.second * PI / 180.0f)*layer_region->layer()->height; + float overhang_dist = tan(params.overhang_angle_deg * PI / 180.0f) * layer_region->layer()->height; + float min_malformation_dist = tan(params.malformation_angle_span_deg.first * PI / 180.0f) + * layer_region->layer()->height; + float max_malformation_dist = tan(params.malformation_angle_span_deg.second * PI / 180.0f) + * layer_region->layer()->height; Points points { }; entity->collect_points(points); @@ -387,9 +389,14 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, } } + if (entity->total_volume() < params.supportable_volume_threshold) { + checked_lines_out.insert(checked_lines_out.end(), lines.begin(), lines.end()); + return; + } + ExtrusionPropertiesAccumulator bridging_acc { }; ExtrusionPropertiesAccumulator malformation_acc { }; - bridging_acc.add_distance(params.bridge_distance); + bridging_acc.add_distance(params.bridge_distance + 1.0f); const float flow_width = get_flow_width(layer_region, entity->role()); for (size_t line_idx = 0; line_idx < lines.size(); ++line_idx) { @@ -416,7 +423,7 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, bool in_layer_dist_condition = bridging_acc.distance > params.bridge_distance / (1.0f + (bridging_acc.max_curvature * params.bridge_distance_decrease_by_curvature_factor / PI)); - bool between_layers_condition = fabs(dist_from_prev_layer) > 3.0f*flow_width || + bool between_layers_condition = fabs(dist_from_prev_layer) > 3.0f * flow_width || prev_layer_lines.get_line(nearest_line_idx).malformation > 3.0f * layer_region->layer()->height; if (in_layer_dist_condition && between_layers_condition) { @@ -427,14 +434,17 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, } //malformation - if (fabs(dist_from_prev_layer) < 3.0f*flow_width) { + if (fabs(dist_from_prev_layer) < 3.0f * flow_width) { const ExtrusionLine &nearest_line = prev_layer_lines.get_line(nearest_line_idx); current_line.malformation += 0.9 * nearest_line.malformation; } if (dist_from_prev_layer > min_malformation_dist && dist_from_prev_layer < max_malformation_dist) { malformation_acc.add_distance(current_line.len); - current_line.malformation += layer_region->layer()->height * (0.5f + - 1.5f * (malformation_acc.max_curvature / PI) * gauss(malformation_acc.distance, 5.0f, 1.0f, 0.2f)); + current_line.malformation += layer_region->layer()->height + * (0.5f + + + 1.5f * (malformation_acc.max_curvature / PI) + * gauss(malformation_acc.distance, 5.0f, 1.0f, 0.2f)); } else { malformation_acc.reset(); } @@ -464,12 +474,13 @@ std::tuple reckon_islands( } } - std::vector islands; // these search trees will be used to determine to which island does the extrusion begin - std::vector> island_extrusions; //final assigment of each extrusion to an island + std::vector islands; // these search trees will be used to determine to which island does the extrusion belong. + std::vector> island_extrusions; //final assigment of each extrusion to an island. // initliaze the search from external perimeters - at the beginning, there is island candidate for each external perimeter. // some of them will disappear (e.g. holes) for (size_t e = 0; e < extrusions.size(); ++e) { - if (layer_lines[extrusions[e].first].is_external_perimeter()) { + if (layer_lines[extrusions[e].first].origin_entity->is_loop() && + layer_lines[extrusions[e].first].is_external_perimeter()) { std::vector copy(extrusions[e].second - extrusions[e].first); for (size_t ex_line_idx = extrusions[e].first; ex_line_idx < extrusions[e].second; ++ex_line_idx) { copy[ex_line_idx - extrusions[e].first] = layer_lines[ex_line_idx]; @@ -478,8 +489,8 @@ std::tuple reckon_islands( island_extrusions.push_back( { e }); } } - // backup code if islands not found - this can currently happen, as external perimeters may be also pure overhang perimeters, and there is no - // way to distinguish external extrusions with total certainty. + + // backup code if islands not found // If that happens, just make the first extrusion into island - it may be wrong, but it won't crash. if (islands.empty() && !extrusions.empty()) { std::vector copy(extrusions[0].second - extrusions[0].first); @@ -492,9 +503,13 @@ std::tuple reckon_islands( // assign non external extrusions to islands for (size_t e = 0; e < extrusions.size(); ++e) { - if (!layer_lines[extrusions[e].first].is_external_perimeter()) { + if (!layer_lines[extrusions[e].first].origin_entity->is_loop() || + !layer_lines[extrusions[e].first].is_external_perimeter()) { bool island_assigned = false; for (size_t i = 0; i < islands.size(); ++i) { + if (island_extrusions[i].empty()) { + continue; + } size_t _idx = 0; Vec2f _pt = Vec2f::Zero(); if (islands[i].signed_distance_from_lines(layer_lines[extrusions[e].first].a, _idx, _pt) < 0) { @@ -509,24 +524,6 @@ std::tuple reckon_islands( } } } - // merge islands which are embedded within each other (mainly holes) - for (size_t i = 0; i < islands.size(); ++i) { - if (islands[i].get_lines().empty()) { - continue; - } - for (size_t j = 0; j < islands.size(); ++j) { - if (islands[j].get_lines().empty() || i == j) { - continue; - } - size_t _idx; - Vec2f _pt; - if (islands[i].signed_distance_from_lines(islands[j].get_line(0).a, _idx, _pt) < 0) { - island_extrusions[i].insert(island_extrusions[i].end(), island_extrusions[j].begin(), - island_extrusions[j].end()); - island_extrusions[j].clear(); - } - } - } float flow_width = get_flow_width(layer->regions()[0], erExternalPerimeter); // after filtering the layer lines into islands, build the result LayerIslands structure. @@ -559,7 +556,8 @@ std::tuple reckon_islands( // Bottom infill lines can be quite long, and algined, so the middle approximaton used above does not work Vec2f dir = (line.b - line.a).normalized(); float segment_length = flow_width; // segments of size flow_width - for (float segment_middle_dist = std::min(line.len, segment_length * 0.5f); segment_middle_dist < line.len; + for (float segment_middle_dist = std::min(line.len, segment_length * 0.5f); + segment_middle_dist < line.len; segment_middle_dist += segment_length) { Vec2f segment_middle = line.a + segment_middle_dist * dir; island.sticking_second_moment_of_area_accumulator += segment_length * flow_width @@ -723,7 +721,8 @@ public: } Vec3f centroid = centroid_accumulator / area; float extreme_fiber_dist = line_alg::distance_to( - Linef(centroid.head<2>().cast(), (centroid.head<2>() + Vec2f(line_dir.y(), -line_dir.x())).cast()), + Linef(centroid.head<2>().cast(), + (centroid.head<2>() + Vec2f(line_dir.y(), -line_dir.x())).cast()), extreme_point.head<2>().cast()); float elastic_section_modulus = area * directional_xy_variance / extreme_fiber_dist; @@ -760,7 +759,7 @@ public: return 1.0f; Vec3f bed_centroid = this->sticking_centroid_accumulator / this->sticking_area; - float bed_yield_torque = - compute_elastic_section_modulus( + float bed_yield_torque = -compute_elastic_section_modulus( line_dir, extreme_point, this->sticking_centroid_accumulator, @@ -771,7 +770,8 @@ public: Vec2f bed_weight_arm = (mass_centroid.head<2>() - bed_centroid.head<2>()); float bed_weight_arm_len = bed_weight_arm.norm(); - float bed_weight_dir_xy_variance = compute_directional_xy_variance(bed_weight_arm, this->sticking_centroid_accumulator, + float bed_weight_dir_xy_variance = compute_directional_xy_variance(bed_weight_arm, + this->sticking_centroid_accumulator, this->sticking_second_moment_of_area_accumulator, this->sticking_second_moment_of_area_covariance_accumulator, this->sticking_area); @@ -1019,6 +1019,13 @@ Issues check_global_stability(SupportGridFilter supports_presence_grid, for (size_t island_idx = 0; island_idx < islands_graph[layer_idx].islands.size(); ++island_idx) { const Island &island = islands_graph[layer_idx].islands[island_idx]; ObjectPart &part = active_object_parts.access(prev_island_to_object_part_mapping[island_idx]); + + //skip small drops of material - if they grow in size, they will be supported in next layers; + // if they dont grow, they are not worthy + if (part.get_volume() < params.supportable_volume_threshold) { + continue; + } + IslandConnection &weakest_conn = prev_island_weakest_connection[island_idx]; #ifdef DETAILED_DEBUG_LOGS weakest_conn.print_info("weakest connection info: "); @@ -1132,7 +1139,7 @@ std::tuple> check_extrusions_and_build_graph(c } for (const auto &line : layer_lines) { if (line.malformation > 0.0f) { - Vec3f color = value_to_rgbf(0, 1.0f, line.malformation); + Vec3f color = value_to_rgbf(-EPSILON, 1.0f, line.malformation); fprintf(malform_f, "v %f %f %f %f %f %f\n", line.b[0], line.b[1], layer->slice_z, color[0], color[1], color[2]); } diff --git a/src/libslic3r/SupportSpotsGenerator.hpp b/src/libslic3r/SupportSpotsGenerator.hpp index a88b7a7be0..b059755e38 100644 --- a/src/libslic3r/SupportSpotsGenerator.hpp +++ b/src/libslic3r/SupportSpotsGenerator.hpp @@ -24,14 +24,17 @@ struct Params { } // the algorithm should use the following units for all computations: distance [mm], mass [g], time [s], force [g*mm/s^2] - const float bridge_distance = 15.0f; //mm - const float bridge_distance_decrease_by_curvature_factor = 5.0f; // allowed bridge distance = bridge_distance / (this factor * (curvature / PI) ) + const float bridge_distance = 12.0f; //mm + const float bridge_distance_decrease_by_curvature_factor = 5.0f; // allowed bridge distance = bridge_distance / (1 + this factor * (curvature / PI) ) const float overhang_angle_deg = 80.0f; const std::pair malformation_angle_span_deg = std::pair { 45.0f, 80.0f }; const float min_distance_between_support_points = 3.0f; //mm const float support_points_interface_radius = 1.5f; // mm + // NOTE: Currently disabled, does not work correctly due to inability of the algorithm to correctly detect islands at each layer + const float supportable_volume_threshold = 0.0f; // mm^3 + std::string filament_type; const float gravity_constant = 9806.65f; // mm/s^2; gravity acceleration on Earth's surface, algorithm assumes that printer is in upwards position. const float max_acceleration = 9 * 1000.0f; // mm/s^2 ; max acceleration of object (bed) in XY (NOTE: The max hit is received by the object in the jerk phase, so the usual machine limits are too low) From 905c602995979f3f775b7a359abc4d01de2e5de3 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Tue, 6 Sep 2022 12:23:42 +0200 Subject: [PATCH 73/78] remove underscore from varaibles, its not C++ friendly practice --- .clang-format | 2 +- src/libslic3r/SupportSpotsGenerator.cpp | 22 +++++++++++----------- src/libslic3r/SupportSpotsGenerator.hpp | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.clang-format b/.clang-format index 440c89ec57..6ec205af84 100644 --- a/.clang-format +++ b/.clang-format @@ -46,7 +46,7 @@ BreakConstructorInitializersBeforeComma: false BreakConstructorInitializers: BeforeComma BreakAfterJavaFieldAnnotations: false BreakStringLiterals: true -ColumnLimit: 78 +ColumnLimit: 140 CommentPragmas: '^ IWYU pragma:' CompactNamespaces: true ConstructorInitializerAllOnOneLineOrOnePerLine: true diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index a405031681..ce071a766c 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -31,8 +31,8 @@ public: ExtrusionLine() : a(Vec2f::Zero()), b(Vec2f::Zero()), len(0.0f), origin_entity(nullptr) { } - ExtrusionLine(const Vec2f &_a, const Vec2f &_b, const ExtrusionEntity *origin_entity) : - a(_a), b(_b), len((_a - _b).norm()), origin_entity(origin_entity) { + ExtrusionLine(const Vec2f &a, const Vec2f &b, const ExtrusionEntity *origin_entity) : + a(a), b(b), len((a - b).norm()), origin_entity(origin_entity) { } float length() { @@ -510,9 +510,9 @@ std::tuple reckon_islands( if (island_extrusions[i].empty()) { continue; } - size_t _idx = 0; - Vec2f _pt = Vec2f::Zero(); - if (islands[i].signed_distance_from_lines(layer_lines[extrusions[e].first].a, _idx, _pt) < 0) { + size_t idx = 0; + Vec2f pt = Vec2f::Zero(); + if (islands[i].signed_distance_from_lines(layer_lines[extrusions[e].first].a, idx, pt) < 0) { island_extrusions[i].push_back(e); island_assigned = true; break; @@ -579,7 +579,7 @@ std::tuple reckon_islands( result.islands.push_back(island); } - //LayerIslands structure built. Now determine connections and their areas to the previous layer using raterization. + //LayerIslands structure built. Now determine connections and their areas to the previous layer using rasterization. PixelGrid current_layer_grid = prev_layer_grid; current_layer_grid.clear(); // build index image of current layer @@ -1040,10 +1040,10 @@ Issues check_global_stability(SupportGridFilter supports_presence_grid, } else { unchecked_dist = line.len; Vec2f target_point; - size_t _idx; + size_t idx; Vec3f pivot_site_search_point = to_3d(Vec2f(line.b + (line.b - line.a).normalized() * 300.0f), layer_z); - island_lines_dist.signed_distance_from_lines(pivot_site_search_point.head<2>(), _idx, + island_lines_dist.signed_distance_from_lines(pivot_site_search_point.head<2>(), idx, target_point); Vec3f support_point = to_3d(target_point, layer_z); auto force = part.is_stable_while_extruding(weakest_conn, line, support_point, layer_z, params); @@ -1239,9 +1239,9 @@ void debug_export(Issues issues, std::string file_name) { } #endif -std::vector quick_search(const PrintObject *po, const Params ¶ms) { - return {}; -} +// std::vector quick_search(const PrintObject *po, const Params ¶ms) { +// return {}; +// } Issues full_search(const PrintObject *po, const Params ¶ms) { auto [local_issues, graph] = check_extrusions_and_build_graph(po, params); diff --git a/src/libslic3r/SupportSpotsGenerator.hpp b/src/libslic3r/SupportSpotsGenerator.hpp index b059755e38..b991b4f6b1 100644 --- a/src/libslic3r/SupportSpotsGenerator.hpp +++ b/src/libslic3r/SupportSpotsGenerator.hpp @@ -66,7 +66,7 @@ struct Issues { std::vector support_points; }; -std::vector quick_search(const PrintObject *po, const Params ¶ms); +// std::vector quick_search(const PrintObject *po, const Params ¶ms); Issues full_search(const PrintObject *po, const Params ¶ms); } From 8a1a31992ae9678716d482577fad300ab6dd6276 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Tue, 6 Sep 2022 16:29:17 +0200 Subject: [PATCH 74/78] use Polyline instead of Points, so that there are no duplicate points --- src/libslic3r/SupportSpotsGenerator.cpp | 110 +++++++++++------------- src/libslic3r/SupportSpotsGenerator.hpp | 7 +- 2 files changed, 58 insertions(+), 59 deletions(-) diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index ce071a766c..f976bac1ac 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -1,5 +1,6 @@ #include "SupportSpotsGenerator.hpp" +#include "ExtrusionEntity.hpp" #include "tbb/parallel_for.h" #include "tbb/blocked_range.h" #include "tbb/blocked_range2d.h" @@ -346,6 +347,41 @@ float gauss(float value, float mean_x_coord, float mean_value, float falloff_spe return mean_value * (std::exp(exponent) - 1.0f) / (std::exp(1.0f) - 1.0f); } +void push_lines(const ExtrusionEntity *e, std::vector& destination) +{ + assert(!e->is_collection()); + Polyline pl = e->as_polyline(); + for (int point_idx = 0; point_idx < int(pl.points.size() - 1); ++point_idx) { + Vec2f start = unscaled(pl.points[point_idx]).cast(); + Vec2f next = unscaled(pl.points[point_idx + 1]).cast(); + ExtrusionLine line{start, next, e}; + destination.push_back(line); + } +} + +std::vector to_short_lines(const ExtrusionEntity *e, float length_limit) +{ + assert(!e->is_collection()); + Polyline pl = e->as_polyline(); + std::vector lines; + lines.reserve(pl.points.size() * 1.5f); + for (int point_idx = 0; point_idx < int(pl.points.size() - 1); ++point_idx) { + Vec2f start = unscaled(pl.points[point_idx]).cast(); + Vec2f next = unscaled(pl.points[point_idx + 1]).cast(); + Vec2f v = next - start; // vector from next to current + float dist_to_next = v.norm(); + v.normalize(); + int lines_count = int(std::ceil(dist_to_next / length_limit)); + float step_size = dist_to_next / lines_count; + for (int i = 0; i < lines_count; ++i) { + Vec2f a(start + v * (i * step_size)); + Vec2f b(start + v * ((i + 1) * step_size)); + lines.emplace_back(a, b, e); + } + } + return lines; +} + void check_extrusion_entity_stability(const ExtrusionEntity *entity, std::vector &checked_lines_out, float layer_z, @@ -371,23 +407,8 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, Points points { }; entity->collect_points(points); - std::vector lines; - lines.reserve(points.size() * 1.5f); - lines.emplace_back(unscaled(points[0]).cast(), unscaled(points[0]).cast(), entity); - for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { - Vec2f start = unscaled(points[point_idx]).cast(); - Vec2f next = unscaled(points[point_idx + 1]).cast(); - Vec2f v = next - start; // vector from next to current - float dist_to_next = v.norm(); - v.normalize(); - int lines_count = int(std::ceil(dist_to_next / params.bridge_distance)); - float step_size = dist_to_next / lines_count; - for (int i = 0; i < lines_count; ++i) { - Vec2f a(start + v * (i * step_size)); - Vec2f b(start + v * ((i + 1) * step_size)); - lines.emplace_back(a, b, entity); - } - } + std::vector lines = to_short_lines(entity, params.bridge_distance); + if (lines.empty()) return; if (entity->total_volume() < params.supportable_volume_threshold) { checked_lines_out.insert(checked_lines_out.end(), lines.begin(), lines.end()); @@ -440,11 +461,9 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, } if (dist_from_prev_layer > min_malformation_dist && dist_from_prev_layer < max_malformation_dist) { malformation_acc.add_distance(current_line.len); - current_line.malformation += layer_region->layer()->height - * (0.5f - + - 1.5f * (malformation_acc.max_curvature / PI) - * gauss(malformation_acc.distance, 5.0f, 1.0f, 0.2f)); + current_line.malformation += layer_region->layer()->height * + (0.5f + 1.5f * (malformation_acc.max_curvature / PI) * + gauss(malformation_acc.distance, 5.0f, 1.0f, 0.2f)); } else { malformation_acc.reset(); } @@ -1049,18 +1068,20 @@ Issues check_global_stability(SupportGridFilter supports_presence_grid, auto force = part.is_stable_while_extruding(weakest_conn, line, support_point, layer_z, params); if (force > 0) { if (!supports_presence_grid.position_taken(support_point)) { - float area = params.support_points_interface_radius * params.support_points_interface_radius - * float(PI); + float area = std::min(float(unscaled(line.origin_entity->length())), + params.support_points_interface_radius * params.support_points_interface_radius + * float(PI)); + float altered_area = area * params.get_support_spots_adhesion_strength() / params.get_bed_adhesion_yield_strength(); part.add_support_point(support_point, area); issues.support_points.emplace_back(support_point, force, to_3d(Vec2f(line.b - line.a).normalized(), 0.0f)); supports_presence_grid.take_position(support_point); - weakest_conn.area += area; - weakest_conn.centroid_accumulator += support_point * area; - weakest_conn.second_moment_of_area_accumulator += area + weakest_conn.area += altered_area; + weakest_conn.centroid_accumulator += support_point * altered_area; + weakest_conn.second_moment_of_area_accumulator += altered_area * support_point.head<2>().cwiseProduct(support_point.head<2>()); - weakest_conn.second_moment_of_area_covariance_accumulator += area * support_point.x() + weakest_conn.second_moment_of_area_covariance_accumulator += altered_area * support_point.x() * support_point.y(); } } @@ -1090,32 +1111,12 @@ std::tuple> check_extrusions_and_build_graph(c for (const LayerRegion *layer_region : layer->regions()) { for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { - Points points { }; - perimeter->collect_points(points); - for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { - Vec2f start = unscaled(points[point_idx]).cast(); - Vec2f next = unscaled(points[point_idx + 1]).cast(); - ExtrusionLine line { start, next, perimeter }; - layer_lines.push_back(line); - } - if (perimeter->is_loop()) { - Vec2f start = unscaled(points[points.size() - 1]).cast(); - Vec2f next = unscaled(points[0]).cast(); - ExtrusionLine line { start, next, perimeter }; - layer_lines.push_back(line); - } + push_lines(perimeter, layer_lines); } // perimeter } // ex_entity for (const ExtrusionEntity *ex_entity : layer_region->fills.entities) { for (const ExtrusionEntity *fill : static_cast(ex_entity)->entities) { - Points points { }; - fill->collect_points(points); - for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { - Vec2f start = unscaled(points[point_idx]).cast(); - Vec2f next = unscaled(points[point_idx + 1]).cast(); - ExtrusionLine line { start, next, fill }; - layer_lines.push_back(line); - } + push_lines(fill, layer_lines); } // fill } // ex_entity } // region @@ -1165,14 +1166,7 @@ std::tuple> check_extrusions_and_build_graph(c check_extrusion_entity_stability(fill, layer_lines, layer->slice_z, layer_region, external_lines, issues, params); } else { - Points points { }; - fill->collect_points(points); - for (int point_idx = 0; point_idx < int(points.size() - 1); ++point_idx) { - Vec2f start = unscaled(points[point_idx]).cast(); - Vec2f next = unscaled(points[point_idx + 1]).cast(); - ExtrusionLine line { start, next, fill }; - layer_lines.push_back(line); - } + push_lines(fill, layer_lines); } } // fill } // ex_entity diff --git a/src/libslic3r/SupportSpotsGenerator.hpp b/src/libslic3r/SupportSpotsGenerator.hpp index b991b4f6b1..9a45c38d98 100644 --- a/src/libslic3r/SupportSpotsGenerator.hpp +++ b/src/libslic3r/SupportSpotsGenerator.hpp @@ -30,7 +30,7 @@ struct Params { const std::pair malformation_angle_span_deg = std::pair { 45.0f, 80.0f }; const float min_distance_between_support_points = 3.0f; //mm - const float support_points_interface_radius = 1.5f; // mm + const float support_points_interface_radius = 2.0f; // mm // NOTE: Currently disabled, does not work correctly due to inability of the algorithm to correctly detect islands at each layer const float supportable_volume_threshold = 0.0f; // mm^3 @@ -53,6 +53,11 @@ struct Params { return 0.018f * 1e6f; } } + + //just return PLA adhesion value as value for supports + float get_support_spots_adhesion_strength() const { + return 0.018f * 1e6f; + } }; struct SupportPoint { From a6a723928c6f5eda7b51b634fdf25cff64dac376 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Wed, 7 Sep 2022 17:11:58 +0200 Subject: [PATCH 75/78] create cradle around small parts, break tiny connections in the model graph, fix PETG support --- src/libslic3r/PrintObject.cpp | 2 +- src/libslic3r/SupportSpotsGenerator.cpp | 61 ++++++++++++------------- src/libslic3r/SupportSpotsGenerator.hpp | 27 +++++------ 3 files changed, 44 insertions(+), 46 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 71ec15e07d..f4b69bb812 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -435,7 +435,7 @@ void PrintObject::generate_support_spots() Vec3f point = Vec3f(inv_transform.cast() * support_point.position); Vec3f origin = Vec3f( inv_transform.cast() * Vec3f(support_point.position.x(), support_point.position.y(), 0.0f)); - selector.enforce_spot(point, origin, 1.5f); + selector.enforce_spot(point, origin, support_point.spot_radius); } model_volume->supported_facets.set(selector.selector); diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index f976bac1ac..0a6ab5e9da 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -16,8 +16,8 @@ #include "libslic3r/ClipperUtils.hpp" #include "Geometry/ConvexHull.hpp" -//#define DETAILED_DEBUG_LOGS -//#define DEBUG_FILES +// #define DETAILED_DEBUG_LOGS +// #define DEBUG_FILES #ifdef DEBUG_FILES #include @@ -66,8 +66,8 @@ auto get_b(ExtrusionLine &&l) { namespace SupportSpotsGenerator { -SupportPoint::SupportPoint(const Vec3f &position, float force, const Vec3f &direction) : - position(position), force(force), direction(direction) { +SupportPoint::SupportPoint(const Vec3f &position, float force, float spot_radius, const Vec3f &direction) : + position(position), force(force), spot_radius(spot_radius), direction(direction) { } class LinesDistancer { @@ -405,16 +405,9 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, float max_malformation_dist = tan(params.malformation_angle_span_deg.second * PI / 180.0f) * layer_region->layer()->height; - Points points { }; - entity->collect_points(points); std::vector lines = to_short_lines(entity, params.bridge_distance); if (lines.empty()) return; - if (entity->total_volume() < params.supportable_volume_threshold) { - checked_lines_out.insert(checked_lines_out.end(), lines.begin(), lines.end()); - return; - } - ExtrusionPropertiesAccumulator bridging_acc { }; ExtrusionPropertiesAccumulator malformation_acc { }; bridging_acc.add_distance(params.bridge_distance + 1.0f); @@ -429,6 +422,7 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, curr_angle = angle(v1, v2); } bridging_acc.add_angle(curr_angle); + // malformation in concave angles does not happen malformation_acc.add_angle(std::max(0.0f, curr_angle)); size_t nearest_line_idx; @@ -444,11 +438,11 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, bool in_layer_dist_condition = bridging_acc.distance > params.bridge_distance / (1.0f + (bridging_acc.max_curvature * params.bridge_distance_decrease_by_curvature_factor / PI)); - bool between_layers_condition = fabs(dist_from_prev_layer) > 3.0f * flow_width || + bool between_layers_condition = fabs(dist_from_prev_layer) > flow_width || prev_layer_lines.get_line(nearest_line_idx).malformation > 3.0f * layer_region->layer()->height; if (in_layer_dist_condition && between_layers_condition) { - issues.support_points.emplace_back(to_vec3f(current_line.b), 0.0f, Vec3f(0.f, 0.0f, -1.0f)); + issues.support_points.emplace_back(to_vec3f(current_line.b), 0.0f, params.support_points_interface_radius, Vec3f(0.f, 0.0f, -1.0f)); current_line.support_point_generated = true; bridging_acc.reset(); } @@ -562,7 +556,7 @@ std::tuple reckon_islands( for (size_t lidx = extrusions[extrusion_idx].first; lidx < extrusions[extrusion_idx].second; ++lidx) { line_to_island_mapping[lidx] = result.islands.size(); const ExtrusionLine &line = layer_lines[lidx]; - float volume = line.origin_entity->min_mm3_per_mm() * line.len; + float volume = line.len * layer->height * flow_width * PI / 4.0f; island.volume += volume; island.volume_centroid_accumulator += to_3d(Vec2f((line.a + line.b) / 2.0f), float(layer->slice_z)) * volume; @@ -632,6 +626,15 @@ std::tuple reckon_islands( } } + // filter out very small connection areas, they brake the graph building + for (Island &island : result.islands) { + std::vector conns_to_remove; + for (const auto &conn : island.connected_islands) { + if (conn.second.area < params.connections_min_considerable_area) { conns_to_remove.push_back(conn.first); } + } + for (size_t conn : conns_to_remove) { island.connected_islands.erase(conn); } + } + return {result, current_layer_grid}; } @@ -1039,12 +1042,6 @@ Issues check_global_stability(SupportGridFilter supports_presence_grid, const Island &island = islands_graph[layer_idx].islands[island_idx]; ObjectPart &part = active_object_parts.access(prev_island_to_object_part_mapping[island_idx]); - //skip small drops of material - if they grow in size, they will be supported in next layers; - // if they dont grow, they are not worthy - if (part.get_volume() < params.supportable_volume_threshold) { - continue; - } - IslandConnection &weakest_conn = prev_island_weakest_connection[island_idx]; #ifdef DETAILED_DEBUG_LOGS weakest_conn.print_info("weakest connection info: "); @@ -1068,21 +1065,21 @@ Issues check_global_stability(SupportGridFilter supports_presence_grid, auto force = part.is_stable_while_extruding(weakest_conn, line, support_point, layer_z, params); if (force > 0) { if (!supports_presence_grid.position_taken(support_point)) { - float area = std::min(float(unscaled(line.origin_entity->length())), - params.support_points_interface_radius * params.support_points_interface_radius - * float(PI)); - float altered_area = area * params.get_support_spots_adhesion_strength() / params.get_bed_adhesion_yield_strength(); - part.add_support_point(support_point, area); - issues.support_points.emplace_back(support_point, force, - to_3d(Vec2f(line.b - line.a).normalized(), 0.0f)); + float orig_area = params.support_points_interface_radius * params.support_points_interface_radius * float(PI); + // artifically lower the area for materials that have strong bed adhesion, as this adhesion does not apply for support points + float altered_area = orig_area * params.get_support_spots_adhesion_strength() / params.get_bed_adhesion_yield_strength(); + part.add_support_point(support_point, altered_area); + + float radius = part.get_volume() < params.small_parts_threshold ? params.small_parts_support_points_interface_radius : params.support_points_interface_radius; + issues.support_points.emplace_back(support_point, force, radius, to_3d(Vec2f(line.b - line.a).normalized(), 0.0f)); supports_presence_grid.take_position(support_point); weakest_conn.area += altered_area; weakest_conn.centroid_accumulator += support_point * altered_area; - weakest_conn.second_moment_of_area_accumulator += altered_area - * support_point.head<2>().cwiseProduct(support_point.head<2>()); - weakest_conn.second_moment_of_area_covariance_accumulator += altered_area * support_point.x() - * support_point.y(); + weakest_conn.second_moment_of_area_accumulator += altered_area * + support_point.head<2>().cwiseProduct(support_point.head<2>()); + weakest_conn.second_moment_of_area_covariance_accumulator += altered_area * support_point.x() * + support_point.y(); } } } @@ -1104,7 +1101,7 @@ std::tuple> check_extrusions_and_build_graph(c std::vector islands_graph; std::vector layer_lines; float flow_width = get_flow_width(po->layers()[po->layer_count() - 1]->regions()[0], erExternalPerimeter); - PixelGrid prev_layer_grid(po, flow_width); + PixelGrid prev_layer_grid(po, flow_width*2.0f); // PREPARE BASE LAYER const Layer *layer = po->layers()[0]; diff --git a/src/libslic3r/SupportSpotsGenerator.hpp b/src/libslic3r/SupportSpotsGenerator.hpp index 9a45c38d98..61a1bc5385 100644 --- a/src/libslic3r/SupportSpotsGenerator.hpp +++ b/src/libslic3r/SupportSpotsGenerator.hpp @@ -30,40 +30,41 @@ struct Params { const std::pair malformation_angle_span_deg = std::pair { 45.0f, 80.0f }; const float min_distance_between_support_points = 3.0f; //mm - const float support_points_interface_radius = 2.0f; // mm - - // NOTE: Currently disabled, does not work correctly due to inability of the algorithm to correctly detect islands at each layer - const float supportable_volume_threshold = 0.0f; // mm^3 + const float support_points_interface_radius = 1.5f; // mm + const float connections_min_considerable_area = 1.5f; //mm^2 + const float small_parts_threshold = 5.0f; //mm^3 + const float small_parts_support_points_interface_radius = 3.0f; // mm std::string filament_type; const float gravity_constant = 9806.65f; // mm/s^2; gravity acceleration on Earth's surface, algorithm assumes that printer is in upwards position. const float max_acceleration = 9 * 1000.0f; // mm/s^2 ; max acceleration of object (bed) in XY (NOTE: The max hit is received by the object in the jerk phase, so the usual machine limits are too low) - const float filament_density = 1.25e-3f; // g/mm^3 ; Common filaments are very lightweight, so precise number is not that important - const float material_yield_strength = 33.0f * 1e6f; // (g*mm/s^2)/mm^2; 33 MPa is yield strength of ABS, which has the lowest yield strength from common materials. + const double filament_density = 1.25e-3f; // g/mm^3 ; Common filaments are very lightweight, so precise number is not that important + const double material_yield_strength = 33.0f * 1e6f; // (g*mm/s^2)/mm^2; 33 MPa is yield strength of ABS, which has the lowest yield strength from common materials. const float standard_extruder_conflict_force = 20.0f * gravity_constant; // force that can occasionally push the model due to various factors (filament leaks, small curling, ... ); const float malformations_additive_conflict_extruder_force = 300.0f * gravity_constant; // for areas with possible high layered curled filaments // MPa * 1e^6 = (g*mm/s^2)/mm^2 = g/(mm*s^2); yield strength of the bed surface - float get_bed_adhesion_yield_strength() const { + double get_bed_adhesion_yield_strength() const { if (filament_type == "PLA") { - return 0.018f * 1e6f; + return 0.018 * 1e6; } else if (filament_type == "PET" || filament_type == "PETG") { - return 0.3f * 1e6f; + return 0.3 * 1e6; } else { //PLA default value - defensive approach, PLA has quite low adhesion - return 0.018f * 1e6f; + return 0.018 * 1e6; } } //just return PLA adhesion value as value for supports - float get_support_spots_adhesion_strength() const { - return 0.018f * 1e6f; + double get_support_spots_adhesion_strength() const { + return 0.018f * 1e6; } }; struct SupportPoint { - SupportPoint(const Vec3f &position, float force, const Vec3f &direction); + SupportPoint(const Vec3f &position, float force, float spot_radius, const Vec3f &direction); Vec3f position; float force; + float spot_radius; Vec3f direction; }; From 9e2a555f1bf6b2cac6e6388fe4d4b5e36f0340c9 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Wed, 7 Sep 2022 18:00:04 +0200 Subject: [PATCH 76/78] fix supporting of start and end of extrusion line --- src/libslic3r/SupportSpotsGenerator.cpp | 4 ++++ src/slic3r/GUI/ConfigManipulation.cpp | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/SupportSpotsGenerator.cpp b/src/libslic3r/SupportSpotsGenerator.cpp index 0a6ab5e9da..697518bd08 100644 --- a/src/libslic3r/SupportSpotsGenerator.cpp +++ b/src/libslic3r/SupportSpotsGenerator.cpp @@ -365,6 +365,7 @@ std::vector to_short_lines(const ExtrusionEntity *e, float length Polyline pl = e->as_polyline(); std::vector lines; lines.reserve(pl.points.size() * 1.5f); + lines.emplace_back(unscaled(pl.points[0]).cast(), unscaled(pl.points[0]).cast(), e); for (int point_idx = 0; point_idx < int(pl.points.size() - 1); ++point_idx) { Vec2f start = unscaled(pl.points[point_idx]).cast(); Vec2f next = unscaled(pl.points[point_idx + 1]).cast(); @@ -415,6 +416,9 @@ void check_extrusion_entity_stability(const ExtrusionEntity *entity, for (size_t line_idx = 0; line_idx < lines.size(); ++line_idx) { ExtrusionLine ¤t_line = lines[line_idx]; + if (line_idx + 1 == lines.size() && current_line.b != lines.begin()->a) { + bridging_acc.add_distance(params.bridge_distance + 1.0f); + } float curr_angle = 0; if (line_idx + 1 < lines.size()) { const Vec2f v1 = current_line.b - current_line.a; diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 9df53caeba..c571d1d2af 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -282,7 +282,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) "dont_support_bridges", "support_material_extrusion_width", "support_material_contact_distance", "support_material_xy_spacing" }) toggle_field(el, have_support_material); -// toggle_field("support_material_threshold", have_support_material_auto); + toggle_field("support_material_threshold", have_support_material_auto); toggle_field("support_material_bottom_contact_distance", have_support_material && ! have_support_soluble); toggle_field("support_material_closing_radius", have_support_material && support_material_style == smsSnug); From bee57e46d4f2b2330c6a159746b933a29515b69d Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Fri, 9 Sep 2022 11:18:14 +0200 Subject: [PATCH 77/78] remove old build fix for gcc --- src/slic3r/GUI/BonjourDialog.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/slic3r/GUI/BonjourDialog.hpp b/src/slic3r/GUI/BonjourDialog.hpp index 5bb61131d0..8bfc076c44 100644 --- a/src/slic3r/GUI/BonjourDialog.hpp +++ b/src/slic3r/GUI/BonjourDialog.hpp @@ -8,8 +8,6 @@ #include #include -#include -#include #include "libslic3r/PrintConfig.hpp" From 670629d883a8ee63894052ded7414055211244b1 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Fri, 9 Sep 2022 15:34:48 +0200 Subject: [PATCH 78/78] Fix compilation - missing include for boost string conv, set supports flags for object AFTER the dialog window and snapshot --- src/libslic3r/GCode/GCodeProcessor.cpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 8de1815ae3..ec57313f41 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -5,6 +5,7 @@ #include "libslic3r/format.hpp" #include "GCodeProcessor.hpp" +#include #include #include #include diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 513a655ec0..b30c29db1f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -503,9 +503,6 @@ void GLGizmoFdmSupports::auto_generate() return vol->type() != ModelVolumeType::MODEL_PART || vol->supported_facets.empty(); }); - mo->config.set("support_material", true); - mo->config.set("support_material_auto", false); - MessageDialog dlg(GUI::wxGetApp().plater(), _L("Autogeneration will erase all currently painted areas.") + "\n\n" + _L("Are you sure you want to do it?") + "\n", @@ -523,6 +520,8 @@ void GLGizmoFdmSupports::auto_generate() } } + mo->config.set("support_material", true); + mo->config.set("support_material_auto", false); this->waiting_for_autogenerated_supports = true; wxGetApp().CallAfter([this]() { reslice_FDM_supports(); }); }