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"
|
2016-11-03 23:10:35 +00:00
|
|
|
#include "Flow.hpp"
|
2015-05-31 20:04:32 +00:00
|
|
|
#include <cmath>
|
2016-11-03 09:24:32 +00:00
|
|
|
#include <limits>
|
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
|
|
|
|
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
|
|
|
{
|
2016-12-13 18:22:23 +00:00
|
|
|
this->_inflate_collection(intersection_pl(this->polyline, collection), 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
|
|
|
{
|
2016-12-13 18:22:23 +00:00
|
|
|
this->_inflate_collection(diff_pl(this->polyline, collection), 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-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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-03 23:10:35 +00:00
|
|
|
void ExtrusionPath::polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const
|
2015-01-15 19:06:30 +00:00
|
|
|
{
|
2016-12-13 18:22:23 +00:00
|
|
|
polygons_append(out, offset(this->polyline, float(scale_(this->width/2)) + scaled_epsilon));
|
2016-11-03 23:10:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ExtrusionPath::polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const
|
|
|
|
{
|
|
|
|
// Instantiating the Flow class to get the line spacing.
|
|
|
|
// Don't know the nozzle diameter, setting to zero. It shall not matter it shall be optimized out by the compiler.
|
2017-05-03 16:28:22 +00:00
|
|
|
Flow flow(this->width, this->height, 0.f, is_bridge(this->role()));
|
2016-12-13 18:22:23 +00:00
|
|
|
polygons_append(out, offset(this->polyline, 0.5f * float(flow.scaled_spacing()) + scaled_epsilon));
|
2015-01-15 19:06:30 +00:00
|
|
|
}
|
|
|
|
|
2017-01-19 12:35:55 +00:00
|
|
|
void ExtrusionMultiPath::reverse()
|
|
|
|
{
|
|
|
|
for (ExtrusionPaths::iterator path = this->paths.begin(); path != this->paths.end(); ++path)
|
|
|
|
path->reverse();
|
|
|
|
std::reverse(this->paths.begin(), this->paths.end());
|
|
|
|
}
|
|
|
|
|
|
|
|
double ExtrusionMultiPath::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 ExtrusionMultiPath::polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const
|
|
|
|
{
|
|
|
|
for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path)
|
|
|
|
path->polygons_covered_by_width(out, scaled_epsilon);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ExtrusionMultiPath::polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const
|
|
|
|
{
|
|
|
|
for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path)
|
|
|
|
path->polygons_covered_by_spacing(out, scaled_epsilon);
|
|
|
|
}
|
|
|
|
|
|
|
|
double ExtrusionMultiPath::min_mm3_per_mm() const
|
|
|
|
{
|
|
|
|
double min_mm3_per_mm = std::numeric_limits<double>::max();
|
|
|
|
for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path)
|
|
|
|
min_mm3_per_mm = std::min(min_mm3_per_mm, path->mm3_per_mm);
|
|
|
|
return min_mm3_per_mm;
|
|
|
|
}
|
|
|
|
|
|
|
|
Polyline ExtrusionMultiPath::as_polyline() const
|
|
|
|
{
|
|
|
|
Polyline out;
|
2017-02-15 16:51:46 +00:00
|
|
|
if (! paths.empty()) {
|
|
|
|
size_t len = 0;
|
|
|
|
for (size_t i_path = 0; i_path < paths.size(); ++ i_path) {
|
|
|
|
assert(! paths[i_path].polyline.points.empty());
|
|
|
|
assert(i_path == 0 || paths[i_path - 1].polyline.points.back() == paths[i_path].polyline.points.front());
|
|
|
|
len += paths[i_path].polyline.points.size();
|
|
|
|
}
|
|
|
|
// The connecting points between the segments are equal.
|
|
|
|
len -= paths.size() - 1;
|
|
|
|
assert(len > 0);
|
2017-01-19 12:35:55 +00:00
|
|
|
out.points.reserve(len);
|
|
|
|
out.points.push_back(paths.front().polyline.points.front());
|
|
|
|
for (size_t i_path = 0; i_path < paths.size(); ++ i_path)
|
|
|
|
out.points.insert(out.points.end(), paths[i_path].polyline.points.begin() + 1, paths[i_path].polyline.points.end());
|
|
|
|
}
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
2014-05-08 09:07:37 +00:00
|
|
|
bool
|
|
|
|
ExtrusionLoop::make_clockwise()
|
2013-07-16 19:04:14 +00:00
|
|
|
{
|
2015-01-25 14:21:45 +00:00
|
|
|
bool was_ccw = this->polygon().is_counter_clockwise();
|
2014-05-08 09:07:37 +00:00
|
|
|
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()
|
|
|
|
{
|
2015-01-25 14:21:45 +00:00
|
|
|
bool was_cw = this->polygon().is_clockwise();
|
2014-05-07 10:02:09 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2015-01-25 14:21:45 +00:00
|
|
|
Polygon
|
|
|
|
ExtrusionLoop::polygon() const
|
2014-05-08 09:07:37 +00:00
|
|
|
{
|
2015-01-25 14:21:45 +00:00
|
|
|
Polygon polygon;
|
2014-05-08 09:07:37 +00:00
|
|
|
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)
|
2015-01-25 14:21:45 +00:00
|
|
|
polygon.points.insert(polygon.points.end(), path->polyline.points.begin(), path->polyline.points.end()-1);
|
2014-05-08 09:07:37 +00:00
|
|
|
}
|
2015-01-25 14:21:45 +00:00
|
|
|
return polygon;
|
2014-05-08 09:07:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2016-09-13 07:44:30 +00:00
|
|
|
new_paths.reserve(this->paths.size() + 1);
|
2014-05-08 09:07:37 +00:00
|
|
|
{
|
|
|
|
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
|
2016-09-13 07:44:30 +00:00
|
|
|
std::swap(this->paths, new_paths);
|
2014-05-08 09:07:37 +00:00
|
|
|
}
|
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
|
|
|
}
|
|
|
|
|
2017-02-02 17:49:33 +00:00
|
|
|
// Splitting an extrusion loop, possibly made of multiple segments, some of the segments may be bridging.
|
|
|
|
void ExtrusionLoop::split_at(const Point &point, bool prefer_non_overhang)
|
2014-05-22 10:28:12 +00:00
|
|
|
{
|
2017-02-02 17:49:33 +00:00
|
|
|
if (this->paths.empty())
|
|
|
|
return;
|
2014-05-22 10:28:12 +00:00
|
|
|
|
2017-02-02 17:49:33 +00:00
|
|
|
// Find the closest path and closest point belonging to that path. Avoid overhangs, if asked for.
|
2014-05-22 10:28:12 +00:00
|
|
|
size_t path_idx = 0;
|
2017-02-02 17:49:33 +00:00
|
|
|
Point p;
|
|
|
|
{
|
|
|
|
double min = std::numeric_limits<double>::max();
|
|
|
|
Point p_non_overhang;
|
|
|
|
size_t path_idx_non_overhang = 0;
|
|
|
|
double min_non_overhang = std::numeric_limits<double>::max();
|
|
|
|
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();
|
|
|
|
}
|
2017-05-03 16:28:22 +00:00
|
|
|
if (prefer_non_overhang && ! is_bridge(path->role()) && dist < min_non_overhang) {
|
2017-02-02 17:49:33 +00:00
|
|
|
p_non_overhang = p_tmp;
|
|
|
|
min_non_overhang = dist;
|
|
|
|
path_idx_non_overhang = path - this->paths.begin();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (prefer_non_overhang && min_non_overhang != std::numeric_limits<double>::max()) {
|
|
|
|
// Only apply the non-overhang point if there is one.
|
|
|
|
path_idx = path_idx_non_overhang;
|
|
|
|
p = p_non_overhang;
|
2014-05-22 10:28:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// now split path_idx in two parts
|
2016-11-03 23:10:35 +00:00
|
|
|
const ExtrusionPath &path = this->paths[path_idx];
|
2017-04-07 15:37:30 +00:00
|
|
|
ExtrusionPath p1(path.role(), path.mm3_per_mm, path.width, path.height);
|
|
|
|
ExtrusionPath p2(path.role(), path.mm3_per_mm, path.width, path.height);
|
2016-11-03 23:10:35 +00:00
|
|
|
path.polyline.split_at(p, &p1.polyline, &p2.polyline);
|
2014-05-22 10:28:12 +00:00
|
|
|
|
2016-09-13 07:44:30 +00:00
|
|
|
if (this->paths.size() == 1) {
|
|
|
|
if (! p1.polyline.is_valid())
|
|
|
|
std::swap(this->paths.front().polyline.points, p2.polyline.points);
|
|
|
|
else if (! p2.polyline.is_valid())
|
|
|
|
std::swap(this->paths.front().polyline.points, p1.polyline.points);
|
|
|
|
else {
|
|
|
|
p2.polyline.points.insert(p2.polyline.points.end(), p1.polyline.points.begin() + 1, p1.polyline.points.end());
|
|
|
|
std::swap(this->paths.front().polyline.points, p2.polyline.points);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// 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);
|
|
|
|
}
|
2014-05-22 10:28:12 +00:00
|
|
|
|
|
|
|
// 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
|
2017-05-03 16:28:22 +00:00
|
|
|
return (is_bridge(path->role()) && pos > 0 && pos != (int)(path->polyline.points.size())-1);
|
2014-05-08 09:07:37 +00:00
|
|
|
}
|
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
|
|
|
}
|
|
|
|
|
2016-11-03 23:10:35 +00:00
|
|
|
void ExtrusionLoop::polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const
|
|
|
|
{
|
|
|
|
for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path)
|
|
|
|
path->polygons_covered_by_width(out, scaled_epsilon);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ExtrusionLoop::polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const
|
2015-01-15 19:06:30 +00:00
|
|
|
{
|
2016-11-03 09:24:32 +00:00
|
|
|
for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path)
|
2016-11-03 23:10:35 +00:00
|
|
|
path->polygons_covered_by_spacing(out, scaled_epsilon);
|
2015-01-15 19:06:30 +00:00
|
|
|
}
|
|
|
|
|
2015-05-31 20:04:32 +00:00
|
|
|
double
|
|
|
|
ExtrusionLoop::min_mm3_per_mm() const
|
|
|
|
{
|
2016-11-03 09:24:32 +00:00
|
|
|
double min_mm3_per_mm = std::numeric_limits<double>::max();
|
|
|
|
for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path)
|
|
|
|
min_mm3_per_mm = std::min(min_mm3_per_mm, path->mm3_per_mm);
|
2015-05-31 20:04:32 +00:00
|
|
|
return min_mm3_per_mm;
|
|
|
|
}
|
|
|
|
|
2013-07-16 19:04:14 +00:00
|
|
|
}
|