mirror of
https://github.com/QIDITECH/QIDISlicer.git
synced 2026-01-30 23:48:44 +03:00
Revert "Merge branch 'master' of https://github.com/QIDITECH/QIDISlicer"
This reverts commit8de170e272, reversing changes made toec37905b75.
This commit is contained in:
@@ -2862,8 +2862,7 @@ std::string GCodeGenerator::extrude_loop(const ExtrusionLoop &loop_src, const GC
|
||||
// if polyline was shorter than the clipping distance we'd get a null polyline, so
|
||||
// we discard it in that case.
|
||||
if (m_enable_loop_clipping)
|
||||
//Y21
|
||||
clip_end(smooth_path, scaled<double>(EXTRUDER_CONFIG(nozzle_diameter)) * (m_config.seam_gap.value / 100), scaled<double>(min_gcode_segment_length));
|
||||
clip_end(smooth_path, scaled<double>(EXTRUDER_CONFIG(nozzle_diameter)) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER, scaled<double>(min_gcode_segment_length));
|
||||
|
||||
if (smooth_path.empty())
|
||||
return {};
|
||||
|
||||
@@ -79,8 +79,7 @@ std::string Wipe::wipe(GCodeGenerator &gcodegen, bool toolchange)
|
||||
static constexpr const std::string_view wipe_retract_comment = "wipe and retract"sv;
|
||||
|
||||
// Remaining quantized retraction length.
|
||||
//w15
|
||||
if (double retract_length = extruder.retract_to_go(toolchange ? extruder.retract_length_toolchange() * 0.95 : extruder.retract_length()) * 0.95;
|
||||
if (double retract_length = extruder.retract_to_go(toolchange ? extruder.retract_length_toolchange() : extruder.retract_length());
|
||||
retract_length > 0 && this->has_path()) {
|
||||
// Delayed emitting of a wipe start tag.
|
||||
bool wiped = false;
|
||||
@@ -198,11 +197,11 @@ std::string Wipe::wipe(GCodeGenerator &gcodegen, bool toolchange)
|
||||
prev = p;
|
||||
auto end = this->path().end();
|
||||
for (; it != end && ! done; ++ it) {
|
||||
//w15
|
||||
if (wipe_dist >= wipe_dist_max)
|
||||
break;
|
||||
p = gcodegen.point_to_gcode(it->point + m_offset);
|
||||
if (p != prev) {
|
||||
//w15
|
||||
if (wipe_dist >= wipe_dist_max)
|
||||
break;
|
||||
start_wipe();
|
||||
if (it->linear() ?
|
||||
wipe_linear(prev, p) :
|
||||
|
||||
@@ -106,56 +106,31 @@ 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;
|
||||
//w16
|
||||
const ExPolygons *upper_slices = this->layer()->upper_layer ? &this->layer()->upper_layer->lslices : nullptr;
|
||||
// Cache for offsetted lower_slices
|
||||
Polygons lower_layer_polygons_cache;
|
||||
Polygons upper_layer_polygons_cache;
|
||||
|
||||
for (const Surface &surface : slices) {
|
||||
auto perimeters_begin = uint32_t(m_perimeters.size());
|
||||
auto gap_fills_begin = uint32_t(m_thin_fills.size());
|
||||
auto fill_expolygons_begin = uint32_t(fill_expolygons.size());
|
||||
if (this->layer()->object()->config().perimeter_generator.value == PerimeterGeneratorType::Arachne && !spiral_vase)
|
||||
|
||||
//w16
|
||||
if (this->layer()->object()->config().top_one_wall_type == TopOneWallType::Alltop)
|
||||
PerimeterGenerator::process_with_one_wall_arachne(
|
||||
PerimeterGenerator::process_arachne(
|
||||
// input:
|
||||
params,
|
||||
surface,
|
||||
lower_slices,
|
||||
//w16
|
||||
upper_slices,
|
||||
lower_layer_polygons_cache,
|
||||
upper_layer_polygons_cache,
|
||||
// output:
|
||||
m_perimeters,
|
||||
m_thin_fills,
|
||||
fill_expolygons);
|
||||
else
|
||||
PerimeterGenerator::process_arachne(
|
||||
// input:
|
||||
params,
|
||||
surface,
|
||||
lower_slices,
|
||||
upper_slices,
|
||||
lower_layer_polygons_cache,
|
||||
// output:
|
||||
m_perimeters,
|
||||
m_thin_fills,
|
||||
fill_expolygons);
|
||||
|
||||
else
|
||||
PerimeterGenerator::process_classic(
|
||||
// input:
|
||||
params,
|
||||
surface,
|
||||
lower_slices,
|
||||
//w16
|
||||
upper_slices,
|
||||
lower_layer_polygons_cache,
|
||||
upper_layer_polygons_cache,
|
||||
// output:
|
||||
m_perimeters,
|
||||
m_thin_fills,
|
||||
|
||||
@@ -1069,37 +1069,6 @@ std::tuple<std::vector<ExtrusionPaths>, Polygons> generate_extra_perimeters_over
|
||||
return {extra_perims, diff(inset_overhang_area, inset_overhang_area_left_unfilled)};
|
||||
}
|
||||
|
||||
//w16
|
||||
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,
|
||||
const Parameters ¶ms,
|
||||
ExPolygons &infill_areas,
|
||||
ExPolygons & out_fill_expolygons)
|
||||
{
|
||||
if (offset_ex(infill_contour, -float(spacing / 2.)).empty()) {
|
||||
infill_contour.clear();
|
||||
}
|
||||
coord_t insert = (loops < 0) ? 0 : ext_perimeter_spacing;
|
||||
if (is_inner_part || loops > 0)
|
||||
insert = perimeter_spacing;
|
||||
|
||||
insert = coord_t(scale_(params.config.get_abs_value("infill_overlap", unscale<double>(insert))));
|
||||
Polygons inner_pp;
|
||||
for (ExPolygon &ex : infill_contour)
|
||||
ex.simplify_p(params.scaled_resolution, &inner_pp);
|
||||
ExPolygons inner_union = union_ex(inner_pp);
|
||||
float offset1 = -min_perimeter_infill_spacing / 2.;
|
||||
float offset2 = insert + min_perimeter_infill_spacing / 2.;
|
||||
infill_areas = offset2_ex(inner_union, offset1, offset2);
|
||||
append(out_fill_expolygons, std::move(infill_areas));
|
||||
}
|
||||
|
||||
|
||||
// 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(
|
||||
@@ -1107,7 +1076,6 @@ 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:
|
||||
@@ -1143,10 +1111,6 @@ void PerimeterGenerator::process_arachne(
|
||||
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);
|
||||
|
||||
//w16
|
||||
if (upper_slices == nullptr && params.object_config.top_one_wall_type == TopOneWallType::Onlytopmost)
|
||||
loop_number = 0;
|
||||
|
||||
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;
|
||||
@@ -1353,387 +1317,13 @@ void PerimeterGenerator::process_arachne(
|
||||
append(out_fill_expolygons, std::move(infill_areas));
|
||||
}
|
||||
|
||||
void PerimeterGenerator::process_with_one_wall_arachne(
|
||||
// Inputs:
|
||||
const Parameters ¶ms,
|
||||
const Surface &surface,
|
||||
const ExPolygons *lower_slices,
|
||||
//w16
|
||||
const ExPolygons *upper_slices,
|
||||
// Cache:
|
||||
Polygons &lower_slices_polygons_cache,
|
||||
Polygons &upper_slices_polygons_cache,
|
||||
// Output:
|
||||
// Loops with the external thin walls
|
||||
ExtrusionEntityCollection &out_loops,
|
||||
// Gaps without the thin walls
|
||||
ExtrusionEntityCollection & /* out_gap_fill */,
|
||||
// Infills without the gap fills
|
||||
ExPolygons &out_fill_expolygons)
|
||||
{
|
||||
// other perimeters
|
||||
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();
|
||||
coord_t ext_perimeter_spacing2 = scaled<coord_t>(0.5f * (params.ext_perimeter_flow.spacing() + params.perimeter_flow.spacing()));
|
||||
// solid infill
|
||||
coord_t solid_infill_spacing = params.solid_infill_flow.scaled_spacing();
|
||||
|
||||
// prepare grown lower layer slices for overhang detection
|
||||
if (params.config.overhangs && lower_slices != nullptr && lower_slices_polygons_cache.empty()) {
|
||||
// We consider overhang any part where the entire nozzle diameter is not supported by the
|
||||
// lower layer, so we take lower slices and offset them by half the nozzle diameter used
|
||||
// in the current layer
|
||||
double nozzle_diameter = params.print_config.nozzle_diameter.get_at(params.config.perimeter_extruder-1);
|
||||
lower_slices_polygons_cache = offset(*lower_slices, float(scale_(+nozzle_diameter/2)));
|
||||
}
|
||||
if (params.config.overhangs && upper_slices != nullptr && upper_slices_polygons_cache.empty()) {
|
||||
double upper_nozzle_diameter = params.print_config.nozzle_diameter.get_at(params.config.perimeter_extruder - 1);
|
||||
upper_slices_polygons_cache = offset(*upper_slices, float(scale_(+upper_nozzle_diameter / 2)));
|
||||
}
|
||||
|
||||
|
||||
// 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 remain_loops = -1;
|
||||
if (params.object_config.top_one_wall_type == TopOneWallType::Alltop) {
|
||||
if (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, params.layer_height, params.object_config, params.print_config);
|
||||
std::vector<Arachne::VariableWidthLines> perimeters = wallToolPaths.getToolPaths();
|
||||
loop_number = int(perimeters.size()) - 1;
|
||||
|
||||
|
||||
|
||||
//w16
|
||||
ExPolygons infill_contour = union_ex(wallToolPaths.getInnerContour());
|
||||
ExPolygons inner_infill_contour;
|
||||
|
||||
if (remain_loops >= 0) {
|
||||
|
||||
ExPolygons the_layer_surface = infill_contour;
|
||||
BoundingBox infill_contour_box = get_extents(infill_contour);
|
||||
infill_contour_box.offset(SCALED_EPSILON);
|
||||
Polygons upper_polygons_series_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(upper_slices_polygons_cache,
|
||||
infill_contour_box);
|
||||
|
||||
infill_contour = diff_ex(infill_contour, upper_polygons_series_clipped);
|
||||
|
||||
coord_t perimeter_width = params.perimeter_flow.scaled_width();
|
||||
if (lower_slices != nullptr) {
|
||||
BoundingBox infill_contour_box = get_extents(infill_contour);
|
||||
infill_contour_box.offset(SCALED_EPSILON);
|
||||
Polygons lower_polygons_series_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(lower_slices_polygons_cache,
|
||||
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);
|
||||
}
|
||||
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);
|
||||
|
||||
ExPolygons surface_not_export_to_top = diff_ex(the_layer_surface, infill_contour);
|
||||
|
||||
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, perimeter_spacing, perimeter_spacing,
|
||||
coord_t(remain_loops + 1), 0, params.layer_height, params.object_config, params.print_config);
|
||||
|
||||
std::vector<Arachne::VariableWidthLines> perimeters_inner = innerWallToolPaths.getToolPaths();
|
||||
remain_loops = int(perimeters_inner.size()) - 1;
|
||||
if (!perimeters.empty()) {
|
||||
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]) {
|
||||
wall.inset_idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
perimeters.insert(perimeters.end(), perimeters_inner.begin(), perimeters_inner.end());
|
||||
|
||||
inner_infill_contour = union_ex(innerWallToolPaths.getInnerContour());
|
||||
}
|
||||
|
||||
#ifdef ARACHNE_DEBUG
|
||||
{
|
||||
static int iRun = 0;
|
||||
export_perimeters_to_svg(debug_out_path("arachne-perimeters-%d-%d.svg", layer_id, iRun++), to_polygons(last), perimeters, union_ex(wallToolPaths.getInnerContour()));
|
||||
}
|
||||
#endif
|
||||
|
||||
// All closed ExtrusionLine should have the same the first and the last point.
|
||||
// But in rare cases, Arachne produce ExtrusionLine marked as closed but without
|
||||
// equal the first and the last point.
|
||||
assert([&perimeters = std::as_const(perimeters)]() -> bool {
|
||||
for (const Arachne::VariableWidthLines &perimeter : perimeters)
|
||||
for (const Arachne::ExtrusionLine &el : perimeter)
|
||||
if (el.is_closed && el.junctions.front().p != el.junctions.back().p)
|
||||
return false;
|
||||
return true;
|
||||
}());
|
||||
|
||||
int start_perimeter = int(perimeters.size()) - 1;
|
||||
int end_perimeter = -1;
|
||||
int direction = -1;
|
||||
|
||||
if (params.config.external_perimeters_first) {
|
||||
start_perimeter = 0;
|
||||
end_perimeter = int(perimeters.size());
|
||||
direction = 1;
|
||||
}
|
||||
|
||||
std::vector<Arachne::ExtrusionLine *> all_extrusions;
|
||||
for (int perimeter_idx = start_perimeter; perimeter_idx != end_perimeter; perimeter_idx += direction) {
|
||||
if (perimeters[perimeter_idx].empty())
|
||||
continue;
|
||||
for (Arachne::ExtrusionLine &wall : perimeters[perimeter_idx])
|
||||
all_extrusions.emplace_back(&wall);
|
||||
}
|
||||
|
||||
// Find topological order with constraints from extrusions_constrains.
|
||||
std::vector<size_t> blocked(all_extrusions.size(), 0); // Value indicating how many extrusions it is blocking (preceding extrusions) an extrusion.
|
||||
std::vector<std::vector<size_t>> blocking(all_extrusions.size()); // Each extrusion contains a vector of extrusions that are blocked by this extrusion.
|
||||
ankerl::unordered_dense::map<const Arachne::ExtrusionLine *, size_t> map_extrusion_to_idx;
|
||||
for (size_t idx = 0; idx < all_extrusions.size(); idx++)
|
||||
map_extrusion_to_idx.emplace(all_extrusions[idx], idx);
|
||||
|
||||
Arachne::WallToolPaths::ExtrusionLineSet extrusions_constrains = Arachne::WallToolPaths::getRegionOrder(all_extrusions, params.config.external_perimeters_first);
|
||||
for (auto [before, after] : extrusions_constrains) {
|
||||
auto after_it = map_extrusion_to_idx.find(after);
|
||||
++blocked[after_it->second];
|
||||
blocking[map_extrusion_to_idx.find(before)->second].emplace_back(after_it->second);
|
||||
}
|
||||
|
||||
std::vector<bool> processed(all_extrusions.size(), false); // Indicate that the extrusion was already processed.
|
||||
Point current_position = all_extrusions.empty() ? Point::Zero() : all_extrusions.front()->junctions.front().p; // Some starting position.
|
||||
std::vector<PerimeterGeneratorArachneExtrusion> ordered_extrusions; // To store our result in. At the end we'll std::swap.
|
||||
ordered_extrusions.reserve(all_extrusions.size());
|
||||
|
||||
while (ordered_extrusions.size() < all_extrusions.size()) {
|
||||
size_t best_candidate = 0;
|
||||
double best_distance_sqr = std::numeric_limits<double>::max();
|
||||
bool is_best_closed = false;
|
||||
|
||||
std::vector<size_t> available_candidates;
|
||||
for (size_t candidate = 0; candidate < all_extrusions.size(); ++candidate) {
|
||||
if (processed[candidate] || blocked[candidate])
|
||||
continue; // Not a valid candidate.
|
||||
available_candidates.push_back(candidate);
|
||||
}
|
||||
|
||||
std::sort(available_candidates.begin(), available_candidates.end(), [&all_extrusions](const size_t a_idx, const size_t b_idx) -> bool {
|
||||
return all_extrusions[a_idx]->is_closed < all_extrusions[b_idx]->is_closed;
|
||||
});
|
||||
|
||||
for (const size_t candidate_path_idx : available_candidates) {
|
||||
auto& path = all_extrusions[candidate_path_idx];
|
||||
|
||||
if (path->junctions.empty()) { // No vertices in the path. Can't find the start position then or really plan it in. Put that at the end.
|
||||
if (best_distance_sqr == std::numeric_limits<double>::max()) {
|
||||
best_candidate = candidate_path_idx;
|
||||
is_best_closed = path->is_closed;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const Point candidate_position = path->junctions.front().p;
|
||||
double distance_sqr = (current_position - candidate_position).cast<double>().norm();
|
||||
if (distance_sqr < best_distance_sqr) { // Closer than the best candidate so far.
|
||||
if (path->is_closed || (!path->is_closed && best_distance_sqr != std::numeric_limits<double>::max()) || (!path->is_closed && !is_best_closed)) {
|
||||
best_candidate = candidate_path_idx;
|
||||
best_distance_sqr = distance_sqr;
|
||||
is_best_closed = path->is_closed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto &best_path = all_extrusions[best_candidate];
|
||||
ordered_extrusions.push_back({best_path, best_path->is_contour(), false});
|
||||
processed[best_candidate] = true;
|
||||
for (size_t unlocked_idx : blocking[best_candidate])
|
||||
blocked[unlocked_idx]--;
|
||||
|
||||
if (!best_path->junctions.empty()) { //If all paths were empty, the best path is still empty. We don't upate the current position then.
|
||||
if(best_path->is_closed)
|
||||
current_position = best_path->junctions[0].p; //We end where we started.
|
||||
else
|
||||
current_position = best_path->junctions.back().p; //Pick the other end from where we started.
|
||||
}
|
||||
}
|
||||
|
||||
if (params.layer_id > 0 && params.config.fuzzy_skin != FuzzySkinType::None) {
|
||||
std::vector<PerimeterGeneratorArachneExtrusion *> closed_loop_extrusions;
|
||||
for (PerimeterGeneratorArachneExtrusion &extrusion : ordered_extrusions)
|
||||
if (extrusion.extrusion->inset_idx == 0) {
|
||||
if (extrusion.extrusion->is_closed && params.config.fuzzy_skin == FuzzySkinType::External) {
|
||||
closed_loop_extrusions.emplace_back(&extrusion);
|
||||
} else {
|
||||
extrusion.fuzzify = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (params.config.fuzzy_skin == FuzzySkinType::External) {
|
||||
ClipperLib_Z::Paths loops_paths;
|
||||
loops_paths.reserve(closed_loop_extrusions.size());
|
||||
for (const auto &cl_extrusion : closed_loop_extrusions) {
|
||||
assert(cl_extrusion->extrusion->junctions.front() == cl_extrusion->extrusion->junctions.back());
|
||||
size_t loop_idx = &cl_extrusion - &closed_loop_extrusions.front();
|
||||
ClipperLib_Z::Path loop_path;
|
||||
loop_path.reserve(cl_extrusion->extrusion->junctions.size() - 1);
|
||||
for (auto junction_it = cl_extrusion->extrusion->junctions.begin(); junction_it != std::prev(cl_extrusion->extrusion->junctions.end()); ++junction_it)
|
||||
loop_path.emplace_back(junction_it->p.x(), junction_it->p.y(), loop_idx);
|
||||
loops_paths.emplace_back(loop_path);
|
||||
}
|
||||
|
||||
ClipperLib_Z::Clipper clipper;
|
||||
clipper.AddPaths(loops_paths, ClipperLib_Z::ptSubject, true);
|
||||
ClipperLib_Z::PolyTree loops_polytree;
|
||||
clipper.Execute(ClipperLib_Z::ctUnion, loops_polytree, ClipperLib_Z::pftEvenOdd, ClipperLib_Z::pftEvenOdd);
|
||||
|
||||
for (const ClipperLib_Z::PolyNode *child_node : loops_polytree.Childs) {
|
||||
// The whole contour must have the same index.
|
||||
coord_t polygon_idx = child_node->Contour.front().z();
|
||||
bool has_same_idx = std::all_of(child_node->Contour.begin(), child_node->Contour.end(),
|
||||
[&polygon_idx](const ClipperLib_Z::IntPoint &point) -> bool { return polygon_idx == point.z(); });
|
||||
if (has_same_idx)
|
||||
closed_loop_extrusions[polygon_idx]->fuzzify = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ExtrusionEntityCollection extrusion_coll = traverse_extrusions(params, lower_slices_polygons_cache, ordered_extrusions); !extrusion_coll.empty())
|
||||
out_loops.append(extrusion_coll);
|
||||
|
||||
|
||||
//w16
|
||||
if (remain_loops >= 0) {
|
||||
const coord_t spacing = (perimeters.size() == 1) ? ext_perimeter_spacing2 : perimeter_spacing;
|
||||
if (offset_ex(infill_contour, -float(spacing / 2.)).empty())
|
||||
infill_contour.clear();
|
||||
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_(params.config.get_abs_value("infill_overlap", unscale<double>(inset))));
|
||||
Polygons pp;
|
||||
for (ExPolygon &ex : infill_contour)
|
||||
ex.simplify_p(params.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
|
||||
ExPolygons infill_areas = offset2_ex(union_ex(pp), float(-min_perimeter_infill_spacing / 2.),
|
||||
float(inset + min_perimeter_infill_spacing / 2.));
|
||||
// ExPolygons infill_areas;
|
||||
|
||||
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
|
||||
auto [extra_perimeters, filled_area] = generate_extra_perimeters_over_overhangs(infill_areas, lower_slices_polygons_cache,
|
||||
loop_number + 1, params.overhang_flow,
|
||||
params.scaled_resolution, params.object_config,
|
||||
params.print_config);
|
||||
if (!extra_perimeters.empty()) {
|
||||
ExtrusionEntityCollection &this_islands_perimeters = static_cast<ExtrusionEntityCollection &>(*out_loops.entities.back());
|
||||
ExtrusionEntitiesPtr old_entities;
|
||||
old_entities.swap(this_islands_perimeters.entities);
|
||||
for (ExtrusionPaths &paths : extra_perimeters)
|
||||
this_islands_perimeters.append(std::move(paths));
|
||||
append(this_islands_perimeters.entities, old_entities);
|
||||
infill_areas = diff_ex(infill_areas, filled_area);
|
||||
}
|
||||
}
|
||||
|
||||
inset = (loop_number < 0) ? 0 :
|
||||
(loop_number == 0) ?
|
||||
// one loop
|
||||
ext_perimeter_spacing :
|
||||
// two or more loops?
|
||||
perimeter_spacing;
|
||||
|
||||
inset = coord_t(scale_(params.config.get_abs_value("infill_overlap", unscale<double>(inset))));
|
||||
for (ExPolygon &ex : infill_contour)
|
||||
ex.simplify_p(params.scaled_resolution, &pp);
|
||||
// collapse too narrow infill areas
|
||||
|
||||
|
||||
if (remain_loops >= 0) {
|
||||
add_infill_contour_for_arachne(infill_contour, loop_number, ext_perimeter_spacing, perimeter_spacing,
|
||||
min_perimeter_infill_spacing, spacing, true, params, infill_areas, out_fill_expolygons);
|
||||
}
|
||||
|
||||
|
||||
if (remain_loops >= 0) {
|
||||
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, params, infill_areas, out_fill_expolygons);
|
||||
}
|
||||
append(out_fill_expolygons, std::move(infill_areas));
|
||||
} else {
|
||||
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.
|
||||
|
||||
coord_t inset = (loop_number < 0) ? 0 : (loop_number == 0) ? ext_perimeter_spacing : perimeter_spacing;
|
||||
|
||||
inset = coord_t(scale_(params.config.get_abs_value("infill_overlap", unscale<double>(inset))));
|
||||
Polygons pp;
|
||||
for (ExPolygon &ex : infill_contour)
|
||||
ex.simplify_p(params.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
|
||||
ExPolygons infill_areas = offset2_ex(union_ex(pp), float(-min_perimeter_infill_spacing / 2.),
|
||||
float(inset + min_perimeter_infill_spacing / 2.));
|
||||
|
||||
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
|
||||
auto [extra_perimeters, filled_area] = generate_extra_perimeters_over_overhangs(infill_areas, lower_slices_polygons_cache,
|
||||
loop_number + 1, params.overhang_flow,
|
||||
params.scaled_resolution, params.object_config,
|
||||
params.print_config);
|
||||
if (!extra_perimeters.empty()) {
|
||||
ExtrusionEntityCollection &this_islands_perimeters = static_cast<ExtrusionEntityCollection &>(*out_loops.entities.back());
|
||||
ExtrusionEntitiesPtr old_entities;
|
||||
old_entities.swap(this_islands_perimeters.entities);
|
||||
for (ExtrusionPaths &paths : extra_perimeters)
|
||||
this_islands_perimeters.append(std::move(paths));
|
||||
append(this_islands_perimeters.entities, old_entities);
|
||||
infill_areas = diff_ex(infill_areas, filled_area);
|
||||
}
|
||||
}
|
||||
|
||||
append(out_fill_expolygons, std::move(infill_areas));
|
||||
}
|
||||
}
|
||||
|
||||
void PerimeterGenerator::process_classic(
|
||||
// Inputs:
|
||||
const Parameters ¶ms,
|
||||
const Surface &surface,
|
||||
const ExPolygons *lower_slices,
|
||||
//w16
|
||||
const ExPolygons *upper_slices,
|
||||
// Cache:
|
||||
Polygons &lower_slices_polygons_cache,
|
||||
Polygons &upper_layer_polygons_cache,
|
||||
// Output:
|
||||
// Loops with the external thin walls
|
||||
ExtrusionEntityCollection &out_loops,
|
||||
@@ -1781,11 +1371,6 @@ void PerimeterGenerator::process_classic(
|
||||
int loop_number = params.config.perimeters + surface.extra_perimeters - 1; // 0-indexed loops
|
||||
ExPolygons last = union_ex(surface.expolygon.simplify_p(params.scaled_resolution));
|
||||
ExPolygons gaps;
|
||||
//w16
|
||||
ExPolygons fill_clip;
|
||||
ExPolygons top_fills;
|
||||
if (loop_number > 0 && params.object_config.top_one_wall_type != TopOneWallType::None && upper_slices == nullptr)
|
||||
loop_number = 0;
|
||||
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
|
||||
@@ -1825,22 +1410,20 @@ void PerimeterGenerator::process_classic(
|
||||
//FIXME Is this offset correct if the line width of the inner perimeters differs
|
||||
// from the line width of the infill?
|
||||
coord_t distance = (i == 1) ? ext_perimeter_spacing2 : perimeter_spacing;
|
||||
//w16
|
||||
//offsets = params.config.thin_walls ?
|
||||
offsets = params.config.thin_walls ?
|
||||
// This path will ensure, that the perimeters do not overfill, as in
|
||||
// qidi3d/Slic3r GH #32, but with the cost of rounding the perimeters
|
||||
// excessively, creating gaps, which then need to be filled in by the not very
|
||||
// reliable gap fill algorithm.
|
||||
// Also the offset2(perimeter, -x, x) may sometimes lead to a perimeter, which is larger than
|
||||
// the original.
|
||||
//offset2_ex(last,
|
||||
// - float(distance + min_spacing / 2. - 1.),
|
||||
// float(min_spacing / 2. - 1.)) :
|
||||
offset2_ex(last,
|
||||
- float(distance + min_spacing / 2. - 1.),
|
||||
float(min_spacing / 2. - 1.)) :
|
||||
// If "detect thin walls" is not enabled, this paths will be entered, which
|
||||
// leads to overflows, as in qidi3d/Slic3r GH #32
|
||||
// offset_ex(last, - float(distance));
|
||||
offset_ex(last, - float(distance));
|
||||
// look for gaps
|
||||
offsets = offset2_ex(last, -float(distance + min_spacing / 2. - 1.), float(min_spacing / 2. - 1.));
|
||||
if (has_gap_fill)
|
||||
// not using safety offset here would "detect" very narrow gaps
|
||||
// (but still long enough to escape the area threshold) that gap fill
|
||||
@@ -1878,54 +1461,6 @@ void PerimeterGenerator::process_classic(
|
||||
}
|
||||
}
|
||||
last = std::move(offsets);
|
||||
|
||||
//w16
|
||||
if (i == 0 && i != loop_number && params.object_config.top_one_wall_type == TopOneWallType::Alltop &&
|
||||
upper_slices != nullptr) {
|
||||
coord_t offset_top_surface = scale_(
|
||||
1.5 * (params.config.perimeters.value == 0 ?
|
||||
0. :
|
||||
unscaled(double(ext_perimeter_width + perimeter_spacing * int(int(params.config.perimeters.value) - int(1))))));
|
||||
if (offset_top_surface > 0.9 * (params.config.perimeters.value <= 1 ? 0. : (perimeter_spacing * (params.config.perimeters.value - 1))))
|
||||
offset_top_surface -= coord_t(
|
||||
0.9 * (params.config.perimeters.value <= 1 ? 0. : (perimeter_spacing * (params.config.perimeters.value - 1))));
|
||||
else
|
||||
offset_top_surface = 0;
|
||||
double min_width_top_surface = (100) *
|
||||
std::max(double(ext_perimeter_spacing / 2 + 10), 1.0 * (double(perimeter_width)));
|
||||
BoundingBox last_box = get_extents(last);
|
||||
last_box.offset(SCALED_EPSILON);
|
||||
Polygons upper_polygons_series_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(upper_layer_polygons_cache,
|
||||
last_box);
|
||||
upper_polygons_series_clipped = offset(upper_polygons_series_clipped, min_width_top_surface);
|
||||
fill_clip = offset_ex(last, -double(ext_perimeter_spacing));
|
||||
ExPolygons grown_lower_slices;
|
||||
ExPolygons bridge_checker;
|
||||
if (lower_slices != nullptr) {
|
||||
Polygons lower_polygons_series_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(lower_slices_polygons_cache,
|
||||
last_box);
|
||||
|
||||
double bridge_offset = std::max(double(ext_perimeter_spacing), (double(perimeter_width)));
|
||||
bridge_checker = offset_ex(diff_ex(last, lower_polygons_series_clipped, ApplySafetyOffset::Yes), 1.5 * bridge_offset);
|
||||
}
|
||||
ExPolygons delete_bridge = diff_ex(last, bridge_checker, ApplySafetyOffset::Yes);
|
||||
|
||||
ExPolygons top_polygons = diff_ex(delete_bridge, upper_polygons_series_clipped, ApplySafetyOffset::Yes);
|
||||
ExPolygons temp_gap = diff_ex(top_polygons, fill_clip);
|
||||
ExPolygons inner_polygons = diff_ex(last,
|
||||
offset_ex(top_polygons, offset_top_surface + min_width_top_surface -
|
||||
double(ext_perimeter_spacing / 2)),
|
||||
ApplySafetyOffset::Yes);
|
||||
top_polygons = diff_ex(fill_clip, inner_polygons, ApplySafetyOffset::Yes);
|
||||
top_fills = union_ex(top_fills, top_polygons);
|
||||
double infill_spacing_unscaled = params.config.infill_extrusion_width.value; // this->config->sparse_infill_line_width.value;
|
||||
fill_clip = offset_ex(last, double(ext_perimeter_spacing / 2) - scale_(infill_spacing_unscaled / 2));
|
||||
last = intersection_ex(inner_polygons, last);
|
||||
if (has_gap_fill)
|
||||
last = union_ex(last, temp_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
|
||||
@@ -2055,10 +1590,6 @@ void PerimeterGenerator::process_classic(
|
||||
float(- inset - min_perimeter_infill_spacing / 2.),
|
||||
float(min_perimeter_infill_spacing / 2.));
|
||||
|
||||
ExPolygons top_infill_exp = intersection_ex(fill_clip, offset_ex(top_fills, double(ext_perimeter_spacing / 2)));
|
||||
|
||||
append(out_fill_expolygons, std::move(top_infill_exp));
|
||||
|
||||
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
|
||||
|
||||
@@ -70,11 +70,8 @@ void process_classic(
|
||||
const Parameters ¶ms,
|
||||
const Surface &surface,
|
||||
const ExPolygons *lower_slices,
|
||||
//w16
|
||||
const ExPolygons *upper_slices,
|
||||
// Cache:
|
||||
Polygons &lower_slices_polygons_cache,
|
||||
Polygons &upper_slices_polygons_cache,
|
||||
// Output:
|
||||
// Loops with the external thin walls
|
||||
ExtrusionEntityCollection &out_loops,
|
||||
@@ -84,32 +81,12 @@ void process_classic(
|
||||
ExPolygons &out_fill_expolygons);
|
||||
|
||||
void process_arachne(
|
||||
// Inputs:
|
||||
const Parameters ¶ms,
|
||||
const Surface & surface,
|
||||
const ExPolygons *lower_slices,
|
||||
//w16
|
||||
const ExPolygons *upper_slices,
|
||||
// Cache:
|
||||
Polygons &lower_slices_polygons_cache,
|
||||
// Output:
|
||||
// Loops with the external thin walls
|
||||
ExtrusionEntityCollection &out_loops,
|
||||
// Gaps without the thin walls
|
||||
ExtrusionEntityCollection &out_gap_fill,
|
||||
// Infills without the gap fills
|
||||
ExPolygons &out_fill_expolygons);
|
||||
|
||||
void process_with_one_wall_arachne(
|
||||
// Inputs:
|
||||
const Parameters ¶ms,
|
||||
const Surface &surface,
|
||||
const ExPolygons *lower_slices,
|
||||
//w16
|
||||
const ExPolygons *upper_slices,
|
||||
// Cache:
|
||||
Polygons &lower_slices_polygons_cache,
|
||||
Polygons &upper_slices_polygons_cache,
|
||||
// Output:
|
||||
// Loops with the external thin walls
|
||||
ExtrusionEntityCollection &out_loops,
|
||||
@@ -118,18 +95,6 @@ void process_with_one_wall_arachne(
|
||||
// Infills without the gap fills
|
||||
ExPolygons &out_fill_expolygons);
|
||||
|
||||
//w16
|
||||
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,
|
||||
const Parameters ¶ms,
|
||||
ExPolygons & infill_areas,
|
||||
ExPolygons & out_fill_expolygons);
|
||||
|
||||
ExtrusionMultiPath thick_polyline_to_multi_path(const ThickPolyline &thick_polyline, ExtrusionRole role, const Flow &flow, float tolerance, float merge_tolerance);
|
||||
|
||||
} // namespace PerimeterGenerator
|
||||
|
||||
@@ -473,10 +473,6 @@ static std::vector<std::string> s_Preset_print_options {
|
||||
,"first_layer_infill_speed"
|
||||
//w11
|
||||
,"detect_narrow_internal_solid_infill"
|
||||
//Y21
|
||||
,"seam_gap"
|
||||
//w16
|
||||
, "top_one_wall_type"
|
||||
};
|
||||
|
||||
static std::vector<std::string> s_Preset_filament_options {
|
||||
|
||||
@@ -142,8 +142,6 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
||||
"retract_restart_extra",
|
||||
"retract_restart_extra_toolchange",
|
||||
"retract_speed",
|
||||
//Y21
|
||||
"seam_gap",
|
||||
"single_extruder_multi_material_priming",
|
||||
"slowdown_below_layer_time",
|
||||
"solid_infill_acceleration",
|
||||
|
||||
@@ -236,12 +236,6 @@ static t_config_enum_values s_keys_map_PerimeterGeneratorType {
|
||||
};
|
||||
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(PerimeterGeneratorType)
|
||||
|
||||
//w16
|
||||
static t_config_enum_values s_keys_map_TopOneWallType{{"not apply", int(TopOneWallType::None)},
|
||||
{"all top", int(TopOneWallType::Alltop)},
|
||||
{"Only top most", int(TopOneWallType::Onlytopmost)}};
|
||||
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(TopOneWallType)
|
||||
|
||||
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)
|
||||
@@ -2539,15 +2533,6 @@ void PrintConfigDef::init_fff_params()
|
||||
def->mode = comSimple;
|
||||
def->set_default_value(new ConfigOptionEnum<SeamPosition>(spAligned));
|
||||
|
||||
//Y21
|
||||
def = this->add("seam_gap", coPercent);
|
||||
def->label = L("Seam gap");
|
||||
def->tooltip = L("In order to reduce the visibility of the seam in a closed loop extrusion, the loop is interrupted and shortened by a specified amount.\n" "This amount as a percentage of the current extruder diameter. The default value for this parameter is 15");
|
||||
def->sidetext = L("%");
|
||||
def->min = 0;
|
||||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionPercent(15));
|
||||
|
||||
def = this->add("staggered_inner_seams", coBool);
|
||||
def->label = L("Staggered inner seams");
|
||||
// TRN PrintSettings: "Staggered inner seams"
|
||||
@@ -3537,19 +3522,6 @@ void PrintConfigDef::init_fff_params()
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionEnum<PerimeterGeneratorType>(PerimeterGeneratorType::Arachne));
|
||||
|
||||
//w16
|
||||
def = this->add("top_one_wall_type", coEnum);
|
||||
def->label = L("Only one wall on top surfaces");
|
||||
def->category = L("Layers and Perimeters");
|
||||
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->set_enum<TopOneWallType>({
|
||||
{ "none", L("None") },
|
||||
{ "Only top most", L("Onlytopmost") },
|
||||
{ "all top", L("Alltop") }
|
||||
});
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionEnum<TopOneWallType>(TopOneWallType::None));
|
||||
|
||||
def = this->add("wall_transition_length", coFloatOrPercent);
|
||||
def->label = L("Perimeter transition length");
|
||||
def->category = L("Advanced");
|
||||
|
||||
@@ -144,9 +144,6 @@ enum class PerimeterGeneratorType
|
||||
//B3
|
||||
enum class GCodeThumbnailsFormat { QIDI,PNG, JPG, QOI };
|
||||
|
||||
//w16
|
||||
enum class TopOneWallType { None, Alltop, Onlytopmost };
|
||||
|
||||
#define CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(NAME) \
|
||||
template<> const t_config_enum_names& ConfigOptionEnum<NAME>::get_enum_names(); \
|
||||
template<> const t_config_enum_values& ConfigOptionEnum<NAME>::get_enum_values();
|
||||
@@ -527,8 +524,6 @@ PRINT_CONFIG_CLASS_DEFINE(
|
||||
((ConfigOptionFloat, raft_first_layer_expansion))
|
||||
((ConfigOptionInt, raft_layers))
|
||||
((ConfigOptionEnum<SeamPosition>, seam_position))
|
||||
//Y21
|
||||
((ConfigOptionPercent, seam_gap))
|
||||
((ConfigOptionBool, staggered_inner_seams))
|
||||
// ((ConfigOptionFloat, seam_preferred_direction))
|
||||
// ((ConfigOptionFloat, seam_preferred_direction_jitter))
|
||||
@@ -590,8 +585,6 @@ PRINT_CONFIG_CLASS_DEFINE(
|
||||
((ConfigOptionBool, wipe_into_objects))
|
||||
//w11
|
||||
((ConfigOptionBool, detect_narrow_internal_solid_infill))
|
||||
//w16
|
||||
((ConfigOptionEnum<TopOneWallType>, top_one_wall_type))
|
||||
)
|
||||
|
||||
PRINT_CONFIG_CLASS_DEFINE(
|
||||
|
||||
@@ -864,14 +864,10 @@ bool PrintObject::invalidate_state_by_config_options(
|
||||
|| opt_key == "wall_transition_angle"
|
||||
|| opt_key == "wall_distribution_count"
|
||||
|| opt_key == "min_feature_size"
|
||||
|| opt_key == "min_bead_width"
|
||||
//w15
|
||||
|| opt_key == "top_one_wall_type"){
|
||||
|| opt_key == "min_bead_width") {
|
||||
steps.emplace_back(posSlice);
|
||||
} else if (
|
||||
opt_key == "seam_position"
|
||||
//Y21
|
||||
|| opt_key == "seam_gap"
|
||||
|| opt_key == "seam_preferred_direction"
|
||||
|| opt_key == "seam_preferred_direction_jitter"
|
||||
|| opt_key == "support_material_speed"
|
||||
|
||||
@@ -244,8 +244,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
|
||||
{
|
||||
bool have_perimeters = config->opt_int("perimeters") > 0;
|
||||
for (auto el : { "extra_perimeters","extra_perimeters_on_overhangs", "thin_walls", "overhangs",
|
||||
// Y21
|
||||
"seam_position","seam_gap","staggered_inner_seams", "external_perimeters_first", "external_perimeter_extrusion_width",
|
||||
"seam_position","staggered_inner_seams", "external_perimeters_first", "external_perimeter_extrusion_width",
|
||||
"perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "enable_dynamic_overhang_speeds"})
|
||||
toggle_field(el, have_perimeters);
|
||||
|
||||
|
||||
@@ -1461,14 +1461,10 @@ void TabPrint::build()
|
||||
|
||||
optgroup = page->new_optgroup(L("Advanced"));
|
||||
optgroup->append_single_option_line("seam_position", category_path + "seam-position");
|
||||
//Y21
|
||||
optgroup->append_single_option_line("seam_gap", category_path + "seam-gap");
|
||||
optgroup->append_single_option_line("staggered_inner_seams", category_path + "staggered-inner-seams");
|
||||
optgroup->append_single_option_line("external_perimeters_first", category_path + "external-perimeters-first");
|
||||
optgroup->append_single_option_line("gap_fill_enabled", category_path + "fill-gaps");
|
||||
optgroup->append_single_option_line("perimeter_generator");
|
||||
//w16
|
||||
optgroup->append_single_option_line("top_one_wall_type");
|
||||
|
||||
optgroup = page->new_optgroup(L("Fuzzy skin (experimental)"));
|
||||
category_path = "fuzzy-skin_246186/#";
|
||||
|
||||
Reference in New Issue
Block a user