New semantics for ExtrusionLoop objects. Early processing of perimeter overhangs for paralellizing such work and making G-code export lighter. Lots of refactoring. This should fix a number of minor bugs, including reversals of perimeter overhangs.

This commit is contained in:
Alessandro Ranellucci 2014-05-08 11:07:37 +02:00
parent d2d885fc53
commit c37ef2f18b
27 changed files with 618 additions and 423 deletions

View file

@ -106,28 +106,12 @@ use overload
'@{}' => sub { $_[0]->arrayref },
'fallback' => 1;
sub new {
my ($class, %args) = @_;
sub new_from_paths {
my ($class, @paths) = @_;
return $class->_new(
$args{polygon}, # required
$args{role}, # required
$args{mm3_per_mm} // die("Missing required mm3_per_mm in ExtrusionLoop constructor"),
$args{width} // -1,
$args{height} // -1,
);
}
sub clone {
my ($self, %args) = @_;
return __PACKAGE__->_new(
$args{polygon} // $self->polygon,
$args{role} // $self->role,
$args{mm3_per_mm} // $self->mm3_per_mm,
$args{width} // $self->width,
$args{height} // $self->height,
);
my $loop = $class->new;
$loop->append($_) for @paths;
return $loop;
}
package Slic3r::ExtrusionLoop::Ref;

View file

@ -300,23 +300,24 @@ void _clipper_do(const ClipperLib::ClipType clipType, const Slic3r::Polygons &su
}
void _clipper_do(const ClipperLib::ClipType clipType, const Slic3r::Polylines &subject,
const Slic3r::Polygons &clip, ClipperLib::PolyTree &retval, const ClipperLib::PolyFillType fillType)
const Slic3r::Polygons &clip, ClipperLib::PolyTree &retval, const ClipperLib::PolyFillType fillType,
const bool safety_offset_)
{
// read input
ClipperLib::Paths* input_subject = new ClipperLib::Paths();
ClipperLib::Paths* input_clip = new ClipperLib::Paths();
Slic3rMultiPoints_to_ClipperPaths(subject, *input_subject);
Slic3rMultiPoints_to_ClipperPaths(clip, *input_clip);
ClipperLib::Paths input_subject, input_clip;
Slic3rMultiPoints_to_ClipperPaths(subject, input_subject);
Slic3rMultiPoints_to_ClipperPaths(clip, input_clip);
// perform safety offset
if (safety_offset_) safety_offset(&input_clip);
// init Clipper
ClipperLib::Clipper clipper;
clipper.Clear();
// add polygons
clipper.AddPaths(*input_subject, ClipperLib::ptSubject, false);
delete input_subject;
clipper.AddPaths(*input_clip, ClipperLib::ptClip, true);
delete input_clip;
clipper.AddPaths(input_subject, ClipperLib::ptSubject, false);
clipper.AddPaths(input_clip, ClipperLib::ptClip, true);
// perform operation
clipper.Execute(clipType, retval, fillType, fillType);
@ -338,52 +339,114 @@ void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject,
const Slic3r::Polygons &clip, Slic3r::ExPolygons &retval, bool safety_offset_)
{
// perform operation
ClipperLib::PolyTree* polytree = new ClipperLib::PolyTree();
_clipper_do<ClipperLib::PolyTree>(clipType, subject, clip, *polytree, ClipperLib::pftNonZero, safety_offset_);
ClipperLib::PolyTree polytree;
_clipper_do<ClipperLib::PolyTree>(clipType, subject, clip, polytree, ClipperLib::pftNonZero, safety_offset_);
// convert into ExPolygons
PolyTreeToExPolygons(*polytree, retval);
delete polytree;
PolyTreeToExPolygons(polytree, retval);
}
void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject,
const Slic3r::Polygons &clip, Slic3r::Polylines &retval)
const Slic3r::Polygons &clip, Slic3r::Polylines &retval, bool safety_offset_)
{
// perform operation
ClipperLib::PolyTree polytree;
_clipper_do(clipType, subject, clip, polytree, ClipperLib::pftNonZero);
_clipper_do(clipType, subject, clip, polytree, ClipperLib::pftNonZero, safety_offset_);
// convert into Polygons
// convert into Polylines
ClipperLib::Paths output;
ClipperLib::PolyTreeToPaths(polytree, output);
ClipperPaths_to_Slic3rMultiPoints(output, retval);
}
template <class T>
void diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, T &retval, bool safety_offset_)
void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject,
const Slic3r::Polygons &clip, Slic3r::Polylines &retval, bool safety_offset_)
{
// transform input polygons into polylines
Slic3r::Polylines polylines;
polylines.reserve(subject.size());
for (Slic3r::Polygons::const_iterator polygon = subject.begin(); polygon != subject.end(); ++polygon)
polylines.push_back(*polygon); // implicit call to split_at_first_point()
/* Clipper will remove a polyline segment if first point coincides with last one.
Until that bug is not fixed upstream, we move one of those points slightly. */
for (Slic3r::Polylines::iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline)
polyline->points.front().translate(1, 0);
// perform clipping
_clipper(clipType, polylines, clip, retval, safety_offset_);
// compensate for the above hack
for (Slic3r::Polylines::iterator polyline = retval.begin(); polyline != retval.end(); ++polyline) {
for (Slic3r::Polylines::iterator subj_polyline = polylines.begin(); subj_polyline != polylines.end(); ++subj_polyline) {
// if first point of clipped line coincides with first point of subject line, compensate for hack
if (polyline->points.front().coincides_with(subj_polyline->points.front())) {
polyline->points.front().translate(-1, 0);
break;
}
// since Clipper does not preserve orientation of polylines, check last point too
if (polyline->points.back().coincides_with(subj_polyline->points.front())) {
polyline->points.back().translate(-1, 0);
break;
}
}
}
/* If the split_at_first_point() call above happens to split the polygon inside the clipping area
we would get two consecutive polylines instead of a single one, so we go through them in order
to recombine continuous polylines. */
for (size_t i = 0; i < retval.size(); ++i) {
for (size_t j = i+1; j < retval.size(); ++j) {
if (retval[i].points.back().coincides_with(retval[j].points.front())) {
/* If last point of i coincides with first point of j,
append points of j to i and delete j */
retval[i].points.insert(retval[i].points.end(), retval[j].points.begin()+1, retval[j].points.end());
retval.erase(retval.begin() + j);
--j;
} else if (retval[i].points.front().coincides_with(retval[j].points.back())) {
/* If first point of i coincides with last point of j,
prepend points of j to i and delete j */
retval[i].points.insert(retval[i].points.begin(), retval[j].points.begin(), retval[j].points.end()-1);
retval.erase(retval.begin() + j);
--j;
} else if (retval[i].points.front().coincides_with(retval[j].points.front())) {
/* Since Clipper does not preserve orientation of polylines,
also check the case when first point of i coincides with first point of j. */
retval[j].reverse();
retval[i].points.insert(retval[i].points.begin(), retval[j].points.begin(), retval[j].points.end()-1);
retval.erase(retval.begin() + j);
--j;
} else if (retval[i].points.back().coincides_with(retval[j].points.back())) {
/* Since Clipper does not preserve orientation of polylines,
also check the case when last point of i coincides with last point of j. */
retval[j].reverse();
retval[i].points.insert(retval[i].points.end(), retval[j].points.begin()+1, retval[j].points.end());
retval.erase(retval.begin() + j);
--j;
}
}
}
}
template <class SubjectType, class ResultType>
void diff(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType &retval, bool safety_offset_)
{
_clipper(ClipperLib::ctDifference, subject, clip, retval, safety_offset_);
}
template void diff<Slic3r::ExPolygons>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons &retval, bool safety_offset_);
template void diff<Slic3r::Polygons>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polygons &retval, bool safety_offset_);
template void diff<Slic3r::Polygons, Slic3r::ExPolygons>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons &retval, bool safety_offset_);
template void diff<Slic3r::Polygons, Slic3r::Polygons>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polygons &retval, bool safety_offset_);
template void diff<Slic3r::Polygons, Slic3r::Polylines>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polylines &retval, bool safety_offset_);
template void diff<Slic3r::Polylines, Slic3r::Polylines>(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, Slic3r::Polylines &retval, bool safety_offset_);
void diff(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, Slic3r::Polylines &retval)
{
_clipper(ClipperLib::ctDifference, subject, clip, retval);
}
template <class T>
void intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, T &retval, bool safety_offset_)
template <class SubjectType, class ResultType>
void intersection(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType &retval, bool safety_offset_)
{
_clipper(ClipperLib::ctIntersection, subject, clip, retval, safety_offset_);
}
template void intersection<Slic3r::ExPolygons>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons &retval, bool safety_offset_);
template void intersection<Slic3r::Polygons>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polygons &retval, bool safety_offset_);
void intersection(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, Slic3r::Polylines &retval)
{
_clipper(ClipperLib::ctIntersection, subject, clip, retval);
}
template void intersection<Slic3r::Polygons, Slic3r::ExPolygons>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons &retval, bool safety_offset_);
template void intersection<Slic3r::Polygons, Slic3r::Polygons>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polygons &retval, bool safety_offset_);
template void intersection<Slic3r::Polygons, Slic3r::Polylines>(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polylines &retval, bool safety_offset_);
template void intersection<Slic3r::Polylines, Slic3r::Polylines>(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, Slic3r::Polylines &retval, bool safety_offset_);
void xor_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons &retval,
bool safety_offset_)
@ -492,24 +555,19 @@ void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::ExPolygons &retv
PolyTreeToExPolygons(polytree, retval);
}
void safety_offset(ClipperLib::Paths* &subject)
void safety_offset(ClipperLib::Paths* paths)
{
// scale input
scaleClipperPolygons(*subject, CLIPPER_OFFSET_SCALE);
scaleClipperPolygons(*paths, CLIPPER_OFFSET_SCALE);
// perform offset (delta = scale 1e-05)
ClipperLib::Paths* retval = new ClipperLib::Paths();
ClipperLib::ClipperOffset co;
co.MiterLimit = 2;
co.AddPaths(*subject, ClipperLib::jtMiter, ClipperLib::etClosedPolygon);
co.Execute(*retval, 10.0 * CLIPPER_OFFSET_SCALE);
co.AddPaths(*paths, ClipperLib::jtMiter, ClipperLib::etClosedPolygon);
co.Execute(*paths, 10.0 * CLIPPER_OFFSET_SCALE);
// unscale output
scaleClipperPolygons(*retval, 1.0/CLIPPER_OFFSET_SCALE);
// delete original data and switch pointer
delete subject;
subject = retval;
scaleClipperPolygons(*paths, 1.0/CLIPPER_OFFSET_SCALE);
}
///////////////////////

View file

@ -70,7 +70,7 @@ template <class T>
void _clipper_do(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject,
const Slic3r::Polygons &clip, T &retval, bool safety_offset_);
void _clipper_do(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject,
const Slic3r::Polygons &clip, ClipperLib::Paths &retval);
const Slic3r::Polygons &clip, ClipperLib::Paths &retval, bool safety_offset_);
void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject,
const Slic3r::Polygons &clip, Slic3r::Polygons &retval, bool safety_offset_);
void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject,
@ -78,15 +78,11 @@ void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject,
void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject,
const Slic3r::Polygons &clip, Slic3r::Polylines &retval);
template <class T>
void diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, T &retval, bool safety_offset_ = false);
template <class SubjectType, class ResultType>
void diff(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType &retval, bool safety_offset_ = false);
void diff(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, Slic3r::Polylines &retval);
template <class T>
void intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, T &retval, bool safety_offset_ = false);
void intersection(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, Slic3r::Polylines &retval);
template <class SubjectType, class ResultType>
void intersection(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType &retval, bool safety_offset_ = false);
void xor_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons &retval,
bool safety_offset_ = false);
@ -101,7 +97,7 @@ static void traverse_pt(ClipperLib::PolyNodes &nodes, Slic3r::Polygons &retval);
void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::Polygons &retval, bool preserve_collinear = false);
void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::ExPolygons &retval, bool preserve_collinear = false);
void safety_offset(ClipperLib::Paths* &subject);
void safety_offset(ClipperLib::Paths* paths);
/////////////////

View file

@ -223,7 +223,7 @@ ExPolygon::get_trapezoids2(Polygons* polygons) const
// intersect with this expolygon
Polygons trapezoids;
intersection(poly, *this, trapezoids);
intersection<Polygons,Polygons>(poly, *this, trapezoids);
// append results to return value
polygons->insert(polygons->end(), trapezoids.begin(), trapezoids.end());

View file

@ -10,31 +10,6 @@
namespace Slic3r {
bool
ExtrusionEntity::is_perimeter() const
{
return this->role == erPerimeter
|| this->role == erExternalPerimeter
|| this->role == erOverhangPerimeter
|| this->role == erContourInternalPerimeter;
}
bool
ExtrusionEntity::is_fill() const
{
return this->role == erFill
|| this->role == erSolidFill
|| this->role == erTopSolidFill;
}
bool
ExtrusionEntity::is_bridge() const
{
return this->role == erBrige
|| this->role == erInternalBridge
|| this->role == erOverhangPerimeter;
}
ExtrusionPath*
ExtrusionPath::clone() const
{
@ -64,7 +39,7 @@ ExtrusionPath::intersect_expolygons(const ExPolygonCollection &collection, Extru
{
// perform clipping
Polylines clipped;
intersection(this->polyline, collection, clipped);
intersection<Polylines,Polylines>(this->polyline, collection, clipped);
return this->_inflate_collection(clipped, retval);
}
@ -73,7 +48,7 @@ ExtrusionPath::subtract_expolygons(const ExPolygonCollection &collection, Extrus
{
// perform clipping
Polylines clipped;
diff(this->polyline, collection, clipped);
diff<Polylines,Polylines>(this->polyline, collection, clipped);
return this->_inflate_collection(clipped, retval);
}
@ -95,6 +70,31 @@ ExtrusionPath::length() const
return this->polyline.length();
}
bool
ExtrusionPath::is_perimeter() const
{
return this->role == erPerimeter
|| this->role == erExternalPerimeter
|| this->role == erOverhangPerimeter
|| this->role == erContourInternalPerimeter;
}
bool
ExtrusionPath::is_fill() const
{
return this->role == erFill
|| this->role == erSolidFill
|| this->role == erTopSolidFill;
}
bool
ExtrusionPath::is_bridge() const
{
return this->role == erBridge
|| this->role == erInternalBridge
|| this->role == erOverhangPerimeter;
}
void
ExtrusionPath::_inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const
{
@ -106,8 +106,22 @@ ExtrusionPath::_inflate_collection(const Polylines &polylines, ExtrusionEntityCo
}
#ifdef SLIC3RXS
SV*
ExtrusionPath::to_SV_ref() {
SV* sv = newSV(0);
sv_setref_pv( sv, perl_class_name_ref(this), this );
return sv;
}
SV*
ExtrusionPath::to_SV_clone_ref() const {
SV* sv = newSV(0);
sv_setref_pv( sv, perl_class_name(this), new ExtrusionPath(*this) );
return sv;
}
REGISTER_CLASS(ExtrusionPath, "ExtrusionPath");
#endif
std::string
ExtrusionPath::gcode(Extruder* extruder, double e, double F,
@ -155,12 +169,12 @@ ExtrusionPath::gcode(Extruder* extruder, double e, double F,
return stream.str();
}
#endif
ExtrusionLoop::ExtrusionLoop(const Polygon &polygon, ExtrusionRole role)
ExtrusionLoop::operator Polygon() const
{
this->role = role;
this->set_polygon(polygon);
Polygon polygon;
this->polygon(&polygon);
return polygon;
}
ExtrusionLoop*
@ -169,32 +183,19 @@ ExtrusionLoop::clone() const
return new ExtrusionLoop (*this);
}
void
ExtrusionLoop::split_at_index(int index, ExtrusionPath* path) const
bool
ExtrusionLoop::make_clockwise()
{
Polygon polygon;
this->polygon(&polygon);
polygon.split_at_index(index, &path->polyline);
path->role = this->role;
path->mm3_per_mm = this->mm3_per_mm;
path->width = this->width;
path->height = this->height;
}
void
ExtrusionLoop::split_at_first_point(ExtrusionPath* path) const
{
return this->split_at_index(0, path);
Polygon polygon = *this;
bool was_ccw = polygon.is_counter_clockwise();
if (was_ccw) this->reverse();
return was_ccw;
}
bool
ExtrusionLoop::make_counter_clockwise()
{
Polygon polygon;
this->polygon(&polygon);
Polygon polygon = *this;
bool was_cw = polygon.is_clockwise();
if (was_cw) this->reverse();
return was_cw;
@ -203,41 +204,116 @@ ExtrusionLoop::make_counter_clockwise()
void
ExtrusionLoop::reverse()
{
for (Polylines::iterator polyline = this->polylines.begin(); polyline != this->polylines.end(); ++polyline)
polyline->reverse();
std::reverse(this->polylines.begin(), this->polylines.end());
for (ExtrusionPaths::iterator path = this->paths.begin(); path != this->paths.end(); ++path)
path->reverse();
std::reverse(this->paths.begin(), this->paths.end());
}
Point
ExtrusionLoop::first_point() const
{
return this->polylines.front().points.front();
return this->paths.front().polyline.points.front();
}
Point
ExtrusionLoop::last_point() const
{
return this->polylines.back().points.back(); // which coincides with first_point(), by the way
}
void
ExtrusionLoop::set_polygon(const Polygon &polygon)
{
Polyline polyline;
polygon.split_at_first_point(&polyline);
this->polylines.clear();
this->polylines.push_back(polyline);
return this->paths.back().polyline.points.back(); // which coincides with first_point(), by the way
}
void
ExtrusionLoop::polygon(Polygon* polygon) const
{
for (Polylines::const_iterator polyline = this->polylines.begin(); polyline != this->polylines.end(); ++polyline) {
for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) {
// for each polyline, append all points except the last one (because it coincides with the first one of the next polyline)
polygon->points.insert(polygon->points.end(), polyline->points.begin(), polyline->points.end()-1);
polygon->points.insert(polygon->points.end(), path->polyline.points.begin(), path->polyline.points.end()-1);
}
}
double
ExtrusionLoop::length() const
{
double len = 0;
for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path)
len += path->polyline.length();
return len;
}
void
ExtrusionLoop::split_at(const Point &point)
{
for (ExtrusionPaths::iterator path = this->paths.begin(); path != this->paths.end(); ++path) {
int idx = path->polyline.find_point(point);
if (idx != -1) {
if (this->paths.size() == 1) {
// just change the order of points
path->polyline.points.insert(path->polyline.points.end(), path->polyline.points.begin() + 1, path->polyline.points.begin() + idx + 1);
path->polyline.points.erase(path->polyline.points.begin(), path->polyline.points.begin() + idx);
} else {
// if we have multiple paths we assume they have different types, so no need to
// check for continuity as we do for the single path case above
// new paths list starts with the second half of current path
ExtrusionPaths new_paths;
{
ExtrusionPath p = *path;
p.polyline.points.erase(p.polyline.points.begin(), p.polyline.points.begin() + idx);
if (!p.polyline.points.empty()) new_paths.push_back(p);
}
// then we add all paths until the end of current path list
new_paths.insert(new_paths.end(), this->paths.begin(), path); // not including this path
// then we add all paths since the beginning of current list up to the previous one
new_paths.insert(new_paths.end(), path+1, this->paths.end()); // not including this path
// finally we add the first half of current path
{
ExtrusionPath p = *path;
p.polyline.points.erase(p.polyline.points.begin() + idx + 1, p.polyline.points.end());
if (!p.polyline.points.empty()) new_paths.push_back(p);
}
// we can now override the old path list with the new one and stop looping
this->paths = new_paths;
}
return;
}
}
CONFESS("Point not found");
}
void
ExtrusionLoop::clip_end(double distance, ExtrusionPaths* paths) const
{
*paths = this->paths;
while (distance > 0 && !paths->empty()) {
ExtrusionPath &last = paths->back();
double len = last.length();
if (len <= distance) {
paths->pop_back();
distance -= len;
} else {
last.polyline.clip_end(distance);
break;
}
}
}
bool
ExtrusionLoop::has_overhang_point(const Point &point) const
{
for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) {
int pos = path->polyline.find_point(point);
if (pos != -1) {
// point belongs to this path
// we consider it overhang only if it's not an endpoint
return (path->is_bridge() && pos > 0 && pos != path->polyline.points.size()-1);
}
}
return false;
}
#ifdef SLIC3RXS
REGISTER_CLASS(ExtrusionLoop, "ExtrusionLoop");
#endif

View file

@ -19,7 +19,7 @@ enum ExtrusionRole {
erFill,
erSolidFill,
erTopSolidFill,
erBrige,
erBridge,
erInternalBridge,
erSkirt,
erSupportMaterial,
@ -29,19 +29,11 @@ enum ExtrusionRole {
class ExtrusionEntity
{
public:
ExtrusionEntity() : mm3_per_mm(-1), width(-1), height(-1) {};
virtual ExtrusionEntity* clone() const = 0;
virtual ~ExtrusionEntity() {};
ExtrusionRole role;
double mm3_per_mm; // mm^3 of plastic per mm of linear head motion
float width;
float height;
virtual void reverse() = 0;
virtual Point first_point() const = 0;
virtual Point last_point() const = 0;
bool is_perimeter() const;
bool is_fill() const;
bool is_bridge() const;
};
typedef std::vector<ExtrusionEntity*> ExtrusionEntitiesPtr;
@ -49,8 +41,14 @@ typedef std::vector<ExtrusionEntity*> ExtrusionEntitiesPtr;
class ExtrusionPath : public ExtrusionEntity
{
public:
ExtrusionPath* clone() const;
Polyline polyline;
ExtrusionRole role;
double mm3_per_mm; // mm^3 of plastic per mm of linear head motion
float width;
float height;
ExtrusionPath() : mm3_per_mm(-1), width(-1), height(-1) {};
ExtrusionPath* clone() const;
void reverse();
Point first_point() const;
Point last_point() const;
@ -59,32 +57,41 @@ class ExtrusionPath : public ExtrusionEntity
void clip_end(double distance);
void simplify(double tolerance);
double length() const;
#ifdef SLIC3RXS
bool is_perimeter() const;
bool is_fill() const;
bool is_bridge() const;
std::string gcode(Extruder* extruder, double e, double F,
double xofs, double yofs, std::string extrusion_axis,
std::string gcode_line_suffix) const;
#ifdef SLIC3RXS
SV* to_SV_ref();
SV* to_SV_clone_ref() const;
#endif
private:
void _inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const;
};
typedef std::vector<ExtrusionPath> ExtrusionPaths;
class ExtrusionLoop : public ExtrusionEntity
{
public:
Polylines polylines;
ExtrusionPaths paths;
ExtrusionLoop(const Polygon &polygon, ExtrusionRole role);
operator Polygon() const;
ExtrusionLoop* clone() const;
void split_at_index(int index, ExtrusionPath* path) const;
void split_at_first_point(ExtrusionPath* path) const;
bool make_clockwise();
bool make_counter_clockwise();
void reverse();
Point first_point() const;
Point last_point() const;
void set_polygon(const Polygon &polygon);
void polygon(Polygon* polygon) const;
double length() const;
void split_at(const Point &point);
void clip_end(double distance, ExtrusionPaths* paths) const;
bool has_overhang_point(const Point &point) const;
};
}

View file

@ -55,6 +55,15 @@ MultiPoint::is_valid() const
return this->points.size() >= 2;
}
int
MultiPoint::find_point(const Point &point) const
{
for (Points::const_iterator it = this->points.begin(); it != this->points.end(); ++it) {
if (it->coincides_with(point)) return it - this->points.begin();
}
return -1; // not found
}
Points
MultiPoint::_douglas_peucker(const Points &points, const double tolerance)
{

View file

@ -21,6 +21,7 @@ class MultiPoint
virtual Lines lines() const = 0;
double length() const;
bool is_valid() const;
int find_point(const Point &point) const;
static Points _douglas_peucker(const Points &points, const double tolerance);
#ifdef SLIC3RXS

View file

@ -15,6 +15,13 @@ Polygon::operator Polygons() const
return pp;
}
Polygon::operator Polyline() const
{
Polyline polyline;
this->split_at_first_point(&polyline);
return polyline;
}
Point&
Polygon::operator[](Points::size_type idx)
{

View file

@ -15,6 +15,7 @@ typedef std::vector<Polygon> Polygons;
class Polygon : public MultiPoint {
public:
operator Polygons() const;
operator Polyline() const;
Point& operator[](Points::size_type idx);
const Point& operator[](Points::size_type idx) const;
Point last_point() const;

View file

@ -3,37 +3,86 @@
use strict;
use warnings;
use List::Util qw(sum);
use Slic3r::XS;
use Test::More tests => 8;
my $square = [
[100, 100],
[200, 100],
[200, 200],
[100, 200],
];
my $loop = Slic3r::ExtrusionLoop->new(
polygon => Slic3r::Polygon->new(@$square),
role => Slic3r::ExtrusionPath::EXTR_ROLE_EXTERNAL_PERIMETER,
mm3_per_mm => 1,
);
isa_ok $loop, 'Slic3r::ExtrusionLoop';
isa_ok $loop->polygon, 'Slic3r::Polygon', 'loop polygon';
is_deeply $loop->polygon->pp, $square, 'polygon points roundtrip';
$loop = $loop->clone;
is $loop->role, Slic3r::ExtrusionPath::EXTR_ROLE_EXTERNAL_PERIMETER, 'role';
$loop->role(Slic3r::ExtrusionPath::EXTR_ROLE_FILL);
is $loop->role, Slic3r::ExtrusionPath::EXTR_ROLE_FILL, 'modify role';
use Test::More tests => 30;
{
my $path = $loop->split_at_first_point;
is_deeply $path->polyline->pp, [ @$square[0,1,2,3,0] ], 'split_at_first_point';
is $path->role, $loop->role, 'role preserved after split';
my $square = [
[100, 100],
[200, 100],
[200, 200],
[100, 200],
];
my $square_p = Slic3r::Polygon->new(@$square);
my $loop = Slic3r::ExtrusionLoop->new;
$loop->append(Slic3r::ExtrusionPath->new(
polyline => $square_p->split_at_first_point,
role => Slic3r::ExtrusionPath::EXTR_ROLE_EXTERNAL_PERIMETER,
mm3_per_mm => 1,
));
isa_ok $loop, 'Slic3r::ExtrusionLoop';
isa_ok $loop->polygon, 'Slic3r::Polygon', 'loop polygon';
is $loop->polygon->area, $square_p->area, 'polygon area';
is $loop->length, $square_p->length(), 'loop length';
$loop = $loop->clone;
is scalar(@$loop), 1, 'loop contains one path';
{
my $path = $loop->[0];
isa_ok $path, 'Slic3r::ExtrusionPath::Ref';
is $path->role, Slic3r::ExtrusionPath::EXTR_ROLE_EXTERNAL_PERIMETER, 'role';
$path->role(Slic3r::ExtrusionPath::EXTR_ROLE_FILL);
is $path->role, Slic3r::ExtrusionPath::EXTR_ROLE_FILL, 'modify role';
}
$loop->split_at($square_p->[2]);
is scalar(@$loop), 1, 'splitting a single-path loop results in a single path';
is scalar(@{$loop->[0]->polyline}), 5, 'path has correct number of points';
ok $loop->[0]->polyline->[0]->coincides_with($square_p->[2]), 'expected point order';
ok $loop->[0]->polyline->[1]->coincides_with($square_p->[3]), 'expected point order';
ok $loop->[0]->polyline->[2]->coincides_with($square_p->[0]), 'expected point order';
ok $loop->[0]->polyline->[3]->coincides_with($square_p->[1]), 'expected point order';
ok $loop->[0]->polyline->[4]->coincides_with($square_p->[2]), 'expected point order';
}
{
my $polyline1 = Slic3r::Polyline->new([100,100], [200,100], [200,200]);
my $polyline2 = Slic3r::Polyline->new([200,200], [100,200], [100,100]);
is_deeply $loop->split_at_index(2)->polyline->pp, [ @$square[2,3,0,1,2] ], 'split_at_index';
my $loop = Slic3r::ExtrusionLoop->new_from_paths(
Slic3r::ExtrusionPath->new(
polyline => $polyline1,
role => Slic3r::ExtrusionPath::EXTR_ROLE_EXTERNAL_PERIMETER,
mm3_per_mm => 1,
),
Slic3r::ExtrusionPath->new(
polyline => $polyline2,
role => Slic3r::ExtrusionPath::EXTR_ROLE_OVERHANG_PERIMETER,
mm3_per_mm => 1,
),
);
is $loop->length, sum($polyline1->length, $polyline2->length), 'length';
is scalar(@$loop), 2, 'loop contains two paths';
$loop->split_at($polyline1->[1]);
is $loop->length, sum($polyline1->length, $polyline2->length), 'length after splitting';
is scalar(@$loop), 3, 'loop contains three paths after splitting';
ok $loop->[0]->polyline->[0]->coincides_with($polyline1->[1]), 'expected starting point';
ok $loop->[-1]->polyline->[-1]->coincides_with($polyline1->[1]), 'expected ending point';
ok $loop->[0]->polyline->[-1]->coincides_with($loop->[1]->polyline->[0]), 'paths have common point';
ok $loop->[1]->polyline->[-1]->coincides_with($loop->[2]->polyline->[0]), 'paths have common point';
is $loop->[0]->role, Slic3r::ExtrusionPath::EXTR_ROLE_EXTERNAL_PERIMETER, 'expected order after splitting';
is $loop->[1]->role, Slic3r::ExtrusionPath::EXTR_ROLE_OVERHANG_PERIMETER, 'expected order after splitting';
is $loop->[2]->role, Slic3r::ExtrusionPath::EXTR_ROLE_EXTERNAL_PERIMETER, 'expected order after splitting';
is scalar(@{$loop->[0]->polyline}), 2, 'path has correct number of points';
is scalar(@{$loop->[1]->polyline}), 3, 'path has correct number of points';
is scalar(@{$loop->[2]->polyline}), 2, 'path has correct number of points';
my @paths = @{$loop->clip_end(3)};
is sum(map $_->length, @paths), $loop->length - 3, 'returned paths have expected length';
}
__END__

View file

@ -4,20 +4,20 @@ use strict;
use warnings;
use Slic3r::XS;
use Test::More tests => 11;
use Test::More tests => 17;
my $square = [ # ccw
my $square = Slic3r::Polygon->new( # ccw
[200, 100],
[200, 200],
[100, 200],
[100, 100],
];
my $hole_in_square = [ # cw
);
my $hole_in_square = Slic3r::Polygon->new( # cw
[160, 140],
[140, 140],
[140, 160],
[160, 160],
];
);
my $expolygon = Slic3r::ExPolygon->new($square, $hole_in_square);
{
@ -106,4 +106,53 @@ my $expolygon = Slic3r::ExPolygon->new($square, $hole_in_square);
}
}
if (0) { # Clipper does not preserve polyline orientation
my $polyline = Slic3r::Polyline->new([50,150], [300,150]);
my $result = Slic3r::Geometry::Clipper::intersection_pl([$polyline], [$square]);
is scalar(@$result), 1, 'intersection_pl - correct number of result lines';
is_deeply $result->[0]->pp, [[100,150], [200,150]], 'clipped line orientation is preserved';
}
if (0) { # Clipper does not preserve polyline orientation
my $polyline = Slic3r::Polyline->new([300,150], [50,150]);
my $result = Slic3r::Geometry::Clipper::intersection_pl([$polyline], [$square]);
is scalar(@$result), 1, 'intersection_pl - correct number of result lines';
is_deeply $result->[0]->pp, [[200,150], [100,150]], 'clipped line orientation is preserved';
}
if (0) { # Clipper does not preserve polyline orientation
my $result = Slic3r::Geometry::Clipper::intersection_ppl([$hole_in_square], [$square]);
is_deeply $result->[0]->pp, $hole_in_square->split_at_first_point->pp,
'intersection_ppl - clipping cw polygon as polyline preserves winding order';
}
{
my $square2 = $square->clone;
$square2->translate(50,50);
{
my $result = Slic3r::Geometry::Clipper::intersection_ppl([$square2], [$square]);
is scalar(@$result), 1, 'intersection_ppl - result contains a single line';
is scalar(@{$result->[0]}), 3, 'intersection_ppl - result contains expected number of points';
# Clipper does not preserve polyline orientation so we only check the middle point
###ok $result->[0][0]->coincides_with(Slic3r::Point->new(150,200)), 'intersection_ppl - expected point order';
ok $result->[0][1]->coincides_with(Slic3r::Point->new(150,150)), 'intersection_ppl - expected point order';
###ok $result->[0][2]->coincides_with(Slic3r::Point->new(200,150)), 'intersection_ppl - expected point order';
}
}
{
my $square2 = $square->clone;
$square2->reverse;
$square2->translate(50,50);
{
my $result = Slic3r::Geometry::Clipper::intersection_ppl([$square2], [$square]);
is scalar(@$result), 1, 'intersection_ppl - result contains a single line';
is scalar(@{$result->[0]}), 3, 'intersection_ppl - result contains expected number of points';
# Clipper does not preserve polyline orientation so we only check the middle point
###ok $result->[0][0]->coincides_with(Slic3r::Point->new(200,150)), 'intersection_ppl - expected point order';
ok $result->[0][1]->coincides_with(Slic3r::Point->new(150,150)), 'intersection_ppl - expected point order';
###ok $result->[0][2]->coincides_with(Slic3r::Point->new(150,200)), 'intersection_ppl - expected point order';
}
}
__END__

View file

@ -18,10 +18,12 @@ my $path = Slic3r::ExtrusionPath->new(
mm3_per_mm => 1,
);
my $loop = Slic3r::ExtrusionLoop->new(
polygon => Slic3r::Polygon->new(@$points),
role => Slic3r::ExtrusionPath::EXTR_ROLE_FILL,
mm3_per_mm => 1,
my $loop = Slic3r::ExtrusionLoop->new_from_paths(
Slic3r::ExtrusionPath->new(
polyline => Slic3r::Polygon->new(@$points)->split_at_first_point,
role => Slic3r::ExtrusionPath::EXTR_ROLE_FILL,
mm3_per_mm => 1,
),
);
my $collection = Slic3r::ExtrusionPath::Collection->new($path);

View file

@ -101,6 +101,15 @@ diff_pl(subject, clip)
OUTPUT:
RETVAL
Polylines
diff_ppl(subject, clip)
Polygons subject
Polygons clip
CODE:
diff(subject, clip, RETVAL);
OUTPUT:
RETVAL
Polygons
intersection(subject, clip, safety_offset = false)
Polygons subject
@ -130,6 +139,15 @@ intersection_pl(subject, clip)
OUTPUT:
RETVAL
Polylines
intersection_ppl(subject, clip)
Polygons subject
Polygons clip
CODE:
intersection(subject, clip, RETVAL);
OUTPUT:
RETVAL
ExPolygons
xor_ex(subject, clip, safety_offset = false)
Polygons subject

View file

@ -21,6 +21,8 @@
bool contains_point(Point* point)
%code{% RETVAL = THIS->contains_point(*point); %};
void simplify(double tolerance);
Polygons polygons()
%code{% RETVAL = *THIS; %};
%{
ExPolygonCollection*

View file

@ -7,92 +7,38 @@
%}
%name{Slic3r::ExtrusionLoop} class ExtrusionLoop {
ExtrusionLoop();
~ExtrusionLoop();
SV* arrayref()
%code{% Polygon polygon; THIS->polygon(&polygon); RETVAL = polygon.to_AV(); %};
SV* pp()
%code{% Polygon polygon; THIS->polygon(&polygon); RETVAL = polygon.to_SV_pureperl(); %};
Clone<ExtrusionLoop> clone()
%code{% RETVAL = THIS; %};
void reverse();
ExtrusionPath* split_at_index(int index)
%code{% RETVAL = new ExtrusionPath (); THIS->split_at_index(index, RETVAL); %};
ExtrusionPath* split_at_first_point()
%code{% RETVAL = new ExtrusionPath (); THIS->split_at_first_point(RETVAL); %};
bool make_clockwise();
bool make_counter_clockwise();
Clone<Point> first_point();
Clone<Point> last_point();
bool is_perimeter();
bool is_fill();
bool is_bridge();
Polygon* polygon()
%code{% RETVAL = new Polygon (*THIS); %};
void append(ExtrusionPath* path)
%code{% THIS->paths.push_back(*path); %};
double length();
void split_at(Point* point)
%code{% THIS->split_at(*point); %};
ExtrusionPaths clip_end(double distance)
%code{% THIS->clip_end(distance, &RETVAL); %};
bool has_overhang_point(Point* point)
%code{% RETVAL = THIS->has_overhang_point(*point); %};
%{
ExtrusionLoop*
_new(CLASS, polygon_sv, role, mm3_per_mm, width, height)
char* CLASS;
SV* polygon_sv;
ExtrusionRole role;
double mm3_per_mm;
float width;
float height;
SV*
ExtrusionLoop::arrayref()
CODE:
Polygon polygon;
polygon.from_SV_check(polygon_sv);
RETVAL = new ExtrusionLoop (polygon, role);
RETVAL->mm3_per_mm = mm3_per_mm;
RETVAL->width = width;
RETVAL->height = height;
OUTPUT:
RETVAL
Polygon*
ExtrusionLoop::polygon(...)
CODE:
if (items > 1) {
Polygon polygon;
polygon.from_SV_check( ST(1) );
THIS->set_polygon(polygon);
AV* av = newAV();
av_fill(av, THIS->paths.size()-1);
int i = 0;
for (ExtrusionPaths::iterator it = THIS->paths.begin(); it != THIS->paths.end(); ++it) {
av_store(av, i++, it->to_SV_ref());
}
RETVAL = new Polygon ();
THIS->polygon(RETVAL);
OUTPUT:
RETVAL
ExtrusionRole
ExtrusionLoop::role(...)
CODE:
if (items > 1) {
THIS->role = (ExtrusionRole)SvUV(ST(1));
}
RETVAL = THIS->role;
OUTPUT:
RETVAL
double
ExtrusionLoop::mm3_per_mm(...)
CODE:
if (items > 1) {
THIS->mm3_per_mm = (double)SvNV(ST(1));
}
RETVAL = THIS->mm3_per_mm;
OUTPUT:
RETVAL
float
ExtrusionLoop::width(...)
CODE:
if (items > 1) {
THIS->width = (float)SvNV(ST(1));
}
RETVAL = THIS->width;
OUTPUT:
RETVAL
float
ExtrusionLoop::height(...)
CODE:
if (items > 1) {
THIS->height = (float)SvNV(ST(1));
}
RETVAL = THIS->height;
RETVAL = newRV_noinc((SV*)av);
OUTPUT:
RETVAL

View file

@ -140,7 +140,7 @@ _constant()
EXTR_ROLE_FILL = erFill
EXTR_ROLE_SOLIDFILL = erSolidFill
EXTR_ROLE_TOPSOLIDFILL = erTopSolidFill
EXTR_ROLE_BRIDGE = erBrige
EXTR_ROLE_BRIDGE = erBridge
EXTR_ROLE_INTERNALBRIDGE = erInternalBridge
EXTR_ROLE_SKIRT = erSkirt
EXTR_ROLE_SUPPORTMATERIAL = erSupportMaterial

View file

@ -97,6 +97,7 @@ Lines T_ARRAYREF
Polygons T_ARRAYREF
Polylines T_ARRAYREF
ExPolygons T_ARRAYREF
ExtrusionPaths T_ARRAYREF
Surfaces T_ARRAYREF
# we return these types whenever we want the items to be returned

View file

@ -70,6 +70,7 @@
%typemap{Polylines};
%typemap{PrintState};
%typemap{ExPolygons};
%typemap{ExtrusionPaths};
%typemap{Surfaces};
%typemap{Polygons*};
%typemap{TriangleMeshPtrs};