void FffPolygonGenerator::processInsets(SliceMeshStorage& mesh, unsigned int layer_nr) { SliceLayer* layer = &mesh.layers[layer_nr]; if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::SURFACE) { int inset_count = mesh.getSettingAsCount("wall_line_count"); if (mesh.getSettingBoolean("magic_spiralize") && static_cast<int>(layer_nr) < mesh.getSettingAsCount("bottom_layers") && layer_nr % 2 == 1)//Add extra insets every 2 layers when spiralizing, this makes bottoms of cups watertight. inset_count += 5; int line_width_x = mesh.getSettingInMicrons("wall_line_width_x"); int line_width_0 = mesh.getSettingInMicrons("wall_line_width_0"); if (mesh.getSettingBoolean("alternate_extra_perimeter")) inset_count += layer_nr % 2; bool recompute_outline_based_on_outer_wall = mesh.getSettingBoolean("support_enable"); WallsComputation walls_computation(mesh.getSettingInMicrons("wall_0_inset"), line_width_0, line_width_x, inset_count, recompute_outline_based_on_outer_wall); walls_computation.generateInsets(layer); } if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL) { for (PolygonRef polyline : layer->openPolyLines) { Polygons segments; for (unsigned int point_idx = 1; point_idx < polyline.size(); point_idx++) { PolygonRef segment = segments.newPoly(); segment.add(polyline[point_idx-1]); segment.add(polyline[point_idx]); } } } }
void FffPolygonGenerator::processFuzzyWalls(SliceMeshStorage& mesh) { if (mesh.getSettingAsCount("wall_line_count") == 0) { return; } int64_t fuzziness = mesh.getSettingInMicrons("magic_fuzzy_skin_thickness"); int64_t avg_dist_between_points = mesh.getSettingInMicrons("magic_fuzzy_skin_point_dist"); int64_t min_dist_between_points = avg_dist_between_points * 3 / 4; // hardcoded: the point distance may vary between 3/4 and 5/4 the supplied value int64_t range_random_point_dist = avg_dist_between_points / 2; for (unsigned int layer_nr = 0; layer_nr < mesh.layers.size(); layer_nr++) { SliceLayer& layer = mesh.layers[layer_nr]; for (SliceLayerPart& part : layer.parts) { Polygons results; Polygons& skin = (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") == ESurfaceMode::SURFACE)? part.outline : part.insets[0]; for (PolygonRef poly : skin) { // generate points in between p0 and p1 PolygonRef result = results.newPoly(); int64_t dist_left_over = rand() % (min_dist_between_points / 2); // the distance to be traversed on the line before making the first new point Point* p0 = &poly.back(); for (Point& p1 : poly) { // 'a' is the (next) new point between p0 and p1 Point p0p1 = p1 - *p0; int64_t p0p1_size = vSize(p0p1); int64_t dist_last_point = dist_left_over + p0p1_size * 2; // so that p0p1_size - dist_last_point evaulates to dist_left_over - p0p1_size for (int64_t p0pa_dist = dist_left_over; p0pa_dist < p0p1_size; p0pa_dist += min_dist_between_points + rand() % range_random_point_dist) { int r = rand() % (fuzziness * 2) - fuzziness; Point perp_to_p0p1 = turn90CCW(p0p1); Point fuzz = normal(perp_to_p0p1, r); Point pa = *p0 + normal(p0p1, p0pa_dist) + fuzz; result.add(pa); dist_last_point = p0pa_dist; } dist_left_over = p0p1_size - dist_last_point; p0 = &p1; } while (result.size() < 3 ) { unsigned int point_idx = poly.size() - 2; result.add(poly[point_idx]); if (point_idx == 0) { break; } point_idx--; } if (result.size() < 3) { result.clear(); for (Point& p : poly) result.add(p); } } skin = results; } } }
void AreaSupport::generateSupportInterface(SliceDataStorage& storage, const SliceMeshStorage& mesh, std::vector<Polygons>& support_areas, const unsigned int layer_count) { const unsigned int roof_layer_count = round_divide(mesh.getSettingInMicrons("support_roof_height"), storage.getSettingInMicrons("layer_height")); const unsigned int bottom_layer_count = round_divide(mesh.getSettingInMicrons("support_bottom_height"), storage.getSettingInMicrons("layer_height")); const unsigned int z_distance_bottom = round_up_divide(mesh.getSettingInMicrons("support_bottom_distance"), storage.getSettingInMicrons("layer_height")); const unsigned int z_distance_top = round_up_divide(mesh.getSettingInMicrons("support_top_distance"), storage.getSettingInMicrons("layer_height")); const int skip_layer_count = std::max(1u, round_divide(mesh.getSettingInMicrons("support_interface_skip_height"), storage.getSettingInMicrons("layer_height"))); const int interface_line_width = storage.meshgroup->getExtruderTrain(storage.getSettingAsIndex("support_interface_extruder_nr"))->getSettingInMicrons("support_interface_line_width"); std::vector<SupportLayer>& supportLayers = storage.support.supportLayers; for (unsigned int layer_idx = 0; layer_idx < layer_count; layer_idx++) { SupportLayer& layer = supportLayers[layer_idx]; const unsigned int top_layer_idx_above = layer_idx + roof_layer_count + z_distance_top; const unsigned int bottom_layer_idx_below = std::max(0, int(layer_idx) - int(bottom_layer_count) - int(z_distance_bottom)); if (top_layer_idx_above < supportLayers.size()) { Polygons roofs; if (roof_layer_count > 0) { Polygons model; const unsigned int n_scans = std::max(1u, (roof_layer_count - 1) / skip_layer_count); const float z_skip = std::max(1.0f, float(roof_layer_count - 1) / float(n_scans)); for (float layer_idx_above = top_layer_idx_above; layer_idx_above > layer_idx + z_distance_top; layer_idx_above -= z_skip) { const Polygons outlines_above = mesh.layers[std::round(layer_idx_above)].getOutlines(); model = model.unionPolygons(outlines_above); } roofs = support_areas[layer_idx].intersection(model); } Polygons bottoms; if (bottom_layer_count > 0) { Polygons model; const unsigned int n_scans = std::max(1u, (bottom_layer_count - 1) / skip_layer_count); const float z_skip = std::max(1.0f, float(bottom_layer_count - 1) / float(n_scans)); for (float layer_idx_below = bottom_layer_idx_below; std::round(layer_idx_below) < (int)(layer_idx - z_distance_bottom); layer_idx_below += z_skip) { const Polygons outlines_below = mesh.layers[std::round(layer_idx_below)].getOutlines(); model = model.unionPolygons(outlines_below); } bottoms = support_areas[layer_idx].intersection(model); } // expand skin a bit so that we're sure it's not too thin to be printed. Polygons skin = roofs.unionPolygons(bottoms).offset(interface_line_width).intersection(support_areas[layer_idx]); skin.removeSmallAreas(1.0); layer.skin.add(skin); layer.supportAreas.add(support_areas[layer_idx].difference(layer.skin)); } else { layer.skin.add(support_areas[layer_idx]); } } }
void SubDivCube::precomputeOctree(SliceMeshStorage& mesh) { radius_addition = mesh.getSettingInMicrons("sub_div_rad_add"); double infill_angle = M_PI / 4.0; coord_t furthest_dist_from_origin = std::sqrt(square(mesh.getSettingInMicrons("machine_height")) + square(mesh.getSettingInMicrons("machine_depth") / 2) + square(mesh.getSettingInMicrons("machine_width") / 2)); coord_t max_side_length = furthest_dist_from_origin * 2; int curr_recursion_depth = 0; const int64_t infill_line_distance = mesh.getSettingInMicrons("infill_line_distance"); if (infill_line_distance > 0) { for (int64_t curr_side_length = infill_line_distance * 2; curr_side_length < max_side_length * 2; curr_side_length *= 2) { cube_properties_per_recursion_step.emplace_back(); CubeProperties& cube_properties_here = cube_properties_per_recursion_step.back(); cube_properties_here.side_length = curr_side_length; cube_properties_here.height = sqrt(3) * curr_side_length; cube_properties_here.square_height = sqrt(2) * curr_side_length; cube_properties_here.max_draw_z_diff = ONE_OVER_SQRT_3 * curr_side_length; cube_properties_here.max_line_offset = ONE_OVER_SQRT_6 * curr_side_length; curr_recursion_depth++; } } Point3 center(0, 0, 0); Point3Matrix tilt; // rotation matrix to get from axis aligned cubes to cubes standing on their tip // The Z axis is transformed to go in positive Y direction // // cross section in a horizontal plane horizontal plane showing // looking down at the origin O positive X and positive Y // Z . // /:\ Y . // / : \ ^ . // / : \ | . // / .O. \ | . // /.~' '~.\ O---->X . // X """"""""""" Y . tilt.matrix[0] = -ONE_OVER_SQRT_2; tilt.matrix[1] = ONE_OVER_SQRT_2; tilt.matrix[2] = 0; tilt.matrix[3] = -ONE_OVER_SQRT_6; tilt.matrix[4] = -ONE_OVER_SQRT_6; tilt.matrix[5] = SQRT_TWO_THIRD ; tilt.matrix[6] = ONE_OVER_SQRT_3; tilt.matrix[7] = ONE_OVER_SQRT_3; tilt.matrix[8] = ONE_OVER_SQRT_3; infill_rotation_matrix = PointMatrix(infill_angle); Point3Matrix infill_angle_mat(infill_rotation_matrix); rotation_matrix = infill_angle_mat.compose(tilt); mesh.base_subdiv_cube = new SubDivCube(mesh, center, curr_recursion_depth - 1); }
coord_t SkinInfillAreaComputation::getWallLineWidthX(const SliceDataStorage& storage, const SliceMeshStorage& mesh, int layer_nr) { coord_t wall_line_width_x = mesh.getSettingInMicrons("wall_line_width_x"); if (layer_nr == 0) { const ExtruderTrain& train_wall_x = *storage.meshgroup->getExtruderTrain(mesh.getSettingAsExtruderNr("wall_x_extruder_nr")); wall_line_width_x *= train_wall_x.getSettingAsRatio("initial_layer_line_width_factor"); } return wall_line_width_x; }
coord_t SkinInfillAreaComputation::getSkinLineWidth(const SliceDataStorage& storage, const SliceMeshStorage& mesh, int layer_nr) { coord_t skin_line_width = mesh.getSettingInMicrons("skin_line_width"); if (layer_nr == 0) { const ExtruderTrain& train_skin = *storage.meshgroup->getExtruderTrain(mesh.getSettingAsExtruderNr("top_bottom_extruder_nr")); skin_line_width *= train_skin.getSettingAsRatio("initial_layer_line_width_factor"); } return skin_line_width; }
void FffPolygonGenerator::processDerivedWallsSkinInfill(SliceMeshStorage& mesh, size_t total_layers) { // combine infill unsigned int combined_infill_layers = mesh.getSettingInMicrons("infill_sparse_thickness") / std::max(mesh.getSettingInMicrons("layer_height"), 1); //How many infill layers to combine to obtain the requested sparse thickness. combineInfillLayers(mesh,combined_infill_layers); // fuzzy skin if (mesh.getSettingBoolean("magic_fuzzy_skin_enabled")) { processFuzzyWalls(mesh); } }
void FffPolygonGenerator::processSkinsAndInfill(SliceMeshStorage& mesh, unsigned int layer_nr) { if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") == ESurfaceMode::SURFACE) { return; } int wall_line_count = mesh.getSettingAsCount("wall_line_count"); int skin_extrusion_width = mesh.getSettingInMicrons("skin_line_width"); int innermost_wall_extrusion_width = (wall_line_count == 1)? mesh.getSettingInMicrons("wall_line_width_0") : mesh.getSettingInMicrons("wall_line_width_x"); generateSkins(layer_nr, mesh, skin_extrusion_width, mesh.getSettingAsCount("bottom_layers"), mesh.getSettingAsCount("top_layers"), wall_line_count, innermost_wall_extrusion_width, mesh.getSettingAsCount("skin_outline_count"), mesh.getSettingBoolean("skin_no_small_gaps_heuristic")); if (mesh.getSettingInMicrons("infill_line_distance") > 0) { int infill_skin_overlap = 0; bool infill_is_dense = mesh.getSettingInMicrons("infill_line_distance") < mesh.getSettingInMicrons("infill_line_width") + 10; if (!infill_is_dense && mesh.getSettingAsFillMethod("infill_pattern") != EFillMethod::CONCENTRIC) { infill_skin_overlap = skin_extrusion_width / 2; } generateInfill(layer_nr, mesh, innermost_wall_extrusion_width, infill_skin_overlap, wall_line_count); } }
SkinInfillAreaComputation::SkinInfillAreaComputation(int layer_nr, const SliceDataStorage& storage, SliceMeshStorage& mesh, bool process_infill) : layer_nr(layer_nr) , mesh(mesh) , bottom_layer_count(mesh.getSettingAsCount("bottom_layers")) , top_layer_count(mesh.getSettingAsCount("top_layers")) , wall_line_count(mesh.getSettingAsCount("wall_line_count")) , skin_line_width(getSkinLineWidth(storage, mesh, layer_nr)) , wall_line_width_0(getWallLineWidth0(storage, mesh, layer_nr)) , wall_line_width_x(getWallLineWidthX(storage, mesh, layer_nr)) , innermost_wall_line_width((wall_line_count == 1) ? wall_line_width_0 : wall_line_width_x) , infill_skin_overlap(getInfillSkinOverlap(storage, mesh, layer_nr, innermost_wall_line_width)) , skin_inset_count(mesh.getSettingAsCount("skin_outline_count")) , no_small_gaps_heuristic(mesh.getSettingBoolean("skin_no_small_gaps_heuristic")) , process_infill(process_infill) , top_reference_wall_expansion(mesh.getSettingInMicrons("top_skin_preshrink")) , bottom_reference_wall_expansion(mesh.getSettingInMicrons("bottom_skin_preshrink")) , top_skin_expand_distance(mesh.getSettingInMicrons("top_skin_expand_distance")) , bottom_skin_expand_distance(mesh.getSettingInMicrons("bottom_skin_expand_distance")) , top_reference_wall_idx(getReferenceWallIdx(top_reference_wall_expansion)) , bottom_reference_wall_idx(getReferenceWallIdx(bottom_reference_wall_expansion)) { }
coord_t SkinInfillAreaComputation::getInfillSkinOverlap(const SliceDataStorage& storage, const SliceMeshStorage& mesh, int layer_nr, coord_t innermost_wall_line_width) { coord_t infill_skin_overlap = 0; { // compute infill_skin_overlap const ExtruderTrain& train_infill = *storage.meshgroup->getExtruderTrain(mesh.getSettingAsExtruderNr("infill_extruder_nr")); const coord_t infill_line_width_factor = (layer_nr == 0) ? train_infill.getSettingAsRatio("initial_layer_line_width_factor") : 1.0; const bool infill_is_dense = mesh.getSettingInMicrons("infill_line_distance") < mesh.getSettingInMicrons("infill_line_width") * infill_line_width_factor + 10; if (!infill_is_dense && mesh.getSettingAsFillMethod("infill_pattern") != EFillMethod::CONCENTRIC) { infill_skin_overlap = innermost_wall_line_width / 2; } } return infill_skin_overlap; }
void SkinInfillAreaComputation::generateInfillSupport(SliceMeshStorage& mesh) { const coord_t layer_height = mesh.getSettingInMicrons("layer_height"); const double support_angle = mesh.getSettingInAngleRadians("infill_support_angle"); const double tan_angle = tan(support_angle) - 0.01; //The X/Y component of the support angle. 0.01 to make 90 degrees work too. const coord_t max_dist_from_lower_layer = tan_angle * layer_height; //Maximum horizontal distance that can be bridged. for (int layer_idx = mesh.layers.size() - 2; layer_idx >= 0; layer_idx--) { SliceLayer& layer = mesh.layers[layer_idx]; SliceLayer& layer_above = mesh.layers[layer_idx + 1]; Polygons inside_above; Polygons infill_above; for (SliceLayerPart& part_above : layer_above.parts) { inside_above.add(part_above.infill_area); infill_above.add(part_above.getOwnInfillArea()); } for (SliceLayerPart& part : layer.parts) { const Polygons& infill_area = part.infill_area; if (infill_area.empty()) { continue; } const Polygons unsupported = infill_area.offset(-max_dist_from_lower_layer); const Polygons basic_overhang = unsupported.difference(inside_above); const Polygons overhang_extented = basic_overhang.offset(max_dist_from_lower_layer + 50); // +50 for easier joining with support from layer above const Polygons full_overhang = overhang_extented.difference(inside_above); const Polygons infill_support = infill_above.unionPolygons(full_overhang); part.infill_area_own = infill_support.intersection(part.getOwnInfillArea()); } } }
bool SubDivCube::isValidSubdivision(SliceMeshStorage& mesh, Point3& center, int64_t radius) { int64_t distance2 = 0; coord_t sphere_slice_radius2;//!< squared radius of bounding sphere slice on target layer bool inside_somewhere = false; bool outside_somewhere = false; int inside; double part_dist;//what percentage of the radius the target layer is away from the center along the z axis. 0 - 1 const coord_t layer_height = mesh.getSettingInMicrons("layer_height"); int bottom_layer = (center.z - radius) / layer_height; int top_layer = (center.z + radius) / layer_height; for (int test_layer = bottom_layer; test_layer <= top_layer; test_layer += 3) // steps of three. Low-hanging speed gain. { part_dist = (double)(test_layer * layer_height - center.z) / radius; sphere_slice_radius2 = radius * radius * (1.0 - (part_dist * part_dist)); Point loc(center.x, center.y); inside = distanceFromPointToMesh(mesh, test_layer, loc, &distance2); if (inside == 1) { inside_somewhere = true; } else { outside_somewhere = true; } if (outside_somewhere && inside_somewhere) { return true; } if ((inside != 2) && distance2 < sphere_slice_radius2) { return true; } } return false; }
bool SpaghettiInfillPathGenerator::processSpaghettiInfill(const SliceDataStorage& storage, const FffGcodeWriter& fff_gcode_writer, LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const int extruder_nr, const PathConfigStorage::MeshPathConfigs& mesh_config, const SliceLayerPart& part, int infill_line_distance, int infill_overlap, int infill_angle, const Point& infill_origin) { if (extruder_nr != mesh.getSettingAsExtruderNr("infill_extruder_nr")) { return false; } bool added_something = false; const GCodePathConfig& config = mesh_config.infill_config[0]; const EFillMethod pattern = mesh.getSettingAsFillMethod("infill_pattern"); const bool zig_zaggify_infill = mesh.getSettingBoolean("zig_zaggify_infill"); const bool connect_polygons = true; // spaghetti infill should have as least as possible travel moves const unsigned int infill_line_width = config.getLineWidth(); constexpr int infill_multiplier = 1; const int64_t infill_shift = 0; constexpr int wall_line_count = 0; const int64_t outline_offset = 0; const double layer_height_mm = (gcode_layer.getLayerNr() == 0) ? mesh.getSettingInMillimeters("layer_height_0") : mesh.getSettingInMillimeters("layer_height"); // For each part on this layer which is used to fill that part and parts below: for (const std::pair<Polygons, double>& filling_area : part.spaghetti_infill_volumes) { Polygons infill_lines; Polygons infill_polygons; const Polygons& area = filling_area.first; // Area of the top within which to move while extruding (might be empty if the spaghetti_inset was too large) const double total_volume = filling_area.second * mesh.getSettingAsRatio("spaghetti_flow") + mesh.getSettingInCubicMillimeters("spaghetti_infill_extra_volume"); // volume to be extruded if (total_volume <= 0.0) { continue; } // generate zigzag print head paths Polygons* perimeter_gaps_output = nullptr; const bool connected_zigzags = true; const bool use_endpieces = false; Infill infill_comp(pattern, zig_zaggify_infill, connect_polygons, area, outline_offset , infill_line_width, infill_line_distance, infill_overlap, infill_multiplier, infill_angle, gcode_layer.z, infill_shift, wall_line_count, infill_origin, perimeter_gaps_output, connected_zigzags, use_endpieces , mesh.getSettingInMicrons("cross_infill_pocket_size")); // cross_fill_patterns is only generated when spaghetti infill is not used, // so we pass nullptr here. infill_comp.generate(infill_polygons, infill_lines, nullptr, &mesh); // add paths to plan with a higher flow ratio in order to extrude the required amount. const coord_t total_length = infill_polygons.polygonLength() + infill_lines.polyLineLength(); if (total_length > 0) { // zigzag path generation actually generated paths // calculate the normal volume extruded when using the layer height and line width to calculate extrusion const double normal_volume = INT2MM(INT2MM(total_length * infill_line_width)) * layer_height_mm; assert(normal_volume > 0.0); const float flow_ratio = total_volume / normal_volume; assert(flow_ratio / mesh.getSettingAsRatio("spaghetti_flow") >= 0.9); assert(!std::isnan(flow_ratio) && !std::isinf(flow_ratio)); if (!infill_polygons.empty() || !infill_lines.empty()) { added_something = true; fff_gcode_writer.setExtruder_addPrime(storage, gcode_layer, extruder_nr); if (!infill_polygons.empty()) { constexpr bool force_comb_retract = false; gcode_layer.addTravel(infill_polygons[0][0], force_comb_retract); gcode_layer.addPolygonsByOptimizer(infill_polygons, config, nullptr, ZSeamConfig(), 0, false, flow_ratio); } const bool is_zigzag = mesh.getSettingBoolean("zig_zaggify_infill") || pattern == EFillMethod::ZIG_ZAG; const coord_t wipe_dist = is_zigzag ? 0 : -mesh.getSettingInMicrons("infill_wipe_dist"); const SpaceFillType line_type = is_zigzag ? SpaceFillType::Lines : SpaceFillType::PolyLines; gcode_layer.addLinesByOptimizer(infill_lines, config, line_type, false, wipe_dist, flow_ratio); } } else { // zigzag path generation couldn't generate paths, probably because the area was too small // generate small path near the middle of the filling area // note that we need a path with positive length because that is currently the only way to insert an extrusion in a layer plan constexpr int path_length = 10; Point middle = AABB(area).getMiddle(); if (!area.inside(middle)) { PolygonUtils::ensureInsideOrOutside(area, middle, infill_line_width / 2); } const double normal_volume = INT2MM(INT2MM(path_length * infill_line_width)) * layer_height_mm; const float flow_ratio = total_volume / normal_volume; gcode_layer.addTravel(middle); gcode_layer.addExtrusionMove(middle + Point(0, path_length), config, SpaceFillType::Lines, flow_ratio); } } return added_something; }
void SkinInfillAreaComputation::generateGradualInfill(SliceMeshStorage& mesh, unsigned int gradual_infill_step_height, unsigned int max_infill_steps) { // no early-out for this function; it needs to initialize the [infill_area_per_combine_per_density] float layer_skip_count = 8; // skip every so many layers as to ignore small gaps in the model making computation more easy if (!mesh.getSettingBoolean("skin_no_small_gaps_heuristic")) { layer_skip_count = 1; } unsigned int gradual_infill_step_layer_count = gradual_infill_step_height / mesh.getSettingInMicrons("layer_height"); // The difference in layer count between consecutive density infill areas // make gradual_infill_step_height divisable by layer_skip_count float n_skip_steps_per_gradual_step = std::max(1.0f, std::ceil(gradual_infill_step_layer_count / layer_skip_count)); // only decrease layer_skip_count to make it a divisor of gradual_infill_step_layer_count layer_skip_count = gradual_infill_step_layer_count / n_skip_steps_per_gradual_step; size_t min_layer = mesh.getSettingAsCount("bottom_layers"); size_t max_layer = mesh.layers.size() - 1 - mesh.getSettingAsCount("top_layers"); for (size_t layer_idx = 0; layer_idx < mesh.layers.size(); layer_idx++) { // loop also over layers which don't contain infill cause of bottom_ and top_layer to initialize their infill_area_per_combine_per_density SliceLayer& layer = mesh.layers[layer_idx]; for (SliceLayerPart& part : layer.parts) { assert(part.infill_area_per_combine_per_density.size() == 0 && "infill_area_per_combine_per_density is supposed to be uninitialized"); const Polygons& infill_area = part.getOwnInfillArea(); if (infill_area.size() == 0 || layer_idx < min_layer || layer_idx > max_layer) { // initialize infill_area_per_combine_per_density empty part.infill_area_per_combine_per_density.emplace_back(); // create a new infill_area_per_combine part.infill_area_per_combine_per_density.back().emplace_back(); // put empty infill area in the newly constructed infill_area_per_combine // note: no need to copy part.infill_area, cause it's the empty vector anyway continue; } Polygons less_dense_infill = infill_area; // one step less dense with each infill_step for (unsigned int infill_step = 0; infill_step < max_infill_steps; infill_step++) { size_t min_layer = layer_idx + infill_step * gradual_infill_step_layer_count + layer_skip_count; size_t max_layer = layer_idx + (infill_step + 1) * gradual_infill_step_layer_count; for (float upper_layer_idx = min_layer; static_cast<unsigned int>(upper_layer_idx) <= max_layer; upper_layer_idx += layer_skip_count) { if (static_cast<unsigned int>(upper_layer_idx) >= mesh.layers.size()) { less_dense_infill.clear(); break; } SliceLayer& upper_layer = mesh.layers[static_cast<unsigned int>(upper_layer_idx)]; Polygons relevent_upper_polygons; for (SliceLayerPart& upper_layer_part : upper_layer.parts) { if (!upper_layer_part.boundaryBox.hit(part.boundaryBox)) { continue; } relevent_upper_polygons.add(upper_layer_part.getOwnInfillArea()); } less_dense_infill = less_dense_infill.intersection(relevent_upper_polygons); } if (less_dense_infill.size() == 0) { break; } // add new infill_area_per_combine for the current density part.infill_area_per_combine_per_density.emplace_back(); std::vector<Polygons>& infill_area_per_combine_current_density = part.infill_area_per_combine_per_density.back(); const Polygons more_dense_infill = infill_area.difference(less_dense_infill); infill_area_per_combine_current_density.push_back(more_dense_infill); if (less_dense_infill.size() == 0) { break; } } part.infill_area_per_combine_per_density.emplace_back(); std::vector<Polygons>& infill_area_per_combine_current_density = part.infill_area_per_combine_per_density.back(); infill_area_per_combine_current_density.push_back(infill_area); part.infill_area_own = nullptr; // clear infill_area_own, it's not needed any more. assert(part.infill_area_per_combine_per_density.size() != 0 && "infill_area_per_combine_per_density is now initialized"); } } }