mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-11-02 20:51:23 -07:00 
			
		
		
		
	Merge branch 'master' into dynamic-flow
Conflicts: lib/Slic3r/ExPolygon.pm
This commit is contained in:
		
						commit
						1cedb00f20
					
				
					 34 changed files with 529 additions and 260 deletions
				
			
		
							
								
								
									
										1
									
								
								MANIFEST
									
										
									
									
									
								
							
							
						
						
									
										1
									
								
								MANIFEST
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -35,6 +35,7 @@ lib/Slic3r/GUI/SkeinPanel.pm
 | 
			
		|||
lib/Slic3r/GUI/Tab.pm
 | 
			
		||||
lib/Slic3r/Layer.pm
 | 
			
		||||
lib/Slic3r/Line.pm
 | 
			
		||||
lib/Slic3r/Model.pm
 | 
			
		||||
lib/Slic3r/Point.pm
 | 
			
		||||
lib/Slic3r/Polygon.pm
 | 
			
		||||
lib/Slic3r/Polyline.pm
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,8 +9,8 @@ A: Yes.
 | 
			
		|||
Slic3r is a G-code generator for 3D printers. It's compatible with RepRaps,
 | 
			
		||||
Makerbots, Ultimakers and many more machines.
 | 
			
		||||
 | 
			
		||||
See the [project homepage](http://slic3r.org/) at slic3r.org
 | 
			
		||||
for more information.
 | 
			
		||||
See the [project homepage](http://slic3r.org/) at slic3r.org and the
 | 
			
		||||
[documentation](https://github.com/alexrj/Slic3r/wiki/Documentation) on the Slic3r wiki for more information.
 | 
			
		||||
 | 
			
		||||
## What language is it written in?
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -170,6 +170,9 @@ The author of the Silk icon set is Mark James.
 | 
			
		|||
        --layer-gcode       Load layer-change G-code from the supplied file (default: nothing).
 | 
			
		||||
        --extra-perimeters  Add more perimeters when needed (default: yes)
 | 
			
		||||
        --randomize-start   Randomize starting point across layers (default: yes)
 | 
			
		||||
        --only-retract-when-crossing-perimeters
 | 
			
		||||
                            Disable retraction when travelling between infill paths inside the same island.
 | 
			
		||||
                            (default: no)
 | 
			
		||||
        --solid-infill-below-area
 | 
			
		||||
                            Force solid infill when a region has a smaller area than this threshold
 | 
			
		||||
                            (mm^2, default: 70)
 | 
			
		||||
| 
						 | 
				
			
			@ -272,38 +275,3 @@ If you want to change a preset file, just do
 | 
			
		|||
If you want to slice a file overriding an option contained in your preset file:
 | 
			
		||||
 | 
			
		||||
    slic3r.pl --load config.ini --layer-height 0.25 file.stl
 | 
			
		||||
 | 
			
		||||
## How can I integrate Slic3r with Pronterface?
 | 
			
		||||
 | 
			
		||||
Put this into *slicecommand*:
 | 
			
		||||
 | 
			
		||||
    slic3r.pl $s --load config.ini --output $o
 | 
			
		||||
 | 
			
		||||
And this into *sliceoptscommand*:
 | 
			
		||||
 | 
			
		||||
    slic3r.pl --load config.ini --ignore-nonexistent-config
 | 
			
		||||
 | 
			
		||||
Replace `slic3r.pl` with the full path to the slic3r executable and `config.ini`
 | 
			
		||||
with the full path of your config file (put it in your home directory or where
 | 
			
		||||
you like).
 | 
			
		||||
On Mac, the executable has a path like this:
 | 
			
		||||
 | 
			
		||||
    /Applications/Slic3r.app/Contents/MacOS/slic3r
 | 
			
		||||
 | 
			
		||||
## How can I specify a custom filename format for output G-code files?
 | 
			
		||||
 | 
			
		||||
You can specify a filename format by using any of the config options. 
 | 
			
		||||
Just enclose them in square brackets, and Slic3r will replace them upon
 | 
			
		||||
exporting.
 | 
			
		||||
The additional `[input_filename]` and `[input_filename_base]` options will
 | 
			
		||||
be replaced by the input file name (in the second case, the .stl extension 
 | 
			
		||||
is stripped).
 | 
			
		||||
 | 
			
		||||
The default format is `[input_filename_base].gcode`, meaning that if you slice
 | 
			
		||||
a *foo.stl* file, the output will be saved to *foo.gcode*.
 | 
			
		||||
 | 
			
		||||
See below for more complex examples:
 | 
			
		||||
 | 
			
		||||
    [input_filename_base]_h[layer_height]_p[perimeters]_s[solid_layers].gcode
 | 
			
		||||
    [input_filename]_center[print_center]_[layer_height]layers.gcode
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,7 +7,7 @@ use strict;
 | 
			
		|||
use warnings;
 | 
			
		||||
require v5.10;
 | 
			
		||||
 | 
			
		||||
our $VERSION = "0.9.2-dev";
 | 
			
		||||
our $VERSION = "0.9.3-dev";
 | 
			
		||||
 | 
			
		||||
our $debug = 0;
 | 
			
		||||
sub debugf {
 | 
			
		||||
| 
						 | 
				
			
			@ -21,6 +21,9 @@ BEGIN {
 | 
			
		|||
    $have_threads = $Config{useithreads} && eval "use threads; use Thread::Queue; 1";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
warn "Running Slic3r under Perl >= 5.16 is not supported nor recommended\n"
 | 
			
		||||
    if $^V >= v5.16;
 | 
			
		||||
 | 
			
		||||
use FindBin;
 | 
			
		||||
our $var = "$FindBin::Bin/var";
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -41,6 +44,7 @@ use Slic3r::GCode;
 | 
			
		|||
use Slic3r::Geometry qw(PI);
 | 
			
		||||
use Slic3r::Layer;
 | 
			
		||||
use Slic3r::Line;
 | 
			
		||||
use Slic3r::Model;
 | 
			
		||||
use Slic3r::Point;
 | 
			
		||||
use Slic3r::Polygon;
 | 
			
		||||
use Slic3r::Polyline;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -178,6 +178,7 @@ our $Options = {
 | 
			
		|||
    # extruder mapping
 | 
			
		||||
    'perimeter_extruder' => {
 | 
			
		||||
        label   => 'Perimeter extruder',
 | 
			
		||||
        tooltip => 'The extruder to use when printing perimeters.',
 | 
			
		||||
        cli     => 'perimeter-extruder=i',
 | 
			
		||||
        type    => 'i',
 | 
			
		||||
        aliases => [qw(perimeters_extruder)],
 | 
			
		||||
| 
						 | 
				
			
			@ -185,12 +186,14 @@ our $Options = {
 | 
			
		|||
    },
 | 
			
		||||
    'infill_extruder' => {
 | 
			
		||||
        label   => 'Infill extruder',
 | 
			
		||||
        tooltip => 'The extruder to use when printing infill.',
 | 
			
		||||
        cli     => 'infill-extruder=i',
 | 
			
		||||
        type    => 'i',
 | 
			
		||||
        default => 1,
 | 
			
		||||
    },
 | 
			
		||||
    'support_material_extruder' => {
 | 
			
		||||
        label   => 'Support material extruder',
 | 
			
		||||
        tooltip => 'The extruder to use when printing support material. This affects brim too.',
 | 
			
		||||
        cli     => 'support-material-extruder=i',
 | 
			
		||||
        type    => 'i',
 | 
			
		||||
        default => 1,
 | 
			
		||||
| 
						 | 
				
			
			@ -470,6 +473,13 @@ our $Options = {
 | 
			
		|||
        type    => 'bool',
 | 
			
		||||
        default => 1,
 | 
			
		||||
    },
 | 
			
		||||
    'only_retract_when_crossing_perimeters' => {
 | 
			
		||||
        label   => 'Only retract when crossing perimeters',
 | 
			
		||||
        tooltip => 'Disables retraction when travelling between infill paths inside the same island.',
 | 
			
		||||
        cli     => 'only-retract-when-crossing-perimeters!',
 | 
			
		||||
        type    => 'bool',
 | 
			
		||||
        default => 0,
 | 
			
		||||
    },
 | 
			
		||||
    'support_material' => {
 | 
			
		||||
        label   => 'Generate support material',
 | 
			
		||||
        tooltip => 'Enable support material generation.',
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -61,13 +61,12 @@ sub boost_polygon {
 | 
			
		|||
 | 
			
		||||
sub offset {
 | 
			
		||||
    my $self = shift;
 | 
			
		||||
    my ($distance, $scale, $joinType, $miterLimit) = @_;
 | 
			
		||||
    $scale      ||= &Slic3r::SCALING_FACTOR * 1000000;
 | 
			
		||||
    $joinType   = JT_MITER if !defined $joinType;
 | 
			
		||||
    $miterLimit ||= 2;
 | 
			
		||||
    
 | 
			
		||||
    my $offsets = Math::Clipper::offset($self, $distance, $scale, $joinType, $miterLimit);
 | 
			
		||||
    return @$offsets;
 | 
			
		||||
    return Slic3r::Geometry::Clipper::offset($self, @_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub offset_ex {
 | 
			
		||||
    my $self = shift;
 | 
			
		||||
    return Slic3r::Geometry::Clipper::offset_ex($self, @_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub safety_offset {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -170,7 +170,7 @@ sub make_fill {
 | 
			
		|||
                            ? ($surface->surface_type == S_TYPE_TOP ? EXTR_ROLE_TOPSOLIDFILL : EXTR_ROLE_SOLIDFILL)
 | 
			
		||||
                            : EXTR_ROLE_FILL),
 | 
			
		||||
                    depth_layers => $surface->depth_layers,
 | 
			
		||||
                    flow_spacing => $params->{flow_spacing} || $flow_spacing,
 | 
			
		||||
                    flow_spacing => $params->{flow_spacing} || (warn "Warning: no flow_spacing was returned by the infill engine, please report this to the developer\n"),
 | 
			
		||||
                ), @paths,
 | 
			
		||||
            ],
 | 
			
		||||
        );
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,7 +18,7 @@ sub fill_surface {
 | 
			
		|||
    my $min_spacing = scale $params{flow_spacing};
 | 
			
		||||
    my $distance = $min_spacing / $params{density};
 | 
			
		||||
    
 | 
			
		||||
    my $flow_spacing;
 | 
			
		||||
    my $flow_spacing = $params{flow_spacing};
 | 
			
		||||
    if ($params{density} == 1) {
 | 
			
		||||
        $distance = $self->adjust_solid_spacing(
 | 
			
		||||
            width       => $bounding_box->[X2] - $bounding_box->[X1],
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -94,7 +94,8 @@ sub fill_surface {
 | 
			
		|||
        paths => [ map Slic3r::ExtrusionPath->pack(polyline => $_, role => -1), @paths ],
 | 
			
		||||
    );
 | 
			
		||||
    
 | 
			
		||||
    return {}, map $_->polyline, $collection->shortest_path;
 | 
			
		||||
    return { flow_spacing => $params{flow_spacing} },
 | 
			
		||||
        map $_->polyline, $collection->shortest_path;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
1;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -60,7 +60,7 @@ sub fill_surface {
 | 
			
		|||
    # paths must be rotated back
 | 
			
		||||
    $self->rotate_points_back(\@paths, $rotate_vector);
 | 
			
		||||
    
 | 
			
		||||
    return {}, @paths;
 | 
			
		||||
    return { flow_spacing => $params{flow_spacing} }, @paths;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
1;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,7 +3,7 @@ use Moo;
 | 
			
		|||
 | 
			
		||||
extends 'Slic3r::Fill::Base';
 | 
			
		||||
 | 
			
		||||
use Slic3r::Geometry qw(X1 Y1 X2 Y2 A B X Y scale unscale epsilon);
 | 
			
		||||
use Slic3r::Geometry qw(X1 Y1 X2 Y2 A B X Y scale unscale scaled_epsilon);
 | 
			
		||||
 | 
			
		||||
sub fill_surface {
 | 
			
		||||
    my $self = shift;
 | 
			
		||||
| 
						 | 
				
			
			@ -22,7 +22,7 @@ sub fill_surface {
 | 
			
		|||
    my $distance_between_lines = $min_spacing / $params{density};
 | 
			
		||||
    my $line_oscillation = $distance_between_lines - $min_spacing;
 | 
			
		||||
    
 | 
			
		||||
    my $flow_spacing;
 | 
			
		||||
    my $flow_spacing = $params{flow_spacing};
 | 
			
		||||
    if ($params{density} == 1) {
 | 
			
		||||
        $distance_between_lines = $self->adjust_solid_spacing(
 | 
			
		||||
            width       => $bounding_box->[X2] - $bounding_box->[X1],
 | 
			
		||||
| 
						 | 
				
			
			@ -36,7 +36,7 @@ sub fill_surface {
 | 
			
		|||
    my $x = $bounding_box->[X1];
 | 
			
		||||
    my $is_line_pattern = $self->isa('Slic3r::Fill::Line');
 | 
			
		||||
    my @vertical_lines = ();
 | 
			
		||||
    for (my $i = 0; $x <= $bounding_box->[X2] + scale epsilon; $i++) {
 | 
			
		||||
    for (my $i = 0; $x <= $bounding_box->[X2] + scaled_epsilon; $i++) {
 | 
			
		||||
        my $vertical_line = Slic3r::Line->new([$x, $bounding_box->[Y2]], [$x, $bounding_box->[Y1]]);
 | 
			
		||||
        if ($is_line_pattern && $i % 2) {
 | 
			
		||||
            $vertical_line->[A][X] += $line_oscillation;
 | 
			
		||||
| 
						 | 
				
			
			@ -49,7 +49,7 @@ sub fill_surface {
 | 
			
		|||
    # clip paths against a slightly offsetted expolygon, so that the first and last paths
 | 
			
		||||
    # are kept even if the expolygon has vertical sides
 | 
			
		||||
    my @paths = @{ Boost::Geometry::Utils::polygon_linestring_intersection(
 | 
			
		||||
        +($expolygon->offset_ex(scale epsilon))[0]->boost_polygon,  # TODO: we should use all the resulting expolygons and clip the linestrings to a multipolygon object
 | 
			
		||||
        +($expolygon->offset_ex(scaled_epsilon))[0]->boost_polygon,  # TODO: we should use all the resulting expolygons and clip the linestrings to a multipolygon object
 | 
			
		||||
        Boost::Geometry::Utils::linestring(@vertical_lines),
 | 
			
		||||
    ) };
 | 
			
		||||
    for (@paths) {
 | 
			
		||||
| 
						 | 
				
			
			@ -64,7 +64,7 @@ sub fill_surface {
 | 
			
		|||
        );
 | 
			
		||||
        @paths = ();
 | 
			
		||||
        
 | 
			
		||||
        my $tolerance = 10 * scale epsilon;
 | 
			
		||||
        my $tolerance = 10 * scaled_epsilon;
 | 
			
		||||
        my $diagonal_distance = $distance_between_lines * 5;
 | 
			
		||||
        my $can_connect = $is_line_pattern
 | 
			
		||||
            ? sub {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,28 +12,18 @@ sub read_file {
 | 
			
		|||
    
 | 
			
		||||
    open my $fh, '<', $file or die "Failed to open $file\n";
 | 
			
		||||
    
 | 
			
		||||
    my $vertices = [];
 | 
			
		||||
    my $materials = {};
 | 
			
		||||
    my $meshes_by_material = {};
 | 
			
		||||
    my $model = Slic3r::Model->new;
 | 
			
		||||
    XML::SAX::PurePerl
 | 
			
		||||
        ->new(Handler => Slic3r::Format::AMF::Parser->new(
 | 
			
		||||
            _vertices           => $vertices,
 | 
			
		||||
            _materials          => $materials,
 | 
			
		||||
            _meshes_by_material => $meshes_by_material,
 | 
			
		||||
         ))
 | 
			
		||||
        ->new(Handler => Slic3r::Format::AMF::Parser->new(_model => $model))
 | 
			
		||||
        ->parse_file($fh);
 | 
			
		||||
    
 | 
			
		||||
    close $fh;
 | 
			
		||||
    
 | 
			
		||||
    $_ = Slic3r::TriangleMesh->new(vertices => $vertices, facets => $_)
 | 
			
		||||
        for values %$meshes_by_material;
 | 
			
		||||
    
 | 
			
		||||
    return $materials, $meshes_by_material;
 | 
			
		||||
    return $model;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub write_file {
 | 
			
		||||
    my $self = shift;
 | 
			
		||||
    my ($file, $materials, $meshes_by_material) = @_;
 | 
			
		||||
    my ($file, $model, %params) = @_;
 | 
			
		||||
    
 | 
			
		||||
    my %vertices_offset = ();
 | 
			
		||||
    
 | 
			
		||||
| 
						 | 
				
			
			@ -42,20 +32,21 @@ sub write_file {
 | 
			
		|||
    printf $fh qq{<?xml version="1.0" encoding="UTF-8"?>\n};
 | 
			
		||||
    printf $fh qq{<amf unit="millimeter">\n};
 | 
			
		||||
    printf $fh qq{  <metadata type="cad">Slic3r %s</metadata>\n}, $Slic3r::VERSION;
 | 
			
		||||
    foreach my $material_id (keys %$materials) {
 | 
			
		||||
        printf $fh qq{  <material id="%s">\n}, $material_id;
 | 
			
		||||
        for (keys %{$materials->{$material_id}}) {
 | 
			
		||||
             printf $fh qq{    <metadata type=\"%s\">%s</metadata>\n}, $_, $materials->{$material_id}{$_};
 | 
			
		||||
    for my $material_id (sort keys %{ $model->materials }) {
 | 
			
		||||
        my $material = $model->materials->{$material_id};
 | 
			
		||||
        printf $fh qq{  <material id="%d">\n}, $material_id;
 | 
			
		||||
        for (keys %$material) {
 | 
			
		||||
             printf $fh qq{    <metadata type=\"%s\">%s</metadata>\n}, $_, $material->{$_};
 | 
			
		||||
        }
 | 
			
		||||
        printf $fh qq{  </material>\n};
 | 
			
		||||
    }
 | 
			
		||||
    printf $fh qq{  <object id="0">\n};
 | 
			
		||||
    printf $fh qq{    <mesh>\n};
 | 
			
		||||
    printf $fh qq{      <vertices>\n};
 | 
			
		||||
    my $vertices_count = 0;
 | 
			
		||||
    foreach my $mesh (values %$meshes_by_material) {
 | 
			
		||||
        $vertices_offset{$mesh} = $vertices_count;
 | 
			
		||||
        foreach my $vertex (@{$mesh->vertices}, ) {
 | 
			
		||||
    my $instances = '';
 | 
			
		||||
    for my $object_id (0 .. $#{ $model->objects }) {
 | 
			
		||||
        my $object = $model->objects->[$object_id];
 | 
			
		||||
        printf $fh qq{  <object id="%d">\n}, $object_id;
 | 
			
		||||
        printf $fh qq{    <mesh>\n};
 | 
			
		||||
        printf $fh qq{      <vertices>\n};
 | 
			
		||||
        foreach my $vertex (@{$object->vertices}, ) {
 | 
			
		||||
            printf $fh qq{        <vertex>\n};
 | 
			
		||||
            printf $fh qq{          <coordinates>\n};
 | 
			
		||||
            printf $fh qq{            <x>%s</x>\n}, $vertex->[X];
 | 
			
		||||
| 
						 | 
				
			
			@ -63,24 +54,35 @@ sub write_file {
 | 
			
		|||
            printf $fh qq{            <z>%s</z>\n}, $vertex->[Z];
 | 
			
		||||
            printf $fh qq{          </coordinates>\n};
 | 
			
		||||
            printf $fh qq{        </vertex>\n};
 | 
			
		||||
            $vertices_count++;
 | 
			
		||||
        }
 | 
			
		||||
        printf $fh qq{      </vertices>\n};
 | 
			
		||||
        foreach my $volume (@{ $object->volumes }) {
 | 
			
		||||
            printf $fh qq{      <volume%s>\n},
 | 
			
		||||
                (!defined $volume->material_id) ? '' : (sprintf ' materialid="%s"', $volume->material_id);
 | 
			
		||||
            foreach my $facet (@{$volume->facets}) {
 | 
			
		||||
                printf $fh qq{        <triangle>\n};
 | 
			
		||||
                printf $fh qq{          <v%d>%d</v%d>\n}, (4+$_), $facet->[$_], (4+$_) for -3..-1;
 | 
			
		||||
                printf $fh qq{        </triangle>\n};
 | 
			
		||||
            }
 | 
			
		||||
            printf $fh qq{      </volume>\n};
 | 
			
		||||
        }
 | 
			
		||||
        printf $fh qq{    </mesh>\n};
 | 
			
		||||
        printf $fh qq{  </object>\n};
 | 
			
		||||
        if ($object->instances) {
 | 
			
		||||
            foreach my $instance (@{$object->instances}) {
 | 
			
		||||
                $instances .= sprintf qq{    <instance objectid="%d">\n}, $object_id;
 | 
			
		||||
                $instances .= sprintf qq{      <deltax>%s</deltax>\n}, $instance->offset->[X];
 | 
			
		||||
                $instances .= sprintf qq{      <deltax>%s</deltax>\n}, $instance->offset->[Y];
 | 
			
		||||
                $instances .= sprintf qq{      <rz>%s</rz>\n}, $instance->rotation;
 | 
			
		||||
                $instances .= sprintf qq{    </instance>\n};
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    printf $fh qq{      </vertices>\n};
 | 
			
		||||
    foreach my $material_id (sort keys %$meshes_by_material) {
 | 
			
		||||
        my $mesh = $meshes_by_material->{$material_id};
 | 
			
		||||
        printf $fh qq{      <volume%s>\n},
 | 
			
		||||
            ($material_id eq '_') ? '' : " materialid=\"$material_id\"";
 | 
			
		||||
        foreach my $facet (@{$mesh->facets}) {
 | 
			
		||||
            printf $fh qq{        <triangle>\n};
 | 
			
		||||
            printf $fh qq{          <v%d>%d</v%d>\n}, $_, $facet->[$_] + $vertices_offset{$mesh}, $_
 | 
			
		||||
                for -3..-1;
 | 
			
		||||
            printf $fh qq{        </triangle>\n};
 | 
			
		||||
        }
 | 
			
		||||
        printf $fh qq{      </volume>\n};
 | 
			
		||||
    if ($instances) {
 | 
			
		||||
        printf $fh qq{  <constellation id="1">\n};
 | 
			
		||||
        printf $fh $instances;
 | 
			
		||||
        printf $fh qq{  </constellation>\n};
 | 
			
		||||
    }
 | 
			
		||||
    printf $fh qq{    </mesh>\n};
 | 
			
		||||
    printf $fh qq{  </object>\n};
 | 
			
		||||
    printf $fh qq{</amf>\n};
 | 
			
		||||
    close $fh;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,6 +11,8 @@ my %xyz_index = (x => 0, y => 1, z => 2); #=
 | 
			
		|||
sub new {
 | 
			
		||||
    my $self = shift->SUPER::new(@_);
 | 
			
		||||
    $self->{_tree} = [];
 | 
			
		||||
    $self->{_objects_map} = {};  # this hash maps AMF object IDs to object indexes in $model->objects
 | 
			
		||||
    $self->{_instances} = {};   # apply these lazily to make sure all objects have been parsed
 | 
			
		||||
    $self;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -18,23 +20,35 @@ sub start_element {
 | 
			
		|||
    my $self = shift;
 | 
			
		||||
    my $data = shift;
 | 
			
		||||
    
 | 
			
		||||
    if ($data->{LocalName} eq 'vertex') {
 | 
			
		||||
    if ($data->{LocalName} eq 'object') {
 | 
			
		||||
        $self->{_object} = $self->{_model}->add_object;
 | 
			
		||||
        $self->{_objects_map}{ $self->_get_attribute($data, 'id') } = $#{ $self->{_model}->objects };
 | 
			
		||||
    } elsif ($data->{LocalName} eq 'vertex') {
 | 
			
		||||
        $self->{_vertex} = ["", "", ""];
 | 
			
		||||
    } elsif ($self->{_vertex} && $data->{LocalName} =~ /^[xyz]$/ && $self->{_tree}[-1] eq 'coordinates') {
 | 
			
		||||
        $self->{_coordinate} = $data->{LocalName};
 | 
			
		||||
    } elsif ($data->{LocalName} eq 'volume') {
 | 
			
		||||
        $self->{_volume_materialid} = $self->_get_attribute($data, 'materialid') || '_';
 | 
			
		||||
        $self->{_volume} = [];
 | 
			
		||||
        $self->{_volume} = $self->{_object}->add_volume(
 | 
			
		||||
            material_id => $self->_get_attribute($data, 'materialid') || undef,
 | 
			
		||||
        );
 | 
			
		||||
    } elsif ($data->{LocalName} eq 'triangle') {
 | 
			
		||||
        $self->{_triangle} = ["", "", ""];
 | 
			
		||||
    } elsif ($self->{_triangle} && $data->{LocalName} =~ /^v([123])$/ && $self->{_tree}[-1] eq 'triangle') {
 | 
			
		||||
        $self->{_vertex_idx} = $1-1;
 | 
			
		||||
    } elsif ($data->{LocalName} eq 'material') {
 | 
			
		||||
        $self->{_material_id} = $self->_get_attribute($data, 'id') || '_';
 | 
			
		||||
        $self->{_material} = {};
 | 
			
		||||
        my $material_id = $self->_get_attribute($data, 'id') || '_';
 | 
			
		||||
        $self->{_material} = $self->{_model}->materials->{ $material_id } = {};
 | 
			
		||||
    } elsif ($data->{LocalName} eq 'metadata' && $self->{_tree}[-1] eq 'material') {
 | 
			
		||||
        $self->{_material_metadata_type} = $self->_get_attribute($data, 'type');
 | 
			
		||||
        $self->{_material}{ $self->{_material_metadata_type} } = "";
 | 
			
		||||
    } elsif ($data->{LocalName} eq 'constellation') {
 | 
			
		||||
        $self->{_constellation} = 1; # we merge all constellations as we don't support more than one
 | 
			
		||||
    } elsif ($data->{LocalName} eq 'instance' && $self->{_constellation}) {
 | 
			
		||||
        my $object_id = $self->_get_attribute($data, 'objectid');
 | 
			
		||||
        $self->{_instances}{$object_id} ||= [];
 | 
			
		||||
        push @{ $self->{_instances}{$object_id} }, $self->{_instance} = {};
 | 
			
		||||
    } elsif ($data->{LocalName} =~ /^(?:deltax|deltay|rz)$/ && $self->{_instance}) {
 | 
			
		||||
        $self->{_instance_property} = $data->{LocalName};
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    push @{$self->{_tree}}, $data->{LocalName};
 | 
			
		||||
| 
						 | 
				
			
			@ -50,6 +64,8 @@ sub characters {
 | 
			
		|||
        $self->{_triangle}[ $self->{_vertex_idx} ] .= $data->{Data};
 | 
			
		||||
    } elsif ($self->{_material_metadata_type}) {
 | 
			
		||||
        $self->{_material}{ $self->{_material_metadata_type} } .= $data->{Data};
 | 
			
		||||
    } elsif ($self->{_instance_property}) {
 | 
			
		||||
        $self->{_instance}{ $self->{_instance_property} } .= $data->{Data};
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -59,26 +75,49 @@ sub end_element {
 | 
			
		|||
    
 | 
			
		||||
    pop @{$self->{_tree}};
 | 
			
		||||
    
 | 
			
		||||
    if ($data->{LocalName} eq 'vertex') {
 | 
			
		||||
        push @{$self->{_vertices}}, $self->{_vertex};
 | 
			
		||||
    if ($data->{LocalName} eq 'object') {
 | 
			
		||||
        $self->{_object} = undef;
 | 
			
		||||
    } elsif ($data->{LocalName} eq 'vertex') {
 | 
			
		||||
        push @{$self->{_object}->vertices}, $self->{_vertex};
 | 
			
		||||
        $self->{_vertex} = undef;
 | 
			
		||||
    } elsif ($self->{_coordinate} && $data->{LocalName} =~ /^[xyz]$/) {
 | 
			
		||||
        $self->{_coordinate} = undef;
 | 
			
		||||
    } elsif ($data->{LocalName} eq 'volume') {
 | 
			
		||||
        $self->{_meshes_by_material}{ $self->{_volume_materialid} } ||= [];
 | 
			
		||||
        push @{ $self->{_meshes_by_material}{ $self->{_volume_materialid} } }, @{$self->{_volume}};
 | 
			
		||||
        $self->{_volume} = undef;
 | 
			
		||||
    } elsif ($data->{LocalName} eq 'triangle') {
 | 
			
		||||
        push @{$self->{_volume}}, $self->{_triangle};
 | 
			
		||||
        push @{$self->{_volume}->facets}, $self->{_triangle};
 | 
			
		||||
        $self->{_triangle} = undef;
 | 
			
		||||
    } elsif ($self->{_vertex_idx} && $data->{LocalName} =~ /^v[123]$/) {
 | 
			
		||||
    } elsif (defined $self->{_vertex_idx} && $data->{LocalName} =~ /^v[123]$/) {
 | 
			
		||||
        $self->{_vertex_idx} = undef;
 | 
			
		||||
    } elsif ($data->{LocalName} eq 'material') {
 | 
			
		||||
        $self->{_materials}{ $self->{_material_id} } = $self->{_material};
 | 
			
		||||
        $self->{_material_id} = undef;
 | 
			
		||||
        $self->{_material} = undef;
 | 
			
		||||
    } elsif ($data->{LocalName} eq 'metadata' && $self->{_material}) {
 | 
			
		||||
        $self->{_material_metadata_type} = undef;
 | 
			
		||||
    } elsif ($data->{LocalName} eq 'constellation') {
 | 
			
		||||
        $self->{_constellation} = undef;
 | 
			
		||||
    } elsif ($data->{LocalName} eq 'instance') {
 | 
			
		||||
        $self->{_instance} = undef;
 | 
			
		||||
    } elsif ($data->{LocalName} =~ /^(?:deltax|deltay|rz)$/ && $self->{_instance}) {
 | 
			
		||||
        $self->{_instance_property} = undef;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub end_document {
 | 
			
		||||
    my $self = shift;
 | 
			
		||||
    
 | 
			
		||||
    foreach my $object_id (keys %{ $self->{_instances} }) {
 | 
			
		||||
        my $new_object_id = $self->{_objects_map}{$object_id};
 | 
			
		||||
        if (!$new_object_id) {
 | 
			
		||||
            warn "Undefined object $object_id referenced in constellation\n";
 | 
			
		||||
            next;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        foreach my $instance (@{ $self->{_instances}{$object_id} }) {
 | 
			
		||||
            $self->{_model}->objects->[$new_object_id]->add_instance(
 | 
			
		||||
                rotation => $instance->{rz} || 0,
 | 
			
		||||
                offset   => [ $instance->{deltax} || 0, $instance->{deltay} ],
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,7 +17,10 @@ sub read_file {
 | 
			
		|||
    }
 | 
			
		||||
    close $fh;
 | 
			
		||||
    
 | 
			
		||||
    return Slic3r::TriangleMesh->new(vertices => $vertices, facets => $facets);
 | 
			
		||||
    my $model = Slic3r::Model->new;
 | 
			
		||||
    my $object = $model->add_object(vertices => $vertices);
 | 
			
		||||
    my $volume = $object->add_volume(facets => $facets);
 | 
			
		||||
    return $model;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
1;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -117,7 +117,10 @@ sub read_file {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    return Slic3r::TriangleMesh->new(vertices => $vertices, facets => $facets);
 | 
			
		||||
    my $model = Slic3r::Model->new;
 | 
			
		||||
    my $object = $model->add_object(vertices => $vertices);
 | 
			
		||||
    my $volume = $object->add_volume(facets => $facets);
 | 
			
		||||
    return $model;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub _read_ascii {
 | 
			
		||||
| 
						 | 
				
			
			@ -161,13 +164,13 @@ sub _read_binary {
 | 
			
		|||
 | 
			
		||||
sub write_file {
 | 
			
		||||
    my $self = shift;
 | 
			
		||||
    my ($file, $mesh, $binary) = @_;
 | 
			
		||||
    my ($file, $model, %params) = @_;
 | 
			
		||||
    
 | 
			
		||||
    open my $fh, '>', $file;
 | 
			
		||||
    
 | 
			
		||||
    $binary
 | 
			
		||||
        ? _write_binary($fh, $mesh)
 | 
			
		||||
        : _write_ascii($fh, $mesh);
 | 
			
		||||
    $params{binary}
 | 
			
		||||
        ? _write_binary($fh, $model->mesh)
 | 
			
		||||
        : _write_ascii($fh, $model->mesh);
 | 
			
		||||
    
 | 
			
		||||
    close $fh;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,8 +1,9 @@
 | 
			
		|||
package Slic3r::GCode;
 | 
			
		||||
use Moo;
 | 
			
		||||
 | 
			
		||||
use List::Util qw(first);
 | 
			
		||||
use Slic3r::ExtrusionPath ':roles';
 | 
			
		||||
use Slic3r::Geometry qw(scale unscale);
 | 
			
		||||
use Slic3r::Geometry qw(scale unscale scaled_epsilon points_coincide PI X Y);
 | 
			
		||||
 | 
			
		||||
has 'layer'              => (is => 'rw');
 | 
			
		||||
has 'shift_x'            => (is => 'rw', default => sub {0} );
 | 
			
		||||
| 
						 | 
				
			
			@ -48,8 +49,6 @@ my %role_speeds = (
 | 
			
		|||
    &EXTR_ROLE_SUPPORTMATERIAL              => 'perimeter',
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
use Slic3r::Geometry qw(points_coincide PI X Y);
 | 
			
		||||
 | 
			
		||||
sub extruder {
 | 
			
		||||
    my $self = shift;
 | 
			
		||||
    return $Slic3r::extruders->[$self->extruder_idx];
 | 
			
		||||
| 
						 | 
				
			
			@ -132,15 +131,13 @@ sub extrude_path {
 | 
			
		|||
    my $gcode = "";
 | 
			
		||||
    
 | 
			
		||||
    # retract if distance from previous position is greater or equal to the one
 | 
			
		||||
    # specified by the user *and* to the maximum distance between infill lines
 | 
			
		||||
    # specified by the user
 | 
			
		||||
    {
 | 
			
		||||
        my $distance_from_last_pos = $self->last_pos->distance_to($path->points->[0]) * &Slic3r::SCALING_FACTOR;
 | 
			
		||||
        my $distance_threshold = $self->extruder->retract_before_travel;
 | 
			
		||||
        $distance_threshold = 2 * ($self->layer ? $self->layer->flow->width : $Slic3r::flow->width) / $Slic3r::Config->fill_density * sqrt(2)
 | 
			
		||||
            if 0 && $Slic3r::Config->fill_density > 0 && $description =~ /fill/;
 | 
			
		||||
    
 | 
			
		||||
        if ($distance_from_last_pos >= $distance_threshold) {
 | 
			
		||||
            $gcode .= $self->retract(travel_to => $path->points->[0]);
 | 
			
		||||
        my $travel = Slic3r::Line->new($self->last_pos, $path->points->[0]);
 | 
			
		||||
        if ($travel->length >= scale $self->extruder->retract_before_travel) {
 | 
			
		||||
            if (!$Slic3r::Config->only_retract_when_crossing_perimeters || $path->role != EXTR_ROLE_FILL || !first { $_->expolygon->encloses_line($travel, scaled_epsilon) } @{$self->layer->slices}) {
 | 
			
		||||
                $gcode .= $self->retract(travel_to => $path->points->[0]);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
| 
						 | 
				
			
			@ -424,25 +421,34 @@ sub set_temperature {
 | 
			
		|||
    
 | 
			
		||||
    return "" if $wait && $Slic3r::Config->gcode_flavor eq 'makerbot';
 | 
			
		||||
    
 | 
			
		||||
    my ($code, $comment) = $wait
 | 
			
		||||
    my ($code, $comment) = ($wait && $Slic3r::Config->gcode_flavor ne 'teacup')
 | 
			
		||||
        ? ('M109', 'wait for temperature to be reached')
 | 
			
		||||
        : ('M104', 'set temperature');
 | 
			
		||||
    return sprintf "$code %s%d %s; $comment\n",
 | 
			
		||||
    my $gcode = sprintf "$code %s%d %s; $comment\n",
 | 
			
		||||
        ($Slic3r::Config->gcode_flavor eq 'mach3' ? 'P' : 'S'), $temperature,
 | 
			
		||||
        (defined $tool && $tool != $self->extruder_idx) ? "T$tool " : "";
 | 
			
		||||
    
 | 
			
		||||
    $gcode .= "M116 ; wait for temperature to be reached\n"
 | 
			
		||||
        if $Slic3r::Config->gcode_flavor eq 'teacup' && $wait;
 | 
			
		||||
    
 | 
			
		||||
    return $gcode;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub set_bed_temperature {
 | 
			
		||||
    my $self = shift;
 | 
			
		||||
    my ($temperature, $wait) = @_;
 | 
			
		||||
    
 | 
			
		||||
    my ($code, $comment) = $wait
 | 
			
		||||
    my ($code, $comment) = ($wait && $Slic3r::Config->gcode_flavor ne 'teacup')
 | 
			
		||||
        ? (($Slic3r::Config->gcode_flavor eq 'makerbot' ? 'M109'
 | 
			
		||||
            : $Slic3r::Config->gcode_flavor eq 'teacup' ? 'M109 P1'
 | 
			
		||||
            : 'M190'), 'wait for bed temperature to be reached')
 | 
			
		||||
        : ('M140', 'set bed temperature');
 | 
			
		||||
    return sprintf "$code %s%d ; $comment\n",
 | 
			
		||||
    my $gcode = sprintf "$code %s%d ; $comment\n",
 | 
			
		||||
        ($Slic3r::Config->gcode_flavor eq 'mach3' ? 'P' : 'S'), $temperature;
 | 
			
		||||
    
 | 
			
		||||
    $gcode .= "M116 ; wait for bed temperature to be reached\n"
 | 
			
		||||
        if $Slic3r::Config->gcode_flavor eq 'teacup' && $wait;
 | 
			
		||||
    
 | 
			
		||||
    return $gcode;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
1;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,6 +22,10 @@ use constant MI_REPEAT_QUICK  => &Wx::NewId;
 | 
			
		|||
use constant MI_QUICK_SAVE_AS => &Wx::NewId;
 | 
			
		||||
use constant MI_SLICE_SVG     => &Wx::NewId;
 | 
			
		||||
 | 
			
		||||
use constant MI_PLATER_EXPORT_GCODE => &Wx::NewId;
 | 
			
		||||
use constant MI_PLATER_EXPORT_STL   => &Wx::NewId;
 | 
			
		||||
use constant MI_PLATER_EXPORT_AMF   => &Wx::NewId;
 | 
			
		||||
 | 
			
		||||
use constant MI_TAB_PLATER    => &Wx::NewId;
 | 
			
		||||
use constant MI_TAB_PRINT     => &Wx::NewId;
 | 
			
		||||
use constant MI_TAB_FILAMENT  => &Wx::NewId;
 | 
			
		||||
| 
						 | 
				
			
			@ -47,7 +51,7 @@ sub OnInit {
 | 
			
		|||
    $self->{notifier} = Slic3r::GUI::Notifier->new;
 | 
			
		||||
    
 | 
			
		||||
    # locate or create data directory
 | 
			
		||||
    $datadir = Wx::StandardPaths::Get->GetUserDataDir;
 | 
			
		||||
    $datadir ||= Wx::StandardPaths::Get->GetUserDataDir;
 | 
			
		||||
    Slic3r::debugf "Data directory: %s\n", $datadir;
 | 
			
		||||
    my $run_wizard = (-d $datadir) ? 0 : 1;
 | 
			
		||||
    for ($datadir, "$datadir/print", "$datadir/filament", "$datadir/printer") {
 | 
			
		||||
| 
						 | 
				
			
			@ -98,6 +102,17 @@ sub OnInit {
 | 
			
		|||
        EVT_MENU($frame, wxID_EXIT, sub {$_[0]->Close(0)});
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    # Plater menu
 | 
			
		||||
    my $platerMenu = Wx::Menu->new;
 | 
			
		||||
    {
 | 
			
		||||
        $platerMenu->Append(MI_PLATER_EXPORT_GCODE, "Export G-code...", 'Export current plate as G-code');
 | 
			
		||||
        $platerMenu->Append(MI_PLATER_EXPORT_STL, "Export STL...", 'Export current plate as STL');
 | 
			
		||||
        $platerMenu->Append(MI_PLATER_EXPORT_AMF, "Export AMF...", 'Export current plate as AMF');
 | 
			
		||||
        EVT_MENU($frame, MI_PLATER_EXPORT_GCODE, sub { $self->{skeinpanel}{plater}->export_gcode });
 | 
			
		||||
        EVT_MENU($frame, MI_PLATER_EXPORT_STL, sub { $self->{skeinpanel}{plater}->export_stl });
 | 
			
		||||
        EVT_MENU($frame, MI_PLATER_EXPORT_AMF, sub { $self->{skeinpanel}{plater}->export_amf });
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    # Window menu
 | 
			
		||||
    my $windowMenu = Wx::Menu->new;
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			@ -128,6 +143,7 @@ sub OnInit {
 | 
			
		|||
    {
 | 
			
		||||
        my $menubar = Wx::MenuBar->new;
 | 
			
		||||
        $menubar->Append($fileMenu, "&File");
 | 
			
		||||
        $menubar->Append($platerMenu, "&Plater");
 | 
			
		||||
        $menubar->Append($windowMenu, "&Window");
 | 
			
		||||
        $menubar->Append($helpMenu, "&Help");
 | 
			
		||||
        $frame->SetMenuBar($menubar);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -296,7 +296,7 @@ sub load_file {
 | 
			
		|||
    my $process_dialog = Wx::ProgressDialog->new('Loading…', "Processing input file…", 100, $self, 0);
 | 
			
		||||
    $process_dialog->Pulse;
 | 
			
		||||
    local $SIG{__WARN__} = Slic3r::GUI::warning_catcher($self);
 | 
			
		||||
    $self->{print}->add_object_from_file($input_file);
 | 
			
		||||
    $self->{print}->add_objects_from_file($input_file);
 | 
			
		||||
    my $obj_idx = $#{$self->{print}->objects};
 | 
			
		||||
    $process_dialog->Destroy;
 | 
			
		||||
    
 | 
			
		||||
| 
						 | 
				
			
			@ -419,9 +419,7 @@ sub rotate {
 | 
			
		|||
    # rotate, realign to 0,0 and update size
 | 
			
		||||
    $object->mesh->rotate($angle);
 | 
			
		||||
    $object->mesh->align_to_origin;
 | 
			
		||||
    my @size = $object->mesh->size;
 | 
			
		||||
    $object->x_length($size[X]);
 | 
			
		||||
    $object->y_length($size[Y]);
 | 
			
		||||
    $object->size([ $object->mesh->size ]);
 | 
			
		||||
    
 | 
			
		||||
    $self->make_thumbnail($obj_idx);
 | 
			
		||||
    $self->recenter;
 | 
			
		||||
| 
						 | 
				
			
			@ -458,9 +456,7 @@ sub changescale {
 | 
			
		|||
    my $mesh = $object->mesh;
 | 
			
		||||
    $mesh->scale($scale/100 / $self->{scale}[$obj_idx]);
 | 
			
		||||
    $object->mesh->align_to_origin;
 | 
			
		||||
    my @size = $object->mesh->size;
 | 
			
		||||
    $object->x_length($size[X]);
 | 
			
		||||
    $object->y_length($size[Y]);
 | 
			
		||||
    $object->size([ $object->mesh->size ]);
 | 
			
		||||
    
 | 
			
		||||
    $self->{scale}[$obj_idx] = $scale/100;
 | 
			
		||||
    $self->{list}->SetItem($obj_idx, 2, "$scale%");
 | 
			
		||||
| 
						 | 
				
			
			@ -512,6 +508,8 @@ sub export_gcode {
 | 
			
		|||
    
 | 
			
		||||
    # set this before spawning the thread because ->config needs GetParent and it's not available there
 | 
			
		||||
    $self->{print}->config($self->skeinpanel->config);
 | 
			
		||||
    $self->{print}->extra_variables->{"${_}_preset"} = $self->skeinpanel->{options_tabs}{$_}->current_preset->{name}
 | 
			
		||||
        for qw(print filament printer);
 | 
			
		||||
    
 | 
			
		||||
    # select output file
 | 
			
		||||
    $self->{output_file} = $main::opt{output};
 | 
			
		||||
| 
						 | 
				
			
			@ -634,39 +632,58 @@ sub on_export_failed {
 | 
			
		|||
 | 
			
		||||
sub export_stl {
 | 
			
		||||
    my $self = shift;
 | 
			
		||||
    
 | 
			
		||||
    my $print = $self->{print};
 | 
			
		||||
        
 | 
			
		||||
    # select output file
 | 
			
		||||
    my $output_file = $self->_get_export_file('STL') or return;
 | 
			
		||||
    Slic3r::Format::STL->write_file($output_file, $self->make_model, binary => 1);
 | 
			
		||||
    $self->statusbar->SetStatusText("STL file exported to $output_file");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub export_amf {
 | 
			
		||||
    my $self = shift;
 | 
			
		||||
        
 | 
			
		||||
    my $output_file = $self->_get_export_file('AMF') or return;
 | 
			
		||||
    Slic3r::Format::AMF->write_file($output_file, $self->make_model);
 | 
			
		||||
    $self->statusbar->SetStatusText("AMF file exported to $output_file");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub _get_export_file {
 | 
			
		||||
    my $self = shift;
 | 
			
		||||
    my ($format) = @_;
 | 
			
		||||
    
 | 
			
		||||
    my $suffix = $format eq 'STL' ? '.stl' : '.amf.xml';
 | 
			
		||||
    
 | 
			
		||||
    my $output_file = $main::opt{output};
 | 
			
		||||
    {
 | 
			
		||||
        $output_file = $print->expanded_output_filepath($output_file);
 | 
			
		||||
        $output_file =~ s/\.gcode$/.stl/i;
 | 
			
		||||
        my $dlg = Wx::FileDialog->new($self, 'Save STL file as:', dirname($output_file),
 | 
			
		||||
        $output_file = $self->{print}->expanded_output_filepath($output_file);
 | 
			
		||||
        $output_file =~ s/\.gcode$/$suffix/i;
 | 
			
		||||
        my $dlg = Wx::FileDialog->new($self, "Save $format file as:", dirname($output_file),
 | 
			
		||||
            basename($output_file), $Slic3r::GUI::SkeinPanel::model_wildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
 | 
			
		||||
        if ($dlg->ShowModal != wxID_OK) {
 | 
			
		||||
            $dlg->Destroy;
 | 
			
		||||
            return;
 | 
			
		||||
            return undef;
 | 
			
		||||
        }
 | 
			
		||||
        $output_file = $Slic3r::GUI::SkeinPanel::last_output_file = $dlg->GetPath;
 | 
			
		||||
        $dlg->Destroy;
 | 
			
		||||
    }
 | 
			
		||||
    return $output_file;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub make_model {
 | 
			
		||||
    my $self = shift;
 | 
			
		||||
    
 | 
			
		||||
    my $mesh = Slic3r::TriangleMesh->new(facets => [], vertices => []);
 | 
			
		||||
    for my $obj_idx (0 .. $#{$print->objects}) {
 | 
			
		||||
        for my $copy (@{$print->copies->[$obj_idx]}) {
 | 
			
		||||
            my $cloned_mesh = $print->objects->[$obj_idx]->mesh->clone;
 | 
			
		||||
            $cloned_mesh->move(@$copy);
 | 
			
		||||
            my $vertices_offset = scalar @{$mesh->vertices};
 | 
			
		||||
            push @{$mesh->vertices}, @{$cloned_mesh->vertices};
 | 
			
		||||
            push @{$mesh->facets}, map [ $_->[0], map $vertices_offset + $_, @$_[-3..-1] ], @{$cloned_mesh->facets};
 | 
			
		||||
    my $model = Slic3r::Model->new;
 | 
			
		||||
    for my $obj_idx (0 .. $#{$self->{print}->objects}) {
 | 
			
		||||
        my $mesh = $self->{print}->objects->[$obj_idx]->mesh->clone;
 | 
			
		||||
        $mesh->scale(&Slic3r::SCALING_FACTOR);
 | 
			
		||||
        my $object = $model->add_object(vertices => $mesh->vertices);
 | 
			
		||||
        $object->add_volume(facets => $mesh->facets);
 | 
			
		||||
        for my $copy (@{$self->{print}->copies->[$obj_idx]}) {
 | 
			
		||||
            $object->add_instance(rotation => 0, offset => [ map unscale $_, @$copy ]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    $mesh->scale(&Slic3r::SCALING_FACTOR);
 | 
			
		||||
    $mesh->align_to_origin;
 | 
			
		||||
    # TODO: $model->align_to_origin;
 | 
			
		||||
    
 | 
			
		||||
    Slic3r::Format::STL->write_file($output_file, $mesh, 1);
 | 
			
		||||
    $self->statusbar->SetStatusText("STL file exported to $output_file");
 | 
			
		||||
    return $model;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub make_thumbnail {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -96,7 +96,7 @@ sub do_slice {
 | 
			
		|||
        Slic3r::GUI->save_settings;
 | 
			
		||||
        
 | 
			
		||||
        my $print = Slic3r::Print->new(config => $config);
 | 
			
		||||
        $print->add_object_from_file($input_file);
 | 
			
		||||
        $print->add_objects_from_file($input_file);
 | 
			
		||||
        $print->validate;
 | 
			
		||||
 | 
			
		||||
        # select output file
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -401,7 +401,7 @@ sub build {
 | 
			
		|||
        },
 | 
			
		||||
        {
 | 
			
		||||
            title => 'Advanced',
 | 
			
		||||
            options => [qw(infill_every_layers fill_angle solid_infill_below_area)],
 | 
			
		||||
            options => [qw(infill_every_layers fill_angle solid_infill_below_area only_retract_when_crossing_perimeters)],
 | 
			
		||||
        },
 | 
			
		||||
    ]);
 | 
			
		||||
    
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -20,6 +20,7 @@ our @EXPORT_OK = qw(
 | 
			
		|||
    shortest_path collinear scale unscale merge_collinear_lines
 | 
			
		||||
    rad2deg_dir bounding_box_center line_intersects_any douglas_peucker
 | 
			
		||||
    polyline_remove_short_segments normal triangle_normal polygon_is_convex
 | 
			
		||||
    scaled_epsilon
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -38,6 +39,7 @@ use constant MAX => 1;
 | 
			
		|||
our $parallel_degrees_limit = abs(deg2rad(3));
 | 
			
		||||
 | 
			
		||||
sub epsilon () { 1E-4 }
 | 
			
		||||
sub scaled_epsilon () { epsilon / &Slic3r::SCALING_FACTOR }
 | 
			
		||||
 | 
			
		||||
sub scale   ($) { $_[0] / &Slic3r::SCALING_FACTOR }
 | 
			
		||||
sub unscale ($) { $_[0] * &Slic3r::SCALING_FACTOR }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,29 +4,34 @@ use warnings;
 | 
			
		|||
 | 
			
		||||
require Exporter;
 | 
			
		||||
our @ISA = qw(Exporter);
 | 
			
		||||
our @EXPORT_OK = qw(explode_expolygon explode_expolygons safety_offset offset
 | 
			
		||||
our @EXPORT_OK = qw(safety_offset offset offset_ex
 | 
			
		||||
    diff_ex diff union_ex intersection_ex xor_ex PFT_EVENODD JT_MITER JT_ROUND
 | 
			
		||||
    JT_SQUARE is_counter_clockwise);
 | 
			
		||||
 | 
			
		||||
use Math::Clipper 1.09 ':all';
 | 
			
		||||
use Math::Clipper 1.09 qw(:cliptypes :polyfilltypes :jointypes is_counter_clockwise area);
 | 
			
		||||
use Slic3r::Geometry qw(scale);
 | 
			
		||||
our $clipper = Math::Clipper->new;
 | 
			
		||||
 | 
			
		||||
sub explode_expolygon {
 | 
			
		||||
    my ($expolygon) = @_;
 | 
			
		||||
    return ($expolygon->{outer}, @{ $expolygon->{holes} });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub explode_expolygons {
 | 
			
		||||
    my ($expolygons) = @_;
 | 
			
		||||
    return map explode_expolygon($_), @$expolygons;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub safety_offset {
 | 
			
		||||
    my ($polygons, $factor) = @_;
 | 
			
		||||
    return Math::Clipper::offset($polygons, $factor || (scale 1e-05), 100, JT_MITER, 2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub offset {
 | 
			
		||||
    my ($polygons, $distance, $scale, $joinType, $miterLimit) = @_;
 | 
			
		||||
    $scale      ||= &Slic3r::SCALING_FACTOR * 1000000;
 | 
			
		||||
    $joinType   = JT_MITER if !defined $joinType;
 | 
			
		||||
    $miterLimit ||= 2;
 | 
			
		||||
    
 | 
			
		||||
    my $offsets = Math::Clipper::offset($polygons, $distance, $scale, $joinType, $miterLimit);
 | 
			
		||||
    return @$offsets;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub offset_ex {
 | 
			
		||||
    # offset polygons and then apply holes to the right contours
 | 
			
		||||
    return @{ union_ex([ offset(@_) ]) };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub diff_ex {
 | 
			
		||||
    my ($subject, $clip, $safety_offset) = @_;
 | 
			
		||||
    
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -346,7 +346,7 @@ sub make_perimeters {
 | 
			
		|||
    {
 | 
			
		||||
        my @thin_paths = ();
 | 
			
		||||
        my %properties = (
 | 
			
		||||
            role            => EXTR_ROLE_PERIMETER,
 | 
			
		||||
            role            => EXTR_ROLE_EXTERNAL_PERIMETER,
 | 
			
		||||
            flow_spacing    => $self->perimeter_flow->spacing,
 | 
			
		||||
        );
 | 
			
		||||
        for (@{ $self->thin_walls }) {
 | 
			
		||||
| 
						 | 
				
			
			@ -386,13 +386,30 @@ sub prepare_fill_surfaces {
 | 
			
		|||
    if ($Slic3r::Config->fill_density == 0) {
 | 
			
		||||
        @surfaces = grep $_->surface_type != S_TYPE_INTERNAL, @surfaces;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    # remove unprintable regions (they would slow down the infill process and also cause
 | 
			
		||||
    # some weird failures during bridge neighbor detection)
 | 
			
		||||
    {
 | 
			
		||||
        my $distance = scale $self->infill_flow->spacing / 2;
 | 
			
		||||
        @surfaces = map {
 | 
			
		||||
            my $surface = $_;
 | 
			
		||||
            
 | 
			
		||||
            # offset inwards
 | 
			
		||||
            my @offsets = $surface->expolygon->offset_ex(-$distance);
 | 
			
		||||
            @offsets = @{union_ex(Math::Clipper::offset([ map @$_, @offsets ], $distance, 100, JT_MITER))};
 | 
			
		||||
            map Slic3r::Surface->new(
 | 
			
		||||
                expolygon => $_,
 | 
			
		||||
                surface_type => $surface->surface_type,
 | 
			
		||||
            ), @offsets;
 | 
			
		||||
        } @surfaces;
 | 
			
		||||
    }
 | 
			
		||||
        
 | 
			
		||||
    # turn too small internal regions into solid regions
 | 
			
		||||
    {
 | 
			
		||||
        my $min_area = scale scale $Slic3r::Config->solid_infill_below_area; # scaling an area requires two calls!
 | 
			
		||||
        my @small = grep $_->surface_type == S_TYPE_INTERNAL && $_->expolygon->contour->area <= $min_area, @surfaces;
 | 
			
		||||
        $_->surface_type(S_TYPE_INTERNALSOLID) for @small;
 | 
			
		||||
        Slic3r::debugf "identified %d small surfaces at layer %d\n", scalar(@small), $self->id if @small > 0;
 | 
			
		||||
        Slic3r::debugf "identified %d small solid surfaces at layer %d\n", scalar(@small), $self->id if @small > 0;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    $self->fill_surfaces([@surfaces]);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										112
									
								
								lib/Slic3r/Model.pm
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								lib/Slic3r/Model.pm
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,112 @@
 | 
			
		|||
package Slic3r::Model;
 | 
			
		||||
use Moo;
 | 
			
		||||
 | 
			
		||||
use Slic3r::Geometry qw(X Y Z);
 | 
			
		||||
 | 
			
		||||
has 'materials' => (is => 'ro', default => sub { {} });
 | 
			
		||||
has 'objects'   => (is => 'ro', default => sub { [] });
 | 
			
		||||
 | 
			
		||||
sub add_object {
 | 
			
		||||
    my $self = shift;
 | 
			
		||||
    
 | 
			
		||||
    my $object = Slic3r::Model::Object->new(model => $self, @_);
 | 
			
		||||
    push @{$self->objects}, $object;
 | 
			
		||||
    return $object;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# flattens everything to a single mesh
 | 
			
		||||
sub mesh {
 | 
			
		||||
    my $self = shift;
 | 
			
		||||
    
 | 
			
		||||
    my $vertices = [];
 | 
			
		||||
    my $facets = [];
 | 
			
		||||
    foreach my $object (@{$self->objects}) {
 | 
			
		||||
        my @instances = $object->instances ? @{$object->instances} : (undef);
 | 
			
		||||
        foreach my $instance (@instances) {
 | 
			
		||||
            my @vertices = @{$object->vertices};
 | 
			
		||||
            if ($instance) {
 | 
			
		||||
                # save Z coordinates, as rotation and translation discard them
 | 
			
		||||
                my @z = map $_->[Z], @vertices;
 | 
			
		||||
                
 | 
			
		||||
                if ($instance->rotation) {
 | 
			
		||||
                    # transform vertex coordinates
 | 
			
		||||
                    my $rad = Slic3r::Geometry::deg2rad($instance->rotation);
 | 
			
		||||
                    @vertices = Slic3r::Geometry::rotate_points($rad, undef, @vertices);
 | 
			
		||||
                }
 | 
			
		||||
                @vertices = Slic3r::Geometry::move_points($instance->offset, @vertices);
 | 
			
		||||
                
 | 
			
		||||
                # reapply Z coordinates
 | 
			
		||||
                $vertices[$_][Z] = $z[$_] for 0 .. $#z;
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            my $v_offset = @$vertices;
 | 
			
		||||
            push @$vertices, @vertices;
 | 
			
		||||
            foreach my $volume (@{$object->volumes}) {
 | 
			
		||||
                push @$facets, map {
 | 
			
		||||
                    my $f = [@$_];
 | 
			
		||||
                    $f->[$_] += $v_offset for -3..-1;
 | 
			
		||||
                    $f;
 | 
			
		||||
                } @{$volume->facets};
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    return Slic3r::TriangleMesh->new(
 | 
			
		||||
        vertices => $vertices,
 | 
			
		||||
        facets   => $facets,
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
package Slic3r::Model::Material;
 | 
			
		||||
use Moo;
 | 
			
		||||
 | 
			
		||||
has 'model'         => (is => 'ro', weak_ref => 1, required => 1);
 | 
			
		||||
has 'attributes'    => (is => 'rw', default => sub { {} });
 | 
			
		||||
 | 
			
		||||
package Slic3r::Model::Object;
 | 
			
		||||
use Moo;
 | 
			
		||||
 | 
			
		||||
has 'model'     => (is => 'ro', weak_ref => 1, required => 1);
 | 
			
		||||
has 'vertices'  => (is => 'ro', default => sub { [] });
 | 
			
		||||
has 'volumes'   => (is => 'ro', default => sub { [] });
 | 
			
		||||
has 'instances' => (is => 'rw');
 | 
			
		||||
 | 
			
		||||
sub add_volume {
 | 
			
		||||
    my $self = shift;
 | 
			
		||||
    
 | 
			
		||||
    my $volume = Slic3r::Model::Volume->new(object => $self, @_);
 | 
			
		||||
    push @{$self->volumes}, $volume;
 | 
			
		||||
    return $volume;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub add_instance {
 | 
			
		||||
    my $self = shift;
 | 
			
		||||
    
 | 
			
		||||
    $self->instances([]) if !defined $self->instances;
 | 
			
		||||
    push @{$self->instances}, Slic3r::Model::Instance->new(object => $self, @_);
 | 
			
		||||
    return $self->instances->[-1];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
package Slic3r::Model::Volume;
 | 
			
		||||
use Moo;
 | 
			
		||||
 | 
			
		||||
has 'object'        => (is => 'ro', weak_ref => 1, required => 1);
 | 
			
		||||
has 'material_id'   => (is => 'rw');
 | 
			
		||||
has 'facets'        => (is => 'rw', default => sub { [] });
 | 
			
		||||
 | 
			
		||||
sub mesh {
 | 
			
		||||
    my $self = shift;
 | 
			
		||||
    return Slic3r::TriangleMesh->new(
 | 
			
		||||
        vertices => $self->object->vertices,
 | 
			
		||||
        facets   => $self->facets,
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
package Slic3r::Model::Instance;
 | 
			
		||||
use Moo;
 | 
			
		||||
 | 
			
		||||
has 'object'    => (is => 'ro', weak_ref => 1, required => 1);
 | 
			
		||||
has 'rotation'  => (is => 'rw', default => sub { 0 });
 | 
			
		||||
has 'offset'    => (is => 'rw');
 | 
			
		||||
 | 
			
		||||
1;
 | 
			
		||||
| 
						 | 
				
			
			@ -71,13 +71,7 @@ sub safety_offset {
 | 
			
		|||
 | 
			
		||||
sub offset {
 | 
			
		||||
    my $self = shift;
 | 
			
		||||
    my ($distance, $scale, $joinType, $miterLimit) = @_;
 | 
			
		||||
    $scale      ||= &Slic3r::SCALING_FACTOR * 1000000;
 | 
			
		||||
    $joinType   = JT_MITER if !defined $joinType;
 | 
			
		||||
    $miterLimit ||= 2;
 | 
			
		||||
    
 | 
			
		||||
    my $offsets = Math::Clipper::offset([$self], $distance, $scale, $joinType, $miterLimit);
 | 
			
		||||
    return map Slic3r::Polygon->new($_), @$offsets;
 | 
			
		||||
    return map Slic3r::Polygon->new($_), Slic3r::Geometry::Clipper::offset([$self], @_);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# this method subdivides the polygon segments to that no one of them
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -144,6 +144,7 @@ sub rotate {
 | 
			
		|||
    my $self = shift;
 | 
			
		||||
    my ($angle, $center) = @_;
 | 
			
		||||
    @$self = Slic3r::Geometry::rotate_points($angle, $center, @$self);
 | 
			
		||||
    bless $_, 'Slic3r::Point' for @$self;
 | 
			
		||||
    return $self;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -151,6 +152,7 @@ sub translate {
 | 
			
		|||
    my $self = shift;
 | 
			
		||||
    my ($x, $y) = @_;
 | 
			
		||||
    @$self = Slic3r::Geometry::move_points([$x, $y], @$self);
 | 
			
		||||
    bless $_, 'Slic3r::Point' for @$self;
 | 
			
		||||
    return $self;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,11 +5,12 @@ use File::Basename qw(basename fileparse);
 | 
			
		|||
use File::Spec;
 | 
			
		||||
use Math::ConvexHull 1.0.4 qw(convex_hull);
 | 
			
		||||
use Slic3r::ExtrusionPath ':roles';
 | 
			
		||||
use Slic3r::Geometry qw(X Y Z X1 Y1 X2 Y2 PI scale unscale move_points);
 | 
			
		||||
use Slic3r::Geometry qw(X Y Z X1 Y1 X2 Y2 PI scale unscale move_points nearest_point);
 | 
			
		||||
use Slic3r::Geometry::Clipper qw(diff_ex union_ex intersection_ex offset JT_ROUND JT_SQUARE);
 | 
			
		||||
use Time::HiRes qw(gettimeofday tv_interval);
 | 
			
		||||
 | 
			
		||||
has 'config'                 => (is => 'rw', default => sub { Slic3r::Config->new_from_defaults }, trigger => 1);
 | 
			
		||||
has 'extra_variables'        => (is => 'rw', default => sub {{}});
 | 
			
		||||
has 'objects'                => (is => 'rw', default => sub {[]});
 | 
			
		||||
has 'copies'                 => (is => 'rw', default => sub {[]});  # obj_idx => [copies...]
 | 
			
		||||
has 'total_extrusion_length' => (is => 'rw');
 | 
			
		||||
| 
						 | 
				
			
			@ -42,6 +43,14 @@ sub _trigger_config {
 | 
			
		|||
    # store config in a handy place
 | 
			
		||||
    $Slic3r::Config = $self->config;
 | 
			
		||||
    
 | 
			
		||||
    # legacy with existing config files
 | 
			
		||||
    $self->config->set('first_layer_height', $self->config->layer_height)
 | 
			
		||||
        if !$self->config->first_layer_height;
 | 
			
		||||
    $self->config->set_ifndef('small_perimeter_speed',  $self->config->perimeter_speed);
 | 
			
		||||
    $self->config->set_ifndef('bridge_speed',           $self->config->infill_speed);
 | 
			
		||||
    $self->config->set_ifndef('solid_infill_speed',     $self->config->infill_speed);
 | 
			
		||||
    $self->config->set_ifndef('top_solid_infill_speed', $self->config->solid_infill_speed);
 | 
			
		||||
    
 | 
			
		||||
    # initialize extruder(s)
 | 
			
		||||
    $Slic3r::extruders = [];
 | 
			
		||||
    for my $t (0, map $_-1, map $self->config->get($_), qw(perimeter_extruder infill_extruder support_material_extruder)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -71,36 +80,45 @@ sub _trigger_config {
 | 
			
		|||
    # G-code flavors
 | 
			
		||||
    $self->config->set('extrusion_axis', 'A') if $self->config->gcode_flavor eq 'mach3';
 | 
			
		||||
    $self->config->set('extrusion_axis', '')  if $self->config->gcode_flavor eq 'no-extrusion';
 | 
			
		||||
    
 | 
			
		||||
    # legacy with existing config files
 | 
			
		||||
    $self->config->set_ifndef('small_perimeter_speed',  $self->config->perimeter_speed);
 | 
			
		||||
    $self->config->set_ifndef('bridge_speed',           $self->config->infill_speed);
 | 
			
		||||
    $self->config->set_ifndef('solid_infill_speed',     $self->config->infill_speed);
 | 
			
		||||
    $self->config->set_ifndef('top_solid_infill_speed', $self->config->solid_infill_speed);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub add_object_from_file {
 | 
			
		||||
sub add_objects_from_file {
 | 
			
		||||
    my $self = shift;
 | 
			
		||||
    my ($input_file) = @_;
 | 
			
		||||
    
 | 
			
		||||
    my $object;
 | 
			
		||||
    if ($input_file =~ /\.stl$/i) {
 | 
			
		||||
        my $mesh = Slic3r::Format::STL->read_file($input_file);
 | 
			
		||||
    my $model = $input_file =~ /\.stl$/i            ? Slic3r::Format::STL->read_file($input_file)
 | 
			
		||||
              : $input_file =~ /\.obj$/i            ? Slic3r::Format::OBJ->read_file($input_file)
 | 
			
		||||
              : $input_file =~ /\.amf(\.xml)?$/i    ? Slic3r::Format::AMF->read_file($input_file)
 | 
			
		||||
              : die "Input file must have .stl, .obj or .amf(.xml) extension\n";
 | 
			
		||||
    
 | 
			
		||||
    my @print_objects = $self->add_model($model);
 | 
			
		||||
    $_->input_file($input_file) for @print_objects;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub add_model {
 | 
			
		||||
    my $self = shift;
 | 
			
		||||
    my ($model) = @_;
 | 
			
		||||
    
 | 
			
		||||
    my @print_objects = ();
 | 
			
		||||
    foreach my $object (@{ $model->objects }) {
 | 
			
		||||
        my $mesh = $object->volumes->[0]->mesh;
 | 
			
		||||
        $mesh->check_manifoldness;
 | 
			
		||||
        $object = $self->add_object_from_mesh($mesh);
 | 
			
		||||
    } elsif ($input_file =~ /\.obj$/i) {
 | 
			
		||||
        my $mesh = Slic3r::Format::OBJ->read_file($input_file);
 | 
			
		||||
        $mesh->check_manifoldness;
 | 
			
		||||
        $object = $self->add_object_from_mesh($mesh);
 | 
			
		||||
    } elsif ( $input_file =~ /\.amf(\.xml)?$/i) {
 | 
			
		||||
        my ($materials, $meshes_by_material) = Slic3r::Format::AMF->read_file($input_file);
 | 
			
		||||
        $_->check_manifoldness for values %$meshes_by_material;
 | 
			
		||||
        $object = $self->add_object_from_mesh($meshes_by_material->{_} || +(values %$meshes_by_material)[0]);
 | 
			
		||||
    } else {
 | 
			
		||||
        die "Input file must have .stl, .obj or .amf(.xml) extension\n";
 | 
			
		||||
        
 | 
			
		||||
        if ($object->instances) {
 | 
			
		||||
            # we ignore the per-instance rotation currently and only 
 | 
			
		||||
            # consider the first one
 | 
			
		||||
            $mesh->rotate($object->instances->[0]->rotation);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        push @print_objects, $self->add_object_from_mesh($mesh);
 | 
			
		||||
        
 | 
			
		||||
        if ($object->instances) {
 | 
			
		||||
            # replace the default [0,0] instance with the custom ones
 | 
			
		||||
            @{$self->copies->[-1]} = map [ scale $_->offset->[X], scale $_->offset->[X] ], @{$object->instances};
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    $object->input_file($input_file);
 | 
			
		||||
    return $object;
 | 
			
		||||
    
 | 
			
		||||
    return @print_objects;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub add_object_from_mesh {
 | 
			
		||||
| 
						 | 
				
			
			@ -112,11 +130,9 @@ sub add_object_from_mesh {
 | 
			
		|||
    $mesh->align_to_origin;
 | 
			
		||||
    
 | 
			
		||||
    # initialize print object
 | 
			
		||||
    my @size = $mesh->size;
 | 
			
		||||
    my $object = Slic3r::Print::Object->new(
 | 
			
		||||
        mesh     => $mesh,
 | 
			
		||||
        x_length => $size[X],
 | 
			
		||||
        y_length => $size[Y],
 | 
			
		||||
        mesh => $mesh,
 | 
			
		||||
        size => [ $mesh->size ],
 | 
			
		||||
    );
 | 
			
		||||
    
 | 
			
		||||
    push @{$self->objects}, $object;
 | 
			
		||||
| 
						 | 
				
			
			@ -201,8 +217,8 @@ sub duplicate {
 | 
			
		|||
        for my $x_copy (1..$Slic3r::Config->duplicate_grid->[X]) {
 | 
			
		||||
            for my $y_copy (1..$Slic3r::Config->duplicate_grid->[Y]) {
 | 
			
		||||
                push @{$self->copies->[0]}, [
 | 
			
		||||
                    ($object->x_length + $dist) * ($x_copy-1),
 | 
			
		||||
                    ($object->y_length + $dist) * ($y_copy-1),
 | 
			
		||||
                    ($object->size->[X] + $dist) * ($x_copy-1),
 | 
			
		||||
                    ($object->size->[Y] + $dist) * ($y_copy-1),
 | 
			
		||||
                ];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -220,8 +236,8 @@ sub arrange_objects {
 | 
			
		|||
    my $total_parts = scalar map @$_, @{$self->copies};
 | 
			
		||||
    my $partx = my $party = 0;
 | 
			
		||||
    foreach my $object (@{$self->objects}) {
 | 
			
		||||
        $partx = $object->x_length if $object->x_length > $partx;
 | 
			
		||||
        $party = $object->y_length if $object->y_length > $party;
 | 
			
		||||
        $partx = $object->size->[X] if $object->size->[X] > $partx;
 | 
			
		||||
        $party = $object->size->[Y] if $object->size->[Y] > $party;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    # object distance is max(duplicate_distance, clearance_radius)
 | 
			
		||||
| 
						 | 
				
			
			@ -246,9 +262,9 @@ sub bounding_box {
 | 
			
		|||
        foreach my $copy (@{$self->copies->[$obj_idx]}) {
 | 
			
		||||
            push @points,
 | 
			
		||||
                [ $copy->[X], $copy->[Y] ],
 | 
			
		||||
                [ $copy->[X] + $object->x_length, $copy->[Y] ],
 | 
			
		||||
                [ $copy->[X] + $object->x_length, $copy->[Y] + $object->y_length ],
 | 
			
		||||
                [ $copy->[X], $copy->[Y] + $object->y_length ];
 | 
			
		||||
                [ $copy->[X] + $object->size->[X], $copy->[Y] ],
 | 
			
		||||
                [ $copy->[X] + $object->size->[X], $copy->[Y] + $object->size->[Y] ],
 | 
			
		||||
                [ $copy->[X], $copy->[Y] + $object->size->[Y] ];
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return Slic3r::Geometry::bounding_box(\@points);
 | 
			
		||||
| 
						 | 
				
			
			@ -497,7 +513,7 @@ sub make_skirt {
 | 
			
		|||
    my @skirt = ();
 | 
			
		||||
    for (my $i = $Slic3r::Config->skirts; $i > 0; $i--) {
 | 
			
		||||
        my $distance = scale ($Slic3r::Config->skirt_distance + ($flow->spacing * $i));
 | 
			
		||||
        my $outline = offset([$convex_hull], $distance, &Slic3r::SCALING_FACTOR * 100, JT_ROUND);
 | 
			
		||||
        my $outline = Math::Clipper::offset([$convex_hull], $distance, &Slic3r::SCALING_FACTOR * 100, JT_ROUND);
 | 
			
		||||
        push @skirt, Slic3r::ExtrusionLoop->pack(
 | 
			
		||||
            polygon => Slic3r::Polygon->new(@{$outline->[0]}),
 | 
			
		||||
            role => EXTR_ROLE_SKIRT,
 | 
			
		||||
| 
						 | 
				
			
			@ -569,15 +585,6 @@ sub write_gcode {
 | 
			
		|||
    print $fh $gcodegen->set_tool(0);
 | 
			
		||||
    print $fh $gcodegen->set_fan(0, 1) if $Slic3r::Config->cooling && $Slic3r::Config->disable_fan_first_layers;
 | 
			
		||||
    
 | 
			
		||||
    # this spits out some platic at start from each extruder when they are first used;
 | 
			
		||||
    # the primary extruder will compensate by the normal retraction length, while 
 | 
			
		||||
    # the others will compensate for their toolchange length + restart extra.
 | 
			
		||||
    # this is a temporary solution as all extruders should use some kind of skirt 
 | 
			
		||||
    # to be put into a consistent state.
 | 
			
		||||
    $_->retracted($_->retract_length_toolchange + $_->retract_restart_extra_toolchange)
 | 
			
		||||
        for @{$Slic3r::extruders}[1 .. $#{$Slic3r::extruders}];
 | 
			
		||||
    $gcodegen->retract;
 | 
			
		||||
    
 | 
			
		||||
    # write start commands to file
 | 
			
		||||
    printf $fh $gcodegen->set_bed_temperature($Slic3r::Config->first_layer_bed_temperature, 1),
 | 
			
		||||
        if $Slic3r::Config->first_layer_bed_temperature && $Slic3r::Config->start_gcode !~ /M190/i;
 | 
			
		||||
| 
						 | 
				
			
			@ -647,6 +654,7 @@ sub write_gcode {
 | 
			
		|||
        
 | 
			
		||||
        # extrude brim
 | 
			
		||||
        if ($layer_id == 0 && !$brim_done) {
 | 
			
		||||
            $gcode .= $gcodegen->set_tool($Slic3r::Config->support_material_extruder-1);
 | 
			
		||||
            $gcodegen->shift_x($shift[X]);
 | 
			
		||||
            $gcodegen->shift_y($shift[Y]);
 | 
			
		||||
            $gcode .= $gcodegen->extrude_loop($_, 'brim') for @{$self->brim};
 | 
			
		||||
| 
						 | 
				
			
			@ -811,6 +819,7 @@ sub expanded_output_filepath {
 | 
			
		|||
    return $Slic3r::Config->replace_options($path, {
 | 
			
		||||
        input_filename      => $input_filename,
 | 
			
		||||
        input_filename_base => $input_filename_base,
 | 
			
		||||
        %{ $self->extra_variables },
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,8 +8,7 @@ use Slic3r::Surface ':types';
 | 
			
		|||
 | 
			
		||||
has 'input_file'        => (is => 'rw', required => 0);
 | 
			
		||||
has 'mesh'              => (is => 'rw', required => 0);
 | 
			
		||||
has 'x_length'          => (is => 'rw', required => 1);
 | 
			
		||||
has 'y_length'          => (is => 'rw', required => 1);
 | 
			
		||||
has 'size'              => (is => 'rw', required => 1);
 | 
			
		||||
 | 
			
		||||
has 'layers' => (
 | 
			
		||||
    traits  => ['Array'],
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										12
									
								
								slic3r.pl
									
										
									
									
									
								
							
							
						
						
									
										12
									
								
								slic3r.pl
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -27,6 +27,7 @@ my %cli_options = ();
 | 
			
		|||
        'save=s'                => \$opt{save},
 | 
			
		||||
        'load=s@'               => \$opt{load},
 | 
			
		||||
        'ignore-nonexistent-config' => \$opt{ignore_nonexistent_config},
 | 
			
		||||
        'datadir=s'             => \$opt{datadir},
 | 
			
		||||
        'export-svg'            => \$opt{export_svg},
 | 
			
		||||
        'merge|m'               => \$opt{merge},
 | 
			
		||||
    );
 | 
			
		||||
| 
						 | 
				
			
			@ -70,6 +71,10 @@ if ($opt{save}) {
 | 
			
		|||
# launch GUI
 | 
			
		||||
my $gui;
 | 
			
		||||
if (!@ARGV && !$opt{save} && eval "require Slic3r::GUI; 1") {
 | 
			
		||||
    {
 | 
			
		||||
        no warnings 'once';
 | 
			
		||||
        $Slic3r::GUI::datadir = $opt{datadir} if $opt{datadir};
 | 
			
		||||
    }
 | 
			
		||||
    $gui = Slic3r::GUI->new;
 | 
			
		||||
    $gui->{skeinpanel}->load_config_file($_) for @{$opt{load}};
 | 
			
		||||
    $gui->{skeinpanel}->load_config($cli_config);
 | 
			
		||||
| 
						 | 
				
			
			@ -83,9 +88,9 @@ if (@ARGV) {  # slicing from command line
 | 
			
		|||
    
 | 
			
		||||
    while (my $input_file = shift @ARGV) {
 | 
			
		||||
        my $print = Slic3r::Print->new(config => $config);
 | 
			
		||||
        $print->add_object_from_file($input_file);
 | 
			
		||||
        $print->add_objects_from_file($input_file);
 | 
			
		||||
        if ($opt{merge}) {
 | 
			
		||||
            $print->add_object_from_file($_) for splice @ARGV, 0;
 | 
			
		||||
            $print->add_objects_from_file($_) for splice @ARGV, 0;
 | 
			
		||||
        }
 | 
			
		||||
        $print->duplicate;
 | 
			
		||||
        $print->arrange_objects if @{$print->objects} > 1;
 | 
			
		||||
| 
						 | 
				
			
			@ -213,6 +218,9 @@ $j
 | 
			
		|||
    --layer-gcode       Load layer-change G-code from the supplied file (default: nothing).
 | 
			
		||||
    --extra-perimeters  Add more perimeters when needed (default: yes)
 | 
			
		||||
    --randomize-start   Randomize starting point across layers (default: yes)
 | 
			
		||||
    --only-retract-when-crossing-perimeters
 | 
			
		||||
                        Disable retraction when travelling between infill paths inside the same island.
 | 
			
		||||
                        (default: no)
 | 
			
		||||
    --solid-infill-below-area
 | 
			
		||||
                        Force solid infill when a region has a smaller area than this threshold
 | 
			
		||||
                        (mm^2, default: $config->{solid_infill_below_area})
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										10
									
								
								t/arcs.t
									
										
									
									
									
								
							
							
						
						
									
										10
									
								
								t/arcs.t
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -11,7 +11,7 @@ BEGIN {
 | 
			
		|||
 | 
			
		||||
use Slic3r;
 | 
			
		||||
use Slic3r::ExtrusionPath ':roles';
 | 
			
		||||
use Slic3r::Geometry qw(epsilon scale X Y);
 | 
			
		||||
use Slic3r::Geometry qw(scaled_epsilon scale X Y);
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
    my $path = Slic3r::ExtrusionPath->new(polyline => Slic3r::Polyline->new(
 | 
			
		||||
| 
						 | 
				
			
			@ -61,17 +61,17 @@ use Slic3r::Geometry qw(epsilon scale X Y);
 | 
			
		|||
    isa_ok $collection2->paths->[0], 'Slic3r::ExtrusionPath::Arc', 'path';
 | 
			
		||||
    
 | 
			
		||||
    my $expected_length = scale 7.06858347057701;
 | 
			
		||||
    ok abs($collection1->paths->[0]->length - $expected_length) < scale epsilon, 'cw oriented arc has correct length';
 | 
			
		||||
    ok abs($collection2->paths->[0]->length - $expected_length) < scale epsilon, 'ccw oriented arc has correct length';
 | 
			
		||||
    ok abs($collection1->paths->[0]->length - $expected_length) < scaled_epsilon, 'cw oriented arc has correct length';
 | 
			
		||||
    ok abs($collection2->paths->[0]->length - $expected_length) < scaled_epsilon, 'ccw oriented arc has correct length';
 | 
			
		||||
 | 
			
		||||
    is $collection1->paths->[0]->orientation, 'cw', 'cw orientation was correctly detected';
 | 
			
		||||
    is $collection2->paths->[0]->orientation, 'ccw', 'ccw orientation was correctly detected';
 | 
			
		||||
    
 | 
			
		||||
    my $center1 = [ map sprintf('%.0f', $_), @{ $collection1->paths->[0]->center } ];
 | 
			
		||||
    ok abs($center1->[X] - scale 10) < scale epsilon && abs($center1->[Y] - scale 10) < scale epsilon, 'center was correctly detected';
 | 
			
		||||
    ok abs($center1->[X] - scale 10) < scaled_epsilon && abs($center1->[Y] - scale 10) < scaled_epsilon, 'center was correctly detected';
 | 
			
		||||
    
 | 
			
		||||
    my $center2 = [ map sprintf('%.0f', $_), @{ $collection2->paths->[0]->center } ];
 | 
			
		||||
    ok abs($center2->[X] - scale 10) < scale epsilon && abs($center1->[Y] - scale 10) < scale epsilon, 'center was correctly detected';
 | 
			
		||||
    ok abs($center2->[X] - scale 10) < scaled_epsilon && abs($center1->[Y] - scale 10) < scaled_epsilon, 'center was correctly detected';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#==========================================================
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										27
									
								
								t/fill.t
									
										
									
									
									
								
							
							
						
						
									
										27
									
								
								t/fill.t
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -2,7 +2,7 @@ use Test::More;
 | 
			
		|||
use strict;
 | 
			
		||||
use warnings;
 | 
			
		||||
 | 
			
		||||
plan tests => 2;
 | 
			
		||||
plan tests => 4;
 | 
			
		||||
 | 
			
		||||
BEGIN {
 | 
			
		||||
    use FindBin;
 | 
			
		||||
| 
						 | 
				
			
			@ -10,14 +10,13 @@ BEGIN {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
use Slic3r;
 | 
			
		||||
use Slic3r::Geometry qw(scale X Y);
 | 
			
		||||
use Slic3r::Surface qw(:types);
 | 
			
		||||
 | 
			
		||||
my $print = Slic3r::Print->new(
 | 
			
		||||
    x_length => 50,
 | 
			
		||||
    y_length => 50,
 | 
			
		||||
);
 | 
			
		||||
sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ }
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
    my $filler = Slic3r::Fill::Rectilinear->new(print => $print);
 | 
			
		||||
    my $filler = Slic3r::Fill::Rectilinear->new(print => Slic3r::Print->new);
 | 
			
		||||
    my $surface_width = 250;
 | 
			
		||||
    my $distance = $filler->adjust_solid_spacing(
 | 
			
		||||
        width       => $surface_width,
 | 
			
		||||
| 
						 | 
				
			
			@ -27,4 +26,20 @@ my $print = Slic3r::Print->new(
 | 
			
		|||
    is $surface_width % $distance, 0, 'adjusted solid distance';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
    my $filler = Slic3r::Fill::Rectilinear->new(
 | 
			
		||||
        print               => Slic3r::Print->new,
 | 
			
		||||
        max_print_dimension => scale 100,
 | 
			
		||||
    );
 | 
			
		||||
    my $surface = Slic3r::Surface->new(
 | 
			
		||||
        surface_type    => S_TYPE_TOP,
 | 
			
		||||
        expolygon       => Slic3r::ExPolygon->new([ scale_points [0,0], [50,0], [50,50], [0,50] ]),
 | 
			
		||||
    );
 | 
			
		||||
    foreach my $angle (0, 45) {
 | 
			
		||||
        $surface->expolygon->rotate($angle, [0,0]);
 | 
			
		||||
        my ($params, @paths) = $filler->fill_surface($surface, flow_spacing => 0.69, density => 0.4);
 | 
			
		||||
        is scalar @paths, 1, 'one continuous path';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
__END__
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,12 +25,12 @@ my %opt = ();
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
    my $mesh = Slic3r::Format::AMF->read_file($ARGV[0]);
 | 
			
		||||
    my $model = Slic3r::Format::AMF->read_file($ARGV[0]);
 | 
			
		||||
    my $output_file = $ARGV[0];
 | 
			
		||||
    $output_file =~ s/\.amf(?:\.xml)?$/\.stl/i;
 | 
			
		||||
    
 | 
			
		||||
    printf "Writing to %s\n", basename($output_file);
 | 
			
		||||
    Slic3r::Format::STL->write_file($output_file, $mesh, !$opt{ascii});
 | 
			
		||||
    Slic3r::Format::STL->write_file($output_file, $model, binary => !$opt{ascii});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,20 +1,30 @@
 | 
			
		|||
#!/usr/bin/perl -i
 | 
			
		||||
#
 | 
			
		||||
# Post-processing script for adding weight of required filament to
 | 
			
		||||
# G-code output.
 | 
			
		||||
# Post-processing script for adding weight and cost of required
 | 
			
		||||
# filament to G-code output.
 | 
			
		||||
 | 
			
		||||
use strict;
 | 
			
		||||
use warnings;
 | 
			
		||||
 | 
			
		||||
# example densities, adjust according to filament specifications
 | 
			
		||||
use constant PLA => 1.25; # g/cm3
 | 
			
		||||
use constant ABS => 1.05; # g/cm3
 | 
			
		||||
use constant PLA_P => 1.25; # g/cm3
 | 
			
		||||
use constant ABS_P => 1.05; # g/cm3
 | 
			
		||||
 | 
			
		||||
# example costs, adjust according to filament prices
 | 
			
		||||
use constant PLA_PRICE => 0.05; # EUR/g
 | 
			
		||||
use constant ABS_PRICE => 0.02; # EUR/g
 | 
			
		||||
use constant CURRENCY => "EUR";
 | 
			
		||||
 | 
			
		||||
while (<>) {
 | 
			
		||||
    if (/^(;\s+filament\s+used\s+=\s.*\((\d+(?:\.\d+)?)cm3)\)/) {
 | 
			
		||||
        my $pla = $2 * PLA;
 | 
			
		||||
        my $abs = $2 * ABS;
 | 
			
		||||
        printf "%s or %.2fg PLA/%.2fg ABS)\n", $1, $pla, $abs;
 | 
			
		||||
        my $pla_weight = $2 * PLA_P;
 | 
			
		||||
        my $abs_weight = $2 * ABS_P;
 | 
			
		||||
 | 
			
		||||
        my $pla_costs = $pla_weight * PLA_PRICE;
 | 
			
		||||
        my $abs_costs = $abs_weight * ABS_PRICE;
 | 
			
		||||
 | 
			
		||||
        printf "%s or %.2fg PLA/%.2fg ABS)\n", $1, $pla_weight, $abs_weight;
 | 
			
		||||
        printf "; costs = %s %.2f (PLA), %s %.2f (ABS)\n", CURRENCY, $pla_costs, CURRENCY, $abs_costs;
 | 
			
		||||
    } else {
 | 
			
		||||
        print;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,15 +25,20 @@ my %opt = ();
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
    my $mesh = Slic3r::Format::STL->read_file($ARGV[0]);
 | 
			
		||||
    my $model = Slic3r::Format::STL->read_file($ARGV[0]);
 | 
			
		||||
    my $basename = $ARGV[0];
 | 
			
		||||
    $basename =~ s/\.stl$//i;
 | 
			
		||||
    
 | 
			
		||||
    my $part_count = 0;
 | 
			
		||||
    foreach my $new_mesh ($mesh->split_mesh) {
 | 
			
		||||
    foreach my $new_mesh ($model->mesh->split_mesh) {
 | 
			
		||||
        my $new_model = Slic3r::Model->new;
 | 
			
		||||
        $new_model
 | 
			
		||||
            ->add_object(vertices   => $new_mesh->vertices)
 | 
			
		||||
            ->add_volume(facets     => $new_mesh->facets);
 | 
			
		||||
        
 | 
			
		||||
        my $output_file = sprintf '%s_%02d.stl', $basename, ++$part_count;
 | 
			
		||||
        printf "Writing to %s\n", basename($output_file);
 | 
			
		||||
        Slic3r::Format::STL->write_file($output_file, $new_mesh, !$opt{ascii});
 | 
			
		||||
        Slic3r::Format::STL->write_file($output_file, $new_model, binary => !$opt{ascii});
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,29 +18,50 @@ my %opt = ();
 | 
			
		|||
{
 | 
			
		||||
    my %options = (
 | 
			
		||||
        'help'                  => sub { usage() },
 | 
			
		||||
        'distinct-materials'    => \$opt{distinct_materials},
 | 
			
		||||
    );
 | 
			
		||||
    GetOptions(%options) or usage(1);
 | 
			
		||||
    $ARGV[0] or usage(1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
{
 | 
			
		||||
    my @meshes = map Slic3r::Format::STL->read_file($_), @ARGV;
 | 
			
		||||
    my @models = map Slic3r::Format::STL->read_file($_), @ARGV;
 | 
			
		||||
    my $output_file = $ARGV[0];
 | 
			
		||||
    $output_file =~ s/\.stl$/.amf.xml/i;
 | 
			
		||||
    
 | 
			
		||||
    my $materials = {};
 | 
			
		||||
    my $meshes_by_material = {};
 | 
			
		||||
    if (@meshes == 1) {
 | 
			
		||||
        $meshes_by_material->{_} = $meshes[0];
 | 
			
		||||
    my $new_model = Slic3r::Model->new;
 | 
			
		||||
    
 | 
			
		||||
    if ($opt{distinct_materials} && @models > 1) {
 | 
			
		||||
        my $new_object = $new_model->add_object;
 | 
			
		||||
        for my $m (0 .. $#models) {
 | 
			
		||||
            my $model = $models[$m];
 | 
			
		||||
            my $v_offset = @{$new_object->vertices};
 | 
			
		||||
            push @{$new_object->vertices}, @{$model->objects->[0]->vertices};
 | 
			
		||||
            my @new_facets = map {
 | 
			
		||||
                my $f = [@$_];
 | 
			
		||||
                $f->[$_] += $v_offset for -3..-1;
 | 
			
		||||
                $f;
 | 
			
		||||
            } @{ $model->objects->[0]->volumes->[0]->facets };
 | 
			
		||||
            
 | 
			
		||||
            my $material_id = scalar keys %{$new_model->materials};
 | 
			
		||||
            $new_model->materials->{$material_id} = { Name => basename($ARGV[$m]) };
 | 
			
		||||
            $new_object->add_volume(
 | 
			
		||||
                material_id => $material_id,
 | 
			
		||||
                facets      => [@new_facets],
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        for (0..$#meshes) {
 | 
			
		||||
            $materials->{$_+1} = { Name => basename($ARGV[$_]) };
 | 
			
		||||
            $meshes_by_material->{$_+1} = $meshes[$_];
 | 
			
		||||
        foreach my $model (@models) {
 | 
			
		||||
            $new_model->add_object(
 | 
			
		||||
                vertices => $model->objects->[0]->vertices,
 | 
			
		||||
            )->add_volume(
 | 
			
		||||
                facets => $model->objects->[0]->volumes->[0]->facets,
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    printf "Writing to %s\n", basename($output_file);
 | 
			
		||||
    Slic3r::Format::AMF->write_file($output_file, $materials, $meshes_by_material);
 | 
			
		||||
    Slic3r::Format::AMF->write_file($output_file, $new_model);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -51,6 +72,7 @@ sub usage {
 | 
			
		|||
Usage: amf-to-stl.pl [ OPTIONS ] file.stl [ file2.stl [ file3.stl ] ]
 | 
			
		||||
 | 
			
		||||
    --help              Output this usage screen and exit
 | 
			
		||||
    --distinct-materials Assign each STL file to a different material
 | 
			
		||||
    
 | 
			
		||||
EOF
 | 
			
		||||
    exit ($exit_code || 0);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue