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::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); } }
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)) { }
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"); } } }
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); } }