2013-07-16 19:04:14 +00:00
|
|
|
#include "ExtrusionEntity.hpp"
|
2013-11-21 16:53:50 +00:00
|
|
|
#include "ExtrusionEntityCollection.hpp"
|
|
|
|
#include "ExPolygonCollection.hpp"
|
|
|
|
#include "ClipperUtils.hpp"
|
2014-04-07 23:43:02 +00:00
|
|
|
#include "Extruder.hpp"
|
2014-04-27 17:18:53 +00:00
|
|
|
#include <sstream>
|
2013-07-16 19:04:14 +00:00
|
|
|
|
|
|
|
namespace Slic3r {
|
|
|
|
|
2013-08-29 09:47:59 +00:00
|
|
|
ExtrusionPath*
|
|
|
|
ExtrusionPath::clone() const
|
|
|
|
{
|
|
|
|
return new ExtrusionPath (*this);
|
|
|
|
}
|
|
|
|
|
2013-07-16 19:04:14 +00:00
|
|
|
void
|
|
|
|
ExtrusionPath::reverse()
|
|
|
|
{
|
|
|
|
this->polyline.reverse();
|
|
|
|
}
|
|
|
|
|
2014-04-24 14:40:10 +00:00
|
|
|
Point
|
2013-09-02 20:33:03 +00:00
|
|
|
ExtrusionPath::first_point() const
|
2013-08-26 21:42:00 +00:00
|
|
|
{
|
2014-04-24 14:40:10 +00:00
|
|
|
return this->polyline.points.front();
|
2013-08-26 21:42:00 +00:00
|
|
|
}
|
|
|
|
|
2014-04-24 14:40:10 +00:00
|
|
|
Point
|
2013-09-02 20:33:03 +00:00
|
|
|
ExtrusionPath::last_point() const
|
2013-08-28 23:36:42 +00:00
|
|
|
{
|
2014-04-24 14:40:10 +00:00
|
|
|
return this->polyline.points.back();
|
2013-08-28 23:36:42 +00:00
|
|
|
}
|
|
|
|
|
2014-03-09 19:19:30 +00:00
|
|
|
void
|
|
|
|
ExtrusionPath::intersect_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const
|
2013-11-21 16:53:50 +00:00
|
|
|
{
|
|
|
|
// perform clipping
|
|
|
|
Polylines clipped;
|
2014-11-15 22:44:03 +00:00
|
|
|
intersection<Polylines,Polylines>(this->polyline, collection, &clipped);
|
2014-03-09 19:19:30 +00:00
|
|
|
return this->_inflate_collection(clipped, retval);
|
2013-11-21 16:53:50 +00:00
|
|
|
}
|
|
|
|
|
2014-03-09 19:19:30 +00:00
|
|
|
void
|
|
|
|
ExtrusionPath::subtract_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const
|
2013-11-21 16:53:50 +00:00
|
|
|
{
|
|
|
|
// perform clipping
|
|
|
|
Polylines clipped;
|
2014-11-15 22:44:03 +00:00
|
|
|
diff<Polylines,Polylines>(this->polyline, collection, &clipped);
|
2014-03-09 19:19:30 +00:00
|
|
|
return this->_inflate_collection(clipped, retval);
|
2013-11-21 16:53:50 +00:00
|
|
|
}
|
|
|
|
|
2013-11-21 17:03:40 +00:00
|
|
|
void
|
|
|
|
ExtrusionPath::clip_end(double distance)
|
|
|
|
{
|
|
|
|
this->polyline.clip_end(distance);
|
|
|
|
}
|
|
|
|
|
2013-11-21 19:25:24 +00:00
|
|
|
void
|
|
|
|
ExtrusionPath::simplify(double tolerance)
|
|
|
|
{
|
|
|
|
this->polyline.simplify(tolerance);
|
|
|
|
}
|
|
|
|
|
2013-11-21 17:03:40 +00:00
|
|
|
double
|
|
|
|
ExtrusionPath::length() const
|
|
|
|
{
|
|
|
|
return this->polyline.length();
|
|
|
|
}
|
|
|
|
|
2014-05-08 09:07:37 +00:00
|
|
|
bool
|
|
|
|
ExtrusionPath::is_perimeter() const
|
|
|
|
{
|
|
|
|
return this->role == erPerimeter
|
|
|
|
|| this->role == erExternalPerimeter
|
2014-05-12 19:49:17 +00:00
|
|
|
|| this->role == erOverhangPerimeter;
|
2014-05-08 09:07:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2014-12-16 23:34:00 +00:00
|
|
|
ExtrusionPath::is_infill() const
|
2014-05-08 09:07:37 +00:00
|
|
|
{
|
2014-12-16 23:34:00 +00:00
|
|
|
return this->role == erBridgeInfill
|
|
|
|
|| this->role == erInternalInfill
|
|
|
|
|| this->role == erSolidInfill
|
|
|
|
|| this->role == erTopSolidInfill;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
ExtrusionPath::is_solid_infill() const
|
|
|
|
{
|
|
|
|
return this->role == erBridgeInfill
|
2014-05-12 19:49:17 +00:00
|
|
|
|| this->role == erSolidInfill
|
|
|
|
|| this->role == erTopSolidInfill;
|
2014-05-08 09:07:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
ExtrusionPath::is_bridge() const
|
|
|
|
{
|
2014-05-12 19:49:17 +00:00
|
|
|
return this->role == erBridgeInfill
|
2014-05-08 09:07:37 +00:00
|
|
|
|| this->role == erOverhangPerimeter;
|
|
|
|
}
|
|
|
|
|
2014-03-09 19:19:30 +00:00
|
|
|
void
|
|
|
|
ExtrusionPath::_inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const
|
2013-11-21 16:53:50 +00:00
|
|
|
{
|
|
|
|
for (Polylines::const_iterator it = polylines.begin(); it != polylines.end(); ++it) {
|
|
|
|
ExtrusionPath* path = this->clone();
|
|
|
|
path->polyline = *it;
|
2014-03-09 19:19:30 +00:00
|
|
|
collection->entities.push_back(path);
|
2013-11-21 16:53:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-01 13:54:57 +00:00
|
|
|
#ifdef SLIC3RXS
|
2014-04-27 17:18:53 +00:00
|
|
|
REGISTER_CLASS(ExtrusionPath, "ExtrusionPath");
|
2014-05-08 09:07:37 +00:00
|
|
|
#endif
|
2014-04-27 17:18:53 +00:00
|
|
|
|
2014-04-01 13:54:57 +00:00
|
|
|
std::string
|
2014-04-07 23:43:02 +00:00
|
|
|
ExtrusionPath::gcode(Extruder* extruder, double e, double F,
|
2014-04-01 13:54:57 +00:00
|
|
|
double xofs, double yofs, std::string extrusion_axis,
|
|
|
|
std::string gcode_line_suffix) const
|
|
|
|
{
|
|
|
|
dSP;
|
|
|
|
|
|
|
|
std::stringstream stream;
|
|
|
|
stream.setf(std::ios::fixed);
|
|
|
|
|
|
|
|
double local_F = F;
|
|
|
|
|
|
|
|
Lines lines = this->polyline.lines();
|
|
|
|
for (Lines::const_iterator line_it = lines.begin();
|
|
|
|
line_it != lines.end(); ++line_it)
|
|
|
|
{
|
|
|
|
const double line_length = line_it->length() * SCALING_FACTOR;
|
|
|
|
|
|
|
|
// calculate extrusion length for this line
|
2014-10-21 18:16:45 +00:00
|
|
|
double E = 0;
|
|
|
|
if (e > 0) {
|
|
|
|
extruder->extrude(e * line_length);
|
|
|
|
E = extruder->E;
|
|
|
|
}
|
2014-04-01 13:54:57 +00:00
|
|
|
|
|
|
|
// compose G-code line
|
|
|
|
|
|
|
|
Point point = line_it->b;
|
|
|
|
const double x = point.x * SCALING_FACTOR + xofs;
|
|
|
|
const double y = point.y * SCALING_FACTOR + yofs;
|
|
|
|
stream.precision(3);
|
|
|
|
stream << "G1 X" << x << " Y" << y;
|
|
|
|
|
|
|
|
if (E != 0) {
|
|
|
|
stream.precision(5);
|
|
|
|
stream << " " << extrusion_axis << E;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (local_F != 0) {
|
|
|
|
stream.precision(3);
|
|
|
|
stream << " F" << local_F;
|
|
|
|
local_F = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
stream << gcode_line_suffix;
|
|
|
|
stream << "\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
return stream.str();
|
|
|
|
}
|
|
|
|
|
2015-01-15 19:06:30 +00:00
|
|
|
Polygons
|
|
|
|
ExtrusionPath::grow() const
|
|
|
|
{
|
|
|
|
Polygons pp;
|
|
|
|
offset(this->polyline, &pp, +this->width/2);
|
|
|
|
return pp;
|
|
|
|
}
|
|
|
|
|
2014-05-08 09:07:37 +00:00
|
|
|
ExtrusionLoop::operator Polygon() const
|
2014-05-07 10:02:09 +00:00
|
|
|
{
|
2014-05-08 09:07:37 +00:00
|
|
|
Polygon polygon;
|
|
|
|
this->polygon(&polygon);
|
|
|
|
return polygon;
|
2014-05-07 10:02:09 +00:00
|
|
|
}
|
|
|
|
|
2013-08-29 09:47:59 +00:00
|
|
|
ExtrusionLoop*
|
|
|
|
ExtrusionLoop::clone() const
|
|
|
|
{
|
|
|
|
return new ExtrusionLoop (*this);
|
|
|
|
}
|
|
|
|
|
2014-05-08 09:07:37 +00:00
|
|
|
bool
|
|
|
|
ExtrusionLoop::make_clockwise()
|
2013-07-16 19:04:14 +00:00
|
|
|
{
|
2014-05-08 09:07:37 +00:00
|
|
|
Polygon polygon = *this;
|
|
|
|
bool was_ccw = polygon.is_counter_clockwise();
|
|
|
|
if (was_ccw) this->reverse();
|
|
|
|
return was_ccw;
|
2013-07-16 19:04:14 +00:00
|
|
|
}
|
|
|
|
|
2013-08-28 23:40:42 +00:00
|
|
|
bool
|
|
|
|
ExtrusionLoop::make_counter_clockwise()
|
|
|
|
{
|
2014-05-08 09:07:37 +00:00
|
|
|
Polygon polygon = *this;
|
2014-05-07 10:02:09 +00:00
|
|
|
bool was_cw = polygon.is_clockwise();
|
|
|
|
if (was_cw) this->reverse();
|
|
|
|
return was_cw;
|
2013-08-28 23:40:42 +00:00
|
|
|
}
|
|
|
|
|
2013-08-29 09:47:59 +00:00
|
|
|
void
|
|
|
|
ExtrusionLoop::reverse()
|
|
|
|
{
|
2014-05-08 09:07:37 +00:00
|
|
|
for (ExtrusionPaths::iterator path = this->paths.begin(); path != this->paths.end(); ++path)
|
|
|
|
path->reverse();
|
|
|
|
std::reverse(this->paths.begin(), this->paths.end());
|
2013-08-29 09:47:59 +00:00
|
|
|
}
|
|
|
|
|
2014-04-24 14:40:10 +00:00
|
|
|
Point
|
2013-09-02 20:33:03 +00:00
|
|
|
ExtrusionLoop::first_point() const
|
2013-08-29 09:47:59 +00:00
|
|
|
{
|
2014-05-08 09:07:37 +00:00
|
|
|
return this->paths.front().polyline.points.front();
|
2013-08-29 09:47:59 +00:00
|
|
|
}
|
|
|
|
|
2014-04-24 14:40:10 +00:00
|
|
|
Point
|
2013-09-02 20:33:03 +00:00
|
|
|
ExtrusionLoop::last_point() const
|
2013-08-29 09:47:59 +00:00
|
|
|
{
|
2014-05-08 09:07:37 +00:00
|
|
|
return this->paths.back().polyline.points.back(); // which coincides with first_point(), by the way
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ExtrusionLoop::polygon(Polygon* polygon) const
|
|
|
|
{
|
|
|
|
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(), 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;
|
2014-05-07 10:02:09 +00:00
|
|
|
}
|
|
|
|
|
2014-11-08 11:56:14 +00:00
|
|
|
bool
|
2014-05-22 10:28:12 +00:00
|
|
|
ExtrusionLoop::split_at_vertex(const Point &point)
|
2014-05-07 10:02:09 +00:00
|
|
|
{
|
2014-05-08 09:07:37 +00:00
|
|
|
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 {
|
|
|
|
// 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);
|
2014-05-22 10:28:12 +00:00
|
|
|
if (p.polyline.is_valid()) new_paths.push_back(p);
|
2014-05-08 09:07:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// then we add all paths until the end of current path list
|
2014-12-23 23:11:29 +00:00
|
|
|
new_paths.insert(new_paths.end(), path+1, this->paths.end()); // not including this path
|
2014-05-08 09:07:37 +00:00
|
|
|
|
|
|
|
// then we add all paths since the beginning of current list up to the previous one
|
2014-12-23 23:11:29 +00:00
|
|
|
new_paths.insert(new_paths.end(), this->paths.begin(), path); // not including this path
|
2014-05-08 09:07:37 +00:00
|
|
|
|
|
|
|
// 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());
|
2014-05-22 10:28:12 +00:00
|
|
|
if (p.polyline.is_valid()) new_paths.push_back(p);
|
2014-05-08 09:07:37 +00:00
|
|
|
}
|
|
|
|
// we can now override the old path list with the new one and stop looping
|
|
|
|
this->paths = new_paths;
|
|
|
|
}
|
2014-11-08 11:56:14 +00:00
|
|
|
return true;
|
2014-05-08 09:07:37 +00:00
|
|
|
}
|
|
|
|
}
|
2014-11-08 11:56:14 +00:00
|
|
|
return false;
|
2014-05-07 10:02:09 +00:00
|
|
|
}
|
|
|
|
|
2014-05-22 10:28:12 +00:00
|
|
|
void
|
|
|
|
ExtrusionLoop::split_at(const Point &point)
|
|
|
|
{
|
|
|
|
if (this->paths.empty()) return;
|
|
|
|
|
2015-01-05 14:51:57 +00:00
|
|
|
// find the closest path and closest point belonging to that path
|
2014-05-22 10:28:12 +00:00
|
|
|
size_t path_idx = 0;
|
|
|
|
Point p = this->paths.front().first_point();
|
|
|
|
double min = point.distance_to(p);
|
|
|
|
for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) {
|
|
|
|
Point p_tmp = point.projection_onto(path->polyline);
|
|
|
|
double dist = point.distance_to(p_tmp);
|
|
|
|
if (dist < min) {
|
|
|
|
p = p_tmp;
|
|
|
|
min = dist;
|
|
|
|
path_idx = path - this->paths.begin();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// now split path_idx in two parts
|
|
|
|
ExtrusionPath p1 = this->paths[path_idx];
|
|
|
|
ExtrusionPath p2 = p1;
|
|
|
|
this->paths[path_idx].polyline.split_at(p, &p1.polyline, &p2.polyline);
|
|
|
|
|
|
|
|
// install the two paths
|
|
|
|
this->paths.erase(this->paths.begin() + path_idx);
|
|
|
|
if (p2.polyline.is_valid()) this->paths.insert(this->paths.begin() + path_idx, p2);
|
|
|
|
if (p1.polyline.is_valid()) this->paths.insert(this->paths.begin() + path_idx, p1);
|
|
|
|
|
|
|
|
// split at the new vertex
|
|
|
|
this->split_at_vertex(p);
|
|
|
|
}
|
|
|
|
|
2014-05-07 10:02:09 +00:00
|
|
|
void
|
2014-05-08 09:07:37 +00:00
|
|
|
ExtrusionLoop::clip_end(double distance, ExtrusionPaths* paths) const
|
2014-05-07 10:02:09 +00:00
|
|
|
{
|
2014-05-08 09:07:37 +00:00
|
|
|
*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);
|
|
|
|
}
|
2014-05-07 10:02:09 +00:00
|
|
|
}
|
2014-05-08 09:07:37 +00:00
|
|
|
return false;
|
2013-08-29 09:47:59 +00:00
|
|
|
}
|
|
|
|
|
2014-12-17 00:15:47 +00:00
|
|
|
bool
|
|
|
|
ExtrusionLoop::is_perimeter() const
|
|
|
|
{
|
|
|
|
return this->paths.front().role == erPerimeter
|
|
|
|
|| this->paths.front().role == erExternalPerimeter
|
|
|
|
|| this->paths.front().role == erOverhangPerimeter;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
ExtrusionLoop::is_infill() const
|
|
|
|
{
|
|
|
|
return this->paths.front().role == erBridgeInfill
|
|
|
|
|| this->paths.front().role == erInternalInfill
|
|
|
|
|| this->paths.front().role == erSolidInfill
|
|
|
|
|| this->paths.front().role == erTopSolidInfill;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
ExtrusionLoop::is_solid_infill() const
|
|
|
|
{
|
|
|
|
return this->paths.front().role == erBridgeInfill
|
|
|
|
|| this->paths.front().role == erSolidInfill
|
|
|
|
|| this->paths.front().role == erTopSolidInfill;
|
|
|
|
}
|
|
|
|
|
2015-01-15 19:06:30 +00:00
|
|
|
Polygons
|
|
|
|
ExtrusionLoop::grow() const
|
|
|
|
{
|
|
|
|
Polygons pp;
|
|
|
|
for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) {
|
|
|
|
Polygons path_pp = path->grow();
|
|
|
|
pp.insert(pp.end(), path_pp.begin(), path_pp.end());
|
|
|
|
}
|
|
|
|
return pp;
|
|
|
|
}
|
|
|
|
|
2014-04-27 17:18:53 +00:00
|
|
|
#ifdef SLIC3RXS
|
|
|
|
REGISTER_CLASS(ExtrusionLoop, "ExtrusionLoop");
|
|
|
|
#endif
|
|
|
|
|
2013-07-16 19:04:14 +00:00
|
|
|
}
|