diff --git a/include/FffGcodeWriter.h b/include/FffGcodeWriter.h index ac0a3ca274..26d85a6af4 100644 --- a/include/FffGcodeWriter.h +++ b/include/FffGcodeWriter.h @@ -501,7 +501,7 @@ class FffGcodeWriter : public NoCopy const SkinPart& skin_part) const; /*! - * Add the roofing which is the area inside the innermost skin inset which has air 'directly' above + * Add the roofing/flooring which is the area inside the innermost skin inset which has air 'directly' above or below * * \param[in] storage where the slice data is stored. * \param gcode_layer The initial planning of the gcode of the layer. @@ -511,13 +511,15 @@ class FffGcodeWriter : public NoCopy * \param skin_part The skin part for which to create gcode * \param[out] added_something Whether this function added anything to the layer plan */ - void processRoofing( + void processRoofingFlooring( const SliceDataStorage& storage, LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const size_t extruder_nr, - const MeshPathConfigs& mesh_config, - const SkinPart& skin_part, + const std::string& setting_prefix, + const Shape& fill, + const GCodePathConfig& config, + const std::vector& angles, bool& added_something) const; /*! diff --git a/include/InsetOrderOptimizer.h b/include/InsetOrderOptimizer.h index 80c0856f3d..2ab4e3454b 100644 --- a/include/InsetOrderOptimizer.h +++ b/include/InsetOrderOptimizer.h @@ -47,6 +47,8 @@ class InsetOrderOptimizer const GCodePathConfig& inset_X_default_config, const GCodePathConfig& inset_0_roofing_config, const GCodePathConfig& inset_X_roofing_config, + const GCodePathConfig& inset_0_flooring_config, + const GCodePathConfig& inset_X_flooring_config, const GCodePathConfig& inset_0_bridge_config, const GCodePathConfig& inset_X_bridge_config, const bool retract_before_outer_wall, @@ -101,6 +103,8 @@ class InsetOrderOptimizer const GCodePathConfig& inset_X_default_config_; const GCodePathConfig& inset_0_roofing_config_; const GCodePathConfig& inset_X_roofing_config_; + const GCodePathConfig& inset_0_flooring_config_; + const GCodePathConfig& inset_X_flooring_config_; const GCodePathConfig& inset_0_bridge_config_; const GCodePathConfig& inset_X_bridge_config_; const bool retract_before_outer_wall_; diff --git a/include/LayerPlan.h b/include/LayerPlan.h index 47cea1aa19..be59e66bb1 100644 --- a/include/LayerPlan.h +++ b/include/LayerPlan.h @@ -117,7 +117,8 @@ class LayerPlan : public NoCopy Shape bridge_wall_mask_; //!< The regions of a layer part that are not supported, used for bridging Shape overhang_mask_; //!< The regions of a layer part where the walls overhang Shape seam_overhang_mask_; //!< The regions of a layer part where the walls overhang, specifically as defined for the seam - Shape roofing_mask_; //!< The regions of a layer part where the walls are exposed to the air + Shape roofing_mask_; //!< The regions of a layer part where the walls are exposed to the air above + Shape flooring_mask_; //!< The regions of a layer part where the walls are exposed to the air below bool min_layer_time_used = false; //!< Wether or not the minimum layer time (cool_min_layer_time) was actually used in this layerplan. @@ -320,6 +321,13 @@ class LayerPlan : public NoCopy */ void setRoofingMask(const Shape& polys); + /*! + * Set flooring_mask. + * + * \param shape The areas of the part currently being processed that will require flooring. + */ + void setFlooringMask(const Shape& shape); + /*! * Travel to a certain point, with all of the procedures necessary to do so. * @@ -491,6 +499,7 @@ class LayerPlan : public NoCopy const Settings& settings, const GCodePathConfig& default_config, const GCodePathConfig& roofing_config, + const GCodePathConfig& flooring_config, const GCodePathConfig& bridge_config, double flow, const Ratio width_factor, @@ -520,6 +529,7 @@ class LayerPlan : public NoCopy const Settings& settings, const GCodePathConfig& default_config, const GCodePathConfig& roofing_config, + const GCodePathConfig& flooring_config, const GCodePathConfig& bridge_config, coord_t wall_0_wipe_dist, double flow_ratio, @@ -554,6 +564,7 @@ class LayerPlan : public NoCopy const Settings& settings, const GCodePathConfig& default_config, const GCodePathConfig& roofing_config, + const GCodePathConfig& flooring_config, const GCodePathConfig& bridge_config, coord_t wall_0_wipe_dist, double flow_ratio, @@ -592,6 +603,7 @@ class LayerPlan : public NoCopy const Settings& settings, const GCodePathConfig& default_config, const GCodePathConfig& roofing_config, + const GCodePathConfig& flooring_config, const GCodePathConfig& bridge_config, const ZSeamConfig& z_seam_config = ZSeamConfig(), coord_t wall_0_wipe_dist = 0, diff --git a/include/settings/MeshPathConfigs.h b/include/settings/MeshPathConfigs.h index 61c993b9a8..e6cf6ca2ad 100644 --- a/include/settings/MeshPathConfigs.h +++ b/include/settings/MeshPathConfigs.h @@ -17,6 +17,8 @@ struct MeshPathConfigs GCodePathConfig insetX_config{}; GCodePathConfig inset0_roofing_config{}; GCodePathConfig insetX_roofing_config{}; + GCodePathConfig inset0_flooring_config{}; + GCodePathConfig insetX_flooring_config{}; GCodePathConfig bridge_inset0_config{}; GCodePathConfig bridge_insetX_config{}; GCodePathConfig skin_config{}; @@ -24,6 +26,7 @@ struct MeshPathConfigs GCodePathConfig bridge_skin_config2{}; // used for second bridge layer GCodePathConfig bridge_skin_config3{}; // used for third bridge layer GCodePathConfig roofing_config{}; + GCodePathConfig flooring_config{}; std::vector infill_config{}; GCodePathConfig ironing_config{}; diff --git a/include/skin.h b/include/skin.h index 558ca59f64..7e73c13777 100644 --- a/include/skin.h +++ b/include/skin.h @@ -127,12 +127,12 @@ class SkinInfillAreaComputation void generateInfill(SliceLayerPart& part); /*! - * Remove the areas which are 'directly' under air from the \ref SkinPart::inner_infill and - * save them in the \ref SkinPart::roofing_fill of the \p part. + * Remove the areas which are 'directly' under/over air from the \ref SkinPart::inner_infill and + * save them in the \ref SkinPart::roofing_fill and \ref SkinPart::flooring_fill of the \p part. * - * \param[in,out] part Where to get the SkinParts to get the outline info from and to store the roofing areas + * \param[in,out] part Where to get the SkinParts to get the outline info from and to store the roofing/flooring areas */ - void generateRoofingFillAndSkinFill(SliceLayerPart& part); + void generateSkinRoofingFlooringFill(SliceLayerPart& part); /*! * Generate the top and bottom-most surfaces of the given \p part, i.e. the surfaces that have nothing above or below diff --git a/include/sliceDataStorage.h b/include/sliceDataStorage.h index cb35355b4a..590ce5297a 100644 --- a/include/sliceDataStorage.h +++ b/include/sliceDataStorage.h @@ -45,6 +45,7 @@ class SkinPart //!< roofing and non-roofing. Shape skin_fill; //!< The part of the skin which is not roofing. Shape roofing_fill; //!< The inner infill which has air directly above + Shape flooring_fill; //!< The inner infill which has air directly below }; /*! @@ -309,6 +310,7 @@ class SliceMeshStorage std::vector infill_angles; //!< a list of angle values which is cycled through to determine the infill angle of each layer std::vector roofing_angles; //!< a list of angle values which is cycled through to determine the roofing angle of each layer + std::vector flooring_angles; //!< a list of angle values which is cycled through to determine the flooring angle of each layer std::vector skin_angles; //!< a list of angle values which is cycled through to determine the skin angle of each layer std::vector overhang_areas; //!< For each layer the areas that are classified as overhang on this mesh. std::vector full_overhang_areas; //!< For each layer the full overhang without the tangent of the overhang angle removed, such that the overhang area adjoins the diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 58e72d8019..bbfffa0558 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -480,6 +480,17 @@ void FffGcodeWriter::setInfillAndSkinAngles(SliceMeshStorage& mesh) } } + if (mesh.flooring_angles.size() == 0) + { + mesh.flooring_angles = mesh.settings.get>("flooring_angles"); + if (mesh.flooring_angles.size() == 0) + { + // user has not specified any infill angles so use defaults + mesh.flooring_angles.push_back(45); + mesh.flooring_angles.push_back(135); + } + } + if (mesh.skin_angles.size() == 0) { mesh.skin_angles = mesh.settings.get>("skin_angles"); @@ -705,6 +716,8 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) config, config, config, + config, + config, retract_before_outer_wall, wipe_dist, wipe_dist, @@ -866,6 +879,8 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) config, config, config, + config, + config, retract_before_outer_wall, wipe_dist, wipe_dist, @@ -1035,6 +1050,8 @@ void FffGcodeWriter::processRaft(const SliceDataStorage& storage) config, config, config, + config, + config, retract_before_outer_wall, wipe_dist, wipe_dist, @@ -2732,6 +2749,8 @@ bool FffGcodeWriter::processSingleLayerInfill( mesh_config.infill_config[0], mesh_config.infill_config[0], mesh_config.infill_config[0], + mesh_config.infill_config[0], + mesh_config.infill_config[0], retract_before_outer_wall, wipe_dist, wipe_dist, @@ -3098,6 +3117,8 @@ bool FffGcodeWriter::processInsets( gcode_layer.setOverhangMask(get_overhang_region(mesh.settings.get("wall_overhang_angle"))); gcode_layer.setSeamOverhangMask(get_overhang_region(mesh.settings.get("seam_overhang_angle"))); + const auto wall_line_width_0 = mesh.settings.get("wall_line_width_0"); + const auto roofing_mask_fn = [&]() -> Shape { const size_t roofing_layer_count = std::min(mesh.settings.get("roofing_layer_count"), mesh.settings.get("top_layers")); @@ -3109,7 +3130,6 @@ bool FffGcodeWriter::processInsets( return roofing_mask; } - const auto wall_line_width_0 = mesh.settings.get("wall_line_width_0"); for (const auto& layer_part : mesh.layers[gcode_layer.getLayerNr() + roofing_layer_count].parts) { if (boundaryBox.hit(layer_part.boundaryBox)) @@ -3118,9 +3138,32 @@ bool FffGcodeWriter::processInsets( } } return roofing_mask; - }(); + }; - gcode_layer.setRoofingMask(roofing_mask_fn); + gcode_layer.setRoofingMask(roofing_mask_fn()); + + const auto flooring_mask_fn = [&]() -> Shape + { + const size_t flooring_layer_count = std::min(mesh.settings.get("flooring_layer_count"), mesh.settings.get("bottom_layers")); + + auto flooring_mask = storage.getMachineBorder(mesh.settings.get("wall_0_extruder_nr").extruder_nr_); + + if (gcode_layer.getLayerNr() - flooring_layer_count < 0) + { + return flooring_mask; + } + + for (const auto& layer_part : mesh.layers[gcode_layer.getLayerNr() - flooring_layer_count].parts) + { + if (boundaryBox.hit(layer_part.boundaryBox)) + { + flooring_mask = flooring_mask.difference(layer_part.outline.offset(-wall_line_width_0 / 4)); + } + } + return flooring_mask; + }; + + gcode_layer.setFlooringMask(flooring_mask_fn()); } else { @@ -3132,6 +3175,8 @@ bool FffGcodeWriter::processInsets( gcode_layer.setSeamOverhangMask(Shape()); // clear to disable use of roofing settings gcode_layer.setRoofingMask(Shape()); + // clear to disable use of flooring settings + gcode_layer.setFlooringMask(Shape()); } if (spiralize && extruder_nr == mesh.settings.get("wall_0_extruder_nr").extruder_nr_ && ! part.spiral_wall.empty()) @@ -3149,7 +3194,7 @@ bool FffGcodeWriter::processInsets( else { // Print the spiral walls of other parts as single walls without Z gradient. - gcode_layer.addWalls(part.spiral_wall, mesh.settings, mesh_config.inset0_config, mesh_config.inset0_config, mesh_config.inset0_config); + gcode_layer.addWalls(part.spiral_wall, mesh.settings, mesh_config.inset0_config, mesh_config.inset0_config, mesh_config.inset0_config, mesh_config.inset0_config); } } else @@ -3174,6 +3219,8 @@ bool FffGcodeWriter::processInsets( mesh_config.insetX_config, mesh_config.inset0_roofing_config, mesh_config.insetX_roofing_config, + mesh_config.inset0_flooring_config, + mesh_config.insetX_flooring_config, mesh_config.bridge_inset0_config, mesh_config.bridge_insetX_config, mesh.settings.get("travel_retract_before_outer_wall"), @@ -3273,51 +3320,42 @@ bool FffGcodeWriter::processSkinPart( gcode_layer.mode_skip_agressive_merge_ = true; - processRoofing(storage, gcode_layer, mesh, extruder_nr, mesh_config, skin_part, added_something); + processRoofingFlooring(storage, gcode_layer, mesh, extruder_nr, "roofing", skin_part.roofing_fill, mesh_config.roofing_config, mesh.roofing_angles, added_something); + processRoofingFlooring(storage, gcode_layer, mesh, extruder_nr, "flooring", skin_part.flooring_fill, mesh_config.flooring_config, mesh.flooring_angles, added_something); processTopBottom(storage, gcode_layer, mesh, extruder_nr, mesh_config, skin_part, added_something); gcode_layer.mode_skip_agressive_merge_ = false; return added_something; } -void FffGcodeWriter::processRoofing( +void FffGcodeWriter::processRoofingFlooring( const SliceDataStorage& storage, LayerPlan& gcode_layer, const SliceMeshStorage& mesh, const size_t extruder_nr, - const MeshPathConfigs& mesh_config, - const SkinPart& skin_part, + const std::string& setting_prefix, + const Shape& fill, + const GCodePathConfig& config, + const std::vector& angles, bool& added_something) const { - const size_t roofing_extruder_nr = mesh.settings.get("roofing_extruder_nr").extruder_nr_; - if (extruder_nr != roofing_extruder_nr) + const size_t skin_extruder_nr = mesh.settings.get(fmt::format("{}_extruder_nr", setting_prefix)).extruder_nr_; + if (extruder_nr != skin_extruder_nr) { return; } - const EFillMethod pattern = mesh.settings.get("roofing_pattern"); + const EFillMethod pattern = mesh.settings.get(fmt::format("{}_pattern", setting_prefix)); AngleDegrees roofing_angle = 45; - if (mesh.roofing_angles.size() > 0) + if (angles.size() > 0) { - roofing_angle = mesh.roofing_angles.at(gcode_layer.getLayerNr() % mesh.roofing_angles.size()); + roofing_angle = angles.at(gcode_layer.getLayerNr() % angles.size()); } const Ratio skin_density = 1.0; const coord_t skin_overlap = 0; // skinfill already expanded over the roofing areas; don't overlap with perimeters - const bool monotonic = mesh.settings.get("roofing_monotonic"); - processSkinPrintFeature( - storage, - gcode_layer, - mesh, - extruder_nr, - skin_part.roofing_fill, - mesh_config.roofing_config, - pattern, - roofing_angle, - skin_overlap, - skin_density, - monotonic, - added_something); + const bool monotonic = mesh.settings.get(fmt::format("{}_monotonic", setting_prefix)); + processSkinPrintFeature(storage, gcode_layer, mesh, extruder_nr, fill, config, pattern, roofing_angle, skin_overlap, skin_density, monotonic, added_something); } void FffGcodeWriter::processTopBottom( @@ -3606,6 +3644,8 @@ void FffGcodeWriter::processSkinPrintFeature( config, config, config, + config, + config, retract_before_outer_wall, wipe_dist, wipe_dist, @@ -3898,6 +3938,8 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer config, config, config, + config, + config, retract_before_outer_wall, wipe_dist, wipe_dist, @@ -4084,6 +4126,8 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer config, config, config, + config, + config, retract_before_outer_wall, wipe_dist, wipe_dist, @@ -4220,6 +4264,8 @@ bool FffGcodeWriter::addSupportRoofsToGCode(const SliceDataStorage& storage, con config, config, config, + config, + config, retract_before_outer_wall, wipe_dist, wipe_dist, @@ -4334,6 +4380,8 @@ bool FffGcodeWriter::addSupportBottomsToGCode(const SliceDataStorage& storage, L config, config, config, + config, + config, retract_before_outer_wall, wipe_dist, wipe_dist, diff --git a/src/InsetOrderOptimizer.cpp b/src/InsetOrderOptimizer.cpp index 7c8723bb95..78d671c40c 100644 --- a/src/InsetOrderOptimizer.cpp +++ b/src/InsetOrderOptimizer.cpp @@ -42,6 +42,8 @@ InsetOrderOptimizer::InsetOrderOptimizer( const GCodePathConfig& inset_X_default_config, const GCodePathConfig& inset_0_roofing_config, const GCodePathConfig& inset_X_roofing_config, + const GCodePathConfig& inset_0_flooring_config, + const GCodePathConfig& inset_X_flooring_config, const GCodePathConfig& inset_0_bridge_config, const GCodePathConfig& inset_X_bridge_config, const bool retract_before_outer_wall, @@ -65,6 +67,8 @@ InsetOrderOptimizer::InsetOrderOptimizer( , inset_X_default_config_(inset_X_default_config) , inset_0_roofing_config_(inset_0_roofing_config) , inset_X_roofing_config_(inset_X_roofing_config) + , inset_0_flooring_config_(inset_0_flooring_config) + , inset_X_flooring_config_(inset_X_flooring_config) , inset_0_bridge_config_(inset_0_bridge_config) , inset_X_bridge_config_(inset_X_bridge_config) , retract_before_outer_wall_(retract_before_outer_wall) @@ -152,6 +156,7 @@ bool InsetOrderOptimizer::addToLayer() const bool is_gap_filler = path.vertices_->is_odd_; const GCodePathConfig& default_config = is_outer_wall ? inset_0_default_config_ : inset_X_default_config_; const GCodePathConfig& roofing_config = is_outer_wall ? inset_0_roofing_config_ : inset_X_roofing_config_; + const GCodePathConfig& flooring_config = is_outer_wall ? inset_0_flooring_config_ : inset_X_flooring_config_; const GCodePathConfig& bridge_config = is_outer_wall ? inset_0_bridge_config_ : inset_X_bridge_config_; const coord_t wipe_dist = is_outer_wall && ! is_gap_filler ? wall_0_wipe_dist_ : wall_x_wipe_dist_; const bool retract_before = is_outer_wall ? retract_before_outer_wall_ : false; @@ -171,6 +176,7 @@ bool InsetOrderOptimizer::addToLayer() settings_, default_config, roofing_config, + flooring_config, bridge_config, wipe_dist, flow, diff --git a/src/LayerPlan.cpp b/src/LayerPlan.cpp index de3594c58d..1758024e08 100644 --- a/src/LayerPlan.cpp +++ b/src/LayerPlan.cpp @@ -57,14 +57,15 @@ GCodePath* LayerPlan::getLatestPathWithConfig( { return &paths.back(); } - paths.emplace_back(GCodePath{ .z_offset = z_offset, - .config = config, - .mesh = current_mesh_, - .space_fill_type = space_fill_type, - .flow = flow, - .width_factor = width_factor, - .spiralize = spiralize, - .speed_factor = speed_factor }); + paths.emplace_back( + GCodePath{ .z_offset = z_offset, + .config = config, + .mesh = current_mesh_, + .space_fill_type = space_fill_type, + .flow = flow, + .width_factor = width_factor, + .spiralize = spiralize, + .speed_factor = speed_factor }); GCodePath* ret = &paths.back(); ret->skip_agressive_merge_hint = mode_skip_agressive_merge_; @@ -716,6 +717,7 @@ void LayerPlan::addWallLine( const Settings& settings, const GCodePathConfig& default_config, const GCodePathConfig& roofing_config, + const GCodePathConfig& flooring_config, const GCodePathConfig& bridge_config, double flow, const Ratio width_factor, @@ -836,41 +838,41 @@ void LayerPlan::addWallLine( } }; - const auto use_roofing_config = [&]() -> bool + const auto use_skin_config = [&default_config, &p0, &p1](const Shape& mask, const GCodePathConfig& config) -> bool { - if (roofing_config == default_config) + if (config == default_config) { // if the roofing config and normal config are the same any way there is no need to check // what part of the line segment will be printed with what config. return false; } - return PolygonUtils::polygonCollidesWithLineSegment(roofing_mask_, p0.toPoint2LL(), p1.toPoint2LL()) || roofing_mask_.inside(p1.toPoint2LL(), true); - }(); + return PolygonUtils::polygonCollidesWithLineSegment(mask, p0.toPoint2LL(), p1.toPoint2LL()) || mask.inside(p1.toPoint2LL(), true); + }; - if (use_roofing_config) + const auto add_skin_extrusion = [&](const Shape& mask, const GCodePathConfig& config) -> void { - // The line segment is wholly or partially in the roofing area. The line is intersected - // with the roofing area into line segments. Each line segment left in this intersection - // will be printed using the roofing config, all removed segments will be printed using + // The line segment is wholly or partially in the skin area. The line is intersected + // with the skin area into line segments. Each line segment left in this intersection + // will be printed using the skin config, all removed segments will be printed using // the default_config. Since the original line segment was straight we can simply print // to the first and last point of the intersected line segments alternating between - // roofing and default_config's. + // skin and default_config's. OpenLinesSet line_polys; line_polys.addSegment(p0.toPoint2LL(), p1.toPoint2LL()); constexpr bool restitch = false; // only a single line doesn't need stitching - auto roofing_line_segments = roofing_mask_.intersection(line_polys, restitch); + auto skin_line_segments = mask.intersection(line_polys, restitch); - if (roofing_line_segments.empty()) + if (skin_line_segments.empty()) { - // roofing_line_segments should never be empty since we already checked that the line segment - // intersects with the roofing area. But if it is empty then just print the line segment + // skin_line_segments should never be empty since we already checked that the line segment + // intersects with the skin area. But if it is empty then just print the line segment // using the default_config. addExtrusionMove(p1, default_config, SpaceFillType::Polygons, flow, width_factor, spiralize, 1.0_r, GCodePathConfig::FAN_SPEED_DEFAULT, travel_to_z); } else { // reorder all the line segments so all lines start at p0 and end at p1 - for (auto& line_poly : roofing_line_segments) + for (auto& line_poly : skin_line_segments) { const Point2LL& line_p0 = line_poly.front(); const Point2LL& line_p1 = line_poly.back(); @@ -880,15 +882,15 @@ void LayerPlan::addWallLine( } } std::sort( - roofing_line_segments.begin(), - roofing_line_segments.end(), + skin_line_segments.begin(), + skin_line_segments.end(), [&](auto& a, auto& b) { return vSize2(a.front() - p0) < vSize2(b.front() - p0); }); // add intersected line segments, alternating between roofing and default_config - for (const auto& line_poly : roofing_line_segments) + for (const auto& line_poly : skin_line_segments) { // This is only relevant for the very fist iteration of the loop // if the start of the line segment is not at minimum distance from p0 @@ -906,15 +908,24 @@ void LayerPlan::addWallLine( travel_to_z); } - addExtrusionMove(line_poly.back(), roofing_config, SpaceFillType::Polygons, flow, width_factor, spiralize, 1.0_r, GCodePathConfig::FAN_SPEED_DEFAULT, travel_to_z); + addExtrusionMove(line_poly.back(), config, SpaceFillType::Polygons, flow, width_factor, spiralize, 1.0_r, GCodePathConfig::FAN_SPEED_DEFAULT, travel_to_z); } // if the last point is not yet at a minimum distance from p1 then add a move to p1 - if (vSize2(roofing_line_segments.back().back() - p1) > min_line_len * min_line_len) + if (vSize2(skin_line_segments.back().back() - p1) > min_line_len * min_line_len) { addExtrusionMove(p1, default_config, SpaceFillType::Polygons, flow, width_factor, spiralize, 1.0_r, GCodePathConfig::FAN_SPEED_DEFAULT, travel_to_z); } } + }; + + if (use_skin_config(roofing_mask_, roofing_config)) + { + add_skin_extrusion(roofing_mask_, roofing_config); + } + else if (use_skin_config(flooring_mask_, flooring_config)) + { + add_skin_extrusion(flooring_mask_, flooring_config); } else if (bridge_wall_mask_.empty()) { @@ -1026,6 +1037,7 @@ void LayerPlan::addWall( const Settings& settings, const GCodePathConfig& default_config, const GCodePathConfig& roofing_config, + const GCodePathConfig& flooring_config, const GCodePathConfig& bridge_config, coord_t wall_0_wipe_dist, double flow_ratio, @@ -1054,7 +1066,20 @@ void LayerPlan::addWall( constexpr bool is_closed = true; constexpr bool is_reversed = false; constexpr bool is_linked_path = false; - addWall(ewall, start_idx, settings, default_config, roofing_config, bridge_config, wall_0_wipe_dist, flow_ratio, always_retract, is_closed, is_reversed, is_linked_path); + addWall( + ewall, + start_idx, + settings, + default_config, + roofing_config, + flooring_config, + bridge_config, + wall_0_wipe_dist, + flow_ratio, + always_retract, + is_closed, + is_reversed, + is_linked_path); } template @@ -1605,6 +1630,7 @@ void LayerPlan::addWall( const Settings& settings, const GCodePathConfig& default_config, const GCodePathConfig& roofing_config, + const GCodePathConfig& flooring_config, const GCodePathConfig& bridge_config, coord_t wall_0_wipe_dist, const double flow_ratio, @@ -1651,6 +1677,7 @@ void LayerPlan::addWall( settings, default_config, roofing_config, + flooring_config, bridge_config, actual_flow_ratio, line_width_ratio, @@ -1699,6 +1726,7 @@ void LayerPlan::addWalls( const Settings& settings, const GCodePathConfig& default_config, const GCodePathConfig& roofing_config, + const GCodePathConfig& flooring_config, const GCodePathConfig& bridge_config, const ZSeamConfig& z_seam_config, coord_t wall_0_wipe_dist, @@ -1714,7 +1742,7 @@ void LayerPlan::addWalls( orderOptimizer.optimize(); for (const PathOrdering& path : orderOptimizer.paths_) { - addWall(*path.vertices_, path.start_vertex_, settings, default_config, roofing_config, bridge_config, wall_0_wipe_dist, flow_ratio, always_retract); + addWall(*path.vertices_, path.start_vertex_, settings, default_config, roofing_config, flooring_config, bridge_config, wall_0_wipe_dist, flow_ratio, always_retract); } } @@ -3190,6 +3218,11 @@ void LayerPlan::setRoofingMask(const Shape& polys) roofing_mask_ = polys; } +void LayerPlan::setFlooringMask(const Shape& shape) +{ + flooring_mask_ = shape; +} + template void LayerPlan::addLinesByOptimizer( const OpenLinesSet& lines, const GCodePathConfig& config, diff --git a/src/TopSurface.cpp b/src/TopSurface.cpp index beb6154d3e..5c6056c68d 100644 --- a/src/TopSurface.cpp +++ b/src/TopSurface.cpp @@ -179,6 +179,8 @@ bool TopSurface::ironing(const SliceDataStorage& storage, const SliceMeshStorage line_config, line_config, line_config, + line_config, + line_config, retract_before_outer_wall, wipe_dist, wipe_dist, diff --git a/src/settings/MeshPathConfigs.cpp b/src/settings/MeshPathConfigs.cpp index fffc0d311a..5e8cf565e4 100644 --- a/src/settings/MeshPathConfigs.cpp +++ b/src/settings/MeshPathConfigs.cpp @@ -48,6 +48,26 @@ MeshPathConfigs::MeshPathConfigs(const SliceMeshStorage& mesh, const coord_t lay .speed_derivatives = { .speed = mesh.settings.get("speed_wall_x_roofing"), .acceleration = mesh.settings.get("acceleration_wall_x_roofing"), .jerk = mesh.settings.get("jerk_wall_x_roofing") } } + , inset0_flooring_config{ .type = PrintFeatureType::OuterWall, + .line_width = static_cast( + mesh.settings.get("wall_line_width_0") + * line_width_factor_per_extruder[mesh.settings.get("wall_0_extruder_nr").extruder_nr_]), + .layer_thickness = layer_thickness, + .flow = mesh.settings.get("wall_0_material_flow_flooring") + * (layer_nr == 0 ? mesh.settings.get("wall_0_material_flow_layer_0") : Ratio{ 1.0 }), + .speed_derivatives = { .speed = mesh.settings.get("speed_wall_0_flooring"), + .acceleration = mesh.settings.get("acceleration_wall_0_flooring"), + .jerk = mesh.settings.get("jerk_wall_0_flooring") } } + , insetX_flooring_config{ .type = PrintFeatureType::InnerWall, + .line_width = static_cast( + mesh.settings.get("wall_line_width_x") + * line_width_factor_per_extruder[mesh.settings.get("wall_x_extruder_nr").extruder_nr_]), + .layer_thickness = layer_thickness, + .flow = mesh.settings.get("wall_x_material_flow_flooring") + * (layer_nr == 0 ? mesh.settings.get("wall_x_material_flow_layer_0") : Ratio{ 1.0 }), + .speed_derivatives = { .speed = mesh.settings.get("speed_wall_x_flooring"), + .acceleration = mesh.settings.get("acceleration_wall_x_flooring"), + .jerk = mesh.settings.get("jerk_wall_x_flooring") } } , bridge_inset0_config{ .type = PrintFeatureType::OuterWall, .line_width = static_cast( mesh.settings.get("wall_line_width_0") @@ -118,6 +138,13 @@ MeshPathConfigs::MeshPathConfigs(const SliceMeshStorage& mesh, const coord_t lay .speed_derivatives = { .speed = mesh.settings.get("speed_roofing"), .acceleration = mesh.settings.get("acceleration_roofing"), .jerk = mesh.settings.get("jerk_roofing") } } + , flooring_config{ .type = PrintFeatureType::Skin, + .line_width = mesh.settings.get("flooring_line_width"), + .layer_thickness = layer_thickness, + .flow = mesh.settings.get("flooring_material_flow") * (layer_nr == 0 ? mesh.settings.get("material_flow_layer_0") : Ratio{ 1.0 }), + .speed_derivatives = { .speed = mesh.settings.get("speed_flooring"), + .acceleration = mesh.settings.get("acceleration_flooring"), + .jerk = mesh.settings.get("jerk_flooring") } } , ironing_config{ .type = PrintFeatureType::Skin, .line_width = mesh.settings.get("ironing_line_spacing"), .layer_thickness = layer_thickness, @@ -131,15 +158,16 @@ MeshPathConfigs::MeshPathConfigs(const SliceMeshStorage& mesh, const coord_t lay for (const auto combine_idx : ranges::views::iota(1, MAX_INFILL_COMBINE + 1)) { - infill_config.emplace_back(GCodePathConfig{ - .type = PrintFeatureType::Infill, - .line_width = static_cast( - mesh.settings.get("infill_line_width") * line_width_factor_per_extruder[mesh.settings.get("infill_extruder_nr").extruder_nr_]), - .layer_thickness = layer_thickness, - .flow = mesh.settings.get("infill_material_flow") * (layer_nr == 0 ? mesh.settings.get("material_flow_layer_0") : Ratio{ 1.0 }) * combine_idx, - .speed_derivatives = { .speed = mesh.settings.get("speed_infill"), - .acceleration = mesh.settings.get("acceleration_infill"), - .jerk = mesh.settings.get("jerk_infill") } }); + infill_config.emplace_back( + GCodePathConfig{ + .type = PrintFeatureType::Infill, + .line_width = static_cast( + mesh.settings.get("infill_line_width") * line_width_factor_per_extruder[mesh.settings.get("infill_extruder_nr").extruder_nr_]), + .layer_thickness = layer_thickness, + .flow = mesh.settings.get("infill_material_flow") * (layer_nr == 0 ? mesh.settings.get("material_flow_layer_0") : Ratio{ 1.0 }) * combine_idx, + .speed_derivatives = { .speed = mesh.settings.get("speed_infill"), + .acceleration = mesh.settings.get("acceleration_infill"), + .jerk = mesh.settings.get("jerk_infill") } }); } } diff --git a/src/skin.cpp b/src/skin.cpp index 987e39a8b9..7fa5982c3c 100644 --- a/src/skin.cpp +++ b/src/skin.cpp @@ -91,7 +91,7 @@ void SkinInfillAreaComputation::generateSkinsAndInfill() for (SliceLayerPart& part : layer->parts) { - generateRoofingFillAndSkinFill(part); + generateSkinRoofingFlooringFill(part); generateTopAndBottomMostSurfaces(part); } @@ -324,22 +324,27 @@ void SkinInfillAreaComputation::generateInfill(SliceLayerPart& part) * * this function may only read/write the skin and infill from the *current* layer. */ -void SkinInfillAreaComputation::generateRoofingFillAndSkinFill(SliceLayerPart& part) +void SkinInfillAreaComputation::generateSkinRoofingFlooringFill(SliceLayerPart& part) { for (SkinPart& skin_part : part.skin_parts) { const size_t roofing_layer_count = std::min(mesh_.settings.get("roofing_layer_count"), mesh_.settings.get("top_layers")); + const size_t flooring_layer_count = std::min(mesh_.settings.get("flooring_layer_count"), mesh_.settings.get("bottom_layers")); const coord_t skin_overlap = mesh_.settings.get("skin_overlap_mm"); Shape filled_area_above = generateFilledAreaAbove(part, roofing_layer_count); + Shape filled_area_below = generateFilledAreaBelow(part, flooring_layer_count); + // An area that would have nothing below nor above is considered a roof skin_part.roofing_fill = skin_part.outline.difference(filled_area_above); - skin_part.skin_fill = skin_part.outline.intersection(filled_area_above); + skin_part.flooring_fill = skin_part.outline.intersection(filled_area_above).difference(filled_area_below); + skin_part.skin_fill = skin_part.outline.intersection(filled_area_above).intersection(filled_area_below); - // We remove offsets areas from roofing_fill anywhere they overlap with skin_fill. - // Otherwise, adjacent skin_fill and roofing_fill would have doubled offset areas. Since they both offset into each other. - skin_part.skin_fill = skin_part.skin_fill.offset(skin_overlap).difference(skin_part.roofing_fill); + // We remove offsets areas from roofing and flooring anywhere they overlap with skin_fill. + // Otherwise, adjacent skin_fill and roofing/flooring would have doubled offset areas. Since they both offset into each other. + skin_part.skin_fill = skin_part.skin_fill.offset(skin_overlap).difference(skin_part.roofing_fill).difference(skin_part.flooring_fill); skin_part.roofing_fill = skin_part.roofing_fill.offset(skin_overlap); + skin_part.flooring_fill = skin_part.flooring_fill.offset(skin_overlap).difference(skin_part.roofing_fill); } }