Browse Source

Refactoring: new Slic3r::Model class to represent files

Alessandro Ranellucci 12 years ago
parent
commit
f90520ed06

+ 1 - 0
MANIFEST

@@ -15,6 +15,7 @@ lib/Slic3r/Fill/Flowsnake.pm
 lib/Slic3r/Fill/HilbertCurve.pm
 lib/Slic3r/Fill/Honeycomb.pm
 lib/Slic3r/Fill/Line.pm
+lib/Slic3r/Fill/Model.pm
 lib/Slic3r/Fill/OctagramSpiral.pm
 lib/Slic3r/Fill/PlanePath.pm
 lib/Slic3r/Fill/Rectilinear.pm

+ 1 - 0
lib/Slic3r.pm

@@ -41,6 +41,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;

+ 34 - 31
lib/Slic3r/Format/AMF.pm

@@ -25,15 +25,21 @@ sub read_file {
     
     close $fh;
     
-    $_ = Slic3r::TriangleMesh->new(vertices => $vertices, facets => $_)
-        for values %$meshes_by_material;
-    
-    return $materials, $meshes_by_material;
+    my $model = Slic3r::Model->new;
+    my $object = $model->add_object(vertices => $vertices);
+    foreach my $material (keys %$meshes_by_material) {
+        push @{$model->materials}, $material;  # TODO: we should not add duplicate materials
+        $object->add_volume(
+            material_id => $#{$model->materials},
+            facets      => $meshes_by_material->{$material},
+        );
+    }
+    return $model;
 }
 
 sub write_file {
     my $self = shift;
-    my ($file, $materials, $meshes_by_material) = @_;
+    my ($file, $model, %params) = @_;
     
     my %vertices_offset = ();
     
@@ -42,20 +48,20 @@ 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 (0 .. $#{ $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}, ) {
+    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 +69,21 @@ 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 $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{      </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}, $_, $facet->[$_], $_ for -3..-1;
+                printf $fh qq{        </triangle>\n};
+            }
+            printf $fh qq{      </volume>\n};
         }
-        printf $fh qq{      </volume>\n};
+        printf $fh qq{    </mesh>\n};
+        printf $fh qq{  </object>\n};
     }
-    printf $fh qq{    </mesh>\n};
-    printf $fh qq{  </object>\n};
     printf $fh qq{</amf>\n};
     close $fh;
 }

+ 1 - 1
lib/Slic3r/Format/AMF/Parser.pm

@@ -71,7 +71,7 @@ sub end_element {
     } elsif ($data->{LocalName} eq 'triangle') {
         push @{$self->{_volume}}, $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};

+ 4 - 1
lib/Slic3r/Format/OBJ.pm

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

+ 8 - 5
lib/Slic3r/Format/STL.pm

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

+ 19 - 16
lib/Slic3r/GUI/Plater.pm

@@ -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;
     
@@ -630,13 +630,11 @@ sub on_export_failed {
 
 sub export_stl {
     my $self = shift;
-    
-    my $print = $self->{print};
         
     # select output file
     my $output_file = $main::opt{output};
     {
-        $output_file = $print->expanded_output_filepath($output_file);
+        $output_file = $self->{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),
             basename($output_file), $Slic3r::GUI::SkeinPanel::model_wildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
@@ -648,21 +646,26 @@ sub export_stl {
         $dlg->Destroy;
     }
     
-    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};
+    Slic3r::Format::STL->write_file($output_file, $self->make_model, binary => 1);
+    $self->statusbar->SetStatusText("STL file exported to $output_file");
+}
+
+sub make_model {
+    my $self = shift;
+    
+    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 {

+ 1 - 1
lib/Slic3r/GUI/SkeinPanel.pm

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

+ 112 - 0
lib/Slic3r/Model.pm

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

+ 32 - 17
lib/Slic3r/Print.pm

@@ -79,28 +79,43 @@ sub _trigger_config {
     $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);
-        $mesh->check_manifoldness;
-        $object = $self->add_object_from_mesh($mesh);
-    } elsif ($input_file =~ /\.obj$/i) {
-        my $mesh = Slic3r::Format::OBJ->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 =~ /\.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 {

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