Polyholes!

slic3r/Sliç3r#3323
slic3r/Sliç3r#2438
prusa3d/PrusaSlicer#515
This commit is contained in:
supermerill 2019-11-01 22:10:17 +01:00
parent dbb6fbe9c3
commit a43b2b123d
7 changed files with 167 additions and 32 deletions

View File

@ -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);

View File

@ -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 "

View File

@ -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);

View File

@ -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);

View File

@ -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,

View File

@ -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",

View File

@ -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");