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
ExPolygon::medial_axis(double max_width, double min_width, Polylines* polylines) const
{
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());
}

View File

@ -57,7 +57,6 @@ public:
ExPolygons simplify(double tolerance) const;
void simplify(double tolerance, ExPolygons* expolygons) const;
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 get_trapezoids(Polygons* polygons) 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.
//ie one that are smaller than an extrusion with width of min and a length of 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) {

View File

@ -1774,7 +1774,8 @@ FillRectilinear2WGapFill::fill_surface_extrusion(const Surface *surface, const F
ExtrusionRole good_role = getRoleFromSurfaceType(params, surface);
// 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);
double rec_area = 0;
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.
//ie one that are smaller than an extrusion with width of min and a length of 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) {

View File

@ -1,3 +1,4 @@
#include "MedialAxis.hpp"
#include "BoundingBox.hpp"
#include "ExPolygon.hpp"
#include "Geometry.hpp"
@ -13,14 +14,12 @@
namespace Slic3r {
int MedialAxis::id = 0;
void
MedialAxis::build(Polylines* polylines)
MedialAxis::build(Polylines &polylines)
{
ThickPolylines tp;
this->build(&tp);
polylines->insert(polylines->end(), tp.begin(), tp.end());
this->build(tp);
polylines.insert(polylines.end(), tp.begin(), tp.end());
}
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);
new_width += to_modify->width[idx_other] * (percent_dist);
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());
//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;
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
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) {
ThickPolyline& polyline = pp[i];
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
// 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
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;
Line line(*(polyline.points.begin() + first_idx), polyline.points.back());
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());
}
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.
Point new_bound_temp;
for (Polygon hole : bounds.holes) {
for (Polygon hole : bounds->holes) {
if (hole.first_intersection(line, &new_bound_temp)) {
if (!finded || line.a.distance_to(new_bound_temp) < line.a.distance_to(new_bound)) {
finded = true;
@ -687,9 +688,8 @@ MedialAxis::extends_line(ThickPolyline& polyline, const ExPolygons& anchors, con
}
// safety check if no intersection
if (!finded) {
if (line.b.coincides_with_epsilon(polyline.points.back())) {
if (line.b.coincides_with_epsilon(polyline.points.back()))
return;
}
//check if we don't over-shoot inside us
bool is_in_anchor = false;
for (const ExPolygon& a : anchors) {
@ -724,7 +724,7 @@ MedialAxis::extends_line(ThickPolyline& polyline, const ExPolygons& anchors, con
p_obj.y() /= 2;
Line l2 = Line(new_back, p_obj);
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)) {
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);
if (find_main_branch && 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";
++idx_point;
@ -1025,7 +1027,7 @@ MedialAxis::main_fusion(ThickPolylines& pp)
}
//remove points that are outside of the geometry
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.width.erase(polyline.width.begin() + idx_point);
--idx_point;
@ -1173,7 +1175,7 @@ MedialAxis::concatenate_polylines_with_crossing(ThickPolylines& pp)
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())) {
best_candidate->reverse();
} else if (polyline.first_point().coincides_with(best_candidate->last_point())) {
@ -1302,12 +1304,12 @@ MedialAxis::ensure_not_overextrude(ThickPolylines& pp)
// compute bounds volume
double boundsVolume = 0;
boundsVolume += height*bounds.area();
boundsVolume += height*bounds->area();
// 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"
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;
}
boundsVolume += perimeterRoundGap + holesGaps;
@ -1323,59 +1325,66 @@ MedialAxis::ensure_not_overextrude(ThickPolylines& pp)
}
}
ExPolygon
void
MedialAxis::simplify_polygon_frontier()
{
//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
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;
for (size_t i = 0; i < simplified_poly.contour.points.size(); i++) {
Point &p_check = simplified_poly.contour.points[i];
for (size_t i = 0; i < this->expolygon.contour.points.size(); i++) {
Point &p_check = this->expolygon.contour.points[i];
//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
size_t prev_i = i == 0 ? simplified_poly.contour.points.size() - 1 : (i - 1);
size_t next_i = i == simplified_poly.contour.points.size() - 1 ? 0 : (i + 1);
const Point* closest = bounds.contour.closest_point(p_check);
size_t prev_i = i == 0 ? this->expolygon.contour.points.size() - 1 : (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);
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.y() = closest->y();
need_intersect = true;
} else {
simplified_poly.contour.points.erase(simplified_poly.contour.points.begin() + i);
this->expolygon.contour.points.erase(this->expolygon.contour.points.begin() + i);
i--;
}
}
}
if (need_intersect) {
ExPolygons simplified_polys = intersection_ex(simplified_poly, bounds);
if (simplified_polys.size() == 1) {
simplified_poly = simplified_polys[0];
ExPolygons simplified_polygons = intersection_ex(this->expolygon, *bounds);
if (simplified_polygons.size() == 1) {
this->expolygon = simplified_polygons[0];
} 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())
simplified_poly.remove_point_too_near((coord_t)SCALED_RESOLUTION);
return simplified_poly;
if (!this->expolygon.contour.points.empty())
this->expolygon.remove_point_too_near((coord_t)SCALED_RESOLUTION);
}
/// Grow the extrusion to at least nozzle_diameter*1.05 (lowest safe extrusion width)
/// Do not grow points inside the anchor.
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
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();
//ensure the width is not lower than 0.4.
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();
//ensure the width is not lower than min_width.
for (ThickPolyline& polyline : pp) {
for (int i = 0; i < polyline.points.size(); ++i) {
bool is_anchored = false;
@ -1392,10 +1401,11 @@ MedialAxis::grow_to_nozzle_diameter(ThickPolylines& pp, const ExPolygons& anchor
}
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).
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;
//ensure the width is not lower than min_size.
for (ThickPolyline& polyline : pp) {
@ -1442,8 +1452,8 @@ MedialAxis::taper_ends(ThickPolylines& pp) {
}
void
MedialAxis::build(ThickPolylines* polylines_out) {
this->id++;
MedialAxis::build(ThickPolylines &polylines_out)
{
//std::cout << this->id << "\n";
//{
// std::stringstream stri;
@ -1452,7 +1462,7 @@ MedialAxis::build(ThickPolylines* polylines_out) {
// svg.draw(this->surface);
// svg.Close();
//}
this->expolygon = simplify_polygon_frontier();
simplify_polygon_frontier();
//{
// std::stringstream stri;
// stri << "medial_axis_0.5_simplified_" << id << ".svg";
@ -1612,7 +1622,9 @@ MedialAxis::build(ThickPolylines* polylines_out) {
// svg.Close();
//}
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);
}
//{
@ -1633,12 +1645,13 @@ MedialAxis::build(ThickPolylines* polylines_out) {
// std::cout << "\n";
//}
polylines_out->insert(polylines_out->end(), pp.begin(), pp.end());
polylines_out.insert(polylines_out.end(), pp.begin(), pp.end());
}
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
// 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
@ -1750,7 +1763,6 @@ thin_variable_width(const ThickPolylines &polylines, ExtrusionRole role, Flow fl
}
}
}
return coll;
}

View File

@ -15,28 +15,57 @@ using boost::polygon::voronoi_diagram;
namespace Slic3r {
/// 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:
Lines lines; //lines is here only to avoid passing it in argument of many methods. Initialized in polyline_from_voronoi.
ExPolygon expolygon;
const ExPolygon& bounds;
const ExPolygon& surface;
const coord_t max_width;
const coord_t min_width;
const coord_t height;
coord_t nozzle_diameter;
coord_t anchor_size;
bool stop_at_min_width;
MedialAxis(const ExPolygon &_expolygon, const ExPolygon &_bounds, const coord_t _max_width, const coord_t _min_width, const coord_t _height)
: surface(_expolygon), bounds(_bounds), max_width(_max_width), min_width(_min_width), height(_height),
nozzle_diameter(_min_width),
anchor_size(0),
stop_at_min_width(true){};
void build(ThickPolylines* polylines_out);
void build(Polylines* polylines);
/// _expolygon: the polygon to fill
/// _max_width : maximum width of the extrusion. _expolygon shouldn't have a spot where a circle diameter is higher than that (or almost).
/// _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)
/// _height: height of the extrusion, used to compute the difference between width and spacing.
MedialAxis(const ExPolygon &_expolygon, const coord_t _max_width, const coord_t _min_width, const coord_t _height)
: surface(_expolygon), max_width(_max_width), min_width(_min_width), height(_height),
bounds(&_expolygon), nozzle_diameter(_min_width), taper_size(0), stop_at_min_width(true){};
/// create the polylines_out collection of variable-width polyline to extrude.
void build(ThickPolylines &polylines_out);
/// You shouldn't use this method as it doesn't give you the variable width. Can be useful for debugging.
void build(Polylines &polylines);
/// optional parameter: anchor area in which the extrusion should extends into. Default : expolygon (no bound)
MedialAxis& use_bounds(const ExPolygon & _bounds) { this->bounds = &_bounds; return *this; }
/// 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)
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:
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> {
public:
typedef double coord_type;
@ -53,23 +82,38 @@ namespace Slic3r {
const Point& retrieve_endpoint(const VD::cell_type* cell) const;
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);
/// fusion polylines created by voronoi, where needed.
void main_fusion(ThickPolylines& pp);
/// like fusion_curve but for sharp angles like a square corner.
void fusion_corners(ThickPolylines &pp);
/// extends the polylines inside bounds, use extends_line on both end
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);
/// remove too thin bits at start & end of polylines
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);
/// remove bits around points that are too thin (can be inside the polyline)
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);
/// 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);
/// 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);
/// taper the ends of polylines (don't activate that for gapfill)
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);
}

View File

@ -410,10 +410,12 @@ void PerimeterGenerator::process()
if (thin[0].area() > min_width*(ext_perimeter_width + ext_perimeter_spacing2)) {
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
Slic3r::MedialAxis ma(thin[0], bound, ext_perimeter_width + ext_perimeter_spacing2, min_width, this->layer_height);
ma.nozzle_diameter = (coord_t)scale_(this->ext_perimeter_flow.nozzle_diameter);
ma.anchor_size = overlap;
ma.build(&thin_walls);
Slic3r::MedialAxis ma{ thin[0], ext_perimeter_width + ext_perimeter_spacing2,
min_width, coord_t(this->layer_height) };
ma.use_bounds(bound)
.use_min_real_width((coord_t)scale_(this->ext_perimeter_flow.nozzle_diameter))
.use_tapers(overlap)
.build(thin_walls);
}
break;
}
@ -601,7 +603,7 @@ void PerimeterGenerator::process()
//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.
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()) {

View File

@ -316,7 +316,7 @@ Point Polygon::point_projection(const Point &point) const
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;
if (points.size() < 3) return 0;

View File

@ -61,7 +61,7 @@ public:
Point point_projection(const Point &point) const;
/// remove points that are (almost) on an existing line from previous & next point.
/// 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);

View File

@ -1984,13 +1984,20 @@ end:
if (layer->m_regions.size() == 1) {
// Optimized version for a single region layer.
if (layer_id == 0) {
if (delta > elephant_foot_compensation) {
delta -= elephant_foot_compensation;
elephant_foot_compensation = 0.f;
} else if (delta > 0)
elephant_foot_compensation -= delta;
if (elephant_foot_compensation > 0) {
delta += elephant_foot_compensation;
elephant_foot_compensation = 0;
}else if (delta > 0) {
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.
LayerRegion *layerm = layer->m_regions.front();
// Apply the XY compensation.
@ -1998,16 +2005,16 @@ end:
to_expolygons(std::move(layerm->slices.surfaces)) :
offset_ex(to_expolygons(std::move(layerm->slices.surfaces)), delta);
// 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 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.
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;
for (size_t i = 0; i < nsteps; ++ i) {
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);
}
}
@ -2042,7 +2049,7 @@ end:
for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id)
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.
std::vector<float> elephant_foot_spacing;
elephant_foot_spacing.reserve(layer->m_regions.size());
@ -2055,12 +2062,12 @@ end:
external_perimeter_nozzle /= (float)layer->m_regions.size();
// Apply the elephant foot compensation by steps of 1/10 nozzle diameter.
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;
for (size_t i = 0; i < nsteps; ++ i) {
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)
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);
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
newHole.make_clockwise();
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;
if (this->model_precision > 0){
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"){
}
}
}