Browse Source

Toolpaths preview

Alessandro Ranellucci 10 years ago
parent
commit
907de1011f

+ 1 - 0
lib/Slic3r/GUI.pm

@@ -12,6 +12,7 @@ use Slic3r::GUI::MainFrame;
 use Slic3r::GUI::Notifier;
 use Slic3r::GUI::Plater;
 use Slic3r::GUI::Plater::2D;
+use Slic3r::GUI::Plater::2DToolpaths;
 use Slic3r::GUI::Plater::ObjectPartsPanel;
 use Slic3r::GUI::Plater::ObjectCutDialog;
 use Slic3r::GUI::Plater::ObjectPreviewDialog;

+ 4 - 0
lib/Slic3r/GUI/MainFrame.pm

@@ -190,6 +190,10 @@ sub _init_menubar {
         $self->_append_menu_item($self->{plater_menu}, "Export AMF...", 'Export current plate as AMF', sub {
             $plater->export_amf;
         });
+        $self->{plater_menu}->AppendSeparator();
+        $self->_append_menu_item($self->{plater_menu}, "Toolpaths preview…", 'Open a viewer with toolpaths preview', sub {
+            $plater->toolpaths_preview;
+        });
         
         $self->{object_menu} = $self->{plater}->object_menu;
         $self->on_plater_selection_changed(0);

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

@@ -1286,6 +1286,19 @@ sub object_settings_dialog {
     }
 }
 
+sub toolpaths_preview {
+    my ($self) = @_;
+    
+    # TODO: we should check whether steps are done in $print rather then checking the thread
+    if ($self->{process_thread}) {
+        Slic3r::GUI::show_error($self, "Unable to show preview while toolpaths are being generated.");
+        return;
+    }
+    
+    my $dlg = Slic3r::GUI::Plater::2DToolpaths::Dialog->new($self, $self->{print});
+	$dlg->ShowModal;
+}
+
 sub object_list_changed {
     my $self = shift;
     

+ 412 - 0
lib/Slic3r/GUI/Plater/2DToolpaths.pm

@@ -0,0 +1,412 @@
+package Slic3r::GUI::Plater::2DToolpaths;
+use strict;
+use warnings;
+use utf8;
+
+use List::Util qw();
+use Slic3r::Geometry qw();
+use Wx qw(:misc :sizer :slider);
+use Wx::Event qw(EVT_SLIDER);
+use base 'Wx::Panel';
+
+sub new {
+    my $class = shift;
+    my ($parent, $print) = @_;
+    
+    my $self = $class->SUPER::new($parent, -1, wxDefaultPosition);
+    $self->{print} = $print;
+    my $sizer = Wx::BoxSizer->new(wxHORIZONTAL);
+    
+    my $canvas = $self->{canvas} = Slic3r::GUI::Plater::2DToolpaths::Canvas->new($self, $print);
+    $sizer->Add($canvas, 1, wxALL | wxEXPAND, 10);
+    
+    my $slider = $self->{slider} = Wx::Slider->new(
+        $self, -1,
+        0,                              # default
+        0,                              # min
+        $print->total_layer_count-1,    # max
+        wxDefaultPosition,
+        wxDefaultSize,
+        wxVERTICAL | wxSL_INVERSE,
+    );
+    $sizer->Add($slider, 0, wxALL | wxEXPAND, 10);
+    
+    EVT_SLIDER($self, $slider, sub {
+        $canvas->set_layer($slider->GetValue);
+    });
+    
+    $self->SetSizer($sizer);
+    $self->SetMinSize($self->GetSize);
+    $sizer->SetSizeHints($self);
+    
+    $canvas->set_layer(0);
+    
+    return $self;
+}
+
+
+package Slic3r::GUI::Plater::2DToolpaths::Canvas;
+
+use Wx::Event qw(EVT_PAINT EVT_SIZE EVT_ERASE_BACKGROUND EVT_IDLE EVT_MOUSEWHEEL EVT_MOUSE_EVENTS);
+use OpenGL qw(:glconstants :glfunctions :glufunctions);
+use base qw(Wx::GLCanvas Class::Accessor);
+use Wx::GLCanvas qw(:all);
+use List::Util qw(min);
+use Slic3r::Geometry qw(scale unscale);
+
+__PACKAGE__->mk_accessors(qw(print layer_id init dirty bb));
+
+# make OpenGL::Array thread-safe
+{
+    no warnings 'redefine';
+    *OpenGL::Array::CLONE_SKIP = sub { 1 };
+}
+
+sub new {
+    my ($class, $parent, $print) = @_;
+    
+    my $self = $class->SUPER::new($parent);
+    $self->print($print);
+    $self->bb($self->print->bounding_box);
+    
+    EVT_PAINT($self, sub {
+        my $dc = Wx::PaintDC->new($self);
+        $self->Render($dc);
+    });
+    EVT_SIZE($self, sub { $self->dirty(1) });
+    EVT_IDLE($self, sub {
+        return unless $self->dirty;
+        return if !$self->IsShownOnScreen;
+        $self->Resize( $self->GetSizeWH );
+        $self->Refresh;
+    });
+    
+    return $self;
+}
+
+sub set_layer {
+    my ($self, $layer_id) = @_;
+    
+    $self->layer_id($layer_id);
+    $self->dirty(1);
+}
+
+sub Render {
+    my ($self, $dc) = @_;
+    
+    # prevent calling SetCurrent() when window is not shown yet
+    return unless $self->IsShownOnScreen;
+    return unless my $context = $self->GetContext;
+    $self->SetCurrent($context);
+    $self->InitGL;
+    
+    glMatrixMode(GL_PROJECTION);
+    glLoadIdentity();
+    my $bb = $self->bb;
+    my ($x1, $y1, $x2, $y2) = ($bb->x_min, $bb->y_min, $bb->x_max, $bb->y_max);
+    my ($x, $y) = $self->GetSizeWH;
+    if (($x2 - $x1)/($y2 - $y1) > $x/$y) {
+        # adjust Y
+        my $new_y = $y * ($x2 - $x1) / $x;
+        $y1 = ($y2 + $y1)/2 - $new_y/2;
+        $y2 = $y1 + $new_y;
+    } else {
+        my $new_x = $x * ($y2 - $y1) / $y;
+        $x1 = ($x2 + $x1)/2 - $new_x/2;
+        $x2 = $x1 + $new_x;
+    }
+    glOrtho($x1, $x2, $y1, $y2, 0, 1);
+    glDisable(GL_DEPTH_TEST);
+    glMatrixMode(GL_MODELVIEW);
+    glLoadIdentity();
+    
+    glClearColor(1, 1, 1, 0);
+    glClear(GL_COLOR_BUFFER_BIT);
+    
+    foreach my $object (@{$self->print->objects}) {
+        my $layer = $object->get_layer($self->layer_id);
+        foreach my $layerm (@{$layer->regions}) {
+            glColor3f(0.7, 0, 0);
+            $self->_draw_extrusionpath($object, $_) for @{$layerm->perimeters};
+            
+            glColor3f(0, 0, 0.7);
+            $self->_draw_extrusionpath($object, $_) for map @$_, @{$layerm->fills};
+        }
+    }
+    
+    glFlush();
+    $self->SwapBuffers;
+}
+
+sub _draw_extrusionpath {
+    my ($self, $object, $path) = @_;
+    
+    my $polyline = $path->isa('Slic3r::ExtrusionLoop')
+        ? $path->polygon->split_at_first_point
+        : $path->polyline;
+    
+    glLineWidth(1);
+    foreach my $copy (@{ $object->_shifted_copies }) {
+        foreach my $line (@{$polyline->lines}) {
+            $line->translate(@$copy);
+            glBegin(GL_LINES);
+            glVertex2f(@{$line->a});
+            glVertex2f(@{$line->b});
+            glEnd();
+        }
+    }
+}
+
+sub InitGL {
+    my $self = shift;
+ 
+    return if $self->init;
+    return unless $self->GetContext;
+    $self->init(1);
+    
+    
+}
+
+sub GetContext {
+    my ($self) = @_;
+    
+    if (Wx::wxVERSION >= 2.009) {
+        return $self->{context} ||= Wx::GLContext->new($self);
+    } else {
+        return $self->SUPER::GetContext;
+    }
+}
+ 
+sub SetCurrent {
+    my ($self, $context) = @_;
+    
+    if (Wx::wxVERSION >= 2.009) {
+        return $self->SUPER::SetCurrent($context);
+    } else {
+        return $self->SUPER::SetCurrent;
+    }
+}
+
+sub Resize {
+    my ($self, $x, $y) = @_;
+ 
+    return unless $self->GetContext;
+    $self->dirty(0);
+ 
+    $self->SetCurrent($self->GetContext);
+    
+    my ($x1, $y1, $x2, $y2) = (0, 0, $x, $y);
+    if (0 && $x > $y) {
+        $x2 = $y;
+        $x1 = ($x - $y)/2;
+    }
+    if (0 && $y > $x) {
+        $y2 = $x;
+        $y1 = ($y - $x)/2;
+    }
+    glViewport($x1, $y1, $x2, $y2);
+}
+
+sub line {
+    my (
+        $x1, $y1, $x2, $y2,     # coordinates of the line
+        $w,                     # width/thickness of the line in pixel
+        $Cr, $Cg, $Cb,          # RGB color components
+        $Br, $Bg, $Bb,          # color of background when alphablend=false
+                                # Br=alpha of color when alphablend=true
+        $alphablend,            # use alpha blend or not
+    ) = @_;
+    
+    my $t;
+    my $R;
+    my $f = $w - int($w);
+    my $A;
+    
+    if ($alphablend) {
+        $A = $Br;
+    } else {
+        $A = 1;
+    }
+    
+    # determine parameters t,R
+    if ($w >= 0 && $w < 1) {
+        $t = 0.05; $R = 0.48 + 0.32 * $f;
+        if (!$alphablend) {
+            $Cr += 0.88 * (1-$f);
+            $Cg += 0.88 * (1-$f);
+            $Cb += 0.88 * (1-$f);
+            $Cr = 1.0 if ($Cr > 1.0);
+            $Cg = 1.0 if ($Cg > 1.0);
+            $Cb = 1.0 if ($Cb > 1.0);
+        } else {
+            $A *= $f;
+        }
+    } elsif ($w >= 1.0 && $w < 2.0) {
+        $t = 0.05 + $f*0.33; $R = 0.768 + 0.312*$f;
+    } elsif ($w >= 2.0 && $w < 3.0) {
+        $t = 0.38 + $f*0.58; $R = 1.08;
+    } elsif ($w >= 3.0 && $w < 4.0) {
+        $t = 0.96 + $f*0.48; $R = 1.08;
+    } elsif ($w >= 4.0 && $w < 5.0) {
+        $t= 1.44 + $f*0.46; $R = 1.08;
+    } elsif ($w >= 5.0 && $w < 6.0) {
+        $t= 1.9 + $f*0.6; $R = 1.08;
+    } elsif ($w >= 6.0) {
+        my $ff = $w - 6.0;
+        $t = 2.5 + $ff*0.50; $R = 1.08;
+    }
+    #printf( "w=%f, f=%f, C=%.4f\n", $w, $f, $C);
+    
+    # determine angle of the line to horizontal
+    my $tx = 0; my $ty = 0; # core thinkness of a line
+    my $Rx = 0; my $Ry = 0; # fading edge of a line
+    my $cx = 0; my $cy = 0; # cap of a line
+    my $ALW = 0.01;
+    my $dx = $x2 - $x1;
+    my $dy = $y2 - $y1;
+    if (abs($dx) < $ALW) {
+        # vertical
+        $tx = $t; $ty = 0;
+        $Rx = $R; $Ry = 0;
+        if ($w > 0.0 && $w < 1.0) {
+            $tx *= 8;
+        } elsif ($w == 1.0) {
+            $tx *= 10;
+        }
+    } elsif (abs($dy) < $ALW) {
+        #horizontal
+        $tx = 0; $ty = $t;
+        $Rx = 0; $Ry = $R;
+        if ($w > 0.0 && $w < 1.0) {
+            $ty *= 8;
+        } elsif ($w == 1.0) {
+            $ty *= 10;
+        }
+    } else {
+        if ($w < 3) { # approximate to make things even faster
+            my $m = $dy/$dx;
+            # and calculate tx,ty,Rx,Ry
+            if ($m > -0.4142 && $m <= 0.4142) {
+                # -22.5 < $angle <= 22.5, approximate to 0 (degree)
+                $tx = $t * 0.1; $ty = $t;
+                $Rx = $R * 0.6; $Ry = $R;
+            } elsif ($m > 0.4142 && $m <= 2.4142) {
+                # 22.5 < $angle <= 67.5, approximate to 45 (degree)
+                $tx = $t * -0.7071; $ty = $t * 0.7071;
+                $Rx = $R * -0.7071; $Ry = $R * 0.7071;
+            } elsif ($m > 2.4142 || $m <= -2.4142) {
+                # 67.5 < $angle <= 112.5, approximate to 90 (degree)
+                $tx = $t; $ty = $t*0.1;
+                $Rx = $R; $Ry = $R*0.6;
+            } elsif ($m > -2.4142 && $m < -0.4142) {
+                # 112.5 < angle < 157.5, approximate to 135 (degree)
+                $tx = $t * 0.7071; $ty = $t * 0.7071;
+                $Rx = $R * 0.7071; $Ry = $R * 0.7071;
+            } else {
+                # error in determining angle
+                printf("error in determining angle: m=%.4f\n", $m);
+            }
+        } else {  # calculate to exact
+            $dx= $y1 - $y2;
+            $dy= $x2 - $x1;
+            my $L = sqrt($dx*$dx + $dy*$dy);
+            $dx /= $L;
+            $dy /= $L;
+            $cx = -0.6*$dy; $cy=0.6*$dx;
+            $tx = $t*$dx; $ty = $t*$dy;
+            $Rx = $R*$dx; $Ry = $R*$dy;
+        }
+    }
+
+    # draw the line by triangle strip
+    glBegin(GL_TRIANGLE_STRIP);
+    if (!$alphablend) {
+        glColor3f($Br, $Bg, $Bb);
+    } else {
+        glColor4f($Cr, $Cg, $Cb, 0);
+    }
+    glVertex2f($x1 - $tx - $Rx, $y1 - $ty - $Ry);   # fading edge
+    glVertex2f($x2 - $tx - $Rx, $y2 - $ty - $Ry);
+    
+    if (!$alphablend) {
+        glColor3f($Cr, $Cg, $Cb);
+    } else {
+        glColor4f($Cr, $Cg, $Cb, $A);
+    }
+    glVertex2f($x1 - $tx, $y1 - $ty); # core
+    glVertex2f($x2 - $tx, $y2 - $ty);
+    glVertex2f($x1 + $tx, $y1 + $ty);
+    glVertex2f($x2 + $tx, $y2 + $ty);
+    
+    if ((abs($dx) < $ALW || abs($dy) < $ALW) && $w <= 1.0) {
+        # printf("skipped one fading edge\n");
+    } else {
+        if (!$alphablend) {
+            glColor3f($Br, $Bg, $Bb);
+        } else {
+            glColor4f($Cr, $Cg, $Cb, 0);
+        }
+        glVertex2f($x1 + $tx+ $Rx, $y1 + $ty + $Ry);    # fading edge
+        glVertex2f($x2 + $tx+ $Rx, $y2 + $ty + $Ry);
+    }
+    glEnd();
+
+    # cap
+    if ($w < 3) {
+        # do not draw cap
+    } else {
+        # draw cap
+        glBegin(GL_TRIANGLE_STRIP);
+        if (!$alphablend) {
+            glColor3f($Br, $Bg, $Bb);
+        } else {
+            glColor4f($Cr, $Cg, $Cb, 0);
+        }
+        glVertex2f($x1 - $Rx + $cx, $y1 - $Ry + $cy);
+        glVertex2f($x1 + $Rx + $cx, $y1 + $Ry + $cy);
+        glColor3f($Cr, $Cg, $Cb);
+        glVertex2f($x1 - $tx - $Rx, $y1 - $ty - $Ry);
+        glVertex2f($x1 + $tx + $Rx, $y1 + $ty + $Ry);
+        glEnd();
+        glBegin(GL_TRIANGLE_STRIP);
+        if (!$alphablend) {
+            glColor3f($Br, $Bg, $Bb);
+        } else {
+            glColor4f($Cr, $Cg, $Cb, 0);
+        }
+        glVertex2f($x2 - $Rx - $cx, $y2 - $Ry - $cy);
+        glVertex2f($x2 + $Rx - $cx, $y2 + $Ry - $cy);
+        glColor3f($Cr, $Cg, $Cb);
+        glVertex2f($x2 - $tx - $Rx, $y2 - $ty - $Ry);
+        glVertex2f($x2 + $tx + $Rx, $y2 + $ty + $Ry);
+        glEnd();
+    }
+}
+
+
+package Slic3r::GUI::Plater::2DToolpaths::Dialog;
+
+use Wx qw(:dialog :id :misc :sizer);
+use Wx::Event qw(EVT_CLOSE);
+use base 'Wx::Dialog';
+
+sub new {
+    my $class = shift;
+    my ($parent, $print) = @_;
+    my $self = $class->SUPER::new($parent, -1, "Toolpaths", wxDefaultPosition, [500,500], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
+    
+    my $sizer = Wx::BoxSizer->new(wxVERTICAL);
+    $sizer->Add(Slic3r::GUI::Plater::2DToolpaths->new($self, $print), 1, wxEXPAND, 0);
+    $self->SetSizer($sizer);
+    $self->SetMinSize($self->GetSize);
+    
+    # needed to actually free memory
+    EVT_CLOSE($self, sub {
+        $self->EndModal(wxID_OK);
+        $self->Destroy;
+    });
+    
+    return $self;
+}
+
+1;

+ 4 - 1
lib/Slic3r/GUI/PreviewCanvas.pm

@@ -24,7 +24,10 @@ use constant SELECTED_COLOR => [0,1,0,1];
 use constant COLORS => [ [1,1,1], [1,0.5,0.5], [0.5,1,0.5], [0.5,0.5,1] ];
 
 # make OpenGL::Array thread-safe
-*OpenGL::Array::CLONE_SKIP = sub { 1 };
+{
+    no warnings 'redefine';
+    *OpenGL::Array::CLONE_SKIP = sub { 1 };
+}
 
 sub new {
     my ($class, $parent, $object) = @_;

+ 10 - 0
lib/Slic3r/Model.pm

@@ -390,6 +390,16 @@ sub raw_mesh {
     return $mesh;
 }
 
+sub raw_bounding_box {
+    my $self = shift;
+    
+    my @meshes = map $_->mesh, grep !$_->modifier, @{ $self->volumes };
+    die "No meshes found" if !@meshes;
+    my $bb = (shift @meshes)->bounding_box;
+    $bb->merge($_->bounding_box) for @meshes;
+    return $bb;
+}
+
 # flattens all volumes and instances into a single mesh
 sub mesh {
     my $self = shift;

+ 2 - 2
lib/Slic3r/Print.pm

@@ -167,9 +167,9 @@ sub add_model_object {
     # initialize print object and store it at the given position
     my $o;
     if (defined $obj_idx) {
-        $o = $self->set_new_object($obj_idx, $object, $object->bounding_box);
+        $o = $self->set_new_object($obj_idx, $object, $object->raw_bounding_box);
     } else {
-        $o = $self->add_object($object, $object->bounding_box);
+        $o = $self->add_object($object, $object->raw_bounding_box);
     }
 
     $o->set_copies([ map Slic3r::Point->new_scale(@{ $_->offset }), @{ $object->instances } ]);

+ 1 - 1
lib/Slic3r/Print/Simple.pm

@@ -8,7 +8,7 @@ has '_print' => (
     default => sub { Slic3r::Print->new },
     handles => [qw(apply_config extruders expanded_output_filepath
                     total_used_filament total_extruded_volume
-                    placeholder_parser)],
+                    placeholder_parser process)],
 );
 
 has 'duplicate' => (

+ 85 - 0
utils/view-toolpaths.pl

@@ -0,0 +1,85 @@
+#!/usr/bin/perl
+# This script displays 3D preview of a mesh
+
+use strict;
+use warnings;
+
+BEGIN {
+    use FindBin;
+    use lib "$FindBin::Bin/../lib";
+}
+
+use Getopt::Long qw(:config no_auto_abbrev);
+use Slic3r;
+use Slic3r::GUI;
+use Slic3r::GUI::PreviewCanvas;
+$|++;
+
+my %opt = ();
+{
+    my %options = (
+        'help'                  => sub { usage() },
+        'load=s'                => \$opt{load},
+    );
+    GetOptions(%options) or usage(1);
+    $ARGV[0] or usage(1);
+}
+
+{
+    # load model
+    my $model = Slic3r::Model->read_from_file($ARGV[0]);
+    
+    # load config
+    my $config = Slic3r::Config->new_from_defaults;
+    $config->set('skirts', 0);
+    if ($opt{load}) {
+        $config->apply(Slic3r::Config->load($opt{load}));
+    }
+    
+    # init print
+    my $sprint = Slic3r::Print::Simple->new;
+    $sprint->apply_config($config);
+    $sprint->set_model($model);
+    $sprint->process;
+    
+    # visualize toolpaths
+    $Slic3r::ViewToolpaths::print = $sprint->_print;
+    my $app = Slic3r::ViewToolpaths->new;
+    $app->MainLoop;
+}
+
+
+sub usage {
+    my ($exit_code) = @_;
+    
+    print <<"EOF";
+Usage: view-toolpaths.pl [ OPTIONS ] file.stl
+
+    --help              Output this usage screen and exit
+    --load CONFIG       Loads the supplied config file
+    
+EOF
+    exit ($exit_code || 0);
+}
+
+
+package Slic3r::ViewToolpaths;
+use Wx qw(:sizer);
+use base qw(Wx::App);
+
+our $print;
+
+sub OnInit {
+    my $self = shift;
+    
+    my $frame = Wx::Frame->new(undef, -1, 'Toolpaths', [-1, -1], [500, 500]);
+    my $panel = Wx::Panel->new($frame, -1);
+    
+    my $sizer = Wx::BoxSizer->new(wxVERTICAL);
+    $sizer->Add(Slic3r::GUI::Plater::2DToolpaths->new($panel, $print), 1, wxEXPAND, 0);
+    $panel->SetSizer($sizer);
+    
+    $frame->Show(1);
+}
+
+__END__