Browse Source

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.

bubnikv 7 years ago
parent
commit
e90279c513

+ 0 - 4
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 {};

+ 10 - 27
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

+ 0 - 712
lib/Slic3r/Print/GCode.pm

@@ -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;

+ 13 - 3
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;
 }

+ 0 - 2
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:

+ 6 - 6
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;

+ 0 - 1
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

+ 0 - 3
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

+ 3 - 0
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 {

+ 2 - 2
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<int>
 {
     public:
     ConfigOptionInt() : ConfigOptionSingle<int>(0) {};
-    ConfigOptionInt(double _value) : ConfigOptionSingle<int>(_value) {};
+    ConfigOptionInt(double _value) : ConfigOptionSingle<int>(int(floor(_value + 0.5))) {};
     
     int getInt() const { return this->value; };
     void setInt(int val) { this->value = val; };

Some files were not shown because too many files changed in this diff