diff options
author | Alessandro Ranellucci <aar@cpan.org> | 2015-07-02 21:24:16 +0300 |
---|---|---|
committer | Alessandro Ranellucci <aar@cpan.org> | 2015-07-02 21:24:16 +0300 |
commit | 3e739b87da9f49b61a6dfc9aa99a06ba28ddfe39 (patch) | |
tree | 9c2317b1dc3ac2f9763e9d8837eb65e28c4dc77e /xs/src/libslic3r/GCode.cpp | |
parent | 0ad4296aaffef41d14f074a1c2689fca9a43e565 (diff) |
Finished porting Slic3r::GCode to XS (speed boost!)
Diffstat (limited to 'xs/src/libslic3r/GCode.cpp')
-rw-r--r-- | xs/src/libslic3r/GCode.cpp | 171 |
1 files changed, 168 insertions, 3 deletions
diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index f153b96f7..51093667a 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -1,6 +1,7 @@ #include "GCode.hpp" #include "ExtrusionEntity.hpp" #include <algorithm> +#include <cstdlib> namespace Slic3r { @@ -320,9 +321,173 @@ GCode::change_layer(const Layer &layer) } std::string -GCode::extrude_path(const ExtrusionPath &path, std::string description, double speed) +GCode::extrude(ExtrusionLoop loop, std::string description, double speed) { - std::string gcode = this->_extrude_path(path, description, speed); + // get a copy; don't modify the orientation of the original loop object otherwise + // next copies (if any) would not detect the correct orientation + + // extrude all loops ccw + bool was_clockwise = loop.make_counter_clockwise(); + + // find the point of the loop that is closest to the current extruder position + // or randomize if requested + Point last_pos = this->last_pos(); + if (this->config.spiral_vase) { + loop.split_at(last_pos); + } else if (this->config.seam_position == spNearest || this->config.seam_position == spAligned) { + Polygon polygon = loop.polygon(); + + // simplify polygon in order to skip false positives in concave/convex detection + // (loop is always ccw as polygon.simplify() only works on ccw polygons) + Polygons simplified = polygon.simplify(scale_(EXTRUDER_CONFIG(nozzle_diameter))/2); + + // restore original winding order so that concave and convex detection always happens + // on the right/outer side of the polygon + if (was_clockwise) { + for (Polygons::iterator p = simplified.begin(); p != simplified.end(); ++p) + p->reverse(); + } + + // concave vertices have priority + Points candidates; + for (Polygons::const_iterator p = simplified.begin(); p != simplified.end(); ++p) { + Points concave = p->concave_points(PI*4/3); + candidates.insert(candidates.end(), concave.begin(), concave.end()); + } + + // if no concave points were found, look for convex vertices + if (candidates.empty()) { + for (Polygons::const_iterator p = simplified.begin(); p != simplified.end(); ++p) { + Points convex = p->convex_points(PI*2/3); + candidates.insert(candidates.end(), convex.begin(), convex.end()); + } + } + + // retrieve the last start position for this object + if (this->layer != NULL && this->_seam_position.count(this->layer->object()) > 0) { + last_pos = this->_seam_position[this->layer->object()]; + } + + Point point; + if (this->config.seam_position == spNearest) { + if (candidates.empty()) candidates = polygon.points; + last_pos.nearest_point(candidates, &point); + + // On 32-bit Linux, Clipper will change some point coordinates by 1 unit + // while performing simplify_polygons(), thus split_at_vertex() won't + // find them anymore. + if (!loop.split_at_vertex(point)) loop.split_at(point); + } else if (!candidates.empty()) { + Points non_overhang; + for (Points::const_iterator p = candidates.begin(); p != candidates.end(); ++p) { + if (!loop.has_overhang_point(*p)) + non_overhang.push_back(*p); + } + + if (!non_overhang.empty()) + candidates = non_overhang; + + last_pos.nearest_point(candidates, &point); + if (!loop.split_at_vertex(point)) loop.split_at(point); // see note above + } else { + point = last_pos.projection_onto(polygon); + loop.split_at(point); + } + if (this->layer != NULL) + this->_seam_position[this->layer->object()] = point; + } else if (this->config.seam_position == spRandom) { + if (loop.role == elrContourInternalPerimeter) { + Polygon polygon = loop.polygon(); + Point centroid = polygon.centroid(); + last_pos = Point(polygon.bounding_box().max.x, centroid.y); + last_pos.rotate(rand() % 2*PI, centroid); + } + loop.split_at(last_pos); + } + + // clip the path to avoid the extruder to get exactly on the first point of the loop; + // if polyline was shorter than the clipping distance we'd get a null polyline, so + // we discard it in that case + double clip_length = this->enable_loop_clipping + ? scale_(EXTRUDER_CONFIG(nozzle_diameter)) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER + : 0; + + // get paths + ExtrusionPaths paths; + loop.clip_end(clip_length, &paths); + if (paths.empty()) return ""; + + // apply the small perimeter speed + if (paths.front().is_perimeter() && loop.length() <= SMALL_PERIMETER_LENGTH) { + if (speed == -1) speed = this->config.get_abs_value("small_perimeter_speed"); + } + + // extrude along the path + std::string gcode; + for (ExtrusionPaths::const_iterator path = paths.begin(); path != paths.end(); ++path) + gcode += this->_extrude(*path, description, speed); + + // reset acceleration + gcode += this->writer.set_acceleration(this->config.default_acceleration.value); + + if (this->wipe.enable) + this->wipe.path = paths.front().polyline; // TODO: don't limit wipe to last path + + // make a little move inwards before leaving loop + if (paths.back().role == erExternalPerimeter && this->layer != NULL && this->config.perimeters > 1) { + Polyline &last_path_polyline = paths.back().polyline; + // detect angle between last and first segment + // the side depends on the original winding order of the polygon (left for contours, right for holes) + Point a = paths.front().polyline.points[1]; // second point + Point b = *(paths.back().polyline.points.end()-3); // second to last point + if (was_clockwise) { + // swap points + Point c = a; a = b; b = c; + } + + double angle = paths.front().first_point().ccw_angle(a, b) / 3; + + // turn left if contour, turn right if hole + if (was_clockwise) angle *= -1; + + // create the destination point along the first segment and rotate it + // we make sure we don't exceed the segment length because we don't know + // the rotation of the second segment so we might cross the object boundary + Line first_segment( + paths.front().polyline.points[0], + paths.front().polyline.points[1] + ); + double distance = std::min( + scale_(EXTRUDER_CONFIG(nozzle_diameter)), + first_segment.length() + ); + Point point = first_segment.point_at(distance); + point.rotate(angle, first_segment.a); + + // generate the travel move + gcode += this->writer.travel_to_xy(this->point_to_gcode(point), "move inwards before travel"); + } + + return gcode; +} + +std::string +GCode::extrude(const ExtrusionEntity &entity, std::string description, double speed) +{ + if (const ExtrusionPath* path = dynamic_cast<const ExtrusionPath*>(&entity)) { + return this->extrude(*path, description, speed); + } else if (const ExtrusionLoop* loop = dynamic_cast<const ExtrusionLoop*>(&entity)) { + return this->extrude(*loop, description, speed); + } else { + CONFESS("Invalid argument supplied to extrude()"); + return ""; + } +} + +std::string +GCode::extrude(const ExtrusionPath &path, std::string description, double speed) +{ + std::string gcode = this->_extrude(path, description, speed); // reset acceleration gcode += this->writer.set_acceleration(this->config.default_acceleration.value); @@ -331,7 +496,7 @@ GCode::extrude_path(const ExtrusionPath &path, std::string description, double s } std::string -GCode::_extrude_path(ExtrusionPath path, std::string description, double speed) +GCode::_extrude(ExtrusionPath path, std::string description, double speed) { path.simplify(SCALED_RESOLUTION); |