From b4019bb438705978aac692b3535fa695d546fb85 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 1 Jul 2015 23:00:52 +0200 Subject: [PATCH] Ported more Slic3r::GCode methods to XS --- lib/Slic3r/GCode.pm | 141 +-------------------------- t/shells.t | 4 +- xs/MANIFEST | 1 + xs/src/libslic3r/GCode.cpp | 167 ++++++++++++++++++++++++++++++-- xs/src/libslic3r/GCode.hpp | 24 +++-- xs/src/libslic3r/MultiPoint.cpp | 18 ++++ xs/src/libslic3r/MultiPoint.hpp | 3 + xs/t/21_gcode.t | 17 ++++ xs/xsp/GCode.xsp | 27 ++++-- 9 files changed, 237 insertions(+), 165 deletions(-) create mode 100644 xs/t/21_gcode.t diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 7885cc9b6..8881cf0cd 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -8,13 +8,6 @@ use Slic3r::ExtrusionPath ':roles'; use Slic3r::Geometry qw(epsilon scale unscale PI X Y B); use Slic3r::Geometry::Clipper qw(union_ex); -sub apply_print_config { - my ($self, $print_config) = @_; - - $self->writer->apply_print_config($print_config); - $self->config->apply_print_config($print_config); -} - sub set_extruders { my ($self, $extruder_ids) = @_; @@ -24,34 +17,6 @@ sub set_extruders { $self->wipe->set_enable(defined first { $self->config->get_at('wipe', $_) } @$extruder_ids); } -sub set_origin { - my ($self, $pointf) = @_; - - # if origin increases (goes towards right), last_pos decreases because it goes towards left - my @translate = ( - scale ($self->origin->x - $pointf->x), - scale ($self->origin->y - $pointf->y), #- - ); - $self->last_pos->translate(@translate); - $self->wipe->path->translate(@translate) if $self->wipe->has_path; - - $self->_set_origin($pointf); -} - -sub preamble { - my ($self) = @_; - - my $gcode = $self->writer->preamble; - - # Perform a *silent* move to z_offset: we need this to initialize the Z - # position of our writer object so that any initial lift taking place - # before the first layer change will raise the extruder from the correct - # initial Z instead of 0. - $self->writer->travel_to_z($self->config->z_offset, ''); - - return $gcode; -} - sub change_layer { my ($self, $layer) = @_; @@ -348,7 +313,7 @@ sub travel_to { if ($needs_retraction && $self->config->avoid_crossing_perimeters && !$self->avoid_crossing_perimeters->disable_once) { - $travel = $self->avoid_crossing_perimeters->travel_to($point, $self->origin, $self->last_pos); + $travel = $self->avoid_crossing_perimeters->travel_to($self, $point); # check again whether the new travel path still needs a retraction $needs_retraction = $self->needs_retraction($travel, $role); @@ -404,51 +369,6 @@ sub needs_retraction { return 1; } -sub retract { - my ($self, $toolchange) = @_; - - return "" if !defined $self->writer->extruder; - - my $gcode = ""; - - # wipe (if it's enabled for this extruder and we have a stored wipe path) - if ($self->config->get_at('wipe', $self->writer->extruder->id) && $self->wipe->has_path) { - $gcode .= $self->wipe->wipe($self, $toolchange); - } - - # The parent class will decide whether we need to perform an actual retraction - # (the extruder might be already retracted fully or partially). We call these - # methods even if we performed wipe, since this will ensure the entire retraction - # length is honored in case wipe path was too short.p - $gcode .= $toolchange ? $self->writer->retract_for_toolchange : $self->writer->retract; - - $gcode .= $self->writer->reset_e; - $gcode .= $self->writer->lift - if $self->writer->extruder->retract_length > 0 || $self->config->use_firmware_retraction; - - return $gcode; -} - -sub unretract { - my ($self) = @_; - - my $gcode = ""; - $gcode .= $self->writer->unlift; - $gcode .= $self->writer->unretract; - return $gcode; -} - -# convert a model-space scaled point into G-code coordinates -sub point_to_gcode { - my ($self, $point) = @_; - - my $extruder_offset = $self->config->get_at('extruder_offset', $self->writer->extruder->id); - return Slic3r::Pointf->new( - ($point->x * &Slic3r::SCALING_FACTOR) + $self->origin->x - $extruder_offset->x, - ($point->y * &Slic3r::SCALING_FACTOR) + $self->origin->y - $extruder_offset->y, #** - ); -} - sub set_extruder { my ($self, $extruder_id) = @_; @@ -535,63 +455,4 @@ sub post_toolchange { return $gcode; } -package Slic3r::GCode::Wipe; -use strict; -use warnings; - -use Slic3r::Geometry qw(scale); - -sub wipe { - my ($self, $gcodegen, $toolchange) = @_; - - my $gcode = ""; - - # Reduce feedrate a bit; travel speed is often too high to move on existing material. - # Too fast = ripping of existing material; too slow = short wipe path, thus more blob. - my $wipe_speed = $gcodegen->writer->config->get('travel_speed') * 0.8; - - # get the retraction length - my $length = $toolchange - ? $gcodegen->writer->extruder->retract_length_toolchange - : $gcodegen->writer->extruder->retract_length; - - if ($length) { - # Calculate how long we need to travel in order to consume the required - # amount of retraction. In other words, how far do we move in XY at $wipe_speed - # for the time needed to consume retract_length at retract_speed? - my $wipe_dist = scale($length / $gcodegen->writer->extruder->retract_speed * $wipe_speed); - - # Take the stored wipe path and replace first point with the current actual position - # (they might be different, for example, in case of loop clipping). - my $wipe_path = Slic3r::Polyline->new( - $gcodegen->last_pos, - @{$self->path}[1..$#{$self->path}], - ); - # - $wipe_path->clip_end($wipe_path->length - $wipe_dist); - - # subdivide the retraction in segments - my $retracted = 0; - foreach my $line (@{$wipe_path->lines}) { - my $segment_length = $line->length; - # Reduce retraction length a bit to avoid effective retraction speed to be greater than the configured one - # due to rounding (TODO: test and/or better math for this) - my $dE = $length * ($segment_length / $wipe_dist) * 0.95; - $gcode .= $gcodegen->writer->set_speed($wipe_speed*60); - $gcode .= $gcodegen->writer->extrude_to_xy( - $gcodegen->point_to_gcode($line->b), - -$dE, - 'wipe and retract' . ($gcodegen->enable_cooling_markers ? ';_WIPE' : ''), - ); - $retracted += $dE; - } - $gcodegen->writer->extruder->set_retracted($gcodegen->writer->extruder->retracted + $retracted); - - # prevent wiping again on same path - $self->reset_path; - } - - return $gcode; -} - 1; diff --git a/t/shells.t b/t/shells.t index f3fd62d14..e7eb5da13 100644 --- a/t/shells.t +++ b/t/shells.t @@ -192,7 +192,7 @@ use Slic3r::Test; push @z_steps, $info->{dist_Z} if $started_extruding && $info->{dist_Z} > 0; $travel_moves_after_first_extrusion++ - if $info->{travel} && $started_extruding && !exists $args->{Z}; + if $info->{travel} && $info->{dist_XY} > 0 && $started_extruding && !exists $args->{Z}; } elsif ($cmd eq 'M104') { $first_layer_temperature_set = 1 if $args->{S} == 205; $temperature_set = 1 if $args->{S} == 200; @@ -271,7 +271,7 @@ use Slic3r::Test; foreach my $segment (@this_layer) { # check that segment's dist_Z is proportioned to its dist_XY $all_layer_segments_have_same_slope = 1 - if abs($segment->[0]*$total_dist_XY/$config->layer_height - $segment->[1]) > 0.1; + if abs($segment->[0]*$total_dist_XY/$config->layer_height - $segment->[1]) > 0.2; } @this_layer = (); diff --git a/xs/MANIFEST b/xs/MANIFEST index 7d7923d26..2011b9cf3 100644 --- a/xs/MANIFEST +++ b/xs/MANIFEST @@ -1752,6 +1752,7 @@ t/17_boundingbox.t t/18_motionplanner.t t/19_model.t t/20_print.t +t/21_gcode.t xsp/BoundingBox.xsp xsp/BridgeDetector.xsp xsp/Clipper.xsp diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index 506e11982..9acfe47d3 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -36,16 +36,15 @@ AvoidCrossingPerimeters::init_layer_mp(const ExPolygons &islands) } Polyline -AvoidCrossingPerimeters::travel_to(Point point, const Pointf &gcodegen_origin, - const Point &gcodegen_last_pos) +AvoidCrossingPerimeters::travel_to(GCode &gcodegen, Point point) { if (this->use_external_mp || this->use_external_mp_once) { // get current origin set in gcodegen // (the one that will be used to translate the G-code coordinates by) - Point scaled_origin = Point::new_scale(gcodegen_origin.x, gcodegen_origin.y); + Point scaled_origin = Point::new_scale(gcodegen.origin.x, gcodegen.origin.y); // represent last_pos in absolute G-code coordinates - Point last_pos = gcodegen_last_pos; + Point last_pos = gcodegen.last_pos(); last_pos.translate(scaled_origin); // represent point in absolute G-code coordinates @@ -59,7 +58,7 @@ AvoidCrossingPerimeters::travel_to(Point point, const Pointf &gcodegen_origin, travel.translate(scaled_origin.negative()); return travel; } else { - return this->_layer_mp->shortest_path(gcodegen_last_pos, point); + return this->_layer_mp->shortest_path(gcodegen.last_pos(), point); } } @@ -93,6 +92,62 @@ Wipe::reset_path() this->path = Polyline(); } +std::string +Wipe::wipe(GCode &gcodegen, bool toolchange) +{ + std::string gcode; + + /* Reduce feedrate a bit; travel speed is often too high to move on existing material. + Too fast = ripping of existing material; too slow = short wipe path, thus more blob. */ + double wipe_speed = gcodegen.writer.config.travel_speed.value * 0.8; + + // get the retraction length + double length = toolchange + ? gcodegen.writer.extruder()->retract_length_toolchange() + : gcodegen.writer.extruder()->retract_length(); + + if (length > 0) { + /* Calculate how long we need to travel in order to consume the required + amount of retraction. In other words, how far do we move in XY at wipe_speed + for the time needed to consume retract_length at retract_speed? */ + double wipe_dist = scale_(length / gcodegen.writer.extruder()->retract_speed() * wipe_speed); + + /* Take the stored wipe path and replace first point with the current actual position + (they might be different, for example, in case of loop clipping). */ + Polyline wipe_path; + wipe_path.append(gcodegen.last_pos()); + wipe_path.append( + this->path.points.begin() + 1, + this->path.points.end() + ); + + wipe_path.clip_end(wipe_path.length() - wipe_dist); + + // subdivide the retraction in segments + double retracted = 0; + Lines lines = wipe_path.lines(); + for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) { + double segment_length = line->length(); + /* Reduce retraction length a bit to avoid effective retraction speed to be greater than the configured one + due to rounding (TODO: test and/or better math for this) */ + double dE = length * (segment_length / wipe_dist) * 0.95; + gcode += gcodegen.writer.set_speed(wipe_speed*60); + gcode += gcodegen.writer.extrude_to_xy( + gcodegen.point_to_gcode(line->b), + -dE, + (std::string)"wipe and retract" + (gcodegen.enable_cooling_markers ? ";_WIPE" : "") + ); + retracted += dE; + } + gcodegen.writer.extruder()->retracted += retracted; + + // prevent wiping again on same path + this->reset_path(); + } + + return gcode; +} + #ifdef SLIC3RXS REGISTER_CLASS(Wipe, "GCode::Wipe"); #endif @@ -100,10 +155,110 @@ REGISTER_CLASS(Wipe, "GCode::Wipe"); GCode::GCode() : enable_loop_clipping(true), enable_cooling_markers(false), layer_count(0), layer_index(-1), first_layer(false), elapsed_time(0), volumetric_speed(0), - last_pos_defined(false) + _last_pos_defined(false) { } +Point& +GCode::last_pos() +{ + return this->_last_pos; +} + +void +GCode::set_last_pos(const Point &pos) +{ + this->_last_pos = pos; + this->_last_pos_defined = true; +} + +bool +GCode::last_pos_defined() const +{ + return this->_last_pos_defined; +} + +void +GCode::apply_print_config(const PrintConfig &print_config) +{ + this->writer.apply_print_config(print_config); + this->config.apply(print_config); +} + +void +GCode::set_origin(const Pointf &pointf) +{ + // if origin increases (goes towards right), last_pos decreases because it goes towards left + Point translate( + scale_(this->origin.x - pointf.x), + scale_(this->origin.y - pointf.y) + ); + this->_last_pos.translate(translate); + this->wipe.path.translate(translate); + + this->origin = pointf; +} + +std::string +GCode::preamble() +{ + std::string gcode = this->writer.preamble(); + + /* Perform a *silent* move to z_offset: we need this to initialize the Z + position of our writer object so that any initial lift taking place + before the first layer change will raise the extruder from the correct + initial Z instead of 0. */ + this->writer.travel_to_z(this->config.z_offset.value); + + return gcode; +} + +std::string +GCode::retract(bool toolchange) +{ + std::string gcode; + + if (this->writer.extruder() == NULL) + return gcode; + + // wipe (if it's enabled for this extruder and we have a stored wipe path) + if (this->config.wipe.get_at(this->writer.extruder()->id) && this->wipe.has_path()) { + gcode += this->wipe.wipe(*this, toolchange); + } + + /* The parent class will decide whether we need to perform an actual retraction + (the extruder might be already retracted fully or partially). We call these + methods even if we performed wipe, since this will ensure the entire retraction + length is honored in case wipe path was too short. */ + gcode += toolchange ? this->writer.retract_for_toolchange() : this->writer.retract(); + + gcode += this->writer.reset_e(); + if (this->writer.extruder()->retract_length() > 0 || this->config.use_firmware_retraction) + gcode += this->writer.lift(); + + return gcode; +} + +std::string +GCode::unretract() +{ + std::string gcode; + gcode += this->writer.unlift(); + gcode += this->writer.unretract(); + return gcode; +} + +// convert a model-space scaled point into G-code coordinates +Pointf +GCode::point_to_gcode(const Point &point) +{ + Pointf extruder_offset = this->config.extruder_offset.get_at(this->writer.extruder()->id); + return Pointf( + unscale(point.x) + this->origin.x - extruder_offset.x, + unscale(point.y) + this->origin.y - extruder_offset.y + ); +} + #ifdef SLIC3RXS REGISTER_CLASS(GCode, "GCode"); #endif diff --git a/xs/src/libslic3r/GCode.hpp b/xs/src/libslic3r/GCode.hpp index 8eed9600f..8b37386e9 100644 --- a/xs/src/libslic3r/GCode.hpp +++ b/xs/src/libslic3r/GCode.hpp @@ -14,7 +14,7 @@ namespace Slic3r { -// draft for a binary representation of a G-code line +class GCode; class AvoidCrossingPerimeters { public: @@ -31,10 +31,7 @@ class AvoidCrossingPerimeters { ~AvoidCrossingPerimeters(); void init_external_mp(const ExPolygons &islands); void init_layer_mp(const ExPolygons &islands); - - //Polyline travel_to(GCode &gcodegen, const Point &point); - Polyline travel_to(Point point, const Pointf &gcodegen_origin, - const Point &gcodegen_last_pos); + Polyline travel_to(GCode &gcodegen, Point point); private: MotionPlanner* _external_mp; @@ -57,7 +54,7 @@ class Wipe { Wipe(); bool has_path(); void reset_path(); - //std::string wipe(GCode &gcodegen, bool toolchange = false); + std::string wipe(GCode &gcodegen, bool toolchange = false); }; class GCode { @@ -81,11 +78,22 @@ class GCode { std::map _seam_position; bool first_layer; // this flag triggers first layer speeds unsigned int elapsed_time; // seconds - Point last_pos; - bool last_pos_defined; double volumetric_speed; GCode(); + Point& last_pos(); + void set_last_pos(const Point &pos); + bool last_pos_defined() const; + void apply_print_config(const PrintConfig &print_config); + void set_origin(const Pointf &pointf); + std::string preamble(); + std::string retract(bool toolchange = false); + std::string unretract(); + Pointf point_to_gcode(const Point &point); + + private: + Point _last_pos; + bool _last_pos_defined; }; } diff --git a/xs/src/libslic3r/MultiPoint.cpp b/xs/src/libslic3r/MultiPoint.cpp index 614306e42..2e7131e6e 100644 --- a/xs/src/libslic3r/MultiPoint.cpp +++ b/xs/src/libslic3r/MultiPoint.cpp @@ -100,6 +100,24 @@ MultiPoint::remove_duplicate_points() } } +void +MultiPoint::append(const Point &point) +{ + this->points.push_back(point); +} + +void +MultiPoint::append(const Points &points) +{ + this->append(points.begin(), points.end()); +} + +void +MultiPoint::append(const Points::const_iterator &begin, const Points::const_iterator &end) +{ + this->points.insert(this->points.end(), begin, end); +} + Points MultiPoint::_douglas_peucker(const Points &points, const double tolerance) { diff --git a/xs/src/libslic3r/MultiPoint.hpp b/xs/src/libslic3r/MultiPoint.hpp index 12c500836..63ee3bda7 100644 --- a/xs/src/libslic3r/MultiPoint.hpp +++ b/xs/src/libslic3r/MultiPoint.hpp @@ -33,6 +33,9 @@ class MultiPoint bool has_boundary_point(const Point &point) const; BoundingBox bounding_box() const; void remove_duplicate_points(); + void append(const Point &point); + void append(const Points &points); + void append(const Points::const_iterator &begin, const Points::const_iterator &end); static Points _douglas_peucker(const Points &points, const double tolerance); diff --git a/xs/t/21_gcode.t b/xs/t/21_gcode.t new file mode 100644 index 000000000..307de6421 --- /dev/null +++ b/xs/t/21_gcode.t @@ -0,0 +1,17 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use Slic3r::XS; +use Test::More tests => 2; + +{ + my $gcodegen = Slic3r::GCode->new; + $gcodegen->set_origin(Slic3r::Pointf->new(10,0)); + is_deeply $gcodegen->origin->pp, [10,0], 'set_origin'; + $gcodegen->origin->translate(5,5); + is_deeply $gcodegen->origin->pp, [15,5], 'origin returns reference to point'; +} + +__END__ diff --git a/xs/xsp/GCode.xsp b/xs/xsp/GCode.xsp index 8000b973a..41bdbc042 100644 --- a/xs/xsp/GCode.xsp +++ b/xs/xsp/GCode.xsp @@ -11,8 +11,8 @@ void init_external_mp(ExPolygons islands); void init_layer_mp(ExPolygons islands); - Clone travel_to(Point* point, Pointf* gcodegen_origin, Point* gcodegen_last_pos) - %code{% RETVAL = THIS->travel_to(*point, *gcodegen_origin, *gcodegen_last_pos); %}; + Clone travel_to(GCode* gcode, Point* point) + %code{% RETVAL = THIS->travel_to(*gcode, *point); %}; bool use_external_mp() %code{% RETVAL = THIS->use_external_mp; %}; @@ -51,6 +51,8 @@ bool has_path(); void reset_path(); + std::string wipe(GCode* gcodegen, bool toolchange = false) + %code{% RETVAL = THIS->wipe(*gcodegen, toolchange); %}; bool enable() %code{% RETVAL = THIS->enable; %}; @@ -69,8 +71,6 @@ Ref origin() %code{% RETVAL = &(THIS->origin); %}; - void _set_origin(Pointf* value) - %code{% THIS->origin = *value; %}; Ref config() %code{% RETVAL = &(THIS->config); %}; @@ -136,15 +136,24 @@ void set_elapsed_time(unsigned int value) %code{% THIS->elapsed_time = value; %}; - bool last_pos_defined() - %code{% RETVAL = THIS->last_pos_defined; %}; + bool last_pos_defined(); Ref last_pos() - %code{% RETVAL = &(THIS->last_pos); %}; - void set_last_pos(Point* value) - %code{% THIS->last_pos = *value; THIS->last_pos_defined = true; %}; + %code{% RETVAL = &(THIS->last_pos()); %}; + void set_last_pos(Point* pos) + %code{% THIS->set_last_pos(*pos); %}; double volumetric_speed() %code{% RETVAL = THIS->volumetric_speed; %}; void set_volumetric_speed(double value) %code{% THIS->volumetric_speed = value; %}; + + void apply_print_config(PrintConfig* print_config) + %code{% THIS->apply_print_config(*print_config); %}; + void set_origin(Pointf* pointf) + %code{% THIS->set_origin(*pointf); %}; + std::string preamble(); + std::string retract(bool toolchange = false); + std::string unretract(); + Clone point_to_gcode(Point* point) + %code{% RETVAL = THIS->point_to_gcode(*point); %}; };