Просмотр исходного кода

Optimization: split meshes automatically when avoid_crossing_perimeters is enabled, so that we reduce the complexity of the MotionPlanner graphs. This commit includes a very large refactoring of the Model class which is now responsible for duplication and arrangement

Alessandro Ranellucci 11 лет назад
Родитель
Сommit
08a0bbd7f0

+ 1 - 0
lib/Slic3r/Fill/Base.pm

@@ -36,6 +36,7 @@ sub infill_direction {
     return [\@rotate, \@shift];
 }
 
+# this method accepts any object that implements rotate() and translate()
 sub rotate_points {
     my $self = shift;
     my ($expolygon, $rotate_vector) = @_;

+ 6 - 6
lib/Slic3r/Fill/Rectilinear.pm

@@ -25,17 +25,17 @@ sub fill_surface {
     my $line_oscillation = $distance_between_lines - $min_spacing;
     my $is_line_pattern = $self->isa('Slic3r::Fill::Line');
     
-    my $cache_id = sprintf "d%s_s%s_a%s",
+    my $cache_id = sprintf "d%s_s%.2f_a%.2f",
         $params{density}, $params{flow_spacing}, $rotate_vector->[0][0];
     
     if (!$self->cache->{$cache_id}) {
         # compute bounding box
-        my $bounding_box = [ @{$self->bounding_box} ];  # clone
-        $bounding_box->[$_] = 0 for X1, Y1;
+        my $bounding_box;
         {
-            my $bb_expolygon = Slic3r::ExPolygon->new(Slic3r::Polygon->new_from_bounding_box($bounding_box));
-            $self->rotate_points($bb_expolygon, $rotate_vector);
-            $bounding_box = [ $bb_expolygon->bounding_box ];
+            my $bb_polygon = Slic3r::Polygon->new_from_bounding_box($self->bounding_box);
+            $bb_polygon->scale(sqrt 2);
+            $self->rotate_points($bb_polygon, $rotate_vector);
+            $bounding_box = [ $bb_polygon->bounding_box ];
         }
         
         # define flow spacing according to requested density

+ 1 - 0
lib/Slic3r/GUI/Plater.pm

@@ -715,6 +715,7 @@ sub make_model {
             rotation    => $plater_object->rotate,
             offset      => [ @$_ ],
         ) for @{$plater_object->instances};
+        $new_model_object->align_to_origin;
     }
     
     return $model;

+ 10 - 1
lib/Slic3r/Geometry.pm

@@ -7,7 +7,7 @@ our @ISA = qw(Exporter);
 our @EXPORT_OK = qw(
     PI X Y Z A B X1 Y1 X2 Y2 MIN MAX epsilon slope line_atan lines_parallel 
     line_point_belongs_to_segment points_coincide distance_between_points 
-    chained_path_items chained_path_points normalize tan
+    chained_path_items chained_path_points normalize tan move_points_3D
     line_length midpoint point_in_polygon point_in_segment segment_in_segment
     point_is_on_left_of_segment polyline_lines polygon_lines nearest_point
     point_along_segment polygon_segment_having_point polygon_has_subsegment
@@ -388,6 +388,15 @@ sub move_points {
     return map Slic3r::Point->new($shift->[X] + $_->[X], $shift->[Y] + $_->[Y]), @points;
 }
 
+sub move_points_3D {
+    my ($shift, @points) = @_;
+    return map [
+        $shift->[X] + $_->[X],
+        $shift->[Y] + $_->[Y],
+        $shift->[Z] + $_->[Z],
+    ], @points;
+}
+
 # implementation of Liang-Barsky algorithm
 # polygon must be convex and ccw
 sub clip_segment_polygon {

+ 226 - 3
lib/Slic3r/Model.pm

@@ -1,7 +1,8 @@
 package Slic3r::Model;
 use Moo;
 
-use Slic3r::Geometry qw(X Y Z);
+use List::Util qw(first max);
+use Slic3r::Geometry qw(X Y Z MIN move_points);
 
 has 'materials' => (is => 'ro', default => sub { {} });
 has 'objects'   => (is => 'ro', default => sub { [] });
@@ -19,6 +20,38 @@ sub read_from_file {
     return $model;
 }
 
+sub merge {
+    my $class = shift;
+    my @models = @_;
+    
+    my $new_model = $class->new;
+    foreach my $model (@models) {
+        # merge material attributes (should we rename them in case of duplicates?)
+        $new_model->set_material($_, { %{$model->materials->{$_}}, %{$model->materials->{$_} || {}} })
+            for keys %{$model->materials};
+        
+        foreach my $object (@{$model->objects}) {
+            my $new_object = $new_model->add_object(
+                input_file          => $object->input_file,
+                vertices            => $object->vertices,
+                layer_height_ranges => $object->layer_height_ranges,
+            );
+            
+            $new_object->add_volume(
+                material_id         => $_->material_id,
+                facets              => $_->facets,
+            ) for @{$object->volumes};
+            
+            $new_object->add_instance(
+                offset              => $_->offset,
+                rotation            => $_->rotation,
+            ) for @{ $object->instances // [] };
+        }
+    }
+    
+    return $new_model;
+}
+
 sub add_object {
     my $self = shift;
     
@@ -39,10 +72,122 @@ sub set_material {
 
 sub scale {
     my $self = shift;
-    
     $_->scale(@_) for @{$self->objects};
 }
 
+sub arrange_objects {
+    my $self = shift;
+    my ($config) = @_;
+    
+    # do we have objects with no position?
+    if (first { !defined $_->instances } @{$self->objects}) {
+        # we shall redefine positions for all objects
+        
+        my ($copies, @positions) = $self->_arrange(
+            config  => $config,
+            items   => $self->objects,
+        );
+        
+        # apply positions to objects
+        foreach my $object (@{$self->objects}) {
+            $object->align_to_origin;
+            
+            $object->instances([]);
+            $object->add_instance(
+                offset      => $_,
+                rotation    => 0,
+            ) for splice @positions, 0, $copies;
+        }
+        
+    } else {
+        # we only have objects with defined position
+        
+        # align the whole model to origin as it is
+        $self->align_to_origin;
+        
+        # arrange this model as a whole
+        my ($copies, @positions) = $self->_arrange(
+            config  => $config,
+            items   => [$self],
+        );
+        
+        # apply positions to objects by translating the current positions
+        foreach my $object (@{$self->objects}) {
+            my @old_instances = @{$object->instances};
+            $object->instances([]);
+            foreach my $instance (@old_instances) {
+                $object->add_instance(
+                    offset      => $_,
+                    rotation    => $instance->rotation,
+                ) for move_points($instance->offset, @positions);
+            }
+        }
+    }
+}
+
+sub _arrange {
+    my $self = shift;
+    my %params = @_;
+    
+    my $config  = $params{config};
+    my @items   = @{$params{items}};  # can be Model or Object objects, they have to implement size()
+    
+    if ($config->duplicate_grid->[X] > 1 || $config->duplicate_grid->[Y] > 1) {
+        if (@items > 1) {
+            die "Grid duplication is not supported with multiple objects\n";
+        }
+        my @positions = ();
+        my $size = $items[0]->size;
+        my $dist = $config->duplicate_distance;
+        for my $x_copy (1..$config->duplicate_grid->[X]) {
+            for my $y_copy (1..$config->duplicate_grid->[Y]) {
+                push @positions, [
+                    ($size->[X] + $dist) * ($x_copy-1),
+                    ($size->[Y] + $dist) * ($y_copy-1),
+                ];
+            }
+        }
+        return ($config->duplicate_grid->[X] * $config->duplicate_grid->[Y]), @positions;
+    } else {
+        my $total_parts = $config->duplicate * @items;
+        my $partx = max(map $_->size->[X], @items);
+        my $party = max(map $_->size->[Y], @items);
+        return $config->duplicate,
+            Slic3r::Geometry::arrange
+                ($total_parts, $partx, $party, (map $_, @{$config->bed_size}),
+                $config->min_object_distance, $config);
+    }
+}
+
+sub vertices {
+    my $self = shift;
+    return [ map @{$_->vertices}, @{$self->objects} ];
+}
+
+sub size {
+    my $self = shift;
+    return [ Slic3r::Geometry::size_3D($self->vertices) ];
+}
+
+sub extents {
+    my $self = shift;
+    return Slic3r::Geometry::bounding_box_3D($self->vertices);
+}
+
+sub align_to_origin {
+    my $self = shift;
+    
+    # calculate the displacements needed to 
+    # have lowest value for each axis at coordinate 0
+    my @extents = $self->extents;
+    $self->move(map -$extents[$_][MIN], X,Y,Z);
+}
+
+sub move {
+    my $self = shift;
+    $_->move(@_) for @{$self->objects};
+}
+
 # flattens everything to a single mesh
 sub mesh {
     my $self = shift;
@@ -64,6 +209,47 @@ sub mesh {
     return Slic3r::TriangleMesh->merge(@meshes);
 }
 
+# this method splits objects into multiple distinct objects by walking their meshes
+sub split_meshes {
+    my $self = shift;
+    
+    my @objects = @{$self->objects};
+    @{$self->objects} = ();
+    
+    foreach my $object (@objects) {
+        if (@{$object->volumes} > 1) {
+            # We can't split meshes if there's more than one material, because
+            # we can't group the resulting meshes by object afterwards
+            push @{$self->objects}, $object;
+            next;
+        }
+        
+        my $volume = $object->volumes->[0];
+        foreach my $mesh ($volume->mesh->split_mesh) {
+            my $new_object = $self->add_object(
+                input_file          => $object->input_file,
+                layer_height_ranges => $object->layer_height_ranges,
+            );
+            $new_object->add_volume(
+                vertices    => $mesh->vertices,
+                facets      => $mesh->facets,
+                material_id => $volume->material_id,
+            );
+            
+            # let's now align the new object to the origin and put its displacement
+            # (extents) in the instances info
+            my @extents = $mesh->extents;
+            $new_object->align_to_origin;
+            
+            # add one instance per original instance applying the displacement
+            $new_object->add_instance(
+                offset      => [ $_->offset->[X] + $extents[X][MIN], $_->offset->[Y] + $extents[Y][MIN] ],
+                rotation    => $_->rotation,
+            ) for @{ $object->instances // [] };
+        }
+    }
+}
+
 package Slic3r::Model::Region;
 use Moo;
 
@@ -74,7 +260,7 @@ package Slic3r::Model::Object;
 use Moo;
 
 use List::Util qw(first);
-use Slic3r::Geometry qw(X Y Z);
+use Slic3r::Geometry qw(X Y Z MIN move_points_3D);
 use Storable qw(dclone);
 
 has 'input_file' => (is => 'rw');
@@ -123,6 +309,30 @@ sub mesh {
     );
 }
 
+sub size {
+    my $self = shift;
+    return [ Slic3r::Geometry::size_3D($self->vertices) ];
+}
+
+sub extents {
+    my $self = shift;
+    return Slic3r::Geometry::bounding_box_3D($self->vertices);
+}
+
+sub align_to_origin {
+    my $self = shift;
+    
+    # calculate the displacements needed to 
+    # have lowest value for each axis at coordinate 0
+    my @extents = $self->extents;
+    $self->move(map -$extents[$_][MIN], X,Y,Z);
+}
+
+sub move {
+    my $self = shift;
+    @{$self->vertices} = move_points_3D([ @_ ], @{$self->vertices});
+}
+
 sub scale {
     my $self = shift;
     my ($factor) = @_;
@@ -134,6 +344,19 @@ sub scale {
     }
 }
 
+sub rotate {
+    my $self = shift;
+    my ($deg) = @_;
+    return if $deg == 0;
+    
+    my $rad = Slic3r::Geometry::deg2rad($deg);
+    
+    # transform vertex coordinates
+    foreach my $vertex (@{$self->vertices}) {
+        @$vertex = (@{ +(Slic3r::Geometry::rotate_points($rad, undef, [ $vertex->[X], $vertex->[Y] ]))[0] }, $vertex->[Z]);
+    }
+}
+
 sub materials_count {
     my $self = shift;
     

+ 18 - 72
lib/Slic3r/Print.pm

@@ -87,6 +87,8 @@ sub _build_fill_maker {
     return Slic3r::Fill->new(print => $self);
 }
 
+# caller is responsible for supplying models whose objects don't collide
+# and have explicit instance positions
 sub add_model {
     my $self = shift;
     my ($model) = @_;
@@ -103,13 +105,18 @@ sub add_model {
         }
     }
     
+    # optimization: if avoid_crossing_perimeters is enabled, split
+    # this mesh into distinct objects so that we reduce the complexity
+    # of the graphs 
+    $model->split_meshes if $Slic3r::Config->avoid_crossing_perimeters && !$Slic3r::Config->complete_objects;
+    
     foreach my $object (@{ $model->objects }) {
+        # extract meshes by material
         my @meshes = ();  # by region_id
-        
         foreach my $volume (@{$object->volumes}) {
-            # should the object contain multiple volumes of the same material, merge them
             my $region_id = defined $volume->material_id ? $materials{$volume->material_id} : 0;
             my $mesh = $volume->mesh->clone;
+            # should the object contain multiple volumes of the same material, merge them
             $meshes[$region_id] = $meshes[$region_id]
                 ? Slic3r::TriangleMesh->merge($meshes[$region_id], $mesh)
                 : $mesh;
@@ -119,41 +126,22 @@ sub add_model {
             next unless $mesh;
             $mesh->check_manifoldness;
             
-            if ($object->instances) {
-                # we ignore the per-instance rotation currently and only 
-                # consider the first one
-                $mesh->rotate($object->instances->[0]->rotation);
-            }
+            # we ignore the per-instance rotation currently and only 
+            # consider the first one
+            $mesh->rotate($object->instances->[0]->rotation);
             
-            $mesh->rotate($Slic3r::Config->rotate);
-            $mesh->scale($Slic3r::Config->scale / &Slic3r::SCALING_FACTOR);
+            $mesh->scale(1 / &Slic3r::SCALING_FACTOR);
         }
         
-        my @defined_meshes = grep defined $_, @meshes;
-        my $complete_mesh = @defined_meshes == 1 ? $defined_meshes[0] : Slic3r::TriangleMesh->merge(@defined_meshes);
-        
         # initialize print object
-        my $print_object = Slic3r::Print::Object->new(
+        push @{$self->objects}, Slic3r::Print::Object->new(
             print       => $self,
             meshes      => [ @meshes ],
-            size        => [ $complete_mesh->size ],
+            copies      => [ map [ scale $_->offset->[X], scale $_->offset->[Y] ], @{$object->instances} ],
+            size        => [ map scale $_, @{ $object->size } ],
             input_file  => $object->input_file,
             layer_height_ranges => $object->layer_height_ranges,
         );
-        push @{$self->objects}, $print_object;
-        
-        # align object to origin
-        {
-            my @extents = $complete_mesh->extents;
-            foreach my $mesh (grep defined $_, @meshes) {
-                $mesh->move(map -$extents[$_][MIN], X,Y,Z);
-            }
-        }
-        
-        if ($object->instances) {
-            # replace the default [0,0] instance with the custom ones
-            $print_object->copies([ map [ scale $_->offset->[X], scale $_->offset->[Y] ], @{$object->instances} ]);
-        }
     }
 }
 
@@ -282,54 +270,12 @@ sub regions_count {
     return scalar @{$self->regions};
 }
 
-sub duplicate {
-    my $self = shift;
-    
-    if ($Slic3r::Config->duplicate_grid->[X] > 1 || $Slic3r::Config->duplicate_grid->[Y] > 1) {
-        if (@{$self->objects} > 1) {
-            die "Grid duplication is not supported with multiple objects\n";
-        }
-        my $object = $self->objects->[0];
-        
-        # generate offsets for copies
-        my $dist = scale $Slic3r::Config->duplicate_distance;
-        @{$self->objects->[0]->copies} = ();
-        for my $x_copy (1..$Slic3r::Config->duplicate_grid->[X]) {
-            for my $y_copy (1..$Slic3r::Config->duplicate_grid->[Y]) {
-                push @{$self->objects->[0]->copies}, [
-                    ($object->size->[X] + $dist) * ($x_copy-1),
-                    ($object->size->[Y] + $dist) * ($y_copy-1),
-                ];
-            }
-        }
-    } elsif ($Slic3r::Config->duplicate > 1) {
-        foreach my $object (@{$self->objects}) {
-            @{$object->copies} = map [0,0], 1..$Slic3r::Config->duplicate;
-        }
-        $self->arrange_objects;
-    }
-}
-
-sub arrange_objects {
-    my $self = shift;
-
-    my $total_parts = scalar map @{$_->copies}, @{$self->objects};
-    my $partx = max(map $_->size->[X], @{$self->objects});
-    my $party = max(map $_->size->[Y], @{$self->objects});
-    
-    my @positions = Slic3r::Geometry::arrange
-        ($total_parts, $partx, $party, (map scale $_, @{$Slic3r::Config->bed_size}), scale $Slic3r::Config->min_object_distance, $self->config);
-    
-    @{$_->copies} = splice @positions, 0, scalar @{$_->copies} for @{$self->objects};
-}
-
 sub bounding_box {
     my $self = shift;
     
     my @points = ();
-    foreach my $obj_idx (0 .. $#{$self->objects}) {
-        my $object = $self->objects->[$obj_idx];
-        foreach my $copy (@{$self->objects->[$obj_idx]->copies}) {
+    foreach my $object (@{$self->objects}) {
+        foreach my $copy (@{$object->copies}) {
             push @points,
                 [ $copy->[X], $copy->[Y] ],
                 [ $copy->[X] + $object->size->[X], $copy->[Y] ],

+ 4 - 1
lib/Slic3r/Print/Object.pm

@@ -11,7 +11,7 @@ has 'print'             => (is => 'ro', weak_ref => 1, required => 1);
 has 'input_file'        => (is => 'rw', required => 0);
 has 'meshes'            => (is => 'rw', default => sub { [] });  # by region_id
 has 'size'              => (is => 'rw', required => 1);
-has 'copies'            => (is => 'rw', default => sub {[ [0,0] ]}, trigger => 1);
+has 'copies'            => (is => 'rw', trigger => 1);  # in scaled coordinates
 has 'layers'            => (is => 'rw', default => sub { [] });
 has 'layer_height_ranges' => (is => 'rw', default => sub { [] }); # [ z_min, z_max, layer_height ]
 
@@ -76,6 +76,7 @@ sub BUILD {
     }
 }
 
+# This should be probably moved in Print.pm at the point where we sort Layer objects
 sub _trigger_copies {
     my $self = shift;
     return unless @{$self->copies} > 1;
@@ -166,6 +167,8 @@ sub slice {
                 }
             },
         );
+        
+        $self->meshes->[$region_id] = undef;  # free memory
     }
     die "Invalid input file\n" if !@{$self->layers};
     

+ 1 - 1
lib/Slic3r/SVG.pm

@@ -9,7 +9,7 @@ use constant Y => 1;
 
 our $filltype = 'evenodd';
 
-sub factor {return 30;
+sub factor {
     return &Slic3r::SCALING_FACTOR * 10;
 }
 

+ 3 - 1
lib/Slic3r/Test.pm

@@ -30,7 +30,9 @@ sub model {
     }
     
     my $model = Slic3r::Model->new;
-    $model->add_object(vertices => $vertices)->add_volume(facets => $facets);
+    my $object = $model->add_object(vertices => $vertices);
+    $object->add_volume(facets => $facets);
+    $object->add_instance(offset => [0,0]);
     return $model;
 }
 

+ 7 - 7
lib/Slic3r/TriangleMesh.pm

@@ -10,9 +10,9 @@ has 'vertices'      => (is => 'ro', required => 1);         # id => [$x,$y,$z]
 has 'facets'        => (is => 'ro', required => 1);         # id => [ $v1_id, $v2_id, $v3_id ]
 
 # private
-has 'edges'         => (is => 'ro', default => sub { [] }); # id => [ $v1_id, $v2_id ]
-has 'facets_edges'  => (is => 'ro', default => sub { [] }); # id => [ $e1_id, $e2_id, $e3_id ]
-has 'edges_facets'  => (is => 'ro', default => sub { [] }); # id => [ $f1_id, $f2_id, (...) ]
+has 'edges'         => (is => 'rw'); # id => [ $v1_id, $v2_id ]
+has 'facets_edges'  => (is => 'rw'); # id => [ $e1_id, $e2_id, $e3_id ]
+has 'edges_facets'  => (is => 'rw'); # id => [ $f1_id, $f2_id, (...) ]
 
 use constant MIN => 0;
 use constant MAX => 1;
@@ -29,13 +29,13 @@ use constant I_FACET_EDGE       => 6;
 use constant FE_TOP             => 0;
 use constant FE_BOTTOM          => 1;
 
-# always make sure this method is idempotent
 sub analyze {
     my $self = shift;
     
-    @{$self->edges} = ();
-    @{$self->facets_edges} = ();
-    @{$self->edges_facets} = ();
+    return if defined $self->edges;
+    $self->edges([]);
+    $self->facets_edges([]);
+    $self->edges_facets([]);
     my %table = ();  # edge_coordinates => edge_id
     
     for (my $facet_id = 0; $facet_id <= $#{$self->facets}; $facet_id++) {

Некоторые файлы не были показаны из-за большого количества измененных файлов