mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-11-02 20:51:23 -07:00 
			
		
		
		
	Merge branch 'master' into visilibity
Conflicts: lib/Slic3r/GCode.pm xs/MANIFEST
This commit is contained in:
		
						commit
						7215e66a6a
					
				
					 68 changed files with 1258 additions and 825 deletions
				
			
		
							
								
								
									
										2
									
								
								Build.PL
									
										
									
									
									
								
							
							
						
						
									
										2
									
								
								Build.PL
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -134,7 +134,7 @@ EOF
 | 
			
		|||
                system './xs/Build', 'distclean';
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        my $res = system $cpanm, @cpanm_args, '--reinstall', './xs';
 | 
			
		||||
        my $res = system $cpanm, @cpanm_args, '--reinstall', '--verbose', './xs';
 | 
			
		||||
        if ($res != 0) {
 | 
			
		||||
            die "The XS/C++ code failed to compile, aborting\n";
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -170,6 +170,9 @@ The author of the Silk icon set is Mark James.
 | 
			
		|||
                            (default: 50)
 | 
			
		||||
        --support-material-speed
 | 
			
		||||
                            Speed of support material print moves in mm/s (default: 60)
 | 
			
		||||
        --support-material-interface-speed
 | 
			
		||||
                            Speed of support material interface print moves in mm/s or % over support material
 | 
			
		||||
                            speed (default: 100%)
 | 
			
		||||
        --bridge-speed      Speed of bridge print moves in mm/s (default: 60)
 | 
			
		||||
        --gap-fill-speed    Speed of gap fill print moves in mm/s (default: 20)
 | 
			
		||||
        --first-layer-speed Speed of print moves for bottom layer, expressed either as an absolute
 | 
			
		||||
| 
						 | 
				
			
			@ -216,7 +219,7 @@ The author of the Silk icon set is Mark James.
 | 
			
		|||
                            home X axis [G28 X], disable motors [M84]).
 | 
			
		||||
        --layer-gcode       Load layer-change G-code from the supplied file (default: nothing).
 | 
			
		||||
        --toolchange-gcode  Load tool-change G-code from the supplied file (default: nothing).
 | 
			
		||||
        --randomize-start   Randomize starting point across layers (default: yes)
 | 
			
		||||
        --seam-position     Position of loop starting points (random/nearest/aligned, default: aligned).
 | 
			
		||||
        --external-perimeters-first Reverse perimeter order. (default: no)
 | 
			
		||||
        --spiral-vase       Experimental option to raise Z gradually when printing single-walled vases
 | 
			
		||||
                            (default: no)
 | 
			
		||||
| 
						 | 
				
			
			@ -233,10 +236,6 @@ The author of the Silk icon set is Mark James.
 | 
			
		|||
       Quality options (slower slicing):
 | 
			
		||||
        --extra-perimeters  Add more perimeters when needed (default: yes)
 | 
			
		||||
        --avoid-crossing-perimeters Optimize travel moves so that no perimeters are crossed (default: no)
 | 
			
		||||
        --start-perimeters-at-concave-points
 | 
			
		||||
                            Try to start perimeters at concave points if any (default: no)
 | 
			
		||||
        --start-perimeters-at-non-overhang
 | 
			
		||||
                            Try to start perimeters at non-overhang points if any (default: no)
 | 
			
		||||
        --thin-walls        Detect single-width walls (default: yes)
 | 
			
		||||
        --overhangs         Experimental option to use bridge flow, speed and fan for overhangs
 | 
			
		||||
                            (default: yes)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,7 +7,7 @@ use strict;
 | 
			
		|||
use warnings;
 | 
			
		||||
require v5.10;
 | 
			
		||||
 | 
			
		||||
our $VERSION = "1.1.3-dev";
 | 
			
		||||
our $VERSION = "1.1.4-dev";
 | 
			
		||||
 | 
			
		||||
our $debug = 0;
 | 
			
		||||
sub debugf {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,7 +8,8 @@ use List::Util qw(first max);
 | 
			
		|||
# cemetery of old config settings
 | 
			
		||||
our @Ignore = qw(duplicate_x duplicate_y multiply_x multiply_y support_material_tool acceleration
 | 
			
		||||
    adjust_overhang_flow standby_temperature scale rotate duplicate duplicate_grid
 | 
			
		||||
    rotate scale duplicate_grid);
 | 
			
		||||
    rotate scale duplicate_grid start_perimeters_at_concave_points start_perimeters_at_non_overhang
 | 
			
		||||
    randomize_start seal_position);
 | 
			
		||||
 | 
			
		||||
our $Options = print_config_def();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -140,6 +141,10 @@ sub _handle_legacy {
 | 
			
		|||
        $value *= 100;
 | 
			
		||||
        $value = "$value";  # force update of the PV value, workaround for bug https://rt.cpan.org/Ticket/Display.html?id=94110
 | 
			
		||||
    }
 | 
			
		||||
    if ($opt_key eq 'randomize_start' && $value) {
 | 
			
		||||
        $opt_key = 'seam_position';
 | 
			
		||||
        $value = 'random';
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    # For historical reasons, the world's full of configs having these very low values;
 | 
			
		||||
    # to avoid unexpected behavior we need to ignore them.  Banning these two hard-coded
 | 
			
		||||
| 
						 | 
				
			
			@ -330,7 +335,7 @@ sub validate {
 | 
			
		|||
        die "Can't make less than one perimeter when spiral vase mode is enabled\n"
 | 
			
		||||
            if $self->perimeters < 1;
 | 
			
		||||
        
 | 
			
		||||
        die "Spiral vase mode is not compatible with non-zero fill density\n"
 | 
			
		||||
        die "Spiral vase mode can only print hollow objects, so you need to set Fill density to 0\n"
 | 
			
		||||
            if $self->fill_density > 0;
 | 
			
		||||
        
 | 
			
		||||
        die "Spiral vase mode is not compatible with top solid layers\n"
 | 
			
		||||
| 
						 | 
				
			
			@ -338,11 +343,6 @@ sub validate {
 | 
			
		|||
        
 | 
			
		||||
        die "Spiral vase mode is not compatible with support material\n"
 | 
			
		||||
            if $self->support_material || $self->support_material_enforce_layers > 0;
 | 
			
		||||
        
 | 
			
		||||
        # This should be enforce automatically only on spiral layers and
 | 
			
		||||
        # done on the others
 | 
			
		||||
        die "Spiral vase mode is not compatible with retraction on layer change\n"
 | 
			
		||||
            if defined first { $_ } @{ $self->retract_layer_change };
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    # extrusion widths
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,4 +2,11 @@ package Slic3r::ExtrusionLoop;
 | 
			
		|||
use strict;
 | 
			
		||||
use warnings;
 | 
			
		||||
 | 
			
		||||
use parent qw(Exporter);
 | 
			
		||||
 | 
			
		||||
our @EXPORT_OK = qw(EXTRL_ROLE_DEFAULT EXTRL_ROLE_EXTERNAL_PERIMETER
 | 
			
		||||
    EXTRL_ROLE_CONTOUR_INTERNAL_PERIMETER);
 | 
			
		||||
our %EXPORT_TAGS = (roles => \@EXPORT_OK);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
1;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,10 +4,9 @@ use warnings;
 | 
			
		|||
 | 
			
		||||
use parent qw(Exporter);
 | 
			
		||||
 | 
			
		||||
our @EXPORT_OK = qw(EXTR_ROLE_PERIMETER EXTR_ROLE_EXTERNAL_PERIMETER 
 | 
			
		||||
    EXTR_ROLE_CONTOUR_INTERNAL_PERIMETER EXTR_ROLE_OVERHANG_PERIMETER
 | 
			
		||||
    EXTR_ROLE_FILL EXTR_ROLE_SOLIDFILL EXTR_ROLE_TOPSOLIDFILL EXTR_ROLE_BRIDGE 
 | 
			
		||||
    EXTR_ROLE_INTERNALBRIDGE EXTR_ROLE_SKIRT EXTR_ROLE_SUPPORTMATERIAL EXTR_ROLE_GAPFILL);
 | 
			
		||||
our @EXPORT_OK = qw(EXTR_ROLE_PERIMETER EXTR_ROLE_EXTERNAL_PERIMETER EXTR_ROLE_OVERHANG_PERIMETER
 | 
			
		||||
    EXTR_ROLE_FILL EXTR_ROLE_SOLIDFILL EXTR_ROLE_TOPSOLIDFILL EXTR_ROLE_GAPFILL EXTR_ROLE_BRIDGE 
 | 
			
		||||
    EXTR_ROLE_SKIRT EXTR_ROLE_SUPPORTMATERIAL EXTR_ROLE_SUPPORTMATERIAL_INTERFACE);
 | 
			
		||||
our %EXPORT_TAGS = (roles => \@EXPORT_OK);
 | 
			
		||||
 | 
			
		||||
1;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -207,6 +207,7 @@ sub make_fill {
 | 
			
		|||
            $h,
 | 
			
		||||
            $is_bridge,
 | 
			
		||||
            $layerm->id == 0,
 | 
			
		||||
            undef,
 | 
			
		||||
            $layerm->object,
 | 
			
		||||
        );
 | 
			
		||||
        
 | 
			
		||||
| 
						 | 
				
			
			@ -230,9 +231,7 @@ sub make_fill {
 | 
			
		|||
        $collection->append(
 | 
			
		||||
            map Slic3r::ExtrusionPath->new(
 | 
			
		||||
                polyline => $_,
 | 
			
		||||
                role => ($surface->surface_type == S_TYPE_INTERNALBRIDGE
 | 
			
		||||
                    ? EXTR_ROLE_INTERNALBRIDGE
 | 
			
		||||
                    : $is_bridge
 | 
			
		||||
                role => ($is_bridge
 | 
			
		||||
                        ? EXTR_ROLE_BRIDGE
 | 
			
		||||
                        : $is_solid
 | 
			
		||||
                            ? (($surface->surface_type == S_TYPE_TOP) ? EXTR_ROLE_TOPSOLIDFILL : EXTR_ROLE_SOLIDFILL)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,13 +2,14 @@ package Slic3r::GCode;
 | 
			
		|||
use Moo;
 | 
			
		||||
 | 
			
		||||
use List::Util qw(min max first);
 | 
			
		||||
use Slic3r::ExtrusionLoop ':roles';
 | 
			
		||||
use Slic3r::ExtrusionPath ':roles';
 | 
			
		||||
use Slic3r::Flow ':roles';
 | 
			
		||||
use Slic3r::Geometry qw(epsilon scale unscale scaled_epsilon points_coincide PI X Y B);
 | 
			
		||||
use Slic3r::Geometry::Clipper qw(union_ex offset_ex);
 | 
			
		||||
use Slic3r::Surface ':types';
 | 
			
		||||
 | 
			
		||||
has 'print_config'       => (is => 'ro', default => sub { Slic3r::Config::Print->new });
 | 
			
		||||
has 'config'             => (is => 'ro', default => sub { Slic3r::Config::Full->new });
 | 
			
		||||
has 'placeholder_parser' => (is => 'rw', default => sub { Slic3r::GCode::PlaceholderParser->new });
 | 
			
		||||
has 'standby_points'     => (is => 'rw');
 | 
			
		||||
has 'enable_loop_clipping' => (is => 'rw', default => sub {1});
 | 
			
		||||
| 
						 | 
				
			
			@ -16,19 +17,15 @@ has 'enable_wipe'        => (is => 'rw', default => sub {0});   # at least one e
 | 
			
		|||
has 'layer_count'        => (is => 'ro', required => 1 );
 | 
			
		||||
has '_layer_index'       => (is => 'rw', default => sub {-1});  # just a counter
 | 
			
		||||
has 'layer'              => (is => 'rw');
 | 
			
		||||
has 'region'             => (is => 'rw');
 | 
			
		||||
has '_layer_islands'     => (is => 'rw');
 | 
			
		||||
has '_upper_layer_islands'  => (is => 'rw');
 | 
			
		||||
has '_seam_position'     => (is => 'ro', default => sub { {} });  # $object => pos
 | 
			
		||||
has 'shift_x'            => (is => 'rw', default => sub {0} );
 | 
			
		||||
has 'shift_y'            => (is => 'rw', default => sub {0} );
 | 
			
		||||
has 'z'                  => (is => 'rw');
 | 
			
		||||
has 'speed'              => (is => 'rw');
 | 
			
		||||
has '_extrusion_axis'    => (is => 'rw');
 | 
			
		||||
has '_retract_lift'      => (is => 'rw');
 | 
			
		||||
has 'extruders'          => (is => 'ro', default => sub {{}});
 | 
			
		||||
has 'multiple_extruders' => (is => 'rw', default => sub {0});
 | 
			
		||||
has 'extruder'           => (is => 'rw');
 | 
			
		||||
has 'speeds'             => (is => 'lazy');  # mm/min
 | 
			
		||||
has 'external_mp'        => (is => 'rw');
 | 
			
		||||
has 'layer_mp'           => (is => 'rw');
 | 
			
		||||
has 'new_object'         => (is => 'rw', default => sub {0});
 | 
			
		||||
| 
						 | 
				
			
			@ -36,23 +33,14 @@ has 'straight_once'      => (is => 'rw', default => sub {1});
 | 
			
		|||
has 'elapsed_time'       => (is => 'rw', default => sub {0} );  # seconds
 | 
			
		||||
has 'lifted'             => (is => 'rw', default => sub {0} );
 | 
			
		||||
has 'last_pos'           => (is => 'rw', default => sub { Slic3r::Point->new(0,0) } );
 | 
			
		||||
has 'last_speed'         => (is => 'rw', default => sub {""});
 | 
			
		||||
has 'last_f'             => (is => 'rw', default => sub {""});
 | 
			
		||||
has 'last_fan_speed'     => (is => 'rw', default => sub {0});
 | 
			
		||||
has 'wipe_path'          => (is => 'rw');
 | 
			
		||||
 | 
			
		||||
sub BUILD {
 | 
			
		||||
    my ($self) = @_;
 | 
			
		||||
    
 | 
			
		||||
    $self->_extrusion_axis($self->print_config->get_extrusion_axis);
 | 
			
		||||
    $self->_retract_lift($self->print_config->retract_lift->[0]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub set_extruders {
 | 
			
		||||
    my ($self, $extruder_ids) = @_;
 | 
			
		||||
    my ($self, $extruder_ids, $print_config) = @_;
 | 
			
		||||
    
 | 
			
		||||
    foreach my $i (@$extruder_ids) {
 | 
			
		||||
        $self->extruders->{$i} = my $e = Slic3r::Extruder->new($i, $self->print_config);
 | 
			
		||||
        $self->extruders->{$i} = my $e = Slic3r::Extruder->new($i, $print_config);
 | 
			
		||||
        $self->enable_wipe(1) if $e->wipe;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
| 
						 | 
				
			
			@ -62,30 +50,6 @@ sub set_extruders {
 | 
			
		|||
    $self->multiple_extruders(max(@$extruder_ids) > 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub _build_speeds {
 | 
			
		||||
    my $self = shift;
 | 
			
		||||
    return {
 | 
			
		||||
        map { $_ => 60 * $self->print_config->get_value("${_}_speed") }
 | 
			
		||||
            qw(travel perimeter small_perimeter external_perimeter infill
 | 
			
		||||
                solid_infill top_solid_infill bridge gap_fill retract),
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# assign speeds to roles
 | 
			
		||||
my %role_speeds = (
 | 
			
		||||
    &EXTR_ROLE_PERIMETER                    => 'perimeter',
 | 
			
		||||
    &EXTR_ROLE_EXTERNAL_PERIMETER           => 'external_perimeter',
 | 
			
		||||
    &EXTR_ROLE_OVERHANG_PERIMETER           => 'bridge',
 | 
			
		||||
    &EXTR_ROLE_CONTOUR_INTERNAL_PERIMETER   => 'perimeter',
 | 
			
		||||
    &EXTR_ROLE_FILL                         => 'infill',
 | 
			
		||||
    &EXTR_ROLE_SOLIDFILL                    => 'solid_infill',
 | 
			
		||||
    &EXTR_ROLE_TOPSOLIDFILL                 => 'top_solid_infill',
 | 
			
		||||
    &EXTR_ROLE_BRIDGE                       => 'bridge',
 | 
			
		||||
    &EXTR_ROLE_INTERNALBRIDGE               => 'bridge',
 | 
			
		||||
    &EXTR_ROLE_SKIRT                        => 'perimeter',
 | 
			
		||||
    &EXTR_ROLE_GAPFILL                      => 'gap_fill',
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
sub set_shift {
 | 
			
		||||
    my ($self, @shift) = @_;
 | 
			
		||||
    
 | 
			
		||||
| 
						 | 
				
			
			@ -110,23 +74,23 @@ sub change_layer {
 | 
			
		|||
    # avoid computing islands and overhangs if they're not needed
 | 
			
		||||
    $self->_layer_islands($layer->islands);
 | 
			
		||||
    $self->_upper_layer_islands($layer->upper_layer ? $layer->upper_layer->islands : []);
 | 
			
		||||
    if ($self->print_config->avoid_crossing_perimeters) {
 | 
			
		||||
    if ($self->config->avoid_crossing_perimeters) {
 | 
			
		||||
        $self->layer_mp(Slic3r::MotionPlanner->new(
 | 
			
		||||
            union_ex([ map @$_, @{$layer->slices} ], 1),
 | 
			
		||||
        ));
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    my $gcode = "";
 | 
			
		||||
    if ($self->print_config->gcode_flavor =~ /^(?:makerware|sailfish)$/) {
 | 
			
		||||
    if ($self->config->gcode_flavor =~ /^(?:makerware|sailfish)$/) {
 | 
			
		||||
        $gcode .= sprintf "M73 P%s%s\n",
 | 
			
		||||
            int(99 * ($self->_layer_index / ($self->layer_count - 1))),
 | 
			
		||||
            ($self->print_config->gcode_comments ? ' ; update progress' : '');
 | 
			
		||||
            ($self->config->gcode_comments ? ' ; update progress' : '');
 | 
			
		||||
    }
 | 
			
		||||
    if ($self->print_config->first_layer_acceleration) {
 | 
			
		||||
    if ($self->config->first_layer_acceleration) {
 | 
			
		||||
        if ($layer->id == 0) {
 | 
			
		||||
            $gcode .= $self->set_acceleration($self->print_config->first_layer_acceleration);
 | 
			
		||||
            $gcode .= $self->set_acceleration($self->config->first_layer_acceleration);
 | 
			
		||||
        } elsif ($layer->id == 1) {
 | 
			
		||||
            $gcode .= $self->set_acceleration($self->print_config->default_acceleration);
 | 
			
		||||
            $gcode .= $self->set_acceleration($self->config->default_acceleration);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
| 
						 | 
				
			
			@ -140,7 +104,7 @@ sub move_z {
 | 
			
		|||
    
 | 
			
		||||
    my $gcode = "";
 | 
			
		||||
    
 | 
			
		||||
    $z += $self->print_config->z_offset;
 | 
			
		||||
    $z += $self->config->z_offset;
 | 
			
		||||
    my $current_z = $self->z;
 | 
			
		||||
    my $nominal_z = defined $current_z ? ($current_z - $self->lifted) : undef;
 | 
			
		||||
    
 | 
			
		||||
| 
						 | 
				
			
			@ -157,8 +121,7 @@ sub move_z {
 | 
			
		|||
            $current_z = $self->z;  # update current z in case retract() changed it
 | 
			
		||||
            $nominal_z = defined $current_z ? ($current_z - $self->lifted) : undef;
 | 
			
		||||
        }
 | 
			
		||||
        $self->speed('travel');
 | 
			
		||||
        $gcode .= $self->G0(undef, $z, 0, $comment || ('move to next layer (' . $self->layer->id . ')'))
 | 
			
		||||
        $gcode .= $self->G0(undef, $z, 0, $self->config->travel_speed*60, $comment || ('move to next layer (' . $self->layer->id . ')'))
 | 
			
		||||
            if !defined $current_z || abs($z - $nominal_z) > epsilon;
 | 
			
		||||
    } elsif ($z < $current_z) {
 | 
			
		||||
        # we're moving above the current nominal layer height and below the current actual one.
 | 
			
		||||
| 
						 | 
				
			
			@ -179,7 +142,7 @@ sub extrude {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
sub extrude_loop {
 | 
			
		||||
    my ($self, $loop, $description) = @_;
 | 
			
		||||
    my ($self, $loop, $description, $speed) = @_;
 | 
			
		||||
    
 | 
			
		||||
    # make a copy; don't modify the orientation of the original loop object otherwise
 | 
			
		||||
    # next copies (if any) would not detect the correct orientation
 | 
			
		||||
| 
						 | 
				
			
			@ -188,43 +151,39 @@ sub extrude_loop {
 | 
			
		|||
    # extrude all loops ccw
 | 
			
		||||
    my $was_clockwise = $loop->make_counter_clockwise;
 | 
			
		||||
    
 | 
			
		||||
    # find candidate starting points
 | 
			
		||||
    # start looking for concave vertices not being overhangs
 | 
			
		||||
    my $polygon = $loop->polygon;
 | 
			
		||||
    my @concave = ();
 | 
			
		||||
    if ($self->print_config->start_perimeters_at_concave_points) {
 | 
			
		||||
        @concave = $polygon->concave_points;
 | 
			
		||||
    }
 | 
			
		||||
    my @candidates = ();
 | 
			
		||||
    if ($self->print_config->start_perimeters_at_non_overhang) {
 | 
			
		||||
        @candidates = grep !$loop->has_overhang_point($_), @concave;
 | 
			
		||||
    }
 | 
			
		||||
    if (!@candidates) {
 | 
			
		||||
        # if none, look for any concave vertex
 | 
			
		||||
        @candidates = @concave;
 | 
			
		||||
        if (!@candidates) {
 | 
			
		||||
            # if none, look for any non-overhang vertex
 | 
			
		||||
            if ($self->print_config->start_perimeters_at_non_overhang) {
 | 
			
		||||
                @candidates = grep !$loop->has_overhang_point($_), @$polygon;
 | 
			
		||||
            }
 | 
			
		||||
            if (!@candidates) {
 | 
			
		||||
                # if none, all points are valid candidates
 | 
			
		||||
                @candidates = @$polygon;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    # find the point of the loop that is closest to the current extruder position
 | 
			
		||||
    # or randomize if requested
 | 
			
		||||
    my $last_pos = $self->last_pos;
 | 
			
		||||
    if ($self->print_config->randomize_start && $loop->role == EXTR_ROLE_CONTOUR_INTERNAL_PERIMETER) {
 | 
			
		||||
        $last_pos = Slic3r::Point->new(scale $self->print_config->print_center->[X], scale $self->print_config->bed_size->[Y]);
 | 
			
		||||
        $last_pos->rotate(rand(2*PI), $self->print_config->print_center);
 | 
			
		||||
    if ($self->config->spiral_vase) {
 | 
			
		||||
        $loop->split_at($last_pos);
 | 
			
		||||
    } elsif ($self->config->seam_position eq 'nearest' || $self->config->seam_position eq 'aligned') {
 | 
			
		||||
        my $polygon = $loop->polygon;
 | 
			
		||||
        my @candidates = @{$polygon->concave_points(PI*4/3)};
 | 
			
		||||
        @candidates = @{$polygon->convex_points(PI*2/3)} if !@candidates;
 | 
			
		||||
        @candidates = @{$polygon} if !@candidates;
 | 
			
		||||
        
 | 
			
		||||
        my @non_overhang = grep !$loop->has_overhang_point($_), @candidates;
 | 
			
		||||
        @candidates = @non_overhang if @non_overhang;
 | 
			
		||||
        
 | 
			
		||||
        if ($self->config->seam_position eq 'nearest') {
 | 
			
		||||
            $loop->split_at_vertex($last_pos->nearest_point(\@candidates));
 | 
			
		||||
        } elsif ($self->config->seam_position eq 'aligned') {
 | 
			
		||||
            if (defined $self->layer && defined $self->_seam_position->{$self->layer->object}) {
 | 
			
		||||
                $last_pos = $self->_seam_position->{$self->layer->object};
 | 
			
		||||
            }
 | 
			
		||||
            my $point = $self->_seam_position->{$self->layer->object} = $last_pos->nearest_point(\@candidates);
 | 
			
		||||
            $loop->split_at_vertex($point);
 | 
			
		||||
        }
 | 
			
		||||
    } elsif ($self->config->seam_position eq 'random') {
 | 
			
		||||
        if ($loop->role == EXTRL_ROLE_CONTOUR_INTERNAL_PERIMETER) {
 | 
			
		||||
            my $polygon = $loop->polygon;
 | 
			
		||||
            my $centroid = $polygon->centroid;
 | 
			
		||||
            $last_pos = Slic3r::Point->new($polygon->bounding_box->x_max, $centroid->y);  #))
 | 
			
		||||
            $last_pos->rotate(rand(2*PI), $centroid);
 | 
			
		||||
        }
 | 
			
		||||
        $loop->split_at($last_pos);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    # split the loop at the starting point
 | 
			
		||||
    $loop->split_at($last_pos->nearest_point(\@candidates));
 | 
			
		||||
    
 | 
			
		||||
    # 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
 | 
			
		||||
| 
						 | 
				
			
			@ -237,17 +196,17 @@ sub extrude_loop {
 | 
			
		|||
    return '' if !@paths;
 | 
			
		||||
    
 | 
			
		||||
    # apply the small perimeter speed
 | 
			
		||||
    my %params = ();
 | 
			
		||||
    if ($paths[0]->is_perimeter && abs($loop->length) <= &Slic3r::SMALL_PERIMETER_LENGTH) {
 | 
			
		||||
        $params{speed} = 'small_perimeter';
 | 
			
		||||
    if ($paths[0]->is_perimeter && $loop->length <= &Slic3r::SMALL_PERIMETER_LENGTH) {
 | 
			
		||||
        $speed //= $self->config->get_abs_value('small_perimeter_speed');
 | 
			
		||||
    }
 | 
			
		||||
    $speed //= -1;
 | 
			
		||||
    
 | 
			
		||||
    # extrude along the path
 | 
			
		||||
    my $gcode = join '', map $self->extrude_path($_, $description, %params), @paths;
 | 
			
		||||
    my $gcode = join '', map $self->extrude_path($_, $description, $speed), @paths;
 | 
			
		||||
    $self->wipe_path($paths[-1]->polyline->clone) if $self->enable_wipe;  # TODO: don't limit wipe to last path
 | 
			
		||||
    
 | 
			
		||||
    # make a little move inwards before leaving loop
 | 
			
		||||
    if ($paths[-1]->role == EXTR_ROLE_EXTERNAL_PERIMETER && defined $self->layer && $self->region->config->perimeters > 1) {
 | 
			
		||||
    if ($paths[-1]->role == EXTR_ROLE_EXTERNAL_PERIMETER && defined $self->layer && $self->config->perimeters > 1) {
 | 
			
		||||
        my $last_path_polyline = $paths[-1]->polyline;
 | 
			
		||||
        # detect angle between last and first segment
 | 
			
		||||
        # the side depends on the original winding order of the polygon (left for contours, right for holes)
 | 
			
		||||
| 
						 | 
				
			
			@ -271,7 +230,7 @@ sub extrude_loop {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
sub extrude_path {
 | 
			
		||||
    my ($self, $path, $description, %params) = @_;
 | 
			
		||||
    my ($self, $path, $description, $speed) = @_;
 | 
			
		||||
    
 | 
			
		||||
    $path->simplify(&Slic3r::SCALED_RESOLUTION);
 | 
			
		||||
    
 | 
			
		||||
| 
						 | 
				
			
			@ -288,26 +247,45 @@ sub extrude_path {
 | 
			
		|||
    
 | 
			
		||||
    # adjust acceleration
 | 
			
		||||
    my $acceleration;
 | 
			
		||||
    if (!$self->print_config->first_layer_acceleration || $self->layer->id != 0) {
 | 
			
		||||
        if ($self->print_config->perimeter_acceleration && $path->is_perimeter) {
 | 
			
		||||
            $acceleration = $self->print_config->perimeter_acceleration;
 | 
			
		||||
        } elsif ($self->print_config->infill_acceleration && $path->is_fill) {
 | 
			
		||||
            $acceleration = $self->print_config->infill_acceleration;
 | 
			
		||||
        } elsif ($self->print_config->infill_acceleration && $path->is_bridge) {
 | 
			
		||||
            $acceleration = $self->print_config->bridge_acceleration;
 | 
			
		||||
    if (!$self->config->first_layer_acceleration || $self->layer->id != 0) {
 | 
			
		||||
        if ($self->config->perimeter_acceleration && $path->is_perimeter) {
 | 
			
		||||
            $acceleration = $self->config->perimeter_acceleration;
 | 
			
		||||
        } elsif ($self->config->infill_acceleration && $path->is_fill) {
 | 
			
		||||
            $acceleration = $self->config->infill_acceleration;
 | 
			
		||||
        } elsif ($self->config->infill_acceleration && $path->is_bridge) {
 | 
			
		||||
            $acceleration = $self->config->bridge_acceleration;
 | 
			
		||||
        }
 | 
			
		||||
        $gcode .= $self->set_acceleration($acceleration) if $acceleration;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    # calculate extrusion length per distance unit
 | 
			
		||||
    my $e = $self->extruder->e_per_mm3 * $path->mm3_per_mm;
 | 
			
		||||
    $e = 0 if !$self->_extrusion_axis;
 | 
			
		||||
    $e = 0 if !$self->config->get_extrusion_axis;
 | 
			
		||||
    
 | 
			
		||||
    # set speed
 | 
			
		||||
    $self->speed( $params{speed} || $role_speeds{$path->role} || die "Unknown role: " . $path->role );
 | 
			
		||||
    my $F = $self->speeds->{$self->speed} // $self->speed;
 | 
			
		||||
    my $F;
 | 
			
		||||
    if ($path->role == EXTR_ROLE_PERIMETER) {
 | 
			
		||||
        $F = $self->config->get_abs_value('perimeter_speed');
 | 
			
		||||
    } elsif ($path->role == EXTR_ROLE_EXTERNAL_PERIMETER) {
 | 
			
		||||
        $F = $self->config->get_abs_value('external_perimeter_speed');
 | 
			
		||||
    } elsif ($path->role == EXTR_ROLE_OVERHANG_PERIMETER || $path->role == EXTR_ROLE_BRIDGE) {
 | 
			
		||||
        $F = $self->config->get_abs_value('bridge_speed');
 | 
			
		||||
    } elsif ($path->role == EXTR_ROLE_FILL) {
 | 
			
		||||
        $F = $self->config->get_abs_value('infill_speed');
 | 
			
		||||
    } elsif ($path->role == EXTR_ROLE_SOLIDFILL) {
 | 
			
		||||
        $F = $self->config->get_abs_value('solid_infill_speed');
 | 
			
		||||
    } elsif ($path->role == EXTR_ROLE_TOPSOLIDFILL) {
 | 
			
		||||
        $F = $self->config->get_abs_value('top_solid_infill_speed');
 | 
			
		||||
    } elsif ($path->role == EXTR_ROLE_GAPFILL) {
 | 
			
		||||
        $F = $self->config->get_abs_value('gap_fill_speed');
 | 
			
		||||
    } else {
 | 
			
		||||
        $F = $speed // -1;
 | 
			
		||||
        die "Invalid speed" if $F < 0;   # $speed == -1
 | 
			
		||||
    }
 | 
			
		||||
    $F *= 60;  # convert mm/sec to mm/min
 | 
			
		||||
    
 | 
			
		||||
    if ($self->layer->id == 0) {
 | 
			
		||||
        $F = $self->print_config->get_abs_value_over('first_layer_speed', $F/60) * 60;
 | 
			
		||||
        $F = $self->config->get_abs_value_over('first_layer_speed', $F/60) * 60;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    # extrude arc or line
 | 
			
		||||
| 
						 | 
				
			
			@ -317,8 +295,8 @@ sub extrude_path {
 | 
			
		|||
        $gcode .= $path->gcode($self->extruder, $e, $F,
 | 
			
		||||
            $self->shift_x - $self->extruder->extruder_offset->x,
 | 
			
		||||
            $self->shift_y - $self->extruder->extruder_offset->y,  #,,
 | 
			
		||||
            $self->_extrusion_axis,
 | 
			
		||||
            $self->print_config->gcode_comments ? " ; $description" : "");
 | 
			
		||||
            $self->config->get_extrusion_axis,
 | 
			
		||||
            $self->config->gcode_comments ? " ; $description" : "");
 | 
			
		||||
 | 
			
		||||
        if ($self->enable_wipe) {
 | 
			
		||||
            $self->wipe_path($path->polyline->clone);
 | 
			
		||||
| 
						 | 
				
			
			@ -328,14 +306,14 @@ sub extrude_path {
 | 
			
		|||
    $gcode .= ";_BRIDGE_FAN_END\n" if $path->is_bridge;
 | 
			
		||||
    $self->last_pos($path->last_point);
 | 
			
		||||
    
 | 
			
		||||
    if ($self->print_config->cooling) {
 | 
			
		||||
    if ($self->config->cooling) {
 | 
			
		||||
        my $path_time = $path_length / $F * 60;
 | 
			
		||||
        $self->elapsed_time($self->elapsed_time + $path_time);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    # reset acceleration
 | 
			
		||||
    $gcode .= $self->set_acceleration($self->print_config->default_acceleration)
 | 
			
		||||
        if $acceleration && $self->print_config->default_acceleration;
 | 
			
		||||
    $gcode .= $self->set_acceleration($self->config->default_acceleration)
 | 
			
		||||
        if $acceleration && $self->config->default_acceleration;
 | 
			
		||||
    
 | 
			
		||||
    return $gcode;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -354,19 +332,17 @@ sub travel_to {
 | 
			
		|||
    # skip retraction if the travel move is contained in an island in the current layer
 | 
			
		||||
    # *and* in an island in the upper layer (so that the ooze will not be visible)
 | 
			
		||||
    if ($travel->length < scale $self->extruder->retract_before_travel
 | 
			
		||||
        || ($self->print_config->only_retract_when_crossing_perimeters
 | 
			
		||||
        || ($self->config->only_retract_when_crossing_perimeters
 | 
			
		||||
            && (first { $_->contains_line($travel) } @{$self->_upper_layer_islands})
 | 
			
		||||
            && (first { $_->contains_line($travel) } @{$self->_layer_islands}))
 | 
			
		||||
        || (defined $role && $role == EXTR_ROLE_SUPPORTMATERIAL && (first { $_->contains_line($travel) } @{$self->layer->support_islands}))
 | 
			
		||||
        ) {
 | 
			
		||||
        $self->straight_once(0);
 | 
			
		||||
        $self->speed('travel');
 | 
			
		||||
        $gcode .= $self->G0($point, undef, 0, $comment || "");
 | 
			
		||||
    } elsif (!$self->print_config->avoid_crossing_perimeters || $self->straight_once) {
 | 
			
		||||
        $gcode .= $self->G0($point, undef, 0, $self->config->travel_speed*60, $comment || "");
 | 
			
		||||
    } elsif (!$self->config->avoid_crossing_perimeters || $self->straight_once) {
 | 
			
		||||
        $self->straight_once(0);
 | 
			
		||||
        $gcode .= $self->retract;
 | 
			
		||||
        $self->speed('travel');
 | 
			
		||||
        $gcode .= $self->G0($point, undef, 0, $comment || "");
 | 
			
		||||
        $gcode .= $self->G0($point, undef, 0, $self->config->travel_speed*60, $comment || "");
 | 
			
		||||
    } else {
 | 
			
		||||
        if ($self->new_object) {
 | 
			
		||||
            $self->new_object(0);
 | 
			
		||||
| 
						 | 
				
			
			@ -395,7 +371,7 @@ sub _plan {
 | 
			
		|||
    my @travel = @{$mp->shortest_path($self->last_pos, $point)->lines};
 | 
			
		||||
    
 | 
			
		||||
    # if the path is not contained in a single island we need to retract
 | 
			
		||||
    my $need_retract = !$self->print_config->only_retract_when_crossing_perimeters;
 | 
			
		||||
    my $need_retract = !$self->config->only_retract_when_crossing_perimeters;
 | 
			
		||||
    if (!$need_retract) {
 | 
			
		||||
        $need_retract = 1;
 | 
			
		||||
        foreach my $island (@{$self->_upper_layer_islands}) {
 | 
			
		||||
| 
						 | 
				
			
			@ -411,9 +387,8 @@ sub _plan {
 | 
			
		|||
    $gcode .= $self->retract if $need_retract;
 | 
			
		||||
    
 | 
			
		||||
    # append the actual path and return
 | 
			
		||||
    $self->speed('travel');
 | 
			
		||||
    # use G1 because we rely on paths being straight (G0 may make round paths)
 | 
			
		||||
    $gcode .= join '', map $self->G1($_->b, undef, 0, $comment || ""), @travel;
 | 
			
		||||
    $gcode .= join '', map $self->G1($_->b, undef, 0, $self->config->travel_speed*60, $comment || ""), @travel;
 | 
			
		||||
    return $gcode;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -435,60 +410,55 @@ sub retract {
 | 
			
		|||
    if ($self->extruder->wipe && $self->wipe_path) {
 | 
			
		||||
        my @points = @{$self->wipe_path};
 | 
			
		||||
        $wipe_path = Slic3r::Polyline->new($self->last_pos, @{$self->wipe_path}[1..$#{$self->wipe_path}]);
 | 
			
		||||
        $wipe_path->clip_end($wipe_path->length - $self->extruder->scaled_wipe_distance($self->print_config->travel_speed));
 | 
			
		||||
        $wipe_path->clip_end($wipe_path->length - $self->extruder->scaled_wipe_distance($self->config->travel_speed));
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    # prepare moves
 | 
			
		||||
    my $retract = [undef, undef, -$length, $comment];
 | 
			
		||||
    my $lift    = ($self->_retract_lift == 0 || defined $params{move_z}) && !$self->lifted
 | 
			
		||||
    my $retract = [undef, undef, -$length, $self->extruder->retract_speed_mm_min, $comment];
 | 
			
		||||
    my $lift    = ($self->config->retract_lift->[0] == 0 || defined $params{move_z}) && !$self->lifted
 | 
			
		||||
        ? undef
 | 
			
		||||
        : [undef, $self->z + $self->_retract_lift, 0, 'lift plate during travel'];
 | 
			
		||||
        : [undef, $self->z + $self->config->retract_lift->[0], 0, $self->config->travel_speed*60, 'lift plate during travel'];
 | 
			
		||||
    
 | 
			
		||||
    # check that we have a positive wipe length
 | 
			
		||||
    if ($wipe_path) {
 | 
			
		||||
        $self->speed($self->speeds->{travel} * 0.8);
 | 
			
		||||
        
 | 
			
		||||
        # subdivide the retraction
 | 
			
		||||
        my $retracted = 0;
 | 
			
		||||
        foreach my $line (@{$wipe_path->lines}) {
 | 
			
		||||
            my $segment_length = $line->length;
 | 
			
		||||
            # reduce retraction length a bit to avoid effective retraction speed to be greater than the configured one
 | 
			
		||||
            # due to rounding
 | 
			
		||||
            my $e = $retract->[2] * ($segment_length / $self->extruder->scaled_wipe_distance($self->print_config->travel_speed)) * 0.95;
 | 
			
		||||
            my $e = $retract->[2] * ($segment_length / $self->extruder->scaled_wipe_distance($self->config->travel_speed)) * 0.95;
 | 
			
		||||
            $retracted += $e;
 | 
			
		||||
            $gcode .= $self->G1($line->b, undef, $e, $retract->[3] . ";_WIPE");
 | 
			
		||||
            $gcode .= $self->G1($line->b, undef, $e, $self->config->travel_speed*60*0.8, $retract->[3] . ";_WIPE");
 | 
			
		||||
        }
 | 
			
		||||
        if ($retracted > $retract->[2]) {
 | 
			
		||||
            # if we retracted less than we had to, retract the remainder
 | 
			
		||||
            # TODO: add regression test
 | 
			
		||||
            $self->speed('retract');
 | 
			
		||||
            $gcode .= $self->G1(undef, undef, $retract->[2] - $retracted, $comment);
 | 
			
		||||
            $gcode .= $self->G1(undef, undef, $retract->[2] - $retracted, $self->extruder->retract_speed_mm_min, $comment);
 | 
			
		||||
        }
 | 
			
		||||
    } elsif ($self->print_config->use_firmware_retraction) {
 | 
			
		||||
    } elsif ($self->config->use_firmware_retraction) {
 | 
			
		||||
        $gcode .= "G10 ; retract\n";
 | 
			
		||||
    } else {
 | 
			
		||||
        $self->speed('retract');
 | 
			
		||||
        $gcode .= $self->G1(@$retract);
 | 
			
		||||
    }
 | 
			
		||||
    if (!$self->lifted) {
 | 
			
		||||
        $self->speed('travel');
 | 
			
		||||
        if (defined $params{move_z} && $self->_retract_lift > 0) {
 | 
			
		||||
            my $travel = [undef, $params{move_z} + $self->_retract_lift, 0, 'move to next layer (' . $self->layer->id . ') and lift'];
 | 
			
		||||
        if (defined $params{move_z} && $self->config->retract_lift->[0] > 0) {
 | 
			
		||||
            my $travel = [undef, $params{move_z} + $self->config->retract_lift->[0], 0, $self->config->travel_speed*60, 'move to next layer (' . $self->layer->id . ') and lift'];
 | 
			
		||||
            $gcode .= $self->G0(@$travel);
 | 
			
		||||
            $self->lifted($self->_retract_lift);
 | 
			
		||||
            $self->lifted($self->config->retract_lift->[0]);
 | 
			
		||||
        } elsif ($lift) {
 | 
			
		||||
            $gcode .= $self->G1(@$lift);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    $self->extruder->set_retracted($self->extruder->retracted + $length);
 | 
			
		||||
    $self->extruder->set_restart_extra($restart_extra);
 | 
			
		||||
    $self->lifted($self->_retract_lift) if $lift;
 | 
			
		||||
    $self->lifted($self->config->retract_lift->[0]) if $lift;
 | 
			
		||||
    
 | 
			
		||||
    # reset extrusion distance during retracts
 | 
			
		||||
    # this makes sure we leave sufficient precision in the firmware
 | 
			
		||||
    $gcode .= $self->reset_e;
 | 
			
		||||
    
 | 
			
		||||
    $gcode .= "M103 ; extruder off\n" if $self->print_config->gcode_flavor eq 'makerware';
 | 
			
		||||
    $gcode .= "M103 ; extruder off\n" if $self->config->gcode_flavor eq 'makerware';
 | 
			
		||||
    
 | 
			
		||||
    return $gcode;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -497,26 +467,24 @@ sub unretract {
 | 
			
		|||
    my ($self) = @_;
 | 
			
		||||
    
 | 
			
		||||
    my $gcode = "";
 | 
			
		||||
    $gcode .= "M101 ; extruder on\n" if $self->print_config->gcode_flavor eq 'makerware';
 | 
			
		||||
    $gcode .= "M101 ; extruder on\n" if $self->config->gcode_flavor eq 'makerware';
 | 
			
		||||
    
 | 
			
		||||
    if ($self->lifted) {
 | 
			
		||||
        $self->speed('travel');
 | 
			
		||||
        $gcode .= $self->G0(undef, $self->z - $self->lifted, 0, 'restore layer Z');
 | 
			
		||||
        $gcode .= $self->G0(undef, $self->z - $self->lifted, 0, $self->config->travel_speed*60, 'restore layer Z');
 | 
			
		||||
        $self->lifted(0);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    my $to_unretract = $self->extruder->retracted + $self->extruder->restart_extra;
 | 
			
		||||
    if ($to_unretract) {
 | 
			
		||||
        $self->speed('retract');
 | 
			
		||||
        if ($self->print_config->use_firmware_retraction) {
 | 
			
		||||
        if ($self->config->use_firmware_retraction) {
 | 
			
		||||
            $gcode .= "G11 ; unretract\n";
 | 
			
		||||
        } elsif ($self->_extrusion_axis) {
 | 
			
		||||
        } elsif ($self->config->get_extrusion_axis) {
 | 
			
		||||
            # use G1 instead of G0 because G0 will blend the restart with the previous travel move
 | 
			
		||||
            $gcode .= sprintf "G1 %s%.5f F%.3f",
 | 
			
		||||
                $self->_extrusion_axis,
 | 
			
		||||
                $self->config->get_extrusion_axis,
 | 
			
		||||
                $self->extruder->extrude($to_unretract),
 | 
			
		||||
                $self->extruder->retract_speed_mm_min;
 | 
			
		||||
            $gcode .= " ; compensate retraction" if $self->print_config->gcode_comments;
 | 
			
		||||
            $gcode .= " ; compensate retraction" if $self->config->gcode_comments;
 | 
			
		||||
            $gcode .= "\n";
 | 
			
		||||
        }
 | 
			
		||||
        $self->extruder->set_retracted(0);
 | 
			
		||||
| 
						 | 
				
			
			@ -528,11 +496,11 @@ sub unretract {
 | 
			
		|||
 | 
			
		||||
sub reset_e {
 | 
			
		||||
    my ($self) = @_;
 | 
			
		||||
    return "" if $self->print_config->gcode_flavor =~ /^(?:mach3|makerware|sailfish)$/;
 | 
			
		||||
    return "" if $self->config->gcode_flavor =~ /^(?:mach3|makerware|sailfish)$/;
 | 
			
		||||
    
 | 
			
		||||
    $self->extruder->set_E(0) if $self->extruder;
 | 
			
		||||
    return sprintf "G92 %s0%s\n", $self->_extrusion_axis, ($self->print_config->gcode_comments ? ' ; reset extrusion distance' : '')
 | 
			
		||||
        if $self->_extrusion_axis && !$self->print_config->use_relative_e_distances;
 | 
			
		||||
    return sprintf "G92 %s0%s\n", $self->config->get_extrusion_axis, ($self->config->gcode_comments ? ' ; reset extrusion distance' : '')
 | 
			
		||||
        if $self->config->get_extrusion_axis && !$self->config->use_relative_e_distances;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub set_acceleration {
 | 
			
		||||
| 
						 | 
				
			
			@ -540,12 +508,12 @@ sub set_acceleration {
 | 
			
		|||
    return "" if !$acceleration;
 | 
			
		||||
    
 | 
			
		||||
    return sprintf "M204 S%s%s\n",
 | 
			
		||||
        $acceleration, ($self->print_config->gcode_comments ? ' ; adjust acceleration' : '');
 | 
			
		||||
        $acceleration, ($self->config->gcode_comments ? ' ; adjust acceleration' : '');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub G0 {
 | 
			
		||||
    my $self = shift;
 | 
			
		||||
    return $self->G1(@_) if !($self->print_config->g0 || $self->print_config->gcode_flavor eq 'mach3');
 | 
			
		||||
    return $self->G1(@_) if !($self->config->g0 || $self->config->gcode_flavor eq 'mach3');
 | 
			
		||||
    return $self->_G0_G1("G0", @_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -555,7 +523,7 @@ sub G1 {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
sub _G0_G1 {
 | 
			
		||||
    my ($self, $gcode, $point, $z, $e, $comment) = @_;
 | 
			
		||||
    my ($self, $gcode, $point, $z, $e, $F, $comment) = @_;
 | 
			
		||||
    
 | 
			
		||||
    if ($point) {
 | 
			
		||||
        $gcode .= sprintf " X%.3f Y%.3f", 
 | 
			
		||||
| 
						 | 
				
			
			@ -568,25 +536,20 @@ sub _G0_G1 {
 | 
			
		|||
        $gcode .= sprintf " Z%.3f", $z;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    return $self->_Gx($gcode, $e, $comment);
 | 
			
		||||
    return $self->_Gx($gcode, $e, $F, $comment);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub _Gx {
 | 
			
		||||
    my ($self, $gcode, $e, $comment) = @_;
 | 
			
		||||
    my ($self, $gcode, $e, $F, $comment) = @_;
 | 
			
		||||
    
 | 
			
		||||
    my $F = $self->speed eq 'retract'
 | 
			
		||||
        ? ($self->extruder->retract_speed_mm_min)
 | 
			
		||||
        : $self->speeds->{$self->speed} // $self->speed;
 | 
			
		||||
    $self->last_speed($self->speed);
 | 
			
		||||
    $self->last_f($F);
 | 
			
		||||
    $gcode .= sprintf " F%.3f", $F;
 | 
			
		||||
    
 | 
			
		||||
    # output extrusion distance
 | 
			
		||||
    if ($e && $self->_extrusion_axis) {
 | 
			
		||||
        $gcode .= sprintf " %s%.5f", $self->_extrusion_axis, $self->extruder->extrude($e);
 | 
			
		||||
    if ($e && $self->config->get_extrusion_axis) {
 | 
			
		||||
        $gcode .= sprintf " %s%.5f", $self->config->get_extrusion_axis, $self->extruder->extrude($e);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    $gcode .= " ; $comment" if $comment && $self->print_config->gcode_comments;
 | 
			
		||||
    $gcode .= " ; $comment" if $comment && $self->config->gcode_comments;
 | 
			
		||||
    return "$gcode\n";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -607,8 +570,8 @@ sub set_extruder {
 | 
			
		|||
    $gcode .= $self->retract(toolchange => 1) if defined $self->extruder;
 | 
			
		||||
    
 | 
			
		||||
    # append custom toolchange G-code
 | 
			
		||||
    if (defined $self->extruder && $self->print_config->toolchange_gcode) {
 | 
			
		||||
        $gcode .= sprintf "%s\n", $self->placeholder_parser->process($self->print_config->toolchange_gcode, {
 | 
			
		||||
    if (defined $self->extruder && $self->config->toolchange_gcode) {
 | 
			
		||||
        $gcode .= sprintf "%s\n", $self->placeholder_parser->process($self->config->toolchange_gcode, {
 | 
			
		||||
            previous_extruder   => $self->extruder->id,
 | 
			
		||||
            next_extruder       => $extruder_id,
 | 
			
		||||
        });
 | 
			
		||||
| 
						 | 
				
			
			@ -617,30 +580,38 @@ sub set_extruder {
 | 
			
		|||
    # set the current extruder to the standby temperature
 | 
			
		||||
    if ($self->standby_points && defined $self->extruder) {
 | 
			
		||||
        # move to the nearest standby point
 | 
			
		||||
        $gcode .= $self->travel_to($self->last_pos->nearest_point($self->standby_points));
 | 
			
		||||
        {
 | 
			
		||||
            my $last_pos = $self->last_pos->clone;
 | 
			
		||||
            $last_pos->translate(scale +$self->shift_x, scale +$self->shift_y);
 | 
			
		||||
            my $standby_point = $last_pos->nearest_point($self->standby_points);
 | 
			
		||||
            $standby_point->translate(scale -$self->shift_x, scale -$self->shift_y);
 | 
			
		||||
            $gcode .= $self->travel_to($standby_point);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        my $temp = defined $self->layer && $self->layer->id == 0
 | 
			
		||||
            ? $self->extruder->first_layer_temperature
 | 
			
		||||
            : $self->extruder->temperature;
 | 
			
		||||
        # we assume that heating is always slower than cooling, so no need to block
 | 
			
		||||
        $gcode .= $self->set_temperature($temp + $self->print_config->standby_temperature_delta, 0);
 | 
			
		||||
        if ($self->config->standby_temperature_delta != 0) {
 | 
			
		||||
            my $temp = defined $self->layer && $self->layer->id == 0
 | 
			
		||||
                ? $self->extruder->first_layer_temperature
 | 
			
		||||
                : $self->extruder->temperature;
 | 
			
		||||
            # we assume that heating is always slower than cooling, so no need to block
 | 
			
		||||
            $gcode .= $self->set_temperature($temp + $self->config->standby_temperature_delta, 0);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    # set the new extruder
 | 
			
		||||
    $self->extruder($self->extruders->{$extruder_id});
 | 
			
		||||
    $gcode .= sprintf "%s%d%s\n", 
 | 
			
		||||
        ($self->print_config->gcode_flavor eq 'makerware'
 | 
			
		||||
        ($self->config->gcode_flavor eq 'makerware'
 | 
			
		||||
            ? 'M135 T'
 | 
			
		||||
            : $self->print_config->gcode_flavor eq 'sailfish'
 | 
			
		||||
            : $self->config->gcode_flavor eq 'sailfish'
 | 
			
		||||
                ? 'M108 T'
 | 
			
		||||
                : 'T'),
 | 
			
		||||
        $extruder_id,
 | 
			
		||||
        ($self->print_config->gcode_comments ? ' ; change extruder' : '');
 | 
			
		||||
        ($self->config->gcode_comments ? ' ; change extruder' : '');
 | 
			
		||||
    
 | 
			
		||||
    $gcode .= $self->reset_e;
 | 
			
		||||
    
 | 
			
		||||
    # set the new extruder to the operating temperature
 | 
			
		||||
    if ($self->print_config->ooze_prevention) {
 | 
			
		||||
    if ($self->config->ooze_prevention && $self->config->standby_temperature_delta != 0) {
 | 
			
		||||
        my $temp = defined $self->layer && $self->layer->id == 0
 | 
			
		||||
            ? $self->extruder->first_layer_temperature
 | 
			
		||||
            : $self->extruder->temperature;
 | 
			
		||||
| 
						 | 
				
			
			@ -656,18 +627,18 @@ sub set_fan {
 | 
			
		|||
    if ($self->last_fan_speed != $speed || $dont_save) {
 | 
			
		||||
        $self->last_fan_speed($speed) if !$dont_save;
 | 
			
		||||
        if ($speed == 0) {
 | 
			
		||||
            my $code = $self->print_config->gcode_flavor eq 'teacup'
 | 
			
		||||
            my $code = $self->config->gcode_flavor eq 'teacup'
 | 
			
		||||
                ? 'M106 S0'
 | 
			
		||||
                : $self->print_config->gcode_flavor =~ /^(?:makerware|sailfish)$/
 | 
			
		||||
                : $self->config->gcode_flavor =~ /^(?:makerware|sailfish)$/
 | 
			
		||||
                    ? 'M127'
 | 
			
		||||
                    : 'M107';
 | 
			
		||||
            return sprintf "$code%s\n", ($self->print_config->gcode_comments ? ' ; disable fan' : '');
 | 
			
		||||
            return sprintf "$code%s\n", ($self->config->gcode_comments ? ' ; disable fan' : '');
 | 
			
		||||
        } else {
 | 
			
		||||
            if ($self->print_config->gcode_flavor =~ /^(?:makerware|sailfish)$/) {
 | 
			
		||||
                return sprintf "M126%s\n", ($self->print_config->gcode_comments ? ' ; enable fan' : '');
 | 
			
		||||
            if ($self->config->gcode_flavor =~ /^(?:makerware|sailfish)$/) {
 | 
			
		||||
                return sprintf "M126%s\n", ($self->config->gcode_comments ? ' ; enable fan' : '');
 | 
			
		||||
            } else {
 | 
			
		||||
                return sprintf "M106 %s%d%s\n", ($self->print_config->gcode_flavor eq 'mach3' ? 'P' : 'S'),
 | 
			
		||||
                    (255 * $speed / 100), ($self->print_config->gcode_comments ? ' ; enable fan' : '');
 | 
			
		||||
                return sprintf "M106 %s%d%s\n", ($self->config->gcode_flavor eq 'mach3' ? 'P' : 'S'),
 | 
			
		||||
                    (255 * $speed / 100), ($self->config->gcode_comments ? ' ; enable fan' : '');
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -677,17 +648,17 @@ sub set_fan {
 | 
			
		|||
sub set_temperature {
 | 
			
		||||
    my ($self, $temperature, $wait, $tool) = @_;
 | 
			
		||||
    
 | 
			
		||||
    return "" if $wait && $self->print_config->gcode_flavor =~ /^(?:makerware|sailfish)$/;
 | 
			
		||||
    return "" if $wait && $self->config->gcode_flavor =~ /^(?:makerware|sailfish)$/;
 | 
			
		||||
    
 | 
			
		||||
    my ($code, $comment) = ($wait && $self->print_config->gcode_flavor ne 'teacup')
 | 
			
		||||
    my ($code, $comment) = ($wait && $self->config->gcode_flavor ne 'teacup')
 | 
			
		||||
        ? ('M109', 'wait for temperature to be reached')
 | 
			
		||||
        : ('M104', 'set temperature');
 | 
			
		||||
    my $gcode = sprintf "$code %s%d %s; $comment\n",
 | 
			
		||||
        ($self->print_config->gcode_flavor eq 'mach3' ? 'P' : 'S'), $temperature,
 | 
			
		||||
        (defined $tool && ($self->multiple_extruders || $self->print_config->gcode_flavor =~ /^(?:makerware|sailfish)$/)) ? "T$tool " : "";
 | 
			
		||||
        ($self->config->gcode_flavor eq 'mach3' ? 'P' : 'S'), $temperature,
 | 
			
		||||
        (defined $tool && ($self->multiple_extruders || $self->config->gcode_flavor =~ /^(?:makerware|sailfish)$/)) ? "T$tool " : "";
 | 
			
		||||
    
 | 
			
		||||
    $gcode .= "M116 ; wait for temperature to be reached\n"
 | 
			
		||||
        if $self->print_config->gcode_flavor eq 'teacup' && $wait;
 | 
			
		||||
        if $self->config->gcode_flavor eq 'teacup' && $wait;
 | 
			
		||||
    
 | 
			
		||||
    return $gcode;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -695,14 +666,14 @@ sub set_temperature {
 | 
			
		|||
sub set_bed_temperature {
 | 
			
		||||
    my ($self, $temperature, $wait) = @_;
 | 
			
		||||
    
 | 
			
		||||
    my ($code, $comment) = ($wait && $self->print_config->gcode_flavor ne 'teacup')
 | 
			
		||||
        ? (($self->print_config->gcode_flavor =~ /^(?:makerware|sailfish)$/ ? 'M109' : 'M190'), 'wait for bed temperature to be reached')
 | 
			
		||||
    my ($code, $comment) = ($wait && $self->config->gcode_flavor ne 'teacup')
 | 
			
		||||
        ? (($self->config->gcode_flavor =~ /^(?:makerware|sailfish)$/ ? 'M109' : 'M190'), 'wait for bed temperature to be reached')
 | 
			
		||||
        : ('M140', 'set bed temperature');
 | 
			
		||||
    my $gcode = sprintf "$code %s%d ; $comment\n",
 | 
			
		||||
        ($self->print_config->gcode_flavor eq 'mach3' ? 'P' : 'S'), $temperature;
 | 
			
		||||
        ($self->config->gcode_flavor eq 'mach3' ? 'P' : 'S'), $temperature;
 | 
			
		||||
    
 | 
			
		||||
    $gcode .= "M116 ; wait for bed temperature to be reached\n"
 | 
			
		||||
        if $self->print_config->gcode_flavor eq 'teacup' && $wait;
 | 
			
		||||
        if $self->config->gcode_flavor eq 'teacup' && $wait;
 | 
			
		||||
    
 | 
			
		||||
    return $gcode;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -46,6 +46,7 @@ sub process_layer {
 | 
			
		|||
    my $gcode = "";
 | 
			
		||||
    
 | 
			
		||||
    my $object = $layer->object;
 | 
			
		||||
    $self->gcodegen->config->apply_object_config($object->config);
 | 
			
		||||
    
 | 
			
		||||
    # check whether we're going to apply spiralvase logic
 | 
			
		||||
    if (defined $self->spiralvase) {
 | 
			
		||||
| 
						 | 
				
			
			@ -95,7 +96,7 @@ sub process_layer {
 | 
			
		|||
                my $extruder_id = $extruder_ids[($i/@extruder_ids) % @extruder_ids];
 | 
			
		||||
                $gcode .= $self->gcodegen->set_extruder($extruder_id)
 | 
			
		||||
                    if $layer->id == 0;
 | 
			
		||||
                $gcode .= $self->gcodegen->extrude_loop($skirt_loops[$i], 'skirt');
 | 
			
		||||
                $gcode .= $self->gcodegen->extrude_loop($skirt_loops[$i], 'skirt', $object->config->support_material_speed);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        $self->skirt_done->{$layer->print_z} = 1;
 | 
			
		||||
| 
						 | 
				
			
			@ -106,7 +107,8 @@ sub process_layer {
 | 
			
		|||
    if (!$self->brim_done) {
 | 
			
		||||
        $gcode .= $self->gcodegen->set_extruder($self->print->objects->[0]->config->support_material_extruder-1);
 | 
			
		||||
        $self->gcodegen->set_shift(@{$self->shift});
 | 
			
		||||
        $gcode .= $self->gcodegen->extrude_loop($_, 'brim') for @{$self->print->brim};
 | 
			
		||||
        $gcode .= $self->gcodegen->extrude_loop($_, 'brim', $object->config->support_material_speed)
 | 
			
		||||
            for @{$self->print->brim};
 | 
			
		||||
        $self->brim_done(1);
 | 
			
		||||
        $self->gcodegen->straight_once(1);
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -122,14 +124,12 @@ sub process_layer {
 | 
			
		|||
        if ($layer->isa('Slic3r::Layer::Support')) {
 | 
			
		||||
            if ($layer->support_interface_fills->count > 0) {
 | 
			
		||||
                $gcode .= $self->gcodegen->set_extruder($object->config->support_material_interface_extruder-1);
 | 
			
		||||
                my %params = (speed => $object->config->support_material_speed*60);
 | 
			
		||||
                $gcode .= $self->gcodegen->extrude_path($_, 'support material interface', %params) 
 | 
			
		||||
                $gcode .= $self->gcodegen->extrude_path($_, 'support material interface', $object->config->get_abs_value('support_material_interface_speed')) 
 | 
			
		||||
                    for @{$layer->support_interface_fills->chained_path_from($self->gcodegen->last_pos, 0)}; 
 | 
			
		||||
            }
 | 
			
		||||
            if ($layer->support_fills->count > 0) {
 | 
			
		||||
                $gcode .= $self->gcodegen->set_extruder($object->config->support_material_extruder-1);
 | 
			
		||||
                my %params = (speed => $object->config->support_material_speed*60);
 | 
			
		||||
                $gcode .= $self->gcodegen->extrude_path($_, 'support material', %params) 
 | 
			
		||||
                $gcode .= $self->gcodegen->extrude_path($_, 'support material', $object->config->get_abs_value('support_material_speed')) 
 | 
			
		||||
                    for @{$layer->support_fills->chained_path_from($self->gcodegen->last_pos, 0)};
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -145,7 +145,7 @@ sub process_layer {
 | 
			
		|||
        foreach my $region_id (@region_ids) {
 | 
			
		||||
            my $layerm = $layer->regions->[$region_id] or next;
 | 
			
		||||
            my $region = $self->print->regions->[$region_id];
 | 
			
		||||
            $self->gcodegen->region($region);
 | 
			
		||||
            $self->gcodegen->config->apply_region_config($region->config);
 | 
			
		||||
            
 | 
			
		||||
            # group extrusions by island
 | 
			
		||||
            my @perimeters_by_island = map [], 0..$#{$layer->slices};   # slice idx => @perimeters
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -43,7 +43,7 @@ sub process_layer {
 | 
			
		|||
        }
 | 
			
		||||
    });
 | 
			
		||||
    
 | 
			
		||||
    #use XXX; YYY [ $gcode, $layer_height, $z, $total_layer_length ];
 | 
			
		||||
    #use XXX; XXX [ $gcode, $layer_height, $z, $total_layer_length ];
 | 
			
		||||
    # remove layer height from initial Z
 | 
			
		||||
    $z -= $layer_height;
 | 
			
		||||
    
 | 
			
		||||
| 
						 | 
				
			
			@ -57,16 +57,19 @@ sub process_layer {
 | 
			
		|||
            my $line = $info->{raw};
 | 
			
		||||
            $line =~ s/ Z[.0-9]+/ Z$z/;
 | 
			
		||||
            $new_gcode .= "$line\n";
 | 
			
		||||
        } elsif ($cmd eq 'G1' && !exists $args->{Z} && $info->{dist_XY}) {
 | 
			
		||||
        } elsif ($cmd eq 'G1' && !exists($args->{Z}) && $info->{dist_XY}) {
 | 
			
		||||
            # horizontal move
 | 
			
		||||
            my $line = $info->{raw};
 | 
			
		||||
            if ($info->{extruding}) {
 | 
			
		||||
                $z += $info->{dist_XY} * $layer_height / $total_layer_length;
 | 
			
		||||
                $line =~ s/^G1 /sprintf 'G1 Z%.3f ', $z/e;
 | 
			
		||||
                $new_gcode .= "$line\n";
 | 
			
		||||
            } else {
 | 
			
		||||
                $new_gcode .= "$line\n";
 | 
			
		||||
            }
 | 
			
		||||
            # skip travel moves: the move to first perimeter point will
 | 
			
		||||
            # cause a visible seam when loops are not aligned in XY; by skipping
 | 
			
		||||
            # it we blend the first loop move in the XY plane (although the smoothness
 | 
			
		||||
            # of such blend depend on how long the first segment is; maybe we should
 | 
			
		||||
            # enforce some minimum length?)
 | 
			
		||||
        } else {
 | 
			
		||||
            $new_gcode .= "$info->{raw}\n";
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -231,7 +231,9 @@ sub _build_field {
 | 
			
		|||
            });
 | 
			
		||||
        } else {
 | 
			
		||||
            $field = Wx::TextCtrl->new($self->parent, -1, $opt->{default}, wxDefaultPosition, $size, $style);
 | 
			
		||||
            $self->_setters->{$opt_key} = sub { $field->ChangeValue($_[0]) };
 | 
			
		||||
            # value supplied to the setter callback might be undef in case user loads a config
 | 
			
		||||
            # that has empty string for multi-value options like 'wipe'
 | 
			
		||||
            $self->_setters->{$opt_key} = sub { $field->ChangeValue($_[0]) if defined $_[0] };
 | 
			
		||||
            EVT_TEXT($self->parent, $field, $on_change);
 | 
			
		||||
            EVT_KILL_FOCUS($field, $on_kill_focus);
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -627,12 +627,6 @@ sub split_object {
 | 
			
		|||
        Slic3r::GUI::warning_catcher($self)->("The selected object couldn't be split because it already contains a single part.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    # remove the original object before spawning the object_loaded event, otherwise 
 | 
			
		||||
    # we'll pass the wrong $obj_idx to it (which won't be recognized after the
 | 
			
		||||
    # thumbnail thread returns)
 | 
			
		||||
    $self->remove($obj_idx);
 | 
			
		||||
    $current_object = $obj_idx = undef;
 | 
			
		||||
    
 | 
			
		||||
    # create a bogus Model object, we only need to instantiate the new Model::Object objects
 | 
			
		||||
    my $new_model = Slic3r::Model->new;
 | 
			
		||||
| 
						 | 
				
			
			@ -666,6 +660,12 @@ sub split_object {
 | 
			
		|||
        $model_object->center_around_origin;
 | 
			
		||||
        push @model_objects, $model_object;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    # remove the original object before spawning the object_loaded event, otherwise 
 | 
			
		||||
    # we'll pass the wrong $obj_idx to it (which won't be recognized after the
 | 
			
		||||
    # thumbnail thread returns)
 | 
			
		||||
    $self->remove($obj_idx);
 | 
			
		||||
    $current_object = $obj_idx = undef;
 | 
			
		||||
    
 | 
			
		||||
    # load all model objects at once, otherwise the plate would be rearranged after each one
 | 
			
		||||
    # causing original positions not to be kept
 | 
			
		||||
| 
						 | 
				
			
			@ -692,6 +692,7 @@ sub export_gcode {
 | 
			
		|||
        $self->{print}->apply_config($config);
 | 
			
		||||
        $self->{print}->validate;
 | 
			
		||||
    };
 | 
			
		||||
    Slic3r::GUI::catch_error($self) and return;
 | 
			
		||||
    
 | 
			
		||||
    # apply extra variables
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -197,7 +197,7 @@ sub on_btn_load {
 | 
			
		|||
        foreach my $object (@{$model->objects}) {
 | 
			
		||||
            foreach my $volume (@{$object->volumes}) {
 | 
			
		||||
                my $new_volume = $self->{model_object}->add_volume($volume);
 | 
			
		||||
                $new_volume->modifier($is_modifier);
 | 
			
		||||
                $new_volume->set_modifier($is_modifier);
 | 
			
		||||
                if (!defined $new_volume->material_id) {
 | 
			
		||||
                    # it looks like this block is never entered because all input volumes seem to have an assigned material
 | 
			
		||||
                    # TODO: check we can assume that for any input format
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -150,7 +150,7 @@ sub Closing {
 | 
			
		|||
    my $self = shift;
 | 
			
		||||
    
 | 
			
		||||
    # save ranges into the plater object
 | 
			
		||||
    $self->model_object->layer_height_ranges([ $self->_get_ranges ]);
 | 
			
		||||
    $self->model_object->set_layer_height_ranges([ $self->_get_ranges ]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub _get_ranges {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -235,15 +235,6 @@ sub extra_variables {
 | 
			
		|||
    return { %extra_variables };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub init_print {
 | 
			
		||||
    my $self = shift;
 | 
			
		||||
    
 | 
			
		||||
    return Slic3r::Print->new(
 | 
			
		||||
        config          => $self->config,
 | 
			
		||||
        extra_variables => $self->extra_variables,
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub export_config {
 | 
			
		||||
    my $self = shift;
 | 
			
		||||
    
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -417,21 +417,17 @@ sub build {
 | 
			
		|||
        },
 | 
			
		||||
        {
 | 
			
		||||
            title => 'Quality (slower slicing)',
 | 
			
		||||
            options => [qw(extra_perimeters avoid_crossing_perimeters start_perimeters_at_concave_points start_perimeters_at_non_overhang thin_walls overhangs)],
 | 
			
		||||
            options => [qw(extra_perimeters avoid_crossing_perimeters thin_walls overhangs)],
 | 
			
		||||
            lines => [
 | 
			
		||||
                Slic3r::GUI::OptionsGroup->single_option_line('extra_perimeters'),
 | 
			
		||||
                Slic3r::GUI::OptionsGroup->single_option_line('avoid_crossing_perimeters'),
 | 
			
		||||
                {
 | 
			
		||||
                    label   => 'Start perimeters at',
 | 
			
		||||
                    options => [qw(start_perimeters_at_concave_points start_perimeters_at_non_overhang)],
 | 
			
		||||
                },
 | 
			
		||||
                Slic3r::GUI::OptionsGroup->single_option_line('thin_walls'),
 | 
			
		||||
                Slic3r::GUI::OptionsGroup->single_option_line('overhangs'),
 | 
			
		||||
            ],
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            title => 'Advanced',
 | 
			
		||||
            options => [qw(randomize_start external_perimeters_first)],
 | 
			
		||||
            options => [qw(seam_position external_perimeters_first)],
 | 
			
		||||
        },
 | 
			
		||||
    ]);
 | 
			
		||||
    
 | 
			
		||||
| 
						 | 
				
			
			@ -454,7 +450,7 @@ sub build {
 | 
			
		|||
    $self->add_options_page('Speed', 'time.png', optgroups => [
 | 
			
		||||
        {
 | 
			
		||||
            title => 'Speed for print moves',
 | 
			
		||||
            options => [qw(perimeter_speed small_perimeter_speed external_perimeter_speed infill_speed solid_infill_speed top_solid_infill_speed support_material_speed bridge_speed gap_fill_speed)],
 | 
			
		||||
            options => [qw(perimeter_speed small_perimeter_speed external_perimeter_speed infill_speed solid_infill_speed top_solid_infill_speed support_material_speed support_material_interface_speed bridge_speed gap_fill_speed)],
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            title => 'Speed for non-print moves',
 | 
			
		||||
| 
						 | 
				
			
			@ -743,7 +739,13 @@ sub _build_extruder_pages {
 | 
			
		|||
        # extend options
 | 
			
		||||
        foreach my $opt_key ($self->_extruder_options) {
 | 
			
		||||
            my $values = $self->{config}->get($opt_key);
 | 
			
		||||
            $values->[$extruder_idx] //= $default_config->get_at($opt_key, 0);
 | 
			
		||||
            if (!defined $values) {
 | 
			
		||||
                $values = [ $default_config->get_at($opt_key, 0) ];
 | 
			
		||||
            } else {
 | 
			
		||||
                # use last extruder's settings for the new one
 | 
			
		||||
                my $last_value = $values->[-1];
 | 
			
		||||
                $values->[$extruder_idx] //= $last_value;
 | 
			
		||||
            }
 | 
			
		||||
            $self->{config}->set($opt_key, $values)
 | 
			
		||||
                or die "Unable to extend $opt_key";
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,6 +2,7 @@ package Slic3r::Layer::Region;
 | 
			
		|||
use Moo;
 | 
			
		||||
 | 
			
		||||
use List::Util qw(sum first);
 | 
			
		||||
use Slic3r::ExtrusionLoop ':roles';
 | 
			
		||||
use Slic3r::ExtrusionPath ':roles';
 | 
			
		||||
use Slic3r::Flow ':roles';
 | 
			
		||||
use Slic3r::Geometry qw(PI A B scale unscale chained_path points_coincide);
 | 
			
		||||
| 
						 | 
				
			
			@ -124,7 +125,7 @@ sub make_perimeters {
 | 
			
		|||
                    )};
 | 
			
		||||
                
 | 
			
		||||
                    # look for gaps
 | 
			
		||||
                    if ($self->print->config->gap_fill_speed > 0 && $self->config->fill_density > 0) {
 | 
			
		||||
                    if ($self->region->config->gap_fill_speed > 0 && $self->config->fill_density > 0) {
 | 
			
		||||
                        # not using safety offset here would "detect" very narrow gaps
 | 
			
		||||
                        # (but still long enough to escape the area threshold) that gap fill
 | 
			
		||||
                        # won't be able to fill but we'd still remove from infill area
 | 
			
		||||
| 
						 | 
				
			
			@ -179,8 +180,7 @@ sub make_perimeters {
 | 
			
		|||
                # and use zigzag).
 | 
			
		||||
                my $w = $gap_size->[2];
 | 
			
		||||
                my @filled = map {
 | 
			
		||||
                    @{($_->isa('Slic3r::ExtrusionLoop') ? $_->split_at_first_point : $_)
 | 
			
		||||
                        ->polyline
 | 
			
		||||
                    @{($_->isa('Slic3r::ExtrusionLoop') ? $_->polygon->split_at_first_point : $_->polyline)
 | 
			
		||||
                        ->grow(scale $w/2)};
 | 
			
		||||
                } @gap_fill;
 | 
			
		||||
                @last = @{diff(\@last, \@filled)};
 | 
			
		||||
| 
						 | 
				
			
			@ -233,7 +233,7 @@ sub make_perimeters {
 | 
			
		|||
    
 | 
			
		||||
    # prepare grown lower layer slices for overhang detection
 | 
			
		||||
    my $lower_slices = Slic3r::ExPolygon::Collection->new;
 | 
			
		||||
    if ($self->layer->lower_layer && $self->layer->print->config->overhangs) {
 | 
			
		||||
    if ($self->layer->lower_layer && $self->region->config->overhangs) {
 | 
			
		||||
        # We consider overhang any part where the entire nozzle diameter is not supported by the
 | 
			
		||||
        # lower layer, so we take lower slices and offset them by half the nozzle diameter used 
 | 
			
		||||
        # in the current layer
 | 
			
		||||
| 
						 | 
				
			
			@ -257,18 +257,27 @@ sub make_perimeters {
 | 
			
		|||
        foreach my $polynode (@$polynodes) {
 | 
			
		||||
            my $polygon = ($polynode->{outer} // $polynode->{hole})->clone;
 | 
			
		||||
            
 | 
			
		||||
            my $role = EXTR_ROLE_PERIMETER;
 | 
			
		||||
            if ($is_contour ? $depth == 0 : !@{ $polynode->{children} }) {
 | 
			
		||||
            my $role        = EXTR_ROLE_PERIMETER;
 | 
			
		||||
            my $loop_role   = EXTRL_ROLE_DEFAULT;
 | 
			
		||||
            
 | 
			
		||||
            my $root_level  = $depth == 0;
 | 
			
		||||
            my $no_children = !@{ $polynode->{children} };
 | 
			
		||||
            my $is_external = $is_contour ? $root_level : $no_children;
 | 
			
		||||
            my $is_internal = $is_contour ? $no_children : $root_level;
 | 
			
		||||
            if ($is_external) {
 | 
			
		||||
                # external perimeters are root level in case of contours
 | 
			
		||||
                # and items with no children in case of holes
 | 
			
		||||
                $role = EXTR_ROLE_EXTERNAL_PERIMETER;
 | 
			
		||||
            } elsif ($depth == 1 && $is_contour) {
 | 
			
		||||
                $role = EXTR_ROLE_CONTOUR_INTERNAL_PERIMETER;
 | 
			
		||||
                $role       = EXTR_ROLE_EXTERNAL_PERIMETER;
 | 
			
		||||
                $loop_role  = EXTRL_ROLE_EXTERNAL_PERIMETER;
 | 
			
		||||
            } elsif ($is_contour && $is_internal) {
 | 
			
		||||
                # internal perimeters are root level in case of holes
 | 
			
		||||
                # and items with no children in case of contours
 | 
			
		||||
                $loop_role  = EXTRL_ROLE_CONTOUR_INTERNAL_PERIMETER;
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            # detect overhanging/bridging perimeters
 | 
			
		||||
            my @paths = ();
 | 
			
		||||
            if ($self->layer->print->config->overhangs && $lower_slices->count > 0) {
 | 
			
		||||
            if ($self->region->config->overhangs && $lower_slices->count > 0) {
 | 
			
		||||
                # get non-overhang paths by intersecting this loop with the grown lower slices
 | 
			
		||||
                foreach my $polyline (@{ intersection_ppl([ $polygon ], $lower_slices_p) }) {
 | 
			
		||||
                    push @paths, Slic3r::ExtrusionPath->new(
 | 
			
		||||
| 
						 | 
				
			
			@ -292,7 +301,7 @@ sub make_perimeters {
 | 
			
		|||
                        height          => $self->height,
 | 
			
		||||
                    );
 | 
			
		||||
                }
 | 
			
		||||
        
 | 
			
		||||
                
 | 
			
		||||
                # reapply the nearest point search for starting point
 | 
			
		||||
                # (clone because the collection gets DESTROY'ed)
 | 
			
		||||
                # We allow polyline reversal because Clipper may have randomly
 | 
			
		||||
| 
						 | 
				
			
			@ -309,6 +318,7 @@ sub make_perimeters {
 | 
			
		|||
                );
 | 
			
		||||
            }
 | 
			
		||||
            my $loop = Slic3r::ExtrusionLoop->new_from_paths(@paths);
 | 
			
		||||
            $loop->role($loop_role);
 | 
			
		||||
            
 | 
			
		||||
            # return ccw contours and cw holes
 | 
			
		||||
            # GCode.pm will convert all of them to ccw, but it needs to know
 | 
			
		||||
| 
						 | 
				
			
			@ -427,6 +437,11 @@ sub _fill_gaps {
 | 
			
		|||
            my $loop = Slic3r::ExtrusionLoop->new;
 | 
			
		||||
            $loop->append(Slic3r::ExtrusionPath->new(polyline => $polylines[$i]->split_at_first_point, %path_args));
 | 
			
		||||
            $polylines[$i] = $loop;
 | 
			
		||||
        } elsif ($polylines[$i]->is_valid && $polylines[$i]->first_point->coincides_with($polylines[$i]->last_point)) {
 | 
			
		||||
            # since medial_axis() now returns only Polyline objects, detect loops here
 | 
			
		||||
            my $loop = Slic3r::ExtrusionLoop->new;
 | 
			
		||||
            $loop->append(Slic3r::ExtrusionPath->new(polyline => $polylines[$i], %path_args));
 | 
			
		||||
            $polylines[$i] = $loop;
 | 
			
		||||
        } else {
 | 
			
		||||
            $polylines[$i] = Slic3r::ExtrusionPath->new(polyline => $polylines[$i], %path_args);
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,6 +15,11 @@ sub wkt {
 | 
			
		|||
    return sprintf "POLYGON((%s))", join ',', map "$_->[0] $_->[1]", @$self;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub dump_perl {
 | 
			
		||||
    my $self = shift;
 | 
			
		||||
    return sprintf "[%s]", join ',', map "[$_->[0],$_->[1]]", @$self;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub grow {
 | 
			
		||||
    my $self = shift;
 | 
			
		||||
    return $self->split_at_first_point->grow(@_);
 | 
			
		||||
| 
						 | 
				
			
			@ -40,15 +45,52 @@ sub subdivide {
 | 
			
		|||
    return Slic3r::Polygon->new(@new_points);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# for cw polygons this will return convex points!
 | 
			
		||||
sub concave_points {
 | 
			
		||||
    my $self = shift;
 | 
			
		||||
    my ($self, $angle) = @_;
 | 
			
		||||
    
 | 
			
		||||
    $angle //= PI;
 | 
			
		||||
    
 | 
			
		||||
    # input angle threshold is checked on the internal side of the polygon
 | 
			
		||||
    # but angle3points measures CCW angle, so we calculate the complementary angle
 | 
			
		||||
    my $ccw_angle = 2*PI-$angle;
 | 
			
		||||
    
 | 
			
		||||
    my @points = @$self;
 | 
			
		||||
    my @points_pp = @{$self->pp};
 | 
			
		||||
    return map $points[$_],
 | 
			
		||||
        grep Slic3r::Geometry::angle3points(@points_pp[$_, $_-1, $_+1]) < PI - epsilon,
 | 
			
		||||
        -1 .. ($#points-1);
 | 
			
		||||
    
 | 
			
		||||
    my @concave = ();
 | 
			
		||||
    for my $i (-1 .. ($#points-1)) {
 | 
			
		||||
        next if $points[$i-1]->coincides_with($points[$i]);
 | 
			
		||||
        # angle is measured in ccw orientation
 | 
			
		||||
        my $vertex_angle = Slic3r::Geometry::angle3points(@points_pp[$i, $i-1, $i+1]);
 | 
			
		||||
        if ($vertex_angle <= $ccw_angle) {
 | 
			
		||||
            push @concave, $points[$i];
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return [@concave];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub convex_points {
 | 
			
		||||
    my ($self, $angle) = @_;
 | 
			
		||||
    
 | 
			
		||||
    $angle //= PI;
 | 
			
		||||
    
 | 
			
		||||
    # input angle threshold is checked on the internal side of the polygon
 | 
			
		||||
    # but angle3points measures CCW angle, so we calculate the complementary angle
 | 
			
		||||
    my $ccw_angle = 2*PI-$angle;
 | 
			
		||||
    
 | 
			
		||||
    my @points = @$self;
 | 
			
		||||
    my @points_pp = @{$self->pp};
 | 
			
		||||
    
 | 
			
		||||
    my @convex = ();
 | 
			
		||||
    for my $i (-1 .. ($#points-1)) {
 | 
			
		||||
        next if $points[$i-1]->coincides_with($points[$i]);
 | 
			
		||||
        # angle is measured in ccw orientation
 | 
			
		||||
        my $vertex_angle = Slic3r::Geometry::angle3points(@points_pp[$i, $i-1, $i+1]);
 | 
			
		||||
        if ($vertex_angle >= $ccw_angle) {
 | 
			
		||||
            push @convex, $points[$i];
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return [@convex];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
1;
 | 
			
		||||
| 
						 | 
				
			
			@ -3,7 +3,7 @@ use strict;
 | 
			
		|||
use warnings;
 | 
			
		||||
 | 
			
		||||
use List::Util qw(first);
 | 
			
		||||
use Slic3r::Geometry qw(X Y epsilon);
 | 
			
		||||
use Slic3r::Geometry qw(X Y PI epsilon);
 | 
			
		||||
use Slic3r::Geometry::Clipper qw(JT_SQUARE);
 | 
			
		||||
 | 
			
		||||
sub new_scale {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -580,7 +580,7 @@ EOF
 | 
			
		|||
        
 | 
			
		||||
        my @current_layer_slices = ();
 | 
			
		||||
        # sort slices so that the outermost ones come first
 | 
			
		||||
        my @slices = sort { $a->contour->encloses_point($b->contour->[0]) ? 0 : 1 } @{$layer->slices};
 | 
			
		||||
        my @slices = sort { $a->contour->contains_point($b->contour->[0]) ? 0 : 1 } @{$layer->slices};
 | 
			
		||||
        foreach my $copy (@{$layer->object->copies}) {
 | 
			
		||||
            foreach my $slice (@slices) {
 | 
			
		||||
                my $expolygon = $slice->clone;
 | 
			
		||||
| 
						 | 
				
			
			@ -625,11 +625,14 @@ EOF
 | 
			
		|||
 | 
			
		||||
sub make_skirt {
 | 
			
		||||
    my $self = shift;
 | 
			
		||||
    
 | 
			
		||||
    # since this method must be idempotent, we clear skirt paths *before*
 | 
			
		||||
    # checking whether we need to generate them
 | 
			
		||||
    $self->skirt->clear;
 | 
			
		||||
    
 | 
			
		||||
    return unless $self->config->skirts > 0
 | 
			
		||||
        || ($self->config->ooze_prevention && @{$self->extruders} > 1);
 | 
			
		||||
    
 | 
			
		||||
    $self->skirt->clear;  # method must be idempotent
 | 
			
		||||
    
 | 
			
		||||
    # First off we need to decide how tall the skirt must be.
 | 
			
		||||
    # The skirt_height option from config is expressed in layers, but our
 | 
			
		||||
    # object might have different layer heights, so we need to find the print_z
 | 
			
		||||
| 
						 | 
				
			
			@ -737,9 +740,12 @@ sub make_skirt {
 | 
			
		|||
 | 
			
		||||
sub make_brim {
 | 
			
		||||
    my $self = shift;
 | 
			
		||||
    return unless $self->config->brim_width > 0;
 | 
			
		||||
    
 | 
			
		||||
    $self->brim->clear;  # method must be idempotent
 | 
			
		||||
    # since this method must be idempotent, we clear brim paths *before*
 | 
			
		||||
    # checking whether we need to generate them
 | 
			
		||||
    $self->brim->clear;
 | 
			
		||||
    
 | 
			
		||||
    return unless $self->config->brim_width > 0;
 | 
			
		||||
    
 | 
			
		||||
    # brim is only printed on first layer and uses support material extruder
 | 
			
		||||
    my $first_layer_height = $self->objects->[0]->config->get_abs_value('first_layer_height');
 | 
			
		||||
| 
						 | 
				
			
			@ -851,11 +857,11 @@ sub write_gcode {
 | 
			
		|||
    
 | 
			
		||||
    # set up our helper object
 | 
			
		||||
    my $gcodegen = Slic3r::GCode->new(
 | 
			
		||||
        print_config        => $self->config,
 | 
			
		||||
        placeholder_parser  => $self->placeholder_parser,
 | 
			
		||||
        layer_count         => $self->layer_count,
 | 
			
		||||
    );
 | 
			
		||||
    $gcodegen->set_extruders($self->extruders);
 | 
			
		||||
    $gcodegen->config->apply_print_config($self->config);
 | 
			
		||||
    $gcodegen->set_extruders($self->extruders, $self->config);
 | 
			
		||||
    
 | 
			
		||||
    print $fh "G21 ; set units to millimeters\n" if $self->config->gcode_flavor ne 'makerware';
 | 
			
		||||
    print $fh $gcodegen->set_fan(0, 1) if $self->config->cooling && $self->config->disable_fan_first_layers;
 | 
			
		||||
| 
						 | 
				
			
			@ -944,6 +950,7 @@ sub write_gcode {
 | 
			
		|||
        
 | 
			
		||||
        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 
 | 
			
		||||
| 
						 | 
				
			
			@ -951,7 +958,7 @@ sub write_gcode {
 | 
			
		|||
                if ($finished_objects > 0) {
 | 
			
		||||
                    $gcodegen->set_shift(map unscale $copy->[$_], X,Y);
 | 
			
		||||
                    print $fh $gcodegen->retract;
 | 
			
		||||
                    print $fh $gcodegen->G0(Slic3r::Point->new(0,0), undef, 0, 'move to origin position for next object');
 | 
			
		||||
                    print $fh $gcodegen->G0($object->_copies_shift->negative, undef, 0, $gcodegen->config->travel_speed*60, 'move to origin position for next object');
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
                my $buffer = Slic3r::GCode::CoolingBuffer->new(
 | 
			
		||||
| 
						 | 
				
			
			@ -959,7 +966,6 @@ sub write_gcode {
 | 
			
		|||
                    gcodegen    => $gcodegen,
 | 
			
		||||
                );
 | 
			
		||||
                
 | 
			
		||||
                my $object = $self->objects->[$obj_idx];
 | 
			
		||||
                my @layers = sort { $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
 | 
			
		||||
| 
						 | 
				
			
			@ -1037,9 +1043,11 @@ sub write_gcode {
 | 
			
		|||
    
 | 
			
		||||
    # append full config
 | 
			
		||||
    print $fh "\n";
 | 
			
		||||
    foreach my $opt_key (sort @{$self->config->get_keys}) {
 | 
			
		||||
        next if $Slic3r::Config::Options->{$opt_key}{shortcut};
 | 
			
		||||
        printf $fh "; %s = %s\n", $opt_key, $self->config->serialize($opt_key);
 | 
			
		||||
    foreach my $config ($self->config, $self->default_object_config, $self->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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    # close our gcode file
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -308,11 +308,10 @@ sub slice {
 | 
			
		|||
    }
 | 
			
		||||
    
 | 
			
		||||
    # remove empty layers from bottom
 | 
			
		||||
    my $first_object_layer_id = $self->config->raft_layers;
 | 
			
		||||
    while (@{$self->layers} && !@{$self->layers->[$first_object_layer_id]->slices}) {
 | 
			
		||||
        splice @{$self->layers}, $first_object_layer_id, 1;
 | 
			
		||||
        for (my $i = $first_object_layer_id; $i <= $#{$self->layers}; $i++) {
 | 
			
		||||
            $self->layers->[$i]->id($i);
 | 
			
		||||
    while (@{$self->layers} && !@{$self->layers->[0]->slices}) {
 | 
			
		||||
        shift @{$self->layers};
 | 
			
		||||
        for (my $i = 0; $i <= $#{$self->layers}; $i++) {
 | 
			
		||||
            $self->layers->[$i]->id( $self->layers->[$i]->id-1 );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
| 
						 | 
				
			
			@ -965,6 +964,9 @@ sub combine_infill {
 | 
			
		|||
 | 
			
		||||
sub generate_support_material {
 | 
			
		||||
    my $self = shift;
 | 
			
		||||
    
 | 
			
		||||
    # TODO: make this method idempotent by removing all support layers
 | 
			
		||||
    # before checking whether we need to generate support or not
 | 
			
		||||
    return unless ($self->config->support_material || $self->config->raft_layers > 0)
 | 
			
		||||
        && scalar(@{$self->layers}) >= 2;
 | 
			
		||||
    
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -132,7 +132,7 @@ sub contact_area {
 | 
			
		|||
                # If a threshold angle was specified, use a different logic for detecting overhangs.
 | 
			
		||||
                if (defined $threshold_rad
 | 
			
		||||
                    || $layer_id < $self->object_config->support_material_enforce_layers
 | 
			
		||||
                    || $self->object_config->raft_layers > 0) {
 | 
			
		||||
                    || ($self->object_config->raft_layers > 0 && $layer_id == 0)) {
 | 
			
		||||
                    my $d = defined $threshold_rad
 | 
			
		||||
                        ? scale $lower_layer->height * ((cos $threshold_rad) / (sin $threshold_rad))
 | 
			
		||||
                        : 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -608,7 +608,7 @@ sub generate_toolpaths {
 | 
			
		|||
            my $mm3_per_mm = $interface_flow->mm3_per_mm($layer->height);
 | 
			
		||||
            @loops = map Slic3r::ExtrusionPath->new(
 | 
			
		||||
                polyline    => $_,
 | 
			
		||||
                role        => EXTR_ROLE_SUPPORTMATERIAL,
 | 
			
		||||
                role        => EXTR_ROLE_SUPPORTMATERIAL_INTERFACE,
 | 
			
		||||
                mm3_per_mm  => $mm3_per_mm,
 | 
			
		||||
                width       => $interface_flow->width,
 | 
			
		||||
                height      => $layer->height,
 | 
			
		||||
| 
						 | 
				
			
			@ -656,7 +656,7 @@ sub generate_toolpaths {
 | 
			
		|||
                
 | 
			
		||||
                push @paths, map Slic3r::ExtrusionPath->new(
 | 
			
		||||
                    polyline    => Slic3r::Polyline->new(@$_),
 | 
			
		||||
                    role        => EXTR_ROLE_SUPPORTMATERIAL,
 | 
			
		||||
                    role        => EXTR_ROLE_SUPPORTMATERIAL_INTERFACE,
 | 
			
		||||
                    mm3_per_mm  => $mm3_per_mm,
 | 
			
		||||
                    width       => $params->{flow}->width,
 | 
			
		||||
                    height      => $layer->height,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -293,6 +293,9 @@ $j
 | 
			
		|||
                        (default: $config->{top_solid_infill_speed})
 | 
			
		||||
    --support-material-speed
 | 
			
		||||
                        Speed of support material print moves in mm/s (default: $config->{support_material_speed})
 | 
			
		||||
    --support-material-interface-speed
 | 
			
		||||
                        Speed of support material interface print moves in mm/s or % over support material
 | 
			
		||||
                        speed (default: $config->{support_material_interface_speed})
 | 
			
		||||
    --bridge-speed      Speed of bridge print moves in mm/s (default: $config->{bridge_speed})
 | 
			
		||||
    --gap-fill-speed    Speed of gap fill print moves in mm/s (default: $config->{gap_fill_speed})
 | 
			
		||||
    --first-layer-speed Speed of print moves for bottom layer, expressed either as an absolute
 | 
			
		||||
| 
						 | 
				
			
			@ -339,7 +342,7 @@ $j
 | 
			
		|||
                        home X axis [G28 X], disable motors [M84]).
 | 
			
		||||
    --layer-gcode       Load layer-change G-code from the supplied file (default: nothing).
 | 
			
		||||
    --toolchange-gcode  Load tool-change G-code from the supplied file (default: nothing).
 | 
			
		||||
    --randomize-start   Randomize starting point across layers (default: yes)
 | 
			
		||||
    --seam-position     Position of loop starting points (random/nearest/aligned, default: $config->{seam_position}).
 | 
			
		||||
    --external-perimeters-first Reverse perimeter order. (default: no)
 | 
			
		||||
    --spiral-vase       Experimental option to raise Z gradually when printing single-walled vases
 | 
			
		||||
                        (default: no)
 | 
			
		||||
| 
						 | 
				
			
			@ -356,10 +359,6 @@ $j
 | 
			
		|||
   Quality options (slower slicing):
 | 
			
		||||
    --extra-perimeters  Add more perimeters when needed (default: yes)
 | 
			
		||||
    --avoid-crossing-perimeters Optimize travel moves so that no perimeters are crossed (default: no)
 | 
			
		||||
    --start-perimeters-at-concave-points
 | 
			
		||||
                        Try to start perimeters at concave points if any (default: no)
 | 
			
		||||
    --start-perimeters-at-non-overhang
 | 
			
		||||
                        Try to start perimeters at non-overhang points if any (default: no)
 | 
			
		||||
    --thin-walls        Detect single-width walls (default: yes)
 | 
			
		||||
    --overhangs         Experimental option to use bridge flow, speed and fan for overhangs
 | 
			
		||||
                        (default: yes)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -82,12 +82,7 @@ use Slic3r::Geometry::Clipper qw(intersection_ex union_ex diff_ex diff_pl);
 | 
			
		|||
    my $res = diff_pl([$square_pl], []);
 | 
			
		||||
    is scalar(@$res), 1, 'no-op diff_pl returns the right number of polylines';
 | 
			
		||||
    isa_ok $res->[0], 'Slic3r::Polyline', 'no-op diff_pl result';
 | 
			
		||||
    
 | 
			
		||||
    ### NOTE: this test will fail when a bug in Clipper is fixed that is currently
 | 
			
		||||
    ### causing loss of one segment when input polyline has coinciding endpoints.
 | 
			
		||||
    ### When the bug is fixed in Clipper, this test should be reverted from isnt() to is()
 | 
			
		||||
    ### and workarounds in Slic3r::Polygon::clip_as_polyline() should be removed.
 | 
			
		||||
    isnt scalar(@{$res->[0]}), scalar(@$square_pl), 'no-op diff_pl returns the unmodified input polyline';
 | 
			
		||||
    is scalar(@{$res->[0]}), scalar(@$square_pl), 'no-op diff_pl returns the unmodified input polyline';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
__END__
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										41
									
								
								t/flow.t
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								t/flow.t
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,41 @@
 | 
			
		|||
use Test::More tests => 1;
 | 
			
		||||
use strict;
 | 
			
		||||
use warnings;
 | 
			
		||||
 | 
			
		||||
BEGIN {
 | 
			
		||||
    use FindBin;
 | 
			
		||||
    use lib "$FindBin::Bin/../lib";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
use List::Util qw(first sum);
 | 
			
		||||
use Slic3r;
 | 
			
		||||
use Slic3r::Geometry qw(scale);
 | 
			
		||||
use Slic3r::Test;
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
    my $config = Slic3r::Config->new_from_defaults;
 | 
			
		||||
    $config->set('skirts', 1);
 | 
			
		||||
    $config->set('brim_width', 2);
 | 
			
		||||
    $config->set('perimeters', 3);
 | 
			
		||||
    $config->set('fill_density', 0.4);
 | 
			
		||||
    $config->set('bottom_solid_layers', 1);
 | 
			
		||||
    $config->set('first_layer_extrusion_width', 2);
 | 
			
		||||
    $config->set('first_layer_height', '100%');
 | 
			
		||||
    
 | 
			
		||||
    my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
 | 
			
		||||
    my @E_per_mm = ();
 | 
			
		||||
    Slic3r::GCode::Reader->new->parse(my $gcode = Slic3r::Test::gcode($print), sub {
 | 
			
		||||
        my ($self, $cmd, $args, $info) = @_;
 | 
			
		||||
        
 | 
			
		||||
        if ($self->Z == $config->layer_height) {  # only consider first layer
 | 
			
		||||
            if ($info->{extruding} && $info->{dist_XY} > 0) {
 | 
			
		||||
                push @E_per_mm, $info->{dist_E} / $info->{dist_XY};
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    my $E_per_mm_avg = sum(@E_per_mm) / @E_per_mm;
 | 
			
		||||
    ok !(defined first { abs($_ - $E_per_mm_avg) > 0.01 } @E_per_mm),
 | 
			
		||||
        'first_layer_extrusion_width applies to everything on first layer';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
__END__
 | 
			
		||||
							
								
								
									
										19
									
								
								t/gcode.t
									
										
									
									
									
								
							
							
						
						
									
										19
									
								
								t/gcode.t
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
use Test::More tests => 8;
 | 
			
		||||
use Test::More tests => 9;
 | 
			
		||||
use strict;
 | 
			
		||||
use warnings;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -9,7 +9,7 @@ BEGIN {
 | 
			
		|||
 | 
			
		||||
use List::Util qw(first);
 | 
			
		||||
use Slic3r;
 | 
			
		||||
use Slic3r::Geometry qw(scale);
 | 
			
		||||
use Slic3r::Geometry qw(scale convex_hull);
 | 
			
		||||
use Slic3r::Test;
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -46,6 +46,7 @@ use Slic3r::Test;
 | 
			
		|||
    # - complete objects does not crash
 | 
			
		||||
    # - no hard-coded "E" are generated
 | 
			
		||||
    # - Z moves are correctly generated for both objects
 | 
			
		||||
    # - no travel moves go outside skirt
 | 
			
		||||
    my $config = Slic3r::Config->new_from_defaults;
 | 
			
		||||
    $config->set('gcode_comments', 1);
 | 
			
		||||
    $config->set('complete_objects', 1);
 | 
			
		||||
| 
						 | 
				
			
			@ -56,16 +57,30 @@ use Slic3r::Test;
 | 
			
		|||
    my $print = Slic3r::Test::init_print('20mm_cube', config => $config, duplicate => 2);
 | 
			
		||||
    ok my $gcode = Slic3r::Test::gcode($print), "complete_objects";
 | 
			
		||||
    my @z_moves = ();
 | 
			
		||||
    my @travel_moves = ();  # array of scaled points
 | 
			
		||||
    my @extrusions = ();    # array of scaled points
 | 
			
		||||
    Slic3r::GCode::Reader->new->parse($gcode, sub {
 | 
			
		||||
        my ($self, $cmd, $args, $info) = @_;
 | 
			
		||||
        fail 'unexpected E argument' if defined $args->{E};
 | 
			
		||||
        if (defined $args->{Z}) {
 | 
			
		||||
            push @z_moves, $args->{Z};
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        if ($info->{dist_XY}) {
 | 
			
		||||
            if ($info->{extruding} || $args->{A}) {
 | 
			
		||||
                push @extrusions, Slic3r::Point->new_scale($info->{new_X}, $info->{new_Y});
 | 
			
		||||
            } else {
 | 
			
		||||
                push @travel_moves, Slic3r::Point->new_scale($info->{new_X}, $info->{new_Y})
 | 
			
		||||
                    if @extrusions;  # skip initial travel move to first skirt point
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    my $layer_count = 20/0.4;  # cube is 20mm tall
 | 
			
		||||
    is scalar(@z_moves), 2*$layer_count, 'complete_objects generates the correct number of Z moves';
 | 
			
		||||
    is_deeply [ @z_moves[0..($layer_count-1)] ], [ @z_moves[$layer_count..$#z_moves] ], 'complete_objects generates the correct Z moves';
 | 
			
		||||
    
 | 
			
		||||
    my $convex_hull = convex_hull(\@extrusions);
 | 
			
		||||
    ok !(defined first { !$convex_hull->contains_point($_) } @travel_moves), 'all travel moves happen within skirt';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										26
									
								
								t/geometry.t
									
										
									
									
									
								
							
							
						
						
									
										26
									
								
								t/geometry.t
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -2,7 +2,7 @@ use Test::More;
 | 
			
		|||
use strict;
 | 
			
		||||
use warnings;
 | 
			
		||||
 | 
			
		||||
plan tests => 29;
 | 
			
		||||
plan tests => 33;
 | 
			
		||||
 | 
			
		||||
BEGIN {
 | 
			
		||||
    use FindBin;
 | 
			
		||||
| 
						 | 
				
			
			@ -189,3 +189,27 @@ my $polygons = [
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
#==========================================================
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
    my $square = Slic3r::Polygon->new(
 | 
			
		||||
        [150,100],
 | 
			
		||||
        [200,100],
 | 
			
		||||
        [200,200],
 | 
			
		||||
        [100,200],
 | 
			
		||||
        [100,100],
 | 
			
		||||
    );
 | 
			
		||||
    is scalar(@{$square->concave_points(PI*4/3)}), 0, 'no concave vertices detected in convex polygon';
 | 
			
		||||
    is scalar(@{$square->convex_points(PI*2/3)}), 4, 'four convex vertices detected in square';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
    my $square = Slic3r::Polygon->new(
 | 
			
		||||
        [200,200],
 | 
			
		||||
        [100,200],
 | 
			
		||||
        [100,100],
 | 
			
		||||
        [150,100],
 | 
			
		||||
        [200,100],
 | 
			
		||||
    );
 | 
			
		||||
    is scalar(@{$square->concave_points(PI*4/3)}), 0, 'no concave vertices detected in convex polygon';
 | 
			
		||||
    is scalar(@{$square->convex_points(PI*2/3)}), 4, 'four convex vertices detected in square';
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										14
									
								
								t/multi.t
									
										
									
									
									
								
							
							
						
						
									
										14
									
								
								t/multi.t
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
use Test::More tests => 12;
 | 
			
		||||
use Test::More tests => 13;
 | 
			
		||||
use strict;
 | 
			
		||||
use warnings;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -10,6 +10,7 @@ BEGIN {
 | 
			
		|||
use List::Util qw(first);
 | 
			
		||||
use Slic3r;
 | 
			
		||||
use Slic3r::Geometry qw(scale convex_hull);
 | 
			
		||||
use Slic3r::Geometry::Clipper qw(offset);
 | 
			
		||||
use Slic3r::Test;
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -39,9 +40,11 @@ use Slic3r::Test;
 | 
			
		|||
                    : $config->temperature->[$tool];
 | 
			
		||||
                die 'standby temperature was not set before toolchange'
 | 
			
		||||
                    if $tool_temp[$tool] != $expected_temp + $config->standby_temperature_delta;
 | 
			
		||||
                
 | 
			
		||||
                # ignore initial toolchange
 | 
			
		||||
                push @toolchange_points, Slic3r::Point->new_scale($self->X, $self->Y);
 | 
			
		||||
            }
 | 
			
		||||
            $tool = $1;
 | 
			
		||||
            push @toolchange_points, Slic3r::Point->new_scale($self->X, $self->Y);
 | 
			
		||||
        } elsif ($cmd eq 'M104' || $cmd eq 'M109') {
 | 
			
		||||
            my $t = $args->{T} // $tool;
 | 
			
		||||
            if ($tool_temp[$t] == 0) {
 | 
			
		||||
| 
						 | 
				
			
			@ -55,7 +58,12 @@ use Slic3r::Test;
 | 
			
		|||
        }
 | 
			
		||||
    });
 | 
			
		||||
    my $convex_hull = convex_hull(\@extrusion_points);
 | 
			
		||||
    ok !(first { $convex_hull->contains_point($_) } @toolchange_points), 'all toolchanges happen outside skirt';
 | 
			
		||||
    ok !(defined first { $convex_hull->contains_point($_) } @toolchange_points), 'all toolchanges happen outside skirt';
 | 
			
		||||
    
 | 
			
		||||
    # offset the skirt by the maximum displacement between extruders plus a safety extra margin
 | 
			
		||||
    my $delta = scale(20 * sqrt(2) + 1);
 | 
			
		||||
    my $outer_convex_hull = offset([$convex_hull], +$delta)->[0];
 | 
			
		||||
    ok !(defined first { !$outer_convex_hull->contains_point($_) } @toolchange_points), 'all toolchanges happen within expected area';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -85,28 +85,6 @@ use Slic3r::Test;
 | 
			
		|||
        ok !$has_outwards_move, 'move inwards after completing external loop';
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    {
 | 
			
		||||
        $config->set('start_perimeters_at_concave_points', 1);
 | 
			
		||||
        my $print = Slic3r::Test::init_print('L', config => $config);
 | 
			
		||||
        my $loop_starts_from_convex_point = 0;
 | 
			
		||||
        my $cur_loop;
 | 
			
		||||
        Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
 | 
			
		||||
            my ($self, $cmd, $args, $info) = @_;
 | 
			
		||||
            
 | 
			
		||||
            if ($info->{extruding} && $info->{dist_XY} > 0) {
 | 
			
		||||
                $cur_loop ||= [ [$self->X, $self->Y] ];
 | 
			
		||||
                push @$cur_loop, [ @$info{qw(new_X new_Y)} ];
 | 
			
		||||
            } else {
 | 
			
		||||
                if ($cur_loop) {
 | 
			
		||||
                    $loop_starts_from_convex_point = 1
 | 
			
		||||
                        if Slic3r::Geometry::angle3points(@$cur_loop[0,-1,1]) >= PI;
 | 
			
		||||
                    $cur_loop = undef;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        ok !$loop_starts_from_convex_point, 'avoid starting from convex points';
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    {
 | 
			
		||||
        $config->set('perimeters', 1);
 | 
			
		||||
        $config->set('perimeter_speed', 77);
 | 
			
		||||
| 
						 | 
				
			
			@ -265,4 +243,11 @@ use Slic3r::Test;
 | 
			
		|||
        'overhangs printed with bridge speed';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
    my $config = Slic3r::Config->new_from_defaults;
 | 
			
		||||
    $config->set('seam_position', 'random');
 | 
			
		||||
    my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
 | 
			
		||||
    ok Slic3r::Test::gcode($print), 'successful generation of G-code with seam_position = random';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
__END__
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,7 +16,7 @@ use Slic3r::Test;
 | 
			
		|||
    $config->set('skirts', 1);
 | 
			
		||||
    $config->set('skirt_height', 2);
 | 
			
		||||
    $config->set('perimeters', 0);
 | 
			
		||||
    $config->set('perimeter_speed', 99);
 | 
			
		||||
    $config->set('support_material_speed', 99);
 | 
			
		||||
    $config->set('cooling', 0);                     # to prevent speeds to be altered
 | 
			
		||||
    $config->set('first_layer_speed', '100%');      # to prevent speeds to be altered
 | 
			
		||||
    
 | 
			
		||||
| 
						 | 
				
			
			@ -33,7 +33,7 @@ use Slic3r::Test;
 | 
			
		|||
            if (defined $self->Z) {
 | 
			
		||||
                $layers_with_skirt{$self->Z} //= 0;
 | 
			
		||||
                $layers_with_skirt{$self->Z} = 1
 | 
			
		||||
                    if $info->{extruding} && ($args->{F} // $self->F) == $config->perimeter_speed*60;
 | 
			
		||||
                    if $info->{extruding} && ($args->{F} // $self->F) == $config->support_material_speed*60;
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        fail "wrong number of layers with skirt"
 | 
			
		||||
| 
						 | 
				
			
			@ -50,6 +50,7 @@ use Slic3r::Test;
 | 
			
		|||
    $config->set('top_solid_layers', 0);            # to prevent solid shells and their speeds
 | 
			
		||||
    $config->set('bottom_solid_layers', 0);         # to prevent solid shells and their speeds
 | 
			
		||||
    $config->set('brim_width', 5);
 | 
			
		||||
    $config->set('support_material_speed', 99);
 | 
			
		||||
    $config->set('cooling', 0);                     # to prevent speeds to be altered
 | 
			
		||||
    $config->set('first_layer_speed', '100%');      # to prevent speeds to be altered
 | 
			
		||||
    
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
use Test::More tests => 15;
 | 
			
		||||
use Test::More tests => 16;
 | 
			
		||||
use strict;
 | 
			
		||||
use warnings;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -130,10 +130,10 @@ use Slic3r::Test;
 | 
			
		|||
        'first object layer is completely supported by raft';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
foreach my $raft_layers (2, 70) {
 | 
			
		||||
    my $config = Slic3r::Config->new_from_defaults;
 | 
			
		||||
    $config->set('skirts', 0);
 | 
			
		||||
    $config->set('raft_layers', 2);
 | 
			
		||||
    $config->set('raft_layers', $raft_layers);
 | 
			
		||||
    $config->set('layer_height', 0.35);
 | 
			
		||||
    $config->set('first_layer_height', 0.3);
 | 
			
		||||
    $config->set('nozzle_diameter', [0.5]);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										14
									
								
								t/svg.t
									
										
									
									
									
								
							
							
						
						
									
										14
									
								
								t/svg.t
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
use Test::More tests => 1;
 | 
			
		||||
use Test::More tests => 2;
 | 
			
		||||
use strict;
 | 
			
		||||
use warnings;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -17,7 +17,19 @@ use Slic3r::Test;
 | 
			
		|||
        $print->print->export_svg(output_fh => $fh, quiet => 1);
 | 
			
		||||
        $fh->close;
 | 
			
		||||
    };
 | 
			
		||||
    die $@ if $@;
 | 
			
		||||
    ok !$@, 'successful SVG export';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
    my $print = Slic3r::Test::init_print('two_hollow_squares');
 | 
			
		||||
    eval {
 | 
			
		||||
        my $fh = IO::Scalar->new(\my $gcode);
 | 
			
		||||
        $print->print->export_svg(output_fh => $fh, quiet => 1);
 | 
			
		||||
        $fh->close;
 | 
			
		||||
    };
 | 
			
		||||
    die $@ if $@;
 | 
			
		||||
    ok !$@, 'successful SVG export of object with two islands';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
__END__
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										6
									
								
								t/thin.t
									
										
									
									
									
								
							
							
						
						
									
										6
									
								
								t/thin.t
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
use Test::More tests => 11;
 | 
			
		||||
use Test::More tests => 13;
 | 
			
		||||
use strict;
 | 
			
		||||
use warnings;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -62,7 +62,9 @@ if (0) {
 | 
			
		|||
    );
 | 
			
		||||
    my $expolygon = Slic3r::ExPolygon->new($square, $hole_in_square);
 | 
			
		||||
    my $res = $expolygon->medial_axis(scale 1, scale 0.5);
 | 
			
		||||
    is scalar(@$res), 1, 'medial axis of a square shape is a single closed loop';
 | 
			
		||||
    is scalar(@$res), 1, 'medial axis of a square shape is a single path';
 | 
			
		||||
    isa_ok $res->[0], 'Slic3r::Polyline', 'medial axis result is a polyline';
 | 
			
		||||
    ok $res->[0]->first_point->coincides_with($res->[0]->last_point), 'polyline forms a closed loop';
 | 
			
		||||
    ok $res->[0]->length > $hole_in_square->length && $res->[0]->length < $square->length,
 | 
			
		||||
        'medial axis loop has reasonable length';
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										47
									
								
								xs/MANIFEST
									
										
									
									
									
								
							
							
						
						
									
										47
									
								
								xs/MANIFEST
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1,13 +1,26 @@
 | 
			
		|||
<<<<<<<				HEAD
 | 
			
		||||
=======
 | 
			
		||||
>>>>>>>				master
 | 
			
		||||
Build.PL
 | 
			
		||||
buildtmp/main.xs
 | 
			
		||||
buildtmp/typemap
 | 
			
		||||
buildtmp/XS.c
 | 
			
		||||
buildtmp/XS.o
 | 
			
		||||
lib/Slic3r/XS.pm
 | 
			
		||||
MANIFEST			This list of files
 | 
			
		||||
src/admesh/connect.c
 | 
			
		||||
src/admesh/connect.o
 | 
			
		||||
src/admesh/normals.c
 | 
			
		||||
src/admesh/normals.o
 | 
			
		||||
src/admesh/shared.c
 | 
			
		||||
src/admesh/shared.o
 | 
			
		||||
src/admesh/stl.h
 | 
			
		||||
src/admesh/stl_io.c
 | 
			
		||||
src/admesh/stl_io.o
 | 
			
		||||
src/admesh/stlinit.c
 | 
			
		||||
src/admesh/stlinit.o
 | 
			
		||||
src/admesh/util.c
 | 
			
		||||
src/admesh/util.o
 | 
			
		||||
src/boost/aligned_storage.hpp
 | 
			
		||||
src/boost/array.hpp
 | 
			
		||||
src/boost/assert.hpp
 | 
			
		||||
| 
						 | 
				
			
			@ -1650,76 +1663,109 @@ src/boost/version.hpp
 | 
			
		|||
src/boost/visit_each.hpp
 | 
			
		||||
src/BoundingBox.cpp
 | 
			
		||||
src/BoundingBox.hpp
 | 
			
		||||
src/BoundingBox.o
 | 
			
		||||
src/clipper.cpp
 | 
			
		||||
src/clipper.hpp
 | 
			
		||||
src/clipper.o
 | 
			
		||||
src/ClipperUtils.cpp
 | 
			
		||||
src/ClipperUtils.hpp
 | 
			
		||||
src/ClipperUtils.o
 | 
			
		||||
src/Config.cpp
 | 
			
		||||
src/Config.hpp
 | 
			
		||||
src/Config.o
 | 
			
		||||
src/ExPolygon.cpp
 | 
			
		||||
src/ExPolygon.hpp
 | 
			
		||||
src/ExPolygon.o
 | 
			
		||||
src/ExPolygonCollection.cpp
 | 
			
		||||
src/ExPolygonCollection.hpp
 | 
			
		||||
src/ExPolygonCollection.o
 | 
			
		||||
src/Extruder.cpp
 | 
			
		||||
src/Extruder.hpp
 | 
			
		||||
src/Extruder.o
 | 
			
		||||
src/ExtrusionEntity.cpp
 | 
			
		||||
src/ExtrusionEntity.hpp
 | 
			
		||||
src/ExtrusionEntity.o
 | 
			
		||||
src/ExtrusionEntityCollection.cpp
 | 
			
		||||
src/ExtrusionEntityCollection.hpp
 | 
			
		||||
src/ExtrusionEntityCollection.o
 | 
			
		||||
src/Flow.cpp
 | 
			
		||||
src/Flow.hpp
 | 
			
		||||
src/Flow.o
 | 
			
		||||
src/GCode.hpp
 | 
			
		||||
src/Geometry.cpp
 | 
			
		||||
src/Geometry.hpp
 | 
			
		||||
src/Geometry.o
 | 
			
		||||
src/Layer.hpp
 | 
			
		||||
src/Line.cpp
 | 
			
		||||
src/Line.hpp
 | 
			
		||||
src/Line.o
 | 
			
		||||
src/Model.cpp
 | 
			
		||||
src/Model.hpp
 | 
			
		||||
src/Model.o
 | 
			
		||||
src/MotionPlanner.cpp
 | 
			
		||||
src/MotionPlanner.hpp
 | 
			
		||||
src/MotionPlanner.o
 | 
			
		||||
src/MultiPoint.cpp
 | 
			
		||||
src/MultiPoint.hpp
 | 
			
		||||
src/MultiPoint.o
 | 
			
		||||
src/myinit.h
 | 
			
		||||
src/perlglue.hpp
 | 
			
		||||
src/Point.cpp
 | 
			
		||||
src/Point.hpp
 | 
			
		||||
src/Point.o
 | 
			
		||||
src/poly2tri/common/shapes.cc
 | 
			
		||||
src/poly2tri/common/shapes.h
 | 
			
		||||
src/poly2tri/common/shapes.o
 | 
			
		||||
src/poly2tri/common/utils.h
 | 
			
		||||
src/poly2tri/poly2tri.h
 | 
			
		||||
src/poly2tri/sweep/advancing_front.cc
 | 
			
		||||
src/poly2tri/sweep/advancing_front.h
 | 
			
		||||
src/poly2tri/sweep/advancing_front.o
 | 
			
		||||
src/poly2tri/sweep/cdt.cc
 | 
			
		||||
src/poly2tri/sweep/cdt.h
 | 
			
		||||
src/poly2tri/sweep/cdt.o
 | 
			
		||||
src/poly2tri/sweep/sweep.cc
 | 
			
		||||
src/poly2tri/sweep/sweep.h
 | 
			
		||||
src/poly2tri/sweep/sweep.o
 | 
			
		||||
src/poly2tri/sweep/sweep_context.cc
 | 
			
		||||
src/poly2tri/sweep/sweep_context.h
 | 
			
		||||
src/poly2tri/sweep/sweep_context.o
 | 
			
		||||
src/Polygon.cpp
 | 
			
		||||
src/Polygon.hpp
 | 
			
		||||
src/Polygon.o
 | 
			
		||||
src/Polyline.cpp
 | 
			
		||||
src/Polyline.hpp
 | 
			
		||||
src/Polyline.o
 | 
			
		||||
src/PolylineCollection.cpp
 | 
			
		||||
src/PolylineCollection.hpp
 | 
			
		||||
src/PolylineCollection.o
 | 
			
		||||
src/polypartition.cpp
 | 
			
		||||
src/polypartition.h
 | 
			
		||||
src/polypartition.o
 | 
			
		||||
src/ppport.h
 | 
			
		||||
src/Print.cpp
 | 
			
		||||
src/Print.hpp
 | 
			
		||||
src/Print.o
 | 
			
		||||
src/PrintConfig.cpp
 | 
			
		||||
src/PrintConfig.hpp
 | 
			
		||||
src/PrintConfig.o
 | 
			
		||||
src/Surface.cpp
 | 
			
		||||
src/Surface.hpp
 | 
			
		||||
src/Surface.o
 | 
			
		||||
src/SurfaceCollection.cpp
 | 
			
		||||
src/SurfaceCollection.hpp
 | 
			
		||||
src/SurfaceCollection.o
 | 
			
		||||
src/SVG.cpp
 | 
			
		||||
src/SVG.hpp
 | 
			
		||||
src/SVG.o
 | 
			
		||||
src/TriangleMesh.cpp
 | 
			
		||||
src/TriangleMesh.hpp
 | 
			
		||||
src/TriangleMesh.o
 | 
			
		||||
src/utils.cpp
 | 
			
		||||
src/utils.o
 | 
			
		||||
src/visilibity.cpp
 | 
			
		||||
src/visilibity.hpp
 | 
			
		||||
src/visilibity.o
 | 
			
		||||
t/01_trianglemesh.t
 | 
			
		||||
t/03_point.t
 | 
			
		||||
t/04_expolygon.t
 | 
			
		||||
| 
						 | 
				
			
			@ -1737,6 +1783,7 @@ t/15_config.t
 | 
			
		|||
t/16_flow.t
 | 
			
		||||
t/17_boundingbox.t
 | 
			
		||||
t/18_motionplanner.t
 | 
			
		||||
t/19_model.t
 | 
			
		||||
xsp/BoundingBox.xsp
 | 
			
		||||
xsp/Clipper.xsp
 | 
			
		||||
xsp/Config.xsp
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -358,30 +358,9 @@ void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject,
 | 
			
		|||
    for (Slic3r::Polygons::const_iterator polygon = subject.begin(); polygon != subject.end(); ++polygon)
 | 
			
		||||
        polylines.push_back(*polygon);  // implicit call to split_at_first_point()
 | 
			
		||||
    
 | 
			
		||||
    /* Clipper will remove a polyline segment if first point coincides with last one.
 | 
			
		||||
       Until that bug is not fixed upstream, we move one of those points slightly. */
 | 
			
		||||
    for (Slic3r::Polylines::iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline)
 | 
			
		||||
        polyline->points.front().translate(1, 0);
 | 
			
		||||
    
 | 
			
		||||
    // perform clipping
 | 
			
		||||
    _clipper(clipType, polylines, clip, retval, safety_offset_);
 | 
			
		||||
    
 | 
			
		||||
    // compensate for the above hack
 | 
			
		||||
    for (Slic3r::Polylines::iterator polyline = retval.begin(); polyline != retval.end(); ++polyline) {
 | 
			
		||||
        for (Slic3r::Polylines::iterator subj_polyline = polylines.begin(); subj_polyline != polylines.end(); ++subj_polyline) {
 | 
			
		||||
            // if first point of clipped line coincides with first point of subject line, compensate for hack
 | 
			
		||||
            if (polyline->points.front().coincides_with(subj_polyline->points.front())) {
 | 
			
		||||
                polyline->points.front().translate(-1, 0);
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            // since Clipper does not preserve orientation of polylines, check last point too
 | 
			
		||||
            if (polyline->points.back().coincides_with(subj_polyline->points.front())) {
 | 
			
		||||
                polyline->points.back().translate(-1, 0);
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /* If the split_at_first_point() call above happens to split the polygon inside the clipping area
 | 
			
		||||
       we would get two consecutive polylines instead of a single one, so we go through them in order
 | 
			
		||||
       to recombine continuous polylines. */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -320,6 +320,15 @@ DynamicConfig::option(const t_config_option_key opt_key, bool create) {
 | 
			
		|||
    return this->options[opt_key];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template<class T>
 | 
			
		||||
T*
 | 
			
		||||
DynamicConfig::opt(const t_config_option_key opt_key, bool create) {
 | 
			
		||||
    return dynamic_cast<T*>(this->option(opt_key, create));
 | 
			
		||||
}
 | 
			
		||||
template ConfigOptionInt* DynamicConfig::opt<ConfigOptionInt>(const t_config_option_key opt_key, bool create);
 | 
			
		||||
template ConfigOptionBool* DynamicConfig::opt<ConfigOptionBool>(const t_config_option_key opt_key, bool create);
 | 
			
		||||
template ConfigOptionBools* DynamicConfig::opt<ConfigOptionBools>(const t_config_option_key opt_key, bool create);
 | 
			
		||||
 | 
			
		||||
const ConfigOption*
 | 
			
		||||
DynamicConfig::option(const t_config_option_key opt_key) const {
 | 
			
		||||
    return const_cast<DynamicConfig*>(this)->option(opt_key, false);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -491,6 +491,7 @@ class DynamicConfig : public ConfigBase
 | 
			
		|||
    DynamicConfig& operator= (DynamicConfig other);
 | 
			
		||||
    void swap(DynamicConfig &other);
 | 
			
		||||
    ~DynamicConfig();
 | 
			
		||||
    template<class T> T* opt(const t_config_option_key opt_key, bool create = false);
 | 
			
		||||
    ConfigOption* option(const t_config_option_key opt_key, bool create = false);
 | 
			
		||||
    const ConfigOption* option(const t_config_option_key opt_key) const;
 | 
			
		||||
    void keys(t_config_option_keys *keys) const;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -158,7 +158,9 @@ ExPolygon::medial_axis(double max_width, double min_width, Polylines* polylines)
 | 
			
		|||
    ma.build(polylines);
 | 
			
		||||
    
 | 
			
		||||
    // extend initial and final segments of each polyline (they will be clipped)
 | 
			
		||||
    // unless they represent closed loops
 | 
			
		||||
    for (Polylines::iterator polyline = polylines->begin(); polyline != polylines->end(); ++polyline) {
 | 
			
		||||
        if (polyline->points.front().coincides_with(polyline->points.back())) continue;
 | 
			
		||||
        polyline->extend_start(max_width);
 | 
			
		||||
        polyline->extend_end(max_width);
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -72,23 +72,21 @@ ExtrusionPath::is_perimeter() const
 | 
			
		|||
{
 | 
			
		||||
    return this->role == erPerimeter
 | 
			
		||||
        || this->role == erExternalPerimeter
 | 
			
		||||
        || this->role == erOverhangPerimeter
 | 
			
		||||
        || this->role == erContourInternalPerimeter;
 | 
			
		||||
        || this->role == erOverhangPerimeter;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool
 | 
			
		||||
ExtrusionPath::is_fill() const
 | 
			
		||||
{
 | 
			
		||||
    return this->role == erFill
 | 
			
		||||
        || this->role == erSolidFill
 | 
			
		||||
        || this->role == erTopSolidFill;
 | 
			
		||||
    return this->role == erInternalInfill
 | 
			
		||||
        || this->role == erSolidInfill
 | 
			
		||||
        || this->role == erTopSolidInfill;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool
 | 
			
		||||
ExtrusionPath::is_bridge() const
 | 
			
		||||
{
 | 
			
		||||
    return this->role == erBridge
 | 
			
		||||
        || this->role == erInternalBridge
 | 
			
		||||
    return this->role == erBridgeInfill
 | 
			
		||||
        || this->role == erOverhangPerimeter;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -223,7 +221,7 @@ ExtrusionLoop::length() const
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
ExtrusionLoop::split_at(const Point &point)
 | 
			
		||||
ExtrusionLoop::split_at_vertex(const Point &point)
 | 
			
		||||
{
 | 
			
		||||
    for (ExtrusionPaths::iterator path = this->paths.begin(); path != this->paths.end(); ++path) {
 | 
			
		||||
        int idx = path->polyline.find_point(point);
 | 
			
		||||
| 
						 | 
				
			
			@ -241,7 +239,7 @@ ExtrusionLoop::split_at(const Point &point)
 | 
			
		|||
                {
 | 
			
		||||
                    ExtrusionPath p = *path;
 | 
			
		||||
                    p.polyline.points.erase(p.polyline.points.begin(), p.polyline.points.begin() + idx);
 | 
			
		||||
                    if (!p.polyline.points.empty()) new_paths.push_back(p);
 | 
			
		||||
                    if (p.polyline.is_valid()) new_paths.push_back(p);
 | 
			
		||||
                }
 | 
			
		||||
            
 | 
			
		||||
                // then we add all paths until the end of current path list
 | 
			
		||||
| 
						 | 
				
			
			@ -254,7 +252,7 @@ ExtrusionLoop::split_at(const Point &point)
 | 
			
		|||
                {
 | 
			
		||||
                    ExtrusionPath p = *path;
 | 
			
		||||
                    p.polyline.points.erase(p.polyline.points.begin() + idx + 1, p.polyline.points.end());
 | 
			
		||||
                    if (!p.polyline.points.empty()) new_paths.push_back(p);
 | 
			
		||||
                    if (p.polyline.is_valid()) new_paths.push_back(p);
 | 
			
		||||
                }
 | 
			
		||||
                // we can now override the old path list with the new one and stop looping
 | 
			
		||||
                this->paths = new_paths;
 | 
			
		||||
| 
						 | 
				
			
			@ -265,6 +263,39 @@ ExtrusionLoop::split_at(const Point &point)
 | 
			
		|||
    CONFESS("Point not found");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
ExtrusionLoop::split_at(const Point &point)
 | 
			
		||||
{
 | 
			
		||||
    if (this->paths.empty()) return;
 | 
			
		||||
    
 | 
			
		||||
    // find the closest path and closest point
 | 
			
		||||
    size_t path_idx = 0;
 | 
			
		||||
    Point p = this->paths.front().first_point();
 | 
			
		||||
    double min = point.distance_to(p);
 | 
			
		||||
    for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) {
 | 
			
		||||
        Point p_tmp = point.projection_onto(path->polyline);
 | 
			
		||||
        double dist = point.distance_to(p_tmp);
 | 
			
		||||
        if (dist < min) {
 | 
			
		||||
            p = p_tmp;
 | 
			
		||||
            min = dist;
 | 
			
		||||
            path_idx = path - this->paths.begin();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // now split path_idx in two parts
 | 
			
		||||
    ExtrusionPath p1 = this->paths[path_idx];
 | 
			
		||||
    ExtrusionPath p2 = p1;
 | 
			
		||||
    this->paths[path_idx].polyline.split_at(p, &p1.polyline, &p2.polyline);
 | 
			
		||||
    
 | 
			
		||||
    // install the two paths
 | 
			
		||||
    this->paths.erase(this->paths.begin() + path_idx);
 | 
			
		||||
    if (p2.polyline.is_valid()) this->paths.insert(this->paths.begin() + path_idx, p2);
 | 
			
		||||
    if (p1.polyline.is_valid()) this->paths.insert(this->paths.begin() + path_idx, p1);
 | 
			
		||||
    
 | 
			
		||||
    // split at the new vertex
 | 
			
		||||
    this->split_at_vertex(p);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
ExtrusionLoop::clip_end(double distance, ExtrusionPaths* paths) const
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,19 +11,26 @@ class ExPolygonCollection;
 | 
			
		|||
class ExtrusionEntityCollection;
 | 
			
		||||
class Extruder;
 | 
			
		||||
 | 
			
		||||
/* Each ExtrusionRole value identifies a distinct set of { extruder, speed } */
 | 
			
		||||
enum ExtrusionRole {
 | 
			
		||||
    erPerimeter,
 | 
			
		||||
    erExternalPerimeter,
 | 
			
		||||
    erOverhangPerimeter,
 | 
			
		||||
    erContourInternalPerimeter,
 | 
			
		||||
    erFill,
 | 
			
		||||
    erSolidFill,
 | 
			
		||||
    erTopSolidFill,
 | 
			
		||||
    erBridge,
 | 
			
		||||
    erInternalBridge,
 | 
			
		||||
    erInternalInfill,
 | 
			
		||||
    erSolidInfill,
 | 
			
		||||
    erTopSolidInfill,
 | 
			
		||||
    erBridgeInfill,
 | 
			
		||||
    erGapFill,
 | 
			
		||||
    erSkirt,
 | 
			
		||||
    erSupportMaterial,
 | 
			
		||||
    erGapFill,
 | 
			
		||||
    erSupportMaterialInterface,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Special flags describing loop */
 | 
			
		||||
enum ExtrusionLoopRole {
 | 
			
		||||
    elrDefault,
 | 
			
		||||
    elrExternalPerimeter,
 | 
			
		||||
    elrContourInternalPerimeter,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class ExtrusionEntity
 | 
			
		||||
| 
						 | 
				
			
			@ -47,7 +54,7 @@ class ExtrusionPath : public ExtrusionEntity
 | 
			
		|||
    float width;
 | 
			
		||||
    float height;
 | 
			
		||||
    
 | 
			
		||||
    ExtrusionPath() : mm3_per_mm(-1), width(-1), height(-1) {};
 | 
			
		||||
    ExtrusionPath(ExtrusionRole role) : role(role), mm3_per_mm(-1), width(-1), height(-1) {};
 | 
			
		||||
    ExtrusionPath* clone() const;
 | 
			
		||||
    void reverse();
 | 
			
		||||
    Point first_point() const;
 | 
			
		||||
| 
						 | 
				
			
			@ -74,7 +81,9 @@ class ExtrusionLoop : public ExtrusionEntity
 | 
			
		|||
{
 | 
			
		||||
    public:
 | 
			
		||||
    ExtrusionPaths paths;
 | 
			
		||||
    ExtrusionLoopRole role;
 | 
			
		||||
    
 | 
			
		||||
    ExtrusionLoop(ExtrusionLoopRole role = elrDefault) : role(role) {};
 | 
			
		||||
    operator Polygon() const;
 | 
			
		||||
    ExtrusionLoop* clone() const;
 | 
			
		||||
    bool make_clockwise();
 | 
			
		||||
| 
						 | 
				
			
			@ -84,6 +93,7 @@ class ExtrusionLoop : public ExtrusionEntity
 | 
			
		|||
    Point last_point() const;
 | 
			
		||||
    void polygon(Polygon* polygon) const;
 | 
			
		||||
    double length() const;
 | 
			
		||||
    void split_at_vertex(const Point &point);
 | 
			
		||||
    void split_at(const Point &point);
 | 
			
		||||
    void clip_end(double distance, ExtrusionPaths* paths) const;
 | 
			
		||||
    bool has_overhang_point(const Point &point) const;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -123,6 +123,21 @@ MedialAxis::build(Polylines* polylines)
 | 
			
		|||
    
 | 
			
		||||
    construct_voronoi(this->lines.begin(), this->lines.end(), &this->vd);
 | 
			
		||||
    
 | 
			
		||||
    /*
 | 
			
		||||
    // DEBUG: dump all Voronoi edges
 | 
			
		||||
    {
 | 
			
		||||
        for (VD::const_edge_iterator edge = this->vd.edges().begin(); edge != this->vd.edges().end(); ++edge) {
 | 
			
		||||
            if (edge->is_infinite()) continue;
 | 
			
		||||
            
 | 
			
		||||
            Polyline polyline;
 | 
			
		||||
            polyline.points.push_back(Point( edge->vertex0()->x(), edge->vertex0()->y() ));
 | 
			
		||||
            polyline.points.push_back(Point( edge->vertex1()->x(), edge->vertex1()->y() ));
 | 
			
		||||
            polylines->push_back(polyline);
 | 
			
		||||
        }
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    */
 | 
			
		||||
    
 | 
			
		||||
    // collect valid edges (i.e. prune those not belonging to MAT)
 | 
			
		||||
    // note: this keeps twins, so it contains twice the number of the valid edges
 | 
			
		||||
    this->edges.clear();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,13 @@
 | 
			
		|||
#include "MultiPoint.hpp"
 | 
			
		||||
#include "BoundingBox.hpp"
 | 
			
		||||
 | 
			
		||||
namespace Slic3r {
 | 
			
		||||
 | 
			
		||||
MultiPoint::operator Points() const
 | 
			
		||||
{
 | 
			
		||||
    return this->points;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
MultiPoint::scale(double factor)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -64,6 +70,12 @@ MultiPoint::find_point(const Point &point) const
 | 
			
		|||
    return -1;  // not found
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
MultiPoint::bounding_box(BoundingBox* bb) const
 | 
			
		||||
{
 | 
			
		||||
    *bb = BoundingBox(this->points);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Points
 | 
			
		||||
MultiPoint::_douglas_peucker(const Points &points, const double tolerance)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,17 +2,21 @@
 | 
			
		|||
#define slic3r_MultiPoint_hpp_
 | 
			
		||||
 | 
			
		||||
#include <myinit.h>
 | 
			
		||||
#include "Line.hpp"
 | 
			
		||||
#include "Point.hpp"
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include "Line.hpp"
 | 
			
		||||
#include "Point.hpp"
 | 
			
		||||
 | 
			
		||||
namespace Slic3r {
 | 
			
		||||
 | 
			
		||||
class BoundingBox;
 | 
			
		||||
 | 
			
		||||
class MultiPoint
 | 
			
		||||
{
 | 
			
		||||
    public:
 | 
			
		||||
    Points points;
 | 
			
		||||
    
 | 
			
		||||
    operator Points() const;
 | 
			
		||||
    void scale(double factor);
 | 
			
		||||
    void translate(double x, double y);
 | 
			
		||||
    void rotate(double angle, const Point ¢er);
 | 
			
		||||
| 
						 | 
				
			
			@ -23,6 +27,8 @@ class MultiPoint
 | 
			
		|||
    double length() const;
 | 
			
		||||
    bool is_valid() const;
 | 
			
		||||
    int find_point(const Point &point) const;
 | 
			
		||||
    void bounding_box(BoundingBox* bb) const;
 | 
			
		||||
    
 | 
			
		||||
    static Points _douglas_peucker(const Points &points, const double tolerance);
 | 
			
		||||
    
 | 
			
		||||
    #ifdef SLIC3RXS
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,10 +1,17 @@
 | 
			
		|||
#include "Point.hpp"
 | 
			
		||||
#include "Line.hpp"
 | 
			
		||||
#include "MultiPoint.hpp"
 | 
			
		||||
#include <cmath>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
 | 
			
		||||
namespace Slic3r {
 | 
			
		||||
 | 
			
		||||
Point::Point(double x, double y)
 | 
			
		||||
{
 | 
			
		||||
    this->x = lrint(x);
 | 
			
		||||
    this->y = lrint(y);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool
 | 
			
		||||
Point::operator==(const Point& rhs) const
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -138,6 +145,69 @@ Point::ccw(const Line &line) const
 | 
			
		|||
    return this->ccw(line.a, line.b);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Point
 | 
			
		||||
Point::projection_onto(const MultiPoint &poly) const
 | 
			
		||||
{
 | 
			
		||||
    Point running_projection = poly.first_point();
 | 
			
		||||
    double running_min = this->distance_to(running_projection);
 | 
			
		||||
    
 | 
			
		||||
    Lines lines = poly.lines();
 | 
			
		||||
    for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) {
 | 
			
		||||
        Point point_temp = this->projection_onto(*line);
 | 
			
		||||
        if (this->distance_to(point_temp) < running_min) {
 | 
			
		||||
	        running_projection = point_temp;
 | 
			
		||||
	        running_min = this->distance_to(running_projection);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return running_projection;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Point
 | 
			
		||||
Point::projection_onto(const Line &line) const
 | 
			
		||||
{
 | 
			
		||||
    if (line.a.coincides_with(line.b)) return line.a;
 | 
			
		||||
    
 | 
			
		||||
    /*
 | 
			
		||||
        (Ported from VisiLibity by Karl J. Obermeyer)
 | 
			
		||||
        The projection of point_temp onto the line determined by
 | 
			
		||||
        line_segment_temp can be represented as an affine combination
 | 
			
		||||
        expressed in the form projection of
 | 
			
		||||
        Point = theta*line_segment_temp.first + (1.0-theta)*line_segment_temp.second.
 | 
			
		||||
        If theta is outside the interval [0,1], then one of the Line_Segment's endpoints
 | 
			
		||||
        must be closest to calling Point.
 | 
			
		||||
    */
 | 
			
		||||
    double theta = ( (double)(line.b.x - this->x)*(double)(line.b.x - line.a.x) + (double)(line.b.y- this->y)*(double)(line.b.y - line.a.y) ) 
 | 
			
		||||
          / ( (double)pow(line.b.x - line.a.x, 2) + (double)pow(line.b.y - line.a.y, 2) );
 | 
			
		||||
    
 | 
			
		||||
    if (0.0 <= theta && theta <= 1.0)
 | 
			
		||||
        return theta * line.a + (1.0-theta) * line.b;
 | 
			
		||||
    
 | 
			
		||||
    // Else pick closest endpoint.
 | 
			
		||||
    if (this->distance_to(line.a) < this->distance_to(line.b)) {
 | 
			
		||||
        return line.a;
 | 
			
		||||
    } else {
 | 
			
		||||
        return line.b;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Point
 | 
			
		||||
Point::negative() const
 | 
			
		||||
{
 | 
			
		||||
    return Point(-this->x, -this->y);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Point
 | 
			
		||||
operator+(const Point& point1, const Point& point2)
 | 
			
		||||
{
 | 
			
		||||
    return Point(point1.x + point2.x, point1.y + point2.y);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Point
 | 
			
		||||
operator*(double scalar, const Point& point2)
 | 
			
		||||
{
 | 
			
		||||
    return Point(scalar * point2.x, scalar * point2.y);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef SLIC3RXS
 | 
			
		||||
 | 
			
		||||
REGISTER_CLASS(Point, "Point");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,6 +9,7 @@
 | 
			
		|||
namespace Slic3r {
 | 
			
		||||
 | 
			
		||||
class Line;
 | 
			
		||||
class MultiPoint;
 | 
			
		||||
class Point;
 | 
			
		||||
class Pointf;
 | 
			
		||||
typedef Point Vector;
 | 
			
		||||
| 
						 | 
				
			
			@ -22,7 +23,10 @@ class Point
 | 
			
		|||
    public:
 | 
			
		||||
    coord_t x;
 | 
			
		||||
    coord_t y;
 | 
			
		||||
    explicit Point(coord_t _x = 0, coord_t _y = 0): x(_x), y(_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(double x, double y);
 | 
			
		||||
    bool operator==(const Point& rhs) const;
 | 
			
		||||
    std::string wkt() const;
 | 
			
		||||
    void scale(double factor);
 | 
			
		||||
| 
						 | 
				
			
			@ -37,6 +41,9 @@ class Point
 | 
			
		|||
    double distance_to(const Line &line) const;
 | 
			
		||||
    double ccw(const Point &p1, const Point &p2) const;
 | 
			
		||||
    double ccw(const Line &line) const;
 | 
			
		||||
    Point projection_onto(const MultiPoint &poly) const;
 | 
			
		||||
    Point projection_onto(const Line &line) const;
 | 
			
		||||
    Point negative() const;
 | 
			
		||||
    
 | 
			
		||||
    #ifdef SLIC3RXS
 | 
			
		||||
    void from_SV(SV* point_sv);
 | 
			
		||||
| 
						 | 
				
			
			@ -45,6 +52,9 @@ class Point
 | 
			
		|||
    #endif
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Point operator+(const Point& point1, const Point& point2);
 | 
			
		||||
Point operator*(double scalar, const Point& point2);
 | 
			
		||||
 | 
			
		||||
class Point3 : public Point
 | 
			
		||||
{
 | 
			
		||||
    public:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -56,7 +56,7 @@ Polygon::lines(Lines* lines) const
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
Polygon::split_at(const Point &point, Polyline* polyline) const
 | 
			
		||||
Polygon::split_at_vertex(const Point &point, Polyline* polyline) const
 | 
			
		||||
{
 | 
			
		||||
    // find index of point
 | 
			
		||||
    for (Points::const_iterator it = this->points.begin(); it != this->points.end(); ++it) {
 | 
			
		||||
| 
						 | 
				
			
			@ -191,6 +191,24 @@ Polygon::triangulate_convex(Polygons* polygons) const
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// center of mass
 | 
			
		||||
Point
 | 
			
		||||
Polygon::centroid() const
 | 
			
		||||
{
 | 
			
		||||
    double area_temp = this->area();
 | 
			
		||||
    double x_temp = 0;
 | 
			
		||||
    double y_temp = 0;
 | 
			
		||||
    
 | 
			
		||||
    Polyline polyline;
 | 
			
		||||
    this->split_at_first_point(&polyline);
 | 
			
		||||
    for (Points::const_iterator point = polyline.points.begin(); point != polyline.points.end() - 1; ++point) {
 | 
			
		||||
        x_temp += (double)( point->x + (point+1)->x ) * ( (double)point->x*(point+1)->y - (double)(point+1)->x*point->y );
 | 
			
		||||
        y_temp += (double)( point->y + (point+1)->y ) * ( (double)point->x*(point+1)->y - (double)(point+1)->x*point->y );
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    return Point(x_temp/(6*area_temp), y_temp/(6*area_temp));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef SLIC3RXS
 | 
			
		||||
REGISTER_CLASS(Polygon, "Polygon");
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,7 +21,7 @@ class Polygon : public MultiPoint {
 | 
			
		|||
    Point last_point() const;
 | 
			
		||||
    Lines lines() const;
 | 
			
		||||
    void lines(Lines* lines) const;
 | 
			
		||||
    void split_at(const Point &point, Polyline* polyline) const;
 | 
			
		||||
    void split_at_vertex(const Point &point, Polyline* polyline) const;
 | 
			
		||||
    void split_at_index(int index, Polyline* polyline) const;
 | 
			
		||||
    void split_at_first_point(Polyline* polyline) const;
 | 
			
		||||
    void equally_spaced_points(double distance, Points* points) const;
 | 
			
		||||
| 
						 | 
				
			
			@ -35,6 +35,7 @@ class Polygon : public MultiPoint {
 | 
			
		|||
    Polygons simplify(double tolerance) const;
 | 
			
		||||
    void simplify(double tolerance, Polygons &polygons) const;
 | 
			
		||||
    void triangulate_convex(Polygons* polygons) const;
 | 
			
		||||
    Point centroid() const;
 | 
			
		||||
    
 | 
			
		||||
    #ifdef SLIC3RXS
 | 
			
		||||
    void from_SV_check(SV* poly_sv);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -119,6 +119,42 @@ Polyline::simplify(double tolerance)
 | 
			
		|||
    this->points = MultiPoint::_douglas_peucker(this->points, tolerance);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
Polyline::split_at(const Point &point, Polyline* p1, Polyline* p2) const
 | 
			
		||||
{
 | 
			
		||||
    if (this->points.empty()) return;
 | 
			
		||||
    
 | 
			
		||||
    // find the line to split at
 | 
			
		||||
    size_t line_idx = 0;
 | 
			
		||||
    Point p = this->first_point();
 | 
			
		||||
    double min = point.distance_to(p);
 | 
			
		||||
    Lines lines = this->lines();
 | 
			
		||||
    for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) {
 | 
			
		||||
        Point p_tmp = point.projection_onto(*line);
 | 
			
		||||
        if (point.distance_to(p_tmp) < min) {
 | 
			
		||||
	        p = p_tmp;
 | 
			
		||||
	        min = point.distance_to(p);
 | 
			
		||||
	        line_idx = line - lines.begin();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // create first half
 | 
			
		||||
    p1->points.clear();
 | 
			
		||||
    for (Lines::const_iterator line = lines.begin(); line != lines.begin() + line_idx + 1; ++line) {
 | 
			
		||||
        if (!line->a.coincides_with(p)) p1->points.push_back(line->a);
 | 
			
		||||
    }
 | 
			
		||||
    // we add point instead of p because they might differ because of numerical issues
 | 
			
		||||
    // and caller might want to rely on point belonging to result polylines
 | 
			
		||||
    p1->points.push_back(point);
 | 
			
		||||
    
 | 
			
		||||
    // create second half
 | 
			
		||||
    p2->points.clear();
 | 
			
		||||
    p2->points.push_back(point);
 | 
			
		||||
    for (Lines::const_iterator line = lines.begin() + line_idx; line != lines.end(); ++line) {
 | 
			
		||||
        if (!line->b.coincides_with(p)) p2->points.push_back(line->b);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#ifdef SLIC3RXS
 | 
			
		||||
REGISTER_CLASS(Polyline, "Polyline");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,6 +21,7 @@ class Polyline : public MultiPoint {
 | 
			
		|||
    void extend_start(double distance);
 | 
			
		||||
    void equally_spaced_points(double distance, Points* points) const;
 | 
			
		||||
    void simplify(double tolerance);
 | 
			
		||||
    void split_at(const Point &point, Polyline* p1, Polyline* p2) const;
 | 
			
		||||
    
 | 
			
		||||
    #ifdef SLIC3RXS
 | 
			
		||||
    void from_SV_check(SV* poly_sv);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,6 +18,10 @@ enum SupportMaterialPattern {
 | 
			
		|||
    smpRectilinear, smpRectilinearGrid, smpHoneycomb, smpPillars,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum SeamPosition {
 | 
			
		||||
    spRandom, spNearest, spAligned
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template<> inline t_config_enum_values ConfigOptionEnum<GCodeFlavor>::get_enum_values() {
 | 
			
		||||
    t_config_enum_values keys_map;
 | 
			
		||||
    keys_map["reprap"]          = gcfRepRap;
 | 
			
		||||
| 
						 | 
				
			
			@ -50,6 +54,14 @@ template<> inline t_config_enum_values ConfigOptionEnum<SupportMaterialPattern>:
 | 
			
		|||
    return keys_map;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template<> inline t_config_enum_values ConfigOptionEnum<SeamPosition>::get_enum_values() {
 | 
			
		||||
    t_config_enum_values keys_map;
 | 
			
		||||
    keys_map["random"]              = spRandom;
 | 
			
		||||
    keys_map["nearest"]             = spNearest;
 | 
			
		||||
    keys_map["aligned"]             = spAligned;
 | 
			
		||||
    return keys_map;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class PrintConfigDef
 | 
			
		||||
{
 | 
			
		||||
    public:
 | 
			
		||||
| 
						 | 
				
			
			@ -105,6 +117,7 @@ class PrintConfigDef
 | 
			
		|||
 | 
			
		||||
        Options["bridge_speed"].type = coFloat;
 | 
			
		||||
        Options["bridge_speed"].label = "Bridges";
 | 
			
		||||
        Options["bridge_speed"].category = "Speed";
 | 
			
		||||
        Options["bridge_speed"].tooltip = "Speed for printing bridges.";
 | 
			
		||||
        Options["bridge_speed"].sidetext = "mm/s";
 | 
			
		||||
        Options["bridge_speed"].cli = "bridge-speed=f";
 | 
			
		||||
| 
						 | 
				
			
			@ -161,6 +174,7 @@ class PrintConfigDef
 | 
			
		|||
 | 
			
		||||
        Options["external_perimeter_speed"].type = coFloatOrPercent;
 | 
			
		||||
        Options["external_perimeter_speed"].label = "External perimeters";
 | 
			
		||||
        Options["external_perimeter_speed"].category = "Speed";
 | 
			
		||||
        Options["external_perimeter_speed"].tooltip = "This separate setting will affect the speed of external perimeters (the visible ones). If expressed as percentage (for example: 80%) it will be calculated on the perimeters speed setting above.";
 | 
			
		||||
        Options["external_perimeter_speed"].sidetext = "mm/s or %";
 | 
			
		||||
        Options["external_perimeter_speed"].cli = "external-perimeter-speed=s";
 | 
			
		||||
| 
						 | 
				
			
			@ -357,6 +371,7 @@ class PrintConfigDef
 | 
			
		|||
 | 
			
		||||
        Options["gap_fill_speed"].type = coFloat;
 | 
			
		||||
        Options["gap_fill_speed"].label = "Gap fill";
 | 
			
		||||
        Options["gap_fill_speed"].category = "Speed";
 | 
			
		||||
        Options["gap_fill_speed"].tooltip = "Speed for filling small gaps using short zigzag moves. Keep this reasonably low to avoid too much shaking and resonance issues. Set zero to disable gaps filling.";
 | 
			
		||||
        Options["gap_fill_speed"].sidetext = "mm/s";
 | 
			
		||||
        Options["gap_fill_speed"].cli = "gap-fill-speed=f";
 | 
			
		||||
| 
						 | 
				
			
			@ -431,6 +446,7 @@ class PrintConfigDef
 | 
			
		|||
 | 
			
		||||
        Options["infill_speed"].type = coFloat;
 | 
			
		||||
        Options["infill_speed"].label = "Infill";
 | 
			
		||||
        Options["infill_speed"].category = "Speed";
 | 
			
		||||
        Options["infill_speed"].tooltip = "Speed for printing the internal fill.";
 | 
			
		||||
        Options["infill_speed"].sidetext = "mm/s";
 | 
			
		||||
        Options["infill_speed"].cli = "infill-speed=f";
 | 
			
		||||
| 
						 | 
				
			
			@ -546,6 +562,7 @@ class PrintConfigDef
 | 
			
		|||
 | 
			
		||||
        Options["perimeter_speed"].type = coFloat;
 | 
			
		||||
        Options["perimeter_speed"].label = "Perimeters";
 | 
			
		||||
        Options["perimeter_speed"].category = "Speed";
 | 
			
		||||
        Options["perimeter_speed"].tooltip = "Speed for perimeters (contours, aka vertical shells).";
 | 
			
		||||
        Options["perimeter_speed"].sidetext = "mm/s";
 | 
			
		||||
        Options["perimeter_speed"].cli = "perimeter-speed=f";
 | 
			
		||||
| 
						 | 
				
			
			@ -579,11 +596,6 @@ class PrintConfigDef
 | 
			
		|||
        Options["raft_layers"].sidetext = "layers";
 | 
			
		||||
        Options["raft_layers"].cli = "raft-layers=i";
 | 
			
		||||
 | 
			
		||||
        Options["randomize_start"].type = coBool;
 | 
			
		||||
        Options["randomize_start"].label = "Randomize starting points";
 | 
			
		||||
        Options["randomize_start"].tooltip = "Start each layer from a different vertex to prevent plastic build-up on the same corner.";
 | 
			
		||||
        Options["randomize_start"].cli = "randomize-start!";
 | 
			
		||||
 | 
			
		||||
        Options["resolution"].type = coFloat;
 | 
			
		||||
        Options["resolution"].label = "Resolution";
 | 
			
		||||
        Options["resolution"].tooltip = "Minimum detail resolution, used to simplify the input file for speeding up the slicing job and reducing memory usage. High-resolution models often carry more detail than printers can render. Set to zero to disable any simplification and use full resolution from input.";
 | 
			
		||||
| 
						 | 
				
			
			@ -639,6 +651,19 @@ class PrintConfigDef
 | 
			
		|||
        Options["retract_speed"].cli = "retract-speed=f@";
 | 
			
		||||
        Options["retract_speed"].max = 1000;
 | 
			
		||||
 | 
			
		||||
        Options["seam_position"].type = coEnum;
 | 
			
		||||
        Options["seam_position"].label = "Seam position";
 | 
			
		||||
        Options["seam_position"].category = "Layers and perimeters";
 | 
			
		||||
        Options["seam_position"].tooltip = "Position of perimeters starting points.";
 | 
			
		||||
        Options["seam_position"].cli = "seam-position=s";
 | 
			
		||||
        Options["seam_position"].enum_keys_map = ConfigOptionEnum<SeamPosition>::get_enum_values();
 | 
			
		||||
        Options["seam_position"].enum_values.push_back("random");
 | 
			
		||||
        Options["seam_position"].enum_values.push_back("nearest");
 | 
			
		||||
        Options["seam_position"].enum_values.push_back("aligned");
 | 
			
		||||
        Options["seam_position"].enum_labels.push_back("Random");
 | 
			
		||||
        Options["seam_position"].enum_labels.push_back("Nearest");
 | 
			
		||||
        Options["seam_position"].enum_labels.push_back("Aligned");
 | 
			
		||||
 | 
			
		||||
        Options["skirt_distance"].type = coFloat;
 | 
			
		||||
        Options["skirt_distance"].label = "Distance from object";
 | 
			
		||||
        Options["skirt_distance"].tooltip = "Distance between skirt and object(s). Set this to zero to attach the skirt to the object(s) and get a brim for better adhesion.";
 | 
			
		||||
| 
						 | 
				
			
			@ -666,6 +691,7 @@ class PrintConfigDef
 | 
			
		|||
 | 
			
		||||
        Options["small_perimeter_speed"].type = coFloatOrPercent;
 | 
			
		||||
        Options["small_perimeter_speed"].label = "Small perimeters";
 | 
			
		||||
        Options["small_perimeter_speed"].category = "Speed";
 | 
			
		||||
        Options["small_perimeter_speed"].tooltip = "This separate setting will affect the speed of perimeters having radius <= 6.5mm (usually holes). If expressed as percentage (for example: 80%) it will be calculated on the perimeters speed setting above.";
 | 
			
		||||
        Options["small_perimeter_speed"].sidetext = "mm/s or %";
 | 
			
		||||
        Options["small_perimeter_speed"].cli = "small-perimeter-speed=s";
 | 
			
		||||
| 
						 | 
				
			
			@ -712,6 +738,7 @@ class PrintConfigDef
 | 
			
		|||
 | 
			
		||||
        Options["solid_infill_speed"].type = coFloatOrPercent;
 | 
			
		||||
        Options["solid_infill_speed"].label = "Solid infill";
 | 
			
		||||
        Options["solid_infill_speed"].category = "Speed";
 | 
			
		||||
        Options["solid_infill_speed"].tooltip = "Speed for printing solid regions (top/bottom/internal horizontal shells). This can be expressed as a percentage (for example: 80%) over the default infill speed above.";
 | 
			
		||||
        Options["solid_infill_speed"].sidetext = "mm/s or %";
 | 
			
		||||
        Options["solid_infill_speed"].cli = "solid-infill-speed=s";
 | 
			
		||||
| 
						 | 
				
			
			@ -746,16 +773,6 @@ class PrintConfigDef
 | 
			
		|||
        Options["start_gcode"].full_width = true;
 | 
			
		||||
        Options["start_gcode"].height = 120;
 | 
			
		||||
 | 
			
		||||
        Options["start_perimeters_at_concave_points"].type = coBool;
 | 
			
		||||
        Options["start_perimeters_at_concave_points"].label = "Concave points";
 | 
			
		||||
        Options["start_perimeters_at_concave_points"].tooltip = "Prefer to start perimeters at a concave point.";
 | 
			
		||||
        Options["start_perimeters_at_concave_points"].cli = "start-perimeters-at-concave-points!";
 | 
			
		||||
 | 
			
		||||
        Options["start_perimeters_at_non_overhang"].type = coBool;
 | 
			
		||||
        Options["start_perimeters_at_non_overhang"].label = "Non-overhang points";
 | 
			
		||||
        Options["start_perimeters_at_non_overhang"].tooltip = "Prefer to start perimeters at non-overhanging points.";
 | 
			
		||||
        Options["start_perimeters_at_non_overhang"].cli = "start-perimeters-at-non-overhang!";
 | 
			
		||||
 | 
			
		||||
        Options["support_material"].type = coBool;
 | 
			
		||||
        Options["support_material"].label = "Generate support material";
 | 
			
		||||
        Options["support_material"].category = "Support material";
 | 
			
		||||
| 
						 | 
				
			
			@ -812,6 +829,14 @@ class PrintConfigDef
 | 
			
		|||
        Options["support_material_interface_spacing"].sidetext = "mm";
 | 
			
		||||
        Options["support_material_interface_spacing"].cli = "support-material-interface-spacing=f";
 | 
			
		||||
 | 
			
		||||
        Options["support_material_interface_speed"].type = coFloatOrPercent;
 | 
			
		||||
        Options["support_material_interface_speed"].label = "Support material interface";
 | 
			
		||||
        Options["support_material_interface_speed"].category = "Support material";
 | 
			
		||||
        Options["support_material_interface_speed"].tooltip = "Speed for printing support material interface layers. If expressed as percentage (for example 50%) it will be calculated over support material speed.";
 | 
			
		||||
        Options["support_material_interface_speed"].sidetext = "mm/s or %";
 | 
			
		||||
        Options["support_material_interface_speed"].cli = "support-material-interface-speed=s";
 | 
			
		||||
        Options["support_material_interface_speed"].ratio_over = "support_material_speed";
 | 
			
		||||
 | 
			
		||||
        Options["support_material_pattern"].type = coEnum;
 | 
			
		||||
        Options["support_material_pattern"].label = "Pattern";
 | 
			
		||||
        Options["support_material_pattern"].category = "Support material";
 | 
			
		||||
| 
						 | 
				
			
			@ -889,6 +914,7 @@ class PrintConfigDef
 | 
			
		|||
 | 
			
		||||
        Options["top_solid_infill_speed"].type = coFloatOrPercent;
 | 
			
		||||
        Options["top_solid_infill_speed"].label = "Top solid infill";
 | 
			
		||||
        Options["top_solid_infill_speed"].category = "Speed";
 | 
			
		||||
        Options["top_solid_infill_speed"].tooltip = "Speed for printing top solid layers (it only applies to the uppermost external layers and not to their internal solid layers). You may want to slow down this to get a nicer surface finish. This can be expressed as a percentage (for example: 80%) over the solid infill speed above.";
 | 
			
		||||
        Options["top_solid_infill_speed"].sidetext = "mm/s or %";
 | 
			
		||||
        Options["top_solid_infill_speed"].cli = "top-solid-infill-speed=s";
 | 
			
		||||
| 
						 | 
				
			
			@ -959,6 +985,13 @@ class DynamicPrintConfig : public DynamicConfig
 | 
			
		|||
            if (!this->has("support_material_interface_extruder"))
 | 
			
		||||
                this->option("support_material_interface_extruder", true)->setInt(extruder);
 | 
			
		||||
        }
 | 
			
		||||
        if (this->has("spiral_vase") && this->opt<ConfigOptionBool>("spiral_vase", true)->value) {
 | 
			
		||||
            {
 | 
			
		||||
                // this should be actually done only on the spiral layers instead of all
 | 
			
		||||
                ConfigOptionBools* opt = this->opt<ConfigOptionBools>("retract_layer_change", true);
 | 
			
		||||
                opt->values.assign(opt->values.size(), false);  // set all values to false
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -980,6 +1013,7 @@ class PrintObjectConfig : public virtual StaticPrintConfig
 | 
			
		|||
    ConfigOptionBool                interface_shells;
 | 
			
		||||
    ConfigOptionFloat               layer_height;
 | 
			
		||||
    ConfigOptionInt                 raft_layers;
 | 
			
		||||
    ConfigOptionEnum<SeamPosition>  seam_position;
 | 
			
		||||
    ConfigOptionBool                support_material;
 | 
			
		||||
    ConfigOptionInt                 support_material_angle;
 | 
			
		||||
    ConfigOptionInt                 support_material_enforce_layers;
 | 
			
		||||
| 
						 | 
				
			
			@ -988,6 +1022,7 @@ class PrintObjectConfig : public virtual StaticPrintConfig
 | 
			
		|||
    ConfigOptionInt                 support_material_interface_extruder;
 | 
			
		||||
    ConfigOptionInt                 support_material_interface_layers;
 | 
			
		||||
    ConfigOptionFloat               support_material_interface_spacing;
 | 
			
		||||
    ConfigOptionFloatOrPercent      support_material_interface_speed;
 | 
			
		||||
    ConfigOptionEnum<SupportMaterialPattern> support_material_pattern;
 | 
			
		||||
    ConfigOptionFloat               support_material_spacing;
 | 
			
		||||
    ConfigOptionFloat               support_material_speed;
 | 
			
		||||
| 
						 | 
				
			
			@ -1003,6 +1038,7 @@ class PrintObjectConfig : public virtual StaticPrintConfig
 | 
			
		|||
        this->interface_shells.value                             = false;
 | 
			
		||||
        this->layer_height.value                                 = 0.4;
 | 
			
		||||
        this->raft_layers.value                                  = 0;
 | 
			
		||||
        this->seam_position.value                                = spAligned;
 | 
			
		||||
        this->support_material.value                             = false;
 | 
			
		||||
        this->support_material_angle.value                       = 0;
 | 
			
		||||
        this->support_material_enforce_layers.value              = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -1012,6 +1048,8 @@ class PrintObjectConfig : public virtual StaticPrintConfig
 | 
			
		|||
        this->support_material_interface_extruder.value          = 1;
 | 
			
		||||
        this->support_material_interface_layers.value            = 3;
 | 
			
		||||
        this->support_material_interface_spacing.value           = 0;
 | 
			
		||||
        this->support_material_interface_speed.value             = 100;
 | 
			
		||||
        this->support_material_interface_speed.percent           = true;
 | 
			
		||||
        this->support_material_pattern.value                     = smpPillars;
 | 
			
		||||
        this->support_material_spacing.value                     = 2.5;
 | 
			
		||||
        this->support_material_speed.value                       = 60;
 | 
			
		||||
| 
						 | 
				
			
			@ -1026,6 +1064,7 @@ class PrintObjectConfig : public virtual StaticPrintConfig
 | 
			
		|||
        if (opt_key == "interface_shells")                           return &this->interface_shells;
 | 
			
		||||
        if (opt_key == "layer_height")                               return &this->layer_height;
 | 
			
		||||
        if (opt_key == "raft_layers")                                return &this->raft_layers;
 | 
			
		||||
        if (opt_key == "seam_position")                              return &this->seam_position;
 | 
			
		||||
        if (opt_key == "support_material")                           return &this->support_material;
 | 
			
		||||
        if (opt_key == "support_material_angle")                     return &this->support_material_angle;
 | 
			
		||||
        if (opt_key == "support_material_enforce_layers")            return &this->support_material_enforce_layers;
 | 
			
		||||
| 
						 | 
				
			
			@ -1034,6 +1073,7 @@ class PrintObjectConfig : public virtual StaticPrintConfig
 | 
			
		|||
        if (opt_key == "support_material_interface_extruder")        return &this->support_material_interface_extruder;
 | 
			
		||||
        if (opt_key == "support_material_interface_layers")          return &this->support_material_interface_layers;
 | 
			
		||||
        if (opt_key == "support_material_interface_spacing")         return &this->support_material_interface_spacing;
 | 
			
		||||
        if (opt_key == "support_material_interface_speed")           return &this->support_material_interface_speed;
 | 
			
		||||
        if (opt_key == "support_material_pattern")                   return &this->support_material_pattern;
 | 
			
		||||
        if (opt_key == "support_material_spacing")                   return &this->support_material_spacing;
 | 
			
		||||
        if (opt_key == "support_material_speed")                     return &this->support_material_speed;
 | 
			
		||||
| 
						 | 
				
			
			@ -1047,67 +1087,98 @@ class PrintRegionConfig : public virtual StaticPrintConfig
 | 
			
		|||
{
 | 
			
		||||
    public:
 | 
			
		||||
    ConfigOptionInt                 bottom_solid_layers;
 | 
			
		||||
    ConfigOptionFloat               bridge_speed;
 | 
			
		||||
    ConfigOptionFloatOrPercent      external_perimeter_speed;
 | 
			
		||||
    ConfigOptionBool                extra_perimeters;
 | 
			
		||||
    ConfigOptionInt                 fill_angle;
 | 
			
		||||
    ConfigOptionPercent             fill_density;
 | 
			
		||||
    ConfigOptionEnum<InfillPattern> fill_pattern;
 | 
			
		||||
    ConfigOptionFloat               gap_fill_speed;
 | 
			
		||||
    ConfigOptionInt                 infill_extruder;
 | 
			
		||||
    ConfigOptionFloatOrPercent      infill_extrusion_width;
 | 
			
		||||
    ConfigOptionInt                 infill_every_layers;
 | 
			
		||||
    ConfigOptionFloat               infill_speed;
 | 
			
		||||
    ConfigOptionBool                overhangs;
 | 
			
		||||
    ConfigOptionInt                 perimeter_extruder;
 | 
			
		||||
    ConfigOptionFloatOrPercent      perimeter_extrusion_width;
 | 
			
		||||
    ConfigOptionFloat               perimeter_speed;
 | 
			
		||||
    ConfigOptionInt                 perimeters;
 | 
			
		||||
    ConfigOptionFloatOrPercent      small_perimeter_speed;
 | 
			
		||||
    ConfigOptionEnum<InfillPattern> solid_fill_pattern;
 | 
			
		||||
    ConfigOptionFloat               solid_infill_below_area;
 | 
			
		||||
    ConfigOptionFloatOrPercent      solid_infill_extrusion_width;
 | 
			
		||||
    ConfigOptionInt                 solid_infill_every_layers;
 | 
			
		||||
    ConfigOptionFloatOrPercent      solid_infill_speed;
 | 
			
		||||
    ConfigOptionBool                thin_walls;
 | 
			
		||||
    ConfigOptionFloatOrPercent      top_infill_extrusion_width;
 | 
			
		||||
    ConfigOptionInt                 top_solid_layers;
 | 
			
		||||
    ConfigOptionFloatOrPercent      top_solid_infill_speed;
 | 
			
		||||
    
 | 
			
		||||
    PrintRegionConfig() : StaticPrintConfig() {
 | 
			
		||||
        this->bottom_solid_layers.value                          = 3;
 | 
			
		||||
        this->bridge_speed.value                                 = 60;
 | 
			
		||||
        this->external_perimeter_speed.value                     = 70;
 | 
			
		||||
        this->external_perimeter_speed.percent                   = true;
 | 
			
		||||
        this->extra_perimeters.value                             = true;
 | 
			
		||||
        this->fill_angle.value                                   = 45;
 | 
			
		||||
        this->fill_density.value                                 = 40;
 | 
			
		||||
        this->fill_pattern.value                                 = ipHoneycomb;
 | 
			
		||||
        this->gap_fill_speed.value                               = 20;
 | 
			
		||||
        this->infill_extruder.value                              = 1;
 | 
			
		||||
        this->infill_extrusion_width.value                       = 0;
 | 
			
		||||
        this->infill_extrusion_width.percent                     = false;
 | 
			
		||||
        this->infill_every_layers.value                          = 1;
 | 
			
		||||
        this->infill_speed.value                                 = 60;
 | 
			
		||||
        this->overhangs.value                                    = true;
 | 
			
		||||
        this->perimeter_extruder.value                           = 1;
 | 
			
		||||
        this->perimeter_extrusion_width.value                    = 0;
 | 
			
		||||
        this->perimeter_extrusion_width.percent                  = false;
 | 
			
		||||
        this->perimeter_speed.value                              = 30;
 | 
			
		||||
        this->perimeters.value                                   = 3;
 | 
			
		||||
        this->small_perimeter_speed.value                        = 30;
 | 
			
		||||
        this->small_perimeter_speed.percent                      = false;
 | 
			
		||||
        this->solid_fill_pattern.value                           = ipRectilinear;
 | 
			
		||||
        this->solid_infill_below_area.value                      = 70;
 | 
			
		||||
        this->solid_infill_extrusion_width.value                 = 0;
 | 
			
		||||
        this->solid_infill_extrusion_width.percent               = false;
 | 
			
		||||
        this->solid_infill_every_layers.value                    = 0;
 | 
			
		||||
        this->solid_infill_speed.value                           = 60;
 | 
			
		||||
        this->solid_infill_speed.percent                         = false;
 | 
			
		||||
        this->thin_walls.value                                   = true;
 | 
			
		||||
        this->top_infill_extrusion_width.value                   = 0;
 | 
			
		||||
        this->top_infill_extrusion_width.percent                 = false;
 | 
			
		||||
        this->top_solid_infill_speed.value                       = 50;
 | 
			
		||||
        this->top_solid_infill_speed.percent                     = false;
 | 
			
		||||
        this->top_solid_layers.value                             = 3;
 | 
			
		||||
    };
 | 
			
		||||
    
 | 
			
		||||
    ConfigOption* option(const t_config_option_key opt_key, bool create = false) {
 | 
			
		||||
        if (opt_key == "bottom_solid_layers")                        return &this->bottom_solid_layers;
 | 
			
		||||
        if (opt_key == "bridge_speed")                               return &this->bridge_speed;
 | 
			
		||||
        if (opt_key == "external_perimeter_speed")                   return &this->external_perimeter_speed;
 | 
			
		||||
        if (opt_key == "extra_perimeters")                           return &this->extra_perimeters;
 | 
			
		||||
        if (opt_key == "fill_angle")                                 return &this->fill_angle;
 | 
			
		||||
        if (opt_key == "fill_density")                               return &this->fill_density;
 | 
			
		||||
        if (opt_key == "fill_pattern")                               return &this->fill_pattern;
 | 
			
		||||
        if (opt_key == "gap_fill_speed")                             return &this->gap_fill_speed;
 | 
			
		||||
        if (opt_key == "infill_extruder")                            return &this->infill_extruder;
 | 
			
		||||
        if (opt_key == "infill_extrusion_width")                     return &this->infill_extrusion_width;
 | 
			
		||||
        if (opt_key == "infill_every_layers")                        return &this->infill_every_layers;
 | 
			
		||||
        if (opt_key == "infill_speed")                               return &this->infill_speed;
 | 
			
		||||
        if (opt_key == "overhangs")                                  return &this->overhangs;
 | 
			
		||||
        if (opt_key == "perimeter_extruder")                         return &this->perimeter_extruder;
 | 
			
		||||
        if (opt_key == "perimeter_extrusion_width")                  return &this->perimeter_extrusion_width;
 | 
			
		||||
        if (opt_key == "perimeter_speed")                            return &this->perimeter_speed;
 | 
			
		||||
        if (opt_key == "perimeters")                                 return &this->perimeters;
 | 
			
		||||
        if (opt_key == "small_perimeter_speed")                      return &this->small_perimeter_speed;
 | 
			
		||||
        if (opt_key == "solid_fill_pattern")                         return &this->solid_fill_pattern;
 | 
			
		||||
        if (opt_key == "solid_infill_below_area")                    return &this->solid_infill_below_area;
 | 
			
		||||
        if (opt_key == "solid_infill_extrusion_width")               return &this->solid_infill_extrusion_width;
 | 
			
		||||
        if (opt_key == "solid_infill_every_layers")                  return &this->solid_infill_every_layers;
 | 
			
		||||
        if (opt_key == "solid_infill_speed")                         return &this->solid_infill_speed;
 | 
			
		||||
        if (opt_key == "thin_walls")                                 return &this->thin_walls;
 | 
			
		||||
        if (opt_key == "top_infill_extrusion_width")                 return &this->top_infill_extrusion_width;
 | 
			
		||||
        if (opt_key == "top_solid_infill_speed")                     return &this->top_solid_infill_speed;
 | 
			
		||||
        if (opt_key == "top_solid_layers")                           return &this->top_solid_layers;
 | 
			
		||||
        
 | 
			
		||||
        return NULL;
 | 
			
		||||
| 
						 | 
				
			
			@ -1123,7 +1194,6 @@ class PrintConfig : public virtual StaticPrintConfig
 | 
			
		|||
    ConfigOptionFloat               bridge_acceleration;
 | 
			
		||||
    ConfigOptionInt                 bridge_fan_speed;
 | 
			
		||||
    ConfigOptionFloat               bridge_flow_ratio;
 | 
			
		||||
    ConfigOptionFloat               bridge_speed;
 | 
			
		||||
    ConfigOptionFloat               brim_width;
 | 
			
		||||
    ConfigOptionBool                complete_objects;
 | 
			
		||||
    ConfigOptionBool                cooling;
 | 
			
		||||
| 
						 | 
				
			
			@ -1131,7 +1201,6 @@ class PrintConfig : public virtual StaticPrintConfig
 | 
			
		|||
    ConfigOptionInt                 disable_fan_first_layers;
 | 
			
		||||
    ConfigOptionFloat               duplicate_distance;
 | 
			
		||||
    ConfigOptionString              end_gcode;
 | 
			
		||||
    ConfigOptionFloatOrPercent      external_perimeter_speed;
 | 
			
		||||
    ConfigOptionBool                external_perimeters_first;
 | 
			
		||||
    ConfigOptionFloat               extruder_clearance_height;
 | 
			
		||||
    ConfigOptionFloat               extruder_clearance_radius;
 | 
			
		||||
| 
						 | 
				
			
			@ -1147,13 +1216,11 @@ class PrintConfig : public virtual StaticPrintConfig
 | 
			
		|||
    ConfigOptionFloatOrPercent      first_layer_speed;
 | 
			
		||||
    ConfigOptionInts                first_layer_temperature;
 | 
			
		||||
    ConfigOptionBool                g0;
 | 
			
		||||
    ConfigOptionFloat               gap_fill_speed;
 | 
			
		||||
    ConfigOptionBool                gcode_arcs;
 | 
			
		||||
    ConfigOptionBool                gcode_comments;
 | 
			
		||||
    ConfigOptionEnum<GCodeFlavor>   gcode_flavor;
 | 
			
		||||
    ConfigOptionFloat               infill_acceleration;
 | 
			
		||||
    ConfigOptionBool                infill_first;
 | 
			
		||||
    ConfigOptionFloat               infill_speed;
 | 
			
		||||
    ConfigOptionString              layer_gcode;
 | 
			
		||||
    ConfigOptionInt                 max_fan_speed;
 | 
			
		||||
    ConfigOptionInt                 min_fan_speed;
 | 
			
		||||
| 
						 | 
				
			
			@ -1164,12 +1231,9 @@ class PrintConfig : public virtual StaticPrintConfig
 | 
			
		|||
    ConfigOptionBool                only_retract_when_crossing_perimeters;
 | 
			
		||||
    ConfigOptionBool                ooze_prevention;
 | 
			
		||||
    ConfigOptionString              output_filename_format;
 | 
			
		||||
    ConfigOptionBool                overhangs;
 | 
			
		||||
    ConfigOptionFloat               perimeter_acceleration;
 | 
			
		||||
    ConfigOptionFloat               perimeter_speed;
 | 
			
		||||
    ConfigOptionStrings             post_process;
 | 
			
		||||
    ConfigOptionPoint               print_center;
 | 
			
		||||
    ConfigOptionBool                randomize_start;
 | 
			
		||||
    ConfigOptionFloat               resolution;
 | 
			
		||||
    ConfigOptionFloats              retract_before_travel;
 | 
			
		||||
    ConfigOptionBools               retract_layer_change;
 | 
			
		||||
| 
						 | 
				
			
			@ -1183,17 +1247,12 @@ class PrintConfig : public virtual StaticPrintConfig
 | 
			
		|||
    ConfigOptionInt                 skirt_height;
 | 
			
		||||
    ConfigOptionInt                 skirts;
 | 
			
		||||
    ConfigOptionInt                 slowdown_below_layer_time;
 | 
			
		||||
    ConfigOptionFloatOrPercent      small_perimeter_speed;
 | 
			
		||||
    ConfigOptionFloatOrPercent      solid_infill_speed;
 | 
			
		||||
    ConfigOptionBool                spiral_vase;
 | 
			
		||||
    ConfigOptionInt                 standby_temperature_delta;
 | 
			
		||||
    ConfigOptionString              start_gcode;
 | 
			
		||||
    ConfigOptionBool                start_perimeters_at_concave_points;
 | 
			
		||||
    ConfigOptionBool                start_perimeters_at_non_overhang;
 | 
			
		||||
    ConfigOptionInts                temperature;
 | 
			
		||||
    ConfigOptionInt                 threads;
 | 
			
		||||
    ConfigOptionString              toolchange_gcode;
 | 
			
		||||
    ConfigOptionFloatOrPercent      top_solid_infill_speed;
 | 
			
		||||
    ConfigOptionFloat               travel_speed;
 | 
			
		||||
    ConfigOptionBool                use_firmware_retraction;
 | 
			
		||||
    ConfigOptionBool                use_relative_e_distances;
 | 
			
		||||
| 
						 | 
				
			
			@ -1208,7 +1267,6 @@ class PrintConfig : public virtual StaticPrintConfig
 | 
			
		|||
        this->bridge_acceleration.value                          = 0;
 | 
			
		||||
        this->bridge_fan_speed.value                             = 100;
 | 
			
		||||
        this->bridge_flow_ratio.value                            = 1;
 | 
			
		||||
        this->bridge_speed.value                                 = 60;
 | 
			
		||||
        this->brim_width.value                                   = 0;
 | 
			
		||||
        this->complete_objects.value                             = false;
 | 
			
		||||
        this->cooling.value                                      = true;
 | 
			
		||||
| 
						 | 
				
			
			@ -1216,8 +1274,6 @@ class PrintConfig : public virtual StaticPrintConfig
 | 
			
		|||
        this->disable_fan_first_layers.value                     = 1;
 | 
			
		||||
        this->duplicate_distance.value                           = 6;
 | 
			
		||||
        this->end_gcode.value                                    = "M104 S0 ; turn off temperature\nG28 X0  ; home X axis\nM84     ; disable motors\n";
 | 
			
		||||
        this->external_perimeter_speed.value                     = 70;
 | 
			
		||||
        this->external_perimeter_speed.percent                   = true;
 | 
			
		||||
        this->external_perimeters_first.value                    = false;
 | 
			
		||||
        this->extruder_clearance_height.value                    = 20;
 | 
			
		||||
        this->extruder_clearance_radius.value                    = 20;
 | 
			
		||||
| 
						 | 
				
			
			@ -1239,13 +1295,11 @@ class PrintConfig : public virtual StaticPrintConfig
 | 
			
		|||
        this->first_layer_temperature.values.resize(1);
 | 
			
		||||
        this->first_layer_temperature.values[0]                  = 200;
 | 
			
		||||
        this->g0.value                                           = false;
 | 
			
		||||
        this->gap_fill_speed.value                               = 20;
 | 
			
		||||
        this->gcode_arcs.value                                   = false;
 | 
			
		||||
        this->gcode_comments.value                               = false;
 | 
			
		||||
        this->gcode_flavor.value                                 = gcfRepRap;
 | 
			
		||||
        this->infill_acceleration.value                          = 0;
 | 
			
		||||
        this->infill_first.value                                 = false;
 | 
			
		||||
        this->infill_speed.value                                 = 60;
 | 
			
		||||
        this->layer_gcode.value                                  = "";
 | 
			
		||||
        this->max_fan_speed.value                                = 100;
 | 
			
		||||
        this->min_fan_speed.value                                = 35;
 | 
			
		||||
| 
						 | 
				
			
			@ -1257,11 +1311,8 @@ class PrintConfig : public virtual StaticPrintConfig
 | 
			
		|||
        this->only_retract_when_crossing_perimeters.value        = true;
 | 
			
		||||
        this->ooze_prevention.value                              = false;
 | 
			
		||||
        this->output_filename_format.value                       = "[input_filename_base].gcode";
 | 
			
		||||
        this->overhangs.value                                    = true;
 | 
			
		||||
        this->perimeter_acceleration.value                       = 0;
 | 
			
		||||
        this->perimeter_speed.value                              = 30;
 | 
			
		||||
        this->print_center.point                                 = Pointf(100,100);
 | 
			
		||||
        this->randomize_start.value                              = false;
 | 
			
		||||
        this->resolution.value                                   = 0;
 | 
			
		||||
        this->retract_before_travel.values.resize(1);
 | 
			
		||||
        this->retract_before_travel.values[0]                    = 2;
 | 
			
		||||
| 
						 | 
				
			
			@ -1283,21 +1334,13 @@ class PrintConfig : public virtual StaticPrintConfig
 | 
			
		|||
        this->skirt_height.value                                 = 1;
 | 
			
		||||
        this->skirts.value                                       = 1;
 | 
			
		||||
        this->slowdown_below_layer_time.value                    = 30;
 | 
			
		||||
        this->small_perimeter_speed.value                        = 30;
 | 
			
		||||
        this->small_perimeter_speed.percent                      = false;
 | 
			
		||||
        this->solid_infill_speed.value                           = 60;
 | 
			
		||||
        this->solid_infill_speed.percent                         = false;
 | 
			
		||||
        this->spiral_vase.value                                  = false;
 | 
			
		||||
        this->standby_temperature_delta.value                    = -5;
 | 
			
		||||
        this->start_gcode.value                                  = "G28 ; home all axes\nG1 Z5 F5000 ; lift nozzle\n";
 | 
			
		||||
        this->start_perimeters_at_concave_points.value           = false;
 | 
			
		||||
        this->start_perimeters_at_non_overhang.value             = false;
 | 
			
		||||
        this->temperature.values.resize(1);
 | 
			
		||||
        this->temperature.values[0]                              = 200;
 | 
			
		||||
        this->threads.value                                      = 2;
 | 
			
		||||
        this->toolchange_gcode.value                             = "";
 | 
			
		||||
        this->top_solid_infill_speed.value                       = 50;
 | 
			
		||||
        this->top_solid_infill_speed.percent                     = false;
 | 
			
		||||
        this->travel_speed.value                                 = 130;
 | 
			
		||||
        this->use_firmware_retraction.value                      = false;
 | 
			
		||||
        this->use_relative_e_distances.value                     = false;
 | 
			
		||||
| 
						 | 
				
			
			@ -1314,7 +1357,6 @@ class PrintConfig : public virtual StaticPrintConfig
 | 
			
		|||
        if (opt_key == "bridge_acceleration")                        return &this->bridge_acceleration;
 | 
			
		||||
        if (opt_key == "bridge_fan_speed")                           return &this->bridge_fan_speed;
 | 
			
		||||
        if (opt_key == "bridge_flow_ratio")                          return &this->bridge_flow_ratio;
 | 
			
		||||
        if (opt_key == "bridge_speed")                               return &this->bridge_speed;
 | 
			
		||||
        if (opt_key == "brim_width")                                 return &this->brim_width;
 | 
			
		||||
        if (opt_key == "complete_objects")                           return &this->complete_objects;
 | 
			
		||||
        if (opt_key == "cooling")                                    return &this->cooling;
 | 
			
		||||
| 
						 | 
				
			
			@ -1322,7 +1364,6 @@ class PrintConfig : public virtual StaticPrintConfig
 | 
			
		|||
        if (opt_key == "disable_fan_first_layers")                   return &this->disable_fan_first_layers;
 | 
			
		||||
        if (opt_key == "duplicate_distance")                         return &this->duplicate_distance;
 | 
			
		||||
        if (opt_key == "end_gcode")                                  return &this->end_gcode;
 | 
			
		||||
        if (opt_key == "external_perimeter_speed")                   return &this->external_perimeter_speed;
 | 
			
		||||
        if (opt_key == "external_perimeters_first")                  return &this->external_perimeters_first;
 | 
			
		||||
        if (opt_key == "extruder_clearance_height")                  return &this->extruder_clearance_height;
 | 
			
		||||
        if (opt_key == "extruder_clearance_radius")                  return &this->extruder_clearance_radius;
 | 
			
		||||
| 
						 | 
				
			
			@ -1338,13 +1379,11 @@ class PrintConfig : public virtual StaticPrintConfig
 | 
			
		|||
        if (opt_key == "first_layer_speed")                          return &this->first_layer_speed;
 | 
			
		||||
        if (opt_key == "first_layer_temperature")                    return &this->first_layer_temperature;
 | 
			
		||||
        if (opt_key == "g0")                                         return &this->g0;
 | 
			
		||||
        if (opt_key == "gap_fill_speed")                             return &this->gap_fill_speed;
 | 
			
		||||
        if (opt_key == "gcode_arcs")                                 return &this->gcode_arcs;
 | 
			
		||||
        if (opt_key == "gcode_comments")                             return &this->gcode_comments;
 | 
			
		||||
        if (opt_key == "gcode_flavor")                               return &this->gcode_flavor;
 | 
			
		||||
        if (opt_key == "infill_acceleration")                        return &this->infill_acceleration;
 | 
			
		||||
        if (opt_key == "infill_first")                               return &this->infill_first;
 | 
			
		||||
        if (opt_key == "infill_speed")                               return &this->infill_speed;
 | 
			
		||||
        if (opt_key == "layer_gcode")                                return &this->layer_gcode;
 | 
			
		||||
        if (opt_key == "max_fan_speed")                              return &this->max_fan_speed;
 | 
			
		||||
        if (opt_key == "min_fan_speed")                              return &this->min_fan_speed;
 | 
			
		||||
| 
						 | 
				
			
			@ -1355,12 +1394,9 @@ class PrintConfig : public virtual StaticPrintConfig
 | 
			
		|||
        if (opt_key == "only_retract_when_crossing_perimeters")      return &this->only_retract_when_crossing_perimeters;
 | 
			
		||||
        if (opt_key == "ooze_prevention")                            return &this->ooze_prevention;
 | 
			
		||||
        if (opt_key == "output_filename_format")                     return &this->output_filename_format;
 | 
			
		||||
        if (opt_key == "overhangs")                                  return &this->overhangs;
 | 
			
		||||
        if (opt_key == "perimeter_acceleration")                     return &this->perimeter_acceleration;
 | 
			
		||||
        if (opt_key == "perimeter_speed")                            return &this->perimeter_speed;
 | 
			
		||||
        if (opt_key == "post_process")                               return &this->post_process;
 | 
			
		||||
        if (opt_key == "print_center")                               return &this->print_center;
 | 
			
		||||
        if (opt_key == "randomize_start")                            return &this->randomize_start;
 | 
			
		||||
        if (opt_key == "resolution")                                 return &this->resolution;
 | 
			
		||||
        if (opt_key == "retract_before_travel")                      return &this->retract_before_travel;
 | 
			
		||||
        if (opt_key == "retract_layer_change")                       return &this->retract_layer_change;
 | 
			
		||||
| 
						 | 
				
			
			@ -1374,17 +1410,12 @@ class PrintConfig : public virtual StaticPrintConfig
 | 
			
		|||
        if (opt_key == "skirt_height")                               return &this->skirt_height;
 | 
			
		||||
        if (opt_key == "skirts")                                     return &this->skirts;
 | 
			
		||||
        if (opt_key == "slowdown_below_layer_time")                  return &this->slowdown_below_layer_time;
 | 
			
		||||
        if (opt_key == "small_perimeter_speed")                      return &this->small_perimeter_speed;
 | 
			
		||||
        if (opt_key == "solid_infill_speed")                         return &this->solid_infill_speed;
 | 
			
		||||
        if (opt_key == "spiral_vase")                                return &this->spiral_vase;
 | 
			
		||||
        if (opt_key == "standby_temperature_delta")                  return &this->standby_temperature_delta;
 | 
			
		||||
        if (opt_key == "start_gcode")                                return &this->start_gcode;
 | 
			
		||||
        if (opt_key == "start_perimeters_at_concave_points")         return &this->start_perimeters_at_concave_points;
 | 
			
		||||
        if (opt_key == "start_perimeters_at_non_overhang")           return &this->start_perimeters_at_non_overhang;
 | 
			
		||||
        if (opt_key == "temperature")                                return &this->temperature;
 | 
			
		||||
        if (opt_key == "threads")                                    return &this->threads;
 | 
			
		||||
        if (opt_key == "toolchange_gcode")                           return &this->toolchange_gcode;
 | 
			
		||||
        if (opt_key == "top_solid_infill_speed")                     return &this->top_solid_infill_speed;
 | 
			
		||||
        if (opt_key == "travel_speed")                               return &this->travel_speed;
 | 
			
		||||
        if (opt_key == "use_firmware_retraction")                    return &this->use_firmware_retraction;
 | 
			
		||||
        if (opt_key == "use_relative_e_distances")                   return &this->use_relative_e_distances;
 | 
			
		||||
| 
						 | 
				
			
			@ -1394,6 +1425,17 @@ class PrintConfig : public virtual StaticPrintConfig
 | 
			
		|||
        
 | 
			
		||||
        return NULL;
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class FullPrintConfig : public PrintObjectConfig, public PrintRegionConfig, public PrintConfig {
 | 
			
		||||
    public:
 | 
			
		||||
    ConfigOption* option(const t_config_option_key opt_key, bool create = false) {
 | 
			
		||||
        ConfigOption* opt;
 | 
			
		||||
        if ((opt = PrintObjectConfig::option(opt_key, create)) != NULL) return opt;
 | 
			
		||||
        if ((opt = PrintRegionConfig::option(opt_key, create)) != NULL) return opt;
 | 
			
		||||
        if ((opt = PrintConfig::option(opt_key, create)) != NULL) return opt;
 | 
			
		||||
        return NULL;
 | 
			
		||||
    };
 | 
			
		||||
    
 | 
			
		||||
    std::string get_extrusion_axis() {
 | 
			
		||||
        if (this->gcode_flavor == gcfMach3) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1405,17 +1447,6 @@ class PrintConfig : public virtual StaticPrintConfig
 | 
			
		|||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class FullPrintConfig : public PrintObjectConfig, public PrintRegionConfig, public PrintConfig {
 | 
			
		||||
    public:
 | 
			
		||||
    ConfigOption* option(const t_config_option_key opt_key, bool create = false) {
 | 
			
		||||
        ConfigOption* opt;
 | 
			
		||||
        if ((opt = PrintObjectConfig::option(opt_key, create)) != NULL) return opt;
 | 
			
		||||
        if ((opt = PrintRegionConfig::option(opt_key, create)) != NULL) return opt;
 | 
			
		||||
        if ((opt = PrintConfig::option(opt_key, create)) != NULL) return opt;
 | 
			
		||||
        return NULL;
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,8 +1,8 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
*                                                                              *
 | 
			
		||||
* Author    :  Angus Johnson                                                   *
 | 
			
		||||
* Version   :  6.1.3a                                                          *
 | 
			
		||||
* Date      :  22 January 2014                                                 *
 | 
			
		||||
* Version   :  6.1.5                                                           *
 | 
			
		||||
* Date      :  22 May 2014                                                     *
 | 
			
		||||
* Website   :  http://www.angusj.com                                           *
 | 
			
		||||
* Copyright :  Angus Johnson 2010-2014                                         *
 | 
			
		||||
*                                                                              *
 | 
			
		||||
| 
						 | 
				
			
			@ -50,15 +50,6 @@
 | 
			
		|||
 | 
			
		||||
namespace ClipperLib {
 | 
			
		||||
 | 
			
		||||
#ifdef use_int32
 | 
			
		||||
  static cInt const loRange = 46340;
 | 
			
		||||
  static cInt const hiRange = 46340;
 | 
			
		||||
#else
 | 
			
		||||
  static cInt const loRange = 0x3FFFFFFF;
 | 
			
		||||
  static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL;
 | 
			
		||||
  typedef unsigned long long ulong64;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static double const pi = 3.141592653589793238;
 | 
			
		||||
static double const two_pi = pi *2;
 | 
			
		||||
static double const def_arc_tolerance = 0.25;
 | 
			
		||||
| 
						 | 
				
			
			@ -240,8 +231,8 @@ bool PolyNode::IsOpen() const
 | 
			
		|||
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
// Int128 class (enables safe math on signed 64bit integers)
 | 
			
		||||
// eg Int128 val1((cInt)9223372036854775807); //ie 2^63 -1
 | 
			
		||||
//    Int128 val2((cInt)9223372036854775807);
 | 
			
		||||
// eg Int128 val1((long64)9223372036854775807); //ie 2^63 -1
 | 
			
		||||
//    Int128 val2((long64)9223372036854775807);
 | 
			
		||||
//    Int128 val3 = val1 * val2;
 | 
			
		||||
//    val3.AsString => "85070591730234615847396907784232501249" (8.5e+37)
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
| 
						 | 
				
			
			@ -249,22 +240,21 @@ bool PolyNode::IsOpen() const
 | 
			
		|||
class Int128
 | 
			
		||||
{
 | 
			
		||||
  public:
 | 
			
		||||
    ulong64 lo;
 | 
			
		||||
    long64 hi;
 | 
			
		||||
 | 
			
		||||
    cUInt lo;
 | 
			
		||||
    cInt hi;
 | 
			
		||||
 | 
			
		||||
    Int128(cInt _lo = 0)
 | 
			
		||||
    Int128(long64 _lo = 0)
 | 
			
		||||
    {
 | 
			
		||||
      lo = (cUInt)_lo;   
 | 
			
		||||
      lo = (ulong64)_lo;   
 | 
			
		||||
      if (_lo < 0)  hi = -1; else hi = 0; 
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    Int128(const Int128 &val): lo(val.lo), hi(val.hi){}
 | 
			
		||||
 | 
			
		||||
    Int128(const cInt& _hi, const ulong64& _lo): lo(_lo), hi(_hi){}
 | 
			
		||||
    Int128(const long64& _hi, const ulong64& _lo): lo(_lo), hi(_hi){}
 | 
			
		||||
    
 | 
			
		||||
    Int128& operator = (const cInt &val)
 | 
			
		||||
    Int128& operator = (const long64 &val)
 | 
			
		||||
    {
 | 
			
		||||
      lo = (ulong64)val;
 | 
			
		||||
      if (val < 0) hi = -1; else hi = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -335,85 +325,10 @@ class Int128
 | 
			
		|||
        return Int128(~hi,~lo +1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Int128 operator/ (const Int128 &rhs) const
 | 
			
		||||
    {
 | 
			
		||||
      if (rhs.lo == 0 && rhs.hi == 0)
 | 
			
		||||
        throw "Int128 operator/: divide by zero";
 | 
			
		||||
 | 
			
		||||
      bool negate = (rhs.hi < 0) != (hi < 0);
 | 
			
		||||
      Int128 dividend = *this;
 | 
			
		||||
      Int128 divisor = rhs;
 | 
			
		||||
      if (dividend.hi < 0) dividend = -dividend;
 | 
			
		||||
      if (divisor.hi < 0) divisor = -divisor;
 | 
			
		||||
 | 
			
		||||
      if (divisor < dividend)
 | 
			
		||||
      {
 | 
			
		||||
          Int128 result = Int128(0);
 | 
			
		||||
          Int128 cntr = Int128(1);
 | 
			
		||||
          while (divisor.hi >= 0 && !(divisor > dividend))
 | 
			
		||||
          {
 | 
			
		||||
              divisor.hi <<= 1;
 | 
			
		||||
              if ((cInt)divisor.lo < 0) divisor.hi++;
 | 
			
		||||
              divisor.lo <<= 1;
 | 
			
		||||
 | 
			
		||||
              cntr.hi <<= 1;
 | 
			
		||||
              if ((cInt)cntr.lo < 0) cntr.hi++;
 | 
			
		||||
              cntr.lo <<= 1;
 | 
			
		||||
          }
 | 
			
		||||
          divisor.lo >>= 1;
 | 
			
		||||
          if ((divisor.hi & 1) == 1)
 | 
			
		||||
              divisor.lo |= 0x8000000000000000LL; 
 | 
			
		||||
          divisor.hi = (ulong64)divisor.hi >> 1;
 | 
			
		||||
 | 
			
		||||
          cntr.lo >>= 1;
 | 
			
		||||
          if ((cntr.hi & 1) == 1)
 | 
			
		||||
              cntr.lo |= 0x8000000000000000LL; 
 | 
			
		||||
          cntr.hi >>= 1;
 | 
			
		||||
 | 
			
		||||
          while (cntr.hi != 0 || cntr.lo != 0)
 | 
			
		||||
          {
 | 
			
		||||
              if (!(dividend < divisor))
 | 
			
		||||
              {
 | 
			
		||||
                  dividend -= divisor;
 | 
			
		||||
                  result.hi |= cntr.hi;
 | 
			
		||||
                  result.lo |= cntr.lo;
 | 
			
		||||
              }
 | 
			
		||||
              divisor.lo >>= 1;
 | 
			
		||||
              if ((divisor.hi & 1) == 1)
 | 
			
		||||
                  divisor.lo |= 0x8000000000000000LL; 
 | 
			
		||||
              divisor.hi >>= 1;
 | 
			
		||||
 | 
			
		||||
              cntr.lo >>= 1;
 | 
			
		||||
              if ((cntr.hi & 1) == 1)
 | 
			
		||||
                  cntr.lo |= 0x8000000000000000LL; 
 | 
			
		||||
              cntr.hi >>= 1;
 | 
			
		||||
          }
 | 
			
		||||
          if (negate) result = -result;
 | 
			
		||||
          return result;
 | 
			
		||||
      }
 | 
			
		||||
      else if (rhs.hi == this->hi && rhs.lo == this->lo)
 | 
			
		||||
          return Int128(negate ? -1: 1);
 | 
			
		||||
      else
 | 
			
		||||
          return Int128(0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    double AsDouble() const
 | 
			
		||||
    {
 | 
			
		||||
      const double shift64 = 18446744073709551616.0; //2^64
 | 
			
		||||
      if (hi < 0)
 | 
			
		||||
      {
 | 
			
		||||
        cUInt lo_ = ~lo + 1;
 | 
			
		||||
        if (lo_ == 0) return (double)hi * shift64;
 | 
			
		||||
        else return -(double)(lo_ + ~hi * shift64);
 | 
			
		||||
      }
 | 
			
		||||
      else
 | 
			
		||||
        return (double)(lo + hi * shift64);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
Int128 Int128Mul (cInt lhs, cInt rhs)
 | 
			
		||||
Int128 Int128Mul (long64 lhs, long64 rhs)
 | 
			
		||||
{
 | 
			
		||||
  bool negate = (lhs < 0) != (rhs < 0);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -431,9 +346,9 @@ Int128 Int128Mul (cInt lhs, cInt rhs)
 | 
			
		|||
  ulong64 c = int1Hi * int2Lo + int1Lo * int2Hi;
 | 
			
		||||
 | 
			
		||||
  Int128 tmp;
 | 
			
		||||
  tmp.hi = cInt(a + (c >> 32));
 | 
			
		||||
  tmp.lo = cInt(c << 32);
 | 
			
		||||
  tmp.lo += cInt(b);
 | 
			
		||||
  tmp.hi = long64(a + (c >> 32));
 | 
			
		||||
  tmp.lo = long64(c << 32);
 | 
			
		||||
  tmp.lo += long64(b);
 | 
			
		||||
  if (tmp.lo < b) tmp.hi++;
 | 
			
		||||
  if (negate) tmp = -tmp;
 | 
			
		||||
  return tmp;
 | 
			
		||||
| 
						 | 
				
			
			@ -444,6 +359,13 @@ Int128 Int128Mul (cInt lhs, cInt rhs)
 | 
			
		|||
// Miscellaneous global functions
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
void Swap(cInt& val1, cInt& val2)
 | 
			
		||||
{
 | 
			
		||||
  cInt tmp = val1;
 | 
			
		||||
  val1 = val2;
 | 
			
		||||
  val2 = tmp;
 | 
			
		||||
}
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
bool Orientation(const Path &poly)
 | 
			
		||||
{
 | 
			
		||||
    return Area(poly) >= 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -494,6 +416,7 @@ bool PointIsVertex(const IntPoint &Pt, OutPt *pp)
 | 
			
		|||
int PointInPolygon (const IntPoint &pt, const Path &path)
 | 
			
		||||
{
 | 
			
		||||
  //returns 0 if false, +1 if true, -1 if pt ON polygon boundary
 | 
			
		||||
  //See "The Point in Polygon Problem for Arbitrary Polygons" by Hormann & Agathos
 | 
			
		||||
  //http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf
 | 
			
		||||
  int result = 0;
 | 
			
		||||
  size_t cnt = path.size();
 | 
			
		||||
| 
						 | 
				
			
			@ -539,7 +462,6 @@ int PointInPolygon (const IntPoint &pt, const Path &path)
 | 
			
		|||
int PointInPolygon (const IntPoint &pt, OutPt *op)
 | 
			
		||||
{
 | 
			
		||||
  //returns 0 if false, +1 if true, -1 if pt ON polygon boundary
 | 
			
		||||
  //http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf
 | 
			
		||||
  int result = 0;
 | 
			
		||||
  OutPt* startOp = op;
 | 
			
		||||
  for(;;)
 | 
			
		||||
| 
						 | 
				
			
			@ -674,20 +596,18 @@ inline cInt TopX(TEdge &edge, const cInt currentY)
 | 
			
		|||
}
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
bool IntersectPoint(TEdge &Edge1, TEdge &Edge2,
 | 
			
		||||
  IntPoint &ip, bool UseFullInt64Range)
 | 
			
		||||
void IntersectPoint(TEdge &Edge1, TEdge &Edge2, IntPoint &ip)
 | 
			
		||||
{
 | 
			
		||||
#ifdef use_xyz  
 | 
			
		||||
  ip.Z = 0;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  double b1, b2;
 | 
			
		||||
  //nb: with very large coordinate values, it's possible for SlopesEqual() to 
 | 
			
		||||
  //return false but for the edge.Dx value be equal due to double precision rounding.
 | 
			
		||||
  if (SlopesEqual(Edge1, Edge2, UseFullInt64Range) || Edge1.Dx == Edge2.Dx)
 | 
			
		||||
  if (Edge1.Dx == Edge2.Dx)
 | 
			
		||||
  {
 | 
			
		||||
    if (Edge2.Bot.Y > Edge1.Bot.Y) ip = Edge2.Bot;
 | 
			
		||||
    else ip = Edge1.Bot;
 | 
			
		||||
    return false;
 | 
			
		||||
    ip.Y = Edge1.Curr.Y;
 | 
			
		||||
    ip.X = TopX(Edge1, ip.Y);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  else if (Edge1.Delta.X == 0)
 | 
			
		||||
  {
 | 
			
		||||
| 
						 | 
				
			
			@ -734,7 +654,15 @@ bool IntersectPoint(TEdge &Edge1, TEdge &Edge2,
 | 
			
		|||
    else
 | 
			
		||||
      ip.X = TopX(Edge2, ip.Y);
 | 
			
		||||
  } 
 | 
			
		||||
  return true;
 | 
			
		||||
  //finally, don't allow 'ip' to be BELOW curr.Y (ie bottom of scanbeam) ...
 | 
			
		||||
  if (ip.Y > Edge1.Curr.Y)
 | 
			
		||||
  {
 | 
			
		||||
    ip.Y = Edge1.Curr.Y;
 | 
			
		||||
    //use the more vertical edge to derive X ...
 | 
			
		||||
    if (std::fabs(Edge1.Dx) > std::fabs(Edge2.Dx))
 | 
			
		||||
      ip.X = TopX(Edge2, ip.Y); else
 | 
			
		||||
      ip.X = TopX(Edge1, ip.Y);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -807,13 +735,9 @@ inline void ReverseHorizontal(TEdge &e)
 | 
			
		|||
  //swap horizontal edges' Top and Bottom x's so they follow the natural
 | 
			
		||||
  //progression of the bounds - ie so their xbots will align with the
 | 
			
		||||
  //adjoining lower edge. [Helpful in the ProcessHorizontal() method.]
 | 
			
		||||
  cInt tmp = e.Top.X;
 | 
			
		||||
  e.Top.X = e.Bot.X;
 | 
			
		||||
  e.Bot.X = tmp;
 | 
			
		||||
  Swap(e.Top.X, e.Bot.X);
 | 
			
		||||
#ifdef use_xyz  
 | 
			
		||||
  tmp = e.Top.Z;
 | 
			
		||||
  e.Top.Z = e.Bot.Z;
 | 
			
		||||
  e.Bot.Z = tmp;
 | 
			
		||||
  Swap(e.Top.Z, e.Bot.Z);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
| 
						 | 
				
			
			@ -905,26 +829,6 @@ OutPt* GetBottomPt(OutPt *pp)
 | 
			
		|||
}
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
bool FindSegment(OutPt* &pp, bool UseFullInt64Range, 
 | 
			
		||||
  IntPoint &pt1, IntPoint &pt2)
 | 
			
		||||
{
 | 
			
		||||
  //OutPt1 & OutPt2 => the overlap segment (if the function returns true)
 | 
			
		||||
  if (!pp) return false;
 | 
			
		||||
  OutPt* pp2 = pp;
 | 
			
		||||
  IntPoint pt1a = pt1, pt2a = pt2;
 | 
			
		||||
  do
 | 
			
		||||
  {
 | 
			
		||||
    if (SlopesEqual(pt1a, pt2a, pp->Pt, pp->Prev->Pt, UseFullInt64Range) &&
 | 
			
		||||
      SlopesEqual(pt1a, pt2a, pp->Pt, UseFullInt64Range) &&
 | 
			
		||||
      GetOverlapSegment(pt1a, pt2a, pp->Pt, pp->Prev->Pt, pt1, pt2))
 | 
			
		||||
        return true;
 | 
			
		||||
    pp = pp->Next;
 | 
			
		||||
  }
 | 
			
		||||
  while (pp != pp2);
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
bool Pt2IsBetweenPt1AndPt3(const IntPoint pt1,
 | 
			
		||||
  const IntPoint pt2, const IntPoint pt3)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -937,41 +841,12 @@ bool Pt2IsBetweenPt1AndPt3(const IntPoint pt1,
 | 
			
		|||
}
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
OutPt* InsertPolyPtBetween(OutPt* p1, OutPt* p2, const IntPoint Pt)
 | 
			
		||||
bool HorzSegmentsOverlap(cInt seg1a, cInt seg1b, cInt seg2a, cInt seg2b)
 | 
			
		||||
{
 | 
			
		||||
  if (p1 == p2) throw "JoinError";
 | 
			
		||||
  OutPt* result = new OutPt;
 | 
			
		||||
  result->Pt = Pt;
 | 
			
		||||
  if (p2 == p1->Next)
 | 
			
		||||
  {
 | 
			
		||||
    p1->Next = result;
 | 
			
		||||
    p2->Prev = result;
 | 
			
		||||
    result->Next = p2;
 | 
			
		||||
    result->Prev = p1;
 | 
			
		||||
  } else
 | 
			
		||||
  {
 | 
			
		||||
    p2->Next = result;
 | 
			
		||||
    p1->Prev = result;
 | 
			
		||||
    result->Next = p1;
 | 
			
		||||
    result->Prev = p2;
 | 
			
		||||
  }
 | 
			
		||||
  return result;
 | 
			
		||||
  if (seg1a > seg1b) Swap(seg1a, seg1b);
 | 
			
		||||
  if (seg2a > seg2b) Swap(seg2a, seg2b);
 | 
			
		||||
  return (seg1a < seg2b) && (seg2a < seg1b);
 | 
			
		||||
}
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
bool HorzSegmentsOverlap(const IntPoint& pt1a, const IntPoint& pt1b, 
 | 
			
		||||
    const IntPoint& pt2a, const IntPoint& pt2b)
 | 
			
		||||
{
 | 
			
		||||
  //precondition: both segments are horizontal
 | 
			
		||||
  if ((pt1a.X > pt2a.X) == (pt1a.X < pt2b.X)) return true;
 | 
			
		||||
  else if ((pt1b.X > pt2a.X) == (pt1b.X < pt2b.X)) return true;
 | 
			
		||||
  else if ((pt2a.X > pt1a.X) == (pt2a.X < pt1b.X)) return true;
 | 
			
		||||
  else if ((pt2b.X > pt1a.X) == (pt2b.X < pt1b.X)) return true;
 | 
			
		||||
  else if ((pt1a.X == pt2a.X) && (pt1b.X == pt2b.X)) return true;
 | 
			
		||||
  else if ((pt1a.X == pt2b.X) && (pt1b.X == pt2a.X)) return true;
 | 
			
		||||
  else return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
// ClipperBase class methods ...
 | 
			
		||||
| 
						 | 
				
			
			@ -1030,20 +905,20 @@ TEdge* ClipperBase::ProcessBound(TEdge* E, bool IsClockwise)
 | 
			
		|||
  cInt StartX;
 | 
			
		||||
  if (IsHorizontal(*E))
 | 
			
		||||
  {
 | 
			
		||||
    //first we need to be careful here with open paths because this
 | 
			
		||||
    //may not be a true local minima (ie may be following a skip edge).
 | 
			
		||||
    //also, watch for adjacent horz edges to start heading left
 | 
			
		||||
    //before finishing right ...
 | 
			
		||||
    if (IsClockwise)
 | 
			
		||||
      {
 | 
			
		||||
        if (E->Prev->Bot.Y == E->Bot.Y) StartX = E->Prev->Bot.X;
 | 
			
		||||
        else StartX = E->Prev->Top.X;
 | 
			
		||||
      }
 | 
			
		||||
    else
 | 
			
		||||
      {
 | 
			
		||||
        if (E->Next->Bot.Y == E->Bot.Y) StartX = E->Next->Bot.X;
 | 
			
		||||
        else StartX = E->Next->Top.X;
 | 
			
		||||
      }
 | 
			
		||||
	//first we need to be careful here with open paths because this
 | 
			
		||||
	//may not be a true local minima (ie may be following a skip edge).
 | 
			
		||||
	//also, watch for adjacent horz edges to start heading left
 | 
			
		||||
	//before finishing right ...
 | 
			
		||||
	if (IsClockwise)
 | 
			
		||||
	{
 | 
			
		||||
		if (E->Prev->Bot.Y == E->Bot.Y) StartX = E->Prev->Bot.X;
 | 
			
		||||
		else StartX = E->Prev->Top.X;
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		if (E->Next->Bot.Y == E->Bot.Y) StartX = E->Next->Bot.X;
 | 
			
		||||
		else StartX = E->Next->Top.X;
 | 
			
		||||
	}
 | 
			
		||||
    if (E->Bot.X != StartX) ReverseHorizontal(*E);
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
| 
						 | 
				
			
			@ -1189,7 +1064,8 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed)
 | 
			
		|||
  TEdge *E = eStart, *eLoopStop = eStart;
 | 
			
		||||
  for (;;)
 | 
			
		||||
  {
 | 
			
		||||
    if ((E->Curr == E->Next->Curr))
 | 
			
		||||
    //nb: allows matching start and end points when not Closed ...
 | 
			
		||||
    if (E->Curr == E->Next->Curr && (Closed || E->Next != eStart))
 | 
			
		||||
    {
 | 
			
		||||
      if (E == E->Next) break;
 | 
			
		||||
      if (E == eStart) eStart = E->Next;
 | 
			
		||||
| 
						 | 
				
			
			@ -1215,7 +1091,7 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed)
 | 
			
		|||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    E = E->Next;
 | 
			
		||||
    if (E == eLoopStop) break;
 | 
			
		||||
    if ((E == eLoopStop) || (!Closed && E->Next == eStart)) break;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if ((!Closed && (E == E->Next)) || (Closed && (E->Prev == E->Next)))
 | 
			
		||||
| 
						 | 
				
			
			@ -1274,6 +1150,11 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed)
 | 
			
		|||
  m_edges.push_back(edges);
 | 
			
		||||
  bool clockwise;
 | 
			
		||||
  TEdge* EMin = 0;
 | 
			
		||||
 | 
			
		||||
  //workaround to avoid an endless loop in the while loop below when
 | 
			
		||||
  //open paths have matching start and end points ...
 | 
			
		||||
  if (E->Prev->Bot == E->Prev->Top) E = E->Next;
 | 
			
		||||
 | 
			
		||||
  for (;;)
 | 
			
		||||
  {
 | 
			
		||||
    E = FindNextLocMin(E);
 | 
			
		||||
| 
						 | 
				
			
			@ -2028,7 +1909,7 @@ void Clipper::InsertLocalMinimaIntoAEL(const cInt botY)
 | 
			
		|||
        Join* jr = m_GhostJoins[i];
 | 
			
		||||
        //if the horizontal Rb and a 'ghost' horizontal overlap, then convert
 | 
			
		||||
        //the 'ghost' join to a real join ready for later ...
 | 
			
		||||
        if (HorzSegmentsOverlap(jr->OutPt1->Pt, jr->OffPt, rb->Bot, rb->Top))
 | 
			
		||||
        if (HorzSegmentsOverlap(jr->OutPt1->Pt.X, jr->OffPt.X, rb->Bot.X, rb->Top.X))
 | 
			
		||||
          AddJoin(jr->OutPt1, Op1, jr->OffPt);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -2098,45 +1979,34 @@ void Clipper::DeleteFromSEL(TEdge *e)
 | 
			
		|||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
#ifdef use_xyz
 | 
			
		||||
 | 
			
		||||
void Clipper::SetZ(IntPoint& pt, TEdge& e)
 | 
			
		||||
void Clipper::SetZ(IntPoint& pt, TEdge& e1, TEdge& e2)
 | 
			
		||||
{
 | 
			
		||||
  pt.Z = 0;
 | 
			
		||||
  if (m_ZFill)
 | 
			
		||||
  {
 | 
			
		||||
    //put the 'preferred' point as first parameter ...
 | 
			
		||||
    if (e.OutIdx < 0)
 | 
			
		||||
      (*m_ZFill)(e.Bot, e.Top, pt); //outside a path so presume entering
 | 
			
		||||
    else
 | 
			
		||||
      (*m_ZFill)(e.Top, e.Bot, pt); //inside a path so presume exiting
 | 
			
		||||
  }
 | 
			
		||||
  if (pt.Z != 0 || !m_ZFill) return;
 | 
			
		||||
  else if (pt == e1.Bot) pt.Z = e1.Bot.Z;
 | 
			
		||||
  else if (pt == e1.Top) pt.Z = e1.Top.Z;
 | 
			
		||||
  else if (pt == e2.Bot) pt.Z = e2.Bot.Z;
 | 
			
		||||
  else if (pt == e2.Top) pt.Z = e2.Top.Z;
 | 
			
		||||
  else (*m_ZFill)(e1.Bot, e1.Top, e2.Bot, e2.Top, pt); 
 | 
			
		||||
}
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
void Clipper::IntersectEdges(TEdge *e1, TEdge *e2,
 | 
			
		||||
     const IntPoint &Pt, bool protect)
 | 
			
		||||
void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &Pt)
 | 
			
		||||
{
 | 
			
		||||
  //e1 will be to the Left of e2 BELOW the intersection. Therefore e1 is before
 | 
			
		||||
  //e2 in AEL except when e1 is being inserted at the intersection point ...
 | 
			
		||||
  bool e1stops = !protect &&  !e1->NextInLML &&
 | 
			
		||||
    e1->Top.X == Pt.X && e1->Top.Y == Pt.Y;
 | 
			
		||||
  bool e2stops = !protect &&  !e2->NextInLML &&
 | 
			
		||||
    e2->Top.X == Pt.X && e2->Top.Y == Pt.Y;
 | 
			
		||||
  bool e1Contributing = ( e1->OutIdx >= 0 );
 | 
			
		||||
  bool e2Contributing = ( e2->OutIdx >= 0 );
 | 
			
		||||
 | 
			
		||||
#ifdef use_xyz
 | 
			
		||||
        SetZ(Pt, *e1, *e2);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef use_lines
 | 
			
		||||
  //if either edge is on an OPEN path ...
 | 
			
		||||
  if (e1->WindDelta == 0 || e2->WindDelta == 0)
 | 
			
		||||
  {
 | 
			
		||||
    //ignore subject-subject open path intersections UNLESS they
 | 
			
		||||
    //are both open paths, AND they are both 'contributing maximas' ...
 | 
			
		||||
    if (e1->WindDelta == 0 && e2->WindDelta == 0)
 | 
			
		||||
    {
 | 
			
		||||
      if ((e1stops || e2stops) && e1Contributing && e2Contributing)
 | 
			
		||||
        AddLocalMaxPoly(e1, e2, Pt);
 | 
			
		||||
    }
 | 
			
		||||
	if (e1->WindDelta == 0 && e2->WindDelta == 0) return;
 | 
			
		||||
 | 
			
		||||
    //if intersecting a subj line with a subj poly ...
 | 
			
		||||
    else if (e1->PolyTyp == e2->PolyTyp && 
 | 
			
		||||
| 
						 | 
				
			
			@ -2175,13 +2045,6 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2,
 | 
			
		|||
        if (e2Contributing) e2->OutIdx = Unassigned;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (e1stops)
 | 
			
		||||
      if (e1->OutIdx < 0) DeleteFromAEL(e1);
 | 
			
		||||
      else throw clipperException("Error intersecting polylines");
 | 
			
		||||
    if (e2stops) 
 | 
			
		||||
      if (e2->OutIdx < 0) DeleteFromAEL(e2);
 | 
			
		||||
      else throw clipperException("Error intersecting polylines");
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -2246,10 +2109,11 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2,
 | 
			
		|||
 | 
			
		||||
  if ( e1Contributing && e2Contributing )
 | 
			
		||||
  {
 | 
			
		||||
    if ( e1stops || e2stops || 
 | 
			
		||||
      (e1Wc != 0 && e1Wc != 1) || (e2Wc != 0 && e2Wc != 1) ||
 | 
			
		||||
    if ((e1Wc != 0 && e1Wc != 1) || (e2Wc != 0 && e2Wc != 1) ||
 | 
			
		||||
      (e1->PolyTyp != e2->PolyTyp && m_ClipType != ctXor) )
 | 
			
		||||
        AddLocalMaxPoly(e1, e2, Pt); 
 | 
			
		||||
    {
 | 
			
		||||
      AddLocalMaxPoly(e1, e2, Pt); 
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
    {
 | 
			
		||||
      AddOutPt(e1, Pt);
 | 
			
		||||
| 
						 | 
				
			
			@ -2276,8 +2140,7 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2,
 | 
			
		|||
      SwapPolyIndexes(*e1, *e2);
 | 
			
		||||
    }
 | 
			
		||||
  } 
 | 
			
		||||
  else if ( (e1Wc == 0 || e1Wc == 1) && 
 | 
			
		||||
    (e2Wc == 0 || e2Wc == 1) && !e1stops && !e2stops )
 | 
			
		||||
  else if ( (e1Wc == 0 || e1Wc == 1) && (e2Wc == 0 || e2Wc == 1))
 | 
			
		||||
  {
 | 
			
		||||
    //neither edge is currently contributing ...
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -2296,7 +2159,9 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2,
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    if (e1->PolyTyp != e2->PolyTyp)
 | 
			
		||||
        AddLocalMinPoly(e1, e2, Pt);
 | 
			
		||||
    {
 | 
			
		||||
      AddLocalMinPoly(e1, e2, Pt);
 | 
			
		||||
    }
 | 
			
		||||
    else if (e1Wc == 1 && e2Wc == 1)
 | 
			
		||||
      switch( m_ClipType ) {
 | 
			
		||||
        case ctIntersection:
 | 
			
		||||
| 
						 | 
				
			
			@ -2318,17 +2183,6 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2,
 | 
			
		|||
    else
 | 
			
		||||
      SwapSides( *e1, *e2 );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if(  (e1stops != e2stops) &&
 | 
			
		||||
    ( (e1stops && (e1->OutIdx >= 0)) || (e2stops && (e2->OutIdx >= 0)) ) )
 | 
			
		||||
  {
 | 
			
		||||
    SwapSides( *e1, *e2 );
 | 
			
		||||
    SwapPolyIndexes( *e1, *e2 );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  //finally, delete any non-contributing maxima edges  ...
 | 
			
		||||
  if( e1stops ) DeleteFromAEL( e1 );
 | 
			
		||||
  if( e2stops ) DeleteFromAEL( e2 );
 | 
			
		||||
}
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -2519,12 +2373,7 @@ OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt)
 | 
			
		|||
    newOp->Prev = newOp;
 | 
			
		||||
    if (!outRec->IsOpen)
 | 
			
		||||
      SetHoleState(e, outRec);
 | 
			
		||||
#ifdef use_xyz
 | 
			
		||||
    if (pt == e->Bot) newOp->Pt = e->Bot;
 | 
			
		||||
    else if (pt == e->Top) newOp->Pt = e->Top;
 | 
			
		||||
    else SetZ(newOp->Pt, *e);
 | 
			
		||||
#endif
 | 
			
		||||
    e->OutIdx = outRec->Idx; //nb: do this after SetZ !
 | 
			
		||||
    e->OutIdx = outRec->Idx;
 | 
			
		||||
    return newOp;
 | 
			
		||||
  } else
 | 
			
		||||
  {
 | 
			
		||||
| 
						 | 
				
			
			@ -2543,11 +2392,6 @@ OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt)
 | 
			
		|||
    newOp->Prev->Next = newOp;
 | 
			
		||||
    op->Prev = newOp;
 | 
			
		||||
    if (ToFront) outRec->Pts = newOp;
 | 
			
		||||
#ifdef use_xyz
 | 
			
		||||
    if (pt == e->Bot) newOp->Pt = e->Bot;
 | 
			
		||||
    else if (pt == e->Top) newOp->Pt = e->Top;
 | 
			
		||||
    else SetZ(newOp->Pt, *e);
 | 
			
		||||
#endif
 | 
			
		||||
    return newOp;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -2714,37 +2558,6 @@ void GetHorzDirection(TEdge& HorzEdge, Direction& Dir, cInt& Left, cInt& Right)
 | 
			
		|||
}
 | 
			
		||||
//------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
void Clipper::PrepareHorzJoins(TEdge* horzEdge, bool isTopOfScanbeam)
 | 
			
		||||
{
 | 
			
		||||
  //get the last Op for this horizontal edge
 | 
			
		||||
  //the point may be anywhere along the horizontal ...
 | 
			
		||||
  OutPt* outPt = m_PolyOuts[horzEdge->OutIdx]->Pts;
 | 
			
		||||
  if (horzEdge->Side != esLeft) outPt = outPt->Prev;
 | 
			
		||||
 | 
			
		||||
  //First, match up overlapping horizontal edges (eg when one polygon's
 | 
			
		||||
  //intermediate horz edge overlaps an intermediate horz edge of another, or
 | 
			
		||||
  //when one polygon sits on top of another) ...
 | 
			
		||||
  //for (JoinList::size_type i = 0; i < m_GhostJoins.size(); ++i)
 | 
			
		||||
  //{
 | 
			
		||||
  //  Join* j = m_GhostJoins[i];
 | 
			
		||||
  //  if (HorzSegmentsOverlap(j->OutPt1->Pt, j->OffPt, horzEdge->Bot, horzEdge->Top))
 | 
			
		||||
  //      AddJoin(j->OutPt1, outPt, j->OffPt);
 | 
			
		||||
  //}
 | 
			
		||||
 | 
			
		||||
  //Also, since horizontal edges at the top of one SB are often removed from
 | 
			
		||||
  //the AEL before we process the horizontal edges at the bottom of the next,
 | 
			
		||||
  //we need to create 'ghost' Join records of 'contrubuting' horizontals that
 | 
			
		||||
  //we can compare with horizontals at the bottom of the next SB.
 | 
			
		||||
  if (isTopOfScanbeam) 
 | 
			
		||||
  {
 | 
			
		||||
    if (outPt->Pt == horzEdge->Top)
 | 
			
		||||
      AddGhostJoin(outPt, horzEdge->Bot); 
 | 
			
		||||
    else
 | 
			
		||||
      AddGhostJoin(outPt, horzEdge->Top);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
/*******************************************************************************
 | 
			
		||||
* Notes: Horizontal edges (HEs) at scanline intersections (ie at the Top or    *
 | 
			
		||||
* Bottom of a scanbeam) are processed as if layered. The order in which HEs    *
 | 
			
		||||
| 
						 | 
				
			
			@ -2784,28 +2597,42 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam)
 | 
			
		|||
      if ((dir == dLeftToRight && e->Curr.X <= horzRight) ||
 | 
			
		||||
        (dir == dRightToLeft && e->Curr.X >= horzLeft))
 | 
			
		||||
      {
 | 
			
		||||
        if (horzEdge->OutIdx >= 0 && horzEdge->WindDelta != 0) 
 | 
			
		||||
          PrepareHorzJoins(horzEdge, isTopOfScanbeam);
 | 
			
		||||
        //so far we're still in range of the horizontal Edge  but make sure
 | 
			
		||||
        //we're at the last of consec. horizontals when matching with eMaxPair
 | 
			
		||||
        if(e == eMaxPair && IsLastHorz)
 | 
			
		||||
        {
 | 
			
		||||
          if (dir == dLeftToRight)
 | 
			
		||||
            IntersectEdges(horzEdge, e, e->Top);
 | 
			
		||||
          else
 | 
			
		||||
            IntersectEdges(e, horzEdge, e->Top);
 | 
			
		||||
          if (eMaxPair->OutIdx >= 0) throw clipperException("ProcessHorizontal error");
 | 
			
		||||
 | 
			
		||||
          if (horzEdge->OutIdx >= 0)
 | 
			
		||||
          {
 | 
			
		||||
            OutPt* op1 = AddOutPt(horzEdge, horzEdge->Top);
 | 
			
		||||
            TEdge* eNextHorz = m_SortedEdges;
 | 
			
		||||
            while (eNextHorz)
 | 
			
		||||
            {
 | 
			
		||||
              if (eNextHorz->OutIdx >= 0 &&
 | 
			
		||||
                HorzSegmentsOverlap(horzEdge->Bot.X,
 | 
			
		||||
                horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X))
 | 
			
		||||
              {
 | 
			
		||||
                OutPt* op2 = AddOutPt(eNextHorz, eNextHorz->Bot);
 | 
			
		||||
                AddJoin(op2, op1, eNextHorz->Top);
 | 
			
		||||
              }
 | 
			
		||||
              eNextHorz = eNextHorz->NextInSEL;
 | 
			
		||||
            }
 | 
			
		||||
            AddGhostJoin(op1, horzEdge->Bot);
 | 
			
		||||
            AddLocalMaxPoly(horzEdge, eMaxPair, horzEdge->Top);
 | 
			
		||||
          }
 | 
			
		||||
          DeleteFromAEL(horzEdge);
 | 
			
		||||
          DeleteFromAEL(eMaxPair);
 | 
			
		||||
          return;
 | 
			
		||||
        }
 | 
			
		||||
        else if(dir == dLeftToRight)
 | 
			
		||||
        {
 | 
			
		||||
          IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y);
 | 
			
		||||
          IntersectEdges(horzEdge, e, Pt, true);
 | 
			
		||||
          IntersectEdges(horzEdge, e, Pt);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
          IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y);
 | 
			
		||||
          IntersectEdges( e, horzEdge, Pt, true);
 | 
			
		||||
          IntersectEdges( e, horzEdge, Pt);
 | 
			
		||||
        }
 | 
			
		||||
        SwapPositionsInAEL( horzEdge, e );
 | 
			
		||||
      }
 | 
			
		||||
| 
						 | 
				
			
			@ -2814,9 +2641,6 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam)
 | 
			
		|||
      e = eNext;
 | 
			
		||||
    } //end while
 | 
			
		||||
 | 
			
		||||
    if (horzEdge->OutIdx >= 0 && horzEdge->WindDelta != 0)
 | 
			
		||||
      PrepareHorzJoins(horzEdge, isTopOfScanbeam);
 | 
			
		||||
 | 
			
		||||
    if (horzEdge->NextInLML && IsHorizontal(*horzEdge->NextInLML))
 | 
			
		||||
    {
 | 
			
		||||
      UpdateEdgeIntoAEL(horzEdge);
 | 
			
		||||
| 
						 | 
				
			
			@ -2831,6 +2655,7 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam)
 | 
			
		|||
    if(horzEdge->OutIdx >= 0)
 | 
			
		||||
    {
 | 
			
		||||
      OutPt* op1 = AddOutPt( horzEdge, horzEdge->Top);
 | 
			
		||||
      if (isTopOfScanbeam) AddGhostJoin(op1, horzEdge->Bot);
 | 
			
		||||
      UpdateEdgeIntoAEL(horzEdge);
 | 
			
		||||
      if (horzEdge->WindDelta == 0) return;
 | 
			
		||||
      //nb: HorzEdge is no longer horizontal here
 | 
			
		||||
| 
						 | 
				
			
			@ -2856,22 +2681,7 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam)
 | 
			
		|||
    else
 | 
			
		||||
      UpdateEdgeIntoAEL(horzEdge); 
 | 
			
		||||
  }
 | 
			
		||||
  else if (eMaxPair)
 | 
			
		||||
  {
 | 
			
		||||
    if (eMaxPair->OutIdx >= 0)
 | 
			
		||||
    {
 | 
			
		||||
      if (dir == dLeftToRight)
 | 
			
		||||
        IntersectEdges(horzEdge, eMaxPair, horzEdge->Top); 
 | 
			
		||||
      else
 | 
			
		||||
        IntersectEdges(eMaxPair, horzEdge, horzEdge->Top);
 | 
			
		||||
      if (eMaxPair->OutIdx >= 0)
 | 
			
		||||
        throw clipperException("ProcessHorizontal error");
 | 
			
		||||
    } else
 | 
			
		||||
    {
 | 
			
		||||
      DeleteFromAEL(horzEdge);
 | 
			
		||||
      DeleteFromAEL(eMaxPair);
 | 
			
		||||
    }
 | 
			
		||||
  } else
 | 
			
		||||
  else
 | 
			
		||||
  {
 | 
			
		||||
    if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Top);
 | 
			
		||||
    DeleteFromAEL(horzEdge);
 | 
			
		||||
| 
						 | 
				
			
			@ -2958,16 +2768,7 @@ void Clipper::BuildIntersectList(const cInt botY, const cInt topY)
 | 
			
		|||
      IntPoint Pt;
 | 
			
		||||
      if(e->Curr.X > eNext->Curr.X)
 | 
			
		||||
      {
 | 
			
		||||
        if (!IntersectPoint(*e, *eNext, Pt, m_UseFullRange) && e->Curr.X > eNext->Curr.X +1)
 | 
			
		||||
          throw clipperException("Intersection error");
 | 
			
		||||
        if (Pt.Y > botY)
 | 
			
		||||
        {
 | 
			
		||||
            Pt.Y = botY;
 | 
			
		||||
            if (std::fabs(e->Dx) > std::fabs(eNext->Dx))
 | 
			
		||||
              Pt.X = TopX(*eNext, botY); else
 | 
			
		||||
              Pt.X = TopX(*e, botY);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        IntersectPoint(*e, *eNext, Pt);
 | 
			
		||||
        IntersectNode * newNode = new IntersectNode;
 | 
			
		||||
        newNode->Edge1 = e;
 | 
			
		||||
        newNode->Edge2 = eNext;
 | 
			
		||||
| 
						 | 
				
			
			@ -2995,7 +2796,7 @@ void Clipper::ProcessIntersectList()
 | 
			
		|||
  {
 | 
			
		||||
    IntersectNode* iNode = m_IntersectList[i];
 | 
			
		||||
    {
 | 
			
		||||
      IntersectEdges( iNode->Edge1, iNode->Edge2, iNode->Pt, true);
 | 
			
		||||
      IntersectEdges( iNode->Edge1, iNode->Edge2, iNode->Pt);
 | 
			
		||||
      SwapPositionsInAEL( iNode->Edge1 , iNode->Edge2 );
 | 
			
		||||
    }
 | 
			
		||||
    delete iNode;
 | 
			
		||||
| 
						 | 
				
			
			@ -3054,7 +2855,7 @@ void Clipper::DoMaxima(TEdge *e)
 | 
			
		|||
  TEdge* eNext = e->NextInAEL;
 | 
			
		||||
  while(eNext && eNext != eMaxPair)
 | 
			
		||||
  {
 | 
			
		||||
    IntersectEdges(e, eNext, e->Top, true);
 | 
			
		||||
    IntersectEdges(e, eNext, e->Top);
 | 
			
		||||
    SwapPositionsInAEL(e, eNext);
 | 
			
		||||
    eNext = e->NextInAEL;
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -3066,7 +2867,9 @@ void Clipper::DoMaxima(TEdge *e)
 | 
			
		|||
  }
 | 
			
		||||
  else if( e->OutIdx >= 0 && eMaxPair->OutIdx >= 0 )
 | 
			
		||||
  {
 | 
			
		||||
    IntersectEdges( e, eMaxPair, e->Top);
 | 
			
		||||
    if (e->OutIdx >= 0) AddLocalMaxPoly(e, eMaxPair, e->Top);
 | 
			
		||||
    DeleteFromAEL(e);
 | 
			
		||||
    DeleteFromAEL(eMaxPair);
 | 
			
		||||
  }
 | 
			
		||||
#ifdef use_lines
 | 
			
		||||
  else if (e->WindDelta == 0)
 | 
			
		||||
| 
						 | 
				
			
			@ -3134,9 +2937,13 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY)
 | 
			
		|||
        if ((e->OutIdx >= 0) && (e->WindDelta != 0) && ePrev && (ePrev->OutIdx >= 0) &&
 | 
			
		||||
          (ePrev->Curr.X == e->Curr.X) && (ePrev->WindDelta != 0))
 | 
			
		||||
        {
 | 
			
		||||
          OutPt* op = AddOutPt(ePrev, e->Curr);
 | 
			
		||||
          OutPt* op2 = AddOutPt(e, e->Curr);
 | 
			
		||||
          AddJoin(op, op2, e->Curr); //StrictlySimple (type-3) join
 | 
			
		||||
          IntPoint pt = e->Curr;
 | 
			
		||||
#ifdef use_xyz
 | 
			
		||||
          SetZ(pt, *ePrev, *e);
 | 
			
		||||
#endif
 | 
			
		||||
          OutPt* op = AddOutPt(ePrev, pt);
 | 
			
		||||
          OutPt* op2 = AddOutPt(e, pt);
 | 
			
		||||
          AddJoin(op, op2, pt); //StrictlySimple (type-3) join
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -3518,6 +3325,7 @@ bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2)
 | 
			
		|||
  (j->OffPt == j->OutPt2->Pt))
 | 
			
		||||
  {
 | 
			
		||||
    //Strictly Simple join ...
 | 
			
		||||
    if (outRec1 != outRec2) return false;
 | 
			
		||||
    op1b = j->OutPt1->Next;
 | 
			
		||||
    while (op1b != op1 && (op1b->Pt == j->OffPt)) 
 | 
			
		||||
      op1b = op1b->Next;
 | 
			
		||||
| 
						 | 
				
			
			@ -3858,8 +3666,7 @@ void ClipperOffset::AddPath(const Path& path, JoinType joinType, EndType endType
 | 
			
		|||
        (path[i].Y == newNode->Contour[k].Y &&
 | 
			
		||||
        path[i].X < newNode->Contour[k].X)) k = j;
 | 
			
		||||
    }
 | 
			
		||||
  if ((endType == etClosedPolygon && j < 2) || 
 | 
			
		||||
    (endType != etClosedPolygon && j < 0))
 | 
			
		||||
  if (endType == etClosedPolygon && j < 2)
 | 
			
		||||
  {
 | 
			
		||||
    delete newNode;
 | 
			
		||||
    return;
 | 
			
		||||
| 
						 | 
				
			
			@ -3869,7 +3676,7 @@ void ClipperOffset::AddPath(const Path& path, JoinType joinType, EndType endType
 | 
			
		|||
  //if this path's lowest pt is lower than all the others then update m_lowest
 | 
			
		||||
  if (endType != etClosedPolygon) return;
 | 
			
		||||
  if (m_lowest.X < 0)
 | 
			
		||||
    m_lowest = IntPoint(0, k);
 | 
			
		||||
    m_lowest = IntPoint(m_polyNodes.ChildCount() - 1, k);
 | 
			
		||||
  else
 | 
			
		||||
  {
 | 
			
		||||
    IntPoint ip = m_polyNodes.Childs[(int)m_lowest.X]->Contour[(int)m_lowest.Y];
 | 
			
		||||
| 
						 | 
				
			
			@ -4159,8 +3966,20 @@ void ClipperOffset::DoOffset(double delta)
 | 
			
		|||
 | 
			
		||||
void ClipperOffset::OffsetPoint(int j, int& k, JoinType jointype)
 | 
			
		||||
{
 | 
			
		||||
  //cross product ...
 | 
			
		||||
  m_sinA = (m_normals[k].X * m_normals[j].Y - m_normals[j].X * m_normals[k].Y);
 | 
			
		||||
  if (m_sinA < 0.00005 && m_sinA > -0.00005) return;
 | 
			
		||||
  if (std::fabs(m_sinA * m_delta) < 1.0) 
 | 
			
		||||
  {
 | 
			
		||||
    //dot product ...
 | 
			
		||||
    double cosA = (m_normals[k].X * m_normals[j].X + m_normals[j].Y * m_normals[k].Y ); 
 | 
			
		||||
    if (cosA > 0) // angle => 0 degrees
 | 
			
		||||
    {
 | 
			
		||||
      m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[k].X * m_delta),
 | 
			
		||||
        Round(m_srcPoly[j].Y + m_normals[k].Y * m_delta)));
 | 
			
		||||
      return; 
 | 
			
		||||
    }
 | 
			
		||||
    //else angle => 180 degrees   
 | 
			
		||||
  }
 | 
			
		||||
  else if (m_sinA > 1.0) m_sinA = 1.0;
 | 
			
		||||
  else if (m_sinA < -1.0) m_sinA = -1.0;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -4358,7 +4177,27 @@ double DistanceFromLineSqrd(
 | 
			
		|||
bool SlopesNearCollinear(const IntPoint& pt1, 
 | 
			
		||||
    const IntPoint& pt2, const IntPoint& pt3, double distSqrd)
 | 
			
		||||
{
 | 
			
		||||
  return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd;
 | 
			
		||||
  //this function is more accurate when the point that's geometrically
 | 
			
		||||
  //between the other 2 points is the one that's tested for distance.
 | 
			
		||||
  //ie makes it more likely to pick up 'spikes' ...
 | 
			
		||||
	if (std::abs(pt1.X - pt2.X) > std::abs(pt1.Y - pt2.Y))
 | 
			
		||||
	{
 | 
			
		||||
    if ((pt1.X > pt2.X) == (pt1.X < pt3.X))
 | 
			
		||||
      return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd;
 | 
			
		||||
    else if ((pt2.X > pt1.X) == (pt2.X < pt3.X))
 | 
			
		||||
      return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd;
 | 
			
		||||
		else
 | 
			
		||||
	    return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd;
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
    if ((pt1.Y > pt2.Y) == (pt1.Y < pt3.Y))
 | 
			
		||||
      return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd;
 | 
			
		||||
    else if ((pt2.Y > pt1.Y) == (pt2.Y < pt3.Y))
 | 
			
		||||
      return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd;
 | 
			
		||||
		else
 | 
			
		||||
      return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -4486,8 +4325,8 @@ void Minkowski(const Path& poly, const Path& path,
 | 
			
		|||
      pp.push_back(p);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  Paths quads; 
 | 
			
		||||
  quads.reserve((pathCnt + delta) * (polyCnt + 1));
 | 
			
		||||
  solution.clear();
 | 
			
		||||
  solution.reserve((pathCnt + delta) * (polyCnt + 1));
 | 
			
		||||
  for (size_t i = 0; i < pathCnt - 1 + delta; ++i)
 | 
			
		||||
    for (size_t j = 0; j < polyCnt; ++j)
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			@ -4498,23 +4337,30 @@ void Minkowski(const Path& poly, const Path& path,
 | 
			
		|||
      quad.push_back(pp[(i + 1) % pathCnt][(j + 1) % polyCnt]);
 | 
			
		||||
      quad.push_back(pp[i % pathCnt][(j + 1) % polyCnt]);
 | 
			
		||||
      if (!Orientation(quad)) ReversePath(quad);
 | 
			
		||||
      quads.push_back(quad);
 | 
			
		||||
      solution.push_back(quad);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  Clipper c;
 | 
			
		||||
  c.AddPaths(quads, ptSubject, true);
 | 
			
		||||
  c.Execute(ctUnion, solution, pftNonZero, pftNonZero);
 | 
			
		||||
}
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed)
 | 
			
		||||
{
 | 
			
		||||
  Minkowski(pattern, path, solution, true, pathIsClosed);
 | 
			
		||||
  Clipper c;
 | 
			
		||||
  c.AddPaths(solution, ptSubject, true);
 | 
			
		||||
  c.Execute(ctUnion, solution, pftNonZero, pftNonZero);
 | 
			
		||||
}
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, 
 | 
			
		||||
    PolyFillType pathFillType, bool pathIsClosed)
 | 
			
		||||
void TranslatePath(const Path& input, Path& output, IntPoint delta) 
 | 
			
		||||
{
 | 
			
		||||
  //precondition: input != output
 | 
			
		||||
  output.resize(input.size());
 | 
			
		||||
  for (size_t i = 0; i < input.size(); ++i)
 | 
			
		||||
    output[i] = IntPoint(input[i].X + delta.X, input[i].Y + delta.Y);
 | 
			
		||||
}
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed)
 | 
			
		||||
{
 | 
			
		||||
  Clipper c;
 | 
			
		||||
  for (size_t i = 0; i < paths.size(); ++i)
 | 
			
		||||
| 
						 | 
				
			
			@ -4522,15 +4368,23 @@ void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution,
 | 
			
		|||
    Paths tmp;
 | 
			
		||||
    Minkowski(pattern, paths[i], tmp, true, pathIsClosed);
 | 
			
		||||
    c.AddPaths(tmp, ptSubject, true);
 | 
			
		||||
    if (pathIsClosed)
 | 
			
		||||
    {
 | 
			
		||||
      Path tmp2;
 | 
			
		||||
      TranslatePath(paths[i], tmp2, pattern[0]);
 | 
			
		||||
      c.AddPath(tmp2, ptClip, true);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if (pathIsClosed) c.AddPaths(paths, ptClip, true);
 | 
			
		||||
  c.Execute(ctUnion, solution, pathFillType, pathFillType);
 | 
			
		||||
    c.Execute(ctUnion, solution, pftNonZero, pftNonZero);
 | 
			
		||||
}
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution)
 | 
			
		||||
{
 | 
			
		||||
  Minkowski(poly1, poly2, solution, false, true);
 | 
			
		||||
  Clipper c;
 | 
			
		||||
  c.AddPaths(solution, ptSubject, true);
 | 
			
		||||
  c.Execute(ctUnion, solution, pftNonZero, pftNonZero);
 | 
			
		||||
}
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,8 +1,8 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
*                                                                              *
 | 
			
		||||
* Author    :  Angus Johnson                                                   *
 | 
			
		||||
* Version   :  6.1.3a                                                          *
 | 
			
		||||
* Date      :  22 January 2014                                                 *
 | 
			
		||||
* Version   :  6.1.5                                                           *
 | 
			
		||||
* Date      :  22 May 2014                                                     *
 | 
			
		||||
* Website   :  http://www.angusj.com                                           *
 | 
			
		||||
* Copyright :  Angus Johnson 2010-2014                                         *
 | 
			
		||||
*                                                                              *
 | 
			
		||||
| 
						 | 
				
			
			@ -34,7 +34,7 @@
 | 
			
		|||
#ifndef clipper_hpp
 | 
			
		||||
#define clipper_hpp
 | 
			
		||||
 | 
			
		||||
#define CLIPPER_VERSION "6.1.3"
 | 
			
		||||
#define CLIPPER_VERSION "6.1.5"
 | 
			
		||||
 | 
			
		||||
//use_int32: When enabled 32bit ints are used instead of 64bit ints. This
 | 
			
		||||
//improve performance but coordinate values are limited to the range +/- 46340
 | 
			
		||||
| 
						 | 
				
			
			@ -69,11 +69,15 @@ enum PolyType { ptSubject, ptClip };
 | 
			
		|||
enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative };
 | 
			
		||||
 | 
			
		||||
#ifdef use_int32
 | 
			
		||||
typedef int cInt;
 | 
			
		||||
typedef unsigned int cUInt;
 | 
			
		||||
  typedef int cInt;
 | 
			
		||||
  static cInt const loRange = 46340;
 | 
			
		||||
  static cInt const hiRange = 46340;
 | 
			
		||||
#else
 | 
			
		||||
typedef signed long long cInt;
 | 
			
		||||
typedef unsigned long long cUInt;
 | 
			
		||||
  typedef signed long long cInt;
 | 
			
		||||
  typedef signed long long long64;     //used by Int128 class
 | 
			
		||||
  typedef unsigned long long ulong64;
 | 
			
		||||
  static cInt const loRange = 0x3FFFFFFF;
 | 
			
		||||
  static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
struct IntPoint {
 | 
			
		||||
| 
						 | 
				
			
			@ -117,7 +121,7 @@ struct DoublePoint
 | 
			
		|||
//------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
#ifdef use_xyz
 | 
			
		||||
typedef void (*TZFillCallback)(IntPoint& z1, IntPoint& z2, IntPoint& pt);
 | 
			
		||||
typedef void (*TZFillCallback)(IntPoint& e1bot, IntPoint& e1top, IntPoint& e2bot, IntPoint& e2top, IntPoint& pt);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
enum InitOptions {ioReverseSolution = 1, ioStrictlySimple = 2, ioPreserveCollinear = 4};
 | 
			
		||||
| 
						 | 
				
			
			@ -183,8 +187,7 @@ void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance = 1.
 | 
			
		|||
void CleanPolygons(Paths& polys, double distance = 1.415);
 | 
			
		||||
 | 
			
		||||
void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed);
 | 
			
		||||
void MinkowskiSum(const Path& pattern, const Paths& paths, 
 | 
			
		||||
  Paths& solution, PolyFillType pathFillType, bool pathIsClosed);
 | 
			
		||||
void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed);
 | 
			
		||||
void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution);
 | 
			
		||||
 | 
			
		||||
void PolyTreeToPaths(const PolyTree& polytree, Paths& paths);
 | 
			
		||||
| 
						 | 
				
			
			@ -308,15 +311,13 @@ private:
 | 
			
		|||
  bool IsTopHorz(const cInt XPos);
 | 
			
		||||
  void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2);
 | 
			
		||||
  void DoMaxima(TEdge *e);
 | 
			
		||||
  void PrepareHorzJoins(TEdge* horzEdge, bool isTopOfScanbeam);
 | 
			
		||||
  void ProcessHorizontals(bool IsTopOfScanbeam);
 | 
			
		||||
  void ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam);
 | 
			
		||||
  void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
 | 
			
		||||
  OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
 | 
			
		||||
  OutRec* GetOutRec(int idx);
 | 
			
		||||
  void AppendPolygon(TEdge *e1, TEdge *e2);
 | 
			
		||||
  void IntersectEdges(TEdge *e1, TEdge *e2,
 | 
			
		||||
    const IntPoint &pt, bool protect = false);
 | 
			
		||||
  void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt);
 | 
			
		||||
  OutRec* CreateOutRec();
 | 
			
		||||
  OutPt* AddOutPt(TEdge *e, const IntPoint &pt);
 | 
			
		||||
  void DisposeAllOutRecs();
 | 
			
		||||
| 
						 | 
				
			
			@ -344,7 +345,7 @@ private:
 | 
			
		|||
  void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec);
 | 
			
		||||
  void FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec);
 | 
			
		||||
#ifdef use_xyz
 | 
			
		||||
  void SetZ(IntPoint& pt, TEdge& e);
 | 
			
		||||
  void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2);
 | 
			
		||||
#endif
 | 
			
		||||
};
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,7 +4,7 @@ use strict;
 | 
			
		|||
use warnings;
 | 
			
		||||
 | 
			
		||||
use Slic3r::XS;
 | 
			
		||||
use Test::More tests => 10;
 | 
			
		||||
use Test::More tests => 15;
 | 
			
		||||
 | 
			
		||||
my $point = Slic3r::Point->new(10, 15);
 | 
			
		||||
is_deeply [ @$point ], [10, 15], 'point roundtrip';
 | 
			
		||||
| 
						 | 
				
			
			@ -46,4 +46,22 @@ ok !$point->coincides_with($point2), 'coincides_with';
 | 
			
		|||
    ok $p0->ccw($p1, $p2) < 0, 'ccw() does not overflow';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
    my $point = Slic3r::Point->new(15,15);
 | 
			
		||||
    my $line = Slic3r::Line->new([10,10], [20,10]);
 | 
			
		||||
    is_deeply $point->projection_onto_line($line)->pp, [15,10], 'project_onto_line';
 | 
			
		||||
    
 | 
			
		||||
    $point = Slic3r::Point->new(0, 15);
 | 
			
		||||
    is_deeply $point->projection_onto_line($line)->pp, [10,10], 'project_onto_line';
 | 
			
		||||
    
 | 
			
		||||
    $point = Slic3r::Point->new(25, 15);
 | 
			
		||||
    is_deeply $point->projection_onto_line($line)->pp, [20,10], 'project_onto_line';
 | 
			
		||||
    
 | 
			
		||||
    $point = Slic3r::Point->new(10,10);
 | 
			
		||||
    is_deeply $point->projection_onto_line($line)->pp, [10,10], 'project_onto_line';
 | 
			
		||||
    
 | 
			
		||||
    $point = Slic3r::Point->new(12, 10);
 | 
			
		||||
    is_deeply $point->projection_onto_line($line)->pp, [12,10], 'project_onto_line';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
__END__
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,7 +5,7 @@ use warnings;
 | 
			
		|||
 | 
			
		||||
use List::Util qw(first);
 | 
			
		||||
use Slic3r::XS;
 | 
			
		||||
use Test::More tests => 19;
 | 
			
		||||
use Test::More tests => 20;
 | 
			
		||||
 | 
			
		||||
use constant PI => 4 * atan2(1, 1);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -36,7 +36,7 @@ is_deeply [ map $_->pp, @$lines ], [
 | 
			
		|||
 | 
			
		||||
is_deeply $polygon->split_at_first_point->pp, [ @$square[0,1,2,3,0] ], 'split_at_first_point';
 | 
			
		||||
is_deeply $polygon->split_at_index(2)->pp, [ @$square[2,3,0,1,2] ], 'split_at_index';
 | 
			
		||||
is_deeply $polygon->split_at(Slic3r::Point->new(@{$square->[2]}))->pp, [ @$square[2,3,0,1,2] ], 'split_at';
 | 
			
		||||
is_deeply $polygon->split_at_vertex(Slic3r::Point->new(@{$square->[2]}))->pp, [ @$square[2,3,0,1,2] ], 'split_at';
 | 
			
		||||
is $polygon->area, 100*100, 'area';
 | 
			
		||||
 | 
			
		||||
ok $polygon->is_counter_clockwise, 'is_counter_clockwise';
 | 
			
		||||
| 
						 | 
				
			
			@ -69,6 +69,10 @@ ok $cw_polygon->contains_point(Slic3r::Point->new(150,150)), 'cw contains_point'
 | 
			
		|||
    ok !(defined first { $_->is_clockwise } @$triangles), 'all triangles are ccw';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
    is_deeply $polygon->centroid->pp, [150,150], 'centroid';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# this is not a test: this just demonstrates bad usage, where $polygon->clone gets
 | 
			
		||||
# DESTROY'ed before the derived object ($point), causing bad memory access
 | 
			
		||||
if (0) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,7 +5,7 @@ use warnings;
 | 
			
		|||
 | 
			
		||||
use List::Util qw(sum);
 | 
			
		||||
use Slic3r::XS;
 | 
			
		||||
use Test::More tests => 30;
 | 
			
		||||
use Test::More tests => 45;
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
    my $square = [
 | 
			
		||||
| 
						 | 
				
			
			@ -39,7 +39,7 @@ use Test::More tests => 30;
 | 
			
		|||
        is $path->role, Slic3r::ExtrusionPath::EXTR_ROLE_FILL, 'modify role';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $loop->split_at($square_p->[2]);
 | 
			
		||||
    $loop->split_at_vertex($square_p->[2]);
 | 
			
		||||
    is scalar(@$loop), 1, 'splitting a single-path loop results in a single path';
 | 
			
		||||
    is scalar(@{$loop->[0]->polyline}), 5, 'path has correct number of points';
 | 
			
		||||
    ok $loop->[0]->polyline->[0]->coincides_with($square_p->[2]), 'expected point order';
 | 
			
		||||
| 
						 | 
				
			
			@ -65,24 +65,58 @@ use Test::More tests => 30;
 | 
			
		|||
            mm3_per_mm  => 1,
 | 
			
		||||
        ),
 | 
			
		||||
    );
 | 
			
		||||
    is $loop->length, sum($polyline1->length, $polyline2->length), 'length';
 | 
			
		||||
    my $tot_len = sum($polyline1->length, $polyline2->length);
 | 
			
		||||
    is $loop->length, $tot_len, 'length';
 | 
			
		||||
    is scalar(@$loop), 2, 'loop contains two paths';
 | 
			
		||||
    $loop->split_at($polyline1->[1]);
 | 
			
		||||
    is $loop->length, sum($polyline1->length, $polyline2->length), 'length after splitting';
 | 
			
		||||
    is scalar(@$loop), 3, 'loop contains three paths after splitting';
 | 
			
		||||
    ok $loop->[0]->polyline->[0]->coincides_with($polyline1->[1]), 'expected starting point';
 | 
			
		||||
    ok $loop->[-1]->polyline->[-1]->coincides_with($polyline1->[1]), 'expected ending point';
 | 
			
		||||
    ok $loop->[0]->polyline->[-1]->coincides_with($loop->[1]->polyline->[0]), 'paths have common point';
 | 
			
		||||
    ok $loop->[1]->polyline->[-1]->coincides_with($loop->[2]->polyline->[0]), 'paths have common point';
 | 
			
		||||
    is $loop->[0]->role, Slic3r::ExtrusionPath::EXTR_ROLE_EXTERNAL_PERIMETER, 'expected order after splitting';
 | 
			
		||||
    is $loop->[1]->role, Slic3r::ExtrusionPath::EXTR_ROLE_OVERHANG_PERIMETER, 'expected order after splitting';
 | 
			
		||||
    is $loop->[2]->role, Slic3r::ExtrusionPath::EXTR_ROLE_EXTERNAL_PERIMETER, 'expected order after splitting';
 | 
			
		||||
    is scalar(@{$loop->[0]->polyline}), 2, 'path has correct number of points';
 | 
			
		||||
    is scalar(@{$loop->[1]->polyline}), 3, 'path has correct number of points';
 | 
			
		||||
    is scalar(@{$loop->[2]->polyline}), 2, 'path has correct number of points';
 | 
			
		||||
    
 | 
			
		||||
    my @paths = @{$loop->clip_end(3)};
 | 
			
		||||
    is sum(map $_->length, @paths), $loop->length - 3, 'returned paths have expected length';
 | 
			
		||||
    {
 | 
			
		||||
        # check splitting at intermediate point
 | 
			
		||||
        my $loop2 = $loop->clone;
 | 
			
		||||
        isa_ok $loop2, 'Slic3r::ExtrusionLoop';
 | 
			
		||||
        $loop2->split_at_vertex($polyline1->[1]);
 | 
			
		||||
        is $loop2->length, $tot_len, 'length after splitting is unchanged';
 | 
			
		||||
        is scalar(@$loop2), 3, 'loop contains three paths after splitting';
 | 
			
		||||
        ok $loop2->[0]->polyline->[0]->coincides_with($polyline1->[1]), 'expected starting point';
 | 
			
		||||
        ok $loop2->[-1]->polyline->[-1]->coincides_with($polyline1->[1]), 'expected ending point';
 | 
			
		||||
        ok $loop2->[0]->polyline->[-1]->coincides_with($loop2->[1]->polyline->[0]), 'paths have common point';
 | 
			
		||||
        ok $loop2->[1]->polyline->[-1]->coincides_with($loop2->[2]->polyline->[0]), 'paths have common point';
 | 
			
		||||
        is $loop2->[0]->role, Slic3r::ExtrusionPath::EXTR_ROLE_EXTERNAL_PERIMETER, 'expected order after splitting';
 | 
			
		||||
        is $loop2->[1]->role, Slic3r::ExtrusionPath::EXTR_ROLE_OVERHANG_PERIMETER, 'expected order after splitting';
 | 
			
		||||
        is $loop2->[2]->role, Slic3r::ExtrusionPath::EXTR_ROLE_EXTERNAL_PERIMETER, 'expected order after splitting';
 | 
			
		||||
        is scalar(@{$loop2->[0]->polyline}), 2, 'path has correct number of points';
 | 
			
		||||
        is scalar(@{$loop2->[1]->polyline}), 3, 'path has correct number of points';
 | 
			
		||||
        is scalar(@{$loop2->[2]->polyline}), 2, 'path has correct number of points';
 | 
			
		||||
    
 | 
			
		||||
        my @paths = @{$loop2->clip_end(3)};
 | 
			
		||||
        is sum(map $_->length, @paths), $loop2->length - 3, 'returned paths have expected length';
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    {
 | 
			
		||||
        # check splitting at endpoint
 | 
			
		||||
        my $loop2 = $loop->clone;
 | 
			
		||||
        $loop2->split_at_vertex($polyline2->[0]);
 | 
			
		||||
        is $loop2->length, $tot_len, 'length after splitting is unchanged';
 | 
			
		||||
        is scalar(@$loop2), 2, 'loop contains two paths after splitting';
 | 
			
		||||
        ok $loop2->[0]->polyline->[0]->coincides_with($polyline2->[0]), 'expected starting point';
 | 
			
		||||
        ok $loop2->[-1]->polyline->[-1]->coincides_with($polyline2->[0]), 'expected ending point';
 | 
			
		||||
        ok $loop2->[0]->polyline->[-1]->coincides_with($loop2->[1]->polyline->[0]), 'paths have common point';
 | 
			
		||||
        ok $loop2->[1]->polyline->[-1]->coincides_with($loop2->[0]->polyline->[0]), 'paths have common point';
 | 
			
		||||
        is $loop2->[0]->role, Slic3r::ExtrusionPath::EXTR_ROLE_OVERHANG_PERIMETER, 'expected order after splitting';
 | 
			
		||||
        is $loop2->[1]->role, Slic3r::ExtrusionPath::EXTR_ROLE_EXTERNAL_PERIMETER, 'expected order after splitting';
 | 
			
		||||
        is scalar(@{$loop2->[0]->polyline}), 3, 'path has correct number of points';
 | 
			
		||||
        is scalar(@{$loop2->[1]->polyline}), 3, 'path has correct number of points';
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    {
 | 
			
		||||
        my $loop2 = $loop->clone;
 | 
			
		||||
        my $point = Slic3r::Point->new(250,150);
 | 
			
		||||
        $loop2->split_at($point);
 | 
			
		||||
        is $loop2->length, $tot_len, 'length after splitting is unchanged';
 | 
			
		||||
        is scalar(@$loop2), 3, 'loop contains three paths after splitting';
 | 
			
		||||
        my $expected_start_point = Slic3r::Point->new(200,150);
 | 
			
		||||
        ok $loop2->[0]->polyline->[0]->coincides_with($expected_start_point), 'expected starting point';
 | 
			
		||||
        ok $loop2->[-1]->polyline->[-1]->coincides_with($expected_start_point), 'expected ending point';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
__END__
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,7 +4,7 @@ use strict;
 | 
			
		|||
use warnings;
 | 
			
		||||
 | 
			
		||||
use Slic3r::XS;
 | 
			
		||||
use Test::More tests => 10;
 | 
			
		||||
use Test::More tests => 14;
 | 
			
		||||
 | 
			
		||||
my $points = [
 | 
			
		||||
    [100, 100],
 | 
			
		||||
| 
						 | 
				
			
			@ -51,4 +51,16 @@ is_deeply $polyline->pp, [ @$points, @$points ], 'append_polyline';
 | 
			
		|||
    is $polyline->length, 100*2 + 50 + 50, 'extend_start';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
    my $polyline = Slic3r::Polyline->new(@$points);
 | 
			
		||||
    my $p1 = Slic3r::Polyline->new;
 | 
			
		||||
    my $p2 = Slic3r::Polyline->new;
 | 
			
		||||
    my $point = Slic3r::Point->new(150, 100);
 | 
			
		||||
    $polyline->split_at($point, $p1, $p2);
 | 
			
		||||
    is scalar(@$p1), 2, 'split_at';
 | 
			
		||||
    is scalar(@$p2), 3, 'split_at';
 | 
			
		||||
    ok $p1->last_point->coincides_with($point), 'split_at';
 | 
			
		||||
    ok $p2->first_point->coincides_with($point), 'split_at';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
__END__
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,7 +4,7 @@ use strict;
 | 
			
		|||
use warnings;
 | 
			
		||||
 | 
			
		||||
use Slic3r::XS;
 | 
			
		||||
use Test::More tests => 17;
 | 
			
		||||
use Test::More tests => 18;
 | 
			
		||||
 | 
			
		||||
my $square = Slic3r::Polygon->new(  # ccw
 | 
			
		||||
    [200, 100],
 | 
			
		||||
| 
						 | 
				
			
			@ -155,4 +155,27 @@ if (0) {  # Clipper does not preserve polyline orientation
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Disabled until Clipper bug #96 (our issue #2028) is fixed
 | 
			
		||||
if (0) {
 | 
			
		||||
    my $subject = Slic3r::Polyline->new(
 | 
			
		||||
        [44735000,31936670],[55270000,31936670],[55270000,25270000],[74730000,25270000],[74730000,44730000],[68063296,44730000],[68063296,55270000],[74730000,55270000],[74730000,74730000],[55270000,74730000],[55270000,68063296],[44730000,68063296],[44730000,74730000],[25270000,74730000],[25270000,55270000],[31936670,55270000],[31936670,44730000],[25270000,44730000],[25270000,25270000],[44730000,25270000],[44730000,31936670]
 | 
			
		||||
    );
 | 
			
		||||
    my $clip = [
 | 
			
		||||
        Slic3r::Polygon->new([75200000,45200000],[54800000,45200000],[54800000,24800000],[75200000,24800000]),
 | 
			
		||||
    ];
 | 
			
		||||
    my $result = Slic3r::Geometry::Clipper::intersection_pl([$subject], $clip);
 | 
			
		||||
    is scalar(@$result), 1, 'intersection_pl - result is not empty';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
    my $subject = Slic3r::Polygon->new(
 | 
			
		||||
        [44730000,31936670],[55270000,31936670],[55270000,25270000],[74730000,25270000],[74730000,44730000],[68063296,44730000],[68063296,55270000],[74730000,55270000],[74730000,74730000],[55270000,74730000],[55270000,68063296],[44730000,68063296],[44730000,74730000],[25270000,74730000],[25270000,55270000],[31936670,55270000],[31936670,44730000],[25270000,44730000],[25270000,25270000],[44730000,25270000]
 | 
			
		||||
    );
 | 
			
		||||
    my $clip = [
 | 
			
		||||
        Slic3r::Polygon->new([75200000,45200000],[54800000,45200000],[54800000,24800000],[75200000,24800000]),
 | 
			
		||||
    ];
 | 
			
		||||
    my $result = Slic3r::Geometry::Clipper::intersection_ppl([$subject], $clip);
 | 
			
		||||
    is scalar(@$result), 1, 'intersection_ppl - result is not empty';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
__END__
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,7 +4,7 @@ use strict;
 | 
			
		|||
use warnings;
 | 
			
		||||
 | 
			
		||||
use Slic3r::XS;
 | 
			
		||||
use Test::More tests => 112;
 | 
			
		||||
use Test::More tests => 115;
 | 
			
		||||
 | 
			
		||||
foreach my $config (Slic3r::Config->new, Slic3r::Config::Full->new) {
 | 
			
		||||
    $config->set('layer_height', 0.3);
 | 
			
		||||
| 
						 | 
				
			
			@ -101,6 +101,8 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Full->new) {
 | 
			
		|||
    is $config->serialize('wipe'), '1,0', 'serialize bools';
 | 
			
		||||
    $config->set_deserialize('wipe', '0,1,1');
 | 
			
		||||
    is_deeply $config->get('wipe'), [0,1,1], 'deserialize bools';
 | 
			
		||||
    $config->set_deserialize('wipe', '');
 | 
			
		||||
    is_deeply $config->get('wipe'), [], 'deserialize bools from empty string';
 | 
			
		||||
    $config->set_deserialize('retract_layer_change', 0);
 | 
			
		||||
    is_deeply $config->get('retract_layer_change'), [0], 'deserialize bools from non-string value';
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			@ -174,4 +176,12 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Full->new) {
 | 
			
		|||
    is $config->get('perimeter_extruder'), 3, 'defined extruder is not overwritten by default extruder';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
    my $config = Slic3r::Config->new;
 | 
			
		||||
    $config->set('spiral_vase', 1);
 | 
			
		||||
    $config->set('retract_layer_change', [1,0]);
 | 
			
		||||
    $config->normalize;
 | 
			
		||||
    is_deeply $config->get('retract_layer_change'), [0,0], 'retract_layer_change is disabled with spiral_vase';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
__END__
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										18
									
								
								xs/t/19_model.t
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								xs/t/19_model.t
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,18 @@
 | 
			
		|||
#!/usr/bin/perl
 | 
			
		||||
 | 
			
		||||
use strict;
 | 
			
		||||
use warnings;
 | 
			
		||||
 | 
			
		||||
use Slic3r::XS;
 | 
			
		||||
use Test::More tests => 3;
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
    my $model = Slic3r::Model->new;
 | 
			
		||||
    my $object = $model->_add_object;
 | 
			
		||||
    isa_ok $object, 'Slic3r::Model::Object::Ref';
 | 
			
		||||
    isa_ok $object->origin_translation, 'Slic3r::Pointf::Ref';
 | 
			
		||||
    $object->origin_translation->translate(10,0);
 | 
			
		||||
    is_deeply \@{$object->origin_translation}, [10,0], 'origin_translation is modified by ref';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
__END__
 | 
			
		||||
| 
						 | 
				
			
			@ -45,7 +45,6 @@
 | 
			
		|||
        %code{% THIS->apply(*other, true); %};
 | 
			
		||||
    std::vector<std::string> get_keys()
 | 
			
		||||
        %code{% THIS->keys(&RETVAL); %};
 | 
			
		||||
    std::string get_extrusion_axis();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
%name{Slic3r::Config::PrintRegion} class PrintRegionConfig {
 | 
			
		||||
| 
						 | 
				
			
			@ -103,10 +102,17 @@
 | 
			
		|||
    double get_abs_value(t_config_option_key opt_key);
 | 
			
		||||
    %name{get_abs_value_over}
 | 
			
		||||
        double get_abs_value(t_config_option_key opt_key, double ratio_over);
 | 
			
		||||
    void apply_print_config(PrintConfig* other)
 | 
			
		||||
        %code{% THIS->apply(*other, true); %};
 | 
			
		||||
    void apply_object_config(PrintObjectConfig* other)
 | 
			
		||||
        %code{% THIS->apply(*other, true); %};
 | 
			
		||||
    void apply_region_config(PrintRegionConfig* other)
 | 
			
		||||
        %code{% THIS->apply(*other, true); %};
 | 
			
		||||
    void apply_dynamic(DynamicPrintConfig* other)
 | 
			
		||||
        %code{% THIS->apply(*other, true); %};
 | 
			
		||||
    std::vector<std::string> get_keys()
 | 
			
		||||
        %code{% THIS->keys(&RETVAL); %};
 | 
			
		||||
    std::string get_extrusion_axis();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
%package{Slic3r::Config};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -20,6 +20,8 @@
 | 
			
		|||
    void append(ExtrusionPath* path)
 | 
			
		||||
        %code{% THIS->paths.push_back(*path); %};
 | 
			
		||||
    double length();
 | 
			
		||||
    void split_at_vertex(Point* point)
 | 
			
		||||
        %code{% THIS->split_at_vertex(*point); %};
 | 
			
		||||
    void split_at(Point* point)
 | 
			
		||||
        %code{% THIS->split_at(*point); %};
 | 
			
		||||
    ExtrusionPaths clip_end(double distance)
 | 
			
		||||
| 
						 | 
				
			
			@ -41,5 +43,31 @@ ExtrusionLoop::arrayref()
 | 
			
		|||
    OUTPUT:
 | 
			
		||||
        RETVAL
 | 
			
		||||
 | 
			
		||||
ExtrusionLoopRole
 | 
			
		||||
ExtrusionLoop::role(...)
 | 
			
		||||
    CODE:
 | 
			
		||||
        if (items > 1) {
 | 
			
		||||
            THIS->role = (ExtrusionLoopRole)SvUV(ST(1));
 | 
			
		||||
        }
 | 
			
		||||
        RETVAL = THIS->role;
 | 
			
		||||
    OUTPUT:
 | 
			
		||||
        RETVAL
 | 
			
		||||
 | 
			
		||||
%}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
%package{Slic3r::ExtrusionLoop};
 | 
			
		||||
%{
 | 
			
		||||
 | 
			
		||||
IV
 | 
			
		||||
_constant()
 | 
			
		||||
  ALIAS:
 | 
			
		||||
    EXTRL_ROLE_DEFAULT                      = elrDefault
 | 
			
		||||
    EXTRL_ROLE_EXTERNAL_PERIMETER           = elrExternalPerimeter
 | 
			
		||||
    EXTRL_ROLE_CONTOUR_INTERNAL_PERIMETER   = elrContourInternalPerimeter
 | 
			
		||||
  PROTOTYPE:
 | 
			
		||||
  CODE:
 | 
			
		||||
    RETVAL = ix;
 | 
			
		||||
  OUTPUT: RETVAL
 | 
			
		||||
 | 
			
		||||
%}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -39,9 +39,8 @@ _new(CLASS, polyline_sv, role, mm3_per_mm, width, height)
 | 
			
		|||
    float           width;
 | 
			
		||||
    float           height;
 | 
			
		||||
    CODE:
 | 
			
		||||
        RETVAL = new ExtrusionPath ();
 | 
			
		||||
        RETVAL = new ExtrusionPath (role);
 | 
			
		||||
        RETVAL->polyline.from_SV_check(polyline_sv);
 | 
			
		||||
        RETVAL->role            = role;
 | 
			
		||||
        RETVAL->mm3_per_mm      = mm3_per_mm;
 | 
			
		||||
        RETVAL->width           = width;
 | 
			
		||||
        RETVAL->height          = height;
 | 
			
		||||
| 
						 | 
				
			
			@ -135,15 +134,14 @@ _constant()
 | 
			
		|||
    EXTR_ROLE_PERIMETER                    = erPerimeter
 | 
			
		||||
    EXTR_ROLE_EXTERNAL_PERIMETER           = erExternalPerimeter
 | 
			
		||||
    EXTR_ROLE_OVERHANG_PERIMETER           = erOverhangPerimeter
 | 
			
		||||
    EXTR_ROLE_CONTOUR_INTERNAL_PERIMETER   = erContourInternalPerimeter
 | 
			
		||||
    EXTR_ROLE_FILL                         = erFill
 | 
			
		||||
    EXTR_ROLE_SOLIDFILL                    = erSolidFill
 | 
			
		||||
    EXTR_ROLE_TOPSOLIDFILL                 = erTopSolidFill
 | 
			
		||||
    EXTR_ROLE_BRIDGE                       = erBridge
 | 
			
		||||
    EXTR_ROLE_INTERNALBRIDGE               = erInternalBridge
 | 
			
		||||
    EXTR_ROLE_FILL                         = erInternalInfill
 | 
			
		||||
    EXTR_ROLE_SOLIDFILL                    = erSolidInfill
 | 
			
		||||
    EXTR_ROLE_TOPSOLIDFILL                 = erTopSolidInfill
 | 
			
		||||
    EXTR_ROLE_BRIDGE                       = erBridgeInfill
 | 
			
		||||
    EXTR_ROLE_GAPFILL                      = erGapFill
 | 
			
		||||
    EXTR_ROLE_SKIRT                        = erSkirt
 | 
			
		||||
    EXTR_ROLE_SUPPORTMATERIAL              = erSupportMaterial
 | 
			
		||||
    EXTR_ROLE_GAPFILL                      = erGapFill
 | 
			
		||||
    EXTR_ROLE_SUPPORTMATERIAL_INTERFACE    = erSupportMaterialInterface
 | 
			
		||||
  PROTOTYPE:
 | 
			
		||||
  CODE:
 | 
			
		||||
    RETVAL = ix;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -151,8 +151,8 @@ ModelMaterial::attributes()
 | 
			
		|||
    void set_layer_height_ranges(t_layer_height_ranges ranges)
 | 
			
		||||
        %code%{ THIS->layer_height_ranges = ranges; %};
 | 
			
		||||
 | 
			
		||||
    Clone<Pointf> origin_translation()
 | 
			
		||||
        %code%{ RETVAL = THIS->origin_translation; %};
 | 
			
		||||
    Ref<Pointf> origin_translation()
 | 
			
		||||
        %code%{ RETVAL = &THIS->origin_translation; %};
 | 
			
		||||
    void set_origin_translation(Pointf* point)
 | 
			
		||||
        %code%{ THIS->origin_translation = *point; %};
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -185,8 +185,8 @@ ModelMaterial::attributes()
 | 
			
		|||
        %code%{ RETVAL = THIS->rotation; %};
 | 
			
		||||
    double scaling_factor()
 | 
			
		||||
        %code%{ RETVAL = THIS->scaling_factor; %};
 | 
			
		||||
    Clone<Pointf> offset()
 | 
			
		||||
        %code%{ RETVAL = THIS->offset; %};
 | 
			
		||||
    Ref<Pointf> offset()
 | 
			
		||||
        %code%{ RETVAL = &THIS->offset; %};
 | 
			
		||||
 | 
			
		||||
    void set_rotation(double val)
 | 
			
		||||
        %code%{ THIS->rotation = val; %};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,6 +29,14 @@
 | 
			
		|||
        %code{% RETVAL = THIS->distance_to(*line); %};
 | 
			
		||||
    double ccw(Point* p1, Point* p2)
 | 
			
		||||
        %code{% RETVAL = THIS->ccw(*p1, *p2); %};
 | 
			
		||||
    Clone<Point> projection_onto_polygon(Polygon* polygon)
 | 
			
		||||
        %code{% RETVAL = new Point(THIS->projection_onto(*polygon)); %};
 | 
			
		||||
    Clone<Point> projection_onto_polyline(Polyline* polyline)
 | 
			
		||||
        %code{% RETVAL = new Point(THIS->projection_onto(*polyline)); %};
 | 
			
		||||
    Clone<Point> projection_onto_line(Line* line)
 | 
			
		||||
        %code{% RETVAL = new Point(THIS->projection_onto(*line)); %};
 | 
			
		||||
    Clone<Point> negative()
 | 
			
		||||
        %code{% RETVAL = new Point(THIS->negative()); %};
 | 
			
		||||
 | 
			
		||||
%{
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,6 +2,7 @@
 | 
			
		|||
 | 
			
		||||
%{
 | 
			
		||||
#include <myinit.h>
 | 
			
		||||
#include "BoundingBox.hpp"
 | 
			
		||||
#include "Polygon.hpp"
 | 
			
		||||
%}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -17,8 +18,8 @@
 | 
			
		|||
    void translate(double x, double y);
 | 
			
		||||
    void reverse();
 | 
			
		||||
    Lines lines();
 | 
			
		||||
    Polyline* split_at(Point* point)
 | 
			
		||||
        %code{% RETVAL = new Polyline(); THIS->split_at(*point, RETVAL); %};
 | 
			
		||||
    Polyline* split_at_vertex(Point* point)
 | 
			
		||||
        %code{% RETVAL = new Polyline(); THIS->split_at_vertex(*point, RETVAL); %};
 | 
			
		||||
    Polyline* split_at_index(int index)
 | 
			
		||||
        %code{% RETVAL = new Polyline(); THIS->split_at_index(index, RETVAL); %};
 | 
			
		||||
    Polyline* split_at_first_point()
 | 
			
		||||
| 
						 | 
				
			
			@ -38,6 +39,12 @@
 | 
			
		|||
    Polygons simplify(double tolerance);
 | 
			
		||||
    Polygons triangulate_convex()
 | 
			
		||||
        %code{% THIS->triangulate_convex(&RETVAL); %};
 | 
			
		||||
    Clone<Point> centroid();
 | 
			
		||||
    BoundingBox* bounding_box()
 | 
			
		||||
        %code{%
 | 
			
		||||
            RETVAL = new BoundingBox();
 | 
			
		||||
            THIS->bounding_box(RETVAL);
 | 
			
		||||
        %};
 | 
			
		||||
%{
 | 
			
		||||
 | 
			
		||||
Polygon*
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,6 +2,7 @@
 | 
			
		|||
 | 
			
		||||
%{
 | 
			
		||||
#include <myinit.h>
 | 
			
		||||
#include "BoundingBox.hpp"
 | 
			
		||||
#include "ClipperUtils.hpp"
 | 
			
		||||
#include "Polyline.hpp"
 | 
			
		||||
%}
 | 
			
		||||
| 
						 | 
				
			
			@ -31,6 +32,8 @@
 | 
			
		|||
    void extend_end(double distance);
 | 
			
		||||
    void extend_start(double distance);
 | 
			
		||||
    void simplify(double tolerance);
 | 
			
		||||
    void split_at(Point* point, Polyline* p1, Polyline* p2)
 | 
			
		||||
        %code{% THIS->split_at(*point, p1, p2); %};
 | 
			
		||||
%{
 | 
			
		||||
 | 
			
		||||
Polyline*
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -114,6 +114,7 @@ MotionPlanner*             O_OBJECT_SLIC3R
 | 
			
		|||
Ref<MotionPlanner>         O_OBJECT_SLIC3R_T
 | 
			
		||||
Clone<MotionPlanner>       O_OBJECT_SLIC3R_T
 | 
			
		||||
 | 
			
		||||
ExtrusionLoopRole     T_UV
 | 
			
		||||
ExtrusionRole     T_UV
 | 
			
		||||
FlowRole     T_UV
 | 
			
		||||
PrintStep     T_UV
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -115,6 +115,12 @@
 | 
			
		|||
    $CVar = (SurfaceType)SvUV($PerlVar);
 | 
			
		||||
  %};
 | 
			
		||||
};
 | 
			
		||||
%typemap{ExtrusionLoopRole}{parsed}{
 | 
			
		||||
  %cpp_type{ExtrusionLoopRole};
 | 
			
		||||
  %precall_code{%
 | 
			
		||||
    $CVar = (ExtrusionLoopRole)SvUV($PerlVar);
 | 
			
		||||
  %};
 | 
			
		||||
};
 | 
			
		||||
%typemap{ExtrusionRole}{parsed}{
 | 
			
		||||
  %cpp_type{ExtrusionRole};
 | 
			
		||||
  %precall_code{%
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue