Variable-width gap fill. Yay! #2960

This commit is contained in:
Alessandro Ranellucci 2016-03-19 15:33:58 +01:00
parent 5ff7511a14
commit 6dc42ee902
19 changed files with 379 additions and 166 deletions

View file

@ -6,7 +6,6 @@
#include "ClipperUtils.hpp"
#include "polypartition.h"
#include "poly2tri/poly2tri.h"
#include <algorithm>
#include <list>
@ -171,10 +170,11 @@ ExPolygon::simplify(double tolerance, ExPolygons* expolygons) const
}
void
ExPolygon::medial_axis(double max_width, double min_width, Polylines* polylines) const
ExPolygon::medial_axis(double max_width, double min_width, ThickPolylines* polylines) const
{
// init helper object
Slic3r::Geometry::MedialAxis ma(max_width, min_width);
ma.expolygon = this;
// populate list of segments for the Voronoi diagram
ma.lines = this->contour.lines();
@ -184,41 +184,71 @@ ExPolygon::medial_axis(double max_width, double min_width, Polylines* polylines)
}
// compute the Voronoi diagram
Polylines pp;
ThickPolylines pp;
ma.build(&pp);
// clip segments to our expolygon area
// (do this before extending endpoints as external segments coule be extended into
// expolygon, this leaving wrong things inside)
pp = intersection(pp, *this);
/*
SVG svg("medial_axis.svg");
svg.draw(*this);
svg.draw(pp);
svg.Close();
*/
// extend initial and final segments of each polyline (they will be clipped)
// unless they represent closed loops
for (Polylines::iterator polyline = pp.begin(); polyline != pp.end(); ++polyline) {
if (polyline->points.front().distance_to(polyline->points.back()) < min_width) continue;
// TODO: we should *not* extend endpoints where other polylines start/end
// (such as T joints, which are returned as three polylines by MedialAxis)
polyline->extend_start(max_width);
polyline->extend_end(max_width);
}
// clip again after extending endpoints to prevent them from exceeding the expolygon boundaries
pp = intersection(pp, *this);
// remove too short polylines
// (we can't do this check before endpoints extension and clipping because we don't
// know how long will the endpoints be extended since it depends on polygon thickness
// which is variable - extension will be <= max_width/2 on each side)
for (size_t i = 0; i < pp.size(); ++i) {
if (pp[i].length() < max_width) {
ThickPolyline& polyline = pp[i];
// extend initial and final segments of each polyline if they're actual endpoints
/* 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 */
Point new_front = polyline.points.front();
Point new_back = polyline.points.back();
if (polyline.endpoints.front() && !this->has_boundary_point(new_front)) {
Line line(polyline.points.front(), polyline.points[1]);
// prevent the line from touching on the other side, otherwise intersection() might return that solution
if (polyline.points.size() == 2) line.b = line.midpoint();
line.extend_start(max_width);
(void)this->contour.intersection(line, &new_front);
}
if (polyline.endpoints.back() && !this->has_boundary_point(new_back)) {
Line line(
*(polyline.points.end() - 2),
polyline.points.back()
);
// prevent the line from touching on the other side, otherwise intersection() might return that solution
if (polyline.points.size() == 2) line.a = line.midpoint();
line.extend_end(max_width);
(void)this->contour.intersection(line, &new_back);
}
polyline.points.front() = new_front;
polyline.points.back() = new_back;
/* remove too short polylines
(we can't do this check before endpoints extension and clipping because we don't
know how long will the endpoints be extended since it depends on polygon thickness
which is variable - extension will be <= max_width/2 on each side) */
if (polyline.length() < max_width) {
pp.erase(pp.begin() + i);
--i;
continue;
}
}
polylines->insert(polylines->end(), pp.begin(), pp.end());
}
void
ExPolygon::medial_axis(double max_width, double min_width, Polylines* polylines) const
{
ThickPolylines tp;
this->medial_axis(max_width, min_width, &tp);
polylines->insert(polylines->end(), tp.begin(), tp.end());
}
void
ExPolygon::get_trapezoids(Polygons* polygons) const
{

View file

@ -32,6 +32,7 @@ class ExPolygon
Polygons simplify_p(double tolerance) const;
ExPolygons simplify(double tolerance) const;
void simplify(double tolerance, ExPolygons* expolygons) const;
void medial_axis(double max_width, double min_width, ThickPolylines* 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, double angle) const;

View file

@ -114,7 +114,7 @@ Polygons
ExtrusionPath::grow() const
{
Polygons pp;
offset(this->polyline, &pp, +this->width/2);
offset(this->polyline, &pp, +scale_(this->width/2));
return pp;
}

View file

@ -104,6 +104,10 @@ class ExtrusionLoop : public ExtrusionEntity
ExtrusionLoop(ExtrusionLoopRole role = elrDefault) : role(role) {};
ExtrusionLoop(const ExtrusionPaths &paths, ExtrusionLoopRole role = elrDefault)
: paths(paths), role(role) {};
ExtrusionLoop(const ExtrusionPath &path, ExtrusionLoopRole role = elrDefault)
: role(role) {
this->paths.push_back(path);
};
bool is_loop() const {
return true;
};

View file

@ -101,6 +101,20 @@ ExtrusionEntityCollection::append(const ExtrusionPaths &paths)
this->append(*path);
}
void
ExtrusionEntityCollection::replace(size_t i, const ExtrusionEntity &entity)
{
delete this->entities[i];
this->entities[i] = entity.clone();
}
void
ExtrusionEntityCollection::remove(size_t i)
{
delete this->entities[i];
this->entities.erase(this->entities.begin() + i);
}
ExtrusionEntityCollection
ExtrusionEntityCollection::chained_path(bool no_reverse, std::vector<size_t>* orig_indices) const
{

View file

@ -36,6 +36,8 @@ class ExtrusionEntityCollection : public ExtrusionEntity
void append(const ExtrusionEntity &entity);
void append(const ExtrusionEntitiesPtr &entities);
void append(const ExtrusionPaths &paths);
void replace(size_t i, const ExtrusionEntity &entity);
void remove(size_t i);
ExtrusionEntityCollection chained_path(bool no_reverse = false, std::vector<size_t>* orig_indices = NULL) const;
void chained_path(ExtrusionEntityCollection* retval, bool no_reverse = false, std::vector<size_t>* orig_indices = NULL) const;
void chained_path_from(Point start_near, ExtrusionEntityCollection* retval, bool no_reverse = false, std::vector<size_t>* orig_indices = NULL) const;

View file

@ -5,12 +5,13 @@
#include "PolylineCollection.hpp"
#include "clipper.hpp"
#include <algorithm>
#include <cassert>
#include <cmath>
#include <list>
#include <map>
#include <set>
#include <utility>
#include <vector>
#include <assert.h>
#ifdef SLIC3R_DEBUG
#include "SVG.hpp"
@ -289,19 +290,8 @@ arrange(size_t total_parts, Pointf part, coordf_t dist, const BoundingBoxf* bb)
return positions;
}
Line
MedialAxis::edge_to_line(const VD::edge_type &edge) const
{
Line line;
line.a.x = edge.vertex0()->x();
line.a.y = edge.vertex0()->y();
line.b.x = edge.vertex1()->x();
line.b.y = edge.vertex1()->y();
return line;
}
void
MedialAxis::build(Polylines* polylines)
MedialAxis::build(ThickPolylines* polylines)
{
/*
// build bounding box (we use it for clipping infinite segments)
@ -317,7 +307,7 @@ MedialAxis::build(Polylines* polylines)
for (VD::const_edge_iterator edge = this->vd.edges().begin(); edge != this->vd.edges().end(); ++edge) {
if (edge->is_infinite()) continue;
Polyline polyline;
ThickPolyline polyline;
polyline.points.push_back(Point( edge->vertex0()->x(), edge->vertex0()->y() ));
polyline.points.push_back(Point( edge->vertex1()->x(), edge->vertex1()->y() ));
polylines->push_back(polyline);
@ -373,7 +363,7 @@ MedialAxis::build(Polylines* polylines)
assert(vertex_edges[v].size() == 1);
edge_t* edge = *vertex_edges[v].begin();
if (!this->is_valid_edge(*edge)) {
if (!this->validate_edge(*edge)) {
// if edge is not valid, erase it and its twin from edge list
(void)this->edges.erase(edge);
(void)this->edges.erase(edge->twin());
@ -400,22 +390,36 @@ MedialAxis::build(Polylines* polylines)
edge_t &edge = **this->edges.begin();
// start a polyline
Polyline polyline;
ThickPolyline polyline;
polyline.points.push_back(Point( edge.vertex0()->x(), edge.vertex0()->y() ));
polyline.points.push_back(Point( edge.vertex1()->x(), edge.vertex1()->y() ));
polyline.width.push_back(this->thickness[&edge].first);
polyline.width.push_back(this->thickness[&edge].second);
// remove this edge and its twin from the available edges
(void)this->edges.erase(&edge);
(void)this->edges.erase(edge.twin());
// get next points
this->process_edge_neighbors(edge, &polyline.points);
this->process_edge_neighbors(edge, &polyline.points, &polyline.width, &polyline.endpoints);
// get previous points
{
Points pp;
this->process_edge_neighbors(*edge.twin(), &pp);
std::vector<coordf_t> width;
std::vector<bool> endpoints;
this->process_edge_neighbors(*edge.twin(), &pp, &width, &endpoints);
polyline.points.insert(polyline.points.begin(), pp.rbegin(), pp.rend());
polyline.width.insert(polyline.width.begin(), width.rbegin(), width.rend());
polyline.endpoints.insert(polyline.endpoints.begin(), endpoints.rbegin(), endpoints.rend());
}
assert(polyline.width.size() == polyline.points.size()*2 - 2);
assert(polyline.endpoints.size() == polyline.points.size());
if (polyline.first_point().coincides_with(polyline.last_point())) {
polyline.endpoints.front() = false;
polyline.endpoints.back() = false;
}
// append polyline to result
@ -424,7 +428,16 @@ MedialAxis::build(Polylines* polylines)
}
void
MedialAxis::process_edge_neighbors(const VD::edge_type& edge, Points* points)
MedialAxis::build(Polylines* polylines)
{
ThickPolylines tp;
this->build(&tp);
polylines->insert(polylines->end(), tp.begin(), tp.end());
}
void
MedialAxis::process_edge_neighbors(const VD::edge_type& edge, Points* points,
std::vector<coordf_t>* width, std::vector<bool>* endpoints)
{
// Since rot_next() works on the edge starting point but we want
// to find neighbors on the ending point, we just swap edge with
@ -439,18 +452,26 @@ MedialAxis::process_edge_neighbors(const VD::edge_type& edge, Points* points)
// if we have a single neighbor then we can continue recursively
if (neighbors.size() == 1) {
endpoints->push_back(false);
const VD::edge_type& neighbor = *neighbors.front();
points->push_back(Point( neighbor.vertex1()->x(), neighbor.vertex1()->y() ));
width->push_back(this->thickness[&neighbor].first);
width->push_back(this->thickness[&neighbor].second);
(void)this->edges.erase(&neighbor);
(void)this->edges.erase(neighbor.twin());
this->process_edge_neighbors(neighbor, points);
this->process_edge_neighbors(neighbor, points, width, endpoints);
} else if (neighbors.size() == 0) {
endpoints->push_back(true);
} else {
// T-shaped or star-shaped joint
endpoints->push_back(false);
}
}
bool
MedialAxis::is_valid_edge(const VD::edge_type& edge) const
MedialAxis::validate_edge(const VD::edge_type& edge)
{
/* If the cells sharing this edge have a common vertex, we're not interested
/* If the cells sharing this edge have a common vertex, we're not (probably) interested
in this edge. Why? Because it means that the edge lies on the bisector of
two contiguous input lines and it was included in the Voronoi graph because
it's the locus of centers of circles tangent to both vertices. Due to the
@ -481,24 +502,20 @@ MedialAxis::is_valid_edge(const VD::edge_type& edge) const
// and we might need to skip the edge since it's not really part of
// our skeleton
// get perpendicular distance of each edge vertex to the segment(s)
double dist0 = segment1.a.distance_to(segment2.b);
double dist1 = segment1.b.distance_to(segment2.a);
/* Calculate perpendicular distance. We consider segment2 instead of segment1
because our Voronoi edge is part of a CCW sequence going around its Voronoi cell
(segment).
This means that such segment is on the left on our edge, and goes backwards.
So we use the cell of the twin edge, which is located on the right of our edge
and goes in the same direction as it. This way we can map dist0 and dist1
correctly. */
const Line line(
Point( edge.vertex0()->x(), edge.vertex0()->y() ),
Point( edge.vertex1()->x(), edge.vertex1()->y() )
);
coordf_t dist0 = segment2.a.perp_distance_to(line)*2;
coordf_t dist1 = segment2.b.perp_distance_to(line)*2;
/*
Line line = this->edge_to_line(edge);
double diff = fabs(dist1 - dist0);
double dist_between_segments1 = segment1.a.distance_to(segment2);
double dist_between_segments2 = segment1.b.distance_to(segment2);
printf("w = %f/%f, dist0 = %f, dist1 = %f, diff = %f, seg1len = %f, seg2len = %f, edgelen = %f, s2s = %f / %f\n",
unscale(this->max_width), unscale(this->min_width),
unscale(dist0), unscale(dist1), unscale(diff),
unscale(segment1.length()), unscale(segment2.length()),
unscale(line.length()),
unscale(dist_between_segments1), unscale(dist_between_segments2)
);
*/
// if this edge is the centerline for a very thin area, we might want to skip it
// in case the area is too thin
if (dist0 < this->min_width && dist1 < this->min_width) {
@ -506,6 +523,11 @@ MedialAxis::is_valid_edge(const VD::edge_type& edge) const
return false;
}
if (this->expolygon != NULL && !this->expolygon->contains(line))
return false;
this->thickness[&edge] = std::make_pair(dist0, dist1);
return true;
}

View file

@ -3,6 +3,7 @@
#include "libslic3r.h"
#include "BoundingBox.hpp"
#include "ExPolygon.hpp"
#include "Polygon.hpp"
#include "Polyline.hpp"
@ -43,18 +44,22 @@ class MedialAxis {
public:
Points points;
Lines lines;
const ExPolygon* expolygon;
double max_width;
double min_width;
MedialAxis(double _max_width, double _min_width) : max_width(_max_width), min_width(_min_width) {};
MedialAxis(double _max_width, double _min_width)
: max_width(_max_width), min_width(_min_width), expolygon(NULL) {};
void build(ThickPolylines* polylines);
void build(Polylines* polylines);
private:
typedef voronoi_diagram<double> VD;
VD vd;
std::set<const VD::edge_type*> edges;
Line edge_to_line(const VD::edge_type &edge) const;
void process_edge_neighbors(const voronoi_diagram<double>::edge_type& edge, Points* points);
bool is_valid_edge(const voronoi_diagram<double>::edge_type& edge) const;
std::map<const VD::edge_type*, std::pair<coordf_t,coordf_t> > thickness;
void process_edge_neighbors(const voronoi_diagram<double>::edge_type& edge,
Points* points, std::vector<coordf_t>* width, std::vector<bool>* endpoints);
bool validate_edge(const voronoi_diagram<double>::edge_type& edge);
const Line& retrieve_segment(const voronoi_diagram<double>::cell_type& cell) const;
};

View file

@ -163,6 +163,55 @@ Line::normal() const
return Vector((this->b.y - this->a.y), -(this->b.x - this->a.x));
}
void
Line::extend_end(double distance)
{
// relocate last point by extending the segment by the specified length
Line line = *this;
line.reverse();
this->b = line.point_at(-distance);
}
void
Line::extend_start(double distance)
{
// relocate first point by extending the first segment by the specified length
this->a = this->point_at(-distance);
}
bool
Line::intersection(const Line& line, Point* intersection) const
{
double denom = ((line.b.y - line.a.y)*(this->b.x - this->a.x)) -
((line.b.x - line.a.x)*(this->b.y - this->a.y));
double nume_a = ((line.b.x - line.a.x)*(this->a.y - line.a.y)) -
((line.b.y - line.a.y)*(this->a.x - line.a.x));
double nume_b = ((this->b.x - this->a.x)*(this->a.y - line.a.y)) -
((this->b.y - this->a.y)*(this->a.x - line.a.x));
if (fabs(denom) < EPSILON) {
if (fabs(nume_a) < EPSILON && fabs(nume_b) < EPSILON) {
return false; // coincident
}
return false; // parallel
}
double ua = nume_a / denom;
double ub = nume_b / denom;
if (ua >= 0 && ua <= 1.0f && ub >= 0 && ub <= 1.0f)
{
// Get the intersection point.
intersection->x = this->a.x + ua*(this->b.x - this->a.x);
intersection->y = this->a.y + ua*(this->b.y - this->a.y);
return true;
}
return false; // not intersecting
}
Pointf3
Linef3::intersect_plane(double z) const
{

View file

@ -9,7 +9,9 @@ namespace Slic3r {
class Line;
class Linef3;
class Polyline;
class ThickLine;
typedef std::vector<Line> Lines;
typedef std::vector<ThickLine> ThickLines;
class Line
{
@ -39,6 +41,18 @@ class Line
double direction() const;
Vector vector() const;
Vector normal() const;
void extend_end(double distance);
void extend_start(double distance);
bool intersection(const Line& line, Point* intersection) const;
};
class ThickLine : public Line
{
public:
coordf_t a_width, b_width;
ThickLine() : a_width(0), b_width(0) {};
ThickLine(Point _a, Point _b) : a_width(0), b_width(0), Line(_a, _b) {};
};
class Linef

View file

@ -118,6 +118,16 @@ MultiPoint::append(const Points::const_iterator &begin, const Points::const_iter
this->points.insert(this->points.end(), begin, end);
}
bool
MultiPoint::intersection(const Line& line, Point* intersection) const
{
Lines lines = this->lines();
for (Lines::const_iterator it = lines.begin(); it != lines.end(); ++it) {
if (it->intersection(line, intersection)) return true;
}
return false;
}
Points
MultiPoint::_douglas_peucker(const Points &points, const double tolerance)
{

View file

@ -36,6 +36,7 @@ class MultiPoint
void append(const Point &point);
void append(const Points &points);
void append(const Points::const_iterator &begin, const Points::const_iterator &end);
bool intersection(const Line& line, Point* intersection) const;
static Points _douglas_peucker(const Points &points, const double tolerance);
};

View file

@ -1,6 +1,7 @@
#include "PerimeterGenerator.hpp"
#include "ClipperUtils.hpp"
#include "ExtrusionEntityCollection.hpp"
#include <cmath>
namespace Slic3r {
@ -91,7 +92,7 @@ PerimeterGenerator::process()
// the following offset2 ensures almost nothing in @thin_walls is narrower than $min_width
// (actually, something larger than that still may exist due to mitering or other causes)
coord_t min_width = ext_pwidth / 2;
coord_t min_width = scale_(this->ext_perimeter_flow.nozzle_diameter / 3);
ExPolygons expp = offset2_ex(diffpp, -min_width/2, +min_width/2);
// the maximum thickness of our thin wall area is equal to the minimum thickness of a single loop
@ -252,43 +253,37 @@ PerimeterGenerator::process()
// fill gaps
if (!gaps.empty()) {
/*
if (false) {
require "Slic3r/SVG.pm";
Slic3r::SVG::output(
"gaps.svg",
expolygons => union_ex(\@gaps),
);
}
SVG svg("gaps.svg");
svg.draw(union_ex(gaps));
svg.Close();
*/
// where $pwidth < thickness < 2*$pspacing, infill with width = 2*$pwidth
// where 0.1*$pwidth < thickness < $pwidth, infill with width = 1*$pwidth
std::vector<PerimeterGeneratorGapSize> gap_sizes;
gap_sizes.push_back(PerimeterGeneratorGapSize(pwidth, 2*pspacing, 2*pwidth));
gap_sizes.push_back(PerimeterGeneratorGapSize(0.1*pwidth, pwidth, 1*pwidth));
// collapse
double min = 0.1*pwidth * (1 - INSET_OVERLAP_TOLERANCE);
double max = 2*pspacing;
ExPolygons gaps_ex = diff_ex(
offset2(gaps, -min/2, +min/2),
offset2(gaps, -max/2, +max/2),
true
);
for (std::vector<PerimeterGeneratorGapSize>::const_iterator gap_size = gap_sizes.begin();
gap_size != gap_sizes.end(); ++gap_size) {
ExtrusionEntityCollection gap_fill = this->_fill_gaps(gap_size->min,
gap_size->max, unscale(gap_size->width), gaps);
this->gap_fill->append(gap_fill.entities);
ThickPolylines polylines;
for (ExPolygons::const_iterator ex = gaps_ex.begin(); ex != gaps_ex.end(); ++ex)
ex->medial_axis(max, min/2, &polylines);
if (!polylines.empty()) {
ExtrusionEntityCollection gap_fill = this->_variable_width(polylines,
erGapFill, this->solid_infill_flow);
// Make sure we don't infill narrow parts that are already gap-filled
// (we only consider this surface's gaps to reduce the diff() complexity).
// Growing actual extrusions ensures that gaps not filled by medial axis
// are not subtracted from fill surfaces (they might be too short gaps
// that medial axis skips but infill might join with other infill regions
// and use zigzag).
coord_t dist = gap_size->width/2;
Polygons filled;
for (ExtrusionEntitiesPtr::const_iterator it = gap_fill.entities.begin();
it != gap_fill.entities.end(); ++it) {
Polygons f;
offset((*it)->as_polyline(), &f, dist);
filled.insert(filled.end(), f.begin(), f.end());
}
last = diff(last, filled);
gaps = diff(gaps, filled); // prevent more gap fill here
this->gap_fill->append(gap_fill.entities);
/* Make sure we don't infill narrow parts that are already gap-filled
(we only consider this surface's gaps to reduce the diff() complexity).
Growing actual extrusions ensures that gaps not filled by medial axis
are not subtracted from fill surfaces (they might be too short gaps
that medial axis skips but infill might join with other infill regions
and use zigzag). */
last = diff(last, gap_fill.grow());
}
}
@ -454,51 +449,92 @@ PerimeterGenerator::_traverse_loops(const PerimeterGeneratorLoops &loops,
}
ExtrusionEntityCollection
PerimeterGenerator::_fill_gaps(double min, double max, double w,
const Polygons &gaps) const
PerimeterGenerator::_variable_width(const ThickPolylines &polylines, ExtrusionRole role, Flow flow) const
{
const double tolerance = scale_(0.1);
ExtrusionEntityCollection coll;
min *= (1 - INSET_OVERLAP_TOLERANCE);
ExPolygons curr = diff_ex(
offset2(gaps, -min/2, +min/2),
offset2(gaps, -max/2, +max/2),
true
);
Polylines polylines;
for (ExPolygons::const_iterator ex = curr.begin(); ex != curr.end(); ++ex)
ex->medial_axis(max, min/2, &polylines);
if (polylines.empty())
return coll;
#ifdef SLIC3R_DEBUG
if (!curr.empty())
printf(" %zu gaps filled with extrusion width = %f\n", curr.size(), w);
#endif
//my $flow = $layerm->flow(FLOW_ROLE_SOLID_INFILL, 0, $w);
Flow flow(
w, this->layer_height, this->solid_infill_flow.nozzle_diameter
);
double mm3_per_mm = flow.mm3_per_mm();
for (Polylines::const_iterator p = polylines.begin(); p != polylines.end(); ++p) {
ExtrusionPath path(erGapFill);
path.polyline = *p;
path.mm3_per_mm = mm3_per_mm;
path.width = flow.width;
path.height = this->layer_height;
for (ThickPolylines::const_iterator p = polylines.begin(); p != polylines.end(); ++p) {
ExtrusionPaths paths;
ExtrusionPath path(role);
ThickLines lines = p->thicklines();
for (size_t i = 0; i < lines.size(); ++i) {
const ThickLine& line = lines[i];
const double thickness_delta = fabs(line.a_width - line.b_width);
if (thickness_delta > tolerance) {
const unsigned short segments = ceil(thickness_delta / tolerance);
const coordf_t line_len = line.length();
const coordf_t seg_len = line_len / segments;
Points pp;
std::vector<coordf_t> width;
{
pp.push_back(line.a);
width.push_back(line.a_width);
for (size_t j = 1; j < segments; ++j) {
pp.push_back(line.point_at(j*seg_len));
coordf_t w = line.a_width + (j*seg_len) * (line.b_width-line.a_width) / line_len;
width.push_back(w);
width.push_back(w);
}
pp.push_back(line.b);
width.push_back(line.b_width);
assert(pp.size() == segments + 1);
assert(width.size() == segments*2);
}
// delete this line and insert new ones
lines.erase(lines.begin() + i);
for (size_t j = 0; j < segments; ++j) {
ThickLine new_line(pp[j], pp[j+1]);
new_line.a_width = width[2*j];
new_line.b_width = width[2*j+1];
lines.insert(lines.begin() + i + j, new_line);
}
--i;
continue;
}
const double w = fmax(line.a_width, line.b_width);
if (path.polyline.points.empty()) {
path.polyline.append(line.a);
path.polyline.append(line.b);
flow.width = unscale(w);
#ifdef SLIC3R_DEBUG
printf(" filling %f gap\n", flow.width);
#endif
path.mm3_per_mm = flow.mm3_per_mm();
path.width = flow.width;
path.height = flow.height;
} else if (fabs(flow.width - w) <= tolerance) {
// the width difference between this line and the current flow width is
// within the accepted tolerance
path.polyline.append(line.b);
} else {
// we need to initialize a new line
paths.push_back(path);
path = ExtrusionPath(role);
--i;
}
}
if (!path.polyline.points.empty())
paths.push_back(path);
if (p->is_valid() && p->first_point().coincides_with(p->last_point())) {
// since medial_axis() now returns only Polyline objects, detect loops here
ExtrusionLoop loop;
loop.paths.push_back(path);
coll.append(loop);
} else {
coll.append(path);
// loop through generated paths
for (ExtrusionPaths::const_iterator p = paths.begin(); p != paths.end(); ++p) {
if (p->polyline.is_valid()) {
if (p->first_point().coincides_with(p->last_point())) {
// since medial_axis() now returns only Polyline objects, detect loops here
coll.append(ExtrusionLoop(*p));
} else {
coll.append(*p);
}
}
}
}

View file

@ -66,17 +66,8 @@ class PerimeterGenerator {
ExtrusionEntityCollection _traverse_loops(const PerimeterGeneratorLoops &loops,
Polylines &thin_walls) const;
ExtrusionEntityCollection _fill_gaps(double min, double max, double w,
const Polygons &gaps) const;
};
class PerimeterGeneratorGapSize {
public:
coord_t min;
coord_t max;
coord_t width;
PerimeterGeneratorGapSize(coord_t min, coord_t max, coord_t width)
: min(min), max(max), width(width) {};
ExtrusionEntityCollection _variable_width
(const ThickPolylines &polylines, ExtrusionRole role, Flow flow) const;
};
}

View file

@ -23,7 +23,7 @@ class Polygon : public MultiPoint {
Polygon() {};
explicit Polygon(const Points &points): MultiPoint(points) {};
Point last_point() const;
Lines lines() const;
virtual Lines lines() const;
Polyline split_at_vertex(const Point &point) const;
Polyline split_at_index(int index) const;
Polyline split_at_first_point() const;

View file

@ -83,17 +83,18 @@ void
Polyline::extend_end(double distance)
{
// relocate last point by extending the last segment by the specified length
Line line(this->points[ this->points.size()-2 ], this->points.back());
this->points.pop_back();
this->points.push_back(line.point_at(line.length() + distance));
Line line(
this->points.back(),
*(this->points.end() - 2)
);
this->points.back() = line.point_at(-distance);
}
void
Polyline::extend_start(double distance)
{
// relocate first point by extending the first segment by the specified length
Line line(this->points[1], this->points.front());
this->points[0] = line.point_at(line.length() + distance);
this->points.front() = Line(this->points.front(), this->points[1]).point_at(-distance);
}
/* this method returns a collection of points picked on the polygon contour
@ -218,4 +219,20 @@ Polyline::wkt() const
return wkt.str();
}
ThickLines
ThickPolyline::thicklines() const
{
ThickLines lines;
if (this->points.size() >= 2) {
lines.reserve(this->points.size() - 1);
for (size_t i = 0; i < this->points.size()-1; ++i) {
ThickLine line(this->points[i], this->points[i+1]);
line.a_width = this->width[2*i];
line.b_width = this->width[2*i+1];
lines.push_back(line);
}
}
return lines;
}
}

View file

@ -5,12 +5,14 @@
#include "Line.hpp"
#include "MultiPoint.hpp"
#include <string>
#include <vector>
namespace Slic3r {
class ExPolygon;
class Polyline;
class ThickPolyline;
typedef std::vector<Polyline> Polylines;
typedef std::vector<ThickPolyline> ThickPolylines;
class Polyline : public MultiPoint {
public:
@ -18,7 +20,7 @@ class Polyline : public MultiPoint {
operator Line() const;
Point last_point() const;
Point leftmost_point() const;
Lines lines() const;
virtual Lines lines() const;
void clip_end(double distance);
void clip_start(double distance);
void extend_end(double distance);
@ -31,6 +33,13 @@ class Polyline : public MultiPoint {
std::string wkt() const;
};
class ThickPolyline : public Polyline {
public:
std::vector<coordf_t> width;
std::vector<bool> endpoints;
ThickLines thicklines() const;
};
}
#endif

View file

@ -6,7 +6,7 @@
namespace Slic3r {
SVG::SVG(const char* filename)
: arrows(true), fill("grey"), stroke("black"), filename(filename)
: arrows(false), fill("grey"), stroke("black"), filename(filename)
{
this->f = fopen(filename, "w");
fprintf(this->f,
@ -90,7 +90,14 @@ void
SVG::draw(const Polylines &polylines, std::string stroke)
{
for (Polylines::const_iterator it = polylines.begin(); it != polylines.end(); ++it)
this->draw(*it, fill);
this->draw(*it, stroke);
}
void
SVG::draw(const ThickPolylines &polylines, std::string stroke)
{
for (ThickPolylines::const_iterator it = polylines.begin(); it != polylines.end(); ++it)
this->draw((Polyline)*it, stroke);
}
void

View file

@ -24,6 +24,7 @@ class SVG
void draw(const Polygons &polygons, std::string fill = "grey");
void draw(const Polyline &polyline, std::string stroke = "black");
void draw(const Polylines &polylines, std::string stroke = "black");
void draw(const ThickPolylines &polylines, std::string stroke = "black");
void draw(const Point &point, std::string fill = "black", unsigned int radius = 3);
void draw(const Points &points, std::string fill = "black", unsigned int radius = 3);
void Close();