From f7e97f7e9b160c81244d6c9b7e98d43408994341 Mon Sep 17 00:00:00 2001
From: Alessandro Ranellucci <aar@cpan.org>
Date: Sat, 19 Dec 2015 23:15:37 +0100
Subject: [PATCH] Refactor cutting logic, don't slice in 3DScene

---
 lib/Slic3r/GUI/3DScene.pm                |  30 +++---
 lib/Slic3r/GUI/Plater/ObjectCutDialog.pm | 130 ++++++++++++++---------
 2 files changed, 89 insertions(+), 71 deletions(-)

diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm
index 7628a6c54..162c385f1 100644
--- a/lib/Slic3r/GUI/3DScene.pm
+++ b/lib/Slic3r/GUI/3DScene.pm
@@ -13,7 +13,6 @@ use Slic3r::Geometry::Clipper qw(offset_ex intersection_pl);
 use Wx::GLCanvas qw(:all);
  
 __PACKAGE__->mk_accessors( qw(_quat _dirty init
-                              enable_cutting
                               enable_picking
                               enable_moving
                               on_viewport_changed
@@ -319,7 +318,9 @@ sub bed_bounding_box {
     my ($self) = @_;
     
     my $bb = Slic3r::Geometry::BoundingBoxf3->new;
-    $bb->merge_point(Slic3r::Pointf3->new(@$_, 0)) for @{$self->bed_shape};
+    if ($self->bed_shape) {
+        $bb->merge_point(Slic3r::Pointf3->new(@$_, 0)) for @{$self->bed_shape};
+    }
     return $bb;
 }
 
@@ -402,25 +403,19 @@ sub select_volume {
 }
 
 sub SetCuttingPlane {
-    my ($self, $z) = @_;
+    my ($self, $z, $expolygons) = @_;
     
     $self->cutting_plane_z($z);
     
-    # perform cut and cache section lines
+    # grow slices in order to display them better
+    $expolygons = offset_ex([ map @$_, @$expolygons ], scale 0.1);
+    
     my @verts = ();
-    foreach my $volume (@{$self->volumes}) {
-        foreach my $volume (@{$self->volumes}) {
-            next if !$volume->mesh;
-            my $expolygons = $volume->mesh->slice([ $z - $volume->origin->z ])->[0];
-            $expolygons = offset_ex([ map @$_, @$expolygons ], scale 0.1);
-            
-            foreach my $line (map @{$_->lines}, map @$_, @$expolygons) {
-                push @verts, (
-                    unscale($line->a->x), unscale($line->a->y), $z,  #))
-                    unscale($line->b->x), unscale($line->b->y), $z,  #))
-                );
-            }
-        }
+    foreach my $line (map @{$_->lines}, map @$_, @$expolygons) {
+        push @verts, (
+            unscale($line->a->x), unscale($line->a->y), $z,  #))
+            unscale($line->b->x), unscale($line->b->y), $z,  #))
+        );
     }
     $self->cut_lines_vertices(OpenGL::Array->new_list(GL_FLOAT, @verts));
 }
@@ -999,7 +994,6 @@ sub load_object {
                 bounding_box    => $mesh->bounding_box,
                 color           => $color,
             );
-            $v->mesh($mesh) if $self->enable_cutting;
             if ($self->select_by eq 'object') {
                 $v->select_group_id($obj_idx*1000000);
             } elsif ($self->select_by eq 'volume') {
diff --git a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm
index e7f0629e7..8b9a2ed99 100644
--- a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm
+++ b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm
@@ -4,7 +4,7 @@ use warnings;
 use utf8;
 
 use Slic3r::Geometry qw(PI X);
-use Wx qw(:dialog :id :misc :sizer wxTAB_TRAVERSAL);
+use Wx qw(wxTheApp :dialog :id :misc :sizer wxTAB_TRAVERSAL);
 use Wx::Event qw(EVT_CLOSE EVT_BUTTON);
 use base 'Wx::Dialog';
 
@@ -33,7 +33,9 @@ sub new {
             my ($opt_id) = @_;
             
             $self->{cut_options}{$opt_id} = $optgroup->get_value($opt_id);
-            $self->_update;
+            wxTheApp->CallAfter(sub {
+                $self->_update;
+            });
         },
         label_width  => 120,
     );
@@ -95,7 +97,6 @@ sub new {
     my $canvas;
     if ($Slic3r::GUI::have_OpenGL) {
         $canvas = $self->{canvas} = Slic3r::GUI::3DScene->new($self);
-        $canvas->enable_cutting(1);
         $canvas->load_object($self->{model_object}, undef, [0]);
         $canvas->set_auto_bed_shape;
         $canvas->SetSize([500,500]);
@@ -112,7 +113,16 @@ sub new {
     $self->{sizer}->SetSizeHints($self);
     
     EVT_BUTTON($self, $self->{btn_cut}, sub {
-        $self->perform_cut(1);
+        if ($self->{new_model_objects}{lower}) {
+            if ($self->{cut_options}{rotate_lower}) {
+                $self->{new_model_objects}{lower}->rotate(PI, X);
+                $self->{new_model_objects}{lower}->center_around_origin;  # align to Z = 0
+            }
+        }
+        if ($self->{new_model_objects}{upper}) {
+            $self->{new_model_objects}{upper}->center_around_origin;  # align to Z = 0
+        }
+        
         $self->EndModal(wxID_OK);
         $self->Close;
     });
@@ -125,66 +135,80 @@ sub new {
 sub _update {
     my ($self) = @_;
     
-    my $optgroup = $self->{optgroup};
-    
-    # update canvas
-    if ($self->{canvas}) {
-        my @objects = ();
-        if ($self->{cut_options}{preview}) {
-            $self->perform_cut;
-            push @objects, @{$self->{new_model_objects}};
-        } else {
-            push @objects, $self->{model_object};
+    {
+        # scale Z down to original size since we're using the transformed mesh for 3D preview
+        # and cut dialog but ModelObject::cut() needs Z without any instance transformation
+        my $z = $self->{cut_options}{z} / $self->{model_object}->instances->[0]->scaling_factor;
+        
+        {
+            my ($new_model) = $self->{model_object}->cut($z);
+            my ($upper_object, $lower_object) = @{$new_model->objects};
+            $self->{new_model} = $new_model;
+            $self->{new_model_objects} = {};
+            if ($self->{cut_options}{keep_upper} && $upper_object->volumes_count > 0) {
+                $self->{new_model_objects}{upper} = $upper_object;
+            }
+            if ($self->{cut_options}{keep_lower} && $lower_object->volumes_count > 0) {
+                $self->{new_model_objects}{lower} = $lower_object;
+            }
+        }
+        
+        # update canvas
+        if ($self->{canvas}) {
+            # get volumes to render
+            my @objects = ();
+            if ($self->{cut_options}{preview}) {
+                push @objects, values %{$self->{new_model_objects}};
+            } else {
+                push @objects, $self->{model_object};
+            }
+        
+            # get section contour
+            my @expolygons = ();
+            foreach my $volume (@{$self->{model_object}->volumes}) {
+                next if !$volume->mesh;
+                next if $volume->modifier;
+                my $expp = $volume->mesh->slice([ $z + $volume->mesh->bounding_box->z_min ])->[0];
+                push @expolygons, @$expp;
+            }
+            foreach my $expolygon (@expolygons) {
+                $self->{model_object}->instances->[0]->transform_polygon($_)
+                    for @$expolygon;
+                $expolygon->translate(map Slic3r::Geometry::scale($_), @{ $self->{model_object}->instances->[0]->offset });
+            }
+            
+            $self->{canvas}->reset_objects;
+            $self->{canvas}->load_object($_, undef, [0]) for @objects;
+            $self->{canvas}->SetCuttingPlane(
+                $self->{cut_options}{z},
+                [@expolygons],
+            );
+            $self->{canvas}->Render;
         }
-        $self->{canvas}->reset_objects;
-        $self->{canvas}->load_object($_, undef, [0]) for @objects;
-        $self->{canvas}->SetCuttingPlane($self->{cut_options}{z});
-        $self->{canvas}->Render;
     }
     
     # update controls
-    my $z = $self->{cut_options}{z};
-    $optgroup->get_field('keep_upper')->toggle(my $have_upper = abs($z - $optgroup->get_option('z')->max) > 0.1);
-    $optgroup->get_field('keep_lower')->toggle(my $have_lower = $z > 0.1);
-    $optgroup->get_field('rotate_lower')->toggle($z > 0 && $self->{cut_options}{keep_lower});
-    $optgroup->get_field('preview')->toggle($self->{cut_options}{keep_upper} != $self->{cut_options}{keep_lower});
+    {
+        my $z = $self->{cut_options}{z};
+        my $optgroup = $self->{optgroup};
+        $optgroup->get_field('keep_upper')->toggle(my $have_upper = abs($z - $optgroup->get_option('z')->max) > 0.1);
+        $optgroup->get_field('keep_lower')->toggle(my $have_lower = $z > 0.1);
+        $optgroup->get_field('rotate_lower')->toggle($z > 0 && $self->{cut_options}{keep_lower});
+        $optgroup->get_field('preview')->toggle($self->{cut_options}{keep_upper} != $self->{cut_options}{keep_lower});
     
-    # update cut button
-    if (($self->{cut_options}{keep_upper} && $have_upper)
-        || ($self->{cut_options}{keep_lower} && $have_lower)) {
-        $self->{btn_cut}->Enable;
-    } else {
-        $self->{btn_cut}->Disable;
-    }
-}
-
-sub perform_cut {
-    my ($self, $final) = @_;
-    
-    # scale Z down to original size since we're using the transformed mesh for 3D preview
-    # and cut dialog but ModelObject::cut() needs Z without any instance transformation
-    my $z = $self->{cut_options}{z} / $self->{model_object}->instances->[0]->scaling_factor;
-    
-    my ($new_model) = $self->{model_object}->cut($z);
-    my ($upper_object, $lower_object) = @{$new_model->objects};
-    $self->{new_model} = $new_model;
-    $self->{new_model_objects} = [];
-    if ($self->{cut_options}{keep_upper} && $upper_object->volumes_count > 0) {
-        $upper_object->center_around_origin if $final;  # align to Z = 0
-        push @{$self->{new_model_objects}}, $upper_object;
-    }
-    if ($self->{cut_options}{keep_lower} && $lower_object->volumes_count > 0) {
-        push @{$self->{new_model_objects}}, $lower_object;
-        if ($self->{cut_options}{rotate_lower} && $final) {
-            $lower_object->rotate(PI, X);
-            $lower_object->center_around_origin;  # align to Z = 0
+        # update cut button
+        if (($self->{cut_options}{keep_upper} && $have_upper)
+            || ($self->{cut_options}{keep_lower} && $have_lower)) {
+            $self->{btn_cut}->Enable;
+        } else {
+            $self->{btn_cut}->Disable;
         }
     }
 }
 
 sub NewModelObjects {
     my ($self) = @_;
-    return @{ $self->{new_model_objects} };
+    return values %{ $self->{new_model_objects} };
 }
 
 1;