new feature: smooth curve

* it create more points to smooth rough curves, do hide polygons in the final print.
also, create a new tab in the print settings for the ones related to the mesh.
This commit is contained in:
supermerill 2019-03-13 12:01:32 +01:00
parent e23f4020af
commit 8ba954d2aa
6 changed files with 161 additions and 25 deletions

View File

@ -172,7 +172,8 @@ private:
void generate_support_material(); void generate_support_material();
void _slice(const std::vector<coordf_t> &layer_height_profile); void _slice(const std::vector<coordf_t> &layer_height_profile);
void _offsetHoles(float hole_delta, LayerRegion *layerm); void _offset_holes(float hole_delta, 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);
void _make_perimeters(); void _make_perimeters();

View File

@ -375,7 +375,7 @@ void PrintConfigDef::init_fff_params()
def = this->add("elefant_foot_compensation", coFloat); def = this->add("elefant_foot_compensation", coFloat);
def->label = L("First layer"); def->label = L("First layer");
def->full_label = L("First layer compensation"); def->full_label = L("First layer compensation");
def->category = L("Advanced"); def->category = L("Slicing");
def->tooltip = L("The first layer will be grown / shrunk in the XY plane by the configured value " def->tooltip = L("The first layer will be grown / shrunk in the XY plane by the configured value "
"to compensate for the 1st layer squish aka an Elephant Foot effect. (should be negative = inwards)"); "to compensate for the 1st layer squish aka an Elephant Foot effect. (should be negative = inwards)");
def->sidetext = L("mm"); def->sidetext = L("mm");
@ -2132,6 +2132,35 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced; def->mode = comAdvanced;
def->default_value = new ConfigOptionFloatOrPercent(15, false); def->default_value = new ConfigOptionFloatOrPercent(15, false);
def = this->add("curve_smoothing_angle", coFloat);
def->label = L("Min angle");
def->full_label = L("Curve smoothing minimum angle");
def->category = L("Slicing");
def->tooltip = L("Minimum angle at a vertex to enable smoothing"
" (trying to create a curve around the vertex). "
"180 : nothing will be smooth, 0 : all angles will be smoothen.");
def->sidetext = L("°");
def->cli = "curve-smoothing-angle=f";
def->min = 0;
def->max = 180;
def->mode = comAdvanced;
def->default_value = new ConfigOptionFloat(0);
def = this->add("curve_smoothing_precision", coFloat);
def->label = L("Precision");
def->full_label = L("Curve smoothing precision");
def->category = L("Slicing");
def->tooltip = L("These parameter allow the slicer to smooth the angles in each layer. "
"The precision will be at least the new precision of the curve. Set to 0 to deactivate."
"\nNote: as it use the polygon's edges and only work in the 2D planes, "
"you must have a very clean or hand-made 3D model."
"\nIt's really only useful to smoothen functional models or very wide angles.");
def->sidetext = L("mm");
def->min = 0;
def->cli = "curve-smoothing-precision=f";
def->mode = comAdvanced;
def->default_value = new ConfigOptionFloat(0);
def = this->add("solid_infill_below_area", coFloat); def = this->add("solid_infill_below_area", coFloat);
def->label = L("Solid infill threshold area"); def->label = L("Solid infill threshold area");
def->category = L("Infill"); def->category = L("Infill");
@ -2258,7 +2287,7 @@ void PrintConfigDef::init_fff_params()
def = this->add("model_precision", coFloat); def = this->add("model_precision", coFloat);
def->label = L("Model rounding precision"); def->label = L("Model rounding precision");
def->full_label = L("Model rounding precision"); def->full_label = L("Model rounding precision");
def->category = L("Advanced"); def->category = L("Slicing");
def->tooltip = L("This is the rounding error of the input object." def->tooltip = L("This is the rounding error of the input object."
" It's used to align points that should be in the same line." " It's used to align points that should be in the same line."
" Put 0 to disable."); " Put 0 to disable.");
@ -2860,7 +2889,7 @@ void PrintConfigDef::init_fff_params()
def = this->add("xy_size_compensation", coFloat); def = this->add("xy_size_compensation", coFloat);
def->label = L("All layers"); def->label = L("All layers");
def->full_label = L("XY size compensation"); def->full_label = L("XY size compensation");
def->category = L("Advanced"); def->category = L("Slicing");
def->tooltip = L("The object will be grown/shrunk in the XY plane by the configured value " def->tooltip = L("The object will be grown/shrunk in the XY plane by the configured value "
"(negative = inwards, positive = outwards). This might be useful " "(negative = inwards, positive = outwards). This might be useful "
"for fine-tuning sizes."); "for fine-tuning sizes.");
@ -2872,7 +2901,7 @@ void PrintConfigDef::init_fff_params()
def = this->add("hole_size_compensation", coFloat); def = this->add("hole_size_compensation", coFloat);
def->label = L("Holes"); def->label = L("Holes");
def->full_label = L("XY holes compensation"); def->full_label = L("XY holes compensation");
def->category = L("Advanced"); 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.");

View File

@ -561,6 +561,8 @@ public:
ConfigOptionEnum<InfillPattern> bottom_fill_pattern; ConfigOptionEnum<InfillPattern> bottom_fill_pattern;
ConfigOptionFloatOrPercent bridged_infill_margin; ConfigOptionFloatOrPercent bridged_infill_margin;
ConfigOptionFloat bridge_speed; ConfigOptionFloat bridge_speed;
ConfigOptionFloat curve_smoothing_precision;
ConfigOptionFloat curve_smoothing_angle;
ConfigOptionBool ensure_vertical_shell_thickness; ConfigOptionBool ensure_vertical_shell_thickness;
ConfigOptionBool enforce_full_fill_volume; ConfigOptionBool enforce_full_fill_volume;
ConfigOptionFloatOrPercent external_infill_margin; ConfigOptionFloatOrPercent external_infill_margin;
@ -620,6 +622,8 @@ protected:
OPT_PTR(bottom_fill_pattern); OPT_PTR(bottom_fill_pattern);
OPT_PTR(bridged_infill_margin); OPT_PTR(bridged_infill_margin);
OPT_PTR(bridge_speed); OPT_PTR(bridge_speed);
OPT_PTR(curve_smoothing_precision);
OPT_PTR(curve_smoothing_angle);
OPT_PTR(ensure_vertical_shell_thickness); OPT_PTR(ensure_vertical_shell_thickness);
OPT_PTR(enforce_full_fill_volume); OPT_PTR(enforce_full_fill_volume);
OPT_PTR(external_infill_margin); OPT_PTR(external_infill_margin);

View File

@ -1882,7 +1882,8 @@ end:
LayerRegion *layerm = layer->m_regions.front(); LayerRegion *layerm = layer->m_regions.front();
layerm->slices.set(offset_ex(to_expolygons(std::move(layerm->slices.surfaces)), delta), stPosInternal | stDensSparse); layerm->slices.set(offset_ex(to_expolygons(std::move(layerm->slices.surfaces)), delta), stPosInternal | stDensSparse);
} }
_offsetHoles(hole_delta, layer->regions().front()); _offset_holes(hole_delta, layer->regions().front());
_smooth_curves(layer->regions().front());
} else if (scale || clip || hole_delta != 0.f) { } else if (scale || clip || hole_delta != 0.f) {
// Multiple regions, growing, shrinking or just clipping one region by the other. // Multiple regions, growing, shrinking or just clipping one region by the other.
// When clipping the regions, priority is given to the first regions. // When clipping the regions, priority is given to the first regions.
@ -1899,7 +1900,8 @@ end:
// Collect the already processed regions to trim the to be processed regions. // Collect the already processed regions to trim the to be processed regions.
polygons_append(processed, slices); polygons_append(processed, slices);
layerm->slices.set(std::move(slices), stPosInternal | stDensSparse); layerm->slices.set(std::move(slices), stPosInternal | stDensSparse);
_offsetHoles(hole_delta, layerm); _offset_holes(hole_delta, layerm);
_smooth_curves(layerm);
} }
} }
// Merge all regions' slices to get islands, chain them by a shortest path. // Merge all regions' slices to get islands, chain them by a shortest path.
@ -1910,9 +1912,8 @@ end:
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - make_slices in parallel - end"; BOOST_LOG_TRIVIAL(debug) << "Slicing objects - make_slices in parallel - end";
} }
void PrintObject::_offsetHoles(float hole_delta, LayerRegion *layerm) { void PrintObject::_offset_holes(float hole_delta, LayerRegion *layerm) {
if (hole_delta != 0.f) { if (hole_delta != 0.f) {
std::cout << "offset_hole z="<<layerm->layer()->id()<<"\n";
ExPolygons polys = to_expolygons(std::move(layerm->slices.surfaces)); ExPolygons polys = to_expolygons(std::move(layerm->slices.surfaces));
ExPolygons new_polys; ExPolygons new_polys;
for (const ExPolygon &ex_poly : polys) { for (const ExPolygon &ex_poly : polys) {
@ -1950,6 +1951,96 @@ void PrintObject::_offsetHoles(float hole_delta, LayerRegion *layerm) {
} }
} }
/// max angle: you ahve to be lwer than that to divide it. PI => all accepted
/// min angle: don't smooth sharp angles! 0 => all accepted
Polygon _smooth_curve(Polygon &p, double max_angle, double min_angle, coord_t max_dist){
if (p.size() < 4) return p;
Polygon pout;
//duplicate points to simplify the loop
p.points.insert(p.points.end(), p.points.begin(), p.points.begin() + 3);
for (size_t idx = 1; idx<p.size() - 2; idx++){
pout.points.push_back(p[idx]);
double angle1 = p[idx].ccw_angle(p.points[idx - 1], p.points[idx + 1]);
if (angle1 > PI) angle1 = 2 * PI - angle1;
double angle2 = p[idx + 1].ccw_angle(p.points[idx], p.points[idx + 2]);
if (angle2 > PI) angle2 = 2 * PI - angle2;
if (angle1 < min_angle && angle2 < min_angle) continue;
if (angle1 > max_angle && angle2 > max_angle) continue;
// add points, but how many?
coordf_t dist = p[idx].distance_to(p[idx + 1]);
int nb_add = dist / max_dist;
if (max_angle < PI) {
int nb_add_per_angle = std::max((PI - angle1) / (PI - max_angle), (PI - angle2) / (PI - max_angle));
nb_add = std::min(nb_add, nb_add_per_angle);
}
if (nb_add == 0) continue;
//création des points de controles
Vec2d vec_ab = (p[idx] - p[idx - 1]).cast<double>();
Vec2d vec_bc = (p[idx + 1] - p[idx]).cast<double>();
Vec2d vec_cb = (p[idx] - p[idx + 1]).cast<double>();
Vec2d vec_dc = (p[idx + 1] - p[idx + 2]).cast<double>();
vec_ab.normalize();
vec_bc.normalize();
vec_cb.normalize();
vec_dc.normalize();
Vec2d vec_b_tang = vec_ab + vec_bc;
vec_b_tang.normalize();
//should be 0.55 / 1.414 = ~0.39 to create a true circle from a square (90°)
// it's ~0.36 for exagon (120°)
// it's ~0.34 for octogon (135°)
vec_b_tang *= dist * (0.31 + 0.12 * (1-(angle1 / PI)));
Vec2d vec_c_tang = vec_dc + vec_cb;
vec_c_tang.normalize();
vec_c_tang *= dist * (0.31 + 0.12 * (1 - (angle1 / PI)));
Point bp = p[idx] + ((angle1 < min_angle) ? vec_bc.cast<coord_t>() : vec_b_tang.cast<coord_t>());
Point cp = p[idx + 1] + ((angle2 < min_angle) ? vec_cb.cast<coord_t>() : vec_c_tang.cast<coord_t>());
for (int idx_np = 0; idx_np < nb_add; idx_np++){
const float percent_np = (idx_np + 1) / (float)(nb_add + 1);
const float inv_percent_np = 1 - percent_np;
pout.points.emplace_back();
Point &new_p = pout.points.back();
const float coeff0 = inv_percent_np * inv_percent_np * inv_percent_np;
const float coeff1 = percent_np * inv_percent_np * inv_percent_np;
const float coeff2 = percent_np * percent_np * inv_percent_np;
const float coeff3 = percent_np * percent_np * percent_np;
new_p.x() = (p[idx].x() * coeff0)
+ (3 * bp.x() * coeff1)
+ (3 * cp.x() * coeff2)
+ (p[idx + 1].x() * coeff3);
new_p.y() = (p[idx].y() * coeff0)
+ (3 * bp.y() * coeff1)
+ (3 * cp.y() * coeff2)
+ (p[idx + 1].y() * coeff3);
}
}
return pout;
}
void PrintObject::_smooth_curves(LayerRegion *layerm) {
if (layerm->region()->config().curve_smoothing_precision.value > 0.f) {
ExPolygons new_polys;
for (const Surface &srf : layerm->slices.surfaces) {
const ExPolygon &ex_poly = srf.expolygon;
ExPolygon new_ex_poly(ex_poly);
new_ex_poly.contour = _smooth_curve(new_ex_poly.contour, PI,
layerm->region()->config().curve_smoothing_angle.value*PI / 180.0,
scale_(layerm->region()->config().curve_smoothing_precision.value));
for (Polygon &phole : new_ex_poly.holes){
phole.reverse(); // make_counter_clockwise();
phole = _smooth_curve(phole, PI,
layerm->region()->config().curve_smoothing_angle.value*PI / 180.0,
scale_(layerm->region()->config().curve_smoothing_precision.value));
phole.reverse(); // make_clockwise();
}
new_polys.push_back(new_ex_poly);
}
layerm->slices.set(std::move(new_polys), stPosInternal | stDensSparse);
}
}
std::vector<ExPolygons> PrintObject::_slice_region(size_t region_id, const std::vector<float> &z, bool modifier) std::vector<ExPolygons> PrintObject::_slice_region(size_t region_id, const std::vector<float> &z, bool modifier)
{ {
std::vector<const ModelVolume*> volumes; std::vector<const ModelVolume*> volumes;

View File

@ -403,6 +403,8 @@ const std::vector<std::string>& Preset::print_options()
, "thin_walls_min_width" , "thin_walls_min_width"
, "thin_walls_overlap" , "thin_walls_overlap"
, "model_precision" , "model_precision"
, "curve_smoothing_precision"
, "curve_smoothing_angle"
}; };
return s_opts; return s_opts;
} }

View File

@ -946,8 +946,8 @@ void Tab::update_frequently_changed_parameters()
void TabPrint::build() void TabPrint::build()
{ {
m_presets = &m_preset_bundle->prints; m_presets = &m_preset_bundle->prints;
Line line{ "", "" };
load_initial_data(); load_initial_data();
auto page = add_options_page(_(L("Layers and perimeters")), "layers.png"); auto page = add_options_page(_(L("Layers and perimeters")), "layers.png");
auto optgroup = page->new_optgroup(_(L("Layer height"))); auto optgroup = page->new_optgroup(_(L("Layer height")));
optgroup->append_single_option_line("layer_height"); optgroup->append_single_option_line("layer_height");
@ -958,7 +958,7 @@ void TabPrint::build()
optgroup->append_single_option_line("perimeters"); optgroup->append_single_option_line("perimeters");
optgroup->append_single_option_line("spiral_vase"); optgroup->append_single_option_line("spiral_vase");
Line line { "", "" }; line = { "", "" };
line.full_width = 1; line.full_width = 1;
line.widget = [this](wxWindow* parent) { line.widget = [this](wxWindow* parent) {
return description_line_widget(parent, &m_recommended_thin_wall_thickness_description_line); return description_line_widget(parent, &m_recommended_thin_wall_thickness_description_line);
@ -1147,6 +1147,26 @@ void TabPrint::build()
optgroup = page->new_optgroup(_(L("Advanced"))); optgroup = page->new_optgroup(_(L("Advanced")));
optgroup->append_single_option_line("interface_shells"); optgroup->append_single_option_line("interface_shells");
page = add_options_page(_(L("Slicing")), "layers.png");
optgroup = page->new_optgroup(_(L("Filtering")));
optgroup->append_single_option_line("slice_closing_radius");
optgroup->append_single_option_line("resolution");
optgroup->append_single_option_line("model_precision");
optgroup = page->new_optgroup(_(L("Modifying")));
line = { _(L("Curve smoothing")), "" };
line.append_option(optgroup->get_option("curve_smoothing_precision"));
line.append_option(optgroup->get_option("curve_smoothing_angle"));
optgroup->append_line(line);
line = { _(L("XY compensation")), "" };
line.append_option(optgroup->get_option("xy_size_compensation"));
line.append_option(optgroup->get_option("elefant_foot_compensation"));
line.append_option(optgroup->get_option("hole_size_compensation"));
optgroup->append_line(line);
page = add_options_page(_(L("Advanced")), "wrench.png"); page = add_options_page(_(L("Advanced")), "wrench.png");
optgroup = page->new_optgroup(_(L("Extrusion width"))); optgroup = page->new_optgroup(_(L("Extrusion width")));
optgroup->append_single_option_line("extrusion_width"); optgroup->append_single_option_line("extrusion_width");
@ -1165,21 +1185,10 @@ void TabPrint::build()
line = { _(L("Bridge flow ratio")), "" }; line = { _(L("Bridge flow ratio")), "" };
line.append_option(optgroup->get_option("bridge_flow_ratio")); line.append_option(optgroup->get_option("bridge_flow_ratio"));
line.append_option(optgroup->get_option("over_bridge_flow_ratio")); line.append_option(optgroup->get_option("over_bridge_flow_ratio"));
optgroup->append_line(line);
optgroup = page->new_optgroup(_(L("Slicing")));
optgroup->append_single_option_line("slice_closing_radius");
optgroup->append_single_option_line("resolution");
line = { _(L("XY compensation")), "" };
line.append_option(optgroup->get_option("xy_size_compensation"));
line.append_option(optgroup->get_option("elefant_foot_compensation"));
line.append_option(optgroup->get_option("hole_size_compensation"));
optgroup->append_line(line); optgroup->append_line(line);
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");
optgroup->append_single_option_line("resolution");
optgroup->append_single_option_line("model_precision");
page = add_options_page(_(L("Output options")), "page_white_go.png"); page = add_options_page(_(L("Output options")), "page_white_go.png");
optgroup = page->new_optgroup(_(L("Sequential printing"))); optgroup = page->new_optgroup(_(L("Sequential printing")));