mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-31 04:31:15 -06:00 
			
		
		
		
	Toolpaths preview
This commit is contained in:
		
							parent
							
								
									d8b1eff62f
								
							
						
					
					
						commit
						907de1011f
					
				
					 9 changed files with 532 additions and 4 deletions
				
			
		|  | @ -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); | ||||
|  |  | |||
|  | @ -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
									
								
								lib/Slic3r/GUI/Plater/2DToolpaths.pm
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										412
									
								
								lib/Slic3r/GUI/Plater/2DToolpaths.pm
									
										
									
									
									
										Normal file
									
								
							|  | @ -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; | ||||
|  | @ -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) = @_; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Alessandro Ranellucci
						Alessandro Ranellucci