ENH: top one wall for arachne

split to 3 mode
not apply
all top surface
topmost surface

Signed-off-by: qing.zhang <qing.zhang@bambulab.com>
Change-Id: Id9bb1a6ded7414b2f093ac01e4ecb4b93057d022
This commit is contained in:
qing.zhang 2023-07-14 18:16:49 +08:00 committed by Lane.Wei
parent 287547132f
commit 0d837e2587
8 changed files with 143 additions and 48 deletions

View File

@ -861,7 +861,7 @@ void PerimeterGenerator::process_classic()
// detect how many perimeters must be generated for this island
int loop_number = this->config->wall_loops + surface.extra_perimeters - 1; // 0-indexed loops
//BBS: set the topmost and bottom most layer to be one wall
if (loop_number > 0 && ((this->object_config->only_one_wall_top && this->upper_slices == nullptr) || (this->object_config->only_one_wall_first_layer && layer_id == 0)))
if (loop_number > 0 && ((this->object_config->top_one_wall_type != TopOneWallType::None && this->upper_slices == nullptr) || (this->object_config->only_one_wall_first_layer && layer_id == 0)))
loop_number = 0;
ExPolygons last = union_ex(surface.expolygon.simplify_p(surface_simplify_resolution));
@ -1008,7 +1008,7 @@ void PerimeterGenerator::process_classic()
//BBS: refer to superslicer
//store surface for top infill if only_one_wall_top
if (i == 0 && i != loop_number && this->object_config->only_one_wall_top && this->upper_slices != NULL) {
if (i == 0 && i != loop_number && this->object_config->top_one_wall_type == TopOneWallType::Alltop && this->upper_slices != NULL) {
//split the polygons with top/not_top
//get the offset from solid surface anchor
coord_t offset_top_surface = scale_(1.5 * (config->wall_loops.value == 0 ? 0. : unscaled(double(ext_perimeter_width + perimeter_spacing * int(int(config->wall_loops.value) - int(1))))));
@ -1272,6 +1272,39 @@ void PerimeterGenerator::process_classic()
} // for each island
}
//BBS:
void PerimeterGenerator::add_infill_contour_for_arachne( ExPolygons infill_contour,
int loops,
coord_t ext_perimeter_spacing,
coord_t perimeter_spacing,
coord_t min_perimeter_infill_spacing,
coord_t spacing,
bool is_inner_part)
{
if( offset_ex(infill_contour, -float(spacing / 2.)).empty() )
{
infill_contour.clear(); // Infill region is too small, so let's filter it out.
}
// create one more offset to be used as boundary for fill
// we offset by half the perimeter spacing (to get to the actual infill boundary)
// and then we offset back and forth by half the infill spacing to only consider the
// non-collapsing regions
coord_t insert = (loops < 0) ? 0: ext_perimeter_spacing;
if (is_inner_part || loops > 0)
insert = perimeter_spacing;
insert = coord_t(scale_(this->config->infill_wall_overlap.get_abs_value(unscale<double>(insert))));
Polygons inner_pp;
for (ExPolygon &ex : infill_contour)
ex.simplify_p(m_scaled_resolution, &inner_pp);
this->fill_surfaces->append(offset2_ex(union_ex(inner_pp), float(-min_perimeter_infill_spacing / 2.), float(insert + min_perimeter_infill_spacing / 2.)), stInternal);
append(*this->fill_no_overlap, offset2_ex(union_ex(inner_pp), float(-min_perimeter_infill_spacing / 2.), float(+min_perimeter_infill_spacing / 2.)));
}
// Thanks, Cura developers, for implementing an algorithm for generating perimeters with variable width (Arachne) that is based on the paper
// "A framework for adaptive width control of dense contour-parallel toolpaths in fused deposition modeling"
void PerimeterGenerator::process_arachne()
@ -1308,8 +1341,9 @@ void PerimeterGenerator::process_arachne()
// extra perimeters for each one
for (const Surface& surface : this->slices->surfaces) {
// detect how many perimeters must be generated for this island
int loop_number = this->config->wall_loops + surface.extra_perimeters - 1; // 0-indexed loops
if (loop_number > 0 && this->object_config->only_one_wall_first_layer && layer_id == 0)
int loop_number = this->config->wall_loops + surface.extra_perimeters - 1; // 0-indexed loops
if (loop_number > 0 && this->object_config->only_one_wall_first_layer && layer_id == 0 ||
(this->object_config->top_one_wall_type == TopOneWallType::Topmost && this->upper_slices == nullptr))
loop_number = 0;
ExPolygons last = offset_ex(surface.expolygon.simplify_p(surface_simplify_resolution), -float(ext_perimeter_width / 2. - ext_perimeter_spacing / 2.));
@ -1334,10 +1368,76 @@ void PerimeterGenerator::process_arachne()
input_params.wall_distribution_count = this->object_config->wall_distribution_count.value;
}
int remain_loops = -1;
if (this->object_config->top_one_wall_type == TopOneWallType::Alltop) {
if (this->upper_slices != nullptr)
remain_loops = loop_number - 1;
loop_number = 0;
}
Arachne::WallToolPaths wallToolPaths(last_p, ext_perimeter_spacing, perimeter_spacing, coord_t(loop_number + 1), 0, layer_height, input_params);
std::vector<Arachne::VariableWidthLines> perimeters = wallToolPaths.getToolPaths();
loop_number = int(perimeters.size()) - 1;
//BBS: top one wall for arachne
ExPolygons infill_contour = union_ex(wallToolPaths.getInnerContour());
ExPolygons inner_infill_contour;
if( remain_loops >= 0 )
{
ExPolygons the_layer_surface = infill_contour;
// BBS: get boungding box of last
BoundingBox infill_contour_box = get_extents(infill_contour);
infill_contour_box.offset(SCALED_EPSILON);
// BBS: get the Polygons upper the polygon this layer
Polygons upper_polygons_series_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*this->upper_slices, infill_contour_box);
infill_contour = diff_ex(infill_contour, upper_polygons_series_clipped);
coord_t perimeter_width = this->perimeter_flow.scaled_width();
//BBS: add bridge area
if (this->lower_slices != nullptr) {
BoundingBox infill_contour_box = get_extents(infill_contour);
infill_contour_box.offset(SCALED_EPSILON);
// BBS: get the Polygons below the polygon this layer
Polygons lower_polygons_series_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*this->lower_slices, infill_contour_box);
ExPolygons bridge_area = offset_ex(diff_ex(infill_contour, lower_polygons_series_clipped), std::max(ext_perimeter_spacing, perimeter_width));
infill_contour = diff_ex(infill_contour, bridge_area);
}
//BBS: filter small area and extend top surface a bit to hide the wall line
double min_width_top_surface = std::max(double(ext_perimeter_spacing / 4 + 10), double(perimeter_width / 4));
infill_contour = offset2_ex(infill_contour, -min_width_top_surface, min_width_top_surface + perimeter_width);
//BBS: get the inner surface that not export to top
ExPolygons surface_not_export_to_top = diff_ex(the_layer_surface, infill_contour);
//BBS: get real top surface
infill_contour = intersection_ex(infill_contour, the_layer_surface);
Polygons surface_not_export_to_top_p = to_polygons(surface_not_export_to_top);
Arachne::WallToolPaths innerWallToolPaths(surface_not_export_to_top_p, ext_perimeter_spacing, perimeter_spacing, coord_t(remain_loops + 1), 0, layer_height, input_params);
std::vector<Arachne::VariableWidthLines> perimeters_inner = innerWallToolPaths.getToolPaths();
remain_loops = int(perimeters_inner.size()) - 1;
//BBS: set wall's perporsity
for (int perimeter_idx = 0; perimeter_idx < perimeters_inner.size(); perimeter_idx++) {
if (perimeters_inner[perimeter_idx].empty())
continue;
for (Arachne::ExtrusionLine &wall : perimeters_inner[perimeter_idx]) {
//BBS: 0 means outer wall
wall.inset_idx++;
wall.is_odd = !wall.is_odd;
}
}
perimeters.insert(perimeters.end(), perimeters_inner.begin(), perimeters_inner.end());
inner_infill_contour = union_ex(innerWallToolPaths.getInnerContour());
}
#ifdef ARACHNE_DEBUG
{
static int iRun = 0;
@ -1503,44 +1603,17 @@ void PerimeterGenerator::process_arachne()
if (ExtrusionEntityCollection extrusion_coll = traverse_extrusions(*this, ordered_extrusions); !extrusion_coll.empty())
this->loops->append(extrusion_coll);
ExPolygons infill_contour = union_ex(wallToolPaths.getInnerContour());
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.
// create one more offset to be used as boundary for fill
// we offset by half the perimeter spacing (to get to the actual infill boundary)
// and then we offset back and forth by half the infill spacing to only consider the
// non-collapsing regions
coord_t inset =
(loop_number < 0) ? 0 :
(loop_number == 0) ?
// one loop
ext_perimeter_spacing :
// two or more loops?
perimeter_spacing;
inset = coord_t(scale_(this->config->infill_wall_overlap.get_abs_value(unscale<double>(inset))));
Polygons pp;
for (ExPolygon& ex : infill_contour)
ex.simplify_p(m_scaled_resolution, &pp);
// collapse too narrow infill areas
const auto min_perimeter_infill_spacing = coord_t(solid_infill_spacing * (1. - INSET_OVERLAP_TOLERANCE));
// append infill areas to fill_surfaces
this->fill_surfaces->append(
offset2_ex(
union_ex(pp),
float(-min_perimeter_infill_spacing / 2.),
float(inset + min_perimeter_infill_spacing / 2.)),
stInternal);
add_infill_contour_for_arachne(infill_contour, loop_number, ext_perimeter_spacing, perimeter_spacing, min_perimeter_infill_spacing, spacing, false);
//BBS: add infill_contour of top one wall part
if( !inner_infill_contour.empty() )
add_infill_contour_for_arachne(inner_infill_contour, remain_loops, ext_perimeter_spacing, perimeter_spacing, min_perimeter_infill_spacing, spacing, true);
// BBS: get the no-overlap infill expolygons
{
append(*this->fill_no_overlap, offset2_ex(
union_ex(pp),
float(-min_perimeter_infill_spacing / 2.),
float(+min_perimeter_infill_spacing / 2.)));
}
}
}

View File

@ -69,6 +69,8 @@ public:
void process_classic();
void process_arachne();
void add_infill_contour_for_arachne( ExPolygons infill_contour, int loops, coord_t ext_perimeter_spacing, coord_t perimeter_spacing, coord_t min_perimeter_infill_spacing, coord_t spacing, bool is_inner_part );
double ext_mm3_per_mm() const { return m_ext_mm3_per_mm; }
double mm3_per_mm() const { return m_mm3_per_mm; }
double mm3_per_mm_overhang() const { return m_mm3_per_mm_overhang; }

View File

@ -777,7 +777,7 @@ static std::vector<std::string> s_Preset_print_options {
"detect_narrow_internal_solid_infill",
"gcode_add_line_number", "enable_arc_fitting", "infill_combination", /*"adaptive_layer_height",*/
"support_bottom_interface_spacing", "enable_overhang_speed", "overhang_1_4_speed", "overhang_2_4_speed", "overhang_3_4_speed", "overhang_4_4_speed",
"initial_layer_infill_speed", "only_one_wall_top", "only_one_wall_first_layer",
"initial_layer_infill_speed", "top_one_wall_type", "only_one_wall_first_layer",
"timelapse_type", "internal_bridge_support_thickness",
"wall_generator", "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle",
"wall_distribution_count", "min_feature_size", "min_bead_width", "post_process",

View File

@ -145,6 +145,14 @@ static t_config_enum_values s_keys_map_IroningType {
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(IroningType)
//BBS:
static t_config_enum_values s_keys_map_TopOneWallType {
{"not apply", int(TopOneWallType::None)},
{"all top", int(TopOneWallType::Alltop)},
{"topmost", int(TopOneWallType::Topmost)}
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(TopOneWallType)
//BBS
static t_config_enum_values s_keys_map_WallInfillOrder {
{ "inner wall/outer wall/infill", int(WallInfillOrder::InnerOuterInfill) },
@ -721,11 +729,18 @@ void PrintConfigDef::init_fff_params()
def->mode = comDevelop;
def->set_default_value(new ConfigOptionFloat(1));
def = this->add("only_one_wall_top", coBool);
def->label = L("Only one wall on top surfaces");
def = this->add("top_one_wall_type", coEnum);
def->label = L("Only one wall type");
def->category = L("Quality");
def->tooltip = L("Use only one wall on flat top surface, to give more space to the top infill pattern");
def->set_default_value(new ConfigOptionBool(false));
def->tooltip = L("Use only one wall on flat top surface, to give more space to the top infill pattern. Could be applyed on topmost surface or all top surface.");
def->enum_keys_map = &ConfigOptionEnum<TopOneWallType>::get_enum_values();
def->enum_values.push_back("not apply");
def->enum_values.push_back("all top");
def->enum_values.push_back("topmost");
def->enum_labels.push_back(L("Not apply"));
def->enum_labels.push_back(L("Top surfaces"));
def->enum_labels.push_back(L("Topmost surface"));
def->set_default_value(new ConfigOptionEnum<TopOneWallType>(TopOneWallType::Alltop));
def = this->add("only_one_wall_first_layer", coBool);
def->label = L("Only one wall on first layer");
@ -4240,7 +4255,7 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
"remove_freq_sweep", "remove_bed_leveling", "remove_extrusion_calibration",
"support_transition_line_width", "support_transition_speed", "bed_temperature", "bed_temperature_initial_layer",
"can_switch_nozzle_type", "can_add_auxiliary_fan", "extra_flush_volume", "spaghetti_detector", "adaptive_layer_height",
"z_hop_type","nozzle_hrc","chamber_temperature"
"z_hop_type","nozzle_hrc","chamber_temperature","only_one_wall_top"
};
if (ignore.find(opt_key) != ignore.end()) {

View File

@ -174,6 +174,13 @@ enum class PerimeterGeneratorType
Arachne
};
enum class TopOneWallType
{
None,
Alltop,
Topmost
};
// BBS
enum OverhangFanThreshold {
Overhang_threshold_none = 0,
@ -304,7 +311,7 @@ CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(ForwardCompatibilitySubstitutionRule)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(PrintHostType)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(AuthorizationType)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(PerimeterGeneratorType)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(TopOneWallType)
#undef CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS
// Defines each and every confiuration option of Slic3r, including the properties of the GUI dialogs.
@ -704,7 +711,7 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionInt, wall_distribution_count))
((ConfigOptionPercent, min_feature_size))
((ConfigOptionPercent, min_bead_width))
((ConfigOptionBool, only_one_wall_top))
((ConfigOptionEnum<TopOneWallType>, top_one_wall_type))
((ConfigOptionBool, only_one_wall_first_layer))
// OrcaSlicer
((ConfigOptionPercent, seam_gap))

View File

@ -709,7 +709,7 @@ bool PrintObject::invalidate_state_by_config_options(
}
} else if (
opt_key == "wall_loops"
|| opt_key == "only_one_wall_top"
|| opt_key == "top_one_wall_type"
|| opt_key == "only_one_wall_first_layer"
|| opt_key == "initial_layer_line_width"
|| opt_key == "inner_wall_line_width"

View File

@ -682,8 +682,6 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
"min_feature_size", "min_bead_width", "wall_distribution_count" })
toggle_line(el, have_arachne);
toggle_field("detect_thin_wall", !have_arachne);
//toggle_field("enable_overhang_speed", !have_arachne);
toggle_field("only_one_wall_top", !have_arachne);
PresetBundle *preset_bundle = wxGetApp().preset_bundle;
// OrcaSlicer

View File

@ -1881,7 +1881,7 @@ void TabPrint::build()
optgroup->append_single_option_line("thick_bridges");
optgroup->append_single_option_line("top_solid_infill_flow_ratio");
optgroup->append_single_option_line("initial_layer_flow_ratio");
optgroup->append_single_option_line("only_one_wall_top");
optgroup->append_single_option_line("top_one_wall_type");
optgroup->append_single_option_line("only_one_wall_first_layer");
optgroup->append_single_option_line("detect_overhang_wall");
optgroup->append_single_option_line("reduce_crossing_wall");