#include "ExtrusionEntity.hpp" #include "ExtrusionEntityCollection.hpp" #include "ExPolygonCollection.hpp" #include "ClipperUtils.hpp" #include "Extruder.hpp" #include #include namespace Slic3r { ExtrusionPath* ExtrusionPath::clone() const { return new ExtrusionPath (*this); } void ExtrusionPath::reverse() { this->polyline.reverse(); } Point ExtrusionPath::first_point() const { return this->polyline.points.front(); } Point ExtrusionPath::last_point() const { return this->polyline.points.back(); } void ExtrusionPath::intersect_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const { // perform clipping Polylines clipped; intersection(this->polyline, collection, &clipped); return this->_inflate_collection(clipped, retval); } void ExtrusionPath::subtract_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const { // perform clipping Polylines clipped; diff(this->polyline, collection, &clipped); return this->_inflate_collection(clipped, retval); } void ExtrusionPath::clip_end(double distance) { this->polyline.clip_end(distance); } void ExtrusionPath::simplify(double tolerance) { this->polyline.simplify(tolerance); } double ExtrusionPath::length() const { return this->polyline.length(); } bool ExtrusionPath::is_perimeter() const { return this->role == erPerimeter || this->role == erExternalPerimeter || this->role == erOverhangPerimeter; } bool ExtrusionPath::is_infill() const { return this->role == erBridgeInfill || this->role == erInternalInfill || this->role == erSolidInfill || this->role == erTopSolidInfill; } bool ExtrusionPath::is_solid_infill() const { return this->role == erBridgeInfill || this->role == erSolidInfill || this->role == erTopSolidInfill; } bool ExtrusionPath::is_bridge() const { return this->role == erBridgeInfill || this->role == erOverhangPerimeter; } void ExtrusionPath::_inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const { for (Polylines::const_iterator it = polylines.begin(); it != polylines.end(); ++it) { ExtrusionPath* path = this->clone(); path->polyline = *it; collection->entities.push_back(path); } } Polygons ExtrusionPath::grow() const { Polygons pp; offset(this->polyline, &pp, +this->width/2); return pp; } ExtrusionLoop* ExtrusionLoop::clone() const { return new ExtrusionLoop (*this); } bool ExtrusionLoop::make_clockwise() { bool was_ccw = this->polygon().is_counter_clockwise(); if (was_ccw) this->reverse(); return was_ccw; } bool ExtrusionLoop::make_counter_clockwise() { bool was_cw = this->polygon().is_clockwise(); if (was_cw) this->reverse(); return was_cw; } void ExtrusionLoop::reverse() { 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->paths.front().polyline.points.front(); } Point ExtrusionLoop::last_point() const { return this->paths.back().polyline.points.back(); // which coincides with first_point(), by the way } Polygon ExtrusionLoop::polygon() const { Polygon polygon; 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); } return polygon; } 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; } bool ExtrusionLoop::split_at_vertex(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 { // 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.is_valid()) new_paths.push_back(p); } // then we add all paths until the end of current path list new_paths.insert(new_paths.end(), path+1, this->paths.end()); // 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(), this->paths.begin(), path); // 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.is_valid()) 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 true; } } return false; } void ExtrusionLoop::split_at(const Point &point) { if (this->paths.empty()) return; // find the closest path and closest point belonging to that path 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); } 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 != (int)(path->polyline.points.size())-1); } } return false; } 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; } 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; } double ExtrusionLoop::min_mm3_per_mm() const { double min_mm3_per_mm = 0; for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) { if (min_mm3_per_mm == 0) { min_mm3_per_mm = path->mm3_per_mm; } else { min_mm3_per_mm = fmin(min_mm3_per_mm, path->mm3_per_mm); } } return min_mm3_per_mm; } }