From e90279c513a1fb1fb50bf78d7e58a74e4889d7af Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 3 May 2017 18:28:22 +0200 Subject: [PATCH] Ported the G-code generator from Perl to C++. Removed GCode.pm Removed the Perl bindigns for AvoidCrossingPerimeters, OozePrevention, SpiralVase, Wipe Changed the std::set of extruder IDs to vector of IDs. Removed some MSVC compiler warnings, removed obnoxious compiler warnings when compiling the Perl bindings. --- lib/Slic3r.pm | 4 - lib/Slic3r/Print.pm | 37 +- lib/Slic3r/Print/GCode.pm | 712 ----------- lib/Slic3r/Test.pm | 16 +- slic3r.pl | 2 - t/cooling.t | 12 +- xs/MANIFEST | 1 - xs/lib/Slic3r/XS.pm | 3 - xs/src/libslic3r/BridgeDetector.hpp | 3 + xs/src/libslic3r/Config.hpp | 4 +- xs/src/libslic3r/ExPolygon.hpp | 6 +- xs/src/libslic3r/Extruder.cpp | 32 +- xs/src/libslic3r/Extruder.hpp | 26 +- xs/src/libslic3r/ExtrusionEntity.cpp | 6 +- xs/src/libslic3r/ExtrusionEntity.hpp | 79 +- .../libslic3r/ExtrusionEntityCollection.cpp | 10 +- .../libslic3r/ExtrusionEntityCollection.hpp | 3 + xs/src/libslic3r/Flow.cpp | 36 + xs/src/libslic3r/Flow.hpp | 6 + xs/src/libslic3r/Format/AMF.cpp | 5 +- xs/src/libslic3r/GCode.cpp | 1051 +++++++++++++---- xs/src/libslic3r/GCode.hpp | 173 ++- xs/src/libslic3r/GCode/Analyzer.cpp | 8 +- xs/src/libslic3r/GCode/CoolingBuffer.cpp | 81 +- xs/src/libslic3r/GCode/CoolingBuffer.hpp | 31 +- xs/src/libslic3r/GCode/PressureEqualizer.cpp | 28 +- xs/src/libslic3r/GCode/PressureEqualizer.hpp | 6 +- xs/src/libslic3r/GCode/SpiralVase.cpp | 1 + xs/src/libslic3r/GCode/SpiralVase.hpp | 1 - xs/src/libslic3r/GCodeWriter.cpp | 32 +- xs/src/libslic3r/GCodeWriter.hpp | 48 +- xs/src/libslic3r/PlaceholderParser.cpp | 29 +- xs/src/libslic3r/PlaceholderParser.hpp | 6 +- xs/src/libslic3r/Point.hpp | 2 +- xs/src/libslic3r/Polygon.hpp | 23 +- xs/src/libslic3r/Print.cpp | 87 +- xs/src/libslic3r/Print.hpp | 8 +- xs/src/libslic3r/Slicing.cpp | 12 +- xs/src/libslic3r/Slicing.hpp | 2 +- xs/src/libslic3r/SupportMaterial.cpp | 30 +- xs/src/libslic3r/SurfaceCollection.hpp | 2 +- xs/src/perlglue.cpp | 5 - xs/src/xsinit.h | 5 + xs/xsp/ExtrusionLoop.xsp | 3 - xs/xsp/ExtrusionMultiPath.xsp | 3 - xs/xsp/ExtrusionPath.xsp | 6 +- xs/xsp/GCode.xsp | 212 +--- xs/xsp/GCodePressureEqualizer.xsp | 32 - xs/xsp/GCodeWriter.xsp | 5 +- xs/xsp/Print.xsp | 24 - xs/xsp/my.map | 20 - xs/xsp/typemap.xspt | 15 - 52 files changed, 1362 insertions(+), 1632 deletions(-) delete mode 100644 lib/Slic3r/Print/GCode.pm delete mode 100644 xs/xsp/GCodePressureEqualizer.xsp diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 7ffeb0e66..2de7a35fb 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -71,7 +71,6 @@ use Slic3r::Point; use Slic3r::Polygon; use Slic3r::Polyline; use Slic3r::Print; -use Slic3r::Print::GCode; use Slic3r::Print::Object; use Slic3r::Print::Simple; use Slic3r::Surface; @@ -174,11 +173,8 @@ sub thread_cleanup { # Therefore the Filler instances shall be released at the end of the thread. # *Slic3r::Filler::DESTROY = sub {}; *Slic3r::GCode::DESTROY = sub {}; - *Slic3r::GCode::AvoidCrossingPerimeters::DESTROY = sub {}; - *Slic3r::GCode::OozePrevention::DESTROY = sub {}; *Slic3r::GCode::PlaceholderParser::DESTROY = sub {}; *Slic3r::GCode::Sender::DESTROY = sub {}; - *Slic3r::GCode::Wipe::DESTROY = sub {}; *Slic3r::GCode::Writer::DESTROY = sub {}; *Slic3r::Geometry::BoundingBox::DESTROY = sub {}; *Slic3r::Geometry::BoundingBoxf::DESTROY = sub {}; diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 8a03fbb2b..3245972d3 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -76,34 +76,17 @@ sub export_gcode { { # open output gcode file if we weren't supplied a file-handle - my ($fh, $tempfile); - if ($params{output_fh}) { - $fh = $params{output_fh}; - } else { - $tempfile = "$output_file.tmp"; - Slic3r::open(\$fh, ">", $tempfile) - or die "Failed to open $tempfile for writing\n"; - - # enable UTF-8 output since user might have entered Unicode characters in fields like notes - binmode $fh, ':utf8'; + my $tempfile = "$output_file.tmp"; + my $gcode = Slic3r::GCode->new(); + my $result = $gcode->do_export($self, $tempfile); + die $result . "\n" if ($result ne ''); + my $i; + for ($i = 0; $i < 5; $i += 1) { + last if (rename Slic3r::encode_path($tempfile), Slic3r::encode_path($output_file)); + # Wait for 1/4 seconds and try to rename once again. + select(undef, undef, undef, 0.25); } - - Slic3r::Print::GCode->new( - print => $self, - fh => $fh, - )->export; - - # close our gcode file - close $fh; - if ($tempfile) { - my $i; - for ($i = 0; $i < 5; $i += 1) { - last if (rename Slic3r::encode_path($tempfile), Slic3r::encode_path($output_file)); - # Wait for 1/4 seconds and try to rename once again. - select(undef, undef, undef, 0.25); - } - Slic3r::debugf "Failed to remove the output G-code file from $tempfile to $output_file. Is $tempfile locked?\n" if ($i == 5); - } + Slic3r::debugf "Failed to remove the output G-code file from $tempfile to $output_file. Is $tempfile locked?\n" if ($i == 5); } # run post-processing scripts diff --git a/lib/Slic3r/Print/GCode.pm b/lib/Slic3r/Print/GCode.pm deleted file mode 100644 index 96628de36..000000000 --- a/lib/Slic3r/Print/GCode.pm +++ /dev/null @@ -1,712 +0,0 @@ -package Slic3r::Print::GCode; -use Moo; - -has 'print' => (is => 'ro', required => 1, handles => [qw(objects placeholder_parser config)]); -has 'fh' => (is => 'ro', required => 1); - -has '_gcodegen' => (is => 'rw'); -has '_cooling_buffer' => (is => 'rw'); -has '_spiral_vase' => (is => 'rw'); -has '_pressure_equalizer' => (is => 'rw'); -has '_skirt_done' => (is => 'rw', default => sub { {} }); # print_z => 1 -has '_brim_done' => (is => 'rw'); -# Flag indicating whether the nozzle temperature changes from 1st to 2nd layer were performed. -has '_second_layer_things_done' => (is => 'rw'); -has '_last_obj_copy' => (is => 'rw'); - -use List::Util qw(first sum min max); -use Slic3r::ExtrusionPath ':roles'; -use Slic3r::Flow ':roles'; -use Slic3r::Geometry qw(X Y scale unscale chained_path convex_hull); -use Slic3r::Geometry::Clipper qw(JT_SQUARE union_ex offset); - -sub BUILD { - my ($self) = @_; - - { - # estimate the total number of layer changes - # TODO: only do this when M73 is enabled - my $layer_count; - if ($self->config->complete_objects) { - $layer_count = sum(map { $_->total_layer_count * @{$_->copies} } @{$self->objects}); - } else { - # if sequential printing is not enable, all copies of the same object share the same layer change command(s) - $layer_count = sum(map { $_->total_layer_count } @{$self->objects}); - } - - # set up our helper object: This is a C++ Slic3r::GCode instance. - my $gcodegen = Slic3r::GCode->new; - $self->_gcodegen($gcodegen); - $gcodegen->set_placeholder_parser($self->placeholder_parser); - # Tell the G-code generator, how many times the $gcodegen->change_layer() will be called. - # $gcodegen->change_layer() in turn increments the progress bar status. - $gcodegen->set_layer_count($layer_count); - $gcodegen->set_enable_cooling_markers(1); - $gcodegen->apply_print_config($self->config); - $gcodegen->set_extruders($self->print->extruders); - - # initialize autospeed - { - # get the minimum cross-section used in the print - my @mm3_per_mm = (); - foreach my $object (@{$self->print->objects}) { - foreach my $region_id (0..$#{$self->print->regions}) { - my $region = $self->print->get_region($region_id); - foreach my $layer (@{$object->layers}) { - my $layerm = $layer->get_region($region_id); - if ($region->config->get_abs_value('perimeter_speed') == 0 - || $region->config->get_abs_value('small_perimeter_speed') == 0 - || $region->config->get_abs_value('external_perimeter_speed') == 0 - || $region->config->get_abs_value('bridge_speed') == 0) { - push @mm3_per_mm, $layerm->perimeters->min_mm3_per_mm; - } - if ($region->config->get_abs_value('infill_speed') == 0 - || $region->config->get_abs_value('solid_infill_speed') == 0 - || $region->config->get_abs_value('top_solid_infill_speed') == 0 - || $region->config->get_abs_value('bridge_speed') == 0) { - push @mm3_per_mm, $layerm->fills->min_mm3_per_mm; - } - } - } - if ($object->config->get_abs_value('support_material_speed') == 0 - || $object->config->get_abs_value('support_material_interface_speed') == 0) { - foreach my $layer (@{$object->support_layers}) { - push @mm3_per_mm, $layer->support_fills->min_mm3_per_mm; - } - } - } - # filter out 0-width segments - @mm3_per_mm = grep $_ > 0.000001, @mm3_per_mm; - if (@mm3_per_mm) { - my $min_mm3_per_mm = min(@mm3_per_mm); - # In order to honor max_print_speed we need to find a target volumetric - # speed that we can use throughout the print. So we define this target - # volumetric speed as the volumetric speed produced by printing the - # smallest cross-section at the maximum speed: any larger cross-section - # will need slower feedrates. - my $volumetric_speed = $min_mm3_per_mm * $self->config->max_print_speed; - - # limit such volumetric speed with max_volumetric_speed if set - if ($self->config->max_volumetric_speed > 0) { - $volumetric_speed = min( - $volumetric_speed, - $self->config->max_volumetric_speed, - ); - } - $gcodegen->set_volumetric_speed($volumetric_speed); - } - } - } - - $self->_cooling_buffer(Slic3r::GCode::CoolingBuffer->new($self->_gcodegen)); - - $self->_spiral_vase(Slic3r::GCode::SpiralVase->new($self->config)) - if $self->config->spiral_vase; - - $self->_pressure_equalizer(Slic3r::GCode::PressureEqualizer->new($self->config)) - if ($self->config->max_volumetric_extrusion_rate_slope_positive > 0 || - $self->config->max_volumetric_extrusion_rate_slope_negative > 0); - - $self->_gcodegen->set_enable_extrusion_role_markers(defined $self->_pressure_equalizer); -} - -# Export a G-code for the complete print. -sub export { - my ($self) = @_; - - my $fh = $self->fh; - my $gcodegen = $self->_gcodegen; - - # Write information on the generator. - my @lt = localtime; - printf $fh "; generated by Slic3r $Slic3r::VERSION on %04d-%02d-%02d at %02d:%02d:%02d\n\n", - $lt[5] + 1900, $lt[4]+1, $lt[3], $lt[2], $lt[1], $lt[0]; - # Write notes (content of the Print Settings tab -> Notes) - print $fh "; $_\n" foreach split /\R/, $self->config->notes; - print $fh "\n" if $self->config->notes; - # Write some terse information on the slicing parameters. - my $first_object = $self->objects->[0]; - my $layer_height = $first_object->config->layer_height; - for my $region_id (0..$#{$self->print->regions}) { - my $region = $self->print->regions->[$region_id]; - printf $fh "; external perimeters extrusion width = %.2fmm\n", - $region->flow(FLOW_ROLE_EXTERNAL_PERIMETER, $layer_height, 0, 0, -1, $first_object)->width; - printf $fh "; perimeters extrusion width = %.2fmm\n", - $region->flow(FLOW_ROLE_PERIMETER, $layer_height, 0, 0, -1, $first_object)->width; - printf $fh "; infill extrusion width = %.2fmm\n", - $region->flow(FLOW_ROLE_INFILL, $layer_height, 0, 0, -1, $first_object)->width; - printf $fh "; solid infill extrusion width = %.2fmm\n", - $region->flow(FLOW_ROLE_SOLID_INFILL, $layer_height, 0, 0, -1, $first_object)->width; - printf $fh "; top infill extrusion width = %.2fmm\n", - $region->flow(FLOW_ROLE_TOP_SOLID_INFILL, $layer_height, 0, 0, -1, $first_object)->width; - printf $fh "; support material extrusion width = %.2fmm\n", - $self->objects->[0]->support_material_flow->width - if $self->print->has_support_material; - printf $fh "; first layer extrusion width = %.2fmm\n", - $region->flow(FLOW_ROLE_PERIMETER, $layer_height, 0, 1, -1, $self->objects->[0])->width - if $region->config->first_layer_extrusion_width; - print $fh "\n"; - } - - # prepare the helper object for replacing placeholders in custom G-code and output filename - $self->placeholder_parser->update_timestamp; - - # disable fan - print $fh $gcodegen->writer->set_fan(0, 1) - if $self->config->cooling && $self->config->disable_fan_first_layers; - - # set bed temperature - if ((my $temp = $self->config->first_layer_bed_temperature) && $self->config->start_gcode !~ /M(?:190|140)/i) { - printf $fh $gcodegen->writer->set_bed_temperature($temp, 1); - } - - # set extruder(s) temperature before and after start G-code - $self->_print_first_layer_extruder_temperatures(0); - printf $fh "%s\n", $gcodegen->placeholder_parser->process($self->config->start_gcode); - $self->_print_first_layer_extruder_temperatures(1); - - # set other general things - print $fh $gcodegen->preamble; - - # initialize a motion planner for object-to-object travel moves - if ($self->config->avoid_crossing_perimeters) { - my $distance_from_objects = scale 1; - - # compute the offsetted convex hull for each object and repeat it for each copy. - my @islands_p = (); - foreach my $object (@{$self->objects}) { - # discard objects only containing thin walls (offset would fail on an empty polygon) - my @polygons = map $_->contour, map @{$_->slices}, @{$object->layers}; - next if !@polygons; - - # translate convex hull for each object copy and append it to the islands array - foreach my $copy (@{ $object->_shifted_copies }) { - my @copy_islands_p = map $_->clone, @polygons; - $_->translate(@$copy) for @copy_islands_p; - push @islands_p, @copy_islands_p; - } - } - $gcodegen->avoid_crossing_perimeters->init_external_mp(union_ex(\@islands_p)); - } - - # calculate wiping points if needed - if ($self->config->ooze_prevention) { - my @skirt_points = map @$_, map @$_, @{$self->print->skirt}; - if (@skirt_points) { - my $outer_skirt = convex_hull(\@skirt_points); - my @skirts = (); - foreach my $extruder_id (@{$self->print->extruders}) { - my $extruder_offset = $self->config->get_at('extruder_offset', $extruder_id); - push @skirts, my $s = $outer_skirt->clone; - $s->translate(-scale($extruder_offset->x), -scale($extruder_offset->y)); #) - } - my $convex_hull = convex_hull([ map @$_, @skirts ]); - - $gcodegen->ooze_prevention->set_enable(1); - $gcodegen->ooze_prevention->set_standby_points( - [ map @{$_->equally_spaced_points(scale 10)}, @{offset([$convex_hull], scale 3)} ] - ); - - if (0) { - require "Slic3r/SVG.pm"; - Slic3r::SVG::output( - "ooze_prevention.svg", - red_polygons => \@skirts, - polygons => [$outer_skirt], - points => $gcodegen->ooze_prevention->standby_points, - ); - } - } - } - - # set initial extruder only after custom start G-code - print $fh $gcodegen->set_extruder($self->print->extruders->[0]); - - # do all objects for each layer - if ($self->config->complete_objects) { - # print objects from the smallest to the tallest to avoid collisions - # when moving onto next object starting point - my @obj_idx = sort { $self->objects->[$a]->size->z <=> $self->objects->[$b]->size->z } 0..($self->print->object_count - 1); - - my $finished_objects = 0; - for my $obj_idx (@obj_idx) { - my $object = $self->objects->[$obj_idx]; - for my $copy (@{ $self->objects->[$obj_idx]->_shifted_copies }) { - # move to the origin position for the copy we're going to print. - # this happens before Z goes down to layer 0 again, so that - # no collision happens hopefully. - if ($finished_objects > 0) { - $gcodegen->set_origin(Slic3r::Pointf->new(map unscale $copy->[$_], X,Y)); - $gcodegen->set_enable_cooling_markers(0); # we're not filtering these moves through CoolingBuffer - $gcodegen->avoid_crossing_perimeters->set_use_external_mp_once(1); - print $fh $gcodegen->retract; - print $fh $gcodegen->travel_to( - Slic3r::Point->new(0,0), - EXTR_ROLE_NONE, - 'move to origin position for next object', - ); - $gcodegen->set_enable_cooling_markers(1); - - # disable motion planner when traveling to first object point - $gcodegen->avoid_crossing_perimeters->set_disable_once(1); - } - - # Order layers by print_z, support layers preceding the object layers. - my @layers = sort - { ($a->print_z == $b->print_z) ? ($a->isa('Slic3r::Layer::Support') ? -1 : 0) : $a->print_z <=> $b->print_z } - @{$object->layers}, @{$object->support_layers}; - for my $layer (@layers) { - # if we are printing the bottom layer of an object, and we have already finished - # another one, set first layer temperatures. this happens before the Z move - # is triggered, so machine has more time to reach such temperatures - if ($layer->id == 0 && $finished_objects > 0) { - printf $fh $gcodegen->writer->set_bed_temperature($self->config->first_layer_bed_temperature), - if $self->config->first_layer_bed_temperature; - # Set first layer extruder - $self->_print_first_layer_extruder_temperatures(0); - } - $self->process_layer($layer, [$copy]); - } - $self->flush_filters; - $finished_objects++; - # Flag indicating whether the nozzle temperature changes from 1st to 2nd layer were performed. - # Reset it when starting another object from 1st layer. - $self->_second_layer_things_done(0); - } - } - } else { - # order objects using a nearest neighbor search - my @obj_idx = @{chained_path([ map Slic3r::Point->new(@{$_->_shifted_copies->[0]}), @{$self->objects} ])}; - - # sort layers by Z - # All extrusion moves with the same top layer height are extruded uninterrupted, - # object extrusion moves are performed first, then the support. - my %layers = (); # print_z => [ [layers], [layers], [layers] ] by obj_idx - foreach my $obj_idx (0 .. ($self->print->object_count - 1)) { - my $object = $self->objects->[$obj_idx]; - # Collect the object layers by z, support layers first, object layers second. - foreach my $layer (@{$object->support_layers}, @{$object->layers}) { - $layers{ $layer->print_z } ||= []; - $layers{ $layer->print_z }[$obj_idx] ||= []; - push @{$layers{ $layer->print_z }[$obj_idx]}, $layer; - } - } - - foreach my $print_z (sort { $a <=> $b } keys %layers) { - foreach my $obj_idx (@obj_idx) { - foreach my $layer (@{ $layers{$print_z}[$obj_idx] // [] }) { - $self->process_layer($layer, $layer->object->_shifted_copies); - } - } - } - $self->flush_filters; - } - - # write end commands to file - print $fh $gcodegen->retract; # TODO: process this retract through PressureRegulator in order to discharge fully - print $fh $gcodegen->writer->set_fan(0); - printf $fh "%s\n", $gcodegen->placeholder_parser->process($self->config->end_gcode); - print $fh $gcodegen->writer->update_progress($gcodegen->layer_count, $gcodegen->layer_count, 1); # 100% - print $fh $gcodegen->writer->postamble; - - # get filament stats - $self->print->clear_filament_stats; - $self->print->total_used_filament(0); - $self->print->total_extruded_volume(0); - $self->print->total_weight(0); - $self->print->total_cost(0); - foreach my $extruder (@{$gcodegen->writer->extruders}) { - my $used_filament = $extruder->used_filament; - my $extruded_volume = $extruder->extruded_volume; - my $filament_weight = $extruded_volume * $extruder->filament_density / 1000; - my $filament_cost = $filament_weight * ($extruder->filament_cost / 1000); - $self->print->set_filament_stats($extruder->id, $used_filament); - - printf $fh "; filament used = %.1fmm (%.1fcm3)\n", - $used_filament, $extruded_volume/1000; - if ($filament_weight > 0) { - $self->print->total_weight($self->print->total_weight + $filament_weight); - printf $fh "; filament used = %.1fg\n", - $filament_weight; - if ($filament_cost > 0) { - $self->print->total_cost($self->print->total_cost + $filament_cost); - printf $fh "; filament cost = %.1f\n", - $filament_cost; - } - } - - $self->print->total_used_filament($self->print->total_used_filament + $used_filament); - $self->print->total_extruded_volume($self->print->total_extruded_volume + $extruded_volume); - } - printf $fh "; total filament cost = %.1f\n", - $self->print->total_cost; - - # append full config - print $fh "\n"; - foreach my $config ($self->print->config, $self->print->default_object_config, $self->print->default_region_config) { - foreach my $opt_key (sort @{$config->get_keys}) { - next if $Slic3r::Config::Options->{$opt_key}{shortcut}; - printf $fh "; %s = %s\n", $opt_key, $config->serialize($opt_key); - } - } -} - -# Write 1st layer extruder temperatures into the G-code. -# Only do that if the start G-code does not already contain any M-code controlling an extruder temperature. -# FIXME this does not work correctly for multi-extruder, single heater configuration as it emits multiple preheat commands for the same heater. -# M104 - Set Extruder Temperature -# M109 - Set Extruder Temperature and Wait -sub _print_first_layer_extruder_temperatures { - my ($self, $wait) = @_; - return if $self->config->start_gcode =~ /M(?:109|104)/i; - for my $t (@{$self->print->extruders}) { - my $temp = $self->config->get_at('first_layer_temperature', $t); - $temp += $self->config->standby_temperature_delta if $self->config->ooze_prevention; - printf {$self->fh} $self->_gcodegen->writer->set_temperature($temp, $wait, $t) if $temp > 0; - } -} - -# Called per object's layer. -# First a $gcode string is collected, -# then filtered and finally written to a file $fh. -#FIXME If printing multiple objects at once, this incorrectly applies cooling logic to a single object's layer instead -# of all the objects printed. -sub process_layer { - my $self = shift; - my ($layer, $object_copies) = @_; - my $gcode = ""; - - my $object = $layer->object; - $self->_gcodegen->config->apply_static($object->config); - - # check whether we're going to apply spiralvase logic - if (defined $self->_spiral_vase) { - $self->_spiral_vase->set_enable( - ($layer->id > 0 || $self->print->config->brim_width == 0) - && ($layer->id >= $self->print->config->skirt_height && !$self->print->has_infinite_skirt) - && !defined(first { $_->region->config->bottom_solid_layers > $layer->id } @{$layer->regions}) - && !defined(first { $_->perimeters->items_count > 1 } @{$layer->regions}) - && !defined(first { $_->fills->items_count > 0 } @{$layer->regions}) - ); - } - - # if we're going to apply spiralvase to this layer, disable loop clipping - $self->_gcodegen->set_enable_loop_clipping(!defined $self->_spiral_vase || !$self->_spiral_vase->enable); - - if (!$self->_second_layer_things_done && $layer->id == 1) { - # Transition from 1st to 2nd layer. Adjust nozzle temperatures as prescribed by the nozzle dependent - # first_layer_temperature vs. temperature settings. - for my $extruder (@{$self->_gcodegen->writer->extruders}) { - my $temperature = $self->config->get_at('temperature', $extruder->id); - $gcode .= $self->_gcodegen->writer->set_temperature($temperature, 0, $extruder->id) - if $temperature && $temperature != $self->config->get_at('first_layer_temperature', $extruder->id); - } - $gcode .= $self->_gcodegen->writer->set_bed_temperature($self->print->config->bed_temperature) - if $self->print->config->bed_temperature && $self->print->config->bed_temperature != $self->print->config->first_layer_bed_temperature; - # Mark the temperature transition from 1st to 2nd layer to be finished. - $self->_second_layer_things_done(1); - } - - # set new layer - this will change Z and force a retraction if retract_layer_change is enabled - if ($self->print->config->before_layer_gcode) { - my $pp = $self->_gcodegen->placeholder_parser->clone; - $pp->set('layer_num' => $self->_gcodegen->layer_index + 1); - $pp->set('layer_z' => $layer->print_z); - $gcode .= $pp->process($self->print->config->before_layer_gcode) . "\n"; - } - $gcode .= $self->_gcodegen->change_layer($layer->as_layer); # this will increase $self->_gcodegen->layer_index - if ($self->print->config->layer_gcode) { - my $pp = $self->_gcodegen->placeholder_parser->clone; - $pp->set('layer_num' => $self->_gcodegen->layer_index); - $pp->set('layer_z' => $layer->print_z); - $gcode .= $pp->process($self->print->config->layer_gcode) . "\n"; - } - - # Extrude skirt at the print_z of the raft layers and normal object layers - # not at the print_z of the interlaced support material layers. - #FIXME this will print the support 1st, skirt 2nd and an object 3rd - # if they are at the same print_z, it is not the 1st print layer and the support is printed before object. - if ( - # Not enough skirt layers printed yer - ((values %{$self->_skirt_done}) < $self->print->config->skirt_height || $self->print->has_infinite_skirt) - # This print_z has not been extruded yet - && !$self->_skirt_done->{$layer->print_z} - # and this layer is the 1st layer, or it is an object layer, or it is a raft layer. - && ($layer->id == 0 || !$layer->isa('Slic3r::Layer::Support') || $layer->id < $object->config->raft_layers)) { - $self->_gcodegen->set_origin(Slic3r::Pointf->new(0,0)); - $self->_gcodegen->avoid_crossing_perimeters->set_use_external_mp(1); - my @extruder_ids = map { $_->id } @{$self->_gcodegen->writer->extruders}; - $gcode .= $self->_gcodegen->set_extruder($extruder_ids[0]); - # skip skirt if we have a large brim - if ($layer->id < $self->print->config->skirt_height || $self->print->has_infinite_skirt) { - my $skirt_flow = $self->print->skirt_flow; - - # distribute skirt loops across all extruders - my @skirt_loops = @{$self->print->skirt}; - for my $i (0 .. $#skirt_loops) { - # when printing layers > 0 ignore 'min_skirt_length' and - # just use the 'skirts' setting; also just use the current extruder - last if ($layer->id > 0) && ($i >= $self->print->config->skirts); - my $extruder_id = $extruder_ids[($i/@extruder_ids) % @extruder_ids]; - $gcode .= $self->_gcodegen->set_extruder($extruder_id) - if $layer->id == 0; - - # adjust flow according to this layer's layer height - my $loop = $skirt_loops[$i]->clone; - { - my $layer_skirt_flow = $skirt_flow->clone; - $layer_skirt_flow->set_height($layer->height); - my $mm3_per_mm = $layer_skirt_flow->mm3_per_mm; - foreach my $path (@$loop) { - $path->height($layer->height); - $path->mm3_per_mm($mm3_per_mm); - } - } - - $gcode .= $self->_gcodegen->extrude_loop($loop, 'skirt', $object->config->support_material_speed); - } - } - $self->_skirt_done->{$layer->print_z} = 1; - $self->_gcodegen->avoid_crossing_perimeters->set_use_external_mp(0); - - # allow a straight travel move to the first object point if this is the first layer - # (but don't in next layers) - if ($layer->id == 0) { - $self->_gcodegen->avoid_crossing_perimeters->set_disable_once(1); - } - } - - # extrude brim - if (!$self->_brim_done) { - $gcode .= $self->_gcodegen->set_extruder($self->print->regions->[0]->config->perimeter_extruder-1); - $self->_gcodegen->set_origin(Slic3r::Pointf->new(0,0)); - $self->_gcodegen->avoid_crossing_perimeters->set_use_external_mp(1); - $gcode .= $self->_gcodegen->extrude_loop($_, 'brim', $object->config->support_material_speed) - for @{$self->print->brim}; - $self->_brim_done(1); - $self->_gcodegen->avoid_crossing_perimeters->set_use_external_mp(0); - - # allow a straight travel move to the first object point - $self->_gcodegen->avoid_crossing_perimeters->set_disable_once(1); - } - - for my $copy (@$object_copies) { - # when starting a new object, use the external motion planner for the first travel move - $self->_gcodegen->avoid_crossing_perimeters->set_use_external_mp_once(1) if ($self->_last_obj_copy // '') ne "$copy"; - $self->_last_obj_copy("$copy"); - - $self->_gcodegen->set_origin(Slic3r::Pointf->new(map unscale $copy->[$_], X,Y)); - - # extrude support material before other things because it might use a lower Z - # and also because we avoid travelling on other things when printing it - if ($layer->isa('Slic3r::Layer::Support') && $layer->support_fills->count > 0) { - if ($object->config->support_material_extruder == $object->config->support_material_interface_extruder) { - # Both the support and the support interface are printed with the same extruder, therefore - # the interface may be interleaved with the support base. - # Don't change extruder if the extruder is set to 0. Use the current extruder instead. - $gcode .= $self->_gcodegen->extrude_support( - $layer->support_fills->chained_path_from($self->_gcodegen->last_pos, 0), - $object->config->support_material_extruder); - } else { - # Extrude the support base before support interface for two reasons. - # 1) Support base may be extruded with the current extruder (extruder ID 0) - # and the support interface may be printed with the solube material, - # then one wants to avoid the base being printed with the soluble material. - # 2) It is likely better to print the interface after the base as the interface is - # often printed by bridges and it is convenient to have the base printed already, - # so the bridges may stick to it. - $gcode .= $self->_gcodegen->extrude_support( - $layer->support_fills->chained_path_from( - $self->_gcodegen->last_pos, 0, EXTR_ROLE_SUPPORTMATERIAL), - $object->config->support_material_extruder); - # Extrude the support interface. - $gcode .= $self->_gcodegen->extrude_support( - $layer->support_fills->chained_path_from( - $self->_gcodegen->last_pos, 0, EXTR_ROLE_SUPPORTMATERIAL_INTERFACE), - $object->config->support_material_interface_extruder); - } - } - - # We now define a strategy for building perimeters and fills. The separation - # between regions doesn't matter in terms of printing order, as we follow - # another logic instead: - # - we group all extrusions by extruder so that we minimize toolchanges - # - we start from the last used extruder - # - for each extruder, we group extrusions by island - # - for each island, we extrude perimeters first, unless user set the infill_first - # option - # (Still, we have to keep track of regions because we need to apply their config) - - # group extrusions by extruder and then by island - my %by_extruder = (); # extruder_id => [ { perimeters => \@perimeters, infill => \@infill } ] - - my $n_slices = $#{$layer->slices}; - my @layer_surface_bboxes = (); - for my $i (0 .. $n_slices) { - push @layer_surface_bboxes, $layer->slices->[$i]->contour->bounding_box; - } - my $point_inside_surface = sub { - my ($i, $point) = @_; - my $bbox = $layer_surface_bboxes[$i]; - return - $point->x >= $bbox->x_min && $point->x < $bbox->x_max && - $point->y >= $bbox->y_min && $point->y < $bbox->y_max && - $layer->slices->[$i]->contour->contains_point($point); - }; - - foreach my $region_id (0..($self->print->region_count-1)) { - my $layerm = $layer->regions->[$region_id] or next; - my $region = $self->print->get_region($region_id); - - # process perimeters - { - my $extruder_id = $region->config->perimeter_extruder-1; - foreach my $perimeter_coll (@{$layerm->perimeters}) { - next if $perimeter_coll->empty; # this shouldn't happen but first_point() would fail - - # init by_extruder item only if we actually use the extruder - $by_extruder{$extruder_id} //= []; - - # $perimeter_coll is an ExtrusionPath::Collection object representing a single slice - for my $i (0 .. $n_slices) { - if ( - # $perimeter_coll->first_point does not fit inside any slice - $i == $n_slices - # $perimeter_coll->first_point fits inside ith slice - || $point_inside_surface->($i, $perimeter_coll->first_point)) { - $by_extruder{$extruder_id}[$i] //= { perimeters => {} }; - $by_extruder{$extruder_id}[$i]{perimeters}{$region_id} //= []; - push @{ $by_extruder{$extruder_id}[$i]{perimeters}{$region_id} }, @$perimeter_coll; - last; - } - } - } - } - - # process infill - # $layerm->fills is a collection of Slic3r::ExtrusionPath::Collection objects (C++ class ExtrusionEntityCollection), - # each one containing the ExtrusionPath objects of a certain infill "group" (also called "surface" - # throughout the code). We can redefine the order of such Collections but we have to - # do each one completely at once. - foreach my $fill (@{$layerm->fills}) { - next if $fill->empty; # this shouldn't happen but first_point() would fail - - # init by_extruder item only if we actually use the extruder - my $extruder_id = $fill->[0]->is_solid_infill - ? $region->config->solid_infill_extruder-1 - : $region->config->infill_extruder-1; - - $by_extruder{$extruder_id} //= []; - - # $fill is an ExtrusionPath::Collection object - for my $i (0 .. $n_slices) { - if ($i == $n_slices - || $point_inside_surface->($i, $fill->first_point)) { - $by_extruder{$extruder_id}[$i] //= { infill => {} }; - $by_extruder{$extruder_id}[$i]{infill}{$region_id} //= []; - push @{ $by_extruder{$extruder_id}[$i]{infill}{$region_id} }, $fill; - last; - } - } - } - } # for regions - - # tweak extruder ordering to save toolchanges - my @extruders = sort keys %by_extruder; - if (@extruders > 1) { - my $last_extruder_id = $self->_gcodegen->writer->extruder->id; - if (exists $by_extruder{$last_extruder_id}) { - @extruders = ( - $last_extruder_id, - grep $_ != $last_extruder_id, @extruders, - ); - } - } - - foreach my $extruder_id (@extruders) { - $gcode .= $self->_gcodegen->set_extruder($extruder_id); - foreach my $island (@{ $by_extruder{$extruder_id} }) { - if ($self->print->config->infill_first) { - $gcode .= $self->_extrude_infill($island->{infill} // {}); - $gcode .= $self->_extrude_perimeters($island->{perimeters} // {}); - } else { - $gcode .= $self->_extrude_perimeters($island->{perimeters} // {}); - $gcode .= $self->_extrude_infill($island->{infill} // {}); - } - } - } - } # for object copies - - # apply spiral vase post-processing if this layer contains suitable geometry - # (we must feed all the G-code into the post-processor, including the first - # bottom non-spiral layers otherwise it will mess with positions) - # we apply spiral vase at this stage because it requires a full layer - $gcode = $self->_spiral_vase->process_layer($gcode) - if defined $self->_spiral_vase; - - # apply cooling logic; this may alter speeds - $gcode = $self->_cooling_buffer->append( - $gcode, - $layer->object->ptr . ref($layer), # differentiate $obj_id between normal layers and support layers - $layer->id, - $layer->print_z, - ) if defined $self->_cooling_buffer; - - $gcode = $self->filter($gcode); - print {$self->fh} $gcode if defined($gcode); -} - -# Extrude perimeters: Decide where to put seams (hide or align seams). -sub _extrude_perimeters { - my ($self, $entities_by_region) = @_; - - my $gcode = ""; - foreach my $region_id (sort keys %$entities_by_region) { - $self->_gcodegen->config->apply_static($self->print->get_region($region_id)->config); - $gcode .= $self->_gcodegen->extrude($_, 'perimeter', -1) - for @{ $entities_by_region->{$region_id} }; - } - return $gcode; -} - -# Chain the paths hierarchically by a greedy algorithm to minimize a travel distance. -sub _extrude_infill { - my ($self, $entities_by_region) = @_; - - my $gcode = ""; - foreach my $region_id (sort keys %$entities_by_region) { - $self->_gcodegen->config->apply_static($self->print->get_region($region_id)->config); - - my $collection = Slic3r::ExtrusionPath::Collection->new(@{ $entities_by_region->{$region_id} }); - for my $fill (@{$collection->chained_path_from($self->_gcodegen->last_pos, 0)}) { - if ($fill->isa('Slic3r::ExtrusionPath::Collection')) { - $gcode .= $self->_gcodegen->extrude($_, 'infill', -1) - for @{$fill->chained_path_from($self->_gcodegen->last_pos, 0)}; - } else { - $gcode .= $self->_gcodegen->extrude($fill, 'infill', -1) ; - } - } - } - return $gcode; -} - -sub flush_filters { - my ($self) = @_; - - print {$self->fh} $self->filter($self->_cooling_buffer->flush, 1); -} - -sub filter { - my ($self, $gcode, $flush) = @_; - $flush //= 0; - - # apply pressure equalization if enabled; -# print "G-code before filter:\n", $gcode; - $gcode = $self->_pressure_equalizer->process($gcode, $flush) - if defined $self->_pressure_equalizer; -# print "G-code after filter:\n", $gcode; - - return $gcode; -} - -1; diff --git a/lib/Slic3r/Test.pm b/lib/Slic3r/Test.pm index 31e53a4a0..2eb619eb4 100644 --- a/lib/Slic3r/Test.pm +++ b/lib/Slic3r/Test.pm @@ -1,6 +1,7 @@ package Slic3r::Test; use strict; use warnings; +use Cwd 'abs_path'; require Exporter; our @ISA = qw(Exporter); @@ -202,10 +203,19 @@ sub gcode { $print = $print->print if $print->isa('Slic3r::Test::Print'); - my $fh = IO::Scalar->new(\my $gcode); + # Write the resulting G-code into a temporary file. + my $gcode_temp_path = abs_path($0) . '.gcode.temp'; $print->process; - $print->export_gcode(output_fh => $fh, quiet => 1); - $fh->close; + $print->export_gcode(output_file => $gcode_temp_path, quiet => 1); + # Read the temoprary G-code file. + my $gcode; + { + local $/; + open my $fh, '<', $gcode_temp_path or die "Test.pm: can't open $gcode_temp_path: $!"; + $gcode = <$fh>; + } + # Remove the temp file. + unlink $gcode_temp_path; return $gcode; } diff --git a/slic3r.pl b/slic3r.pl index 9caffce8a..060f69016 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -304,8 +304,6 @@ $j --use-relative-e-distances Enable this to get relative E values (default: no) --use-firmware-retraction Enable firmware-controlled retraction using G10/G11 (default: no) --use-volumetric-e Express E in cubic millimeters and prepend M200 (default: no) - --gcode-arcs Use G2/G3 commands for native arcs (experimental, not supported - by all firmwares) --gcode-comments Make G-code verbose by adding comments (default: no) Filament options: diff --git a/t/cooling.t b/t/cooling.t index aa9936276..2324e86c1 100644 --- a/t/cooling.t +++ b/t/cooling.t @@ -32,7 +32,7 @@ $config->set('disable_fan_first_layers', 0); { my $buffer = buffer($config); $buffer->gcodegen->set_elapsed_time($buffer->gcodegen->config->slowdown_below_layer_time + 1); - my $gcode = $buffer->append('G1 F3000;_EXTRUDE_SET_SPEED\nG1 X100 E1', 0, 0, 0.4) . $buffer->flush; + my $gcode = $buffer->append('G1 F3000;_EXTRUDE_SET_SPEED\nG1 X100 E1', 0, 0, 0) . $buffer->flush; like $gcode, qr/F3000/, 'speed is not altered when elapsed time is greater than slowdown threshold'; } @@ -44,7 +44,7 @@ $config->set('disable_fan_first_layers', 0); "G1 F3000;_EXTRUDE_SET_SPEED\n" . "G1 X100 E1\n" . "G1 E4 F400", - 0, 0, 0.4 + 0, 0, 0 ) . $buffer->flush; unlike $gcode, qr/F3000/, 'speed is altered when elapsed time is lower than slowdown threshold'; like $gcode, qr/F2500/, 'speed is not altered for travel moves'; @@ -54,7 +54,7 @@ $config->set('disable_fan_first_layers', 0); { my $buffer = buffer($config); $buffer->gcodegen->set_elapsed_time($buffer->gcodegen->config->fan_below_layer_time + 1); - my $gcode = $buffer->append('G1 X100 E1 F3000', 0, 0, 0.4) . $buffer->flush; + my $gcode = $buffer->append('G1 X100 E1 F3000', 0, 0, 0) . $buffer->flush; unlike $gcode, qr/M106/, 'fan is not activated when elapsed time is greater than fan threshold'; } @@ -64,7 +64,7 @@ $config->set('disable_fan_first_layers', 0); for my $obj_id (0 .. 1) { # use an elapsed time which is < the slowdown threshold but greater than it when summed twice $buffer->gcodegen->set_elapsed_time($buffer->gcodegen->config->slowdown_below_layer_time - 1); - $gcode .= $buffer->append("G1 X100 E1 F3000\n", $obj_id, 0, 0.4); + $gcode .= $buffer->append("G1 X100 E1 F3000\n", $obj_id, 0, 0); } $gcode .= $buffer->flush; like $gcode, qr/F3000/, 'slowdown is computed on all objects printing at same Z'; @@ -77,7 +77,7 @@ $config->set('disable_fan_first_layers', 0); for my $obj_id (0 .. 1) { # use an elapsed time which is < the threshold but greater than it when summed twice $buffer->gcodegen->set_elapsed_time($buffer->gcodegen->config->fan_below_layer_time - 1); - $gcode .= $buffer->append("G1 X100 E1 F3000\n", $obj_id, $layer_id, 0.4 + 0.4*$layer_id + 0.1*$obj_id); # print same layer at distinct heights + $gcode .= $buffer->append("G1 X100 E1 F3000\n", $obj_id, $layer_id, 0); } } $gcode .= $buffer->flush; @@ -91,7 +91,7 @@ $config->set('disable_fan_first_layers', 0); for my $obj_id (0 .. 1) { # use an elapsed time which is < the threshold even when summed twice $buffer->gcodegen->set_elapsed_time($buffer->gcodegen->config->fan_below_layer_time/2 - 1); - $gcode .= $buffer->append("G1 X100 E1 F3000\n", $obj_id, $layer_id, 0.4 + 0.4*$layer_id + 0.1*$obj_id); # print same layer at distinct heights + $gcode .= $buffer->append("G1 X100 E1 F3000\n", $obj_id, $layer_id, 0); } } $gcode .= $buffer->flush; diff --git a/xs/MANIFEST b/xs/MANIFEST index 52bf72263..aa83d43cf 100644 --- a/xs/MANIFEST +++ b/xs/MANIFEST @@ -514,7 +514,6 @@ xsp/Flow.xsp xsp/GCode.xsp xsp/GCodeSender.xsp xsp/GCodeWriter.xsp -xsp/GCodePressureEqualizer.xsp xsp/Geometry.xsp xsp/GUI.xsp xsp/GUI_3DScene.xsp diff --git a/xs/lib/Slic3r/XS.pm b/xs/lib/Slic3r/XS.pm index ea0d12be6..c2af3fd73 100644 --- a/xs/lib/Slic3r/XS.pm +++ b/xs/lib/Slic3r/XS.pm @@ -278,10 +278,7 @@ for my $class (qw( Slic3r::Filler Slic3r::Flow Slic3r::GCode - Slic3r::GCode::AvoidCrossingPerimeters - Slic3r::GCode::OozePrevention Slic3r::GCode::PlaceholderParser - Slic3r::GCode::Wipe Slic3r::GCode::Writer Slic3r::Geometry::BoundingBox Slic3r::Geometry::BoundingBoxf diff --git a/xs/src/libslic3r/BridgeDetector.hpp b/xs/src/libslic3r/BridgeDetector.hpp index b04db63e4..8dc93336a 100644 --- a/xs/src/libslic3r/BridgeDetector.hpp +++ b/xs/src/libslic3r/BridgeDetector.hpp @@ -37,6 +37,9 @@ public: Polylines unsupported_edges(double angle = -1) const; private: + // Suppress warning "assignment operator could not be generated" + BridgeDetector& operator=(const BridgeDetector &); + void initialize(); struct BridgeDirection { diff --git a/xs/src/libslic3r/Config.hpp b/xs/src/libslic3r/Config.hpp index 6c8fa9f27..afb0671a5 100644 --- a/xs/src/libslic3r/Config.hpp +++ b/xs/src/libslic3r/Config.hpp @@ -34,7 +34,7 @@ class ConfigOption { virtual int getInt() const { return 0; }; virtual double getFloat() const { return 0; }; virtual bool getBool() const { return false; }; - virtual void setInt(int val) {}; + virtual void setInt(int /* val */) { }; friend bool operator== (const ConfigOption &a, const ConfigOption &b); friend bool operator!= (const ConfigOption &a, const ConfigOption &b); }; @@ -142,7 +142,7 @@ class ConfigOptionInt : public ConfigOptionSingle { public: ConfigOptionInt() : ConfigOptionSingle(0) {}; - ConfigOptionInt(double _value) : ConfigOptionSingle(_value) {}; + ConfigOptionInt(double _value) : ConfigOptionSingle(int(floor(_value + 0.5))) {}; int getInt() const { return this->value; }; void setInt(int val) { this->value = val; }; diff --git a/xs/src/libslic3r/ExPolygon.hpp b/xs/src/libslic3r/ExPolygon.hpp index 509d2eb93..af34694b7 100644 --- a/xs/src/libslic3r/ExPolygon.hpp +++ b/xs/src/libslic3r/ExPolygon.hpp @@ -326,7 +326,7 @@ namespace boost { namespace polygon { } // Get the winding direction of the polygon - static inline winding_direction winding(const Slic3r::ExPolygon& t) { + static inline winding_direction winding(const Slic3r::ExPolygon& /* t */) { return unknown_winding; } }; @@ -391,8 +391,8 @@ namespace boost { namespace polygon { } //don't worry about these, just return false from them - static inline bool clean(const Slic3r::ExPolygons& polygon_set) { return false; } - static inline bool sorted(const Slic3r::ExPolygons& polygon_set) { return false; } + static inline bool clean(const Slic3r::ExPolygons& /* polygon_set */) { return false; } + static inline bool sorted(const Slic3r::ExPolygons& /* polygon_set */) { return false; } }; template <> diff --git a/xs/src/libslic3r/Extruder.cpp b/xs/src/libslic3r/Extruder.cpp index 8cf3e8b0a..31523cb48 100644 --- a/xs/src/libslic3r/Extruder.cpp +++ b/xs/src/libslic3r/Extruder.cpp @@ -4,12 +4,12 @@ namespace Slic3r { Extruder::Extruder(unsigned int id, GCodeConfig *config) : id(id), - config(config) + m_config(config) { reset(); // cache values that are going to be called often - if (config->use_volumetric_e) { + if (m_config->use_volumetric_e) { this->e_per_mm3 = this->extrusion_multiplier(); } else { this->e_per_mm3 = this->extrusion_multiplier() @@ -31,7 +31,7 @@ double Extruder::extrude(double dE) { // in case of relative E distances we always reset to 0 before any output - if (this->config->use_relative_e_distances) + if (m_config->use_relative_e_distances) this->E = 0; this->E += dE; @@ -50,7 +50,7 @@ double Extruder::retract(double length, double restart_extra) { // in case of relative E distances we always reset to 0 before any output - if (this->config->use_relative_e_distances) + if (m_config->use_relative_e_distances) this->E = 0; double to_retract = length - this->retracted; @@ -84,7 +84,7 @@ Extruder::e_per_mm(double mm3_per_mm) const double Extruder::extruded_volume() const { - if (this->config->use_volumetric_e) { + if (m_config->use_volumetric_e) { // Any current amount of retraction should not affect used filament, since // it represents empty volume in the nozzle. We add it back to E. return this->absolute_E + this->retracted; @@ -96,7 +96,7 @@ Extruder::extruded_volume() const double Extruder::used_filament() const { - if (this->config->use_volumetric_e) { + if (m_config->use_volumetric_e) { return this->extruded_volume() / (this->filament_diameter() * this->filament_diameter() * PI/4); } @@ -108,61 +108,61 @@ Extruder::used_filament() const double Extruder::filament_diameter() const { - return this->config->filament_diameter.get_at(this->id); + return m_config->filament_diameter.get_at(this->id); } double Extruder::filament_density() const { - return this->config->filament_density.get_at(this->id); + return m_config->filament_density.get_at(this->id); } double Extruder::filament_cost() const { - return this->config->filament_cost.get_at(this->id); + return m_config->filament_cost.get_at(this->id); } double Extruder::extrusion_multiplier() const { - return this->config->extrusion_multiplier.get_at(this->id); + return m_config->extrusion_multiplier.get_at(this->id); } double Extruder::retract_length() const { - return this->config->retract_length.get_at(this->id); + return m_config->retract_length.get_at(this->id); } double Extruder::retract_lift() const { - return this->config->retract_lift.get_at(this->id); + return m_config->retract_lift.get_at(this->id); } int Extruder::retract_speed() const { - return this->config->retract_speed.get_at(this->id); + return m_config->retract_speed.get_at(this->id); } double Extruder::retract_restart_extra() const { - return this->config->retract_restart_extra.get_at(this->id); + return m_config->retract_restart_extra.get_at(this->id); } double Extruder::retract_length_toolchange() const { - return this->config->retract_length_toolchange.get_at(this->id); + return m_config->retract_length_toolchange.get_at(this->id); } double Extruder::retract_restart_extra_toolchange() const { - return this->config->retract_restart_extra_toolchange.get_at(this->id); + return m_config->retract_restart_extra_toolchange.get_at(this->id); } } diff --git a/xs/src/libslic3r/Extruder.hpp b/xs/src/libslic3r/Extruder.hpp index 729996cd1..813623772 100644 --- a/xs/src/libslic3r/Extruder.hpp +++ b/xs/src/libslic3r/Extruder.hpp @@ -9,7 +9,7 @@ namespace Slic3r { class Extruder { - public: +public: unsigned int id; double E; double absolute_E; @@ -17,10 +17,11 @@ class Extruder double restart_extra; double e_per_mm3; double retract_speed_mm_min; - + Extruder(unsigned int id, GCodeConfig *config); virtual ~Extruder() {} - void reset(); + + void reset(); double extrude(double dE); double retract(double length, double restart_extra); double unretract(); @@ -34,15 +35,26 @@ class Extruder double extrusion_multiplier() const; double retract_length() const; double retract_lift() const; - int retract_speed() const; + int retract_speed() const; double retract_restart_extra() const; double retract_length_toolchange() const; double retract_restart_extra_toolchange() const; - - private: - GCodeConfig *config; + + static Extruder key(unsigned int id) { return Extruder(id); } + +private: + // Private constructor to create a key for a search in std::set. + Extruder(unsigned int id) : id(id) {} + + GCodeConfig *m_config; }; +// Sort Extruder objects by the extruder id by default. +inline bool operator==(const Extruder &e1, const Extruder &e2) { return e1.id == e2.id; } +inline bool operator!=(const Extruder &e1, const Extruder &e2) { return e1.id != e2.id; } +inline bool operator< (const Extruder &e1, const Extruder &e2) { return e1.id < e2.id; } +inline bool operator> (const Extruder &e1, const Extruder &e2) { return e1.id > e2.id; } + } #endif diff --git a/xs/src/libslic3r/ExtrusionEntity.cpp b/xs/src/libslic3r/ExtrusionEntity.cpp index 5ed56d17b..c6f67b169 100644 --- a/xs/src/libslic3r/ExtrusionEntity.cpp +++ b/xs/src/libslic3r/ExtrusionEntity.cpp @@ -59,7 +59,7 @@ void ExtrusionPath::polygons_covered_by_spacing(Polygons &out, const float scale { // 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. - Flow flow(this->width, this->height, 0.f, this->is_bridge()); + Flow flow(this->width, this->height, 0.f, is_bridge(this->role())); polygons_append(out, offset(this->polyline, 0.5f * float(flow.scaled_spacing()) + scaled_epsilon)); } @@ -226,7 +226,7 @@ void ExtrusionLoop::split_at(const Point &point, bool prefer_non_overhang) min = dist; path_idx = path - this->paths.begin(); } - if (prefer_non_overhang && ! path->is_bridge() && dist < min_non_overhang) { + if (prefer_non_overhang && ! is_bridge(path->role()) && dist < min_non_overhang) { p_non_overhang = p_tmp; min_non_overhang = dist; path_idx_non_overhang = path - this->paths.begin(); @@ -291,7 +291,7 @@ ExtrusionLoop::has_overhang_point(const Point &point) const 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 (is_bridge(path->role()) && pos > 0 && pos != (int)(path->polyline.points.size())-1); } } return false; diff --git a/xs/src/libslic3r/ExtrusionEntity.hpp b/xs/src/libslic3r/ExtrusionEntity.hpp index dc0dcdd34..17b0b07f9 100644 --- a/xs/src/libslic3r/ExtrusionEntity.hpp +++ b/xs/src/libslic3r/ExtrusionEntity.hpp @@ -29,6 +29,33 @@ enum ExtrusionRole { erMixed, }; +inline bool is_perimeter(ExtrusionRole role) +{ + return role == erPerimeter + || role == erExternalPerimeter + || role == erOverhangPerimeter; +} + +inline bool is_infill(ExtrusionRole role) +{ + return role == erBridgeInfill + || role == erInternalInfill + || role == erSolidInfill + || role == erTopSolidInfill; +} + +inline bool is_solid_infill(ExtrusionRole role) +{ + return role == erBridgeInfill + || role == erSolidInfill + || role == erTopSolidInfill; +} + +inline bool is_bridge(ExtrusionRole role) { + return role == erBridgeInfill + || role == erOverhangPerimeter; +} + /* Special flags describing loop */ enum ExtrusionLoopRole { elrDefault, @@ -101,26 +128,6 @@ public: void simplify(double tolerance); virtual double length() const; virtual ExtrusionRole role() const { return m_role; } - bool is_perimeter() const { - return this->m_role == erPerimeter - || this->m_role == erExternalPerimeter - || this->m_role == erOverhangPerimeter; - } - bool is_infill() const { - return this->m_role == erBridgeInfill - || this->m_role == erInternalInfill - || this->m_role == erSolidInfill - || this->m_role == erTopSolidInfill; - } - bool is_solid_infill() const { - return this->m_role == erBridgeInfill - || this->m_role == erSolidInfill - || this->m_role == erTopSolidInfill; - } - bool is_bridge() const { - return this->m_role == erBridgeInfill - || this->m_role == erOverhangPerimeter; - } // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width. // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps. void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const; @@ -167,22 +174,6 @@ public: Point last_point() const { return this->paths.back().polyline.points.back(); } virtual double length() const; virtual ExtrusionRole role() const { return this->paths.empty() ? erNone : this->paths.front().role(); } - bool is_perimeter() const { - return this->paths.front().role() == erPerimeter - || this->paths.front().role() == erExternalPerimeter - || this->paths.front().role() == erOverhangPerimeter; - } - bool 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 is_solid_infill() const { - return this->paths.front().role() == erBridgeInfill - || this->paths.front().role() == erSolidInfill - || this->paths.front().role() == erTopSolidInfill; - } // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width. // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps. void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const; @@ -230,22 +221,6 @@ public: bool has_overhang_point(const Point &point) const; virtual ExtrusionRole role() const { return this->paths.empty() ? erNone : this->paths.front().role(); } ExtrusionLoopRole loop_role() const { return m_loop_role; } - bool is_perimeter() const { - return this->paths.front().role() == erPerimeter - || this->paths.front().role() == erExternalPerimeter - || this->paths.front().role() == erOverhangPerimeter; - } - bool 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 is_solid_infill() const { - return this->paths.front().role() == erBridgeInfill - || this->paths.front().role() == erSolidInfill - || this->paths.front().role() == erTopSolidInfill; - } // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width. // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps. void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const; diff --git a/xs/src/libslic3r/ExtrusionEntityCollection.cpp b/xs/src/libslic3r/ExtrusionEntityCollection.cpp index 93dd8dfb8..4513139e2 100644 --- a/xs/src/libslic3r/ExtrusionEntityCollection.cpp +++ b/xs/src/libslic3r/ExtrusionEntityCollection.cpp @@ -95,8 +95,14 @@ ExtrusionEntityCollection::chained_path(ExtrusionEntityCollection* retval, bool this->chained_path_from(this->entities.front()->first_point(), retval, no_reverse, role, orig_indices); } -void -ExtrusionEntityCollection::chained_path_from(Point start_near, ExtrusionEntityCollection* retval, bool no_reverse, ExtrusionRole role, std::vector* orig_indices) const +ExtrusionEntityCollection ExtrusionEntityCollection::chained_path_from(Point start_near, bool no_reverse, ExtrusionRole role) const +{ + ExtrusionEntityCollection coll; + this->chained_path_from(start_near, &coll, no_reverse, role); + return coll; +} + +void ExtrusionEntityCollection::chained_path_from(Point start_near, ExtrusionEntityCollection* retval, bool no_reverse, ExtrusionRole role, std::vector* orig_indices) const { if (this->no_sort) { *retval = *this; diff --git a/xs/src/libslic3r/ExtrusionEntityCollection.hpp b/xs/src/libslic3r/ExtrusionEntityCollection.hpp index 07e206214..03bd2ba97 100644 --- a/xs/src/libslic3r/ExtrusionEntityCollection.hpp +++ b/xs/src/libslic3r/ExtrusionEntityCollection.hpp @@ -59,6 +59,7 @@ public: void remove(size_t i); ExtrusionEntityCollection chained_path(bool no_reverse = false, ExtrusionRole role = erMixed) const; void chained_path(ExtrusionEntityCollection* retval, bool no_reverse = false, ExtrusionRole role = erMixed, std::vector* orig_indices = nullptr) const; + ExtrusionEntityCollection chained_path_from(Point start_near, bool no_reverse = false, ExtrusionRole role = erMixed) const; void chained_path_from(Point start_near, ExtrusionEntityCollection* retval, bool no_reverse = false, ExtrusionRole role = erMixed, std::vector* orig_indices = nullptr) const; void reverse(); Point first_point() const { return this->entities.front()->first_point(); } @@ -78,6 +79,8 @@ public: void flatten(ExtrusionEntityCollection* retval) const; ExtrusionEntityCollection flatten() const; double min_mm3_per_mm() const; + + // Following methods shall never be called on an ExtrusionEntityCollection. Polyline as_polyline() const { CONFESS("Calling as_polyline() on a ExtrusionEntityCollection"); return Polyline(); diff --git a/xs/src/libslic3r/Flow.cpp b/xs/src/libslic3r/Flow.cpp index c7e5f8495..d3414da57 100644 --- a/xs/src/libslic3r/Flow.cpp +++ b/xs/src/libslic3r/Flow.cpp @@ -1,4 +1,5 @@ #include "Flow.hpp" +#include "Print.hpp" #include #include @@ -113,4 +114,39 @@ float Flow::_width_from_spacing(float spacing, float nozzle_diameter, float heig #endif } +Flow support_material_flow(const PrintObject *object, float layer_height) +{ + return Flow::new_from_config_width( + frSupportMaterial, + // The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution. + (object->config.support_material_extrusion_width.value > 0) ? object->config.support_material_extrusion_width : object->config.extrusion_width, + // if object->config.support_material_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component. + float(object->print()->config.nozzle_diameter.get_at(object->config.support_material_extruder-1)), + (layer_height > 0.f) ? layer_height : float(object->config.layer_height.value), + false); +} + +Flow support_material_1st_layer_flow(const PrintObject *object, float layer_height) +{ + return Flow::new_from_config_width( + frSupportMaterial, + // The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution. + (object->print()->config.first_layer_extrusion_width.value > 0) ? object->print()->config.first_layer_extrusion_width : object->config.support_material_extrusion_width, + float(object->print()->config.nozzle_diameter.get_at(object->config.support_material_extruder-1)), + (layer_height > 0.f) ? layer_height : object->config.first_layer_height.get_abs_value(object->config.layer_height.value), + false); +} + +Flow support_material_interface_flow(const PrintObject *object, float layer_height) +{ + return Flow::new_from_config_width( + frSupportMaterialInterface, + // The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution. + (object->config.support_material_extrusion_width > 0) ? object->config.support_material_extrusion_width : object->config.extrusion_width, + // if object->config.support_material_interface_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component. + float(object->print()->config.nozzle_diameter.get_at(object->config.support_material_interface_extruder-1)), + (layer_height > 0.f) ? layer_height : float(object->config.layer_height.value), + false); +} + } diff --git a/xs/src/libslic3r/Flow.hpp b/xs/src/libslic3r/Flow.hpp index 118273067..a01070c45 100644 --- a/xs/src/libslic3r/Flow.hpp +++ b/xs/src/libslic3r/Flow.hpp @@ -7,6 +7,8 @@ namespace Slic3r { +class PrintObject; + // Extra spacing of bridge threads, in mm. #define BRIDGE_EXTRA_SPACING 0.05 @@ -59,6 +61,10 @@ public: static float _spacing(float width, float nozzle_diameter, float height, float bridge_flow_ratio); }; +extern Flow support_material_flow(const PrintObject *object, float layer_height = 0.f); +extern Flow support_material_1st_layer_flow(const PrintObject *object, float layer_height = 0.f); +extern Flow support_material_interface_flow(const PrintObject *object, float layer_height = 0.f); + } #endif diff --git a/xs/src/libslic3r/Format/AMF.cpp b/xs/src/libslic3r/Format/AMF.cpp index 7e5e90597..9a9047eac 100644 --- a/xs/src/libslic3r/Format/AMF.cpp +++ b/xs/src/libslic3r/Format/AMF.cpp @@ -147,6 +147,9 @@ struct AMFParserContext Instance *m_instance; // Generic string buffer for vertices, face indices, metadata etc. std::string m_value[3]; + +private: + AMFParserContext& operator=(AMFParserContext&); }; void AMFParserContext::startElement(const char *name, const char **atts) @@ -307,7 +310,7 @@ void AMFParserContext::characters(const XML_Char *s, int len) } } -void AMFParserContext::endElement(const char *name) +void AMFParserContext::endElement(const char * /* name */) { switch (m_path.back()) { diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index 0c66422d5..41459e296 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -1,10 +1,17 @@ #include "GCode.hpp" #include "ExtrusionEntity.hpp" #include "EdgeGrid.hpp" +#include "Geometry.hpp" + #include #include #include +#include +#include +#include +#include + #include "SVG.hpp" #if 0 @@ -57,7 +64,7 @@ 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(); @@ -78,15 +85,14 @@ AvoidCrossingPerimeters::travel_to(GCode &gcodegen, Point point) } } -std::string -OozePrevention::pre_toolchange(GCode &gcodegen) +std::string OozePrevention::pre_toolchange(GCode &gcodegen) { std::string gcode; // move to the nearest standby point if (!this->standby_points.empty()) { // get current position in print coordinates - Pointf3 writer_pos = gcodegen.writer.get_position(); + Pointf3 writer_pos = gcodegen.writer().get_position(); Point pos = Point::new_scale(writer_pos.x, writer_pos.y); // find standby point @@ -96,14 +102,14 @@ OozePrevention::pre_toolchange(GCode &gcodegen) /* We don't call gcodegen.travel_to() because we don't need retraction (it was already triggered by the caller) nor avoid_crossing_perimeters and also because the coordinates of the destination point must not be transformed by origin nor current extruder offset. */ - gcode += gcodegen.writer.travel_to_xy(Pointf::new_unscale(standby_point), + gcode += gcodegen.writer().travel_to_xy(Pointf::new_unscale(standby_point), "move to standby position"); } - if (gcodegen.config.standby_temperature_delta.value != 0) { + if (gcodegen.config().standby_temperature_delta.value != 0) { // we assume that heating is always slower than cooling, so no need to block - gcode += gcodegen.writer.set_temperature - (this->_get_temp(gcodegen) + gcodegen.config.standby_temperature_delta.value, false); + gcode += gcodegen.writer().set_temperature + (this->_get_temp(gcodegen) + gcodegen.config().standby_temperature_delta.value, false); } return gcode; @@ -111,17 +117,17 @@ OozePrevention::pre_toolchange(GCode &gcodegen) std::string OozePrevention::post_toolchange(GCode &gcodegen) { - return (gcodegen.config.standby_temperature_delta.value != 0) ? - gcodegen.writer.set_temperature(this->_get_temp(gcodegen), true) : + return (gcodegen.config().standby_temperature_delta.value != 0) ? + gcodegen.writer().set_temperature(this->_get_temp(gcodegen), true) : std::string(); } int OozePrevention::_get_temp(GCode &gcodegen) { - return (gcodegen.layer != NULL && gcodegen.layer->id() == 0) - ? gcodegen.config.first_layer_temperature.get_at(gcodegen.writer.extruder()->id) - : gcodegen.config.temperature.get_at(gcodegen.writer.extruder()->id); + return (gcodegen.layer() != NULL && gcodegen.layer()->id() == 0) + ? gcodegen.config().first_layer_temperature.get_at(gcodegen.writer().extruder()->id) + : gcodegen.config().temperature.get_at(gcodegen.writer().extruder()->id); } std::string @@ -131,18 +137,18 @@ Wipe::wipe(GCode &gcodegen, bool toolchange) /* 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; + 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(); + ? 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); + 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). */ @@ -165,15 +171,15 @@ Wipe::wipe(GCode &gcodegen, bool toolchange) double dE = length * (segment_length / wipe_dist) * 0.95; //FIXME one shall not generate the unnecessary G1 Fxxx commands, here wipe_speed is a constant inside this cycle. // Is it here for the cooling markers? Or should it be outside of the cycle? - gcode += gcodegen.writer.set_speed(wipe_speed*60, "", gcodegen.enable_cooling_markers ? ";_WIPE" : ""); - gcode += gcodegen.writer.extrude_to_xy( + gcode += gcodegen.writer().set_speed(wipe_speed*60, "", gcodegen.enable_cooling_markers() ? ";_WIPE" : ""); + gcode += gcodegen.writer().extrude_to_xy( gcodegen.point_to_gcode(line->b), -dE, "wipe and retract" ); retracted += dE; } - gcodegen.writer.extruder()->retracted += retracted; + gcodegen.writer().extruder()->retracted += retracted; // prevent wiping again on same path this->reset_path(); @@ -182,132 +188,720 @@ Wipe::wipe(GCode &gcodegen, bool toolchange) return gcode; } -#define EXTRUDER_CONFIG(OPT) this->config.OPT.get_at(this->writer.extruder()->id) +#define EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(m_writer.extruder()->id) -GCode::GCode() - : placeholder_parser(NULL), enable_loop_clipping(true), - enable_cooling_markers(false), enable_extrusion_role_markers(false), enable_analyzer_markers(false), - layer_count(0), - layer_index(-1), layer(NULL), first_layer(false), elapsed_time(0.0), volumetric_speed(0), - _last_pos_defined(false), - _lower_layer_edge_grid(NULL), - _last_extrusion_role(erNone) +inline void write(FILE *file, const std::string &what) { + fwrite(what.data(), 1, what.size(), file); } -GCode::~GCode() +inline void writeln(FILE *file, const std::string &what) { - delete _lower_layer_edge_grid; - _lower_layer_edge_grid = NULL; + write(file, what); + fprintf(file, "\n"); } -const Point& -GCode::last_pos() const +bool GCode::do_export(FILE *file, Print &print) { - return this->_last_pos; + // How many times will be change_layer() called? + // change_layer() in turn increments the progress bar status. + m_layer_count = 0; + for (auto object : print.objects) + // if sequential printing is not enable, all copies of the same object share the same layer change command(s) + m_layer_count += (unsigned int)((print.config.complete_objects.value ? object->copies().size() : 1) * object->total_layer_count()); + + m_enable_cooling_markers = true; + this->apply_print_config(print.config); + this->set_extruders(print.extruders()); + + // Initialize autospeed. + { + // get the minimum cross-section used in the print + std::vector mm3_per_mm; + for (auto object : print.objects) { + for (size_t region_id = 0; region_id < print.regions.size(); ++ region_id) { + auto region = print.regions[region_id]; + for (auto layer : object->layers) { + auto layerm = layer->regions[region_id]; + if (region->config.get_abs_value("perimeter_speed" ) == 0 || + region->config.get_abs_value("small_perimeter_speed" ) == 0 || + region->config.get_abs_value("external_perimeter_speed" ) == 0 || + region->config.get_abs_value("bridge_speed" ) == 0) + mm3_per_mm.push_back(layerm->perimeters.min_mm3_per_mm()); + if (region->config.get_abs_value("infill_speed" ) == 0 || + region->config.get_abs_value("solid_infill_speed" ) == 0 || + region->config.get_abs_value("top_solid_infill_speed" ) == 0 || + region->config.get_abs_value("bridge_speed" ) == 0) + mm3_per_mm.push_back(layerm->fills.min_mm3_per_mm()); + } + } + if (object->config.get_abs_value("support_material_speed" ) == 0 || + object->config.get_abs_value("support_material_interface_speed" ) == 0) + for (auto layer : object->support_layers) + mm3_per_mm.push_back(layer->support_fills.min_mm3_per_mm()); + } + // filter out 0-width segments + mm3_per_mm.erase(std::remove_if(mm3_per_mm.begin(), mm3_per_mm.end(), [](double v) { return v < 0.000001; }), mm3_per_mm.end()); + if (! mm3_per_mm.empty()) { + // In order to honor max_print_speed we need to find a target volumetric + // speed that we can use throughout the print. So we define this target + // volumetric speed as the volumetric speed produced by printing the + // smallest cross-section at the maximum speed: any larger cross-section + // will need slower feedrates. + m_volumetric_speed = *std::min_element(mm3_per_mm.begin(), mm3_per_mm.end()) * print.config.max_print_speed.value; + // limit such volumetric speed with max_volumetric_speed if set + if (print.config.max_volumetric_speed.value > 0) + m_volumetric_speed = std::min(m_volumetric_speed, print.config.max_volumetric_speed.value); + } + } + + m_cooling_buffer = std::make_unique(*this); + if (print.config.spiral_vase.value) + m_spiral_vase = std::make_unique(print.config); + if (print.config.max_volumetric_extrusion_rate_slope_positive.value > 0 || + print.config.max_volumetric_extrusion_rate_slope_negative.value > 0) + m_pressure_equalizer = std::make_unique(&print.config); + m_enable_extrusion_role_markers = (bool)m_pressure_equalizer; + + // Write information on the generator. + { + const auto now = boost::posix_time::second_clock::local_time(); + const auto date = now.date(); + fprintf(file, "; generated by Slic3r %s on %04d-%02d-%02d at %02d:%02d:%02d\n\n", + SLIC3R_VERSION, + // Local date in an ANSII format. + int(now.date().year()), int(now.date().month()), int(now.date().day()), + int(now.time_of_day().hours()), int(now.time_of_day().minutes()), int(now.time_of_day().seconds())); + } + // Write notes (content of the Print Settings tab -> Notes) + { + std::list lines; + boost::split(lines, print.config.notes.value, boost::is_any_of("\n"), boost::token_compress_off); + for (auto line : lines) { + // Remove the trailing '\r' from the '\r\n' sequence. + if (! line.empty() && line.back() == '\r') + line.pop_back(); + fprintf(file, "; %s\n", line.c_str()); + } + if (! lines.empty()) + fprintf(file, "\n"); + } + // Write some terse information on the slicing parameters. + { + const PrintObject *first_object = print.objects.front(); + const double layer_height = first_object->config.layer_height.value; + for (size_t region_id = 0; region_id < print.regions.size(); ++ region_id) { + auto region = print.regions[region_id]; + fprintf(file, "; external perimeters extrusion width = %.2fmm\n", region->flow(frExternalPerimeter, layer_height, false, false, -1., *first_object).width); + fprintf(file, "; perimeters extrusion width = %.2fmm\n", region->flow(frPerimeter, layer_height, false, false, -1., *first_object).width); + fprintf(file, "; infill extrusion width = %.2fmm\n", region->flow(frInfill, layer_height, false, false, -1., *first_object).width); + fprintf(file, "; solid infill extrusion width = %.2fmm\n", region->flow(frSolidInfill, layer_height, false, false, -1., *first_object).width); + fprintf(file, "; top infill extrusion width = %.2fmm\n", region->flow(frTopSolidInfill, layer_height, false, false, -1., *first_object).width); + if (print.has_support_material()) + fprintf(file, "; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width); + if (print.config.first_layer_extrusion_width.value > 0) + fprintf(file, "; first layer extrusion width = %.2fmm\n", region->flow(frPerimeter, layer_height, false, true, -1., *first_object).width); + fprintf(file, "\n"); + } + } + + // Prepare the helper object for replacing placeholders in custom G-code and output filename. + m_placeholder_parser = print.placeholder_parser; + m_placeholder_parser.update_timestamp(); + + // Disable fan. + if (print.config.cooling.value && print.config.disable_fan_first_layers.value) + write(file, m_writer.set_fan(0, true)); + + // Set bed temperature if the start G-code does not contain any bed temp control G-codes. + if (print.config.first_layer_bed_temperature.value > 0 && + boost::ifind_first(print.config.start_gcode.value, std::string("M140")).empty() && + boost::ifind_first(print.config.start_gcode.value, std::string("M190")).empty()) + write(file, m_writer.set_bed_temperature(print.config.first_layer_bed_temperature.value, true)); + + // Set extruder(s) temperature before and after start G-code. + this->_print_first_layer_extruder_temperatures(file, print, false); + fprintf(file, "%s\n", m_placeholder_parser.process(print.config.start_gcode.value).c_str()); + this->_print_first_layer_extruder_temperatures(file, print, true); + + // Set other general things. + write(file, this->preamble()); + + // Initialize a motion planner for object-to-object travel moves. + if (print.config.avoid_crossing_perimeters.value) { + //coord_t distance_from_objects = coord_t(scale_(1.)); + // Compute the offsetted convex hull for each object and repeat it for each copy. + Polygons islands_p; + for (const PrintObject *object : print.objects) { + // Discard objects only containing thin walls (offset would fail on an empty polygon). + Polygons polygons; + for (const Layer *layer : object->layers) + for (const ExPolygon &expoly : layer->slices.expolygons) + polygons.push_back(expoly.contour); + if (! polygons.empty()) { + // Translate convex hull for each object copy and append it to the islands array. + for (const Point © : object->_shifted_copies) + for (Polygon poly : polygons) { + poly.translate(copy); + islands_p.emplace_back(std::move(poly)); + } + } + } + m_avoid_crossing_perimeters.init_external_mp(union_ex(islands_p)); + } + + // Calculate wiping points if needed + if (print.config.ooze_prevention.value) { + Points skirt_points; + for (const ExtrusionEntity *ee : print.skirt.entities) + for (const ExtrusionPath &path : dynamic_cast(ee)->paths) + append(skirt_points, path.polyline.points); + if (! skirt_points.empty()) { + Polygon outer_skirt = Slic3r::Geometry::convex_hull(skirt_points); + Polygons skirts; + for (unsigned int extruder_id : print.extruders()) { + const Pointf &extruder_offset = print.config.extruder_offset.get_at(extruder_id); + Polygon s(outer_skirt); + s.translate(-scale_(extruder_offset.x), -scale_(extruder_offset.y)); + skirts.emplace_back(std::move(s)); + } + m_ooze_prevention.enable = true; + m_ooze_prevention.standby_points = + offset(Slic3r::Geometry::convex_hull(skirts), scale_(3.f)).front().equally_spaced_points(scale_(10.)); +#if 0 + require "Slic3r/SVG.pm"; + Slic3r::SVG::output( + "ooze_prevention.svg", + red_polygons => \@skirts, + polygons => [$outer_skirt], + points => $gcodegen->ooze_prevention->standby_points, + ); +#endif + } + } + + // Set initial extruder only after custom start G-code. + write(file, this->set_extruder(print.extruders().front())); + + // Do all objects for each layer. + if (print.config.complete_objects.value) { + // Print objects from the smallest to the tallest to avoid collisions + // when moving onto next object starting point. + std::vector objects(print.objects); + std::sort(objects.begin(), objects.end(), [](const PrintObject* po1, const PrintObject* po2) { return po1->size.z < po2->size.z; }); + size_t finished_objects = 0; + for (PrintObject *object : objects) { + for (const Point © : object->_shifted_copies) { + Points copies; + copies.push_back(copy); + // Move to the origin position for the copy we're going to print. + // This happens before Z goes down to layer 0 again, so that no collision happens hopefully. + if (finished_objects > 0) { + this->set_origin(unscale(copy.x), unscale(copy.y)); + m_enable_cooling_markers = false; // we're not filtering these moves through CoolingBuffer + m_avoid_crossing_perimeters.use_external_mp_once = true; + write(file, this->retract()); + write(file, this->travel_to(Point(0, 0), erNone, "move to origin position for next object")); + m_enable_cooling_markers = true; + // Disable motion planner when traveling to first object point. + m_avoid_crossing_perimeters.disable_once = true; + } + + // Order layers by print_z, support layers preceding the object layers. + std::vector layers(object->layers); + layers.insert(layers.end(), object->support_layers.begin(), object->support_layers.end()); + std::sort(layers.begin(), layers.end(), [](const Layer *l1, const Layer *l2) + { return (l1->print_z == l2->print_z) ? dynamic_cast(l1) != nullptr : l1->print_z < l2->print_z; }); + for (Layer *layer : layers) { + // Ff we are printing the bottom layer of an object, and we have already finished + // another one, set first layer temperatures. This happens before the Z move + // is triggered, so machine has more time to reach such temperatures. + if (layer->id() == 0 && finished_objects > 0) { + if (print.config.first_layer_bed_temperature.value > 0) + write(file, m_writer.set_bed_temperature(print.config.first_layer_bed_temperature)); + // Set first layer extruder. + this->_print_first_layer_extruder_temperatures(file, print, false); + } + this->process_layer(file, print, *layer, copies); + } + write(file, this->filter(m_cooling_buffer->flush(), true)); + ++ finished_objects; + // Flag indicating whether the nozzle temperature changes from 1st to 2nd layer were performed. + // Reset it when starting another object from 1st layer. + m_second_layer_things_done = false; + } + } + } else { + // Order objects using a nearest neighbor search. + std::vector object_indices; + Points object_reference_points; + for (PrintObject *object : print.objects) + object_reference_points.push_back(object->_shifted_copies.front()); + Slic3r::Geometry::chained_path(object_reference_points, object_indices); + // Sort layers by Z. + // All extrusion moves with the same top layer height are extruded uninterrupted, + // object extrusion moves are performed first, then the support. + std::map> layers; // print_z => [ [layers], [layers], [layers] ] by obj_idx + for (size_t obj_idx = 0; obj_idx < print.objects.size(); ++ obj_idx) { + PrintObject *object = print.objects[obj_idx]; + // Collect the object layers by z, support layers first, object layers second. + LayerPtrs object_layers(object->support_layers.begin(), object->support_layers.end()); + append(object_layers, object->layers); + for (Layer *layer : object_layers) { + std::vector &object_layers_at_printz = layers[layer->print_z]; + if (object_layers_at_printz.empty()) + object_layers_at_printz.resize(print.objects.size(), LayerPtrs()); + object_layers_at_printz[obj_idx].push_back(layer); + } + } + for (auto &layer : layers) + for (size_t obj_idx : object_indices) + for (Layer *l : layer.second[obj_idx]) + this->process_layer(file, print, *l, l->object()->_shifted_copies); + write(file, this->filter(m_cooling_buffer->flush(), true)); + } + + // write end commands to file + write(file, this->retract()); // TODO: process this retract through PressureRegulator in order to discharge fully + write(file, m_writer.set_fan(false)); + writeln(file, m_placeholder_parser.process(print.config.end_gcode)); + write(file, m_writer.update_progress(m_layer_count, m_layer_count, true)); // 100% + write(file, m_writer.postamble()); + + // get filament stats + print.filament_stats.clear(); + print.total_used_filament = 0.; + print.total_extruded_volume = 0.; + print.total_weight = 0.; + print.total_cost = 0.; + for (const Extruder &extruder : m_writer.extruders) { + double used_filament = extruder.used_filament(); + double extruded_volume = extruder.extruded_volume(); + double filament_weight = extruded_volume * extruder.filament_density() * 0.001; + double filament_cost = filament_weight * extruder.filament_cost() * 0.001; + print.filament_stats.insert(std::pair(extruder.id, used_filament)); + fprintf(file, "; filament used = %.1lfmm (%.1lfcm3)\n", used_filament, extruded_volume * 0.001); + if (filament_weight > 0.) { + print.total_weight = print.total_weight + filament_weight; + fprintf(file, "; filament used = %.1lf\n", filament_weight); + if (filament_cost > 0.) { + print.total_cost = print.total_cost + filament_cost; + fprintf(file, "; filament cost = %.1lf\n", filament_cost); + } + } + print.total_used_filament = print.total_used_filament + used_filament; + print.total_extruded_volume = print.total_extruded_volume + extruded_volume; + } + fprintf(file, "; total filament cost = %.1lf\n", print.total_cost); + + // Append full config. + fprintf(file, "\n"); + for (const std::string &key : print.config.keys()) + fprintf(file, "; %s = %s\n", key.c_str(), print.config.serialize(key).c_str()); + for (const std::string &key : print.default_object_config.keys()) + fprintf(file, "; %s = %s\n", key.c_str(), print.default_object_config.serialize(key).c_str()); + + return true; } -void -GCode::set_last_pos(const Point &pos) +// Write 1st layer extruder temperatures into the G-code. +// Only do that if the start G-code does not already contain any M-code controlling an extruder temperature. +// FIXME this does not work correctly for multi-extruder, single heater configuration as it emits multiple preheat commands for the same heater. +// M104 - Set Extruder Temperature +// M109 - Set Extruder Temperature and Wait +void GCode::_print_first_layer_extruder_temperatures(FILE *file, Print &print, bool wait) { - this->_last_pos = pos; - this->_last_pos_defined = true; + if (boost::ifind_first(print.config.start_gcode.value, std::string("M104")).empty() && + boost::ifind_first(print.config.start_gcode.value, std::string("M109")).empty()) { + for (unsigned int tool_id : print.extruders()) { + int temp = print.config.first_layer_temperature.get_at(tool_id); + if (print.config.ooze_prevention.value) + temp += print.config.standby_temperature_delta.value; + if (temp > 0) + write(file, m_writer.set_temperature(temp, wait, tool_id)); + } + } } -bool -GCode::last_pos_defined() const +// Called per object's layer. +// First a $gcode string is collected, +// then filtered and finally written to a file $fh. +//FIXME If printing multiple objects at once, this incorrectly applies cooling logic to a single object's layer instead +// of all the objects printed. +void GCode::process_layer(FILE *file, const Print &print, const Layer &layer, const Points &object_copies) { - return this->_last_pos_defined; + std::string gcode; + + const PrintObject &object = *layer.object(); + m_config.apply(object.config, true); + + const SupportLayer *support_layer = dynamic_cast(&layer); + + // Check whether it is possible to apply the spiral vase logic for this layer. + if (m_spiral_vase) { + bool enable = (layer.id() > 0 || print.config.brim_width.value == 0.) && (layer.id() >= print.config.skirt_height.value && ! print.has_infinite_skirt()); + if (enable) { + for (const LayerRegion *layer_region : layer.regions) + if (layer_region->region()->config.bottom_solid_layers.value > layer.id() || + layer_region->perimeters.items_count() > 1 || + layer_region->fills.items_count() > 0) { + enable = false; + break; + } + } + m_spiral_vase->enable = enable; + } + + // If we're going to apply spiralvase to this layer, disable loop clipping + m_enable_loop_clipping = (! m_spiral_vase || ! m_spiral_vase->enable); + + if (! m_second_layer_things_done && layer.id() == 1) { + // Transition from 1st to 2nd layer. Adjust nozzle temperatures as prescribed by the nozzle dependent + // first_layer_temperature vs. temperature settings. + for (const Extruder &extruder : m_writer.extruders) { + int temperature = print.config.temperature.get_at(extruder.id); + if (temperature > 0 && temperature != print.config.first_layer_temperature.get_at(extruder.id)) + gcode += m_writer.set_temperature(temperature, false, extruder.id); + } + if (print.config.bed_temperature.value > 0 && print.config.bed_temperature != print.config.first_layer_bed_temperature.value) + gcode += m_writer.set_bed_temperature(print.config.bed_temperature); + // Mark the temperature transition from 1st to 2nd layer to be finished. + m_second_layer_things_done = true; + } + + // Set new layer - this will change Z and force a retraction if retract_layer_change is enabled. + if (! print.config.before_layer_gcode.value.empty()) { + PlaceholderParser pp(m_placeholder_parser); + pp.set("layer_num", m_layer_index + 1); + pp.set("layer_z", layer.print_z); + gcode += pp.process(print.config.before_layer_gcode.value) + "\n"; + } + gcode += this->change_layer(layer); // this will increase m_layer_index + if (! print.config.layer_gcode.value.empty()) { + PlaceholderParser pp(m_placeholder_parser); + pp.set("layer_num", m_layer_index); + pp.set("layer_z", layer.print_z); + gcode += pp.process(print.config.layer_gcode.value) + "\n"; + } + + // Extrude skirt at the print_z of the raft layers and normal object layers + // not at the print_z of the interlaced support material layers. + //FIXME this will print the support 1st, skirt 2nd and an object 3rd + // if they are at the same print_z, it is not the 1st print layer and the support is printed before object. + if (// Not enough skirt layers printed yer + (m_skirt_done.size() < print.config.skirt_height.value || print.has_infinite_skirt()) && + // This print_z has not been extruded yet + m_skirt_done.find(layer.print_z) == m_skirt_done.end() && + // and this layer is the 1st layer, or it is an object layer, or it is a raft layer. + (layer.id() == 0 || support_layer == nullptr || layer.id() < object.config.raft_layers.value)) { + this->set_origin(0.,0.); + m_avoid_crossing_perimeters.use_external_mp = true; + std::vector extruder_ids = m_writer.extruder_ids(); + gcode += this->set_extruder(extruder_ids.front()); + // Skip skirt if we have a large brim. + if (layer.id() < print.config.skirt_height.value || print.has_infinite_skirt()) { + Flow skirt_flow = print.skirt_flow(); + // Distribute skirt loops across all extruders. + for (size_t i = 0; i < print.skirt.entities.size(); ++ i) { + // When printing layers > 0 ignore 'min_skirt_length' and + // just use the 'skirts' setting; also just use the current extruder. + if (layer.id() > 0 && i >= print.config.skirts) + break; + unsigned int extruder_id = extruder_ids[(i / extruder_ids.size()) % extruder_ids.size()]; + if (layer.id() == 0) + gcode += this->set_extruder(extruder_id); + // Adjust flow according to this layer's layer height. + ExtrusionLoop loop = *dynamic_cast(print.skirt.entities[i]); + Flow layer_skirt_flow(skirt_flow); + layer_skirt_flow.height = (float)layer.height; + double mm3_per_mm = layer_skirt_flow.mm3_per_mm(); + for (ExtrusionPath &path : loop.paths) { + path.height = (float)layer.height; + path.mm3_per_mm = mm3_per_mm; + } + gcode += this->extrude(loop, "skirt", object.config.support_material_speed.value); + } + } + m_skirt_done.insert(layer.print_z); + m_avoid_crossing_perimeters.use_external_mp = false; + // Allow a straight travel move to the first object point if this is the first layer (but don't in next layers). + if (layer.id() == 0) + m_avoid_crossing_perimeters.disable_once = true; + } + + // extrude brim + if (! m_brim_done) { + gcode += this->set_extruder(print.regions.front()->config.perimeter_extruder.value - 1); + this->set_origin(0.f, 0.f); + m_avoid_crossing_perimeters.use_external_mp = true; + for (const ExtrusionEntity *ee : print.brim.entities) + gcode += this->extrude(*dynamic_cast(ee), "brim", object.config.support_material_speed.value); + m_brim_done = true; + m_avoid_crossing_perimeters.use_external_mp = false; + // Allow a straight travel move to the first object point. + m_avoid_crossing_perimeters.disable_once = true; + } + + for (const Point © : object_copies) { + // When starting a new object, use the external motion planner for the first travel move. + if (m_last_obj_copy != copy) + m_avoid_crossing_perimeters.use_external_mp_once = true; + m_last_obj_copy = copy; + this->set_origin(unscale(copy.x), unscale(copy.y)); + // Extrude support material before other things because it might use a lower Z + // and also because we avoid travelling on other things when printing it. + if (support_layer != nullptr) { + if (support_layer->support_fills.entities.size() > 0) { + if (object.config.support_material_extruder.value == object.config.support_material_interface_extruder.value) { + // Both the support and the support interface are printed with the same extruder, therefore + // the interface may be interleaved with the support base. + // Don't change extruder if the extruder is set to 0. Use the current extruder instead. + gcode += this->extrude_support( + support_layer->support_fills.chained_path_from(m_last_pos, false), + object.config.support_material_extruder); + } else { + // Extrude the support base before support interface for two reasons. + // 1) Support base may be extruded with the current extruder (extruder ID 0) + // and the support interface may be printed with the solube material, + // then one wants to avoid the base being printed with the soluble material. + // 2) It is likely better to print the interface after the base as the interface is + // often printed by bridges and it is convenient to have the base printed already, + // so the bridges may stick to it. + gcode += this->extrude_support( + support_layer->support_fills.chained_path_from(m_last_pos, false, erSupportMaterial), + object.config.support_material_extruder); + // Extrude the support interface. + gcode += this->extrude_support( + support_layer->support_fills.chained_path_from(m_last_pos, false, erSupportMaterialInterface), + object.config.support_material_interface_extruder); + } + } + continue; + } + + // We now define a strategy for building perimeters and fills. The separation + // between regions doesn't matter in terms of printing order, as we follow + // another logic instead: + // - we group all extrusions by extruder so that we minimize toolchanges + // - we start from the last used extruder + // - for each extruder, we group extrusions by island + // - for each island, we extrude perimeters first, unless user set the infill_first + // option + // (Still, we have to keep track of regions because we need to apply their config) + + // group extrusions by extruder and then by island + std::map> by_extruder; + + size_t n_slices = layer.slices.expolygons.size(); + std::vector layer_surface_bboxes; + layer_surface_bboxes.reserve(n_slices); + for (const ExPolygon &expoly : layer.slices.expolygons) + layer_surface_bboxes.push_back(get_extents(expoly.contour)); + auto point_inside_surface = [&layer, &layer_surface_bboxes](const size_t i, const Point &point) { + const BoundingBox &bbox = layer_surface_bboxes[i]; + return point.x >= bbox.min.x && point.x < bbox.max.x && + point.y >= bbox.min.y && point.y < bbox.max.y && + layer.slices.expolygons[i].contour.contains(point); + }; + + for (size_t region_id = 0; region_id < print.regions.size(); ++ region_id) { + const LayerRegion *layerm = layer.regions[region_id]; + if (layerm == nullptr) + continue; + const PrintRegion ®ion = *print.regions[region_id]; + + // process perimeters + for (const ExtrusionEntity *ee : layerm->perimeters.entities) { + // perimeter_coll represents perimeter extrusions of a single island. + const auto *perimeter_coll = dynamic_cast(ee); + if (perimeter_coll->entities.empty()) + // This shouldn't happen but first_point() would fail. + continue; + // Init by_extruder item only if we actually use the extruder. + std::vector &byex = by_extruder[std::max(region.config.perimeter_extruder.value - 1, 0)]; + if (byex.empty()) + byex.assign(n_slices, ByExtruder()); + for (size_t i = 0; i <= n_slices; ++ i) + if (// perimeter_coll->first_point does not fit inside any slice + i == n_slices || + // perimeter_coll->first_point fits inside ith slice + point_inside_surface(i, perimeter_coll->first_point())) { + if (byex[i].by_region.empty()) + byex[i].by_region.assign(print.regions.size(), ByExtruder::ToExtrude()); + byex[i].by_region[region_id].perimeters.append(perimeter_coll->entities); + break; + } + } + + // process infill + // layerm->fills is a collection of Slic3r::ExtrusionPath::Collection objects (C++ class ExtrusionEntityCollection), + // each one containing the ExtrusionPath objects of a certain infill "group" (also called "surface" + // throughout the code). We can redefine the order of such Collections but we have to + // do each one completely at once. + for (const ExtrusionEntity *ee : layerm->fills.entities) { + // fill represents infill extrusions of a single island. + const auto *fill = dynamic_cast(ee); + if (fill->entities.empty()) + // This shouldn't happen but first_point() would fail. + continue; + // init by_extruder item only if we actually use the extruder + int extruder_id = std::max(0, (is_solid_infill(fill->entities.front()->role()) ? region.config.solid_infill_extruder : region.config.infill_extruder) - 1); + // Init by_extruder item only if we actually use the extruder. + std::vector &byex = by_extruder[extruder_id]; + if (byex.empty()) + byex.assign(n_slices, ByExtruder()); + for (size_t i = 0; i <= n_slices; ++i) + if (// fill->first_point does not fit inside any slice + i == n_slices || + // fill->first_point fits inside ith slice + point_inside_surface(i, fill->first_point())) { + if (byex[i].by_region.empty()) + byex[i].by_region.assign(print.regions.size(), ByExtruder::ToExtrude()); + byex[i].by_region[region_id].infills.append(fill->entities); + break; + } + } + } // for regions + + // Tweak extruder ordering to save toolchanges. + std::vector extruders; + extruders.reserve(by_extruder.size()); + for (const auto &ex : by_extruder) + extruders.push_back(ex.first); + // Reorder the extruders, so that the last used extruder is at the front. + for (size_t i = 1; i < extruders.size(); ++ i) + if (extruders[i] == m_writer.extruder()->id) { + // Move the last extruder to the front. + memmove(extruders.data() + 1, extruders.data(), i); + extruders.front() = m_writer.extruder()->id; + break; + } + // Extrude the perimeters & infill ordered by the extruders. + for (unsigned int extruder_id : extruders) { + gcode += this->set_extruder(extruder_id); + for (const ByExtruder &island : by_extruder[extruder_id]) { + if (print.config.infill_first) { + gcode += this->extrude_infill(print, island.by_region); + gcode += this->extrude_perimeters(print, island.by_region); + } else { + gcode += this->extrude_perimeters(print, island.by_region); + gcode += this->extrude_infill(print, island.by_region); + } + } + } + } // for object copies + + // Apply spiral vase post-processing if this layer contains suitable geometry + // (we must feed all the G-code into the post-processor, including the first + // bottom non-spiral layers otherwise it will mess with positions) + // we apply spiral vase at this stage because it requires a full layer. + if (m_spiral_vase) + gcode = m_spiral_vase->process_layer(gcode); + // Apply cooling logic; this may alter speeds. + if (m_cooling_buffer) + gcode = m_cooling_buffer->append( + gcode, + // Index of the current layer's object + //FIXME add an index into the objects? + std::find(print.objects.begin(), print.objects.end(), layer.object()) - print.objects.begin(), + layer.id(), + // Differentiate normal layers from the support layers for the purpose of layer cooling time. + support_layer != nullptr); + write(file, this->filter(std::move(gcode), false)); } -void -GCode::apply_print_config(const PrintConfig &print_config) +std::string GCode::filter(std::string &&gcode, bool flush) { - this->writer.apply_print_config(print_config); - this->config.apply(print_config); + // apply pressure equalization if enabled; + // printf("G-code before filter:\n%s\n", gcode.c_str()); + std::string out = m_pressure_equalizer ? + m_pressure_equalizer->process(gcode.c_str(), flush) : + std::move(gcode); + // printf("G-code after filter:\n%s\n", out.c_str()); + return out; } -void -GCode::set_extruders(const std::vector &extruder_ids) +void GCode::apply_print_config(const PrintConfig &print_config) { - this->writer.set_extruders(extruder_ids); + m_writer.apply_print_config(print_config); + m_config.apply(print_config); +} + +void GCode::set_extruders(const std::vector &extruder_ids) +{ + m_writer.set_extruders(extruder_ids); // enable wipe path generation if any extruder has wipe enabled - this->wipe.enable = false; + m_wipe.enable = false; for (auto id : extruder_ids) - if (this->config.wipe.get_at(id)) { - this->wipe.enable = true; + if (m_config.wipe.get_at(id)) { + m_wipe.enable = true; break; } } -void -GCode::set_origin(const Pointf &pointf) +void GCode::set_origin(const Pointf &pointf) { // if origin increases (goes towards right), last_pos decreases because it goes towards left const Point translate( - scale_(this->origin.x - pointf.x), - scale_(this->origin.y - pointf.y) + scale_(m_origin.x - pointf.x), + scale_(m_origin.y - pointf.y) ); - this->_last_pos.translate(translate); - this->wipe.path.translate(translate); - this->origin = pointf; + m_last_pos.translate(translate); + m_wipe.path.translate(translate); + m_origin = pointf; } -std::string -GCode::preamble() +std::string GCode::preamble() { - std::string gcode = this->writer.preamble(); + std::string gcode = m_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); + m_writer.travel_to_z(m_config.z_offset.value); return gcode; } -std::string -GCode::change_layer(const Layer &layer) +std::string GCode::change_layer(const Layer &layer) { - this->layer = &layer; - this->layer_index++; - this->first_layer = (layer.id() == 0); - delete this->_lower_layer_edge_grid; - this->_lower_layer_edge_grid = NULL; + m_layer = &layer; + m_layer_index++; + m_first_layer = (layer.id() == 0); + m_lower_layer_edge_grid.release(); std::string gcode; - if (enable_analyzer_markers) { + if (m_enable_analyzer_markers) { // Store the binary pointer to the layer object directly into the G-code to be accessed by the GCodeAnalyzer. char buf[64]; - sprintf(buf, ";_LAYEROBJ:%p\n", this->layer); + sprintf(buf, ";_LAYEROBJ:%p\n", m_layer); gcode += buf; } // avoid computing islands and overhangs if they're not needed - if (this->config.avoid_crossing_perimeters) { + if (m_config.avoid_crossing_perimeters) { ExPolygons islands = union_ex(layer.slices, true); - this->avoid_crossing_perimeters.init_layer_mp(islands); + m_avoid_crossing_perimeters.init_layer_mp(islands); } - if (this->layer_count > 0) { - gcode += this->writer.update_progress(this->layer_index, this->layer_count); - } + if (m_layer_count > 0) + gcode += m_writer.update_progress(m_layer_index, m_layer_count); - coordf_t z = layer.print_z + this->config.z_offset.value; // in unscaled coordinates - if (EXTRUDER_CONFIG(retract_layer_change) && this->writer.will_move_z(z)) { + coordf_t z = layer.print_z + m_config.z_offset.value; // in unscaled coordinates + if (EXTRUDER_CONFIG(retract_layer_change) && m_writer.will_move_z(z)) { gcode += this->retract(); } { std::ostringstream comment; - comment << "move to next layer (" << this->layer_index << ")"; - gcode += this->writer.travel_to_z(z, comment.str()); + comment << "move to next layer (" << m_layer_index << ")"; + gcode += m_writer.travel_to_z(z, comment.str()); } // forget last wiping path as wiping after raising Z is pointless - this->wipe.reset_path(); + m_wipe.reset_path(); return gcode; } @@ -348,7 +942,7 @@ static inline float bspline_kernel(float x) { x = std::abs(x); if (x < 1.f) { - return 1.f - (3. / 2.) * x * x + (3.f / 4.f) * x * x * x; + return 1.f - (3.f / 2.f) * x * x + (3.f / 4.f) * x * x * x; } else if (x < 2.f) { x -= 1.f; @@ -448,8 +1042,8 @@ std::vector polygon_parameter_by_length(const Polygon &polygon) // Parametrize the polygon by its length. std::vector lengths(polygon.points.size()+1, 0.); for (size_t i = 1; i < polygon.points.size(); ++ i) - lengths[i] = lengths[i-1] + polygon.points[i].distance_to(polygon.points[i-1]); - lengths.back() = lengths[lengths.size()-2] + polygon.points.front().distance_to(polygon.points.back()); + lengths[i] = lengths[i-1] + float(polygon.points[i].distance_to(polygon.points[i-1])); + lengths.back() = lengths[lengths.size()-2] + float(polygon.points.front().distance_to(polygon.points.back())); return lengths; } @@ -508,28 +1102,27 @@ std::vector polygon_angles_at_vertices(const Polygon &polygon, const std: return angles; } -std::string -GCode::extrude(ExtrusionLoop loop, std::string description, double speed) +std::string GCode::extrude(ExtrusionLoop loop, std::string description, double 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 - if (this->layer->lower_layer != NULL) { - if (this->_lower_layer_edge_grid == NULL) { + if (m_layer->lower_layer != NULL) { + if (! this->m_lower_layer_edge_grid) { // Create the distance field for a layer below. const coord_t distance_field_resolution = scale_(1.f); - this->_lower_layer_edge_grid = new EdgeGrid::Grid(); - this->_lower_layer_edge_grid->create(this->layer->lower_layer->slices, distance_field_resolution); - this->_lower_layer_edge_grid->calculate_sdf(); + this->m_lower_layer_edge_grid = std::make_unique(); + this->m_lower_layer_edge_grid->create(m_layer->lower_layer->slices, distance_field_resolution); + this->m_lower_layer_edge_grid->calculate_sdf(); #if 0 { static int iRun = 0; - BoundingBox bbox = this->_lower_layer_edge_grid->bbox(); + BoundingBox bbox = this->m_lower_layer_edge_grid->bbox(); bbox.min.x -= scale_(5.f); bbox.min.y -= scale_(5.f); bbox.max.x += scale_(5.f); bbox.max.y += scale_(5.f); - EdgeGrid::save_png(*this->_lower_layer_edge_grid, bbox, scale_(0.1f), debug_out_path("GCode_extrude_loop_edge_grid-%d.png", iRun++)); + EdgeGrid::save_png(*this->m_lower_layer_edge_grid, bbox, scale_(0.1f), debug_out_path("GCode_extrude_loop_edge_grid-%d.png", iRun++)); } #endif } @@ -538,14 +1131,14 @@ GCode::extrude(ExtrusionLoop loop, std::string description, double speed) // extrude all loops ccw bool was_clockwise = loop.make_counter_clockwise(); - SeamPosition seam_position = this->config.seam_position; + SeamPosition seam_position = m_config.seam_position; if (loop.loop_role() == elrSkirt) seam_position = spNearest; // 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) { + if (m_config.spiral_vase) { loop.split_at(last_pos, false); } else if (seam_position == spNearest || seam_position == spAligned || seam_position == spRear) { Polygon polygon = loop.polygon(); @@ -557,14 +1150,14 @@ GCode::extrude(ExtrusionLoop loop, std::string description, double speed) switch (seam_position) { case spAligned: // Seam is aligned to the seam at the preceding layer. - if (this->layer != NULL && this->_seam_position.count(this->layer->object()) > 0) { - last_pos = this->_seam_position[this->layer->object()]; + if (m_layer != NULL && m_seam_position.count(m_layer->object()) > 0) { + last_pos = m_seam_position[m_layer->object()]; last_pos_weight = 1.f; } break; case spRear: - last_pos = this->layer->object()->bounding_box().center(); - last_pos.y += coord_t(3. * this->layer->object()->bounding_box().radius()); + last_pos = m_layer->object()->bounding_box().center(); + last_pos.y += coord_t(3. * m_layer->object()->bounding_box().radius()); last_pos_weight = 5.f; break; } @@ -620,7 +1213,7 @@ GCode::extrude(ExtrusionLoop loop, std::string description, double speed) } // Penalty for overhangs. - if (this->_lower_layer_edge_grid) { + if (m_lower_layer_edge_grid) { // Use the edge grid distance field structure over the lower layer to calculate overhangs. coord_t nozzle_r = scale_(0.5*nozzle_dmr); coord_t search_r = scale_(0.8*nozzle_dmr); @@ -630,8 +1223,8 @@ GCode::extrude(ExtrusionLoop loop, std::string description, double speed) // Signed distance is positive outside the object, negative inside the object. // The point is considered at an overhang, if it is more than nozzle radius // outside of the lower layer contour. - bool found = this->_lower_layer_edge_grid->signed_distance(p, search_r, dist); - // If the approximate Signed Distance Field was initialized over this->_lower_layer_edge_grid, + bool found = m_lower_layer_edge_grid->signed_distance(p, search_r, dist); + // If the approximate Signed Distance Field was initialized over m_lower_layer_edge_grid, // then the signed distnace shall always be known. assert(found); penalties[i] += extrudate_overlap_penalty(nozzle_r, penaltyOverhangHalf, dist); @@ -657,7 +1250,7 @@ GCode::extrude(ExtrusionLoop loop, std::string description, double speed) // Align the seams as accurately as possible. idx_min = last_pos_proj_idx; } - this->_seam_position[this->layer->object()] = polygon.points[idx_min]; + m_seam_position[m_layer->object()] = polygon.points[idx_min]; } // Export the contour into a SVG file. @@ -665,8 +1258,8 @@ GCode::extrude(ExtrusionLoop loop, std::string description, double speed) { static int iRun = 0; SVG svg(debug_out_path("GCode_extrude_loop-%d.svg", iRun ++)); - if (this->layer->lower_layer != NULL) - svg.draw(this->layer->lower_layer->slices.expolygons); + if (m_layer->lower_layer != NULL) + svg.draw(m_layer->lower_layer->slices.expolygons); for (size_t i = 0; i < loop.paths.size(); ++ i) svg.draw(loop.paths[i].as_polyline(), "red"); Polylines polylines; @@ -707,19 +1300,18 @@ GCode::extrude(ExtrusionLoop loop, std::string description, double speed) // 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; - + double clip_length = m_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"); - } + if (is_perimeter(paths.front().role()) && loop.length() <= SMALL_PERIMETER_LENGTH && speed == -1) + speed = m_config.small_perimeter_speed.get_abs_value(m_config.perimeter_speed); // extrude along the path std::string gcode; @@ -731,13 +1323,13 @@ GCode::extrude(ExtrusionLoop loop, std::string description, double speed) } // reset acceleration - gcode += this->writer.set_acceleration(this->config.default_acceleration.value); + gcode += m_writer.set_acceleration(m_config.default_acceleration.value); - if (this->wipe.enable) - this->wipe.path = paths.front().polyline; // TODO: don't limit wipe to last path + if (m_wipe.enable) + m_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) { + if (paths.back().role() == erExternalPerimeter && m_layer != NULL && m_config.perimeters.value > 1) { // 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 @@ -767,14 +1359,13 @@ GCode::extrude(ExtrusionLoop loop, std::string description, double speed) 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"); + gcode += m_writer.travel_to_xy(this->point_to_gcode(point), "move inwards before travel"); } return gcode; } -std::string -GCode::extrude(ExtrusionMultiPath multipath, std::string description, double speed) +std::string GCode::extrude(ExtrusionMultiPath multipath, std::string description, double speed) { // extrude along the path std::string gcode; @@ -784,17 +1375,16 @@ GCode::extrude(ExtrusionMultiPath multipath, std::string description, double spe path->simplify(SCALED_RESOLUTION); gcode += this->_extrude(*path, description, speed); } - if (this->wipe.enable) { - this->wipe.path = std::move(multipath.paths.back().polyline); // TODO: don't limit wipe to last path - this->wipe.path.reverse(); + if (m_wipe.enable) { + m_wipe.path = std::move(multipath.paths.back().polyline); // TODO: don't limit wipe to last path + m_wipe.path.reverse(); } // reset acceleration - gcode += this->writer.set_acceleration(this->config.default_acceleration.value); + gcode += m_writer.set_acceleration(m_config.default_acceleration.value); return gcode; } -std::string -GCode::extrude(const ExtrusionEntity &entity, std::string description, double speed) +std::string GCode::extrude(const ExtrusionEntity &entity, std::string description, double speed) { if (const ExtrusionPath* path = dynamic_cast(&entity)) { return this->extrude(*path, description, speed); @@ -808,37 +1398,68 @@ GCode::extrude(const ExtrusionEntity &entity, std::string description, double sp } } -std::string -GCode::extrude(ExtrusionPath path, std::string description, double speed) +std::string GCode::extrude(ExtrusionPath path, std::string description, double speed) { // description += ExtrusionRole2String(path.role()); path.simplify(SCALED_RESOLUTION); std::string gcode = this->_extrude(path, description, speed); - if (this->wipe.enable) { - this->wipe.path = std::move(path.polyline); - this->wipe.path.reverse(); + if (m_wipe.enable) { + m_wipe.path = std::move(path.polyline); + m_wipe.path.reverse(); } // reset acceleration - gcode += this->writer.set_acceleration(this->config.default_acceleration.value); + gcode += m_writer.set_acceleration(m_config.default_acceleration.value); return gcode; } -std::string GCode::extrude_support(const ExtrusionEntityCollection *support_fills, unsigned int extruder_id) +// Extrude perimeters: Decide where to put seams (hide or align seams). +std::string GCode::extrude_perimeters(const Print &print, const std::vector &by_region) { std::string gcode; - if (! support_fills->entities.empty()) { + for (const ByExtruder::ToExtrude ®ion : by_region) { + m_config.apply(print.regions[®ion - &by_region.front()]->config); + for (ExtrusionEntity *ee : region.perimeters.entities) + gcode += this->extrude(*ee, "perimeter"); + } + return gcode; +} + +// Chain the paths hierarchically by a greedy algorithm to minimize a travel distance. +std::string GCode::extrude_infill(const Print &print, const std::vector &by_region) +{ + std::string gcode; + for (const ByExtruder::ToExtrude ®ion : by_region) { + m_config.apply(print.regions[®ion - &by_region.front()]->config); + ExtrusionEntityCollection chained = region.infills.chained_path_from(m_last_pos, false); + for (ExtrusionEntity *fill : chained.entities) { + auto *eec = dynamic_cast(fill); + if (eec) { + ExtrusionEntityCollection chained2 = eec->chained_path_from(m_last_pos, false); + for (ExtrusionEntity *ee : chained2.entities) + gcode += this->extrude(*ee, "infill"); + } else + gcode += this->extrude(*fill, "infill"); + } + } + return gcode; +} + +std::string GCode::extrude_support(const ExtrusionEntityCollection &support_fills, unsigned int extruder_id) +{ + std::string gcode; + if (! support_fills.entities.empty()) { const char *support_label = "support material"; const char *support_interface_label = "support material interface"; - const double support_speed = this->config.get_abs_value("support_material_speed"); - const double support_interface_speed = this->config.get_abs_value("support_material_interface_speed"); + const double support_speed = m_config.support_material_speed.value; + const double support_interface_speed = m_config.support_material_interface_speed.get_abs_value(support_speed); // Only trigger extruder change if the extruder is not set to zero, // but make sure the extruder is initialized. // Extruder ID zero means "does not matter", extrude with the current extruder. - if (this->writer.extruder() == nullptr && extruder_id == 0) + if (m_writer.extruder() == nullptr && extruder_id == 0) extruder_id = 1; if (extruder_id > 0) gcode += this->set_extruder(extruder_id - 1); - for (const ExtrusionEntity *ee : support_fills->entities) { + for (const ExtrusionEntity *ee : support_fills.entities) { ExtrusionRole role = ee->role(); assert(role == erSupportMaterial || role == erSupportMaterialInterface); const char *label = (role == erSupportMaterial) ? support_label : support_interface_label; @@ -863,7 +1484,7 @@ GCode::_extrude(const ExtrusionPath &path, std::string description, double speed std::string gcode; // go to first point of extrusion path - if (!this->_last_pos_defined || !this->_last_pos.coincides_with(path.first_point())) { + if (!m_last_pos_defined || !m_last_pos.coincides_with(path.first_point())) { gcode += this->travel_to( path.first_point(), path.role(), @@ -877,55 +1498,55 @@ GCode::_extrude(const ExtrusionPath &path, std::string description, double speed // adjust acceleration { double acceleration; - if (this->config.first_layer_acceleration.value > 0 && this->first_layer) { - acceleration = this->config.first_layer_acceleration.value; - } else if (this->config.perimeter_acceleration.value > 0 && path.is_perimeter()) { - acceleration = this->config.perimeter_acceleration.value; - } else if (this->config.bridge_acceleration.value > 0 && path.is_bridge()) { - acceleration = this->config.bridge_acceleration.value; - } else if (this->config.infill_acceleration.value > 0 && path.is_infill()) { - acceleration = this->config.infill_acceleration.value; + if (m_config.first_layer_acceleration.value > 0 && m_first_layer) { + acceleration = m_config.first_layer_acceleration.value; + } else if (m_config.perimeter_acceleration.value > 0 && is_perimeter(path.role())) { + acceleration = m_config.perimeter_acceleration.value; + } else if (m_config.bridge_acceleration.value > 0 && is_bridge(path.role())) { + acceleration = m_config.bridge_acceleration.value; + } else if (m_config.infill_acceleration.value > 0 && is_infill(path.role())) { + acceleration = m_config.infill_acceleration.value; } else { - acceleration = this->config.default_acceleration.value; + acceleration = m_config.default_acceleration.value; } - gcode += this->writer.set_acceleration(acceleration); + gcode += m_writer.set_acceleration(acceleration); } // calculate extrusion length per distance unit - double e_per_mm = this->writer.extruder()->e_per_mm3 * path.mm3_per_mm; - if (this->writer.extrusion_axis().empty()) e_per_mm = 0; + double e_per_mm = m_writer.extruder()->e_per_mm3 * path.mm3_per_mm; + if (m_writer.extrusion_axis().empty()) e_per_mm = 0; // set speed if (speed == -1) { if (path.role() == erPerimeter) { - speed = this->config.get_abs_value("perimeter_speed"); + speed = m_config.get_abs_value("perimeter_speed"); } else if (path.role() == erExternalPerimeter) { - speed = this->config.get_abs_value("external_perimeter_speed"); + speed = m_config.get_abs_value("external_perimeter_speed"); } else if (path.role() == erOverhangPerimeter || path.role() == erBridgeInfill) { - speed = this->config.get_abs_value("bridge_speed"); + speed = m_config.get_abs_value("bridge_speed"); } else if (path.role() == erInternalInfill) { - speed = this->config.get_abs_value("infill_speed"); + speed = m_config.get_abs_value("infill_speed"); } else if (path.role() == erSolidInfill) { - speed = this->config.get_abs_value("solid_infill_speed"); + speed = m_config.get_abs_value("solid_infill_speed"); } else if (path.role() == erTopSolidInfill) { - speed = this->config.get_abs_value("top_solid_infill_speed"); + speed = m_config.get_abs_value("top_solid_infill_speed"); } else if (path.role() == erGapFill) { - speed = this->config.get_abs_value("gap_fill_speed"); + speed = m_config.get_abs_value("gap_fill_speed"); } else { CONFESS("Invalid speed"); } } - if (this->first_layer) { - speed = this->config.get_abs_value("first_layer_speed", speed); + if (m_first_layer) { + speed = m_config.get_abs_value("first_layer_speed", speed); } - if (this->volumetric_speed != 0 && speed == 0) { - speed = this->volumetric_speed / path.mm3_per_mm; + if (m_volumetric_speed != 0. && speed == 0) { + speed = m_volumetric_speed / path.mm3_per_mm; } - if (this->config.max_volumetric_speed.value > 0) { + if (m_config.max_volumetric_speed.value > 0) { // cap speed with max_volumetric_speed anyway (even if user is not using autospeed) speed = std::min( speed, - this->config.max_volumetric_speed.value / path.mm3_per_mm + m_config.max_volumetric_speed.value / path.mm3_per_mm ); } if (EXTRUDER_CONFIG(filament_max_volumetric_speed) > 0) { @@ -938,39 +1559,39 @@ GCode::_extrude(const ExtrusionPath &path, std::string description, double speed double F = speed * 60; // convert mm/sec to mm/min // extrude arc or line - if (this->enable_extrusion_role_markers || this->enable_analyzer_markers) { - if (path.role() != this->_last_extrusion_role) { - this->_last_extrusion_role = path.role(); + if (m_enable_extrusion_role_markers || m_enable_analyzer_markers) { + if (path.role() != m_last_extrusion_role) { + m_last_extrusion_role = path.role(); char buf[32]; sprintf(buf, ";_EXTRUSION_ROLE:%d\n", int(path.role())); gcode += buf; } } - if (path.is_bridge() && this->enable_cooling_markers) + if (is_bridge(path.role()) && m_enable_cooling_markers) gcode += ";_BRIDGE_FAN_START\n"; - gcode += this->writer.set_speed(F, "", this->enable_cooling_markers ? ";_EXTRUDE_SET_SPEED" : ""); + gcode += m_writer.set_speed(F, "", m_enable_cooling_markers ? ";_EXTRUDE_SET_SPEED" : ""); double path_length = 0; { - std::string comment = this->config.gcode_comments ? description : ""; + std::string comment = m_config.gcode_comments ? description : ""; Lines lines = path.polyline.lines(); for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) { const double line_length = line->length() * SCALING_FACTOR; path_length += line_length; - gcode += this->writer.extrude_to_xy( + gcode += m_writer.extrude_to_xy( this->point_to_gcode(line->b), e_per_mm * line_length, comment ); } } - if (path.is_bridge() && this->enable_cooling_markers) + if (is_bridge(path.role()) && m_enable_cooling_markers) gcode += ";_BRIDGE_FAN_END\n"; this->set_last_pos(path.last_point()); - if (this->config.cooling) - this->elapsed_time += path_length / F * 60; + if (m_config.cooling) + m_elapsed_time += path_length / F * 60; return gcode; } @@ -992,18 +1613,18 @@ GCode::travel_to(const Point &point, ExtrusionRole role, std::string comment) // if a retraction would be needed, try to use avoid_crossing_perimeters to plan a // multi-hop travel path inside the configuration space if (needs_retraction - && this->config.avoid_crossing_perimeters - && !this->avoid_crossing_perimeters.disable_once) { - travel = this->avoid_crossing_perimeters.travel_to(*this, point); + && m_config.avoid_crossing_perimeters + && ! m_avoid_crossing_perimeters.disable_once) { + travel = m_avoid_crossing_perimeters.travel_to(*this, point); // check again whether the new travel path still needs a retraction needs_retraction = this->needs_retraction(travel, role); - //if (needs_retraction && this->layer_index > 1) exit(0); + //if (needs_retraction && m_layer_index > 1) exit(0); } // Re-allow avoid_crossing_perimeters for the next travel moves - this->avoid_crossing_perimeters.disable_once = false; - this->avoid_crossing_perimeters.use_external_mp_once = false; + m_avoid_crossing_perimeters.disable_once = false; + m_avoid_crossing_perimeters.use_external_mp_once = false; // generate G-code for the travel move std::string gcode; @@ -1011,19 +1632,19 @@ GCode::travel_to(const Point &point, ExtrusionRole role, std::string comment) gcode += this->retract(); else // Reset the wipe path when traveling, so one would not wipe along an old path. - this->wipe.reset_path(); + m_wipe.reset_path(); // use G1 because we rely on paths being straight (G0 may make round paths) Lines lines = travel.lines(); for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) - gcode += this->writer.travel_to_xy(this->point_to_gcode(line->b), comment); + gcode += m_writer.travel_to_xy(this->point_to_gcode(line->b), comment); /* While this makes the estimate more accurate, CoolingBuffer calculates the slowdown factor on the whole elapsed time but only alters non-travel moves, thus the resulting time is still shorter than the configured threshold. We could create a new elapsed_travel_time but we would still need to account for bridges, retractions, wipe etc. - if (this->config.cooling) - this->elapsed_time += unscale(travel.length()) / this->config.get_abs_value("travel_speed"); + if (m_config.cooling) + m_elapsed_time += unscale(travel.length()) / m_config.get_abs_value("travel_speed"); */ return gcode; @@ -1038,7 +1659,7 @@ GCode::needs_retraction(const Polyline &travel, ExtrusionRole role) } if (role == erSupportMaterial) { - const SupportLayer* support_layer = dynamic_cast(this->layer); + const SupportLayer* support_layer = dynamic_cast(m_layer); //FIXME support_layer->support_islands.contains should use some search structure! if (support_layer != NULL && support_layer->support_islands.contains(travel)) { // skip retraction if this is a travel move inside a support material island @@ -1046,9 +1667,9 @@ GCode::needs_retraction(const Polyline &travel, ExtrusionRole role) } } - if (this->config.only_retract_when_crossing_perimeters && this->layer != NULL) { - if (this->config.fill_density.value > 0 - && this->layer->any_internal_region_slice_contains(travel)) { + if (m_config.only_retract_when_crossing_perimeters && m_layer != nullptr) { + if (m_config.fill_density.value > 0 + && m_layer->any_internal_region_slice_contains(travel)) { /* skip retraction if travel is contained in an internal slice *and* internal infill is enabled (so that stringing is entirely not visible) */ return false; @@ -1064,23 +1685,22 @@ GCode::retract(bool toolchange) { std::string gcode; - if (this->writer.extruder() == NULL) + if (m_writer.extruder() == NULL) return gcode; // wipe (if it's enabled for this extruder and we have a stored wipe path) - if (EXTRUDER_CONFIG(wipe) && this->wipe.has_path()) { - gcode += this->wipe.wipe(*this, toolchange); - } + if (EXTRUDER_CONFIG(wipe) && m_wipe.has_path()) + gcode += m_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 += toolchange ? m_writer.retract_for_toolchange() : m_writer.retract(); - gcode += this->writer.reset_e(); - if (this->writer.extruder()->retract_length() > 0 || this->config.use_firmware_retraction) - gcode += this->writer.lift(); + gcode += m_writer.reset_e(); + if (m_writer.extruder()->retract_length() > 0 || m_config.use_firmware_retraction) + gcode += m_writer.lift(); return gcode; } @@ -1089,60 +1709,59 @@ std::string GCode::unretract() { std::string gcode; - gcode += this->writer.unlift(); - gcode += this->writer.unretract(); + gcode += m_writer.unlift(); + gcode += m_writer.unretract(); return gcode; } std::string GCode::set_extruder(unsigned int extruder_id) { - this->placeholder_parser->set("current_extruder", extruder_id); - if (!this->writer.need_toolchange(extruder_id)) + m_placeholder_parser.set("current_extruder", extruder_id); + if (!m_writer.need_toolchange(extruder_id)) return ""; // if we are running a single-extruder setup, just set the extruder and return nothing - if (!this->writer.multiple_extruders) { - return this->writer.toolchange(extruder_id); + if (!m_writer.multiple_extruders) { + return m_writer.toolchange(extruder_id); } // prepend retraction on the current extruder std::string gcode = this->retract(true); // Always reset the extrusion path, even if the tool change retract is set to zero. - this->wipe.reset_path(); + m_wipe.reset_path(); // append custom toolchange G-code - if (this->writer.extruder() != NULL && !this->config.toolchange_gcode.value.empty()) { - PlaceholderParser pp = *this->placeholder_parser; - pp.set("previous_extruder", this->writer.extruder()->id); + if (m_writer.extruder() != NULL && !m_config.toolchange_gcode.value.empty()) { + PlaceholderParser pp = m_placeholder_parser; + pp.set("previous_extruder", m_writer.extruder()->id); pp.set("next_extruder", extruder_id); - gcode += pp.process(this->config.toolchange_gcode.value) + '\n'; + gcode += pp.process(m_config.toolchange_gcode.value) + '\n'; } // if ooze prevention is enabled, park current extruder in the nearest // standby point and set it to the standby temperature - if (this->ooze_prevention.enable && this->writer.extruder() != NULL) - gcode += this->ooze_prevention.pre_toolchange(*this); + if (m_ooze_prevention.enable && m_writer.extruder() != NULL) + gcode += m_ooze_prevention.pre_toolchange(*this); // append the toolchange command - gcode += this->writer.toolchange(extruder_id); + gcode += m_writer.toolchange(extruder_id); // set the new extruder to the operating temperature - if (this->ooze_prevention.enable) - gcode += this->ooze_prevention.post_toolchange(*this); + if (m_ooze_prevention.enable) + gcode += m_ooze_prevention.post_toolchange(*this); return gcode; } // convert a model-space scaled point into G-code coordinates -Pointf -GCode::point_to_gcode(const Point &point) +Pointf GCode::point_to_gcode(const Point &point) const { Pointf extruder_offset = EXTRUDER_CONFIG(extruder_offset); return Pointf( - unscale(point.x) + this->origin.x - extruder_offset.x, - unscale(point.y) + this->origin.y - extruder_offset.y + unscale(point.x) + m_origin.x - extruder_offset.x, + unscale(point.y) + m_origin.y - extruder_offset.y ); } diff --git a/xs/src/libslic3r/GCode.hpp b/xs/src/libslic3r/GCode.hpp index 5e05455c3..71af58b6a 100644 --- a/xs/src/libslic3r/GCode.hpp +++ b/xs/src/libslic3r/GCode.hpp @@ -10,16 +10,20 @@ #include "PlaceholderParser.hpp" #include "Print.hpp" #include "PrintConfig.hpp" +#include "GCode/CoolingBuffer.hpp" +#include "GCode/PressureEqualizer.hpp" +#include "GCode/SpiralVase.hpp" +#include "EdgeGrid.hpp" + #include namespace Slic3r { // Forward declarations. class GCode; -namespace EdgeGrid { class Grid; } class AvoidCrossingPerimeters { - public: +public: // this flag triggers the use of the external configuration space bool use_external_mp; @@ -35,7 +39,7 @@ class AvoidCrossingPerimeters { void init_layer_mp(const ExPolygons &islands); Polyline travel_to(GCode &gcodegen, Point point); - private: +private: MotionPlanner* _external_mp; MotionPlanner* _layer_mp; }; @@ -65,76 +69,141 @@ public: }; class GCode { - public: - +public: + GCode() : + m_enable_loop_clipping(true), + m_enable_cooling_markers(false), + m_enable_extrusion_role_markers(false), + m_enable_analyzer_markers(false), + m_layer_count(0), + m_layer_index(-1), + m_layer(nullptr), + m_first_layer(false), + m_elapsed_time(0.0), + m_volumetric_speed(0), + m_last_pos_defined(false), + m_last_extrusion_role(erNone), + m_brim_done(false), + m_second_layer_things_done(false), + m_last_obj_copy(Point(std::numeric_limits::max(), std::numeric_limits::max())) + {} + ~GCode() {} + + bool do_export(FILE *file, Print &print); + + // Exported for the helper classes (OozePrevention, Wipe) and for the Perl binding for unit tests. + const Pointf& origin() const { return m_origin; } + void set_origin(const Pointf &pointf); + void set_origin(const coordf_t x, const coordf_t y) { this->set_origin(Pointf(x, y)); } + const Point& last_pos() const { return m_last_pos; } + Pointf point_to_gcode(const Point &point) const; + const FullPrintConfig &config() const { return m_config; } + const Layer* layer() const { return m_layer; } + GCodeWriter& writer() { return m_writer; } + bool enable_cooling_markers() const { return m_enable_cooling_markers; } + float get_reset_elapsed_time() { float t = m_elapsed_time; m_elapsed_time = 0.f; return t; } + + // For Perl bindings, to be used exclusively by unit tests. + unsigned int layer_count() const { return m_layer_count; } + void set_layer_count(unsigned int value) { m_layer_count = value; } + float elapsed_time() const { return m_elapsed_time; } + void set_elapsed_time(float value) { m_elapsed_time = value; } + void apply_print_config(const PrintConfig &print_config); + +private: + void process_layer(FILE *file, const Print &print, const Layer &layer, const Points &object_copies); + + void set_last_pos(const Point &pos) { m_last_pos = pos; m_last_pos_defined = true; } + bool last_pos_defined() const { return m_last_pos_defined; } + void set_extruders(const std::vector &extruder_ids); + std::string preamble(); + std::string change_layer(const Layer &layer); + std::string extrude(const ExtrusionEntity &entity, std::string description = "", double speed = -1); + std::string extrude(ExtrusionLoop loop, std::string description = "", double speed = -1); + std::string extrude(ExtrusionMultiPath multipath, std::string description = "", double speed = -1); + std::string extrude(ExtrusionPath path, std::string description = "", double speed = -1); + + struct ByExtruder + { + struct ToExtrude { + ExtrusionEntityCollection perimeters; + ExtrusionEntityCollection infills; + }; + std::vector by_region; + }; + std::string extrude_perimeters(const Print &print, const std::vector &by_region); + std::string extrude_infill(const Print &print, const std::vector &by_region); + std::string extrude_support(const ExtrusionEntityCollection &support_fills, unsigned int extruder_id); + + std::string travel_to(const Point &point, ExtrusionRole role, std::string comment); + bool needs_retraction(const Polyline &travel, ExtrusionRole role = erNone); + std::string retract(bool toolchange = false); + std::string unretract(); + std::string set_extruder(unsigned int extruder_id); + /* Origin of print coordinates expressed in unscaled G-code coordinates. This affects the input arguments supplied to the extrude*() and travel_to() methods. */ - Pointf origin; - FullPrintConfig config; - GCodeWriter writer; - PlaceholderParser* placeholder_parser; - OozePrevention ooze_prevention; - Wipe wipe; - AvoidCrossingPerimeters avoid_crossing_perimeters; - bool enable_loop_clipping; + Pointf m_origin; + FullPrintConfig m_config; + GCodeWriter m_writer; + PlaceholderParser m_placeholder_parser; + OozePrevention m_ooze_prevention; + Wipe m_wipe; + AvoidCrossingPerimeters m_avoid_crossing_perimeters; + bool m_enable_loop_clipping; // If enabled, the G-code generator will put following comments at the ends // of the G-code lines: _EXTRUDE_SET_SPEED, _WIPE, _BRIDGE_FAN_START, _BRIDGE_FAN_END // Those comments are received and consumed (removed from the G-code) by the CoolingBuffer.pm Perl module. - bool enable_cooling_markers; + bool m_enable_cooling_markers; // Markers for the Pressure Equalizer to recognize the extrusion type. // The Pressure Equalizer removes the markers from the final G-code. - bool enable_extrusion_role_markers; + bool m_enable_extrusion_role_markers; // Extended markers for the G-code Analyzer. // The G-code Analyzer will remove these comments from the final G-code. - bool enable_analyzer_markers; + bool m_enable_analyzer_markers; // How many times will change_layer() be called? // change_layer() will update the progress bar. - size_t layer_count; + unsigned int m_layer_count; // Progress bar indicator. Increments from -1 up to layer_count. - int layer_index; + int m_layer_index; // Current layer processed. Insequential printing mode, only a single copy will be printed. // In non-sequential mode, all its copies will be printed. - const Layer* layer; - std::map _seam_position; + const Layer* m_layer; + std::map m_seam_position; // Distance Field structure to - EdgeGrid::Grid *_lower_layer_edge_grid; - bool first_layer; // this flag triggers first layer speeds - // Used by the CoolingBuffer.pm Perl module to calculate time spent per layer change. + std::unique_ptr m_lower_layer_edge_grid; + // this flag triggers first layer speeds + bool m_first_layer; + // Used by the CoolingBuffer G-code filter to calculate time spent per layer change. // This value is not quite precise. First it only accouts for extrusion moves and travel moves, // it does not account for wipe, retract / unretract moves. // second it does not account for the velocity profiles of the printer. - float elapsed_time; // seconds - double volumetric_speed; + float m_elapsed_time; // seconds + double m_volumetric_speed; // Support for the extrusion role markers. Which marker is active? - ExtrusionRole _last_extrusion_role; - - GCode(); - ~GCode(); - const Point& last_pos() const; - void set_last_pos(const Point &pos); - bool last_pos_defined() const; - void apply_print_config(const PrintConfig &print_config); - void set_extruders(const std::vector &extruder_ids); - void set_origin(const Pointf &pointf); - std::string preamble(); - std::string change_layer(const Layer &layer); - std::string extrude(const ExtrusionEntity &entity, std::string description = "", double speed = -1); - std::string extrude(ExtrusionLoop loop, std::string description = "", double speed = -1); - std::string extrude(ExtrusionMultiPath multipath, std::string description = "", double speed = -1); - std::string extrude(ExtrusionPath path, std::string description = "", double speed = -1); - std::string extrude_support(const ExtrusionEntityCollection *support_fills, unsigned int extruder_id); - std::string travel_to(const Point &point, ExtrusionRole role, std::string comment); - bool needs_retraction(const Polyline &travel, ExtrusionRole role = erNone); - std::string retract(bool toolchange = false); - std::string unretract(); - std::string set_extruder(unsigned int extruder_id); - Pointf point_to_gcode(const Point &point); - - private: - Point _last_pos; - bool _last_pos_defined; + ExtrusionRole m_last_extrusion_role; + + Point m_last_pos; + bool m_last_pos_defined; + + std::unique_ptr m_cooling_buffer; + std::unique_ptr m_spiral_vase; + std::unique_ptr m_pressure_equalizer; + + // Heights at which the skirt has already been extruded. + std::set m_skirt_done; + // Has the brim been extruded already? Brim is being extruded only for the first object of a multi-object print. + bool m_brim_done; + // Flag indicating whether the nozzle temperature changes from 1st to 2nd layer were performed. + bool m_second_layer_things_done; + // Index of a last object copy extruded. -1 for not set yet. + Point m_last_obj_copy; + std::string _extrude(const ExtrusionPath &path, std::string description = "", double speed = -1); + void _print_first_layer_extruder_temperatures(FILE *file, Print &print, bool wait); + + std::string filter(std::string &&gcode, bool flush); }; } diff --git a/xs/src/libslic3r/GCode/Analyzer.cpp b/xs/src/libslic3r/GCode/Analyzer.cpp index 63f9639b6..ab2955eb5 100644 --- a/xs/src/libslic3r/GCode/Analyzer.cpp +++ b/xs/src/libslic3r/GCode/Analyzer.cpp @@ -90,7 +90,7 @@ static inline int parse_int(const char *&line) char *endptr = NULL; long result = strtol(line, &endptr, 10); if (endptr == NULL || !is_ws_or_eol(*endptr)) - throw std::runtime_error("GCodePressureEqualizer: Error parsing an int"); + throw std::runtime_error("GCodeAnalyzer: Error parsing an int"); line = endptr; return int(result); }; @@ -102,7 +102,7 @@ static inline float parse_float(const char *&line) char *endptr = NULL; float result = strtof(line, &endptr); if (endptr == NULL || !is_ws_or_eol(*endptr)) - throw std::runtime_error("GCodePressureEqualizer: Error parsing a float"); + throw std::runtime_error("GCodeAnalyzer: Error parsing a float"); line = endptr; return result; }; @@ -171,7 +171,7 @@ bool GCodeAnalyzer::process_line(const char *line, const size_t len) assert(false); } if (i == -1) - throw std::runtime_error(std::string("GCodePressureEqualizer: Invalid axis for G0/G1: ") + axis); + throw std::runtime_error(std::string("GCodeAnalyzer: Invalid axis for G0/G1: ") + axis); buf.pos_provided[i] = true; new_pos[i] = parse_float(line); if (i == 3 && m_config->use_relative_e_distances.value) @@ -235,7 +235,7 @@ bool GCodeAnalyzer::process_line(const char *line, const size_t len) set = true; break; default: - throw std::runtime_error(std::string("GCodePressureEqualizer: Incorrect axis in a G92 G-code: ") + axis); + throw std::runtime_error(std::string("GCodeAnalyzer: Incorrect axis in a G92 G-code: ") + axis); } eatws(line); } diff --git a/xs/src/libslic3r/GCode/CoolingBuffer.cpp b/xs/src/libslic3r/GCode/CoolingBuffer.cpp index 3254eb17d..7de2a49c0 100644 --- a/xs/src/libslic3r/GCode/CoolingBuffer.cpp +++ b/xs/src/libslic3r/GCode/CoolingBuffer.cpp @@ -1,3 +1,4 @@ +#include "../GCode.hpp" #include "CoolingBuffer.hpp" #include #include @@ -5,28 +6,26 @@ namespace Slic3r { -std::string -CoolingBuffer::append(const std::string &gcode, std::string obj_id, size_t layer_id, float print_z) +std::string CoolingBuffer::append(const std::string &gcode, size_t object_id, size_t layer_id, bool is_support) { std::string out; - if (this->_last_z.find(obj_id) != this->_last_z.end()) { - // A layer was finished, Z of the object's layer changed. Process the layer. + size_t signature = object_id * 2 + is_support ? 1 : 0; + if (m_object_ids_visited.find(signature) != m_object_ids_visited.end()) + // For a single print_z, a combination of (object_id, is_support) could repeat once only. + // If the combination of (object_id, is_support) reappears, this must be for another print_z, + // therefore a layer has to be finalized. out = this->flush(); - } - - this->_layer_id = layer_id; - this->_last_z[obj_id] = print_z; - this->_gcode += gcode; + + m_object_ids_visited.insert(signature); + m_layer_id = layer_id; + m_gcode += gcode; // This is a very rough estimate of the print time, // not taking into account the acceleration curves generated by the printer firmware. - this->_elapsed_time += this->_gcodegen->elapsed_time; - this->_gcodegen->elapsed_time = 0; - + m_elapsed_time += m_gcodegen.get_reset_elapsed_time(); return out; } -void -apply_speed_factor(std::string &line, float speed_factor, float min_print_speed) +void apply_speed_factor(std::string &line, float speed_factor, float min_print_speed) { // find pos of F size_t pos = line.find_first_of('F'); @@ -51,36 +50,34 @@ apply_speed_factor(std::string &line, float speed_factor, float min_print_speed) } } -std::string -CoolingBuffer::flush() +std::string CoolingBuffer::flush() { - GCode &gg = *this->_gcodegen; + const FullPrintConfig &config = m_gcodegen.config(); - std::string gcode = this->_gcode; - float elapsed = this->_elapsed_time; - this->_gcode = ""; - this->_elapsed_time = 0; - this->_last_z.clear(); // reset the whole table otherwise we would compute overlapping times - - int fan_speed = gg.config.fan_always_on ? gg.config.min_fan_speed.value : 0; + std::string gcode = m_gcode; + float elapsed = m_elapsed_time; + m_gcode.clear(); + m_elapsed_time = 0.; + + int fan_speed = config.fan_always_on ? config.min_fan_speed.value : 0; float speed_factor = 1.0; - if (gg.config.cooling) { + if (config.cooling) { #ifdef SLIC3R_DEBUG - printf("Layer %zu estimated printing time: %f seconds\n", this->_layer_id, elapsed); + printf("Layer %zu estimated printing time: %f seconds\n", m_layer_id, elapsed); #endif - if (elapsed < (float)gg.config.slowdown_below_layer_time) { + if (elapsed < (float)config.slowdown_below_layer_time) { // Layer time very short. Enable the fan to a full throttle and slow down the print // (stretch the layer print time to slowdown_below_layer_time). - fan_speed = gg.config.max_fan_speed; - speed_factor = elapsed / (float)gg.config.slowdown_below_layer_time; - } else if (elapsed < (float)gg.config.fan_below_layer_time) { + fan_speed = config.max_fan_speed; + speed_factor = elapsed / (float)config.slowdown_below_layer_time; + } else if (elapsed < (float)config.fan_below_layer_time) { // Layer time quite short. Enable the fan proportionally according to the current layer time. - fan_speed = gg.config.max_fan_speed - - (gg.config.max_fan_speed - gg.config.min_fan_speed) - * (elapsed - (float)gg.config.slowdown_below_layer_time) - / (gg.config.fan_below_layer_time - gg.config.slowdown_below_layer_time); + fan_speed = config.max_fan_speed + - (config.max_fan_speed - config.min_fan_speed) + * (elapsed - (float)config.slowdown_below_layer_time) + / (config.fan_below_layer_time - config.slowdown_below_layer_time); } #ifdef SLIC3R_DEBUG @@ -94,13 +91,14 @@ CoolingBuffer::flush() std::string new_gcode; std::istringstream ss(gcode); std::string line; - bool bridge_fan_start = false; + bool bridge_fan_start = false; + float min_print_speed = float(config.min_print_speed * 60.); while (std::getline(ss, line)) { if (boost::starts_with(line, "G1") && boost::contains(line, ";_EXTRUDE_SET_SPEED") && !boost::contains(line, ";_WIPE") && !bridge_fan_start) { - apply_speed_factor(line, speed_factor, this->_min_print_speed); + apply_speed_factor(line, speed_factor, min_print_speed); boost::replace_first(line, ";_EXTRUDE_SET_SPEED", ""); } bridge_fan_start = boost::contains(line, ";_BRIDGE_FAN_START"); @@ -109,22 +107,23 @@ CoolingBuffer::flush() gcode = new_gcode; } } - if (this->_layer_id < gg.config.disable_fan_first_layers) + if (m_layer_id < config.disable_fan_first_layers) fan_speed = 0; - gcode = gg.writer.set_fan(fan_speed) + gcode; + gcode = m_gcodegen.writer().set_fan(fan_speed) + gcode; // bridge fan speed - if (!gg.config.cooling || gg.config.bridge_fan_speed == 0 || this->_layer_id < gg.config.disable_fan_first_layers) { + if (!config.cooling || config.bridge_fan_speed == 0 || m_layer_id < config.disable_fan_first_layers) { boost::replace_all(gcode, ";_BRIDGE_FAN_START", ""); boost::replace_all(gcode, ";_BRIDGE_FAN_END", ""); } else { - boost::replace_all(gcode, ";_BRIDGE_FAN_START", gg.writer.set_fan(gg.config.bridge_fan_speed, true)); - boost::replace_all(gcode, ";_BRIDGE_FAN_END", gg.writer.set_fan(fan_speed, true)); + boost::replace_all(gcode, ";_BRIDGE_FAN_START", m_gcodegen.writer().set_fan(config.bridge_fan_speed, true)); + boost::replace_all(gcode, ";_BRIDGE_FAN_END", m_gcodegen.writer().set_fan(fan_speed, true)); } boost::replace_all(gcode, ";_WIPE", ""); boost::replace_all(gcode, ";_EXTRUDE_SET_SPEED", ""); + m_object_ids_visited.clear(); return gcode; } diff --git a/xs/src/libslic3r/GCode/CoolingBuffer.hpp b/xs/src/libslic3r/GCode/CoolingBuffer.hpp index 01770a58d..ac4ba8a88 100644 --- a/xs/src/libslic3r/GCode/CoolingBuffer.hpp +++ b/xs/src/libslic3r/GCode/CoolingBuffer.hpp @@ -2,12 +2,14 @@ #define slic3r_CoolingBuffer_hpp_ #include "libslic3r.h" -#include "GCode.hpp" #include #include namespace Slic3r { +class GCode; +class Layer; + /* A standalone G-code filter, to control cooling of the print. The G-code is processed per layer. Once a layer is collected, fan start / stop commands are edited @@ -15,23 +17,20 @@ and the print is modified to stretch over a minimum layer time. */ class CoolingBuffer { - public: - CoolingBuffer(GCode &gcodegen) - : _gcodegen(&gcodegen), _elapsed_time(0.), _layer_id(0) - { - this->_min_print_speed = this->_gcodegen->config.min_print_speed * 60; - }; - std::string append(const std::string &gcode, std::string obj_id, size_t layer_id, float print_z); +public: + CoolingBuffer(GCode &gcodegen) : m_gcodegen(gcodegen), m_elapsed_time(0.), m_layer_id(0) {} + std::string append(const std::string &gcode, size_t object_id, size_t layer_id, bool is_support); std::string flush(); - GCode* gcodegen() { return this->_gcodegen; }; + GCode* gcodegen() { return &m_gcodegen; }; - private: - GCode* _gcodegen; - std::string _gcode; - float _elapsed_time; - size_t _layer_id; - std::map _last_z; - float _min_print_speed; +private: + CoolingBuffer& operator=(const CoolingBuffer&); + + GCode& m_gcodegen; + std::string m_gcode; + float m_elapsed_time; + size_t m_layer_id; + std::set m_object_ids_visited; }; } diff --git a/xs/src/libslic3r/GCode/PressureEqualizer.cpp b/xs/src/libslic3r/GCode/PressureEqualizer.cpp index eab93aa9d..e6b118a28 100644 --- a/xs/src/libslic3r/GCode/PressureEqualizer.cpp +++ b/xs/src/libslic3r/GCode/PressureEqualizer.cpp @@ -9,17 +9,17 @@ namespace Slic3r { -GCodePressureEqualizer::GCodePressureEqualizer(const Slic3r::GCodeConfig *config) : +PressureEqualizer::PressureEqualizer(const Slic3r::GCodeConfig *config) : m_config(config) { reset(); } -GCodePressureEqualizer::~GCodePressureEqualizer() +PressureEqualizer::~PressureEqualizer() { } -void GCodePressureEqualizer::reset() +void PressureEqualizer::reset() { circular_buffer_pos = 0; circular_buffer_size = 100; @@ -69,7 +69,7 @@ void GCodePressureEqualizer::reset() line_idx = 0; } -const char* GCodePressureEqualizer::process(const char *szGCode, bool flush) +const char* PressureEqualizer::process(const char *szGCode, bool flush) { // Reset length of the output_buffer. output_buffer_length = 0; @@ -147,7 +147,7 @@ static inline int parse_int(const char *&line) char *endptr = NULL; long result = strtol(line, &endptr, 10); if (endptr == NULL || !is_ws_or_eol(*endptr)) - throw std::runtime_error("GCodePressureEqualizer: Error parsing an int"); + throw std::runtime_error("PressureEqualizer: Error parsing an int"); line = endptr; return int(result); }; @@ -159,13 +159,13 @@ static inline float parse_float(const char *&line) char *endptr = NULL; float result = strtof(line, &endptr); if (endptr == NULL || !is_ws_or_eol(*endptr)) - throw std::runtime_error("GCodePressureEqualizer: Error parsing a float"); + throw std::runtime_error("PressureEqualizer: Error parsing a float"); line = endptr; return result; }; #define EXTRUSION_ROLE_TAG ";_EXTRUSION_ROLE:" -bool GCodePressureEqualizer::process_line(const char *line, const size_t len, GCodeLine &buf) +bool PressureEqualizer::process_line(const char *line, const size_t len, GCodeLine &buf) { if (strncmp(line, EXTRUSION_ROLE_TAG, strlen(EXTRUSION_ROLE_TAG)) == 0) { line += strlen(EXTRUSION_ROLE_TAG); @@ -228,7 +228,7 @@ bool GCodePressureEqualizer::process_line(const char *line, const size_t len, GC assert(false); } if (i == -1) - throw std::runtime_error(std::string("GCodePressureEqualizer: Invalid axis for G0/G1: ") + axis); + throw std::runtime_error(std::string("GCode::PressureEqualizer: Invalid axis for G0/G1: ") + axis); buf.pos_provided[i] = true; new_pos[i] = parse_float(line); if (i == 3 && m_config->use_relative_e_distances.value) @@ -297,7 +297,7 @@ bool GCodePressureEqualizer::process_line(const char *line, const size_t len, GC set = true; break; default: - throw std::runtime_error(std::string("GCodePressureEqualizer: Incorrect axis in a G92 G-code: ") + axis); + throw std::runtime_error(std::string("GCode::PressureEqualizer: Incorrect axis in a G92 G-code: ") + axis); } eatws(line); } @@ -355,7 +355,7 @@ bool GCodePressureEqualizer::process_line(const char *line, const size_t len, GC return true; } -void GCodePressureEqualizer::output_gcode_line(GCodeLine &line) +void PressureEqualizer::output_gcode_line(GCodeLine &line) { if (! line.modified) { push_to_output(line.raw.data(), line.raw_length, true); @@ -453,7 +453,7 @@ void GCodePressureEqualizer::output_gcode_line(GCodeLine &line) } } -void GCodePressureEqualizer::adjust_volumetric_rate() +void PressureEqualizer::adjust_volumetric_rate() { if (circular_buffer_items < 2) return; @@ -563,7 +563,7 @@ void GCodePressureEqualizer::adjust_volumetric_rate() } } -void GCodePressureEqualizer::push_axis_to_output(const char axis, const float value, bool add_eol) +void PressureEqualizer::push_axis_to_output(const char axis, const float value, bool add_eol) { char buf[2048]; int len = sprintf(buf, @@ -572,7 +572,7 @@ void GCodePressureEqualizer::push_axis_to_output(const char axis, const float va push_to_output(buf, len, add_eol); } -void GCodePressureEqualizer::push_to_output(const char *text, const size_t len, bool add_eol) +void PressureEqualizer::push_to_output(const char *text, const size_t len, bool add_eol) { // New length of the output buffer content. size_t len_new = output_buffer_length + len + 1; @@ -604,7 +604,7 @@ void GCodePressureEqualizer::push_to_output(const char *text, const size_t len, output_buffer[output_buffer_length] = 0; } -void GCodePressureEqualizer::push_line_to_output(const GCodeLine &line, const float new_feedrate, const char *comment) +void PressureEqualizer::push_line_to_output(const GCodeLine &line, const float new_feedrate, const char *comment) { push_to_output("G1", 2, false); for (char i = 0; i < 3; ++ i) diff --git a/xs/src/libslic3r/GCode/PressureEqualizer.hpp b/xs/src/libslic3r/GCode/PressureEqualizer.hpp index e97f17323..13cdc9418 100644 --- a/xs/src/libslic3r/GCode/PressureEqualizer.hpp +++ b/xs/src/libslic3r/GCode/PressureEqualizer.hpp @@ -9,11 +9,11 @@ namespace Slic3r { // Processes a G-code. Finds changes in the volumetric extrusion speed and adjusts the transitions // between these paths to limit fast changes in the volumetric extrusion speed. -class GCodePressureEqualizer +class PressureEqualizer { public: - GCodePressureEqualizer(const Slic3r::GCodeConfig *config); - ~GCodePressureEqualizer(); + PressureEqualizer(const Slic3r::GCodeConfig *config); + ~PressureEqualizer(); void reset(); diff --git a/xs/src/libslic3r/GCode/SpiralVase.cpp b/xs/src/libslic3r/GCode/SpiralVase.cpp index 9299da86a..fd300067f 100644 --- a/xs/src/libslic3r/GCode/SpiralVase.cpp +++ b/xs/src/libslic3r/GCode/SpiralVase.cpp @@ -1,4 +1,5 @@ #include "SpiralVase.hpp" +#include "GCode.hpp" #include namespace Slic3r { diff --git a/xs/src/libslic3r/GCode/SpiralVase.hpp b/xs/src/libslic3r/GCode/SpiralVase.hpp index f14d15879..234642524 100644 --- a/xs/src/libslic3r/GCode/SpiralVase.hpp +++ b/xs/src/libslic3r/GCode/SpiralVase.hpp @@ -2,7 +2,6 @@ #define slic3r_SpiralVase_hpp_ #include "libslic3r.h" -#include "GCode.hpp" #include "GCodeReader.hpp" namespace Slic3r { diff --git a/xs/src/libslic3r/GCodeWriter.cpp b/xs/src/libslic3r/GCodeWriter.cpp index ab92c6f36..2f86f576e 100644 --- a/xs/src/libslic3r/GCodeWriter.cpp +++ b/xs/src/libslic3r/GCodeWriter.cpp @@ -23,8 +23,8 @@ GCodeWriter::apply_print_config(const PrintConfig &print_config) void GCodeWriter::set_extruders(const std::vector &extruder_ids) { - for (std::vector::const_iterator i = extruder_ids.begin(); i != extruder_ids.end(); ++i) - this->extruders.insert( std::pair(*i, Extruder(*i, &this->config)) ); + for (unsigned int extruder_id : extruder_ids) + this->extruders.insert(Extruder(extruder_id, &this->config)); /* we enable support for multiple extruder if any extruder greater than 0 is used (even if prints only uses that one) since we need to output Tx commands @@ -194,11 +194,12 @@ GCodeWriter::reset_e(bool force) || FLAVOR_IS(gcfSailfish)) return ""; - if (this->_extruder != NULL) { - if (this->_extruder->E == 0 && !force) return ""; - this->_extruder->E = 0; + if (this->_extruder != nullptr) { + if (this->_extruder->E == 0. && ! force) + return ""; + this->_extruder->E = 0.; } - + if (!this->_extrusion_axis.empty() && !this->config.use_relative_e_distances) { std::ostringstream gcode; gcode << "G92 " << this->_extrusion_axis << "0"; @@ -226,25 +227,10 @@ GCodeWriter::update_progress(unsigned int num, unsigned int tot, bool allow_100) return gcode.str(); } -bool -GCodeWriter::need_toolchange(unsigned int extruder_id) const -{ - // return false if this extruder was already selected - return (this->_extruder == NULL) || (this->_extruder->id != extruder_id); -} - -std::string -GCodeWriter::set_extruder(unsigned int extruder_id) -{ - if (!this->need_toolchange(extruder_id)) return ""; - return this->toolchange(extruder_id); -} - -std::string -GCodeWriter::toolchange(unsigned int extruder_id) +std::string GCodeWriter::toolchange(unsigned int extruder_id) { // set the new extruder - this->_extruder = &this->extruders.find(extruder_id)->second; + this->_extruder = const_cast(&*this->extruders.find(Extruder::key(extruder_id))); // return the toolchange command // if we are running a single-extruder setup, just set the extruder and return nothing diff --git a/xs/src/libslic3r/GCodeWriter.hpp b/xs/src/libslic3r/GCodeWriter.hpp index cd1eab008..c3c90397a 100644 --- a/xs/src/libslic3r/GCodeWriter.hpp +++ b/xs/src/libslic3r/GCodeWriter.hpp @@ -12,17 +12,25 @@ namespace Slic3r { class GCodeWriter { public: GCodeConfig config; - std::map extruders; + std::set extruders; bool multiple_extruders; - GCodeWriter() - : multiple_extruders(false), _extrusion_axis("E"), _extruder(NULL), - _last_acceleration(0), _last_fan_speed(0), _lifted(0) - {}; - Extruder* extruder() const { return this->_extruder; } + GCodeWriter() : + multiple_extruders(false), _extrusion_axis("E"), _extruder(nullptr), + _last_acceleration(0), _last_fan_speed(0), _lifted(0) + {} + Extruder* extruder() { return this->_extruder; } + const Extruder* extruder() const { return this->_extruder; } std::string extrusion_axis() const { return this->_extrusion_axis; } - void apply_print_config(const PrintConfig &print_config); - void set_extruders(const std::vector &extruder_ids); + void apply_print_config(const PrintConfig &print_config); + void set_extruders(const std::vector &extruder_ids); + std::vector extruder_ids() const { + std::vector out; + out.reserve(extruders.size()); + for (const auto e : extruders) + out.push_back(e.id); + return out; + } std::string preamble(); std::string postamble() const; std::string set_temperature(unsigned int temperature, bool wait = false, int tool = -1) const; @@ -31,14 +39,17 @@ public: std::string set_acceleration(unsigned int acceleration); std::string reset_e(bool force = false); std::string update_progress(unsigned int num, unsigned int tot, bool allow_100 = false) const; - bool need_toolchange(unsigned int extruder_id) const; - std::string set_extruder(unsigned int extruder_id); + // return false if this extruder was already selected + bool need_toolchange(unsigned int extruder_id) const + { return (this->_extruder == nullptr) || (this->_extruder->id != extruder_id); } + std::string set_extruder(unsigned int extruder_id) + { return this->need_toolchange(extruder_id) ? this->toolchange(extruder_id) : ""; } std::string toolchange(unsigned int extruder_id); std::string set_speed(double F, const std::string &comment = std::string(), const std::string &cooling_marker = std::string()) const; std::string travel_to_xy(const Pointf &point, const std::string &comment = std::string()); std::string travel_to_xyz(const Pointf3 &point, const std::string &comment = std::string()); std::string travel_to_z(double z, const std::string &comment = std::string()); - bool will_move_z(double z) const; + bool will_move_z(double z) const; std::string extrude_to_xy(const Pointf &point, double dE, const std::string &comment = std::string()); std::string extrude_to_xyz(const Pointf3 &point, double dE, const std::string &comment = std::string()); std::string retract(); @@ -46,14 +57,15 @@ public: std::string unretract(); std::string lift(); std::string unlift(); - Pointf3 get_position() const { return this->_pos; } + Pointf3 get_position() const { return this->_pos; } + private: - std::string _extrusion_axis; - Extruder* _extruder; - unsigned int _last_acceleration; - unsigned int _last_fan_speed; - double _lifted; - Pointf3 _pos; + std::string _extrusion_axis; + Extruder* _extruder; + unsigned int _last_acceleration; + unsigned int _last_fan_speed; + double _lifted; + Pointf3 _pos; std::string _travel_to_z(double z, const std::string &comment); std::string _retract(double length, double restart_extra, const std::string &comment); diff --git a/xs/src/libslic3r/PlaceholderParser.cpp b/xs/src/libslic3r/PlaceholderParser.cpp index 501349d5c..db444c8f9 100644 --- a/xs/src/libslic3r/PlaceholderParser.cpp +++ b/xs/src/libslic3r/PlaceholderParser.cpp @@ -97,23 +97,34 @@ PlaceholderParser::apply_env_variables() } } -void -PlaceholderParser::set(const std::string &key, const std::string &value) +void PlaceholderParser::set(const std::string &key, const std::string &value) { this->_single[key] = value; this->_multiple.erase(key); } -void -PlaceholderParser::set(const std::string &key, int value) +void PlaceholderParser::set(const std::string &key, int value) { std::ostringstream ss; ss << value; this->set(key, ss.str()); } -void -PlaceholderParser::set(const std::string &key, std::vector values) +void PlaceholderParser::set(const std::string &key, unsigned int value) +{ + std::ostringstream ss; + ss << value; + this->set(key, ss.str()); +} + +void PlaceholderParser::set(const std::string &key, double value) +{ + std::ostringstream ss; + ss << value; + this->set(key, ss.str()); +} + +void PlaceholderParser::set(const std::string &key, std::vector values) { if (values.empty()) { this->_multiple.erase(key); @@ -124,8 +135,7 @@ PlaceholderParser::set(const std::string &key, std::vector values) } } -std::string -PlaceholderParser::process(std::string str) const +std::string PlaceholderParser::process(std::string str) const { // replace single options, like [foo] for (t_strstr_map::const_iterator it = this->_single.begin(); it != this->_single.end(); ++it) { @@ -154,8 +164,7 @@ PlaceholderParser::process(std::string str) const return str; } -bool -PlaceholderParser::find_and_replace(std::string &source, std::string const &find, std::string const &replace) const +bool PlaceholderParser::find_and_replace(std::string &source, std::string const &find, std::string const &replace) const { bool found = false; for (std::string::size_type i = 0; (i = source.find(find, i)) != std::string::npos; ) { diff --git a/xs/src/libslic3r/PlaceholderParser.hpp b/xs/src/libslic3r/PlaceholderParser.hpp index 0cdf809d5..f736c14b0 100644 --- a/xs/src/libslic3r/PlaceholderParser.hpp +++ b/xs/src/libslic3r/PlaceholderParser.hpp @@ -15,7 +15,7 @@ typedef std::map > t_strstrs_map; class PlaceholderParser { - public: +public: t_strstr_map _single; t_strstrs_map _multiple; @@ -25,10 +25,12 @@ class PlaceholderParser void apply_env_variables(); void set(const std::string &key, const std::string &value); void set(const std::string &key, int value); + void set(const std::string &key, unsigned int value); + void set(const std::string &key, double value); void set(const std::string &key, std::vector values); std::string process(std::string str) const; - private: +private: bool find_and_replace(std::string &source, std::string const &find, std::string const &replace) const; }; diff --git a/xs/src/libslic3r/Point.hpp b/xs/src/libslic3r/Point.hpp index b59100bfb..43c131a44 100644 --- a/xs/src/libslic3r/Point.hpp +++ b/xs/src/libslic3r/Point.hpp @@ -32,7 +32,7 @@ class Point coord_t y; Point(coord_t _x = 0, coord_t _y = 0): x(_x), y(_y) {}; Point(int _x, int _y): x(_x), y(_y) {}; - Point(long long _x, long long _y): x(_x), y(_y) {}; // for Clipper + Point(long long _x, long long _y): x(coord_t(_x)), y(coord_t(_y)) {}; // for Clipper Point(double x, double y); static Point new_scale(coordf_t x, coordf_t y) { return Point(scale_(x), scale_(y)); diff --git a/xs/src/libslic3r/Polygon.hpp b/xs/src/libslic3r/Polygon.hpp index 2a595d36a..a938492ed 100644 --- a/xs/src/libslic3r/Polygon.hpp +++ b/xs/src/libslic3r/Polygon.hpp @@ -93,6 +93,23 @@ inline void polygons_rotate(Polygons &polys, double angle) p->rotate(angle); } +inline Points to_points(const Polygon &poly) +{ + return poly.points; +} + +inline Points to_points(const Polygons &polys) +{ + size_t n_points = 0; + for (size_t i = 0; i < polys.size(); ++ i) + n_points += polys[i].points.size(); + Points points; + points.reserve(n_points); + for (const Polygon &poly : polys) + append(points, poly.points); + return points; +} + inline Lines to_lines(const Polygon &poly) { Lines lines; @@ -179,7 +196,7 @@ namespace boost { namespace polygon { } // Get the winding direction of the polygon - static inline winding_direction winding(const Slic3r::Polygon& t) { + static inline winding_direction winding(const Slic3r::Polygon& /* t */) { return unknown_winding; } }; @@ -220,8 +237,8 @@ namespace boost { namespace polygon { } //don't worry about these, just return false from them - static inline bool clean(const Slic3r::Polygons& polygon_set) { return false; } - static inline bool sorted(const Slic3r::Polygons& polygon_set) { return false; } + static inline bool clean(const Slic3r::Polygons& /* polygon_set */) { return false; } + static inline bool sorted(const Slic3r::Polygons& /* polygon_set */) { return false; } }; template <> diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index cafbd8037..6c8d13dec 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -30,7 +30,7 @@ Print::~Print() void Print::clear_objects() { - for (int i = this->objects.size()-1; i >= 0; --i) + for (int i = int(this->objects.size())-1; i >= 0; --i) this->delete_object(i); this->clear_regions(); @@ -255,70 +255,65 @@ Print::step_done(PrintObjectStep step) const } // returns 0-based indices of used extruders -std::set -Print::object_extruders() const +std::vector Print::object_extruders() const { - std::set extruders; + std::vector extruders; FOREACH_REGION(this, region) { // these checks reflect the same logic used in the GUI for enabling/disabling // extruder selection fields if ((*region)->config.perimeters.value > 0 || this->config.brim_width.value > 0) - extruders.insert((*region)->config.perimeter_extruder - 1); - + extruders.push_back((*region)->config.perimeter_extruder - 1); if ((*region)->config.fill_density.value > 0) - extruders.insert((*region)->config.infill_extruder - 1); - + extruders.push_back((*region)->config.infill_extruder - 1); if ((*region)->config.top_solid_layers.value > 0 || (*region)->config.bottom_solid_layers.value > 0) - extruders.insert((*region)->config.solid_infill_extruder - 1); + extruders.push_back((*region)->config.solid_infill_extruder - 1); } + std::sort(extruders.begin(), extruders.end()); + extruders.erase(std::unique(extruders.begin(), extruders.end()), extruders.end()); return extruders; } // returns 0-based indices of used extruders -std::set -Print::support_material_extruders() const +std::vector Print::support_material_extruders() const { - std::set extruders; + std::vector extruders; bool support_uses_current_extruder = false; - FOREACH_OBJECT(this, object) { - if ((*object)->has_support_material()) { - if ((*object)->config.support_material_extruder == 0) + for (PrintObject *object : this->objects) { + if (object->has_support_material()) { + if (object->config.support_material_extruder == 0) support_uses_current_extruder = true; else - extruders.insert((*object)->config.support_material_extruder - 1); - if ((*object)->config.support_material_interface_extruder == 0) + extruders.push_back(object->config.support_material_extruder - 1); + if (object->config.support_material_interface_extruder == 0) support_uses_current_extruder = true; else - extruders.insert((*object)->config.support_material_interface_extruder - 1); + extruders.push_back(object->config.support_material_interface_extruder - 1); } } - if (support_uses_current_extruder) { + if (support_uses_current_extruder) // Add all object extruders to the support extruders as it is not know which one will be used to print supports. - std::set object_extruders = this->object_extruders(); - extruders.insert(object_extruders.begin(), object_extruders.end()); - } + append(extruders, this->object_extruders()); + std::sort(extruders.begin(), extruders.end()); + extruders.erase(std::unique(extruders.begin(), extruders.end()), extruders.end()); return extruders; } // returns 0-based indices of used extruders -std::set -Print::extruders() const +std::vector Print::extruders() const { - std::set extruders = this->object_extruders(); - - std::set s_extruders = this->support_material_extruders(); - extruders.insert(s_extruders.begin(), s_extruders.end()); - + std::vector extruders = this->object_extruders(); + append(extruders, this->support_material_extruders()); + std::sort(extruders.begin(), extruders.end()); + extruders.erase(std::unique(extruders.begin(), extruders.end()), extruders.end()); return extruders; } -void -Print::_simplify_slices(double distance) +void Print::_simplify_slices(double distance) { FOREACH_OBJECT(this, object) { FOREACH_LAYER(*object, layer) { @@ -330,23 +325,17 @@ Print::_simplify_slices(double distance) } } -double -Print::max_allowed_layer_height() const +double Print::max_allowed_layer_height() const { - std::vector nozzle_diameter; - - std::set extruders = this->extruders(); - for (std::set::const_iterator e = extruders.begin(); e != extruders.end(); ++e) { - nozzle_diameter.push_back(this->config.nozzle_diameter.get_at(*e)); - } - - return *std::max_element(nozzle_diameter.begin(), nozzle_diameter.end()); + double nozzle_diameter_max = 0.; + for (unsigned int extruder_id : this->extruders()) + nozzle_diameter_max = std::max(nozzle_diameter_max, this->config.nozzle_diameter.get_at(extruder_id)); + return nozzle_diameter_max; } /* Caller is responsible for supplying models whose objects don't collide and have explicit instance positions */ -void -Print::add_model_object(ModelObject* model_object, int idx) +void Print::add_model_object(ModelObject* model_object, int idx) { DynamicPrintConfig object_config = model_object->config; // clone object_config.normalize(); @@ -617,9 +606,9 @@ Print::validate() const convex_hull = offset(convex_hull, scale_(this->config.extruder_clearance_radius.value)/2, jtRound, scale_(0.1)).front(); // now we check that no instance of convex_hull intersects any of the previously checked object instances - for (Points::const_iterator copy = object->_shifted_copies.begin(); copy != object->_shifted_copies.end(); ++copy) { + for (const Point © : object->_shifted_copies) { Polygon p = convex_hull; - p.translate(*copy); + p.translate(copy); if (! intersection(a, p).empty()) return "Some objects are too close; your extruder will collide with them."; polygons_append(a, p); @@ -654,13 +643,13 @@ Print::validate() const { // find the smallest nozzle diameter - std::set extruders = this->extruders(); + std::vector extruders = this->extruders(); if (extruders.empty()) return "The supplied settings will cause an empty print."; - std::set nozzle_diameters; - for (std::set::iterator it = extruders.begin(); it != extruders.end(); ++it) - nozzle_diameters.insert(this->config.nozzle_diameter.get_at(*it)); + std::vector nozzle_diameters; + for (unsigned int extruder_id : extruders) + nozzle_diameters.push_back(this->config.nozzle_diameter.get_at(extruder_id)); double min_nozzle_diameter = *std::min_element(nozzle_diameters.begin(), nozzle_diameters.end()); FOREACH_OBJECT(this, i_object) { diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index e1f386fc9..9ad45bbcd 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -117,7 +117,7 @@ public: ModelObject* model_object() { return this->_model_object; } const ModelObject* model_object() const { return this->_model_object; } - Points copies() const { return this->_copies; } + const Points& copies() const { return this->_copies; } bool add_copy(const Pointf &point); bool delete_last_copy(); bool delete_all_copies(); @@ -249,9 +249,9 @@ public: Flow brim_flow() const; Flow skirt_flow() const; - std::set object_extruders() const; - std::set support_material_extruders() const; - std::set extruders() const; + std::vector object_extruders() const; + std::vector support_material_extruders() const; + std::vector extruders() const; void _simplify_slices(double distance); double max_allowed_layer_height() const; bool has_support_material() const; diff --git a/xs/src/libslic3r/Slicing.cpp b/xs/src/libslic3r/Slicing.cpp index afc0f3f9c..0348af0f9 100644 --- a/xs/src/libslic3r/Slicing.cpp +++ b/xs/src/libslic3r/Slicing.cpp @@ -44,7 +44,7 @@ SlicingParameters SlicingParameters::create_from_config( const PrintConfig &print_config, const PrintObjectConfig &object_config, coordf_t object_height, - const std::set &object_extruders) + const std::vector &object_extruders) { coordf_t first_layer_height = (object_config.first_layer_height.value <= 0) ? object_config.layer_height.value : @@ -84,9 +84,9 @@ SlicingParameters SlicingParameters::create_from_config( params.min_layer_height = std::max(params.min_layer_height, min_layer_height_from_nozzle(print_config, 0)); params.max_layer_height = std::min(params.max_layer_height, max_layer_height_from_nozzle(print_config, 0)); } else { - for (std::set::const_iterator it_extruder = object_extruders.begin(); it_extruder != object_extruders.end(); ++ it_extruder) { - params.min_layer_height = std::max(params.min_layer_height, min_layer_height_from_nozzle(print_config, *it_extruder)); - params.max_layer_height = std::min(params.max_layer_height, max_layer_height_from_nozzle(print_config, *it_extruder)); + for (unsigned int extruder_id : object_extruders) { + params.min_layer_height = std::max(params.min_layer_height, min_layer_height_from_nozzle(print_config, extruder_id)); + params.max_layer_height = std::min(params.max_layer_height, max_layer_height_from_nozzle(print_config, extruder_id)); } } params.min_layer_height = std::min(params.min_layer_height, params.layer_height); @@ -113,8 +113,8 @@ SlicingParameters SlicingParameters::create_from_config( //FIXME It is expected, that the 1st layer of the object is printed with a bridging flow over a full raft. Shall it not be vice versa? coordf_t average_object_extruder_dmr = 0.; if (! object_extruders.empty()) { - for (std::set::const_iterator it_extruder = object_extruders.begin(); it_extruder != object_extruders.end(); ++ it_extruder) - average_object_extruder_dmr += print_config.nozzle_diameter.get_at(*it_extruder); + for (unsigned int extruder_id : object_extruders) + average_object_extruder_dmr += print_config.nozzle_diameter.get_at(extruder_id); average_object_extruder_dmr /= coordf_t(object_extruders.size()); } params.first_object_layer_height = average_object_extruder_dmr; diff --git a/xs/src/libslic3r/Slicing.hpp b/xs/src/libslic3r/Slicing.hpp index 0b48443b6..3ed520e96 100644 --- a/xs/src/libslic3r/Slicing.hpp +++ b/xs/src/libslic3r/Slicing.hpp @@ -26,7 +26,7 @@ struct SlicingParameters const PrintConfig &print_config, const PrintObjectConfig &object_config, coordf_t object_height, - const std::set &object_extruders); + const std::vector &object_extruders); // Has any raft layers? bool has_raft() const { return raft_layers() > 0; } diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp index 170afbd00..de2a4bc80 100644 --- a/xs/src/libslic3r/SupportMaterial.cpp +++ b/xs/src/libslic3r/SupportMaterial.cpp @@ -145,32 +145,10 @@ PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object m_print_config (&object->print()->config), m_object_config (&object->config), m_slicing_params (slicing_params), - - m_first_layer_flow (Flow::new_from_config_width( - frSupportMaterial, - // The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution. - (object->print()->config.first_layer_extrusion_width.value > 0) ? object->print()->config.first_layer_extrusion_width : object->config.support_material_extrusion_width, - float(object->print()->config.nozzle_diameter.get_at(object->config.support_material_extruder-1)), - float(slicing_params.first_print_layer_height), - false)), - m_support_material_flow (Flow::new_from_config_width( - frSupportMaterial, - // The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution. - (object->config.support_material_extrusion_width.value > 0) ? object->config.support_material_extrusion_width : object->config.extrusion_width, - // if object->config.support_material_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component. - float(object->print()->config.nozzle_diameter.get_at(object->config.support_material_extruder-1)), - float(slicing_params.layer_height), - false)), - m_support_material_interface_flow(Flow::new_from_config_width( - frSupportMaterialInterface, - // The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution. - (object->config.support_material_extrusion_width > 0) ? object->config.support_material_extrusion_width : object->config.extrusion_width, - // if object->config.support_material_interface_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component. - float(object->print()->config.nozzle_diameter.get_at(object->config.support_material_interface_extruder-1)), - float(slicing_params.layer_height), - false)), - - m_support_layer_height_min (0.01) + m_first_layer_flow (support_material_1st_layer_flow(object, float(slicing_params.first_print_layer_height))), + m_support_material_flow (support_material_flow(object, float(slicing_params.layer_height))), + m_support_material_interface_flow(support_material_interface_flow(object, float(slicing_params.layer_height))), + m_support_layer_height_min(0.01) { // Calculate a minimum support layer height as a minimum over all extruders, but not smaller than 10um. m_support_layer_height_min = 1000000.; diff --git a/xs/src/libslic3r/SurfaceCollection.hpp b/xs/src/libslic3r/SurfaceCollection.hpp index 22c26936e..41e4c81b9 100644 --- a/xs/src/libslic3r/SurfaceCollection.hpp +++ b/xs/src/libslic3r/SurfaceCollection.hpp @@ -9,7 +9,7 @@ namespace Slic3r { class SurfaceCollection { - public: +public: Surfaces surfaces; SurfaceCollection() {}; diff --git a/xs/src/perlglue.cpp b/xs/src/perlglue.cpp index 571f4664b..f814b3de3 100644 --- a/xs/src/perlglue.cpp +++ b/xs/src/perlglue.cpp @@ -14,15 +14,10 @@ REGISTER_CLASS(ExtrusionEntityCollection, "ExtrusionPath::Collection"); REGISTER_CLASS(ExtrusionSimulator, "ExtrusionSimulator"); REGISTER_CLASS(Filler, "Filler"); REGISTER_CLASS(Flow, "Flow"); -REGISTER_CLASS(AvoidCrossingPerimeters, "GCode::AvoidCrossingPerimeters"); REGISTER_CLASS(CoolingBuffer, "GCode::CoolingBuffer"); -REGISTER_CLASS(OozePrevention, "GCode::OozePrevention"); -REGISTER_CLASS(SpiralVase, "GCode::SpiralVase"); -REGISTER_CLASS(Wipe, "GCode::Wipe"); REGISTER_CLASS(GCode, "GCode"); REGISTER_CLASS(GCodeSender, "GCode::Sender"); REGISTER_CLASS(GCodeWriter, "GCode::Writer"); -REGISTER_CLASS(GCodePressureEqualizer, "GCode::PressureEqualizer"); REGISTER_CLASS(Layer, "Layer"); REGISTER_CLASS(SupportLayer, "Layer::Support"); REGISTER_CLASS(LayerRegion, "Layer::Region"); diff --git a/xs/src/xsinit.h b/xs/src/xsinit.h index 79abc2f54..987912433 100644 --- a/xs/src/xsinit.h +++ b/xs/src/xsinit.h @@ -1,6 +1,11 @@ #ifndef _xsinit_h_ #define _xsinit_h_ +#ifdef _MSC_VER +// Disable some obnoxious warnings given by Visual Studio with the default warning level 4. +#pragma warning(disable: 4100 4127 4189 4244 4267 4700 4702 4800) +#endif + // undef some macros set by Perl which cause compilation errors on Win32 #undef read #undef seekdir diff --git a/xs/xsp/ExtrusionLoop.xsp b/xs/xsp/ExtrusionLoop.xsp index 85a204381..6784fcbbe 100644 --- a/xs/xsp/ExtrusionLoop.xsp +++ b/xs/xsp/ExtrusionLoop.xsp @@ -29,9 +29,6 @@ %code{% RETVAL = THIS->has_overhang_point(*point); %}; ExtrusionRole role() const; ExtrusionLoopRole loop_role() const; - bool is_perimeter(); - bool is_infill(); - bool is_solid_infill(); Polygons polygons_covered_by_width(); Polygons polygons_covered_by_spacing(); %{ diff --git a/xs/xsp/ExtrusionMultiPath.xsp b/xs/xsp/ExtrusionMultiPath.xsp index d835e76fb..5dd938245 100644 --- a/xs/xsp/ExtrusionMultiPath.xsp +++ b/xs/xsp/ExtrusionMultiPath.xsp @@ -16,9 +16,6 @@ void append(ExtrusionPath* path) %code{% THIS->paths.push_back(*path); %}; double length(); - bool is_perimeter(); - bool is_infill(); - bool is_solid_infill(); Polygons polygons_covered_by_width(); Polygons polygons_covered_by_spacing(); Clone polyline() diff --git a/xs/xsp/ExtrusionPath.xsp b/xs/xsp/ExtrusionPath.xsp index ff4316d44..078e6fe72 100644 --- a/xs/xsp/ExtrusionPath.xsp +++ b/xs/xsp/ExtrusionPath.xsp @@ -23,10 +23,8 @@ void simplify(double tolerance); double length(); ExtrusionRole role() const; - bool is_perimeter(); - bool is_infill(); - bool is_solid_infill(); - bool is_bridge(); + bool is_bridge() + %code{% RETVAL = is_bridge(THIS->role()); %}; Polygons polygons_covered_by_width(); Polygons polygons_covered_by_spacing(); %{ diff --git a/xs/xsp/GCode.xsp b/xs/xsp/GCode.xsp index 8d2856b13..f634958f2 100644 --- a/xs/xsp/GCode.xsp +++ b/xs/xsp/GCode.xsp @@ -4,176 +4,45 @@ #include #include "libslic3r/GCode.hpp" #include "libslic3r/GCode/CoolingBuffer.hpp" -#include "libslic3r/GCode/SpiralVase.hpp" %} -%name{Slic3r::GCode::AvoidCrossingPerimeters} class AvoidCrossingPerimeters { - AvoidCrossingPerimeters(); - ~AvoidCrossingPerimeters(); - - void init_external_mp(ExPolygons islands); - void init_layer_mp(ExPolygons islands); - Clone travel_to(GCode* gcode, Point* point) - %code{% RETVAL = THIS->travel_to(*gcode, *point); %}; - - bool use_external_mp() - %code{% RETVAL = THIS->use_external_mp; %}; - void set_use_external_mp(bool value) - %code{% THIS->use_external_mp = value; %}; - - bool use_external_mp_once() - %code{% RETVAL = THIS->use_external_mp_once; %}; - void set_use_external_mp_once(bool value) - %code{% THIS->use_external_mp_once = value; %}; - - bool disable_once() - %code{% RETVAL = THIS->disable_once; %}; - void set_disable_once(bool value) - %code{% THIS->disable_once = value; %}; -}; - -%name{Slic3r::GCode::OozePrevention} class OozePrevention { - OozePrevention(); - ~OozePrevention(); - - bool enable() - %code{% RETVAL = THIS->enable; %}; - void set_enable(bool value) - %code{% THIS->enable = value; %}; - - Points standby_points() - %code{% RETVAL = THIS->standby_points; %}; - void set_standby_points(Points points) - %code{% THIS->standby_points = points; %}; - - std::string pre_toolchange(GCode* gcodegen) - %code{% RETVAL = THIS->pre_toolchange(*gcodegen); %}; - std::string post_toolchange(GCode* gcodegen) - %code{% RETVAL = THIS->post_toolchange(*gcodegen); %}; -}; - -%name{Slic3r::GCode::Wipe} class Wipe { - Wipe(); - ~Wipe(); - - 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; %}; - void set_enable(bool value) - %code{% THIS->enable = value; %}; - - Ref path() - %code{% RETVAL = &(THIS->path); %}; - void set_path(Polyline* value) - %code{% THIS->path = *value; %}; -}; - %name{Slic3r::GCode::CoolingBuffer} class CoolingBuffer { CoolingBuffer(GCode* gcode) %code{% RETVAL = new CoolingBuffer(*gcode); %}; ~CoolingBuffer(); Ref gcodegen(); - std::string append(std::string gcode, std::string obj_id, size_t layer_id, float print_z); + std::string append(std::string gcode, size_t object_id, size_t layer_id, bool support_layer); std::string flush(); }; -%name{Slic3r::GCode::SpiralVase} class SpiralVase { - SpiralVase(StaticPrintConfig* config) - %code{% RETVAL = new SpiralVase(*dynamic_cast(config)); %}; - ~SpiralVase(); - - bool enable() - %code{% RETVAL = THIS->enable; %}; - void set_enable(bool enable) - %code{% THIS->enable = enable; %}; - std::string process_layer(std::string gcode); -}; - %name{Slic3r::GCode} class GCode { GCode(); ~GCode(); - - Ref origin() - %code{% RETVAL = &(THIS->origin); %}; - - Ref config() - %code{% RETVAL = &(THIS->config); %}; - - Ref writer() - %code{% RETVAL = &(THIS->writer); %}; - - Ref placeholder_parser() - %code{% RETVAL = THIS->placeholder_parser; %}; - void set_placeholder_parser(PlaceholderParser* ptr) - %code{% THIS->placeholder_parser = ptr; %}; - - Ref ooze_prevention() - %code{% RETVAL = &(THIS->ooze_prevention); %}; - - Ref wipe() - %code{% RETVAL = &(THIS->wipe); %}; - - Ref avoid_crossing_perimeters() - %code{% RETVAL = &(THIS->avoid_crossing_perimeters); %}; - - bool enable_loop_clipping() - %code{% RETVAL = THIS->enable_loop_clipping; %}; - void set_enable_loop_clipping(bool value) - %code{% THIS->enable_loop_clipping = value; %}; - - bool enable_cooling_markers() - %code{% RETVAL = THIS->enable_cooling_markers; %}; - void set_enable_cooling_markers(bool value) - %code{% THIS->enable_cooling_markers = value; %}; - - bool enable_extrusion_role_markers() - %code{% RETVAL = THIS->enable_extrusion_role_markers; %}; - void set_enable_extrusion_role_markers(bool value) - %code{% THIS->enable_extrusion_role_markers = value; %}; + std::string do_export(Print *print, const char *path) + %code{% + FILE *file = fopen(path, "wb"); + if (file == nullptr) { + RETVAL = std::string("Failed to open ") + path + " for writing."; + } else { + THIS->do_export(file, *print); + fclose(file); + RETVAL = std::string(); + } + %}; - int layer_count() - %code{% RETVAL = THIS->layer_count; %}; - void set_layer_count(int value) - %code{% THIS->layer_count = value; %}; - - int layer_index() - %code{% RETVAL = THIS->layer_index; %}; - void set_layer_index(int value) - %code{% THIS->layer_index = value; %}; - - bool has_layer() - %code{% RETVAL = THIS->layer != NULL; %}; - Ref layer() - %code{% RETVAL = THIS->layer; %}; - void set_layer(Layer* ptr) - %code{% THIS->layer = ptr; %}; - - bool first_layer() - %code{% RETVAL = THIS->first_layer; %}; - void set_first_layer(bool value) - %code{% THIS->first_layer = value; %}; - - float elapsed_time() - %code{% RETVAL = THIS->elapsed_time; %}; - void set_elapsed_time(float value) - %code{% THIS->elapsed_time = value; %}; - - bool last_pos_defined(); + Ref origin() + %code{% RETVAL = &(THIS->origin()); %}; + void set_origin(Pointf* pointf) + %code{% THIS->set_origin(*pointf); %}; Ref last_pos() %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; %}; - + + unsigned int layer_count() const; + void set_layer_count(unsigned int value); + float elapsed_time() const; + void set_elapsed_time(float value); + void apply_print_config(StaticPrintConfig* print_config) %code{% if (const PrintConfig* config = dynamic_cast(print_config)) { @@ -182,40 +51,7 @@ CONFESS("A PrintConfig object was not supplied to apply_print_config()"); } %}; - void set_extruders(std::vector extruder_ids); - void set_origin(Pointf* pointf) - %code{% THIS->set_origin(*pointf); %}; - std::string preamble(); - std::string change_layer(Layer* layer) - %code{% RETVAL = THIS->change_layer(*layer); %}; - %name{extrude_loop} std::string extrude(ExtrusionLoop* loop, std::string description = "", double speed = -1) - %code{% RETVAL = THIS->extrude(*loop, description, speed); %}; - %name{extrude_multipath} std::string extrude(ExtrusionMultiPath* multipath, std::string description = "", double speed = -1) - %code{% RETVAL = THIS->extrude(*multipath, description, speed); %}; - %name{extrude_path} std::string extrude(ExtrusionPath* path, std::string description = "", double speed = -1) - %code{% RETVAL = THIS->extrude(*path, description, speed); %}; - std::string extrude_support(ExtrusionEntityCollection *support_fills, unsigned int extruder_id); - std::string travel_to(Point* point, ExtrusionRole role, std::string comment) - %code{% RETVAL = THIS->travel_to(*point, role, comment); %}; - bool needs_retraction(Polyline* travel, ExtrusionRole role = erNone) - %code{% RETVAL = THIS->needs_retraction(*travel, role); %}; - std::string retract(bool toolchange = false); - std::string unretract(); - std::string set_extruder(unsigned int extruder_id); - Clone point_to_gcode(Point* point) - %code{% RETVAL = THIS->point_to_gcode(*point); %}; - -%{ -std::string -GCode::extrude(entity, description, speed) - SV* entity - std::string description; - double speed; - CODE: - ExtrusionEntity* e = (ExtrusionEntity *)SvIV((SV*)SvRV( entity )); - RETVAL = THIS->extrude(*e, description, speed); - OUTPUT: - RETVAL -%} + Ref config() + %code{% RETVAL = const_cast(dynamic_cast(&THIS->config())); %}; }; diff --git a/xs/xsp/GCodePressureEqualizer.xsp b/xs/xsp/GCodePressureEqualizer.xsp deleted file mode 100644 index 39372e0e0..000000000 --- a/xs/xsp/GCodePressureEqualizer.xsp +++ /dev/null @@ -1,32 +0,0 @@ -%module{Slic3r::XS}; - -%{ -#include -#include "libslic3r/GCode/PressureEqualizer.hpp" -%} - -%name{Slic3r::GCode::PressureEqualizer} class GCodePressureEqualizer { - GCodePressureEqualizer(StaticPrintConfig* config) - %code%{ RETVAL = new GCodePressureEqualizer(dynamic_cast(config)); %}; - ~GCodePressureEqualizer(); - - void reset(); - - // Process a next batch of G-code lines. Flush the internal buffers if asked for. -// const char* process(const char *szGCode, bool flush); -// std::string process(const char *szGCode, bool flush) -// %code{% const char *out = THIS->process(szGCode, flush); RETVAL = (out == NULL) ? "" : std::string(out); %}; - -%{ - -SV* -GCodePressureEqualizer::process(const char *szGCode, bool flush) - CODE: - const char *out = THIS->process(szGCode, flush); - RETVAL = newSVpv(out, THIS->get_output_buffer_length()); - OUTPUT: - RETVAL - -%} - -}; diff --git a/xs/xsp/GCodeWriter.xsp b/xs/xsp/GCodeWriter.xsp index a51e3a44b..094dcf765 100644 --- a/xs/xsp/GCodeWriter.xsp +++ b/xs/xsp/GCodeWriter.xsp @@ -54,9 +54,8 @@ GCodeWriter::extruders() AV* av = newAV(); av_fill(av, THIS->extruders.size()-1); int i = 0; - for (std::map::iterator it = THIS->extruders.begin(); it != THIS->extruders.end(); ++it) { - av_store(av, i++, perl_to_SV_ref(it->second)); - } + for (const Extruder &extruder : THIS->extruders) + av_store(av, i++, perl_to_SV_ref(const_cast(extruder))); RETVAL = newRV_noinc((SV*)av); OUTPUT: RETVAL diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index 618347b50..2a60f101b 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -195,30 +195,6 @@ _constant() void set_step_started(PrintStep step) %code%{ THIS->state.set_started(step); %}; - std::vector object_extruders() - %code%{ - std::set extruders = THIS->object_extruders(); - RETVAL.reserve(extruders.size()); - for (std::set::const_iterator e = extruders.begin(); e != extruders.end(); ++e) { - RETVAL.push_back(*e); - } - %}; - std::vector support_material_extruders() - %code%{ - std::set extruders = THIS->support_material_extruders(); - RETVAL.reserve(extruders.size()); - for (std::set::const_iterator e = extruders.begin(); e != extruders.end(); ++e) { - RETVAL.push_back(*e); - } - %}; - std::vector extruders() - %code%{ - std::set extruders = THIS->extruders(); - RETVAL.reserve(extruders.size()); - for (std::set::const_iterator e = extruders.begin(); e != extruders.end(); ++e) { - RETVAL.push_back(*e); - } - %}; void clear_filament_stats() %code%{ THIS->filament_stats.clear(); diff --git a/xs/xsp/my.map b/xs/xsp/my.map index 0d28d1ac2..8fd011c7d 100644 --- a/xs/xsp/my.map +++ b/xs/xsp/my.map @@ -185,26 +185,10 @@ PlaceholderParser* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T Clone O_OBJECT_SLIC3R_T -AvoidCrossingPerimeters* O_OBJECT_SLIC3R -Ref O_OBJECT_SLIC3R_T -Clone O_OBJECT_SLIC3R_T - -Wipe* O_OBJECT_SLIC3R -Ref O_OBJECT_SLIC3R_T -Clone O_OBJECT_SLIC3R_T - -OozePrevention* O_OBJECT_SLIC3R -Ref O_OBJECT_SLIC3R_T -Clone O_OBJECT_SLIC3R_T - CoolingBuffer* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T Clone O_OBJECT_SLIC3R_T -SpiralVase* O_OBJECT_SLIC3R -Ref O_OBJECT_SLIC3R_T -Clone O_OBJECT_SLIC3R_T - GCode* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T Clone O_OBJECT_SLIC3R_T @@ -221,10 +205,6 @@ GCodeWriter* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T Clone O_OBJECT_SLIC3R_T -GCodePressureEqualizer* O_OBJECT_SLIC3R -Ref O_OBJECT_SLIC3R_T -Clone O_OBJECT_SLIC3R_T - BridgeDetector* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T Clone O_OBJECT_SLIC3R_T diff --git a/xs/xsp/typemap.xspt b/xs/xsp/typemap.xspt index 9a0efb8d8..129ca8eb9 100644 --- a/xs/xsp/typemap.xspt +++ b/xs/xsp/typemap.xspt @@ -105,9 +105,6 @@ %typemap{GCodeSender*}; %typemap{Ref}{simple}; %typemap{Clone}{simple}; -%typemap{GCodePressureEqualizer*}; -%typemap{Ref}{simple}; -%typemap{Clone}{simple}; %typemap{BridgeDetector*}; %typemap{Ref}{simple}; %typemap{Clone}{simple}; @@ -152,18 +149,6 @@ %typemap{Ref}{simple}; %typemap{Clone}{simple}; -%typemap{AvoidCrossingPerimeters*}; -%typemap{Ref}{simple}; -%typemap{Clone}{simple}; - -%typemap{Wipe*}; -%typemap{Ref}{simple}; -%typemap{Clone}{simple}; - -%typemap{OozePrevention*}; -%typemap{Ref}{simple}; -%typemap{Clone}{simple}; - %typemap{CoolingBuffer*}; %typemap{Ref}{simple}; %typemap{Clone}{simple};