change elepehant foot direction (again)

use reference in medial axis
add comments in medial axis
This commit is contained in:
supermerill 2019-06-08 15:04:27 +02:00
parent 6df17e1dd1
commit 26d17e03ee
12 changed files with 189 additions and 115 deletions

View File

@ -219,17 +219,11 @@ ExPolygon::remove_point_too_near(const coord_t tolerance) {
} }
} }
void
ExPolygon::medial_axis(const ExPolygon &bounds, double max_width, double min_width, ThickPolylines* polylines, double height) const {
Slic3r::MedialAxis ma(*this, bounds, (coord_t)max_width, (coord_t)min_width, height);
ma.build(polylines);
}
void void
ExPolygon::medial_axis(double max_width, double min_width, Polylines* polylines) const ExPolygon::medial_axis(double max_width, double min_width, Polylines* polylines) const
{ {
ThickPolylines tp; ThickPolylines tp;
this->medial_axis(*this, max_width, min_width, &tp, max_width/2.0); MedialAxis{ *this, coord_t(max_width), coord_t(min_width), coord_t(max_width / 2.0) }.build(tp);
polylines->insert(polylines->end(), tp.begin(), tp.end()); polylines->insert(polylines->end(), tp.begin(), tp.end());
} }

View File

@ -57,7 +57,6 @@ public:
ExPolygons simplify(double tolerance) const; ExPolygons simplify(double tolerance) const;
void simplify(double tolerance, ExPolygons* expolygons) const; void simplify(double tolerance, ExPolygons* expolygons) const;
void remove_point_too_near(const coord_t tolerance); void remove_point_too_near(const coord_t tolerance);
void medial_axis(const ExPolygon &bounds, double max_width, double min_width, ThickPolylines* polylines, double height) const;
void medial_axis(double max_width, double min_width, Polylines* polylines) const; void medial_axis(double max_width, double min_width, Polylines* polylines) const;
// void get_trapezoids(Polygons* polygons) const; // void get_trapezoids(Polygons* polygons) const;
// void get_trapezoids(Polygons* polygons, double angle) const; // void get_trapezoids(Polygons* polygons, double angle) const;

View File

@ -138,7 +138,7 @@ void FillConcentricWGapFill::fill_surface_extrusion(const Surface *surface, cons
//remove too small gaps that are too hard to fill. //remove too small gaps that are too hard to fill.
//ie one that are smaller than an extrusion with width of min and a length of max. //ie one that are smaller than an extrusion with width of min and a length of max.
if (ex.area() > min*max) { if (ex.area() > min*max) {
ex.medial_axis(ex, max, min, &polylines, params.flow->height); MedialAxis{ ex, coord_t(max), coord_t(min), coord_t(params.flow->height) }.build(polylines);
} }
} }
if (!polylines.empty() && good_role != erBridgeInfill) { if (!polylines.empty() && good_role != erBridgeInfill) {

View File

@ -1774,7 +1774,8 @@ FillRectilinear2WGapFill::fill_surface_extrusion(const Surface *surface, const F
ExtrusionRole good_role = getRoleFromSurfaceType(params, surface); ExtrusionRole good_role = getRoleFromSurfaceType(params, surface);
// remove areas for gapfill // remove areas for gapfill
ExPolygons rectilinear_areas = offset2_ex(ExPolygons{ surface->expolygon }, -params.flow->scaled_spacing() * 0.8f, params.flow->scaled_spacing() * 0.8f); float factor = 1; // 0.8f;
ExPolygons rectilinear_areas = offset2_ex(ExPolygons{ surface->expolygon }, -params.flow->scaled_spacing() * factor, params.flow->scaled_spacing() * factor);
ExPolygons gapfill_areas = diff_ex(ExPolygons{ surface->expolygon }, rectilinear_areas); ExPolygons gapfill_areas = diff_ex(ExPolygons{ surface->expolygon }, rectilinear_areas);
double rec_area = 0; double rec_area = 0;
for (ExPolygon &p : rectilinear_areas)rec_area += p.area(); for (ExPolygon &p : rectilinear_areas)rec_area += p.area();
@ -1854,7 +1855,7 @@ FillRectilinear2WGapFill::fill_surface_extrusion(const Surface *surface, const F
//remove too small gaps that are too hard to fill. //remove too small gaps that are too hard to fill.
//ie one that are smaller than an extrusion with width of min and a length of max. //ie one that are smaller than an extrusion with width of min and a length of max.
if (ex.area() > min * max) { if (ex.area() > min * max) {
ex.medial_axis(ex, params.flow->scaled_width()*2, params.flow->scaled_width()/5, &polylines_gapfill, params.flow->height); MedialAxis{ ex, params.flow->scaled_width() * 2, params.flow->scaled_width() / 5, coord_t(params.flow->height) }.build(polylines_gapfill);
} }
} }
if (!polylines_gapfill.empty() && good_role != erBridgeInfill) { if (!polylines_gapfill.empty() && good_role != erBridgeInfill) {

View File

@ -1,3 +1,4 @@
#include "MedialAxis.hpp"
#include "BoundingBox.hpp" #include "BoundingBox.hpp"
#include "ExPolygon.hpp" #include "ExPolygon.hpp"
#include "Geometry.hpp" #include "Geometry.hpp"
@ -12,15 +13,13 @@
#include <list> #include <list>
namespace Slic3r { namespace Slic3r {
int MedialAxis::id = 0;
void void
MedialAxis::build(Polylines* polylines) MedialAxis::build(Polylines &polylines)
{ {
ThickPolylines tp; ThickPolylines tp;
this->build(&tp); this->build(tp);
polylines->insert(polylines->end(), tp.begin(), tp.end()); polylines.insert(polylines.end(), tp.begin(), tp.end());
} }
void void
@ -353,7 +352,9 @@ add_point_same_percent(ThickPolyline* pattern, ThickPolyline* to_modify)
coordf_t new_width = to_modify->width[idx_other - 1] * (1 - percent_dist); coordf_t new_width = to_modify->width[idx_other - 1] * (1 - percent_dist);
new_width += to_modify->width[idx_other] * (percent_dist); new_width += to_modify->width[idx_other] * (percent_dist);
to_modify->width.insert(to_modify->width.begin() + idx_other, new_width); to_modify->width.insert(to_modify->width.begin() + idx_other, new_width);
to_modify->points.insert(to_modify->points.begin() + idx_other, to_modify->points[idx_other - 1].interpolate(percent_dist, to_modify->points[idx_other])); to_modify->points.insert(
to_modify->points.begin() + idx_other,
to_modify->points[idx_other - 1].interpolate(percent_dist, to_modify->points[idx_other]));
} }
} }
} }
@ -455,7 +456,7 @@ MedialAxis::fusion_curve(ThickPolylines &pp)
size_t closest_point_idx = this->expolygon.contour.closest_point_index(polyline.points.back()); size_t closest_point_idx = this->expolygon.contour.closest_point_index(polyline.points.back());
//check the 0-wodth point is on the contour. //check the 0-width point is on the contour.
if (closest_point_idx == (size_t)-1) continue; if (closest_point_idx == (size_t)-1) continue;
size_t prev_idx = closest_point_idx == 0 ? this->expolygon.contour.points.size() - 1 : closest_point_idx - 1; size_t prev_idx = closest_point_idx == 0 ? this->expolygon.contour.points.size() - 1 : closest_point_idx - 1;
@ -616,7 +617,7 @@ MedialAxis::fusion_corners(ThickPolylines &pp)
void void
MedialAxis::extends_line_both_side(ThickPolylines& pp) { MedialAxis::extends_line_both_side(ThickPolylines& pp) {
const ExPolygons anchors = offset2_ex(diff_ex(this->bounds, this->expolygon), double(-SCALED_RESOLUTION), double(SCALED_RESOLUTION)); const ExPolygons anchors = offset2_ex(diff_ex(*this->bounds, this->expolygon), double(-SCALED_RESOLUTION), double(SCALED_RESOLUTION));
for (size_t i = 0; i < pp.size(); ++i) { for (size_t i = 0; i < pp.size(); ++i) {
ThickPolyline& polyline = pp[i]; ThickPolyline& polyline = pp[i];
this->extends_line(polyline, anchors, this->min_width); this->extends_line(polyline, anchors, this->min_width);
@ -632,7 +633,7 @@ MedialAxis::extends_line(ThickPolyline& polyline, const ExPolygons& anchors, con
// We assign new endpoints to temporary variables because in case of a single-line // We assign new endpoints to temporary variables because in case of a single-line
// polyline, after we extend the start point it will be caught by the intersection() // polyline, after we extend the start point it will be caught by the intersection()
// call, so we keep the inner point until we perform the second intersection() as well // call, so we keep the inner point until we perform the second intersection() as well
if (polyline.endpoints.second && !bounds.has_boundary_point(polyline.points.back())) { if (polyline.endpoints.second && !bounds->has_boundary_point(polyline.points.back())) {
size_t first_idx = polyline.points.size() - 2; size_t first_idx = polyline.points.size() - 2;
Line line(*(polyline.points.begin() + first_idx), polyline.points.back()); Line line(*(polyline.points.begin() + first_idx), polyline.points.back());
while (line.length() < SCALED_RESOLUTION && first_idx>0) { while (line.length() < SCALED_RESOLUTION && first_idx>0) {
@ -674,10 +675,10 @@ MedialAxis::extends_line(ThickPolyline& polyline, const ExPolygons& anchors, con
polyline.width.push_back(polyline.width.back()); polyline.width.push_back(polyline.width.back());
} }
Point new_bound; Point new_bound;
bool finded = bounds.contour.first_intersection(line, &new_bound); bool finded = bounds->contour.first_intersection(line, &new_bound);
//verify also for holes. //verify also for holes.
Point new_bound_temp; Point new_bound_temp;
for (Polygon hole : bounds.holes) { for (Polygon hole : bounds->holes) {
if (hole.first_intersection(line, &new_bound_temp)) { if (hole.first_intersection(line, &new_bound_temp)) {
if (!finded || line.a.distance_to(new_bound_temp) < line.a.distance_to(new_bound)) { if (!finded || line.a.distance_to(new_bound_temp) < line.a.distance_to(new_bound)) {
finded = true; finded = true;
@ -687,9 +688,8 @@ MedialAxis::extends_line(ThickPolyline& polyline, const ExPolygons& anchors, con
} }
// safety check if no intersection // safety check if no intersection
if (!finded) { if (!finded) {
if (line.b.coincides_with_epsilon(polyline.points.back())) { if (line.b.coincides_with_epsilon(polyline.points.back()))
return; return;
}
//check if we don't over-shoot inside us //check if we don't over-shoot inside us
bool is_in_anchor = false; bool is_in_anchor = false;
for (const ExPolygon& a : anchors) { for (const ExPolygon& a : anchors) {
@ -724,7 +724,7 @@ MedialAxis::extends_line(ThickPolyline& polyline, const ExPolygons& anchors, con
p_obj.y() /= 2; p_obj.y() /= 2;
Line l2 = Line(new_back, p_obj); Line l2 = Line(new_back, p_obj);
l2.extend_end(max_width); l2.extend_end(max_width);
(void)bounds.contour.first_intersection(l2, &new_bound); (void)bounds->contour.first_intersection(l2, &new_bound);
} }
if (new_bound.coincides_with_epsilon(new_back)) { if (new_bound.coincides_with_epsilon(new_back)) {
return; return;
@ -983,6 +983,8 @@ MedialAxis::main_fusion(ThickPolylines& pp)
coord_t max_width_from_main = std::sqrt(main_branch_width*main_branch_width + main_branch_dist*main_branch_dist); coord_t max_width_from_main = std::sqrt(main_branch_width*main_branch_width + main_branch_dist*main_branch_dist);
if (find_main_branch && polyline.width[idx_point] > max_width_from_main) if (find_main_branch && polyline.width[idx_point] > max_width_from_main)
polyline.width[idx_point] = max_width_from_main; polyline.width[idx_point] = max_width_from_main;
if (find_main_branch && polyline.width[idx_point] > pp[biggest_main_branch_id].width.front() * 1.1)
polyline.width[idx_point] = pp[biggest_main_branch_id].width.front() * 1.1;
//std::cout << "main fusion, max dist : " << max_width_from_main << "\n"; //std::cout << "main fusion, max dist : " << max_width_from_main << "\n";
++idx_point; ++idx_point;
@ -1025,7 +1027,7 @@ MedialAxis::main_fusion(ThickPolylines& pp)
} }
//remove points that are outside of the geometry //remove points that are outside of the geometry
for (size_t idx_point = 0; idx_point < polyline.points.size(); ++idx_point) { for (size_t idx_point = 0; idx_point < polyline.points.size(); ++idx_point) {
if (!bounds.contains_b(polyline.points[idx_point])) { if (!bounds->contains_b(polyline.points[idx_point])) {
polyline.points.erase(polyline.points.begin() + idx_point); polyline.points.erase(polyline.points.begin() + idx_point);
polyline.width.erase(polyline.width.begin() + idx_point); polyline.width.erase(polyline.width.begin() + idx_point);
--idx_point; --idx_point;
@ -1071,7 +1073,7 @@ MedialAxis::remove_too_thin_extrusion(ThickPolylines& pp)
//try to split if possible //try to split if possible
if (polyline.width[1] > min_width) { if (polyline.width[1] > min_width) {
double percent_can_keep = (min_width - polyline.width[0]) / (polyline.width[1] - polyline.width[0]); double percent_can_keep = (min_width - polyline.width[0]) / (polyline.width[1] - polyline.width[0]);
if (polyline.points.front().distance_to(polyline.points[1]) * (1-percent_can_keep) > SCALED_RESOLUTION) { if (polyline.points.front().distance_to(polyline.points[1]) * (1 - percent_can_keep) > SCALED_RESOLUTION) {
//Can split => move the first point and assign a new weight. //Can split => move the first point and assign a new weight.
//the update of endpoints wil be performed in concatThickPolylines //the update of endpoints wil be performed in concatThickPolylines
polyline.points.front() = polyline.points.front().interpolate(percent_can_keep, polyline.points[1]); polyline.points.front() = polyline.points.front().interpolate(percent_can_keep, polyline.points[1]);
@ -1173,7 +1175,7 @@ MedialAxis::concatenate_polylines_with_crossing(ThickPolylines& pp)
best_dot = other_dot; best_dot = other_dot;
} }
} }
if (best_candidate != nullptr && best_candidate->size() > 1) { if (best_candidate != nullptr && best_candidate->points.size() > 1) {
if (polyline.last_point().coincides_with(best_candidate->last_point())) { if (polyline.last_point().coincides_with(best_candidate->last_point())) {
best_candidate->reverse(); best_candidate->reverse();
} else if (polyline.first_point().coincides_with(best_candidate->last_point())) { } else if (polyline.first_point().coincides_with(best_candidate->last_point())) {
@ -1302,12 +1304,12 @@ MedialAxis::ensure_not_overextrude(ThickPolylines& pp)
// compute bounds volume // compute bounds volume
double boundsVolume = 0; double boundsVolume = 0;
boundsVolume += height*bounds.area(); boundsVolume += height*bounds->area();
// add external "perimeter gap" // add external "perimeter gap"
double perimeterRoundGap = bounds.contour.length() * height * (1 - 0.25*PI) * 0.5; double perimeterRoundGap = bounds->contour.length() * height * (1 - 0.25*PI) * 0.5;
// add holes "perimeter gaps" // add holes "perimeter gaps"
double holesGaps = 0; double holesGaps = 0;
for (const Polygon &hole : bounds.holes) { for (const Polygon &hole : bounds->holes) {
holesGaps += hole.length() * height * (1 - 0.25*PI) * 0.5; holesGaps += hole.length() * height * (1 - 0.25*PI) * 0.5;
} }
boundsVolume += perimeterRoundGap + holesGaps; boundsVolume += perimeterRoundGap + holesGaps;
@ -1323,59 +1325,66 @@ MedialAxis::ensure_not_overextrude(ThickPolylines& pp)
} }
} }
ExPolygon void
MedialAxis::simplify_polygon_frontier() MedialAxis::simplify_polygon_frontier()
{ {
//simplify the boundary between us and the bounds. //simplify the boundary between us and the bounds.
ExPolygon simplified_poly = this->surface;
simplified_poly.contour.remove_colinear_points(SCALED_EPSILON);
for (Polygon &hole : simplified_poly.holes)
hole.remove_colinear_points(SCALED_EPSILON);
//it will remove every point in the surface contour that aren't on the bounds contour //it will remove every point in the surface contour that aren't on the bounds contour
if (&this->surface != &this->bounds) { this->expolygon = this->surface;
this->expolygon.contour.remove_collinear_points(SCALED_EPSILON);
for (Polygon &hole : this->expolygon.holes)
hole.remove_collinear_points(SCALED_EPSILON);
if (&this->surface != this->bounds) {
bool need_intersect = false; bool need_intersect = false;
for (size_t i = 0; i < simplified_poly.contour.points.size(); i++) { for (size_t i = 0; i < this->expolygon.contour.points.size(); i++) {
Point &p_check = simplified_poly.contour.points[i]; Point &p_check = this->expolygon.contour.points[i];
//if (!find) { //if (!find) {
if (!bounds.has_boundary_point(p_check)) { if (!bounds->has_boundary_point(p_check)) {
//check if we put it at a bound point instead of delete it //check if we put it at a bound point instead of delete it
size_t prev_i = i == 0 ? simplified_poly.contour.points.size() - 1 : (i - 1); size_t prev_i = i == 0 ? this->expolygon.contour.points.size() - 1 : (i - 1);
size_t next_i = i == simplified_poly.contour.points.size() - 1 ? 0 : (i + 1); size_t next_i = i == this->expolygon.contour.points.size() - 1 ? 0 : (i + 1);
const Point* closest = bounds.contour.closest_point(p_check); const Point* closest = bounds->contour.closest_point(p_check);
if (closest != nullptr && closest->distance_to(p_check) + SCALED_EPSILON if (closest != nullptr && closest->distance_to(p_check) + SCALED_EPSILON
< std::min(p_check.distance_to(simplified_poly.contour.points[prev_i]), p_check.distance_to(simplified_poly.contour.points[next_i])) / 2) { < std::min(p_check.distance_to(this->expolygon.contour.points[prev_i]), p_check.distance_to(this->expolygon.contour.points[next_i])) / 2) {
p_check.x() = closest->x(); p_check.x() = closest->x();
p_check.y() = closest->y(); p_check.y() = closest->y();
need_intersect = true; need_intersect = true;
} else { } else {
simplified_poly.contour.points.erase(simplified_poly.contour.points.begin() + i); this->expolygon.contour.points.erase(this->expolygon.contour.points.begin() + i);
i--; i--;
} }
} }
} }
if (need_intersect) { if (need_intersect) {
ExPolygons simplified_polys = intersection_ex(simplified_poly, bounds); ExPolygons simplified_polygons = intersection_ex(this->expolygon, *bounds);
if (simplified_polys.size() == 1) { if (simplified_polygons.size() == 1) {
simplified_poly = simplified_polys[0]; this->expolygon = simplified_polygons[0];
} else { } else {
simplified_poly = this->surface; //can't simplify that much, reuse the given one
this->expolygon = this->surface;
this->expolygon.contour.remove_collinear_points(SCALED_EPSILON);
for (Polygon &hole : this->expolygon.holes)
hole.remove_collinear_points(SCALED_EPSILON);
} }
} }
} }
if (!simplified_poly.contour.points.empty()) if (!this->expolygon.contour.points.empty())
simplified_poly.remove_point_too_near((coord_t)SCALED_RESOLUTION); this->expolygon.remove_point_too_near((coord_t)SCALED_RESOLUTION);
return simplified_poly;
} }
/// Grow the extrusion to at least nozzle_diameter*1.05 (lowest safe extrusion width) /// Grow the extrusion to at least nozzle_diameter*1.05 (lowest safe extrusion width)
/// Do not grow points inside the anchor. /// Do not grow points inside the anchor.
void void
MedialAxis::grow_to_nozzle_diameter(ThickPolylines& pp, const ExPolygons& anchors) { MedialAxis::grow_to_nozzle_diameter(ThickPolylines& pp, const ExPolygons& anchors)
{
//compute the min width //compute the min width
coord_t min_width = this->nozzle_diameter; coord_t min_width = this->nozzle_diameter;
if (this->height > 0)min_width = Flow::new_from_spacing(float(unscale_(this->nozzle_diameter)), float(unscale_(this->nozzle_diameter)), float(unscale_(this->height)), false).scaled_width(); if (this->height > 0) min_width = Flow::new_from_spacing(
//ensure the width is not lower than 0.4. float(unscale_(this->nozzle_diameter)),
float(unscale_(this->nozzle_diameter)),
float(unscale_(this->height)), false).scaled_width();
//ensure the width is not lower than min_width.
for (ThickPolyline& polyline : pp) { for (ThickPolyline& polyline : pp) {
for (int i = 0; i < polyline.points.size(); ++i) { for (int i = 0; i < polyline.points.size(); ++i) {
bool is_anchored = false; bool is_anchored = false;
@ -1392,10 +1401,11 @@ MedialAxis::grow_to_nozzle_diameter(ThickPolylines& pp, const ExPolygons& anchor
} }
void void
MedialAxis::taper_ends(ThickPolylines& pp) { MedialAxis::taper_ends(ThickPolylines& pp)
{
// minimum size of the taper: be sure to extrude at least the "round edges" of the extrusion (0-spacing extrusion). // minimum size of the taper: be sure to extrude at least the "round edges" of the extrusion (0-spacing extrusion).
const coord_t min_size = std::max(this->nozzle_diameter * 0.1, this->height * (1. - 0.25 * PI)); const coord_t min_size = std::max(this->nozzle_diameter * 0.1, this->height * (1. - 0.25 * PI));
const coordf_t length = std::min(this->anchor_size, (this->nozzle_diameter - min_size) / 2); const coordf_t length = std::min(this->taper_size, (this->nozzle_diameter - min_size) / 2);
if (length <= SCALED_RESOLUTION) return; if (length <= SCALED_RESOLUTION) return;
//ensure the width is not lower than min_size. //ensure the width is not lower than min_size.
for (ThickPolyline& polyline : pp) { for (ThickPolyline& polyline : pp) {
@ -1405,7 +1415,7 @@ MedialAxis::taper_ends(ThickPolylines& pp) {
coord_t current_dist = min_size; coord_t current_dist = min_size;
coord_t last_dist = min_size; coord_t last_dist = min_size;
for (size_t i = 1; i<polyline.width.size(); ++i) { for (size_t i = 1; i<polyline.width.size(); ++i) {
current_dist += (coord_t)polyline.points[i - 1].distance_to(polyline.points[i]); current_dist += (coord_t) polyline.points[i - 1].distance_to(polyline.points[i]);
if (current_dist > length) { if (current_dist > length) {
//create a new point if not near enough //create a new point if not near enough
if (current_dist > length + SCALED_RESOLUTION) { if (current_dist > length + SCALED_RESOLUTION) {
@ -1442,8 +1452,8 @@ MedialAxis::taper_ends(ThickPolylines& pp) {
} }
void void
MedialAxis::build(ThickPolylines* polylines_out) { MedialAxis::build(ThickPolylines &polylines_out)
this->id++; {
//std::cout << this->id << "\n"; //std::cout << this->id << "\n";
//{ //{
// std::stringstream stri; // std::stringstream stri;
@ -1452,7 +1462,7 @@ MedialAxis::build(ThickPolylines* polylines_out) {
// svg.draw(this->surface); // svg.draw(this->surface);
// svg.Close(); // svg.Close();
//} //}
this->expolygon = simplify_polygon_frontier(); simplify_polygon_frontier();
//{ //{
// std::stringstream stri; // std::stringstream stri;
// stri << "medial_axis_0.5_simplified_" << id << ".svg"; // stri << "medial_axis_0.5_simplified_" << id << ".svg";
@ -1469,7 +1479,7 @@ MedialAxis::build(ThickPolylines* polylines_out) {
// compute the Voronoi diagram and extract medial axis polylines // compute the Voronoi diagram and extract medial axis polylines
ThickPolylines pp; ThickPolylines pp;
this->polyline_from_voronoi(this->expolygon.lines(), &pp); this->polyline_from_voronoi(this->expolygon.lines(), &pp);
concatThickPolylines(pp); concatThickPolylines(pp);
//std::cout << "concatThickPolylines\n"; //std::cout << "concatThickPolylines\n";
@ -1612,7 +1622,9 @@ MedialAxis::build(ThickPolylines* polylines_out) {
// svg.Close(); // svg.Close();
//} //}
if (nozzle_diameter != min_width) { if (nozzle_diameter != min_width) {
grow_to_nozzle_diameter(pp, diff_ex(this->bounds, this->expolygon)); grow_to_nozzle_diameter(pp, diff_ex(*this->bounds, this->expolygon));
}
if(this->taper_size != 0){
taper_ends(pp); taper_ends(pp);
} }
//{ //{
@ -1633,29 +1645,30 @@ MedialAxis::build(ThickPolylines* polylines_out) {
// std::cout << "\n"; // std::cout << "\n";
//} //}
polylines_out->insert(polylines_out->end(), pp.begin(), pp.end()); polylines_out.insert(polylines_out.end(), pp.begin(), pp.end());
} }
ExtrusionEntityCollection ExtrusionEntityCollection
thin_variable_width(const ThickPolylines &polylines, ExtrusionRole role, Flow flow) { thin_variable_width(const ThickPolylines &polylines, ExtrusionRole role, Flow flow)
{
// this value determines granularity of adaptive width, as G-code does not allow // this value determines granularity of adaptive width, as G-code does not allow
// variable extrusion within a single move; this value shall only affect the amount // variable extrusion within a single move; this value shall only affect the amount
// of segments, and any pruning shall be performed before we apply this tolerance // of segments, and any pruning shall be performed before we apply this tolerance
const double tolerance = 4*SCALED_RESOLUTION;//scale_(0.05); const double tolerance = 4*SCALED_RESOLUTION;//scale_(0.05);
ExtrusionEntityCollection coll; ExtrusionEntityCollection coll;
for (const ThickPolyline &p : polylines) { for (const ThickPolyline &p : polylines) {
ExtrusionPaths paths; ExtrusionPaths paths;
ExtrusionPath path(role); ExtrusionPath path(role);
ThickLines lines = p.thicklines(); ThickLines lines = p.thicklines();
for (int i = 0; i < (int)lines.size(); ++i) { for (int i = 0; i < (int)lines.size(); ++i) {
ThickLine& line = lines[i]; ThickLine& line = lines[i];
const coordf_t line_len = line.length(); const coordf_t line_len = line.length();
if (line_len < SCALED_EPSILON) continue; if (line_len < SCALED_EPSILON) continue;
assert(line.a_width >= 0); assert(line.a_width >= 0);
assert(line.b_width >= 0); assert(line.b_width >= 0);
double thickness_delta = fabs(line.a_width - line.b_width); double thickness_delta = fabs(line.a_width - line.b_width);
@ -1750,7 +1763,6 @@ thin_variable_width(const ThickPolylines &polylines, ExtrusionRole role, Flow fl
} }
} }
} }
return coll; return coll;
} }

View File

@ -15,28 +15,57 @@ using boost::polygon::voronoi_diagram;
namespace Slic3r { namespace Slic3r {
class MedialAxis { /// This class is used to create single-line extrusion pattern with variable width to cover a ExPolygon.
/// The ends can enter a boundary area if neded, and can have a taper at each end.
/// The constructor initialize the mandatory variable.
/// you must use the setter to add the opptional settings before calling build().
class MedialAxis {
public: public:
Lines lines; //lines is here only to avoid passing it in argument of many methods. Initialized in polyline_from_voronoi. /// _expolygon: the polygon to fill
ExPolygon expolygon; /// _max_width : maximum width of the extrusion. _expolygon shouldn't have a spot where a circle diameter is higher than that (or almost).
const ExPolygon& bounds; /// _min_width : minimum width of the extrusion, every spot where a circle diameter is lower than that will be ignored (unless it's the tip of the extrusion)
const ExPolygon& surface; /// _height: height of the extrusion, used to compute the difference between width and spacing.
const coord_t max_width; MedialAxis(const ExPolygon &_expolygon, const coord_t _max_width, const coord_t _min_width, const coord_t _height)
const coord_t min_width; : surface(_expolygon), max_width(_max_width), min_width(_min_width), height(_height),
const coord_t height; bounds(&_expolygon), nozzle_diameter(_min_width), taper_size(0), stop_at_min_width(true){};
coord_t nozzle_diameter;
coord_t anchor_size; /// create the polylines_out collection of variable-width polyline to extrude.
bool stop_at_min_width; void build(ThickPolylines &polylines_out);
MedialAxis(const ExPolygon &_expolygon, const ExPolygon &_bounds, const coord_t _max_width, const coord_t _min_width, const coord_t _height) /// You shouldn't use this method as it doesn't give you the variable width. Can be useful for debugging.
: surface(_expolygon), bounds(_bounds), max_width(_max_width), min_width(_min_width), height(_height), void build(Polylines &polylines);
nozzle_diameter(_min_width),
anchor_size(0), /// optional parameter: anchor area in which the extrusion should extends into. Default : expolygon (no bound)
stop_at_min_width(true){}; MedialAxis& use_bounds(const ExPolygon & _bounds) { this->bounds = &_bounds; return *this; }
void build(ThickPolylines* polylines_out); /// optional parameter: the real minimum width : it will grow the width of every extrusion that has a width lower than that. Default : min_width (same min)
void build(Polylines* polylines); MedialAxis& use_min_real_width(const coord_t nozzle_diameter) { this->nozzle_diameter = nozzle_diameter; return *this; }
/// optional parameter: create a taper of this length at each end (inside a bound or not). Default : 0 (no taper)
MedialAxis& use_tapers(const coord_t taper_size) { this->taper_size = taper_size; return *this; }
/// optional parameter: if true, the entension inside the bounds can be cut if the width is too small. Default : true
MedialAxis& set_stop_at_min_width(const bool stop_at_min_width) { this->stop_at_min_width = stop_at_min_width; return *this; }
private: private:
static int id; /// Cache value: lines is here only to avoid passing it in argument of many methods. Initialized in polyline_from_voronoi.
Lines lines;
/// input polygon to fill
const ExPolygon& surface;
/// the copied expolygon from surface, it's modified in build() to simplify it. It's then used to create the voronoi diagram.
ExPolygon expolygon;
const ExPolygon* bounds;
/// maximum width of the extrusion. _expolygon shouldn't have a spot where a circle diameter is higher than that (or almost).
const coord_t max_width;
/// minimum width of the extrusion, every spot where a circle diameter is lower than that will be ignored (unless it's the tip of the extrusion)
const coord_t min_width;
/// height of the extrusion, used to compute the diufference between width and spacing.
const coord_t height;
/// Used to compute the real minimum width we can extrude. if != min_width, it activate grow_to_nozzle_diameter().
coord_t nozzle_diameter;
/// if != , it activates taper_ends(). Can use nozzle_diameter.
coord_t taper_size;
//if true, remove_too_* can shorten the bits created by extends_line.
bool stop_at_min_width;
//voronoi stuff
class VD : public voronoi_diagram<double> { class VD : public voronoi_diagram<double> {
public: public:
typedef double coord_type; typedef double coord_type;
@ -53,23 +82,38 @@ namespace Slic3r {
const Point& retrieve_endpoint(const VD::cell_type* cell) const; const Point& retrieve_endpoint(const VD::cell_type* cell) const;
void polyline_from_voronoi(const Lines& voronoi_edges, ThickPolylines* polylines_out); void polyline_from_voronoi(const Lines& voronoi_edges, ThickPolylines* polylines_out);
ExPolygon simplify_polygon_frontier(); // functions called by build:
/// create a simplied version of surface, store it in expolygon
void simplify_polygon_frontier();
/// fusion little polylines created (by voronoi) on the external side of a curve inside the main polyline.
void fusion_curve(ThickPolylines &pp); void fusion_curve(ThickPolylines &pp);
/// fusion polylines created by voronoi, where needed.
void main_fusion(ThickPolylines& pp); void main_fusion(ThickPolylines& pp);
/// like fusion_curve but for sharp angles like a square corner.
void fusion_corners(ThickPolylines &pp); void fusion_corners(ThickPolylines &pp);
/// extends the polylines inside bounds, use extends_line on both end
void extends_line_both_side(ThickPolylines& pp); void extends_line_both_side(ThickPolylines& pp);
/// extends the polylines inside bounds (anchors)
void extends_line(ThickPolyline& polyline, const ExPolygons& anchors, const coord_t join_width); void extends_line(ThickPolyline& polyline, const ExPolygons& anchors, const coord_t join_width);
/// remove too thin bits at start & end of polylines
void remove_too_thin_extrusion(ThickPolylines& pp); void remove_too_thin_extrusion(ThickPolylines& pp);
/// instead of keeping polyline split at each corssing, we try to create long strait polylines that can cross each other.
void concatenate_polylines_with_crossing(ThickPolylines& pp); void concatenate_polylines_with_crossing(ThickPolylines& pp);
/// remove bits around points that are too thin (can be inside the polyline)
void remove_too_thin_points(ThickPolylines& pp); void remove_too_thin_points(ThickPolylines& pp);
/// delete polylines that are too short
void remove_too_short_polylines(ThickPolylines& pp, const coord_t min_size); void remove_too_short_polylines(ThickPolylines& pp, const coord_t min_size);
/// be sure we didn't try to push more plastic than the volume defined by surface * height can receive. If overextruded, reduce all widths by the correct %.
void ensure_not_overextrude(ThickPolylines& pp); void ensure_not_overextrude(ThickPolylines& pp);
/// if nozzle_diameter > min_width, grow bits that are < width(nozzle_diameter) to width(nozzle_diameter) (don't activate that for gapfill)
void grow_to_nozzle_diameter(ThickPolylines& pp, const ExPolygons& anchors); void grow_to_nozzle_diameter(ThickPolylines& pp, const ExPolygons& anchors);
/// taper the ends of polylines (don't activate that for gapfill)
void taper_ends(ThickPolylines& pp); void taper_ends(ThickPolylines& pp);
}; };
/// create a ExtrusionEntityCollection from ThickPolylines, discretizing the variable width into little sections (of 4*SCALED_RESOLUTION length) where needed.
ExtrusionEntityCollection thin_variable_width(const ThickPolylines &polylines, ExtrusionRole role, Flow flow); ExtrusionEntityCollection thin_variable_width(const ThickPolylines &polylines, ExtrusionRole role, Flow flow);
} }

View File

@ -410,10 +410,12 @@ void PerimeterGenerator::process()
if (thin[0].area() > min_width*(ext_perimeter_width + ext_perimeter_spacing2)) { if (thin[0].area() > min_width*(ext_perimeter_width + ext_perimeter_spacing2)) {
bound.remove_point_too_near((coord_t)SCALED_RESOLUTION); bound.remove_point_too_near((coord_t)SCALED_RESOLUTION);
// the maximum thickness of our thin wall area is equal to the minimum thickness of a single loop // the maximum thickness of our thin wall area is equal to the minimum thickness of a single loop
Slic3r::MedialAxis ma(thin[0], bound, ext_perimeter_width + ext_perimeter_spacing2, min_width, this->layer_height); Slic3r::MedialAxis ma{ thin[0], ext_perimeter_width + ext_perimeter_spacing2,
ma.nozzle_diameter = (coord_t)scale_(this->ext_perimeter_flow.nozzle_diameter); min_width, coord_t(this->layer_height) };
ma.anchor_size = overlap; ma.use_bounds(bound)
ma.build(&thin_walls); .use_min_real_width((coord_t)scale_(this->ext_perimeter_flow.nozzle_diameter))
.use_tapers(overlap)
.build(thin_walls);
} }
break; break;
} }
@ -601,7 +603,7 @@ void PerimeterGenerator::process()
//remove too small gaps that are too hard to fill. //remove too small gaps that are too hard to fill.
//ie one that are smaller than an extrusion with width of min and a length of max. //ie one that are smaller than an extrusion with width of min and a length of max.
if (ex.area() > min*max) { if (ex.area() > min*max) {
ex.medial_axis(ex, max, min, &polylines, this->layer_height); MedialAxis{ ex, coord_t(max), coord_t(min), coord_t(this->layer_height) }.build(polylines);
} }
} }
if (!polylines.empty()) { if (!polylines.empty()) {

View File

@ -316,7 +316,7 @@ Point Polygon::point_projection(const Point &point) const
return proj; return proj;
} }
size_t Polygon::remove_colinear_points(coord_t max_offset){ size_t Polygon::remove_collinear_points(coord_t max_offset){
size_t nb_del = 0; size_t nb_del = 0;
if (points.size() < 3) return 0; if (points.size() < 3) return 0;

View File

@ -61,7 +61,7 @@ public:
Point point_projection(const Point &point) const; Point point_projection(const Point &point) const;
/// remove points that are (almost) on an existing line from previous & next point. /// remove points that are (almost) on an existing line from previous & next point.
/// return number of point removed /// return number of point removed
size_t remove_colinear_points(coord_t max_offset); size_t remove_collinear_points(coord_t max_offset);
}; };
extern BoundingBox get_extents(const Polygon &poly); extern BoundingBox get_extents(const Polygon &poly);

View File

@ -1984,13 +1984,20 @@ end:
if (layer->m_regions.size() == 1) { if (layer->m_regions.size() == 1) {
// Optimized version for a single region layer. // Optimized version for a single region layer.
if (layer_id == 0) { if (layer_id == 0) {
if (delta > elephant_foot_compensation) { if (elephant_foot_compensation > 0) {
delta -= elephant_foot_compensation; delta += elephant_foot_compensation;
elephant_foot_compensation = 0.f; elephant_foot_compensation = 0;
} else if (delta > 0) }else if (delta > 0) {
elephant_foot_compensation -= delta; if (-elephant_foot_compensation < delta) {
delta += elephant_foot_compensation;
elephant_foot_compensation = 0;
} else {
elephant_foot_compensation += delta;
delta = 0;
}
}
} }
if (delta != 0.f || elephant_foot_compensation > 0.f) { if (delta != 0.f || elephant_foot_compensation != 0.f) {
// Single region, growing or shrinking. // Single region, growing or shrinking.
LayerRegion *layerm = layer->m_regions.front(); LayerRegion *layerm = layer->m_regions.front();
// Apply the XY compensation. // Apply the XY compensation.
@ -1998,16 +2005,16 @@ end:
to_expolygons(std::move(layerm->slices.surfaces)) : to_expolygons(std::move(layerm->slices.surfaces)) :
offset_ex(to_expolygons(std::move(layerm->slices.surfaces)), delta); offset_ex(to_expolygons(std::move(layerm->slices.surfaces)), delta);
// Apply the elephant foot compensation. // Apply the elephant foot compensation.
if (elephant_foot_compensation > 0) { if (elephant_foot_compensation != 0) {
float elephant_foot_spacing = float(layerm->flow(frExternalPerimeter).scaled_elephant_foot_spacing()); float elephant_foot_spacing = float(layerm->flow(frExternalPerimeter).scaled_elephant_foot_spacing());
float external_perimeter_nozzle = float(scale_(this->print()->config().nozzle_diameter.get_at(layerm->region()->config().perimeter_extruder.value - 1))); float external_perimeter_nozzle = float(scale_(this->print()->config().nozzle_diameter.get_at(layerm->region()->config().perimeter_extruder.value - 1)));
// Apply the elephant foot compensation by steps of 1/10 nozzle diameter. // Apply the elephant foot compensation by steps of 1/10 nozzle diameter.
float steps = std::ceil(elephant_foot_compensation / (0.1f * external_perimeter_nozzle)); float steps = std::ceil(elephant_foot_compensation / (0.1f * external_perimeter_nozzle));
size_t nsteps = size_t(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);
} }
} }
@ -2042,7 +2049,7 @@ end:
for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id) for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id)
layer->m_regions[region_id]->trim_surfaces(trimming); layer->m_regions[region_id]->trim_surfaces(trimming);
} }
if (elephant_foot_compensation > 0.f) { if (elephant_foot_compensation != 0.f) {
// Apply the elephant foot compensation. // Apply the elephant foot compensation.
std::vector<float> elephant_foot_spacing; std::vector<float> elephant_foot_spacing;
elephant_foot_spacing.reserve(layer->m_regions.size()); elephant_foot_spacing.reserve(layer->m_regions.size());
@ -2055,12 +2062,12 @@ end:
external_perimeter_nozzle /= (float)layer->m_regions.size(); external_perimeter_nozzle /= (float)layer->m_regions.size();
// Apply the elephant foot compensation by steps of 1/10 nozzle diameter. // Apply the elephant foot compensation by steps of 1/10 nozzle diameter.
float steps = std::ceil(elephant_foot_compensation / (0.1f * external_perimeter_nozzle)); float steps = std::ceil(elephant_foot_compensation / (0.1f * external_perimeter_nozzle));
size_t nsteps = size_t(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 trimming_polygons = offset(layer->merged(float(EPSILON)), double(- step - EPSILON)); Polygons trimming_polygons = offset(layer->merged(float(EPSILON)), double(- step - EPSILON));
for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id) for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id)
layer->m_regions[region_id]->elephant_foot_compensation_step(elephant_foot_spacing[region_id] + step, trimming_polygons); layer->m_regions[region_id]->elephant_foot_compensation_step(elephant_foot_spacing[region_id] - step, trimming_polygons);
} }
} }
} }
@ -2096,7 +2103,7 @@ void PrintObject::_offset_holes(double hole_delta, LayerRegion *layerm) {
ok &= (hole.points.back().ccw_angle(*(hole.points.end() - 2), hole.points.front()) <= PI + 0.1); ok &= (hole.points.back().ccw_angle(*(hole.points.end() - 2), hole.points.front()) <= PI + 0.1);
if (ok) { if (ok) {
for (Polygon newHole : offset(hole, -hole_delta)) { for (Polygon newHole : offset(hole, hole_delta)) {
//reverse because it's a hole, not an object //reverse because it's a hole, not an object
newHole.make_clockwise(); newHole.make_clockwise();
new_ex_poly.holes.push_back(newHole); new_ex_poly.holes.push_back(newHole);

View File

@ -1663,7 +1663,7 @@ void TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slic
Polygons filered_polys = loops; Polygons filered_polys = loops;
if (this->model_precision > 0){ if (this->model_precision > 0){
for (Polygon &hole : filered_polys){ for (Polygon &hole : filered_polys){
hole.remove_colinear_points(scale_(this->model_precision)); hole.remove_collinear_points(scale_(this->model_precision));
} }
} }

View File

@ -59,3 +59,18 @@ SCENARIO("Model construction") {
} }
} }
SCENARIO("xy compensations"){
GIVEN(("A Square with a complex hole inside")){
Polygon square/*new_scale*/{ std::vector<Point>{
Point{ 100, 100 },
Point{ 200, 100 },
Point{ 200, 200 },
Point{ 100, 200 }} };
THEN("elephant and xy can compensate each other"){
}
THEN("hole and xy can compensate each othere"){
}
}
}