mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-01 04:02:02 +08:00
SPE-1804: Single perimeter for top and bottom layers working with both perimeter generators.
Co-authored-by: supermerill <merill@free.fr> Co-authored-by: Morton Jonuschat <mjonuschat@gmail.com> Co-authored-by: Vovodroid <vovodroid@users.noreply.github.com> Co-authored-by: qing.zhang <qing.zhang@bambulab.com> Co-authored-by: lane.wei <lane.wei@bambulab.com>
This commit is contained in:
parent
d383e59438
commit
a3190d94d0
@ -183,6 +183,18 @@ namespace ClipperUtils {
|
||||
out.end());
|
||||
return out;
|
||||
}
|
||||
[[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygons &src, const BoundingBox &bbox)
|
||||
{
|
||||
Polygons out;
|
||||
out.reserve(number_polygons(src));
|
||||
for (const ExPolygon &p : src) {
|
||||
Polygons temp = clip_clipper_polygons_with_subject_bbox(p, bbox);
|
||||
out.insert(out.end(), temp.begin(), temp.end());
|
||||
}
|
||||
|
||||
out.erase(std::remove_if(out.begin(), out.end(), [](const Polygon &polygon) {return polygon.empty(); }), out.end());
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
static ExPolygons PolyTreeToExPolygons(ClipperLib::PolyTree &&polytree)
|
||||
|
@ -335,6 +335,7 @@ namespace ClipperUtils {
|
||||
[[nodiscard]] Polygon clip_clipper_polygon_with_subject_bbox(const Polygon &src, const BoundingBox &bbox);
|
||||
[[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const Polygons &src, const BoundingBox &bbox);
|
||||
[[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygon &src, const BoundingBox &bbox);
|
||||
[[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygons &src, const BoundingBox &bbox);
|
||||
}
|
||||
|
||||
// offset Polygons
|
||||
|
@ -111,6 +111,7 @@ void LayerRegion::make_perimeters(
|
||||
|
||||
// Cummulative sum of polygons over all the regions.
|
||||
const ExPolygons *lower_slices = this->layer()->lower_layer ? &this->layer()->lower_layer->lslices : nullptr;
|
||||
const ExPolygons *upper_slices = this->layer()->upper_layer ? &this->layer()->upper_layer->lslices : nullptr;
|
||||
// Cache for offsetted lower_slices
|
||||
Polygons lower_layer_polygons_cache;
|
||||
|
||||
@ -124,6 +125,7 @@ void LayerRegion::make_perimeters(
|
||||
params,
|
||||
surface,
|
||||
lower_slices,
|
||||
upper_slices,
|
||||
lower_layer_polygons_cache,
|
||||
// output:
|
||||
m_perimeters,
|
||||
@ -135,6 +137,7 @@ void LayerRegion::make_perimeters(
|
||||
params,
|
||||
surface,
|
||||
lower_slices,
|
||||
upper_slices,
|
||||
lower_layer_polygons_cache,
|
||||
// output:
|
||||
m_perimeters,
|
||||
|
@ -1083,6 +1083,7 @@ void PerimeterGenerator::process_arachne(
|
||||
const Parameters ¶ms,
|
||||
const Surface &surface,
|
||||
const ExPolygons *lower_slices,
|
||||
const ExPolygons *upper_slices,
|
||||
// Cache:
|
||||
Polygons &lower_slices_polygons_cache,
|
||||
// Output:
|
||||
@ -1094,7 +1095,8 @@ void PerimeterGenerator::process_arachne(
|
||||
ExPolygons &out_fill_expolygons)
|
||||
{
|
||||
// other perimeters
|
||||
coord_t perimeter_spacing = params.perimeter_flow.scaled_spacing();
|
||||
coord_t perimeter_width = params.perimeter_flow.scaled_width();
|
||||
coord_t perimeter_spacing = params.perimeter_flow.scaled_spacing();
|
||||
// external perimeters
|
||||
coord_t ext_perimeter_width = params.ext_perimeter_flow.scaled_width();
|
||||
coord_t ext_perimeter_spacing = params.ext_perimeter_flow.scaled_spacing();
|
||||
@ -1114,12 +1116,82 @@ void PerimeterGenerator::process_arachne(
|
||||
// we need to process each island separately because we might have different
|
||||
// extra perimeters for each one
|
||||
// detect how many perimeters must be generated for this island
|
||||
int loop_number = params.config.perimeters + surface.extra_perimeters - 1; // 0-indexed loops
|
||||
ExPolygons last = offset_ex(surface.expolygon.simplify_p(params.scaled_resolution), - float(ext_perimeter_width / 2. - ext_perimeter_spacing / 2.));
|
||||
Polygons last_p = to_polygons(last);
|
||||
int loop_number = params.config.perimeters + surface.extra_perimeters - 1; // 0-indexed loops
|
||||
if (loop_number > 0 && ((params.config.top_one_perimeter_type == TopOnePerimeterType::TopmostOnly && upper_slices == nullptr) || (params.config.only_one_perimeter_first_layer && params.layer_id == 0)))
|
||||
loop_number = 0;
|
||||
|
||||
// Calculate how many inner loops remain when TopSurfaces is selected.
|
||||
const int inner_loop_number = (params.config.top_one_perimeter_type == TopOnePerimeterType::TopSurfaces && upper_slices != nullptr) ? loop_number - 1 : -1;
|
||||
|
||||
// Set one perimeter when TopSurfaces is selected.
|
||||
if (params.config.top_one_perimeter_type == TopOnePerimeterType::TopSurfaces)
|
||||
loop_number = 0;
|
||||
|
||||
ExPolygons last = offset_ex(surface.expolygon.simplify_p(params.scaled_resolution), - float(ext_perimeter_width / 2. - ext_perimeter_spacing / 2.));
|
||||
Polygons last_p = to_polygons(last);
|
||||
Arachne::WallToolPaths wall_tool_paths(last_p, ext_perimeter_spacing, perimeter_spacing, coord_t(loop_number + 1), 0, params.layer_height, params.object_config, params.print_config);
|
||||
std::vector<Arachne::VariableWidthLines> perimeters = wall_tool_paths.getToolPaths();
|
||||
ExPolygons infill_contour = union_ex(wall_tool_paths.getInnerContour());
|
||||
|
||||
// Check if there are some remaining perimeters to generate (the number of perimeters
|
||||
// is greater than one together with enabled the single perimeter on top surface feature).
|
||||
if (inner_loop_number >= 0) {
|
||||
assert(upper_slices != nullptr);
|
||||
|
||||
// Infill contour bounding box.
|
||||
BoundingBox infill_contour_bbox = get_extents(infill_contour);
|
||||
infill_contour_bbox.offset(SCALED_EPSILON);
|
||||
|
||||
// Get top ExPolygons from current infill contour.
|
||||
const Polygons upper_slices_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*upper_slices, infill_contour_bbox);
|
||||
ExPolygons top_expolygons = diff_ex(infill_contour, upper_slices_clipped);
|
||||
|
||||
if (!top_expolygons.empty()) {
|
||||
if (lower_slices != nullptr) {
|
||||
const float bridge_offset = float(std::max<coord_t>(ext_perimeter_spacing, perimeter_width));
|
||||
const Polygons lower_slices_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*lower_slices, infill_contour_bbox);
|
||||
const ExPolygons current_slices_bridges = offset_ex(diff_ex(top_expolygons, lower_slices_clipped), bridge_offset);
|
||||
|
||||
// Remove bridges from top surface polygons.
|
||||
top_expolygons = diff_ex(top_expolygons, current_slices_bridges);
|
||||
}
|
||||
|
||||
// Filter out areas that are too thin and expand top surface polygons a bit to hide the wall line.
|
||||
const float top_surface_min_width = std::max<float>(float(ext_perimeter_spacing) / 4.f + scaled<float>(0.00001), float(perimeter_width) / 4.f);
|
||||
top_expolygons = offset2_ex(top_expolygons, -top_surface_min_width, top_surface_min_width + float(perimeter_width));
|
||||
|
||||
// Get the not-top ExPolygons (including bridges) from current slices and expanded real top ExPolygons (without bridges).
|
||||
const ExPolygons not_top_expolygons = diff_ex(infill_contour, top_expolygons);
|
||||
|
||||
// Get final top ExPolygons.
|
||||
top_expolygons = intersection_ex(top_expolygons, infill_contour);
|
||||
|
||||
const Polygons not_top_polygons = to_polygons(not_top_expolygons);
|
||||
Arachne::WallToolPaths inner_wall_tool_paths(not_top_polygons, perimeter_spacing, perimeter_spacing, coord_t(inner_loop_number + 1), 0, params.layer_height, params.object_config, params.print_config);
|
||||
std::vector<Arachne::VariableWidthLines> inner_perimeters = inner_wall_tool_paths.getToolPaths();
|
||||
|
||||
// Recalculate indexes of inner perimeters before merging them.
|
||||
if (!perimeters.empty()) {
|
||||
for (Arachne::VariableWidthLines &inner_perimeter : inner_perimeters) {
|
||||
if (inner_perimeter.empty())
|
||||
continue;
|
||||
|
||||
for (Arachne::ExtrusionLine &el : inner_perimeter)
|
||||
++el.inset_idx;
|
||||
}
|
||||
}
|
||||
|
||||
perimeters.insert(perimeters.end(), inner_perimeters.begin(), inner_perimeters.end());
|
||||
infill_contour = union_ex(top_expolygons, inner_wall_tool_paths.getInnerContour());
|
||||
} else {
|
||||
// There is no top surface ExPolygon, so we call Arachne again with parameters
|
||||
// like when the single perimeter feature is disabled.
|
||||
Arachne::WallToolPaths no_single_perimeter_tool_paths(last_p, ext_perimeter_spacing, perimeter_spacing, coord_t(inner_loop_number + 2), 0, params.layer_height, params.object_config, params.print_config);
|
||||
perimeters = no_single_perimeter_tool_paths.getToolPaths();
|
||||
infill_contour = union_ex(no_single_perimeter_tool_paths.getInnerContour());
|
||||
}
|
||||
}
|
||||
|
||||
Arachne::WallToolPaths wallToolPaths(last_p, ext_perimeter_spacing, perimeter_spacing, coord_t(loop_number + 1), 0, params.layer_height, params.object_config, params.print_config);
|
||||
std::vector<Arachne::VariableWidthLines> perimeters = wallToolPaths.getToolPaths();
|
||||
loop_number = int(perimeters.size()) - 1;
|
||||
|
||||
#ifdef ARACHNE_DEBUG
|
||||
@ -1272,8 +1344,7 @@ void PerimeterGenerator::process_arachne(
|
||||
if (ExtrusionEntityCollection extrusion_coll = traverse_extrusions(params, lower_slices_polygons_cache, ordered_extrusions); !extrusion_coll.empty())
|
||||
out_loops.append(extrusion_coll);
|
||||
|
||||
ExPolygons infill_contour = union_ex(wallToolPaths.getInnerContour());
|
||||
const coord_t spacing = (perimeters.size() == 1) ? ext_perimeter_spacing2 : perimeter_spacing;
|
||||
const coord_t spacing = (perimeters.size() == 1) ? ext_perimeter_spacing2 : perimeter_spacing;
|
||||
if (offset_ex(infill_contour, -float(spacing / 2.)).empty())
|
||||
infill_contour.clear(); // Infill region is too small, so let's filter it out.
|
||||
|
||||
@ -1329,6 +1400,7 @@ void PerimeterGenerator::process_classic(
|
||||
const Parameters ¶ms,
|
||||
const Surface &surface,
|
||||
const ExPolygons *lower_slices,
|
||||
const ExPolygons *upper_slices,
|
||||
// Cache:
|
||||
Polygons &lower_slices_polygons_cache,
|
||||
// Output:
|
||||
@ -1376,8 +1448,15 @@ void PerimeterGenerator::process_classic(
|
||||
// extra perimeters for each one
|
||||
// detect how many perimeters must be generated for this island
|
||||
int loop_number = params.config.perimeters + surface.extra_perimeters - 1; // 0-indexed loops
|
||||
ExPolygons last = union_ex(surface.expolygon.simplify_p(params.scaled_resolution));
|
||||
|
||||
// Set the topmost layer to be one perimeter.
|
||||
if (loop_number > 0 && ((params.config.top_one_perimeter_type != TopOnePerimeterType::None && upper_slices == nullptr) || (params.config.only_one_perimeter_first_layer && params.layer_id == 0)))
|
||||
loop_number = 0;
|
||||
|
||||
ExPolygons last = union_ex(surface.expolygon.simplify_p(params.scaled_resolution));
|
||||
ExPolygons gaps;
|
||||
ExPolygons top_fills;
|
||||
ExPolygons fill_clip;
|
||||
if (loop_number >= 0) {
|
||||
// In case no perimeters are to be generated, loop_number will equal to -1.
|
||||
std::vector<PerimeterGeneratorLoops> contours(loop_number+1); // depth => loops
|
||||
@ -1468,6 +1547,65 @@ void PerimeterGenerator::process_classic(
|
||||
}
|
||||
}
|
||||
last = std::move(offsets);
|
||||
|
||||
// Store surface for top infill if top_one_perimeter_type is set to TopSurfaces.
|
||||
if (i == 0 && i != loop_number && params.config.top_one_perimeter_type == TopOnePerimeterType::TopSurfaces && upper_slices != nullptr) {
|
||||
// Split the polygons with top/not_top.
|
||||
|
||||
// Get the offset from solid surface anchor.
|
||||
const coordf_t total_perimeter_spacing = coordf_t(perimeter_spacing * (params.config.perimeters.value - 1));
|
||||
const coordf_t top_surface_offset_threshold = params.config.perimeters.value <= 1 ? 0. : 0.9 * total_perimeter_spacing;
|
||||
coordf_t top_surface_offset = params.config.perimeters.value == 0 ? 0. : 1.5 * coordf_t(ext_perimeter_width + total_perimeter_spacing);
|
||||
|
||||
// If possible, try to not push the extra perimeters inside the sparse infill.
|
||||
if (top_surface_offset > top_surface_offset_threshold) {
|
||||
top_surface_offset -= top_surface_offset_threshold;
|
||||
} else
|
||||
top_surface_offset = 0.;
|
||||
|
||||
// Don't take into account too thin areas.
|
||||
const float top_surface_min_width = std::max<float>(float(ext_perimeter_spacing) / 2.f + scaled<float>(0.00001), float(perimeter_width));
|
||||
|
||||
// Current slices bounding box.
|
||||
BoundingBox current_perimeters_bbox = get_extents(last);
|
||||
current_perimeters_bbox.offset(SCALED_EPSILON);
|
||||
|
||||
ExPolygons current_slices_without_bridges;
|
||||
if (lower_slices != nullptr) {
|
||||
const float bridge_offset = 1.5f * float(std::max<coord_t>(ext_perimeter_spacing, perimeter_width));
|
||||
const Polygons lower_slices_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*lower_slices, current_perimeters_bbox);
|
||||
const ExPolygons current_slices_bridges = offset_ex(diff_ex(last, lower_slices_clipped, ApplySafetyOffset::Yes), bridge_offset);
|
||||
current_slices_without_bridges = diff_ex(last, current_slices_bridges, ApplySafetyOffset::Yes);
|
||||
} else {
|
||||
current_slices_without_bridges = last;
|
||||
}
|
||||
|
||||
// Get top ExPolygons (including external perimeters) from current slices without bridges.
|
||||
const Polygons upper_slices_clipped = expand(ClipperUtils::clip_clipper_polygons_with_subject_bbox(*upper_slices, current_perimeters_bbox), top_surface_min_width);
|
||||
const ExPolygons top_polygons = diff_ex(current_slices_without_bridges, upper_slices_clipped, ApplySafetyOffset::Yes);
|
||||
|
||||
if (!top_polygons.empty()) {
|
||||
// Set the clip to a virtual second perimeter.
|
||||
fill_clip = offset_ex(last, -float(ext_perimeter_spacing));
|
||||
|
||||
// Get the not-top ExPolygons (including bridges) from current slices and expanded real top ExPolygons (without bridges).
|
||||
const ExPolygons not_top_polygons = diff_ex(last, offset_ex(top_polygons, float(top_surface_offset) + top_surface_min_width - (float(ext_perimeter_spacing) / 2.f)), ApplySafetyOffset::Yes);
|
||||
|
||||
// Get difference between top ExPolygons without bridges and the area defined by the virtual second perimeter.
|
||||
const ExPolygons top_gap = diff_ex(top_polygons, fill_clip);
|
||||
|
||||
// Get top infill surface ExPolygons (without bridges) using the difference between the area defined by the virtual second perimeter and non-top ExPolygons.
|
||||
top_fills = diff_ex(fill_clip, not_top_polygons, ApplySafetyOffset::Yes);
|
||||
|
||||
// Set the clip to the external perimeter but go back inside by infill_extrusion_width/2 to ensure the extrusion won't go outside even with a 100% overlap.
|
||||
fill_clip = offset_ex(last, float((coordf_t(ext_perimeter_spacing) / 2.) - params.config.infill_extrusion_width.get_abs_value(params.solid_infill_flow.nozzle_diameter()) / 2.));
|
||||
last = intersection_ex(not_top_polygons, last);
|
||||
|
||||
if (has_gap_fill)
|
||||
last = union_ex(last, top_gap);
|
||||
}
|
||||
}
|
||||
|
||||
if (i == loop_number && (! has_gap_fill || params.config.fill_density.value == 0)) {
|
||||
// The last run of this loop is executed to collect gaps for gap fill.
|
||||
// As the gap fill is either disabled or not
|
||||
@ -1581,9 +1719,11 @@ void PerimeterGenerator::process_classic(
|
||||
ext_perimeter_spacing / 2 :
|
||||
// two or more loops?
|
||||
perimeter_spacing / 2;
|
||||
// only apply infill overlap if we actually have one perimeter
|
||||
if (inset > 0)
|
||||
inset -= coord_t(scale_(params.config.get_abs_value("infill_overlap", unscale<double>(inset + solid_infill_spacing / 2))));
|
||||
|
||||
// Only apply infill overlap if we actually have one perimeter.
|
||||
const coord_t infill_perimeter_overlap = (inset > 0) ? coord_t(params.config.get_abs_value("infill_overlap", coordf_t(inset + solid_infill_spacing / 2.))) : 0;
|
||||
inset -= infill_perimeter_overlap;
|
||||
|
||||
// simplify infill contours according to resolution
|
||||
Polygons pp;
|
||||
for (ExPolygon &ex : last)
|
||||
@ -1597,6 +1737,12 @@ void PerimeterGenerator::process_classic(
|
||||
float(- inset - min_perimeter_infill_spacing / 2.),
|
||||
float(min_perimeter_infill_spacing / 2.));
|
||||
|
||||
// Apply single perimeter feature.
|
||||
if (!top_fills.empty()) {
|
||||
const ExPolygons top_infill_areas = intersection_ex(fill_clip, offset_ex(top_fills, float(ext_perimeter_spacing) / 2.f));
|
||||
infill_areas = union_ex(infill_areas, offset_ex(top_infill_areas, float(infill_perimeter_overlap)));
|
||||
}
|
||||
|
||||
if (lower_slices != nullptr && params.config.overhangs && params.config.extra_perimeters_on_overhangs &&
|
||||
params.config.perimeters > 0 && params.layer_id > params.object_config.raft_layers) {
|
||||
// Generate extra perimeters on overhang areas, and cut them to these parts only, to save print time and material
|
||||
|
@ -76,6 +76,7 @@ void process_classic(
|
||||
const Parameters ¶ms,
|
||||
const Surface &surface,
|
||||
const ExPolygons *lower_slices,
|
||||
const ExPolygons *upper_slices,
|
||||
// Cache:
|
||||
Polygons &lower_slices_polygons_cache,
|
||||
// Output:
|
||||
@ -91,6 +92,7 @@ void process_arachne(
|
||||
const Parameters ¶ms,
|
||||
const Surface &surface,
|
||||
const ExPolygons *lower_slices,
|
||||
const ExPolygons *upper_slices,
|
||||
// Cache:
|
||||
Polygons &lower_slices_polygons_cache,
|
||||
// Output:
|
||||
|
@ -471,7 +471,8 @@ static std::vector<std::string> s_Preset_print_options {
|
||||
"wipe_tower_width", "wipe_tower_cone_angle", "wipe_tower_rotation_angle", "wipe_tower_brim_width", "wipe_tower_bridging", "single_extruder_multi_material_priming", "mmu_segmented_region_max_width",
|
||||
"mmu_segmented_region_interlocking_depth", "wipe_tower_extruder", "wipe_tower_no_sparse_layers", "wipe_tower_extra_flow", "wipe_tower_extra_spacing", "compatible_printers", "compatible_printers_condition", "inherits",
|
||||
"perimeter_generator", "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle",
|
||||
"wall_distribution_count", "min_feature_size", "min_bead_width"
|
||||
"wall_distribution_count", "min_feature_size", "min_bead_width",
|
||||
"top_one_perimeter_type", "only_one_perimeter_first_layer",
|
||||
};
|
||||
|
||||
static std::vector<std::string> s_Preset_filament_options {
|
||||
|
@ -257,6 +257,13 @@ static t_config_enum_values s_keys_map_PerimeterGeneratorType {
|
||||
};
|
||||
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(PerimeterGeneratorType)
|
||||
|
||||
static t_config_enum_values s_keys_map_TopOnePerimeterType {
|
||||
{ "none", int(TopOnePerimeterType::None) },
|
||||
{ "top", int(TopOnePerimeterType::TopSurfaces) },
|
||||
{ "topmost", int(TopOnePerimeterType::TopmostOnly) }
|
||||
};
|
||||
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(TopOnePerimeterType)
|
||||
|
||||
static void assign_printer_technology_to_unknown(t_optiondef_map &options, PrinterTechnology printer_technology)
|
||||
{
|
||||
for (std::pair<const t_config_option_key, ConfigOptionDef> &kvp : options)
|
||||
@ -569,6 +576,25 @@ void PrintConfigDef::init_fff_params()
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloat(1));
|
||||
|
||||
def = this->add("top_one_perimeter_type", coEnum);
|
||||
def->label = L("Only one perimeter type");
|
||||
def->category = L("Layers and Perimeters");
|
||||
def->tooltip = L("Use only one perimeter on flat top surface, to give more space to the top infill pattern. Could be applied on topmost surface or all top surface.");
|
||||
def->mode = comExpert;
|
||||
def->set_enum<TopOnePerimeterType>({
|
||||
{ "none", L("None surfaces") },
|
||||
{ "top", L("All top surfaces") },
|
||||
{ "topmost", L("Topmost surface only") }
|
||||
});
|
||||
def->set_default_value(new ConfigOptionEnum<TopOnePerimeterType>(TopOnePerimeterType::None));
|
||||
|
||||
def = this->add("only_one_perimeter_first_layer", coBool);
|
||||
def->label = L("Only one perimeter on first layer");
|
||||
def->category = L("Layers and Perimeters");
|
||||
def->tooltip = L("Use only one perimeter on the first layer of model.");
|
||||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
def = this->add("bridge_speed", coFloat);
|
||||
def->label = L("Bridges");
|
||||
def->category = L("Speed");
|
||||
|
@ -159,6 +159,14 @@ enum class PerimeterGeneratorType
|
||||
Arachne
|
||||
};
|
||||
|
||||
enum class TopOnePerimeterType
|
||||
{
|
||||
None,
|
||||
TopSurfaces,
|
||||
TopmostOnly,
|
||||
Count
|
||||
};
|
||||
|
||||
enum class GCodeThumbnailsFormat {
|
||||
PNG, JPG, QOI
|
||||
};
|
||||
@ -190,7 +198,7 @@ CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(LabelObjectsStyle)
|
||||
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(GCodeThumbnailsFormat)
|
||||
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(ForwardCompatibilitySubstitutionRule)
|
||||
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(PerimeterGeneratorType)
|
||||
|
||||
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(TopOnePerimeterType)
|
||||
|
||||
#undef CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS
|
||||
|
||||
@ -662,6 +670,9 @@ PRINT_CONFIG_CLASS_DEFINE(
|
||||
((ConfigOptionFloat, top_solid_min_thickness))
|
||||
((ConfigOptionFloatOrPercent, top_solid_infill_speed))
|
||||
((ConfigOptionBool, wipe_into_infill))
|
||||
// Single perimeter.
|
||||
((ConfigOptionEnum<TopOnePerimeterType>, top_one_perimeter_type))
|
||||
((ConfigOptionBool, only_one_perimeter_first_layer))
|
||||
)
|
||||
|
||||
PRINT_CONFIG_CLASS_DEFINE(
|
||||
|
@ -715,7 +715,9 @@ bool PrintObject::invalidate_state_by_config_options(
|
||||
|| opt_key == "perimeter_extrusion_width"
|
||||
|| opt_key == "infill_overlap"
|
||||
|| opt_key == "external_perimeters_first"
|
||||
|| opt_key == "arc_fitting") {
|
||||
|| opt_key == "arc_fitting"
|
||||
|| opt_key == "top_one_perimeter_type"
|
||||
|| opt_key == "only_one_perimeter_first_layer") {
|
||||
steps.emplace_back(posPerimeters);
|
||||
} else if (
|
||||
opt_key == "gap_fill_enabled"
|
||||
|
@ -1467,6 +1467,10 @@ void TabPrint::build()
|
||||
optgroup->append_single_option_line("fuzzy_skin_thickness", category_path + "fuzzy-skin-thickness");
|
||||
optgroup->append_single_option_line("fuzzy_skin_point_dist", category_path + "fuzzy-skin-point-distance");
|
||||
|
||||
optgroup = page->new_optgroup(L("Only one perimeter"));
|
||||
optgroup->append_single_option_line("top_one_perimeter_type", category_path + "top-one-perimeter-type");
|
||||
optgroup->append_single_option_line("only_one_perimeter_first_layer", category_path + "only-one-perimeter-first-layer");
|
||||
|
||||
page = add_options_page(L("Infill"), "infill");
|
||||
category_path = "infill_42#";
|
||||
optgroup = page->new_optgroup(L("Infill"));
|
||||
|
@ -65,6 +65,7 @@ SCENARIO("Perimeter nesting", "[Perimeters]")
|
||||
perimeter_generator_params,
|
||||
surface,
|
||||
nullptr,
|
||||
nullptr,
|
||||
// cache:
|
||||
lower_layer_polygons_cache,
|
||||
// output:
|
||||
|
Loading…
x
Reference in New Issue
Block a user