mirror of
https://git.mirrors.martin98.com/https://github.com/slic3r/Slic3r.git
synced 2025-07-15 02:01:47 +08:00
Polyholes!
slic3r/Sliç3r#3323 slic3r/Sliç3r#2438 prusa3d/PrusaSlicer#515
This commit is contained in:
parent
dbb6fbe9c3
commit
a43b2b123d
@ -182,6 +182,7 @@ private:
|
|||||||
|
|
||||||
void _slice(const std::vector<coordf_t> &layer_height_profile);
|
void _slice(const std::vector<coordf_t> &layer_height_profile);
|
||||||
void _offset_holes(double hole_delta, LayerRegion *layerm);
|
void _offset_holes(double hole_delta, LayerRegion *layerm);
|
||||||
|
void _transform_hole_to_polyholes();
|
||||||
void _smooth_curves(LayerRegion *layerm);
|
void _smooth_curves(LayerRegion *layerm);
|
||||||
std::string _fix_slicing_errors();
|
std::string _fix_slicing_errors();
|
||||||
void _simplify_slices(double distance);
|
void _simplify_slices(double distance);
|
||||||
|
@ -1232,6 +1232,7 @@ void PrintConfigDef::init_fff_params()
|
|||||||
"over the default layer height.");
|
"over the default layer height.");
|
||||||
def->sidetext = L("mm or %");
|
def->sidetext = L("mm or %");
|
||||||
def->ratio_over = "layer_height";
|
def->ratio_over = "layer_height";
|
||||||
|
def->min = 0;
|
||||||
def->mode = comAdvanced;
|
def->mode = comAdvanced;
|
||||||
def->set_default_value(new ConfigOptionFloatOrPercent(0.2, false));
|
def->set_default_value(new ConfigOptionFloatOrPercent(0.2, false));
|
||||||
|
|
||||||
@ -3005,12 +3006,22 @@ void PrintConfigDef::init_fff_params()
|
|||||||
def->full_label = L("XY holes compensation");
|
def->full_label = L("XY holes compensation");
|
||||||
def->category = L("Slicing");
|
def->category = L("Slicing");
|
||||||
def->tooltip = L("The convex holes will be grown / shrunk in the XY plane by the configured value"
|
def->tooltip = L("The convex holes will be grown / shrunk in the XY plane by the configured value"
|
||||||
" (negative = inwards, positive = outwards, should be negative as the holes are always a bit smaller irl)."
|
" (negative = inwards, positive = outwards, should be negative as the holes are always a bit smaller irl)."
|
||||||
" This might be useful for fine-tuning hole sizes.");
|
" This might be useful for fine-tuning hole sizes.");
|
||||||
def->sidetext = L("mm");
|
def->sidetext = L("mm");
|
||||||
def->mode = comExpert;
|
def->mode = comExpert;
|
||||||
def->set_default_value(new ConfigOptionFloat(0));
|
def->set_default_value(new ConfigOptionFloat(0));
|
||||||
|
|
||||||
|
def = this->add("hole_to_polyhole", coBool);
|
||||||
|
def->label = L("Convert round holes to polyholes");
|
||||||
|
def->full_label = L("Convert round holes to polyholes");
|
||||||
|
def->category = L("Slicing");
|
||||||
|
def->tooltip = L("Search for almost-circular holes that span more than one layer and conert the geometry to polyholes."
|
||||||
|
" Use the nozzle size and the (biggest) diameter to compute the polyhole."
|
||||||
|
"\nSee http://hydraraptor.blogspot.com/2011/02/polyholes.html");
|
||||||
|
def->mode = comAdvanced;
|
||||||
|
def->set_default_value(new ConfigOptionBool(false));
|
||||||
|
|
||||||
def = this->add("z_offset", coFloat);
|
def = this->add("z_offset", coFloat);
|
||||||
def->label = L("Z offset");
|
def->label = L("Z offset");
|
||||||
def->tooltip = L("This value will be added (or subtracted) from all the Z coordinates "
|
def->tooltip = L("This value will be added (or subtracted) from all the Z coordinates "
|
||||||
|
@ -598,6 +598,7 @@ public:
|
|||||||
ConfigOptionBool gap_fill;
|
ConfigOptionBool gap_fill;
|
||||||
ConfigOptionFloatOrPercent gap_fill_min_area;
|
ConfigOptionFloatOrPercent gap_fill_min_area;
|
||||||
ConfigOptionFloat gap_fill_speed;
|
ConfigOptionFloat gap_fill_speed;
|
||||||
|
ConfigOptionBool hole_to_polyhole;
|
||||||
ConfigOptionInt infill_extruder;
|
ConfigOptionInt infill_extruder;
|
||||||
ConfigOptionFloatOrPercent infill_extrusion_width;
|
ConfigOptionFloatOrPercent infill_extrusion_width;
|
||||||
ConfigOptionInt infill_every_layers;
|
ConfigOptionInt infill_every_layers;
|
||||||
@ -667,6 +668,7 @@ protected:
|
|||||||
OPT_PTR(gap_fill);
|
OPT_PTR(gap_fill);
|
||||||
OPT_PTR(gap_fill_min_area);
|
OPT_PTR(gap_fill_min_area);
|
||||||
OPT_PTR(gap_fill_speed);
|
OPT_PTR(gap_fill_speed);
|
||||||
|
OPT_PTR(hole_to_polyhole);
|
||||||
OPT_PTR(infill_extruder);
|
OPT_PTR(infill_extruder);
|
||||||
OPT_PTR(infill_extrusion_width);
|
OPT_PTR(infill_extrusion_width);
|
||||||
OPT_PTR(infill_every_layers);
|
OPT_PTR(infill_every_layers);
|
||||||
|
@ -122,11 +122,127 @@ void PrintObject::slice()
|
|||||||
// Simplify slices if required.
|
// Simplify slices if required.
|
||||||
if (m_print->config().resolution)
|
if (m_print->config().resolution)
|
||||||
this->_simplify_slices(scale_(this->print()->config().resolution));
|
this->_simplify_slices(scale_(this->print()->config().resolution));
|
||||||
|
//create polyholes
|
||||||
|
this->_transform_hole_to_polyholes();
|
||||||
if (m_layers.empty())
|
if (m_layers.empty())
|
||||||
throw std::runtime_error("No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry.\n");
|
throw std::runtime_error("No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry.\n");
|
||||||
this->set_done(posSlice);
|
this->set_done(posSlice);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Polygon create_polyhole(const Point center, const coord_t diameter, const coord_t nozzle_diameter)
|
||||||
|
{
|
||||||
|
// n = max(round(2 * d), 3); // for 0.4mm nozzle
|
||||||
|
size_t nb_polygons = (int)std::max(3, (int)std::round(2.0 * unscaled(diameter) * 0.4 / unscaled(nozzle_diameter)));
|
||||||
|
// cylinder(h = h, r = (d / 2) / cos (180 / n), $fn = n);
|
||||||
|
Points pts;
|
||||||
|
const float rayon = (diameter / 1) / std::cos(PI / nb_polygons);
|
||||||
|
for (int i = 0; i < nb_polygons; ++i) {
|
||||||
|
float angle = (PI * 2 * i) / nb_polygons;
|
||||||
|
pts.emplace_back(center.x() + rayon * cos(angle), center.y() + rayon * sin(angle));
|
||||||
|
}
|
||||||
|
return Polygon{ pts };
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintObject::_transform_hole_to_polyholes()
|
||||||
|
{
|
||||||
|
// get all circular holes for each layer
|
||||||
|
// the id is center-diameter-extruderid
|
||||||
|
std::vector<std::vector<std::pair<std::tuple<Point,float, int>, Polygon*>>> layerid2center;
|
||||||
|
for (size_t i = 0; i < this->m_layers.size(); i++) layerid2center.emplace_back();
|
||||||
|
//tbb::parallel_for(
|
||||||
|
//tbb::blocked_range<size_t>(0, m_layers.size()),
|
||||||
|
//[this, layerid2center](const tbb::blocked_range<size_t>& range) {
|
||||||
|
//for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) {
|
||||||
|
for (size_t layer_idx = 0; layer_idx < this->m_layers.size(); ++layer_idx) {
|
||||||
|
m_print->throw_if_canceled();
|
||||||
|
Layer *layer = m_layers[layer_idx];
|
||||||
|
for (size_t region_idx = 0; region_idx < layer->m_regions.size(); ++region_idx)
|
||||||
|
{
|
||||||
|
if (layer->m_regions[region_idx]->region()->config().hole_to_polyhole) {
|
||||||
|
for (Surface &surf : layer->m_regions[region_idx]->m_slices.surfaces) {
|
||||||
|
for (Polygon &hole : surf.expolygon.holes) {
|
||||||
|
//test if convex (as it's clockwise bc it's a hole, we have to do the opposite)
|
||||||
|
if (hole.convex_points().empty() && hole.points.size() > 4) {
|
||||||
|
double center_x = 0, center_y = 0;
|
||||||
|
for (int i = 0; i < hole.points.size(); ++i) {
|
||||||
|
center_x += hole.points[i].x();
|
||||||
|
center_y += hole.points[i].y();
|
||||||
|
}
|
||||||
|
Point center{ center_x / hole.points.size(), center_y / hole.points.size() };
|
||||||
|
double diameter_min = std::numeric_limits<float>::max(), diameter_max = 0;
|
||||||
|
for (int i = 0; i < hole.points.size(); ++i) {
|
||||||
|
double dist = hole.points[i].distance_to_square(center);
|
||||||
|
diameter_min = std::min(diameter_min, dist);
|
||||||
|
diameter_max = std::max(diameter_max, dist);
|
||||||
|
}
|
||||||
|
diameter_min = std::sqrt(diameter_min);
|
||||||
|
diameter_max = std::sqrt(diameter_max);
|
||||||
|
if (diameter_max - diameter_min < SCALED_EPSILON) {
|
||||||
|
layerid2center[layer_idx].emplace_back(
|
||||||
|
std::tuple<Point, float, int>{center, diameter_max, layer->m_regions[region_idx]->region()->config().perimeter_extruder.value}, &hole);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// for layer->slices, it will be also replaced later.
|
||||||
|
}
|
||||||
|
//});
|
||||||
|
//sort holes per center-diameter
|
||||||
|
std::map<std::tuple<Point, float, int>,std::vector<std::pair<Polygon*,int>>> id2layerz2hole;
|
||||||
|
|
||||||
|
//search & find hole that span at least X layers
|
||||||
|
const size_t min_nb_layers = 2;
|
||||||
|
float max_layer_height = config().layer_height * 2;
|
||||||
|
for (size_t layer_idx = 0; layer_idx < this->m_layers.size(); ++layer_idx) {
|
||||||
|
for (size_t hole_idx = 0; hole_idx < layerid2center[layer_idx].size(); ++hole_idx) {
|
||||||
|
//get all other same polygons
|
||||||
|
std::tuple<Point, float, int> &id = layerid2center[layer_idx][hole_idx].first;
|
||||||
|
float max_z = layers()[layer_idx]->print_z;
|
||||||
|
std::vector<std::pair<Polygon*,int>> holes;
|
||||||
|
holes.emplace_back(layerid2center[layer_idx][hole_idx].second, layer_idx);
|
||||||
|
for (size_t search_layer_idx = layer_idx + 1; search_layer_idx < this->m_layers.size(); ++search_layer_idx) {
|
||||||
|
if (layers()[search_layer_idx]->print_z - layers()[search_layer_idx]->height - max_z > EPSILON) break;
|
||||||
|
//search an other polygon with same id
|
||||||
|
for (size_t search_hole_idx = 0; search_hole_idx < layerid2center[search_layer_idx].size(); ++search_hole_idx) {
|
||||||
|
std::tuple<Point, float, int> &search_id = layerid2center[search_layer_idx][search_hole_idx].first;
|
||||||
|
if (std::get<0>(id).distance_to(std::get<0>(search_id)) < SCALED_EPSILON
|
||||||
|
&& std::abs(std::get<1>(id) - std::get<1>(search_id)) < SCALED_EPSILON
|
||||||
|
&& std::get<2>(id) == std::get<2>(search_id)) {
|
||||||
|
max_z = layers()[search_layer_idx]->print_z;
|
||||||
|
holes.emplace_back(layerid2center[search_layer_idx][search_hole_idx].second, search_layer_idx);
|
||||||
|
layerid2center[search_layer_idx].erase(layerid2center[search_layer_idx].begin() + search_hole_idx);
|
||||||
|
search_hole_idx--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (holes.size() >= min_nb_layers) {
|
||||||
|
id2layerz2hole.emplace(std::move(id), std::move(holes));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//create a polyhole per id and replace holes points by it.
|
||||||
|
for (auto entry : id2layerz2hole) {
|
||||||
|
Polygon polyhole = create_polyhole(std::get<0>(entry.first), std::get<1>(entry.first), scale_(print()->config().nozzle_diameter.get_at(std::get<2>(entry.first) - 1)));
|
||||||
|
polyhole.make_clockwise();
|
||||||
|
for (auto &poly_to_replace : entry.second) {
|
||||||
|
//search the clone in layers->slices
|
||||||
|
for (ExPolygon &explo_slice : m_layers[poly_to_replace.second]->slices.expolygons) {
|
||||||
|
for (Polygon &poly_slice : explo_slice.holes) {
|
||||||
|
if (poly_slice.points == poly_to_replace.first->points) {
|
||||||
|
poly_slice.points = polyhole.points;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// copy
|
||||||
|
poly_to_replace.first->points = polyhole.points;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 1) Merges typed region slices into stInternal type.
|
// 1) Merges typed region slices into stInternal type.
|
||||||
// 2) Increases an "extra perimeters" counter at region slices where needed.
|
// 2) Increases an "extra perimeters" counter at region slices where needed.
|
||||||
// 3) Generates perimeters, gap fills and fill regions (fill regions of type stInternal).
|
// 3) Generates perimeters, gap fills and fill regions (fill regions of type stInternal).
|
||||||
@ -500,7 +616,8 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
|
|||||||
|| opt_key == "support_material_contact_distance_top"
|
|| opt_key == "support_material_contact_distance_top"
|
||||||
|| opt_key == "support_material_contact_distance_bottom"
|
|| opt_key == "support_material_contact_distance_bottom"
|
||||||
|| opt_key == "xy_size_compensation"
|
|| opt_key == "xy_size_compensation"
|
||||||
|| opt_key == "hole_size_compensation") {
|
|| opt_key == "hole_size_compensation"
|
||||||
|
|| opt_key == "hole_to_polyhole") {
|
||||||
steps.emplace_back(posSlice);
|
steps.emplace_back(posSlice);
|
||||||
} else if (opt_key == "support_material") {
|
} else if (opt_key == "support_material") {
|
||||||
steps.emplace_back(posSupportMaterial);
|
steps.emplace_back(posSupportMaterial);
|
||||||
@ -1958,29 +2075,29 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
|
|||||||
};
|
};
|
||||||
std::vector<SlicedVolume> sliced_volumes;
|
std::vector<SlicedVolume> sliced_volumes;
|
||||||
sliced_volumes.reserve(num_volumes);
|
sliced_volumes.reserve(num_volumes);
|
||||||
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
|
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
|
||||||
const std::vector<std::pair<t_layer_height_range, int>> &volumes_and_ranges = this->region_volumes[region_id];
|
const std::vector<std::pair<t_layer_height_range, int>> &volumes_and_ranges = this->region_volumes[region_id];
|
||||||
for (size_t i = 0; i < volumes_and_ranges.size(); ) {
|
for (size_t i = 0; i < volumes_and_ranges.size(); ) {
|
||||||
int volume_id = volumes_and_ranges[i].second;
|
int volume_id = volumes_and_ranges[i].second;
|
||||||
const ModelVolume *model_volume = this->model_object()->volumes[volume_id];
|
const ModelVolume *model_volume = this->model_object()->volumes[volume_id];
|
||||||
if (model_volume->is_model_part()) {
|
if (model_volume->is_model_part()) {
|
||||||
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - volume " << volume_id;
|
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - volume " << volume_id;
|
||||||
// Find the ranges of this volume. Ranges in volumes_and_ranges must not overlap for a single volume.
|
// Find the ranges of this volume. Ranges in volumes_and_ranges must not overlap for a single volume.
|
||||||
std::vector<t_layer_height_range> ranges;
|
std::vector<t_layer_height_range> ranges;
|
||||||
ranges.emplace_back(volumes_and_ranges[i].first);
|
ranges.emplace_back(volumes_and_ranges[i].first);
|
||||||
size_t j = i + 1;
|
size_t j = i + 1;
|
||||||
for (; j < volumes_and_ranges.size() && volume_id == volumes_and_ranges[j].second; ++ j)
|
for (; j < volumes_and_ranges.size() && volume_id == volumes_and_ranges[j].second; ++ j)
|
||||||
if (! ranges.empty() && std::abs(ranges.back().second - volumes_and_ranges[j].first.first) < EPSILON)
|
if (! ranges.empty() && std::abs(ranges.back().second - volumes_and_ranges[j].first.first) < EPSILON)
|
||||||
ranges.back().second = volumes_and_ranges[j].first.second;
|
ranges.back().second = volumes_and_ranges[j].first.second;
|
||||||
else
|
else
|
||||||
ranges.emplace_back(volumes_and_ranges[j].first);
|
ranges.emplace_back(volumes_and_ranges[j].first);
|
||||||
// slicing in parallel
|
// slicing in parallel
|
||||||
sliced_volumes.emplace_back(volume_id, (int)region_id, this->slice_volume(slice_zs, ranges, *model_volume));
|
sliced_volumes.emplace_back(volume_id, (int)region_id, this->slice_volume(slice_zs, ranges, *model_volume));
|
||||||
i = j;
|
i = j;
|
||||||
} else
|
} else
|
||||||
++ i;
|
++ i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Second clip the volumes in the order they are presented at the user interface.
|
// Second clip the volumes in the order they are presented at the user interface.
|
||||||
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - parallel clipping - start";
|
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - parallel clipping - start";
|
||||||
tbb::parallel_for(
|
tbb::parallel_for(
|
||||||
@ -2042,7 +2159,7 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
|
|||||||
BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - stealing " << region_id << " start";
|
BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - stealing " << region_id << " start";
|
||||||
tbb::parallel_for(
|
tbb::parallel_for(
|
||||||
tbb::blocked_range<size_t>(0, m_layers.size()),
|
tbb::blocked_range<size_t>(0, m_layers.size()),
|
||||||
[this, &expolygons_by_layer, region_id](const tbb::blocked_range<size_t>& range) {
|
[this, &expolygons_by_layer, region_id](const tbb::blocked_range<size_t>& range) {
|
||||||
for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) {
|
for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) {
|
||||||
for (size_t other_region_id = 0; other_region_id < this->region_volumes.size(); ++ other_region_id) {
|
for (size_t other_region_id = 0; other_region_id < this->region_volumes.size(); ++ other_region_id) {
|
||||||
if (region_id == other_region_id)
|
if (region_id == other_region_id)
|
||||||
@ -2085,7 +2202,7 @@ end:
|
|||||||
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - make_slices in parallel - begin";
|
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - make_slices in parallel - begin";
|
||||||
tbb::parallel_for(
|
tbb::parallel_for(
|
||||||
tbb::blocked_range<size_t>(0, m_layers.size()),
|
tbb::blocked_range<size_t>(0, m_layers.size()),
|
||||||
[this, upscaled, clipped](const tbb::blocked_range<size_t>& range) {
|
[this, upscaled, clipped](const tbb::blocked_range<size_t>& range) {
|
||||||
for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) {
|
for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) {
|
||||||
m_print->throw_if_canceled();
|
m_print->throw_if_canceled();
|
||||||
Layer *layer = m_layers[layer_id];
|
Layer *layer = m_layers[layer_id];
|
||||||
@ -2127,9 +2244,9 @@ end:
|
|||||||
size_t nsteps = size_t(std::abs(steps));
|
size_t nsteps = size_t(std::abs(steps));
|
||||||
float step = elephant_foot_compensation / steps;
|
float step = elephant_foot_compensation / steps;
|
||||||
for (size_t i = 0; i < nsteps; ++ i) {
|
for (size_t i = 0; i < nsteps; ++ i) {
|
||||||
Polygons tmp = offset(expolygons, - step);
|
Polygons tmp = offset(expolygons, - step);
|
||||||
append(tmp, diff(to_polygons(expolygons), offset(offset_ex(expolygons, -elephant_foot_spacing + step), elephant_foot_spacing - step)));
|
append(tmp, diff(to_polygons(expolygons), offset(offset_ex(expolygons, -elephant_foot_spacing + step), elephant_foot_spacing - step)));
|
||||||
expolygons = union_ex(tmp);
|
expolygons = union_ex(tmp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
layerm->m_slices.set(std::move(expolygons), stPosInternal | stDensSparse);
|
layerm->m_slices.set(std::move(expolygons), stPosInternal | stDensSparse);
|
||||||
|
@ -47,7 +47,7 @@ SlicingParameters SlicingParameters::create_from_config(
|
|||||||
coordf_t object_height,
|
coordf_t object_height,
|
||||||
const std::vector<unsigned int> &object_extruders)
|
const std::vector<unsigned int> &object_extruders)
|
||||||
{
|
{
|
||||||
coordf_t first_layer_height = (object_config.first_layer_height.value <= 0) ?
|
coordf_t first_layer_height = (object_config.first_layer_height.get_abs_value(object_config.layer_height.value) <= 0) ?
|
||||||
object_config.layer_height.value :
|
object_config.layer_height.value :
|
||||||
object_config.first_layer_height.get_abs_value(object_config.layer_height.value);
|
object_config.first_layer_height.get_abs_value(object_config.layer_height.value);
|
||||||
// If object_config.support_material_extruder == 0 resp. object_config.support_material_interface_extruder == 0,
|
// If object_config.support_material_extruder == 0 resp. object_config.support_material_interface_extruder == 0,
|
||||||
|
@ -426,7 +426,9 @@ const std::vector<std::string>& Preset::print_options()
|
|||||||
"top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "bridge_flow_ratio",
|
"top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "bridge_flow_ratio",
|
||||||
"clip_multipart_objects",
|
"clip_multipart_objects",
|
||||||
"over_bridge_flow_ratio", "clip_multipart_objects", "enforce_full_fill_volume", "external_infill_margin", "bridged_infill_margin",
|
"over_bridge_flow_ratio", "clip_multipart_objects", "enforce_full_fill_volume", "external_infill_margin", "bridged_infill_margin",
|
||||||
"elefant_foot_compensation", "xy_size_compensation", "hole_size_compensation", "threads", "resolution",
|
"elefant_foot_compensation", "xy_size_compensation", "hole_size_compensation",
|
||||||
|
"hole_to_polyhole",
|
||||||
|
"threads", "resolution",
|
||||||
"wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_bridging",
|
"wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_bridging",
|
||||||
"single_extruder_multi_material_priming", "compatible_printers", "compatible_printers_condition", "inherits",
|
"single_extruder_multi_material_priming", "compatible_printers", "compatible_printers_condition", "inherits",
|
||||||
"infill_dense", "infill_dense_algo",
|
"infill_dense", "infill_dense_algo",
|
||||||
|
@ -1109,6 +1109,8 @@ void TabPrint::build()
|
|||||||
line.append_option(optgroup->get_option("hole_size_compensation"));
|
line.append_option(optgroup->get_option("hole_size_compensation"));
|
||||||
optgroup->append_line(line);
|
optgroup->append_line(line);
|
||||||
|
|
||||||
|
optgroup->append_single_option_line("hole_to_polyhole");
|
||||||
|
|
||||||
optgroup = page->new_optgroup(_(L("Other")));
|
optgroup = page->new_optgroup(_(L("Other")));
|
||||||
optgroup->append_single_option_line("clip_multipart_objects");
|
optgroup->append_single_option_line("clip_multipart_objects");
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user