From 7387e60706510f47e5e8e14007ced99ccc1fb80c Mon Sep 17 00:00:00 2001
From: Alessandro Ranellucci <aar@cpan.org>
Date: Sat, 18 Jan 2014 18:43:55 +0100
Subject: [PATCH] More incomplete work

---
 lib/Slic3r/GUI.pm                         |  22 +++-
 lib/Slic3r/GUI/Plater.pm                  |   9 +-
 lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 133 ++++++++++++++++------
 lib/Slic3r/GUI/PreviewCanvas.pm           |  74 ++++++------
 lib/Slic3r/Model.pm                       |  61 ++++++----
 5 files changed, 197 insertions(+), 102 deletions(-)

diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm
index 20a993044..8efdca8f7 100644
--- a/lib/Slic3r/GUI.pm
+++ b/lib/Slic3r/GUI.pm
@@ -18,7 +18,8 @@ use Slic3r::GUI::Tab;
 
 our $have_OpenGL = eval "use Slic3r::GUI::PreviewCanvas; 1";
 
-use Wx 0.9901 qw(:bitmap :dialog :frame :icon :id :misc :systemsettings :toplevelwindow);
+use Wx 0.9901 qw(:bitmap :dialog :frame :icon :id :misc :systemsettings :toplevelwindow
+    :filedialog);
 use Wx::Event qw(EVT_CLOSE EVT_MENU EVT_IDLE);
 use base 'Wx::App';
 
@@ -349,6 +350,25 @@ sub output_path {
         : $dir;
 }
 
+sub open_model {
+    my ($self) = @_;
+    
+    my $dir = $Slic3r::GUI::Settings->{recent}{skein_directory}
+           || $Slic3r::GUI::Settings->{recent}{config_directory}
+           || '';
+    
+    my $dialog = Wx::FileDialog->new($self, 'Choose one or more files (STL/OBJ/AMF):', $dir, "",
+        &Slic3r::GUI::SkeinPanel::MODEL_WILDCARD, wxFD_OPEN | wxFD_MULTIPLE | wxFD_FILE_MUST_EXIST);
+    if ($dialog->ShowModal != wxID_OK) {
+        $dialog->Destroy;
+        return;
+    }
+    my @input_files = $dialog->GetPaths;
+    $dialog->Destroy;
+    
+    return @input_files;
+}
+
 sub CallAfter {
     my $class = shift;
     my ($cb) = @_;
diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm
index 8fd9ed5bb..00f4d8eba 100644
--- a/lib/Slic3r/GUI/Plater.pm
+++ b/lib/Slic3r/GUI/Plater.pm
@@ -361,14 +361,7 @@ sub filament_presets {
 sub add {
     my $self = shift;
     
-    my $dir = $Slic3r::GUI::Settings->{recent}{skein_directory} || $Slic3r::GUI::Settings->{recent}{config_directory} || '';
-    my $dialog = Wx::FileDialog->new($self, 'Choose one or more files (STL/OBJ/AMF):', $dir, "", &Slic3r::GUI::SkeinPanel::MODEL_WILDCARD, wxFD_OPEN | wxFD_MULTIPLE | wxFD_FILE_MUST_EXIST);
-    if ($dialog->ShowModal != wxID_OK) {
-        $dialog->Destroy;
-        return;
-    }
-    my @input_files = $dialog->GetPaths;
-    $dialog->Destroy;
+    my @input_files = Slic3r::GUI::open_model($self);
     $self->load_file($_) for @input_files;
 }
 
diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
index 9ec29d3b7..ef4582b31 100644
--- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
+++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm
@@ -3,7 +3,8 @@ use strict;
 use warnings;
 use utf8;
 
-use Wx qw(:misc :sizer :treectrl wxTAB_TRAVERSAL wxSUNKEN_BORDER wxBITMAP_TYPE_PNG);
+use File::Basename qw(basename);
+use Wx qw(:misc :sizer :treectrl :button wxTAB_TRAVERSAL wxSUNKEN_BORDER wxBITMAP_TYPE_PNG);
 use Wx::Event qw(EVT_BUTTON EVT_TREE_ITEM_COLLAPSING EVT_TREE_SEL_CHANGED);
 use base 'Wx::Panel';
 
@@ -29,33 +30,22 @@ sub new {
         $self->{tree_icons}->Add(Wx::Bitmap->new("$Slic3r::var/package.png", wxBITMAP_TYPE_PNG));
         $self->{tree_icons}->Add(Wx::Bitmap->new("$Slic3r::var/package_green.png", wxBITMAP_TYPE_PNG));
         
-        my $rootId = $tree->AddRoot("");
-        my %nodes = ();  # material_id => nodeId
-        foreach my $volume_id (0..$#{$object->volumes}) {
-            my $volume = $object->volumes->[$volume_id];
-            my $material_id = $volume->material_id;
-            $material_id //= '_';
-            
-            if (!exists $nodes{$material_id}) {
-                my $material_name = $material_id eq ''
-                    ? 'default'
-                    : $object->model->get_material_name($material_id);
-                $nodes{$material_id} = $tree->AppendItem($rootId, "Material: $material_name", ICON_MATERIAL);
-            }
-            my $name = $volume->modifier ? 'Modifier mesh' : 'Solid mesh';
-            my $icon = $volume->modifier ? ICON_MODIFIERMESH : ICON_SOLIDMESH;
-            my $itemId = $tree->AppendItem($nodes{$material_id}, $name, $icon);
-            $tree->SetPlData($itemId, {
-                type        => 'volume',
-                volume_id   => $volume_id,
-            });
-        }
-        $tree->ExpandAll;
+        $tree->AddRoot("");
+        $self->reload_tree;
     }
     
+    $self->{btn_load} = Wx::Button->new($self, -1, "Load part…", wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
+    $self->{btn_delete} = Wx::Button->new($self, -1, "Delete part", wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
+    
     # left pane with tree
     my $left_sizer = Wx::BoxSizer->new(wxVERTICAL);
     $left_sizer->Add($tree, 0, wxEXPAND | wxALL, 10);
+    $left_sizer->Add($self->{btn_load}, 0);
+    $left_sizer->Add($self->{btn_delete}, 0);
+    if ($Slic3r::GUI::have_button_icons) {
+        $self->{btn_load}->SetBitmap(Wx::Bitmap->new("$Slic3r::var/brick_add.png", wxBITMAP_TYPE_PNG));
+        $self->{btn_delete}->SetBitmap(Wx::Bitmap->new("$Slic3r::var/brick_delete.png", wxBITMAP_TYPE_PNG));
+    }
     
     # right pane with preview canvas
     my $canvas = $self->{canvas} = Slic3r::GUI::PreviewCanvas->new($self, $self->{model_object});
@@ -75,23 +65,94 @@ sub new {
     });
     EVT_TREE_SEL_CHANGED($self, $tree, sub {
         my ($self, $event) = @_;
-        
-        # deselect all meshes
-        $_->{selected} = 0 for @{$canvas->volumes};
-        
-        my $nodeId = $tree->GetSelection;
-        if ($nodeId->IsOk) {
-            my $itemData = $tree->GetPlData($nodeId);
-            if ($itemData && $itemData->{type} eq 'volume') {
-                $canvas->volumes->[ $itemData->{volume_id} ]{selected} = 1;
-            }
-        }
-        
-        $canvas->Render;
+        $self->selection_changed;
     });
+    EVT_BUTTON($self, $self->{btn_load}, \&on_btn_load);
     
+    $self->selection_changed;
     
     return $self;
 }
 
+sub reload_tree {
+    my ($self) = @_;
+    
+    my $object  = $self->{model_object};
+    my $tree    = $self->{tree};
+    my $rootId  = $tree->GetRootItem;
+    
+    $tree->DeleteChildren($rootId);
+    
+    my %nodes = ();  # material_id => nodeId
+    foreach my $volume_id (0..$#{$object->volumes}) {
+        my $volume = $object->volumes->[$volume_id];
+        my $material_id = $volume->material_id;
+        $material_id //= '_';
+        
+        if (!exists $nodes{$material_id}) {
+            my $material_name = $material_id eq '_'
+                ? 'default'
+                : $object->model->get_material_name($material_id);
+            $nodes{$material_id} = $tree->AppendItem($rootId, "Material: $material_name", ICON_MATERIAL);
+        }
+        my $name = $volume->modifier ? 'Modifier mesh' : 'Solid mesh';
+        my $icon = $volume->modifier ? ICON_MODIFIERMESH : ICON_SOLIDMESH;
+        my $itemId = $tree->AppendItem($nodes{$material_id}, $name, $icon);
+        $tree->SetPlData($itemId, {
+            type        => 'volume',
+            volume_id   => $volume_id,
+        });
+    }
+    $tree->ExpandAll;
+}
+
+sub selection_changed {
+    my ($self) = @_;
+    
+    # deselect all meshes
+    $_->{selected} = 0 for @{$self->{canvas}->volumes};
+    
+    # disable buttons
+    $self->{btn_delete}->Disable;
+    
+    my $nodeId = $self->{tree}->GetSelection;
+    if ($nodeId->IsOk) {
+        my $itemData = $self->{tree}->GetPlData($nodeId);
+        if ($itemData && $itemData->{type} eq 'volume') {
+            $self->{canvas}->volumes->[ $itemData->{volume_id} ]{selected} = 1;
+            $self->{btn_delete}->Enable;
+        }
+    }
+    
+    $self->{canvas}->Render;
+}
+
+sub on_btn_load {
+    my ($self) = @_;
+    
+    my @input_files = Slic3r::GUI::open_model($self);
+    foreach my $input_file (@input_files) {
+        my $model = eval { Slic3r::Model->read_from_file($input_file) };
+        if ($@) {
+            Slic3r::GUI::show_error($self, $@);
+            next;
+        }
+        
+        foreach my $object (@{$model->objects}) {
+            foreach my $volume (@{$object->volumes}) {
+                my $new_volume = $self->{model_object}->add_volume($volume);
+                if (!defined $new_volume->material_id) {
+                    my $material_name = basename($input_file);
+                    $material_name =~ s/\.(stl|obj)$//i;
+                    $self->{model_object}->model->set_material($material_name);
+                    $new_volume->material_id($material_name);
+                }
+            }
+        }
+    }
+    
+    $self->reload_tree;
+    $self->{canvas}->load_object($self->{model_object});
+}
+
 1;
diff --git a/lib/Slic3r/GUI/PreviewCanvas.pm b/lib/Slic3r/GUI/PreviewCanvas.pm
index 7635d33c6..64d7fb013 100644
--- a/lib/Slic3r/GUI/PreviewCanvas.pm
+++ b/lib/Slic3r/GUI/PreviewCanvas.pm
@@ -29,40 +29,7 @@ sub new {
     $self->sphi(45);
     $self->stheta(-45);
 
-    my $bb = $object->raw_mesh->bounding_box;
-    my $center = $bb->center;
-    $self->object_shift(Slic3r::Pointf3->new(-$center->x, -$center->y, -$bb->z_min));  #,,
-    $bb->translate(@{ $self->object_shift });
-    $self->object_bounding_box($bb);
-    
-    # group mesh(es) by material
-    my @materials = ();
-    $self->volumes([]);
-    foreach my $volume (@{$object->volumes}) {
-        my $mesh = $volume->mesh->clone;
-        $mesh->translate(@{ $self->object_shift });  
-        
-        my $material_id = $volume->material_id // '_';
-        my $color_idx = first { $materials[$_] eq $material_id } 0..$#materials;
-        if (!defined $color_idx) {
-            push @materials, $material_id;
-            $color_idx = $#materials;
-        }
-        push @{$self->volumes}, my $v = {
-            color => COLORS->[ $color_idx % scalar(@{&COLORS}) ],
-        };
-        
-        {
-            my $vertices = $mesh->vertices;
-            my @verts = map @{ $vertices->[$_] }, map @$_, @{$mesh->facets};
-            $v->{verts} = OpenGL::Array->new_list(GL_FLOAT, @verts);
-        }
-        
-        {
-            my @norms = map { @$_, @$_, @$_ } @{$mesh->normals};
-            $v->{norms} = OpenGL::Array->new_list(GL_FLOAT, @norms);
-        }
-    }
+    $self->load_object($object);
     
     EVT_PAINT($self, sub {
         my $dc = Wx::PaintDC->new($self);
@@ -102,6 +69,45 @@ sub new {
     return $self;
 }
 
+sub load_object {
+    my ($self, $object) = @_;
+    
+    my $bb = $object->raw_mesh->bounding_box;
+    my $center = $bb->center;
+    $self->object_shift(Slic3r::Pointf3->new(-$center->x, -$center->y, -$bb->z_min));  #,,
+    $bb->translate(@{ $self->object_shift });
+    $self->object_bounding_box($bb);
+    
+    # group mesh(es) by material
+    my @materials = ();
+    $self->volumes([]);
+    foreach my $volume (@{$object->volumes}) {
+        my $mesh = $volume->mesh->clone;
+        $mesh->translate(@{ $self->object_shift });  
+        
+        my $material_id = $volume->material_id // '_';
+        my $color_idx = first { $materials[$_] eq $material_id } 0..$#materials;
+        if (!defined $color_idx) {
+            push @materials, $material_id;
+            $color_idx = $#materials;
+        }
+        push @{$self->volumes}, my $v = {
+            color => COLORS->[ $color_idx % scalar(@{&COLORS}) ],
+        };
+        
+        {
+            my $vertices = $mesh->vertices;
+            my @verts = map @{ $vertices->[$_] }, map @$_, @{$mesh->facets};
+            $v->{verts} = OpenGL::Array->new_list(GL_FLOAT, @verts);
+        }
+        
+        {
+            my @norms = map { @$_, @$_, @$_ } @{$mesh->normals};
+            $v->{norms} = OpenGL::Array->new_list(GL_FLOAT, @norms);
+        }
+    }
+}
+
 # Given an axis and angle, compute quaternion.
 sub axis_to_quat {
     my ($ax, $phi) = @_;
diff --git a/lib/Slic3r/Model.pm b/lib/Slic3r/Model.pm
index 10184a077..6e86ed00a 100644
--- a/lib/Slic3r/Model.pm
+++ b/lib/Slic3r/Model.pm
@@ -47,20 +47,7 @@ sub add_object {
         );
         
         foreach my $volume (@{$object->volumes}) {
-            $new_object->add_volume(
-                material_id         => $volume->material_id,
-                mesh                => $volume->mesh->clone,
-                modifier            => $volume->modifier,
-            );
-            
-            if (defined $volume->material_id) {
-                #  merge material attributes (should we rename materials in case of duplicates?)
-                my %attributes = %{ $object->model->materials->{$volume->material_id}->attributes };
-                if (exists $self->materials->{$volume->material_id}) {
-                    %attributes = (%attributes, %{ $self->materials->{$volume->material_id}->attributes });
-                }
-                $self->set_material($volume->material_id, {%attributes});
-            }
+            $new_object->add_volume($volume);
         }
         
         $new_object->add_instance(
@@ -325,14 +312,43 @@ has '_bounding_box'         => (is => 'rw');
 
 sub add_volume {
     my $self = shift;
-    my %args = @_;
     
-    push @{$self->volumes}, my $volume = Slic3r::Model::Volume->new(
-        object => $self,
-        %args,
-    );
+    my $new_volume;
+    if (@_ == 1) {
+        # we have a Model::Volume
+        my ($volume) = @_;
+        
+        $new_volume = Slic3r::Model::Volume->new(
+            object      => $self,
+            material_id => $volume->material_id,
+            mesh        => $volume->mesh->clone,
+            modifier    => $volume->modifier,
+        );
+        
+        if (defined $volume->material_id) {
+            #  merge material attributes (should we rename materials in case of duplicates?)
+            if (my $material = $volume->object->model->materials->{$volume->material_id}) {
+                my %attributes = %{ $material->attributes };
+                if (exists $self->model->materials->{$volume->material_id}) {
+                    %attributes = (%attributes, %{ $self->model->materials->{$volume->material_id}->attributes });
+                }
+                $self->model->set_material($volume->material_id, {%attributes});
+            }
+        }
+    } else {
+        my %args = @_;
+        $new_volume = Slic3r::Model::Volume->new(
+            object => $self,
+            %args,
+        );
+    }
+    
+    push @{$self->volumes}, $new_volume;
+    
+    # invalidate cached bounding box
     $self->_bounding_box(undef);
-    return $volume;
+    
+    return $new_volume;
 }
 
 sub add_instance {
@@ -411,18 +427,17 @@ sub center_around_origin {
     # center this object around the origin
     my $bb = $self->raw_mesh->bounding_box;
     
-    # first align to origin on XYZ
+    # first align to origin on XY
     my @shift = (
         -$bb->x_min,
         -$bb->y_min,
-        -$bb->z_min,
+        0,
     );
     
     # then center it on XY
     my $size = $bb->size;
     $shift[X] -= $size->x/2;
     $shift[Y] -= $size->y/2;  #//
-    $shift[Z] -= $size->z/2;
     
     $self->translate(@shift);